Adding Call of Duty Style Low Health Regeneration

This is a copy and paste tutorial to add Call of Duty's Low Health Regeneration for single-player.
This feature is inspired by Regenerating Health.
With this feature, Medkit, and Health Chargers are kinda pointless
== How does this feature work? ==
* Check if the health is below the max health (100 hp) or below 25 hp (critical health state)
* Start the last damage timer for 5 seconds. It will reset each time player take damage so hide in the safe place
* While in critical health state, the player will experience breathing like someone in pain sound.
* After the last damage timer ends, the regeneration function will start and the health will be boosted to 100 again.
* Repeat cycle :>
== Implement ==
First, open up player.h and search this function
float ConsumeMovementTimeForUserCmdProcessing( float flTimeNeeded )
	{
		if ( m_flMovementTimeForUserCmdProcessingRemaining <= 0.0f )
		{
			return 0.0f;
		}
		else if ( flTimeNeeded > m_flMovementTimeForUserCmdProcessingRemaining + FLT_EPSILON )
		{
			float flResult = m_flMovementTimeForUserCmdProcessingRemaining;
			m_flMovementTimeForUserCmdProcessingRemaining = 0.0f;
			return flResult;
		}
		else
		{
			m_flMovementTimeForUserCmdProcessingRemaining -= flTimeNeeded;
			if ( m_flMovementTimeForUserCmdProcessingRemaining < 0.0f )
				m_flMovementTimeForUserCmdProcessingRemaining = 0.0f;
			return flTimeNeeded;
		}
	}
Under that function, add this lines of code:
void HandleFastRegen();
float m_flLastDamageTime;
bool m_bFastRegenActive;
float m_flFastRegenStartTime;
float m_flNextFastRegenTime;
bool m_isPlayerNearDying = false;
bool m_bBuzzingSoundActive = false;
float m_flBuzzingSoundEndTime;
float m_flNextLowHealthSoundTime;
float m_flStartReliefSoundTime;
== player.cpp ==
Then, we open up the player.cpp
DEFINE_FIELD( m_tbdPrev, FIELD_TIME ),
Below that code, we add this
DEFINE_FIELD(m_flLastDamageTime, FIELD_TIME),
DEFINE_FIELD(m_isPlayerNearDying, FIELD_BOOLEAN),
== CBasePlayer::CBasePlayer() == 
On CBasePlayer::CBasePlayer() under m_iReplayEntity = 0; 
We add this
m_flNextLowHealthSoundTime = 0.0f;
m_flStartReliefSoundTime = 0.0f;
== OnTakeDamage() ==
Look up to OnTakeDamage function and find this code
if ( bitsDamage & DMG_BLAST )
	{
		OnDamagedByExplosion( info );
	}
Then, after that code. Add this code
if (GetHealth() < 100) {
	m_flLastDamageTime = gpGlobals->curtime;
}
if (IsAlive() && GetHealth() < 100 /* && !m_bFastRegenActive*/) {
	
	m_bFastRegenActive = true;
	// HUGAMOD: Start regeneration after 5 seconds
	// but it will reset each time you take damage
	m_flFastRegenStartTime = gpGlobals->curtime + 5.0f;
	m_flNextFastRegenTime = m_flFastRegenStartTime;
}
float flDamage = info.GetDamage();
if (GetHealth() < 25) {
	color32 red = { 128, 0, 0, 128 };
	UTIL_ScreenFade(this, red, 2.15f, 5.0f, FFADE_IN);
// HUGAMOD: Commented out ugly PunchAngle
//ViewPunch(QAngle(random->RandomInt(-2, 2), random->RandomInt(-2, 2), random->RandomInt(-2, 2)));
	if (!m_isPlayerNearDying)
	{
		// HUGAMOD: Checks if the player is in a vehicle so that
		// the player won't get punchangle, it will bug the view
		// if you remove this check (i already bug-tested it :>)
		if (!this->IsInAVehicle()) {
			// HUGAMOD: Ported CSS Headshot PunchAngle
			flDamage *= 4;
			QAngle punchAngle = GetPunchAngle();
			punchAngle.x = flDamage * -0.5;
			if (punchAngle.x < -12)
				punchAngle.x = -12;
			punchAngle.z = flDamage * random->RandomFloat(-1, 1);
			if (punchAngle.z < -9)
				punchAngle.z = -9;
			else if (punchAngle.z > 9)
				punchAngle.z = 9;
			SetPunchAngle(punchAngle);
		}
		EmitSound("Flesh.Headshot");
		m_isPlayerNearDying = true;
	}
	if (!m_bBuzzingSoundActive)
	{
		int effect = random->RandomInt(32, 34);
		CSingleUserRecipientFilter user(this);
		enginesound->SetPlayerDSP(user, effect, false);
		m_flBuzzingSoundEndTime = gpGlobals->curtime + 5.0f;
		m_bBuzzingSoundActive = true;
	}
}
== HandleFastRegen() ==
Below the OnTakeDamage() function, we create another function called HandleFastRegen()
void CBasePlayer::HandleFastRegen()
{
	
	if (gpGlobals->curtime - m_flLastDamageTime < 5.0f)
	{
		// Not enough time has passed, do not start regeneration
		return;
	}
	if (m_bFastRegenActive && gpGlobals->curtime >= m_flFastRegenStartTime)
	{
		if (gpGlobals->curtime >= m_flNextFastRegenTime)
		{
			// Add 5 health each time
			TakeHealth(5, DMG_GENERIC); 
			// Set the next regeneration time for 0.1 seconds
			m_flNextFastRegenTime = gpGlobals->curtime + 0.1f; 
			m_isPlayerNearDying = false;
			// Stop the regeneration if the health reaches 100
			if (GetHealth() >= 100)
			{
				m_bFastRegenActive = false;
			}
			
			if (m_bBuzzingSoundActive && gpGlobals->curtime >= m_flBuzzingSoundEndTime)
			{
				// HUGAMOD: Delays the relief breathing sound 
				// for 0.45 - 0.5 seconds because it will collide with
				// the hurt breathing sound
				m_flStartReliefSoundTime = gpGlobals->curtime + RandomFloat(0.45, 0.5);
				m_bBuzzingSoundActive = false;
			}
		}
	}
	
}
== PostThink() ==
Then, we go to the PostThink function and find this code
#if !defined( NO_ENTITY_PREDICTION )
	// Even if dead simulate entities
	SimulatePlayerSimulatedEntities();
