2025-01-06 08:06:20 +00:00
|
|
|
extends Control
|
2025-01-05 11:25:13 +00:00
|
|
|
|
2025-01-14 10:20:31 +00:00
|
|
|
signal exit(passed: bool)
|
|
|
|
|
2025-01-05 11:25:13 +00:00
|
|
|
# answer for the puzzle, by columns
|
2025-01-06 11:13:48 +00:00
|
|
|
# @export var answer := [1, 0, 0, 0, 0, 0, 0, 0, 0]
|
|
|
|
@export var answer := [1, 4, 3, 1, 3, 6, 4, 3, 2]
|
|
|
|
@export var playing_step_degree := 45.0
|
|
|
|
@export var playing_steps := 10
|
2025-01-07 10:54:50 +00:00
|
|
|
@export var debug_reload := false:
|
|
|
|
set(value):
|
|
|
|
if value:
|
|
|
|
debug_reload = false
|
|
|
|
_chechout_mode()
|
2025-01-05 11:25:13 +00:00
|
|
|
|
|
|
|
@onready var points_polygon = $ButtonPositionPoints as Polygon2D
|
2025-01-06 08:06:20 +00:00
|
|
|
@onready var box = $Box as TextureRect
|
2025-01-06 11:13:48 +00:00
|
|
|
@onready var side_handle = $Box/SideHandle as AnimatedSprite2D
|
|
|
|
@onready var inside_handle = $InsideHandle as TextureButton
|
2025-01-05 11:25:13 +00:00
|
|
|
@onready var audio_player = $MusicPlayer2D as AudioStreamPlayer2D
|
2025-01-06 11:13:48 +00:00
|
|
|
@onready var handle_wheel = $Wheel
|
2025-01-05 11:25:13 +00:00
|
|
|
|
|
|
|
var button_texture = preload("res://asset/art/little_game/八音盒/小猫.png")
|
|
|
|
var button_highlight_texture = preload("res://asset/art/little_game/八音盒/小猫高亮.png")
|
|
|
|
|
|
|
|
var box_closed_texture = preload("res://asset/art/little_game/八音盒/无按钮.png")
|
|
|
|
var box_opened_texture = preload("res://asset/art/little_game/八音盒/打开盒子.png")
|
|
|
|
var box_finished_texture = preload("res://asset/art/little_game/八音盒/有按钮.png")
|
|
|
|
|
|
|
|
var sfx_interact = preload("res://asset/audio/sfx/game/八音盒/八音盒互动.mp3") as AudioStream
|
|
|
|
var sfx_drag = preload("res://asset/audio/sfx/game/八音盒/拖动放置.mp3") as AudioStream
|
|
|
|
var sfx_open = preload("res://asset/audio/sfx/game/八音盒/八音盒打开.mp3") as AudioStream
|
2025-01-06 11:13:48 +00:00
|
|
|
var sfx_assemble = preload("res://asset/audio/sfx/game/八音盒/装上插销.mp3") as AudioStream
|
2025-01-05 11:25:13 +00:00
|
|
|
var sfx_close = preload("res://asset/audio/sfx/game/八音盒/关盖子.mp3") as AudioStream
|
|
|
|
var sfx_exit = preload("res://asset/audio/sfx/game/八音盒/退出界面.mp3") as AudioStream
|
|
|
|
|
|
|
|
var audio_manual = preload("res://asset/audio/sfx/game/八音盒/操纵八音盒.mp3") as AudioStream
|
|
|
|
var audio_auto = preload("res://asset/audio/sfx/game/八音盒/自走八音盒.mp3") as AudioStream
|
|
|
|
|
|
|
|
var current_selected_btn := 0:
|
|
|
|
set(value):
|
|
|
|
if has_node("button_root"):
|
|
|
|
# set previous button to normal
|
|
|
|
var root = get_node("button_root")
|
|
|
|
root.get_node(str(current_selected_btn)).texture = button_texture
|
|
|
|
# set current button to highlight
|
|
|
|
root.get_node(str(value)).texture = button_highlight_texture
|
|
|
|
current_selected_btn = value
|
|
|
|
|
2025-01-06 11:13:48 +00:00
|
|
|
var playing_step_sec := 0.5
|
|
|
|
|
2025-01-05 11:25:13 +00:00
|
|
|
|
|
|
|
func _ready() -> void:
|
2025-01-07 10:54:50 +00:00
|
|
|
# debug mode: reset
|
|
|
|
if GlobalConfig.DEBUG:
|
|
|
|
ArchiveManager.archive.bayinhe_mode = "closed"
|
|
|
|
ArchiveManager.archive.bayinhe_current_answer = [0, 0, 0, 0, 0, 0, 0, 0, 0]
|
2025-01-06 11:13:48 +00:00
|
|
|
playing_step_sec = audio_manual.get_length() / playing_steps
|
2025-01-05 11:25:13 +00:00
|
|
|
_chechout_mode()
|
|
|
|
inside_handle.pressed.connect(_on_handle_pressed)
|
2025-01-06 11:13:48 +00:00
|
|
|
handle_wheel.rotated.connect(_on_wheel_rotated)
|
|
|
|
|
|
|
|
# # test close shaking effect
|
2025-01-07 10:54:50 +00:00
|
|
|
# ArchiveManager.archive.bayinhe_mode = "playing"
|
2025-01-06 11:13:48 +00:00
|
|
|
# _chechout_mode(true)
|
|
|
|
# get_tree().create_timer(1.0).timeout.connect(_on_auto_play_finished)
|
2025-01-05 11:25:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
func _chechout_mode(play_sfx := false) -> void:
|
2025-01-07 10:54:50 +00:00
|
|
|
var mode = ArchiveManager.archive.bayinhe_mode
|
2025-01-06 11:13:48 +00:00
|
|
|
inside_handle.visible = mode == "opened"
|
|
|
|
side_handle.visible = mode == "playing" or mode == "finished"
|
2025-01-07 10:54:50 +00:00
|
|
|
$LidHint.visible = mode == "closed"
|
2025-01-05 11:25:13 +00:00
|
|
|
_reset_buttons()
|
|
|
|
match mode:
|
|
|
|
"closed":
|
|
|
|
audio_player.stream = sfx_interact
|
|
|
|
audio_player.play()
|
|
|
|
box.texture = box_closed_texture
|
|
|
|
_reset_buttons()
|
|
|
|
audio_player.stream = sfx_drag
|
|
|
|
"opened":
|
|
|
|
if play_sfx:
|
|
|
|
audio_player.stream = sfx_open
|
|
|
|
audio_player.play()
|
|
|
|
box.texture = box_opened_texture
|
|
|
|
"playing":
|
2025-01-06 11:13:48 +00:00
|
|
|
audio_player.stream = audio_manual
|
2025-01-05 11:25:13 +00:00
|
|
|
box.texture = box_opened_texture
|
|
|
|
"finished":
|
|
|
|
if play_sfx:
|
|
|
|
audio_player.stream = sfx_close
|
|
|
|
audio_player.play()
|
|
|
|
box.texture = box_finished_texture
|
|
|
|
|
|
|
|
|
|
|
|
func _reset_buttons():
|
2025-01-07 10:54:50 +00:00
|
|
|
if ArchiveManager.archive.bayinhe_mode != "closed":
|
2025-01-05 11:25:13 +00:00
|
|
|
if has_node("button_root"):
|
|
|
|
get_node("button_root").visible = false
|
|
|
|
return
|
|
|
|
# create buttons if not exists
|
|
|
|
if not has_node("button_root"):
|
|
|
|
var root = Node2D.new()
|
|
|
|
root.name = "button_root"
|
|
|
|
add_child(root)
|
|
|
|
for i in range(9):
|
|
|
|
var button = Sprite2D.new()
|
|
|
|
button.name = str(i)
|
|
|
|
root.add_child(button)
|
|
|
|
# reset buttons' position and texture
|
|
|
|
for i in range(9):
|
|
|
|
var button = get_node("button_root" + "/" + str(i))
|
|
|
|
if i == current_selected_btn:
|
|
|
|
button.texture = button_highlight_texture
|
|
|
|
else:
|
|
|
|
button.texture = button_texture
|
|
|
|
# load current_answer
|
2025-01-07 10:54:50 +00:00
|
|
|
button.position = _get_position(i, ArchiveManager.archive.bayinhe_current_answer[i])
|
2025-01-05 11:25:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
# 自动计算的位置会有偏差,使用 polygon 的点进行标记
|
|
|
|
# row rom bottom to top, 7 rows in total
|
|
|
|
# col from left to right, 9 cols in total
|
|
|
|
func _get_position(col: int, row: int) -> Vector2:
|
|
|
|
# var col_gap = col_gap_down + (col_gap_up - col_gap_down) * row / 6
|
|
|
|
# return Vector2(origin_position.x + col_gap * col, origin_position.y - row_gap * row)
|
|
|
|
var index = col + row * 9
|
|
|
|
if points_polygon.polygon and index < points_polygon.polygon.size():
|
2025-01-06 08:06:20 +00:00
|
|
|
return points_polygon.position + points_polygon.polygon[index]
|
2025-01-05 11:25:13 +00:00
|
|
|
else:
|
2025-01-06 08:06:20 +00:00
|
|
|
return points_polygon.position
|
2025-01-05 11:25:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
# 1 up 2 down
|
|
|
|
func _move_button(delta: int) -> void:
|
2025-01-07 10:54:50 +00:00
|
|
|
if ArchiveManager.archive.bayinhe_mode != "closed":
|
2025-01-05 11:25:13 +00:00
|
|
|
return
|
2025-01-07 10:54:50 +00:00
|
|
|
var current_row = ArchiveManager.archive.bayinhe_current_answer[current_selected_btn]
|
|
|
|
var target_row = clampi(current_row + delta, 0, 6)
|
|
|
|
ArchiveManager.archive.bayinhe_current_answer[current_selected_btn] = target_row
|
2025-01-05 11:25:13 +00:00
|
|
|
# tween if not in correct position
|
|
|
|
if current_row != target_row:
|
|
|
|
var target_position = _get_position(current_selected_btn, target_row)
|
|
|
|
var node = get_node("button_root/" + str(current_selected_btn))
|
|
|
|
create_tween().tween_property(node, "position", target_position, .5)
|
|
|
|
# play sfx
|
|
|
|
audio_player.stream = sfx_drag
|
|
|
|
audio_player.play()
|
|
|
|
# check if all buttons are in correct position
|
2025-01-07 10:54:50 +00:00
|
|
|
if ArchiveManager.archive.bayinhe_current_answer == answer:
|
|
|
|
ArchiveManager.archive.bayinhe_mode = "opened"
|
2025-01-05 11:25:13 +00:00
|
|
|
_chechout_mode(true)
|
|
|
|
|
|
|
|
|
|
|
|
func _unhandled_input(event: InputEvent) -> void:
|
2025-01-07 10:54:50 +00:00
|
|
|
if event.is_action_pressed("cancel"):
|
2025-01-14 10:20:31 +00:00
|
|
|
var success = ArchiveManager.archive.bayinhe_mode == "finished"
|
|
|
|
exit.emit(success)
|
2025-01-07 10:54:50 +00:00
|
|
|
if ArchiveManager.archive.bayinhe_mode == "closed":
|
2025-01-05 11:25:13 +00:00
|
|
|
# move button
|
|
|
|
if event.is_action_pressed("up"):
|
|
|
|
_move_button(1)
|
|
|
|
elif event.is_action_pressed("down"):
|
|
|
|
_move_button(-1)
|
|
|
|
elif event.is_action_pressed("left"):
|
|
|
|
current_selected_btn = clampi(current_selected_btn - 1, 0, 8)
|
|
|
|
elif event.is_action_pressed("right"):
|
|
|
|
current_selected_btn = clampi(current_selected_btn + 1, 0, 8)
|
|
|
|
|
|
|
|
|
|
|
|
func _on_handle_pressed() -> void:
|
2025-01-06 11:13:48 +00:00
|
|
|
inside_handle.disabled = true
|
|
|
|
$AnimationPlayer.play("handle_animation")
|
|
|
|
|
|
|
|
|
|
|
|
func _checkout_playing():
|
2025-01-07 10:54:50 +00:00
|
|
|
ArchiveManager.archive.bayinhe_mode = "playing"
|
2025-01-06 11:13:48 +00:00
|
|
|
box.z_index = 10
|
|
|
|
var tween = create_tween()
|
|
|
|
audio_player.stream = sfx_assemble
|
|
|
|
audio_player.play()
|
|
|
|
tween.tween_property(inside_handle, "modulate:a", 0, 0.2)
|
|
|
|
tween.tween_callback(func(): _chechout_mode(true))
|
|
|
|
|
|
|
|
|
|
|
|
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:
|
2025-01-07 10:54:50 +00:00
|
|
|
if ArchiveManager.archive.bayinhe_mode != "playing" or accumulated_steps >= playing_steps:
|
2025-01-06 11:13:48 +00:00
|
|
|
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:
|
|
|
|
audio_player.play()
|
|
|
|
else:
|
|
|
|
audio_player.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
|
|
|
|
audio_player.stream_paused = true
|
|
|
|
side_handle.pause()
|
|
|
|
|
|
|
|
|
|
|
|
func _checkout_auto_play():
|
|
|
|
audio_player.stop()
|
|
|
|
audio_player.stream = audio_auto
|
|
|
|
audio_player.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()
|
2025-01-07 10:54:50 +00:00
|
|
|
ArchiveManager.archive.bayinhe_mode = "finished"
|
2025-01-06 11:13:48 +00:00
|
|
|
# 不播放关闭音效,因为自动播放时已经播放过了
|
|
|
|
_chechout_mode(false)
|
|
|
|
# 抖动效果,逐渐减弱
|
|
|
|
var tween = create_tween()
|
2025-01-07 10:54:50 +00:00
|
|
|
var fps := 10.0
|
|
|
|
var duration := 0.4
|
|
|
|
var delta := 6.0
|
2025-01-06 11:13:48 +00:00
|
|
|
var origin_pos = box.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, "position", origin_pos + offset, delta_t).set_trans(
|
|
|
|
Tween.TRANS_CUBIC
|
|
|
|
)
|