Adding Portal-style regenerating health: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
No edit summary
No edit summary
Line 1: Line 1:
This is a copy and paste tutorial to add '''Call of Duty's Low Health Regeneration''' for single-player.
This is a copy and paste tutorial to add '''Call of Duty's Low Health Regeneration''' for single-player.


[[File:Codstyle.gif|Demonstration]]
[[File:Codstyle.gif|Demonstration]]


This feature is inspired by [[Regenerating_Health|Regenerating Health]].
This feature is inspired by [[Regenerating Health]].


With this feature, Medkits and Health Chargers are kinda pointless.
With this feature, Medkits and Health Chargers are kinda pointless.


== How does this feature work? ==
== How does this feature work? ==
 
* Check if the health is below the max health (100 HP) or below 25 HP (critical health state)
* 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 the player takes damage, so hide in a safe place is required.
* 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.
* 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.
* After the last damage timer ends, the regeneration function will start and the health will be boosted to 100 again.
Line 17: Line 15:


== Implement ==
== Implement ==
 
First, open up '''player.h''' and search for this function:
First, open up '''player.h''' and search this function


<source lang=cpp>
<source lang=cpp>
Line 43: Line 40:
</source>
</source>


Under that function, add this lines of code:
Under that function, add these lines of code:


<source lang=cpp>
<source lang=cpp>
Line 59: Line 56:


== player.cpp ==
== player.cpp ==
 
Then, we open up the '''player.cpp''':
Then, we open up the '''player.cpp'''


<source lang=cpp>
<source lang=cpp>
Line 66: Line 62:
</source>
</source>


Below that code, we add this
Below that code, we add this:


<source lang=cpp>
<source lang=cpp>
Line 75: Line 71:
== CBasePlayer::CBasePlayer() ==  
== CBasePlayer::CBasePlayer() ==  
{{Note| I forgot to add this!}}
{{Note| I forgot to add this!}}
On <code> CBasePlayer::CBasePlayer()</code> under <code> m_iReplayEntity = 0; </code>
On <code>CBasePlayer::CBasePlayer()</code> under <code> m_iReplayEntity = 0;</code>
We add this
we add this:
<source lang=cpp>
<source lang=cpp>
m_flNextLowHealthSoundTime = 0.0f;
m_flNextLowHealthSoundTime = 0.0f;
Line 83: Line 79:


== OnTakeDamage() ==
== OnTakeDamage() ==
Look up to '''OnTakeDamage''' function and find this code
Look up the '''OnTakeDamage''' function and find this code:


<source lang=cpp>
<source lang=cpp>
Line 92: Line 88:
</source>
</source>


Then, after that code. Add this code
Then after that, add the following:


<source lang=cpp>
<source lang=cpp>
Line 156: Line 152:


== HandleFastRegen() ==
== HandleFastRegen() ==
 
Below the '''OnTakeDamage()''' function, we create another function called HandleFastRegen():
Below the '''OnTakeDamage()''' function, we create another function called HandleFastRegen()
<source lang=cpp>
<source lang=cpp>
void CBasePlayer::HandleFastRegen()
void CBasePlayer::HandleFastRegen()
Line 197: Line 192:


== PostThink() ==
== PostThink() ==
 
Then, we go to the '''PostThink''' function and find this code:
Then, we go to the '''PostThink''' function and find this code
<source lang=cpp>
<source lang=cpp>
#if !defined( NO_ENTITY_PREDICTION )
#if !defined( NO_ENTITY_PREDICTION )
Line 205: Line 199:
#endif
#endif
</source>
</source>
Below, we add this code
Below, we add the following:


<source lang=cpp>
<source lang=cpp>
Line 238: Line 232:
</source>
</source>


Lastly, we must precache the sound, you may want to precache the sound either in '''player.cpp''' or '''hl2_player.cpp'''
Lastly, we must precache the sound, you may want to precache the sound either in '''player.cpp''' or '''hl2_player.cpp'''!


<source lang=cpp>
<source lang=cpp>
Line 247: Line 241:


== Optional ==
== Optional ==
Oh, you can customize the sound to your liking. [[:User:Hugancrit|I]] personally [https://github.com/Huga22118/source-sdk-2013/tree/master/sp/game/hugamod/sound/player/h1_misc use] this for my custom mod. Don't forget to precache the sound with the sound you want to change!


Oh, you can customize the sound to your liking. I personally [https://github.com/Huga22118/source-sdk-2013/tree/master/sp/game/hugamod/sound/player/h1_misc use] this for my custom mod. Don't forget to precache the sound with sound you want to change!
; <code>Here are my sound scripts:</code>
 
; <code>my sound scripts</code>


<pre>  
<pre>  
Line 259: Line 252:
"soundlevel" "SNDLVL_GUNFIRE"
"soundlevel" "SNDLVL_GUNFIRE"
"pitch" "PITCH_NORM"
"pitch" "PITCH_NORM"
"rndwave"
"rndwave"
{
{
Line 270: Line 262:
"wave" "player/h1_misc/player_breathhurt7.wav"
"wave" "player/h1_misc/player_breathhurt7.wav"
}
}
}
}


Line 280: Line 270:
"soundlevel" "SNDLVL_GUNFIRE"
"soundlevel" "SNDLVL_GUNFIRE"
"pitch" "PITCH_NORM"
"pitch" "PITCH_NORM"
"rndwave"
"rndwave"
{
{
Line 288: Line 275:
"wave" "player/h1_misc/player_breathbetter2.wav"
"wave" "player/h1_misc/player_breathbetter2.wav"
"wave" "player/h1_misc/player_breathbetter3.wav"
"wave" "player/h1_misc/player_breathbetter3.wav"
        }
}
 
}
}
<pre>
<pre>

Revision as of 13:42, 7 May 2025

This is a copy and paste tutorial to add Call of Duty's Low Health Regeneration for single-player.

Demonstration

This feature is inspired by Regenerating Health.

With this feature, Medkits 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 the player takes damage, so hide in a safe place is required.
  • 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 for 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 these 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()

Note.pngNote: I forgot to add this!

On CBasePlayer::CBasePlayer() under m_iReplayEntity = 0; we add this:

m_flNextLowHealthSoundTime = 0.0f;
m_flStartReliefSoundTime = 0.0f;

OnTakeDamage()

Look up the OnTakeDamage function and find this code:

if ( bitsDamage & DMG_BLAST )
	{
		OnDamagedByExplosion( info );
	}

Then after that, add the following:

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 the following:

// 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 the sound you want to change!

Here are my sound scripts:
 
"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"
	}
}