xiandie/scene/little_game/八音盒/八音盒.gd
2025-06-20 20:36:08 +08:00

343 lines
10 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 xiaomao = $"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_xiaomao_taken"):
xiaomao.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"))
xiaomao.picked.connect(_on_prop_picked.bind("xiaomao"))
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)
if key == "prop_无头小猫玩具":
_on_pick_catty()
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)
func _on_pick_catty():
if not SceneManager.has_prop("prop_小猫玩具的脑袋"):
return
var inspector = SceneManager.get_inspector()
# 将小猫头与身子合并
SceneManager.disable_prop_item("prop_小猫玩具的脑袋")
SceneManager.disable_prop_item("prop_无头小猫玩具")
SceneManager.enable_prop_item_silently("prop_小猫玩具完整")
await inspector.quit_and_hidden
var sprite = $"拼接小猫玩具"
sprite.visible = true
sprite.play()
await sprite.animation_finished
sprite.visible = false
SceneManager.enable_prop_item("prop_小猫玩具完整")