Skip to content

Instantly share code, notes, and snippets.

@sigsegv-mvm
Created November 10, 2016 21:51
Show Gist options
  • Save sigsegv-mvm/ee7ed6c6deaec50c5de59cd03fe6c4b2 to your computer and use it in GitHub Desktop.
Save sigsegv-mvm/ee7ed6c6deaec50c5de59cd03fe6c4b2 to your computer and use it in GitHub Desktop.
How TF2 health regen works, in detail
// Health regen stuff
// Based on server_srv.so version 20151007a
// Reverse engineering by sigsegv
/* this Think function is called once per second */
void CTFPlayer::RegenThink()
{
if (!this->IsAlive())
{
// The member variable m_flRegenAmount is stored between calls
// This means that fractional health regen amounts will build up over time
// (e.g. 0.2 health regen will result in +1 hp every 5 seconds or so)
if (this->IsPlayerClass(TF_CLASS_MEDIC))
{
float flTimeSinceDamage = this->GetLastDamageTime() - gpGlobals->curtime;
// Set the innate medic health regen based on time-since-last-damage:
// < 5 seconds: +3.0
// 5-10 seconds: smoothly scales between +3.0 and +6.0
// > 10 seconds: +6.0
float flMedicRegen = RemapValClamped(flTimeSinceDamage, 5.0f, 10.0f, 3.0f, 6.0f);
// Handling for the Healing Mastery upgrade
if (TFGameRules()->GameModeUsesUpgrades())
{
int iAttr_HealingMastery = 0;
CALL_ATTRIB_HOOK_INT(iAttr_HealingMastery, healing_mastery);
if (iAttr_HealingMastery != 0)
{
// The attribute value range (0 to 4) is mapped to a scale factor range (1.00x to 2.00x)
// [ 0: 1.00x 1: 1.25x 2: 1.50x 3: 1.75x 4: 2.00x ]
float flScaleFactor = RemapValClamped((float)iAttr_HealingMastery, 0.0f, 4.0f, 1.00f, 2.00f);
flMedicRegen *= flScaleFactor;
}
}
this->m_flRegenAmount += flMedicRegen;
}
// Add/subtract the health regen attribute(s) active on the player
float flAttr_HealthRegen = 0.0f;
CALL_ATTRIB_HOOK_FLOAT(flAttr_HealthRegen, add_health_regen);
this->m_flRegenAmount += flAttr_HealthRegen;
// SKIPPED: Mannpower rune stuff
int iRegenAmount = 0;
// If we are regenning by at least 1 positive health point, then heal
if (this->m_flRegenAmount >= 1.0f)
{
// Get just the integer part of the regen amount
iRegenAmount = (int)floor(this->m_flRegenAmount);
// Don't bother healing if already at full health
// This is completely redundant because CBaseEntity::TakeHealth does the exact same check
if (this->GetHealth() < this->GetMaxHealth())
{
// Heal the player by iRegenAmount health points (will not exceed max health)
this->TakeHealth((float)iRegenAmount, DMG_GENERIC);
}
}
// If we are regenning by at least 1 negative health point, then hurt
// BUG: if m_flRegenAmount is exactly -1.0f, no hurt occurs (should be: <= -1.0f)
if (this->m_flRegenAmount < -1.0f)
{
// Get just the integer part of the regen amount
iRegenAmount = (int)ceil(this->m_flRegenAmount);
// Hurt the player by -iRegenAmount health points
// [ Attacker: self Inflictor: self Weapon: none Force: none Location: middle of player ]
this->TakeDamage(CTakeDamageInfo(this, this, nullptr, vec3_origin, this->WorldSpaceCenter(), (float)(-iRegenAmount), DMG_GENERIC));
}
// SKIPPED: irrelevant stuff
// If we did heal or hurt ourselves, then remove the integer amount we
// healed/hurt from the future regen amount (but leave any fractional part)
this->m_flRegenAmount -= (float)iRegenAmount;
// SKIPPED: irrelevant stuff
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment