//========= 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. */