/******************************************************************************* FILE: SEQ.CPP DESCRIPTION: Sequence engine AUTHOR: Peter M. Freese CREATED: 06-23-95 COPYRIGHT: Copyright (c) 1995 Q Studios Corporation *******************************************************************************/ #include #include "seq.h" #include "db.h" #include "debug4g.h" #include "globals.h" #include "tile.h" #include "error.h" #include "actor.h" #include "sfx.h" #define kMaxSequences 1024 struct ACTIVE { uchar type; ushort index; }; struct SEQINST { RESHANDLE hSeq; Seq *pSequence; SEQCALLBACK callback; short timeCounter; // age of current frame in ticks uchar frameIndex; // current frame BOOL isPlaying; void Update( ACTIVE *active ); }; SEQINST siWall[kMaxXWalls], siMasked[kMaxXWalls]; SEQINST siCeiling[kMaxXSectors], siFloor[kMaxXSectors]; SEQINST siSprite[kMaxXSprites]; ACTIVE activeList[kMaxSequences]; static int activeCount = 0; void Seq::Preload( void ) { if ( memcmp(signature, kSEQSig, sizeof(signature)) != 0 ) ThrowError("Invalid sequence", ES_ERROR); if ( (version & 0xFF00) != (kSEQVersion & 0xFF00) ) ThrowError("Obsolete sequence version", ES_ERROR); for (int i = 0; i < nFrames; i++) tilePreloadTile(frame[i].nTile); } static void UpdateSprite( int nXSprite, SEQFRAME *pFrame ) { dassert(nXSprite > 0 && nXSprite < kMaxXSprites); int nSprite = xsprite[nXSprite].reference; dassert(nSprite >= 0 && nSprite < kMaxSprites); SPRITE *pSprite = &sprite[nSprite]; dassert(pSprite->extra == nXSprite); // set the motion bit if the vertical size or position of the sprite changes if ( (pSprite->flags & kAttrGravity) && ( tilesizy[pSprite->picnum] != tilesizy[pFrame->nTile] || picanm[pSprite->picnum].ycenter != picanm[pFrame->nTile].ycenter || pFrame->yrepeat && pFrame->yrepeat != pSprite->yrepeat) ) pSprite->flags |= kAttrFalling; pSprite->picnum = (short)pFrame->nTile; pSprite->pal = (char)pFrame->pal; // if (pFrame->shadeRelative) // pSprite->shade += (char)pFrame->shade; // else pSprite->shade = (char)pFrame->shade; if (pFrame->xrepeat) pSprite->xrepeat = (uchar)pFrame->xrepeat; if (pFrame->yrepeat) pSprite->yrepeat = (uchar)pFrame->yrepeat; if ( pFrame->translucent ) pSprite->cstat |= kSpriteTranslucent; else pSprite->cstat &= ~kSpriteTranslucent; if ( pFrame->translucentR ) pSprite->cstat |= kSpriteTranslucentR; else pSprite->cstat &= ~kSpriteTranslucentR; if ( pFrame->blocking ) pSprite->cstat |= kSpriteBlocking; else pSprite->cstat &= ~kSpriteBlocking; if ( pFrame->hitscan ) pSprite->cstat |= kSpriteHitscan; else pSprite->cstat &= ~kSpriteHitscan; if ( pFrame->smoke ) pSprite->flags |= kAttrSmoke; else pSprite->flags &= ~kAttrSmoke; } static void UpdateWall( int nXWall, SEQFRAME *pFrame ) { dassert(nXWall > 0 && nXWall < kMaxXWalls); int nWall = xwall[nXWall].reference; dassert(nWall >= 0 && nWall < kMaxWalls); WALL *pWall = &wall[nWall]; dassert(pWall->extra == nXWall); pWall->picnum = (short)pFrame->nTile; // pWall->shade = (char)pFrame->shade; pWall->pal = (char)pFrame->pal; if ( pFrame->translucent ) pWall->cstat |= kWallTranslucent; else pWall->cstat &= ~kWallTranslucent; if ( pFrame->translucentR ) pWall->cstat |= kWallTranslucentR; else pWall->cstat &= ~kWallTranslucentR; if ( pFrame->blocking ) pWall->cstat |= kWallBlocking; else pWall->cstat &= ~kWallBlocking; if ( pFrame->hitscan ) pWall->cstat |= kWallHitscan; else pWall->cstat &= ~kWallHitscan; } static void UpdateMasked( int nXWall, SEQFRAME *pFrame ) { dassert(nXWall > 0 && nXWall < kMaxXWalls); int nWall = xwall[nXWall].reference; dassert(nWall >= 0 && nWall < kMaxWalls); WALL *pWall = &wall[nWall]; dassert(pWall->extra == nXWall); dassert(pWall->nextwall >= 0); // it must be a 2 sided wall WALL *pWall2 = &wall[pWall->nextwall]; pWall->overpicnum = pWall2->overpicnum = (short)pFrame->nTile; // pWall->shade = (char)pFrame->shade; pWall->pal = pWall2->pal = (char)pFrame->pal; if ( pFrame->translucent ) { pWall->cstat |= kWallTranslucent; pWall2->cstat |= kWallTranslucent; } else { pWall->cstat &= ~kWallTranslucent; pWall2->cstat &= ~kWallTranslucent; } if ( pFrame->translucentR ) { pWall->cstat |= kWallTranslucentR; pWall2->cstat |= kWallTranslucentR; } else { pWall->cstat &= ~kWallTranslucentR; pWall2->cstat &= ~kWallTranslucentR; } if ( pFrame->blocking ) { pWall->cstat |= kWallBlocking; pWall2->cstat |= kWallBlocking; } else { pWall->cstat &= ~kWallBlocking; pWall2->cstat &= ~kWallBlocking; } if ( pFrame->hitscan ) { pWall->cstat |= kWallHitscan; pWall2->cstat |= kWallHitscan; } else { pWall->cstat &= ~kWallHitscan; pWall2->cstat &= ~kWallHitscan; } } static void UpdateFloor( int nXSector, SEQFRAME *pFrame ) { dassert(nXSector > 0 && nXSector < kMaxXSectors); int nSector = xsector[nXSector].reference; dassert(nSector >= 0 && nSector < kMaxSectors); SECTOR *pSector = §or[nSector]; dassert(pSector->extra == nXSector); pSector->floorpicnum = (short)pFrame->nTile; pSector->floorshade = (char)pFrame->shade; pSector->floorpal = (char)pFrame->pal; } static void UpdateCeiling( int nXSector, SEQFRAME *pFrame ) { dassert(nXSector > 0 && nXSector < kMaxXSectors); int nSector = xsector[nXSector].reference; dassert(nSector >= 0 && nSector < kMaxSectors); SECTOR *pSector = §or[nSector]; dassert(pSector->extra == nXSector); pSector->ceilingpicnum = (short)pFrame->nTile; pSector->ceilingshade = (char)pFrame->shade; pSector->ceilingpal = (char)pFrame->pal; } void SEQINST::Update( ACTIVE * active ) { dassert(frameIndex < pSequence->nFrames); switch (active->type) { case SS_WALL: UpdateWall(active->index, &pSequence->frame[frameIndex]); break; case SS_CEILING: UpdateCeiling(active->index, &pSequence->frame[frameIndex]); break; case SS_FLOOR: UpdateFloor(active->index, &pSequence->frame[frameIndex]); break; case SS_SPRITE: UpdateSprite(active->index, &pSequence->frame[frameIndex]); break; case SS_MASKED: UpdateMasked(active->index, &pSequence->frame[frameIndex]); break; } if ( pSequence->frame[frameIndex].trigger && callback != NULL ) callback(active->type, active->index); } SEQINST *GetInstance( int type, int nXIndex ) { switch ( type ) { case SS_WALL: dassert(nXIndex > 0 && nXIndex < kMaxXWalls); return &siWall[nXIndex]; case SS_CEILING: dassert(nXIndex > 0 && nXIndex < kMaxXSectors); return &siCeiling[nXIndex]; case SS_FLOOR: dassert(nXIndex > 0 && nXIndex < kMaxXSectors); return &siFloor[nXIndex]; case SS_SPRITE: dassert(nXIndex > 0 && nXIndex < kMaxXSprites); return &siSprite[nXIndex]; case SS_MASKED: dassert(nXIndex > 0 && nXIndex < kMaxXWalls); return &siMasked[nXIndex]; } ThrowError("Unexcepted object type", ES_ERROR); return NULL; } void UnlockInstance( SEQINST *pInst ) { dassert( pInst != NULL ); dassert( pInst->hSeq != NULL ); dassert( pInst->pSequence != NULL ); gSysRes.Unlock( pInst->hSeq ); pInst->hSeq = NULL; pInst->pSequence = NULL; pInst->isPlaying = FALSE; } void seqSpawn( int nSeqID, int type, int nXIndex, SEQCALLBACK callback ) { SEQINST *pInst = GetInstance(type, nXIndex); RESHANDLE hSeq = gSysRes.Lookup(nSeqID, ".SEQ"); dassert( hSeq != NULL); int i = activeCount; if ( pInst->isPlaying ) { // already playing this sequence? if (pInst->hSeq == hSeq) return; UnlockInstance( pInst ); for (i = 0; i < activeCount; i++) { if (activeList[i].type == type && activeList[i].index == nXIndex) break; } dassert(i < activeCount); // should have been found } Seq *pSequence = (Seq *)gSysRes.Lock(hSeq); if ( memcmp(pSequence->signature, kSEQSig, sizeof(pSequence->signature)) != 0 ) ThrowError("Invalid sequence", ES_ERROR); if ( (pSequence->version & 0xFF00) != (kSEQVersion & 0xFF00) ) ThrowError("Obsolete sequence version", ES_ERROR); pInst->hSeq = hSeq; pInst->pSequence = pSequence; pInst->callback = callback; pInst->isPlaying = TRUE; pInst->timeCounter = pSequence->ticksPerFrame; pInst->frameIndex = 0; if (i == activeCount) { dassert(activeCount < kMaxSequences); activeList[activeCount].type = (uchar)type; activeList[activeCount].index = (short)nXIndex; activeCount++; } pInst->Update(&activeList[i]); } void seqKill( int type, int nXIndex ) { SEQINST *pInst = GetInstance(type, nXIndex); if ( pInst->isPlaying ) { for (int i = 0; i < activeCount; i++) { if (activeList[i].type == type && activeList[i].index == nXIndex) break; } dassert(i < activeCount); // remove it from the active list activeList[i] = activeList[--activeCount]; pInst->isPlaying = FALSE; UnlockInstance( pInst ); } } void seqKillAll( void ) { int i; for (i = 0; i < kMaxXWalls; i++) { if ( siWall[i].isPlaying ) UnlockInstance( &siWall[i] ); if ( siMasked[i].isPlaying ) UnlockInstance( &siMasked[i] ); } for (i = 0; i < kMaxXSectors; i++) { if ( siCeiling[i].isPlaying ) UnlockInstance( &siCeiling[i] ); if ( siFloor[i].isPlaying ) UnlockInstance( &siFloor[i] ); } for (i = 0; i < kMaxXSprites; i++) { if ( siSprite[i].isPlaying ) UnlockInstance( &siSprite[i] ); } activeCount = 0; } int seqGetStatus( int type, int nXIndex ) { SEQINST *pInst = GetInstance(type, nXIndex); if ( pInst->isPlaying ) return pInst->frameIndex; return -1; } void seqProcess( int nTicks ) { for (int i = 0; i < activeCount; i++) { SEQINST *pInst = GetInstance(activeList[i].type, activeList[i].index); Seq *pSeq = pInst->pSequence; dassert(pInst->frameIndex < pSeq->nFrames); pInst->timeCounter -= nTicks; while (pInst->timeCounter < 0) { pInst->timeCounter += pSeq->ticksPerFrame; ++pInst->frameIndex; if (pInst->frameIndex == pSeq->nFrames) { if ( pSeq->flags & kSeqLoop ) pInst->frameIndex = 0; else { UnlockInstance( pInst ); if ( pSeq->flags & kSeqRemove ) { short nSprite, nWall, nNextWall; switch (activeList[i].type) { case SS_SPRITE: nSprite = (short)xsprite[activeList[i].index].reference; dassert(nSprite >= 0 && nSprite < kMaxSprites); deletesprite(nSprite); // safe to not use actPostSprite here break; case SS_MASKED: nWall = (short)xwall[activeList[i].index].reference; dassert(nWall >= 0 && nWall < kMaxWalls); wall[nWall].cstat &= ~kWallMasked & ~kWallFlipX & ~kWallOneWay; if ((nNextWall = wall[nWall].nextwall) != -1) wall[nNextWall].cstat &= ~kWallMasked & ~kWallFlipX & ~kWallOneWay; break; } } // remove it from the active list activeList[i--] = activeList[--activeCount]; break; } } pInst->Update(&activeList[i]); } } }