新的ui工作流框架

This commit is contained in:
jkboy 2025-05-10 21:20:19 +08:00
parent 3bb02fcde6
commit c58b1f8ed8
28 changed files with 7787 additions and 0 deletions

View File

@ -0,0 +1,390 @@
# gameplay.gd (更新版)
extends NinePatchRect
# UI Node References
@onready var list_container = $Option/Info
@onready var previous_button = $Title/previous/Button
@onready var next_button = $Title/next/Button
@onready var title_label = $Title/Title
# Configuration
var items_per_page: int = 7 # 每页显示的选项数量
# Data Storage
var gameplay_list_nodes: Array[VBoxContainer] = [] # 存储 List_1, List_2 等节点
var option_nodes_per_list: Array = [] # 存储每个 List 对应的 option_X 按钮数组
var all_enabled_gameplays: Array = [] # 存储筛选后的玩法数据 { "name": String, "data": Dictionary }
# State
var total_pages: int = 0
var current_page_index: int = 0
var selected_gameplay_key: String = "" # 当前逻辑上选中的项的 Key (用于确认)
var currently_selected_button: Button = null # 当前高亮/聚焦的按钮节点引用
var initial_key_to_highlight: String = "" # 弹窗打开时需要高亮的 Key (由父节点设置)
func _ready():
# 1. 获取 List 节点 (与原版一致)
for i in range(1, 4): # 检查 List_1, List_2, List_3
var list_node = list_container.get_node_or_null("List_" + str(i))
if list_node:
gameplay_list_nodes.append(list_node)
else:
break
if gameplay_list_nodes.is_empty():
printerr("错误:在 gameplay.tscn 中未找到 List 节点!无法显示选项。")
set_process(false)
return
# 2. 获取每个 List 内的 Option 节点 (与原版一致)
for list_node in gameplay_list_nodes:
var current_list_options: Array = []
for i in range(1, items_per_page + 1):
var option_node = list_node.get_node_or_null("option_" + str(i))
if option_node and option_node is Button:
current_list_options.append(option_node)
else:
push_warning("警告:在 %s 中未能找到 option_%d 按钮" % [list_node.name, i])
option_nodes_per_list.append(current_list_options)
# 3. 连接按钮信号 (与原版一致)
if previous_button:
if not previous_button.pressed.is_connected(_on_previous_pressed):
previous_button.pressed.connect(_on_previous_pressed)
else: printerr("错误:在 gameplay.tscn 中未找到 Previous 按钮")
if next_button:
if not next_button.pressed.is_connected(_on_next_pressed):
next_button.pressed.connect(_on_next_pressed)
else: printerr("错误:在 gameplay.tscn 中未找到 Next 按钮")
# 4. 连接 visibility_changed 信号 (与原版一致)
if not is_connected("visibility_changed", _on_visibility_changed):
visibility_changed.connect(_on_visibility_changed)
# 5. 移除 _ready 中的初始数据加载和显示更新
# 这些逻辑现在完全由 _on_visibility_changed -> reset_display 处理
# --- Visibility Change Handler ---
func _on_visibility_changed():
if is_visible_in_tree():
print("Gameplay: 弹窗变为可见,调用 reset_display()")
reset_display() # 当变为可见时调用 reset
# else: # 可选:隐藏时的清理逻辑
# print("Gameplay: 弹窗变为隐藏。")
# --- 新增:用于父节点设置初始选中项 ---
func set_initial_selection(key: String):
"""
(task_development)
task_info Key
"""
initial_key_to_highlight = key
print("Gameplay: 父节点设置初始高亮 Key 为: '%s'" % initial_key_to_highlight)
# --- Data Processing and Display Logic ---
func _process_gameplay_data(all_gameplays_data: Dictionary):
"""【修改】根据从 GameState 获取的玩法数据,筛选出启用的玩法,并计算总页数。"""
all_enabled_gameplays.clear()
if all_gameplays_data.is_empty():
push_warning("警告:接收到的玩法数据为空。")
total_pages = 0
return
# 遍历从 GameState 获取的玩法字典
for gameplay_name in all_gameplays_data:
var gameplay_info = all_gameplays_data[gameplay_name]
# 检查是否为字典且 "enabled" 字段为 true
if typeof(gameplay_info) == TYPE_DICTIONARY and gameplay_info.get("enabled", false) == true:
all_enabled_gameplays.append({"name": gameplay_name, "data": gameplay_info})
# else: # Debugging: 可以取消注释来看哪些被过滤了
# print("Gameplay: 过滤掉未启用或格式错误的玩法: ", gameplay_name)
# 根据筛选结果计算总页数
if items_per_page > 0:
total_pages = ceil(float(all_enabled_gameplays.size()) / items_per_page)
else:
total_pages = 0
printerr("错误items_per_page 为零,无法计算页数。")
print("Gameplay: 处理完成,找到 %d 个启用的玩法,共 %d 页。" % [all_enabled_gameplays.size(), total_pages])
func reset_display():
"""【修改】重置状态,从 GameState 重新加载数据,并准备更新显示。"""
print("Gameplay: 重置显示状态 (reset_display)。")
# 1. 重置本地状态变量
currently_selected_button = null
selected_gameplay_key = ""
# initial_key_to_highlight 由 set_initial_selection 设置,这里不清空
# 2. 【修改】从 GameState 重新加载并处理数据
if not GameState:
printerr("错误GameState 在 reset_display 期间不可用。")
total_pages = 0
all_enabled_gameplays.clear()
_update_display() # 即使没有数据也要更新显示(显示空状态)
return
var task_dev_data = GameState.get_value("task_development", {})
if task_dev_data.is_empty():
printerr("错误:从 GameState 获取 'task_development' 数据失败。")
total_pages = 0
all_enabled_gameplays.clear()
else:
var all_gameplays_data = task_dev_data.get("gameplays", {})
_process_gameplay_data(all_gameplays_data) # 重新处理数据,计算 total_pages
# 3. 【修改】根据 initial_key_to_highlight 确定初始页面
current_page_index = 0 # 默认为第一页
var found_initial_key = false
if total_pages > 0 and not initial_key_to_highlight.is_empty():
# 查找 initial_key_to_highlight 所在的索引和页面
for i in range(all_enabled_gameplays.size()):
if all_enabled_gameplays[i].name == initial_key_to_highlight:
current_page_index = int(floor(float(i) / items_per_page))
found_initial_key = true
print("Gameplay: 找到需要初始高亮的 Key '%s' 在索引 %d, 目标页面 %d" % [initial_key_to_highlight, i, current_page_index])
break # 找到了,停止循环
if not found_initial_key:
print("Gameplay: 需要初始高亮的 Key '%s' 未在启用的玩法中找到,将显示第一页。" % initial_key_to_highlight)
# 保持 current_page_index 为 0
initial_key_to_highlight = "" # 清空,避免后续 _update_display 尝试高亮不存在的项
# 确保页面索引有效
if total_pages == 0:
current_page_index = 0
elif current_page_index >= total_pages:
current_page_index = total_pages - 1
elif current_page_index < 0:
current_page_index = 0
# 4. 触发显示更新
_update_display()
func _update_display():
"""【更新版】更新列表的可见性并填充当前页面的内容。
"""
# --- 处理无数据情况 ---
if gameplay_list_nodes.is_empty():
print("Gameplay: 无可用的 List 节点。")
if title_label: title_label.text = "玩法选择"
return
if total_pages == 0:
for list_node in gameplay_list_nodes: list_node.visible = false
print("Gameplay: 没有启用的玩法选项可供显示。")
if title_label: title_label.text = "玩法选择 (无可用)"
# 确保按钮也禁用或隐藏
if previous_button: previous_button.disabled = true
if next_button: next_button.disabled = true
return
else:
# 确保按钮状态正确 (启用/禁用基于页数)
if previous_button: previous_button.disabled = (total_pages <= 1)
if next_button: next_button.disabled = (total_pages <= 1)
# --- 更新页面索引 (循环) ---
# 注意: reset_display 设置了初始页, 取模主要用于翻页
if total_pages > 0 : # 避免除以零
current_page_index = current_page_index % total_pages
if current_page_index < 0:
current_page_index += total_pages
else:
current_page_index = 0
# --- 更新标题 ---
if title_label:
var display_page_number = current_page_index + 1 # 显示给用户的页码从1开始
title_label.text = "玩法选择 %d/%d" % [display_page_number, total_pages]
# --- 更新 List 可见性 ---
# 确保只有一个 List 节点是可见的,即当前页对应的 List
for i in range(gameplay_list_nodes.size()):
gameplay_list_nodes[i].visible = (i == current_page_index)
# --- 填充可见列表并查找要高亮的按钮 ---
var button_to_highlight: Button = null
var first_enabled_button_on_page: Button = null # 如果 initial_key 不在本页,则使用此按钮
# 检查当前页索引是否在有效范围内
if current_page_index < option_nodes_per_list.size():
var current_options = option_nodes_per_list[current_page_index] # 获取当前页的所有按钮节点
var start_index = current_page_index * items_per_page # 计算当前页数据在 all_enabled_gameplays 中的起始索引
# 遍历当前页的按钮槽位
for i in range(current_options.size()):
var option_button = current_options[i] # 获取具体的按钮节点
var item_index = start_index + i # 计算此按钮对应的数据索引
# --- 断开旧信号 ---
# 确保在连接新信号前断开旧的,防止重复连接
if option_button.pressed.is_connected(_on_option_selected):
option_button.pressed.disconnect(_on_option_selected)
# 检查数据索引是否在有效范围内
if item_index < all_enabled_gameplays.size():
# 获取对应的玩法数据
var gameplay_entry = all_enabled_gameplays[item_index]
var gameplay_name = gameplay_entry.name
var gameplay_data = gameplay_entry.data # 这是包含 Cost, Experience, Popularity 等信息的字典
# 获取按钮内部用于显示信息的 Row 节点
var row = option_button.get_node_or_null("Row")
if row:
# 填充标签文本
var title_label_in_row = row.get_node_or_null("Title")
var exp_label = row.get_node_or_null("Experience")
var pop_label = row.get_node_or_null("Popularity")
# 查找成本标签,兼容 "Cost" 和 "cost" 两种可能的节点名
var cost_label = row.get_node_or_null("Cost") if row.has_node("Cost") else row.get_node_or_null("cost")
# 设置标题
if title_label_in_row: title_label_in_row.text = gameplay_name
# 【修改】设置经验值,显示为整数
if exp_label:
var experience_value = gameplay_data.get("Experience", 0.0) # 获取原始值
exp_label.text = str(int(experience_value)) # 转为整数再转为字符串
# 【保持】设置流行度,保持原始字符串显示
if pop_label:
var popularity_value = gameplay_data.get("Popularity", "未知") # 获取原始值
pop_label.text = str(popularity_value) # 直接转为字符串
# 【修改】设置成本,显示为整数
if cost_label:
var cost_value = gameplay_data.get("Cost", 0.0) # 获取原始值
cost_label.text = str(int(cost_value)) # 转为整数再转为字符串
# 设置按钮可见、可用,并存储玩法名称到元数据
option_button.visible = true
option_button.disabled = false
option_button.set_meta("gameplay_name", gameplay_name)
# --- 连接新信号 (使用 bind) ---
# 传递玩法名称和按钮节点本身给处理函数
option_button.pressed.connect(_on_option_selected.bind(gameplay_name, option_button))
# 记录本页第一个可用的按钮,作为备选高亮目标
if first_enabled_button_on_page == null:
first_enabled_button_on_page = option_button
# 检查此按钮是否是需要初始高亮的按钮
if not initial_key_to_highlight.is_empty() and gameplay_name == initial_key_to_highlight:
button_to_highlight = option_button
# print("Gameplay: 找到需要初始高亮的按钮: ", button_to_highlight.name) # Debug
else: # 未找到 Row 节点
push_warning("警告:在按钮 %s 中未找到 Row 节点" % option_button.get_path())
option_button.visible = false # 隐藏按钮以避免显示不完整
option_button.disabled = true
else: # 此槽位没有对应的启用玩法数据 (例如最后一页不满)
option_button.visible = false # 隐藏按钮
option_button.disabled = true
else:
# 这种情况理论上不应发生,除非页面计算或节点结构有问题
printerr("错误:当前页面索引 %d 超出 option_nodes_per_list 的范围 (大小 %d)" % [current_page_index, option_nodes_per_list.size()])
# --- 设置初始高亮和焦点 ---
# 优先高亮 initial_key_to_highlight 对应的按钮
if button_to_highlight:
# print("Gameplay: 尝试高亮指定按钮: ", button_to_highlight.name) # Debug
_set_initial_highlight(button_to_highlight)
# 如果没有指定按钮或指定按钮不在当前页,则高亮当前页的第一个可用按钮
elif first_enabled_button_on_page:
print("Gameplay: 初始 Key '%s' 不在本页或为空。高亮本页第一个按钮: %s" % [initial_key_to_highlight, first_enabled_button_on_page.name])
_set_initial_highlight(first_enabled_button_on_page)
# 如果当前页没有任何可用按钮
else:
print("Gameplay: 本页没有可供高亮的按钮。")
currently_selected_button = null # 确保没有按钮被标记为选中
selected_gameplay_key = "" # 确保没有 Key 被标记为选中
# (Helper) 设置初始高亮和焦点 (与原版类似,增加日志)
func _set_initial_highlight(button_to_highlight: Button):
if not is_instance_valid(button_to_highlight):
printerr("错误:传递给 _set_initial_highlight 的按钮无效。")
currently_selected_button = null
selected_gameplay_key = ""
return
# print("Gameplay: 设置高亮按钮: ", button_to_highlight.name) # Debug
currently_selected_button = button_to_highlight
selected_gameplay_key = button_to_highlight.get_meta("gameplay_name", "") # 同时设置 Key
# print("Gameplay: currently_selected_button 设置为: ", currently_selected_button.name) # Debug
# print("Gameplay: selected_gameplay_key 设置为: '", selected_gameplay_key, "'") # Debug
# 延迟调用 grab_focus 确保按钮已准备好接收焦点
if currently_selected_button and currently_selected_button.is_inside_tree():
# print("Gameplay: 尝试为按钮 call_deferred('grab_focus'): ", currently_selected_button.name) # Debug
currently_selected_button.call_deferred("grab_focus")
else:
print("Gameplay: 按钮无效或不在场景树中,无法获取焦点。")
# --- Signal Handlers ---
func _on_previous_pressed():
if total_pages > 1: # 仅当有多页时才翻页
current_page_index -= 1
# 循环由 _update_display 处理
_update_display()
func _on_next_pressed():
if total_pages > 1: # 仅当有多页时才翻页
current_page_index += 1
# 循环由 _update_display 处理
_update_display()
# 【修改】处理选项按钮按下事件 (与原版类似,增加日志)
func _on_option_selected(gameplay_name: String, button_node: Button):
"""当选项按钮被按下时调用。"""
if not is_instance_valid(button_node):
printerr("错误:在 _on_option_selected 中收到无效按钮节点Key: " + gameplay_name)
return
if button_node == currently_selected_button:
# --- 双击 (或点击已选中的按钮) ---
print("Gameplay: 检测到双击或确认点击: ", gameplay_name)
_confirm_and_close(gameplay_name) # 确认此选择
else:
# --- 首次点击或点击不同按钮 ---
print("Gameplay: 选中 (高亮) 玩法: ", gameplay_name)
currently_selected_button = button_node
selected_gameplay_key = gameplay_name # 更新 Key以便后续可能的确认
# 更新视觉焦点
button_node.grab_focus()
# 【修改】确认选择并关闭弹窗的逻辑
func _confirm_and_close(key_to_confirm: String):
"""更新父节点并关闭此弹窗。"""
if key_to_confirm.is_empty():
printerr("错误:尝试确认一个空的玩法 Key。")
return
print("Gameplay: 确认选择: ", key_to_confirm)
# --- 更新父节点 (Task Development Popup) ---
var parent_node = get_parent()
if parent_node and parent_node.has_method("update_task_options"):
# 通知父节点更新其内部的 task_info
parent_node.update_task_options({"玩法": key_to_confirm})
print("Gameplay: 已更新父节点的 task_options: 玩法 = ", key_to_confirm)
else:
printerr("错误Gameplay: 父节点未找到或缺少 'update_task_options' 方法。")
# 考虑是否应该在此处停止或仅记录错误后继续
# --- 关闭此弹窗 ---
if parent_node and parent_node.has_method("_close_child_popup_and_return"):
# 调用父节点的标准方法来关闭子弹窗并返回
parent_node._close_child_popup_and_return(self)
else:
printerr("错误Gameplay: 父节点未找到或缺少 '_close_child_popup_and_return' 方法。")
self.hide() # 作为备选方案,直接隐藏自身

