1 //-------------------------------------------------------------------------
2 /*
3 Copyright (C) 2010-2019 EDuke32 developers and contributors
4 Copyright (C) 2019 sirlemonhead, Nuke.YKT
5 
6 This file is part of PCExhumed.
7 
8 PCExhumed is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License version 2
10 as published by the Free Software Foundation.
11 
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 
16 See the GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 */
22 //-------------------------------------------------------------------------
23 
24 #include "exhumed.h"
25 #include "engine.h"
26 #include "runlist.h"
27 #include "player.h"
28 #include "trigdat.h"
29 #include "move.h"
30 #include "random.h"
31 #include "mummy.h"
32 #include "fish.h"
33 #include "lion.h"
34 #include "rex.h"
35 #include "set.h"
36 #include "rat.h"
37 #include "wasp.h"
38 #include "anubis.h"
39 #include "snake.h"
40 #include "scorp.h"
41 #include "ra.h"
42 #include "spider.h"
43 #include "bullet.h"
44 #include "queen.h"
45 #include "roach.h"
46 #include "bubbles.h"
47 #include "lavadude.h"
48 #include "grenade.h"
49 #include "object.h"
50 #include "switch.h"
51 #include "anims.h"
52 #include "sound.h"
53 #include "init.h"
54 #include "lighting.h"
55 #include <assert.h>
56 
57 #include "save.h"
58 
59 //#define kFuncMax	0x260000 // the number 38 stored in the high word of an int
60 #define kFuncMax		39
61 #define kMaxRunStack	200
62 
63 
64 short RunCount = -1;
65 short nRadialSpr = -1;
66 short nStackCount = 0;
67 short word_966BE = 0;
68 short ChannelList = -1;
69 short ChannelLast = -1;
70 
71 int nRadialOwner;
72 int nDamageRadius;
73 int nRadialDamage;
74 short RunChain;
75 short NewRun;
76 
77 int sRunStack[kMaxRunStack];
78 short RunFree[kMaxRuns];
79 RunChannel sRunChannels[kMaxChannels];
80 RunStruct RunData[kMaxRuns];
81 
82 AiFunc aiFunctions[kFuncMax] = {
83     FuncElev,
84     FuncSwReady,
85     FuncSwPause,
86     FuncSwStepOn,
87     FuncSwNotOnPause,
88     FuncSwPressSector,
89     FuncSwPressWall,
90     FuncWallFace,
91     FuncSlide,
92     FuncAnubis,
93     FuncPlayer,
94     FuncBullet,
95     FuncSpider,
96     FuncCreatureChunk,
97     FuncMummy,
98     FuncGrenade,
99     FuncAnim,
100     FuncSnake,
101     FuncFish,
102     FuncLion,
103     FuncBubble,
104     FuncLava,
105     FuncLavaLimb,
106     FuncObject,
107     FuncRex,
108     FuncSet,
109     FuncQueen,
110     FuncQueenHead,
111     FuncRoach,
112     FuncQueenEgg,
113     FuncWasp,
114     FuncTrap,
115     FuncFishLimb,
116     FuncRa,
117     FuncScorp,
118     FuncSoul,
119     FuncRat,
120     FuncEnergyBlock,
121     FuncSpark,
122 };
123 
124 
runlist_GrabRun()125 int runlist_GrabRun()
126 {
127     assert(RunCount > 0 && RunCount <= kMaxRuns);
128 
129     RunCount--;
130 
131     return RunFree[RunCount];
132 }
133 
runlist_FreeRun(int nRun)134 int runlist_FreeRun(int nRun)
135 {
136     assert(RunCount >= 0 && RunCount < kMaxRuns);
137     assert(nRun >= 0 && nRun < kMaxRuns);
138 
139     RunFree[RunCount] = nRun;
140     RunCount++;
141 
142     RunData[nRun]._6 = -1;
143     RunData[nRun].nMoves = -1;
144     RunData[nRun]._4 = RunData[nRun]._6;
145 
146     return 1;
147 }
148 
runlist_HeadRun()149 int runlist_HeadRun()
150 {
151     int nRun = runlist_GrabRun();
152 
153     RunData[nRun]._4 = -1;
154     RunData[nRun]._6 = -1;
155 
156     return nRun;
157 }
158 
runlist_InitRun()159 void runlist_InitRun()
160 {
161     int i;
162 
163     RunCount = kMaxRuns;
164     nStackCount = 0;
165 
166     for (i = 0; i < kMaxRuns; i++)
167     {
168         RunData[i].nMoves = -1;
169         RunData[i]._6 = -1;
170         RunFree[i] = i;
171         RunData[i]._4 = -1;
172     }
173 
174     int nRun = runlist_HeadRun();
175     RunChain = nRun;
176     NewRun = nRun;
177 
178     for (i = 0; i < kMaxPlayers; i++) {
179         PlayerList[i].nRun = -1;
180     }
181 
182     nRadialSpr = -1;
183 }
184 
runlist_UnlinkRun(int nRun)185 int runlist_UnlinkRun(int nRun)
186 {
187     assert(nRun >= 0 && nRun < kMaxRuns);
188 
189     if (nRun == RunChain)
190     {
191         RunChain = RunData[nRun]._4;
192         return nRun;
193     }
194 
195     if (RunData[nRun]._6 >= 0)
196     {
197         RunData[RunData[nRun]._6]._4 = RunData[nRun]._4;
198     }
199 
200     if (RunData[nRun]._4 >= 0)
201     {
202         RunData[RunData[nRun]._4]._6 = RunData[nRun]._6;
203     }
204 
205     return nRun;
206 }
207 
runlist_InsertRun(int RunLst,int RunNum)208 int runlist_InsertRun(int RunLst, int RunNum)
209 {
210     assert(RunLst >= 0 && RunLst < kMaxRuns);
211     assert(RunNum >= 0 && RunNum < kMaxRuns);
212 
213     RunData[RunNum]._6 = RunLst;
214     int val = RunData[RunLst]._4;
215     RunData[RunNum]._4 = val;
216 
217     if (val >= 0) {
218         RunData[val]._6 = RunNum;
219     }
220 
221     RunData[RunLst]._4 = RunNum;
222     return RunNum;
223 }
224 
runlist_AddRunRec(int a,int b)225 int runlist_AddRunRec(int a, int b)
226 {
227     int nRun = runlist_GrabRun();
228 
229     RunData[nRun].nMoves = b; // TODO - split this into the two shorts?
230 
231     runlist_InsertRun(a, nRun);
232     return nRun;
233 }
234 
runlist_DoSubRunRec(int RunPtr)235 void runlist_DoSubRunRec(int RunPtr)
236 {
237     assert(RunPtr >= 0 && RunPtr < kMaxRuns);
238 
239     runlist_UnlinkRun(RunPtr);
240     runlist_FreeRun(RunPtr);
241 }
242 
runlist_CleanRunRecs()243 void runlist_CleanRunRecs()
244 {
245     int nextPtr = RunChain;
246 
247     if (nextPtr >= 0)
248     {
249         assert(nextPtr < kMaxRuns);
250         nextPtr = RunData[nextPtr]._4;
251     }
252 
253     while (nextPtr >= 0)
254     {
255         int runPtr = nextPtr;
256         assert(runPtr < kMaxRuns);
257 
258         int val = RunData[runPtr].nRef; // >> 16;
259         nextPtr = RunData[runPtr]._4;
260 
261         if (val < 0) {
262             runlist_DoSubRunRec(runPtr);
263         }
264     }
265 }
266 
runlist_SubRunRec(int RunPtr)267 void runlist_SubRunRec(int RunPtr)
268 {
269     assert(RunPtr >= 0 && RunPtr < kMaxRuns);
270 
271     RunData[RunPtr].nMoves = -totalmoves;
272 }
273 
runlist_SendMessageToRunRec(int nRun,int nMessage,int nDamage)274 void runlist_SendMessageToRunRec(int nRun, int nMessage, int nDamage)
275 {
276     int nFunc = RunData[nRun].nRef;// >> 16;
277 
278     if (nFunc < 0) {
279         return;
280     }
281 
282     assert(nFunc >= 0 && nFunc <= kFuncMax);
283 
284     if (nFunc > kFuncMax) {
285         return;
286     }
287 
288     assert(nFunc < kFuncMax); // REMOVE
289 
290     // do function pointer call here.
291     aiFunctions[nFunc](nMessage, nDamage, nRun);
292 }
293 
runlist_ExplodeSignalRun()294 void runlist_ExplodeSignalRun()
295 {
296     short nextPtr = RunChain;
297 
298     if (nextPtr >= 0)
299     {
300         assert(nextPtr < kMaxRuns);
301         nextPtr = RunData[nextPtr]._4;
302     }
303 
304     // LOOP
305     while (nextPtr >= 0)
306     {
307         int runPtr = nextPtr;
308         assert(runPtr < kMaxRuns);
309 
310         int val = RunData[runPtr].nMoves;
311         nextPtr = RunData[runPtr]._4;
312 
313         if (val >= 0)
314         {
315             runlist_SendMessageToRunRec(runPtr, 0xA0000, 0);
316         }
317     }
318 }
319 
runlist_PushMoveRun(int eax)320 void runlist_PushMoveRun(int eax)
321 {
322     if (nStackCount < kMaxRunStack)
323     {
324         sRunStack[nStackCount] = eax;
325         nStackCount++;
326     }
327 }
328 
runlist_PopMoveRun()329 int runlist_PopMoveRun()
330 {
331     if (nStackCount <= 0) {
332         bail2dos("PopMoveRun() called inappropriately\n");
333         exit(-1);
334     }
335 
336     nStackCount--;
337     return sRunStack[nStackCount];
338 }
339 
runlist_SignalRun(int NxtPtr,int edx)340 void runlist_SignalRun(int NxtPtr, int edx)
341 {
342     if (NxtPtr == RunChain && word_966BE != 0) {
343         runlist_PushMoveRun(edx);
344         return;
345     }
346 
347     while (1)
348     {
349         word_966BE = 1;
350 
351         if (NxtPtr >= 0)
352         {
353             assert(NxtPtr < kMaxRuns);
354             NxtPtr = RunData[NxtPtr]._4;
355         }
356 
357         while (NxtPtr >= 0)
358         {
359             int RunPtr = NxtPtr;
360 
361             if (RunPtr >= 0)
362             {
363                 assert(RunPtr < kMaxRuns);
364                 int val = RunData[RunPtr].nMoves;
365                 NxtPtr = RunData[RunPtr]._4;
366 
367                 if (val >= 0) {
368                     runlist_SendMessageToRunRec(RunPtr, edx, 0);
369                 }
370             }
371         }
372 
373         word_966BE = 0;
374         if (nStackCount == 0) {
375             return;
376         }
377 
378         edx = runlist_PopMoveRun();
379         NxtPtr = RunChain;
380     }
381 }
382 
runlist_InitChan()383 void runlist_InitChan()
384 {
385     ChannelList = -1;
386     ChannelLast = -1;
387 
388     for (int i = 0; i < kMaxChannels; i++)
389     {
390         sRunChannels[i].c = -1;
391         sRunChannels[i].a = runlist_HeadRun();
392         sRunChannels[i].b = -1;
393         sRunChannels[i].d = 0;
394     }
395 }
396 
runlist_ChangeChannel(int eax,short nVal)397 void runlist_ChangeChannel(int eax, short nVal)
398 {
399     if (sRunChannels[eax].b < 0)
400     {
401         short nChannel = ChannelList;
402         ChannelList = eax;
403         sRunChannels[eax].b = nChannel;
404     }
405 
406     sRunChannels[eax].c = nVal;
407     sRunChannels[eax].d |= 2;
408 }
409 
runlist_ReadyChannel(short eax)410 void runlist_ReadyChannel(short eax)
411 {
412     if (sRunChannels[eax].b < 0)
413     {
414         short nChannel = ChannelList;
415         ChannelList = eax;
416         sRunChannels[eax].b = nChannel;
417     }
418 
419     sRunChannels[eax].d |= 1;
420 }
421 
runlist_ProcessChannels()422 void runlist_ProcessChannels()
423 {
424 #if 1
425     short v0;
426     short v1;
427     int v5;
428     short b;
429     short d;
430 
431     do
432     {
433         v0 = -1;
434         v1 = -1;
435 
436         while (ChannelList >= 0)
437         {
438             b = sRunChannels[ChannelList].b;
439             d = sRunChannels[ChannelList].d;
440 
441             if (d & 2)
442             {
443                 sRunChannels[ChannelList].d = d ^ 2;
444                 runlist_SignalRun(sRunChannels[ChannelList].a, ChannelList | 0x10000);
445             }
446 
447             if (d & 1)
448             {
449                 sRunChannels[ChannelList].d ^= 1;
450                 runlist_SignalRun(sRunChannels[ChannelList].a, 0x30000);
451             }
452 
453             if (sRunChannels[ChannelList].d)
454             {
455                 if (v1 == -1)
456                 {
457                     v1 = ChannelList;
458                     v0 = ChannelList;
459                 }
460                 else
461                 {
462                     v5 = v0;
463                     v0 = ChannelList;
464                     sRunChannels[v5].b = ChannelList;
465                 }
466             }
467             else
468             {
469                 sRunChannels[ChannelList].b = -1;
470             }
471             ChannelList = b;
472         }
473         ChannelList = v1;
474     } while (v1 != -1);
475 
476 #else
477     int edi = -1;
478     int esi = edi;
479 
480     while (1)
481     {
482         short nChannel = ChannelList;
483         if (nChannel < 0)
484         {
485             ChannelList = esi;
486             if (esi != -1)
487             {
488                 edi = -1;
489                 esi = edi;
490                 continue;
491             }
492             else {
493                 return;
494             }
495         }
496 
497         short b = sRunChannels[nChannel].b;
498         short d = sRunChannels[nChannel].d;
499 
500         if (d & 2)
501         {
502             sRunChannels[nChannel].d = d ^ 2;
503             runlist_SignalRun(sRunChannels[nChannel].a, ChannelList | 0x10000);
504         }
505 
506         if (d & 1)
507         {
508             sRunChannels[nChannel].d = d ^ 1;
509             runlist_SignalRun(sRunChannels[nChannel].a, 0x30000);
510         }
511 
512         if (sRunChannels[nChannel].d == 0)
513         {
514             sRunChannels[ChannelList].b = -1;
515         }
516         else
517         {
518             if (esi == -1)
519             {
520                 esi = ChannelList;
521                 edi = esi;
522             }
523             else
524             {
525                 sRunChannels[edi].b = ChannelList;
526                 edi = ChannelList;
527             }
528         }
529 
530         ChannelList = b;
531     }
532 #endif
533 }
534 
runlist_FindChannel(short ax)535 int runlist_FindChannel(short ax)
536 {
537     for (int i = 0; i < kMaxChannels; i++)
538     {
539         if (sRunChannels[i].c == -1)
540         {
541             sRunChannels[i].c = ax;
542             return i;
543         }
544     }
545 
546     return -1;
547 }
548 
runlist_AllocChannel(int a)549 int runlist_AllocChannel(int a)
550 {
551     if (a)
552     {
553         for (int i = 0; i < kMaxChannels; i++)
554         {
555             if (sRunChannels[i].c == a) {
556                 return i;
557             }
558         }
559     }
560 
561     return runlist_FindChannel(a);
562 }
563 
runlist_ExecObjects()564 void runlist_ExecObjects()
565 {
566     runlist_ProcessChannels();
567     runlist_SignalRun(RunChain, 0x20000);
568 }
569 
runlist_ProcessSectorTag(int nSector,int nLotag,int nHitag)570 void runlist_ProcessSectorTag(int nSector, int nLotag, int nHitag)
571 {
572     int zListA[8];
573     int zListB[8];
574 
575     int nChannel = runlist_AllocChannel(nHitag % 1000);
576     assert(nChannel >= 0 && nChannel < kMaxChannels);
577 
578     int keyMask = (nHitag / 1000) << 12;
579     int nSpeed = nLotag / 1000;
580 
581     if (!nSpeed) {
582         nSpeed = 1;
583     }
584 
585     nSpeed <<= 2;
586 
587     int nEffectTag = (nLotag % 1000);
588 
589     switch (nEffectTag)
590     {
591         case 1: // Ceiling Doom door
592         {
593             /*
594                 This function searches z-coordinates of neighboring sectors to find the
595                 closest (next) ceiling starting at the given z-coordinate (thez).
596             */
597             short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, -1, -1);
598             assert(nextSector > -1);
599 
600             int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].floorz, sector[nextSector].ceilingz);
601 
602             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
603 
604             int nSwPress = BuildSwPressSector(nChannel, BuildLink(1, 1), nSector, keyMask);
605 
606             runlist_AddRunRec(sRunChannels[nChannel].a, nSwPress);
607 
608             int nSwPause = BuildSwPause(nChannel, BuildLink(2, -1, 0), 60);
609 
610             runlist_AddRunRec(sRunChannels[nChannel].a, nSwPause);
611             return;
612         }
613 
614         case 2: // Floor Doom door
615         {
616             short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1);
617             assert(nextSector > -1);
618 
619             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].ceilingz, sector[nextSector].floorz);
620 
621             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
622 
623             int nSwPress = BuildSwPressSector(nChannel, BuildLink(1, 1), nSector, keyMask);
624 
625             runlist_AddRunRec(sRunChannels[nChannel].a, nSwPress);
626 
627             int nSwPause = BuildSwPause(nChannel, BuildLink(2, -1, 0), 60);
628 
629             runlist_AddRunRec(sRunChannels[nChannel].a, nSwPause);
630             return;
631         }
632 
633         case 3:
634         case 4:
635         case 19:
636         case 20:
637         case 22:
638         case 29:
639         case 30:
640         case 46:
641         case 47:
642         case 60:
643         case 65:
644         case 66:
645         case 67:
646         case 69:
647         case 72:
648         case 73:
649         case 74:
650         case 76:
651         case 77:
652         case 78:
653         case 79:
654         case 81:
655         case 82:
656         case 83:
657         case 84:
658         case 85:
659         {
660             return;
661         }
662 
663         case 5: // Permanent floor raise
664         {
665             short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz + 1, -1, -1);
666             assert(nextSector > -1);
667 
668             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].floorz, sector[nextSector].ceilingz);
669 
670             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
671             return;
672         }
673 
674         case 6: // Touchplate floor lower, single
675         {
676             short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, -1);
677             assert(nextSector > -1);
678 
679             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), 400, 400, 2, sector[nextSector].floorz, sector[nSector].floorz);
680 
681             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
682 
683             int nSwitch = BuildSwStepOn(nChannel, BuildLink(2, 1, 1), nSector);
684 
685             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
686 
687             sector[nSector].floorz = sector[nextSector].floorz;
688             return;
689         }
690 
691         case 7: // Touchplate floor lower, multiple
692         {
693             short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1);
694             assert(nextSector > -1);
695 
696             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].floorz, sector[nextSector].floorz);
697 
698             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
699 
700             int nSwitch = BuildSwStepOn(nChannel, BuildLink(1, 1), nSector);
701 
702             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
703 
704             int nSwitch2 = BuildSwNotOnPause(nChannel, BuildLink(2, -1, 0), nSector, 8);
705 
706             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch2);
707             return;
708         }
709 
710         case 8: // Permanent floor lower
711         {
712             short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1);
713             assert(nextSector > -1);
714 
715             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].floorz, sector[nextSector].floorz);
716 
717             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
718             return;
719         }
720 
721         case 9: // Switch activated lift down
722         {
723             short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1);
724             assert(nextSector > -1);
725 
726             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].floorz, sector[nextSector].floorz);
727 
728             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
729 
730             int nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 150);
731 
732             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
733             return;
734         }
735 
736         case 10: // Touchplate Floor Raise
737         {
738             short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, -1);
739             assert(nextSector > -1);
740 
741             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].floorz, sector[nextSector].floorz);
742 
743             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
744 
745             int nSwitch = BuildSwStepOn(nChannel, BuildLink(1, 1), nSector);
746 
747             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
748 
749             int nSwitch2 = BuildSwNotOnPause(nChannel, BuildLink(2, -1, 0), nSector, 8);
750 
751             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch2);
752             return;
753         }
754 
755         case 11: // Permanent floor raise
756         case 14: // Sector raise / lower
757         case 38: // Sector raise / lower
758         {
759             /*
760                 fix for original behaviour - nextSector could be -1 the and game would do an invalid memory read
761                 when getting the floorz for nextSector. Here, we assume 0 and only set the correct value if nextSector
762                 is valid.
763             */
764             int zVal = 0;
765 
766             short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, -1);
767             if (nextSector >= 0) {
768                 zVal = sector[nextSector].floorz;
769             }
770 
771             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].floorz, zVal);
772 
773             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
774             return;
775         }
776 
777         case 12: // Switch activated lift up
778         {
779             /*
780                 fix for original behaviour - nextSector could be -1 the and game would do an invalid memory read
781                 when getting the floorz for nextSector. Here, we assume 0 and only set the correct value if nextSector
782                 is valid.
783             */
784             int zVal = 0;
785 
786             short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, -1);
787             if (nextSector >= 0) {
788                 zVal = sector[nextSector].floorz;
789             }
790 
791             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].floorz, zVal);
792 
793             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
794 
795             int nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 150);
796 
797             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
798             return;
799         }
800 
801         case 13: // Bobbing floor
802         {
803             /*
804                 fix for original behaviour - nextSector could be -1 the and game would do an invalid memory read
805                 when getting the floorz for nextSector. Here, we assume 0 and only set the correct value if nextSector
806                 is valid.
807             */
808             int zVal = 0;
809 
810             short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1);
811             if (nextSector >= 0) {
812                 zVal = sector[nextSector].floorz;
813             }
814 
815             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].floorz, zVal);
816 
817             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
818 
819             int nSwitch = BuildSwReady(nChannel, BuildLink(2, 1, 0));
820 
821             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
822             return;
823         }
824 
825         case 15: // Sector raise/lower
826         {
827             short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, -1);
828             assert(nextSector > -1);
829 
830             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].floorz, sector[nextSector].floorz);
831 
832             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
833             return;
834         }
835 
836         case 16: // Stuttering noise (floor makes noise)
837         {
838             int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), 200, nSpeed * 100, 2, sector[nSector].ceilingz, sector[nSector].floorz - 8);
839 
840             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
841 
842             int nSwitch = BuildSwStepOn(nChannel, BuildLink(1, 1), nSector);
843 
844             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
845 
846             int nSwitch2 = BuildSwReady(nChannel, BuildLink(2, -1, 0));
847 
848             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch2);
849             return;
850         }
851 
852         case 17: // Reserved?
853         {
854             int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), 200, nSpeed * 100, 2, sector[nSector].ceilingz, sector[nSector].floorz - 8);
855 
856             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
857 
858             int nSwitch = BuildSwReady(nChannel, BuildLink(2, -1, 0));
859 
860             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
861             return;
862         }
863 
864         case 18: // Raises floor AND lowers ceiling
865         {
866             int ebx = ((sector[nSector].floorz - sector[nSector].ceilingz) / 2) + sector[nSector].ceilingz;
867 
868             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), 200, nSpeed * 100, 2, sector[nSector].floorz, ebx);
869 
870             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
871 
872             int ebx2 = (((sector[nSector].floorz - sector[nSector].ceilingz) / 2) + sector[nSector].ceilingz) - 8;
873 
874             int nElev2 = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), 200, nSpeed * 100, 2, sector[nSector].ceilingz, ebx2);
875 
876             runlist_AddRunRec(sRunChannels[nChannel].a, nElev2);
877 
878             int nSwitch = BuildSwStepOn(nChannel, BuildLink(1, 1), nSector);
879 
880             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
881             return;
882         }
883 
884         case 21: // Touchplate
885         {
886             int nSwitch = BuildSwStepOn(nChannel, BuildLink(2, 1, 1), nSector);
887 
888             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
889             return;
890         }
891 
892         case 23: // Floor raise, Sychronize
893         {
894             /*
895                 fix for original behaviour - nextSector could be -1 the and game would do an invalid memory read
896                 when getting the floorz for nextSector. Here, we assume 0 and only set the correct value if nextSector
897                 is valid.
898             */
899             int zVal = 0;
900 
901             short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1);
902             if (nextSector >= 0) {
903                 zVal = sector[nextSector].floorz;
904             }
905 
906             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), 32767, 200, 2, sector[nSector].floorz, zVal);
907 
908             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
909 
910             int nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), nSpeed * 60);
911 
912             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
913             return;
914         }
915 
916         case 24: // Ceiling door, channel trigger only
917         {
918             short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, -1, -1);
919             assert(nextSector > -1);
920 
921             int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].floorz, sector[nextSector].ceilingz);
922 
923             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
924 
925             int nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 60);
926 
927             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
928             return;
929         }
930 
931         case 25:
932         {
933             short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1);
934             assert(nextSector > -1);
935 
936             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].floorz, sector[nextSector].floorz);
937 
938             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
939 
940             int nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 300);
941 
942             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
943             return;
944         }
945 
946         case 26:
947         {
948             short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1);
949             assert(nextSector > -1);
950 
951             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].floorz, sector[nextSector].floorz);
952 
953             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
954 
955             int nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 450);
956 
957             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
958             return;
959         }
960 
961         case 27:
962         {
963             short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1);
964             assert(nextSector > -1);
965 
966             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].floorz, sector[nextSector].floorz);
967 
968             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
969 
970             int nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 600);
971 
972             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
973             return;
974         }
975 
976         case 28:
977         {
978             short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1);
979             assert(nextSector > -1);
980 
981             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].floorz, sector[nextSector].floorz);
982 
983             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
984 
985             int nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 900);
986 
987             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
988             return;
989         }
990 
991         case 31: // Touchplate
992         {
993             short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1);
994             assert(nextSector > -1);
995 
996             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), 0x7FFF, 0x7FFF, 2, sector[nSector].floorz, sector[nextSector].floorz);
997 
998             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
999 
1000             int nSwitch = BuildSwStepOn(nChannel, BuildLink(1, 1), nSector);
1001 
1002             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
1003             return;
1004         }
1005 
1006         case 32:
1007         {
1008             short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1);
1009             assert(nextSector > -1);
1010 
1011             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), 0x7FFF, 0x7FFF, 2, sector[nSector].floorz, sector[nextSector].floorz);
1012 
1013             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1014             return;
1015         }
1016 
1017         case 33: // Ceiling Crusher
1018         {
1019             short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, -1, -1);
1020             assert(nextSector > -1);
1021 
1022             int nElev = BuildElevC(20, nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nextSector].ceilingz, sector[nSector].floorz);
1023 
1024             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1025             return;
1026         }
1027 
1028         case 34: // Triggerable Ceiling Crusher(Inactive)
1029         {
1030             short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, -1, -1);
1031             assert(nextSector > -1);
1032 
1033             int nElev = BuildElevC(28, nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nextSector].ceilingz, sector[nSector].floorz);
1034 
1035             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1036             return;
1037         }
1038 
1039         case 35: // Destructible sector
1040         case 36:
1041         {
1042             nEnergyTowers++;
1043 
1044             int nEnergyBlock = BuildEnergyBlock(nSector);
1045 
1046             if (nLotag == 36) {
1047                 nFinaleSpr = nEnergyBlock;
1048             }
1049 
1050             return;
1051         }
1052 
1053         case 37:
1054         {
1055             short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1);
1056             assert(nextSector > -1);
1057 
1058             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].floorz, sector[nextSector].floorz);
1059 
1060             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1061             return;
1062         }
1063 
1064         case 39: // Touchplate
1065         {
1066             short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1);
1067             assert(nextSector > -1);
1068 
1069             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), 0x7FFF, 0x7FFF, 2, sector[nSector].floorz, sector[nextSector].floorz);
1070 
1071             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1072 
1073             int nSwitch = BuildSwStepOn(nChannel, BuildLink(1, 1), nSector);
1074 
1075             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
1076 
1077             int nSwitch2 = BuildSwNotOnPause(nChannel, BuildLink(2, -1, 0), nSector, 8);
1078 
1079             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch2);
1080             return;
1081         }
1082 
1083         case 40: // Moving sector(follows waypoints)
1084         {
1085             AddMovingSector(nSector, nLotag, nHitag % 1000, 2);
1086             return;
1087         }
1088 
1089         case 41: // Moving sector(follows waypoints)
1090         {
1091             AddMovingSector(nSector, nLotag, nHitag % 1000, 18);
1092             return;
1093         }
1094 
1095         case 42: // Moving sector(follows waypoints)
1096         {
1097             AddMovingSector(nSector, nLotag, nHitag % 1000, 58);
1098             return;
1099         }
1100 
1101         case 43: // Moving sector(follows waypoints)
1102         {
1103             AddMovingSector(nSector, nLotag, nHitag % 1000, 122);
1104             return;
1105         }
1106 
1107         case 44: // Moving sector(follows waypoints)
1108         {
1109             AddMovingSector(nSector, nLotag, nHitag % 1000, 90);
1110             return;
1111         }
1112 
1113         case 45: // Pushbox sector
1114         {
1115             CreatePushBlock(nSector);
1116             return;
1117         }
1118 
1119         case 48: // Ceiling lower
1120         {
1121             /*
1122                 fix for original behaviour - nextSector could be -1 the and game would do an invalid memory read
1123                 when getting the floorz for nextSector. Here, we assume 0 and only set the correct value if nextSector
1124                 is valid.
1125             */
1126             int zVal = 0;
1127 
1128             short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, -1, 1);
1129             if (nextSector >= 0) {
1130                 zVal = sector[nextSector].ceilingz;
1131             }
1132 
1133             int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), 200, nSpeed * 100, 2, sector[nSector].ceilingz, zVal);
1134 
1135             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1136             return;
1137         }
1138 
1139         case 49:
1140         {
1141             short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, -1, -1);
1142             assert(nextSector > -1);
1143 
1144             int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), 200, nSpeed * 100, 2, sector[nSector].ceilingz, sector[nextSector].ceilingz);
1145 
1146             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1147             return;
1148         }
1149 
1150         case 50: // Floor lower / raise
1151         {
1152             short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, 1, 1);
1153             assert(nextSector > -1);
1154 
1155             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), 0x7FFF, 200, 2, sector[nextSector].floorz, sector[nSector].floorz);
1156 
1157             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1158             return;
1159         }
1160 
1161         case 51:
1162         {
1163             int edx = ((sector[nSector].floorz - sector[nSector].ceilingz) / 2) + sector[nSector].ceilingz;
1164 
1165             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), 200, nSpeed * 100, 2, sector[nSector].floorz, edx);
1166 
1167             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1168 
1169             int eax = (((sector[nSector].floorz - sector[nSector].ceilingz) / 2) + sector[nSector].ceilingz) - 8;
1170 
1171             nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), 200, nSpeed * 100, 2, sector[nSector].ceilingz, eax);
1172 
1173             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1174 
1175             int nSwitch = BuildSwReady(nChannel, BuildLink(2, 1, 0));
1176 
1177             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
1178             return;
1179         }
1180 
1181         case 52:
1182         {
1183             int eax = ((sector[nSector].floorz - sector[nSector].ceilingz) / 2) + sector[nSector].ceilingz;
1184 
1185             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, eax, sector[nSector].floorz);
1186 
1187             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1188 
1189             eax = ((sector[nSector].floorz - sector[nSector].ceilingz) / 2) + sector[nSector].ceilingz;
1190 
1191             nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, eax, sector[nSector].ceilingz);
1192 
1193             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1194 
1195             int nSwitch = BuildSwPressSector(nChannel, BuildLink(1, 1), nSector, keyMask);
1196 
1197             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
1198 
1199             nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 60);
1200 
1201             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
1202             return;
1203         }
1204 
1205         case 53:
1206         {
1207             int eax = ((sector[nSector].floorz - sector[nSector].ceilingz) / 2) + sector[nSector].ceilingz;
1208 
1209             int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, eax, sector[nSector].floorz);
1210 
1211             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1212 
1213             eax = ((sector[nSector].floorz - sector[nSector].ceilingz) / 2) + sector[nSector].ceilingz;
1214 
1215             nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, eax, sector[nSector].ceilingz);
1216 
1217             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1218 
1219             int nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 150);
1220 
1221             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
1222             return;
1223         }
1224 
1225         case 54:
1226         {
1227             short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, -1, -1);
1228             assert(nextSector > -1);
1229 
1230             int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].floorz, sector[nextSector].ceilingz);
1231 
1232             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1233 
1234             int nSwitch = BuildSwPressSector(nChannel, BuildLink(1, 1), nSector, keyMask);
1235 
1236             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
1237             return;
1238         }
1239 
1240         case 55:
1241         {
1242             short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, -1, -1);
1243             assert(nextSector > -1);
1244 
1245             int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].floorz, sector[nextSector].ceilingz);
1246 
1247             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1248 
1249             int nSwitch = BuildSwPressSector(nChannel, BuildLink(1, 1), nSector, keyMask);
1250 
1251             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
1252             return;
1253         }
1254 
1255         case 56:
1256         {
1257             short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, -1, -1);
1258             assert(nextSector > -1);
1259 
1260             int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].floorz, sector[nextSector].ceilingz);
1261 
1262             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1263             return;
1264         }
1265 
1266         case 57:
1267         {
1268             short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1);
1269             assert(nextSector > -1);
1270 
1271             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].ceilingz, sector[nextSector].floorz);
1272 
1273             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1274             return;
1275         }
1276 
1277         case 58:
1278         {
1279             int nSwitch = BuildSwPressSector(nChannel, BuildLink(1, 1), nSector, keyMask);
1280 
1281             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
1282 
1283             // Fall through to case 62
1284             fallthrough__;
1285         }
1286         case 63: // Ceiling door, kill trigger (Enemy death triggers door)
1287         {
1288             if (nLotag == 63) {
1289                 nEnergyChan = nChannel;
1290             }
1291 
1292             short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, -1, -1);
1293             assert(nextSector > -1);
1294 
1295             int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].floorz, sector[nextSector].ceilingz);
1296 
1297             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1298             return;
1299         }
1300 
1301         case 59:
1302         {
1303             short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1);
1304             assert(nextSector > -1);
1305 
1306             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].floorz, sector[nextSector].floorz);
1307 
1308             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1309 
1310             int nSwitch = BuildSwStepOn(nChannel, BuildLink(1, 1), nSector);
1311 
1312             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
1313 
1314             int nSwitch2 = BuildSwNotOnPause(nChannel, BuildLink(1, 1), nSector, 60);
1315 
1316             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch2);
1317             return;
1318         }
1319 
1320         case 61:
1321         {
1322             zListB[0] = sector[nSector].floorz;
1323             int var_1C = 1;
1324 
1325             while (1)
1326             {
1327                 short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, -1);
1328                 if (nextSector < 0 || var_1C >= 8) {
1329                     break;
1330                 }
1331 
1332                 zListB[var_1C] = sector[nextSector].floorz;
1333 
1334                 var_1C++;
1335             }
1336 
1337             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, var_1C,
1338                 zListB[0], zListB[1], zListB[2], zListB[3], zListB[4], zListB[5], zListB[6], zListB[7]);
1339 
1340             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1341             return;
1342         }
1343 
1344         case 62:
1345         {
1346             zListA[0] = sector[nSector].floorz;
1347             int var_20 = 1;
1348 
1349             while (1)
1350             {
1351                 short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1);
1352                 if (nextSector < 0 || var_20 >= 8) {
1353                     break;
1354                 }
1355 
1356                 zListA[var_20] = sector[nextSector].floorz;
1357 
1358                 var_20++;
1359             }
1360 
1361             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, var_20,
1362                 zListA[0], zListA[1], zListA[2], zListA[3], zListA[4], zListA[5], zListA[6], zListA[7]);
1363 
1364             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1365             return;
1366         }
1367 
1368         case 64:
1369         {
1370             int nSwitch = BuildSwStepOn(nChannel, BuildLink(2, 0, 0), nSector);
1371             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
1372             return;
1373         }
1374 
1375         case 68:
1376         {
1377             short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1);
1378             assert(nextSector > -1);
1379 
1380             int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, sector[nSector].floorz, sector[nextSector].floorz);
1381 
1382             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1383             return;
1384         }
1385 
1386         case 70:
1387         case 71:
1388         {
1389             short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, -1, -1);
1390             assert(nextSector > -1);
1391 
1392             int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, (int)sector[nSector].floorz, (int)sector[nextSector].ceilingz);
1393 
1394             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1395 
1396             int nSwitch = BuildSwPressSector(nChannel, BuildLink(1, 1), nSector, keyMask);
1397 
1398             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
1399 
1400             int nSwitch2 = BuildSwPause(nChannel, BuildLink(2, -1, 0), 60);
1401 
1402             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch2);
1403             return;
1404         }
1405 
1406         case 75:
1407         {
1408             int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), nSpeed * 100, nSpeed * 100, 2, (int)sector[nSector].ceilingz, (int)sector[nSector].floorz);
1409 
1410             runlist_AddRunRec(sRunChannels[nChannel].a, nElev);
1411             return;
1412         }
1413 
1414         case 80:
1415         {
1416             SectFlag[nSector] |= 0x8000;
1417             return;
1418         }
1419     }
1420 }
1421 
runlist_ProcessWallTag(int nWall,short nLotag,short nHitag)1422 void runlist_ProcessWallTag(int nWall, short nLotag, short nHitag)
1423 {
1424     int nChannel = runlist_AllocChannel(nHitag % 1000);
1425     assert(nChannel >= 0 && nChannel < kMaxChannels);
1426 
1427     int nPanSpeed = nLotag / 1000;
1428     if (!nPanSpeed) {
1429         nPanSpeed = 1;
1430     }
1431 
1432     nPanSpeed <<= 2;
1433 
1434     int nEffectTag = nLotag % 1000;
1435 
1436     switch (nEffectTag)
1437     {
1438         default:
1439             return;
1440 
1441         case 1:
1442         {
1443             int nWallFace = BuildWallFace(nChannel, nWall, 2, wall[nWall].picnum, wall[nWall].picnum + 1);
1444             runlist_AddRunRec(sRunChannels[nChannel].a, nWallFace);
1445 
1446             int nSwitch = BuildSwPressWall(nChannel, BuildLink(2, nEffectTag, 0), nWall);
1447 
1448             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
1449             return;
1450         }
1451 
1452         case 6:
1453         {
1454             int nSwitch = BuildSwPressWall(nChannel, BuildLink(2, 1, 0), nWall);
1455             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
1456             return;
1457         }
1458 
1459         case 7: // Regular switch
1460         {
1461             int nWallFace = BuildWallFace(nChannel, nWall, 2, wall[nWall].picnum, wall[nWall].picnum + 1);
1462             runlist_AddRunRec(sRunChannels[nChannel].a, nWallFace);
1463 
1464             int nSwitch = BuildSwPressWall(nChannel, BuildLink(1, 1), nWall);
1465             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
1466             return;
1467         }
1468 
1469         case 8: // Reverse switch
1470         {
1471             int nWallFace = BuildWallFace(nChannel, nWall, 2, wall[nWall].picnum, wall[nWall].picnum + 1);
1472             runlist_AddRunRec(sRunChannels[nChannel].a, nWallFace);
1473 
1474             int nSwitch = BuildSwPressWall(nChannel, BuildLink(2, -1, 0), nWall);
1475 
1476             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
1477             return;
1478         }
1479 
1480         case 9: // Invisible switch
1481         {
1482             int nSwitch = BuildSwPressWall(nChannel, BuildLink(2, 1, 1), nWall);
1483             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
1484             return;
1485         }
1486 
1487         case 10:
1488         {
1489             int nSwitch = BuildSwPressWall(nChannel, BuildLink(2, -1, 0), nWall);
1490             runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch);
1491             return;
1492         }
1493 
1494         case 12: // Twin star trek door
1495         case 14:
1496         case 16:
1497         case 19:
1498         case 20:
1499         {
1500             int nLastWall = 0;
1501             int n2ndLastWall = 0;
1502 
1503             short nStart = nWall;
1504 
1505             while (1)
1506             {
1507                 nWall = wall[nWall].point2; // get the next (right side) wall point
1508 
1509                 if (nStart == nWall) { // we've looped back around
1510                     break;
1511                 }
1512 
1513                 n2ndLastWall = nLastWall;
1514                 nLastWall = nWall;
1515             }
1516 
1517             short nWall2 = wall[nStart].point2;
1518             short nWall3 = wall[nWall2].point2;
1519             short nWall4 = wall[nWall3].point2;
1520 
1521             int nSlide = BuildSlide(nChannel, nStart, nLastWall, n2ndLastWall, nWall2, nWall3, nWall4);
1522 
1523             runlist_AddRunRec(sRunChannels[nChannel].a, nSlide);
1524             return;
1525         }
1526 
1527         case 24: // Waterfall
1528         {
1529             AddFlow(nWall, nPanSpeed, 3);
1530             return;
1531         }
1532 
1533         case 25: // Inverse waterfall
1534         {
1535             AddFlow(nWall, nPanSpeed, 2);
1536             return;
1537         }
1538     }
1539 }
1540 
runlist_CheckRadialDamage(short nSprite)1541 int runlist_CheckRadialDamage(short nSprite)
1542 {
1543     if (nSprite == nRadialSpr) {
1544         return 0;
1545     }
1546 
1547     if (!(sprite[nSprite].cstat & 0x101)) {
1548         return 0;
1549     }
1550 
1551     if (sprite[nSprite].statnum >= kMaxStatus || sprite[nRadialSpr].statnum >= kMaxStatus) {
1552         return 0;
1553     }
1554 
1555     if (sprite[nSprite].statnum != 100 && nSprite == nRadialOwner) {
1556         return 0;
1557     }
1558 
1559     int x = (sprite[nSprite].x - sprite[nRadialSpr].x) >> 8;
1560     int y = (sprite[nSprite].y - sprite[nRadialSpr].y) >> 8;
1561     int z = (sprite[nSprite].z - sprite[nRadialSpr].z) >> 12;
1562 
1563     if (klabs(x) > nDamageRadius) {
1564         return 0;
1565     }
1566 
1567     if (klabs(y) > nDamageRadius) {
1568         return 0;
1569     }
1570 
1571     if (klabs(z) > nDamageRadius) {
1572         return 0;
1573     }
1574 
1575     int edi = 0;
1576 
1577     uint32_t xDiff = klabs(x);
1578     uint32_t yDiff = klabs(y);
1579 
1580     uint32_t sqrtNum = xDiff * xDiff + yDiff * yDiff;
1581 
1582     if (sqrtNum > INT_MAX)
1583     {
1584         OSD_Printf("%s %d: overflow\n", EDUKE32_FUNCTION, __LINE__);
1585         sqrtNum = INT_MAX;
1586     }
1587 
1588     int nDist = ksqrt(sqrtNum);
1589 
1590     if (nDist < nDamageRadius)
1591     {
1592         uint16_t nCStat = sprite[nSprite].cstat;
1593         sprite[nSprite].cstat = 0x101;
1594 
1595         if (((kStatExplodeTarget - sprite[nSprite].statnum) <= 1) ||
1596             cansee(sprite[nRadialSpr].x,
1597                 sprite[nRadialSpr].y,
1598                 sprite[nRadialSpr].z - 512,
1599                 sprite[nRadialSpr].sectnum,
1600                 sprite[nSprite].x,
1601                 sprite[nSprite].y,
1602                 sprite[nSprite].z - 8192,
1603                 sprite[nSprite].sectnum))
1604         {
1605             edi = (nRadialDamage * (nDamageRadius - nDist)) / nDamageRadius;
1606 
1607             if (edi < 0) {
1608                 edi = 0;
1609             }
1610             else if (edi > 20)
1611             {
1612                 int nAngle = GetMyAngle(x, y);
1613 
1614                 sprite[nSprite].xvel += (edi * Cos(nAngle)) >> 3;
1615                 sprite[nSprite].yvel += (edi * Sin(nAngle)) >> 3;
1616                 sprite[nSprite].zvel -= edi * 24;
1617 
1618                 if (sprite[nSprite].zvel < -3584) {
1619                     sprite[nSprite].zvel = -3584;
1620                 }
1621             }
1622         }
1623 
1624         sprite[nSprite].cstat = nCStat;
1625     }
1626 
1627     if (edi > 0x7FFF) {
1628         edi = 0x7FFF;
1629     }
1630 
1631     return edi;
1632 }
1633 
runlist_RadialDamageEnemy(short nSprite,short nDamage,short nRadius)1634 void runlist_RadialDamageEnemy(short nSprite, short nDamage, short nRadius)
1635 {
1636     if (!nRadius) {
1637         return;
1638     }
1639 
1640     if (nRadialSpr == -1)
1641     {
1642         nRadialDamage = nDamage * 4;
1643         nDamageRadius = nRadius;
1644         nRadialSpr = nSprite;
1645         nRadialOwner = sprite[nSprite].owner;
1646 
1647         runlist_ExplodeSignalRun();
1648 
1649         nRadialSpr = -1;
1650     }
1651 }
1652 
runlist_DamageEnemy(int nSprite,int nSprite2,short nDamage)1653 void runlist_DamageEnemy(int nSprite, int nSprite2, short nDamage)
1654 {
1655     if (sprite[nSprite].statnum >= kMaxStatus) {
1656         return;
1657     }
1658 
1659     short nRun = sprite[nSprite].owner;
1660     if (nRun <= -1) {
1661         return;
1662     }
1663 
1664     short nPreCreaturesLeft = nCreaturesLeft;
1665 
1666     runlist_SendMessageToRunRec(nRun, (nSprite2 & 0xFFFF) | 0x80000, nDamage * 4);
1667 
1668     // is there now one less creature? (has one died)
1669     if (nPreCreaturesLeft > nCreaturesLeft && nSprite2 > -1)
1670     {
1671         if (sprite[nSprite2].statnum != 100) {
1672             return;
1673         }
1674 
1675         short nPlayer = GetPlayerFromSprite(nSprite2);
1676         nTauntTimer[nPlayer]--;
1677 
1678         if (nTauntTimer[nPlayer] <= 0)
1679         {
1680             // Do a taunt
1681             int nPlayerSprite = PlayerList[nPlayer].nSprite;
1682             int nSector = sprite[nPlayerSprite].sectnum;
1683 
1684             if (!(SectFlag[nSector] & kSectUnderwater))
1685             {
1686                 int ebx = 0x4000;
1687 
1688                 if (nPlayer == nLocalPlayer) {
1689                     ebx = 0x6000;
1690                 }
1691 
1692                 int nDopSprite = nDoppleSprite[nPlayer];
1693                 D3PlayFX(StaticSound[kSoundTauntStart + (RandomSize(3) % 5)], nDopSprite | ebx);
1694             }
1695 
1696             nTauntTimer[nPlayer] = RandomSize(3) + 3;
1697         }
1698     }
1699 }
1700 
1701 class RunListLoadSave : public LoadSave
1702 {
1703 public:
1704     virtual void Load();
1705     virtual void Save();
1706 };
1707 
Load()1708 void RunListLoadSave::Load()
1709 {
1710     Read(&RunCount, sizeof(RunCount));
1711     Read(&nRadialSpr, sizeof(nRadialSpr));
1712     Read(&nStackCount, sizeof(nStackCount));
1713     Read(&word_966BE, sizeof(word_966BE));
1714     Read(&ChannelList, sizeof(ChannelList));
1715     Read(&ChannelLast, sizeof(ChannelLast));
1716     Read(&nRadialOwner, sizeof(nRadialOwner));
1717     Read(&nDamageRadius, sizeof(nDamageRadius));
1718     Read(&nRadialDamage, sizeof(nRadialDamage));
1719     Read(&RunChain, sizeof(RunChain));
1720     Read(&NewRun, sizeof(NewRun));
1721 
1722     Read(sRunStack, sizeof(sRunStack[0]) * nStackCount);
1723     Read(RunFree, sizeof(RunFree));
1724     Read(sRunChannels, sizeof(sRunChannels));
1725     Read(RunData, sizeof(RunData));
1726 }
1727 
Save()1728 void RunListLoadSave::Save()
1729 {
1730     Write(&RunCount, sizeof(RunCount));
1731     Write(&nRadialSpr, sizeof(nRadialSpr));
1732     Write(&nStackCount, sizeof(nStackCount));
1733     Write(&word_966BE, sizeof(word_966BE));
1734     Write(&ChannelList, sizeof(ChannelList));
1735     Write(&ChannelLast, sizeof(ChannelLast));
1736     Write(&nRadialOwner, sizeof(nRadialOwner));
1737     Write(&nDamageRadius, sizeof(nDamageRadius));
1738     Write(&nRadialDamage, sizeof(nRadialDamage));
1739     Write(&RunChain, sizeof(RunChain));
1740     Write(&NewRun, sizeof(NewRun));
1741 
1742     Write(sRunStack, sizeof(sRunStack[0]) * nStackCount);
1743     Write(RunFree, sizeof(RunFree));
1744     Write(sRunChannels, sizeof(sRunChannels));
1745     Write(RunData, sizeof(RunData));
1746 }
1747 
1748 static RunListLoadSave* myLoadSave;
1749 
RunListLoadSaveConstruct()1750 void RunListLoadSaveConstruct()
1751 {
1752     myLoadSave = new RunListLoadSave();
1753 }
1754