1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245 |
- #include <string.h>
- #include <stdio.h>
- #include "engine.h"
- #include "db.h"
- #include "globals.h"
- #include "eventq.h"
- #include "triggers.h"
- #include "debug4g.h"
- #include "sectorfx.h"
- #include "misc.h"
- #include "gameutil.h"
- #include "trig.h"
- #include "screen.h"
- #include "actor.h"
- #include "seq.h"
- #include "error.h"
- #include "names.h"
- #include "levels.h"
- #include "view.h"
- #include "sfx.h"
- #include "sound.h" // can be removed once sfx complete
- #define kRemoteDelay 12 // 0.1 second
- #define kProxDelay 30 // 0.25 second
- static struct
- {
- long x, y;
- } kwall[kMaxWalls];
- static struct
- {
- long x, y, z;
- } ksprite[kMaxSprites];
- // trigger effects
- enum
- {
- kSeqClearGlass = 0x80,
- kSeqStainedGlass,
- kSeqWeb,
- kSeqBeam,
- kSeqJar,
- kSeqWaterOff,
- kSeqWaterOn,
- kSeqSpike,
- kSeqMetalGrate1,
- kSeqFireTrap1,
- kSeqFireTrap2,
- kSeqPadlock,
- kSeqMGunOpen,
- kSeqMGunFire,
- kSeqMGunClose,
- kSeqMGunFlare,
- kSeqTriggerMax,
- };
- /*******************************************************************************
- DOOR STATES
- OFF means closed
- ON means open
- Doors should be created in map edit in the open position, since it is easier to
- computationally determine closed than open. The trInit function should set
- the initial positions of the doors based on their state flags.
- *******************************************************************************/
- /*******************************************************************************
- Busy System Constants
- *******************************************************************************/
- enum {
- kBusyOk = 0,
- kBusyRestore,
- kBusyReverse,
- kBusyComplete,
- };
- typedef int (*BUSYPROC)( int nIndex, int nBusy );
- struct BUSY {
- int nIndex;
- int nDelta;
- int nBusy;
- BUSYPROC busyProc;
- };
- #define kMaxBusyArray 128
- int gBusyCount = 0;
- BUSY gBusy[kMaxBusyArray];
- /*******************************************************************************
- LOCAL FUNCTION PROTOTYPES
- *******************************************************************************/
- static void InitGenerator( int nSprite );
- static void ActivateGenerator( int nSprite );
- static void FireballTrapCallback( int /* type */, int nXIndex );
- static void MGunFireCallback( int /* type */, int nXIndex );
- static void MGunOpenCallback( int /* type */, int nXIndex );
- /*******************************************************************************
- LOCAL HELPER FUNCTIONS
- *******************************************************************************/
- /*******************************************************************************
- FUNCTION: Lin2Sin()
- DESCRIPTION: Convert a linear busy ramp to a sinusoidal ramp
- *******************************************************************************/
- inline int Lin2Sin( int nBusy )
- {
- return (1 << 15) - (Cos(nBusy * kAngle180 / kMaxBusyValue) >> 15);
- }
- /*******************************************************************************
- FUNCTION: SetSpriteState()
- DESCRIPTION: Change the state of an xsprite, and broadcast message if
- necessary.
- *******************************************************************************/
- static void SetSpriteState( int nSprite, XSPRITE *pXSprite, int state )
- {
- if ( (pXSprite->busy & kFluxMask) == 0 && pXSprite->state == state )
- return;
- pXSprite->busy = state << 16;
- pXSprite->state = state;
- if ( state != pXSprite->restState && pXSprite->waitTime > 0 )
- evPost(nSprite, SS_SPRITE, pXSprite->waitTime * kTimerRate / 10,
- pXSprite->restState ? kCommandOn : kCommandOff);
- if (pXSprite->command == kCommandLink)
- return;
- if ( pXSprite->txID != 0 )
- {
- if (pXSprite->triggerOn && pXSprite->state == 1)
- {
- dprintf( "evSend(%i, SS_SPRITE, %i, %i)\n", nSprite, pXSprite->txID, pXSprite->command );
- evSend(nSprite, SS_SPRITE, pXSprite->txID, pXSprite->command);
- }
- if (pXSprite->triggerOff && pXSprite->state == 0)
- {
- dprintf( "evSend(%i, SS_SPRITE, %i, %i)\n", nSprite, pXSprite->txID, pXSprite->command );
- evSend(nSprite, SS_SPRITE, pXSprite->txID, pXSprite->command);
- }
- }
- }
- /*******************************************************************************
- FUNCTION: SetWallState()
- DESCRIPTION: Change the state of an xwall, and broadcast message if
- necessary.
- *******************************************************************************/
- static void SetWallState( int nWall, XWALL *pXWall, int state )
- {
- if ( (pXWall->busy & kFluxMask) == 0 && pXWall->state == state )
- return;
- pXWall->busy = state << 16;
- pXWall->state = state;
- if ( state != pXWall->restState && pXWall->waitTime > 0 )
- evPost(nWall, SS_WALL, pXWall->waitTime * kTimerRate / 10,
- pXWall->restState ? kCommandOn : kCommandOff);
- if (pXWall->command == kCommandLink)
- return;
- if ( pXWall->txID != 0 )
- {
- if (pXWall->triggerOn && pXWall->state == 1)
- {
- dprintf( "evSend(%i,SS_WALL, %i, %i)\n", nWall, pXWall->txID, pXWall->command );
- evSend(nWall, SS_WALL, pXWall->txID, pXWall->command);
- }
- if (pXWall->triggerOff && pXWall->state == 0)
- {
- dprintf( "evSend(%i,SS_WALL, %i, %i)\n", nWall, pXWall->txID, pXWall->command );
- evSend(nWall, SS_WALL, pXWall->txID, pXWall->command);
- }
- }
- }
- /*******************************************************************************
- FUNCTION: SetSectorState()
- DESCRIPTION: Change the state of an xsector, and broadcast message if
- necessary.
- *******************************************************************************/
- static void SetSectorState( int nSector, XSECTOR *pXSector, int state )
- {
- if ( (pXSector->busy & kFluxMask) == 0 && pXSector->state == state )
- return;
- pXSector->busy = state << 16;
- pXSector->state = state;
- if ( state != pXSector->restState && pXSector->waitTime > 0 )
- evPost(nSector, SS_SECTOR, pXSector->waitTime * kTimerRate / 10,
- pXSector->restState ? kCommandOn : kCommandOff);
- if (pXSector->command == kCommandLink)
- return;
- if ( pXSector->txID != 0 )
- {
- if (pXSector->triggerOn && pXSector->state == 1)
- {
- dprintf( "evSend(%i, SS_SECTOR, %i, %i)\n", nSector, pXSector->txID, pXSector->command );
- evSend(nSector, SS_SECTOR, pXSector->txID, pXSector->command);
- }
- if (pXSector->triggerOff && pXSector->state == 0)
- {
- dprintf( "evSend(%i, SS_SECTOR, %i, %i)\n", nSector, pXSector->txID, pXSector->command );
- evSend(nSector, SS_SECTOR, pXSector->txID, pXSector->command);
- }
- }
- }
- /*******************************************************************************
- FUNCTION: AddBusy()
- DESCRIPTION: Add a busy process. The callback procedure will be called
- once per frame with the updated busy value.
- PARAMETERS: nIndex is used to identify the xobject to the callback
- function.
- NOTES: if nDelta is < 0, the busy value will count down from
- kMaxBusyValue.
- *******************************************************************************/
- static void AddBusy( int nIndex, BUSYPROC busyProc, int nDelta )
- {
- dassert(nDelta != 0);
- dassert(busyProc != NULL);
- // find an existing nIndex busy, or an unused busy slot
- for (int i = 0; i < gBusyCount; i++)
- {
- if ((nIndex == gBusy[i].nIndex) && (busyProc == gBusy[i].busyProc))
- break;
- }
- // adding a new busy?
- if (i == gBusyCount)
- {
- if ( gBusyCount == kMaxBusyArray )
- {
- dprintf("OVERFLOW: AddBusy() ignored\n");
- return;
- };
- gBusy[i].nIndex = nIndex;
- gBusy[i].busyProc = busyProc;
- gBusy[i].nBusy = (nDelta > 0) ? 0 : kMaxBusyValue;
- gBusyCount++;
- }
- gBusy[i].nDelta = nDelta;
- }
- /*******************************************************************************
- FUNCTION: ReverseBusy()
- DESCRIPTION: Reverse the delta of a busy process. This is used to
- change the direction of a process from outside the
- callback.
- *******************************************************************************/
- void ReverseBusy( int nIndex, BUSYPROC busyProc )
- {
- dassert(busyProc != NULL);
- // find an existing nIndex busy, or an unused busy slot
- for (int i = 0; i < gBusyCount; i++)
- {
- if ( nIndex == gBusy[i].nIndex && busyProc == gBusy[i].busyProc )
- {
- gBusy[i].nDelta = -gBusy[i].nDelta;
- return;
- }
- }
- dprintf("ReverseBusy: matching busy not found!\n");
- }
- /*******************************************************************************
- FUNCTION: GetSourceBusy()
- DESCRIPTION: Get the busy value of the object that originated the event.
- This is used for kCommandLink messages.
- *******************************************************************************/
- static unsigned GetSourceBusy( EVENT event )
- {
- int nXIndex;
- switch ( event.type )
- {
- case SS_SECTOR:
- nXIndex = sector[event.index].extra;
- dassert(nXIndex > 0 && nXIndex < kMaxXSectors);
- return xsector[nXIndex].busy;
- case SS_WALL:
- nXIndex = wall[event.index].extra;
- dassert(nXIndex > 0 && nXIndex < kMaxXWalls);
- return xwall[nXIndex].busy;
- case SS_SPRITE:
- nXIndex = sprite[event.index].extra;
- dassert(nXIndex > 0 && nXIndex < kMaxXSprites);
- return xsprite[nXIndex].busy;
- }
- // shouldn't reach this point
- return 0;
- }
- /*******************************************************************************
- FUNCTION: OperateSprite()
- DESCRIPTION:
- PARAMETERS:
- RETURNS:
- NOTES:
- *******************************************************************************/
- void OperateSprite( int nSprite, XSPRITE *pXSprite, EVENT event )
- {
- SPRITE *pSprite = &sprite[nSprite];
- // handle respawns first, to avoid toggling the sprite state
- if ( event.command == kCommandRespawn)
- {
- actRespawnSprite( (short)nSprite );
- switch (pSprite->type)
- {
- case kThingTNTBarrel:
- pSprite->cstat |= kSpriteBlocking | kSpriteHitscan;
- break;
- }
- return;
- }
- // special handling for lock/unlock commands
- switch ( event.command )
- {
- case kCommandLock:
- pXSprite->locked = 1;
- return;
- case kCommandUnlock:
- pXSprite->locked = 0;
- return;
- case kCommandToggleLock:
- pXSprite->locked ^= 1;
- return;
- }
- switch (pSprite->type)
- {
- case kMissileStarburstFlare:
- {
- if ( sprite[nSprite].statnum != kStatMissile) // don't trigger if already exploded
- break;
- pSprite->type = kMissileExplodingFlare;
- int nVel = (M2X(2.0) << 4) / kTimerRate; // meters per second
- int nAngle = getangle(pSprite->xvel, pSprite->yvel);
- for (int i = 0; i < 16; i++)
- {
- int nBurst = actCloneSprite( pSprite );
- dbInsertXSprite( nBurst );
- long dxVel = 0;
- long dyVel = mulscale30r(nVel, Cos(i * kAngle360 / 16));
- long dzVel = mulscale30r(nVel, Sin(i * kAngle360 / 16));
- if (i & 1)
- {
- dyVel >>= 1;
- dzVel >>= 1;
- }
- RotateVector( &dxVel, &dyVel, nAngle );
- SPRITE *pBurst = &sprite[nBurst];
- pBurst->xvel += dxVel;
- pBurst->yvel += dyVel;
- pBurst->zvel += dzVel;
- }
- break;
- }
- // case kMissileExplodingFlare:
- case kMissileFlare:
- actPostSprite( nSprite, kStatFree );
- break;
- case kThingMachineGun:
- if (pXSprite->health > 0)
- {
- if ( event.command == kCommandOn )
- {
- if (pXSprite->state == 0)
- {
- SetSpriteState(nSprite, pXSprite, 1);
- seqSpawn(kSeqMGunOpen, SS_SPRITE, pSprite->extra, MGunOpenCallback);
- //sfxStart3DSound(pSprite->extra, kSfxSwitch2);
- // data1 = max ammo (defaults to infinite)
- // data2 = dynamic ammo if data1 > 0
- if (pXSprite->data1 > 0)
- pXSprite->data2 = pXSprite->data1;
- }
- }
- else if ( event.command == kCommandOff )
- {
- if (pXSprite->state)
- {
- SetSpriteState(nSprite, pXSprite, 0);
- seqSpawn(kSeqMGunClose, SS_SPRITE, pSprite->extra);
- //sfxStart3DSound(pSprite->extra, kSfxSwitch2);
- }
- }
- }
- break;
- case kThingWallCrack:
- SetSpriteState(nSprite, pXSprite, 0);
- actPostSprite( nSprite, kStatFree );
- break;
- case kThingCrateFace:
- SetSpriteState(nSprite, pXSprite, 0);
- actPostSprite( nSprite, kStatFree );
- break;
- case kTrapPoweredZap:
- switch( event.command )
- {
- case kCommandOff:
- dprintf("zap off\n");
- pXSprite->state = 0;
- pSprite->cstat |= kSpriteInvisible;
- pSprite->cstat &= ~kSpriteBlocking;
- break;
- case kCommandOn:
- dprintf("zap on\n");
- pXSprite->state = 1;
- pSprite->cstat &= ~kSpriteInvisible;
- pSprite->cstat |= kSpriteBlocking;
- break;
- case kCommandToggle:
- dprintf("zap toggle\n");
- pXSprite->state ^= 1;
- pSprite->cstat ^= kSpriteInvisible;
- pSprite->cstat ^= kSpriteBlocking;
- break;
- }
- break;
- case kSwitchPadlock:
- switch( event.command )
- {
- case kCommandOff:
- SetSpriteState(nSprite, pXSprite, 0);
- break;
- case kCommandOn:
- SetSpriteState(nSprite, pXSprite, 1);
- seqSpawn(kSeqPadlock, SS_SPRITE, pSprite->extra);
- sfxStart3DSound(pSprite->extra, kSfxChains);
- break;
- default:
- SetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1);
- if (pXSprite->state)
- {
- seqSpawn(kSeqPadlock, SS_SPRITE, pSprite->extra);
- sfxStart3DSound(pSprite->extra, kSfxChains);
- }
- break;
- }
- break;
- case kSwitchMomentary:
- switch( event.command )
- {
- case kCommandOff:
- SetSpriteState(nSprite, pXSprite, 0);
- break;
- case kCommandOn:
- SetSpriteState(nSprite, pXSprite, 1);
- break;
- default:
- SetSpriteState(nSprite, pXSprite, pXSprite->restState ^ 1);
- break;
- }
- sfxStart3DSound(pSprite->extra, kSfxSwitch2);
- break;
- case kSwitchCombination:
- switch( event.command )
- {
- case kCommandOff:
- if (--pXSprite->data1 < 0) // underflow?
- pXSprite->data1 += pXSprite->data3;
- break;
- default:
- if (++pXSprite->data1 >= pXSprite->data3) // overflow?
- pXSprite->data1 -= pXSprite->data3;
- break;
- }
- // handle master switches
- if ( pXSprite->command == kCommandLink && pXSprite->txID != 0 )
- evSend(nSprite, SS_SPRITE, pXSprite->txID, kCommandLink);
- // at right combination?
- if (pXSprite->data1 == pXSprite->data2)
- SetSpriteState(nSprite, pXSprite, 1);
- else
- SetSpriteState(nSprite, pXSprite, 0);
- break;
- case kThingTNTStick:
- case kThingTNTBundle:
- case kThingTNTBarrel:
- if ( sprite[nSprite].statnum != kStatRespawn ) // don't trigger if waiting to be respawned!
- actExplodeSprite((short)nSprite);
- break;
- case kThingTNTRemArmed:
- if ( sprite[nSprite].statnum == kStatRespawn )
- break;
- switch ( event.command )
- {
- case kCommandOn:
- sfxStart3DSound(pSprite->extra, kSfxTNTDetRemote);
- evPost(nSprite, SS_SPRITE, kRemoteDelay);
- break;
- default:
- actExplodeSprite((short)nSprite);
- break;
- }
- break;
- case kThingTNTProxArmed:
- if ( sprite[nSprite].statnum == kStatRespawn )
- break;
- switch ( event.command )
- {
- case kCommandSpriteProximity:
- if ( pXSprite->state ) // don't trigger it if already triggered
- break;
- sfxStart3DSound(pSprite->extra, kSfxTNTDetProx);
- evPost(nSprite, SS_SPRITE, kProxDelay, kCommandOff);
- pXSprite->state = 1;
- break;
- case kCommandCallback:
- sfxStart3DSound(pSprite->extra, kSfxTNTArmProx);
- actPostSprite(nSprite, kStatProximity); // activate it by changing list
- break;
- default:
- actExplodeSprite((short)nSprite);
- break;
- }
- break;
- case kGenTrigger:
- case kGenWaterDrip:
- case kGenBloodDrip:
- case kGenFireball:
- case kGenEctoSkull:
- case kGenDart:
- case kGenSound:
- switch( event.command )
- {
- case kCommandCallback:
- pXSprite->data3 = FALSE; // prevent callback reentrancy
- if ( pXSprite->state )
- {
- if ( pSprite->type != kGenTrigger )
- ActivateGenerator( nSprite );
- if ( pXSprite->txID != 0 )
- evSend(nSprite, SS_SPRITE, pXSprite->txID, pXSprite->command);
- if ( pXSprite->busyTime > 0 )
- {
- pXSprite->data3 = TRUE;
- evPost(nSprite, SS_SPRITE, (pXSprite->busyTime + BiRandom(pXSprite->data1)) * kTimerRate / 10);
- }
- }
- break;
- case kCommandOff:
- SetSpriteState(nSprite, pXSprite, 0);
- break;
- default:
- if ( event.command == kCommandOn )
- SetSpriteState(nSprite, pXSprite, 1);
- else
- SetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1);
- if ( pXSprite->state && pXSprite->data3 == FALSE )
- {
- pXSprite->data3 = TRUE;
- evPost(nSprite, SS_SPRITE, 0); // trigger immediately
- }
- break;
- }
- break;
- default:
- switch( event.command )
- {
- case kCommandOff:
- SetSpriteState(nSprite, pXSprite, 0);
- break;
- case kCommandOn:
- SetSpriteState(nSprite, pXSprite, 1);
- break;
- default:
- SetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1);
- break;
- }
- break;
- }
- }
- /*******************************************************************************
- FUNCTION: OperateWall()
- DESCRIPTION:
- PARAMETERS:
- RETURNS:
- NOTES:
- *******************************************************************************/
- void OperateWall( int nWall, XWALL *pXWall, EVENT event )
- {
- WALL *pWall = &wall[nWall];
- // special handling for lock/unlock commands
- switch ( event.command )
- {
- case kCommandLock:
- pXWall->locked = 1;
- return;
- case kCommandUnlock:
- pXWall->locked = 0;
- return;
- case kCommandToggleLock:
- pXWall->locked ^= 1;
- return;
- }
- switch (pWall->type)
- {
- case kWallClearGlass:
- if ( pXWall->state == 0 )
- {
- SetWallState(nWall, pXWall, 1);
- seqSpawn(kSeqClearGlass, SS_MASKED, wall[nWall].extra);
- sndStartSample( "glasshit2", 64 );
- }
- break;
- case kWallStainedGlass:
- if ( pXWall->state == 0 )
- {
- SetWallState(nWall, pXWall, 1);
- seqSpawn(kSeqStainedGlass, SS_MASKED, wall[nWall].extra);
- sndStartSample( "glasshit2", 64 );
- }
- break;
- case kWallWoodBeams:
- if ( pXWall->state == 0 )
- {
- SetWallState(nWall, pXWall, 1);
- seqSpawn(kSeqBeam, SS_MASKED, wall[nWall].extra);
- }
- break;
- case kWallWeb:
- if ( pXWall->state == 0 )
- {
- SetWallState(nWall, pXWall, 1);
- seqSpawn(kSeqWeb, SS_MASKED, wall[nWall].extra);
- }
- break;
- case kWallMetalGrate1:
- if ( pXWall->state == 0 )
- {
- SetWallState(nWall, pXWall, 1);
- seqSpawn(kSeqMetalGrate1, SS_MASKED, wall[nWall].extra);
- }
- break;
- default:
- switch( event.command )
- {
- case kCommandOff:
- SetWallState(nWall, pXWall, 0);
- break;
- case kCommandOn:
- SetWallState(nWall, pXWall, 1);
- break;
- default:
- SetWallState(nWall, pXWall, pXWall->state ^ 1);
- break;
- }
- break;
- }
- }
- void TranslateSector( int nSector, int i0, int i1, int x0, int y0, int a0,
- int x1, int y1, int a1, BOOL fAllWalls )
- {
- XSECTOR *pXSector = &xsector[sector[nSector].extra];
- int vX = x1 - x0;
- int vY = y1 - y0;
- int vA = a1 - a0;
- int Xi0 = mulscale16(i0, vX);
- int Xi1 = mulscale16(i1, vX);
- int dX = Xi1 - Xi0;
- int Yi0 = mulscale16(i0, vY);
- int Yi1 = mulscale16(i1, vY);
- int dY = Yi1 - Yi0;
- int Ai0 = mulscale16(i0, vA);
- int Ai1 = mulscale16(i1, vA);
- int dA = Ai1 - Ai0;
- long x, y;
- short nWall = sector[nSector].wallptr;
- if ( fAllWalls )
- {
- for (int i = 0; i < sector[nSector].wallnum; nWall++, i++)
- {
- x = kwall[nWall].x;
- y = kwall[nWall].y;
- if ( Ai1 )
- RotatePoint(&x, &y, Ai1, x0, y0);
- // move vertex
- dragpoint(nWall, x + Xi1, y + Yi1);
- }
- }
- else
- {
- for (int i = 0; i < sector[nSector].wallnum; nWall++, i++)
- {
- short nWall2 = wall[nWall].point2;
- x = kwall[nWall].x;
- y = kwall[nWall].y;
- if ( wall[nWall].cstat & kWallMoveForward )
- {
- if ( Ai1 )
- RotatePoint(&x, &y, Ai1, x0, y0);
- dragpoint(nWall, x + Xi1, y + Yi1);
- // move next vertex if not explicitly tagged
- if ( !(wall[nWall2].cstat & kWallMoveMask) )
- {
- x = kwall[nWall2].x;
- y = kwall[nWall2].y;
- if ( Ai1 )
- RotatePoint(&x, &y, Ai1, x0, y0);
- dragpoint(nWall2, x + Xi1, y + Yi1);
- }
- }
- else if ( wall[nWall].cstat & kWallMoveBackward )
- {
- if ( Ai1 )
- RotatePoint(&x, &y, -Ai1, x0, y0);
- dragpoint(nWall, x - Xi1, y - Yi1);
- // move next vertex if not explicitly tagged
- if ( !(wall[nWall2].cstat & kWallMoveMask) )
- {
- x = kwall[nWall2].x;
- y = kwall[nWall2].y;
- if ( Ai1 )
- RotatePoint(&x, &y, -Ai1, x0, y0);
- dragpoint(nWall2, x - Xi1, y - Yi1);
- }
- }
- }
- }
- for (short nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
- {
- SPRITE *pSprite = &sprite[nSprite];
- // don't move markers
- if ( pSprite->statnum == kStatMarker )
- continue;
- x = ksprite[nSprite].x;
- y = ksprite[nSprite].y;
- if ( sprite[nSprite].cstat & kSpriteMoveForward )
- {
- if ( Ai1 )
- RotatePoint(&x, &y, Ai1, x0, y0);
- pSprite->ang = (short)((pSprite->ang + dA) & kAngleMask);
- pSprite->x = x + Xi1;
- pSprite->y = y + Yi1;
- }
- else if ( sprite[nSprite].cstat & kSpriteMoveReverse )
- {
- if ( Ai1 )
- RotatePoint(&x, &y, -Ai1, x0, y0);
- pSprite->ang = (short)((pSprite->ang - dA) & kAngleMask);
- pSprite->x = x - Xi1;
- pSprite->y = y - Yi1;
- }
- else if ( pXSector->drag )
- {
- int zTop, zBot;
- GetSpriteExtents(pSprite, &zTop, &zBot);
- if ( !(pSprite->cstat & kSpriteRMask) && zBot >= sector[nSector].floorz )
- {
- // translate relatively (degenerative)
- if ( dA )
- RotatePoint(&pSprite->x, &pSprite->y, dA, x0 + Xi0, y0 + Yi0);
- pSprite->ang = (short)((pSprite->ang + dA) & kAngleMask);
- pSprite->x += dX;
- pSprite->y += dY;
- }
- }
- }
- }
- /*******************************************************************************
- FUNCTION: GetHighestSprite()
- DESCRIPTION: Helper function to return the highest sprite with the
- specified statnum equal to nStatus in the sector specified
- by nSector.
- PARAMETERS: nSector = the sector to search
- nStatus = the status number to match, or kMaxStatus = ALL
- RETURNS: The sprite number with the highest Z, or -1 = none
- NOTES:
- *******************************************************************************/
- int GetHighestSprite( int nSector, int nStatus, int *hiz )
- {
- *hiz = sector[nSector].floorz;
- int retSprite = -1;
- for (short nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
- {
- if ( sprite[nSprite].statnum == nStatus || nStatus == kMaxStatus)
- {
- SPRITE *pSprite = &sprite[nSprite];
- int zUp, zDown;
- GetSpriteExtents(pSprite, &zUp, &zDown);
- if ( pSprite->z - zUp < *hiz )
- {
- *hiz = pSprite->z - zUp;
- retSprite = nSprite;
- }
- }
- }
- return retSprite;
- }
- /*******************************************************************************
- FUNCTION: VCrushBusy()
- DESCRIPTION: Called by trProcessBusy : calculates the new floorz and
- ceilingz height based on the nBusy value
- PARAMETERS:
- RETURNS:
- NOTES: This busy proc works for lifts, Top doors, Bottom doors,
- and HSplit doors
- *******************************************************************************/
- int VCrushBusy( int nSector, int nBusy )
- {
- dassert( nSector >= 0 && nSector < numsectors);
- int nXSector = sector[nSector]. extra;
- dassert(nXSector > 0 && nXSector < kMaxXSectors);
- XSECTOR *pXSector = &xsector[nXSector];
- int newCeilZ, newFloorZ;
- int dCeilZ = pXSector->onCeilZ - pXSector->offCeilZ;
- if ( dCeilZ )
- newCeilZ = pXSector->offCeilZ + mulscale16(dCeilZ, Lin2Sin(nBusy));
- int dFloorZ = pXSector->onFloorZ - pXSector->offFloorZ;
- if ( dFloorZ )
- newFloorZ = pXSector->offFloorZ + mulscale16(dFloorZ, Lin2Sin(nBusy));
- int z;
- int nSprite = GetHighestSprite( nSector, kStatDude, &z );
- if ( nSprite >= 0 && newCeilZ >= z )
- return kBusyRestore;
- if (dCeilZ)
- sector[nSector].ceilingz = newCeilZ;
- if (dFloorZ)
- sector[nSector].floorz = newFloorZ;
- pXSector->busy = nBusy;
- if ( pXSector->command == kCommandLink && pXSector->txID != 0 )
- evSend(nSector, SS_SECTOR, pXSector->txID, kCommandLink);
- if ( (nBusy & kFluxMask) == 0 )
- {
- SetSectorState(nSector, pXSector, nBusy >> 16);
- return kBusyComplete;
- }
- return kBusyOk;
- }
- /*******************************************************************************
- FUNCTION: VSpriteBusy()
- DESCRIPTION: Called by trProcessBusy : calculates the new sprite z
- based on the nBusy value
- PARAMETERS:
- RETURNS:
- NOTES: This busy proc works for z motion sprites only. The sector
- floor and ceiling heights are unaffected.
- *******************************************************************************/
- int VSpriteBusy( int nSector, int nBusy )
- {
- dassert( nSector >= 0 && nSector < numsectors);
- int nXSector = sector[nSector]. extra;
- dassert(nXSector > 0 && nXSector < kMaxXSectors);
- XSECTOR *pXSector = &xsector[nXSector];
- int floorZRange = pXSector->onFloorZ - pXSector->offFloorZ;
- if ( floorZRange != 0 )
- {
- // adjust the z for any floor relative sprites or face sprites in the floor
- for (short nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
- {
- SPRITE *pSprite = &sprite[nSprite];
- if ( pSprite->cstat & kSpriteMoveFloor )
- pSprite->z = ksprite[nSprite].z + mulscale16(floorZRange, Lin2Sin(nBusy));
- }
- }
- int ceilZRange = pXSector->onCeilZ - pXSector->offCeilZ;
- if ( ceilZRange != 0 )
- {
- // adjust the z for any ceiling relative sprites or face sprites in the ceiling
- for (short nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
- {
- SPRITE *pSprite = &sprite[nSprite];
- if ( pSprite->cstat & kSpriteMoveCeiling )
- pSprite->z = ksprite[nSprite].z + mulscale16(ceilZRange, Lin2Sin(nBusy));
- }
- }
- pXSector->busy = nBusy;
- if ( pXSector->command == kCommandLink && pXSector->txID != 0 )
- evSend(nSector, SS_SECTOR, pXSector->txID, kCommandLink);
- if ( (nBusy & kFluxMask) == 0 )
- {
- SetSectorState(nSector, pXSector, nBusy >> 16);
- return kBusyComplete;
- }
- return kBusyOk;
- }
- /*******************************************************************************
- FUNCTION: VDoorBusy()
- DESCRIPTION: Called by trProcessBusy : calculates the new floorz and
- ceilingz height based on the nBusy value
- PARAMETERS:
- RETURNS:
- NOTES: This busy proc works for lifts, Top doors, Bottom doors,
- and HSplit doors
- *******************************************************************************/
- int VDoorBusy( int nSector, int nBusy )
- {
- dassert( nSector >= 0 && nSector < numsectors);
- int nXSector = sector[nSector]. extra;
- dassert(nXSector > 0 && nXSector < kMaxXSectors);
- XSECTOR *pXSector = &xsector[nXSector];
- int floorZRange = pXSector->onFloorZ - pXSector->offFloorZ;
- if ( floorZRange != 0 )
- {
- int oldFloorZ = sector[nSector].floorz;
- sector[nSector].floorz = pXSector->offFloorZ + mulscale16(floorZRange, Lin2Sin(nBusy));
- // adjust the z for any floor relative sprites or face sprites in the floor
- for (short nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
- {
- SPRITE *pSprite = &sprite[nSprite];
- int zTop, zBot;
- GetSpriteExtents(pSprite, &zTop, &zBot);
- if ( pSprite->cstat & kSpriteMoveFloor ||
- ( !(pSprite->cstat & kSpriteRMask) && zBot >= oldFloorZ) )
- pSprite->z += sector[nSector].floorz - oldFloorZ;
- }
- }
- int ceilZRange = pXSector->onCeilZ - pXSector->offCeilZ;
- if ( ceilZRange != 0 )
- {
- int oldCeilZ = sector[nSector].ceilingz;
- sector[nSector].ceilingz = pXSector->offCeilZ + mulscale16(ceilZRange, Lin2Sin(nBusy));
- // adjust the z for any ceiling relative sprites or face sprites in the ceiling
- for (short nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
- {
- SPRITE *pSprite = &sprite[nSprite];
- int zTop, zBot;
- GetSpriteExtents(pSprite, &zTop, &zBot);
- if ( pSprite->cstat & kSpriteMoveCeiling ||
- ( !(pSprite->cstat & kSpriteRMask) && zTop <= oldCeilZ) )
- pSprite->z += sector[nSector].ceilingz - oldCeilZ;
- }
- }
- pXSector->busy = nBusy;
- if ( pXSector->command == kCommandLink && pXSector->txID != 0 )
- evSend(nSector, SS_SECTOR, pXSector->txID, kCommandLink);
- if ( (nBusy & kFluxMask) == 0 )
- {
- SetSectorState(nSector, pXSector, nBusy >> 16);
- return kBusyComplete;
- }
- return kBusyOk;
- }
- /*******************************************************************************
- FUNCTION: HDoorBusy()
- DESCRIPTION: Called by trProcessBusy : calculates the new wall x,y
- based on the nBusy value
- RETURNS:
- NOTES: This busy proc works for slide doors
- *******************************************************************************/
- int HDoorBusy( int nSector, int nBusy )
- {
- dassert( nSector >= 0 && nSector < numsectors);
- SECTOR *pSector = §or[nSector];
- int nXSector = pSector->extra;
- dassert(nXSector > 0 && nXSector < kMaxXSectors);
- XSECTOR *pXSector = &xsector[nXSector];
- SPRITE *pMark0 = &sprite[pXSector->marker0];
- SPRITE *pMark1 = &sprite[pXSector->marker1];
- TranslateSector(nSector, Lin2Sin(pXSector->busy), Lin2Sin(nBusy),
- pMark0->x, pMark0->y, pMark0->ang, pMark1->x, pMark1->y, pMark1->ang,
- pSector->type == kSectorSlide);
- // SlideSector(nSector, mulscale16(dx, nAmount), mulscale16(dy, nAmount));
- pXSector->busy = nBusy;
- if ( pXSector->command == kCommandLink && pXSector->txID != 0 )
- evSend(nSector, SS_SECTOR, pXSector->txID, kCommandLink);
- if ( (nBusy & kFluxMask) == 0 )
- {
- SetSectorState(nSector, pXSector, nBusy >> 16);
- return kBusyComplete;
- }
- return kBusyOk;
- }
- /*******************************************************************************
- FUNCTION: RDoorBusy()
- DESCRIPTION: Called by trProcessBusy : calculates the new rotating door
- wall angles based on the nBusy value.
- PARAMETERS:
- RETURNS:
- NOTES:
- *******************************************************************************/
- int RDoorBusy( int nSector, int nBusy )
- {
- dassert( nSector >= 0 && nSector < numsectors);
- SECTOR *pSector = §or[nSector];
- int nXSector = pSector->extra;
- dassert(nXSector > 0 && nXSector < kMaxXSectors);
- XSECTOR *pXSector = &xsector[nXSector];
- SPRITE *pMark = &sprite[pXSector->marker0];
- TranslateSector(nSector, Lin2Sin(pXSector->busy), Lin2Sin(nBusy),
- pMark->x, pMark->y, 0, pMark->x, pMark->y, pMark->ang,
- pSector->type == kSectorRotate);
- pXSector->busy = nBusy;
- if ( pXSector->command == kCommandLink && pXSector->txID != 0 )
- evSend(nSector, SS_SECTOR, pXSector->txID, kCommandLink);
- if ( (nBusy & kFluxMask) == 0 )
- {
- SetSectorState(nSector, pXSector, nBusy >> 16);
- return kBusyComplete;
- }
- return kBusyOk;
- }
- /*******************************************************************************
- FUNCTION: OperateDoor()
- DESCRIPTION:
- PARAMETERS:
- RETURNS:
- NOTES:
- *******************************************************************************/
- void OperateDoor( int nSector, EVENT event, BUSYPROC busyProc )
- {
- dassert( nSector >= 0 && nSector < numsectors);
- int nXSector = sector[nSector].extra;
- dassert(nXSector > 0 && nXSector < kMaxXSectors);
- XSECTOR *pXSector = &xsector[nXSector];
- int nDelta = kMaxBusyValue / ClipLow(pXSector->busyTime * kTimerRate / 10, 1);
- switch (event.command)
- {
- case kCommandOff:
- if ( pXSector->busy != 0x0000 )
- AddBusy(nSector, busyProc, -nDelta);
- break;
- case kCommandOn:
- if ( pXSector->busy != 0x10000 )
- AddBusy(nSector, busyProc, nDelta);
- break;
- default:
- if ( (pXSector->busy & kFluxMask) && pXSector->interruptable )
- ReverseBusy(nSector, busyProc);
- else
- AddBusy(nSector, busyProc, pXSector->state ? -nDelta : nDelta);
- break;
- }
- }
- /*******************************************************************************
- FUNCTION: OperateTeleport()
- DESCRIPTION:
- PARAMETERS:
- RETURNS:
- NOTES:
- *******************************************************************************/
- void OperateTeleport( int nSector, EVENT event )
- {
- dassert( nSector >= 0 && nSector < numsectors );
- int nXSector = sector[nSector].extra;
- dassert( nXSector > 0 && nXSector < kMaxXSectors );
- XSECTOR *pXSector = &xsector[nXSector];
- BOOL bChanged = FALSE;
- switch (event.command)
- {
- case kCommandOff:
- bChanged = (pXSector->state == 1);
- SetSectorState(nSector, pXSector, 0);
- break;
- case kCommandOn:
- bChanged = (pXSector->state == 0);
- SetSectorState(nSector, pXSector, 1);
- break;
- default:
- bChanged = (pXSector->state == pXSector->restState);
- SetSectorState(nSector, pXSector, pXSector->restState ^ 1);
- break;
- }
- if ( bChanged && (pXSector->state != pXSector->restState) )
- {
- int nDest = pXSector->marker0;
- dassert( nDest < kMaxSprites );
- SPRITE *pDest = &sprite[nDest];
- dassert( pDest->statnum == kStatMarker);
- dassert( pDest->type == kMarkerWarpDest );
- for (int nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
- {
- SPRITE *pSprite = &sprite[nSprite];
- if ( pSprite->statnum == kStatDude )
- {
- pSprite->x = pDest->x;
- pSprite->y = pDest->y;
- pSprite->z += sector[pDest->sectnum].floorz - sector[nSector].floorz;
- pSprite->ang = pDest->ang;
- changespritesect( (short)nSprite, pDest->sectnum );
- if (pSprite->type >= kDudePlayer1 && pSprite->type <= kDudePlayer8)
- {
- // fix backup position for interpolation to work properly
- viewBackupPlayerLoc(pSprite->type - kDudePlayer1);
- }
- }
- }
- }
- }
- /*******************************************************************************
- FUNCTION: OperateSector()
- DESCRIPTION:
- PARAMETERS:
- RETURNS:
- NOTES:
- *******************************************************************************/
- void OperateSector( int nSector, XSECTOR *pXSector, EVENT event )
- {
- SECTOR *pSector = §or[nSector];
- // special handling for lock/unlock commands
- switch ( event.command )
- {
- case kCommandLock:
- pXSector->locked = 1;
- return;
- case kCommandUnlock:
- pXSector->locked = 0;
- return;
- case kCommandToggleLock:
- pXSector->locked ^= 1;
- return;
- }
- switch ( pSector->type)
- {
- case kSectorZSprite:
- OperateDoor(nSector, event, VSpriteBusy);
- break;
- case kSectorZMotion:
- OperateDoor(nSector, event, VDoorBusy);
- break;
- case kSectorZCrusher:
- OperateDoor(nSector, event, VCrushBusy);
- break;
- case kSectorSlideMarked:
- case kSectorSlide:
- case kSectorSlideCrush:
- OperateDoor(nSector, event, HDoorBusy);
- break;
- case kSectorRotateMarked:
- case kSectorRotate:
- case kSectorRotateCrush:
- OperateDoor(nSector, event, RDoorBusy);
- break;
- case kSectorTeleport:
- OperateTeleport( nSector, event );
- break;
- default:
- switch( event.command )
- {
- case kCommandOff:
- SetSectorState(nSector, pXSector, 0);
- break;
- case kCommandOn:
- SetSectorState(nSector, pXSector, 1);
- break;
- case kCommandToggle:
- case kCommandSectorPush:
- case kCommandSectorImpact:
- case kCommandSectorEnter:
- case kCommandSectorExit:
- SetSectorState(nSector, pXSector, pXSector->state ^ 1);
- break;
- }
- break;
- }
- }
- /*******************************************************************************
- FUNCTION: LinkSector()
- DESCRIPTION:
- PARAMETERS:
- RETURNS:
- NOTES:
- *******************************************************************************/
- void LinkSector( int nSector, XSECTOR *pXSector, EVENT event )
- {
- SECTOR *pSector = §or[nSector];
- int nBusy = GetSourceBusy(event);
- switch ( pSector->type )
- {
- case kSectorZSprite:
- VSpriteBusy( nSector, nBusy );
- break;
- case kSectorZMotion:
- VDoorBusy( nSector, nBusy );
- break;
- case kSectorZCrusher:
- VCrushBusy( nSector, nBusy );
- break;
- case kSectorSlide:
- case kSectorSlideMarked:
- case kSectorSlideCrush:
- HDoorBusy( nSector, nBusy );
- break;
- case kSectorRotate:
- case kSectorRotateMarked:
- case kSectorRotateCrush:
- RDoorBusy( nSector, nBusy );
- break;
- default:
- pXSector->busy = nBusy;
- if ( (nBusy & kFluxMask) == 0 )
- SetSectorState(nSector, pXSector, nBusy >> 16);
- break;
- }
- }
- /*******************************************************************************
- FUNCTION: LinkSprite()
- DESCRIPTION:
- PARAMETERS:
- RETURNS:
- NOTES:
- *******************************************************************************/
- void LinkSprite( int nSprite, XSPRITE *pXSprite, EVENT event )
- {
- SPRITE *pSprite = &sprite[nSprite];
- int nBusy = GetSourceBusy(event);
- switch ( pSprite->type )
- {
- case kSwitchCombination:
- // should only be linked to a master switch
- if (event.type == SS_SPRITE)
- {
- int nXSprite2 = sprite[event.index].extra;
- dassert(nXSprite2 > 0 && nXSprite2 < kMaxXSprites);
- // get master switch selection
- pXSprite->data1 = xsprite[nXSprite2].data1;
- // at right combination?
- if (pXSprite->data1 == pXSprite->data2)
- SetSpriteState(nSprite, pXSprite, 1);
- else
- SetSpriteState(nSprite, pXSprite, 0);
- }
- break;
- default:
- pXSprite->busy = nBusy;
- if ( (nBusy & kFluxMask) == 0 )
- SetSpriteState(nSprite, pXSprite, nBusy >> 16);
- break;
- }
- }
- /*******************************************************************************
- FUNCTION: LinkWall()
- DESCRIPTION:
- PARAMETERS:
- RETURNS:
- NOTES:
- *******************************************************************************/
- void LinkWall( int nWall, XWALL *pXWall, EVENT event )
- {
- WALL *pWall = &wall[nWall];
- int nBusy = GetSourceBusy(event);
- switch ( pWall->type )
- {
- default:
- pXWall->busy = nBusy;
- if ( (nBusy & kFluxMask) == 0 )
- SetWallState(nWall, pXWall, nBusy >> 16);
- break;
- }
- }
- /*******************************************************************************
- EXPORTED FUNCTIONS
- *******************************************************************************/
- /*******************************************************************************
- FUNCTION: trTriggerSector()
- DESCRIPTION:
- PARAMETERS:
- RETURNS:
- NOTES:
- *******************************************************************************/
- void trTriggerSector( unsigned nSector, XSECTOR *pXSector, int command )
- {
- dprintf("TriggerSector: nSector=%i, command=%i\n", nSector, command);
- // bypass locked XObjects
- if ( pXSector->locked )
- return;
- // bypass triggered one-shots
- if ( pXSector->isTriggered )
- return;
- if ( pXSector->triggerOnce )
- pXSector->isTriggered = 1;
- if ( pXSector->decoupled )
- {
- if ( pXSector->txID != 0 )
- evSend(nSector, SS_SECTOR, pXSector->txID, pXSector->command);
- }
- else
- {
- EVENT event;
- event.command = command;
- // operate the sector
- OperateSector( nSector, pXSector, event );
- }
- }
- /*******************************************************************************
- FUNCTION: trMessageSector()
- DESCRIPTION:
- PARAMETERS:
- RETURNS:
- NOTES:
- *******************************************************************************/
- void trMessageSector( unsigned nSector, EVENT event )
- {
- dassert(sector[nSector].extra > 0 && sector[nSector].extra < kMaxXSectors);
- XSECTOR *pXSector = &xsector[sector[nSector].extra];
- // don't send it to the originator
- /*
- if (event.type == SS_SECTOR && event.index == nSector && !pXSector->decoupled
- && event.command != kCommandCallback )
- return;
- */
- // operate the sector
- if ( event.command == kCommandLink )
- LinkSector( nSector, pXSector, event );
- else
- OperateSector( nSector, pXSector, event );
- }
- /*******************************************************************************
- FUNCTION: trTriggerWall()
- DESCRIPTION:
- PARAMETERS:
- RETURNS:
- NOTES:
- *******************************************************************************/
- void trTriggerWall( unsigned nWall, XWALL *pXWall, int command )
- {
- dprintf("TriggerWall: nWall=%i, command=%i\n", nWall, command);
- // bypass locked XObjects
- if ( pXWall->locked )
- return;
- // bypass triggered one-shots
- if ( pXWall->isTriggered )
- return;
- if ( pXWall->triggerOnce )
- pXWall->isTriggered = 1;
- if ( pXWall->decoupled )
- {
- if ( pXWall->txID != 0 )
- evSend(nWall, SS_WALL, pXWall->txID, pXWall->command);
- }
- else
- {
- EVENT event;
- event.command = command;
- // operate the wall
- OperateWall(nWall, pXWall, event);
- }
- }
- /*******************************************************************************
- FUNCTION: trMessageWall()
- DESCRIPTION:
- PARAMETERS:
- RETURNS:
- NOTES:
- *******************************************************************************/
- void trMessageWall( unsigned nWall, EVENT event )
- {
- dassert(wall[nWall].extra > 0 && wall[nWall].extra < kMaxXWalls);
- XWALL *pXWall = &xwall[wall[nWall].extra];
- // don't send it to the originator
- /*
- if (event.type == SS_WALL && event.index == nWall && !pXWall->decoupled
- && event.command != kCommandCallback )
- return;
- */
- // operate the wall
- if ( event.command == kCommandLink )
- LinkWall( nWall, pXWall, event );
- else
- OperateWall( nWall, pXWall, event );
- }
- /*******************************************************************************
- FUNCTION: trTriggerSprite()
- DESCRIPTION:
- PARAMETERS:
- RETURNS:
- NOTES:
- *******************************************************************************/
- void trTriggerSprite( unsigned nSprite, XSPRITE *pXSprite, int command )
- {
- dprintf("TriggerSprite: nSprite=%i, type= %i command=%i\n", nSprite, sprite[nSprite].type, command);
- // bypass locked XObjects
- if ( pXSprite->locked )
- return;
- // bypass triggered one-shots
- if ( pXSprite->isTriggered )
- return;
- if ( pXSprite->triggerOnce )
- pXSprite->isTriggered = 1;
- if ( pXSprite->decoupled )
- {
- if ( pXSprite->txID != 0 )
- evSend(nSprite, SS_SPRITE, pXSprite->txID, pXSprite->command);
- }
- else
- {
- EVENT event;
- event.command = command;
- // operate the sprite
- OperateSprite(nSprite, pXSprite, event);
- }
- }
- /*******************************************************************************
- FUNCTION: trMessageSprite()
- DESCRIPTION:
- PARAMETERS:
- RETURNS:
- NOTES:
- *******************************************************************************/
- void trMessageSprite( unsigned nSprite, EVENT event )
- {
- // return immediately if sprite has been deleted
- if ( sprite[nSprite].statnum == kMaxStatus )
- return;
- dassert(sprite[nSprite].extra > 0 && sprite[nSprite].extra < kMaxXSprites);
- XSPRITE *pXSprite = &xsprite[sprite[nSprite].extra];
- // don't send it to the originator
- /*
- if (event.type == SS_SPRITE && event.index == nSprite && !pXSprite->decoupled
- && event.command != kCommandCallback )
- return;
- */
- // operate the sprite
- if ( event.command == kCommandLink )
- LinkSprite( nSprite, pXSprite, event );
- else
- OperateSprite( nSprite, pXSprite, event );
- }
- /*******************************************************************************
- FUNCTION: trProcessBusy()
- DESCRIPTION:
- PARAMETERS:
- RETURNS:
- NOTES:
- *******************************************************************************/
- void trProcessBusy( void )
- {
- int i;
- for ( i = gBusyCount - 1; i >= 0; i-- )
- {
- // temporarily store nBusy before normalization
- int tempBusy = gBusy[i].nBusy;
- gBusy[i].nBusy = ClipRange(gBusy[i].nBusy + gBusy[i].nDelta * kFrameTicks, 0, kMaxBusyValue);
- int rcode = gBusy[i].busyProc( gBusy[i].nIndex, gBusy[i].nBusy );
- switch (rcode)
- {
- case kBusyRestore:
- gBusy[i].nBusy = tempBusy; // restore previous nBusy
- break;
- case kBusyReverse:
- gBusy[i].nBusy = tempBusy; // restore previous nBusy
- gBusy[i].nDelta = -gBusy[i].nDelta; // reverse delta
- break;
- case kBusyComplete:
- gBusyCount--;
- gBusy[i] = gBusy[gBusyCount];
- break;
- }
- }
- }
- /*******************************************************************************
- FUNCTION: trInit()
- DESCRIPTION: Initialize the trigger system.
- PARAMETERS:
- RETURNS:
- NOTES: Called in prepareboard, AFTER call to dbLoadMap
- *******************************************************************************/
- void trInit( void )
- {
- int i;
- int nSector, nWall, nSprite;
- gBusyCount = 0;
- // get wall vertice positions
- for (nWall = 0; nWall < numwalls; nWall++)
- {
- kwall[nWall].x = wall[nWall].x;
- kwall[nWall].y = wall[nWall].y;
- }
- // get sprite positions
- for ( nSprite = 0; nSprite < kMaxSprites; nSprite++ )
- {
- if ( sprite[nSprite].statnum < kMaxStatus )
- {
- ksprite[nSprite].x = sprite[nSprite].x;
- ksprite[nSprite].y = sprite[nSprite].y;
- ksprite[nSprite].z = sprite[nSprite].z;
- }
- }
- // init wall trigger masks (must be done first)
- for (nWall = 0; nWall < numwalls; nWall++)
- {
- if ( wall[nWall].extra > 0 )
- {
- int nXWall = wall[nWall].extra;
- dassert(nXWall < kMaxXWalls);
- XWALL *pXWall = &xwall[nXWall];
- if ( pXWall->state )
- pXWall->busy = kMaxBusyValue;
- switch( wall[nWall].type )
- {
- default:
- break;
- }
- }
- }
- // init sector trigger masks
- dassert((numsectors >= 0) && (numsectors < kMaxSectors));
- for ( nSector = 0; nSector < numsectors; nSector++ )
- {
- SECTOR *pSector = §or[nSector];
- int nXSector = pSector->extra;
- if ( nXSector > 0 )
- {
- dassert(nXSector < kMaxXSectors);
- XSECTOR *pXSector = &xsector[nXSector];
- if ( pXSector->state )
- pXSector->busy = kMaxBusyValue;
- int oldFloorZ, oldCeilZ;
- SPRITE *pMark0 = NULL, *pMark1 = NULL;
- switch( pSector->type )
- {
- case kSectorTeleport:
- pXSector->data = -1;
- break;
- case kSectorZSprite: // it's probabyl okay to start this one with the on or off z
- case kSectorZMotion:
- case kSectorZCrusher:
- oldFloorZ = pSector->floorz;
- oldCeilZ = pSector->ceilingz;
- // open or close door as necessary for initial state
- if ( pXSector->state == 0 )
- {
- pSector->ceilingz = pXSector->offCeilZ;
- pSector->floorz = pXSector->offFloorZ;
- }
- else
- {
- pSector->ceilingz = pXSector->onCeilZ;
- pSector->floorz = pXSector->onFloorZ;
- }
- // adjust the z for any floor relative sprites or face sprites in the floor
- for (nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
- {
- SPRITE *pSprite = &sprite[nSprite];
- if ( pSprite->cstat & kSpriteMoveFloor ||
- ( !(pSprite->cstat & kSpriteRMask) && pSprite->z >= oldFloorZ) )
- pSprite->z += pSector->floorz - oldFloorZ;
- }
- // adjust the z for any ceiling relative sprites or face sprites in the ceiling
- for (nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
- {
- SPRITE *pSprite = &sprite[nSprite];
- if ( pSprite->cstat & kSpriteMoveCeiling ||
- ( !(pSprite->cstat & kSpriteRMask) && pSprite->z <= oldCeilZ) )
- pSprite->z += pSector->ceilingz - oldCeilZ;
- }
- break;
- case kSectorSlideMarked:
- case kSectorSlide:
- case kSectorSlideCrush:
- pMark0 = &sprite[pXSector->marker0];
- pMark1 = &sprite[pXSector->marker1];
- // move door to off position by reversing markers
- TranslateSector(nSector, 0, 0x10000,
- pMark1->x, pMark1->y, pMark1->ang, pMark0->x, pMark0->y, pMark0->ang,
- pSector->type == kSectorSlide);
- // grab updated positions of walls
- for (i = 0; i < pSector->wallnum; i++)
- {
- int nWall = pSector->wallptr + i;
- kwall[nWall].x = wall[nWall].x;
- kwall[nWall].y = wall[nWall].y;
- }
- // grab updated positions of sprites
- for (nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
- {
- ksprite[nSprite].x = sprite[nSprite].x;
- ksprite[nSprite].y = sprite[nSprite].y;
- ksprite[nSprite].z = sprite[nSprite].z;
- }
- // open door if necessary
- TranslateSector(nSector, 0, pXSector->busy,
- pMark0->x, pMark0->y, pMark0->ang, pMark1->x, pMark1->y, pMark1->ang,
- pSector->type == kSectorSlide);
- break;
- case kSectorRotateMarked:
- case kSectorRotate:
- case kSectorRotateCrush:
- pMark0 = &sprite[pXSector->marker0];
- // move door to off position
- TranslateSector(nSector, 0, -0x10000,
- pMark0->x, pMark0->y, 0, pMark0->x, pMark0->y, pMark0->ang,
- pSector->type == kSectorRotate);
- // grab updated positions of walls
- for (i = 0; i < pSector->wallnum; i++)
- {
- int nWall = pSector->wallptr + i;
- kwall[nWall].x = wall[nWall].x;
- kwall[nWall].y = wall[nWall].y;
- }
- // grab updated positions of sprites
- for (nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
- {
- ksprite[nSprite].x = sprite[nSprite].x;
- ksprite[nSprite].y = sprite[nSprite].y;
- ksprite[nSprite].z = sprite[nSprite].z;
- }
- // open door if necessary
- TranslateSector(nSector, 0, pXSector->busy,
- pMark0->x, pMark0->y, 0, pMark0->x, pMark0->y, pMark0->ang,
- pSector->type == kSectorRotate);
- break;
- default:
- break;
- }
- }
- }
- // init sprite trigger masks
- for ( nSprite = 0; nSprite < kMaxSprites; nSprite++ )
- {
- int nXSprite = sprite[nSprite].extra;
- if ( sprite[nSprite].statnum < kMaxStatus && nXSprite > 0 )
- {
- dassert(nXSprite < kMaxXSprites);
- XSPRITE *pXSprite = &xsprite[nXSprite];
- if ( pXSprite->state )
- pXSprite->busy = kMaxBusyValue;
- // special initialization for implicit trigger types
- switch ( sprite[nSprite].type )
- {
- case kSwitchPadlock:
- pXSprite->triggerOnce = 1; // force trigger once
- break;
- case kGenTrigger:
- case kGenWaterDrip:
- case kGenBloodDrip:
- case kGenFireball:
- case kGenEctoSkull:
- case kGenDart:
- case kGenSound:
- InitGenerator( nSprite );
- break;
- case kThingTNTProxArmed:
- pXSprite->triggerProximity = 1;
- break;
- }
- if ( pXSprite->triggerPush || pXSprite->triggerImpact )
- sprite[nSprite].cstat |= kSpriteHitscan;
- /*******************************************************************************
- Proximity sensors are being put on a separate list here, but this presents a
- problem, since this means they can't be on any other list, such as the Thing
- list. This prevents the proximity bombs from being initialized properly.
- How do we make them affected by explosions?
- *******************************************************************************/
- if ( pXSprite->triggerProximity )
- actPostSprite( nSprite, kStatProximity );
- // changespritestat((short)nSprite, kStatProximity);
- switch( sprite[nSprite].type )
- {
- default:
- break;
- }
- }
- }
- evSend( 0, 0, kChannelTriggerStart, kCommandOn );
- if (gNetMode == kNetModeCoop)
- evSend( 0, 0, kChannelTriggerCoop, kCommandOn );
- else if (gNetMode == kNetModeBloodBath || gNetMode == kNetModeTeams)
- evSend( 0, 0, kChannelTriggerMatch, kCommandOn );
- }
- /*******************************************************************************
- FUNCTION: trTextOver()
- DESCRIPTION:
- PARAMETERS:
- RETURNS:
- NOTES:
- *******************************************************************************/
- void trTextOver( int textId )
- {
- if ( gLevelMessage[textId] != NULL )
- viewSetMessage(gLevelMessage[textId]);
- }
- /*******************************************************************************
- FUNCTION: trLightning()
- DESCRIPTION:
- PARAMETERS:
- RETURNS:
- NOTES:
- *******************************************************************************/
- void trLightning( int nForkID )
- {
- dassert( nForkID >= 0 && nForkID < kMaxLightning );
- dprintf( "trLightning: nForkID = %i\n", nForkID );
- int nSlot = gLightningInfo[ nForkID ].slot;
- dprintf( "trLightning: nSlot = %i\n", nSlot );
- if ( nSlot != -1 )
- {
- dassert( nSlot >= 0 && nSlot < kMaxSkyTiles );
- dprintf( "trLightning: gLightningInfo[ %i ].offset = %i\n", nForkID, gLightningInfo[ nForkID ].offset );
- pskyoff[ nSlot ] = (short)gLightningInfo[ nForkID ].offset;
- }
- }
- /*******************************************************************************
- FUNCTION: InitGenerator()
- DESCRIPTION:
- PARAMETERS:
- RETURNS:
- NOTES:
- *******************************************************************************/
- static void InitGenerator( int nSprite )
- {
- dassert(nSprite < kMaxSprites);
- SPRITE *pSprite = &sprite[nSprite];
- dassert(pSprite->statnum != kMaxStatus);
- int nXSprite = pSprite->extra;
- dassert(nXSprite > 0);
- XSPRITE *pXSprite = &xsprite[nXSprite];
- dassert(pXSprite->busyTime > 0); // should be > 0 for timer events
- switch ( sprite[nSprite].type )
- {
- case kGenTrigger:
- pSprite->cstat &= ~kSpriteBlocking;
- pSprite->cstat |= kSpriteInvisible;
- break;
- default:
- break; //return;
- }
- pXSprite->data3 = FALSE;
- if ( pXSprite->state != pXSprite->restState && pXSprite->busyTime > 0 )
- {
- pXSprite->data3 = TRUE;
- evPost(nSprite, SS_SPRITE, (pXSprite->busyTime + BiRandom(pXSprite->data1)) * kTimerRate / 10);
- }
- }
- /*******************************************************************************
- FUNCTION: ActivateGenerator()
- DESCRIPTION:
- PARAMETERS:
- RETURNS:
- NOTES:
- *******************************************************************************/
- static void ActivateGenerator( int nSprite )
- {
- dassert(nSprite < kMaxSprites);
- SPRITE *pSprite = &sprite[nSprite];
- dassert(pSprite->statnum != kMaxStatus);
- int nXSprite = pSprite->extra;
- dassert(nXSprite > 0);
- XSPRITE *pXSprite = &xsprite[nXSprite];
- int zTop, zBot;
- switch ( pSprite->type )
- {
- case kGenWaterDrip:
- GetSpriteExtents(pSprite, &zTop, &zBot);
- actSpawnThing( pSprite->sectnum, pSprite->x, pSprite->y, zBot, kThingWaterDrip);
- break;
- case kGenBloodDrip:
- GetSpriteExtents(pSprite, &zTop, &zBot);
- actSpawnThing( pSprite->sectnum, pSprite->x, pSprite->y, zBot, kThingBloodDrip);
- break;
- case kGenSound:
- sfxCreate3DSound(pSprite->x, pSprite->y, pSprite->z, pXSprite->soundId);
- break;
- case kGenFireball:
- switch(pXSprite->data2)
- {
- case 0:
- FireballTrapCallback( SS_SPRITE, nXSprite );
- break;
- case 1:
- seqSpawn(kSeqFireTrap1, SS_SPRITE, nXSprite, FireballTrapCallback);
- break;
- case 2:
- seqSpawn(kSeqFireTrap2, SS_SPRITE, nXSprite, FireballTrapCallback);
- break;
- }
- break;
- case kGenEctoSkull:
- case kGenDart:
- // spawn a missile that moves and damages accordingly
- break;
- default:
- break;
- }
- }
- static void FireballTrapCallback( int /* type */, int nXIndex )
- {
- XSPRITE *pXSprite = &xsprite[nXIndex];
- int nSprite = pXSprite->reference;
- SPRITE *pSprite = &sprite[nSprite];
- int dx, dy, dz;
- if ( pSprite->cstat & kSpriteFloor ) // floor sprite
- {
- dx = dy = 0;
- if ( pSprite->cstat & kSpriteFlipY ) // face down floor sprite
- dz = 1 << 14;
- else // face up floor sprite
- dz = -1 << 14;
- }
- else // wall sprite or face sprite
- {
- dx = Cos(pSprite->ang) >> 16;
- dy = Sin(pSprite->ang) >> 16;
- dz = 0;
- }
- actFireMissile( nSprite, pSprite->z, dx, dy, dz, kMissileFireball);
- }
- static void MGunFireCallback( int /* type */, int nXIndex )
- {
- int nSprite = xsprite[nXIndex].reference;
- SPRITE *pSprite = &sprite[nSprite];
- XSPRITE *pXSprite = &xsprite[nXIndex];
- // if dynamic ammo left or infinite ammo
- if (pXSprite->data2 > 0 || pXSprite->data1 == 0)
- {
- if (pXSprite->data2 > 0)
- {
- pXSprite->data2--; // subtract ammo
- if (pXSprite->data2 == 0)
- evPost(nSprite, SS_SPRITE, 1, kCommandOff); // empty! turn it off
- }
- int dx = (Cos(pSprite->ang) >> 16) + BiRandom(1000);
- int dy = (Sin(pSprite->ang) >> 16) + BiRandom(1000);
- int dz = BiRandom(1000);
- actFireVector(nSprite, pSprite->z, dx, dy, dz, kVectorBullet);
- sfxCreate3DSound(pSprite->x, pSprite->y, pSprite->z, kSfxTomFire);
- }
- }
- static void MGunOpenCallback( int /* type */, int nXIndex )
- {
- seqSpawn(kSeqMGunFire, SS_SPRITE, nXIndex, MGunFireCallback);
- }
|