c4.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906
  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/c4.cpp $*
  25. * *
  26. * $Author:: Byon_g $*
  27. * *
  28. * $Modtime:: 2/12/02 10:25a $*
  29. * *
  30. * $Revision:: 72 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "c4.h"
  36. #include "debug.h"
  37. #include "phys.h"
  38. #include "combat.h"
  39. #include "explosion.h"
  40. #include "soldier.h"
  41. #include "persistfactory.h"
  42. #include "combatchunkid.h"
  43. #include "weaponmanager.h"
  44. #include "simpledefinitionfactory.h"
  45. #include "wwhack.h"
  46. #include "decophys.h"
  47. #include "assets.h"
  48. #include "gameobjmanager.h"
  49. #include "wwaudio.h"
  50. #include "wwprofile.h"
  51. #include "projectile.h"
  52. #include "wwphysids.h"
  53. #include "buildingaggregate.h"
  54. #include "rendobj.h"
  55. #include "definitionmgr.h"
  56. #include "apppackettypes.h"
  57. #include "bitpackids.h"
  58. #include "surfaceeffects.h"
  59. #include "gametype.h"
  60. /*
  61. ** C4GameObjDef
  62. */
  63. DECLARE_FORCE_LINK( C4 )
  64. SimplePersistFactoryClass<C4GameObjDef, CHUNKID_GAME_OBJECT_DEF_C4> _C4GameObjDefPersistFactory;
  65. DECLARE_DEFINITION_FACTORY(C4GameObjDef, CLASSID_GAME_OBJECT_DEF_C4, "C4") _C4GameObjDefDefFactory;
  66. C4GameObjDef::C4GameObjDef( void ) :
  67. ThrowVelocity( 5 )
  68. {
  69. EDITABLE_PARAM (C4GameObjDef, ParameterClass::TYPE_FLOAT, ThrowVelocity);
  70. }
  71. uint32 C4GameObjDef::Get_Class_ID (void) const
  72. {
  73. return CLASSID_GAME_OBJECT_DEF_C4;
  74. }
  75. PersistClass * C4GameObjDef::Create( void ) const
  76. {
  77. C4GameObj * obj = new C4GameObj;
  78. obj->Init( *this );
  79. return obj;
  80. }
  81. enum {
  82. CHUNKID_DEF_PARENT = 930991700,
  83. CHUNKID_DEF_VARIABLES,
  84. MICROCHUNKID_DEF_THROW_VELOCITY = 1,
  85. };
  86. bool C4GameObjDef::Save( ChunkSaveClass & csave )
  87. {
  88. csave.Begin_Chunk( CHUNKID_DEF_PARENT );
  89. PhysicalGameObjDef::Save( csave );
  90. csave.End_Chunk();
  91. csave.Begin_Chunk( CHUNKID_DEF_VARIABLES );
  92. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_THROW_VELOCITY, ThrowVelocity );
  93. csave.End_Chunk();
  94. return true;
  95. }
  96. bool C4GameObjDef::Load( ChunkLoadClass &cload )
  97. {
  98. while (cload.Open_Chunk()) {
  99. switch(cload.Cur_Chunk_ID()) {
  100. case CHUNKID_DEF_PARENT:
  101. PhysicalGameObjDef::Load( cload );
  102. break;
  103. case CHUNKID_DEF_VARIABLES:
  104. while (cload.Open_Micro_Chunk()) {
  105. switch(cload.Cur_Micro_Chunk_ID()) {
  106. READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_THROW_VELOCITY, ThrowVelocity );
  107. default:
  108. Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__));
  109. break;
  110. }
  111. cload.Close_Micro_Chunk();
  112. }
  113. break;
  114. default:
  115. Debug_Say(( "Unrecognized SimpleDef chunkID\n" ));
  116. break;
  117. }
  118. cload.Close_Chunk();
  119. }
  120. return true;
  121. }
  122. const PersistFactoryClass & C4GameObjDef::Get_Factory (void) const
  123. {
  124. return _C4GameObjDefPersistFactory;
  125. }
  126. /*
  127. **
  128. */
  129. SimplePersistFactoryClass<C4GameObj, CHUNKID_GAME_OBJECT_C4> _C4GameObjPersistFactory;
  130. const PersistFactoryClass & C4GameObj::Get_Factory (void) const
  131. {
  132. return _C4GameObjPersistFactory;
  133. }
  134. /*
  135. **
  136. */
  137. C4GameObj::C4GameObj( void ) :
  138. AmmoDefinition( NULL ),
  139. Stuck( false ),
  140. StuckMCT( false ),
  141. StuckToObject( false ),
  142. StuckBone( 0 ),
  143. StuckStaticAnimObj(NULL),
  144. OwnerBackup( NULL ),
  145. Age( 0 )
  146. {
  147. Set_App_Packet_Type(APPPACKETTYPE_C4);
  148. }
  149. C4GameObj::~C4GameObj()
  150. {
  151. REF_PTR_RELEASE(StuckStaticAnimObj);
  152. }
  153. /*
  154. **
  155. */
  156. void C4GameObj::Init( void )
  157. {
  158. Init( Get_Definition() );
  159. }
  160. /*
  161. **
  162. */
  163. void C4GameObj::Init( const C4GameObjDef & definition )
  164. {
  165. SimpleGameObj::Init( definition );
  166. Peek_Physical_Object()->Set_Collision_Group( TERRAIN_AND_BULLET_COLLISION_GROUP );
  167. Peek_Physical_Object()->Set_Collision_Group( DEFAULT_COLLISION_GROUP );
  168. }
  169. const C4GameObjDef & C4GameObj::Get_Definition( void ) const
  170. {
  171. return (const C4GameObjDef &)BaseGameObj::Get_Definition();
  172. }
  173. void C4GameObj::Init_C4( const AmmoDefinitionClass * def, SoldierGameObj *owner, int detonation_mode, const Matrix3D & tm )
  174. {
  175. WWASSERT( AmmoDefinition == NULL );
  176. AmmoDefinition = def;
  177. if ( !def->ModelName.Is_Empty() ) {
  178. Peek_Physical_Object()->Set_Model_By_Name( def->ModelName ) ;
  179. }
  180. Owner = owner;
  181. DetonationMode = detonation_mode;
  182. Set_Transform( tm );
  183. Stuck = false;
  184. StuckMCT = false;
  185. StuckToObject = false;
  186. Peek_Physical_Object()->Set_Collision_Group( DEFAULT_COLLISION_GROUP );
  187. OwnerBackup = NULL;
  188. if ( owner ) {
  189. Set_Player_Type( owner->Get_Player_Type() );
  190. OwnerBackup = owner->Get_Player_Data();
  191. if ( CombatManager::I_Am_Server() && !IS_MISSION ) {
  192. Maintain_C4_Limit( Get_Player_Type() );
  193. }
  194. }
  195. int type = AmmoDefinition->AmmoType;
  196. if ( type != AmmoDefinitionClass::AMMO_TYPE_C4_REMOTE ) {
  197. // Setup Arming Timer
  198. float time = def->C4TriggerTime1;
  199. if ( detonation_mode == 2 ) time = def->C4TriggerTime2;
  200. if ( detonation_mode == 3 ) time = def->C4TriggerTime3;
  201. Timer = time;
  202. }
  203. float sound_id = def->C4TimingSound1ID;
  204. if ( detonation_mode == 2 ) sound_id = def->C4TimingSound1ID;
  205. if ( detonation_mode == 3 ) sound_id = def->C4TimingSound1ID;
  206. if ( sound_id ) {
  207. RefCountedGameObjReference *owner_ref = new RefCountedGameObjReference(Owner);
  208. WWAudioClass::Get_Instance()->Create_Instant_Sound( sound_id, Get_Transform(), owner_ref);
  209. REF_PTR_RELEASE(owner_ref);
  210. }
  211. ProjectileClass * po = Peek_Physical_Object()->As_ProjectileClass();
  212. if ( po ) {
  213. po->Set_Velocity( tm.Get_X_Vector() * Get_Definition().ThrowVelocity );
  214. }
  215. //
  216. // "Dirty" the object for networking
  217. //
  218. Set_Object_Dirty_Bit( NetworkObjectClass::BIT_RARE, true );
  219. }
  220. CollisionReactionType C4GameObj::Collision_Occurred( const CollisionEventClass & event )
  221. {
  222. Debug_Say(( "C4 collision\n" ));
  223. // if ( ( !Stuck ) && (CombatManager::I_Am_Server()) )
  224. if (!Stuck)
  225. {
  226. // Figure out who/what/where we hit
  227. PhysicalGameObj * other = NULL;
  228. BuildingGameObj * building = NULL;
  229. bool hit_projectile = event.OtherObj->As_ProjectileClass() != NULL;
  230. if ( event.OtherObj->Get_Observer() != NULL ) {
  231. other = ((CombatPhysObserverClass *)event.OtherObj->Get_Observer())->As_PhysicalGameObj();
  232. building = ((CombatPhysObserverClass *)event.OtherObj->Get_Observer())->As_BuildingGameObj();
  233. }
  234. Restore_Owner();
  235. // Ignore my owner and my owners vehicle
  236. if ( other != NULL && Get_Owner() != NULL ) {
  237. VehicleGameObj * vehicle = other->As_VehicleGameObj();
  238. SoldierGameObj * soldier = Get_Owner()->As_SoldierGameObj();
  239. if ( vehicle != NULL && vehicle == soldier->Get_Vehicle() ) {
  240. return COLLISION_REACTION_NO_BOUNCE;
  241. }
  242. if ( other == Get_Owner() ) {
  243. return COLLISION_REACTION_NO_BOUNCE;
  244. }
  245. // if ( !other->Is_Teammate( Get_Owner() ) )
  246. {
  247. Debug_Say(( "Sticking to game object %p (%p)\n", other, Get_Owner() ));
  248. Stuck = true;
  249. StuckToObject = true;
  250. StuckObject = other;
  251. StuckBone = 0;
  252. RenderObjClass * parent_model = other->Peek_Model();
  253. if ( parent_model ) {
  254. StuckBone = parent_model->Get_Sub_Object_Bone_Index( event.CollidedRenderObj );
  255. }
  256. Vector3 my_pos;
  257. Get_Position(&my_pos);
  258. Matrix3D::Inverse_Transform_Vector( parent_model->Get_Bone_Transform( StuckBone ), my_pos, &StuckOffset );
  259. Peek_Physical_Object()->Enable_User_Control( true );
  260. if (CombatManager::I_Am_Server()) {
  261. Set_Object_Dirty_Bit(NetworkObjectClass::BIT_RARE, true);
  262. }
  263. return COLLISION_REACTION_STOP_MOTION;
  264. }
  265. } else if ( building != NULL ) {
  266. // Stick to the building
  267. Stuck = true;
  268. StuckObject = (ScriptableGameObj*)building;
  269. StuckMCT = false;
  270. // Check for MCT collision
  271. if (event.OtherObj->Get_Factory().Chunk_ID() == PHYSICS_CHUNKID_BUILDINGAGGREGATE) {
  272. if (((BuildingAggregateClass *)event.OtherObj)->Is_MCT()) {
  273. StuckMCT = true;
  274. }
  275. }
  276. Peek_Physical_Object()->Enable_User_Control( true );
  277. if (CombatManager::I_Am_Server()) {
  278. Set_Object_Dirty_Bit(NetworkObjectClass::BIT_RARE, true);
  279. }
  280. return COLLISION_REACTION_STOP_MOTION;
  281. } else if ( other == NULL && !hit_projectile ) {
  282. // if this is a static anim, then try to stick to it
  283. if (event.OtherObj->As_StaticAnimPhysClass() != NULL) {
  284. Debug_Say(( "Sticking to static anim object %p (%p)\n", event.OtherObj ));
  285. REF_PTR_SET(StuckStaticAnimObj,(StaticAnimPhysClass *)event.OtherObj);
  286. StuckBone = 0;
  287. if ( StuckStaticAnimObj->Peek_Model() ) {
  288. StuckBone = StuckStaticAnimObj->Peek_Model()->Get_Sub_Object_Bone_Index( event.CollidedRenderObj );
  289. Vector3 my_pos;
  290. Get_Position(&my_pos);
  291. Matrix3D::Inverse_Transform_Vector( StuckStaticAnimObj->Peek_Model()->Get_Bone_Transform( StuckBone ), my_pos, &StuckOffset );
  292. }
  293. }
  294. // If we hit permiable, pass through
  295. if ( event.CollisionResult != NULL &&
  296. SurfaceEffectsManager::Is_Surface_Permeable( event.CollisionResult->SurfaceType ) ) {
  297. Debug_Say(( "C4 passes through permeable\n" ));
  298. return COLLISION_REACTION_NO_BOUNCE;
  299. }
  300. // We are hitting a static terrain, just stick
  301. Debug_Say(( "Sticking to terrain\n" ));
  302. Peek_Physical_Object()->Enable_User_Control( true );
  303. Stuck = true;
  304. if (CombatManager::I_Am_Server()) {
  305. Set_Object_Dirty_Bit(NetworkObjectClass::BIT_RARE, true);
  306. }
  307. return COLLISION_REACTION_STOP_MOTION;
  308. }
  309. }
  310. return COLLISION_REACTION_NO_BOUNCE;
  311. }
  312. /*
  313. ** C4GameObj Save and Load
  314. */
  315. enum {
  316. CHUNKID_PARENT = 922991750,
  317. CHUNKID_VARIABLES,
  318. XXXCHUNKID_C4_TIMER,
  319. CHUNKID_OWNER,
  320. CHUNKID_STUCK_OBJECT,
  321. XXXXMICROCHUNKID_PARAMS_NAME = 1,
  322. XXXXXMICROCHUNKID_PARAMS_NAME,
  323. MICROCHUNKID_AMMO_DEF_ID,
  324. MICROCHUNKID_DETONATION_MODE,
  325. MICROCHUNKID_TIMER,
  326. MICROCHUNKID_STUCK,
  327. MICROCHUNKID_STUCK_OFFSET,
  328. MICROCHUNKID_STUCK_MCT,
  329. MICROCHUNKID_STUCK_BONE,
  330. MICROCHUNKID_STUCK_STATIC_ANIM_OBJ_ID,
  331. MICROCHUNKID_STUCK_TO_OBJECT,
  332. MICROCHUNKID_AGE,
  333. };
  334. bool C4GameObj::Save( ChunkSaveClass & csave )
  335. {
  336. csave.Begin_Chunk( CHUNKID_PARENT );
  337. SimpleGameObj::Save( csave );
  338. csave.End_Chunk();
  339. csave.Begin_Chunk( CHUNKID_VARIABLES );
  340. int ammo_def_id = AmmoDefinition->Get_ID();
  341. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_AMMO_DEF_ID, ammo_def_id );
  342. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DETONATION_MODE, DetonationMode );
  343. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TIMER, Timer );
  344. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STUCK, Stuck );
  345. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STUCK_OFFSET, StuckOffset );
  346. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STUCK_MCT, StuckMCT );
  347. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STUCK_BONE, StuckBone );
  348. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STUCK_TO_OBJECT, StuckToObject );
  349. if (StuckStaticAnimObj != NULL) {
  350. uint32 id = StuckStaticAnimObj->Get_ID();
  351. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STUCK_STATIC_ANIM_OBJ_ID, id);
  352. }
  353. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_AGE, Age );
  354. csave.End_Chunk();
  355. if ( Owner != NULL ) {
  356. csave.Begin_Chunk( CHUNKID_OWNER );
  357. Owner.Save( csave );
  358. csave.End_Chunk();
  359. }
  360. if ( StuckObject != NULL ) {
  361. csave.Begin_Chunk( CHUNKID_STUCK_OBJECT );
  362. StuckObject.Save( csave );
  363. csave.End_Chunk();
  364. }
  365. return true;
  366. }
  367. bool C4GameObj::Load( ChunkLoadClass &cload )
  368. {
  369. REF_PTR_RELEASE(StuckStaticAnimObj);
  370. uint32 static_anim_obj_id = 0xFFFFFFFF;
  371. while (cload.Open_Chunk()) {
  372. switch(cload.Cur_Chunk_ID()) {
  373. case CHUNKID_PARENT:
  374. SimpleGameObj::Load( cload );
  375. break;
  376. case CHUNKID_VARIABLES:
  377. {
  378. int ammo_def_id = 0;
  379. while (cload.Open_Micro_Chunk()) {
  380. switch(cload.Cur_Micro_Chunk_ID()) {
  381. READ_MICRO_CHUNK( cload, MICROCHUNKID_AMMO_DEF_ID, ammo_def_id );
  382. READ_MICRO_CHUNK( cload, MICROCHUNKID_DETONATION_MODE, DetonationMode );
  383. READ_MICRO_CHUNK( cload, MICROCHUNKID_TIMER, Timer );
  384. READ_MICRO_CHUNK( cload, MICROCHUNKID_STUCK, Stuck );
  385. READ_MICRO_CHUNK( cload, MICROCHUNKID_STUCK_OFFSET, StuckOffset );
  386. READ_MICRO_CHUNK( cload, MICROCHUNKID_STUCK_MCT, StuckMCT );
  387. READ_MICRO_CHUNK( cload, MICROCHUNKID_STUCK_BONE, StuckBone );
  388. READ_MICRO_CHUNK( cload, MICROCHUNKID_STUCK_STATIC_ANIM_OBJ_ID, static_anim_obj_id);
  389. READ_MICRO_CHUNK( cload, MICROCHUNKID_STUCK_TO_OBJECT, StuckToObject );
  390. READ_MICRO_CHUNK( cload, MICROCHUNKID_AGE, Age );
  391. default:
  392. Debug_Say(( "Unrecognized C4 Variable chunkID\n" ));
  393. break;
  394. }
  395. cload.Close_Micro_Chunk();
  396. }
  397. WWASSERT( AmmoDefinition == NULL );
  398. AmmoDefinition = WeaponManager::Find_Ammo_Definition( ammo_def_id );
  399. WWASSERT( AmmoDefinition != NULL );
  400. break;
  401. }
  402. case CHUNKID_OWNER:
  403. Owner.Load( cload );
  404. break;
  405. case CHUNKID_STUCK_OBJECT:
  406. StuckObject.Load( cload );
  407. break;
  408. default:
  409. Debug_Say(( "Unrecognized C4 chunkID\n" ));
  410. break;
  411. }
  412. cload.Close_Chunk();
  413. }
  414. if (static_anim_obj_id != 0xFFFFFFFF) {
  415. StaticPhysClass * pobj = PhysicsSceneClass::Get_Instance()->Get_Static_Object_By_ID(static_anim_obj_id);
  416. if (pobj && (pobj->As_StaticAnimPhysClass() != NULL)) {
  417. REF_PTR_SET(StuckStaticAnimObj,(StaticAnimPhysClass *)pobj);
  418. }
  419. REF_PTR_RELEASE(pobj);
  420. }
  421. return true;
  422. }
  423. void C4GameObj::Think( void )
  424. {
  425. SimpleGameObj::Think();
  426. WWPROFILE( "C4 Think" );
  427. if ( !CombatManager::I_Am_Server() ) {
  428. return;
  429. }
  430. Age += TimeManager::Get_Frame_Seconds();
  431. int type = AmmoDefinition->AmmoType;
  432. WWASSERT( type >= AmmoDefinitionClass::AMMO_TYPE_C4_REMOTE );
  433. WWASSERT( type <= AmmoDefinitionClass::AMMO_TYPE_C4_PROXIMITY );
  434. Restore_Owner();
  435. if ( type == AmmoDefinitionClass::AMMO_TYPE_C4_REMOTE ) {
  436. if ( CombatManager::I_Am_Server() ) {
  437. // Check for owner to detonate
  438. SoldierGameObj * owner = Get_Owner();
  439. if ( owner == NULL ) {
  440. Defuse();
  441. } else if ( owner->Detonate_C4() ) {
  442. Detonate();
  443. }
  444. } else {
  445. Set_Delete_Pending();
  446. }
  447. }
  448. if ( type == AmmoDefinitionClass::AMMO_TYPE_C4_TIMED ) {
  449. // Check for time's up
  450. Timer -= TimeManager::Get_Frame_Seconds();
  451. if ( Timer <= 0 ) {
  452. Detonate();
  453. }
  454. }
  455. if ( type == AmmoDefinitionClass::AMMO_TYPE_C4_PROXIMITY ) {
  456. Timer -= TimeManager::Get_Frame_Seconds();
  457. if ( Timer <= 0 ) {
  458. // Reset Timer
  459. // Timer += 1; // Check every second
  460. Timer += 0.25; // Check every 1/4 second
  461. // Check for smart objs in proximity
  462. float trigger_range = AmmoDefinition->C4TriggerRange1;
  463. if ( DetonationMode == 2 ) trigger_range = AmmoDefinition->C4TriggerRange2;
  464. if ( DetonationMode == 3 ) trigger_range = AmmoDefinition->C4TriggerRange3;
  465. Vector3 c4_pos;
  466. Get_Position( &c4_pos );
  467. SLNode<SmartGameObj> * smart_objnode;
  468. for (smart_objnode = GameObjManager::Get_Smart_Game_Obj_List()->Head(); smart_objnode; smart_objnode = smart_objnode->Next()) {
  469. SmartGameObj * obj = smart_objnode->Data();
  470. if ( obj && Is_Enemy( obj ) ) {
  471. Vector3 obj_pos;
  472. obj->Get_Position( &obj_pos );
  473. float range = (obj_pos - c4_pos).Length();
  474. if ( range <= trigger_range ) {
  475. Detonate();
  476. }
  477. }
  478. }
  479. }
  480. }
  481. }
  482. void C4GameObj::Post_Think( void )
  483. {
  484. SimpleGameObj::Post_Think();
  485. WWPROFILE( "C4 Post_Think" );
  486. // Follow your stuck object
  487. if ( Stuck ) {
  488. if ( StuckObject.Get_Ptr() != NULL ) {
  489. PhysicalGameObj * obj = StuckObject.Get_Ptr()->As_PhysicalGameObj();
  490. if ( obj ) {
  491. RenderObjClass * parent_model = obj->Peek_Model();
  492. Vector3 pos;
  493. if ( parent_model ) {
  494. pos = parent_model->Get_Bone_Transform( StuckBone ) * StuckOffset;
  495. } else {
  496. pos = obj->Get_Transform() * StuckOffset;
  497. }
  498. Set_Position( pos );
  499. if (obj->As_SoldierGameObj()) {
  500. bool hide = (obj->As_SoldierGameObj()->Get_Vehicle() != NULL);
  501. if (Peek_Model()) {
  502. Peek_Model()->Set_Hidden(hide);
  503. }
  504. }
  505. }
  506. } else if ( StuckStaticAnimObj != NULL) {
  507. Vector3 pos;
  508. pos = StuckStaticAnimObj->Peek_Model()->Get_Bone_Transform( StuckBone ) * StuckOffset;
  509. Set_Position(pos);
  510. } else {
  511. // Delete without exploding if my object is gone
  512. if ( StuckToObject ) {
  513. Set_Delete_Pending();
  514. }
  515. }
  516. }
  517. }
  518. void C4GameObj::Detonate( void )
  519. {
  520. if ( CombatManager::I_Am_Server() ) {
  521. Restore_Owner();
  522. if ( AmmoDefinition && AmmoDefinition->ExplosionDefID ) {
  523. int owner_id = 0;
  524. if ( Get_Owner() ) {
  525. owner_id = Get_Owner()->Get_ID();
  526. }
  527. DamageableGameObj * force_victim = NULL;
  528. if ( Stuck && StuckToObject ) {
  529. force_victim = (DamageableGameObj *)StuckObject.Get_Ptr();
  530. }
  531. ExplosionManager::Server_Explode( AmmoDefinition->ExplosionDefID, Get_Transform().Get_Translation(), owner_id, force_victim );
  532. }
  533. // If I am stuck to a building, apply damage to that building
  534. if ( Stuck ) {
  535. if ( StuckObject.Get_Ptr() != NULL ) {
  536. BuildingGameObj * building = StuckObject.Get_Ptr()->As_BuildingGameObj();
  537. if ( building ) {
  538. ExplosionManager::Explosion_Damage_Building( AmmoDefinition->ExplosionDefID, building, StuckMCT, Get_Owner() );
  539. }
  540. }
  541. }
  542. }
  543. Set_Delete_Pending();
  544. }
  545. void C4GameObj::Completely_Damaged( const OffenseObjectClass & damager )
  546. {
  547. Defuse();
  548. }
  549. /*
  550. **
  551. */
  552. void C4GameObj::Get_Information( StringClass & string )
  553. {
  554. SimpleGameObj::Get_Information( string );
  555. int type = AmmoDefinition->AmmoType;
  556. if ( type == AmmoDefinitionClass::AMMO_TYPE_C4_TIMED ) {
  557. StringClass temp(0,true);
  558. temp.Format( "Timer: %1.1f\n", Timer );
  559. string += temp;
  560. }
  561. }
  562. /*
  563. **
  564. */
  565. void C4GameObj::Export_Rare( BitStreamClass &packet )
  566. {
  567. SimpleGameObj::Export_Rare( packet );
  568. int ammo_def_id = 0;
  569. if ( AmmoDefinition != NULL ) {
  570. ammo_def_id = AmmoDefinition->Get_ID();
  571. }
  572. packet.Add( ammo_def_id );
  573. int owner_id = 0;
  574. if (Get_Owner()) {
  575. owner_id = Get_Owner()->Get_ID();
  576. }
  577. packet.Add(owner_id);
  578. Vector3 pos(0,0,0);
  579. Vector3 vel(0,0,0);
  580. ProjectileClass * po = Peek_Physical_Object()->As_ProjectileClass();
  581. if ( po ) {
  582. po->Get_Velocity(&vel);
  583. po->Get_Position(&pos);
  584. }
  585. packet.Add(vel.X, BITPACK_VEHICLE_VELOCITY);
  586. packet.Add(vel.Y, BITPACK_VEHICLE_VELOCITY);
  587. packet.Add(vel.Z, BITPACK_VEHICLE_VELOCITY);
  588. //
  589. // Synchronize the stuck state of C4
  590. //
  591. packet.Add(Stuck);
  592. if (Stuck) {
  593. packet.Add(pos.X, BITPACK_WORLD_POSITION_X);
  594. packet.Add(pos.Y, BITPACK_WORLD_POSITION_Y);
  595. packet.Add(pos.Z, BITPACK_WORLD_POSITION_Z);
  596. packet.Add(StuckMCT);
  597. packet.Add(StuckToObject);
  598. int stuck_object_id=0;
  599. if (StuckObject.Get_Ptr()) {
  600. stuck_object_id = StuckObject.Get_Ptr()->Get_ID();
  601. }
  602. packet.Add(stuck_object_id);
  603. if (StuckToObject) {
  604. packet.Add(StuckOffset.X, BITPACK_VEHICLE_VELOCITY); // offset, using velocity packing...
  605. packet.Add(StuckOffset.Y, BITPACK_VEHICLE_VELOCITY);
  606. packet.Add(StuckOffset.Z, BITPACK_VEHICLE_VELOCITY);
  607. packet.Add(StuckBone);
  608. }
  609. bool stuck_static_anim = (StuckStaticAnimObj != NULL);
  610. packet.Add(stuck_static_anim);
  611. if (stuck_static_anim) {
  612. packet.Add(StuckStaticAnimObj->Get_ID());
  613. }
  614. }
  615. }
  616. void C4GameObj::Import_Rare( BitStreamClass &packet )
  617. {
  618. SimpleGameObj::Import_Rare( packet );
  619. int ammo_def_id = packet.Get( ammo_def_id );
  620. if ( ammo_def_id != 0 ) {
  621. AmmoDefinition = (AmmoDefinitionClass *)DefinitionMgrClass::Find_Definition( ammo_def_id );
  622. if ( !AmmoDefinition->ModelName.Is_Empty() ) {
  623. Peek_Physical_Object()->Set_Model_By_Name( AmmoDefinition->ModelName ) ;
  624. }
  625. }
  626. int owner_id = packet.Get(owner_id);
  627. if (owner_id != 0) {
  628. Owner = GameObjManager::Find_SmartGameObj(owner_id);
  629. } else {
  630. Owner = NULL;
  631. }
  632. Vector3 vel;
  633. packet.Get(vel.X, BITPACK_VEHICLE_VELOCITY);
  634. packet.Get(vel.Y, BITPACK_VEHICLE_VELOCITY);
  635. packet.Get(vel.Z, BITPACK_VEHICLE_VELOCITY);
  636. ProjectileClass * po = Peek_Physical_Object()->As_ProjectileClass();
  637. if ( po ) {
  638. po->Set_Velocity(vel);
  639. }
  640. //
  641. // Synchronize the stuck state of C4
  642. //
  643. Stuck = packet.Get(Stuck);
  644. if (Stuck) {
  645. Peek_Physical_Object()->Enable_User_Control( true );
  646. // Update the position
  647. Vector3 pos;
  648. packet.Get(pos.X, BITPACK_WORLD_POSITION_X);
  649. packet.Get(pos.Y, BITPACK_WORLD_POSITION_Y);
  650. packet.Get(pos.Z, BITPACK_WORLD_POSITION_Z);
  651. ProjectileClass * po = Peek_Physical_Object()->As_ProjectileClass();
  652. if ( po ) {
  653. Vector3 local_pos;
  654. po->Get_Position(&local_pos);
  655. if ((local_pos - pos).Length2() > 0.5f * 0.5f) {
  656. po->Set_Position(pos);
  657. }
  658. }
  659. WWDEBUG_SAY(("C4 %d is now STUCK, pos= %f, %f, %f",(int)this, pos.X,pos.Y,pos.Z));
  660. packet.Get(StuckMCT);
  661. packet.Get(StuckToObject);
  662. int stuck_object_id;
  663. packet.Get(stuck_object_id);
  664. StuckObject = GameObjManager::Find_ScriptableGameObj(stuck_object_id);
  665. if (StuckToObject) {
  666. packet.Get(StuckOffset.X, BITPACK_VEHICLE_VELOCITY); // offset, using velocity packing...
  667. packet.Get(StuckOffset.Y, BITPACK_VEHICLE_VELOCITY);
  668. packet.Get(StuckOffset.Z, BITPACK_VEHICLE_VELOCITY);
  669. packet.Get(StuckBone);
  670. }
  671. bool stuck_static_anim;
  672. packet.Get(stuck_static_anim);
  673. if (stuck_static_anim) {
  674. uint32 static_anim_obj_id = 0;
  675. packet.Get(static_anim_obj_id);
  676. if (static_anim_obj_id != 0xFFFFFFFF) {
  677. StaticPhysClass * pobj = PhysicsSceneClass::Get_Instance()->Get_Static_Object_By_ID(static_anim_obj_id);
  678. if (pobj && (pobj->As_StaticAnimPhysClass() != NULL)) {
  679. REF_PTR_SET(StuckStaticAnimObj,(StaticAnimPhysClass *)pobj);
  680. }
  681. REF_PTR_RELEASE(pobj);
  682. }
  683. }
  684. }
  685. }
  686. void C4GameObj::Defuse( void )
  687. {
  688. if ( CombatManager::I_Am_Server() ) {
  689. Restore_Owner();
  690. const AmmoDefinitionClass * disarmed_ammo = WeaponManager::Find_Ammo_Definition( "KilledC4" );
  691. if ( disarmed_ammo && disarmed_ammo->ExplosionDefID ) {
  692. int owner_id = 0;
  693. if ( Get_Owner() ) {
  694. owner_id = Get_Owner()->Get_ID();
  695. }
  696. ExplosionManager::Server_Explode( disarmed_ammo->ExplosionDefID, Get_Transform().Get_Translation(), owner_id );
  697. }
  698. }
  699. Set_Delete_Pending();
  700. }
  701. void C4GameObj::Restore_Owner( void )
  702. {
  703. if ( Get_Owner() == NULL && OwnerBackup != NULL &&
  704. AmmoDefinition && (int)AmmoDefinition->AmmoType != (int)AmmoDefinitionClass::AMMO_TYPE_C4_REMOTE ) {
  705. // Try and find a smart game obj with the same playerdata
  706. SLNode<SmartGameObj> * smart_objnode;
  707. for (smart_objnode = GameObjManager::Get_Smart_Game_Obj_List()->Head(); smart_objnode; smart_objnode = smart_objnode->Next()) {
  708. SmartGameObj * obj = smart_objnode->Data();
  709. if ( obj->Get_Player_Data() == OwnerBackup ) {
  710. Owner = obj;
  711. Debug_Say(( "Found C4 owner\n" ));
  712. }
  713. }
  714. if ( Get_Owner() == NULL ) {
  715. OwnerBackup = NULL;
  716. Defuse();
  717. Debug_Say(( "Didn't find C4 owner\n" ));
  718. }
  719. }
  720. }
  721. /*
  722. **
  723. */
  724. #define C4_LIMIT 30
  725. void C4GameObj::Maintain_C4_Limit( int player_type )
  726. {
  727. if ( !CombatManager::I_Am_Server() || IS_MISSION ) {
  728. return;
  729. }
  730. SLNode<BaseGameObj> *objnode;
  731. C4GameObj * oldest_c4 = NULL;
  732. int count = 0;
  733. for ( objnode = GameObjManager::Get_Game_Obj_List()->Head(); objnode; objnode = objnode->Next()) {
  734. PhysicalGameObj * phys = objnode->Data()->As_PhysicalGameObj();
  735. if ( phys ) {
  736. C4GameObj * c4 = phys->As_C4GameObj();
  737. if ( c4 &&
  738. c4->Get_Player_Type() == player_type &&
  739. c4->AmmoDefinition &&
  740. (int)c4->AmmoDefinition->AmmoType != (int)AmmoDefinitionClass::AMMO_TYPE_C4_TIMED ) {
  741. count++;
  742. if ( (oldest_c4 == NULL) || (c4->Age > oldest_c4->Age) ) {
  743. oldest_c4 = c4;
  744. }
  745. }
  746. }
  747. }
  748. if ( count > C4_LIMIT && oldest_c4 != NULL ) {
  749. oldest_c4->Defuse();
  750. }
  751. }