From a60beeea1db9ba9f83061717ed6a0eb1d5414be8 Mon Sep 17 00:00:00 2001 From: cakipaul Date: Wed, 16 Jul 2025 23:20:55 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20debug=20panel=EF=BC=88?= =?UTF-8?q?=E8=BF=9E=E6=8C=89=E4=BA=94=E6=AC=A1=E3=80=8C`=E3=80=8D?= =?UTF-8?q?=E5=BC=80=E5=90=AFdebug=EF=BC=8C=E4=B8=8B=E6=AC=A1=E8=BF=9B?= =?UTF-8?q?=E5=85=A5=E6=97=B6=E9=87=8D=E7=BD=AE=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manager/archive_manager/archive_manager.gd | 32 +- manager/archive_manager/assembled_archive.gd | 4 +- manager/archive_manager/savings_panel.gd | 314 ++++++++++++++++++ manager/archive_manager/savings_panel.gd.uid | 1 + manager/archive_manager/savings_panel.tscn | 97 ++++++ manager/config_manager/global_config.gd | 7 +- .../config_manager/global_config_manager.gd | 4 - manager/scene/scene_manager.gd | 19 +- project.godot | 5 + scene/ground/ground.gd | 2 +- scene/ground/ground_loader.gd | 78 +++-- scene/main.gd | 24 +- 12 files changed, 523 insertions(+), 64 deletions(-) create mode 100644 manager/archive_manager/savings_panel.gd create mode 100644 manager/archive_manager/savings_panel.gd.uid create mode 100644 manager/archive_manager/savings_panel.tscn diff --git a/manager/archive_manager/archive_manager.gd b/manager/archive_manager/archive_manager.gd index 4b3300a1..ad71c5fa 100644 --- a/manager/archive_manager/archive_manager.gd +++ b/manager/archive_manager/archive_manager.gd @@ -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() diff --git a/manager/archive_manager/assembled_archive.gd b/manager/archive_manager/assembled_archive.gd index 000bb1ad..b93961fa 100644 --- a/manager/archive_manager/assembled_archive.gd +++ b/manager/archive_manager/assembled_archive.gd @@ -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") diff --git a/manager/archive_manager/savings_panel.gd b/manager/archive_manager/savings_panel.gd new file mode 100644 index 00000000..5051a041 --- /dev/null +++ b/manager/archive_manager/savings_panel.gd @@ -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() diff --git a/manager/archive_manager/savings_panel.gd.uid b/manager/archive_manager/savings_panel.gd.uid new file mode 100644 index 00000000..2bd49f06 --- /dev/null +++ b/manager/archive_manager/savings_panel.gd.uid @@ -0,0 +1 @@ +uid://cirf1nw72l315 diff --git a/manager/archive_manager/savings_panel.tscn b/manager/archive_manager/savings_panel.tscn new file mode 100644 index 00000000..053547bb --- /dev/null +++ b/manager/archive_manager/savings_panel.tscn @@ -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 diff --git a/manager/config_manager/global_config.gd b/manager/config_manager/global_config.gd index d341bb47..9fb472ee 100644 --- a/manager/config_manager/global_config.gd +++ b/manager/config_manager/global_config.gd @@ -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 # 当前周目数 diff --git a/manager/config_manager/global_config_manager.gd b/manager/config_manager/global_config_manager.gd index 33561e57..98f28d98 100644 --- a/manager/config_manager/global_config_manager.gd +++ b/manager/config_manager/global_config_manager.gd @@ -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, diff --git a/manager/scene/scene_manager.gd b/manager/scene/scene_manager.gd index 2b41b879..e98b0858 100644 --- a/manager/scene/scene_manager.gd +++ b/manager/scene/scene_manager.gd @@ -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() diff --git a/project.godot b/project.godot index 78503047..40c9c88a 100644 --- a/project.godot +++ b/project.godot @@ -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] diff --git a/scene/ground/ground.gd b/scene/ground/ground.gd index 9a8487c8..54382e40 100644 --- a/scene/ground/ground.gd +++ b/scene/ground/ground.gd @@ -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() diff --git a/scene/ground/ground_loader.gd b/scene/ground/ground_loader.gd index f70ffd63..a00dd3ed 100644 --- a/scene/ground/ground_loader.gd +++ b/scene/ground/ground_loader.gd @@ -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)) diff --git a/scene/main.gd b/scene/main.gd index 9201e109..45767ca6 100644 --- a/scene/main.gd +++ b/scene/main.gd @@ -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 ) - - -