Files
Papay-Shooter/movement/states/state_ground.gd
T

154 lines
5.3 KiB
GDScript

extends Node
class_name StateGround
var machine: MovementStateMachine
var params: MovementParams:
get: return machine.params
var _footstep_timer: float = 0.0
func enter(_data: Dictionary = {}) -> void:
machine.on_ground = true
machine.current_jump_count = 0
# Clear wall tilt on landing
var rig = _get_camera_rig()
if rig:
rig.clear_wall_tilt()
# Ensure capsule is correct when entering ground state
_update_capsule_height()
func exit() -> void:
pass
func update(delta: float) -> void:
var player := machine.player
var vel: Vector3 = player.velocity
# ── Jump (coyote time + jump buffer) ──────────────────────────────────
if machine.input_jump_just_pressed and machine.coyote_timer > 0.0 and machine.jump_cooldown_timer <= 0.0:
player.velocity.y = params.jump_velocity
machine.current_jump_count = 1
machine.on_ground = false
machine.coyote_timer = 0.0
machine.register_chain_mechanic("jump")
machine.jump_cooldown_timer = params.jump_cooldown
if player.jump_player:
player.jump_player.play()
machine.switch_to("air")
return
# Jump buffer: player pressed jump just before landing
if machine.on_ground and machine.jump_buffer_time > 0.0 and machine.jump_cooldown_timer <= 0.0:
player.velocity.y = params.jump_velocity
machine.current_jump_count = 1
machine.on_ground = false
machine.jump_buffer_time = 0.0
machine.register_chain_mechanic("jump")
machine.jump_cooldown_timer = params.jump_cooldown
if player.jump_player:
player.jump_player.play()
machine.switch_to("air")
return
# ── Slide (crouch while moving fast enough) ───────────────────────────
if machine.input_crouch and machine.slide_cooldown_timer <= 0.0:
var hspeed := Vector3(player.velocity.x, 0.0, player.velocity.z).length()
if hspeed > params.slide_min_speed:
machine.switch_to("slide")
return
# ── Determine target speed ────────────────────────────────────────────
var wish_dir: Vector3 = machine.wish_dir_world
var speed := params.walk_speed
if machine.input_crouch:
speed = params.crouch_speed
var effective_speed := machine.get_effective_speed(speed)
# ── Horizontal movement (exponential interpolation for smoothness) ───
var hvel := Vector3(vel.x, 0.0, vel.z)
var target_vel := wish_dir * effective_speed
if wish_dir.length_squared() > 0.0:
hvel = hvel.lerp(target_vel, 1.0 - exp(-params.ground_acceleration * delta))
else:
hvel = hvel.lerp(Vector3.ZERO, 1.0 - exp(-params.ground_deceleration * delta))
vel.x = hvel.x
vel.z = hvel.z
# ── Gravity (correct direction: downward) ─────────────────────────────
if not player.is_on_floor():
vel.y -= params.gravity * delta
else:
# Snap to floor: small downward velocity keeps is_on_floor() stable
# ONLY if we aren't being launched upward by an impulse!
if vel.y <= 0.0:
vel.y = -0.5
player.velocity = vel
player.move_and_slide()
_update_capsule_height()
var current_hspeed = Vector2(player.velocity.x, player.velocity.z).length()
if current_hspeed > 1.0:
_footstep_timer -= delta
if _footstep_timer <= 0.0:
if player.footstep_player:
player.footstep_player.play()
_footstep_timer = max(0.2, 3.0 / current_hspeed)
else:
_footstep_timer = 0.0
# ── Update ground status ──────────────────────────────────────────────
if player.is_on_floor():
machine.on_ground = true
machine.current_jump_count = 0
else:
machine.on_ground = false
# ── Launch or Falling off ledge → air state ───────────────────────────
if not machine.on_ground:
if player.velocity.y > 0.0 or machine.coyote_timer <= 0.0:
machine.switch_to("air")
return
# ── Wall run check ────────────────────────────────────────────────────
var wall_n := machine.detect_wall_horizontal()
if wall_n != Vector3.ZERO and machine.input_dir.length() > 0.1:
if not player.is_on_floor():
machine.switch_to("wall_run")
return
# ── Dash ──────────────────────────────────────────────────────────────
if machine.input_dash:
var hspeed := Vector3(player.velocity.x, 0.0, player.velocity.z).length()
if hspeed > 0.1 or machine.input_dir.length() > 0.1:
machine.switch_to("dash")
return
func _get_camera_rig():
if machine.player and machine.player.has_node("HeadPivot"):
return machine.player.get_node("HeadPivot")
return null
func _update_capsule_height() -> void:
var shape = _get_capsule()
if shape:
if machine.input_crouch:
shape.height = machine.original_capsule_height * 0.5
else:
shape.height = machine.original_capsule_height
func _get_capsule() -> CapsuleShape3D:
for child in machine.player.get_children():
if child is CollisionShape3D and child.shape is CapsuleShape3D:
return child.shape as CapsuleShape3D
return null