/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /*********************************************************************************************** *** Confidential - Westwood Studios *** *********************************************************************************************** * * * Project Name : Commando * * * * $Archive:: /Commando/Code/Combat/c4.cpp $* * * * $Author:: Byon_g $* * * * $Modtime:: 2/12/02 10:25a $* * * * $Revision:: 72 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "c4.h" #include "debug.h" #include "phys.h" #include "combat.h" #include "explosion.h" #include "soldier.h" #include "persistfactory.h" #include "combatchunkid.h" #include "weaponmanager.h" #include "simpledefinitionfactory.h" #include "wwhack.h" #include "decophys.h" #include "assets.h" #include "gameobjmanager.h" #include "wwaudio.h" #include "wwprofile.h" #include "projectile.h" #include "wwphysids.h" #include "buildingaggregate.h" #include "rendobj.h" #include "definitionmgr.h" #include "apppackettypes.h" #include "bitpackids.h" #include "surfaceeffects.h" #include "gametype.h" /* ** C4GameObjDef */ DECLARE_FORCE_LINK( C4 ) SimplePersistFactoryClass _C4GameObjDefPersistFactory; DECLARE_DEFINITION_FACTORY(C4GameObjDef, CLASSID_GAME_OBJECT_DEF_C4, "C4") _C4GameObjDefDefFactory; C4GameObjDef::C4GameObjDef( void ) : ThrowVelocity( 5 ) { EDITABLE_PARAM (C4GameObjDef, ParameterClass::TYPE_FLOAT, ThrowVelocity); } uint32 C4GameObjDef::Get_Class_ID (void) const { return CLASSID_GAME_OBJECT_DEF_C4; } PersistClass * C4GameObjDef::Create( void ) const { C4GameObj * obj = new C4GameObj; obj->Init( *this ); return obj; } enum { CHUNKID_DEF_PARENT = 930991700, CHUNKID_DEF_VARIABLES, MICROCHUNKID_DEF_THROW_VELOCITY = 1, }; bool C4GameObjDef::Save( ChunkSaveClass & csave ) { csave.Begin_Chunk( CHUNKID_DEF_PARENT ); PhysicalGameObjDef::Save( csave ); csave.End_Chunk(); csave.Begin_Chunk( CHUNKID_DEF_VARIABLES ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_THROW_VELOCITY, ThrowVelocity ); csave.End_Chunk(); return true; } bool C4GameObjDef::Load( ChunkLoadClass &cload ) { while (cload.Open_Chunk()) { switch(cload.Cur_Chunk_ID()) { case CHUNKID_DEF_PARENT: PhysicalGameObjDef::Load( cload ); break; case CHUNKID_DEF_VARIABLES: while (cload.Open_Micro_Chunk()) { switch(cload.Cur_Micro_Chunk_ID()) { READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_THROW_VELOCITY, ThrowVelocity ); default: Debug_Say(("Unhandled Micro Chunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__)); break; } cload.Close_Micro_Chunk(); } break; default: Debug_Say(( "Unrecognized SimpleDef chunkID\n" )); break; } cload.Close_Chunk(); } return true; } const PersistFactoryClass & C4GameObjDef::Get_Factory (void) const { return _C4GameObjDefPersistFactory; } /* ** */ SimplePersistFactoryClass _C4GameObjPersistFactory; const PersistFactoryClass & C4GameObj::Get_Factory (void) const { return _C4GameObjPersistFactory; } /* ** */ C4GameObj::C4GameObj( void ) : AmmoDefinition( NULL ), Stuck( false ), StuckMCT( false ), StuckToObject( false ), StuckBone( 0 ), StuckStaticAnimObj(NULL), OwnerBackup( NULL ), Age( 0 ) { Set_App_Packet_Type(APPPACKETTYPE_C4); } C4GameObj::~C4GameObj() { REF_PTR_RELEASE(StuckStaticAnimObj); } /* ** */ void C4GameObj::Init( void ) { Init( Get_Definition() ); } /* ** */ void C4GameObj::Init( const C4GameObjDef & definition ) { SimpleGameObj::Init( definition ); Peek_Physical_Object()->Set_Collision_Group( TERRAIN_AND_BULLET_COLLISION_GROUP ); Peek_Physical_Object()->Set_Collision_Group( DEFAULT_COLLISION_GROUP ); } const C4GameObjDef & C4GameObj::Get_Definition( void ) const { return (const C4GameObjDef &)BaseGameObj::Get_Definition(); } void C4GameObj::Init_C4( const AmmoDefinitionClass * def, SoldierGameObj *owner, int detonation_mode, const Matrix3D & tm ) { WWASSERT( AmmoDefinition == NULL ); AmmoDefinition = def; if ( !def->ModelName.Is_Empty() ) { Peek_Physical_Object()->Set_Model_By_Name( def->ModelName ) ; } Owner = owner; DetonationMode = detonation_mode; Set_Transform( tm ); Stuck = false; StuckMCT = false; StuckToObject = false; Peek_Physical_Object()->Set_Collision_Group( DEFAULT_COLLISION_GROUP ); OwnerBackup = NULL; if ( owner ) { Set_Player_Type( owner->Get_Player_Type() ); OwnerBackup = owner->Get_Player_Data(); if ( CombatManager::I_Am_Server() && !IS_MISSION ) { Maintain_C4_Limit( Get_Player_Type() ); } } int type = AmmoDefinition->AmmoType; if ( type != AmmoDefinitionClass::AMMO_TYPE_C4_REMOTE ) { // Setup Arming Timer float time = def->C4TriggerTime1; if ( detonation_mode == 2 ) time = def->C4TriggerTime2; if ( detonation_mode == 3 ) time = def->C4TriggerTime3; Timer = time; } float sound_id = def->C4TimingSound1ID; if ( detonation_mode == 2 ) sound_id = def->C4TimingSound1ID; if ( detonation_mode == 3 ) sound_id = def->C4TimingSound1ID; if ( sound_id ) { RefCountedGameObjReference *owner_ref = new RefCountedGameObjReference(Owner); WWAudioClass::Get_Instance()->Create_Instant_Sound( sound_id, Get_Transform(), owner_ref); REF_PTR_RELEASE(owner_ref); } ProjectileClass * po = Peek_Physical_Object()->As_ProjectileClass(); if ( po ) { po->Set_Velocity( tm.Get_X_Vector() * Get_Definition().ThrowVelocity ); } // // "Dirty" the object for networking // Set_Object_Dirty_Bit( NetworkObjectClass::BIT_RARE, true ); } CollisionReactionType C4GameObj::Collision_Occurred( const CollisionEventClass & event ) { Debug_Say(( "C4 collision\n" )); // if ( ( !Stuck ) && (CombatManager::I_Am_Server()) ) if (!Stuck) { // Figure out who/what/where we hit PhysicalGameObj * other = NULL; BuildingGameObj * building = NULL; bool hit_projectile = event.OtherObj->As_ProjectileClass() != NULL; if ( event.OtherObj->Get_Observer() != NULL ) { other = ((CombatPhysObserverClass *)event.OtherObj->Get_Observer())->As_PhysicalGameObj(); building = ((CombatPhysObserverClass *)event.OtherObj->Get_Observer())->As_BuildingGameObj(); } Restore_Owner(); // Ignore my owner and my owners vehicle if ( other != NULL && Get_Owner() != NULL ) { VehicleGameObj * vehicle = other->As_VehicleGameObj(); SoldierGameObj * soldier = Get_Owner()->As_SoldierGameObj(); if ( vehicle != NULL && vehicle == soldier->Get_Vehicle() ) { return COLLISION_REACTION_NO_BOUNCE; } if ( other == Get_Owner() ) { return COLLISION_REACTION_NO_BOUNCE; } // if ( !other->Is_Teammate( Get_Owner() ) ) { Debug_Say(( "Sticking to game object %p (%p)\n", other, Get_Owner() )); Stuck = true; StuckToObject = true; StuckObject = other; StuckBone = 0; RenderObjClass * parent_model = other->Peek_Model(); if ( parent_model ) { StuckBone = parent_model->Get_Sub_Object_Bone_Index( event.CollidedRenderObj ); } Vector3 my_pos; Get_Position(&my_pos); Matrix3D::Inverse_Transform_Vector( parent_model->Get_Bone_Transform( StuckBone ), my_pos, &StuckOffset ); Peek_Physical_Object()->Enable_User_Control( true ); if (CombatManager::I_Am_Server()) { Set_Object_Dirty_Bit(NetworkObjectClass::BIT_RARE, true); } return COLLISION_REACTION_STOP_MOTION; } } else if ( building != NULL ) { // Stick to the building Stuck = true; StuckObject = (ScriptableGameObj*)building; StuckMCT = false; // Check for MCT collision if (event.OtherObj->Get_Factory().Chunk_ID() == PHYSICS_CHUNKID_BUILDINGAGGREGATE) { if (((BuildingAggregateClass *)event.OtherObj)->Is_MCT()) { StuckMCT = true; } } Peek_Physical_Object()->Enable_User_Control( true ); if (CombatManager::I_Am_Server()) { Set_Object_Dirty_Bit(NetworkObjectClass::BIT_RARE, true); } return COLLISION_REACTION_STOP_MOTION; } else if ( other == NULL && !hit_projectile ) { // if this is a static anim, then try to stick to it if (event.OtherObj->As_StaticAnimPhysClass() != NULL) { Debug_Say(( "Sticking to static anim object %p (%p)\n", event.OtherObj )); REF_PTR_SET(StuckStaticAnimObj,(StaticAnimPhysClass *)event.OtherObj); StuckBone = 0; if ( StuckStaticAnimObj->Peek_Model() ) { StuckBone = StuckStaticAnimObj->Peek_Model()->Get_Sub_Object_Bone_Index( event.CollidedRenderObj ); Vector3 my_pos; Get_Position(&my_pos); Matrix3D::Inverse_Transform_Vector( StuckStaticAnimObj->Peek_Model()->Get_Bone_Transform( StuckBone ), my_pos, &StuckOffset ); } } // If we hit permiable, pass through if ( event.CollisionResult != NULL && SurfaceEffectsManager::Is_Surface_Permeable( event.CollisionResult->SurfaceType ) ) { Debug_Say(( "C4 passes through permeable\n" )); return COLLISION_REACTION_NO_BOUNCE; } // We are hitting a static terrain, just stick Debug_Say(( "Sticking to terrain\n" )); Peek_Physical_Object()->Enable_User_Control( true ); Stuck = true; if (CombatManager::I_Am_Server()) { Set_Object_Dirty_Bit(NetworkObjectClass::BIT_RARE, true); } return COLLISION_REACTION_STOP_MOTION; } } return COLLISION_REACTION_NO_BOUNCE; } /* ** C4GameObj Save and Load */ enum { CHUNKID_PARENT = 922991750, CHUNKID_VARIABLES, XXXCHUNKID_C4_TIMER, CHUNKID_OWNER, CHUNKID_STUCK_OBJECT, XXXXMICROCHUNKID_PARAMS_NAME = 1, XXXXXMICROCHUNKID_PARAMS_NAME, MICROCHUNKID_AMMO_DEF_ID, MICROCHUNKID_DETONATION_MODE, MICROCHUNKID_TIMER, MICROCHUNKID_STUCK, MICROCHUNKID_STUCK_OFFSET, MICROCHUNKID_STUCK_MCT, MICROCHUNKID_STUCK_BONE, MICROCHUNKID_STUCK_STATIC_ANIM_OBJ_ID, MICROCHUNKID_STUCK_TO_OBJECT, MICROCHUNKID_AGE, }; bool C4GameObj::Save( ChunkSaveClass & csave ) { csave.Begin_Chunk( CHUNKID_PARENT ); SimpleGameObj::Save( csave ); csave.End_Chunk(); csave.Begin_Chunk( CHUNKID_VARIABLES ); int ammo_def_id = AmmoDefinition->Get_ID(); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_AMMO_DEF_ID, ammo_def_id ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DETONATION_MODE, DetonationMode ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_TIMER, Timer ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STUCK, Stuck ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STUCK_OFFSET, StuckOffset ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STUCK_MCT, StuckMCT ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STUCK_BONE, StuckBone ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STUCK_TO_OBJECT, StuckToObject ); if (StuckStaticAnimObj != NULL) { uint32 id = StuckStaticAnimObj->Get_ID(); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_STUCK_STATIC_ANIM_OBJ_ID, id); } WRITE_MICRO_CHUNK( csave, MICROCHUNKID_AGE, Age ); csave.End_Chunk(); if ( Owner != NULL ) { csave.Begin_Chunk( CHUNKID_OWNER ); Owner.Save( csave ); csave.End_Chunk(); } if ( StuckObject != NULL ) { csave.Begin_Chunk( CHUNKID_STUCK_OBJECT ); StuckObject.Save( csave ); csave.End_Chunk(); } return true; } bool C4GameObj::Load( ChunkLoadClass &cload ) { REF_PTR_RELEASE(StuckStaticAnimObj); uint32 static_anim_obj_id = 0xFFFFFFFF; while (cload.Open_Chunk()) { switch(cload.Cur_Chunk_ID()) { case CHUNKID_PARENT: SimpleGameObj::Load( cload ); break; case CHUNKID_VARIABLES: { int ammo_def_id = 0; while (cload.Open_Micro_Chunk()) { switch(cload.Cur_Micro_Chunk_ID()) { READ_MICRO_CHUNK( cload, MICROCHUNKID_AMMO_DEF_ID, ammo_def_id ); READ_MICRO_CHUNK( cload, MICROCHUNKID_DETONATION_MODE, DetonationMode ); READ_MICRO_CHUNK( cload, MICROCHUNKID_TIMER, Timer ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STUCK, Stuck ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STUCK_OFFSET, StuckOffset ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STUCK_MCT, StuckMCT ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STUCK_BONE, StuckBone ); READ_MICRO_CHUNK( cload, MICROCHUNKID_STUCK_STATIC_ANIM_OBJ_ID, static_anim_obj_id); READ_MICRO_CHUNK( cload, MICROCHUNKID_STUCK_TO_OBJECT, StuckToObject ); READ_MICRO_CHUNK( cload, MICROCHUNKID_AGE, Age ); default: Debug_Say(( "Unrecognized C4 Variable chunkID\n" )); break; } cload.Close_Micro_Chunk(); } WWASSERT( AmmoDefinition == NULL ); AmmoDefinition = WeaponManager::Find_Ammo_Definition( ammo_def_id ); WWASSERT( AmmoDefinition != NULL ); break; } case CHUNKID_OWNER: Owner.Load( cload ); break; case CHUNKID_STUCK_OBJECT: StuckObject.Load( cload ); break; default: Debug_Say(( "Unrecognized C4 chunkID\n" )); break; } cload.Close_Chunk(); } if (static_anim_obj_id != 0xFFFFFFFF) { StaticPhysClass * pobj = PhysicsSceneClass::Get_Instance()->Get_Static_Object_By_ID(static_anim_obj_id); if (pobj && (pobj->As_StaticAnimPhysClass() != NULL)) { REF_PTR_SET(StuckStaticAnimObj,(StaticAnimPhysClass *)pobj); } REF_PTR_RELEASE(pobj); } return true; } void C4GameObj::Think( void ) { SimpleGameObj::Think(); WWPROFILE( "C4 Think" ); if ( !CombatManager::I_Am_Server() ) { return; } Age += TimeManager::Get_Frame_Seconds(); int type = AmmoDefinition->AmmoType; WWASSERT( type >= AmmoDefinitionClass::AMMO_TYPE_C4_REMOTE ); WWASSERT( type <= AmmoDefinitionClass::AMMO_TYPE_C4_PROXIMITY ); Restore_Owner(); if ( type == AmmoDefinitionClass::AMMO_TYPE_C4_REMOTE ) { if ( CombatManager::I_Am_Server() ) { // Check for owner to detonate SoldierGameObj * owner = Get_Owner(); if ( owner == NULL ) { Defuse(); } else if ( owner->Detonate_C4() ) { Detonate(); } } else { Set_Delete_Pending(); } } if ( type == AmmoDefinitionClass::AMMO_TYPE_C4_TIMED ) { // Check for time's up Timer -= TimeManager::Get_Frame_Seconds(); if ( Timer <= 0 ) { Detonate(); } } if ( type == AmmoDefinitionClass::AMMO_TYPE_C4_PROXIMITY ) { Timer -= TimeManager::Get_Frame_Seconds(); if ( Timer <= 0 ) { // Reset Timer // Timer += 1; // Check every second Timer += 0.25; // Check every 1/4 second // Check for smart objs in proximity float trigger_range = AmmoDefinition->C4TriggerRange1; if ( DetonationMode == 2 ) trigger_range = AmmoDefinition->C4TriggerRange2; if ( DetonationMode == 3 ) trigger_range = AmmoDefinition->C4TriggerRange3; Vector3 c4_pos; Get_Position( &c4_pos ); SLNode * smart_objnode; for (smart_objnode = GameObjManager::Get_Smart_Game_Obj_List()->Head(); smart_objnode; smart_objnode = smart_objnode->Next()) { SmartGameObj * obj = smart_objnode->Data(); if ( obj && Is_Enemy( obj ) ) { Vector3 obj_pos; obj->Get_Position( &obj_pos ); float range = (obj_pos - c4_pos).Length(); if ( range <= trigger_range ) { Detonate(); } } } } } } void C4GameObj::Post_Think( void ) { SimpleGameObj::Post_Think(); WWPROFILE( "C4 Post_Think" ); // Follow your stuck object if ( Stuck ) { if ( StuckObject.Get_Ptr() != NULL ) { PhysicalGameObj * obj = StuckObject.Get_Ptr()->As_PhysicalGameObj(); if ( obj ) { RenderObjClass * parent_model = obj->Peek_Model(); Vector3 pos; if ( parent_model ) { pos = parent_model->Get_Bone_Transform( StuckBone ) * StuckOffset; } else { pos = obj->Get_Transform() * StuckOffset; } Set_Position( pos ); if (obj->As_SoldierGameObj()) { bool hide = (obj->As_SoldierGameObj()->Get_Vehicle() != NULL); if (Peek_Model()) { Peek_Model()->Set_Hidden(hide); } } } } else if ( StuckStaticAnimObj != NULL) { Vector3 pos; pos = StuckStaticAnimObj->Peek_Model()->Get_Bone_Transform( StuckBone ) * StuckOffset; Set_Position(pos); } else { // Delete without exploding if my object is gone if ( StuckToObject ) { Set_Delete_Pending(); } } } } void C4GameObj::Detonate( void ) { if ( CombatManager::I_Am_Server() ) { Restore_Owner(); if ( AmmoDefinition && AmmoDefinition->ExplosionDefID ) { int owner_id = 0; if ( Get_Owner() ) { owner_id = Get_Owner()->Get_ID(); } DamageableGameObj * force_victim = NULL; if ( Stuck && StuckToObject ) { force_victim = (DamageableGameObj *)StuckObject.Get_Ptr(); } ExplosionManager::Server_Explode( AmmoDefinition->ExplosionDefID, Get_Transform().Get_Translation(), owner_id, force_victim ); } // If I am stuck to a building, apply damage to that building if ( Stuck ) { if ( StuckObject.Get_Ptr() != NULL ) { BuildingGameObj * building = StuckObject.Get_Ptr()->As_BuildingGameObj(); if ( building ) { ExplosionManager::Explosion_Damage_Building( AmmoDefinition->ExplosionDefID, building, StuckMCT, Get_Owner() ); } } } } Set_Delete_Pending(); } void C4GameObj::Completely_Damaged( const OffenseObjectClass & damager ) { Defuse(); } /* ** */ void C4GameObj::Get_Information( StringClass & string ) { SimpleGameObj::Get_Information( string ); int type = AmmoDefinition->AmmoType; if ( type == AmmoDefinitionClass::AMMO_TYPE_C4_TIMED ) { StringClass temp(0,true); temp.Format( "Timer: %1.1f\n", Timer ); string += temp; } } /* ** */ void C4GameObj::Export_Rare( BitStreamClass &packet ) { SimpleGameObj::Export_Rare( packet ); int ammo_def_id = 0; if ( AmmoDefinition != NULL ) { ammo_def_id = AmmoDefinition->Get_ID(); } packet.Add( ammo_def_id ); int owner_id = 0; if (Get_Owner()) { owner_id = Get_Owner()->Get_ID(); } packet.Add(owner_id); Vector3 pos(0,0,0); Vector3 vel(0,0,0); ProjectileClass * po = Peek_Physical_Object()->As_ProjectileClass(); if ( po ) { po->Get_Velocity(&vel); po->Get_Position(&pos); } packet.Add(vel.X, BITPACK_VEHICLE_VELOCITY); packet.Add(vel.Y, BITPACK_VEHICLE_VELOCITY); packet.Add(vel.Z, BITPACK_VEHICLE_VELOCITY); // // Synchronize the stuck state of C4 // packet.Add(Stuck); if (Stuck) { packet.Add(pos.X, BITPACK_WORLD_POSITION_X); packet.Add(pos.Y, BITPACK_WORLD_POSITION_Y); packet.Add(pos.Z, BITPACK_WORLD_POSITION_Z); packet.Add(StuckMCT); packet.Add(StuckToObject); int stuck_object_id=0; if (StuckObject.Get_Ptr()) { stuck_object_id = StuckObject.Get_Ptr()->Get_ID(); } packet.Add(stuck_object_id); if (StuckToObject) { packet.Add(StuckOffset.X, BITPACK_VEHICLE_VELOCITY); // offset, using velocity packing... packet.Add(StuckOffset.Y, BITPACK_VEHICLE_VELOCITY); packet.Add(StuckOffset.Z, BITPACK_VEHICLE_VELOCITY); packet.Add(StuckBone); } bool stuck_static_anim = (StuckStaticAnimObj != NULL); packet.Add(stuck_static_anim); if (stuck_static_anim) { packet.Add(StuckStaticAnimObj->Get_ID()); } } } void C4GameObj::Import_Rare( BitStreamClass &packet ) { SimpleGameObj::Import_Rare( packet ); int ammo_def_id = packet.Get( ammo_def_id ); if ( ammo_def_id != 0 ) { AmmoDefinition = (AmmoDefinitionClass *)DefinitionMgrClass::Find_Definition( ammo_def_id ); if ( !AmmoDefinition->ModelName.Is_Empty() ) { Peek_Physical_Object()->Set_Model_By_Name( AmmoDefinition->ModelName ) ; } } int owner_id = packet.Get(owner_id); if (owner_id != 0) { Owner = GameObjManager::Find_SmartGameObj(owner_id); } else { Owner = NULL; } Vector3 vel; packet.Get(vel.X, BITPACK_VEHICLE_VELOCITY); packet.Get(vel.Y, BITPACK_VEHICLE_VELOCITY); packet.Get(vel.Z, BITPACK_VEHICLE_VELOCITY); ProjectileClass * po = Peek_Physical_Object()->As_ProjectileClass(); if ( po ) { po->Set_Velocity(vel); } // // Synchronize the stuck state of C4 // Stuck = packet.Get(Stuck); if (Stuck) { Peek_Physical_Object()->Enable_User_Control( true ); // Update the position Vector3 pos; packet.Get(pos.X, BITPACK_WORLD_POSITION_X); packet.Get(pos.Y, BITPACK_WORLD_POSITION_Y); packet.Get(pos.Z, BITPACK_WORLD_POSITION_Z); ProjectileClass * po = Peek_Physical_Object()->As_ProjectileClass(); if ( po ) { Vector3 local_pos; po->Get_Position(&local_pos); if ((local_pos - pos).Length2() > 0.5f * 0.5f) { po->Set_Position(pos); } } WWDEBUG_SAY(("C4 %d is now STUCK, pos= %f, %f, %f",(int)this, pos.X,pos.Y,pos.Z)); packet.Get(StuckMCT); packet.Get(StuckToObject); int stuck_object_id; packet.Get(stuck_object_id); StuckObject = GameObjManager::Find_ScriptableGameObj(stuck_object_id); if (StuckToObject) { packet.Get(StuckOffset.X, BITPACK_VEHICLE_VELOCITY); // offset, using velocity packing... packet.Get(StuckOffset.Y, BITPACK_VEHICLE_VELOCITY); packet.Get(StuckOffset.Z, BITPACK_VEHICLE_VELOCITY); packet.Get(StuckBone); } bool stuck_static_anim; packet.Get(stuck_static_anim); if (stuck_static_anim) { uint32 static_anim_obj_id = 0; packet.Get(static_anim_obj_id); if (static_anim_obj_id != 0xFFFFFFFF) { StaticPhysClass * pobj = PhysicsSceneClass::Get_Instance()->Get_Static_Object_By_ID(static_anim_obj_id); if (pobj && (pobj->As_StaticAnimPhysClass() != NULL)) { REF_PTR_SET(StuckStaticAnimObj,(StaticAnimPhysClass *)pobj); } REF_PTR_RELEASE(pobj); } } } } void C4GameObj::Defuse( void ) { if ( CombatManager::I_Am_Server() ) { Restore_Owner(); const AmmoDefinitionClass * disarmed_ammo = WeaponManager::Find_Ammo_Definition( "KilledC4" ); if ( disarmed_ammo && disarmed_ammo->ExplosionDefID ) { int owner_id = 0; if ( Get_Owner() ) { owner_id = Get_Owner()->Get_ID(); } ExplosionManager::Server_Explode( disarmed_ammo->ExplosionDefID, Get_Transform().Get_Translation(), owner_id ); } } Set_Delete_Pending(); } void C4GameObj::Restore_Owner( void ) { if ( Get_Owner() == NULL && OwnerBackup != NULL && AmmoDefinition && (int)AmmoDefinition->AmmoType != (int)AmmoDefinitionClass::AMMO_TYPE_C4_REMOTE ) { // Try and find a smart game obj with the same playerdata SLNode * smart_objnode; for (smart_objnode = GameObjManager::Get_Smart_Game_Obj_List()->Head(); smart_objnode; smart_objnode = smart_objnode->Next()) { SmartGameObj * obj = smart_objnode->Data(); if ( obj->Get_Player_Data() == OwnerBackup ) { Owner = obj; Debug_Say(( "Found C4 owner\n" )); } } if ( Get_Owner() == NULL ) { OwnerBackup = NULL; Defuse(); Debug_Say(( "Didn't find C4 owner\n" )); } } } /* ** */ #define C4_LIMIT 30 void C4GameObj::Maintain_C4_Limit( int player_type ) { if ( !CombatManager::I_Am_Server() || IS_MISSION ) { return; } SLNode *objnode; C4GameObj * oldest_c4 = NULL; int count = 0; for ( objnode = GameObjManager::Get_Game_Obj_List()->Head(); objnode; objnode = objnode->Next()) { PhysicalGameObj * phys = objnode->Data()->As_PhysicalGameObj(); if ( phys ) { C4GameObj * c4 = phys->As_C4GameObj(); if ( c4 && c4->Get_Player_Type() == player_type && c4->AmmoDefinition && (int)c4->AmmoDefinition->AmmoType != (int)AmmoDefinitionClass::AMMO_TYPE_C4_TIMED ) { count++; if ( (oldest_c4 == NULL) || (c4->Age > oldest_c4->Age) ) { oldest_c4 = c4; } } } } if ( count > C4_LIMIT && oldest_c4 != NULL ) { oldest_c4->Defuse(); } }