rocket.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  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. // rocket.cpp
  19. // Project: Postal
  20. //
  21. // This module implements the CRocket weapon class which is an unguided
  22. // rocket missile.
  23. //
  24. //
  25. // History:
  26. // 01/17/97 BRH Started this weapon object.
  27. //
  28. // 02/04/97 JMI Changed LoadDib() call to Load() (which now supports
  29. // loading of DIBs).
  30. //
  31. // 02/06/97 BRH Moved Explosion position down some.
  32. //
  33. // 02/11/97 BRH Changed the rocket to use the game res manager rather
  34. // than loading its assets from a file directly.
  35. //
  36. // 02/16/97 BRH Rocket now sends a message to the CDude when it hits him.
  37. //
  38. // 02/18/97 JMI Now uses typeExplosion instead of msg_Explosion.
  39. //
  40. // 02/19/97 BRH Added ProcessMessage routine to look for ObjectDelete
  41. // messages.
  42. //
  43. // 02/23/97 BRH Set the transform for the rocket so it faces the right
  44. // direction. Also changed the coordinate system to x,-z
  45. //
  46. // 02/23/97 BRH Added Preload() function to cache resources for this
  47. // object before play begins.
  48. //
  49. // 02/23/97 BRH Added State_Hide so that the rocket can be created but
  50. // is not shown.
  51. //
  52. // 02/24/97 JMI No longer sets the m_type member of the m_sprite b/c it
  53. // is set by m_sprite's constructor.
  54. //
  55. // 03/03/97 BRH Derived this from the CWeapon base class
  56. //
  57. // 03/06/97 JMI Upgraded to current rspMod360 usage.
  58. //
  59. // 03/13/97 JMI Load now takes a version number.
  60. //
  61. // 03/19/97 BRH Changed the ProcessMessages function to return void so
  62. // it matches the new virtual function in the CWeapon
  63. // base class.
  64. //
  65. // 04/10/97 BRH Updated this to work with the new multi layer attribute
  66. // maps.
  67. //
  68. // 04/15/97 BRH Added CSmash::Item to the collision items so that rockets
  69. // can blow up barrels and other items.
  70. //
  71. // 04/15/97 BRH Took out old State_Find code that was used to seek the
  72. // CDude. The rocket is now aimed by the shooter and
  73. // this old code was using the CDude list which will soon
  74. // be changed and since it is no longer needed, it was
  75. // best to take it out.
  76. //
  77. // 04/23/97 JMI Now collides with Characters, Miscs, and Barrels.
  78. // Also, sets its m_smash's bits to Projectile instead of
  79. // Character.
  80. // Also, changed layer priority to simply use Z position.
  81. //
  82. // 04/24/97 JMI Now when it hits something, it does not update its
  83. // position (i.e., it keeps its old valid position).
  84. //
  85. // 04/24/97 BRH Added puffs of smoke in addition to the explosion.
  86. //
  87. // 04/29/97 BRH Added an off screen distance at which the rocket will self
  88. // destruct.
  89. //
  90. // 05/26/97 BRH Changed the check for wall collisions to ignore the
  91. // NOT_WALKABLE attribute which caused it to blow up in
  92. // the wrong places. Now only the height is checked.
  93. //
  94. // 05/29/97 JMI Removed ASSERT on m_pRealm->m_pAttribMap which no longer
  95. // exists.
  96. //
  97. // 06/10/97 BRH Increased the rocket arming time from 200ms to 500ms to
  98. // avoid killing yourself when you are moving & shooting.
  99. //
  100. // 06/11/97 BRH Passes shooter ID to the explosion that is created.
  101. //
  102. // 06/12/97 BRH Added shooter ID to the call to Setup for the explosion.
  103. //
  104. // 06/18/97 BRH Changed over to using GetRandom()
  105. //
  106. // 06/25/97 BRH Added use of base class 2D shadow on the ground, but loaded
  107. // a smaller shadow resource.
  108. //
  109. // 06/30/97 BRH Added cache samples to the Preload function.
  110. //
  111. // 06/30/97 JMI Now uses CRealm's new GetRealmWidth() and *Height()
  112. // for dimensions of realm's X/Z plane.
  113. //
  114. // 06/30/97 JMI Now uses IsPathClear() to determine if the path to the new
  115. // position is clear.
  116. //
  117. // 07/01/97 BRH Added smoke trails.
  118. //
  119. // 07/01/97 JMI In Update(), when the weapon explodes, it didn't set dNewX
  120. // and dNewZ but still created smoke at this unitialized
  121. // position. Fixed.
  122. //
  123. // 07/07/97 JMI In ProcessMessages(), it was deleting this. Then, once
  124. // ProcessMessages() returned to Update(), it would check
  125. // m_eState to see if it had been deleted and then return.
  126. // The problem is that once deleted you cannot access m_eState.
  127. // Changed it so ProcessMessages() does not delete this but
  128. // merely sets the state to delete so that Update() can do it.
  129. //
  130. // 07/08/97 BRH Adjusted the position of the smoke, and cut down the trail
  131. // length.
  132. //
  133. // 07/09/97 JMI Now uses m_pRealm->Make2dResPath() to get the fullpath
  134. // for 2D image components.
  135. //
  136. // 07/09/97 JMI Changed Preload() to take a pointer to the calling realm
  137. // as a parameter.
  138. //
  139. // 07/16/97 BRH Retuned, or untuned the hotspot for the smoke trails.
  140. // Now that the hotspot for the smoke is correct, the
  141. // smoke does not need to be adjusted here.
  142. //
  143. // 07/18/97 JMI Added m_siThrust to track our thrust play instance so we
  144. // can loop it and then terminate the looping when we explode.
  145. // The sound tapers way too much to be loopable now.
  146. // Hopefully, we can get a better one.
  147. //
  148. // 07/18/97 JMI Got rid of bogus immitation PlaySample functions.
  149. // Now there is one PlaySample() function. Also, you now
  150. // MUST specify a category and you don't have to specify a
  151. // SoundInstance ptr to specify a volume.
  152. //
  153. // 08/08/97 BRH Changed the arming so that the missle won't arm until
  154. // it stops colliding with the shooter's smash. Also, added
  155. // a special case so that missiles shot by Sentry guns won't
  156. // blow up other Sentry guns.
  157. //
  158. // 08/12/97 BRH Changed collision bits to exclude any object that is
  159. // ducking (which should only be the main dude when he
  160. // is ducking down).
  161. //
  162. // 08/15/97 BRH Made the smash radius larger.
  163. //
  164. // 08/17/97 JMI Changed m_pthingParent to m_idParent.
  165. //
  166. // 08/24/97 BRH Now when the rocket goes into the expoode state, it sets
  167. // its position back to the previous position so that the
  168. // explosion won't always be created behind the thing it hit
  169. // and alwyas blow it forward which looked kind of weird.
  170. //
  171. // 08/26/97 BRH Fixed bug with brackets where rocket always went back to
  172. // its previous position when fired by a sentry gun.
  173. //
  174. // 08/27/97 JMI No longer sets the smash radius to m_sCurRadius during
  175. // Render().
  176. //
  177. ////////////////////////////////////////////////////////////////////////////////
  178. #define ROCKET_CPP
  179. #include "RSPiX.h"
  180. #include <math.h>
  181. #include "rocket.h"
  182. #include "dude.h"
  183. #include "explode.h"
  184. #include "fire.h"
  185. #include "SampleMaster.h"
  186. ////////////////////////////////////////////////////////////////////////////////
  187. // Macros/types/etc.
  188. ////////////////////////////////////////////////////////////////////////////////
  189. #define SMALL_SHADOW_FILE "smallshadow.img"
  190. ////////////////////////////////////////////////////////////////////////////////
  191. // Variables/data
  192. ////////////////////////////////////////////////////////////////////////////////
  193. // These are default values -- actually values are set using the editor!
  194. double CRocket::ms_dAccUser = 250.0; // Acceleration due to user
  195. double CRocket::ms_dMaxVelFore = 250.0; // Maximum forward velocity
  196. double CRocket::ms_dMaxVelBack = -250.0; // Maximum backward velocity
  197. double CRocket::ms_dCloseDistance = 30.0; // Close enough to hit CDude
  198. long CRocket::ms_lArmingTime = 500; // Time before weapon arms.
  199. short CRocket::ms_sOffScreenDist = 200; // Go off screen this far before blowing up
  200. long CRocket::ms_lSmokeTrailInterval = 10; // Time to emit smoke trail.
  201. long CRocket::ms_lSmokeTimeToLive = 1000; // Time for smoke to stick around.
  202. // Let this auto-init to 0
  203. short CRocket::ms_sFileCount;
  204. /// Rocket Animation Files
  205. static char* ms_apszResNames[] =
  206. {
  207. "3d/missile.sop",
  208. "3d/missile.mesh",
  209. "3d/missile.tex",
  210. "3d/missile.hot",
  211. "3d/missile.bounds",
  212. "3d/missile.floor",
  213. NULL,
  214. NULL
  215. };
  216. ////////////////////////////////////////////////////////////////////////////////
  217. // Load object (should call base class version!)
  218. ////////////////////////////////////////////////////////////////////////////////
  219. short CRocket::Load( // Returns 0 if successfull, non-zero otherwise
  220. RFile* pFile, // In: File to load from
  221. bool bEditMode, // In: True for edit mode, false otherwise
  222. short sFileCount, // In: File count (unique per file, never 0)
  223. ULONG ulFileVersion) // In: Version of file format to load.
  224. {
  225. short sResult = CWeapon::Load(pFile, bEditMode, sFileCount, ulFileVersion);
  226. if (sResult == SUCCESS)
  227. {
  228. // Load common data just once per file (not with each object)
  229. if (ms_sFileCount != sFileCount)
  230. {
  231. ms_sFileCount = sFileCount;
  232. // Load static data
  233. switch (ulFileVersion)
  234. {
  235. default:
  236. case 1:
  237. pFile->Read(&ms_dAccUser);
  238. pFile->Read(&ms_dMaxVelFore);
  239. pFile->Read(&ms_dMaxVelBack);
  240. pFile->Read(&ms_dCloseDistance);
  241. break;
  242. }
  243. }
  244. // Load object data
  245. switch (ulFileVersion)
  246. {
  247. default:
  248. case 1:
  249. break;
  250. }
  251. // Make sure there were no file errors
  252. if (!pFile->Error())
  253. {
  254. // Get resources
  255. sResult = GetResources();
  256. }
  257. else
  258. {
  259. sResult = -1;
  260. TRACE("CRocket::Load(): Error reading from file!\n");
  261. }
  262. }
  263. return sResult;
  264. }
  265. ////////////////////////////////////////////////////////////////////////////////
  266. // Save object (should call base class version!)
  267. ////////////////////////////////////////////////////////////////////////////////
  268. short CRocket::Save( // Returns 0 if successfull, non-zero otherwise
  269. RFile* pFile, // In: File to save to
  270. short sFileCount) // In: File count (unique per file, never 0)
  271. {
  272. // In most cases, the base class Save() should be called. In this case it
  273. // isn't because the base class doesn't have a Save()!
  274. // Save common data just once per file (not with each object)
  275. if (ms_sFileCount != sFileCount)
  276. {
  277. ms_sFileCount = sFileCount;
  278. // Save static data
  279. pFile->Write(&ms_dAccUser);
  280. pFile->Write(&ms_dMaxVelFore);
  281. pFile->Write(&ms_dMaxVelBack);
  282. pFile->Write(&ms_dCloseDistance);
  283. }
  284. // Save object data
  285. return 0;
  286. }
  287. ////////////////////////////////////////////////////////////////////////////////
  288. // Update object
  289. ////////////////////////////////////////////////////////////////////////////////
  290. void CRocket::Update(void)
  291. {
  292. USHORT usAttrib;
  293. short sHeight;
  294. double dNewX;
  295. double dNewZ;
  296. double dPrevX = 0; // compiler warning "not initialized before being used"
  297. double dPrevZ = 0; // compiler warning "not initialized before being used"
  298. if (!m_sSuspend)
  299. {
  300. // Get new time
  301. long lThisTime = m_pRealm->m_time.GetGameTime();
  302. // Calculate elapsed time in seconds
  303. double dSeconds = (double)(lThisTime - m_lPrevTime) / 1000.0;
  304. ProcessMessages();
  305. // If we're to be deleted . . .
  306. // Note that this could be a case in the switch below, but, if
  307. // for whatever reason, that moves or something else is inserted
  308. // between here and the switch that might change the state, we
  309. // might not get deleted.
  310. if (m_eState == State_Deleted)
  311. {
  312. // We are to be deleted. Do it.
  313. delete this;
  314. // Must get out of here before we touch any of our invalidated
  315. // this.
  316. return;
  317. }
  318. // Check the current state
  319. switch (m_eState)
  320. {
  321. case CWeapon::State_Hide:
  322. case CWeapon::State_Idle:
  323. dPrevX = m_dX;
  324. dPrevZ = m_dZ;
  325. break;
  326. case CWeapon::State_Fire:
  327. PlaySample( // Returns nothing.
  328. // Does not fail.
  329. g_smidRocketFire, // In: Identifier of sample you want played.
  330. SampleMaster::Weapon, // In: Sound Volume Category for user adjustment
  331. DistanceToVolume(m_dX, m_dY, m_dZ, LaunchSndHalfLife), // In: Initial Sound Volume (0 - 255)
  332. &m_siThrust, // Out: Handle for adjusting sound volume
  333. NULL, // Out: Sample duration in ms, if not NULL.
  334. 2841, // In: Where to loop back to in milliseconds.
  335. // -1 indicates no looping (unless m_sLoop is
  336. // explicitly set).
  337. 3090, // In: Where to loop back from in milliseconds.
  338. // In: If less than 1, the end + lLoopEndTime is used.
  339. false); // In: Call ReleaseAndPurge rather than Release after playing
  340. // Old Call: PlaySample(g_smidRocketFire);
  341. m_lTimer = lThisTime + ms_lArmingTime;
  342. m_eState = State_Chase;
  343. break;
  344. //-----------------------------------------------------------------------
  345. // Chase
  346. //-----------------------------------------------------------------------
  347. case CWeapon::State_Chase:
  348. // Accelerate toward the target and check for proximity
  349. // and obstacles
  350. // Accelerate doofus up to max velocity
  351. m_dHorizVel += ms_dAccUser * dSeconds;
  352. // Limit to maximum velocity
  353. if (m_dHorizVel > ms_dMaxVelFore)
  354. m_dHorizVel = ms_dMaxVelFore;
  355. else if (m_dHorizVel < ms_dMaxVelBack)
  356. m_dHorizVel = ms_dMaxVelBack;
  357. // Adjust position based on velocity (this will clearly be optimized later on!)
  358. dNewX = m_dX + COSQ[(short)m_dRot] * (m_dHorizVel * dSeconds);
  359. dNewZ = m_dZ - SINQ[(short)m_dRot] * (m_dHorizVel * dSeconds);
  360. // Check for obstacles
  361. sHeight = m_pRealm->GetHeight((short) dNewX, (short) dNewZ);
  362. usAttrib = m_pRealm->GetFloorAttribute((short) dNewX, (short) dNewZ);
  363. // If the new position's height is too high, the new position is a ways
  364. // off screen, or the path to the new position is not clear of terrain . . .
  365. if (sHeight > m_dY ||
  366. m_dZ > ms_sOffScreenDist + m_pRealm->GetRealmHeight() ||
  367. m_dZ < -ms_sOffScreenDist ||
  368. m_dX > ms_sOffScreenDist + m_pRealm->GetRealmWidth() ||
  369. m_dX < -ms_sOffScreenDist ||
  370. !m_pRealm->IsPathClear( // Returns true, if the entire path is clear.
  371. // Returns false, if only a portion of the path is clear.
  372. // (see *psX, *psY, *psZ).
  373. (short) m_dX, // In: Starting X.
  374. (short) m_dY, // In: Starting Y.
  375. (short) m_dZ, // In: Starting Z.
  376. 3.0, // In: Rate at which to scan ('crawl') path in pixels per
  377. // iteration.
  378. // NOTE: Values less than 1.0 are inefficient.
  379. // NOTE: We scan terrain using GetHeight()
  380. // at only one pixel.
  381. // NOTE: We could change this to a speed in pixels per second
  382. // where we'd assume a certain frame rate.
  383. (short) dNewX, // In: Destination X.
  384. (short) dNewZ, // In: Destination Z.
  385. 0, // In: Max traverser can step up.
  386. NULL, // Out: If not NULL, last clear point on path.
  387. NULL, // Out: If not NULL, last clear point on path.
  388. NULL, // Out: If not NULL, last clear point on path.
  389. false) ) // In: If true, will consider the edge of the realm a path
  390. // inhibitor. If false, reaching the edge of the realm
  391. // indicates a clear path.
  392. {
  393. // Blow Up
  394. m_eState = CWeapon::State_Explode;
  395. // Note that these need to be set even if we explode; otherwise, they
  396. // never get initialized and totally hosened values are sent to
  397. // pSmoke-Setup() which makes Alpha unhappy.
  398. dPrevX = m_dX;
  399. dPrevZ = m_dZ;
  400. }
  401. else
  402. {
  403. dPrevX = m_dX;
  404. dPrevZ = m_dZ;
  405. m_dX = dNewX;
  406. m_dZ = dNewZ;
  407. }
  408. // Check for collisions with other characters if
  409. // the weapon is armed, else see if it is time to arm
  410. // the weapon yet.
  411. if (m_bArmed)
  412. {
  413. CSmash* pSmashed = NULL;
  414. m_pRealm->m_smashatorium.QuickCheckReset(
  415. &m_smash,
  416. m_u32CollideIncludeBits,
  417. m_u32CollideDontcareBits,
  418. m_u32CollideExcludeBits & ~CSmash::Ducking);
  419. while (m_pRealm->m_smashatorium.QuickCheckNext(&pSmashed))
  420. {
  421. ASSERT(pSmashed->m_pThing);
  422. const bool bIsPlayer = (pSmashed->m_pThing->GetClassID() == CDudeID);
  423. // we need to check ducking collisions unconditionally so we can unlock an achievement, but then we carry on if it should have missed.
  424. if ((m_u32CollideExcludeBits & CSmash::Ducking) && (pSmashed->m_bits & CSmash::Ducking))
  425. {
  426. if (bIsPlayer)
  427. UnlockAchievement(ACHIEVEMENT_DUCK_UNDER_ROCKET);
  428. continue; // keep going.
  429. }
  430. if (bIsPlayer)
  431. UnlockAchievement(ACHIEVEMENT_ROCKET_TO_THE_FACE);
  432. CThing* pShooter;
  433. m_pRealm->m_idbank.GetThingByID(&pShooter, m_u16ShooterID);
  434. if (pShooter)
  435. {
  436. // If a Sentry gun shot this weapon, and it hit another Sentry gun, then
  437. // ignore the collision.
  438. if (!(pSmashed->m_pThing->GetClassID() == CSentryID && pShooter->GetClassID() == CSentryID))
  439. {
  440. m_eState = CWeapon::State_Explode;
  441. // Move back to previous position where expolosion should appear
  442. m_dX = dPrevX;
  443. m_dZ = dPrevZ;
  444. }
  445. }
  446. // Can't determine the shooter,but we did collide, so blow up.
  447. else
  448. {
  449. m_eState = CWeapon::State_Explode;
  450. // Move back to the previous position before doing the explosion so
  451. // that the explosion doesn't always go off behind the thing it
  452. // hits and blow it forward.
  453. m_dX = dPrevX;
  454. m_dZ = dPrevZ;
  455. }
  456. }
  457. }
  458. else
  459. {
  460. // Check for collision with self and if no collision, then arm
  461. CThing* pShooter = NULL;
  462. m_pRealm->m_idbank.GetThingByID(&pShooter, m_u16ShooterID);
  463. // If the shooter is valid, then arm when it clears the shooter
  464. if (pShooter)
  465. {
  466. CSmash* pSmashed = pShooter->GetSmash();
  467. if (pSmashed)
  468. {
  469. pSmashed = (CSmash*) &(((CThing3d*) pShooter)->m_smash);
  470. if (!(m_pRealm->m_smashatorium.QuickCheck(&m_smash, pSmashed)))
  471. m_bArmed = true;
  472. }
  473. else
  474. {
  475. if (lThisTime > m_lTimer)
  476. m_bArmed = true;
  477. }
  478. }
  479. // else do it the old fashioned way, so at least it will arm
  480. else
  481. {
  482. if (lThisTime > m_lTimer)
  483. m_bArmed = true;
  484. }
  485. }
  486. // See if its time to create a new puff of smoke
  487. if (lThisTime > m_lSmokeTimer)
  488. {
  489. m_lSmokeTimer = lThisTime + ms_lSmokeTrailInterval;
  490. CFire* pSmoke = NULL;
  491. if (CThing::Construct(CThing::CFireID, m_pRealm, (CThing**) &pSmoke) == 0)
  492. {
  493. // This needs to be fixed by calculating the position of the back end of
  494. // the rocket in 3D based on the rotation.
  495. pSmoke->Setup(dPrevX, m_dY, dPrevZ, ms_lSmokeTimeToLive, true, CFire::SmallSmoke);
  496. pSmoke->m_u16ShooterID = m_u16ShooterID;
  497. }
  498. }
  499. // Update sound position.
  500. SetInstanceVolume(m_siThrust, DistanceToVolume(m_dX, m_dY, m_dZ, LaunchSndHalfLife) );
  501. break;
  502. //-----------------------------------------------------------------------
  503. // RemoteControl
  504. //-----------------------------------------------------------------------
  505. case CWeapon::State_RemoteControl:
  506. m_bArmed = true;
  507. {
  508. CSmash* pSmashed = NULL;
  509. if (m_pRealm->m_smashatorium.QuickCheck(&m_smash,
  510. m_u32CollideIncludeBits,
  511. m_u32CollideDontcareBits,
  512. m_u32CollideExcludeBits, &pSmashed))
  513. m_eState = CWeapon::State_Explode;
  514. }
  515. // See if its time to create a new puff of smoke
  516. if (lThisTime > m_lSmokeTimer)
  517. {
  518. m_lSmokeTimer = lThisTime + ms_lSmokeTrailInterval;
  519. CFire* pSmoke = NULL;
  520. if (CThing::Construct(CThing::CFireID, m_pRealm, (CThing**) &pSmoke) == 0)
  521. {
  522. // This needs to be fixed by calculating the position of the back end of
  523. // the rocket in 3D based on the rotation.
  524. pSmoke->Setup(dPrevX, m_dY, dPrevZ, ms_lSmokeTimeToLive, true, CFire::SmallSmoke);
  525. pSmoke->m_u16ShooterID = m_u16ShooterID;
  526. }
  527. }
  528. dPrevX = m_dX;
  529. dPrevZ = m_dZ;
  530. // Update sound position.
  531. SetInstanceVolume(m_siThrust, DistanceToVolume(m_dX, m_dY, m_dZ, LaunchSndHalfLife) );
  532. break;
  533. //-----------------------------------------------------------------------
  534. // Explode
  535. //-----------------------------------------------------------------------
  536. case CWeapon::State_Explode:
  537. // Start an explosion object and then kill rocket
  538. // object
  539. CExplode* pExplosion;
  540. if (CThing::Construct(CThing::CExplodeID, m_pRealm, (CThing**) &pExplosion) == 0)
  541. {
  542. pExplosion->Setup(m_dX, MAX(m_dY-30, 0.0), m_dZ, m_u16ShooterID);
  543. PlaySample( // Returns nothing.
  544. // Does not fail.
  545. g_smidRocketExplode, // In: Identifier of sample you want played.
  546. SampleMaster::Destruction, // In: Sound Volume Category for user adjustment
  547. DistanceToVolume(m_dX, m_dY, m_dZ, ExplosionSndHalfLife) ); // In: Initial Sound Volume (0 - 255)
  548. // Old call: PlaySample(g_smidRocketExplode);
  549. }
  550. short a;
  551. CFire* pSmoke;
  552. for (a = 0; a < 8; a++)
  553. {
  554. if (CThing::Construct(CThing::CFireID, m_pRealm, (CThing**) &pSmoke) == 0)
  555. {
  556. pSmoke->Setup(m_dX - 4 + GetRandom() % 9, m_dY-20, m_dZ - 4 + GetRandom() % 9, 4000, true, CFire::Smoke);
  557. pSmoke->m_u16ShooterID = m_u16ShooterID;
  558. }
  559. }
  560. delete this;
  561. return;
  562. break;
  563. }
  564. // Update sphere.
  565. m_smash.m_sphere.sphere.X = m_dX;
  566. m_smash.m_sphere.sphere.Y = m_dY;
  567. m_smash.m_sphere.sphere.Z = m_dZ;
  568. m_smash.m_sphere.sphere.lRadius = 2 * m_sprite.m_sRadius;
  569. // Update the smash.
  570. m_pRealm->m_smashatorium.Update(&m_smash);
  571. // Save time for next time
  572. m_lPrevTime = lThisTime;
  573. }
  574. }
  575. ////////////////////////////////////////////////////////////////////////////////
  576. // Render object
  577. ////////////////////////////////////////////////////////////////////////////////
  578. void CRocket::Render(void)
  579. {
  580. long lThisTime = m_pRealm->m_time.GetGameTime();
  581. m_sprite.m_pmesh = (RMesh*) m_anim.m_pmeshes->GetAtTime(lThisTime);
  582. m_sprite.m_psop = (RSop*) m_anim.m_psops->GetAtTime(lThisTime);
  583. m_sprite.m_ptex = (RTexture*) m_anim.m_ptextures->GetAtTime(lThisTime);
  584. m_sprite.m_psphere = (RP3d*) m_anim.m_pbounds->GetAtTime(lThisTime);
  585. // Reset rotation so it is not cumulative
  586. m_trans.Make1();
  587. // Set its pointing direction
  588. m_trans.Ry(rspMod360(m_dRot));
  589. // Eventually this should be channel driven also
  590. // m_sprite.m_sRadius = m_sCurRadius;
  591. if (m_eState == State_Hide)
  592. m_sprite.m_sInFlags = CSprite::InHidden;
  593. else
  594. m_sprite.m_sInFlags = 0;
  595. // If we're not a child of someone else...
  596. if (m_idParent == CIdBank::IdNil)
  597. {
  598. // Map from 3d to 2d coords
  599. Map3Dto2D((short) m_dX, (short) m_dY, (short) m_dZ, &m_sprite.m_sX2, &m_sprite.m_sY2);
  600. // Priority is based on Z.
  601. m_sprite.m_sPriority = m_dZ;
  602. // Layer should be based on info we get from the attribute map
  603. m_sprite.m_sLayer = CRealm::GetLayerViaAttrib(m_pRealm->GetLayer((short) m_dX, (short) m_dZ));
  604. m_sprite.m_ptrans = &m_trans;
  605. // Update sprite in scene
  606. m_pRealm->m_scene.UpdateSprite(&m_sprite);
  607. // Render the 2D shadow sprite
  608. CWeapon::Render();
  609. }
  610. else
  611. {
  612. // m_idParent is setting our transform relative to its position
  613. // and we are drawn by the scene with the parent.
  614. }
  615. }
  616. ////////////////////////////////////////////////////////////////////////////////
  617. // Setup
  618. ////////////////////////////////////////////////////////////////////////////////
  619. short CRocket::Setup( // Returns 0 if successfull, non-zero otherwise
  620. short sX, // In: New x coord
  621. short sY, // In: New y coord
  622. short sZ) // In: New z coord
  623. {
  624. short sResult = 0;
  625. // Use specified position
  626. m_dX = (double)sX;
  627. m_dY = (double)sY;
  628. m_dZ = (double)sZ;
  629. m_dHorizVel = 0.0;
  630. // Load resources
  631. sResult = GetResources();
  632. // Enable the 2D shadow sprite
  633. PrepareShadow();
  634. m_bArmed = false;
  635. // Set the collision bits
  636. m_u32CollideIncludeBits = CSmash::Character | CSmash::Misc | CSmash::Barrel;
  637. m_u32CollideDontcareBits = CSmash::Good | CSmash::Bad;
  638. m_u32CollideExcludeBits = CSmash::Ducking;
  639. m_smash.m_bits = CSmash::Projectile;
  640. m_smash.m_pThing = this;
  641. m_sCurRadius = 10 * m_pRealm->m_scene.m_dScale3d;
  642. m_lSmokeTimer = 0;
  643. return sResult;
  644. }
  645. ////////////////////////////////////////////////////////////////////////////////
  646. // Get all required resources
  647. ////////////////////////////////////////////////////////////////////////////////
  648. short CRocket::GetResources(void) // Returns 0 if successfull, non-zero otherwise
  649. {
  650. short sResult = 0;
  651. sResult = m_anim.Get(ms_apszResNames);
  652. if (sResult == 0)
  653. {
  654. sResult = rspGetResource(&g_resmgrGame, m_pRealm->Make2dResPath(SMALL_SHADOW_FILE), &(m_spriteShadow.m_pImage), RFile::LittleEndian);
  655. if (sResult == 0)
  656. {
  657. // add more gets
  658. }
  659. else
  660. {
  661. TRACE("CGrenade::GetResources - Failed to open 2D shadow image\n");
  662. }
  663. }
  664. else
  665. {
  666. TRACE("CRocket::GetResources - Failed to open 3D animation for rocket\n");
  667. }
  668. return sResult;
  669. }
  670. ////////////////////////////////////////////////////////////////////////////////
  671. // Free all resources
  672. ////////////////////////////////////////////////////////////////////////////////
  673. short CRocket::FreeResources(void) // Returns 0 if successfull, non-zero otherwise
  674. {
  675. m_anim.Release();
  676. return 0;
  677. }
  678. ////////////////////////////////////////////////////////////////////////////////
  679. // Preload - basically trick the resource manager into caching resources
  680. // for this object so there won't be a delay the first time it is
  681. // created.
  682. ////////////////////////////////////////////////////////////////////////////////
  683. short CRocket::Preload(
  684. CRealm* prealm) // In: Calling realm.
  685. {
  686. CAnim3D anim;
  687. RImage* pimage;
  688. short sResult = anim.Get(ms_apszResNames);
  689. anim.Release();
  690. rspGetResource(&g_resmgrGame, prealm->Make2dResPath(SMALL_SHADOW_FILE), &pimage, RFile::LittleEndian);
  691. rspReleaseResource(&g_resmgrGame, &pimage);
  692. CacheSample(g_smidRocketFire);
  693. CacheSample(g_smidRocketExplode);
  694. return sResult;
  695. }
  696. ////////////////////////////////////////////////////////////////////////////////
  697. // ProcessMessages
  698. ////////////////////////////////////////////////////////////////////////////////
  699. void CRocket::ProcessMessages(void)
  700. {
  701. GameMessage msg;
  702. if (m_MessageQueue.DeQ(&msg) == true)
  703. {
  704. switch(msg.msg_Generic.eType)
  705. {
  706. case typeObjectDelete:
  707. m_MessageQueue.Empty();
  708. m_eState = State_Deleted;
  709. // Don't delete this here. Instead make sure the state is
  710. // set so Update() knows to delete us and return immediately
  711. // (before something else changes our state).
  712. return;
  713. break;
  714. }
  715. }
  716. // Dump the rest of the messages
  717. m_MessageQueue.Empty();
  718. return;
  719. }
  720. ////////////////////////////////////////////////////////////////////////////////
  721. // EOF
  722. ////////////////////////////////////////////////////////////////////////////////