增加 debug panel(连按五次「`」开启debug,下次进入时重置)

This commit is contained in:
cakipaul 2025-07-16 23:20:55 +08:00
parent e42b434bed
commit a60beeea1d
12 changed files with 523 additions and 64 deletions

View File

@ -10,8 +10,8 @@ const ARCHIVE_ID_MAX = 99
const ARCHIVE_ID_DIGITS = 3
# Static paths
static var user_root_dir := "user://data/" # must end with "/"
static var archive_dir := "user://data/archives/"
static var user_data_root_dir := "user://data/" # must end with "/"
static var user_archives_dir := "user://data/archives/"
static var archive_prefix := "save"
# Archive management
@ -19,6 +19,8 @@ var archive: AssembledArchive:
set(val):
archive = val
if archive:
# emit signal
archive_loaded.emit()
GlobalConfigManager.print_global_info()
print("use archive ", archive.resource_path)
@ -124,11 +126,11 @@ func _try_auto_save() -> void:
func _check_dirs_and_archives() -> bool:
# Ensure directories exist
_ensure_directory_exists(user_root_dir)
_ensure_directory_exists(archive_dir)
_ensure_directory_exists(user_data_root_dir)
_ensure_directory_exists(user_archives_dir)
# Check if the archive directory is accessible
var archive_dir_access = DirAccess.open(archive_dir)
var archive_dir_access = DirAccess.open(user_archives_dir)
if not archive_dir_access:
_handle_load_error("存档目录", "读取")
return false
@ -158,7 +160,7 @@ func _load_existing_archives(dir_access: DirAccess) -> void:
var note = archive_info.note
archives_notes_dict[id] = note
if not archives_dict.has(id):
var archive_resource = _load_archive_resource(archive_dir + file)
var archive_resource = _load_archive_resource(user_archives_dir + file)
if archive_resource:
archives_dict[id] = archive_resource
@ -207,7 +209,7 @@ func create_and_use_new_archive(id := -1) -> void:
_create_and_save_new_archive_resoure(id)
else:
# 如果 id 大于等于 0创建指定 id 的存档
var archive_path = _get_archive_path(id)
var archive_path = get_archive_path(id)
var take_over_path = FileAccess.file_exists(archive_path)
_create_and_save_new_archive_resoure(id, take_over_path)
@ -217,15 +219,15 @@ func create_and_use_new_archive(id := -1) -> void:
func _find_next_available_id() -> int:
var id = 0
var archive_path = _get_archive_path(id)
var archive_path = get_archive_path(id)
while FileAccess.file_exists(archive_path) and id <= ARCHIVE_ID_MAX:
id += 1
archive_path = _get_archive_path(id)
archive_path = get_archive_path(id)
return id
func _create_and_save_new_archive_resoure(id: int, take_over_path := false) -> void:
var archive_path = _get_archive_path(id)
var archive_path = get_archive_path(id)
archive = AssembledArchive.new() as Resource
archive.version = CURRENT_VERSION
if take_over_path:
@ -239,9 +241,9 @@ func _create_and_save_new_archive_resoure(id: int, take_over_path := false) -> v
# 超过 999 个存档会出问题;不过这个游戏不会有这么多存档
func _get_archive_path(id: int) -> String:
func get_archive_path(id: int) -> String:
var id_str := str(id).pad_zeros(ARCHIVE_ID_DIGITS)
return archive_dir + archive_prefix + id_str + GlobalConfig.RES_FILE_FORMAT
return user_archives_dir + archive_prefix + id_str + GlobalConfig.RES_FILE_FORMAT
func allow_resume(id := 1) -> bool:
@ -274,7 +276,7 @@ func _save_player_state() -> void:
func load_config() -> void:
if GlobalConfigManager.config:
return
var path = user_root_dir + "config" + GlobalConfig.RES_FILE_FORMAT
var path = user_data_root_dir + "config" + GlobalConfig.RES_FILE_FORMAT
if FileAccess.file_exists(path):
var loaded_config = ResourceLoader.load(path)
if is_instance_valid(loaded_config) and loaded_config.version >= CURRENT_VERSION:
@ -284,7 +286,6 @@ func load_config() -> void:
if not GlobalConfigManager.config:
_create_default_config(path)
GlobalConfigManager.config.resource_path = path
if not Engine.is_editor_hint():
_connect_config_signals()
@ -292,6 +293,7 @@ func load_config() -> void:
func _create_default_config(path: String) -> void:
var config = GlobalConfig.new()
config.version = CURRENT_VERSION
config.resource_path = path
GlobalConfigManager.config = config
ResourceSaver.save(config, path)
@ -314,8 +316,6 @@ func load_archive() -> void:
_handle_load_error(str(selected_id) + " 号存档", "查找")
return
archive = archives_dict[selected_id]
# emit signal
archive_loaded.emit()
check_autosave_options()

View File

@ -11,8 +11,8 @@ class_name AssembledArchive extends Resource
printerr("[AssembledArchive] current_scene is not valid: " + val)
return
# 尝试后台预先加载该场景
if GroundLoader.GROUND_SCENE_PATH_DICT.has(val):
var path = GroundLoader.GROUND_SCENE_PATH_DICT[val]
var path = GroundLoader.get_ground_scene_uid(val)
if path:
if GlobalConfig.DEBUG:
print("[AssembledArchive] current_scene: " + current_scene)
ResourceLoader.load_threaded_request(path, "PackedScene")

View File

@ -0,0 +1,314 @@
extends Control
# UI References
@onready var quit_debug_button: Button = %QuitDebugModeButton
@onready var archive_grid: GridContainer = %ArchiveGrid
@onready var scroll_container: ScrollContainer = %ScrollContainer
@onready var current_archive_label: Label = %CurrentArchiveLabel
@onready var name_input: LineEdit = %NameInput
@onready var save_button: Button = %SaveButton
@onready var refresh_button: Button = %RefreshButton
# Constants
const MAX_MANUAL_ARCHIVES = 99
const GRID_COLUMNS = 4
# Variables
var manual_archives: Dictionary = {} # {id: {name: String, path: String, time: String}}
var next_available_id: int = 2 # Start from 2 since 1 is reserved for main archive
func _ready() -> void:
get_parent().layer = GlobalConfig.CANVAS_LAYER_SETTINGS
# Setup UI
archive_grid.columns = GRID_COLUMNS
# Connect signals
quit_debug_button.pressed.connect(_on_quit_debug_button_pressed)
save_button.pressed.connect(_on_save_button_pressed)
refresh_button.pressed.connect(_refresh_archive_list)
name_input.text_submitted.connect(_on_name_submitted)
# Set default name
_update_default_name()
# Initial load
_refresh_archive_list()
_update_current_archive_label()
func _update_current_archive_label() -> void:
current_archive_label.text = "当前使用存档1号存档主存档"
func _update_default_name() -> void:
name_input.placeholder_text = "输入存档名称"
var chapter_name = EventManager.get_chapter_stage()
if chapter_name == 1:
chapter_name = "序章"
elif chapter_name <= 5:
chapter_name = "%s" % (chapter_name - 1)
elif chapter_name == 6:
chapter_name = "结尾"
else:
chapter_name = "未知"
var scene_name = SceneManager.get_current_scene_name()
var saving_name = chapter_name + "_" + scene_name
name_input.text = _get_unique_archive_name(saving_name)
func _on_name_submitted(_text: String) -> void:
_on_save_button_pressed()
func _on_quit_debug_button_pressed() -> void:
# 不写入配置
GlobalConfig.DEBUG = false
quit()
func _on_save_button_pressed() -> void:
# Check limit
if manual_archives.size() >= MAX_MANUAL_ARCHIVES:
_show_notification("已达到最大存档数量限制99个")
return
# Get and validate name
var archive_name = name_input.text.strip_edges()
if archive_name.is_empty():
archive_name = "未命名存档_" + Time.get_datetime_string_from_system()
# Save current progress
ArchiveManager.save_all()
# Get unique name
archive_name = _get_unique_archive_name(archive_name)
# Copy current archive
_copy_current_archive(archive_name)
# Reset input field
_update_default_name()
name_input.select_all()
func _get_unique_archive_name(base_name: String) -> String:
var final_name = base_name
var counter = 1
# Check if name already exists
var name_exists = true
while name_exists:
name_exists = false
for data in manual_archives.values():
if data.name == final_name:
name_exists = true
final_name = base_name + "_" + str(counter)
counter += 1
break
return final_name
func _copy_current_archive(archive_name: String) -> void:
# Get current archive path
var current_archive = ArchiveManager.archive
if not current_archive:
_show_notification("当前没有活动存档")
return
# Find next available ID
while manual_archives.has(next_available_id) and next_available_id <= MAX_MANUAL_ARCHIVES + 1:
next_available_id += 1
if next_available_id > MAX_MANUAL_ARCHIVES + 1:
_show_notification("无法创建更多存档")
return
# Create new archive path
var new_archive_path = (
ArchiveManager.user_archives_dir
+ "manual_"
+ str(next_available_id)
+ "_"
+ archive_name.validate_filename()
+ GlobalConfig.RES_FILE_FORMAT
)
# Copy the archive file
var source_path = current_archive.resource_path
var dir = DirAccess.open(ArchiveManager.user_archives_dir)
if dir:
var error = dir.copy(source_path, new_archive_path)
print("Copying archive from: ", source_path, " to: ", new_archive_path)
if error == OK:
# Save manual archive info
manual_archives[next_available_id] = {
"name": archive_name,
"path": new_archive_path,
"time": Time.get_datetime_string_from_system(false, true)
}
# Save manual archives data
_save_manual_archives_data()
# Refresh UI
_refresh_archive_list()
_show_notification("存档已保存:" + archive_name)
next_available_id += 1
else:
_show_notification("存档复制失败:" + error_string(error))
else:
_show_notification("无法访问存档目录")
func _refresh_archive_list() -> void:
# refresh savings name
_update_default_name()
# Clear existing items
for child in archive_grid.get_children():
child.queue_free()
# Load manual archives data
_load_manual_archives_data()
# Create UI items for each manual archive
var sorted_ids = manual_archives.keys()
sorted_ids.sort()
for id in sorted_ids:
var data = manual_archives[id]
_create_archive_item(id, data)
func _create_archive_item(id: int, data: Dictionary) -> void:
# Create container for the archive item
var item_container = PanelContainer.new()
item_container.custom_minimum_size = Vector2(200, 60)
var vbox = VBoxContainer.new()
vbox.add_theme_constant_override("separation", 4)
item_container.add_child(vbox)
# Archive name (editable)
var name_edit = LineEdit.new()
name_edit.text = data.name
name_edit.tooltip_text = "创建时间:" + data.time
vbox.add_child(name_edit)
# Time label
var time_label = Label.new()
time_label.text = data.time
time_label.add_theme_font_size_override("font_size", 12)
time_label.modulate.a = 0.7
vbox.add_child(time_label)
# Action buttons container
var button_container = HBoxContainer.new()
button_container.add_theme_constant_override("separation", 4)
# Load button
var load_btn = Button.new()
load_btn.text = "加载"
load_btn.size_flags_horizontal = Control.SIZE_EXPAND_FILL
button_container.add_child(load_btn)
# Delete button
var delete_btn = Button.new()
delete_btn.text = "删除"
delete_btn.size_flags_horizontal = Control.SIZE_EXPAND_FILL
button_container.add_child(delete_btn)
vbox.add_child(button_container)
# Connect signals
name_edit.text_changed.connect(func(new_text): _on_archive_renamed(id, new_text))
load_btn.pressed.connect(func(): _load_manual_archive(id, data))
delete_btn.pressed.connect(func(): _delete_manual_archive(id))
archive_grid.add_child(item_container)
func _on_archive_renamed(id: int, new_name: String) -> void:
if new_name.strip_edges().is_empty():
return
# Update name in data
manual_archives[id].name = new_name.strip_edges()
# Save changes
_save_manual_archives_data()
func _load_manual_archive(_id: int, data: Dictionary) -> void:
# Save current state first
ArchiveManager.save_all()
# Copy manual archive to archive 1
var dir = DirAccess.open(ArchiveManager.user_archives_dir)
if dir:
var saving_archive = load(data.path)
if saving_archive:
var target_path = ArchiveManager.get_archive_path(1)
print("Loading archive from: ", data.path, " to: ", target_path)
GlobalConfigManager.config.current_selected_archive_id = 1
if ArchiveManager.archive:
saving_archive.take_over_path(target_path)
else:
saving_archive.resource_path = target_path
ArchiveManager.archives_dict[1] = saving_archive
ArchiveManager.archive = saving_archive
print("Loading archive from: ", data.path, " to: ", target_path)
# Reload current scene
get_tree().reload_current_scene()
else:
_show_notification("加载存档失败:" + data.name)
func _delete_manual_archive(id: int) -> void:
var data = manual_archives[id]
# Delete file
var dir = DirAccess.open(ArchiveManager.user_archives_dir)
if dir:
dir.remove(data.path)
# Remove from dictionary
manual_archives.erase(id)
# Save changes
_save_manual_archives_data()
# Refresh UI
_refresh_archive_list()
_show_notification("已删除存档:" + data.name)
func _save_manual_archives_data() -> void:
var save_path = ArchiveManager.user_data_root_dir + "test_manual_archives.dat"
var file = FileAccess.open(save_path, FileAccess.WRITE)
if file:
file.store_var(manual_archives)
file.close()
func _load_manual_archives_data() -> void:
var save_path = ArchiveManager.user_data_root_dir + "test_manual_archives.dat"
if FileAccess.file_exists(save_path):
var file = FileAccess.open(save_path, FileAccess.READ)
if file:
manual_archives = file.get_var()
file.close()
# Find next available ID
next_available_id = 2
for id in manual_archives.keys():
if id >= next_available_id:
next_available_id = id + 1
func _show_notification(message: String) -> void:
SceneManager.pop_notification(message)
func _enter_tree() -> void:
SceneManager.toggle_pause_counter(true, "savings")
func _exit_tree() -> void:
SceneManager.toggle_pause_counter(false, "savings")
func quit() -> void:
queue_free()
func _unhandled_input(event: InputEvent) -> void:
# savings 界面接受所有输入事件
get_viewport().set_input_as_handled()
if (
event.is_action_pressed("savings")
or event.is_action_pressed("cancel")
or event.is_action_pressed("escape")
):
quit()

View File

@ -0,0 +1 @@
uid://cirf1nw72l315

View File

@ -0,0 +1,97 @@
[gd_scene load_steps=2 format=3 uid="uid://d4jeeteyq8kk3"]
[ext_resource type="Script" uid="uid://cirf1nw72l315" path="res://manager/archive_manager/savings_panel.gd" id="1_oo2ip"]
[node name="SavingsLayer" type="CanvasLayer"]
[node name="TestArchivePanel" type="PanelContainer" parent="."]
process_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 59.0
offset_top = 21.0
offset_right = 395.0
offset_bottom = 262.0
grow_horizontal = 2
grow_vertical = 2
scale = Vector2(0.5, 0.5)
script = ExtResource("1_oo2ip")
[node name="VBoxContainer" type="VBoxContainer" parent="TestArchivePanel"]
layout_mode = 2
[node name="HBoxContainer" type="HBoxContainer" parent="TestArchivePanel/VBoxContainer"]
layout_mode = 2
[node name="Title" type="Label" parent="TestArchivePanel/VBoxContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_font_sizes/font_size = 24
text = "存档测试管理器(测试专用)"
horizontal_alignment = 1
[node name="QuitDebugModeButton" type="Button" parent="TestArchivePanel/VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
text = "关闭 Debug 模式"
[node name="存档管理" type="VBoxContainer" parent="TestArchivePanel/VBoxContainer"]
layout_mode = 2
theme_override_constants/separation = 10
[node name="CurrentArchiveLabel" type="Label" parent="TestArchivePanel/VBoxContainer/存档管理"]
unique_name_in_owner = true
layout_mode = 2
theme_override_colors/font_color = Color(0.7, 0.9, 0.7, 1)
text = "当前使用存档1号存档主存档"
[node name="HSeparator" type="HSeparator" parent="TestArchivePanel/VBoxContainer/存档管理"]
layout_mode = 2
[node name="SaveContainer" type="HBoxContainer" parent="TestArchivePanel/VBoxContainer/存档管理"]
layout_mode = 2
theme_override_constants/separation = 10
[node name="NameInput" type="LineEdit" parent="TestArchivePanel/VBoxContainer/存档管理/SaveContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
placeholder_text = "输入存档名称"
[node name="SaveButton" type="Button" parent="TestArchivePanel/VBoxContainer/存档管理/SaveContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(120, 0)
layout_mode = 2
text = "保存当前进度"
[node name="RefreshButton" type="Button" parent="TestArchivePanel/VBoxContainer/存档管理/SaveContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(60, 0)
layout_mode = 2
text = "刷新"
[node name="HSeparator2" type="HSeparator" parent="TestArchivePanel/VBoxContainer/存档管理"]
layout_mode = 2
[node name="Label" type="Label" parent="TestArchivePanel/VBoxContainer/存档管理"]
layout_mode = 2
theme_override_colors/font_color = Color(0.8, 0.8, 0.8, 1)
text = "手动存档列表:"
[node name="ScrollContainer" type="ScrollContainer" parent="TestArchivePanel/VBoxContainer/存档管理"]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 380)
layout_mode = 2
size_flags_vertical = 3
horizontal_scroll_mode = 0
vertical_scroll_mode = 2
[node name="ArchiveGrid" type="GridContainer" parent="TestArchivePanel/VBoxContainer/存档管理/ScrollContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/h_separation = 10
theme_override_constants/v_separation = 10
columns = 4

View File

@ -1,9 +1,6 @@
class_name GlobalConfig extends Resource
#const DEBUG = true
static var DEBUG = false
# 与 Editor 编辑器有 Debugger 连接
# static var EDITOR = false
# .res would be binary encoded, .tres is text encoded
const RES_FILE_FORMAT = ".tres"
@ -19,7 +16,7 @@ const CAPTION_OPTIONS_DICT = {0: ["上海话", "普通话"], 1: [""]}
const CAPTION_LOCALES_DICT = {0: ["zh_SH", "zh_CN"], 1: ["en"]}
## layers
# 设置
# 设置, DebugPanel
const CANVAS_LAYER_SETTINGS = 30
# note
const CANVAS_LAYER_NOTE = 25
@ -67,9 +64,9 @@ signal current_selected_archive_id_changed
signal auto_save_enabled_changed
signal auto_save_seconds_changed
@export var version: int #存档版本
@export var debug_mode := false # 开启 debug 模式
@export var skip_trailer := false # 跳过 trailer
@export var version: int #存档版本
@export var game_launched_times := 0 # 启动游戏次数
@export var game_total_seconds := 0 # 游戏总时长
@export var game_rounds := 1 # 当前周目数

View File

@ -31,7 +31,6 @@ static func _set_config(val: GlobalConfig) -> void:
config = val
if not config or Engine.is_editor_hint():
return
_apply_debug_mode()
_apply_window_settings()
_apply_audio_settings()
@ -46,12 +45,10 @@ static func _apply_debug_mode() -> void:
static func _apply_window_settings() -> void:
var window = Engine.get_main_loop().root.get_window()
if config.window_fullscreen:
window.mode = Window.MODE_EXCLUSIVE_FULLSCREEN
else:
window.mode = Window.MODE_WINDOWED
window.always_on_top = config.window_top
@ -65,7 +62,6 @@ static func _apply_audio_settings() -> void:
AudioServer.set_bus_volume_db(
AudioServer.get_bus_index(GlobalConfig.BUS_DIALOG), config.db_dialog
)
prints(
"config load volume_db settings (master, sfx, dialog): ",
config.db_master,

View File

@ -53,6 +53,13 @@ func get_ground() -> Ground2D:
return null
func get_current_scene_name() -> String:
var ground = get_ground()
if ground:
return GroundLoader.get_ground_scene_readable_name(ground.scene_name)
return ""
func get_camera_marker() -> CameraFocusMarker:
var ground = get_ground()
if ground:
@ -175,6 +182,7 @@ func checkout_prop_inventory(character: String) -> void:
else:
printerr("checkout_prop_inventory: PropHud node not found")
# 无效参数
func get_current_prop(_must_selected = null) -> String:
var prop_hud = get_prop_hud()
@ -356,6 +364,15 @@ func enter_main_scene() -> void:
get_tree().paused = false
#### Savings
var savings_scene = preload("uid://d4jeeteyq8kk3")
func show_savings() -> void:
get_tree().current_scene.add_child(savings_scene.instantiate())
#### UX: settings; panel: note, bag, memory ####
var panel_scene = preload("uid://ddlwnsccsmr8u")
@ -394,7 +411,7 @@ func show_settings() -> void:
#### 游戏场景树暂停计数器设置、memory、bag 等菜单都会导致 pause
## 目前有(5类): settings, panel, bag, note, memory
## 目前有(6类): settings, panel, bag, note, memory, savings
var pause_counter_arr: Array[String] = []
var pause_counter_mutex := Mutex.new()

View File

@ -211,6 +211,11 @@ panel={
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":80,"key_label":0,"unicode":112,"location":0,"echo":false,"script":null)
]
}
savings={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":96,"key_label":0,"unicode":96,"location":0,"echo":false,"script":null)
]
}
[internationalization]

View File

@ -71,11 +71,11 @@ func _ready() -> void:
if restarting:
print("restarting: skip ground _ready()")
return
_setup_scene()
_validate_scene_name()
_set_camera_and_player_boundary()
if Engine.is_editor_hint():
return
_setup_scene()
_setup_runtime()

View File

@ -6,36 +6,36 @@ const MIN_TRANSITION_TIME := 0.6
const EASE_DURATION := 0.3
# Scene name to path mapping
const GROUND_SCENE_PATH_DICT = {
"c01_s05": "uid://dlx5xxbg53rb8",
"c01_s06": "uid://bx16c8nn32f40",
"c01_s07": "uid://ds2iyfndwamiy",
"c01_s08": "uid://cwu4dhayra8pg",
"c01_s09": "uid://c777lv8mjojcw",
"c01_s10": "uid://be57l2o3vxxtm",
"c01_s11": "uid://coiumaaenimbc",
"c01_s12": "uid://bol5hl68pbpgq",
"c02_s01": "uid://bbs7yy5aofw1v",
"c02_s02": "uid://brck77w81fhvc",
"c02_s03": "uid://djc2uaefhmu7",
"c02_s04": "uid://bivc5cdap370p",
"c02_s05": "uid://cp8d3ag5nbjq0",
"c02_s06": "uid://cootarwb44vvh",
"c02_s07": "uid://t4xjt774ngwh",
"c02_s08": "uid://ce2vyyg2reg52",
"c02_s09": "uid://ryups1dnwdto",
"c02_s10": "uid://dny21yhtuteap",
"c02_s11": "uid://dq41rvwl5hyrk", # 注:该场景合并在了 c02_s03 院子中
"c02_s12": "uid://da4cuf2i3nwpj",
"c02_s13": "uid://bvjutch6jex0v",
"c02_s14": "uid://d0p4x5st2r315",
"c02_s15": "uid://b21p53g42j2nt",
"c02_s16": "uid://22hc3oe8t0id",
"c02_s17": "uid://cbr6gbgrl2wb1",
"c02_s18": "uid://d27gv3pbkn4b8",
"c03_s01": "uid://dlrbhfvnd3cs0", # 三楼
"c03_s02": "uid://rkro7u5wd3t1", # 三楼内侧
"c03_s03": "uid://bsqt2c061fmin", # 瞎子理发店
const GROUND_SCENE_PATH_DICT: Dictionary[String, Dictionary] = {
"c01_s05": {"path": "uid://dlx5xxbg53rb8", "name": "院长房间"},
"c01_s06": {"path": "uid://bx16c8nn32f40", "name": "孤儿院长廊"},
"c01_s07": {"path": "uid://ds2iyfndwamiy", "name": "书店外"},
"c01_s08": {"path": "uid://cwu4dhayra8pg", "name": "书店"},
"c01_s09": {"path": "uid://c777lv8mjojcw", "name": "公寓楼外"},
"c01_s10": {"path": "uid://be57l2o3vxxtm", "name": "公寓楼道"},
"c01_s11": {"path": "uid://coiumaaenimbc", "name": "黄包车"},
"c01_s12": {"path": "uid://bol5hl68pbpgq", "name": "诡异书店外"},
"c02_s01": {"path": "uid://bbs7yy5aofw1v", "name": "公寓门口"},
"c02_s02": {"path": "uid://brck77w81fhvc", "name": "公寓楼道"},
"c02_s03": {"path": "uid://djc2uaefhmu7", "name": "一楼院子"},
"c02_s04": {"path": "uid://bivc5cdap370p", "name": "一楼保卫科"},
"c02_s05": {"path": "uid://cp8d3ag5nbjq0", "name": "一楼内侧楼道"},
"c02_s06": {"path": "uid://cootarwb44vvh", "name": "二楼楼道"},
"c02_s07": {"path": "uid://t4xjt774ngwh", "name": "二楼内侧楼道"},
"c02_s08": {"path": "uid://ce2vyyg2reg52", "name": "瞎子卧室"},
"c02_s09": {"path": "uid://ryups1dnwdto", "name": "裂缝空间"},
"c02_s10": {"path": "uid://dny21yhtuteap", "name": "空房间"},
"c02_s11": {"path": "uid://dq41rvwl5hyrk", "name": "一楼火灾"},
"c02_s12": {"path": "uid://da4cuf2i3nwpj", "name": "盒子猫安全屋"},
"c02_s13": {"path": "uid://bvjutch6jex0v", "name": "盒子猫二楼"},
"c02_s14": {"path": "uid://d0p4x5st2r315", "name": "盒子猫二楼内侧"},
"c02_s15": {"path": "uid://b21p53g42j2nt", "name": "盒子猫一楼内侧"},
"c02_s16": {"path": "uid://22hc3oe8t0id", "name": "盒子猫三楼内侧"},
"c02_s17": {"path": "uid://cbr6gbgrl2wb1", "name": "盒子猫三楼"},
"c02_s18": {"path": "uid://d27gv3pbkn4b8", "name": "盒子猫一楼"},
"c03_s01": {"path": "uid://dlrbhfvnd3cs0", "name": "三楼楼道"},
"c03_s02": {"path": "uid://rkro7u5wd3t1", "name": "三楼内侧"},
"c03_s03": {"path": "uid://bsqt2c061fmin", "name": "瞎子理发店"}
}
# Exports
@ -111,6 +111,20 @@ func _load_save() -> void:
entrance_portal = archive.entrance_portal
static func get_ground_scene_uid(scene_name: String) -> String:
if GROUND_SCENE_PATH_DICT.has(scene_name):
return GROUND_SCENE_PATH_DICT[scene_name]["path"]
printerr("GroundLoader get_ground_scene_uid: scene not found:", scene_name)
return ""
static func get_ground_scene_readable_name(scene_name: String) -> String:
if GROUND_SCENE_PATH_DICT.has(scene_name):
return GROUND_SCENE_PATH_DICT[scene_name]["name"]
printerr("GroundLoader get_ground_scene_readable_name: scene not found:", scene_name)
return scene_name
func toggle_mask(
display: bool, wait_time: float, ease_min_duration := EASE_DURATION, mask_color := Color.BLACK
) -> Tween:
@ -253,7 +267,7 @@ func _update_player_position_from_archive() -> void:
func _load_ground_node(scene_name: String) -> Ground2D:
if not GROUND_SCENE_PATH_DICT.has(scene_name):
return null
var path = GROUND_SCENE_PATH_DICT[scene_name]
var path = get_ground_scene_uid(scene_name)
var scene: PackedScene = _load_scene_resource(path)
if not scene:
return null
@ -292,4 +306,4 @@ func _preload_neighbor_scenes() -> void:
print("preload neighbor scenes:", scene_names)
for scene_name in scene_names:
if GROUND_SCENE_PATH_DICT.has(scene_name):
ResourceLoader.load_threaded_request(GROUND_SCENE_PATH_DICT[scene_name])
ResourceLoader.load_threaded_request(get_ground_scene_uid(scene_name))

View File

@ -16,6 +16,10 @@ func _ready() -> void:
# settings.exited.connect(grab_focus)
var debug_button_last_press_msec := 0
var debug_button_pressed_count := 0
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("escape"):
get_viewport().set_input_as_handled()
@ -33,6 +37,23 @@ func _unhandled_input(event: InputEvent) -> void:
elif event.is_action_pressed("panel"):
get_viewport().set_input_as_handled()
SceneManager.show_panel()
elif event.is_action_pressed("savings"):
if GlobalConfig.DEBUG:
get_viewport().set_input_as_handled()
SceneManager.show_savings()
else:
# 连续按 5 次开启 debug mode
var time = Time.get_ticks_msec()
if time - debug_button_last_press_msec < 500:
debug_button_pressed_count += 1
else:
debug_button_pressed_count = 1
debug_button_last_press_msec = time
if debug_button_pressed_count >= 5:
get_viewport().set_input_as_handled()
# 不写入配置
GlobalConfig.DEBUG = true
SceneManager.show_savings()
var vignette_tween: Tween
@ -48,6 +69,3 @@ func tween_vignette(ratio := 0.5, duration := 0.3, color := Color.RED):
vignette_tween.parallel().tween_property(
vignette.material, "shader_parameter/vignette_rgb", color, duration
)