Stamina callback to player to decide transition.

This commit is contained in:
Dustin 2025-04-10 22:39:48 -07:00
parent 41b740be4f
commit 6dfba1b5b9
4 changed files with 76 additions and 23 deletions

View File

@ -2,11 +2,24 @@ class_name StaminaComponent
extends Node extends Node
export var max_stamina: int = 0 export var max_stamina: int = 0
## Recovery Rate in units per second
export var recovery_rate: int = 0
onready var stamina :int = max_stamina onready var stamina :int = max_stamina
signal stamina_depleted() signal stamina_depleted()
var recovery_counter: float = 0
func _process(delta):
recovery_counter += delta
if recovery_rate != 0:
var _rate :float = 1.0/float(recovery_rate)
if recovery_counter > _rate: ## Divide by zero warning
recover(1)
recovery_counter = 0
func reduce_stamina(_amount :int): func reduce_stamina(_amount :int):
if _amount < 0: if _amount < 0:
push_error("ERROR: Only positive numbers in stamina component functions.") push_error("ERROR: Only positive numbers in stamina component functions.")

View File

@ -1,6 +1,7 @@
extends Movement_StateReceiver extends Movement_StateReceiver
var parent_request_state_change :FuncRef var parent_request_state_change :FuncRef
var parent_use_primary_item :FuncRef
var player_number: int = 1 var player_number: int = 1
@ -17,6 +18,10 @@ func _ready():
#TODO Should probably assert here or something. #TODO Should probably assert here or something.
state_ref.connect("state_entered", self, "_on_state_entered_" + state_name) state_ref.connect("state_entered", self, "_on_state_entered_" + state_name)
parent_request_state_change = funcref(get_node(callable_state_machine), 'check_parent_before_state_change') parent_request_state_change = funcref(get_node(callable_state_machine), 'check_parent_before_state_change')
if parent.has_method("use_primary_item"):
parent_use_primary_item = funcref(parent, "use_primary_item")
# Lets see if this nutty thing works # Lets see if this nutty thing works
# var state_ref :State = get_node(callable_state_machine).get_state_reference('idle') # var state_ref :State = get_node(callable_state_machine).get_state_reference('idle')
# state_ref.connect("state_entered", self, "_on_state_entered_idle") # state_ref.connect("state_entered", self, "_on_state_entered_idle")
@ -80,11 +85,11 @@ func wants_dash() -> bool:
var _wants_roll :bool var _wants_roll :bool
func wants_roll() -> bool: func wants_roll() -> bool:
if Input.is_action_just_pressed("dash_" + str(player_number)) and Input.is_action_pressed("move_up_" + str(player_number)): if Input.is_action_just_pressed("dash_" + str(player_number)) and Input.is_action_pressed("move_up_" + str(player_number)):
_wants_roll = true if parent.enough_stamina_for("roll"):
return true _wants_roll = true
else: return true
_wants_roll = false _wants_roll = false
return false return false
var _wants_climb :bool var _wants_climb :bool
func wants_climb() -> bool: func wants_climb() -> bool:
@ -154,15 +159,20 @@ func _state_process_physics_idle():
func attack_primary(): func attack_primary():
##Another thing I wish I could avoid the funcref or string call ##Another thing I wish I could avoid the funcref or string call
if get_parent().has_method("primary_item"): # if get_parent().has_method("use_primary_item"):
var _item_state_name :String = get_parent().primary_item() # var _item_state_name :String = get_parent().use_primary_item()
# if _item_state_name != '':
# request_state_change.call_func(_item_state_name)
if parent_use_primary_item.is_valid():
var _item_state_name :String = parent_use_primary_item.call_func()
if _item_state_name != '': if _item_state_name != '':
request_state_change.call_func(_item_state_name) request_state_change.call_func(_item_state_name)
func attack_secondary(): func attack_secondary():
if get_parent().has_method("secondary_item"): if get_parent().has_method("use_secondary_item"):
var _item_state_name :String = get_parent().secondary_item() var _item_state_name :String = get_parent().use_secondary_item()
if _item_state_name != '': if _item_state_name != '':
request_state_change.call_func(_item_state_name) request_state_change.call_func(_item_state_name)

View File

