Merge remote-tracking branch 'origin/demo'
This commit is contained in:
commit
2f431cff52
@ -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)
|
||||
archive.event_stage["release_stage"] = GlobalConfig.RELEASE_STAGE
|
||||
@ -129,11 +131,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
|
||||
@ -163,7 +165,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
|
||||
|
||||
@ -212,7 +214,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)
|
||||
|
||||
@ -222,15 +224,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:
|
||||
@ -244,9 +246,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:
|
||||
@ -279,7 +281,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:
|
||||
@ -289,7 +291,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()
|
||||
|
||||
@ -297,6 +298,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)
|
||||
|
||||
@ -319,8 +321,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()
|
||||
|
||||
|
||||
|
@ -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")
|
||||
|
314
manager/archive_manager/savings_panel.gd
Normal file
314
manager/archive_manager/savings_panel.gd
Normal 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()
|
1
manager/archive_manager/savings_panel.gd.uid
Normal file
1
manager/archive_manager/savings_panel.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://cirf1nw72l315
|
97
manager/archive_manager/savings_panel.tscn
Normal file
97
manager/archive_manager/savings_panel.tscn
Normal 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
|
@ -2,16 +2,12 @@ class_name GlobalConfig extends Resource
|
||||
|
||||
signal streamer_mode_updated
|
||||
|
||||
#const DEBUG = true
|
||||
static var DEBUG = false
|
||||
# 影响事件的 release_stage
|
||||
# 0:demo
|
||||
# 1:v1.0正式版
|
||||
const RELEASE_STAGE := 1
|
||||
|
||||
# 与 Editor 编辑器有 Debugger 连接
|
||||
# static var EDITOR = false
|
||||
|
||||
# .res would be binary encoded, .tres is text encoded
|
||||
const RES_FILE_FORMAT = ".tres"
|
||||
|
||||
@ -26,7 +22,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
|
||||
@ -74,9 +70,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 # 当前周目数
|
||||
|
@ -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,
|
||||
|
@ -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:
|
||||
@ -356,6 +363,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 +410,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()
|
||||
|
||||
|
@ -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]
|
||||
|
||||
|
@ -69,11 +69,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()
|
||||
|
||||
|
||||
|
@ -6,42 +6,42 @@ 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", # s01_三楼
|
||||
"c03_s02": "uid://ctwy1ubhm68la", # s03_瞎子卧室
|
||||
"c03_s03": "uid://bsqt2c061fmin", # s02_瞎子理发店
|
||||
"c03_s04": "uid://c7c88hg2cl1j7", # s04_李癞房间
|
||||
"c03_s05": "uid://6ehb3ux2kilu", # s05_肉铺
|
||||
"c03_s06": "uid://cxacrp8mrrbry", # s06_胖子卧室
|
||||
"c03_s07": "uid://c67732f2we13j", # s07_屠宰间
|
||||
"c03_s08": "uid://bixdbbyhroepi", # s08_囚室
|
||||
"c03_s09": "uid://dfln301xllqpn", # s09_棺材房
|
||||
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://ctwy1ubhm68la", "name": "瞎子卧室"},
|
||||
"c03_s03": {"path": "uid://bsqt2c061fmin", "name": "瞎子理发店"},
|
||||
"c03_s04": {"path": "uid://c7c88hg2cl1j7", "name": "李癞房间"},
|
||||
"c03_s05": {"path": "uid://6ehb3ux2kilu", "name": "胖子肉铺"},
|
||||
"c03_s06": {"path": "uid://cxacrp8mrrbry", "name": "胖子卧室"},
|
||||
"c03_s07": {"path": "uid://c67732f2we13j", "name": "屠宰间"},
|
||||
"c03_s08": {"path": "uid://bixdbbyhroepi", "name": "囚室"},
|
||||
"c03_s09": {"path": "uid://dfln301xllqpn", "name": "棺材房"},
|
||||
}
|
||||
|
||||
# Exports
|
||||
@ -117,6 +117,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:
|
||||
@ -259,7 +273,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
|
||||
@ -298,4 +312,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))
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user