2025-01-27 15:13:37 +00:00
|
|
|
|
extends CanvasLayer
|
2025-01-05 11:25:13 +00:00
|
|
|
|
|
2025-05-13 11:45:33 +00:00
|
|
|
|
signal exit(success: bool)
|
2025-01-14 10:20:31 +00:00
|
|
|
|
|
2025-05-30 11:05:06 +00:00
|
|
|
|
var audio_manual = preload("res://asset/audio/sfx/game/八音盒/操纵八音盒.mp3") as AudioStream
|
|
|
|
|
var audio_auto = preload("res://asset/audio/sfx/game/八音盒/自走八音盒.mp3") as AudioStream
|
2025-01-05 11:25:13 +00:00
|
|
|
|
|
2025-05-30 11:05:06 +00:00
|
|
|
|
@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:
|
2025-01-05 11:25:13 +00:00
|
|
|
|
set(value):
|
2025-05-30 11:05:06 +00:00
|
|
|
|
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
|
2025-01-06 11:13:48 +00:00
|
|
|
|
|
2025-01-05 11:25:13 +00:00
|
|
|
|
|
|
|
|
|
func _ready() -> void:
|
2025-05-14 20:43:55 +00:00
|
|
|
|
if Engine.is_editor_hint():
|
2025-01-05 11:25:13 +00:00
|
|
|
|
return
|
2025-05-30 11:05:06 +00:00
|
|
|
|
# 读取、配置参数
|
|
|
|
|
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)
|
|
|
|
|
|
2025-05-31 06:45:34 +00:00
|
|
|
|
draggables = [d1, d2, d3, d4]
|
|
|
|
|
slots = [s0.position, s1.position, s2.position, s3.position]
|
|
|
|
|
|
|
|
|
|
# 1关闭 2打开 3放入小蝉人偶 4全部放置正确(可摇手柄) 5已播放完成
|
|
|
|
|
_chechout_stage(stage, false)
|
|
|
|
|
|
2025-05-30 11:05:06 +00:00
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
2025-05-31 06:45:34 +00:00
|
|
|
|
|
2025-05-30 11:05:06 +00:00
|
|
|
|
func _on_drawer_picked(_node):
|
|
|
|
|
sfx_open_drawer.play()
|
|
|
|
|
box_animation.visible = true
|
|
|
|
|
box_animation.play()
|
2025-05-31 06:45:34 +00:00
|
|
|
|
drawer_btn.visible = false
|
2025-05-30 11:05:06 +00:00
|
|
|
|
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):
|
2025-05-31 06:45:34 +00:00
|
|
|
|
var limit_squared = 100.0
|
2025-05-30 11:05:06 +00:00
|
|
|
|
# 判断是否对应到 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):
|
2025-05-31 06:45:34 +00:00
|
|
|
|
if draggables[i].get_meta("plug_pos", -1) != i:
|
2025-05-30 11:05:06 +00:00
|
|
|
|
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
|
|
|
|
|
)
|
|
|
|
|
|
2025-01-05 11:25:13 +00:00
|
|
|
|
|
2025-05-30 11:05:06 +00:00
|
|
|
|
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
|
2025-05-31 06:45:34 +00:00
|
|
|
|
d4.visible = false
|
2025-05-30 11:05:06 +00:00
|
|
|
|
if pic:
|
|
|
|
|
pic.visible = true
|
|
|
|
|
if play_sfx:
|
|
|
|
|
sfx_open_lid.play()
|
|
|
|
|
3:
|
2025-05-31 06:45:34 +00:00
|
|
|
|
d4.visible = true
|
|
|
|
|
d4.modulate.a = 0
|
|
|
|
|
create_tween().tween_property(d4, "modulate:a", 1.0, 1.0)
|
2025-05-30 11:05:06 +00:00
|
|
|
|
box_opened.visible = true
|
|
|
|
|
if pic:
|
|
|
|
|
pic.visible = true
|
|
|
|
|
if play_sfx:
|
|
|
|
|
sfx_open_lid.play()
|
|
|
|
|
4:
|
|
|
|
|
box_opened.visible = true
|
2025-05-31 06:45:34 +00:00
|
|
|
|
d4.visible = true
|
2025-05-30 11:05:06 +00:00
|
|
|
|
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]
|
2025-05-31 06:45:34 +00:00
|
|
|
|
draggables[i].rotation = 0
|
2025-05-30 11:05:06 +00:00
|
|
|
|
draggables[i].set_meta("plug_pos", i)
|
|
|
|
|
draggables[i].freezing = true
|
2025-01-05 11:25:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _unhandled_input(event: InputEvent) -> void:
|
2025-05-30 11:05:06 +00:00
|
|
|
|
if event.is_action_pressed("interact") and stage == 2:
|
2025-05-14 20:43:55 +00:00
|
|
|
|
# 尝试使用道具
|
2025-05-30 11:05:06 +00:00
|
|
|
|
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()
|