/*
** 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/damage.cpp $*
* *
* $Author:: Greg_h $*
* *
* $Modtime:: 6/21/02 2:42p $*
* *
* $Revision:: 114 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*
** Includes
*/
#include "damage.h"
#include "assets.h"
#include "debug.h"
#include "smartgameobj.h"
#include "combat.h"
#include "slist.h"
#include "wwpacket.h"
#include "playerdata.h"
#include "soldier.h"
#include "weapons.h"
#include "diaglog.h"
#include "playertype.h"
#include
#include
#include "wwmath.h"
#include "encoderlist.h"
#include "bitpackids.h"
#include "gametype.h"
#include "csdamageevent.h"
#ifdef WWDEBUG
bool DefenseObjectClass::OneShotKills = false;
#endif // _WWDEBUG
ArmorWarheadManager ArmorManagerObject;
DynamicVectorClass ArmorNames;
DynamicVectorClass WarheadNames;
DynamicVectorClass ArmorSaveIDs;
DynamicVectorClass WarheadSaveIDs;
DynamicVectorClass SoftArmorTable;
// For each Warhead
DynamicVectorClass SpecialDamageTypes;
DynamicVectorClass SpecialDamageProbability;
DynamicVectorClass VisceroidProbability;
// For each special damage type
int SpecialDamageWarhead[ArmorWarheadManager::NUM_SPECIAL_DAMAGE_TYPES];
float SpecialDamageDuration[ArmorWarheadManager::NUM_SPECIAL_DAMAGE_TYPES];
float SpecialDamageScale[ArmorWarheadManager::NUM_SPECIAL_DAMAGE_TYPES];
StringClass SpecialDamageExplosion[ArmorWarheadManager::NUM_SPECIAL_DAMAGE_TYPES];
// Skins that are imprevious to special damage
DynamicVectorClass ImperviousSkins[ArmorWarheadManager::NUM_SPECIAL_DAMAGE_TYPES];
safe_float * ArmorWarheadManager::Multipliers = NULL;
safe_float * ArmorWarheadManager::Absorbsion = NULL;
#define ARMOR_INI_FILENAME "armor.ini"
#define SECTION_SOFT_ARMOR_TYPES "Soft_Armor_Types"
#define SECTION_HARD_ARMOR_TYPES "Hard_Armor_Types"
#define SECTION_ARMOR_TYPES "Armor_Types"
#define SECTION_WARHEAD_TYPES "Warhead_Types"
#define SECTION_SCALE "Scale_%s"
#define SECTION_SHIELD "Shield_%s"
#define SECTION_ARMOR_SAVE_IDS "Armor_Save_IDs"
#define SECTION_WARHEAD_SAVE_IDS "Warhead_Save_IDs"
#define SECTION_SOFT_ARMOR "Soft_Armor"
#define SECTION_SPECIAL_DAMAGE_TYPE "Special_Damage_Type"
#define SECTION_SPECIAL_DAMAGE_PROBABILITY "Special_Damage_Probability"
#define SECTION_VISCEROID_PROBABILITY "Visceroid_Probability"
#define ENTRY_WARHEAD "Warhead"
#define ENTRY_DURATION "Duration"
#define ENTRY_SCALE "Scale"
#define ENTRY_EXPLOSION "Explosion"
/*
**
*/
void ArmorWarheadManager::Init( void )
{
Shutdown();
INIClass * armorINI = Get_INI( ARMOR_INI_FILENAME );
if (armorINI != NULL) {
WWASSERT( armorINI && armorINI->Section_Count() > 0 );
int count, entry;
// Load Armor Types
count = armorINI->Entry_Count( SECTION_ARMOR_TYPES );
for ( entry = 0; entry < count; entry++ ) {
char entry_name[80];
armorINI->Get_String( SECTION_ARMOR_TYPES,
armorINI->Get_Entry( SECTION_ARMOR_TYPES, entry),
"", entry_name, sizeof( entry_name ) );
ArmorNames.Add( entry_name );
ArmorSaveIDs.Add( entry );
SoftArmorTable.Add( false );
}
// Load Warhead Types
count = armorINI->Entry_Count( SECTION_WARHEAD_TYPES );
for ( entry = 0; entry < count; entry++ ) {
char entry_name[80];
armorINI->Get_String( SECTION_WARHEAD_TYPES,
armorINI->Get_Entry( SECTION_WARHEAD_TYPES, entry),
"", entry_name, sizeof( entry_name ) );
WarheadNames.Add( entry_name );
WarheadSaveIDs.Add( entry );
}
int armor_num;
// Load Armor Save IDs
for ( armor_num = 0; armor_num < ArmorNames.Count(); armor_num++ ) {
int id = armorINI->Get_Int( SECTION_ARMOR_SAVE_IDS, ArmorNames[armor_num], -100 );
if ( id == -100 ) {
Debug_Say(( "Missing Armor_Save_ID for %s\n", ArmorNames[armor_num] ));
}
ArmorSaveIDs[ armor_num ] = id;
}
// Load Warhead Save IDs
int warhead_num;
for ( warhead_num = 0; warhead_num < WarheadNames.Count(); warhead_num++ ) {
int id = armorINI->Get_Int( SECTION_WARHEAD_SAVE_IDS, WarheadNames[warhead_num], -100 );
if ( id == -100 ) {
Debug_Say(( "Missing Warhead_Save_ID for %s\n", WarheadNames[warhead_num] ));
}
WarheadSaveIDs[ warhead_num ] = id;
}
// Load Soft Armor Table
count = armorINI->Entry_Count( SECTION_SOFT_ARMOR );
for ( entry = 0; entry < count; entry++ ) {
char entry_name[80];
armorINI->Get_String( SECTION_SOFT_ARMOR,
armorINI->Get_Entry( SECTION_SOFT_ARMOR, entry),
"", entry_name, sizeof( entry_name ) );
ArmorType type = Get_Armor_Type( entry_name );
SoftArmorTable[ type ] = true;
}
// Load Multipliers
Multipliers = new safe_float[ Get_Num_Armor_Types() * Get_Num_Warhead_Types() ];
for ( armor_num = 0; armor_num < ArmorNames.Count(); armor_num++ ) {
char section_name[80];
sprintf( section_name, SECTION_SCALE, ArmorNames[armor_num] );
for ( int warhead_num = 0; warhead_num < WarheadNames.Count(); warhead_num++ ) {
Multipliers[ armor_num * Get_Num_Warhead_Types() + warhead_num ] =
armorINI->Get_Float( section_name, WarheadNames[warhead_num], 1.0f );
}
}
// Load Shield Absorbsion
Absorbsion = new safe_float[ Get_Num_Armor_Types() * Get_Num_Warhead_Types() ];
for ( armor_num = 0; armor_num < ArmorNames.Count(); armor_num++ ) {
char section_name[80];
sprintf( section_name, SECTION_SHIELD, ArmorNames[armor_num] );
for ( int warhead_num = 0; warhead_num < WarheadNames.Count(); warhead_num++ ) {
Absorbsion[ armor_num * Get_Num_Warhead_Types() + warhead_num ] =
armorINI->Get_Float( section_name, WarheadNames[warhead_num], 0.0f );
}
}
// Load Warhead Special Damage
for ( warhead_num = 0; warhead_num < WarheadNames.Count(); warhead_num++ ) {
SpecialDamageTypes.Add( SPECIAL_DAMAGE_TYPE_NONE );
SpecialDamageProbability.Add( 0 );
VisceroidProbability.Add( 0 );
StringClass special_damage_type(0,true);
armorINI->Get_String(special_damage_type, SECTION_SPECIAL_DAMAGE_TYPE, WarheadNames[warhead_num] );
if ( !special_damage_type.Is_Empty() ) {
if ( !stricmp( special_damage_type, "FIRE" ) ) {
SpecialDamageTypes[warhead_num] = SPECIAL_DAMAGE_TYPE_FIRE;
}
if ( !stricmp( special_damage_type, "CHEM" ) ) {
SpecialDamageTypes[warhead_num] = SPECIAL_DAMAGE_TYPE_CHEM;
}
if ( !stricmp( special_damage_type, "CNC_FIRE" ) ) {
SpecialDamageTypes[warhead_num] = SPECIAL_DAMAGE_TYPE_CNC_FIRE;
}
if ( !stricmp( special_damage_type, "CNC_CHEM" ) ) {
SpecialDamageTypes[warhead_num] = SPECIAL_DAMAGE_TYPE_CNC_CHEM;
}
if ( !stricmp( special_damage_type, "ELECTRIC" ) ) {
SpecialDamageTypes[warhead_num] = SPECIAL_DAMAGE_TYPE_ELECTRIC;
}
}
SpecialDamageProbability[warhead_num] =
armorINI->Get_Float( SECTION_SPECIAL_DAMAGE_PROBABILITY, WarheadNames[warhead_num], 0.0f );
VisceroidProbability[warhead_num] =
armorINI->Get_Float( SECTION_VISCEROID_PROBABILITY, WarheadNames[warhead_num], 0.0f );
// Debug_Say(( "Warhead %s %d %f\n", WarheadNames[warhead_num],
// SpecialDamageTypes[warhead_num], SpecialDamageProbability[warhead_num] ));
}
int i;
for ( i = 0; i < NUM_SPECIAL_DAMAGE_TYPES; i++ ) {
SpecialDamageWarhead[i] = 0;
SpecialDamageDuration[i] = 0;
SpecialDamageScale[i] = 0;
SpecialDamageExplosion[i] = "";
}
StringClass temp_string(0,true);
for ( i = SPECIAL_DAMAGE_TYPE_FIRE; i < NUM_SPECIAL_DAMAGE_TYPES; i++ ) {
const char * SpecialDamageSectionNames[NUM_SPECIAL_DAMAGE_TYPES] = {
"Special_Damage_None",
"Special_Damage_Fire",
"Special_Damage_Chem",
"Special_Damage_Electric",
"Special_Damage_CNC_Fire",
"Special_Damage_CNC_Chem",
};
StringClass section(SpecialDamageSectionNames[i],true);
SpecialDamageWarhead[i] = Get_Warhead_Type( armorINI->Get_String( temp_string, section, ENTRY_WARHEAD ) );
SpecialDamageDuration[i] = armorINI->Get_Float( section, ENTRY_DURATION, 0 );
SpecialDamageScale[i] = armorINI->Get_Float( section, ENTRY_SCALE, 1 );
SpecialDamageExplosion[i] = armorINI->Get_String( temp_string, section, ENTRY_EXPLOSION );
}
//
// Copy fire into super fire
//
SpecialDamageWarhead[SPECIAL_DAMAGE_TYPE_SUPER_FIRE] = SpecialDamageWarhead[SPECIAL_DAMAGE_TYPE_FIRE];
SpecialDamageDuration[SPECIAL_DAMAGE_TYPE_SUPER_FIRE] = SpecialDamageDuration[SPECIAL_DAMAGE_TYPE_FIRE];
SpecialDamageScale[SPECIAL_DAMAGE_TYPE_SUPER_FIRE] = SpecialDamageScale[SPECIAL_DAMAGE_TYPE_FIRE];
SpecialDamageExplosion[SPECIAL_DAMAGE_TYPE_SUPER_FIRE] = SpecialDamageExplosion[SPECIAL_DAMAGE_TYPE_FIRE];
// Load Impervious skin
for ( int damage = SPECIAL_DAMAGE_TYPE_FIRE; damage < NUM_SPECIAL_DAMAGE_TYPES; damage++ ) {
const char * ImperviousSectionNames[NUM_SPECIAL_DAMAGE_TYPES] = {
"Impervious_None",
"Impervious_Fire",
"Impervious_Chem",
"Impervious_Electric",
"Impervious_CNC_Fire",
"Impervious_CNC_Chem",
};
StringClass section = ImperviousSectionNames[damage];
int count = armorINI->Entry_Count( section );
for ( entry = 0; entry < count; entry++ ) {
StringClass entry_name = armorINI->Get_String( temp_string, section, armorINI->Get_Entry( section, entry) );
if ( !entry_name.Is_Empty() ) {
ImperviousSkins[damage].Add( Get_Armor_Type( entry_name ) );
}
}
// for ( int i = 0; i < ImperviousSkins[damage].Count(); i++ ) {
// Debug_Say(( "Loaded %s %d\n", (const char *)section, ImperviousSkins[damage][i] ));
// }
}
Release_INI( armorINI );
} else {
Debug_Say(("ArmorWarheadManager::Init - Unable to load %s\n", ARMOR_INI_FILENAME));
}
}
void ArmorWarheadManager::Shutdown( void )
{
if ( Multipliers != NULL ) {
delete [] Multipliers;
Multipliers = NULL;
}
if ( Absorbsion != NULL ) {
delete [] Absorbsion;
Absorbsion = NULL;
}
ArmorNames.Delete_All();
WarheadNames.Delete_All();
ArmorSaveIDs.Delete_All();
WarheadSaveIDs.Delete_All();
SoftArmorTable.Delete_All();
SpecialDamageTypes.Delete_All();
SpecialDamageProbability.Delete_All();
VisceroidProbability.Delete_All();
for (int i=0; i= 0 );
WWASSERT( (int)type < Get_Num_Armor_Types() );
return ArmorNames[type];
}
const char * ArmorWarheadManager::Get_Warhead_Name( WarheadType type )
{
WWASSERT( (int)type >= 0 );
WWASSERT( (int)type < Get_Num_Warhead_Types() );
return WarheadNames[type];
}
float ArmorWarheadManager::Get_Damage_Multiplier( ArmorType armor, WarheadType warhead )
{
WWASSERT( (int)armor >= 0 );
WWASSERT( (int)armor < Get_Num_Armor_Types() );
WWASSERT( (int)warhead >= 0 );
WWASSERT( (int)warhead < Get_Num_Warhead_Types() );
return( Multipliers[ (unsigned int)armor * Get_Num_Warhead_Types() + (unsigned int)warhead ] );
/*
int index = armor * Get_Num_Warhead_Types() + warhead;
float multiplier = Multipliers[index];
WWASSERT(WWMath::Is_Valid_Float(multiplier));
Debug_Say(("index = %d, armor = %d, warhead = %d, multiplier = %5.2f\n",
index, armor, warhead, multiplier));
return multiplier;
*/
}
float ArmorWarheadManager::Get_Shield_Absorbsion( ArmorType armor, WarheadType warhead )
{
WWASSERT( (int)armor >= 0 );
WWASSERT( (int)armor < Get_Num_Armor_Types() );
WWASSERT( (int)warhead >= 0 );
WWASSERT( (int)warhead < Get_Num_Warhead_Types() );
return( Absorbsion[ (unsigned int)armor * Get_Num_Warhead_Types() + (unsigned int)warhead ] );
}
bool ArmorWarheadManager::Is_Armor_Soft( ArmorType armor )
{
return SoftArmorTable[armor];
}
int ArmorWarheadManager::Get_Armor_Save_ID( ArmorType type )
{
return ArmorSaveIDs[ type ];
}
ArmorType ArmorWarheadManager::Find_Armor_Save_ID( int id )
{
for ( int index = 0; index < ArmorSaveIDs.Count(); index++ ) {
if ( ArmorSaveIDs[ index ] == id ) {
return index;
}
}
//Debug_Say(( "FAILED to find ARMOR_SAVE_ID for %d\n", id ));
return 0;
}
int ArmorWarheadManager::Get_Warhead_Save_ID( WarheadType type )
{
return WarheadSaveIDs[ type ];
}
ArmorType ArmorWarheadManager::Find_Warhead_Save_ID( int id )
{
for ( int index = 0; index < WarheadSaveIDs.Count(); index++ ) {
if ( WarheadSaveIDs[ index ] == id ) {
return index;
}
}
Debug_Say(( "FAILED to find WARHEAD_SAVE_ID for %d\n", id ));
return 0;
}
ArmorWarheadManager::SpecialDamageType ArmorWarheadManager::Get_Special_Damage_Type( WarheadType type )
{
WWASSERT( (int)type >= 0 );
WWASSERT( (int)type < Get_Num_Warhead_Types() );
return (SpecialDamageType)SpecialDamageTypes[type];
}
float ArmorWarheadManager::Get_Special_Damage_Probability( WarheadType type )
{
WWASSERT( (int)type >= 0 );
WWASSERT( (int)type < Get_Num_Warhead_Types() );
return SpecialDamageProbability[type];
}
WarheadType ArmorWarheadManager::Get_Special_Damage_Warhead( SpecialDamageType type )
{
WWASSERT( (int)type >= 0 );
WWASSERT( (int)type < NUM_SPECIAL_DAMAGE_TYPES );
return SpecialDamageWarhead[type];
}
float ArmorWarheadManager::Get_Special_Damage_Duration( SpecialDamageType type )
{
WWASSERT( (int)type >= 0 );
WWASSERT( (int)type < NUM_SPECIAL_DAMAGE_TYPES );
return SpecialDamageDuration[type];
}
float ArmorWarheadManager::Get_Special_Damage_Scale( SpecialDamageType type )
{
WWASSERT( (int)type >= 0 );
WWASSERT( (int)type < NUM_SPECIAL_DAMAGE_TYPES );
return SpecialDamageScale[type];
}
const char * ArmorWarheadManager::Get_Special_Damage_Explosion( SpecialDamageType type )
{
WWASSERT( (int)type >= 0 );
WWASSERT( (int)type < NUM_SPECIAL_DAMAGE_TYPES );
return SpecialDamageExplosion[type];
}
float ArmorWarheadManager::Get_Visceroid_Probability( WarheadType type )
{
WWASSERT( (int)type >= 0 );
WWASSERT( (int)type < Get_Num_Warhead_Types() );
return VisceroidProbability[type];
}
bool ArmorWarheadManager::Is_Skin_Impervious( SpecialDamageType type, ArmorType skin )
{
WWASSERT( (int)type >= 0 );
WWASSERT( (int)type < NUM_SPECIAL_DAMAGE_TYPES );
for ( int i = 0; i < ImperviousSkins[type].Count(); i++ ) {
if ( ImperviousSkins[type][i] == skin ) {
return true;
}
}
return false;
}
/*
**
*/
OffenseObjectClass::OffenseObjectClass( const OffenseObjectClass & base ) :
Damage( base.Damage ),
Warhead( base.Warhead )
{
Set_Owner( base.Get_Owner() );
}
/*
** Save and Load
*/
enum {
CHUNKID_VARIABLES = 914991020,
CHUNKID_OWNER,
MICROCHUNKID_WARHEAD = 1,
MICROCHUNKID_DAMAGE,
MICROCHUNKID_HEALTH,
MICROCHUNKID_HEALTH_MAX,
MICROCHUNKID_SKIN,
MICROCHUNKID_SHIELD_STRENGTH,
MICROCHUNKID_SHIELD_STRENGTH_MAX,
MICROCHUNKID_SHIELD_TYPE,
XXXMICROCHUNKID_LAST_HEALTH,
XXXMICROCHUNKID_LAST_SKIN,
XXXMICROCHUNKID_LAST_SHIELD_STRENGTH,
XXXMICROCHUNKID_LAST_SHIELD_TYPE,
MICROCHUNKID_DAMAGE_POINTS,
MICROCHUNKID_DEATH_POINTS,
};
bool OffenseObjectClass::Save( ChunkSaveClass & csave )
{
csave.Begin_Chunk( CHUNKID_VARIABLES );
int save_id = ArmorWarheadManager::Get_Warhead_Save_ID( Warhead );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_WARHEAD, save_id );
WRITE_SAFE_MICRO_CHUNK( csave, MICROCHUNKID_DAMAGE, Damage, float );
csave.End_Chunk();
csave.Begin_Chunk( CHUNKID_OWNER );
Owner.Save( csave );
csave.End_Chunk();
return true;
}
bool OffenseObjectClass::Load( ChunkLoadClass &cload )
{
int save_id = -2;
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_VARIABLES:
while (cload.Open_Micro_Chunk()) {
switch(cload.Cur_Micro_Chunk_ID()) {
READ_MICRO_CHUNK( cload, MICROCHUNKID_WARHEAD, save_id );
READ_SAFE_MICRO_CHUNK( cload, MICROCHUNKID_DAMAGE, Damage, float);
default:
Debug_Say(( "Unrecognized Offense Variable chunkID\n" ));
break;
}
cload.Close_Micro_Chunk();
}
break;
case CHUNKID_OWNER:
Owner.Load( cload );
break;
default:
Debug_Say(( "Unrecognized Offense chunkID\n" ));
break;
}
cload.Close_Chunk();
}
Warhead = ArmorWarheadManager::Find_Warhead_Save_ID( save_id );
return true;
}
void DefenseObjectClass::Init( const DefenseObjectDefClass & def, DamageableGameObj * owner )
{
Health = def.Health;
HealthMax = def.HealthMax;
Skin = def.Skin;
ShieldStrength = def.ShieldStrength;
ShieldStrengthMax = def.ShieldStrengthMax;
ShieldType = def.ShieldType;
DamagePoints = def.DamagePoints;
DeathPoints = def.DeathPoints;
Set_Owner( owner );
}
bool DefenseObjectClass::Save( ChunkSaveClass & csave )
{
csave.Begin_Chunk( CHUNKID_VARIABLES );
WRITE_SAFE_MICRO_CHUNK( csave, MICROCHUNKID_HEALTH, Health, float );
WRITE_SAFE_MICRO_CHUNK( csave, MICROCHUNKID_HEALTH_MAX, HealthMax, float );
int skin_save_id = ArmorWarheadManager::Get_Armor_Save_ID( Skin );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SKIN, skin_save_id );
WRITE_SAFE_MICRO_CHUNK( csave, MICROCHUNKID_SHIELD_STRENGTH, ShieldStrength, float );
WRITE_SAFE_MICRO_CHUNK( csave, MICROCHUNKID_SHIELD_STRENGTH_MAX, ShieldStrengthMax, float );
int shield_save_id = ArmorWarheadManager::Get_Armor_Save_ID( ShieldType );
WRITE_MICRO_CHUNK( csave, MICROCHUNKID_SHIELD_TYPE, shield_save_id );
WRITE_SAFE_MICRO_CHUNK( csave, MICROCHUNKID_DAMAGE_POINTS, DamagePoints, float );
WRITE_SAFE_MICRO_CHUNK( csave, MICROCHUNKID_DEATH_POINTS, DeathPoints, float );
csave.End_Chunk();
csave.Begin_Chunk( CHUNKID_OWNER );
Owner.Save( csave );
csave.End_Chunk();
return true;
}
bool DefenseObjectClass::Load( ChunkLoadClass &cload )
{
int skin_save_id = -2;
int shield_save_id = -2;
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID()) {
case CHUNKID_VARIABLES:
while (cload.Open_Micro_Chunk()) {
switch(cload.Cur_Micro_Chunk_ID()) {
READ_SAFE_MICRO_CHUNK( cload, MICROCHUNKID_HEALTH, Health, float );
READ_SAFE_MICRO_CHUNK( cload, MICROCHUNKID_HEALTH_MAX, HealthMax, float );
READ_MICRO_CHUNK( cload, MICROCHUNKID_SKIN, skin_save_id );
READ_SAFE_MICRO_CHUNK( cload, MICROCHUNKID_SHIELD_STRENGTH, ShieldStrength, float );
READ_SAFE_MICRO_CHUNK( cload, MICROCHUNKID_SHIELD_STRENGTH_MAX, ShieldStrengthMax, float );
READ_MICRO_CHUNK( cload, MICROCHUNKID_SHIELD_TYPE, shield_save_id );
READ_SAFE_MICRO_CHUNK( cload, MICROCHUNKID_DAMAGE_POINTS, DamagePoints, float );
READ_SAFE_MICRO_CHUNK( cload, MICROCHUNKID_DEATH_POINTS, DeathPoints, float );
default:
Debug_Say(( "Unrecognized Defense Variable chunkID\n" ));
break;
}
cload.Close_Micro_Chunk();
}
break;
case CHUNKID_OWNER:
Owner.Load( cload );
break;
default:
Debug_Say(( "Unrecognized Defense chunkID\n" ));
break;
}
cload.Close_Chunk();
}
Skin = ArmorWarheadManager::Find_Armor_Save_ID( skin_save_id );
ShieldType = ArmorWarheadManager::Find_Armor_Save_ID( shield_save_id );
return true;
}
bool DefenseObjectClass::Is_Soft( void )
{
if ( !ArmorWarheadManager::Is_Armor_Soft( Skin ) ) {
return false;
}
if ( ( (float)ShieldStrength > 0 ) && ( !ArmorWarheadManager::Is_Armor_Soft( ShieldType ) ) ) {
return false;
}
return true;
}
/*
**
*/
float DefenseObjectClass::Apply_Damage( const OffenseObjectClass & offense, float scale, int alternate_skin )
{
#if 1
// If singleplay or non trusted clients, just apply damage normally, if server
bool normal = IS_SOLOPLAY || !cCsDamageEvent::Get_Are_Clients_Trusted();
DamageableGameObj * owner = Get_Owner();
// Buildings have no owner
if ( owner == NULL || owner->As_BuildingGameObj() ) {
normal = true;
}
if ( offense.ForceServerDamage || !offense.EnableClientDamage ) {
normal = true;
}
if ( normal ) {
if ( CombatManager::I_Am_Server() ) {
return Do_Damage( offense, scale, alternate_skin );
} else {
return Health;
}
}
// Client Damage
// If I am a client, and damager is my guy, tell the server that damage should apply
// If I am a server, and the damager has a client, ignore the damage
bool has_client = false;
bool my_client = false;
if ( offense.Get_Owner() != NULL && offense.Get_Owner()->As_SmartGameObj() ) {
int control_owner = offense.Get_Owner()->As_SmartGameObj()->Get_Control_Owner();
has_client = control_owner > 0;
my_client = control_owner == CombatManager::Get_My_Id();
}
if ( CombatManager::I_Am_Server() ) {
if ( my_client || !has_client ) {
return Do_Damage( offense, scale, alternate_skin );
} else {
return Health; // assume we will be notified by the owner Don't apply the damage
}
}
// I am a client. If this is my object, request damage
if ( my_client ) {
Request_Damage( offense, scale );
}
return Health; // Don't apply the damage
#else
// Client Authorative damage is no longer done here.
if ( CombatManager::I_Am_Server() ) {
return Do_Damage( offense, scale, alternate_skin );
} else {
return Health;
}
#endif
}
/*
**
*/
void DefenseObjectClass::Request_Damage( const OffenseObjectClass & offense, float scale )
{
WWASSERT(CombatManager::I_Am_Only_Client());
WWASSERT(cCsDamageEvent::Get_Are_Clients_Trusted() == true);
cCsDamageEvent * event = new cCsDamageEvent();
if ( event ) {
int damager_id = 0;
if ( offense.Get_Owner() ) {
damager_id = offense.Get_Owner()->Get_ID();
}
int damagee_id = 0;
if ( Get_Owner() ) {
damagee_id = Get_Owner()->Get_ID();
}
int warhead = offense.Get_Warhead();
float damage = offense.Get_Damage() * scale;
event->Init( damager_id, damagee_id, damage, warhead );
// Debug_Say(( "Requesting damage of %f from %d to %d\n", damage, damager_id, damagee_id ));
}
}
/*
**
*/
float DefenseObjectClass::Do_Damage( const OffenseObjectClass & offense, float scale, int alternate_skin )
{
SmartGameObj * smart = NULL;
if ( offense.Get_Owner() != NULL ) {
smart = offense.Get_Owner()->As_SmartGameObj();
}
//TSS102201
//
// Keep track of who is attempting to damage who. This is used as a server
// heuristic used in network priority calculations. It doesn't matter whether
// or not damage is actually done.
//
int damager_id = -1;
int damagee_id = -1;
if (offense.Get_Owner() != NULL) {
damager_id = offense.Get_Owner()->Get_Network_ID();
}
if (Get_Owner() != NULL) {
damagee_id = Get_Owner()->Get_Network_ID();
}
if (offense.Get_Owner() != NULL && damagee_id != -1) {
offense.Get_Owner()->Set_Last_Object_Id_I_Damaged(damagee_id);
}
if (Get_Owner() != NULL && damager_id != -1) {
Get_Owner()->Set_Last_Object_Id_I_Got_Damaged_By(damager_id);
}
float damage = offense.Get_Damage() * scale;
// Scale damage by difficulty if star is applying
if ( IS_MISSION && offense.Get_Owner() == COMBAT_STAR ) {
switch ( CombatManager::Get_Difficulty_Level() ) {
case 0: damage *= 2.0f; break;
case 1: damage *= 1.333f; break;
case 2: damage *= 1.0f; break;
};
}
float shield_damage = 0;
float damage_scale = 1;
float shield_damage_scale = 1;
if (alternate_skin != -1) {
damage_scale = ArmorWarheadManager::Get_Damage_Multiplier( alternate_skin, offense.Get_Warhead() );
shield_damage_scale = 0;
} else {
damage_scale = ArmorWarheadManager::Get_Damage_Multiplier( Skin, offense.Get_Warhead() );
shield_damage_scale = ArmorWarheadManager::Get_Damage_Multiplier( ShieldType, offense.Get_Warhead() );
}
// check for punish = no more damage
if ( smart != NULL && smart->Get_Player_Data() ) {
#define PUNISH_DELAY 60
if ( smart->Get_Player_Data()->Get_Punish_Timer() > PUNISH_DELAY ) {
damage_scale = 0;
shield_damage_scale = 0;
smart->Get_Player_Data()->Set_Money( 0 );
smart->Get_Player_Data()->Set_Score( 0 );
}
}
bool is_repair = false;
// check for repair on either health of shield
// Note: humans don't repair health, but vehicles do.
if ( (damage * damage_scale < 0) || (damage * shield_damage_scale < 0) ) { // We are repairing
is_repair = true;
Mark_Owner_Dirty();
// Apply first to health, then to shield
if ( Health < HealthMax && (damage_scale != 0) ) {
damage *= damage_scale;
float min_damage = (float)Health - (float)HealthMax;
damage = WWMath::Clamp( damage, min_damage, 0 );
//Debug_Say(( "Repairing %f health\n", damage ));
Health = (float)Health - damage;
} else {
damage *= shield_damage_scale;
shield_damage = damage;
damage = 0;
float min_shield_damage = (float)ShieldStrength - (float)ShieldStrengthMax;
shield_damage = WWMath::Clamp( shield_damage, min_shield_damage, 0 );
//Debug_Say(( "Repairing %f shield\n", shield_damage ));
ShieldStrength = (float)ShieldStrength - shield_damage;
}
} else {
if ( smart && Get_Owner() &&
(smart != Get_Owner() ) &&
smart->Is_Teammate( Get_Owner() ) ) {
// This is friendly fire!!
if ( !CombatManager::Is_Friendly_Fire_Permitted() ) {
// damage = 0;
return Health;
}
}
if ( damage != 0 ) {
Mark_Owner_Dirty();
}
// if we have a shield, redirect a fraction of our damage;
// If alternate skin (MCT) ignore sheild damage;
if ( (float)ShieldStrength > 0.0f && alternate_skin == -1 ) {
shield_damage = damage * ArmorWarheadManager::Get_Shield_Absorbsion( ShieldType, offense.Get_Warhead() );
damage -= shield_damage;
shield_damage *= shield_damage_scale;
float shield_damage_to_apply = shield_damage; // how much scaled damage to apply
if ( shield_damage > (float)ShieldStrength ) {
shield_damage = ShieldStrength;
}
ShieldStrength = (float)ShieldStrength - shield_damage;
if ( shield_damage_scale != 0 ) {
shield_damage /= shield_damage_scale; // how much un scaled damage did we apply
shield_damage_to_apply /= shield_damage_scale; // how much scaled damage did we try to apply
}
damage += shield_damage_to_apply - shield_damage;
// Clamp Shield Strength
ShieldStrength = WWMath::Clamp( (float)ShieldStrength, 0, (float)ShieldStrengthMax );
}
// scale the (remaining) damage
// (gth) added alternate_skin feature which is used by buildings when their MCT is being damaged.
damage *= damage_scale;
if ( (float)Health < damage ) {
damage = Health;
}
#ifdef WWDEBUG
//
// TSS090601
//
if (OneShotKills) {
damage = Health;
}
#endif // _WWDEBUG
Health = (float)Health - damage;
//
// Don't allow this object to die (if necessary)
//
if ( CanObjectDie == false ) {
Health = max ( (float)Health, 1.0F );
}
}
if ( COMBAT_STAR != NULL && // dedicated server test
Get_Owner() == COMBAT_STAR ) {
Vector3 pos;
Get_Owner()->Get_Position( &pos );
int hitter_id = 0;
const char * weapon_name = "";
if ( offense.Get_Owner() ) {
hitter_id = offense.Get_Owner()->Get_ID();
if ( offense.Get_Owner()->Get_Weapon() ) {
weapon_name = offense.Get_Owner()->Get_Weapon()->Get_Definition()->Get_Name();
}
}
float points = damage;
float armor = Get_Shield_Strength();
DIAG_LOG(( "DRCV", "%s; %d; %1.2f; %1.2f; %1.2f; %1.2f; %1.2f; %1.2f", weapon_name, hitter_id, points, armor, Health, pos.X, pos.Y, pos.Z ));
}
if (( smart == COMBAT_STAR ) && ( smart != NULL )) {
Vector3 pos;
smart->Get_Position( &pos );
const char * weapon_name = "";
int ammo = 0;
if ( smart->Get_Weapon() ) {
weapon_name = smart->Get_Weapon()->Get_Definition()->Get_Name();
ammo = smart->Get_Weapon()->Get_Total_Rounds();
}
int hittee_id = 0;
Vector3 victim_pos(0,0,0);
const char * team_name = "";
if ( Get_Owner() ) {
hittee_id = Get_Owner()->Get_ID();
Get_Owner()->Get_Position( &victim_pos );
team_name = Player_Type_Name( Get_Owner()->Get_Player_Type() );
}
float points = damage;
float armor = Get_Shield_Strength();
DIAG_LOG(( "DEFC", "%1.2f; %1.2f; %1.2f; %s; %d; %d; %1.2f; %1.2f; %1.2f; %1.2f; %1.2f; %1.2f; %s ", pos.X, pos.Y, pos.Z, weapon_name, ammo, hittee_id, points, armor, Health, victim_pos.X, victim_pos.Y, victim_pos.Z, team_name ));
}
// Clamp Health to Max
Health = WWMath::Clamp( (float)Health, 0, (float)HealthMax );
if ( damage > 0 && (float)Health <= 0 ) {
int victim_id = 0;
Vector3 victim_pos(0,0,0);
if ( Get_Owner() != NULL ) {
victim_id = Get_Owner()->Get_ID();
Get_Owner()->Get_Position( &victim_pos );
}
int killer_id = 0;
Vector3 killer_pos(0,0,0);
const char * weapon_name = "";
if ( smart != NULL ) {
killer_id = smart->Get_ID();
smart->Get_Position( &killer_pos );
if ( smart->Get_Weapon() ) {
weapon_name = smart->Get_Weapon()->Get_Definition()->Get_Name();
}
}
DIAG_LOG(( "OBDE", "%d; %1.2f; %1.2f; %1.2f; %d; %1.2f; %1.2f; %1.2f; %s", victim_id, victim_pos.X, victim_pos.Y, victim_pos.Z, killer_id, killer_pos.X, killer_pos.Y, killer_pos.Z, weapon_name ));
if ( Get_Owner() == COMBAT_STAR ) {
DIAG_LOG(( "STDE", "%1.2f; %1.2f; %1.2f; %d; %1.2f; %1.2f; %1.2f; %s", victim_pos.X, victim_pos.Y, victim_pos.Z, killer_id, killer_pos.X, killer_pos.Y, killer_pos.Z, weapon_name ));
}
}
if (damage > 0 &&
(float) Health <= 0 &&
smart != NULL &&
smart->As_SoldierGameObj() != NULL &&
Get_Owner() != NULL &&
Get_Owner()->As_SoldierGameObj() != NULL) {
CombatManager::On_Soldier_Kill(smart->As_SoldierGameObj(), Get_Owner()->As_SoldierGameObj());
}
// Apply Points for damage/death
if ( smart != NULL && smart->Get_Player_Data() ) {
float points_dir = 1;
// If same team, make the points negative
if ( Get_Owner() ) {
if ( smart->Is_Teammate( Get_Owner() ) ) {
points_dir = -1;
} else if ( !smart->Is_Enemy( Get_Owner() ) ) {
// No points for neutrals
points_dir = 0;
}
}
if (offense.Get_Owner() != Get_Owner()) {
float points = (float)DamagePoints;
if ( is_repair ) { // half points for repair
points *= 0.5f;
}
smart->Get_Player_Data()->Apply_Damage_Points( points_dir * (damage + shield_damage) * points, Get_Owner() );
}
if ( damage > 0 && (float)Health <= 0 ) {
if (offense.Get_Owner() != Get_Owner()) {
smart->Get_Player_Data()->Apply_Death_Points( points_dir * (float)DeathPoints, Get_Owner() );
}
}
}
return Health;
}
bool DefenseObjectClass::Is_Repair( const OffenseObjectClass & offense, float scale )
{
float damage = offense.Get_Damage() * scale;
float damage_scale = ArmorWarheadManager::Get_Damage_Multiplier( Skin, offense.Get_Warhead() );
float shield_damage_scale = ArmorWarheadManager::Get_Damage_Multiplier( ShieldType, offense.Get_Warhead() );
// check for repair on either health of shield
return ( (damage * damage_scale < 0) || (damage * shield_damage_scale < 0) );
}
bool DefenseObjectClass::Would_Damage( const OffenseObjectClass & offense, float scale )
{
float damage = offense.Get_Damage() * scale;
float damage_scale = ArmorWarheadManager::Get_Damage_Multiplier( Skin, offense.Get_Warhead() );
float shield_damage_scale = ArmorWarheadManager::Get_Damage_Multiplier( ShieldType, offense.Get_Warhead() );
SmartGameObj * smart = NULL;
if ( offense.Get_Owner() != NULL ) {
smart = offense.Get_Owner()->As_SmartGameObj();
}
if ( smart && Get_Owner() &&
smart->Is_Teammate( Get_Owner() ) &&
(smart != Get_Owner() ) ) {
// This is friendly fire!!
if ( !CombatManager::Is_Friendly_Fire_Permitted() ) {
return false;
}
}
if ( damage * damage_scale > 0 && (float)Health > 0 ) {
return true;
}
if ( damage * shield_damage_scale > 0 && (float)ShieldStrength > 0 ) {
return true;
}
return false;
}
void DefenseObjectClass::Import(BitStreamClass & packet)
{
bool is_health_zero = packet.Get(is_health_zero);
int health = packet.Get(health, BITPACK_HEALTH);
int shield_strength = packet.Get(shield_strength, BITPACK_SHIELD_STRENGTH);
unsigned int shield_type;
packet.Get(shield_type, BITPACK_SHIELD_TYPE);
ShieldType = shield_type;
Health = (float)health;
ShieldStrength = (float)shield_strength;
if (is_health_zero) {
Health = 0;
} else {
Health = WWMath::Max(Health, 0.01f);
}
//WWASSERT(WWMath::Is_Valid_Float(ShieldStrength));
//WWASSERT(packet.Is_Flushed());
}
void DefenseObjectClass::Export(BitStreamClass & packet)
{
//
// N.B. The funky syntax is to prevent datasafe asserts
//
int health = cMathUtil::Round((double) (float)Health);
int shield_strength = cMathUtil::Round((double) (float)ShieldStrength);
//TSS012202
//packet.Add((bool)(health == 0));
packet.Add((bool)(((float)Health) == 0));
packet.Add(health, BITPACK_HEALTH);
packet.Add(shield_strength, BITPACK_SHIELD_STRENGTH);
packet.Add((unsigned long)ShieldType, BITPACK_SHIELD_TYPE);
//LastSentHealth = Health;
//LastSentSkin = Skin;
//LastSentShieldStrength = ShieldStrength;
//LastSentShieldType = ShieldType;
}
/*
//
// This was only valid with 1 client. We would need dirty tests for every client.
//
bool DefenseObjectClass::Is_Defense_State_Dirty(void)
{
return (
fabs(Health - LastSentHealth) > MISCUTIL_EPSILON ||
Skin != LastSentSkin ||
fabs(ShieldStrength - LastSentShieldStrength) > MISCUTIL_EPSILON ||
ShieldType != LastSentShieldType);
}
*/
/*****************************************************************************************
**
** DefenseObjectDefClass Implementation
**
*****************************************************************************************/
enum
{
DEFENSEOBJECTDEF_CHUNK_VARIABLES = 7311607,
DEFENSEOBJECTDEF_VARIABLE_HEALTH = 0x00,
DEFENSEOBJECTDEF_VARIABLE_HEALTHMAX,
DEFENSEOBJECTDEF_VARIABLE_SKIN,
DEFENSEOBJECTDEF_VARIABLE_SHIELDSTRENGTH,
DEFENSEOBJECTDEF_VARIABLE_SHIELDSTRENGTHMAX,
DEFENSEOBJECTDEF_VARIABLE_SHIELDTYPE,
DEFENSEOBJECTDEF_VARIABLE_DAMAGE_POINTS,
DEFENSEOBJECTDEF_VARIABLE_DEATH_POINTS,
};
DefenseObjectDefClass::DefenseObjectDefClass(void) :
Health( 100.0f ),
HealthMax( 100.0f ),
Skin( 0 ),
ShieldStrength( 0 ),
ShieldStrengthMax( 0 ),
ShieldType( 0 ),
DamagePoints( 0 ),
DeathPoints( 0 )
{
}
DefenseObjectDefClass::~DefenseObjectDefClass(void)
{
}
bool DefenseObjectDefClass::Save(ChunkSaveClass &csave)
{
csave.Begin_Chunk(DEFENSEOBJECTDEF_CHUNK_VARIABLES);
WRITE_SAFE_MICRO_CHUNK(csave,DEFENSEOBJECTDEF_VARIABLE_HEALTH, Health, float);
WRITE_SAFE_MICRO_CHUNK(csave,DEFENSEOBJECTDEF_VARIABLE_HEALTHMAX,HealthMax, float);
int skin_save_id = ArmorWarheadManager::Get_Armor_Save_ID( Skin );
WRITE_MICRO_CHUNK(csave,DEFENSEOBJECTDEF_VARIABLE_SKIN,skin_save_id);
WRITE_SAFE_MICRO_CHUNK(csave,DEFENSEOBJECTDEF_VARIABLE_SHIELDSTRENGTH, ShieldStrength, float);
WRITE_SAFE_MICRO_CHUNK(csave,DEFENSEOBJECTDEF_VARIABLE_SHIELDSTRENGTHMAX, ShieldStrengthMax, float);
int shield_save_id = ArmorWarheadManager::Get_Armor_Save_ID( ShieldType );
WRITE_MICRO_CHUNK(csave,DEFENSEOBJECTDEF_VARIABLE_SHIELDTYPE,shield_save_id);
WRITE_SAFE_MICRO_CHUNK(csave,DEFENSEOBJECTDEF_VARIABLE_DAMAGE_POINTS,DamagePoints, float);
WRITE_SAFE_MICRO_CHUNK(csave,DEFENSEOBJECTDEF_VARIABLE_DEATH_POINTS,DeathPoints, float);
csave.End_Chunk();
return true;
}
bool DefenseObjectDefClass::Load(ChunkLoadClass &cload)
{
int skin_save_id = -2;
int shield_save_id = -2;
while (cload.Open_Chunk()) {
switch(cload.Cur_Chunk_ID())
{
case DEFENSEOBJECTDEF_CHUNK_VARIABLES:
while (cload.Open_Micro_Chunk()) {
switch(cload.Cur_Micro_Chunk_ID()) {
READ_SAFE_MICRO_CHUNK(cload,DEFENSEOBJECTDEF_VARIABLE_HEALTH,Health,float);
READ_SAFE_MICRO_CHUNK(cload,DEFENSEOBJECTDEF_VARIABLE_HEALTHMAX,HealthMax,float);
READ_MICRO_CHUNK(cload,DEFENSEOBJECTDEF_VARIABLE_SKIN,skin_save_id);
READ_SAFE_MICRO_CHUNK(cload,DEFENSEOBJECTDEF_VARIABLE_SHIELDSTRENGTH,ShieldStrength,float);
READ_SAFE_MICRO_CHUNK(cload,DEFENSEOBJECTDEF_VARIABLE_SHIELDSTRENGTHMAX,ShieldStrengthMax,float);
READ_MICRO_CHUNK(cload,DEFENSEOBJECTDEF_VARIABLE_SHIELDTYPE,shield_save_id);
READ_SAFE_MICRO_CHUNK(cload,DEFENSEOBJECTDEF_VARIABLE_DAMAGE_POINTS,DamagePoints,float);
READ_SAFE_MICRO_CHUNK(cload,DEFENSEOBJECTDEF_VARIABLE_DEATH_POINTS,DeathPoints,float);
}
cload.Close_Micro_Chunk();
}
break;
default:
WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
break;
};
cload.Close_Chunk();
}
Skin = ArmorWarheadManager::Find_Armor_Save_ID( skin_save_id );
ShieldType = ArmorWarheadManager::Find_Armor_Save_ID( shield_save_id );
return true;
}
void DefenseObjectClass::Set_Health(float health)
{
float old_health = Health;
Health = WWMath::Clamp(health, 0, HealthMax);
if ( old_health != (float)Health ) {
Mark_Owner_Dirty();
}
}
void DefenseObjectClass::Add_Health(float add_health)
{
Set_Health( WWMath::Clamp((float)Health + add_health, 0, HealthMax) );
}
float DefenseObjectClass::Get_Health(void) const
{
return Health;
}
void DefenseObjectClass::Set_Health_Max(float health)
{
HealthMax = WWMath::Clamp(health, 0, MAX_MAX_HEALTH);
Mark_Owner_Dirty();
}
float DefenseObjectClass::Get_Health_Max(void) const
{
return HealthMax;
}
void DefenseObjectClass::Set_Shield_Strength(float str)
{
float old = ShieldStrength;
ShieldStrength = WWMath::Clamp(str, 0, ShieldStrengthMax);
if ( old != (float)ShieldStrength ) {
Mark_Owner_Dirty();
}
}
void DefenseObjectClass::Add_Shield_Strength(float str)
{
Set_Shield_Strength( WWMath::Clamp((float)ShieldStrength + str, 0, ShieldStrengthMax) );
}
float DefenseObjectClass::Get_Shield_Strength(void) const
{
return ShieldStrength;
}
void DefenseObjectClass::Set_Shield_Strength_Max(float str)
{
ShieldStrengthMax = WWMath::Clamp(str, 0, MAX_MAX_SHIELD_STRENGTH);
Mark_Owner_Dirty();
}
float DefenseObjectClass::Get_Shield_Strength_Max(void) const
{
return ShieldStrengthMax;
}
void DefenseObjectClass::Set_Precision(void)
{
//
// This static function needs to be called after ArmorWarheadManager::Init
// has done its work.
//
cEncoderList::Set_Precision(BITPACK_HEALTH, 0, (int) MAX_MAX_HEALTH);
cEncoderList::Set_Precision(BITPACK_SHIELD_STRENGTH, 0, (int) MAX_MAX_SHIELD_STRENGTH);
cEncoderList::Set_Precision(BITPACK_SHIELD_TYPE, 0,
//ArmorWarheadManager::Get_Num_Armor_Types(), 1);
ArmorWarheadManager::Get_Num_Armor_Types());
}
/*
**
*/
void DefenseObjectClass::Mark_Owner_Dirty( void )
{
if ( Get_Owner() != NULL ) {
Get_Owner()->Set_Object_Dirty_Bit( NetworkObjectClass::BIT_OCCASIONAL, true );
}
}
/*
**
*/
void DefenseObjectClass::Set_Shield_Type( ArmorType type )
{
ShieldType = type;
Mark_Owner_Dirty();
}
//
// Do extra stuff when somebody kills somebody
//
/*
if ( Get_Owner() != NULL &&
Get_Owner()->As_SmartGameObj() &&
Get_Owner()->As_SmartGameObj()->Has_Player() ) {
int victim_id = Get_Owner()->As_SmartGameObj()->Get_Control_Owner();
smart->Get_Player_Data()->On_Kill(victim_id);
}
*/
/*
if ( Get_Owner() != NULL &&
Get_Owner()->As_DamageableGameObj() != NULL &&
Get_Owner()->As_DamageableGameObj()->As_PhysicalGameObj() != NULL &&
Get_Owner()->As_DamageableGameObj()->As_PhysicalGameObj()->As_SoldierGameObj() != NULL &&
Get_Owner()->As_SmartGameObj() != smart) {
int victim_id = Get_Owner()->As_SmartGameObj()->Get_Control_Owner();
int victim_team = Get_Owner()->As_SmartGameObj()->Get_Player_Type();
smart->Get_Player_Data()->On_Kill(victim_id, victim_team);
}
*/
/*
if ( Get_Owner() != NULL &&
Get_Owner()->As_SoldierGameObj != NULL &&
Get_Owner()->As_SmartGameObj() != smart) {
WWASSERT(Get_Owner()->As_SmartGameObj() != NULL);
int victim_id = Get_Owner()->As_SmartGameObj()->Get_Control_Owner();
int victim_team = Get_Owner()->As_SmartGameObj()->Get_Player_Type();
smart->Get_Player_Data()->On_Kill(victim_id, victim_team);
}
*/