source-sdk-2013/mp/src/game/shared/takedamageinfo.cpp
Joe Ludwig beaae8ac45 Updated the SDK with the latest code from the TF and HL2 branches
* Adds support for Visual Studio 2012 and 2013
* VR Mode:
. Switches from headtrack.dll to sourcevr.dll
. Improved readability of the UI in VR
. Removed the IPD calibration tool. TF2 will now obey the Oculus
configuration file. Use the Oculus calibration tool in your SDK or
install and run "OpenVR" under Tools in Steam to calibrate your IPD.
. Added dropdown to enable VR mode in the Video options. Removed the -vr
command line option.
. Added the ability to switch in and out of VR mode without quitting the
game
. By default VR mode will run full screen. To switch back to a
borderless window set the vr_force_windowed convar.
. Added support for VR mode on Linux
* Many assorted bug fixes and other changes from Team Fortress in
various shared files
2013-12-03 08:54:16 -08:00

495 lines
19 KiB
C++

//========= 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;
}
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() );
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)
}
}
}
/*
// 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.
*/