113 lines
4.3 KiB
GDScript
113 lines
4.3 KiB
GDScript
extends Node
|
|
class_name StateAir
|
|
|
|
var machine: MovementStateMachine
|
|
var params: MovementParams:
|
|
get: return machine.params
|
|
|
|
|
|
func enter(_data: Dictionary = {}) -> void:
|
|
machine.on_ground = false
|
|
|
|
|
|
func exit() -> void:
|
|
pass
|
|
|
|
|
|
func update(delta: float) -> void:
|
|
var player := machine.player
|
|
var vel: Vector3 = player.velocity
|
|
|
|
# ── Gravity with variable jump height ─────────────────────────────────
|
|
var grav := params.gravity
|
|
if vel.y < 0.0:
|
|
# Falling: heavier gravity for snappy arcs
|
|
grav *= params.fall_multiplier
|
|
elif vel.y > 0.0 and not machine.input_jump_pressed:
|
|
# Released jump early: cut the jump short
|
|
grav *= params.low_jump_multiplier
|
|
vel.y -= grav * delta
|
|
|
|
# ── Air control ───────────────────────────────────────────────────────
|
|
var wish_dir: Vector3 = machine.wish_dir_world
|
|
var hvel := Vector3(vel.x, 0.0, vel.z)
|
|
|
|
if wish_dir.length_squared() > 0.01:
|
|
wish_dir = wish_dir.normalized()
|
|
var current_speed = hvel.length()
|
|
|
|
# Add a strong force in the wish direction
|
|
var air_accel = params.air_strafe_accel * delta
|
|
var new_hvel = hvel + wish_dir * air_accel
|
|
|
|
# Limit the speed so we don't gain speed purely from air control.
|
|
# We only allow air control to change our direction (strafing)
|
|
# or to accelerate us up to our walk speed if we started slow.
|
|
var max_allowed_speed = maxf(current_speed, params.walk_speed)
|
|
|
|
if new_hvel.length() > max_allowed_speed:
|
|
new_hvel = new_hvel.normalized() * max_allowed_speed
|
|
|
|
hvel = new_hvel
|
|
else:
|
|
# No input: slight air drag (very subtle)
|
|
hvel *= (1.0 - 0.5 * delta)
|
|
|
|
vel.x = hvel.x
|
|
vel.z = hvel.z
|
|
|
|
player.velocity = vel
|
|
player.move_and_slide()
|
|
|
|
# ── Landing ───────────────────────────────────────────────────────────
|
|
if player.is_on_floor():
|
|
machine.on_ground = true
|
|
machine.current_jump_count = 0
|
|
|
|
# Bunny hop: if jump was buffered or pressed on landing frame
|
|
if (machine.input_jump_pressed or machine.jump_buffer_time > 0.0) and machine.jump_cooldown_timer <= 0.0:
|
|
var land_speed := Vector3(player.velocity.x, 0.0, player.velocity.z).length()
|
|
var bhop_speed := maxf(land_speed, minf(land_speed + params.bunny_hop_speed_gain, params.bunny_hop_speed_cap))
|
|
# Maintain horizontal direction, boost speed
|
|
var hdir := Vector3(player.velocity.x, 0.0, player.velocity.z)
|
|
if hdir.length_squared() > 0.01:
|
|
hdir = hdir.normalized()
|
|
else:
|
|
hdir = machine.wish_dir_world
|
|
player.velocity.x = hdir.x * bhop_speed
|
|
player.velocity.z = hdir.z * bhop_speed
|
|
player.velocity.y = params.jump_velocity * params.bunny_hop_impulse
|
|
machine.current_jump_count = 1
|
|
machine.on_ground = false
|
|
machine.jump_buffer_time = 0.0
|
|
machine.register_chain_mechanic("bunny_hop")
|
|
machine.jump_cooldown_timer = params.jump_cooldown
|
|
if player.jump_player:
|
|
player.jump_player.play()
|
|
# Stay in air state
|
|
return
|
|
|
|
machine.switch_to("ground")
|
|
return
|
|
|
|
# ── Double Jump ───────────────────────────────────────────────────────
|
|
if machine.input_jump_just_pressed and machine.current_jump_count < params.double_jump_max_count and machine.jump_cooldown_timer <= 0.0:
|
|
player.velocity.y = params.double_jump_velocity
|
|
machine.jump_cooldown_timer = params.jump_cooldown
|
|
machine.current_jump_count += 1
|
|
machine.register_chain_mechanic("double_jump")
|
|
if player.double_jump_player:
|
|
player.double_jump_player.play()
|
|
return
|
|
|
|
# ── Wall run transition ───────────────────────────────────────────────
|
|
var wall_n := machine.detect_wall_horizontal()
|
|
if wall_n != Vector3.ZERO and machine.input_dir.length() > 0.1 and machine.wall_cooldown_timer <= 0.0:
|
|
machine.switch_to("wall_run")
|
|
return
|
|
|
|
# ── Dash ──────────────────────────────────────────────────────────────
|
|
if machine.input_dash:
|
|
machine.switch_to("dash")
|
|
return
|