event_manager formula_dict 解析与运行功能
This commit is contained in:
parent
9ca51cde97
commit
28b067c4f6
@ -181,6 +181,7 @@ current_scene 是通过 GroundLoader 加载的,在 ground loader 加载 ground
|
||||
- 影响 SignSnapper 的等待时长(如 Boss 战时加快节奏)
|
||||
- 转场 process 机制优化:暂停 & AnimationPlayer 保持运行
|
||||
- EventManager 控制事件,使用 Event2D 控制绑定关系
|
||||
- formula_dict: 前置事件推动后续。示例(条件数>=1): `xx=2 & yy=1 & xy=3 -> zz=1`
|
||||
- 比 Event2D 更轻量灵活的 EventBinder,内有 updater 与 trigger 两种绑定
|
||||
- updater 由 event 驱动更新父节点状态
|
||||
- trigger 由父节点 signal 驱动更新 event
|
||||
|
15
asset/dialogue/event_stage.csv
Normal file
15
asset/dialogue/event_stage.csv
Normal file
@ -0,0 +1,15 @@
|
||||
keys,zh_CN,_character,_notes,_tags
|
||||
c03_invite_xchan_supper=2 & c03_laizi_braid=2 ~ c03_f2_madman_runaway=2,c03_invite_xchan_supper=2 & c03_laizi_braid=2 ~ c03_f2_madman_runaway=2,,,
|
||||
c03_invite_xchan_supper=2 & c03_laizi_braid=2 -> c03_f2_madman_runaway=2,c03_invite_xchan_supper=2 & c03_laizi_braid=2 -> c03_f2_madman_runaway=2,,,
|
||||
0:demo 1:release,0:demo 1:release,release_stage,,
|
||||
1:序章 2:第一章 3:第二章 4:第三章 5:第四章 6:尾声,1:序章 2:第一章 3:第二章 4:第三章 5:第四章 6:尾声,current_chapter_stage,,
|
||||
0:初始化_关闭 1:打开 2:放入小蝉人偶 3:全部放置正确_可摇手柄 4:已播放完成,0:初始化_关闭 1:打开 2:放入小蝉人偶 3:全部放置正确_可摇手柄 4:已播放完成,c02_musicbox_stage,,
|
||||
0:初始化 1:已交互疯子 2:小鞋已掉落,0:初始化 1:已交互疯子 2:小鞋已掉落,c02_madman_interacted_stage,,
|
||||
0:初始化 1:寻找弹珠_老虎钳可以换弹珠 2:给出弹珠 3:游戏结束_小猫纸片 4:游戏结束_小猫离开,0:初始化 1:寻找弹珠_老虎钳可以换弹珠 2:给出弹珠 3:游戏结束_小猫纸片 4:游戏结束_小猫离开,c02_ball_game_stage,,
|
||||
0:初始化 1:已放肉,0:初始化 1:已放肉,c03_s01_meat_put,,
|
||||
0:初始化 1:已偷听_需邀请 2:完成邀请,0:初始化 1:已偷听_需邀请 2:完成邀请,c03_invite_xchan_supper,,
|
||||
0:初始化 1:已使用剪刀 2:已剪下,0:初始化 1:已使用剪刀 2:已剪下,c03_laizi_braid,,
|
||||
0:初始化 1:跑开_纸人挡路 2:消除纸人,0:初始化 1:跑开_纸人挡路 2:消除纸人,c03_f2_madman_runaway,,
|
||||
c03_invite_xchan_supper=2 & c03_laizi_braid=2 & c03_mahjong_game=1 -> c03_f2_madman_runaway=2,c03_invite_xchan_supper=2 & c03_laizi_braid=2 & c03_mahjong_game=1 -> c03_f2_madman_runaway=2,,,
|
||||
0:初始化 1:粘舌头和刀把 2:给药 4:准备好进入游戏,0:初始化 1:粘舌头和刀把 2:给药 4:准备好进入游戏,c03_before_mahjong_game,,
|
||||
0:麻将理牌 1:麻将出千 2:麻将结束 3:演出结束,0:麻将理牌 1:麻将出千 2:麻将结束 3:演出结束,c03_mahjong_game,,
|
|
17
asset/dialogue/event_stage.csv.import
Normal file
17
asset/dialogue/event_stage.csv.import
Normal file
@ -0,0 +1,17 @@
|
||||
[remap]
|
||||
|
||||
importer="csv_translation"
|
||||
type="Translation"
|
||||
uid="uid://biw8l6b4d3v3j"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://asset/dialogue/event_stage.zh_CN.translation"]
|
||||
|
||||
source_file="res://asset/dialogue/event_stage.csv"
|
||||
dest_files=["res://asset/dialogue/event_stage.zh_CN.translation"]
|
||||
|
||||
[params]
|
||||
|
||||
compress=true
|
||||
delimiter=0
|
@ -13,8 +13,10 @@ c02_ball_game_stage: 0:初始化 1:寻找弹珠_老虎钳可以换弹珠 2:给
|
||||
c03_s01_meat_put: 0:初始化 1:已放肉
|
||||
c03_invite_xchan_supper: 0:初始化 1:已偷听_需邀请 2:完成邀请
|
||||
c03_laizi_braid: 0:初始化 1:已使用剪刀 2:已剪下
|
||||
c03_f2_madman_runaway: 0:初始化 1:跑开_纸人挡路 2:消除纸人
|
||||
c03_invite_xchan_supper=2 & c03_laizi_braid=2 -> c03_f2_madman_runaway=2
|
||||
c03_before_mahjong_game: 0:初始化 1:粘舌头和刀把 2:给药 4:准备好进入游戏
|
||||
c03_mahjong_game: 0::麻将理牌 1:麻将出千 2:麻将结束 3:演出结束
|
||||
c03_mahjong_game: 0:麻将理牌 1:麻将出千 2:麻将结束 3:演出结束
|
||||
=> END
|
||||
|
||||
~ EventStage_c04
|
||||
|
@ -5,12 +5,142 @@ extends Node
|
||||
## 其他系统(如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:
|
||||
@ -32,7 +162,12 @@ func set_stage(event_name: StringName, stage := 1) -> void:
|
||||
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 时更新
|
||||
@ -96,12 +231,13 @@ func get_event_stage_map_array(event_name: StringName) -> PackedStringArray:
|
||||
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("#"):
|
||||
if line_text.begins_with("#") or line_text.find("->") >= 0 or not e_name:
|
||||
continue
|
||||
#xxxx: 0:未开始 1:已偷听,需邀请 2:已完成邀请
|
||||
var parts = line_text.split(" ")
|
||||
|
Loading…
Reference in New Issue
Block a user