# task_development.gd extends Control # --- 节点引用 --- @onready var confirm_button = $main_page/Confirm @onready var platform_button = $"main_page/Part_3/Select_List/1_right/Button" @onready var gameplay_button = $"main_page/Part_3/Select_List/2_right/Button" @onready var theme_button = $"main_page/Part_3/Select_List/3_right/Button" @onready var strategy_button = $"main_page/Part_3/Select_List/4_right/Button" @onready var budget_button = $"main_page/Part_2/HBoxContainer/Button" @onready var main_page = $main_page @onready var platform_popup = $platform @onready var gameplay_popup = $gameplay @onready var theme_popup = $theme @onready var strategy_popup = $strategy @onready var product_focus = $product_focus @onready var dialogue: Control = $dialogue # --- 内部状态 --- var pop_up_list: Array = [] # 记录打开的UI弹出窗口 var node_name: String var is_active: bool = false # 存储用户在当前配置过程中的临时选择 (使用中文 Key) var task_info: Dictionary = { "平台": "", "玩法": "", "题材": "", "开发策略": "", "预算": 0, "产品侧重点": {}, "关键环节负责人": [] # <--- 新增并初始化为空数组 } # --- 初始化 --- func _ready() -> void: node_name = str(self.name) add_to_group(node_name) # 用于 UI_MAIN 查找 main_page.hide() # 连接信号 confirm_button.pressed.connect(_on_confirm_button_pressed) platform_button.pressed.connect(_on_category_button_pressed.bind(platform_popup)) gameplay_button.pressed.connect(_on_category_button_pressed.bind(gameplay_popup)) theme_button.pressed.connect(_on_category_button_pressed.bind(theme_popup)) strategy_button.pressed.connect(_on_category_button_pressed.bind(strategy_popup)) # 注意:不再需要加载静态数据 # --- 核心显示与关闭逻辑 --- func show_up(): if GameState.is_on_task_status(): self.visible = true show_dialogue("正在进行其它项目","") return print(node_name + ": Showing up.") # 1. 设置界面状态 main_page.show() self.visible = true is_active = true pop_up_list.clear() pop_up_list.append(self) # 2. 设置默认选项 (从 GameState 读取) _set_default_selections() # 3. 更新按钮文本和预算 (基于默认选项) update_button_texts() calculate_and_update_budget() # 初始计算预算 # 4. 暂停游戏并处理输入 GameState.pause_game() set_process_input(true) func close_all_popups(): print(node_name + ": Closing all popups...") while not pop_up_list.is_empty(): var popup = pop_up_list.pop_back() if popup and is_instance_valid(popup): popup.visible = false self.visible = false is_active = false if GameState: GameState.resume_game() # 假设 GameState 有此方法 set_process_input(false) # --- 数据初始化 (从 GameState 设置默认值) --- # 根据 GameState 设置默认选项 func _set_default_selections() -> void: if not GameState: printerr(node_name + ": GameState not available during _set_default_selections.") # 将所有选项设置为空,让用户必须选择 task_info["平台"] = "" task_info["玩法"] = "" task_info["题材"] = "" task_info["开发策略"] = "" return var task_dev_data = GameState.get_value("task_development", {}) if task_dev_data.is_empty(): printerr(node_name + ": Failed to get 'task_development' data from GameState during _set_default_selections.") # 将所有选项设置为空 task_info["平台"] = "" task_info["玩法"] = "" task_info["题材"] = "" task_info["开发策略"] = "" return # 定义 GameState 中的 Key 和 task_info 中的 Key 的映射 var categories_map = { "平台": "platforms", "玩法": "gameplays", "题材": "themes", "开发策略": "strategies" } # 为每个类别查找第一个启用的选项 for task_key in categories_map: var gamestate_key = categories_map[task_key] var items_data = task_dev_data.get(gamestate_key, {}) var default_item_key = _find_first_enabled_key_in_gamestate(items_data) task_info[task_key] = default_item_key # 如果没找到,会是 "" if default_item_key.is_empty(): print(node_name + ": No enabled default found for category '%s'." % task_key) print(node_name + ": Default selections set from GameState: ", task_info) # 辅助函数:在 GameState 的某个类别数据中查找第一个 enabled 的 key (中文名称) func _find_first_enabled_key_in_gamestate(items_data: Dictionary) -> String: if items_data.is_empty(): return "" # 尝试按某种顺序查找,如果字典是无序的,结果可能不固定 # 如果需要固定顺序,可能需要在 GameState 中存储时就排序或提供顺序信息 for item_key in items_data: var item_details = items_data[item_key] if typeof(item_details) == TYPE_DICTIONARY and item_details.get("enabled", false) == true: return item_key # 返回第一个找到的 enabled key (中文名称) # 如果没有找到 enabled 的 return "" # --- 预算计算与 UI 更新 --- # 计算并更新预算显示 (完全基于 GameState 数据) func calculate_and_update_budget() -> void: if not GameState: printerr(node_name + ": Cannot calculate budget, GameState not available.") task_info["预算"] = 0 if budget_button: budget_button.text = "错误" return var task_dev_data = GameState.get_value("task_development", {}) if task_dev_data.is_empty(): printerr(node_name + ": Cannot calculate budget, 'task_development' data not found in GameState.") task_info["预算"] = 0 if budget_button: budget_button.text = "错误" return var platform_cost: float = 0.0 var gameplay_cost: float = 0.0 var theme_cost: float = 0.0 var strategy_multiplier: float = 0.0 # 策略的 "Cost" 是乘数因子 # 1. 获取平台成本 var platform_key = task_info["平台"] if not platform_key.is_empty(): var platform_data = task_dev_data.get("platforms", {}).get(platform_key, {}) platform_cost = platform_data.get("Cost", 0.0) else: print(node_name + ": Platform not selected, cost is 0.") # 2. 获取玩法成本 var gameplay_key = task_info["玩法"] if not gameplay_key.is_empty(): var gameplay_data = task_dev_data.get("gameplays", {}).get(gameplay_key, {}) gameplay_cost = gameplay_data.get("Cost", 0.0) else: print(node_name + ": Gameplay not selected, cost is 0.") # 3. 获取题材成本 var theme_key = task_info["题材"] if not theme_key.is_empty(): var theme_data = task_dev_data.get("themes", {}).get(theme_key, {}) theme_cost = theme_data.get("Cost", 0.0) else: print(node_name + ": Theme not selected, cost is 0.") # 4. 获取策略成本乘数 var strategy_key = task_info["开发策略"] if not strategy_key.is_empty(): var strategy_data = task_dev_data.get("strategies", {}).get(strategy_key, {}) # 假设策略的 "Cost" 字段存储的是成本增加的百分比 (例如 0.2 代表增加 20%) strategy_multiplier = strategy_data.get("Cost", 0.0) else: print(node_name + ": Strategy not selected, multiplier is 0.") # 计算总预算: (平台 + 玩法 + 题材) * (1 + 策略乘数) var base_cost = platform_cost + gameplay_cost + theme_cost var calculated_budget = int(base_cost * (1.0 + strategy_multiplier)) # 结果取整 task_info["预算"] = calculated_budget # 更新预算显示 if budget_button: budget_button.text = str(calculated_budget) # print(node_name + ": Budget updated to " + str(calculated_budget)) # Debug else: printerr(node_name + ": Budget button not found when updating budget.") # 更新所有主界面按钮文本 (直接使用 task_info 中的中文 Key) func update_button_texts() -> void: var placeholder_text = "请选择" # 如果未选择,显示的文本 platform_button.text = task_info["平台"] if not task_info["平台"].is_empty() else placeholder_text gameplay_button.text = task_info["玩法"] if not task_info["玩法"].is_empty() else placeholder_text theme_button.text = task_info["题材"] if not task_info["题材"].is_empty() else placeholder_text strategy_button.text = task_info["开发策略"] if not task_info["开发策略"].is_empty() else placeholder_text # --- 子窗口交互与数据更新 --- # 提供给子节点调用的接口,用于启动此节点内的对话框 # dialogues: String 或 PackedStringArray - 要显示的对话内容 # next_node: Control (可选) - 对话结束后要显示的下一个UI节点 func show_dialogue(dialogues, next_node_name: String = "") -> void: if not is_instance_valid(dialogue): printerr(node_name + ": Dialogue node is not valid or ready.") return if not dialogue.has_method("_start"): printerr(node_name + ": Dialogue node does not have a _start method.") return print(node_name + ": Requesting to show dialogue with content: ", dialogues) # 调用 dialogue 节点的 _start 方法来显示对话框 # 注意:这里假设 dialogue 节点的 _start 方法会处理好显示、暂停游戏等逻辑 var next_node = get_node_or_null(next_node_name) dialogue._start(dialogues, next_node) # 可选:如果希望对话框显示时,task_development 的其他部分(如 main_page 或子弹窗)隐藏, # 可以在这里添加隐藏逻辑,但这取决于具体交互需求。 # 例如: # if main_page.visible: main_page.hide() # if not pop_up_list.is_empty() and pop_up_list[-1] != self and is_instance_valid(pop_up_list[-1]): # pop_up_list[-1].hide() # 隐藏最上层的子弹窗 # 处理分类按钮点击的通用函数 func _on_category_button_pressed(popup_node: Control) -> void: if not is_instance_valid(popup_node): printerr(node_name + ": Invalid popup node provided for category button.") return if not GameState: printerr(node_name + ": Cannot open popup, GameState not available.") return # 子窗口现在自行从 GameState 加载数据,无需父窗口传递 # 只需要显示对应的弹窗即可 # --- 确保子窗口在显示时会刷新其内容 --- # 最好在子窗口的 _on_visibility_changed 或类似方法中处理数据加载 # 如果子窗口没有这个逻辑,可以在这里强制调用一下(如果它们有 reset_display 方法) if popup_node.has_method("reset_display"): popup_node.reset_display() elif popup_node.has_method("_on_visibility_changed"): # 备选方案 # 注意:直接调用 _on_visibility_changed 可能不符合预期,因为它通常由引擎触发 # 更好的方式是确保子窗口的 _ready 或 visibility_changed 信号连接正确并处理加载 pass main_page.hide() popup_node.show() # 显示弹窗 pop_up_list.append(popup_node) print(node_name + ": Showing category popup: ", popup_node.name) # 提供给子弹出窗口调用的接口函数,用于更新选择 # options: 字典,包含更新的类别和对应的 Key func update_task_options(options: Dictionary) -> void: var updated = false for key in options: var new_value = options[key] if key == "关键环节负责人": # --- 特殊处理 "关键环节负责人" --- if not task_info.has(key) or not typeof(task_info[key]) == TYPE_ARRAY: task_info[key] = [] # 如果不存在或类型不对,则初始化为空数组 if not new_value in task_info[key]: # 避免重复添加 (可选,根据需求) task_info[key].append(new_value) updated = true print(node_name + ": Appended '%s' to '%s'. Current: %s" % [new_value, key, task_info[key]]) else: print(node_name + ": '%s' already in '%s'. No change." % [new_value, key]) elif task_info.has(key): # --- 原有逻辑处理其他普通键值对 --- if task_info[key] != new_value: task_info[key] = new_value updated = true print(node_name + ": Option '%s' updated to '%s'." % [key, new_value]) else: printerr(node_name + ": Invalid key '%s' provided in update_task_options and not handled as a special case." % key) if updated: # 对于 "关键环节负责人" 的更新,通常不需要更新按钮文本或重新计算预算 # 但如果其他选项更新了,则需要执行 if not (options.has("关键环节负责人") and options.size() == 1): # 如果不仅仅是更新负责人 update_button_texts() calculate_and_update_budget() print(node_name + ": Task info updated: ", task_info) # 处理子弹出窗口确认后关闭并返回主页面的逻辑 func _close_child_popup_and_return(child_popup_node: Control) -> void: if not is_instance_valid(child_popup_node): printerr(node_name + ": Invalid child popup node passed.") return print(node_name + ": Request received to close child popup '%s' and return." % child_popup_node.name) if child_popup_node in pop_up_list: pop_up_list.erase(child_popup_node) else: printerr(node_name + ": Child popup '%s' was not found in pop_up_list during close request." % child_popup_node.name) child_popup_node.hide() main_page.show() print(node_name + ": Hid child popup '%s' and showed main_page." % child_popup_node.name) # --- 输入处理 --- # (保持不变) func _input(event): if not is_active: return if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_RIGHT and event.is_pressed(): if pop_up_list.size() > 0: var last_popup = pop_up_list.pop_back() if last_popup and is_instance_valid(last_popup): if last_popup != self: last_popup.visible = false print(node_name + ": Closed popup via right-click: ", last_popup.name) # 检查是否需要重显主页面 if not pop_up_list.is_empty() and pop_up_list[-1] == self and not main_page.visible: main_page.show() print(node_name + ": Re-showing main_page after closing sub-popup.") get_viewport().set_input_as_handled() else: # 在主界面右键点击,关闭整个界面 print(node_name + ": Right-click on main page. Closing task_development.") pop_up_list.append(last_popup) # 把 self 放回去,让下面的判断处理关闭 # 如果列表为空(或只剩 self 且刚刚弹出的是 self),则关闭所有 if pop_up_list.is_empty() or (pop_up_list.size() == 1 and pop_up_list[0] == self and last_popup == self): print(node_name + ": Closing task_development due to right-click logic.") close_all_popups() # 不需要 set_input_as_handled,因为界面关闭了 # --- 确认与后续流程 --- func _on_confirm_button_pressed(): print(node_name + ": Confirmed with task_info: ", task_info) # 检查是否所有选项都已选择 (Key 不为空) if task_info["平台"].is_empty() or \ task_info["玩法"].is_empty() or \ task_info["题材"].is_empty() or \ task_info["开发策略"].is_empty(): printerr(node_name + ": Cannot confirm, not all options are selected.") # 此处可以添加用户提示 var ui_main = get_tree().get_first_node_in_group("UI_MAIN") if ui_main and ui_main.has_method("show_notification"): ui_main.show_notification("请确保所有开发选项都已选择!") return # --- 触发后续流程 --- product_focus.show() main_page.hide() # 发出开始任务的信号 func start_task(): print(node_name + ": start task with info: ", task_info) GameState.set_system_status_value("is_on_task", true)