D5/Autoload/GameState.gd

227 lines
10 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# GameState.gd
# Autoload Singleton
# 负责存储和管理核心游戏状态。
# 使用一个中央字典 (_state_data) 来存储所有状态,提高灵活性和可维护性。
# 状态的键名直接来源于初始化数据 (Init_Base.json) 或后续动态添加。
extends Node
# --- 状态信号 ---
# 当游戏状态通过 initialize_for_new_game 初始化完成时发出
signal state_initialized(initial_state: Dictionary)
# 当游戏状态通过 restore_state 从存档恢复完成时发出
signal state_restored(restored_state: Dictionary)
# 当任何状态值通过 set_value 发生改变时发出
signal state_value_changed(key: String, new_value)
# [兼容性信号] 当 "time" 状态值被设置或更新时发出 (保持与旧代码或特定UI的兼容)
signal date_changed(new_date: Dictionary)
# --- 内部状态变量 ---
# 核心状态字典,存储所有动态游戏数据
var _state_data: Dictionary = {}
# 注意:内部的 game_pause 和 is_on_task 变量已被移除,
# 相关状态现在存储在 _state_data["system_status"] 中。
# --- 状态初始化与恢复 ---
# 为新游戏初始化状态。
# 直接使用传入的 initial_settings 字典填充状态。
func initialize_for_new_game(initial_settings: Dictionary) -> void:
print("GameState: Initializing state directly from initial settings...")
# 使用深拷贝 (duplicate(true)),确保 GameState 拥有独立的数据副本
_state_data = initial_settings.duplicate(true)
# 在 _state_data 初始化后,调用 add_init_data 来填充 "task_info"
add_init_data() # 调用修改后的函数
print("GameState: State initialized. Data:", _state_data)
# 发出通用初始化完成信号,传递状态副本
emit_signal("state_initialized", _state_data.duplicate(true))
# [兼容性] 如果存在 "time" 键,则发出 date_changed 信号
if _state_data.has("time") and typeof(_state_data["time"]) == TYPE_DICTIONARY:
emit_signal("date_changed", _state_data["time"].duplicate(true))
# 从加载的数据恢复状态。
# 直接使用 loaded_data 覆盖当前状态。
func restore_state(loaded_data: Dictionary) -> void:
print("GameState: Restoring state from loaded data...")
# 同样建议使用深拷贝,虽然从存档加载的数据理论上已经是独立的
_state_data = loaded_data.duplicate(true)
print("GameState: State restored. Data:", _state_data)
# 发出通用恢复完成信号,传递状态副本
emit_signal("state_restored", _state_data.duplicate(true))
# [兼容性] 如果存在 "time" 键,则发出 date_changed 信号
if _state_data.has("time") and typeof(_state_data["time"]) == TYPE_DICTIONARY:
emit_signal("date_changed", _state_data["time"].duplicate(true))
# 获取需要保存到存档的状态。
# 直接返回核心状态字典的副本。
func get_savable_state() -> Dictionary:
print("GameState: Gathering savable state...")
# 返回深拷贝,确保存档系统拿到的是快照,不会意外修改当前状态
return _state_data.duplicate(true)
# --- 状态访问 (通用 Getter) ---
# 获取指定键 (key) 的状态值。
# 如果键不存在,返回提供的默认值 (default)。
func get_value(key: String, default = null):
# 使用字典的 get 方法,它能优雅地处理键不存在的情况
return _state_data.get(key, default)
# --- 状态修改 (通用 Setter) ---
# 设置指定键 (key) 的状态值。
# 如果值发生变化,会发出 state_value_changed 信号。
# 如果键是 "time",还会额外发出 date_changed 信号。
func set_value(key: String, value) -> void:
var old_value = _state_data.get(key)
# 只有当值确实发生改变时才执行更新和发信号,提高效率
# 注意:对于字典或数组等引用类型,这个比较是浅比较。
if old_value != value:
_state_data[key] = value
#print("GameState: Value changed - ", key, ": ", value)
# 发出通用值变化信号
emit_signal("state_value_changed", key, value)
# [兼容性] 如果更新的是 "time" 键,且值是字典,则发出 date_changed 信号
if key == "time" and typeof(value) == TYPE_DICTIONARY:
emit_signal("date_changed", value.duplicate(true)) # 发送新日期的副本
# --- 系统状态访问辅助函数 (新添加) ---
# 内部辅助函数:安全地获取 "system_status" 字典
# 返回一个字典的副本,如果原始数据无效或不存在,则返回包含默认值的字典
func _get_system_status_dict() -> Dictionary:
var status = get_value("system_status")
# 验证获取到的值是否为字典,如果不是或为 null则返回默认结构
if typeof(status) != TYPE_DICTIONARY:
printerr("GameState: 'system_status' key missing or not a Dictionary in _state_data. Returning default.")
return {"game_pause": false, "is_on_task": false}
# 返回深拷贝以防止外部意外修改原始字典的引用(虽然 get_value 返回的通常是副本或基础类型)
return status.duplicate(true)
# 设置 "system_status" 中的特定键值 (game_pause 或 is_on_task)
# key: 要设置的键名 ("game_pause" 或 "is_on_task")
# value: 要设置的布尔值 (true 或 false)
func set_system_status_value(key: String, value: bool) -> void:
# 获取当前的 system_status 字典的副本
var current_status = _get_system_status_dict()
# 检查键是否是预期的 "game_pause" 或 "is_on_task"
if key == "game_pause" or key == "is_on_task":
# 只有当值实际改变时才更新,以避免不必要的 set_value 调用和信号发射
if current_status.get(key) != value:
current_status[key] = value
# 使用通用的 set_value 来更新整个 system_status 字典
# 这将触发 state_value_changed("system_status", new_dictionary) 信号
set_value("system_status", current_status)
print("GameState: Updated system_status -> ", key, " set to ", value)
# else: # 值未改变,无需操作
# print("GameState: system_status -> ", key, " already set to ", value)
else:
printerr("GameState: Invalid key '", key, "' provided to set_system_status_value. Expected 'game_pause' or 'is_on_task'.")
# 检查游戏是否暂停
# 返回: bool - true 如果游戏已暂停, false 否则
func is_game_paused() -> bool:
return _get_system_status_dict().get("game_pause", false)
# 检查是否处于任务中状态
# 返回: bool - true 如果正在进行任务, false 否则
func is_on_task_status() -> bool:
return _get_system_status_dict().get("is_on_task", false)
# 暂停游戏
func pause_game():
set_system_status_value("game_pause", true)
# 恢复游戏
func resume_game():
set_system_status_value("game_pause", false)
# --- 辅助函数:查找字典中第一个 enabled:true 的条目的键 ---
# data_dict: 期望是一个结构为 { "key1": {"enabled": bool, ...}, "key2": {...} } 的字典
# 返回: 第一个找到的 enabled 为 true 的条目的键名 (String),如果未找到则返回空字符串
func _find_first_enabled_key(data_dict: Dictionary) -> String:
if typeof(data_dict) != TYPE_DICTIONARY:
printerr("GameState (_find_first_enabled_key): Expected a Dictionary, got ", typeof(data_dict))
return ""
# GDScript 字典从 Godot 3.1 开始保持插入顺序,
# 因此迭代将遵循 JSON 中定义的顺序或它们被添加到字典中的顺序。
for key in data_dict:
var item = data_dict[key]
if typeof(item) == TYPE_DICTIONARY and item.has("enabled") and item["enabled"] == true:
return key # 返回第一个找到的键名
# print("GameState (_find_first_enabled_key): No enabled item found in dictionary with keys: ", data_dict.keys()) # 可选的调试信息
return "" # 如果没有找到 enabled:true 的条目
# --- 初始化 task_info ---
# MODIFIED: 此函数现在会从 _state_data["task_development"] 获取初始值
func add_init_data() -> void:
var initial_platform: String = ""
var initial_gameplay: String = ""
var initial_theme: String = ""
var initial_strategy: String = ""
var initial_product_focus_points: Dictionary = {}
# 确保 _state_data["task_development"] 存在并且是字典类型
if _state_data.has("task_development") and typeof(_state_data["task_development"]) == TYPE_DICTIONARY:
var task_dev_data = _state_data["task_development"]
# 获取平台初始值
if task_dev_data.has("platforms") and typeof(task_dev_data["platforms"]) == TYPE_DICTIONARY:
initial_platform = _find_first_enabled_key(task_dev_data["platforms"])
else:
printerr("GameState (add_init_data): 'task_development.platforms' is missing or not a Dictionary.")
# 获取玩法初始值
if task_dev_data.has("gameplays") and typeof(task_dev_data["gameplays"]) == TYPE_DICTIONARY:
initial_gameplay = _find_first_enabled_key(task_dev_data["gameplays"])
else:
printerr("GameState (add_init_data): 'task_development.gameplays' is missing or not a Dictionary.")
# 获取题材初始值
if task_dev_data.has("themes") and typeof(task_dev_data["themes"]) == TYPE_DICTIONARY:
initial_theme = _find_first_enabled_key(task_dev_data["themes"])
else:
printerr("GameState (add_init_data): 'task_development.themes' is missing or not a Dictionary.")
# 获取开发策略初始值
if task_dev_data.has("strategies") and typeof(task_dev_data["strategies"]) == TYPE_DICTIONARY:
initial_strategy = _find_first_enabled_key(task_dev_data["strategies"])
else:
printerr("GameState (add_init_data): 'task_development.strategies' is missing or not a Dictionary.")
# 获取产品侧重点初始值
if task_dev_data.has("product_focus_points") and typeof(task_dev_data["product_focus_points"]) == TYPE_DICTIONARY:
initial_product_focus_points = task_dev_data["product_focus_points"]
else:
printerr("GameState (add_init_data): 'task_development.product_focus_points' is missing or not a Dictionary.")
else:
printerr("GameState (add_init_data): 'task_development' key is missing in _state_data or is not a Dictionary.")
# 初始化 _state_data["task_info"]
_state_data["task_info"] = {
"平台": initial_platform,
"玩法": initial_gameplay,
"题材": initial_theme,
"开发策略": initial_strategy,
"预算": 0, # 保持原有的默认值
"产品侧重点": initial_product_focus_points,
#"关键环节负责人": [] # 保持原有的默认值
}