2025-02-09 11:43:35 +00:00
|
|
|
|
@tool
|
2025-02-05 12:34:38 +00:00
|
|
|
|
extends CanvasLayer
|
|
|
|
|
|
|
|
|
|
signal exiting
|
|
|
|
|
signal success
|
2025-01-07 10:54:50 +00:00
|
|
|
|
|
|
|
|
|
const NON_SELECTED = [-1, -1]
|
2025-01-08 07:59:27 +00:00
|
|
|
|
|
|
|
|
|
@export var shuffle_times := 20
|
2025-02-09 11:43:35 +00:00
|
|
|
|
@export var debug_relocate := false:
|
|
|
|
|
set(value):
|
|
|
|
|
debug_relocate = false
|
|
|
|
|
if Engine.is_editor_hint():
|
|
|
|
|
_init_answer_and_connect_signals()
|
|
|
|
|
_measure_width_by_row()
|
|
|
|
|
for row in range(3):
|
|
|
|
|
_relocate_books(row)
|
2025-01-07 10:54:50 +00:00
|
|
|
|
|
|
|
|
|
@onready var sfx_select = $SfxSelect as Sfx
|
|
|
|
|
@onready var sfx_interchange = $SfxInterchange as Sfx
|
|
|
|
|
|
2025-01-08 07:59:27 +00:00
|
|
|
|
var current_answer := []
|
2025-01-07 10:54:50 +00:00
|
|
|
|
var book_width_by_row := []
|
|
|
|
|
var suffling := false
|
|
|
|
|
# [row, col]
|
|
|
|
|
var selected_book := NON_SELECTED:
|
|
|
|
|
set(value):
|
|
|
|
|
if selected_book == value:
|
|
|
|
|
return
|
2025-02-09 11:43:35 +00:00
|
|
|
|
# 在 suffling 时不允许改变 selected_book
|
2025-01-07 10:54:50 +00:00
|
|
|
|
if suffling:
|
|
|
|
|
selected_book = value
|
|
|
|
|
return
|
|
|
|
|
if selected_book != NON_SELECTED:
|
|
|
|
|
_toggle_book(false, selected_book[0], selected_book[1])
|
|
|
|
|
if value != NON_SELECTED:
|
|
|
|
|
_toggle_book(true, value[0], value[1])
|
|
|
|
|
selected_book = value
|
2025-02-09 11:43:35 +00:00
|
|
|
|
var gameover = false
|
2025-01-07 10:54:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _ready() -> void:
|
2025-03-20 10:00:53 +00:00
|
|
|
|
layer = GlobalConfig.CANVAS_LAYER_LITTLE_GAME
|
2025-01-08 07:59:27 +00:00
|
|
|
|
# init answer first
|
|
|
|
|
_init_answer_and_connect_signals()
|
2025-02-09 11:43:35 +00:00
|
|
|
|
if Engine.is_editor_hint():
|
|
|
|
|
return
|
2025-01-07 10:54:50 +00:00
|
|
|
|
_measure_width_by_row()
|
|
|
|
|
# shuffle at the end
|
2025-02-09 11:43:35 +00:00
|
|
|
|
_shuffle_books()
|
2025-01-08 07:59:27 +00:00
|
|
|
|
|
2025-06-15 05:11:41 +00:00
|
|
|
|
func _enter_tree() -> void:
|
|
|
|
|
SceneManager.pop_center_notification(tr("ui_press_q_to_exit"))
|
2025-01-08 07:59:27 +00:00
|
|
|
|
|
|
|
|
|
func _init_answer_and_connect_signals() -> void:
|
2025-02-09 11:43:35 +00:00
|
|
|
|
current_answer.clear()
|
2025-01-08 07:59:27 +00:00
|
|
|
|
for row in range(3):
|
|
|
|
|
var r_size = get_node("./Shelf/Layer" + str(row)).get_child_count()
|
|
|
|
|
# current_answer append a r_size arr
|
|
|
|
|
var arr = []
|
|
|
|
|
for id in range(r_size):
|
|
|
|
|
arr.append(id)
|
|
|
|
|
var book = _get_book_by_id(row, id)
|
2025-02-09 11:43:35 +00:00
|
|
|
|
if not Engine.is_editor_hint():
|
|
|
|
|
book.get_node("BookButton").pressed.connect(_on_book_pressed.bind(row, id))
|
2025-01-08 07:59:27 +00:00
|
|
|
|
current_answer.append(arr)
|
2025-01-07 10:54:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _measure_width_by_row() -> void:
|
2025-02-09 11:43:35 +00:00
|
|
|
|
# 书本间的额外间隙,用于调整书本之间的间隔
|
2025-02-10 00:30:47 +00:00
|
|
|
|
var extra_gap = [2., 3., 2.]
|
2025-02-09 11:43:35 +00:00
|
|
|
|
book_width_by_row = []
|
2025-01-07 10:54:50 +00:00
|
|
|
|
for row in range(3):
|
|
|
|
|
var length = current_answer[row].size()
|
|
|
|
|
var width_arr = []
|
|
|
|
|
for col in range(length):
|
|
|
|
|
var book_btn = _get_book_by_id(row, col).get_node("BookButton") as TextureButton
|
2025-02-09 11:43:35 +00:00
|
|
|
|
width_arr.append(book_btn.texture_normal.get_width() + extra_gap[row])
|
2025-01-07 10:54:50 +00:00
|
|
|
|
book_width_by_row.append(width_arr)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _shuffle_books() -> void:
|
|
|
|
|
selected_book = NON_SELECTED
|
2025-02-09 11:43:35 +00:00
|
|
|
|
if GlobalConfig.DEBUG:
|
|
|
|
|
SceneManager.pop_debug_dialog_info(
|
|
|
|
|
"备注", "debug模式每列随机洗牌 1 次,正常模式每列随机洗牌 " + str(shuffle_times) + " 次"
|
|
|
|
|
)
|
2025-04-20 17:36:44 +00:00
|
|
|
|
shuffle_times = 1
|
2025-01-07 10:54:50 +00:00
|
|
|
|
suffling = true
|
2025-01-08 07:59:27 +00:00
|
|
|
|
rand_from_seed(Time.get_ticks_usec())
|
2025-01-07 10:54:50 +00:00
|
|
|
|
for row in range(3):
|
|
|
|
|
# shuffle each row 20 times
|
|
|
|
|
var r_size = current_answer[row].size()
|
2025-01-08 07:59:27 +00:00
|
|
|
|
for _i in range(shuffle_times):
|
2025-01-07 10:54:50 +00:00
|
|
|
|
var col_1 = randi() % r_size
|
|
|
|
|
var col_2 = randi() % r_size
|
|
|
|
|
selected_book = [row, col_1]
|
|
|
|
|
_interchange_book(row, col_2, false)
|
|
|
|
|
_relocate_books(row)
|
|
|
|
|
# turn off initilazing after shuffle
|
|
|
|
|
suffling = false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _on_book_pressed(row: int, id: int) -> void:
|
2025-02-09 11:43:35 +00:00
|
|
|
|
if gameover:
|
|
|
|
|
return
|
2025-01-07 10:54:50 +00:00
|
|
|
|
var col = current_answer[row].find(id)
|
|
|
|
|
if selected_book == NON_SELECTED:
|
|
|
|
|
selected_book = [row, col]
|
|
|
|
|
else:
|
|
|
|
|
if selected_book == [row, col]:
|
|
|
|
|
selected_book = NON_SELECTED
|
|
|
|
|
elif selected_book[0] == row:
|
|
|
|
|
_interchange_book(row, col)
|
|
|
|
|
else:
|
|
|
|
|
selected_book = [row, col]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _get_book_by_id(row: int, id: int) -> Node2D:
|
|
|
|
|
return get_node("./Shelf/Layer" + str(row) + "/Book" + str(id))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# func _get_book_by_pos(row: int, col: int) -> Node2D:
|
|
|
|
|
# var layer = get_node("./Shelf/Layer" + str(row))
|
|
|
|
|
# return layer.get_child(col)
|
|
|
|
|
|
2025-01-08 07:59:27 +00:00
|
|
|
|
var mute_toggle = false
|
|
|
|
|
|
2025-01-07 10:54:50 +00:00
|
|
|
|
|
|
|
|
|
func _toggle_book(selected: bool, row: int, col: int) -> void:
|
|
|
|
|
var id = current_answer[row][col]
|
|
|
|
|
var book = _get_book_by_id(row, id)
|
|
|
|
|
var book_tween = create_tween()
|
2025-01-08 07:59:27 +00:00
|
|
|
|
if not mute_toggle:
|
|
|
|
|
sfx_select.play()
|
2025-01-07 10:54:50 +00:00
|
|
|
|
if selected:
|
|
|
|
|
book_tween.parallel().tween_property(book, "position:y", -10.0, 0.2)
|
|
|
|
|
else:
|
|
|
|
|
book_tween.parallel().tween_property(book, "position:y", 0.0, 0.2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _interchange_book(row: int, col: int, relocate := true) -> void:
|
|
|
|
|
if selected_book == NON_SELECTED or selected_book[0] != row:
|
|
|
|
|
return
|
|
|
|
|
var col2 = selected_book[1]
|
|
|
|
|
sfx_interchange.play()
|
|
|
|
|
# interchange current_answer
|
|
|
|
|
var temp = current_answer[row][col]
|
|
|
|
|
current_answer[row][col] = current_answer[row][col2]
|
|
|
|
|
current_answer[row][col2] = temp
|
|
|
|
|
# reset selected_book
|
2025-01-08 07:59:27 +00:00
|
|
|
|
mute_toggle = true
|
2025-01-07 10:54:50 +00:00
|
|
|
|
selected_book = NON_SELECTED
|
2025-01-08 07:59:27 +00:00
|
|
|
|
mute_toggle = false
|
2025-01-07 10:54:50 +00:00
|
|
|
|
# relocate after reset selected_book
|
|
|
|
|
if relocate:
|
|
|
|
|
_relocate_books(row)
|
|
|
|
|
# check answer
|
2025-03-19 16:07:00 +00:00
|
|
|
|
if relocate:
|
|
|
|
|
_check_answer()
|
2025-01-07 10:54:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _relocate_books(row: int) -> void:
|
|
|
|
|
selected_book = NON_SELECTED
|
|
|
|
|
var r_size = current_answer[row].size()
|
|
|
|
|
var x = 0
|
|
|
|
|
for col in range(r_size):
|
|
|
|
|
var id = current_answer[row][col]
|
|
|
|
|
var book = _get_book_by_id(row, id)
|
|
|
|
|
book.position.x = x
|
|
|
|
|
book.position.y = 0.0
|
|
|
|
|
x += book_width_by_row[row][id]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _check_answer() -> void:
|
2025-01-08 07:59:27 +00:00
|
|
|
|
# 第一行需要顺序排列
|
|
|
|
|
var row1 = current_answer[0]
|
|
|
|
|
for col in range(row1.size()):
|
|
|
|
|
if row1[col] != col:
|
|
|
|
|
return
|
2025-02-10 10:59:19 +00:00
|
|
|
|
# 第二行需要正序排列
|
2025-01-08 07:59:27 +00:00
|
|
|
|
var row2 = current_answer[1]
|
|
|
|
|
var size2 = row2.size()
|
|
|
|
|
if row2[0] == 0:
|
|
|
|
|
# 正序
|
|
|
|
|
for col in range(1, size2):
|
|
|
|
|
if row2[col] != col:
|
2025-01-07 10:54:50 +00:00
|
|
|
|
return
|
2025-01-08 07:59:27 +00:00
|
|
|
|
else:
|
2025-02-10 10:59:19 +00:00
|
|
|
|
return
|
|
|
|
|
# # 倒序
|
|
|
|
|
# for col in range(size2):
|
|
|
|
|
# if row2[col] != size2 - 1 - col:
|
|
|
|
|
# return
|
2025-02-09 11:43:35 +00:00
|
|
|
|
# 第三行正序或者倒序都可以
|
|
|
|
|
var _row3 = current_answer[2]
|
|
|
|
|
var _size3 = _row3.size()
|
|
|
|
|
if _row3[0] == 0:
|
|
|
|
|
# 正序
|
|
|
|
|
for col in range(1, _size3):
|
|
|
|
|
if _row3[col] != col:
|
|
|
|
|
return
|
|
|
|
|
else:
|
|
|
|
|
# 倒序
|
|
|
|
|
for col in range(_size3):
|
|
|
|
|
if _row3[col] != _size3 - 1 - col:
|
|
|
|
|
return
|
|
|
|
|
# # 最后一行按色块排列;0-6 蓝色(7个)在一起,7-11 红色(5个)在一起,12-17 黄色(6个)在一起
|
|
|
|
|
# var row3 = current_answer[2]
|
|
|
|
|
# # 0: blue, 1: red, 2: yellow
|
|
|
|
|
# var visited = [7, 5, 6]
|
|
|
|
|
# var visiting_init = true
|
|
|
|
|
# var visiting = -1
|
|
|
|
|
# for col in range(row3.size()):
|
|
|
|
|
# var color = _get_color(row3[col])
|
|
|
|
|
# if visiting_init:
|
|
|
|
|
# visiting = color
|
|
|
|
|
# visiting_init = false
|
|
|
|
|
# if color != visiting:
|
|
|
|
|
# return
|
|
|
|
|
# visited[color] -= 1
|
|
|
|
|
# if visited[color] == 0:
|
|
|
|
|
# visiting_init = true
|
2025-01-07 10:54:50 +00:00
|
|
|
|
# success
|
|
|
|
|
_success()
|
|
|
|
|
|
|
|
|
|
|
2025-01-08 07:59:27 +00:00
|
|
|
|
# 0: blue, 1: red, 2: yellow
|
|
|
|
|
func _get_color(index: int) -> int:
|
|
|
|
|
# 0-6 蓝色(7个)在一起,7-11 红色(5个)在一起,12-17 黄色(6个)在一起
|
|
|
|
|
if index < 7:
|
|
|
|
|
return 0
|
|
|
|
|
elif index < 12:
|
|
|
|
|
return 1
|
|
|
|
|
else:
|
|
|
|
|
return 2
|
|
|
|
|
|
|
|
|
|
|
2025-01-07 10:54:50 +00:00
|
|
|
|
func _success() -> void:
|
2025-02-09 11:43:35 +00:00
|
|
|
|
gameover = true
|
2025-03-10 12:57:20 +00:00
|
|
|
|
# 生死簿倒下的动画
|
2025-05-31 06:45:34 +00:00
|
|
|
|
$"Shelf/Layer0/Book17/BookButton".hide()
|
2025-03-10 12:57:20 +00:00
|
|
|
|
$"Shelf/生死簿".show()
|
|
|
|
|
$"Shelf/生死簿".play("书架倒下")
|
|
|
|
|
$"Shelf/生死簿/Button".pressed.connect(_on_target_book_pressed, CONNECT_ONE_SHOT)
|
2025-04-20 13:35:13 +00:00
|
|
|
|
$"Sfx书本掉落".play()
|
2025-03-10 12:57:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _on_target_book_pressed():
|
|
|
|
|
$"Shelf/生死簿".play("书架扶正")
|
|
|
|
|
await get_tree().create_timer(1.5).timeout
|
2025-02-09 11:43:35 +00:00
|
|
|
|
$"生死簿".show()
|
|
|
|
|
# 完成后释放信号
|
|
|
|
|
$"生死簿".finished.connect(_on_success_finished)
|
2025-04-20 13:35:13 +00:00
|
|
|
|
$"Sfx扶正书本".play()
|
2025-02-09 11:43:35 +00:00
|
|
|
|
|
2025-02-12 07:03:41 +00:00
|
|
|
|
|
2025-02-09 11:43:35 +00:00
|
|
|
|
# 生死簿阅读完成后
|
|
|
|
|
func _on_success_finished() -> void:
|
|
|
|
|
# exit
|
2025-02-12 07:03:41 +00:00
|
|
|
|
# exiting.emit()
|
|
|
|
|
success.emit()
|
2025-02-09 11:43:35 +00:00
|
|
|
|
get_parent().remove_child(self)
|
2025-02-05 12:34:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _unhandled_input(event: InputEvent) -> void:
|
2025-06-13 08:03:19 +00:00
|
|
|
|
if event.is_action_pressed("cancel"):
|
2025-02-12 07:03:41 +00:00
|
|
|
|
if not gameover:
|
2025-06-13 08:03:19 +00:00
|
|
|
|
get_viewport().set_input_as_handled()
|
2025-02-12 07:03:41 +00:00
|
|
|
|
exiting.emit()
|
|
|
|
|
get_parent().remove_child(self)
|
2025-06-13 08:03:19 +00:00
|
|
|
|
elif event.is_action_pressed("interact"):
|
|
|
|
|
# 阻塞交互输入
|
|
|
|
|
get_viewport().set_input_as_handled()
|