This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/Combat/c4.cpp

906 lines
24 KiB
C++

/*
** 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 <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** 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<C4GameObjDef, CHUNKID_GAME_OBJECT_DEF_C4> _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<C4GameObj, CHUNKID_GAME_OBJECT_C4> _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<SmartGameObj> * 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<SmartGameObj> * 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<BaseGameObj> *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();
}
}