Frictional Games Forum (read-only)
Basic stamina system - Printable Version

+- Frictional Games Forum (read-only) (https://www.frictionalgames.com/forum)
+-- Forum: Amnesia: The Dark Descent (https://www.frictionalgames.com/forum/forum-6.html)
+--- Forum: Custom Stories, TCs & Mods - Development (https://www.frictionalgames.com/forum/forum-38.html)
+---- Forum: Development Articles (https://www.frictionalgames.com/forum/forum-40.html)
+---- Thread: Basic stamina system (/thread-16949.html)

Pages: 1 2


Basic stamina system - Your Computer - 07-13-2012

This is something i've been meaning to create for a while but was busy deciding how i should handle this. Unfortunately (though not entirely), Amnesia uses an outdated version of AngelScript, so i had to rely on classes as a means of imitating namespaces. Likewise unfortunate, in AngelScript functions are taken as "read-only" and the engine doesn't look for member functions for what i was trying to do, so i couldn't create proxies for the AddTimer function by changing the "pointers" of functions to member functions.

Here is the code for the stamina system:

PHP Code:
const string STAMINA_TIMER_CALLBACK_NAME "STAMINA_TIMER_CALLBACK";
const 
string STAMINA_PLAYER_SPEED "STAMINA_PLAYER_SPEED";
const 
string STAMINA_PLAYER_LENGTH "STAMINA_PLAYER_LENGTH";
const 
string STAMINA_PLAYER_BREATH_SLOW "STAMINA_PLAYER_BREATH_SLOW";
const 
string STAMINA_PLAYER_BREATH_SNT "react_breath_slow.snt";
const 
string STAMINA_PLAYER_HEAD_BOB "STAMINA_PLAYER_HEAD_BOB";

const 
float STAMINA_TIMER_SPEED_TIME 0.5f;
const 
float STAMINA_PLAYER_HEAD_BOB_SPEED 0.7f;

enum StaminaStages
    
{
        
Full,
        
Low,
        
Exhausted
    
}

class 
StaminaSystem
    
{
        private 
int state;
        private 
uint length;
        private 
bool head_bobbing;
        private 
bool is_running;

        
StaminaSystem()
            {
                
PreloadSound(STAMINA_PLAYER_BREATH_SNT);
                
this.state StaminaStages::Full;
                
this.length 10;
                
this.head_bobbing false;
                
this.is_running false;
            }

        ~
StaminaSystem()
            {
                
this.Stop();
            }

        
void AddDebugMessage(string &in messagebool checkbool to_file)
            {
                ::
AddDebugMessage("Stamina system: " messagecheck);
                if (
to_file) Print("Stamina system: " message);
            }

        
bool IsPlayerRunning() const
            {
                return 
this.is_running;
            }

        
void PlayBreathSound()
            {
                
PlaySoundAtEntity(STAMINA_PLAYER_BREATH_SLOWSTAMINA_PLAYER_BREATH_SNT"Player"0false);
            }

        
void Start()
            {
                
SetPlayerRunSpeedMul(1);
                
SetPlayerMoveSpeedMul(1);
                
AddTimer(STAMINA_PLAYER_SPEEDSTAMINA_TIMER_SPEED_TIMESTAMINA_TIMER_CALLBACK_NAME);
            }

        
void StartPlayerHeadBob()
            {
                if (
this.head_bobbing)
                    
MovePlayerHeadPos(0, -0.350STAMINA_PLAYER_HEAD_BOB_SPEED0.6);
                else
                    
MovePlayerHeadPos(000STAMINA_PLAYER_HEAD_BOB_SPEED0.6);

                
this.head_bobbing = !this.head_bobbing;
                
AddTimer(STAMINA_PLAYER_HEAD_BOB0.75fSTAMINA_TIMER_CALLBACK_NAME);
            }

        
void StartPlayerTiredEffect()
            {
                
this.PlayBreathSound();
                
AddTimer(STAMINA_PLAYER_BREATH_SLOW1.5fSTAMINA_TIMER_CALLBACK_NAME);
            }

        
void Stop()
            {
                
this.StopPlayerTiredEffect();
                
RemoveTimer(STAMINA_PLAYER_SPEED);
                
RemoveTimer(STAMINA_PLAYER_LENGTH);
                
SetPlayerRunSpeedMul(1);
                
SetPlayerMoveSpeedMul(1);
                
SetPlayerJumpDisabled(false);
            }

        
void StopPlayerTiredEffect()
            {
                
this.head_bobbing false;
                
RemoveTimer(STAMINA_PLAYER_BREATH_SLOW);
                
RemoveTimer(STAMINA_PLAYER_HEAD_BOB);
                
MovePlayerHeadPos(0,0,011);
                
// StopSound(STAMINA_PLAYER_BREATH_SLOW, 1); // Crashes the game upon exit.
            
}

        
void SetStaminaLength(uint length)
            {
                if (
length != 0)
                    
this.length length;
                else
                    
this.AddDebugMessage("Length cannot be 0!"falsetrue);
            }

        
void Update(string &in timer_name)
            {
                if (
timer_name == STAMINA_PLAYER_SPEED)
                {
                    
this.is_running = (GetPlayerSpeed() > 3) ? true false;

                    if (
this.is_running && GetTimerTimeLeft(STAMINA_PLAYER_LENGTH) == 0)
                        
AddTimer(STAMINA_PLAYER_LENGTHthis.lengthSTAMINA_TIMER_CALLBACK_NAME);

                    else if(!
this.is_running)
                    {
                        if (
GetTimerTimeLeft(STAMINA_PLAYER_LENGTH) > && this.state == StaminaStages::Full)
                            
RemoveTimer(STAMINA_PLAYER_LENGTH);

                        else if(
this.state == StaminaStages::Low)
                            
AddTimer(STAMINA_PLAYER_LENGTHthis.length/4STAMINA_TIMER_CALLBACK_NAME);

                        else if(
this.state == StaminaStages::Exhausted)
                            
AddTimer(STAMINA_PLAYER_LENGTHthis.length/2STAMINA_TIMER_CALLBACK_NAME);
                    }

                    
AddTimer(timer_nameSTAMINA_TIMER_SPEED_TIMESTAMINA_TIMER_CALLBACK_NAME);
                }

                else if (
timer_name == STAMINA_PLAYER_BREATH_SLOW)
                    
this.StartPlayerTiredEffect();

                else if (
timer_name == STAMINA_PLAYER_HEAD_BOB)
                    
this.StartPlayerHeadBob();

                else if (
timer_name == STAMINA_PLAYER_LENGTH)
                {
                    if (
this.is_running)
                    {
                        if (
this.state == StaminaStages::Full)
                        {
                            
this.state++;
                            
SetPlayerRunSpeedMul(0.3);
                        }

                        else if (
this.state == StaminaStages::Low)
                        {
                            
this.state++;
                            
SetPlayerRunSpeedMul(0);
                            
SetPlayerMoveSpeedMul(0);
                            
SetPlayerJumpDisabled(true);
                            
this.StartPlayerTiredEffect();
                            
this.StartPlayerHeadBob();
                        }
                    }

                    else
                    {
                        
this.StopPlayerTiredEffect();

                        
SetPlayerRunSpeedMul(1);
                        
SetPlayerMoveSpeedMul(1);
                        
SetPlayerJumpDisabled(false);

                        if (
this.state == StaminaStages::Low)
                            
this.state StaminaStages::Full;

                        else if (
this.state == StaminaStages::Exhausted)
                            
this.state StaminaStages::Full;
                    }
                }
            }
    }

StaminaSystem stamina_obj;

void STAMINA_TIMER_CALLBACK(string &in timer_name)
    { 
stamina_obj.Update(timer_name); } 
  • StaminaSystem:Confusedtate is the current "StaminaStage" of the player.
  • StaminaSystem::length is the amount of time in seconds that the player is allowed to run for before entering a more exhausted state.
  • StaminaSystem::is_running holds whether or not the player is understood to be "running." Use StaminaSystem::IsPlayerRunning() to access the variable outside of the class. (Note: StaminaSystem must have been started in order for this to return expected values.)
  • StaminaSystem::head_bobbing is used to store a toggle state to determine which direction the player's head should move in.
The StaminaStages values should be self-explanatory.

When the player reaches the StaminaStages::Exhausted state, the player is prevented from moving for 1/2 the time of StaminaSystem::length; and when the player reaches StaminaStages::Low and is not running, 1/4 the time of StaminaSystem::length is required to regain full stamina.

To activate it for your map, having something as simple as this is enough:

PHP Code:
void OnEnter()
{
    
stamina_obj.Start();
}

void OnLeave()
{
    
stamina_obj.Stop();


In action:





RE: Basic stamina system - Bas - 07-14-2012

Hilarious if you could gain stamina after enabling an energydrink.


RE: Basic stamina system - Ironbard - 07-15-2012

Pretty sweet, I am trying to make something similar for a mod me and some friends are working on Big Grin


RE: Basic stamina system - nemesis567 - 07-17-2012

I think I'm missing something. Why do you use the this pointer all the time? Is that necessary?


RE: Basic stamina system - Your Computer - 07-18-2012

(07-17-2012, 11:00 PM)nemesis567 Wrote: I think I'm missing something. Why do you use the this pointer all the time? Is that necessary?

Mostly for readability. It's not required to do that when accessing a member (except when a variable with the same name as another member is declared within the same, local scope).


RE: Basic stamina system - finScript - 08-27-2012

Where should I put this code to make it work?


RE: Basic stamina system - DnALANGE - 08-23-2013

I took it and works perfectly fine!
Also it is sort of random here for the breathing and walking again!
Awesome feature!
Also i added some FadeImageTrailTo(5,1); for the exausted part, gives it that little extra.
Nice toutch Your computer, + rep for that !Wink


RE: Basic stamina system - Daemian - 08-23-2013

How nice. I actually did something alike in my mod.
It works different, it stops when the player stops running.

I didn't know how to call it, so i put "auto sprint".
But it's basically the guy running faster over time.

PHP Code:
Onstart{
AutoSprintInit(); 


PHP Code:
// SPRINT {

void AutoSprintInit ()
{    
AddTimer ( EMPTY, 1.0"AutoSprintCheck" ); }

void AutoSprintCheck string &in asTimer )
{
    
float pSpeed GetPlayerSpeed();
    
    if ( 
pSpeed 3.7f ) { AutoSprintNormalize(); }
    if ( 
pSpeed >= 3.8f ) { AutoSprintIncreasepSpeed ); }
    
    
AddTimer ( EMPTY, 1.0"AutoSprintCheck" );
}

void AutoSprintIncrease float pSpeed )
{    
    
float fBonus pSpeed 0.4;
    
float fBonusMax 3;

    if ( 
fBonus fBonusMax ) { fBonus fBonusMax; }
    
SetPlayerRunSpeedMulfBonus );
}

void AutoSprintNormalize ()
SetPlayerRunSpeedMul1.0 ); }

// } SPRINT 

It's useful for large levels.
If you change fBonusMax speed goes to hell.

Hey YC, just a question, why you declare these constants?
STAMINA_PLAYER_HEAD_BOB = "STAMINA_PLAYER_HEAD_BOB";
Just to avoid writing quotation marks? I'm curious.


RE: Basic stamina system - Your Computer - 08-23-2013

(08-23-2013, 07:29 PM)Amn Wrote: Hey YC, just a question, why you declare these constants?
STAMINA_PLAYER_HEAD_BOB = "STAMINA_PLAYER_HEAD_BOB";
Just to avoid writing quotation marks? I'm curious.

I used them in multiple places in my script. So, if i ever choose to edit the string value, i won't have to search for every location i used it in, i can just change the value and it'll be reflected across the script.


RE: Basic stamina system - Kullin - 09-03-2013

Hey, this might sound noobish, but where should i put everything? :p