refactor(代码术语): 卡① flag→seal 全族换代+game/ 禁用词清扫(迭代 31 卡①)

标识符面:flag_*→seal_ 族(含 Godot 信号 flag_judged→seal_judged 与连接/回调同名、
FACTS 两键 flags_count→seals_count/dug_under_flag→dug_under_seal 全链、
.tres 键名与 schema .gd 同批同步);显式豁免零改动=size_flags_*(Godot 布局属性)+_fatal_flag。
中文面:金币→GP(金币袋→GP 袋)/插旗→封印/雷格→危险格/惩罚格→危险格/旗→封印·封印印记族,
game/ 内 *.gd/.tres/.md/.json 四类禁用词与 flag 双双零命中(V1/V4)。
守卫字面量不动(被检查的禁字不是使用):smoke_all V21 零「雷/旗」检查面、
ui_font_asset_test 字形集攻/雷/旗/封 保持原断言逻辑。
代拍板:板面单字槽 glyph「旗」→「封」(禁字纪律+单字位宽,消费面四处同名随动)。
素材换代(2026-10-18 用户裁案甲):assets/art/board/flag_seal.png→seal_mark.png(素材不入 git,
盘上改名+导入重刷),代码两处 res:// 字符串+COL_FLAG/CURSOR_FLAG 常量同步,
闸件四处随批(run_all_gates.sh/render_gate.sh/golden_baseline_refresh.sh/path_check.py)。
anchor.json 键名手改,重锚与改前改后 diff 随本卡收口(同裁案甲)。

