Files
Papay-Shooter/movement/states/state_air.gd
T
Hermes 462d12bee5 fix: Godot 4.2.1 compat and map builder fixes
- Fix tab character in _build_elevated_walkways() call (line 63)
- Fix untyped 'pos' variable in grapple pillar loop (line 376)
- Remove fog_sky_affinity (Godot 4.3+ only property)
- Replace preload() with load() for runtime script loading
- Replace 'is DoubleBarrelShotgun' type check with duck-typing
- Fix 'is not OfflineMultiplayerPeer' syntax in grenade spawn
- Replace absf() with abs() in state_machine, state_air, state_ground
- Update test_level.tscn to remove stale UID reference

All fixes verified: PARSE OK and scene runs without script errors
on Godot 4.2.1 headless.
2026-06-21 01:24:42 -04:00

156 lines
6.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 interaction (Run, Climb, Vault) ──────────────────────────────
if machine.input_dir.length() > 0.1 and machine.input_dir.y <= 0.0 and machine.wall_cooldown_timer <= 0.0:
var fwd_wall = machine.detect_wall_forward()
if fwd_wall.hit:
if fwd_wall.is_short:
# Instant Vault
var look_dir := -player.global_transform.basis.z
var h_look := Vector3(look_dir.x, 0.0, look_dir.z)
if h_look.length_squared() > 0.01:
h_look = h_look.normalized()
player.velocity = h_look * params.wall_climb_vault_forward
player.velocity.y = params.wall_climb_vault_up
if player.vault_player:
player.vault_player.play()
# Quick camera animation (tilt up and forward bob)
var camera = player.camera
if camera:
var tween = player.create_tween()
tween.tween_property(camera, "rotation_degrees:x", camera.rotation_degrees.x + 10.0, 0.15).set_trans(Tween.TRANS_SINE)
tween.parallel().tween_property(camera, "v_offset", -0.2, 0.15).set_trans(Tween.TRANS_SINE)
tween.chain().tween_property(camera, "rotation_degrees:x", camera.rotation_degrees.x, 0.2).set_trans(Tween.TRANS_SINE)
tween.parallel().tween_property(camera, "v_offset", 0.0, 0.2).set_trans(Tween.TRANS_SINE)
machine.wall_cooldown_timer = 0.3
return
else:
# Check if moving directly into wall and looking generally towards it
var look_dir := -player.global_transform.basis.z
var looking_towards := look_dir.dot(-fwd_wall.normal) > 0.0
var moving_towards := wish_dir.dot(-fwd_wall.normal) > cos(deg_to_rad(params.wall_climb_max_angle))
if looking_towards and moving_towards and machine.can_wall_climb:
machine.wall_normal = fwd_wall.normal
machine.switch_to("wall_climb")
return
# Fallback to wall run
var wall_n := machine.detect_wall_horizontal()
if wall_n != Vector3.ZERO:
var w_look_dir := -player.global_transform.basis.z
var h_look := Vector3(w_look_dir.x, 0.0, w_look_dir.z).normalized()
# Require looking mostly along the wall (angle > 41 degrees from normal)
# and inputting mostly along the wall (angle > 31 degrees from normal, allows W+A/D diagonally into wall)
if abs(h_look.dot(wall_n)) < 0.75 and abs(wish_dir.dot(wall_n)) < 0.85:
machine.switch_to("wall_run")
return
# ── Dash ──────────────────────────────────────────────────────────────
if machine.input_dash:
machine.switch_to("dash")
return