1412 lines
42 KiB
C++
1412 lines
42 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/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 <string.h>
|
||
|
#include <stdio.h>
|
||
|
|
||
|
#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<StringClass> ArmorNames;
|
||
|
DynamicVectorClass<StringClass> WarheadNames;
|
||
|
DynamicVectorClass<int> ArmorSaveIDs;
|
||
|
DynamicVectorClass<int> WarheadSaveIDs;
|
||
|
DynamicVectorClass<bool> SoftArmorTable;
|
||
|
|
||
|
// For each Warhead
|
||
|
DynamicVectorClass<int> SpecialDamageTypes;
|
||
|
DynamicVectorClass<float> SpecialDamageProbability;
|
||
|
DynamicVectorClass<float> 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<ArmorType> 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<ArmorWarheadManager::NUM_SPECIAL_DAMAGE_TYPES; i++) {
|
||
|
ImperviousSkins[i].Delete_All();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int ArmorWarheadManager::Get_Num_Armor_Types( void )
|
||
|
{
|
||
|
return ArmorNames.Count();
|
||
|
}
|
||
|
|
||
|
int ArmorWarheadManager::Get_Num_Warhead_Types( void )
|
||
|
{
|
||
|
return WarheadNames.Count();
|
||
|
}
|
||
|
|
||
|
ArmorType ArmorWarheadManager::Get_Armor_Type( const char *name )
|
||
|
{
|
||
|
for ( int index = 0; index < ArmorNames.Count(); index++ ) {
|
||
|
if ( !stricmp( ArmorNames[index], name ) ) {
|
||
|
return index;
|
||
|
}
|
||
|
}
|
||
|
Debug_Say(( "Armor %s Not Found\n", name ));
|
||
|
// WWASSERT( 0 ); //Armor type not found!
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
WarheadType ArmorWarheadManager::Get_Warhead_Type( const char *name )
|
||
|
{
|
||
|
for ( int index = 0; index < WarheadNames.Count(); index++ ) {
|
||
|
if ( !stricmp( WarheadNames[index], name ) ) {
|
||
|
return index;
|
||
|
}
|
||
|
}
|
||
|
Debug_Say(( "Warhead %s Not Found\n", name ));
|
||
|
// WWASSERT( 0 ); //Warhead type not found!
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
const char * ArmorWarheadManager::Get_Armor_Name( ArmorType type )
|
||
|
{
|
||
|
WWASSERT( (int)type >= 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);
|
||
|
}
|
||
|
*/
|
||
|
|