grenade.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792
  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. // grenade.cpp
  19. // Project: Nostril (aka Postal)
  20. //
  21. // This module implements the CGrenade weapon class which is a hand
  22. // thrown grenade weapon.
  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/11/97 JMI Stripped 2D. Added 3D and a concept of having a parent.
  32. //
  33. // 02/13/97 JMI Changing RForm3d to RSop.
  34. //
  35. // 02/15/97 JMI Now sets m_sprite.m_psphere Render().
  36. //
  37. // 02/15/97 JMI Made rotation to match CDude's in Update().
  38. //
  39. // 02/17/97 JMI Removed m_sCurRadius from 3D to 2D mapping in Render().
  40. // Also, changed all querries on the height map to multiply
  41. // by 4.
  42. //
  43. // 02/18/97 BRH Fixed Explosion. Was calling the wrong setup function.
  44. //
  45. // 02/19/97 BRH Added processing of message queue to check for
  46. // ObjectDelted messages.
  47. //
  48. // 02/23/97 BRH Added Preload() function to cache resources for this
  49. // object before game play begins.
  50. //
  51. // 02/24/97 JMI Now uses AdjustPosVel() for accel due to gravity and
  52. // AdjustVel() for ground drag.
  53. // Also, plays sound when it hits terrain wall while in air.
  54. //
  55. // 02/24/97 BRH Made roll state check for ground to see if it is falling,
  56. // and if so it goes back to airborne State_Go.
  57. //
  58. // 02/24/97 BRH Changed ground and wall finding algorithm and made the
  59. // grenade delete itself it is fired in an invalid position
  60. // i.e. stuck in a wall.
  61. //
  62. // 02/24/97 JMI No longer sets the m_type member of the m_sprite b/c it
  63. // is set by m_sprite's constructor.
  64. //
  65. // 02/24/97 BRH Tuned the distance it travels on the ground by increasing
  66. // the requirements for bounce. Also made it possible to
  67. // blow up in states other than the final state so that the
  68. // fuse time is consistant.
  69. //
  70. // 03/03/97 JMI Commented out dHorizVelocity and dVertVelocity parameters
  71. // to Setup() so that this version would be a virtual over-
  72. // ride of CWeapon's.
  73. //
  74. // 03/13/97 JMI Load now takes a version number.
  75. //
  76. // 03/19/97 BRH Changed ProcessMessages to return void so that it
  77. // matches the new virtual function in the CWeapon
  78. // base class.
  79. //
  80. // 03/21/97 BRH Changed attribute checking to ignore the NOT_WALKABLE
  81. // attribute so that grenades won't bounce off the edge
  82. // of the world anymore.
  83. //
  84. // 04/10/97 BRH Updated this to work with the new multi layer attribute
  85. // maps.
  86. //
  87. // 04/21/97 BRH Changed call to explosion Setup function to include a
  88. // parameter to select the special grenade explosion rather
  89. // than the default explosion animation.
  90. //
  91. // 04/24/97 BRH Added a puff of smoke in addition to the explosion.
  92. //
  93. // 05/04/97 BRH Took out an unused reference to an STL iterator.
  94. // Increased the drag for the grenade when it hits the grounds.
  95. //
  96. // 05/20/97 BRH Bumped up the ground drag to make the SetRange more
  97. // accurate.
  98. //
  99. // 05/29/97 JMI Removed ASSERT on m_pRealm->m_pAttribMap which no longer
  100. // exists.
  101. //
  102. // 06/03/97 BRH Capped the ground velocity and decreased the drag back
  103. // to a more normal level. Also added a bit of variance in
  104. // the angle when it hits the ground.
  105. //
  106. // 06/11/97 BRH Passes on shooter ID to the explosion that it creates.
  107. //
  108. // 06/12/97 BRH Added shooter ID to the call to Setup for the explosion.
  109. // Also added the State_Hide to hide the grenade.
  110. //
  111. // 06/13/97 JMI Grenade was not unhiding until it had no parent b/c the
  112. // part that unhides was inside the 'if I don't have a parent'
  113. // condition.
  114. //
  115. // 06/16/97 BRH Fixed starting condition over not walkable attribute.
  116. //
  117. // 06/17/97 BRH Changed the velocity of the grenade to 33% of its
  118. // original value when it hits a wall.
  119. //
  120. // 06/17/97 JMI Converted all occurrences of rand() to GetRand() and
  121. // srand() to SeedRand().
  122. //
  123. // 06/25/97 BRH Added use of base class 2D shadow on the ground, but loaded
  124. // a smaller shadow resource.
  125. //
  126. // 06/30/97 BRH Added sound effect cache to the Preload function.
  127. //
  128. // 07/09/97 JMI Now uses m_pRealm->Make2dResPath() to get the fullpath
  129. // for 2D image components.
  130. //
  131. // 07/09/97 JMI Changed Preload() to take a pointer to the calling realm
  132. // as a parameter.
  133. //
  134. // 07/18/97 JMI Got rid of bogus immitation PlaySample functions.
  135. // Now there is one PlaySample() function. Also, you now
  136. // MUST specify a category and you don't have to specify a
  137. // SoundInstance ptr to specify a volume.
  138. //
  139. // 07/30/97 JMI Same old delete error showed up on Alpha.
  140. // ProcessMessages() was deleting the grenade on a delete msg
  141. // but, once returned to Update(), it was checking the
  142. // m_eState member to see if it should return. Unfortunately,
  143. // since 'this' had already been deallocated, it was too late
  144. // to do such a thing.
  145. //
  146. // 08/05/97 JMI Changed priority to use Z position rather than 2D
  147. // projected Y position.
  148. //
  149. // 08/08/97 JMI Added Style so this can be visually represented in multiple
  150. // ways.
  151. //
  152. // 08/08/97 JMI Added variables for rotating the weapon and smoke when
  153. // dynamite.
  154. //
  155. // 08/17/97 JMI Changed m_pthingParent to m_idParent.
  156. //
  157. ////////////////////////////////////////////////////////////////////////////////
  158. #define GRENADE_CPP
  159. #include "RSPiX.h"
  160. #include <math.h>
  161. #include "grenade.h"
  162. #include "dude.h"
  163. #include "explode.h"
  164. #include "fire.h"
  165. #include "SampleMaster.h"
  166. #include "reality.h"
  167. ////////////////////////////////////////////////////////////////////////////////
  168. // Macros/types/etc.
  169. ////////////////////////////////////////////////////////////////////////////////
  170. #define SMALL_SHADOW_FILE "smallshadow.img"
  171. ////////////////////////////////////////////////////////////////////////////////
  172. // Variables/data
  173. ////////////////////////////////////////////////////////////////////////////////
  174. // These are default values -- actually values are set using the editor!
  175. double CGrenade::ms_dAccUser = 250.0; // Acceleration due to user
  176. double CGrenade::ms_dAccDrag = 300.0; // Acceleration due to drag
  177. double CGrenade::ms_dMaxVelFore = 150.0; // Maximum forward velocity
  178. double CGrenade::ms_dMaxVelBack = -150.0; // Maximum backward velocity
  179. double CGrenade::ms_dDegPerSec = 150.0; // Degrees of rotation per second
  180. double CGrenade::ms_dCloseDistance = 30.0; // Close enough to hit CDude
  181. double CGrenade::ms_dGravity = -9.5; // acceleration due to gravity
  182. double CGrenade::ms_dThrowVertVel = 30; // Throw up at this velocity
  183. double CGrenade::ms_dThrowHorizVel = 200; // Throw out at this velocity
  184. double CGrenade::ms_dMinBounceVel = 40.0; // Will bounce up if more than this
  185. double CGrenade::ms_dBounceTransferFract = -0.2; // Amount of bounce velocity transferred.
  186. long CGrenade::ms_lRandomAvoidTime = 200; // Time to wander before looking again
  187. long CGrenade::ms_lReseekTime = 1000; // Do a 'find' again
  188. long CGrenade::ms_lGrenadeFuseTime = 1500; // Time from throw to blow
  189. long CGrenade::ms_lSmokeInterval = 100; // Time between smokes.
  190. char* CGrenade::ms_apszResNames[CGrenade::NumStyles] = // Res names indexed Style.
  191. {
  192. "3d/grenade", // Grenade.
  193. "3d/dynamite", // Dynamite.
  194. };
  195. // Dimishes velocities once it hits the ground.
  196. double CGrenade::ms_adGroundDimisher[NumStyles] =
  197. {
  198. 0.66, // Grenade.
  199. 0.66, // Dynamite.
  200. };
  201. // Dimishes velocities when bouncing.
  202. double CGrenade::ms_adBounceDimisher[NumStyles] =
  203. {
  204. 0.33, // Grenade.
  205. 0.66, // Dynamite.
  206. };
  207. // Let this auto-init to 0
  208. short CGrenade::ms_sFileCount;
  209. ////////////////////////////////////////////////////////////////////////////////
  210. // Load object (should call base class version!)
  211. ////////////////////////////////////////////////////////////////////////////////
  212. short CGrenade::Load( // Returns 0 if successfull, non-zero otherwise
  213. RFile* pFile, // In: File to load from
  214. bool bEditMode, // In: True for edit mode, false otherwise
  215. short sFileCount, // In: File count (unique per file, never 0)
  216. ULONG ulFileVersion) // In: Version of file format to load.
  217. {
  218. short sResult = 0;
  219. sResult = CWeapon::Load(pFile, bEditMode, sFileCount, ulFileVersion);
  220. if (sResult == SUCCESS)
  221. {
  222. // Load common data just once per file (not with each object)
  223. if (ms_sFileCount != sFileCount)
  224. {
  225. ms_sFileCount = sFileCount;
  226. // Load static data
  227. switch (ulFileVersion)
  228. {
  229. default:
  230. case 1:
  231. pFile->Read(&ms_dAccUser);
  232. pFile->Read(&ms_dAccDrag);
  233. pFile->Read(&ms_dMaxVelFore);
  234. pFile->Read(&ms_dMaxVelBack);
  235. pFile->Read(&ms_dDegPerSec);
  236. break;
  237. }
  238. }
  239. // Load object data
  240. switch (ulFileVersion)
  241. {
  242. default:
  243. case 1:
  244. break;
  245. }
  246. // Make sure there were no file errors or format errors . . .
  247. if (!pFile->Error() && sResult == 0)
  248. {
  249. // Get resources
  250. sResult = GetResources();
  251. }
  252. else
  253. {
  254. sResult = -1;
  255. TRACE("CGrenade::Load(): Error reading from file!\n");
  256. }
  257. }
  258. return sResult;
  259. }
  260. ////////////////////////////////////////////////////////////////////////////////
  261. // Save object (should call base class version!)
  262. ////////////////////////////////////////////////////////////////////////////////
  263. short CGrenade::Save( // Returns 0 if successfull, non-zero otherwise
  264. RFile* pFile, // In: File to save to
  265. short sFileCount) // In: File count (unique per file, never 0)
  266. {
  267. // In most cases, the base class Save() should be called. In this case it
  268. // isn't because the base class doesn't have a Save()!
  269. CWeapon::Save(pFile, sFileCount);
  270. // Save common data just once per file (not with each object)
  271. if (ms_sFileCount != sFileCount)
  272. {
  273. ms_sFileCount = sFileCount;
  274. // Save static data
  275. pFile->Write(&ms_dAccUser);
  276. pFile->Write(&ms_dAccDrag);
  277. pFile->Write(&ms_dMaxVelFore);
  278. pFile->Write(&ms_dMaxVelBack);
  279. pFile->Write(&ms_dDegPerSec);
  280. }
  281. // Save object data
  282. return 0;
  283. }
  284. ////////////////////////////////////////////////////////////////////////////////
  285. // Update object
  286. ////////////////////////////////////////////////////////////////////////////////
  287. void CGrenade::Update(void)
  288. {
  289. USHORT usAttrib;
  290. short sHeight = m_sPrevHeight;
  291. double dPrevVertVel;
  292. double dNewX;
  293. double dNewY;
  294. double dNewZ;
  295. if (!m_sSuspend)
  296. {
  297. // Get new time
  298. long lThisTime = m_pRealm->m_time.GetGameTime();
  299. ProcessMessages();
  300. if (m_eState == State_Deleted)
  301. {
  302. delete this;
  303. return;
  304. }
  305. // Calculate elapsed time in seconds
  306. double dSeconds = (double)(lThisTime - m_lPrevTime) / 1000.0;
  307. // Check the current state
  308. switch (m_eState)
  309. {
  310. case CWeapon::State_Hide:
  311. case CWeapon::State_Idle:
  312. Smoke();
  313. break;
  314. case CWeapon::State_Fire:
  315. sHeight = m_pRealm->GetHeight((short) m_dX, (short) m_dZ);
  316. usAttrib = m_pRealm->GetFloorAttribute((short) m_dX, (short) m_dZ);
  317. // If it starts at an invalid position like inside a wall, kill it
  318. if (m_dY < sHeight)
  319. {
  320. delete this;
  321. return;
  322. }
  323. m_eState = State_Go;
  324. m_lTimer = lThisTime + ms_lGrenadeFuseTime;
  325. break;
  326. //-----------------------------------------------------------------------
  327. // Go - fly through the air until hit the ground, change directions on
  328. // obstacle collision.
  329. //-----------------------------------------------------------------------
  330. case CWeapon::State_Go:
  331. // Do horizontal velocity
  332. dNewX = m_dX + COSQ[(short)m_dRot] * (m_dHorizVel * dSeconds);
  333. dNewZ = m_dZ - SINQ[(short)m_dRot] * (m_dHorizVel * dSeconds);
  334. // Do vertical velocity
  335. dNewY = m_dY;
  336. dPrevVertVel = m_dVertVel;
  337. AdjustPosVel(&dNewY, &m_dVertVel, dSeconds/*, dAccelerationDueToGravity*/);
  338. // Check the height to see if it hit the ground
  339. sHeight = m_pRealm->GetHeight((short) dNewX, (short) dNewZ);
  340. // Adjust apparent rotation.
  341. m_dAnimRotY = rspMod360(m_dAnimRotY + m_dAnimRotVelY * dSeconds);
  342. m_dAnimRotZ = rspMod360(m_dAnimRotZ + m_dAnimRotVelZ * dSeconds);
  343. // If its lower than the last and current height, assume it
  344. // hit the ground.
  345. if (dNewY < sHeight && m_dY >= sHeight)
  346. {
  347. m_dY = sHeight;
  348. m_dVertVel = dPrevVertVel;
  349. // If its vertical velocity is high enough, it will bounce, else
  350. // it will just start rolling
  351. if (-m_dVertVel > ms_dMinBounceVel)
  352. {
  353. m_dVertVel = m_dVertVel * ms_dBounceTransferFract;
  354. if (m_dHorizVel > 0)
  355. {
  356. AdjustVel(&m_dHorizVel, dSeconds, -ms_dAccDrag);
  357. if (m_dHorizVel < 0)
  358. m_dHorizVel = 0;
  359. }
  360. else if (m_dHorizVel < 0)
  361. {
  362. AdjustVel(&m_dHorizVel, dSeconds, ms_dAccDrag);
  363. m_dHorizVel = 0;
  364. }
  365. }
  366. else
  367. {
  368. m_eState = CWeapon::State_Roll;
  369. // Pop a cap on that horizontal velocity
  370. if (m_dHorizVel > 0)
  371. m_dHorizVel = MIN(m_dHorizVel, ms_adBounceDimisher[m_style] * ms_dMaxVelFore);
  372. else
  373. m_dHorizVel = MAX(m_dHorizVel, ms_adBounceDimisher[m_style] * ms_dMaxVelBack);
  374. m_dRot = rspMod360(m_dRot - 100 + (GetRand() % 201));
  375. }
  376. }
  377. else
  378. {
  379. // If it is above the last known ground and is now lower
  380. // than the height at its new position, assume it hit
  381. // a wall and should bounce.
  382. if (dNewY < sHeight && m_dY < sHeight)
  383. {
  384. dNewX = m_dX; // Restore last x position
  385. dNewZ = m_dZ; // Restore last z position
  386. m_dRot = BounceAngle(m_dRot); // Change directions
  387. // Cut down its horizontal velocity
  388. m_dHorizVel *= ms_adBounceDimisher[m_style];
  389. // Cut down rotations.
  390. m_dAnimRotY *= ms_adBounceDimisher[m_style];
  391. m_dAnimRotZ *= ms_adBounceDimisher[m_style];
  392. PlaySample(
  393. g_smidGrenadeBounce,
  394. SampleMaster::Weapon,
  395. DistanceToVolume(m_dX, m_dY, m_dZ, SideEffectSndHalfLife) ); // In: Initial Sound Volume (0 - 255)
  396. }
  397. else
  398. m_dY = dNewY;
  399. }
  400. m_dX = dNewX;
  401. m_dZ = dNewZ;
  402. Smoke();
  403. // See if its fuse is up
  404. if (lThisTime > m_lTimer)
  405. m_eState = CWeapon::State_Explode;
  406. break;
  407. //-----------------------------------------------------------------------
  408. // Roll - Once it hits the ground, roll until it stops.
  409. //-----------------------------------------------------------------------
  410. case CWeapon::State_Roll:
  411. // Ground causes drag
  412. // Decelerate to zero. When you reach zero, go
  413. // to find state.
  414. #if 0
  415. if (m_dVel > 0)
  416. {
  417. m_dVel -= ms_dAccDrag * dSeconds;
  418. if (m_dVel < 0)
  419. m_dVel = 0;
  420. }
  421. else if (m_dVel < 0)
  422. {
  423. m_dVel += ms_dAccDrag * dSeconds;
  424. if (m_dVel > 0)
  425. m_dVel = 0;
  426. }
  427. #else
  428. if (m_dHorizVel > 0)
  429. {
  430. AdjustVel(&m_dHorizVel, dSeconds, -ms_dAccDrag);
  431. if (m_dHorizVel < 0)
  432. m_dHorizVel = 0;
  433. }
  434. else if (m_dHorizVel < 0)
  435. {
  436. AdjustVel(&m_dHorizVel, dSeconds, ms_dAccDrag);
  437. // if (m_dVel > 0)
  438. m_dHorizVel = 0;
  439. }
  440. #endif
  441. // If it has stopped, then change to find state
  442. if (m_dHorizVel == 0)
  443. m_eState = CWeapon::State_Explode;
  444. dNewX = m_dX + COSQ[(short)m_dRot] * (m_dHorizVel * dSeconds);
  445. dNewZ = m_dZ - SINQ[(short)m_dRot] * (m_dHorizVel * dSeconds);
  446. // Check for obstacles
  447. usAttrib = m_pRealm->GetFloorAttribute((short) dNewX, (short) dNewZ);
  448. sHeight = m_pRealm->GetHeight((short) dNewX, (short) dNewZ);
  449. // If it hit any obstacles, make it bounce off
  450. if (usAttrib & REALM_ATTR_NOT_WALKABLE || sHeight > m_dY)
  451. {
  452. // Restore previous position
  453. dNewX = m_dX;
  454. dNewZ = m_dZ;
  455. // Change directions
  456. m_dRot = BounceAngle(m_dRot);
  457. }
  458. // See if it fell off of something, make it go back to the
  459. // airborne state.
  460. if (sHeight < (short) m_dY)
  461. {
  462. m_dVertVel = 0;
  463. m_eState = State_Go;
  464. }
  465. m_dX = dNewX;
  466. m_dZ = dNewZ;
  467. Smoke();
  468. // See if its fuse is up
  469. if (lThisTime > m_lTimer)
  470. m_eState = CWeapon::State_Explode;
  471. break;
  472. //-----------------------------------------------------------------------
  473. // Explode
  474. //-----------------------------------------------------------------------
  475. case CWeapon::State_Explode:
  476. if (lThisTime > m_lTimer)
  477. {
  478. // Start an explosion object and then kill rocket
  479. // object
  480. CExplode* pExplosion;
  481. if (CThing::Construct(CThing::CExplodeID, m_pRealm, (CThing**) &pExplosion) == 0)
  482. {
  483. pExplosion->Setup(m_dX, m_dY, m_dZ, m_u16ShooterID, 1);
  484. PlaySample(g_smidGrenadeExplode,
  485. SampleMaster::Destruction,
  486. DistanceToVolume(m_dX, m_dY, m_dZ, ExplosionSndHalfLife) ); // In: Initial Sound Volume (0 - 255)
  487. }
  488. short a;
  489. CFire* pSmoke;
  490. for (a = 0; a < 4; a++)
  491. {
  492. if (CThing::Construct(CThing::CFireID, m_pRealm, (CThing**) &pSmoke) == 0)
  493. {
  494. pSmoke->Setup(m_dX + GetRand() % 8, m_dY, m_dZ + GetRand() % 8, 2000, true, CFire::Smoke);
  495. pSmoke->m_u16ShooterID = m_u16ShooterID;
  496. }
  497. }
  498. delete this;
  499. return;
  500. }
  501. break;
  502. }
  503. // Save height for next time
  504. m_sPrevHeight = sHeight;
  505. // Save time for next time
  506. m_lPrevTime = lThisTime;
  507. }
  508. }
  509. ////////////////////////////////////////////////////////////////////////////////
  510. // Render object
  511. ////////////////////////////////////////////////////////////////////////////////
  512. void CGrenade::Render(void)
  513. {
  514. // Animate.
  515. long lCurTime = m_pRealm->m_time.GetGameTime();
  516. m_sprite.m_pmesh = (RMesh*) m_anim.m_pmeshes->GetAtTime( lCurTime % m_anim.m_pmeshes->TotalTime());
  517. m_sprite.m_psop = (RSop*) m_anim.m_psops->GetAtTime( lCurTime % m_anim.m_psops->TotalTime());
  518. m_sprite.m_ptex = (RTexture*) m_anim.m_ptextures->GetAtTime( lCurTime % m_anim.m_ptextures->TotalTime());
  519. m_sprite.m_psphere = (RP3d*) m_anim.m_pbounds->GetAtTime( lCurTime % m_anim.m_pbounds->TotalTime());
  520. // This should eventually be channel driven too.
  521. m_sprite.m_sRadius = m_sCurRadius;
  522. // See if it is hidden or not
  523. if (m_eState == State_Hide)
  524. m_sprite.m_sInFlags = CSprite::InHidden;
  525. else
  526. m_sprite.m_sInFlags = 0;
  527. // If we're not a child of someone else . . .
  528. if (m_idParent == CIdBank::IdNil)
  529. {
  530. // Map from 3d to 2d coords
  531. Map3Dto2D((short) m_dX, (short) m_dY, (short) m_dZ, &m_sprite.m_sX2, &m_sprite.m_sY2);
  532. // Priority is based on bottom edge of sprite
  533. m_sprite.m_sPriority = m_dZ;
  534. // Layer should be based on info we get from attribute map.
  535. m_sprite.m_sLayer = CRealm::GetLayerViaAttrib(m_pRealm->GetLayer((short) m_dX, (short) m_dZ));
  536. // Adjust transformation based current rotations.
  537. m_trans.Make1();
  538. m_trans.Ry(m_dAnimRotY);
  539. m_trans.Rx(m_dAnimRotZ);
  540. m_sprite.m_ptrans = &m_trans;
  541. // Update sprite in scene
  542. m_pRealm->m_scene.UpdateSprite(&m_sprite);
  543. // Let it render the shadow sprite.
  544. CWeapon::Render();
  545. }
  546. else
  547. {
  548. // m_idParent is setting our transform relative to its.
  549. // We are drawn when m_idBank is drawn. Don't add to scene.
  550. }
  551. }
  552. ////////////////////////////////////////////////////////////////////////////////
  553. // Setup new object - called by object that created this object
  554. ////////////////////////////////////////////////////////////////////////////////
  555. short CGrenade::Setup( // Returns 0 if successfull, non-zero otherwise
  556. short sX, // In: New x coord
  557. short sY, // In: New y coord
  558. short sZ/*, // In: New z coord
  559. double dHorizVelocity, // In: Horiz Vel (has a default)
  560. double dVertVelocity*/) // In: Vertical velocity (has a default)
  561. {
  562. short sResult = 0;
  563. // Use specified position
  564. m_dX = (double)sX;
  565. m_dY = (double)sY;
  566. m_dZ = (double)sZ;
  567. m_dHorizVel = ms_dThrowHorizVel;//dHorizVelocity;
  568. m_dVertVel = ms_dThrowVertVel;//dVertVelocity;
  569. m_dAnimRotVelY = GetRand() % 180;
  570. m_dAnimRotVelZ = GetRand() % 180;
  571. // HARD-WIRED CODE ALERT!
  572. // Eventually, this should be set via the bounding sphere radius.
  573. m_sCurRadius = 22; // FOR NOW, always half of scene.cpp:SCREEN_DIAMETER_FOR_3D.
  574. // Load resources
  575. sResult = GetResources();
  576. // Enable the 2D shadow.
  577. PrepareShadow();
  578. return sResult;
  579. }
  580. ////////////////////////////////////////////////////////////////////////////////
  581. // Get all required resources
  582. ////////////////////////////////////////////////////////////////////////////////
  583. short CGrenade::GetResources(void) // Returns 0 if successfull, non-zero otherwise
  584. {
  585. short sResult = 0;
  586. sResult = m_anim.Get(ms_apszResNames[m_style], NULL, NULL, NULL, RChannel_LoopAtStart | RChannel_LoopAtEnd);
  587. if (sResult == 0)
  588. {
  589. sResult = rspGetResource(&g_resmgrGame, m_pRealm->Make2dResPath(SMALL_SHADOW_FILE), &(m_spriteShadow.m_pImage), RFile::LittleEndian);
  590. if (sResult == 0)
  591. {
  592. // add more gets
  593. }
  594. else
  595. {
  596. TRACE("CGrenade::GetResources - Failed to open 2D shadow image\n");
  597. }
  598. }
  599. else
  600. {
  601. TRACE("CGrenade::GetResources - Failed to open 3D stuff file\n");
  602. }
  603. return sResult;
  604. }
  605. ////////////////////////////////////////////////////////////////////////////////
  606. // Free all resources
  607. ////////////////////////////////////////////////////////////////////////////////
  608. short CGrenade::FreeResources(void) // Returns 0 if successfull, non-zero otherwise
  609. {
  610. short sResult = 0;
  611. // Release resources for animation.
  612. m_anim.Release();
  613. return sResult;
  614. }
  615. ////////////////////////////////////////////////////////////////////////////////
  616. // Preload - basically trick the resource manager into caching resources
  617. // for this object so there won't be a delay the first time it is
  618. // created.
  619. ////////////////////////////////////////////////////////////////////////////////
  620. short CGrenade::Preload(
  621. CRealm* prealm) // In: Calling realm.
  622. {
  623. short sResult = 0;
  624. CAnim3D anim;
  625. RImage* pimage;
  626. short sStyle;
  627. for (sStyle = 0; sStyle < NumStyles; sStyle++)
  628. {
  629. if (anim.Get(ms_apszResNames[sStyle], NULL, NULL, NULL, RChannel_LoopAtStart | RChannel_LoopAtEnd) == 0)
  630. {
  631. anim.Release();
  632. }
  633. else
  634. {
  635. // Go ahead and overwrite any previous error.
  636. sResult = 1;
  637. }
  638. }
  639. rspGetResource(&g_resmgrGame, prealm->Make2dResPath(SMALL_SHADOW_FILE), &pimage, RFile::LittleEndian);
  640. rspReleaseResource(&g_resmgrGame, &pimage);
  641. CacheSample(g_smidGrenadeBounce);
  642. CacheSample(g_smidGrenadeExplode);
  643. return sResult;
  644. }
  645. ////////////////////////////////////////////////////////////////////////////////
  646. // ProcessMessages
  647. ////////////////////////////////////////////////////////////////////////////////
  648. void CGrenade::ProcessMessages(void)
  649. {
  650. GameMessage msg;
  651. if (m_MessageQueue.DeQ(&msg) == true)
  652. {
  653. switch(msg.msg_Generic.eType)
  654. {
  655. case typeObjectDelete:
  656. m_MessageQueue.Empty();
  657. m_eState = State_Deleted;
  658. return;
  659. break;
  660. }
  661. }
  662. // Dump the rest of the messages
  663. m_MessageQueue.Empty();
  664. return;
  665. }
  666. ////////////////////////////////////////////////////////////////////////////////
  667. // Smoke, if correct time.
  668. ////////////////////////////////////////////////////////////////////////////////
  669. void CGrenade::Smoke(void)
  670. {
  671. if (m_style == Dynamite)
  672. {
  673. long lCurTime = m_pRealm->m_time.GetGameTime();
  674. // If the timer has expired . . .
  675. if (lCurTime > m_lNextSmokeTime)
  676. {
  677. CFire* pSmoke;
  678. if (CThing::Construct(CThing::CFireID, m_pRealm, (CThing**) &pSmoke) == 0)
  679. {
  680. pSmoke->Setup(m_dX + GetRand() % 8, m_dY, m_dZ + GetRand() % 8, 2000, true, CFire::SmallSmoke);
  681. pSmoke->m_u16ShooterID = m_u16ShooterID;
  682. }
  683. m_lNextSmokeTime = lCurTime + ms_lSmokeInterval;
  684. }
  685. }
  686. }
  687. ////////////////////////////////////////////////////////////////////////////////
  688. // EOF
  689. ////////////////////////////////////////////////////////////////////////////////