123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 |
- #include <stdlib.h>
- #include "engine.h"
- #include "debug4g.h"
- #include "error.h"
- #include "eventq.h"
- #include "db.h"
- #include "pqueue.h"
- #include "triggers.h"
- #include "globals.h"
- #include "levels.h"
- #define kMaxChannels 4096
- #define kMaxID 1024
- static PriorityQueue eventQ;
- /*******************************************************************************
- Each channel bucket is a contiguous range in a rxBucket[] array. The
- bucketHead[] array points to the start index for each channel id. Creating the
- buckets is not particularly fast, but using them is very fast and storage
- efficient.
- *******************************************************************************/
- struct RXBUCKET
- {
- unsigned index : 13; // object array index (sprite[], sector[], wall[])
- unsigned type : 3; // 0=sprite, 1=sector, 2=wall
- } rxBucket[kMaxChannels];
- ushort bucketHead[kMaxID + 1];
- static int GetBucketChannel( RXBUCKET *pBucket )
- {
- int nXIndex;
- switch (pBucket->type)
- {
- case SS_SECTOR:
- nXIndex = sector[pBucket->index].extra;
- dassert(nXIndex > 0);
- return xsector[nXIndex].rxID;
- case SS_WALL:
- nXIndex = wall[pBucket->index].extra;
- dassert(nXIndex > 0);
- return xwall[nXIndex].rxID;
- case SS_SPRITE:
- nXIndex = sprite[pBucket->index].extra;
- dassert(nXIndex > 0);
- return xsprite[nXIndex].rxID;
- }
- ThrowError("Unexpected rxBucket type", ES_ERROR);
- return 0;
- }
- int CompareChannels( const void *ref1, const void *ref2 )
- {
- return GetBucketChannel((RXBUCKET *)ref1) - GetBucketChannel((RXBUCKET *)ref2);
- }
- /*******************************************************************************
- FUNCTION: evInit()
- DESCRIPTION: Initialize the event queue
- NOTES: The map should be loaded so that all the rxIDs can be
- scanned
- *******************************************************************************/
- void evInit( void )
- {
- int i, j;
- int nCount = 0;
- eventQ.Flush();
- // add all the tags to the bucket array
- for (i = 0; i < kMaxSectors; i++)
- {
- int nXSector = sector[i].extra;
- if (nXSector > 0 && xsector[nXSector].rxID > 0)
- {
- dassert(nCount < kMaxChannels);
- rxBucket[nCount].type = SS_SECTOR;
- rxBucket[nCount].index = i;
- nCount++;
- }
- }
- for (i = 0; i < kMaxWalls; i++)
- {
- int nXWall = wall[i].extra;
- if (nXWall > 0 && xwall[nXWall].rxID > 0)
- {
- dassert(nCount < kMaxChannels);
- rxBucket[nCount].type = SS_WALL;
- rxBucket[nCount].index = i;
- nCount++;
- }
- }
- for (i = 0; i < kMaxSprites; i++)
- {
- if (sprite[i].statnum < kMaxStatus)
- {
- int nXSprite = sprite[i].extra;
- if (nXSprite > 0 && xsprite[nXSprite].rxID > 0)
- {
- dassert(nCount < kMaxChannels);
- rxBucket[nCount].type = SS_SPRITE;
- rxBucket[nCount].index = i;
- nCount++;
- }
- }
- }
- // sort the array on rx tags
- qsort(rxBucket, nCount, sizeof(RXBUCKET), &CompareChannels);
- // create the list of header indices
- j = 0;
- for (i = 0; i < kMaxID; i++)
- {
- bucketHead[i] = (short)j;
- while ( j < nCount && GetBucketChannel(&rxBucket[j]) == i)
- j++;
- }
- bucketHead[i] = (short)j;
- }
- /***********************************************************************
- * evSourceState()
- *
- * Return the operational state of the event's source.
- *
- **********************************************************************/
- static BOOL evGetSourceState( int type, int nIndex )
- {
- int nXIndex;
- switch ( type )
- {
- case SS_SECTOR:
- nXIndex = sector[nIndex].extra;
- dassert(nXIndex > 0 && nXIndex < kMaxXSectors);
- return (BOOL)xsector[nXIndex].state;
- case SS_WALL:
- nXIndex = wall[nIndex].extra;
- dassert(nXIndex > 0 && nXIndex < kMaxXWalls);
- return (BOOL)xwall[nXIndex].state;
- case SS_SPRITE:
- nXIndex = sprite[nIndex].extra;
- dassert(nXIndex > 0 && nXIndex < kMaxXSprites);
- return (BOOL)xsprite[nXIndex].state;
- }
- // shouldn't reach this point
- return FALSE;
- }
- /***********************************************************************
- * evSend()
- *
- **********************************************************************/
- void evSend( int index, int type, int to, int command )
- {
- if ( command == kCommandState )
- command = evGetSourceState(type, index) ? kCommandOn : kCommandOff;
- else if ( command == kCommandNotState )
- command = evGetSourceState(type, index) ? kCommandOff : kCommandOn;
- EVENT event;
- event.index = index;
- event.type = type;
- event.to = to;
- event.command = command;
- // handle transmit-only system triggers
- if (to > kChannelNull)
- {
- switch (to)
- {
- // case kChannelNull:
- // ThrowError("Event with null tag encountered", ES_ERROR);
- case kChannelEndLevelA:
- gEndLevelFlag = gEndingA;
- // Hooray. You finished the level.
- return;
- case kChannelEndLevelB:
- gEndLevelFlag = gEndingB;
- // Hooray. You finished the level via the secret ending.
- return;
- case kChannelTextOver:
- if (command < kCommandNumbered)
- ThrowError("Non-numbered command triggered for TextOver", ES_ERROR);
- trTextOver(command - kCommandNumbered);
- return;
- case kChannelLightning:
- if (command < kCommandNumbered)
- ThrowError("Non-numbered command triggered for Lightning", ES_ERROR);
- trLightning(command - kCommandNumbered);
- return;
- case kChannelTriggerStart:
- dprintf("Trigger start broadcast\n");
- break;
- case kChannelTriggerMatch:
- dprintf("BloodBath start broadcast\n");
- break;
- case kChannelTriggerCoop:
- dprintf("Coop start broadcast\n");
- break;
- case kChannelRemoteFire1:
- case kChannelRemoteFire2:
- case kChannelRemoteFire3:
- case kChannelRemoteFire4:
- case kChannelRemoteFire5:
- case kChannelRemoteFire6:
- case kChannelRemoteFire7:
- case kChannelRemoteFire8:
- // these can't use the rx buckets since they are dynamically created
- for (short nSprite = headspritestat[kStatThing]; nSprite >= 0; nSprite = nextspritestat[nSprite] )
- {
- SPRITE *pSprite = &sprite[nSprite];
- if ( pSprite->extra > 0 )
- {
- XSPRITE *pXSprite = &xsprite[pSprite->extra];
- if ( pXSprite->rxID == to )
- trMessageSprite( nSprite, event );
- }
- }
- return;
- }
- }
- // the event is a broadcast message
- for (int i = bucketHead[event.to]; i < bucketHead[event.to + 1]; i++)
- {
- // don't send it to the originator
- if (rxBucket[i].type == event.type && rxBucket[i].index == event.index)
- continue;
- switch ( rxBucket[i].type )
- {
- case SS_SECTOR:
- trMessageSector( rxBucket[i].index, event );
- break;
- case SS_WALL:
- trMessageWall( rxBucket[i].index, event );
- break;
- case SS_SPRITE:
- trMessageSprite( rxBucket[i].index, event );
- break;
- default:
- break;
- }
- }
- }
- /***********************************************************************
- * evPost()
- *
- **********************************************************************/
- void evPost( int index, int type, ulong time, int command )
- {
- if ( command == kCommandState )
- command = evGetSourceState(type, index) ? kCommandOn : kCommandOff;
- else if ( command == kCommandNotState )
- command = evGetSourceState(type, index) ? kCommandOff : kCommandOn;
- EVENT event;
- event.index = index;
- event.type = type;
- event.command = command;
- eventQ.Insert(gFrameClock + time, (void *&)event);
- }
- /***********************************************************************
- * evProcess()
- *
- * check for and process broadcast commands to objects
- *
- **********************************************************************/
- void evProcess( ulong time )
- {
- // while there are events to be processed
- while ( eventQ.Check(time) )
- {
- void *p = eventQ.Remove();
- EVENT event = (EVENT &)p;
- // is it a special callback event?
- // if (event.command != kCommandCallback)
- // ThrowError("Non-callback in event queue", ES_ERROR);
- dprintf("Dispatching callback event ");
- switch ( event.type )
- {
- case SS_SECTOR:
- dprintf("to sector %d\n", event.index);
- trMessageSector( event.index, event );
- break;
- case SS_WALL:
- dprintf("to wall %d\n", event.index);
- trMessageWall( event.index, event );
- break;
- case SS_SPRITE:
- dprintf("to sprite %d\n", event.index);
- trMessageSprite( event.index, event );
- break;
- }
- }
- }
|