@tool extends Node ## 当任何事件的状态(stage)发生变化时发出此信号。 ## 其他系统(如UI、角色状态机等)可以监听此信号来做出反应。 signal stage_updated(event_name: StringName, stage: int) # 存储公式的字典。键是事件名称(StringName),值是当该事件更新时需要被评估的公式(Callable)数组。 # {event_name: [formula_callable_1, formula_callable_2, ...]} var formula_dict: Dictionary[StringName, Array] = {} # 节点初始化时,异步加载并解析事件规则。 func _ready() -> void: # 异步加载,避免阻塞主线程。 # 使用 await a, b 语法等待协程完成。 await _load_and_parse_formulas() stage_updated.connect(_on_stage_updated_for_handnote) # 将加载和解析逻辑封装到一个独立的异步函数中,使 _ready 更清晰。 func _load_and_parse_formulas() -> void: var event_stage_resource = load("uid://dohpsb4jttuv1") as DialogueResource if not event_stage_resource: printerr("[EventManager] Failed to load event stage resource (uid://dohpsb4jttuv1).") return print("[EventManager] Refreshing event formulas...") for title in event_stage_resource.get_titles(): # 使用 await 等待异步函数 get_lines 完成 var lines = await Util.get_lines(event_stage_resource, title) for line in lines: var line_text: String = line.text.strip_edges() # 跳过注释行和不包含"->"的行 if line_text.begins_with("#") or not "->" in line_text: continue _build_and_add_formula(line_text) print("[EventManager] Formula refreshing completed.") # 解析单行规则,构建公式并添加到字典中。 # 示例: "c03_invite_xchan_supper=2 & c03_laizi_braid=2 -> c03_f2_madman_runaway=2" func _build_and_add_formula(line_text: String) -> void: var parts: PackedStringArray = line_text.split("->") if parts.size() != 2: printerr("[EventManager] Invalid formula line (must contain one '->'): %s" % line_text) return var condition_events: Array[StringName] = [] var condition_callable := _build_condition(parts[0], condition_events) var execution_callable := _build_execution(parts[1]) # 关键的健壮性检查:确保条件和执行都成功构建 if not condition_callable.is_valid() or not execution_callable.is_valid(): printerr("[EventManager] Failed to build formula due to invalid parts from line: %s" % line_text) return var formula := func(): if condition_callable.call(): if Engine.is_editor_hint() or GlobalConfig.DEBUG: print("[EventManager] Formula condition met, executing: ", line_text) execution_callable.call() if Engine.is_editor_hint() or GlobalConfig.DEBUG: print("[EventManager] Built formula from: ", line_text) for event_name in condition_events: if not formula_dict.has(event_name): formula_dict[event_name] = [] formula_dict[event_name].append(formula) if Engine.is_editor_hint() or GlobalConfig.DEBUG: print("[EventManager] Added formula for event: ", event_name) # 构建条件 Callable。重构为迭代,而不是递归,以提高清晰度和健壮性。 # 示例: "c03_invite_xchan_supper=2 & c03_laizi_braid=2" func _build_condition(text: String, r_condition_events: Array[StringName]) -> Callable: var condition_text := text.strip_edges() if condition_text.is_empty(): return func(): return true # 空条件始终为真 var sub_conditions: Array[Callable] = [] # 按 "&" 分割所有子条件 for part in condition_text.split("&"): var single_condition_text := part.strip_edges() if single_condition_text.is_empty(): continue # 忽略由 "&&" 或末尾 "&" 产生的空部分 var args := single_condition_text.split("=", false, 1) if args.size() != 2 or args[0].strip_edges().is_empty(): printerr("[EventManager] Invalid condition part (format: 'event=value'): %s" % single_condition_text) continue # 跳过此错误部分,继续解析下一个 var event_name_str := args[0].strip_edges() var value_str := args[1].strip_edges() if not value_str.is_valid_int(): printerr("[EventManager] Invalid integer value in condition: %s" % single_condition_text) continue # 跳过 var event_name := StringName(event_name_str) var target_value := value_str.to_int() if not r_condition_events.has(event_name): r_condition_events.append(event_name) sub_conditions.append(func(): return get_stage(event_name) == target_value) if sub_conditions.is_empty(): printerr("[EventManager] No valid conditions were parsed from text: ", text) return Callable() # 返回无效 Callable,表示构建失败 # 返回一个复合 Callable,它会检查所有子条件是否都为真 return func(): for sub_condition in sub_conditions: if not sub_condition.call(): return false # 短路:任何一个为假,则整个条件为假 return true # 所有子条件都为真 # 构建执行 Callable。增加了健壮性检查。 # 示例: "c03_f2_madman_runaway=2" func _build_execution(text: String) -> Callable: var execution_text := text.strip_edges() var parts := execution_text.split("=", false, 1) if parts.size() != 2 or parts[0].strip_edges().is_empty(): printerr("[EventManager] Invalid execution format (format: 'event=value'): %s" % execution_text) return Callable() # 返回无效 Callable var event_name_str := parts[0].strip_edges() var value_str := parts[1].strip_edges() if not value_str.is_valid_int(): printerr("[EventManager] Invalid integer value in execution: %s" % execution_text) return Callable() # 返回无效 Callable var event_name := StringName(event_name_str) var target_value := value_str.to_int() # 使用 .bind() 是创建带参数 Callable 的首选方式,比手动包装一个 lambda 更清晰高效。 return set_stage_if_greater.bind(event_name, target_value) # --- 公共 API --- func get_chapter_stage() -> int: return get_stage(&"current_chapter_stage") func get_stage(event_name: StringName) -> int: if not ArchiveManager.archive or ArchiveManager.archive.event_stage == null: printerr("[EventManager] Archive or event_stage is null. Cannot get stage for '", event_name, "'.") return 0 return ArchiveManager.archive.event_stage.get(event_name, 0) # 核心的 stage 设置函数 # default 为 0,首次更新为 1 func set_stage(event_name: StringName, stage := 1) -> void: if not ArchiveManager.archive or ArchiveManager.archive.event_stage == null: printerr("[EventManager] Archive or event_stage is null. Cannot set stage for '", event_name, "'.") return ArchiveManager.archive.event_stage[event_name] = stage print("[EventManager] Stage updated: %s -> %s" % [event_name, stage]) # 1. 发出通用信号 stage_updated.emit(event_name, stage) # 2. 触发关联的公式 if formula_dict.has(event_name): for formula in formula_dict.get(event_name, []): formula.call() # 仅当设置的 stage > 当前 stage 时更新 func set_stage_if_greater(event_name: StringName, stage: int) -> bool: if stage > get_stage(event_name): set_stage(event_name, stage) return true return false # stage 最大 99999 func next_stage(event_name: StringName, stage_max := 99999) -> int: var current_stage = get_stage(event_name) if current_stage < stage_max: var new_stage = current_stage + 1 set_stage(event_name, new_stage) return new_stage return stage_max # --- 游戏特定逻辑 --- func _on_stage_updated_for_handnote(event_name: StringName, stage: int) -> void: # 检查条件是否满足 if not ( SceneManager.is_node_ready() and SceneManager.get_player() and SceneManager.get_player().character.begins_with("吕萍") ): return if event_name.begins_with("handnote_"): # 笔记条目更新 # 0 初始化隐藏,1 开始显示,2 划掉,3 结束隐藏 if stage == 1: SceneManager.lock_player(3.0, 16, true) SceneManager.pop_notification("ui_notify_note_update") ##### 其他事件 ##### func prop_interacted(e_name, prop_key, interacted_times) -> void: print("Event: %s interacted with %s. total times: %s" % [e_name, prop_key, interacted_times]) ##### TOOL 方法 # event_name -> {stages:PackedStringArray, update_time:int} var _debug_event_stage_dict := {} # return: stages func get_event_stage_map_array(event_name: StringName) -> PackedStringArray: if not Engine.is_editor_hint(): return PackedStringArray() var current_dict = _debug_event_stage_dict.get(event_name) var time_msec = Time.get_ticks_msec() # 3秒更新 if current_dict and time_msec - current_dict["update_time"] < 3000: return current_dict["stages"] # 文件: res://asset/dialogue/event_stage.dialogue var event_stage_resource = load("uid://dohpsb4jttuv1") as DialogueResource print("[EventManager] get_event_stage_map_array refreshing...") for title in event_stage_resource.get_titles(): var lines = await Util.get_lines(event_stage_resource, title) for line in lines: var line_text = line.text.strip_edges() var e_name = line.character if line_text.begins_with("#") or line_text.find("->") >= 0 or not e_name: continue #xxxx: 0:未开始 1:已偷听,需邀请 2:已完成邀请 var parts = line_text.split(" ") var stages = PackedStringArray() for id in range(0, len(parts)): var tuple = parts[id].split(":") if len(tuple) == 2: var event_stage = int(tuple[0]) if stages.size() < event_stage + 1: stages.resize(event_stage + 1) stages[event_stage] = tuple[1] for id in len(stages): if stages[id] == "": stages[id] = str(id) _debug_event_stage_dict[e_name] = { "stages": stages, "update_time": time_msec, } if _debug_event_stage_dict.has(event_name): prints("reload", event_name, "stages:", _debug_event_stage_dict[event_name]["stages"]) return _debug_event_stage_dict[event_name]["stages"] return PackedStringArray() # map stage_id->name, 必定保持相同长度的数组 func map_event_stages( event_name: StringName, stages: Array, including_id := true ) -> PackedStringArray: var result = PackedStringArray() result.resize(stages.size()) var dict = await get_event_stage_map_array(event_name) for id in range(stages.size()): var stage = stages[id] if dict.size() > stage: if including_id: result[id] = str(stage) + ":" + dict[stage] else: result[id] = str(stage) return result