extends CanvasLayer signal exit(passed: bool) # answer for the puzzle, by columns # @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 @export var debug_reload := false: set(value): if value: debug_reload = false _chechout_mode() @onready var points_polygon = $ButtonPositionPoints as Polygon2D @onready var box = $Box as TextureRect @onready var side_handle = $Box/SideHandle as AnimatedSprite2D @onready var inside_handle = $InsideHandle as TextureButton @onready var audio_player = $MusicPlayer2D as AudioStreamPlayer2D @onready var handle_wheel = $Wheel 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 var sfx_assemble = preload("res://asset/audio/sfx/game/八音盒/装上插销.mp3") as AudioStream 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 var playing_step_sec := 0.5 func _ready() -> void: layer = GlobalConfig.LAYER_LITTLE_GAME # 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] playing_step_sec = audio_manual.get_length() / playing_steps if visible: _chechout_mode() inside_handle.pressed.connect(_on_handle_pressed) handle_wheel.rotated.connect(_on_wheel_rotated) visibility_changed.connect(_on_visibility_changed) # # test close shaking effect # ArchiveManager.archive.bayinhe_mode = "playing" # _chechout_mode(true) # get_tree().create_timer(1.0).timeout.connect(_on_auto_play_finished) func _on_visibility_changed() -> void: if visible: _chechout_mode() else: audio_player.stop() func _chechout_mode(play_sfx := false) -> void: var mode = ArchiveManager.archive.bayinhe_mode inside_handle.visible = mode == "opened" side_handle.visible = mode == "playing" or mode == "finished" $LidHint.visible = mode == "closed" _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": audio_player.stream = audio_manual 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(): if ArchiveManager.archive.bayinhe_mode != "closed": 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 button.position = _get_position(i, ArchiveManager.archive.bayinhe_current_answer[i]) # 自动计算的位置会有偏差,使用 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(): return points_polygon.position + points_polygon.polygon[index] else: return points_polygon.position # 1 up 2 down func _move_button(delta: int) -> void: if ArchiveManager.archive.bayinhe_mode != "closed": return 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 # 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 if ArchiveManager.archive.bayinhe_current_answer == answer: ArchiveManager.archive.bayinhe_mode = "opened" _chechout_mode(true) func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("cancel"): var success = ArchiveManager.archive.bayinhe_mode == "finished" exit.emit(success) if ArchiveManager.archive.bayinhe_mode == "closed": # 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: inside_handle.disabled = true $AnimationPlayer.play("handle_animation") func _checkout_playing(): ArchiveManager.archive.bayinhe_mode = "playing" 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: if ArchiveManager.archive.bayinhe_mode != "playing" 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: 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() ArchiveManager.archive.bayinhe_mode = "finished" # 不播放关闭音效,因为自动播放时已经播放过了 _chechout_mode(false) # 抖动效果,逐渐减弱 var tween = create_tween() var fps := 10.0 var duration := 0.4 var delta := 6.0 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 )