diff options
Diffstat (limited to 'game/shared/takedamageinfo.cpp')
| -rw-r--r-- | game/shared/takedamageinfo.cpp | 511 |
1 files changed, 511 insertions, 0 deletions
diff --git a/game/shared/takedamageinfo.cpp b/game/shared/takedamageinfo.cpp new file mode 100644 index 0000000..c15b801 --- /dev/null +++ b/game/shared/takedamageinfo.cpp @@ -0,0 +1,511 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "takedamageinfo.h" +#include "ammodef.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +ConVar phys_pushscale( "phys_pushscale", "1", FCVAR_REPLICATED ); + +BEGIN_SIMPLE_DATADESC( CTakeDamageInfo ) + DEFINE_FIELD( m_vecDamageForce, FIELD_VECTOR ), + DEFINE_FIELD( m_vecDamagePosition, FIELD_POSITION_VECTOR), + DEFINE_FIELD( m_vecReportedPosition, FIELD_POSITION_VECTOR), + DEFINE_FIELD( m_hInflictor, FIELD_EHANDLE), + DEFINE_FIELD( m_hAttacker, FIELD_EHANDLE), + DEFINE_FIELD( m_hWeapon, FIELD_EHANDLE), + DEFINE_FIELD( m_flDamage, FIELD_FLOAT), + DEFINE_FIELD( m_flMaxDamage, FIELD_FLOAT), + DEFINE_FIELD( m_flBaseDamage, FIELD_FLOAT ), + DEFINE_FIELD( m_bitsDamageType, FIELD_INTEGER), + DEFINE_FIELD( m_iDamageCustom, FIELD_INTEGER), + DEFINE_FIELD( m_iDamageStats, FIELD_INTEGER), + DEFINE_FIELD( m_iAmmoType, FIELD_INTEGER), + DEFINE_FIELD( m_iDamagedOtherPlayers, FIELD_INTEGER), +END_DATADESC() + +void CTakeDamageInfo::Init( CBaseEntity *pInflictor, CBaseEntity *pAttacker, CBaseEntity *pWeapon, const Vector &damageForce, const Vector &damagePosition, const Vector &reportedPosition, float flDamage, int bitsDamageType, int iCustomDamage ) +{ + m_hInflictor = pInflictor; + if ( pAttacker ) + { + m_hAttacker = pAttacker; + } + else + { + m_hAttacker = pInflictor; + } + + m_hWeapon = pWeapon; + + m_flDamage = flDamage; + + m_flBaseDamage = BASEDAMAGE_NOT_SPECIFIED; + + m_bitsDamageType = bitsDamageType; + m_iDamageCustom = iCustomDamage; + + m_flMaxDamage = flDamage; + m_vecDamageForce = damageForce; + m_vecDamagePosition = damagePosition; + m_vecReportedPosition = reportedPosition; + m_iAmmoType = -1; + m_iDamagedOtherPlayers = 0; + m_iPlayerPenetrationCount = 0; + m_flDamageBonus = 0.f; + m_bForceFriendlyFire = false; + m_flDamageForForce = 0.f; + m_eCritType = CRIT_NONE; +} + +CTakeDamageInfo::CTakeDamageInfo() +{ + Init( NULL, NULL, NULL, vec3_origin, vec3_origin, vec3_origin, 0, 0, 0 ); +} + +CTakeDamageInfo::CTakeDamageInfo( CBaseEntity *pInflictor, CBaseEntity *pAttacker, float flDamage, int bitsDamageType, int iKillType ) +{ + Set( pInflictor, pAttacker, flDamage, bitsDamageType, iKillType ); +} + +CTakeDamageInfo::CTakeDamageInfo( CBaseEntity *pInflictor, CBaseEntity *pAttacker, CBaseEntity *pWeapon, float flDamage, int bitsDamageType, int iKillType ) +{ + Set( pInflictor, pAttacker, pWeapon, flDamage, bitsDamageType, iKillType ); +} + +CTakeDamageInfo::CTakeDamageInfo( CBaseEntity *pInflictor, CBaseEntity *pAttacker, const Vector &damageForce, const Vector &damagePosition, float flDamage, int bitsDamageType, int iKillType, Vector *reportedPosition ) +{ + Set( pInflictor, pAttacker, damageForce, damagePosition, flDamage, bitsDamageType, iKillType, reportedPosition ); +} + +CTakeDamageInfo::CTakeDamageInfo( CBaseEntity *pInflictor, CBaseEntity *pAttacker, CBaseEntity *pWeapon, const Vector &damageForce, const Vector &damagePosition, float flDamage, int bitsDamageType, int iKillType, Vector *reportedPosition ) +{ + Set( pInflictor, pAttacker, pWeapon, damageForce, damagePosition, flDamage, bitsDamageType, iKillType, reportedPosition ); +} + +void CTakeDamageInfo::Set( CBaseEntity *pInflictor, CBaseEntity *pAttacker, float flDamage, int bitsDamageType, int iKillType ) +{ + Init( pInflictor, pAttacker, NULL, vec3_origin, vec3_origin, vec3_origin, flDamage, bitsDamageType, iKillType ); +} + +void CTakeDamageInfo::Set( CBaseEntity *pInflictor, CBaseEntity *pAttacker, CBaseEntity *pWeapon, float flDamage, int bitsDamageType, int iKillType ) +{ + Init( pInflictor, pAttacker, pWeapon, vec3_origin, vec3_origin, vec3_origin, flDamage, bitsDamageType, iKillType ); +} + +void CTakeDamageInfo::Set( CBaseEntity *pInflictor, CBaseEntity *pAttacker, const Vector &damageForce, const Vector &damagePosition, float flDamage, int bitsDamageType, int iKillType, Vector *reportedPosition ) +{ + Set( pInflictor, pAttacker, NULL, damageForce, damagePosition, flDamage, bitsDamageType, iKillType, reportedPosition ); +} + +void CTakeDamageInfo::Set( CBaseEntity *pInflictor, CBaseEntity *pAttacker, CBaseEntity *pWeapon, const Vector &damageForce, const Vector &damagePosition, float flDamage, int bitsDamageType, int iKillType, Vector *reportedPosition ) +{ + Vector vecReported = vec3_origin; + if ( reportedPosition ) + { + vecReported = *reportedPosition; + } + Init( pInflictor, pAttacker, pWeapon, damageForce, damagePosition, vecReported, flDamage, bitsDamageType, iKillType ); +} + +//----------------------------------------------------------------------------- +// Squirrel the damage value away as BaseDamage, which will later be used to +// calculate damage force. +//----------------------------------------------------------------------------- +void CTakeDamageInfo::AdjustPlayerDamageInflictedForSkillLevel() +{ +#ifndef CLIENT_DLL + CopyDamageToBaseDamage(); + SetDamage( g_pGameRules->AdjustPlayerDamageInflicted(GetDamage()) ); +#endif +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CTakeDamageInfo::AdjustPlayerDamageTakenForSkillLevel() +{ +#ifndef CLIENT_DLL + CopyDamageToBaseDamage(); + g_pGameRules->AdjustPlayerDamageTaken(this); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: get the name of the ammo that caused damage +// Note: returns the ammo name, or the classname of the object, or the model name in the case of physgun ammo. +//----------------------------------------------------------------------------- +const char *CTakeDamageInfo::GetAmmoName() const +{ + const char *pszAmmoType; + + if ( m_iAmmoType >= 0 ) + { + pszAmmoType = GetAmmoDef()->GetAmmoOfIndex( m_iAmmoType )->pName; + } + // no ammoType, so get the ammo name from the inflictor + else if ( m_hInflictor != NULL ) + { + pszAmmoType = m_hInflictor->GetClassname(); + + // check for physgun ammo. unfortunate that this is in game_shared. + if ( Q_strcmp( pszAmmoType, "prop_physics" ) == 0 ) + { + pszAmmoType = STRING( m_hInflictor->GetModelName() ); + } + } + else + { + pszAmmoType = "Unknown"; + } + + return pszAmmoType; +} + +// -------------------------------------------------------------------------------------------------- // +// MultiDamage +// Collects multiple small damages into a single damage +// -------------------------------------------------------------------------------------------------- // +BEGIN_SIMPLE_DATADESC_( CMultiDamage, CTakeDamageInfo ) + DEFINE_FIELD( m_hTarget, FIELD_EHANDLE), +END_DATADESC() + +CMultiDamage g_MultiDamage; + +CMultiDamage::CMultiDamage() +{ + m_hTarget = NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMultiDamage::Init( CBaseEntity *pTarget, CBaseEntity *pInflictor, CBaseEntity *pAttacker, CBaseEntity *pWeapon, const Vector &damageForce, const Vector &damagePosition, const Vector &reportedPosition, float flDamage, int bitsDamageType, int iKillType ) +{ + m_hTarget = pTarget; + BaseClass::Init( pInflictor, pAttacker, pWeapon, damageForce, damagePosition, reportedPosition, flDamage, bitsDamageType, iKillType ); +} + +//----------------------------------------------------------------------------- +// Purpose: Resets the global multi damage accumulator +//----------------------------------------------------------------------------- +void ClearMultiDamage( void ) +{ + g_MultiDamage.Init( NULL, NULL, NULL, NULL, vec3_origin, vec3_origin, vec3_origin, 0, 0, 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: inflicts contents of global multi damage register on gMultiDamage.pEntity +//----------------------------------------------------------------------------- +void ApplyMultiDamage( void ) +{ + Vector vecSpot1;//where blood comes from + Vector vecDir;//direction blood should go + trace_t tr; + + if ( !g_MultiDamage.GetTarget() ) + return; + +#ifndef CLIENT_DLL + const CBaseEntity *host = te->GetSuppressHost(); + te->SetSuppressHost( NULL ); + + g_MultiDamage.GetTarget()->TakeDamage( g_MultiDamage ); + + te->SetSuppressHost( (CBaseEntity*)host ); +#endif + + // Damage is done, clear it out + ClearMultiDamage(); +} + +//----------------------------------------------------------------------------- +// Purpose: Add damage to the existing multidamage, and apply if it won't fit +//----------------------------------------------------------------------------- +void AddMultiDamage( const CTakeDamageInfo &info, CBaseEntity *pEntity ) +{ + if ( !pEntity ) + return; + + if ( pEntity != g_MultiDamage.GetTarget() ) + { + ApplyMultiDamage(); + g_MultiDamage.Init( pEntity, info.GetInflictor(), info.GetAttacker(), info.GetWeapon(), vec3_origin, vec3_origin, vec3_origin, 0.0, info.GetDamageType(), info.GetDamageCustom() ); + } + + g_MultiDamage.AddDamageType( info.GetDamageType() ); + g_MultiDamage.SetDamage( g_MultiDamage.GetDamage() + info.GetDamage() ); + g_MultiDamage.SetDamageForce( g_MultiDamage.GetDamageForce() + info.GetDamageForce() ); + g_MultiDamage.SetDamagePosition( info.GetDamagePosition() ); + g_MultiDamage.SetReportedPosition( info.GetReportedPosition() ); + g_MultiDamage.SetMaxDamage( MAX( g_MultiDamage.GetMaxDamage(), info.GetDamage() ) ); + g_MultiDamage.SetAmmoType( info.GetAmmoType() ); + g_MultiDamage.SetCritType( info.GetCritType() ); + + if ( g_MultiDamage.GetPlayerPenetrationCount() == 0 ) + { + g_MultiDamage.SetPlayerPenetrationCount( info.GetPlayerPenetrationCount() ); + } + + bool bHasPhysicsForceDamage = !g_pGameRules->Damage_NoPhysicsForce( info.GetDamageType() ); + if ( bHasPhysicsForceDamage && g_MultiDamage.GetDamageType() != DMG_GENERIC ) + { + // If you hit this assert, you've called TakeDamage with a damage type that requires a physics damage + // force & position without specifying one or both of them. Decide whether your damage that's causing + // this is something you believe should impart physics force on the receiver. If it is, you need to + // setup the damage force & position inside the CTakeDamageInfo (Utility functions for this are in + // takedamageinfo.cpp. If you think the damage shouldn't cause force (unlikely!) then you can set the + // damage type to DMG_GENERIC, or | DMG_CRUSH if you need to preserve the damage type for purposes of HUD display. + if ( g_MultiDamage.GetDamageForce() == vec3_origin || g_MultiDamage.GetDamagePosition() == vec3_origin ) + { + static int warningCount = 0; + if ( ++warningCount < 10 ) + { + if ( g_MultiDamage.GetDamageForce() == vec3_origin ) + { + Warning( "AddMultiDamage: g_MultiDamage.GetDamageForce() == vec3_origin\n" ); + } + + if ( g_MultiDamage.GetDamagePosition() == vec3_origin) + { + Warning( "AddMultiDamage: g_MultiDamage.GetDamagePosition() == vec3_origin\n" ); + } + } + } + } +} + + +//============================================================================================================ +// Utility functions for physics damage force calculation +//============================================================================================================ +//----------------------------------------------------------------------------- +// Purpose: Returns an impulse scale required to push an object. +// Input : flTargetMass - Mass of the target object, in kg +// flDesiredSpeed - Desired speed of the target, in inches/sec. +//----------------------------------------------------------------------------- +float ImpulseScale( float flTargetMass, float flDesiredSpeed ) +{ + return (flTargetMass * flDesiredSpeed); +} + +//----------------------------------------------------------------------------- +// Purpose: Fill out a takedamageinfo with a damage force for an explosive +//----------------------------------------------------------------------------- +void CalculateExplosiveDamageForce( CTakeDamageInfo *info, const Vector &vecDir, const Vector &vecForceOrigin, float flScale ) +{ + info->SetDamagePosition( vecForceOrigin ); + + float flClampForce = ImpulseScale( 75, 400 ); + + // Calculate an impulse large enough to push a 75kg man 4 in/sec per point of damage + float flForceScale = info->GetBaseDamage() * ImpulseScale( 75, 4 ); + + if( flForceScale > flClampForce ) + flForceScale = flClampForce; + + // Fudge blast forces a little bit, so that each + // victim gets a slightly different trajectory. + // This simulates features that usually vary from + // person-to-person variables such as bodyweight, + // which are all indentical for characters using the same model. + flForceScale *= random->RandomFloat( 0.85, 1.15 ); + + // Calculate the vector and stuff it into the takedamageinfo + Vector vecForce = vecDir; + VectorNormalize( vecForce ); + vecForce *= flForceScale; + vecForce *= phys_pushscale.GetFloat(); + vecForce *= flScale; + info->SetDamageForce( vecForce ); +} + +//----------------------------------------------------------------------------- +// Purpose: Fill out a takedamageinfo with a damage force for a bullet impact +//----------------------------------------------------------------------------- +void CalculateBulletDamageForce( CTakeDamageInfo *info, int iBulletType, const Vector &vecBulletDir, const Vector &vecForceOrigin, float flScale ) +{ + info->SetDamagePosition( vecForceOrigin ); + Vector vecForce = vecBulletDir; + VectorNormalize( vecForce ); + vecForce *= GetAmmoDef()->DamageForce( iBulletType ); + vecForce *= phys_pushscale.GetFloat(); + vecForce *= flScale; + info->SetDamageForce( vecForce ); + Assert(vecForce!=vec3_origin); +} + +//----------------------------------------------------------------------------- +// Purpose: Fill out a takedamageinfo with a damage force for a melee impact +//----------------------------------------------------------------------------- +void CalculateMeleeDamageForce( CTakeDamageInfo *info, const Vector &vecMeleeDir, const Vector &vecForceOrigin, float flScale ) +{ + info->SetDamagePosition( vecForceOrigin ); + + // Calculate an impulse large enough to push a 75kg man 4 in/sec per point of damage + float flForceScale = info->GetBaseDamage() * ImpulseScale( 75, 4 ); + Vector vecForce = vecMeleeDir; + VectorNormalize( vecForce ); + vecForce *= flForceScale; + vecForce *= phys_pushscale.GetFloat(); + vecForce *= flScale; + info->SetDamageForce( vecForce ); +} + +//----------------------------------------------------------------------------- +// Purpose: Try and guess the physics force to use. +// This shouldn't be used for any damage where the damage force is unknown. +// i.e. only use it for mapmaker specified damages. +//----------------------------------------------------------------------------- +void GuessDamageForce( CTakeDamageInfo *info, const Vector &vecForceDir, const Vector &vecForceOrigin, float flScale ) +{ + if ( info->GetDamageType() & DMG_BULLET ) + { + CalculateBulletDamageForce( info, GetAmmoDef()->Index("SMG1"), vecForceDir, vecForceOrigin, flScale ); + } + else if ( info->GetDamageType() & DMG_BLAST ) + { + CalculateExplosiveDamageForce( info, vecForceDir, vecForceOrigin, flScale ); + } + else + { + CalculateMeleeDamageForce( info, vecForceDir, vecForceOrigin, flScale ); + } +} + + +// Debug functions for printing out damage types + +// This table maps the DMG_* defines to their strings such that +// for DMG_XXX = i << x then table[i] = string for DMG_XXX + +static const char * const s_DamageTypeToStrTable[] = +{ + "GENERIC", + "CRUSH", + "BULLET", + "SLASH", + "BURN", + "VEHICLE", + "FALL", + "BLAST", + "CLUB", + "SHOCK", + "SONIC", + "ENERGYBEAM", + "PREVENT_PHYSICS_FORCE", + "NEVERGIB", + "ALWAYSGIB", + "DROWN", + "PARALYZE", + "NERVEGAS", + "POISON", + "RADIATION", + "DROWNRECOVER", + "ACID", + "SLOWBURN", + "REMOVENORAGDOLL", + "PHYSGUN", + "PLASMA", + "AIRBOAT", + "DISSOLVE", + "BLAST_SURFACE", + "DIRECT", + "BUCKSHOT" +}; +#define DAMAGE_TYPE_STR_TABLE_ENTRIES 31 // number of entries in table above + +void CTakeDamageInfo::DebugGetDamageTypeString(unsigned int damageType, char *outbuf, int outbuflength ) +{ + Assert(outbuflength > 0); + + // we need to use snprintf to actually copy out the strings here because that's the only function that returns + // how much text was output + if ( damageType == 0 ) + { + int charsWrit = Q_snprintf(outbuf, outbuflength, "%s", s_DamageTypeToStrTable[0]); + + outbuflength -= charsWrit; + outbuf += charsWrit; // advance the output pointer (now it sits on the null terminator) + } + + // loop through the other entries in the table + for (int i = 0; + outbuflength > 0 && i < (DAMAGE_TYPE_STR_TABLE_ENTRIES - 1); + ++i ) + { + if ( damageType & (1 << i) ) + { + // this bit was set. Print the corresponding entry from the table + // (the index is +1 because entry 1 in the table corresponds to 1 << 0) + int charsWrit = Q_snprintf(outbuf, outbuflength, "%s ", s_DamageTypeToStrTable[i + 1]); + + outbuflength -= charsWrit; // reduce the chars left + outbuf += charsWrit; // advance the output pointer (now it sits on the null terminator) + } + } +} + +void CTakeDamageInfo::SetCritType( ECritType eType ) +{ + if ( eType == CRIT_NONE ) + { + // always let CRIT_NONE override the current setting + m_eCritType = eType; + } + else + { + // don't let CRIT_MINI override CRIT_FULL + m_eCritType = ( eType > m_eCritType ) ? eType : m_eCritType; + } +} + +/* +// instant damage + +#define DMG_GENERIC 0 // generic damage was done +#define DMG_CRUSH (1 << 0) // crushed by falling or moving object. +// NOTE: It's assumed crush damage is occurring as a result of physics collision, so no extra physics force is generated by crush damage. +// DON'T use DMG_CRUSH when damaging entities unless it's the result of a physics collision. You probably want DMG_CLUB instead. +#define DMG_BULLET (1 << 1) // shot +#define DMG_SLASH (1 << 2) // cut, clawed, stabbed +#define DMG_BURN (1 << 3) // heat burned +#define DMG_VEHICLE (1 << 4) // hit by a vehicle +#define DMG_FALL (1 << 5) // fell too far +#define DMG_BLAST (1 << 6) // explosive blast damage +#define DMG_CLUB (1 << 7) // crowbar, punch, headbutt +#define DMG_SHOCK (1 << 8) // electric shock +#define DMG_SONIC (1 << 9) // sound pulse shockwave +#define DMG_ENERGYBEAM (1 << 10) // laser or other high energy beam +#define DMG_PREVENT_PHYSICS_FORCE (1 << 11) // Prevent a physics force +#define DMG_NEVERGIB (1 << 12) // with this bit OR'd in, no damage type will be able to gib victims upon death +#define DMG_ALWAYSGIB (1 << 13) // with this bit OR'd in, any damage type can be made to gib victims upon death. +#define DMG_DROWN (1 << 14) // Drowning + + +#define DMG_PARALYZE (1 << 15) // slows affected creature down +#define DMG_NERVEGAS (1 << 16) // nerve toxins, very bad +#define DMG_POISON (1 << 17) // blood poisoning - heals over time like drowning damage +#define DMG_RADIATION (1 << 18) // radiation exposure +#define DMG_DROWNRECOVER (1 << 19) // drowning recovery +#define DMG_ACID (1 << 20) // toxic chemicals or acid burns +#define DMG_SLOWBURN (1 << 21) // in an oven + +#define DMG_REMOVENORAGDOLL (1<<22) // with this bit OR'd in, no ragdoll will be created, and the target will be quietly removed. +// use this to kill an entity that you've already got a server-side ragdoll for + +#define DMG_PHYSGUN (1<<23) // Hit by manipulator. Usually doesn't do any damage. +#define DMG_PLASMA (1<<24) // Shot by Cremator +#define DMG_AIRBOAT (1<<25) // Hit by the airboat's gun + +#define DMG_DISSOLVE (1<<26) // Dissolving! +#define DMG_BLAST_SURFACE (1<<27) // A blast on the surface of water that cannot harm things underwater +#define DMG_DIRECT (1<<28) +#define DMG_BUCKSHOT (1<<29) // not quite a bullet. Little, rounder, different. +*/
\ No newline at end of file |