xiandie/manager/event_manager/event_manager.gd

287 lines
10 KiB
GDScript3
Raw Normal View History

@tool
2024-12-23 01:29:31 +00:00
extends Node
## 当任何事件的状态stage发生变化时发出此信号。
## 其他系统如UI、角色状态机等可以监听此信号来做出反应。
2025-06-27 14:52:46 +00:00
signal stage_updated(event_name: StringName, stage: int)
# 存储公式的字典。键是事件名称StringName值是当该事件更新时需要被评估的公式Callable数组。
# {event_name: [formula_callable_1, formula_callable_2, ...]}
var formula_dict: Dictionary[StringName, Array] = {}
2025-06-27 14:52:46 +00:00
# 节点初始化时,异步加载并解析事件规则。
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_s03_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_s03_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")
2025-06-27 14:52:46 +00:00
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, "'.")
2025-06-27 14:52:46 +00:00
return 0
return ArchiveManager.archive.event_stage.get(event_name, 0)
2025-06-27 14:52:46 +00:00
# 核心的 stage 设置函数
2025-06-27 14:52:46 +00:00
# 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, "'.")
2025-06-27 14:52:46 +00:00
return
ArchiveManager.archive.event_stage[event_name] = stage
print("[EventManager] Stage updated: %s -> %s" % [event_name, stage])
# 1. 发出通用信号
2025-06-27 14:52:46 +00:00
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):
2025-06-27 14:52:46 +00:00
set_stage(event_name, stage)
2025-07-02 18:29:39 +00:00
return true
return false
2025-06-27 14:52:46 +00:00
# stage 最大 99999
func next_stage(event_name: StringName, stage_max := 99999) -> int:
2025-06-27 14:52:46 +00:00
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
2025-06-27 14:52:46 +00:00
# --- 游戏特定逻辑 ---
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")
2025-06-27 14:52:46 +00:00
2025-06-27 14:52:46 +00:00
##### 其他事件 #####
2025-01-13 08:09:57 +00:00
2025-06-17 05:01:19 +00:00
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 := {}
var _event_stage_map_update_time := 0
var _event_stage_map_mutex := Mutex.new()
# 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秒更新
_event_stage_map_mutex.lock()
if time_msec - _event_stage_map_update_time < 3000:
_event_stage_map_mutex.unlock()
if current_dict:
return current_dict["stages"]
else:
return PackedStringArray()
_event_stage_map_update_time = time_msec
# 文件: 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,
}
_event_stage_map_mutex.unlock()
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