feat: added grapple mechanic

This commit is contained in:
DottsGit
2026-06-05 02:05:07 -04:00
parent 0912669759
commit ecefeb9f7e
8 changed files with 232 additions and 0 deletions
+84
View File
@@ -20,10 +20,17 @@ var wind_player: AudioStreamPlayer
var jump_player: AudioStreamPlayer
var double_jump_player: AudioStreamPlayer
var grapple_shoot_player: AudioStreamPlayer
var grapple_latch_player: AudioStreamPlayer
var grapple_swing_player: AudioStreamPlayer
# UI
var hit_marker: Control
var _hit_marker_tween: Tween
# Grapple
var grapple_rope: MeshInstance3D
func _ready() -> void:
var sm := _ensure_machine()
@@ -42,6 +49,7 @@ func _ready() -> void:
_setup_audio()
_setup_hit_marker()
_setup_grapple()
set_process(true)
set_physics_process(true)
@@ -84,6 +92,24 @@ func _setup_audio() -> void:
wind_player.volume_db = -80.0 # Start silent
add_child(wind_player)
wind_player.play()
grapple_shoot_player = AudioStreamPlayer.new()
grapple_shoot_player.stream = load("res://assets/sounds/dash.wav")
grapple_shoot_player.volume_db = -5.0
grapple_shoot_player.pitch_scale = 1.5
add_child(grapple_shoot_player)
grapple_latch_player = AudioStreamPlayer.new()
grapple_latch_player.stream = load("res://assets/sounds/hit_confirm.wav")
grapple_latch_player.pitch_scale = 0.8
add_child(grapple_latch_player)
grapple_swing_player = AudioStreamPlayer.new()
grapple_swing_player.stream = load("res://assets/sounds/wind.wav")
grapple_swing_player.pitch_scale = 1.5
grapple_swing_player.volume_db = -80.0
add_child(grapple_swing_player)
grapple_swing_player.play()
func _setup_hit_marker() -> void:
hit_marker = Control.new()
@@ -106,6 +132,22 @@ func _setup_hit_marker() -> void:
rect.rotation = angle
hit_marker.add_child(rect)
func _setup_grapple() -> void:
grapple_rope = MeshInstance3D.new()
var rope_mesh = CylinderMesh.new()
rope_mesh.top_radius = 0.04
rope_mesh.bottom_radius = 0.04
rope_mesh.height = 1.0 # Will be scaled dynamically
var mat = StandardMaterial3D.new()
mat.albedo_color = Color(0.1, 0.1, 0.1) # Dark grey cable
mat.roughness = 0.8
rope_mesh.material = mat
grapple_rope.mesh = rope_mesh
grapple_rope.visible = false
get_tree().current_scene.call_deferred("add_child", grapple_rope)
var _last_hit_sound_time: int = 0
func spawn_damage_number(amount: float, hit_pos: Vector3) -> void:
@@ -179,6 +221,44 @@ func _physics_process(_delta: float) -> void:
sm.input_jump_pressed = Input.is_action_pressed("jump")
sm.input_crouch = Input.is_action_pressed("crouch")
sm.input_dash = Input.is_action_just_pressed("dash")
sm.input_grapple = Input.is_action_pressed("grapple")
sm.input_grapple_just_pressed = Input.is_action_just_pressed("grapple")
# Update grapple rope visuals
if (sm.current_state == "grapple" or sm.is_grapple_shooting) and camera:
grapple_rope.visible = true
# For shoot, start at right side of camera slightly below and closer
var start_pos = camera.global_position + camera.global_transform.basis * Vector3(0.3, -0.3, -0.15)
var end_pos = sm.grapple_point
if sm.is_grapple_shooting and sm.grapple_travel_time > 0.0:
# Interpolate end_pos
var t = clampf(sm.grapple_shoot_time / sm.grapple_travel_time, 0.0, 1.0)
end_pos = start_pos.lerp(sm.grapple_point, t)
var dist = start_pos.distance_to(end_pos)
if dist > 0.01:
grapple_rope.global_position = (start_pos + end_pos) / 2.0
# Look at end point
var up_vec = Vector3.UP
var dir = (end_pos - start_pos).normalized()
if abs(dir.dot(up_vec)) > 0.99:
up_vec = Vector3.RIGHT
# Set rotation first, which resets scale
grapple_rope.transform.basis = Basis.looking_at(dir, up_vec) * Basis.from_euler(Vector3(PI/2.0, 0, 0))
# Then apply the scale
grapple_rope.scale = Vector3(1.0, dist, 1.0)
else:
if grapple_rope:
grapple_rope.visible = false
# Swing sound
if sm.current_state == "grapple" and sm.player.velocity.length() > 5.0:
grapple_swing_player.volume_db = lerpf(grapple_swing_player.volume_db, -15.0, _delta * 10.0)
else:
grapple_swing_player.volume_db = lerpf(grapple_swing_player.volume_db, -80.0, _delta * 15.0)
# Raw 2D input
var raw_input := Input.get_vector("move_left", "move_right", "move_forward", "move_back")
@@ -202,3 +282,7 @@ func _physics_process(_delta: float) -> void:
func _on_movement_event(ev: String, data: Dictionary) -> void:
if ev == "chain_updated":
chain_updated.emit(data.count, data.bonus)
elif ev == "grapple_shoot":
grapple_shoot_player.play()
elif ev == "grapple_latch":
grapple_latch_player.play()