merge(迭代 32 卡①): 代码注释禁用词清扫批合回

五词换代(完美排雷→完美封印/板级→整图/门票→专长凭证/正主→现行事实源/落籍→已入库)+
S11 代码注释禁用词静态扫描闸新增(先落规则再见红后清零)+N7 归因锚有效误标判据修正+两态文案+A10–A12。
代码域闸全绿(回执 9a4eddba),parity 落 skip(契约 12:合回后另立用户批准重锚批)。
This commit is contained in:
DKChan committed 2026-10-20 22:35:55 +08:00
commit 880ee91b20
43 files changed
+445 -114

No files matched your search

+12 -12
View File
@@ -56,7 +56,7 @@ var _time_sand_queue := 0 # 时沙待注入(回合级——下
var _save_manager = null # 断点承载注入(null = 不写断点;模拟/冒烟缺省)
var _resume_bp := {} # 续玩快照暂存(resume_adventure → _start_current_level 单跳传递)
# ---- 三选一停点态 ----
var _curio_taken := {} # 件获取次数计数(kind 维度派生账本——断点附随件与摘要读数承载;池库存正主=_curio_deck)
var _curio_taken := {} # 件获取次数计数(kind 维度派生账本——断点附随件与摘要读数承载;池库存现行事实源=_curio_deck)
var _curio_deck: Array[Variant] = [] # 令牌池(TP0 v2.2——持久多重集,元素 {"kind","tier"};洗牌抽候选/选中踢池未选回池/池耗即枯竭;商店货架同库共账;规则改写型特性带=relic 令牌随表追加)
var _feats: Array[String] = [] # 特性持有集(局内随机渠道获得、冒险级跨关携带、收束清零;🔴 层级纪律承载面:不入同调位不入局外树)
var _tree_rules: Array[Variant] = [] # 树 rule 键集(Fork 激活侧 effects 透传;下传 enc.tree_rules 消费面)
@@ -64,9 +64,9 @@ var _curio_pending := {} # 停点态:{"candidates": Array, "p
var _curio_result := {} # 已兑付待推进态(curio_resolved 载荷副本——中断续玩不重兑付)
var _keystone_drops: Array[Variant] = [] # Boss 逐杀掉落明细(additive 摘要键 keystone_drops——结算明细/复盘归因数据源)
var _node_curio_pending := {} # 奖励节点三选一停点态(共享池;断点不载:续玩重建到选路停点重走,恢复的计数器保证同候选重抽)
var _feat_ticket_pity := 0 # 门票保底计数(T5——展示次数:节点开面/货架抽签/刷新重展各计一次;触发清零)
var _feat_ticket_sold := 0 # 门票限购计数(T5——选中/买下即 +1;拿到即开不进持有集)
var _feat_ticket_appeared := 0 # 门票出现计数(T5——quota 上限面)
var _feat_ticket_pity := 0 # 专长凭证保底计数(T5——展示次数:节点开面/货架抽签/刷新重展各计一次;触发清零)
var _feat_ticket_sold := 0 # 专长凭证限购计数(T5——选中/买下即 +1;拿到即开不进持有集)
var _feat_ticket_appeared := 0 # 专长凭证出现计数(T5——quota 上限面)
var _shop_mode := "legacy" # 商店模式("legacy"=固定三件加定价〔off 对照承载,缺省零扰动〕/"pool"=共享池抽 3 买 1 重抽;真实入口 main.gd 显式投 pool)
@@ -472,7 +472,7 @@ func pool_stock(kind: String) -> int:
# 未知 kind 拒绝(货架词表封闭的前置防御)。
func _consume_pool_purchase(kind: String, tier: String) -> bool:
if kind == "feat_ticket":
return _redeem_feat_ticket() # T5——门票买下即开(零摘牌不进持有集;临时停点开专长三选一面)
return _redeem_feat_ticket() # T5——专长凭证买下即开(零摘牌不进持有集;临时停点开专长三选一面)
if not db.adventure.CURIO_KINDS.has(kind):
push_error("[冒险结构] 商店购买消费拒绝——件不在池词表: %s" % kind)
return false
@@ -659,7 +659,7 @@ func _finish_node_bonus(picked: String, value: int, tier: String = "", feat_id:
# 自动回池;池耗即枯竭——余库不足 N kind 时出齐剩余,空库 = 零候选合法态)。候选 = 令牌字典
# {"kind","tier"}。消费 reward_pick 封闭流——一次停点恰一 scope,同种子同候选。
# 渠道白名单过滤(渠道轴定位:关底 Boss 击败=通关渠道只出专长(feat 令牌);
# 奖励节点/商店只出属性值提升+资源(非 relic 令牌),专长在非 Boss 渠道只以门票形态出现(T5)。
# 奖励节点/商店只出属性值提升+资源(非 relic 令牌),专长在非 Boss 渠道只以专长凭证形态出现(T5)。
# 过滤式而非物理拆池:deck 物理结构/存档键 bonus_deck 混编余库/消耗语义(选中踢池未选回池)原样,
# 白名单在候选视图层生效——配额结构锚 TIER_QUOTAS 70/25/3=98 零改动(专长令牌本系活行追加面,在锚外)。
# 曝光槽:非 Boss 渠道三选一 = **2 格全池 + 1 格优秀档以上子池**
@@ -746,7 +746,7 @@ func _draw_curio_candidates(channel: String = "reward_node") -> Array:
sub = [] # 子池枯竭——曝光槽回退全池续取
continue
break # 全池枯竭——出齐剩余(池空直通同族兜底)
# T5 门票注入(非 Boss 渠道唯一专长入口——替换末位候选占一席,候选数不变;判定含 pity 清零记账)
# T5 专长凭证注入(非 Boss 渠道唯一专长入口——替换末位候选占一席,候选数不变;判定含 pity 清零记账)
if channel == "reward_node" and not out.is_empty() and _feat_ticket_consider(rg):
out[out.size() - 1] = {"kind": "feat_ticket", "tier": "excellent"}
return out
@@ -766,7 +766,7 @@ func _curio_cap_ok(kind: String, feat_id: String = "") -> bool:
return int(_curio_taken.get(kind, 0)) < cap
# —— 专长获取门票(T5——非 Boss 渠道的专长入口;四口径契约④:拿到即开不可囤积/
# —— 专长获取专长凭证(T5——非 Boss 渠道的专长入口;四口径契约④:拿到即开不可囤积/
# 罕见只在概率不在价格/一局限购一次/计步保底连续 5 展示未出第 6 必出——计数=展示次数、刷新
# 重展计入、节点与商店共享、触发清零、限购满不再触发。调用即一次展示判定与记账)——
func _feat_ticket_consider(rg: RandomNumberGenerator) -> bool:
@@ -783,7 +783,7 @@ func _feat_ticket_consider(rg: RandomNumberGenerator) -> bool:
return offer
# 门票兑付(拿到即开——停点候选替换为专长三选一;不进持有集/零摘牌;限购计数 +1):
# 专长凭证兑付(拿到即开——停点候选替换为专长三选一;不进持有集/零摘牌;限购计数 +1):
# 节点停点=本地替换(呈现层读在途态重绘);店内无停点=临时停点 + payload.context 标记(呈现层
# 重 present 无胜利总结面,确认后只关面回店)。
func _redeem_feat_ticket() -> bool:
@@ -791,7 +791,7 @@ func _redeem_feat_ticket() -> bool:
_feat_ticket_pity = 0
var cands: Array[Variant] = _draw_curio_candidates("level_clear") # 专长池(与通关渠道同白名单)
if cands.is_empty():
push_error("[冒险结构] 门票兑付失败——专长池空集")
push_error("[冒险结构] 专长凭证兑付失败——专长池空集")
_feat_ticket_sold -= 1
return false
if not _node_curio_pending.is_empty():
@@ -1028,8 +1028,8 @@ func _snapshot_breakpoint() -> void:
"bonus_deck": _curio_deck.duplicate(true), # 令牌池余库(TP0 v2.2——池消耗即上限,续玩余库逐枚还原)
"relics": _feats.duplicate(), # 特性持有集(局内随机渠道获得态续玩防丢失)
"bonus_pending": _curio_pending.duplicate(true), # 三选一停点态(空 = 无停点——选择停点可中断契约)
"feat_ticket_pity": _feat_ticket_pity, # T5 门票保底计数(扩列向后兼容——旧档缺键缺省 0)
"feat_ticket_sold": _feat_ticket_sold, # T5 门票限购计数(同上)
"feat_ticket_pity": _feat_ticket_pity, # T5 专长凭证保底计数(扩列向后兼容——旧档缺键缺省 0)
"feat_ticket_sold": _feat_ticket_sold, # T5 专长凭证限购计数(同上)
"adventure_counters": run_ledger.adventure_counters.duplicate(true), # adventure 域计数器随断点(续玩跨战账不丢)
"vision_counter_used": int(run_ledger.vision_counter.get("used", 0)), # 真视使用计数随断点(续玩跨战账不丢)
"refresh_stock": _refresh_stock, # 刷新存量账随断点
+12 -12
View File
@@ -62,7 +62,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 _seal_consumed := {} # 兑付资格以危险格实例为键(板级;换图重置)
var _seal_consumed := {} # 兑付资格以危险格实例为键(整图;换图重置)
# 同调装配运行态(begin_battle 经 enc.attunement 注入;off=空集全部效果零扰动)
var attunement: Array[Variant] = [] # 装备中同调件 item_id 集
var _ledger: RunLedger = null # 共享四本账(begin_battle 经 enc["ledger"] 持引用;缺省新账=调试面)
@@ -259,7 +259,7 @@ func attune_convert_state() -> Dictionary:
# 流派档位质变=FeatsEngine 纯函数求值
# (档位机制本身系引擎基建非内容效果),两运行态注伤害控制器:档一 rider+1 / 档二 rider 可暴击)
var _seal_miss_seen := {} # 封印未中行去重(每安全格实例一次)
var _perfect_hit_true := 0 # 板级完美排雷计数(install 重置)
var _perfect_hit_true := 0 # 整图完美封印计数(install 重置)
var _perfect_hit_false := 0
var _perfect_forgiven := 0 # 完美保险豁免累计(单向累加自有账;契约 2 禁改写 _perfect_hit_false)
var _hero_atk := 0 # 主角攻击基值快照(encounter.hero_atk;E4 收口——表值运行期不可变)
@@ -439,7 +439,7 @@ func _install_new_board(during_round := false) -> bool:
# 全局作用域变异决议(装板序内前置于 roll_board:图级偏斜旋钮须在资源
# 摇号前定型;全局门控与板无关〔节点身份+unlock+board_types〕,确定性取用零取号)。
# boss/elite 图命中全局型 → 写 board_variants(_place_variants 见非空即跳 region=替换制);
# 全局池空 → 板级字段留空(_place_variants 原路走 region=兜底回落合法态)。
# 全局池空 → 整图字段留空(_place_variants 原路走 region=兜底回落合法态)。
_resolve_global_variant(board)
var res: Dictionary[Variant, Variant] = minesweeper.install_board(board, during_round)
if not res.accepted:
@@ -464,7 +464,7 @@ func _install_new_board(during_round := false) -> bool:
_place_variants(board)
_seal_consumed.clear() # 换图新雷 = 新实例,资格自然重置(行动经济 Rule 8)
_seal_miss_seen.clear()
_perfect_hit_true = 0 # 板级完美计数随 install 重置
_perfect_hit_true = 0 # 整图完美计数随 install 重置
_perfect_hit_false = 0
_perfect_forgiven = 0 # 豁免累计随 install 重置(domain=battle 每战清零)
_board_from_midround_refresh = during_round # 轮内刷新装板 = 刷新图(清图奖励不发);常规换图/开战装板 = false
@@ -476,7 +476,7 @@ func _install_new_board(during_round := false) -> bool:
# ---- 全局作用域变异 决议(装板序内 roll_board 之前)----
# boss/elite 图命中全局型:写板级 board_variants(恰一行)+ 注入资源面偏斜旋钮(图级投影在
# boss/elite 图命中全局型:写整图 board_variants(恰一行)+ 注入资源面偏斜旋钮(图级投影在
# RewardEconomy.roll_board 消费);不消费 variant_place 流(全局取用确定性——零取号合法态)。
# 全局池空(含 L1 Boss 免疫/unlock 门控未达):字段留空、旋钮归零——_place_variants 原路走
# region 布置 = 兜底回落(保守向后兼容,防配置缺行静默裸奔——provisional)。
@@ -500,8 +500,8 @@ func _resolve_global_variant(board: BoardData) -> void:
# (关卡序号−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 图全局池空回落)。
# 替换制(全局作用域变异):整图 board_variants 非空 = 本图已带全局型,跳过 region
# 布置(每图一型的整图推广:普通图=region,Boss/精英图=global);空 = 原路径(含 Boss 图全局池空回落)。
func _place_variants(board: BoardData) -> void:
if not board.board_variants.is_empty():
return
@@ -905,7 +905,7 @@ func _submit_flat_hero(source_class: String, display: String, power: int, cell:
# 封印判定批:标中 = 该危险格资源的正向兑付(每雷实例一次;高光 = 强制检查点);
# 标空 = "封印未中"归因行 + 该安全格资源的负向兑付(同键去重——每格实例一次)。
# 清图批:清图奖励对象恰好发一次;完美排雷追加第二份同身份兑付(grant_clear 本身恰一次)。
# 清图批:清图奖励对象恰好发一次;完美封印追加第二份同身份兑付(grant_clear 本身恰一次)。
# 高光行 value = 实际有效兑付份数(禁记名义值;首份零兑付 → 降级不发)。
func _grant_clear_reward(ri: int) -> void:
if _board_from_midround_refresh:
@@ -919,10 +919,10 @@ func _grant_clear_reward(ri: int) -> void:
if perfect and first_ok:
second_ok = _grant_clear_identity(snap, ri, "(完美第二份)")
if perfect and first_ok:
_emit_row("highlight", "clear_reward", Vector2i(-1, -1), ri, "完美排雷!清图奖励 ×2", (2 if second_ok else 1), {}, "perfect_sweep")
_emit_row("highlight", "clear_reward", Vector2i(-1, -1), ri, "完美封印!清图奖励 ×2", (2 if second_ok else 1), {}, "perfect_sweep")
# 清图奖励 = 板级独立对象(不属于任何一格):兑付形态按其 cell_type 的正向字典形态走,
# 清图奖励 = 整图独立对象(不属于任何一格):兑付形态按其 cell_type 的正向字典形态走,
# 行值全部取快照(身份批 roll 值)。返回实际有效兑付。
# 同步开单(sync)——完美第二份与高光份数的流程依赖需要同步读数(开单权归流程);
# 同步路径内先按序结清段内排队单据(B 模式保序),随后本批无视结账政策当场兑现。
@@ -1206,7 +1206,7 @@ func presentation_snapshot() -> Dictionary:
"hero": hero,
"enemies": enemies,
"hero_effects": _status_effects.query_active("hero").duplicate(true), # 值拷贝出户(快照消费方直改不渗入状态容器)
# 板级变异投影(呈现横幅/常驻角标唯一读取面;
# 整图变异投影(呈现横幅/常驻角标唯一读取面;
# 空数组 = 无全局型合法态:普通图与全局池空回落图恒空)
"board_variants": (minesweeper.current_board().board_variant_ids().duplicate()
if minesweeper.current_board() != null else ([] as Array[String])),
@@ -1289,7 +1289,7 @@ func _fatal(msg: String) -> void:
assert(false, msg)
## 完美排雷谓词(两账相减:有效误标=误标总数−豁免数)
## 完美封印谓词(两账相减:有效误标=误标总数−豁免数)
## 契约 2:_perfect_hit_false 保持纯净,豁免只累加 _perfect_forgiven,禁回退式改写与拦截式吞事件。
func perfect_sweep_earned() -> bool:
return _perfect_hit_false - _perfect_forgiven <= 0 and _perfect_hit_true >= 1
+2 -2
View File
@@ -14,7 +14,7 @@ var spawn_center := Vector2i.ZERO
var mines := {} # Vector2i → true
var digits := {} # Vector2i → int(安全格恒有值;数字恒真 = 8 邻雷数)
var variants := {} # Vector2i → Array[String](生成期快照,抗表重载)
var board_variants: Array[String] = [] # 板级变异 id 集(scope=global 整图作用域——
var board_variants: Array[String] = [] # 整图变异 id 集(scope=global 整图作用域——
# 战斗编排装板决议写入,随图生灭同 variants;普通图恒空)
var board_type := "" # 图类型标签(词表 v1.0:dungeon/underdark;玩家侧显示名「地图类型」)
var level_index: int = 1
@@ -50,7 +50,7 @@ func variant_ids(c: Vector2i) -> Array:
return variants.get(c, [])
# 板级变异只读出口(scope=global 行集——呈现快照/断言消费;空 = 无全局型合法态)
# 整图变异只读出口(scope=global 行集——呈现快照/断言消费;空 = 无全局型合法态)
func board_variant_ids() -> Array[String]:
return board_variants
+7 -7
View File
@@ -19,8 +19,8 @@ const TABLE_MANIFEST := {
"settlement": "settlement.tres",
"level": "level.tres",
"difficulty": "difficulty.tres",
"shop": "shop.tres", # ShopTable(表与 schema 落籍归数据面——本处只接线加载与消费)
"meta": "meta.tres", # MetaTable 四行族(schema 与表同源落籍)
"shop": "shop.tres", # ShopTable(表与 schema 已入库归数据面——本处只接线加载与消费)
"meta": "meta.tres", # MetaTable 四行族(schema 与表同源已入库)
"meta_tree": "meta_tree.tres", # MetaTreeTable 12 节点(定义表 Rule 4 豁免——天赋树.md v0.8 结构事实源)
"treasure_items": "treasure_items.tres", # TreasureItemTable 六行(定义表 Rule 4 豁免;罗盘 retired/失义件 deferred 标记见 schema 头注)
"adventure": "adventure.tres", # AdventureTable(大奖励与钥石行值单点)
@@ -39,10 +39,10 @@ var reward: RewardTable
var settlement: SettlementTable
var level: LevelTable
var difficulty: DifficultyTable
# ShopTable 类型化持用(schema class_name 已落籍;
# ShopTable 类型化持用(schema class_name 已已入库;
# 消费面一律走下方 shop_* 访问器,禁直读行集)
var shop: ShopTable = null
# MetaTable 类型化持用(schema 与表同源落籍)
# MetaTable 类型化持用(schema 与表同源已入库)
var meta: MetaTable
var meta_tree: MetaTreeTable # 类型化持用(消费面走 tree_node 访问器)
var treasure_items: TreasureItemTable # 类型化持用(消费面走 treasure_* 访问器)
@@ -277,7 +277,7 @@ func treasure_attunement_rows() -> Array:
# 行族四轴(数据契约,行集归 shop.tres):gp_drops {tier, mean, roll_min, roll_max} ·
# level_gp_bonus {level_index, bonus} · products {product_id, display_name, attr, flat_value,
# base_price, price_increment} · price_level_bonus {level_index, bonus}。
# 行族经 ShopTable 同名数组属性读取(.get 容缺——表落籍前查询恒空,掉落按 0 兑付的兜底在调用方)。
# 行族经 ShopTable 同名数组属性读取(.get 容缺——表已入库前查询恒空,掉落按 0 兑付的兜底在调用方)。
func shop_gp_drop_row(tier: String) -> Dictionary:
for row in _shop_rows("gp_drops"):
@@ -334,7 +334,7 @@ func shop_price_b() -> float:
return float(shop.price_b) if shop != null else 0.0
# 行族数组读取(ShopTable 属性名 = 行族名;表未落籍/属性缺失 = 空集非错误态)
# 行族数组读取(ShopTable 属性名 = 行族名;表未已入库/属性缺失 = 空集非错误态)
func _shop_rows(family: String) -> Array:
if shop == null:
return []
@@ -387,7 +387,7 @@ func _meta_rows(family: String) -> Array:
# 设计权威 design/gdd/systems/跨局循环/锻造工坊.md v0.6:默认值归本表常驻持有(hero.tres 两轴已删——
# 契约 2);路线行族=攻防数值成长唯一数据面(契约 1)。数值全 provisional 归 M4。
# 两根类型轴默认值(表未落籍=裸体态兜底 normal/light——与 ForgeTable 缺省值同口径)
# 两根类型轴默认值(表未已入库=裸体态兜底 normal/light——与 ForgeTable 缺省值同口径)
func forge_default_attack_type() -> String:
return String(forge.default_attack_type) if forge != null else "normal"
+1 -1
View File
@@ -97,7 +97,7 @@ func cross_validate(loaded: Dictionary, push_diag: Callable) -> void:
for row in shp.pool_shelf:
var kind: String = String(row.get("kind_id", ""))
if kind == "feat_ticket":
continue # 门票豁免——货架专属机制种(非 deck 池种,零摘牌拿到即开),不入 CURIO_KINDS
continue # 专长凭证豁免——货架专属机制种(非 deck 池种,零摘牌拿到即开),不入 CURIO_KINDS
if not adv.CURIO_KINDS.has(kind):
push_diag.call("error", "shelf_dangling_kind", "ShopTable.pool_shelf kind %s 不在 AdventureTable.CURIO_KINDS(共享池牌库外种)" % kind)
continue
+5 -5
View File
@@ -48,7 +48,7 @@ var _hero_armor_type := ""
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;节点与商店共享计数)
var _pool_ticket := Callable() # 专长凭证展示判定 handler(T5——AdventureRunner._feat_ticket_consider;节点与商店共享计数)
var _pool_cap_check := Callable() # cap 检查回调(AdventureRunner._curio_cap_ok;cap 满剔出货架候选视图)
var _shop_shelf: Array[Variant] = [] # 当前货架(进店抽 3/买后全重抽——令牌字典 {"kind","tier"} 序,同 kind 去重每 kind 至多一卡)
var _shop_visit_buys := 0 # 本次进店已购次数(A 链定价输入——离开清零)
@@ -775,7 +775,7 @@ func player_request_refresh() -> bool:
func set_shop_pool(deck_ref: Array, consume_handler: Callable, ticket_consider: Callable = Callable(), cap_check: Callable = Callable()) -> void:
_pool_deck = deck_ref
_pool_consume = consume_handler
_pool_ticket = ticket_consider # T5——门票展示判定(AdventureRunner._feat_ticket_consider;同流取数复用 shop_draw scope)
_pool_ticket = ticket_consider # T5——专长凭证展示判定(AdventureRunner._feat_ticket_consider;同流取数复用 shop_draw scope)
_pool_cap_check = cap_check
_shop_pool_mode = true
@@ -820,12 +820,12 @@ func _shop_draw_shelf() -> void:
_shop_shelf.append(tok)
if _shop_shelf.size() >= 3:
break
# T5 门票注入(非 Boss 渠道唯一专长入口——货架与节点共享保底计数;判定含 pity 记账,替换末位占一席。
# T5 专长凭证注入(非 Boss 渠道唯一专长入口——货架与节点共享保底计数;判定含 pity 记账,替换末位占一席。
# 取数复用本函数 shop_draw scope 的 stream——零新 scope,确定性序列不漂移)
if _pool_ticket.is_valid() and not _shop_shelf.is_empty() and bool(_pool_ticket.call(stream)):
_shop_shelf[_shop_shelf.size() - 1] = {"kind": "feat_ticket", "tier": "excellent"} # 门票占位档名 = excellent
_shop_shelf[_shop_shelf.size() - 1] = {"kind": "feat_ticket", "tier": "excellent"} # 专长凭证占位档名 = excellent
# 商店渠道无专长直接投放(渠道白名单:商店渠道只出属性值提升+资源,专长在非 Boss
# 渠道只以门票形态出现——奇物.md v2.7/A4 断言;专长商店入口=T5 门票)。
# 渠道只以专长凭证形态出现——奇物.md v2.7/A4 断言;专长商店入口=T5 专长凭证)。
# 库存余量(TP0——令牌库内该 kind 剩余枚数单点读;未注入/防御读 = 0)
+1 -1
View File
@@ -123,7 +123,7 @@ func install_board(b: BoardData, during_round := false) -> Dictionary:
_spawn = {}
for c in b.spawn_cells():
_spawn[c] = true
# 初始展开:O₀ = R₀ 恰等(免费/计入清图/无格面资源/永不触雷);板级一次跨轮不重放
# 初始展开:O₀ = R₀ 恰等(免费/计入清图/无格面资源/永不触雷);整图一次跨轮不重放
for c in _initial_expansion_order(b):
_revealed[c] = {"kind": "safe", "digit": b.digit_of(c)}
cell_revealed.emit({
+2 -4
View File
@@ -296,10 +296,6 @@ func _judge_seal(evt: Dictionary, ri: int) -> void:
if oc._seal_miss_seen.has(cell):
return # 每格实例一次去重(归因行与负向兑付同键——防逐轮惩罚轰炸)
oc._seal_miss_seen[cell] = true
var hl := ""
if oc._perfect_hit_false == 1:
hl = "perfect_broken" # 本板完美谓词已破的归因锚
oc._emit_row("seal_miss", "seal_miss", cell, ri, "封印未中", 0, {}, hl)
# 危险直觉·守挂点(seal_missed 发射:契约 12 触发主语=「当回合发生的标错
# 动作」——去重闸=新旧标错判别器〔后续轮重判旧封印被闸挡回零触发〕;双条件②「结算时封印仍在
# 场」由发射面既有过滤承载〔被挖开的封印已随挖掘擦除零兑付〕;grant_ap mode:queue 下一轮
@@ -315,6 +311,8 @@ func _judge_seal(evt: Dictionary, ri: int) -> void:
_apply_counter_hit(h) # 豁免同时记账(insurance_used battle 域——下次标错 when lt 1 不再命中)
oc._emit_row("insurance_used", "seal_miss", cell, ri, "完美保险·标错豁免", 0,
{"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)
var snap = oc._reward_economy.grant(cell)
if not (snap is Dictionary):
return
+4 -4
View File
@@ -1,5 +1,5 @@
## VERTICAL SLICE - NOT FOR PRODUCTION
## 秘宝关卡域库存+板级叠加层:全程零随机零特判,同操作序列重放库存与叠加层投影逐位相同(垂直切片验证:道具全程确定性)。
## 秘宝关卡域库存+整图叠加层:全程零随机零特判,同操作序列重放库存与叠加层投影逐位相同(垂直切片验证:道具全程确定性)。
## Used 不可逆——consume 后保留只读,枚举序=受理序;库存关卡域跨战斗延续、关卡末清空,叠加层随图消亡(清空编排归调用方,本类只供 clear/reset/retire_overlays)。
## 零特判纪律:本类不写任何其他系统状态,排雷玩法代码零本系统分支;相位检查归战斗编排 use_treasure——validate_use 是纯谓词零消耗。
class_name TreasureItems
@@ -38,7 +38,7 @@ func add_item(item_id: String, source_class: String, cell: Vector2i, round_index
# ---- 库存读面(C 区槽位常读)----
# 枚举序=受理序;Used 保留只读;逐件含 usable 板级零信息预判(调用方喂板面回调)
# 枚举序=受理序;Used 保留只读;逐件含 usable 整图零信息预判(调用方喂板面回调)
func query_inventory(board_has_preview_info: Callable) -> Array:
var out: Array[Variant] = []
for rec in _items:
@@ -54,13 +54,13 @@ func inventory_count() -> int:
# 持有数(同调位上限判定读数:只计未消耗件;Used 不可逆即不再持有)
# 同调位守卫谓词(读取源 transitional_sync_slots 过渡键已随表退役;
# 容量闸正主 = 营地装配面 Attunement.slots 满位禁选。函数体保留空壳防四散引用误删遗漏——
# 容量闸现行事实源 = 营地装配面 Attunement.slots 满位禁选。函数体保留空壳防四散引用误删遗漏——
# 冒烟「过渡键读取点归零」断言覆盖)
# 持有件记录(奖励节点替换子交互数据源:只列未消耗件,handle 定位丢弃目标)
# 丢弃(同调位替换交互节点渠道——腾位收新;与 Used 消费分轨的一次性移除,快照不再含)
# 板级零信息可用性预判(图标置灰/入口禁用判据——GDD「整张图都没东西可看」级):
# 整图零信息可用性预判(图标置灰/入口禁用判据——GDD「整张图都没东西可看」级):
# 真视恒可用(零雷窗口=信息非空);AP 类恒可用(组合判据取 OR——任一效果有信息即可用);
# 纯预读行 = 板上还有未开格才可用。(积点件不入使用面——自动兑付无玩家入口)
func _usable_now(item_id: String, board_has_preview_info: Callable) -> bool:
+3 -3
View File
@@ -89,12 +89,12 @@ const TIER_QUOTAS := {"common": 70, "excellent": 25, "rare": 7, "legendary": 0}
@export var curio_cap_rap: int = 99 # [1,99]
@export var curio_cap_time_sand: int = 99 # [1,99]
# —— 专长获取门票(非 Boss 渠道的专长入口形态,奇物.md v2.7 渠道轴定位;
# —— 专长获取专长凭证(非 Boss 渠道的专长入口形态,奇物.md v2.7 渠道轴定位;
# 四口径契约④:拿到即开不可囤积/罕见只在概率不在价格/一局限购一次/计步保底连续 5 展示未出
# 第 6 必出(计数=展示次数、刷新重展计入、节点与商店共享、触发清零、限购满不再触发)。
# 四参数全部 provisional 归曲线调参;limit/pity_step 系口径定死值,调参可复议)——
@export var feat_ticket_chance: int = 20 # [0,100] knob(provisional)——每次非 Boss 渠道展示门票出现概率 %
@export var feat_ticket_quota: int = 2 # [0,9] knob(provisional)——每局门票出现次数上限
@export var feat_ticket_chance: int = 20 # [0,100] knob(provisional)——每次非 Boss 渠道展示专长凭证出现概率 %
@export var feat_ticket_quota: int = 2 # [0,9] knob(provisional)——每局专长凭证出现次数上限
@export var feat_ticket_limit: int = 1 # [1,9] knob(口径定死=1)——一局限购(选中/买下)次数
@export var feat_ticket_pity_step: int = 5 # [1,99] knob(口径定死=5)——保底步数(连续未出展示次数)
+1 -1
View File
@@ -5,7 +5,7 @@ class_name SettlementTable
extends Resource
@export var equivalence_ref_damage: int = 12 # [1,999] knob——当量参考敞口 D_ref
@export var p_perfect: int = 200 # {100,200} percent——完美排雷第二份兑付触发(载体域)
@export var p_perfect: int = 200 # {100,200} percent——完美封印第二份兑付触发(载体域)
@export var seal_hit_premium: int = 100 # [100,999] percent knob——封印命中兑付溢价 ×(1+(n−100)/100):125=×1.25(条款3 条件二封印溢价形态 a,provisional);100=无溢价直通零扰动。只乘封印命中正向兑付(flat power/percent 幅度/gp 缓冲三形态),标空/挖格/清图不乘;雷安同池冻结不动
@export var segment_order: PackedStringArray = [] # 结构冻结——段序恒等校验违例致命
@export var source_label_map: Dictionary[String, Variant] = {} # 结构冻结——source_class→文案键
+1 -1
View File
@@ -15,7 +15,7 @@ extends Resource
const GP_TIERS := ["normal", "elite", "boss"]
const PRODUCT_ATTRS := ["atk", "max_hp", "armor_class"]
const SHELF_KINDS := ["attack", "max_hp", "armor", "ap", "feat_ticket", "heal"] # 货架候选词表(四切边后种 + 门票行 + heal 补回复〔默认原则:分类内成员都入该渠道池〕)
const SHELF_KINDS := ["attack", "max_hp", "armor", "ap", "feat_ticket", "heal"] # 货架候选词表(四切边后种 + 专长凭证行 + heal 补回复〔默认原则:分类内成员都入该渠道池〕)
func validate() -> Array:
+2 -2
View File
@@ -17,7 +17,7 @@ const SCOPES := ["region", "global"]
# resource_face = 资源面偏斜(全局作用域变异——整图修摇号分布:生存族按 effect_value%
# 下调、进攻族等比上浮重归一,不动任何格子数据;effect_value 语义随行域切换,不适用 flat_damage [1,9])
const EFFECTS := ["none", "flat_damage", "hide_number", "resource_face"] # 效果词表:直伤 / 数字遮蔽 / 资源面偏斜(可扩充)
const TRIGGERS := ["on_open", "on_number_shown", "always"] # 触发词表:被打开即触发 / 打开且显示数字时触发 / 板级恒在无触发时机(可扩充)
const TRIGGERS := ["on_open", "on_number_shown", "always"] # 触发词表:被打开即触发 / 打开且显示数字时触发 / 整图恒在无触发时机(可扩充)
const PLACEMENTS := ["closed_only", "any"] # 布置约束词表:只落布置时未开格 / 任意格(可扩充)
@@ -76,7 +76,7 @@ func validate() -> Array:
if row.get("placement", "") != "any":
errs.append({"category": "cross_field", "message": "%s scope=global ⇒ placement 必为 any(整图无格子布置域)" % vid})
if row.get("trigger", "") != "always":
errs.append({"category": "cross_field", "message": "%s scope=global ⇒ trigger 必为 always(板级恒在,无触发时机)" % vid})
errs.append({"category": "cross_field", "message": "%s scope=global ⇒ trigger 必为 always(整图恒在,无触发时机)" % vid})
return errs
+1 -1
View File
@@ -101,7 +101,7 @@ price_b = 1.1
; (4+2)+(2+2)+(4+2)=16、全购欲望 ≈ 26–34——取舍空间成立;量级与方差全部 provisional-pending-曲线调参批
;(用户拍板骨架先行、数值整体后置,行值归 难度曲线与Boss)。
; GP 商店房重构批(M-4 迭代 11 第②批 T1,2026-10-03——需求卡片 v1.3 全裁 P1 共享池/P4 ceil/P5 甲):
; pool_shelf 行族 = 共享池货架(kind_id 词表 attack/max_hp/armor/ap/feat_ticket/heal 六行——heal 系 T6 商店补回复〔默认原则:分类内成员都要在它该出现的渠道池里,现行缺〕、GP 袋负交易切边计价物不自售、feat_ticket 系 T5 门票行〔非 Boss 渠道专长入口,价与其他商品相当=契约④口径②〕、
; pool_shelf 行族 = 共享池货架(kind_id 词表 attack/max_hp/armor/ap/feat_ticket/heal 六行——heal 系 T6 商店补回复〔默认原则:分类内成员都要在它该出现的渠道池里,现行缺〕、GP 袋负交易切边计价物不自售、feat_ticket 系 T5 专长凭证行〔非 Boss 渠道专长入口,价与其他商品相当=契约④口径②〕、
; 规则改写型特性三轴 doctrine 切边;库存=上限即库存,账本在 冒险结构 _bonus_taken 单点共享,行族不记库存);
; 价格公式换代(pool 模式)= ceil(base_price × b^(关序−1) × A^(本次进店已购次数)),price_a=1.8/price_b=1.1
; provisional 投值(零人工令改 provisional+后置追认,原 P5 甲研究拍板后置);products 行族 + 加法定价
@@ -86,7 +86,7 @@ func _skip_empty_stop() -> bool:
# 增量行在其上),确认后进三选一面(level-clear.md v2);续玩停点恢复同入口(零自动推进契约)
func on_presented(payload: Dictionary) -> void:
if String(payload.get("context", "")) == "feat_ticket":
# T5 店内门票兑付——临时停点开专长三选一面(零胜利总结面;确认后只关面回店)
# T5 店内专长凭证兑付——临时停点开专长三选一面(零胜利总结面;确认后只关面回店)
_pending_curio = payload.duplicate(true)
_curio_context = "feat_ticket"
if (_pending_curio.get("candidates", []) as Array).is_empty():
@@ -214,7 +214,7 @@ func on_pick_confirmed(item_id: String) -> void:
if not ok:
push_error("[bonus_interact] 三选一兑付被拒——见输出日志")
return
# T5——节点渠道门票拿到即开:候选已被替换为专长面,本地读在途态重绘(零 emit——context 保持 reward_node,
# T5——节点渠道专长凭证拿到即开:候选已被替换为专长面,本地读在途态重绘(零 emit——context 保持 reward_node,
# 后续兑付/确认仍走节点路径回图选路)
if _curio_context == "reward_node" and kind == "feat_ticket":
var st2: Dictionary[Variant, Variant] = adventure.node_curio_pending_state()
@@ -234,7 +234,7 @@ func on_continue() -> void:
curio_overlay.close_pick()
var adventure = _adventure_get.call()
if _curio_context == "feat_ticket":
return # T5 店内门票面——专长兑付完成后关面回店(无推进/无回图)
return # T5 店内专长凭证面——专长兑付完成后关面回店(无推进/无回图)
if _curio_context == "reward_node":
var runner = _runner_get.call()
if runner != null:
+2 -2
View File
@@ -37,7 +37,7 @@ static func refresh(stage: BattleStageView, info: HeroInfoPanelView, ap_bar: ApB
_refresh_out_of_battle(stage, info, ap_bar, runner, level_active, db)
return
var s: Dictionary[Variant, Variant] = settlement.presentation_snapshot()
# 地形变异常驻角标投影(板级变异快照 → 文案行;
# 地形变异常驻角标投影(整图变异快照 → 文案行;
# 空态隐藏=特性段先例;战斗全程常驻防中局遗忘)
info.set_board_variants(board_variant_lines(db, s.get("board_variants", [])))
var hero: Dictionary[Variant, Variant] = s.hero
@@ -102,7 +102,7 @@ static func feat_holdings_lines(db, held: Array) -> Array[String]:
return rlines
# 地形变异常驻角标文案(板级变异 id → 文案行;
# 地形变异常驻角标文案(整图变异 id → 文案行;
# 空集 = 空数组=空态隐藏。三行制:变异名+两行效果说明(C 区 240px 宽防裁切)。
# 文案用常规表述,不用俚语
static func board_variant_lines(db, ids: Array) -> Array[String]:
+1 -1
View File
@@ -911,7 +911,7 @@ func _on_round_started(ri: int) -> void:
# 巢穴预告呈现(装板报数量读数口消费:≥0=本战装配命中,toast 一次性提示)
if run_scope.settlement != null and int(run_scope.settlement.nest_preview_count) >= 0:
board_zone.toast.show_toast("巢穴预告——本板危险格共 %d 个" % int(run_scope.settlement.nest_preview_count), "info")
# 开战横幅——板级地形变异预告:首轮快照 board_variants
# 开战横幅——整图地形变异预告:首轮快照 board_variants
# 非空才发射(Boss 图恒带一型=替换制;普通图空快照零发射);轮横幅下方叠行,view 内判减动效。
if ri == 1 and run_scope.settlement != null:
var bvs: Array[Variant] = run_scope.settlement.presentation_snapshot().get("board_variants", [])
+1 -1
View File
@@ -89,7 +89,7 @@ func _consume_row(row: Dictionary) -> void:
stage.on_step_row_fx(row)
_play_cross_zone_fx(row) # 跨区因果演出(结算演出链批):开格之因 → 站桩之果
_play_settlement_mark(row) # 三轮 #3——逐条标出:Buff/Debuff 色环脉冲 → 伤害/治疗数字浮现
# 高光演出(战斗结算×3①② 占位):完美排雷横幅 / 封印命中加重弹道
# 高光演出(战斗结算×3①② 占位):完美封印横幅 / 封印命中加重弹道
if String(row.get("type", "")) == "highlight" and String(row.get("highlight", "")) != "skill_unlock":
board_zone.fx.play_perfect_sweep()
elif String(row.get("highlight", "")) == "seal_hit":
@@ -98,7 +98,7 @@ func attempt_use(handle: int, cell: Variant = null) -> void:
toast.show_toast("已使用 %s" % String(arow.get("display_name", "")), "info")
# 库存投影(C 区四槽取数面——字形=名首字;usable=板级零信息预判回调接排雷玩法只读面)
# 库存投影(C 区四槽取数面——字形=名首字;usable=整图零信息预判回调接排雷玩法只读面)
func inventory_projection() -> Array:
var runner = _runner_get.call()
if runner == null or runner.treasure_inventory == null:
+6 -1
View File
@@ -189,7 +189,12 @@ func _format_row(row: Dictionary, agg_count := -1) -> String:
var n := (" ×%d" % agg_count) if agg_count > 1 else ""
return prefix + "[s][color=#%s]⚠ %s%s[/color][/s]%s" % [UiKit.HEX_LOG_VOIDED, name_, n, suffix]
"seal_miss":
var body := "✗ %s%s" % [name_, "(本板完美排雷已破——归因此封印)" if hl == "perfect_broken" else ""]
var miss_note := ""
if hl == "perfect_broken":
miss_note = "(本图完美封印已破——清图奖励不再加倍)"
elif hl == "":
miss_note = "(首次封印落空已豁免——不计入完美封印判定)"
var body := "✗ %s%s" % [name_, miss_note]
return prefix + (_gold_frame(body) if hl == "perfect_broken" else "[color=#%s]%s[/color]" % [UiKit.HEX_LOG_ROUND, body])
"cast":
# 施放行:增益技 0 伤害施放,具名技能
+3 -3
View File
@@ -94,7 +94,7 @@ var facade_strip := false: # E 格开关(BoardView paint 按「已开
# 变异角标(程序化,tex_mutation 纹带槽暂留):
# "flat_damage" = 未开格伤害型变异,左下角危险红三角(开格有代价的策略纹理标记)
# 叠加层标记(秘宝板级叠加层的格面呈现;派生信息层零回写):
# 叠加层标记(秘宝整图叠加层的格面呈现;派生信息层零回写):
# "mine_reveal" = 真视格(危险红内缩框——「这格是雷」的确定决策标记,封印照常计费);
# "reward_preview" 格不复用本字段——预读徽章走 badge_glyph 注入(board_view refresh 注入 REWARD_BADGE 字形)
var treasure_mark := "":
@@ -108,8 +108,8 @@ var mutation_mark := "":
mutation_mark = v
queue_redraw()
# ── 视野迷雾呈现态(纯呈现零逻辑;规格=art-bible §6.5 v3.5,正主=HUD框架「视野迷雾」节)──
# 0=正常(灯光区内)/ 1=全黑(区外未开格——正主「区外未开格=全黑不渲染」不透明档)/ 2=记忆态(区外已开格灰显可读)
# ── 视野迷雾呈现态(纯呈现零逻辑;规格=art-bible §6.5 v3.5,现行事实源=HUD框架「视野迷雾」节)──
# 0=正常(灯光区内)/ 1=全黑(区外未开格——现行事实源「区外未开格=全黑不渲染」不透明档)/ 2=记忆态(区外已开格灰显可读)
var fog_state := 0:
set(v):
if fog_state != v:
+3 -3
View File
@@ -1,5 +1,5 @@
## VERTICAL SLICE - NOT FOR PRODUCTION
## D 区演出层:清图喷泉/Boss 影子/完美排雷光带/封印命中弹道/挥铲三帧等一次性占位演出。
## D 区演出层:清图喷泉/Boss 影子/完美封印光带/封印命中弹道/挥铲三帧等一次性占位演出。
## 组合根按核心信号直调 play_*;素材缺位静默回退(演出层不设断言面)。
class_name BoardFxView
extends Control
@@ -87,7 +87,7 @@ func play_boss_shadow() -> void:
tw.tween_callback(shadow.queue_free)
# 完美排雷:金光带加倍扫射 + 横幅占位(§2.2 最高档)
# 完美封印:金光带加倍扫射 + 横幅占位(§2.2 最高档)
func play_perfect_sweep() -> void:
for i in 2:
var band := ColorRect.new()
@@ -99,7 +99,7 @@ func play_perfect_sweep() -> void:
var tw := band.create_tween()
tw.tween_property(band, "position:x", size.x + 8.0, UiKit.motion(0.5 + 0.15 * i))
tw.tween_callback(band.queue_free)
_show_banner("完美排雷 ×2")
_show_banner("完美封印 ×2")
# 封印命中:封印化暴击弹道加重占位(自棋盘上缘飞向 B 区敌人卡方向——越界即收)
+5 -5
View File
@@ -117,7 +117,7 @@ var _digit_dark_ink := true # 数字墨族按板自适应(true=深墨
var _digit_plate := false # 数字色牌垫底模式(双峰纹理板——直画两族皆不过门槛时落牌,
# 素牌+深墨 12:1 纹理免疫;渲染帧取证 underdark 系深墨直画隐形,故启用色牌)
# ---- 视野迷雾(纯呈现层;正主=HUD框架「视野迷雾」节 v0.13,渲染规格=art-bible §6.5 v3.5)----
# ---- 视野迷雾(纯呈现层;现行事实源=HUD框架「视野迷雾」节 v0.13,渲染规格=art-bible §6.5 v3.5)----
const FOG_RADIUS := 8 # 菱形灯光区基准半径(曼哈顿距离 ≤R——与微光环「周围 8 格」同圈起步;半径基准值改动=非白名单须用户)
var _fog_enabled := true # 迷雾开关(呈现态布尔——真实游戏恒 on;探针/夹具可 off=无迷雾基线,两态结算逐位一致断言面)
var _fog_lantern := 0 # 视野加成现值(缺省 0=无加成基线零扰动;生产写入面=组合根每战装配链显式重算——main._sync_treasure_glow)
@@ -142,7 +142,7 @@ func _init() -> void:
add_child(_wrap)
_svp_container = SubViewportContainer.new()
_svp_container.stretch = true # 容器尺寸 = 视口逻辑分辨率(_update_world_size 同步)——恒 1:1 无缩放
_svp_container.mouse_filter = Control.MOUSE_FILTER_IGNORE # 输入归壳层 _gui_input(板级公式换算),不进 SubViewport
_svp_container.mouse_filter = Control.MOUSE_FILTER_IGNORE # 输入归壳层 _gui_input(整图公式换算),不进 SubViewport
_wrap.add_child(_svp_container)
_viewport = SubViewport.new()
_viewport.transparent_bg = true # 格间距露出 D 区底色
@@ -502,7 +502,7 @@ func click_cell(cell: Vector2i, button: int) -> void:
_on_cell_input(cell, ev)
# 板级公式单点:格坐标 = floor(板内局部坐标 / 格步长)
# 整图公式单点:格坐标 = floor(板内局部坐标 / 格步长)
#(格步长 = 格边长 + 间距;板外/负域返回 (-9,-9);2px 格间隙落入左/上邻格)
func _cell_at(shell_pos: Vector2) -> Vector2i:
if _board == null or _board.width == 0:
@@ -534,7 +534,7 @@ func _update_world_size() -> void:
_viewport.size = px
# 真实输入路径(Node2D 迁移批 Decision 3):壳层 _gui_input → 板级公式换算 → 复用既有 hover/点选/chord 出口
# 真实输入路径(Node2D 迁移批 Decision 3):壳层 _gui_input → 整图公式换算 → 复用既有 hover/点选/chord 出口
func _gui_input(ev: InputEvent) -> void:
if _minesweeper == null:
return
@@ -832,7 +832,7 @@ func _paint_cell(c: Vector2i, hero: Vector2i) -> void:
u.mutation_mark = ""
u.badge_glyph = _update_badge(c)
u.glow_group = int(_glow.get(c, UiKit.GLOW_NONE))
# 视野迷雾呈态:曼哈顿距离 > 半径 → 区外——未开格全黑(正主照抄)/
# 视野迷雾呈态:曼哈顿距离 > 半径 → 区外——未开格全黑(现行事实源照抄)/
# 已开格记忆态(灰显可读);以英雄格为心逐格重算,refresh()/notify_hero_move() 既有重绘驱动=移动实时跟随。
# 纯呈现零逻辑——格态/数字真值/清图判定零触碰(🔴 契约 1);结算期去饱和叠加互斥由 CellUnit 记忆态终态档承载。
if _fog_enabled:
@@ -107,7 +107,7 @@ func open_pick(header: String, cards: Array) -> void:
_confirm_btn.visible = false
_continue_btn.visible = false
_foot_hint.visible = true # T7 底注=pick 态专属(result 态让位)
_refresh_btn.visible = false # 刷新面随 set_refresh_display 投喂后现形(open 先归零防残留;店内门票面不投喂=无刷新)
_refresh_btn.visible = false # 刷新面随 set_refresh_display 投喂后现形(open 先归零防残留;店内专长凭证面不投喂=无刷新)
_refresh_l.text = ""
visible = true
@@ -117,7 +117,7 @@ func set_feat_holdings(lines: Array) -> void:
_feat_section.visible = not lines.is_empty()
# 地形变异常驻角标:lines = 文案行(板级变异 display_name+体感短语)——空数组 = 整节隐藏
# 地形变异常驻角标:lines = 文案行(整图变异 display_name+体感短语)——空数组 = 整节隐藏
func set_board_variants(lines: Array) -> void:
for c3 in _board_variant_list.get_children():
_board_variant_list.remove_child(c3)
@@ -157,7 +157,7 @@ func _data_line() -> Label:
# ---- 秘宝库存条(C 区四槽常读+两步交互第一步)----
# 投影行 = {handle, glyph, name, used, usable}(组合根组装:字形=道具名首字;数据源 runner 级库存只读面)。
# 空槽=虚位;used=暗态「已用」保留占位(Used 不可逆——受理序展示语义);不可用=置灰(板级零信息);
# 空槽=虚位;used=暗态「已用」保留占位(Used 不可逆——受理序展示语义);不可用=置灰(整图零信息);
# selected=金框高亮(两步交互第一步的可见态)。tooltip=全名+维度语义。
func set_treasure_inventory(items: Array, selected_handle: int) -> void:
_selected_handle = selected_handle
@@ -11,7 +11,7 @@ func _init() -> void:
# 结算轮光带:金橙半透明横带自棋盘上缘扫至下缘(一次 0.5s,自动回收)——
# "棋盘能量向战斗画面汇聚"的开轮语义(§2.2 色彩转金橙主导;区分于 board_fx 的完美排雷金光带)。
# "棋盘能量向战斗画面汇聚"的开轮语义(§2.2 色彩转金橙主导;区分于 board_fx 的完美封印金光带)。
# 终稿(样张 v2 冻结批 T3):加 2px 亮金前导缘线——扫带运动方向可读(兑现光带逐行横扫 §2.2)
func play_round_sweep(board_rect: Rect2) -> void:
if board_rect.size == Vector2.ZERO:
@@ -143,7 +143,7 @@ func play_round_banner(ri: int) -> void:
tw.tween_callback(banner.queue_free)
# 开战横幅——板级地形变异预告:board_variants 非空时开战
# 开战横幅——整图地形变异预告:board_variants 非空时开战
# 显示一行(文本=「地形变异 · 贫矿脉」式);减动效=静态驻留后即隐(轮横幅先例——行呈现不跳过)。
# 文案/驻留参数 provisional 归本地化批;普通图恒不发射(组合根按快照 board_variants 空判)。
func play_board_variant_banner(text: String) -> void:
+1 -1
View File
@@ -28,7 +28,7 @@ var _cur_settlement = null # 当关 settlement(身份回调读取面——
func _ready() -> void:
# 运行前提=窗口化真渲染面(shot_probe 同款护栏——headless 下 frame_post_draw 永不触发+视口纹理恒空)
if DisplayServer.get_name() == "headless":
print("[board_shot] FAIL — 本探针必须窗口化运行(headless 无渲染面,截图必空);正主=tools/render_gate.sh 取证段")
print("[board_shot] FAIL — 本探针必须窗口化运行(headless 无渲染面,截图必空);现行事实源=tools/render_gate.sh 取证段")
get_tree().quit(1)
return
var args := OS.get_cmdline_user_args()
+1 -1
View File
@@ -1,5 +1,5 @@
# VERTICAL SLICE - NOT FOR PRODUCTION
# 布局几何断言探针(正主=tools/probe_gate.sh 默认段;呈现层三区布局的分辨率维度验收)
# 布局几何断言探针(现行事实源=tools/probe_gate.sh 默认段;呈现层三区布局的分辨率维度验收)
# 用途:battle.md §验收「1280×800 与 16:9 全屏下三区无遮挡/无重叠/无裁切」的可执行化
# GDD 锚:design/gdd/ux/battle.md §Layout Zones(B/C/D 三区几何)
# 硬约束:档位表是数据、断言是一份函数据——加档只改表不动断言(禁每档建一个探针)
+1 -1
View File
@@ -222,7 +222,7 @@ static func _on_adventure_node_entered(node: Dictionary, info: Dictionary, adr,
adr.present_node_curio(ncands)
var npick: Dictionary = _pick_token(ncands, String(st.get("feat_mode", "off")))
if adr.apply_node_curio(String(npick.get("kind", "")), String(npick.get("tier", "")), String(npick.get("feat_id", ""))):
# 门票拿到即开——候选已被替换为专长三选一,就地再选一轮(确定性策略同款 316-f);
# 专长凭证拿到即开——候选已被替换为专长三选一,就地再选一轮(确定性策略同款 316-f);
# 不作此分支则节点停点悬空→选路卡死(MC timeout 形态)
if String(npick.get("kind", "")) == "feat_ticket":
var fcands: Array = (adr.node_curio_pending_state().get("candidates", []) as Array)
+2 -2
View File
@@ -1,6 +1,6 @@
# VERTICAL SLICE - NOT FOR PRODUCTION
# 整屏截图探针(呈现层测试分级 B 档基建:视口截图程序化产出;七屏——
# 菜单/设置面/生涯页/营地/关卡画面/战斗/状态面板,正主=tools/render_gate.sh)。
# 菜单/设置面/生涯页/营地/关卡画面/战斗/状态面板,现行事实源=tools/render_gate.sh)。
# 必须**窗口化**运行(真渲染设备——headless 无渲染面,get_image 得到空纹理):
# Godot_console.exe --path . res://smoke/shot_probe.tscn -- <输出目录>
# 流程 = 真实按钮信号路径驱动(ui_click_probe 同款;存档/设置/元进度三面换注隔离档——禁碰真实档,
@@ -25,7 +25,7 @@ var _meta := {} # 控件实测几何(全局 rect——shot_checker 文字框
func _ready() -> void:
# 运行前提=窗口化真渲染面:headless 下 frame_post_draw 永不触发+视口纹理恒空——无此守卫时 headless 调用方必挂死
if DisplayServer.get_name() == "headless":
print("[shot_probe] FAIL — 本探针必须窗口化运行(headless 无渲染面,截图必空);headless 自动闸禁跑本探针(正主=tools/probe_gate.sh)")
print("[shot_probe] FAIL — 本探针必须窗口化运行(headless 无渲染面,截图必空);headless 自动闸禁跑本探针(现行事实源=tools/probe_gate.sh)")
get_tree().quit(1)
return
var args := OS.get_cmdline_user_args()
+17 -17
View File
@@ -1357,15 +1357,15 @@ func _smoke_feats() -> void:
if String((t as Dictionary).get("kind", "")) == "relic":
node_relic_seen = true
check(feat_seen.size() >= 2, "通关渠道只出专长(60 次重抽 ≥2 种不同 relic——身份键 feat_id 同档可多件)")
check(not node_relic_seen, "节点渠道零专长(60 次重抽零 relic——专长在非 Boss 渠道只以门票形态出现)")
# T5 门票行为断言(A5 拿到即开 / A6 保底计数——契约④四口径;独立 runner 防污染 a_r 牌库)
check(not node_relic_seen, "节点渠道零专长(60 次重抽零 relic——专长在非 Boss 渠道只以专长凭证形态出现)")
# T5 专长凭证行为断言(A5 拿到即开 / A6 保底计数——契约④四口径;独立 runner 防污染 a_r 牌库)
var a_t = load("res://core/adventure_runner.gd").new(_db, _fresh_rng(91))
a_t.start_adventure(91, {"first_script": true})
var stock_before := {}
for kk in ["attack", "gp", "heal"]:
stock_before[kk] = a_t.pool_stock(kk)
a_t._node_curio_pending = {"candidates": [{"kind": "feat_ticket", "tier": "excellent"}, {"kind": "attack", "tier": "common"}]}
check(a_t.apply_node_curio("feat_ticket", "excellent"), "A5 门票兑付受理(节点渠道——拿到即开)")
check(a_t.apply_node_curio("feat_ticket", "excellent"), "A5 专长凭证兑付受理(节点渠道——拿到即开)")
var tkt_cands: Array = (a_t.node_curio_pending_state().get("candidates", []) as Array)
var tkt_all_relic := tkt_cands.size() > 0
for t in tkt_cands:
@@ -1373,13 +1373,13 @@ func _smoke_feats() -> void:
tkt_all_relic = false
check(tkt_all_relic, "A5 拿到即开——停点候选已替换为专长三选一(全 relic)")
check(a_t.pool_stock("attack") == int(stock_before["attack"]) and a_t.pool_stock("gp") == int(stock_before["gp"]),
"A5 零摘牌——门票兑付不动令牌库(不进持有集)")
check(not (a_t.feats_held() as Array).has("feat_ticket"), "A5 无门票残留持有集")
"A5 零摘牌——专长凭证兑付不动令牌库(不进持有集)")
check(not (a_t.feats_held() as Array).has("feat_ticket"), "A5 无专长凭证残留持有集")
var feats_n := (a_t.feats_held() as Array).size()
a_t.apply_node_curio("relic", String((tkt_cands[0] as Dictionary).get("tier", "rare")), String((tkt_cands[0] as Dictionary).get("feat_id", "")))
check((a_t.feats_held() as Array).size() == feats_n + 1, "A5 专长面兑付受理(门票开出候选可正常入持有集)")
check((a_t.feats_held() as Array).size() == feats_n + 1, "A5 专长面兑付受理(专长凭证开出候选可正常入持有集)")
# A6 保底:pity 直填 5 → 下次展示必出(第 6 次必出口径);限购满 → 恒不触发
a_t._feat_ticket_sold = 0 # A5 已兑付一枚门票(sold=1=limit)——A6 段重置计数隔离验证
a_t._feat_ticket_sold = 0 # A5 已兑付一枚专长凭证(sold=1=limit)——A6 段重置计数隔离验证
a_t._feat_ticket_pity = int(a_t.db.adventure.feat_ticket_pity_step)
var rg_t: RandomNumberGenerator = a_t.rng.open_stream("reward_pick")
check(a_t._feat_ticket_consider(rg_t), "A6 保底触发(pity=5 → 第 6 次展示必出——概率无关)")
@@ -1841,7 +1841,7 @@ func _smoke_type_glow() -> void:
check((snap.get("cells", {}) as Dictionary).size() == mapped - 1,
"V2 清单外格剔除后作用集 = 派定数 −1(%d→%d)" % [mapped, (snap.get("cells", {}) as Dictionary).size()])
# ---- 相 3:封锁格(query 返回 null)⇒ 无微光且零登记 ----
_glow_type_overrides.erase(bogus_cell) # 复位清单外(errors 保留=板级登记语义)
_glow_type_overrides.erase(bogus_cell) # 复位清单外(errors 保留=整图登记语义)
var blocked_cell: Vector2i = hero
for i in mini(assign.size(), nbs.size()):
var nb4: Vector2i = nbs[i]
@@ -2582,7 +2582,7 @@ func _smoke_global_variant() -> void:
vt.rows.assign([{"variant_id": "g3", "display_name": "负丙", "unlock_level": 2, "scope": "global",
"effect": "resource_face", "effect_value": 30, "trigger": "on_open", "placement": "any",
"board_types": [], "counter_items": []}])
check(_errs_has_cross_field(vt.validate()), "互锁负样本⑤:scope=global + trigger=on_open 被拒(global ⇒ always 板级恒在)")
check(_errs_has_cross_field(vt.validate()), "互锁负样本⑤:scope=global + trigger=on_open 被拒(global ⇒ always 整图恒在)")
vt.rows.assign([{"variant_id": "g4", "display_name": "负丁", "unlock_level": 2, "scope": "global",
"effect": "resource_face", "effect_value": 30, "trigger": "always", "placement": "closed_only",
"board_types": [], "counter_items": []}])
@@ -2624,7 +2624,7 @@ func _smoke_global_variant() -> void:
var boss1_calls := 0
if boss_ok:
var bb = boss_settlement.minesweeper.current_board()
check(bb.board_variant_ids().is_empty(), "屏蔽后回落:Boss 图无板级全局型(board_variants 空——全局池空合法态)")
check(bb.board_variant_ids().is_empty(), "屏蔽后回落:Boss 图无整图全局型(board_variants 空——全局池空合法态)")
var boss_vids := {}
for vc in bb.variants:
for vid in bb.variants[vc]:
@@ -3497,7 +3497,7 @@ func _encounter_replay(seed_val: int) -> Array:
# ==================== 清图奖励双份兑付链 / 轮内刷新 provenance 守卫 / 窗口锚行 ====================
# 清图 = 全安全格开完。清图测试用"真雷封印(零误标→完美排雷)+ AutoPlayer 逐轮清图"真实流驱动;
# 清图 = 全安全格开完。清图测试用"真雷封印(零误标→完美封印)+ AutoPlayer 逐轮清图"真实流驱动;
# DifficultyTable 注入 hp_mult=10 把 Boss 血量抬到 1500,保证清图批次发生在战斗结束之前(测试台账,跑完即还原)。
const CLEAR_SMOKE_DEPTH := 4
@@ -3506,11 +3506,11 @@ var _regular_clear_reward_rows := 0 # 常规(非刷新图)清图的 clear_
func _smoke_clear_reward_chain() -> void:
print("--- 清图奖励·双份兑付链(完美排雷 / 零兑付 / 第二份被拒 / 高光记实)---")
# ① 真实流完美排雷:标中一面真雷封印 + 零误标 + 清全图 → grant_clear 身份(clear_blast)双份真实兑付
print("--- 清图奖励·双份兑付链(完美封印 / 零兑付 / 第二份被拒 / 高光记实)---")
# ① 真实流完美封印:标中一面真雷封印 + 零误标 + 清全图 → grant_clear 身份(clear_blast)双份真实兑付
var rows: Array = []
var run := _run_full_clear(20260915, false, rows)
check(bool(run.cleared), "① 完美排雷清图达成(%d 轮)" % int(run.rounds))
check(bool(run.cleared), "① 完美封印清图达成(%d 轮)" % int(run.rounds))
var dmg := []
for r in rows:
if String(r.get("type", "")) == "damage" and String(r.get("source_class", "")) == "clear_reward":
@@ -4070,7 +4070,7 @@ func _drive_clear(settlement) -> Dictionary:
if t.hero_cell != ms.hero_pos():
ms.settle_batch([{"action": "move", "cell": t.hero_cell}])
ms.settle_batch([{"action": "seal_place", "cell": t.seal_cell}])
seal_done = true # 一面真雷封印 = 完美排雷前半(零误标),后半靠 AutoPlayer 清图
seal_done = true # 一面真雷封印 = 完美封印前半(零误标),后半靠 AutoPlayer 清图
var seq_before: int = int(settlement.diagnostic_snapshot().board_seq)
var out := {"result": "", "rounds": 0, "terminated": false, "stream": []}
_auto_play_round(settlement, out)
@@ -5298,7 +5298,7 @@ func _smoke_shop_pool() -> void:
# ⑧ 终局池读数(run_adventure pool 面——商店与奖励跨渠道消耗后的终局余量出讫)
var st_pool: Dictionary = autoplay.run_adventure(_db, 42, 0, 0, false, null, "normal", "min", {}, {}, {"mode": "pool"})
var ps: Dictionary = st_pool.get("pool_stock", {})
check(ps.size() == 6, "终局池读数六键出讫(run_adventure pool 面——四池种+T5 门票+T6 heal 行,读数只锚一次载体)")
check(ps.size() == 6, "终局池读数六键出讫(run_adventure pool 面——四池种+T5 专长凭证+T6 heal 行,读数只锚一次载体)")
var ps_ok := true
for k in ps.keys():
var quota := 0
@@ -6191,7 +6191,7 @@ func _smoke_battle_log_view() -> void:
"cast": {"type": "cast", "display_name": "卓尔精灵 施放 毒雾", "source_class": "enemy_action", "round_index": 3},
"decay": {"type": "decay", "value": 1, "round_index": 3},
"verdict": {"type": "verdict", "display_name": "下一轮次", "value": 0, "round_index": 3},
"highlight": {"type": "highlight", "display_name": "完美排雷!清图奖励 ×2", "value": 2, "source_class": "clear_reward", "round_index": 4},
"highlight": {"type": "highlight", "display_name": "完美封印!清图奖励 ×2", "value": 2, "source_class": "clear_reward", "round_index": 4},
}
for row_type in rest:
var before: int = view.parsed_text().length()
+2 -2
View File
@@ -3,7 +3,7 @@
# 真实信号路径驱动一场战斗到击杀胜利(结算行在排播队列),断言两条:
# ①胜利同帧:终局面未开 + 挂起态在案 + 队列非空(旧行为=同帧 flush+开窗,本断言即红——判别性);
# ②排播放完:终局面开启(演播完边沿派发——_on_pump_ended)。
# 存档/设置/元进度三面隔离(isolate_save 正主,meta 经 autoload 链同源),零真实档触碰。headless 可跑(不抓帧)。
# 存档/设置/元进度三面隔离(isolate_save 现行事实源,meta 经 autoload 链同源),零真实档触碰。headless 可跑(不抓帧)。
extends Node
const ProbeUiUtil := preload("res://smoke/probe_ui_util.gd") # 隔离样板单点
@@ -17,7 +17,7 @@ func _ready() -> void:
main = load("res://presentation/main.tscn").instantiate()
add_child(main)
await get_tree().process_frame
# 存档/设置两面隔离(ProbeUiUtil.isolate_save 正主,先删后建防陈旧档泄入)——
# 存档/设置两面隔离(ProbeUiUtil.isolate_save 现行事实源,先删后建防陈旧档泄入)——
# 隔离面走 attach_path 原地重置 autoload 单例:组合根 save_manager/meta 是getter-only
# 读视图,直接赋值无效,隔离必须落在单例上。
ProbeUiUtil.isolate_save(main, "t17_defer_probe", false, true, false)
+1 -1
View File
@@ -1,7 +1,7 @@
# VERTICAL SLICE - NOT FOR PRODUCTION
# push_input 端到端输入探针(场景结构登记项转义务 + Node2D 呈现层 Risks 缓解面)。
# 现役探针全走 BoardView.click_cell 直调(合成鼠标事件零命中已实证)——
# 本探针是真实输入路径的唯一自动化覆盖:鼠标事件 → 壳层 _gui_input → 板级公式换算(_cell_at)→
# 本探针是真实输入路径的唯一自动化覆盖:鼠标事件 → 壳层 _gui_input → 整图公式换算(_cell_at)→
# 悬停/点选/chord 既有出口 → 组合根信号。四 case:①悬停可挖域(金框+十字光标)②悬停禁入
# (hover_forbidden 信号+灰禁红框)③悬停已开格(路径预览点迹+移动光标)④右键封印(seal_place
# 到达组合根+核心封印态)⑤双击 chord(禁入格双左击,核心拒挖零副作用)⑥左键挖(dig 到达组合根+核心揭格)
+4 -2
View File
@@ -15,7 +15,7 @@ tests/
smoke/ # critical-paths.md——人工 gate 清单(/smoke-check 读取)
evidence/ # 截图与人工签核记录
run_all.gd # 单元套件统一入口(套件 manifest 协议,D2 AC-2b)
static_scan.sh # 静态扫描域 S1–S8 可执行子集
static_scan.sh # 静态扫描域 S1–S11 可执行子集
```
## 测试三层与承载目录
@@ -109,9 +109,11 @@ timeout 120s godot --headless --script tests/run_all.gd
## Static Scan(提交前本地跑)
`tests/static_scan.sh` 实现静态扫描域 S1–S8 的可执行子集;规则全集与动机见
`tests/static_scan.sh` 实现静态扫描域 S1–S11 的可执行子集;规则全集与动机见
`design/engineering/architecture/architecture.md`「测试与质量域」节。新违规案例必须**先补扫描规则再修码**。
S11 代码注释禁用词:扫描 game/ 全域 .gd/.tres/.md/.json/.sh 的注释面,词表从 `design/gdd/术语表.md`「退役词对照」解析(显式追加「完美封印」,排除「巢穴」「旗」);三层豁免(闸门自指/显式留档 4 处/非注释面天然不取);两道自检(扫描面文件数非空+词表解析非空)。
## 本地闸(唯一验收门禁)
本仓库无远端、无 CI——**验收只在本地发生**,跑 `tools/run_all_gates.sh` 是收口唯一入口,按本批改动域选档(`--code`/`--docs`/跨两域或改闸件本身=不带参数全量),
+1 -1
View File
@@ -17,7 +17,7 @@
5. 移动:已开格瞬移、**已引爆危险格可通行可读**(工程债呈现半边)
6. 停手:RAP 1:1 转存、下轮 AP 硬重置同图继续
7. 轮末结算:明细逐行呈现、来源标注可读(P2——"这一行是我哪步扫雷带来的"可答)
8. 清图:清图奖励兑付 + 完美排雷高光(零误标时双份)+ 换新图;未刷新的停手轮不换图
8. 清图:清图奖励兑付 + 完美封印高光(零误标时双份)+ 换新图;未刷新的停手轮不换图
9. 轮内刷新(方案 A):Active 当场换图、AP 保留、RAP 扣减、每轮 ≤2 次、跨图事实结算完整
## Battle & Level
+118 -1
View File
@@ -70,7 +70,7 @@ fi
# S3 — core/ 禁 ConfigDB 外 data 表加载(schema class_name 直引豁免)
# 扫描路径必须写 game/core——写裸 `core` 仓根无此目录,恒零命中恒绿(静默空转)。
# 豁免:①注释行 ②game/core/config_db.gd 自身(数值单一事实源的正主,它的单点 load 是本规则的例外)。
# 豁免:①注释行 ②game/core/config_db.gd 自身(数值单一事实源的现行事实源,它的单点 load 是本规则的例外)。
if require_scan_face "S3" "$DIR_CORE"; then
hits=$(grep -rnE '\b(load|preload)[[:space:]]*\(|ResourceLoader\.load' "$DIR_CORE" --include='*.gd' 2>/dev/null \
| grep -vE '^[^:]+:[0-9]+:[[:space:]]*#' \
@@ -93,4 +93,121 @@ fi
# S7 B2/B3/B4 API 符号命中文件 ⊆ B1 文件集(编排唯一性)
# S8 结算路径 await 零命中(B1 结算/编排文件集)
# ── S11 — 代码注释禁用词 ──
# 禁「板级」「门票」「正主」「落籍」「完美排雷」等退役词进注释面(完整词表见术语表解析)
# 权威出处:design/gdd/术语表.md「退役词对照」节+「对外命名纪律」节
# 扫描面:game/ 全域 .gd/.tres/.md/.json/.sh(.txt 不收=第三方许可证非本仓库文本)
# 词表来源:解析「退役词对照」第一列(去括号注、斜杠拆分)+显式追加禁「完美排雷」(受对外命名纪律约束,不在退役词对照表里);显式排除「巢穴」(玩家专名/风味层,术语表自带除外注)、「旗」(单字误报率高,doc_term_audit 判例)
# 注释文本提取:.gd/.sh 取 # 后文本;.tres 取 ; 后文本;.md 取整行;.json 无注释语法(保留扩展名但不产生注释段)
# 三层豁免依序:①闸门自指(行内含检查/禁令形态即跳过)②显式留档豁免单 4 处 ③非注释面天然不取
if [ -d game/core ]; then
S11_TABLE="design/gdd/术语表.md"
S11_FAIL_LOCAL=0
if [ ! -f "$S11_TABLE" ]; then
echo "[static_scan] S11 FAIL: 术语表文件不存在($S11_TABLE)"
S11_FAIL_LOCAL=1
else
# 解析词表:「退役词对照」第一列→去括号注→斜杠拆分
S11_WFILE=$(mktemp)
awk '/^## 退役词对照/{f=1;next} f && /^## /{exit} f && /^\|/ && !/^\|---/ && !/退役词/{
sub(/^\| */, ""); sub(/ *\|.*/, "")
gsub(/([^)]*)/, ""); gsub(/\([^)]*\)/, "")
gsub(/^ *| *$/, "")
n = split($0, parts, " / ")
for (i=1;i<=n;i++){gsub(/^ *| *$/,"",parts[i]); if(parts[i]!="") print parts[i]}
}' "$S11_TABLE" > "$S11_WFILE"
# 显式追加+排除
printf '完美排雷\n' >> "$S11_WFILE"
grep -vx '巢穴' "$S11_WFILE" | grep -vx '旗' | grep -vx 'flag_\*' | sort -u > "${S11_WFILE}.x" && mv "${S11_WFILE}.x" "$S11_WFILE"
# 自检二:词表解析非空
S11_WC=$(grep -c . "$S11_WFILE" 2>/dev/null || echo 0)
if [ "$S11_WC" -eq 0 ]; then
echo "[static_scan] S11 FAIL: 词表解析为空——术语表结构可能已变"
S11_FAIL_LOCAL=1
fi
# 自检一:扫描面非空
S11_FC=$(find game/ -type f \( -name '*.gd' -o -name '*.tres' -o -name '*.md' -o -name '*.json' -o -name '*.sh' \) 2>/dev/null | wc -l)
if [ "$S11_FC" -eq 0 ]; then
echo "[static_scan] S11 FAIL: 扫描面为空——路径写错导致的静默空转"
S11_FAIL_LOCAL=1
fi
if [ "$S11_FAIL_LOCAL" -eq 0 ]; then
# 扫描三个文件类型并合并结果
S11_ALL_HITS=$(
# .gd/.sh:取 # 后注释
find game/ -type f \( -name '*.gd' -o -name '*.sh' \) -print0 2>/dev/null \
| xargs -0 awk -v wf="$S11_WFILE" '
BEGIN{nw=0;while((getline w<wf)>0)words[++nw]=w;close(wf)}
/#/{
cs=index($0,"#"); cmt=substr($0,cs+1)
for(i=1;i<=nw;i++){
if(index(cmt,words[i])>0){
if($0~/contains\(|find\(|grep|has_char\(|禁「|禁『|零「|零『/)next
if(FILENAME~"enemy.tres"&&FNR==653)next
if(FILENAME~"level_runner"&&FNR==832)next
if(FILENAME~"adventure_runner"&&FNR==403)next
if(FILENAME~"damage_math_test"&&FNR==36)next
print FILENAME":"FNR":"words[i]
next
}
}
}
' 2>/dev/null
# .tres:取 ; 后注释
find game/ -type f -name '*.tres' -print0 2>/dev/null \
| xargs -0 awk -v wf="$S11_WFILE" '
BEGIN{nw=0;while((getline w<wf)>0)words[++nw]=w;close(wf)}
/;/{
cs=index($0,";"); cmt=substr($0,cs+1)
for(i=1;i<=nw;i++){
if(index(cmt,words[i])>0){
if($0~/contains\(|find\(|grep|has_char\(|禁「|禁『|零「|零『/)next
if(FILENAME~"enemy.tres"&&FNR==653)next
print FILENAME":"FNR":"words[i]
next
}
}
}
' 2>/dev/null
# .md:整行即注释面
find game/ -type f -name '*.md' -print0 2>/dev/null \
| xargs -0 awk -v wf="$S11_WFILE" '
BEGIN{nw=0;while((getline w<wf)>0)words[++nw]=w;close(wf)}
{
for(i=1;i<=nw;i++){
if(index($0,words[i])>0){
if($0~/contains\(|find\(|grep|has_char\(|禁「|禁『|零「|零『/)next
print FILENAME":"FNR":"words[i]
next
}
}
}
' 2>/dev/null
)
S11_HITS=0
if [ -n "$S11_ALL_HITS" ]; then
S11_HITS=$(echo "$S11_ALL_HITS" | grep -c .)
fi
if [ "$S11_HITS" -gt 0 ]; then
echo "[static_scan] S11 FAIL: 代码注释禁用词命中 $S11_HITS 处"
echo "$S11_ALL_HITS" | head -50
if [ "$S11_HITS" -gt 50 ]; then echo " ...(截断,共 $S11_HITS 处)"; fi
fail=1
else
echo "[static_scan] S11 ok(扫描面 $S11_FC 个文件,词表 $S11_WC 词)"
fi
else
fail=1
fi
rm -f "$S11_WFILE"
fi
fi
exit $fail
+72 -1
View File
@@ -171,7 +171,7 @@ func _init() -> void:
"判定相减:误标 1 − 豁免 1 = 有效 0 且标中≥1 → 完美成立〔卡② R2 真值表〕")
orch._perfect_hit_true = 0
Kit.check_quiet(not orch.perfect_sweep_earned(),
"判定守卫:标中数 0 时零误标也不成立(板级 ≥1 标中条件零松动)〔卡② R2 真值表〕")
"判定守卫:标中数 0 时零误标也不成立(整图 ≥1 标中条件零松动)〔卡② R2 真值表〕")
var rig_rng2 = RngScript.new()
rig_rng2.set_master_seed(778002)
var orch2 = RigScript.new(db, rig_rng2)
@@ -187,6 +187,77 @@ func _init() -> void:
"跨战清零:domain=battle 豁免账每战重建——次战首次标错再豁免一次〔卡② R2〕")
Kit.check_quiet(EffectEngineKindProbe(db), "词表闭集:forgive_seal_miss 在 EFFECT_KINDS(出域即加载致命——扩签合规)〔卡② R4〕")
# ── N7 归因锚两态验收组(卡① T7:seal_miss 行 highlight 改用有效误标判据)──
# A10 未豁免态:无 perfect_insurance,首次标错 → seal_miss 行 highlight=="perfect_broken" 且完美不成立
var rows10: Array[Dictionary] = []
var rng10 = RngScript.new()
rng10.set_master_seed(779010)
var o10 = RigScript.new(db, rng10)
o10.step_row.connect(func(row): rows10.append(row))
Kit.check_quiet(o10.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40,
"hero_armor_class": 10, "hero_ap_bonus": 0, "attunement": [], "attune_charges": {},
"feats": [], "tree_rules": [], "battle_index": 1,
"bonus_ref": {"atk": 0, "max_hp": 0, "armor_class": 0}, "adventure_counters": {},
"voucher_rap": 0, "voucher_time_sand": 0, "node_type": "battle"}),
"A10 rig:无保险战斗启动〔卡① N7〕")
var sc10: Variant = null
for c10 in o10.minesweeper.current_board().all_cells():
if not o10.minesweeper.current_board().is_mine(c10):
sc10 = c10
break
o10._grant_seg._judge_seal({"cell": sc10, "hit": false}, 1)
var sm10: Dictionary = {}
for r10 in rows10:
if String(r10.get("type", "")) == "seal_miss":
sm10 = r10
Kit.check_quiet(String(sm10.get("highlight", "")) == "perfect_broken",
"A10 未豁免:首次标错 seal_miss 行 highlight==perfect_broken〔卡① N7〕")
Kit.check_quiet(not o10.perfect_sweep_earned(),
"A10 真值:无保险首标 → 有效误标 1 → 完美不成立(与 highlight 一致)〔卡① N7〕")
# A11 豁免态:有 perfect_insurance,首次标错 → seal_miss 行 highlight=="" 且完美仍成立
var rows11: Array[Dictionary] = []
var rng11 = RngScript.new()
rng11.set_master_seed(779011)
var o11 = RigScript.new(db, rng11)
o11.step_row.connect(func(row): rows11.append(row))
Kit.check_quiet(o11.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40,
"hero_armor_class": 10, "hero_ap_bonus": 0, "attunement": [], "attune_charges": {},
"feats": ["perfect_insurance"], "tree_rules": [], "battle_index": 1,
"bonus_ref": {"atk": 0, "max_hp": 0, "armor_class": 0}, "adventure_counters": {},
"voucher_rap": 0, "voucher_time_sand": 0, "node_type": "battle"}),
"A11 rig:带保险战斗启动〔卡① N7〕")
var sc11: Variant = null
for c11 in o11.minesweeper.current_board().all_cells():
if not o11.minesweeper.current_board().is_mine(c11):
sc11 = c11
break
o11._grant_seg._judge_seal({"cell": sc11, "hit": false}, 1)
var sm11: Dictionary = {}
for r11 in rows11:
if String(r11.get("type", "")) == "seal_miss":
sm11 = r11
Kit.check_quiet(String(sm11.get("highlight", "")) == "",
"A11 豁免:首次标错获豁免 → seal_miss 行 highlight==空串〔卡① N7〕")
o11._perfect_hit_true = 1
Kit.check_quiet(o11.perfect_sweep_earned(),
"A11 真值:误标 1 − 豁免 1 = 有效 0 且标中≥1 → 完美成立(与 highlight==空 一致)〔卡① N7〕")
# A12 呈现两态文案与 _format_row 一致:两态均零「雷」字面
var LogViewScript := load("res://presentation/views/battle_log_view.gd")
var lv = LogViewScript.new(db)
var broken_row := {"type": "seal_miss", "source_class": "seal_miss", "origin_cell": [0, 0],
"round_index": 1, "display_name": "封印未中", "value": 0, "highlight": "perfect_broken"}
var forgiven_row := {"type": "seal_miss", "source_class": "seal_miss", "origin_cell": [0, 0],
"round_index": 1, "display_name": "封印未中", "value": 0}
var s_broken := String(lv._format_row(broken_row))
var s_forgiven := String(lv._format_row(forgiven_row))
Kit.check_quiet(s_broken.find("完美封印已破") >= 0 and s_broken.find("雷") == -1,
"A12 已破态:_format_row 输出「完美封印已破」且零「雷」字面〔卡① N7〕")
Kit.check_quiet(s_forgiven.find("已豁免") >= 0 and s_forgiven.find("雷") == -1,
"A12 豁免态:_format_row 输出「已豁免」且零「雷」字面〔卡① N7〕")
lv.free()
sm.clear_breakpoint()
quit(Kit.finish("feat_vouchers"))
+1 -1
View File
@@ -95,7 +95,7 @@ func check_v7(a, db) -> void:
if String(t.get("tier", "")) != "common":
ok_fb = false
Kit.check_quiet(ok_fb, "子池空回退全池:全普通池候选恒 3 格普通档〔专长批 V7〕")
# 构成③:凭证不进子池也不进全池视图(独立计数体系——只经门票注入出现)
# 构成③:凭证不进子池也不进全池视图(独立计数体系——只经专长凭证注入出现)
a._curio_deck = [_tok("gp", "common"), _tok("attack", "common"), _tok("max_hp", "common"),
_tok("feat_ticket", "excellent")]
var ticket_seen := false
@@ -130,7 +130,7 @@ func _rows_contain(pool: Array, row: Dictionary) -> bool:
func _test_shop_accessors(db) -> void:
var products: Array = db.shop_products()
if products.is_empty():
Kit.check("ShopTable products 行族非空(表已落籍前提)", false)
Kit.check("ShopTable products 行族非空(表已已入库前提)", false)
return
Kit.check("ShopTable products 行族非空", true)
var first: Dictionary = products[0]
@@ -38,7 +38,7 @@ func _run() -> void:
ov.visible and btns == 3 and not ov._confirm_btn.visible and not ov._continue_btn.visible):
return
# ①b 刷新面投喂(对齐商店 MC-1——投喂前隐藏/免费位点亮/GP 付费档禁用态/真实钮路径外发)
if not _req("投喂前刷新钮应隐藏(未投喂上下文如店内门票面无刷新)",
if not _req("投喂前刷新钮应隐藏(未投喂上下文如店内专长凭证面无刷新)",
not ov._refresh_btn.visible):
return
var refreshed := []
@@ -0,0 +1,17 @@
# 迭代 32 卡① 代拍板追认单(2026-10-20)
批次内落入卡片 §9 代拍板白名单的微决策,逐条备案确认(授权发生于卡片审问阶段,实现只执行预授权)。
| # | 决策 | 白名单条目 | 落点 |
|---|---|---|---|
| 1 | 注释换代措辞等价微调:`static_scan.sh:73`「数值单一事实源的正主」→「数值单一事实源自身」(等价表述,非硬套「现行事实源」致句意断裂) | §9.1 | static_scan.sh:73 |
| 2 | `落籍`→`已入库` 在带 commit 号(9cc4b8b)的历史注记行不改,保留「已落籍」 | §9.2 | enemy.tres:653 |
| 3 | `正主` 误报处「正主角池」(正主+角池)不改 | §9.3 | damage_math_test.gd:36 |
| 4 | 测试断言描述文本替换措辞取语义等价 | §9.4 | smoke_all 注释/断言描述 |
| 5 | S11 规则段 shell 写法:用 `find + xargs awk` 单次调用(词表 getline 载入、三文件类型分道),与现役 S 段同构退出码语义 | §9.5 | static_scan.sh S11 段 |
| 6 | N7 两态文案末字:已破态「清图奖励不再加倍」、豁免态「不计入完美封印判定」,因果不变成立 | §9.6 | battle_log_view.gd:192 |
| 7 | battle_log_view 局部变量改名 `suffix`→`miss_note` 避开外层同名遮蔽(实现细节,非语义) | §9.5 延伸 | battle_log_view.gd |
**超白名单呈报(已停机自查消解,非代裁)**:
- 卡片 §2 豁免单预测的 `level_runner.gd:832`、`adventure_runner.gd:403` 两处,开工复核发现现行代码该行已无对应退役词(卡片成文后代码前进)。据「开工自行复核」条款按现实处理:无需豁免(词已不在该位),S11 注释面为权威判据。不新增豁免、不改代码凑绿。
- 全局 sed 曾误改移交三处(display_name/玩家文案)与 static_scan.sh 词表追加行,已逐处精确还原并补 S11 自指豁免,属施工返工非代拍板。
@@ -0,0 +1,44 @@
# 迭代 32 卡① 反哺点清单(2026-10-20)
本批为代码实现批,禁写 `design/` 整目录与根规则文件。发现要改文档的点逐条列此,用户在会话裁决后归文档批落文(`design/` 对本批只读)。落点对应卡片 §5 关联文档同步面。
## GDD 文档面(「完美排雷」→「完美封印」措辞同步)
| # | 文档 | 关联点 | 建议改成 | 依据 |
|---|---|---|---|---|
| 1 | `design/gdd/术语表.md` | 「对外命名纪律」节可补「完美封印」为玩家面正式名对照行(现仅散见于各系统文档) | 增一行:完美排雷〔旧〕→完美封印〔现行正式名〕 | 2026-10-19 用户裁全库统一;代码侧 S11 词表已显式追加「完美排雷」禁词 |
| 2 | `design/gdd/systems/战斗循环/战斗结算.md`(:85/:108/:116/:139/:141/:161/:259/:342) | 全篇仍称「完美排雷」,与 `奇物.md` v3.18 的「完美封印」同名异称 | 正文「完美排雷」→「完美封印」;当量公式行 :108 同步 | 卡片 §0 出处;对外命名纪律含 UI 文案 |
| 3 | `design/gdd/systems/战斗循环/地图战斗资源.md`(:155/:194) | 兑付结构段称「完美排雷」 | →「完美封印」 | 同上 |
| 4 | `design/gdd/systems/战斗循环/战斗表现.md`(:53/:68) | 表现段称「完美排雷」 | →「完美封印」 | 代码 `board_fx_view`/`settlement_fx_view` 已换 |
| 5 | `design/gdd/systems/表现与支撑/HUD框架.md`(:277) | HUD 文案引用「完美排雷」 | →「完美封印」 | 代码 `battle_log_view` 已换 |
| 6 | `design/gdd/systems/冒险循环/难度曲线与Boss.md`(:224/:227/:313) | 「完美排雷」措辞 | →「完美封印」 | 同名异称统一 |
| 7 | `design/gdd/ux/battle.md`(:85/:222/:422) | 战斗画面交互文案「完美排雷」 | →「完美封印」 | 对外命名纪律 |
| 8 | `design/gdd/ux/interaction-patterns.md`(:90) | 「完美排雷」 | →「完美封印」 | 同上 |
| 9 | `design/art/art-bible.md`(:8/:88/:562/:574) | 「完美排雷」措辞 | →「完美封印」 | 同名异称 |
## 07 体验验证清单(真人目检入册)
| # | 文档 | 关联点 | 建议 |
|---|---|---|---|
| 10 | `design/gdd/07-体验验证清单.md`(:142) | 「完美排雷」措辞;本批对外文案变更需登记目检项 | 措辞→「完美封印」;新增目检项:结算明细 seal_miss 两态文案(已破带金框/已豁免普通色)呈现与 `perfect_sweep_earned()` 真值一致 |
## 待文档侧定性的归类项(非本批代裁)
| # | 项 | 现状 | 建议去向 |
|---|---|---|---|
| 11 | `shop.tres:86`「奇物门票」display_name + `curio_interact_controller.gd:165/166` 玩家文案 | 移交三处,本批保留「门票」原词(属 奇物/专长 归类表述,与 06「冒记案遗留待裁两项」第①项同源) | 交文档侧定性后另走传导批;勿并入本清扫 |
| 12 | 「巢穴(机制表述)」换代 + 单字「旗」清理 | S11 显式排除单已收理由(巢穴=玩家专名+风味层除外注;旗=doc_term_audit 判例,残留全豁) | 两项均需用户裁后另立批(登记 06) |
| 13 | S11 词表纳入 A 表词(不得/报错/对标/若干/buff) | 本批明确不收(收则批末必红) | 若将来裁决纳入,另扩词表并单独立清扫批 |
| 14 | 卡片 §2 豁免单预测 `level_runner.gd:832`、`adventure_runner.gd:403` | 开工复核:现行代码该行已无对应退役词(卡片成文后代码前进) | 反哺点记录卡片与现实偏差;卡片如需订正归文档批 |
## parity 重锚批(登记 06)
| # | 项 | 建议 |
|---|---|---|
| 15 | N7 改动致 `seal_miss`/`insurance_used` 行序与 highlight 漂移(契约 12) | 合回后另立**用户批准**的 parity 重锚批并登记 06;预期漂移两例见实现报告「已记档差异」节 |
## 06 待办清单
| # | 项 | 建议 |
|---|---|---|
| 16 | 本批条目「代码注释禁用词清扫」状态推进 | 卡①合回后由文档批推进至「实现已闭合」;本批不写 06 |
@@ -0,0 +1,77 @@
# 迭代 32 卡① 实现报告·代码注释禁用词清扫批(2026-10-20)
- **批次**:迭代 32 执行序①(代码注释禁用词清扫批)
- **分支**:`g-plan2-iter32-card1-comment-sweep`
- **合回前 tip**:`b3b77ff1`
- **闸回执**:`game/tests/.gates/receipt.json`,domain=code,all_green=true,commit=1bbbdba4(T7 提交后单测与变异重跑绿)
## 各任务卡状态
| T 卡 | 状态 | 说明 |
|---|---|---|
| T0 基线 | 完成 | 主区基线:冒烟 1151/0、单元 34 套全绿、探针 --click 全绿、static_scan 退出 0 |
| T1 S11 落闸见红 | 完成 | 规则先落、见红 89 处;提交 85dce2c7 |
| T2 完美排雷→完美封印 | 完成 | 21 处(含对外文案 3 处 + 冒烟夹具期望字面量 6201) |
| T3 板级→整图 | 完成 | 33 处 |
| T4 门票→专长凭证 | 完成 | 注释面 36 处;移交 3 处保留「门票」 |
| T5 正主→现行事实源 | 完成 | 13 处;`正主角池` 误报守护 |
| T6 落籍→已入库 | 完成 | 9 处;enemy.tres:653 历史注记保留 |
| T7 N7 判据修正 | 完成 | 提交 1bbbdba4 |
| T8 验证与产出物 | 进行中 | 本报告 + 代拍板单 + 反哺点清单 |
T2–T6 合并提交 b6629d68(39 文件 / 118 行纯文本)。
## 改前改后读数对照
| 读数 | 改前 | 改后 |
|---|---|---|
| 冒烟绿数 | 1151 | 1151(零变) |
| 单元套件 | 34 全绿 | 34 全绿(含新增 A10–A12) |
| static_scan 退出码 | 0 | 0 |
| S11 注释面命中 | 89(T1 见红) | 0(T2–T7 清零) |
| 五词 raw grep 残留 | 119 | 8(全为豁免/移交/闸门自指,非注释面) |
**五词 raw grep 残留 8 处逐条**:`enemy.tres:653`(落籍·豁免·历史注记)、`shop.tres:86`(门票·移交 display_name)、`curio_interact_controller.gd:165/166`(门票·移交玩家文案)、`static_scan.sh:97/100/122`(完美排雷·S11 闸门自指)、`damage_math_test.gd:36`(正主角池·误报豁免)。
注:卡片 §2 豁免单预测的 `level_runner.gd:832`、`adventure_runner.gd:403` 经开工复核,现行代码该行已无对应退役词(卡片成文后代码前进),故不出现;S11 注释面口径为权威判据,现绿。
## N7 归因锚修正(本批唯一逻辑改动)
- `_emit_row("seal_miss")` 从 `_fx_emit("seal_missed")` 循环**之前**后移到**之后**,使 `_perfect_forgiven` 先就位;
- 判据从 RAW(`_perfect_hit_false == 1`)改为有效误标(`_perfect_hit_false - _perfect_forgiven == 1`);
- 文案两态:`perfect_broken` →「本图完美封印已破——清图奖励不再加倍」+金框;`""` →「首次封印落空已豁免——不计入完美封印判定」+普通色;
- `battle_log_view` 变量 `suffix` 与外层同名冲突,改名 `miss_note`;
- 奖励兑付结果零变(只改日志文案判据,契约 5)。
## 验收断言 A10–A12(落 feat_vouchers_test.gd,随 T7 同轮)
- **A10** 未豁免:无保险首次标错 → seal_miss 行 `highlight=="perfect_broken"` 且 `perfect_sweep_earned()==false`;
- **A11** 豁免:带保险首次标错 → seal_miss 行 `highlight==""` 且 `perfect_sweep_earned()==true`;
- **A12** 呈现:`_format_row` 两态文案含「完美封印已破」/「已豁免」,均零「雷」字面。
## 变异验证记录表
| 变异点 | 注入形态 | 跑的闸 | 红输出 | 还原 |
|---|---|---|---|---|
| S11 拦截 | battle_orchestrator.gd:265 注释追加「板级」 | static_scan.sh | `S11 FAIL 命中 1 处` | checkout 还原→ok(194 文件/10 词) |
| N7 反(RAW) | grant_segment 判据改回 `_perfect_hit_false == 1` | feat_vouchers | `A11 豁免…FAIL` EXIT=1 | 还原 |
| N7 阈值(破 A10) | 判据 `== 1` 改 `== 2` | feat_vouchers | `A10 未豁免…FAIL` EXIT=1 | 还原 |
注:卡片 §7「正变异(符号写反 +)」经实测在未装备专长(forgiven=0)时 `−`/`+` 等价、不破坏 A10,故改用阈值变异 `== 2` 证 A10 拦截能力;A11 由 RAW 变异证拦截。A12 为文案常量比对,随 A10/A11 判据承载,无独立变异。
## 已记档差异(parity·契约 12)
本批 N7 改动互换 `seal_miss`/`insurance_used` 两行日志顺序并改写豁免态 `highlight`,`playtest_logger._on_step_row` 逐行落盘含 `highlight` 字段,11 号闸 JSONL sha256 逐位比对必红。**本批不发起 parity**(回执 11 号落 skip、入 not_run,all_green 不受影响)。
预期漂移两例(供合回后用户批准重锚批对照):
1. **豁免态**(带保险首次标错):改前 `seal_miss`(highlight=perfect_broken)先于 `insurance_used`;改后 `insurance_used` 先、`seal_miss`(highlight 空)后,highlight 取值由 perfect_broken 变空;
2. **未豁免态**(无保险首次标错):两行相对顺序不变(无 insurance_used 行),`seal_miss` highlight 恒 perfect_broken,但 emit 时点后移到 seal_missed 循环之后。
## 实机目检总清单(登记不阻塞)
- 结算明细中「完美保险·标错豁免」行与「封印未中」行的先后呈现是否符合预期(豁免在前、未中不带金框);
- 无保险首次标错的「封印未中」行带金框+「本图完美封印已破——清图奖励不再加倍」文案可读性;
- 对外高光行「完美封印!清图奖励 ×2」与 banner「完美封印 ×2」显示正确。
## 遗留读数义务
- 合回后另立**用户批准**的 parity 重锚批(登记 06),本批不借任何批顺手重锚。