From d9b5d2785545a54d64da7dfafa8faec2383b07ce Mon Sep 17 00:00:00 2001 From: Dustin Date: Sun, 23 Mar 2025 00:15:31 -0700 Subject: [PATCH] State modifier progress. --- lib/classes/animated_sprite_state_receiver.gd | 6 + lib/classes/state.gd | 3 + lib/classes/state_animated_actor.gd | 5 - lib/classes/state_machine.gd | 17 ++- lib/classes/state_modifier.gd | 130 ++++++++++++++++++ lib/classes/state_modifier_animation.gd | 26 ++++ lib/classes/state_modifier_movement.gd | 14 ++ project.godot | 20 ++- .../PlayerE-AnimatedSprite_StateReceiver.gd | 11 +- 9 files changed, 220 insertions(+), 12 deletions(-) create mode 100644 lib/classes/state_modifier.gd create mode 100644 lib/classes/state_modifier_animation.gd create mode 100644 lib/classes/state_modifier_movement.gd diff --git a/lib/classes/animated_sprite_state_receiver.gd b/lib/classes/animated_sprite_state_receiver.gd index 71b6fe6..03b0fcf 100644 --- a/lib/classes/animated_sprite_state_receiver.gd +++ b/lib/classes/animated_sprite_state_receiver.gd @@ -11,6 +11,7 @@ onready var current_state = StateAnimatedActor.new() #var animation_finished: bool = false var request_state_change: FuncRef +var add_state_modifier: FuncRef # this just wouldn't work well. Maybe I can try a resource # export(Array, NodePath) var transition_state_nodes @@ -32,6 +33,8 @@ var previous_speed_multiplier: float # Called when the node enters the scene tree for the first time. func _ready(): request_state_change = funcref(get_node(callable_state_machine), 'change_to_known_state') + add_state_modifier = funcref(get_node(callable_state_machine),'push_state_modifier') + get_node(callable_state_machine).connect("state_changed", self,"_on_state_change") self.connect("animation_finished",self,"_on_animation_finished") @@ -85,6 +88,9 @@ func change_animation(enter_frame := 0, index := 0, suffix := ''): # if modifier.state_timeout and modifier.timeout_seconds == 0: # modifier = null + if current_state.modifier: + print("hey you got one! -> ", current_state.modifier.name) + # By Default, we build an animation sequence off of this state's before checking # for modifiers var animation_sequence = current_state.animation_sequence diff --git a/lib/classes/state.gd b/lib/classes/state.gd index b023e61..440faab 100644 --- a/lib/classes/state.gd +++ b/lib/classes/state.gd @@ -12,6 +12,8 @@ signal state_exited() var state_timeout: Timer var state_ready: bool = true +var modifier: StateModifier + export var name: String # Called when the node enters the scene tree for the first time. @@ -55,6 +57,7 @@ func enter() -> void: func exit() -> void: emit_signal("state_exited") + modifier = null return # Disable to test whether actually needed. diff --git a/lib/classes/state_animated_actor.gd b/lib/classes/state_animated_actor.gd index f073f0e..b909f5c 100644 --- a/lib/classes/state_animated_actor.gd +++ b/lib/classes/state_animated_actor.gd @@ -30,11 +30,6 @@ var animation_finished: bool = false # Not sure if this should be here. probably not export(Array, String) var animation_sequence -## Modifiers may not be needed anymore. -#var modifier: StateModifier -#var physics_modifier :StateModifier - - func enter() -> void: # Call parent class enter diff --git a/lib/classes/state_machine.gd b/lib/classes/state_machine.gd index e9ab48d..d43cc0c 100644 --- a/lib/classes/state_machine.gd +++ b/lib/classes/state_machine.gd @@ -62,11 +62,15 @@ func change_state(new_state: State) -> void: current_state = new_state current_state.enter() + + if(state_modifiers.size() > 0): + print("Active Modifiers:") + for mods in state_modifiers: + if mods is AnimationStateModifier: + print(mods.name, "asm") + current_state.modifier = mods #mods.copy_AnimationStateModifier(AnimationStateModifier.new()) + emit_signal("state_changed",current_state_name,current_state) -# if(state_modifiers.size() > 0 and debug_state_machine): -# print("Active Modifiers:") -# for mods in state_modifiers: -# print(mods.name) func change_to_known_state(new_state_name: String) -> void: if new_state_name.empty() == false: @@ -82,6 +86,11 @@ func get_state_reference(state_name: String) -> State: return states_index[state_name] return null +func push_state_modifier(_state_modifier: StateModifier) -> void: + if state_modifiers.has(_state_modifier) == false: + state_modifiers.push_front(_state_modifier) + print("Add State Modifier: ", _state_modifier.name, " now size ", state_modifiers.size()) + # Disable to test whether actually needed. ## Pass through functions for the Player to call, ## handling state changes as needed. diff --git a/lib/classes/state_modifier.gd b/lib/classes/state_modifier.gd new file mode 100644 index 0000000..f12230c --- /dev/null +++ b/lib/classes/state_modifier.gd @@ -0,0 +1,130 @@ +class_name StateModifier +extends Reference +## State modification +## +## A state modifier doesn't have any direct control of a game object. +## They are transfered from the enter and exit of other states and +## influence the movement or animation of their parent states. +## Rather than going full into paralell state machines I hope that this +## modifier will be suitable for a basic state machine. +## +## @WIP + +enum TYPE {NONE,EXIT_ANIMATION, ANIMATION_SUFFIX, REPLACE_ANIMATION} + +var debug: bool = false + +##TODO: Animation speed + +## Modification Type +## if an animation is specified, the default behavior will be just to override +## the state animation. Or perform a 'Replace Animation' +var modifier_type = TYPE.NONE + +var name :String = '' +var ready :bool = false + +# Move Speed in Pixels Per Second +# These should only apply if Modification Type Set to None. +# (But I'm not sure if I like that model.) +#var move_speed: float +#var move_speed_modifier: float = 0 +#var move_speed_modifier_decay: float = 0 + +#var gravity: int = ProjectSettings.get_setting("physics/2d/default_gravity") + + +# Attempting to use this as an animation suffix that can linger after +# state transition like 'run-fire' to 'idle-fire' 'jump-fire' etc. +#var animation_suffix: bool = false +#var pre_append_animation: bool = false + +# not sure if I can do the array thing from state change. Maybe? +# disabling this sequencing for now. +# export(Array, String) var animation_sequence +# var animation_index: int = 0 +# var current_animation_sequence: int = 0 + + +#var animation_sequence_timer: Timer + +var timeout: Timer +var timeout_seconds: float = 0.0 + +# Meant to be called on timeout or other counters this modifier may have. +# Should be updated whenever modifier owner transfers +var state_call_function: FuncRef + +#func merge_modifier(merging_mod: ModifierProperties): +# if merging_mod.modifier_type != TYPE.NONE: +# print("Warning Merging modifier types that aren't set to 'none' is not supported!") +# return + +#func get_modifier_properties() -> ModifierProperties: +# #( p_move_speed:float, p_gravity:int , p_move_acceleration , p_move_speed_modifier:float, +# # p_move_modifier_move_acceleration:float, p_jerk_factor: float): +# var mp = ModifierProperties.new( modifier_properties.move_speed, +# modifier_properties.gravity, +# modifier_properties.move_acceleration, +# modifier_properties.move_speed_modifier, +# modifier_properties.move_modifier_move_acceleration, +# modifier_properties.jerk_factor) +# mp.directional_modifier = modifier_properties.directional_modifier +# return mp + +func setup(modifier_name:String, type = TYPE.NONE,_timeout_seconds : float = 0): + name = modifier_name + modifier_type = type + + if _timeout_seconds > 0: + timeout = Timer.new() + timeout.wait_time = _timeout_seconds + timeout.one_shot = true + timeout.autostart = false + +func copy(_copy_state: StateModifier): + _copy_state.name = name + _copy_state.modifier_type = modifier_type + +func merge(_copy_state: StateModifier): + pass + + +#func ready(modifier_name:String, anim_name:String = '', type = TYPE.NONE, timeout : float = 0, parent:Node = null, function_name : String = "") -> Timer: +# name = modifier_name +# # If somebody forgot to specify the type +# if anim_name != '' and type == TYPE.NONE: +# modifier_type = TYPE.REPLACE_ANIMATION +# animation_name = anim_name +# modifier_type = type +## if parent != null and function_name != "": +## state_call_function = funcref(parent, function_name) +# +## modifier_properties = ModifierProperties.new(0,0,0,0,0,0) +# +# if timeout > 0: +# state_timeout = Timer.new() +# state_timeout.wait_time = timeout +# state_timeout.one_shot = true +# state_timeout.autostart = false +# return state_timeout +# #add_child(state_timeout) +# #ModifierProperties.new() +# return null + + +## Transfer and callback related methods. These sort of worked but I didn't end up using them +## They're an interesting idea though. +#func transfer_owner(parent:Node): +# print("Modifier owner will now be: ", parent.name) +# state_call_function = funcref(parent, 'update_animation') + +func _on_Timer_timeout(): + #print("Oh Crap! I can't beleive it worked") + if state_call_function: + if state_call_function.is_valid(): + state_call_function.call_func() + else: + print("Warning! Modifier timed out with no callback to alert.") + else: + print("Warning! no function applies.") diff --git a/lib/classes/state_modifier_animation.gd b/lib/classes/state_modifier_animation.gd new file mode 100644 index 0000000..5fbc2b6 --- /dev/null +++ b/lib/classes/state_modifier_animation.gd @@ -0,0 +1,26 @@ +class_name AnimationStateModifier +extends StateModifier + + +## Animation modifier variables +## The name of the animation that could be applied and starting frame +var animation_name: String = '' +var animation_starting_frame: int = 0 +var animation_speed: float + +func copy_AnimationStateModifier(_copy_state: AnimationStateModifier): + .copy(_copy_state) + _copy_state.animation_name = animation_name + _copy_state.animation_starting_frame = animation_starting_frame + _copy_state.animation_speed = animation_speed + + +# This is probably not necessary just access properties directly +#func animation_mod_setup( anim_name:String = ''): +# # If somebody forgot to specify the type +# if anim_name != '' and modifier_type == TYPE.NONE: +# modifier_type = TYPE.REPLACE_ANIMATION +# animation_name = anim_name +## if parent != null and function_name != "": +## state_call_function = funcref(parent, function_name) + diff --git a/lib/classes/state_modifier_movement.gd b/lib/classes/state_modifier_movement.gd new file mode 100644 index 0000000..970e315 --- /dev/null +++ b/lib/classes/state_modifier_movement.gd @@ -0,0 +1,14 @@ +class_name MovementStateModifier +extends StateModifier + + +var horizontal_speed: float = 0 +var horizontal_acceleration: float = 0 +## Movement Speed Offsets (Positive or Negative) +var horizontal_speed_offset: float = 0 +## Applies only if offset applies +var horizontal_speed_offset_acceleration: float = 0 +var jerk_factor: float = 0 + +var gravity: int = 0 + diff --git a/project.godot b/project.godot index a4baa00..e23c091 100644 --- a/project.godot +++ b/project.godot @@ -19,6 +19,11 @@ _global_script_classes=[ { "language": "GDScript", "path": "res://lib/classes/animated_sprite_state_receiver.gd" }, { +"base": "StateModifier", +"class": "AnimationStateModifier", +"language": "GDScript", +"path": "res://lib/classes/state_modifier_animation.gd" +}, { "base": "Reference", "class": "DEPR_ModifierProperties", "language": "GDScript", @@ -69,6 +74,11 @@ _global_script_classes=[ { "language": "GDScript", "path": "res://src/classes/movement_component.gd" }, { +"base": "StateModifier", +"class": "MovementStateModifier", +"language": "GDScript", +"path": "res://lib/classes/state_modifier_movement.gd" +}, { "base": "Node", "class": "Movement_StateReceiver", "language": "GDScript", @@ -93,10 +103,16 @@ _global_script_classes=[ { "class": "StateMachineAnimatedActor", "language": "GDScript", "path": "res://lib/classes/state_machine_animated_actor.gd" +}, { +"base": "Reference", +"class": "StateModifier", +"language": "GDScript", +"path": "res://lib/classes/state_modifier.gd" } ] _global_script_class_icons={ "Actor": "", "AnimatedSprite_StateReceiver": "", +"AnimationStateModifier": "", "DEPR_ModifierProperties": "", "DumbOldStateModifier": "", "GitAPI": "", @@ -107,11 +123,13 @@ _global_script_class_icons={ "Level": "", "Modifier_Receiver": "", "MovementComponent": "", +"MovementStateModifier": "", "Movement_StateReceiver": "", "State": "", "StateAnimatedActor": "", "StateMachine": "", -"StateMachineAnimatedActor": "" +"StateMachineAnimatedActor": "", +"StateModifier": "" } [application] diff --git a/src/actors/players/playerE/PlayerE-AnimatedSprite_StateReceiver.gd b/src/actors/players/playerE/PlayerE-AnimatedSprite_StateReceiver.gd index 6ec3a9c..68d6fbc 100644 --- a/src/actors/players/playerE/PlayerE-AnimatedSprite_StateReceiver.gd +++ b/src/actors/players/playerE/PlayerE-AnimatedSprite_StateReceiver.gd @@ -1,5 +1,7 @@ extends AnimatedSprite_StateReceiver +var attack_sword: AnimationStateModifier + func _ready(): # Lets see if this nutty thing works var state_ref :State @@ -7,8 +9,11 @@ func _ready(): #state_ref.connect("state_entered", self, "_on_state_entered_fall") state_ref = get_node(callable_state_machine).get_state_reference('attack_sword') state_ref.connect("state_exited", self, "_on_state_exited_attack_sword") - - pass + + attack_sword = AnimationStateModifier.new() + attack_sword.setup('sword_drawn',StateModifier.TYPE.ANIMATION_SUFFIX,5.0) + add_child(attack_sword.timeout) + add_state_modifier.call_func(attack_sword) # I don't need this right now but I proved it can be done! #func _state_process_idle(): @@ -46,3 +51,5 @@ func _on_state_change_fall(): func _on_state_exited_attack_sword(): print("you just swung your sword, you should get a modifier.") + attack_sword.timeout.start() +