From 85653ba634ec4348f542fc1aa96c10de8962c47a Mon Sep 17 00:00:00 2001 From: Dustin Date: Sun, 30 Mar 2025 18:26:20 -0700 Subject: [PATCH] Ladder climbing --- lib/classes/DEPR_state_modifier.gd | 133 ------------------ lib/classes/DEPR_state_modifier_properties.gd | 28 ---- lib/classes/state_animated_actor.gd | 4 +- project.godot | 12 -- .../PlayerE-AnimatedSprite_StateReceiver.gd | 14 ++ .../playerE/PlayerE-Movement_StateReceiver.gd | 54 ++++++- src/actors/players/playerE/PlayerE.tscn | 29 ++-- src/actors/players/playerE/states/climb.tres | 16 +++ src/levels/TestBox.tscn | 25 +++- 9 files changed, 129 insertions(+), 186 deletions(-) delete mode 100644 lib/classes/DEPR_state_modifier.gd delete mode 100644 lib/classes/DEPR_state_modifier_properties.gd create mode 100644 src/actors/players/playerE/states/climb.tres diff --git a/lib/classes/DEPR_state_modifier.gd b/lib/classes/DEPR_state_modifier.gd deleted file mode 100644 index cb3159d..0000000 --- a/lib/classes/DEPR_state_modifier.gd +++ /dev/null @@ -1,133 +0,0 @@ -class_name DumbOldStateModifier -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_state: bool = false - -## These should be the original properties set when the modifier is created. -# property changes can be applied over time but these are the instantiated values. -var modifier_properties: ModifierProperties - -## Animation modifier variables -## The name of the animation that could be applied and starting frame -var animation_name: String -var starting_frame: int = 0 -##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 = '' - -# 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 state_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 - -## Can't do it this way. -#func copy_modifier() -> StateModifier: -# var new_modifier = StateModifier.new() -# new_modifier.ready(animation_name, modifier_type, timeout_seconds) -# -# return StateModifier.new() -# -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 - # Can't support timeouts or animation stuff right now. - #timeout_seconds = merging_mod.timeout_seconds -# move_speed = merging_mod.move_speed -# gravity = merging_mod.gravity -# move_speed_modifier = merging_mod.move_speed_modifier -# move_speed_modifier_decay = merging_mod.move_speed_modifier_decay - - -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 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/DEPR_state_modifier_properties.gd b/lib/classes/DEPR_state_modifier_properties.gd deleted file mode 100644 index 28f131c..0000000 --- a/lib/classes/DEPR_state_modifier_properties.gd +++ /dev/null @@ -1,28 +0,0 @@ -class_name DEPR_ModifierProperties -extends Reference - -var modifier_type: int = 0 -#var timeout_seconds: float = 0.0 - -var move_speed: float = 0.0 -var move_acceleration: float = 0.0 -var move_speed_modifier: float = 0.0 -var move_modifier_move_acceleration: float = 0.0 -var jerk_factor: float = 0 - -var gravity:int = 0 - -# Bad name. Just means that it is intended to apply in a certain direction -# (Positive or negative) not an offset of the existing values. -var directional_modifier: bool = false - - -func _init( 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): - #timeout_seconds = p_timeout_seconds - move_speed = p_move_speed - move_acceleration = p_move_acceleration - gravity = p_gravity - move_speed_modifier = p_move_speed_modifier - move_modifier_move_acceleration = p_move_modifier_move_acceleration - jerk_factor = p_jerk_factor diff --git a/lib/classes/state_animated_actor.gd b/lib/classes/state_animated_actor.gd index b909f5c..3ce39f9 100644 --- a/lib/classes/state_animated_actor.gd +++ b/lib/classes/state_animated_actor.gd @@ -24,8 +24,9 @@ var gravity: int = ProjectSettings.get_setting("physics/2d/default_gravity") #var adjusted_move_speed: float var jerk: float - +## Variables intended to allow state receivers to communicate var animation_finished: bool = false +var movement_stopped: bool = false # Not sure if this should be here. probably not export(Array, String) var animation_sequence @@ -35,6 +36,7 @@ func enter() -> void: # Call parent class enter .enter() animation_finished = false + movement_stopped = false jerk = 0 return diff --git a/project.godot b/project.godot index 105718d..a2e1ff4 100644 --- a/project.godot +++ b/project.godot @@ -19,16 +19,6 @@ _global_script_classes=[ { "language": "GDScript", "path": "res://lib/classes/animated_sprite_state_receiver.gd" }, { -"base": "Reference", -"class": "DEPR_ModifierProperties", -"language": "GDScript", -"path": "res://lib/classes/DEPR_state_modifier_properties.gd" -}, { -"base": "Reference", -"class": "DumbOldStateModifier", -"language": "GDScript", -"path": "res://lib/classes/DEPR_state_modifier.gd" -}, { "base": "", "class": "GitAPI", "language": "NativeScript", @@ -117,8 +107,6 @@ _global_script_classes=[ { _global_script_class_icons={ "Actor": "", "AnimatedSprite_StateReceiver": "", -"DEPR_ModifierProperties": "", -"DumbOldStateModifier": "", "GitAPI": "", "HealthComponent": "", "HealthPickup": "", diff --git a/src/actors/players/playerE/PlayerE-AnimatedSprite_StateReceiver.gd b/src/actors/players/playerE/PlayerE-AnimatedSprite_StateReceiver.gd index 4602372..3f1cb29 100644 --- a/src/actors/players/playerE/PlayerE-AnimatedSprite_StateReceiver.gd +++ b/src/actors/players/playerE/PlayerE-AnimatedSprite_StateReceiver.gd @@ -54,6 +54,20 @@ func _on_state_change_fall(): change_animation(2) stop() +var climb_latch:bool = false +func _state_process_climb(): + UiManager.debug_text = str(frame) + if current_state.movement_stopped == true and climb_latch == false: + stop() + if frame != 0 and (frame + 1) >= frames.get_frame_count(animation): + frame = 0 + else: + frame = frame + 1 + climb_latch = true + if current_state.movement_stopped == false and climb_latch == true: + play() + climb_latch = false + #func _on_state_change_ledge_grab(): # change_animation(1) # stop() diff --git a/src/actors/players/playerE/PlayerE-Movement_StateReceiver.gd b/src/actors/players/playerE/PlayerE-Movement_StateReceiver.gd index fd3b962..47ff109 100644 --- a/src/actors/players/playerE/PlayerE-Movement_StateReceiver.gd +++ b/src/actors/players/playerE/PlayerE-Movement_StateReceiver.gd @@ -77,7 +77,14 @@ func wants_roll() -> bool: _wants_roll = false return false - +var _wants_climb :bool +func wants_climb() -> bool: + if Input.is_action_pressed("ui_up"): + _wants_climb = true + return true + else: + _wants_climb = false + return false #TODO: This is probably not a good way to do this. # we want to check input once and make decisions based on @@ -102,6 +109,7 @@ func process_physics_input(delta): wants_attack_secondary() wants_dash() wants_roll() + wants_climb() func flip_sprite_to_movement_direction(): if desired_movement_vector.x != 0.0: @@ -129,6 +137,9 @@ func _state_process_physics_idle(): if _wants_attack_secondary == true: request_state_change.call_func('attack_sword') + + if $"../LadderDetector".is_colliding() and _wants_climb == true: + request_state_change.call_func('climb') func _state_process_physics_attack_shoot(): if current_state.animation_finished == true: @@ -253,6 +264,47 @@ func _state_process_physics_move(): move_actor_as_desired() +func get_climb_shape_location() -> Vector2: + if $"../LadderDetector".is_colliding(): + var point: Vector2 = $"../LadderDetector".get_collision_point() + var shape_id = $"../LadderDetector".get_collider_shape() + var object = $"../LadderDetector".get_collider() + + # This appears to get the shapes location! + var area = Physics2DServer.area_get_transform(object) + var areashape = Physics2DServer.area_get_shape_transform(object, shape_id) + + var y_dir = Input.get_axis("ui_up" , "ui_down") + var global_origin = areashape.origin + area.origin + return Vector2(global_origin.x, y_dir) + else: + return Vector2(-1,-1) + + +func _state_process_physics_climb(): + #$"../LadderDetector".set_enabled(false) + + var ladder_location = get_climb_shape_location() + if ladder_location != Vector2(-1,-1): + #UiManager.debug_text = str(ladder_location) + '\n' + str(ladder_location - parent.global_position) #+ '\n' + str(parent.global_position.distance_to(ladder_location)) + # An attempt to snap to the ladder location + if parent.global_position.x != round(ladder_location.x): + parent.global_translate(Vector2(round((ladder_location.x - parent.global_position.x)),(ladder_location.y * current_state.horizontal_speed) * physics_delta)) + if ladder_location.y != 0: + #print("singal that I'm climbing?") + current_state.movement_stopped = false + else: + #print("singal that I'm not climbing?") + current_state.movement_stopped = true + # Make sure we hopped the step index +# if animation_index > 0: +# animations.stop() + else: + return request_state_change.call_func('idle') + + if _wants_crouch: + request_state_change.call_func('fall') + func _state_process_physics_ledge_grab(): UiManager.debug_text = (str(velocity)) $"../LedgeDetector".set_enabled(false) diff --git a/src/actors/players/playerE/PlayerE.tscn b/src/actors/players/playerE/PlayerE.tscn index faec529..04c43c0 100644 --- a/src/actors/players/playerE/PlayerE.tscn +++ b/src/actors/players/playerE/PlayerE.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=26 format=2] +[gd_scene load_steps=27 format=2] [ext_resource path="res://lib/templates/Actor/ActorTemplate.tscn" type="PackedScene" id=1] [ext_resource path="res://assets/actors/players/playerE/PlayerE_SpriteFrames.tres" type="SpriteFrames" id=2] @@ -22,6 +22,7 @@ [ext_resource path="res://src/actors/players/playerE/states/ledge_climb.tres" type="Resource" id=20] [ext_resource path="res://lib/templates/Interactable/Interactable_Receiver.gd" type="Script" id=21] [ext_resource path="res://src/actors/players/playerE/states/enter_right.tres" type="Resource" id=22] +[ext_resource path="res://src/actors/players/playerE/states/climb.tres" type="Resource" id=23] [sub_resource type="Resource" id=3] resource_local_to_scene = true @@ -50,7 +51,7 @@ player_number = 1 [node name="Movement_StateMachine" parent="." index="0"] starting_state_name = "idle" -states = [ ExtResource( 4 ), ExtResource( 9 ), ExtResource( 7 ), ExtResource( 8 ), ExtResource( 12 ), ExtResource( 11 ), ExtResource( 10 ), ExtResource( 13 ), ExtResource( 14 ), ExtResource( 15 ), SubResource( 3 ), ExtResource( 19 ), ExtResource( 20 ), ExtResource( 22 ) ] +states = [ ExtResource( 4 ), ExtResource( 9 ), ExtResource( 7 ), ExtResource( 8 ), ExtResource( 12 ), ExtResource( 11 ), ExtResource( 10 ), ExtResource( 13 ), ExtResource( 14 ), ExtResource( 15 ), SubResource( 3 ), ExtResource( 19 ), ExtResource( 20 ), ExtResource( 22 ), ExtResource( 23 ) ] [node name="AnimatedSprite_StateReceiver" parent="." index="1"] frames = ExtResource( 2 ) @@ -75,24 +76,32 @@ script = ExtResource( 6 ) [node name="LedgeDetector" type="RayCast2D" parent="." index="3"] modulate = Color( 0, 1, 0, 1 ) -position = Vector2( 8, 5 ) +position = Vector2( 8, -10 ) enabled = true -cast_to = Vector2( 1.36422e-12, 1 ) +cast_to = Vector2( 1.36422e-12, -1 ) [node name="WallDetector" type="RayCast2D" parent="." index="4"] modulate = Color( 0, 1, 0, 1 ) -position = Vector2( 6, 10 ) +position = Vector2( 6, -16 ) enabled = true -cast_to = Vector2( 4, -4 ) +cast_to = Vector2( 4, 4 ) -[node name="CollisionShape2D" type="CollisionShape2D" parent="." index="6"] +[node name="LadderDetector" type="RayCast2D" parent="." index="5"] +modulate = Color( 0, 1, 0, 1 ) +enabled = true +cast_to = Vector2( 1.36422e-12, 20 ) +collision_mask = 1024 +collide_with_areas = true +collide_with_bodies = false + +[node name="CollisionShape2D" type="CollisionShape2D" parent="." index="7"] position = Vector2( 0, 2 ) shape = SubResource( 1 ) -[node name="Health_Component" parent="." index="7" instance=ExtResource( 17 )] +[node name="Health_Component" parent="." index="8" instance=ExtResource( 17 )] max_health = 100 -[node name="Hurtbox_Component" parent="." index="8" instance=ExtResource( 16 )] +[node name="Hurtbox_Component" parent="." index="9" instance=ExtResource( 16 )] collision_layer = 16 collision_mask = 128 hurtbox_entered_function = "hit_Receiver" @@ -102,6 +111,6 @@ modulate = Color( 0, 0, 1, 1 ) position = Vector2( -1, 2 ) shape = SubResource( 2 ) -[node name="Interactable_Receiver" type="Node" parent="." index="9"] +[node name="Interactable_Receiver" type="Node" parent="." index="10"] script = ExtResource( 21 ) interactable_parent_callback = "touch_the_thing" diff --git a/src/actors/players/playerE/states/climb.tres b/src/actors/players/playerE/states/climb.tres new file mode 100644 index 0000000..9eeffe9 --- /dev/null +++ b/src/actors/players/playerE/states/climb.tres @@ -0,0 +1,16 @@ +[gd_resource type="Resource" load_steps=2 format=2] + +[ext_resource path="res://lib/classes/state_animated_actor.gd" type="Script" id=1] + +[resource] +resource_local_to_scene = true +script = ExtResource( 1 ) +debug_state = false +timeout_seconds = 0.0 +name = "climb" +horizontal_speed = 20.0 +horizontal_acceleration = 0.0 +horizontal_speed_offset = 0.0 +horizontal_speed_offset_acceleration = 0.0 +jerk_factor = 0.0 +animation_sequence = [ "climb" ] diff --git a/src/levels/TestBox.tscn b/src/levels/TestBox.tscn index ca4fc62..2742139 100644 --- a/src/levels/TestBox.tscn +++ b/src/levels/TestBox.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=7 format=2] +[gd_scene load_steps=8 format=2] [ext_resource path="res://src/templates/level_templates/LevelTemplate.tscn" type="PackedScene" id=1] [ext_resource path="res://assets/levels/LegacyFantasy-High_Forest/tileset.tres" type="TileSet" id=2] @@ -11,6 +11,9 @@ radius = 5.09902 [sub_resource type="RectangleShape2D" id=2] extents = Vector2( 4.5, 10 ) +[sub_resource type="RectangleShape2D" id=3] +extents = Vector2( 8, 40 ) + [node name="TestBox" instance=ExtResource( 1 )] [node name="TileMap Collision" parent="." index="1"] @@ -49,3 +52,23 @@ _destination_position_name = "EnterLeft" modulate = Color( 1, 0, 1, 1 ) position = Vector2( 316, 121 ) shape = SubResource( 2 ) + +[node name="Ladder" type="Area2D" parent="." index="6"] +position = Vector2( 208, 64 ) +collision_layer = 1024 +collision_mask = 0 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="Ladder" index="0"] +position = Vector2( 8, 40 ) +shape = SubResource( 3 ) +__meta__ = { +"_edit_lock_": true +} + +[node name="ColorRect" type="ColorRect" parent="Ladder" index="1"] +margin_right = 16.0 +margin_bottom = 80.0 +color = Color( 0.45098, 0.192157, 0.192157, 1 ) +__meta__ = { +"_edit_lock_": true +}