PageRenderTime 73ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 1ms

/src/effects.c

https://github.com/blezek/warzone2100
C | 2698 lines | 1985 code | 389 blank | 324 comment | 307 complexity | 840fa7c007353e9504d2f269a6025344 MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0, BSD-3-Clause, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. This file is part of Warzone 2100.
  3. Copyright (C) 1999-2004 Eidos Interactive
  4. Copyright (C) 2005-2010 Warzone 2100 Project
  5. Warzone 2100 is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. Warzone 2100 is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with Warzone 2100; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  16. */
  17. /*
  18. Spot FX code - will handle every miscellaneous imd render and update for temporary
  19. entities except projectiles.
  20. Handles stuff like
  21. - Smoke sprites on the card.
  22. - Explosions
  23. - Building body kit - flashing lights etc etc
  24. - Construction graphics
  25. - Gravitons
  26. - Dust
  27. - Blood
  28. It's now PSX friendly in that there's no floats
  29. ************************************************************
  30. * STILL NEED TO REMOVE SOME MAGIC NUMBERS INTO #DEFINES!!! *
  31. ************************************************************
  32. */
  33. #include "lib/framework/frame.h"
  34. #include "lib/framework/frameresource.h"
  35. #include "lib/framework/input.h"
  36. #include "lib/framework/tagfile.h"
  37. #include "lib/framework/math_ext.h"
  38. #include "lib/ivis_common/ivisdef.h" //ivis matrix code
  39. #include "lib/ivis_common/piedef.h" //ivis matrix code
  40. #include "lib/framework/fixedpoint.h"
  41. #include "lib/ivis_common/piepalette.h"
  42. #include "lib/ivis_common/piestate.h"
  43. #include "lib/ivis_opengl/piematrix.h"
  44. #include "lib/ivis_common/piemode.h"
  45. #include "lib/gamelib/gtime.h"
  46. #include "lib/sound/audio.h"
  47. #include "lib/sound/audio_id.h"
  48. #include "display3d.h"
  49. #include "map.h"
  50. #include "bucket3d.h"
  51. #include "effects.h"
  52. #include "mission.h"
  53. #include "miscimd.h"
  54. #include "hci.h"
  55. #include "lighting.h"
  56. #include "console.h"
  57. #include "loop.h"
  58. #include "multiplay.h"
  59. #include "game.h"
  60. #include "component.h"
  61. #define GRAVITON_GRAVITY ((float)-800)
  62. #define EFFECT_X_FLIP 0x1
  63. #define EFFECT_Y_FLIP 0x2
  64. #define EFFECT_CYCLIC 0x4
  65. #define EFFECT_ESSENTIAL 0x8
  66. #define EFFECT_FACING 0x10
  67. #define EFFECT_SCALED 0x20
  68. #define EFFECT_LIT 0x40
  69. #define TEST_FLIPPED_X(x) (x->control & EFFECT_X_FLIP)
  70. #define TEST_FLIPPED_Y(x) (x->control & EFFECT_Y_FLIP)
  71. #define TEST_ESSENTIAL(x) (x->control & EFFECT_ESSENTIAL)
  72. #define TEST_FACING(x) (x->control & EFFECT_FACING)
  73. #define TEST_CYCLIC(x) (x->control & EFFECT_CYCLIC)
  74. #define TEST_SCALED(x) (x->control & EFFECT_SCALED)
  75. #define TEST_LIT(x) (x->control & EFFECT_LIT)
  76. #define SET_FLIPPED_X(x) ((x->control) = (UBYTE)(x->control | EFFECT_X_FLIP))
  77. #define SET_FLIPPED_Y(x) ((x->control) = (UBYTE)(x->control | EFFECT_Y_FLIP))
  78. #define SET_ESSENTIAL(x) ((x->control) = (UBYTE)(x->control | EFFECT_ESSENTIAL))
  79. #define SET_FACING(x) ((x->control) = (UBYTE)(x->control | EFFECT_FACING))
  80. #define SET_CYCLIC(x) ((x->control) = (UBYTE)(x->control | EFFECT_CYCLIC))
  81. #define SET_SCALED(x) ((x->control) = (UBYTE)(x->control | EFFECT_SCALED))
  82. #define SET_LIT(x) ((x->control) = (UBYTE)(x->control | EFFECT_LIT))
  83. #define MINIMUM_IMPACT_VELOCITY (16)
  84. #define NORMAL_SMOKE_LIFESPAN (6000 + rand()%3000)
  85. #define SMALL_SMOKE_LIFESPAN (3000 + rand()%3000)
  86. #define TRAIL_SMOKE_LIFESPAN (1200)
  87. #define CONSTRUCTION_LIFESPAN (5000)
  88. #define SMOKE_FRAME_DELAY (40 + rand()%30)
  89. #define EXPLOSION_FRAME_DELAY (25 + rand()%40)
  90. #define EXPLOSION_TESLA_FRAME_DELAY (65)
  91. #define EXPLOSION_PLASMA_FRAME_DELAY (45)
  92. #define BLOOD_FRAME_DELAY (150)
  93. #define DESTRUCTION_FRAME_DELAY (200)
  94. #define TESLA_SPEED (170)// + (30 - rand()%60))
  95. #define TESLA_SIZE (100)// + (20 - rand()%40))
  96. #define GRAVITON_FRAME_DELAY (100 + rand()%50)
  97. #define GRAVITON_BLOOD_DELAY (200 + rand()%100)
  98. #define CONSTRUCTION_FRAME_DELAY (40 + rand()%30)
  99. #define EXPLOSION_SIZE (110+(30-rand()%60))
  100. #define LIMITED_EXPLOSION_SIZE (100+(5-rand()%10))
  101. #define BLOOD_SIZE (100+(30-rand()%60))
  102. #define BLOOD_FALL_SPEED (-(20+rand()%20))
  103. #define GRAVITON_INIT_VEL_X (float)(200 - rand() % 300)
  104. #define GRAVITON_INIT_VEL_Z (float)(200 - rand() % 300)
  105. #define GRAVITON_INIT_VEL_Y (float)(300 + rand() % 100)
  106. #define GIBLET_INIT_VEL_X (float)(50 - rand() % 100)
  107. #define GIBLET_INIT_VEL_Z (float)(50 - rand() % 100)
  108. #define GIBLET_INIT_VEL_Y 12.f
  109. #define DROID_DESTRUCTION_DURATION (3*GAME_TICKS_PER_SEC/2) // 1.5 seconds
  110. #define STRUCTURE_DESTRUCTION_DURATION ((7*GAME_TICKS_PER_SEC)/2) // 3.5 seconds
  111. #define FIREWORK_EXPLODE_HEIGHT 400
  112. #define STARBURST_RADIUS 150
  113. #define STARBURST_DIAMETER 300
  114. #define EFFECT_SMOKE_ADDITIVE 1
  115. #define EFFECT_STEAM_ADDITIVE 8
  116. #define EFFECT_WAYPOINT_ADDITIVE 32
  117. #define EFFECT_EXPLOSION_ADDITIVE 164
  118. #define EFFECT_PLASMA_ADDITIVE 224
  119. #define EFFECT_SMOKE_TRANSPARENCY 130
  120. #define EFFECT_STEAM_TRANSPARENCY 128
  121. #define EFFECT_WAYPOINT_TRANSPARENCY 128
  122. #define EFFECT_BLOOD_TRANSPARENCY 128
  123. #define EFFECT_EXPLOSION_TRANSPARENCY 164
  124. #define EFFECT_DROID_DIVISION 101
  125. #define EFFECT_STRUCTURE_DIVISION 103
  126. #define DROID_UPDATE_INTERVAL 500
  127. #define STRUCTURE_UPDATE_INTERVAL 1250
  128. #define BASE_FLAME_SIZE 80
  129. #define BASE_LASER_SIZE 10
  130. #define BASE_PLASMA_SIZE 0
  131. #define DISCOVERY_SIZE 60
  132. #define FLARE_SIZE 100
  133. #define SHOCKWAVE_SPEED (GAME_TICKS_PER_SEC)
  134. #define MAX_SHOCKWAVE_SIZE 500
  135. /*! Number of effects in one chunk */
  136. #define EFFECT_CHUNK_SIZE 10000
  137. /*! A memory chunk of effects */
  138. typedef struct _EffectChunk EffectChunk;
  139. struct _EffectChunk {
  140. EFFECT effects[EFFECT_CHUNK_SIZE]; //!< Chunk of effects
  141. EffectChunk *next; //!< Next element in list
  142. };
  143. /*! List containing all allocated effect chunks */
  144. struct {
  145. EffectChunk *first; //!< First element of list, used for iteration
  146. EffectChunk *last; //!< Last element of list, used for appending
  147. } chunkList = {
  148. NULL, NULL
  149. };
  150. /*! Our lists of all game world effects */
  151. struct {
  152. size_t num; //!< Number of effects in this list, used when saving
  153. EFFECT *first; //!< First element of list, used for iteration / finding free effects
  154. EFFECT *last; //!< Last element of list, used for appending
  155. } activeList = { //!< List of all active effects
  156. 0, NULL, NULL
  157. }, inactiveList = { //!< List of unused effects
  158. 0, NULL, NULL
  159. };
  160. /* Tick counts for updates on a particular interval */
  161. static UDWORD lastUpdateDroids[EFFECT_DROID_DIVISION];
  162. static UDWORD lastUpdateStructures[EFFECT_STRUCTURE_DIVISION];
  163. static UDWORD auxVar; // dirty filthy hack - don't look for what this does.... //FIXME
  164. static UDWORD auxVarSec; // dirty filthy hack - don't look for what this does.... //FIXME
  165. static UDWORD specifiedSize;
  166. static UDWORD ellSpec;
  167. static uint8_t EffectForPlayer = 0;
  168. // ----------------------------------------------------------------------------------------
  169. /* PROTOTYPES */
  170. // ----------------------------------------------------------------------------------------
  171. // ---- Update functions - every group type of effect has one of these */
  172. static void updateWaypoint(EFFECT *psEffect);
  173. static void updateExplosion(EFFECT *psEffect);
  174. static void updatePolySmoke(EFFECT *psEffect);
  175. static void updateGraviton(EFFECT *psEffect);
  176. static void updateConstruction(EFFECT *psEffect);
  177. static void updateBlood (EFFECT *psEffect);
  178. static void updateDestruction(EFFECT *psEffect);
  179. static void updateFire(EFFECT *psEffect);
  180. static void updateSatLaser(EFFECT *psEffect);
  181. static void updateFirework(EFFECT *psEffect);
  182. static void updateEffect(EFFECT *psEffect); // MASTER function
  183. // ----------------------------------------------------------------------------------------
  184. // ---- The render functions - every group type of effect has a distinct one
  185. static void renderExplosionEffect ( const EFFECT *psEffect );
  186. static void renderSmokeEffect ( const EFFECT *psEffect );
  187. static void renderGravitonEffect ( const EFFECT *psEffect );
  188. static void renderConstructionEffect( const EFFECT *psEffect );
  189. static void renderWaypointEffect ( const EFFECT *psEffect );
  190. static void renderBloodEffect ( const EFFECT *psEffect );
  191. static void renderDestructionEffect ( const EFFECT *psEffect );
  192. static void renderFirework ( const EFFECT *psEffect );
  193. static void positionEffect(const EFFECT *psEffect);
  194. /* There is no render destruction effect! */
  195. // ----------------------------------------------------------------------------------------
  196. // ---- The set up functions - every type has one
  197. static void effectSetupSmoke ( EFFECT *psEffect );
  198. static void effectSetupGraviton ( EFFECT *psEffect );
  199. static void effectSetupExplosion ( EFFECT *psEffect );
  200. static void effectSetupConstruction ( EFFECT *psEffect );
  201. static void effectSetupWayPoint ( EFFECT *psEffect );
  202. static void effectSetupBlood ( EFFECT *psEffect );
  203. static void effectSetupDestruction ( EFFECT *psEffect );
  204. static void effectSetupFire ( EFFECT *psEffect );
  205. static void effectSetupSatLaser ( EFFECT *psEffect );
  206. static void effectSetupFirework ( EFFECT *psEffect );
  207. static void effectStructureUpdates(void);
  208. static void effectDroidUpdates(void);
  209. static UDWORD effectGetNumFrames(EFFECT *psEffect);
  210. static void killEffect(EFFECT *e);
  211. /*!
  212. * Initialise memory between first and last as singly linked list
  213. * \param first First element in memory chunk
  214. * \param last Last element in memory chunk
  215. */
  216. static void initEffectPool(EFFECT *first, EFFECT *last)
  217. {
  218. EFFECT *it;
  219. for (it = first; it < last; it++)
  220. {
  221. // We do not need a double-linked-list for inactiveeffects, since we always pick from the front:
  222. it->prev = NULL;
  223. it->next = it+1;
  224. }
  225. last->prev = NULL;
  226. last->next = NULL;
  227. }
  228. /*!
  229. * Allocate a new effect from memory pool
  230. * FIXME: Does not deal with out-of-memory conditions (yet)
  231. * \return New, uninitialised effect
  232. */
  233. static EFFECT *Effect_malloc(void)
  234. {
  235. /* Take the first item in inactiveList */
  236. EFFECT *instance = inactiveList.first;
  237. /* Remove from inactiveList */
  238. inactiveList.first = instance->next; // Singly linked, so do not update prev
  239. instance->next = NULL; // We are the last in activeList now
  240. /* Append to activeList */
  241. instance->prev = activeList.last;
  242. if (instance->prev == NULL) {
  243. activeList.first = instance; // activeList was empty, so fill it
  244. }
  245. else {
  246. activeList.last->next = instance;
  247. }
  248. activeList.last = instance;
  249. /* Adjust counts */
  250. activeList.num++;
  251. inactiveList.num--;
  252. /* Ensure the next search will have something to feed its hunger with */
  253. if (inactiveList.first == NULL)
  254. {
  255. /* Allocate new effect chunk */
  256. EffectChunk *chunk = calloc(1, sizeof(EffectChunk));
  257. debug(LOG_MEMORY, "%zd effects in use, allocating %d extra", activeList.num, EFFECT_CHUNK_SIZE);
  258. /* Deal with out-of-memory conditions */
  259. if (chunk == NULL) {
  260. debug(LOG_ERROR, "Out of memory");
  261. return NULL; // The next call relies on inactiveList being non-empty, and would thus segfault, so bail out early instead
  262. }
  263. /* Append to list */
  264. chunk->next = NULL; // Last element
  265. chunkList.last->next = chunk; // chunkList is never empty, thus we can rely on last!=NULL
  266. /* Update inactiveList */
  267. inactiveList.num = EFFECT_CHUNK_SIZE;
  268. inactiveList.first = &chunk->effects[0];
  269. inactiveList.last = &chunk->effects[EFFECT_CHUNK_SIZE-1];
  270. /* Initialise list links between inactive effects */
  271. initEffectPool(inactiveList.first, inactiveList.last);
  272. }
  273. return instance;
  274. }
  275. /*!
  276. * Return an effect into memory pool
  277. * \param self Effect to be freed
  278. */
  279. static void Effect_free(void *self)
  280. {
  281. EFFECT *instance = self;
  282. /* Remove from activeList and fixup endings necessary */
  283. if (instance->prev != NULL) {
  284. instance->prev->next = instance->next;
  285. }
  286. else {
  287. activeList.first = instance->next; // We were the first in activeList
  288. }
  289. if (instance->next != NULL) {
  290. instance->next->prev = instance->prev;
  291. }
  292. else {
  293. activeList.last = instance->prev; // We were the last in activeList
  294. }
  295. /* Append to inactiveList */
  296. instance->prev = NULL; // Singly linked
  297. instance->next = NULL; // Last element
  298. // inactiveList is never empty (guaranteed in Effect_malloc), thus we can rely on last!=NULL
  299. inactiveList.last->next = instance;
  300. inactiveList.last = instance;
  301. /* Adjust counts */
  302. inactiveList.num++;
  303. ASSERT_OR_RETURN(, activeList.num > 0, "Underflow");
  304. activeList.num--;
  305. }
  306. void shutdownEffectsSystem(void)
  307. {
  308. EFFECT *eff;
  309. /* Traverse the list */
  310. for (eff = activeList.first; eff;)
  311. {
  312. EFFECT *effNext = eff->next;
  313. killEffect(eff);
  314. eff = effNext;
  315. }
  316. chunkList.first = NULL;
  317. chunkList.last = NULL;
  318. }
  319. /*!
  320. * Initialise effects system
  321. * Cleans up old effects, allocates a first chunk of effects, initialises (in)active lists
  322. */
  323. void initEffectsSystem(void)
  324. {
  325. EffectChunk *chunk;
  326. /* Clean up old chunks */
  327. shutdownEffectsSystem();
  328. /* Allocate new chunk */
  329. chunk = calloc(1, sizeof(EffectChunk));
  330. /* Deal with out-of-memory conditions */
  331. if (chunk == NULL)
  332. return; // FIXME We have no way to report an error here...
  333. /* Create chunkList */
  334. chunkList.first = chunk;
  335. chunkList.last = chunk;
  336. chunk->next = NULL; // Last element
  337. /* activeList is empty at start */
  338. activeList.num = 0;
  339. activeList.first = NULL;
  340. activeList.last = NULL;
  341. /* inactiveList contains all elements */
  342. inactiveList.num = EFFECT_CHUNK_SIZE;
  343. inactiveList.first = &chunk->effects[0];
  344. inactiveList.last = &chunk->effects[EFFECT_CHUNK_SIZE-1];
  345. /* Initialise free-effects pool */
  346. initEffectPool(inactiveList.first, inactiveList.last);
  347. }
  348. static void positionEffect(const EFFECT *psEffect)
  349. {
  350. int rx, rz;
  351. /* Establish world position */
  352. Vector3i dv = {
  353. (psEffect->position.x - player.p.x) - terrainMidX * TILE_UNITS,
  354. psEffect->position.y,
  355. terrainMidY * TILE_UNITS - (psEffect->position.z - player.p.z)
  356. };
  357. /* Push the indentity matrix */
  358. pie_MatBegin();
  359. /* Move to position */
  360. pie_TRANSLATE(dv.x, dv.y, dv.z);
  361. /* Get the x,z translation components */
  362. rx = map_round(player.p.x);
  363. rz = map_round(player.p.z);
  364. /* Move to camera reference */
  365. pie_TRANSLATE(rx, 0, -rz);
  366. }
  367. static void killEffect(EFFECT *e)
  368. {
  369. /* Put effect back into pool */
  370. Effect_free(e);
  371. }
  372. void effectSetLandLightSpec(LAND_LIGHT_SPEC spec)
  373. {
  374. ellSpec = spec;
  375. }
  376. void effectSetSize(UDWORD size)
  377. {
  378. specifiedSize = size;
  379. }
  380. void addMultiEffect(const Vector3i *basePos, Vector3i *scatter, EFFECT_GROUP group,
  381. EFFECT_TYPE type, bool specified, iIMDShape *imd, unsigned int number, bool lit, unsigned int size)
  382. {
  383. if (number == 0)
  384. {
  385. return;
  386. }
  387. /* Set up the scaling for specified ones */
  388. specifiedSize = size;
  389. /* If there's only one, make sure it's in the centre */
  390. if (number == 1)
  391. {
  392. addEffect(basePos,group,type,specified,imd,lit);
  393. }
  394. else
  395. {
  396. unsigned int i;
  397. /* Fix for jim */
  398. scatter->x/=10;
  399. scatter->y/=10;
  400. scatter->z/=10;
  401. /* There are multiple effects - so scatter them around according to parameter */
  402. for(i=0; i<number; i++)
  403. {
  404. Vector3i scatPos = {
  405. basePos->x + (scatter->x ? ( scatter->x - (rand()%(2*scatter->x)) ) : 0 ),
  406. basePos->y + (scatter->y ? ( scatter->y - (rand()%(2*scatter->y)) ) : 0 ),
  407. basePos->z + (scatter->z ? ( scatter->z - (rand()%(2*scatter->z)) ) : 0 )
  408. };
  409. addEffect(&scatPos,group,type,specified,imd,lit);
  410. }
  411. }
  412. }
  413. // When we need to set the effect for the player's color
  414. void SetEffectForPlayer(uint8_t player)
  415. {
  416. ASSERT(player < MAX_PLAYERS, "player is set to a invalid number of %d", (int) player);
  417. EffectForPlayer = getPlayerColour(player);
  418. }
  419. void addEffect(const Vector3i *pos, EFFECT_GROUP group, EFFECT_TYPE type, bool specified, iIMDShape *imd, int lit)
  420. {
  421. EFFECT *psEffect = NULL;
  422. if(gamePaused())
  423. {
  424. return;
  425. }
  426. /* Retrieve a new effect from pool */
  427. psEffect = Effect_malloc();
  428. /* Deal with out-of-memory conditions */
  429. if (psEffect == NULL) {
  430. debug(LOG_ERROR, "Out of memory");
  431. return; // FIXME We have no way to report an error here...
  432. }
  433. /* Reset control bits */
  434. psEffect->control = 0;
  435. /* Store away it's position - into FRACTS */
  436. psEffect->position.x = pos->x;
  437. psEffect->position.y = pos->y;
  438. psEffect->position.z = pos->z;
  439. /* Now, note group and type */
  440. psEffect->group = group;
  441. psEffect->type = type;
  442. // and if the effect needs the player's color for certain things
  443. psEffect->player = EffectForPlayer;
  444. SetEffectForPlayer(0); // reset it
  445. /* Set when it entered the world */
  446. psEffect->birthTime = psEffect->lastFrame = graphicsTime;
  447. if(group == EFFECT_GRAVITON && (type == GRAVITON_TYPE_GIBLET || type == GRAVITON_TYPE_EMITTING_DR))
  448. {
  449. psEffect->frameNumber = lit;
  450. }
  451. else
  452. {
  453. /* Starts off on frame zero */
  454. psEffect->frameNumber = 0;
  455. }
  456. /*
  457. See what kind of effect it is - the add fucnction is different for each,
  458. although some things are shared
  459. */
  460. psEffect->imd = NULL;
  461. if(lit)
  462. {
  463. SET_LIT(psEffect);
  464. }
  465. if(specified)
  466. {
  467. /* We're specifying what the imd is - override */
  468. psEffect->imd = imd;
  469. psEffect->size = specifiedSize;
  470. }
  471. /* Do all the effect type specific stuff */
  472. switch(group)
  473. {
  474. case EFFECT_SMOKE:
  475. effectSetupSmoke(psEffect);
  476. break;
  477. case EFFECT_GRAVITON:
  478. effectSetupGraviton(psEffect);
  479. break;
  480. case EFFECT_EXPLOSION:
  481. effectSetupExplosion(psEffect);
  482. break;
  483. case EFFECT_CONSTRUCTION:
  484. effectSetupConstruction(psEffect);
  485. break;
  486. case EFFECT_WAYPOINT:
  487. effectSetupWayPoint(psEffect);
  488. break;
  489. case EFFECT_BLOOD:
  490. effectSetupBlood(psEffect);
  491. break;
  492. case EFFECT_DESTRUCTION:
  493. effectSetupDestruction(psEffect);
  494. break;
  495. case EFFECT_FIRE:
  496. effectSetupFire(psEffect);
  497. break;
  498. case EFFECT_SAT_LASER:
  499. effectSetupSatLaser(psEffect);
  500. break;
  501. case EFFECT_FIREWORK:
  502. effectSetupFirework(psEffect);
  503. break;
  504. case EFFECT_STRUCTURE:
  505. case EFFECT_DUST_BALL:
  506. ASSERT( false,"Weirdy group type for an effect" );
  507. break;
  508. }
  509. /* As of yet, it hasn't bounced (or whatever)... */
  510. if(type!=EXPLOSION_TYPE_LAND_LIGHT)
  511. {
  512. psEffect->specific = 0;
  513. }
  514. ASSERT(psEffect->imd != NULL || group == EFFECT_DESTRUCTION || group == EFFECT_FIRE || group == EFFECT_SAT_LASER, "null effect imd");
  515. }
  516. /* Calls all the update functions for each different currently active effect */
  517. void processEffects(void)
  518. {
  519. EFFECT *it;
  520. /* Traverse the list */
  521. for (it = activeList.first; it != NULL;)
  522. {
  523. EFFECT *itNext = it->next; // If updateEffect deletes something, we would be screwed...
  524. /* Run updates, effect may be deleted here */
  525. updateEffect(it);
  526. /* Is it on the grid */
  527. if (clipXY(it->position.x, it->position.z))
  528. {
  529. /* Add it to the bucket */
  530. bucketAddTypeToList(RENDER_EFFECT, it);
  531. }
  532. it = itNext;
  533. }
  534. /* Add any droid effects */
  535. effectDroidUpdates();
  536. /* Add any structure effects */
  537. effectStructureUpdates();
  538. }
  539. /* The general update function for all effects - calls a specific one for each */
  540. static void updateEffect(EFFECT *psEffect)
  541. {
  542. /* What type of effect are we dealing with? */
  543. switch(psEffect->group)
  544. {
  545. case EFFECT_EXPLOSION:
  546. updateExplosion(psEffect);
  547. return;
  548. case EFFECT_WAYPOINT:
  549. if(!gamePaused()) updateWaypoint(psEffect);
  550. return;
  551. case EFFECT_CONSTRUCTION:
  552. if(!gamePaused()) updateConstruction(psEffect);
  553. return;
  554. case EFFECT_SMOKE:
  555. if(!gamePaused()) updatePolySmoke(psEffect);
  556. return;
  557. case EFFECT_STRUCTURE:
  558. return;
  559. case EFFECT_GRAVITON:
  560. if(!gamePaused()) updateGraviton(psEffect);
  561. return;
  562. case EFFECT_BLOOD:
  563. if(!gamePaused()) updateBlood(psEffect);
  564. return;
  565. case EFFECT_DESTRUCTION:
  566. if(!gamePaused()) updateDestruction(psEffect);
  567. return;
  568. case EFFECT_FIRE:
  569. if(!gamePaused()) updateFire(psEffect);
  570. return;
  571. case EFFECT_SAT_LASER:
  572. if(!gamePaused()) updateSatLaser(psEffect);
  573. return;
  574. case EFFECT_FIREWORK:
  575. if(!gamePaused()) updateFirework(psEffect);
  576. return;
  577. case EFFECT_DUST_BALL: // Apparently not a valid effect...
  578. break;
  579. }
  580. debug( LOG_ERROR, "Weirdy class of effect passed to updateEffect" );
  581. abort();
  582. }
  583. // ----------------------------------------------------------------------------------------
  584. // ALL THE UPDATE FUNCTIONS
  585. // ----------------------------------------------------------------------------------------
  586. /** Update the waypoint effects.*/
  587. static void updateWaypoint(EFFECT *psEffect)
  588. {
  589. if(!(keyDown(KEY_LCTRL) || keyDown(KEY_RCTRL) ||
  590. keyDown(KEY_LSHIFT) || keyDown(KEY_RSHIFT)))
  591. {
  592. killEffect(psEffect);
  593. }
  594. }
  595. static void updateFirework(EFFECT *psEffect)
  596. {
  597. UDWORD height;
  598. UDWORD xDif,yDif,radius,val;
  599. Vector3i dv;
  600. SDWORD dif;
  601. UDWORD drop;
  602. /* Move it */
  603. psEffect->position.x += graphicsTimeAdjustedIncrement(psEffect->velocity.x);
  604. psEffect->position.y += graphicsTimeAdjustedIncrement(psEffect->velocity.y);
  605. psEffect->position.z += graphicsTimeAdjustedIncrement(psEffect->velocity.z);
  606. if(psEffect->type == FIREWORK_TYPE_LAUNCHER)
  607. {
  608. height = psEffect->position.y;
  609. if(height > psEffect->size)
  610. {
  611. dv.x = psEffect->position.x;
  612. dv.z = psEffect->position.z;
  613. dv.y = psEffect->position.y + (psEffect->radius / 2);
  614. addEffect(&dv,EFFECT_EXPLOSION,EXPLOSION_TYPE_MEDIUM,false,NULL,0);
  615. audio_PlayStaticTrack(psEffect->position.x, psEffect->position.z, ID_SOUND_EXPLOSION);
  616. for(dif =0; dif < (psEffect->radius*2); dif+=20)
  617. {
  618. if(dif<psEffect->radius)
  619. {
  620. drop = psEffect->radius-dif;
  621. }
  622. else
  623. {
  624. drop = dif - psEffect->radius;
  625. }
  626. radius = (UDWORD)sqrtf(psEffect->radius * psEffect->radius - drop * drop);
  627. //val = getStaticTimeValueRange(720,360); // grab an angle - 4 seconds cyclic
  628. for(val = 0; val<=180; val+=20)
  629. {
  630. xDif = iSinR(DEG(val), radius);
  631. yDif = iCosR(DEG(val), radius);
  632. dv.x = psEffect->position.x + xDif;
  633. dv.z = psEffect->position.z + yDif;
  634. dv.y = psEffect->position.y + dif;
  635. effectGiveAuxVar(100);
  636. addEffect(&dv,EFFECT_FIREWORK, FIREWORK_TYPE_STARBURST,false,NULL,0);
  637. dv.x = psEffect->position.x - xDif;
  638. dv.z = psEffect->position.z - yDif;
  639. dv.y = psEffect->position.y + dif;
  640. effectGiveAuxVar(100);
  641. addEffect(&dv,EFFECT_FIREWORK, FIREWORK_TYPE_STARBURST,false,NULL,0);
  642. dv.x = psEffect->position.x + xDif;
  643. dv.z = psEffect->position.z - yDif;
  644. dv.y = psEffect->position.y + dif;
  645. effectGiveAuxVar(100);
  646. addEffect(&dv,EFFECT_FIREWORK, FIREWORK_TYPE_STARBURST,false,NULL,0);
  647. dv.x = psEffect->position.x - xDif;
  648. dv.z = psEffect->position.z + yDif;
  649. dv.y = psEffect->position.y + dif;
  650. effectGiveAuxVar(100);
  651. addEffect(&dv,EFFECT_FIREWORK, FIREWORK_TYPE_STARBURST,false,NULL,0);
  652. }
  653. }
  654. killEffect(psEffect);
  655. }
  656. else
  657. {
  658. /* Add an effect at the firework's position */
  659. dv.x = psEffect->position.x;
  660. dv.y = psEffect->position.y;
  661. dv.z = psEffect->position.z;
  662. /* Add a trail graphic */
  663. addEffect(&dv,EFFECT_SMOKE,SMOKE_TYPE_TRAIL,false,NULL,0);
  664. }
  665. }
  666. else // must be a startburst
  667. {
  668. /* Time to update the frame number on the smoke sprite */
  669. if(graphicsTime - psEffect->lastFrame > psEffect->frameDelay)
  670. {
  671. /* Store away last frame change time */
  672. psEffect->lastFrame = graphicsTime;
  673. /* Are we on the last frame? */
  674. if(++psEffect->frameNumber >= effectGetNumFrames(psEffect))
  675. {
  676. /* Does the anim wrap around? */
  677. if(TEST_CYCLIC(psEffect))
  678. {
  679. psEffect->frameNumber = 0;
  680. }
  681. else
  682. {
  683. /* Kill it off */
  684. killEffect(psEffect);
  685. return;
  686. }
  687. }
  688. }
  689. /* If it doesn't get killed by frame number, then by age */
  690. if(TEST_CYCLIC(psEffect))
  691. {
  692. /* Has it overstayed it's welcome? */
  693. if(graphicsTime - psEffect->birthTime > psEffect->lifeSpan)
  694. {
  695. /* Kill it */
  696. killEffect(psEffect);
  697. }
  698. }
  699. }
  700. }
  701. static void updateSatLaser(EFFECT *psEffect)
  702. {
  703. Vector3i dv;
  704. UDWORD val;
  705. UDWORD radius;
  706. UDWORD xDif,yDif;
  707. UDWORD i;
  708. UDWORD startHeight,endHeight;
  709. iIMDShape *pie;
  710. UDWORD xPos,yPos;
  711. LIGHT light;
  712. // Do these here cause there used by the lighting code below this if.
  713. xPos = psEffect->position.x;
  714. startHeight = psEffect->position.y;
  715. endHeight = startHeight+1064;
  716. yPos = psEffect->position.z;
  717. if(psEffect->baseScale)
  718. {
  719. psEffect->baseScale = 0;
  720. pie = getImdFromIndex(MI_FLAME);
  721. /* Add some big explosions....! */
  722. for(i=0; i<16; i++)
  723. {
  724. dv.x = xPos+(200-rand()%400);
  725. dv.z = yPos+(200-rand()%400);
  726. dv.y = startHeight + rand()%100;
  727. addEffect(&dv,EFFECT_EXPLOSION,EXPLOSION_TYPE_MEDIUM,false,NULL,0);
  728. }
  729. /* Add a sound effect */
  730. audio_PlayStaticTrack(psEffect->position.x, psEffect->position.z, ID_SOUND_EXPLOSION);
  731. /* Add a shockwave */
  732. dv.x = xPos;
  733. dv.z = yPos;
  734. dv.y = startHeight+SHOCK_WAVE_HEIGHT;
  735. addEffect(&dv,EFFECT_EXPLOSION,EXPLOSION_TYPE_SHOCKWAVE,false,NULL,0);
  736. /* Now, add the column of light */
  737. for(i=startHeight; i<endHeight; i+=56)
  738. {
  739. radius = 80;
  740. /* Add 36 around in a circle..! */
  741. for(val = 0; val<=180; val+=30)
  742. {
  743. xDif = iSinR(DEG(val), radius);
  744. yDif = iCosR(DEG(val), radius);
  745. dv.x = xPos+xDif+i/64;
  746. dv.z = yPos+yDif;
  747. dv.y = startHeight+i;
  748. effectGiveAuxVar(100);
  749. addEffect(&dv,EFFECT_EXPLOSION, EXPLOSION_TYPE_MEDIUM,false,NULL,0);
  750. dv.x = xPos-xDif+i/64;
  751. dv.z = yPos-yDif;
  752. dv.y = startHeight+i;
  753. effectGiveAuxVar(100);
  754. addEffect(&dv,EFFECT_EXPLOSION, EXPLOSION_TYPE_MEDIUM,false,NULL,0);
  755. dv.x = xPos+xDif+i/64;
  756. dv.z = yPos-yDif;
  757. dv.y = startHeight+i;
  758. effectGiveAuxVar(100);
  759. addEffect(&dv,EFFECT_EXPLOSION, EXPLOSION_TYPE_MEDIUM,false,NULL,0);
  760. dv.x = xPos-xDif+i/64;
  761. dv.z = yPos+yDif;
  762. dv.y = startHeight+i;
  763. effectGiveAuxVar(100);
  764. addEffect(&dv,EFFECT_EXPLOSION, EXPLOSION_TYPE_MEDIUM,false,NULL,0);
  765. }
  766. }
  767. }
  768. if(graphicsTime - psEffect->birthTime < 1000)
  769. {
  770. light.position.x = xPos;
  771. light.position.y = startHeight;
  772. light.position.z = yPos;
  773. light.range = 800;
  774. light.colour = LIGHT_BLUE;
  775. processLight(&light);
  776. }
  777. else
  778. {
  779. killEffect(psEffect);
  780. }
  781. }
  782. /** The update function for the explosions */
  783. static void updateExplosion(EFFECT *psEffect)
  784. {
  785. LIGHT light;
  786. UDWORD percent;
  787. UDWORD range;
  788. float scaling;
  789. if (TEST_LIT(psEffect))
  790. {
  791. if (psEffect->lifeSpan)
  792. {
  793. percent = PERCENT(graphicsTime - psEffect->birthTime, psEffect->lifeSpan);
  794. if (percent > 100)
  795. {
  796. percent = 100;
  797. }
  798. else if (percent > 50)
  799. {
  800. percent = 100 - percent;
  801. }
  802. }
  803. else
  804. {
  805. percent = 100;
  806. }
  807. range = percent;
  808. light.position.x = psEffect->position.x;
  809. light.position.y = psEffect->position.y;
  810. light.position.z = psEffect->position.z;
  811. light.range = (3*range)/2;
  812. light.colour = LIGHT_RED;
  813. processLight(&light);
  814. }
  815. if (psEffect->type == EXPLOSION_TYPE_SHOCKWAVE)
  816. {
  817. psEffect->size += graphicsTimeAdjustedIncrement(SHOCKWAVE_SPEED);
  818. scaling = (float)psEffect->size / (float)MAX_SHOCKWAVE_SIZE;
  819. psEffect->frameNumber = scaling * effectGetNumFrames(psEffect);
  820. light.position.x = psEffect->position.x;
  821. light.position.y = psEffect->position.y;
  822. light.position.z = psEffect->position.z;
  823. light.range = psEffect->size+200;
  824. light.colour = LIGHT_YELLOW;
  825. processLight(&light);
  826. if (psEffect->size > MAX_SHOCKWAVE_SIZE || light.range > 600)
  827. {
  828. /* Kill it off */
  829. killEffect(psEffect);
  830. return;
  831. }
  832. }
  833. /* Time to update the frame number on the explosion */
  834. else if (graphicsTime - psEffect->lastFrame > psEffect->frameDelay)
  835. {
  836. psEffect->lastFrame = graphicsTime;
  837. /* Are we on the last frame? */
  838. if (++psEffect->frameNumber >= effectGetNumFrames(psEffect))
  839. {
  840. if (psEffect->type != EXPLOSION_TYPE_LAND_LIGHT)
  841. {
  842. /* Kill it off */
  843. killEffect(psEffect);
  844. return;
  845. }
  846. else
  847. {
  848. psEffect->frameNumber = 0;
  849. }
  850. }
  851. }
  852. if(!gamePaused())
  853. {
  854. /* Tesla explosions are the only ones that rise, or indeed move */
  855. if(psEffect->type == EXPLOSION_TYPE_TESLA)
  856. {
  857. psEffect->position.y += graphicsTimeAdjustedIncrement(psEffect->velocity.y);
  858. }
  859. }
  860. }
  861. /** The update function for blood */
  862. static void updateBlood(EFFECT *psEffect)
  863. {
  864. /* Time to update the frame number on the blood */
  865. if(graphicsTime - psEffect->lastFrame > psEffect->frameDelay)
  866. {
  867. psEffect->lastFrame = graphicsTime;
  868. /* Are we on the last frame? */
  869. if(++psEffect->frameNumber >= effectGetNumFrames(psEffect))
  870. {
  871. /* Kill it off */
  872. killEffect(psEffect);
  873. return;
  874. }
  875. }
  876. /* Move it about in the world */
  877. psEffect->position.x += graphicsTimeAdjustedIncrement(psEffect->velocity.x);
  878. psEffect->position.y += graphicsTimeAdjustedIncrement(psEffect->velocity.y);
  879. psEffect->position.z += graphicsTimeAdjustedIncrement(psEffect->velocity.z);
  880. }
  881. /** Processes all the drifting smoke
  882. Handles the smoke puffing out the factory as well */
  883. static void updatePolySmoke(EFFECT *psEffect)
  884. {
  885. /* Time to update the frame number on the smoke sprite */
  886. if(graphicsTime - psEffect->lastFrame > psEffect->frameDelay)
  887. {
  888. /* Store away last frame change time */
  889. psEffect->lastFrame = graphicsTime;
  890. /* Are we on the last frame? */
  891. if(++psEffect->frameNumber >= effectGetNumFrames(psEffect))
  892. {
  893. /* Does the anim wrap around? */
  894. if(TEST_CYCLIC(psEffect))
  895. {
  896. /* Does it change drift direction? */
  897. if(psEffect->type == SMOKE_TYPE_DRIFTING)
  898. {
  899. /* Make it change direction */
  900. psEffect->velocity.x = (float)(rand() % 20);
  901. psEffect->velocity.z = (float)(10 - rand() % 20);
  902. psEffect->velocity.y = (float)(10 + rand() % 20);
  903. }
  904. /* Reset the frame */
  905. psEffect->frameNumber = 0;
  906. }
  907. else
  908. {
  909. /* Kill it off */
  910. killEffect(psEffect);
  911. return;
  912. }
  913. }
  914. }
  915. /* Update position */
  916. psEffect->position.x += graphicsTimeAdjustedIncrement(psEffect->velocity.x);
  917. psEffect->position.y += graphicsTimeAdjustedIncrement(psEffect->velocity.y);
  918. psEffect->position.z += graphicsTimeAdjustedIncrement(psEffect->velocity.z);
  919. /* If it doesn't get killed by frame number, then by age */
  920. if(TEST_CYCLIC(psEffect))
  921. {
  922. /* Has it overstayed it's welcome? */
  923. if(graphicsTime - psEffect->birthTime > psEffect->lifeSpan)
  924. {
  925. /* Kill it */
  926. killEffect(psEffect);
  927. }
  928. }
  929. }
  930. /**
  931. Gravitons just fly up for a bit and then drop down and are
  932. killed off when they hit the ground
  933. */
  934. static void updateGraviton(EFFECT *psEffect)
  935. {
  936. float accel;
  937. Vector3i dv;
  938. UDWORD groundHeight;
  939. MAPTILE *psTile;
  940. LIGHT light;
  941. if(psEffect->type!=GRAVITON_TYPE_GIBLET)
  942. {
  943. light.position.x = psEffect->position.x;
  944. light.position.y = psEffect->position.y;
  945. light.position.z = psEffect->position.z;
  946. light.range = 128;
  947. light.colour = LIGHT_YELLOW;
  948. processLight(&light);
  949. }
  950. if(gamePaused())
  951. {
  952. /* Only update the lights if it's paused */
  953. return;
  954. }
  955. /* Move it about in the world */
  956. psEffect->position.x += graphicsTimeAdjustedIncrement(psEffect->velocity.x);
  957. psEffect->position.y += graphicsTimeAdjustedIncrement(psEffect->velocity.y);
  958. psEffect->position.z += graphicsTimeAdjustedIncrement(psEffect->velocity.z);
  959. /* If it's bounced/drifted off the map then kill it */
  960. if (map_coord(psEffect->position.x) >= mapWidth
  961. || map_coord(psEffect->position.z) >= mapHeight)
  962. {
  963. killEffect(psEffect);
  964. return;
  965. }
  966. groundHeight = map_Height(psEffect->position.x, psEffect->position.z);
  967. /* If it's going up and it's still under the landscape, then remove it... */
  968. if (psEffect->position.y<groundHeight
  969. && psEffect->velocity.y > 0)
  970. {
  971. killEffect(psEffect);
  972. return;
  973. }
  974. /* Does it emit a trail? And is it high enough? */
  975. if ((psEffect->type == GRAVITON_TYPE_EMITTING_DR)
  976. || ((psEffect->type == GRAVITON_TYPE_EMITTING_ST)
  977. && (psEffect->position.y>(groundHeight+10))))
  978. {
  979. /* Time to add another trail 'thing'? */
  980. if(graphicsTime > psEffect->lastFrame + psEffect->frameDelay)
  981. {
  982. /* Store away last update */
  983. psEffect->lastFrame = graphicsTime;
  984. /* Add an effect at the gravitons's position */
  985. dv.x = psEffect->position.x;
  986. dv.y = psEffect->position.y;
  987. dv.z = psEffect->position.z;
  988. /* Add a trail graphic */
  989. addEffect(&dv,EFFECT_SMOKE,SMOKE_TYPE_TRAIL,false,NULL,0);
  990. }
  991. }
  992. else if(psEffect->type == GRAVITON_TYPE_GIBLET && (psEffect->position.y>(groundHeight+5)))
  993. {
  994. /* Time to add another trail 'thing'? */
  995. if(graphicsTime > psEffect->lastFrame + psEffect->frameDelay)
  996. {
  997. /* Store away last update */
  998. psEffect->lastFrame = graphicsTime;
  999. /* Add an effect at the gravitons's position */
  1000. dv.x = psEffect->position.x;
  1001. dv.y = psEffect->position.y;
  1002. dv.z = psEffect->position.z;
  1003. addEffect(&dv,EFFECT_BLOOD,BLOOD_TYPE_NORMAL,false,NULL,0);
  1004. }
  1005. }
  1006. /* Spin it round a bit */
  1007. psEffect->rotation.x += graphicsTimeAdjustedIncrement(psEffect->spin.x);
  1008. psEffect->rotation.y += graphicsTimeAdjustedIncrement(psEffect->spin.y);
  1009. psEffect->rotation.z += graphicsTimeAdjustedIncrement(psEffect->spin.z);
  1010. /* Update velocity (and retarding of descent) according to present frame rate */
  1011. accel = graphicsTimeAdjustedIncrement(GRAVITON_GRAVITY);
  1012. psEffect->velocity.y += accel;
  1013. /* If it's bounced/drifted off the map then kill it */
  1014. if ((int)psEffect->position.x <= TILE_UNITS
  1015. || (int)psEffect->position.z <= TILE_UNITS)
  1016. {
  1017. killEffect(psEffect);
  1018. return;
  1019. }
  1020. /* Are we below it? - Hit the ground? */
  1021. if ((int)psEffect->position.y < (int)groundHeight)
  1022. {
  1023. psTile = mapTile(map_coord(psEffect->position.x), map_coord(psEffect->position.z));
  1024. if (terrainType(psTile) == TER_WATER)
  1025. {
  1026. killEffect(psEffect);
  1027. return;
  1028. }
  1029. else
  1030. /* Are we falling - rather than rising? */
  1031. if((int)psEffect->velocity.y < 0)
  1032. {
  1033. /* Has it sufficient energy to keep bouncing? */
  1034. if (abs(psEffect->velocity.y) > 16
  1035. && psEffect->specific <= 2)
  1036. {
  1037. psEffect->specific++;
  1038. /* Half it's velocity */
  1039. psEffect->velocity.y/=(float)(-2); // only y gets flipped
  1040. /* Set it at ground level - may have gone through */
  1041. psEffect->position.y = (float)groundHeight;
  1042. }
  1043. else
  1044. {
  1045. /* Giblets don't blow up when they hit the ground! */
  1046. if(psEffect->type!=GRAVITON_TYPE_GIBLET)
  1047. {
  1048. /* Remove the graviton and add an explosion */
  1049. dv.x = psEffect->position.x;
  1050. dv.y = psEffect->position.y + 10;
  1051. dv.z = psEffect->position.z;
  1052. addEffect(&dv,EFFECT_EXPLOSION,EXPLOSION_TYPE_VERY_SMALL,false,NULL,0);
  1053. }
  1054. killEffect(psEffect);
  1055. return;
  1056. }
  1057. }
  1058. }
  1059. }
  1060. /** This isn't really an on-screen effect itself - it just spawns other ones.... */
  1061. static void updateDestruction(EFFECT *psEffect)
  1062. {
  1063. Vector3i pos;
  1064. UDWORD effectType;
  1065. UDWORD widthScatter = 0, breadthScatter = 0, heightScatter = 0;
  1066. SDWORD iX, iY;
  1067. LIGHT light;
  1068. UDWORD percent;
  1069. UDWORD range;
  1070. float div;
  1071. UDWORD height;
  1072. percent = PERCENT(graphicsTime - psEffect->birthTime, psEffect->lifeSpan);
  1073. if(percent > 100)
  1074. {
  1075. percent = 100;
  1076. }
  1077. range = 50 - abs(50-percent);
  1078. light.position.x = psEffect->position.x;
  1079. light.position.y = psEffect->position.y;
  1080. light.position.z = psEffect->position.z;
  1081. if(psEffect->type == DESTRUCTION_TYPE_STRUCTURE)
  1082. {
  1083. light.range = range*10;
  1084. }
  1085. else
  1086. {
  1087. light.range = range*4;
  1088. }
  1089. if(psEffect->type == DESTRUCTION_TYPE_POWER_STATION)
  1090. {
  1091. light.range *=3;
  1092. light.colour = LIGHT_WHITE;
  1093. }
  1094. else
  1095. {
  1096. light.colour = LIGHT_RED;
  1097. }
  1098. processLight(&light);
  1099. if(graphicsTime > psEffect->birthTime + psEffect->lifeSpan)
  1100. {
  1101. /* Kill it - it's too old */
  1102. killEffect(psEffect);
  1103. return;
  1104. }
  1105. if(psEffect->type == DESTRUCTION_TYPE_SKYSCRAPER)
  1106. {
  1107. if(graphicsTime - psEffect->birthTime > psEffect->lifeSpan * 9/10)
  1108. {
  1109. pos.x = psEffect->position.x;
  1110. pos.z = psEffect->position.z;
  1111. pos.y = psEffect->position.y;
  1112. addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_LARGE,false,NULL,0);
  1113. killEffect(psEffect);
  1114. return;
  1115. }
  1116. div = 1.f - (float)(graphicsTime - psEffect->birthTime) / psEffect->lifeSpan;
  1117. if(div < 0.f)
  1118. div = 0.f;
  1119. height = div * psEffect->imd->max.y;
  1120. }
  1121. else
  1122. {
  1123. height = 16;
  1124. }
  1125. /* Time to add another effect? */
  1126. if((graphicsTime - psEffect->lastFrame) > psEffect->frameDelay)
  1127. {
  1128. psEffect->lastFrame = graphicsTime;
  1129. switch(psEffect->type)
  1130. {
  1131. case DESTRUCTION_TYPE_SKYSCRAPER:
  1132. widthScatter = TILE_UNITS;
  1133. breadthScatter = TILE_UNITS;
  1134. heightScatter = TILE_UNITS;
  1135. break;
  1136. case DESTRUCTION_TYPE_POWER_STATION:
  1137. case DESTRUCTION_TYPE_STRUCTURE:
  1138. widthScatter = TILE_UNITS/2;
  1139. breadthScatter = TILE_UNITS/2;
  1140. heightScatter = TILE_UNITS/4;
  1141. break;
  1142. case DESTRUCTION_TYPE_DROID:
  1143. case DESTRUCTION_TYPE_WALL_SECTION:
  1144. case DESTRUCTION_TYPE_FEATURE:
  1145. widthScatter = TILE_UNITS/6;
  1146. breadthScatter = TILE_UNITS/6;
  1147. heightScatter = TILE_UNITS/6;
  1148. break;
  1149. default:
  1150. ASSERT( false,"Weirdy destruction type effect" );
  1151. break;
  1152. }
  1153. /* Find a position to dump it at */
  1154. pos.x = psEffect->position.x + widthScatter - rand() % (2 * widthScatter);
  1155. pos.z = psEffect->position.z + breadthScatter - rand() % (2 * breadthScatter);
  1156. pos.y = psEffect->position.y + height + rand() % heightScatter;
  1157. if(psEffect->type == DESTRUCTION_TYPE_SKYSCRAPER)
  1158. {
  1159. pos.y = psEffect->position.y + height;
  1160. }
  1161. /* Choose an effect */
  1162. effectType = rand()%15;
  1163. switch(effectType)
  1164. {
  1165. case 0:
  1166. addEffect(&pos,EFFECT_SMOKE,SMOKE_TYPE_DRIFTING,false,NULL,0);
  1167. break;
  1168. case 1:
  1169. case 2:
  1170. case 3:
  1171. case 4:
  1172. case 5:
  1173. if(psEffect->type == DESTRUCTION_TYPE_SKYSCRAPER)
  1174. {
  1175. addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_LARGE,false,NULL,0);
  1176. }
  1177. /* Only structures get the big explosions */
  1178. else if(psEffect->type==DESTRUCTION_TYPE_STRUCTURE)
  1179. {
  1180. addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_MEDIUM,false,NULL,0);
  1181. }
  1182. else
  1183. {
  1184. addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_SMALL,false,NULL,0);
  1185. }
  1186. break;
  1187. case 6:
  1188. case 7:
  1189. case 8:
  1190. case 9:
  1191. case 10:
  1192. if(psEffect->type == DESTRUCTION_TYPE_STRUCTURE)
  1193. {
  1194. addEffect(&pos,EFFECT_GRAVITON,GRAVITON_TYPE_EMITTING_ST,true,getRandomDebrisImd(),0);
  1195. }
  1196. else
  1197. {
  1198. addEffect(&pos,EFFECT_GRAVITON,GRAVITON_TYPE_EMITTING_DR,true,getRandomDebrisImd(),0);
  1199. }
  1200. break;
  1201. case 11:
  1202. addEffect(&pos,EFFECT_SMOKE,SMOKE_TYPE_DRIFTING,false,NULL,0);
  1203. break;
  1204. case 12:
  1205. case 13:
  1206. addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_SMALL,false,NULL,0);
  1207. break;
  1208. case 14:
  1209. /* Add sound effect, but only if we're less than 3/4 of the way thru' destruction */
  1210. if (graphicsTime < (psEffect->birthTime + psEffect->lifeSpan) * 3/4)
  1211. {
  1212. iX = psEffect->position.x;
  1213. iY = psEffect->position.z;
  1214. audio_PlayStaticTrack( iX, iY, ID_SOUND_EXPLOSION );
  1215. }
  1216. break;
  1217. }
  1218. }
  1219. }
  1220. /** Moves the construction graphic about - dust cloud or whatever.... */
  1221. static void updateConstruction(EFFECT *psEffect)
  1222. {
  1223. /* Time to update the frame number on the construction sprite */
  1224. if(graphicsTime - psEffect->lastFrame > psEffect->frameDelay)
  1225. {
  1226. psEffect->lastFrame = graphicsTime;
  1227. /* Are we on the last frame? */
  1228. if(++psEffect->frameNumber >= effectGetNumFrames(psEffect))
  1229. {
  1230. /* Is it a cyclic sprite? */
  1231. if(TEST_CYCLIC(psEffect))
  1232. {
  1233. psEffect->frameNumber = 0;
  1234. }
  1235. else
  1236. {
  1237. killEffect(psEffect);
  1238. return;
  1239. }
  1240. }
  1241. }
  1242. /* Move it about in the world */
  1243. psEffect->position.x += graphicsTimeAdjustedIncrement(psEffect->velocity.x);
  1244. psEffect->position.y += graphicsTimeAdjustedIncrement(psEffect->velocity.y);
  1245. psEffect->position.z += graphicsTimeAdjustedIncrement(psEffect->velocity.z);
  1246. /* If it doesn't get killed by frame number, then by height */
  1247. if(TEST_CYCLIC(psEffect))
  1248. {
  1249. /* Has it hit the ground */
  1250. if ((int)psEffect->position.y <=
  1251. map_Height(psEffect->position.x, psEffect->position.z))
  1252. {
  1253. killEffect(psEffect);
  1254. return;
  1255. }
  1256. if (graphicsTime - psEffect->birthTime > psEffect->lifeSpan)
  1257. {
  1258. killEffect(psEffect);
  1259. return;
  1260. }
  1261. }
  1262. }
  1263. /** Update fire sequences */
  1264. static void updateFire(EFFECT *psEffect)
  1265. {
  1266. Vector3i pos;
  1267. LIGHT light;
  1268. UDWORD percent;
  1269. percent = PERCENT(graphicsTime - psEffect->birthTime, psEffect->lifeSpan);
  1270. if(percent > 100)
  1271. {
  1272. percent = 100;
  1273. }
  1274. light.position.x = psEffect->position.x;
  1275. light.position.y = psEffect->position.y;
  1276. light.position.z = psEffect->position.z;
  1277. light.range = (percent*psEffect->radius*3)/100;
  1278. light.colour = LIGHT_RED;
  1279. processLight(&light);
  1280. /* Time to update the frame number on the construction sprite */
  1281. if (graphicsTime - psEffect->lastFrame > psEffect->frameDelay)
  1282. {
  1283. psEffect->lastFrame = graphicsTime;
  1284. pos.x = (psEffect->position.x + ((rand() % psEffect->radius) - (rand() % (2 * psEffect->radius))));
  1285. pos.z = (psEffect->position.z + ((rand() % psEffect->radius) - (rand() % (2 * psEffect->radius))));
  1286. // Effect is off map, no need to update it anymore
  1287. if(!worldOnMap(pos.x, pos.z))
  1288. {
  1289. killEffect(psEffect);
  1290. return;
  1291. }
  1292. pos.y = map_Height(pos.x,pos.z);
  1293. if(psEffect->type == FIRE_TYPE_SMOKY_BLUE)
  1294. {
  1295. addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_FLAMETHROWER,false,NULL,0);
  1296. }
  1297. else
  1298. {
  1299. addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_SMALL,false,NULL,0);
  1300. }
  1301. if(psEffect->type == FIRE_TYPE_SMOKY || psEffect->type == FIRE_TYPE_SMOKY_BLUE)
  1302. {
  1303. pos.x = (psEffect->position.x + ((rand() % psEffect->radius / 2) - (rand() % (2 * psEffect->radius / 2))));
  1304. pos.z = (psEffect->position.z + ((rand() % psEffect->radius / 2) - (rand() % (2 * psEffect->radius / 2))));
  1305. pos.y = map_Height(pos.x,pos.z);
  1306. addEffect(&pos,EFFECT_SMOKE,SMOKE_TYPE_DRIFTING_HIGH,false,NULL,0);
  1307. }
  1308. else
  1309. {
  1310. pos.x = (psEffect->position.x + ((rand() % psEffect->radius) - (rand() % (2 * psEffect->radius))));
  1311. pos.z = (psEffect->position.z + ((rand() % psEffect->radius) - (rand() % (2 * psEffect->radius))));
  1312. // Effect is off map, no need to update it anymore
  1313. if(!worldOnMap(pos.x, pos.z))
  1314. {
  1315. killEffect(psEffect);
  1316. return;
  1317. }
  1318. pos.y = map_Height(pos.x,pos.z);
  1319. addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_SMALL,false,NULL,0);
  1320. }
  1321. /*
  1322. pos.x = psEffect->position.x;
  1323. pos.y = psEffect->position.y;
  1324. pos.z = psEffect->position.z;
  1325. scatter.x = psEffect->radius; scatter.y = 0; scatter.z = psEffect->radius;
  1326. addMultiEffect(&pos,&scatter,EFFECT_EXPLOSION,EXPLOSION_TYPE_SMALL,false,NULL,2,0,0);
  1327. */
  1328. }
  1329. if (graphicsTime - psEffect->birthTime > psEffect->lifeSpan)
  1330. {
  1331. killEffect(psEffect);
  1332. return;
  1333. }
  1334. }
  1335. // ----------------------------------------------------------------------------------------
  1336. // ALL THE RENDER FUNCTIONS
  1337. // ----------------------------------------------------------------------------------------
  1338. /** Calls the appropriate render routine for each type of effect */
  1339. void renderEffect(const EFFECT *psEffect)
  1340. {
  1341. /* What type of effect are we dealing with? */
  1342. switch(psEffect->group)
  1343. {
  1344. case EFFECT_WAYPOINT:
  1345. renderWaypointEffect(psEffect);
  1346. return;
  1347. case EFFECT_EXPLOSION:
  1348. renderExplosionEffect(psEffect);
  1349. return;
  1350. case EFFECT_CONSTRUCTION:
  1351. renderConstructionEffect(psEffect);
  1352. return;
  1353. case EFFECT_SMOKE:
  1354. renderSmokeEffect(psEffect);
  1355. return;
  1356. case EFFECT_GRAVITON:
  1357. renderGravitonEffect(psEffect);
  1358. return;
  1359. case EFFECT_BLOOD:
  1360. renderBloodEffect(psEffect);
  1361. return;
  1362. case EFFECT_STRUCTURE:
  1363. return;
  1364. case EFFECT_DESTRUCTION:
  1365. /* There is no display func for a destruction effect -
  1366. it merely spawn other effects over time */
  1367. renderDestructionEffect(psEffect);
  1368. return;
  1369. case EFFECT_FIRE:
  1370. /* Likewise */
  1371. return;
  1372. case EFFECT_SAT_LASER:
  1373. /* Likewise */
  1374. return;
  1375. case EFFECT_FIREWORK:
  1376. renderFirework(psEffect);
  1377. return;
  1378. case EFFECT_DUST_BALL: // Apparently not a valid effect...
  1379. break;
  1380. }
  1381. debug( LOG_ERROR, "Weirdy class of effect passed to renderEffect" );
  1382. abort();
  1383. }
  1384. /** drawing func for wapypoints */
  1385. static void renderWaypointEffect(const EFFECT *psEffect)
  1386. {
  1387. positionEffect(psEffect);
  1388. pie_Draw3DShape(psEffect->imd, 0, 0, WZCOL_WHITE, WZCOL_BLACK, 0, 0);
  1389. pie_MatEnd();
  1390. }
  1391. static void renderFirework(const EFFECT *psEffect)
  1392. {
  1393. /* these don't get rendered */
  1394. if(psEffect->type == FIREWORK_TYPE_LAUNCHER)
  1395. {
  1396. return;
  1397. }
  1398. positionEffect(psEffect);
  1399. pie_MatRotY(-player.r.y);
  1400. pie_MatRotX(-player.r.x);
  1401. pie_MatScale(psEffect->size / 100.f);
  1402. pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, 0, WZCOL_WHITE, WZCOL_BLACK, pie_ADDITIVE, EFFECT_EXPLOSION_ADDITIVE);
  1403. pie_MatEnd();
  1404. }
  1405. /** drawing func for blood. */
  1406. static void renderBloodEffect(const EFFECT *psEffect)
  1407. {
  1408. positionEffect(psEffect);
  1409. pie_MatRotY(-player.r.y);
  1410. pie_MatRotX(-player.r.x);
  1411. pie_MatScale(psEffect->size / 100.f);
  1412. pie_Draw3DShape(getImdFromIndex(MI_BLOOD), psEffect->frameNumber, 0, WZCOL_WHITE, WZCOL_BLACK, pie_TRANSLUCENT, EFFECT_BLOOD_TRANSPARENCY);
  1413. pie_MatEnd();
  1414. }
  1415. static void renderDestructionEffect(const EFFECT *psEffect)
  1416. {
  1417. float div;
  1418. SDWORD percent;
  1419. if(psEffect->type!=DESTRUCTION_TYPE_SKYSCRAPER)
  1420. {
  1421. return;
  1422. }
  1423. positionEffect(psEffect);
  1424. div = (float)(graphicsTime - psEffect->birthTime) / psEffect->lifeSpan;
  1425. if(div>1.0) div = 1.0; //temporary!
  1426. {
  1427. div = 1.0 - div;
  1428. percent = (SDWORD)(div*pie_RAISE_SCALE);
  1429. }
  1430. if(!gamePaused())
  1431. {
  1432. pie_MatRotX(SKY_SHIMMY);
  1433. pie_MatRotY(SKY_SHIMMY);
  1434. pie_MatRotZ(SKY_SHIMMY);
  1435. }
  1436. pie_Draw3DShape(psEffect->imd, 0, 0, WZCOL_WHITE, WZCOL_BLACK, pie_RAISE, percent);
  1437. pie_MatEnd();
  1438. }
  1439. static bool rejectLandLight(LAND_LIGHT_SPEC type)
  1440. {
  1441. unsigned int timeSlice = graphicsTime%2000;
  1442. if (timeSlice < 400)
  1443. {
  1444. return (type != LL_MIDDLE); // reject all expect middle
  1445. }
  1446. else if (timeSlice < 800)
  1447. {
  1448. return (type == LL_OUTER); // reject only outer
  1449. }
  1450. else if (timeSlice < 1200)
  1451. {
  1452. return(false); //reject none
  1453. }
  1454. else if (timeSlice < 1600)
  1455. {
  1456. return (type == LL_OUTER); // reject only outer
  1457. }
  1458. else
  1459. {
  1460. return (type != LL_MIDDLE); // reject all expect middle
  1461. }
  1462. }
  1463. /** Renders the standard explosion effect */
  1464. static void renderExplosionEffect(const EFFECT *psEffect)
  1465. {
  1466. const PIELIGHT brightness = WZCOL_WHITE;
  1467. if(psEffect->type == EXPLOSION_TYPE_LAND_LIGHT)
  1468. {
  1469. if(rejectLandLight((LAND_LIGHT_SPEC)psEffect->specific))
  1470. {
  1471. return;
  1472. }
  1473. }
  1474. positionEffect(psEffect);
  1475. /* Bit in comments - doesn't quite work yet? */
  1476. if(TEST_FACING(psEffect))
  1477. {
  1478. /* Always face the viewer! */
  1479. pie_MatRotY(-player.r.y);
  1480. pie_MatRotX(-player.r.x);
  1481. }
  1482. /* Tesla explosions diminish in size */
  1483. if(psEffect->type == EXPLOSION_TYPE_TESLA)
  1484. {
  1485. float scale = (graphicsTime - psEffect->birthTime) / (float)psEffect->lifeSpan;
  1486. if (scale < 0.f)
  1487. scale = 0.f;
  1488. else if (scale > .45f)
  1489. scale = .45f;
  1490. pie_MatScale(psEffect->size / 100.f - scale);
  1491. }
  1492. else if(psEffect->type == EXPLOSION_TYPE_PLASMA)
  1493. {
  1494. float scale = (graphicsTime - psEffect->birthTime) / (float)psEffect->lifeSpan / 3.f;
  1495. pie_MatScale(BASE_PLASMA_SIZE / 100.f + scale);
  1496. }
  1497. else
  1498. {
  1499. pie_MatScale(psEffect->size / 100.f);
  1500. }
  1501. if(psEffect->type == EXPLOSION_TYPE_PLASMA)
  1502. {
  1503. pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, 0, brightness, WZCOL_BLACK, pie_ADDITIVE, EFFECT_PLASMA_ADDITIVE);
  1504. }
  1505. else if(psEffect->type == EXPLOSION_TYPE_KICKUP)
  1506. {
  1507. pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, 0, brightness, WZCOL_BLACK, pie_TRANSLUCENT, 128);
  1508. }
  1509. else
  1510. {
  1511. pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, 0, brightness, WZCOL_BLACK, pie_ADDITIVE, EFFECT_EXPLOSION_ADDITIVE);
  1512. }
  1513. pie_MatEnd();
  1514. }
  1515. static void renderGravitonEffect(const EFFECT *psEffect)
  1516. {
  1517. positionEffect(psEffect);
  1518. pie_MatRotX(psEffect->rotation.x);
  1519. pie_MatRotY(psEffect->rotation.y);
  1520. pie_MatRotZ(psEffect->rotation.z);
  1521. /* Buildings emitted by gravitons are chunkier */
  1522. if(psEffect->type == GRAVITON_TYPE_EMITTING_ST)
  1523. {
  1524. /* Twice as big - 150 percent */
  1525. pie_MatScale(psEffect->size / 100.f);
  1526. }
  1527. pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, psEffect->player, WZCOL_WHITE, WZCOL_BLACK, 0, 0);
  1528. /* Pop the matrix */
  1529. pie_MatEnd();
  1530. }
  1531. /** Renders the standard construction effect */
  1532. static void renderConstructionEffect(const EFFECT *psEffect)
  1533. {
  1534. Vector3i null;
  1535. SDWORD percent;
  1536. UDWORD translucency;
  1537. float size;
  1538. /* No rotation about arbitrary axis */
  1539. null.x = null.y = null.z = 0;
  1540. positionEffect(psEffect);
  1541. /* Bit in comments doesn't quite work yet? */
  1542. if(TEST_FACING(psEffect))
  1543. {
  1544. pie_MatRotY(-player.r.y);
  1545. pie_MatRotX(-player.r.x);
  1546. }
  1547. /* Scale size according to age */
  1548. percent = PERCENT(graphicsTime - psEffect->birthTime, psEffect->lifeSpan);
  1549. if(percent<0) percent = 0;
  1550. if(percent>100) percent = 100;
  1551. /* Make imds be transparent on 3dfx */
  1552. if(percent<50)
  1553. {
  1554. translucency = percent * 2;
  1555. }
  1556. else
  1557. {
  1558. translucency = (100 - percent) * 2;
  1559. }
  1560. translucency+=10;
  1561. size = MIN(2.f * translucency / 100.f, .90f);
  1562. pie_MatScale(size);
  1563. pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, 0, WZCOL_WHITE, WZCOL_BLACK, pie_TRANSLUCENT, (UBYTE)(translucency));
  1564. /* Pop the matrix */
  1565. pie_MatEnd();
  1566. }
  1567. /** Renders the standard smoke effect - it is now scaled in real-time as well */
  1568. static void renderSmokeEffect(const EFFECT *psEffect)
  1569. {
  1570. UDWORD transparency = 0;
  1571. const PIELIGHT brightness = WZCOL_WHITE;
  1572. const PIELIGHT specular = WZCOL_BLACK;
  1573. positionEffect(psEffect);
  1574. /* Bit in comments doesn't quite work yet? */
  1575. if(TEST_FACING(psEffect))
  1576. {
  1577. /* Always face the viewer! */
  1578. pie_MatRotY(-player.r.y);
  1579. pie_MatRotX(-player.r.x);
  1580. }
  1581. /* Small smoke - used for the droids */
  1582. // if(psEffect->type == SMOKE_TYPE_DRIFTING_SMALL || psEffect->type == SMOKE_TYPE_TRAIL)
  1583. if(TEST_SCALED(psEffect))
  1584. {
  1585. const unsigned int lifetime = graphicsTime - psEffect->birthTime;
  1586. unsigned int percent;
  1587. // Since psEffect->birthTime will be set to graphicsTime on
  1588. // creation, and graphicsTime only increments, birthTime should be
  1589. // smaller than or equal to graphicsTime. As a great man once said
  1590. // though (hehe, I just love exaggarating ;-) -- Giel):
  1591. // "Always assert your assumptions!". So here it goes:
  1592. assert(psEffect->birthTime <= graphicsTime);
  1593. ASSERT(psEffect->lifeSpan != 0, "Effect lifespan is zero (seconds); it really should be non-zero!");
  1594. // HACK: Work around a bug that bit me causing a "integer divide by zero" at this location -- Giel
  1595. if (psEffect->lifeSpan != 0)
  1596. percent = (lifetime * 100) / psEffect->lifeSpan;
  1597. else
  1598. percent = 100;
  1599. pie_M

Large files files are truncated, but you can click here to view the full file