bulletFest.cpp 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022
  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. // bulletFest.CPP
  19. // Project: Postal
  20. //
  21. // History:
  22. // 02/18/97 JMI Started.
  23. //
  24. // 02/19/97 JMI Now Ricochet(), Impact(), and Flare() create animations.
  25. //
  26. // 02/19/97 JMI Now Fire() and FireDeluxe() take all 3 CSmash masks.
  27. //
  28. // 02/19/97 JMI FireDeluxe(), forces impact, if terrain contact is off
  29. // of realm.
  30. //
  31. // 02/27/97 JMI In progress.
  32. //
  33. // 02/27/97 JMI Experimented with various ways of determining the terrain
  34. // angle but it is very difficult considering the attribute
  35. // maps granularity is 8!
  36. //
  37. // 02/27/97 JMI Forgot to remove feedback flag to preprocessor.
  38. //
  39. // 02/27/97 JMI Fongoolishness.
  40. //
  41. // 03/03/97 JMI Now uses QuickCheckClosest() instead of QuickCheck().
  42. //
  43. // 03/05/97 JMI Fixed rspMod360() usage to current.
  44. //
  45. // 03/10/97 JMI Added unimplemented UpdateTarget() stub.
  46. //
  47. // 03/21/97 JMI Added optional tracers.
  48. //
  49. // 03/21/97 BRH Fixed a small divide by zero error check.
  50. //
  51. // 04/02/97 JMI Changed ms_ati to m_ati.
  52. // Also, added ms_u8TracerIndex.
  53. // Also, removed all the edge info crap.
  54. // Also, added two additional ways of displaying tracers.
  55. //
  56. // 04/02/97 JMI Feedback now uses CSpriteLine2d.
  57. // Also, now uses mapping using 4 clusters &'d together
  58. // as an index to determine the terrain angle.
  59. // Does not seem to work.
  60. //
  61. // 04/02/97 JMI Switched back to METHOD 2 and fixed a divide by zero bug
  62. // in there. Also, reduced TRACER_MAX_LENGTH to 3.
  63. //
  64. // 04/10/97 BRH Updated this to work with the new multi layer attribute
  65. // maps.
  66. //
  67. // 04/24/97 JMI Added smid parameter specifying type of ammo noise to
  68. // FireDeluxe() and Flare().
  69. //
  70. // 04/29/97 JMI Fire() now allows bullets to go 10 pixels off the realm
  71. // to make it appear as if the edge of the realm has no
  72. // affect on them (except the bottom edge which is greater
  73. // so that, if the bullet is exceptionally high, it will
  74. // go, hopefully, entirely off screen).
  75. //
  76. // 05/29/97 JMI Removed references to m_pRealm->m_pAttribMap which no longer
  77. // exists.
  78. //
  79. // 06/11/97 JMI Added Preload() for loading assets used during play.
  80. //
  81. // 06/17/97 JMI Converted all occurrences of rand() to GetRand() and
  82. // srand() to SeedRand().
  83. //
  84. // 06/26/97 JMI Now uses pRealm->Map3Dto2D() in Fire() (was using
  85. // the global version defined in reality.h which is no more).
  86. //
  87. // 06/28/97 JMI Missed some Map3Dto2D()'s that were #if 0'ed out.
  88. //
  89. // 06/30/97 JMI Now uses CRealm's new GetRealmWidth() and *Height()
  90. // for dimensions of realm's X/Z plane.
  91. //
  92. // 07/01/97 JMI Now bases ricochets on GetRandom().
  93. //
  94. // 07/09/97 JMI Now uses m_pRealm->Make2dResPath() to get the fullpath
  95. // for 2D image components.
  96. //
  97. // 07/09/97 JMI Changed Preload() to take a pointer to the calling realm
  98. // as a parameter.
  99. //
  100. // 07/18/97 JMI Got rid of bogus immitation PlaySample functions.
  101. // Now there is one PlaySample() function. Also, you now
  102. // MUST specify a category and you don't have to specify a
  103. // SoundInstance ptr to specify a volume.
  104. //
  105. // 07/27/97 JMI No longer calls CRealm::Make2dResPath() on res names
  106. // given to CAnimThing as it already does that
  107. // automatically.
  108. //
  109. // 08/20/97 BRH Changed ricochet sounds to Weapon volume control
  110. // instead of destruction.
  111. //
  112. // 09/18/97 JMI UpdateTracerColor() now takes a realm ptr and now is
  113. // color mapped on the first Fire() call.
  114. //
  115. //////////////////////////////////////////////////////////////////////////////
  116. //
  117. // bulletFest handles launching of bullets. Currently it doesn't seem like
  118. // much more than a function call, but eventually it will need to keep track
  119. // of some per instance data.
  120. //
  121. //////////////////////////////////////////////////////////////////////////////
  122. //////////////////////////////////////////////////////////////////////////////
  123. // C Headers -- Must be included before RSPiX.h b/c RSPiX utilizes SHMalloc.
  124. //////////////////////////////////////////////////////////////////////////////
  125. ///////////////////////////////////////////////////////////////////////////////
  126. // RSPiX Headers.
  127. ///////////////////////////////////////////////////////////////////////////////
  128. #include "RSPiX.h"
  129. //////////////////////////////////////////////////////////////////////////////
  130. // Postal headers.
  131. //////////////////////////////////////////////////////////////////////////////
  132. #include "bulletFest.h"
  133. #include "AnimThing.h"
  134. #include "SampleMaster.h"
  135. #include "reality.h"
  136. //////////////////////////////////////////////////////////////////////////////
  137. // Module specific macros.
  138. //////////////////////////////////////////////////////////////////////////////
  139. #define IMPACT_RES_NAME "Ricochet.aan"
  140. #define RICOCHET_RES_NAME "Ricochet.aan"
  141. #define FLARE_RES_NAME "MuzzleFlare.aan"
  142. #define TERRAIN_CHECK_DIST 1
  143. #define TERRAIN_CHECK_GRANULARITY 4
  144. #define TRACER_COLOR_RED 250
  145. #define TRACER_COLOR_GREEN 200
  146. #define TRACER_COLOR_BLUE 50
  147. // The color range that is the same on all hoods.
  148. #define CONSTANT_COLOR_START_INDEX 95
  149. #define NUM_CONSTANT_COLOR_INDICES (256 - CONSTANT_COLOR_START_INDEX - 10)
  150. #define TRACER_WRAP_DISTANCE 10
  151. #define TRACER_MAX_LENGTH 3
  152. #define LEFT_TOP 0x1
  153. #define RIGHT_TOP 0x2
  154. #define LEFT_BOTTOM 0x4
  155. #define RIGHT_BOTTOM 0x8
  156. // This is done as a mask instead of a mod for speed.
  157. // Note that 0xFFFF is 100% of the time, 0x00FF is 50%, etc.
  158. // Chances are number of set bits out of 16.
  159. #define RICOCHET_CHANCE_MASK 0x0001
  160. #define BULLET_SND_HALF_LIFE 1000
  161. //////////////////////////////////////////////////////////////////////////////
  162. // Module specific typedefs.
  163. //////////////////////////////////////////////////////////////////////////////
  164. //////////////////////////////////////////////////////////////////////////////
  165. // Exported (extern) variables.
  166. //////////////////////////////////////////////////////////////////////////////
  167. //////////////////////////////////////////////////////////////////////////////
  168. // Module specific (static) variables / Instantiate class statics.
  169. //////////////////////////////////////////////////////////////////////////////
  170. U8 CBulletFest::ms_u8TracerIndex = 0; // The color index to use for tracers.
  171. // This value is gotten only once per
  172. // execution of this program; therefore,
  173. // it is gotten from the group of colors
  174. // that don't change from hood to hood.
  175. // Maps a combination of bits to an angle.
  176. // Each bit set to 1 indicates the terrain in that location is higher than
  177. // the specified height.
  178. // Less than 2 bits set is considered not enough info.
  179. // 2 diagonally adjacent bits is considered not enough info.
  180. static short ms_asTerrainAngleMap[16] =
  181. {
  182. -1, // 0000 ==
  183. -1, // 0001 == LEFT_TOP
  184. -1, // 0010 == RIGHT_TOP
  185. 0, // 0011 == RIGHT_TOP | LEFT_TOP
  186. -1, // 0100 == LEFT_BOTTOM
  187. 90, // 0101 == LEFT_BOTTOM | LEFT_TOP
  188. -1, // 0110 == LEFT_BOTTOM | RIGHT_TOP
  189. 45, // 0111 == LEFT_BOTTOM | RIGHT_TOP | LEFT_TOP
  190. -1, // 1000 == RIGHT_BOTTOM
  191. -1, // 1001 == RIGHT_BOTTOM | LEFT_TOP
  192. 270, // 1010 == RIGHT_BOTTOM | RIGHT_TOP
  193. 315, // 1011 == RIGHT_BOTTOM | RIGHT_TOP | LEFT_TOP
  194. 180, // 1100 == RIGHT_BOTTOM | LEFT_BOTTOM
  195. 135, // 1101 == RIGHT_BOTTOM | LEFT_BOTTOM | LEFT_TOP
  196. 225, // 1110 == RIGHT_BOTTOM | LEFT_BOTTOM | RIGHT_TOP
  197. -1, // 1111 == RIGHT_BOTTOM | LEFT_BOTTOM | RIGHT_TOP | LEFT_TOP
  198. };
  199. //////////////////////////////////////////////////////////////////////////////
  200. // Module specific (static) protos.
  201. //////////////////////////////////////////////////////////////////////////////
  202. //////////////////////////////////////////////////////////////////////////////
  203. // Functions.
  204. //////////////////////////////////////////////////////////////////////////////
  205. //////////////////////////////////////////////////////////////////////////////
  206. // Get max height in a rectangle.
  207. //////////////////////////////////////////////////////////////////////////////
  208. inline
  209. short GetMaxHeight(
  210. CRealm* pRealm, // In: Realm in question.
  211. short sX, // In: X position on terrain.
  212. short sZ, // In: Z position on terrain.
  213. short sW, // In: Amount in X direction.
  214. short sH) // In: Amount in Z direction.
  215. {
  216. short sX2 = sX + sW;
  217. short sZ2 = sZ + sH;
  218. short sIterX;
  219. short sIterZ;
  220. short sMaxHeight = -32767;
  221. for (sIterZ = sZ; sIterZ < sZ2; sIterZ++)
  222. {
  223. for (sIterX = sX; sIterX < sX2; sIterX++)
  224. {
  225. // Relying on NON-macro MAX (macro MAX would evaluate one of the paramters
  226. // twice).
  227. sMaxHeight = MAX(sMaxHeight, pRealm->GetHeight(sIterX, sIterZ));
  228. #if 0
  229. // FEEDBACK.
  230. // Create a line sprite.
  231. CSpriteLine2d* psl2d = new CSpriteLine2d;
  232. if (psl2d != NULL)
  233. {
  234. psl2d->m_sX2 = sIterX,
  235. psl2d->m_sY2 = sIterZ;
  236. psl2d->m_sX2End = sIterX + 1;
  237. psl2d->m_sY2End = sIterZ + 1;
  238. psl2d->m_sPriority = 0;
  239. psl2d->m_sLayer = CRealm::GetLayerViaAttrib(m_pRealm->GetLayer(sIterX, sIterZ));
  240. psl2d->m_u8Color = 250;
  241. // Destroy when done.
  242. psl2d->m_sInFlags = CSprite::InDeleteOnRender;
  243. // Put 'er there.
  244. pRealm->m_scene.UpdateSprite(psl2d);
  245. }
  246. #endif
  247. }
  248. }
  249. return sMaxHeight;
  250. }
  251. //////////////////////////////////////////////////////////////////////////////
  252. //
  253. // Take a point inside terrain and find our way out via X/Z plane.
  254. // Once out, scan along terrain to determine the angle of the terrain that
  255. // exceeds sY in height.
  256. // ASSUMPTION: sX, sY, sZ is a point inside terrain.
  257. //
  258. //////////////////////////////////////////////////////////////////////////////
  259. inline
  260. bool GetTerrainAngle( // true, if terrain angle determined.
  261. // false, if not enough iterations available to
  262. // determine angle.
  263. CRealm* pRealm, // In: Realm in question.
  264. short sX, // In: X position on terrain.
  265. short sY, // In: Y position on terrain.
  266. short sZ, // In: Z position on terrain.
  267. short sMaxOutIterations, // In: Max iterations to get out of terrain
  268. // (i.e., find terrain edge).
  269. short sNumScanIterations, // In: Num iterations to scan edge of terrain
  270. // in EACH direction.
  271. short sInAngle, // In: Angle from which object entered terrain.
  272. // This angle + 180 is used to scan out.
  273. short* psAngle) // Out: Angle of terrain at specified location
  274. // on X/Z plane.
  275. {
  276. bool bFoundAngle = false; // Assume not found.
  277. #if 0 // NOT WORKING CORRECTLY.
  278. ////////////////////// Get out of terrain /////////////////////////////////
  279. sInAngle = rspMod360(sInAngle);
  280. // Determine rate to get in deeper.
  281. float fInRateX = COSQ[sInAngle];
  282. float fInRateZ = -SINQ[sInAngle];
  283. // Get angles to get out.
  284. short sOutAngle = rspMod360(90 + sInAngle); // 180 + sInAngle - 90
  285. // Determine proper rate to get out of terrain.
  286. float fRateX = COSQ[sOutAngle]/* * pRealm->m_pAttribMap->m_sScaleX*/;
  287. float fRateZ = -SINQ[sOutAngle]/* * pRealm->m_pAttribMap->m_sScaleY*/;
  288. const float fInMult = (sMaxOutIterations - 3);
  289. // Set initial position inside terrain.
  290. float fPosX = sX + fInRateX * fInMult;
  291. float fPosZ = sZ + fInRateZ * fInMult;
  292. // Store extents.
  293. short sMaxX = pRealm->GetRealmWidth();
  294. short sMaxZ = pRealm->GetRealmHeight();
  295. short sIterations = 0;
  296. // Quicker access to attribute map for loop.
  297. RAttributeMap* pattrib = pRealm->m_pAttribMap;
  298. short sTerrainH;
  299. bool bGotOut = false;
  300. // Scan while in realm.
  301. while (
  302. fPosX > 0.0
  303. && fPosZ > 0.0
  304. && fPosX < sMaxX
  305. && fPosZ < sMaxZ
  306. && sIterations < sMaxOutIterations)
  307. {
  308. sTerrainH = m_pRealm->GetHeight((short) fPosX, (short) fPosZ);
  309. // If above terrain . . .
  310. if (sY > sTerrainH)
  311. {
  312. bGotOut = true;
  313. // fPosX -= fRateX;
  314. // fPosZ -= fRateZ;
  315. break;
  316. }
  317. fPosX += fRateX;
  318. fPosZ += fRateZ;
  319. sIterations++;
  320. }
  321. // If we did not find our way out . . .
  322. if (bGotOut == false)
  323. {
  324. sOutAngle = rspMod360(270 + sInAngle); // 180 + sInAngle + 90
  325. fRateX = COSQ[sOutAngle]/* * pRealm->m_pAttribMap->m_sScaleX*/;
  326. fRateZ = -SINQ[sOutAngle]/* * pRealm->m_pAttribMap->m_sScaleY*/;
  327. fPosX = sX + fInRateX * fInMult;
  328. fPosZ = sZ + fInRateZ * fInMult;
  329. sIterations = 0;
  330. // Scan while in realm.
  331. while (
  332. fPosX > 0.0
  333. && fPosZ > 0.0
  334. && fPosX < sMaxX
  335. && fPosZ < sMaxZ
  336. && sIterations < sMaxOutIterations)
  337. {
  338. sTerrainH = m_pRealm->GetHeight((short) fPosX, (short) fPosZ);
  339. // If above terrain . . .
  340. if (sY > sTerrainH)
  341. {
  342. bGotOut = true;
  343. // fPosX -= fRateX;
  344. // fPosZ -= fRateZ;
  345. break;
  346. }
  347. fPosX += fRateX;
  348. fPosZ += fRateZ;
  349. sIterations++;
  350. }
  351. }
  352. // If we found our way out in either case . . .
  353. if (bGotOut == true)
  354. {
  355. // Determine angle of a line between the two points.
  356. short sDeltaX = fPosX - (sX - fInRateX);
  357. short sDeltaZ = (sZ - fInRateZ) - fPosZ;
  358. *psAngle = rspATan(sDeltaX, sDeltaZ);
  359. // Found angle.
  360. bFoundAngle = true;
  361. }
  362. #else
  363. // Check 4 surrounding points.
  364. short sIndex = 0;
  365. if (GetMaxHeight(pRealm, sX - TERRAIN_CHECK_DIST - TERRAIN_CHECK_GRANULARITY / 2, sZ - TERRAIN_CHECK_DIST - TERRAIN_CHECK_GRANULARITY / 2, TERRAIN_CHECK_GRANULARITY, TERRAIN_CHECK_GRANULARITY) > sY)
  366. sIndex = LEFT_TOP;
  367. if (GetMaxHeight(pRealm, sX + TERRAIN_CHECK_DIST + TERRAIN_CHECK_GRANULARITY / 2, sZ - TERRAIN_CHECK_DIST - TERRAIN_CHECK_GRANULARITY / 2, TERRAIN_CHECK_GRANULARITY, TERRAIN_CHECK_GRANULARITY) > sY)
  368. sIndex |= RIGHT_TOP;
  369. if (GetMaxHeight(pRealm, sX - TERRAIN_CHECK_DIST - TERRAIN_CHECK_GRANULARITY / 2, sZ + TERRAIN_CHECK_DIST + TERRAIN_CHECK_GRANULARITY / 2, TERRAIN_CHECK_GRANULARITY, TERRAIN_CHECK_GRANULARITY) > sY)
  370. sIndex |= LEFT_BOTTOM;
  371. if (GetMaxHeight(pRealm, sX + TERRAIN_CHECK_DIST + TERRAIN_CHECK_GRANULARITY / 2, sZ + TERRAIN_CHECK_DIST + TERRAIN_CHECK_GRANULARITY / 2, TERRAIN_CHECK_GRANULARITY, TERRAIN_CHECK_GRANULARITY) > sY)
  372. sIndex |= RIGHT_BOTTOM;
  373. // Get terrain angle.
  374. *psAngle = ms_asTerrainAngleMap[sIndex];
  375. if (*psAngle > -1)
  376. {
  377. bFoundAngle = true;
  378. }
  379. #endif
  380. return bFoundAngle;
  381. }
  382. //////////////////////////////////////////////////////////////////////////////
  383. //
  384. // Launch a bullet, create a muzzle flare at the src (sX, sY, sZ), and,
  385. // if no CThing hit, create a ricochet at where the bullet contacted
  386. // terrain (*psX, *psY, *psZ).
  387. // This same process can be done in pieces with more control to the user
  388. // by calling Fire(), Flare(), and Ricochet().
  389. //
  390. //////////////////////////////////////////////////////////////////////////////
  391. bool CBulletFest::FireDeluxe( // Returns what and as Fire() would.
  392. short sAngleY, // In: Angle of launch in degrees (on X/Z plane).
  393. short sAngleZ, // In: Angle of launch in degrees (on X/Y plane).
  394. short sX, // In: Launch position.
  395. short sY, // In: Launch position.
  396. short sZ, // In: Launch position.
  397. short sRange, // In: Maximum distance.
  398. CRealm* pRealm, // In: Realm in which to fire.
  399. CSmash::Bits bitsInclude, // In: Mask of CSmash masks that this bullet can hit.
  400. CSmash::Bits bitsDontCare, // In: Mask of CSmash masks that this bullet does not care to hit.
  401. CSmash::Bits bitsExclude, // In: Mask of CSmash masks that this bullet cannot hit.
  402. short sMaxRicochetAngle, // In: Maximum angle with terrain that can cause
  403. // a ricochet (on X/Z plane).
  404. short sMaxRicochets, // In: The maximum number of ricochets.
  405. short* psX, // Out: Hit position.
  406. short* psY, // Out: Hit position.
  407. short* psZ, // Out: Hit position.
  408. CThing** ppthing, // Out: Ptr to thing hit or NULL.
  409. bool bTracer /*= true*/, // In: Draw a tracer at random point along path.
  410. SampleMasterID smid /*= g_smidBulletFire*/) // In: Use ammo sample.
  411. {
  412. bool bHit = false; // Assume no collision with CThing within u32ThingMask.
  413. // Create a muzzle flare.
  414. Flare(sAngleY, sX, sY, sZ, pRealm, smid);
  415. short sRicochets = 0;
  416. bool bImpact = false;
  417. short sTerrainAngle = 0;
  418. // While no hits and not out of ricochets . . .
  419. while (bHit == false && sRicochets <= sMaxRicochets && bImpact == false)
  420. {
  421. sAngleY = rspMod360(sAngleY);
  422. bHit = Fire(
  423. sAngleY, // In: Angle of launch in degrees (on X/Z plane).
  424. sAngleZ, // In: Angle of launch in degrees (on X/Y plane).
  425. sX, // In: Launch position.
  426. sY, // In: Launch position.
  427. sZ, // In: Launch position.
  428. sRange, // In: Maximum distance.
  429. pRealm, // In: Realm in which to fire.
  430. bitsInclude, // In: Mask of CSmash masks that this bullet can hit.
  431. bitsDontCare, // In: Mask of CSmash masks that this bullet does not care to hit.
  432. bitsExclude, // In: Mask of CSmash masks that this bullet cannot hit.
  433. psX, // Out: Hit position.
  434. psY, // Out: Hit position.
  435. psZ, // Out: Hit position.
  436. ppthing, // Out: Ptr to thing hit or NULL.
  437. bTracer); // In: Draw a tracer at random point along path.
  438. // If no hit . . .
  439. if (bHit == false)
  440. {
  441. // If on screen . . .
  442. if ( *psX > 0 && *psX < pRealm->GetRealmWidth()
  443. && *psZ > 0 && *psZ - *psY < pRealm->GetRealmHeight() )
  444. {
  445. #if 0
  446. // If out of ricochets . . .
  447. if (sRicochets >= sMaxRicochets)
  448. {
  449. // Impact.
  450. bImpact = true;
  451. }
  452. else
  453. {
  454. if (GetTerrainAngle(pRealm, *psX, *psY, *psZ, 10, 10, sAngleY, &sTerrainAngle) == true)
  455. {
  456. // Check for ricochet. If our contact angle is within sMaxRicochetAngle
  457. // of terrain angle . . .
  458. short sAngleDif = sTerrainAngle - sAngleY;
  459. if (ABS(sAngleDif) > sMaxRicochetAngle)
  460. {
  461. bImpact = true;
  462. }
  463. else
  464. {
  465. sAngleY = sTerrainAngle + sAngleDif;
  466. }
  467. #if 0
  468. // Might have to break this into four quadrants.
  469. // This works for the case when sAngle is in quadrant 1
  470. // and sTerrainAngle is in quadrant 2.
  471. short sAngleDif = (180 - sTerrainAngle) + sAngleY;
  472. if (sAngleDif > 90)
  473. {
  474. sAngleDif = 180 - sAngleDif;
  475. }
  476. if (sAngleDif > sMaxRicochetAngle)
  477. {
  478. // Impact.
  479. bImpact = true;
  480. }
  481. else // Ricochet.
  482. {
  483. // **** Determine deflection angle here.
  484. sAngleY = 360 - sAngleDif;
  485. }
  486. #endif
  487. }
  488. else
  489. {
  490. bImpact = true;
  491. }
  492. }
  493. // If impacting . . .
  494. if (bImpact == true)
  495. {
  496. // Impact at angle of shot.
  497. Impact(sAngleY, *psX, *psY, *psZ, pRealm);
  498. }
  499. else
  500. {
  501. // Ricochet at angle of deflection.
  502. Ricochet(sAngleY, *psX, *psY, *psZ, pRealm);
  503. sX = *psX;
  504. sY = *psY;
  505. sZ = *psZ;
  506. }
  507. #else
  508. // Ricochet is random.
  509. if (GetRandom() & RICOCHET_CHANCE_MASK)
  510. {
  511. // Impact at angle of shot.
  512. Impact(sAngleY, *psX, *psY, *psZ, pRealm);
  513. }
  514. else
  515. {
  516. // Ricochet at angle of deflection.
  517. Ricochet(sAngleY, *psX, *psY, *psZ, pRealm);
  518. }
  519. // Always stop though.
  520. bImpact = true;
  521. #endif
  522. }
  523. }
  524. sRicochets++;
  525. }
  526. return bHit;
  527. }
  528. //////////////////////////////////////////////////////////////////////////////
  529. //
  530. // Create a muzzle flare effect.
  531. //
  532. //////////////////////////////////////////////////////////////////////////////
  533. void CBulletFest::Flare( // Returns nothing.
  534. short sAngle, // In: Angle of launch in degrees (on X/Z plane).
  535. short sX, // In: Launch position.
  536. short sY, // In: Launch position.
  537. short sZ, // In: Launch position.
  538. CRealm* pRealm, // In: Realm in which to fire.
  539. SampleMasterID smid /*= g_smidBulletFire*/) // In: Use ammo sample.
  540. {
  541. // Create the animator . . .
  542. CAnimThing* pat = new CAnimThing(pRealm);
  543. ASSERT(pat != NULL);
  544. strcpy(pat->m_szResName, FLARE_RES_NAME);
  545. // Start it up:
  546. // No looping.
  547. pat->m_sLoop = FALSE;
  548. // No notification necessary.
  549. // NOTE: sAngle is currently not utilized.
  550. pat->Setup(sX, sY, sZ);
  551. // Start sample.
  552. PlaySample(
  553. smid,
  554. SampleMaster::Weapon,
  555. DistanceToVolume(sX, sY, sZ, BULLET_SND_HALF_LIFE) );
  556. }
  557. //////////////////////////////////////////////////////////////////////////////
  558. //
  559. // Create a impact effect.
  560. //
  561. //////////////////////////////////////////////////////////////////////////////
  562. void CBulletFest::Impact( // Returns nothing.
  563. short sAngle, // In: Angle of launch in degrees (on X/Z plane).
  564. short sX, // In: Launch position.
  565. short sY, // In: Launch position.
  566. short sZ, // In: Launch position.
  567. CRealm* pRealm) // In: Realm in which to fire.
  568. {
  569. // Create the animator . . .
  570. CAnimThing* pat = new CAnimThing(pRealm);
  571. ASSERT(pat != NULL);
  572. strcpy(pat->m_szResName, IMPACT_RES_NAME);
  573. // Start it up:
  574. // No looping.
  575. pat->m_sLoop = FALSE;
  576. // No notification necessary.
  577. // NOTE: sAngle is currently not utilized.
  578. pat->Setup(sX, sY, sZ);
  579. }
  580. //////////////////////////////////////////////////////////////////////////////
  581. //
  582. // Create a ricochet effect.
  583. //
  584. //////////////////////////////////////////////////////////////////////////////
  585. void CBulletFest::Ricochet( // Returns nothing.
  586. short sAngle, // In: Angle of launch in degrees (on X/Z plane).
  587. short sX, // In: Launch position.
  588. short sY, // In: Launch position.
  589. short sZ, // In: Launch position.
  590. CRealm* pRealm) // In: Realm in which to fire.
  591. {
  592. // Create the animator . . .
  593. CAnimThing* pat = new CAnimThing(pRealm);
  594. ASSERT(pat != NULL);
  595. strcpy(pat->m_szResName, RICOCHET_RES_NAME);
  596. // Start it up:
  597. // No looping.
  598. pat->m_sLoop = FALSE;
  599. // No notification necessary.
  600. // NOTE: sAngle is currently not utilized.
  601. pat->Setup(sX, sY, sZ);
  602. // Start sample.
  603. PlaySample(
  604. (GetRand() % 2) ? g_smidRicochet1 : g_smidRicochet2,
  605. SampleMaster::Weapon,
  606. DistanceToVolume(sX, sY, sZ, BULLET_SND_HALF_LIFE) );
  607. }
  608. //////////////////////////////////////////////////////////////////////////////
  609. //
  610. // Launch a bullet.
  611. //
  612. //////////////////////////////////////////////////////////////////////////////
  613. bool CBulletFest::Fire( // Returns true if a hit, false otherwise.
  614. short sAngleY, // In: Angle of launch in degrees (on X/Z plane).
  615. short sAngleZ, // In: Angle of launch in degrees (on X/Y plane).
  616. short sX, // In: Launch position.
  617. short sY, // In: Launch position.
  618. short sZ, // In: Launch position.
  619. short sRange, // In: Maximum distance.
  620. CRealm* pRealm, // In: Realm in which to fire.
  621. CSmash::Bits bitsInclude, // In: Mask of CSmash masks that this bullet can hit.
  622. CSmash::Bits bitsDontCare, // In: Mask of CSmash masks that this bullet does not care to hit.
  623. CSmash::Bits bitsExclude, // In: Mask of CSmash masks that this bullet cannot hit.
  624. short* psX, // Out: Hit position.
  625. short* psY, // Out: Hit position.
  626. short* psZ, // Out: Hit position.
  627. CThing** ppthing, // Out: Ptr to thing hit or NULL.
  628. bool bTracer /*= true*/) // In: Draw a tracer at random point along path.
  629. {
  630. bool bHit = false; // Assume no collision with CThing within u32ThingMask.
  631. // If tracer color not yet color mapped . . .
  632. if (ms_u8TracerIndex == 0)
  633. {
  634. UpdateTracerColor(pRealm);
  635. }
  636. /////////// Determine length of travel if no CThings hit ///////////////////
  637. // Get most efficient increments that won't miss any attributes.
  638. // For the rates we use trig with a hypotenuse of 1 which will give
  639. // us a rate <= 1.0 and then multiply by the scaling of the map for
  640. // a reasonable doubling or so of the speed of this alg.
  641. // sAngle must be between 0 and 359.
  642. sAngleY = rspMod360(sAngleY);
  643. sAngleZ = rspMod360(sAngleZ);
  644. float fRateX = COSQ[sAngleY]/* * pRealm->m_pAttribMap->m_sScaleX*/;
  645. float fRateZ = -SINQ[sAngleY]/* * pRealm->m_pAttribMap->m_sScaleY*/;
  646. float fRateY = SINQ[sAngleZ];
  647. // Determine amount traveled per iteration just once.
  648. float fIterDist = sqrt(ABS2(fRateX, fRateY, fRateZ) );
  649. // Set initial position to first point to check (NEVER checks original position).
  650. float fPosX = sX + fRateX;
  651. float fPosY = sY + fRateY;
  652. float fPosZ = sZ + fRateZ;
  653. float fTotalDist = 0.0F;
  654. // Store extents allowing bullets to go 10 pixels off each boundary.
  655. // This makes them appear to be unhindered by the edge of the realm.
  656. // (except the bottom edge which is greater so that, if the bullet
  657. // is exceptionally high, it will go, hopefully, entirely off screen).
  658. short sMaxX = pRealm->GetRealmWidth() + 10;
  659. short sMaxY = 512; // Robustness: Guard against infinite loop
  660. // in the remote possibility that we shoot
  661. // straight up (currently one can only shoot
  662. // horizontally).
  663. short sMaxZ = pRealm->GetRealmHeight() + sY + 10;
  664. short sMinX = -10;
  665. short sMinY = -512;
  666. short sMinZ = -10;
  667. short sCurH;
  668. // Scan while in realm.
  669. while (
  670. fPosX > sMinX
  671. && fPosY > sMinY
  672. && fPosZ > sMinZ
  673. && fPosX < sMaxX
  674. && fPosY < sMaxY
  675. && fPosZ < sMaxZ)
  676. {
  677. sCurH = pRealm->GetHeight((short) fPosX, (short) fPosZ);
  678. // If bullet below or at terrain . . .
  679. if (fPosY <= sCurH)
  680. {
  681. break;
  682. }
  683. // If we've exceeded range . . .
  684. if (fTotalDist > sRange)
  685. {
  686. // Drop bullet via gravity.
  687. fPosY -= 1.0; // FUDGE!
  688. }
  689. fPosX += fRateX;
  690. fPosY += fRateY;
  691. fPosZ += fRateZ;
  692. fTotalDist += fIterDist;
  693. }
  694. // Create 3D line segment.
  695. R3DLine line;
  696. line.X1 = sX;
  697. line.Y1 = sY;
  698. line.Z1 = sZ;
  699. line.X2 = fPosX;
  700. line.Y2 = fPosY;
  701. line.Z2 = fPosZ;
  702. // Determine if anything with mask u32ThingMask was hit . . .
  703. CSmash* psmash;
  704. if (pRealm->m_smashatorium.QuickCheckClosest(
  705. &line,
  706. bitsInclude,
  707. bitsDontCare,
  708. bitsExclude,
  709. &psmash) == true)
  710. {
  711. // Set *ppthing to thing hit.
  712. *ppthing = psmash->m_pThing;
  713. bHit = true;
  714. // Set end pt.
  715. *psX = psmash->m_sphere.sphere.X;
  716. *psY = psmash->m_sphere.sphere.Y;
  717. *psZ = psmash->m_sphere.sphere.Z;
  718. }
  719. else
  720. {
  721. // Clear thing ptr.
  722. *ppthing = NULL;
  723. // Set end pt.
  724. *psX = fPosX;
  725. *psY = fPosY;
  726. *psZ = fPosZ;
  727. }
  728. if (bTracer == true)
  729. {
  730. // Create a line sprite.
  731. CSpriteLine2d* psl2d = new CSpriteLine2d;
  732. if (psl2d != NULL)
  733. {
  734. // Get distance. Since an interception may have occurred, we need to redetermine the
  735. // distance.
  736. // We use one coordinate of the end point against one coordinate of the interception point
  737. // to get a ratio and apply that to the total distance.
  738. short sDistance = 0;
  739. if (fPosX - sX != 0.0)
  740. {
  741. sDistance = fTotalDist * ((*psX - sX) / (fPosX - sX));
  742. }
  743. #define METHOD 2
  744. #define TRACER_ACCUMMULATION_STEP_MAX 80
  745. #define TRACER_ACCUMMULATION_STEP_MIN 10
  746. short sRand = 0;
  747. #if METHOD == 1 // Method 1: Random dist between here and there.
  748. if (sDistance != 0)
  749. {
  750. sRand = GetRand() % sDistance;
  751. }
  752. #elif METHOD == 2 // Method 2: Cummulating position with wrapping.
  753. if (sDistance != 0)
  754. {
  755. sRand = m_sCurTracerPos = (m_sCurTracerPos + GetRand() % TRACER_ACCUMMULATION_STEP_MAX) % sDistance;
  756. }
  757. #else METHOD == 3 // Method 3: Multiple Method 1 and Method 2's.
  758. // Reduce distance.
  759. sDistance -= TRACER_ACCUMMULATION_STEP_MAX;
  760. while (sRand < sDistance)
  761. {
  762. sRand += (GetRand() % (TRACER_ACCUMMULATION_STEP_MAX - TRACER_ACCUMMULATION_STEP_MIN)) + TRACER_ACCUMMULATION_STEP_MIN;
  763. // If no current sprite . . .
  764. if (psl2d == NULL)
  765. {
  766. // Create a line sprite.
  767. psl2d = new CSpriteLine2d;
  768. }
  769. // If we have a sprite . . .
  770. if (psl2d != NULL)
  771. {
  772. #endif
  773. short sRand2 = GetRand() % TRACER_MAX_LENGTH;
  774. float fStartX = sX + fRateX * sRand;
  775. float fStartY = sY + fRateY * sRand;
  776. float fStartZ = sZ + fRateZ * sRand;
  777. pRealm->Map3Dto2D(
  778. fStartX,
  779. fStartY,
  780. fStartZ,
  781. &(psl2d->m_sX2),
  782. &(psl2d->m_sY2) );
  783. pRealm->Map3Dto2D(
  784. fStartX + fRateX * sRand2,
  785. fStartY + fRateY * sRand2,
  786. fStartZ + fRateZ * sRand2,
  787. &(psl2d->m_sX2End),
  788. &(psl2d->m_sY2End) );
  789. psl2d->m_sPriority = fStartZ;
  790. psl2d->m_sLayer = CRealm::GetLayerViaAttrib(pRealm->GetLayer((short) fStartX, (short) fStartZ));
  791. psl2d->m_u8Color = ms_u8TracerIndex;
  792. // Destroy when done.
  793. psl2d->m_sInFlags = CSprite::InDeleteOnRender;
  794. // Put 'er there.
  795. pRealm->m_scene.UpdateSprite(psl2d);
  796. #if METHOD == 3
  797. // Done with this 'sprite'.
  798. psl2d = NULL;
  799. }
  800. }
  801. #endif
  802. }
  803. else
  804. {
  805. TRACE("Fire(): Unable to allocate CSpriteLine2d.\n");
  806. }
  807. }
  808. #if 0
  809. // FEEDBACK.
  810. // Create a line sprite.
  811. CSpriteLine2d* psl2d = new CSpriteLine2d;
  812. if (psl2d != NULL)
  813. {
  814. pRealm->Map3Dto2D(
  815. sX,
  816. sY,
  817. sZ,
  818. &(psl2d->m_sX2),
  819. &(psl2d->m_sY2) );
  820. pRealm->Map3Dto2D(
  821. sX + fRateX * fTotalDist,
  822. sY + fRateY * fTotalDist,
  823. sZ + fRateZ * fTotalDist,
  824. &(psl2d->m_sX2End),
  825. &(psl2d->m_sY2End) );
  826. psl2d->m_sPriority = sZ;
  827. psl2d->m_sLayer = CRealm::GetLayerViaAttrib(pRealm->GetLayer(sX, sZ));
  828. psl2d->m_u8Color = ms_u8TracerIndex;
  829. // Destroy when done.
  830. psl2d->m_sInFlags = CSprite::InDeleteOnRender;
  831. // Put 'er there.
  832. pRealm->m_scene.UpdateSprite(psl2d);
  833. }
  834. #endif
  835. return bHit;
  836. }
  837. ///////////////////////////////////////////////////////////////////////////////
  838. // Update current targeting information. This will function will
  839. // continually track the position of a target in order to determine
  840. // how much it is moving. The idea being that targets that move
  841. // irratically(sp?) and more often are harder to hit.
  842. ///////////////////////////////////////////////////////////////////////////////
  843. void CBulletFest::UpdateTarget( // Returns nothing.
  844. short sAngle, // In: Angle of aim in degrees (on X/Z plane).
  845. short sX, // In: Aim position.
  846. short sY, // In: Aim position.
  847. short sZ, // In: Aim position.
  848. CRealm* pRealm) // In: Realm in which to target.
  849. {
  850. // NYI!
  851. }
  852. ///////////////////////////////////////////////////////////////////////////////
  853. // Updates the static tracer color.
  854. // (static)
  855. ///////////////////////////////////////////////////////////////////////////////
  856. void CBulletFest::UpdateTracerColor(
  857. CRealm* prealm) // In: Calling realm.
  858. {
  859. ASSERT(prealm->m_phood);
  860. ASSERT(prealm->m_phood->m_pimBackground);
  861. ASSERT(prealm->m_phood->m_pimBackground->m_pPalette);
  862. RPal* ppal = prealm->m_phood->m_pimBackground->m_pPalette;
  863. ms_u8TracerIndex = rspMatchColorRGB( // Out: Matched index.
  864. TRACER_COLOR_RED, // In: Pixel's red value.
  865. TRACER_COLOR_GREEN, // In: Pixel's green value.
  866. TRACER_COLOR_BLUE, // In: Pixel's blue value.
  867. CONSTANT_COLOR_START_INDEX, // In: Min mappable index (affects source).
  868. // Do NOT include the top 10 windows colors!
  869. NUM_CONSTANT_COLOR_INDICES, // In: Num mappable indices (affects source).
  870. ppal->Red(0), // In: Beginning of red color table.
  871. ppal->Green(0), // In: Beginning of green color table.
  872. ppal->Blue(0), // In: Beginning of blue color table.
  873. ppal->GetPalEntrySize() // In: Size to increment between each index in each table.
  874. );
  875. }
  876. ///////////////////////////////////////////////////////////////////////////////
  877. // Preload any assets that may be used.
  878. // (static).
  879. ///////////////////////////////////////////////////////////////////////////////
  880. short CBulletFest::Preload(
  881. CRealm* prealm) // In: Calling realm.
  882. {
  883. CAnimThing::ChannelAA* paaCache;
  884. short sResult = 0;
  885. if (rspGetResource(&g_resmgrGame, prealm->Make2dResPath(IMPACT_RES_NAME), &paaCache) == 0)
  886. rspReleaseResource(&g_resmgrGame, &paaCache);
  887. else
  888. sResult |= 1;
  889. if (rspGetResource(&g_resmgrGame, prealm->Make2dResPath(RICOCHET_RES_NAME), &paaCache) == 0)
  890. rspReleaseResource(&g_resmgrGame, &paaCache);
  891. else
  892. sResult |= 1;
  893. if (rspGetResource(&g_resmgrGame, prealm->Make2dResPath(FLARE_RES_NAME), &paaCache) == 0)
  894. rspReleaseResource(&g_resmgrGame, &paaCache);
  895. else
  896. sResult |= 1;
  897. return sResult;
  898. }
  899. ///////////////////////////////////////////////////////////////////////////////
  900. // EOF
  901. ///////////////////////////////////////////////////////////////////////////////