377 lines
13 KiB
C
377 lines
13 KiB
C
![]() |
/*
|
||
|
** Command & Conquer Generals Zero Hour(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/>.
|
||
|
*/
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
// //
|
||
|
// (c) 2001-2003 Electronic Arts Inc. //
|
||
|
// //
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
// FILE: Damage.h /////////////////////////////////////////////////////////////////////////////////
|
||
|
// Author: Colin Day, November 2001
|
||
|
// Desc: Damage description
|
||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
#ifndef __DAMAGE_H_
|
||
|
#define __DAMAGE_H_
|
||
|
|
||
|
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
|
||
|
#include "Common/BitFlags.h"
|
||
|
#include "Common/GameType.h"
|
||
|
#include "Common/ObjectStatusTypes.h" // Precompiled header anyway, no detangling possibility
|
||
|
#include "Common/Snapshot.h"
|
||
|
|
||
|
|
||
|
// FORWARD REFERENCES /////////////////////////////////////////////////////////////////////////////
|
||
|
class Object;
|
||
|
class INI;
|
||
|
class ThingTemplate;
|
||
|
|
||
|
//-------------------------------------------------------------------------------------------------
|
||
|
/** Damage types, keep this in sync with DamageTypeFlags::s_bitNameList[] */
|
||
|
//-------------------------------------------------------------------------------------------------
|
||
|
enum DamageType
|
||
|
{
|
||
|
DAMAGE_EXPLOSION = 0,
|
||
|
DAMAGE_CRUSH = 1,
|
||
|
DAMAGE_ARMOR_PIERCING = 2,
|
||
|
DAMAGE_SMALL_ARMS = 3,
|
||
|
DAMAGE_GATTLING = 4,
|
||
|
DAMAGE_RADIATION = 5,
|
||
|
DAMAGE_FLAME = 6,
|
||
|
DAMAGE_LASER = 7,
|
||
|
DAMAGE_SNIPER = 8,
|
||
|
DAMAGE_POISON = 9,
|
||
|
DAMAGE_HEALING = 10,
|
||
|
DAMAGE_UNRESISTABLE = 11, // this is for scripting to cause 'armorproof' damage
|
||
|
DAMAGE_WATER = 12,
|
||
|
DAMAGE_DEPLOY = 13, // for transports to deploy units and order them to all attack.
|
||
|
DAMAGE_SURRENDER = 14, // if something "dies" to surrender damage, they surrender.... duh!
|
||
|
DAMAGE_HACK = 15,
|
||
|
DAMAGE_KILLPILOT = 16, // special snipe attack that kills the pilot and renders a vehicle unmanned.
|
||
|
DAMAGE_PENALTY = 17, // from game penalty (you won't receive radar warnings BTW)
|
||
|
DAMAGE_FALLING = 18,
|
||
|
DAMAGE_MELEE = 19, // Blades, clubs...
|
||
|
DAMAGE_DISARM = 20, // "special" damage type used for disarming mines, bombs, etc (NOT for "disarming" an opponent!)
|
||
|
DAMAGE_HAZARD_CLEANUP = 21, // special damage type for cleaning up hazards like radiation or bio-poison.
|
||
|
DAMAGE_PARTICLE_BEAM = 22, // Incinerates virtually everything (insanely powerful orbital beam)
|
||
|
DAMAGE_TOPPLING = 23, // damage from getting toppled.
|
||
|
DAMAGE_INFANTRY_MISSILE = 24,
|
||
|
DAMAGE_AURORA_BOMB = 25,
|
||
|
DAMAGE_LAND_MINE = 26,
|
||
|
DAMAGE_JET_MISSILES = 27,
|
||
|
DAMAGE_STEALTHJET_MISSILES = 28,
|
||
|
DAMAGE_MOLOTOV_COCKTAIL = 29,
|
||
|
DAMAGE_COMANCHE_VULCAN = 30,
|
||
|
DAMAGE_SUBDUAL_MISSILE = 31, ///< Damage that does not kill you, but produces some special effect based on your Body Module. Seperate HP from normal damage.
|
||
|
DAMAGE_SUBDUAL_VEHICLE = 32,
|
||
|
DAMAGE_SUBDUAL_BUILDING = 33,
|
||
|
DAMAGE_SUBDUAL_UNRESISTABLE = 34,
|
||
|
DAMAGE_MICROWAVE = 35, ///< Radiation that only affects infantry
|
||
|
DAMAGE_KILL_GARRISONED = 36, ///< Kills Passengers up to the number specified in Damage
|
||
|
DAMAGE_STATUS = 37, ///< Damage that gives a status condition, not that does hitpoint damage
|
||
|
|
||
|
// Please note: There is a string array DamageTypeFlags::s_bitNameList[]
|
||
|
|
||
|
DAMAGE_NUM_TYPES // keep this last
|
||
|
};
|
||
|
|
||
|
//-------------------------------------------------------------------------------------------------
|
||
|
//-------------------------------------------------------------------------------------------------
|
||
|
|
||
|
typedef BitFlags<DAMAGE_NUM_TYPES> DamageTypeFlags;
|
||
|
|
||
|
inline Bool getDamageTypeFlag(DamageTypeFlags flags, DamageType dt)
|
||
|
{
|
||
|
return flags.test(dt);
|
||
|
}
|
||
|
|
||
|
inline DamageTypeFlags setDamageTypeFlag(DamageTypeFlags flags, DamageType dt)
|
||
|
{
|
||
|
flags.set(dt, TRUE);
|
||
|
return flags;
|
||
|
}
|
||
|
|
||
|
inline DamageTypeFlags clearDamageTypeFlag(DamageTypeFlags flags, DamageType dt)
|
||
|
{
|
||
|
flags.set(dt, FALSE);
|
||
|
return flags;
|
||
|
}
|
||
|
|
||
|
// Instead of checking against a single damage type, gather the question here so we can have more than one
|
||
|
inline Bool IsSubdualDamage( DamageType type )
|
||
|
{
|
||
|
switch( type )
|
||
|
{
|
||
|
case DAMAGE_SUBDUAL_MISSILE:
|
||
|
case DAMAGE_SUBDUAL_VEHICLE:
|
||
|
case DAMAGE_SUBDUAL_BUILDING:
|
||
|
case DAMAGE_SUBDUAL_UNRESISTABLE:
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
/// Does this type of damage go to internalChangeHealth?
|
||
|
inline Bool IsHealthDamagingDamage( DamageType type )
|
||
|
{
|
||
|
// The need for this function brought to you by "Have the guy with no game experience write the weapon code" Foundation.
|
||
|
// Health Damage should be one type of WeaponEffect. Thinking "Weapons can only do damage" is why AoE is boring.
|
||
|
switch( type )
|
||
|
{
|
||
|
case DAMAGE_STATUS:
|
||
|
case DAMAGE_SUBDUAL_MISSILE:
|
||
|
case DAMAGE_SUBDUAL_VEHICLE:
|
||
|
case DAMAGE_SUBDUAL_BUILDING:
|
||
|
case DAMAGE_SUBDUAL_UNRESISTABLE:
|
||
|
case DAMAGE_KILLPILOT:
|
||
|
case DAMAGE_KILL_GARRISONED:
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
inline void SET_ALL_DAMAGE_TYPE_BITS(DamageTypeFlags& m)
|
||
|
{
|
||
|
m.clear();
|
||
|
m.flip();
|
||
|
}
|
||
|
|
||
|
extern DamageTypeFlags DAMAGE_TYPE_FLAGS_NONE;
|
||
|
extern DamageTypeFlags DAMAGE_TYPE_FLAGS_ALL;
|
||
|
void initDamageTypeFlags();
|
||
|
|
||
|
|
||
|
//-------------------------------------------------------------------------------------------------
|
||
|
/** Death types, keep this in sync with TheDeathNames[] */
|
||
|
//-------------------------------------------------------------------------------------------------
|
||
|
enum DeathType
|
||
|
{
|
||
|
// note that these DELIBERATELY have (slightly) different names from the damage names,
|
||
|
// since there isn't necessarily a one-to-one correspondence. e.g., DEATH_BURNED
|
||
|
// can come from DAMAGE_FLAME but also from DAMAGE_PARTICLE_BEAM.
|
||
|
DEATH_NORMAL = 0,
|
||
|
DEATH_NONE = 1, ///< this is a "special case" that can't normally cause death
|
||
|
DEATH_CRUSHED = 2,
|
||
|
DEATH_BURNED = 3,
|
||
|
DEATH_EXPLODED = 4,
|
||
|
DEATH_POISONED = 5,
|
||
|
DEATH_TOPPLED = 6,
|
||
|
DEATH_FLOODED = 7,
|
||
|
DEATH_SUICIDED = 8,
|
||
|
DEATH_LASERED = 9,
|
||
|
DEATH_DETONATED = 10, /**< this is the "death" that occurs when a missile/warhead/etc detonates normally,
|
||
|
as opposed to being shot down, etc */
|
||
|
DEATH_SPLATTED = 11, /**< the death that results from DAMAGE_FALLING */
|
||
|
DEATH_POISONED_BETA = 12,
|
||
|
|
||
|
// these are the "extra" types for yet-to-be-defined stuff. Don't bother renaming or adding
|
||
|
// or removing these; they are reserved for modders :-)
|
||
|
DEATH_EXTRA_2 = 13,
|
||
|
DEATH_EXTRA_3 = 14,
|
||
|
DEATH_EXTRA_4 = 15,
|
||
|
DEATH_EXTRA_5 = 16,
|
||
|
DEATH_EXTRA_6 = 17,
|
||
|
DEATH_EXTRA_7 = 18,
|
||
|
DEATH_EXTRA_8 = 19,
|
||
|
DEATH_POISONED_GAMMA = 20,
|
||
|
|
||
|
DEATH_NUM_TYPES // keep this last
|
||
|
};
|
||
|
|
||
|
#ifdef DEFINE_DEATH_NAMES
|
||
|
static const char *TheDeathNames[] =
|
||
|
{
|
||
|
"NORMAL",
|
||
|
"NONE",
|
||
|
"CRUSHED",
|
||
|
"BURNED",
|
||
|
"EXPLODED",
|
||
|
"POISONED",
|
||
|
"TOPPLED",
|
||
|
"FLOODED",
|
||
|
"SUICIDED",
|
||
|
"LASERED",
|
||
|
"DETONATED",
|
||
|
"SPLATTED",
|
||
|
"POISONED_BETA",
|
||
|
"EXTRA_2",
|
||
|
"EXTRA_3",
|
||
|
"EXTRA_4",
|
||
|
"EXTRA_5",
|
||
|
"EXTRA_6",
|
||
|
"EXTRA_7",
|
||
|
"EXTRA_8",
|
||
|
"POISONED_GAMMA",
|
||
|
|
||
|
NULL
|
||
|
};
|
||
|
#endif // end DEFINE_DEATH_NAMES
|
||
|
|
||
|
|
||
|
//-------------------------------------------------------------------------------------------------
|
||
|
//-------------------------------------------------------------------------------------------------
|
||
|
|
||
|
typedef UnsignedInt DeathTypeFlags;
|
||
|
|
||
|
const DeathTypeFlags DEATH_TYPE_FLAGS_ALL = 0xffffffff;
|
||
|
const DeathTypeFlags DEATH_TYPE_FLAGS_NONE = 0x00000000;
|
||
|
|
||
|
inline Bool getDeathTypeFlag(DeathTypeFlags flags, DeathType dt)
|
||
|
{
|
||
|
return (flags & (1UL << (dt - 1))) != 0;
|
||
|
}
|
||
|
|
||
|
inline DeathTypeFlags setDeathTypeFlag(DeathTypeFlags flags, DeathType dt)
|
||
|
{
|
||
|
return (flags | (1UL << (dt - 1)));
|
||
|
}
|
||
|
|
||
|
inline DeathTypeFlags clearDeathTypeFlag(DeathTypeFlags flags, DeathType dt)
|
||
|
{
|
||
|
return (flags & ~(1UL << (dt - 1)));
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------------------------------
|
||
|
/** Damage info inputs */
|
||
|
//-------------------------------------------------------------------------------------------------
|
||
|
class DamageInfoInput : public Snapshot
|
||
|
{
|
||
|
|
||
|
public:
|
||
|
|
||
|
DamageInfoInput( void )
|
||
|
{
|
||
|
m_sourceID = INVALID_ID;
|
||
|
m_sourceTemplate = NULL;
|
||
|
m_sourcePlayerMask = 0;
|
||
|
m_damageType = DAMAGE_EXPLOSION;
|
||
|
m_damageStatusType = OBJECT_STATUS_NONE;
|
||
|
m_damageFXOverride = DAMAGE_UNRESISTABLE;
|
||
|
m_deathType = DEATH_NORMAL;
|
||
|
m_amount = 0;
|
||
|
m_kill = FALSE;
|
||
|
|
||
|
m_shockWaveVector.zero();
|
||
|
m_shockWaveAmount = 0.0f;
|
||
|
m_shockWaveRadius = 0.0f;
|
||
|
m_shockWaveTaperOff = 0.0f;
|
||
|
}
|
||
|
|
||
|
ObjectID m_sourceID; ///< source of the damage
|
||
|
const ThingTemplate *m_sourceTemplate; ///< source of the damage (the template).
|
||
|
PlayerMaskType m_sourcePlayerMask; ///< Player mask of m_sourceID.
|
||
|
DamageType m_damageType; ///< type of damage
|
||
|
ObjectStatusTypes m_damageStatusType; ///< If status damage, what type
|
||
|
DamageType m_damageFXOverride; ///< If not marked as the default of Unresistable, the damage type to use in doDamageFX instead of the real damamge type
|
||
|
DeathType m_deathType; ///< if this kills us, death type to be used
|
||
|
Real m_amount; ///< # value of how much damage to inflict
|
||
|
Bool m_kill; ///< will always cause object to die regardless of damage.
|
||
|
|
||
|
// These are used for damage causing shockwave, forcing units affected to be pushed around
|
||
|
Coord3D m_shockWaveVector; ///< This represents the incoming damage vector
|
||
|
Real m_shockWaveAmount; ///< This represents the amount of shockwave created by the damage. 0 = no shockwave, 1.0 = shockwave equal to damage.
|
||
|
Real m_shockWaveRadius; ///< This represents the effect radius of the shockwave.
|
||
|
Real m_shockWaveTaperOff; ///< This represents the taper off effect of the shockwave at the tip of the radius. 0.0 means shockwave is 0% at the radius edge.
|
||
|
|
||
|
|
||
|
protected:
|
||
|
|
||
|
// snapshot methods
|
||
|
virtual void crc( Xfer *xfer ) { }
|
||
|
virtual void xfer( Xfer *xfer );
|
||
|
virtual void loadPostProcess( void ) { }
|
||
|
|
||
|
};
|
||
|
|
||
|
const Real HUGE_DAMAGE_AMOUNT = 999999.0f;
|
||
|
|
||
|
//-------------------------------------------------------------------------------------------------
|
||
|
/** Damage into outputs */
|
||
|
//-------------------------------------------------------------------------------------------------
|
||
|
class DamageInfoOutput : public Snapshot
|
||
|
{
|
||
|
|
||
|
public:
|
||
|
|
||
|
DamageInfoOutput( void )
|
||
|
{
|
||
|
m_actualDamageDealt = 0;
|
||
|
m_actualDamageClipped = 0;
|
||
|
m_noEffect = false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
m_actualDamageDealt is the damage we tried to apply to object (after multipliers and such).
|
||
|
m_actualDamageClipped is the value of m_actualDamageDealt, but clipped to the max health remaining of the obj.
|
||
|
example:
|
||
|
a mammoth tank fires a round at a small tank, attempting 100 damage.
|
||
|
the small tank has a damage multiplier of 50%, meaning that only 50 damage is applied.
|
||
|
furthermore, the small tank has only 30 health remaining.
|
||
|
so: m_actualDamageDealt = 50, m_actualDamageClipped = 30.
|
||
|
|
||
|
this distinction is useful, since visual fx really wants to do the fx for "50 damage",
|
||
|
even though it was more than necessary to kill this object; game logic, on the other hand,
|
||
|
may want to know the "clipped" damage for ai purposes.
|
||
|
*/
|
||
|
Real m_actualDamageDealt;
|
||
|
Real m_actualDamageClipped; ///< (see comment for m_actualDamageDealt)
|
||
|
Bool m_noEffect; ///< if true, no damage was done at all (generally due to being InactiveBody)
|
||
|
|
||
|
protected:
|
||
|
|
||
|
// snapshot methods
|
||
|
virtual void crc( Xfer *xfer ) { }
|
||
|
virtual void xfer( Xfer *xfer );
|
||
|
virtual void loadPostProcess( void ) { }
|
||
|
|
||
|
};
|
||
|
|
||
|
//-------------------------------------------------------------------------------------------------
|
||
|
/** DamageInfo is a descriptor of damage we're trying to inflict. The structure
|
||
|
* is divided up into two parts, inputs and outputs.
|
||
|
*
|
||
|
* INPUTS: You must provide valid values for these fields in order for damage
|
||
|
* calculation to correctly take place
|
||
|
* OUTPUT: Upon returning from damage issuing functions, the output fields
|
||
|
* will be filled with the results of the damage occurrence
|
||
|
*/
|
||
|
//-------------------------------------------------------------------------------------------------
|
||
|
class DamageInfo : public Snapshot
|
||
|
{
|
||
|
|
||
|
public:
|
||
|
|
||
|
DamageInfoInput in; ///< inputs for the damage info
|
||
|
DamageInfoOutput out; ///< results for the damage occurrence
|
||
|
|
||
|
protected:
|
||
|
|
||
|
virtual void crc( Xfer *xfer ) { }
|
||
|
virtual void xfer( Xfer *xfer );
|
||
|
virtual void loadPostProcess( void ){ }
|
||
|
|
||
|
};
|
||
|
|
||
|
#endif // __DAMAGE_H_
|
||
|
|