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

1524 lines
35 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 "player.h"
#include "view.h"
#include "status.h"
#include "sound.h"
#include "mapinfo.h"
#include <string.h>
#include <assert.h>
BEGIN_PS_NS
short nPushBlocks;
// TODO - moveme?
short overridesect;
DExhumedActor* nBodySprite[50];
2021-10-21 21:41:54 +00:00
int sprceiling, sprfloor;
Collision loHit, hiHit;
enum
{
kMaxPushBlocks = 100,
kMaxMoveChunks = 75
};
// think this belongs in init.c?
BlockInfo sBlockInfo[kMaxPushBlocks];
DExhumedActor *nChunkSprite[kMaxMoveChunks];
2020-11-29 23:51:56 +00:00
FSerializer& Serialize(FSerializer& arc, const char* keyname, BlockInfo& w, BlockInfo* def)
{
if (arc.BeginObject(keyname))
{
arc("at8", w.field_8)
2021-10-22 15:45:06 +00:00
("sprite", w.pActor)
2020-11-29 23:51:56 +00:00
("x", w.x)
("y", w.y)
.EndObject();
}
return arc;
}
2020-11-29 23:51:56 +00:00
void SerializeMove(FSerializer& arc)
{
if (arc.BeginObject("move"))
{
arc ("pushcount", nPushBlocks)
2020-11-29 23:51:56 +00:00
.Array("blocks", sBlockInfo, nPushBlocks)
("chunkcount", nCurChunkNum)
.Array("chunks", nChunkSprite, kMaxMoveChunks)
("overridesect", overridesect)
.Array("bodysprite", nBodySprite, 50)
.EndObject();
}
}
signed int lsqrt(int a1)
{
int v1;
int v2;
signed int result;
v1 = a1;
v2 = a1 - 0x40000000;
result = 0;
if (v2 >= 0)
{
result = 32768;
v1 = v2;
}
if (v1 - ((result << 15) + 0x10000000) >= 0)
{
v1 -= (result << 15) + 0x10000000;
result += 16384;
}
if (v1 - ((result << 14) + 0x4000000) >= 0)
{
v1 -= (result << 14) + 0x4000000;
result += 8192;
}
if (v1 - ((result << 13) + 0x1000000) >= 0)
{
v1 -= (result << 13) + 0x1000000;
result += 4096;
}
if (v1 - ((result << 12) + 0x400000) >= 0)
{
v1 -= (result << 12) + 0x400000;
result += 2048;
}
if (v1 - ((result << 11) + 0x100000) >= 0)
{
v1 -= (result << 11) + 0x100000;
result += 1024;
}
if (v1 - ((result << 10) + 0x40000) >= 0)
{
v1 -= (result << 10) + 0x40000;
result += 512;
}
if (v1 - ((result << 9) + 0x10000) >= 0)
{
v1 -= (result << 9) + 0x10000;
result += 256;
}
if (v1 - ((result << 8) + 0x4000) >= 0)
{
v1 -= (result << 8) + 0x4000;
result += 128;
}
if (v1 - ((result << 7) + 4096) >= 0)
{
v1 -= (result << 7) + 4096;
result += 64;
}
if (v1 - ((result << 6) + 1024) >= 0)
{
v1 -= (result << 6) + 1024;
result += 32;
}
if (v1 - (32 * result + 256) >= 0)
{
v1 -= 32 * result + 256;
result += 16;
}
if (v1 - (16 * result + 64) >= 0)
{
v1 -= 16 * result + 64;
result += 8;
}
if (v1 - (8 * result + 16) >= 0)
{
v1 -= 8 * result + 16;
result += 4;
}
if (v1 - (4 * result + 4) >= 0)
{
v1 -= 4 * result + 4;
result += 2;
}
if (v1 - (2 * result + 1) >= 0)
result += 1;
return result;
}
void MoveThings()
{
2020-08-28 07:07:36 +00:00
thinktime.Reset();
thinktime.Clock();
UndoFlashes();
DoLights();
if (nFreeze)
{
if (nFreeze == 1 || nFreeze == 2) {
DoSpiritHead();
}
}
else
{
2020-08-28 07:07:36 +00:00
actortime.Reset();
actortime.Clock();
runlist_ExecObjects();
runlist_CleanRunRecs();
2020-08-28 07:07:36 +00:00
actortime.Unclock();
}
DoBubbleMachines();
DoDrips();
DoMovingSects();
DoRegenerates();
if (currentLevel->gameflags & LEVEL_EX_COUNTDOWN)
{
DoFinale();
if (lCountDown < 1800 && nDronePitch < 2400 && !lFinaleStart)
{
nDronePitch += 64;
BendAmbientSound();
}
}
2020-08-28 07:07:36 +00:00
thinktime.Unclock();
}
void ResetMoveFifo()
{
movefifoend = 0;
movefifopos = 0;
}
// not used
void clipwall()
{
}
int BelowNear(DExhumedActor* pActor, int x, int y, int walldist, int nSector)
{
auto pSprite = &pActor->s();
nSector = pSprite->sectnum;
int z = pSprite->z;
2021-10-21 21:41:54 +00:00
int z2;
2021-10-21 21:41:54 +00:00
if (loHit.type == kHitSprite)
{
2021-10-21 21:41:54 +00:00
z2 = loHit.actor->s().z;
}
else
{
z2 = sector[nSector].floorz + sector[nSector].Depth;
BFSSearch search(numsectors, nSector);
int edx = 0;
for (unsigned nCurSector; (nCurSector = search.GetNext()) != BFSSearch::EOL;)
{
for (auto& wal : wallsofsector(nCurSector))
{
int nNextSector = wal.nextsector;
if (nNextSector >= 0)
{
if (!search.Check(nNextSector))
{
vec2_t pos = { x, y };
if (clipinsidebox(&pos, wallnum(&wal), walldist))
{
search.Add(nNextSector);
}
}
}
}
int nSect2 = nCurSector;
while (nSect2 >= 0)
{
edx = nSect2;
nSect2 = sector[nSect2].Below;
}
int ecx = sector[edx].floorz + sector[edx].Depth;
int eax = ecx - z;
if (eax < 0 && eax >= -5120)
{
z2 = ecx;
nSector = edx;
}
}
}
if (z2 < pSprite->z)
{
pSprite->z = z2;
overridesect = nSector;
pSprite->zvel = 0;
bTouchFloor = true;
2021-10-21 21:41:54 +00:00
return kHitAux2;
}
else
{
return 0;
}
}
2021-10-21 21:41:54 +00:00
Collision movespritez(DExhumedActor* pActor, int z, int height, int, int clipdist)
{
2021-10-21 21:41:54 +00:00
spritetype* pSprite = &pActor->s();
int nSector =pSprite->sectnum;
assert(validSectorIndex(nSector));
overridesect = nSector;
2019-09-25 21:16:12 +00:00
short edi = nSector;
// backup cstat
2019-09-25 21:16:12 +00:00
uint16_t cstat = pSprite->cstat;
2019-09-25 21:16:12 +00:00
pSprite->cstat &= ~CSTAT_SPRITE_BLOCK;
2021-10-21 21:41:54 +00:00
Collision nRet(0);
short nSectFlags = sector[nSector].Flag;
2019-09-25 21:16:12 +00:00
if (nSectFlags & kSectUnderwater) {
z >>= 1;
}
2019-09-25 21:16:12 +00:00
int spriteZ = pSprite->z;
int floorZ = sector[nSector].floorz;
int ebp = spriteZ + z;
int eax = sector[nSector].ceilingz + (height >> 1);
2019-09-25 21:16:12 +00:00
if ((nSectFlags & kSectUnderwater) && ebp < eax) {
ebp = eax;
}
// loc_151E7:
while (ebp > pSprite->sector()->floorz && pSprite->sector()->Below >= 0)
{
ChangeActorSect(pActor, pSprite->sector()->Below);
}
2019-09-25 21:16:12 +00:00
if (edi != nSector)
{
2019-09-25 21:16:12 +00:00
pSprite->z = ebp;
if (sector[edi].Flag & kSectUnderwater)
{
2021-10-21 21:41:54 +00:00
if (pActor == PlayerList[nLocalPlayer].Actor()) {
D3PlayFX(StaticSound[kSound2], pActor);
}
2019-09-25 21:16:12 +00:00
if (pSprite->statnum <= 107) {
pSprite->hitag = 0;
}
}
}
2019-09-25 21:16:12 +00:00
else
{
while ((ebp < pSprite->sector()->ceilingz) && (pSprite->sector()->Above >= 0))
2019-09-25 21:16:12 +00:00
{
edi = pSprite->sector()->Above;
2019-09-25 21:16:12 +00:00
2021-10-21 21:41:54 +00:00
ChangeActorSect(pActor, edi);
2019-09-25 21:16:12 +00:00
}
}
// This function will keep the player from falling off cliffs when you're too close to the edge.
// This function finds the highest and lowest z coordinates that your clipping BOX can get to.
2021-10-21 21:41:54 +00:00
int hihit, lohit;
vec3_t pos = pSprite->pos;
pos.z -= 256;
getzrange(&pos, pSprite->sectnum,
2019-09-25 21:16:12 +00:00
&sprceiling, &hihit, &sprfloor, &lohit, 128, CLIPMASK0);
2021-10-21 21:41:54 +00:00
hiHit.setFromEngine(hihit);
loHit.setFromEngine(lohit);
int mySprfloor = sprfloor;
2021-10-21 21:41:54 +00:00
if (loHit.type != kHitSprite) {
mySprfloor += pSprite->sector()->Depth;
}
if (ebp > mySprfloor)
{
if (z > 0)
{
bTouchFloor = true;
2021-10-21 21:41:54 +00:00
if (loHit.type == kHitSprite)
{
2019-09-25 21:16:12 +00:00
// Path A
2021-10-21 21:41:54 +00:00
auto pFloorSprite = &loHit.actor->s();
2021-10-21 21:41:54 +00:00
if (pSprite->statnum == 100 && pFloorSprite->statnum != 0 && pFloorSprite->statnum < 100)
2019-09-25 21:16:12 +00:00
{
short nDamage = (z >> 9);
if (nDamage)
{
2021-10-21 21:41:54 +00:00
runlist_DamageEnemy(loHit.actor, pActor, nDamage << 1);
}
2019-09-25 21:16:12 +00:00
pSprite->zvel = -z;
}
2019-09-25 21:16:12 +00:00
else
{
2021-10-21 21:41:54 +00:00
if (pFloorSprite->statnum == 0 || pFloorSprite->statnum > 199)
{
2021-10-21 21:41:54 +00:00
nRet.exbits |= kHitAux2;
}
else
{
2021-10-21 21:41:54 +00:00
nRet = loHit;
}
2019-09-25 21:16:12 +00:00
pSprite->zvel = 0;
}
2019-09-25 21:16:12 +00:00
}
else
{
// Path B
if (pSprite->sector()->Below == -1)
{
2021-10-21 21:41:54 +00:00
nRet.exbits |= kHitAux2;
2019-09-25 21:16:12 +00:00
int nSectDamage = pSprite->sector()->Damage;
2019-09-25 21:16:12 +00:00
if (nSectDamage != 0)
{
2019-09-25 21:16:12 +00:00
if (pSprite->hitag < 15)
{
2021-10-21 21:41:54 +00:00
IgniteSprite(pActor);
2019-09-25 21:16:12 +00:00
pSprite->hitag = 20;
}
nSectDamage >>= 2;
nSectDamage = nSectDamage - (nSectDamage>>2);
if (nSectDamage) {
2021-10-21 21:41:54 +00:00
runlist_DamageEnemy(pActor, nullptr, nSectDamage);
2019-09-25 21:16:12 +00:00
}
}
2019-09-25 21:16:12 +00:00
pSprite->zvel = 0;
}
}
}
// loc_1543B:
ebp = mySprfloor;
2019-09-25 21:16:12 +00:00
pSprite->z = mySprfloor;
}
else
{
if ((ebp - height) < sprceiling && (hiHit.type == kHitSprite || pSprite->sector()->Above == -1))
{
ebp = sprceiling + height;
2021-10-21 21:41:54 +00:00
nRet.exbits |= kHitAux1;
}
}
if (spriteZ <= floorZ && ebp > floorZ)
{
if ((sector[nSector].Depth != 0) || (edi != nSector && (sector[edi].Flag & kSectUnderwater)))
{
assert(validSectorIndex(nSector));
2021-10-21 21:41:54 +00:00
BuildSplash(pActor, nSector);
}
}
2019-09-25 21:16:12 +00:00
pSprite->cstat = cstat; // restore cstat
pSprite->z = ebp;
2019-09-25 21:16:12 +00:00
if (pSprite->statnum == 100)
{
nRet.exbits |= BelowNear(pActor, pSprite->x, pSprite->y, clipdist + (clipdist / 2), pSprite->sectnum);
}
return nRet;
}
2021-10-15 22:31:28 +00:00
int GetActorHeight(DExhumedActor* actor)
{
return tileHeight(actor->s().picnum) * actor->s().yrepeat * 4;
}
DExhumedActor* insertActor(int sect, int stat)
{
int ndx = insertsprite(sect, stat);
return ndx >= 0 ? &exhumedActors[ndx] : nullptr;
}
2021-10-21 18:25:15 +00:00
Collision movesprite(DExhumedActor* pActor, int dx, int dy, int dz, int ceildist, int flordist, unsigned int clipmask)
{
2021-10-21 18:25:15 +00:00
spritetype *pSprite = &pActor->s();
bTouchFloor = false;
2019-09-25 21:16:12 +00:00
int x = pSprite->x;
int y = pSprite->y;
int z = pSprite->z;
2021-10-21 18:25:15 +00:00
int nSpriteHeight = GetActorHeight(pActor);
2019-09-25 21:16:12 +00:00
int nClipDist = (int8_t)pSprite->clipdist << 2;
int nSector = pSprite->sectnum;
assert(validSectorIndex(nSector));
int floorZ = sector[nSector].floorz;
if ((sector[nSector].Flag & kSectUnderwater) || (floorZ < z))
{
dx >>= 1;
dy >>= 1;
}
2021-10-21 21:41:54 +00:00
Collision nRet = movespritez(pActor, dz, nSpriteHeight, flordist, nClipDist);
2019-09-25 21:16:12 +00:00
nSector = pSprite->sectnum; // modified in movespritez so re-grab this variable
2019-09-25 21:16:12 +00:00
if (pSprite->statnum == 100)
{
int nPlayer = GetPlayerFromActor(pActor);
int varA = 0;
int varB = 0;
2019-09-25 21:16:12 +00:00
CheckSectorFloor(overridesect, pSprite->z, &varB, &varA);
if (varB || varA)
{
PlayerList[nPlayer].nXDamage = varB;
PlayerList[nPlayer].nYDamage = varA;
}
dx += PlayerList[nPlayer].nXDamage;
dy += PlayerList[nPlayer].nYDamage;
}
else
{
2019-09-25 21:16:12 +00:00
CheckSectorFloor(overridesect, pSprite->z, &dx, &dy);
}
int colv = clipmove(&pSprite->pos, &nSector, dx, dy, nClipDist, nSpriteHeight, flordist, clipmask);
2021-10-21 21:41:54 +00:00
Collision coll(colv);
if (coll.type != kHitNone) // originally this or'ed the two values which can create unpredictable bad values in some edge cases.
{
coll.exbits = nRet.exbits;
nRet = coll;
}
2019-09-25 21:16:12 +00:00
if ((nSector != pSprite->sectnum) && nSector >= 0)
{
2021-10-21 21:41:54 +00:00
if (nRet.exbits & kHitAux2) {
dz = 0;
}
if ((sector[nSector].floorz - z) < (dz + flordist))
{
2019-09-25 21:16:12 +00:00
pSprite->x = x;
pSprite->y = y;
}
else
{
2021-10-21 18:25:15 +00:00
ChangeActorSect(pActor, nSector);
2019-09-25 21:16:12 +00:00
if (pSprite->pal < 5 && !pSprite->hitag)
{
pSprite->pal = pSprite->sector()->ceilingpal;
}
}
}
2021-10-21 21:41:54 +00:00
return nRet;
}
2021-10-20 22:00:26 +00:00
void Gravity(DExhumedActor* actor)
{
2021-10-20 22:00:26 +00:00
auto pSprite = &actor->s();
int nSector =pSprite->sectnum;
if (sector[nSector].Flag & kSectUnderwater)
{
if (pSprite->statnum != 100)
{
if (pSprite->zvel <= 1024)
{
if (pSprite->zvel < 2048) {
pSprite->zvel += 512;
}
}
else
{
pSprite->zvel -= 64;
}
}
else
{
if (pSprite->zvel > 0)
{
pSprite->zvel -= 64;
if (pSprite->zvel < 0) {
pSprite->zvel = 0;
}
}
else if (pSprite->zvel < 0)
{
pSprite->zvel += 64;
if (pSprite->zvel > 0) {
pSprite->zvel = 0;
}
}
}
}
else
{
pSprite->zvel += 512;
if (pSprite->zvel > 16384) {
pSprite->zvel = 16384;
}
}
}
2021-10-21 18:25:15 +00:00
Collision MoveCreature(DExhumedActor* pActor)
{
2021-10-21 18:25:15 +00:00
auto pSprite = &pActor->s();
return movesprite(pActor, pSprite->xvel << 8, pSprite->yvel << 8, pSprite->zvel, 15360, -5120, CLIPMASK0);
}
2021-10-21 18:25:15 +00:00
Collision MoveCreatureWithCaution(DExhumedActor* pActor)
{
2021-10-21 18:25:15 +00:00
auto pSprite = &pActor->s();
int x = pSprite->x;
int y = pSprite->y;
int z = pSprite->z;
auto pSectorPre = pSprite->sector();
2021-10-21 18:25:15 +00:00
auto ecx = MoveCreature(pActor);
auto pSector =pSprite->sector();
if (pSector != pSectorPre)
{
int zDiff = pSectorPre->floorz - pSector->floorz;
if (zDiff < 0) {
zDiff = -zDiff;
}
if (zDiff > 15360 || (pSector->Flag & kSectUnderwater) || (pSector->Below > -1 && sector[pSector->Below].Flag) || pSector->Damage)
{
pSprite->x = x;
pSprite->y = y;
pSprite->z = z;
ChangeActorSect(pActor, sectnum(pSectorPre));
pSprite->ang = (pSprite->ang + 256) & kAngleMask;
pSprite->xvel = bcos(pSprite->ang, -2);
pSprite->yvel = bsin(pSprite->ang, -2);
2021-10-21 18:25:15 +00:00
return Collision(0);
}
}
return ecx;
}
int GetAngleToSprite(DExhumedActor* a1, DExhumedActor* a2)
{
if (!a1 || !a2)
return -1;
auto pSprite1 = &a1->s();
auto pSprite2 = &a2->s();
return GetMyAngle(pSprite2->x - pSprite1->x, pSprite2->y - pSprite1->y);
}
int PlotCourseToSprite(DExhumedActor* pActor1, DExhumedActor* pActor2)
{
if (pActor1 == nullptr || pActor2 == nullptr)
return -1;
auto pSprite1 = &pActor1->s();
auto pSprite2 = &pActor2->s();
int x = pSprite2->x - pSprite1->x;
int y = pSprite2->y - pSprite1->y;
pSprite1->ang = GetMyAngle(x, y);
uint32_t x2 = abs(x);
uint32_t y2 = abs(y);
uint32_t diff = x2 * x2 + y2 * y2;
if (diff > INT_MAX)
{
DPrintf(DMSG_WARNING, "%s %d: overflow\n", __func__, __LINE__);
diff = INT_MAX;
}
return ksqrt(diff);
}
DExhumedActor* FindPlayer(DExhumedActor* pActor, int nDistance, bool dontengage)
{
auto pSprite = &pActor->s();
int var_18 = !dontengage;
if (nDistance < 0)
nDistance = 100;
int x = pSprite->x;
int y = pSprite->y;
int nSector =pSprite->sectnum;
int z = pSprite->z - GetActorHeight(pActor);
nDistance <<= 8;
DExhumedActor* pPlayerActor = nullptr;
int i = 0;
while (1)
{
if (i >= nTotalPlayers)
return nullptr;
pPlayerActor = PlayerList[i].Actor();
auto pPlayerSprite = &pPlayerActor->s();
if ((pPlayerSprite->cstat & 0x101) && (!(pPlayerSprite->cstat & 0x8000)))
{
int v9 = abs(pPlayerSprite->x - x);
if (v9 < nDistance)
{
int v10 = abs(pPlayerSprite->y - y);
if (v10 < nDistance && cansee(pPlayerSprite->x, pPlayerSprite->y, pPlayerSprite->z - 7680, pPlayerSprite->sectnum, x, y, z, nSector))
{
break;
}
}
}
i++;
}
if (var_18) {
PlotCourseToSprite(pActor, pPlayerActor);
}
return pPlayerActor;
}
void CheckSectorFloor(int nSector, int z, int *x, int *y)
{
auto pSector = &sector[nSector];
int nSpeed = pSector->Speed;
if (!nSpeed) {
return;
}
short nFlag = pSector->Flag;
short nAng = nFlag & kAngleMask;
if (z >= sector[nSector].floorz)
{
*x += bcos(nAng, 3) * nSpeed;
*y += bsin(nAng, 3) * nSpeed;
}
else if (nFlag & 0x800)
{
*x += bcos(nAng, 4) * nSpeed;
*y += bsin(nAng, 4) * nSpeed;
}
}
int GetUpAngle(DExhumedActor* pActor1, int nVal, DExhumedActor* pActor2, int ecx)
{
auto pSprite1 = &pActor1->s();
auto pSprite2 = &pActor2->s();
int x = pSprite2->x - pSprite1->x;
int y = pSprite2->y - pSprite1->y;
int ebx = (pSprite2->z + ecx) - (pSprite1->z + nVal);
int edx = (pSprite2->z + ecx) - (pSprite1->z + nVal);
ebx >>= 4;
edx >>= 8;
ebx = -ebx;
ebx -= edx;
int nSqrt = lsqrt(x * x + y * y);
return GetMyAngle(nSqrt, ebx);
}
void InitPushBlocks()
{
nPushBlocks = 0;
}
int GrabPushBlock()
{
if (nPushBlocks >= kMaxPushBlocks) {
return -1;
}
return nPushBlocks++;
}
void CreatePushBlock(int nSector)
{
auto sectp = &sector[nSector];
int nBlock = GrabPushBlock();
int i;
int startwall = sectp->wallptr;
int nWalls = sectp->wallnum;
int xSum = 0;
int ySum = 0;
for (i = 0; i < nWalls; i++)
{
xSum += wall[startwall + i].x;
ySum += wall[startwall + i].y;
}
int xAvg = xSum / nWalls;
int yAvg = ySum / nWalls;
sBlockInfo[nBlock].x = xAvg;
sBlockInfo[nBlock].y = yAvg;
2021-10-22 15:45:06 +00:00
auto pActor = insertActor(nSector, 0);
auto pSprite = &pActor->s();
2021-10-22 15:45:06 +00:00
sBlockInfo[nBlock].pActor = pActor;
pSprite->x = xAvg;
pSprite->y = yAvg;
pSprite->z = sectp->floorz - 256;
pSprite->cstat = 0x8000;
int var_28 = 0;
for (i = 0; i < nWalls; i++)
{
uint32_t xDiff = abs(xAvg - wall[startwall + i].x);
uint32_t yDiff = abs(yAvg - wall[startwall + i].y);
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 > var_28) {
var_28 = nSqrt;
}
}
sBlockInfo[nBlock].field_8 = var_28;
pSprite->clipdist = (var_28 & 0xFF) << 2;
sectp->extra = nBlock;
}
void MoveSector(int nSector, int nAngle, int *nXVel, int *nYVel)
{
if (nSector == -1) {
return;
}
int nXVect, nYVect;
if (nAngle < 0)
{
nXVect = *nXVel;
nYVect = *nYVel;
nAngle = GetMyAngle(nXVect, nYVect);
}
else
{
nXVect = bcos(nAngle, 6);
nYVect = bsin(nAngle, 6);
}
sectortype *pSector = &sector[nSector];
short nBlock = pSector->extra;
short nSectFlag = sector[nSector].Flag;
int nFloorZ = pSector->floorz;
int startwall = pSector->wallptr;
int nWalls = pSector->wallnum;
walltype *pStartWall = &wall[startwall];
short nNextSector = wall[startwall].nextsector;
BlockInfo *pBlockInfo = &sBlockInfo[nBlock];
vec3_t pos;
pos.x = sBlockInfo[nBlock].x;
int x_b = sBlockInfo[nBlock].x;
pos.y = sBlockInfo[nBlock].y;
int y_b = sBlockInfo[nBlock].y;
int nSectorB = nSector;
int nZVal;
int bUnderwater = nSectFlag & kSectUnderwater;
if (nSectFlag & kSectUnderwater)
{
nZVal = pSector->ceilingz;
pos.z = sector[nNextSector].ceilingz + 256;
pSector->ceilingz = sector[nNextSector].ceilingz;
}
else
{
nZVal = pSector->floorz;
pos.z = sector[nNextSector].floorz - 256;
pSector->floorz = sector[nNextSector].floorz;
}
clipmove(&pos, &nSectorB, nXVect, nYVect, pBlockInfo->field_8, 0, 0, CLIPMASK1);
int yvect = pos.y - y_b;
int xvect = pos.x - x_b;
if (nSectorB != nNextSector && nSectorB != nSector)
{
yvect = 0;
xvect = 0;
}
else
{
if (!bUnderwater)
{
pos = { x_b, y_b, nZVal };
clipmove(&pos, &nSectorB, nXVect, nYVect, pBlockInfo->field_8, 0, 0, CLIPMASK1);
int ebx = pos.x;
int ecx = x_b;
int edx = pos.y;
int eax = xvect;
int esi = y_b;
if (eax < 0) {
eax = -eax;
}
ebx -= ecx;
ecx = eax;
eax = ebx;
edx -= esi;
if (eax < 0) {
eax = -eax;
}
if (ecx > eax)
{
xvect = ebx;
}
eax = yvect;
if (eax < 0) {
eax = -eax;
}
ebx = eax;
eax = edx;
if (eax < 0) {
eax = -eax;
}
if (ebx > eax) {
yvect = edx;
}
}
}
// GREEN
if (yvect || xvect)
{
ExhumedSectIterator it(nSector);
while (auto pActor = it.Next())
{
auto sp = &pActor->s();
if (sp->statnum < 99)
{
sp->x += xvect;
sp->y += yvect;
}
else
{
pos.z = sp->z;
if ((nSectFlag & kSectUnderwater) || pos.z != nZVal || sp->cstat & 0x8000)
{
pos.x = sp->x;
pos.y = sp->y;
nSectorB = nSector;
clipmove(&pos, &nSectorB, -xvect, -yvect, 4 * sp->clipdist, 0, 0, CLIPMASK0);
if (validSectorIndex(nSectorB)) {
ChangeActorSect(pActor, nSectorB);
}
}
}
}
2020-10-15 17:10:51 +00:00
it.Reset(nNextSector);
while (auto pActor = it.Next())
{
auto pSprite = &pActor->s();
2021-10-15 22:34:31 +00:00
if (pSprite->statnum >= 99)
{
pos = pSprite->pos;
nSectorB = nNextSector;
clipmove(&pos, &nSectorB,
2021-10-15 22:34:31 +00:00
-xvect - (bcos(nAngle) * (4 * pSprite->clipdist)),
-yvect - (bsin(nAngle) * (4 * pSprite->clipdist)),
4 * pSprite->clipdist, 0, 0, CLIPMASK0);
if (nSectorB != nNextSector && (nSectorB == nSector || nNextSector == nSector))
{
2021-10-15 22:34:31 +00:00
if (nSectorB != nSector || nFloorZ >= pSprite->z)
{
if (validSectorIndex(nSectorB)) {
ChangeActorSect(pActor, nSectorB);
}
}
else
{
movesprite(pActor,
2021-10-15 22:34:31 +00:00
(xvect << 14) + bcos(nAngle) * pSprite->clipdist,
(yvect << 14) + bsin(nAngle) * pSprite->clipdist,
0, 0, 0, CLIPMASK0);
}
}
}
}
for (int i = 0; i < nWalls; i++)
{
dragpoint(startwall, xvect + pStartWall->x, yvect + pStartWall->y);
pStartWall++;
startwall++;
}
pBlockInfo->x += xvect;
pBlockInfo->y += yvect;
}
// loc_163DD
xvect <<= 14;
yvect <<= 14;
if (!(nSectFlag & kSectUnderwater))
{
ExhumedSectIterator it(nSector);
while (auto pActor = it.Next())
{
auto pSprite = &pActor->s();
2021-10-15 22:34:31 +00:00
if (pSprite->statnum >= 99 && nZVal == pSprite->z && !(pSprite->cstat & 0x8000))
{
nSectorB = nSector;
clipmove(&pSprite->pos, &nSectorB, xvect, yvect, 4 * pSprite->clipdist, 5120, -5120, CLIPMASK0);
}
}
}
if (nSectFlag & kSectUnderwater) {
pSector->ceilingz = nZVal;
}
else {
pSector->floorz = nZVal;
}
*nXVel = xvect;
*nYVel = yvect;
/*
Update player position variables, in case the player sprite was moved by a sector,
Otherwise these can be out of sync when used in sound code (before being updated in PlayerFunc()).
Can cause local player sounds to play off-centre.
TODO: Might need to be done elsewhere too?
*/
2021-10-15 22:34:31 +00:00
auto pActor = PlayerList[nLocalPlayer].Actor();
auto pSprite = &pActor->s();
initx = pSprite->x;
inity = pSprite->y;
initz = pSprite->z;
inita = pSprite->ang;
initsect = pSprite->sectnum;
}
2021-10-21 19:10:17 +00:00
void SetQuake(DExhumedActor* pActor, int nVal)
{
2021-10-21 19:10:17 +00:00
auto pSprite = &pActor->s();
int x = pSprite->x;
int y = pSprite->y;
nVal *= 256;
for (int i = 0; i < nTotalPlayers; i++)
{
2021-10-21 20:39:17 +00:00
auto pPlayerActor = PlayerList[i].Actor();
2021-10-21 20:39:17 +00:00
uint32_t xDiff = abs((int32_t)((pPlayerActor->s().x - x) >> 8));
uint32_t yDiff = abs((int32_t)((pPlayerActor->s().y - y) >> 8));
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);
int eax = nVal;
if (nSqrt)
{
eax = eax / nSqrt;
if (eax >= 256)
{
if (eax > 3840) {
eax = 3840;
}
}
else
{
eax = 0;
}
}
if (eax > nQuake[i]) {
nQuake[i] = eax;
}
}
}
2021-10-21 17:57:46 +00:00
Collision AngleChase(DExhumedActor* pActor, DExhumedActor* pActor2, int ebx, int ecx, int push1)
{
2021-10-21 17:57:46 +00:00
auto pSprite = &pActor->s();
int nClipType = pSprite->statnum != 107;
/* bjd - need to handle cliptype to clipmask change that occured in later build engine version */
if (nClipType == 1) {
nClipType = CLIPMASK1;
}
else {
nClipType = CLIPMASK0;
}
int nAngle;
2021-10-21 17:57:46 +00:00
if (pActor2 == nullptr)
{
pSprite->zvel = 0;
nAngle = pSprite->ang;
}
else
{
2021-10-21 17:57:46 +00:00
auto pSprite2 = &pActor2->s();
int nHeight = tileHeight(pSprite2->picnum) * pSprite2->yrepeat * 2;
int nMyAngle = GetMyAngle(pSprite2->x - pSprite->x, pSprite2->y - pSprite->y);
uint32_t xDiff = abs(pSprite2->x - pSprite->x);
uint32_t yDiff = abs(pSprite2->y - pSprite->y);
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);
int var_18 = GetMyAngle(nSqrt, ((pSprite2->z - nHeight) - pSprite->z) >> 8);
int nAngDelta = AngleDelta(pSprite->ang, nMyAngle, 1024);
int nAngDelta2 = abs(nAngDelta);
if (nAngDelta2 > 63)
{
nAngDelta2 = abs(nAngDelta >> 6);
ebx /= nAngDelta2;
if (ebx < 5) {
ebx = 5;
}
}
int nAngDeltaC = abs(nAngDelta);
if (nAngDeltaC > push1)
{
if (nAngDelta >= 0)
nAngDelta = push1;
else
nAngDelta = -push1;
}
nAngle = (nAngDelta + pSprite->ang) & kAngleMask;
int nAngDeltaD = AngleDelta(pSprite->zvel, var_18, 24);
pSprite->zvel = (pSprite->zvel + nAngDeltaD) & kAngleMask;
}
pSprite->ang = nAngle;
int eax = abs(bcos(pSprite->zvel));
int x = ((bcos(nAngle) * ebx) >> 14) * eax;
int y = ((bsin(nAngle) * ebx) >> 14) * eax;
int xshift = x >> 8;
int yshift = y >> 8;
uint32_t sqrtNum = xshift * xshift + yshift * yshift;
if (sqrtNum > INT_MAX)
{
DPrintf(DMSG_WARNING, "%s %d: overflow\n", __func__, __LINE__);
sqrtNum = INT_MAX;
}
int z = bsin(pSprite->zvel) * ksqrt(sqrtNum);
2021-10-21 17:57:46 +00:00
return movesprite(pActor, x >> 2, y >> 2, (z >> 13) + bsin(ecx, -5), 0, 0, nClipType);
}
2021-11-09 16:32:50 +00:00
int GetWallNormal(int nWall)
{
2021-11-17 23:54:40 +00:00
auto delta = wall[nWall].delta();
2021-11-17 23:54:40 +00:00
int nAngle = GetMyAngle(delta.x, delta.y);
return (nAngle + 512) & kAngleMask;
}
void WheresMyMouth(int nPlayer, vec3_t* pos, int *sectnum)
{
2021-09-17 13:52:44 +00:00
auto pActor = PlayerList[nPlayer].Actor();
auto pSprite = &pActor->s();
int height = GetActorHeight(pActor) >> 1;
*sectnum = pSprite->sectnum;
*pos = pSprite->pos;
pos->z -= height;
clipmove(pos, sectnum,
bcos(pSprite->ang, 7),
bsin(pSprite->ang, 7),
5120, 1280, 1280, CLIPMASK1);
}
void InitChunks()
{
nCurChunkNum = 0;
memset(nChunkSprite, 0, sizeof(nChunkSprite));
2021-09-17 13:33:07 +00:00
memset(nBodyGunSprite, 0, sizeof(nBodyGunSprite));
memset(nBodySprite, 0, sizeof(nBodySprite));
nCurBodyNum = 0;
nCurBodyGunNum = 0;
nBodyTotal = 0;
nChunkTotal = 0;
}
2021-09-17 13:33:07 +00:00
DExhumedActor* GrabBodyGunSprite()
{
2021-09-17 13:33:07 +00:00
auto pActor = nBodyGunSprite[nCurBodyGunNum];
spritetype* pSprite;
if (pActor == nullptr)
{
2021-09-17 13:33:07 +00:00
pActor = insertActor(0, 899);
pSprite = &pActor->s();
nBodyGunSprite[nCurBodyGunNum] = pActor;
pSprite->lotag = -1;
pSprite->owner = -1;
}
else
{
pSprite = &pActor->s();
2021-10-24 18:21:27 +00:00
DestroyAnim(pActor);
pSprite->lotag = -1;
pSprite->owner = -1;
}
nCurBodyGunNum++;
if (nCurBodyGunNum >= 50) { // TODO - enum/define
nCurBodyGunNum = 0;
}
pSprite->cstat = 0;
2021-09-17 13:33:07 +00:00
return pActor;
}
DExhumedActor* GrabBody()
{
DExhumedActor* pActor = nullptr;
spritetype* pSprite = nullptr;
do
{
pActor = nBodySprite[nCurBodyNum];
if (pActor == nullptr)
{
pActor = insertActor(0, 899);
pSprite = &pActor->s();
nBodySprite[nCurBodyNum] = pActor;
pSprite->cstat = 0x8000;
}
else
pSprite = &pActor->s();
nCurBodyNum++;
if (nCurBodyNum >= 50) {
nCurBodyNum = 0;
}
} while (pSprite->cstat & 0x101);
if (nBodyTotal < 50) {
nBodyTotal++;
}
pSprite->cstat = 0;
return pActor;
}
DExhumedActor* GrabChunkSprite()
{
auto pActor = nChunkSprite[nCurChunkNum];
if (pActor == nullptr)
{
pActor = insertActor(0, 899);
nChunkSprite[nCurChunkNum] = pActor;
}
else if (pActor->s().statnum)
{
// TODO MonoOut("too many chunks being used at once!\n");
return nullptr;
}
ChangeActorStat(pActor, 899);
nCurChunkNum++;
if (nCurChunkNum >= kMaxMoveChunks)
nCurChunkNum = 0;
if (nChunkTotal < kMaxMoveChunks)
nChunkTotal++;
pActor->s().cstat = 0x80;
return pActor;
}
2021-10-21 19:10:17 +00:00
DExhumedActor* BuildCreatureChunk(DExhumedActor* pSrc, int nPic, bool bSpecial)
{
2021-10-15 22:31:28 +00:00
auto actor = GrabChunkSprite();
2021-10-15 22:31:28 +00:00
if (actor == nullptr) {
2021-10-21 19:10:17 +00:00
return nullptr;
}
2021-10-15 22:31:28 +00:00
auto pSprite = &actor->s();
2021-10-21 19:10:17 +00:00
auto pSrcSpr = &pSrc->s();
2021-10-21 19:10:17 +00:00
pSprite->x = pSrcSpr->x;
pSprite->y = pSrcSpr->y;
pSprite->z = pSrcSpr->z;
2021-10-21 19:10:17 +00:00
ChangeActorSect(actor, pSrcSpr->sectnum);
pSprite->cstat = 0x80;
pSprite->shade = -12;
pSprite->pal = 0;
pSprite->xvel = (RandomSize(5) - 16) << 7;
pSprite->yvel = (RandomSize(5) - 16) << 7;
pSprite->zvel = (-(RandomSize(8) + 512)) << 3;
2021-10-21 19:10:17 +00:00
if (bSpecial)
{
pSprite->xvel *= 4;
pSprite->yvel *= 4;
pSprite->zvel *= 2;
}
pSprite->xrepeat = 64;
pSprite->yrepeat = 64;
pSprite->xoffset = 0;
pSprite->yoffset = 0;
pSprite->picnum = nPic;
pSprite->lotag = runlist_HeadRun() + 1;
pSprite->clipdist = 40;
// GrabTimeSlot(3);
pSprite->extra = -1;
2021-10-21 19:10:17 +00:00
pSprite->owner = runlist_AddRunRec(pSprite->lotag - 1, actor, 0xD0000);
pSprite->hitag = runlist_AddRunRec(NewRun, actor, 0xD0000);
2021-10-21 19:10:17 +00:00
return actor;
}
void AICreatureChunk::Tick(RunListEvent* ev)
{
2021-10-21 19:10:17 +00:00
auto pActor = ev->pObjActor;
if (!pActor) return;
auto pSprite = &pActor->s();
2021-10-20 22:00:26 +00:00
Gravity(pActor);
int nSector = pSprite->sectnum;
pSprite->pal = sector[nSector].ceilingpal;
2021-10-21 18:25:15 +00:00
auto nVal = movesprite(pActor, pSprite->xvel << 10, pSprite->yvel << 10, pSprite->zvel, 2560, -2560, CLIPMASK1);
if (pSprite->z >= sector[nSector].floorz)
{
// re-grab this variable as it may have changed in movesprite(). Note the check above is against the value *before* movesprite so don't change it.
nSector = pSprite->sectnum;
pSprite->xvel = 0;
pSprite->yvel = 0;
pSprite->zvel = 0;
pSprite->z = sector[nSector].floorz;
}
else
{
2021-10-21 18:25:15 +00:00
if (!nVal.type && !nVal.exbits)
return;
int nAngle;
2021-10-21 18:25:15 +00:00
if (nVal.exbits & kHitAux2)
{
pSprite->cstat = 0x8000;
}
else
{
2021-10-21 18:25:15 +00:00
if (nVal.exbits & kHitAux1)
{
pSprite->xvel >>= 1;
pSprite->yvel >>= 1;
pSprite->zvel = -pSprite->zvel;
return;
}
2021-10-21 18:25:15 +00:00
else if (nVal.type == kHitSprite)
{
2021-10-21 18:25:15 +00:00
nAngle = nVal.actor->s().ang;
}
2021-10-21 18:25:15 +00:00
else if (nVal.type == kHitWall)
{
2021-10-21 18:25:15 +00:00
nAngle = GetWallNormal(nVal.index);
}
else
{
return;
}
// loc_16E0C
int nSqrt = lsqrt(((pSprite->yvel >> 10) * (pSprite->yvel >> 10)
+ (pSprite->xvel >> 10) * (pSprite->xvel >> 10)) >> 8);
pSprite->xvel = bcos(nAngle) * (nSqrt >> 1);
pSprite->yvel = bsin(nAngle) * (nSqrt >> 1);
return;
}
}
runlist_DoSubRunRec(pSprite->owner);
runlist_FreeRun(pSprite->lotag - 1);
runlist_SubRunRec(pSprite->hitag);
2021-10-21 18:25:15 +00:00
ChangeActorStat(pActor, 0);
pSprite->hitag = 0;
pSprite->lotag = 0;
}
2021-10-21 19:10:17 +00:00
DExhumedActor* UpdateEnemy(DExhumedActor** ppEnemy)
{
2021-10-21 19:10:17 +00:00
if (*ppEnemy)
{
2021-10-21 19:10:17 +00:00
if (!((*ppEnemy)->s().cstat & 0x101)) {
*ppEnemy = nullptr;
}
}
2021-10-21 19:10:17 +00:00
return *ppEnemy;
}
END_PS_NS