extends CanvasLayer signal exit(success: bool) var audio_manual = preload("res://asset/audio/sfx/game/八音盒/操纵八音盒.mp3") as AudioStream var audio_auto = preload("res://asset/audio/sfx/game/八音盒/自走八音盒.mp3") as AudioStream @onready var box_closed = $All/ClosedBox as Draggable2D @onready var box_opened = $All/OpenedBox as Sprite2D @onready var box_broken = $All/BrokenBox as Sprite2D @onready var box_drawer = $All/Drawer as Node2D @onready var box_animation = $All/BoxAnimation as AnimatedSprite2D @onready var drawer_btn = $"All/BrokenBox/Draggable抽屉" as Draggable2D @onready var handle_wheel = $All/Wheel @onready var side_handle = $All/SideHandle as AnimatedSprite2D @onready var pic = $"All/Draggable照片" as Draggable2D @onready var d1 = $"All/OpenedBox/Panel/Draggable2D1" as Draggable2D @onready var d2 = $"All/OpenedBox/Panel/Draggable2D2" as Draggable2D @onready var d3 = $"All/OpenedBox/Panel/Draggable2D3" as Draggable2D @onready var d4 = $"All/OpenedBox/Panel/Draggable2D4" as Draggable2D @onready var s0 = $"All/OpenedBox/Panel/Marker2D1" as Marker2D @onready var s1 = $"All/OpenedBox/Panel/Marker2D2" as Marker2D @onready var s2 = $"All/OpenedBox/Panel/Marker2D3" as Marker2D @onready var s3 = $"All/OpenedBox/Panel/Marker2D4" as Marker2D @onready var danzhu = $"All/Drawer/Draggable1" as Draggable2D @onready var maotou = $"All/Drawer/Draggable2" as Draggable2D @onready var xiaochan = $"All/Drawer/Draggable3" as Draggable2D var slots: PackedVector2Array = [] var draggables: Array[Draggable2D] = [] @onready var sfx_music = $MusicPlayer as AudioStreamPlayer @onready var sfx_picked = $Picked as AudioStreamPlayer @onready var sfx_dropped = $Dropped as AudioStreamPlayer @onready var sfx_plugged = $Plugged as AudioStreamPlayer @onready var sfx_plugged_finish = $PluggedFinish as AudioStreamPlayer @onready var sfx_open_lid = $OpenLid as AudioStreamPlayer @onready var sfx_close_lid = $CloseLid as AudioStreamPlayer @onready var sfx_open_drawer = $OpenDrawer as AudioStreamPlayer # 1关闭 2打开 3放入小蝉人偶 4全部放置正确(可摇手柄) 5已播放完成 var stage := 1: set(value): stage = value ArchiveManager.set_global_entry("c02_musicbox_stage", value) # 父,母,蝶,蝉 var playing_step_sec := 0.5 var playing_step_degree := 45.0 var playing_steps := 10 func _ready() -> void: if Engine.is_editor_hint(): return # 读取、配置参数 stage = ArchiveManager.get_global_value("c02_musicbox_stage", 1) playing_step_sec = audio_manual.get_length() / playing_steps handle_wheel.rotated.connect(_on_wheel_rotated) box_closed.picked.connect(_on_box_picked) drawer_btn.picked.connect(_on_drawer_picked) if ArchiveManager.get_global_value("c02_musicbox_pic_taken"): pic.queue_free() if ArchiveManager.get_global_value("c02_musicbox_danzhu_taken"): danzhu.queue_free() if ArchiveManager.get_global_value("c02_musicbox_maotou_taken"): maotou.queue_free() if ArchiveManager.get_global_value("c02_musicbox_xiaochan_taken"): xiaochan.queue_free() pic.picked.connect(_on_prop_picked.bind("pic")) danzhu.picked.connect(_on_prop_picked.bind("danzhu")) maotou.picked.connect(_on_prop_picked.bind("maotou")) xiaochan.picked.connect(_on_prop_picked.bind("xiaochan")) d1.picked.connect(_on_picked) d1.dropped.connect(_on_dropped) d2.picked.connect(_on_picked) d2.dropped.connect(_on_dropped) d3.picked.connect(_on_picked) d3.dropped.connect(_on_dropped) d4.picked.connect(_on_picked) d4.dropped.connect(_on_dropped) draggables = [d1, d2, d3, d4] slots = [s0.position, s1.position, s2.position, s3.position] # 1关闭 2打开 3放入小蝉人偶 4全部放置正确(可摇手柄) 5已播放完成 _chechout_stage(stage, false) func _toggle_side_handle_outline(display: bool): var tween = create_tween() if display: tween.tween_property(side_handle.material, "shader_parameter/thickness", 1.0, 0.2) else: tween.tween_property(side_handle.material, "shader_parameter/thickness", 0.0, 0.2) func _on_box_picked(_node): # 1关闭 2打开 3放入小蝉人偶 4全部放置正确(可摇手柄) 5已播放完成 if stage == 1: _chechout_stage(2) else: box_closed.visible = false box_broken.visible = true if pic: pic.visible = true func _on_drawer_picked(_node): sfx_open_drawer.play() box_animation.visible = true box_animation.play() drawer_btn.visible = false await box_animation.animation_finished await get_tree().create_timer(1.0).timeout box_animation.visible = false box_broken.visible = false box_drawer.visible = true box_drawer.modulate.a = 0 create_tween().tween_property(box_drawer, "modulate:a", 1.0, 0.5) func _on_prop_picked(node: Draggable2D, archive_key: String): node.freezing = true var key = node.item_name # var tween = create_tween() # # 撕下来的 fade out # tween.tween_property(node, "modulate:a", 0.0, 1.0) # tween.tween_callback(node.queue_free) # tween.tween_interval(0.5) # await tween.finished node.queue_free() SceneManager.enable_prop_item(key) ArchiveManager.set_global_entry("c02_musicbox_" + archive_key + "_taken", true) func _on_picked(node: Draggable2D): sfx_picked.play() # 使用 plug_pos 作为 meta 标记 node 位置 node.set_meta("plug_pos", -1) func _on_dropped(node: Draggable2D): var limit_squared = 100.0 # 判断是否对应到 slots 在 limit_squared 范围内 for i in range(len(slots)): var slot_pos = slots[i] var distance_squared = (node.position - slot_pos).length_squared() if distance_squared < limit_squared: node.position = slot_pos node.set_meta("plug_pos", i) post_plugged() else: sfx_dropped.play() func post_plugged(): # If all items are placed var all_plugged = true for i in range(4): if draggables[i].get_meta("plug_pos", -1) != i: all_plugged = false break if all_plugged: # 全部插入,stage 进入下一阶段 sfx_plugged_finish.play() _chechout_stage(stage + 1) else: sfx_plugged.play() var accumulated_radiant := 0.0 # 8 steps in total, then auto play var accumulated_steps := 0 var playing := false var continue_playing := false func _on_wheel_rotated(radiant: float) -> void: if stage != 4 or accumulated_steps >= playing_steps: return accumulated_radiant += radiant if abs(accumulated_radiant) > deg_to_rad(playing_step_degree): accumulated_radiant = 0 if playing: continue_playing = true else: playing = true side_handle.play("default") if accumulated_steps == 0: sfx_music.play() else: sfx_music.stream_paused = false get_tree().create_timer(playing_step_sec).timeout.connect(_on_playing_step_finished) func _on_playing_step_finished(): accumulated_steps += 1 if accumulated_steps == playing_steps: # auto play _checkout_auto_play() return if continue_playing: continue_playing = false get_tree().create_timer(playing_step_sec).timeout.connect(_on_playing_step_finished) else: playing = false sfx_music.stream_paused = true side_handle.pause() func _checkout_auto_play(): sfx_music.stop() sfx_music.stream = audio_auto sfx_music.play() side_handle.play() # var audio_len = audio_auto.get_length() # 音效 9.5 秒时关闭盒子 var audio_len = 9.5 get_tree().create_timer(audio_len).timeout.connect(_on_auto_play_finished) func _on_auto_play_finished(): side_handle.pause() stage = 5 # 不播放关闭音效,因为自动播放时已经播放过了 _chechout_stage(5, false) # 抖动效果,逐渐减弱 var tween = create_tween() var fps := 10.0 var duration := 0.4 var delta := 6.0 var origin_pos = box_closed.position var count = int(duration * fps) var delta_t = 1.0 / fps for i in range(count): var _offset = Vector2(randf_range(-delta, delta), randf_range(-delta, delta)) * exp(-i) tween.tween_property(box_closed, "position", origin_pos + _offset, delta_t).set_trans( Tween.TRANS_CUBIC ) func _hide_all(): if pic: pic.visible = false box_broken.visible = false box_closed.visible = false box_opened.visible = false box_drawer.visible = false box_animation.visible = false func _chechout_stage(s: int, play_sfx := true) -> void: _hide_all() var display_handle_outline = false # 1关闭 2打开 3放入小蝉人偶 4全部放置正确(可摇手柄) 5已播放完成 stage = s match s: 1: box_closed.visible = true if play_sfx: sfx_close_lid.play() 2: SceneManager.pop_center_notification(tr("ui_use_prop")) box_opened.visible = true d4.visible = false if pic: pic.visible = true if play_sfx: sfx_open_lid.play() 3: d4.visible = true d4.modulate.a = 0 create_tween().tween_property(d4, "modulate:a", 1.0, 1.0) box_opened.visible = true if pic: pic.visible = true if play_sfx: sfx_open_lid.play() 4: box_opened.visible = true d4.visible = true if pic: pic.visible = true _settle_plug_positions() display_handle_outline = true if play_sfx: sfx_open_lid.play() 5: box_closed.visible = true _toggle_side_handle_outline(display_handle_outline) func _settle_plug_positions(): for i in range(4): draggables[i].position = slots[i] draggables[i].rotation = 0 draggables[i].set_meta("plug_pos", i) draggables[i].freezing = true func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("interact") and stage == 2: get_viewport().set_input_as_handled() # 尝试使用道具 var hud = SceneManager.get_prop_hud() as PropHud var key = SceneManager.get_current_prop(false) if key == "prop_木头人偶": SceneManager.disable_prop_item(key) _chechout_stage(3) else: # hud 无效shaking hud.on_toggle_invalid_prop() elif event.is_action_pressed("escape"): get_viewport().set_input_as_handled() exit.emit(false)