raze/source/games/exhumed/src/snake.cpp

412 lines
11 KiB
C++
Raw Normal View History

//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 sirlemonhead, Nuke.YKT
This file is part of PCExhumed.
PCExhumed is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//-------------------------------------------------------------------------
#include "ns.h"
#include "engine.h"
#include "exhumed.h"
#include "aistuff.h"
#include "status.h"
#include "player.h"
#include "sequence.h"
#include "input.h"
#include "sound.h"
#include <string.h>
#include <assert.h>
BEGIN_PS_NS
FreeListArray<Snake, kMaxSnakes> SnakeList;
int16_t nPlayerSnake[kMaxPlayers];
2021-12-07 17:53:02 +00:00
size_t MarkSnake()
{
for (int i = 0; i < kMaxSnakes; i++)
{
GC::Mark(SnakeList[i].pEnemy);
GC::MarkArray(SnakeList[i].pSprites, kSnakeSprites);
}
return kMaxSnakes * (1 + kSnakeSprites);
}
FSerializer& Serialize(FSerializer& arc, const char* keyname, Snake& w, Snake* def)
{
if (arc.BeginObject(keyname))
{
2021-10-19 09:19:50 +00:00
arc("enemy", w.pEnemy)
("countdown", w.nCountdown)
.Array("sprites", w.pSprites, kSnakeSprites)
("run", w.nRun)
.Array("c", w.c, countof(w.c))
("se", w.nAngle)
("player", w.nSnakePlayer)
.EndObject();
}
return arc;
}
void SerializeSnake(FSerializer& arc)
{
arc("snake", SnakeList);
arc.Array("playersnake", nPlayerSnake, PlayerCount);
}
void InitSnakes()
{
SnakeList.Clear();
memset(nPlayerSnake, 0, sizeof(nPlayerSnake));
}
int GrabSnake()
{
return SnakeList.Get();
}
void DestroySnake(int nSnake)
{
int nRun = SnakeList[nSnake].nRun;
runlist_SubRunRec(nRun);
for (int i = 0; i < kSnakeSprites; i++)
{
2021-12-07 17:53:02 +00:00
DExhumedActor* pSnake = SnakeList[nSnake].pSprites[i];
if (!pSnake) continue;
runlist_DoSubRunRec(pSnake->spr.lotag - 1);
runlist_DoSubRunRec(pSnake->spr.intowner);
2021-10-19 09:19:50 +00:00
DeleteActor(pSnake);
}
SnakeList.Release(nSnake);
if (nSnake == nSnakeCam)
{
nSnakeCam = -1;
}
}
void ExplodeSnakeSprite(DExhumedActor* pActor, int nPlayer)
{
int nDamage = BulletInfo[kWeaponStaff].nDamage;
if (PlayerList[nPlayer].nDouble > 0) {
nDamage *= 2;
}
// take a copy of this, to revert after call to runlist_RadialDamageEnemy()
2021-12-07 17:53:02 +00:00
DExhumedActor* nOwner = pActor->pTarget;
2021-10-21 20:39:17 +00:00
pActor->pTarget = PlayerList[nPlayer].pActor;
2021-10-19 09:19:50 +00:00
runlist_RadialDamageEnemy(pActor, nDamage, BulletInfo[kWeaponStaff].nRadius);
2021-10-21 20:39:17 +00:00
pActor->pTarget = nOwner;
2022-01-31 22:33:44 +00:00
BuildAnim(nullptr, 23, 0, pActor->int_pos().X, pActor->int_pos().Y, pActor->int_pos().Z, pActor->sector(), 40, 4);
2022-01-31 22:33:44 +00:00
AddFlash(pActor->sector(), pActor->int_pos().X, pActor->int_pos().Y, pActor->int_pos().Z, 128);
2021-10-19 09:19:50 +00:00
StopActorSound(pActor);
}
void BuildSnake(int nPlayer, int zVal)
{
zVal -= 1280;
auto pPlayerActor = PlayerList[nPlayer].pActor;
2021-11-22 23:20:15 +00:00
auto pViewSect = PlayerList[nPlayer].pPlayerViewSect;
int nPic = seq_GetSeqPicnum(kSeqSnakBody, 0, 0);
2022-01-31 22:33:44 +00:00
int x = pPlayerActor->int_pos().X;
int y = pPlayerActor->int_pos().Y;
int z = (pPlayerActor->int_pos().Z + zVal) - 2560;
2022-08-16 21:17:01 +00:00
int nAngle = pPlayerActor->int_ang();
HitInfo hit{};
2022-08-20 13:47:52 +00:00
hitscan(vec3_t( x, y, z ), pPlayerActor->sector(), { bcos(nAngle), bsin(nAngle), 0 }, hit, CLIPMASK1);
uint32_t yDiff = abs(hit.int_hitpos().Y - y);
uint32_t xDiff = abs(hit.int_hitpos().X - x);
uint32_t sqrtNum = xDiff * xDiff + yDiff * yDiff;
if (sqrtNum > INT_MAX)
{
DPrintf(DMSG_WARNING, "%s %d: overflow\n", __func__, __LINE__);
sqrtNum = INT_MAX;
}
int nSqrt = ksqrt(sqrtNum);
if (nSqrt < bsin(512, -4))
{
2022-08-17 16:53:45 +00:00
auto v = hit.int_hitpos();
BackUpBullet(&v.X, &v.Y, nAngle);
hit.set_int_hitpos_xy(v.X, v.Y);
2021-11-25 23:25:28 +00:00
auto pActor = insertActor(hit.hitSector, 202);
pActor->set_int_pos(hit.int_hitpos());
2021-10-19 09:19:50 +00:00
ExplodeSnakeSprite(pActor, nPlayer);
DeleteActor(pActor);
return;
}
else
{
2021-10-19 09:19:50 +00:00
DExhumedActor* pTarget = nullptr;
2021-11-25 23:25:28 +00:00
auto hitactor = hit.actor();
if (hitactor && hitactor->spr.statnum >= 90 && hitactor->spr.statnum <= 199) {
2021-10-19 09:19:50 +00:00
pTarget = hitactor;
}
2021-10-21 20:18:29 +00:00
else if (sPlayerInput[nPlayer].pTarget != nullptr)
2021-10-19 09:19:50 +00:00
{
2021-10-21 20:18:29 +00:00
pTarget = sPlayerInput[nPlayer].pTarget;
}
int nSnake = GrabSnake();
if (nSnake == -1) return;
2021-10-15 21:31:00 +00:00
// GrabTimeSlot(3);
DExhumedActor* sprt = nullptr;
for (int i = 0; i < kSnakeSprites; i++)
{
2021-11-22 23:20:15 +00:00
auto pActor = insertActor(pViewSect, 202);
2021-10-19 09:19:50 +00:00
pActor->pTarget = pPlayerActor;
//pActor->spr.intowner = nPlayerSprite;
2021-12-23 16:08:49 +00:00
pActor->spr.picnum = nPic;
2019-11-18 19:38:19 +00:00
if (i == 0)
{
2022-08-16 21:46:18 +00:00
pActor->spr.pos = pPlayerActor->spr.pos.plusZ(zVal * zinttoworld);
2021-12-23 16:08:49 +00:00
pActor->spr.xrepeat = 32;
pActor->spr.yrepeat = 32;
pViewSect = pActor->sector();
2021-10-19 09:19:50 +00:00
sprt = pActor;
}
2019-11-18 19:38:19 +00:00
else
{
2022-08-16 21:46:18 +00:00
pActor->spr.pos = sprt->spr.pos;
2021-12-23 16:08:49 +00:00
pActor->spr.xrepeat = 40 - 3 * i;
pActor->spr.yrepeat = 40 - 3 * i;
2019-11-18 19:38:19 +00:00
}
2021-12-23 16:08:49 +00:00
pActor->spr.clipdist = 10;
pActor->spr.cstat = 0;
pActor->spr.shade = -64;
pActor->spr.pal = 0;
pActor->spr.xoffset = 0;
pActor->spr.yoffset = 0;
pActor->spr.angle = pPlayerActor->spr.angle;
2021-12-23 16:08:49 +00:00
pActor->spr.xvel = 0;
pActor->spr.yvel = 0;
pActor->spr.zvel = 0;
pActor->spr.hitag = 0;
pActor->spr.extra = -1;
pActor->spr.lotag = runlist_HeadRun() + 1;
pActor->backuppos();
2021-10-19 09:19:50 +00:00
SnakeList[nSnake].pSprites[i] = pActor;
pActor->spr.intowner = runlist_AddRunRec(pActor->spr.lotag - 1, ((nSnake << 8) | i), 0x110000);
}
SnakeList[nSnake].nRun = runlist_AddRunRec(NewRun, nSnake, 0x110000);
SnakeList[nSnake].c[1] = 2;
SnakeList[nSnake].c[5] = 5;
SnakeList[nSnake].c[2] = 4;
SnakeList[nSnake].c[3] = 6;
SnakeList[nSnake].c[4] = 7;
SnakeList[nSnake].c[6] = 6;
SnakeList[nSnake].c[7] = 7;
2021-10-19 09:19:50 +00:00
SnakeList[nSnake].pEnemy = pTarget;
SnakeList[nSnake].nCountdown = 0;
SnakeList[nSnake].nAngle = 0;
SnakeList[nSnake].nSnakePlayer = nPlayer;
nPlayerSnake[nPlayer] = nSnake;
if (bSnakeCam)
{
if (nSnakeCam < 0) {
nSnakeCam = nSnake;
}
}
2021-10-19 09:19:50 +00:00
D3PlayFX(StaticSound[kSound6], sprt);
}
}
DExhumedActor* FindSnakeEnemy(int nSnake)
{
int nPlayer = SnakeList[nSnake].nSnakePlayer;
auto pPlayerActor = PlayerList[nPlayer].pActor;
2021-12-07 17:53:02 +00:00
DExhumedActor* pActor = SnakeList[nSnake].pSprites[0]; // CHECKME
if (!pActor) return nullptr;
2022-08-16 21:17:01 +00:00
int nAngle = pActor->int_ang();
auto pSector =pActor->sector();
int esi = 2048;
2021-10-19 09:19:50 +00:00
DExhumedActor* pEnemy = nullptr;
ExhumedSectIterator it(pSector);
2021-10-19 09:19:50 +00:00
while (auto pAct2 = it.Next())
{
2021-12-23 18:20:50 +00:00
if (pAct2->spr.statnum >= 90 && pAct2->spr.statnum < 150 && (pAct2->spr.cstat & CSTAT_SPRITE_BLOCK_ALL))
{
2021-12-23 18:20:50 +00:00
if (pAct2 != pPlayerActor && !(pAct2->spr.cstat & CSTAT_SPRITE_INVISIBLE))
{
int nAngle2 = (nAngle - GetAngleToSprite(pActor, pAct2)) & kAngleMask;
if (nAngle2 < esi)
{
2021-10-19 09:19:50 +00:00
pEnemy = pAct2;
esi = nAngle2;
}
}
}
}
2021-10-19 09:19:50 +00:00
if (pEnemy)
2019-11-18 19:38:19 +00:00
{
2021-10-19 09:19:50 +00:00
SnakeList[nSnake].pEnemy = pEnemy;
SnakeList[nSnake].nCountdown = 0;
2019-11-18 19:38:19 +00:00
}
else
{
2021-10-19 09:19:50 +00:00
SnakeList[nSnake].nCountdown--;
if (SnakeList[nSnake].nCountdown < -25)
{
2021-10-19 09:19:50 +00:00
pEnemy = pPlayerActor;
SnakeList[nSnake].pEnemy = pPlayerActor;
}
}
2021-10-19 09:19:50 +00:00
return pEnemy;
}
2021-10-15 21:31:00 +00:00
void AISnake::Tick(RunListEvent* ev)
{
int nSnake = RunData[ev->nRun].nObjIndex;
2021-10-15 21:31:00 +00:00
assert(nSnake >= 0 && nSnake < kMaxSnakes);
2021-12-07 17:53:02 +00:00
DExhumedActor* pActor = SnakeList[nSnake].pSprites[0];
if (!pActor) return;
2021-10-19 09:19:50 +00:00
seq_MoveSequence(pActor, SeqOffsets[kSeqSnakehed], 0);
2021-12-07 17:53:02 +00:00
DExhumedActor* pEnemySprite = SnakeList[nSnake].pEnemy;
2021-10-19 09:19:50 +00:00
Collision nMov;
2021-10-15 21:31:00 +00:00
int zVal;
2021-10-19 09:19:50 +00:00
if (pEnemySprite == nullptr)
2021-10-15 21:31:00 +00:00
{
SEARCH_ENEMY:
2021-10-19 09:19:50 +00:00
nMov = movesprite(pActor,
2022-08-16 21:17:01 +00:00
600 * bcos(pActor->int_ang()),
600 * bsin(pActor->int_ang()),
bsin(SnakeList[nSnake].nAngle, -5),
2021-10-15 21:31:00 +00:00
0, 0, CLIPMASK1);
2021-10-15 21:31:00 +00:00
FindSnakeEnemy(nSnake);
2021-10-15 21:31:00 +00:00
zVal = 0;
}
else
{
if (!(pEnemySprite->spr.cstat & CSTAT_SPRITE_BLOCK_ALL))
2021-10-15 21:31:00 +00:00
{
2021-10-19 09:19:50 +00:00
SnakeList[nSnake].pEnemy = nullptr;
2021-10-15 21:31:00 +00:00
goto SEARCH_ENEMY;
}
2022-01-31 22:33:44 +00:00
zVal = pActor->int_pos().Z;
nMov = AngleChase(pActor, pEnemySprite, 1200, SnakeList[nSnake].nAngle, 32);
2022-01-31 22:33:44 +00:00
zVal = pActor->int_pos().Z - zVal;
2021-10-15 21:31:00 +00:00
}
2021-10-19 09:19:50 +00:00
if (nMov.type || nMov.exbits)
2021-10-15 21:31:00 +00:00
{
int nPlayer = SnakeList[nSnake].nSnakePlayer;
2021-10-19 09:19:50 +00:00
ExplodeSnakeSprite(SnakeList[nSnake].pSprites[0], nPlayer);
2021-10-15 21:31:00 +00:00
nPlayerSnake[nPlayer] = -1;
SnakeList[nSnake].nSnakePlayer = -1;
2021-10-15 21:31:00 +00:00
DestroySnake(nSnake);
}
else
{
2022-08-16 21:17:01 +00:00
int nAngle = pActor->int_ang();
2021-10-15 21:31:00 +00:00
int var_30 = -bcos(nAngle, 6);
int var_34 = -bsin(nAngle, 6);
int var_20 = SnakeList[nSnake].nAngle;
SnakeList[nSnake].nAngle = (SnakeList[nSnake].nAngle + 64) & 0x7FF;
2021-10-15 21:31:00 +00:00
int var_28 = (nAngle + 512) & kAngleMask;
auto pSector = pActor->sector();
2022-01-31 22:33:44 +00:00
int x = pActor->int_pos().X;
int y = pActor->int_pos().Y;
int z = pActor->int_pos().Z;
2021-10-15 21:31:00 +00:00
for (int i = 7; i > 0; i--)
{
2021-12-07 17:53:02 +00:00
DExhumedActor* pActor2 = SnakeList[nSnake].pSprites[i];
if (!pActor2) continue;
2022-08-16 21:21:10 +00:00
pActor2->set_int_ang(nAngle);
pActor2->set_int_pos({ x, y, z });
2021-11-23 00:15:28 +00:00
ChangeActorSect(pActor2, pSector);
2021-10-15 21:31:00 +00:00
int eax = (bsin(var_20) * SnakeList[nSnake].c[i]) >> 9;
2021-10-19 09:19:50 +00:00
movesprite(pActor2, var_30 + var_30 * i + eax * bcos(var_28), var_30 + var_34 * i + eax * bsin(var_28),
2021-10-15 21:31:00 +00:00
-zVal * (i - 1), 0, 0, CLIPMASK1);
2021-10-15 21:31:00 +00:00
var_20 = (var_20 + 128) & kAngleMask;
2019-11-18 19:38:19 +00:00
}
2021-10-15 21:31:00 +00:00
}
}
2019-11-18 19:38:19 +00:00
2021-10-15 21:31:00 +00:00
void AISnake::Draw(RunListEvent* ev)
{
2021-11-09 12:40:20 +00:00
int nSnake = RunData[ev->nRun].nObjIndex;
int nSprite = ev->nParam;
2019-11-18 19:38:19 +00:00
2021-10-15 21:31:00 +00:00
if ((nSnake & 0xFF) == 0) {
seq_PlotSequence(nSprite, SeqOffsets[kSeqSnakehed], 0, 0);
}
else {
seq_PlotSequence(nSprite, SeqOffsets[kSeqSnakBody], 0, 0);
}
2019-11-18 19:38:19 +00:00
ev->pTSprite->ownerActor = nullptr;
2021-10-15 21:31:00 +00:00
}
2019-11-18 19:38:19 +00:00
END_PS_NS