- Quake-style air-strafe accelerate (real speed gain from strafing; external speed from dash/rockets never clamped) - Wall run: keeps entry momentum (incl. upward carry), accelerates along the wall instead of hard-setting velocity, gravity fades in over the run - Slide: slope physics (gravity projected along floor accelerates downhill), flat-ground decel instead of multiplicative friction, entry boost, direct slide->wallrun and slide->dash transitions - Dash: cooldown 10s -> 2s, per-player (was a static shared across instances), momentum fully kept on exit, FOV punch event - Grapple: taut pendulum with active rope control (W reels in, S pays out), release keeps swing energy - Wall climb: carries upward momentum in, jump-off kick, shared vault helper - Crouch capsule: single owner in the machine, smoothly lerped, ceiling check; camera eye height follows via crouch factor (no more head snapping) - Landing: machine emits 'land' events scaled by impact; camera dip + pitched down thud; footsteps get pitch variation - Camera rig: event-driven (land dip, dash FOV kick, vault pitch impulse), slide roll tilt - Model: Jump vs Fall split by vertical velocity, Land one-shot on heavy landings, wall-run body lean (synced to remotes via synced_wall_side) Co-Authored-By: Claude Fable 5 <[email protected]>
84 lines
3.0 KiB
GDScript
84 lines
3.0 KiB
GDScript
extends Node
|
|
class_name StateWallCling
|
|
|
|
var machine: MovementStateMachine
|
|
var params: MovementParams:
|
|
get: return machine.params
|
|
|
|
var elapsed: float = 0.0
|
|
var stamina: float = 0.0
|
|
|
|
|
|
func enter(_data: Dictionary = {}) -> void:
|
|
elapsed = 0.0
|
|
stamina = params.wall_cling_max_stamina
|
|
machine.register_chain_mechanic("wall_cling")
|
|
# Keep a touch of momentum so the stop reads as a grab, not a freeze
|
|
machine.player.velocity *= 0.15
|
|
|
|
|
|
func exit() -> void:
|
|
pass
|
|
|
|
|
|
func update(delta: float) -> void:
|
|
elapsed += delta
|
|
stamina -= params.wall_cling_stamina_drain * delta
|
|
|
|
var player := machine.player
|
|
var vel: Vector3 = player.velocity
|
|
|
|
# ── Slow slide down ───────────────────────────────────────────────────
|
|
var slide_factor := 1.0 - (stamina / params.wall_cling_max_stamina) # 0→1 as stamina drains
|
|
vel.x = 0.0
|
|
vel.z = 0.0
|
|
vel.y = params.wall_cling_slide_speed * (1.0 + slide_factor * 2.0)
|
|
|
|
# Push into wall to maintain contact
|
|
vel -= machine.wall_normal * 1.0
|
|
|
|
player.velocity = vel
|
|
player.move_and_slide()
|
|
|
|
# ── Stamina depleted → fall ───────────────────────────────────────────
|
|
if stamina <= 0.0:
|
|
machine.wall_normal = Vector3.ZERO
|
|
machine.wall_side = 0.0
|
|
machine.switch_to("air")
|
|
return
|
|
|
|
# ── Jump off wall ─────────────────────────────────────────────────────
|
|
if machine.input_jump_just_pressed and machine.jump_cooldown_timer <= 0.0:
|
|
var push_dir := machine.wall_normal * params.wall_run_jump_off_normal
|
|
player.velocity = Vector3(push_dir.x, params.jump_velocity, push_dir.z)
|
|
machine.wall_normal = Vector3.ZERO
|
|
machine.wall_side = 0.0
|
|
machine.wall_cooldown_timer = 0.3
|
|
machine.register_chain_mechanic("wall_cling_jump")
|
|
machine.jump_cooldown_timer = params.jump_cooldown
|
|
machine.movement_event.emit("jump", {})
|
|
if machine.player.jump_player:
|
|
machine.player.jump_player.play()
|
|
machine.switch_to("air")
|
|
return
|
|
|
|
# ── Re-enter wall run with movement input ─────────────────────────────
|
|
if machine.input_dir.length() > 0.1:
|
|
machine.switch_to("wall_run")
|
|
return
|
|
|
|
# ── Lost wall contact ─────────────────────────────────────────────────
|
|
var wall := machine.detect_wall_horizontal()
|
|
if wall == Vector3.ZERO:
|
|
machine.wall_normal = Vector3.ZERO
|
|
machine.wall_side = 0.0
|
|
machine.switch_to("air")
|
|
return
|
|
|
|
# ── Hit floor ─────────────────────────────────────────────────────────
|
|
if player.is_on_floor():
|
|
machine.on_ground = true
|
|
machine.wall_normal = Vector3.ZERO
|
|
machine.wall_side = 0.0
|
|
machine.switch_to("ground")
|