PageRenderTime 60ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/src/effects.cpp

https://bitbucket.org/dak180/warzone2100
C++ | 2603 lines | 1925 code | 365 blank | 313 comment | 293 complexity | 23761e801e4bd3c91c8cd6e70b9526ba MD5 | raw file
Possible License(s): LGPL-2.0, BSD-3-Clause, LGPL-2.1, GPL-2.0, CC-BY-SA-4.0

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

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