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 = §or[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 = §or[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, §or[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 = §or[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 = §or[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 = §or[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 = §or[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 = §or[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 = §or[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 = §or[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 = §or[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