feat: implement movement state machine architecture and add various sound assets
This commit is contained in:
@@ -55,6 +55,12 @@ func _ready() -> void:
|
||||
grapple_state.set_script(load("res://movement/states/state_grapple.gd"))
|
||||
add_child(grapple_state)
|
||||
|
||||
# Ensure wall climb state is injected
|
||||
var wall_climb_state = Node.new()
|
||||
wall_climb_state.name = "state_wall_climb"
|
||||
wall_climb_state.set_script(load("res://movement/states/state_wall_climb.gd"))
|
||||
add_child(wall_climb_state)
|
||||
|
||||
for child in get_children():
|
||||
if child is Node and child.name.begins_with("state_"):
|
||||
states[child.name.replace("state_", "")] = child
|
||||
@@ -214,6 +220,9 @@ func detect_wall_horizontal() -> Vector3:
|
||||
ray.exclude = [player.get_rid()]
|
||||
var hit := space_state.intersect_ray(ray)
|
||||
if not hit.is_empty():
|
||||
var collider = hit.get("collider")
|
||||
if collider is PlayerMovementController:
|
||||
continue
|
||||
var n: Vector3 = hit.get("normal", Vector3.ZERO)
|
||||
# Wall must be roughly vertical (normal mostly horizontal)
|
||||
if absf(n.y) < 0.3 and n.length_squared() > 0.0:
|
||||
@@ -232,6 +241,9 @@ func detect_wall_horizontal() -> Vector3:
|
||||
ray.exclude = [player.get_rid()]
|
||||
var hit := space_state.intersect_ray(ray)
|
||||
if not hit.is_empty():
|
||||
var collider = hit.get("collider")
|
||||
if collider is PlayerMovementController:
|
||||
continue
|
||||
var n: Vector3 = hit.get("normal", Vector3.ZERO)
|
||||
if absf(n.y) < 0.3 and n.length_squared() > 0.0:
|
||||
wall_normal = n.normalized()
|
||||
@@ -242,6 +254,62 @@ func detect_wall_horizontal() -> Vector3:
|
||||
wall_side = 0.0
|
||||
return Vector3.ZERO
|
||||
|
||||
## Utility: detect wall directly in front of the player.
|
||||
## Useful for wall climbing and vaulting over short walls.
|
||||
func detect_wall_forward() -> Dictionary:
|
||||
if not player:
|
||||
return {"hit": false, "normal": Vector3.ZERO, "is_short": false}
|
||||
|
||||
var hvel := Vector3(player.velocity.x, 0.0, player.velocity.z)
|
||||
var move_dir := Vector3.ZERO
|
||||
if hvel.length_squared() > 0.1:
|
||||
move_dir = hvel.normalized()
|
||||
else:
|
||||
move_dir = -player.global_transform.basis.z
|
||||
move_dir.y = 0.0
|
||||
if move_dir.length_squared() > 0.01:
|
||||
move_dir = move_dir.normalized()
|
||||
|
||||
if move_dir.length_squared() < 0.1:
|
||||
return {"hit": false, "normal": Vector3.ZERO, "is_short": false}
|
||||
|
||||
var space_state := player.get_world_3d().direct_space_state
|
||||
var dist := params.wall_detect_distance
|
||||
|
||||
# Lower ray (feet/knees)
|
||||
var origin_low := player.global_position + Vector3.UP * params.wall_ray_down_height
|
||||
var ray_low := PhysicsRayQueryParameters3D.create(origin_low, origin_low + move_dir * dist)
|
||||
ray_low.exclude = [player.get_rid()]
|
||||
var hit_low := space_state.intersect_ray(ray_low)
|
||||
|
||||
# Mid ray (chest/head)
|
||||
var origin_mid := player.global_position + Vector3.UP * params.wall_ray_up_height
|
||||
var ray_mid := PhysicsRayQueryParameters3D.create(origin_mid, origin_mid + move_dir * dist)
|
||||
ray_mid.exclude = [player.get_rid()]
|
||||
var hit_mid := space_state.intersect_ray(ray_mid)
|
||||
|
||||
# If both lower rays miss, there is no wall directly in front
|
||||
if hit_low.is_empty() and hit_mid.is_empty():
|
||||
return {"hit": false, "normal": Vector3.ZERO, "is_short": false}
|
||||
|
||||
# Get the normal from the highest point we hit, or the lowest if we only hit low
|
||||
var best_hit = hit_mid if not hit_mid.is_empty() else hit_low
|
||||
|
||||
if best_hit.get("collider") is PlayerMovementController:
|
||||
return {"hit": false, "normal": Vector3.ZERO, "is_short": false}
|
||||
|
||||
var normal: Vector3 = best_hit.get("normal", Vector3.ZERO)
|
||||
if absf(normal.y) >= 0.3:
|
||||
return {"hit": false, "normal": Vector3.ZERO, "is_short": false} # Not a vertical wall
|
||||
|
||||
# Check if wall is short (ledge check) using a top ray
|
||||
var origin_high := player.global_position + Vector3.UP * (params.wall_ray_up_height + params.wall_climb_vault_height_check)
|
||||
var ray_high := PhysicsRayQueryParameters3D.create(origin_high, origin_high + move_dir * dist)
|
||||
ray_high.exclude = [player.get_rid()]
|
||||
var hit_high := space_state.intersect_ray(ray_high)
|
||||
|
||||
return {"hit": true, "normal": normal.normalized(), "is_short": hit_high.is_empty()}
|
||||
|
||||
|
||||
func _apply_crouch(crouched: bool) -> void:
|
||||
var shape_node = null
|
||||
|
||||
Reference in New Issue
Block a user