382 lines
15 KiB
GDScript
382 lines
15 KiB
GDScript
# 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)
|