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

1198 lines
30 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
int nPushBlocks;
// TODO - moveme?
2021-11-22 23:20:15 +00:00
sectortype* overridesect;
enum
{
2021-12-07 17:53:02 +00:00
kMaxPushBlocks = 100,
kMaxMoveChunks = 75
};
2021-12-07 17:53:02 +00:00
TObjPtr<DExhumedActor*> nBodySprite[50];
TObjPtr<DExhumedActor*> nChunkSprite[kMaxMoveChunks];
BlockInfo sBlockInfo[kMaxPushBlocks];
2021-12-07 17:53:02 +00:00
TObjPtr<DExhumedActor*> nBodyGunSprite[50];
int nCurBodyGunNum;
Collision loHit, hiHit;
// think this belongs in init.c?
2021-12-07 17:53:02 +00:00
size_t MarkMove()
{
GC::MarkArray(nBodySprite, 50);
GC::MarkArray(nChunkSprite, kMaxMoveChunks);
for(int i = 0; i < nPushBlocks; i++)
GC::Mark(sBlockInfo[i].pActor);
return 50 + kMaxMoveChunks + nPushBlocks;
}
2020-11-29 23:51:56 +00:00
FSerializer& Serialize(FSerializer& arc, const char* keyname, BlockInfo& w, BlockInfo* def)
{
if (arc.BeginObject(keyname))
{
2022-09-10 08:32:39 +00:00
arc("mindist", w.mindist)
2021-10-22 15:45:06 +00:00
("sprite", w.pActor)
2022-09-10 08:32:39 +00:00
("pos", w.pos)
2020-11-29 23:51:56 +00:00
.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)
2021-12-07 17:53:02 +00:00
.Array("bodysprite", nBodySprite, countof(nBodySprite))
("curbodygun", nCurBodyGunNum)
.Array("bodygunsprite", nBodyGunSprite, countof(nBodyGunSprite))
2020-11-29 23:51:56 +00:00
.EndObject();
}
}
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, double walldist)
{
auto pSector = pActor->sector();
double z = pActor->spr.pos.Z;
double z2;
2021-10-21 21:41:54 +00:00
if (loHit.type == kHitSprite)
{
z2 = loHit.actor()->spr.pos.Z;
}
else
{
z2 = pSector->floorz + pSector->Depth;
2021-11-22 21:38:27 +00:00
BFSSectorSearch search(pSector);
2021-11-22 21:38:27 +00:00
sectortype* pTempSect = nullptr;
while (auto pCurSector = search.GetNext())
{
2021-11-22 21:38:27 +00:00
for (auto& wal : wallsofsector(pCurSector))
{
2021-11-22 21:38:27 +00:00
if (wal.twoSided())
{
2021-11-22 21:38:27 +00:00
if (!search.Check(wal.nextSector()))
{
if (IsCloseToWall(pActor->spr.pos, &wal, walldist) != EClose::Outside)
{
2021-11-22 21:38:27 +00:00
search.Add(wal.nextSector());
}
}
}
}
2021-11-22 21:38:27 +00:00
auto pSect2 = pCurSector;
2021-11-22 21:38:27 +00:00
while (pSect2)
{
2021-11-22 21:38:27 +00:00
pTempSect = pSect2;
2021-11-22 22:05:48 +00:00
pSect2 = pSect2->pBelow;
}
double lowestZ = pTempSect->floorz + pTempSect->Depth;
double lowestDiff = lowestZ - z;
if (lowestDiff < 0 && lowestDiff >= -20)
{
z2 = lowestZ;
2021-11-22 21:38:27 +00:00
pSector = pTempSect;
}
}
}
if (z2 < pActor->spr.pos.Z)
{
pActor->spr.pos.Z = z2;
2021-11-22 23:20:15 +00:00
overridesect = pSector;
2022-09-03 08:02:25 +00:00
pActor->vel.Z = 0;
bTouchFloor = true;
2021-10-21 21:41:54 +00:00
return kHitAux2;
}
else
{
return 0;
}
}
2022-09-10 20:16:37 +00:00
Collision movespritez(DExhumedActor* pActor, double z, double height, double clipdist)
{
auto pSector = pActor->sector();
2021-11-22 23:13:30 +00:00
assert(pSector);
2021-11-22 23:20:15 +00:00
overridesect = pSector;
2021-11-22 23:13:30 +00:00
auto pSect2 = pSector;
// backup cstat
2021-12-23 16:04:24 +00:00
auto cstat = pActor->spr.cstat;
2021-12-23 16:04:24 +00:00
pActor->spr.cstat &= ~CSTAT_SPRITE_BLOCK;
2021-11-26 13:26:03 +00:00
Collision nRet;
nRet.setNone();
2021-11-22 23:13:30 +00:00
int nSectFlags = pSector->Flag;
2019-09-25 21:16:12 +00:00
if (nSectFlags & kSectUnderwater) {
2022-09-09 19:52:43 +00:00
z *= 0.5;
}
2022-09-09 19:52:43 +00:00
double spriteZ = pActor->spr.pos.Z;
double floorZ = pSector->floorz;
2022-09-09 19:52:43 +00:00
double destZ = spriteZ + z;
double highestZ = pSector->ceilingz + (height * 0.5);
2022-09-09 19:52:43 +00:00
if ((nSectFlags & kSectUnderwater) && destZ < highestZ) {
destZ = highestZ;
}
// loc_151E7:
2022-09-09 19:52:43 +00:00
while (destZ > pActor->sector()->floorz && pActor->sector()->pBelow != nullptr)
{
ChangeActorSect(pActor, pActor->sector()->pBelow);
}
2021-11-22 23:13:30 +00:00
if (pSect2 != pSector)
{
2022-09-09 19:52:43 +00:00
pActor->spr.pos.Z = destZ;
2021-11-22 23:13:30 +00:00
if (pSect2->Flag & kSectUnderwater)
{
if (pActor == PlayerList[nLocalPlayer].pActor) {
2021-10-21 21:41:54 +00:00
D3PlayFX(StaticSound[kSound2], pActor);
}
2021-12-23 16:04:24 +00:00
if (pActor->spr.statnum <= 107) {
pActor->spr.hitag = 0;
}
}
}
2019-09-25 21:16:12 +00:00
else
{
2022-09-09 19:52:43 +00:00
while ((destZ < pActor->sector()->ceilingz) && (pActor->sector()->pAbove != nullptr))
2019-09-25 21:16:12 +00:00
{
ChangeActorSect(pActor, pActor->sector()->pAbove);
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.
2022-09-09 19:52:43 +00:00
double sprceiling, sprfloor;
auto pos = pActor->spr.pos.plusZ(-1);
getzrange(pos, pActor->sector(), &sprceiling, hiHit, &sprfloor, loHit, 128, CLIPMASK0);
2022-09-09 19:52:43 +00:00
double mySprfloor = sprfloor;
2021-10-21 21:41:54 +00:00
if (loHit.type != kHitSprite) {
mySprfloor += pActor->sector()->Depth;
}
2022-09-09 19:52:43 +00:00
if (destZ > 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-12-23 17:25:58 +00:00
auto pFloorActor = loHit.actor();
2021-12-23 17:25:58 +00:00
if (pActor->spr.statnum == 100 && pFloorActor->spr.statnum != 0 && pFloorActor->spr.statnum < 100)
2019-09-25 21:16:12 +00:00
{
2022-09-09 19:52:43 +00:00
int nDamage = int(z * 0.5);
2019-09-25 21:16:12 +00:00
if (nDamage)
{
2021-11-26 13:26:03 +00:00
runlist_DamageEnemy(loHit.actor(), pActor, nDamage << 1);
}
2022-09-09 19:52:43 +00:00
pActor->vel.Z = -z;
}
2019-09-25 21:16:12 +00:00
else
{
2021-12-23 17:25:58 +00:00
if (pFloorActor->spr.statnum == 0 || pFloorActor->spr.statnum > 199)
{
2021-10-21 21:41:54 +00:00
nRet.exbits |= kHitAux2;
}
else
{
2021-10-21 21:41:54 +00:00
nRet = loHit;
}
2022-09-03 08:02:25 +00:00
pActor->vel.Z = 0;
}
2019-09-25 21:16:12 +00:00
}
else
{
// Path B
if (pActor->sector()->pBelow == nullptr)
{
2021-10-21 21:41:54 +00:00
nRet.exbits |= kHitAux2;
2019-09-25 21:16:12 +00:00
int nSectDamage = pActor->sector()->Damage;
2019-09-25 21:16:12 +00:00
if (nSectDamage != 0)
{
2021-12-23 16:04:24 +00:00
if (pActor->spr.hitag < 15)
2019-09-25 21:16:12 +00:00
{
2021-10-21 21:41:54 +00:00
IgniteSprite(pActor);
2021-12-23 16:04:24 +00:00
pActor->spr.hitag = 20;
2019-09-25 21:16:12 +00:00
}
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
}
}
2022-09-03 08:02:25 +00:00
pActor->vel.Z = 0;
}
}
}
// loc_1543B:
2022-09-09 19:52:43 +00:00
destZ = mySprfloor;
pActor->spr.pos.Z = mySprfloor;
}
else
{
2022-09-09 19:52:43 +00:00
if ((destZ - height) < sprceiling && (hiHit.type == kHitSprite || pActor->sector()->pAbove == nullptr))
{
2022-09-09 19:52:43 +00:00
destZ = sprceiling + height;
2021-10-21 21:41:54 +00:00
nRet.exbits |= kHitAux1;
}
}
2022-09-09 19:52:43 +00:00
if (spriteZ <= floorZ && destZ > floorZ)
{
2021-11-22 23:13:30 +00:00
if ((pSector->Depth != 0) || (pSect2 != pSector && (pSect2->Flag & kSectUnderwater)))
{
2021-11-22 21:38:27 +00:00
BuildSplash(pActor, pSector);
}
}
2021-12-23 16:04:24 +00:00
pActor->spr.cstat = cstat; // restore cstat
2022-09-09 19:52:43 +00:00
pActor->spr.pos.Z = destZ;
2021-12-23 16:04:24 +00:00
if (pActor->spr.statnum == 100)
{
2022-09-10 20:16:37 +00:00
nRet.exbits |= BelowNear(pActor, clipdist * 1.5);
}
return nRet;
}
double GetActorHeight(DExhumedActor* actor)
2021-10-15 22:31:28 +00:00
{
2022-09-10 19:24:03 +00:00
return tileHeight(actor->spr.picnum) * actor->spr.yrepeat * REPEAT_SCALE;
2021-10-15 22:31:28 +00:00
}
DExhumedActor* insertActor(sectortype* s, int st)
2021-10-15 22:31:28 +00:00
{
return static_cast<DExhumedActor*>(::InsertActor(RUNTIME_CLASS(DExhumedActor), s, st));
2021-10-15 22:31:28 +00:00
}
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)
{
DVector2 vect(FixedToFloat<18>(dx), FixedToFloat<18>(dy));
bTouchFloor = false;
2022-08-29 20:30:11 +00:00
auto spos = pActor->spr.pos;
double nSpriteHeight = GetActorHeight(pActor);
auto pSector = pActor->sector();
2021-11-22 23:47:25 +00:00
assert(pSector);
2022-08-29 20:30:11 +00:00
double floorZ = pSector->floorz;
2022-08-29 20:30:11 +00:00
if ((pSector->Flag & kSectUnderwater) || (floorZ < spos.Z))
{
vect *= 0.5;
}
2022-09-10 20:16:37 +00:00
Collision nRet = movespritez(pActor, dz * zinttoworld, nSpriteHeight, pActor->fClipdist());
pSector = pActor->sector(); // modified in movespritez so re-grab this variable
2021-12-23 16:04:24 +00:00
if (pActor->spr.statnum == 100)
{
int nPlayer = GetPlayerFromActor(pActor);
DVector2 thrust(0, 0);
CheckSectorFloor(overridesect, pActor->spr.pos.Z, thrust);
if (!thrust.isZero())
{
PlayerList[nPlayer].nThrust = thrust;
}
vect += PlayerList[nPlayer].nThrust;
}
else
{
CheckSectorFloor(overridesect, pActor->spr.pos.Z, vect);
}
2021-11-26 13:26:03 +00:00
Collision coll;
2022-09-10 20:16:37 +00:00
clipmove(pActor->spr.pos, &pSector, FloatToFixed<18>(vect.X), FloatToFixed<18>(vect.Y), pActor->int_clipdist(), int(nSpriteHeight * zworldtoint), flordist, clipmask, coll);
2021-10-21 21:41:54 +00:00
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;
}
if ((pSector != pActor->sector()) && pSector != nullptr)
{
2021-10-21 21:41:54 +00:00
if (nRet.exbits & kHitAux2) {
dz = 0;
}
2022-08-29 20:30:11 +00:00
if ((pSector->floorz - spos.Z) < (dz + flordist) * zinttoworld)
{
2022-08-29 20:30:11 +00:00
pActor->spr.pos.XY() = spos.XY();
}
else
{
2021-11-22 23:47:25 +00:00
ChangeActorSect(pActor, pSector);
2021-12-23 16:04:24 +00:00
if (pActor->spr.pal < 5 && !pActor->spr.hitag)
{
pActor->spr.pal = pActor->sector()->ceilingpal;
}
}
}
2021-10-21 21:41:54 +00:00
return nRet;
}
void Gravity(DExhumedActor* pActor)
{
if (pActor->sector()->Flag & kSectUnderwater)
{
2021-12-23 16:04:24 +00:00
if (pActor->spr.statnum != 100)
{
if (pActor->vel.Z <= 4)
{
if (pActor->vel.Z < 8) {
2022-09-09 17:29:11 +00:00
pActor->vel.Z += 2;
}
}
else
{
2022-09-09 17:29:11 +00:00
pActor->vel.Z -= 0.25;
}
}
else
{
if (pActor->vel.Z > 0)
{
2022-09-09 17:29:11 +00:00
pActor->vel.Z -= 0.25;
if (pActor->vel.Z < 0) {
2022-09-03 08:02:25 +00:00
pActor->vel.Z = 0;
}
}
else if (pActor->vel.Z < 0)
{
2022-09-09 17:29:11 +00:00
pActor->vel.Z += 0.25;
if (pActor->vel.Z > 0) {
2022-09-03 08:02:25 +00:00
pActor->vel.Z = 0;
}
}
}
}
else
{
2022-09-09 17:29:11 +00:00
pActor->vel.Z += 2;
if (pActor->vel.Z > 64) {
2022-09-09 17:29:11 +00:00
pActor-> vel.Z = 64;
}
}
}
2021-10-21 18:25:15 +00:00
Collision MoveCreature(DExhumedActor* pActor)
{
return movesprite(pActor, pActor->vel, 256., 15360, -5120, CLIPMASK0);
}
2021-10-21 18:25:15 +00:00
Collision MoveCreatureWithCaution(DExhumedActor* pActor)
{
2022-08-16 22:18:47 +00:00
auto oldv = pActor->spr.pos;
auto pSectorPre = pActor->sector();
2022-09-10 14:09:32 +00:00
auto result = MoveCreature(pActor);
auto pSector =pActor->sector();
if (pSector != pSectorPre)
{
2022-09-10 14:09:32 +00:00
double zDiff = abs(pSectorPre->floorz - pSector->floorz);
2022-09-10 14:09:32 +00:00
if (zDiff > 60 || (pSector->Flag & kSectUnderwater) || (pSector->pBelow != nullptr && pSector->pBelow->Flag) || pSector->Damage)
{
2022-08-16 22:18:47 +00:00
pActor->spr.pos = oldv;
2021-11-22 22:35:11 +00:00
ChangeActorSect(pActor, pSectorPre);
2022-09-03 17:18:12 +00:00
pActor->spr.angle += DAngle45;
pActor->VelFromAngle(-2);
2021-11-26 13:26:03 +00:00
Collision c;
c.setNone();
return c;
}
}
2022-09-10 14:09:32 +00:00
return result;
}
2022-09-10 19:30:26 +00:00
DAngle GetAngleToSprite(DExhumedActor* a1, DExhumedActor* a2)
{
if (!a1 || !a2)
2022-09-10 19:30:26 +00:00
return -minAngle;
2022-09-10 19:30:26 +00:00
return VecToAngle(a2->spr.pos - a1->spr.pos);
}
int PlotCourseToSprite(DExhumedActor* pActor1, DExhumedActor* pActor2)
{
if (pActor1 == nullptr || pActor2 == nullptr)
return -1;
2022-08-20 15:07:56 +00:00
auto vect = pActor2->spr.pos.XY() - pActor1->spr.pos.XY();
pActor1->spr.angle = VecToAngle(vect);
return int(vect.Length() * worldtoint);
}
DExhumedActor* FindPlayer(DExhumedActor* pActor, int nDistance, bool dontengage)
{
int var_18 = !dontengage;
if (nDistance < 0)
nDistance = 100;
2022-08-22 16:40:37 +00:00
auto pSector =pActor->sector();
nDistance <<= 4;
DExhumedActor* pPlayerActor = nullptr;
int i = 0;
while (1)
{
if (i >= nTotalPlayers)
return nullptr;
pPlayerActor = PlayerList[i].pActor;
2021-12-23 17:25:58 +00:00
if ((pPlayerActor->spr.cstat & CSTAT_SPRITE_BLOCK_ALL) && (!(pPlayerActor->spr.cstat & CSTAT_SPRITE_INVISIBLE)))
{
2022-08-22 16:40:37 +00:00
int v9 = abs(pPlayerActor->spr.pos.X - pActor->spr.pos.X);
if (v9 < nDistance)
{
2022-08-22 16:40:37 +00:00
int v10 = abs(pPlayerActor->spr.pos.Y - pActor->spr.pos.Y);
if (v10 < nDistance && cansee(pPlayerActor->spr.pos.plusZ(-30), pPlayerActor->sector(), pActor->spr.pos.plusZ(-GetActorHeight(pActor)), pSector))
{
break;
}
}
}
i++;
}
if (var_18) {
PlotCourseToSprite(pActor, pPlayerActor);
}
return pPlayerActor;
}
void CheckSectorFloor(sectortype* pSector, double z, DVector2& xy)
{
int nSpeed = pSector->Speed;
if (!nSpeed) {
return;
}
DAngle nAng = DAngle::fromBuild(pSector->Flag & kAngleMask);
if (z >= pSector->floorz)
{
xy += nAng.ToVector() * nSpeed * 0.5;
}
else if (pSector->Flag & 0x800)
{
xy += nAng.ToVector() * nSpeed;
}
}
void InitPushBlocks()
{
nPushBlocks = 0;
2021-12-07 18:05:40 +00:00
memset(sBlockInfo, 0, sizeof(sBlockInfo));
}
int GrabPushBlock()
{
if (nPushBlocks >= kMaxPushBlocks) {
return -1;
}
return nPushBlocks++;
}
2021-11-22 23:03:18 +00:00
void CreatePushBlock(sectortype* pSector)
{
int nBlock = GrabPushBlock();
2022-09-10 08:32:39 +00:00
DVector2 sum(0, 0);
2021-11-22 23:03:18 +00:00
for (auto& wal : wallsofsector(pSector))
{
2022-09-10 08:32:39 +00:00
sum += wal.pos;
}
2022-09-10 08:32:39 +00:00
DVector2 avg = sum / pSector->wallnum;
2022-09-10 08:32:39 +00:00
sBlockInfo[nBlock].pos = avg;
2021-11-22 23:03:18 +00:00
auto pActor = insertActor(pSector, 0);
2021-10-22 15:45:06 +00:00
sBlockInfo[nBlock].pActor = pActor;
2022-09-10 08:32:39 +00:00
pActor->spr.pos = { avg, pSector->floorz- 1 };
2021-12-23 16:04:24 +00:00
pActor->spr.cstat = CSTAT_SPRITE_INVISIBLE;
double mindist = 0;
2021-11-22 23:03:18 +00:00
for (auto& wal : wallsofsector(pSector))
{
2022-09-10 08:32:39 +00:00
double length = (avg - wal.pos).Length();
2022-09-10 08:32:39 +00:00
if (length > mindist) {
mindist = length;
}
}
2022-09-10 08:32:39 +00:00
sBlockInfo[nBlock].mindist = mindist;
2022-09-09 16:32:42 +00:00
pActor->set_native_clipdist( (int(mindist * worldtoint) & 0xFF) << 2);
2021-11-22 23:03:18 +00:00
pSector->extra = nBlock;
}
2022-09-10 08:32:39 +00:00
2022-09-10 11:29:42 +00:00
void MoveSector(sectortype* pSector, DAngle nAngle, DVector2& nVel)
{
2021-11-22 20:03:10 +00:00
if (pSector == nullptr) {
return;
}
2022-09-10 08:32:39 +00:00
DVector2 nVect;
2022-09-10 08:32:39 +00:00
if (nAngle < nullAngle)
{
2022-09-10 11:29:42 +00:00
nVect = nVel;
2022-09-10 08:32:39 +00:00
nAngle = VecToAngle(nVect);
}
else
{
2022-09-10 08:32:39 +00:00
nVect = nAngle.ToVector() * 4;
}
int nBlock = pSector->extra;
2021-11-22 20:03:10 +00:00
int nSectFlag = pSector->Flag;
2022-09-10 08:32:39 +00:00
double nFloorZ = pSector->floorz;
2021-11-21 08:31:40 +00:00
walltype *pStartWall = pSector->firstWall();
sectortype* pNextSector = pStartWall->nextSector();
BlockInfo *pBlockInfo = &sBlockInfo[nBlock];
2022-09-10 08:32:39 +00:00
DVector3 pos;
2022-09-10 08:32:39 +00:00
pos.XY() = sBlockInfo[nBlock].pos;
auto b_pos = pos.XY();
2022-09-10 08:32:39 +00:00
double nZVal;
int bUnderwater = nSectFlag & kSectUnderwater;
if (nSectFlag & kSectUnderwater)
{
2022-09-10 08:32:39 +00:00
nZVal = pSector->ceilingz;
pos.Z = pNextSector->ceilingz + 1;
pSector->setceilingz(pNextSector->ceilingz);
}
else
{
2022-09-10 08:32:39 +00:00
nZVal = pSector->floorz;
pos.Z = pNextSector->floorz - 1;
2022-08-20 18:25:38 +00:00
pSector->setfloorz(pNextSector->floorz);
}
2021-11-22 20:03:10 +00:00
auto pSectorB = pSector;
2021-11-26 13:26:03 +00:00
Collision scratch;
2022-09-10 08:32:39 +00:00
clipmove(pos, &pSectorB, FloatToFixed<18>(nVect.X), FloatToFixed<18>(nVect.Y), pBlockInfo->mindist, 0, 0, CLIPMASK1, scratch);
2022-09-10 08:32:39 +00:00
auto vect = pos.XY() - b_pos;
2021-11-22 20:03:10 +00:00
if (pSectorB != pNextSector && pSectorB != pSector)
{
2022-09-10 08:32:39 +00:00
vect.Zero();
}
else
{
if (!bUnderwater)
{
2022-09-10 08:32:39 +00:00
pos.XY() = b_pos;
pos.Z = nZVal;
2022-09-10 08:32:39 +00:00
clipmove(pos, &pSectorB, FloatToFixed<18>(nVect.X), FloatToFixed<18>(nVect.Y), pBlockInfo->mindist, 0, 0, CLIPMASK1, scratch);
2022-09-10 08:32:39 +00:00
auto delta = pos.XY() - b_pos;
2022-09-10 08:32:39 +00:00
if (abs(vect.X) > abs(delta.X))
{
2022-09-10 08:32:39 +00:00
vect.X = delta.X;
}
2022-09-10 08:32:39 +00:00
if (abs(vect.Y) > abs(delta.Y))
{
2022-09-10 08:32:39 +00:00
vect.Y = delta.Y;
}
}
}
// GREEN
2022-09-10 08:32:39 +00:00
if (!vect.isZero())
{
2021-11-22 20:03:10 +00:00
ExhumedSectIterator it(pSector);
while (auto pActor = it.Next())
{
2021-12-23 17:25:58 +00:00
if (pActor->spr.statnum < 99)
{
2022-09-10 08:32:39 +00:00
pActor->spr.pos += vect;
}
else
{
2022-09-10 08:32:39 +00:00
pos.Z = pActor->spr.pos.Z;
2021-12-23 17:25:58 +00:00
if ((nSectFlag & kSectUnderwater) || pos.Z != nZVal || pActor->spr.cstat & CSTAT_SPRITE_INVISIBLE)
{
2022-09-10 08:32:39 +00:00
pos.XY() = pActor->spr.pos.XY();
2021-11-22 20:03:10 +00:00
pSectorB = pSector;
2022-09-10 08:32:39 +00:00
// The vector that got passed in here originally was Q28.4, while clipmove expects Q14.18, effectively resulting in actual zero movement
// because the resulting offset would be far below the coordinate's precision.
clipmove(pos, &pSectorB, FloatToFixed<18>(-vect.X / 16384.), FloatToFixed<18>(-vect.Y / 16384), pActor->int_clipdist(), 0, 0, CLIPMASK0, scratch);
2021-11-22 20:03:10 +00:00
if (pSectorB) {
ChangeActorSect(pActor, pSectorB);
}
}
}
}
2021-11-21 08:31:40 +00:00
it.Reset(pNextSector);
while (auto pActor = it.Next())
{
2021-12-23 16:04:24 +00:00
if (pActor->spr.statnum >= 99)
{
2022-09-10 08:32:39 +00:00
pos = pActor->spr.pos;
2021-11-22 20:03:10 +00:00
pSectorB = pNextSector;
2022-09-10 08:32:39 +00:00
// Original used 14 bits of scale from the sine table and 4 bits from clipdist.
// vect was added unscaled, essentially nullifying its effect entirely.
auto vect2 = -nAngle.ToVector() * pActor->fClipdist()/* - vect*/;
clipmove(pos, &pSectorB, FloatToFixed<18>(vect2.X), FloatToFixed<18>(vect2.Y), pActor->int_clipdist(), 0, 0, CLIPMASK0, scratch);
2021-11-22 20:03:10 +00:00
if (pSectorB != pNextSector && (pSectorB == pSector || pNextSector == pSector))
{
2022-09-10 08:32:39 +00:00
if (pSectorB != pSector || nFloorZ >= pActor->spr.pos.Z)
{
2021-11-22 20:03:10 +00:00
if (pSectorB) {
ChangeActorSect(pActor, pSectorB);
}
}
else
{
2022-09-10 08:32:39 +00:00
// Unlike the above, this one *did* scale vect
vect2 = nAngle.ToVector() * pActor->fClipdist() * 0.25 + vect;
movesprite(pActor, FloatToFixed<18>(vect2.X), FloatToFixed<18>(vect2.Y), 0, 0, 0, CLIPMASK0);
}
}
}
}
2021-11-21 08:31:40 +00:00
for(auto& wal : wallsofsector(pSector))
{
2022-09-10 08:32:39 +00:00
dragpoint(&wal, vect + wal.pos);
}
2022-09-10 08:32:39 +00:00
pBlockInfo->pos += vect;
}
// loc_163DD
if (!(nSectFlag & kSectUnderwater))
{
2021-11-22 20:03:10 +00:00
ExhumedSectIterator it(pSector);
while (auto pActor = it.Next())
{
2022-09-10 08:32:39 +00:00
if (pActor->spr.statnum >= 99 && nZVal == pActor->spr.pos.Z && !(pActor->spr.cstat & CSTAT_SPRITE_INVISIBLE))
{
2021-11-22 20:03:10 +00:00
pSectorB = pSector;
2022-09-10 08:32:39 +00:00
clipmove(pActor->spr.pos, &pSectorB, FloatToFixed<18>(vect.X), FloatToFixed<18>(vect.Y), pActor->int_clipdist(), 5120, -5120, CLIPMASK0, scratch);
}
}
}
if (nSectFlag & kSectUnderwater) {
2022-09-10 08:32:39 +00:00
pSector->setceilingz(nZVal);
}
else {
2022-09-10 08:32:39 +00:00
pSector->setfloorz(nZVal);
}
2022-09-10 11:29:42 +00:00
nVel = vect;
/*
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?
*/
auto pActor = PlayerList[nLocalPlayer].pActor;
2022-08-23 21:36:23 +00:00
initpos = pActor->spr.pos;
inita = pActor->spr.angle;
initsectp = pActor->sector();
}
2021-10-21 19:10:17 +00:00
void SetQuake(DExhumedActor* pActor, int nVal)
{
for (int i = 0; i < nTotalPlayers; i++)
{
2022-09-07 07:58:41 +00:00
auto nSqrt = ((PlayerList[i].pActor->spr.pos.XY() - pActor->spr.pos.XY()) * (1. / 16.)).Length();
if (nSqrt)
{
2022-09-10 14:09:32 +00:00
nVal = clamp(int(nVal / nSqrt), 0, 15);
}
2022-09-10 14:09:32 +00:00
if (nVal > nQuake[i])
2022-09-07 07:58:41 +00:00
{
2022-09-10 14:09:32 +00:00
nQuake[i] = nVal;
}
}
}
2022-09-10 13:54:31 +00:00
Collision AngleChase(DExhumedActor* pActor, DExhumedActor* pActor2, int threshold, int zbob, DAngle push1)
{
2021-12-23 16:04:24 +00:00
int nClipType = pActor->spr.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;
}
2022-09-10 13:54:31 +00:00
DAngle nAngle;
2021-10-21 17:57:46 +00:00
if (pActor2 == nullptr)
{
2022-09-10 13:54:31 +00:00
pActor->pitch = nullAngle;
nAngle = pActor->spr.angle;
}
else
{
double nHeight = GetActorHeight(pActor2) / 2;
2022-08-20 15:07:56 +00:00
auto vect = pActor2->spr.pos.XY() - pActor->spr.pos.XY();
2022-09-10 13:54:31 +00:00
DAngle nMyAngle = VecToAngle(vect);
double nSqrt = vect.Length();
DAngle nPitch = VecToAngle(nSqrt, (pActor2->spr.pos.Z - nHeight - pActor->spr.pos.Z) / 16.);
2022-09-10 13:54:31 +00:00
DAngle nAngDelta = deltaangle(pActor->spr.angle, nMyAngle);
2022-09-10 13:54:31 +00:00
if (abs(nAngDelta) >= DAngle22_5 / 2)
{
2022-09-10 13:54:31 +00:00
int nAngDelta2 = abs(nAngDelta.Buildang() >> 6);
2022-09-10 13:54:31 +00:00
threshold /= nAngDelta2;
2022-09-10 13:54:31 +00:00
if (threshold < 5) {
threshold = 5;
}
}
2022-09-10 13:54:31 +00:00
nAngDelta = clamp(nAngDelta, -push1, push1);
nAngle = (nAngDelta + pActor->spr.angle).Normalized360();
auto nPitchDelta = clamp(deltaangle(pActor->pitch, nPitch), -DAngle22_5 / 5, DAngle22_5 / 5);
pActor->pitch = (pActor->pitch + nPitchDelta).Normalized180();
}
2022-09-10 13:54:31 +00:00
pActor->spr.angle = nAngle;
2022-09-10 13:54:31 +00:00
auto cospitch = pActor->pitch.Cos();
2022-09-10 13:54:31 +00:00
auto vec = nAngle.ToVector() * threshold * (1/64.) * cospitch;
auto veclen = vec.Length();
double zz = g_sindeg(pActor->vel.Z * 45) * veclen;
2022-09-10 13:54:31 +00:00
return movesprite(pActor, FloatToFixed<18>(vec.X), FloatToFixed<18>(vec.Y), zz * 4096 + BobVal(zbob) * 512, 0, 0, nClipType);
}
DAngle GetWallNormal(walltype* pWall)
{
return (VecToAngle(pWall->delta()) + DAngle90).Normalized360();
}
DVector3 WheresMyMouth(int nPlayer, sectortype **sectnum)
{
auto pActor = PlayerList[nPlayer].pActor;
double height = GetActorHeight(pActor) * 0.5;
*sectnum = pActor->sector();
2022-09-10 13:54:31 +00:00
auto pos = pActor->spr.pos.plusZ(-height);
2022-09-10 14:09:32 +00:00
auto vect = pActor->spr.angle.ToVector() * 8;
2021-11-26 13:26:03 +00:00
Collision scratch;
2022-09-10 14:09:32 +00:00
clipmove(pos, sectnum, FloatToFixed<18>(vect.X), FloatToFixed<18>(vect.Y), 5120, 1280, 1280, CLIPMASK1, scratch);
return pos;
}
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-12-07 17:53:02 +00:00
DExhumedActor* pActor = nBodyGunSprite[nCurBodyGunNum];
2021-09-17 13:33:07 +00:00
if (pActor == nullptr)
{
2021-09-17 13:33:07 +00:00
pActor = insertActor(0, 899);
nBodyGunSprite[nCurBodyGunNum] = pActor;
2021-12-23 16:04:24 +00:00
pActor->spr.lotag = -1;
pActor->spr.intowner = -1;
}
else
{
2021-10-24 18:21:27 +00:00
DestroyAnim(pActor);
2021-12-23 16:04:24 +00:00
pActor->spr.lotag = -1;
pActor->spr.intowner = -1;
}
nCurBodyGunNum++;
if (nCurBodyGunNum >= 50) { // TODO - enum/define
nCurBodyGunNum = 0;
}
2021-12-23 16:04:24 +00:00
pActor->spr.cstat = 0;
2021-09-17 13:33:07 +00:00
return pActor;
}
DExhumedActor* GrabBody()
{
DExhumedActor* pActor = nullptr;
do
{
pActor = nBodySprite[nCurBodyNum];
if (pActor == nullptr)
{
pActor = insertActor(0, 899);
nBodySprite[nCurBodyNum] = pActor;
2021-12-23 16:04:24 +00:00
pActor->spr.cstat = CSTAT_SPRITE_INVISIBLE;
}
nCurBodyNum++;
if (nCurBodyNum >= 50) {
nCurBodyNum = 0;
}
2021-12-23 16:04:24 +00:00
} while (pActor->spr.cstat & CSTAT_SPRITE_BLOCK_ALL);
if (nBodyTotal < 50) {
nBodyTotal++;
}
2021-12-23 16:04:24 +00:00
pActor->spr.cstat = 0;
return pActor;
}
DExhumedActor* GrabChunkSprite()
{
2021-12-07 17:53:02 +00:00
DExhumedActor* pActor = nChunkSprite[nCurChunkNum];
if (pActor == nullptr)
{
pActor = insertActor(0, 899);
nChunkSprite[nCurChunkNum] = pActor;
}
else if (pActor->spr.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->spr.cstat = CSTAT_SPRITE_YCENTER;
return pActor;
}
2021-10-21 19:10:17 +00:00
DExhumedActor* BuildCreatureChunk(DExhumedActor* pSrc, int nPic, bool bSpecial)
{
auto pActor = GrabChunkSprite();
if (pActor == nullptr) {
2021-10-21 19:10:17 +00:00
return nullptr;
}
2022-08-16 21:46:18 +00:00
pActor->spr.pos = pSrc->spr.pos;
ChangeActorSect(pActor, pSrc->sector());
2021-12-23 16:04:24 +00:00
pActor->spr.cstat = CSTAT_SPRITE_YCENTER;
pActor->spr.shade = -12;
pActor->spr.pal = 0;
2022-09-09 17:24:25 +00:00
pActor->vel.X = ((RandomSize(5) - 16) << 3);
pActor->vel.Y = ((RandomSize(5) - 16) << 3);
pActor->vel.Z = -(RandomSize(8) / 32. + 16);
2021-10-21 19:10:17 +00:00
if (bSpecial)
{
2022-09-03 08:04:16 +00:00
pActor->vel.X *= 4;
pActor->vel.Y *= 4;
pActor->vel.Z *= 2;
}
2021-12-23 16:04:24 +00:00
pActor->spr.xrepeat = 64;
pActor->spr.yrepeat = 64;
pActor->spr.xoffset = 0;
pActor->spr.yoffset = 0;
pActor->spr.picnum = nPic;
pActor->spr.lotag = runlist_HeadRun() + 1;
2022-09-09 16:24:22 +00:00
pActor->set_const_clipdist(40);
// GrabTimeSlot(3);
2021-12-23 16:04:24 +00:00
pActor->spr.extra = -1;
pActor->spr.intowner = runlist_AddRunRec(pActor->spr.lotag - 1, pActor, 0xD0000);
2021-12-23 16:04:24 +00:00
pActor->spr.hitag = runlist_AddRunRec(NewRun, pActor, 0xD0000);
return pActor;
}
void AICreatureChunk::Tick(RunListEvent* ev)
{
2021-10-21 19:10:17 +00:00
auto pActor = ev->pObjActor;
if (!pActor) return;
2021-10-20 22:00:26 +00:00
Gravity(pActor);
auto pSector = pActor->sector();
2021-12-23 16:04:24 +00:00
pActor->spr.pal = pSector->ceilingpal;
auto nVal = movesprite(pActor, pActor->vel, 1024., 2560, -2560, CLIPMASK1);
2022-08-20 18:25:38 +00:00
if (pActor->spr.pos.Z >= pSector->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.
pSector = pActor->sector();
2022-09-03 08:02:25 +00:00
pActor->vel.X = 0;
pActor->vel.Y = 0;
pActor->vel.Z = 0;
pActor->spr.pos.Z = pSector->floorz;
}
else
{
2021-10-21 18:25:15 +00:00
if (!nVal.type && !nVal.exbits)
return;
2022-09-09 17:24:25 +00:00
DAngle nAngle;
2021-10-21 18:25:15 +00:00
if (nVal.exbits & kHitAux2)
{
2021-12-23 16:04:24 +00:00
pActor->spr.cstat = CSTAT_SPRITE_INVISIBLE;
}
else
{
2021-10-21 18:25:15 +00:00
if (nVal.exbits & kHitAux1)
{
2022-09-03 08:04:16 +00:00
pActor->vel.X *= 0.5;
pActor->vel.Y *= 0.5;
2022-09-03 08:05:20 +00:00
pActor->vel.Z = -pActor->vel.Z;
return;
}
2021-10-21 18:25:15 +00:00
else if (nVal.type == kHitSprite)
{
2022-09-09 17:24:25 +00:00
nAngle = nVal.actor()->spr.angle;
}
2021-10-21 18:25:15 +00:00
else if (nVal.type == kHitWall)
{
nAngle = GetWallNormal(nVal.hitWall);
}
else
{
return;
}
// loc_16E0C
2022-09-09 17:24:25 +00:00
double nSqrt = pActor->vel.Length();
2022-09-09 17:24:25 +00:00
pActor->vel.XY() = nAngle.ToVector() * nSqrt * 0.5;
return;
}
}
runlist_DoSubRunRec(pActor->spr.intowner);
2021-12-23 16:04:24 +00:00
runlist_FreeRun(pActor->spr.lotag - 1);
runlist_SubRunRec(pActor->spr.hitag);
2021-10-21 18:25:15 +00:00
ChangeActorStat(pActor, 0);
2021-12-23 16:04:24 +00:00
pActor->spr.hitag = 0;
pActor->spr.lotag = 0;
}
2021-10-21 19:10:17 +00:00
DExhumedActor* UpdateEnemy(DExhumedActor** ppEnemy)
{
2021-10-21 19:10:17 +00:00
if (*ppEnemy)
{
if (!((*ppEnemy)->spr.cstat & CSTAT_SPRITE_BLOCK_ALL)) {
2021-10-21 19:10:17 +00:00
*ppEnemy = nullptr;
}
}
2021-10-21 19:10:17 +00:00
return *ppEnemy;
}
END_PS_NS