void OnStart ()
{
stamina_obj.Start();
}
void OnLeave
{
stamina_obj.Stop();
}
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 message, bool check, bool to_file)
            {
                ::AddDebugMessage("Stamina system: " + message, check);
                if (to_file) Print("Stamina system: " + message);
            }
        bool IsPlayerRunning() const
            {
                return this.is_running;
            }
        void PlayBreathSound()
            {
                PlaySoundAtEntity(STAMINA_PLAYER_BREATH_SLOW, STAMINA_PLAYER_BREATH_SNT, "Player", 0, false);
            }
        void Start()
            {
                SetPlayerRunSpeedMul(1);
                SetPlayerMoveSpeedMul(1);
                AddTimer(STAMINA_PLAYER_SPEED, STAMINA_TIMER_SPEED_TIME, STAMINA_TIMER_CALLBACK_NAME);
            }
        void StartPlayerHeadBob()
            {
                if (this.head_bobbing)
                    MovePlayerHeadPos(0, -0.35, 0, STAMINA_PLAYER_HEAD_BOB_SPEED, 0.6);
                else
                    MovePlayerHeadPos(0, 0, 0, STAMINA_PLAYER_HEAD_BOB_SPEED, 0.6);
                this.head_bobbing = !this.head_bobbing;
                AddTimer(STAMINA_PLAYER_HEAD_BOB, 0.75f, STAMINA_TIMER_CALLBACK_NAME);
            }
        void StartPlayerTiredEffect()
            {
                this.PlayBreathSound();
                AddTimer(STAMINA_PLAYER_BREATH_SLOW, 1.5f, STAMINA_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,0, 1, 1);
                // 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!", false, true);
            }
        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_LENGTH, this.length, STAMINA_TIMER_CALLBACK_NAME);
                    else if(!this.is_running)
                    {
                        if (GetTimerTimeLeft(STAMINA_PLAYER_LENGTH) > 0 && this.state == StaminaStages::Full)
                            RemoveTimer(STAMINA_PLAYER_LENGTH);
                        else if(this.state == StaminaStages::Low)
                            AddTimer(STAMINA_PLAYER_LENGTH, this.length/4, STAMINA_TIMER_CALLBACK_NAME);
                        else if(this.state == StaminaStages::Exhausted)
                            AddTimer(STAMINA_PLAYER_LENGTH, this.length/2, STAMINA_TIMER_CALLBACK_NAME);
                    }
                    AddTimer(timer_name, STAMINA_TIMER_SPEED_TIME, STAMINA_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); }