seq.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 <string.h> 24 #include "build.h" 25 #include "common_game.h" 26 27 #include "blood.h" 28 #include "db.h" 29 #include "eventq.h" 30 #include "globals.h" 31 #include "levels.h" 32 #include "loadsave.h" 33 #include "sfx.h" 34 #include "sound.h" 35 #include "seq.h" 36 #include "gameutil.h" 37 #include "actor.h" 38 #include "tile.h" 39 #include "view.h" 40 41 #define kMaxClients 256 42 #define kMaxSequences (1024*4) 43 #define kMaxSequencesVanilla 1024 44 #define kSurfSoundBase 800 45 46 static ACTIVE activeList[kMaxSequences]; 47 static int activeCount = 0; 48 static char bActiveListWarnVanilla = 0; 49 static int nClients = 0; 50 static void(*clientCallback[kMaxClients])(int, int); 51 52 int seqRegisterClient(void(*pClient)(int, int)) 53 { 54 dassert(nClients < kMaxClients); 55 clientCallback[nClients] = pClient; 56 return nClients++; 57 } 58 59 void Seq::Preload(void) 60 { 61 if (memcmp(signature, "SEQ\x1a", 4) != 0) 62 ThrowError("Invalid sequence"); 63 if ((version & 0xff00) != 0x300) 64 ThrowError("Obsolete sequence version"); 65 for (int i = 0; i < nFrames; i++) 66 tilePreloadTile(seqGetTile(&frames[i])); 67 } 68 69 void Seq::Precache(void) 70 { 71 if (memcmp(signature, "SEQ\x1a", 4) != 0) 72 ThrowError("Invalid sequence"); 73 if ((version & 0xff00) != 0x300) 74 ThrowError("Obsolete sequence version"); 75 for (int i = 0; i < nFrames; i++) 76 tilePrecacheTile(seqGetTile(&frames[i])); 77 } 78 79 void seqPrecacheId(int id) 80 { 81 DICTNODE *hSeq = gSysRes.Lookup(id, "SEQ"); 82 if (!hSeq) 83 return; 84 Seq *pSeq = (Seq*)gSysRes.Lock(hSeq); 85 pSeq->Precache(); 86 gSysRes.Unlock(hSeq); 87 } 88 89 SEQINST siWall[kMaxXWalls]; 90 SEQINST siCeiling[kMaxXSectors]; 91 SEQINST siFloor[kMaxXSectors]; 92 SEQINST siSprite[kMaxXSprites]; 93 SEQINST siMasked[kMaxXWalls]; 94 95 void UpdateSprite(int nXSprite, SEQFRAME *pFrame) 96 { 97 dassert(nXSprite > 0 && nXSprite < kMaxXSprites); 98 int nSprite = xsprite[nXSprite].reference; 99 dassert(nSprite >= 0 && nSprite < kMaxSprites); 100 spritetype *pSprite = &sprite[nSprite]; 101 dassert(pSprite->extra == nXSprite); 102 103 int nScale = xsprite[nXSprite].scale; // SEQ size scaling 104 105 if (pSprite->flags & kPhysGravity) 106 { 107 if (tilesiz[pSprite->picnum].y != tilesiz[seqGetTile(pFrame)].y || picanm[pSprite->picnum].yofs != picanm[seqGetTile(pFrame)].yofs 108 || (pFrame->yrepeat && pFrame->yrepeat != pSprite->yrepeat)) 109 pSprite->flags |= kPhysFalling; 110 } 111 112 pSprite->picnum = seqGetTile(pFrame); 113 114 if (pFrame->pal) 115 pSprite->pal = seqGetPal(pFrame); 116 117 pSprite->shade = pFrame->shade; 118 119 if (pFrame->xrepeat) 120 { 121 if (nScale) pSprite->xrepeat = ClipRange(mulscale8(pFrame->xrepeat, nScale), 0, 255); 122 else pSprite->xrepeat = pFrame->xrepeat; 123 } 124 125 if (pFrame->yrepeat) 126 { 127 if (nScale) pSprite->yrepeat = ClipRange(mulscale8(pFrame->yrepeat, nScale), 0, 255); 128 else pSprite->yrepeat = pFrame->yrepeat; 129 } 130 131 if (pFrame->transparent) 132 pSprite->cstat |= CSTAT_SPRITE_TRANSLUCENT; 133 else 134 pSprite->cstat &= ~CSTAT_SPRITE_TRANSLUCENT; 135 136 if (pFrame->transparent2) 137 pSprite->cstat |= CSTAT_SPRITE_TRANSLUCENT_INVERT; 138 else 139 pSprite->cstat &= ~CSTAT_SPRITE_TRANSLUCENT_INVERT; 140 141 if (pFrame->blockable) 142 pSprite->cstat |= CSTAT_SPRITE_BLOCK; 143 else 144 pSprite->cstat &= ~CSTAT_SPRITE_BLOCK; 145 146 if (pFrame->hittable) 147 pSprite->cstat |= CSTAT_SPRITE_BLOCK_HITSCAN; 148 else 149 pSprite->cstat &= ~CSTAT_SPRITE_BLOCK_HITSCAN; 150 151 if (pFrame->invisible) 152 pSprite->cstat |= CSTAT_SPRITE_INVISIBLE; 153 else 154 pSprite->cstat &= (unsigned short)~CSTAT_SPRITE_INVISIBLE; 155 156 if (pFrame->pushable) 157 pSprite->cstat |= CSTAT_SPRITE_RESERVED3; 158 else 159 pSprite->cstat &= ~CSTAT_SPRITE_RESERVED3; 160 161 if (pFrame->smoke) 162 pSprite->flags |= kHitagSmoke; 163 else 164 pSprite->flags &= ~kHitagSmoke; 165 166 if (pFrame->autoaim) 167 pSprite->flags |= kHitagAutoAim; 168 else 169 pSprite->flags &= ~kHitagAutoAim; 170 171 if (pFrame->xflip) 172 pSprite->flags |= kHitagFlipX; 173 else 174 pSprite->flags &= ~kHitagFlipX; 175 176 if (pFrame->yflip) 177 pSprite->flags |= kHitagFlipY; 178 else 179 pSprite->flags &= ~kHitagFlipY; 180 181 } 182 183 void UpdateWall(int nXWall, SEQFRAME *pFrame) 184 { 185 dassert(nXWall > 0 && nXWall < kMaxXWalls); 186 int nWall = xwall[nXWall].reference; 187 dassert(nWall >= 0 && nWall < kMaxWalls); 188 walltype *pWall = &wall[nWall]; 189 dassert(pWall->extra == nXWall); 190 191 pWall->picnum = seqGetTile(pFrame); 192 193 if (pFrame->pal) 194 pWall->pal = seqGetPal(pFrame); 195 196 if (pFrame->transparent) 197 pWall->cstat |= CSTAT_WALL_TRANSLUCENT; 198 else 199 pWall->cstat &= ~CSTAT_WALL_TRANSLUCENT; 200 201 if (pFrame->transparent2) 202 pWall->cstat |= CSTAT_WALL_TRANS_FLIP; 203 else 204 pWall->cstat &= ~CSTAT_WALL_TRANS_FLIP; 205 206 if (pFrame->blockable) 207 pWall->cstat |= CSTAT_WALL_BLOCK; 208 else 209 pWall->cstat &= ~CSTAT_WALL_BLOCK; 210 211 if (pFrame->hittable) 212 pWall->cstat |= CSTAT_WALL_BLOCK_HITSCAN; 213 else 214 pWall->cstat &= ~CSTAT_WALL_BLOCK_HITSCAN; 215 } 216 217 void UpdateMasked(int nXWall, SEQFRAME *pFrame) 218 { 219 dassert(nXWall > 0 && nXWall < kMaxXWalls); 220 int nWall = xwall[nXWall].reference; 221 dassert(nWall >= 0 && nWall < kMaxWalls); 222 walltype *pWall = &wall[nWall]; 223 dassert(pWall->extra == nXWall); 224 dassert(pWall->nextwall >= 0); 225 walltype *pWallNext = &wall[pWall->nextwall]; 226 227 pWall->overpicnum = pWallNext->overpicnum = seqGetTile(pFrame); 228 229 if (pFrame->pal) 230 pWall->pal = pWallNext->pal = seqGetPal(pFrame); 231 232 if (pFrame->transparent) 233 { 234 pWall->cstat |= CSTAT_WALL_TRANSLUCENT; 235 pWallNext->cstat |= CSTAT_WALL_TRANSLUCENT; 236 } 237 else 238 { 239 pWall->cstat &= ~CSTAT_WALL_TRANSLUCENT; 240 pWallNext->cstat &= ~CSTAT_WALL_TRANSLUCENT; 241 } 242 243 if (pFrame->transparent2) 244 { 245 pWall->cstat |= CSTAT_WALL_TRANS_FLIP; 246 pWallNext->cstat |= CSTAT_WALL_TRANS_FLIP; 247 } 248 else 249 { 250 pWall->cstat &= ~CSTAT_WALL_TRANS_FLIP; 251 pWallNext->cstat &= ~CSTAT_WALL_TRANS_FLIP; 252 } 253 254 if (pFrame->blockable) 255 { 256 pWall->cstat |= CSTAT_WALL_BLOCK; 257 pWallNext->cstat |= CSTAT_WALL_BLOCK; 258 } 259 else 260 { 261 pWall->cstat &= ~CSTAT_WALL_BLOCK; 262 pWallNext->cstat &= ~CSTAT_WALL_BLOCK; 263 } 264 265 if (pFrame->hittable) 266 { 267 pWall->cstat |= CSTAT_WALL_BLOCK_HITSCAN; 268 pWallNext->cstat |= CSTAT_WALL_BLOCK_HITSCAN; 269 } 270 else 271 { 272 pWall->cstat &= ~CSTAT_WALL_BLOCK_HITSCAN; 273 pWallNext->cstat &= ~CSTAT_WALL_BLOCK_HITSCAN; 274 } 275 } 276 277 void UpdateFloor(int nXSector, SEQFRAME *pFrame) 278 { 279 dassert(nXSector > 0 && nXSector < kMaxXSectors); 280 int nSector = xsector[nXSector].reference; 281 dassert(nSector >= 0 && nSector < kMaxSectors); 282 sectortype *pSector = §or[nSector]; 283 dassert(pSector->extra == nXSector); 284 285 pSector->floorpicnum = seqGetTile(pFrame); 286 287 pSector->floorshade = pFrame->shade; 288 289 if (pFrame->pal) 290 pSector->floorpal = seqGetPal(pFrame); 291 } 292 293 void UpdateCeiling(int nXSector, SEQFRAME *pFrame) 294 { 295 dassert(nXSector > 0 && nXSector < kMaxXSectors); 296 int nSector = xsector[nXSector].reference; 297 dassert(nSector >= 0 && nSector < kMaxSectors); 298 sectortype *pSector = §or[nSector]; 299 dassert(pSector->extra == nXSector); 300 301 pSector->ceilingpicnum = seqGetTile(pFrame); 302 303 pSector->ceilingshade = pFrame->shade; 304 305 if (pFrame->pal) 306 pSector->ceilingpal = seqGetPal(pFrame); 307 } 308 309 void SEQINST::Update(ACTIVE *pActive) 310 { 311 dassert(frameIndex < pSequence->nFrames); 312 SEQFRAME* pFrame = &pSequence->frames[frameIndex]; 313 314 switch (pActive->type) 315 { 316 case 0: 317 UpdateWall(pActive->xindex, pFrame); 318 break; 319 case 1: 320 UpdateCeiling(pActive->xindex , pFrame); 321 break; 322 case 2: 323 UpdateFloor(pActive->xindex, pFrame); 324 break; 325 case 3: 326 { 327 UpdateSprite(pActive->xindex, pFrame); 328 spritetype* pSpr = &sprite[xsprite[pActive->xindex].reference]; 329 int nSnd, nSurf; 330 331 #ifdef NOONE_EXTENSIONS 332 if (pFrame->playSound) 333 { 334 nSnd = pSequence->nSoundID; 335 336 // add random sound range feature 337 if (!VanillaMode() && pFrame->soundRange > 0) 338 nSnd += Random((pFrame->soundRange == 1) ? 2 : pFrame->soundRange); 339 340 sfxPlay3DSound(pSpr, nSnd, -1, 0); 341 } 342 343 // add surfaceSound trigger feature 344 if (!VanillaMode() && pFrame->surfaceSound && zvel[pSpr->index] == 0 && approxDist(xvel[pSpr->index], yvel[pSpr->index])) 345 { 346 if (pSpr->sectnum >= 0 && gUpperLink[pSpr->sectnum] < 0 && (nSurf = tileGetSurfType(pSpr->sectnum, 0x4000)) > 0) 347 { 348 DICTNODE* hRes; 349 350 nSnd = kSurfSoundBase + ((nSurf << 1) + Random(2)); 351 if ((hRes = gSoundRes.Lookup(nSnd, "SFX")) != NULL) 352 { 353 SFX* pSFX = (SFX*)gSoundRes.Load(hRes); 354 sfxPlay3DSoundCP(pSpr, nSnd, -1, 0, 0, (pSFX->relVol != 80) ? pSFX->relVol : 25); 355 } 356 } 357 } 358 #else 359 if (pFrame->playSound) 360 sfxPlay3DSound(pSpr, pSequence->nSoundID, -1, 0); 361 #endif 362 break; 363 } 364 case 4: 365 UpdateMasked(pActive->xindex, pFrame); 366 break; 367 } 368 if (pFrame->trigger && nCallbackID != -1) 369 clientCallback[nCallbackID](pActive->type, pActive->xindex); 370 } 371 372 SEQINST * GetInstance(int nType, int nXIndex) 373 { 374 switch (nType) 375 { 376 case 0: 377 if (nXIndex > 0 && nXIndex < kMaxXWalls) return &siWall[nXIndex]; 378 break; 379 case 1: 380 if (nXIndex > 0 && nXIndex < kMaxXSectors) return &siCeiling[nXIndex]; 381 break; 382 case 2: 383 if (nXIndex > 0 && nXIndex < kMaxXSectors) return &siFloor[nXIndex]; 384 break; 385 case 3: 386 if (nXIndex > 0 && nXIndex < kMaxXSprites) return &siSprite[nXIndex]; 387 break; 388 case 4: 389 if (nXIndex > 0 && nXIndex < kMaxWalls) return &siMasked[nXIndex]; 390 break; 391 } 392 return NULL; 393 } 394 395 void UnlockInstance(SEQINST *pInst) 396 { 397 dassert(pInst != NULL); 398 dassert(pInst->hSeq != NULL); 399 dassert(pInst->pSequence != NULL); 400 gSysRes.Unlock(pInst->hSeq); 401 pInst->hSeq = NULL; 402 pInst->pSequence = NULL; 403 pInst->isPlaying = 0; 404 } 405 406 void seqSpawn(int nSeq, int nType, int nXIndex, int nCallbackID) 407 { 408 SEQINST *pInst = GetInstance(nType, nXIndex); 409 if (!pInst) return; 410 411 DICTNODE *hSeq = gSysRes.Lookup(nSeq, "SEQ"); 412 if (!hSeq) 413 ThrowError("Missing sequence #%d", nSeq); 414 415 int i = activeCount; 416 if (pInst->isPlaying) 417 { 418 if (hSeq == pInst->hSeq) 419 return; 420 UnlockInstance(pInst); 421 for (i = 0; i < activeCount; i++) 422 { 423 if (activeList[i].type == nType && activeList[i].xindex == nXIndex) 424 break; 425 } 426 dassert(i < activeCount); 427 } 428 Seq *pSeq = (Seq*)gSysRes.Lock(hSeq); 429 if (memcmp(pSeq->signature, "SEQ\x1a", 4) != 0) 430 ThrowError("Invalid sequence %d", nSeq); 431 if ((pSeq->version & 0xff00) != 0x300) 432 ThrowError("Sequence %d is obsolete version", nSeq); 433 if ((pSeq->version & 0xff) == 0x00) 434 { 435 for (int i = 0; i < pSeq->nFrames; i++) 436 { 437 pSeq->frames[i].tile2 = 0; 438 #ifdef NOONE_EXTENSIONS 439 pSeq->frames[i].pal2 = 0; 440 #endif 441 } 442 } 443 pInst->isPlaying = 1; 444 pInst->hSeq = hSeq; 445 pInst->pSequence = pSeq; 446 pInst->nSeq = nSeq; 447 pInst->nCallbackID = nCallbackID; 448 pInst->timeCount = pSeq->ticksPerFrame; 449 pInst->frameIndex = 0; 450 if (i == activeCount) 451 { 452 dassert(activeCount < kMaxSequences); 453 if (!bActiveListWarnVanilla && (activeCount >= kMaxSequencesVanilla) && VanillaMode()) 454 { 455 OSD_Printf("Warning: Active SEQ list over vanilla limit (%d/%d)\n", activeCount, kMaxSequencesVanilla); 456 bActiveListWarnVanilla = 1; 457 } 458 activeList[activeCount].type = nType; 459 activeList[activeCount].xindex = nXIndex; 460 activeCount++; 461 } 462 pInst->Update(&activeList[i]); 463 } 464 465 void seqKill(int nType, int nXIndex) 466 { 467 SEQINST *pInst = GetInstance(nType, nXIndex); 468 if (!pInst || !pInst->isPlaying) 469 return; 470 int i; 471 for (i = 0; i < activeCount; i++) 472 { 473 if (activeList[i].type == nType && activeList[i].xindex == nXIndex) 474 break; 475 } 476 dassert(i < activeCount); 477 activeCount--; 478 activeList[i] = activeList[activeCount]; 479 pInst->isPlaying = 0; 480 UnlockInstance(pInst); 481 } 482 483 void seqKillAll(void) 484 { 485 for (int i = 0; i < kMaxXWalls; i++) 486 { 487 if (siWall[i].isPlaying) 488 UnlockInstance(&siWall[i]); 489 if (siMasked[i].isPlaying) 490 UnlockInstance(&siMasked[i]); 491 } 492 for (int i = 0; i < kMaxXSectors; i++) 493 { 494 if (siCeiling[i].isPlaying) 495 UnlockInstance(&siCeiling[i]); 496 if (siFloor[i].isPlaying) 497 UnlockInstance(&siFloor[i]); 498 } 499 for (int i = 0; i < kMaxXSprites; i++) 500 { 501 if (siSprite[i].isPlaying) 502 UnlockInstance(&siSprite[i]); 503 } 504 activeCount = 0; 505 bActiveListWarnVanilla = 0; 506 } 507 508 int seqGetStatus(int nType, int nXIndex) 509 { 510 SEQINST *pInst = GetInstance(nType, nXIndex); 511 if (pInst && pInst->isPlaying) 512 return pInst->frameIndex; 513 return -1; 514 } 515 516 int seqGetID(int nType, int nXIndex) 517 { 518 SEQINST *pInst = GetInstance(nType, nXIndex); 519 if (pInst) 520 return pInst->nSeq; 521 return -1; 522 } 523 524 void seqProcess(int nTicks) 525 { 526 for (int i = 0; i < activeCount; i++) 527 { 528 SEQINST *pInst = GetInstance(activeList[i].type, activeList[i].xindex); 529 Seq *pSeq = pInst->pSequence; 530 dassert(pInst->frameIndex < pSeq->nFrames); 531 pInst->timeCount -= nTicks; 532 while (pInst->timeCount < 0) 533 { 534 pInst->timeCount += pSeq->ticksPerFrame; 535 pInst->frameIndex++; 536 if (pInst->frameIndex == pSeq->nFrames) 537 { 538 if (pSeq->flags & 1) 539 pInst->frameIndex = 0; 540 else 541 { 542 UnlockInstance(pInst); 543 if (pSeq->flags & 2) 544 { 545 switch (activeList[i].type) 546 { 547 case 3: 548 { 549 int nXSprite = activeList[i].xindex; 550 int nSprite = xsprite[nXSprite].reference; 551 dassert(nSprite >= 0 && nSprite < kMaxSprites); 552 evKill(nSprite, 3); 553 if ((sprite[nSprite].flags & kHitagRespawn) && sprite[nSprite].inittype >= kDudeBase && sprite[nSprite].inittype < kDudeMax) 554 evPost(nSprite, 3, gGameOptions.nMonsterRespawnTime, kCallbackRespawn); 555 else if (VanillaMode() || sprite[nSprite].statnum < kMaxStatus) 556 DeleteSprite(nSprite); 557 break; 558 } 559 case 4: 560 { 561 int nXWall = activeList[i].xindex; 562 int nWall = xwall[nXWall].reference; 563 dassert(nWall >= 0 && nWall < kMaxWalls); 564 wall[nWall].cstat &= ~(8 + 16 + 32); 565 if (wall[nWall].nextwall != -1) 566 wall[wall[nWall].nextwall].cstat &= ~(8 + 16 + 32); 567 break; 568 } 569 } 570 } 571 activeList[i--] = activeList[--activeCount]; 572 break; 573 } 574 } 575 pInst->Update(&activeList[i]); 576 } 577 } 578 } 579 580 class SeqLoadSave : public LoadSave { 581 virtual void Load(void); 582 virtual void Save(void); 583 }; 584 585 void SeqLoadSave::Load(void) 586 { 587 Read(&siWall, sizeof(siWall)); 588 Read(&siMasked, sizeof(siMasked)); 589 Read(&siCeiling, sizeof(siCeiling)); 590 Read(&siFloor, sizeof(siFloor)); 591 Read(&siSprite, sizeof(siSprite)); 592 Read(&activeList, sizeof(activeList)); 593 Read(&activeCount, sizeof(activeCount)); 594 for (int i = 0; i < kMaxXWalls; i++) 595 { 596 siWall[i].hSeq = NULL; 597 siMasked[i].hSeq = NULL; 598 siWall[i].pSequence = NULL; 599 siMasked[i].pSequence = NULL; 600 } 601 for (int i = 0; i < kMaxXSectors; i++) 602 { 603 siCeiling[i].hSeq = NULL; 604 siFloor[i].hSeq = NULL; 605 siCeiling[i].pSequence = NULL; 606 siFloor[i].pSequence = NULL; 607 } 608 for (int i = 0; i < kMaxXSprites; i++) 609 { 610 siSprite[i].hSeq = NULL; 611 siSprite[i].pSequence = NULL; 612 } 613 for (int i = 0; i < activeCount; i++) 614 { 615 SEQINST *pInst = GetInstance(activeList[i].type, activeList[i].xindex); 616 if (pInst->isPlaying) 617 { 618 int nSeq = pInst->nSeq; 619 DICTNODE *hSeq = gSysRes.Lookup(nSeq, "SEQ"); 620 if (!hSeq) { 621 ThrowError("Missing sequence #%d", nSeq); 622 continue; 623 } 624 Seq *pSeq = (Seq*)gSysRes.Lock(hSeq); 625 if (memcmp(pSeq->signature, "SEQ\x1a", 4) != 0) 626 ThrowError("Invalid sequence %d", nSeq); 627 if ((pSeq->version & 0xff00) != 0x300) 628 ThrowError("Sequence %d is obsolete version", nSeq); 629 630 // Edited SEQ and loading old savegame... 631 if (pInst->frameIndex >= pSeq->nFrames) 632 pInst->frameIndex = pSeq->nFrames - 1; 633 634 pInst->hSeq = hSeq; 635 pInst->pSequence = pSeq; 636 } 637 } 638 } 639 640 void SeqLoadSave::Save(void) 641 { 642 Write(&siWall, sizeof(siWall)); 643 Write(&siMasked, sizeof(siMasked)); 644 Write(&siCeiling, sizeof(siCeiling)); 645 Write(&siFloor, sizeof(siFloor)); 646 Write(&siSprite, sizeof(siSprite)); 647 Write(&activeList, sizeof(activeList)); 648 Write(&activeCount, sizeof(activeCount)); 649 } 650 651 static SeqLoadSave *myLoadSave; 652 653 void SeqLoadSaveConstruct(void) 654 { 655 myLoadSave = new SeqLoadSave(); 656 }