Regenerating Health

From Valve Developer Community
Revision as of 00:58, 13 January 2015 by ~TwoTails~ (talk | contribs) (For some reason, the code was disabled by default. I fixed it.)

Jump to: navigation, search

In this article, we will walk through the steps to add a Call of Duty/Portal style regenerating health system for singleplayer, and with a little modification multiplayer too. The code used was discovered in the Alien Swarm SDK.

Overview

Here is how the logic will work out:

  • Check to see if the player's health is below the max amount
  • Check the regeneration delay timer and the last time since the player was hurt
  • Add the regeneration amount to a float based on the frametime
  • Check if the float is greater than one, and if it is, add it to the player's health

Adding the ConVars

For this tutorial, we will be modifying the base player class. Open up server/player.cpp and add the following convars:

ConVar sv_regeneration ("sv_regeneration", "1", FCVAR_REPLICATED );
ConVar sv_regeneration_wait_time ("sv_regeneration_wait_time", "1.0", FCVAR_REPLICATED );
ConVar sv_regeneration_rate ("sv_regeneration_rate", "0.5", FCVAR_REPLICATED );

This will allow us to balance the regeneration rate while in-game. However, we will also need a value to hold the current regeneration amount. Place:

float     m_fRegenRemander;

under

int	   gEvilImpulse101;

To get the constructor to work go to this code in player.cpp: CBasePlayer::CBasePlayer( ) below m_iHealth = 0; around line 552 add this:

m_fRegenRemander = 0;

Getting Last Hurt Time

Note: If using the HL2-Singleplayer codebase, simply use m_flLastDamageTime rather than adding m_fTimeLastHurt.

Now we need to figure out the time since the player was last hurt. We need this because we want to delay the time before regeneration starts so that the player does not start regenerating when he is taking damage.

To do this, open up player.h and add the following around line 860

float			m_fTimeLastHurt;

Back in player.cpp place:

DEFINE_FIELD( m_fTimeLastHurt, FIELD_TIME ),

under

DEFINE_FIELD( m_tbdPrev, FIELD_TIME ),

at about line 306.

Now that we have a number to keep track of when the player was last hurt, we will need to keep that value up to date. To do this add:

if ( GetHealth() < 100 )
{
    m_fTimeLastHurt = gpGlobals->curtime;
}

at the end of the CBasePlayer::OnTakeDamage method.

Regenerating

Now that everything is in place, it is time to add in the regeneration logic. At the end of CBasePlayer::PostThink add the following:

// Regenerate heath
if ( IsAlive() && GetHealth() < GetMaxHealth() && (sv_regeneration.GetInt() == 1) )
{
	// Color to overlay on the screen while the player is taking damage
	color32 hurtScreenOverlay = {80,0,0,64};

	if ( gpGlobals->curtime > m_fTimeLastHurt + sv_regeneration_wait_time.GetFloat() )
	{
                //Regenerate based on rate, and scale it by the frametime
		m_fRegenRemander += sv_regeneration_rate.GetFloat() * gpGlobals->frametime;
		
		if(m_fRegenRemander >= 1)
		{
			TakeHealth( m_fRegenRemander, DMG_GENERIC );
			m_fRegenRemander = 0;
		}
	}
	else
	{
		UTIL_ScreenFade( this, hurtScreenOverlay, 1.0f, 0.1f, FFADE_IN|FFADE_PURGE );
	}	
}

And that is it.