TRIGGERS.CPP 56 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245
  1. #include <string.h>
  2. #include <stdio.h>
  3. #include "engine.h"
  4. #include "db.h"
  5. #include "globals.h"
  6. #include "eventq.h"
  7. #include "triggers.h"
  8. #include "debug4g.h"
  9. #include "sectorfx.h"
  10. #include "misc.h"
  11. #include "gameutil.h"
  12. #include "trig.h"
  13. #include "screen.h"
  14. #include "actor.h"
  15. #include "seq.h"
  16. #include "error.h"
  17. #include "names.h"
  18. #include "levels.h"
  19. #include "view.h"
  20. #include "sfx.h"
  21. #include "sound.h" // can be removed once sfx complete
  22. #define kRemoteDelay 12 // 0.1 second
  23. #define kProxDelay 30 // 0.25 second
  24. static struct
  25. {
  26. long x, y;
  27. } kwall[kMaxWalls];
  28. static struct
  29. {
  30. long x, y, z;
  31. } ksprite[kMaxSprites];
  32. // trigger effects
  33. enum
  34. {
  35. kSeqClearGlass = 0x80,
  36. kSeqStainedGlass,
  37. kSeqWeb,
  38. kSeqBeam,
  39. kSeqJar,
  40. kSeqWaterOff,
  41. kSeqWaterOn,
  42. kSeqSpike,
  43. kSeqMetalGrate1,
  44. kSeqFireTrap1,
  45. kSeqFireTrap2,
  46. kSeqPadlock,
  47. kSeqMGunOpen,
  48. kSeqMGunFire,
  49. kSeqMGunClose,
  50. kSeqMGunFlare,
  51. kSeqTriggerMax,
  52. };
  53. /*******************************************************************************
  54. DOOR STATES
  55. OFF means closed
  56. ON means open
  57. Doors should be created in map edit in the open position, since it is easier to
  58. computationally determine closed than open. The trInit function should set
  59. the initial positions of the doors based on their state flags.
  60. *******************************************************************************/
  61. /*******************************************************************************
  62. Busy System Constants
  63. *******************************************************************************/
  64. enum {
  65. kBusyOk = 0,
  66. kBusyRestore,
  67. kBusyReverse,
  68. kBusyComplete,
  69. };
  70. typedef int (*BUSYPROC)( int nIndex, int nBusy );
  71. struct BUSY {
  72. int nIndex;
  73. int nDelta;
  74. int nBusy;
  75. BUSYPROC busyProc;
  76. };
  77. #define kMaxBusyArray 128
  78. int gBusyCount = 0;
  79. BUSY gBusy[kMaxBusyArray];
  80. /*******************************************************************************
  81. LOCAL FUNCTION PROTOTYPES
  82. *******************************************************************************/
  83. static void InitGenerator( int nSprite );
  84. static void ActivateGenerator( int nSprite );
  85. static void FireballTrapCallback( int /* type */, int nXIndex );
  86. static void MGunFireCallback( int /* type */, int nXIndex );
  87. static void MGunOpenCallback( int /* type */, int nXIndex );
  88. /*******************************************************************************
  89. LOCAL HELPER FUNCTIONS
  90. *******************************************************************************/
  91. /*******************************************************************************
  92. FUNCTION: Lin2Sin()
  93. DESCRIPTION: Convert a linear busy ramp to a sinusoidal ramp
  94. *******************************************************************************/
  95. inline int Lin2Sin( int nBusy )
  96. {
  97. return (1 << 15) - (Cos(nBusy * kAngle180 / kMaxBusyValue) >> 15);
  98. }
  99. /*******************************************************************************
  100. FUNCTION: SetSpriteState()
  101. DESCRIPTION: Change the state of an xsprite, and broadcast message if
  102. necessary.
  103. *******************************************************************************/
  104. static void SetSpriteState( int nSprite, XSPRITE *pXSprite, int state )
  105. {
  106. if ( (pXSprite->busy & kFluxMask) == 0 && pXSprite->state == state )
  107. return;
  108. pXSprite->busy = state << 16;
  109. pXSprite->state = state;
  110. if ( state != pXSprite->restState && pXSprite->waitTime > 0 )
  111. evPost(nSprite, SS_SPRITE, pXSprite->waitTime * kTimerRate / 10,
  112. pXSprite->restState ? kCommandOn : kCommandOff);
  113. if (pXSprite->command == kCommandLink)
  114. return;
  115. if ( pXSprite->txID != 0 )
  116. {
  117. if (pXSprite->triggerOn && pXSprite->state == 1)
  118. {
  119. dprintf( "evSend(%i, SS_SPRITE, %i, %i)\n", nSprite, pXSprite->txID, pXSprite->command );
  120. evSend(nSprite, SS_SPRITE, pXSprite->txID, pXSprite->command);
  121. }
  122. if (pXSprite->triggerOff && pXSprite->state == 0)
  123. {
  124. dprintf( "evSend(%i, SS_SPRITE, %i, %i)\n", nSprite, pXSprite->txID, pXSprite->command );
  125. evSend(nSprite, SS_SPRITE, pXSprite->txID, pXSprite->command);
  126. }
  127. }
  128. }
  129. /*******************************************************************************
  130. FUNCTION: SetWallState()
  131. DESCRIPTION: Change the state of an xwall, and broadcast message if
  132. necessary.
  133. *******************************************************************************/
  134. static void SetWallState( int nWall, XWALL *pXWall, int state )
  135. {
  136. if ( (pXWall->busy & kFluxMask) == 0 && pXWall->state == state )
  137. return;
  138. pXWall->busy = state << 16;
  139. pXWall->state = state;
  140. if ( state != pXWall->restState && pXWall->waitTime > 0 )
  141. evPost(nWall, SS_WALL, pXWall->waitTime * kTimerRate / 10,
  142. pXWall->restState ? kCommandOn : kCommandOff);
  143. if (pXWall->command == kCommandLink)
  144. return;
  145. if ( pXWall->txID != 0 )
  146. {
  147. if (pXWall->triggerOn && pXWall->state == 1)
  148. {
  149. dprintf( "evSend(%i,SS_WALL, %i, %i)\n", nWall, pXWall->txID, pXWall->command );
  150. evSend(nWall, SS_WALL, pXWall->txID, pXWall->command);
  151. }
  152. if (pXWall->triggerOff && pXWall->state == 0)
  153. {
  154. dprintf( "evSend(%i,SS_WALL, %i, %i)\n", nWall, pXWall->txID, pXWall->command );
  155. evSend(nWall, SS_WALL, pXWall->txID, pXWall->command);
  156. }
  157. }
  158. }
  159. /*******************************************************************************
  160. FUNCTION: SetSectorState()
  161. DESCRIPTION: Change the state of an xsector, and broadcast message if
  162. necessary.
  163. *******************************************************************************/
  164. static void SetSectorState( int nSector, XSECTOR *pXSector, int state )
  165. {
  166. if ( (pXSector->busy & kFluxMask) == 0 && pXSector->state == state )
  167. return;
  168. pXSector->busy = state << 16;
  169. pXSector->state = state;
  170. if ( state != pXSector->restState && pXSector->waitTime > 0 )
  171. evPost(nSector, SS_SECTOR, pXSector->waitTime * kTimerRate / 10,
  172. pXSector->restState ? kCommandOn : kCommandOff);
  173. if (pXSector->command == kCommandLink)
  174. return;
  175. if ( pXSector->txID != 0 )
  176. {
  177. if (pXSector->triggerOn && pXSector->state == 1)
  178. {
  179. dprintf( "evSend(%i, SS_SECTOR, %i, %i)\n", nSector, pXSector->txID, pXSector->command );
  180. evSend(nSector, SS_SECTOR, pXSector->txID, pXSector->command);
  181. }
  182. if (pXSector->triggerOff && pXSector->state == 0)
  183. {
  184. dprintf( "evSend(%i, SS_SECTOR, %i, %i)\n", nSector, pXSector->txID, pXSector->command );
  185. evSend(nSector, SS_SECTOR, pXSector->txID, pXSector->command);
  186. }
  187. }
  188. }
  189. /*******************************************************************************
  190. FUNCTION: AddBusy()
  191. DESCRIPTION: Add a busy process. The callback procedure will be called
  192. once per frame with the updated busy value.
  193. PARAMETERS: nIndex is used to identify the xobject to the callback
  194. function.
  195. NOTES: if nDelta is < 0, the busy value will count down from
  196. kMaxBusyValue.
  197. *******************************************************************************/
  198. static void AddBusy( int nIndex, BUSYPROC busyProc, int nDelta )
  199. {
  200. dassert(nDelta != 0);
  201. dassert(busyProc != NULL);
  202. // find an existing nIndex busy, or an unused busy slot
  203. for (int i = 0; i < gBusyCount; i++)
  204. {
  205. if ((nIndex == gBusy[i].nIndex) && (busyProc == gBusy[i].busyProc))
  206. break;
  207. }
  208. // adding a new busy?
  209. if (i == gBusyCount)
  210. {
  211. if ( gBusyCount == kMaxBusyArray )
  212. {
  213. dprintf("OVERFLOW: AddBusy() ignored\n");
  214. return;
  215. };
  216. gBusy[i].nIndex = nIndex;
  217. gBusy[i].busyProc = busyProc;
  218. gBusy[i].nBusy = (nDelta > 0) ? 0 : kMaxBusyValue;
  219. gBusyCount++;
  220. }
  221. gBusy[i].nDelta = nDelta;
  222. }
  223. /*******************************************************************************
  224. FUNCTION: ReverseBusy()
  225. DESCRIPTION: Reverse the delta of a busy process. This is used to
  226. change the direction of a process from outside the
  227. callback.
  228. *******************************************************************************/
  229. void ReverseBusy( int nIndex, BUSYPROC busyProc )
  230. {
  231. dassert(busyProc != NULL);
  232. // find an existing nIndex busy, or an unused busy slot
  233. for (int i = 0; i < gBusyCount; i++)
  234. {
  235. if ( nIndex == gBusy[i].nIndex && busyProc == gBusy[i].busyProc )
  236. {
  237. gBusy[i].nDelta = -gBusy[i].nDelta;
  238. return;
  239. }
  240. }
  241. dprintf("ReverseBusy: matching busy not found!\n");
  242. }
  243. /*******************************************************************************
  244. FUNCTION: GetSourceBusy()
  245. DESCRIPTION: Get the busy value of the object that originated the event.
  246. This is used for kCommandLink messages.
  247. *******************************************************************************/
  248. static unsigned GetSourceBusy( EVENT event )
  249. {
  250. int nXIndex;
  251. switch ( event.type )
  252. {
  253. case SS_SECTOR:
  254. nXIndex = sector[event.index].extra;
  255. dassert(nXIndex > 0 && nXIndex < kMaxXSectors);
  256. return xsector[nXIndex].busy;
  257. case SS_WALL:
  258. nXIndex = wall[event.index].extra;
  259. dassert(nXIndex > 0 && nXIndex < kMaxXWalls);
  260. return xwall[nXIndex].busy;
  261. case SS_SPRITE:
  262. nXIndex = sprite[event.index].extra;
  263. dassert(nXIndex > 0 && nXIndex < kMaxXSprites);
  264. return xsprite[nXIndex].busy;
  265. }
  266. // shouldn't reach this point
  267. return 0;
  268. }
  269. /*******************************************************************************
  270. FUNCTION: OperateSprite()
  271. DESCRIPTION:
  272. PARAMETERS:
  273. RETURNS:
  274. NOTES:
  275. *******************************************************************************/
  276. void OperateSprite( int nSprite, XSPRITE *pXSprite, EVENT event )
  277. {
  278. SPRITE *pSprite = &sprite[nSprite];
  279. // handle respawns first, to avoid toggling the sprite state
  280. if ( event.command == kCommandRespawn)
  281. {
  282. actRespawnSprite( (short)nSprite );
  283. switch (pSprite->type)
  284. {
  285. case kThingTNTBarrel:
  286. pSprite->cstat |= kSpriteBlocking | kSpriteHitscan;
  287. break;
  288. }
  289. return;
  290. }
  291. // special handling for lock/unlock commands
  292. switch ( event.command )
  293. {
  294. case kCommandLock:
  295. pXSprite->locked = 1;
  296. return;
  297. case kCommandUnlock:
  298. pXSprite->locked = 0;
  299. return;
  300. case kCommandToggleLock:
  301. pXSprite->locked ^= 1;
  302. return;
  303. }
  304. switch (pSprite->type)
  305. {
  306. case kMissileStarburstFlare:
  307. {
  308. if ( sprite[nSprite].statnum != kStatMissile) // don't trigger if already exploded
  309. break;
  310. pSprite->type = kMissileExplodingFlare;
  311. int nVel = (M2X(2.0) << 4) / kTimerRate; // meters per second
  312. int nAngle = getangle(pSprite->xvel, pSprite->yvel);
  313. for (int i = 0; i < 16; i++)
  314. {
  315. int nBurst = actCloneSprite( pSprite );
  316. dbInsertXSprite( nBurst );
  317. long dxVel = 0;
  318. long dyVel = mulscale30r(nVel, Cos(i * kAngle360 / 16));
  319. long dzVel = mulscale30r(nVel, Sin(i * kAngle360 / 16));
  320. if (i & 1)
  321. {
  322. dyVel >>= 1;
  323. dzVel >>= 1;
  324. }
  325. RotateVector( &dxVel, &dyVel, nAngle );
  326. SPRITE *pBurst = &sprite[nBurst];
  327. pBurst->xvel += dxVel;
  328. pBurst->yvel += dyVel;
  329. pBurst->zvel += dzVel;
  330. }
  331. break;
  332. }
  333. // case kMissileExplodingFlare:
  334. case kMissileFlare:
  335. actPostSprite( nSprite, kStatFree );
  336. break;
  337. case kThingMachineGun:
  338. if (pXSprite->health > 0)
  339. {
  340. if ( event.command == kCommandOn )
  341. {
  342. if (pXSprite->state == 0)
  343. {
  344. SetSpriteState(nSprite, pXSprite, 1);
  345. seqSpawn(kSeqMGunOpen, SS_SPRITE, pSprite->extra, MGunOpenCallback);
  346. //sfxStart3DSound(pSprite->extra, kSfxSwitch2);
  347. // data1 = max ammo (defaults to infinite)
  348. // data2 = dynamic ammo if data1 > 0
  349. if (pXSprite->data1 > 0)
  350. pXSprite->data2 = pXSprite->data1;
  351. }
  352. }
  353. else if ( event.command == kCommandOff )
  354. {
  355. if (pXSprite->state)
  356. {
  357. SetSpriteState(nSprite, pXSprite, 0);
  358. seqSpawn(kSeqMGunClose, SS_SPRITE, pSprite->extra);
  359. //sfxStart3DSound(pSprite->extra, kSfxSwitch2);
  360. }
  361. }
  362. }
  363. break;
  364. case kThingWallCrack:
  365. SetSpriteState(nSprite, pXSprite, 0);
  366. actPostSprite( nSprite, kStatFree );
  367. break;
  368. case kThingCrateFace:
  369. SetSpriteState(nSprite, pXSprite, 0);
  370. actPostSprite( nSprite, kStatFree );
  371. break;
  372. case kTrapPoweredZap:
  373. switch( event.command )
  374. {
  375. case kCommandOff:
  376. dprintf("zap off\n");
  377. pXSprite->state = 0;
  378. pSprite->cstat |= kSpriteInvisible;
  379. pSprite->cstat &= ~kSpriteBlocking;
  380. break;
  381. case kCommandOn:
  382. dprintf("zap on\n");
  383. pXSprite->state = 1;
  384. pSprite->cstat &= ~kSpriteInvisible;
  385. pSprite->cstat |= kSpriteBlocking;
  386. break;
  387. case kCommandToggle:
  388. dprintf("zap toggle\n");
  389. pXSprite->state ^= 1;
  390. pSprite->cstat ^= kSpriteInvisible;
  391. pSprite->cstat ^= kSpriteBlocking;
  392. break;
  393. }
  394. break;
  395. case kSwitchPadlock:
  396. switch( event.command )
  397. {
  398. case kCommandOff:
  399. SetSpriteState(nSprite, pXSprite, 0);
  400. break;
  401. case kCommandOn:
  402. SetSpriteState(nSprite, pXSprite, 1);
  403. seqSpawn(kSeqPadlock, SS_SPRITE, pSprite->extra);
  404. sfxStart3DSound(pSprite->extra, kSfxChains);
  405. break;
  406. default:
  407. SetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1);
  408. if (pXSprite->state)
  409. {
  410. seqSpawn(kSeqPadlock, SS_SPRITE, pSprite->extra);
  411. sfxStart3DSound(pSprite->extra, kSfxChains);
  412. }
  413. break;
  414. }
  415. break;
  416. case kSwitchMomentary:
  417. switch( event.command )
  418. {
  419. case kCommandOff:
  420. SetSpriteState(nSprite, pXSprite, 0);
  421. break;
  422. case kCommandOn:
  423. SetSpriteState(nSprite, pXSprite, 1);
  424. break;
  425. default:
  426. SetSpriteState(nSprite, pXSprite, pXSprite->restState ^ 1);
  427. break;
  428. }
  429. sfxStart3DSound(pSprite->extra, kSfxSwitch2);
  430. break;
  431. case kSwitchCombination:
  432. switch( event.command )
  433. {
  434. case kCommandOff:
  435. if (--pXSprite->data1 < 0) // underflow?
  436. pXSprite->data1 += pXSprite->data3;
  437. break;
  438. default:
  439. if (++pXSprite->data1 >= pXSprite->data3) // overflow?
  440. pXSprite->data1 -= pXSprite->data3;
  441. break;
  442. }
  443. // handle master switches
  444. if ( pXSprite->command == kCommandLink && pXSprite->txID != 0 )
  445. evSend(nSprite, SS_SPRITE, pXSprite->txID, kCommandLink);
  446. // at right combination?
  447. if (pXSprite->data1 == pXSprite->data2)
  448. SetSpriteState(nSprite, pXSprite, 1);
  449. else
  450. SetSpriteState(nSprite, pXSprite, 0);
  451. break;
  452. case kThingTNTStick:
  453. case kThingTNTBundle:
  454. case kThingTNTBarrel:
  455. if ( sprite[nSprite].statnum != kStatRespawn ) // don't trigger if waiting to be respawned!
  456. actExplodeSprite((short)nSprite);
  457. break;
  458. case kThingTNTRemArmed:
  459. if ( sprite[nSprite].statnum == kStatRespawn )
  460. break;
  461. switch ( event.command )
  462. {
  463. case kCommandOn:
  464. sfxStart3DSound(pSprite->extra, kSfxTNTDetRemote);
  465. evPost(nSprite, SS_SPRITE, kRemoteDelay);
  466. break;
  467. default:
  468. actExplodeSprite((short)nSprite);
  469. break;
  470. }
  471. break;
  472. case kThingTNTProxArmed:
  473. if ( sprite[nSprite].statnum == kStatRespawn )
  474. break;
  475. switch ( event.command )
  476. {
  477. case kCommandSpriteProximity:
  478. if ( pXSprite->state ) // don't trigger it if already triggered
  479. break;
  480. sfxStart3DSound(pSprite->extra, kSfxTNTDetProx);
  481. evPost(nSprite, SS_SPRITE, kProxDelay, kCommandOff);
  482. pXSprite->state = 1;
  483. break;
  484. case kCommandCallback:
  485. sfxStart3DSound(pSprite->extra, kSfxTNTArmProx);
  486. actPostSprite(nSprite, kStatProximity); // activate it by changing list
  487. break;
  488. default:
  489. actExplodeSprite((short)nSprite);
  490. break;
  491. }
  492. break;
  493. case kGenTrigger:
  494. case kGenWaterDrip:
  495. case kGenBloodDrip:
  496. case kGenFireball:
  497. case kGenEctoSkull:
  498. case kGenDart:
  499. case kGenSound:
  500. switch( event.command )
  501. {
  502. case kCommandCallback:
  503. pXSprite->data3 = FALSE; // prevent callback reentrancy
  504. if ( pXSprite->state )
  505. {
  506. if ( pSprite->type != kGenTrigger )
  507. ActivateGenerator( nSprite );
  508. if ( pXSprite->txID != 0 )
  509. evSend(nSprite, SS_SPRITE, pXSprite->txID, pXSprite->command);
  510. if ( pXSprite->busyTime > 0 )
  511. {
  512. pXSprite->data3 = TRUE;
  513. evPost(nSprite, SS_SPRITE, (pXSprite->busyTime + BiRandom(pXSprite->data1)) * kTimerRate / 10);
  514. }
  515. }
  516. break;
  517. case kCommandOff:
  518. SetSpriteState(nSprite, pXSprite, 0);
  519. break;
  520. default:
  521. if ( event.command == kCommandOn )
  522. SetSpriteState(nSprite, pXSprite, 1);
  523. else
  524. SetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1);
  525. if ( pXSprite->state && pXSprite->data3 == FALSE )
  526. {
  527. pXSprite->data3 = TRUE;
  528. evPost(nSprite, SS_SPRITE, 0); // trigger immediately
  529. }
  530. break;
  531. }
  532. break;
  533. default:
  534. switch( event.command )
  535. {
  536. case kCommandOff:
  537. SetSpriteState(nSprite, pXSprite, 0);
  538. break;
  539. case kCommandOn:
  540. SetSpriteState(nSprite, pXSprite, 1);
  541. break;
  542. default:
  543. SetSpriteState(nSprite, pXSprite, pXSprite->state ^ 1);
  544. break;
  545. }
  546. break;
  547. }
  548. }
  549. /*******************************************************************************
  550. FUNCTION: OperateWall()
  551. DESCRIPTION:
  552. PARAMETERS:
  553. RETURNS:
  554. NOTES:
  555. *******************************************************************************/
  556. void OperateWall( int nWall, XWALL *pXWall, EVENT event )
  557. {
  558. WALL *pWall = &wall[nWall];
  559. // special handling for lock/unlock commands
  560. switch ( event.command )
  561. {
  562. case kCommandLock:
  563. pXWall->locked = 1;
  564. return;
  565. case kCommandUnlock:
  566. pXWall->locked = 0;
  567. return;
  568. case kCommandToggleLock:
  569. pXWall->locked ^= 1;
  570. return;
  571. }
  572. switch (pWall->type)
  573. {
  574. case kWallClearGlass:
  575. if ( pXWall->state == 0 )
  576. {
  577. SetWallState(nWall, pXWall, 1);
  578. seqSpawn(kSeqClearGlass, SS_MASKED, wall[nWall].extra);
  579. sndStartSample( "glasshit2", 64 );
  580. }
  581. break;
  582. case kWallStainedGlass:
  583. if ( pXWall->state == 0 )
  584. {
  585. SetWallState(nWall, pXWall, 1);
  586. seqSpawn(kSeqStainedGlass, SS_MASKED, wall[nWall].extra);
  587. sndStartSample( "glasshit2", 64 );
  588. }
  589. break;
  590. case kWallWoodBeams:
  591. if ( pXWall->state == 0 )
  592. {
  593. SetWallState(nWall, pXWall, 1);
  594. seqSpawn(kSeqBeam, SS_MASKED, wall[nWall].extra);
  595. }
  596. break;
  597. case kWallWeb:
  598. if ( pXWall->state == 0 )
  599. {
  600. SetWallState(nWall, pXWall, 1);
  601. seqSpawn(kSeqWeb, SS_MASKED, wall[nWall].extra);
  602. }
  603. break;
  604. case kWallMetalGrate1:
  605. if ( pXWall->state == 0 )
  606. {
  607. SetWallState(nWall, pXWall, 1);
  608. seqSpawn(kSeqMetalGrate1, SS_MASKED, wall[nWall].extra);
  609. }
  610. break;
  611. default:
  612. switch( event.command )
  613. {
  614. case kCommandOff:
  615. SetWallState(nWall, pXWall, 0);
  616. break;
  617. case kCommandOn:
  618. SetWallState(nWall, pXWall, 1);
  619. break;
  620. default:
  621. SetWallState(nWall, pXWall, pXWall->state ^ 1);
  622. break;
  623. }
  624. break;
  625. }
  626. }
  627. void TranslateSector( int nSector, int i0, int i1, int x0, int y0, int a0,
  628. int x1, int y1, int a1, BOOL fAllWalls )
  629. {
  630. XSECTOR *pXSector = &xsector[sector[nSector].extra];
  631. int vX = x1 - x0;
  632. int vY = y1 - y0;
  633. int vA = a1 - a0;
  634. int Xi0 = mulscale16(i0, vX);
  635. int Xi1 = mulscale16(i1, vX);
  636. int dX = Xi1 - Xi0;
  637. int Yi0 = mulscale16(i0, vY);
  638. int Yi1 = mulscale16(i1, vY);
  639. int dY = Yi1 - Yi0;
  640. int Ai0 = mulscale16(i0, vA);
  641. int Ai1 = mulscale16(i1, vA);
  642. int dA = Ai1 - Ai0;
  643. long x, y;
  644. short nWall = sector[nSector].wallptr;
  645. if ( fAllWalls )
  646. {
  647. for (int i = 0; i < sector[nSector].wallnum; nWall++, i++)
  648. {
  649. x = kwall[nWall].x;
  650. y = kwall[nWall].y;
  651. if ( Ai1 )
  652. RotatePoint(&x, &y, Ai1, x0, y0);
  653. // move vertex
  654. dragpoint(nWall, x + Xi1, y + Yi1);
  655. }
  656. }
  657. else
  658. {
  659. for (int i = 0; i < sector[nSector].wallnum; nWall++, i++)
  660. {
  661. short nWall2 = wall[nWall].point2;
  662. x = kwall[nWall].x;
  663. y = kwall[nWall].y;
  664. if ( wall[nWall].cstat & kWallMoveForward )
  665. {
  666. if ( Ai1 )
  667. RotatePoint(&x, &y, Ai1, x0, y0);
  668. dragpoint(nWall, x + Xi1, y + Yi1);
  669. // move next vertex if not explicitly tagged
  670. if ( !(wall[nWall2].cstat & kWallMoveMask) )
  671. {
  672. x = kwall[nWall2].x;
  673. y = kwall[nWall2].y;
  674. if ( Ai1 )
  675. RotatePoint(&x, &y, Ai1, x0, y0);
  676. dragpoint(nWall2, x + Xi1, y + Yi1);
  677. }
  678. }
  679. else if ( wall[nWall].cstat & kWallMoveBackward )
  680. {
  681. if ( Ai1 )
  682. RotatePoint(&x, &y, -Ai1, x0, y0);
  683. dragpoint(nWall, x - Xi1, y - Yi1);
  684. // move next vertex if not explicitly tagged
  685. if ( !(wall[nWall2].cstat & kWallMoveMask) )
  686. {
  687. x = kwall[nWall2].x;
  688. y = kwall[nWall2].y;
  689. if ( Ai1 )
  690. RotatePoint(&x, &y, -Ai1, x0, y0);
  691. dragpoint(nWall2, x - Xi1, y - Yi1);
  692. }
  693. }
  694. }
  695. }
  696. for (short nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
  697. {
  698. SPRITE *pSprite = &sprite[nSprite];
  699. // don't move markers
  700. if ( pSprite->statnum == kStatMarker )
  701. continue;
  702. x = ksprite[nSprite].x;
  703. y = ksprite[nSprite].y;
  704. if ( sprite[nSprite].cstat & kSpriteMoveForward )
  705. {
  706. if ( Ai1 )
  707. RotatePoint(&x, &y, Ai1, x0, y0);
  708. pSprite->ang = (short)((pSprite->ang + dA) & kAngleMask);
  709. pSprite->x = x + Xi1;
  710. pSprite->y = y + Yi1;
  711. }
  712. else if ( sprite[nSprite].cstat & kSpriteMoveReverse )
  713. {
  714. if ( Ai1 )
  715. RotatePoint(&x, &y, -Ai1, x0, y0);
  716. pSprite->ang = (short)((pSprite->ang - dA) & kAngleMask);
  717. pSprite->x = x - Xi1;
  718. pSprite->y = y - Yi1;
  719. }
  720. else if ( pXSector->drag )
  721. {
  722. int zTop, zBot;
  723. GetSpriteExtents(pSprite, &zTop, &zBot);
  724. if ( !(pSprite->cstat & kSpriteRMask) && zBot >= sector[nSector].floorz )
  725. {
  726. // translate relatively (degenerative)
  727. if ( dA )
  728. RotatePoint(&pSprite->x, &pSprite->y, dA, x0 + Xi0, y0 + Yi0);
  729. pSprite->ang = (short)((pSprite->ang + dA) & kAngleMask);
  730. pSprite->x += dX;
  731. pSprite->y += dY;
  732. }
  733. }
  734. }
  735. }
  736. /*******************************************************************************
  737. FUNCTION: GetHighestSprite()
  738. DESCRIPTION: Helper function to return the highest sprite with the
  739. specified statnum equal to nStatus in the sector specified
  740. by nSector.
  741. PARAMETERS: nSector = the sector to search
  742. nStatus = the status number to match, or kMaxStatus = ALL
  743. RETURNS: The sprite number with the highest Z, or -1 = none
  744. NOTES:
  745. *******************************************************************************/
  746. int GetHighestSprite( int nSector, int nStatus, int *hiz )
  747. {
  748. *hiz = sector[nSector].floorz;
  749. int retSprite = -1;
  750. for (short nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
  751. {
  752. if ( sprite[nSprite].statnum == nStatus || nStatus == kMaxStatus)
  753. {
  754. SPRITE *pSprite = &sprite[nSprite];
  755. int zUp, zDown;
  756. GetSpriteExtents(pSprite, &zUp, &zDown);
  757. if ( pSprite->z - zUp < *hiz )
  758. {
  759. *hiz = pSprite->z - zUp;
  760. retSprite = nSprite;
  761. }
  762. }
  763. }
  764. return retSprite;
  765. }
  766. /*******************************************************************************
  767. FUNCTION: VCrushBusy()
  768. DESCRIPTION: Called by trProcessBusy : calculates the new floorz and
  769. ceilingz height based on the nBusy value
  770. PARAMETERS:
  771. RETURNS:
  772. NOTES: This busy proc works for lifts, Top doors, Bottom doors,
  773. and HSplit doors
  774. *******************************************************************************/
  775. int VCrushBusy( int nSector, int nBusy )
  776. {
  777. dassert( nSector >= 0 && nSector < numsectors);
  778. int nXSector = sector[nSector]. extra;
  779. dassert(nXSector > 0 && nXSector < kMaxXSectors);
  780. XSECTOR *pXSector = &xsector[nXSector];
  781. int newCeilZ, newFloorZ;
  782. int dCeilZ = pXSector->onCeilZ - pXSector->offCeilZ;
  783. if ( dCeilZ )
  784. newCeilZ = pXSector->offCeilZ + mulscale16(dCeilZ, Lin2Sin(nBusy));
  785. int dFloorZ = pXSector->onFloorZ - pXSector->offFloorZ;
  786. if ( dFloorZ )
  787. newFloorZ = pXSector->offFloorZ + mulscale16(dFloorZ, Lin2Sin(nBusy));
  788. int z;
  789. int nSprite = GetHighestSprite( nSector, kStatDude, &z );
  790. if ( nSprite >= 0 && newCeilZ >= z )
  791. return kBusyRestore;
  792. if (dCeilZ)
  793. sector[nSector].ceilingz = newCeilZ;
  794. if (dFloorZ)
  795. sector[nSector].floorz = newFloorZ;
  796. pXSector->busy = nBusy;
  797. if ( pXSector->command == kCommandLink && pXSector->txID != 0 )
  798. evSend(nSector, SS_SECTOR, pXSector->txID, kCommandLink);
  799. if ( (nBusy & kFluxMask) == 0 )
  800. {
  801. SetSectorState(nSector, pXSector, nBusy >> 16);
  802. return kBusyComplete;
  803. }
  804. return kBusyOk;
  805. }
  806. /*******************************************************************************
  807. FUNCTION: VSpriteBusy()
  808. DESCRIPTION: Called by trProcessBusy : calculates the new sprite z
  809. based on the nBusy value
  810. PARAMETERS:
  811. RETURNS:
  812. NOTES: This busy proc works for z motion sprites only. The sector
  813. floor and ceiling heights are unaffected.
  814. *******************************************************************************/
  815. int VSpriteBusy( int nSector, int nBusy )
  816. {
  817. dassert( nSector >= 0 && nSector < numsectors);
  818. int nXSector = sector[nSector]. extra;
  819. dassert(nXSector > 0 && nXSector < kMaxXSectors);
  820. XSECTOR *pXSector = &xsector[nXSector];
  821. int floorZRange = pXSector->onFloorZ - pXSector->offFloorZ;
  822. if ( floorZRange != 0 )
  823. {
  824. // adjust the z for any floor relative sprites or face sprites in the floor
  825. for (short nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
  826. {
  827. SPRITE *pSprite = &sprite[nSprite];
  828. if ( pSprite->cstat & kSpriteMoveFloor )
  829. pSprite->z = ksprite[nSprite].z + mulscale16(floorZRange, Lin2Sin(nBusy));
  830. }
  831. }
  832. int ceilZRange = pXSector->onCeilZ - pXSector->offCeilZ;
  833. if ( ceilZRange != 0 )
  834. {
  835. // adjust the z for any ceiling relative sprites or face sprites in the ceiling
  836. for (short nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
  837. {
  838. SPRITE *pSprite = &sprite[nSprite];
  839. if ( pSprite->cstat & kSpriteMoveCeiling )
  840. pSprite->z = ksprite[nSprite].z + mulscale16(ceilZRange, Lin2Sin(nBusy));
  841. }
  842. }
  843. pXSector->busy = nBusy;
  844. if ( pXSector->command == kCommandLink && pXSector->txID != 0 )
  845. evSend(nSector, SS_SECTOR, pXSector->txID, kCommandLink);
  846. if ( (nBusy & kFluxMask) == 0 )
  847. {
  848. SetSectorState(nSector, pXSector, nBusy >> 16);
  849. return kBusyComplete;
  850. }
  851. return kBusyOk;
  852. }
  853. /*******************************************************************************
  854. FUNCTION: VDoorBusy()
  855. DESCRIPTION: Called by trProcessBusy : calculates the new floorz and
  856. ceilingz height based on the nBusy value
  857. PARAMETERS:
  858. RETURNS:
  859. NOTES: This busy proc works for lifts, Top doors, Bottom doors,
  860. and HSplit doors
  861. *******************************************************************************/
  862. int VDoorBusy( int nSector, int nBusy )
  863. {
  864. dassert( nSector >= 0 && nSector < numsectors);
  865. int nXSector = sector[nSector]. extra;
  866. dassert(nXSector > 0 && nXSector < kMaxXSectors);
  867. XSECTOR *pXSector = &xsector[nXSector];
  868. int floorZRange = pXSector->onFloorZ - pXSector->offFloorZ;
  869. if ( floorZRange != 0 )
  870. {
  871. int oldFloorZ = sector[nSector].floorz;
  872. sector[nSector].floorz = pXSector->offFloorZ + mulscale16(floorZRange, Lin2Sin(nBusy));
  873. // adjust the z for any floor relative sprites or face sprites in the floor
  874. for (short nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
  875. {
  876. SPRITE *pSprite = &sprite[nSprite];
  877. int zTop, zBot;
  878. GetSpriteExtents(pSprite, &zTop, &zBot);
  879. if ( pSprite->cstat & kSpriteMoveFloor ||
  880. ( !(pSprite->cstat & kSpriteRMask) && zBot >= oldFloorZ) )
  881. pSprite->z += sector[nSector].floorz - oldFloorZ;
  882. }
  883. }
  884. int ceilZRange = pXSector->onCeilZ - pXSector->offCeilZ;
  885. if ( ceilZRange != 0 )
  886. {
  887. int oldCeilZ = sector[nSector].ceilingz;
  888. sector[nSector].ceilingz = pXSector->offCeilZ + mulscale16(ceilZRange, Lin2Sin(nBusy));
  889. // adjust the z for any ceiling relative sprites or face sprites in the ceiling
  890. for (short nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
  891. {
  892. SPRITE *pSprite = &sprite[nSprite];
  893. int zTop, zBot;
  894. GetSpriteExtents(pSprite, &zTop, &zBot);
  895. if ( pSprite->cstat & kSpriteMoveCeiling ||
  896. ( !(pSprite->cstat & kSpriteRMask) && zTop <= oldCeilZ) )
  897. pSprite->z += sector[nSector].ceilingz - oldCeilZ;
  898. }
  899. }
  900. pXSector->busy = nBusy;
  901. if ( pXSector->command == kCommandLink && pXSector->txID != 0 )
  902. evSend(nSector, SS_SECTOR, pXSector->txID, kCommandLink);
  903. if ( (nBusy & kFluxMask) == 0 )
  904. {
  905. SetSectorState(nSector, pXSector, nBusy >> 16);
  906. return kBusyComplete;
  907. }
  908. return kBusyOk;
  909. }
  910. /*******************************************************************************
  911. FUNCTION: HDoorBusy()
  912. DESCRIPTION: Called by trProcessBusy : calculates the new wall x,y
  913. based on the nBusy value
  914. RETURNS:
  915. NOTES: This busy proc works for slide doors
  916. *******************************************************************************/
  917. int HDoorBusy( int nSector, int nBusy )
  918. {
  919. dassert( nSector >= 0 && nSector < numsectors);
  920. SECTOR *pSector = &sector[nSector];
  921. int nXSector = pSector->extra;
  922. dassert(nXSector > 0 && nXSector < kMaxXSectors);
  923. XSECTOR *pXSector = &xsector[nXSector];
  924. SPRITE *pMark0 = &sprite[pXSector->marker0];
  925. SPRITE *pMark1 = &sprite[pXSector->marker1];
  926. TranslateSector(nSector, Lin2Sin(pXSector->busy), Lin2Sin(nBusy),
  927. pMark0->x, pMark0->y, pMark0->ang, pMark1->x, pMark1->y, pMark1->ang,
  928. pSector->type == kSectorSlide);
  929. // SlideSector(nSector, mulscale16(dx, nAmount), mulscale16(dy, nAmount));
  930. pXSector->busy = nBusy;
  931. if ( pXSector->command == kCommandLink && pXSector->txID != 0 )
  932. evSend(nSector, SS_SECTOR, pXSector->txID, kCommandLink);
  933. if ( (nBusy & kFluxMask) == 0 )
  934. {
  935. SetSectorState(nSector, pXSector, nBusy >> 16);
  936. return kBusyComplete;
  937. }
  938. return kBusyOk;
  939. }
  940. /*******************************************************************************
  941. FUNCTION: RDoorBusy()
  942. DESCRIPTION: Called by trProcessBusy : calculates the new rotating door
  943. wall angles based on the nBusy value.
  944. PARAMETERS:
  945. RETURNS:
  946. NOTES:
  947. *******************************************************************************/
  948. int RDoorBusy( int nSector, int nBusy )
  949. {
  950. dassert( nSector >= 0 && nSector < numsectors);
  951. SECTOR *pSector = &sector[nSector];
  952. int nXSector = pSector->extra;
  953. dassert(nXSector > 0 && nXSector < kMaxXSectors);
  954. XSECTOR *pXSector = &xsector[nXSector];
  955. SPRITE *pMark = &sprite[pXSector->marker0];
  956. TranslateSector(nSector, Lin2Sin(pXSector->busy), Lin2Sin(nBusy),
  957. pMark->x, pMark->y, 0, pMark->x, pMark->y, pMark->ang,
  958. pSector->type == kSectorRotate);
  959. pXSector->busy = nBusy;
  960. if ( pXSector->command == kCommandLink && pXSector->txID != 0 )
  961. evSend(nSector, SS_SECTOR, pXSector->txID, kCommandLink);
  962. if ( (nBusy & kFluxMask) == 0 )
  963. {
  964. SetSectorState(nSector, pXSector, nBusy >> 16);
  965. return kBusyComplete;
  966. }
  967. return kBusyOk;
  968. }
  969. /*******************************************************************************
  970. FUNCTION: OperateDoor()
  971. DESCRIPTION:
  972. PARAMETERS:
  973. RETURNS:
  974. NOTES:
  975. *******************************************************************************/
  976. void OperateDoor( int nSector, EVENT event, BUSYPROC busyProc )
  977. {
  978. dassert( nSector >= 0 && nSector < numsectors);
  979. int nXSector = sector[nSector].extra;
  980. dassert(nXSector > 0 && nXSector < kMaxXSectors);
  981. XSECTOR *pXSector = &xsector[nXSector];
  982. int nDelta = kMaxBusyValue / ClipLow(pXSector->busyTime * kTimerRate / 10, 1);
  983. switch (event.command)
  984. {
  985. case kCommandOff:
  986. if ( pXSector->busy != 0x0000 )
  987. AddBusy(nSector, busyProc, -nDelta);
  988. break;
  989. case kCommandOn:
  990. if ( pXSector->busy != 0x10000 )
  991. AddBusy(nSector, busyProc, nDelta);
  992. break;
  993. default:
  994. if ( (pXSector->busy & kFluxMask) && pXSector->interruptable )
  995. ReverseBusy(nSector, busyProc);
  996. else
  997. AddBusy(nSector, busyProc, pXSector->state ? -nDelta : nDelta);
  998. break;
  999. }
  1000. }
  1001. /*******************************************************************************
  1002. FUNCTION: OperateTeleport()
  1003. DESCRIPTION:
  1004. PARAMETERS:
  1005. RETURNS:
  1006. NOTES:
  1007. *******************************************************************************/
  1008. void OperateTeleport( int nSector, EVENT event )
  1009. {
  1010. dassert( nSector >= 0 && nSector < numsectors );
  1011. int nXSector = sector[nSector].extra;
  1012. dassert( nXSector > 0 && nXSector < kMaxXSectors );
  1013. XSECTOR *pXSector = &xsector[nXSector];
  1014. BOOL bChanged = FALSE;
  1015. switch (event.command)
  1016. {
  1017. case kCommandOff:
  1018. bChanged = (pXSector->state == 1);
  1019. SetSectorState(nSector, pXSector, 0);
  1020. break;
  1021. case kCommandOn:
  1022. bChanged = (pXSector->state == 0);
  1023. SetSectorState(nSector, pXSector, 1);
  1024. break;
  1025. default:
  1026. bChanged = (pXSector->state == pXSector->restState);
  1027. SetSectorState(nSector, pXSector, pXSector->restState ^ 1);
  1028. break;
  1029. }
  1030. if ( bChanged && (pXSector->state != pXSector->restState) )
  1031. {
  1032. int nDest = pXSector->marker0;
  1033. dassert( nDest < kMaxSprites );
  1034. SPRITE *pDest = &sprite[nDest];
  1035. dassert( pDest->statnum == kStatMarker);
  1036. dassert( pDest->type == kMarkerWarpDest );
  1037. for (int nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
  1038. {
  1039. SPRITE *pSprite = &sprite[nSprite];
  1040. if ( pSprite->statnum == kStatDude )
  1041. {
  1042. pSprite->x = pDest->x;
  1043. pSprite->y = pDest->y;
  1044. pSprite->z += sector[pDest->sectnum].floorz - sector[nSector].floorz;
  1045. pSprite->ang = pDest->ang;
  1046. changespritesect( (short)nSprite, pDest->sectnum );
  1047. if (pSprite->type >= kDudePlayer1 && pSprite->type <= kDudePlayer8)
  1048. {
  1049. // fix backup position for interpolation to work properly
  1050. viewBackupPlayerLoc(pSprite->type - kDudePlayer1);
  1051. }
  1052. }
  1053. }
  1054. }
  1055. }
  1056. /*******************************************************************************
  1057. FUNCTION: OperateSector()
  1058. DESCRIPTION:
  1059. PARAMETERS:
  1060. RETURNS:
  1061. NOTES:
  1062. *******************************************************************************/
  1063. void OperateSector( int nSector, XSECTOR *pXSector, EVENT event )
  1064. {
  1065. SECTOR *pSector = &sector[nSector];
  1066. // special handling for lock/unlock commands
  1067. switch ( event.command )
  1068. {
  1069. case kCommandLock:
  1070. pXSector->locked = 1;
  1071. return;
  1072. case kCommandUnlock:
  1073. pXSector->locked = 0;
  1074. return;
  1075. case kCommandToggleLock:
  1076. pXSector->locked ^= 1;
  1077. return;
  1078. }
  1079. switch ( pSector->type)
  1080. {
  1081. case kSectorZSprite:
  1082. OperateDoor(nSector, event, VSpriteBusy);
  1083. break;
  1084. case kSectorZMotion:
  1085. OperateDoor(nSector, event, VDoorBusy);
  1086. break;
  1087. case kSectorZCrusher:
  1088. OperateDoor(nSector, event, VCrushBusy);
  1089. break;
  1090. case kSectorSlideMarked:
  1091. case kSectorSlide:
  1092. case kSectorSlideCrush:
  1093. OperateDoor(nSector, event, HDoorBusy);
  1094. break;
  1095. case kSectorRotateMarked:
  1096. case kSectorRotate:
  1097. case kSectorRotateCrush:
  1098. OperateDoor(nSector, event, RDoorBusy);
  1099. break;
  1100. case kSectorTeleport:
  1101. OperateTeleport( nSector, event );
  1102. break;
  1103. default:
  1104. switch( event.command )
  1105. {
  1106. case kCommandOff:
  1107. SetSectorState(nSector, pXSector, 0);
  1108. break;
  1109. case kCommandOn:
  1110. SetSectorState(nSector, pXSector, 1);
  1111. break;
  1112. case kCommandToggle:
  1113. case kCommandSectorPush:
  1114. case kCommandSectorImpact:
  1115. case kCommandSectorEnter:
  1116. case kCommandSectorExit:
  1117. SetSectorState(nSector, pXSector, pXSector->state ^ 1);
  1118. break;
  1119. }
  1120. break;
  1121. }
  1122. }
  1123. /*******************************************************************************
  1124. FUNCTION: LinkSector()
  1125. DESCRIPTION:
  1126. PARAMETERS:
  1127. RETURNS:
  1128. NOTES:
  1129. *******************************************************************************/
  1130. void LinkSector( int nSector, XSECTOR *pXSector, EVENT event )
  1131. {
  1132. SECTOR *pSector = &sector[nSector];
  1133. int nBusy = GetSourceBusy(event);
  1134. switch ( pSector->type )
  1135. {
  1136. case kSectorZSprite:
  1137. VSpriteBusy( nSector, nBusy );
  1138. break;
  1139. case kSectorZMotion:
  1140. VDoorBusy( nSector, nBusy );
  1141. break;
  1142. case kSectorZCrusher:
  1143. VCrushBusy( nSector, nBusy );
  1144. break;
  1145. case kSectorSlide:
  1146. case kSectorSlideMarked:
  1147. case kSectorSlideCrush:
  1148. HDoorBusy( nSector, nBusy );
  1149. break;
  1150. case kSectorRotate:
  1151. case kSectorRotateMarked:
  1152. case kSectorRotateCrush:
  1153. RDoorBusy( nSector, nBusy );
  1154. break;
  1155. default:
  1156. pXSector->busy = nBusy;
  1157. if ( (nBusy & kFluxMask) == 0 )
  1158. SetSectorState(nSector, pXSector, nBusy >> 16);
  1159. break;
  1160. }
  1161. }
  1162. /*******************************************************************************
  1163. FUNCTION: LinkSprite()
  1164. DESCRIPTION:
  1165. PARAMETERS:
  1166. RETURNS:
  1167. NOTES:
  1168. *******************************************************************************/
  1169. void LinkSprite( int nSprite, XSPRITE *pXSprite, EVENT event )
  1170. {
  1171. SPRITE *pSprite = &sprite[nSprite];
  1172. int nBusy = GetSourceBusy(event);
  1173. switch ( pSprite->type )
  1174. {
  1175. case kSwitchCombination:
  1176. // should only be linked to a master switch
  1177. if (event.type == SS_SPRITE)
  1178. {
  1179. int nXSprite2 = sprite[event.index].extra;
  1180. dassert(nXSprite2 > 0 && nXSprite2 < kMaxXSprites);
  1181. // get master switch selection
  1182. pXSprite->data1 = xsprite[nXSprite2].data1;
  1183. // at right combination?
  1184. if (pXSprite->data1 == pXSprite->data2)
  1185. SetSpriteState(nSprite, pXSprite, 1);
  1186. else
  1187. SetSpriteState(nSprite, pXSprite, 0);
  1188. }
  1189. break;
  1190. default:
  1191. pXSprite->busy = nBusy;
  1192. if ( (nBusy & kFluxMask) == 0 )
  1193. SetSpriteState(nSprite, pXSprite, nBusy >> 16);
  1194. break;
  1195. }
  1196. }
  1197. /*******************************************************************************
  1198. FUNCTION: LinkWall()
  1199. DESCRIPTION:
  1200. PARAMETERS:
  1201. RETURNS:
  1202. NOTES:
  1203. *******************************************************************************/
  1204. void LinkWall( int nWall, XWALL *pXWall, EVENT event )
  1205. {
  1206. WALL *pWall = &wall[nWall];
  1207. int nBusy = GetSourceBusy(event);
  1208. switch ( pWall->type )
  1209. {
  1210. default:
  1211. pXWall->busy = nBusy;
  1212. if ( (nBusy & kFluxMask) == 0 )
  1213. SetWallState(nWall, pXWall, nBusy >> 16);
  1214. break;
  1215. }
  1216. }
  1217. /*******************************************************************************
  1218. EXPORTED FUNCTIONS
  1219. *******************************************************************************/
  1220. /*******************************************************************************
  1221. FUNCTION: trTriggerSector()
  1222. DESCRIPTION:
  1223. PARAMETERS:
  1224. RETURNS:
  1225. NOTES:
  1226. *******************************************************************************/
  1227. void trTriggerSector( unsigned nSector, XSECTOR *pXSector, int command )
  1228. {
  1229. dprintf("TriggerSector: nSector=%i, command=%i\n", nSector, command);
  1230. // bypass locked XObjects
  1231. if ( pXSector->locked )
  1232. return;
  1233. // bypass triggered one-shots
  1234. if ( pXSector->isTriggered )
  1235. return;
  1236. if ( pXSector->triggerOnce )
  1237. pXSector->isTriggered = 1;
  1238. if ( pXSector->decoupled )
  1239. {
  1240. if ( pXSector->txID != 0 )
  1241. evSend(nSector, SS_SECTOR, pXSector->txID, pXSector->command);
  1242. }
  1243. else
  1244. {
  1245. EVENT event;
  1246. event.command = command;
  1247. // operate the sector
  1248. OperateSector( nSector, pXSector, event );
  1249. }
  1250. }
  1251. /*******************************************************************************
  1252. FUNCTION: trMessageSector()
  1253. DESCRIPTION:
  1254. PARAMETERS:
  1255. RETURNS:
  1256. NOTES:
  1257. *******************************************************************************/
  1258. void trMessageSector( unsigned nSector, EVENT event )
  1259. {
  1260. dassert(sector[nSector].extra > 0 && sector[nSector].extra < kMaxXSectors);
  1261. XSECTOR *pXSector = &xsector[sector[nSector].extra];
  1262. // don't send it to the originator
  1263. /*
  1264. if (event.type == SS_SECTOR && event.index == nSector && !pXSector->decoupled
  1265. && event.command != kCommandCallback )
  1266. return;
  1267. */
  1268. // operate the sector
  1269. if ( event.command == kCommandLink )
  1270. LinkSector( nSector, pXSector, event );
  1271. else
  1272. OperateSector( nSector, pXSector, event );
  1273. }
  1274. /*******************************************************************************
  1275. FUNCTION: trTriggerWall()
  1276. DESCRIPTION:
  1277. PARAMETERS:
  1278. RETURNS:
  1279. NOTES:
  1280. *******************************************************************************/
  1281. void trTriggerWall( unsigned nWall, XWALL *pXWall, int command )
  1282. {
  1283. dprintf("TriggerWall: nWall=%i, command=%i\n", nWall, command);
  1284. // bypass locked XObjects
  1285. if ( pXWall->locked )
  1286. return;
  1287. // bypass triggered one-shots
  1288. if ( pXWall->isTriggered )
  1289. return;
  1290. if ( pXWall->triggerOnce )
  1291. pXWall->isTriggered = 1;
  1292. if ( pXWall->decoupled )
  1293. {
  1294. if ( pXWall->txID != 0 )
  1295. evSend(nWall, SS_WALL, pXWall->txID, pXWall->command);
  1296. }
  1297. else
  1298. {
  1299. EVENT event;
  1300. event.command = command;
  1301. // operate the wall
  1302. OperateWall(nWall, pXWall, event);
  1303. }
  1304. }
  1305. /*******************************************************************************
  1306. FUNCTION: trMessageWall()
  1307. DESCRIPTION:
  1308. PARAMETERS:
  1309. RETURNS:
  1310. NOTES:
  1311. *******************************************************************************/
  1312. void trMessageWall( unsigned nWall, EVENT event )
  1313. {
  1314. dassert(wall[nWall].extra > 0 && wall[nWall].extra < kMaxXWalls);
  1315. XWALL *pXWall = &xwall[wall[nWall].extra];
  1316. // don't send it to the originator
  1317. /*
  1318. if (event.type == SS_WALL && event.index == nWall && !pXWall->decoupled
  1319. && event.command != kCommandCallback )
  1320. return;
  1321. */
  1322. // operate the wall
  1323. if ( event.command == kCommandLink )
  1324. LinkWall( nWall, pXWall, event );
  1325. else
  1326. OperateWall( nWall, pXWall, event );
  1327. }
  1328. /*******************************************************************************
  1329. FUNCTION: trTriggerSprite()
  1330. DESCRIPTION:
  1331. PARAMETERS:
  1332. RETURNS:
  1333. NOTES:
  1334. *******************************************************************************/
  1335. void trTriggerSprite( unsigned nSprite, XSPRITE *pXSprite, int command )
  1336. {
  1337. dprintf("TriggerSprite: nSprite=%i, type= %i command=%i\n", nSprite, sprite[nSprite].type, command);
  1338. // bypass locked XObjects
  1339. if ( pXSprite->locked )
  1340. return;
  1341. // bypass triggered one-shots
  1342. if ( pXSprite->isTriggered )
  1343. return;
  1344. if ( pXSprite->triggerOnce )
  1345. pXSprite->isTriggered = 1;
  1346. if ( pXSprite->decoupled )
  1347. {
  1348. if ( pXSprite->txID != 0 )
  1349. evSend(nSprite, SS_SPRITE, pXSprite->txID, pXSprite->command);
  1350. }
  1351. else
  1352. {
  1353. EVENT event;
  1354. event.command = command;
  1355. // operate the sprite
  1356. OperateSprite(nSprite, pXSprite, event);
  1357. }
  1358. }
  1359. /*******************************************************************************
  1360. FUNCTION: trMessageSprite()
  1361. DESCRIPTION:
  1362. PARAMETERS:
  1363. RETURNS:
  1364. NOTES:
  1365. *******************************************************************************/
  1366. void trMessageSprite( unsigned nSprite, EVENT event )
  1367. {
  1368. // return immediately if sprite has been deleted
  1369. if ( sprite[nSprite].statnum == kMaxStatus )
  1370. return;
  1371. dassert(sprite[nSprite].extra > 0 && sprite[nSprite].extra < kMaxXSprites);
  1372. XSPRITE *pXSprite = &xsprite[sprite[nSprite].extra];
  1373. // don't send it to the originator
  1374. /*
  1375. if (event.type == SS_SPRITE && event.index == nSprite && !pXSprite->decoupled
  1376. && event.command != kCommandCallback )
  1377. return;
  1378. */
  1379. // operate the sprite
  1380. if ( event.command == kCommandLink )
  1381. LinkSprite( nSprite, pXSprite, event );
  1382. else
  1383. OperateSprite( nSprite, pXSprite, event );
  1384. }
  1385. /*******************************************************************************
  1386. FUNCTION: trProcessBusy()
  1387. DESCRIPTION:
  1388. PARAMETERS:
  1389. RETURNS:
  1390. NOTES:
  1391. *******************************************************************************/
  1392. void trProcessBusy( void )
  1393. {
  1394. int i;
  1395. for ( i = gBusyCount - 1; i >= 0; i-- )
  1396. {
  1397. // temporarily store nBusy before normalization
  1398. int tempBusy = gBusy[i].nBusy;
  1399. gBusy[i].nBusy = ClipRange(gBusy[i].nBusy + gBusy[i].nDelta * kFrameTicks, 0, kMaxBusyValue);
  1400. int rcode = gBusy[i].busyProc( gBusy[i].nIndex, gBusy[i].nBusy );
  1401. switch (rcode)
  1402. {
  1403. case kBusyRestore:
  1404. gBusy[i].nBusy = tempBusy; // restore previous nBusy
  1405. break;
  1406. case kBusyReverse:
  1407. gBusy[i].nBusy = tempBusy; // restore previous nBusy
  1408. gBusy[i].nDelta = -gBusy[i].nDelta; // reverse delta
  1409. break;
  1410. case kBusyComplete:
  1411. gBusyCount--;
  1412. gBusy[i] = gBusy[gBusyCount];
  1413. break;
  1414. }
  1415. }
  1416. }
  1417. /*******************************************************************************
  1418. FUNCTION: trInit()
  1419. DESCRIPTION: Initialize the trigger system.
  1420. PARAMETERS:
  1421. RETURNS:
  1422. NOTES: Called in prepareboard, AFTER call to dbLoadMap
  1423. *******************************************************************************/
  1424. void trInit( void )
  1425. {
  1426. int i;
  1427. int nSector, nWall, nSprite;
  1428. gBusyCount = 0;
  1429. // get wall vertice positions
  1430. for (nWall = 0; nWall < numwalls; nWall++)
  1431. {
  1432. kwall[nWall].x = wall[nWall].x;
  1433. kwall[nWall].y = wall[nWall].y;
  1434. }
  1435. // get sprite positions
  1436. for ( nSprite = 0; nSprite < kMaxSprites; nSprite++ )
  1437. {
  1438. if ( sprite[nSprite].statnum < kMaxStatus )
  1439. {
  1440. ksprite[nSprite].x = sprite[nSprite].x;
  1441. ksprite[nSprite].y = sprite[nSprite].y;
  1442. ksprite[nSprite].z = sprite[nSprite].z;
  1443. }
  1444. }
  1445. // init wall trigger masks (must be done first)
  1446. for (nWall = 0; nWall < numwalls; nWall++)
  1447. {
  1448. if ( wall[nWall].extra > 0 )
  1449. {
  1450. int nXWall = wall[nWall].extra;
  1451. dassert(nXWall < kMaxXWalls);
  1452. XWALL *pXWall = &xwall[nXWall];
  1453. if ( pXWall->state )
  1454. pXWall->busy = kMaxBusyValue;
  1455. switch( wall[nWall].type )
  1456. {
  1457. default:
  1458. break;
  1459. }
  1460. }
  1461. }
  1462. // init sector trigger masks
  1463. dassert((numsectors >= 0) && (numsectors < kMaxSectors));
  1464. for ( nSector = 0; nSector < numsectors; nSector++ )
  1465. {
  1466. SECTOR *pSector = &sector[nSector];
  1467. int nXSector = pSector->extra;
  1468. if ( nXSector > 0 )
  1469. {
  1470. dassert(nXSector < kMaxXSectors);
  1471. XSECTOR *pXSector = &xsector[nXSector];
  1472. if ( pXSector->state )
  1473. pXSector->busy = kMaxBusyValue;
  1474. int oldFloorZ, oldCeilZ;
  1475. SPRITE *pMark0 = NULL, *pMark1 = NULL;
  1476. switch( pSector->type )
  1477. {
  1478. case kSectorTeleport:
  1479. pXSector->data = -1;
  1480. break;
  1481. case kSectorZSprite: // it's probabyl okay to start this one with the on or off z
  1482. case kSectorZMotion:
  1483. case kSectorZCrusher:
  1484. oldFloorZ = pSector->floorz;
  1485. oldCeilZ = pSector->ceilingz;
  1486. // open or close door as necessary for initial state
  1487. if ( pXSector->state == 0 )
  1488. {
  1489. pSector->ceilingz = pXSector->offCeilZ;
  1490. pSector->floorz = pXSector->offFloorZ;
  1491. }
  1492. else
  1493. {
  1494. pSector->ceilingz = pXSector->onCeilZ;
  1495. pSector->floorz = pXSector->onFloorZ;
  1496. }
  1497. // adjust the z for any floor relative sprites or face sprites in the floor
  1498. for (nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
  1499. {
  1500. SPRITE *pSprite = &sprite[nSprite];
  1501. if ( pSprite->cstat & kSpriteMoveFloor ||
  1502. ( !(pSprite->cstat & kSpriteRMask) && pSprite->z >= oldFloorZ) )
  1503. pSprite->z += pSector->floorz - oldFloorZ;
  1504. }
  1505. // adjust the z for any ceiling relative sprites or face sprites in the ceiling
  1506. for (nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
  1507. {
  1508. SPRITE *pSprite = &sprite[nSprite];
  1509. if ( pSprite->cstat & kSpriteMoveCeiling ||
  1510. ( !(pSprite->cstat & kSpriteRMask) && pSprite->z <= oldCeilZ) )
  1511. pSprite->z += pSector->ceilingz - oldCeilZ;
  1512. }
  1513. break;
  1514. case kSectorSlideMarked:
  1515. case kSectorSlide:
  1516. case kSectorSlideCrush:
  1517. pMark0 = &sprite[pXSector->marker0];
  1518. pMark1 = &sprite[pXSector->marker1];
  1519. // move door to off position by reversing markers
  1520. TranslateSector(nSector, 0, 0x10000,
  1521. pMark1->x, pMark1->y, pMark1->ang, pMark0->x, pMark0->y, pMark0->ang,
  1522. pSector->type == kSectorSlide);
  1523. // grab updated positions of walls
  1524. for (i = 0; i < pSector->wallnum; i++)
  1525. {
  1526. int nWall = pSector->wallptr + i;
  1527. kwall[nWall].x = wall[nWall].x;
  1528. kwall[nWall].y = wall[nWall].y;
  1529. }
  1530. // grab updated positions of sprites
  1531. for (nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
  1532. {
  1533. ksprite[nSprite].x = sprite[nSprite].x;
  1534. ksprite[nSprite].y = sprite[nSprite].y;
  1535. ksprite[nSprite].z = sprite[nSprite].z;
  1536. }
  1537. // open door if necessary
  1538. TranslateSector(nSector, 0, pXSector->busy,
  1539. pMark0->x, pMark0->y, pMark0->ang, pMark1->x, pMark1->y, pMark1->ang,
  1540. pSector->type == kSectorSlide);
  1541. break;
  1542. case kSectorRotateMarked:
  1543. case kSectorRotate:
  1544. case kSectorRotateCrush:
  1545. pMark0 = &sprite[pXSector->marker0];
  1546. // move door to off position
  1547. TranslateSector(nSector, 0, -0x10000,
  1548. pMark0->x, pMark0->y, 0, pMark0->x, pMark0->y, pMark0->ang,
  1549. pSector->type == kSectorRotate);
  1550. // grab updated positions of walls
  1551. for (i = 0; i < pSector->wallnum; i++)
  1552. {
  1553. int nWall = pSector->wallptr + i;
  1554. kwall[nWall].x = wall[nWall].x;
  1555. kwall[nWall].y = wall[nWall].y;
  1556. }
  1557. // grab updated positions of sprites
  1558. for (nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
  1559. {
  1560. ksprite[nSprite].x = sprite[nSprite].x;
  1561. ksprite[nSprite].y = sprite[nSprite].y;
  1562. ksprite[nSprite].z = sprite[nSprite].z;
  1563. }
  1564. // open door if necessary
  1565. TranslateSector(nSector, 0, pXSector->busy,
  1566. pMark0->x, pMark0->y, 0, pMark0->x, pMark0->y, pMark0->ang,
  1567. pSector->type == kSectorRotate);
  1568. break;
  1569. default:
  1570. break;
  1571. }
  1572. }
  1573. }
  1574. // init sprite trigger masks
  1575. for ( nSprite = 0; nSprite < kMaxSprites; nSprite++ )
  1576. {
  1577. int nXSprite = sprite[nSprite].extra;
  1578. if ( sprite[nSprite].statnum < kMaxStatus && nXSprite > 0 )
  1579. {
  1580. dassert(nXSprite < kMaxXSprites);
  1581. XSPRITE *pXSprite = &xsprite[nXSprite];
  1582. if ( pXSprite->state )
  1583. pXSprite->busy = kMaxBusyValue;
  1584. // special initialization for implicit trigger types
  1585. switch ( sprite[nSprite].type )
  1586. {
  1587. case kSwitchPadlock:
  1588. pXSprite->triggerOnce = 1; // force trigger once
  1589. break;
  1590. case kGenTrigger:
  1591. case kGenWaterDrip:
  1592. case kGenBloodDrip:
  1593. case kGenFireball:
  1594. case kGenEctoSkull:
  1595. case kGenDart:
  1596. case kGenSound:
  1597. InitGenerator( nSprite );
  1598. break;
  1599. case kThingTNTProxArmed:
  1600. pXSprite->triggerProximity = 1;
  1601. break;
  1602. }
  1603. if ( pXSprite->triggerPush || pXSprite->triggerImpact )
  1604. sprite[nSprite].cstat |= kSpriteHitscan;
  1605. /*******************************************************************************
  1606. Proximity sensors are being put on a separate list here, but this presents a
  1607. problem, since this means they can't be on any other list, such as the Thing
  1608. list. This prevents the proximity bombs from being initialized properly.
  1609. How do we make them affected by explosions?
  1610. *******************************************************************************/
  1611. if ( pXSprite->triggerProximity )
  1612. actPostSprite( nSprite, kStatProximity );
  1613. // changespritestat((short)nSprite, kStatProximity);
  1614. switch( sprite[nSprite].type )
  1615. {
  1616. default:
  1617. break;
  1618. }
  1619. }
  1620. }
  1621. evSend( 0, 0, kChannelTriggerStart, kCommandOn );
  1622. if (gNetMode == kNetModeCoop)
  1623. evSend( 0, 0, kChannelTriggerCoop, kCommandOn );
  1624. else if (gNetMode == kNetModeBloodBath || gNetMode == kNetModeTeams)
  1625. evSend( 0, 0, kChannelTriggerMatch, kCommandOn );
  1626. }
  1627. /*******************************************************************************
  1628. FUNCTION: trTextOver()
  1629. DESCRIPTION:
  1630. PARAMETERS:
  1631. RETURNS:
  1632. NOTES:
  1633. *******************************************************************************/
  1634. void trTextOver( int textId )
  1635. {
  1636. if ( gLevelMessage[textId] != NULL )
  1637. viewSetMessage(gLevelMessage[textId]);
  1638. }
  1639. /*******************************************************************************
  1640. FUNCTION: trLightning()
  1641. DESCRIPTION:
  1642. PARAMETERS:
  1643. RETURNS:
  1644. NOTES:
  1645. *******************************************************************************/
  1646. void trLightning( int nForkID )
  1647. {
  1648. dassert( nForkID >= 0 && nForkID < kMaxLightning );
  1649. dprintf( "trLightning: nForkID = %i\n", nForkID );
  1650. int nSlot = gLightningInfo[ nForkID ].slot;
  1651. dprintf( "trLightning: nSlot = %i\n", nSlot );
  1652. if ( nSlot != -1 )
  1653. {
  1654. dassert( nSlot >= 0 && nSlot < kMaxSkyTiles );
  1655. dprintf( "trLightning: gLightningInfo[ %i ].offset = %i\n", nForkID, gLightningInfo[ nForkID ].offset );
  1656. pskyoff[ nSlot ] = (short)gLightningInfo[ nForkID ].offset;
  1657. }
  1658. }
  1659. /*******************************************************************************
  1660. FUNCTION: InitGenerator()
  1661. DESCRIPTION:
  1662. PARAMETERS:
  1663. RETURNS:
  1664. NOTES:
  1665. *******************************************************************************/
  1666. static void InitGenerator( int nSprite )
  1667. {
  1668. dassert(nSprite < kMaxSprites);
  1669. SPRITE *pSprite = &sprite[nSprite];
  1670. dassert(pSprite->statnum != kMaxStatus);
  1671. int nXSprite = pSprite->extra;
  1672. dassert(nXSprite > 0);
  1673. XSPRITE *pXSprite = &xsprite[nXSprite];
  1674. dassert(pXSprite->busyTime > 0); // should be > 0 for timer events
  1675. switch ( sprite[nSprite].type )
  1676. {
  1677. case kGenTrigger:
  1678. pSprite->cstat &= ~kSpriteBlocking;
  1679. pSprite->cstat |= kSpriteInvisible;
  1680. break;
  1681. default:
  1682. break; //return;
  1683. }
  1684. pXSprite->data3 = FALSE;
  1685. if ( pXSprite->state != pXSprite->restState && pXSprite->busyTime > 0 )
  1686. {
  1687. pXSprite->data3 = TRUE;
  1688. evPost(nSprite, SS_SPRITE, (pXSprite->busyTime + BiRandom(pXSprite->data1)) * kTimerRate / 10);
  1689. }
  1690. }
  1691. /*******************************************************************************
  1692. FUNCTION: ActivateGenerator()
  1693. DESCRIPTION:
  1694. PARAMETERS:
  1695. RETURNS:
  1696. NOTES:
  1697. *******************************************************************************/
  1698. static void ActivateGenerator( int nSprite )
  1699. {
  1700. dassert(nSprite < kMaxSprites);
  1701. SPRITE *pSprite = &sprite[nSprite];
  1702. dassert(pSprite->statnum != kMaxStatus);
  1703. int nXSprite = pSprite->extra;
  1704. dassert(nXSprite > 0);
  1705. XSPRITE *pXSprite = &xsprite[nXSprite];
  1706. int zTop, zBot;
  1707. switch ( pSprite->type )
  1708. {
  1709. case kGenWaterDrip:
  1710. GetSpriteExtents(pSprite, &zTop, &zBot);
  1711. actSpawnThing( pSprite->sectnum, pSprite->x, pSprite->y, zBot, kThingWaterDrip);
  1712. break;
  1713. case kGenBloodDrip:
  1714. GetSpriteExtents(pSprite, &zTop, &zBot);
  1715. actSpawnThing( pSprite->sectnum, pSprite->x, pSprite->y, zBot, kThingBloodDrip);
  1716. break;
  1717. case kGenSound:
  1718. sfxCreate3DSound(pSprite->x, pSprite->y, pSprite->z, pXSprite->soundId);
  1719. break;
  1720. case kGenFireball:
  1721. switch(pXSprite->data2)
  1722. {
  1723. case 0:
  1724. FireballTrapCallback( SS_SPRITE, nXSprite );
  1725. break;
  1726. case 1:
  1727. seqSpawn(kSeqFireTrap1, SS_SPRITE, nXSprite, FireballTrapCallback);
  1728. break;
  1729. case 2:
  1730. seqSpawn(kSeqFireTrap2, SS_SPRITE, nXSprite, FireballTrapCallback);
  1731. break;
  1732. }
  1733. break;
  1734. case kGenEctoSkull:
  1735. case kGenDart:
  1736. // spawn a missile that moves and damages accordingly
  1737. break;
  1738. default:
  1739. break;
  1740. }
  1741. }
  1742. static void FireballTrapCallback( int /* type */, int nXIndex )
  1743. {
  1744. XSPRITE *pXSprite = &xsprite[nXIndex];
  1745. int nSprite = pXSprite->reference;
  1746. SPRITE *pSprite = &sprite[nSprite];
  1747. int dx, dy, dz;
  1748. if ( pSprite->cstat & kSpriteFloor ) // floor sprite
  1749. {
  1750. dx = dy = 0;
  1751. if ( pSprite->cstat & kSpriteFlipY ) // face down floor sprite
  1752. dz = 1 << 14;
  1753. else // face up floor sprite
  1754. dz = -1 << 14;
  1755. }
  1756. else // wall sprite or face sprite
  1757. {
  1758. dx = Cos(pSprite->ang) >> 16;
  1759. dy = Sin(pSprite->ang) >> 16;
  1760. dz = 0;
  1761. }
  1762. actFireMissile( nSprite, pSprite->z, dx, dy, dz, kMissileFireball);
  1763. }
  1764. static void MGunFireCallback( int /* type */, int nXIndex )
  1765. {
  1766. int nSprite = xsprite[nXIndex].reference;
  1767. SPRITE *pSprite = &sprite[nSprite];
  1768. XSPRITE *pXSprite = &xsprite[nXIndex];
  1769. // if dynamic ammo left or infinite ammo
  1770. if (pXSprite->data2 > 0 || pXSprite->data1 == 0)
  1771. {
  1772. if (pXSprite->data2 > 0)
  1773. {
  1774. pXSprite->data2--; // subtract ammo
  1775. if (pXSprite->data2 == 0)
  1776. evPost(nSprite, SS_SPRITE, 1, kCommandOff); // empty! turn it off
  1777. }
  1778. int dx = (Cos(pSprite->ang) >> 16) + BiRandom(1000);
  1779. int dy = (Sin(pSprite->ang) >> 16) + BiRandom(1000);
  1780. int dz = BiRandom(1000);
  1781. actFireVector(nSprite, pSprite->z, dx, dy, dz, kVectorBullet);
  1782. sfxCreate3DSound(pSprite->x, pSprite->y, pSprite->z, kSfxTomFire);
  1783. }
  1784. }
  1785. static void MGunOpenCallback( int /* type */, int nXIndex )
  1786. {
  1787. seqSpawn(kSeqMGunFire, SS_SPRITE, nXIndex, MGunFireCallback);
  1788. }