text: //========= 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;
  }
  
  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 )
  {
  	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.
  */