+1 vote

I am making a 2D horror game where you move right to left and am using a clamp function to avoid having to make simple collision for each map. I have two concurrent state machines handling player states (idle, walk, run) and stamina meter states (empty, charging, full with a slower recovery when emptying). All my scripts are working and the debugger's not throwing any errors but for some reason when i hit my clamp limits when running, the variable for stamina starts doing strange things (going back up, not recharging fully, etc). Any help appreciated.

extends KinematicBody2D


const spdDefault = 150
const maxStamina = 300
export var speed = 150
export var runSpeed = 300
export var stamina = 300
export var burnRate = 2
export var chargeRate = 1
export var breatherTime = 2
export var deepBreath = 5
export var marg = 50
var direction = Vector2.ZERO
onready var sprite = $AnimatedSprite
var screen_size

enum STATE {IDLE, WALKING, RUNNING}
enum CHARGESTATE {EMPTY, CHARGING, FULL}
var playerState
var staminaState


func _ready():
    stamina = maxStamina
    playerState = STATE.IDLE
    staminaState = CHARGESTATE.FULL
    screen_size = get_viewport_rect().size


func _input(_event):
    if Input.is_action_pressed("ui_left") or Input.is_action_pressed("ui_right"):
        playerState = STATE.WALKING
        if Input.is_action_pressed("run") and staminaState != CHARGESTATE.EMPTY:
            playerState = STATE.RUNNING
            if staminaState == CHARGESTATE.EMPTY:
                playerState = STATE.WALKING
    else:
        playerState = STATE.IDLE

    if Input.is_action_just_released("run"):
        playerState = STATE.WALKING

    if Input.is_action_just_pressed("debug"):
        debugCommand()


func _process(delta):       
    match playerState:
        STATE.IDLE:
            sprite.play('idle')
        STATE.WALKING:
            walking(delta)
            speed = spdDefault
        STATE.RUNNING:
            running(delta)

    match staminaState:
        CHARGESTATE.EMPTY:
            recharging()
        CHARGESTATE.CHARGING:
            idle_charging()
        CHARGESTATE.FULL:
            pass    

    stamDebug()


func walking(delta):
    direction.x = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
    anim_controller()

    if direction.x == 0:
        playerState = STATE.IDLE

    var movement = direction * speed * delta
    move_and_collide(movement)

    var x_limit_l = (0 - ((screen_size.x/2)-marg))
    var x_limit_r = ((screen_size.x/2)-marg)
    position.x = clamp(position.x, x_limit_l, x_limit_r)


func running(delta):
        walking(delta)
        speed = runSpeed
        while stamina > 0 and staminaState != CHARGESTATE.EMPTY and Input.is_action_pressed("run"):
            stamina = stamina-burnRate
            var burn = Timer.new()
            add_child(burn)
            burn.set_wait_time(burnRate)
            burn.start()
            yield(burn,"timeout")
            if stamina < 1:
                playerState = STATE.WALKING
                break



func stamDebug():
    match stamina:
        maxStamina:
            staminaState = CHARGESTATE.FULL
        0:
            staminaState = CHARGESTATE.EMPTY

    if stamina != maxStamina and staminaState != CHARGESTATE.EMPTY:
        staminaState = CHARGESTATE.CHARGING

    if stamina >= maxStamina:
        stamina = maxStamina
    if stamina < 1:
        stamina = 0



func anim_controller():
    match playerState:
        STATE.WALKING:
            sprite.play('walk')
        STATE.RUNNING:
            sprite.play('run')
        STATE.IDLE:
            sprite.play('idle')


    if direction.x < 0:
        sprite.flip_h = true
    if direction.x > 0:
        sprite.flip_h = false


func recharging():
    var breather = Timer.new()
    add_child(breather)
    breather.set_wait_time(deepBreath)
    breather.start()
    yield(breather,"timeout")
    staminaUp()
    breather.queue_free()


func idle_charging():
    var idleT = Timer.new()
    add_child(idleT)
    idleT.set_wait_time(breatherTime)
    idleT.start()
    yield(idleT,"timeout")
    staminaUp()
    idleT.queue_free()


func staminaUp():
    var sT = Timer.new()
    add_child(sT)
    sT.set_wait_time(chargeRate)
    while stamina < maxStamina:
        sT.start()
        stamina = stamina+chargeRate
        yield(sT,"timeout")
        if stamina >= maxStamina:
            staminaState = CHARGESTATE.FULL
            sT.queue_free()
            break



func chargeCheck():
    if stamina >= maxStamina:
        staminaState = CHARGESTATE.FULL
        stamina = maxStamina
    if stamina != maxStamina:
        staminaState = CHARGESTATE.CHARGING
    if stamina < 1:
        stamina = 0
        staminaState = CHARGESTATE.EMPTY


func debugCommand():
    print("Player state: " + str(playerState))
    print("Charge state: " + str(staminaState))
    print("Stamina level: " + str(stamina))
Godot version v3.4.3.stable.official [242c05d12]
in Engine by (312 points)

Please log in or register to answer this question.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.