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