/* ** 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/damageablegameobj.cpp $* * * * $Author:: Tom_s $* * * * $Modtime:: 12/18/01 3:02p $* * * * $Revision:: 19 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "damageablegameobj.h" #include "debug.h" #include "armedgameobj.h" #include "playertype.h" #include "colors.h" /* ** DamageableGameObjDef - Defintion class for a DamageableGameObj */ DamageableGameObjDef::DamageableGameObjDef( void ) : TranslatedNameID( 0 ), EncyclopediaType( EncyclopediaMgrClass::TYPE_UNKNOWN ), EncyclopediaID( 0 ), NotTargetable( false ), DefaultPlayerType( PLAYERTYPE_NEUTRAL ) { DEFENSEOBJECTDEF_EDITABLE_PARAMS( DamageableGameObjDef, DefenseObjectDef ); EDITABLE_PARAM( DamageableGameObjDef, ParameterClass::TYPE_STRINGSDB_ID, TranslatedNameID ); FILENAME_PARAM( DamageableGameObjDef, InfoIconTextureFilename, "InfoIconTextureFilename", ".TGA" ); #ifdef PARAM_EDITING_ON EnumParameterClass *param = new EnumParameterClass( (int *)&EncyclopediaType ); param->Set_Name( "Encyclopedia Type" ); param->Add_Value( "", 0 ); param->Add_Value( "Character", EncyclopediaMgrClass::TYPE_CHARACTER ); param->Add_Value( "Weapon", EncyclopediaMgrClass::TYPE_WEAPON ); param->Add_Value( "Vehicle", EncyclopediaMgrClass::TYPE_VEHICLE ); param->Add_Value( "Building", EncyclopediaMgrClass::TYPE_BUILDING ); GENERIC_EDITABLE_PARAM( DamageableGameObjDef, param ) param = new EnumParameterClass( &DefaultPlayerType ); param->Set_Name ("PlayerType"); param->Add_Value ( "Mutant", PLAYERTYPE_MUTANT ); param->Add_Value ( "Unteamed", PLAYERTYPE_NEUTRAL ); param->Add_Value ( "Renegade", PLAYERTYPE_RENEGADE ); param->Add_Value ( "Nod", PLAYERTYPE_NOD ); param->Add_Value ( "GDI", PLAYERTYPE_GDI ); GENERIC_EDITABLE_PARAM(DamageableGameObjDef,param) #endif EDITABLE_PARAM( DamageableGameObjDef, ParameterClass::TYPE_INT, EncyclopediaID ); EDITABLE_PARAM( DamageableGameObjDef, ParameterClass::TYPE_BOOL, NotTargetable ); return ; } enum { CHUNKID_DEF_PARENT = 207011205, CHUNKID_DEF_VARIABLES, CHUNKID_DEF_DEFENSEOBJECTDEF, MICROCHUNKID_DEF_TRANSLATED_NAME_ID = 1, MICROCHUNKID_DEF_INFO_ICON_TEXTURE_FILENAME, MICROCHUNKID_DEF_ENCY_TYPE, MICROCHUNKID_DEF_ENCY_ID, MICROCHUNKID_DEF_NOT_TARGETABLE, MICROCHUNKID_DEF_DEFAULT_PLAYER_TYPE, }; bool DamageableGameObjDef::Save( ChunkSaveClass & csave ) { csave.Begin_Chunk( CHUNKID_DEF_PARENT ); ScriptableGameObjDef::Save( csave ); csave.End_Chunk(); csave.Begin_Chunk( CHUNKID_DEF_VARIABLES ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_TRANSLATED_NAME_ID, TranslatedNameID ); WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_DEF_INFO_ICON_TEXTURE_FILENAME, InfoIconTextureFilename ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_ENCY_TYPE, EncyclopediaType ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_ENCY_ID, EncyclopediaID ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_NOT_TARGETABLE, NotTargetable ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_DEF_DEFAULT_PLAYER_TYPE, DefaultPlayerType ); csave.End_Chunk(); csave.Begin_Chunk( CHUNKID_DEF_DEFENSEOBJECTDEF ); DefenseObjectDef.Save(csave); csave.End_Chunk(); return true; } bool DamageableGameObjDef::Load( ChunkLoadClass &cload ) { while (cload.Open_Chunk()) { switch(cload.Cur_Chunk_ID()) { case CHUNKID_DEF_PARENT: ScriptableGameObjDef::Load( cload ); break; case CHUNKID_DEF_VARIABLES: while (cload.Open_Micro_Chunk()) { switch(cload.Cur_Micro_Chunk_ID()) { READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_TRANSLATED_NAME_ID, TranslatedNameID ); READ_MICRO_CHUNK_WWSTRING( cload, MICROCHUNKID_DEF_INFO_ICON_TEXTURE_FILENAME, InfoIconTextureFilename ); READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_ENCY_TYPE, EncyclopediaType ); READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_ENCY_ID, EncyclopediaID ); READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_NOT_TARGETABLE, NotTargetable ); READ_MICRO_CHUNK( cload, MICROCHUNKID_DEF_DEFAULT_PLAYER_TYPE, DefaultPlayerType ); default: Debug_Say(( "Unhandled MicroChunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__)); break; } cload.Close_Micro_Chunk(); } break; case CHUNKID_DEF_DEFENSEOBJECTDEF: DefenseObjectDef.Load(cload); break; default: Debug_Say(( "Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__)); break; } cload.Close_Chunk(); } return true; } /* ** DamageableGameObj */ DamageableGameObj::DamageableGameObj( void ) : IsHealthBarDisplayed( true ) { Set_Player_Type(PLAYERTYPE_NEUTRAL); } DamageableGameObj::~DamageableGameObj( void ) { Remove_All_Observers(); } /* ** */ void DamageableGameObj::Init( const DamageableGameObjDef & definition ) { ScriptableGameObj::Init( definition ); Copy_Settings( definition ); return ; } /* ** */ void DamageableGameObj::Copy_Settings( const DamageableGameObjDef & definition ) { Set_Player_Type(definition.DefaultPlayerType); DefenseObject.Init(definition.DefenseObjectDef, this ); return ; } /* ** */ void DamageableGameObj::Re_Init( const DamageableGameObjDef & definition ) { int old_player_type = PlayerType; // // Record the health and shield percent so we can restore the // appropriate amount of health and shield after we've re-initialized // //float health_percent = DefenseObject.Get_Health() / DefenseObject.Get_Health_Max(); //float shield_percent = DefenseObject.Get_Shield_Strength() / DefenseObject.Get_Shield_Strength_Max(); // // Re-initialize the base class // ScriptableGameObj::Re_Init( definition ); // // Copy any internal settings from the definition // Copy_Settings( definition ); // // Reset the health and shield to appropriate values // //DefenseObject.Set_Health (DefenseObject.Get_Health_Max () * health_percent); //DefenseObject.Set_Shield_Strength (DefenseObject.Get_Shield_Strength_Max () * shield_percent); Set_Player_Type( old_player_type ); return ; } const DamageableGameObjDef & DamageableGameObj::Get_Definition( void ) const { return (const DamageableGameObjDef &)BaseGameObj::Get_Definition(); } /* ** DamageableGameObj Save and Load */ enum { CHUNKID_PARENT = 207011212, CHUNKID_DEFENSEOBJECT, CHUNKID_VARIABLES, MICROCHUNKID_PLAYER_TYPE = 1, MICROCHUNKID_IS_HEALTH_BAR_DISPLAYED, }; bool DamageableGameObj::Save( ChunkSaveClass & csave ) { csave.Begin_Chunk( CHUNKID_PARENT ); ScriptableGameObj::Save( csave ); csave.End_Chunk(); csave.Begin_Chunk( CHUNKID_VARIABLES ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_PLAYER_TYPE, PlayerType ); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_IS_HEALTH_BAR_DISPLAYED, IsHealthBarDisplayed ); csave.End_Chunk(); csave.Begin_Chunk( CHUNKID_DEFENSEOBJECT ); DefenseObject.Save(csave); csave.End_Chunk(); return true; } bool DamageableGameObj::Load( ChunkLoadClass &cload ) { while (cload.Open_Chunk()) { switch(cload.Cur_Chunk_ID()) { case CHUNKID_PARENT: ScriptableGameObj::Load( cload ); break; case CHUNKID_VARIABLES: while (cload.Open_Micro_Chunk()) { switch(cload.Cur_Micro_Chunk_ID()) { READ_MICRO_CHUNK( cload, MICROCHUNKID_PLAYER_TYPE, PlayerType ); READ_MICRO_CHUNK( cload, MICROCHUNKID_IS_HEALTH_BAR_DISPLAYED, IsHealthBarDisplayed ); default: Debug_Say(( "Unhandled MicroChunk:%d File:%s Line:%d\r\n",cload.Cur_Micro_Chunk_ID(),__FILE__,__LINE__)); break; } cload.Close_Micro_Chunk(); } break; case CHUNKID_DEFENSEOBJECT: DefenseObject.Load( cload ); break; default: Debug_Say(( "Unhandled Chunk:%d File:%s Line:%d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__)); break; } cload.Close_Chunk(); } return true; } void DamageableGameObj::Apply_Damage( const OffenseObjectClass & damager, float scale, int alternate_skin ) { if ( DefenseObject.Get_Health() <= 0 ) { return; } if (Is_Delete_Pending()) { return; } float old_health = DefenseObject.Get_Health(); float old_shield = DefenseObject.Get_Shield_Strength(); DefenseObject.Apply_Damage( damager, scale, alternate_skin ); float new_health = DefenseObject.Get_Health(); float new_shield = DefenseObject.Get_Shield_Strength(); float diff = old_health + old_shield - new_health - new_shield; // Notify the observers const GameObjObserverList & observer_list = Get_Observers(); for( int index = 0; index < observer_list.Count(); index++ ) { observer_list[ index ]->Damaged( this, damager.Get_Owner(), diff ); } if ( DefenseObject.Get_Health() <= 0 ) { // notify the observers for( int index = 0; index < observer_list.Count(); index++ ) { observer_list[ index ]->Killed( this, damager.Get_Owner() ); } Completely_Damaged( damager ); } } //------------------------------------------------------------------------------------ /*void DamageableGameObj::Get_Information( StringClass & string ) { // If we just came from the editor, call created on all out observers const GameObjObserverList & observer_list = Get_Observers(); for( int index = 0; index < observer_list.Count(); index++ ) { StringClass temp; temp.Format( "%s\n", observer_list[ index ]->Get_Name() ); string += temp; } } */ /* ** */ void DamageableGameObj::Export_Occasional( BitStreamClass &packet ) { ScriptableGameObj::Export_Occasional( packet ); // // Export the defense object's state // DefenseObject.Export (packet); } /* ** */ void DamageableGameObj::Import_Occasional( BitStreamClass &packet ) { ScriptableGameObj::Import_Occasional( packet ); // // Update the defense object's state // float old_health = DefenseObject.Get_Health(); DefenseObject.Import (packet); float new_health = DefenseObject.Get_Health(); /* // // Hack ! // if ( Is_Delete_Pending() ) { if ( new_health != 0 ) { DefenseObject.Set_Health( 0 ); new_health = DefenseObject.Get_Health(); } } else { if ( new_health == 0 ) { DefenseObject.Set_Health( 0.01f ); new_health = DefenseObject.Get_Health(); } } */ if (old_health > 0 && old_health > new_health) { // Notify the observers that we are damaged const GameObjObserverList& observer_list = Get_Observers(); int count = observer_list.Count(); for (int index = 0; index < count; ++index) { observer_list[index]->Damaged(this, NULL, old_health - new_health); } } // // Check to see if the object is completely damaged // if ( old_health > 0 && new_health <= 0 ) { // Notify the observers that the building has been destroyed const GameObjObserverList& observer_list = Get_Observers(); int count = observer_list.Count(); for (int index = 0; index < count; ++index) { observer_list[index]->Killed(this, NULL); } OffenseObjectClass dummy_offense_obj; Completely_Damaged( dummy_offense_obj ); } } //----------------------------------------------------------------------------- bool DamageableGameObj::Is_Team_Player(void) { return PlayerType == PLAYERTYPE_NOD || PlayerType == PLAYERTYPE_GDI; } //----------------------------------------------------------------------------- Vector3 DamageableGameObj::Get_Team_Color(void) { return Get_Color_For_Team(PlayerType); } //----------------------------------------------------------------------------- void DamageableGameObj::Set_Player_Type(int id) { PlayerType = id; Set_Object_Dirty_Bit( NetworkObjectClass::BIT_RARE, true ); } //----------------------------------------------------------------------------- bool DamageableGameObj::Is_Teammate(DamageableGameObj * p_obj) { WWASSERT(p_obj != NULL); return ((p_obj == this) || (Is_Team_Player() && Get_Player_Type() == p_obj->Get_Player_Type())); } bool DamageableGameObj::Is_Enemy(DamageableGameObj * p_obj) { WWASSERT(p_obj != NULL); return ( (p_obj != this) && Player_Types_Are_Enemies( Get_Player_Type(), p_obj->Get_Player_Type() ) ); }