151 lines
5.3 KiB
GDScript
151 lines
5.3 KiB
GDScript
extends Node
|
|
class_name StateWallRun
|
|
|
|
var machine: MovementStateMachine
|
|
var params: MovementParams:
|
|
get: return machine.params
|
|
|
|
var elapsed: float = 0.0
|
|
var _run_speed: float = 0.0
|
|
var _last_vel: Vector3 = Vector3.ZERO
|
|
|
|
|
|
var _current_tangent: Vector3 = Vector3.ZERO
|
|
|
|
func enter(_data: Dictionary = {}) -> void:
|
|
elapsed = 0.0
|
|
machine.register_chain_mechanic("wall_run")
|
|
# Cap the upward momentum so they don't fly up the wall,
|
|
# but preserve some if they just jumped onto it.
|
|
machine.player.velocity.y = minf(machine.player.velocity.y, 1.5)
|
|
machine.on_ground = false
|
|
_last_vel = machine.player.velocity
|
|
|
|
if machine.player.wallrun_player and not machine.player.wallrun_player.playing:
|
|
machine.player.wallrun_player.play()
|
|
|
|
var hvel := Vector3(machine.player.velocity.x, 0.0, machine.player.velocity.z)
|
|
_current_tangent = machine.wall_normal.cross(Vector3.UP).normalized()
|
|
if hvel.dot(_current_tangent) < 0.0:
|
|
_current_tangent = -_current_tangent
|
|
|
|
# Preserve speed along the wall if it's faster than base wall_run_speed
|
|
var base_speed = machine.get_effective_speed(params.wall_run_speed)
|
|
var projected_speed = hvel.dot(_current_tangent)
|
|
_run_speed = maxf(base_speed, projected_speed)
|
|
|
|
# Camera tilt
|
|
var rig = _get_camera_rig()
|
|
if rig:
|
|
rig.set_wall_tilt(machine.wall_side)
|
|
|
|
|
|
func exit() -> void:
|
|
# Clear camera tilt
|
|
var rig = _get_camera_rig()
|
|
if rig:
|
|
rig.clear_wall_tilt()
|
|
|
|
if machine.player.wallrun_player:
|
|
machine.player.wallrun_player.stop()
|
|
|
|
|
|
func update(delta: float) -> void:
|
|
elapsed += delta
|
|
if elapsed > params.wall_run_duration:
|
|
machine.wall_normal = Vector3.ZERO
|
|
machine.wall_side = 0.0
|
|
machine.wall_cooldown_timer = 0.3
|
|
machine.switch_to("air")
|
|
return
|
|
|
|
var player := machine.player
|
|
var vel: Vector3 = player.velocity
|
|
|
|
if player.wallrun_player and not player.wallrun_player.playing:
|
|
player.wallrun_player.play()
|
|
|
|
if vel.distance_squared_to(_last_vel) > 100.0:
|
|
machine.wall_normal = Vector3.ZERO
|
|
machine.wall_side = 0.0
|
|
machine.switch_to("air")
|
|
return
|
|
|
|
# ── Gradual gravity pull (starts light, increases over time) ──────────
|
|
var gravity_factor := 0.1 + 0.9 * (elapsed / params.wall_run_duration)
|
|
vel.y -= params.wall_run_gravity * gravity_factor * delta
|
|
|
|
# ── Move along wall tangent ───────────────────────────────────────────
|
|
var wall_tangent := machine.wall_normal.cross(Vector3.UP).normalized()
|
|
# Ensure the new tangent aligns with our locked forward direction
|
|
if _current_tangent.dot(wall_tangent) < 0.0:
|
|
wall_tangent = -wall_tangent
|
|
_current_tangent = wall_tangent
|
|
|
|
vel.x = wall_tangent.x * _run_speed
|
|
vel.z = wall_tangent.z * _run_speed
|
|
|
|
# ── Look away to break wall run ───────────────────────────────────────
|
|
var look_dir := -player.global_transform.basis.z
|
|
if look_dir.dot(machine.wall_normal) > 0.4:
|
|
machine.wall_normal = Vector3.ZERO
|
|
machine.wall_side = 0.0
|
|
machine.wall_cooldown_timer = 0.3
|
|
machine.switch_to("air")
|
|
return
|
|
|
|
# ── Push slightly toward wall to maintain contact ─────────────────────
|
|
vel -= machine.wall_normal * 2.0
|
|
|
|
# ── Wall jump ─────────────────────────────────────────────────────────
|
|
if machine.input_jump_just_pressed and machine.jump_cooldown_timer <= 0.0:
|
|
# Base jump off velocity on current preserved momentum
|
|
var jump_vel: Vector3 = _current_tangent * _run_speed
|
|
|
|
# Spring off the wall
|
|
jump_vel += machine.wall_normal * params.wall_run_jump_off_normal
|
|
var h_look := Vector3(look_dir.x, 0.0, look_dir.z)
|
|
if h_look.length_squared() > 0.01:
|
|
jump_vel += h_look.normalized() * params.wall_run_jump_horizontal
|
|
|
|
jump_vel.y = params.wall_run_auto_jump_speed
|
|
player.velocity = jump_vel
|
|
machine.wall_normal = Vector3.ZERO
|
|
machine.wall_side = 0.0
|
|
machine.wall_cooldown_timer = 0.3
|
|
machine.register_chain_mechanic("wall_jump")
|
|
machine.jump_cooldown_timer = params.jump_cooldown
|
|
machine.switch_to("air")
|
|
return
|
|
|
|
# ── Wall cling (no movement input) ────────────────────────────────────
|
|
if machine.input_dir.length() < 0.1:
|
|
machine.switch_to("wall_cling")
|
|
return
|
|
|
|
player.velocity = vel
|
|
player.move_and_slide()
|
|
_last_vel = player.velocity
|
|
|
|
# ── Check still on wall ───────────────────────────────────────────────
|
|
var still_on_wall := machine.detect_wall_horizontal()
|
|
if still_on_wall == Vector3.ZERO:
|
|
machine.wall_normal = Vector3.ZERO
|
|
machine.wall_side = 0.0
|
|
machine.switch_to("air")
|
|
return
|
|
|
|
# ── Hit floor during wall run ─────────────────────────────────────────
|
|
if player.is_on_floor():
|
|
machine.on_ground = true
|
|
machine.wall_normal = Vector3.ZERO
|
|
machine.wall_side = 0.0
|
|
machine.current_jump_count = 0
|
|
machine.switch_to("ground")
|
|
|
|
|
|
func _get_camera_rig():
|
|
if machine.player and machine.player.has_node("HeadPivot"):
|
|
return machine.player.get_node("HeadPivot")
|
|
return null
|