extends AnimatedSprite2D signal before_restart # 在玩家移速基础上增加的速度 var MOVE_PLUS_SPEED := 300.0 const RESET_Y := -300.0 @export var mute := false: set(val): mute = val _on_mute_updated() @export var move_target: Node2D @export_enum("left", "right") var hand_mode := 0 @export var left_side_sprite: AnimatedSprite2D @export var right_side_sprite: AnimatedSprite2D @export var left_area: Area2D @export var right_area: Area2D @export var left_static_collision: CollisionShape2D @export var right_static_collision: CollisionShape2D @onready var footstep_sfx = $"Sfx2D棺材怪脚步声" as Sfx2D @onready var chop_sfx = $"Sfx2D棺材怪切刀" as Sfx @onready var remote_left_side = $"左侧" as RemoteTransform2D @onready var remote_right_side = $"右侧" as RemoteTransform2D @onready var remote_left_shadow = $"左落点" as RemoteTransform2D @onready var remote_right_shadow = $"右落点" as RemoteTransform2D var player: MainPlayer var left_hand_x_offset: float var right_hand_x_offset: float var chopped_safe_period := false var finished := false func _ready() -> void: await SceneManager.ground_ready player = SceneManager.get_player() left_hand_x_offset = remote_left_shadow.position.x right_hand_x_offset = remote_right_shadow.position.x left_area.body_entered.connect(_on_player_chopped) right_area.body_entered.connect(_on_player_chopped) _on_mute_updated() func _on_mute_updated() -> void: if not is_node_ready(): return if mute: footstep_sfx.volume_db = -100 else: footstep_sfx.reset_volumn_to_default() # 停止切肉动作, x 对齐到 move_target func refresh_position() -> void: var target_x = move_target.global_position.x if hand_mode == 0: target_x -= left_hand_x_offset else: target_x -= right_hand_x_offset global_position.x = target_x remote_right_side.position.y = RESET_Y remote_left_side.position.y = RESET_Y remote_left_shadow.scale = Vector2.ZERO remote_right_shadow.scale = Vector2.ZERO var aiming := 0 const AIMING_DURATION := 2.0 func chop_left(aiming_duration := AIMING_DURATION) -> void: if aiming: return hand_mode = 0 aiming += 1 _checkout_hand_animation(false) var tween = create_tween() if aiming_duration > 0.0: tween.tween_property(remote_left_shadow, "scale", Vector2.ONE, aiming_duration) tween.tween_callback(left_side_sprite.play) tween.tween_property(remote_left_side, "position:y", 0.0, 0.15) if aiming_duration <= 0.0: tween.parallel().tween_property(remote_left_shadow, "scale", Vector2.ONE, 0.3) tween.tween_callback(chop_sfx.play) tween.tween_callback(toggle_safe_period.bind(true)) tween.tween_callback(shake) tween.tween_callback(func(): aiming -= 1) tween.tween_interval(0.1) tween.tween_property(remote_left_shadow, "scale", Vector2(0.2, 0.2), 0.2) tween.parallel().tween_property(remote_left_side, "position:y", RESET_Y, 0.4) tween.tween_callback(toggle_safe_period.bind(false)) func chop_right(aiming_duration := AIMING_DURATION) -> void: if aiming: return hand_mode = 1 aiming += 1 _checkout_hand_animation(false) var tween = create_tween() if aiming_duration > 0.0: tween.tween_property(remote_right_shadow, "scale", Vector2.ONE, aiming_duration) tween.tween_callback(right_side_sprite.play) tween.tween_property(remote_right_side, "position:y", 0.0, 0.15) if aiming_duration <= 0.0: tween.parallel().tween_property(remote_right_shadow, "scale", Vector2.ONE, 0.3) tween.tween_callback(chop_sfx.play) tween.tween_callback(toggle_safe_period.bind(true)) tween.tween_callback(shake) tween.tween_callback(func(): aiming -= 1) tween.tween_interval(0.1) tween.tween_property(remote_right_shadow, "scale", Vector2(0.2, 0.2), 0.5) tween.parallel().tween_property(remote_right_side, "position:y", RESET_Y, 0.4) tween.tween_callback(toggle_safe_period.bind(false)) func toggle_safe_period(safe: bool) -> void: chopped_safe_period = safe left_static_collision.disabled = not safe right_static_collision.disabled = not safe func shake() -> void: SceneManager.get_camera_marker().shake_camera(6.0) func _on_player_chopped(_body) -> void: if chopped_safe_period: return if finished: return finished = true SceneManager.lock_player() await SceneManager.player_action(7) await Util.wait(1.0) SceneManager.unlock_player() before_restart.emit() # restart get_tree().reload_current_scene() func _physics_process(delta: float) -> void: if not player: return var target_x = move_target.global_position.x if hand_mode == 0: target_x -= left_hand_x_offset else: target_x -= right_hand_x_offset var x = global_position.x if chopped_safe_period or abs(target_x - x) < 0.1: stop() if footstep_sfx.playing: footstep_sfx.stop() else: var speed = MOVE_PLUS_SPEED speed += abs(player.velocity.x) if aiming: speed *= 0.6 global_position.x = move_toward(x, target_x, delta * speed) # if Time.get_ticks_msec() % 100 == 0: # prints(x, target_x) if not footstep_sfx.playing: footstep_sfx.play() _checkout_hand_animation() func _checkout_hand_animation(is_move := true) -> void: var target_animation = "" if is_move: target_animation = "棺材怪移动" if hand_mode == 0 else "棺材怪移动右" else: target_animation = "棺材怪左砍" if hand_mode == 0 else "棺材怪右砍" if not is_playing() or animation != target_animation: if hand_mode == 0: play(target_animation) else: play(target_animation)