deathWad.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845
  1. ////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2016 RWS Inc, All Rights Reserved
  4. //
  5. // This program is free software; you can redistribute it and/or modify
  6. // it under the terms of version 2 of the GNU General Public License as published by
  7. // the Free Software Foundation
  8. //
  9. // This program 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. //
  14. // You should have received a copy of the GNU General Public License along
  15. // with this program; if not, write to the Free Software Foundation, Inc.,
  16. // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. //
  18. // deathWad.cpp
  19. // Project: Nostril (aka Postal)
  20. //
  21. // This module implements the CDeathWad weapon class which is an unguided
  22. // projectile.
  23. //
  24. //
  25. // History:
  26. // 07/30/97 JMI Started this weapon object from the CRocket.
  27. //
  28. // 08/07/97 JMI Added additional parameter to CAnim3D::Get() call.
  29. //
  30. // 08/17/97 JMI Changed m_pthingParent to m_idParent.
  31. //
  32. ////////////////////////////////////////////////////////////////////////////////
  33. //
  34. // Wad vt wad-ded; wad-ding (1579) 1 a: to insert a wad into <~ a gun>
  35. // b: to hold in by a wad <~ a bullet in a gun> 2: to form into a wad or
  36. // wadding; esp : to roll or crush into a tight wad 3: to stuff or line with
  37. // some soft substance -- wad-der n
  38. //
  39. // This weapon is a wad of several ammunitions stuffed (or wadded) into a rocket
  40. // cylinder and a napalm canister. Fuel is used to propel the weapon, grenades
  41. // for extra (in addition to the rockets normal payload) explosive power, and
  42. // napalm for lasting fire(burn) power. The rocket cylinder stores the fuel and
  43. // provides the propulsion. The napalm canister stores the extra explosive/fire
  44. // powerload using some up with every collision. It is for these reasons this
  45. // weapon requires:
  46. // - exactly 1 rocket cylinder (including its original payload (solid fuel and
  47. // explosive power))
  48. // - exactly 1 napalm canister (including its original payload (let's call it
  49. // liquid fire) )
  50. // - at least 1 canister fluid fuel (e.g., from flame thrower) (more provides
  51. // greater distance).
  52. // - at least 1 grenade (more provides greater explosive power over longer
  53. // distances).
  54. //
  55. ////////////////////////////////////////////////////////////////////////////////
  56. #define DEATHWAD_CPP
  57. #include "RSPiX.h"
  58. #include <math.h>
  59. #include "deathWad.h"
  60. #include "dude.h"
  61. #include "explode.h"
  62. #include "fire.h"
  63. #include "SampleMaster.h"
  64. #include "fireball.h"
  65. ////////////////////////////////////////////////////////////////////////////////
  66. // Macros/types/etc.
  67. ////////////////////////////////////////////////////////////////////////////////
  68. #define SMALL_SHADOW_FILE "smallshadow.img"
  69. #define RES_BASE_NAME "3d/missile"
  70. // Define this if you want the empty missile casing (when there's no final
  71. // explosive power) to become a powerup flung through the air from the point
  72. // at which it runs out of fuel.
  73. //#define CAN_CHANGE_TO_POWERUP 1
  74. // Gets a random between -range / 2 and range / 2.
  75. #define RAND_SWAY(sway) ((GetRand() % sway) - sway / 2)
  76. ////////////////////////////////////////////////////////////////////////////////
  77. // Variables/data
  78. ////////////////////////////////////////////////////////////////////////////////
  79. // These are constant values!
  80. // Internal acceleration.
  81. const double CDeathWad::ms_dAccInternal = 350.0;
  82. // Maximum forward velocity
  83. const double CDeathWad::ms_dMaxVelFore = 350.0;
  84. // Maximum backward velocity
  85. const double CDeathWad::ms_dMaxVelBack = -350.0;
  86. // Units moved each iteration while traversing the weapon path.
  87. const double CDeathWad::ms_dTraversalRate = 3.0;
  88. // Distance between thrust feedbacks.
  89. const double CDeathWad::ms_dThrustDelta = 9.0;
  90. // Go off screen this far before blowing up
  91. const short CDeathWad::ms_sOffScreenDist = 200;
  92. // Time for smoke to stick around.
  93. const long CDeathWad::ms_lSmokeTimeToLive = 500;
  94. // Time for fireball to stick around.
  95. const long CDeathWad::ms_lFireBallTimeToLive = 500;
  96. // Amount to stagger final explosions.
  97. const short CDeathWad::ms_sFinalExplosionStagger = 5;
  98. // Radius of collision area (whether sphere or cylinder).
  99. const short CDeathWad::ms_sCollisionRadius = 30;
  100. // Velocity for kick from launch.
  101. const double CDeathWad::ms_dKickVelocity = 350.0;
  102. // Max a WAD can hold.
  103. const CStockPile CDeathWad::ms_stockpileMax =
  104. {
  105. 0, // m_sHitPoints
  106. 5, // m_sNumGrenades
  107. 0, // m_sNumFireBombs
  108. 1, // m_sNumMissiles
  109. 1, // m_sNumNapalms
  110. 0, // m_sNumBullets
  111. 0, // m_sNumShells
  112. 50, // m_sNumFuel
  113. 0, // m_sNumMines
  114. 0, // m_sNumHeatseekers
  115. 0, // m_sMachineGun
  116. 0, // m_sMissileLauncher
  117. 0, // m_sShotGun
  118. 0, // m_sSprayCannon
  119. 0, // m_sFlameThrower
  120. 0, // m_sNapalmLauncher
  121. 0, // m_sDeathWadLauncher
  122. 0, // m_sKevlarLayers
  123. 0, // m_sBackpack
  124. };
  125. // Let this auto-init to 0
  126. short CDeathWad::ms_sFileCount;
  127. ////////////////////////////////////////////////////////////////////////////////
  128. // Load object (should call base class version!)
  129. ////////////////////////////////////////////////////////////////////////////////
  130. short CDeathWad::Load( // Returns 0 if successfull, non-zero otherwise
  131. RFile* pFile, // In: File to load from
  132. bool bEditMode, // In: True for edit mode, false otherwise
  133. short sFileCount, // In: File count (unique per file, never 0)
  134. ULONG ulFileVersion) // In: Version of file format to load.
  135. {
  136. short sResult = CWeapon::Load(pFile, bEditMode, sFileCount, ulFileVersion);
  137. if (sResult == SUCCESS)
  138. {
  139. // Load common data just once per file (not with each object)
  140. if (ms_sFileCount != sFileCount)
  141. {
  142. ms_sFileCount = sFileCount;
  143. // Load static data
  144. switch (ulFileVersion)
  145. {
  146. default:
  147. case 1:
  148. break;
  149. }
  150. }
  151. // Load object data
  152. switch (ulFileVersion)
  153. {
  154. default:
  155. case 1:
  156. break;
  157. }
  158. // Make sure there were no file errors
  159. if (!pFile->Error())
  160. {
  161. // Get resources
  162. sResult = GetResources();
  163. }
  164. else
  165. {
  166. sResult = -1;
  167. TRACE("CDeathWad::Load(): Error reading from file!\n");
  168. }
  169. }
  170. return sResult;
  171. }
  172. ////////////////////////////////////////////////////////////////////////////////
  173. // Save object (should call base class version!)
  174. ////////////////////////////////////////////////////////////////////////////////
  175. short CDeathWad::Save( // Returns 0 if successfull, non-zero otherwise
  176. RFile* pFile, // In: File to save to
  177. short sFileCount) // In: File count (unique per file, never 0)
  178. {
  179. // In most cases, the base class Save() should be called. In this case it
  180. // isn't because the base class doesn't have a Save()!
  181. // Save common data just once per file (not with each object)
  182. if (ms_sFileCount != sFileCount)
  183. {
  184. ms_sFileCount = sFileCount;
  185. // Save static data
  186. }
  187. // Save object data
  188. return 0;
  189. }
  190. ////////////////////////////////////////////////////////////////////////////////
  191. // Update object
  192. ////////////////////////////////////////////////////////////////////////////////
  193. void CDeathWad::Update(void)
  194. {
  195. double dNewX;
  196. double dNewZ;
  197. ASSERT(m_dRot >= 0);
  198. ASSERT(m_dRot < 360);
  199. if (!m_sSuspend)
  200. {
  201. // Get new time
  202. long lThisTime = m_pRealm->m_time.GetGameTime();
  203. // Calculate elapsed time in seconds
  204. double dSeconds = (double)(lThisTime - m_lPrevTime) / 1000.0;
  205. ProcessMessages();
  206. // If we're to be deleted . . .
  207. // Note that this could be a case in the switch below, but, if
  208. // for whatever reason, that moves or something else is inserted
  209. // between here and the switch that might change the state, we
  210. // might not get deleted.
  211. if (m_eState == State_Deleted)
  212. {
  213. // We are to be deleted. Do it.
  214. delete this;
  215. // Must get out of here before we touch any of our invalidated
  216. // this.
  217. return;
  218. }
  219. // Check the current state
  220. switch (m_eState)
  221. {
  222. case CWeapon::State_Hide:
  223. case CWeapon::State_Idle:
  224. break;
  225. case CWeapon::State_Fire:
  226. Launch();
  227. m_eState = State_Chase;
  228. break;
  229. //-----------------------------------------------------------------------
  230. // Chase
  231. //-----------------------------------------------------------------------
  232. case CWeapon::State_Chase:
  233. {
  234. // Accelerate toward the target and check for proximity
  235. // and obstacles
  236. // Accelerate up to max velocity
  237. m_dHorizVel += ms_dAccInternal * dSeconds;
  238. // Limit to maximum velocity
  239. if (m_dHorizVel > ms_dMaxVelFore)
  240. m_dHorizVel = ms_dMaxVelFore;
  241. else if (m_dHorizVel < ms_dMaxVelBack)
  242. m_dHorizVel = ms_dMaxVelBack;
  243. // Adjust position based on velocity.
  244. dNewX = m_dX + COSQ[(short)m_dRot] * (m_dHorizVel * dSeconds);
  245. dNewZ = m_dZ - SINQ[(short)m_dRot] * (m_dHorizVel * dSeconds);
  246. // If the new position is a ways off screen
  247. if ( m_dZ > ms_sOffScreenDist + m_pRealm->GetRealmHeight()
  248. || m_dZ < -ms_sOffScreenDist
  249. || m_dX > ms_sOffScreenDist + m_pRealm->GetRealmWidth()
  250. || m_dX < -ms_sOffScreenDist)
  251. {
  252. // Blow Up
  253. m_eState = CWeapon::State_Explode;
  254. }
  255. else
  256. {
  257. // Traverse the path until we hit the newest position.
  258. while (TraversePath( // Returns true, when destination reached; false,
  259. // if terrain change.
  260. m_dX, // In: Starting position.
  261. m_dY, // In: Starting position.
  262. m_dZ, // In: Starting position.
  263. &m_bInsideTerrain, // In: true, if starting in terrain.
  264. // Out: true, if ending in terrain.
  265. dNewX, // In: Destination position.
  266. dNewZ, // In: Destination position.
  267. &m_dX, // Out: Position of inside terrain status change.
  268. &m_dZ) // Out: Position of inside terrain status change.
  269. == false)
  270. {
  271. // Explosion at this point.
  272. Explosion();
  273. }
  274. }
  275. // If we have any charge left . . .
  276. if (m_stockpile.m_sNumGrenades)
  277. {
  278. // If we hit someone . . .
  279. CSmash* pSmashed = NULL;
  280. if (m_pRealm->m_smashatorium.QuickCheck(
  281. &m_smash,
  282. m_u32CollideIncludeBits,
  283. m_u32CollideDontcareBits,
  284. m_u32CollideExcludeBits,
  285. &pSmashed) == true)
  286. {
  287. ASSERT(pSmashed->m_pThing);
  288. // Protect the launcher of the death wad . . .
  289. if (pSmashed->m_pThing->GetInstanceID() != m_u16ShooterID)
  290. {
  291. m_stockpile.m_sNumGrenades--;
  292. Explosion();
  293. }
  294. }
  295. }
  296. // If out of fuel . . .
  297. if (m_stockpile.m_sNumFuel <= 0)
  298. {
  299. // Blow Up
  300. m_eState = CWeapon::State_Explode;
  301. }
  302. // Update sound position.
  303. short sVolumeHalfLife = LaunchSndHalfLife;
  304. // If inside terrain . . .
  305. if (m_bInsideTerrain == true)
  306. {
  307. // Half half life.
  308. sVolumeHalfLife /= 4;
  309. }
  310. SetInstanceVolume(m_siThrust, DistanceToVolume(m_dX, m_dY, m_dZ, sVolumeHalfLife) );
  311. // If no rocket or napalm canister . . .
  312. if (m_stockpile.m_sNumMissiles < 0 || m_stockpile.m_sNumNapalms < 0)
  313. {
  314. // Blow Up.
  315. m_eState = CWeapon::State_Explode;
  316. }
  317. break;
  318. }
  319. //-----------------------------------------------------------------------
  320. // Explode
  321. //-----------------------------------------------------------------------
  322. case CWeapon::State_Explode:
  323. // Start explosion objects and then kill deathwad
  324. // object.
  325. #ifdef CAN_CHANGE_TO_POWERUP
  326. if (m_stockpile.m_sNumGrenades > 0)
  327. {
  328. #endif
  329. while (m_stockpile.m_sNumGrenades > 0 || m_stockpile.m_sNumMissiles > 0)
  330. {
  331. Explosion();
  332. // Stagger.
  333. m_dX += RAND_SWAY(ms_sFinalExplosionStagger);
  334. m_dZ += RAND_SWAY(ms_sFinalExplosionStagger);
  335. m_stockpile.m_sNumGrenades--;
  336. m_stockpile.m_sNumMissiles--;
  337. }
  338. #ifdef CAN_CHANGE_TO_POWERUP
  339. }
  340. else
  341. {
  342. // Otherwise, persist as powerup.
  343. CPowerUp* ppowerup = NULL;
  344. if (CThing::Construct(CPowerUpID, m_pRealm, (CThing**)&ppowerup) == 0)
  345. {
  346. // Copy whatever's left.
  347. ppowerup->m_stockpile.Copy(&m_stockpile);
  348. // Our's should now be empty for safety. Matter cannot be created or destroyed
  349. // and all.
  350. m_stockpile.Zero();
  351. // Place powerup at our current location.
  352. ppowerup->Setup(m_dX, m_dY, m_dZ);
  353. // Blow it up.
  354. GameMessage msg;
  355. msg.msg_Explosion.eType = typeExplosion;
  356. msg.msg_Explosion.sPriority = 0;
  357. msg.msg_Explosion.sDamage = 0;
  358. msg.msg_Explosion.sX = m_dX;
  359. msg.msg_Explosion.sY = m_dY;
  360. msg.msg_Explosion.sZ = m_dZ;
  361. msg.msg_Explosion.sVelocity = 130;
  362. msg.msg_Explosion.u16ShooterID = m_u16ShooterID;
  363. SendThingMessage(&msg, msg.msg_Explosion.sPriority, ppowerup);
  364. }
  365. else
  366. {
  367. TRACE("Update(): Failed to allocate new CPowerUp.\n");
  368. }
  369. }
  370. #endif
  371. delete this;
  372. return;
  373. break;
  374. }
  375. // Update sphere.
  376. m_smash.m_sphere.sphere.X = m_dX;
  377. m_smash.m_sphere.sphere.Y = m_dY;
  378. m_smash.m_sphere.sphere.Z = m_dZ;
  379. m_smash.m_sphere.sphere.lRadius = m_sprite.m_sRadius;
  380. // Update the smash.
  381. m_pRealm->m_smashatorium.Update(&m_smash);
  382. // Save time for next time
  383. m_lPrevTime = lThisTime;
  384. }
  385. }
  386. ////////////////////////////////////////////////////////////////////////////////
  387. // Render object
  388. ////////////////////////////////////////////////////////////////////////////////
  389. void CDeathWad::Render(void)
  390. {
  391. long lThisTime = m_pRealm->m_time.GetGameTime();
  392. m_sprite.m_pmesh = (RMesh*) m_anim.m_pmeshes->GetAtTime(lThisTime);
  393. m_sprite.m_psop = (RSop*) m_anim.m_psops->GetAtTime(lThisTime);
  394. m_sprite.m_ptex = (RTexture*) m_anim.m_ptextures->GetAtTime(lThisTime);
  395. m_sprite.m_psphere = (RP3d*) m_anim.m_pbounds->GetAtTime(lThisTime);
  396. // Reset rotation so it is not cumulative
  397. m_trans.Make1();
  398. // Set its pointing direction
  399. m_trans.Ry(rspMod360(m_dRot));
  400. // Eventually this should be channel driven also
  401. m_sprite.m_sRadius = ms_sCollisionRadius;
  402. if (m_eState == State_Hide || m_bInsideTerrain == true)
  403. {
  404. m_sprite.m_sInFlags = CSprite::InHidden;
  405. }
  406. else
  407. {
  408. m_sprite.m_sInFlags = 0;
  409. }
  410. // If we're not a child of someone else...
  411. if (m_idParent == CIdBank::IdNil)
  412. {
  413. // Map from 3d to 2d coords
  414. Map3Dto2D((short) m_dX, (short) m_dY, (short) m_dZ, &m_sprite.m_sX2, &m_sprite.m_sY2);
  415. // Priority is based on Z.
  416. m_sprite.m_sPriority = m_dZ;
  417. // Layer should be based on info we get from the attribute map
  418. m_sprite.m_sLayer = CRealm::GetLayerViaAttrib(m_pRealm->GetLayer((short) m_dX, (short) m_dZ));
  419. m_sprite.m_ptrans = &m_trans;
  420. // Update sprite in scene
  421. m_pRealm->m_scene.UpdateSprite(&m_sprite);
  422. // Render the 2D shadow sprite
  423. CWeapon::Render();
  424. }
  425. else
  426. {
  427. // m_idParent is setting our transform relative to its position
  428. // and we are drawn by the scene with the parent.
  429. }
  430. }
  431. ////////////////////////////////////////////////////////////////////////////////
  432. // Setup
  433. ////////////////////////////////////////////////////////////////////////////////
  434. short CDeathWad::Setup( // Returns 0 if successfull, non-zero otherwise
  435. short sX, // In: New x coord
  436. short sY, // In: New y coord
  437. short sZ) // In: New z coord
  438. {
  439. short sResult = 0;
  440. // Use specified position
  441. m_dX = (double)sX;
  442. m_dY = (double)sY;
  443. m_dZ = (double)sZ;
  444. m_dHorizVel = 0.0;
  445. // Load resources
  446. sResult = GetResources();
  447. // Enable the 2D shadow sprite
  448. PrepareShadow();
  449. // Set the collision bits
  450. m_u32CollideIncludeBits = CSmash::Character | CSmash::Misc | CSmash::Barrel;
  451. m_u32CollideDontcareBits = CSmash::Good | CSmash::Bad;
  452. m_u32CollideExcludeBits = 0;
  453. m_smash.m_bits = CSmash::Projectile;
  454. m_smash.m_pThing = this;
  455. return sResult;
  456. }
  457. ////////////////////////////////////////////////////////////////////////////////
  458. // Get all required resources
  459. ////////////////////////////////////////////////////////////////////////////////
  460. short CDeathWad::GetResources(void) // Returns 0 if successfull, non-zero otherwise
  461. {
  462. short sResult = 0;
  463. sResult = m_anim.Get(RES_BASE_NAME, NULL, NULL, NULL, 0);
  464. if (sResult == 0)
  465. {
  466. sResult = rspGetResource(&g_resmgrGame, m_pRealm->Make2dResPath(SMALL_SHADOW_FILE), &(m_spriteShadow.m_pImage), RFile::LittleEndian);
  467. if (sResult == 0)
  468. {
  469. // add more gets
  470. }
  471. else
  472. {
  473. TRACE("CGrenade::GetResources - Failed to open 2D shadow image\n");
  474. }
  475. }
  476. else
  477. {
  478. TRACE("CDeathWad::GetResources - Failed to open 3D animation for deathwad projectile\n");
  479. }
  480. return sResult;
  481. }
  482. ////////////////////////////////////////////////////////////////////////////////
  483. // Free all resources
  484. ////////////////////////////////////////////////////////////////////////////////
  485. short CDeathWad::FreeResources(void) // Returns 0 if successfull, non-zero otherwise
  486. {
  487. m_anim.Release();
  488. return 0;
  489. }
  490. ////////////////////////////////////////////////////////////////////////////////
  491. // Preload - basically trick the resource manager into caching resources
  492. // for this object so there won't be a delay the first time it is
  493. // created.
  494. ////////////////////////////////////////////////////////////////////////////////
  495. short CDeathWad::Preload(
  496. CRealm* prealm) // In: Calling realm.
  497. {
  498. CAnim3D anim;
  499. RImage* pimage;
  500. short sResult = anim.Get(RES_BASE_NAME, NULL, NULL, NULL, 0);
  501. if (sResult == 0)
  502. {
  503. anim.Release();
  504. }
  505. if (rspGetResource(&g_resmgrGame, prealm->Make2dResPath(SMALL_SHADOW_FILE), &pimage, RFile::LittleEndian) == 0)
  506. {
  507. rspReleaseResource(&g_resmgrGame, &pimage);
  508. }
  509. else
  510. {
  511. sResult = -1;
  512. }
  513. CacheSample(g_smidDeathWadLaunch);
  514. CacheSample(g_smidDeathWadThrust);
  515. CacheSample(g_smidDeathWadExplode);
  516. return sResult;
  517. }
  518. ////////////////////////////////////////////////////////////////////////////////
  519. // ProcessMessages
  520. ////////////////////////////////////////////////////////////////////////////////
  521. void CDeathWad::ProcessMessages(void)
  522. {
  523. GameMessage msg;
  524. if (m_MessageQueue.DeQ(&msg) == true)
  525. {
  526. switch(msg.msg_Generic.eType)
  527. {
  528. case typeObjectDelete:
  529. m_MessageQueue.Empty();
  530. m_eState = State_Deleted;
  531. // Don't delete this here. Instead make sure the state is
  532. // set so Update() knows to delete us and return immediately
  533. // (before something else changes our state).
  534. return;
  535. break;
  536. }
  537. }
  538. // Dump the rest of the messages
  539. m_MessageQueue.Empty();
  540. return;
  541. }
  542. ////////////////////////////////////////////////////////////////////////////////
  543. // Traverse the path until the inside terrain status changes or
  544. // the destination is reached.
  545. ////////////////////////////////////////////////////////////////////////////////
  546. bool CDeathWad::TraversePath( // Returns true, when destination reached; false,
  547. // if terrain change.
  548. short sSrcX, // In: Starting position.
  549. short sSrcY, // In: Starting position.
  550. short sSrcZ, // In: Starting position.
  551. bool* pbInTerrain, // In: true, if starting in terrain.
  552. // Out: true, if ending in terrain.
  553. short sDstX, // In: Destination position.
  554. short sDstZ, // In: Destination position.
  555. double* pdCurX, // Out: Position of inside terrain status change.
  556. double* pdCurZ) // Out: Position of inside terrain status change.
  557. {
  558. bool bMadeDestination = true; // Assume we make it.
  559. ASSERT(pbInTerrain);
  560. ASSERT(pdCurX);
  561. ASSERT(pdCurZ);
  562. ASSERT(m_dRot >= 0);
  563. ASSERT(m_dRot < 360);
  564. // Determine distance on X/Z plane to destination.
  565. double dDistance = rspSqrt(ABS2(float(sSrcX - sDstX), float(sSrcZ - sDstZ) ) );
  566. // Set starting position.
  567. double dX = sSrcX;
  568. double dZ = sSrcZ;
  569. // Determine iteration rate on X and Z.
  570. double dRateX = COSQ[(short)m_dRot] * ms_dTraversalRate;
  571. double dRateZ = -SINQ[(short)m_dRot] * ms_dTraversalRate;
  572. // Store original status.
  573. bool bInitiallyInTerrain = *pbInTerrain;
  574. // Loop until change in status or we hit destination.
  575. while (
  576. dDistance > 0 &&
  577. *pbInTerrain == bInitiallyInTerrain)
  578. {
  579. if (m_pRealm->GetHeight(dX, dZ) > sSrcY)
  580. *pbInTerrain = true;
  581. else
  582. *pbInTerrain = false;
  583. // See if it's time to create a thrust . . .
  584. m_dUnthrustedDistance += ms_dTraversalRate;
  585. if (m_dUnthrustedDistance >= ms_dThrustDelta)
  586. {
  587. // Thrustage.
  588. Thrust();
  589. // Reset for next.
  590. m_dUnthrustedDistance = 0.0;
  591. }
  592. dX += dRateX;
  593. dZ += dRateZ;
  594. dDistance -= ms_dTraversalRate;
  595. }
  596. // If we did not make the destination . . .
  597. if (dDistance > 0)
  598. {
  599. bMadeDestination = false;
  600. }
  601. // Store new position.
  602. *pdCurX = dX;
  603. *pdCurZ = dZ;
  604. return bMadeDestination;
  605. }
  606. ////////////////////////////////////////////////////////////////////////////////
  607. // Generate an explosion at the current position.
  608. ////////////////////////////////////////////////////////////////////////////////
  609. void CDeathWad::Explosion(void)
  610. {
  611. // Start an explosion object and some smoke (doesn't an explosion object
  612. // automatically make smoke??).
  613. CExplode* pExplosion;
  614. if (CThing::Construct(CThing::CExplodeID, m_pRealm, (CThing**) &pExplosion) == 0)
  615. {
  616. // Don't blow us up.
  617. pExplosion->m_u16ExceptID = m_u16ShooterID;
  618. pExplosion->Setup(m_dX, MAX(m_dY-30, 0.0), m_dZ, m_u16ShooterID);
  619. PlaySample( // Returns nothing.
  620. // Does not fail.
  621. g_smidDeathWadExplode, // In: Identifier of sample you want played.
  622. SampleMaster::Destruction, // In: Sound Volume Category for user adjustment
  623. DistanceToVolume(m_dX, m_dY, m_dZ, ExplosionSndHalfLife) ); // In: Initial Sound Volume (0 - 255)
  624. }
  625. short a;
  626. CFire* pSmoke;
  627. for (a = 0; a < 8; a++)
  628. {
  629. if (CThing::Construct(CThing::CFireID, m_pRealm, (CThing**) &pSmoke) == 0)
  630. {
  631. pSmoke->Setup(m_dX - 4 + GetRandom() % 9, m_dY-20, m_dZ - 4 + GetRandom() % 9, ms_lSmokeTimeToLive, true, CFire::Smoke);
  632. pSmoke->m_u16ShooterID = m_u16ShooterID;
  633. }
  634. }
  635. if (m_stockpile.m_sNumFuel > 0 || m_stockpile.m_sNumNapalms > 1)
  636. {
  637. // Also, create a fire.
  638. }
  639. }
  640. ////////////////////////////////////////////////////////////////////////////////
  641. // Generate some thrust at the current position.
  642. ////////////////////////////////////////////////////////////////////////////////
  643. void CDeathWad::Thrust(void)
  644. {
  645. m_stockpile.m_sNumFuel--;
  646. if (m_bInsideTerrain == false)
  647. {
  648. CFire* pSmoke = NULL;
  649. if (CThing::Construct(CThing::CFireID, m_pRealm, (CThing**) &pSmoke) == 0)
  650. {
  651. // This needs to be fixed by calculating the position of the back end of
  652. // the deathwad in 3D based on the rotation.
  653. pSmoke->Setup(m_dX, m_dY, m_dZ, ms_lSmokeTimeToLive, true, CFire::SmallSmoke);
  654. pSmoke->m_u16ShooterID = m_u16ShooterID;
  655. }
  656. // Also, create a fire (moving at the wad's velocity?).
  657. CFireball* pfireball = NULL;
  658. if (CThing::Construct(CFireballID, m_pRealm, (CThing**) &pfireball) == 0)
  659. {
  660. pfireball->Setup(m_dX, m_dY, m_dZ, m_dRot, ms_lFireBallTimeToLive, m_u16ShooterID);
  661. pfireball->m_dHorizVel = m_dHorizVel / 4.0;
  662. pfireball->m_eState = State_Fire;
  663. }
  664. }
  665. }
  666. ////////////////////////////////////////////////////////////////////////////////
  667. // Generate the launch kick/debris.
  668. ////////////////////////////////////////////////////////////////////////////////
  669. void CDeathWad::Launch(void)
  670. {
  671. // The launch sound.
  672. PlaySample( // Returns nothing.
  673. // Does not fail.
  674. g_smidDeathWadLaunch, // In: Identifier of sample you want played.
  675. SampleMaster::Weapon, // In: Sound Volume Category for user adjustment
  676. DistanceToVolume(m_dX, m_dY, m_dZ, LaunchSndHalfLife) ); // In: Initial Sound Volume (0 - 255)
  677. // The looping thrust sound.
  678. PlaySample( // Returns nothing.
  679. // Does not fail.
  680. g_smidDeathWadThrust, // In: Identifier of sample you want played.
  681. SampleMaster::Weapon, // In: Sound Volume Category for user adjustment
  682. DistanceToVolume(m_dX, m_dY, m_dZ, LaunchSndHalfLife), // In: Initial Sound Volume (0 - 255)
  683. &m_siThrust, // Out: Handle for adjusting sound volume
  684. NULL, // Out: Sample duration in ms, if not NULL.
  685. 100, // In: Where to loop back to in milliseconds.
  686. // -1 indicates no looping (unless m_sLoop is
  687. // explicitly set).
  688. 500, // In: Where to loop back from in milliseconds.
  689. // In: If less than 1, the end + lLoopEndTime is used.
  690. false); // In: Call ReleaseAndPurge rather than Release after playing
  691. Explosion();
  692. CThing* pthing = NULL;
  693. // Get the launcher . . .
  694. if (m_pRealm->m_idbank.GetThingByID(&pthing, m_u16ShooterID) == 0)
  695. {
  696. // If it's a dude . . .
  697. if (pthing->GetClassID() == CDudeID)
  698. {
  699. CDude* pdude = (CDude*)pthing;
  700. // Add force vector for kick. See ya.
  701. pdude->AddForceVector(ms_dKickVelocity, m_dRot - 180);
  702. }
  703. }
  704. }
  705. ////////////////////////////////////////////////////////////////////////////////
  706. // Feed the WAD prior to moving its state to State_Fire.
  707. ////////////////////////////////////////////////////////////////////////////////
  708. void CDeathWad::FeedWad(
  709. CStockPile* pstockpile) // In: Src for WAD's arsenal.
  710. {
  711. // Take needed ammo.
  712. m_stockpile.m_sNumMissiles = pstockpile->m_sNumMissiles;
  713. m_stockpile.m_sNumNapalms = pstockpile->m_sNumNapalms;
  714. m_stockpile.m_sNumFuel = pstockpile->m_sNumFuel;
  715. m_stockpile.m_sNumGrenades = pstockpile->m_sNumGrenades;
  716. // Truncate to max we can hold.
  717. m_stockpile.Intersect(&ms_stockpileMax);
  718. // Subtract from provider.
  719. pstockpile->Sub(&m_stockpile);
  720. }
  721. ////////////////////////////////////////////////////////////////////////////////
  722. // EOF
  723. ////////////////////////////////////////////////////////////////////////////////