feat: added grapple mechanic
This commit is contained in:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user