基线读数(改前,主区 ed2dfaf6):冒烟 1151 检查 0 失败/单元 34 套全绿/探针 --click
存量红仅 screen_flow_probe(user:// 断点污染在册已知项)。
改后同批读数(本 worktree):冒烟 1151 检查 0 失败/探针 --click 同一存量红、其余全绿。
This commit is contained in:
DKChan committed 2026-10-19 02:52:38 +08:00
1 parent ed2dfaf66f
commit 32fdf957fd
79 files changed
+899 -899

No files matched your search

+6 -6
View File
@@ -34,7 +34,7 @@ var last_round_end: Dictionary[Variant, Variant]:
var _settlement_consumed := false # AC-14 门:notify_settlement_done 消费后才受理 round_start
var _next_round_mode := "" # 调试观测量(同板续轮 / 换新图)
var _pending_ap_delta := 0 # 下轮行动点修正排队(格面资源八型批 2026-09-20——action 型 ±1 排队,轮开始兑现后清零)
# 蓄力流运行态(迭代 12 T5 → 内容数据驱动扩展批 T3 换代:旧 feat_ap_carry/feat_dig_extra 注入旗
# 蓄力流运行态(迭代 12 T5 → 内容数据驱动扩展批 T3 换代:旧 feat_ap_carry/feat_dig_extra 注入封印
# 退役——转蓄力=round_end grant_ap(mode:transfer) 订阅(finish_round_stop 定夺)、深挖对价=dig_paid
# counter_spend 订阅(request_dig 施加面);charge_layers=蓄力层账本)
var _charge_layers := 0
@@ -43,7 +43,7 @@ var charge_layers: int:
# 效果引擎(内容数据驱动扩展批 T1——dig_paid/charge_full 发射面;round_end 转存决策面)
var _fx: EffectEngine = null
var _last_stop_ap := 0 # 停手侧剩余 AP 记账(转存决策移至 round_end 发射点——grant_ap(transfer) 订阅定夺)
var _ap_exhausted_last := false # 上轮 AP 耗尽旗(迭代 20 T1——一滴不剩 round_start when 面)
var _ap_exhausted_last := false # 上轮 AP 耗尽封印(迭代 20 T1——一滴不剩 round_start when 面)
func set_fx(fx: EffectEngine) -> void:
@@ -68,8 +68,8 @@ func _cost(kind: String) -> int:
match kind:
"dig":
return ae.dig_ap_cost # 基础挖格成本恒表值(钻头对价挂付费开掘侧——2026-10-04 缺陷修复)
"flag_place":
return ae.flag_place_ap_cost
"seal_place":
return ae.seal_place_ap_cost
"move":
return ae.move_ap_cost
"chain":
@@ -78,7 +78,7 @@ func _cost(kind: String) -> int:
return 0
# 通用扣减(排雷玩法 用于旗动作;挖握走 request_dig——cleared 仲裁需要 clears_map 参数)
# 通用扣减(排雷玩法 用于封印动作;挖握走 request_dig——cleared 仲裁需要 clears_map 参数)
func try_spend(action_kind: String) -> bool:
if _phase != Phase.ACTIVE:
_reject("phase")
@@ -156,7 +156,7 @@ func _spend_and_arbitrate(action_kind: String, clears_map: bool) -> bool:
# 整备军备改写(迭代 12 T5——feat_ap_carry=true 时剩余 AP 转蓄力层〔上限 3〕替代 RAP 转存:
# 「不动也是行动」——cleared 作废口径改写的停手侧承载;cleared 分支 AP 恒清零维持既有作废语义)。
# 内容数据驱动扩展批 T1:本方法改记账制——剩余 AP 存 _last_stop_ap 后清零,转存决策移至
# round_end 发射点 finish_round_stop(grant_ap(mode:transfer) 订阅定夺;双轨期旧旗 feat_ap_carry
# round_end 发射点 finish_round_stop(grant_ap(mode:transfer) 订阅定夺;双轨期旧封印 feat_ap_carry
# 并行生效,行为与迁移前逐位一致)。
func request_stop() -> bool:
if _phase != Phase.ACTIVE:
+44 -44
View File
@@ -15,7 +15,7 @@ signal settlement_snapshot(snap: Dictionary) # 每结算末尾的全量状态
# 明细流水行型协议(战斗结算 Core Rule;9 类——2026-09-20 伏击单轮上限退役,溢出未生效行 overflow 随之下线;
# 2026-09-15 格面资源批由 15 类收拢,清单改动须同步 battle_log_view 的 _format_row 与本文档头注):
# 效果行 effect · 伤害行 damage · 治疗行 heal ·
# 奖励未生效行 reward_ineffective · 标旗未中行 flag_miss · 施放行 cast(敌人技能池批由蓄力行 charge 改型)· 衰减行 decay ·
# 奖励未生效行 reward_ineffective · 封印未中行 seal_miss · 施放行 cast(敌人技能池批由蓄力行 charge 改型)· 衰减行 decay ·
# 判定行 verdict · 高光行 highlight
# 行来源断言豁免集合(行型层)——判定段 / 衰减段本就无棋盘来源,勿一刀切误炸
const SOURCE_EXEMPT_TYPES := ["decay", "verdict"]
@@ -48,7 +48,7 @@ var battle_result := "" # "" / "victory" / "defeat" / "fatal_
var escrow := 0 # 战斗内 GP 缓冲(战斗域中间态——begin_battle 清零;gp_gain/gp_loss 兑付累记;随 gp_dropped 上抛即弃,不入钱包)
var _facts_reveals: Array[Dictionary] = []
var _facts_flags: Array[Dictionary] = []
var _facts_seals: Array[Dictionary] = []
var _facts_path := ""
var _settling := false
var _defer_board_damage := false # 案C 暂存闸(2026-10-06 用户裁:地图奖励伤害兑付迁战斗段末——怪物还手后)
@@ -56,7 +56,7 @@ var _deferred_board: Array[Dictionary] = [] # 授予段暂存伤害单据(
# 结算五段 Segment(迭代 27 T2——P1-1 拆段;段边界=审计五段〔裁点③甲〕,段序骨架冻结于
# SettlementTable.segment_order 语义:授予→增益/减益→战斗→衰减→判定。本类退为段序编排+
# 行发射/开单原语单点——段体逻辑归 game/core/settlement/ 五类)
var _grant_seg = null # GrantSegment(①授予段——含旗判与 counter/grant 助手单点)
var _grant_seg = null # GrantSegment(①授予段——含封印判与 counter/grant 助手单点)
var _aura_seg = null # AuraSegment(②增益/减益段)
var _combat_seg = null # CombatSegment(③战斗段——原 _combat_segment 体)
var _decay_seg = null # DecaySegment(④衰减段)
@@ -64,7 +64,7 @@ var _verdict_seg = null # VerdictSegment(⑤判定段——返回裁定,
var _refresh_used_this_round := 0 # 每轮刷新计数(Active 相位合计,兜底上限 = ActionEconomyTable.refresh_cap_per_round)
var _board_seq := 0 # 板序号(本战斗第几张板——快照归因用)
var _board_from_midround_refresh := false # 装板 provenance:轮内刷新换来的图 = true(清图奖励守卫——刷新图不发清图奖励)
var _flag_consumed := {} # 兑付资格以雷格实例为键(板级;换图重置)
var _seal_consumed := {} # 兑付资格以危险格实例为键(板级;换图重置)
# 同调装配运行态(迭代 11 第①批 T5——begin_battle 经 enc.attunement 注入;off=空集全部效果零扰动)
var attunement: Array[Variant] = [] # 装备中同调件 item_id 集
var _ledger: RunLedger = null # 共享四本账(迭代 27 T3——begin_battle 经 enc["ledger"] 持引用;缺省新账=调试面)
@@ -78,12 +78,12 @@ var _fx_bonus_delta := {"atk": 0, "max_hp": 0, "armor_class": 0, "ap": 0} #
var _fx_new_resource_types := {} # 本战已见资源类型集(地质学家 fact new_resource_type 判定——battle 域)
var _fx_pending_reveals: Array[Dictionary] = [] # 揭示效果待兑队列(见血开窍/斩首学者/地质学家/跳格采掘——授予段末统一兑付,防轮 facts 迭代中追加重入)
var _fx_revealing := false # fx 揭示兑付中标记(_on_cell_revealed 缓冲旁路——fx 揭示的奖励由兑付点直接入账,防二次缓冲二次兑付)
var _fx_insurance := 0 # 完美保险余量(迭代 20 T4——本战标错豁免次数;消费于 flag_miss 首见判定)
var _fx_insurance := 0 # 完美保险余量(迭代 20 T4——本战标错豁免次数;消费于 seal_miss 首见判定)
# 特性运行态(内容数据驱动扩展批 T3——relic 持有集经 enc.feats 注入:效果行为=数据行 triggers 经
# 效果引擎分发;魔矢每战首次计数 battle 域。空集=零扰动)
var _feat_first_dig_used := false
# 效果引擎(内容数据驱动扩展批 T1——发布订阅解释器;begin_battle 从持有集/装备集重建订阅。
# T3 起施加面全量切换:发射点 dispatch 返回命中,发射点按组合规则施加——旧 feat_flags 旗注入消亡)
# T3 起施加面全量切换:发射点 dispatch 返回命中,发射点按组合规则施加——旧 feat_seals 封印注入消亡)
var _fx: EffectEngine
@@ -133,7 +133,7 @@ func _fx_enqueue_reveal() -> void:
# 揭示效果兑付(授予段末统一——fx_reveal 封闭流从未揭示安全格掷 1 格全揭示;
# 信息红线=只揭真格〔雷格不入候选〕;空候选=零兑付留归因行;揭示奖励经 _grant_routed
# 信息红线=只揭真格〔危险格不入候选〕;空候选=零兑付留归因行;揭示奖励经 _grant_routed
# 正向路由直接入账——_fx_revealing 旁路 _on_cell_revealed 缓冲防二次兑付)
func _flush_pending_reveals(ri: int) -> void:
if _fx_pending_reveals.is_empty():
@@ -177,9 +177,9 @@ func _fx_fact(fact: String):
return round_index
"battle_index":
return int(encounter.get("battle_index", 1)) # 关卡域战斗序(1 基——level_runner enc 携带;开局姿态窗条件面)
"flags_count":
"seals_count":
var hits_n := 0
for fevt in _facts_flags:
for fevt in _facts_seals:
if bool(fevt.get("hit", false)):
hits_n += 1
return hits_n
@@ -259,10 +259,10 @@ func attune_convert_state() -> Dictionary:
"display_name": String(row.get("display_name", "")), "item_id": String(row.get("item_id", ""))}
# 特性求值(内容数据驱动扩展批 T3——_feat_flags_for 挂点求值退役:效果行为=数据行 triggers 订阅,
# 效果引擎 dispatch 分发;旧 23 hook 枚举与旗注入消亡。流派档位质变=FeatsEngine 纯函数求值
# 特性求值(内容数据驱动扩展批 T3——_feat_seals_for 挂点求值退役:效果行为=数据行 triggers 订阅,
# 效果引擎 dispatch 分发;旧 23 hook 枚举与封印注入消亡。流派档位质变=FeatsEngine 纯函数求值
# (档位机制本身系引擎基建非内容效果),两运行态注伤害控制器:档一 rider+1 / 档二 rider 可暴击)
var _flag_miss_seen := {} # 标旗未中行去重(每安全格实例一次)
var _seal_miss_seen := {} # 封印未中行去重(每安全格实例一次)
var _perfect_hit_true := 0 # 板级完美排雷计数(install 重置)
var _perfect_hit_false := 0
var _hero_atk := 0 # 主角攻击基值快照(encounter.hero_atk;E4 收口——表值运行期不可变)
@@ -284,7 +284,7 @@ func _init(db_ref: ConfigDb, rng_ref: RngServiceCore) -> void:
action_economy.set_fx(_fx) # dig_paid/charge_full 发射面
_reward_economy.set_fx(_fx) # reward_roll 发射面
minesweeper.cell_revealed.connect(_on_cell_revealed)
minesweeper.flag_judged.connect(_on_flag_judged)
minesweeper.seal_judged.connect(_on_seal_judged)
minesweeper.round_end_board.connect(_on_round_end)
action_economy.round_end_stopped.connect(_on_round_end)
# 结算五段接线(迭代 27 T2——段持宿主引用;信号连接纪律不涉段〔段零信号连接〕)
@@ -391,11 +391,11 @@ func begin_battle(enc: Dictionary) -> bool:
# 迭代 20 T6——adventure 域计数器从共享引用回灌〔跨战持久〕)
_fx.rebuild(feat_ids, attunement, tree_rules)
_fx.counters_seed(_ledger.counters_seed_snapshot())
# 秘宝常时参数注入(迭代 22 秘宝批 T3——装配集 kind 分发:远程镐挖距放宽/信标网络旗可达豁免;
# 秘宝常时参数注入(迭代 22 秘宝批 T3——装配集 kind 分发:远程镐挖距放宽/信标网络封印可达豁免;
# 缺省 0/false=零装配零扰动。注入先于首板 install 已不成立〔板在早段〕——本批板生成不读两参数,
# 参数只影响挖掘/插棋动作判据,时序安全)
minesweeper.dig_range_bonus = attune_param_sum("dig_range_bonus")
minesweeper.flag_range_free = attune_param_sum("flag_range_free") > 0
minesweeper.seal_range_free = attune_param_sum("seal_range_free") > 0
# 充能重置周期(迭代 22 秘宝批 T3——battle=每战回满/level=每关首战回满〔battle_index==1〕;
# adventure 周期维持冒险开始 _seed_attune_charges 单次)
_reseed_attune_charges("battle")
@@ -449,7 +449,7 @@ func _install_new_board(during_round := false) -> bool:
if not res.accepted:
_fatal("排雷玩法 install 拒绝(不变式违例): %s" % str(res))
return false
# 地图战斗资源 义务⑥:roll_board 恰一次(install 后、挖掘前);资格格 = 全部非封锁格(安全格 + 雷格,雷安同池)
# 地图战斗资源 义务⑥:roll_board 恰一次(install 后、挖掘前);资格格 = 全部非封锁格(安全格 + 危险格,雷安同池)
var r0 := {}
for c in board.compute_r0():
r0[c] = true
@@ -466,8 +466,8 @@ func _install_new_board(during_round := false) -> bool:
# 地形变异 布置段(2026-09-19 用户拍板棋盘设置顺序第 4 步:生成棋盘 → 打开状态初始化 →
# 战斗资源格设置 → 变异格设置)——布置在初始展开与资源格之后,变异天然不落已开区
_place_variants(board)
_flag_consumed.clear() # 换图新雷 = 新实例,资格自然重置(行动经济 Rule 8)
_flag_miss_seen.clear()
_seal_consumed.clear() # 换图新雷 = 新实例,资格自然重置(行动经济 Rule 8)
_seal_miss_seen.clear()
_perfect_hit_true = 0 # 板级完美计数随 install 重置
_perfect_hit_false = 0
_board_from_midround_refresh = during_round # 轮内刷新装板 = 刷新图(清图奖励不发);常规换图/开战装板 = false
@@ -498,7 +498,7 @@ func _resolve_global_variant(board: BoardData) -> void:
# 候选池门控(unlock_level ≤ 关卡序号 + board_types 与本地形交集,∅ 通配)经 config_db.variant_pool;
# 池空 = 合法态,静默零布置(框架 P0 口径不变)。零特判:本段只写格子变异数据快照,不改扫雷规则本体。
# 候选格域 = 安全格 − 初始展开区(伤害型 placement=closed_only 的结构保障——布置发生在初始展开之后,
# 已开格天然排除);雷格不吃变异(挖雷一层惩罚 + 变异伤害双层叠罚污染逐行归因,首版排除——provisional)。
# 已开格天然排除);危险格不吃变异(挖雷一层惩罚 + 变异伤害双层叠罚污染逐行归因,首版排除——provisional)。
# 每图至多一种类型(候选池内均匀选一,混型布置归因复杂留待后续批);数量 = round(候选池格数×percent) +
# (关卡序号−1)×per_level_bonus——基数为候选池而非总格数(2026-09-21 用户拍板基数修正批:初始展开
# 占全板 56–67%,总格数基数使有效密度膨胀 4–6 倍、L1 小板极端全池覆盖;percent 语义 = 占可挖格比例,
@@ -570,7 +570,7 @@ func _adjacent_to_packs(board: BoardData, placed: Dictionary, c: Vector2i, own_p
func _on_cell_revealed(evt: Dictionary) -> void:
# 轮 facts 缓冲不设相位守卫——轮末最后一挖的揭示事件在 request_dig 翻相之后才到(事件序:
# cell_revealed 批 → flag_judged 批 → round_end),phase==Active 守卫会丢最后一挖的事实;
# cell_revealed 批 → seal_judged 批 → round_end),phase==Active 守卫会丢最后一挖的事实;
# initial 通道事件(install 初始展开——封锁格)resource=false,进入授予段被资格检查自然跳过。
# fx_reveal 通道事件旁路缓冲(迭代 20 T1——专长揭示的奖励由 _flush_pending_reveals 兑付点
# 直接入账,此处缓冲会导致二次兑付)。
@@ -589,7 +589,7 @@ func _on_cell_revealed(evt: Dictionary) -> void:
action_economy.grant_ap(int(cs_row.get("magnitude", 1)))
if bool(evt.get("resource", false)):
# 身份快照随缓冲捕获(批次隔离——轮内刷新后 mapping 已换批,授予必须用缓冲期身份,
# 防旧图已挖格在轮末错拿新图同坐标身份)。雷格同样携带(雷安同池——负向兑付的来源格)
# 防旧图已挖格在轮末错拿新图同坐标身份)。危险格同样携带(雷安同池——负向兑付的来源格)
var snap = _reward_economy.query_full_snapshot(evt.cell)
if snap != null:
evt = evt.duplicate()
@@ -597,8 +597,8 @@ func _on_cell_revealed(evt: Dictionary) -> void:
_facts_reveals.append(evt)
func _on_flag_judged(evt: Dictionary) -> void:
_facts_flags.append(evt)
func _on_seal_judged(evt: Dictionary) -> void:
_facts_seals.append(evt)
func _on_round_end(_payload: Dictionary) -> void:
@@ -664,7 +664,7 @@ func _harmful_variants(vids: Array) -> Array:
return out
# 邻接伤害型变异收集(terrain_kind fact 取数面——flag_hit 发射点用:标中旗相邻八格的伤害型变异集;
# 邻接伤害型变异收集(terrain_kind fact 取数面——seal_hit 发射点用:封印命中相邻八格的伤害型变异集;
# 🔴 R4 甲永不对冲边界=仅限伤害型,信息型〔hide_number 族〕不入集)
func _harmful_adjacent(cell: Vector2i) -> Array:
var out: Array[Variant] = []
@@ -685,7 +685,7 @@ func _hits_of(hits: Array, kind: String) -> Array:
return out
# 「动作 × 格型」矩阵的执行面:snap = 缓冲期冻结身份快照(cell_type = 该格装板期摇定的类型身份),
# polarity 由动作决定(开安全格/标中旗 = positive,开雷格/标错旗 = negative),
# polarity 由动作决定(开安全格/封印命中 = positive,开危险格/标错封印 = negative),
# 兑付行 = 奖励字典(类型, 极性)行。返回 true = percent 行实际生效(flat 行恒 false——兑付结果由行协议承载)。
# ---- 秘宝(use_treasure 四步受理编排保留给同调主动件复用——迭代 11 第①批 T7:过渡授予路由
# _grant_treasure 随掉落/停点渠道退役拆除;能力件注入归 LevelRunner.set_attune_charges 骑库存面)----
@@ -730,23 +730,23 @@ func use_treasure(handle: int, target: Vector2i) -> Dictionary:
return {"ok": false, "reason": "行动点加量被拒(相位防御)"}
if kinds.has("safe_dig"):
# 天眼安全格直开(迭代 22 秘宝批 T3——C5 裁全额兑付:fx_reveal 通道复用=免 AP 零相位、
# 连锁与格面资源与普通挖格同链;危险格仅真视显示不移除=契约 3;有旗格跳过=旗=玩家承诺不代开)
# 连锁与格面资源与普通挖格同链;危险格仅真视显示不移除=契约 3;有封印格跳过=封印=玩家承诺不代开)
var opened_any := false
for c_v in v.resolved:
var c := Vector2i(c_v)
if board.has_flag(c):
if board.has_seal(c):
continue
if board.fx_reveal(c):
opened_any = true
if not opened_any:
return {"ok": false, "reason": "天眼:窗口内安全格均已开(无可开格)"}
if kinds.has("cell_removal"):
# 爆破工兵包兑付(迭代 13 T8/D6——中立移除雷格:未揭示/无旗/是雷过滤归排雷玩法;
# P1 甲邻接数字冻结零重算、清图分母零涉(雷格不入分母);空集拒绝零消耗——
# 爆破工兵包兑付(迭代 13 T8/D6——中立移除危险格:未揭示/无封印/是雷过滤归排雷玩法;
# P1 甲邻接数字冻结零重算、清图分母零涉(危险格不入分母);空集拒绝零消耗——
# consume 之前 return(拒绝面注记扩「③内零消耗分支」),拒绝 reason 可呈现)
var blasted: Array[Variant] = board.remove_mines_in(v.resolved)
if blasted.is_empty():
return {"ok": false, "reason": "爆破工兵包:3×3 区域内无可移除雷格(须未揭示且未插旗)"}
return {"ok": false, "reason": "爆破工兵包:3×3 区域内无可移除危险格(须未揭示且未封印)"}
var rec: Dictionary[Variant, Variant] = treasure_inventory.consume(handle)
# 同调主动件充能扣减(迭代 11 第①批 T6——共享账本直改:adventure/level/settlement 同一字典引用)
if not rec.is_empty() and String(rec.get("source_class", "")) == "attunement":
@@ -773,14 +773,14 @@ func _grant_routed(snap: Dictionary, polarity: String, source_class: String, cel
return false
if polarity == "positive":
row["power"] = int(snap.get("power", int(row.power))) # 区间行快照 power(身份批 roll 值)覆盖表值——授予期零随机口径(E14)
# 标中兑付溢价(迭代14②判式换代批 T4——条款3 条件二旗溢价形态 a,2026-10-04 用户裁 P3):
# δ 读 settlement.flag_hit_premium(percent 域 125=×1.25 provisional);100=无溢价直通零扰动。
# 兑付公式一处=本块:只乘标中旗正向(flat power 乘后入单据/治疗/排队;percent 幅度经
# 标中兑付溢价(迭代14②判式换代批 T4——条款3 条件二封印溢价形态 a,2026-10-04 用户裁 P3):
# δ 读 settlement.seal_hit_premium(percent 域 125=×1.25 provisional);100=无溢价直通零扰动。
# 兑付公式一处=本块:只乘封印命中正向(flat power 乘后入单据/治疗/排队;percent 幅度经
# premium_pct 贯穿 _apply_percent_row 应用与呈现同乘)——标空/挖格/清图不乘,雷安同池冻结不动。
# 取整=四舍五入(与伤害管线 round_direction=floor 分域——registry clause3_inequality 随批登记;
# 1 点量纲行 ac_up/action 取整后不变=整数粒度地板)。
if source_class == "flag_hit" and int(db.settlement.flag_hit_premium) != 100:
var prem_pct: int = int(db.settlement.flag_hit_premium)
if source_class == "seal_hit" and int(db.settlement.seal_hit_premium) != 100:
var prem_pct: int = int(db.settlement.seal_hit_premium)
row = row.duplicate()
row["power"] = int(round(int(row.power) * prem_pct / 100.0))
row["premium_pct"] = prem_pct
@@ -811,7 +811,7 @@ func _apply_percent_row(row: Dictionary, source_class: String, cell: Vector2i, r
if eff == "gp_gain" or eff == "gp_loss":
return _apply_gp_row(row, source_class, cell, ri, highlight, label_suffix)
var srow: Dictionary[Variant, Variant] = db.status_effect_row(eff)
# 旗溢价贯穿(迭代14② T4——仅标中旗正向行携带 premium_pct;其余授予路径缺省 100 零扰动):
# 封印溢价贯穿(迭代14② T4——仅封印命中正向行携带 premium_pct;其余授予路径缺省 100 零扰动):
# 应用幅度与呈现行值同乘,容器经 magnitude_override 拿同一快照(抗表重载口径保持)。
var prem_pct: int = int(row.get("premium_pct", 100))
var eff_mag: int = int(srow.magnitude)
@@ -828,7 +828,7 @@ func _apply_percent_row(row: Dictionary, source_class: String, cell: Vector2i, r
# GP 型行兑付(GP+商店房实施批 2026-09-21):战斗内缓冲 escrow 记账——不入钱包不入增益容器。
# 正向(gp_gain)= 挖开金币安全格/标中旗;负向(gp_loss)= 挖雷格/标错旗。兑付行经既有 effect 行
# 正向(gp_gain)= 挖开GP安全格/封印命中;负向(gp_loss)= 挖危险格/标错封印。兑付行经既有 effect 行
# 通道外发(escrow 随行——归因可读),战斗结束随 gp_dropped 一次上抛后即弃。
func _apply_gp_row(row: Dictionary, source_class: String, cell: Vector2i, ri: int, highlight: String = "", label_suffix: String = "") -> bool:
var eff := String(row.effect_id)
@@ -906,8 +906,8 @@ func _submit_flat_hero(source_class: String, display: String, power: int, cell:
return _outcome_of(_submit(t, sync))
# 旗判定批:标中 = 该雷格资源的正向兑付(每雷实例一次;高光 = 强制检查点);
# 标空 = "标旗未中"归因行 + 该安全格资源的负向兑付(同键去重——每格实例一次)。
# 封印判定批:标中 = 该危险格资源的正向兑付(每雷实例一次;高光 = 强制检查点);
# 标空 = "封印未中"归因行 + 该安全格资源的负向兑付(同键去重——每格实例一次)。
# 清图批:清图奖励对象恰好发一次;完美排雷追加第二份同身份兑付(grant_clear 本身恰一次)。
# 高光行 value = 实际有效兑付份数(禁记名义值;首份零兑付 → 降级不发)。
func _grant_clear_reward(ri: int) -> void:
@@ -1044,7 +1044,7 @@ func _emit_settlement_snapshot(verdict: String) -> void:
board_stats["w"] = minesweeper.current_board().width
board_stats["h"] = minesweeper.current_board().height
board_stats["revealed"] = minesweeper.revealed_cells().size()
board_stats["flags"] = minesweeper.flag_count()
board_stats["seals"] = minesweeper.seal_count()
settlement_snapshot.emit({
"kind": "settlement",
"level": int(encounter.get("level_index", 0)),
@@ -1058,7 +1058,7 @@ func _emit_settlement_snapshot(verdict: String) -> void:
"board": board_stats,
"enemies": enemies,
"buffs": buffs,
"round_facts": {"path": _facts_path, "safe": safe_n, "mines": mines_hit, "flag_events": _facts_flags.size(), },
"round_facts": {"path": _facts_path, "safe": safe_n, "mines": mines_hit, "seal_events": _facts_seals.size(), },
})
@@ -1238,11 +1238,11 @@ func diagnostic_snapshot() -> Dictionary:
"board_seq": _board_seq,
"facts_path": _facts_path,
"facts_reveals": _facts_reveals.size(),
"facts_flags": _facts_flags.size(),
"facts_seals": _facts_seals.size(),
"action_economy": {"phase": String(action_economy.phase), "ap": action_economy.ap, "rap": action_economy.rap},
"minesweeper_board_installed": minesweeper.current_board() != null,
"minesweeper_revealed": minesweeper.revealed_cells().size() if minesweeper.current_board() != null else 0,
"minesweeper_flags": minesweeper.flag_count() if minesweeper.current_board() != null else 0,
"minesweeper_seals": minesweeper.seal_count() if minesweeper.current_board() != null else 0,
"reward_economy_phase": _reward_economy.phase(),
"enemies_alive": _enemy_system.query_alive("enemy").size(),
"hero_hp": int(_enemy_system.hero_unit().get("hp", -1)) if not _enemy_system.hero_unit().is_empty() else -1,
@@ -1251,7 +1251,7 @@ func diagnostic_snapshot() -> Dictionary:
func _reset_round_facts() -> void:
_facts_reveals.clear()
_facts_flags.clear()
_facts_seals.clear()
_facts_path = ""
+1 -1
View File
@@ -28,7 +28,7 @@ func in_bounds(c: Vector2i) -> bool:
return c.x >= 0 and c.y >= 0 and c.x < width and c.y < height
# 行主序排序键(2026-10-02 修复批——排雷玩法 旗判定批与 地图战斗资源 资格规范化两处手抄收口单点):
# 行主序排序键(2026-10-02 修复批——排雷玩法 封印判定批与 地图战斗资源 资格规范化两处手抄收口单点):
# y×1000+x 字典序 = 行主序;板宽域 [8,64] 远小于 1000,x 不跨行、键值不碰撞。
static func row_major_key(c: Vector2i) -> int:
return c.y * 1000 + c.x
+1 -1
View File
@@ -220,7 +220,7 @@ func _build_constraints(b: BoardData, r: Dictionary, known_mines: Dictionary, sa
if known_mines.has(n):
known_here += 1
elif not r.has(n):
unknown[n] = true # 未揭示格一律计入未知(含雷格)——求解器禁读 safe_set 真值(棋盘生成:雷位不许偷看)
unknown[n] = true # 未揭示格一律计入未知(含危险格)——求解器禁读 safe_set 真值(棋盘生成:雷位不许偷看)
var d_eff := d - known_here
if d_eff < 0 or (d_eff > 0 and unknown.is_empty()):
continue # 矛盾态(非法板)——不产约束,验收由 legality 闸承担
+2 -2
View File
@@ -1,7 +1,7 @@
## VERTICAL SLICE - NOT FOR PRODUCTION
## 数值表单点访问入口:全部 .tres 表的加载、校验诊断与类型化访问器(垂直切片验证:改表即改玩法,数值零硬编码)。
## 玩法代码禁止绕过本单点自建 load();消费面一律走访问器,禁直读行集。
## headless --script 模式无 autoload——冒烟脚本显式实例化本类;加载失败 is_ready=false 加 fatal 旗,调用方须查。
## headless --script 模式无 autoload——冒烟脚本显式实例化本类;加载失败 is_ready=false 加 fatal 封印,调用方须查。
class_name ConfigDb
extends Node
@@ -79,7 +79,7 @@ func _fatal(category: String, message: String) -> void:
_fatal_flag = true
var _fatal_flag := false # 运行期致命旗(迭代 27 T6——退出归根承载;组合根/闸读面)
var _fatal_flag := false # 运行期致命封印(迭代 27 T6——退出归根承载;组合根/闸读面)
func is_fatal() -> bool:
+3 -3
View File
@@ -35,7 +35,7 @@ func cross_validate(loaded: Dictionary, push_diag: Callable) -> void:
if String(row.get("cell_type", "")) != RewardTable.TREASURE_ITEM_TYPE:
continue
var treasure_eff: String = String(row.get("effect_id", ""))
if treasure_eff == "": # 负占位行(雷格极性字典兜底,零兑付)——不入投放词表校验
if treasure_eff == "": # 负占位行(危险格极性字典兜底,零兑付)——不入投放词表校验
continue
if not treasure_live.has(treasure_eff):
push_diag.call("error", "reward_dangling_treasure", "RewardTable %s aux 行引用无效道具 %s(retired/deferred/不存在): %s" % [row.get("reward_id"), treasure_eff, "双表校验"])
@@ -49,8 +49,8 @@ func cross_validate(loaded: Dictionary, push_diag: Callable) -> void:
for eff in st_tab.effect_pool_map.keys():
if not effect_ids.has(eff):
push_diag.call("error", "poolmap_dangling", "SettlementTable.effect_pool_map 悬空 effect_id: %s" % eff)
# ③ source_label_map 键集封闭性(键 = 增益状态 source_class ∪ {enemy_action, hero_attack, flag_miss}——2026-09-15 格面资源批删 item 键)
var required_sources := ["safe_cell", "flag_hit", "mine_hit", "clear_reward", "hero_attack", "enemy_action", "flag_miss"]
# ③ source_label_map 键集封闭性(键 = 增益状态 source_class ∪ {enemy_action, hero_attack, seal_miss}——2026-09-15 格面资源批删 item 键)
var required_sources := ["safe_cell", "seal_hit", "mine_hit", "clear_reward", "hero_attack", "enemy_action", "seal_miss"]
for s in required_sources:
if not st_tab.source_label_map.has(s):
push_diag.call("error", "segorder_violation", "source_label_map 缺必需键: %s" % s)
+2 -2
View File
@@ -39,10 +39,10 @@ var policy := POLICY_SEGMENT # 结账时机开关(2026-09-24 用户拍板
var _queue: Array[Dictionary] = [] # 未结单据(B 模式攒批;A 模式恒空)
var _outcomes := {} # ticket_id → "redeemed"/"zero"/"voided"(结账后可查——清图高光份数消费)
var _seq := 0 # 流水号
var _halted := false # 主角倒下终止旗(死亡事件置位——flush 余单作废)
var _halted := false # 主角倒下终止封印(死亡事件置位——flush 余单作废)
var _hero_ac_override := -1 # 主角护甲等级合流入参(begin_battle 显式设定——-1 = 表值兜底,口径同战斗编排器)
# 效果引擎(内容数据驱动扩展批 T1/T3——attack_commit/clamp_check/armor_roll/kill/damage_taken
# 发射+施加面:dispatch 返回命中,本类按组合规则施加——T3 起旧 feat_flags 旗消费点全部拆除)
# 发射+施加面:dispatch 返回命中,本类按组合规则施加——T3 起旧 feat_seals 封印消费点全部拆除)
var _fx: EffectEngine = null
# 战斗段运行态事实(编排器每轮刷新——fact 取数面:attack_commit 载荷 dig_streak /
# charge_layers facts〔猎巢人 per/整备 attack_bonus per/厚积薄发 when/铁壁 shield per〕;
+15 -15
View File
@@ -6,18 +6,18 @@ class_name EffectEngine
extends RefCounted
## 事件词汇(迭代 20 专长实装批 T1 扩签——16→18:+dig_stop 停手挂点〔弃图兑换〕
## +battle_won 战斗胜利〔战地包扎〕;迭代 21 天赋树批 T3 扩签 18→19:+flag_missed 标错判定
## 〔危险直觉·守挂点——_judge_flag 去重闸后发射,契约 12 触发主语=当回合标错动作;
## 命名避让 _emit_row 在册 source_class 名 flag_miss〕;迭代 22 秘宝实装批 T3 扩签 19→20:
## +battle_won 战斗胜利〔战地包扎〕;迭代 21 天赋树批 T3 扩签 18→19:+seal_missed 标错判定
## 〔危险直觉·守挂点——_judge_seal 去重闸后发射,契约 12 触发主语=当回合标错动作;
## 命名避让 _emit_row 在册 source_class 名 seal_miss〕;迭代 22 秘宝实装批 T3 扩签 19→20:
## +board_installed 装板完成〔巢穴预告——每板恰一次,战斗板生成/重建发射点〕;原发射点不动)
const EVENTS := ["dig_reveal", "dig_paid", "flag_hit", "mine_hit", "attack_commit", "clamp_check",
"armor_roll", "kill", "damage_taken", "enemy_hp_threshold", "round_start", "flag_settle",
"round_end", "reward_roll", "treasure_used", "charge_full", "dig_stop", "battle_won", "flag_missed",
const EVENTS := ["dig_reveal", "dig_paid", "seal_hit", "mine_hit", "attack_commit", "clamp_check",
"armor_roll", "kill", "damage_taken", "enemy_hp_threshold", "round_start", "seal_settle",
"round_end", "reward_roll", "treasure_used", "charge_full", "dig_stop", "battle_won", "seal_missed",
"board_installed"]
## 扫雷侧事件集(🔴 流派扫雷触发铁律新形态:每流派 ≥1 行订阅本集事件——schema 加载校验。
## 原 MINE_HOOKS 的同义迁移:挖掘/标旗/轮末结算面事件)
const MINE_EVENTS := ["dig_reveal", "dig_paid", "flag_hit", "mine_hit", "flag_settle", "round_end", "reward_roll"]
## 原 MINE_HOOKS 的同义迁移:挖掘/封印/轮末结算面事件)
const MINE_EVENTS := ["dig_reveal", "dig_paid", "seal_hit", "mine_hit", "seal_settle", "round_end", "reward_roll"]
## 效果类型词汇(解释器 switch 闭集——需求卡片 §3.3 定稿 19 行;类型实数 24,含共享行拆型:
## grant_gp/bias_wallet、counter_add/counter_spend、reveal_tiles/remove_mines/refresh_pick、
@@ -44,12 +44,12 @@ const EFFECT_KINDS := ["add_ticket_rider", "modify_ticket", "damage_mult", "open
## fact 条件词汇(封闭枚举——迭代 20 T1 扩签 9→17:payload 同名键直读面〔fact 取值序 payload
## 优先——effect_engine._fact_value 既有机制〕。new_resource_type=首开资源类型〔地质学家〕;
## is_edge_cell=边缘格〔边陲矿工〕;is_blank_zero=空白连锁〔跳格采掘〕;dug_under_flag=被挖格
## 当时有旗〔破封一击〕;ap_exhausted_last=上轮 AP 耗尽〔一滴不剩〕;hp_ratio=生命比例〔背水
## is_edge_cell=边缘格〔边陲矿工〕;is_blank_zero=空白连锁〔跳格采掘〕;dug_under_seal=被挖格
## 当时有封印〔破封一击〕;ap_exhausted_last=上轮 AP 耗尽〔一滴不剩〕;hp_ratio=生命比例〔背水
## 06 前置条兑付〕;amount=单次伤害量〔保险丝〕;ap_left=停手剩余 AP〔弃图兑换〕)
const FACTS := ["matchup", "is_first_dig_of_battle", "terrain_kind", "target_armor_class",
"flags_count", "charge_layers", "nest_count", "round_index", "counter",
"new_resource_type", "is_edge_cell", "is_blank_zero", "dug_under_flag",
"seals_count", "charge_layers", "nest_count", "round_index", "counter",
"new_resource_type", "is_edge_cell", "is_blank_zero", "dug_under_seal",
"ap_exhausted_last", "hp_ratio", "amount", "ap_left", "pct",
"battle_index"] # 迭代 21 T3 扩签——关卡域战斗序(1 基,enc 携带;开局姿态组「每关首轮窗」条件面)
@@ -144,12 +144,12 @@ func rebuild(feat_ids: Array, item_ids: Array, tree_rules: Array = []) -> void:
"do": {"kind": "counter_add", "counter_id": "tree_sense_sharp_used",
"value": 1, "domain": "adventure"}}, {})
"sense_guard":
# 守=标错返还行动点·每关限次(契约 12 失误补偿——flag_missed 由 _judge_flag 去重闸后
# 守=标错返还行动点·每关限次(契约 12 失误补偿——seal_missed 由 _judge_seal 去重闸后
# 发射;queue=下一轮开轮兑现,末轮判出无下一轮=合法作废自动成立)
_push_trigger("tree", rk, {"on": "flag_missed",
_push_trigger("tree", rk, {"on": "seal_missed",
"when": [{"fact": "counter", "id": "tree_sense_guard_used", "op": "lt", "value": 2}],
"do": {"kind": "grant_ap", "mode": "queue", "value": 1}}, {})
_push_trigger("tree", rk, {"on": "flag_missed",
_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"}}, {})
+2 -2
View File
@@ -45,7 +45,7 @@ var _attunement: Array[Variant] = [] # 同调装配集(迭
var _hero_attack_type := ""
var _hero_armor_type := ""
# ---- 商店共享池模式态(迭代 11 第②批 T3——缺省 legacy=固定三件加定价 off 对照承载,零 RNG 零池消费;TP0 令牌池换代)----
var _shop_pool_mode := false # pool 模式旗(set_shop_pool 注入时置位——AdventureRunner 按 params.shop_mode 下传)
var _shop_pool_mode := false # pool 模式封印(set_shop_pool 注入时置位——AdventureRunner 按 params.shop_mode 下传)
var _pool_deck: Array[Variant] = [] # 共享池令牌库共享引用(AdventureRunner._curio_deck——库存=剩余令牌数单点)
var _pool_consume := Callable() # 池消费 handler(AdventureRunner._consume_pool_purchase——_apply_curio_pick 同口写加算区+账本+摘牌)
var _pool_ticket := Callable() # 门票展示判定 handler(T5——AdventureRunner._feat_ticket_consider;节点与商店共享计数)
@@ -56,7 +56,7 @@ var _shop_paid_refreshes := 0 # 本次进店已付费刷新次数
var hero_atk_pct := 0 # 测试面板覆盖值(切片专用)
var hero_hp_pct := 0
# ---- 关卡相位轴(2026-10-02 修复批——布尔旗收拢:_running/_awaiting_choice 两旗 → 显式 Phase enum;
# ---- 关卡相位轴(2026-10-02 修复批——布尔封印收拢:_running/_awaiting_choice 两封印 → 显式 Phase enum;
# 对齐 行动经济 Phase 先例(action_economy.gd:内部 enum + 守卫换算,外部读面契约零变化)——
# level_result:String 与 awaiting_choice() 被 AdventureRunner/呈现层消费的语义逐字不变)----
enum Phase { IDLE, RUNNING, AWAITING_CHOICE, ENDED }
+71 -71
View File
@@ -1,28 +1,28 @@
## VERTICAL SLICE - NOT FOR PRODUCTION
## 排雷规则引擎:棋盘产物+玩家意图批次 → 揭示/旗判定/轮末事件流,install 四不变式守门(垂直切片验证:挖掘逐行归因)。
## 排雷规则引擎:棋盘产物+玩家意图批次 → 揭示/封印判定/轮末事件流,install 四不变式守门(垂直切片验证:挖掘逐行归因)。
## 零随机、零表读取、零 UI——判定只依赖棋盘事实与动作序列。
## 板态对外只经 current_board()/reveal_record() 等查询面,禁跨模块直读写内部字典;轮末双发射源之棋盘路径由 round_end_board 外发。
class_name MinesweeperRules
extends RefCounted
signal cell_revealed(evt: Dictionary)
signal flag_judged(evt: Dictionary)
signal seal_judged(evt: Dictionary)
signal round_end_board(payload: Dictionary) # 双发射源之棋盘路径(排雷玩法 外发;stopped 由 行动经济 直发)
var db: ConfigDb
var action_economy: ActionEconomy
# 秘宝常时参数注入面(迭代 22 秘宝批 T3——编排器 begin_battle 按装配集 kind 分发注入:
# dig_range_bonus=挖掘切比雪夫距离放宽〔远程镐 dig_range_bonus magnitude〕/flag_range_free=
# 插旗不限相邻〔信标网络〕;缺省 0/false=零装配零扰动,棋盘事实不可变契约不受影响——
# dig_range_bonus=挖掘切比雪夫距离放宽〔远程镐 dig_range_bonus magnitude〕/seal_range_free=
# 封印不限相邻〔信标网络〕;缺省 0/false=零装配零扰动,棋盘事实不可变契约不受影响——
# 两参数只放宽「动作可达性」判据,禁碰危险格/安全格分布)
var dig_range_bonus := 0
var flag_range_free := false
var seal_range_free := false
# 棋盘状态私有(E2 封装收口 2026-09-13):对外只暴露 current_board()/reveal_record() 等查询面,
# 禁止跨模块直读/直写原始字典——先例教训 = board_view 绕过查询面直读 revealed。
var _board: BoardData = null
var _revealed := {} # Vector2i → {"kind": "safe"/"mine", "digit": int}
var _flags := {} # Vector2i → true(棋盘单态;跨轮保留随图存续)
var _removed := {} # Vector2i → true(迭代 13 T8/D6——中立移除雷格集;随 install/retire 清空)
var _seals := {} # Vector2i → true(棋盘单态;跨轮保留随图存续)
var _removed := {} # Vector2i → true(迭代 13 T8/D6——中立移除危险格集;随 install/retire 清空)
var _hero := Vector2i.ZERO
var _spawn: Dictionary[Vector2i, bool] = {}
@@ -52,8 +52,8 @@ func is_revealed(c: Vector2i) -> bool:
return _revealed.has(c)
func has_flag(c: Vector2i) -> bool:
return _flags.has(c)
func has_seal(c: Vector2i) -> bool:
return _seals.has(c)
func revealed_cells() -> Array:
@@ -73,13 +73,13 @@ func clear_map_cleared() -> bool:
# ---- 中立移除(迭代 13 T8/D6——P1 甲:邻接数字=原始雷数冻结零重算;P2 甲:移除格呈现退色降档;
# 移除集=未揭示、无旗、是雷——旗判定基线零回溯;雷格不入清图分母/连锁/C_dig 模拟,判定逻辑零改动) ----
# 移除集=未揭示、无封印、是雷——封印判定基线零回溯;危险格不入清图分母/连锁/C_dig 模拟,判定逻辑零改动) ----
func remove_mines_in(cells: Array) -> Array:
var out: Array[Variant] = []
for c in cells:
var cell: Vector2i = c
if not _board.in_bounds(cell) or is_revealed(cell) or has_flag(cell) or not _board.is_mine(cell):
if not _board.in_bounds(cell) or is_revealed(cell) or has_seal(cell) or not _board.is_mine(cell):
continue
if _removed.has(cell):
continue
@@ -104,7 +104,7 @@ func has_unrevealed() -> bool:
# ---- install(编排边界相位 = 行动经济 Idle/Waiting;Active 拒绝——防轮内换板)----
# during_round = 轮内刷新编排路径(2026-09-12 方案 A,战斗结算 唯一调用方):Active 相位合法——
# 玩家挖掘中立即换图,棋盘状态(revealed/flags)随 install 重置,初始展开事件照发(initial 通道)
# 玩家挖掘中立即换图,棋盘状态(revealed/seals)随 install 重置,初始展开事件照发(initial 通道)
func install_board(b: BoardData, during_round := false) -> Dictionary:
if action_economy.phase == action_economy.PHASE_ACTIVE and not during_round:
return _outcome(false, "rejected", "phase", "")
@@ -118,7 +118,7 @@ func install_board(b: BoardData, during_round := false) -> Dictionary:
return _outcome(false, "rejected", "board_invalid", inv[0])
_board = b
_revealed.clear()
_flags.clear()
_seals.clear()
_removed.clear()
_spawn = {}
for c in b.spawn_cells():
@@ -134,8 +134,8 @@ func install_board(b: BoardData, during_round := false) -> Dictionary:
return _outcome(true, "executed", "", "")
func flag_count() -> int:
return _flags.size()
func seal_count() -> int:
return _seals.size()
# during_round = 轮内刷新编排路径(2026-09-12 方案 A):Active 相位合法——换图前退役旧板
@@ -146,7 +146,7 @@ func retire_board(during_round := false) -> Dictionary:
return _outcome(false, "rejected", "phase", "")
_board = null
_revealed.clear()
_flags.clear()
_seals.clear()
_removed.clear()
return _outcome(true, "executed", "", "")
@@ -231,10 +231,10 @@ func _classify(intent: Dictionary) -> int:
if intent.get("action", "") == "dig":
var c: Vector2i = intent.get("cell", Vector2i(-9, -9))
if _board_face_dig(c) == "" and not _board.is_mine(c):
# 模拟 Chain(O, c) 后清图判定——挖旗格同口径:本格旗视为已隐式退役(其余旗照挡)
var sim_flags: Dictionary[Variant, Variant] = _flags.duplicate()
sim_flags.erase(c)
var chain := _chain_closure(c, sim_flags) # 与真实执行同口径——空旗集捷径会在旗挡连锁时误判 C_dig(排雷玩法:真模拟)
# 模拟 Chain(O, c) 后清图判定——挖封印格同口径:本格封印视为已隐式退役(其余封印照挡)
var sim_seals: Dictionary[Variant, Variant] = _seals.duplicate()
sim_seals.erase(c)
var chain := _chain_closure(c, sim_seals) # 与真实执行同口径——空封印集捷径会在封印挡连锁时误判 C_dig(排雷玩法:真模拟)
var remaining := 0
for sc in _board.safe_cells():
if not _revealed.has(sc) and not chain.has(sc):
@@ -251,8 +251,8 @@ func _execute(intent: Dictionary) -> Dictionary:
match intent.get("action", ""):
"dig":
return _do_dig(intent.get("cell", Vector2i(-9, -9)))
"flag_place":
return _do_flag_place(intent.get("cell", Vector2i(-9, -9)))
"seal_place":
return _do_seal_place(intent.get("cell", Vector2i(-9, -9)))
"move":
return _do_move(intent.get("cell", Vector2i(-9, -9)))
"stop":
@@ -267,16 +267,16 @@ func _do_dig(c: Vector2i) -> Dictionary:
var sub := _board_face_dig(c)
if sub != "":
return _outcome(false, "rejected", "board_face", sub)
# 挖旗格(插旗不可逆批 2026-09-19):本格旗事务内隐式退役——链闭包按「本格无旗、其余旗照挡」
# 计算(旗格为 0 时连锁照常外扩);request_dig 被拒则旗原地保留(事务性),受理后该旗轮末不判
# (发射面基线 = 在场旗——标中/标空兑付让位于挖掘本体结局,四情况②④)。
var was_flagged := has_flag(c)
var eff_flags: Dictionary[Variant, Variant] = _flags
if was_flagged:
eff_flags = _flags.duplicate()
eff_flags.erase(c)
# 挖封印格(封印不可逆批 2026-09-19):本格封印事务内隐式退役——链闭包按「本格无封印、其余封印照挡」
# 计算(封印格为 0 时连锁照常外扩);request_dig 被拒则封印原地保留(事务性),受理后该封印轮末不判
# (发射面基线 = 在场封印——标中/标空兑付让位于挖掘本体结局,四情况②④)。
var was_sealed := has_seal(c)
var eff_seals: Dictionary[Variant, Variant] = _seals
if was_sealed:
eff_seals = _seals.duplicate()
eff_seals.erase(c)
# 真模拟连锁与清图(F2/F1——单一实现两名字)
var chain := _chain_closure(c, eff_flags)
var chain := _chain_closure(c, eff_seals)
var clears: bool = _cleared_after(chain)
# F2 口径:chain_size = |O′ ∖ (O ∪ {c})|——闭包含已开集 O,只数新开格(不含首格)
var chain_size := 0
@@ -287,33 +287,33 @@ func _do_dig(c: Vector2i) -> Dictionary:
if not action_economy.request_dig(chain_size, clears):
return _outcome(false, "rejected", "resource_face", "")
# 事务内执行:首格 + 连锁新格揭示(BFS 规范序,零特判);事件 = O′ ∖ O
if was_flagged:
_flags.erase(c)
var reveal_order: Array[Variant] = _chain_reveal_order(c, eff_flags)
if was_sealed:
_seals.erase(c)
var reveal_order: Array[Variant] = _chain_reveal_order(c, eff_seals)
for rc in reveal_order:
if _revealed.has(rc):
continue # 闭包可穿过已开区——已开格零事件(cell_revealed × |O′ ∖ O|)
var is_mine := _board.is_mine(rc)
var channel := "dig" if rc == c else "chain"
# 格面资源资格(2026-09-15 格面资源批):chain/dig 恒 true——安全格与雷格一视同仁(雷格亦携带,
# 格面资源资格(2026-09-15 格面资源批):chain/dig 恒 true——安全格与危险格一视同仁(危险格亦携带,
# 负向兑付归 战斗结算 极性路由);initial(封锁格)恒 false。资源本体与极性不在本系统。
# dug_under_flag = 首格带旗直挖标记(战斗结算 情况④兑付改判消费;连锁格恒 false——连锁不越旗)
# dug_under_seal = 首格带封印直挖标记(战斗结算 情况④兑付改判消费;连锁格恒 false——连锁不越封印)
if is_mine:
_revealed[rc] = {"kind": "mine", "digit": -1}
cell_revealed.emit({
"cell": rc, "kind": "mine", "digit": -1,
"variant_ids": [], "channel": channel, "resource": true,
"dug_under_flag": was_flagged and rc == c,
"dug_under_seal": was_sealed and rc == c,
})
else:
_revealed[rc] = {"kind": "safe", "digit": _board.digit_of(rc)}
cell_revealed.emit({
"cell": rc, "kind": "safe", "digit": _board.digit_of(rc),
"variant_ids": _board.variant_ids(rc), "channel": channel, "resource": true,
"dug_under_flag": was_flagged and rc == c,
"dug_under_seal": was_sealed and rc == c,
})
if action_economy.phase != action_economy.PHASE_ACTIVE:
_emit_round_end_flags_and_payload()
_emit_round_end_seals_and_payload()
return _outcome(true, "executed", "", "")
@@ -326,54 +326,54 @@ func _board_face_dig(c: Vector2i) -> String:
return "target_revealed"
if _removed.has(c):
return "target_removed" # 迭代 13 T8/D6——中立移除格不可交互(拒绝先于消耗,pattern 1 board_face 腿)
# 旗不拦挖掘(插旗不可逆批 2026-09-19):有旗格直接挖 = 对承诺的兑现,兑付改判归 战斗结算。
# 封印不拦挖掘(封印不可逆批 2026-09-19):有封印格直接挖 = 对承诺的兑现,兑付改判归 战斗结算。
return ""
# fx_reveal 效果通道揭示(迭代 20 专长实装批 T1——订阅类「揭示 1 格」效果的棋盘面执行口:
# 不经行动经济〔零 AP 消耗零相位要求〕、不要求邻接〔任意揭示〕、只受理安全格〔信息红线=只揭真格,
# 雷格/已开/出界/移除格=拒绝零副作用〕。连锁与揭示事件与 _do_dig 同构(chain 通道复用),资源兑付归
# 危险格/已开/出界/移除格=拒绝零副作用〕。连锁与揭示事件与 _do_dig 同构(chain 通道复用),资源兑付归
# 战斗结算 _flush_pending_reveals 直接入账——channel=fx_reveal 供归因)
func fx_reveal(c: Vector2i) -> bool:
if _board == null or not _board.in_bounds(c) or is_revealed(c) or _board.is_mine(c) or _removed.has(c):
return false
var chain := _chain_closure(c, _flags)
for rc in _chain_reveal_order(c, _flags):
var chain := _chain_closure(c, _seals)
for rc in _chain_reveal_order(c, _seals):
if _revealed.has(rc):
continue
_revealed[rc] = {"kind": "safe", "digit": _board.digit_of(rc)}
cell_revealed.emit({
"cell": rc, "kind": "safe", "digit": _board.digit_of(rc),
"variant_ids": _board.variant_ids(rc), "channel": "fx_reveal", "resource": true,
"dug_under_flag": false,
"dug_under_seal": false,
})
return true
# 连锁闭包:μX.[O∪{c} ⊆ X ∧ ∀x∈X:(x∈S ∧ num(x)=0 ⇒ N₈(x)∩S∖G ⊆ X)]——连锁不越旗
func _chain_closure(start: Vector2i, flag_set: Dictionary) -> Dictionary:
# 连锁闭包:μX.[O∪{c} ⊆ X ∧ ∀x∈X:(x∈S ∧ num(x)=0 ⇒ N₈(x)∩S∖G ⊆ X)]——连锁不越封印
func _chain_closure(start: Vector2i, seal_set: Dictionary) -> Dictionary:
var chain := {start: true}
var queue: Array[Variant] = [start]
while not queue.is_empty():
var c: Vector2i = queue.pop_front()
if _board.is_safe(c) and not flag_set.has(c) and _board.digit_of(c) == 0:
if _board.is_safe(c) and not seal_set.has(c) and _board.digit_of(c) == 0:
for n in _board.neighbors8(c):
if _board.is_safe(n) and not flag_set.has(n) and not chain.has(n):
if _board.is_safe(n) and not seal_set.has(n) and not chain.has(n):
chain[n] = true
queue.append(n)
return chain
# 揭示规范序:首格 + 触发格 BFS、8 邻行主序
func _chain_reveal_order(start: Vector2i, flag_set: Dictionary) -> Array:
func _chain_reveal_order(start: Vector2i, seal_set: Dictionary) -> Array:
var order: Array[Variant] = [start]
var seen := {start: true}
var queue: Array[Variant] = [start]
while not queue.is_empty():
var c: Vector2i = queue.pop_front()
if _board.is_safe(c) and not flag_set.has(c) and _board.digit_of(c) == 0:
if _board.is_safe(c) and not seal_set.has(c) and _board.digit_of(c) == 0:
for n in _board.neighbors8(c):
if _board.is_safe(n) and not flag_set.has(n) and not seen.has(n):
if _board.is_safe(n) and not seal_set.has(n) and not seen.has(n):
seen[n] = true
order.append(n)
queue.append(n)
@@ -387,33 +387,33 @@ func _cleared_after(chain: Dictionary) -> bool:
return true
# ---- 旗 ----
# ---- 封印 ----
func _do_flag_place(c: Vector2i) -> Dictionary:
func _do_seal_place(c: Vector2i) -> Dictionary:
var sub := ""
if not _board.in_bounds(c):
sub = "out_of_catalog"
elif not (flag_range_free or _adjacent_to_hero(c)):
sub = "not_adjacent" # 信标网络装配时豁免(插旗不限相邻——旗动作仍按标准价计费,规则放宽仅可达性)
elif not (seal_range_free or _adjacent_to_hero(c)):
sub = "not_adjacent" # 信标网络装配时豁免(封印不限相邻——封印动作仍按标准价计费,规则放宽仅可达性)
elif is_revealed(c):
sub = "target_revealed"
elif has_flag(c):
sub = "flag_present"
elif has_seal(c):
sub = "seal_present"
elif _removed.has(c):
sub = "target_removed" # 迭代 13 T8/D6——中立移除格不可交互
if sub != "":
return _outcome(false, "rejected", "board_face", sub)
if not action_economy.try_spend("flag_place"):
if not action_economy.try_spend("seal_place"):
return _outcome(false, "rejected", "resource_face", "")
_flags[c] = true # 旗动作零格子事件外发(发射面封闭;反馈经调用返回值命令式驱动)
_seals[c] = true # 封印动作零格子事件外发(发射面封闭;反馈经调用返回值命令式驱动)
if action_economy.phase != action_economy.PHASE_ACTIVE:
# 旗动作花完末点 AP 同样终结轮次(行动经济 三出口——AP 用完与动作种类无关;缺漏使 round_end
# 封印动作花完末点 AP 同样终结轮次(行动经济 三出口——AP 用完与动作种类无关;缺漏使 round_end
# 永不发出、战斗结算 的 Waiting 结算点永不到达 = 整局软锁。playtest 缺陷②,行为探针复现)
_emit_round_end_flags_and_payload()
_emit_round_end_seals_and_payload()
return _outcome(true, "executed", "", "")
# ---- 移动(瞬移;免费不经账本;BFS 限已探明区含已引爆雷格)----
# ---- 移动(瞬移;免费不经账本;BFS 限已探明区含已引爆危险格)----
func _do_move(c: Vector2i) -> Dictionary:
var sub := ""
@@ -455,29 +455,29 @@ func compute_move_path(target: Vector2i) -> Array:
return path
# ---- 停手(经批次入口委托 行动经济;先发旗判定批再委托——事件序契约 flag_judged < round_end 全路径统一)----
# ---- 停手(经批次入口委托 行动经济;先发封印判定批再委托——事件序契约 seal_judged < round_end 全路径统一)----
func _do_stop() -> Dictionary:
_emit_flags_batch() # 基线 = 事务开始旗位(停手不改旗位)
_emit_seals_batch() # 基线 = 事务开始封印位(停手不改封印位)
if not action_economy.request_stop():
return _outcome(false, "rejected", "phase", "")
# 行动经济 直发 stopped 路径 round_end;排雷玩法 禁双发(双发射源纪律)
return _outcome(true, "executed", "", "")
# ---- 轮末旗判定 + 棋盘路径 round_end 外发 ----
# ---- 轮末封印判定 + 棋盘路径 round_end 外发 ----
func _emit_flags_batch() -> void:
# 判定基线 = 轮次结束事务开始时旗位;行主序枚举(确定性——排序键单点 BoardData.row_major_key,
func _emit_seals_batch() -> void:
# 判定基线 = 轮次结束事务开始时封印位;行主序枚举(确定性——排序键单点 BoardData.row_major_key,
# 2026-10-02 修复批);被拔的不判;诚实重复上报(去重归 战斗结算)
var cells: Array[Variant] = _flags.keys()
var cells: Array[Variant] = _seals.keys()
cells.sort_custom(func(a, b): return BoardData.row_major_key(a) < BoardData.row_major_key(b))
for c in cells:
flag_judged.emit({"cell": c, "hit": _board.is_mine(c)})
seal_judged.emit({"cell": c, "hit": _board.is_mine(c)})
func _emit_round_end_flags_and_payload() -> void:
_emit_flags_batch()
func _emit_round_end_seals_and_payload() -> void:
_emit_seals_batch()
var payload: Dictionary[Variant, Variant] = action_economy.last_round_end # 逐字段取自 行动经济(读取权 = 接口契约)
round_end_board.emit({"path_tag": payload.get("path_tag", ""), "continuation_hint": payload.get("continuation_hint", "")})
+1 -1
View File
@@ -31,7 +31,7 @@ func _init(db_ref: ConfigDb, rng_ref: RngServiceCore) -> void:
# ---- install 期 roll(恰一次;Ready 相位再调拒绝)----
# eligible = 全部非封锁格(安全格 + 雷格,战斗结算 install 编排供给;封锁格不入清单)
# eligible = 全部非封锁格(安全格 + 危险格,战斗结算 install 编排供给;封锁格不入清单)
func roll_board(eligible: Array) -> bool:
# 相位守卫:Ready 再调 = double-roll 拒绝(E5);Retired(换图后)= 合法新批重置(E6 roll 半)
if _phase == "ready":
+52 -52
View File
@@ -1,7 +1,7 @@
class_name GrantSegment
extends RefCounted
# ① 授予段 Segment(迭代 27 T2——P1-1 结算巨函数拆段;段边界=审计五段〔裁点③甲〕)。
# 职责:结算队列(开格/旗判定/清图)→ 极性路由逐批兑付 + 旗判/军规开单 + round_end 施加 +
# 职责:结算队列(开格/封印判定/清图)→ 极性路由逐批兑付 + 封印判/军规开单 + round_end 施加 +
# 转存收口 + 揭示兑付 + 段界结账。行发射/开单/兑付原语仍归宿主编排器(oc)——
# 本段只搬运时序,零算法改动(🔴 零行为变更契约:同种子完整局输出逐位一致)。
# kind 分派单点化(P1-1 同批):counter_add 五位三行组 / grant_material 三位兑付块
@@ -16,8 +16,8 @@ func _init(host) -> void:
func run(ri: int) -> void:
# ① 授予段:结算队列(开格/旗判定/清图)→ 极性路由逐批兑付(伤害形态单据暂存——见 _submit 案C 闸)。
# 判定基线 = 轮末在场旗(发射面已过滤——被挖开的旗已随挖掘擦除零兑付,插旗不可逆批 2026-09-19;
# ① 授予段:结算队列(开格/封印判定/清图)→ 极性路由逐批兑付(伤害形态单据暂存——见 _submit 案C 闸)。
# 判定基线 = 轮末在场封印(发射面已过滤——被挖开的封印已随挖掘擦除零兑付,封印不可逆批 2026-09-19;
# 开格事实不可撤)。
oc._defer_board_damage = true # 案C:授予段伤害单据只暂存不结账(2026-10-06 用户裁——兑付时点=战斗段末)
for evt in oc._facts_reveals:
@@ -27,10 +27,10 @@ func run(ri: int) -> void:
if not (snap is Dictionary):
continue
if evt.kind == "safe":
if bool(evt.get("dug_under_flag", false)):
# 插旗不可逆四情况④(2026-09-19 用户拍板):插安全格再挖开 = 零惩罚零奖励——正向兑付
# 作废(归因行在案);标空判定已随旗擦除自然缺席(被挖的旗不在轮末在场基线)。
oc._emit_row("reward_ineffective", "safe_cell", evt.cell, ri, "奖励作废(插旗格直接挖开——零惩罚零奖励)", 0, {}, "")
if bool(evt.get("dug_under_seal", false)):
# 封印不可逆四情况④(2026-09-19 用户拍板):插安全格再挖开 = 零惩罚零奖励——正向兑付
# 作废(归因行在案);标空判定已随封印擦除自然缺席(被挖的封印不在轮末在场基线)。
oc._emit_row("reward_ineffective", "safe_cell", evt.cell, ri, "奖励作废(封印格直接挖开——零惩罚零奖励)", 0, {}, "")
continue
# dig_reveal 发射+施加(内容数据驱动扩展批 T3——桶序固定承载等价:
# 乘区收集 → 正向兑付 → 计数器 → 追加产出(深挖)→ 奥术矢。
@@ -81,16 +81,16 @@ func run(ri: int) -> void:
DamageController.TARGET_FRONT_ALIVE, ri, evt.cell, "magic")
oc._submit(spell_t, false)
elif evt.kind == "mine":
# 插旗不可逆四情况②:插雷格再挖开 = 只受挖雷一层惩罚(mine_hit 照常兑付——惊动巢穴),
# 标中不发(旗已随挖掘擦除、不入轮末判定基线)。
# 封印不可逆四情况②:插危险格再挖开 = 只受挖雷一层惩罚(mine_hit 照常兑付——惊动巢穴),
# 标中不发(封印已随挖掘擦除、不入轮末判定基线)。
# mine_hit 发射+施加(内容数据驱动扩展批 T3——桶序固定承载等价:
# 减半收集 → 负向兑付〔带减半系数〕→ 反打开单 → 连击清零计数器。
# 迭代 20 T1——发射前移+payload 扩 dug_under_flag〔破封一击〕;新增施加面:
# 迭代 20 T1——发射前移+payload 扩 dug_under_seal〔破封一击〕;新增施加面:
# damage_reduce〔安全帽耗层减伤——value_counter 层值,一次清零〕/ heal〔血蜜〕/
# blast_scaled〔陷阱爆发——雷伤×倍率全体敌〕/ bonus_delta〔血税〕/ reveal_tiles〔见血开窍〕)
var mine_power := int((snap as Dictionary).get("power", 0))
var mh_hits := oc._fx_emit("mine_hit", {"cell": evt.cell, "round": ri,
"damage": mine_power, "dug_under_flag": bool(evt.get("dug_under_flag", false)),
"damage": mine_power, "dug_under_seal": bool(evt.get("dug_under_seal", false)),
"hp_ratio": int(round(100.0 * float(int(oc._enemy_system.hero_unit().get("hp", 0))) / float(maxi(1, int(oc._enemy_system.hero_unit().get("max_hp", 1))))))})
var halve_pct := 0
var mine_reduce := 0
@@ -130,7 +130,7 @@ func run(ri: int) -> void:
oc._submit(nct, false)
# 地形变异 兑付段(授予段尾——触发枚举 on_open:挖开即伤、chain 通道同口径;伤害单据案C 暂存到
# 战斗段末兑付〔2026-10-06 用户裁〕;信息型 hide_number 不进结算——呈现层读取,见 board_view 数字遮蔽;
# 雷格恒无变异——布置段排除,双层叠罚不存在;插旗格直挖照吃变异伤害——地形后果同 mine_hit 口径,
# 危险格恒无变异——布置段排除,双层叠罚不存在;封印格直挖照吃变异伤害——地形后果同 mine_hit 口径,
# 非奖励不作废;吞噬流资源兑付〔荆棘编织/地脉补偿〕中伤害形态行同样经案C闸暂存,非伤害形态照常当场)
for evt in oc._facts_reveals:
var vids: Array[Variant] = evt.get("variant_ids", [])
@@ -160,40 +160,40 @@ func run(ri: int) -> void:
for h in dt_hits:
if String(h.get("kind", "")) == "grant_material":
_apply_material_hit(h, evt.cell, ri)
for evt in oc._facts_flags:
_judge_flag(evt, ri)
# 战旗军规开单段(迭代 12 T3 标旗流——flag_judged 批后统一开单:轮末每面标中旗对最前存活敌
# 结算 board_typed 必中伤害〔复用零新配方;「旗所在巢穴敌」简化为 default_target 最前存活敌——
# 台账披露待追认〕;完美布防=本轮旗批全中(hit>0 且 miss==0)时旗伤 ×mult——全对推理奖励)。
for evt in oc._facts_seals:
_judge_seal(evt, ri)
# 战封印军规开单段(迭代 12 T3 封印流——seal_judged 批后统一开单:轮末每面封印命中对最前存活敌
# 结算 board_typed 必中伤害〔复用零新配方;「封印所在巢穴敌」简化为 default_target 最前存活敌——
# 台账披露待追认〕;完美布防=本轮封印批全中(hit>0 且 miss==0)时封印伤 ×mult——全对推理奖励)。
# 伤害单据案C 暂存战斗段末兑付。
# flag_settle 发射+施加(内容数据驱动扩展批 T3——战旗 open_ticket + 完美布防 modify_ticket
# 改写窗口:旗伤基值×完美倍率后逐旗开单,与迁移前算序一致)
# seal_settle 发射+施加(内容数据驱动扩展批 T3——战封印 open_ticket + 完美布防 modify_ticket
# 改写窗口:封印伤基值×完美倍率后逐封印开单,与迁移前算序一致)
var fs_hits := 0
var fs_misses := 0
for fevt in oc._facts_flags:
for fevt in oc._facts_seals:
if bool(fevt.get("hit", false)):
fs_hits += 1
else:
fs_misses += 1
var fs_fx := oc._fx_emit("flag_settle", {"flags_hit_all": fs_hits > 0 and fs_misses == 0,
"flags_count": fs_hits, "round": ri})
var fs_fx := oc._fx_emit("seal_settle", {"seals_hit_all": fs_hits > 0 and fs_misses == 0,
"seals_count": fs_hits, "round": ri})
var banner_hits: Array[Variant] = []
for h in fs_fx:
if String(h.get("kind", "")) == "open_ticket" and String(h.get("channel", "")) == "attack":
banner_hits.append(h)
if not banner_hits.is_empty():
var strike_value := int((banner_hits[0] as Dictionary).get("value", 0))
var flag_mult := 1
var seal_mult := 1
for h in fs_fx:
if String(h.get("kind", "")) == "modify_ticket" and String(h.get("mode", "")) == "flag_mult":
flag_mult *= maxi(1, int(h.get("value", 1)))
if fs_misses == 0 and fs_hits > 0 and flag_mult > 1:
strike_value *= flag_mult # 完美布防:同轮全中旗伤倍增(全对推理奖励)
for fevt in oc._facts_flags:
if String(h.get("kind", "")) == "modify_ticket" and String(h.get("mode", "")) == "seal_mult":
seal_mult *= maxi(1, int(h.get("value", 1)))
if fs_misses == 0 and fs_hits > 0 and seal_mult > 1:
strike_value *= seal_mult # 完美布防:同轮全中封印伤倍增(全对推理奖励)
for fevt in oc._facts_seals:
if not bool(fevt.get("hit", false)):
continue
var st_t := oc._damage.open_ticket(DamageController.KIND_BOARD_TYPED, "feat_flag_strike",
"战旗军规", "board", strike_value, DamageController.TARGET_FRONT_ALIVE, ri,
var st_t := oc._damage.open_ticket(DamageController.KIND_BOARD_TYPED, "feat_seal_strike",
"战封印军规", "board", strike_value, DamageController.TARGET_FRONT_ALIVE, ri,
fevt.cell, "normal")
oc._submit(st_t, false)
# round_end 发射+施加(内容数据驱动扩展批 T3——每轮结算收口:熔火之躯反打单开立〔open_ticket
@@ -232,26 +232,26 @@ func run(ri: int) -> void:
oc._damage.flush() # 授予段界结账(案C 后本段伤害单据全数暂存,此处对空队 no-op——保留作防御性段界)
# 旗判定(迭代 12 T3 标旗流本体随段迁入——原编排器 _judge_flag 逐字搬运,仅宿主引用加前缀;
# 唯一调用面=本段 run 的 facts_flags 循环)。
func _judge_flag(evt: Dictionary, ri: int) -> void:
# 封印判定(迭代 12 T3 封印流本体随段迁入——原编排器 _judge_seal 逐字搬运,仅宿主引用加前缀;
# 唯一调用面=本段 run 的 facts_seals 循环)。
func _judge_seal(evt: Dictionary, ri: int) -> void:
var cell: Vector2i = evt.cell
if evt.hit:
if oc._flag_consumed.has(cell):
return # 去重跳过(资格以雷格实例为键——排雷玩法 诚实重复上报的对侧);零明细行
oc._flag_consumed[cell] = true
if oc._seal_consumed.has(cell):
return # 去重跳过(资格以危险格实例为键——排雷玩法 诚实重复上报的对侧);零明细行
oc._seal_consumed[cell] = true
oc._perfect_hit_true += 1
# flag_hit 发射+施加(内容数据驱动扩展批 T3——桶序固定承载等价:殉爆〔T4 秘宝面〕→
# seal_hit 发射+施加(内容数据驱动扩展批 T3——桶序固定承载等价:殉爆〔T4 秘宝面〕→
# 净化改写 → 图谱额外资源 → 基线兑付)
var fh_hits := oc._fx_emit("flag_hit", {"cell": cell, "round": ri,
var fh_hits := oc._fx_emit("seal_hit", {"cell": cell, "round": ri,
"terrain_kind": oc._harmful_adjacent(cell)})
# 标中殉爆(迭代 11 同调批 T5 → 内容数据驱动扩展批 T4:订阅施加——行 triggers open_ticket
# channel blast value_from magnitude;每标中一面旗全体存活敌吃 N 群伤,无类型走 blast 通道。
# channel blast value_from magnitude;每标中一面封印全体存活敌吃 N 群伤,无类型走 blast 通道。
# 迭代 20 T1——封印爆裂〔迁入专长〕同面消费:行 triggers open_ticket channel blast)
for h in oc._hits_of(fh_hits, "open_ticket"):
if String(h.get("channel", "")) != "blast":
continue
var fbt := oc._damage.open_ticket(DamageController.KIND_BOARD_BLAST, "feat_flag_burst", "封印爆裂", "board",
var fbt := oc._damage.open_ticket(DamageController.KIND_BOARD_BLAST, "feat_seal_burst", "封印爆裂", "board",
int(h.get("value", 0)), DamageController.TARGET_ALL_ALIVE, ri, cell, "", 0, "highlight", {})
oc._submit(fbt, false)
# 迭代 20 T1——标中新施加面:counter_add〔血税 标中+1 攻——bonus_delta atk 轴〕/
@@ -282,20 +282,20 @@ func _judge_flag(evt: Dictionary, ri: int) -> void:
if oc.treasure_inventory != null and not types.is_empty():
oc.treasure_inventory.apply_overlay("reward_preview", board_p.neighbors8(cell))
oc._emit_row("fx_preview_area", "", cell, ri, "示警烟火·周围资源类型", 0, {"types": types}, "")
# 雷区图谱(迭代 12 T3 标旗流——标中雷额外获得一份格面资源:插旗开始产出对标开格;
# 雷区图谱(迭代 12 T3 封印流——标中雷额外获得一份格面资源:封印开始产出对标开格;
# 复用 grant 单点,额外兑付走同一 _grant_routed 正向路由)
for h in fh_hits:
if String(h.get("kind", "")) == "grant_material":
_apply_material_hit(h, cell, ri, "flag_hit")
_apply_material_hit(h, cell, ri, "seal_hit")
var snap = oc._reward_economy.grant(cell)
if not (snap is Dictionary):
return
oc._grant_routed(snap, "positive", "flag_hit", cell, ri, "flag_hit")
oc._grant_routed(snap, "positive", "seal_hit", cell, ri, "seal_hit")
else:
oc._perfect_hit_false += 1
if oc._flag_miss_seen.has(cell):
if oc._seal_miss_seen.has(cell):
return # 每格实例一次去重(归因行与负向兑付同键——防逐轮惩罚轰炸)
oc._flag_miss_seen[cell] = true
oc._seal_miss_seen[cell] = true
# 完美保险(迭代 20 T4——本战下一次标错不破坏完美资格:_perfect_hit_false 回退=本次标错
# 对完美谓词无痕;只保一次/件,节点级用完即止)
if oc._fx_insurance > 0:
@@ -305,19 +305,19 @@ func _judge_flag(evt: Dictionary, ri: int) -> void:
var hl := ""
if oc._perfect_hit_false == 1:
hl = "perfect_broken" # 本板完美谓词已破的归因锚
oc._emit_row("flag_miss", "flag_miss", cell, ri, "标旗未中", 0, {}, hl)
# 危险直觉·守挂点(迭代 21 T3——flag_missed 发射:契约 12 触发主语=「当回合发生的标错
# 动作」——去重闸=新旧标错判别器〔后续轮重判旧旗被闸挡回零触发〕;双条件②「结算时旗仍在
# 场」由发射面既有过滤承载〔被挖开的旗已随挖掘擦除零兑付〕;grant_ap mode:queue 下一轮
oc._emit_row("seal_miss", "seal_miss", cell, ri, "封印未中", 0, {}, hl)
# 危险直觉·守挂点(迭代 21 T3——seal_missed 发射:契约 12 触发主语=「当回合发生的标错
# 动作」——去重闸=新旧标错判别器〔后续轮重判旧封印被闸挡回零触发〕;双条件②「结算时封印仍在
# 场」由发射面既有过滤承载〔被挖开的封印已随挖掘擦除零兑付〕;grant_ap mode:queue 下一轮
# 开轮兑现——末轮判出无下一轮=合法作废自动成立〔编排器每战新建,队列不跨战〕;限次=合成行
# adventure 域计数器闸)
for h in oc._fx_emit("flag_missed", {"cell": cell, "round": ri}):
for h in oc._fx_emit("seal_missed", {"cell": cell, "round": ri}):
if String(h.get("kind", "")) == "grant_ap" and String(h.get("mode", "")) == "queue":
oc.action_economy.queue_ap_delta(int(h.get("value", 0)))
var snap = oc._reward_economy.grant(cell)
if not (snap is Dictionary):
return
oc._grant_routed(snap, "negative", "flag_miss", cell, ri, "")
oc._grant_routed(snap, "negative", "seal_miss", cell, ri, "")
# counter_add 三行组单点(原五位分支本体逐字收拢——kind 分派随段归位 P1-1)
@@ -326,7 +326,7 @@ func _apply_counter_hit(h: Dictionary) -> void:
oc._fx.counter_add(String(h.get("counter_id", "")), int(h.get("value", 0)) if set_v == null else int(set_v), set_v != null)
# grant_material 兑付块单点(原三位分支本体逐字收拢——source_class 两态 safe_cell/flag_hit 由参承载)
# grant_material 兑付块单点(原三位分支本体逐字收拢——source_class 两态 safe_cell/seal_hit 由参承载)
func _apply_material_hit(h: Dictionary, cell: Vector2i, ri: int, source_class: String = "safe_cell") -> void:
var g_snap = oc._reward_economy.grant(cell)
if g_snap is Dictionary:
+1 -1
View File
@@ -39,7 +39,7 @@ func apply_effect(effect_id: String, source: Dictionary, duration_override: int
# 授予时快照(抗表重载——EC9)
var snap_m: int = int(row.magnitude)
if source.has("magnitude_override"):
snap_m = int(source["magnitude_override"]) # 旗溢价贯穿(迭代14② T4——标中旗 percent 形态同乘快照;缺省路径零扰动)
snap_m = int(source["magnitude_override"]) # 封印溢价贯穿(迭代14② T4——封印命中 percent 形态同乘快照;缺省路径零扰动)
var snap_unit: String = String(row.unit)
var snap_display: String = String(row.display_name)
match policy:
+1 -1
View File
@@ -7,7 +7,7 @@ extends RefCounted
var _items: Array[Variant] = [] # [{handle, item_id, used}]——受理序;Used 保留只读
var _handle_seq := 0 # 受理序句柄(单调递增——零随机)
var _reveal_cells := {} # 真视集:Vector2i → true(惩罚格标记——插旗仍走既有 flag 路径)
var _reveal_cells := {} # 真视集:Vector2i → true(危险格标记——封印仍走既有 seal 路径)
var _preview_cells := {} # 预读集:Vector2i → true(奖励身份徽章呈现挂点)
var _last_reject := "" # 诊断面(最近一次拒绝原因——呈现反馈取数)
+1 -1
View File
@@ -7,7 +7,7 @@ extends RefCounted
# 死亡事件(伤害管线单据化批 2026-09-24 用户裁决③——单位自行宣布死亡):apply_damage 扣血归零、
# alive 翻 false 的同一时点发出;载荷 = 单位句柄(Dictionary 引用,is_hero 区分主角/敌)。
# 消费方 = 伤害控制器(怪死 → 其未结单据作废〔死亡即不动手〕;主角死 → 兑付终止旗)。
# 消费方 = 伤害控制器(怪死 → 其未结单据作废〔死亡即不动手〕;主角死 → 兑付终止封印)。
signal unit_died(u: Dictionary)
var db: ConfigDb