armedgameobj.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** Confidential - Westwood Studios ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : Commando *
  23. * *
  24. * $Archive:: /Commando/Code/Combat/armedgameobj.cpp $*
  25. * *
  26. * $Author:: Greg_h $*
  27. * *
  28. * $Modtime:: 12/20/01 7:10p $*
  29. * *
  30. * $Revision:: 38 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. /*
  36. ** Includes
  37. */
  38. #include "armedgameobj.h"
  39. #include "debug.h"
  40. #include "weaponbag.h"
  41. #include "weapons.h"
  42. #include "rendobj.h"
  43. #include "bitpackids.h"
  44. #include "wwprofile.h"
  45. #include "smartgameobj.h"
  46. /*
  47. ** ArmedGameObjDef
  48. */
  49. ArmedGameObjDef::ArmedGameObjDef( void ) :
  50. WeaponTiltRate( 1.0f ),
  51. WeaponTiltMin( -10000.0f ),
  52. WeaponTiltMax( 10000.0f ),
  53. WeaponTurnRate( 1.0f ),
  54. WeaponTurnMin( -10000.0f ),
  55. WeaponTurnMax( 10000.0f ),
  56. WeaponError( 0 ),
  57. WeaponDefID( 0 ),
  58. SecondaryWeaponDefID( 0 ),
  59. WeaponRounds( -1 )
  60. {
  61. EDITABLE_PARAM( ArmedGameObjDef, ParameterClass::TYPE_ANGLE, WeaponTiltRate );
  62. EDITABLE_PARAM( ArmedGameObjDef, ParameterClass::TYPE_ANGLE, WeaponTiltMin );
  63. EDITABLE_PARAM( ArmedGameObjDef, ParameterClass::TYPE_ANGLE, WeaponTiltMax );
  64. EDITABLE_PARAM( ArmedGameObjDef, ParameterClass::TYPE_ANGLE, WeaponTurnRate );
  65. EDITABLE_PARAM( ArmedGameObjDef, ParameterClass::TYPE_ANGLE, WeaponTurnMin );
  66. EDITABLE_PARAM( ArmedGameObjDef, ParameterClass::TYPE_ANGLE, WeaponTurnMax );
  67. EDITABLE_PARAM( ArmedGameObjDef, ParameterClass::TYPE_ANGLE, WeaponError );
  68. EDITABLE_PARAM( ArmedGameObjDef, ParameterClass::TYPE_WEAPONOBJDEFINITIONID, WeaponDefID );
  69. EDITABLE_PARAM( ArmedGameObjDef, ParameterClass::TYPE_INT, WeaponRounds );
  70. EDITABLE_PARAM( ArmedGameObjDef, ParameterClass::TYPE_WEAPONOBJDEFINITIONID, SecondaryWeaponDefID );
  71. }
  72. enum {
  73. CHUNKID_DEF_PARENT = 418001829,
  74. CHUNKID_DEF_VARIABLES,
  75. MICROCHUNKID_DEF_WEAPON_TILT_RATE = 1,
  76. MICROCHUNKID_DEF_WEAPON_TILT_MIN,
  77. MICROCHUNKID_DEF_WEAPON_TILT_MAX,
  78. MICROCHUNKID_DEF_WEAPON_TURN_RATE,
  79. MICROCHUNKID_DEF_WEAPON_TURN_MIN,
  80. MICROCHUNKID_DEF_WEAPON_TURN_MAX,
  81. XXXMICROCHUNKID_DEF_PRIMARY_ROUNDS,
  82. XXXMICROCHUNKID_DEF_PRIMARY_AMMO_WEAPON_DEF_ID,
  83. XXXMICROCHUNKID_DEF_SECONDARY_AMMO_WEAPON_DEF_ID,
  84. XXXMICROCHUNKID_DEF_SECONDARY_ROUNDS,
  85. MICROCHUNKID_DEF_WEAPON_DEF_ID,
  86. MICROCHUNKID_DEF_WEAPON_ROUNDS,
  87. MICROCHUNKID_DEF_WEAPON_ERROR,
  88. MICROCHUNKID_DEF_SECONDARY_WEAPON_DEF_ID,
  89. };
  90. bool ArmedGameObjDef::Save( ChunkSaveClass & csave )
  91. {
  92. csave.Begin_Chunk( CHUNKID_DEF_PARENT );
  93. PhysicalGameObjDef::Save( csave );
  94. csave.End_Chunk();
  95. csave.Begin_Chunk( CHUNKID_DEF_VARIABLES );
  96. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_WEAPON_TILT_RATE, WeaponTiltRate );
  97. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_WEAPON_TILT_MIN, WeaponTiltMin );
  98. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_WEAPON_TILT_MAX, WeaponTiltMax );
  99. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_WEAPON_TURN_RATE, WeaponTurnRate );
  100. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_WEAPON_TURN_MIN, WeaponTurnMin );
  101. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_WEAPON_TURN_MAX, WeaponTurnMax );
  102. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_WEAPON_DEF_ID, WeaponDefID );
  103. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_SECONDARY_WEAPON_DEF_ID, SecondaryWeaponDefID );
  104. WRITE_SAFE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_WEAPON_ROUNDS, WeaponRounds, int );
  105. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_WEAPON_ERROR, WeaponError );
  106. csave.End_Chunk();
  107. return true;
  108. }
  109. bool ArmedGameObjDef::Load( ChunkLoadClass &cload )
  110. {
  111. while (cload.Open_Chunk()) {
  112. switch(cload.Cur_Chunk_ID()) {
  113. case CHUNKID_DEF_PARENT:
  114. PhysicalGameObjDef::Load( cload );
  115. break;
  116. case CHUNKID_DEF_VARIABLES:
  117. while (cload.Open_Micro_Chunk()) {
  118. switch(cload.Cur_Micro_Chunk_ID()) {
  119. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_WEAPON_TILT_RATE, WeaponTiltRate );
  120. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_WEAPON_TILT_MIN, WeaponTiltMin );
  121. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_WEAPON_TILT_MAX, WeaponTiltMax );
  122. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_WEAPON_TURN_RATE, WeaponTurnRate );
  123. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_WEAPON_TURN_MIN, WeaponTurnMin );
  124. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_WEAPON_TURN_MAX, WeaponTurnMax );
  125. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_WEAPON_DEF_ID, WeaponDefID );
  126. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_SECONDARY_WEAPON_DEF_ID, SecondaryWeaponDefID );
  127. READ_SAFE_MICRO_CHUNK( cload, MICROCHUNKID_DEF_WEAPON_ROUNDS, WeaponRounds, int );
  128. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_WEAPON_ERROR, WeaponError );
  129. default:
  130. Debug_Say(( "Unrecognized ArmedDef Variable chunkID %d\n", cload.Cur_Micro_Chunk_ID() ));
  131. break;
  132. }
  133. cload.Close_Micro_Chunk();
  134. }
  135. break;
  136. default:
  137. Debug_Say(( "Unrecognized ArmedDef chunkID\n" ));
  138. break;
  139. }
  140. cload.Close_Chunk();
  141. }
  142. return true;
  143. }
  144. /*
  145. ** ArmedGameObj
  146. */
  147. ArmedGameObj::ArmedGameObj( void ) :
  148. MuzzleA0Bone( 0 ),
  149. MuzzleA1Bone( 0 ),
  150. MuzzleB0Bone( 0 ),
  151. MuzzleB1Bone( 0 ),
  152. TargetingPos(0,0,0)
  153. {
  154. WeaponBag = new WeaponBagClass( this );
  155. }
  156. ArmedGameObj::~ArmedGameObj( void )
  157. {
  158. delete WeaponBag;
  159. }
  160. /*
  161. **
  162. */
  163. void ArmedGameObj::Init( const ArmedGameObjDef & definition )
  164. {
  165. PhysicalGameObj::Init( definition );
  166. Copy_Settings( definition );
  167. return ;
  168. }
  169. /*
  170. **
  171. */
  172. void ArmedGameObj::Copy_Settings( const ArmedGameObjDef & definition )
  173. {
  174. WeaponClass * weapon = NULL;
  175. if ( definition.WeaponDefID != 0 ) {
  176. weapon = WeaponBag->Add_Weapon( definition.WeaponDefID, definition.WeaponRounds );
  177. }
  178. if ( definition.SecondaryWeaponDefID != 0 ) {
  179. WeaponClass * s_weapon = WeaponBag->Add_Weapon( definition.SecondaryWeaponDefID, definition.WeaponRounds );
  180. if ( weapon == NULL ) {
  181. weapon = s_weapon;
  182. }
  183. }
  184. if ( weapon != NULL ) {
  185. WeaponBag->Select_Weapon( weapon );
  186. }
  187. Init_Muzzle_Bones();
  188. return ;
  189. }
  190. /*
  191. **
  192. */
  193. void ArmedGameObj::Re_Init( const ArmedGameObjDef & definition )
  194. {
  195. PhysicalGameObj::Re_Init( definition );
  196. //
  197. // Remove all non-beacon entries from the weapon bag...
  198. //
  199. WeaponBagClass *old_bag = WeaponBag;
  200. if ( WeaponBag != NULL ) {
  201. //
  202. // Loop over all the weapons in the bag
  203. //
  204. int weapon_index = WeaponBag->Get_Count();
  205. while (weapon_index --) {
  206. WeaponClass *weapon = WeaponBag->Peek_Weapon( weapon_index );
  207. //
  208. // If this isn't a beacon, then remove it
  209. //
  210. if (weapon != NULL && weapon->Get_Definition ()->Style != WEAPON_HOLD_STYLE_BEACON) {
  211. WeaponBag->Remove_Weapon( weapon_index );
  212. }
  213. }
  214. WeaponBag = NULL;
  215. }
  216. //
  217. // Re-initialize the weapon bag
  218. //
  219. WeaponBag = new WeaponBagClass( this );
  220. //
  221. // Copy any internal settings from the definition
  222. //
  223. Copy_Settings( definition );
  224. //
  225. // Now add any beacons back into the weapon bag
  226. //
  227. if ( old_bag != NULL ) {
  228. WeaponBag->Move_Contents( old_bag );
  229. delete old_bag;
  230. }
  231. return ;
  232. }
  233. const ArmedGameObjDef & ArmedGameObj::Get_Definition( void ) const
  234. {
  235. return (const ArmedGameObjDef &)BaseGameObj::Get_Definition();
  236. }
  237. /*
  238. ** ArmedGameObj Save and Load
  239. */
  240. enum {
  241. CHUNKID_PARENT = 418001841,
  242. CHUNKID_VARIABLES,
  243. CHUNKID_WEAPONBAG,
  244. MICROCHUNKID_TARGETING_POS = 1,
  245. };
  246. bool ArmedGameObj::Save( ChunkSaveClass & csave )
  247. {
  248. csave.Begin_Chunk( CHUNKID_PARENT );
  249. PhysicalGameObj::Save( csave );
  250. csave.End_Chunk();
  251. csave.Begin_Chunk( CHUNKID_VARIABLES );
  252. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TARGETING_POS, TargetingPos );
  253. csave.End_Chunk();
  254. csave.Begin_Chunk( CHUNKID_WEAPONBAG );
  255. WeaponBag->Save( csave );
  256. csave.End_Chunk();
  257. return true;
  258. }
  259. bool ArmedGameObj::Load( ChunkLoadClass &cload )
  260. {
  261. while (cload.Open_Chunk()) {
  262. switch(cload.Cur_Chunk_ID()) {
  263. case CHUNKID_PARENT:
  264. PhysicalGameObj::Load( cload );
  265. break;
  266. case CHUNKID_VARIABLES:
  267. while (cload.Open_Micro_Chunk()) {
  268. switch(cload.Cur_Micro_Chunk_ID()) {
  269. READ_MICRO_CHUNK( cload, MICROCHUNKID_TARGETING_POS, TargetingPos );
  270. default:
  271. Debug_Say(( "Unrecognized ArmedGameObj Variable chunkID\n" ));
  272. break;
  273. }
  274. cload.Close_Micro_Chunk();
  275. }
  276. break;
  277. case CHUNKID_WEAPONBAG:
  278. WeaponBag->Load( cload );
  279. break;
  280. default:
  281. Debug_Say(( "Unrecognized ArmedGameObj chunkID\n" ));
  282. break;
  283. }
  284. cload.Close_Chunk();
  285. }
  286. SaveLoadSystemClass::Register_Post_Load_Callback(this);
  287. return true;
  288. }
  289. void ArmedGameObj::On_Post_Load( void )
  290. {
  291. PhysicalGameObj::On_Post_Load();
  292. Init_Muzzle_Bones();
  293. }
  294. //-----------------------------------------------------------------------------
  295. void ArmedGameObj::Import_Frequent(BitStreamClass & packet)
  296. {
  297. PhysicalGameObj::Import_Frequent( packet );
  298. Vector3 targeting_pos;
  299. packet.Get(targeting_pos.X, BITPACK_WORLD_POSITION_X);
  300. packet.Get(targeting_pos.Y, BITPACK_WORLD_POSITION_Y);
  301. packet.Get(targeting_pos.Z, BITPACK_WORLD_POSITION_Z);
  302. //
  303. // Don't force the targetting if the object is controlled
  304. // by this player
  305. //
  306. SmartGameObj *smart_game_obj = As_SmartGameObj ();
  307. if (smart_game_obj == NULL || smart_game_obj->Is_Controlled_By_Me() == false) {
  308. Set_Targeting(targeting_pos);
  309. }
  310. return ;
  311. }
  312. //-----------------------------------------------------------------------------
  313. void ArmedGameObj::Export_Frequent(BitStreamClass & packet)
  314. {
  315. PhysicalGameObj::Export_Frequent( packet );
  316. packet.Add(TargetingPos.X, BITPACK_WORLD_POSITION_X);
  317. packet.Add(TargetingPos.Y, BITPACK_WORLD_POSITION_Y);
  318. packet.Add(TargetingPos.Z, BITPACK_WORLD_POSITION_Z);
  319. }
  320. //-----------------------------------------------------------------------------
  321. void ArmedGameObj::Export_State_Cs(BitStreamClass & packet)
  322. {
  323. #if 1
  324. Vector3 my_pos;
  325. Get_Position( &my_pos );
  326. Vector3 rel_target = TargetingPos - my_pos;
  327. packet.Add(rel_target.X, BITPACK_WORLD_POSITION_X);
  328. packet.Add(rel_target.Y, BITPACK_WORLD_POSITION_Y);
  329. packet.Add(rel_target.Z, BITPACK_WORLD_POSITION_Z);
  330. #else
  331. packet.Add(TargetingPos.X, BITPACK_WORLD_POSITION_X);
  332. packet.Add(TargetingPos.Y, BITPACK_WORLD_POSITION_Y);
  333. packet.Add(TargetingPos.Z, BITPACK_WORLD_POSITION_Z);
  334. #endif
  335. }
  336. //-----------------------------------------------------------------------------
  337. void ArmedGameObj::Import_State_Cs(BitStreamClass & packet)
  338. {
  339. #if 1
  340. Vector3 rel_target;
  341. packet.Get(rel_target.X, BITPACK_WORLD_POSITION_X);
  342. packet.Get(rel_target.Y, BITPACK_WORLD_POSITION_Y);
  343. packet.Get(rel_target.Z, BITPACK_WORLD_POSITION_Z);
  344. Vector3 my_pos;
  345. Get_Position( &my_pos );
  346. Vector3 targeting_pos = rel_target + my_pos;
  347. Set_Targeting(targeting_pos);
  348. #else
  349. Vector3 targeting_pos;
  350. packet.Get(targeting_pos.X, BITPACK_WORLD_POSITION_X);
  351. packet.Get(targeting_pos.Y, BITPACK_WORLD_POSITION_Y);
  352. packet.Get(targeting_pos.Z, BITPACK_WORLD_POSITION_Z);
  353. Set_Targeting(targeting_pos);
  354. #endif
  355. }
  356. void ArmedGameObj::Post_Think( void )
  357. {
  358. PhysicalGameObj::Post_Think();
  359. WWPROFILE( "Armed PostThink" );
  360. if ( Is_Delete_Pending() ) { // don't update if destroying... (so we don't create a new laser!)
  361. return;
  362. }
  363. if ( Get_Weapon() != NULL ) { // Update the weapon after the commands and update_human_animation
  364. Get_Weapon()->Update();
  365. }
  366. // allow any recoil animation to progress
  367. if (Peek_Model() != NULL) {
  368. for (int i=0; i<MAX_MUZZLES; i++) {
  369. MuzzleRecoilController[i].Update(Peek_Model());
  370. }
  371. }
  372. }
  373. /*
  374. ** Weapons
  375. */
  376. WeaponClass * ArmedGameObj::Get_Weapon( void )
  377. {
  378. return WeaponBag->Get_Weapon();
  379. }
  380. void ArmedGameObj::Init_Muzzle_Bones( void )
  381. {
  382. MuzzleA0Bone = Peek_Model()->Get_Bone_Index( "muzzlea0" );
  383. MuzzleA1Bone = Peek_Model()->Get_Bone_Index( "muzzlea1" );
  384. MuzzleB0Bone = Peek_Model()->Get_Bone_Index( "muzzleb0" );
  385. MuzzleB1Bone = Peek_Model()->Get_Bone_Index( "muzzleb1" );
  386. if (MuzzleA1Bone == 0) {
  387. MuzzleA1Bone = MuzzleA0Bone;
  388. }
  389. if (MuzzleB0Bone == 0) {
  390. MuzzleB0Bone = MuzzleA0Bone;
  391. }
  392. if (MuzzleB1Bone == 0) {
  393. MuzzleB1Bone = MuzzleB0Bone;
  394. }
  395. MuzzleRecoilController[0].Init(MuzzleA0Bone);
  396. MuzzleRecoilController[1].Init(MuzzleA1Bone);
  397. MuzzleRecoilController[2].Init(MuzzleB0Bone);
  398. MuzzleRecoilController[3].Init(MuzzleB1Bone);
  399. // Let the weapon learn about muzzle flashes
  400. if ( Get_Weapon() ) {
  401. Get_Weapon()->Set_Model( Peek_Model() );
  402. }
  403. }
  404. bool ArmedGameObj::Set_Targeting( const Vector3 & pos, bool do_tilt )
  405. {
  406. TargetingPos = pos;
  407. // Move the turret to match the target
  408. return true;
  409. }
  410. const Matrix3D & ArmedGameObj::Get_Muzzle( int index )
  411. {
  412. RenderObjClass * model = Peek_Model();
  413. if ( index == 3 && MuzzleB1Bone != 0 ) {
  414. return model->Get_Bone_Transform( MuzzleB1Bone );
  415. }
  416. if ( index >= 2 && MuzzleB0Bone != 0 ) {
  417. return model->Get_Bone_Transform( MuzzleB0Bone );
  418. }
  419. if ( index == 1 && MuzzleA1Bone != 0 ) {
  420. return model->Get_Bone_Transform( MuzzleA1Bone );
  421. }
  422. if ( MuzzleA0Bone != 0 ) {
  423. return model->Get_Bone_Transform( MuzzleA0Bone );
  424. }
  425. return Get_Transform();
  426. }
  427. bool ArmedGameObj::Muzzle_Exists( int index )
  428. {
  429. if ( index == 0 ) {
  430. return MuzzleA0Bone != 0;
  431. }
  432. if ( index == 1 ) {
  433. return MuzzleA1Bone != 0;
  434. }
  435. if ( index == 2 ) {
  436. return MuzzleB0Bone != 0;
  437. }
  438. if ( index == 3 ) {
  439. return MuzzleB1Bone != 0;
  440. }
  441. return false;
  442. }
  443. void ArmedGameObj::Start_Recoil( int muzzle_index,float recoil_scale,float recoil_time )
  444. {
  445. WWASSERT(muzzle_index >= 0);
  446. WWASSERT(muzzle_index < MAX_MUZZLES);
  447. MuzzleRecoilController[muzzle_index].Start_Recoil(recoil_scale,recoil_time);
  448. }