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

2570 lines
61 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 "aistuff.h"
#include "exhumed.h"
#include "view.h"
#include "sound.h"
#include "names.h"
#include "sequence.h"
#include "player.h"
#include "interpolate.h"
#include "mapinfo.h"
#include <string.h>
#include <assert.h>
BEGIN_PS_NS
static const int8_t ObjectSeq[] = {
46, -1, 72, -1
};
static const int16_t ObjectStatnum[] = {
kStatExplodeTrigger, kStatExplodeTarget, 98, kStatDestructibleSprite
};
struct Trail
{
int16_t nPoint;
int16_t nVal;
int16_t nPoint2;
};
struct TrailPoint
{
int x;
int y;
2021-11-09 16:46:10 +00:00
uint8_t nTrailPointVal;
int16_t nTrailPointPrev;
int16_t nTrailPointNext;
2020-11-29 23:49:25 +00:00
};
struct Bob
{
2021-11-22 22:40:53 +00:00
sectortype* pSector;
int z;
uint8_t nPhase;
2021-11-09 16:46:10 +00:00
uint8_t field_3;
uint16_t sBobID;
};
struct Drip
{
2021-12-07 17:53:02 +00:00
TObjPtr<DExhumedActor*> pActor;
int16_t nCount;
};
// 56 bytes
struct Elev
{
2021-12-07 17:53:02 +00:00
TObjPtr<DExhumedActor*> pActor;
2021-11-22 22:45:19 +00:00
sectortype* pSector;
int16_t nFlags;
int16_t nChannel;
int nParam1;
int nParam2;
int16_t nCountZOffsets; // count of items in zOffsets
int16_t nCurZOffset;
int zOffsets[8]; // different Z offsets
int16_t nRunRec;
};
// 16 bytes
struct MoveSect
{
2021-11-22 20:03:10 +00:00
sectortype* pSector;
sectortype* pCurSector;
int field_10;
int16_t nTrail;
int16_t nTrailPoint;
int16_t nFlags;
int16_t nChannel; // nChannel?
int16_t sMoveDir;
2020-11-29 23:49:25 +00:00
};
struct wallFace
{
walltype* pWall;
2021-11-21 18:24:46 +00:00
int16_t nChannel;
int16_t count;
int16_t piclist[8];
};
2020-11-29 23:49:25 +00:00
struct slideData
{
2021-12-07 17:53:02 +00:00
TObjPtr<DExhumedActor*> pActor;
int16_t nChannel;
int16_t nStart;
int16_t nRunRec;
int16_t nRunC;
2021-11-22 18:32:47 +00:00
walltype* pStartWall;
walltype* pWall1;
walltype* pWall2;
walltype* pWall3;
int x1;
int y1;
int x2;
int y2;
2021-10-21 10:08:22 +00:00
int x3;
int y3;
int x4;
int y4;
int x5;
int y5;
int x6;
int y6;
};
struct Point
{
2021-11-22 18:32:47 +00:00
sectortype* pSector;
int16_t nNext;
};
struct Trap
{
2021-12-07 17:53:02 +00:00
TObjPtr<DExhumedActor*> pActor;
2021-11-22 18:45:48 +00:00
walltype* pWall1;
walltype* pWall2;
2021-10-21 11:09:29 +00:00
int16_t nState;
int16_t nType;
2021-11-22 18:45:48 +00:00
int16_t nPicnum1;
int16_t nPicnum2;
int16_t nTrapInterval;
2020-11-29 23:49:25 +00:00
};
2020-11-29 23:49:25 +00:00
TArray<Point> PointList;
TArray<Trap> sTrap;
TArray<Bob> sBob;
TArray<Trail> sTrail;
TArray<TrailPoint> sTrailPoint;
TArray<Elev> Elevator;
2021-10-21 15:24:52 +00:00
TArray<DExhumedActor*> ObjectList;
2020-11-29 23:49:25 +00:00
TArray<MoveSect> sMoveSect;
TArray<slideData> SlideData;
TArray<wallFace> WallFace;
TArray<Drip> sDrip;
2021-10-21 13:02:18 +00:00
TArray<DExhumedActor*> EnergyBlocks;
2021-12-07 17:53:02 +00:00
TObjPtr<DExhumedActor*> pFinaleSpr;
size_t MarkObjects()
{
GC::Mark(pFinaleSpr);
for (auto& d : sDrip) GC::Mark(d.pActor);
for (auto& d : sTrap) GC::Mark(d.pActor);
for (auto& d : Elevator) GC::Mark(d.pActor);
for (auto& d : SlideData) GC::Mark(d.pActor);
GC::MarkArray(EnergyBlocks);
return 1 + sDrip.Size() + sTrap.Size() + Elevator.Size() + SlideData.Size();
}
int lFinaleStart;
int nFinaleStage;
int nDronePitch = 0;
int nSmokeSparks = 0;
2020-11-29 23:49:25 +00:00
FSerializer& Serialize(FSerializer& arc, const char* keyname, Trail& w, Trail* def)
{
if (arc.BeginObject(keyname))
{
arc("at0", w.nPoint)
("at2", w.nVal)
("at4", w.nPoint2)
.EndObject();
}
return arc;
2020-11-29 23:49:25 +00:00
}
FSerializer& Serialize(FSerializer& arc, const char* keyname, TrailPoint& w, TrailPoint* def)
{
if (arc.BeginObject(keyname))
{
arc("x", w.x)
("y", w.y)
("val", w.nTrailPointVal)
("next", w.nTrailPointNext)
("prev", w.nTrailPointPrev)
.EndObject();
}
return arc;
2020-11-29 23:49:25 +00:00
}
FSerializer& Serialize(FSerializer& arc, const char* keyname, Bob& w, Bob* def)
{
if (arc.BeginObject(keyname))
{
2021-11-22 22:40:53 +00:00
arc("sector", w.pSector)
("at2", w.nPhase)
("at3", w.field_3)
("z", w.z)
("id", w.sBobID)
.EndObject();
}
return arc;
2020-11-29 23:49:25 +00:00
}
FSerializer& Serialize(FSerializer& arc, const char* keyname, Drip& w, Drip* def)
{
if (arc.BeginObject(keyname))
{
2021-10-21 16:28:06 +00:00
arc("sprite", w.pActor)
("at2", w.nCount)
.EndObject();
}
return arc;
2020-11-29 23:49:25 +00:00
}
FSerializer& Serialize(FSerializer& arc, const char* keyname, Elev& w, Elev* def)
{
if (arc.BeginObject(keyname))
{
arc("at0", w.nFlags)
("channel", w.nChannel)
2021-11-22 22:45:19 +00:00
("sector", w.pSector)
("at6", w.nParam1)
("ata", w.nParam2)
("countz", w.nCountZOffsets)
("curz", w.nCurZOffset)
.Array("zofs", w.zOffsets, 8)
("at32", w.nRunRec)
2021-10-21 21:41:54 +00:00
("sprite", w.pActor)
.EndObject();
}
return arc;
2020-11-29 23:49:25 +00:00
}
FSerializer& Serialize(FSerializer& arc, const char* keyname, MoveSect& w, MoveSect* def)
{
if (arc.BeginObject(keyname))
{
2021-11-22 20:03:10 +00:00
arc("sector", w.pSector)
("trail", w.nTrail)
("trailpoint", w.nTrailPoint)
("at6", w.nFlags)
2021-11-22 20:03:10 +00:00
("at8", w.pCurSector)
("at10", w.field_10)
("at14", w.nChannel)
("movedir", w.sMoveDir)
.EndObject();
}
return arc;
2020-11-29 23:49:25 +00:00
}
2021-10-21 15:24:52 +00:00
2020-11-29 23:49:25 +00:00
FSerializer& Serialize(FSerializer& arc, const char* keyname, wallFace& w, wallFace* def)
{
if (arc.BeginObject(keyname))
{
arc("channel", w.nChannel)
("wall", w.pWall)
2021-11-21 18:24:46 +00:00
("at4", w.count)
.Array("at6", w.piclist, 8)
.EndObject();
}
return arc;
2020-11-29 23:49:25 +00:00
}
FSerializer& Serialize(FSerializer& arc, const char* keyname, slideData& w, slideData* def)
{
if (arc.BeginObject(keyname))
{
2021-11-22 18:32:47 +00:00
arc("at0", w.pStartWall)
("at4", w.pWall1)
("at8", w.pWall2)
("atc", w.pWall3)
("x1", w.x1)
("y1", w.y1)
("x2", w.x2)
("y2", w.y2)
2021-10-21 10:08:22 +00:00
("at20", w.x3)
("at24", w.y3)
("at28", w.x4)
("at2c", w.y4)
("at30", w.x5)
("at34", w.y5)
("at38", w.x6)
("at3c", w.y6)
("channel", w.nChannel)
2021-10-21 10:08:22 +00:00
("at2a", w.nStart)
("at4a", w.nRunRec)
2021-10-21 10:08:22 +00:00
("at6a", w.pActor)
("at8a", w.nRunC)
.EndObject();
}
return arc;
2020-11-29 23:49:25 +00:00
}
FSerializer& Serialize(FSerializer& arc, const char* keyname, Point& w, Point* def)
{
if (arc.BeginObject(keyname))
{
2021-11-22 18:32:47 +00:00
arc("at0", w.pSector)
2021-10-21 10:08:22 +00:00
("ate", w.nNext)
.EndObject();
}
return arc;
2020-11-29 23:49:25 +00:00
}
FSerializer& Serialize(FSerializer& arc, const char* keyname, Trap& w, Trap* def)
{
if (arc.BeginObject(keyname))
{
arc("at0", w.nState)
2021-10-21 11:09:29 +00:00
("sprite", w.pActor)
("type", w.nType)
2021-11-22 18:45:48 +00:00
("wall1", w.pWall1)
("wall2", w.pWall2)
("pic1", w.nPicnum1)
("pic2", w.nPicnum2)
("interval", w.nTrapInterval)
.EndObject();
}
return arc;
2020-11-29 23:49:25 +00:00
}
void SerializeObjects(FSerializer& arc)
{
if (arc.BeginObject("objects"))
{
arc("points", PointList)
("traps", sTrap)
("bob", sBob)
("trails", sTrail)
("trailpoints", sTrailPoint)
("elevators", Elevator)
("objects", ObjectList)
("movesect", sMoveSect)
("slides", SlideData)
("wallface", WallFace)
("drips", sDrip)
("finalestart", lFinaleStart)
2021-10-21 13:02:18 +00:00
("energyblocks", EnergyBlocks)
("finalestage", nFinaleStage)
2021-10-21 11:20:00 +00:00
("finalespr", pFinaleSpr)
("dronepitch", nDronePitch)
("smokesparks", nSmokeSparks)
.EndObject();
}
2020-11-29 23:49:25 +00:00
}
// done
void InitObjects()
{
2020-11-29 23:49:25 +00:00
sTrap.Clear();
sBob.Clear();
sTrail.Clear();
sTrailPoint.Clear();
ObjectList.Clear();
sMoveSect.Clear();
sDrip.Clear();
2021-10-21 13:02:18 +00:00
EnergyBlocks.Clear();
nDronePitch = 0;
nFinaleStage = 0;
lFinaleStart = 0;
nSmokeSparks = 0;
}
// done
void InitElev()
{
2020-11-29 23:49:25 +00:00
Elevator.Clear();
}
// done
2021-11-22 22:45:19 +00:00
DExhumedActor* BuildWallSprite(sectortype* pSector)
{
2021-11-22 22:45:19 +00:00
auto wal = pSector->firstWall();
2021-11-22 22:45:19 +00:00
auto pActor = insertActor(pSector, 401);
pActor->spr.pos = DVector3(wal->fcenter(), (pSector->floorz + pSector->ceilingz) * 0.5);
2021-12-23 16:04:54 +00:00
pActor->spr.cstat = CSTAT_SPRITE_INVISIBLE;
2021-10-20 20:16:50 +00:00
return pActor;
}
// done
DExhumedActor* FindWallSprites(sectortype* pSector)
{
2022-08-30 19:35:00 +00:00
double min_x = DBL_MAX;
double min_y = DBL_MAX;
2022-08-30 19:35:00 +00:00
double max_x = -DBL_MAX;
double max_y = -DBL_MAX;
for (auto& wal : wallsofsector(pSector))
{
2022-08-30 19:35:00 +00:00
if (wal.pos.X < min_x) {
min_x = wal.pos.X;
}
2022-08-30 19:35:00 +00:00
if (max_x < wal.pos.X) {
max_x = wal.pos.X;
}
2022-08-30 19:35:00 +00:00
if (min_y > wal.pos.Y) {
min_y = wal.pos.Y;
}
2022-08-30 19:35:00 +00:00
if (max_y < wal.pos.Y) {
max_y = wal.pos.Y;
}
}
2022-08-30 19:35:00 +00:00
min_y -= 5./16;
max_x += 5./16;
max_y += 5./16;
min_x -= 5./16;
2021-10-20 20:16:50 +00:00
DExhumedActor* pAct = nullptr;
2021-10-20 20:16:50 +00:00
ExhumedSpriteIterator it;
while (auto actor = it.Next())
{
2021-12-23 17:54:40 +00:00
if (actor->spr.lotag == 0)
{
2021-12-23 17:54:40 +00:00
if ((actor->spr.cstat & (CSTAT_SPRITE_ALIGNMENT_WALL | CSTAT_SPRITE_ONE_SIDE)) == (CSTAT_SPRITE_ALIGNMENT_WALL | CSTAT_SPRITE_ONE_SIDE))
{
2022-08-30 19:35:00 +00:00
double act_x = actor->spr.pos.X;
double act_y = actor->spr.pos.Y;
if ((act_x >= min_x) && (max_x >= act_x) && (act_y >= min_y) && (act_y <= max_y))
{
2021-10-20 20:16:50 +00:00
actor->pTarget = pAct;
pAct = actor;
}
}
}
}
2021-10-20 20:16:50 +00:00
if (pAct == nullptr)
{
2021-11-22 22:45:19 +00:00
pAct = insertActor(pSector, 401);
2022-08-30 19:35:00 +00:00
pAct->spr.pos = { (min_x + max_x) / 2, (min_y + max_y) / 2, pSector->floorz };
pAct->spr.cstat = CSTAT_SPRITE_INVISIBLE;
pAct->spr.intowner = -1;
pAct->spr.lotag = 0;
pAct->spr.hitag = 0;
}
2021-10-21 21:41:54 +00:00
return pAct;
}
2022-08-30 19:35:00 +00:00
2021-11-22 22:45:19 +00:00
int BuildElevF(int nChannel, sectortype* pSector, DExhumedActor* nWallSprite, int arg_4, int arg_5, int nCount, ...)
{
2020-11-29 23:49:25 +00:00
auto ElevCount = Elevator.Reserve(1);
Elevator[ElevCount].nFlags = 2;
Elevator[ElevCount].nParam1 = arg_4;
Elevator[ElevCount].nRunRec = -1;
Elevator[ElevCount].nParam2 = arg_5;
Elevator[ElevCount].nChannel = nChannel;
2021-11-22 22:45:19 +00:00
Elevator[ElevCount].pSector = pSector;
Elevator[ElevCount].nCountZOffsets = 0;
Elevator[ElevCount].nCurZOffset = 0;
2021-10-21 21:41:54 +00:00
if (nWallSprite == nullptr) {
2021-11-22 22:45:19 +00:00
nWallSprite = BuildWallSprite(pSector);
}
2021-10-21 21:41:54 +00:00
Elevator[ElevCount].pActor = nWallSprite;
2019-08-31 10:36:26 +00:00
va_list zlist;
va_start(zlist, nCount);
while (1)
{
if (Elevator[ElevCount].nCountZOffsets >= nCount) {
return ElevCount;
}
int nVal = Elevator[ElevCount].nCountZOffsets;
Elevator[ElevCount].nCountZOffsets++;
2019-08-31 10:36:26 +00:00
Elevator[ElevCount].zOffsets[nVal] = va_arg(zlist, int);
}
2019-08-31 10:36:26 +00:00
va_end(zlist);
return ElevCount;
}
2021-11-22 22:45:19 +00:00
int BuildElevC(int arg1, int nChannel, sectortype* pSector, DExhumedActor* nWallSprite, int arg5, int arg6, int nCount, ...)
{
int edi = arg5;
2020-11-29 23:49:25 +00:00
auto ElevCount = Elevator.Reserve(1);
Elevator[ElevCount].nFlags = arg1;
if (arg1 & 4)
{
edi = arg5 / 2;
}
Elevator[ElevCount].nParam1 = edi;
Elevator[ElevCount].nCountZOffsets = 0;
Elevator[ElevCount].nCurZOffset = 0;
Elevator[ElevCount].nParam2 = arg6;
Elevator[ElevCount].nRunRec = -1;
Elevator[ElevCount].nChannel = nChannel;
2021-11-22 22:45:19 +00:00
Elevator[ElevCount].pSector = pSector;
2021-10-21 21:41:54 +00:00
if (nWallSprite == nullptr) {
2021-11-22 22:45:19 +00:00
nWallSprite = BuildWallSprite(pSector);
}
2021-10-21 21:41:54 +00:00
Elevator[ElevCount].pActor = nWallSprite;
2019-08-31 10:36:26 +00:00
va_list zlist;
va_start(zlist, nCount);
while (1)
{
if (Elevator[ElevCount].nCountZOffsets >= nCount) {
return ElevCount;
}
int nVal = Elevator[ElevCount].nCountZOffsets;
Elevator[ElevCount].nCountZOffsets++;
2019-08-31 10:36:26 +00:00
Elevator[ElevCount].zOffsets[nVal] = va_arg(zlist, int);
}
2019-08-31 10:36:26 +00:00
va_end(zlist);
return ElevCount;
}
// TODO - tidy me up
// RENAME param A - not always Z
// Confirmed 100% correct with original .exe
int LongSeek(int* pZVal, int a2, int a3, int a4)
{
int v4; // edx@1
int v5; // ebx@2
v4 = a2 - *pZVal;
if (v4 < 0)
{
v5 = -a3;
if (v5 > v4)
v4 = v5;
(*pZVal) += v4;
}
if (v4 > 0)
{
if (a4 < v4)
v4 = a4;
(*pZVal) += v4;
}
return v4;
}
// done
2021-11-22 22:45:19 +00:00
int CheckSectorSprites(sectortype* pSector, int nVal)
{
int b = 0;
if (nVal)
{
int nZDiff = pSector->int_floorz() - pSector->int_ceilingz();
2021-11-22 22:45:19 +00:00
ExhumedSectIterator it(pSector);
while (auto pActor= it.Next())
{
2021-12-23 16:04:54 +00:00
if ((pActor->spr.cstat & CSTAT_SPRITE_BLOCK_ALL) && (nZDiff < GetActorHeight(pActor)))
{
if (nVal != 1) {
return 1;
}
b = 1;
2021-10-21 22:08:40 +00:00
runlist_DamageEnemy(pActor, nullptr, 5);
2021-12-23 16:04:54 +00:00
if (pActor->spr.statnum == 100 && PlayerList[GetPlayerFromActor(pActor)].nHealth <= 0)
{
PlayFXAtXYZ(StaticSound[kSoundJonFDie],
pActor->spr.pos,
2021-11-22 20:37:33 +00:00
CHANF_NONE, 0x4000);
}
}
}
}
else
{
2021-11-22 22:45:19 +00:00
ExhumedSectIterator it(pSector);
while (auto pActor = it.Next())
{
if (pActor->spr.cstat & CSTAT_SPRITE_BLOCK_ALL) {
return 1;
}
}
b = 0;
}
return b;
}
// done
void MoveSectorSprites(sectortype* pSector, double z)
{
double newz = pSector->floorz;
double oldz = newz - z;
double minz = min(newz, oldz);
double maxz = max(newz, oldz);
2021-11-22 22:40:53 +00:00
ExhumedSectIterator it(pSector);
while (auto pActor = it.Next())
{
double actz = pActor->spr.pos.Z;
if ((pActor->spr.statnum != 200 && actz >= minz && actz <= maxz) || pActor->spr.statnum >= 900)
{
pActor->spr.pos.Z = newz;
}
}
}
2021-10-21 21:41:54 +00:00
void StartElevSound(DExhumedActor* pActor, int nVal)
{
int nSound;
if (nVal & 2) {
nSound = nElevSound;
}
else {
nSound = nStoneSound;
}
2021-10-21 21:41:54 +00:00
D3PlayFX(StaticSound[nSound], pActor);
}
void AIElev::ProcessChannel(RunListEvent* ev)
{
int nRun = ev->nRun;
int nElev = RunData[nRun].nObjIndex;
2020-11-29 23:49:25 +00:00
assert(nElev >= 0 && nElev < (int)Elevator.Size());
int nChannel = Elevator[nElev].nChannel;
int nFlags = Elevator[nElev].nFlags;
assert(nChannel >= 0 && nChannel < kMaxChannels);
// int ax = var_18 & 8;
int dx = sRunChannels[nChannel].c;
int edi = 999; // FIXME CHECKME - this isn't default set to anything in the ASM that I can see - if ax is 0 and var_24 is 0, this will never be set to a known value otherwise!
if (nFlags & 0x8)
{
if (dx) {
edi = 1;
}
else {
edi = 0;
}
}
else
{
// loc_20D48:
if (nFlags & 0x10) // was var_24
{
if (Elevator[nElev].nRunRec < 0)
{
Elevator[nElev].nRunRec = runlist_AddRunRec(NewRun, &RunData[nRun]);
StartElevSound(Elevator[nElev].pActor, nFlags);
edi = 1;
}
}
else
{
if (dx < 0) {
edi = 0;
}
else
{
if (dx == Elevator[nElev].nCurZOffset || dx >= Elevator[nElev].nCountZOffsets)
{
edi = 1;
}
else
{
Elevator[nElev].nCurZOffset = sRunChannels[nChannel].c;
edi = 1;
}
}
}
}
assert(edi != 999);
// loc_20DF9:
if (edi)
{
if (Elevator[nElev].nRunRec < 0)
{
Elevator[nElev].nRunRec = runlist_AddRunRec(NewRun, &RunData[nRun]);
StartElevSound(Elevator[nElev].pActor, nFlags);
}
}
else
{
//loc_20E4E:
if (Elevator[nElev].nRunRec >= 0)
{
runlist_SubRunRec(Elevator[nElev].nRunRec);
Elevator[nElev].nRunRec = -1;
}
}
}
void AIElev::Tick(RunListEvent* ev)
{
int nRun = ev->nRun;
int nElev = RunData[nRun].nObjIndex;
assert(nElev >= 0 && nElev < (int)Elevator.Size());
int nChannel = Elevator[nElev].nChannel;
int var_18 = Elevator[nElev].nFlags;
assert(nChannel >= 0 && nChannel < kMaxChannels);
2021-11-22 22:45:19 +00:00
auto pSector =Elevator[nElev].pSector;
2021-12-07 17:53:02 +00:00
DExhumedActor* pElevSpr = Elevator[nElev].pActor;
int ebp = 0; // initialise to *something*
if (var_18 & 0x2)
{
int nZOffset = Elevator[nElev].nCurZOffset;
int nZVal = Elevator[nElev].zOffsets[nZOffset];
2021-11-22 22:45:19 +00:00
StartInterpolation(pSector, Interp_Sect_Floorz);
int fz = pSector->int_floorz();
int nVal = LongSeek(&fz, nZVal, Elevator[nElev].nParam1, Elevator[nElev].nParam2);
pSector->set_int_floorz(fz);
ebp = nVal;
if (!nVal)
{
if (var_18 & 0x10)
{
Elevator[nElev].nCurZOffset ^= 1;
2021-10-21 21:41:54 +00:00
StartElevSound(pElevSpr, var_18);
}
else
{
2021-10-20 20:16:50 +00:00
StopActorSound(pElevSpr);
runlist_SubRunRec(nRun);
Elevator[nElev].nRunRec = -1;
runlist_ReadyChannel(nChannel);
2021-10-21 21:41:54 +00:00
D3PlayFX(StaticSound[nStopSound], Elevator[nElev].pActor);
}
}
else
{
MoveSectorSprites(pSector, nVal * inttoworld);
2021-11-22 22:45:19 +00:00
if (nVal < 0 && CheckSectorSprites(pSector, 2))
{
runlist_ChangeChannel(nChannel, sRunChannels[nChannel].c == 0);
return;
}
}
}
else
{
// loc_20FC3:
int ceilZ = pSector->int_ceilingz();
int nZOffset = Elevator[nElev].nCurZOffset;
int zVal = Elevator[nElev].zOffsets[nZOffset];
2021-11-22 22:45:19 +00:00
StartInterpolation(pSector, Interp_Sect_Ceilingz);
int nVal = LongSeek(&ceilZ, zVal, Elevator[nElev].nParam1, Elevator[nElev].nParam2);
ebp = nVal;
if (!nVal)
{
if (var_18 & 0x10)
{
Elevator[nElev].nCurZOffset ^= 1;
2021-10-21 21:41:54 +00:00
StartElevSound(Elevator[nElev].pActor, var_18);
}
else
{
runlist_SubRunRec(nRun);
Elevator[nElev].nRunRec = -1;
2021-10-21 21:41:54 +00:00
StopActorSound(Elevator[nElev].pActor);
D3PlayFX(StaticSound[nStopSound], Elevator[nElev].pActor);
runlist_ReadyChannel(nChannel);
}
return;
}
else if (nVal > 0)
{
if (ceilZ == zVal)
{
if (var_18 & 0x4) {
2021-10-20 20:16:50 +00:00
SetQuake(pElevSpr, 30);
}
PlayFXAtXYZ(StaticSound[kSound26], pElevSpr->spr.pos);
}
if (var_18 & 0x4)
{
2021-11-22 22:45:19 +00:00
if (CheckSectorSprites(pSector, 1)) {
return;
}
}
else
{
2021-11-22 22:45:19 +00:00
if (CheckSectorSprites(pSector, 0))
{
runlist_ChangeChannel(nChannel, sRunChannels[nChannel].c == 0);
return;
}
}
}
2021-11-22 22:45:19 +00:00
StartInterpolation(pSector, Interp_Sect_Ceilingz);
pSector->set_int_ceilingz(ceilZ);
}
// maybe this doesn't go here?
2021-10-20 20:16:50 +00:00
while (pElevSpr)
{
pElevSpr->add_int_z(ebp);
2021-10-20 20:16:50 +00:00
pElevSpr = pElevSpr->pTarget;
}
}
// done
void InitWallFace()
{
2020-11-29 23:49:25 +00:00
WallFace.Clear();
}
int BuildWallFace(int nChannel, walltype* pWall, int nCount, ...)
{
2020-11-29 23:49:25 +00:00
auto WallFaceCount = WallFace.Reserve(1);
2021-11-21 18:24:46 +00:00
WallFace[WallFaceCount].count = 0;
WallFace[WallFaceCount].pWall = pWall;
WallFace[WallFaceCount].nChannel = nChannel;
if (nCount > 8) {
nCount = 8;
}
2019-08-31 10:36:26 +00:00
va_list piclist;
va_start(piclist, nCount);
2021-11-21 18:24:46 +00:00
while (WallFace[WallFaceCount].count < nCount)
{
2021-11-21 18:24:46 +00:00
int i = WallFace[WallFaceCount].count;
WallFace[WallFaceCount].count++;
2021-11-21 18:24:46 +00:00
WallFace[WallFaceCount].piclist[i] = (int16_t)va_arg(piclist, int);
}
2019-08-31 10:36:26 +00:00
va_end(piclist);
return WallFaceCount;
}
void AIWallFace::ProcessChannel(RunListEvent* ev)
{
int nWallFace = RunData[ev->nRun].nObjIndex;
2020-11-29 23:49:25 +00:00
assert(nWallFace >= 0 && nWallFace < (int)WallFace.Size());
2021-11-21 18:24:46 +00:00
int16_t nChannel = WallFace[nWallFace].nChannel;
2021-11-21 18:24:46 +00:00
int16_t si = sRunChannels[nChannel].c;
2021-11-21 18:24:46 +00:00
if ((si <= WallFace[nWallFace].count) && (si >= 0))
{
WallFace[nWallFace].pWall->picnum = WallFace[nWallFace].piclist[si];
}
}
// done
void InitPoint()
{
2020-11-29 23:49:25 +00:00
PointList.Clear();
}
// done
int GrabPoint()
{
2020-11-29 23:49:25 +00:00
return PointList.Reserve(1);
}
// done
void InitSlide()
{
2020-11-29 23:49:25 +00:00
SlideData.Clear();
}
2021-11-22 18:32:47 +00:00
int BuildSlide(int nChannel, walltype* pStartWall, walltype* pWall1, walltype* p2ndLastWall, walltype* pWall2, walltype* pWall3, walltype* pWall4)
{
2020-11-29 23:49:25 +00:00
auto nSlide = SlideData.Reserve(1);
2021-11-22 18:32:47 +00:00
auto pSector = pStartWall->sectorp();
SlideData[nSlide].nRunRec = -1;
2020-11-29 23:49:25 +00:00
SlideData[nSlide].nChannel = nChannel;
2021-10-21 10:08:22 +00:00
SlideData[nSlide].nStart = -1;
int nPoint = GrabPoint();
2021-10-21 10:08:22 +00:00
SlideData[nSlide].nStart = nPoint;
2021-10-21 10:08:22 +00:00
PointList[nPoint].nNext = -1;
2021-11-22 18:32:47 +00:00
PointList[nPoint].pSector = pSector;
2021-11-22 18:32:47 +00:00
for(auto& wal : wallsofsector(pSector))
{
int ax = SlideData[nSlide].nStart;
if (ax >= 0)
{
while (ax >= 0)
{
2021-11-22 18:32:47 +00:00
if (wal.nextSector() == PointList[ax].pSector) {
break;
}
2021-10-21 10:08:22 +00:00
ax = PointList[ax].nNext;
}
}
else
{
2021-11-22 18:32:47 +00:00
if (wal.twoSided())
{
nPoint = GrabPoint();
2021-10-21 10:08:22 +00:00
PointList[nPoint].nNext = SlideData[nSlide].nStart;
2021-11-22 18:32:47 +00:00
PointList[nPoint].pSector = wal.nextSector();
2021-10-21 10:08:22 +00:00
SlideData[nSlide].nStart = nPoint;
}
}
}
2021-11-22 18:32:47 +00:00
SlideData[nSlide].pStartWall = pStartWall;
SlideData[nSlide].pWall1 = pWall1;
SlideData[nSlide].pWall2 = pWall2;
SlideData[nSlide].pWall3 = pWall3;
SlideData[nSlide].x1 = pStartWall->wall_int_pos().X;
SlideData[nSlide].y1 = pStartWall->wall_int_pos().Y;
SlideData[nSlide].x2 = pWall2->wall_int_pos().X;
SlideData[nSlide].y2 = pWall2->wall_int_pos().Y;
SlideData[nSlide].x3 = pWall1->wall_int_pos().X;
SlideData[nSlide].y3 = pWall1->wall_int_pos().Y;
SlideData[nSlide].x4 = pWall3->wall_int_pos().X;
SlideData[nSlide].y4 = pWall3->wall_int_pos().Y;
SlideData[nSlide].x5 = p2ndLastWall->wall_int_pos().X;
SlideData[nSlide].y5 = p2ndLastWall->wall_int_pos().Y;
SlideData[nSlide].x6 = pWall4->wall_int_pos().X;
SlideData[nSlide].y6 = pWall4->wall_int_pos().Y;
2021-11-22 18:32:47 +00:00
StartInterpolation(pStartWall, Interp_Wall_X);
StartInterpolation(pStartWall, Interp_Wall_Y);
2021-11-22 18:32:47 +00:00
StartInterpolation(pWall1, Interp_Wall_X);
StartInterpolation(pWall1, Interp_Wall_Y);
2021-11-22 18:32:47 +00:00
StartInterpolation(pWall2, Interp_Wall_X);
StartInterpolation(pWall2, Interp_Wall_Y);
2021-11-22 18:32:47 +00:00
StartInterpolation(pWall3, Interp_Wall_X);
StartInterpolation(pWall3, Interp_Wall_Y);
2021-11-22 18:32:47 +00:00
auto pActor = insertActor(pSector, 899);
2021-10-21 10:08:22 +00:00
SlideData[nSlide].pActor = pActor;
2021-12-23 16:04:54 +00:00
pActor->spr.cstat = CSTAT_SPRITE_INVISIBLE;
pActor->spr.pos = DVector3(pStartWall->pos, pSector->floorz);
pActor->backuppos();
SlideData[nSlide].nRunC = 0;
return nSlide;
}
void AISlide::ProcessChannel(RunListEvent* ev)
{
int nRun = ev->nRun;
int nSlide = RunData[nRun].nObjIndex;
2020-11-29 23:49:25 +00:00
assert(nSlide >= 0 && nSlide < (int)SlideData.Size());
int nChannel = SlideData[nSlide].nChannel;
if (SlideData[nSlide].nRunRec >= 0)
{
runlist_SubRunRec(SlideData[nSlide].nRunRec);
SlideData[nSlide].nRunRec = -1;
}
if (sRunChannels[nChannel].c && sRunChannels[nChannel].c != 1) {
return;
}
SlideData[nSlide].nRunRec = runlist_AddRunRec(NewRun, &RunData[nRun]);
if (SlideData[nSlide].nRunC != sRunChannels[nChannel].c)
{
2021-10-21 10:08:22 +00:00
D3PlayFX(StaticSound[kSound23], SlideData[nSlide].pActor);
SlideData[nSlide].nRunC = sRunChannels[nChannel].c;
}
}
void AISlide::Tick(RunListEvent* ev)
{
int nRun = ev->nRun;
int nSlide = RunData[nRun].nObjIndex;
assert(nSlide >= 0 && nSlide < (int)SlideData.Size());
int nChannel = SlideData[nSlide].nChannel;
int ebp = 0;
int cx = sRunChannels[nChannel].c;
int clipmask = ebp + 1; // RENAME
if (cx == 1)
{
2021-11-22 18:32:47 +00:00
auto pWall = SlideData[nSlide].pWall1;
int x = pWall->wall_int_pos().X;
int y = pWall->wall_int_pos().Y;
2021-10-21 10:08:22 +00:00
int nSeekA = LongSeek(&x, SlideData[nSlide].x5, 20, 20);
int var_34 = nSeekA;
int var_20 = nSeekA;
2021-10-21 10:08:22 +00:00
int nSeekB = LongSeek(&y, SlideData[nSlide].y5, 20, 20);
int var_2C = nSeekB;
int var_24 = nSeekB;
2021-11-22 18:32:47 +00:00
dragpoint(SlideData[nSlide].pWall1, x, y);
2021-10-21 10:08:22 +00:00
movesprite(SlideData[nSlide].pActor, var_34 << 14, var_2C << 14, 0, 0, 0, CLIPMASK1);
if (var_34 == 0)
{
if (var_2C == 0)
{
ebp = clipmask;
}
}
2021-11-22 18:32:47 +00:00
pWall = SlideData[nSlide].pStartWall;
y = pWall->wall_int_pos().Y + var_24;
x = pWall->wall_int_pos().X + var_20;
2021-11-22 18:32:47 +00:00
dragpoint(SlideData[nSlide].pStartWall, x, y);
2021-11-22 18:32:47 +00:00
pWall = SlideData[nSlide].pWall3;
x = pWall->wall_int_pos().X;
y = pWall->wall_int_pos().Y;
2021-10-21 10:08:22 +00:00
int nSeekC = LongSeek(&x, SlideData[nSlide].x6, 20, 20);
int var_30 = nSeekC;
var_20 = nSeekC;
2021-10-21 10:08:22 +00:00
int nSeekD = LongSeek(&y, SlideData[nSlide].y6, 20, 20);
int edi = nSeekD;
var_24 = nSeekD;
2021-11-22 18:32:47 +00:00
dragpoint(SlideData[nSlide].pWall3, x, y);
if (var_30 == 0 && edi == 0) {
ebp++;
}
2021-11-22 18:32:47 +00:00
pWall = SlideData[nSlide].pWall2;
x = pWall->wall_int_pos().X + var_20;
y = pWall->wall_int_pos().Y + var_24;
2021-11-22 18:32:47 +00:00
dragpoint(SlideData[nSlide].pWall2, x, y);
}
else if (cx == 0) // right branch
{
2021-11-22 18:32:47 +00:00
auto pWall = SlideData[nSlide].pStartWall;
int x = pWall->wall_int_pos().X;
int y = pWall->wall_int_pos().Y;
int nSeekA = LongSeek(&x, SlideData[nSlide].x1, 20, 20);
int edi = nSeekA;
int var_1C = nSeekA;
int nSeekB = LongSeek(&y, SlideData[nSlide].y1, 20, 20);
int ecx = nSeekB;
int var_28 = nSeekB;
2021-11-22 18:32:47 +00:00
dragpoint(SlideData[nSlide].pStartWall, x, y);
if (edi == 0 && ecx == 0) {
ebp = clipmask;
}
2021-11-22 18:32:47 +00:00
pWall = SlideData[nSlide].pWall1;
y = pWall->wall_int_pos().Y + var_28;
x = pWall->wall_int_pos().X + var_1C;
2021-11-22 18:32:47 +00:00
dragpoint(SlideData[nSlide].pWall1, x, y);
2021-11-22 18:32:47 +00:00
pWall = SlideData[nSlide].pWall2;
x = pWall->wall_int_pos().X;
y = pWall->wall_int_pos().Y;
int nSeekC = LongSeek(&x, SlideData[nSlide].x2, 20, 20);
edi = nSeekC;
var_1C = nSeekC;
int nSeekD = LongSeek(&y, SlideData[nSlide].y2, 20, 20);
ecx = nSeekD;
var_28 = nSeekD;
2021-11-22 18:32:47 +00:00
dragpoint(SlideData[nSlide].pWall2, x, y);
if (edi == 0 && ecx == 0) {
ebp++;
}
2021-11-22 18:32:47 +00:00
pWall = SlideData[nSlide].pWall3;
y = pWall->wall_int_pos().Y + var_28;
x = pWall->wall_int_pos().X + var_1C;
2021-11-22 18:32:47 +00:00
dragpoint(SlideData[nSlide].pWall3, x, y);
}
// loc_21A51:
if (ebp >= 2)
{
runlist_SubRunRec(SlideData[nSlide].nRunRec);
SlideData[nSlide].nRunRec = -1;
2021-10-21 10:08:22 +00:00
D3PlayFX(StaticSound[nStopSound], SlideData[nSlide].pActor);
runlist_ReadyChannel(nChannel);
}
}
2021-10-21 11:09:29 +00:00
int BuildTrap(DExhumedActor* pActor, int edx, int ebx, int ecx)
{
int var_14 = edx;
int var_18 = ebx;
int var_10 = ecx;
2020-11-29 23:49:25 +00:00
int nTrap = sTrap.Reserve(1);
sTrap[nTrap] = {};
2021-10-21 11:09:29 +00:00
ChangeActorStat(pActor, 0);
2021-12-23 16:04:54 +00:00
pActor->spr.cstat = CSTAT_SPRITE_INVISIBLE;
pActor->spr.xvel = 0;
pActor->spr.yvel = 0;
pActor->spr.zvel = 0;
pActor->spr.extra = -1;
2021-12-23 16:04:54 +00:00
pActor->spr.lotag = runlist_HeadRun() + 1;
pActor->spr.hitag = runlist_AddRunRec(NewRun, nTrap, 0x1F0000);
pActor->spr.intowner = runlist_AddRunRec(pActor->spr.lotag - 1, nTrap, 0x1F0000);
// GrabTimeSlot(3);
2021-10-21 11:09:29 +00:00
sTrap[nTrap].pActor = pActor;
sTrap[nTrap].nType = (var_14 == 0) + 14;
sTrap[nTrap].nState = -1;
2020-11-29 23:49:25 +00:00
sTrap[nTrap].nTrapInterval = 64 - (2 * var_10);
if (sTrap[nTrap].nTrapInterval < 5) {
sTrap[nTrap].nTrapInterval = 5;
}
sTrap[nTrap].nPicnum2 = 0;
2021-11-22 18:45:48 +00:00
sTrap[nTrap].nPicnum1 = 0;
if (var_18 == -1) {
return nTrap;
}
auto pSector = pActor->sector();
for(auto& wal : wallsofsector(pSector))
{
2021-11-22 18:45:48 +00:00
if (var_18 == wal.hitag)
{
2021-11-22 18:45:48 +00:00
if (sTrap[nTrap].pWall1 != nullptr)
{
2021-11-22 18:45:48 +00:00
sTrap[nTrap].pWall2 = &wal;
sTrap[nTrap].nPicnum2 = wal.picnum;
2021-10-21 11:09:29 +00:00
break;
}
else
{
2021-11-22 18:45:48 +00:00
sTrap[nTrap].pWall1 = &wal;
sTrap[nTrap].nPicnum1 = wal.picnum;
}
}
}
pActor->backuppos();
2021-10-21 11:09:29 +00:00
return nTrap;
}
void AITrap::ProcessChannel(RunListEvent* ev)
{
int nChannel = ev->nParam & 0x3FFF;
int nTrap = RunData[ev->nRun].nObjIndex;
if (sRunChannels[nChannel].c > 0)
{
sTrap[nTrap].nState = 12;
}
else
{
sTrap[nTrap].nState = -1;
}
}
void AITrap::Tick(RunListEvent* ev)
{
int nTrap = RunData[ev->nRun].nObjIndex;
2021-12-07 17:53:02 +00:00
DExhumedActor* pActor = sTrap[nTrap].pActor;
if (!pActor) return;
if (sTrap[nTrap].nState >= 0)
{
sTrap[nTrap].nState--;
if (sTrap[nTrap].nState > 10) {
return;
}
int nType = sTrap[nTrap].nType;
if (sTrap[nTrap].nState == 0)
{
sTrap[nTrap].nState = sTrap[nTrap].nTrapInterval;
if (nType == 14)
{
2021-11-22 18:45:48 +00:00
auto pWall = sTrap[nTrap].pWall1;
if (pWall)
{
2021-11-22 18:45:48 +00:00
pWall->picnum = sTrap[nTrap].nPicnum1;
}
2021-11-22 18:45:48 +00:00
pWall = sTrap[nTrap].pWall1;
if (pWall)
{
2021-11-22 18:45:48 +00:00
pWall->picnum = sTrap[nTrap].nPicnum2;
}
}
}
else
{
// loc_21D92:
if (sTrap[nTrap].nState != 5) {
return;
}
2022-08-16 21:17:01 +00:00
auto pBullet = BuildBullet(pActor, nType, 0, pActor->int_ang(), nullptr, 1);
2021-10-19 22:26:26 +00:00
if (pBullet)
{
if (nType == 15)
{
2022-08-16 21:21:10 +00:00
pBullet->set_int_ang((pBullet->int_ang() - 512) & kAngleMask);
2021-10-19 22:26:26 +00:00
D3PlayFX(StaticSound[kSound32], pBullet);
}
else
{
pBullet->spr.clipdist = 50;
2021-11-22 18:45:48 +00:00
auto pWall = sTrap[nTrap].pWall1;
if (pWall)
{
2021-11-22 18:45:48 +00:00
pWall->picnum = sTrap[nTrap].nPicnum1 + 1;
}
2021-11-22 18:45:48 +00:00
pWall = sTrap[nTrap].pWall2;
if (pWall)
{
2021-11-22 18:45:48 +00:00
pWall->picnum = sTrap[nTrap].nPicnum2;
}
2021-10-19 22:26:26 +00:00
D3PlayFX(StaticSound[kSound36], pBullet);
}
}
}
}
}
2021-10-21 11:09:29 +00:00
int BuildArrow(DExhumedActor* nSprite, int nVal)
{
return BuildTrap(nSprite, 0, -1, nVal);
}
2021-10-21 11:09:29 +00:00
int BuildFireBall(DExhumedActor* nSprite, int a, int b)
{
return BuildTrap(nSprite, 1, a, b);
}
2021-10-21 11:15:47 +00:00
DExhumedActor* BuildSpark(DExhumedActor* pActor, int nVal)
{
auto pSpark = insertActor(pActor->sector(), 0);
2022-08-29 17:27:52 +00:00
pSpark->spr.pos.XY() = pActor->spr.pos.XY();
2021-12-23 17:54:40 +00:00
pSpark->spr.cstat = 0;
pSpark->spr.shade = -127;
pSpark->spr.pal = 1;
pSpark->spr.xoffset = 0;
pSpark->spr.yoffset = 0;
pSpark->spr.xrepeat = 50;
pSpark->spr.yrepeat = 50;
if (nVal >= 2)
{
2021-12-23 17:54:40 +00:00
pSpark->spr.picnum = kEnergy2;
nSmokeSparks++;
if (nVal == 3)
{
2021-12-23 17:54:40 +00:00
pSpark->spr.xrepeat = 120;
pSpark->spr.yrepeat = 120;
}
else
{
2021-12-23 17:54:40 +00:00
pSpark->spr.xrepeat = pActor->spr.xrepeat + 15;
pSpark->spr.yrepeat = pActor->spr.xrepeat + 15;
}
}
else
{
2022-08-16 21:15:49 +00:00
int nAngle = (pActor->int_ang() + 256) - RandomSize(9);
if (nVal)
{
2021-12-23 17:54:40 +00:00
pSpark->spr.xvel = bcos(nAngle, -5);
pSpark->spr.yvel = bsin(nAngle, -5);
}
else
{
2021-12-23 17:54:40 +00:00
pSpark->spr.xvel = bcos(nAngle, -6);
pSpark->spr.yvel = bsin(nAngle, -6);
}
2021-12-23 17:54:40 +00:00
pSpark->spr.zvel = -(RandomSize(4) << 7);
pSpark->spr.picnum = kTile985 + nVal;
}
pSpark->spr.pos.Z = pActor->spr.pos.Z;
2021-12-23 17:54:40 +00:00
pSpark->spr.lotag = runlist_HeadRun() + 1;
pSpark->spr.clipdist = 1;
pSpark->spr.hitag = 0;
pSpark->backuppos();
// GrabTimeSlot(3);
2021-12-23 17:54:40 +00:00
pSpark->spr.extra = -1;
pSpark->spr.intowner = runlist_AddRunRec(pSpark->spr.lotag - 1, pSpark, 0x260000);
2021-12-23 17:54:40 +00:00
pSpark->spr.hitag = runlist_AddRunRec(NewRun, pSpark, 0x260000);
2021-10-21 11:15:47 +00:00
return pSpark;
}
void AISpark::Tick(RunListEvent* ev)
{
2021-10-21 11:15:47 +00:00
auto pActor = ev->pObjActor;
if (!pActor) return;
2021-12-23 16:04:54 +00:00
pActor->spr.shade += 3;
pActor->spr.xrepeat -= 2;
2021-12-23 16:04:54 +00:00
if (pActor->spr.xrepeat >= 4 && pActor->spr.shade <= 100)
{
2021-12-23 16:04:54 +00:00
pActor->spr.yrepeat -= 2;
// calling BuildSpark() with 2nd parameter as '1' will set kTile986
2021-12-23 16:04:54 +00:00
if (pActor->spr.picnum == kTile986 && (pActor->spr.xrepeat & 2))
{
2021-10-21 11:15:47 +00:00
BuildSpark(pActor, 2);
}
2021-12-23 16:04:54 +00:00
if (pActor->spr.picnum >= kTile3000) {
return;
}
2021-12-23 16:04:54 +00:00
pActor->spr.zvel += 128;
2021-12-23 16:04:54 +00:00
auto nMov = movesprite(pActor, pActor->spr.xvel << 12, pActor->spr.yvel << 12, pActor->spr.zvel, 2560, -2560, CLIPMASK1);
2021-10-21 11:15:47 +00:00
if (!nMov.type && !nMov.exbits) {
return;
}
2021-12-23 16:04:54 +00:00
if (pActor->spr.zvel <= 0) {
return;
}
}
2021-12-23 16:04:54 +00:00
pActor->spr.xvel = 0;
pActor->spr.yvel = 0;
pActor->spr.zvel = 0;
2021-12-23 16:04:54 +00:00
if (pActor->spr.picnum > kTile3000) {
nSmokeSparks--;
}
runlist_DoSubRunRec(pActor->spr.intowner);
2021-12-23 16:04:54 +00:00
runlist_FreeRun(pActor->spr.lotag - 1);
runlist_SubRunRec(pActor->spr.hitag);
2021-10-21 11:15:47 +00:00
DeleteActor(pActor);
}
void DimLights()
{
static int word_96786 = 0;
word_96786 = word_96786 == 0;
if (word_96786 == 0)
return;
for (auto&sect: sector)
{
2021-11-22 19:18:14 +00:00
if (sect.ceilingshade < 100)
sect.ceilingshade++;
2021-11-22 19:18:14 +00:00
if (sect.floorshade < 100)
sect.floorshade++;
2021-11-22 19:18:14 +00:00
for (auto& wal : wallsofsector(&sect))
{
2021-11-22 19:18:14 +00:00
if (wal.shade < 100)
wal.shade++;
}
}
}
void DoFinale()
{
static int dword_96788 = 0;
static int nextstage = 0;
if (!lFinaleStart)
return;
dword_96788++;
if (dword_96788 < 90)
{
if (!(dword_96788 & 2))
{
int nAng = RandomSize(11);
2022-08-16 21:21:10 +00:00
pFinaleSpr->set_int_ang(nAng);
2021-10-21 11:20:00 +00:00
BuildSpark(pFinaleSpr, 1);
}
if (!RandomSize(2))
{
2021-10-21 11:20:00 +00:00
PlayFX2(StaticSound[kSound78] | 0x2000, pFinaleSpr);
for (int i = 0; i < nTotalPlayers; i++) {
nQuake[i] = 1280;
}
}
}
else
{
DimLights();
if (nDronePitch <= -2400)
{
if (nFinaleStage < 2)
{
if (nFinaleStage == 1)
{
StopLocalSound();
PlayLocalSound(StaticSound[kSound76], 0);
nextstage = PlayClock + 120;
nFinaleStage++;
}
}
else if (nFinaleStage <= 2)
{
if (PlayClock >= nextstage)
{
PlayLocalSound(StaticSound[kSound77], 0);
nFinaleStage++;
nextstage = PlayClock + 360;
}
}
else if (nFinaleStage == 3 && PlayClock >= nextstage)
{
LevelFinished();
}
}
else
{
nDronePitch -= 128;
BendAmbientSound();
nFinaleStage = 1;
}
}
}
2021-11-22 22:56:36 +00:00
DExhumedActor* BuildEnergyBlock(sectortype* pSector)
{
DVector2 apos(0, 0);
2021-11-21 08:42:43 +00:00
for(auto& wal : wallsofsector(pSector))
{
apos += wal.pos;
2021-11-21 08:42:43 +00:00
wal.picnum = kClockSymbol16;
wal.pal = 0;
wal.shade = 50;
}
2021-11-22 22:56:36 +00:00
auto pActor = insertActor(pSector, 406);
2020-10-14 22:10:37 +00:00
pActor->spr.pos.XY() = apos / pSector->wallnum;
2021-11-22 22:56:36 +00:00
pSector->extra = (int16_t)EnergyBlocks.Push(pActor);
// GrabTimeSlot(3);
pActor->spr.pos.Z = pSector->firstWall()->nextSector()->floorz;
// CHECKME - name of this variable?
int nRepeat = (pActor->int_pos().Z - pSector->int_floorz()) >> 8;
if (nRepeat > 255) {
nRepeat = 255;
}
2021-12-23 17:54:40 +00:00
pActor->spr.xrepeat = nRepeat;
pActor->spr.cstat = CSTAT_SPRITE_INVISIBLE;
pActor->spr.xvel = 0;
pActor->spr.yvel = 0;
pActor->spr.zvel = 0;
pActor->spr.extra = -1;
pActor->spr.lotag = runlist_HeadRun() + 1;
pActor->spr.hitag = 0;
pActor->spr.intowner = runlist_AddRunRec(pActor->spr.lotag - 1, pActor, 0x250000);
pActor->backuppos();
2021-10-21 13:02:18 +00:00
return pActor;
}
// TODO - tidy
void KillCreatures()
{
signed int v0;
signed int v1;
v0 = 99;
v1 = 99;
while (1)
{
if (v0 != 100)
{
ExhumedStatIterator it(v1);
while (auto i = it.Next())
{
2021-10-21 13:02:18 +00:00
runlist_DamageEnemy(i, nullptr, 1600);
}
}
++v0;
++v1;
if (v0 > 107) {
return;
}
}
}
2021-10-21 13:02:18 +00:00
void ExplodeEnergyBlock(DExhumedActor* pActor)
{
auto pSector = pActor->sector();
2021-11-21 08:20:44 +00:00
for(auto& wal : wallsofsector(pSector))
{
2021-11-21 08:20:44 +00:00
if (!wal.twoSided()) continue;
auto nextwal = wal.nextWall();
if (nextwal->pal >= 4) {
nextwal->pal = 7;
}
else {
nextwal->pal = 0;
}
nextwal->shade = 50;
}
2021-11-21 08:20:44 +00:00
if (pSector->floorpal >= 4) {
pSector->floorpal = 7;
}
else {
2021-11-21 08:20:44 +00:00
pSector->floorpal = 0;
}
2021-11-21 08:20:44 +00:00
pSector->floorshade = 50;
pSector->extra = -1;
pSector->set_int_floorz(pActor->int_pos().Z);
pActor->spr.pos.Z = (pActor->spr.pos.Z + pSector->floorz) * 0.5;
2021-10-21 11:15:47 +00:00
BuildSpark(pActor, 3);
2021-12-23 16:04:54 +00:00
pActor->spr.cstat = 0;
pActor->spr.xrepeat = 100;
2021-10-21 13:02:18 +00:00
PlayFX2(StaticSound[kSound78], pActor);
2021-12-23 16:04:54 +00:00
pActor->spr.xrepeat = 0;
nEnergyTowers--;
2021-11-21 08:20:44 +00:00
for (int i = 0; i < 20; i++)
{
2022-08-16 21:21:10 +00:00
pActor->set_int_ang(RandomSize(11));
2021-10-21 11:15:47 +00:00
BuildSpark(pActor, 1); // shoot out blue orbs
}
TintPalette(64, 64, 64);
if (nEnergyTowers == 1)
{
runlist_ChangeChannel(nEnergyChan, nEnergyTowers);
StatusMessage(1000, GStrings("TXT_EX_TAKEOUT"));
}
else if (nEnergyTowers != 0)
{
FString msg = GStrings("TXT_EX_TOWERSREMAIN");
msg.Substitute("%d", std::to_string(nEnergyTowers).c_str());
StatusMessage(500, msg.GetChars());
}
else
{
2021-10-21 13:02:18 +00:00
pFinaleSpr = pActor;
lFinaleStart = PlayClock;
if (!lFinaleStart) {
lFinaleStart = lFinaleStart + 1;
}
for(auto& sect: sector)
{
2021-11-21 08:20:44 +00:00
if (sect.ceilingpal == 1) {
sect.ceilingpal = 0;
}
2021-11-21 08:20:44 +00:00
if (sect.floorpal == 1) {
sect.floorpal = 0;
}
2021-11-21 08:20:44 +00:00
for (auto& wal : wallsofsector(&sect))
{
if (wal.pal == 1) {
wal.pal = 0;
}
}
}
KillCreatures();
}
2021-10-21 13:02:18 +00:00
ChangeActorStat(pActor, 0);
}
void AIEnergyBlock::Damage(RunListEvent* ev)
{
2021-10-21 13:02:18 +00:00
auto pActor = ev->pObjActor;
if (!pActor) return;
ev->nDamage >>= 2;
if (ev->nDamage <= 0) {
return;
}
2021-12-23 17:54:40 +00:00
if (ev->nDamage < pActor->spr.xrepeat)
{
2021-12-23 17:54:40 +00:00
pActor->spr.xrepeat -= ev->nDamage;
2021-10-21 11:15:47 +00:00
auto pActor2 = insertActor(lasthitsect, 0);
2022-08-16 21:21:10 +00:00
pActor2->set_int_ang(ev->nParam);
2022-08-20 15:17:50 +00:00
pActor2->spr.pos = lasthit;
2021-10-21 11:15:47 +00:00
BuildSpark(pActor2, 0); // shoot out blue orb when damaged
DeleteActor(pActor2);
}
else
{
2021-12-23 17:54:40 +00:00
pActor->spr.xrepeat = 0; // using xrepeat to store health
2021-10-21 13:02:18 +00:00
ExplodeEnergyBlock(pActor);
}
}
void AIEnergyBlock::RadialDamage(RunListEvent* ev)
{
2021-10-21 13:02:18 +00:00
auto pActor = ev->pObjActor;
if (!pActor) return;
auto pSector =pActor->sector();
2021-11-22 23:51:32 +00:00
if (pSector->extra == -1) {
return;
}
2022-08-20 18:25:38 +00:00
double nFloorZ = pSector->floorz;
2022-08-20 18:25:38 +00:00
pSector->setfloorz(pActor->spr.pos.Z);
pActor->spr.pos.Z--;
2021-10-21 13:02:18 +00:00
ev->nDamage = runlist_CheckRadialDamage(pActor);
// restore previous values
pSector->set_int_floorz(nFloorZ);
pActor->spr.pos.Z++;
if (ev->nDamage <= 0) {
return;
}
// fall through to case 0x80000
Damage(ev);
}
2021-10-21 15:24:52 +00:00
DExhumedActor* BuildObject(DExhumedActor* pActor, int nOjectType, int nHitag)
{
2021-10-21 13:33:41 +00:00
ChangeActorStat(pActor, ObjectStatnum[nOjectType]);
// 0x7FFD to ensure set as blocking ('B' and 'H') sprite and also disable translucency and set not invisible
2021-12-23 17:54:40 +00:00
pActor->spr.cstat = (pActor->spr.cstat | CSTAT_SPRITE_BLOCK_ALL) & ~(CSTAT_SPRITE_TRANSLUCENT | CSTAT_SPRITE_INVISIBLE);
pActor->spr.xvel = 0;
pActor->spr.yvel = 0;
pActor->spr.zvel = 0;
pActor->spr.extra = -1;
pActor->spr.lotag = runlist_HeadRun() + 1;
pActor->spr.hitag = 0;
pActor->spr.intowner = runlist_AddRunRec(pActor->spr.lotag - 1, pActor, 0x170000);
// GrabTimeSlot(3);
2021-10-21 15:24:52 +00:00
pActor->nPhase = ObjectList.Push(pActor);
2021-12-23 17:54:40 +00:00
if (pActor->spr.statnum == kStatDestructibleSprite) {
2021-10-21 15:24:52 +00:00
pActor->nHealth = 4;
}
else {
2021-10-21 15:24:52 +00:00
pActor->nHealth = 120;
}
2021-10-21 15:24:52 +00:00
pActor->nRun = runlist_AddRunRec(NewRun, pActor, 0x170000);
int nSeq = ObjectSeq[nOjectType];
if (nSeq > -1)
{
2021-10-21 15:24:52 +00:00
pActor->nIndex = SeqOffsets[nSeq];
if (!nOjectType) // if not Explosion Trigger (e.g. Exploding Fire Cauldron)
{
2021-10-21 15:24:52 +00:00
pActor->nFrame = RandomSize(4) % (SeqSize[pActor->nIndex] - 1);
}
auto pActor2 = insertActor(pActor->sector(), 0);
2021-10-21 15:24:52 +00:00
pActor->pTarget = pActor2;
pActor->nIndex2 = -1;
2021-12-23 17:54:40 +00:00
pActor2->spr.cstat = CSTAT_SPRITE_INVISIBLE;
pActor2->spr.pos = pActor->spr.pos;
}
else
{
2021-10-21 15:24:52 +00:00
pActor->nFrame = 0;
pActor->nIndex = -1;
2021-12-23 17:54:40 +00:00
if (pActor->spr.statnum == kStatDestructibleSprite) {
2021-10-21 15:24:52 +00:00
pActor->nIndex2 = -1;
}
else {
2021-10-21 15:24:52 +00:00
pActor->nIndex2 = -nHitag;
}
}
pActor->backuppos();
2021-10-21 15:24:52 +00:00
return pActor;
}
// in-game destructable wall mounted screen
2021-10-21 15:24:52 +00:00
void ExplodeScreen(DExhumedActor* pActor)
{
2022-08-20 14:38:47 +00:00
pActor->spr.pos.Z -= GetActorHeightF(pActor) * 0.5;
for (int i = 0; i < 30; i++) {
2021-10-21 15:24:52 +00:00
BuildSpark(pActor, 0); // shoot out blue orbs
}
2021-12-23 16:04:54 +00:00
pActor->spr.cstat = CSTAT_SPRITE_INVISIBLE;
2021-10-21 15:24:52 +00:00
PlayFX2(StaticSound[kSound78], pActor);
}
void AIObject::Tick(RunListEvent* ev)
{
2021-10-21 15:24:52 +00:00
auto pActor = ev->pObjActor;
if (!pActor) return;
2021-12-23 16:04:54 +00:00
int nStat = pActor->spr.statnum;
int bx = pActor->nIndex;
2021-12-23 16:04:54 +00:00
if (nStat == 97 || (!(pActor->spr.cstat & CSTAT_SPRITE_BLOCK_ALL))) {
return;
}
if (nStat != kStatExplodeTarget) {
2021-10-20 22:00:26 +00:00
Gravity(pActor);
}
// do animation
if (bx != -1)
{
2021-10-21 15:24:52 +00:00
pActor->nFrame++;
if (pActor->nFrame >= SeqSize[bx]) {
pActor->nFrame = 0;
}
2021-12-23 16:04:54 +00:00
pActor->spr.picnum = seq_GetSeqPicnum2(bx, pActor->nFrame);
}
2021-10-21 15:24:52 +00:00
if (pActor->nHealth >= 0) {
goto FUNCOBJECT_GOTO;
}
2021-10-21 15:24:52 +00:00
pActor->nHealth++;
2021-10-21 15:24:52 +00:00
if (pActor->nHealth)
{
FUNCOBJECT_GOTO:
if (nStat != kStatExplodeTarget)
{
2021-12-23 16:04:54 +00:00
auto nMov = movesprite(pActor, pActor->spr.xvel << 6, pActor->spr.yvel << 6, pActor->spr.zvel, 0, 0, CLIPMASK0);
2021-12-23 16:04:54 +00:00
if (pActor->spr.statnum == kStatExplodeTrigger) {
pActor->spr.pal = 1;
}
2021-10-21 15:24:52 +00:00
if (nMov.exbits & kHitAux2)
{
2021-12-23 16:04:54 +00:00
pActor->spr.xvel -= pActor->spr.xvel >> 3;
pActor->spr.yvel -= pActor->spr.yvel >> 3;
}
2021-10-21 15:24:52 +00:00
if (nMov.type == kHitSprite)
{
2021-12-23 16:04:54 +00:00
pActor->spr.yvel = 0;
pActor->spr.xvel = 0;
}
}
return;
}
else
{
int var_18;
// red branch
2022-08-20 18:25:38 +00:00
if ((nStat == kStatExplodeTarget) || (pActor->spr.pos.Z < pActor->sector()->floorz))
{
var_18 = 36;
}
else
{
var_18 = 34;
}
2022-08-20 14:19:07 +00:00
AddFlash(pActor->sector(), pActor->spr.pos, 128);
BuildAnim(nullptr, var_18, 0, DVector3(pActor->spr.pos.XY(), pActor->sector()->floorz), pActor->sector(), 240, 4);
// int edi = nSprite | 0x4000;
if (nStat == kStatExplodeTrigger)
{
for (int i = 4; i < 8; i++) {
2021-10-21 15:24:52 +00:00
BuildCreatureChunk(pActor, seq_GetSeqPicnum(kSeqFirePot, (i >> 2) + 1, 0), true);
}
2021-10-21 15:24:52 +00:00
runlist_RadialDamageEnemy(pActor, 200, 20);
}
else if (nStat == kStatExplodeTarget)
{
for (int i = 0; i < 8; i++) {
2021-10-21 15:24:52 +00:00
BuildCreatureChunk(pActor, seq_GetSeqPicnum(kSeqFirePot, (i >> 1) + 3, 0), true);
}
}
if (!(currentLevel->gameflags & LEVEL_EX_MULTI) || nStat != kStatExplodeTrigger)
{
runlist_SubRunRec(pActor->spr.intowner);
2021-10-21 15:24:52 +00:00
runlist_SubRunRec(pActor->nRun);
2021-10-21 15:24:52 +00:00
DeleteActor(pActor);
return;
}
else
{
StartRegenerate(pActor);
2021-10-21 15:24:52 +00:00
pActor->nHealth = 120;
pActor->spr.pos = pActor->pTarget->spr.pos;
ChangeActorSect(pActor, pActor->pTarget->sector());
return;
}
}
}
void AIObject::Damage(RunListEvent* ev)
{
2021-10-21 15:24:52 +00:00
auto pActor = ev->pObjActor;
if (!pActor) return;
2021-12-23 16:04:54 +00:00
int nStat = pActor->spr.statnum;
2021-10-21 15:24:52 +00:00
if (nStat >= 150 || pActor->nHealth <= 0) {
return;
}
if (nStat == 98)
{
2021-10-21 15:24:52 +00:00
D3PlayFX((StaticSound[kSound47] | 0x2000) | (RandomSize(2) << 9), pActor);
return;
}
2021-11-21 18:24:46 +00:00
pActor->nHealth -= (int16_t)ev->nDamage;
2021-10-21 15:24:52 +00:00
if (pActor->nHealth > 0) {
return;
}
if (nStat == kStatDestructibleSprite)
{
2021-10-21 15:24:52 +00:00
ExplodeScreen(pActor);
}
else
{
2021-10-21 15:24:52 +00:00
pActor->nHealth = -(RandomSize(3) + 1);
}
}
void AIObject::Draw(RunListEvent* ev)
{
2021-10-21 15:24:52 +00:00
auto pActor = ev->pObjActor;
if (!pActor) return;
int bx = pActor->nIndex;
if (bx > -1)
{
2021-10-21 15:24:52 +00:00
seq_PlotSequence(ev->nParam, bx, pActor->nFrame, 1);
}
return;
}
void AIObject::RadialDamage(RunListEvent* ev)
{
2021-10-21 15:24:52 +00:00
auto pActor = ev->pObjActor;
if (!pActor) return;
2021-12-23 16:04:54 +00:00
int nStat = pActor->spr.statnum;
2021-12-23 16:04:54 +00:00
if (pActor->nHealth > 0 && pActor->spr.cstat & CSTAT_SPRITE_BLOCK_ALL
&& (nStat != kStatExplodeTarget
|| ev->pRadialActor->spr.statnum == 201
|| (nRadialBullet != 3 && nRadialBullet > -1)
|| ev->pRadialActor->spr.statnum == kStatExplodeTrigger))
{
2021-10-21 15:24:52 +00:00
int nDamage = runlist_CheckRadialDamage(pActor);
if (nDamage <= 0) {
return;
}
2021-12-23 16:04:54 +00:00
if (pActor->spr.statnum != kStatAnubisDrum) {
2021-10-21 15:24:52 +00:00
pActor->nHealth -= nDamage;
}
2021-12-23 16:04:54 +00:00
if (pActor->spr.statnum == kStatExplodeTarget)
{
2021-12-23 16:04:54 +00:00
pActor->spr.xvel = 0;
pActor->spr.yvel = 0;
pActor->spr.zvel = 0;
}
2021-12-23 16:04:54 +00:00
else if (pActor->spr.statnum != kStatAnubisDrum)
{
2021-12-23 16:04:54 +00:00
pActor->spr.xvel >>= 1;
pActor->spr.yvel >>= 1;
pActor->spr.zvel >>= 1;
}
2021-10-21 15:24:52 +00:00
if (pActor->nHealth > 0) {
return;
}
2021-12-23 16:04:54 +00:00
if (pActor->spr.statnum == kStatExplodeTarget)
{
2021-10-21 15:24:52 +00:00
pActor->nHealth = -1;
int ax = pActor->nIndex2;
2021-10-21 15:24:52 +00:00
if (ax < 0 || ObjectList[ax]->nHealth <= 0) {
return;
}
2021-10-21 15:24:52 +00:00
ObjectList[ax]->nHealth = -1;
}
2021-12-23 16:04:54 +00:00
else if (pActor->spr.statnum == kStatDestructibleSprite)
{
2021-10-21 15:24:52 +00:00
pActor->nHealth = 0;
2021-10-21 15:24:52 +00:00
ExplodeScreen(pActor);
}
else
{
2021-10-21 15:24:52 +00:00
pActor->nHealth = -(RandomSize(4) + 1);
}
}
}
2021-10-21 16:28:06 +00:00
void BuildDrip(DExhumedActor* pActor)
{
2020-11-29 23:49:25 +00:00
auto nDrips = sDrip.Reserve(1);
2021-10-21 16:28:06 +00:00
sDrip[nDrips].pActor = pActor;
sDrip[nDrips].nCount = RandomSize(8) + 90;
2021-12-23 16:04:54 +00:00
pActor->spr.cstat = CSTAT_SPRITE_INVISIBLE;
}
void DoDrips()
{
2020-11-29 23:49:25 +00:00
for (unsigned i = 0; i < sDrip.Size(); i++)
{
2021-10-21 16:28:06 +00:00
sDrip[i].nCount--;
if (sDrip[i].nCount <= 0)
{
2021-12-07 17:53:02 +00:00
DExhumedActor* pActor = sDrip[i].pActor;
if (!pActor) continue;
int nSeqOffset = SeqOffsets[kSeqDrips];
if (!(pActor->sector()->Flag & kSectLava)) {
nSeqOffset++;
}
2021-10-21 16:28:06 +00:00
seq_MoveSequence(pActor, nSeqOffset, RandomSize(2) % SeqSize[nSeqOffset]);
2021-10-21 16:28:06 +00:00
sDrip[i].nCount = RandomSize(8) + 90;
}
}
2020-11-29 23:49:25 +00:00
for (unsigned i = 0; i < sBob.Size(); i++)
{
sBob[i].nPhase += 4;
int edx = bsin(sBob[i].nPhase << 3, -4);
2021-11-22 22:40:53 +00:00
auto pSector =sBob[i].pSector;
if (sBob[i].field_3)
{
pSector->set_int_ceilingz(edx + sBob[i].z);
}
else
{
double nFloorZ = pSector->floorz;
pSector->set_int_floorz(edx + sBob[i].z);
MoveSectorSprites(pSector, pSector->floorz - nFloorZ);
}
}
}
2021-11-22 22:40:53 +00:00
void SnapBobs(sectortype* pSectorA, sectortype* pSectorB)
{
2021-11-22 22:40:53 +00:00
int select1 = -1;
int select2 = select1;
int esi;
2020-11-29 23:49:25 +00:00
for (unsigned i = 0; i < sBob.Size(); i++)
{
2021-11-22 22:40:53 +00:00
auto pSector = sBob[i].pSector;
2021-11-22 22:40:53 +00:00
if (pSector != pSectorA)
{
2021-11-22 22:40:53 +00:00
if (pSectorB != pSector)
continue;
2021-11-22 22:40:53 +00:00
esi = select2;
select1 = i;
}
else
{
2021-11-22 22:40:53 +00:00
esi = select1;
select2 = i;
}
if (esi != -1) {
break;
}
}
2021-11-22 22:40:53 +00:00
if (select1 <= -1) {
return;
}
2021-11-22 22:40:53 +00:00
if (select2 <= -1) {
return;
}
2021-11-22 22:40:53 +00:00
sBob[select1].nPhase = sBob[select2].nPhase;
}
2021-11-22 22:40:53 +00:00
void AddSectorBob(sectortype* pSector, int nHitag, int bx)
{
2020-11-29 23:49:25 +00:00
auto nBobs = sBob.Reserve(1);
sBob[nBobs].field_3 = bx;
int z;
if (bx == 0) {
z = pSector->int_floorz();
}
else {
z = pSector->int_ceilingz();
}
sBob[nBobs].z = z;
sBob[nBobs].nPhase = nHitag << 4;
2020-11-29 23:49:25 +00:00
sBob[nBobs].sBobID = nHitag;
2021-11-22 22:40:53 +00:00
sBob[nBobs].pSector = pSector;
StartInterpolation(pSector, bx == 0 ? Interp_Sect_Floorz : Interp_Sect_Ceilingz);
2021-11-22 22:40:53 +00:00
pSector->Flag |= 0x0010;
}
int FindTrail(int nVal)
{
2020-11-29 23:49:25 +00:00
for (unsigned i = 0; i < sTrail.Size(); i++)
{
if (sTrail[i].nVal == nVal)
return i;
}
2020-11-29 23:49:25 +00:00
auto nTrails = sTrail.Reserve(1);
sTrail[nTrails].nVal = nVal;
sTrail[nTrails].nPoint = -1;
sTrail[nTrails].nPoint2 = -1;
2020-11-29 23:49:25 +00:00
return nTrails;
}
// ok ?
2021-10-21 16:28:06 +00:00
void ProcessTrailSprite(DExhumedActor* pActor, int nLotag, int nHitag)
{
2020-11-29 23:49:25 +00:00
auto nPoint = sTrailPoint.Reserve(1);
2022-01-31 22:33:44 +00:00
sTrailPoint[nPoint].x = pActor->int_pos().X;
sTrailPoint[nPoint].y = pActor->int_pos().Y;
int nTrail = FindTrail(nHitag);
int var_14 = nLotag - 900;
2020-11-29 23:49:25 +00:00
sTrailPoint[nPoint].nTrailPointVal = var_14;
int field0 = sTrail[nTrail].nPoint;
if (field0 == -1)
{
sTrail[nTrail].nPoint = nPoint;
sTrail[nTrail].nPoint2 = nPoint;
2020-11-29 23:49:25 +00:00
sTrailPoint[nPoint].nTrailPointNext = -1;
sTrailPoint[nPoint].nTrailPointPrev = -1;
}
else
{
int ecx = -1;
while (field0 != -1)
{
2020-11-29 23:49:25 +00:00
if (sTrailPoint[field0].nTrailPointVal > var_14)
{
2020-11-29 23:49:25 +00:00
sTrailPoint[nPoint].nTrailPointPrev = sTrailPoint[field0].nTrailPointPrev;
sTrailPoint[field0].nTrailPointPrev = nPoint;
sTrailPoint[nPoint].nTrailPointNext = field0;
if (field0 == sTrail[nTrail].nPoint) {
sTrail[nTrail].nPoint = nPoint;
}
break;
}
ecx = field0;
2020-11-29 23:49:25 +00:00
field0 = sTrailPoint[field0].nTrailPointNext;
}
if (field0 == -1)
{
2020-11-29 23:49:25 +00:00
sTrailPoint[ecx].nTrailPointNext = nPoint;
sTrailPoint[nPoint].nTrailPointPrev = ecx;
sTrailPoint[nPoint].nTrailPointNext = -1;
sTrail[nTrail].nPoint2 = nPoint;
}
}
2021-10-21 16:28:06 +00:00
DeleteActor(pActor);
}
// ok?
2021-11-22 23:03:18 +00:00
void AddMovingSector(sectortype* pSector, int edx, int ebx, int ecx)
{
2021-11-22 23:03:18 +00:00
CreatePushBlock(pSector);
2021-11-22 20:03:10 +00:00
setsectinterpolate(pSector);
int nTrail = FindTrail(ebx);
2020-11-29 23:49:25 +00:00
auto nMoveSects = sMoveSect.Reserve(1);
MoveSect* pMoveSect = &sMoveSect[nMoveSects];
2020-11-29 23:49:25 +00:00
pMoveSect->sMoveDir = 1;
pMoveSect->nTrail = nTrail;
pMoveSect->nTrailPoint = -1;
2021-11-22 20:03:10 +00:00
pMoveSect->pCurSector = nullptr;
pMoveSect->nFlags = ecx;
pMoveSect->field_10 = (edx / 1000) + 1;
2021-11-22 20:03:10 +00:00
pMoveSect->pSector = pSector;
if (ecx & 8)
{
pMoveSect->nChannel = runlist_AllocChannel(ebx % 1000);
}
else
{
pMoveSect->nChannel = -1;
}
2021-12-18 12:23:08 +00:00
pSector->floorstat |= CSTAT_SECTOR_ALIGN;
}
void DoMovingSects()
{
2020-11-29 23:49:25 +00:00
for (unsigned i = 0; i < sMoveSect.Size(); i++)
{
2021-11-22 20:03:10 +00:00
if (sMoveSect[i].pSector == nullptr) {
continue;
}
if (sMoveSect[i].nChannel != -1 && !sRunChannels[sMoveSect[i].nChannel].c) {
continue;
}
2021-11-22 20:03:10 +00:00
auto pSector =sMoveSect[i].pSector;
int nBlock = pSector->extra;
BlockInfo* pBlockInfo = &sBlockInfo[nBlock];
if (sMoveSect[i].nTrailPoint == -1)
{
if (sMoveSect[i].nFlags & 0x20)
{
runlist_ChangeChannel(sMoveSect[i].nChannel, 0);
}
int ax;
if (sMoveSect[i].nFlags & 0x10)
{
2020-11-29 23:49:25 +00:00
sMoveSect[i].sMoveDir = -sMoveSect[i].sMoveDir;
if (sMoveSect[i].sMoveDir > 0)
{
ax = sTrail[sMoveSect[i].nTrail].nPoint;
}
else
{
ax = sTrail[sMoveSect[i].nTrail].nPoint2;
}
}
else
{
ax = sTrail[sMoveSect[i].nTrail].nPoint;
}
sMoveSect[i].nTrailPoint = ax;
}
int nTrail = sMoveSect[i].nTrailPoint;
// TrailPoint *pTrail = &sTrailPoint[nTrail];
// loc_23872:
int nAngle = getangle(sTrailPoint[nTrail].x - pBlockInfo->x, sTrailPoint[nTrail].y - pBlockInfo->y);
int nXVel = bcos(nAngle, 4) * sMoveSect[i].field_10;
int nYVel = bsin(nAngle, 4) * sMoveSect[i].field_10;
int ebx = (sTrailPoint[nTrail].x - pBlockInfo->x) << 14;
int eax = nXVel;
if (eax < 0) {
eax = -eax;
}
int edx = eax;
eax = ebx;
int ecx = (sTrailPoint[nTrail].y - pBlockInfo->y) << 14;
if (eax < 0) {
eax = -eax;
}
// loc_238EC:
if (edx <= eax)
{
eax = nYVel;
if (eax < 0) {
eax = -eax;
}
edx = eax;
eax = ecx;
if (eax < 0) {
eax = -eax;
}
if (edx > eax)
{
// loc_23908:
nYVel = ecx;
nXVel = ebx;
2020-11-29 23:49:25 +00:00
if (sMoveSect[i].sMoveDir > 0)
{
2020-11-29 23:49:25 +00:00
sMoveSect[i].nTrailPoint = sTrailPoint[sMoveSect[i].nTrailPoint].nTrailPointNext;
}
else
{
2020-11-29 23:49:25 +00:00
sMoveSect[i].nTrailPoint = sTrailPoint[sMoveSect[i].nTrailPoint].nTrailPointPrev;
}
}
}
else
{
// repeat of code from loc_23908
nYVel = ecx;
nXVel = ebx;
2020-11-29 23:49:25 +00:00
if (sMoveSect[i].sMoveDir > 0)
{
2020-11-29 23:49:25 +00:00
sMoveSect[i].nTrailPoint = sTrailPoint[sMoveSect[i].nTrailPoint].nTrailPointNext;
}
else
{
2020-11-29 23:49:25 +00:00
sMoveSect[i].nTrailPoint = sTrailPoint[sMoveSect[i].nTrailPoint].nTrailPointPrev;
}
}
// loc_2393A:
2021-11-22 20:03:10 +00:00
if (sMoveSect[i].pCurSector != nullptr)
{
2021-11-22 20:03:10 +00:00
MoveSector(sMoveSect[i].pCurSector, -1, &nXVel, &nYVel);
}
int var_2C = nXVel;
int var_30 = nYVel;
2021-11-22 20:03:10 +00:00
MoveSector(pSector, -1, &nXVel, &nYVel);
if (nXVel != var_2C || nYVel != var_30)
{
2021-11-22 20:03:10 +00:00
MoveSector(sMoveSect[i].pCurSector, -1, &var_2C, &var_30);
MoveSector(sMoveSect[i].pCurSector, -1, &nXVel, &nYVel);
}
}
}
void PostProcess()
{
2020-11-29 23:49:25 +00:00
for (unsigned i = 0; i < sMoveSect.Size(); i++)
{
int nTrail = sMoveSect[i].nTrail;
sMoveSect[i].nTrailPoint = sTrail[nTrail].nPoint;
if (sMoveSect[i].nFlags & 0x40) {
runlist_ChangeChannel(sMoveSect[i].nChannel, 1);
}
2021-11-22 20:03:10 +00:00
auto pSector =sMoveSect[i].pSector;
2021-11-22 20:03:10 +00:00
if (pSector->Flag & kSectUnderwater)
{
2021-12-18 12:23:08 +00:00
pSector->ceilingstat |= CSTAT_SECTOR_ALIGN;
pSector->floorstat &= ~(CSTAT_SECTOR_EXHUMED_BIT1 | CSTAT_SECTOR_EXHUMED_BIT2);
2020-11-29 23:49:25 +00:00
for (unsigned j = 0; j < sMoveSect.Size(); j++)
{
if (j != i && sMoveSect[i].nTrail == sMoveSect[j].nTrail)
{
2021-11-22 20:03:10 +00:00
sMoveSect[j].pCurSector = sMoveSect[i].pSector;
2021-11-22 22:40:53 +00:00
SnapSectors(sMoveSect[j].pSector, sMoveSect[i].pSector, 0);
2021-11-22 20:03:10 +00:00
sMoveSect[i].pSector = nullptr;
}
}
}
}
2020-11-29 23:49:25 +00:00
for (unsigned i = 0; i < sBob.Size(); i++)
{
if (sBob[i].field_3 == 0)
{
2020-11-29 23:49:25 +00:00
int bobID = sBob[i].sBobID;
2020-11-29 23:49:25 +00:00
for (unsigned j = 0; j < sBob.Size(); j++)
{
if (j != i)
{
2020-11-29 23:49:25 +00:00
if (sBob[i].field_3 != 0 && sBob[j].sBobID == bobID) {
2021-11-22 22:40:53 +00:00
SnapSectors(sBob[i].pSector, sBob[j].pSector, 0);
}
}
}
}
}
if (!(currentLevel->gameflags & LEVEL_EX_COUNTDOWN))
{
for (auto& sect: sector)
{
int var_20 = 30000;
2021-11-22 19:18:14 +00:00
if (sect.Speed && sect.Depth && !(sect.Flag & kSectLava))
{
2021-11-22 19:18:14 +00:00
sect.pSoundSect = &sect;
sect.Sound = StaticSound[kSound43];
}
else
{
for (auto& sectj: sector)
{
// loc_23CA6:
2021-11-22 19:18:14 +00:00
if (&sect != &sectj && sectj.Speed && !(sect.Flag & kSectLava))
{
int xVal = abs(sect.firstWall()->wall_int_pos().X - sectj.firstWall()->wall_int_pos().X);
int yVal = abs(sect.firstWall()->wall_int_pos().Y - sectj.firstWall()->wall_int_pos().Y);
if (xVal < 15000 && yVal < 15000 && (xVal + yVal < var_20))
{
var_20 = xVal + yVal;
2021-11-22 19:18:14 +00:00
sect.pSoundSect = &sectj;
sect.Sound = StaticSound[kSound43];
}
}
}
}
}
}
else // nMap == kMap20)
{
for(auto& sect: sector)
{
2021-11-22 19:18:14 +00:00
sect.pSoundSect = &sect;
sect.Sound = StaticSound[kSound62];
2021-11-22 19:18:14 +00:00
for(auto& wal : wallsofsector(&sect))
{
2021-11-22 19:18:14 +00:00
if (wal.picnum == kTile3603)
{
2021-11-22 19:18:14 +00:00
wal.pal = 1;
auto pActor = insertActor(&sect, 407);
pActor->spr.cstat = CSTAT_SPRITE_INVISIBLE;
}
}
}
ExhumedSpriteIterator it;
2021-10-21 16:42:07 +00:00
while (auto act = it.Next())
{
2021-12-23 17:54:40 +00:00
if (act->spr.statnum < kMaxStatus && act->spr.picnum == kTile3603)
{
2021-10-21 16:42:07 +00:00
ChangeActorStat(act, 407);
2021-12-23 17:54:40 +00:00
act->spr.pal = 1;
}
}
}
2020-11-29 23:49:25 +00:00
for (unsigned i = 0; i < ObjectList.Size(); i++)
{
2021-10-21 15:24:52 +00:00
auto pObjectActor = ObjectList[i];
if (pObjectActor->spr.statnum == kStatExplodeTarget)
{
2021-10-21 15:24:52 +00:00
if (!pObjectActor->nIndex2) {
pObjectActor->nIndex2 = -1;
}
else
{
2021-10-21 15:24:52 +00:00
int edi = pObjectActor->nIndex2;
pObjectActor->nIndex2 = -1;
2020-11-29 23:49:25 +00:00
for (unsigned j = 0; j < ObjectList.Size(); j++)
{
2021-10-21 15:24:52 +00:00
if (i != j && ObjectList[j]->spr.statnum == kStatExplodeTarget && edi == ObjectList[j]->nIndex2)
{
2021-10-21 15:24:52 +00:00
pObjectActor->nIndex2 = j;
ObjectList[j]->nIndex2 = i;
}
}
}
}
}
}
END_PS_NS