sentry.cpp 32 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. // sentry.cpp
  19. // Project: Postal
  20. //
  21. // This module implements the automatic sentry gun
  22. //
  23. // History:
  24. // 06/02/97 BRH Created this Sentry gun from gunner.cpp.
  25. //
  26. // 06/05/97 JMI Changed m_sHitPoints to m_stockpile.m_sHitPoints to
  27. // accommodate new m_stockpile in base class, CThing3d (which
  28. // used to contain the m_sHitPoints).
  29. //
  30. // 06/13/97 BRH Added Turret and Base for the Sentry.
  31. //
  32. // 06/16/97 BRH Added blown up animation, dialog box for weapon selection
  33. // and settings. Fixed positioning problems.
  34. //
  35. // 06/17/97 BRH Added SetRangeToTarget call for weapons that
  36. // require range adjustment.
  37. //
  38. // 06/18/97 BRH Changed over to using GetRandom()
  39. //
  40. // 06/25/97 JMI Now calls PrepareShadow() in Init() which loads and sets up
  41. // a shadow sprite.
  42. //
  43. // 06/30/97 JMI Added override for EditRect() and EditHotSpot().
  44. // Now sets priority and layer for turret from base's values.
  45. //
  46. // MJR Replaced SAFE_GUI_REF with new GuiItem.h-defined macro.
  47. //
  48. // BRH Caches the sound effects during the static portion of
  49. // load so that the sound effect will be ready on any
  50. // level that has a sentry gun.
  51. //
  52. // 07/01/97 BRH Added angular velocity to allow tuning of the rotation
  53. // rate of the sentry gun. Still need to edit the dialog
  54. // box and EditModify to set the change.
  55. //
  56. // 07/02/97 BRH Added angular velocity setting to edit modify dialog.
  57. //
  58. // 07/18/97 JMI Got rid of bogus immitation PlaySample functions.
  59. // Now there is one PlaySample() function. Also, you now
  60. // MUST specify a category and you don't have to specify a
  61. // SoundInstance ptr to specify a volume.
  62. //
  63. // 07/31/97 JMI Changed m_sPriority to use 3D z position like CThin3d does.
  64. //
  65. // 08/01/97 BRH Took out reference to animation hots since we are not
  66. // using those.
  67. //
  68. // 08/08/97 JMI Now dynamically builds the weapons list for the
  69. // EditModify().
  70. // Also, since it doesn't call the base class Update(), it
  71. // has to monitor the flamage sample.
  72. //
  73. // 08/11/97 JMI Added transform for base, m_transBase.
  74. // Also, made UpdatePosition() bypass its logic when the
  75. // animations are not yet set.
  76. //
  77. // 08/16/97 BRH Added collision bits for the Sentry gun to pass to
  78. // ShootWeapon.
  79. //
  80. // 08/18/97 JMI Now plays impact animation when hit by bullets.
  81. //
  82. // 08/18/97 JMI Changed State_Dead to call DeadRender3D() (which used to be
  83. // known/called as just another Render() overload).
  84. //
  85. // 08/20/97 BRH Changed ricochet sounds from Destruction to Weapon volume
  86. // slider.
  87. //
  88. // 08/26/97 BRH Changed sentry gun getting hit by bullets sound.
  89. //
  90. // 09/02/97 JMI Changed use of Misc bit to Sentry bit.
  91. //
  92. // 09/03/97 JMI Sentries now exclude CSmash::Bads and CSmash::Civilians.
  93. //
  94. ////////////////////////////////////////////////////////////////////////////////
  95. #define SENTRY_CPP
  96. #include "RSPiX.h"
  97. #include "sentry.h"
  98. #include "game.h"
  99. #include "SampleMaster.h"
  100. #include "reality.h"
  101. ////////////////////////////////////////////////////////////////////////////////
  102. // Macros/types/etc.
  103. ////////////////////////////////////////////////////////////////////////////////
  104. // Anim for when a barrel is hit by a bullet.
  105. #define SENTRY_HIT_RES_NAME "Ricochet.aan"
  106. #define HULL_RADIUS (m_sprite.m_sRadius / 2)
  107. // Gets a GetRandom()om between -range / 2 and range / 2.
  108. #define RAND_SWAY(sway) ((GetRandom() % sway) - sway / 2)
  109. // Tunable bullet parameters.
  110. #define MAX_BULLET_RANGE 400
  111. #define MAX_BULLETS_PER_SEC 6
  112. #define MS_BETWEEN_BULLETS (1000 / MAX_BULLETS_PER_SEC)
  113. ////////////////////////////////////////////////////////////////////////////////
  114. // Variables/data
  115. ////////////////////////////////////////////////////////////////////////////////
  116. // These are default values -- actually values are set using the editor!
  117. double CSentry::ms_dTooCloseDistance = 190*190; // Close enough to hit CDude
  118. double CSentry::ms_dLongRange = 500*500; // Squared distance (500 pixels away)
  119. double CSentry::ms_dInRangeLow = 30*30; // Squared distance to be in range with weapon
  120. double CSentry::ms_dInRangeHigh = 230*230;// Squared distance to be in range with weapon
  121. double CSentry::ms_dGravity = -19.5; // Cheater gravity
  122. double CSentry::ms_dBlowupVelocity = 190; // Initial vertical velocity
  123. long CSentry::ms_lRandomAvoidTime = 200; // Time to wander before looking again
  124. long CSentry::ms_lReseekTime = 1000; // Do a 'find' again
  125. long CSentry::ms_lWatchWaitTime = 2500; // Time to watch shot go
  126. long CSentry::ms_lPatrolTime = 5000; // Time to patrol before shooting
  127. long CSentry::ms_lDeathTimeout = 20000; // Wait around after dying
  128. long CSentry::ms_lBurningRunTime = 50; // Run this time before turning
  129. short CSentry::ms_sHitLimit = 150; // Number of starting hit points
  130. short CSentry::ms_sBurntBrightness = -40; // Brightness after being burnt
  131. long CSentry::ms_lMaxShootTime = MS_BETWEEN_BULLETS; // Maximum in ms of continuous shooting.
  132. long CSentry::ms_lReselectDudeTime = 3000; // Time to go without finding a dude
  133. // before calling SelectDude() to find
  134. // possibly a closer one.
  135. U32 CSentry::ms_u32WeaponIncludeBits = CSmash::Character | CSmash::Barrel | CSmash::Misc;
  136. U32 CSentry::ms_u32WeaponDontcareBits = CSmash::Good | CSmash::Bad;
  137. U32 CSentry::ms_u32WeaponExcludeBits = CSmash::SpecialBarrel | CSmash::Ducking | CSmash::Bad | CSmash::Civilian;
  138. // Let this auto-init to 0
  139. short CSentry::ms_sFileCount;
  140. /// Throwing Animation Files ////////////////////////////////////////////////////
  141. // An array of pointers to resource names (one for each channel of the animation)
  142. static char* ms_apszShootResNames[] =
  143. {
  144. "3d/sentry_shoot.sop",
  145. "3d/sentry_shoot.mesh",
  146. "3d/sentry_shoot.tex",
  147. "3d/sentry_shoot.hot",
  148. "3d/sentry_shoot.bounds",
  149. "3d/sentry_shoot.floor",
  150. "3d/sentry_shoot_tip.trans",
  151. NULL
  152. };
  153. static char* ms_apszStandResNames[] =
  154. {
  155. "3d/sentry_still.sop",
  156. "3d/sentry_still.mesh",
  157. "3d/sentry_still.tex",
  158. "3d/sentry_still.hot",
  159. "3d/sentry_still.bounds",
  160. "3d/sentry_still.floor",
  161. "3d/sentry_still_tip.trans",
  162. NULL
  163. };
  164. static char* ms_apszDieResNames[] =
  165. {
  166. "3d/sentry_damaged.sop",
  167. "3d/sentry_damaged.mesh",
  168. "3d/sentry_damaged.tex",
  169. "3d/sentry_damaged.hot",
  170. "3d/sentry_damaged.bounds",
  171. "3d/sentry_damaged.floor",
  172. "3d/sentry_damaged_tip.trans",
  173. NULL
  174. };
  175. static char* ms_apszBaseStandResNames[] =
  176. {
  177. "3d/stand_still.sop",
  178. "3d/stand_still.mesh",
  179. "3d/stand_still.tex",
  180. "3d/stand_still.hot",
  181. "3d/stand_still.bounds",
  182. "3d/stand_still.floor",
  183. "3d/stand_still_stand.trans",
  184. NULL
  185. };
  186. static char* ms_apszBaseDieResNames[] =
  187. {
  188. "3d/stand_damaged.sop",
  189. "3d/stand_damaged.mesh",
  190. "3d/stand_damaged.tex",
  191. "3d/stand_damaged.hot",
  192. "3d/stand_damaged.bounds",
  193. "3d/stand_damaged.floor",
  194. "3d/stand_damaged_stand.trans",
  195. NULL
  196. };
  197. // These are the points that are checked on the attribute map relative to his origin
  198. static RP3d ms_apt3dAttribCheck[] =
  199. {
  200. {-6, 0, -6},
  201. { 0, 0, -6},
  202. { 6, 0, -6},
  203. {-6, 0, 6},
  204. { 0, 0, 6},
  205. { 6, 0, 6},
  206. };
  207. ////////////////////////////////////////////////////////////////////////////////
  208. // Load object (should call base class version!)
  209. ////////////////////////////////////////////////////////////////////////////////
  210. short CSentry::Load( // Returns 0 if successfull, non-zero otherwise
  211. RFile* pFile, // In: File to load from
  212. bool bEditMode, // In: True for edit mode, false otherwise
  213. short sFileCount, // In: File count (unique per file, never 0)
  214. ULONG ulFileVersion) // In: Version of file format to load.
  215. {
  216. short sResult = 0;
  217. // Call the base load function to get ID, position, etc.
  218. sResult = CDoofus::Load(pFile, bEditMode, sFileCount, ulFileVersion);
  219. if (sResult == 0)
  220. {
  221. // Load common data just once per file (not with each object)
  222. if (ms_sFileCount != sFileCount)
  223. {
  224. ms_sFileCount = sFileCount;
  225. CacheSample(g_smidShotSentry1);
  226. CacheSample(g_smidShotSentry2);
  227. CacheSample(g_smidShotSentry3);
  228. // Load static data
  229. switch (ulFileVersion)
  230. {
  231. default:
  232. case 1:
  233. pFile->Read(&ms_dTooCloseDistance);
  234. pFile->Read(&ms_dLongRange);
  235. pFile->Read(&ms_dInRangeLow);
  236. pFile->Read(&ms_dInRangeHigh);
  237. pFile->Read(&ms_lRandomAvoidTime);
  238. pFile->Read(&ms_lReseekTime);
  239. pFile->Read(&ms_lWatchWaitTime);
  240. pFile->Read(&ms_lPatrolTime);
  241. break;
  242. }
  243. }
  244. // Load other values
  245. // for now, temporarily set values here to default values
  246. pFile->Read(&m_sNumRounds);
  247. pFile->Read(&m_sRoundsPerShot);
  248. pFile->Read(&m_lSqDistRange);
  249. pFile->Read(&m_lShootDelay);
  250. pFile->Read(&m_eWeaponType);
  251. if (ulFileVersion > 24)
  252. pFile->Read(&m_dAngularVelocity);
  253. // m_sNumRounds = 32000;
  254. // m_sRoundsPerShot = 2;
  255. // m_lSqDistRange = 280*280;
  256. // m_eWeaponType = CShotGunID;
  257. // m_eWeaponType = CShotGunID;
  258. // m_lShootDelay = 500;
  259. // Make sure there were no file errors or format errors . . .
  260. if (!pFile->Error() && sResult == 0)
  261. {
  262. // Get resources
  263. sResult = GetResources();
  264. }
  265. else
  266. {
  267. sResult = -1;
  268. TRACE("CSentry::Load(): Error reading from file!\n");
  269. }
  270. }
  271. else
  272. {
  273. TRACE("CSentry::Load(): CDoofus::Load() failed.\n");
  274. }
  275. return sResult;
  276. }
  277. ////////////////////////////////////////////////////////////////////////////////
  278. // Save object (should call base class version!)
  279. ////////////////////////////////////////////////////////////////////////////////
  280. short CSentry::Save( // Returns 0 if successfull, non-zero otherwise
  281. RFile* pFile, // In: File to save to
  282. short sFileCount) // In: File count (unique per file, never 0)
  283. {
  284. // Swap the hotspot we want to save in.
  285. double dTempX = m_dX;
  286. double dTempY = m_dY;
  287. double dTempZ = m_dZ;
  288. m_dX = m_dXBase;
  289. m_dY = m_dYBase;
  290. m_dZ = m_dZBase;
  291. short sResult;
  292. // Call the base class save to save the instance ID, position, etc
  293. CDoofus::Save(pFile, sFileCount);
  294. // Save common data just once per file (not with each object)
  295. if (ms_sFileCount != sFileCount)
  296. {
  297. ms_sFileCount = sFileCount;
  298. // Save static data
  299. pFile->Write(&ms_dTooCloseDistance);
  300. pFile->Write(&ms_dLongRange);
  301. pFile->Write(&ms_dInRangeLow);
  302. pFile->Write(&ms_dInRangeHigh);
  303. pFile->Write(&ms_lRandomAvoidTime);
  304. pFile->Write(&ms_lReseekTime);
  305. pFile->Write(&ms_lWatchWaitTime);
  306. pFile->Write(&ms_lPatrolTime);
  307. }
  308. // Save additinal stuff here.
  309. pFile->Write(&m_sNumRounds);
  310. pFile->Write(&m_sRoundsPerShot);
  311. pFile->Write(&m_lSqDistRange);
  312. pFile->Write(&m_lShootDelay);
  313. pFile->Write(&m_eWeaponType);
  314. pFile->Write(&m_dAngularVelocity);
  315. if (!pFile->Error())
  316. {
  317. sResult = SUCCESS;
  318. }
  319. else
  320. {
  321. TRACE("CSentry::Save() - Error writing to file\n");
  322. sResult = -1;
  323. }
  324. m_dX = dTempX;
  325. m_dY = dTempY;
  326. m_dZ = dTempZ;
  327. return sResult;
  328. }
  329. ////////////////////////////////////////////////////////////////////////////////
  330. // Render - Override to skip over CDoofus::Render right to CCharacter::Render
  331. ////////////////////////////////////////////////////////////////////////////////
  332. void CSentry::Render(void)
  333. {
  334. // Do our own render of the stationary base
  335. U16 u16CombinedAttributes;
  336. short sLightTally;
  337. GetEffectAttributes(m_dXBase, m_dZBase, &u16CombinedAttributes, &sLightTally);
  338. // Brightness.
  339. m_spriteBase.m_sBrightness = m_sBrightness + sLightTally * gsGlobalBrightnessPerLightAttribute;
  340. // If no parent . . .
  341. if (m_u16IdParent == CIdBank::IdNil)
  342. {
  343. // Reset transform back to start to set absolute rather than cummulative rotation
  344. m_trans.Make1();
  345. // m_transBase.Make1(); Not currently needed since the base does not change its transform.
  346. m_trans.Ry(rspMod360(m_dRot) );
  347. m_trans.Rz(rspMod360(m_dRotZ) );
  348. // Map from 3d to 2d coords
  349. Map3Dto2D((short) m_dXBase, (short) m_dYBase, (short) m_dZBase, &m_spriteBase.m_sX2, &m_spriteBase.m_sY2);
  350. // Layer should be based on info from attribute map.
  351. GetLayer(m_dXBase, m_dZBase, &(m_spriteBase.m_sLayer) );
  352. // Priority is based on bottom edge of sprite which is currently the origin
  353. m_spriteBase.m_sPriority = m_dZBase;
  354. // Update sprite in scene
  355. m_pRealm->m_scene.UpdateSprite(&m_spriteBase);
  356. // Set transform.
  357. m_spriteBase.m_ptrans = &m_transBase;
  358. }
  359. ASSERT(m_panimCurBase != NULL);
  360. m_spriteBase.m_pmesh = (RMesh*) m_panimCurBase->m_pmeshes->GetAtTime(m_lAnimTime);
  361. m_spriteBase.m_psop = (RSop*) m_panimCurBase->m_psops->GetAtTime(m_lAnimTime);
  362. m_spriteBase.m_ptex = (RTexture*) m_panimCurBase->m_ptextures->GetAtTime(m_lAnimTime);
  363. m_spriteBase.m_psphere = (RP3d*) m_panimCurBase->m_pbounds->GetAtTime(m_lAnimTime);
  364. CCharacter::Render();
  365. // The turret is always at a just higher priority than the base.
  366. m_sprite.m_sPriority = m_spriteBase.m_sPriority + 1;
  367. m_sprite.m_sLayer = m_spriteBase.m_sLayer;
  368. // Update sprite in scene
  369. m_pRealm->m_scene.UpdateSprite(&m_sprite);
  370. }
  371. ////////////////////////////////////////////////////////////////////////////////
  372. // Init - Call this after the resources are in place
  373. ////////////////////////////////////////////////////////////////////////////////
  374. short CSentry::Init(void)
  375. {
  376. short sResult = 0;
  377. // Prepare shadow (get resources and setup sprite).
  378. sResult = PrepareShadow();
  379. // Init other stuff
  380. m_dVel = 0.0;
  381. m_dRot = 0.0;
  382. m_dShootAngle = 0.0;
  383. // Set to different starting state based on the design of the animation, but
  384. // for now, ok. Then also set his current animation.
  385. m_state = CSentry::State_Wait;
  386. m_dAcc = ms_dAccUser;
  387. m_panimCur = &m_animStand;
  388. m_panimCurBase = &m_animBaseStand;
  389. m_lAnimTime = 0;
  390. m_lTimer = m_pRealm->m_time.GetGameTime() + 500;
  391. // Set up the animations that are supposed to loop.
  392. m_animShoot.m_psops->SetLooping(RChannel_LoopAtStart | RChannel_LoopAtEnd);
  393. m_animShoot.m_pmeshes->SetLooping(RChannel_LoopAtStart | RChannel_LoopAtEnd);
  394. m_animShoot.m_ptextures->SetLooping(RChannel_LoopAtStart | RChannel_LoopAtEnd);
  395. // m_animShoot.m_phots->SetLooping(RChannel_LoopAtStart | RChannel_LoopAtEnd);
  396. m_animShoot.m_pbounds->SetLooping(RChannel_LoopAtStart | RChannel_LoopAtEnd);
  397. // Set up the base sprite so we can get the position
  398. m_spriteBase.m_pmesh = (RMesh*) m_panimCurBase->m_pmeshes->GetAtTime(m_lAnimTime);
  399. m_spriteBase.m_psop = (RSop*) m_panimCurBase->m_psops->GetAtTime(m_lAnimTime);
  400. m_spriteBase.m_ptex = (RTexture*) m_panimCurBase->m_ptextures->GetAtTime(m_lAnimTime);
  401. m_spriteBase.m_psphere = (RP3d*) m_panimCurBase->m_pbounds->GetAtTime(m_lAnimTime);
  402. m_spriteBase.m_ptrans = (RTransform*) m_panimCurBase->m_ptransRigid->GetAtTime(m_lAnimTime);
  403. // Update base and turret position via m_dX, Y, & Z.
  404. UpdatePosition();
  405. m_stockpile.m_sHitPoints = ms_sHitLimit;
  406. m_smash.m_bits = CSmash::Bad | CSmash::Sentry;
  407. m_smash.m_pThing = this;
  408. m_sBrightness = 0; // Default Brightness level
  409. return sResult;
  410. }
  411. ////////////////////////////////////////////////////////////////////////////////
  412. // Position the base and turret based on m_dX, Y, & Z.
  413. ////////////////////////////////////////////////////////////////////////////////
  414. void CSentry::UpdatePosition(void)
  415. {
  416. // Move Base and Turret into position
  417. m_dXBase = m_dX;
  418. m_dYBase = m_dY;
  419. m_dZBase = m_dZ;
  420. if (m_panimCurBase != NULL)
  421. {
  422. // Below was copied from DetachChild in Thing3d
  423. // Update its position.
  424. // set up translation based on the combined last character and child transforms
  425. RTransform transChildAbsolute;
  426. RTransform* ptransRigid = m_panimCurBase->m_ptransRigid->GetAtTime(m_lAnimTime);
  427. // Apply child and parent to transChildAbs
  428. transChildAbsolute.Mul(m_trans.T, ptransRigid->T);
  429. // Set up pt at origin for child.
  430. RP3d pt3Src = {0, 0, 0, 1};
  431. RP3d pt3Dst;
  432. // Get last transition position by mapping origin.
  433. m_pRealm->m_scene.TransformPtsToRealm(&transChildAbsolute, &pt3Src, &pt3Dst, 1);
  434. // Set child position to character's position offset by rigid body's realm offset.
  435. m_dX = m_dXBase + pt3Dst.x;
  436. m_dY = m_dYBase + pt3Dst.y;
  437. m_dZ = m_dZBase + pt3Dst.z;
  438. }
  439. }
  440. ////////////////////////////////////////////////////////////////////////////////
  441. // Startup object
  442. ////////////////////////////////////////////////////////////////////////////////
  443. short CSentry::Startup(void) // Returns 0 if successfull, non-zero otherwise
  444. {
  445. short sResult = 0;
  446. // Set the current height, previous time, and Nav Net
  447. CDoofus::Startup();
  448. // Init other stuff
  449. Init();
  450. return sResult;
  451. }
  452. ////////////////////////////////////////////////////////////////////////////////
  453. // Shutdown object
  454. ////////////////////////////////////////////////////////////////////////////////
  455. short CSentry::Shutdown(void) // Returns 0 if successfull, non-zero otherwise
  456. {
  457. short sResult = 0;
  458. m_trans.Make1();
  459. m_transBase.Make1();
  460. return sResult;
  461. }
  462. ////////////////////////////////////////////////////////////////////////////////
  463. // Suspend object
  464. ////////////////////////////////////////////////////////////////////////////////
  465. void CSentry::Suspend(void)
  466. {
  467. // Call base class suspend, and add anything else here if necessary
  468. CDoofus::Suspend();
  469. }
  470. ////////////////////////////////////////////////////////////////////////////////
  471. // Resume object
  472. ////////////////////////////////////////////////////////////////////////////////
  473. void CSentry::Resume(void)
  474. {
  475. // Call the base class resume and add anything else you suspended
  476. CDoofus::Resume();
  477. }
  478. ////////////////////////////////////////////////////////////////////////////////
  479. // Update object
  480. ////////////////////////////////////////////////////////////////////////////////
  481. void CSentry::Update(void)
  482. {
  483. short sHeight = m_sPrevHeight;
  484. long lThisTime;
  485. long lTimeDifference;
  486. long lSqDistanceToDude = 0;
  487. short sTargetAngle;
  488. short sAngleCCL;
  489. short sAngleCL;
  490. short sAngleDistance;
  491. double dRotDistance;
  492. bool bShootThisTime = false;
  493. if (!m_sSuspend)
  494. {
  495. // Get new time
  496. lThisTime = m_pRealm->m_time.GetGameTime();
  497. lTimeDifference = lThisTime - m_lPrevTime;
  498. // Calculate elapsed time in seconds
  499. double dSeconds = (double)(lThisTime - m_lPrevTime) / 1000.0;
  500. // Check for new messages that may change the state
  501. ProcessMessages();
  502. switch(m_state)
  503. {
  504. case CSentry::State_Wait:
  505. if (lThisTime > m_lTimer)
  506. {
  507. m_state = CSentry::State_Guard;
  508. }
  509. // Update sphere.
  510. m_smash.m_sphere.sphere.X = m_dXBase;
  511. m_smash.m_sphere.sphere.Y = m_dYBase;
  512. m_smash.m_sphere.sphere.Z = m_dZBase;
  513. m_smash.m_sphere.sphere.lRadius = 30; //m_spriteBase.m_sRadius;
  514. // Update the smash.
  515. m_pRealm->m_smashatorium.Update(&m_smash);
  516. break;
  517. //-----------------------------------------------------------------------
  518. // Guard - normal operation
  519. //-----------------------------------------------------------------------
  520. case CSentry::State_Guard:
  521. // Target closest Dude
  522. SelectDude();
  523. sTargetAngle = CDoofus::FindDirection();
  524. sAngleCCL = rspMod360(sTargetAngle - m_dRot);
  525. sAngleCL = rspMod360((360 - sTargetAngle) + m_dRot);
  526. sAngleDistance = MIN(sAngleCCL, sAngleCL);
  527. // Calculate the amount it can turn this time.
  528. dRotDistance = dSeconds * m_dAngularVelocity;
  529. // Don't over rotate - if it is within reach this time, then its OK to shoot
  530. if (sAngleDistance < dRotDistance)
  531. {
  532. dRotDistance = sAngleDistance;
  533. bShootThisTime = true;
  534. }
  535. if (sAngleCCL < sAngleCL)
  536. // Rotate Counter Clockwise
  537. {
  538. m_dShootAngle = rspMod360(m_dShootAngle + dRotDistance);
  539. m_dRot = m_dAnimRot = m_dShootAngle;
  540. }
  541. else
  542. // Rotate Clockwise
  543. {
  544. m_dShootAngle = rspMod360(m_dShootAngle - dRotDistance);
  545. m_dRot = m_dAnimRot = m_dShootAngle;
  546. }
  547. // Turn to him directly for now.
  548. // m_dRot = m_dAnimRot = m_dShootAngle = CDoofus::FindDirection();
  549. lSqDistanceToDude = CDoofus::SQDistanceToDude();
  550. if (bShootThisTime &&
  551. m_idDude != CIdBank::IdNil &&
  552. lSqDistanceToDude < m_lSqDistRange &&
  553. lThisTime > m_lTimer &&
  554. m_sNumRounds > 0)
  555. {
  556. if (TryClearShot(m_dShootAngle, 3))
  557. {
  558. m_panimCur = &m_animShoot;
  559. m_lAnimTime += lTimeDifference;
  560. CWeapon* pweapon = PrepareWeapon();
  561. if (pweapon != NULL)
  562. pweapon->SetRangeToTarget(rspSqrt(lSqDistanceToDude));
  563. ShootWeapon(ms_u32WeaponIncludeBits, ms_u32WeaponDontcareBits, ms_u32WeaponExcludeBits);
  564. m_sNumRounds--;
  565. m_lTimer = lThisTime + m_lShootDelay;
  566. }
  567. else
  568. {
  569. m_lAnimTime = 0;
  570. m_panimCur = &m_animStand;
  571. }
  572. }
  573. break;
  574. //-----------------------------------------------------------------------
  575. // Blownup - You were blown up so pop up into the air and come down dead
  576. //-----------------------------------------------------------------------
  577. case CSentry::State_BlownUp:
  578. // Make her animate
  579. m_lAnimTime += lTimeDifference;
  580. if (!WhileBlownUp())
  581. m_state = State_Dead;
  582. else
  583. {
  584. if (lThisTime > m_lTimer && m_sNumRounds > 0)
  585. {
  586. m_dShootAngle = m_dRot;
  587. PrepareWeapon();
  588. ShootWeapon(ms_u32WeaponIncludeBits, ms_u32WeaponDontcareBits, ms_u32WeaponExcludeBits);
  589. m_sNumRounds--;
  590. m_lTimer = lThisTime + m_lShootDelay;
  591. }
  592. UpdateFirePosition();
  593. }
  594. break;
  595. //-----------------------------------------------------------------------
  596. // Dead - You are dead, so lay there and decompose, then go away
  597. //-----------------------------------------------------------------------
  598. case CSentry::State_Dead:
  599. CHood* phood = m_pRealm->m_phood;
  600. // Render current dead frame into background to stay.
  601. m_pRealm->m_scene.DeadRender3D(
  602. phood->m_pimBackground, // Destination image.
  603. &m_spriteBase, // Tree of 3D sprites to render.
  604. phood); // Dst clip rect.
  605. CDoofus::OnDead();
  606. delete this;
  607. return;
  608. break;
  609. }
  610. // Here's a little piece of CCharacter::Update() since this class
  611. // doesn't use the base class update.
  612. // If we have a weapon sound play instance . . .
  613. if (m_siLastWeaponPlayInstance)
  614. {
  615. // If time has expired . . .
  616. if (m_pRealm->m_time.GetGameTime() > m_lStopLoopingWeaponSoundTime)
  617. {
  618. // Stop looping the sound.
  619. StopLoopingSample(m_siLastWeaponPlayInstance);
  620. // Forget about it.
  621. m_siLastWeaponPlayInstance = 0;
  622. }
  623. }
  624. // Save time for next time
  625. m_lPrevTime = lThisTime;
  626. m_lAnimPrevUpdateTime = m_lAnimTime;
  627. }
  628. }
  629. ////////////////////////////////////////////////////////////////////////////////
  630. // Called by editor to init new object at specified position
  631. ////////////////////////////////////////////////////////////////////////////////
  632. short CSentry::EditNew( // Returns 0 if successfull, non-zero otherwise
  633. short sX, // In: New x coord
  634. short sY, // In: New y coord
  635. short sZ) // In: New z coord
  636. {
  637. short sResult = 0;
  638. sResult = CDoofus::EditNew(sX, sY, sZ);
  639. if (sResult == SUCCESS)
  640. {
  641. // Load resources
  642. sResult = GetResources();
  643. if (sResult == SUCCESS)
  644. {
  645. sResult = Init();
  646. }
  647. }
  648. else
  649. {
  650. sResult = -1;
  651. }
  652. return sResult;
  653. }
  654. ////////////////////////////////////////////////////////////////////////////////
  655. // Edit Move
  656. ////////////////////////////////////////////////////////////////////////////////
  657. short CSentry::EditMove(short sX, short sY, short sZ)
  658. {
  659. short sResult = CDoofus::EditMove(sX, sY, sZ);
  660. UpdatePosition();
  661. return sResult;
  662. }
  663. ////////////////////////////////////////////////////////////////////////////////
  664. // Give Edit a rectangle around this object
  665. ////////////////////////////////////////////////////////////////////////////////
  666. void CSentry::EditRect(RRect* pRect)
  667. {
  668. // Swap the hotspot and anim we want to get the rect of in.
  669. double dTempX = m_dX;
  670. double dTempY = m_dY;
  671. double dTempZ = m_dZ;
  672. m_dX = m_dXBase;
  673. m_dY = m_dYBase;
  674. m_dZ = m_dZBase;
  675. CAnim3D* panimTemp = m_panimCur;
  676. m_panimCur = m_panimCurBase;
  677. // Call base class.
  678. CDoofus::EditRect(pRect);
  679. // Restore.
  680. m_dX = dTempX;
  681. m_dY = dTempY;
  682. m_dZ = dTempZ;
  683. m_panimCur = panimTemp;
  684. }
  685. ////////////////////////////////////////////////////////////////////////////////
  686. // Called by editor to get the hotspot of an object in 2D.
  687. // (virtual (Overridden here)).
  688. ////////////////////////////////////////////////////////////////////////////////
  689. void CSentry::EditHotSpot( // Returns nothiing.
  690. short* psX, // Out: X coord of 2D hotspot relative to
  691. // EditRect() pos.
  692. short* psY) // Out: Y coord of 2D hotspot relative to
  693. // EditRect() pos.
  694. {
  695. // Get rectangle.
  696. RRect rc;
  697. EditRect(&rc);
  698. // Get 2D hotspot.
  699. short sX;
  700. short sY;
  701. Map3Dto2D(
  702. m_dXBase,
  703. m_dYBase,
  704. m_dZBase,
  705. &sX,
  706. &sY);
  707. // Get relation.
  708. *psX = sX - rc.sX;
  709. *psY = sY - rc.sY;
  710. }
  711. ////////////////////////////////////////////////////////////////////////////////
  712. // Called by editor to modify object
  713. ////////////////////////////////////////////////////////////////////////////////
  714. short CSentry::EditModify(void)
  715. {
  716. short sResult = 0;
  717. RGuiItem* pGuiItem = NULL;
  718. RGuiItem* pGui = RGuiItem::LoadInstantiate(FullPathVD("res/editor/sentry.gui"));
  719. if (pGui)
  720. {
  721. RListBox* pWeaponList = (RListBox*) pGui->GetItemFromId(4);
  722. REdit* peditAmmoCount = (REdit*) pGui->GetItemFromId(101);
  723. REdit* peditShotDelay = (REdit*) pGui->GetItemFromId(102);
  724. REdit* peditRange = (REdit*) pGui->GetItemFromId(103);
  725. REdit* peditRotVel = (REdit*) pGui->GetItemFromId(104);
  726. if ( pWeaponList && peditAmmoCount && peditShotDelay && peditRange && peditRotVel)
  727. {
  728. // Verify these are the type we think they are before accessing type specific
  729. // members.
  730. ASSERT(pWeaponList->m_type == RGuiItem::ListBox);
  731. ASSERT(peditAmmoCount->m_type == RGuiItem::Edit);
  732. ASSERT(peditShotDelay->m_type == RGuiItem::Edit);
  733. ASSERT(peditRange->m_type == RGuiItem::Edit);
  734. ASSERT(peditRotVel->m_type == RGuiItem::Edit);
  735. #if 0
  736. // Show which weapon is currently selected
  737. pWeaponList->SetSel(pGui->GetItemFromId(m_eWeaponType));
  738. #else
  739. // Empty list box. We don't want to modify the .GUI resource just yet
  740. // so we don't screw up people using the current .EXE on the server.
  741. pWeaponList->RemoveAll();
  742. // Fill in the list box with current available weapons.
  743. short i;
  744. for (i = 0; i < NumWeaponTypes; i++)
  745. {
  746. if ( (i != DeathWad || CStockPile::ms_sEnableDeathWad) &&
  747. (i != DoubleBarrel || CStockPile::ms_sEnableDoubleBarrel) &&
  748. (i != ProximityMine) &&
  749. (i != TimedMine) &&
  750. (i != RemoteControlMine) &&
  751. (i != BouncingBettyMine) )
  752. {
  753. pGuiItem = pWeaponList->AddString(ms_awdWeapons[i].pszName);
  754. if (pGuiItem != NULL)
  755. {
  756. // Store class ID so we can determine user selection
  757. pGuiItem->m_lId = ms_awdWeapons[i].id;
  758. }
  759. }
  760. }
  761. pWeaponList->AdjustContents();
  762. // Show which weapon is currently selected
  763. pGuiItem = pGui->GetItemFromId(m_eWeaponType);
  764. if (pGuiItem)
  765. {
  766. pWeaponList->SetSel(pGuiItem);
  767. pWeaponList->EnsureVisible(pGuiItem);
  768. }
  769. #endif
  770. // Set current Ammo Count
  771. peditAmmoCount->SetText("%d", m_sNumRounds);
  772. // Reflect changes
  773. peditAmmoCount->Compose();
  774. // Set current range
  775. peditRange->SetText("%d", rspSqrt(m_lSqDistRange));
  776. // Reflect changes
  777. peditRange->Compose();
  778. // Set current delay
  779. peditShotDelay->SetText("%d", m_lShootDelay);
  780. // Reflect changes
  781. peditShotDelay->Compose();
  782. // Set current rotational velocity
  783. peditRotVel->SetText("%d", (short) m_dAngularVelocity);
  784. peditRotVel->Compose();
  785. sResult = DoGui(pGui);
  786. if (sResult == 1)
  787. {
  788. {
  789. RGuiItem* pSelection = pWeaponList->GetSel();
  790. if (pSelection)
  791. {
  792. m_eWeaponType = pSelection->m_lId;
  793. }
  794. m_sNumRounds = RSP_SAFE_GUI_REF(peditAmmoCount, GetVal());
  795. long lDist = RSP_SAFE_GUI_REF(peditRange, GetVal());
  796. m_lSqDistRange = lDist * lDist;
  797. m_lShootDelay = RSP_SAFE_GUI_REF(peditShotDelay, GetVal());
  798. m_dAngularVelocity = (double) RSP_SAFE_GUI_REF(peditRotVel, GetVal());
  799. }
  800. }
  801. }
  802. }
  803. delete pGui;
  804. return 0;
  805. }
  806. ////////////////////////////////////////////////////////////////////////////////
  807. // Get all required resources
  808. ////////////////////////////////////////////////////////////////////////////////
  809. short CSentry::GetResources(void) // Returns 0 if successfull, non-zero otherwise
  810. {
  811. short sResult = 0;
  812. sResult = m_animShoot.Get(ms_apszShootResNames);
  813. if (sResult == 0)
  814. {
  815. sResult = m_animStand.Get(ms_apszStandResNames);
  816. if (sResult == 0)
  817. {
  818. sResult = m_animDie.Get(ms_apszDieResNames);
  819. if (sResult == 0)
  820. {
  821. sResult = m_animBaseStand.Get(ms_apszBaseStandResNames);
  822. if (sResult == 0)
  823. {
  824. sResult = m_animBaseDie.Get(ms_apszBaseDieResNames);
  825. if (sResult == 0)
  826. {
  827. // Add new animation loads here
  828. }
  829. else
  830. {
  831. TRACE("CSentry::GetResources - Failed to load base damaged animation\n");
  832. }
  833. }
  834. else
  835. {
  836. TRACE("CSentry::GetResources - Failed to load base still animation\n");
  837. }
  838. }
  839. else
  840. {
  841. TRACE("CSentry::GetResources - Failed to load turret damaged animation\n");
  842. }
  843. }
  844. else
  845. {
  846. TRACE("CSentry::GetResources - Failed to open 3D turret still animation\n");
  847. }
  848. }
  849. else
  850. {
  851. TRACE("CSentry::GetResources - Failed to open 3D turret shoot animation\n");
  852. }
  853. return sResult;
  854. }
  855. ////////////////////////////////////////////////////////////////////////////////
  856. // Free all resources
  857. ////////////////////////////////////////////////////////////////////////////////
  858. short CSentry::FreeResources(void) // Returns 0 if successfull, non-zero otherwise
  859. {
  860. m_animShoot.Release();
  861. m_animStand.Release();
  862. m_animDie.Release();
  863. m_animBaseStand.Release();
  864. m_animBaseDie.Release();
  865. return 0;
  866. }
  867. ////////////////////////////////////////////////////////////////////////////////
  868. // Message handlers
  869. ////////////////////////////////////////////////////////////////////////////////
  870. ////////////////////////////////////////////////////////////////////////////////
  871. // Shot Message
  872. ////////////////////////////////////////////////////////////////////////////////
  873. void CSentry::OnShotMsg(Shot_Message* pMessage)
  874. {
  875. // Audible and visual feedback.
  876. short sSound = GetRandom() % 3;
  877. switch (sSound)
  878. {
  879. case 0:
  880. PlaySample(g_smidShotSentry1, SampleMaster::Weapon);
  881. break;
  882. case 1:
  883. PlaySample(g_smidShotSentry2, SampleMaster::Weapon);
  884. break;
  885. case 2:
  886. PlaySample(g_smidShotSentry3, SampleMaster::Weapon);
  887. break;
  888. }
  889. // X/Z position depends on angle of shot (it is opposite).
  890. short sDeflectionAngle = rspMod360(pMessage->sAngle + 180);
  891. double dHitX = m_dX + COSQ[sDeflectionAngle] * HULL_RADIUS + RAND_SWAY(4);
  892. double dHitZ = m_dZ - SINQ[sDeflectionAngle] * HULL_RADIUS + RAND_SWAY(4);
  893. StartAnim(
  894. SENTRY_HIT_RES_NAME,
  895. dHitX,
  896. m_dY + RAND_SWAY(10),
  897. dHitZ,
  898. false);
  899. // Fow now we made the sentry bulletproof, the only
  900. // way it can be destroyed is by blowing it up.
  901. }
  902. ////////////////////////////////////////////////////////////////////////////////
  903. // Explosion message
  904. ////////////////////////////////////////////////////////////////////////////////
  905. void CSentry::OnExplosionMsg(Explosion_Message* pMessage)
  906. {
  907. if (
  908. m_state != State_BlownUp &&
  909. m_state != State_Die &&
  910. m_state != State_Dead)
  911. {
  912. CCharacter::OnExplosionMsg(pMessage);
  913. m_ePreviousState = m_state;
  914. m_state = State_BlownUp;
  915. m_panimPrev = m_panimCur;
  916. m_panimCur = &m_animDie;
  917. m_lAnimTime = 0;
  918. m_stockpile.m_sHitPoints = 0;
  919. m_lTimer = m_pRealm->m_time.GetGameTime();
  920. m_dExtHorzVel *= 1.4; //2.5;
  921. m_dExtVertVel *= 1.1; //1.4;
  922. // Send it spinning.
  923. m_dExtRotVelY = GetRandom() % 720;
  924. m_dExtRotVelZ = GetRandom() % 720;
  925. // Show the gun as damaged
  926. m_panimCurBase = &m_animBaseDie;
  927. m_panimCur = &m_animDie;
  928. }
  929. }
  930. ////////////////////////////////////////////////////////////////////////////////
  931. // Burning message
  932. ////////////////////////////////////////////////////////////////////////////////
  933. void CSentry::OnBurnMsg(Burn_Message* pMessage)
  934. {
  935. // For now we made the sentry fireproof, the only
  936. // way it can be destroyed is by blowing it up.
  937. }
  938. ////////////////////////////////////////////////////////////////////////////////
  939. // EOF
  940. ////////////////////////////////////////////////////////////////////////////////