/* ** 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/physicalgameobj.cpp $* * * * $Author:: Greg_h $* * * * $Modtime:: 6/14/02 5:58p $* * * * $Revision:: 223 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "physicalgameobj.h" #include "damage.h" #include "scripts.h" #include "debug.h" #include "explosion.h" #include "assets.h" #include "combatsound.h" #include "matrix3d.h" #include "phys.h" #include "smartgameobj.h" #include "soldier.h" #include "animcontrol.h" #include "chunkio.h" #include "saveload.h" #include "combat.h" #include "persistfactory.h" #include "combatchunkid.h" #include "parameter.h" #include "radar.h" #include "playertype.h" #include "matinfo.h" #include "gameobjmanager.h" #include "pscene.h" #include "wwprofile.h" #include "rbody.h" #include "bitpackids.h" #include "activeconversation.h" #include "oratortypes.h" #include "vistable.h" #include "encyclopediamgr.h" #include "combatmaterialeffectmanager.h" #include "phys.h" #include "transitioneffect.h" #include "phys3.h" #include "surfaceeffects.h" // Hibernate after 30 seconds #define HIBERNATION_DELAY 30 bool _DisplayHibernating = false; /* ** PhysicalGameObjDef */ PhysicalGameObjDef::PhysicalGameObjDef( void ) : Type( 0 ), BullseyeOffsetZ( 0.0f ), RadarBlipType( 0 ), PhysDefID( 0 ), KilledExplosion( 0 ), OratorType( ORATOR_TYPE_START - 1 ), DefaultHibernationEnable( true ), AllowInnateConversations( false ), UseCreationEffect( false ) { #ifdef PARAM_EDITING_ON int i; EnumParameterClass *param; EDITABLE_PARAM( PhysicalGameObjDef, ParameterClass::TYPE_FLOAT, BullseyeOffsetZ ); param = new EnumParameterClass( &RadarBlipType ); param->Set_Name("Radar Blip Type"); for ( i = 0; i < RadarManager::Get_Num_Blip_Shape_Types(); i++ ) { param->Add_Value ( RadarManager::Get_Blip_Shape_Type_Name( i ), i ); } GENERIC_EDITABLE_PARAM(PhysicalGameObjDef,param) EDITABLE_PARAM( PhysicalGameObjDef, ParameterClass::TYPE_STRING, Animation ); EDITABLE_PARAM( PhysicalGameObjDef, ParameterClass::TYPE_EXPLOSIONDEFINITIONID, KilledExplosion); EDITABLE_PARAM( PhysicalGameObjDef, ParameterClass::TYPE_BOOL, DefaultHibernationEnable ); EDITABLE_PARAM( PhysicalGameObjDef, ParameterClass::TYPE_BOOL, AllowInnateConversations ); EDITABLE_PARAM( PhysicalGameObjDef, ParameterClass::TYPE_BOOL, UseCreationEffect ); // // Configure the orator types parameter // EnumParameterClass *orator_type_param = new EnumParameterClass (&OratorType); orator_type_param->Set_Name ("Orator Type"); // // Add all the orator types to the list // int count = OratorTypeClass::Get_Count (); for (int index = 0; index < count; index ++) { orator_type_param->Add_Value (OratorTypeClass::Get_Description (index), OratorTypeClass::Get_ID (index)); } GENERIC_EDITABLE_PARAM( PhysicalGameObjDef, orator_type_param ); #endif } enum { CHUNKID_DEF_VARIABLES = 909991657, LEGACY_CHUNKID_DEF_PARENT_OLD, XXXCHUNKID_DEF_PARENT_OLD_OLD, LEGACY_CHUNKID_DEF_DEFENSEOBJECTDEF, CHUNKID_DEF_PARENT, MICROCHUNKID_DEF_TYPE = 1, MICROCHUNKID_DEF_BULLSEYE_OFFSET_Z, XXXMICROCHUNKID_DEF_DEFAULT_GANG, MICROCHUNKID_DEF_BLIP_TYPE, XXXMICROCHUNKID_DEF_MODEL_NAME, XXXMICROCHUNKID_DEF_HEALTH, XXXMICROCHUNKID_DEF_HEALTH_MAX, XXXMICROCHUNKID_DEF_SKIN, XXXMICROCHUNKID_DEF_SHIELD_STRENGTH, XXXMICROCHUNKID_DEF_SHIELD_STRENGTH_MAX, XXXMICROCHUNKID_DEF_SHIELD_TYPE, XXXMICROCHUNKID_DEF_LISTEN_RANGE, XXX_MICROCHUNKID_DEF_SCRIPT_NAME, XXX_MICROCHUNKID_DEF_SCRIPT_PARAMETERS, XXXMICROCHUNKID_DEF_POSITION, // ??? MICROCHUNKID_DEF_FACING, MICROCHUNKID_DEF_ANIMATION, MICROCHUNKID_DEF_PHYS_ID, LEGACY_MICROCHUNKID_DEF_DEFAULT_PLAYER_TYPE, MICROCHUNKID_DEF_KILLED_EXPLOSION, LEGACY_MICROCHUNKID_DEF_TRANSLATED_NAME_ID, MICROCHUNKID_DEF_DEFAULT_HIBERNATION_ENABLE, MICROCHUNKID_DEF_ALLOW_INNATE_CONVERSATIONS, MICROCHUNKID_DEF_ORATOR_TYPE, MICROCHUNKID_DEF_USE_CREATION_EFFECT, }; bool PhysicalGameObjDef::Save( ChunkSaveClass & csave ) { csave.Begin_Chunk( CHUNKID_DEF_PARENT ); DamageableGameObjDef::Save( csave ); csave.End_Chunk(); csave.Begin_Chunk( CHUNKID_DEF_VARIABLES ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_TYPE, Type ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_BULLSEYE_OFFSET_Z, BullseyeOffsetZ ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_BLIP_TYPE, RadarBlipType ); WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_DEF_ANIMATION, Animation ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_PHYS_ID, PhysDefID ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_KILLED_EXPLOSION, KilledExplosion ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_DEFAULT_HIBERNATION_ENABLE, DefaultHibernationEnable ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_ALLOW_INNATE_CONVERSATIONS, AllowInnateConversations ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_ORATOR_TYPE, OratorType ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_USE_CREATION_EFFECT, UseCreationEffect ); csave.End_Chunk(); return true; } bool PhysicalGameObjDef::Load( ChunkLoadClass &cload ) { while (cload.Open_Chunk()) { switch(cload.Cur_Chunk_ID()) { case LEGACY_CHUNKID_DEF_PARENT_OLD: ScriptableGameObjDef::Load( cload ); break; case CHUNKID_DEF_PARENT: DamageableGameObjDef::Load( cload ); break; case CHUNKID_DEF_VARIABLES: while (cload.Open_Micro_Chunk()) { switch(cload.Cur_Micro_Chunk_ID()) { READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_TYPE, Type ); READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_BULLSEYE_OFFSET_Z, BullseyeOffsetZ ); READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_BLIP_TYPE, RadarBlipType ); READ_MICRO_CHUNK_WWSTRING( cload, MICROCHUNKID_DEF_ANIMATION, Animation ); READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_PHYS_ID, PhysDefID ); READ_MICRO_CHUNK( cload, LEGACY_MICROCHUNKID_DEF_DEFAULT_PLAYER_TYPE, DefaultPlayerType ); READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_KILLED_EXPLOSION, KilledExplosion ); READ_MICRO_CHUNK( cload, LEGACY_MICROCHUNKID_DEF_TRANSLATED_NAME_ID, TranslatedNameID ); READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_DEFAULT_HIBERNATION_ENABLE, DefaultHibernationEnable ); READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_ALLOW_INNATE_CONVERSATIONS, AllowInnateConversations ); READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_ORATOR_TYPE, OratorType ); READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_USE_CREATION_EFFECT, UseCreationEffect ); default: Debug_Say(( "Unrecognized PhysicalDef Variable chunkID %d\n", cload.Cur_Micro_Chunk_ID() )); break; } cload.Close_Micro_Chunk(); } break; case LEGACY_CHUNKID_DEF_DEFENSEOBJECTDEF: DefenseObjectDef.Load(cload); break; default: Debug_Say(( "Unrecognized PhysicalGameObjDef chunkID %d\n", cload.Cur_Chunk_ID() )); break; } cload.Close_Chunk(); } return true; } bool PhysicalGameObjDef::Is_Valid_Config (StringClass &message) { bool retval = false; DefinitionClass *phys_def = DefinitionMgrClass::Find_Definition (PhysDefID); if (phys_def != NULL) { retval = phys_def->Is_Valid_Config (message); } else { message += "Can't find physics object definition.\n"; } return retval; } /* ** PhysicalGameObj */ PhysicalGameObj::PhysicalGameObj( void ) : PhysObj( NULL ), AnimControl( NULL ), //TintColor(1, 1, 1), HibernationTimer( 0 ), // Start alseep HibernationEnable( true ), HostGameObjBone( 0 ), RadarBlipShapeType( 0 ), RadarBlipColorType( 0 ), RadarBlipIntensity( 0 ), ActiveConversation( NULL ), PendingHostObjID( 0 ), HUDPokableIndicatorEnabled( false ), IsInnateConversationsEnabled( true ) { Reset_Server_Skips(255); return ; } PhysicalGameObj::~PhysicalGameObj( void ) { if ( AnimControl != NULL ) { delete AnimControl; AnimControl = NULL; } if ( PhysObj != NULL ) { COMBAT_SCENE->Remove_Object( PhysObj ); PhysObj->Release_Ref(); PhysObj = NULL; } REF_PTR_RELEASE( ActiveConversation ); } /* ** */ void PhysicalGameObj::Init( const PhysicalGameObjDef & definition ) { DamageableGameObj::Init( definition ); Copy_Settings( definition ); Hide_Muzzle_Flashes(); /* ** If the definition calls for it, add a material effect to the object */ if ( definition.UseCreationEffect ) { PhysClass * physobj = Peek_Physical_Object(); if (physobj != NULL) { TransitionEffectClass * effect = CombatMaterialEffectManager::Get_Spawn_Effect(); physobj->Add_Effect_To_Me(effect); REF_PTR_RELEASE(effect); } } return ; } /* ** */ void PhysicalGameObj::Copy_Settings( const PhysicalGameObjDef & definition ) { // // Release our hold on the physics object // if ( PhysObj != NULL ) { COMBAT_SCENE->Remove_Object( PhysObj ); PhysObj->Release_Ref(); PhysObj = NULL; } // Set the Physical Object WWASSERT( PhysObj == NULL ); DefinitionClass * podef = DefinitionMgrClass::Find_Definition( definition.PhysDefID ); WWASSERT( SuperClassID_From_ClassID( podef->Get_Class_ID() ) == CLASSID_PHYSICS ); PhysObj = (PhysClass *)podef->Create(); WWASSERT( PhysObj != NULL ); PhysObj->Set_Collision_Group( DEFAULT_COLLISION_GROUP ); PhysObj->Set_Observer( this ); COMBAT_SCENE->Add_Dynamic_Object( PhysObj ); // Do we still use this????? if ( !definition.Animation.Is_Empty() ) { Set_Animation( definition.Animation ); } Enable_Hibernation( definition.DefaultHibernationEnable ); Reset_Radar_Blip_Shape_Type(); return ; } /* ** */ void PhysicalGameObj::Re_Init( const PhysicalGameObjDef & definition ) { Matrix3D tm = Get_Transform (); // // Re-initialize the base class // DamageableGameObj::Re_Init( definition ); // // Copy any internal settings from the definition // Copy_Settings( definition ); // // Restore the necessary settings // Set_Transform( tm ); return ; } const PhysicalGameObjDef & PhysicalGameObj::Get_Definition( void ) const { return (const PhysicalGameObjDef &)BaseGameObj::Get_Definition(); } /* ** PhysicalGameObj Save and Load */ enum { XXXCHUNKID_PARENT_OLD_OLD = 910991145, CHUNKID_VARIABLES, XXX_CHUNKID_SCRIPTS, LEGACY_CHUNKID_DEFENSE, XXXCHUNKID_LISTENER, XXXCHUNKID_REFERENCEABLE, XXXCHUNKID_OBSER_XXX_VER, LEGACY_CHUNKID_PARENT_OLD, CHUNKID_ANIM_CONTROL, CHUNKID_HOST_GAME_OBJ, CHUNKID_PARENT, XXXMICROCHUNKID_ID = 1, XXXMICROCHUNKID_GANG, MICROCHUNKID_PHYS_OBSERVER_PTR, XXXMICROCHUNKID_REFERENCEABLE_PTR, XXXMICROCHUNKID_DISTANCE_PRIORITY, XXXMICROCHUNKID_TIME_PRIORITY, XXXMICROCHUNKID_PRIORITY, XXXMICROCHUNKID_GAME_OBJ_OBSERVER_PTR, LEGACY_MICROCHUNKID_PLAYER_TYPE, MICROCHUNKID_PHYSICAL_OBJECT, MICROCHUNKID_HIBERNATION_TIMER, MICROCHUNKID_HIBERNATION_ENABLE, MICROCHUNKID_HOST_GAME_OBJ_BONE, MICROCHUNKID_RADAR_BLIP_SHAPE_TYPE, MICROCHUNKID_RADAR_BLIP_COLOR_TYPE, MICROCHUNKID_RADAR_BLIP_INTENSITY, MICROCHUNKID_ACTIVE_CONVERSATION, MICROCHUNKID_HUD_POKABLE_INDICATOR, MICROCHUNKID_IS_INNATE_CONVERSATIONS_ENABLED, }; bool PhysicalGameObj::Save( ChunkSaveClass & csave ) { csave.Begin_Chunk( CHUNKID_PARENT ); DamageableGameObj::Save( csave ); csave.End_Chunk(); csave.Begin_Chunk( CHUNKID_VARIABLES ); CombatPhysObserverClass * phys_observer_ptr = (CombatPhysObserverClass *)this; WRITE_MICRO_CHUNK( csave, MICROCHUNKID_PHYS_OBSERVER_PTR, phys_observer_ptr ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_PHYSICAL_OBJECT, PhysObj ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_HIBERNATION_TIMER, HibernationTimer ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_HIBERNATION_ENABLE, HibernationEnable ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_HOST_GAME_OBJ_BONE, HostGameObjBone ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_RADAR_BLIP_SHAPE_TYPE, RadarBlipShapeType ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_RADAR_BLIP_COLOR_TYPE, RadarBlipColorType ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_RADAR_BLIP_INTENSITY, RadarBlipIntensity ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ACTIVE_CONVERSATION, ActiveConversation ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_HUD_POKABLE_INDICATOR, HUDPokableIndicatorEnabled ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_IS_INNATE_CONVERSATIONS_ENABLED, IsInnateConversationsEnabled ); csave.End_Chunk(); if ( AnimControl ) { csave.Begin_Chunk( CHUNKID_ANIM_CONTROL ); AnimControl->Save( csave ); csave.End_Chunk(); } if ( HostGameObj.Get_Ptr() != NULL ) { csave.Begin_Chunk( CHUNKID_HOST_GAME_OBJ ); HostGameObj.Save( csave ); csave.End_Chunk(); } /* #pragma message( "Tom, do these need to be saved?" ) int ImportStateCount; float DistancePriority; float TimePriority; float VolatilityPriority; float Priority; BYTE ClientUpdateSkips[255]; BYTE ServerUpdateSkips; cPacket StatePacket; int StatePacketUnchangedCount; Vector3 TintColor; */ return true; } bool PhysicalGameObj::Load( ChunkLoadClass &cload ) { WWASSERT( PhysObj == NULL ); // May need to change to release??? CombatPhysObserverClass * phys_observer_ptr = NULL; while (cload.Open_Chunk()) { switch(cload.Cur_Chunk_ID()) { case LEGACY_CHUNKID_PARENT_OLD: ScriptableGameObj::Load( cload ); break; case CHUNKID_PARENT: DamageableGameObj::Load( cload ); break; case CHUNKID_VARIABLES: while (cload.Open_Micro_Chunk()) { switch(cload.Cur_Micro_Chunk_ID()) { READ_MICRO_CHUNK( cload, MICROCHUNKID_PHYS_OBSERVER_PTR, phys_observer_ptr ); READ_MICRO_CHUNK( cload, LEGACY_MICROCHUNKID_PLAYER_TYPE, PlayerType ); READ_MICRO_CHUNK( cload, MICROCHUNKID_PHYSICAL_OBJECT, PhysObj ); READ_MICRO_CHUNK( cload, MICROCHUNKID_HIBERNATION_TIMER, HibernationTimer ); READ_MICRO_CHUNK( cload, MICROCHUNKID_HIBERNATION_ENABLE, HibernationEnable ); READ_MICRO_CHUNK( cload, MICROCHUNKID_HOST_GAME_OBJ_BONE, HostGameObjBone ); READ_MICRO_CHUNK( cload, MICROCHUNKID_RADAR_BLIP_SHAPE_TYPE, RadarBlipShapeType ); READ_MICRO_CHUNK( cload, MICROCHUNKID_RADAR_BLIP_COLOR_TYPE, RadarBlipColorType ); READ_MICRO_CHUNK( cload, MICROCHUNKID_RADAR_BLIP_INTENSITY, RadarBlipIntensity ); READ_MICRO_CHUNK( cload, MICROCHUNKID_ACTIVE_CONVERSATION, ActiveConversation ); READ_MICRO_CHUNK( cload, MICROCHUNKID_HUD_POKABLE_INDICATOR, HUDPokableIndicatorEnabled ); READ_MICRO_CHUNK( cload, MICROCHUNKID_IS_INNATE_CONVERSATIONS_ENABLED, IsInnateConversationsEnabled ); default: Debug_Say(( "Unrecognized PhysicalGameObj Variable chunkID\n" )); break; } cload.Close_Micro_Chunk(); } break; case LEGACY_CHUNKID_DEFENSE: DefenseObject.Load( cload ); break; case CHUNKID_ANIM_CONTROL: Set_Animation( NULL ); // Build AnimControl AnimControl->Load( cload ); break; case CHUNKID_HOST_GAME_OBJ: HostGameObj.Load( cload ); break; default: Debug_Say(( "Unrecognized PhysicalGameObj chunkID\n" )); break; } cload.Close_Chunk(); } WWASSERT( PhysObj != NULL ); REQUEST_REF_COUNTED_POINTER_REMAP( (RefCountClass **)&PhysObj ); if ( ActiveConversation != NULL ) { REQUEST_REF_COUNTED_POINTER_REMAP( (RefCountClass **)&ActiveConversation ); } // Register the multiple-inheritance versions of our this pointer. WWASSERT(phys_observer_ptr != NULL); if (phys_observer_ptr != NULL) { SaveLoadSystemClass::Register_Pointer(phys_observer_ptr, (CombatPhysObserverClass *)this); } SaveLoadSystemClass::Register_Post_Load_Callback(this); return true; } void PhysicalGameObj::On_Post_Load (void) { // Plug ourselves back into the physics object as an observer WWASSERT(PhysObj != NULL); PhysObj->Set_Observer(this); Hide_Muzzle_Flashes(); DamageableGameObj::On_Post_Load (); } AnimControlClass * PhysicalGameObj::Get_Anim_Control( void ) { return AnimControl; } void PhysicalGameObj::Set_Anim_Control( AnimControlClass * anim_control ) { WWASSERT( AnimControl == NULL ); AnimControl = anim_control; WWASSERT( AnimControl != NULL ); } bool PhysicalGameObj::Is_Soft( void ) { return DefenseObject.Is_Soft(); } Vector3 PhysicalGameObj::Get_Bullseye_Position( void ) { Vector3 pos; Get_Position(&pos); // pos.Z += Get_Bullseye_Offset_Z(); return pos; } void PhysicalGameObj::Apply_Damage( const OffenseObjectClass & damager, float scale, int alternate_skin ) { // If this damage is allowed if ( !CombatManager::Can_Damage( damager.Get_Owner(), this)) { return; } #ifdef WWDEBUG // // Tone it down for VIP's // scale *= CombatManager::Get_Damage_Factor(damager.Get_Owner(), this); #endif // WWDEBUG DamageableGameObj::Apply_Damage( damager, scale ); } void PhysicalGameObj::Apply_Damage_Extended( const OffenseObjectClass & damager, float scale, const Vector3 & direction, const char * collision_box_name ) { // if ( CombatManager::I_Am_Server() ) Clients can apply damage now { Apply_Damage( damager, scale ); } } void PhysicalGameObj::Completely_Damaged( const OffenseObjectClass & damager ) { if ( Get_Definition().KilledExplosion != 0 ) { Vector3 pos; Get_Position(&pos); WWASSERT(pos.Is_Valid());// most likely candidate for explosion damage bug....? // Build a transform with the same heading as the object float z_rot = Get_Transform().Get_Z_Rotation(); Matrix3D tm(pos); tm.Rotate_Z(z_rot); // Create the explosion ExplosionManager::Create_Explosion_At( Get_Definition().KilledExplosion, tm/*pos*/, damager.Get_Owner() ); // no one gets credit for this // // Reveal this object to the player's encyclopedia // if ( damager.Get_Owner () == COMBAT_STAR ) { EncyclopediaMgrClass::Reveal_Object( this ); } } Set_Delete_Pending(); } void PhysicalGameObj::Teleport_To_Host_Bone( void ) { // Debug_Say(( "Teleporting %d to host\n", Get_ID() )); if ( HostGameObj.Get_Ptr() ) { RenderObjClass * model = ((PhysicalGameObj * )(HostGameObj.Get_Ptr()))->Peek_Model(); if ( model != NULL ) { /* ** Calculate the bone's transform and try to go there */ bool ok = false; Matrix3D new_transform = model->Get_Bone_Transform( HostGameObjBone ); /* ** If we are some kind of moveable object see if we can teleport to the desired position. ** Also, clear our velocity whenever being controlled by an animation. */ if (Peek_Physical_Object()->As_MoveablePhysClass()) { /* ** Don't collide with our host */ PhysClass * host_phys = ((PhysicalGameObj * )HostGameObj.Get_Ptr())->Peek_Physical_Object(); if (host_phys != NULL) { host_phys->Inc_Ignore_Counter(); } /* ** Try to teleport to the new location while pushing any blocking dynamic ** objects out of the way */ MoveablePhysClass * movephys = Peek_Physical_Object()->As_MoveablePhysClass(); movephys->Set_Velocity(Vector3(0,0,0)); movephys->Cinematic_Move_To(new_transform); /* ** Re-enable collision for our host object */ if (host_phys != NULL) { host_phys->Dec_Ignore_Counter(); } } /* ** If we are not a moveable object, just set the transform. */ if (Peek_Physical_Object()->As_MoveablePhysClass() == NULL) { Set_Transform(new_transform); ok = true; } /* ** Always reset the hibernation state */ Reset_Hibernating(); } } } void PhysicalGameObj::Post_Think( void ) { if ( AnimControl != NULL ) { if ( AnimControl->Peek_Model() != Peek_Model() ) { Debug_Say(( "Anim control doesn't match Model\n" )); // For some reason??? some vehicles come in with an anim control, but no model in the anim control. if ( Get_Anim_Control() != NULL && Get_Anim_Control()->Peek_Model() == NULL ) { Get_Anim_Control()->Set_Model( Peek_Model() ); } } // WWASSERT( AnimControl->Peek_Model() == Peek_Model() ); } // Handle Pending Host if ( PendingHostObjID != 0 ) { Reset_Hibernating(); HostGameObj = GameObjManager::Find_PhysicalGameObj( PendingHostObjID ); Set_Object_Dirty_Bit( NetworkObjectClass::BIT_RARE, true ); if ( HostGameObj.Get_Ptr() != NULL ) { // Debug_Say(( "Found Pending Host\n" )); PendingHostObjID = 0; // Fond em } } // If host bone controlled if ( HostGameObj.Get_Ptr() ) { Teleport_To_Host_Bone(); } DamageableGameObj::Post_Think(); WWPROFILE( "Physical PostThink" ); if ( HibernationEnable && HibernationTimer > 0 ) { HibernationTimer -= TimeManager::Get_Frame_Seconds(); if ( HibernationTimer <= 0 ) { Begin_Hibernation(); // Debug_Say(( "Hibernate!!!\n" )); } } if ( AnimControl != NULL ) { bool anim_complete = AnimControl->Is_Complete(); AnimControl->Update( TimeManager::Get_Frame_Seconds() ); // update the animation control if ( !anim_complete && AnimControl->Is_Complete() ) { // we just completed. Return Animation_Complete IF this is not a smart obj with animation action if ( As_SmartGameObj() == NULL || !As_SmartGameObj()->Get_Action()->Is_Animating() ) { const GameObjObserverList & observer_list = Get_Observers(); for( int index = 0; index < observer_list.Count(); index++ ) { observer_list[ index ]->Animation_Complete( this, AnimControl->Get_Animation_Name() ); } } } } #pragma message ("Going to hell on a client is problematic.") #ifndef PARAM_EDITING_ON //(gth) don't go to hell in the editor cause it will cause a crash! if (CombatManager::I_Am_Only_Client () == false && COMBAT_SCENE != NULL) { Vector3 pos; Get_Position(&pos); Vector3 min; Vector3 max; COMBAT_SCENE->Get_Level_Extents(min, max); if ( pos.Z < min.Z - 20.0f ) { Debug_Say(( "Object %d is going to hell at (%1.1f, %1.1f, %1.1f). Die!\n", Get_ID(), pos.X, pos.Y, pos.Z )); Set_Delete_Pending(); } } #endif } void PhysicalGameObj::Set_Collision_Group( int group ) { Peek_Physical_Object()->Set_Collision_Group( group ); } void PhysicalGameObj::Attach_To_Object_Bone( PhysicalGameObj * host, const char * bone_name ) { // Make sure we get teleported immeadiately! Teleport_To_Host_Bone(); // // Zero the velocity of the object if we are detaching it from // the bone... (This makes physics behave better) // if (HostGameObj != host && host == NULL) { RigidBodyClass *rigid_body = Peek_Physical_Object()->As_RigidBodyClass(); if (rigid_body != NULL) { Vector3 velocity; rigid_body->Get_Velocity (&velocity); Matrix3D curr_tm = rigid_body->Get_Transform (); float heading = curr_tm.Get_Z_Rotation (); Matrix3D new_tm (1); new_tm.Rotate_Z (heading); new_tm.Set_Translation (curr_tm.Get_Translation ()); rigid_body->Set_Transform (new_tm); velocity.X = 0; velocity.Y = 0; velocity.Z = 0; rigid_body->Set_Velocity (velocity); rigid_body->Set_Angular_Velocity(velocity); } } HostGameObj = host; if ( host != NULL ) { WWASSERT( host->Peek_Model() ); HostGameObjBone = host->Peek_Model()->Get_Bone_Index( bone_name ); // Make sure we get teleported immeadiately! Teleport_To_Host_Bone(); } // // "Dirty" the object for networking // Set_Object_Dirty_Bit( NetworkObjectClass::BIT_RARE, true ); return ; } void PhysicalGameObj::Reset_Server_Skips(BYTE value) { ServerUpdateSkips = value; } void PhysicalGameObj::Increment_Server_Skips(void) { if (ServerUpdateSkips < 254) { ServerUpdateSkips++; } } //----------------------------------------------------------------------------- void PhysicalGameObj::Reset_Radar_Blip_Color_Type( void ) { switch( Get_Player_Type() ) { case PLAYERTYPE_NOD: RadarBlipColorType = RadarManager::BLIP_COLOR_TYPE_NOD; break; case PLAYERTYPE_GDI: RadarBlipColorType = RadarManager::BLIP_COLOR_TYPE_GDI; break; case PLAYERTYPE_MUTANT: RadarBlipColorType = RadarManager::BLIP_COLOR_TYPE_MUTANT; break; case PLAYERTYPE_RENEGADE:RadarBlipColorType = RadarManager::BLIP_COLOR_TYPE_RENEGADE; break; default: RadarBlipColorType = RadarManager::BLIP_COLOR_TYPE_NEUTRAL; break; } } //----------------------------------------------------------------------------- /* ** */ void PhysicalGameObj::Set_Animation( const char *animation_name, bool looping, float frame_offset ) { if ( AnimControl == NULL ) { Set_Anim_Control( new SimpleAnimControlClass ); // be sure we have a anim control } StringClass anim_name(animation_name,true); if ( !anim_name.Is_Empty() ) { // make sure it lead with model name if ( ::strchr( anim_name, '.' ) == NULL ) { Create_Animation_Name( anim_name, animation_name, Peek_Model()->Get_Name() ); } AnimControl->Set_Model( Peek_Model() ); AnimControl->Set_Animation( anim_name, 0, frame_offset ); AnimControl->Set_Mode( looping ? ANIM_MODE_LOOP : ANIM_MODE_ONCE ); // Force the object to start using the anim AnimControl->Update( 0 ); // // "Dirty" the object for networking // Set_Object_Dirty_Bit( NetworkObjectClass::BIT_RARE, true ); } } /* ** */ void PhysicalGameObj::Set_Animation_Frame ( const char *animation_name, int frame ) { if ( AnimControl == NULL ) { Set_Anim_Control( new SimpleAnimControlClass ); // be sure we have a anim control } StringClass anim_name(animation_name,true); if ( !anim_name.Is_Empty() ) { // make sure it lead with model name if ( ::strchr( anim_name, '.' ) == NULL ) { Create_Animation_Name( anim_name, animation_name, Peek_Model()->Get_Name() ); } AnimControl->Set_Model( Peek_Model() ); AnimControl->Set_Animation( anim_name, 0 ); AnimControl->Set_Mode( ANIM_MODE_STOP, frame ); // // "Dirty" the object for networking // Set_Object_Dirty_Bit( NetworkObjectClass::BIT_RARE, true ); } } /* ** */ void PhysicalGameObj::Set_Transform(const Matrix3D & tm) { WWASSERT(Peek_Physical_Object() != NULL); Peek_Physical_Object()->Set_Transform(tm); } const Matrix3D & PhysicalGameObj::Get_Transform(void) const { WWASSERT(Peek_Physical_Object() != NULL); return Peek_Physical_Object()->Get_Transform(); } void PhysicalGameObj::Get_Position(Vector3 * set_pos) const { WWASSERT(Peek_Physical_Object() != NULL); Peek_Physical_Object()->Get_Position(set_pos); } void PhysicalGameObj::Set_Position(const Vector3 & pos) { WWASSERT(Peek_Physical_Object() != NULL); Peek_Physical_Object()->Set_Position(pos); } float PhysicalGameObj::Get_Facing(void) const { WWASSERT(Peek_Physical_Object() != NULL); return Peek_Physical_Object()->Get_Facing(); } void PhysicalGameObj::Reset_Hibernating( void ) { // // Notify the object that is has just finished hiibernating // if (Is_Hibernating ()) { End_Hibernation (); } HibernationTimer = MIN( HIBERNATION_DELAY, HibernationTimer + TimeManager::Get_Frame_Seconds() * 2 ); } void PhysicalGameObj::Begin_Hibernation( void ) { if (_DisplayHibernating) { Debug_Say(( "Object %d Hibernating\n", Get_ID() )); } } void PhysicalGameObj::End_Hibernation( void ) { if (_DisplayHibernating) { Debug_Say(( "Object %d De-Hibernating\n", Get_ID() )); } } void PhysicalGameObj::Get_Information( StringClass & string ) { StringClass temp; temp.Format( "%s\n", Get_Definition().Get_Name() ); string += temp; temp.Format( "ID: %d\n", Get_ID() ); string += temp; temp.Format( "Health: %d\n", (int)DefenseObject.Get_Health() ); string += temp; if ( Is_Hibernating() ) { string += "HIBERNATING\n"; } DamageableGameObj::Get_Information( string ); } void PhysicalGameObj::Export_Creation( BitStreamClass &packet ) { DamageableGameObj::Export_Creation( packet ); // // Get the object's position and facing // Vector3 position (0, 0, 0); float facing = 0; Get_Position (&position); facing = Get_Facing (); // // Send the object's position // packet.Add( position.X, BITPACK_WORLD_POSITION_X ); packet.Add( position.Y, BITPACK_WORLD_POSITION_Y ); packet.Add( position.Z, BITPACK_WORLD_POSITION_Z ); // // Send the object's facing // packet.Add( facing ); /* // // Send the player type // int player_type = Get_Player_Type(); packet.Add( player_type ); */ return ; } void PhysicalGameObj::Import_Creation( BitStreamClass &packet ) { DamageableGameObj::Import_Creation( packet ); // // Read the object's position // Vector3 position (0, 0, 0); packet.Get( position.X, BITPACK_WORLD_POSITION_X ); packet.Get( position.Y, BITPACK_WORLD_POSITION_Y ); packet.Get( position.Z, BITPACK_WORLD_POSITION_Z ); // // Read the object's facing // float facing = 0; packet.Get( facing ); // // Build a matrix from the position and facing, then set it // Matrix3D tm (1); tm.Translate (position); tm.Rotate_Z (facing); Set_Transform (tm); /* // // Get the player type from the packet // int player_type = 0; packet.Get( player_type ); Set_Player_Type( player_type ); */ return ; } void PhysicalGameObj::Export_Rare( BitStreamClass &packet ) { DamageableGameObj::Export_Rare( packet ); // // Pass the model name across // WWASSERT(Peek_Physical_Object() != NULL); WWASSERT(Peek_Physical_Object()->Peek_Model() != NULL); //WWASSERT(Peek_Physical_Object()->Peek_Model()->Get_Name()); const char *model_name = Peek_Physical_Object()->Peek_Model()->Get_Name(); WWASSERT(model_name != NULL); WWASSERT(::strlen(model_name) < 256); packet.Add_Terminated_String( model_name, true ); // // Get information about the animation // StringClass animation_name; int target_frame = 0; int curr_frame = 0; AnimMode anim_mode = ANIM_MODE_TARGET; if (AnimControl != NULL) { animation_name = AnimControl->Get_Animation_Name(); target_frame = AnimControl->Get_Target_Frame (); curr_frame = AnimControl->Get_Current_Frame (); anim_mode = AnimControl->Get_Mode (); } // // Send the information to the client // packet.Add_Terminated_String( (const char *)animation_name, true ); packet.Add( curr_frame ); packet.Add( target_frame ); packet.Add( anim_mode ); // // Lookup the id of the host object // int host_model_id = 0; if (HostGameObj != NULL) { host_model_id = HostGameObj.Get_Ptr ()->Get_ID (); } // // Pass the host information across // packet.Add( host_model_id ); packet.Add( HostGameObjBone ); // // Send the player type // int player_type = Get_Player_Type(); packet.Add( player_type ); packet.Add( HUDPokableIndicatorEnabled ); // We want to copy the hidden status for cinematics, (specefically, airstrip drops of vehicles) // but, we only want to change vehicles visibility if ( As_VehicleGameObj() != NULL ) { // Send hidden bool hidden = false; if ( Peek_Model() ) { hidden = !!(Peek_Model()->Is_Hidden()); } packet.Add( hidden ); } return ; } void PhysicalGameObj::Import_Rare( BitStreamClass &packet ) { DamageableGameObj::Import_Rare( packet ); // // Get the model name // StringClass model_name; packet.Get_Terminated_String( model_name.Get_Buffer( 256 ), 256, true ); // // Set the new model (if necessary) // const char *old_model_name = Peek_Physical_Object()->Peek_Model()->Get_Name(); if ( model_name.Compare_No_Case (old_model_name) != 0 ) { Peek_Physical_Object()->Set_Model_By_Name( model_name ); } // // Get information about the animation // StringClass animation_name; int target_frame = 0; int curr_frame = 0; int anim_mode = ANIM_MODE_TARGET; packet.Get_Terminated_String( animation_name.Get_Buffer( 256 ), 256, true ); packet.Get( curr_frame ); packet.Get( target_frame ); packet.Get( anim_mode ); // // Pass the animation information onto the controller // if (AnimControl != NULL) { AnimControl->Set_Animation( animation_name, 0, curr_frame ); AnimControl->Set_Target_Frame( target_frame ); AnimControl->Set_Mode( (AnimMode)anim_mode ); } // // Get the host information // int host_model_id = 0; packet.Get( host_model_id ); packet.Get( HostGameObjBone ); // // Change the host object // PendingHostObjID = 0; // Assume no pending if ( host_model_id != 0 ) { HostGameObj = GameObjManager::Find_PhysicalGameObj( host_model_id ); if ( HostGameObj.Get_Ptr() == NULL ) { PendingHostObjID = host_model_id; // Pending Host Reset_Hibernating(); } } else { HostGameObj = NULL; } // // Get the player type from the packet // int player_type = packet.Get( player_type ); Set_Player_Type( player_type ); HUDPokableIndicatorEnabled = packet.Get( HUDPokableIndicatorEnabled ); if ( As_VehicleGameObj() != NULL ) { // Get Hidden bool hidden = packet.Get( hidden ); if ( Peek_Model() ) { Peek_Model()->Set_Hidden( hidden ); } } return ; } void PhysicalGameObj::Export_Frequent( BitStreamClass &packet ) { bool on_host_bone = false; if ( HostGameObj.Get_Ptr() ) { RenderObjClass * model = ((PhysicalGameObj * )(HostGameObj.Get_Ptr()))->Peek_Model(); on_host_bone = ( model != NULL ); } packet.Add(on_host_bone); } void PhysicalGameObj::Import_Frequent( BitStreamClass &packet ) { bool on_host_bone; packet.Get(on_host_bone); if ( (on_host_bone) && (Peek_Physical_Object()) && (Peek_Physical_Object()->As_MoveablePhysClass()) ) { MoveablePhysClass * movephys = Peek_Physical_Object()->As_MoveablePhysClass(); movephys->Set_Velocity(Vector3(0,0,0)); } } /* ** */ void PhysicalGameObj::Set_Conversation( ActiveConversationClass *conversation ) { REF_PTR_SET (ActiveConversation, conversation); return ; } /* ** */ void PhysicalGameObj::Hide_Muzzle_Flashes( bool hide ) { RenderObjClass * model = Peek_Model(); if ( model ) { for (int i=0; iGet_Num_Sub_Objects(); i++) { RenderObjClass * robj = model->Get_Sub_Object(i); if (strstr(robj->Get_Name(),"MUZZLEFLASH") || strstr(robj->Get_Name(),"MZ")) { robj->Set_Hidden( hide ); } robj->Release_Ref(); } } } /* ** Get_Vis_ID - return the vis id for this object */ int PhysicalGameObj::Get_Vis_ID () { PhysClass *phys_obj = Peek_Physical_Object (); // // Do we have a physics object we can use? // if (phys_obj != NULL) { return phys_obj->Get_Vis_Object_ID(); } return -1; } void PhysicalGameObj::Set_Player_Type(int id) { DamageableGameObj::Set_Player_Type(id); Reset_Radar_Blip_Color_Type(); } /* if (Is_Team_Player()) { // // Handle tinting for soldiers, pedestals, flags // if ( stricmp( Get_Definition().Get_Name(), "CtfFlag" ) == 0 || stricmp( Get_Definition().Get_Name(), "CtfPedestal" ) == 0) { Set_Tint( Get_Team_Color() ); } } */ //----------------------------------------------------------------------------- /* void Tint(RenderObjClass *robj, const Vector3 & color) { // Debug_Say(( "%s has %d sub objs\n", robj->Get_Name(), robj->Get_Num_Sub_Objects() )); for( int so = 0; so < robj->Get_Num_Sub_Objects(); so++ ) { RenderObjClass *child = robj->Get_Sub_Object( so ); Tint(child, color); child->Release_Ref(); } // Tint the commando body MaterialInfoClass * matinfo = robj->Get_Material_Info(); if ( matinfo ) { for ( int vm = 0; vm < matinfo->Vertex_Material_Count(); vm++ ) { VertexMaterialClass *vertmat = matinfo->Peek_Vertex_Material(vm); // Debug_Say(( "VM %p %s\n", vertmat, vertmat->Get_Name() )); // if (1) {// !strnicmp( vertmat->Get_Name(), "tintt", 5 ) ) { vertmat->Set_Diffuse( color[0], color[1], color[2] ); vertmat->Set_Ambient( color[0], color[1], color[2] ); // } } matinfo->Release_Ref(); } } //----------------------------------------------------------------------------- void PhysicalGameObj::Set_Tint(Vector3 color) { if (TintColor != color && Peek_Model() != NULL) { Tint(Peek_Model(), color); TintColor = color; } } */ void PhysicalGameObj::Enable_HUD_Pokable_Indicator( bool enable ) { HUDPokableIndicatorEnabled = enable; Set_Object_Dirty_Bit( NetworkObjectClass::BIT_RARE, true ); } void PhysicalGameObj::Object_Shattered_Something ( PhysClass * observed_obj, PhysClass * shattered_obj, int surface_type ) { const Matrix3D & tm = observed_obj->Get_Transform(); SurfaceEffectsManager::Apply_Effect( surface_type, SurfaceEffectsManager::HITTER_TYPE_BULLET, tm, NULL, NULL, false, // no decals false // no emitter ); }