358 lines
12 KiB
C++

/*
** Command & Conquer Generals(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/GameType.h"
#include "Common/Snapshot.h"
// FORWARD REFERENCES /////////////////////////////////////////////////////////////////////////////
class Object;
class INI;
//-------------------------------------------------------------------------------------------------
/** Damage types, keep this in sync with TheDamageNames[] */
//-------------------------------------------------------------------------------------------------
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.
// this stays, even if ALLOW_SURRENDER is not defed, since flashbangs still use 'em
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_FLESHY_SNIPER = 31, // like DAMAGE_SNIPER, but (generally) does no damage to vehicles.
// Please note: There is a string array below this enum, and when you change them,
// you need to search on the array names to find all the stuff that generates names
// based on these strings. (eg DamageFX does a strcat to make its array of names so
// change DamageFX.ini and its Default)
// !!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// if you add additional damage types, you will PROBABLY HAVE TO
// ENLARGE A BITMASK IN WEAPONSET.
//
// !!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
DAMAGE_NUM_TYPES // keep this last
};
#ifdef DEFINE_DAMAGE_NAMES
static const char *TheDamageNames[] =
{
"EXPLOSION",
"CRUSH",
"ARMOR_PIERCING",
"SMALL_ARMS",
"GATTLING",
"RADIATION",
"FLAME",
"LASER",
"SNIPER",
"POISON",
"HEALING",
"UNRESISTABLE",
"WATER",
"DEPLOY",
"SURRENDER",
"HACK",
"KILL_PILOT",
"PENALTY",
"FALLING",
"MELEE",
"DISARM",
"HAZARD_CLEANUP",
"PARTICLE_BEAM",
"TOPPLING",
"INFANTRY_MISSILE",
"AURORA_BOMB",
"LAND_MINE",
"JET_MISSILES",
"STEALTHJET_MISSILES",
"MOLOTOV_COCKTAIL",
"COMANCHE_VULCAN",
"FLESHY_SNIPER",
NULL
};
#endif // end DEFINE_DAMAGE_NAMES
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
typedef UnsignedInt DamageTypeFlags;
const DamageTypeFlags DAMAGE_TYPE_FLAGS_ALL = 0xffffffff;
const DamageTypeFlags DAMAGE_TYPE_FLAGS_NONE = 0x00000000;
inline Bool getDamageTypeFlag(DamageTypeFlags flags, DamageType dt)
{
return (flags & (1UL << (dt - 1))) != 0;
}
inline DamageTypeFlags setDamageTypeFlag(DamageTypeFlags flags, DamageType dt)
{
return (flags | (1UL << (dt - 1)));
}
inline DamageTypeFlags clearDamageTypeFlag(DamageTypeFlags flags, DamageType dt)
{
return (flags & ~(1UL << (dt - 1)));
}
//-------------------------------------------------------------------------------------------------
/** 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_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",
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_sourcePlayerMask = 0;
m_damageType = DAMAGE_EXPLOSION;
m_deathType = DEATH_NORMAL;
m_amount = 0;
}
ObjectID m_sourceID; ///< source of the damage
PlayerMaskType m_sourcePlayerMask; ///< Player mask of m_sourceID.
DamageType m_damageType; ///< type of damage
DeathType m_deathType; ///< if this kills us, death type to be used
Real m_amount; ///< # value of how much damage to inflict
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_