优化存档加载逻辑与命名方式;读取存档后自动加载场景
This commit is contained in:
parent
9e1e7f82f5
commit
675ae26d6a
10
README.md
10
README.md
@ -18,8 +18,14 @@
|
|||||||
|
|
||||||
## 存档结构
|
## 存档结构
|
||||||
|
|
||||||
- 开发阶段存档:save0
|
- 存档命名为:"save"+三位数字
|
||||||
- 正常继续游戏存档:save1
|
- 如果不足三位数字,则会忽略;超过三位数字不会忽略,可以正常读取
|
||||||
|
- 这是为了方便多存档功能进行文本排序等
|
||||||
|
- 开发阶段 debug 存档:save000
|
||||||
|
- 开发阶段 index 页面进入的游戏存档:save001
|
||||||
|
- 发行后,玩家游戏过程中,自动保存的存档编号范围(最多 10 个,循环保存):000-019
|
||||||
|
- 发行后,玩家游戏过程中,手动保存的存档编号范围(最多 10 个栏位):050-059
|
||||||
|
- 发行后,玩家游戏过程中最多有以上 20 个存档
|
||||||
|
|
||||||
每个场景都有一份 GroundArchive 存档,通过 ArchiveManager.archive.ground_archive() 可以获得。
|
每个场景都有一份 GroundArchive 存档,通过 ArchiveManager.archive.ground_archive() 可以获得。
|
||||||
|
|
||||||
|
@ -285,7 +285,8 @@ func show_dialogue_balloon_scene(balloon_scene, resource: DialogueResource, titl
|
|||||||
balloon_scene = balloon_scene.instantiate()
|
balloon_scene = balloon_scene.instantiate()
|
||||||
|
|
||||||
var balloon: Node = balloon_scene
|
var balloon: Node = balloon_scene
|
||||||
get_current_scene.call().add_child(balloon)
|
if balloon.get_parent() == null:
|
||||||
|
get_current_scene.call().add_child(balloon)
|
||||||
if balloon.has_method(&"start"):
|
if balloon.has_method(&"start"):
|
||||||
balloon.start(resource, title, extra_game_states)
|
balloon.start(resource, title, extra_game_states)
|
||||||
elif balloon.has_method(&"Start"):
|
elif balloon.has_method(&"Start"):
|
||||||
|
@ -9,7 +9,7 @@ static var user_root_dir := "user://data/" # must end with "/"
|
|||||||
static var archive_dir := "user://data/archives/"
|
static var archive_dir := "user://data/archives/"
|
||||||
static var archive_prefix := "save"
|
static var archive_prefix := "save"
|
||||||
|
|
||||||
var archives: Array[int] # archive id list in ascending order
|
var archives := {}
|
||||||
|
|
||||||
var autosave_timer := Timer.new()
|
var autosave_timer := Timer.new()
|
||||||
|
|
||||||
@ -30,7 +30,8 @@ func _ready() -> void:
|
|||||||
if archives.size() == 0:
|
if archives.size() == 0:
|
||||||
create_and_use_new_archive()
|
create_and_use_new_archive()
|
||||||
else:
|
else:
|
||||||
GlobalConfigManager.config.current_selected_archive_id = archives[0]
|
# debug 模式下默认使用 0 号存档
|
||||||
|
GlobalConfigManager.config.current_selected_archive_id = 0
|
||||||
|
|
||||||
|
|
||||||
func _notification(what):
|
func _notification(what):
|
||||||
@ -48,34 +49,35 @@ func _notification(what):
|
|||||||
|
|
||||||
func _on_archive_id_changed():
|
func _on_archive_id_changed():
|
||||||
var selected_id = GlobalConfigManager.config.current_selected_archive_id
|
var selected_id = GlobalConfigManager.config.current_selected_archive_id
|
||||||
|
if selected_id < 0:
|
||||||
|
return
|
||||||
if archive:
|
if archive:
|
||||||
if selected_id != archive.archive_id:
|
if selected_id != archive.archive_id:
|
||||||
ResourceSaver.save(archive)
|
ResourceSaver.save(archive)
|
||||||
archive = null
|
archive = null
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
if selected_id < 0:
|
if not archives.has(selected_id):
|
||||||
return
|
|
||||||
var path = archive_dir + archive_prefix + str(selected_id) + GlobalConfig.RES_FILE_FORMAT
|
|
||||||
archive = ResourceLoader.load(path, "", ResourceLoader.CACHE_MODE_REPLACE_DEEP)
|
|
||||||
if !archive:
|
|
||||||
create_and_use_new_archive(selected_id)
|
create_and_use_new_archive(selected_id)
|
||||||
SceneManager.pop_notification("已创建新存档")
|
SceneManager.pop_notification("已创建新存档")
|
||||||
else:
|
else:
|
||||||
load_archive()
|
load_archive()
|
||||||
# emit signal
|
|
||||||
archive_loaded.emit()
|
|
||||||
|
|
||||||
|
|
||||||
func check_autosave_options():
|
func check_autosave_options():
|
||||||
if not GlobalConfigManager.config.auto_save_enabled:
|
if (
|
||||||
autosave_timer.stop()
|
GlobalConfigManager.config.auto_save_enabled
|
||||||
elif archive and GlobalConfigManager.config.auto_save_seconds > 1:
|
and archive
|
||||||
|
and GlobalConfigManager.config.auto_save_seconds > 1
|
||||||
|
):
|
||||||
# reset left time
|
# reset left time
|
||||||
autosave_timer.stop()
|
autosave_timer.stop()
|
||||||
autosave_timer.one_shot = false
|
autosave_timer.one_shot = false
|
||||||
autosave_timer.wait_time = GlobalConfigManager.config.auto_save_seconds
|
autosave_timer.wait_time = GlobalConfigManager.config.auto_save_seconds
|
||||||
autosave_timer.start()
|
autosave_timer.start()
|
||||||
|
else:
|
||||||
|
autosave_timer.stop()
|
||||||
|
|
||||||
if GlobalConfig.DEBUG:
|
if GlobalConfig.DEBUG:
|
||||||
print(
|
print(
|
||||||
"check_autosave_option: ",
|
"check_autosave_option: ",
|
||||||
@ -106,15 +108,24 @@ func _check_dirs_and_archives() -> bool:
|
|||||||
_handle_load_error("存档目录", "读取")
|
_handle_load_error("存档目录", "读取")
|
||||||
# TODO pop up a dialog to inform the user
|
# TODO pop up a dialog to inform the user
|
||||||
return false
|
return false
|
||||||
archives.clear()
|
|
||||||
var files = archive_dir_access.get_files()
|
var files = archive_dir_access.get_files()
|
||||||
|
files.sort()
|
||||||
# get archive number
|
# get archive number
|
||||||
for file in files:
|
for file in files:
|
||||||
if file.begins_with(archive_prefix):
|
if file.begins_with(archive_prefix) and file.ends_with(GlobalConfig.RES_FILE_FORMAT):
|
||||||
var id_str = file.substr(archive_prefix.length())
|
var id_str = file.get_basename().substr(archive_prefix.length())
|
||||||
|
# 低于三位数的 id 会被忽略
|
||||||
|
if id_str.length() < 3:
|
||||||
|
continue
|
||||||
var id = int(id_str)
|
var id = int(id_str)
|
||||||
archives.append(id)
|
# 读取范围是 0-99
|
||||||
archives.sort()
|
if id < 0 or id > 99:
|
||||||
|
continue
|
||||||
|
var path = archive_dir + file
|
||||||
|
if not archives.has(id):
|
||||||
|
archives[id] = ResourceLoader.load(
|
||||||
|
path, "AssembledArchive", ResourceLoader.CACHE_MODE_REPLACE_DEEP
|
||||||
|
)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
|
||||||
@ -122,23 +133,35 @@ func _check_dirs_and_archives() -> bool:
|
|||||||
func create_and_use_new_archive(id := -1) -> void:
|
func create_and_use_new_archive(id := -1) -> void:
|
||||||
_check_dirs_and_archives()
|
_check_dirs_and_archives()
|
||||||
archive = AssembledArchive.new()
|
archive = AssembledArchive.new()
|
||||||
var archive_path = archive_dir + archive_prefix + str(id) + GlobalConfig.RES_FILE_FORMAT
|
var archive_path = _get_archive_path(id)
|
||||||
if id < 0:
|
if id < 0:
|
||||||
id = 0
|
id = 0
|
||||||
# find a new id
|
# find a new id
|
||||||
archive_path = (archive_dir + archive_prefix + str(id) + GlobalConfig.RES_FILE_FORMAT)
|
archive_path = _get_archive_path(id)
|
||||||
while FileAccess.file_exists(archive_path):
|
while FileAccess.file_exists(archive_path):
|
||||||
id += 1
|
id += 1
|
||||||
archive_path = (archive_dir + archive_prefix + str(id) + GlobalConfig.RES_FILE_FORMAT)
|
archive_path = _get_archive_path(id)
|
||||||
archive.resource_path = archive_path
|
archive.resource_path = archive_path
|
||||||
archive.archive_id = id
|
archive.archive_id = id
|
||||||
archive.created_time = Time.get_datetime_string_from_system(false, true)
|
archive.created_time = Time.get_datetime_string_from_system(false, true)
|
||||||
ResourceSaver.save(archive, archive_path)
|
ResourceSaver.save(archive, archive_path)
|
||||||
archives.append(id)
|
archives[id] = archive
|
||||||
# this will auto trigger signal and load the new archive
|
# this will auto trigger signal and load the new archive
|
||||||
GlobalConfigManager.config.current_selected_archive_id = id
|
GlobalConfigManager.config.current_selected_archive_id = id
|
||||||
|
|
||||||
|
|
||||||
|
# 超过 999 个存档会出问题;不过这个游戏不会有这么多存档
|
||||||
|
func _get_archive_path(id: int) -> String:
|
||||||
|
var id_str := ""
|
||||||
|
if id < 10:
|
||||||
|
id_str = "00" + str(id)
|
||||||
|
elif id < 100:
|
||||||
|
id_str = "0" + str(id)
|
||||||
|
else:
|
||||||
|
id_str = str(id)
|
||||||
|
return archive_dir + archive_prefix + id_str + GlobalConfig.RES_FILE_FORMAT
|
||||||
|
|
||||||
|
|
||||||
func save_all() -> void:
|
func save_all() -> void:
|
||||||
# save config
|
# save config
|
||||||
var config = GlobalConfigManager.config
|
var config = GlobalConfigManager.config
|
||||||
@ -147,6 +170,7 @@ func save_all() -> void:
|
|||||||
|
|
||||||
# player_global_position
|
# player_global_position
|
||||||
var player = SceneManager.get_player() as MainPlayer
|
var player = SceneManager.get_player() as MainPlayer
|
||||||
|
# 在此处保存 player 的位置信息
|
||||||
if archive and player:
|
if archive and player:
|
||||||
archive.player_global_position_x = player.global_position.x
|
archive.player_global_position_x = player.global_position.x
|
||||||
archive.player_direction = player.facing_direction
|
archive.player_direction = player.facing_direction
|
||||||
@ -179,15 +203,14 @@ func load_config() -> void:
|
|||||||
func load_archive() -> void:
|
func load_archive() -> void:
|
||||||
_check_dirs_and_archives()
|
_check_dirs_and_archives()
|
||||||
var selected_id = GlobalConfigManager.config.current_selected_archive_id
|
var selected_id = GlobalConfigManager.config.current_selected_archive_id
|
||||||
|
if archive and selected_id == archive.archive_id:
|
||||||
|
return
|
||||||
if not archives.has(selected_id):
|
if not archives.has(selected_id):
|
||||||
_handle_load_error(str(selected_id) + " 号存档", "查找")
|
_handle_load_error(str(selected_id) + " 号存档", "查找")
|
||||||
return
|
return
|
||||||
var path = archive_dir + archive_prefix + str(selected_id) + GlobalConfig.RES_FILE_FORMAT
|
archive = archives[selected_id]
|
||||||
archive = ResourceLoader.load(path, "", ResourceLoader.CACHE_MODE_REPLACE_DEEP)
|
# emit signal
|
||||||
if !archive:
|
archive_loaded.emit()
|
||||||
_handle_load_error(str(selected_id) + " 号存档", "加载")
|
|
||||||
return
|
|
||||||
archive.resource_path = path
|
|
||||||
check_autosave_options()
|
check_autosave_options()
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,13 +1,22 @@
|
|||||||
class_name AssembledArchive extends Resource
|
class_name AssembledArchive extends Resource
|
||||||
|
|
||||||
@export var archive_id := 0
|
@export var archive_id := 0
|
||||||
@export var entrance_portal := ""
|
# TODO: 注意设置游戏起点
|
||||||
@export var current_scene := "c00_s00":
|
@export var entrance_portal := "left"
|
||||||
|
@export var current_scene := "c01_s05":
|
||||||
set(val):
|
set(val):
|
||||||
current_scene = val
|
current_scene = val
|
||||||
if val and val.length() == 7:
|
if val and val.length() != 7:
|
||||||
current_chapter = int(val.substr(1, 2))
|
printerr("[AssembledArchive] current_scene is not valid: " + val)
|
||||||
current_section = int(val.substr(5))
|
return
|
||||||
|
current_chapter = int(val.substr(1, 2))
|
||||||
|
current_section = int(val.substr(5))
|
||||||
|
# 尝试后台预先加载该场景
|
||||||
|
if GroundLoader.GROUND_SCENE_PATH_DICT.has(val):
|
||||||
|
var path = GroundLoader.GROUND_SCENE_PATH_DICT[val]
|
||||||
|
if GlobalConfig.DEBUG:
|
||||||
|
print("[AssembledArchive] preload scene: " + path)
|
||||||
|
ResourceLoader.load_threaded_request(path, "PackedScene")
|
||||||
# current_chapter and current_section are derived from current_scene
|
# current_chapter and current_section are derived from current_scene
|
||||||
@export var current_chapter := 0
|
@export var current_chapter := 0
|
||||||
@export var current_section := 0
|
@export var current_section := 0
|
||||||
|
@ -19,7 +19,6 @@ const CANVAS_LAYER_HD_ENTITY = 1
|
|||||||
|
|
||||||
const CHARACTER_COLOR_MAP = {
|
const CHARACTER_COLOR_MAP = {
|
||||||
"default": Color.LIGHT_SEA_GREEN,
|
"default": Color.LIGHT_SEA_GREEN,
|
||||||
"音效": Color.DARK_VIOLET, # 非 bug 模式时不显示
|
|
||||||
"吕萍": Color.ORANGE,
|
"吕萍": Color.ORANGE,
|
||||||
"雾": Color.MEDIUM_SEA_GREEN,
|
"雾": Color.MEDIUM_SEA_GREEN,
|
||||||
"获得": Color.WHITE,
|
"获得": Color.WHITE,
|
||||||
|
@ -9,14 +9,6 @@ enum VIBE {
|
|||||||
|
|
||||||
@export var first_entered = true
|
@export var first_entered = true
|
||||||
|
|
||||||
var prop_bag_instance = preload("res://scene/prop/prop_bag.tscn").instantiate()
|
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
|
||||||
add_child(prop_bag_instance)
|
|
||||||
prop_bag_instance.visible = false
|
|
||||||
|
|
||||||
|
|
||||||
#### Ground and Loader ####
|
#### Ground and Loader ####
|
||||||
|
|
||||||
|
|
||||||
@ -133,16 +125,19 @@ func set_player_boundary(rect: Rect2) -> void:
|
|||||||
printerr("Player node not found")
|
printerr("Player node not found")
|
||||||
|
|
||||||
|
|
||||||
|
var balloon_node
|
||||||
|
|
||||||
|
|
||||||
func pop_debug_dialog_info(character: String, content: String):
|
func pop_debug_dialog_info(character: String, content: String):
|
||||||
if GlobalConfig.DEBUG:
|
if GlobalConfig.DEBUG:
|
||||||
|
if not is_instance_valid(balloon_node):
|
||||||
|
balloon_node = preload("res://scene/dialog/balloon_debug.tscn").instantiate()
|
||||||
var title = "title"
|
var title = "title"
|
||||||
var body = "~ " + title + "\n"
|
var body = "~ " + title + "\n"
|
||||||
body += character + ": " + content + "\n"
|
body += character + ": " + content + "\n"
|
||||||
body += "=> END"
|
body += "=> END"
|
||||||
var res = DialogueManager.create_resource_from_text(body)
|
var res = DialogueManager.create_resource_from_text(body)
|
||||||
DialogueManager.show_dialogue_balloon_scene(
|
DialogueManager.show_dialogue_balloon_scene(balloon_node, res, title)
|
||||||
preload("res://scene/dialog/balloon_debug.tscn"), res, title
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
#### Prop ####
|
#### Prop ####
|
||||||
@ -219,14 +214,11 @@ func checkout_index_page():
|
|||||||
get_tree().change_scene_to_packed(preload("res://scene/index_page.tscn"))
|
get_tree().change_scene_to_packed(preload("res://scene/index_page.tscn"))
|
||||||
|
|
||||||
|
|
||||||
|
var prop_bag = preload("res://scene/prop/prop_bag.tscn")
|
||||||
|
|
||||||
|
|
||||||
func show_bag():
|
func show_bag():
|
||||||
freeze_player(0)
|
get_node("/root/Main").add_child(prop_bag.instantiate())
|
||||||
prop_bag_instance.visible = true
|
|
||||||
|
|
||||||
|
|
||||||
func hide_bag():
|
|
||||||
release_player()
|
|
||||||
prop_bag_instance.visible = false
|
|
||||||
|
|
||||||
|
|
||||||
func quit_game():
|
func quit_game():
|
||||||
|
188
scene/dialog/balloon_debug.gd
Normal file
188
scene/dialog/balloon_debug.gd
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
extends CanvasLayer
|
||||||
|
|
||||||
|
@export var force_locale :String:
|
||||||
|
set(val):
|
||||||
|
force_locale = val
|
||||||
|
if force_locale:
|
||||||
|
TranslationServer.set_locale(force_locale)
|
||||||
|
|
||||||
|
@export var auto_play := true
|
||||||
|
|
||||||
|
## The action to use for advancing the dialogue
|
||||||
|
@export var next_action: StringName = &"interact"
|
||||||
|
|
||||||
|
## The action to use to skip typing the dialogue
|
||||||
|
@export var skip_action: StringName = &""
|
||||||
|
@onready var audio_stream_player: AudioStreamPlayer = $AudioStreamPlayer
|
||||||
|
|
||||||
|
@onready var balloon: Control = %Balloon
|
||||||
|
@onready var character_label: RichTextLabel = %CharacterLabel
|
||||||
|
@onready var dialogue_label: DialogueLabel = %DialogueLabel
|
||||||
|
@onready var responses_menu: DialogueResponsesMenu = %ResponsesMenu
|
||||||
|
|
||||||
|
## The dialogue resource
|
||||||
|
var resource: DialogueResource
|
||||||
|
|
||||||
|
## Temporary game states
|
||||||
|
var temporary_game_states: Array = []
|
||||||
|
|
||||||
|
## See if we are waiting for the player
|
||||||
|
var is_waiting_for_input: bool = false
|
||||||
|
|
||||||
|
## See if we are running a long mutation and should hide the balloon
|
||||||
|
var will_hide_balloon: bool = false
|
||||||
|
|
||||||
|
const CHARACTER_COLOR_MAP = {
|
||||||
|
"音效": Color.DARK_VIOLET,
|
||||||
|
"default": Color.LIGHT_SALMON,
|
||||||
|
}
|
||||||
|
|
||||||
|
## The current line
|
||||||
|
var dialogue_line: DialogueLine:
|
||||||
|
set(next_dialogue_line):
|
||||||
|
is_waiting_for_input = false
|
||||||
|
balloon.focus_mode = Control.FOCUS_ALL
|
||||||
|
balloon.grab_focus()
|
||||||
|
# The dialogue has finished so close the balloon
|
||||||
|
if not next_dialogue_line:
|
||||||
|
queue_free()
|
||||||
|
return
|
||||||
|
# If the node isn't ready yet then none of the labels will be ready yet either
|
||||||
|
if not is_node_ready():
|
||||||
|
await ready
|
||||||
|
dialogue_line = next_dialogue_line
|
||||||
|
character_label.visible = not dialogue_line.character.is_empty()
|
||||||
|
character_label.text = tr(dialogue_line.character, "dialogue")
|
||||||
|
#主要角色颜色
|
||||||
|
if CHARACTER_COLOR_MAP.has(dialogue_line.character):
|
||||||
|
character_label.modulate = CHARACTER_COLOR_MAP[dialogue_line.character]
|
||||||
|
else:
|
||||||
|
character_label.modulate = CHARACTER_COLOR_MAP["default"]
|
||||||
|
|
||||||
|
#print(dialogue_line.character,character_label.modulate)
|
||||||
|
|
||||||
|
dialogue_label.hide()
|
||||||
|
_setup_content_text()
|
||||||
|
dialogue_label.dialogue_line = dialogue_line
|
||||||
|
|
||||||
|
responses_menu.hide()
|
||||||
|
responses_menu.set_responses(dialogue_line.responses)
|
||||||
|
|
||||||
|
# Show our balloon
|
||||||
|
balloon.show()
|
||||||
|
will_hide_balloon = false
|
||||||
|
|
||||||
|
dialogue_label.show()
|
||||||
|
if not dialogue_line.text.is_empty():
|
||||||
|
dialogue_label.type_out()
|
||||||
|
|
||||||
|
is_waiting_for_input = true
|
||||||
|
balloon.focus_mode = Control.FOCUS_ALL
|
||||||
|
balloon.grab_focus()
|
||||||
|
await get_tree().create_timer(2.0).timeout
|
||||||
|
next(next_dialogue_line.next_id)
|
||||||
|
|
||||||
|
# 如果当前 line 运行结束,则 queue free 释放资源
|
||||||
|
if dialogue_line == next_dialogue_line:
|
||||||
|
queue_free()
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
layer = GlobalConfig.CANVAS_LAYER_DIALOG
|
||||||
|
balloon.hide()
|
||||||
|
Engine.get_singleton("DialogueManager").mutated.connect(_on_mutated)
|
||||||
|
|
||||||
|
# If the responses menu doesn't have a next action set, use this one
|
||||||
|
if responses_menu.next_action.is_empty():
|
||||||
|
responses_menu.next_action = next_action
|
||||||
|
|
||||||
|
|
||||||
|
# 自定义获得文本,从 tags 中获取备注参数
|
||||||
|
func _setup_content_text() -> void:
|
||||||
|
var translation_key = dialogue_line.translation_key
|
||||||
|
var text
|
||||||
|
if translation_key:
|
||||||
|
text = tr(translation_key, "dialogue")
|
||||||
|
if text == translation_key:
|
||||||
|
text = dialogue_line.text
|
||||||
|
else:
|
||||||
|
text = dialogue_line.text
|
||||||
|
if dialogue_line.tags.has("shake"):
|
||||||
|
# eg. [shake rate=20 level=10][/shake]
|
||||||
|
text = "[shake rate=20 level=6]" + text + "[/shake]"
|
||||||
|
character_label.text = "[shake rate=20 level=6]" + character_label.text + "[/shake]"
|
||||||
|
if dialogue_line.tags.has("wave"):
|
||||||
|
# eg. [wave amp=25 freq=5][/wave]
|
||||||
|
text = "[wave amp=15 freq=5]" + text + "[/wave]"
|
||||||
|
character_label.text = "[wave amp=15 freq=5]" + character_label.text + "[/wave]"
|
||||||
|
if dialogue_line.tags.has("item"):
|
||||||
|
# orange color
|
||||||
|
text = "[color=orange]" + text + "[/color]"
|
||||||
|
dialogue_line.text = text
|
||||||
|
|
||||||
|
|
||||||
|
func _notification(what: int) -> void:
|
||||||
|
# Detect a change of locale and update the current dialogue line to show the new language
|
||||||
|
if what == NOTIFICATION_TRANSLATION_CHANGED and is_instance_valid(dialogue_label):
|
||||||
|
var visible_ratio = dialogue_label.visible_ratio
|
||||||
|
self.dialogue_line = await resource.get_next_dialogue_line(dialogue_line.id)
|
||||||
|
if visible_ratio < 1:
|
||||||
|
dialogue_label.skip_typing()
|
||||||
|
|
||||||
|
## Start some dialogue
|
||||||
|
func start(dialogue_resource: DialogueResource, title: String, extra_game_states: Array = []) -> void:
|
||||||
|
temporary_game_states = [self] + extra_game_states
|
||||||
|
is_waiting_for_input = false
|
||||||
|
resource = dialogue_resource
|
||||||
|
self.dialogue_line = await resource.get_next_dialogue_line(title, temporary_game_states)
|
||||||
|
|
||||||
|
## Go to the next line
|
||||||
|
func next(next_id: String) -> void:
|
||||||
|
self.dialogue_line = await resource.get_next_dialogue_line(next_id, temporary_game_states)
|
||||||
|
|
||||||
|
#region Signals
|
||||||
|
|
||||||
|
func _on_mutated(_mutation: Dictionary) -> void:
|
||||||
|
is_waiting_for_input = false
|
||||||
|
will_hide_balloon = true
|
||||||
|
get_tree().create_timer(0.1).timeout.connect(func():
|
||||||
|
if will_hide_balloon:
|
||||||
|
will_hide_balloon = false
|
||||||
|
balloon.hide()
|
||||||
|
)
|
||||||
|
|
||||||
|
# debug balloon 无需接管输入事件
|
||||||
|
|
||||||
|
# func _unhandled_input(_event: InputEvent) -> void:
|
||||||
|
# # Only the balloon is allowed to handle input while it's showing
|
||||||
|
# get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
|
|
||||||
|
# func _on_balloon_gui_input(event: InputEvent) -> void:
|
||||||
|
# # See if we need to skip typing of the dialogue
|
||||||
|
# if dialogue_label.is_typing:
|
||||||
|
# if event.is_action_pressed("interact") or event.is_action_pressed("cancel"):
|
||||||
|
# dialogue_label.skip_typing()
|
||||||
|
# get_viewport().set_input_as_handled()
|
||||||
|
# return
|
||||||
|
|
||||||
|
# # if not is_waiting_for_input: return
|
||||||
|
# if dialogue_line.responses.size() > 0: return
|
||||||
|
|
||||||
|
# # When there are no response options the balloon itself is the clickable thing
|
||||||
|
# # get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
|
# #if event is InputEventMouseButton and event.is_pressed() and event.button_index == MOUSE_BUTTON_LEFT:
|
||||||
|
# #next(dialogue_line.next_id)
|
||||||
|
# #elif event.is_action_pressed(next_action) and get_viewport().gui_get_focus_owner() == balloon:
|
||||||
|
# #next(dialogue_line.next_id)
|
||||||
|
|
||||||
|
# if event.is_action_pressed("interact") or event.is_action_pressed("cancel"):
|
||||||
|
# # if event.is_action_pressed("interact") and get_viewport().gui_get_focus_owner() == balloon:
|
||||||
|
# get_viewport().set_input_as_handled()
|
||||||
|
# manually_skipped_line.emit()
|
||||||
|
# next(dialogue_line.next_id)
|
||||||
|
|
||||||
|
func _on_responses_menu_response_selected(response: DialogueResponse) -> void:
|
||||||
|
next(response.next_id)
|
||||||
|
|
||||||
|
#endregion
|
@ -1,6 +1,6 @@
|
|||||||
[gd_scene load_steps=10 format=3 uid="uid://bni3dt3xcb72o"]
|
[gd_scene load_steps=10 format=3 uid="uid://bni3dt3xcb72o"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://scene/dialog/balloon.gd" id="1_vvk1n"]
|
[ext_resource type="Script" path="res://scene/dialog/balloon_debug.gd" id="1_jfh0c"]
|
||||||
[ext_resource type="FontFile" uid="uid://dr8bp6p7byb37" path="res://asset/font/字体/方正楷体简体.TTF" id="2_qwe3r"]
|
[ext_resource type="FontFile" uid="uid://dr8bp6p7byb37" path="res://asset/font/字体/方正楷体简体.TTF" id="2_qwe3r"]
|
||||||
[ext_resource type="PackedScene" uid="uid://ckvgyvclnwggo" path="res://addons/dialogue_manager/dialogue_label.tscn" id="3_jo1vi"]
|
[ext_resource type="PackedScene" uid="uid://ckvgyvclnwggo" path="res://addons/dialogue_manager/dialogue_label.tscn" id="3_jo1vi"]
|
||||||
[ext_resource type="Script" path="res://addons/dialogue_manager/dialogue_reponses_menu.gd" id="4_4netn"]
|
[ext_resource type="Script" path="res://addons/dialogue_manager/dialogue_reponses_menu.gd" id="4_4netn"]
|
||||||
@ -58,7 +58,7 @@ Panel/styles/panel = SubResource("StyleBoxEmpty_jydvi")
|
|||||||
|
|
||||||
[node name="Balloon" type="CanvasLayer"]
|
[node name="Balloon" type="CanvasLayer"]
|
||||||
layer = 100
|
layer = 100
|
||||||
script = ExtResource("1_vvk1n")
|
script = ExtResource("1_jfh0c")
|
||||||
metadata/_edit_vertical_guides_ = [-78.0]
|
metadata/_edit_vertical_guides_ = [-78.0]
|
||||||
metadata/_edit_horizontal_guides_ = [276.0]
|
metadata/_edit_horizontal_guides_ = [276.0]
|
||||||
|
|
||||||
@ -134,7 +134,8 @@ theme_override_constants/shadow_offset_x = 1
|
|||||||
theme_override_font_sizes/normal_font_size = 9
|
theme_override_font_sizes/normal_font_size = 9
|
||||||
text = "Dialogue..."
|
text = "Dialogue..."
|
||||||
autowrap_mode = 0
|
autowrap_mode = 0
|
||||||
seconds_per_step = 0.06
|
seconds_per_step = 0.01
|
||||||
|
seconds_per_pause_step = 0.01
|
||||||
|
|
||||||
[node name="Responses" type="MarginContainer" parent="Balloon"]
|
[node name="Responses" type="MarginContainer" parent="Balloon"]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
|
@ -21,18 +21,31 @@ var first_entered := true
|
|||||||
var ground: Ground2D
|
var ground: Ground2D
|
||||||
var display_mask_time = 0.0
|
var display_mask_time = 0.0
|
||||||
|
|
||||||
var scenes_dir = "res://scene/ground/scene/"
|
|
||||||
|
static func _static_init() -> void:
|
||||||
|
_read_grounds()
|
||||||
|
|
||||||
|
|
||||||
# 场景名字映射到路径
|
# 场景名字映射到路径
|
||||||
var ground_scene_path_dict = {}
|
static var GROUND_SCENE_PATH_DICT = {}
|
||||||
|
|
||||||
|
|
||||||
|
static func _read_grounds() -> void:
|
||||||
|
var scenes_dir = "res://scene/ground/scene/"
|
||||||
|
# read grounds
|
||||||
|
var dir = DirAccess.open(scenes_dir)
|
||||||
|
for c_dir in dir.get_directories():
|
||||||
|
var c_path = scenes_dir + c_dir + "/"
|
||||||
|
for s_file in DirAccess.open(c_path).get_files():
|
||||||
|
if s_file.ends_with(".tscn"):
|
||||||
|
var s_path = c_path + s_file
|
||||||
|
GROUND_SCENE_PATH_DICT[c_dir.substr(0, 3) + "_" + s_file.substr(0, 3)] = s_path
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
mask_layer.layer = GlobalConfig.CANVAS_LAYER_GROUND_MASK
|
mask_layer.layer = GlobalConfig.CANVAS_LAYER_GROUND_MASK
|
||||||
mask.visible = true
|
mask.visible = true
|
||||||
mask.color.a = 0.0
|
mask.color.a = 0.0
|
||||||
# grounds
|
|
||||||
_read_grounds()
|
|
||||||
ground = get_node_or_null("Ground") as Ground2D
|
ground = get_node_or_null("Ground") as Ground2D
|
||||||
if ground:
|
if ground:
|
||||||
ground.queue_free()
|
ground.queue_free()
|
||||||
@ -43,22 +56,6 @@ func _ready() -> void:
|
|||||||
transition_to_scene(current_scene, entrance_portal, true)
|
transition_to_scene(current_scene, entrance_portal, true)
|
||||||
|
|
||||||
|
|
||||||
func _read_grounds() -> void:
|
|
||||||
# read grounds
|
|
||||||
var dir = DirAccess.open(scenes_dir)
|
|
||||||
for c_dir in dir.get_directories():
|
|
||||||
var c_path = scenes_dir + c_dir + "/"
|
|
||||||
for s_file in DirAccess.open(c_path).get_files():
|
|
||||||
if s_file.ends_with(".tscn"):
|
|
||||||
var s_path = c_path + s_file
|
|
||||||
ground_scene_path_dict[c_dir.substr(0, 3) + "_" + s_file.substr(0, 3)] = s_path
|
|
||||||
# # 确保每个 ground 都初始化 archive
|
|
||||||
# for key in ground_scene_path_dict.keys():
|
|
||||||
# if GlobalConfig.DEBUG:
|
|
||||||
# print("check ground_archive:", key)
|
|
||||||
# ArchiveManager.archive.ground_archive(key)
|
|
||||||
|
|
||||||
|
|
||||||
func _load_save():
|
func _load_save():
|
||||||
# 强制覆盖 archive 记录
|
# 强制覆盖 archive 记录
|
||||||
if force_archive_scene or force_archive_portal:
|
if force_archive_scene or force_archive_portal:
|
||||||
@ -89,7 +86,7 @@ func _toggle_mask(display: bool, _immediately: bool) -> Tween:
|
|||||||
|
|
||||||
|
|
||||||
func transition_to_scene(scene_name: String, portal: String, immediately: bool) -> void:
|
func transition_to_scene(scene_name: String, portal: String, immediately: bool) -> void:
|
||||||
var scene_path = ground_scene_path_dict.get(scene_name)
|
var scene_path = GROUND_SCENE_PATH_DICT.get(scene_name)
|
||||||
if scene_path:
|
if scene_path:
|
||||||
current_scene = scene_name
|
current_scene = scene_name
|
||||||
entrance_portal = portal
|
entrance_portal = portal
|
||||||
@ -153,9 +150,9 @@ func _update_player_position_from_archive():
|
|||||||
|
|
||||||
|
|
||||||
func _load_ground_node(scene_name: String) -> Node2D:
|
func _load_ground_node(scene_name: String) -> Node2D:
|
||||||
if not ground_scene_path_dict.has(scene_name):
|
if not GROUND_SCENE_PATH_DICT.has(scene_name):
|
||||||
return null
|
return null
|
||||||
var path = ground_scene_path_dict[scene_name]
|
var path = GROUND_SCENE_PATH_DICT[scene_name]
|
||||||
var scene = ResourceLoader.load(path) as PackedScene
|
var scene = ResourceLoader.load(path) as PackedScene
|
||||||
if scene:
|
if scene:
|
||||||
var instance = scene.instantiate() as Node2D
|
var instance = scene.instantiate() as Node2D
|
||||||
@ -177,11 +174,11 @@ func _post_transition():
|
|||||||
var portal = node as Portal2D
|
var portal = node as Portal2D
|
||||||
if not portal or not portal.target_scene:
|
if not portal or not portal.target_scene:
|
||||||
continue
|
continue
|
||||||
if ground_scene_path_dict.has(portal.target_scene):
|
if GROUND_SCENE_PATH_DICT.has(portal.target_scene):
|
||||||
scene_names.append(portal.target_scene)
|
scene_names.append(portal.target_scene)
|
||||||
if scene_names:
|
if scene_names:
|
||||||
for scene_name in scene_names:
|
for scene_name in scene_names:
|
||||||
ResourceLoader.load_threaded_request(ground_scene_path_dict[scene_name])
|
ResourceLoader.load_threaded_request(GROUND_SCENE_PATH_DICT[scene_name])
|
||||||
if GlobalConfig.DEBUG:
|
if GlobalConfig.DEBUG:
|
||||||
print("preload neighbor scenes:", scene_names)
|
print("preload neighbor scenes:", scene_names)
|
||||||
|
|
||||||
@ -192,7 +189,7 @@ var last_modify_time = 0
|
|||||||
|
|
||||||
# DEBUG 时重新加载资源
|
# DEBUG 时重新加载资源
|
||||||
func _watch_scene_update():
|
func _watch_scene_update():
|
||||||
var scene_path = ground_scene_path_dict[current_scene]
|
var scene_path = GROUND_SCENE_PATH_DICT[current_scene]
|
||||||
if scene_path:
|
if scene_path:
|
||||||
last_modify_time = FileAccess.get_modified_time(scene_path)
|
last_modify_time = FileAccess.get_modified_time(scene_path)
|
||||||
if not update_watcher:
|
if not update_watcher:
|
||||||
|
@ -8,6 +8,7 @@ var paper: Interactable2D
|
|||||||
var right_door: Portal2D
|
var right_door: Portal2D
|
||||||
var piano: Interactable2D
|
var piano: Interactable2D
|
||||||
|
|
||||||
|
|
||||||
# 覆盖该方法
|
# 覆盖该方法
|
||||||
func _default_data() -> Dictionary:
|
func _default_data() -> Dictionary:
|
||||||
return {}
|
return {}
|
||||||
@ -44,7 +45,7 @@ func _on_deploy_layer_ready() -> void:
|
|||||||
# 画框未正位时,首先允许互动相框
|
# 画框未正位时,首先允许互动相框
|
||||||
frame.read_note.connect(_on_note_read, CONNECT_ONE_SHOT)
|
frame.read_note.connect(_on_note_read, CONNECT_ONE_SHOT)
|
||||||
ambush.triggered.connect(_on_ambush_triggered)
|
ambush.triggered.connect(_on_ambush_triggered)
|
||||||
|
|
||||||
# 纸片状态
|
# 纸片状态
|
||||||
if frame_relocated:
|
if frame_relocated:
|
||||||
# 画框已经正位,纸片已经被拾取
|
# 画框已经正位,纸片已经被拾取
|
||||||
@ -53,7 +54,7 @@ func _on_deploy_layer_ready() -> void:
|
|||||||
paper.enabled = false
|
paper.enabled = false
|
||||||
right_door.enabled = true
|
right_door.enabled = true
|
||||||
else:
|
else:
|
||||||
# 画框已经正位,纸片未被拾取,直接掉落
|
# 画框已经正位,纸片未被拾取,直接掉落
|
||||||
paper.visible = true
|
paper.visible = true
|
||||||
paper.enabled = true
|
paper.enabled = true
|
||||||
paper.interacted.connect(_on_paper_interacted, CONNECT_ONE_SHOT)
|
paper.interacted.connect(_on_paper_interacted, CONNECT_ONE_SHOT)
|
||||||
@ -94,5 +95,22 @@ func _on_paper_interacted():
|
|||||||
SceneManager.pop_debug_dialog_info("音效", "开门声")
|
SceneManager.pop_debug_dialog_info("音效", "开门声")
|
||||||
$"../sfx_door_open".play()
|
$"../sfx_door_open".play()
|
||||||
|
|
||||||
|
|
||||||
|
# 钢琴音效,每次按下播放不同音符
|
||||||
|
# 最长间隔时间
|
||||||
|
var piano_time_epsilon := 2.0
|
||||||
|
# 钢琴音符最大编号
|
||||||
|
var piano_id_max := 10
|
||||||
|
var piano_last_played_time := 0.0
|
||||||
|
var piano_id := 0
|
||||||
|
|
||||||
|
|
||||||
func _on_piano_interacted():
|
func _on_piano_interacted():
|
||||||
SceneManager.freeze_and_play(0.0, "钢琴")
|
# 播放音符
|
||||||
|
var now = Time.get_ticks_msec()
|
||||||
|
if now - piano_last_played_time < piano_time_epsilon * 1000:
|
||||||
|
piano_id = wrapi(piano_id + 1, 0, piano_id_max)
|
||||||
|
else:
|
||||||
|
piano_id = 0
|
||||||
|
piano_last_played_time = now
|
||||||
|
SceneManager.pop_debug_dialog_info("音效", "钢琴声: " + str(piano_id))
|
||||||
|
@ -51,8 +51,8 @@ func _on_resume_pressed():
|
|||||||
if GlobalConfig.DEBUG:
|
if GlobalConfig.DEBUG:
|
||||||
print("Resume")
|
print("Resume")
|
||||||
if ArchiveManager.archives.has(1):
|
if ArchiveManager.archives.has(1):
|
||||||
|
# 设置 current_selected_archive_id 后,存档会自动加载
|
||||||
GlobalConfigManager.config.current_selected_archive_id = 1
|
GlobalConfigManager.config.current_selected_archive_id = 1
|
||||||
ArchiveManager.load_archive()
|
|
||||||
else:
|
else:
|
||||||
ArchiveManager.create_and_use_new_archive(1)
|
ArchiveManager.create_and_use_new_archive(1)
|
||||||
_enter_main_scene()
|
_enter_main_scene()
|
||||||
|
@ -1,5 +1,24 @@
|
|||||||
extends CanvasLayer
|
extends Panel
|
||||||
|
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
layer = GlobalConfig.CANVAS_LAYER_BAG
|
get_parent().layer = GlobalConfig.CANVAS_LAYER_BAG
|
||||||
|
_load_inventory()
|
||||||
|
SceneManager.lock_player()
|
||||||
|
get_tree().paused = true
|
||||||
|
|
||||||
|
|
||||||
|
func _load_inventory():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
func _unhandled_input(event: InputEvent) -> void:
|
||||||
|
if (
|
||||||
|
event.is_action_pressed("bag")
|
||||||
|
or event.is_action_pressed("cancel")
|
||||||
|
or event.is_action_pressed("escape")
|
||||||
|
):
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
get_tree().paused = false
|
||||||
|
SceneManager.unlock_player()
|
||||||
|
queue_free()
|
||||||
|
@ -4,17 +4,17 @@
|
|||||||
[ext_resource type="Texture2D" uid="uid://dsj3l0baqg1g7" path="res://asset/art/ui/小蝶笔记.png" id="2_3s314"]
|
[ext_resource type="Texture2D" uid="uid://dsj3l0baqg1g7" path="res://asset/art/ui/小蝶笔记.png" id="2_3s314"]
|
||||||
|
|
||||||
[node name="PropBag" type="CanvasLayer"]
|
[node name="PropBag" type="CanvasLayer"]
|
||||||
script = ExtResource("1_f3hpu")
|
process_mode = 3
|
||||||
|
|
||||||
[node name="Control" type="Control" parent="."]
|
[node name="Bag" type="Panel" parent="."]
|
||||||
layout_mode = 3
|
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
|
script = ExtResource("1_f3hpu")
|
||||||
|
|
||||||
[node name="TextureRect" type="TextureRect" parent="Control"]
|
[node name="TextureRect" type="TextureRect" parent="Bag"]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 14
|
anchors_preset = 14
|
||||||
anchor_top = 0.5
|
anchor_top = 0.5
|
||||||
@ -28,7 +28,7 @@ texture = ExtResource("2_3s314")
|
|||||||
expand_mode = 5
|
expand_mode = 5
|
||||||
stretch_mode = 2
|
stretch_mode = 2
|
||||||
|
|
||||||
[node name="GridContainer" type="GridContainer" parent="Control"]
|
[node name="GridContainer" type="GridContainer" parent="Bag"]
|
||||||
custom_minimum_size = Vector2(270, 120)
|
custom_minimum_size = Vector2(270, 120)
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 8
|
anchors_preset = 8
|
||||||
@ -44,66 +44,66 @@ grow_horizontal = 2
|
|||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
columns = 9
|
columns = 9
|
||||||
|
|
||||||
[node name="Panel" type="Panel" parent="Control/GridContainer"]
|
[node name="Panel" type="Panel" parent="Bag/GridContainer"]
|
||||||
custom_minimum_size = Vector2(30, 30)
|
custom_minimum_size = Vector2(30, 30)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Panel2" type="Panel" parent="Control/GridContainer"]
|
[node name="Panel2" type="Panel" parent="Bag/GridContainer"]
|
||||||
custom_minimum_size = Vector2(30, 30)
|
custom_minimum_size = Vector2(30, 30)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Panel3" type="Panel" parent="Control/GridContainer"]
|
[node name="Panel3" type="Panel" parent="Bag/GridContainer"]
|
||||||
custom_minimum_size = Vector2(30, 30)
|
custom_minimum_size = Vector2(30, 30)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Panel4" type="Panel" parent="Control/GridContainer"]
|
[node name="Panel4" type="Panel" parent="Bag/GridContainer"]
|
||||||
custom_minimum_size = Vector2(30, 30)
|
custom_minimum_size = Vector2(30, 30)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Panel5" type="Panel" parent="Control/GridContainer"]
|
[node name="Panel5" type="Panel" parent="Bag/GridContainer"]
|
||||||
custom_minimum_size = Vector2(30, 30)
|
custom_minimum_size = Vector2(30, 30)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Panel6" type="Panel" parent="Control/GridContainer"]
|
[node name="Panel6" type="Panel" parent="Bag/GridContainer"]
|
||||||
custom_minimum_size = Vector2(30, 30)
|
custom_minimum_size = Vector2(30, 30)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Panel7" type="Panel" parent="Control/GridContainer"]
|
[node name="Panel7" type="Panel" parent="Bag/GridContainer"]
|
||||||
custom_minimum_size = Vector2(30, 30)
|
custom_minimum_size = Vector2(30, 30)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Panel8" type="Panel" parent="Control/GridContainer"]
|
[node name="Panel8" type="Panel" parent="Bag/GridContainer"]
|
||||||
custom_minimum_size = Vector2(30, 30)
|
custom_minimum_size = Vector2(30, 30)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Panel9" type="Panel" parent="Control/GridContainer"]
|
[node name="Panel9" type="Panel" parent="Bag/GridContainer"]
|
||||||
custom_minimum_size = Vector2(30, 30)
|
custom_minimum_size = Vector2(30, 30)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Panel10" type="Panel" parent="Control/GridContainer"]
|
[node name="Panel10" type="Panel" parent="Bag/GridContainer"]
|
||||||
custom_minimum_size = Vector2(30, 30)
|
custom_minimum_size = Vector2(30, 30)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Panel11" type="Panel" parent="Control/GridContainer"]
|
[node name="Panel11" type="Panel" parent="Bag/GridContainer"]
|
||||||
custom_minimum_size = Vector2(30, 30)
|
custom_minimum_size = Vector2(30, 30)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Panel12" type="Panel" parent="Control/GridContainer"]
|
[node name="Panel12" type="Panel" parent="Bag/GridContainer"]
|
||||||
custom_minimum_size = Vector2(30, 30)
|
custom_minimum_size = Vector2(30, 30)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Panel13" type="Panel" parent="Control/GridContainer"]
|
[node name="Panel13" type="Panel" parent="Bag/GridContainer"]
|
||||||
custom_minimum_size = Vector2(30, 30)
|
custom_minimum_size = Vector2(30, 30)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Panel14" type="Panel" parent="Control/GridContainer"]
|
[node name="Panel14" type="Panel" parent="Bag/GridContainer"]
|
||||||
custom_minimum_size = Vector2(30, 30)
|
custom_minimum_size = Vector2(30, 30)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Panel15" type="Panel" parent="Control/GridContainer"]
|
[node name="Panel15" type="Panel" parent="Bag/GridContainer"]
|
||||||
custom_minimum_size = Vector2(30, 30)
|
custom_minimum_size = Vector2(30, 30)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Panel16" type="Panel" parent="Control/GridContainer"]
|
[node name="Panel16" type="Panel" parent="Bag/GridContainer"]
|
||||||
custom_minimum_size = Vector2(30, 30)
|
custom_minimum_size = Vector2(30, 30)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
[ext_resource type="Texture2D" uid="uid://d03la4d2swk0k" path="res://asset/art/ui/hud/pressed_right.png" id="11_a512b"]
|
[ext_resource type="Texture2D" uid="uid://d03la4d2swk0k" path="res://asset/art/ui/hud/pressed_right.png" id="11_a512b"]
|
||||||
|
|
||||||
[node name="PropHUD" type="Control"]
|
[node name="PropHUD" type="Control"]
|
||||||
|
process_mode = 3
|
||||||
custom_minimum_size = Vector2(600, 500)
|
custom_minimum_size = Vector2(600, 500)
|
||||||
layout_mode = 3
|
layout_mode = 3
|
||||||
anchors_preset = 0
|
anchors_preset = 0
|
||||||
|
@ -87,8 +87,9 @@ func _on_autosave_box_toggled(is_pressed: bool) -> void:
|
|||||||
|
|
||||||
func _on_autosave_time_edit_text_submitted(_text = null) -> void:
|
func _on_autosave_time_edit_text_submitted(_text = null) -> void:
|
||||||
var seconds = autosave_time_edit.text.to_int()
|
var seconds = autosave_time_edit.text.to_int()
|
||||||
if seconds < 0:
|
# limit the value, at least 10 seconds
|
||||||
seconds = 0
|
if seconds < 10:
|
||||||
|
seconds = 10
|
||||||
autosave_time_edit.text = str(seconds)
|
autosave_time_edit.text = str(seconds)
|
||||||
GlobalConfigManager.config.auto_save_seconds = seconds
|
GlobalConfigManager.config.auto_save_seconds = seconds
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user