extends Node class_name StateGround var machine: MovementStateMachine var params: MovementParams: get: return machine.params func enter(_data: Dictionary = {}) -> void: pass func exit() -> void: pass func update(delta: float) -> void: var vel: Vector3 = machine.player.velocity # Input direction var input_v := machine.input_dir var wish_dir := Vector3(input_v.x, 0.0, -input_v.y) if wish_dir.length_squared() > 1.0: wish_dir = wish_dir.normalized() var speed := params.walk_speed if machine.input_sprint and wish_dir.length_squared() > 0.1: speed = params.sprint_speed var effective_speed := machine.get_effective_speed(speed) # Horizontal movement var hvel := Vector3(vel.x, 0.0, vel.z) var target_vel := wish_dir * effective_speed if wish_dir.length_squared() > 0.0: var accel := params.ground_acceleration hvel = hvel.lerp(target_vel, 1.0 - exp(-accel * delta)) else: hvel = hvel.lerp(Vector3.ZERO, 1.0 - exp(-params.ground_deceleration * delta)) vel.x = hvel.x vel.z = hvel.z # Gravity (we apply tuned gravity so we don't double-apply from _velocity) var actual_gravity := params.gravity if vel.y < 0.0: vel.y += actual_gravity * params.fall_multiplier * delta elif vel.y > 0.0 and not machine.input_jump_pressed: vel.y += actual_gravity * params.low_jump_multiplier * delta else: vel.y -= actual_gravity * delta machine.player.velocity = vel machine.player.move_and_slide() var on_wall := detect_wall() if on_wall and machine.input_dir.length() > 0.0: machine.wall_normal = on_wall machine.switch_to("wall_run") if machine.input_jump_just_pressed and machine.coyote_timer > 0.0: machine.player.velocity.y = params.jump_velocity machine.current_jump_count = 1 machine.on_ground = false machine.register_chain_mechanic("jump") machine.switch_to("air") if machine.player.is_on_floor(): machine.on_ground = true machine.last_ground_time = machine.get_process_delta_time() machine.current_jump_count = 0 else: machine.on_ground = false if machine.input_jump_just_pressed and machine.jump_buffer_time > 0.0: machine.player.velocity.y = params.jump_velocity machine.current_jump_count = 1 machine.register_chain_mechanic("jump") machine.switch_to("air") elif vel.y < 0.0: machine.switch_to("air") # Slide if machine.input_crouch and machine.input_sprint and machine.player.velocity.length() > params.slide_min_speed: machine.switch_to("slide") return # Dash if machine.input_dash and machine.player.velocity.length() > 0.0: machine.switch_to("dash") func detect_wall() -> Vector3: var origin := machine.player.global_position var hvel := Vector3(machine.player.velocity.x, 0.0, machine.player.velocity.z).normalized() if hvel.length_squared() < 0.01: hvel = Vector3.FORWARD var space_state := machine.player.get_world_3d().direct_space_state for offset_sign in [1.0, -1.0]: var dir: Vector3 = (hvel.cross(Vector3.UP) * offset_sign).normalized() var offset: Vector3 = dir * 0.4 var ray := PhysicsRayQueryParameters3D.create(origin + offset + Vector3.UP * params.wall_ray_up_height, origin + offset - Vector3.DOWN * params.wall_ray_down_height) ray.exclude = [machine.player.get_rid()] var hit := space_state.intersect_ray(ray) if not hit.is_empty(): var n: Vector3 = hit.get("normal", Vector3.ZERO) if n.y < 0.3 and n.length_squared() > 0.0: return n.normalized() return Vector3.ZERO