extends Node class_name StateGrapple var machine: MovementStateMachine var params: MovementParams: get: return machine.params func enter(_data: Dictionary = {}) -> void: machine.on_ground = false machine.wall_normal = Vector3.ZERO machine.wall_side = 0.0 machine.register_chain_mechanic("grapple") func exit() -> void: pass func update(delta: float) -> void: var player := machine.player var vel: Vector3 = player.velocity # If player releases the button, disconnect if not machine.input_grapple: machine.switch_to("air") return var grapple_pos: Vector3 = machine.grapple_point var current_dist: float = player.global_position.distance_to(grapple_pos) var dir_to_point: Vector3 = (grapple_pos - player.global_position).normalized() # Jump to detach and get a boost if machine.input_jump_just_pressed and machine.jump_cooldown_timer <= 0.0: vel.y = params.jump_velocity # Add a directional boost if jumping while swinging var h_look = machine.wish_dir_world if h_look.length_squared() > 0.1: var h_boost = h_look.normalized() * params.grapple_jump_boost vel.x += h_boost.x vel.z += h_boost.z player.velocity = vel machine.jump_cooldown_timer = params.jump_cooldown machine.register_chain_mechanic("grapple_jump") machine.switch_to("air") if player.jump_player: player.jump_player.play() return # Apply gravity vel.y -= params.gravity * delta # Apply slight inward pull (reeling in) vel += dir_to_point * params.grapple_pull_force * delta # Pendulum / Rope physics # If the player tries to go further than the original rope length, pull them back aggressively var max_len: float = machine.grapple_length if current_dist > max_len: # Project velocity onto the tangent of the sphere (prevent moving further away) var radial_vel = vel.project(dir_to_point) # If radial_vel is pointing AWAY from the grapple point (dot < 0), kill it if radial_vel.dot(dir_to_point) < 0: vel -= radial_vel # Add a spring force to pull them back to the sphere var diff = current_dist - max_len var spring_force = dir_to_point * diff * params.grapple_spring_strength vel += spring_force * delta # Air control while swinging var wish_dir: Vector3 = machine.wish_dir_world if wish_dir.length_squared() > 0.01: wish_dir = wish_dir.normalized() # Add force tangentially to the rope # We want to steer, but not pull away from or directly into the rope. var tangent_wish = wish_dir - wish_dir.project(dir_to_point) if tangent_wish.length_squared() > 0.01: vel += tangent_wish.normalized() * params.grapple_air_control * delta player.velocity = vel player.move_and_slide() # Remove ground detach so players can grapple along the floor # if player.is_on_floor(): # machine.on_ground = true # machine.switch_to("ground") # return