feat(凭证件裁削): 卡② 同调电池删除+完美保险迁专长+配额净删落地(迭代 31 卡②)
用户 2026-10-18 逐裁四项落码:
- T1/T2 同调电池整件删除:CURIO_KINDS 摘 kind、三字段与三处分发表分支删净、
runner 侧 _battery_pending/断点键/getter/choose_battery_target 全清(呈现层零接线=有账本无入口)。
- T3 完美保险出池:kind/字段/分发摘除,_pending_insurance 与 level_runner 注入通道
(voucher_insurance 键、set_voucher_grants 第三参)整链拆除,battle_orchestrator._fx_insurance 删。
- T4 引擎扩签 forgive_seal_miss(EFFECT_KINDS 31→32)+两账相减:新增 _perfect_forgiven 单向累加、
谓词抽成 perfect_sweep_earned()(有效误标=误标总数−豁免数),_perfect_hit_false 保持纯净=契约 2;
seal_missed 发射时机与消费点零改动=契约 3(危险直觉·守同事件订阅面在验收组里对拍)。
- T5 feats.tres 第 30 行完美保险专长:rarity rare/cap 省略/domain=battle 显式写出(战斗域首例)/
desc「本场首次封印落空,不计入完美封印判定」;限次由行 when counter(insurance_used) lt 1 承载,
do 自带 counter_id 记账由授予段消费点落账(代拍板 3 选型:单行落表,理由见实现报告)。
- T6 配额净删:TIER_QUOTAS rare 9→7、令牌池总量 104→102,validate() 硬断言过;smoke 配额锚/牌库
构成/专长行数随批换代。
- T7 验收:feat_vouchers_test 39 项全绿(出池反例/断点缺键兼容/账本保留/配额锚/装载/限次一次/
计数纯净/两账相减真值表/跨战清零/事件不吞)+forge_table 词表闭集断言 31→32。
变异验证 6 条:5 条证红;「domain battle→adventure」在本 rig 不可观测(未红),已如实登记。
- 存量缺陷顺带修:旧 _emit_row("insurance_used","",…) 空来源会触发行来源断言(旧路径无消费点故从未走红),
新订阅面给出 source_class=seal_miss。
基线(卡① 合回后 master 4f953ce8):冒烟 1151/0、单元 34 套全绿、探针 --click 全绿(user:// 存档移开态)。
This commit is contained in:
1 parent
4f953ce829
commit
072c19b4a5
10 files changed
+183
-110
No files matched your search
@@ -1,12 +1,18 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# 凭证件五件+留任二件单测(刷新券/储备金袋/时沙/完美保险/同调电池 五件 + 留任二件——刷新写错账修复钉面)。
|
||||
# 覆盖:刷新券→刷新存量账(非 _attune_refresh_x——写错账复发反例)/储备金袋→RAP/时沙→首轮 AP 池/
|
||||
# 完美保险→标错豁免/同调电池→充能账(choose_battery_target 玩家点选——禁自动替选反例);
|
||||
# 凭证件留任二件+两件出池+完美保险专长单测(刷新券/储备金袋/时沙 三件路由+留任二件——刷新写错账修复钉面)。
|
||||
# 覆盖:刷新券→刷新存量账(非 _attune_refresh_x——写错账复发反例)/储备金袋→RAP/时沙→首轮 AP 池;
|
||||
# 迭代 31 卡② 凭证件裁削批——同调电池整件删除、完美保险迁专长:两件出池反例(词表/兑付/符号清零/断点缺键)、
|
||||
# 配额净删锚(稀有 7/总量 102)、完美保险专长验收组(装载/限次一次/计数纯净/两账相减判定/跨战清零/事件不吞);
|
||||
# 留任 2 件路由与值不变(GP 钱包/当即回血)+行动点上限归属性轴(bonus.ap 落点)。
|
||||
# 运行:godot --headless --path game --script res://tests/unit/core/feat_vouchers_test.gd(expect 0)
|
||||
extends SceneTree
|
||||
|
||||
const Kit := preload("res://tests/unit/test_kit.gd")
|
||||
const EffectEngineScript := preload("res://core/effect_engine.gd")
|
||||
|
||||
|
||||
func EffectEngineKindProbe(_db) -> bool:
|
||||
return EffectEngineScript.EFFECT_KINDS.has("forgive_seal_miss")
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
@@ -48,20 +54,36 @@ func _init() -> void:
|
||||
Kit.check_quiet(a._apply_curio_pick("time_sand", "rare", 2), "时沙兑付受理〔专长批 V9〕")
|
||||
Kit.check_quiet(a._time_sand_queue == 2, "时沙:首轮 AP 队列 +2(池增量非上限)〔专长批 V9〕")
|
||||
|
||||
# V9-4 完美保险:标错豁免待注入(节点级)
|
||||
Kit.check_quiet(a._apply_curio_pick("perfect_insurance", "rare", 1), "完美保险兑付受理〔专长批 V9〕")
|
||||
Kit.check_quiet(a._pending_insurance == 1, "完美保险:豁免待注入 1(节点级)〔专长批 V9〕")
|
||||
|
||||
# V9-5 同调电池:待分配充能 + 玩家点选(禁自动替选反例)+ 跨层写充能账
|
||||
Kit.check_quiet(a._apply_curio_pick("attune_battery", "rare", 1), "同调电池兑付受理〔专长批 V9〕")
|
||||
Kit.check_quiet(a.battery_pending() == 1, "同调电池:待分配充能 1〔专长批 V9〕")
|
||||
Kit.check_quiet(not a.choose_battery_target("incense_stick"),
|
||||
"电池反例:件不在装配集拒绝(禁自动替选——零副作用)〔专长批 V9·秘宝批换主语〕")
|
||||
a._attunement = ["incense_stick"] # 注入装配集(incense_stick=新目录 active_charged)
|
||||
a.run_ledger.attune_charges = {"incense_stick": 1} # 迭代 27 T3——四账住 RunLedger
|
||||
Kit.check_quiet(a.choose_battery_target("incense_stick") and int(a.run_ledger.attune_charges["incense_stick"]) == 2,
|
||||
"电池正例:玩家点选 incense_stick→充能账 +1(跨层写 attune_charges)〔专长批 V9·秘宝批换主语〕")
|
||||
Kit.check_quiet(a.battery_pending() == 0, "电池:分配后待账清零〔专长批 V9〕")
|
||||
# V23 两件出池(迭代 31 卡② 凭证件裁削批 R1——同调电池删除/完美保险迁专长)
|
||||
Kit.check_quiet(not db.adventure.CURIO_KINDS.has("attune_battery")
|
||||
and not db.adventure.CURIO_KINDS.has("perfect_insurance"),
|
||||
"出池词表:CURIO_KINDS 零两件(三选一池物理不可再出)〔卡② R1〕")
|
||||
Kit.check_quiet(not a._apply_curio_pick("attune_battery", "rare", 1)
|
||||
and not a._apply_curio_pick("perfect_insurance", "rare", 1),
|
||||
"出池反例:两件兑付不再受理(牌库无令牌=拒绝零副作用)〔卡② R1〕")
|
||||
Kit.check_quiet(not a.has_method("choose_battery_target") and not a.has_method("battery_pending"),
|
||||
"电池符号清零:runner 无点选函数与 getter(编译级孤儿面)〔卡② R1〕")
|
||||
Kit.check_quiet(int(db.adventure.curio_cap("attune_battery")) == 99,
|
||||
"出池后未知 kind 走分发表 default(cap 读数兜底 99——零崩溃面)〔卡② R1〕")
|
||||
# 账本保留(契约 1):主动件充能账与每冒险重置入口零改动
|
||||
a._attunement = ["incense_stick"]
|
||||
a.run_ledger.attune_charges = {"incense_stick": 99}
|
||||
a._seed_attune_charges()
|
||||
Kit.check_quiet(int(a.run_ledger.attune_charges.get("incense_stick", -1)) != 99,
|
||||
"账本保留:删件后 _seed_attune_charges 仍按装备重置充能账(attune_charges 语义零变化)〔卡② R1〕")
|
||||
# V24 配额净删锚(R3——用户裁「净删」,rare 9→7/总量 104→102)
|
||||
var rare_sum := 0
|
||||
var rare_kinds: Array[String] = []
|
||||
for k in db.adventure.CURIO_KINDS:
|
||||
var q := int(db.adventure.curio_quota(k, "rare"))
|
||||
rare_sum += q
|
||||
if q > 0:
|
||||
rare_kinds.append(k)
|
||||
Kit.check_quiet(int(db.adventure.TIER_QUOTAS["rare"]) == 7 and rare_sum == 7
|
||||
and int(db.adventure.deck_total()) == 102 and rare_kinds.has("ap") and rare_kinds.has("rap") and rare_kinds.has("time_sand")
|
||||
and rare_kinds.size() == 3,
|
||||
"配额锚:稀有档合计 7、投放品种={ap,rap,time_sand}、令牌池总量 102(validate 硬断言同批)〔卡② R3〕")
|
||||
Kit.check_quiet(db.adventure.validate().is_empty(), "AdventureTable.validate() 零违例(两件出池后)〔卡② R3〕")
|
||||
|
||||
# V9-6 全链:凭证令牌兑付=牌库摘牌(选中即消尽)
|
||||
a._curio_deck = [{"kind": "rap", "tier": "rare"}, {"kind": "attack", "tier": "common"}]
|
||||
@@ -91,17 +113,80 @@ func _init() -> void:
|
||||
sm.load_from_disk()
|
||||
a.run_ledger.adventure_counters = {"pain_layers": 3, "twin_digs": 5} # 迭代 27 T3——四账住 RunLedger
|
||||
a._refresh_stock = 2
|
||||
a._battery_pending = 1
|
||||
a.set_save_manager(sm)
|
||||
a._on_level_finished({"result": "victory", "hp": 10, "node_index": 1, "node_id": 1, "node_type": "shop"})
|
||||
var bp16: Dictionary = sm.breakpoint_snapshot()
|
||||
Kit.check_quiet(int((bp16.get("adventure_counters", {}) as Dictionary).get("pain_layers", -1)) == 3
|
||||
and int(bp16.get("refresh_stock", -1)) == 2 and int(bp16.get("battery_pending", -1)) == 1,
|
||||
"V16:新账本三件入断点快照(adventure_counters/refresh_stock/battery_pending)〔专长批 V16〕")
|
||||
and int(bp16.get("refresh_stock", -1)) == 2 and not bp16.has("battery_pending"),
|
||||
"V16:账本入断点快照;电池键已随卡② 摘除(断点结构零该键)〔专长批 V16+卡② R1〕")
|
||||
var b2 = load("res://core/adventure_runner.gd").new(db, load("res://core/rng_service.gd").new())
|
||||
Kit.check_quiet(b2.resume_adventure(bp16.duplicate(), {}), "V16 前置:续玩受理")
|
||||
Kit.check_quiet(int(b2.run_ledger.adventure_counters.get("pain_layers", -1)) == 3 and b2.refresh_stock() == 2 and b2.battery_pending() == 1,
|
||||
var bp_legacy: Dictionary = bp16.duplicate(true)
|
||||
bp_legacy["battery_pending"] = 7 # 旧断点档遗留键(Alpha 无迁移——缺键/多键兼容面)
|
||||
var b3 = load("res://core/adventure_runner.gd").new(db, load("res://core/rng_service.gd").new())
|
||||
Kit.check_quiet(b3.resume_adventure(bp_legacy, {}), "V16:旧档含 battery_pending 键续玩受理(缺键/多键零报错)〔卡② R1〕")
|
||||
Kit.check_quiet(int(b2.run_ledger.adventure_counters.get("pain_layers", -1)) == 3 and b2.refresh_stock() == 2,
|
||||
"V16:续玩回填逐项一致(计数恢复——跨战账不丢)〔专长批 V16〕")
|
||||
# V25 完美保险专长验收组(迭代 31 卡② T4/T5——R2 全项:装载/限次一次/计数纯净/两账相减/跨战清零/事件不吞)
|
||||
var fi: Dictionary = db.feats.row_by_id("perfect_insurance")
|
||||
Kit.check_quiet(not fi.is_empty() and String(fi.get("rarity", "")) == "rare"
|
||||
and String(fi.get("desc", "")).find("雷") == -1 and String(fi.get("desc", "")).find("封印") >= 0,
|
||||
"专长行合法:第 30 行 perfect_insurance 在表、rarity rare、desc 零「雷」字面〔卡② R2〕")
|
||||
Kit.check_quiet(db.feats.validate().is_empty(), "feats.validate() 零违例(新行订阅形态与词表合法)〔卡② R2〕")
|
||||
var RigScript := load("res://core/battle_orchestrator.gd")
|
||||
var RngScript := load("res://core/rng_service.gd")
|
||||
var rig_rng = RngScript.new()
|
||||
rig_rng.set_master_seed(778001) # EC7 未播种取数致命——rig 须先播种子
|
||||
var orch = RigScript.new(db, rig_rng)
|
||||
Kit.check_quiet(orch.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": ["sense_guard"], "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"}),
|
||||
"保险 rig:战斗启动(feats=perfect_insurance 与 tree_rules=sense_guard 双订阅 seal_missed)〔卡② R2〕")
|
||||
var safe_cells: Array = []
|
||||
for c in orch.minesweeper.current_board().all_cells():
|
||||
if not orch.minesweeper.current_board().is_mine(c):
|
||||
safe_cells.append(c)
|
||||
if safe_cells.size() >= 2:
|
||||
break
|
||||
Kit.check_quiet(safe_cells.size() == 2, "保险 rig:两枚安全格定位(去重闸按格实例——限次样本须异格)〔卡② R2〕")
|
||||
Kit.check_quiet(int(orch._perfect_hit_false) == 0 and int(orch._perfect_forgiven) == 0,
|
||||
"保险前置:误标账与豁免账双零起〔卡② R2〕")
|
||||
orch._grant_seg._judge_seal({"cell": safe_cells[0], "hit": false}, 1)
|
||||
Kit.check_quiet(int(orch._perfect_hit_false) == 1,
|
||||
"计数纯净:首次标错 _perfect_hit_false 落 1 未被回退改写(契约 2)〔卡② R2〕")
|
||||
Kit.check_quiet(int(orch._perfect_forgiven) == 1,
|
||||
"豁免一次:首次标错 _perfect_forgiven=1(forgive_seal_miss 派发)〔卡② R2〕")
|
||||
Kit.check_quiet(int(orch.action_economy.pending_ap_delta()) == 1,
|
||||
"事件不吞:同一标错的 seal_missed 仍被危险直觉·守收到(queue AP+1)〔卡② R2〕")
|
||||
orch._grant_seg._judge_seal({"cell": safe_cells[1], "hit": false}, 2)
|
||||
Kit.check_quiet(int(orch._perfect_hit_false) == 2 and int(orch._perfect_forgiven) == 1,
|
||||
"限次:第二次标错不获豁免(forgiven 恒 1——when counter lt 1)〔卡② R2〕")
|
||||
Kit.check_quiet(not orch.perfect_sweep_earned(),
|
||||
"判定相减:误标 2 − 豁免 1 = 有效 1 → 完美不成立〔卡② R2 真值表〕")
|
||||
orch._perfect_hit_false = 1
|
||||
orch._perfect_hit_true = 1
|
||||
Kit.check_quiet(orch.perfect_sweep_earned(),
|
||||
"判定相减:误标 1 − 豁免 1 = 有效 0 且标中≥1 → 完美成立〔卡② R2 真值表〕")
|
||||
orch._perfect_hit_true = 0
|
||||
Kit.check_quiet(not orch.perfect_sweep_earned(),
|
||||
"判定守卫:标中数 0 时零误标也不成立(板级 ≥1 标中条件零松动)〔卡② R2 真值表〕")
|
||||
var rig_rng2 = RngScript.new()
|
||||
rig_rng2.set_master_seed(778002)
|
||||
var orch2 = RigScript.new(db, rig_rng2)
|
||||
Kit.check_quiet(orch2.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": 2,
|
||||
"bonus_ref": {"atk": 0, "max_hp": 0, "armor_class": 0}, "adventure_counters": {},
|
||||
"voucher_rap": 0, "voucher_time_sand": 0, "node_type": "battle"}),
|
||||
"次战 rig:第二战编排启动(feats 同件)〔卡② R2〕")
|
||||
var sc2 = safe_cells[0]
|
||||
orch2._grant_seg._judge_seal({"cell": sc2, "hit": false}, 1)
|
||||
Kit.check_quiet(int(orch2._perfect_forgiven) == 1 and int(orch2._perfect_hit_false) == 1,
|
||||
"跨战清零:domain=battle 豁免账每战重建——次战首次标错再豁免一次〔卡② R2〕")
|
||||
Kit.check_quiet(EffectEngineKindProbe(db), "词表闭集:forgive_seal_miss 在 EFFECT_KINDS(出域即加载致命——扩签合规)〔卡② R4〕")
|
||||
|
||||
sm.clear_breakpoint()
|
||||
|
||||
quit(Kit.finish("feat_vouchers"))
|
||||
Reference in new issue
Block a user