feat(卡② T2): EffectEngine 删运行时状态 + ctx 接口 + 静态写口
- 删成员 _counters/_reset_on_round_start/_adventure_counter_ids(引擎零运行时可变状态,仅 _triggers+db) - dispatch(ctx, event, payload, facts);round_start 特判改 ctx.round_bucket.clear() 整桶就地清空 - counter/dispatch 读路径 _when_ok/_cond_ok/_fact_value 全经 ctx - 新增 static bucket_of(ctx,id)(前缀→桶唯一出处)+ counter_id_legal(只禁 meta_) - 写口改 static counter_add/counter_spend(算术逐字承旧);删实例版与 counters_export/counters_seed - 树合成行 sense 两键改名 lvl_tree_sense_*,删 domain/reset_at 键;_push_trigger 增前缀 assert - rebuild 签名不变(冒烟调用面零迁移) 调用点施加点迁移见 T3,数据行/测试冒烟迁移见 T6/T7(本提交为无状态内核检查点,全绿在批末)。
This commit is contained in:
1 parent
ae5baed699
commit
a721e529ef
1 file changed
+63
-73
+63
-73
@@ -79,12 +79,8 @@ const DO_KEYS := ["kind", "value", "value_from", "delta", "delta_from", "mult",
|
||||
## 含连锁展开与格面资源,危险格仅真视显示不移除——棋盘事实不可变契约;免行动点即效果本身)
|
||||
const ACTIVE_KINDS := ["reveal_tiles", "preview", "grant_ap", "remove_mines", "refresh_pick", "safe_dig"]
|
||||
|
||||
# ---- 订阅集与计数器账本 ----
|
||||
# ---- 订阅集(编译产物=程序,非状态;计数器四桶经 ctx 外置宿主持有)----
|
||||
var _triggers: Array[Variant] = [] # [{source, id, on, when, do}](装载序=声明序,重建时定死)
|
||||
var _counters: Dictionary[Variant, Variant] = {} # counter_id → int(battle 域账本;重建清零;重置点由订阅行声明)
|
||||
var _reset_on_round_start: Array[String] = [] # round_start 重置点计数器名单(装载时收集)
|
||||
var _adventure_counter_ids: Array[String] = [] # domain=adventure 计数器名单(重建时收集、不清零;
|
||||
# battle 末 export → 冒险账本、begin_battle seed 回灌)
|
||||
var db: ConfigDb # 矩阵/表读取(advantageous 算子消费)——可空(纯计数器用法零依赖)
|
||||
|
||||
|
||||
@@ -93,12 +89,11 @@ func _init(db_ref: ConfigDb = null) -> void:
|
||||
|
||||
|
||||
## 订阅装载(每战 begin_battle 调用——重建时机 v1.1):feats 表序→treasure 表序(各按持有/装备
|
||||
## 集过滤)→树规则合成行。同一数据行的多个 trigger 按行内数组序。计数器账本同步清零并按订阅行
|
||||
## 声明收集重置点(round_start)。
|
||||
## 集过滤)→树规则合成行。同一数据行的多个 trigger 按行内数组序。计数器四桶由宿主持有、随宿主生灭,
|
||||
## 本方法只重建订阅集(运行时可变状态=零;battle/level 桶的清零由宿主 new 天然承载,round 桶清零
|
||||
## 在 dispatch 的 round_start 特判内整桶 clear,adventure 桶随 RunLedger 引用跨战持久——无副本搬运)。
|
||||
func rebuild(feat_ids: Array, item_ids: Array, tree_rules: Array = []) -> void:
|
||||
_triggers.clear()
|
||||
_reset_on_round_start.clear()
|
||||
_adventure_counter_ids.clear()
|
||||
if db != null and db.feats != null:
|
||||
for row in db.feats.rows:
|
||||
var fid := String(row.get("feat_id", ""))
|
||||
@@ -115,11 +110,9 @@ func rebuild(feat_ids: Array, item_ids: Array, tree_rules: Array = []) -> void:
|
||||
_push_trigger("attune", iid, tr, row)
|
||||
# 树规则合成订阅(六键按 v0.8 三组双态):posture 两键=「每关首轮窗」语义
|
||||
# 〔battle_index==1 且 round_index==1;battle_index=关卡域战斗序由 enc 携带〕;
|
||||
# sense 两组=每关限次〔adventure 域计数器 lt 上限闸 + 命中即 counter_add
|
||||
# 记账双行——同事件双行同预评估态见闸同记,序=装载序确定;生命周期四态:跨战持久=domain
|
||||
# adventure 导出/回灌、按关重置=AdventureRunner._start_current_level 清零、新冒险清零=账本
|
||||
# 归零、断点保留=断点快照随键〕;economy 两组零订阅行——消费点=GP 掉落乘算与商品计价折价的
|
||||
# tree_rules 成员判定(编排器/关卡 runner 读 enc 键集),不入事件面。数值全 provisional 归 M4。
|
||||
# sense 两组=每关限次〔level 桶计数器 lt 上限闸 + 命中即 counter_add 记账双行——同事件双行同
|
||||
# 预评估态见闸同记,序=装载序确定;寿命随 LevelRunner.level_counters 每关 new 天然按关清零〕;
|
||||
# economy 两组零订阅行——消费点=GP 掉落乘算与商品计价折价的 tree_rules 成员判定。数值全 provisional 归 M4。
|
||||
for key_v in tree_rules:
|
||||
var rk := String(key_v)
|
||||
match rk:
|
||||
@@ -137,30 +130,31 @@ func rebuild(feat_ids: Array, item_ids: Array, tree_rules: Array = []) -> void:
|
||||
# 锋=踩中危险格伤害减半·每关限次——mine_hit 挂点现成(damage_mult 收集→halve_pct,
|
||||
# 与惊巢反制同路径;伤害在结算战斗段末兑付,探索阶段零改动)
|
||||
_push_trigger("tree", rk, {"on": "mine_hit",
|
||||
"when": [{"fact": "counter", "id": "tree_sense_sharp_used", "op": "lt", "value": 2}],
|
||||
"when": [{"fact": "counter", "id": "lvl_tree_sense_sharp_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "damage_mult", "value": 50}}, {})
|
||||
_push_trigger("tree", rk, {"on": "mine_hit",
|
||||
"when": [{"fact": "counter", "id": "tree_sense_sharp_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "counter_add", "counter_id": "tree_sense_sharp_used",
|
||||
"value": 1, "domain": "adventure"}}, {})
|
||||
"when": [{"fact": "counter", "id": "lvl_tree_sense_sharp_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "counter_add", "counter_id": "lvl_tree_sense_sharp_used",
|
||||
"value": 1}}, {})
|
||||
"sense_guard":
|
||||
# 守=标错返还行动点·每关限次(契约 12 失误补偿——seal_missed 由 _judge_seal 去重闸后
|
||||
# 发射;queue=下一轮开轮兑现,末轮判出无下一轮=合法作废自动成立)
|
||||
_push_trigger("tree", rk, {"on": "seal_missed",
|
||||
"when": [{"fact": "counter", "id": "tree_sense_guard_used", "op": "lt", "value": 2}],
|
||||
"when": [{"fact": "counter", "id": "lvl_tree_sense_guard_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "grant_ap", "mode": "queue", "value": 1}}, {})
|
||||
_push_trigger("tree", rk, {"on": "seal_missed",
|
||||
"when": [{"fact": "counter", "id": "tree_sense_guard_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "counter_add", "counter_id": "tree_sense_guard_used",
|
||||
"value": 1, "domain": "adventure"}}, {})
|
||||
# 计数器账本重建(battle 域清零;adventure 域由 counters_seed 回灌〔rebuild 后调用〕)
|
||||
_counters = {}
|
||||
"when": [{"fact": "counter", "id": "lvl_tree_sense_guard_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "counter_add", "counter_id": "lvl_tree_sense_guard_used",
|
||||
"value": 1}}, {})
|
||||
|
||||
|
||||
func _push_trigger(source: String, id: String, tr: Dictionary, row: Dictionary) -> void:
|
||||
var on := String(tr.get("on", ""))
|
||||
assert(EVENTS.has(on), "[效果引擎] 订阅事件出域: %s(%s %s)" % [on, source, id])
|
||||
var do_d: Dictionary[Variant, Variant] = (tr.get("do", {}) as Dictionary).duplicate(true)
|
||||
# 树合成行 counter_id 前缀校验(与 validate_trigger_row 同源判据;写错前缀调试期致命)
|
||||
if do_d.has("counter_id") and not counter_id_legal(String(do_d.counter_id)):
|
||||
assert(false, "[效果引擎] 合成行 counter_id 前缀非法: %s(%s %s)" % [String(do_d.counter_id), source, id])
|
||||
# *_from 解析(rebuild 时把行字段引用定影为值——value_from:"magnitude" → value=行 magnitude)
|
||||
for pair in [["value_from", "value"], ["delta_from", "delta"], ["mult_from", "mult"], ["cap_from", "cap"]]:
|
||||
if do_d.has(pair[0]):
|
||||
@@ -178,16 +172,6 @@ func _push_trigger(source: String, id: String, tr: Dictionary, row: Dictionary)
|
||||
"when": when_d,
|
||||
"do": do_d}
|
||||
_triggers.append(t)
|
||||
# 计数器重置点收集(counter_add/counter_spend 行声明 reset_at)
|
||||
if do_d.has("counter_id") and String(do_d.get("reset_at", "")) == "round_start":
|
||||
var cid := String(do_d.counter_id)
|
||||
if not _reset_on_round_start.has(cid):
|
||||
_reset_on_round_start.append(cid)
|
||||
# 寿命域收集(domain=adventure 计数器跨战持久:重建不清零、battle 末导出回冒险账本)
|
||||
if do_d.has("counter_id") and String(do_d.get("domain", "")) == "adventure":
|
||||
var aid := String(do_d.counter_id)
|
||||
if not _adventure_counter_ids.has(aid):
|
||||
_adventure_counter_ids.append(aid)
|
||||
|
||||
|
||||
## 订阅查询面(发射点缺省行为仲裁用——如 round_end 无 transfer 订阅时走 RAP 转存)
|
||||
@@ -203,74 +187,80 @@ func has_effect_on(event: String, kind: String, mode: String = "") -> bool:
|
||||
|
||||
|
||||
## 事件分发(发射点唯一入口):返回命中效果列表(装载序=声明序;零命中=空数组零扰动)。
|
||||
## payload = 事件现场(fact 取值第一优先源);facts = 补充事实面(charge_layers/nest_count 等
|
||||
## 运行态读数 Callable——payload 未带时回落)。
|
||||
func dispatch(event: String, payload: Dictionary, facts: Callable = Callable()) -> Array:
|
||||
## ctx = 四桶引用束(引擎不自持状态,计数器读写全经 ctx);
|
||||
## payload = 事件现场(fact 取值第一优先源);facts = 补充事实面(运行态读数 Callable)。
|
||||
func dispatch(ctx: EffectContext, event: String, payload: Dictionary, facts: Callable = Callable()) -> Array:
|
||||
assert(EVENTS.has(event), "[效果引擎] 未知事件: %s(发射点出域)" % event)
|
||||
if event == "round_start":
|
||||
for cid in _reset_on_round_start:
|
||||
_counters[cid] = 0
|
||||
ctx.round_bucket.clear() # 整桶就地清空(禁 ={} 重赋值断引用;round_ 前缀键集≡旧名单,逐位等价)
|
||||
var hits: Array[Variant] = []
|
||||
for tr in _triggers:
|
||||
if String(tr.on) != event:
|
||||
continue
|
||||
if not _when_ok(tr.when, payload, facts):
|
||||
if not _when_ok(ctx, tr.when, payload, facts):
|
||||
continue
|
||||
hits.append(tr.do)
|
||||
return hits
|
||||
|
||||
|
||||
## 计数器读数(fact counter 与发射点配额面共用)
|
||||
func counter(counter_id: String) -> int:
|
||||
return int(_counters.get(counter_id, 0))
|
||||
## 计数器归属桶解析(前缀规则唯一出处——round_/lvl_/adv_ 显式桶,其余落 battle 缺省桶)。
|
||||
static func bucket_of(ctx: EffectContext, counter_id: String) -> Dictionary[Variant, Variant]:
|
||||
if counter_id.begins_with("round_"):
|
||||
return ctx.round_bucket
|
||||
elif counter_id.begins_with("lvl_"):
|
||||
return ctx.level_bucket
|
||||
elif counter_id.begins_with("adv_"):
|
||||
return ctx.adventure_bucket
|
||||
return ctx.battle_bucket
|
||||
|
||||
|
||||
func counter_add(counter_id: String, value: int, set_mode: bool = false) -> void:
|
||||
## counter_id 前缀合法性(加载期校验判定单点):round_/lvl_/adv_ 合法、无前缀落 battle 合法、meta_ 非法
|
||||
## (引擎不接存档级);其余未知前缀合法(落 battle——封闭词表方案误伤全部既有 battle id,见卡② §12 裁点②)。
|
||||
static func counter_id_legal(counter_id: String) -> bool:
|
||||
if counter_id.begins_with("meta_"):
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
## 计数器读数(fact counter 与发射点配额面共用——纯查询读 ctx,引擎不自持)。
|
||||
static func counter(ctx: EffectContext, counter_id: String) -> int:
|
||||
return int(bucket_of(ctx, counter_id).get(counter_id, 0))
|
||||
|
||||
|
||||
## 计数器累加/覆写(静态纯函数——写口语义单点,7 施加点共用;算术逐字承自旧实例方法)。
|
||||
static func counter_add(ctx: EffectContext, counter_id: String, value: int, set_mode: bool = false) -> void:
|
||||
var bucket := bucket_of(ctx, counter_id)
|
||||
if set_mode:
|
||||
_counters[counter_id] = value
|
||||
bucket[counter_id] = value
|
||||
else:
|
||||
_counters[counter_id] = int(_counters.get(counter_id, 0)) + value
|
||||
bucket[counter_id] = int(bucket.get(counter_id, 0)) + value
|
||||
|
||||
|
||||
## 计数器消耗(counter_spend 组合规则——按订阅序消耗共享资源,耗尽即止):
|
||||
## 返回 true=消耗成功;false=资源不足(调用方按 reason 通道拒绝)。
|
||||
func counter_spend(counter_id: String, value: int) -> bool:
|
||||
var cur := int(_counters.get(counter_id, 0))
|
||||
static func counter_spend(ctx: EffectContext, counter_id: String, value: int) -> bool:
|
||||
var bucket := bucket_of(ctx, counter_id)
|
||||
var cur := int(bucket.get(counter_id, 0))
|
||||
if cur < value:
|
||||
return false
|
||||
_counters[counter_id] = cur - value
|
||||
bucket[counter_id] = cur - value
|
||||
return true
|
||||
|
||||
|
||||
## adventure 域计数器导出(battle 末调用,快照回冒险账本;仅 domain=adventure 名单内键)
|
||||
func counters_export() -> Dictionary:
|
||||
var out := {}
|
||||
for cid in _adventure_counter_ids:
|
||||
out[cid] = int(_counters.get(cid, 0))
|
||||
return out
|
||||
# ---- when 判定(条件谓词——零随机全确定;计数器读经 ctx) ----
|
||||
|
||||
|
||||
## adventure 域计数器回灌(rebuild 后由发射宿主调用:冒险账本值回填名单内键,缺省 0;
|
||||
## 名单=本次 rebuild 装载行声明 domain=adventure 的 counter_id 集)
|
||||
func counters_seed(seed: Dictionary) -> void:
|
||||
for cid in _adventure_counter_ids:
|
||||
_counters[cid] = maxi(0, int(seed.get(cid, 0)))
|
||||
|
||||
|
||||
# ---- when 判定(条件谓词——零随机全确定) ----
|
||||
|
||||
func _when_ok(when: Array, payload: Dictionary, facts: Callable) -> bool:
|
||||
func _when_ok(ctx: EffectContext, when: Array, payload: Dictionary, facts: Callable) -> bool:
|
||||
for c_v in when:
|
||||
if not _cond_ok(c_v as Dictionary, payload, facts):
|
||||
if not _cond_ok(ctx, c_v as Dictionary, payload, facts):
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
func _cond_ok(c: Dictionary, payload: Dictionary, facts: Callable) -> bool:
|
||||
func _cond_ok(ctx: EffectContext, c: Dictionary, payload: Dictionary, facts: Callable) -> bool:
|
||||
var fact := String(c.get("fact", ""))
|
||||
var op := String(c.get("op", "eq"))
|
||||
var val = c.get("value", null)
|
||||
var actual = _fact_value(fact, c, payload, facts)
|
||||
var actual = _fact_value(ctx, fact, c, payload, facts)
|
||||
match op:
|
||||
"eq":
|
||||
return str(actual) == str(val)
|
||||
@@ -312,17 +302,17 @@ func _cond_ok(c: Dictionary, payload: Dictionary, facts: Callable) -> bool:
|
||||
# 克制矩阵正面判定(matchup = "{attack_type}_vs_{armor}"——矩阵值 > 0 = 克制成立)
|
||||
return _counter_delta_of(String(actual)) > 0
|
||||
"gte_ref":
|
||||
return int(actual) >= counter(String(c.get("ref", "")))
|
||||
return int(actual) >= counter(ctx, String(c.get("ref", "")))
|
||||
"lt_ref":
|
||||
return int(actual) < counter(String(c.get("ref", "")))
|
||||
return int(actual) < counter(ctx, String(c.get("ref", "")))
|
||||
assert(false, "[效果引擎] when 算子出域: %s" % op)
|
||||
return false
|
||||
|
||||
|
||||
func _fact_value(fact: String, c: Dictionary, payload: Dictionary, facts: Callable):
|
||||
func _fact_value(ctx: EffectContext, fact: String, c: Dictionary, payload: Dictionary, facts: Callable):
|
||||
match fact:
|
||||
"counter":
|
||||
return counter(String(c.get("id", "")))
|
||||
return counter(ctx, String(c.get("id", "")))
|
||||
_:
|
||||
# fact 取值序:payload 同名字段优先 → 补充事实面(发射点注入的运行态读数)
|
||||
if payload.has(fact):
|
||||
|
||||
Reference in new issue
Block a user