extends UIPage # --- UI 节点引用 --- @onready var title_label: Label = $BG/Title/Title @onready var npc_name_label: Label = $BG/Option/NPC_Info_1/Name @onready var npc_staff_type_label: Label = $BG/Option/NPC_Info_1/Staff # 通常显示 "员工" @onready var npc_job_label: Label = $BG/Option/NPC_Info_2/Info_List_0/Job/Title @onready var npc_level_label: Label = $BG/Option/NPC_Info_2/Info_List_0/Job/Level @onready var npc_icon_rect: TextureRect = $BG/Option/NPC_Info_2/Info_List_0/Icon @onready var stat_design_label: Label = $BG/Option/NPC_Info_2/Info_List_1/Info_1/info @onready var stat_code_label: Label = $BG/Option/NPC_Info_2/Info_List_1/Info_2/info @onready var stat_art_label: Label = $BG/Option/NPC_Info_2/Info_List_1/Info_3/info @onready var stat_audio_label: Label = $BG/Option/NPC_Info_2/Info_List_1/Info_4/info @onready var cost_label: Label = $BG/Option/NPC_Info_2/Info_List_3/info_4/info @onready var power_bar_segments_container: HBoxContainer = $BG/Option/NPC_Info_2/Info_List_2 var _power_bar_segments: Array[ColorRect] = [] # 用于存储体力条的各个片段 @onready var prev_button: Button = $BG/Title/previous/Button @onready var next_button: Button = $BG/Title/next/Button @onready var confirm_button: Button = $BG/Confirm @onready var notice_path: Control = $notice @export var Suitable_position:Array = [] # --- 内部状态 --- var _all_npcs_data: Dictionary = {} # 存储从 GameState 加载的所有NPC原始数据 var enabled_npc_keys: Array[String] = [] # 存储所有当前可选的NPC的键 (名字) var current_npc_index: int = -1 # 当前在 enabled_npc_keys 数组中选中的NPC的索引 var temp_next_ui_node_path = '' # --- 常量 --- const NPC_ICON_BASE_PATH = "res://Entity/NPC/art/" # NPC 图标的基础路径 (如果后续有 Icon 字段) const DEFAULT_NPC_ICON_PATH = "res://Entity/NPC/art/秘书.png" # 默认/占位NPC图标 const POWER_CURRENT_COLOR = Color.GREEN_YELLOW # 当前体力颜色 const POWER_MAX_POTENTIAL_COLOR = Color(0.2, 0.4, 0.2, 0.8) # 最大潜力槽颜色 (稍暗的绿色) const POWER_DISABLED_COLOR = Color(0.1, 0.1, 0.1, 0.3) # 体力条禁用/未达到部分的颜色 func _ready() -> void: super._ready() var node_name = str(self.name) add_to_group(node_name) # 方便父节点或其他系统查找 temp_next_ui_node_path = next_ui_node_path # 收集体力条的片段 for child in power_bar_segments_container.get_children(): if child is ColorRect: _power_bar_segments.append(child) if _power_bar_segments.size() != 20: printerr(node_name + ": Expected 20 power bar segments, found " + str(_power_bar_segments.size())) # 连接信号 if prev_button and not prev_button.pressed.is_connected(_on_previous_pressed): prev_button.pressed.connect(_on_previous_pressed) if next_button and not next_button.pressed.is_connected(_on_next_pressed): next_button.pressed.connect(_on_next_pressed) if confirm_button and not confirm_button.pressed.is_connected(_on_confirm_pressed): confirm_button.pressed.connect(_on_confirm_pressed) if not is_connected("visibility_changed",Callable(self,"_on_visibility_changed")): visibility_changed.connect(Callable(self,"_on_visibility_changed")) # 如果节点在 _ready 时就可见,则立即刷新显示 if is_visible_in_tree(): reset_display() func _on_visibility_changed() -> void: if is_visible_in_tree(): reset_display() # 重置显示状态,在窗口可见时调用 func reset_display() -> void: print(str(self.name) + ": Resetting display state.") _load_and_filter_npcs() if enabled_npc_keys.is_empty(): current_npc_index = -1 print(str(self.name) + ": No enabled NPCs found.") else: current_npc_index = 0 # 默认显示第一个可用的NPC print(str(self.name) + ": Found %d enabled NPCs. Defaulting to index 0." % enabled_npc_keys.size()) _display_current_npc() # 从 GameState 加载NPC数据并筛选出可用的负责人 func _load_and_filter_npcs() -> void: _all_npcs_data.clear() enabled_npc_keys.clear() if not GameState: printerr(str(self.name) + ": GameState not available in _load_and_filter_npcs.") return var raw_npcs_data = GameState.get_value("npcs", {}) if raw_npcs_data.is_empty(): print(str(self.name) + ": 'npcs' data in GameState is empty or not found.") return _all_npcs_data = raw_npcs_data.duplicate(true) # Suitable_position 是一个 @export var Array,你需要确保它在编辑器中或通过代码被正确赋值。 # 例如: Suitable_position = ["项目经理", "技术总监", "艺术总监"] for npc_key in _all_npcs_data: var npc_info = _all_npcs_data[npc_key] if typeof(npc_info) == TYPE_DICTIONARY: var npc_type: String = npc_info.get("type", "") # 获取NPC类型,默认为空字符串 var npc_title: String = npc_info.get("title", "") # 获取NPC职位,默认为空字符串 var npc_outsourcing: float = float(npc_info.get("outsourcing", 0.0)) # 获取外包费用,默认为0.0 # 检查条件1: type是员工,同时title在Suitable_position中有 var condition1_met = false if npc_type == "员工" and Suitable_position.has(npc_title): condition1_met = true # 检查条件2: outsourcing>0,同时title在Suitable_position中有 var condition2_met = false if npc_outsourcing > 0.0 and Suitable_position.has(npc_title): # 浮点数比较建议与 0.0 比较 condition2_met = true # 如果任一条件满足,则将该NPC加入到可用列表中 if condition1_met or condition2_met: enabled_npc_keys.append(npc_key) # 可选:对 enabled_npc_keys进行排序,以确保显示顺序一致 # enabled_npc_keys.sort() print(str(self.name) + ": Loaded and filtered NPCs. Enabled count: " + str(enabled_npc_keys.size())) # 根据 current_npc_index 更新UI显示 func _display_current_npc() -> void: if current_npc_index < 0 or current_npc_index >= enabled_npc_keys.size(): # --- 处理没有可用NPC或索引无效的情况 --- title_label.text = "策划案负责人" npc_name_label.text = "无可用负责人" npc_staff_type_label.text = "-" # 通常显示 "员工" npc_job_label.text = "-" npc_level_label.text = "-" npc_icon_rect.texture = null # 或者一个“无”的占位图 stat_design_label.text = "-" stat_code_label.text = "-" stat_art_label.text = "-" stat_audio_label.text = "-" cost_label.text = "-" # 隐藏所有体力条片段 for segment in _power_bar_segments: segment.visible = false if prev_button: prev_button.disabled = true if next_button: next_button.disabled = true if confirm_button: confirm_button.disabled = true return # --- 如果有有效NPC --- if prev_button: prev_button.disabled = enabled_npc_keys.size() <= 1 if next_button: next_button.disabled = enabled_npc_keys.size() <= 1 if confirm_button: confirm_button.disabled = false var display_index = current_npc_index + 1 var total_count = enabled_npc_keys.size() title_label.text = "策划案负责人 %d/%d" % [display_index, total_count] var npc_key = enabled_npc_keys[current_npc_index] if not _all_npcs_data.has(npc_key): printerr(str(self.name) + ": Current NPC key '%s' not found in cached _all_npcs_data!" % npc_key) # 显示错误状态,可以做得更友好 npc_name_label.text = "错误:数据丢失" # 隐藏所有体力条片段 for segment in _power_bar_segments: segment.visible = false return var npc_info = _all_npcs_data[npc_key] # 更新基本信息 npc_name_label.text = npc_key npc_staff_type_label.text = npc_info.get("type", "员工") # 根据筛选,这里应该是"员工" npc_job_label.text = npc_info.get("title", "-") npc_level_label.text = "Lv" + str(int(npc_info.get("level", 1))) # 更新图标 (后续会从npc_info.Icon获取) var icon_name_from_data = npc_info.get("icon", "") # 假设后续会有这个字段 if not icon_name_from_data.is_empty(): var dynamic_icon_path = NPC_ICON_BASE_PATH + icon_name_from_data + ".png" # 假设文件名规则 if ResourceLoader.exists(dynamic_icon_path): npc_icon_rect.texture = ResourceLoader.load(dynamic_icon_path) else: printerr(str(self.name) + ": Failed to load NPC icon: " + dynamic_icon_path + ". Falling back to default.") npc_icon_rect.texture = ResourceLoader.load(DEFAULT_NPC_ICON_PATH) else: # print(str(self.name) + ": NPC icon name is empty for key '%s'. Using default." % npc_key) npc_icon_rect.texture = ResourceLoader.load(DEFAULT_NPC_ICON_PATH) # 更新能力值 (确保转换为整数显示) stat_design_label.text = str(int(npc_info.get("design", 0.0))) stat_code_label.text = str(int(npc_info.get("code", 0.0))) stat_art_label.text = str(int(npc_info.get("art", 0.0))) stat_audio_label.text = str(int(npc_info.get("audio", 0.0))) # 更新费用 cost_label.text = str(int(npc_info.get("outsourcing", 0.0))) # --- 更新体力条 (包含 visible 控制) --- var power = float(npc_info.get("power", 0.0)) var power_max = float(npc_info.get("power_max", 0.0)) if power_max <= 0: # 防止除零错误或无效数据 for segment in _power_bar_segments: segment.visible = false # 如果最大体力为0或无效,所有格段都不可见 else: var total_visible_segments = floori(power_max / 10.0) # 根据 power_max 计算总共应显示的格数 var current_power_segments = floori(power / 10.0) # 当前体力对应的格数 # 确保 current_power_segments 不会超过 total_visible_segments (逻辑上 power 不应大于 power_max) current_power_segments = mini(current_power_segments, total_visible_segments) for i in range(_power_bar_segments.size()): # 遍历所有20个可能的格段 var segment = _power_bar_segments[i] if i < total_visible_segments: segment.visible = true # 此格段在NPC的最大体力范围内,设为可见 if i < current_power_segments: segment.color = POWER_CURRENT_COLOR # 当前体力部分 else: segment.color = POWER_MAX_POTENTIAL_COLOR # 最大潜力中未充满的部分 else: segment.visible = false # 此格段超出NPC的最大体力范围,设为不可见 func _on_previous_pressed() -> void: if enabled_npc_keys.size() > 1: current_npc_index -= 1 if current_npc_index < 0: current_npc_index = enabled_npc_keys.size() - 1 _display_current_npc() func _on_next_pressed() -> void: if enabled_npc_keys.size() > 1: current_npc_index += 1 if current_npc_index >= enabled_npc_keys.size(): current_npc_index = 0 _display_current_npc() func _on_confirm_pressed() -> void: if current_npc_index != -1 and current_npc_index < enabled_npc_keys.size(): var selected_npc_key = enabled_npc_keys[current_npc_index] # 从 cost_label 获取费用文本并转换为整数 var cost_string = cost_label.text var current_npc_cost: int # 进行转换,并处理可能的转换失败 (尽管在此逻辑下,cost_label.text 应该总是数字) if cost_string.is_valid_int(): current_npc_cost = cost_string.to_int() else: printerr(str(self.name) + ": Error converting cost_label.text ('" + cost_string + "') to integer.") # 可以选择在这里弹出一个错误提示给用户,或者阻止后续操作 # 例如,可以简单地返回,或者显示一个通知 go_to_child_page(notice_path) # 假设 notice_path 可以显示一个通用错误 return # 导航到下一个页面或结束工作流 next_ui_node_path = temp_next_ui_node_path # 将 GameState 中的 money 与当前NPC的 cost 进行比较 if GameState.get_value("money") < current_npc_cost: # 如果钱不够,跳转到提示页面 (例如,显示“资金不足”) # 您可能需要一个特定的提示页面或消息来指明是费用不足 # GameState.set_value("notice_msg","资金不足以支付该负责人费用:" + str(current_npc_cost)) # 示例:设置特定提示信息 go_to_child_page(notice_path) else: # 如果钱足够,则扣除费用 var new_money = GameState.get_value("money") - current_npc_cost GameState.set_value("money", new_money) # 设置选中的NPC为关键环节负责人 set_main_data("关键环节负责人", selected_npc_key) GameState.set_value("dialogue_npc", selected_npc_key) #设置任务状态开启 GameState.set_value("is_on_task",true) go_next() # 进入下一个UI流程