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