mirror of
https://github.com/ZDoom/Raze.git
synced 2024-12-17 08:00:51 +00:00
1803 lines
50 KiB
C++
1803 lines
50 KiB
C++
//-------------------------------------------------------------------------
|
|
/*
|
|
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 "exhumed.h"
|
|
#include "engine.h"
|
|
#include "aistuff.h"
|
|
#include "player.h"
|
|
#include "sound.h"
|
|
#include <assert.h>
|
|
|
|
BEGIN_PS_NS
|
|
|
|
enum
|
|
{
|
|
kFuncMax = 39,
|
|
kMaxRunStack = 200
|
|
};
|
|
|
|
|
|
DExhumedActor* pRadialActor;
|
|
int nStackCount = 0;
|
|
int word_966BE = 0;
|
|
int ChannelList = -1;
|
|
int ChannelLast = -1;
|
|
|
|
int nDamageRadius;
|
|
int nRadialDamage;
|
|
int RunChain;
|
|
int NewRun;
|
|
|
|
int sRunStack[kMaxRunStack];
|
|
RunChannel sRunChannels[kMaxChannels];
|
|
FreeListArray<RunStruct, kMaxRuns> RunData;
|
|
|
|
|
|
FSerializer& Serialize(FSerializer& arc, const char* keyname, RunStruct& w, RunStruct* def)
|
|
{
|
|
if (arc.BeginObject(keyname))
|
|
{
|
|
arc("ref", w.nAIType)
|
|
("val", w.nObjIndex)
|
|
("actor", w.pObjActor)
|
|
("_4", w.next)
|
|
("_6", w.prev)
|
|
.EndObject();
|
|
}
|
|
return arc;
|
|
}
|
|
|
|
void SerializeRunList(FSerializer& arc)
|
|
{
|
|
if (arc.BeginObject("runlist"))
|
|
{
|
|
arc("data", RunData)
|
|
("stackcount", nStackCount)
|
|
("w966be", word_966BE)
|
|
("list", ChannelList)
|
|
("last", ChannelLast)
|
|
("damageradius", nDamageRadius)
|
|
("radialdamage", nRadialDamage)
|
|
("runchain", RunChain)
|
|
("newrun", NewRun)
|
|
.Array("runstack", sRunStack, nStackCount)
|
|
.Array("runchannels", &sRunChannels[0].a, 4 * kMaxChannels) // save this in a more compact form than an array of structs.
|
|
.EndObject();
|
|
}
|
|
}
|
|
|
|
AIElev aiElev;
|
|
AISWReady aiSwReady;
|
|
AISWPause aiSwPause;
|
|
AISWStepOn aiSwStepOn;
|
|
AISWNotOnPause aiSwNotOnPause;
|
|
AISWPressSector aiSwPressSector;
|
|
AISWPressWall aiSwPressWall;
|
|
AIWallFace aiWallFace;
|
|
AISlide aiSlide;
|
|
AIAnubis aiAnubis;
|
|
AIPlayer aiPlayer;
|
|
AIBullet aiBullet;
|
|
AISpider aiSpider;
|
|
AICreatureChunk aiCreatureChunk;
|
|
AIMummy aiMummy;
|
|
AIGrenade aiGrenade;
|
|
AIAnim aiAnim;
|
|
AISnake aiSnake;
|
|
AIFish aiFish;
|
|
AILion aiLion;
|
|
AIBubble aiBubble;
|
|
AILavaDude aiLava;
|
|
AILavaDudeLimb aiLavaLimb;
|
|
AIObject aiObject;
|
|
AIRex aiRex;
|
|
AISet aiSet;
|
|
AIQueen aiQueen;
|
|
AIQueenHead aiQueenHead;
|
|
AIRoach aiRoach;
|
|
AIQueenEgg aiQueenEgg;
|
|
AIWasp aiWasp;
|
|
AITrap aiTrap;
|
|
AIFishLimb aiFishLimb;
|
|
AIRa aiRa;
|
|
AIScorp aiScorp;
|
|
AISoul aiSoul;
|
|
AIRat aiRat;
|
|
AIEnergyBlock aiEnergyBlock;
|
|
AISpark aiSpark;
|
|
|
|
|
|
ExhumedAI* ais[kFuncMax] =
|
|
{
|
|
&aiElev,
|
|
&aiSwReady,
|
|
&aiSwPause,
|
|
&aiSwStepOn,
|
|
&aiSwNotOnPause,
|
|
&aiSwPressSector,
|
|
&aiSwPressWall,
|
|
&aiWallFace,
|
|
&aiSlide,
|
|
&aiAnubis,
|
|
&aiPlayer,
|
|
&aiBullet,
|
|
&aiSpider,
|
|
&aiCreatureChunk,
|
|
&aiMummy,
|
|
&aiGrenade,
|
|
&aiAnim,
|
|
&aiSnake,
|
|
&aiFish,
|
|
&aiLion,
|
|
&aiBubble,
|
|
&aiLava,
|
|
&aiLavaLimb,
|
|
&aiObject,
|
|
&aiRex,
|
|
&aiSet,
|
|
&aiQueen,
|
|
&aiQueenHead,
|
|
&aiRoach,
|
|
&aiQueenEgg,
|
|
&aiWasp,
|
|
&aiTrap,
|
|
&aiFishLimb,
|
|
&aiRa,
|
|
&aiScorp,
|
|
&aiSoul,
|
|
&aiRat,
|
|
&aiEnergyBlock,
|
|
&aiSpark,
|
|
};
|
|
|
|
|
|
int runlist_GrabRun()
|
|
{
|
|
return RunData.Get();
|
|
}
|
|
|
|
int runlist_FreeRun(int nRun)
|
|
{
|
|
assert(nRun >= 0 && nRun < kMaxRuns);
|
|
|
|
RunData[nRun].prev = -1;
|
|
RunData[nRun].nAIType = -1;
|
|
RunData[nRun].nObjIndex = -1;
|
|
RunData[nRun].pObjActor = nullptr;
|
|
RunData[nRun].next = RunData[nRun].prev;
|
|
RunData.Release(nRun);
|
|
return 1;
|
|
}
|
|
|
|
int runlist_HeadRun()
|
|
{
|
|
int nRun = runlist_GrabRun();
|
|
|
|
RunData[nRun].next = -1;
|
|
RunData[nRun].prev = -1;
|
|
|
|
return nRun;
|
|
}
|
|
|
|
void runlist_InitRun()
|
|
{
|
|
int i;
|
|
|
|
RunData.Clear();
|
|
nStackCount = 0;
|
|
|
|
for (i = 0; i < kMaxRuns; i++)
|
|
{
|
|
RunData[i].nAIType = -1;
|
|
RunData[i].pObjActor = nullptr;
|
|
RunData[i].nObjIndex = -1;
|
|
RunData[i].prev = -1;
|
|
RunData[i].next = -1;
|
|
}
|
|
|
|
int nRun = runlist_HeadRun();
|
|
RunChain = nRun;
|
|
NewRun = nRun;
|
|
|
|
for (i = 0; i < kMaxPlayers; i++) {
|
|
PlayerList[i].nRun = -1;
|
|
}
|
|
|
|
pRadialActor = nullptr;
|
|
}
|
|
|
|
void runlist_UnlinkRun(int nRun)
|
|
{
|
|
if (!(nRun >= 0 && nRun < kMaxRuns)) return;
|
|
|
|
if (nRun == RunChain)
|
|
{
|
|
RunChain = RunData[nRun].next;
|
|
return;
|
|
}
|
|
|
|
if (RunData[nRun].prev >= 0)
|
|
{
|
|
RunData[RunData[nRun].prev].next = RunData[nRun].next;
|
|
}
|
|
|
|
if (RunData[nRun].next >= 0)
|
|
{
|
|
RunData[RunData[nRun].next].prev = RunData[nRun].prev;
|
|
}
|
|
}
|
|
|
|
void runlist_InsertRun(int RunLst, int RunNum)
|
|
{
|
|
if (!(RunLst >= 0 && RunLst < kMaxRuns)) return;
|
|
if (!(RunNum >= 0 && RunNum < kMaxRuns)) return;
|
|
|
|
RunData[RunNum].prev = RunLst;
|
|
int val = RunData[RunLst].next;
|
|
RunData[RunNum].next = val;
|
|
|
|
if (val >= 0) {
|
|
RunData[val].prev = RunNum;
|
|
}
|
|
|
|
RunData[RunLst].next = RunNum;
|
|
}
|
|
|
|
int runlist_AddRunRec(int nIndex, int nObject, int nAIType)
|
|
{
|
|
int nRun = runlist_GrabRun();
|
|
|
|
RunData[nRun].nAIType = nAIType;
|
|
RunData[nRun].nObjIndex = nObject;
|
|
RunData[nRun].pObjActor = nullptr;
|
|
|
|
runlist_InsertRun(nIndex, nRun);
|
|
return nRun;
|
|
}
|
|
|
|
int runlist_AddRunRec(int nIndex, DExhumedActor* pObject, int nAIType)
|
|
{
|
|
int nRun = runlist_GrabRun();
|
|
|
|
RunData[nRun].nAIType = nAIType;
|
|
RunData[nRun].nObjIndex = -1;
|
|
RunData[nRun].pObjActor = pObject;
|
|
|
|
runlist_InsertRun(nIndex, nRun);
|
|
return nRun;
|
|
}
|
|
|
|
int runlist_AddRunRec(int nIndex, RunStruct* other)
|
|
{
|
|
int nRun = runlist_GrabRun();
|
|
|
|
RunData[nRun].nAIType = other->nAIType;
|
|
RunData[nRun].nObjIndex = other->nObjIndex;
|
|
RunData[nRun].pObjActor = other->pObjActor;
|
|
|
|
runlist_InsertRun(nIndex, nRun);
|
|
return nRun;
|
|
}
|
|
|
|
|
|
void runlist_DoSubRunRec(int RunPtr)
|
|
{
|
|
if (!(RunPtr >= 0 && RunPtr < kMaxRuns)) return;
|
|
|
|
runlist_UnlinkRun(RunPtr);
|
|
runlist_FreeRun(RunPtr);
|
|
}
|
|
|
|
void runlist_CleanRunRecs()
|
|
{
|
|
int nextPtr = RunChain;
|
|
|
|
if (nextPtr >= 0)
|
|
{
|
|
assert(nextPtr < kMaxRuns);
|
|
nextPtr = RunData[nextPtr].next;
|
|
}
|
|
|
|
while (nextPtr >= 0)
|
|
{
|
|
int runPtr = nextPtr;
|
|
assert(runPtr < kMaxRuns);
|
|
|
|
int val = RunData[runPtr].nAIType; // >> 16;
|
|
nextPtr = RunData[runPtr].next;
|
|
|
|
if (val < 0) {
|
|
runlist_DoSubRunRec(runPtr);
|
|
}
|
|
}
|
|
}
|
|
|
|
void runlist_SubRunRec(int RunPtr)
|
|
{
|
|
if (!(RunPtr >= 0 && RunPtr < kMaxRuns)) return;
|
|
|
|
RunData[RunPtr].nAIType = -totalmoves;
|
|
}
|
|
|
|
void runlist_SendMessage(int nRun, int nObject, void(ExhumedAI::* func)(RunListEvent*), RunListEvent* ev)
|
|
{
|
|
int nFunc = RunData[nRun].nAIType >> 16;
|
|
|
|
if (nFunc < 0 || nFunc >= (int)countof(ais)) {
|
|
return;
|
|
}
|
|
|
|
RunListEvent defev;
|
|
if (!ev)
|
|
{
|
|
defev = {};
|
|
ev = &defev;
|
|
}
|
|
ev->nObjIndex = RunData[nRun].nObjIndex;
|
|
ev->pObjActor = RunData[nRun].pObjActor;
|
|
ev->nParam = nObject;
|
|
ev->nRun = nRun;
|
|
|
|
|
|
(ais[nFunc]->*func)(ev);
|
|
}
|
|
|
|
void runlist_ExplodeSignalRun()
|
|
{
|
|
int nextPtr = RunChain;
|
|
|
|
if (nextPtr >= 0)
|
|
{
|
|
assert(nextPtr < kMaxRuns);
|
|
nextPtr = RunData[nextPtr].next;
|
|
}
|
|
|
|
// LOOP
|
|
while (nextPtr >= 0)
|
|
{
|
|
int runPtr = nextPtr;
|
|
assert(runPtr < kMaxRuns);
|
|
|
|
nextPtr = RunData[runPtr].next;
|
|
|
|
if (RunData[runPtr].nObjIndex >= 0 || RunData[runPtr].pObjActor)
|
|
{
|
|
RunListEvent ev{};
|
|
ev.nMessage = 1;
|
|
ev.nRadialDamage = nRadialDamage;
|
|
ev.nDamageRadius = nDamageRadius;
|
|
ev.pRadialActor = pRadialActor;
|
|
runlist_SendMessage(runPtr, 0, &ExhumedAI::RadialDamage, &ev);
|
|
}
|
|
}
|
|
}
|
|
|
|
void runlist_PushMoveRun(int eax)
|
|
{
|
|
if (nStackCount < kMaxRunStack)
|
|
{
|
|
sRunStack[nStackCount] = eax;
|
|
nStackCount++;
|
|
}
|
|
}
|
|
|
|
int runlist_PopMoveRun()
|
|
{
|
|
if (nStackCount <= 0) {
|
|
I_Error("PopMoveRun() called inappropriately\n");
|
|
exit(-1);
|
|
}
|
|
|
|
nStackCount--;
|
|
return sRunStack[nStackCount];
|
|
}
|
|
|
|
void runlist_SignalRun(int NxtPtr, int edx, void(ExhumedAI::* func)(RunListEvent*), RunListEvent* ev)
|
|
{
|
|
if (NxtPtr == RunChain && word_966BE != 0) {
|
|
runlist_PushMoveRun(edx);
|
|
return;
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
word_966BE = 1;
|
|
|
|
if (NxtPtr >= 0)
|
|
{
|
|
assert(NxtPtr < kMaxRuns);
|
|
NxtPtr = RunData[NxtPtr].next;
|
|
}
|
|
|
|
while (NxtPtr >= 0)
|
|
{
|
|
int RunPtr = NxtPtr;
|
|
|
|
if (RunPtr >= 0)
|
|
{
|
|
assert(RunPtr < kMaxRuns);
|
|
NxtPtr = RunData[RunPtr].next;
|
|
|
|
if (RunData[RunPtr].nObjIndex >= 0 || RunData[RunPtr].pObjActor) {
|
|
runlist_SendMessage(RunPtr, edx, func, ev);
|
|
}
|
|
}
|
|
}
|
|
|
|
word_966BE = 0;
|
|
if (nStackCount == 0) {
|
|
return;
|
|
}
|
|
|
|
edx = runlist_PopMoveRun();
|
|
NxtPtr = RunChain;
|
|
}
|
|
}
|
|
|
|
void runlist_InitChan()
|
|
{
|
|
ChannelList = -1;
|
|
ChannelLast = -1;
|
|
|
|
for (int i = 0; i < kMaxChannels; i++)
|
|
{
|
|
sRunChannels[i].c = -1;
|
|
sRunChannels[i].a = runlist_HeadRun();
|
|
sRunChannels[i].b = -1;
|
|
sRunChannels[i].d = 0;
|
|
}
|
|
}
|
|
|
|
void runlist_ChangeChannel(int eax, int nVal)
|
|
{
|
|
if (sRunChannels[eax].b < 0)
|
|
{
|
|
int nChannel = ChannelList;
|
|
ChannelList = eax;
|
|
sRunChannels[eax].b = nChannel;
|
|
}
|
|
|
|
sRunChannels[eax].c = nVal;
|
|
sRunChannels[eax].d |= 2;
|
|
}
|
|
|
|
void runlist_ReadyChannel(int eax)
|
|
{
|
|
if (sRunChannels[eax].b < 0)
|
|
{
|
|
int nChannel = ChannelList;
|
|
ChannelList = eax;
|
|
sRunChannels[eax].b = nChannel;
|
|
}
|
|
|
|
sRunChannels[eax].d |= 1;
|
|
}
|
|
|
|
void runlist_ProcessChannels()
|
|
{
|
|
#if 1
|
|
int v0;
|
|
int v1;
|
|
int v5;
|
|
int b;
|
|
int d;
|
|
|
|
do
|
|
{
|
|
v0 = -1;
|
|
v1 = -1;
|
|
|
|
while (ChannelList >= 0)
|
|
{
|
|
b = sRunChannels[ChannelList].b;
|
|
d = sRunChannels[ChannelList].d;
|
|
|
|
if (d & 2)
|
|
{
|
|
sRunChannels[ChannelList].d = d ^ 2;
|
|
runlist_SignalRun(sRunChannels[ChannelList].a, ChannelList, &ExhumedAI::ProcessChannel);
|
|
}
|
|
|
|
if (d & 1)
|
|
{
|
|
sRunChannels[ChannelList].d ^= 1;
|
|
runlist_SignalRun(sRunChannels[ChannelList].a, 0, &ExhumedAI::Process);
|
|
}
|
|
|
|
if (sRunChannels[ChannelList].d)
|
|
{
|
|
if (v1 == -1)
|
|
{
|
|
v1 = ChannelList;
|
|
v0 = ChannelList;
|
|
}
|
|
else
|
|
{
|
|
v5 = v0;
|
|
v0 = ChannelList;
|
|
sRunChannels[v5].b = ChannelList;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sRunChannels[ChannelList].b = -1;
|
|
}
|
|
ChannelList = b;
|
|
}
|
|
ChannelList = v1;
|
|
} while (v1 != -1);
|
|
|
|
#else
|
|
int edi = -1;
|
|
int esi = edi;
|
|
|
|
while (1)
|
|
{
|
|
int nChannel = ChannelList;
|
|
if (nChannel < 0)
|
|
{
|
|
ChannelList = esi;
|
|
if (esi != -1)
|
|
{
|
|
edi = -1;
|
|
esi = edi;
|
|
continue;
|
|
}
|
|
else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
int b = sRunChannels[nChannel].b;
|
|
int d = sRunChannels[nChannel].d;
|
|
|
|
if (d & 2)
|
|
{
|
|
sRunChannels[nChannel].d = d ^ 2;
|
|
runlist_SignalRun(sRunChannels[nChannel].a, ChannelList, &ExhumedAI::ProcessChannel);
|
|
}
|
|
|
|
if (d & 1)
|
|
{
|
|
sRunChannels[nChannel].d = d ^ 1;
|
|
runlist_SignalRun(sRunChannels[nChannel].a, 0, &ExhumedAI::Process);
|
|
}
|
|
|
|
if (sRunChannels[nChannel].d == 0)
|
|
{
|
|
sRunChannels[ChannelList].b = -1;
|
|
}
|
|
else
|
|
{
|
|
if (esi == -1)
|
|
{
|
|
esi = ChannelList;
|
|
edi = esi;
|
|
}
|
|
else
|
|
{
|
|
sRunChannels[edi].b = ChannelList;
|
|
edi = ChannelList;
|
|
}
|
|
}
|
|
|
|
ChannelList = b;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int runlist_FindChannel(int ax)
|
|
{
|
|
for (int i = 0; i < kMaxChannels; i++)
|
|
{
|
|
if (sRunChannels[i].c == -1)
|
|
{
|
|
sRunChannels[i].c = ax;
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int runlist_AllocChannel(int a)
|
|
{
|
|
if (a)
|
|
{
|
|
for (int i = 0; i < kMaxChannels; i++)
|
|
{
|
|
if (sRunChannels[i].c == a) {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
|
|
return runlist_FindChannel(a);
|
|
}
|
|
|
|
void runlist_ExecObjects()
|
|
{
|
|
runlist_ProcessChannels();
|
|
runlist_SignalRun(RunChain, 0, &ExhumedAI::Tick);
|
|
}
|
|
|
|
void runlist_ProcessSectorTag(sectortype* pSector, int nLotag, int nHitag)
|
|
{
|
|
int zListA[8];
|
|
int zListB[8];
|
|
|
|
int nChannel = runlist_AllocChannel(nHitag % 1000);
|
|
assert(nChannel >= 0 && nChannel < kMaxChannels);
|
|
|
|
int keyMask = (nHitag / 1000) << 12;
|
|
int nSpeed = nLotag / 1000;
|
|
|
|
if (!nSpeed) {
|
|
nSpeed = 1;
|
|
}
|
|
|
|
nSpeed <<= 2;
|
|
|
|
int nEffectTag = (nLotag % 1000);
|
|
|
|
switch (nEffectTag)
|
|
{
|
|
case 1: // Ceiling Doom door
|
|
{
|
|
/*
|
|
This function searches z-coordinates of neighboring sectors to find the
|
|
closest (next) ceiling starting at the given z-coordinate (thez).
|
|
*/
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->ceilingz, -1, -1);
|
|
|
|
|
|
int nElev = BuildElevC(0, nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->floorz, nextSectorP->ceilingz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwPress = BuildSwPressSector(nChannel, BuildLink(1, 1), pSector, keyMask);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nSwPress.first, nSwPress.second);
|
|
|
|
auto nSwPause = BuildSwPause(nChannel, BuildLink(2, -1, 0), 60);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nSwPause.first, nSwPause.second);
|
|
return;
|
|
}
|
|
|
|
case 2: // Floor Doom door
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, 1);
|
|
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->ceilingz, nextSectorP->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwPress = BuildSwPressSector(nChannel, BuildLink(1, 1), pSector, keyMask);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nSwPress.first, nSwPress.second);
|
|
|
|
auto nSwPause = BuildSwPause(nChannel, BuildLink(2, -1, 0), 60);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nSwPause.first, nSwPause.second);
|
|
return;
|
|
}
|
|
|
|
case 3:
|
|
case 4:
|
|
case 19:
|
|
case 20:
|
|
case 22:
|
|
case 29:
|
|
case 30:
|
|
case 46:
|
|
case 47:
|
|
case 60:
|
|
case 65:
|
|
case 66:
|
|
case 67:
|
|
case 69:
|
|
case 72:
|
|
case 73:
|
|
case 74:
|
|
case 76:
|
|
case 77:
|
|
case 78:
|
|
case 79:
|
|
case 81:
|
|
case 82:
|
|
case 83:
|
|
case 84:
|
|
case 85:
|
|
{
|
|
return;
|
|
}
|
|
|
|
case 5: // Permanent floor raise
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz + 1, -1, -1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->floorz, nextSectorP->ceilingz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
return;
|
|
}
|
|
|
|
case 6: // Touchplate floor lower, single
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, -1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), 400, 400, 2, nextSectorP->floorz, pSector->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwitch = BuildSwStepOn(nChannel, BuildLink(2, 1, 1), pSector);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
|
|
pSector->floorz = nextSectorP->floorz;
|
|
return;
|
|
}
|
|
|
|
case 7: // Touchplate floor lower, multiple
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, 1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->floorz, nextSectorP->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwitch = BuildSwStepOn(nChannel, BuildLink(1, 1), pSector);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
|
|
auto nSwitch2 = BuildSwNotOnPause(nChannel, BuildLink(2, -1, 0), pSector, 8);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch2.first, nSwitch2.second);
|
|
return;
|
|
}
|
|
|
|
case 8: // Permanent floor lower
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, 1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->floorz, nextSectorP->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
return;
|
|
}
|
|
|
|
case 9: // Switch activated lift down
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, 1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->floorz, nextSectorP->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 150);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 10: // Touchplate Floor Raise
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, -1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->floorz, nextSectorP->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwitch = BuildSwStepOn(nChannel, BuildLink(1, 1), pSector);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
|
|
auto nSwitch2 = BuildSwNotOnPause(nChannel, BuildLink(2, -1, 0), pSector, 8);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch2.first, nSwitch2.second);
|
|
return;
|
|
}
|
|
|
|
case 11: // Permanent floor raise
|
|
case 14: // Sector raise / lower
|
|
case 38: // Sector raise / lower
|
|
{
|
|
/*
|
|
fix for original behaviour - nextSector could be -1 the and game would do an invalid memory read
|
|
when getting the floorz for nextSector. Here, we assume 0 and only set the correct value if nextSector
|
|
is valid.
|
|
*/
|
|
int zVal = 0;
|
|
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, -1);
|
|
if (nextSectorP != nullptr) {
|
|
zVal = nextSectorP->floorz;
|
|
}
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->floorz, zVal);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
return;
|
|
}
|
|
|
|
case 12: // Switch activated lift up
|
|
{
|
|
/*
|
|
fix for original behaviour - nextSector could be -1 the and game would do an invalid memory read
|
|
when getting the floorz for nextSector. Here, we assume 0 and only set the correct value if nextSector
|
|
is valid.
|
|
*/
|
|
int zVal = 0;
|
|
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, -1);
|
|
if (nextSectorP != nullptr) {
|
|
zVal = nextSectorP->floorz;
|
|
}
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->floorz, zVal);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 150);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 13: // Bobbing floor
|
|
{
|
|
/*
|
|
fix for original behaviour - nextSector could be -1 the and game would do an invalid memory read
|
|
when getting the floorz for nextSector. Here, we assume 0 and only set the correct value if nextSector
|
|
is valid.
|
|
*/
|
|
int zVal = 0;
|
|
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, 1);
|
|
if (nextSectorP != nullptr) {
|
|
zVal = nextSectorP->floorz;
|
|
}
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->floorz, zVal);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwitch = BuildSwReady(nChannel, BuildLink(2, 1, 0));
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 15: // Sector raise/lower
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, -1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->floorz, nextSectorP->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
return;
|
|
}
|
|
|
|
case 16: // Stuttering noise (floor makes noise)
|
|
{
|
|
int nElev = BuildElevC(0, nChannel, pSector, FindWallSprites(pSector), 200, nSpeed * 100, 2, pSector->ceilingz, pSector->floorz - 8);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwitch = BuildSwStepOn(nChannel, BuildLink(1, 1), pSector);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
|
|
auto nSwitch2 = BuildSwReady(nChannel, BuildLink(2, -1, 0));
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch2.first, nSwitch2.second);
|
|
return;
|
|
}
|
|
|
|
case 17: // Reserved?
|
|
{
|
|
int nElev = BuildElevC(0, nChannel, pSector, FindWallSprites(pSector), 200, nSpeed * 100, 2, pSector->ceilingz, pSector->floorz - 8);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwitch = BuildSwReady(nChannel, BuildLink(2, -1, 0));
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 18: // Raises floor AND lowers ceiling
|
|
{
|
|
int ebx = ((pSector->floorz - pSector->ceilingz) / 2) + pSector->ceilingz;
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), 200, nSpeed * 100, 2, pSector->floorz, ebx);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
int ebx2 = (((pSector->floorz - pSector->ceilingz) / 2) + pSector->ceilingz) - 8;
|
|
|
|
int nElev2 = BuildElevC(0, nChannel, pSector, FindWallSprites(pSector), 200, nSpeed * 100, 2, pSector->ceilingz, ebx2);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev2, 0);
|
|
|
|
auto nSwitch = BuildSwStepOn(nChannel, BuildLink(1, 1), pSector);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 21: // Touchplate
|
|
{
|
|
auto nSwitch = BuildSwStepOn(nChannel, BuildLink(2, 1, 1), pSector);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 23: // Floor raise, Sychronize
|
|
{
|
|
/*
|
|
fix for original behaviour - nextSector could be -1 the and game would do an invalid memory read
|
|
when getting the floorz for nextSector. Here, we assume 0 and only set the correct value if nextSector
|
|
is valid.
|
|
*/
|
|
int zVal = 0;
|
|
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, 1);
|
|
if (nextSectorP) {
|
|
zVal = nextSectorP->floorz;
|
|
}
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), 32767, 200, 2, pSector->floorz, zVal);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), nSpeed * 60);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 24: // Ceiling door, channel trigger only
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->ceilingz, -1, -1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevC(0, nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->floorz, nextSectorP->ceilingz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 60);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 25:
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, 1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->floorz, nextSectorP->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 300);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 26:
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, 1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->floorz, nextSectorP->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 450);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 27:
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, 1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->floorz, nextSectorP->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 600);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 28:
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, 1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->floorz, nextSectorP->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 900);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 31: // Touchplate
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, 1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), 0x7FFF, 0x7FFF, 2, pSector->floorz, nextSectorP->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwitch = BuildSwStepOn(nChannel, BuildLink(1, 1), pSector);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 32:
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, 1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), 0x7FFF, 0x7FFF, 2, pSector->floorz, nextSectorP->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
return;
|
|
}
|
|
|
|
case 33: // Ceiling Crusher
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->ceilingz, -1, -1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevC(20, nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, nextSectorP->ceilingz, pSector->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
return;
|
|
}
|
|
|
|
case 34: // Triggerable Ceiling Crusher(Inactive)
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->ceilingz, -1, -1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevC(28, nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, nextSectorP->ceilingz, pSector->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
return;
|
|
}
|
|
|
|
case 35: // Destructible sector
|
|
case 36:
|
|
{
|
|
nEnergyTowers++;
|
|
|
|
auto nEnergyBlock = BuildEnergyBlock(pSector);
|
|
|
|
if (nLotag == 36) {
|
|
pFinaleSpr = nEnergyBlock;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
case 37:
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, 1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->floorz, nextSectorP->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
return;
|
|
}
|
|
|
|
case 39: // Touchplate
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, 1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), 0x7FFF, 0x7FFF, 2, pSector->floorz, nextSectorP->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwitch = BuildSwStepOn(nChannel, BuildLink(1, 1), pSector);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
|
|
auto nSwitch2 = BuildSwNotOnPause(nChannel, BuildLink(2, -1, 0), pSector, 8);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch2.first, nSwitch2.second);
|
|
return;
|
|
}
|
|
|
|
case 40: // Moving sector(follows waypoints)
|
|
{
|
|
AddMovingSector(pSector, nLotag, nHitag % 1000, 2);
|
|
return;
|
|
}
|
|
|
|
case 41: // Moving sector(follows waypoints)
|
|
{
|
|
AddMovingSector(pSector, nLotag, nHitag % 1000, 18);
|
|
return;
|
|
}
|
|
|
|
case 42: // Moving sector(follows waypoints)
|
|
{
|
|
AddMovingSector(pSector, nLotag, nHitag % 1000, 58);
|
|
return;
|
|
}
|
|
|
|
case 43: // Moving sector(follows waypoints)
|
|
{
|
|
AddMovingSector(pSector, nLotag, nHitag % 1000, 122);
|
|
return;
|
|
}
|
|
|
|
case 44: // Moving sector(follows waypoints)
|
|
{
|
|
AddMovingSector(pSector, nLotag, nHitag % 1000, 90);
|
|
return;
|
|
}
|
|
|
|
case 45: // Pushbox sector
|
|
{
|
|
CreatePushBlock(pSector);
|
|
return;
|
|
}
|
|
|
|
case 48: // Ceiling lower
|
|
{
|
|
/*
|
|
fix for original behaviour - nextSector could be -1 the and game would do an invalid memory read
|
|
when getting the floorz for nextSector. Here, we assume 0 and only set the correct value if nextSector
|
|
is valid.
|
|
*/
|
|
int zVal = 0;
|
|
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->ceilingz, -1, 1);
|
|
if (nextSectorP != nullptr) {
|
|
zVal = nextSectorP->ceilingz;
|
|
}
|
|
|
|
int nElev = BuildElevC(0, nChannel, pSector, FindWallSprites(pSector), 200, nSpeed * 100, 2, pSector->ceilingz, zVal);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
return;
|
|
}
|
|
|
|
case 49:
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->ceilingz, -1, -1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevC(0, nChannel, pSector, FindWallSprites(pSector), 200, nSpeed * 100, 2, pSector->ceilingz, nextSectorP->ceilingz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
return;
|
|
}
|
|
|
|
case 50: // Floor lower / raise
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->ceilingz, 1, 1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), 0x7FFF, 200, 2, nextSectorP->floorz, pSector->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
return;
|
|
}
|
|
|
|
case 51:
|
|
{
|
|
int edx = ((pSector->floorz - pSector->ceilingz) / 2) + pSector->ceilingz;
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), 200, nSpeed * 100, 2, pSector->floorz, edx);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
int eax = (((pSector->floorz - pSector->ceilingz) / 2) + pSector->ceilingz) - 8;
|
|
|
|
nElev = BuildElevC(0, nChannel, pSector, FindWallSprites(pSector), 200, nSpeed * 100, 2, pSector->ceilingz, eax);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwitch = BuildSwReady(nChannel, BuildLink(2, 1, 0));
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 52:
|
|
{
|
|
int eax = ((pSector->floorz - pSector->ceilingz) / 2) + pSector->ceilingz;
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, eax, pSector->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
eax = ((pSector->floorz - pSector->ceilingz) / 2) + pSector->ceilingz;
|
|
|
|
nElev = BuildElevC(0, nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, eax, pSector->ceilingz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwitch = BuildSwPressSector(nChannel, BuildLink(1, 1), pSector, keyMask);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
|
|
nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 60);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 53:
|
|
{
|
|
int eax = ((pSector->floorz - pSector->ceilingz) / 2) + pSector->ceilingz;
|
|
|
|
int nElev = BuildElevC(0, nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, eax, pSector->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
eax = ((pSector->floorz - pSector->ceilingz) / 2) + pSector->ceilingz;
|
|
|
|
nElev = BuildElevC(0, nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, eax, pSector->ceilingz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 150);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 54:
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->ceilingz, -1, -1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevC(0, nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->floorz, nextSectorP->ceilingz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwitch = BuildSwPressSector(nChannel, BuildLink(1, 1), pSector, keyMask);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 55:
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->ceilingz, -1, -1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevC(0, nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->floorz, nextSectorP->ceilingz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwitch = BuildSwPressSector(nChannel, BuildLink(1, 1), pSector, keyMask);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 56:
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->ceilingz, -1, -1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevC(0, nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->floorz, nextSectorP->ceilingz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
return;
|
|
}
|
|
|
|
case 57:
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, 1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->ceilingz, nextSectorP->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
return;
|
|
}
|
|
|
|
case 58:
|
|
{
|
|
auto nSwitch = BuildSwPressSector(nChannel, BuildLink(1, 1), pSector, keyMask);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
|
|
// Fall through to case 62
|
|
[[fallthrough]];
|
|
}
|
|
case 63: // Ceiling door, kill trigger (Enemy death triggers door)
|
|
{
|
|
if (nLotag == 63) {
|
|
nEnergyChan = nChannel;
|
|
}
|
|
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->ceilingz, -1, -1);
|
|
|
|
|
|
int nElev = BuildElevC(0, nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->floorz, nextSectorP->ceilingz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
return;
|
|
}
|
|
|
|
case 59:
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, 1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->floorz, nextSectorP->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwitch = BuildSwStepOn(nChannel, BuildLink(1, 1), pSector);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
|
|
auto nSwitch2 = BuildSwNotOnPause(nChannel, BuildLink(1, 1), pSector, 60);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch2.first, nSwitch2.second);
|
|
return;
|
|
}
|
|
|
|
case 61:
|
|
{
|
|
zListB[0] = pSector->floorz;
|
|
int var_1C = 1;
|
|
|
|
while (1)
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, -1);
|
|
if (nextSectorP == nullptr || var_1C >= 8) {
|
|
break;
|
|
}
|
|
|
|
zListB[var_1C] = nextSectorP->floorz;
|
|
|
|
var_1C++;
|
|
}
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, var_1C,
|
|
zListB[0], zListB[1], zListB[2], zListB[3], zListB[4], zListB[5], zListB[6], zListB[7]);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
return;
|
|
}
|
|
|
|
case 62:
|
|
{
|
|
zListA[0] = pSector->floorz;
|
|
int var_20 = 1;
|
|
|
|
while (1)
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, 1);
|
|
if (nextSectorP == nullptr || var_20 >= 8) {
|
|
break;
|
|
}
|
|
|
|
zListA[var_20] = nextSectorP->floorz;
|
|
|
|
var_20++;
|
|
}
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, var_20,
|
|
zListA[0], zListA[1], zListA[2], zListA[3], zListA[4], zListA[5], zListA[6], zListA[7]);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
return;
|
|
}
|
|
|
|
case 64:
|
|
{
|
|
auto nSwitch = BuildSwStepOn(nChannel, BuildLink(2, 0, 0), pSector);
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 68:
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->floorz, 1, 1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevF(nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, pSector->floorz, nextSectorP->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
return;
|
|
}
|
|
|
|
case 70:
|
|
case 71:
|
|
{
|
|
auto nextSectorP = nextsectorneighborzptr(pSector, pSector->ceilingz, -1, -1);
|
|
if (nextSectorP == nullptr) break;
|
|
|
|
|
|
int nElev = BuildElevC(0, nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, (int)pSector->floorz, (int)nextSectorP->ceilingz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
|
|
auto nSwitch = BuildSwPressSector(nChannel, BuildLink(1, 1), pSector, keyMask);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
|
|
auto nSwitch2 = BuildSwPause(nChannel, BuildLink(2, -1, 0), 60);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch2.first, nSwitch2.second);
|
|
return;
|
|
}
|
|
|
|
case 75:
|
|
{
|
|
int nElev = BuildElevC(0, nChannel, pSector, FindWallSprites(pSector), nSpeed * 100, nSpeed * 100, 2, (int)pSector->ceilingz, (int)pSector->floorz);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nElev, 0);
|
|
return;
|
|
}
|
|
|
|
case 80:
|
|
{
|
|
pSector->Flag |= 0x8000;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void runlist_ProcessWallTag(walltype* pWall, int nLotag, int nHitag)
|
|
{
|
|
auto& wal = *pWall;
|
|
int nChannel = runlist_AllocChannel(nHitag % 1000);
|
|
assert(nChannel >= 0 && nChannel < kMaxChannels);
|
|
|
|
int nPanSpeed = nLotag / 1000;
|
|
if (!nPanSpeed) {
|
|
nPanSpeed = 1;
|
|
}
|
|
|
|
nPanSpeed <<= 2;
|
|
|
|
int nEffectTag = nLotag % 1000;
|
|
|
|
switch (nEffectTag)
|
|
{
|
|
default:
|
|
return;
|
|
|
|
case 1:
|
|
{
|
|
int nWallFace = BuildWallFace(nChannel, pWall, 2, pWall->picnum, pWall->picnum + 1);
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nWallFace, 0x70000);
|
|
|
|
auto nSwitch = BuildSwPressWall(nChannel, BuildLink(2, nEffectTag, 0), pWall);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 6:
|
|
{
|
|
auto nSwitch = BuildSwPressWall(nChannel, BuildLink(2, 1, 0), pWall);
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 7: // Regular switch
|
|
{
|
|
int nWallFace = BuildWallFace(nChannel, pWall, 2, pWall->picnum, pWall->picnum + 1);
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nWallFace, 0x70000);
|
|
|
|
auto nSwitch = BuildSwPressWall(nChannel, BuildLink(1, 1), pWall);
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 8: // Reverse switch
|
|
{
|
|
int nWallFace = BuildWallFace(nChannel, pWall, 2, pWall->picnum, pWall->picnum + 1);
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nWallFace, 0x70000);
|
|
|
|
auto nSwitch = BuildSwPressWall(nChannel, BuildLink(2, -1, 0), pWall);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 9: // Invisible switch
|
|
{
|
|
auto nSwitch = BuildSwPressWall(nChannel, BuildLink(2, 1, 1), pWall);
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 10:
|
|
{
|
|
auto nSwitch = BuildSwPressWall(nChannel, BuildLink(2, -1, 0), pWall);
|
|
runlist_AddRunRec(sRunChannels[nChannel].a,nSwitch.first, nSwitch.second);
|
|
return;
|
|
}
|
|
|
|
case 12: // Twin star trek door
|
|
case 14:
|
|
case 16:
|
|
case 19:
|
|
case 20:
|
|
{
|
|
walltype* pLastWall = nullptr;
|
|
walltype* p2ndLastWall = nullptr;
|
|
|
|
auto pStart = pWall;
|
|
|
|
while (1)
|
|
{
|
|
pWall = pWall->point2Wall(); // get the next (right side) wall point
|
|
|
|
if (pStart == pWall) { // we've looped back around
|
|
break;
|
|
}
|
|
|
|
p2ndLastWall = pLastWall;
|
|
pLastWall = pWall;
|
|
}
|
|
|
|
auto pWall2 = pStart->point2Wall();
|
|
auto pWall3 = pWall2->point2Wall();
|
|
auto pWall4 = pWall3->point2Wall();
|
|
|
|
int nSlide = BuildSlide(nChannel, pStart, pLastWall, p2ndLastWall, pWall2, pWall3, pWall4);
|
|
|
|
runlist_AddRunRec(sRunChannels[nChannel].a, nSlide, 0x80000);
|
|
return;
|
|
}
|
|
|
|
case 24: // Waterfall
|
|
{
|
|
AddFlow(pWall, nPanSpeed, 3);
|
|
return;
|
|
}
|
|
|
|
case 25: // Inverse waterfall
|
|
{
|
|
AddFlow(pWall, nPanSpeed, 2);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
int runlist_CheckRadialDamage(DExhumedActor* pActor)
|
|
{
|
|
auto pSprite = &pActor->s();
|
|
auto pRadialSpr = &pRadialActor->s();
|
|
|
|
if (pActor == pRadialActor) {
|
|
return 0;
|
|
}
|
|
|
|
if (!(pSprite->cstat & 0x101)) {
|
|
return 0;
|
|
}
|
|
|
|
if (pSprite->statnum >= kMaxStatus || pRadialSpr->statnum >= kMaxStatus) {
|
|
return 0;
|
|
}
|
|
|
|
if (pSprite->statnum != 100 && pActor == pRadialActor->pTarget) {
|
|
return 0;
|
|
}
|
|
|
|
int x = (pSprite->x - pRadialSpr->x) >> 8;
|
|
int y = (pSprite->y - pRadialSpr->y) >> 8;
|
|
int z = (pSprite->z - pRadialSpr->z) >> 12;
|
|
|
|
if (abs(x) > nDamageRadius) {
|
|
return 0;
|
|
}
|
|
|
|
if (abs(y) > nDamageRadius) {
|
|
return 0;
|
|
}
|
|
|
|
if (abs(z) > nDamageRadius) {
|
|
return 0;
|
|
}
|
|
|
|
int edi = 0;
|
|
|
|
uint32_t xDiff = abs(x);
|
|
uint32_t yDiff = abs(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 nDist = ksqrt(sqrtNum);
|
|
|
|
if (nDist < nDamageRadius)
|
|
{
|
|
uint16_t nCStat = pSprite->cstat;
|
|
pSprite->cstat = 0x101;
|
|
|
|
if (((kStatExplodeTarget - pSprite->statnum) <= 1) ||
|
|
cansee(pRadialSpr->x,
|
|
pRadialSpr->y,
|
|
pRadialSpr->z - 512,
|
|
pRadialSpr->sector(),
|
|
pSprite->x,
|
|
pSprite->y,
|
|
pSprite->z - 8192,
|
|
pSprite->sector()))
|
|
{
|
|
edi = (nRadialDamage * (nDamageRadius - nDist)) / nDamageRadius;
|
|
|
|
if (edi < 0) {
|
|
edi = 0;
|
|
}
|
|
else if (edi > 20)
|
|
{
|
|
int nAngle = GetMyAngle(x, y);
|
|
|
|
pSprite->xvel += (edi * bcos(nAngle)) >> 3;
|
|
pSprite->yvel += (edi * bsin(nAngle)) >> 3;
|
|
pSprite->zvel -= edi * 24;
|
|
|
|
if (pSprite->zvel < -3584) {
|
|
pSprite->zvel = -3584;
|
|
}
|
|
}
|
|
}
|
|
|
|
pSprite->cstat = nCStat;
|
|
}
|
|
|
|
if (edi > 0x7FFF) {
|
|
edi = 0x7FFF;
|
|
}
|
|
|
|
return edi;
|
|
}
|
|
|
|
void runlist_RadialDamageEnemy(DExhumedActor* pActor, int nDamage, int nRadius)
|
|
{
|
|
if (!nRadius) {
|
|
return;
|
|
}
|
|
|
|
if (pRadialActor == nullptr)
|
|
{
|
|
nRadialDamage = nDamage * 4;
|
|
nDamageRadius = nRadius;
|
|
pRadialActor = pActor;
|
|
|
|
runlist_ExplodeSignalRun();
|
|
|
|
pRadialActor = nullptr;
|
|
}
|
|
}
|
|
|
|
void runlist_DamageEnemy(DExhumedActor* pActor, DExhumedActor* pActor2, int nDamage)
|
|
{
|
|
auto pSprite = &pActor->s();
|
|
|
|
if (pSprite->statnum >= kMaxStatus) {
|
|
return;
|
|
}
|
|
|
|
int nRun = pSprite->owner;
|
|
if (nRun <= -1) {
|
|
return;
|
|
}
|
|
|
|
int nPreCreaturesKilled = nCreaturesKilled;
|
|
|
|
RunListEvent ev{};
|
|
ev.pOtherActor = pActor2;
|
|
ev.nDamage = nDamage * 4;
|
|
runlist_SendMessage(nRun, -1, &ExhumedAI::Damage, &ev);
|
|
|
|
// is there now one less creature? (has one died)
|
|
if (nPreCreaturesKilled < nCreaturesKilled && pActor2 != nullptr)
|
|
{
|
|
if (pActor2->s().statnum != 100) {
|
|
return;
|
|
}
|
|
|
|
int nPlayer = GetPlayerFromActor(pActor2);
|
|
PlayerList[nPlayer].nTauntTimer--;
|
|
|
|
if (PlayerList[nPlayer].nTauntTimer <= 0)
|
|
{
|
|
// Do a taunt
|
|
auto pPlayerActor = PlayerList[nPlayer].Actor();
|
|
auto pSector = pPlayerActor->s().sector();
|
|
|
|
if (!(pSector->Flag & kSectUnderwater))
|
|
{
|
|
int ebx = 0x4000;
|
|
|
|
if (nPlayer == nLocalPlayer) {
|
|
ebx = 0x6000;
|
|
}
|
|
|
|
D3PlayFX(StaticSound[kSoundTauntStart + (RandomSize(3) % 5)], PlayerList[nPlayer].pDoppleSprite, ebx);
|
|
}
|
|
|
|
PlayerList[nPlayer].nTauntTimer = RandomSize(3) + 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
END_PS_NS
|