View File

@ -0,0 +1 @@
uid://cbiwirr68lspl

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
[gd_resource type="Theme" load_steps=5 format=3 uid="uid://ddq54ba6vwyn0"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_kemom"]
bg_color = Color(0.513883, 0.609153, 0.731076, 1)
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ao15e"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_kemom"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_mp8x4"]
bg_color = Color(0.513883, 0.609153, 0.731076, 1)
[resource]
Button/styles/focus = SubResource("StyleBoxFlat_kemom")
Button/styles/hover = SubResource("StyleBoxEmpty_ao15e")
Button/styles/normal = SubResource("StyleBoxEmpty_kemom")
Button/styles/pressed = SubResource("StyleBoxFlat_mp8x4")

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ctled08bky4mu"
path="res://.godot/imported/key_output.png-784f6b35ef722e290819983a5a30c217.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://UI/popup/task_development/key_output/key_output.png"
dest_files=["res://.godot/imported/key_output.png-784f6b35ef722e290819983a5a30c217.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View File

@ -0,0 +1,278 @@
extends NinePatchRect
# References to UI elements
@onready var title_label = $Title/Title
@onready var name_label = $Option/Name
@onready var icon_rect = $Option/Info/Icon
@onready var info1_label = $Option/Info/Info_List_1/Info_1/info # 制造商 (Maker)
@onready var info2_label = $Option/Info/Info_List_1/Info_2/info # 发售 (Sales)
@onready var info3_label = $Option/Info/Info_List_1/Info_3/info # 市场占比 (Market_share)
@onready var info4_label = $Option/Info/Info_List_2/info_4/info # 开发费用 (Cost)
# @onready var info5_label = $Option/Info/Info_List_2/info_5/info # 开发次数 - 暂不处理,数据源未知
@onready var prev_button = $Title/previous/Button
@onready var next_button = $Title/next/Button
@onready var confirm_button = $Confirm
# Internal state
var enabled_platform_keys: Array[String] = [] # Store keys (names) of enabled platforms
var current_platform_index: int = -1 # Index in the enabled_platform_keys array
const PLATFORM_ICON_PATH = "res://UI/popup/task_development/platform/"
# --- 新增:用于存储从 GameState 获取的平台数据 ---
var _platforms_data: Dictionary = {}
# --- 新增:用于存储上次确认的平台 Key 的 GameState 键名 ---
const SELECTED_PLATFORM_KEY_NAME = "selected_platform_key" # 假设用这个顶层键存储
func _ready():
add_to_group(str(name)) # 保持不变
# Connect button signals (保持不变)
if prev_button:
if not prev_button.pressed.is_connected(_on_previous_pressed):
prev_button.pressed.connect(_on_previous_pressed)
if next_button:
if not next_button.pressed.is_connected(_on_next_pressed):
next_button.pressed.connect(_on_next_pressed)
if confirm_button:
if not confirm_button.pressed.is_connected(_on_confirm_pressed):
confirm_button.pressed.connect(_on_confirm_pressed)
# Connect visibility changed signal (保持不变)
if not is_connected("visibility_changed", _on_visibility_changed):
visibility_changed.connect(_on_visibility_changed)
# Initial call if already visible on ready (保持不变)
if is_visible_in_tree():
reset_display()
# Visibility Change Handler (保持不变)
func _on_visibility_changed():
if is_visible_in_tree():
reset_display()
# --- 修改reset_display ---
# 重置显示状态,在窗口可见时调用
func reset_display():
print("Platform: Resetting display state.")
# 1. 加载并过滤平台数据
_load_and_filter_platforms() # 这会更新 _platforms_data 和 enabled_platform_keys
# 2. 检查是否有可用的平台
if enabled_platform_keys.is_empty():
print("Platform: No enabled platforms found during reset.")
current_platform_index = -1
_display_current_platform() # 显示 "无可用平台" 状态
return
# 3. 获取上次确认的平台 Key
var last_confirmed_key = ""
if GameState:
# --- 修改:使用 get_value 获取上次选择 ---
last_confirmed_key = GameState.get_value(SELECTED_PLATFORM_KEY_NAME, "")
else:
push_error("GameState not available during reset_display.")
# 默认使用第一个可用平台
current_platform_index = 0
_display_current_platform()
return
# 4. 查找上次确认的 Key 在当前可用列表中的索引
if not last_confirmed_key.is_empty():
var found_index = enabled_platform_keys.find(last_confirmed_key)
if found_index != -1:
# 5. 如果找到,更新当前索引
current_platform_index = found_index
print("Platform: Found last confirmed key '%s' at index %d." % [last_confirmed_key, found_index])
else:
# 6. 如果找不到(可能平台不再可用),默认使用第一个可用平台
print("Platform: Last confirmed key '%s' not found in enabled list. Defaulting to index 0." % last_confirmed_key)
current_platform_index = 0
else:
# 7. 如果 GameState 中没有记录,默认使用第一个可用平台
print("Platform: No last confirmed key found in GameState. Defaulting to index 0.")
current_platform_index = 0
# 8. 更新显示
_display_current_platform()
# --- 修改_load_and_filter_platforms ---
# 从 GameState 获取平台数据并筛选出启用的平台
func _load_and_filter_platforms():
enabled_platform_keys.clear()
_platforms_data = {} # 清空旧数据
if not GameState:
push_error("GameState not available in _load_and_filter_platforms.")
return
# --- 修改:从 GameState 获取 task_development 数据 ---
var task_dev_data = GameState.get_value("task_development", {})
if task_dev_data.is_empty():
printerr("Platform: Failed to get 'task_development' data from GameState.")
return
# --- 修改:获取 platforms 字典 ---
var all_platforms = task_dev_data.get("platforms", {})
if all_platforms.is_empty():
print("Platform: 'platforms' data in GameState is empty.")
return
# --- 修改:将获取到的平台数据存储到内部变量 ---
_platforms_data = all_platforms
# 遍历平台数据,筛选出启用的平台 Key
for platform_key in _platforms_data:
var platform_info = _platforms_data[platform_key]
# 确保 platform_info 是字典并且 'enabled' 键存在且为 true
if typeof(platform_info) == TYPE_DICTIONARY and platform_info.get("enabled", false) == true:
enabled_platform_keys.append(platform_key)
# 可选:按键名排序,保持一致性
#if not enabled_platform_keys.is_empty():
#enabled_platform_keys.sort() # 简单字符串排序
## current_platform_index 会在 reset_display 中设置
#else:
#print("Platform: No enabled platforms found.")
#current_platform_index = -1 # 明确设置无可用平台
# 根据 current_platform_index 更新 UI 显示
func _display_current_platform():
# 处理无可用平台或索引无效的情况
if current_platform_index < 0 or current_platform_index >= enabled_platform_keys.size():
title_label.text = "游戏平台"
name_label.text = "无可用平台"
if icon_rect: icon_rect.texture = null
if info1_label: info1_label.text = "-" # Maker
if info2_label: info2_label.text = "-" # Sales
if info3_label: info3_label.text = "-" # Market_share
if info4_label: info4_label.text = "-" # Cost
if prev_button: prev_button.disabled = true
if next_button: next_button.disabled = true
if confirm_button: confirm_button.disabled = true
return
# --- 如果有有效平台 ---
if confirm_button: confirm_button.disabled = false # 启用确认按钮
# 更新标题
var display_index = current_platform_index + 1
var total_count = enabled_platform_keys.size()
title_label.text = "游戏平台 %d/%d" % [display_index, total_count]
# 获取当前平台的 Key 和数据
var platform_key = enabled_platform_keys[current_platform_index]
# --- 从内部缓存 _platforms_data 获取平台信息 ---
if not _platforms_data.has(platform_key):
push_error("Current platform key '%s' not found in cached _platforms_data!" % platform_key)
name_label.text = "错误:数据丢失"
# ... 清空其他信息 ...
return
var platform_info = _platforms_data[platform_key]
# 更新平台名称
name_label.text = platform_key
# --- 修改:从 platform_info 读取所有需要的信息,数值转为整数显示 ---
# 使用 .get(key, default) 确保安全访问
# 制造商 (Maker) - 通常是字符串,保持不变
info1_label.text = platform_info.get("Maker", "-")
# 发售 (Sales) - 转换为整数显示
var sales_value = platform_info.get("Sales", null)
if typeof(sales_value) == TYPE_INT or typeof(sales_value) == TYPE_FLOAT:
info2_label.text = str(int(sales_value)) # 转换为整数再转字符串
else:
info2_label.text = "-" # 如果不是数字或不存在,显示 "-"
# 市场占比 (Market_share) - 转换为整数显示
var market_share_value = platform_info.get("Market_share", null)
if typeof(market_share_value) == TYPE_INT or typeof(market_share_value) == TYPE_FLOAT:
info3_label.text = str(int(market_share_value)) # 转换为整数再转字符串
else:
info3_label.text = "-"
# 开发费用 (Cost) - 转换为整数显示
var cost_value = platform_info.get("Cost", null)
if typeof(cost_value) == TYPE_INT or typeof(cost_value) == TYPE_FLOAT:
info4_label.text = str(int(cost_value)) # 转换为整数再转字符串
else:
info4_label.text = "-"
# info5 (开发次数) 暂不处理
# 更新图标 (逻辑不变)
var icon_name = platform_info.get("Icon", "")
if not icon_name.is_empty():
var icon_path = PLATFORM_ICON_PATH + icon_name + ".png"
if ResourceLoader.exists(icon_path):
icon_rect.texture = ResourceLoader.load(icon_path)
else:
push_error("Failed to load platform icon: " + icon_path)
icon_rect.texture = null
else:
print("Platform icon name is empty for key: ", platform_key)
icon_rect.texture = null
# 更新导航按钮状态 (逻辑不变)
var can_navigate = enabled_platform_keys.size() > 1
if prev_button: prev_button.disabled = not can_navigate
if next_button: next_button.disabled = not can_navigate
# --- _on_previous_pressed (逻辑不变) ---
func _on_previous_pressed():
if enabled_platform_keys.size() > 1:
current_platform_index -= 1
if current_platform_index < 0:
current_platform_index = enabled_platform_keys.size() - 1
_display_current_platform()
# --- _on_next_pressed (逻辑不变) ---
func _on_next_pressed():
if enabled_platform_keys.size() > 1:
current_platform_index += 1
if current_platform_index >= enabled_platform_keys.size():
current_platform_index = 0
_display_current_platform()
# --- 修改_on_confirm_pressed ---
# 处理确认选择
func _on_confirm_pressed():
if current_platform_index != -1 and current_platform_index < enabled_platform_keys.size():
# 1. 获取选中的平台 Key
var selected_platform_key = enabled_platform_keys[current_platform_index]
# 2. 获取父节点 (task_development)
var parent_node = get_parent()
# 3. 调用父节点的 update_task_options 更新选项 (保持不变)
if parent_node and parent_node.has_method("update_task_options"):
parent_node.update_task_options({"平台": selected_platform_key})
else:
push_error("Platform: Parent node not found or missing 'update_task_options' method.")
# 即使父节点更新失败,也尝试关闭窗口
# 4. 调用父节点的方法来处理关闭自身 (保持不变)
if parent_node and parent_node.has_method("_close_child_popup_and_return"):
parent_node._close_child_popup_and_return(self)
else:
push_error("Platform: Parent node not found or missing '_close_child_popup_and_return' method.")
self.hide() # Fallback hide
else:
# 处理没有有效平台被选中的情况
print("Platform: No valid platform selected to confirm.")
# 仍然尝试关闭窗口
var parent_node = get_parent()
if parent_node and parent_node.has_method("_close_child_popup_and_return"):
parent_node._close_child_popup_and_return(self)
else:
self.hide() # Fallback hide

View File

@ -0,0 +1 @@
uid://ddadoyxwx3sbh

View File

@ -0,0 +1,488 @@
[gd_scene load_steps=29 format=3 uid="uid://blv6i6w651pcs"]
[ext_resource type="Texture2D" uid="uid://pucudatqrcrq" path="res://UI/popup/popup_bg_1.png" id="1_kr1r5"]
[ext_resource type="Texture2D" uid="uid://dgcugleiv7sfw" path="res://UI/popup/task_development/arrow.png" id="2_6a8uh"]
[ext_resource type="Script" uid="uid://ddadoyxwx3sbh" path="res://UI/popup/task_development/platform/platform.gd" id="2_bu5lv"]
[ext_resource type="Texture2D" uid="uid://b2n1f22bd06tc" path="res://UI/popup/task_development/platform/platform_1.png" id="3_bu5lv"]
[ext_resource type="LabelSettings" uid="uid://qcn0aduvrgvw" path="res://UI/tres/task_development_platform_label_settings.tres" id="4_mue51"]
[ext_resource type="Theme" uid="uid://bau80ps6kx783" path="res://UI/tres/Bottom_Info_button_theme.tres" id="5_wpgbx"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_6a8uh"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_bu5lv"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_mue51"]
[sub_resource type="Animation" id="Animation_bu5lv"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Title/previous:position")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector2(9, 7)]
}
[sub_resource type="Animation" id="Animation_6a8uh"]
resource_name = "base"
length = 0.6
loop_mode = 1
step = 0.2
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Title/previous:position")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 0.2, 0.4, 0.6),
"transitions": PackedFloat32Array(1, 1, 1, 1),
"update": 1,
"values": [Vector2(16, 7), Vector2(12, 7), Vector2(9, 7), Vector2(16, 7)]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_mue51"]
_data = {
&"RESET": SubResource("Animation_bu5lv"),
&"base": SubResource("Animation_6a8uh")
}
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_wpgbx"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_r6b7x"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_hsiy4"]
[sub_resource type="Animation" id="Animation_wpgbx"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:position")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector2(461, 7)]
}
[sub_resource type="Animation" id="Animation_mue51"]
resource_name = "base"
length = 0.6
loop_mode = 1
step = 0.2
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:position")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 0.2, 0.4, 0.6),
"transitions": PackedFloat32Array(1, 1, 1, 1),
"update": 1,
"values": [Vector2(455, 7), Vector2(458, 7), Vector2(461, 7), Vector2(455, 7)]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_r6b7x"]
_data = {
&"RESET": SubResource("Animation_wpgbx"),
&"base": SubResource("Animation_mue51")
}
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_nckjb"]
bg_color = Color(0.913725, 0.913725, 0.913725, 0.913725)
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_6r1e0"]
bg_color = Color(0.913725, 0.913725, 0.913725, 0.913725)
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_at31c"]
bg_color = Color(0.913725, 0.913725, 0.913725, 0.913725)
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_qrj6y"]
bg_color = Color(0.913725, 0.913725, 0.913725, 0.913725)
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_v0qi7"]
bg_color = Color(0.913725, 0.913725, 0.913725, 0.913725)
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_rl25l"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_tndxe"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_xx8ge"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ep6fu"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ci4su"]
[node name="platform" type="NinePatchRect"]
custom_minimum_size = Vector2(500, 350)
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -250.0
offset_top = -175.0
offset_right = 250.0
offset_bottom = 175.0
grow_horizontal = 2
grow_vertical = 2
texture = ExtResource("1_kr1r5")
region_rect = Rect2(1, 35, 86, 53)
patch_margin_left = 8
patch_margin_top = 35
patch_margin_right = 8
patch_margin_bottom = 32
script = ExtResource("2_bu5lv")
[node name="Title" type="NinePatchRect" parent="."]
custom_minimum_size = Vector2(500, 0)
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -250.0
offset_right = 250.0
offset_bottom = 44.0
grow_horizontal = 2
texture = ExtResource("1_kr1r5")
region_rect = Rect2(1, 27, 86, 8)
patch_margin_left = 8
patch_margin_top = 7
patch_margin_right = 8
patch_margin_bottom = 6
[node name="Title" type="Label" parent="Title"]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -34.5
offset_top = -18.0
offset_right = 34.5
offset_bottom = 18.0
grow_horizontal = 2
grow_vertical = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 27
text = "游戏平台"
vertical_alignment = 1
[node name="previous" type="NinePatchRect" parent="Title"]
layout_mode = 1
anchors_preset = 4
anchor_top = 0.5
anchor_bottom = 0.5
offset_left = 9.0
offset_top = -15.0
offset_right = 39.0
offset_bottom = 15.0
grow_vertical = 2
texture = ExtResource("2_6a8uh")
region_rect = Rect2(0, 0, 6, 9)
[node name="Button" type="Button" parent="Title/previous"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_styles/focus = SubResource("StyleBoxEmpty_6a8uh")
theme_override_styles/hover = SubResource("StyleBoxEmpty_bu5lv")
theme_override_styles/pressed = SubResource("StyleBoxEmpty_mue51")
flat = true
[node name="AnimationPlayer" type="AnimationPlayer" parent="Title/previous"]
root_node = NodePath("../../..")
libraries = {
&"": SubResource("AnimationLibrary_mue51")
}
autoplay = "base"
[node name="next" type="NinePatchRect" parent="Title"]
layout_mode = 1
anchors_preset = 6
anchor_left = 1.0
anchor_top = 0.5
anchor_right = 1.0
anchor_bottom = 0.5
offset_left = -39.0
offset_top = -15.0
offset_right = -9.0
offset_bottom = 15.0
grow_horizontal = 0
grow_vertical = 2
texture = ExtResource("2_6a8uh")
region_rect = Rect2(6, 0, 6, 9)
[node name="Button" type="Button" parent="Title/next"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_styles/focus = SubResource("StyleBoxEmpty_wpgbx")
theme_override_styles/hover = SubResource("StyleBoxEmpty_r6b7x")
theme_override_styles/pressed = SubResource("StyleBoxEmpty_hsiy4")
flat = true
[node name="AnimationPlayer" type="AnimationPlayer" parent="Title/next"]
libraries = {
&"": SubResource("AnimationLibrary_r6b7x")
}
autoplay = "base"
[node name="Option" type="Control" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
[node name="Name" type="Label" parent="Option"]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -75.0
offset_top = -129.5
offset_right = 75.0
offset_bottom = -95.5
grow_horizontal = 2
grow_vertical = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 24
text = "台式 & 笔记本"
horizontal_alignment = 1
[node name="Info" type="NinePatchRect" parent="Option"]
custom_minimum_size = Vector2(440, 200)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -220.0
offset_top = -92.0
offset_right = 220.0
offset_bottom = 130.0
grow_horizontal = 2
grow_vertical = 2
texture = ExtResource("1_kr1r5")
region_rect = Rect2(92, 38, 76, 34)
patch_margin_left = 2
patch_margin_top = 33
patch_margin_right = 2
patch_margin_bottom = 33
[node name="Icon" type="NinePatchRect" parent="Option/Info"]
custom_minimum_size = Vector2(144, 144)
layout_mode = 1
anchors_preset = 4
anchor_top = 0.5
anchor_bottom = 0.5
offset_top = -108.0
offset_right = 190.0
offset_bottom = 82.0
grow_vertical = 2
texture = ExtResource("3_bu5lv")
[node name="Info_List_1" type="GridContainer" parent="Option/Info"]
layout_mode = 1
anchors_preset = 6
anchor_left = 1.0
anchor_top = 0.5
anchor_right = 1.0
anchor_bottom = 0.5
offset_left = -225.0
offset_top = -82.0
offset_right = -9.0
offset_bottom = 19.0
grow_horizontal = 0
grow_vertical = 2
theme_override_constants/v_separation = 7
[node name="Info_1" type="HBoxContainer" parent="Option/Info/Info_List_1"]
layout_mode = 2
theme_override_constants/separation = 0
[node name="start" type="Label" parent="Option/Info/Info_List_1/Info_1"]
custom_minimum_size = Vector2(6, 0)
layout_mode = 2
theme_override_styles/normal = SubResource("StyleBoxFlat_nckjb")
text = " "
label_settings = ExtResource("4_mue51")
[node name="title" type="Label" parent="Option/Info/Info_List_1/Info_1"]
custom_minimum_size = Vector2(90, 0)
layout_mode = 2
theme_override_styles/normal = SubResource("StyleBoxFlat_nckjb")
text = "制造商"
label_settings = ExtResource("4_mue51")
[node name="info" type="Label" parent="Option/Info/Info_List_1/Info_1"]
custom_minimum_size = Vector2(120, 0)
layout_mode = 2
text = "不定"
label_settings = ExtResource("4_mue51")
horizontal_alignment = 1
[node name="Info_2" type="HBoxContainer" parent="Option/Info/Info_List_1"]
layout_mode = 2
theme_override_constants/separation = 0
[node name="start" type="Label" parent="Option/Info/Info_List_1/Info_2"]
custom_minimum_size = Vector2(6, 0)
layout_mode = 2
theme_override_styles/normal = SubResource("StyleBoxFlat_nckjb")
text = " "
label_settings = ExtResource("4_mue51")
[node name="title" type="Label" parent="Option/Info/Info_List_1/Info_2"]
custom_minimum_size = Vector2(90, 0)
layout_mode = 2
theme_override_styles/normal = SubResource("StyleBoxFlat_6r1e0")
text = "发售"
label_settings = ExtResource("4_mue51")
[node name="info" type="Label" parent="Option/Info/Info_List_1/Info_2"]
custom_minimum_size = Vector2(60, 0)
layout_mode = 2
text = "50"
label_settings = ExtResource("4_mue51")
horizontal_alignment = 2
[node name="unit" type="Label" parent="Option/Info/Info_List_1/Info_2"]
layout_mode = 2
text = "万台"
label_settings = ExtResource("4_mue51")
horizontal_alignment = 2
[node name="Info_3" type="HBoxContainer" parent="Option/Info/Info_List_1"]
layout_mode = 2
theme_override_constants/separation = 0
[node name="start" type="Label" parent="Option/Info/Info_List_1/Info_3"]
custom_minimum_size = Vector2(6, 0)
layout_mode = 2
theme_override_styles/normal = SubResource("StyleBoxFlat_nckjb")
text = " "
label_settings = ExtResource("4_mue51")
[node name="title" type="Label" parent="Option/Info/Info_List_1/Info_3"]
custom_minimum_size = Vector2(90, 0)
layout_mode = 2
theme_override_styles/normal = SubResource("StyleBoxFlat_at31c")
text = "市场占比"
label_settings = ExtResource("4_mue51")
[node name="info" type="Label" parent="Option/Info/Info_List_1/Info_3"]
custom_minimum_size = Vector2(60, 0)
layout_mode = 2
text = "38"
label_settings = ExtResource("4_mue51")
horizontal_alignment = 2
[node name="unit" type="Label" parent="Option/Info/Info_List_1/Info_3"]
layout_mode = 2
text = "%"
label_settings = ExtResource("4_mue51")
horizontal_alignment = 2
[node name="Info_List_2" type="HBoxContainer" parent="Option/Info"]
layout_mode = 1
anchors_preset = 7
anchor_left = 0.5
anchor_top = 1.0
anchor_right = 0.5
anchor_bottom = 1.0
offset_left = -191.5
offset_top = -38.0
offset_right = 191.5
offset_bottom = -10.0
grow_horizontal = 2
grow_vertical = 0
alignment = 1
[node name="info_4" type="HBoxContainer" parent="Option/Info/Info_List_2"]
layout_mode = 2
[node name="title" type="Label" parent="Option/Info/Info_List_2/info_4"]
custom_minimum_size = Vector2(100, 0)
layout_mode = 2
theme_override_styles/normal = SubResource("StyleBoxFlat_qrj6y")
text = "开发费用"
label_settings = ExtResource("4_mue51")
horizontal_alignment = 1
[node name="info" type="Label" parent="Option/Info/Info_List_2/info_4"]
custom_minimum_size = Vector2(70, 0)
layout_mode = 2
text = "500"
label_settings = ExtResource("4_mue51")
horizontal_alignment = 1
[node name="info_5" type="HBoxContainer" parent="Option/Info/Info_List_2"]
layout_mode = 2
[node name="title" type="Label" parent="Option/Info/Info_List_2/info_5"]
custom_minimum_size = Vector2(100, 0)
layout_mode = 2
theme_override_styles/normal = SubResource("StyleBoxFlat_v0qi7")
text = "开发次数"
label_settings = ExtResource("4_mue51")
horizontal_alignment = 1
[node name="info" type="Label" parent="Option/Info/Info_List_2/info_5"]
custom_minimum_size = Vector2(30, 0)
layout_mode = 2
text = "0"
label_settings = ExtResource("4_mue51")
horizontal_alignment = 2
[node name="unit" type="Label" parent="Option/Info/Info_List_2/info_5"]
layout_mode = 2
text = "次"
label_settings = ExtResource("4_mue51")
horizontal_alignment = 2
[node name="Confirm" type="Button" parent="."]
custom_minimum_size = Vector2(70, 0)
layout_mode = 1
anchors_preset = 7
anchor_left = 0.5
anchor_top = 1.0
anchor_right = 0.5
anchor_bottom = 1.0
offset_left = -35.0
offset_top = -43.0
offset_right = 35.0
grow_horizontal = 2
grow_vertical = 0
theme = ExtResource("5_wpgbx")
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_colors/font_focus_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 23
theme_override_styles/focus = SubResource("StyleBoxEmpty_rl25l")
theme_override_styles/hover_pressed = SubResource("StyleBoxFlat_tndxe")
theme_override_styles/hover = SubResource("StyleBoxFlat_xx8ge")
theme_override_styles/pressed = SubResource("StyleBoxEmpty_ep6fu")
theme_override_styles/normal = SubResource("StyleBoxEmpty_ci4su")
text = "确定"

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b2n1f22bd06tc"
path="res://.godot/imported/platform_1.png-0e1645d04a8add797ec527f1c14cb0a9.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://UI/popup/task_development/platform/platform_1.png"
dest_files=["res://.godot/imported/platform_1.png-0e1645d04a8add797ec527f1c14cb0a9.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c53cngwtsi5hb"
path="res://.godot/imported/platform_2.png-f66d0147b3d6ab941ada98eef168dcb4.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://UI/popup/task_development/platform/platform_2.png"
dest_files=["res://.godot/imported/platform_2.png-f66d0147b3d6ab941ada98eef168dcb4.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View File

@ -0,0 +1,16 @@
extends NinePatchRect
@onready var confirm_button: Button = $Confirm
func _ready() -> void:
var node_name = str(self.name)
add_to_group(node_name)
if confirm_button:
if not confirm_button.pressed.is_connected(_on_confirm_pressed):
confirm_button.pressed.connect(_on_confirm_pressed)
func _on_confirm_pressed():
get_parent().start_task()
self.hide()
get_parent().close_all_popups()

View File

@ -0,0 +1 @@
uid://ciqayqceaadw4

View File

@ -0,0 +1,632 @@
[gd_scene load_steps=31 format=3 uid="uid://be000l53jxash"]
[ext_resource type="Texture2D" uid="uid://pucudatqrcrq" path="res://UI/popup/popup_bg_1.png" id="1_0ruly"]
[ext_resource type="Script" uid="uid://ciqayqceaadw4" path="res://UI/popup/task_development/proposal/proposal.gd" id="2_uudne"]
[ext_resource type="Texture2D" uid="uid://dgcugleiv7sfw" path="res://UI/popup/task_development/arrow.png" id="3_uudne"]
[ext_resource type="FontFile" uid="uid://egugs822n8gr" path="res://UI/font/AlimamaFangYuanTiVF-Thin.ttf" id="4_csatq"]
[ext_resource type="LabelSettings" uid="uid://qcn0aduvrgvw" path="res://UI/tres/task_development_platform_label_settings.tres" id="5_l6j0s"]
[ext_resource type="Texture2D" uid="uid://bb3kyiufyyj05" path="res://Entity/NPC/NPC_1_UI.png" id="5_uudne"]
[ext_resource type="Theme" uid="uid://bau80ps6kx783" path="res://UI/tres/Bottom_Info_button_theme.tres" id="6_6v1sj"]
[ext_resource type="Texture2D" uid="uid://hdidwixuokxn" path="res://Entity/NPC/npc_ability_icon.png" id="6_ybd2x"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_6a8uh"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_bu5lv"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_mue51"]
[sub_resource type="Animation" id="Animation_bu5lv"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Title/previous:position")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector2(9, 7)]
}
[sub_resource type="Animation" id="Animation_6a8uh"]
resource_name = "base"
length = 0.6
loop_mode = 1
step = 0.2
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Title/previous:position")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 0.2, 0.4, 0.6),
"transitions": PackedFloat32Array(1, 1, 1, 1),
"update": 1,
"values": [Vector2(16, 7), Vector2(12, 7), Vector2(9, 7), Vector2(16, 7)]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_mue51"]
_data = {
&"RESET": SubResource("Animation_bu5lv"),
&"base": SubResource("Animation_6a8uh")
}
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_wpgbx"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_r6b7x"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_hsiy4"]
[sub_resource type="Animation" id="Animation_wpgbx"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:position")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector2(461, 7)]
}
[sub_resource type="Animation" id="Animation_mue51"]
resource_name = "base"
length = 0.6
loop_mode = 1
step = 0.2
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:position")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 0.2, 0.4, 0.6),
"transitions": PackedFloat32Array(1, 1, 1, 1),
"update": 1,
"values": [Vector2(455, 7), Vector2(458, 7), Vector2(461, 7), Vector2(455, 7)]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_r6b7x"]
_data = {
&"RESET": SubResource("Animation_wpgbx"),
&"base": SubResource("Animation_mue51")
}
[sub_resource type="LabelSettings" id="LabelSettings_l6j0s"]
font = ExtResource("4_csatq")
font_size = 23
font_color = Color(0, 0, 0, 1)
[sub_resource type="LabelSettings" id="LabelSettings_6v1sj"]
font = ExtResource("4_csatq")
font_size = 24
font_color = Color(0, 0, 0, 1)
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_nckjb"]
bg_color = Color(0.913725, 0.913725, 0.913725, 0.913725)
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_qrj6y"]
bg_color = Color(0.913725, 0.913725, 0.913725, 0.913725)
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_v0qi7"]
bg_color = Color(0.913725, 0.913725, 0.913725, 0.913725)
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_rl25l"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_tndxe"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_xx8ge"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ep6fu"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ci4su"]
[node name="proposal" type="NinePatchRect"]
custom_minimum_size = Vector2(500, 350)
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -250.0
offset_top = -175.0
offset_right = 250.0
offset_bottom = 175.0
grow_horizontal = 2
grow_vertical = 2
texture = ExtResource("1_0ruly")
region_rect = Rect2(1, 35, 86, 53)
patch_margin_left = 8
patch_margin_top = 35
patch_margin_right = 8
patch_margin_bottom = 32
script = ExtResource("2_uudne")
[node name="Title" type="NinePatchRect" parent="."]
custom_minimum_size = Vector2(500, 0)
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -250.0
offset_right = 250.0
offset_bottom = 44.0
grow_horizontal = 2
texture = ExtResource("1_0ruly")
region_rect = Rect2(1, 27, 86, 8)
patch_margin_left = 8
patch_margin_top = 7
patch_margin_right = 8
patch_margin_bottom = 6
[node name="Title" type="Label" parent="Title"]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -34.5
offset_top = -18.0
offset_right = 34.5
offset_bottom = 18.0
grow_horizontal = 2
grow_vertical = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 27
text = "策划案负责人"
vertical_alignment = 1
[node name="previous" type="NinePatchRect" parent="Title"]
layout_mode = 1
anchors_preset = 4
anchor_top = 0.5
anchor_bottom = 0.5
offset_left = 9.0
offset_top = -15.0
offset_right = 39.0
offset_bottom = 15.0
grow_vertical = 2
texture = ExtResource("3_uudne")
region_rect = Rect2(0, 0, 6, 9)
[node name="Button" type="Button" parent="Title/previous"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_styles/focus = SubResource("StyleBoxEmpty_6a8uh")
theme_override_styles/hover = SubResource("StyleBoxEmpty_bu5lv")
theme_override_styles/pressed = SubResource("StyleBoxEmpty_mue51")
flat = true
[node name="AnimationPlayer" type="AnimationPlayer" parent="Title/previous"]
root_node = NodePath("../../..")
libraries = {
&"": SubResource("AnimationLibrary_mue51")
}
autoplay = "base"
[node name="next" type="NinePatchRect" parent="Title"]
layout_mode = 1
anchors_preset = 6
anchor_left = 1.0
anchor_top = 0.5
anchor_right = 1.0
anchor_bottom = 0.5
offset_left = -39.0
offset_top = -15.0
offset_right = -9.0
offset_bottom = 15.0
grow_horizontal = 0
grow_vertical = 2
texture = ExtResource("3_uudne")
region_rect = Rect2(6, 0, 6, 9)
[node name="Button" type="Button" parent="Title/next"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_styles/focus = SubResource("StyleBoxEmpty_wpgbx")
theme_override_styles/hover = SubResource("StyleBoxEmpty_r6b7x")
theme_override_styles/pressed = SubResource("StyleBoxEmpty_hsiy4")
flat = true
[node name="AnimationPlayer" type="AnimationPlayer" parent="Title/next"]
libraries = {
&"": SubResource("AnimationLibrary_r6b7x")
}
autoplay = "base"
[node name="Option" type="Control" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
[node name="NPC_Info_1" type="HBoxContainer" parent="Option"]
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -170.0
offset_top = 47.0
offset_right = 202.0
offset_bottom = 87.0
grow_horizontal = 2
theme_override_constants/separation = 160
[node name="Name" type="Label" parent="Option/NPC_Info_1"]
layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 24
text = "米子一"
label_settings = SubResource("LabelSettings_l6j0s")
horizontal_alignment = 1
[node name="Staff" type="Label" parent="Option/NPC_Info_1"]
custom_minimum_size = Vector2(140, 0)
layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_fonts/font = ExtResource("4_csatq")
theme_override_font_sizes/font_size = 20
text = "员工"
horizontal_alignment = 2
[node name="NPC_Info_2" type="NinePatchRect" parent="Option"]
custom_minimum_size = Vector2(440, 200)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -220.0
offset_top = -92.0
offset_right = 220.0
offset_bottom = 130.0
grow_horizontal = 2
grow_vertical = 2
texture = ExtResource("1_0ruly")
region_rect = Rect2(92, 38, 76, 34)
patch_margin_left = 2
patch_margin_top = 33
patch_margin_right = 2
patch_margin_bottom = 33
[node name="Info_List_0" type="VBoxContainer" parent="Option/NPC_Info_2"]
custom_minimum_size = Vector2(0, 95)
layout_mode = 0
offset_left = 26.0
offset_top = 23.0
offset_right = 185.0
offset_bottom = 147.0
theme_override_constants/separation = 14
[node name="Job" type="HBoxContainer" parent="Option/NPC_Info_2/Info_List_0"]
layout_mode = 2
theme_override_constants/separation = 25
[node name="Label" type="Label" parent="Option/NPC_Info_2/Info_List_0/Job"]
custom_minimum_size = Vector2(100, 0)
layout_mode = 2
text = "产品总监"
label_settings = SubResource("LabelSettings_6v1sj")
[node name="Level" type="Label" parent="Option/NPC_Info_2/Info_List_0/Job"]
layout_mode = 2
text = "Lv1"
label_settings = SubResource("LabelSettings_6v1sj")
horizontal_alignment = 2
[node name="Icon" type="TextureRect" parent="Option/NPC_Info_2/Info_List_0"]
custom_minimum_size = Vector2(95, 95)
layout_mode = 2
texture = ExtResource("5_uudne")
stretch_mode = 5
[node name="Info_List_1" type="GridContainer" parent="Option/NPC_Info_2"]
layout_mode = 1
anchors_preset = 6
anchor_left = 1.0
anchor_top = 0.5
anchor_right = 1.0
anchor_bottom = 0.5
offset_left = -204.0
offset_top = -87.0
offset_right = -8.0
offset_bottom = 46.0
grow_horizontal = 0
grow_vertical = 2
theme_override_constants/v_separation = 7
[node name="Info_1" type="HBoxContainer" parent="Option/NPC_Info_2/Info_List_1"]
layout_mode = 2
theme_override_constants/separation = 0
[node name="icon" type="NinePatchRect" parent="Option/NPC_Info_2/Info_List_1/Info_1"]
custom_minimum_size = Vector2(28, 28)
layout_mode = 2
texture = ExtResource("6_ybd2x")
region_rect = Rect2(38, 30, 20, 15)
[node name="title" type="Label" parent="Option/NPC_Info_2/Info_List_1/Info_1"]
custom_minimum_size = Vector2(60, 0)
layout_mode = 2
theme_override_fonts/font = ExtResource("4_csatq")
theme_override_styles/normal = SubResource("StyleBoxFlat_nckjb")
text = "策划"
label_settings = ExtResource("5_l6j0s")
horizontal_alignment = 1
[node name="info" type="Label" parent="Option/NPC_Info_2/Info_List_1/Info_1"]
custom_minimum_size = Vector2(100, 0)
layout_mode = 2
text = "80"
label_settings = ExtResource("5_l6j0s")
horizontal_alignment = 1
[node name="Info_2" type="HBoxContainer" parent="Option/NPC_Info_2/Info_List_1"]
layout_mode = 2
theme_override_constants/separation = 0
[node name="icon" type="NinePatchRect" parent="Option/NPC_Info_2/Info_List_1/Info_2"]
custom_minimum_size = Vector2(28, 28)
layout_mode = 2
texture = ExtResource("6_ybd2x")
region_rect = Rect2(12, 31, 17, 16)
[node name="title" type="Label" parent="Option/NPC_Info_2/Info_List_1/Info_2"]
custom_minimum_size = Vector2(60, 0)
layout_mode = 2
theme_override_fonts/font = ExtResource("4_csatq")
theme_override_styles/normal = SubResource("StyleBoxFlat_nckjb")
text = "程序"
label_settings = ExtResource("5_l6j0s")
horizontal_alignment = 1
[node name="info" type="Label" parent="Option/NPC_Info_2/Info_List_1/Info_2"]
custom_minimum_size = Vector2(100, 0)
layout_mode = 2
text = "80"
label_settings = ExtResource("5_l6j0s")
horizontal_alignment = 1
[node name="Info_3" type="HBoxContainer" parent="Option/NPC_Info_2/Info_List_1"]
layout_mode = 2
theme_override_constants/separation = 0
[node name="icon" type="NinePatchRect" parent="Option/NPC_Info_2/Info_List_1/Info_3"]
custom_minimum_size = Vector2(28, 28)
layout_mode = 2
texture = ExtResource("6_ybd2x")
region_rect = Rect2(63, 29, 21, 16)
[node name="title" type="Label" parent="Option/NPC_Info_2/Info_List_1/Info_3"]
custom_minimum_size = Vector2(60, 0)
layout_mode = 2
theme_override_fonts/font = ExtResource("4_csatq")
theme_override_styles/normal = SubResource("StyleBoxFlat_nckjb")
text = "美术"
label_settings = ExtResource("5_l6j0s")
horizontal_alignment = 1
[node name="info" type="Label" parent="Option/NPC_Info_2/Info_List_1/Info_3"]
custom_minimum_size = Vector2(100, 0)
layout_mode = 2
text = "80"
label_settings = ExtResource("5_l6j0s")
horizontal_alignment = 1
[node name="Info_4" type="HBoxContainer" parent="Option/NPC_Info_2/Info_List_1"]
layout_mode = 2
theme_override_constants/separation = 0
[node name="icon" type="NinePatchRect" parent="Option/NPC_Info_2/Info_List_1/Info_4"]
custom_minimum_size = Vector2(28, 28)
layout_mode = 2
texture = ExtResource("6_ybd2x")
region_rect = Rect2(92, 29, 22, 16)
[node name="title" type="Label" parent="Option/NPC_Info_2/Info_List_1/Info_4"]
custom_minimum_size = Vector2(60, 0)
layout_mode = 2
theme_override_fonts/font = ExtResource("4_csatq")
theme_override_styles/normal = SubResource("StyleBoxFlat_nckjb")
text = "音频"
label_settings = ExtResource("5_l6j0s")
horizontal_alignment = 1
[node name="info" type="Label" parent="Option/NPC_Info_2/Info_List_1/Info_4"]
custom_minimum_size = Vector2(100, 0)
layout_mode = 2
text = "80"
label_settings = ExtResource("5_l6j0s")
horizontal_alignment = 1
[node name="Info_List_2" type="HBoxContainer" parent="Option/NPC_Info_2"]
custom_minimum_size = Vector2(160, 10)
layout_mode = 1
offset_left = 27.0
offset_top = 159.0
offset_right = 187.0
offset_bottom = 169.0
theme_override_constants/separation = 0
alignment = 1
[node name="Segment_1" type="ColorRect" parent="Option/NPC_Info_2/Info_List_2"]
custom_minimum_size = Vector2(6, 0)
layout_mode = 2
[node name="Segment_2" type="ColorRect" parent="Option/NPC_Info_2/Info_List_2"]
custom_minimum_size = Vector2(6, 0)
layout_mode = 2
[node name="Segment_3" type="ColorRect" parent="Option/NPC_Info_2/Info_List_2"]
custom_minimum_size = Vector2(6, 0)
layout_mode = 2
[node name="Segment_4" type="ColorRect" parent="Option/NPC_Info_2/Info_List_2"]
custom_minimum_size = Vector2(6, 0)
layout_mode = 2
[node name="Segment_5" type="ColorRect" parent="Option/NPC_Info_2/Info_List_2"]
custom_minimum_size = Vector2(6, 0)
layout_mode = 2
[node name="Segment_6" type="ColorRect" parent="Option/NPC_Info_2/Info_List_2"]
custom_minimum_size = Vector2(6, 0)
layout_mode = 2
[node name="Segment_7" type="ColorRect" parent="Option/NPC_Info_2/Info_List_2"]
custom_minimum_size = Vector2(6, 0)
layout_mode = 2
[node name="Segment_8" type="ColorRect" parent="Option/NPC_Info_2/Info_List_2"]
custom_minimum_size = Vector2(6, 0)
layout_mode = 2
[node name="Segment_9" type="ColorRect" parent="Option/NPC_Info_2/Info_List_2"]
custom_minimum_size = Vector2(6, 0)
layout_mode = 2
[node name="Segment_10" type="ColorRect" parent="Option/NPC_Info_2/Info_List_2"]
custom_minimum_size = Vector2(6, 0)
layout_mode = 2
[node name="Segment_11" type="ColorRect" parent="Option/NPC_Info_2/Info_List_2"]
custom_minimum_size = Vector2(6, 0)
layout_mode = 2
[node name="Segment_12" type="ColorRect" parent="Option/NPC_Info_2/Info_List_2"]
custom_minimum_size = Vector2(6, 0)
layout_mode = 2
[node name="Segment_13" type="ColorRect" parent="Option/NPC_Info_2/Info_List_2"]
custom_minimum_size = Vector2(6, 0)
layout_mode = 2
[node name="Segment_14" type="ColorRect" parent="Option/NPC_Info_2/Info_List_2"]
custom_minimum_size = Vector2(6, 0)
layout_mode = 2
[node name="Segment_15" type="ColorRect" parent="Option/NPC_Info_2/Info_List_2"]
custom_minimum_size = Vector2(6, 0)
layout_mode = 2
[node name="Segment_16" type="ColorRect" parent="Option/NPC_Info_2/Info_List_2"]
custom_minimum_size = Vector2(6, 0)
layout_mode = 2
[node name="Segment_17" type="ColorRect" parent="Option/NPC_Info_2/Info_List_2"]
custom_minimum_size = Vector2(6, 0)
layout_mode = 2
[node name="Segment_18" type="ColorRect" parent="Option/NPC_Info_2/Info_List_2"]
custom_minimum_size = Vector2(6, 0)
layout_mode = 2
[node name="Segment_19" type="ColorRect" parent="Option/NPC_Info_2/Info_List_2"]
custom_minimum_size = Vector2(6, 0)
layout_mode = 2
[node name="Segment_20" type="ColorRect" parent="Option/NPC_Info_2/Info_List_2"]
custom_minimum_size = Vector2(6, 0)
layout_mode = 2
[node name="Info_List_3" type="HBoxContainer" parent="Option/NPC_Info_2"]
layout_mode = 1
anchors_preset = 7
anchor_left = 0.5
anchor_top = 1.0
anchor_right = 0.5
anchor_bottom = 1.0
offset_left = -191.5
offset_top = -38.0
offset_right = 191.5
offset_bottom = -10.0
grow_horizontal = 2
grow_vertical = 0
[node name="info_4" type="HBoxContainer" parent="Option/NPC_Info_2/Info_List_3"]
layout_mode = 2
[node name="title" type="Label" parent="Option/NPC_Info_2/Info_List_3/info_4"]
custom_minimum_size = Vector2(100, 0)
layout_mode = 2
theme_override_fonts/font = ExtResource("4_csatq")
theme_override_styles/normal = SubResource("StyleBoxFlat_qrj6y")
text = "费用"
label_settings = ExtResource("5_l6j0s")
horizontal_alignment = 1
[node name="info" type="Label" parent="Option/NPC_Info_2/Info_List_3/info_4"]
custom_minimum_size = Vector2(70, 0)
layout_mode = 2
text = "500"
label_settings = ExtResource("5_l6j0s")
horizontal_alignment = 1
[node name="info_5" type="HBoxContainer" parent="Option/NPC_Info_2/Info_List_3"]
visible = false
layout_mode = 2
[node name="title" type="Label" parent="Option/NPC_Info_2/Info_List_3/info_5"]
custom_minimum_size = Vector2(100, 0)
layout_mode = 2
theme_override_styles/normal = SubResource("StyleBoxFlat_v0qi7")
text = "开发次数"
label_settings = ExtResource("5_l6j0s")
horizontal_alignment = 1
[node name="info" type="Label" parent="Option/NPC_Info_2/Info_List_3/info_5"]
custom_minimum_size = Vector2(30, 0)
layout_mode = 2
text = "0"
label_settings = ExtResource("5_l6j0s")
horizontal_alignment = 2
[node name="unit" type="Label" parent="Option/NPC_Info_2/Info_List_3/info_5"]
layout_mode = 2
text = "次"
label_settings = ExtResource("5_l6j0s")
horizontal_alignment = 2
[node name="Confirm" type="Button" parent="."]
custom_minimum_size = Vector2(70, 0)
layout_mode = 1
anchors_preset = 7
anchor_left = 0.5
anchor_top = 1.0
anchor_right = 0.5
anchor_bottom = 1.0
offset_left = -35.0
offset_top = -43.0
offset_right = 35.0
grow_horizontal = 2
grow_vertical = 0
theme = ExtResource("6_6v1sj")
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_colors/font_focus_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 23
theme_override_styles/focus = SubResource("StyleBoxEmpty_rl25l")
theme_override_styles/hover_pressed = SubResource("StyleBoxFlat_tndxe")
theme_override_styles/hover = SubResource("StyleBoxFlat_xx8ge")
theme_override_styles/pressed = SubResource("StyleBoxEmpty_ep6fu")
theme_override_styles/normal = SubResource("StyleBoxEmpty_ci4su")
text = "确定"

View File

@ -0,0 +1,291 @@
# 文件名: strategy.gd
# 脚本作用: 控制策略选择界面的显示和交互 (使用主题资源处理焦点视觉)
# 版本:已根据方案 A 修改,使用通用 GameState 接口,从父节点获取初始选择
extends NinePatchRect
# --- 节点引用 ---
@onready var options_container: VBoxContainer = $Option/Options/List_1
@onready var explanation_label: Label = $Option/Explaination
# --- 内部状态 ---
var option_buttons: Array[Button] = [] # 存储选项按钮节点的数组
var highlighted_button: Button = null # 当前逻辑上选中的按钮 (用于区分首次点击和确认点击)
var current_strategy_data: Dictionary = {} # 存储从 GameState 获取的原始策略数据
var initial_selection_key: String = "" # 由父节点传入的初始/当前选择的策略 Key (中文名)
# --- 初始化 ---
func _ready() -> void:
add_to_group(str(name)) # 便于父节点或其他系统查找
# --- 获取按钮引用 ---
option_buttons.clear()
for child in options_container.get_children():
if child is Button:
option_buttons.append(child)
# 断开旧连接以防万一 (虽然理论上 _ready 只执行一次)
if child.is_connected("pressed", _on_option_button_pressed):
child.disconnect("pressed", _on_option_button_pressed)
# --- 连接 GameState 通用值变化信号 ---
# 用于响应策略数据可能的动态变化 (如解锁新策略)
if GameState and not GameState.state_value_changed.is_connected(_on_game_state_value_changed):
GameState.state_value_changed.connect(_on_game_state_value_changed)
# --- 连接 visibility_changed 信号 ---
# 确保每次界面可见时都刷新列表内容
if not is_connected("visibility_changed", _on_visibility_changed):
visibility_changed.connect(_on_visibility_changed)
# _update_strategy_list() 不再在 _ready 中调用,移至 _on_visibility_changed
# --- 父节点调用接口 ---
# 由父节点 (task_development.gd) 在显示此弹窗前调用,传入当前的临时选择
func set_initial_selection(strategy_key: String) -> void:
initial_selection_key = strategy_key
print("Strategy: Initial selection key set by parent: '", initial_selection_key, "'")
# 注意:这里只存储值,实际的高亮逻辑仍在 _update_strategy_list 中处理
# --- 信号处理 ---
# 处理可见性变化的函数
func _on_visibility_changed() -> void:
print("Strategy: _on_visibility_changed called, visible = ", is_visible_in_tree())
# 当节点变为可见时,更新列表并设置初始焦点
if is_visible_in_tree():
print("Strategy popup became visible. Updating list and setting focus...")
# 确保在更新列表前initial_selection_key 已被父节点设置
_update_strategy_list()
# else: # 可选:如果需要在隐藏时做清理,可以在这里添加逻辑
# print("Strategy popup became hidden.")
# 当 GameState 的任何值发生变化时调用
func _on_game_state_value_changed(key: String, new_value) -> void:
# 检查是否是 task_development 数据发生了变化,这可能影响可用策略
# 注意:这是一个比较宽泛的检查。如果 task_development 下其他不相关数据变化也会触发刷新。
if key == "task_development":
# 并且确保当前界面是可见的避免在后台刷新不可见的UI
if is_visible_in_tree():
print("Strategy: Detected change in 'task_development', refreshing list...")
# 重新执行列表更新,它会使用最新的 GameState 数据
# 并尝试根据 initial_selection_key (由父节点设定,理论上不变) 恢复高亮
_update_strategy_list()
# --- 核心 UI 更新逻辑 ---
# 更新策略列表UI的函数
func _update_strategy_list() -> void:
# 0. 重置界面逻辑状态 (清除上次的高亮和说明)
_reset_highlight_state()
# 1. 从 GameState 获取最新的 task_development 数据
if not GameState:
printerr("Strategy: GameState not available during _update_strategy_list.")
for button in option_buttons: button.visible = false
explanation_label.text = "错误:无法访问游戏状态。"
return
var task_dev_data: Dictionary = GameState.get_value("task_development", {})
if task_dev_data.is_empty():
printerr("Strategy: Failed to get 'task_development' data from GameState.")
for button in option_buttons: button.visible = false
explanation_label.text = "错误:无法获取开发数据。"
return
# 提取策略子字典
current_strategy_data = task_dev_data.get("strategies", {})
if current_strategy_data.is_empty():
printerr("Strategy: 'strategies' data not found or empty in task_development.")
for button in option_buttons: button.visible = false
explanation_label.text = "没有可用的开发策略。"
return
# 2. 筛选出已启用的策略
var enabled_strategies = []
for strategy_name in current_strategy_data: # 遍历字典的键 (策略中文名)
var details: Dictionary = current_strategy_data[strategy_name]
# 确保 details 是字典并且 'enabled' 键存在且为 true
if typeof(details) == TYPE_DICTIONARY and details.get("enabled", false) == true:
enabled_strategies.append({"name": strategy_name, "details": details})
# 如果需要固定顺序,可能需要先对 enabled_strategies 排序 (基于某个字段或预设顺序)
# enabled_strategies.sort_custom(func(a, b): return a["name"] < b["name"]) # 示例:按名称排序
# 3. 填充按钮
var strategy_index: int = 0
var first_enabled_button: Button = null # 用于没有初始选择时的默认高亮
# --- 确保每次更新时正确设置按钮状态和信号连接 ---
for button in option_buttons: # 遍历场景中预设的按钮
button.visible = false # 默认隐藏
button.disabled = true # 默认禁用
# 断开旧的 pressed 信号连接,防止重复触发
if button.is_connected("pressed", _on_option_button_pressed):
button.disconnect("pressed", _on_option_button_pressed)
# 清除可能存在的旧元数据
if button.has_meta("strategy_name"):
button.remove_meta("strategy_name")
# 使用筛选出的启用策略数据填充按钮
while strategy_index < enabled_strategies.size() and strategy_index < option_buttons.size():
var button: Button = option_buttons[strategy_index]
var strategy_data: Dictionary = enabled_strategies[strategy_index]
var strategy_name: String = strategy_data["name"]
var strategy_details: Dictionary = strategy_data["details"]
# 获取按钮内部的标签节点
var title_label: Label = button.find_child("Title", true, false) as Label
var cost_label: Label = button.find_child("Cost", true, false) as Label
if title_label and cost_label:
# 设置策略名称
title_label.text = strategy_name
# 设置成本显示 (格式化为百分比)
var cost_value = strategy_details.get("Cost", null) # 尝试获取 Cost 值
if typeof(cost_value) == TYPE_FLOAT or typeof(cost_value) == TYPE_INT:
var percentage: int = int(cost_value * 100)
cost_label.text = "+%d%%" % percentage # GDScript 中 % 需要转义
else:
cost_label.text = "N/A" # 如果 Cost 不存在或不是数字
# 将策略名称存储在按钮元数据中,方便点击时获取
button.set_meta("strategy_name", strategy_name)
button.visible = true # 显示按钮
button.disabled = false # 启用按钮
# 重新连接 pressed 信号,并绑定按钮自身作为参数
button.pressed.connect(_on_option_button_pressed.bind(button))
# 记录第一个可用的按钮,作为没有初始选择时的备选高亮目标
if first_enabled_button == null:
first_enabled_button = button
else:
printerr("Strategy: 在按钮 '%s' 下未找到 Title 或 Cost 标签。" % button.name)
# 保持按钮隐藏和禁用
strategy_index += 1
# 4. 设置初始逻辑选中状态和焦点
print("--- Updating Strategy List ---")
print("Initial selection key from parent: '", initial_selection_key, "'")
var initial_highlight_button: Button = null
# 尝试根据父节点传入的 initial_selection_key 找到对应的按钮
if not initial_selection_key.is_empty():
print("Searching for matching button for key: '", initial_selection_key, "'")
for button in option_buttons:
# 检查按钮是否可见、启用,并且其元数据中的策略名与初始选择键匹配
if button.visible and not button.disabled:
var meta_name = button.get_meta("strategy_name", "")
# print(" Checking button: '", button.name, "' with meta: '", meta_name, "'") # Debug
if meta_name == initial_selection_key:
print(" Match found!: ", button.name)
initial_highlight_button = button
break # 找到后即可停止搜索
# 如果没有找到匹配的按钮 (例如初始选择为空,或对应的策略不再可用)
if initial_highlight_button == null:
print("No match found for initial key or key was empty. Trying first enabled button.")
initial_highlight_button = first_enabled_button # 使用第一个可用的按钮作为默认高亮
# 如果找到了要高亮的按钮 (无论是匹配到的还是第一个可用的)
if initial_highlight_button:
print("Button to initially highlight: ", initial_highlight_button.name)
# 设置逻辑高亮状态并尝试让其获得焦点
_set_initial_highlight(initial_highlight_button)
else:
# 如果连第一个可用的按钮都没有 (例如所有策略都未启用)
print("No button available to highlight.")
explanation_label.text = "没有可用的开发策略。" # 更新说明文本
print("--- Finished Updating Strategy List ---")
# --- 交互处理 ---
# 处理选项按钮点击事件的函数
func _on_option_button_pressed(button_node: Button) -> void:
# 从按钮元数据获取对应的策略名称
var strategy_name: String = button_node.get_meta("strategy_name", "")
if strategy_name.is_empty():
printerr("Strategy: 无法从按钮 %s 获取 strategy_name 元数据。" % button_node.name)
return
# 检查点击的按钮是否就是当前已逻辑高亮的按钮
if button_node == highlighted_button:
# --- 第二次点击 (确认选择) ---
print("Strategy: Confirmed selection - ", strategy_name)
# 1. 通知父节点更新临时选择状态
var parent_node = get_parent() # 获取父节点 (应该是 task_development)
if parent_node and parent_node.has_method("update_task_options"):
# 调用父节点的接口,传递更新信息 (使用中文键名)
parent_node.update_task_options({"开发策略": strategy_name})
print("Strategy: Updated parent task options with 开发策略 = ", strategy_name)
else:
printerr("Strategy: Parent node not found or missing 'update_task_options' method.")
# 2. 请求父节点关闭当前弹窗并返回主界面
if parent_node and parent_node.has_method("_close_child_popup_and_return"):
parent_node._close_child_popup_and_return(self)
else:
# 作为备选方案,如果找不到父节点或方法,直接隐藏自己
printerr("Strategy: Parent node not found or missing '_close_child_popup_and_return' method. Hiding self.")
self.hide()
else:
# --- 第一次点击 或 点击了不同的按钮 (更新逻辑选择和说明) ---
print("Strategy: Logically selected - ", strategy_name)
# 更新当前逻辑高亮的按钮引用
highlighted_button = button_node
# 更新底部的说明文本
# 确保 current_strategy_data 是最新的 (理论上 _update_strategy_list 刚更新过)
if current_strategy_data.has(strategy_name):
var details: Dictionary = current_strategy_data[strategy_name]
explanation_label.text = details.get("Explaination", "暂无说明。") # 使用 .get 提供默认值
else:
# 如果因为某些原因数据不同步,显示错误
explanation_label.text = "错误:找不到策略详情。"
printerr("Strategy: Could not find details for strategy '", strategy_name, "' in current_strategy_data.")
# 注意:此时按钮的视觉焦点可能还未改变,依赖于 Button 主题的 focus 样式
# _set_initial_highlight 内部会处理 grab_focus
# --- 辅助函数 ---
# (辅助函数) 重置界面逻辑高亮状态
func _reset_highlight_state() -> void:
highlighted_button = null # 清除逻辑高亮引用
explanation_label.text = "" # 清空说明文本
# (辅助函数) 设置初始逻辑选中按钮并赋予焦点
func _set_initial_highlight(button_to_highlight: Button) -> void:
print("--- Setting Initial Highlight ---")
if not is_instance_valid(button_to_highlight):
printerr("Strategy: Invalid button passed to _set_initial_highlight.")
return
print("Button received: ", button_to_highlight.name)
# 设置逻辑高亮引用
highlighted_button = button_to_highlight
print("highlighted_button variable set to: ", highlighted_button.name)
# 更新说明文本
var strategy_name = highlighted_button.get_meta("strategy_name", "")
if not strategy_name.is_empty() and current_strategy_data.has(strategy_name):
var details: Dictionary = current_strategy_data[strategy_name]
explanation_label.text = details.get("Explaination", "暂无说明。")
elif not strategy_name.is_empty():
explanation_label.text = "错误:找不到策略详情。"
printerr("Strategy: Could not find details for highlighted strategy '", strategy_name, "'")
else:
explanation_label.text = "错误:无法获取策略名称。" # 如果连元数据都没有
# 尝试让按钮获得实际输入焦点 (使用 call_deferred 确保在安全的时机执行)
# 这会让按钮应用其主题中的 "focus" 样式
if highlighted_button.is_inside_tree() and highlighted_button.visible and not highlighted_button.disabled:
print("Attempting call_deferred('grab_focus') for: ", highlighted_button.name)
highlighted_button.call_deferred("grab_focus")
else:
print("Button '%s' not valid, not in tree, hidden, or disabled. Cannot grab focus." % highlighted_button.name)
print("--- Finished Setting Initial Highlight ---")

View File

@ -0,0 +1 @@
uid://b5mfw2kc6lqpd

View File

@ -0,0 +1,351 @@
[gd_scene load_steps=5 format=3 uid="uid://ctcbxvgljrkvi"]
[ext_resource type="Texture2D" uid="uid://pucudatqrcrq" path="res://UI/popup/popup_bg_1.png" id="1_pbd5q"]
[ext_resource type="Script" uid="uid://b5mfw2kc6lqpd" path="res://UI/popup/task_development/strategy/strategy.gd" id="2_rq5uk"]
[ext_resource type="Theme" uid="uid://ddq54ba6vwyn0" path="res://UI/popup/task_development/gameplay/gameplay_option_button.tres" id="3_rq5uk"]
[ext_resource type="Texture2D" uid="uid://bb3kyiufyyj05" path="res://Entity/NPC/NPC_1_UI.png" id="4_pbd5q"]
[node name="strategy" type="NinePatchRect"]
custom_minimum_size = Vector2(500, 350)
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -250.0
offset_top = -175.0
offset_right = 250.0
offset_bottom = 175.0
grow_horizontal = 2
grow_vertical = 2
texture = ExtResource("1_pbd5q")
region_rect = Rect2(1, 35, 86, 53)
patch_margin_left = 8
patch_margin_top = 35
patch_margin_right = 8
patch_margin_bottom = 32
script = ExtResource("2_rq5uk")
[node name="Title" type="NinePatchRect" parent="."]
custom_minimum_size = Vector2(500, 0)
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -250.0
offset_right = 250.0
offset_bottom = 44.0
grow_horizontal = 2
texture = ExtResource("1_pbd5q")
region_rect = Rect2(1, 27, 86, 8)
patch_margin_left = 8
patch_margin_top = 7
patch_margin_right = 8
patch_margin_bottom = 6
[node name="Title" type="Label" parent="Title"]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -34.5
offset_top = -18.0
offset_right = 34.5
offset_bottom = 18.0
grow_horizontal = 2
grow_vertical = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 27
text = "开发策略"
vertical_alignment = 1
[node name="Option" type="Control" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
[node name="Column_Headers" type="HBoxContainer" parent="Option"]
custom_minimum_size = Vector2(420, 0)
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -210.0
offset_top = 51.0
offset_right = 210.0
offset_bottom = 82.0
grow_horizontal = 2
theme_override_constants/separation = 0
[node name="Title" type="Label" parent="Option/Column_Headers"]
custom_minimum_size = Vector2(320, 0)
layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 22
text = "开发方针"
[node name="Cost" type="Label" parent="Option/Column_Headers"]
custom_minimum_size = Vector2(80, 0)
layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 22
text = "成本"
horizontal_alignment = 2
[node name="Options" type="NinePatchRect" parent="Option"]
custom_minimum_size = Vector2(440, 170)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -220.0
offset_top = -92.0
offset_right = 220.0
offset_bottom = 78.0
grow_horizontal = 2
grow_vertical = 2
texture = ExtResource("1_pbd5q")
region_rect = Rect2(92, 38, 76, 34)
patch_margin_left = 2
patch_margin_top = 33
patch_margin_right = 2
patch_margin_bottom = 33
[node name="List_1" type="VBoxContainer" parent="Option/Options"]
custom_minimum_size = Vector2(436, 160)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -210.0
offset_top = -80.0
offset_right = 210.0
offset_bottom = 80.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/separation = 0
[node name="option_1" type="Button" parent="Option/Options/List_1"]
custom_minimum_size = Vector2(0, 32)
layout_mode = 2
theme = ExtResource("3_rq5uk")
disabled = true
flat = true
[node name="Row" type="HBoxContainer" parent="Option/Options/List_1/option_1"]
custom_minimum_size = Vector2(420, 0)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -210.0
offset_top = -15.5
offset_right = 210.0
offset_bottom = 15.5
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/separation = 0
[node name="Title" type="Label" parent="Option/Options/List_1/option_1/Row"]
custom_minimum_size = Vector2(300, 0)
layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 20
text = "正常开发"
[node name="Cost" type="Label" parent="Option/Options/List_1/option_1/Row"]
custom_minimum_size = Vector2(120, 0)
layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 20
text = "0"
horizontal_alignment = 2
[node name="option_2" type="Button" parent="Option/Options/List_1"]
custom_minimum_size = Vector2(0, 32)
layout_mode = 2
theme = ExtResource("3_rq5uk")
disabled = true
flat = true
[node name="Row" type="HBoxContainer" parent="Option/Options/List_1/option_2"]
custom_minimum_size = Vector2(420, 0)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -210.0
offset_top = -15.5
offset_right = 210.0
offset_bottom = 15.5
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/separation = 0
[node name="Title" type="Label" parent="Option/Options/List_1/option_2/Row"]
custom_minimum_size = Vector2(300, 0)
layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 20
[node name="Cost" type="Label" parent="Option/Options/List_1/option_2/Row"]
custom_minimum_size = Vector2(120, 0)
layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 20
horizontal_alignment = 2
[node name="option_3" type="Button" parent="Option/Options/List_1"]
custom_minimum_size = Vector2(0, 32)
layout_mode = 2
theme = ExtResource("3_rq5uk")
disabled = true
flat = true
[node name="Row" type="HBoxContainer" parent="Option/Options/List_1/option_3"]
custom_minimum_size = Vector2(420, 0)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -210.0
offset_top = -15.5
offset_right = 210.0
offset_bottom = 15.5
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/separation = 0
[node name="Title" type="Label" parent="Option/Options/List_1/option_3/Row"]
custom_minimum_size = Vector2(300, 0)
layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 20
[node name="Cost" type="Label" parent="Option/Options/List_1/option_3/Row"]
custom_minimum_size = Vector2(120, 0)
layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 20
horizontal_alignment = 2
[node name="option_4" type="Button" parent="Option/Options/List_1"]
custom_minimum_size = Vector2(0, 32)
layout_mode = 2
theme = ExtResource("3_rq5uk")
disabled = true
flat = true
[node name="Row" type="HBoxContainer" parent="Option/Options/List_1/option_4"]
custom_minimum_size = Vector2(420, 0)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -210.0
offset_top = -15.5
offset_right = 210.0
offset_bottom = 15.5
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/separation = 0
[node name="Title" type="Label" parent="Option/Options/List_1/option_4/Row"]
custom_minimum_size = Vector2(300, 0)
layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 20
[node name="Cost" type="Label" parent="Option/Options/List_1/option_4/Row"]
custom_minimum_size = Vector2(120, 0)
layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 20
horizontal_alignment = 2
[node name="option_5" type="Button" parent="Option/Options/List_1"]
custom_minimum_size = Vector2(0, 32)
layout_mode = 2
theme = ExtResource("3_rq5uk")
disabled = true
flat = true
[node name="Row" type="HBoxContainer" parent="Option/Options/List_1/option_5"]
custom_minimum_size = Vector2(420, 0)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -210.0
offset_top = -15.5
offset_right = 210.0
offset_bottom = 15.5
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/separation = 0
[node name="Title" type="Label" parent="Option/Options/List_1/option_5/Row"]
custom_minimum_size = Vector2(300, 0)
layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 20
[node name="Cost" type="Label" parent="Option/Options/List_1/option_5/Row"]
custom_minimum_size = Vector2(120, 0)
layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 20
horizontal_alignment = 2
[node name="Explaination" type="Label" parent="Option"]
custom_minimum_size = Vector2(200, 60)
layout_mode = 1
anchors_preset = 2
anchor_top = 1.0
anchor_bottom = 1.0
offset_left = 47.0
offset_top = -83.0
offset_right = 358.0
offset_bottom = -23.0
grow_vertical = 0
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 20
text = "说明"
horizontal_alignment = 1
vertical_alignment = 1
autowrap_mode = 3
[node name="Human" type="TextureRect" parent="Option"]
layout_mode = 1
anchors_preset = 3
anchor_left = 1.0
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = -128.0
offset_top = -90.0
offset_right = -55.0
offset_bottom = -17.0
grow_horizontal = 0
grow_vertical = 0
texture = ExtResource("4_pbd5q")

View File

@ -0,0 +1,362 @@
# 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 = {
"平台": "", # 中文 Key for the selected platform (e.g., "台式 & 笔记本")
"玩法": "", # 中文 Key for the selected gameplay (e.g., "角色扮演(RPG)")
"题材": "", # 中文 Key for the selected theme (e.g., "现代都市")
"开发策略": "", # 中文 Key for the selected strategy (e.g., "正常开发")
"预算": 0, # Calculated budget based on selections
"产品侧重点": {} # To be filled by product_focus popup
}
# --- 初始化 ---
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() # 假设 GameState 有此方法
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例如 {"平台": "Switch"}
func update_task_options(options: Dictionary) -> void:
var updated = false
for key in options: # key 是 "平台", "玩法" 等中文名
if task_info.has(key):
var new_value_key = options[key] # new_value_key 是 "Switch" 等中文 Key
if task_info[key] != new_value_key:
task_info[key] = new_value_key
updated = true
print(node_name + ": Option '%s' updated to '%s'." % [key, new_value_key])
else:
printerr(node_name + ": Invalid key '%s' provided in update_task_options." % key)
if updated:
update_button_texts() # 更新主界面按钮显示名称
calculate_and_update_budget() # 重新计算预算
print(node_name + ": Task info updated: ", task_info) # Debug
# 处理子弹出窗口确认后关闭并返回主页面的逻辑
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)

View File

@ -0,0 +1 @@
uid://chgujg3vq0jbl

View File

@ -0,0 +1,548 @@
[gd_scene load_steps=33 format=3 uid="uid://b4ll7wwg1s0qc"]
[ext_resource type="Texture2D" uid="uid://pucudatqrcrq" path="res://UI/popup/popup_bg_1.png" id="1_57348"]
[ext_resource type="Script" uid="uid://chgujg3vq0jbl" path="res://UI/popup/task_development/task_development.gd" id="1_v0qi7"]
[ext_resource type="Texture2D" uid="uid://bk8155f5le6k4" path="res://UI/main/icon_money.png" id="3_lcrvn"]
[ext_resource type="Theme" uid="uid://bau80ps6kx783" path="res://UI/tres/Bottom_Info_button_theme.tres" id="4_sqys5"]
[ext_resource type="LabelSettings" uid="uid://cvwpqds25xnfs" path="res://UI/tres/ui_main_label_number.tres" id="4_xtl2p"]
[ext_resource type="FontFile" uid="uid://egugs822n8gr" path="res://UI/font/AlimamaFangYuanTiVF-Thin.ttf" id="5_qrj6y"]
[ext_resource type="PackedScene" uid="uid://blv6i6w651pcs" path="res://UI/popup/task_development/platform/platform.tscn" id="6_eatmm"]
[ext_resource type="PackedScene" uid="uid://c7brdhj8ybu1y" path="res://UI/popup/task_development/gameplay/gameplay.tscn" id="7_xfbvu"]
[ext_resource type="PackedScene" uid="uid://bej6f0cqirn4j" path="res://UI/popup/task_development/theme/theme.tscn" id="8_nckjb"]
[ext_resource type="PackedScene" uid="uid://ctcbxvgljrkvi" path="res://UI/popup/task_development/strategy/strategy.tscn" id="9_6r1e0"]
[ext_resource type="PackedScene" uid="uid://dpshanwm4o3by" path="res://UI/popup/task_development/product_focus/product_focus.tscn" id="10_at31c"]
[ext_resource type="PackedScene" uid="uid://be000l53jxash" path="res://UI/popup/task_development/proposal/proposal.tscn" id="12_v0qi7"]
[ext_resource type="PackedScene" uid="uid://dtywh0m5odikx" path="res://UI/popup/dialogue.tscn" id="13_kctqw"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_xtl2p"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_sqys5"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ts1uk"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ka0mh"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_4dqyo"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_k5x0l"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_rl8fy"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ka0mh"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_sqys5"]
bg_color = Color(0.639216, 0.639216, 0.639216, 1)
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_4dqyo"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ln7qd"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_iashq"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_kfl1n"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_20m4o"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_rl25l"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_tndxe"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_xx8ge"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ep6fu"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ci4su"]
[node name="task_development" type="Control"]
custom_minimum_size = Vector2(1000, 600)
layout_mode = 3
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -500.0
offset_top = -300.0
offset_right = 500.0
offset_bottom = 300.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_v0qi7")
[node name="main_page" type="NinePatchRect" parent="."]
custom_minimum_size = Vector2(500, 350)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -250.0
offset_top = -175.0
offset_right = 250.0
offset_bottom = 175.0
grow_horizontal = 2
grow_vertical = 2
texture = ExtResource("1_57348")
region_rect = Rect2(1, 35, 86, 53)
patch_margin_left = 8
patch_margin_top = 35
patch_margin_right = 8
patch_margin_bottom = 32
[node name="Part_1" type="NinePatchRect" parent="main_page"]
custom_minimum_size = Vector2(500, 0)
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -250.0
offset_right = 250.0
offset_bottom = 44.0
grow_horizontal = 2
texture = ExtResource("1_57348")
region_rect = Rect2(1, 27, 86, 8)
patch_margin_left = 8
patch_margin_top = 7
patch_margin_right = 8
patch_margin_bottom = 6
[node name="Title" type="Label" parent="main_page/Part_1"]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -34.5
offset_top = -18.0
offset_right = 34.5
offset_bottom = 18.0
grow_horizontal = 2
grow_vertical = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 27
text = "新项目"
vertical_alignment = 1
[node name="Part_2" type="Control" parent="main_page"]
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -20.0
offset_top = 34.0
offset_right = 20.0
offset_bottom = 53.0
grow_horizontal = 2
[node name="HBoxContainer" type="HBoxContainer" parent="main_page/Part_2"]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -24.0
offset_top = 5.0
offset_right = 208.0
offset_bottom = 39.0
grow_horizontal = 2
grow_vertical = 2
[node name="Label" type="Label" parent="main_page/Part_2/HBoxContainer"]
layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 24
text = "预算"
horizontal_alignment = 1
[node name="Button" type="Button" parent="main_page/Part_2/HBoxContainer"]
custom_minimum_size = Vector2(180, 0)
layout_mode = 2
theme_override_colors/font_disabled_color = Color(0, 0, 0, 1)
theme_override_colors/font_hover_pressed_color = Color(0, 0, 0, 1)
theme_override_colors/font_hover_color = Color(0, 0, 0, 1)
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_colors/font_focus_color = Color(0, 0, 0, 1)
theme_override_colors/font_pressed_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 20
theme_override_styles/focus = SubResource("StyleBoxEmpty_xtl2p")
theme_override_styles/disabled = SubResource("StyleBoxEmpty_sqys5")
theme_override_styles/hover_pressed = SubResource("StyleBoxEmpty_ts1uk")
theme_override_styles/hover = SubResource("StyleBoxEmpty_ka0mh")
theme_override_styles/pressed = SubResource("StyleBoxEmpty_4dqyo")
theme_override_styles/normal = SubResource("StyleBoxEmpty_k5x0l")
text = "500"
icon = ExtResource("3_lcrvn")
flat = true
alignment = 2
icon_alignment = 2
[node name="Part_3" type="NinePatchRect" parent="main_page"]
custom_minimum_size = Vector2(440, 200)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -220.0
offset_top = -91.0
offset_right = 220.0
offset_bottom = 131.0
grow_horizontal = 2
grow_vertical = 2
texture = ExtResource("1_57348")
region_rect = Rect2(92, 38, 76, 34)
patch_margin_left = 2
patch_margin_top = 33
patch_margin_right = 2
patch_margin_bottom = 33
[node name="BG" type="NinePatchRect" parent="main_page/Part_3"]
custom_minimum_size = Vector2(440, 0)
layout_mode = 1
anchors_preset = 7
anchor_left = 0.5
anchor_top = 1.0
anchor_right = 0.5
anchor_bottom = 1.0
offset_left = -220.0
offset_top = -127.0
offset_right = 220.0
offset_bottom = 2.0
grow_horizontal = 2
grow_vertical = 0
texture = ExtResource("1_57348")
region_rect = Rect2(100, 95, 240, 75)
[node name="Select_List" type="GridContainer" parent="main_page/Part_3"]
custom_minimum_size = Vector2(320, 0)
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -180.0
offset_top = 7.0
offset_right = 180.0
offset_bottom = 179.0
grow_horizontal = 2
size_flags_horizontal = 4
theme_override_constants/h_separation = 0
columns = 2
[node name="1_left" type="NinePatchRect" parent="main_page/Part_3/Select_List"]
custom_minimum_size = Vector2(140, 40)
layout_mode = 2
size_flags_horizontal = 4
texture = ExtResource("1_57348")
region_rect = Rect2(1, 95, 36, 24)
patch_margin_left = 7
patch_margin_top = 6
patch_margin_right = 2
patch_margin_bottom = 6
[node name="Label" type="Label" parent="main_page/Part_3/Select_List/1_left"]
custom_minimum_size = Vector2(120, 0)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -60.0
offset_top = -12.0
offset_right = 60.0
offset_bottom = 24.0
grow_horizontal = 2
grow_vertical = 2
text = "平台"
label_settings = ExtResource("4_xtl2p")
horizontal_alignment = 1
[node name="1_right" type="NinePatchRect" parent="main_page/Part_3/Select_List"]
custom_minimum_size = Vector2(220, 40)
layout_mode = 2
size_flags_horizontal = 4
texture = ExtResource("1_57348")
region_rect = Rect2(38, 95, 50, 24)
patch_margin_top = 6
patch_margin_right = 6
patch_margin_bottom = 6
[node name="Button" type="Button" parent="main_page/Part_3/Select_List/1_right"]
custom_minimum_size = Vector2(100, 0)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -70.0
offset_top = -17.5
offset_right = 70.0
offset_bottom = 17.5
grow_horizontal = 2
grow_vertical = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_colors/font_focus_color = Color(0, 0, 0, 1)
theme_override_fonts/font = ExtResource("5_qrj6y")
theme_override_font_sizes/font_size = 18
theme_override_styles/focus = SubResource("StyleBoxEmpty_rl8fy")
theme_override_styles/hover_pressed = SubResource("StyleBoxFlat_ka0mh")
theme_override_styles/hover = SubResource("StyleBoxFlat_sqys5")
theme_override_styles/pressed = SubResource("StyleBoxFlat_4dqyo")
theme_override_styles/normal = SubResource("StyleBoxEmpty_ln7qd")
text = "Steam & Epic"
[node name="2_left" type="NinePatchRect" parent="main_page/Part_3/Select_List"]
custom_minimum_size = Vector2(140, 40)
layout_mode = 2
size_flags_horizontal = 4
texture = ExtResource("1_57348")
region_rect = Rect2(1, 95, 36, 24)
patch_margin_left = 7
patch_margin_top = 6
patch_margin_right = 2
patch_margin_bottom = 6
[node name="Label" type="Label" parent="main_page/Part_3/Select_List/2_left"]
custom_minimum_size = Vector2(120, 0)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -60.0
offset_top = -12.0
offset_right = 60.0
offset_bottom = 24.0
grow_horizontal = 2
grow_vertical = 2
text = "玩法"
label_settings = ExtResource("4_xtl2p")
horizontal_alignment = 1
[node name="2_right" type="NinePatchRect" parent="main_page/Part_3/Select_List"]
custom_minimum_size = Vector2(220, 40)
layout_mode = 2
size_flags_horizontal = 4
texture = ExtResource("1_57348")
region_rect = Rect2(38, 95, 50, 24)
patch_margin_top = 6
patch_margin_right = 6
patch_margin_bottom = 6
[node name="Button" type="Button" parent="main_page/Part_3/Select_List/2_right"]
custom_minimum_size = Vector2(100, 0)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -70.0
offset_top = -17.5
offset_right = 70.0
offset_bottom = 17.5
grow_horizontal = 2
grow_vertical = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_colors/font_focus_color = Color(0, 0, 0, 1)
theme_override_fonts/font = ExtResource("5_qrj6y")
theme_override_font_sizes/font_size = 18
theme_override_styles/focus = SubResource("StyleBoxEmpty_iashq")
theme_override_styles/hover_pressed = SubResource("StyleBoxFlat_ka0mh")
theme_override_styles/hover = SubResource("StyleBoxFlat_sqys5")
theme_override_styles/pressed = SubResource("StyleBoxFlat_4dqyo")
theme_override_styles/normal = SubResource("StyleBoxEmpty_ln7qd")
text = "休闲放置"
[node name="3_left" type="NinePatchRect" parent="main_page/Part_3/Select_List"]
custom_minimum_size = Vector2(140, 40)
layout_mode = 2
size_flags_horizontal = 4
texture = ExtResource("1_57348")
region_rect = Rect2(1, 95, 36, 24)
patch_margin_left = 7
patch_margin_top = 6
patch_margin_right = 2
patch_margin_bottom = 6
[node name="Label" type="Label" parent="main_page/Part_3/Select_List/3_left"]
custom_minimum_size = Vector2(120, 0)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -60.0
offset_top = -12.0
offset_right = 60.0
offset_bottom = 24.0
grow_horizontal = 2
grow_vertical = 2
text = "题材"
label_settings = ExtResource("4_xtl2p")
horizontal_alignment = 1
[node name="3_right" type="NinePatchRect" parent="main_page/Part_3/Select_List"]
custom_minimum_size = Vector2(220, 40)
layout_mode = 2
size_flags_horizontal = 4
texture = ExtResource("1_57348")
region_rect = Rect2(38, 95, 50, 24)
patch_margin_top = 6
patch_margin_right = 6
patch_margin_bottom = 6
[node name="Button" type="Button" parent="main_page/Part_3/Select_List/3_right"]
custom_minimum_size = Vector2(100, 0)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -70.0
offset_top = -17.5
offset_right = 70.0
offset_bottom = 17.5
grow_horizontal = 2
grow_vertical = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_colors/font_focus_color = Color(0, 0, 0, 1)
theme_override_fonts/font = ExtResource("5_qrj6y")
theme_override_font_sizes/font_size = 18
theme_override_styles/focus = SubResource("StyleBoxEmpty_kfl1n")
theme_override_styles/hover_pressed = SubResource("StyleBoxFlat_ka0mh")
theme_override_styles/hover = SubResource("StyleBoxFlat_sqys5")
theme_override_styles/pressed = SubResource("StyleBoxFlat_4dqyo")
theme_override_styles/normal = SubResource("StyleBoxEmpty_ln7qd")
text = "古装仙侠"
[node name="4_left" type="NinePatchRect" parent="main_page/Part_3/Select_List"]
custom_minimum_size = Vector2(140, 40)
layout_mode = 2
size_flags_horizontal = 4
texture = ExtResource("1_57348")
region_rect = Rect2(1, 95, 36, 24)
patch_margin_left = 7
patch_margin_top = 6
patch_margin_right = 2
patch_margin_bottom = 6
[node name="Label" type="Label" parent="main_page/Part_3/Select_List/4_left"]
custom_minimum_size = Vector2(120, 0)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -60.0
offset_top = -12.0
offset_right = 60.0
offset_bottom = 24.0
grow_horizontal = 2
grow_vertical = 2
text = "开发策略"
label_settings = ExtResource("4_xtl2p")
horizontal_alignment = 1
[node name="4_right" type="NinePatchRect" parent="main_page/Part_3/Select_List"]
custom_minimum_size = Vector2(220, 40)
layout_mode = 2
size_flags_horizontal = 4
texture = ExtResource("1_57348")
region_rect = Rect2(38, 95, 50, 24)
patch_margin_top = 6
patch_margin_right = 6
patch_margin_bottom = 6
[node name="Button" type="Button" parent="main_page/Part_3/Select_List/4_right"]
custom_minimum_size = Vector2(100, 0)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -70.0
offset_top = -17.5
offset_right = 70.0
offset_bottom = 17.5
grow_horizontal = 2
grow_vertical = 2
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_colors/font_focus_color = Color(0, 0, 0, 1)
theme_override_fonts/font = ExtResource("5_qrj6y")
theme_override_font_sizes/font_size = 18
theme_override_styles/focus = SubResource("StyleBoxEmpty_20m4o")
theme_override_styles/hover_pressed = SubResource("StyleBoxFlat_ka0mh")
theme_override_styles/hover = SubResource("StyleBoxFlat_sqys5")
theme_override_styles/pressed = SubResource("StyleBoxFlat_4dqyo")
theme_override_styles/normal = SubResource("StyleBoxEmpty_ln7qd")
text = "普通开发"
[node name="Confirm" type="Button" parent="main_page"]
custom_minimum_size = Vector2(70, 0)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -25.0
offset_top = 131.0
offset_right = 25.0
offset_bottom = 174.0
grow_horizontal = 2
grow_vertical = 2
theme = ExtResource("4_sqys5")
theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_colors/font_focus_color = Color(0, 0, 0, 1)
theme_override_font_sizes/font_size = 23
theme_override_styles/focus = SubResource("StyleBoxEmpty_rl25l")
theme_override_styles/hover_pressed = SubResource("StyleBoxFlat_tndxe")
theme_override_styles/hover = SubResource("StyleBoxFlat_xx8ge")
theme_override_styles/pressed = SubResource("StyleBoxEmpty_ep6fu")
theme_override_styles/normal = SubResource("StyleBoxEmpty_ci4su")
text = "确定"
[node name="platform" parent="." instance=ExtResource("6_eatmm")]
visible = false
layout_mode = 1
[node name="gameplay" parent="." instance=ExtResource("7_xfbvu")]
visible = false
layout_mode = 1
[node name="theme" parent="." instance=ExtResource("8_nckjb")]
visible = false
layout_mode = 1
[node name="strategy" parent="." instance=ExtResource("9_6r1e0")]
visible = false
layout_mode = 1
[node name="product_focus" parent="." instance=ExtResource("10_at31c")]
visible = false
layout_mode = 1
[node name="proposal" parent="." instance=ExtResource("12_v0qi7")]
visible = false
layout_mode = 1
[node name="dialogue" parent="." instance=ExtResource("13_kctqw")]
visible = false
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ihwg8cc3h6ar"
path="res://.godot/imported/task_general.png-7185c01d8faaa195d2a4887f6238e3ef.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://UI/popup/task_development/task_general.png"
dest_files=["res://.godot/imported/task_general.png-7185c01d8faaa195d2a4887f6238e3ef.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View File

@ -0,0 +1,369 @@
# theme.gd (更新版 - 对齐 gameplay.gd 逻辑)
extends NinePatchRect
# UI Node References
@onready var list_container = $Option/Info
@onready var previous_button = $Title/previous/Button
@onready var next_button = $Title/next/Button
@onready var title_label = $Title/Title
# Configuration
var items_per_page: int = 7 # 每页显示的选项数量 (保持与原 theme.gd 一致)
# Data Storage
var theme_list_nodes: Array[VBoxContainer] = [] # 存储 List_1, List_2 等节点
var option_nodes_per_list: Array = [] # 存储每个 List 对应的 option_X 按钮数组
var all_enabled_themes: Array = [] # 存储筛选后的题材数据 { "name": String, "data": Dictionary }
# State
var total_pages: int = 0
var current_page_index: int = 0
var selected_theme_key: String = "" # 当前逻辑上选中的项的 Key (用于确认)
var currently_selected_button: Button = null # 当前高亮/聚焦的按钮节点引用
var initial_key_to_highlight: String = "" # 弹窗打开时需要高亮的 Key (由父节点设置)
func _ready():
# 1. 获取 List 节点 (与原版 theme.gd 类似,但使用 gameplay.gd 的错误处理)
for i in range(1, 7): # 假设最多有 List_6 (与原 theme.gd 匹配)
var list_node = list_container.get_node_or_null("List_" + str(i))
if list_node:
theme_list_nodes.append(list_node)
else:
break # 假设 List 节点是连续的
if theme_list_nodes.is_empty():
printerr("错误:在 theme.tscn 中未找到 List 节点!无法显示选项。")
set_process(false) # 禁用处理因为没有UI元素
return
# 2. 获取每个 List 内的 Option 节点 (与原版 theme.gd 类似)
for list_node in theme_list_nodes:
var current_list_options: Array = []
for i in range(1, items_per_page + 1):
var option_node = list_node.get_node_or_null("option_" + str(i))
if option_node and option_node is Button:
current_list_options.append(option_node)
else:
# 使用 gameplay.gd 的警告格式
push_warning("警告:在 %s 中未能找到 option_%d 按钮" % [list_node.name, i])
option_nodes_per_list.append(current_list_options)
# 3. 连接按钮信号 (与 gameplay.gd 一致)
if previous_button:
if not previous_button.pressed.is_connected(_on_previous_pressed):
previous_button.pressed.connect(_on_previous_pressed)
else: printerr("错误:在 theme.tscn 中未找到 Previous 按钮")
if next_button:
if not next_button.pressed.is_connected(_on_next_pressed):
next_button.pressed.connect(_on_next_pressed)
else: printerr("错误:在 theme.tscn 中未找到 Next 按钮")
# 4. 连接 visibility_changed 信号 (与 gameplay.gd 一致)
if not is_connected("visibility_changed", _on_visibility_changed):
visibility_changed.connect(_on_visibility_changed)
# 5. 移除 _ready 中的初始数据加载和显示更新
# 这些逻辑现在完全由 _on_visibility_changed -> reset_display 处理
# --- Visibility Change Handler (与 gameplay.gd 一致) ---
func _on_visibility_changed():
if is_visible_in_tree():
print("Theme: 弹窗变为可见,调用 reset_display()")
reset_display() # 当变为可见时调用 reset
# else: # 可选:隐藏时的清理逻辑
# print("Theme: 弹窗变为隐藏。")
# --- 新增:用于父节点设置初始选中项 (与 gameplay.gd 一致) ---
func set_initial_selection(key: String):
"""
(task_development)
task_info Key
"""
initial_key_to_highlight = key
print("Theme: 父节点设置初始高亮 Key 为: '%s'" % initial_key_to_highlight)
# --- Data Processing and Display Logic ---
func _process_theme_data(all_themes_data: Dictionary):
"""【修改】根据从 GameState 获取的题材数据,筛选出启用的题材,并计算总页数。"""
all_enabled_themes.clear()
if all_themes_data.is_empty():
push_warning("警告:接收到的题材数据为空。")
total_pages = 0
return
# 遍历从 GameState 获取的题材字典
for theme_name in all_themes_data:
var theme_info = all_themes_data[theme_name]
# 检查是否为字典且 "enabled" 字段为 true
if typeof(theme_info) == TYPE_DICTIONARY and theme_info.get("enabled", false) == true:
all_enabled_themes.append({"name": theme_name, "data": theme_info})
# else: # Debugging
# print("Theme: 过滤掉未启用或格式错误的题材: ", theme_name)
# 根据筛选结果计算总页数
if items_per_page > 0:
total_pages = ceil(float(all_enabled_themes.size()) / items_per_page)
else:
total_pages = 0
printerr("错误items_per_page 为零,无法计算页数。")
print("Theme: 处理完成,找到 %d 个启用的题材,共 %d 页。" % [all_enabled_themes.size(), total_pages])
func reset_display():
"""【修改】重置状态,从 GameState 重新加载数据,并准备更新显示。"""
print("Theme: 重置显示状态 (reset_display)。")
# 1. 重置本地状态变量
currently_selected_button = null
selected_theme_key = ""
# initial_key_to_highlight 由 set_initial_selection 设置,这里不清空
# 2. 【修改】从 GameState 重新加载并处理数据
if not GameState:
printerr("错误GameState 在 reset_display 期间不可用。")
total_pages = 0
all_enabled_themes.clear()
_update_display() # 即使没有数据也要更新显示(显示空状态)
return
# --- 获取题材数据 ---
var task_dev_data = GameState.get_value("task_development", {})
if task_dev_data.is_empty():
printerr("错误:从 GameState 获取 'task_development' 数据失败。")
total_pages = 0
all_enabled_themes.clear()
else:
# --- 从 task_development 中获取 themes ---
var all_themes_data = task_dev_data.get("themes", {})
_process_theme_data(all_themes_data) # 重新处理数据,计算 total_pages
# 3. 【修改】根据 initial_key_to_highlight 确定初始页面
current_page_index = 0 # 默认为第一页
var found_initial_key = false
if total_pages > 0 and not initial_key_to_highlight.is_empty():
# 查找 initial_key_to_highlight 所在的索引和页面
for i in range(all_enabled_themes.size()):
if all_enabled_themes[i].name == initial_key_to_highlight:
current_page_index = int(floor(float(i) / items_per_page))
found_initial_key = true
print("Theme: 找到需要初始高亮的 Key '%s' 在索引 %d, 目标页面 %d" % [initial_key_to_highlight, i, current_page_index])
break # 找到了,停止循环
if not found_initial_key:
print("Theme: 需要初始高亮的 Key '%s' 未在启用的题材中找到,将显示第一页。" % initial_key_to_highlight)
# 保持 current_page_index 为 0
initial_key_to_highlight = "" # 清空,避免后续 _update_display 尝试高亮不存在的项
# 确保页面索引有效 (与 gameplay.gd 一致)
if total_pages == 0:
current_page_index = 0
elif current_page_index >= total_pages:
current_page_index = total_pages - 1
elif current_page_index < 0:
current_page_index = 0
# 4. 触发显示更新
_update_display()
func _update_display():
"""【更新版】更新列表的可见性并填充当前页面的内容。
"""
# --- 处理无数据情况 (与 gameplay.gd 一致) ---
if theme_list_nodes.is_empty():
print("Theme: 无可用的 List 节点。")
if title_label: title_label.text = "题材选择"
return
if total_pages == 0:
for list_node in theme_list_nodes: list_node.visible = false
print("Theme: 没有启用的题材选项可供显示。")
if title_label: title_label.text = "题材选择 (无可用)"
if previous_button: previous_button.disabled = true
if next_button: next_button.disabled = true
return
else:
if previous_button: previous_button.disabled = (total_pages <= 1)
if next_button: next_button.disabled = (total_pages <= 1)
# --- 更新页面索引 (循环) (与 gameplay.gd 一致) ---
if total_pages > 0 :
current_page_index = current_page_index % total_pages
if current_page_index < 0:
current_page_index += total_pages
else:
current_page_index = 0
# --- 更新标题 (与 gameplay.gd 一致) ---
if title_label:
var display_page_number = current_page_index + 1
title_label.text = "题材选择 %d/%d" % [display_page_number, total_pages]
# --- 更新 List 可见性 (与 gameplay.gd 一致) ---
for i in range(theme_list_nodes.size()):
theme_list_nodes[i].visible = (i == current_page_index)
# --- 填充可见列表并查找要高亮的按钮 (与 gameplay.gd 类似) ---
var button_to_highlight: Button = null
var first_enabled_button_on_page: Button = null
if current_page_index < option_nodes_per_list.size():
var current_options = option_nodes_per_list[current_page_index]
var start_index = current_page_index * items_per_page
for i in range(current_options.size()):
var option_button = current_options[i]
var item_index = start_index + i
# --- 断开旧信号 ---
if option_button.pressed.is_connected(_on_option_selected):
option_button.pressed.disconnect(_on_option_selected)
if item_index < all_enabled_themes.size():
var theme_entry = all_enabled_themes[item_index]
var theme_name = theme_entry.name
var theme_data = theme_entry.data # 包含 Cost, Experience, Popularity 等
var row = option_button.get_node_or_null("Row")
if row:
# 填充标签文本
var title_label_in_row = row.get_node_or_null("Title")
var exp_label = row.get_node_or_null("Experience")
var pop_label = row.get_node_or_null("Popularity")
var cost_label = row.get_node_or_null("Cost") if row.has_node("Cost") else row.get_node_or_null("cost")
if title_label_in_row: title_label_in_row.text = theme_name
# 【修改】设置经验值,显示为整数
if exp_label:
var experience_value = theme_data.get("Experience", 0.0)
exp_label.text = str(int(experience_value))
# 【保持】设置流行度,保持原始字符串显示
if pop_label:
var popularity_value = theme_data.get("Popularity", "未知")
pop_label.text = str(popularity_value)
# 【修改】设置成本,显示为整数
if cost_label:
var cost_value = theme_data.get("Cost", 0.0)
cost_label.text = str(int(cost_value))
option_button.visible = true
option_button.disabled = false
option_button.set_meta("theme_name", theme_name) # 存储题材名称
# --- 连接新信号 (使用 bind) ---
option_button.pressed.connect(_on_option_selected.bind(theme_name, option_button))
if first_enabled_button_on_page == null:
first_enabled_button_on_page = option_button
if not initial_key_to_highlight.is_empty() and theme_name == initial_key_to_highlight:
button_to_highlight = option_button
# print("Theme: 找到需要初始高亮的按钮: ", button_to_highlight.name) # Debug
else: # 未找到 Row 节点
push_warning("警告:在按钮 %s 中未找到 Row 节点" % option_button.get_path())
option_button.visible = false
option_button.disabled = true
else: # 此槽位没有对应的启用题材数据
option_button.visible = false
option_button.disabled = true
else:
printerr("错误:当前页面索引 %d 超出 option_nodes_per_list 的范围 (大小 %d)" % [current_page_index, option_nodes_per_list.size()])
# --- 设置初始高亮和焦点 (与 gameplay.gd 一致) ---
if button_to_highlight:
_set_initial_highlight(button_to_highlight)
elif first_enabled_button_on_page:
print("Theme: 初始 Key '%s' 不在本页或为空。高亮本页第一个按钮: %s" % [initial_key_to_highlight, first_enabled_button_on_page.name])
_set_initial_highlight(first_enabled_button_on_page)
else:
print("Theme: 本页没有可供高亮的按钮。")
currently_selected_button = null
selected_theme_key = ""
# (Helper) 设置初始高亮和焦点 (与 gameplay.gd 一致)
func _set_initial_highlight(button_to_highlight: Button):
if not is_instance_valid(button_to_highlight):
printerr("错误:传递给 _set_initial_highlight 的按钮无效。")
currently_selected_button = null
selected_theme_key = ""
return
# print("Theme: 设置高亮按钮: ", button_to_highlight.name) # Debug
currently_selected_button = button_to_highlight
# --- 修改元数据键名 ---
selected_theme_key = button_to_highlight.get_meta("theme_name", "") # 同时设置 Key
# print("Theme: currently_selected_button 设置为: ", currently_selected_button.name) # Debug
# print("Theme: selected_theme_key 设置为: '", selected_theme_key, "'") # Debug
if currently_selected_button and currently_selected_button.is_inside_tree():
# print("Theme: 尝试为按钮 call_deferred('grab_focus'): ", currently_selected_button.name) # Debug
currently_selected_button.call_deferred("grab_focus")
else:
print("Theme: 按钮无效或不在场景树中,无法获取焦点。")
# --- Signal Handlers ---
# (与 gameplay.gd 一致)
func _on_previous_pressed():
if total_pages > 1:
current_page_index -= 1
_update_display()
# (与 gameplay.gd 一致)
func _on_next_pressed():
if total_pages > 1:
current_page_index += 1
_update_display()
# 【修改】处理选项按钮按下事件 (与 gameplay.gd 一致)
func _on_option_selected(theme_name: String, button_node: Button):
"""当选项按钮被按下时调用。"""
if not is_instance_valid(button_node):
# --- 修改错误信息中的 Key 类型 ---
printerr("错误:在 _on_option_selected 中收到无效按钮节点Key: " + theme_name)
return
if button_node == currently_selected_button:
# --- 双击 (或点击已选中的按钮) ---
print("Theme: 检测到双击或确认点击: ", theme_name)
_confirm_and_close(theme_name) # 确认此选择
else:
# --- 首次点击或点击不同按钮 ---
print("Theme: 选中 (高亮) 题材: ", theme_name)
currently_selected_button = button_node
selected_theme_key = theme_name # 更新 Key
# 更新视觉焦点
button_node.grab_focus()
# 【修改】确认选择并关闭弹窗的逻辑 (与 gameplay.gd 一致)
func _confirm_and_close(key_to_confirm: String):
"""更新父节点并关闭此弹窗。"""
if key_to_confirm.is_empty():
printerr("错误:尝试确认一个空的题材 Key。")
return
print("Theme: 确认选择: ", key_to_confirm)
# --- 更新父节点 (Task Development Popup) ---
var parent_node = get_parent()
if parent_node and parent_node.has_method("update_task_options"):
# --- 修改传递给父节点的字典键名 ---
parent_node.update_task_options({"题材": key_to_confirm})
print("Theme: 已更新父节点的 task_options: 题材 = ", key_to_confirm)
else:
printerr("错误Theme: 父节点未找到或缺少 'update_task_options' 方法。")
# --- 关闭此弹窗 (与 gameplay.gd 一致) ---
if parent_node and parent_node.has_method("_close_child_popup_and_return"):
parent_node._close_child_popup_and_return(self)
else:
printerr("错误Theme: 父节点未找到或缺少 '_close_child_popup_and_return' 方法。")
self.hide() # 备选方案

View File

@ -0,0 +1 @@
uid://do3rv2hocchy3

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
[gd_resource type="Theme" load_steps=5 format=3 uid="uid://r5e0kacnb3xf"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_kemom"]
bg_color = Color(0.513883, 0.609153, 0.731076, 1)
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_ao15e"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_kemom"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_mp8x4"]
bg_color = Color(0.513883, 0.609153, 0.731076, 1)
[resource]
Button/styles/focus = SubResource("StyleBoxFlat_kemom")
Button/styles/hover = SubResource("StyleBoxEmpty_ao15e")
Button/styles/normal = SubResource("StyleBoxEmpty_kemom")
Button/styles/pressed = SubResource("StyleBoxFlat_mp8x4")