fireball.cpp 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106
  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. // fireball.cpp
  19. // Project: Nostril (aka Postal)
  20. //
  21. // This module implements the CFireball weapon class which is a burning flame
  22. // for several different effects and weapons.
  23. //
  24. //
  25. // History:
  26. // 01/17/97 BRH Started this fireball ammo for use in making a flamethrower
  27. //
  28. // 04/25/97 JMI PreLoad() was not properly assigning into sResult.
  29. //
  30. // 04/29/97 JMI Changed name of ProcessMessages() to
  31. // ProcessFireballMessages() to avoid conflicts with
  32. // CWeapon's ProcessMessages().
  33. // Also, Update() was not moving the m_smash (it was merely
  34. // set the one time in Init()) which was causing it to not
  35. // collide with anything (I guess it might've collided with
  36. // things hovering around the upper, left corner of the realm).
  37. //
  38. // 05/29/97 JMI Removed ASSERT on m_pRealm->m_pAttribMap which no longer
  39. // exists.
  40. //
  41. // 06/11/97 BRH Passed on shooter ID to the message it sends when it burns
  42. // someone.
  43. //
  44. // 06/17/97 JMI Converted all occurrences of rand() to GetRand() and
  45. // srand() to SeedRand().
  46. //
  47. // 07/02/97 BRH Added CFirestream object that creates several CFireball
  48. // objects.
  49. //
  50. // 07/04/97 BRH Added auto alpha based on the time to live.
  51. //
  52. // 07/07/97 BRH Fixed casing on MIN to compile on both PC and Mac.
  53. //
  54. // 07/08/97 JMI Put in code in Render() to avoid an unlikely divide by zero.
  55. // Still have a divide by zero release mode problem though.
  56. // Also, there was initialization typo in CFireball::Setup().
  57. //
  58. // 07/08/97 JMI Fixed Render() to distribute the homogeneous alpha level
  59. // better. Still needs tuning.
  60. //
  61. // JMI Made heavier again.
  62. //
  63. // 07/09/97 JMI Now uses m_pRealm->Make2dResPath() to get the fullpath
  64. // for 2D image components.
  65. //
  66. // 07/09/97 JMI Changed Preload() to take a pointer to the calling realm
  67. // as a parameter.
  68. //
  69. // 07/10/97 JMI Now uses alpha mask and level for animation.
  70. //
  71. // 07/12/97 BRH Added IsPathClear check to path of fireball since its
  72. // velocity is so high that it can pass through walls
  73. // otherwise.
  74. //
  75. // 07/13/97 BRH Added a check to the Firestream creation of the
  76. // fireballs to make sure that the iniial position is
  77. // not in a wall. If it is, then the other two fireballs
  78. // with a further offset should not be created, otherwise
  79. // it will shoot through walls.
  80. //
  81. // Also changed the alpha to use 1 mask and tuned the levels
  82. // so that you can see it at the gun end.
  83. //
  84. // 07/14/97 JMI Now will detach itself from its parent via
  85. // m_sprite.m_psprParent->RemoveChild() in the rare event
  86. // that it is still parented going into the Render().
  87. // Also, threw in some random to the m_dRot before they are
  88. // separated from the CFirestream.
  89. //
  90. // 07/27/97 JMI Changed to use Z position (i.e., X/Z plane) instead of
  91. // Y2 position (i.e., viewing plane) position for draw
  92. // priority.
  93. //
  94. // 07/30/97 JMI Changed several deletes that occurred just before reading
  95. // instantiable member variables. Now both
  96. // ProcessFireballMessage() functions (there are two (one for
  97. // CFireballs and one for CFireStreams) ) do NOT delete this
  98. // when they receive a delete message. They instead set the
  99. // state to delete and allow the calling function to do the
  100. // delete. The problem here was that the calling function,
  101. // Update(), needed to look at one more member var, m_eState,
  102. // before returning, but, at that point (that is, after the
  103. // delete) this was invalid. This works find though when
  104. // using SmartHeap I think b/c of SmartHeap's bounds checking
  105. // areas to detect overwrites. The Alpha does not have these
  106. // areas (no SmartHeap) and, so, crashes easily in these cases.
  107. // There was also a problem in Update() where it deleted this
  108. // but did not return right away and one more member var access
  109. // was done after that point (probably added later). Fixed.
  110. //
  111. // 07/30/97 JMI Now uses m_dHorizVel for its velocity which is initially
  112. // set to ms_dFireVelocity but can be overridden after the
  113. // Setup() call.
  114. //
  115. // 08/08/97 JMI Now CFirestream::ProcessFireballMessages() passes on
  116. // delete messages to any fireballs it owns.
  117. //
  118. // 08/08/97 JMI Changed m_pFireball1, 2, & 3 to m_idFireball1, 2, & 3.
  119. //
  120. ////////////////////////////////////////////////////////////////////////////////
  121. #define FIREBALL_CPP
  122. #include "RSPiX.h"
  123. #include <math.h>
  124. #include "fireball.h"
  125. #include "game.h"
  126. #include "reality.h"
  127. ////////////////////////////////////////////////////////////////////////////////
  128. // Macros/types/etc.
  129. ////////////////////////////////////////////////////////////////////////////////
  130. #define SMALL_FILE "tinyfire.aan"
  131. #define MIN_ALPHA 30
  132. #define MAX_ALPHA 200
  133. // Note the sum of these two values should not exceed 255
  134. // Gets a random between -range / 2 and range / 2.
  135. #define RAND_SWAY(sway) ((GetRand() % sway) - sway / 2)
  136. #define FIREBALL_SWAY 15
  137. ////////////////////////////////////////////////////////////////////////////////
  138. // Variables/data
  139. ////////////////////////////////////////////////////////////////////////////////
  140. // Let this auto-init to 0
  141. short CFirestream::ms_sFileCount;
  142. short CFirestream::ms_sOffset1 = 12; // pixels from 1st to 2nd fireball
  143. short CFirestream::ms_sOffset2 = 24; // pixels from 1st to 3rd fireball
  144. ////////////////////////////////////////////////////////////////////////////////
  145. // Load object (should call base class version!)
  146. ////////////////////////////////////////////////////////////////////////////////
  147. short CFirestream::Load( // Returns 0 if successfull, non-zero otherwise
  148. RFile* pFile, // In: File to load from
  149. bool bEditMode, // In: True for edit mode, false otherwise
  150. short sFileCount, // In: File count (unique per file, never 0)
  151. ULONG ulFileVersion) // In: Version of file format to load.
  152. {
  153. short sResult = CThing::Load(pFile, bEditMode, sFileCount, ulFileVersion);
  154. if (sResult == 0)
  155. {
  156. // Load common data just once per file (not with each object)
  157. if (ms_sFileCount != sFileCount)
  158. {
  159. ms_sFileCount = sFileCount;
  160. // Load static data.
  161. switch (ulFileVersion)
  162. {
  163. default:
  164. case 1:
  165. break;
  166. }
  167. }
  168. // Load instance data.
  169. switch (ulFileVersion)
  170. {
  171. default:
  172. case 1:
  173. break;
  174. }
  175. // Make sure there were no file errors or format errors . . .
  176. if (!pFile->Error() && sResult == 0)
  177. {
  178. }
  179. else
  180. {
  181. sResult = -1;
  182. TRACE("CFirestream::Load(): Error reading from file!\n");
  183. }
  184. }
  185. else
  186. {
  187. TRACE("CFirestream::Load(): CThing::Load() failed.\n");
  188. }
  189. return sResult;
  190. }
  191. ////////////////////////////////////////////////////////////////////////////////
  192. // Save object (should call base class version!)
  193. ////////////////////////////////////////////////////////////////////////////////
  194. short CFirestream::Save( // Returns 0 if successfull, non-zero otherwise
  195. RFile* pFile, // In: File to save to
  196. short sFileCount) // In: File count (unique per file, never 0)
  197. {
  198. short sResult = CThing::Save(pFile, sFileCount);
  199. if (sResult == 0)
  200. {
  201. // Save common data just once per file (not with each object)
  202. if (ms_sFileCount != sFileCount)
  203. {
  204. ms_sFileCount = sFileCount;
  205. // Save static data
  206. }
  207. }
  208. else
  209. {
  210. TRACE("CFirestream::Save(): CThing::Save() failed.\n");
  211. }
  212. return sResult;
  213. }
  214. ////////////////////////////////////////////////////////////////////////////////
  215. // Startup object
  216. ////////////////////////////////////////////////////////////////////////////////
  217. short CFirestream::Startup(void) // Returns 0 if successfull, non-zero otherwise
  218. {
  219. return Init();
  220. }
  221. ////////////////////////////////////////////////////////////////////////////////
  222. // Shutdown object
  223. ////////////////////////////////////////////////////////////////////////////////
  224. short CFirestream::Shutdown(void) // Returns 0 if successfull, non-zero otherwise
  225. {
  226. return 0;
  227. }
  228. ////////////////////////////////////////////////////////////////////////////////
  229. // Suspend object
  230. ////////////////////////////////////////////////////////////////////////////////
  231. void CFirestream::Suspend(void)
  232. {
  233. m_sSuspend++;
  234. }
  235. ////////////////////////////////////////////////////////////////////////////////
  236. // Resume object
  237. ////////////////////////////////////////////////////////////////////////////////
  238. void CFirestream::Resume(void)
  239. {
  240. m_sSuspend--;
  241. // If we're actually going to start updating again, we need to reset
  242. // the time so as to ignore any time that passed while we were suspended.
  243. // This method is far from precise, but I'm hoping it's good enough.
  244. if (m_sSuspend == 0)
  245. m_lPrevTime = m_pRealm->m_time.GetGameTime();
  246. }
  247. ////////////////////////////////////////////////////////////////////////////////
  248. // Update object
  249. ////////////////////////////////////////////////////////////////////////////////
  250. void CFirestream::Update(void)
  251. {
  252. long lThisTime;
  253. if (!m_sSuspend)
  254. {
  255. lThisTime = m_pRealm->m_time.GetGameTime();
  256. // See if we killed ourselves
  257. if (ProcessFireballMessages() == State_Deleted)
  258. {
  259. delete this;
  260. return;
  261. }
  262. CFireball* pfireball1 = NULL;
  263. CFireball* pfireball2 = NULL;
  264. CFireball* pfireball3 = NULL;
  265. // Update the other fireballs
  266. if (m_pRealm->m_idbank.GetThingByID((CThing**)&pfireball1, m_idFireball1) == 0)
  267. {
  268. pfireball1->m_dX = m_dX;
  269. pfireball1->m_dY = m_dY;
  270. pfireball1->m_dZ = m_dZ;
  271. pfireball1->m_dRot = rspMod360(m_dRot + RAND_SWAY(FIREBALL_SWAY) );
  272. }
  273. else
  274. {
  275. m_idFireball1 = CIdBank::IdNil;
  276. }
  277. if (m_pRealm->m_idbank.GetThingByID((CThing**)&pfireball2, m_idFireball2) == 0)
  278. {
  279. pfireball2->m_dX = m_dX + COSQ[(short) m_dRot] * ms_sOffset1;
  280. pfireball2->m_dY = m_dY;
  281. pfireball2->m_dZ = m_dZ - SINQ[(short) m_dRot] * ms_sOffset1;
  282. pfireball2->m_dRot = rspMod360(m_dRot + RAND_SWAY(FIREBALL_SWAY) );
  283. }
  284. else
  285. {
  286. m_idFireball2 = CIdBank::IdNil;
  287. }
  288. if (m_pRealm->m_idbank.GetThingByID((CThing**)&pfireball3, m_idFireball3) == 0)
  289. {
  290. pfireball3->m_dX = m_dX + COSQ[(short) m_dRot] * ms_sOffset2;
  291. pfireball3->m_dY = m_dY;
  292. pfireball3->m_dZ = m_dZ - SINQ[(short) m_dRot] * ms_sOffset2;
  293. pfireball3->m_dRot = rspMod360(m_dRot + RAND_SWAY(FIREBALL_SWAY) );
  294. }
  295. else
  296. {
  297. m_idFireball3 = CIdBank::IdNil;
  298. }
  299. if (m_eState == CWeapon::State_Fire)
  300. {
  301. if (pfireball1)
  302. pfireball1->m_eState = CWeapon::State_Fire;
  303. if (pfireball2)
  304. pfireball2->m_eState = CWeapon::State_Fire;
  305. if (pfireball3)
  306. pfireball3->m_eState = CWeapon::State_Fire;
  307. delete this;
  308. return;
  309. }
  310. }
  311. }
  312. ////////////////////////////////////////////////////////////////////////////////
  313. // Render object
  314. ////////////////////////////////////////////////////////////////////////////////
  315. void CFirestream::Render(void)
  316. {
  317. // If we have a parent . . .
  318. if (m_sprite.m_psprParent)
  319. {
  320. // Get outta there!
  321. m_sprite.m_psprParent->RemoveChild(&m_sprite);
  322. }
  323. // This should never ever be rendered.
  324. ASSERT(m_sprite.m_psprParent == NULL);
  325. ASSERT( (m_sprite.m_sInFlags & CSprite::PrivInserted) == 0);
  326. }
  327. ////////////////////////////////////////////////////////////////////////////////
  328. // Setup
  329. ////////////////////////////////////////////////////////////////////////////////
  330. short CFirestream::Setup( // Returns 0 if successfull, non-zero otherwise
  331. short sX, // In: New x coord
  332. short sY, // In: New y coord
  333. short sZ, // In: New z coord
  334. short sDir, // In: Direction of travel
  335. long lTimeToLive, // In: Number of milliseconds to burn, default 1sec
  336. U16 u16ShooterID) // In: Shooter's ID so you don't hit him
  337. {
  338. short sResult = 0;
  339. double dX;
  340. double dZ;
  341. // Use specified position
  342. m_dX = (double)sX;
  343. m_dY = (double)sY;
  344. m_dZ = (double)sZ;
  345. m_dRot = sDir;
  346. m_lPrevTime = m_pRealm->m_time.GetGameTime();
  347. m_u16ShooterID = u16ShooterID;
  348. // Make sure that the starting positions are valid before creating
  349. // fireballs here, otherwise they will shoot through walls.
  350. dX = m_dX + COSQ[(short) m_dRot] * ms_sOffset2; // Second interval
  351. dZ = m_dZ - SINQ[(short) m_dRot] * ms_sOffset2;
  352. if (m_pRealm->IsPathClear( // Returns true, if the entire path is clear.
  353. // Returns false, if only a portion of the path is clear.
  354. // (see *psX, *psY, *psZ).
  355. (short) m_dX, // In: Starting X.
  356. (short) m_dY, // In: Starting Y.
  357. (short) m_dZ, // In: Starting Z.
  358. 3.0, // In: Rate at which to scan ('crawl') path in pixels per
  359. // iteration.
  360. // NOTE: Values less than 1.0 are inefficient.
  361. // NOTE: We scan terrain using GetHeight()
  362. // at only one pixel.
  363. // NOTE: We could change this to a speed in pixels per second
  364. // where we'd assume a certain frame rate.
  365. (short) dX, // In: Destination X.
  366. (short) dZ, // In: Destination Z.
  367. 0, // In: Max traverser can step up.
  368. NULL, // Out: If not NULL, last clear point on path.
  369. NULL, // Out: If not NULL, last clear point on path.
  370. NULL, // Out: If not NULL, last clear point on path.
  371. false) ) // In: If true, will consider the edge of the realm a path
  372. // inhibitor. If false, reaching the edge of the realm
  373. // indicates a clear path.
  374. {
  375. m_lTimeToLive = m_pRealm->m_time.GetGameTime() + lTimeToLive;
  376. CFireball* pfireball;
  377. if (CThing::ConstructWithID(CThing::CFireballID, m_pRealm, (CThing**) &pfireball) == 0)
  378. {
  379. pfireball->Setup(m_dX, m_dY, m_dZ, sDir, lTimeToLive, u16ShooterID);
  380. m_idFireball1 = pfireball->GetInstanceID();
  381. }
  382. dX = m_dX + COSQ[(short) m_dRot] * ms_sOffset1; // First interval
  383. dZ = m_dZ - SINQ[(short) m_dRot] * ms_sOffset1;
  384. if (CThing::ConstructWithID(CThing::CFireballID, m_pRealm, (CThing**) &pfireball) == 0)
  385. {
  386. pfireball->Setup(dX, m_dY, dZ, sDir, lTimeToLive, u16ShooterID);
  387. m_idFireball2 = pfireball->GetInstanceID();
  388. }
  389. dX = m_dX + COSQ[(short) m_dRot] * ms_sOffset2; // Second interval
  390. dZ = m_dZ - SINQ[(short) m_dRot] * ms_sOffset2;
  391. if (CThing::ConstructWithID(CThing::CFireballID, m_pRealm, (CThing**) &pfireball) == 0)
  392. {
  393. pfireball->Setup(dX, m_dY, dZ, sDir, lTimeToLive, u16ShooterID);
  394. m_idFireball3 = pfireball->GetInstanceID();
  395. }
  396. if (sResult == SUCCESS)
  397. sResult = Init();
  398. }
  399. return sResult;
  400. }
  401. ////////////////////////////////////////////////////////////////////////////////
  402. // Init
  403. ////////////////////////////////////////////////////////////////////////////////
  404. short CFirestream::Init(void)
  405. {
  406. short sResult = SUCCESS;
  407. return sResult;
  408. }
  409. ////////////////////////////////////////////////////////////////////////////////
  410. // Called by editor to init new object at specified position
  411. ////////////////////////////////////////////////////////////////////////////////
  412. short CFirestream::EditNew( // Returns 0 if successfull, non-zero otherwise
  413. short sX, // In: New x coord
  414. short sY, // In: New y coord
  415. short sZ) // In: New z coord
  416. {
  417. short sResult = 0;
  418. // Use specified position
  419. m_dX = (double)sX;
  420. m_dY = (double)sY;
  421. m_dZ = (double)sZ;
  422. m_lTimer = GetRand(); //m_pRealm->m_time.GetGameTime() + 1000;
  423. m_lPrevTime = m_pRealm->m_time.GetGameTime();
  424. return sResult;
  425. }
  426. ////////////////////////////////////////////////////////////////////////////////
  427. // Called by editor to modify object
  428. ////////////////////////////////////////////////////////////////////////////////
  429. short CFirestream::EditModify(void)
  430. {
  431. return 0;
  432. }
  433. ////////////////////////////////////////////////////////////////////////////////
  434. // Called by editor to move object to specified position
  435. ////////////////////////////////////////////////////////////////////////////////
  436. short CFirestream::EditMove( // Returns 0 if successfull, non-zero otherwise
  437. short sX, // In: New x coord
  438. short sY, // In: New y coord
  439. short sZ) // In: New z coord
  440. {
  441. m_dX = (double)sX;
  442. m_dY = (double)sY;
  443. m_dZ = (double)sZ;
  444. return 0;
  445. }
  446. ////////////////////////////////////////////////////////////////////////////////
  447. // Called by editor to update object
  448. ////////////////////////////////////////////////////////////////////////////////
  449. void CFirestream::EditUpdate(void)
  450. {
  451. }
  452. ////////////////////////////////////////////////////////////////////////////////
  453. // Called by editor to render object
  454. ////////////////////////////////////////////////////////////////////////////////
  455. void CFirestream::EditRender(void)
  456. {
  457. // In some cases, object's might need to do a special-case render in edit
  458. // mode because Startup() isn't called. In this case it doesn't matter, so
  459. // we can call the normal Render().
  460. Render();
  461. }
  462. ////////////////////////////////////////////////////////////////////////////////
  463. // Preload - basically trick the resource manager into caching resources for fire
  464. // animations before play begins so that when a fire is set for
  465. // the first time, there won't be a delay while it loads.
  466. ////////////////////////////////////////////////////////////////////////////////
  467. short CFirestream::Preload(
  468. CRealm* /*prealm*/) // In: Calling realm.
  469. {
  470. return 0;
  471. }
  472. ////////////////////////////////////////////////////////////////////////////////
  473. // ProcessFireballMessages
  474. ////////////////////////////////////////////////////////////////////////////////
  475. CFirestream::CFirestreamState CFirestream::ProcessFireballMessages(void)
  476. {
  477. CFirestreamState eNewState = State_Idle;
  478. GameMessage msg;
  479. if (m_MessageQueue.DeQ(&msg) == true)
  480. {
  481. switch(msg.msg_Generic.eType)
  482. {
  483. case typeObjectDelete:
  484. {
  485. // Pass the message on . . .
  486. SendThingMessage(&msg, m_idFireball1);
  487. SendThingMessage(&msg, m_idFireball2);
  488. SendThingMessage(&msg, m_idFireball3);
  489. m_MessageQueue.Empty();
  490. return State_Deleted;
  491. break;
  492. }
  493. }
  494. }
  495. // Dump the rest of the messages
  496. m_MessageQueue.Empty();
  497. return eNewState;
  498. }
  499. ////////////////////////////////// Fireball ////////////////////////////////////
  500. ////////////////////////////////////////////////////////////////////////////////
  501. // Macros/types/etc.
  502. ////////////////////////////////////////////////////////////////////////////////
  503. #define SMALL_FILE "tinyfire.aan"
  504. ////////////////////////////////////////////////////////////////////////////////
  505. // Variables/data
  506. ////////////////////////////////////////////////////////////////////////////////
  507. // Let this auto-init to 0
  508. short CFireball::ms_sFileCount;
  509. short CFireball::ms_sSmallRadius = 8;
  510. long CFireball::ms_lCollisionTime = 250; // Check for collisions this often
  511. double CFireball::ms_dFireVelocity = 300;
  512. ////////////////////////////////////////////////////////////////////////////////
  513. // Load object (should call base class version!)
  514. ////////////////////////////////////////////////////////////////////////////////
  515. short CFireball::Load( // Returns 0 if successfull, non-zero otherwise
  516. RFile* pFile, // In: File to load from
  517. bool bEditMode, // In: True for edit mode, false otherwise
  518. short sFileCount, // In: File count (unique per file, never 0)
  519. ULONG ulFileVersion) // In: Version of file format to load.
  520. {
  521. short sResult = CThing::Load(pFile, bEditMode, sFileCount, ulFileVersion);
  522. if (sResult == 0)
  523. {
  524. // Load common data just once per file (not with each object)
  525. if (ms_sFileCount != sFileCount)
  526. {
  527. ms_sFileCount = sFileCount;
  528. // Load static data.
  529. switch (ulFileVersion)
  530. {
  531. default:
  532. case 1:
  533. break;
  534. }
  535. }
  536. // Load instance data.
  537. switch (ulFileVersion)
  538. {
  539. default:
  540. case 1:
  541. break;
  542. }
  543. // Make sure there were no file errors or format errors . . .
  544. if (!pFile->Error() && sResult == 0)
  545. {
  546. // Get resources
  547. sResult = GetResources();
  548. }
  549. else
  550. {
  551. sResult = -1;
  552. TRACE("CFireball::Load(): Error reading from file!\n");
  553. }
  554. }
  555. else
  556. {
  557. TRACE("CFireball::Load(): CThing::Load() failed.\n");
  558. }
  559. return sResult;
  560. }
  561. ////////////////////////////////////////////////////////////////////////////////
  562. // Save object (should call base class version!)
  563. ////////////////////////////////////////////////////////////////////////////////
  564. short CFireball::Save( // Returns 0 if successfull, non-zero otherwise
  565. RFile* pFile, // In: File to save to
  566. short sFileCount) // In: File count (unique per file, never 0)
  567. {
  568. short sResult = CThing::Save(pFile, sFileCount);
  569. if (sResult == 0)
  570. {
  571. // Save common data just once per file (not with each object)
  572. if (ms_sFileCount != sFileCount)
  573. {
  574. ms_sFileCount = sFileCount;
  575. // Save static data
  576. }
  577. }
  578. else
  579. {
  580. TRACE("CFireball::Save(): CThing::Save() failed.\n");
  581. }
  582. return sResult;
  583. }
  584. ////////////////////////////////////////////////////////////////////////////////
  585. // Startup object
  586. ////////////////////////////////////////////////////////////////////////////////
  587. short CFireball::Startup(void) // Returns 0 if successfull, non-zero otherwise
  588. {
  589. return Init();
  590. }
  591. ////////////////////////////////////////////////////////////////////////////////
  592. // Shutdown object
  593. ////////////////////////////////////////////////////////////////////////////////
  594. short CFireball::Shutdown(void) // Returns 0 if successfull, non-zero otherwise
  595. {
  596. return 0;
  597. }
  598. ////////////////////////////////////////////////////////////////////////////////
  599. // Suspend object
  600. ////////////////////////////////////////////////////////////////////////////////
  601. void CFireball::Suspend(void)
  602. {
  603. m_sSuspend++;
  604. }
  605. ////////////////////////////////////////////////////////////////////////////////
  606. // Resume object
  607. ////////////////////////////////////////////////////////////////////////////////
  608. void CFireball::Resume(void)
  609. {
  610. m_sSuspend--;
  611. // If we're actually going to start updating again, we need to reset
  612. // the time so as to ignore any time that passed while we were suspended.
  613. // This method is far from precise, but I'm hoping it's good enough.
  614. if (m_sSuspend == 0)
  615. m_lPrevTime = m_pRealm->m_time.GetGameTime();
  616. }
  617. ////////////////////////////////////////////////////////////////////////////////
  618. // Update object
  619. ////////////////////////////////////////////////////////////////////////////////
  620. void CFireball::Update(void)
  621. {
  622. long lThisTime;
  623. double dSeconds;
  624. double dDistance;
  625. double dNewX;
  626. double dNewZ;
  627. if (!m_sSuspend)
  628. {
  629. lThisTime = m_pRealm->m_time.GetGameTime();
  630. m_lAnimTime += lThisTime - m_lPrevTime;
  631. // See if we killed ourselves
  632. if (ProcessFireballMessages() == State_Deleted)
  633. {
  634. delete this;
  635. return;
  636. }
  637. switch (m_eState)
  638. {
  639. case State_Fire:
  640. if (lThisTime < m_lTimeToLive)
  641. {
  642. if (m_bMoving)
  643. {
  644. // Update position using wind direction and velocity
  645. dSeconds = ((double) lThisTime - (double) m_lPrevTime) / 1000.0;
  646. // Apply internal velocity.
  647. dDistance = m_dHorizVel * dSeconds;
  648. dNewX = m_dX + COSQ[(short) m_dRot] * dDistance;
  649. dNewZ = m_dZ - SINQ[(short) m_dRot] * dDistance;
  650. // Check attribute map for walls, and if you hit a wall,
  651. // set the timer so you will die off next time around.
  652. short sHeight = m_pRealm->GetHeight((short) dNewX, (short) dNewZ);
  653. // If it hits a wall taller than itself, then it will rotate in the
  654. // predetermined direction until it is free to move.
  655. if ((short) m_dY < sHeight ||
  656. !m_pRealm->IsPathClear( // Returns true, if the entire path is clear.
  657. // Returns false, if only a portion of the path is clear.
  658. // (see *psX, *psY, *psZ).
  659. (short) m_dX, // In: Starting X.
  660. (short) m_dY, // In: Starting Y.
  661. (short) m_dZ, // In: Starting Z.
  662. 3.0, // In: Rate at which to scan ('crawl') path in pixels per
  663. // iteration.
  664. // NOTE: Values less than 1.0 are inefficient.
  665. // NOTE: We scan terrain using GetHeight()
  666. // at only one pixel.
  667. // NOTE: We could change this to a speed in pixels per second
  668. // where we'd assume a certain frame rate.
  669. (short) dNewX, // In: Destination X.
  670. (short) dNewZ, // In: Destination Z.
  671. 0, // In: Max traverser can step up.
  672. NULL, // Out: If not NULL, last clear point on path.
  673. NULL, // Out: If not NULL, last clear point on path.
  674. NULL, // Out: If not NULL, last clear point on path.
  675. false) // In: If true, will consider the edge of the realm a path
  676. // inhibitor. If false, reaching the edge of the realm
  677. // indicates a clear path.
  678. )
  679. {
  680. // Stop moving and fix yourself on a random spot on the wall.
  681. m_bMoving = false;
  682. m_dX += (-3 + GetRand() % 7);
  683. m_dY += (-3 + GetRand() % 7);
  684. m_dZ += (-3 + GetRand() % 7);
  685. // Update sphere
  686. m_smash.m_sphere.sphere.X = m_dX;
  687. m_smash.m_sphere.sphere.Y = m_dY;
  688. m_smash.m_sphere.sphere.Z = m_dZ;
  689. }
  690. else
  691. {
  692. m_dX = dNewX;
  693. m_dZ = dNewZ;
  694. // Update sphere
  695. m_smash.m_sphere.sphere.X = m_dX;
  696. m_smash.m_sphere.sphere.Y = m_dY;
  697. m_smash.m_sphere.sphere.Z = m_dZ;
  698. // Check for collisions
  699. CSmash* pSmashed = NULL;
  700. GameMessage msg;
  701. msg.msg_Burn.eType = typeBurn;
  702. msg.msg_Burn.sPriority = 0;
  703. msg.msg_Burn.sDamage = 10;
  704. msg.msg_Burn.u16ShooterID = m_u16ShooterID;
  705. m_pRealm->m_smashatorium.QuickCheckReset(&m_smash, m_u32CollideIncludeBits,
  706. m_u32CollideDontcareBits,
  707. m_u32CollideExcludeBits);
  708. while (m_pRealm->m_smashatorium.QuickCheckNext(&pSmashed))
  709. if (pSmashed->m_pThing->GetInstanceID() != m_u16ShooterID)
  710. SendThingMessage(&msg, pSmashed->m_pThing);
  711. }
  712. }
  713. }
  714. else
  715. {
  716. delete this;
  717. return;
  718. }
  719. break;
  720. default:
  721. break;
  722. }
  723. m_lPrevTime = lThisTime;
  724. }
  725. }
  726. ////////////////////////////////////////////////////////////////////////////////
  727. // Render object
  728. ////////////////////////////////////////////////////////////////////////////////
  729. void CFireball::Render(void)
  730. {
  731. CAlphaAnim* pAnim;
  732. pAnim = (CAlphaAnim*) m_pAnimChannel->GetAtTime(m_lAnimTime % m_pAnimChannel->TotalTime());
  733. if (pAnim) // && m_sCurrentAlphaChannel >= 0)
  734. {
  735. // No special flags
  736. m_sprite.m_sInFlags = 0;
  737. // Map from 3d to 2d coords
  738. Map3Dto2D(m_dX, m_dY, m_dZ, &(m_sprite.m_sX2), &(m_sprite.m_sY2) );
  739. // Offset by animations 2D offsets.
  740. m_sprite.m_sX2 += pAnim->m_sX;
  741. m_sprite.m_sY2 += pAnim->m_sY;
  742. // Priority is based on our Z position.
  743. m_sprite.m_sPriority = m_dZ;
  744. // Layer should be based on info we get from attribute map.
  745. m_sprite.m_sLayer = CRealm::GetLayerViaAttrib(m_pRealm->GetLayer((short) m_dX, (short) m_dZ));
  746. // m_sprite.m_sAlphaLevel = 200;
  747. if (m_lTotalFlameTime == 0)
  748. {
  749. // Safety.
  750. m_lTotalFlameTime = 500;
  751. }
  752. // m_sprite.m_sAlphaLevel = MIN((long)255, (long) (((m_lTimeToLive - m_pRealm->m_time.GetGameTime()) / m_lTotalFlameTime) * 255));
  753. // Copy the color info and the alpha channel to the Alpha Sprite
  754. m_sprite.m_pImage = &(pAnim->m_imColor);
  755. // Now there is only 1 alpha mask.
  756. m_sCurrentAlphaChannel = 0; //MIN(m_sCurrentAlphaChannel, (short) (m_sTotalAlphaChannels - 1));
  757. m_sprite.m_pimAlpha = &(pAnim->m_pimAlphaArray[0]);
  758. // Adjust level between 0 and max so it gets more opaque with time.
  759. m_sprite.m_sAlphaLevel = MIN_ALPHA + MAX_ALPHA - (MAX_ALPHA * (m_lTimeToLive - m_pRealm->m_time.GetGameTime()) ) / m_lTotalFlameTime ;
  760. // Keep in range.
  761. if (m_sprite.m_sAlphaLevel < 0)
  762. m_sprite.m_sAlphaLevel = 0;
  763. else if (m_sprite.m_sAlphaLevel > MAX_ALPHA)
  764. m_sprite.m_sAlphaLevel = MAX_ALPHA;
  765. // Update sprite in scene
  766. m_pRealm->m_scene.UpdateSprite(&m_sprite);
  767. }
  768. }
  769. ////////////////////////////////////////////////////////////////////////////////
  770. // Setup
  771. ////////////////////////////////////////////////////////////////////////////////
  772. short CFireball::Setup( // Returns 0 if successfull, non-zero otherwise
  773. short sX, // In: New x coord
  774. short sY, // In: New y coord
  775. short sZ, // In: New z coord
  776. short sDir, // In: Direction of travel
  777. long lTimeToLive, // In: Number of milliseconds to burn, default 1sec
  778. U16 u16ShooterID) // In: Shooter's ID so you don't hit him
  779. {
  780. short sResult = 0;
  781. // Use specified position
  782. m_dX = (double)sX;
  783. m_dY = (double)sY;
  784. m_dZ = (double)sZ;
  785. m_dRot = sDir;
  786. m_lPrevTime = m_pRealm->m_time.GetGameTime();
  787. m_lCollisionTimer = m_lPrevTime + ms_lCollisionTime;
  788. m_u16ShooterID = u16ShooterID;
  789. m_lAnimTime = GetRandom();
  790. m_dHorizVel = ms_dFireVelocity;
  791. m_lTotalFlameTime = lTimeToLive;
  792. m_lTimeToLive = m_pRealm->m_time.GetGameTime() + lTimeToLive;
  793. m_sCurrentAlphaChannel = 0;
  794. // Load resources
  795. sResult = GetResources();
  796. if (sResult == SUCCESS)
  797. sResult = Init();
  798. return sResult;
  799. }
  800. ////////////////////////////////////////////////////////////////////////////////
  801. // Init
  802. ////////////////////////////////////////////////////////////////////////////////
  803. short CFireball::Init(void)
  804. {
  805. short sResult = SUCCESS;
  806. CAlphaAnim* pAnim = NULL;
  807. // Update sphere
  808. m_smash.m_sphere.sphere.X = m_dX;
  809. m_smash.m_sphere.sphere.Y = m_dY;
  810. m_smash.m_sphere.sphere.Z = m_dZ;
  811. m_smash.m_sphere.sphere.lRadius = ms_sSmallRadius;
  812. m_smash.m_bits = CSmash::Fire;
  813. m_smash.m_pThing = this;
  814. // Update the smash
  815. m_pRealm->m_smashatorium.Update(&m_smash);
  816. m_eState = CWeapon::State_Idle;
  817. // Set the collision bits
  818. m_u32CollideIncludeBits = CSmash::Character | CSmash::Barrel | CSmash::Mine | CSmash::Misc;
  819. m_u32CollideDontcareBits = CSmash::Good | CSmash::Bad;
  820. m_u32CollideExcludeBits = 0;
  821. return sResult;
  822. }
  823. ////////////////////////////////////////////////////////////////////////////////
  824. // Called by editor to init new object at specified position
  825. ////////////////////////////////////////////////////////////////////////////////
  826. short CFireball::EditNew( // Returns 0 if successfull, non-zero otherwise
  827. short sX, // In: New x coord
  828. short sY, // In: New y coord
  829. short sZ) // In: New z coord
  830. {
  831. short sResult = 0;
  832. // Use specified position
  833. m_dX = (double)sX;
  834. m_dY = (double)sY;
  835. m_dZ = (double)sZ;
  836. m_lTimer = GetRand(); //m_pRealm->m_time.GetGameTime() + 1000;
  837. m_lPrevTime = m_pRealm->m_time.GetGameTime();
  838. // Load resources
  839. sResult = GetResources();
  840. return sResult;
  841. }
  842. ////////////////////////////////////////////////////////////////////////////////
  843. // Called by editor to modify object
  844. ////////////////////////////////////////////////////////////////////////////////
  845. short CFireball::EditModify(void)
  846. {
  847. return 0;
  848. }
  849. ////////////////////////////////////////////////////////////////////////////////
  850. // Called by editor to move object to specified position
  851. ////////////////////////////////////////////////////////////////////////////////
  852. short CFireball::EditMove( // Returns 0 if successfull, non-zero otherwise
  853. short sX, // In: New x coord
  854. short sY, // In: New y coord
  855. short sZ) // In: New z coord
  856. {
  857. m_dX = (double)sX;
  858. m_dY = (double)sY;
  859. m_dZ = (double)sZ;
  860. return 0;
  861. }
  862. ////////////////////////////////////////////////////////////////////////////////
  863. // Called by editor to update object
  864. ////////////////////////////////////////////////////////////////////////////////
  865. void CFireball::EditUpdate(void)
  866. {
  867. }
  868. ////////////////////////////////////////////////////////////////////////////////
  869. // Called by editor to render object
  870. ////////////////////////////////////////////////////////////////////////////////
  871. void CFireball::EditRender(void)
  872. {
  873. // In some cases, object's might need to do a special-case render in edit
  874. // mode because Startup() isn't called. In this case it doesn't matter, so
  875. // we can call the normal Render().
  876. Render();
  877. }
  878. ////////////////////////////////////////////////////////////////////////////////
  879. // Get all required resources
  880. ////////////////////////////////////////////////////////////////////////////////
  881. short CFireball::GetResources(void) // Returns 0 if successfull, non-zero otherwise
  882. {
  883. short sResult = SUCCESS;
  884. sResult = rspGetResource(&g_resmgrGame, m_pRealm->Make2dResPath(SMALL_FILE), &m_pAnimChannel, RFile::LittleEndian);
  885. if (sResult != 0)
  886. TRACE("CFireball::GetResources - Error getting fire animation resource\n");
  887. return sResult;
  888. }
  889. ////////////////////////////////////////////////////////////////////////////////
  890. // Free all resources
  891. ////////////////////////////////////////////////////////////////////////////////
  892. short CFireball::FreeResources(void) // Returns 0 if successfull, non-zero otherwise
  893. {
  894. short sResult = 0;
  895. rspReleaseResource(&g_resmgrGame, &m_pAnimChannel);
  896. return sResult;
  897. }
  898. ////////////////////////////////////////////////////////////////////////////////
  899. // Preload - basically trick the resource manager into caching resources for fire
  900. // animations before play begins so that when a fire is set for
  901. // the first time, there won't be a delay while it loads.
  902. ////////////////////////////////////////////////////////////////////////////////
  903. short CFireball::Preload(
  904. CRealm* prealm) // In: Calling realm.
  905. {
  906. short sResult;
  907. ChannelAA* pRes;
  908. sResult = rspGetResource(&g_resmgrGame, prealm->Make2dResPath(SMALL_FILE), &pRes, RFile::LittleEndian);
  909. rspReleaseResource(&g_resmgrGame, &pRes);
  910. return sResult;
  911. }
  912. ////////////////////////////////////////////////////////////////////////////////
  913. // ProcessFireballMessages
  914. ////////////////////////////////////////////////////////////////////////////////
  915. CFireball::CFireballState CFireball::ProcessFireballMessages(void)
  916. {
  917. CFireballState eNewState = State_Idle;
  918. GameMessage msg;
  919. if (m_MessageQueue.DeQ(&msg) == true)
  920. {
  921. switch(msg.msg_Generic.eType)
  922. {
  923. case typeObjectDelete:
  924. m_MessageQueue.Empty();
  925. return State_Deleted;
  926. break;
  927. }
  928. }
  929. // Dump the rest of the messages
  930. m_MessageQueue.Empty();
  931. return eNewState;
  932. }
  933. ////////////////////////////////////////////////////////////////////////////////
  934. // EOF
  935. ////////////////////////////////////////////////////////////////////////////////