#endif
Below, we add this code
// HUGAMOD: Dead player will ignore this
if (IsAlive()) {
	if (GetHealth() < 25)
	{
		if (gpGlobals->curtime >= m_flNextLowHealthSoundTime)
		{
			// Play hurt breath sound
			EmitSound("Player.Breathhurt");
			// Set the next hurt breath sound time
			m_flNextLowHealthSoundTime = gpGlobals->curtime + 1.0f; // Interval 1 detik
		}
	}
	else
	{
		if (m_flStartReliefSoundTime > 0.0f && gpGlobals->curtime >= m_flStartReliefSoundTime)
		{
			EmitSound("Player.Breathbetter");
			// Reset relief breath time to prevent looping
			m_flStartReliefSoundTime = 0.0f;
		}
		StopSound("Player.Breathhurt"); // Stop breath hurt sound
		// Reset breath hurt time to prevent looping
		m_flNextLowHealthSoundTime = 0.0f;
	}
}
HandleFastRegen();
Lastly, we must precache the sound, you may want to precache the sound either in player.cpp or hl2_player.cpp
PrecacheScriptSound("Player.Breathhurt");
PrecacheScriptSound("Player.Breathbetter");
PrecacheScriptSound("Flesh.Headshot");
== Optional ==
Oh, you can customize the sound to your liking. I personally use this for my custom mod. Don't forget to precache the sound with sound you want to change!
; my sound scripts
<pre> 
"Player.Breathhurt"
{
	"channel"		"CHAN_STATIC"
	"volume"		"1.0"
	"soundlevel"	"SNDLVL_GUNFIRE"
	"pitch"			"PITCH_NORM"
	"rndwave"
	{
		"wave"		"player/h1_misc/player_breathhurt1.wav"
		"wave"		"player/h1_misc/player_breathhurt2.wav"
		"wave"		"player/h1_misc/player_breathhurt3.wav"
		"wave"		"player/h1_misc/player_breathhurt4.wav"
		"wave"		"player/h1_misc/player_breathhurt5.wav"
		"wave"		"player/h1_misc/player_breathhurt6.wav"
		"wave"		"player/h1_misc/player_breathhurt7.wav"
	}
	
}
"Player.Breathbetter"
{
	"channel"		"CHAN_STATIC"
	"volume"		"1.0"
	"soundlevel"	"SNDLVL_GUNFIRE"
	"pitch"			"PITCH_NORM"
	
	"rndwave"
	{
		"wave"		"player/h1_misc/player_breathbetter1.wav"
		"wave"		"player/h1_misc/player_breathbetter2.wav"
		"wave"		"player/h1_misc/player_breathbetter3.wav"
 }
	
}
<pre>
Category:Programming
Category:Tutorials