1 //-------------------------------------------------------------------------
2 /*
3 Copyright (C) 2010-2019 EDuke32 developers and contributors
4 Copyright (C) 2019 Nuke.YKT
5 
6 This file is part of NBlood.
7 
8 NBlood 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 #include <random>
24 #include <iostream>
25 
26 #include "build.h"
27 #include "compat.h"
28 #include "mmulti.h"
29 #include "common_game.h"
30 
31 #include "ai.h"
32 #include "actor.h"
33 #include "blood.h"
34 #include "db.h"
35 #include "endgame.h"
36 #include "eventq.h"
37 #ifdef NOONE_EXTENSIONS
38 #include "aiunicult.h"
39 #endif
40 #include "fx.h"
41 #include "gameutil.h"
42 #include "gib.h"
43 #include "globals.h"
44 #include "levels.h"
45 #include "loadsave.h"
46 #include "player.h"
47 #include "seq.h"
48 #include "qav.h"
49 #include "sfx.h"
50 #include "sound.h"
51 #include "triggers.h"
52 #include "trig.h"
53 #include "view.h"
54 #include "messages.h"
55 #include "weapon.h"
56 #ifdef NOONE_EXTENSIONS
57 #include "nnexts.h"
58 #endif
59 
60 int basePath[kMaxSectors];
61 
62 void FireballTrapSeqCallback(int, int);
63 void MGunFireSeqCallback(int, int);
64 void MGunOpenSeqCallback(int, int);
65 
66 int nFireballTrapClient = seqRegisterClient(FireballTrapSeqCallback);
67 int nMGunFireClient = seqRegisterClient(MGunFireSeqCallback);
68 int nMGunOpenClient = seqRegisterClient(MGunOpenSeqCallback);
69 
70 
71 
GetWaveValue(unsigned int nPhase,int nType)72 unsigned int GetWaveValue(unsigned int nPhase, int nType)
73 {
74     switch (nType)
75     {
76     case 0:
77         return 0x8000-(Cos((nPhase<<10)>>16)>>15);
78     case 1:
79         return nPhase;
80     case 2:
81         return 0x10000-(Cos((nPhase<<9)>>16)>>14);
82     case 3:
83         return Sin((nPhase<<9)>>16)>>14;
84     }
85     return nPhase;
86 }
87 
SetSpriteState(int nSprite,XSPRITE * pXSprite,int nState)88 char SetSpriteState(int nSprite, XSPRITE* pXSprite, int nState)
89 {
90     if ((pXSprite->busy & 0xffff) == 0 && pXSprite->state == nState)
91         return 0;
92     pXSprite->busy = nState << 16;
93     pXSprite->state = nState;
94     evKill(nSprite, 3);
95     if ((sprite[nSprite].flags & kHitagRespawn) != 0 && sprite[nSprite].inittype >= kDudeBase && sprite[nSprite].inittype < kDudeMax)
96     {
97         pXSprite->respawnPending = 3;
98         evPost(nSprite, 3, gGameOptions.nMonsterRespawnTime, kCallbackRespawn);
99         return 1;
100     }
101     if (pXSprite->restState != nState && pXSprite->waitTime > 0)
102         evPost(nSprite, 3, (pXSprite->waitTime * 120) / 10, pXSprite->restState ? kCmdOn : kCmdOff);
103     if (pXSprite->txID)
104     {
105         if (pXSprite->command != kCmdLink && pXSprite->triggerOn && pXSprite->state)
106             evSend(nSprite, 3, pXSprite->txID, (COMMAND_ID)pXSprite->command);
107         if (pXSprite->command != kCmdLink && pXSprite->triggerOff && !pXSprite->state)
108             evSend(nSprite, 3, pXSprite->txID, (COMMAND_ID)pXSprite->command);
109     }
110     return 1;
111 }
112 
113 
114 
SetWallState(int nWall,XWALL * pXWall,int nState)115 char SetWallState(int nWall, XWALL *pXWall, int nState)
116 {
117     if ((pXWall->busy&0xffff) == 0 && pXWall->state == nState)
118         return 0;
119     pXWall->busy = nState<<16;
120     pXWall->state = nState;
121     evKill(nWall, 0);
122     if (pXWall->restState != nState && pXWall->waitTime > 0)
123         evPost(nWall, 0, (pXWall->waitTime*120) / 10, pXWall->restState ? kCmdOn : kCmdOff);
124     if (pXWall->txID)
125     {
126         if (pXWall->command != kCmdLink && pXWall->triggerOn && pXWall->state)
127             evSend(nWall, 0, pXWall->txID, (COMMAND_ID)pXWall->command);
128         if (pXWall->command != kCmdLink && pXWall->triggerOff && !pXWall->state)
129             evSend(nWall, 0, pXWall->txID, (COMMAND_ID)pXWall->command);
130     }
131     return 1;
132 }
133 
SetSectorState(int nSector,XSECTOR * pXSector,int nState)134 char SetSectorState(int nSector, XSECTOR *pXSector, int nState)
135 {
136     if ((pXSector->busy&0xffff) == 0 && pXSector->state == nState)
137         return 0;
138     pXSector->busy = nState<<16;
139     pXSector->state = nState;
140     evKill(nSector, 6);
141     if (nState == 1)
142     {
143         if (pXSector->command != kCmdLink && pXSector->triggerOn && pXSector->txID)
144             evSend(nSector, 6, pXSector->txID, (COMMAND_ID)pXSector->command);
145         if (pXSector->stopOn)
146         {
147             pXSector->stopOn = 0;
148             pXSector->stopOff = 0;
149         }
150         else if (pXSector->reTriggerA)
151             evPost(nSector, 6, (pXSector->waitTimeA * 120) / 10, kCmdOff);
152     }
153     else
154     {
155         if (pXSector->command != kCmdLink && pXSector->triggerOff && pXSector->txID)
156             evSend(nSector, 6, pXSector->txID, (COMMAND_ID)pXSector->command);
157         if (pXSector->stopOff)
158         {
159             pXSector->stopOn = 0;
160             pXSector->stopOff = 0;
161         }
162         else if (pXSector->reTriggerB)
163             evPost(nSector, 6, (pXSector->waitTimeB * 120) / 10, kCmdOn);
164     }
165     return 1;
166 }
167 
168 int gBusyCount = 0;
169 BUSY gBusy[];
170 
AddBusy(int a1,BUSYID a2,int nDelta)171 void AddBusy(int a1, BUSYID a2, int nDelta)
172 {
173     dassert(nDelta != 0);
174     int i;
175     for (i = 0; i < gBusyCount; i++)
176     {
177         if (gBusy[i].at0 == a1 && gBusy[i].atc == a2)
178             break;
179     }
180     if (i == gBusyCount)
181     {
182         if (gBusyCount == kMaxBusyCount)
183             return;
184         gBusy[i].at0 = a1;
185         gBusy[i].atc = a2;
186         gBusy[i].at8 = nDelta > 0 ? 0 : 65536;
187         gBusyCount++;
188     }
189     gBusy[i].at4 = nDelta;
190 }
191 
ReverseBusy(int a1,BUSYID a2)192 void ReverseBusy(int a1, BUSYID a2)
193 {
194     int i;
195     for (i = 0; i < gBusyCount; i++)
196     {
197         if (gBusy[i].at0 == a1 && gBusy[i].atc == a2)
198         {
199             gBusy[i].at4 = -gBusy[i].at4;
200             break;
201         }
202     }
203 }
204 
GetSourceBusy(EVENT a1)205 unsigned int GetSourceBusy(EVENT a1)
206 {
207     int nIndex = a1.index;
208     switch (a1.type)
209     {
210     case 6:
211     {
212         int nXIndex = sector[nIndex].extra;
213         dassert(nXIndex > 0 && nXIndex < kMaxXSectors);
214         return xsector[nXIndex].busy;
215     }
216     case 0:
217     {
218         int nXIndex = wall[nIndex].extra;
219         dassert(nXIndex > 0 && nXIndex < kMaxXWalls);
220         return xwall[nXIndex].busy;
221     }
222     case 3:
223     {
224         int nXIndex = sprite[nIndex].extra;
225         dassert(nXIndex > 0 && nXIndex < kMaxXSprites);
226         return xsprite[nXIndex].busy;
227     }
228     }
229     return 0;
230 }
231 
LifeLeechOperate(spritetype * pSprite,XSPRITE * pXSprite,EVENT event)232 void LifeLeechOperate(spritetype *pSprite, XSPRITE *pXSprite, EVENT event)
233 {
234     switch (event.cmd) {
235     case kCmdSpritePush:
236     {
237         int nPlayer = pXSprite->data4;
238         if (nPlayer >= 0 && nPlayer < gNetPlayers)
239         {
240             PLAYER *pPlayer = &gPlayer[nPlayer];
241             if (pPlayer->pXSprite->health > 0)
242             {
243                 pPlayer->ammoCount[8] = ClipHigh(pPlayer->ammoCount[8]+pXSprite->data3, gAmmoInfo[8].max);
244                 pPlayer->hasWeapon[9] = 1;
245                 if (pPlayer->curWeapon != 9)
246                 {
247                     pPlayer->weaponState = 0;
248                     pPlayer->nextWeapon = 9;
249                 }
250                 evKill(pSprite->index, 3);
251             }
252         }
253         break;
254     }
255     case kCmdSpriteProximity:
256     {
257         int nTarget = pXSprite->target;
258         if (nTarget >= 0 && nTarget < kMaxSprites)
259         {
260             if (!pXSprite->stateTimer)
261             {
262                 spritetype *pTarget = &sprite[nTarget];
263                 if (pTarget->statnum == kStatDude && !(pTarget->flags&32) && pTarget->extra > 0 && pTarget->extra < kMaxXSprites)
264                 {
265                     int top, bottom;
266                     GetSpriteExtents(pSprite, &top, &bottom);
267                     int nType = pTarget->type-kDudeBase;
268                     DUDEINFO *pDudeInfo = getDudeInfo(nType+kDudeBase);
269                     int z1 = (top-pSprite->z)-256;
270                     int x = pTarget->x;
271                     int y = pTarget->y;
272                     int z = pTarget->z;
273                     int nDist = approxDist(x - pSprite->x, y - pSprite->y);
274                     if (nDist != 0 && cansee(pSprite->x, pSprite->y, top, pSprite->sectnum, x, y, z, pTarget->sectnum))
275                     {
276                         int t = divscale(nDist, 0x1aaaaa, 12);
277                         x += (xvel[nTarget]*t)>>12;
278                         y += (yvel[nTarget]*t)>>12;
279                         int angBak = pSprite->ang;
280                         pSprite->ang = getangle(x-pSprite->x, y-pSprite->y);
281                         int dx = Cos(pSprite->ang)>>16;
282                         int dy = Sin(pSprite->ang)>>16;
283                         int tz = pTarget->z - (pTarget->yrepeat * pDudeInfo->aimHeight) * 4;
284                         int dz = divscale(tz - top - 256, nDist, 10);
285                         int nMissileType = kMissileLifeLeechAltNormal + (pXSprite->data3 ? 1 : 0);
286                         int t2;
287                         if (!pXSprite->data3)
288                             t2 = 120 / 10.0;
289                         else
290                             t2 = (3*120) / 10.0;
291                         spritetype *pMissile = actFireMissile(pSprite, 0, z1, dx, dy, dz, nMissileType);
292                         if (pMissile)
293                         {
294                             pMissile->owner = pSprite->owner;
295                             pXSprite->stateTimer = 1;
296                             evPost(pSprite->index, 3, t2, kCallbackLeechStateTimer);
297                             pXSprite->data3 = ClipLow(pXSprite->data3-1, 0);
298                         }
299                         pSprite->ang = angBak;
300                     }
301                 }
302             }
303         }
304         return;
305     }
306     }
307     actPostSprite(pSprite->index, kStatFree);
308 }
309 
310 void ActivateGenerator(int);
311 
OperateSprite(int nSprite,XSPRITE * pXSprite,EVENT event)312 void OperateSprite(int nSprite, XSPRITE *pXSprite, EVENT event)
313 {
314     spritetype *pSprite = &sprite[nSprite];
315 
316     #ifdef NOONE_EXTENSIONS
317         if (gModernMap && modernTypeOperateSprite(nSprite, pSprite, pXSprite, event))
318             return;
319     #endif
320 
321     switch (event.cmd) {
322         case kCmdLock:
323             pXSprite->locked = 1;
324             return;
325         case kCmdUnlock:
326             pXSprite->locked = 0;
327             return;
328         case kCmdToggleLock:
329             pXSprite->locked = pXSprite->locked ^ 1;
330             return;
331     }
332 
333     if (pSprite->statnum == kStatDude && pSprite->type >= kDudeBase && pSprite->type < kDudeMax) {
334 
335         switch (event.cmd) {
336             case kCmdOff:
337                 SetSpriteState(nSprite, pXSprite, 0);
338                 break;
339             case kCmdSpriteProximity:
340                 if (pXSprite->state) break;
341                 fallthrough__;
342             case kCmdOn:
343             case kCmdSpritePush:
344             case kCmdSpriteTouch:
345                 if (!pXSprite->state) SetSpriteState(nSprite, pXSprite, 1);
346                 aiActivateDude(pSprite, pXSprite);
347                 break;
348         }
349 
350         return;
351     }
352 
353 
354     switch (pSprite->type) {
355     case kTrapMachinegun:
356         if (pXSprite->health <= 0) break;
357         switch (event.cmd) {
358             case kCmdOff:
359                 if (!SetSpriteState(nSprite, pXSprite, 0)) break;
360                 seqSpawn(40, 3, pSprite->extra, -1);
361                 break;
362             case kCmdOn:
363                 if (!SetSpriteState(nSprite, pXSprite, 1)) break;
364                 seqSpawn(38, 3, pSprite->extra, nMGunOpenClient);
365                 if (pXSprite->data1 > 0)
366                     pXSprite->data2 = pXSprite->data1;
367                 break;
368         }
369         break;
370     case kThingFallingRock:
371         if (SetSpriteState(nSprite, pXSprite, 1))
372             pSprite->flags |= 7;
373         break;
374     case kThingWallCrack:
375         if (SetSpriteState(nSprite, pXSprite, 0))
376             actPostSprite(nSprite, kStatFree);
377         break;
378     case kThingCrateFace:
379         if (SetSpriteState(nSprite, pXSprite, 0))
380             actPostSprite(nSprite, kStatFree);
381         break;
382     case kTrapZapSwitchable:
383         switch (event.cmd) {
384             case kCmdOff:
385                 pXSprite->state = 0;
386                 pSprite->cstat |= CSTAT_SPRITE_INVISIBLE;
387                 pSprite->cstat &= ~CSTAT_SPRITE_BLOCK;
388                 break;
389             case kCmdOn:
390                 pXSprite->state = 1;
391                 pSprite->cstat &= (unsigned short)~CSTAT_SPRITE_INVISIBLE;
392                 pSprite->cstat |= CSTAT_SPRITE_BLOCK;
393                 break;
394             case kCmdToggle:
395                 pXSprite->state ^= 1;
396                 pSprite->cstat ^= CSTAT_SPRITE_INVISIBLE;
397                 pSprite->cstat ^= CSTAT_SPRITE_BLOCK;
398                 break;
399         }
400         break;
401     case kTrapFlame:
402         switch (event.cmd) {
403             case kCmdOff:
404                 if (!SetSpriteState(nSprite, pXSprite, 0)) break;
405                 seqSpawn(40, 3, pSprite->extra, -1);
406                 sfxKill3DSound(pSprite, 0, -1);
407                 break;
408             case kCmdOn:
409                 if (!SetSpriteState(nSprite, pXSprite, 1)) break;
410                 seqSpawn(38, 3, pSprite->extra, -1);
411                 sfxPlay3DSound(pSprite, 441, 0, 0);
412                 break;
413         }
414         break;
415     case kSwitchPadlock:
416         switch (event.cmd) {
417             case kCmdOff:
418                 SetSpriteState(nSprite, pXSprite, 0);
419                 break;
420             case kCmdOn:
421                 if (!SetSpriteState(nSprite, pXSprite, 1)) break;
422                 seqSpawn(37, 3, pSprite->extra, -1);
423                 break;
424             default:
425                 SetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1);
426                 if (pXSprite->state) seqSpawn(37, 3, pSprite->extra, -1);
427                 break;
428         }
429         break;
430     case kSwitchToggle:
431         switch (event.cmd) {
432             case kCmdOff:
433                 if (!SetSpriteState(nSprite, pXSprite, 0)) break;
434                 sfxPlay3DSound(pSprite, pXSprite->data2, 0, 0);
435                 break;
436             case kCmdOn:
437                 if (!SetSpriteState(nSprite, pXSprite, 1)) break;
438                 sfxPlay3DSound(pSprite, pXSprite->data1, 0, 0);
439                 break;
440             default:
441                 if (!SetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1)) break;
442                 if (pXSprite->state) sfxPlay3DSound(pSprite, pXSprite->data1, 0, 0);
443                 else sfxPlay3DSound(pSprite, pXSprite->data2, 0, 0);
444                 break;
445         }
446         break;
447     case kSwitchOneWay:
448         switch (event.cmd) {
449             case kCmdOff:
450                 if (!SetSpriteState(nSprite, pXSprite, 0)) break;
451                 sfxPlay3DSound(pSprite, pXSprite->data2, 0, 0);
452                 break;
453             case kCmdOn:
454                 if (!SetSpriteState(nSprite, pXSprite, 1)) break;
455                 sfxPlay3DSound(pSprite, pXSprite->data1, 0, 0);
456                 break;
457             default:
458                 if (!SetSpriteState(nSprite, pXSprite, pXSprite->restState ^ 1)) break;
459                 if (pXSprite->state) sfxPlay3DSound(pSprite, pXSprite->data1, 0, 0);
460                 else sfxPlay3DSound(pSprite, pXSprite->data2, 0, 0);
461                 break;
462         }
463         break;
464     case kSwitchCombo:
465         switch (event.cmd) {
466             case kCmdOff:
467                 pXSprite->data1--;
468                 if (pXSprite->data1 < 0)
469                     pXSprite->data1 += pXSprite->data3;
470                 break;
471             default:
472                 pXSprite->data1++;
473                 if (pXSprite->data1 >= pXSprite->data3)
474                     pXSprite->data1 -= pXSprite->data3;
475                 break;
476         }
477 
478         sfxPlay3DSound(pSprite, pXSprite->data4, -1, 0);
479 
480         if (pXSprite->command == kCmdLink && pXSprite->txID > 0)
481             evSend(nSprite, 3, pXSprite->txID, kCmdLink);
482 
483         if (pXSprite->data1 == pXSprite->data2)
484             SetSpriteState(nSprite, pXSprite, 1);
485         else
486             SetSpriteState(nSprite, pXSprite, 0);
487 
488         break;
489     case kMarkerDudeSpawn:
490         if (gGameOptions.nMonsterSettings && pXSprite->data1 >= kDudeBase && pXSprite->data1 < kDudeMax) {
491             spritetype* pSpawn = actSpawnDude(pSprite, pXSprite->data1, -1, 0);
492             if (pSpawn) {
493                 XSPRITE *pXSpawn = &xsprite[pSpawn->extra];
494                 gKillMgr.sub_263E0(1);
495                 switch (pXSprite->data1) {
496                     case kDudeBurningInnocent:
497                     case kDudeBurningCultist:
498                     case kDudeBurningZombieButcher:
499                     case kDudeBurningTinyCaleb:
500                     case kDudeBurningBeast: {
501                         pXSpawn->health = getDudeInfo(pXSprite->data1)->startHealth << 4;
502                         pXSpawn->burnTime = 10;
503                         pXSpawn->target = -1;
504                         aiActivateDude(pSpawn, pXSpawn);
505                         break;
506                     default:
507                         break;
508                     }
509                 }
510             }
511         }
512         break;
513     case kMarkerEarthQuake:
514         pXSprite->triggerOn = 0;
515         pXSprite->isTriggered = 1;
516         SetSpriteState(nSprite, pXSprite, 1);
517         for (int p = connecthead; p >= 0; p = connectpoint2[p]) {
518             spritetype *pPlayerSprite = gPlayer[p].pSprite;
519             int dx = (pSprite->x - pPlayerSprite->x)>>4;
520             int dy = (pSprite->y - pPlayerSprite->y)>>4;
521             int dz = (pSprite->z - pPlayerSprite->z)>>8;
522             int nDist = dx*dx+dy*dy+dz*dz+0x40000;
523             gPlayer[p].quakeEffect = divscale16(pXSprite->data1, nDist);
524         }
525         break;
526     case kThingTNTBarrel:
527         if (pSprite->flags & kHitagRespawn) return;
528         fallthrough__;
529     case kThingArmedTNTStick:
530     case kThingArmedTNTBundle:
531     case kThingArmedSpray:
532         actExplodeSprite(pSprite);
533         break;
534     case kTrapExploder:
535         switch (event.cmd) {
536             case kCmdOn:
537                 SetSpriteState(nSprite, pXSprite, 1);
538                 break;
539             default:
540                 pSprite->cstat &= (unsigned short)~CSTAT_SPRITE_INVISIBLE;
541                 actExplodeSprite(pSprite);
542                 break;
543         }
544         break;
545     case kThingArmedRemoteBomb:
546         if (pSprite->statnum != kStatRespawn) {
547             if (event.cmd != kCmdOn) actExplodeSprite(pSprite);
548             else {
549                 sfxPlay3DSound(pSprite, 454, 0, 0);
550                 evPost(nSprite, 3, 18, kCmdOff);
551             }
552         }
553         break;
554     case kThingArmedProxBomb:
555         if (pSprite->statnum != kStatRespawn) {
556             switch (event.cmd) {
557                 case kCmdSpriteProximity:
558                     if (pXSprite->state) break;
559                     sfxPlay3DSound(pSprite, 452, 0, 0);
560                     evPost(nSprite, 3, 30, kCmdOff);
561                     pXSprite->state = 1;
562                     fallthrough__;
563                 case kCmdOn:
564                     sfxPlay3DSound(pSprite, 451, 0, 0);
565                     pXSprite->Proximity = 1;
566                     break;
567                 default:
568                     actExplodeSprite(pSprite);
569                     break;
570             }
571         }
572         break;
573     case kThingDroppedLifeLeech:
574         LifeLeechOperate(pSprite, pXSprite, event);
575         break;
576     case kGenTrigger:
577     case kGenDripWater:
578     case kGenDripBlood:
579     case kGenMissileFireball:
580     case kGenMissileEctoSkull:
581     case kGenDart:
582     case kGenBubble:
583     case kGenBubbleMulti:
584     case kGenSound:
585         switch (event.cmd) {
586             case kCmdOff:
587                 SetSpriteState(nSprite, pXSprite, 0);
588                 break;
589             case kCmdRepeat:
590                 if (pSprite->type != kGenTrigger) ActivateGenerator(nSprite);
591                 if (pXSprite->txID) evSend(nSprite, 3, pXSprite->txID, (COMMAND_ID)pXSprite->command);
592                 if (pXSprite->busyTime > 0) {
593                     int nRand = Random2(pXSprite->data1);
594                     evPost(nSprite, 3, 120*(nRand+pXSprite->busyTime) / 10, kCmdRepeat);
595                 }
596                 break;
597             default:
598                 if (!pXSprite->state) {
599                     SetSpriteState(nSprite, pXSprite, 1);
600                     evPost(nSprite, 3, 0, kCmdRepeat);
601                 }
602                 break;
603         }
604         break;
605     case kSoundPlayer:
606         if (gGameOptions.nGameType == 0)
607         {
608             if (gMe->pXSprite->health <= 0)
609                 break;
610             gMe->restTime = 0;
611         }
612         sndStartSample(pXSprite->data1, -1, 1, 0);
613         break;
614     case kThingObjectGib:
615     case kThingObjectExplode:
616     case kThingBloodBits:
617     case kThingBloodChunks:
618     case kThingZombieHead:
619         switch (event.cmd) {
620             case kCmdOff:
621                 if (!SetSpriteState(nSprite, pXSprite, 0)) break;
622                 actActivateGibObject(pSprite, pXSprite);
623                 break;
624             case kCmdOn:
625                 if (!SetSpriteState(nSprite, pXSprite, 1)) break;
626                 actActivateGibObject(pSprite, pXSprite);
627                 break;
628             default:
629                 if (!SetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1)) break;
630                 actActivateGibObject(pSprite, pXSprite);
631                 break;
632         }
633         break;
634     default:
635         switch (event.cmd) {
636             case kCmdOff:
637                 SetSpriteState(nSprite, pXSprite, 0);
638                 break;
639             case kCmdOn:
640                 SetSpriteState(nSprite, pXSprite, 1);
641                 break;
642             default:
643                 SetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1);
644                 break;
645         }
646         break;
647     }
648 }
649 
SetupGibWallState(walltype * pWall,XWALL * pXWall)650 void SetupGibWallState(walltype *pWall, XWALL *pXWall)
651 {
652     walltype *pWall2 = NULL;
653     if (pWall->nextwall >= 0)
654         pWall2 = &wall[pWall->nextwall];
655     if (pXWall->state)
656     {
657         pWall->cstat &= ~65;
658         if (pWall2)
659         {
660             pWall2->cstat &= ~65;
661             pWall->cstat &= ~16;
662             pWall2->cstat &= ~16;
663         }
664         return;
665     }
666     char bVector = pXWall->triggerVector != 0;
667     pWall->cstat |= 1;
668     if (bVector)
669         pWall->cstat |= 64;
670     if (pWall2)
671     {
672         pWall2->cstat |= 1;
673         if (bVector)
674             pWall2->cstat |= 64;
675         pWall->cstat |= 16;
676         pWall2->cstat |= 16;
677     }
678 }
679 
OperateWall(int nWall,XWALL * pXWall,EVENT event)680 void OperateWall(int nWall, XWALL *pXWall, EVENT event) {
681     walltype *pWall = &wall[nWall];
682 
683     switch (event.cmd) {
684         case kCmdLock:
685             pXWall->locked = 1;
686             return;
687         case kCmdUnlock:
688             pXWall->locked = 0;
689             return;
690         case kCmdToggleLock:
691             pXWall->locked ^= 1;
692             return;
693     }
694 
695     #ifdef NOONE_EXTENSIONS
696     if (gModernMap && modernTypeOperateWall(nWall, pWall, pXWall, event))
697         return;
698     #endif
699 
700     switch (pWall->type) {
701         case kWallGib:
702             if (GetWallType(nWall) != pWall->type) break;
703             char bStatus;
704             switch (event.cmd) {
705                 case kCmdOn:
706                 case kCmdWallImpact:
707                     bStatus = SetWallState(nWall, pXWall, 1);
708                     break;
709                 case kCmdOff:
710                     bStatus = SetWallState(nWall, pXWall, 0);
711                     break;
712                 default:
713                     bStatus = SetWallState(nWall, pXWall, pXWall->state ^ 1);
714                     break;
715             }
716 
717             if (bStatus) {
718                 SetupGibWallState(pWall, pXWall);
719                 if (pXWall->state) {
720                     CGibVelocity vel(100, 100, 250);
721                     int nType = ClipRange(pXWall->data, 0, 31);
722                     if (nType > 0)
723                         GibWall(nWall, (GIBTYPE)nType, &vel);
724                 }
725             }
726             return;
727         default:
728             switch (event.cmd) {
729                 case kCmdOff:
730                     SetWallState(nWall, pXWall, 0);
731                     break;
732                 case kCmdOn:
733                     SetWallState(nWall, pXWall, 1);
734                     break;
735                 default:
736                     SetWallState(nWall, pXWall, pXWall->state ^ 1);
737                     break;
738             }
739             return;
740     }
741 
742 
743 }
744 
SectorStartSound(int nSector,int nState)745 void SectorStartSound(int nSector, int nState)
746 {
747     for (int nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
748     {
749         spritetype *pSprite = &sprite[nSprite];
750         if (pSprite->statnum == kStatDecoration && pSprite->type == kSoundSector)
751         {
752             int nXSprite = pSprite->extra;
753             dassert(nXSprite > 0 && nXSprite < kMaxXSprites);
754             XSPRITE *pXSprite = &xsprite[nXSprite];
755             if (nState)
756             {
757                 if (pXSprite->data3)
758                     sfxPlay3DSound(pSprite, pXSprite->data3, 0, 0);
759             }
760             else
761             {
762                 if (pXSprite->data1)
763                     sfxPlay3DSound(pSprite, pXSprite->data1, 0, 0);
764             }
765         }
766     }
767 }
768 
SectorEndSound(int nSector,int nState)769 void SectorEndSound(int nSector, int nState)
770 {
771     for (int nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
772     {
773         spritetype *pSprite = &sprite[nSprite];
774         if (pSprite->statnum == kStatDecoration && pSprite->type == kSoundSector)
775         {
776             int nXSprite = pSprite->extra;
777             dassert(nXSprite > 0 && nXSprite < kMaxXSprites);
778             XSPRITE *pXSprite = &xsprite[nXSprite];
779             if (nState)
780             {
781                 if (pXSprite->data2)
782                     sfxPlay3DSound(pSprite, pXSprite->data2, 0, 0);
783             }
784             else
785             {
786                 if (pXSprite->data4)
787                     sfxPlay3DSound(pSprite, pXSprite->data4, 0, 0);
788             }
789         }
790     }
791 }
792 
PathSound(int nSector,int nSound)793 void PathSound(int nSector, int nSound)
794 {
795     for (int nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
796     {
797         spritetype *pSprite = &sprite[nSprite];
798         if (pSprite->statnum == kStatDecoration && pSprite->type == kSoundSector)
799             sfxPlay3DSound(pSprite, nSound, 0, 0);
800     }
801 }
802 
DragPoint(int nWall,int x,int y)803 void DragPoint(int nWall, int x, int y)
804 {
805     viewInterpolateWall(nWall, &wall[nWall]);
806     wall[nWall].x = x;
807     wall[nWall].y = y;
808 
809     int vsi = numwalls;
810     int vb = nWall;
811     do
812     {
813         if (wall[vb].nextwall >= 0)
814         {
815             vb = wall[wall[vb].nextwall].point2;
816             viewInterpolateWall(vb, &wall[vb]);
817             wall[vb].x = x;
818             wall[vb].y = y;
819         }
820         else
821         {
822             vb = nWall;
823             do
824             {
825                 if (wall[lastwall(vb)].nextwall >= 0)
826                 {
827                     vb = wall[lastwall(vb)].nextwall;
828                     viewInterpolateWall(vb, &wall[vb]);
829                     wall[vb].x = x;
830                     wall[vb].y = y;
831                 }
832                 else
833                     break;
834                 vsi--;
835             } while (vb != nWall && vsi > 0);
836             break;
837         }
838         vsi--;
839     } while (vb != nWall && vsi > 0);
840 }
841 
TranslateSector(int nSector,int a2,int a3,int a4,int a5,int a6,int a7,int a8,int a9,int a10,int a11,char a12)842 void TranslateSector(int nSector, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11, char a12)
843 {
844     int x, y;
845     int nXSector = sector[nSector].extra;
846     XSECTOR *pXSector = &xsector[nXSector];
847     int v20 = interpolate(a6, a9, a2);
848     int vc = interpolate(a6, a9, a3);
849     int v28 = vc - v20;
850     int v24 = interpolate(a7, a10, a2);
851     int v8 = interpolate(a7, a10, a3);
852     int v2c = v8 - v24;
853     int v44 = interpolate(a8, a11, a2);
854     int vbp = interpolate(a8, a11, a3);
855     int v14 = vbp - v44;
856     int nWall = sector[nSector].wallptr;
857     if (a12)
858     {
859         for (int i = 0; i < sector[nSector].wallnum; nWall++, i++)
860         {
861             x = baseWall[nWall].x;
862             y = baseWall[nWall].y;
863             if (vbp)
864                 RotatePoint((int*)&x, (int*)&y, vbp, a4, a5);
865             DragPoint(nWall, x+vc-a4, y+v8-a5);
866         }
867     }
868     else
869     {
870         for (int i = 0; i < sector[nSector].wallnum; nWall++, i++)
871         {
872             int v10 = wall[nWall].point2;
873             x = baseWall[nWall].x;
874             y = baseWall[nWall].y;
875             if (wall[nWall].cstat&16384)
876             {
877                 if (vbp)
878                     RotatePoint((int*)&x, (int*)&y, vbp, a4, a5);
879                 DragPoint(nWall, x+vc-a4, y+v8-a5);
880                 if ((wall[v10].cstat&49152) == 0)
881                 {
882                     x = baseWall[v10].x;
883                     y = baseWall[v10].y;
884                     if (vbp)
885                         RotatePoint((int*)&x, (int*)&y, vbp, a4, a5);
886                     DragPoint(v10, x+vc-a4, y+v8-a5);
887                 }
888                 continue;
889             }
890             if (wall[nWall].cstat&32768)
891             {
892                 if (vbp)
893                     RotatePoint((int*)&x, (int*)&y, -vbp, a4, a5);
894                 DragPoint(nWall, x-(vc-a4), y-(v8-a5));
895                 if ((wall[v10].cstat&49152) == 0)
896                 {
897                     x = baseWall[v10].x;
898                     y = baseWall[v10].y;
899                     if (vbp)
900                         RotatePoint((int*)&x, (int*)&y, -vbp, a4, a5);
901                     DragPoint(v10, x-(vc-a4), y-(v8-a5));
902                 }
903                 continue;
904             }
905         }
906     }
907     for (int nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
908     {
909         spritetype *pSprite = &sprite[nSprite];
910         // allow to move markers by sector movements in game if flags 1 is added in editor.
911         switch (pSprite->statnum) {
912             case kStatMarker:
913             case kStatPathMarker:
914                 #ifdef NOONE_EXTENSIONS
915                     if (!gModernMap || !(pSprite->flags & 0x1)) continue;
916                 #else
917                     continue;
918                 #endif
919                 break;
920         }
921 
922         x = baseSprite[nSprite].x;
923         y = baseSprite[nSprite].y;
924         if (sprite[nSprite].cstat&8192)
925         {
926             if (vbp)
927                 RotatePoint((int*)&x, (int*)&y, vbp, a4, a5);
928             viewBackupSpriteLoc(nSprite, pSprite);
929             pSprite->ang = (pSprite->ang+v14)&2047;
930             pSprite->x = x+vc-a4;
931             pSprite->y = y+v8-a5;
932         }
933         else if (sprite[nSprite].cstat&16384)
934         {
935             if (vbp)
936                 RotatePoint((int*)& x, (int*)& y, -vbp, a4, a4);
937             viewBackupSpriteLoc(nSprite, pSprite);
938             pSprite->ang = (pSprite->ang-v14)&2047;
939             pSprite->x = x-(vc-a4);
940             pSprite->y = y-(v8-a5);
941         }
942         else if (pXSector->Drag)
943         {
944             int top, bottom;
945             GetSpriteExtents(pSprite, &top, &bottom);
946             int floorZ = getflorzofslope(nSector, pSprite->x, pSprite->y);
947             if (!(pSprite->cstat&48) && floorZ <= bottom)
948             {
949                 if (v14)
950                     RotatePoint((int*)&pSprite->x, (int*)&pSprite->y, v14, v20, v24);
951                 viewBackupSpriteLoc(nSprite, pSprite);
952                 pSprite->ang = (pSprite->ang+v14)&2047;
953                 pSprite->x += v28;
954                 pSprite->y += v2c;
955             }
956         }
957     }
958 }
959 
ZTranslateSector(int nSector,XSECTOR * pXSector,int a3,int a4)960 void ZTranslateSector(int nSector, XSECTOR *pXSector, int a3, int a4)
961 {
962     sectortype *pSector = &sector[nSector];
963     viewInterpolateSector(nSector, pSector);
964     int dz = pXSector->onFloorZ-pXSector->offFloorZ;
965     if (dz != 0)
966     {
967         int oldZ = pSector->floorz;
968         baseFloor[nSector] = pSector->floorz = pXSector->offFloorZ + mulscale16(dz, GetWaveValue(a3, a4));
969         velFloor[nSector] += (pSector->floorz-oldZ)<<8;
970         for (int nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
971         {
972             spritetype *pSprite = &sprite[nSprite];
973             if (pSprite->statnum == kStatMarker || pSprite->statnum == kStatPathMarker)
974                 continue;
975             int top, bottom;
976             GetSpriteExtents(pSprite, &top, &bottom);
977             if (pSprite->cstat&8192)
978             {
979                 viewBackupSpriteLoc(nSprite, pSprite);
980                 pSprite->z += pSector->floorz-oldZ;
981             }
982             else if (pSprite->flags&2)
983                 pSprite->flags |= 4;
984             else if (oldZ <= bottom && !(pSprite->cstat&48))
985             {
986                 viewBackupSpriteLoc(nSprite, pSprite);
987                 pSprite->z += pSector->floorz-oldZ;
988             }
989         }
990     }
991     dz = pXSector->onCeilZ-pXSector->offCeilZ;
992     if (dz != 0)
993     {
994         int oldZ = pSector->ceilingz;
995         baseCeil[nSector] = pSector->ceilingz = pXSector->offCeilZ + mulscale16(dz, GetWaveValue(a3, a4));
996         velCeil[nSector] += (pSector->ceilingz-oldZ)<<8;
997         for (int nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
998         {
999             spritetype *pSprite = &sprite[nSprite];
1000             if (pSprite->statnum == kStatMarker || pSprite->statnum == kStatPathMarker)
1001                 continue;
1002             if (pSprite->cstat&16384)
1003             {
1004                 viewBackupSpriteLoc(nSprite, pSprite);
1005                 pSprite->z += pSector->ceilingz-oldZ;
1006             }
1007         }
1008     }
1009 }
1010 
GetHighestSprite(int nSector,int nStatus,int * a3)1011 int GetHighestSprite(int nSector, int nStatus, int *a3)
1012 {
1013     *a3 = sector[nSector].floorz;
1014     int v8 = -1;
1015     for (int nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
1016     {
1017         if (sprite[nSprite].statnum == nStatus || nStatus == kStatFree)
1018         {
1019             spritetype *pSprite = &sprite[nSprite];
1020             int top, bottom;
1021             GetSpriteExtents(pSprite, &top, &bottom);
1022             if (top-pSprite->z > *a3)
1023             {
1024                 *a3 = top-pSprite->z;
1025                 v8 = nSprite;
1026             }
1027         }
1028     }
1029     return v8;
1030 }
1031 
GetCrushedSpriteExtents(unsigned int nSector,int * pzTop,int * pzBot)1032 int GetCrushedSpriteExtents(unsigned int nSector, int *pzTop, int *pzBot)
1033 {
1034     dassert(pzTop != NULL && pzBot != NULL);
1035     dassert(nSector < (unsigned int)numsectors);
1036     int vc = -1;
1037     sectortype *pSector = &sector[nSector];
1038     int vbp = pSector->ceilingz;
1039     for (int nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
1040     {
1041         spritetype *pSprite = &sprite[nSprite];
1042         if (pSprite->statnum == kStatDude || pSprite->statnum == kStatThing)
1043         {
1044             int top, bottom;
1045             GetSpriteExtents(pSprite, &top, &bottom);
1046             if (vbp > top)
1047             {
1048                 vbp = top;
1049                 *pzTop = top;
1050                 *pzBot = bottom;
1051                 vc = nSprite;
1052             }
1053         }
1054     }
1055     return vc;
1056 }
1057 
VCrushBusy(unsigned int nSector,unsigned int a2)1058 int VCrushBusy(unsigned int nSector, unsigned int a2)
1059 {
1060     dassert(nSector < (unsigned int)numsectors);
1061     int nXSector = sector[nSector].extra;
1062     dassert(nXSector > 0 && nXSector < kMaxXSectors);
1063     XSECTOR *pXSector = &xsector[nXSector];
1064     int nWave;
1065     if (pXSector->busy < a2)
1066         nWave = pXSector->busyWaveA;
1067     else
1068         nWave = pXSector->busyWaveB;
1069     int dz1 = pXSector->onCeilZ - pXSector->offCeilZ;
1070     int vc = pXSector->offCeilZ;
1071     if (dz1 != 0)
1072         vc += mulscale16(dz1, GetWaveValue(a2, nWave));
1073     int dz2 = pXSector->onFloorZ - pXSector->offFloorZ;
1074     int v10 = pXSector->offFloorZ;
1075     if (dz2 != 0)
1076         v10 += mulscale16(dz2, GetWaveValue(a2, nWave));
1077     int v18;
1078     if (GetHighestSprite(nSector, 6, &v18) >= 0 && vc >= v18)
1079         return 1;
1080     viewInterpolateSector(nSector, &sector[nSector]);
1081     if (dz1 != 0)
1082         sector[nSector].ceilingz = vc;
1083     if (dz2 != 0)
1084         sector[nSector].floorz = v10;
1085     pXSector->busy = a2;
1086     if (pXSector->command == kCmdLink && pXSector->txID)
1087         evSend(nSector, 6, pXSector->txID, kCmdLink);
1088     if ((a2&0xffff) == 0)
1089     {
1090         SetSectorState(nSector, pXSector, a2>>16);
1091         SectorEndSound(nSector, a2>>16);
1092         return 3;
1093     }
1094     return 0;
1095 }
1096 
VSpriteBusy(unsigned int nSector,unsigned int a2)1097 int VSpriteBusy(unsigned int nSector, unsigned int a2)
1098 {
1099     dassert(nSector < (unsigned int)numsectors);
1100     int nXSector = sector[nSector].extra;
1101     dassert(nXSector > 0 && nXSector < kMaxXSectors);
1102     XSECTOR *pXSector = &xsector[nXSector];
1103     int nWave;
1104     if (pXSector->busy < a2)
1105         nWave = pXSector->busyWaveA;
1106     else
1107         nWave = pXSector->busyWaveB;
1108     int dz1 = pXSector->onFloorZ - pXSector->offFloorZ;
1109     if (dz1 != 0)
1110     {
1111         for (int nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
1112         {
1113             spritetype *pSprite = &sprite[nSprite];
1114             if (pSprite->cstat&8192)
1115             {
1116                 viewBackupSpriteLoc(nSprite, pSprite);
1117                 pSprite->z = baseSprite[nSprite].z+mulscale16(dz1, GetWaveValue(a2, nWave));
1118             }
1119         }
1120     }
1121     int dz2 = pXSector->onCeilZ - pXSector->offCeilZ;
1122     if (dz2 != 0)
1123     {
1124         for (int nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
1125         {
1126             spritetype *pSprite = &sprite[nSprite];
1127             if (pSprite->cstat&16384)
1128             {
1129                 viewBackupSpriteLoc(nSprite, pSprite);
1130                 pSprite->z = baseSprite[nSprite].z+mulscale16(dz2, GetWaveValue(a2, nWave));
1131             }
1132         }
1133     }
1134     pXSector->busy = a2;
1135     if (pXSector->command == kCmdLink && pXSector->txID)
1136         evSend(nSector, 6, pXSector->txID, kCmdLink);
1137     if ((a2&0xffff) == 0)
1138     {
1139         SetSectorState(nSector, pXSector, a2>>16);
1140         SectorEndSound(nSector, a2>>16);
1141         return 3;
1142     }
1143     return 0;
1144 }
1145 
VDoorBusy(unsigned int nSector,unsigned int a2)1146 int VDoorBusy(unsigned int nSector, unsigned int a2)
1147 {
1148     dassert(nSector < (unsigned int)numsectors);
1149     int nXSector = sector[nSector].extra;
1150     dassert(nXSector > 0 && nXSector < kMaxXSectors);
1151     XSECTOR *pXSector = &xsector[nXSector];
1152     int vbp;
1153     if (pXSector->state)
1154         vbp = 65536/ClipLow((120*pXSector->busyTimeA)/10, 1);
1155     else
1156         vbp = -65536/ClipLow((120*pXSector->busyTimeB)/10, 1);
1157     int top, bottom;
1158     int nSprite = GetCrushedSpriteExtents(nSector,&top,&bottom);
1159     if (nSprite >= 0 && a2 > pXSector->busy)
1160     {
1161         spritetype *pSprite = &sprite[nSprite];
1162         dassert(pSprite->extra > 0 && pSprite->extra < kMaxXSprites);
1163         XSPRITE *pXSprite = &xsprite[pSprite->extra];
1164         if (pXSector->onCeilZ > pXSector->offCeilZ || pXSector->onFloorZ < pXSector->offFloorZ)
1165         {
1166             if (pXSector->interruptable)
1167             {
1168                 if (pXSector->Crush)
1169                 {
1170                     if (pXSprite->health <= 0)
1171                         return 2;
1172                     int nDamage;
1173                     if (pXSector->data == 0)
1174                         nDamage = 500;
1175                     else
1176                         nDamage = pXSector->data;
1177                     actDamageSprite(nSprite, &sprite[nSprite], kDamageFall, nDamage<<4);
1178                 }
1179                 a2 = ClipRange(a2-(vbp/2)*4, 0, 65536);
1180             }
1181             else if (pXSector->Crush && pXSprite->health > 0)
1182             {
1183                 int nDamage;
1184                 if (pXSector->data == 0)
1185                     nDamage = 500;
1186                 else
1187                     nDamage = pXSector->data;
1188                 actDamageSprite(nSprite, &sprite[nSprite], kDamageFall, nDamage<<4);
1189                 a2 = ClipRange(a2-(vbp/2)*4, 0, 65536);
1190             }
1191         }
1192     }
1193     else if (nSprite >= 0 && a2 < pXSector->busy)
1194     {
1195         spritetype *pSprite = &sprite[nSprite];
1196         dassert(pSprite->extra > 0 && pSprite->extra < kMaxXSprites);
1197         XSPRITE *pXSprite = &xsprite[pSprite->extra];
1198         if (pXSector->offCeilZ > pXSector->onCeilZ || pXSector->offFloorZ < pXSector->onFloorZ)
1199         {
1200             if (pXSector->interruptable)
1201             {
1202                 if (pXSector->Crush)
1203                 {
1204                     if (pXSprite->health <= 0)
1205                         return 2;
1206                     int nDamage;
1207                     if (pXSector->data == 0)
1208                         nDamage = 500;
1209                     else
1210                         nDamage = pXSector->data;
1211                     actDamageSprite(nSprite, &sprite[nSprite], kDamageFall, nDamage<<4);
1212                 }
1213                 a2 = ClipRange(a2+(vbp/2)*4, 0, 65536);
1214             }
1215             else if (pXSector->Crush && pXSprite->health > 0)
1216             {
1217                 int nDamage;
1218                 if (pXSector->data == 0)
1219                     nDamage = 500;
1220                 else
1221                     nDamage = pXSector->data;
1222                 actDamageSprite(nSprite, &sprite[nSprite], kDamageFall, nDamage<<4);
1223                 a2 = ClipRange(a2+(vbp/2)*4, 0, 65536);
1224             }
1225         }
1226     }
1227     int nWave;
1228     if (pXSector->busy < a2)
1229         nWave = pXSector->busyWaveA;
1230     else
1231         nWave = pXSector->busyWaveB;
1232     ZTranslateSector(nSector, pXSector, a2, nWave);
1233     pXSector->busy = a2;
1234     if (pXSector->command == kCmdLink && pXSector->txID)
1235         evSend(nSector, 6, pXSector->txID, kCmdLink);
1236     if ((a2&0xffff) == 0)
1237     {
1238         SetSectorState(nSector, pXSector, a2>>16);
1239         SectorEndSound(nSector, a2>>16);
1240         return 3;
1241     }
1242     return 0;
1243 }
1244 
HDoorBusy(unsigned int nSector,unsigned int a2)1245 int HDoorBusy(unsigned int nSector, unsigned int a2)
1246 {
1247     dassert(nSector < (unsigned int)numsectors);
1248     sectortype *pSector = &sector[nSector];
1249     int nXSector = pSector->extra;
1250     dassert(nXSector > 0 && nXSector < kMaxXSectors);
1251     XSECTOR *pXSector = &xsector[nXSector];
1252     int nWave;
1253     if (pXSector->busy < a2)
1254         nWave = pXSector->busyWaveA;
1255     else
1256         nWave = pXSector->busyWaveB;
1257     spritetype *pSprite1 = &sprite[pXSector->marker0];
1258     spritetype *pSprite2 = &sprite[pXSector->marker1];
1259     TranslateSector(nSector, GetWaveValue(pXSector->busy, nWave), GetWaveValue(a2, nWave), pSprite1->x, pSprite1->y, pSprite1->x, pSprite1->y, pSprite1->ang, pSprite2->x, pSprite2->y, pSprite2->ang, pSector->type == kSectorSlide);
1260     ZTranslateSector(nSector, pXSector, a2, nWave);
1261     pXSector->busy = a2;
1262     if (pXSector->command == kCmdLink && pXSector->txID)
1263         evSend(nSector, 6, pXSector->txID, kCmdLink);
1264     if ((a2&0xffff) == 0)
1265     {
1266         SetSectorState(nSector, pXSector, a2>>16);
1267         SectorEndSound(nSector, a2>>16);
1268         return 3;
1269     }
1270     return 0;
1271 }
1272 
RDoorBusy(unsigned int nSector,unsigned int a2)1273 int RDoorBusy(unsigned int nSector, unsigned int a2)
1274 {
1275     dassert(nSector < (unsigned int)numsectors);
1276     sectortype *pSector = &sector[nSector];
1277     int nXSector = pSector->extra;
1278     dassert(nXSector > 0 && nXSector < kMaxXSectors);
1279     XSECTOR *pXSector = &xsector[nXSector];
1280     int nWave;
1281     if (pXSector->busy < a2)
1282         nWave = pXSector->busyWaveA;
1283     else
1284         nWave = pXSector->busyWaveB;
1285     spritetype *pSprite = &sprite[pXSector->marker0];
1286     TranslateSector(nSector, GetWaveValue(pXSector->busy, nWave), GetWaveValue(a2, nWave), pSprite->x, pSprite->y, pSprite->x, pSprite->y, 0, pSprite->x, pSprite->y, pSprite->ang, pSector->type == kSectorRotate);
1287     ZTranslateSector(nSector, pXSector, a2, nWave);
1288     pXSector->busy = a2;
1289     if (pXSector->command == kCmdLink && pXSector->txID)
1290         evSend(nSector, 6, pXSector->txID, kCmdLink);
1291     if ((a2&0xffff) == 0)
1292     {
1293         SetSectorState(nSector, pXSector, a2>>16);
1294         SectorEndSound(nSector, a2>>16);
1295         return 3;
1296     }
1297     return 0;
1298 }
1299 
StepRotateBusy(unsigned int nSector,unsigned int a2)1300 int StepRotateBusy(unsigned int nSector, unsigned int a2)
1301 {
1302     dassert(nSector < (unsigned int)numsectors);
1303     sectortype *pSector = &sector[nSector];
1304     int nXSector = pSector->extra;
1305     dassert(nXSector > 0 && nXSector < kMaxXSectors);
1306     XSECTOR *pXSector = &xsector[nXSector];
1307     spritetype *pSprite = &sprite[pXSector->marker0];
1308     int vbp;
1309     if (pXSector->busy < a2)
1310     {
1311         vbp = pXSector->data+pSprite->ang;
1312         int nWave = pXSector->busyWaveA;
1313         TranslateSector(nSector, GetWaveValue(pXSector->busy, nWave), GetWaveValue(a2, nWave), pSprite->x, pSprite->y, pSprite->x, pSprite->y, pXSector->data, pSprite->x, pSprite->y, vbp, 1);
1314     }
1315     else
1316     {
1317         vbp = pXSector->data-pSprite->ang;
1318         int nWave = pXSector->busyWaveB;
1319         TranslateSector(nSector, GetWaveValue(pXSector->busy, nWave), GetWaveValue(a2, nWave), pSprite->x, pSprite->y, pSprite->x, pSprite->y, vbp, pSprite->x, pSprite->y, pXSector->data, 1);
1320     }
1321     pXSector->busy = a2;
1322     if (pXSector->command == kCmdLink && pXSector->txID)
1323         evSend(nSector, 6, pXSector->txID, kCmdLink);
1324     if ((a2&0xffff) == 0)
1325     {
1326         SetSectorState(nSector, pXSector, a2>>16);
1327         SectorEndSound(nSector, a2>>16);
1328         pXSector->data = vbp&2047;
1329         return 3;
1330     }
1331     return 0;
1332 }
1333 
GenSectorBusy(unsigned int nSector,unsigned int a2)1334 int GenSectorBusy(unsigned int nSector, unsigned int a2)
1335 {
1336     dassert(nSector < (unsigned int)numsectors);
1337     sectortype *pSector = &sector[nSector];
1338     int nXSector = pSector->extra;
1339     dassert(nXSector > 0 && nXSector < kMaxXSectors);
1340     XSECTOR *pXSector = &xsector[nXSector];
1341     pXSector->busy = a2;
1342     if (pXSector->command == kCmdLink && pXSector->txID)
1343         evSend(nSector, 6, pXSector->txID, kCmdLink);
1344     if ((a2&0xffff) == 0)
1345     {
1346         SetSectorState(nSector, pXSector, a2>>16);
1347         SectorEndSound(nSector, a2>>16);
1348         return 3;
1349     }
1350     return 0;
1351 }
1352 
PathBusy(unsigned int nSector,unsigned int a2)1353 int PathBusy(unsigned int nSector, unsigned int a2)
1354 {
1355     dassert(nSector < (unsigned int)numsectors);
1356     sectortype *pSector = &sector[nSector];
1357     int nXSector = pSector->extra;
1358     dassert(nXSector > 0 && nXSector < kMaxXSectors);
1359     XSECTOR *pXSector = &xsector[nXSector];
1360     spritetype *pSprite = &sprite[basePath[nSector]];
1361     spritetype *pSprite1 = &sprite[pXSector->marker0];
1362     XSPRITE *pXSprite1 = &xsprite[pSprite1->extra];
1363     spritetype *pSprite2 = &sprite[pXSector->marker1];
1364     XSPRITE *pXSprite2 = &xsprite[pSprite2->extra];
1365     int nWave = pXSprite1->wave;
1366     TranslateSector(nSector, GetWaveValue(pXSector->busy, nWave), GetWaveValue(a2, nWave), pSprite->x, pSprite->y, pSprite1->x, pSprite1->y, pSprite1->ang, pSprite2->x, pSprite2->y, pSprite2->ang, 1);
1367     ZTranslateSector(nSector, pXSector, a2, nWave);
1368     pXSector->busy = a2;
1369     if ((a2&0xffff) == 0)
1370     {
1371         evPost(nSector, 6, (120*pXSprite2->waitTime)/10, kCmdOn);
1372         pXSector->state = 0;
1373         pXSector->busy = 0;
1374         if (pXSprite1->data4)
1375             PathSound(nSector, pXSprite1->data4);
1376         pXSector->marker0 = pXSector->marker1;
1377         pXSector->data = pXSprite2->data1;
1378         return 3;
1379     }
1380     return 0;
1381 }
1382 
OperateDoor(unsigned int nSector,XSECTOR * pXSector,EVENT event,BUSYID busyWave)1383 void OperateDoor(unsigned int nSector, XSECTOR *pXSector, EVENT event, BUSYID busyWave)
1384 {
1385     switch (event.cmd) {
1386         case kCmdOff:
1387             if (!pXSector->busy) break;
1388             AddBusy(nSector, busyWave, -65536/ClipLow((pXSector->busyTimeB*120)/10, 1));
1389             SectorStartSound(nSector, 1);
1390             break;
1391         case kCmdOn:
1392             if (pXSector->busy == 0x10000) break;
1393             AddBusy(nSector, busyWave, 65536/ClipLow((pXSector->busyTimeA*120)/10, 1));
1394             SectorStartSound(nSector, 0);
1395             break;
1396         default:
1397             if (pXSector->busy & 0xffff)  {
1398                 if (pXSector->interruptable) {
1399                     ReverseBusy(nSector, busyWave);
1400                     pXSector->state = !pXSector->state;
1401                 }
1402             } else {
1403                 char t = !pXSector->state; int nDelta;
1404 
1405                 if (t) nDelta = 65536/ClipLow((pXSector->busyTimeA*120)/10, 1);
1406                 else nDelta = -65536/ClipLow((pXSector->busyTimeB*120)/10, 1);
1407 
1408                 AddBusy(nSector, busyWave, nDelta);
1409                 SectorStartSound(nSector, pXSector->state);
1410             }
1411             break;
1412     }
1413 }
1414 
SectorContainsDudes(int nSector)1415 char SectorContainsDudes(int nSector)
1416 {
1417     for (int nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
1418     {
1419         if (sprite[nSprite].statnum == kStatDude)
1420             return 1;
1421     }
1422     return 0;
1423 }
1424 
TeleFrag(int nKiller,int nSector)1425 void TeleFrag(int nKiller, int nSector)
1426 {
1427     for (int nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
1428     {
1429         spritetype *pSprite = &sprite[nSprite];
1430         if (pSprite->statnum == kStatDude)
1431             actDamageSprite(nKiller, pSprite, kDamageExplode, 4000);
1432         else if (pSprite->statnum == kStatThing)
1433             actDamageSprite(nKiller, pSprite, kDamageExplode, 4000);
1434     }
1435 }
1436 
OperateTeleport(unsigned int nSector,XSECTOR * pXSector)1437 void OperateTeleport(unsigned int nSector, XSECTOR *pXSector)
1438 {
1439     dassert(nSector < (unsigned int)numsectors);
1440     int nDest = pXSector->marker0;
1441     dassert(nDest < kMaxSprites);
1442     spritetype *pDest = &sprite[nDest];
1443     dassert(pDest->statnum == kStatMarker);
1444     dassert(pDest->type == kMarkerWarpDest);
1445     dassert(pDest->sectnum >= 0 && pDest->sectnum < kMaxSectors);
1446     for (int nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
1447     {
1448         spritetype *pSprite = &sprite[nSprite];
1449         if (pSprite->statnum == kStatDude)
1450         {
1451             PLAYER *pPlayer;
1452             char bPlayer = IsPlayerSprite(pSprite);
1453             if (bPlayer)
1454                 pPlayer = &gPlayer[pSprite->type-kDudePlayer1];
1455             else
1456                 pPlayer = NULL;
1457             if (bPlayer || !SectorContainsDudes(pDest->sectnum))
1458             {
1459                 if (!(gGameOptions.uNetGameFlags&2))
1460                     TeleFrag(pXSector->data, pDest->sectnum);
1461                 pSprite->x = pDest->x;
1462                 pSprite->y = pDest->y;
1463                 pSprite->z += sector[pDest->sectnum].floorz-sector[nSector].floorz;
1464                 pSprite->ang = pDest->ang;
1465                 ChangeSpriteSect(nSprite, pDest->sectnum);
1466                 sfxPlay3DSound(pDest, 201, -1, 0);
1467                 xvel[nSprite] = yvel[nSprite] = zvel[nSprite] = 0;
1468                 ClearBitString(gInterpolateSprite, nSprite);
1469                 viewBackupSpriteLoc(nSprite, pSprite);
1470                 if (pPlayer)
1471                 {
1472                     playerResetInertia(pPlayer);
1473                     pPlayer->zViewVel = pPlayer->zWeaponVel = 0;
1474                 }
1475             }
1476         }
1477     }
1478 }
1479 
OperatePath(unsigned int nSector,XSECTOR * pXSector,EVENT event)1480 void OperatePath(unsigned int nSector, XSECTOR *pXSector, EVENT event)
1481 {
1482     int nSprite;
1483     spritetype *pSprite = NULL;
1484     XSPRITE *pXSprite;
1485     dassert(nSector < (unsigned int)numsectors);
1486     spritetype *pSprite2 = &sprite[pXSector->marker0];
1487     XSPRITE *pXSprite2 = &xsprite[pSprite2->extra];
1488     int nId = pXSprite2->data2;
1489     for (nSprite = headspritestat[kStatPathMarker]; nSprite >= 0; nSprite = nextspritestat[nSprite])
1490     {
1491         pSprite = &sprite[nSprite];
1492         if (pSprite->type == kMarkerPath)
1493         {
1494             pXSprite = &xsprite[pSprite->extra];
1495             if (pXSprite->data1 == nId)
1496                 break;
1497         }
1498     }
1499 
1500     // trigger marker after it gets reached
1501     #ifdef NOONE_EXTENSIONS
1502         if (gModernMap && pXSprite2->state != 1)
1503             trTriggerSprite(pSprite2->index, pXSprite2, kCmdOn);
1504     #endif
1505 
1506     if (nSprite < 0) {
1507         viewSetSystemMessage("Unable to find path marker with id #%d for path sector #%d", nId, nSector);
1508         pXSector->state = 0;
1509         pXSector->busy = 0;
1510         return;
1511     }
1512 
1513     pXSector->marker1 = nSprite;
1514     pXSector->offFloorZ = pSprite2->z;
1515     pXSector->onFloorZ = pSprite->z;
1516     switch (event.cmd) {
1517         case kCmdOn:
1518             pXSector->state = 0;
1519             pXSector->busy = 0;
1520             AddBusy(nSector, BUSYID_7, 65536/ClipLow((120*pXSprite2->busyTime)/10,1));
1521             if (pXSprite2->data3) PathSound(nSector, pXSprite2->data3);
1522             break;
1523     }
1524 }
1525 
OperateSector(unsigned int nSector,XSECTOR * pXSector,EVENT event)1526 void OperateSector(unsigned int nSector, XSECTOR *pXSector, EVENT event)
1527 {
1528     dassert(nSector < (unsigned int)numsectors);
1529     sectortype *pSector = &sector[nSector];
1530 
1531     #ifdef NOONE_EXTENSIONS
1532     if (gModernMap && modernTypeOperateSector(nSector, pSector, pXSector, event))
1533         return;
1534     #endif
1535 
1536     switch (event.cmd) {
1537         case kCmdLock:
1538             pXSector->locked = 1;
1539             break;
1540         case kCmdUnlock:
1541             pXSector->locked = 0;
1542             break;
1543         case kCmdToggleLock:
1544             pXSector->locked ^= 1;
1545             break;
1546         case kCmdStopOff:
1547             pXSector->stopOn = 0;
1548             pXSector->stopOff = 1;
1549             break;
1550         case kCmdStopOn:
1551             pXSector->stopOn = 1;
1552             pXSector->stopOff = 0;
1553             break;
1554         case kCmdStopNext:
1555             pXSector->stopOn = 1;
1556             pXSector->stopOff = 1;
1557             break;
1558         default:
1559         #ifdef NOONE_EXTENSIONS
1560         if (gModernMap && pXSector->unused1) break;
1561         #endif
1562             switch (pSector->type) {
1563                 case kSectorZMotionSprite:
1564                     OperateDoor(nSector, pXSector, event, BUSYID_1);
1565                     break;
1566                 case kSectorZMotion:
1567                     OperateDoor(nSector, pXSector, event, BUSYID_2);
1568                     break;
1569                 case kSectorSlideMarked:
1570                 case kSectorSlide:
1571                     OperateDoor(nSector, pXSector, event, BUSYID_3);
1572                     break;
1573                 case kSectorRotateMarked:
1574                 case kSectorRotate:
1575                     OperateDoor(nSector, pXSector, event, BUSYID_4);
1576                     break;
1577                 case kSectorRotateStep:
1578                     switch (event.cmd) {
1579                         case kCmdOn:
1580                             pXSector->state = 0;
1581                             pXSector->busy = 0;
1582                             AddBusy(nSector, BUSYID_5, 65536/ClipLow((120*pXSector->busyTimeA)/10, 1));
1583                             SectorStartSound(nSector, 0);
1584                             break;
1585                         case kCmdOff:
1586                             pXSector->state = 1;
1587                             pXSector->busy = 65536;
1588                             AddBusy(nSector, BUSYID_5, -65536/ClipLow((120*pXSector->busyTimeB)/10, 1));
1589                             SectorStartSound(nSector, 1);
1590                             break;
1591                     }
1592                     break;
1593                 case kSectorTeleport:
1594                     OperateTeleport(nSector, pXSector);
1595                     break;
1596                 case kSectorPath:
1597                     OperatePath(nSector, pXSector, event);
1598                     break;
1599                 default:
1600                     if (!pXSector->busyTimeA && !pXSector->busyTimeB) {
1601 
1602                         switch (event.cmd) {
1603                             case kCmdOff:
1604                                 SetSectorState(nSector, pXSector, 0);
1605                                 break;
1606                             case kCmdOn:
1607                                 SetSectorState(nSector, pXSector, 1);
1608                                 break;
1609                             default:
1610                                 SetSectorState(nSector, pXSector, pXSector->state ^ 1);
1611                                 break;
1612                         }
1613 
1614                     } else {
1615 
1616                         OperateDoor(nSector, pXSector, event, BUSYID_6);
1617 
1618                     }
1619 
1620                     break;
1621             }
1622             break;
1623     }
1624 }
1625 
InitPath(unsigned int nSector,XSECTOR * pXSector)1626 void InitPath(unsigned int nSector, XSECTOR *pXSector)
1627 {
1628     int nSprite;
1629     spritetype *pSprite;
1630     XSPRITE *pXSprite;
1631     dassert(nSector < (unsigned int)numsectors);
1632     int nId = pXSector->data;
1633     for (nSprite = headspritestat[kStatPathMarker]; nSprite >= 0; nSprite = nextspritestat[nSprite])
1634     {
1635         pSprite = &sprite[nSprite];
1636         if (pSprite->type == kMarkerPath)
1637         {
1638             pXSprite = &xsprite[pSprite->extra];
1639             if (pXSprite->data1 == nId)
1640                 break;
1641         }
1642     }
1643 
1644     if (nSprite < 0) {
1645         //ThrowError("Unable to find path marker with id #%d", nId);
1646         viewSetSystemMessage("Unable to find path marker with id #%d for path sector #%d", nId, nSector);
1647         return;
1648 
1649     }
1650 
1651     pXSector->marker0 = nSprite;
1652     basePath[nSector] = nSprite;
1653     if (pXSector->state)
1654         evPost(nSector, 6, 0, kCmdOn);
1655 }
1656 
LinkSector(int nSector,XSECTOR * pXSector,EVENT event)1657 void LinkSector(int nSector, XSECTOR *pXSector, EVENT event)
1658 {
1659     sectortype *pSector = &sector[nSector];
1660     int nBusy = GetSourceBusy(event);
1661     switch (pSector->type) {
1662         case kSectorZMotionSprite:
1663             VSpriteBusy(nSector, nBusy);
1664             break;
1665         case kSectorZMotion:
1666             VDoorBusy(nSector, nBusy);
1667             break;
1668         case kSectorSlideMarked:
1669         case kSectorSlide:
1670             HDoorBusy(nSector, nBusy);
1671             break;
1672         case kSectorRotateMarked:
1673         case kSectorRotate:
1674             RDoorBusy(nSector, nBusy);
1675             break;
1676         default:
1677             pXSector->busy = nBusy;
1678             if ((pXSector->busy&0xffff) == 0)
1679                 SetSectorState(nSector, pXSector, nBusy>>16);
1680             break;
1681     }
1682 }
1683 
LinkSprite(int nSprite,XSPRITE * pXSprite,EVENT event)1684 void LinkSprite(int nSprite, XSPRITE *pXSprite, EVENT event) {
1685     spritetype *pSprite = &sprite[nSprite];
1686     int nBusy = GetSourceBusy(event);
1687 
1688     switch (pSprite->type)  {
1689         case kSwitchCombo:
1690         {
1691             if (event.type == 3)
1692             {
1693                 int nSprite2 = event.index;
1694                 int nXSprite2 = sprite[nSprite2].extra;
1695                 dassert(nXSprite2 > 0 && nXSprite2 < kMaxXSprites);
1696                 pXSprite->data1 = xsprite[nXSprite2].data1;
1697                 if (pXSprite->data1 == pXSprite->data2)
1698                     SetSpriteState(nSprite, pXSprite, 1);
1699                 else
1700                     SetSpriteState(nSprite, pXSprite, 0);
1701             }
1702         }
1703         break;
1704         default:
1705         {
1706             pXSprite->busy = nBusy;
1707             if ((pXSprite->busy & 0xffff) == 0)
1708                 SetSpriteState(nSprite, pXSprite, nBusy >> 16);
1709         }
1710         break;
1711     }
1712 }
1713 
LinkWall(int nWall,XWALL * pXWall,EVENT event)1714 void LinkWall(int nWall, XWALL *pXWall, EVENT event)
1715 {
1716     int nBusy = GetSourceBusy(event);
1717     pXWall->busy = nBusy;
1718     if ((pXWall->busy & 0xffff) == 0)
1719         SetWallState(nWall, pXWall, nBusy>>16);
1720 }
1721 
trTriggerSector(unsigned int nSector,XSECTOR * pXSector,int command)1722 void trTriggerSector(unsigned int nSector, XSECTOR *pXSector, int command) {
1723     dassert(nSector < (unsigned int)numsectors);
1724     if (!pXSector->locked && !pXSector->isTriggered) {
1725 
1726         if (pXSector->triggerOnce)
1727             pXSector->isTriggered = 1;
1728 
1729         if (pXSector->decoupled && pXSector->txID > 0)
1730             evSend(nSector, 6, pXSector->txID, (COMMAND_ID)pXSector->command);
1731 
1732         else {
1733             EVENT event;
1734             event.cmd = command;
1735             OperateSector(nSector, pXSector, event);
1736         }
1737 
1738     }
1739 }
1740 
trTriggerWall(unsigned int nWall,XWALL * pXWall,int command)1741 void trTriggerWall(unsigned int nWall, XWALL *pXWall, int command) {
1742     dassert(nWall < (unsigned int)numwalls);
1743     if (!pXWall->locked && !pXWall->isTriggered) {
1744 
1745         if (pXWall->triggerOnce)
1746             pXWall->isTriggered = 1;
1747 
1748         if (pXWall->decoupled && pXWall->txID > 0)
1749             evSend(nWall, 0, pXWall->txID, (COMMAND_ID)pXWall->command);
1750 
1751         else {
1752             EVENT event;
1753             event.cmd = command;
1754             OperateWall(nWall, pXWall, event);
1755         }
1756 
1757     }
1758 }
1759 
trTriggerSprite(unsigned int nSprite,XSPRITE * pXSprite,int command)1760 void trTriggerSprite(unsigned int nSprite, XSPRITE *pXSprite, int command) {
1761     if (!pXSprite->locked && !pXSprite->isTriggered) {
1762 
1763         if (pXSprite->triggerOnce)
1764             pXSprite->isTriggered = 1;
1765 
1766         if (pXSprite->Decoupled && pXSprite->txID > 0)
1767            evSend(nSprite, 3, pXSprite->txID, (COMMAND_ID)pXSprite->command);
1768 
1769         else {
1770             EVENT event;
1771             event.cmd = command;
1772             OperateSprite(nSprite, pXSprite, event);
1773         }
1774 
1775     }
1776 }
1777 
1778 
trMessageSector(unsigned int nSector,EVENT event)1779 void trMessageSector(unsigned int nSector, EVENT event) {
1780     dassert(nSector < (unsigned int)numsectors);
1781     dassert(sector[nSector].extra > 0 && sector[nSector].extra < kMaxXSectors);
1782     XSECTOR *pXSector = &xsector[sector[nSector].extra];
1783     if (!pXSector->locked || event.cmd == kCmdUnlock || event.cmd == kCmdToggleLock) {
1784         switch (event.cmd) {
1785             case kCmdLink:
1786                 LinkSector(nSector, pXSector, event);
1787                 break;
1788             #ifdef NOONE_EXTENSIONS
1789             case kCmdModernUse:
1790                 modernTypeTrigger(6, nSector, event);
1791                 break;
1792             #endif
1793             default:
1794                 OperateSector(nSector, pXSector, event);
1795                 break;
1796         }
1797     }
1798 }
1799 
trMessageWall(unsigned int nWall,EVENT event)1800 void trMessageWall(unsigned int nWall, EVENT event) {
1801     dassert(nWall < (unsigned int)numwalls);
1802     dassert(wall[nWall].extra > 0 && wall[nWall].extra < kMaxXWalls);
1803 
1804     XWALL *pXWall = &xwall[wall[nWall].extra];
1805     if (!pXWall->locked || event.cmd == kCmdUnlock || event.cmd == kCmdToggleLock) {
1806         switch (event.cmd) {
1807             case kCmdLink:
1808                 LinkWall(nWall, pXWall, event);
1809                 break;
1810             #ifdef NOONE_EXTENSIONS
1811             case kCmdModernUse:
1812                 modernTypeTrigger(0, nWall, event);
1813                 break;
1814             #endif
1815             default:
1816                 OperateWall(nWall, pXWall, event);
1817                 break;
1818         }
1819     }
1820 }
1821 
trMessageSprite(unsigned int nSprite,EVENT event)1822 void trMessageSprite(unsigned int nSprite, EVENT event) {
1823     if (sprite[nSprite].statnum != kStatFree) {
1824 
1825         XSPRITE* pXSprite = &xsprite[sprite[nSprite].extra];
1826         if (!pXSprite->locked || event.cmd == kCmdUnlock || event.cmd == kCmdToggleLock) {
1827             switch (event.cmd) {
1828                 case kCmdLink:
1829                     LinkSprite(nSprite, pXSprite, event);
1830                     break;
1831                 #ifdef NOONE_EXTENSIONS
1832                 case kCmdModernUse:
1833                     modernTypeTrigger(3, nSprite, event);
1834                     break;
1835                 #endif
1836                 default:
1837                     OperateSprite(nSprite, pXSprite, event);
1838                     break;
1839             }
1840         }
1841     }
1842 }
1843 
1844 
1845 
ProcessMotion(void)1846 void ProcessMotion(void)
1847 {
1848     sectortype *pSector;
1849     int nSector;
1850     for (pSector = sector, nSector = 0; nSector < numsectors; nSector++, pSector++)
1851     {
1852         int nXSector = pSector->extra;
1853         if (nXSector <= 0)
1854             continue;
1855         XSECTOR *pXSector = &xsector[nXSector];
1856         if (pXSector->bobSpeed != 0)
1857         {
1858             if (pXSector->bobAlways)
1859                 pXSector->bobTheta += pXSector->bobSpeed;
1860             else if (pXSector->busy == 0)
1861                 continue;
1862             else
1863                 pXSector->bobTheta += mulscale16(pXSector->bobSpeed, pXSector->busy);
1864             int vdi = mulscale30(Sin(pXSector->bobTheta), pXSector->bobZRange<<8);
1865             for (int nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
1866             {
1867                 spritetype *pSprite = &sprite[nSprite];
1868                 if (pSprite->cstat&24576)
1869                 {
1870                     viewBackupSpriteLoc(nSprite, pSprite);
1871                     pSprite->z += vdi;
1872                 }
1873             }
1874             if (pXSector->bobFloor)
1875             {
1876                 int floorZ = pSector->floorz;
1877                 viewInterpolateSector(nSector, pSector);
1878                 pSector->floorz = baseFloor[nSector]+vdi;
1879                 for (int nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
1880                 {
1881                     spritetype *pSprite = &sprite[nSprite];
1882                     if (pSprite->flags&2)
1883                         pSprite->flags |= 4;
1884                     else
1885                     {
1886                         int top, bottom;
1887                         GetSpriteExtents(pSprite, &top, &bottom);
1888                         if (bottom >= floorZ && (pSprite->cstat&48) == 0)
1889                         {
1890                             viewBackupSpriteLoc(nSprite, pSprite);
1891                             pSprite->z += vdi;
1892                         }
1893                     }
1894                 }
1895             }
1896             if (pXSector->bobCeiling)
1897             {
1898                 int ceilZ = pSector->ceilingz;
1899                 viewInterpolateSector(nSector, pSector);
1900                 pSector->ceilingz = baseCeil[nSector]+vdi;
1901                 for (int nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
1902                 {
1903                     spritetype *pSprite = &sprite[nSprite];
1904                     int top, bottom;
1905                     GetSpriteExtents(pSprite, &top, &bottom);
1906                     if (top <= ceilZ && (pSprite->cstat&48) == 0)
1907                     {
1908                         viewBackupSpriteLoc(nSprite, pSprite);
1909                         pSprite->z += vdi;
1910                     }
1911                 }
1912             }
1913         }
1914     }
1915 }
1916 
AlignSlopes(void)1917 void AlignSlopes(void)
1918 {
1919     sectortype *pSector;
1920     int nSector;
1921     for (pSector = sector, nSector = 0; nSector < numsectors; nSector++, pSector++)
1922     {
1923         if (qsector_filler[nSector])
1924         {
1925             walltype *pWall = &wall[pSector->wallptr+qsector_filler[nSector]];
1926             walltype *pWall2 = &wall[pWall->point2];
1927             int nNextSector = pWall->nextsector;
1928             if (nNextSector >= 0)
1929             {
1930                 int x = (pWall->x+pWall2->x)/2;
1931                 int y = (pWall->y+pWall2->y)/2;
1932                 viewInterpolateSector(nSector, pSector);
1933                 alignflorslope(nSector, x, y, getflorzofslope(nNextSector, x, y));
1934                 alignceilslope(nSector, x, y, getceilzofslope(nNextSector, x, y));
1935             }
1936         }
1937     }
1938 }
1939 
1940 int(*gBusyProc[])(unsigned int, unsigned int) =
1941 {
1942     VCrushBusy,
1943     VSpriteBusy,
1944     VDoorBusy,
1945     HDoorBusy,
1946     RDoorBusy,
1947     StepRotateBusy,
1948     GenSectorBusy,
1949     PathBusy
1950 };
1951 
trProcessBusy(void)1952 void trProcessBusy(void)
1953 {
1954     memset(velFloor, 0, sizeof(velFloor));
1955     memset(velCeil, 0, sizeof(velCeil));
1956     for (int i = gBusyCount-1; i >= 0; i--)
1957     {
1958         int nStatus;
1959         int oldBusy = gBusy[i].at8;
1960         gBusy[i].at8 = ClipRange(oldBusy+gBusy[i].at4*4, 0, 65536);
1961         #ifdef NOONE_EXTENSIONS
1962             if (!gModernMap || !xsector[sector[gBusy[i].at0].extra].unused1) nStatus = gBusyProc[gBusy[i].atc](gBusy[i].at0, gBusy[i].at8);
1963             else nStatus = 3; // allow to pause/continue motion for sectors any time by sending special command
1964         #else
1965             nStatus = gBusyProc[gBusy[i].atc](gBusy[i].at0, gBusy[i].at8);
1966         #endif
1967         switch (nStatus) {
1968             case 1:
1969                 gBusy[i].at8 = oldBusy;
1970                 break;
1971             case 2:
1972                 gBusy[i].at8 = oldBusy;
1973                 gBusy[i].at4 = -gBusy[i].at4;
1974                 break;
1975             case 3:
1976                 gBusy[i] = gBusy[--gBusyCount];
1977                 break;
1978         }
1979     }
1980     ProcessMotion();
1981     AlignSlopes();
1982 }
1983 
1984 void InitGenerator(int);
1985 
trInit(void)1986 void trInit(void)
1987 {
1988     gBusyCount = 0;
1989     for (int i = 0; i < numwalls; i++)
1990     {
1991         baseWall[i].x = wall[i].x;
1992         baseWall[i].y = wall[i].y;
1993     }
1994     for (int i = 0; i < kMaxSprites; i++)
1995     {
1996         if (sprite[i].statnum < kStatFree)
1997         {
1998             sprite[i].inittype = sprite[i].type;
1999             baseSprite[i].x = sprite[i].x;
2000             baseSprite[i].y = sprite[i].y;
2001             baseSprite[i].z = sprite[i].z;
2002         }
2003         else
2004             sprite[i].inittype = -1;
2005     }
2006     for (int i = 0; i < numwalls; i++)
2007     {
2008         int nXWall = wall[i].extra;
2009         dassert(nXWall < kMaxXWalls);
2010         if (nXWall > 0)
2011         {
2012             XWALL *pXWall = &xwall[nXWall];
2013             if (pXWall->state)
2014                 pXWall->busy = 65536;
2015         }
2016     }
2017     dassert((numsectors >= 0) && (numsectors < kMaxSectors));
2018     for (int i = 0; i < numsectors; i++)
2019     {
2020         sectortype *pSector = &sector[i];
2021         baseFloor[i] = pSector->floorz;
2022         baseCeil[i] = pSector->ceilingz;
2023         int nXSector = pSector->extra;
2024         if (nXSector > 0)
2025         {
2026             dassert(nXSector < kMaxXSectors);
2027             XSECTOR *pXSector = &xsector[nXSector];
2028             if (pXSector->state)
2029                 pXSector->busy = 65536;
2030             switch (pSector->type)
2031             {
2032             case kSectorCounter:
2033                 #ifdef NOONE_EXTENSIONS
2034                 if (gModernMap)
2035                     pXSector->triggerOff = false;
2036                 else
2037                 #endif
2038                 pXSector->triggerOnce = 1;
2039                 evPost(i, 6, 0, kCallbackCounterCheck);
2040                 break;
2041             case kSectorZMotion:
2042             case kSectorZMotionSprite:
2043                 ZTranslateSector(i, pXSector, pXSector->busy, 1);
2044                 break;
2045             case kSectorSlideMarked:
2046             case kSectorSlide:
2047             {
2048                 spritetype *pSprite1 = &sprite[pXSector->marker0];
2049                 spritetype *pSprite2 = &sprite[pXSector->marker1];
2050                 TranslateSector(i, 0, -65536, pSprite1->x, pSprite1->y, pSprite1->x, pSprite1->y, pSprite1->ang, pSprite2->x, pSprite2->y, pSprite2->ang, pSector->type == kSectorSlide);
2051                 for (int j = 0; j < pSector->wallnum; j++)
2052                 {
2053                     baseWall[pSector->wallptr+j].x = wall[pSector->wallptr+j].x;
2054                     baseWall[pSector->wallptr+j].y = wall[pSector->wallptr+j].y;
2055                 }
2056                 for (int nSprite = headspritesect[i]; nSprite >= 0; nSprite = nextspritesect[nSprite])
2057                 {
2058                     baseSprite[nSprite].x = sprite[nSprite].x;
2059                     baseSprite[nSprite].y = sprite[nSprite].y;
2060                     baseSprite[nSprite].z = sprite[nSprite].z;
2061                 }
2062                 TranslateSector(i, 0, pXSector->busy, pSprite1->x, pSprite1->y, pSprite1->x, pSprite1->y, pSprite1->ang, pSprite2->x, pSprite2->y, pSprite2->ang, pSector->type == kSectorSlide);
2063                 ZTranslateSector(i, pXSector, pXSector->busy, 1);
2064                 break;
2065             }
2066             case kSectorRotateMarked:
2067             case kSectorRotate:
2068             {
2069                 spritetype *pSprite1 = &sprite[pXSector->marker0];
2070                 TranslateSector(i, 0, -65536, pSprite1->x, pSprite1->y, pSprite1->x, pSprite1->y, 0, pSprite1->x, pSprite1->y, pSprite1->ang, pSector->type == kSectorRotate);
2071                 for (int j = 0; j < pSector->wallnum; j++)
2072                 {
2073                     baseWall[pSector->wallptr+j].x = wall[pSector->wallptr+j].x;
2074                     baseWall[pSector->wallptr+j].y = wall[pSector->wallptr+j].y;
2075                 }
2076                 for (int nSprite = headspritesect[i]; nSprite >= 0; nSprite = nextspritesect[nSprite])
2077                 {
2078                     baseSprite[nSprite].x = sprite[nSprite].x;
2079                     baseSprite[nSprite].y = sprite[nSprite].y;
2080                     baseSprite[nSprite].z = sprite[nSprite].z;
2081                 }
2082                 TranslateSector(i, 0, pXSector->busy, pSprite1->x, pSprite1->y, pSprite1->x, pSprite1->y, 0, pSprite1->x, pSprite1->y, pSprite1->ang, pSector->type == kSectorRotate);
2083                 ZTranslateSector(i, pXSector, pXSector->busy, 1);
2084                 break;
2085             }
2086             case kSectorPath:
2087                 InitPath(i, pXSector);
2088                 break;
2089             default:
2090                 break;
2091             }
2092         }
2093     }
2094     for (int i = 0; i < kMaxSprites; i++)
2095     {
2096         int nXSprite = sprite[i].extra;
2097         if (sprite[i].statnum < kStatFree && nXSprite > 0)
2098         {
2099             dassert(nXSprite < kMaxXSprites);
2100             XSPRITE *pXSprite = &xsprite[nXSprite];
2101             if (pXSprite->state)
2102                 pXSprite->busy = 65536;
2103             switch (sprite[i].type) {
2104             case kSwitchPadlock:
2105                 pXSprite->triggerOnce = 1;
2106                 break;
2107             #ifdef NOONE_EXTENSIONS
2108             case kModernRandom:
2109             case kModernRandom2:
2110                 if (!gModernMap || pXSprite->state == pXSprite->restState) break;
2111                 evPost(i, 3, (120 * pXSprite->busyTime) / 10, kCmdRepeat);
2112                 if (pXSprite->waitTime > 0)
2113                     evPost(i, 3, (pXSprite->waitTime * 120) / 10, pXSprite->restState ? kCmdOn : kCmdOff);
2114                 break;
2115             case kModernSeqSpawner:
2116             case kModernObjDataAccumulator:
2117             case kModernDudeTargetChanger:
2118             case kModernEffectSpawner:
2119             case kModernWindGenerator:
2120                 if (pXSprite->state == pXSprite->restState) break;
2121                 evPost(i, 3, 0, kCmdRepeat);
2122                 if (pXSprite->waitTime > 0)
2123                     evPost(i, 3, (pXSprite->waitTime * 120) / 10, pXSprite->restState ? kCmdOn : kCmdOff);
2124                 break;
2125             #endif
2126             case kGenTrigger:
2127             case kGenDripWater:
2128             case kGenDripBlood:
2129             case kGenMissileFireball:
2130             case kGenDart:
2131             case kGenBubble:
2132             case kGenBubbleMulti:
2133             case kGenMissileEctoSkull:
2134             case kGenSound:
2135                 InitGenerator(i);
2136                 break;
2137             case kThingArmedProxBomb:
2138                 pXSprite->Proximity = 1;
2139                 break;
2140             case kThingFallingRock:
2141                 if (pXSprite->state) sprite[i].flags |= 7;
2142                 else sprite[i].flags &= ~7;
2143                 break;
2144             }
2145             if (pXSprite->Vector) sprite[i].cstat |= CSTAT_SPRITE_BLOCK_HITSCAN;
2146             if (pXSprite->Push) sprite[i].cstat |= 4096;
2147         }
2148     }
2149 
2150     evSend(0, 0, kChannelLevelStart, kCmdOn);
2151     switch (gGameOptions.nGameType) {
2152         case 1:
2153             evSend(0, 0, kChannelLevelStartCoop, kCmdOn);
2154             break;
2155         case 2:
2156             evSend(0, 0, kChannelLevelStartMatch, kCmdOn);
2157             break;
2158         case 3:
2159             evSend(0, 0, kChannelLevelStartMatch, kCmdOn);
2160             evSend(0, 0, kChannelLevelStartTeamsOnly, kCmdOn);
2161             break;
2162     }
2163 }
2164 
trTextOver(int nId)2165 void trTextOver(int nId)
2166 {
2167     char *pzMessage = levelGetMessage(nId);
2168     if (pzMessage)
2169         viewSetMessage(pzMessage, VanillaMode() ? 0 : 8, MESSAGE_PRIORITY_INI); // 8: gold
2170 }
2171 
InitGenerator(int nSprite)2172 void InitGenerator(int nSprite)
2173 {
2174     dassert(nSprite < kMaxSprites);
2175     spritetype *pSprite = &sprite[nSprite];
2176     dassert(pSprite->statnum != kMaxStatus);
2177     int nXSprite = pSprite->extra;
2178     dassert(nXSprite > 0);
2179     XSPRITE *pXSprite = &xsprite[nXSprite];
2180     switch (sprite[nSprite].type) {
2181         case kGenTrigger:
2182             pSprite->cstat &= ~CSTAT_SPRITE_BLOCK;
2183             pSprite->cstat |= CSTAT_SPRITE_INVISIBLE;
2184             break;
2185     }
2186     if (pXSprite->state != pXSprite->restState && pXSprite->busyTime > 0)
2187         evPost(nSprite, 3, (120*(pXSprite->busyTime+Random2(pXSprite->data1)))/10, kCmdRepeat);
2188 }
2189 
ActivateGenerator(int nSprite)2190 void ActivateGenerator(int nSprite)
2191 {
2192     dassert(nSprite < kMaxSprites);
2193     spritetype *pSprite = &sprite[nSprite];
2194     dassert(pSprite->statnum != kMaxStatus);
2195     int nXSprite = pSprite->extra;
2196     dassert(nXSprite > 0);
2197     XSPRITE *pXSprite = &xsprite[nXSprite];
2198     switch (pSprite->type) {
2199         case kGenDripWater:
2200         case kGenDripBlood: {
2201             int top, bottom;
2202             GetSpriteExtents(pSprite, &top, &bottom);
2203             actSpawnThing(pSprite->sectnum, pSprite->x, pSprite->y, bottom, (pSprite->type == kGenDripWater) ? kThingDripWater : kThingDripBlood);
2204             break;
2205         }
2206         case kGenSound:
2207             sfxPlay3DSound(pSprite, pXSprite->data2, -1, 0);
2208             break;
2209         case kGenMissileFireball:
2210             switch (pXSprite->data2) {
2211                 case 0:
2212                     FireballTrapSeqCallback(3, nXSprite);
2213                     break;
2214                 case 1:
2215                     seqSpawn(35, 3, nXSprite, nFireballTrapClient);
2216                     break;
2217                 case 2:
2218                     seqSpawn(36, 3, nXSprite, nFireballTrapClient);
2219                     break;
2220             }
2221             break;
2222         case kGenMissileEctoSkull:
2223             break;
2224         case kGenBubble:
2225         case kGenBubbleMulti: {
2226             int top, bottom;
2227             GetSpriteExtents(pSprite, &top, &bottom);
2228             gFX.fxSpawn((pSprite->type == kGenBubble) ? FX_23 : FX_26, pSprite->sectnum, pSprite->x, pSprite->y, top, 0);
2229             break;
2230         }
2231     }
2232 }
2233 
FireballTrapSeqCallback(int,int nXSprite)2234 void FireballTrapSeqCallback(int, int nXSprite)
2235 {
2236     XSPRITE *pXSprite = &xsprite[nXSprite];
2237     int nSprite = pXSprite->reference;
2238     spritetype *pSprite = &sprite[nSprite];
2239     if (pSprite->cstat&32)
2240         actFireMissile(pSprite, 0, 0, 0, 0, (pSprite->cstat&8) ? 0x4000 : -0x4000, kMissileFireball);
2241     else
2242         actFireMissile(pSprite, 0, 0, Cos(pSprite->ang)>>16, Sin(pSprite->ang)>>16, 0, kMissileFireball);
2243 }
2244 
2245 
MGunFireSeqCallback(int,int nXSprite)2246 void MGunFireSeqCallback(int, int nXSprite)
2247 {
2248     int nSprite = xsprite[nXSprite].reference;
2249     spritetype *pSprite = &sprite[nSprite];
2250     XSPRITE *pXSprite = &xsprite[nXSprite];
2251     if (pXSprite->data2 > 0 || pXSprite->data1 == 0)
2252     {
2253         if (pXSprite->data2 > 0)
2254         {
2255             pXSprite->data2--;
2256             if (pXSprite->data2 == 0)
2257                 evPost(nSprite, 3, 1, kCmdOff);
2258         }
2259         int dx = (Cos(pSprite->ang)>>16)+Random2(1000);
2260         int dy = (Sin(pSprite->ang)>>16)+Random2(1000);
2261         int dz = Random2(1000);
2262         actFireVector(pSprite, 0, 0, dx, dy, dz, kVectorBullet);
2263         sfxPlay3DSound(pSprite, 359, -1, 0);
2264     }
2265 }
2266 
MGunOpenSeqCallback(int,int nXSprite)2267 void MGunOpenSeqCallback(int, int nXSprite)
2268 {
2269     seqSpawn(39, 3, nXSprite, nMGunFireClient);
2270 }
2271 
2272 class TriggersLoadSave : public LoadSave
2273 {
2274 public:
2275     virtual void Load();
2276     virtual void Save();
2277 };
2278 
Load()2279 void TriggersLoadSave::Load()
2280 {
2281     Read(&gBusyCount, sizeof(gBusyCount));
2282     Read(gBusy, sizeof(gBusy));
2283     Read(basePath, sizeof(basePath));
2284 }
2285 
Save()2286 void TriggersLoadSave::Save()
2287 {
2288     Write(&gBusyCount, sizeof(gBusyCount));
2289     Write(gBusy, sizeof(gBusy));
2290     Write(basePath, sizeof(basePath));
2291 }
2292 
2293 static TriggersLoadSave *myLoadSave;
2294 
TriggersLoadSaveConstruct(void)2295 void TriggersLoadSaveConstruct(void)
2296 {
2297     myLoadSave = new TriggersLoadSave();
2298 }
2299