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

/src/effects.cpp

http://github.com/Warzone2100/warzone2100
C++ | 2308 lines | 1794 code | 276 blank | 238 comment | 295 complexity | 75cf406cdbeb661de0f6ec80382bbc10 MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0, CC-BY-SA-4.0, LGPL-2.0, BSD-3-Clause

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

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