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/damageablegameobj.cpp

457 lines
14 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/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( "<NA>", 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() ) );
}