卡①T3(拆件批wr5): 宿主超标函数拆分全清+_emit_row束形改形——begin_battle(141行/17分支/16局部/7返回四键)拆编排骨架+_begin_<段>私有族(accept含spawn受理门/dice区间骰+_dice_roll单轴函数/preview_economy),段序=受理与敌人spawn→装板→同调/fx运行态与订阅重建→常时参数与充能重置→巢穴预告与经济注入→轮始发射,取号序逐字(数量骰先/atk骰后/行集空不开流);_place_variants(52行/14局部/6嵌套三键)拆_place_variants_apply+_variants_fringe(嵌套深度5→3,variant_place取号序逐字:vrow单抽先于片生长);_emit_row 8参→5参head归因束(键集type/source_class/origin_cell/highlight缺省空,行字典字段与step_row载荷逐字)调用点全量改形28处=宿主2+grant段6+combat段2+verdict段3+decay段1+battle_fx_host emit_cb 3+reward_grant 9+damage_controller emit_row_cb 6+damage_controller_test _on_row签名+extended_row_violation直调2(卡面G5/G6清单漏计combat2/verdict3/decay1与grant段实为6处、两测试消费方,实查补全);宿主行数压至999≤1000(契约2还债归绿:拆分壳开销以家族归并+陈旧横幅合并+叙事类注释清理吸收,禁删注释凑行数红线未碰——所删皆变更叙事/三写重复说明);gdstyle键账=宿主仅剩类变量38(豁免第三条在案)行数键消失,全闸0 NEW。验收:十套件绿(round_flow/seal/feats/settle/treasure/level_shop/forge/damage/contracts/node_map_determinism)+零SCRIPT ERROR〔拆件批 V9〕
This commit is contained in:
1 parent
cc02bb9315
commit
8fb3310cae
10 files changed
+202
-182
No files matched your search
@@ -20,7 +20,7 @@ var rng: RngServiceCore
|
||||
var minesweeper: MinesweeperRules
|
||||
var action_economy: ActionEconomy
|
||||
var damage: DamageController
|
||||
var emit_cb: Callable # 宿主 _emit_row——行协议面单点(束形改形后随束形)
|
||||
var emit_cb: Callable # 宿主 _emit_row——行协议面单点(5 参 head 束形:head/ri/display/value/extra)
|
||||
var facts_cb: Callable # 宿主 _fx_host_fact——round_index/battle_index/seals_count/nest_count/hp_ratio 五事实宿主侧单点
|
||||
var grant_cb: Callable = Callable() # 揭示兑付回调=RewardGrant 授予面(bind_grant 注入;构造序解法——本件对授予面零强引用)
|
||||
var engine: EffectEngine # 效果引擎(原编排器 _fx——宿主 _init 经此转注 damage/action_economy/reward_economy 三 set_fx)
|
||||
@@ -99,7 +99,7 @@ func flush_pending_reveals(ri: int) -> void:
|
||||
if not minesweeper.is_revealed(c) and not board.is_mine(c):
|
||||
cands.append(c)
|
||||
if cands.is_empty():
|
||||
emit_cb.call("extended", "fx_reveal", Vector2i(-1, -1), ri, "揭示落空(无未揭示安全格)", 0, {"effect_id": "fx_reveal"}, "")
|
||||
emit_cb.call({"type": "extended", "source_class": "fx_reveal", "origin_cell": Vector2i(-1, -1)}, ri, "揭示落空(无未揭示安全格)", 0, {"effect_id": "fx_reveal"})
|
||||
continue
|
||||
var rg: RandomNumberGenerator = rng.open_stream("fx_reveal")
|
||||
var cell: Vector2i = cands[rg.randi_range(0, cands.size() - 1)]
|
||||
@@ -107,10 +107,10 @@ func flush_pending_reveals(ri: int) -> void:
|
||||
var ok: bool = minesweeper.fx_reveal(cell)
|
||||
reveal.busy = false
|
||||
if not ok:
|
||||
emit_cb.call("extended", "fx_reveal", Vector2i(-1, -1), ri, "揭示落空(格子不可揭)", 0, {"effect_id": "fx_reveal"}, "")
|
||||
emit_cb.call({"type": "extended", "source_class": "fx_reveal", "origin_cell": Vector2i(-1, -1)}, ri, "揭示落空(格子不可揭)", 0, {"effect_id": "fx_reveal"})
|
||||
continue
|
||||
grant_cb.call(cell, ri)
|
||||
emit_cb.call("extended", "fx_reveal", cell, ri, "专长揭示", 0, {"effect_id": "fx_reveal"}, "fx_reveal")
|
||||
emit_cb.call({"type": "extended", "source_class": "fx_reveal", "origin_cell": cell, "highlight": "fx_reveal"}, ri, "专长揭示", 0, {"effect_id": "fx_reveal"})
|
||||
|
||||
|
||||
# 效果引擎订阅重建(每战从持有集/装备集重建,中途获得下一战生效;树规则合成订阅同面装载。
|
||||
|
||||
+144
-130
@@ -1,9 +1,8 @@
|
||||
## VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
## 战斗结算编排中枢:轮次生命周期唯一 owner,五段结算(授予→增益/减益→战斗→衰减→判定)与明细行协议发射(垂直切片验证:一场完整战斗逐行归因)。
|
||||
## 效果引擎施加面归 BattleFxHost(core/battle_fx_host——fx 族持有与兑付)、奖励授予管线归 RewardGrant
|
||||
## (core/reward_grant——极性路由/行应用/秘宝使用/escrow);本类保留宿主职责面=状态机与生命周期、
|
||||
## 轮 facts 缓冲、A2 三事件订阅、五段驱动、attune 族、行发射基建(_emit_row/_assert_row_source/_fatal)、
|
||||
## 呈现投影、刷新面;两协作件经组件出口 fx_host()/reward_grant() 取件,禁链式穿透其内部。
|
||||
## 效果引擎施加面归 BattleFxHost、奖励授予管线归 RewardGrant(两拆件,经组件出口 fx_host()/
|
||||
## reward_grant() 取件,禁链式穿透其内部);本类保留宿主职责面=状态机与生命周期、轮 facts 缓冲、
|
||||
## A2 三事件订阅、五段驱动、attune 族、行发射基建(_emit_row/_assert_row_source/_fatal)、呈现投影、刷新面。
|
||||
## 零 await;随机只经 rng 封闭流(encounter/behavior_roll/armor_roll/gp_drop)——同种子同输入逐位复现。
|
||||
## 呈现层读取唯一出口 = presentation_snapshot()/diagnostic_snapshot(),禁链式穿透内部协作对象。
|
||||
## 伤害只向 DamageController 开单(何时开单归编排、怎么算归控制器);刷新唯一通道 player_request_refresh,禁 RAP 满自动刷。
|
||||
@@ -37,8 +36,7 @@ const SOURCE_EXEMPT_CLASSES := ["hero_attack", "enemy_action", "clear_reward", "
|
||||
|
||||
var db: ConfigDb
|
||||
var rng: RngServiceCore
|
||||
# E1 封装收口:协作对象 board_generator/enemy_system/status_effects/reward_economy 私有化——跨层读取一律经 presentation_snapshot()/
|
||||
# diagnostic_snapshot() 成文出口(呈现层链式穿透 settlement._enemy_system.hero_unit() 的根因收口)。
|
||||
# E1 封装收口:协作对象 board_generator/enemy_system/status_effects/reward_economy 私有化——跨层读取一律经 presentation_snapshot()/diagnostic_snapshot() 成文出口。
|
||||
# minesweeper/action_economy 保持公有 = 架构文档 API Boundaries 的 HUD框架消费面(意图提交 settle_batch + compute_move_path
|
||||
# 只读;ap/rap/相位读取)——非本文档登记的契约面,勿增新用途。
|
||||
var _board_generator: BoardGenerator
|
||||
@@ -86,8 +84,7 @@ var nest_preview_count := -1 # 巢穴预告读数口(board_insta
|
||||
var _feat_first_dig_used := false
|
||||
|
||||
|
||||
# 效果引擎宿主侧事实单点(fx_host facts_cb 注入——round_index/battle_index/seals_count/nest_count/
|
||||
# hp_ratio 五事实;charge_layers 归 fx_host 自答不经此;未知事实回落 null)
|
||||
# 效果引擎宿主侧事实单点(fx_host facts_cb 注入——round_index/battle_index/seals_count/nest_count/hp_ratio 五事实;charge_layers 归 fx_host 自答;未知事实回落 null)
|
||||
func _fx_host_fact(fact: String) -> Variant:
|
||||
match fact:
|
||||
"nest_count":
|
||||
@@ -112,8 +109,7 @@ func _fx_host_fact(fact: String) -> Variant:
|
||||
return null
|
||||
|
||||
|
||||
# attune 积点读投影(RewardGrant attune_safe_seen_cb 注入面——use_treasure 拒绝文案读数;
|
||||
# attune 族原位保留,经读投影出户防组件回调宿主私有族)
|
||||
# attune 积点读投影(RewardGrant attune_safe_seen_cb 注入面——use_treasure 拒绝文案读数;经读投影出户防组件回调宿主私有族)
|
||||
func _attune_safe_seen_read() -> int:
|
||||
return _attune_safe_seen
|
||||
|
||||
@@ -217,12 +213,11 @@ func _init(db_ref: ConfigDb, rng_ref: RngServiceCore) -> void:
|
||||
_reward_economy = RewardEconomy.new(db, rng)
|
||||
_damage = DamageController.new(db, rng, _enemy_system, _status_effects)
|
||||
_damage.emit_row_cb = _emit_row # 行协议归属本类(9 类行协议 + 来源断言不外泄)
|
||||
_fx_host = BattleFxHost.new({"db": db, "rng": rng, "minesweeper": minesweeper,
|
||||
"action_economy": action_economy, "damage": _damage, "emit_cb": _emit_row, "facts_cb": _fx_host_fact})
|
||||
_reward_grant = RewardGrant.new({"db": db, "reward_economy": _reward_economy, "damage": _damage,
|
||||
"fx_host": _fx_host, "action_economy": action_economy, "status_effects": _status_effects,
|
||||
"units": _enemy_system, "emit_cb": _emit_row, "fatal_cb": _fatal,
|
||||
"attune_safe_seen_cb": _attune_safe_seen_read, "pool_effective_cb": _pool_effective})
|
||||
_fx_host = BattleFxHost.new({"db": db, "rng": rng, "minesweeper": minesweeper, "action_economy": action_economy,
|
||||
"damage": _damage, "emit_cb": _emit_row, "facts_cb": _fx_host_fact})
|
||||
_reward_grant = RewardGrant.new({"db": db, "reward_economy": _reward_economy, "damage": _damage, "fx_host": _fx_host,
|
||||
"action_economy": action_economy, "status_effects": _status_effects, "units": _enemy_system, "emit_cb": _emit_row,
|
||||
"fatal_cb": _fatal, "attune_safe_seen_cb": _attune_safe_seen_read, "pool_effective_cb": _pool_effective})
|
||||
_fx_host.bind_grant(_reward_grant.reveal_redeem) # 构造序禁环:两件建齐后接线(fx 件对授予面走注入回调)
|
||||
_damage.set_fx(_fx_host.engine, _fx_host.counters_ctx) # 效果引擎注入(attack_commit/clamp_check/armor_roll/kill/damage_taken 发射面)
|
||||
action_economy.set_fx(_fx_host.engine, _fx_host.counters_ctx) # dig_paid/charge_full 发射面
|
||||
@@ -240,11 +235,34 @@ func _init(db_ref: ConfigDb, rng_ref: RngServiceCore) -> void:
|
||||
|
||||
|
||||
# ---- 战斗开始序列(编排专章①:门 → 增益状态 → 敌人系统 → install+roll → 行动经济)----
|
||||
|
||||
# 段序=受理与敌人 spawn → 装板 → 同调/fx 运行态与订阅重建 → 常时参数与充能重置 → 巢穴预告与经济注入 → 轮始发射;各段=_begin_<段> 私有族。
|
||||
func begin_battle(enc: Dictionary) -> bool:
|
||||
if round_index != 0:
|
||||
push_warning("[战斗结算] begin_battle 重复受理")
|
||||
return false
|
||||
if not _begin_battle_accept(enc):
|
||||
return false
|
||||
if not _install_new_board():
|
||||
return false
|
||||
_reset_round_facts() # 首板 install 的初始展开事件不入轮 facts(resource 恒 false 的双保险)
|
||||
_begin_battle_fx_state(enc)
|
||||
_begin_battle_attune_params(enc)
|
||||
_begin_battle_preview_economy(enc)
|
||||
# 工兵晨令=round_start grant_ap(mode:queue) 订阅行承载(when round_index ≤ effective_rounds
|
||||
# value_from 定影);round_start 发射先于行动经济轮切换=树规则 queue 型当轮入队当轮兑现
|
||||
# (先手压制=首轮 AP+1 语义锚);计数器 round_start 重置点同点生效
|
||||
round_index = 1
|
||||
_fx_host.emit_round_start()
|
||||
if not action_economy.notify_round_start():
|
||||
_fatal("行动经济 首轮 notify_round_start 被拒")
|
||||
return false
|
||||
_roll_round_actions() # 开轮摇号(首轮同样摇;先于 round_started,挖掘期动画位可读)
|
||||
round_started.emit(round_index)
|
||||
return true
|
||||
|
||||
|
||||
# 受理段:encounter 装载+缓冲清零+护甲合流+重载闸+开域门+遭遇骰与敌人系统 spawn 受理门。
|
||||
func _begin_battle_accept(enc: Dictionary) -> bool:
|
||||
encounter.assign(enc)
|
||||
_reward_grant.reset_battle() # 战斗域零 GP 持久(每场战斗缓冲从零起算;案C 闸复位)
|
||||
_damage.set_hero_ac_override(int(enc.get("hero_armor_class", -1))) # 主角护甲合流入参(伤害控制器 defender_ac 消费)
|
||||
@@ -252,27 +270,30 @@ func begin_battle(enc: Dictionary) -> bool:
|
||||
if not _status_effects.notify_battle_start():
|
||||
_fatal("增益状态 开域被拒(编排违例)")
|
||||
return false
|
||||
# 遭遇骰(难度曲线与Boss GDD Core Rule 8):行有 unit_count_max 时按 [unit_count, max] 均匀骰;
|
||||
# 行有 atk_min 时按 [atk_min, atk_max] 骰 spawn 期整战持久值
|
||||
# (每拍抖动否决——数字可读性锚;中点 = 行值)。encounter 封闭流恰一 scope(数量骰先、atk 骰后);
|
||||
# 挖掘决策域之外——P1 合规;敌人系统 零随机纪律保持(收 override 即确定值)。
|
||||
# 遭遇骰(难度曲线与Boss GDD Core Rule 8):行有 unit_count_max 时按 [unit_count, max] 均匀骰;
|
||||
# 行有 atk_min 时按 [atk_min, atk_max] 骰 spawn 期整战持久值
|
||||
# (每拍抖动否决——数字可读性锚;中点 = 行值)。encounter 封闭流恰一 scope(数量骰先、atk 骰后);
|
||||
# 挖掘决策域之外——P1 合规;敌人系统 零随机纪律保持(收 override 即确定值)。
|
||||
# 逐行骰:混编行集**每行各掷自己的区间骰**——
|
||||
# 先按行序逐行数量骰、再按行序逐行 atk 骰(单行行集 = 既有一次数量骰 + 一次 atk 骰,判定条件、
|
||||
# 开流条件、消费序、消费次数逐位一致——零内容期随机流零变化;区间宽度为零的行不掷 = 「定值不骰」
|
||||
# 既有口径;atk 区间逐行同构 = 行值语义完整,属「每行一骰」语义内的
|
||||
# 行字段区间骰逐行化,非新增随机源形态)。
|
||||
# 行类维度:节点身份 battle/boss 决定行集键域(缺省 battle 只服务测试直呼面——生产路径恒传 node_type)
|
||||
var enc_kind := String(enc.get("node_type", "battle"))
|
||||
var erowset_pre: Array[Variant] = db.resolve_enemy_rowset(int(enc.level_index), int(enc.node_depth), enc_kind)
|
||||
if erowset_pre.is_empty():
|
||||
_fatal("敌人表 行集解析为空(%d,%d,%s)" % [int(enc.level_index), int(enc.node_depth), enc_kind])
|
||||
var dice := _begin_battle_dice(enc)
|
||||
if dice.is_empty():
|
||||
_fatal("敌人表 行集解析为空(%d,%d,%s)" % [int(enc.level_index), int(enc.node_depth), String(enc.get("node_type", "battle"))])
|
||||
return false
|
||||
var count_overrides: Array[Variant] = []
|
||||
var atk_overrides: Array[Variant] = []
|
||||
# E4 收口:加成经 encounter 显式携带(hero_atk/hero_max_hp;缺省 = 表值——冒烟/工具直呼路径)
|
||||
_hero_atk = int(enc.get("hero_atk", int(db.hero.atk)))
|
||||
if not _enemy_system.spawn(int(enc.level_index), int(enc.node_depth), int(enc.hero_hp), dice[0],
|
||||
_hero_atk, int(enc.get("hero_max_hp", -1)), dice[1],
|
||||
String(enc.get("attack_type", "")), String(enc.get("armor_type", "")), String(enc.get("node_type", "battle"))): # 两根类型轴(空串=锻造配置默认值回退,行为连续)+行类维(battle/boss)
|
||||
_fatal("敌人系统 spawn 被拒")
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
# 遭遇骰(难度曲线与Boss GDD Core Rule 8):行有 unit_count_max 时按 [unit_count, max] 均匀骰;
|
||||
# 行有 atk_min 时按 [atk_min, atk_max] 骰 spawn 期整战持久值
|
||||
# encounter 封闭流恰一 scope(数量骰先、atk 骰后——区间宽度为零的行不掷=「定值不骰」既有口径);
|
||||
# 敌人系统零随机纪律保持(收 override 即确定值)。
|
||||
# 返回 [count_overrides, atk_overrides];行集为空 = 返回 [](fail 判据交受理段 _fatal)。
|
||||
func _begin_battle_dice(enc: Dictionary) -> Array:
|
||||
# 行类维度:节点身份 battle/boss 决定行集键域(缺省 battle 只服务测试直呼面——生产路径恒传 node_type)
|
||||
var erowset_pre: Array[Variant] = db.resolve_enemy_rowset(int(enc.level_index), int(enc.node_depth), String(enc.get("node_type", "battle")))
|
||||
if erowset_pre.is_empty():
|
||||
return []
|
||||
var needs_count := false
|
||||
var needs_atk := false
|
||||
for r in erowset_pre:
|
||||
@@ -280,38 +301,38 @@ func begin_battle(enc: Dictionary) -> bool:
|
||||
needs_count = true
|
||||
if r.has("atk_min"):
|
||||
needs_atk = true
|
||||
if needs_count or needs_atk:
|
||||
var estream: RandomNumberGenerator = rng.open_stream("encounter")
|
||||
if needs_count:
|
||||
for r in erowset_pre:
|
||||
var lo: int = int(r.get("unit_count", 1))
|
||||
var hi: int = int(r.get("unit_count_max", lo))
|
||||
count_overrides.append((lo + estream.randi_range(0, hi - lo)) if hi > lo else lo)
|
||||
if needs_atk:
|
||||
for r in erowset_pre:
|
||||
if r.has("atk_min"):
|
||||
atk_overrides.append(int(r.atk_min) + estream.randi_range(0, int(r.atk_max) - int(r.atk_min)))
|
||||
else:
|
||||
atk_overrides.append(-1)
|
||||
# E4 收口:加成经 encounter 显式携带(hero_atk/hero_max_hp;缺省 = 表值——冒烟/工具直呼路径)
|
||||
_hero_atk = int(enc.get("hero_atk", int(db.hero.atk)))
|
||||
if not _enemy_system.spawn(int(enc.level_index), int(enc.node_depth), int(enc.hero_hp), count_overrides,
|
||||
_hero_atk, int(enc.get("hero_max_hp", -1)), atk_overrides,
|
||||
String(enc.get("attack_type", "")), String(enc.get("armor_type", "")), enc_kind): # 两根类型轴(空串=锻造配置默认值回退,行为连续)+行类维(battle/boss)
|
||||
_fatal("敌人系统 spawn 被拒")
|
||||
return false
|
||||
if not _install_new_board():
|
||||
return false
|
||||
_reset_round_facts() # 首板 install 的初始展开事件不入轮 facts(resource 恒 false 的双保险)
|
||||
if not (needs_count or needs_atk):
|
||||
return [[], []] # 区间全定值 = 零骰合法态(不开流零取号)
|
||||
var estream: RandomNumberGenerator = rng.open_stream("encounter")
|
||||
return [_begin_battle_dice_roll(erowset_pre, estream, false), _begin_battle_dice_roll(erowset_pre, estream, true)]
|
||||
|
||||
|
||||
# 区间骰单轴(roll_atk=false=数量骰〔宽度为零的行不掷=「定值不骰」〕/true=atk 骰〔行缺 atk_min 记 -1=无覆盖表值〕;消费序=数量骰先、atk 骰后)
|
||||
func _begin_battle_dice_roll(rows: Array, estream: RandomNumberGenerator, roll_atk: bool) -> Array:
|
||||
var out: Array[Variant] = []
|
||||
for r in rows:
|
||||
if roll_atk:
|
||||
if r.has("atk_min"):
|
||||
out.append(int(r.atk_min) + estream.randi_range(0, int(r.atk_max) - int(r.atk_min)))
|
||||
else:
|
||||
out.append(-1)
|
||||
else:
|
||||
var lo: int = int(r.get("unit_count", 1))
|
||||
var hi: int = int(r.get("unit_count_max", lo))
|
||||
out.append((lo + estream.randi_range(0, hi - lo)) if hi > lo else lo)
|
||||
return out
|
||||
|
||||
|
||||
# 淬毒/偏运=triggers 订阅行(淬毒=clamp_check
|
||||
# modify 窗口、偏运=reward_roll bias_rewards;value 行字段 value_from 定影,begin_battle 不注入)
|
||||
# 特性运行态(enc.feats 携带持有集:效果行为=数据行 triggers 经效果
|
||||
# 引擎分发;流派档位=FeatsEngine 纯函数求值两运行态注伤害控制器。魔矢每战首次开格计数同步归零)
|
||||
# 扩签运行态就位(归 fx_host)+账本挂接+桶挂引用+揭示/AC 两回调+rider 档位恒零+订阅重建。
|
||||
func _begin_battle_fx_state(enc: Dictionary) -> void:
|
||||
# 同调装配运行态注入(enc.attunement 携带;off=空集零扰动。淬毒 N 注入
|
||||
# 伤害控制器、偏运 N 注入地图战斗资源——均须先于首板 install)
|
||||
attunement = (enc.get("attunement", []) as Array).duplicate()
|
||||
_attune_safe_seen = 0
|
||||
# 淬毒/偏运=triggers 订阅行(淬毒=clamp_check
|
||||
# modify 窗口、偏运=reward_roll bias_rewards;value 行字段 value_from 定影,begin_battle 不注入)
|
||||
# 特性运行态(enc.feats 携带持有集:效果行为=数据行 triggers 经效果
|
||||
# 引擎分发;流派档位=FeatsEngine 纯函数求值两运行态注伤害控制器。魔矢每战首次开格计数同步归零)
|
||||
# 扩签运行态就位(归 fx_host:AC 基线快照、累积器清零、揭示队列+再入闸清零、bonus_delta 四轴清零)
|
||||
_fx_host.reset_battle_state(int(enc.get("hero_armor_class", -1)))
|
||||
var ledger_ref = enc.get("ledger", null)
|
||||
_ledger = ledger_ref if ledger_ref is RunLedger else RunLedger.new() # 缺省新账=调试/单场零扰动面
|
||||
@@ -329,51 +350,41 @@ func begin_battle(enc: Dictionary) -> bool:
|
||||
# 订阅(rebuild 注入——round_start 首轮 AP+1 / damage_taken 减伤+1)。其余 rule 键
|
||||
# =stat 面投影不入战斗(T8 表内容 provisional 口径披露))
|
||||
var tree_rules: Array[Variant] = enc.get("tree_rules", []) as Array
|
||||
# 效果引擎订阅重建(每战从持有集/装备集重建,中途获得下一战生效;
|
||||
# 树规则合成订阅同面装载。装载序=feats 表序→treasure 表序→树规则,写死保种子复放。
|
||||
# 计数器四桶经 counters_ctx 引用宿主容器,无回灌时点(旧战首回灌时点删除))
|
||||
# 效果引擎订阅重建(装载序=feats 表序→treasure 表序→树规则,写死保种子复放——细则见 battle_fx_host.rebuild_subscriptions)
|
||||
_fx_host.rebuild_subscriptions(feat_ids, attunement, tree_rules)
|
||||
# 秘宝常时参数注入(装配集 kind 分发:远程镐挖距放宽/信标网络封印可达豁免;
|
||||
# 缺省 0/false=零装配零扰动。注入在首板 install 之后〔板在早段〕——板生成不读两参数,
|
||||
# 参数只影响挖掘/插棋动作判据,时序安全)
|
||||
|
||||
|
||||
# 秘宝常时参数注入(装配集 kind 分发:远程镐挖距放宽/信标网络封印可达豁免;
|
||||
# 缺省 0/false=零装配零扰动。注入在首板 install 之后〔板在早段〕——板生成不读两参数,
|
||||
# 参数只影响挖掘/插棋动作判据,时序安全)+充能重置周期(battle=每战回满/level=每关首战回满
|
||||
# 〔battle_index==1〕;adventure 周期维持冒险开始 _seed_attune_charges 单次)
|
||||
func _begin_battle_attune_params(enc: Dictionary) -> void:
|
||||
minesweeper.dig_range_bonus = attune_param_sum("dig_range_bonus")
|
||||
minesweeper.seal_range_free = attune_param_sum("seal_range_free") > 0
|
||||
# 充能重置周期(battle=每战回满/level=每关首战回满〔battle_index==1〕;
|
||||
# adventure 周期维持冒险开始 _seed_attune_charges 单次)
|
||||
_reseed_attune_charges("battle")
|
||||
if int(enc.get("battle_index", 1)) == 1:
|
||||
_reseed_attune_charges("level")
|
||||
# board_installed 发射+施加(巢穴预告:装板后一次报本板危险格数量;
|
||||
# 只报数量不标位置=信息红线,不碰分布=棋盘事实不可变契约 3。命中=读数口落值,呈现归
|
||||
# 呈现桥消费(无格来源不进结算明细行——行来源纪律);板已就绪〔早段 install〕、订阅行已在 rebuild 装载)
|
||||
|
||||
|
||||
# board_installed 发射+施加(巢穴预告:装板后一次报本板危险格数量;
|
||||
# 只报数量不标位置=信息红线,不碰分布=棋盘事实不可变契约 3。命中=读数口落值,呈现归
|
||||
# 呈现桥消费(无格来源不进结算明细行——行来源纪律);板已就绪〔早段 install〕、订阅行已在 rebuild 装载)+
|
||||
# 跨冒险行动点加成轴(统一冒险加算区第四轴):经排队修正通道兑现
|
||||
# 「每场战斗开始首轮 AP +加成值」(queue_ap_delta = 下轮开轮基线加算、notify_round_start 兑现后
|
||||
# 清零——首轮即消费,后续轮次不重复兑现;语义锚 = 行动经济 GDD「跨冒险行动点加成轴」节)+
|
||||
# 储备金袋(节点级 RAP 注入)接收(完美保险注入= feats.tres 订阅承载,不经此通道)
|
||||
func _begin_battle_preview_economy(enc: Dictionary) -> void:
|
||||
nest_preview_count = -1
|
||||
var bi_hits := _fx_host.fx_emit("board_installed", {})
|
||||
for h in bi_hits:
|
||||
if String(h.get("kind", "")) == "mine_count_preview":
|
||||
nest_preview_count = minesweeper.mine_total()
|
||||
# 跨冒险行动点加成轴(统一冒险加算区第四轴):经排队修正通道兑现
|
||||
# 「每场战斗开始首轮 AP +加成值」(queue_ap_delta = 下轮开轮基线加算、notify_round_start 兑现后
|
||||
# 清零——首轮即消费,后续轮次不重复兑现;语义锚 = 行动经济 GDD「跨冒险行动点加成轴」节)
|
||||
var ap_bonus: int = int(enc.get("hero_ap_bonus", 0)) + int(enc.get("voucher_time_sand", 0)) # 时沙同通道(首轮池增量,不改上限)
|
||||
if ap_bonus > 0:
|
||||
action_economy.queue_ap_delta(ap_bonus)
|
||||
# 储备金袋(节点级 RAP 注入)接收(完美保险注入= feats.tres 订阅承载,不经此通道)
|
||||
var voucher_rap: int = int(enc.get("voucher_rap", 0))
|
||||
if voucher_rap > 0:
|
||||
action_economy.grant_reserve_ap(voucher_rap)
|
||||
# 工兵晨令=round_start grant_ap(mode:queue) 订阅行承载,
|
||||
# when round_index ≤ effective_rounds 行字段 value_from 定影;发射位先于轮切换=当轮兑现
|
||||
# round_start 发射+施加(发射位先于行动经济轮切换:树规则 grant_ap
|
||||
# mode:queue 当轮入队、notify_round_start 即兑现(先手压制=首轮 AP+1 语义锚);
|
||||
# 计数器 round_start 重置点同点生效)
|
||||
round_index = 1
|
||||
_fx_host.emit_round_start()
|
||||
if not action_economy.notify_round_start():
|
||||
_fatal("行动经济 首轮 notify_round_start 被拒")
|
||||
return false
|
||||
_roll_round_actions() # 开轮摇号(首轮同样摇;先于 round_started,挖掘期动画位可读)
|
||||
round_started.emit(round_index)
|
||||
return true
|
||||
|
||||
|
||||
func _install_new_board(during_round := false) -> bool:
|
||||
@@ -381,10 +392,7 @@ func _install_new_board(during_round := false) -> bool:
|
||||
var board: BoardData = _board_generator.generate(int(encounter.level_index), int(encounter.node_depth), stream, db)
|
||||
if board == null:
|
||||
return false
|
||||
# 全局作用域变异决议(装板序内前置于 roll_board:图级偏斜旋钮须在资源
|
||||
# 摇号前定型;全局门控与板无关〔节点身份+unlock+board_types〕,确定性取用零取号)。
|
||||
# boss/elite 图命中全局型 → 写 board_variants(_place_variants 见非空即跳 region=替换制);
|
||||
# 全局池空 → 整图字段留空(_place_variants 原路走 region=兜底回落合法态)。
|
||||
# 全局作用域变异决议(装板序内前置于 roll_board:图级偏斜旋钮须在资源摇号前定型——详见 _resolve_global_variant)
|
||||
_resolve_global_variant(board)
|
||||
var res: Dictionary[Variant, Variant] = minesweeper.install_board(board, during_round)
|
||||
if not res.accepted:
|
||||
@@ -424,7 +432,7 @@ func _install_new_board(during_round := false) -> bool:
|
||||
# boss/elite 图命中全局型:写整图 board_variants(恰一行)+ 注入资源面偏斜旋钮(图级投影在
|
||||
# RewardEconomy.roll_board 消费);不消费 variant_place 流(全局取用确定性——零取号合法态)。
|
||||
# 全局池空(含 L1 Boss 免疫/unlock 门控未达):字段留空、旋钮归零——_place_variants 原路走
|
||||
# region 布置 = 兜底回落(保守向后兼容,防配置缺行静默裸奔——provisional)。
|
||||
# region 布置 = 兜底回落合法态(防配置缺行静默裸奔——provisional)。
|
||||
func _resolve_global_variant(board: BoardData) -> void:
|
||||
board.board_variants.clear()
|
||||
_reward_economy.board_skew = 0
|
||||
@@ -441,12 +449,10 @@ func _resolve_global_variant(board: BoardData) -> void:
|
||||
# 池空 = 合法态,静默零布置(框架 P0 口径不变)。零特判:本段只写格子变异数据快照,不改扫雷规则本体。
|
||||
# 候选格域 = 安全格 − 初始展开区(伤害型 placement=closed_only 的结构保障——布置发生在初始展开之后,
|
||||
# 已开格天然排除);危险格不吃变异(挖雷一层惩罚 + 变异伤害双层叠罚污染逐行归因,首版排除——provisional)。
|
||||
# 每图至多一种类型(候选池内均匀选一,混型布置归因复杂留待后续);数量 = round(候选池格数×percent) +
|
||||
# (关卡序号−1)×per_level_bonus——基数为候选池而非总格数(初始展开
|
||||
# 占全板 56–67%,总格数基数使有效密度膨胀 4–6 倍、L1 小板极端全池覆盖;percent 语义 = 占可挖格比例,
|
||||
# 关卡递增由 per_level_bonus 绝对格数承载);连通片生长(8 邻),单片 ≤ pack_max(2×2 / 1×4 / 1×3+1 等形)。
|
||||
# 替换制(全局作用域变异):整图 board_variants 非空 = 本图已带全局型,跳过 region
|
||||
# 布置(每图一型的整图推广:普通图=region,Boss/精英图=global);空 = 原路径(含 Boss 图全局池空回落)。
|
||||
# 每图至多一种类型(候选池内均匀选一);数量 = round(候选池格数×percent) +
|
||||
# (关卡序号−1)×per_level_bonus——基数为候选池而非总格数(percent 语义 = 占可挖格比例,
|
||||
# 关卡递增由 per_level_bonus 绝对格数承载);连通片生长(8 邻),单片 ≤ pack_max。
|
||||
# 替换制:整图 board_variants 非空 = 本图已带全局型,跳过 region 布置(详见 _resolve_global_variant)。
|
||||
func _place_variants(board: BoardData) -> void:
|
||||
if not board.board_variants.is_empty():
|
||||
return
|
||||
@@ -467,6 +473,11 @@ func _place_variants(board: BoardData) -> void:
|
||||
var total: int = int(round(float(candidates.size()) * float(db.variation.percent_of_cells) / 100.0)) \
|
||||
+ (int(encounter.level_index) - 1) * int(db.variation.per_level_bonus)
|
||||
total = mini(total, candidates.size())
|
||||
_place_variants_apply(board, candidates, stream, vrow, total)
|
||||
|
||||
|
||||
# 变异片生长施加面(布置域=候选格,数量=total,vrow 单型贯穿;取号序=stream 顺序消费)
|
||||
func _place_variants_apply(board: BoardData, candidates: Array[Vector2i], stream: RandomNumberGenerator, vrow: Dictionary, total: int) -> void:
|
||||
var placed := {}
|
||||
while placed.size() < total:
|
||||
var pack_size: int = mini(total - placed.size(), stream.randi_range(2, int(db.variation.pack_max)))
|
||||
@@ -478,18 +489,13 @@ func _place_variants(board: BoardData) -> void:
|
||||
continue
|
||||
free.append(c)
|
||||
if free.is_empty():
|
||||
break
|
||||
break # 生长域耗尽——该片就地收束(数量不足总目标为合法态,候选域碎片化时触发)
|
||||
var seed_c: Vector2i = free[stream.randi_range(0, free.size() - 1)]
|
||||
placed[seed_c] = true
|
||||
var pack: Array[Variant] = [seed_c]
|
||||
var pack_set := {seed_c: true}
|
||||
while pack.size() < pack_size:
|
||||
var fringe: Array[Vector2i] = []
|
||||
for c in pack:
|
||||
for n in board.neighbors8(c):
|
||||
if candidates.has(n) and not placed.has(n) and not fringe.has(n) \
|
||||
and not _adjacent_to_packs(board, placed, n, pack_set):
|
||||
fringe.append(n)
|
||||
var fringe := _variants_fringe(board, pack, candidates, placed, pack_set)
|
||||
if fringe.is_empty():
|
||||
break # 生长域耗尽——该片就地收束(数量不足总目标为合法态,候选域碎片化时触发)
|
||||
var nxt: Vector2i = fringe[stream.randi_range(0, fringe.size() - 1)]
|
||||
@@ -500,6 +506,17 @@ func _place_variants(board: BoardData) -> void:
|
||||
board.variants[c] = [String(vrow.variant_id)]
|
||||
|
||||
|
||||
# 片生长待选格收集(本片内各生长格的 8 邻候选并集——未布置、未在途、不与他片贴接)
|
||||
func _variants_fringe(board: BoardData, pack: Array, candidates: Array[Vector2i], placed: Dictionary, pack_set: Dictionary) -> Array[Vector2i]:
|
||||
var fringe: Array[Vector2i] = []
|
||||
for c in pack:
|
||||
for n in board.neighbors8(c):
|
||||
if candidates.has(n) and not placed.has(n) and not fringe.has(n) \
|
||||
and not _adjacent_to_packs(board, placed, n, pack_set):
|
||||
fringe.append(n)
|
||||
return fringe
|
||||
|
||||
|
||||
# 候选格是否与其他已布片(own_pack 之外的 placed 格)8 邻贴接——片间隔离保证连通分量 = 单片 ≤ pack_max
|
||||
func _adjacent_to_packs(board: BoardData, placed: Dictionary, c: Vector2i, own_pack: Dictionary = {}) -> bool:
|
||||
for m in board.neighbors8(c):
|
||||
@@ -561,8 +578,7 @@ func _maybe_settle() -> void:
|
||||
func _settle() -> void:
|
||||
_settling = true
|
||||
var ri := round_index
|
||||
# 段序编排(五段 Segment;段体归 game/core/settlement/,本类只做段序+
|
||||
# 行发射/开单原语单点)
|
||||
# 段序编排(五段——段体归 game/core/settlement/,本类只做段序+行发射/开单原语单点)
|
||||
_grant_seg.run(ri) # ① 授予段(案C 闸随段装载)
|
||||
_aura_seg.run(ri) # ② 增益/减益段(续存实例组合行)
|
||||
_combat_seg.run(ri) # ③ 战斗段(主角先手 → 逐敌还手 → 地图伤害账殿后〔案C〕)
|
||||
@@ -577,7 +593,7 @@ func _settle() -> void:
|
||||
|
||||
# 开格产出乘区(贪婪矿灯/矿脉直觉/赌徒的血签事件收集器注入):
|
||||
# positive=power ×(1+静态 mult%);negative=power ×(1+cost%)。
|
||||
# 全零收集器=原快照原样返回(零扰动)。旧 dig_combo 连击加成死读数(全仓无声明行、combo_val 恒 0)已删——零行为差。
|
||||
# 全零收集器=原快照原样返回(零扰动)。
|
||||
func _feat_yield_adjust(snap: Dictionary, positive: bool, pos_m: int, neg_m: int) -> Dictionary:
|
||||
if positive and pos_m <= 0:
|
||||
return snap
|
||||
@@ -643,9 +659,9 @@ func _roll_round_actions() -> void:
|
||||
# 消费仍在 behavior_roll 既有流——同种子逐位复现不破坏)
|
||||
for sid in _enemy_system.check_skill_unlocks(u):
|
||||
var srow_u: Dictionary[Variant, Variant] = db.skill_row(sid)
|
||||
_emit_row("highlight", "enemy_action", Vector2i(-1, -1), round_index,
|
||||
"%s 解锁了 %s" % [String(u.display_name), String(srow_u.get("display_name", sid))],
|
||||
0, {"skill_id": sid}, "skill_unlock")
|
||||
_emit_row({"type": "highlight", "source_class": "enemy_action", "origin_cell": Vector2i(-1, -1), "highlight": "skill_unlock"},
|
||||
round_index, "%s 解锁了 %s" % [String(u.display_name), String(srow_u.get("display_name", sid))],
|
||||
0, {"skill_id": sid})
|
||||
# enemy_hp_threshold 发射点(内容数据驱动扩展批 T1——血量阈值沿;对所有带该字段敌人逐个发射,
|
||||
# 非 Boss 专属——Boss 语义留给订阅者 when 判定。pct=本次解锁条目的 unlock_hp_pct 表值)
|
||||
var pct_v := 0
|
||||
@@ -670,15 +686,8 @@ func _roll_round_actions() -> void:
|
||||
_enemy_system.tick_cooldowns() # 递减在摇号读值之后——"施放后连续 N 拍不可摇中"的等价实现(单位容器 注记)
|
||||
|
||||
|
||||
# ---- 战斗段 ----
|
||||
# 本段只开单——主角先手单 + 敌方伤害技单;掷点/折扣/暴击/克制/
|
||||
# 池乘区/下限/交付/commit 全部收口 伤害控制器(A 模式提交即结;B 模式段尾 flush)。
|
||||
# 开单守卫归流程(流程决定给谁开单):主角存活+目标存在才开先手单;hero hp=0 立即
|
||||
# 停开(B 模式下若 flush 中主角倒下,由控制器死亡事件终止余单——两模式同一结果)。
|
||||
|
||||
# ---- 算术读数 ----
|
||||
# 掷点/折扣/克制点数/池乘区/攻击行文本 归 伤害控制器(damage_controller.gd 管线单点);
|
||||
# 本类只保留一处薄委托——_defender_ac(快照面消费)。
|
||||
# ---- 战斗段/算术读数(段体与掷点/折扣/暴击/克制/池乘区/下限/交付管线归 damage_controller.gd
|
||||
# 与 settlement/combat_segment.gd;本类只保留薄委托——_defender_ac 供结算/呈现快照消费)----
|
||||
|
||||
# 防守方护甲等级读数——实现归 伤害控制器 defender_ac(本委托供结算快照/呈现快照)
|
||||
func _defender_ac(unit: Dictionary) -> int:
|
||||
@@ -700,7 +709,8 @@ func _emit_composition_effects(side: String, ri: int) -> void:
|
||||
for eff in _status_effects.query_active(side):
|
||||
if eff.round_index < ri:
|
||||
# 续存实例(前轮授予)——每结算兑付一次的可见面(合并实例只持首授归因——增益状态 义务⑦)
|
||||
_emit_row("effect", eff.source_class, eff.origin_cell, ri, eff.display_name, eff.snap_magnitude, {"effect_id": eff.effect_id, "pool_value": eff.effective_value})
|
||||
_emit_row({"type": "effect", "source_class": eff.source_class, "origin_cell": eff.origin_cell}, ri,
|
||||
eff.display_name, eff.snap_magnitude, {"effect_id": eff.effect_id, "pool_value": eff.effective_value})
|
||||
|
||||
|
||||
# ---- 结算快照(playtest 数值分析数据面——每次结算全量状态;运行时零决策读取)----
|
||||
@@ -800,12 +810,10 @@ func _battle_over(result: String) -> void:
|
||||
var hu_w: Dictionary[Variant, Variant] = _enemy_system.hero_unit()
|
||||
hu_w["hp"] = mini(int(hu_w.get("hp", 0)) + int(h.get("value", 0)), maxi(1, int(hu_w.get("max_hp", 1))))
|
||||
# bonus_delta 战末汇入(血税双向账:本战累积并入冒险加算区共享引用后清零)+
|
||||
# adventure 域计数器导出回写(T6——共享引用覆写,attune_charges 先例)
|
||||
var drained := _fx_host.bonus_delta_drain()
|
||||
if not _ledger.bonus.is_empty():
|
||||
for axis in drained.keys():
|
||||
_ledger.bonus_add_axis(axis, int(drained[axis]))
|
||||
# 搬运链删除:adventure 桶经 ctx 引用直写 RunLedger,无战末导出时点(旧导出/汇入搬运链删除)
|
||||
# 映射随战斗结束退役(地图战斗资源 义务④——下一场战斗 = 新板新 scope 批,roll_board 相位守卫的前提)
|
||||
var hero_hp_left := int(_enemy_system.hero_unit().get("hp", 0)) # despawn 前捕获(信号载荷供呈现层"HP 带入下一层")
|
||||
var gp_dropped := _roll_gp_drop() # 战斗掉落(escrow 随此值上抛即弃)
|
||||
@@ -938,7 +946,13 @@ func _reset_round_facts() -> void:
|
||||
|
||||
# ---- 行协议(Core Rule:公共字段 + 行类型特有字段)----
|
||||
|
||||
func _emit_row(type: String, source_class: String, origin_cell: Vector2i, ri: int, display: String, value: int, extra: Dictionary, highlight: String = "") -> void:
|
||||
func _emit_row(head: Dictionary, ri: int, display: String, value: int, extra: Dictionary) -> void:
|
||||
# head 归因束封闭键集:type/source_class/origin_cell/highlight(缺省"")——束形只改函数形参,
|
||||
# 行字典字段与 step_row 载荷逐字不变(行协议词表零触碰)
|
||||
var type := String(head.get("type", ""))
|
||||
var source_class := String(head.get("source_class", ""))
|
||||
var origin_cell: Vector2i = head.get("origin_cell", Vector2i(-1, -1))
|
||||
var highlight := String(head.get("highlight", ""))
|
||||
# 扩展行必载效果身份(行协议 fail-fast——违约行先炸后不发,零发射;开发期违约即炸,
|
||||
# 与 _assert_row_source 同族 assert 形态)
|
||||
if type == "extended" and String(extra.get("effect_id", "")) == "":
|
||||
|
||||
@@ -33,7 +33,7 @@ var db: ConfigDb
|
||||
var rng: RngServiceCore # 注入成员类型注(单测经真实 RngService 构造,标注安全)
|
||||
var units: UnitContainer # UnitContainer——目标解析/伤害交付/存活查询(句柄即 Dictionary 引用)
|
||||
var effects: StatusContainer # StatusContainer——池值/AC 型效果读取(增益状态 只读消费面)
|
||||
var emit_row_cb: Callable # 行协议注入口:func(type, source_class, origin_cell, ri, display, value, extra, highlight)
|
||||
var emit_row_cb: Callable # 行协议注入口:func(head, ri, display, value, extra)——head 归因束键集 type/source_class/origin_cell/highlight(缺省"")
|
||||
|
||||
var policy := POLICY_SEGMENT # 结账时机开关(B=攒批段界统一结;回切 A 改 POLICY_IMMEDIATE 即开单即结)
|
||||
var _queue: Array[Dictionary] = [] # 未结单据(B 模式攒批;A 模式恒空)
|
||||
@@ -430,7 +430,7 @@ func _hero_attack_deliver(t: Dictionary, ctx: Dictionary, redemption: Dictionary
|
||||
var label := _attack_label(String(t.display), bool(ctx["crit"]), int(ctx["discount"]))
|
||||
if int(redemption["hero_rider"]) > 0:
|
||||
label += "+奥术%d(必中)" % int(redemption["hero_rider"])
|
||||
emit_row_cb.call("damage", String(t.source_class), t.origin_cell, int(t.round_index), label, int(redemption["landed"]), payload, String(t.highlight))
|
||||
emit_row_cb.call({"type": "damage", "source_class": String(t.source_class), "origin_cell": t.origin_cell, "highlight": String(t.highlight)}, int(t.round_index), label, int(redemption["landed"]), payload)
|
||||
_outcomes[int(t.ticket_id)] = "redeemed"
|
||||
|
||||
|
||||
@@ -477,7 +477,7 @@ func _resolve_enemy_skill(t: Dictionary) -> void:
|
||||
var label := _attack_label(String(t.display), crit, discount)
|
||||
if int(t.rider) > 0:
|
||||
label += "+附加%d(必中)" % int(t.rider)
|
||||
emit_row_cb.call("damage", String(t.source_class), t.origin_cell, int(t.round_index), label, total, payload, String(t.highlight))
|
||||
emit_row_cb.call({"type": "damage", "source_class": String(t.source_class), "origin_cell": t.origin_cell, "highlight": String(t.highlight)}, int(t.round_index), label, total, payload)
|
||||
units.commit_action(t.source_unit)
|
||||
_outcomes[int(t.ticket_id)] = "redeemed"
|
||||
|
||||
@@ -501,8 +501,9 @@ func _resolve_board_typed(t: Dictionary) -> void:
|
||||
cdelta += int(h.delta)
|
||||
var dealt: int = DamageMath.counter_component(int(t.base_value), cdelta, int(db.settlement.damage_floor))
|
||||
units.apply_damage(target, dealt)
|
||||
emit_row_cb.call("damage", String(t.source_class), t.origin_cell, int(t.round_index), "%s → %s" % [String(t.display), String(target.display_name)], dealt,
|
||||
{"target": String(target.display_name), "target_id": String(target.get("enemy_id", "")), "attack_type": String(t.attack_type), "effectiveness": cdelta}, String(t.highlight))
|
||||
emit_row_cb.call({"type": "damage", "source_class": String(t.source_class), "origin_cell": t.origin_cell, "highlight": String(t.highlight)}, int(t.round_index),
|
||||
"%s → %s" % [String(t.display), String(target.display_name)], dealt,
|
||||
{"target": String(target.display_name), "target_id": String(target.get("enemy_id", "")), "attack_type": String(t.attack_type), "effectiveness": cdelta})
|
||||
_outcomes[int(t.ticket_id)] = "redeemed"
|
||||
|
||||
|
||||
@@ -516,8 +517,9 @@ func _resolve_board_blast(t: Dictionary) -> void:
|
||||
var blast_dmg: int = maxi(int(db.settlement.damage_floor), int(t.base_value)) # 伤害下限全来源
|
||||
for u in alive:
|
||||
units.apply_damage(u, blast_dmg) # 恒定表值(下限保底),逐个;受击即死——死亡即不动手
|
||||
emit_row_cb.call("damage", String(t.source_class), t.origin_cell, int(t.round_index), "%s → 全体敌人 ×%d" % [String(t.display), alive.size()], blast_dmg,
|
||||
{"target": "all_enemies", "count": alive.size()}, String(t.highlight))
|
||||
emit_row_cb.call({"type": "damage", "source_class": String(t.source_class), "origin_cell": t.origin_cell, "highlight": String(t.highlight)}, int(t.round_index),
|
||||
"%s → 全体敌人 ×%d" % [String(t.display), alive.size()], blast_dmg,
|
||||
{"target": "all_enemies", "count": alive.size()})
|
||||
_outcomes[int(t.ticket_id)] = "redeemed"
|
||||
|
||||
|
||||
@@ -528,7 +530,7 @@ func _resolve_board_flat_hero(t: Dictionary) -> void:
|
||||
var extra: Dictionary[Variant, Variant] = {"target": "hero"}
|
||||
for k in t.context.keys():
|
||||
extra[k] = t.context[k]
|
||||
emit_row_cb.call("damage", String(t.source_class), t.origin_cell, int(t.round_index), "%s → 主角" % String(t.display), dealt, extra, String(t.highlight))
|
||||
emit_row_cb.call({"type": "damage", "source_class": String(t.source_class), "origin_cell": t.origin_cell, "highlight": String(t.highlight)}, int(t.round_index), "%s → 主角" % String(t.display), dealt, extra)
|
||||
_outcomes[int(t.ticket_id)] = "redeemed"
|
||||
|
||||
|
||||
@@ -625,5 +627,5 @@ func _target_for(rule: String) -> Variant:
|
||||
|
||||
|
||||
func _ineffective_row(t: Dictionary, reason: String) -> void:
|
||||
emit_row_cb.call("reward_ineffective", String(t.source_class), t.origin_cell, int(t.round_index),
|
||||
"%s 未生效(%s)" % [String(t.display), reason], 0, {"batch_key": String(t.context.get("batch_key", ""))}, String(t.highlight))
|
||||
emit_row_cb.call({"type": "reward_ineffective", "source_class": String(t.source_class), "origin_cell": t.origin_cell, "highlight": String(t.highlight)}, int(t.round_index),
|
||||
"%s 未生效(%s)" % [String(t.display), reason], 0, {"batch_key": String(t.context.get("batch_key", ""))})
|
||||
+23
-19
@@ -206,7 +206,7 @@ func grant_routed(snap: Dictionary, polarity: String, att: Dictionary) -> bool:
|
||||
if halve_pct > 0 and polarity == "negative" and source_class == "mine_hit":
|
||||
row = row.duplicate()
|
||||
var half: int = int(ceil(float(int(row.power)) * float(100 - halve_pct) / 100.0))
|
||||
emit_cb.call("effect", source_class, cell, ri, "惊巢反制——危险格挖掘惩罚减半(%d→%d)" % [int(row.power), half], 0, {"effect_id": "nest_counter"}, highlight)
|
||||
emit_cb.call({"type": "effect", "source_class": source_class, "origin_cell": cell}, ri, "惊巢反制——危险格挖掘惩罚减半(%d→%d)" % [int(row.power), half], 0, {"effect_id": "nest_counter"})
|
||||
row.power = half
|
||||
return _dispatch_row(row, att)
|
||||
|
||||
@@ -235,12 +235,13 @@ func _apply_percent_row(row: Dictionary, att: Dictionary) -> bool:
|
||||
eff_mag = int(round(eff_mag * prem_pct / 100.0))
|
||||
var ok: bool = status_effects.apply_effect(eff, _grant_src(att, prem_pct, eff_mag))
|
||||
if ok:
|
||||
emit_cb.call("effect", String(att.get("source_class", "")), att.get("cell", Vector2i(-1, -1)), int(att.get("ri", 0)),
|
||||
String(srow.display_name) + String(att.get("label_suffix", "")), eff_mag,
|
||||
{"effect_id": eff, "pool_value": int(pool_effective_cb.call(eff))}, String(att.get("highlight", "")))
|
||||
emit_cb.call({"type": "effect", "source_class": String(att.get("source_class", "")), "origin_cell": att.get("cell", Vector2i(-1, -1))},
|
||||
int(att.get("ri", 0)), String(srow.display_name) + String(att.get("label_suffix", "")), eff_mag,
|
||||
{"effect_id": eff, "pool_value": int(pool_effective_cb.call(eff))})
|
||||
return true
|
||||
emit_cb.call("reward_ineffective", String(att.get("source_class", "")), att.get("cell", Vector2i(-1, -1)), int(att.get("ri", 0)),
|
||||
"奖励未生效(满层)%s" % String(att.get("label_suffix", "")), 0, {"batch_key": eff}, String(att.get("highlight", "")))
|
||||
emit_cb.call({"type": "reward_ineffective", "source_class": String(att.get("source_class", "")), "origin_cell": att.get("cell", Vector2i(-1, -1)), "highlight": String(att.get("highlight", ""))},
|
||||
int(att.get("ri", 0)), "奖励未生效(满层)%s" % String(att.get("label_suffix", "")), 0,
|
||||
{"batch_key": eff})
|
||||
return false
|
||||
|
||||
|
||||
@@ -260,9 +261,9 @@ func _apply_gp_row(row: Dictionary, att: Dictionary) -> bool:
|
||||
var eff := String(row.effect_id)
|
||||
var delta: int = int(row.power) if eff == "gp_gain" else -int(row.power)
|
||||
escrow += delta
|
||||
emit_cb.call("effect", String(att.get("source_class", "")), att.get("cell", Vector2i(-1, -1)), int(att.get("ri", 0)),
|
||||
"%s %+d%s" % [String(row.display_name), delta, String(att.get("label_suffix", ""))], delta,
|
||||
{"effect_id": eff, "escrow": escrow}, String(att.get("highlight", "")))
|
||||
emit_cb.call({"type": "effect", "source_class": String(att.get("source_class", "")), "origin_cell": att.get("cell", Vector2i(-1, -1)), "highlight": String(att.get("highlight", ""))},
|
||||
int(att.get("ri", 0)), "%s %+d%s" % [String(row.display_name), delta, String(att.get("label_suffix", ""))], delta,
|
||||
{"effect_id": eff, "escrow": escrow})
|
||||
return true
|
||||
|
||||
|
||||
@@ -293,9 +294,9 @@ func _flat_action(row: Dictionary, display: String, att: Dictionary) -> String:
|
||||
var power := int(row.power)
|
||||
var delta: int = power if String(row.get("polarity", "positive")) == "positive" else -power
|
||||
action_economy.queue_ap_delta(delta)
|
||||
emit_cb.call("effect", String(att.get("source_class", "")), att.get("cell", Vector2i(-1, -1)), int(att.get("ri", 0)),
|
||||
"%s(下轮行动点 %+d)" % [display, delta], delta,
|
||||
{"effect_id": "ap_delta", "pool_value": action_economy.pending_ap_delta()}, String(att.get("highlight", "")))
|
||||
emit_cb.call({"type": "effect", "source_class": String(att.get("source_class", "")), "origin_cell": att.get("cell", Vector2i(-1, -1)), "highlight": String(att.get("highlight", ""))},
|
||||
int(att.get("ri", 0)), "%s(下轮行动点 %+d)" % [display, delta], delta,
|
||||
{"effect_id": "ap_delta", "pool_value": action_economy.pending_ap_delta()})
|
||||
return "redeemed"
|
||||
|
||||
|
||||
@@ -325,16 +326,18 @@ func _flat_heal(row: Dictionary, display: String, att: Dictionary, sync: bool) -
|
||||
return _submit_flat_hero(display, power, att, sync)
|
||||
if int(units.hero_unit().get("hp", 0)) <= 0:
|
||||
# 主角已倒下(同轮更早的负向直伤致死)——治疗不再合法作用,按"奖励未生效"留痕
|
||||
emit_cb.call("reward_ineffective", String(att.get("source_class", "")), att.get("cell", Vector2i(-1, -1)), int(att.get("ri", 0)),
|
||||
"%s 未生效(主角已倒下)" % display, 0, {"batch_key": String(row.reward_id)}, String(att.get("highlight", "")))
|
||||
emit_cb.call({"type": "reward_ineffective", "source_class": String(att.get("source_class", "")), "origin_cell": att.get("cell", Vector2i(-1, -1)), "highlight": String(att.get("highlight", ""))},
|
||||
int(att.get("ri", 0)), "%s 未生效(主角已倒下)" % display, 0,
|
||||
{"batch_key": String(row.reward_id)})
|
||||
return "zero"
|
||||
if int(units.hero_unit().hp) >= int(units.hero_unit().max_hp):
|
||||
emit_cb.call("reward_ineffective", String(att.get("source_class", "")), att.get("cell", Vector2i(-1, -1)), int(att.get("ri", 0)),
|
||||
"%s 未生效(满血)" % display, 0, {"batch_key": String(row.reward_id)}, String(att.get("highlight", "")))
|
||||
emit_cb.call({"type": "reward_ineffective", "source_class": String(att.get("source_class", "")), "origin_cell": att.get("cell", Vector2i(-1, -1)), "highlight": String(att.get("highlight", ""))},
|
||||
int(att.get("ri", 0)), "%s 未生效(满血)" % display, 0,
|
||||
{"batch_key": String(row.reward_id)})
|
||||
return "zero"
|
||||
var healed: int = units.heal(units.hero_unit(), power) # clamp 归 战斗结算
|
||||
emit_cb.call("heal", String(att.get("source_class", "")), att.get("cell", Vector2i(-1, -1)), int(att.get("ri", 0)),
|
||||
"%s 回复" % display, healed, {"target": "hero"}, String(att.get("highlight", "")))
|
||||
emit_cb.call({"type": "heal", "source_class": String(att.get("source_class", "")), "origin_cell": att.get("cell", Vector2i(-1, -1)), "highlight": String(att.get("highlight", ""))},
|
||||
int(att.get("ri", 0)), "%s 回复" % display, healed, {"target": "hero"})
|
||||
return "redeemed"
|
||||
|
||||
|
||||
@@ -375,7 +378,8 @@ func grant_clear_reward(ri: int, skip_on_refresh: bool, perfect: bool) -> void:
|
||||
if perfect and first_ok:
|
||||
second_ok = _grant_clear_identity(snap, ri, "(完美第二份)")
|
||||
if perfect and first_ok:
|
||||
emit_cb.call("highlight", "clear_reward", Vector2i(-1, -1), ri, "完美封印!清图奖励 ×2", (2 if second_ok else 1), {}, "perfect_sweep")
|
||||
emit_cb.call({"type": "highlight", "source_class": "clear_reward", "origin_cell": Vector2i(-1, -1), "highlight": "perfect_sweep"},
|
||||
ri, "完美封印!清图奖励 ×2", (2 if second_ok else 1), {})
|
||||
|
||||
|
||||
# 清图奖励 = 整图独立对象(不属于任何一格):兑付形态按其 cell_type 的正向字典形态走,
|
||||
|
||||
@@ -74,7 +74,7 @@ func run(ri: int) -> void:
|
||||
# 落点由状态行 target_side 路由(buff → 敌方侧 / debuff → 主角侧——容器零改动,proposal-status-vocabulary §1)。
|
||||
var dur: int = int(srow.get("buff_duration", 1))
|
||||
oc._status_effects.apply_effect(String(srow.buff_ref), {"source_class": "enemy_action", "origin_cell": Vector2i(-1, -1), "round_index": ri}, dur)
|
||||
oc._emit_row("cast", "enemy_action", Vector2i(-1, -1), ri, "%s 施放 %s" % [u.display_name, String(srow.display_name)], 0, {"skill_id": skill_id, "effect_id": String(srow.buff_ref), "duration": dur})
|
||||
oc._emit_row({"type": "cast", "source_class": "enemy_action", "origin_cell": Vector2i(-1, -1)}, ri, "%s 施放 %s" % [u.display_name, String(srow.display_name)], 0, {"skill_id": skill_id, "effect_id": String(srow.buff_ref), "duration": dur})
|
||||
oc._enemy_system.commit_action(u)
|
||||
continue
|
||||
# 伤害技(含普攻——倍率恒 1)= 敌方伤害技单据:基线 = round(atk×倍率)、rider 预计算归开单侧
|
||||
@@ -95,8 +95,8 @@ func run(ri: int) -> void:
|
||||
# 补提交后若地图直伤反杀主角,控制器 _halted 作废余单——战斗已败,胜负归判定段)
|
||||
if int(oc._enemy_system.hero_unit().get("hp", 0)) <= 0:
|
||||
for t in (reward_grant.defer["queue"] as Array):
|
||||
oc._emit_row("reward_ineffective", String(t.get("source_class", "")), t.get("origin_cell", Vector2i(-1, -1)), ri,
|
||||
"%s 未生效(主角已倒下)" % String(t.get("display", "")), 0, {"forfeit": "hero_down"}, "")
|
||||
oc._emit_row({"type": "reward_ineffective", "source_class": String(t.get("source_class", "")), "origin_cell": t.get("origin_cell", Vector2i(-1, -1))}, ri,
|
||||
"%s 未生效(主角已倒下)" % String(t.get("display", "")), 0, {"forfeit": "hero_down"})
|
||||
else:
|
||||
for t in (reward_grant.defer["queue"] as Array):
|
||||
oc._damage.submit(t)
|
||||
|
||||
@@ -13,4 +13,4 @@ func _init(host) -> void:
|
||||
|
||||
func run(ri: int) -> void:
|
||||
var k: int = oc._status_effects.decay_round()
|
||||
oc._emit_row("decay", "", Vector2i(-1, -1), ri, "状态衰减", k, {}, "")
|
||||
oc._emit_row({"type": "decay", "source_class": "", "origin_cell": Vector2i(-1, -1)}, ri, "状态衰减", k, {})
|
||||
@@ -35,7 +35,7 @@ func run(ri: int) -> void:
|
||||
if bool(evt.get("dug_under_seal", false)):
|
||||
# 封印不可逆四情况④:插安全格再挖开 = 零惩罚零奖励——正向兑付
|
||||
# 作废(归因行在案);标空判定已随封印擦除自然缺席(被挖的封印不在轮末在场基线)。
|
||||
oc._emit_row("reward_ineffective", "safe_cell", evt.cell, ri, "奖励作废(封印格直接挖开——零惩罚零奖励)", 0, {}, "")
|
||||
oc._emit_row({"type": "reward_ineffective", "source_class": "safe_cell", "origin_cell": evt.cell}, ri, "奖励作废(封印格直接挖开——零惩罚零奖励)", 0, {})
|
||||
continue
|
||||
# dig_reveal 发射+施加(内容数据驱动扩展批 T3——桶序固定承载等价:
|
||||
# 乘区收集 → 正向兑付 → 计数器 → 追加产出(深挖)→ 奥术矢。
|
||||
@@ -220,7 +220,7 @@ func run(ri: int) -> void:
|
||||
for h in re_fx:
|
||||
if String(h.get("kind", "")) == "grant_gp":
|
||||
reward_grant.escrow += int(h.get("value", 0))
|
||||
oc._emit_row("effect", "fx_grant_gp", Vector2i(-1, -1), ri, "专长·GP 入账 +%d" % int(h.get("value", 0)), int(h.get("value", 0)), {"effect_id": "fx_grant_gp", "escrow": reward_grant.escrow}, "")
|
||||
oc._emit_row({"type": "effect", "source_class": "fx_grant_gp", "origin_cell": Vector2i(-1, -1)}, ri, "专长·GP 入账 +%d" % int(h.get("value", 0)), int(h.get("value", 0)), {"effect_id": "fx_grant_gp", "escrow": reward_grant.escrow})
|
||||
elif String(h.get("kind", "")) == "grant_ap" and String(h.get("mode", "")) == "queue":
|
||||
oc.action_economy.queue_ap_delta(int(h.get("value", 0)))
|
||||
# 转存率入参(迭代 22 秘宝批 T3——储备罐 rap_transfer_mult magnitude=目标转存率百分比 125=1.25:1;
|
||||
@@ -273,7 +273,7 @@ func _judge_seal(evt: Dictionary, ri: int) -> void:
|
||||
"remove_mines":
|
||||
var removed: Array[Variant] = oc.minesweeper.remove_mines_in([cell])
|
||||
if not removed.is_empty():
|
||||
oc._emit_row("extended", "fx_seal_smith", cell, ri, "封印匠·危险格中立移除", 0, {"effect_id": "mine_neutralized"}, "")
|
||||
oc._emit_row({"type": "extended", "source_class": "fx_seal_smith", "origin_cell": cell}, ri, "封印匠·危险格中立移除", 0, {"effect_id": "mine_neutralized"})
|
||||
"preview_area":
|
||||
var board_p: BoardData = oc.minesweeper.current_board()
|
||||
var types: Array[Variant] = []
|
||||
@@ -283,7 +283,7 @@ func _judge_seal(evt: Dictionary, ri: int) -> void:
|
||||
types.append(String((n_snap as Dictionary).get("cell_type", "")))
|
||||
if oc.treasure_inventory != null and not types.is_empty():
|
||||
oc.treasure_inventory.apply_overlay("reward_preview", board_p.neighbors8(cell))
|
||||
oc._emit_row("extended", "fx_warning_beacon", cell, ri, "示警烟火·周围资源类型", 0, {"effect_id": "fx_preview_area", "types": types}, "")
|
||||
oc._emit_row({"type": "extended", "source_class": "fx_warning_beacon", "origin_cell": cell}, ri, "示警烟火·周围资源类型", 0, {"effect_id": "fx_preview_area", "types": types})
|
||||
# 雷区图谱(封印流——标中雷额外获得一份格面资源:封印开始产出对标开格;
|
||||
# 复用 grant 单点,额外兑付走同一 _grant_routed 正向路由)
|
||||
for h in fh_hits:
|
||||
@@ -311,10 +311,10 @@ func _judge_seal(evt: Dictionary, ri: int) -> void:
|
||||
# _perfect_forgiven,绝不回写 _perfect_hit_false=契约 2 计数纯净;发射时机与消费点零改动=契约 3)
|
||||
oc._perfect_forgiven += 1
|
||||
_apply_counter_hit(h) # 豁免同时记账(insurance_used battle 域——下次标错 when lt 1 不再命中)
|
||||
oc._emit_row("extended", "seal_miss", cell, ri, "完美保险·标错豁免", 0,
|
||||
{"effect_id": "insurance_used", "forgiven": oc._perfect_forgiven}, "")
|
||||
oc._emit_row({"type": "extended", "source_class": "seal_miss", "origin_cell": cell}, ri, "完美保险·标错豁免", 0,
|
||||
{"effect_id": "insurance_used", "forgiven": oc._perfect_forgiven})
|
||||
var hl := "perfect_broken" if oc._perfect_hit_false - oc._perfect_forgiven == 1 else ""
|
||||
oc._emit_row("seal_miss", "seal_miss", cell, ri, "封印未中", 0, {}, hl)
|
||||
oc._emit_row({"type": "seal_miss", "source_class": "seal_miss", "origin_cell": cell, "highlight": hl}, ri, "封印未中", 0, {})
|
||||
var snap = oc._reward_economy.grant(cell)
|
||||
if not (snap is Dictionary):
|
||||
return
|
||||
|
||||
@@ -21,13 +21,13 @@ func run(ri: int) -> String:
|
||||
verdict = "victory"
|
||||
oc._emit_settlement_snapshot(verdict) # 全量状态快照(despawn 前组装——终局含 result)
|
||||
if verdict == "defeat":
|
||||
oc._emit_row("verdict", "", Vector2i(-1, -1), ri, "战斗失败", 0, {}, "")
|
||||
oc._emit_row({"type": "verdict", "source_class": "", "origin_cell": Vector2i(-1, -1)}, ri, "战斗失败", 0, {})
|
||||
oc._battle_over("defeat")
|
||||
return verdict
|
||||
if verdict == "victory":
|
||||
oc._emit_row("verdict", "", Vector2i(-1, -1), ri, "战斗胜利", 1, {}, "")
|
||||
oc._emit_row({"type": "verdict", "source_class": "", "origin_cell": Vector2i(-1, -1)}, ri, "战斗胜利", 1, {})
|
||||
oc._battle_over("victory")
|
||||
return verdict
|
||||
oc._emit_row("verdict", "", Vector2i(-1, -1), ri, "下一轮次", 0, {}, "")
|
||||
oc._emit_row({"type": "verdict", "source_class": "", "origin_cell": Vector2i(-1, -1)}, ri, "下一轮次", 0, {})
|
||||
oc._exit_distribution()
|
||||
return verdict
|
||||
@@ -40,9 +40,9 @@ func _init() -> void:
|
||||
|
||||
# ---- 夹具 ----
|
||||
|
||||
func _on_row(type, source_class, origin_cell, ri, display, value, extra, highlight) -> void:
|
||||
_rows.append({"type": type, "source_class": source_class, "origin_cell": origin_cell,
|
||||
"round_index": ri, "display": display, "value": value, "extra": extra, "highlight": highlight})
|
||||
func _on_row(head, ri, display, value, extra) -> void:
|
||||
_rows.append({"type": head.get("type", ""), "source_class": head.get("source_class", ""), "origin_cell": head.get("origin_cell", Vector2i(-1, -1)),
|
||||
"round_index": ri, "display": display, "value": value, "extra": extra, "highlight": head.get("highlight", "")})
|
||||
|
||||
|
||||
func _harness(db):
|
||||
|
||||
@@ -23,13 +23,13 @@ func _init() -> void:
|
||||
oc.step_row.connect(func(row): hits.append(row))
|
||||
# 违约直调(缺 effect_id 载荷——行协议违约):debug 下 a2 校验 assert 炸中断 _emit_row 本体,
|
||||
# 控制流回到本函数(headless assert 语义=中断当前函数不中止运行);release 下 return 零发射同效
|
||||
oc._emit_row("extended", "fx_reveal", Vector2i.ZERO, 1, "负路径", 0, {}, "")
|
||||
oc._emit_row({"type": "extended", "source_class": "fx_reveal", "origin_cell": Vector2i.ZERO}, 1, "负路径", 0, {})
|
||||
if not hits.is_empty():
|
||||
push_error("[extended_row_violation_test] 违约行被发射——a2 校验失守(回归)")
|
||||
quit(2)
|
||||
return
|
||||
# 对照组:合法扩展行(带 effect_id)照常发射——校验只拦违约形态
|
||||
oc._emit_row("extended", "fx_reveal", Vector2i.ZERO, 1, "合法对照", 0, {"effect_id": "fx_reveal"}, "")
|
||||
oc._emit_row({"type": "extended", "source_class": "fx_reveal", "origin_cell": Vector2i.ZERO}, 1, "合法对照", 0, {"effect_id": "fx_reveal"})
|
||||
if hits.size() != 1 or String(hits[0].get("type", "")) != "extended":
|
||||
push_error("[extended_row_violation_test] 合法扩展行未发射——校验误拦(回归)")
|
||||
quit(2)
|
||||
|
||||
Reference in new issue
Block a user