906 lines
24 KiB
C++
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();
|
|
}
|
|
|
|
}
|