@ -14,7 +14,10 @@ var punch_item :Item = preload("res://assets/items/punch.tres")
const state_stamina_cost :Dictionary = { const state_stamina_cost :Dictionary = {
"jump": 10, "jump": 10,
"attack_sword":20 "attack_sword":20,
"attack_punch":10,
"roll":40,
"ledge_climb":10
} }
# Called when the node enters the scene tree for the first time. # Called when the node enters the scene tree for the first time.
@ -31,16 +34,16 @@ func _ready():
movement_component.state_stamina_cost = state_stamina_cost movement_component.state_stamina_cost = state_stamina_cost
var stamina_recovery :float = 0.0 #var stamina_recovery :float = 0.0
# Called every frame. 'delta' is the elapsed time since the previous frame. # Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta): func _process(delta):
PlayerInfo.player_position = global_position.round() PlayerInfo.player_position = global_position.round()
stamina_recovery += delta # stamina_recovery += delta
if stamina_recovery > 1.0: # if stamina_recovery > 1.0:
stamina_component.recover(1) # stamina_component.recover(1)
stamina_recovery = 0.0 # stamina_recovery = 0.0
#print("recover ", stamina_component.stamina) # #print("recover ", stamina_component.stamina)
PlayerInfo.player_stamina = stamina_component.stamina PlayerInfo.player_stamina = stamina_component.stamina
@ -89,6 +92,15 @@ func touch_the_thing(the_thing: Interactable) -> bool:
the_thing.trigger_interaction() the_thing.trigger_interaction()
return true return true
func enough_stamina_for(_state_name :String) -> bool:
if state_stamina_cost.has(_state_name):
if stamina_component.stamina >= state_stamina_cost[_state_name]:
return true
else:
return false
## Return true by default because we don't have a cost for this move
return true
var state_to_item_map :Dictionary = { var state_to_item_map :Dictionary = {
"sword": "attack_sword" , "sword": "attack_sword" ,
"gun": "attack_shoot", "gun": "attack_shoot",
@ -97,7 +109,7 @@ var state_to_item_map :Dictionary = {
} }
##TODO: We need to find a way to 'use' the item. ##TODO: We need to find a way to 'use' the item.
func primary_item() -> String: func use_primary_item() -> String:
# if player_inventory.primary_selection.consumable == false: # if player_inventory.primary_selection.consumable == false:
# if state_to_item_map.has(player_inventory.primary_selection.name): # if state_to_item_map.has(player_inventory.primary_selection.name):
# player_inventory.remove_from_inventory(player_inventory.primary_selection) # player_inventory.remove_from_inventory(player_inventory.primary_selection)
@ -106,18 +118,33 @@ func primary_item() -> String:
if player_inventory.primary_selection != null: # Have to make sure we even have a second if player_inventory.primary_selection != null: # Have to make sure we even have a second
var _item_name :String = player_inventory.primary_selection.name var _item_name :String = player_inventory.primary_selection.name
if state_to_item_map.has(_item_name): if state_to_item_map.has(_item_name):
var state_name = state_to_item_map[_item_name]
## This item state has a cost associated with it.
if state_stamina_cost.has(state_name):
if state_stamina_cost[state_name] <= stamina_component.stamina:
#print("Yup: ", state_stamina_cost[state_name], "<=", stamina_component.stamina)
player_inventory.remove_from_inventory(player_inventory.primary_selection)
return state_to_item_map[_item_name]
else:
## TODO: maybe some sort of 'fail' animation state
print("Nope: ", state_stamina_cost[state_name], "<=", stamina_component.stamina)
return '' ## you can't use this
player_inventory.remove_from_inventory(player_inventory.primary_selection) player_inventory.remove_from_inventory(player_inventory.primary_selection)
return state_to_item_map[_item_name] return state_to_item_map[_item_name]
return '' return ''
func secondary_item() -> String: func use_secondary_item() -> String:
if player_inventory.secondary_selection != null: # Have to make sure we even have a second if player_inventory.secondary_selection != null: # Have to make sure we even have a second
var _item_name :String = player_inventory.secondary_selection.name var _item_name :String = player_inventory.secondary_selection.name
if state_to_item_map.has(_item_name): if state_to_item_map.has(_item_name):
player_inventory.remove_from_inventory(player_inventory.secondary_selection) var state_name = state_to_item_map[_item_name]
return state_to_item_map[_item_name] if enough_stamina_for(state_name):
player_inventory.remove_from_inventory(player_inventory.secondary_selection)
return state_to_item_map[_item_name]
else:
## TODO: maybe some sort of 'fail' animation state
return ''
return '' return ''
#func check_state_change(_new_state_name: String) -> bool: #func check_state_change(_new_state_name: String) -> bool:
@ -133,5 +160,7 @@ func secondary_item() -> String:
func _on_Movement_StateMachine_state_changed(old_state_name, new_state): func _on_Movement_StateMachine_state_changed(old_state_name, new_state):
if new_state.name == "jump": # if new_state.name == "jump":
stamina_component.reduce_stamina(state_stamina_cost["jump"]) # stamina_component.reduce_stamina(state_stamina_cost["jump"])
if state_stamina_cost.has(new_state.name):
stamina_component.reduce_stamina(state_stamina_cost[new_state.name])

View File

@ -138,6 +138,7 @@ max_health = 100
[node name="Stamina_Component" parent="." index="9" instance=ExtResource( 29 )] [node name="Stamina_Component" parent="." index="9" instance=ExtResource( 29 )]
unique_name_in_owner = true unique_name_in_owner = true
max_stamina = 50 max_stamina = 50
recovery_rate = 5
[node name="Hurtbox_Component" parent="." index="10" instance=ExtResource( 16 )] [node name="Hurtbox_Component" parent="." index="10" instance=ExtResource( 16 )]
collision_layer = 16 collision_layer = 16