diff --git a/design/gdd/systems/跨局循环/生涯.md b/design/gdd/systems/跨局循环/生涯.md index 74a3dc4b..fc52d19d 100644 --- a/design/gdd/systems/跨局循环/生涯.md +++ b/design/gdd/systems/跨局循环/生涯.md @@ -52,7 +52,7 @@ 2026-10-06 用户裁(P2 甲):每局一行摘要,行内给**结局 / 到达关卡 / 钥石收入 / 用时**四件事,上限存最近 N 局。对应三件实绩(本批代码级复核确认的缺口,非新建数据面): 1. **扩容**:cap 10 → N。N 候选 20 / 50 / 维持 10,实装期按存档体积读数三选一;读数方法与列表形态的参数化维度由设计稿给出、实装期执行。 -2. **补「用时」埋点**:全仓现在没有任何用时采集点(`game/core/` 内 grep elapsed / ticks / duration / Time 零命中;摘要字段集 `adventure_runner.gd:1007-1018` 无此项)——属新增埋点,随期二。 +2. **补「用时」埋点**:全仓现在没有任何用时采集点(`game/core/` 内 grep elapsed / ticks / duration / Time 零命中;摘要字段集 `adventure_runner.gd:814-826` 无此项)——属新增埋点,随期二。 3. **旧档迁移**:旧档既有 ≤10 条摘要原样保留迁移,向后兼容。 单局结算明细回看:遗留件 `settlement_review_overlay.gd`(`game/presentation/views/`,文件本体在盘、全仓无实例化引用——仅 `ui_kit.gd:356` 样式注记提及)由期二实装批复活承接,改挂生涯(2026-10-06 用户裁 P5 甲)。 diff --git a/game/core/action_economy.gd b/game/core/action_economy.gd index 9fef4244..17a48750 100644 --- a/game/core/action_economy.gd +++ b/game/core/action_economy.gd @@ -6,11 +6,12 @@ class_name ActionEconomy extends RefCounted -signal round_end_stopped(payload: Dictionary) # 双发射源之停手路径(行动经济 直发;payload 与 排雷玩法 棋盘路径同构) +signal round_end_stopped(payload: Dictionary) ## 双发射源之停手路径(行动经济 直发;payload 与 排雷玩法 棋盘路径同构) const PHASE_IDLE := "Idle" const PHASE_ACTIVE := "Active" const PHASE_WAITING := "Waiting" +const CHARGE_LAYER_CAP := 3 # 蓄力层上限(机制结构常量,非玩法表值——整备军备面) # 相位轴:内部 enum + match,外部 phase getter 保持 # String 面(presentation_snapshot 与冒烟链的 "Idle"/"Active"/"Waiting" 对照契约)。 @@ -182,14 +183,14 @@ func grant_reserve_ap(n: int) -> void: # 轮末停手转存决策(round_end 发射点调用——编排器授予段尾):transfer=true=剩余 AP 转蓄力层 -# (上限 3,整备军备面);false=按转存率转存 RAP(transfer_pct 百分比口径缺省 100=1:1; +# (上限 CHARGE_LAYER_CAP,整备军备面);false=按转存率转存 RAP(transfer_pct 百分比口径缺省 100=1:1; # 储备罐 rap_transfer_mult magnitude 125=1.25:1,整数除法向下取整=每 4 AP 多转 1)。 # 非停手轮(cleared/耗尽)_last_stop_ap=0 两分支均零操作。转蓄后达上限的沿发 charge_full(R6 框架先行——现无订阅者)。 func finish_round_stop(transfer: bool, transfer_pct: int = 100) -> void: if transfer: var prev_layers := _charge_layers - _charge_layers = mini(3, _charge_layers + _last_stop_ap) - if _fx != null and _charge_layers >= 3 and prev_layers < 3: + _charge_layers = mini(CHARGE_LAYER_CAP, _charge_layers + _last_stop_ap) + if _fx != null and _charge_layers >= CHARGE_LAYER_CAP and prev_layers < CHARGE_LAYER_CAP: _fx.dispatch(_ctx, "charge_full", {"layers": _charge_layers}) else: _rap += _last_stop_ap * maxi(1, transfer_pct) / 100 diff --git a/game/core/adventure_runner.gd b/game/core/adventure_runner.gd index 4823b21e..a91d904e 100644 --- a/game/core/adventure_runner.gd +++ b/game/core/adventure_runner.gd @@ -1,15 +1,15 @@ ## VERTICAL SLICE - NOT FOR PRODUCTION -## 冒险结构编排器:三关序列、生命/GP 账本、三选一停点与三结局结算唯一所有者,headless 可跑完整冒险(垂直切片验证:一条命走完三关)。 +## 冒险结构编排器:三关序列、生命/GP 账本、三选一停点与三结局结算唯一所有者,headless 可跑完整冒险(垂直切片验证:一条命走完三关);令牌池与凭证判定归 CurioDeck 件。 ## Core 层禁 UI 引用;数值零字面量只读 db;core↔core 连信号一律方法 Callable,禁 lambda 捕获 self(RefCounted 成环)。 ## 主种子恰一次注入归本类(LevelRunner 不重播种);set_save_manager 缺省 null = 断点面整体静默(模拟/冒烟零扰动)。 ## 呈现层须在 level_started 后重取 runner()(每关新建实例)——先重取再开选路口,否则被空防御丢弃。 class_name AdventureRunner extends RefCounted -signal level_started(level_index: int, display_name: String) # 每关开始(含冒险首关)——呈现层重取 runner()、刷新图条与层名行 -signal curio_choice_presented(payload: Dictionary) # 专长三选一停点(非末关通关恰一次;通道白名单后候选恒专长+回血在载荷)——{level_index, candidates, heal_gained, hp_before, hp_after, keystone_gained, keystone_total, gp, aux_cleared} -signal curio_resolved(result: Dictionary) # 三选一兑付完成(pick_curio/替换受理后恰一次)——{picked, value, hp_before, hp_after, level_index} -signal adventure_finished(summary: Dictionary) # 冒险结算(completed/failed/abandoned)——summary 五件事:打穿没有/通关几关/止步第几关/钥石合计/主种子(+additive keystone_drops) +signal level_started(level_index: int, display_name: String) ## 每关开始(含冒险首关)——呈现层重取 runner()、刷新图条与层名行 +signal curio_choice_presented(payload: Dictionary) ## 专长三选一停点(非末关通关恰一次;通道白名单后候选恒专长+回血在载荷)——{level_index, candidates, heal_gained, hp_before, hp_after, keystone_gained, keystone_total, gp, aux_cleared} +signal curio_resolved(result: Dictionary) ## 三选一兑付完成(pick_curio/替换受理后恰一次)——{picked, value, hp_before, hp_after, level_index} +signal adventure_finished(summary: Dictionary) ## 冒险结算(completed/failed/abandoned)——summary 五件事:打穿没有/通关几关/止步第几关/钥石合计/主种子(+additive keystone_drops) # ---- 转发族(签名与 LevelRunner 同款——呈现层对接透明;S6 经 _forward_* 方法)---- signal node_entered(node: Dictionary, info: Dictionary) signal choices_presented(candidates: Array) @@ -56,23 +56,33 @@ 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_deck: Array[Variant] = [] # 令牌池(TP0 v2.2——持久多重集,元素 {"kind","tier"};洗牌抽候选/选中踢池未选回池/池耗即枯竭;商店货架同库共账;规则改写型特性带=relic 令牌随表追加) +var _curio: CurioDeck = null # 令牌池件(_init 建;多重集/抽签/cap/凭证判定唯一宿主) +var _curio_taken: Dictionary[Variant, Variant]: # 件获取次数计数(kind 维度派生账本——断点附随件与摘要读数承载) + get: return _curio.taken + set(v): _curio.taken = v +var _curio_deck: Array[Variant]: # 令牌池(TP0 v2.2——持久多重集,元素 {"kind","tier"};商店货架同库共账) + get: return _curio.deck + set(v): _curio.deck = v var _feats: Array[String] = [] # 特性持有集(局内随机渠道获得、冒险级跨关携带、收束清零;🔴 层级纪律承载面:不入同调位不入局外树) var _tree_rules: Array[Variant] = [] # 树 rule 键集(Fork 激活侧 effects 透传;下传 enc.tree_rules 消费面) var _curio_pending := {} # 停点态:{"candidates": Array, "payload": Dictionary, "chosen"/"incoming": 替换在途}——空 = 无停点 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: int: # 专长凭证保底计数(T5——展示次数:节点开面/货架抽签/刷新重展各计一次;触发清零) + get: return _curio.ticket_pity + set(v): _curio.ticket_pity = v +var _feat_ticket_sold: int: # 专长凭证限购计数(T5——选中/买下即 +1;拿到即开不进持有集) + get: return _curio.ticket_sold + set(v): _curio.ticket_sold = v var _shop_mode := "legacy" # 商店模式("legacy"=固定三件加定价〔off 对照承载,缺省零扰动〕/"pool"=共享池抽 3 买 1 重抽;真实入口 main.gd 显式投 pool) func _init(db_ref: ConfigDb, rng_ref: RngServiceCore) -> void: db = db_ref rng = rng_ref + _curio = CurioDeck.new(db, rng) + _curio.bind_handlers(_apply_curio_pick, _redeem_feat_ticket) # ---- 冒险生命周期 ---- @@ -150,6 +160,32 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool: return false master_seed = int(bp.get("master_seed", 0)) rng.restore_adventure(master_seed, int(bp.get("rng_counter", 0))) + _resume_ledger(bp) + _resume_curio_state(bp) + _resume_params(bp, params) + _resume_counters(bp) + var bp_pending: Dictionary[Variant, Variant] = bp.get("bonus_pending", {}) as Dictionary + var bp_result: Dictionary[Variant, Variant] = bp.get("bonus_result", {}) as Dictionary + if not bp_pending.is_empty(): + _resume_pending(bp_pending) + return true + if not bp_result.is_empty(): + _pending_next_level = true + _curio_result = bp_result.duplicate(true) + _state = State.RUNNING + curio_resolved.emit(_curio_result.duplicate(true)) + return true + _resume_bp = bp.duplicate(true) + # 充能账本随断点回局(缺键 = 旧档/无断点,保持 _seed_attune_charges 种子态) + var bp_charges = _bp_read("attunement_charges", bp) + if bp_charges is Dictionary: + run_ledger.attune_charges = (bp_charges as Dictionary).duplicate(true) + _state = State.RUNNING + return _start_current_level() + + +# 续玩账面回填(账本/加算区/已购档/钥石/关序——段序与原读侧一致;bonus 子键 clamp 类夹 ≥0) +func _resume_ledger(bp: Dictionary) -> void: ledger = {"hero_hp": int(bp.get("hero_hp", 0)), "gp": int(bp.get("gp", 0))} var bp_bonus: Dictionary[Variant, Variant] = bp.get("bonus", {}) as Dictionary run_ledger.bonus = {"atk": maxi(0, int(bp_bonus.get("atk", 0))), @@ -162,6 +198,10 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool: levels_total = db.level_count() # 表访问收口(关数读 level 表行数) level_index = int(bp.get("level_index", 1)) _pending_next_level = false + + +# 续玩三选一态回填(池与件计数归件、停点态归本类;dual_merge/keep_on_null 策略见 BREAKPOINT_FIELDS) +func _resume_curio_state(bp: Dictionary) -> void: _reset_curio_state() var bp_taken: Dictionary[Variant, Variant] = bp.get("bonus_taken", {}) as Dictionary for id in _curio_taken.keys(): @@ -170,14 +210,14 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool: _curio_taken[String(id)] = maxi(0, int(bp_taken.get(id, 0))) # 身份维键(relic:)随快照还原 # 令牌池余库还原(TP0 v2.2——快照缺键 = 旧版断点,保持满库重建态:旧档只读一版周期的防御口径; # relic 令牌 feat_id 随还原) - var bp_deck = bp.get("bonus_deck", null) + var bp_deck = _bp_read("bonus_deck", bp) if bp_deck is Array: var restored: Array[Variant] = [] for t in (bp_deck as Array): var tok: Dictionary[Variant, Variant] = t as Dictionary var rtok := {"kind": String(tok.get("kind", "")), "tier": String(tok.get("tier", ""))} if String(rtok["kind"]) == "relic": - rtok["feat_id"] = String(tok.get("feat_id", "")) + rtok["feat_id"] = String(tok.get("feat_id", "")) # relic 令牌 feat_id 随还原 restored.append(rtok) _curio_deck = restored # 特性持有集还原(缺键 = 旧版断点防御空集) @@ -185,6 +225,9 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool: for r in (bp.get("relics", []) as Array): _feats.append(String(r)) _keystone_drops = (bp.get("keystone_drops", []) as Array).duplicate(true) + +# 续玩参数面回填(同 start_adventure 参数面;生命账本越界钳制在此落——clamp 类) +func _resume_params(bp: Dictionary, params: Dictionary) -> void: _base_override = _normalize_override(params.get("hero_override", {})) hero_attack_type = String(params.get("attack_type", "")) # 续玩参数面同 start_adventure(账号层选中态随 resume 重注入) hero_armor_type = String(params.get("armor_type", "")) @@ -198,58 +241,46 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool: _shop_mode = "pool" if String(params.get("shop_mode", "legacy")) == "pool" else "legacy" # 续玩参数面同 start_adventure(缺省 legacy) _treasure_unlocked = params.get("treasure_unlocked", {}) if params.get("treasure_unlocked", {}) is Dictionary else {} _attunement = params.get("attunement", []) if params.get("attunement", []) is Array else [] # additive - _attune_refresh_y = maxi(0, int(bp.get("attune_refresh_y", int(params.get("attune_refresh_y", 0))))) # Y 余量优先读断点(花掉不回血=堵「用完退菜单再续玩满血复活」可利用漏洞);缺键=旧版断点,回退参数面整额注入(旧档行为零变化) + # Y 余量优先读断点(花掉不回血=堵「用完退菜单再续玩满血复活」可利用漏洞);缺键=旧版断点, + # 回退参数面整额注入(旧档行为零变化)——params_fallback 类 + _attune_refresh_y = _bp_read_int("attune_refresh_y", bp, 0, params) _seed_attune_charges() # 冒险开始充能账本按装备主动件重置(每冒险充能语义) _tree_rules = params.get("tree_rules", []) if params.get("tree_rules", []) is Array else [] # A/B 面 rule 键集(续玩参数面同 start_adventure 同式,消费面 set_tree_rules 下传) - # 三选一停点续玩分支(选择态/已兑付态不重跑关卡;奖励选择态不重抽、已兑付不重付) - var bp_pending: Dictionary[Variant, Variant] = bp.get("bonus_pending", {}) as Dictionary + + +# 续玩计数器回填(凭证计数/冒险域计数/刷新存量/真视计数) +func _resume_counters(bp: Dictionary) -> void: _feat_ticket_pity = int(bp.get("feat_ticket_pity", 0)) # T5 扩列——旧档缺键缺省 0(存档兼容) _feat_ticket_sold = int(bp.get("feat_ticket_sold", 0)) # adventure 域计数器回填(缺键 = 旧版断点防御空集;_reset_curio_state 已清零在先) - var bp_counters = bp.get("adventure_counters", null) + var bp_counters = _bp_read("adventure_counters", bp) if bp_counters is Dictionary: run_ledger.adventure_counters = (bp_counters as Dictionary).duplicate(true) - _refresh_stock = maxi(0, int(bp.get("refresh_stock", 0))) # 旧档缺键缺省 0 - run_ledger.vision_counter = {"used": maxi(0, int(bp.get("vision_counter_used", 0)))} # 真视计数随断点回填(缺键=旧断点缺省 0) - var bp_result: Dictionary[Variant, Variant] = bp.get("bonus_result", {}) as Dictionary - if not bp_pending.is_empty(): - _pending_next_level = true - # 候选令牌化迁移(TP0——旧版断点候选为 kind 字符串,按普通档令牌防御归一;单版本周期口径) - var migrated: Array[Variant] = [] - for c in (bp_pending.get("candidates", []) as Array): - if c is Dictionary: - # T4——feat_id 随迁移(通关渠道候选=纯专长令牌,身份键 relic|tier|feat_id 三段全要) - var mig := {"kind": String((c as Dictionary).get("kind", "")), "tier": String((c as Dictionary).get("tier", ""))} - if String(mig["kind"]) == "relic": - mig["feat_id"] = String((c as Dictionary).get("feat_id", "")) - migrated.append(mig) - else: - migrated.append({"kind": String(c), "tier": db.adventure.TIER_COMMON}) - _curio_pending = {"candidates": migrated} - var payload: Dictionary[Variant, Variant] = (bp_pending.get("payload", {}) as Dictionary).duplicate(true) - _state = State.RUNNING - curio_choice_presented.emit(payload) - return true - if not bp_result.is_empty(): - _pending_next_level = true - _curio_result = bp_result.duplicate(true) - _state = State.RUNNING - curio_resolved.emit(_curio_result.duplicate(true)) - return true - _resume_bp = bp.duplicate(true) - # 充能账本随断点回局(缺键 = 旧档/无断点,保持 _seed_attune_charges 种子态) - var bp_charges = bp.get("attunement_charges", null) - if bp_charges is Dictionary: - run_ledger.attune_charges = (bp_charges as Dictionary).duplicate(true) + _refresh_stock = _bp_read_int("refresh_stock", bp, 0) # 旧档缺键缺省 0(clamp 类) + run_ledger.vision_counter = {"used": _bp_read_int("vision_counter_used", bp, 0)} # 真视计数随断点回填(clamp 类) + + +# 续玩选择停点回填(token_migration 类:旧版断点候选为 kind 字符串,按普通档令牌防御归一) +func _resume_pending(bp_pending: Dictionary) -> void: + _pending_next_level = true + var migrated: Array[Variant] = [] + for c in (bp_pending.get("candidates", []) as Array): + if c is Dictionary: + # T4——feat_id 随迁移(通关渠道候选=纯专长令牌,身份键 relic|tier|feat_id 三段全要) + var mig := {"kind": String((c as Dictionary).get("kind", "")), "tier": String((c as Dictionary).get("tier", ""))} + if String(mig["kind"]) == "relic": + mig["feat_id"] = String((c as Dictionary).get("feat_id", "")) + migrated.append(mig) + else: + migrated.append({"kind": String(c), "tier": db.adventure.TIER_COMMON}) + _curio_pending = {"candidates": migrated} + var payload: Dictionary[Variant, Variant] = (bp_pending.get("payload", {}) as Dictionary).duplicate(true) _state = State.RUNNING - return _start_current_level() + curio_choice_presented.emit(payload) -# 三选一停点态归零(冒险开始/结算收束共用;令牌池按表配额重建——池消耗即上限,账本随池重生。 -# adventure 域计数器账本随冒险开始清零;resume 路径调用后由断点附随件回填) func _reset_curio_state() -> void: - _curio_taken = {"attack": 0, "max_hp": 0, "armor": 0, "gp": 0, "ap": 0, "heal": 0} - _curio_deck = _build_curio_deck() + _curio.reset() # 池与件获取计数(池半归件;停点态归本类下行清理) _feats = [] _curio_pending = {} _curio_result = {} @@ -261,30 +292,6 @@ func _reset_curio_state() -> void: _time_sand_queue = 0 -# 令牌池构建(TP0 v2.2——按表 kind×tier 配额展开为持久多重集;构建序确定 = CURIO_KINDS×TIERS 逐层, -# 随机性只发生在洗牌消费 reward_pick 封闭流的时刻。规则改写型特性带=relic 活行每件 1 张追加。 -# relic 令牌档 = 行 rarity 直映射(四级词表内直用;词表外的 transitional 值回退映射 -# rarest→rare/其余→excellent);relic 可入普通档(奇物.md 池结构节无档位限制)。 -func _build_curio_deck() -> Array: - var deck: Array[Variant] = [] - for kind in db.adventure.CURIO_KINDS: - for tier in db.adventure.TIERS: - for i in int(db.adventure.curio_quota(String(kind), String(tier))): - deck.append({"kind": String(kind), "tier": String(tier)}) - for row in db.feat_rows(): - var rraw := String(row.get("rarity", "")) - var rtier := rraw if db.adventure.TIERS.has(rraw) else ("rare" if rraw == "rarest" else "excellent") - deck.append({"kind": "relic", "tier": rtier, "feat_id": String(row.get("feat_id", ""))}) - return deck - - -# 令牌唯一身份(抽签去重/兑付匹配共用——属性加成型特性=kind,规则改写型特性=feat_id;同档多张不同 relic 可同屏) -func _token_identity(tok: Dictionary) -> String: - if String(tok.get("kind", "")) == "relic": - return "relic:" + String(tok.get("feat_id", "")) - return String(tok.get("kind", "")) - - # 调试浮窗百分比通道(测试工具覆盖——每关新 runner 都要应用;正式侧归元进度) func set_hero_override(atk_pct: int, hp_pct: int) -> void: _override = [atk_pct, hp_pct] @@ -347,14 +354,7 @@ func skip_empty_curio() -> bool: # 候选令牌匹配(身份键恒等——属性加成型特性 kind+tier、规则改写型特性 feat_id) func _find_token(candidates: Array, kind: String, tier: String, feat_id: String = "") -> Dictionary: - for t in candidates: - var tok: Dictionary[Variant, Variant] = t as Dictionary - if String(tok.get("kind", "")) != kind or String(tok.get("tier", "")) != tier: - continue - if kind == "relic" and String(tok.get("feat_id", "")) != feat_id: - continue - return tok - return {} + return _curio.find_token(candidates, kind, tier, feat_id) # 玩家点选兑付(呈现层/冒烟/自动模拟确认入口):令牌 ∈ 停点候选(身份键精确匹配——relic 件 @@ -441,15 +441,7 @@ func _apply_curio_pick(kind: String, tier: String, value: int, feat_id: String = # 从牌库摘除一枚令牌(TP0——选中踢池单点;relic 按 feat_id 精确摘;找不到 = false) func _consume_deck_token(kind: String, tier: String, feat_id: String = "") -> bool: - for i in _curio_deck.size(): - var tok: Dictionary[Variant, Variant] = _curio_deck[i] as Dictionary - if String(tok.get("kind", "")) != kind or String(tok.get("tier", "")) != tier: - continue - if kind == "relic" and String(tok.get("feat_id", "")) != feat_id: - continue - _curio_deck.remove_at(i) - return true - return false + return _curio.consume_deck_token(kind, tier, feat_id) # 特性持有集读面(效果接线/求值/摘要唯一取数口——副本防外部改写) @@ -459,37 +451,19 @@ func feats_held() -> Array: # ---- 商店共享池消费面(共享池单点账本;TP0——库存=牌库剩余令牌)---- -# 库存余量(TP0:stock = 牌库内该 kind 剩余令牌数——「池消耗即上限」,无种类次数上限; -# 奖励三选一兑付摘牌 与 商店购买消费 由同一 _curio_deck 承载——两渠道跨关共库,全冒险一次性)。 +# 库存余量(池消耗即上限;奖励摘牌与商店购买同库承载,全冒险一次性) func pool_stock(kind: String) -> int: - var n := 0 - for t in _curio_deck: - if String((t as Dictionary).get("kind", "")) == kind: - n += 1 - return n + return _curio.pool_stock(kind) -# 商店购买消费 handler(LevelRunner.buy_pool_item 经 Callable 调用——S6 方法引用): -# 消费在架令牌本体(kind+tier 精确——货架卡即令牌,TP0 令牌级货架)→ _apply_curio_pick 同口 -# 写加算区+账本+摘牌(与奖励三选一真单点——买一件=奖励面少一件的库存语义由同库成立)。 -# 未知 kind 拒绝(货架词表封闭的前置防御)。 +# 商店购买消费 handler(LevelRunner.buy_pool_item 经 Callable 调用——回 _apply_curio_pick 同口) func _consume_pool_purchase(kind: String, tier: String) -> bool: - if kind == "feat_ticket": - return _redeem_feat_ticket() # T5——专长凭证买下即开(零摘牌不进持有集;临时停点开专长三选一面) - if not db.adventure.CURIO_KINDS.has(kind): - push_error("[冒险结构] 商店购买消费拒绝——件不在池词表: %s" % kind) - return false - return _apply_curio_pick(kind, tier, int(db.adventure.curio_value(kind, tier))) + return _curio.consume_pool_purchase(kind, tier) -# 终局池快照(_settle 清账前调用——_reset_curio_state 会重置 _curio_taken,摘要 additive 键 -# pool_stock 须在此前取数;货架四种 stock=cap−taken,读数面 = 商店买走 vs 奖励领走 vs 枯竭) +# 终局池快照(_settle 清账前调用——复位会重置计数,摘要 additive 键须在此前取数) func _pool_stock_snapshot() -> Dictionary: - var out := {} - for row in db.shop_pool_rows(): - var kind := String(row.get("kind_id", "")) - out[kind] = pool_stock(kind) - return out + return _curio.stock_snapshot() # 兑付收口(恰一次):清停点态 → 记已兑付态 → emit → 停点快照(等继续确认——中断续玩不重兑付)。 @@ -656,134 +630,19 @@ func _finish_node_bonus(picked: String, value: int, tier: String = "", feat_id: curio_resolved.emit(payload) -# 令牌池洗牌不放回抽 3(TP0 v2.2——Fisher-Yates 全库洗牌后按序收集;**同 kind 去重每 kind 至多 -# 一枚**〔先到先得,档位取该 kind 首枚——「三选一=三种不同选项」体验延续,与货架抽签同规则〕; -# kind 出现率∝剩余令牌数〔档位配额即分布——无独立权重 knob〕;抽签不摘牌、兑付才摘牌=未选 -# 自动回池;池耗即枯竭——余库不足 N kind 时出齐剩余,空库 = 零候选合法态)。候选 = 令牌字典 -# {"kind","tier"}。消费 reward_pick 封闭流——一次停点恰一 scope,同种子同候选。 -# 渠道白名单过滤(渠道轴定位:关底 Boss 击败=通关渠道只出专长(feat 令牌); -# 奖励节点/商店只出属性值提升+资源(非 relic 令牌),专长在非 Boss 渠道只以专长凭证形态出现(T5)。 -# 过滤式而非物理拆池:deck 物理结构/存档键 bonus_deck 混编余库/消耗语义(选中踢池未选回池)原样, -# 白名单在候选视图层生效——配额结构锚 TIER_QUOTAS 70/25/3=98 零改动(专长令牌本系活行追加面,在锚外)。 -# 曝光槽:非 Boss 渠道三选一 = **2 格全池 + 1 格优秀档以上子池** -# (rank≥excellent;子池空该格回退全池;池空直通同族兜底);凭证(feat_ticket)不进子池 -# 〔独立计数体系〕;已拿上限 cap:taken≥cap 的身份在候选视图剔除〔牌库本体不动, -# 拿了才计、刷出不计——_curio_cap_ok〕。 +# 三选一候选抽签(实装归 CurioDeck 件;消费 reward_pick 封闭流——一次停点恰一 scope) func _draw_curio_candidates(channel: String = "reward_node") -> Array: - var pool := _curio_deck.duplicate() - if channel == "level_clear": - pool = pool.filter(func(tok): return String((tok as Dictionary).get("kind", "")) == "relic") - else: - pool = pool.filter(func(tok): - var k := String((tok as Dictionary).get("kind", "")) - return k != "relic" and k != "feat_ticket") - pool = pool.filter(func(tok): - var t: Dictionary[Variant, Variant] = tok as Dictionary - return _curio_cap_ok(String(t.get("kind", "")), String(t.get("feat_id", "")))) - var rg: RandomNumberGenerator = rng.open_stream("reward_pick") - if channel == "level_clear": - # Boss 渠道按档配比加权抽取(无放回加权抽:逐席按 boss_feat_weight 掷点, - # 身份去重;rarity 语义约束兑付面——奇物.md v3.1,配比数值归 adventure 表) - var out_w: Array[Variant] = [] - var seen_w: Array[Variant] = [] - var want_w := int(db.adventure.curio_candidate_count) - while out_w.size() < want_w and not pool.is_empty(): - var total_w := 0 - for tok_w in pool: - total_w += db.adventure.boss_feat_weight(String((tok_w as Dictionary).get("tier", ""))) - if total_w <= 0: - break - var roll_w := rg.randi_range(1, total_w) - var pick_i := 0 - for i_w in pool.size(): - roll_w -= db.adventure.boss_feat_weight(String((pool[i_w] as Dictionary).get("tier", ""))) - if roll_w <= 0: - pick_i = i_w - break - var tok_p: Dictionary[Variant, Variant] = pool[pick_i] - var ident_p := _token_identity(tok_p) - if seen_w.has(ident_p): - pool.remove_at(pick_i) # 同身份已出——剔牌重掷(无放回) - continue - seen_w.append(ident_p) - var cand_p := {"kind": String(tok_p.get("kind", "")), "tier": String(tok_p.get("tier", "")), "feat_id": String(tok_p.get("feat_id", ""))} - out_w.append(cand_p) - pool.remove_at(pick_i) - return out_w - for i in pool.size(): - var j := rg.randi_range(i, pool.size() - 1) - var tmp = pool[i] - pool[i] = pool[j] - pool[j] = tmp - # 子池(优秀档以上)独立洗牌——同 stream 续消费,确定性保持 - var sub := pool.filter(func(tok): - return db.adventure.tier_rank(String((tok as Dictionary).get("tier", ""))) >= db.adventure.tier_rank(db.adventure.TIER_EXCELLENT)) - for i in sub.size(): - var j2 := rg.randi_range(i, sub.size() - 1) - var tmp2 = sub[i] - sub[i] = sub[j2] - sub[j2] = tmp2 - var out: Array[Variant] = [] - var seen: Array[Variant] = [] - # 收集器:全池序取 n_full 格(普通档打底)→ 子池序取 1 格(曝光槽)→ 子池空回退全池续取 - var want := int(db.adventure.curio_candidate_count) - var n_full := maxi(1, want - 1) if want >= 2 else want - while out.size() < want: - var src: Array[Variant] = pool - if out.size() >= n_full and not sub.is_empty(): - src = sub # 曝光槽位——优先子池 - var placed := false - for tok in src: - var ident := _token_identity(tok) - if seen.has(ident): - continue - seen.append(ident) - var cand := {"kind": String((tok as Dictionary).get("kind", "")), "tier": String((tok as Dictionary).get("tier", ""))} - if ident.begins_with("relic:"): - cand["feat_id"] = String((tok as Dictionary).get("feat_id", "")) - out.append(cand) - placed = true - break - if not placed: - if out.size() >= n_full and not sub.is_empty() and not pool.is_empty(): - sub = [] # 子池枯竭——曝光槽回退全池续取 - continue - break # 全池枯竭——出齐剩余(池空直通同族兜底) - # 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 + return _curio.draw_candidates(channel) -# 已拿上限判定(身份维度:kind 走 curio_cap、relic 走 feats 行 cap 字段; -# taken 只在兑付时 +1〔拿了才计〕、刷出不计)。缺省放行=白名单外 kind 兜底。 +# 已拿上限判定(身份维度;缺省放行=白名单外 kind 兜底) func _curio_cap_ok(kind: String, feat_id: String = "") -> bool: - var cap := 99 - if kind == "relic": - var row: Dictionary[Variant, Variant] = db.feat_row(feat_id) - cap = int(row.get("cap", 1)) if not row.is_empty() else 1 - return _curio_taken.get("relic:" + feat_id, 0) < cap - if not db.adventure.CURIO_KINDS.has(kind): - return true - cap = int(db.adventure.curio_cap(kind)) - return int(_curio_taken.get(kind, 0)) < cap + return _curio.cap_ok(kind, feat_id) -# —— 专长获取专长凭证(T5——非 Boss 渠道的专长入口;四口径契约④:拿到即开不可囤积/ -# 罕见只在概率不在价格/一局限购一次/计步保底连续 5 展示未出第 6 必出——计数=展示次数、刷新 -# 重展计入、节点与商店共享、触发清零、限购满不再触发。调用即一次展示判定与记账)—— +# —— 专长获取专长凭证四口径判定(实装归 CurioDeck 件:调用即一次展示判定与记账)—— func _feat_ticket_consider(rg: RandomNumberGenerator) -> bool: - if _feat_ticket_sold >= int(db.adventure.feat_ticket_limit): - return false # 限购满——不再触发(保底冻结) - if _feat_ticket_appeared >= int(db.adventure.feat_ticket_quota): - return false # 配额满——本局不再出现 - var offer: bool = _feat_ticket_pity >= int(db.adventure.feat_ticket_pity_step) or rg.randi_range(1, 100) <= int(db.adventure.feat_ticket_chance) - if offer: - _feat_ticket_pity = 0 - _feat_ticket_appeared += 1 - else: - _feat_ticket_pity += 1 - return offer + return _curio.feat_ticket_consider(rg) # 专长凭证兑付(拿到即开——停点候选替换为专长三选一;不进持有集/零摘牌;限购计数 +1): @@ -837,6 +696,29 @@ func abandon() -> bool: func _start_current_level() -> bool: _runner = LevelRunner.new(db, rng) + _bind_level_runner() + # 断点续玩分支:图由主种子+参数档重生成同图(产图与计数器号位无关,停点零重抽语义随之消解), + # resume_state 带停点句柄与已到序——重建到停点后经 present_resumed_choice 呈现(信号顺序契约同入口)。 + var resume_state: Dictionary[Variant, Variant] = {} + if not _resume_bp.is_empty(): + resume_state = {"node_id": int(_bp_read("node_id", _resume_bp, -1)), + "visited_ids": _bp_read("visited_ids", _resume_bp, []) as Array} + # 接口①开始关卡:生命经共享账本交接(账本当前值即开局生命——冒险开始置满 / 大奖励回满已写好); + # _base_override 共享引用随载荷传入(永久强化 flat——LevelRunner.effective_* 叠加序第二项)。 + # defer_entry=true 起每关(含首关)——先 emit level_started(呈现层在 + # 该回调重取 runner 实例),后入口候选呈现;顺序反了选路口会被呈现层的 runner 空防御静默丢弃。 + # 呈现零 RNG 消耗、pick 走同一 _enter_node——测量面逐位零漂移。 + var ok: bool = _runner.start_level_external(level_index, ledger, purchased, + apply_rest, _base_override, true, resume_state) + if ok: + _inject_level_params() + _present_level_entry(resume_state) + _resume_bp = {} + return ok + + +# 关卡行进器接线(每关新实例:信号转发 + 共享账下传) +func _bind_level_runner() -> void: # 树限次计数按关清零现由 LevelRunner.level_counters 随每关 new 天然承载(旧硬编码清除名单函数已删) # core↔core 接线只许方法 Callable(S6——lambda 捕获 self 会延长 RefCounted 生命周期乃至成环) _runner.node_entered.connect(_forward_node_entered) @@ -847,46 +729,36 @@ func _start_current_level() -> bool: _runner.wallet_changed.connect(_forward_wallet_changed) _runner.level_finished.connect(_on_level_finished) _runner.set_hero_override(int(_override[0]), int(_override[1])) - # 断点续玩分支:图由主种子+参数档重生成同图(产图与计数器号位无关,停点零重抽语义随之消解), - # resume_state 带停点句柄与已到序——重建到停点后经 present_resumed_choice 呈现(信号顺序契约同入口)。 - var resume_state: Dictionary[Variant, Variant] = {} - if not _resume_bp.is_empty(): - resume_state = {"node_id": int(_resume_bp.get("node_id", -1)), - "visited_ids": _resume_bp.get("visited_ids", []) as Array} - # 接口①开始关卡:生命经共享账本交接(账本当前值即开局生命——冒险开始置满 / 大奖励回满已写好); - # _base_override 共享引用随载荷传入(永久强化 flat——LevelRunner.effective_* 叠加序第二项)。 - # defer_entry=true 起每关(含首关)——先 emit level_started(呈现层在 - # 该回调重取 runner 实例),后入口候选呈现;顺序反了选路口会被呈现层的 runner 空防御静默丢弃。 - # 呈现零 RNG 消耗、pick 走同一 _enter_node——测量面逐位零漂移。 _runner.set_run_ledger(run_ledger) # 共享四账单类下传(run_ledger=四账类,ledger=关卡账) _runner.sync_attune_inventory() # 能力件骑库存面注入(随账下传同拍) - var ok: bool = _runner.start_level_external(level_index, ledger, purchased, - apply_rest, _base_override, true, resume_state) - if ok: - _runner.set_attunement(_attunement) # 同调装配下传(enc 携带进战斗结算) - _runner.set_feats(_feats) # 特性持有集下传(begin_battle enc.feats 求值面) - _runner.set_tree_rules(_tree_rules) # 树 rule 键集下传(A/B 面消费) - _runner.set_hero_axes(hero_attack_type, hero_armor_type) # 两根类型轴快照下传(effective_ac 甲型基础 AC 层+enc 携带进 spawn) - if _shop_mode == "pool": - # 商店共享池注入(库存账本引用+消费 handler 双下传;legacy 模式零注入零扰动。 - # cap 检查回调四参注入) - _runner.set_shop_pool(_curio_deck, _consume_pool_purchase, _feat_ticket_consider, _curio_cap_ok) - # 凭证件三账注入(节点级/回合级一次性:注入即清零,未用即过期=寿命档语义) - _runner.set_voucher_grants(_pending_rap, _time_sand_queue) - _pending_rap = 0 - _time_sand_queue = 0 - # 三选一道具件跨关携带注入面不存在(aux 卡不在池种内→ carry 恒空集; - # 容器字段保留=替换子交互惰性承载,零投放零行为) - level_started.emit(level_index, String(_runner.level_row.get("display_name", ""))) - # 入口停点断点(node_id=-1)与 defer_entry 同态(node_index=-1+单候选)——走入口呈现; - # 中途停点(node_id>=0)走停点呈现(restore_treasure_items 先于呈现=C 区库存就位)。 - if resume_state.is_empty() or int(resume_state.get("node_id", -1)) < 0: - _runner.present_entry_choice() - else: - _runner.restore_treasure_items(_resume_bp.get("aux_items", []) as Array) - _runner.present_resumed_choice() - _resume_bp = {} - return ok + + +# 关卡参数注入(装配集/特性/rule 键/类型轴/商店池/凭证件 + level_started 广播) +func _inject_level_params() -> void: + _runner.set_attunement(_attunement) # 同调装配下传(enc 携带进战斗结算) + _runner.set_feats(_feats) # 特性持有集下传(begin_battle enc.feats 求值面) + _runner.set_tree_rules(_tree_rules) # 树 rule 键集下传(A/B 面消费) + _runner.set_hero_axes(hero_attack_type, hero_armor_type) # 两根类型轴快照下传(effective_ac 甲型基础 AC 层+enc 携带进 spawn) + if _shop_mode == "pool": + # 商店共享池注入(库存账本引用+消费 handler 双下传;legacy 模式零注入零扰动。 + # cap 检查回调四参注入) + _runner.set_shop_pool(_curio_deck, _consume_pool_purchase, _feat_ticket_consider, _curio_cap_ok) + # 凭证件三账注入(节点级/回合级一次性:注入即清零,未用即过期=寿命档语义) + _runner.set_voucher_grants(_pending_rap, _time_sand_queue) + _pending_rap = 0 + _time_sand_queue = 0 + # 三选一道具件跨关携带注入面不存在(aux 卡不在池种内→ carry 恒空集; + # 容器字段保留=替换子交互惰性承载,零投放零行为) + level_started.emit(level_index, String(_runner.level_row.get("display_name", ""))) + + +# 入口呈现(入口停点与中途停点两态;restore_treasure_items 先于呈现=C 区库存就位) +func _present_level_entry(resume_state: Dictionary) -> void: + if resume_state.is_empty() or int(resume_state.get("node_id", -1)) < 0: + _runner.present_entry_choice() + else: + _runner.restore_treasure_items(_bp_read("aux_items", _resume_bp, []) as Array) + _runner.present_resumed_choice() # 接口④关卡结束(结果 + 生命终值 + 节点位置——胜负皆报;结算受理只算一次) @@ -983,20 +855,62 @@ func _forward_choices_presented(candidates: Array) -> void: _snapshot_breakpoint() # 停点快照(选路停点=唯一可恢复停点形态;战斗中不存) -# ---- 断点快照(七字段契约面+恢复必需附随件)---- -# 七字段(契约面):关卡序号/节点位/生命/属性加成区/GP 钱包/商店已购档/已抽模板; -# 附随件(续玩正确性必需):主种子+取数计数器(rng 快照契约回填——续接原时间线)、 -# 钥石累计(大奖励中途值——终局摘要五件事不失真)、已到节点序(呈现层图条重建)、道具库存 -# (奖励节点已选件——防静默丢失)。注入缺省 null = 整面静默(模拟/冒烟零扰动)。 +# ---- 断点快照(字段登记单源:写侧拼装与读侧回退策略同登记一表)---- +# 键序即快照键序(27 顶层键——写侧/读侧/键集断言三面同源)。读侧回退策略词表: +# plain 无特殊回退面(缺键取代码内缺省)/clamp 数值夹 ≥0/keep_on_null 缺键或类型不符保持既有态 +# dual_merge 双向合并(bonus_taken)/params_fallback 缺键回退参数面(attune_refresh_y) +# token_migration 旧版候选形态迁移(bonus_pending.candidates) +# 注入缺省 null = 整面静默(模拟/冒烟零扰动)。 +const BREAKPOINT_FIELDS := { + "level_index": {"strategy": "plain"}, + "node_id": {"strategy": "plain"}, + "hero_hp": {"strategy": "clamp"}, + "bonus": {"strategy": "clamp"}, + "gp": {"strategy": "plain"}, + "shop_purchased": {"strategy": "plain"}, + "shop_shelf": {"strategy": "plain"}, + "shop_visit_buys": {"strategy": "plain"}, + "attunement_charges": {"strategy": "keep_on_null"}, + "graph_gen_v2": {"strategy": "plain"}, + "master_seed": {"strategy": "plain"}, + "rng_counter": {"strategy": "plain"}, + "keystone": {"strategy": "plain"}, + "visited_ids": {"strategy": "plain"}, + "aux_items": {"strategy": "plain"}, + "bonus_taken": {"strategy": "dual_merge"}, + "bonus_deck": {"strategy": "keep_on_null"}, + "relics": {"strategy": "plain"}, + "bonus_pending": {"strategy": "token_migration"}, + "feat_ticket_pity": {"strategy": "plain"}, + "feat_ticket_sold": {"strategy": "plain"}, + "adventure_counters": {"strategy": "keep_on_null"}, + "vision_counter_used": {"strategy": "clamp"}, + "refresh_stock": {"strategy": "clamp"}, + "attune_refresh_y": {"strategy": "params_fallback"}, + "bonus_result": {"strategy": "plain"}, + "keystone_drops": {"strategy": "plain"}, +} + + func _snapshot_breakpoint() -> void: if _save_manager == null or state != "running": return + var parts: Dictionary[Variant, Variant] = _bp_parts() + var bp: Dictionary[Variant, Variant] = {} + for key in BREAKPOINT_FIELDS.keys(): + bp[key] = parts[key] + _save_manager.set_breakpoint(bp) + _save_manager.write_to_disk() + + +# 断点写侧取值(键集以 BREAKPOINT_FIELDS 为准——未登记键即取值越界,fail-fast) +func _bp_parts() -> Dictionary[Variant, Variant]: var treasure_snapshot: Array[Variant] = [] if _runner != null and _runner.treasure_inventory != null: treasure_snapshot = _runner.treasure_inventory.snapshot_inventory() # 三选一停点续玩分支 _runner 为 null(关卡已收束、下一关未开)——节点域字段按缺省落档, # 该两态的续玩只消费 bonus_* 附随件(选择停点/已兑付停点快照) - var bp: Dictionary[Variant, Variant] = { + return { "level_index": level_index, "node_id": _runner.current_node_id() if _runner != null else -1, "hero_hp": int(ledger.get("hero_hp", 0)), @@ -1030,8 +944,33 @@ func _snapshot_breakpoint() -> void: "bonus_result": _curio_result.duplicate(true), # 已兑付待推进态(空 = 无——中断续玩不重兑付) "keystone_drops": _keystone_drops.duplicate(true), # 逐杀掉落明细(摘要归因续接) } - _save_manager.set_breakpoint(bp) - _save_manager.write_to_disk() + + +# 读侧单点(BREAKPOINT_FIELDS 策略标签分发;未登记键 fail-fast 不静默按 plain 兜)。 +# 二次消费面(_start_current_level 经 _resume_bp 取 node_id/visited_ids/aux_items)同走此口。 +func _bp_read(key: String, bp: Dictionary, fallback: Variant = null) -> Variant: + if not BREAKPOINT_FIELDS.has(key): + push_error("[冒险结构] 断点字段未登记(读侧): %s" % key) + var v: Variant = bp.get(key, fallback) + if _bp_strategy(key) == "keep_on_null" and v != null and not (v is Dictionary or v is Array): + return null # 类型不符——保持既有态(调用方跳过覆盖) + return v + + +# 读侧策略标签(BREAKPOINT_FIELDS 读侧消费面单点) +func _bp_strategy(key: String) -> String: + return String((BREAKPOINT_FIELDS.get(key, {}) as Dictionary).get("strategy", "plain")) + + +# 数值类读侧(clamp 夹 ≥0 / params_fallback 断点缺键回退参数面整额注入 / plain 直取) +func _bp_read_int(key: String, bp: Dictionary, fallback: int, params: Dictionary = {}) -> int: + match _bp_strategy(key): + "clamp": + return maxi(0, int(bp.get(key, fallback))) + "params_fallback": + return maxi(0, int(bp.get(key, int(params.get(key, fallback))))) + _: + return int(bp.get(key, fallback)) func _forward_step_row(row: Dictionary) -> void: diff --git a/game/core/battle_orchestrator.gd b/game/core/battle_orchestrator.gd index 89e30d56..263a7a2f 100644 --- a/game/core/battle_orchestrator.gd +++ b/game/core/battle_orchestrator.gd @@ -7,10 +7,10 @@ class_name BattleOrchestrator extends RefCounted signal step_row(row: Dictionary) -signal battle_finished(payload: Dictionary) # {result, hero_hp, gp_dropped}——hero 在 despawn 前捕获(呈现层销毁后读状态的时序防护);gp_dropped = 战斗掉落合计 +signal battle_finished(payload: Dictionary) ## {result, hero_hp, gp_dropped}——hero 在 despawn 前捕获(呈现层销毁后读状态的时序防护);gp_dropped = 战斗掉落合计 signal round_started(round_index: int) signal board_changed() -signal settlement_snapshot(snap: Dictionary) # 每结算末尾的全量状态快照(playtest_logger 消费——JSONL 数值分析) +signal settlement_snapshot(snap: Dictionary) ## 每结算末尾的全量状态快照(playtest_logger 消费——JSONL 数值分析) # 明细流水行型协议(战斗结算 Core Rule;9 类; # 清单改动须同步 battle_log_view 的 _format_row 与本文档头注): diff --git a/game/core/board_generator.gd b/game/core/board_generator.gd index a3b5d1ae..c5d4db2b 100644 --- a/game/core/board_generator.gd +++ b/game/core/board_generator.gd @@ -1,7 +1,7 @@ ## VERTICAL SLICE - NOT FOR PRODUCTION ## 确定性棋盘生成器:生成-验证循环出图,不达标走接受最优/最不坏诚实降级(垂直切片验证:每张图可推理可归因)。 ## 只消费传入的单条 rng 流(重试共用,attempt 边界非 scope 事件)——同流同参逐位复现。 -## generate/legality 的 db 参数鸭子型(不标类型)——冒烟测量面注入桩表依赖此形态,勿收紧签名。 +## generate 的 db 参数鸭子型(不标类型)——冒烟测量面注入桩表依赖此形态,勿收紧签名。 class_name BoardGenerator extends RefCounted @@ -10,6 +10,11 @@ const METRIC_VERSION := 1 # ==================== 生成入口 ==================== +# 目标雷数 N = round(d×(W×H−9))(params 快照键:density/w/h)——布雷定数与产物断言两处的单点公式 +static func target_mine_count(params: Dictionary) -> int: + return int(round(float(params.density) * float(int(params.w) * int(params.h) - BoardData.SPAWN_AREA_CELLS))) + + # rng_stream = 一次 open_stream("board_gen") 实例(一张图的全部取数;重试共用——attempt 边界非 scope 事件)。 # db 参数不标具体类型原因:冒烟工具注入 FakeDB 桩(experiment_density.gd 等)——typed 形参会拒收桩表 func generate(L: int, D: int, rng_stream: RandomNumberGenerator, db) -> BoardData: # type-hint: ignore(测量面 _BoardRowStub 冒烟夹具鸭子型注入——typed 参拒 stub) @@ -50,7 +55,7 @@ func generate(L: int, D: int, rng_stream: RandomNumberGenerator, db) -> BoardDat # R₀≤cap 重掷闸触发计数(迭代14① T3——尝试事实,与合法性判定独立记录;占比=T5 闸触发率读数) if int(params.r0_cap) > 0 and b.compute_r0().size() > int(params.r0_cap): r0_cap_rejected += 1 - if legality(b, params, db): + if legality(b, params): var m: Dictionary[Variant, Variant] = measure_inferability(b) # 求解器每合法板恰一次(O3 收口——此前重复调用两次) var inf: float = m["inferability"] if best_legal == null or inf > best_legal_score: @@ -94,7 +99,7 @@ func _attempt(params: Dictionary, rng_stream: RandomNumberGenerator, db) -> Boar for c in b.all_cells(): if not spawn.has(c): domain.append(c) - var n_mines: int = int(round(params.density * float(params.w * params.h - BoardData.SPAWN_AREA_CELLS))) + var n_mines: int = target_mine_count(params) # 部分 Fisher-Yates(确定性,消费 board_gen 流) for i in range(0, mini(n_mines, domain.size())): var j := rng_stream.randi_range(i, domain.size() - 1) @@ -278,7 +283,7 @@ func is_clearable(b: BoardData) -> bool: return true -func legality(b: BoardData, params: Dictionary, db) -> bool: # type-hint: ignore(同 generate——测量面 stub 注入) +func legality(b: BoardData, params: Dictionary) -> bool: var reasons := _legality_faults(b, params) return reasons.is_empty() @@ -331,7 +336,7 @@ func _neighbor_mine_count(b: BoardData, c: Vector2i) -> int: func _finalize(b: BoardData, params: Dictionary, attempts_used: int, accept_mode: String, _db, r0_cap_rejected: int = 0) -> void: # type-hint: ignore(同 generate——测量面 stub 注入) var m := measure_inferability(b) # 产物断言(棋盘生成 出生区安全与反退化布雷):实际雷数 ≤ 目标雷数 N——先布后删只减不增,失败即致命 - var n_target: int = int(round(float(params.density) * float(params.w * params.h - BoardData.SPAWN_AREA_CELLS))) + var n_target: int = target_mine_count(params) if b.mines.size() > n_target: _fatal("产物断言违例:实际雷数 %d > 目标雷数 N=%d(先布后删只减不增)" % [b.mines.size(), n_target]) var attempt_facts := b.metrics # 保留尝试期事实(ring_removed) diff --git a/game/core/config_db.gd b/game/core/config_db.gd index 4b316db8..ea4362e0 100644 --- a/game/core/config_db.gd +++ b/game/core/config_db.gd @@ -161,7 +161,7 @@ func feat_row(feat_id: String) -> Dictionary: return feats.row_by_id(feat_id) if feats != null else {} -# 规则改写型特性带令牌构建源(池接线——adventure_runner._build_curio_deck 消费;行 rarity→投放档映射) +# 规则改写型特性带令牌构建源(池接线——CurioDeck.build 消费;行 rarity→投放档映射) func feat_rows() -> Array: return feats.rows if feats != null else [] diff --git a/game/core/curio_deck.gd b/game/core/curio_deck.gd new file mode 100644 index 00000000..5de243f2 --- /dev/null +++ b/game/core/curio_deck.gd @@ -0,0 +1,259 @@ +## VERTICAL SLICE - NOT FOR PRODUCTION +## 令牌池件:三选一/商店共享池的持久多重集、抽签、库存与专长凭证判定唯一所有者(自 adventure_runner 拆出,零行为变更)。 +## 停点编排与兑付推进不在此件(留冒险编排器)——件只管池与判定;随机只消费注入的 RngServiceCore(reward_pick 封闭流)。 +## 宿主兑付单点经 bind_handlers 注入方法 Callable(选中摘牌后的加算区/账本落点仍在宿主)。 +class_name CurioDeck +extends RefCounted + +var db: ConfigDb +var rng: RngServiceCore +var deck: Array[Variant] = [] # 令牌多重集(元素 {"kind","tier"};relic 令牌带 feat_id) +var taken: Dictionary[Variant, Variant] = {} # 件获取次数计数(kind 维度派生账本;池库存现行事实源 = deck) +var ticket_pity := 0 # 专长凭证保底计数(展示次数;触发清零) +var ticket_sold := 0 # 专长凭证限购计数(选中/买下即 +1) +var ticket_appeared := 0 # 专长凭证出现计数(quota 上限面) +var _apply_pick := Callable() # 宿主兑付回调(AdventureRunner._apply_curio_pick) +var _redeem_ticket := Callable() # 宿主凭证兑付回调(AdventureRunner._redeem_feat_ticket) + + +func _init(db_ref: ConfigDb, rng_ref: RngServiceCore) -> void: + db = db_ref + rng = rng_ref + + +# 宿主回调注入(商店购买消费面经此回宿主兑付单点——core↔core 方法 Callable,禁 lambda 捕获 self) +func bind_handlers(apply_pick: Callable, redeem_ticket: Callable) -> void: + _apply_pick = apply_pick + _redeem_ticket = redeem_ticket + + +# 池与计数归零(宿主停点复位调用的池半;令牌池按表配额重建——池消耗即上限,账本随池重生) +func reset() -> void: + taken = {"attack": 0, "max_hp": 0, "armor": 0, "gp": 0, "ap": 0, "heal": 0} + deck = build() + + +# 令牌池构建(按表 kind×tier 配额展开为持久多重集;构建序确定 = CURIO_KINDS×TIERS 逐层, +# 随机性只发生在洗牌消费 reward_pick 封闭流的时刻。规则改写型特性带 = relic 活行每件 1 张追加; +# relic 令牌档 = 行 rarity 直映射,词表外 transitional 值回退映射 rarest→rare/其余→excellent) +func build() -> Array: + var out: Array[Variant] = [] + for kind in db.adventure.CURIO_KINDS: + for tier in db.adventure.TIERS: + for i in int(db.adventure.curio_quota(String(kind), String(tier))): + out.append({"kind": String(kind), "tier": String(tier)}) + for row in db.feat_rows(): + var rraw := String(row.get("rarity", "")) + var rtier := rraw if db.adventure.TIERS.has(rraw) else ("rare" if rraw == "rarest" else "excellent") + out.append({"kind": "relic", "tier": rtier, "feat_id": String(row.get("feat_id", ""))}) + return out + + +# 令牌唯一身份(抽签去重/兑付匹配共用——属性加成型特性=kind,规则改写型特性=feat_id;同档多张不同 relic 可同屏) +func token_identity(tok: Dictionary) -> String: + if String(tok.get("kind", "")) == "relic": + return "relic:" + String(tok.get("feat_id", "")) + return String(tok.get("kind", "")) + + +# 候选令牌匹配(身份键恒等——属性加成型特性 kind+tier、规则改写型特性 feat_id) +func find_token(candidates: Array, kind: String, tier: String, feat_id: String = "") -> Dictionary: + for t in candidates: + var tok: Dictionary[Variant, Variant] = t as Dictionary + if String(tok.get("kind", "")) != kind or String(tok.get("tier", "")) != tier: + continue + if kind == "relic" and String(tok.get("feat_id", "")) != feat_id: + continue + return tok + return {} + + +# 库存余量(stock = 牌库内该 kind 剩余令牌数——「池消耗即上限」,无种类次数上限; +# 奖励三选一兑付摘牌 与 商店购买消费 由同一 pool 承载——两渠道跨关共库,全冒险一次性) +func pool_stock(kind: String) -> int: + var n := 0 + for t in deck: + if String((t as Dictionary).get("kind", "")) == kind: + n += 1 + return n + + +# 终局池快照(宿主清账前调用——复位会重置 taken,摘要 additive 键须在此前取数; +# 读数面 = 商店买走 vs 奖励领走 vs 枯竭) +func stock_snapshot() -> Dictionary: + var out := {} + for row in db.shop_pool_rows(): + var kind := String(row.get("kind_id", "")) + out[kind] = pool_stock(kind) + return out + + +# 从牌库摘除一枚令牌(选中踢池单点;relic 按 feat_id 精确摘;找不到 = false) +func consume_deck_token(kind: String, tier: String, feat_id: String = "") -> bool: + for i in deck.size(): + var tok: Dictionary[Variant, Variant] = deck[i] as Dictionary + if String(tok.get("kind", "")) != kind or String(tok.get("tier", "")) != tier: + continue + if kind == "relic" and String(tok.get("feat_id", "")) != feat_id: + continue + deck.remove_at(i) + return true + return false + + +# 商店购买消费入口(LevelRunner.buy_pool_item 经 Callable 调用):专长凭证买下即开(零摘牌不进持有集); +# 其余件同口回宿主兑付单点(消费在架令牌本体 kind+tier——写加算区+账本+摘牌与奖励三选一真单点)。 +# 未知 kind 拒绝(货架词表封闭的前置防御)。 +func consume_pool_purchase(kind: String, tier: String) -> bool: + if kind == "feat_ticket": + return bool(_redeem_ticket.call()) + if not db.adventure.CURIO_KINDS.has(kind): + push_error("[冒险结构] 商店购买消费拒绝——件不在池词表: %s" % kind) + return false + return bool(_apply_pick.call(kind, tier, int(db.adventure.curio_value(kind, tier)))) + + +# 已拿上限判定(身份维度:kind 走 curio_cap、relic 走 feats 行 cap 字段; +# taken 只在兑付时 +1〔拿了才计〕、刷出不计)。缺省放行=白名单外 kind 兜底。 +func cap_ok(kind: String, feat_id: String = "") -> bool: + var cap := 99 + if kind == "relic": + var row: Dictionary[Variant, Variant] = db.feat_row(feat_id) + cap = int(row.get("cap", 1)) if not row.is_empty() else 1 + return taken.get("relic:" + feat_id, 0) < cap + if not db.adventure.CURIO_KINDS.has(kind): + return true + cap = int(db.adventure.curio_cap(kind)) + return int(taken.get(kind, 0)) < cap + + +# 专长凭证展示判定(四口径:一局限购一次/罕见只在概率不在价格/计步保底/触发清零。 +# 调用即一次展示判定与记账——展示次数计 pity、配额与限购双闸) +func feat_ticket_consider(rg: RandomNumberGenerator) -> bool: + if ticket_sold >= int(db.adventure.feat_ticket_limit): + return false # 限购满——不再触发(保底冻结) + if ticket_appeared >= int(db.adventure.feat_ticket_quota): + return false # 配额满——本局不再出现 + var offer: bool = ticket_pity >= int(db.adventure.feat_ticket_pity_step) or rg.randi_range(1, 100) <= int(db.adventure.feat_ticket_chance) + if offer: + ticket_pity = 0 + ticket_appeared += 1 + else: + ticket_pity += 1 + return offer + + +# 令牌池洗牌不放回抽签(Fisher-Yates 全库洗牌后按序收集;同 kind 去重每 kind 至多一枚; +# kind 出现率∝剩余令牌数〔档位配额即分布——无独立权重 knob〕;抽签不摘牌、兑付才摘牌;池耗即枯竭)。 +# 渠道白名单在候选视图层生效(过滤式而非物理拆池):level_clear=关底 Boss 渠道只出 relic; +# reward_node=非 relic 令牌+cap 剔除,专长只以凭证形态注入末位。消费 reward_pick 封闭流——一次停点恰一 scope。 +func draw_candidates(channel: String = "reward_node") -> Array: + var pool: Array[Variant] = _candidate_pool(channel) + var rg: RandomNumberGenerator = rng.open_stream("reward_pick") + if channel == "level_clear": + return _draw_weighted(pool, rg) + var out := _draw_exposure(pool, rg) + # 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 + + +# 候选池视图(白名单+cap 剔除;牌库本体不动——过滤只在视图层) +func _candidate_pool(channel: String) -> Array[Variant]: + var pool: Array[Variant] = [] + for tok in deck: + var k := String((tok as Dictionary).get("kind", "")) + if channel == "level_clear": + if k == "relic": + pool.append(tok) + continue + if k == "relic" or k == "feat_ticket": + continue + var t: Dictionary[Variant, Variant] = tok as Dictionary + if cap_ok(k, String(t.get("feat_id", ""))): + pool.append(tok) + return pool + + +# Boss 渠道按档配比加权抽取(无放回加权抽:逐席按 boss_feat_weight 掷点,身份去重; +# 配比数值归 adventure 表) +func _draw_weighted(pool: Array[Variant], rg: RandomNumberGenerator) -> Array: + var out: Array[Variant] = [] + var seen: Array[Variant] = [] + var want := int(db.adventure.curio_candidate_count) + while out.size() < want and not pool.is_empty(): + var total := _pool_weight(pool) + if total <= 0: + break + var idx := _weighted_index(pool, rg.randi_range(1, total)) + var tok: Dictionary[Variant, Variant] = pool[idx] + var ident := token_identity(tok) + if seen.has(ident): + pool.remove_at(idx) # 同身份已出——剔牌重掷(无放回) + continue + seen.append(ident) + out.append({"kind": String(tok.get("kind", "")), "tier": String(tok.get("tier", "")), "feat_id": String(tok.get("feat_id", ""))}) + pool.remove_at(idx) + return out + + +func _pool_weight(pool: Array[Variant]) -> int: + var total := 0 + for tok in pool: + total += db.adventure.boss_feat_weight(String((tok as Dictionary).get("tier", ""))) + return total + + +func _weighted_index(pool: Array[Variant], roll: int) -> int: + for i in pool.size(): + roll -= db.adventure.boss_feat_weight(String((pool[i] as Dictionary).get("tier", ""))) + if roll <= 0: + return i + return 0 + + +# 曝光槽抽签(普通档打底 n_full 格 + 优秀档以上子池 1 格;子池空/枯竭回退全池续取—— +# 同 stream 续消费,确定性保持) +func _draw_exposure(pool: Array[Variant], rg: RandomNumberGenerator) -> Array: + _shuffle(pool, rg) + var sub: Array[Variant] = [] + for tok in pool: + if db.adventure.tier_rank(String((tok as Dictionary).get("tier", ""))) >= db.adventure.tier_rank(db.adventure.TIER_EXCELLENT): + sub.append(tok) + _shuffle(sub, rg) + var out: Array[Variant] = [] + var seen: Array[Variant] = [] + var want := int(db.adventure.curio_candidate_count) + var n_full := maxi(1, want - 1) if want >= 2 else want + while out.size() < want: + var src: Array[Variant] = sub if out.size() >= n_full and not sub.is_empty() else pool + if not _collect_one(src, out, seen): + if out.size() >= n_full and not sub.is_empty() and not pool.is_empty(): + sub = [] # 子池枯竭——曝光槽回退全池续取 + continue + break # 全池枯竭——出齐剩余(池空直通同族兜底) + return out + + +# 收集器:按 src 序取首个未出身份(同身份跳过) +func _collect_one(src: Array[Variant], out: Array[Variant], seen: Array[Variant]) -> bool: + for tok in src: + var ident := token_identity(tok) + if seen.has(ident): + continue + seen.append(ident) + var cand := {"kind": String((tok as Dictionary).get("kind", "")), "tier": String((tok as Dictionary).get("tier", ""))} + if ident.begins_with("relic:"): + cand["feat_id"] = String((tok as Dictionary).get("feat_id", "")) + out.append(cand) + return true + return false + + +func _shuffle(list: Array[Variant], rg: RandomNumberGenerator) -> void: + for i in list.size(): + var j := rg.randi_range(i, list.size() - 1) + var tmp = list[i] + list[i] = list[j] + list[j] = tmp diff --git a/game/core/curio_deck.gd.uid b/game/core/curio_deck.gd.uid new file mode 100644 index 00000000..97abd3ef --- /dev/null +++ b/game/core/curio_deck.gd.uid @@ -0,0 +1 @@ +uid://dwgji8w1qv3js diff --git a/game/core/damage_controller.gd b/game/core/damage_controller.gd index 07b370cb..3648ac9e 100644 --- a/game/core/damage_controller.gd +++ b/game/core/damage_controller.gd @@ -166,6 +166,7 @@ func open_ticket(kind: String, source_class: String, display: String, channel: S var matchup_o: String = (attack_type + "_vs_" + armor_b) if attack_type != "" else "" var ac_payload := {"atk": int(base_value), "target": String(tgt.get("display_name", "")) if tgt != null else "", + "target_id": String(tgt.get("enemy_id", "")) if tgt != null else "", "matchup": matchup_o, "dig_streak": dig_streak, "nest_count": _nest_count()} t.context["fx_attack_hits"] = _fx_hits("attack_commit", ac_payload) t.context["fx_attack_payload"] = ac_payload @@ -212,7 +213,7 @@ func _on_unit_died(u: Dictionary) -> void: _halted = true # 主角倒下——flush 余单作废;Game Over 归判定段既有 hp 读数路径 return for t in _queue: - if t.source_unit == u: + if is_same(t.source_unit, u): t["cancelled"] = true # 死亡即不动手——该敌未结单据作废(静默零行零 commit) @@ -367,7 +368,7 @@ func _hero_attack_redeem(t: Dictionary, ctx: Dictionary, base_value: float) -> D var roll_read: Dictionary[Variant, Variant] = ctx["roll_read"] var atk_type: String = ctx["atk_type"] var rolled: int = DamageMath.rolled_component(base_value, int(ctx["discount"]), bool(ctx["crit"]), int(db.settlement.crit_multiplier), int(ctx["cdelta"])) - var payload: Dictionary[Variant, Variant] = {"target": String(target.display_name)} + var payload: Dictionary[Variant, Variant] = {"target": String(target.display_name), "target_id": String(target.get("enemy_id", ""))} payload[atk_type] = rolled # 分量键 = 实际攻击类型(归因字段,单分量模型) payload["roll"] = int(roll_read.roll) payload["ac"] = int(ctx["threshold"]) @@ -397,7 +398,7 @@ func _hero_attack_redeem(t: Dictionary, ctx: Dictionary, base_value: float) -> D # kill 发射+施加(敌人死亡确认;贯穿射击溢出弹射订阅面:对下一存活敌 # 以穿刺类型弹射本体 value%〔无次敌=弹射落空〕。B 模式入队段尾 flush 兑现;A 模式递归 resolve) if bool(payload.get("killed", false)): - for h in _fx_hits("kill", {"target": String(target.display_name), "overflow": landed}): + for h in _fx_hits("kill", {"target": String(target.display_name), "target_id": String(target.get("enemy_id", "")), "overflow": landed}): if String(h.get("kind", "")) == "reveal_tiles" and fx_reveal_request.is_valid(): fx_reveal_request.call() # 斩首学者——击杀揭示 1 格(编排器入队,授予段末兑付) continue @@ -501,7 +502,7 @@ func _resolve_board_typed(t: Dictionary) -> void: var dealt: int = DamageMath.counter_component(int(t.base_value), cdelta, int(db.settlement.damage_floor)) units.apply_damage(target, dealt) emit_row_cb.call("damage", String(t.source_class), t.origin_cell, int(t.round_index), "%s → %s" % [String(t.display), String(target.display_name)], dealt, - {"target": String(target.display_name), "attack_type": String(t.attack_type), "effectiveness": cdelta}, String(t.highlight)) + {"target": String(target.display_name), "target_id": String(target.get("enemy_id", "")), "attack_type": String(t.attack_type), "effectiveness": cdelta}, String(t.highlight)) _outcomes[int(t.ticket_id)] = "redeemed" diff --git a/game/core/level_runner.gd b/game/core/level_runner.gd index 35dfc7b5..8d88a30c 100644 --- a/game/core/level_runner.gd +++ b/game/core/level_runner.gd @@ -1,5 +1,5 @@ ## VERTICAL SLICE - NOT FOR PRODUCTION -## 关卡域生命周期编排者:节点图行进、HP 跨战斗携带、商店房与恢复点兑付、关卡末收束(垂直切片验证:一关内数值提升可感知)。 +## 关卡域生命周期编排者:节点图行进、HP 跨战斗携带、恢复点兑付、关卡末收束(垂直切片验证:一关内数值提升可感知);商店族归 ShopSession 件。 ## Core 层零 UI 依赖(headless 可跑完整关卡);零自动推进——兑付确认后经 choices_presented,推进只走 pick_next_node。 ## 种子域:单关模式 start_level 自播种;冒险模式 start_level_external 不重播种(主种子恰一次归 AdventureRunner)。 ## hero_atk_pct/hero_hp_pct 为测试专用覆盖——经 encounter 显式传入战斗,零表写,db.hero 运行期只读。 @@ -7,13 +7,13 @@ class_name LevelRunner extends RefCounted -signal node_entered(node: Dictionary, info: Dictionary) # info 按 type:battle/boss={} · reward={}(内容待定) · rest={healed,hp,max_hp} · shop={gp} -signal choices_presented(candidates: Array) # 选路口(兑付确认后——候选节点引用数组,零自动推进) -signal step_row(row: Dictionary) # 转发当前 战斗结算 结算行 +signal node_entered(node: Dictionary, info: Dictionary) ## info 按 type:battle/boss={} · reward={}(内容待定) · rest={healed,hp,max_hp} · shop={gp} +signal choices_presented(candidates: Array) ## 选路口(兑付确认后——候选节点引用数组,零自动推进) +signal step_row(row: Dictionary) ## 转发当前 战斗结算 结算行 signal round_started(round_index: int) signal board_changed() -signal level_finished(payload: Dictionary) # {result, hp, node_index, node_id, node_type} -signal wallet_changed(event: String, gp: int, extra: Dictionary) # GP 钱包事件投影:gain{amount}/buy{product,price}/clear——试玩日志与商店覆盖层刷新源(GP+商店房实施批) +signal level_finished(payload: Dictionary) ## {result, hp, node_index, node_id, node_type} +signal wallet_changed(event: String, gp: int, extra: Dictionary) ## GP 钱包事件投影:gain{amount}/buy{product,price}/clear——试玩日志与商店覆盖层刷新源(GP+商店房实施批) var db: ConfigDb var rng: RngServiceCore @@ -34,7 +34,7 @@ var gp: int: # GP 冒险钱包(局内货币— get: return int(_ledger["gp"]) set(v): _ledger["gp"] = v var base_override := {"atk": 0, "max_hp": 0, "armor_class": 0} # 永久强化 flat 注入(元进度实现批——冒险模式 = AdventureRunner 共享字典经 start_level_external 注入;单关测试模式 = 私有全零) -var _shop_purchased := {} # product_id → 已购档数(现价重算输入;冒险开始清零) +var _shop: ShopSession = null # 商店件(_init 建;双模式货架/定价/购买/刷新唯一宿主) # 协作对象私有(E1/E3 封装收口):settlement 读取经 current_battle() 成文出口 var _settlement: BattleOrchestrator = null # 当前战斗编排器(关卡结束置 null) var treasure_inventory: TreasureItems = null # 秘宝 关卡域库存(TreasureItems;跨战斗延续, @@ -46,14 +46,24 @@ var _attunement: Array[Variant] = [] # 同调装配集(adve var _hero_attack_type := "" var _hero_armor_type := "" # ---- 商店共享池模式态(缺省 legacy=固定三件加定价 off 对照承载,零 RNG 零池消费;TP0 令牌池)---- -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_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 链定价输入——离开清零) -var _shop_paid_refreshes := 0 # 本次进店已付费刷新次数(MC-1——GP 刷新价曲线输入,离开清零) +# 件内状态白盒读面(断点写侧与既有取数面沿用原字段名;状态本体单点在 ShopSession) +var _shop_pool_mode: bool: + get: return _shop.pool_mode +var _pool_deck: Array[Variant]: + get: return _shop.pool_deck +var _pool_consume: Callable: + get: return _shop.pool_consume +var _pool_ticket: Callable: + get: return _shop.pool_ticket +var _shop_shelf: Array[Variant]: + get: return _shop.shelf +var _shop_visit_buys: int: + get: return _shop.visit_buys +var _shop_paid_refreshes: int: + get: return _shop.paid_refreshes +var _shop_purchased: Dictionary[Variant, Variant]: # product_id → 已购档数(现价重算输入;冒险开始清零) + get: return _shop.purchased + set(v): _shop.purchased = v var hero_atk_pct := 0 # 测试面板覆盖值(切片专用) var hero_hp_pct := 0 @@ -70,6 +80,8 @@ var _generator # 参数化长图生成器(惰性建——NodeMapGenerator func _init(db_ref: ConfigDb, rng_ref: RngServiceCore) -> void: db = db_ref rng = rng_ref + _shop = ShopSession.new(db, rng) + _bind_shop() # 进行中谓词(RUNNING 或 AWAITING_CHOICE 两相位合计;内部守卫专用) @@ -150,6 +162,7 @@ func start_level(level_index: int, seed_val: int, base_override_in: Dictionary = run_ledger.bonus = {"atk": 0, "max_hp": 0, "armor_class": 0} # 加算区冒险开始全 0(单关入口零元进度——注入面见上) base_override = {"atk": 0, "max_hp": 0, "armor_class": 0} # 永久强化 flat:缺省全零白板(单关入口零元进度——注入面见上) _shop_purchased = {} # 已购档数清零(跨冒险不延续——递增定价按冒险内累计) + _bind_shop() # 钱包引用换代重绑(_ledger 已重建) for k in base_override.keys(): base_override[k] = maxi(0, int(base_override_in.get(k, 0))) # 注入规范化(契约同冒险开始参数面) hero_hp = effective_max_hp() @@ -189,6 +202,7 @@ func start_level_external(level_index: int, ledger: Dictionary, _ledger = ledger base_override = base_override_ref _shop_purchased = purchased_ref + _bind_shop() # 钱包共享引用换代重绑(_ledger = 冒险账本) _rest_handler = rest_handler if not _assign_generated_graph(row, rng.master_seed): return false @@ -472,7 +486,7 @@ func _enter_node(node: Dictionary) -> bool: # 商店房兑付:到达即开店(覆盖层归呈现层),购买出账/ # 离开归玩家意图——零自动推进;离开确认经 continue_after_node 回选路口 if _shop_pool_mode: - _shop_open_pool() # 进店抽 3(恰一 shop_draw scope;买后全重抽同签) + _shop.open_pool() # 进店抽 3(恰一 shop_draw scope;买后全重抽同签) node_entered.emit(node, {"gp": gp, "shelf": true}) return true node_entered.emit(node, {"gp": gp}) # legacy 固定三件(off 对照承载——零 RNG 零池消费) @@ -629,6 +643,7 @@ func set_treasure_unlocked(ids: Dictionary) -> void: # 同调装配集注入(冒险开始参数面;AdventureRunner 唯一调用方) func set_attunement(ids: Array) -> void: _attunement = ids.duplicate() + _bind_shop() # 装配集共享引用换代重绑(刷新执照读数面) # 特性持有集注入(AdventureRunner._start_current_level 唯一调用方;begin_battle @@ -652,6 +667,7 @@ var _tree_rules: Array[Variant] = [] func set_tree_rules(rules: Array) -> void: _tree_rules = rules.duplicate() + _bind_shop() # rule 键集共享引用换代重绑(散折价判定面) # 两根类型轴注入(adventure 参数面快照;空串=未注入→锻造配置默认值回退。 @@ -669,6 +685,7 @@ var run_ledger: RunLedger = RunLedger.new() func set_run_ledger(l: RunLedger) -> void: run_ledger = l + _bind_shop() # 账类换代重绑(加算区 flat 落点面) # 凭证件三账注入(储备金袋 RAP/时沙 首轮 AP/完美保险 标错豁免;节点级一次性, @@ -713,9 +730,7 @@ func continue_after_node() -> bool: if t != "reward" and t != "rest" and t != "shop": return false if t == "shop": - _shop_shelf = [] # 离开店面货架态清零(断点快照 shop_shelf 键随离开落空架合法态) - _shop_visit_buys = 0 - _shop_paid_refreshes = 0 # MC-1 + _shop.leave() # 离开店面货架态清零(断点快照 shop_shelf 键随离开落空架合法态) return _present_choices() @@ -735,265 +750,108 @@ func close_wallet() -> void: # ---- 商店房兑付(GP+商店房实施批:呈现层只读投影 + 购买出账)---- +# 实装归 ShopSession 件(双模式货架/定价/购买/刷新唯一宿主);本类保留同签名委托与件注入/ +# 白盒入口——外部调用面与断点取数面零变化。 + +# 件注入(建件与宿主引用换代时调用——钱包/账类/图行/装配集为共享引用,出账直接落同一引用) +func _bind_shop() -> void: + _shop.bind({ + "wallet": _ledger, + "run_ledger": run_ledger, + "level_row": level_row, + "tree_rules": _tree_rules, + "attunement": _attunement, + }, { + "wallet_emit": _emit_wallet_event, + "max_hp_gain": _gain_max_hp_flat, + "battle": current_battle, + }) + + +func _emit_wallet_event(event: String, gp_now: int, extra: Dictionary) -> void: + wallet_changed.emit(event, gp_now, extra) + + +# 购血(商店 max_hp 商品——当前生命 +Δ 并封顶新上限,与账本置满/通关回血同式) +func _gain_max_hp_flat(flat: int) -> void: + hero_hp = mini(hero_hp + flat, effective_max_hp()) + -# 商店覆盖层数据源(只读投影——现价/已购档数在此现算,呈现层禁复算定价公式) func shop_products() -> Array: - var out: Array[Dictionary] = [] - for prow in db.shop_products(): - out.append({ - "product_id": String(prow.get("product_id", "")), - "display_name": String(prow.get("display_name", "")), - "attr": String(prow.get("attr", "")), - "flat_value": int(prow.get("flat_value", 0)), - "price": current_price(prow), - "purchased": purchased_tier(String(prow.get("product_id", ""))), - }) - return out + return _shop.shop_products() -# 现价 = base_price + price_level_bonus(level_index) + price_increment × 该商品已购档数 -# (散折价:economy_spend 激活时商品标价 ×80%——见 _spend_discounted) func current_price(prow: Dictionary) -> int: - return _spend_discounted(int(prow.get("base_price", 0)) + db.shop_price_level_bonus(int(level_row.level_index)) \ - + int(prow.get("price_increment", 0)) * purchased_tier(String(prow.get("product_id", "")))) - - -# 散折价单点(财富之道·散 economy_spend 激活时商品购买价 ×80% provisional, -# floor 取整偏向玩家、下限 1 防 1 元商品折成 0;范围=商品标价定义边界〔legacy 商品面 current_price -# + 池货架面 pool_price 同折〕——商店刷新价 shop_refresh_cost 与三选一付费刷新不折) -func _spend_discounted(price: int) -> int: - if _tree_rules.has("economy_spend"): - return maxi(1, int(floor(float(price) * 0.8))) - return price + return _shop.current_price(prow) func purchased_tier(product_id: String) -> int: - return int(_shop_purchased.get(product_id, 0)) + return _shop.purchased_tier(product_id) -# 购买(未知商品/属性键出域/余额不足拒绝——返回 false 零副作用): -# 出账 → 加算区 += flat_value → 购血同步当前生命(封顶新上限)→ 已购档数 +1 func buy_product(product_id: String) -> bool: - var prow: Dictionary[Variant, Variant] = db.shop_product_row(product_id) - if prow.is_empty(): - push_error("[关卡节点地图] 商店房未知商品: %s" % product_id) - return false - var attr := String(prow.get("attr", "")) - if not run_ledger.bonus.has(attr): - push_error("[关卡节点地图] 商品属性键出域: %s(attr=%s)" % [product_id, attr]) - return false - var price := current_price(prow) - if gp < price: - push_warning("[关卡节点地图] 购买拒绝——余额不足: %s(需 %d,钱包 %d)" % [product_id, price, gp]) - return false # 余额不足(覆盖层按钮禁用为常态防线——此处兜底拒绝+静默留痕) - gp -= price - var flat := int(prow.get("flat_value", 0)) - run_ledger.bonus[attr] = int(run_ledger.bonus[attr]) + flat - if attr == "max_hp": - hero_hp = mini(hero_hp + flat, effective_max_hp()) # 购血:上限与当前生命同步 +Δ(封顶新上限) - _shop_purchased[product_id] = purchased_tier(product_id) + 1 - wallet_changed.emit("buy", gp, {"product": product_id, "price": price}) - return true + return _shop.buy_product(product_id) func player_request_refresh() -> bool: - if _settlement == null: - return false - return _settlement.player_request_refresh() + return _shop.player_request_refresh() -# ---- 商店共享池模式(legacy 固定三件路径保留为 off 对照承载)---- +# 散折价单点(测试取数面沿用本类调用名) +func _spend_discounted(price: int) -> int: + return _shop._spend_discounted(price) + # 注入(AdventureRunner._start_current_level 唯一调用方——pool 模式专用):令牌库共享引用 + -# 池消费 handler。legacy 模式不注入(_shop_pool_mode 恒 false——off 态零 RNG 零池消费零扰动)。 -# cap 检查回调(cap 满 kind 剔出货架候选视图)。 +# 池消费 handler + 专长凭证判定 + cap 检查回调(cap 满 kind 剔出货架候选视图)。 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_cap_check = cap_check - _shop_pool_mode = true + _shop.set_shop_pool(deck_ref, consume_handler, ticket_consider, cap_check) -# 进店开店(_enter_node shop 分支 pool 模式):进店购买次计数清零 + 抽 3 落架(恰一 shop_draw scope) -func _shop_open_pool() -> void: - _shop_visit_buys = 0 - _shop_paid_refreshes = 0 # MC-1——进店付费刷新计数清零 - _shop_draw_shelf() - - -# 货架抽签(进店 + 每次买后全重抽共用;TP0 令牌级货架):牌库 = 令牌库中货架词表内 kind 的剩余 -# 令牌(库存=剩余令牌数——池消耗即上限,与奖励三选一兑付摘牌同库承载);部分 Fisher-Yates 等概率 -# 洗牌不放回(逐枚等概率——kind 出现率∝剩余令牌数,无独立权重 knob;同 kind 去重每 kind 至多一卡, -# 购买身份 kind 单键无歧义);消费 shop_draw 封闭流恰一 scope(每次抽 3 恰一取号)。牌库空 = 空货架 -# 合法态(池空硬闸——买光即枯竭既定后果)。 +# 货架抽签白盒入口(测试取数面沿用本类调用名) func _shop_draw_shelf() -> void: - _shop_shelf = [] - var sellable: Array[Variant] = [] - for row in db.shop_pool_rows(): - var shelf_kind := String(row.get("kind_id", "")) - if _pool_cap_check.is_valid() and not bool(_pool_cap_check.call(shelf_kind, "")): - continue # cap 满剔除(货架候选视图剔除;牌库本体不动) - for t in _pool_deck: - var tok: Dictionary[Variant, Variant] = t as Dictionary - if String(tok.get("kind", "")) == shelf_kind: - sellable.append({"kind": shelf_kind, "tier": String(tok.get("tier", ""))}) - if sellable.is_empty(): - return - var stream: RandomNumberGenerator = rng.open_stream("shop_draw") - for i in sellable.size(): - var j := stream.randi_range(i, sellable.size() - 1) - var tmp = sellable[i] - sellable[i] = sellable[j] - sellable[j] = tmp - var seen: Array[Variant] = [] - for tok in sellable: - var k := String((tok as Dictionary).get("kind", "")) - if seen.has(k): - continue - seen.append(k) - _shop_shelf.append(tok) - if _shop_shelf.size() >= 3: - break - # 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 - # 商店渠道无专长直接投放(渠道白名单:商店渠道只出属性值提升+资源,专长在非 Boss - # 渠道只以专长凭证形态出现——奇物.md v2.7/A4 断言;专长商店入口=T5 专长凭证)。 + _shop._draw_shelf() -# 库存余量(TP0——令牌库内该 kind 剩余枚数单点读;未注入/防御读 = 0) func pool_stock(kind: String) -> int: - var n := 0 - for t in _pool_deck: - if String((t as Dictionary).get("kind", "")) == kind: - n += 1 - return n + return _shop.pool_stock(kind) -# 现价 = ceil(基价 × b^(关序−1) × A^(本次进店已购次数))(P4 甲 ceil 向上单一取整——玩家不利向; -# A/b 表 knob provisional;核心现算,呈现层禁复算——契约 5)。relic 卡(保底占位——shop 词表外) -# 基价 provisional 15 固定锚(台账披露待追认——shop.tres 词表扩列归数值批)。 func pool_price(kind: String) -> int: - var row: Dictionary[Variant, Variant] = db.shop_pool_row(kind) - if row.is_empty(): - if kind == "relic": - return _spend_discounted(int(ceil(15.0 * pow(db.shop_price_b(), float(maxi(0, int(level_row.level_index) - 1))) * pow(db.shop_price_a(), float(_shop_visit_buys))))) - return 999999 - var base := float(maxi(1, int(row.get("base_price", 1)))) - var price_f: float = base * pow(db.shop_price_b(), float(maxi(0, int(level_row.level_index) - 1))) \ - * pow(db.shop_price_a(), float(_shop_visit_buys)) - return _spend_discounted(int(ceil(price_f))) + return _shop.pool_price(kind) -# 货架只读投影(呈现层数据源——kind/显示名/件值/现价/库存余量齐备,UI 零复算)。 -# 件值=在架令牌本体值(TP0——令牌级货架,kind+tier 定值;价格公式 tier-blind〔不随档位变〕)。 func shop_shelf() -> Array: - var out: Array[Dictionary] = [] - for tok in _shop_shelf: - var kind := String((tok as Dictionary).get("kind", "")) - var tier := String((tok as Dictionary).get("tier", "")) - var row: Dictionary[Variant, Variant] = db.shop_pool_row(kind) - var display := String(row.get("display_name", kind)) - var value := int(db.adventure.curio_value(kind, tier)) - if row.is_empty() and kind == "relic": - var rrow: Dictionary[Variant, Variant] = db.feat_row(String(tok.get("feat_id", ""))) - display = String(rrow.get("display_name", "特性")) - value = 0 - out.append({ - "kind_id": kind, - "tier": tier, - "feat_id": String(tok.get("feat_id", "")), - "display_name": display, - "value": value, - "price": pool_price(kind), - "stock_left": pool_stock(kind), - }) - return out + return _shop.shop_shelf() -# 商店独立刷新(MC-1):免费次数(天赋增益位,基础 -# knob=0=「基础免费 0 次」)用尽后 GP 付费刷新;刷新=货架整架重抽(shop_draw 恰一新 scope, -# 与进店/买后重抽同签——重抽同源共享池);池空/非池模式=不可刷新合法态。 func shop_refresh_free_left() -> int: - # 刷新执照 shop_refresh_bonus 装配合计叠加(进店入账语义=读数即本店可用, - # 与基础 knob 同生命周期;未装配=基值) - return maxi(0, int(db.adventure.shop_refresh_free) + _attune_shop_refresh_bonus()) - - -# 刷新执照装配合计(装配集 kind 分发扫描;零装配=0) -func _attune_shop_refresh_bonus() -> int: - var total := 0 - for iid_v in _attunement: - var arow: Dictionary[Variant, Variant] = db.treasure_item_row(String(iid_v)) - if (arow.get("effect_kinds", []) as Array).has("shop_refresh_bonus"): - total += int(arow.get("magnitude", 0)) - return total + return _shop.shop_refresh_free_left() func shop_refresh_cost() -> int: - return int(ceil(float(db.adventure.shop_refresh_cost_base) - * pow(db.adventure.shop_refresh_cost_curve, float(_shop_paid_refreshes)))) + return _shop.shop_refresh_cost() func shop_refresh() -> bool: - if not _shop_pool_mode or _shop_shelf.is_empty(): - return false - if shop_refresh_free_left() <= 0: - var cost := shop_refresh_cost() - if gp < cost: - return false - gp -= cost - _shop_paid_refreshes += 1 - _shop_draw_shelf() - return true + return _shop.shop_refresh() -# GP 冒险钱包单口支出(三选一 GP 付费刷新与商店刷新共用同一钱包;余额不足 false 零副作用) func spend_gp(amount: int) -> bool: - if amount <= 0 or gp < amount: - return false - gp -= amount - return true + return _shop.spend_gp(amount) -# 购买(pool 模式唯一购买口):在架(kind→在架令牌)/余额前置校验零副作用 → handler 消费在架 -# 令牌本体(kind+tier——_apply_curio_pick 同口写加算区+账本+摘牌,跨渠道共库)→ 出账 → 进店购买 -# 次计数 → wallet 事件 → 三件全重抽(未买不保留——重抽绑购买,无独立刷新动作) func buy_pool_item(kind: String) -> bool: - var shelf_tok := {} - for tok in _shop_shelf: - if String((tok as Dictionary).get("kind", "")) == kind: - shelf_tok = tok - break - if not _shop_pool_mode or shelf_tok.is_empty(): - push_error("[关卡节点地图] 商店购买拒绝——非池模式或件不在架: %s" % kind) - return false - var price := pool_price(kind) - if gp < price: - push_warning("[关卡节点地图] 购买拒绝——余额不足: %s(需 %d,钱包 %d)" % [kind, price, gp]) - return false - var tier := String(shelf_tok.get("tier", "")) - if _pool_consume.is_null() or not bool(_pool_consume.call(kind, tier)): - push_error("[关卡节点地图] 购买拒绝——池库存消费失败: %s|%s" % [kind, tier]) - return false - gp -= price - _shop_visit_buys += 1 - wallet_changed.emit("buy", gp, {"product": kind, "price": price}) - _shop_draw_shelf() # 买 1 → 三件全重抽(恰一 shop_draw scope——同进店取号签) - return true + return _shop.buy_pool_item(kind) -# 池模式谓词(呈现层组合根分发用——货架面 vs legacy 商品面) func is_shop_pool() -> bool: - return _shop_pool_mode + return _shop.is_shop_pool() -# 断点快照读面(P2 甲货架+账本入断点——AdventureRunner._snapshot_breakpoint 消费;离开店即空架) func shop_bp_shelf() -> Array: - return _shop_shelf.duplicate() + return _shop.shop_bp_shelf() func shop_bp_visit_buys() -> int: - return _shop_visit_buys + return _shop.shop_bp_visit_buys() diff --git a/game/core/minesweeper_rules.gd b/game/core/minesweeper_rules.gd index 2db833ac..211e77a9 100644 --- a/game/core/minesweeper_rules.gd +++ b/game/core/minesweeper_rules.gd @@ -5,9 +5,23 @@ class_name MinesweeperRules extends RefCounted +## 揭示事实流:每格首次进入已开集恰发一次(已开格零事件)。 +## 载荷键集(四处发射位并集):cell: Vector2i/kind: "safe"|"mine"/digit: int(安全格邻雷数;危险格 -1)/ +## variant_ids: Array(地形变异身份,危险格空集)/channel: "initial"|"dig"|"chain"|"fx_reveal"/ +## resource: bool(格面资源兑付资格:initial=false,其余 true)/dug_under_seal: bool(首格带封印直挖标记—— +## initial 通道不发此键)。 +## 发送时点:initial=install_board 初始展开逐格;dig/chain=_do_dig 揭示闭包内(本格 AP 扣费在其后); +## fx_reveal=fx_reveal 闭包内(零 AP 消耗零相位要求)。 +## 消费方依赖:战斗结算 _on_cell_revealed 以 resource 分流缓冲(fx_reveal 由兑付点直入账,禁二次缓冲)、 +## 以 kind 驱动积点计数、以 cell 取奖励快照——禁改键名与语义;事件序 cell_revealed 批先于 seal_judged 批。 signal cell_revealed(evt: Dictionary) +## 封印判定事实流:轮末事务开始时的封印位逐格判定(行主序确定性枚举,被拔的不判)。 +## 载荷键集:cell: Vector2i(封印格)/hit: bool(该格是否为雷)。 +## 发送时点:停手路径 _do_stop 委托行动经济之前;棋盘路径揭示闭包收尾(phase 非 Active 时)—— +## 全路径统一先于 round_end 发射(事件序契约 seal_judged < round_end)。 +## 消费方依赖:战斗结算 _on_seal_judged 收批后统一开封印命中单——载荷缺键即错判,禁改键名与语义。 signal seal_judged(evt: Dictionary) -signal round_end_board(payload: Dictionary) # 双发射源之棋盘路径(排雷玩法 外发;stopped 由 行动经济 直发) +signal round_end_board(payload: Dictionary) ## 双发射源之棋盘路径(排雷玩法 外发;stopped 由 行动经济 直发) var db: ConfigDb var action_economy: ActionEconomy diff --git a/game/core/shop_session.gd b/game/core/shop_session.gd new file mode 100644 index 00000000..f44ccbc5 --- /dev/null +++ b/game/core/shop_session.gd @@ -0,0 +1,339 @@ +## VERTICAL SLICE - NOT FOR PRODUCTION +## 商店会话件:关卡商店双模式(legacy 固定三件加定价/pool 共享令牌池)的货架、定价、购买与刷新唯一宿主。 +## 账本/钱包/装配集归宿主 LevelRunner(共享引用经 bind 注入,出账直接落同一引用改写宿主账本); +## Core 层零 UI 依赖;公开面由 LevelRunner 同签名委托承载(外部调用面零变化); +## legacy 模式零 RNG 零池消费(off 对照承载)。 +class_name ShopSession +extends RefCounted + +var db: ConfigDb +var rng: RngServiceCore +var _refs: Dictionary[Variant, Variant] = {} # wallet/run_ledger/level_row/tree_rules/attunement 宿主共享引用绑定 +var _hooks: Dictionary[Variant, Variant] = {} # wallet_emit/max_hp_gain/battle 宿主回调绑定 +var purchased: Dictionary[Variant, Variant] = {} # product_id → 已购档数(现价重算输入;冒险开始清零) +var pool_mode := false # pool 模式封印(set_shop_pool 注入时置位) +var pool_deck: Array[Variant] = [] # 共享池令牌库共享引用(库存=剩余令牌数单点) +var pool_consume := Callable() # 池消费 handler(跨渠道共库兑付) +var pool_ticket := Callable() # 专长凭证展示判定 handler(节点与商店共享计数) +var _pool_cap := Callable() # cap 检查回调(cap 满剔出货架候选视图) +var shelf: Array[Variant] = [] # 当前货架(进店抽 3/买后全重抽——令牌字典 {"kind","tier"} 序) +var visit_buys := 0 # 本次进店已购次数(A 链定价输入——离开清零) +var paid_refreshes := 0 # 本次进店已付费刷新次数(MC-1——GP 刷新价曲线输入,离开清零) + + +func _init(db_ref: ConfigDb, rng_ref: RngServiceCore) -> void: + db = db_ref + rng = rng_ref + + +# 宿主引用与回调注入(LevelRunner 建件与引用换代时重绑——钱包/账类/图行/装配集为共享引用) +func bind(refs: Dictionary, hooks: Dictionary) -> void: + _refs = refs + _hooks = hooks + + +# 商店覆盖层数据源(只读投影——现价/已购档数在此现算,呈现层禁复算定价公式) +func shop_products() -> Array: + var out: Array[Dictionary] = [] + for prow in db.shop_products(): + out.append({ + "product_id": String(prow.get("product_id", "")), + "display_name": String(prow.get("display_name", "")), + "attr": String(prow.get("attr", "")), + "flat_value": int(prow.get("flat_value", 0)), + "price": current_price(prow), + "purchased": purchased_tier(String(prow.get("product_id", ""))), + }) + return out + + +# 现价 = base_price + price_level_bonus(level_index) + price_increment × 该商品已购档数 +# (散折价:economy_spend 激活时商品标价 ×80%——见 _spend_discounted) +func current_price(prow: Dictionary) -> int: + return _spend_discounted(int(prow.get("base_price", 0)) + db.shop_price_level_bonus(_level_index()) \ + + int(prow.get("price_increment", 0)) * purchased_tier(String(prow.get("product_id", "")))) + + +# 散折价单点(财富之道·散 economy_spend 激活时商品购买价 ×spend_discount〔表 knob,缺省 0.8〕, +# floor 取整偏向玩家、下限 1 防 1 元商品折成 0;范围=商品标价定义边界〔legacy 商品面 current_price +# + 池货架面 pool_price 同折〕——商店刷新价 shop_refresh_cost 与三选一付费刷新不折) +func _spend_discounted(price: int) -> int: + if _rules().has("economy_spend"): + return maxi(1, int(floor(float(price) * float(db.shop.spend_discount)))) + return price + + +func purchased_tier(product_id: String) -> int: + return int(purchased.get(product_id, 0)) + + +# 购买(未知商品/属性键出域/余额不足拒绝——返回 false 零副作用): +# 出账 → 加算区 += flat_value → 购血同步当前生命(封顶新上限)→ 已购档数 +1 +func buy_product(product_id: String) -> bool: + var prow: Dictionary[Variant, Variant] = db.shop_product_row(product_id) + if prow.is_empty(): + push_error("[关卡节点地图] 商店房未知商品: %s" % product_id) + return false + var attr := String(prow.get("attr", "")) + if not _bonus().has(attr): + push_error("[关卡节点地图] 商品属性键出域: %s(attr=%s)" % [product_id, attr]) + return false + var price := current_price(prow) + if _gp() < price: + push_warning("[关卡节点地图] 购买拒绝——余额不足: %s(需 %d,钱包 %d)" % [product_id, price, _gp()]) + return false # 余额不足(覆盖层按钮禁用为常态防线——此处兜底拒绝+静默留痕) + _set_gp(_gp() - price) + var flat := int(prow.get("flat_value", 0)) + var bonus := _bonus() + bonus[attr] = int(bonus[attr]) + flat + if attr == "max_hp": + (_hooks["max_hp_gain"] as Callable).call(flat) # 购血:上限与当前生命同步 +Δ(封顶新上限) + purchased[product_id] = purchased_tier(product_id) + 1 + _emit("buy", {"product": product_id, "price": price}) + return true + + +func player_request_refresh() -> bool: + var battle = (_hooks["battle"] as Callable).call() + if battle == null: + return false + return battle.player_request_refresh() + + +# ---- 商店共享池模式(legacy 固定三件路径保留为 off 对照承载)---- + +# 注入(AdventureRunner._start_current_level 唯一调用方——pool 模式专用):令牌库共享引用 + +# 池消费 handler。legacy 模式不注入(pool_mode 恒 false——off 态零 RNG 零池消费零扰动)。 +# cap 检查回调(cap 满 kind 剔出货架候选视图)。 +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——专长凭证展示判定(同流取数复用 shop_draw scope) + _pool_cap = cap_check + pool_mode = true + + +# 进店开店(_enter_node shop 分支 pool 模式):进店购买次计数清零 + 抽 3 落架(恰一 shop_draw scope) +func open_pool() -> void: + visit_buys = 0 + paid_refreshes = 0 # MC-1——进店付费刷新计数清零 + _draw_shelf() + + +# 离店清零(continue_after_node shop 分支——断点快照 shop_shelf 键随离开落空架合法态) +func leave() -> void: + shelf = [] + visit_buys = 0 + paid_refreshes = 0 # MC-1 + + +# 货架抽签(进店 + 每次买后全重抽共用;TP0 令牌级货架):牌库 = 令牌库中货架词表内 kind 的剩余 +# 令牌(库存=剩余令牌数——池消耗即上限,与奖励三选一兑付摘牌同库承载);部分 Fisher-Yates 等概率 +# 洗牌不放回(逐枚等概率——kind 出现率∝剩余令牌数,无独立权重 knob;同 kind 去重每 kind 至多一卡, +# 购买身份 kind 单键无歧义);消费 shop_draw 封闭流恰一 scope(每次抽 3 恰一取号)。牌库空 = 空货架 +# 合法态(池空硬闸——买光即枯竭既定后果)。 +func _draw_shelf() -> void: + shelf = [] + var sellable: Array[Variant] = [] + for row in db.shop_pool_rows(): + var shelf_kind := String(row.get("kind_id", "")) + if _pool_cap.is_valid() and not bool(_pool_cap.call(shelf_kind, "")): + continue # cap 满剔除(货架候选视图剔除;牌库本体不动) + for t in pool_deck: + var tok: Dictionary[Variant, Variant] = t as Dictionary + if String(tok.get("kind", "")) == shelf_kind: + sellable.append({"kind": shelf_kind, "tier": String(tok.get("tier", ""))}) + if sellable.is_empty(): + return + var stream: RandomNumberGenerator = rng.open_stream("shop_draw") + _shuffle(sellable, stream) + var seen: Array[Variant] = [] + for tok in sellable: + var k := String((tok as Dictionary).get("kind", "")) + if seen.has(k): + continue + seen.append(k) + shelf.append(tok) + if shelf.size() >= 3: + break + # T5 专长凭证注入(非 Boss 渠道唯一专长入口——货架与节点共享保底计数;判定含 pity 记账,替换末位占一席。 + # 取数复用本函数 shop_draw scope 的 stream——零新 scope,确定性序列不漂移) + if pool_ticket.is_valid() and not shelf.is_empty() and bool(pool_ticket.call(stream)): + shelf[shelf.size() - 1] = {"kind": "feat_ticket", "tier": "excellent"} # 专长凭证占位档名 = excellent + # 商店渠道无专长直接投放(渠道白名单:商店渠道只出属性值提升+资源,专长在非 Boss + # 渠道只以专长凭证形态出现——奇物.md v2.7/A4 断言;专长商店入口=T5 专长凭证)。 + + +# 部分 Fisher-Yates 洗牌(牌库抽签序单点——逐枚等概率、同序列取号次数不变) +func _shuffle(list: Array[Variant], stream: RandomNumberGenerator) -> void: + for i in list.size(): + var j := stream.randi_range(i, list.size() - 1) + var tmp = list[i] + list[i] = list[j] + list[j] = tmp + + +# 库存余量(TP0——令牌库内该 kind 剩余枚数单点读;未注入/防御读 = 0) +func pool_stock(kind: String) -> int: + var n := 0 + for t in pool_deck: + if String((t as Dictionary).get("kind", "")) == kind: + n += 1 + return n + + +# 现价 = ceil(基价 × b^(关序−1) × A^(本次进店已购次数))(P4 甲 ceil 向上单一取整——玩家不利向; +# A/b 表 knob provisional;核心现算,呈现层禁复算——契约 5)。relic 卡(保底占位——shop 词表外) +# 基价 provisional 15(表 knob relic_base_price——shop.tres 词表扩列归数值批)。 +func pool_price(kind: String) -> int: + var row: Dictionary[Variant, Variant] = db.shop_pool_row(kind) + if row.is_empty(): + if kind == "relic": + return _spend_discounted(int(ceil(float(db.shop.relic_base_price) * pow(db.shop_price_b(), float(maxi(0, _level_index() - 1))) * pow(db.shop_price_a(), float(visit_buys))))) + return 999999 + var base := float(maxi(1, int(row.get("base_price", 1)))) + var price_f: float = base * pow(db.shop_price_b(), float(maxi(0, _level_index() - 1))) \ + * pow(db.shop_price_a(), float(visit_buys)) + return _spend_discounted(int(ceil(price_f))) + + +# 货架只读投影(呈现层数据源——kind/显示名/件值/现价/库存余量齐备,UI 零复算)。 +# 件值=在架令牌本体值(TP0——令牌级货架,kind+tier 定值;价格公式 tier-blind〔不随档位变〕)。 +func shop_shelf() -> Array: + var out: Array[Dictionary] = [] + for tok in shelf: + var kind := String((tok as Dictionary).get("kind", "")) + var tier := String((tok as Dictionary).get("tier", "")) + var row: Dictionary[Variant, Variant] = db.shop_pool_row(kind) + var display := String(row.get("display_name", kind)) + var value := int(db.adventure.curio_value(kind, tier)) + if row.is_empty() and kind == "relic": + var rrow: Dictionary[Variant, Variant] = db.feat_row(String(tok.get("feat_id", ""))) + display = String(rrow.get("display_name", "特性")) + value = 0 + out.append({ + "kind_id": kind, + "tier": tier, + "feat_id": String(tok.get("feat_id", "")), + "display_name": display, + "value": value, + "price": pool_price(kind), + "stock_left": pool_stock(kind), + }) + return out + + +# 商店独立刷新(MC-1):免费次数(天赋增益位,基础 +# knob=0=「基础免费 0 次」)用尽后 GP 付费刷新;刷新=货架整架重抽(shop_draw 恰一新 scope, +# 与进店/买后重抽同签——重抽同源共享池);池空/非池模式=不可刷新合法态。 +func shop_refresh_free_left() -> int: + # 刷新执照 shop_refresh_bonus 装配合计叠加(进店入账语义=读数即本店可用, + # 与基础 knob 同生命周期;未装配=基值) + return maxi(0, int(db.adventure.shop_refresh_free) + _attune_shop_refresh_bonus()) + + +# 刷新执照装配合计(装配集 kind 分发扫描;零装配=0) +func _attune_shop_refresh_bonus() -> int: + var total := 0 + for iid_v in _attunement(): + var arow: Dictionary[Variant, Variant] = db.treasure_item_row(String(iid_v)) + if (arow.get("effect_kinds", []) as Array).has("shop_refresh_bonus"): + total += int(arow.get("magnitude", 0)) + return total + + +func shop_refresh_cost() -> int: + return int(ceil(float(db.adventure.shop_refresh_cost_base) + * pow(db.adventure.shop_refresh_cost_curve, float(paid_refreshes)))) + + +func shop_refresh() -> bool: + if not pool_mode or shelf.is_empty(): + return false + if shop_refresh_free_left() <= 0: + var cost := shop_refresh_cost() + if _gp() < cost: + return false + _set_gp(_gp() - cost) + paid_refreshes += 1 + _draw_shelf() + return true + + +# GP 冒险钱包单口支出(三选一 GP 付费刷新与商店刷新共用同一钱包;余额不足 false 零副作用) +func spend_gp(amount: int) -> bool: + if amount <= 0 or _gp() < amount: + return false + _set_gp(_gp() - amount) + return true + + +# 购买(pool 模式唯一购买口):在架(kind→在架令牌)/余额前置校验零副作用 → handler 消费在架 +# 令牌本体(kind+tier——_apply_curio_pick 同口写加算区+账本+摘牌,跨渠道共库)→ 出账 → 进店购买 +# 次计数 → wallet 事件 → 三件全重抽(未买不保留——重抽绑购买,无独立刷新动作) +func buy_pool_item(kind: String) -> bool: + var shelf_tok := {} + for tok in shelf: + if String((tok as Dictionary).get("kind", "")) == kind: + shelf_tok = tok + break + if not pool_mode or shelf_tok.is_empty(): + push_error("[关卡节点地图] 商店购买拒绝——非池模式或件不在架: %s" % kind) + return false + var price := pool_price(kind) + if _gp() < price: + push_warning("[关卡节点地图] 购买拒绝——余额不足: %s(需 %d,钱包 %d)" % [kind, price, _gp()]) + return false + var tier := String(shelf_tok.get("tier", "")) + if pool_consume.is_null() or not bool(pool_consume.call(kind, tier)): + push_error("[关卡节点地图] 购买拒绝——池库存消费失败: %s|%s" % [kind, tier]) + return false + _set_gp(_gp() - price) + visit_buys += 1 + _emit("buy", {"product": kind, "price": price}) + _draw_shelf() # 买 1 → 三件全重抽(恰一 shop_draw scope——同进店取号签) + return true + + +# 池模式谓词(呈现层组合根分发用——货架面 vs legacy 商品面) +func is_shop_pool() -> bool: + return pool_mode + + +# 断点快照读面(P2 甲货架+账本入断点——AdventureRunner._snapshot_breakpoint 消费;离开店即空架) +func shop_bp_shelf() -> Array: + return shelf.duplicate() + + +func shop_bp_visit_buys() -> int: + return visit_buys + + +# ---- 宿主引用读取(共享引用绑定面——钱包/账类/图行/装配集)---- + +func _gp() -> int: + return int((_refs["wallet"] as Dictionary).get("gp", 0)) + + +func _set_gp(v: int) -> void: + (_refs["wallet"] as Dictionary)["gp"] = v + + +func _bonus() -> Dictionary: + return (_refs["run_ledger"] as RunLedger).bonus + + +func _level_index() -> int: + return int((_refs["level_row"] as Dictionary).get("level_index", 0)) + + +func _rules() -> Array: + return _refs["tree_rules"] as Array + + +func _attunement() -> Array: + return _refs["attunement"] as Array + + +func _emit(event: String, extra: Dictionary) -> void: + (_hooks["wallet_emit"] as Callable).call(event, _gp(), extra) diff --git a/game/core/shop_session.gd.uid b/game/core/shop_session.gd.uid new file mode 100644 index 00000000..e4af0d6c --- /dev/null +++ b/game/core/shop_session.gd.uid @@ -0,0 +1 @@ +uid://bwqf28xjs0tnf diff --git a/game/core/status_container.gd b/game/core/status_container.gd index 813b5a18..e1433e10 100644 --- a/game/core/status_container.gd +++ b/game/core/status_container.gd @@ -56,9 +56,9 @@ func apply_effect(effect_id: String, source: Dictionary, duration_override: int "stack": for inst in _instances: if inst.effect_id == effect_id: - if inst.layers >= max_stack: + if int(inst.layers) >= max_stack: return false # 满层拒绝原子——容器零写入(拒收由战斗结算记"奖励未生效"行) - inst.layers += 1 # stack 授予不触碰 R(spec 决议) + inst.layers = int(inst.layers) + 1 # stack 授予不触碰 R(spec 决议) return true _instances.append(_new_instance(effect_id, 1, dur, source, snap_m, snap_unit, snap_display)) return true @@ -99,7 +99,7 @@ func query_active(side: String) -> Array: "display_name": inst.snap_display, "snap_magnitude": inst.snap_m, "snap_unit": inst.snap_unit, - "effective_value": inst.layers * inst.snap_m, # L×m 加算(percent 越域不 clamp——收口归 战斗结算) + "effective_value": int(inst.layers) * int(inst.snap_m), # L×m 加算(percent 越域不 clamp——收口归 战斗结算) }) return out @@ -111,7 +111,7 @@ func decay_round() -> int: var k := 0 var survivors: Array[Dictionary] = [] for inst in _instances: - inst.remaining -= 1 + inst.remaining = int(inst.remaining) - 1 if inst.remaining <= 0: k += 1 else: diff --git a/game/core/treasure_items.gd b/game/core/treasure_items.gd index e3ec1fc6..fb0ab1b9 100644 --- a/game/core/treasure_items.gd +++ b/game/core/treasure_items.gd @@ -52,14 +52,6 @@ func inventory_count() -> int: return _items.size() -# 持有数(同调位上限判定读数:只计未消耗件;Used 不可逆即不再持有) -# 同调位守卫谓词(读取源 transitional_sync_slots 过渡键已随表退役; -# 容量闸现行事实源 = 营地装配面 Attunement.slots 满位禁选。函数体保留空壳防四散引用误删遗漏—— -# 冒烟「过渡键读取点归零」断言覆盖) - - -# 持有件记录(奖励节点替换子交互数据源:只列未消耗件,handle 定位丢弃目标) -# 丢弃(同调位替换交互节点渠道——腾位收新;与 Used 消费分轨的一次性移除,快照不再含) # 整图零信息可用性预判(图标置灰/入口禁用判据——GDD「整张图都没东西可看」级): # 真视恒可用(零雷窗口=信息非空);AP 类恒可用(组合判据取 OR——任一效果有信息即可用); # 纯预读行 = 板上还有未开格才可用。(积点件不入使用面——自动兑付无玩家入口) diff --git a/game/core/unit_container.gd b/game/core/unit_container.gd index f1716edb..b34925cc 100644 --- a/game/core/unit_container.gd +++ b/game/core/unit_container.gd @@ -52,7 +52,7 @@ func spawn(level_index: int, node_depth: int, hero_hp: int, unit_count_overrides return false # max_hp = 生效上限(非入参 hero_hp)——跨战斗携带 HP 时上限不得压扁 # (恢复点/治疗包的满血守卫与治疗上限随之失效) - _hero = {"hp": hero_hp, "max_hp": eff_max_hp, "atk": eff_atk, "is_hero": true, "alive": true, "display_name": "探险者", "enemy_id": "hero", "armor_type": hero_armor_type if hero_armor_type != "" else db.forge_default_armor_type(), "attack_type": hero_attack_type if hero_attack_type != "" else db.forge_default_attack_type()} # 护甲/攻击类型轴快照(读注入值,空串回退锻造工坊配置默认值 normal/light;甲效矩阵消费面) + _hero = {"hp": hero_hp, "max_hp": eff_max_hp, "atk": eff_atk, "is_hero": true, "alive": true, "display_name": String(db.hero.display_name), "enemy_id": "hero", "armor_type": hero_armor_type if hero_armor_type != "" else db.forge_default_armor_type(), "attack_type": hero_attack_type if hero_attack_type != "" else db.forge_default_attack_type()} # 护甲/攻击类型轴快照(读注入值,空串回退锻造工坊配置默认值 normal/light;甲效矩阵消费面) _enemies.clear() # 难度曲线与Boss DifficultyTable 修饰:施加于行值、行值快照之前——MVP 空表 = 恒 1.0 无行为(AC-10);混编行集内全部敌种同乘((L,D) 精确匹配一次取值——F9 同乘不改变异,敌人系统 v0.15) var mod: Dictionary[Variant, Variant] = db.resolve_difficulty_modifiers(level_index, node_depth) var hp_mult: float = float(mod.get("hp_mult", 1.0)) @@ -254,7 +254,7 @@ func apply_damage(u: Dictionary, n: int) -> bool: if u.is_empty() or _despawned or not u.alive: _diag("apply_damage 失效/死亡句柄") return false - u.hp = maxi(0, u.hp - n) + u.hp = maxi(0, int(u.hp) - n) if u.hp == 0: u.alive = false # Dead 不可逆(死亡判定时机归 战斗结算) unit_died.emit(u) # 死亡事件——单位自行宣布 @@ -267,7 +267,7 @@ func heal(u: Dictionary, n: int) -> int: _diag("heal 非法/失效句柄") return 0 var actual: int = mini(n, int(u.max_hp) - int(u.hp)) - u.hp += actual + u.hp = int(u.hp) + actual return actual diff --git a/game/data/schemas/hero_table.gd b/game/data/schemas/hero_table.gd index 11f8176f..e567d56d 100644 --- a/game/data/schemas/hero_table.gd +++ b/game/data/schemas/hero_table.gd @@ -8,6 +8,7 @@ extends Resource @export var max_hp: int = 50 # [1,999] knob(provisional,溯源 = 原型锚) @export var atk: int = 6 # [1,999] knob——基础攻击(溯源 = 概念原型 HERO_BASE_ATK,provisional 归 难度曲线与Boss);消费面 = 战斗结算·战斗段先手攻击 @export var armor_class: int = 10 # [0,999] knob——防守方护甲等级基准(暂定 10 归 难度曲线与Boss);消费面 = 战斗结算掷点判定阈值基数(ac_up 效果加算其上) +@export var display_name: String = "探险者" # 英雄单位显示名(与敌人行 display_name 同轴;单位快照读取面单源) func validate() -> Array: @@ -18,4 +19,6 @@ func validate() -> Array: errs.append({"category": "domain", "message": "atk 越域 [1,999]: %d" % atk}) if armor_class < 0 or armor_class > 999: errs.append({"category": "domain", "message": "armor_class 越域 [0,999]: %d" % armor_class}) + if display_name.strip_edges() == "": + errs.append({"category": "domain", "message": "display_name 为空"}) return errs diff --git a/game/data/schemas/shop_table.gd b/game/data/schemas/shop_table.gd index 4868d8da..8028e36e 100644 --- a/game/data/schemas/shop_table.gd +++ b/game/data/schemas/shop_table.gd @@ -12,6 +12,8 @@ extends Resource @export var pool_shelf: Array[Dictionary] = [] # Array[Dictionary] 键 kind_id @export var price_a: float = 0.0 # 进店已购次数爬价因子(provisional——0 = 未投值即致命域) @export var price_b: float = 0.0 # 跨关递增因子(provisional) +@export var relic_base_price: float = 15.0 # relic 保底卡基价(provisional——池令牌非货架种,词表外专枝) +@export var spend_discount: float = 0.8 # 财富之道·散 economy_spend 激活时的购买折扣(provisional) const GP_TIERS := ["normal", "elite", "boss"] const PRODUCT_ATTRS := ["atk", "max_hp", "armor_class"] @@ -20,11 +22,24 @@ const SHELF_KINDS := ["attack", "max_hp", "armor", "ap", "feat_ticket", "heal"] func validate() -> Array: var errs: Array[Dictionary] = [] - # 行族空护栏(legacy 四轴 + pool_shelf 皆为 ConfigDB 消费面——缺族即致命) + _validate_families(errs) + _validate_gp_drops(errs) + _validate_level_bonus(errs) + _validate_products(errs) + _validate_pool_shelf(errs) + _validate_price_factors(errs) + return errs + + +# 行族空护栏(legacy 四轴 + pool_shelf 皆为 ConfigDB 消费面——缺族即致命) +func _validate_families(errs: Array[Dictionary]) -> void: for family in ["gp_drops", "level_gp_bonus", "products", "price_level_bonus", "pool_shelf"]: if get(family).is_empty(): errs.append({"category": "empty_table", "message": "ShopTable 行族为空: %s" % family}) - # gp_drops:tier 词表封闭 + 掷定域 + 均值锚 + tier 唯一 + + +# gp_drops:tier 词表封闭 + 掷定域 + 均值锚 + tier 唯一 +func _validate_gp_drops(errs: Array[Dictionary]) -> void: var seen_tier := {} for row in gp_drops: var tier: String = row.get("tier", "") @@ -42,7 +57,10 @@ func validate() -> Array: errs.append({"category": "domain", "message": "gp_drops %s 掷定域越域 [0,999]: %d..%d..%d" % [tier, rmin, mean, rmax]}) if rmin > mean or mean > rmax: errs.append({"category": "cross_field", "message": "gp_drops %s 违反 roll_min(%d) ≤ mean(%d) ≤ roll_max(%d)——均值恒等期望锚" % [tier, rmin, mean, rmax]}) - # level_gp_bonus / price_level_bonus:level_index 连续从 1 递增 + 加值域 + + +# level_gp_bonus / price_level_bonus:level_index 连续从 1 递增 + 加值域 +func _validate_level_bonus(errs: Array[Dictionary]) -> void: for family in ["level_gp_bonus", "price_level_bonus"]: var arr: Array[Variant] = get(family) var seen := {} @@ -59,7 +77,10 @@ func validate() -> Array: if li != i + 1: errs.append({"category": "structural", "message": "%s level_index 不连续(须从 1 连续递增): 位 %d = %d" % [family, i + 1, li]}) break - # products:product_id 唯一 + attr 词表 + 价格域 [1,999] + + +# products:product_id 唯一 + attr 词表 + 价格域 [1,999] +func _validate_products(errs: Array[Dictionary]) -> void: var seen_pid := {} for row in products: var pid: String = row.get("product_id", "") @@ -83,7 +104,10 @@ func validate() -> Array: var inc: int = int(row.get("price_increment", -1)) if inc < 0 or inc > 999: errs.append({"category": "domain", "message": "%s price_increment 越域 [0,999]: %d" % [pid, inc]}) - # pool_shelf:kind_id 词表封闭(四切边后种)+ 唯一 + display_name + base_price 域 [1,999] + + +# pool_shelf:kind_id 词表封闭(四切边后种)+ 唯一 + display_name + base_price 域 [1,999] +func _validate_pool_shelf(errs: Array[Dictionary]) -> void: var seen_kind := {} for row in pool_shelf: var kind: String = row.get("kind_id", "") @@ -97,9 +121,15 @@ func validate() -> Array: var base: int = int(row.get("base_price", 0)) if base < 1 or base > 999: errs.append({"category": "domain", "message": "pool_shelf %s base_price 越域 [1,999]: %d" % [kind, base]}) - # 价格因子域(乘法语义——A/b 均 ≥1 单调不降;上界防手滑投值;provisional 值本体归难度曲线与Boss) + + +# 价格因子域(乘法语义——A/b 均 ≥1 单调不降;上界防手滑投值;provisional 值本体归难度曲线与Boss) +func _validate_price_factors(errs: Array[Dictionary]) -> void: if price_a < 1.0 or price_a > 3.0: errs.append({"category": "domain", "message": "price_a 越域 [1.0,3.0]: %f" % price_a}) if price_b < 1.0 or price_b > 2.0: errs.append({"category": "domain", "message": "price_b 越域 [1.0,2.0]: %f" % price_b}) - return errs + if relic_base_price < 1.0 or relic_base_price > 99.0: + errs.append({"category": "domain", "message": "relic_base_price 越域 [1.0,99.0]: %f" % relic_base_price}) + if spend_discount <= 0.0 or spend_discount > 1.0: + errs.append({"category": "domain", "message": "spend_discount 越域 (0.0,1.0]: %f" % spend_discount}) diff --git a/game/data/tables/hero.tres b/game/data/tables/hero.tres index b7889025..33e9d1ad 100644 --- a/game/data/tables/hero.tres +++ b/game/data/tables/hero.tres @@ -7,6 +7,7 @@ script = ExtResource("1") max_hp = 26 atk = 2 armor_class = 10 +display_name = "探险者" ; 〔2026-10-12 迭代 23 锻造工坊实装批 T2——两根类型轴删除(契约 2 所有权移交):armor_type/ ; attack_type 两字段随本批从静态表删除,默认值归锻造工坊配置常驻持有(forge.tres diff --git a/game/data/tables/shop.tres b/game/data/tables/shop.tres index 8d502b2f..60b6f6a1 100644 --- a/game/data/tables/shop.tres +++ b/game/data/tables/shop.tres @@ -92,6 +92,8 @@ pool_shelf = [{ }] price_a = 1.8 price_b = 1.1 +relic_base_price = 15.0 +spend_discount = 0.8 ; GP+商店房实施批(2026-09-20 用户拍板,设计权威 design/research/proposal-gp-shop-room-2026-09-20.md 裁决记录): ; 掉落=均值±方差掷定(gp_drop 封闭流)+关级加值;精英行为预留行(当前无消费方,随精英节点内容批启用); diff --git a/game/presentation/board_resolvers.gd b/game/presentation/board_resolvers.gd new file mode 100644 index 00000000..554d8c5e --- /dev/null +++ b/game/presentation/board_resolvers.gd @@ -0,0 +1,67 @@ +## VERTICAL SLICE - NOT FOR PRODUCTION +## 棋盘每格渲染数据的只读解析回调族:奖励徽章身份(挖掘期 Active 相位内)、地形变异数字遮蔽与角标、 +## 调试加成生效值。有态绑定件——构造注入 db、调试浮窗与结算活态取值,产出绑定 Callable 供组合根接线; +## 与无态静态投影族(HudPresenter)职责互斥。 +extends RefCounted + +var _db: ConfigDb +var _debug_overlay: DebugOverlay +var _settlement_getter: Callable + + +func _init(db_ref: ConfigDb, debug_overlay_ref: DebugOverlay, settlement_getter: Callable) -> void: + _db = db_ref + _debug_overlay = debug_overlay_ref + _settlement_getter = settlement_getter + + +# 加成生效值供给(debug_session 注入 Callable——每次从浮窗百分比现算,经 encounter 传参零表写; +# 禁表写路径(双写叠加隐患源头); +# 合成式单点在 UiKit.scaled——与调试浮窗生效值预览共用,本类不持有同式私有实现) +func debug_hero_override() -> Dictionary: + return {"atk": UiKit.scaled(int(_db.hero.atk), _debug_overlay.atk_pct()), + "max_hp": UiKit.scaled(int(_db.hero.max_hp), _debug_overlay.hp_pct())} + + +# 奖励徽章身份解析(组合根守卫:仅挖掘期 Active 相位内查询——地图战斗资源 Ready 域外零读、零诊断噪音; +# 相位取数经 presentation_snapshot 成文面——禁 settlement.action_economy 链式穿透) +func identity_lookup(c: Vector2i) -> Variant: + var st: BattleOrchestrator = _settlement_getter.call() + if st == null or st.minesweeper.current_board() == null: + return null + if String(st.presentation_snapshot().get("phase", "")) != ActionEconomy.PHASE_ACTIVE: + return null + return st.query_reward_identity(c) + + +# 地形变异 数字遮蔽解析(信息型 hide_number——格带该效果变异即遮蔽;读取方按格子变异数据行动。 +# 变异表直读注入 db——settlement.db 即组合根注入的同一 ConfigDB 实例(AdventureRunner/DebugSession +# 构造传参),免 settlement 内部链式穿透) +func variant_mask_lookup(c: Vector2i) -> bool: + var st: BattleOrchestrator = _settlement_getter.call() + if st == null: + return false + var b = st.minesweeper.current_board() + if b == null: + return false + for vid in b.variant_ids(c): + var vrow: Dictionary[Variant, Variant] = _db.variation.row_by_id(String(vid)) + if String(vrow.get("effect", "")) == "hide_number": + return true + return false + + +# 地形变异 角标解析(伤害型变异未开格视觉标记;读取方按格子变异数据行动,规则本体零特判。 +# 策略纹理非数值墙:标记=玩家绕行/承担的决策输入,难度本体仍住数值包络。变异表直读注入 db) +func variant_mark_lookup(c: Vector2i) -> String: + var st: BattleOrchestrator = _settlement_getter.call() + if st == null: + return "" + var b = st.minesweeper.current_board() + if b == null: + return "" + for vid in b.variant_ids(c): + var vrow: Dictionary[Variant, Variant] = _db.variation.row_by_id(String(vid)) + if String(vrow.get("effect", "")) == "flat_damage": + return "flat_damage" + return "" diff --git a/game/presentation/board_resolvers.gd.uid b/game/presentation/board_resolvers.gd.uid new file mode 100644 index 00000000..f07cc1cb --- /dev/null +++ b/game/presentation/board_resolvers.gd.uid @@ -0,0 +1 @@ +uid://crn30mow6d36j diff --git a/game/presentation/debug_session.gd b/game/presentation/debug_session.gd index d8702bc3..595a0adb 100644 --- a/game/presentation/debug_session.gd +++ b/game/presentation/debug_session.gd @@ -4,8 +4,8 @@ ## 加成生效值经注入 Callable 现算、随 encounter 显式传参——零表写。 extends RefCounted -signal battle_created(settlement, level: int, carry_hp: int) # begin_battle 前——组合根完成信号接线 -signal battle_ready(settlement, level: int, carry_hp: int) # begin_battle 完成后——组合根 rebuild 棋盘 + 刷新呈现 +signal battle_created(settlement, level: int, carry_hp: int) ## begin_battle 前——组合根完成信号接线 +signal battle_ready(settlement, level: int, carry_hp: int) ## begin_battle 完成后——组合根 rebuild 棋盘 + 刷新呈现 var db var rng # /root/RngService(组合根注入) diff --git a/game/presentation/hud_presenter.gd b/game/presentation/hud_presenter.gd index f86a7541..d11cbf0c 100644 --- a/game/presentation/hud_presenter.gd +++ b/game/presentation/hud_presenter.gd @@ -5,6 +5,8 @@ class_name HudPresenter extends RefCounted +const Narrator := preload("res://presentation/battle_narrator.gd") + # 每次结算行/轮次/节点变化/动作返回后调用(组合根全量重查装配) # 钱包 GP/钥石不在本装配面投喂(中/大循环资源迁出战斗屏——信息归属单一展示面原则); @@ -216,3 +218,113 @@ static func _p90(a: Array) -> float: var b := a.duplicate() b.sort() return float(b[mini(b.size() - 1, int(0.9 * b.size()))]) + + +# ---- 组合根判离承接·数据投影族(静态无状态——db/save/meta/runner 现值由调用侧传参) ---- + +# 状态面板快照投影(数据只读同源:每次开层实时读访问器现值拼装—— +# 快照仅服务单次开层生命周期(open_status 重建各行),跨开层零持有;零写调用。 +# 八键换代(迭代 36 卡③):跨关解锁概览退役(营地/生涯承载)——只留本局信息; +# ledger 直取下标(键缺失=致命,禁 .get 缺省留门——fail-fast 红线);禁 null 守卫软回退 +# (两入口开层时 runner 恒在役——状态面板卡 F3 证据链) +static func status_data(db: ConfigDb, meta: MetaProgressionCore, runner: Variant, treasure_ui: Variant) -> Dictionary: + return { + "balance": meta.keystone_balance(), + "hero_hp": int(runner.hero_hp), # LevelRunner 属性面(_ledger 直取下标——键缺失=致命) + "max_hp": runner.effective_max_hp(), + "atk": runner.effective_atk(), + "ac": runner.effective_ac(), + "gp": int(runner.gp), + "curios": status_curios(treasure_ui), + "feats": feat_holdings_lines(db, runner.feats_held()), + } + + +# 秘宝行映射(inventory_projection 现役投影逐行取 name/used 两键——受理序沿库存投影; +# 禁第二份投影:件名换算已在 inventory_projection 收口,此处只做键裁剪) +static func status_curios(treasure_ui: Variant) -> Array: + var out: Array[Variant] = [] + for row in (treasure_ui.inventory_projection() as Array): + var r: Dictionary[Variant, Variant] = row + out.append({"name": String(r.get("name", "")), "used": bool(r.get("used", false))}) + return out + + +# 生涯页快照投影(只读同源——每次开层实时读 SaveManager 计数访问器 + MetaProgression 访问器 +# 现值拼装(状态面板 status_data 同构);快照仅服务单次开层生命周期,跨开层零持有;零写调用。 +# 生涯页只统计终身计数——不统计天赋树与账号成长态, +# 故 forge_levels_total/workshop/tree 三投影不在本投影内) +static func career_data(db: ConfigDb, save_manager: SaveManagerCore, meta: MetaProgressionCore) -> Dictionary: + var clears: Array[Dictionary] = [] + var boss_first := 0 + for i in db.level_count(): + var li: int = i + 1 + clears.append({"display_name": String(db.level_row(li).get("display_name", "")), + "count": save_manager.clear_count(li)}) + if save_manager.boss_first_kill_marked(li): + boss_first += 1 + return { + "total": save_manager.adventure_total(), + "cleared": save_manager.adventure_cleared(), + "clears": clears, + "boss_first_kills": boss_first, + "boss_first_total": db.level_count(), + "boss_kills": save_manager.boss_kill_count(), + "keystone": meta.keystone_balance(), + } + + +# 槽数据快照(只读同源——每次开层实时经卡① 窥视 API 逐槽取数拼装;零写调用)。 +# occupied = has_file ∧ readable:坏槽/高版本槽按空槽呈现(Q2 裁甲——进档/新档均经卡① 兜底链接管); +# 摘要仅三项(裁决 4),has_file 只是空/有档操作集二分判定来处(非摘要项)。 +static func save_select_data(save_manager: SaveManagerCore) -> Dictionary: + var slots: Array[Dictionary] = [] + for i in range(1, save_manager.SLOT_COUNT + 1): + var p := save_manager.peek_slot_summary(i) # 推断 Dictionary(窥视返回合同——裸注解触 R1、参数化赋值运行期严格拒) + slots.append({"slot": i, + "occupied": bool(p.get("has_file", false)) and bool(p.get("readable", false)), + "keystone": int(p.get("keystone", 0)), + "cleared": int(p.get("cleared", 0)), + "total": int(p.get("total", 0))}) + return {"slots": slots} + + +# 营地投影(承接件单点转发 camp_shop_presenter——组合根只留探针缝薄委托) +static func camp_data(pres: Variant) -> Dictionary: + return pres.camp_data() + + +# ---- 组合根判离承接·文案拼装三件(纯拼装零状态) ---- + +# 战利品合计行(runner 缺位 = 续玩恢复面:resume_adventure 的 bonus_pending 分支在 level_started +# 之前同步 curio_choice_presented.emit〔resume 提前 return〕,本行求值时 runner 尚未赋值; +# 合计改读断点快照 gp 兜底——恢复源快照此刻必在档,语义=续玩恢复面的合计即存档合计) +static func gp_summary_text(payload: Dictionary, runner: Variant, save_manager: SaveManagerCore) -> String: + if runner == null: + var bp: Dictionary[Variant, Variant] = save_manager.breakpoint_snapshot() + return "战利品 GP +%d · 合计 %d" % [int(payload.get("gp_dropped", 0)), int(bp.get("gp", 0))] + return "战利品 GP +%d · 合计 %d" % [int(payload.get("gp_dropped", 0)), int(runner.gp)] + + +# 结算三行账·本关加成行装配(呈现只读投影——runner.bonus 统一加算区同源;零加成=行隐藏) +static func bonus_summary_text(runner: Variant) -> String: + if runner == null: + return "" + var b: Dictionary[Variant, Variant] = runner.run_ledger.bonus # 加算区住 RunLedger + var parts: Array[String] = [] + if int(b.get("atk", 0)) != 0: + parts.append("攻 %+d" % int(b.get("atk", 0))) + if int(b.get("max_hp", 0)) != 0: + parts.append("上限 %+d" % int(b.get("max_hp", 0))) + if int(b.get("armor_class", 0)) != 0: + parts.append("AC %+d" % int(b.get("armor_class", 0))) + return "本关加成 " + " · ".join(parts) + + +# 相位行(文案装配归 narrator——settlement 缺位 = 非战斗态;相位取数经 presentation_snapshot 成文面) +static func phase_text(settlement: BattleOrchestrator, runner: Variant, boss_active: bool, db: ConfigDb) -> String: + if settlement == null: + return "第 — 轮 · —" + return Narrator.phase_text(int(settlement.round_index), String(settlement.battle_result), + String(settlement.presentation_snapshot().get("phase", "")) == ActionEconomy.PHASE_WAITING, + boss_active, Narrator.boss_name(runner, db)) diff --git a/game/presentation/main.gd b/game/presentation/main.gd index 357b51f4..04700243 100644 --- a/game/presentation/main.gd +++ b/game/presentation/main.gd @@ -22,6 +22,11 @@ const RowPacer := preload("res://presentation/row_pacer.gd") const TreasureInteractController := preload("res://presentation/treasure_interact_controller.gd") # 秘宝两步交互 const CurioInteractController := preload("res://presentation/curio_interact_controller.gd") # 三选一与替换子交互 const CampShopPresenter := preload("res://presentation/camp_shop_presenter.gd") # 营地/商店投影 +const BoardResolversScript := preload("res://presentation/board_resolvers.gd") # 棋盘解析回调族 +const MainUiBuild := preload("res://presentation/main_ui_build.gd") # 装配分步承接件 +const MainAdventureFlow := preload("res://presentation/main_adventure_flow.gd") # 冒险流程承接件 +const MainOverlayFlow := preload("res://presentation/main_overlay_flow.gd") # 覆盖层流程承接件 +const MainDebugFlow := preload("res://presentation/main_debug_flow.gd") # 调试流程承接件 const MenuScreenScene := preload("res://presentation/scenes/menu_screen.tscn") # 三屏壳场景 const CampScreenScene := preload("res://presentation/scenes/camp_screen.tscn") const AdventureScreenScene := preload("res://presentation/scenes/adventure_screen.tscn") @@ -188,10 +193,6 @@ func _ready() -> void: settings_store = SettingsStore.new() settings_store.load_settings() settings_store.apply() - _debug = DebugSessionScript.new(db, get_node(AUTOLOAD_PATH_RNG), - _debug_hero_override, _seed_value) - _debug.battle_created.connect(_on_debug_battle_created) - _debug.battle_ready.connect(_on_debug_battle_ready) _build_ui() _begin_session(false) # 闲时会话(gp_ledger 等探针注入面自 _ready 起恒在——ui_probe 合成段时序) _start_overlay.visible = true @@ -204,199 +205,12 @@ func _ready() -> void: # ==================== 视图接线(三屏壳 + 模态层装配;构建归屏壳/UiAssembler,本类只接线) ==================== func _build_ui() -> void: - # 三屏壳实例化:冒险屏先挂、菜单屏后挂(启动态菜单在顶层=玩家可见—— - # 装配序纪律:菜单先挂会被冒险屏盖死,F5 见战斗界面);最终树序+登记序以 LAYER_CONTRACT - # 为唯一事实源,_apply_layer_contract 在装配尾统一落位。 - # 屏内 level_screen 与 battle_root 无同时可见态(屏级互斥+模态栈遮挡),层内先后零视觉差; - # 视觉共存对逐对验算:legend/battle(图例战斗中开——legend 在冒险屏壳之后=其上 ✓)、 - # debug/level_screen(浮窗最后子=其上 ✓)、shop/level_screen(商店节点域弹出——模态均在屏壳上 ✓)。 - _adventure_screen = AdventureScreenScene.instantiate() - add_child(_adventure_screen) - _menu_screen = MenuScreenScene.instantiate() - add_child(_menu_screen) - _start_overlay = _menu_screen.start_overlay - _battle_root = _adventure_screen.battle_root - stage = _adventure_screen.stage - log_view = _adventure_screen.log_view - info_panel = _adventure_screen.info_panel - board_zone = _adventure_screen.board_zone - board_view = _adventure_screen.board_view # 探针兼容别名(= board_zone.board_view) - ap_bar = _adventure_screen.ap_bar # 探针兼容别名(= board_zone.ap_bar) - settlement_fx = _adventure_screen.settlement_fx - level_screen = _adventure_screen.level_screen - - # 结算行排播泵(本体在 row_pacer.gd;本类注入视图句柄与呈现刷新回调,零信号连接) - _row_pacer = RowPacer.new() - _row_pacer.log_view = log_view - _row_pacer.stage = stage - _row_pacer.board_zone = board_zone - _row_pacer.settlement_fx = settlement_fx - _row_pacer.board_view = board_view - _row_pacer.refresh_request = _refresh_presentations - _row_pacer.pump_ended = _on_pump_ended # 演播完边沿统一派发(挂起 modal 优先,其次锁期点击重放) - add_child(_row_pacer) - # 秘宝两步交互(本体在 treasure_interact_controller.gd;settlement/runner 随关卡更替, - # 经现值回调注入——仿 identity_resolver 只读回调先例) - _treasure_ui = TreasureInteractController.new(db, meta, info_panel, board_zone.toast, board_view, - func() -> Variant: return run_scope.settlement, func() -> Variant: return run_scope.runner) - - board_zone.action_requested.connect(_on_board_action) - board_zone.status_badge.pressed.connect(_on_status_badge_pressed) # 状态面板入口(战斗屏 HUD 角标) - info_panel.treasure_slot_clicked.connect(_treasure_ui.on_slot_clicked) # 两步交互第一步 - board_zone.stop_pressed.connect(_on_stop_pressed) - board_zone.refresh_confirmed.connect(_on_refresh_confirmed) - board_zone.legend_toggled.connect(_on_legend_toggled) - board_zone.hover_forbidden.connect(_on_hover_forbidden) - board_zone.chord_attempted.connect(_on_chord_attempted) - - # 奖励徽章数据源:地图战斗资源 身份解析只读回调(已开格身份 icon——挖掘期 Active 相位内有效) - board_view.set_identity_resolver(_identity_lookup) - # 数字遮蔽数据源:地形变异 hide_number 解析只读回调(信息型呈现——board_view 渲染面读取) - board_view.set_variant_mask_resolver(_variant_mask_lookup) - board_view.set_variant_mark_resolver(_variant_mark_lookup) # flat_damage 未开格角标 - - logger = PlaytestLogger.new() - logger.ap_per_round = int(db.action_economy.ap_per_round) - - # 模态覆盖层(登记序 = 树序 = z 遮挡序:图例 < 明细回看 < 节点 < 关卡画面 < 三选一 < 终局 - # < 商店 < 营地 < 设置 < 状态面板(只读) < 生涯页(只读) < 确认弹窗 < 调试浮窗; - # 关卡画面住冒险屏壳(树序在模态之下)——登记序仍夹在节点层与三选一层之间,遮挡语义零改动) - var overlays: Dictionary[Variant, Variant] = UiAssembler.build_overlays(self, db) - legend_overlay = overlays.legend_overlay - node_overlay = overlays.node_overlay - _end_overlay = overlays.end_overlay - shop_overlay = overlays.shop_overlay - _camp_screen = CampScreenScene.instantiate() - add_child(_camp_screen) - camp_overlay = _camp_screen.camp_overlay - # 营地屏壳插位(夹商店层与设置层之间)由 LAYER_CONTRACT 营地槽统一落位——禁散落 move_child - legend_overlay.closed.connect(_on_modal_closed.bind(legend_overlay)) - node_overlay.continue_pressed.connect(_on_node_continue) - level_screen.route_chosen.connect(_on_route_chosen) - level_screen.status_badge.pressed.connect(_on_status_badge_pressed) # 状态面板入口(地图屏顶栏角标) - level_screen.abandon_requested.connect(_on_abandon_requested) # 放弃入口 - _end_overlay.continue_pressed.connect(_on_battle_end_continue) - settings_overlay = SettingsOverlay.new(settings_store) # 最小设置面(音量/全屏/减动效) - add_child(settings_overlay) - settings_overlay.closed.connect(_on_settings_closed) - # 局内状态面板·只读(登记设置层同档带 dim 遮罩,确认弹窗之前; - # 数据=组合根开层实时读 MetaProgression 访问器喂入,本层零写通路) - status_panel = StatusPanel.new() - add_child(status_panel) - status_panel.closed.connect(_on_status_closed) - # 生涯页·只读全屏页(登记序=营地同档,设置层旁、确认弹窗之前; - # 树序同步=addChild 同位(登记序=树序=z 序纪律);数据=组合根开层实时读 SaveManager 计数 - # 访问器+MetaProgression 访问器喂入,本层零写通路零跳转;入口=开始层「营地与系统」组生涯钮) - career_overlay = CareerOverlay.new() - add_child(career_overlay) - career_overlay.closed.connect(_on_career_closed) - # 选档界面(营地交互重做卡③):登记序=生涯同档(树序同步=addChild 同位);三动作只发意图信号 - #(激活/创建/删除+确认分派归组合根——视图零数据层依赖);入口=「进入存档」钮(卡② mn3 接线)。 - save_select_overlay = SaveSelectOverlay.new() - add_child(save_select_overlay) - save_select_overlay.closed.connect(_on_save_select_closed) - save_select_overlay.new_slot_requested.connect(_on_save_select_new) - save_select_overlay.load_slot_requested.connect(_on_save_select_load) - save_select_overlay.delete_slot_requested.connect(_on_save_select_delete) - # 提升三选一覆盖层(z 序 = 节点层之上、终局层之下;确认后同拍推进; - # 插位由 LAYER_CONTRACT curio 槽统一落位——禁散落 move_child) - curio_overlay = CurioPickOverlay.new() - add_child(curio_overlay) - # 提升三选一与替换子交互(本体在 curio_interact_controller.gd;本类只接线与回调注入, - # 模态栈/演播后排窗/呈现刷新/fatal 经回调注入,adventure/runner 现值回调) - _bonus = CurioInteractController.new(CurioFlowContext.new(db, func() -> Variant: return meta, curio_overlay, _end_overlay, board_view, - board_zone.toast, func() -> Variant: return save_manager, func() -> Variant: return run_scope.adventure, - func() -> Variant: return run_scope.runner, func() -> Variant: return run_scope.is_adventure, - _open_modal, _open_modal_after_playback, _close_modal, _refresh_presentations, - _fatal_screen, func() -> String: return _gp_summary_text({"gp_dropped": run_scope.last_gain_amount}))) # 构造参数收拢 context - curio_overlay.pick_confirmed.connect(_bonus.on_pick_confirmed) - curio_overlay.continue_pressed.connect(_bonus.on_continue) - curio_overlay.refresh_requested.connect(_bonus.on_refresh) - - # 菜单屏接线(句柄经屏壳暴露;三入口化——进入存档/设置/退出游戏,卡② mn3: - # 进入存档钮落点=卡③选档层 _open_save_select,点击链在此闭环) - _menu_screen.entersavebtn.pressed.connect(_open_save_select) - # 调试入口=UI 最顶层全局浮层角标(任何 UI 不遮挡——菜单/战斗/地图三枚 - # 屏内角标由全局浮层承载;连接归组合根,树序/登记面见 LAYER_CONTRACT 末位) - debug_badge = UiAssembler.build_debug_badge(self) - add_child(debug_badge) - debug_badge.pressed.connect(_on_debug_badge_pressed) - # 营地/商店投影(本体在 camp_shop_presenter.gd;本类只接线、探针缝保缝转发; - # 购买写调用唯一经 MetaProgression/LevelRunner core 单口——组合根 lambda 现值注入) - _camp_pres = CampShopPresenter.new(CampFlowContext.new(db, func() -> Variant: return meta, func() -> Variant: return save_manager, camp_overlay, shop_overlay, - board_zone.toast, _start_overlay, logger, func() -> Variant: return run_scope.runner, - _open_modal, _close_modal, _refresh_presentations)) # 构造参数收拢 context - shop_overlay.buy_requested.connect(_camp_pres.on_shop_buy) - shop_overlay.refresh_requested.connect(_camp_pres.on_shop_refresh) # MC-1 - shop_overlay.leave_pressed.connect(_camp_pres.on_shop_leave) - camp_overlay.buy_requested.connect(_camp_pres.on_camp_buy) - camp_overlay.attune_toggled.connect(_camp_pres.on_camp_attune_toggle) - camp_overlay.tree_buy_requested.connect(_camp_pres.on_camp_tree_buy) - camp_overlay.tree_fork_toggled.connect(_camp_pres.on_camp_fork_toggle) - camp_overlay.forge_buy_requested.connect(_camp_pres.on_camp_forge_buy) # 锻造三意图(写口唯一归 MetaProgression) - camp_overlay.forge_unlock_requested.connect(_camp_pres.on_camp_forge_unlock) - camp_overlay.forge_select_requested.connect(_camp_pres.on_camp_forge_select) - camp_overlay.closed.connect(_camp_pres.on_camp_closed) - _menu_screen.settingsbtn.pressed.connect(_open_settings) # 最小设置面入口 - _menu_screen.quitbtn.pressed.connect(_quit_game) # 退出游戏提级一级菜单 - - var inter: Dictionary[Variant, Variant] = UiAssembler.build_inter_overlay(self) - _inter_overlay = inter.overlay - _inter_label = inter.label - inter.next_btn.pressed.connect(_on_inter_next_pressed) - - # 确认弹窗(新冒险互斥确认弹窗化):树序落在开始层/过场层之后 = 子级最末 - #(纪律「登记序 = 树序 = z 序」——confirm_dialog.open 禁运行时 move_child 自提抬层; - # 弹窗只在开始菜单域弹出,绘制序恒最上; - # 诊断转储入口在调试浮窗) - confirm_dialog = ConfirmDialog.new() - add_child(confirm_dialog) - # 消费方编排(卡③ 契约 3/sl8——禁双常驻静态接线):open 前设 _confirm_consumer, - # confirmed/cancelled 后按该状态量分派;分派面可容纳后续消费方(卡④ 到位直接接入不返工) - confirm_dialog.confirmed.connect(_on_confirm_accepted) - confirm_dialog.cancelled.connect(_on_confirm_declined) - - # 调试浮窗(主菜单三散件收一:随机种子/数值调整/批量模拟/两层调试入口; - # 全局可开浮窗:调试会话恒开=启动即开〔_ready〕+两屏角标再开+当局数据只读区块; - # 诊断转储入口在本浮窗,玩家画面零常驻调试件)。 - # 树序在开始层与确认弹窗之后 = 浮于菜单之上的 Dialog(不隐藏开始层——与确认弹窗同款形态); - # 开窗唯一入口 = _show_debug_overlay(C6 单口——角标/探针/启动路径全收口) - debug_overlay = DebugOverlay.new(db, int(db.hero.atk), int(db.hero.max_hp)) - add_child(debug_overlay) - _seed_edit = debug_overlay.seed_edit # 种子读取面在浮窗(_seed_value 唯一消费点不变) - debug_overlay.closed.connect(_on_modal_closed.bind(debug_overlay)) - debug_overlay.apply_requested.connect(_on_tune_apply) - debug_overlay.simulate_requested.connect(_on_tune_simulate) - debug_overlay.debug_levels_requested.connect(_start_debug_levels) + MainUiBuild.build(self) debug_overlay.diagnose_requested.connect(_on_diagnose_pressed) # 诊断转储入口 - # 模态登记(缺陷⑤纪律——登记序 = 树序 = z 序):层序唯一事实源=LAYER_CONTRACT, - # _apply_layer_contract 统一落位(关卡画面宿住冒险屏壳、营地屏壳夹商店与设置层之间、 - # 三选一对位终局层之下等特例均在合同数据内表达,禁散落 move_child) - # 模态编排注入(ModalFlow 纯逻辑类:依赖仅 ModalStack+查询 Callable) - _modal_flow = ModalFlow.new() - _modal_flow.start_visible_get = func() -> bool: return _start_overlay.visible - _modal_flow.inter_visible_get = func() -> bool: return _inter_overlay.visible - _modal_flow.settlement_get = func() -> Variant: return run_scope.settlement - _modal_flow.rows_empty_get = func() -> bool: return _row_pacer.pending_rows().is_empty() - _modal_flow.flush_rows = _flush_row_queue - _modal_flow.on_open_sync = func() -> void: - _sync_status_entry() # 状态面板入口角标可见性/置灰随开态同步 - _sync_debug_overlay() # 浮窗当局数据/按钮态随开态同步 - _modal_flow.on_close_sync = func() -> void: - _sync_status_entry() # 关闭恢复角标可点态(无残留——同源口径) - _sync_debug_overlay() # 他层关闭复活浮窗时同步刷新(关后自动恢复语义配套) - _modal_flow.replay_click = _replay_locked_click - _modals = _modal_flow.stack # 探针 _list 登记序断言缝(ui_struct 登记序=z 序纪律断言) - # 视觉层合同装配(唯一事实源=LAYER_CONTRACT:树序+登记序同源派生——改序只改合同表) - _apply_layer_contract() - _tune_sim = TuneSimController.new(db, debug_overlay, int(db.hero.max_hp)) - func _fatal_screen(msg: String) -> void: - var l := UiKit.label(msg,UiKit.FS_SUB, true, UiKit.RED_ALERT) - l.set_anchors_preset(Control.PRESET_CENTER) - add_child(l) + MainUiBuild.fatal_screen(self, msg) # ==================== 模态 overlay 层级管理(pattern 6 / 缺陷⑤——状态机归 modal_stack.gd) ==================== @@ -499,7 +313,7 @@ func _on_confirm_accepted() -> void: match _confirm_consumer: "delete_slot": save_manager.delete_slot(_confirm_consumer_slot) - save_select_overlay.open_save_select(_save_select_data()) # 就地刷新(该槽回空态) + save_select_overlay.open_save_select(HudPresenter.save_select_data(save_manager)) # 就地刷新(该槽回空态) _confirm_consumer = "" @@ -537,21 +351,8 @@ func _resume_from_menu() -> void: return -# 冒险编排构建+接线(开新局/续玩共用——信号连接只在组合根;断点注入同口) func _wire_adventure() -> void: - run_scope.adventure = AdventureRunner.new(db, get_node(AUTOLOAD_PATH_RNG)) - run_scope.adventure.step_row.connect(_on_step_row) - run_scope.adventure.round_started.connect(_on_round_started) - run_scope.adventure.board_changed.connect(_on_board_changed) - run_scope.adventure.node_entered.connect(_on_runner_node) - run_scope.adventure.choices_presented.connect(_on_choices_presented) - run_scope.adventure.wallet_changed.connect(_on_wallet_changed) - # 三选一信号族接线 - run_scope.adventure.curio_choice_presented.connect(_bonus.on_presented) - run_scope.adventure.curio_resolved.connect(_bonus.on_resolved) - run_scope.adventure.adventure_finished.connect(_on_adventure_finished) - run_scope.adventure.level_started.connect(_on_adventure_level_started) - run_scope.adventure.set_save_manager(save_manager) # 断点承载注入(停点快照写盘;调试/模拟不经过此路径) + MainAdventureFlow._wire_adventure(self) # 冒险开局参数面构造(主菜单开新/续玩两入口共用:同调三键随开局下传。 @@ -596,204 +397,39 @@ func _start_adventure() -> void: return -# 每关开始(冒险模式):重取 runner 实例 + 层名行(L1 与单关模式同序——层名行随首战横幅后到达; -# 本回调返回后才有 choices_presented 到达——入口选路层随即弹出) -func _on_adventure_level_started(level_index: int, display_name: String) -> void: - run_scope.runner = run_scope.adventure.runner() - # 开场行传当前生效值(runner.effective_atk/effective_max_hp 与战斗面板同源: - # 表值+永久强化 flat+加算区 flat 后乘调试浮窗 pct,本回调时点 runner 已带全部加成);「含面板加成」 - # 后缀判断认任一加成源——永久强化 atk/max_hp flat 或调试浮窗 pct 或局内加算区 atk/max_hp - #(run_ledger.bonus——属性值提升入账后显示值已含加算区 flat,注记随动;armor_class/ap 不入条件: - # 开场行只显示 atk/max_hp 两值,注记指向不存在的显示轴反成失真),白板局不带 - var ov: Dictionary[Variant, Variant] = meta.hero_override() - var boosted: bool = int(ov.get("atk", 0)) > 0 or int(ov.get("max_hp", 0)) > 0 \ - or debug_overlay.atk_pct() > 0 or debug_overlay.hp_pct() > 0 \ - or int(run_scope.runner.run_ledger.bonus.get("atk", 0)) > 0 \ - or int(run_scope.runner.run_ledger.bonus.get("max_hp", 0)) > 0 - log_view.append_bb(Narrator.level_open_line(display_name, int(run_scope.adventure.master_seed), - run_scope.runner.effective_atk(), run_scope.runner.effective_max_hp(), boosted)) +func _on_adventure_level_started(level_index: int, display_name: String)-> void: + MainAdventureFlow._on_adventure_level_started(self, level_index, display_name) -func _on_runner_node(node: Dictionary, info: Dictionary) -> void: - # 冒险模式:runner 逐关更替——每次节点事件重取(单关模式幂等);须先于 board_changed 回退读面 - if run_scope.is_adventure and run_scope.adventure != null: - run_scope.runner = run_scope.adventure.runner() - var t := String(node.get("type", "")) - if t == "battle" or t == "boss": - run_scope.settlement = run_scope.runner.current_battle() # 呈现层统一读 settlement(与调试模式共用接线) - logger.attach_battle(run_scope.settlement) - run_scope.boss_active = t == "boss" - _show_board_fx_for_node(t) - log_view.append_bb(Narrator.node_banner(run_scope.runner, node, t)) - elif t == "reward": - # 奖励节点三选一(与通关三选一共享同池同权重同上限;CurioPickOverlay 共用面)。 - # 呈现编排归 curio_interact_controller.present_node_curio。 - _bonus.present_node_curio() - log_view.append_bb(Narrator.reward_line()) - elif t == "rest": - var healed: int = int(info.get("healed", 0)) - _open_modal(node_overlay) - node_overlay.show_rest(healed, int(info.get("hp", 0)), int(info.get("max_hp", 0))) - log_view.append_bb(Narrator.rest_line(healed, int(info.get("hp", 0)), int(info.get("max_hp", 0)))) - elif t == "shop": - # 商店房兑付:到达即开店——商品/货架快照与余额经 runner 只读投影喂入; - # 购买/离开归玩家意图(buy_requested/leave_pressed 接线),零自动推进。 - # pool 面 = 共享池货架三选一同构卡;legacy 面 = 固定三件(off 对照承载) - _open_modal(shop_overlay) - if run_scope.runner.is_shop_pool(): - shop_overlay.open_shelf(run_scope.runner.shop_shelf(), int(run_scope.runner.gp)) - shop_overlay.set_visit_info(int(run_scope.runner.shop_bp_visit_buys()), float(db.shop_price_a())) # 爬价规则行 - shop_overlay.set_refresh_display(run_scope.runner.shop_refresh_free_left(), run_scope.runner.shop_refresh_cost(), int(run_scope.runner.gp) >= run_scope.runner.shop_refresh_cost()) # MC-1 - else: - shop_overlay.open_shop(run_scope.runner.shop_products(), int(run_scope.runner.gp)) - shop_overlay.set_visit_info(-1, 1.0) # legacy 面无爬价行(off 对照承载) - log_view.append_bb(Narrator.shop_line(int(run_scope.runner.gp))) - logger.log_node(node, info, run_scope.runner.node_index) - _refresh_presentations() +func _on_runner_node(node: Dictionary, info: Dictionary)-> void: + MainAdventureFlow._on_runner_node(self, node, info) -# ---- 冒险模式终局面(三选一选择面 / 冒险结算屏)---- - -# 冒险结算(completed/failed/abandoned):复用 BattleEndOverlay——摘要五件事 + 钥石入账明细进文案 -func _on_adventure_finished(summary: Dictionary) -> void: - run_scope.level_active = false - var result: String = String(summary.get("result", "")) - run_scope.settlement = null - _exit_level_screen() # 终局兜底:独立屏在开态(放弃入口)也先回战斗屏呈现摘要——屏级互斥单一出口 - _sync_status_entry() # 离局熄灭状态面板入口角标(防菜单误现) - _sync_settlement_visuals() # → 全关 - HudPresenter.battle_end_feedback(stage, ap_bar, false) # 灰化/idle 回正/状态全清——HUD 单通道 - # 存档入账:settle = Boss 两档入账 + 工坊解锁 + 摘要入档 + 写盘(唯一写档点不变—— - # 调试两层不经过本处理器,天然不写档不污染生涯数据)。 - var detail: Dictionary[String, Variant] = {} - detail.assign(meta.settle_adventure(summary)) - var text := Narrator.adventure_summary_text(summary) - if not detail.is_empty(): - text += "\n" + Narrator.keystone_settle_text(detail, _bonus.item_name_lookup()) - if result == "completed": - _end_overlay.open_victory(text, "返回冒险", board_view.get_global_rect(), - "本局 GP 入账 · 合计 %d" % int(summary.get("gp_final", 0))) # GP 行(取数=summary gp_final 清账前快照);钥石明细已在 text(keystone_settle_text)——三行账 - else: - _end_overlay.open_defeat(text, "返回", board_view.get_global_rect()) - logger.log_level_end(summary) # 试玩时间线:冒险终局(摘要随载荷) - if run_scope.runner != null: - run_scope.runner.restore_hero_base() - _open_modal_after_playback(_end_overlay) - _refresh_presentations() +func _on_adventure_finished(summary: Dictionary)-> void: + MainAdventureFlow._on_adventure_finished(self, summary) func _on_battle_end_continue() -> void: - # 终局出口:返回开始界面(冒险摘要留档试玩日志;钥石在收束时入存档) - _close_modal(_end_overlay) - _end_overlay.close() - # 三选一停点挂起分支:战斗总结结算(Boss 胜利钥石增量行)确认后进三选一面 - if _bonus.resume_if_pending(): # 三选一停点在途恢复入口(curio_interact_controller) - return - # 挂起分派:战斗胜利总结结算框的「下一节点」≠ 终局——进选路页(暂存候选恢复呈现) - if _settlement_stop_pending: - _settlement_stop_pending = false - _enter_level_screen() - level_screen.set_feat_holdings(" · ".join(HudPresenter.feat_holdings_lines(db, run_scope.runner.feats_held()))) - level_screen.open_choices(run_scope.pending_candidates, run_scope.runner) - run_scope.pending_candidates = [] - _refresh_presentations() - return - # 会话收束:run_scope 整节点 queue_free()(帧末回收)——新闲时会话即建, - # 局态封印/账本/模态挂起全复位(局态字段清单见 run_scope.gd) - _debug.reset() - _begin_session(false) - debug_overlay.reset_sliders() - _start_overlay.visible = true - _refresh_presentations() + MainAdventureFlow._on_battle_end_continue(self) func _start_debug_levels() -> void: - # 守卫:调试对局与活跃冒险/结算并存不开放——并发会清写 - # settlement/编排态=状态机破坏;对局中开窗是只读浮窗,不是开局前置 - if run_scope.is_adventure or run_scope.settlement != null: - return - debug_overlay.reset_sliders() - _close_modal(debug_overlay) # 浮窗内入口发起——随开随收(对局期不可再开:门禁在 _show_debug_overlay) - _start_overlay.visible = false - log_view.clear() - log_view.append_bb(Narrator.debug_open_line(_seed_value(), 1, true)) - _begin_session(false) - if not _debug.start_debug_levels(): - _fatal_screen("战斗启动失败——见输出日志") + MainDebugFlow._start_debug_levels(self) -func _on_debug_battle_created(settlement_ref: BattleOrchestrator, _level: int, _carry: int) -> void: - # begin_battle 前接手 + 接线(signal.connect 唯一归组合根)。settlement 必须先于 begin_battle - # 就位——begin 期间发出的 board_changed 要用新板重建棋盘并触发一次性提示(缺陷①同款时序义务) - run_scope.settlement = settlement_ref - _wire_battle(settlement_ref) +func _on_debug_battle_created(settlement_ref: BattleOrchestrator, _level: int, _carry: int)-> void: + MainDebugFlow._on_debug_battle_created(self, settlement_ref, _level, _carry) -func _on_debug_battle_ready(settlement_ref: BattleOrchestrator, _level: int, _carry: int) -> void: - run_scope.settlement = settlement_ref - board_view.rebuild(run_scope.settlement.minesweeper.current_board(), run_scope.settlement.minesweeper) - _check_board_hints() - _refresh_presentations() +func _on_debug_battle_ready(settlement_ref: BattleOrchestrator, _level: int, _carry: int)-> void: + MainDebugFlow._on_debug_battle_ready(self, settlement_ref, _level, _carry) func _on_inter_next_pressed() -> void: - _inter_overlay.visible = false - if run_scope.is_adventure: - # 冒险模式:大奖励屏确认 → 进下一关(AdventureRunner 序列推进——零自动推进的冒险级停点) - if run_scope.adventure != null and not run_scope.adventure.continue_to_next_level(): - _fatal_screen("关卡推进失败——见输出日志") - return - if not _debug.next_layer(): - _fatal_screen("战斗启动失败——见输出日志") + MainAdventureFlow._on_inter_next_pressed(self) -# 加成生效值供给(debug_session 注入 Callable——每次从浮窗百分比现算,经 encounter 传参零表写; -# 禁表写路径(双写叠加隐患源头); -# 合成式单点在 UiKit.scaled——与调试浮窗生效值预览共用,本类不持有同式私有实现) -func _debug_hero_override() -> Dictionary: - return {"atk": UiKit.scaled(int(db.hero.atk), debug_overlay.atk_pct()), - "max_hp": UiKit.scaled(int(db.hero.max_hp), debug_overlay.hp_pct())} - - -# 奖励徽章身份解析(组合根守卫:仅挖掘期 Active 相位内查询——地图战斗资源 Ready 域外零读、零诊断噪音; -# 相位取数经 presentation_snapshot 成文面——禁 settlement.action_economy 链式穿透) -func _identity_lookup(c: Vector2i) -> Variant: - if run_scope.settlement == null or run_scope.settlement.minesweeper.current_board() == null: - return null - if String(run_scope.settlement.presentation_snapshot().get("phase", "")) != ActionEconomy.PHASE_ACTIVE: - return null - return run_scope.settlement.query_reward_identity(c) - - -# 地形变异 数字遮蔽解析(信息型 hide_number——格带该效果变异即遮蔽;读取方按格子变异数据行动。 -# 变异表直读本根 db——settlement.db 即组合根注入的同一 ConfigDB 实例(AdventureRunner/DebugSession -# 构造传参),免 settlement 内部链式穿透) -func _variant_mask_lookup(c: Vector2i) -> bool: - if run_scope.settlement == null: - return false - var b = run_scope.settlement.minesweeper.current_board() - if b == null: - return false - for vid in b.variant_ids(c): - var vrow: Dictionary[Variant, Variant] = db.variation.row_by_id(String(vid)) - if String(vrow.get("effect", "")) == "hide_number": - return true - return false - - -# 地形变异 角标解析(伤害型变异未开格视觉标记;读取方按格子变异数据行动,规则本体零特判。 -# 策略纹理非数值墙:标记=玩家绕行/承担的决策输入,难度本体仍住数值包络。变异表直读本根 db) -func _variant_mark_lookup(c: Vector2i) -> String: - if run_scope.settlement == null: - return "" - var b = run_scope.settlement.minesweeper.current_board() - if b == null: - return "" - for vid in b.variant_ids(c): - var vrow: Dictionary[Variant, Variant] = db.variation.row_by_id(String(vid)) - if String(vrow.get("effect", "")) == "flat_damage": - return "flat_damage" - return "" +# 加成生效值供给 / 奖励徽章身份解析 / 地形变异两种解析 —— 见 board_resolvers.gd(有态绑定族判离) func _show_board_fx_for_node(node_type: String = "") -> void: @@ -818,13 +454,13 @@ func _on_battle_finished(payload: Dictionary) -> void: "level_end": _end_overlay.open_victory(Narrator.battle_end_text("victory", true), "返回", board_view.get_global_rect(), _gp_summary_text(payload), - "钥石 · 累计 %d" % run_scope.runner.keystone, _bonus_summary_text(), + "钥石 · 累计 %d" % run_scope.runner.keystone, HudPresenter.bonus_summary_text(run_scope.runner), int(payload.get("gp_dropped", 0)), int(run_scope.runner.gp)) "standalone_end": if result == "victory": _end_overlay.open_victory(Narrator.battle_end_text("victory", false), "回到遭遇选择", board_view.get_global_rect(), _gp_summary_text(payload), - "钥石 · 累计 %d" % run_scope.runner.keystone, _bonus_summary_text(), + "钥石 · 累计 %d" % run_scope.runner.keystone, HudPresenter.bonus_summary_text(run_scope.runner), int(payload.get("gp_dropped", 0)), int(run_scope.runner.gp)) else: _end_overlay.open_defeat(Narrator.battle_end_text("defeat", false), @@ -839,27 +475,9 @@ func _on_battle_finished(payload: Dictionary) -> void: # runner 缺位守卫(续玩停点恢复分支:adventure_runner # resume_adventure 的 bonus_pending 分支在 level_started 之前同步 curio_choice_presented.emit # 〔resume 提前 return〕,本 lambda 求值时 runner 尚未赋值;合计改读断点快照 gp 兜底——恢复源快照 -# 此刻必在档,语义=续玩恢复面的合计即存档合计;与 _bonus_summary_text 同款守卫式) +# 此刻必在档,语义=续玩恢复面的合计即存档合计;承接体见 HudPresenter.gp_summary_text) func _gp_summary_text(payload: Dictionary) -> String: - if run_scope.runner == null: - var bp: Dictionary[Variant, Variant] = save_manager.breakpoint_snapshot() - return "战利品 GP +%d · 合计 %d" % [int(payload.get("gp_dropped", 0)), int(bp.get("gp", 0))] - return "战利品 GP +%d · 合计 %d" % [int(payload.get("gp_dropped", 0)), int(run_scope.runner.gp)] - - -# 结算三行账·本关加成行装配(呈现只读投影——runner.bonus 统一加算区同源;零加成=行隐藏) -func _bonus_summary_text() -> String: - if run_scope.runner == null: - return "" - var b: Dictionary[Variant, Variant] = run_scope.runner.run_ledger.bonus # 加算区住 RunLedger - var parts: Array[String] = [] - if int(b.get("atk", 0)) != 0: - parts.append("攻 %+d" % int(b.get("atk", 0))) - if int(b.get("max_hp", 0)) != 0: - parts.append("上限 %+d" % int(b.get("max_hp", 0))) - if int(b.get("armor_class", 0)) != 0: - parts.append("AC %+d" % int(b.get("armor_class", 0))) - return "本关加成 " + " · ".join(parts) + return HudPresenter.gp_summary_text(payload, run_scope.runner, save_manager) # ==================== core↔view 接线(战斗信号统一走这里)==================== @@ -978,46 +596,15 @@ func _show_hint(text: String, hint_id: String) -> void: # ---- 节点覆盖层"继续"(奖励/恢复点兑付确认)与节点图选路覆盖层 ---- func _on_node_continue() -> void: - _close_modal(node_overlay) - if run_scope.runner != null: - # 确认 ≠ 推进——兑付确认后经 choices_presented 弹出选路口 - run_scope.runner.continue_after_node() + MainAdventureFlow._on_node_continue(self) -func _on_choices_presented(candidates: Array) -> void: - # 零自动推进的停点呈现:整幅节点图 + 图上点击选路(战斗胜利/兑付确认统一到达此处) - if run_scope.runner == null: - return - log_view.append_bb(Narrator.route_line(candidates, run_scope.runner)) - # 战斗胜利总结结算(battle.md OQ14):战斗胜利停点先弹 BattleEndOverlay - # 胜利形态(金蜡封+GP 增量+总量+判定行+「下一节点」+明细回看),确认后进选路页。判据 = 战斗节点 - # 的选路停点且战斗已收场(battle 收尾分支 _settlement 置 null 先于本信号;入口/兑付/商店停点 - # 的当前节点非 battle 型——三态天然分流)。Boss 胜/失败走 level_finished 另路不经此处。 - if run_scope.is_adventure and run_scope.runner.current_battle() == null \ - and String(run_scope.runner.current_node().get("type", "")) == "battle" and not candidates.is_empty(): - _settlement_stop_pending = true - run_scope.pending_candidates = candidates.duplicate() - _end_overlay.open_victory(Narrator.battle_end_text("victory", false), "下一节点", - board_view.get_global_rect(), - "战利品 GP +%d · 合计 %d" % [run_scope.last_gain_amount, int(run_scope.runner.gp)]) - _open_modal_after_playback(_end_overlay) - _refresh_presentations() - return - _enter_level_screen() - level_screen.set_feat_holdings(" · ".join(HudPresenter.feat_holdings_lines(db, run_scope.runner.feats_held()))) - level_screen.open_choices(candidates, run_scope.runner) - _refresh_presentations() +func _on_choices_presented(candidates: Array)-> void: + MainAdventureFlow._on_choices_presented(self, candidates) -func _on_route_chosen(node_id: int) -> void: - # 视图已按节点类型定去留——战斗/Boss 出屏(visible=false,此处收栈 - # 记录一致性+回战斗屏);奖励/恢复/商店本屏保持激活(兑付覆盖层由节点事件域内弹出,模态栈 - # 打开低层时自动隐藏本屏、关闭自动恢复) - if not level_screen.visible: - _close_modal(level_screen) - _exit_level_screen() - if run_scope.runner != null and not run_scope.runner.pick_next_node(node_id): - push_error("[组合根] 选路被拒(编号 %d 不在候选)" % node_id) +func _on_route_chosen(node_id: int)-> void: + MainAdventureFlow._on_route_chosen(self, node_id) # ---- 独立屏屏级互斥(单一出口:进屏隐战斗屏 / 出屏显战斗屏)---- @@ -1025,229 +612,90 @@ func _on_route_chosen(node_id: int) -> void: # 关闭自动恢复本屏(遮挡语义复用),战斗屏不在此期间回显。 func _enter_level_screen() -> void: - run_scope.level_screen_active = true - _battle_root.visible = false - _open_modal(level_screen) - _sync_status_entry() # 屏级互斥切换同步(_open_modal 单口不识屏级状态) + MainAdventureFlow._enter_level_screen(self) func _exit_level_screen() -> void: - run_scope.level_screen_active = false - _battle_root.visible = true - if level_screen.visible: - _close_modal(level_screen) - _sync_status_entry() # 同上 + MainAdventureFlow._exit_level_screen(self) func _on_abandon_requested() -> void: - # 放弃入口(冒险结构.md 已放开「MVP 无界面入口」后置条款; - # 入口挂选路停点 = 天然非战斗期,拒绝分支仅防御在案):受理 → 冒险放弃结算(同失败型, - # 已获大奖励不追回)→ adventure_finished 同步到达 → 终局面收层 - if not run_scope.is_adventure or run_scope.adventure == null: - return - if not run_scope.adventure.abandon(): - logger.log_ux("ux_reject", {"source": "abandon", "sub_reason": "battle_in_flight"}) - board_zone.toast.show_toast("战斗进行中——无法放弃", "warn") + MainAdventureFlow._on_abandon_requested(self) -# ---- 营地覆盖层与钥石账本(写调用唯一归组合根)---- - # ---- 最小设置面(入口/开合仿营地先例:开始层互斥;生效与持久化归 SettingsStore)---- func _open_settings() -> void: - _start_overlay.visible = false - _open_modal(settings_overlay) # 模态栈单口 + MainOverlayFlow._open_settings(self) func _on_settings_closed() -> void: - _close_modal(settings_overlay) # 模态栈单口 - _start_overlay.visible = true + MainOverlayFlow._on_settings_closed(self) -# ---- 局内状态面板·只读(入口角标在两屏装配段;开合与数据喂入归组合根)---- - -# 状态面板快照投影(数据只读同源:每次开层实时读访问器现值拼装—— -# 快照仅服务单次开层生命周期(open_status 重建各行),跨开层零持有;零写调用。 -# 八键换代(迭代 36 卡③):跨关解锁概览退役(营地/生涯承载)——只留本局信息; -# ledger 直取下标(键缺失=致命,禁 .get 缺省留门——fail-fast 红线);禁 null 守卫软回退 -# (两入口开层时 runner 恒在役——状态面板卡 F3 证据链) func _status_data() -> Dictionary: - return { - "balance": meta.keystone_balance(), - "hero_hp": int(run_scope.runner.hero_hp), # LevelRunner 属性面(_ledger 直取下标——键缺失=致命) - "max_hp": run_scope.runner.effective_max_hp(), - "atk": run_scope.runner.effective_atk(), - "ac": run_scope.runner.effective_ac(), - "gp": int(run_scope.runner.gp), - "curios": _status_curios(), - "feats": HudPresenter.feat_holdings_lines(db, run_scope.runner.feats_held()), - } - - -# 秘宝行映射(inventory_projection 现役投影逐行取 name/used 两键——受理序沿库存投影; -# 禁第二份投影:件名换算已在 inventory_projection 收口,此处只做键裁剪) -func _status_curios() -> Array: - var out: Array[Variant] = [] - for row in (_treasure_ui.inventory_projection() as Array): - var r: Dictionary[Variant, Variant] = row - out.append({"name": String(r.get("name", "")), "used": bool(r.get("used", false))}) - return out + return MainOverlayFlow._status_data(self) func _open_status_panel() -> void: - _open_modal(status_panel) # 模态栈单口(开层前排空排播队列——既有层同款行为,目检确认项) - status_panel.open_status(_status_data()) + MainOverlayFlow._open_status_panel(self) func _on_status_closed() -> void: - _close_modal(status_panel) # 模态栈单口 + MainOverlayFlow._on_status_closed(self) # ---- 生涯页·只读(入口=开始层生涯钮;开合仿设置面先例:开始层互斥; # 数据快照随开随建零写通路,页内零跳转)---- func _open_career() -> void: - _start_overlay.visible = false - _open_modal(career_overlay) # 模态栈单口 - career_overlay.open_career(_career_data()) + MainOverlayFlow._open_career(self) func _on_career_closed() -> void: - _close_modal(career_overlay) # 模态栈单口 - _start_overlay.visible = true + MainOverlayFlow._on_career_closed(self) -# 生涯页快照投影(只读同源——每次开层实时读 SaveManager 计数访问器 + MetaProgression 访问器 -# 现值拼装(状态面板 _status_data 同构);快照仅服务单次开层生命周期,跨开层零持有;零写调用。 -# 生涯页只统计终身计数——不统计天赋树与账号成长态, -# 故 forge_levels_total/workshop/tree 三投影不在本投影内) -func _career_data() -> Dictionary: - var clears: Array[Dictionary] = [] - var boss_first := 0 - for i in db.level_count(): - var li: int = i + 1 - clears.append({"display_name": String(db.level_row(li).get("display_name", "")), - "count": save_manager.clear_count(li)}) - if save_manager.boss_first_kill_marked(li): - boss_first += 1 - return { - "total": save_manager.adventure_total(), - "cleared": save_manager.adventure_cleared(), - "clears": clears, - "boss_first_kills": boss_first, - "boss_first_total": db.level_count(), - "boss_kills": save_manager.boss_kill_count(), - "keystone": meta.keystone_balance(), - } - - -# ==================== 选档界面(营地交互重做卡③)==================== - -# 开层(模态栈单口;与开始层互斥=career 同款先例)。「进入存档」钮归卡② mn3——本函数为 -# 该钮的唯一落点(卡③ 批内探针函数直调=中间态,实现报告「已知中间态」登记)。 func _open_save_select() -> void: - _start_overlay.visible = false - _open_modal(save_select_overlay) # 模态栈单口 - save_select_overlay.open_save_select(_save_select_data()) + MainOverlayFlow._open_save_select(self) func _on_save_select_closed() -> void: - _close_modal(save_select_overlay) # 模态栈单口 - _start_overlay.visible = true + MainOverlayFlow._on_save_select_closed(self) -# 槽数据快照(只读同源——每次开层实时经卡① 窥视 API 逐槽取数拼装;零写调用)。 -# occupied = has_file ∧ readable:坏槽/高版本槽按空槽呈现(Q2 裁甲——进档/新档均经卡① 兜底链接管); -# 摘要仅三项(裁决 4),has_file 只是空/有档操作集二分判定来处(非摘要项)。 -func _save_select_data() -> Dictionary: - var slots: Array[Dictionary] = [] - for i in range(1, save_manager.SLOT_COUNT + 1): - var p := save_manager.peek_slot_summary(i) # 推断 Dictionary(窥视返回合同——裸注解触 R1、参数化赋值运行期严格拒) - slots.append({"slot": i, - "occupied": bool(p.get("has_file", false)) and bool(p.get("readable", false)), - "keystone": int(p.get("keystone", 0)), - "cleared": int(p.get("cleared", 0)), - "total": int(p.get("total", 0))}) - return {"slots": slots} +func _on_save_select_new(slot: int)-> void: + MainOverlayFlow._on_save_select_new(self, slot) -# 空槽新档链(Q3 裁乙暂行落点=现役 CampOverlay;卡④ 重立卡后改挂场景图):卡① 创建=激活=白板落盘, -# 落点开层。动线序(禁先关后开):先落点开层(开始层保持隐藏),选档层被模态栈遮挡隐藏、不关闭。 -func _on_save_select_new(slot: int) -> void: - save_manager.create_slot(slot) - _camp_pres.open_camp() +func _on_save_select_load(slot: int)-> void: + MainOverlayFlow._on_save_select_load(self, slot) -# 有档槽进档链(契约 8 进档即激活):卡① 激活+读盘(兜底/拒读链按槽成立)→暂行落点 CampOverlay。 -func _on_save_select_load(slot: int) -> void: - save_manager.activate_slot(slot) - _camp_pres.open_camp() +func _on_save_select_delete(slot: int)-> void: + MainOverlayFlow._on_save_select_delete(self, slot) -# 删档链(契约 3):第一次点击只出确认窗(消费方编排——open 前设消费方=delete_slot), -# 确认才删、取消不删;禁长按删/一次点击即删/撤销式软删。 -func _on_save_select_delete(slot: int) -> void: - _confirm_consumer = "delete_slot" - _confirm_consumer_slot = slot - confirm_dialog.open("删除存档位 %d?删除后该档不可恢复。" % slot, "删除", "取消") - - -# 入口角标点击(两屏同款处理器):P1 守卫兜底——其他模态开着即拒开(常态防线=角标置灰, -# 探针 pressed.emit 绕过 disabled 时由此兜住;地图屏自身的登记开态不计——has_open_excluding) func _on_status_badge_pressed() -> void: - var map_on: bool = level_screen != null and level_screen.visible - # P1 守卫兜底——其他模态开着即拒开(常态防线=角标置灰,探针 pressed.emit 绕过 disabled 时由此兜住; - # 地图屏自身的登记开态不计;判据排除调试浮窗:对局中浮窗开着时状态角标仍可点) - if (map_on and _modal_flow.badge_greyed_map()) or (not map_on and _modal_flow.badge_greyed_battle()): - return - _open_status_panel() + MainOverlayFlow._on_status_badge_pressed(self) -# 入口角标可见性/置灰同步(P1 丁+守卫——单一同步点):战斗屏角标=对局内且地图屏未激活; -# 地图屏角标=随屏;置灰=登记册上除地图屏自身外任一层开着(防「看得见点不着」态)。 -# 调用面=模态开合单口与屏级互斥口(凡影响开态/屏态的路径必经其一)。 func _sync_status_entry() -> void: - if status_panel == null or _modals == null or board_zone == null or level_screen == null: - return - var map_on: bool = level_screen.visible - var battle_live: bool = run_scope.level_active or run_scope.settlement != null # 调试两层无 level_active——settlement 判据补位 - board_zone.status_badge.visible = run_scope.level_active and not map_on - level_screen.status_badge.visible = map_on - # 置灰判据为排除式(调试浮窗开态不计入:对局中浮窗开着时角标仍可点) - board_zone.status_badge.disabled = _modal_flow.badge_greyed_battle() # 互斥域谓词 - level_screen.status_badge.disabled = _modal_flow.badge_greyed_map() - # 状态角标判据(同上——重复语句在案): - # 战斗屏=对局内(冒险或调试两层)且地图屏未激活;地图屏=随屏;开始菜单零角标 - board_zone.status_badge.visible = run_scope.level_active and not map_on - level_screen.status_badge.visible = map_on - board_zone.status_badge.disabled = _modal_flow.badge_greyed_battle() # 互斥域谓词 - level_screen.status_badge.disabled = _modal_flow.badge_greyed_map() - # 调试浮层角标(全局浮层任何 UI 不遮挡——可见性不随场景变,只抬 offset - # 避让:地图屏底栏 85px 常驻,角标抬至底栏上方 16px;其余场景右下角 16px 贴底) - debug_badge.offset_bottom = -101.0 if map_on else -16.0 + MainOverlayFlow._sync_status_entry(self) # ---- 探针缝保缝(click/ui_probe 营地段直呼组合根面;实现归 camp_shop_presenter.gd)---- func _on_camp_closed() -> void: - _camp_pres.on_camp_closed() + MainOverlayFlow._on_camp_closed(self) func _camp_data() -> Dictionary: - return _camp_pres.camp_data() + return MainOverlayFlow._camp_data(self) -func _on_wallet_changed(event: String, gp: int, extra: Dictionary) -> void: - # wallet 行族转发(到账/购买/清零——试玩日志验收取数面)+ 常驻余额与在开商店覆盖层重刷 - logger.log_wallet(event, gp, extra) - if String(event) == "gain": - run_scope.last_gain_amount = int(extra.get("amount", 0)) # GP 增量取数(每战恰一笔 gain) - if shop_overlay.visible and run_scope.runner != null: - if run_scope.runner.is_shop_pool(): - shop_overlay.refresh_shelf_state(int(run_scope.runner.gp)) # pool 面余额联动(货架内容仅买后重抽变) - else: - shop_overlay.refresh_state(run_scope.runner.shop_products(), int(run_scope.runner.gp)) - _refresh_presentations() +func _on_wallet_changed(event: String, gp: int, extra: Dictionary)-> void: + MainOverlayFlow._on_wallet_changed(self, event, gp, extra) # ==================== 玩家意图 → 核心写调用(唯一出口)==================== @@ -1371,7 +819,8 @@ func _sync_settlement_visuals() -> void: if settling and not run_scope.was_settling: settlement_fx.play_round_sweep(board_view.get_global_rect()) # 结算轮光带——时序敏感演出触发,例外留在组合根 run_scope.was_settling = settling - HudPresenter.sync_settlement_visuals(stage, board_view, log_view, settling, _phase_text()) + HudPresenter.sync_settlement_visuals(stage, board_view, log_view, settling, + HudPresenter.phase_text(run_scope.settlement, run_scope.runner, run_scope.boss_active, db)) func _is_settling() -> bool: @@ -1388,80 +837,37 @@ func _is_settling() -> bool: return not _row_queue.is_empty() -func _phase_text() -> String: - # 文案装配归 narrator(settlement 缺位 = 非战斗态——组合根模式状态守卫;相位取数经 - # presentation_snapshot 成文面——禁 settlement.action_economy 链式穿透) - if run_scope.settlement == null: - return "第 — 轮 · —" - return Narrator.phase_text(int(run_scope.settlement.round_index), String(run_scope.settlement.battle_result), - String(run_scope.settlement.presentation_snapshot().get("phase", "")) == ActionEconomy.PHASE_WAITING, - run_scope.boss_active, Narrator.boss_name(run_scope.runner, db)) +# 相位行 —— 承接体见 HudPresenter.phase_text(caller 直接改引) # ==================== B 区明细带 / 图例(模态入口)==================== func _on_legend_toggled() -> void: - if legend_overlay.is_open(): - _close_modal(legend_overlay) - else: - _open_modal(legend_overlay) + MainOverlayFlow._on_legend_toggled(self) # ==================== 诊断转储(卡死时点击——全量核心状态 + 环形缓冲)==================== func _on_diagnose_pressed() -> void: - # 本局种子现值走 _current_master_seed 只读单点 - var ctx := {"mode": "level" if run_scope.level_active else "debug", "seed": _current_master_seed()} - if run_scope.level_active and run_scope.runner != null: - ctx["runner"] = {"node_index": run_scope.runner.node_index, "level_result": String(run_scope.runner.level_result), - "hero_hp": run_scope.runner.hero_hp, "settlement_is_null": run_scope.runner.current_battle() == null} - if run_scope.settlement != null: - ctx["settlement"] = run_scope.settlement.diagnostic_snapshot() # 诊断观测面(私有字段零直读) - var path: String = logger.dump_diagnosis(ctx) - board_zone.toast.show_toast("已转储诊断: " + path, "info") + MainDebugFlow._on_diagnose_pressed(self) # ==================== 调试浮窗(开合/应用/模拟编排由组合根承接)==================== func _show_debug_overlay() -> void: - # 门禁:对局中可开;本函数仍是唯一开窗入口,门禁逻辑收函数内: - # 棋盘输入锁/状态角标判据排除浮窗, - # 遮挡语义=ModalStack 登记制(他模态开时暂隐、关后自动恢复),此处零场景限制 - if debug_overlay == null: - return - _open_modal(debug_overlay) - _sync_debug_overlay() + MainDebugFlow._show_debug_overlay(self) -# 调试浮窗角标点击(两屏同款处理器——与状态角标同形制守卫;仍收口 _show_debug_overlay 单口) func _on_debug_badge_pressed() -> void: - var map_on: bool = level_screen != null and level_screen.visible - if (map_on and _modal_flow.badge_greyed_map()) or (not map_on and _modal_flow.badge_greyed_battle()): - return - _show_debug_overlay() + MainDebugFlow._on_debug_badge_pressed(self) -# 浮窗上下文同步(当局数据刷新+对局中按钮禁用单点):调用面=开窗口与模态开合 -# 单口(凡影响开态/对局态的路径必经其一——与 _sync_status_entry 同款纪律) func _sync_debug_overlay() -> void: - if debug_overlay == null: - return - debug_overlay.set_context_locked(run_scope.settlement != null or run_scope.is_adventure) # 对局中批量模拟/两层调试入口禁用(长程异步模拟与活跃结算并存态不开放) - debug_overlay.update_run_data(_debug_run_data()) # 当局数据只读区块喂入(开窗+模态开合信号驱动) + MainDebugFlow._sync_debug_overlay(self) -# 当局数据六项只读投影(零直读内部状态、零写通路):种子=adventure.master_seed 现值 -# (非 _seed_value() 重算);调试两层/非对局无冒险对象=RNG 服务 master_seed 现值(只读出口, -# 未播种 -1 显「—」);关卡/节点/HP/GP=runner 成文出口、钥石=adventure 冒险账本(LevelRunner 无此域); -# 缺位=「—」占位。零动效(纯标签重查)。 func _debug_run_data() -> Dictionary: - var r = run_scope.adventure.runner() if run_scope.adventure != null else null - var seed_now: int = _current_master_seed() - var seed_txt: String = str(seed_now) if seed_now >= 0 else "—" - if r == null: - return {"seed": seed_txt, "level": "—", "node": "—", "hp": "—", "gp": "—", "keystone": "—"} - return {"seed": seed_txt, "level": int(run_scope.adventure.level_index), "node": int(r.node_index), - "hp": "%d/%d" % [int(r.hero_hp), int(r.effective_max_hp())], "gp": int(r.gp), "keystone": int(run_scope.adventure.keystone)} + return MainDebugFlow._debug_run_data(self) # 本局种子现值(只读单点——当局数据区块与诊断转储共用):冒险=adventure.master_seed 访问器现值; @@ -1473,20 +879,15 @@ func _current_master_seed() -> int: func _close_debug_overlay() -> void: - _close_modal(debug_overlay) # 模态栈单口——开始层全程未隐藏,无需恢复 + MainDebugFlow._close_debug_overlay(self) -func _on_tune_apply(atk_pct: int, hp_pct: int) -> void: - # 加成无持久态可"落"——各战斗开局经 _debug_hero_override/runner 现算生效(应用=关闭浮窗) - _close_debug_overlay() +func _on_tune_apply(atk_pct: int, hp_pct: int)-> void: + MainDebugFlow._on_tune_apply(self, atk_pct, hp_pct) -func _on_tune_simulate(n: int) -> void: - # 批量模拟编排 = 控制器承接(tune_sim_controller.gd);E4 后无表态需复位——模拟走 runner 覆盖参数。 - # 对局中禁用兜底(长程异步模拟与活跃结算并存态不开放;探针 emit 绕过 disabled 时由此守住) - if run_scope.settlement != null: - return - _tune_sim.run(n) +func _on_tune_simulate(n: int)-> void: + MainDebugFlow._on_tune_simulate(self, n) # ==================== 工具 ==================== diff --git a/game/presentation/main_adventure_flow.gd b/game/presentation/main_adventure_flow.gd new file mode 100644 index 00000000..e6aa68b9 --- /dev/null +++ b/game/presentation/main_adventure_flow.gd @@ -0,0 +1,213 @@ +## VERTICAL SLICE - NOT FOR PRODUCTION +## 组合根冒险流程承接件:菜单开局/续玩之后的冒险编排接线、节点事件分发、终局面与选路屏开合。 +## 本件是 main.gd 同名函数的逐字搬运(成员访问改 root 前缀)——main 侧留同名薄委托,探针 main.X 路径零改动。 +## 局态仍全局住在 run_scope 节点:本件零成员态、零信号连接新增(连接语句随原段搬运)。 +extends RefCounted + +# 冒险编排构建+接线(开新局/续玩共用——信号连接只在组合根;断点注入同口) +static func _wire_adventure(root: Variant) -> void: + root.run_scope.adventure = AdventureRunner.new(root.db, root.get_node(root.AUTOLOAD_PATH_RNG)) + root.run_scope.adventure.step_row.connect(root._on_step_row) + root.run_scope.adventure.round_started.connect(root._on_round_started) + root.run_scope.adventure.board_changed.connect(root._on_board_changed) + root.run_scope.adventure.node_entered.connect(root._on_runner_node) + root.run_scope.adventure.choices_presented.connect(root._on_choices_presented) + root.run_scope.adventure.wallet_changed.connect(root._on_wallet_changed) + # 三选一信号族接线 + root.run_scope.adventure.curio_choice_presented.connect(root._bonus.on_presented) + root.run_scope.adventure.curio_resolved.connect(root._bonus.on_resolved) + root.run_scope.adventure.adventure_finished.connect(root._on_adventure_finished) + root.run_scope.adventure.level_started.connect(root._on_adventure_level_started) + root.run_scope.adventure.set_save_manager(root.save_manager) # 断点承载注入(停点快照写盘;调试/模拟不经过此路径) + + + +# 每关开始(冒险模式):重取 runner 实例 + 层名行(L1 与单关模式同序——层名行随首战横幅后到达; +# 本回调返回后才有 choices_presented 到达——入口选路层随即弹出) +static func _on_adventure_level_started(root: Variant, level_index: int, display_name: String) -> void: + root.run_scope.runner = root.run_scope.adventure.runner() + # 开场行传当前生效值(runner.effective_atk/effective_max_hp 与战斗面板同源: + # 表值+永久强化 flat+加算区 flat 后乘调试浮窗 pct,本回调时点 runner 已带全部加成);「含面板加成」 + # 后缀判断认任一加成源——永久强化 atk/max_hp flat 或调试浮窗 pct 或局内加算区 atk/max_hp + #(run_ledger.bonus——属性值提升入账后显示值已含加算区 flat,注记随动;armor_class/ap 不入条件: + # 开场行只显示 atk/max_hp 两值,注记指向不存在的显示轴反成失真),白板局不带 + var ov: Dictionary[Variant, Variant] = root.meta.hero_override() + var boosted: bool = int(ov.get("atk", 0)) > 0 or int(ov.get("max_hp", 0)) > 0 \ + or root.debug_overlay.atk_pct() > 0 or root.debug_overlay.hp_pct() > 0 \ + or int(root.run_scope.runner.run_ledger.bonus.get("atk", 0)) > 0 \ + or int(root.run_scope.runner.run_ledger.bonus.get("max_hp", 0)) > 0 + root.log_view.append_bb(root.Narrator.level_open_line(display_name, int(root.run_scope.adventure.master_seed), + root.run_scope.runner.effective_atk(), root.run_scope.runner.effective_max_hp(), boosted)) + +static func _on_runner_node(root: Variant, node: Dictionary, info: Dictionary) -> void: + # 冒险模式:runner 逐关更替——每次节点事件重取(单关模式幂等);须先于 board_changed 回退读面 + if root.run_scope.is_adventure and root.run_scope.adventure != null: + root.run_scope.runner = root.run_scope.adventure.runner() + var t := String(node.get("type", "")) + if t == "battle" or t == "boss": + root.run_scope.settlement = root.run_scope.runner.current_battle() # 呈现层统一读 settlement(与调试模式共用接线) + root.logger.attach_battle(root.run_scope.settlement) + root.run_scope.boss_active = t == "boss" + root._show_board_fx_for_node(t) + root.log_view.append_bb(root.Narrator.node_banner(root.run_scope.runner, node, t)) + elif t == "reward": + # 奖励节点三选一(与通关三选一共享同池同权重同上限;CurioPickOverlay 共用面)。 + # 呈现编排归 curio_interact_controller.present_node_curio。 + root._bonus.present_node_curio() + root.log_view.append_bb(root.Narrator.reward_line()) + elif t == "rest": + var healed: int = int(info.get("healed", 0)) + root._open_modal(root.node_overlay) + root.node_overlay.show_rest(healed, int(info.get("hp", 0)), int(info.get("max_hp", 0))) + root.log_view.append_bb(root.Narrator.rest_line(healed, int(info.get("hp", 0)), int(info.get("max_hp", 0)))) + elif t == "shop": + # 商店房兑付:到达即开店——商品/货架快照与余额经 runner 只读投影喂入; + # 购买/离开归玩家意图(buy_requested/leave_pressed 接线),零自动推进。 + # pool 面 = 共享池货架三选一同构卡;legacy 面 = 固定三件(off 对照承载) + root._open_modal(root.shop_overlay) + if root.run_scope.runner.is_shop_pool(): + root.shop_overlay.open_shelf(root.run_scope.runner.shop_shelf(), int(root.run_scope.runner.gp)) + root.shop_overlay.set_visit_info(int(root.run_scope.runner.shop_bp_visit_buys()), float(root.db.shop_price_a())) # 爬价规则行 + root.shop_overlay.set_refresh_display(root.run_scope.runner.shop_refresh_free_left(), root.run_scope.runner.shop_refresh_cost(), int(root.run_scope.runner.gp) >= root.run_scope.runner.shop_refresh_cost()) # MC-1 + else: + root.shop_overlay.open_shop(root.run_scope.runner.shop_products(), int(root.run_scope.runner.gp)) + root.shop_overlay.set_visit_info(-1, 1.0) # legacy 面无爬价行(off 对照承载) + root.log_view.append_bb(root.Narrator.shop_line(int(root.run_scope.runner.gp))) + root.logger.log_node(node, info, root.run_scope.runner.node_index) + root._refresh_presentations() + + +# ---- 冒险模式终局面(三选一选择面 / 冒险结算屏)---- + + +# 冒险结算(completed/failed/abandoned):复用 BattleEndOverlay——摘要五件事 + 钥石入账明细进文案 +static func _on_adventure_finished(root: Variant, summary: Dictionary) -> void: + root.run_scope.level_active = false + var result: String = String(summary.get("result", "")) + root.run_scope.settlement = null + _exit_level_screen(root) # 终局兜底:独立屏在开态(放弃入口)也先回战斗屏呈现摘要——屏级互斥单一出口 + root._sync_status_entry() # 离局熄灭状态面板入口角标(防菜单误现) + root._sync_settlement_visuals() # → 全关 + HudPresenter.battle_end_feedback(root.stage, root.ap_bar, false) # 灰化/idle 回正/状态全清——HUD 单通道 + # 存档入账:settle = Boss 两档入账 + 工坊解锁 + 摘要入档 + 写盘(唯一写档点不变—— + # 调试两层不经过本处理器,天然不写档不污染生涯数据)。 + var detail: Dictionary[String, Variant] = {} + detail.assign(root.meta.settle_adventure(summary)) + var text: String = root.Narrator.adventure_summary_text(summary) + if not detail.is_empty(): + text += "\n" + root.Narrator.keystone_settle_text(detail, root._bonus.item_name_lookup()) + if result == "completed": + root._end_overlay.open_victory(text, "返回冒险", root.board_view.get_global_rect(), + "本局 GP 入账 · 合计 %d" % int(summary.get("gp_final", 0))) # GP 行(取数=summary gp_final 清账前快照);钥石明细已在 text(keystone_settle_text)——三行账 + else: + root._end_overlay.open_defeat(text, "返回", root.board_view.get_global_rect()) + root.logger.log_level_end(summary) # 试玩时间线:冒险终局(摘要随载荷) + if root.run_scope.runner != null: + root.run_scope.runner.restore_hero_base() + root._open_modal_after_playback(root._end_overlay) + root._refresh_presentations() + +static func _on_battle_end_continue(root: Variant) -> void: + # 终局出口:返回开始界面(冒险摘要留档试玩日志;钥石在收束时入存档) + root._close_modal(root._end_overlay) + root._end_overlay.close() + # 三选一停点挂起分支:战斗总结结算(Boss 胜利钥石增量行)确认后进三选一面 + if root._bonus.resume_if_pending(): # 三选一停点在途恢复入口(curio_interact_controller) + return + # 挂起分派:战斗胜利总结结算框的「下一节点」≠ 终局——进选路页(暂存候选恢复呈现) + if root._settlement_stop_pending: + root._settlement_stop_pending = false + _enter_level_screen(root) + root.level_screen.set_feat_holdings(" · ".join(HudPresenter.feat_holdings_lines(root.db, root.run_scope.runner.feats_held()))) + root.level_screen.open_choices(root.run_scope.pending_candidates, root.run_scope.runner) + root.run_scope.pending_candidates = [] + root._refresh_presentations() + return + # 会话收束:run_scope 整节点 queue_free()(帧末回收)——新闲时会话即建, + # 局态封印/账本/模态挂起全复位(局态字段清单见 run_scope.gd) + root._debug.reset() + root._begin_session(false) + root.debug_overlay.reset_sliders() + root._start_overlay.visible = true + root._refresh_presentations() + +static func _on_inter_next_pressed(root: Variant) -> void: + root._inter_overlay.visible = false + if root.run_scope.is_adventure: + # 冒险模式:大奖励屏确认 → 进下一关(AdventureRunner 序列推进——零自动推进的冒险级停点) + if root.run_scope.adventure != null and not root.run_scope.adventure.continue_to_next_level(): + root._fatal_screen("关卡推进失败——见输出日志") + return + if not root._debug.next_layer(): + root._fatal_screen("战斗启动失败——见输出日志") + + + +static func _on_node_continue(root: Variant) -> void: + root._close_modal(root.node_overlay) + if root.run_scope.runner != null: + # 确认 ≠ 推进——兑付确认后经 choices_presented 弹出选路口 + root.run_scope.runner.continue_after_node() + +static func _on_choices_presented(root: Variant, candidates: Array) -> void: + # 零自动推进的停点呈现:整幅节点图 + 图上点击选路(战斗胜利/兑付确认统一到达此处) + if root.run_scope.runner == null: + return + root.log_view.append_bb(root.Narrator.route_line(candidates, root.run_scope.runner)) + # 战斗胜利总结结算(battle.md OQ14):战斗胜利停点先弹 BattleEndOverlay + # 胜利形态(金蜡封+GP 增量+总量+判定行+「下一节点」+明细回看),确认后进选路页。判据 = 战斗节点 + # 的选路停点且战斗已收场(battle 收尾分支 _settlement 置 null 先于本信号;入口/兑付/商店停点 + # 的当前节点非 battle 型——三态天然分流)。Boss 胜/失败走 level_finished 另路不经此处。 + if root.run_scope.is_adventure and root.run_scope.runner.current_battle() == null \ + and String(root.run_scope.runner.current_node().get("type", "")) == "battle" and not candidates.is_empty(): + root._settlement_stop_pending = true + root.run_scope.pending_candidates = candidates.duplicate() + root._end_overlay.open_victory(root.Narrator.battle_end_text("victory", false), "下一节点", + root.board_view.get_global_rect(), + "战利品 GP +%d · 合计 %d" % [root.run_scope.last_gain_amount, int(root.run_scope.runner.gp)]) + root._open_modal_after_playback(root._end_overlay) + root._refresh_presentations() + return + _enter_level_screen(root) + root.level_screen.set_feat_holdings(" · ".join(HudPresenter.feat_holdings_lines(root.db, root.run_scope.runner.feats_held()))) + root.level_screen.open_choices(candidates, root.run_scope.runner) + root._refresh_presentations() + +static func _on_route_chosen(root: Variant, node_id: int) -> void: + # 视图已按节点类型定去留——战斗/Boss 出屏(visible=false,此处收栈 + # 记录一致性+回战斗屏);奖励/恢复/商店本屏保持激活(兑付覆盖层由节点事件域内弹出,模态栈 + # 打开低层时自动隐藏本屏、关闭自动恢复) + if not root.level_screen.visible: + root._close_modal(root.level_screen) + _exit_level_screen(root) + if root.run_scope.runner != null and not root.run_scope.runner.pick_next_node(node_id): + push_error("[组合根] 选路被拒(编号 %d 不在候选)" % node_id) + + + +static func _enter_level_screen(root: Variant) -> void: + root.run_scope.level_screen_active = true + root._battle_root.visible = false + root._open_modal(root.level_screen) + root._sync_status_entry() # 屏级互斥切换同步(_open_modal 单口不识屏级状态) + +static func _exit_level_screen(root: Variant) -> void: + root.run_scope.level_screen_active = false + root._battle_root.visible = true + if root.level_screen.visible: + root._close_modal(root.level_screen) + root._sync_status_entry() # 同上 + +static func _on_abandon_requested(root: Variant) -> void: + # 放弃入口(冒险结构.md 已放开「MVP 无界面入口」后置条款; + # 入口挂选路停点 = 天然非战斗期,拒绝分支仅防御在案):受理 → 冒险放弃结算(同失败型, + # 已获大奖励不追回)→ adventure_finished 同步到达 → 终局面收层 + if not root.run_scope.is_adventure or root.run_scope.adventure == null: + return + if not root.run_scope.adventure.abandon(): + root.logger.log_ux("ux_reject", {"source": "abandon", "sub_reason": "battle_in_flight"}) + root.board_zone.toast.show_toast("战斗进行中——无法放弃", "warn") + + +# ---- 营地覆盖层与钥石账本(写调用唯一归组合根)---- + diff --git a/game/presentation/main_adventure_flow.gd.uid b/game/presentation/main_adventure_flow.gd.uid new file mode 100644 index 00000000..6488df99 --- /dev/null +++ b/game/presentation/main_adventure_flow.gd.uid @@ -0,0 +1 @@ +uid://b7dq82pyu4xlw diff --git a/game/presentation/main_debug_flow.gd b/game/presentation/main_debug_flow.gd new file mode 100644 index 00000000..daac63a0 --- /dev/null +++ b/game/presentation/main_debug_flow.gd @@ -0,0 +1,105 @@ +## VERTICAL SLICE - NOT FOR PRODUCTION +## 组合根调试流程承接件:调试浮窗开合与上下文同步、批量模拟入口、两层调试对局起手、诊断转储。 +## 本件是 main.gd 同名函数的逐字搬运(成员访问改 root 前缀)——main 侧留同名薄委托,探针 main.X 路径零改动。 +## 调试会话状态机归 debug_session.gd;本件只接线与出 UI,零成员态。 +extends RefCounted + +static func _on_diagnose_pressed(root: Variant) -> void: + # 本局种子现值走 _current_master_seed 只读单点 + var ctx := {"mode": "level" if root.run_scope.level_active else "debug", "seed": root._current_master_seed()} + if root.run_scope.level_active and root.run_scope.runner != null: + ctx["runner"] = {"node_index": root.run_scope.runner.node_index, "level_result": String(root.run_scope.runner.level_result), + "hero_hp": root.run_scope.runner.hero_hp, "settlement_is_null": root.run_scope.runner.current_battle() == null} + if root.run_scope.settlement != null: + ctx["settlement"] = root.run_scope.settlement.diagnostic_snapshot() # 诊断观测面(私有字段零直读) + var path: String = root.logger.dump_diagnosis(ctx) + root.board_zone.toast.show_toast("已转储诊断: " + path, "info") + + + +static func _show_debug_overlay(root: Variant) -> void: + # 门禁:对局中可开;本函数仍是唯一开窗入口,门禁逻辑收函数内: + # 棋盘输入锁/状态角标判据排除浮窗, + # 遮挡语义=ModalStack 登记制(他模态开时暂隐、关后自动恢复),此处零场景限制 + if root.debug_overlay == null: + return + root._open_modal(root.debug_overlay) + _sync_debug_overlay(root) + + + +# 调试浮窗角标点击(两屏同款处理器——与状态角标同形制守卫;仍收口 _show_debug_overlay 单口) +static func _on_debug_badge_pressed(root: Variant) -> void: + var map_on: bool = root.level_screen != null and root.level_screen.visible + if (map_on and root._modal_flow.badge_greyed_map()) or (not map_on and root._modal_flow.badge_greyed_battle()): + return + _show_debug_overlay(root) + + + +# 浮窗上下文同步(当局数据刷新+对局中按钮禁用单点):调用面=开窗口与模态开合 +# 单口(凡影响开态/对局态的路径必经其一——与 _sync_status_entry 同款纪律) +static func _sync_debug_overlay(root: Variant) -> void: + if root.debug_overlay == null: + return + root.debug_overlay.set_context_locked(root.run_scope.settlement != null or root.run_scope.is_adventure) # 对局中批量模拟/两层调试入口禁用(长程异步模拟与活跃结算并存态不开放) + root.debug_overlay.update_run_data(_debug_run_data(root)) # 当局数据只读区块喂入(开窗+模态开合信号驱动) + + + +# 当局数据六项只读投影(零直读内部状态、零写通路):种子=adventure.master_seed 现值 +# (非 _seed_value() 重算);调试两层/非对局无冒险对象=RNG 服务 master_seed 现值(只读出口, +# 未播种 -1 显「—」);关卡/节点/HP/GP=runner 成文出口、钥石=adventure 冒险账本(LevelRunner 无此域); +# 缺位=「—」占位。零动效(纯标签重查)。 +static func _debug_run_data(root: Variant) -> Dictionary: + var r = root.run_scope.adventure.runner() if root.run_scope.adventure != null else null + var seed_now: int = root._current_master_seed() + var seed_txt: String = str(seed_now) if seed_now >= 0 else "—" + if r == null: + return {"seed": seed_txt, "level": "—", "node": "—", "hp": "—", "gp": "—", "keystone": "—"} + return {"seed": seed_txt, "level": int(root.run_scope.adventure.level_index), "node": int(r.node_index), + "hp": "%d/%d" % [int(r.hero_hp), int(r.effective_max_hp())], "gp": int(r.gp), "keystone": int(root.run_scope.adventure.keystone)} + + + +static func _close_debug_overlay(root: Variant) -> void: + root._close_modal(root.debug_overlay) # 模态栈单口——开始层全程未隐藏,无需恢复 + +static func _on_tune_apply(root: Variant, atk_pct: int, hp_pct: int) -> void: + # 加成无持久态可"落"——各战斗开局经 board_resolvers.debug_hero_override/runner 现算生效(应用=关闭浮窗) + _close_debug_overlay(root) + +static func _on_tune_simulate(root: Variant, n: int) -> void: + # 批量模拟编排 = 控制器承接(tune_sim_controller.gd);E4 后无表态需复位——模拟走 runner 覆盖参数。 + # 对局中禁用兜底(长程异步模拟与活跃冒险/结算并存态不开放;探针 emit 绕过 disabled 时由此守住) + if root.run_scope.settlement != null or root.run_scope.is_adventure: + return + root._tune_sim.run(n) + + + +static func _start_debug_levels(root: Variant) -> void: + # 守卫:调试对局与活跃冒险/结算/批量模拟并存不开放——并发会清写 + # settlement/编排态=状态机破坏;模拟在跑时 reset_sliders 会作废测量读数(d5 置灰之外的信号绕过闸) + if root.run_scope.is_adventure or root.run_scope.settlement != null or root._tune_sim.is_running(): + return + root.debug_overlay.reset_sliders() + root._close_modal(root.debug_overlay) # 浮窗内入口发起——随开随收(对局期不可再开:门禁在 _show_debug_overlay) + root._start_overlay.visible = false + root.log_view.clear() + root.log_view.append_bb(root.Narrator.debug_open_line(root._seed_value(), 1, true)) + root._begin_session(false) + if not root._debug.start_debug_levels(): + root._fatal_screen("战斗启动失败——见输出日志") + +static func _on_debug_battle_created(root: Variant, settlement_ref: BattleOrchestrator, _level: int, _carry: int) -> void: + # begin_battle 前接手 + 接线(signal.connect 唯一归组合根)。settlement 必须先于 begin_battle + # 就位——begin 期间发出的 board_changed 要用新板重建棋盘并触发一次性提示(缺陷①同款时序义务) + root.run_scope.settlement = settlement_ref + root._wire_battle(settlement_ref) + +static func _on_debug_battle_ready(root: Variant, settlement_ref: BattleOrchestrator, _level: int, _carry: int) -> void: + root.run_scope.settlement = settlement_ref + root.board_view.rebuild(root.run_scope.settlement.minesweeper.current_board(), root.run_scope.settlement.minesweeper) + root._check_board_hints() + root._refresh_presentations() diff --git a/game/presentation/main_debug_flow.gd.uid b/game/presentation/main_debug_flow.gd.uid new file mode 100644 index 00000000..095a713f --- /dev/null +++ b/game/presentation/main_debug_flow.gd.uid @@ -0,0 +1 @@ +uid://by2sg5pm2rdkb diff --git a/game/presentation/main_overlay_flow.gd b/game/presentation/main_overlay_flow.gd new file mode 100644 index 00000000..ce9755d4 --- /dev/null +++ b/game/presentation/main_overlay_flow.gd @@ -0,0 +1,155 @@ +## VERTICAL SLICE - NOT FOR PRODUCTION +## 组合根覆盖层流程承接件:设置/状态面板/生涯页/选档界面/图例/钱包事件的开合与数据投喂。 +## 本件是 main.gd 同名函数的逐字搬运(成员访问改 root 前缀)——main 侧留同名薄委托,探针 main.X 路径零改动。 +## 全部开合走组合根模态单口(root._open_modal/_close_modal);本件零成员态。 +extends RefCounted + +static func _open_settings(root: Variant) -> void: + root._start_overlay.visible = false + root._open_modal(root.settings_overlay) # 模态栈单口 + +static func _on_settings_closed(root: Variant) -> void: + root._close_modal(root.settings_overlay) # 模态栈单口 + root._start_overlay.visible = true + + +# ---- 局内状态面板·只读(入口角标在两屏装配段;开合与数据喂入归组合根)---- + + +# 状态面板快照投影(数据只读同源:每次开层实时读访问器现值拼装—— +# 快照仅服务单次开层生命周期(open_status 重建各行),跨开层零持有;零写调用。 +# 八键换代(迭代 36 卡③):跨关解锁概览退役(营地/生涯承载)——只留本局信息; +# ledger 直取下标(键缺失=致命,禁 .get 缺省留门——fail-fast 红线);禁 null 守卫软回退 +# (两入口开层时 runner 恒在役——状态面板卡 F3 证据链) +static func _status_data(root: Variant) -> Dictionary: + return HudPresenter.status_data(root.db, root.meta, root.run_scope.runner, root._treasure_ui) + +static func _open_status_panel(root: Variant) -> void: + root._open_modal(root.status_panel) # 模态栈单口(开层前排空排播队列——既有层同款行为,目检确认项) + root.status_panel.open_status(_status_data(root)) + +static func _on_status_closed(root: Variant) -> void: + root._close_modal(root.status_panel) # 模态栈单口 + + + +static func _open_career(root: Variant) -> void: + root._start_overlay.visible = false + root._open_modal(root.career_overlay) # 模态栈单口 + root.career_overlay.open_career(HudPresenter.career_data(root.db, root.save_manager, root.meta)) + +static func _on_career_closed(root: Variant) -> void: + root._close_modal(root.career_overlay) # 模态栈单口 + root._start_overlay.visible = true + + +# 生涯页快照投影 —— 承接体见 HudPresenter.career_data(开层调用点直接改引) + + +# ==================== 选档界面(营地交互重做卡③)==================== + + +# 开层(模态栈单口;与开始层互斥=career 同款先例)。「进入存档」钮归卡② mn3——本函数为 +# 该钮的唯一落点(卡③ 批内探针函数直调=中间态,实现报告「已知中间态」登记)。 +static func _open_save_select(root: Variant) -> void: + root._start_overlay.visible = false + root._open_modal(root.save_select_overlay) # 模态栈单口 + root.save_select_overlay.open_save_select(HudPresenter.save_select_data(root.save_manager)) + +static func _on_save_select_closed(root: Variant) -> void: + root._close_modal(root.save_select_overlay) # 模态栈单口 + root._start_overlay.visible = true + + +# 槽数据快照 —— 承接体见 HudPresenter.save_select_data(开层调用点直接改引) + + + +# 空槽新档链(Q3 裁乙暂行落点=现役 CampOverlay;卡④ 重立卡后改挂场景图):卡① 创建=激活=白板落盘, +# 落点开层。动线序(禁先关后开):先落点开层(开始层保持隐藏),选档层被模态栈遮挡隐藏、不关闭。 +static func _on_save_select_new(root: Variant, slot: int) -> void: + root.save_manager.create_slot(slot) + root._camp_pres.open_camp() + + + +# 有档槽进档链(契约 8 进档即激活):卡① 激活+读盘(兜底/拒读链按槽成立)→暂行落点 CampOverlay。 +static func _on_save_select_load(root: Variant, slot: int) -> void: + root.save_manager.activate_slot(slot) + root._camp_pres.open_camp() + + + +# 删档链(契约 3):第一次点击只出确认窗(消费方编排——open 前设消费方=delete_slot), +# 确认才删、取消不删;禁长按删/一次点击即删/撤销式软删。 +static func _on_save_select_delete(root: Variant, slot: int) -> void: + root._confirm_consumer = "delete_slot" + root._confirm_consumer_slot = slot + root.confirm_dialog.open("删除存档位 %d?删除后该档不可恢复。" % slot, "删除", "取消") + + + +# 入口角标点击(两屏同款处理器):P1 守卫兜底——其他模态开着即拒开(常态防线=角标置灰, +# 探针 pressed.emit 绕过 disabled 时由此兜住;地图屏自身的登记开态不计——has_open_excluding) +static func _on_status_badge_pressed(root: Variant) -> void: + var map_on: bool = root.level_screen != null and root.level_screen.visible + # P1 守卫兜底——其他模态开着即拒开(常态防线=角标置灰,探针 pressed.emit 绕过 disabled 时由此兜住; + # 地图屏自身的登记开态不计;判据排除调试浮窗:对局中浮窗开着时状态角标仍可点) + if (map_on and root._modal_flow.badge_greyed_map()) or (not map_on and root._modal_flow.badge_greyed_battle()): + return + _open_status_panel(root) + + + +# 入口角标可见性/置灰同步(P1 丁+守卫——单一同步点):战斗屏角标=对局内且地图屏未激活; +# 地图屏角标=随屏;置灰=登记册上除地图屏自身外任一层开着(防「看得见点不着」态)。 +# 调用面=模态开合单口与屏级互斥口(凡影响开态/屏态的路径必经其一)。 +static func _sync_status_entry(root: Variant) -> void: + if root.status_panel == null or root._modals == null or root.board_zone == null or root.level_screen == null: + return + var map_on: bool = root.level_screen.visible + var battle_live: bool = root.run_scope.level_active or root.run_scope.settlement != null # 调试两层无 level_active——settlement 判据补位 + root.board_zone.status_badge.visible = root.run_scope.level_active and not map_on + root.level_screen.status_badge.visible = map_on + # 置灰判据为排除式(调试浮窗开态不计入:对局中浮窗开着时角标仍可点) + root.board_zone.status_badge.disabled = root._modal_flow.badge_greyed_battle() # 互斥域谓词 + root.level_screen.status_badge.disabled = root._modal_flow.badge_greyed_map() + # 状态角标判据(同上——重复语句在案): + # 战斗屏=对局内(冒险或调试两层)且地图屏未激活;地图屏=随屏;开始菜单零角标 + root.board_zone.status_badge.visible = root.run_scope.level_active and not map_on + root.level_screen.status_badge.visible = map_on + root.board_zone.status_badge.disabled = root._modal_flow.badge_greyed_battle() # 互斥域谓词 + root.level_screen.status_badge.disabled = root._modal_flow.badge_greyed_map() + # 调试浮层角标(全局浮层任何 UI 不遮挡——可见性不随场景变,只抬 offset + # 避让:地图屏底栏 85px 常驻,角标抬至底栏上方 16px;其余场景右下角 16px 贴底) + root.debug_badge.offset_bottom = -101.0 if map_on else -16.0 + + + +static func _on_camp_closed(root: Variant) -> void: + root._camp_pres.on_camp_closed() + +static func _camp_data(root: Variant) -> Dictionary: + return HudPresenter.camp_data(root._camp_pres) + +static func _on_wallet_changed(root: Variant, event: String, gp: int, extra: Dictionary) -> void: + # wallet 行族转发(到账/购买/清零——试玩日志验收取数面)+ 常驻余额与在开商店覆盖层重刷 + root.logger.log_wallet(event, gp, extra) + if String(event) == "gain": + root.run_scope.last_gain_amount = int(extra.get("amount", 0)) # GP 增量取数(每战恰一笔 gain) + if root.shop_overlay.visible and root.run_scope.runner != null: + if root.run_scope.runner.is_shop_pool(): + root.shop_overlay.refresh_shelf_state(int(root.run_scope.runner.gp)) # pool 面余额联动(货架内容仅买后重抽变) + else: + root.shop_overlay.refresh_state(root.run_scope.runner.shop_products(), int(root.run_scope.runner.gp)) + root._refresh_presentations() + + + +static func _on_legend_toggled(root: Variant) -> void: + if root.legend_overlay.is_open(): + root._close_modal(root.legend_overlay) + else: + root._open_modal(root.legend_overlay) + + diff --git a/game/presentation/main_overlay_flow.gd.uid b/game/presentation/main_overlay_flow.gd.uid new file mode 100644 index 00000000..47b85452 --- /dev/null +++ b/game/presentation/main_overlay_flow.gd.uid @@ -0,0 +1 @@ +uid://bhphsistuq7e3 diff --git a/game/presentation/main_ui_build.gd b/game/presentation/main_ui_build.gd new file mode 100644 index 00000000..269be5fa --- /dev/null +++ b/game/presentation/main_ui_build.gd @@ -0,0 +1,238 @@ +## VERTICAL SLICE - NOT FOR PRODUCTION +## 组合根 UI 装配分步承接件:三屏壳实例化、视图句柄接线、覆盖层装配与模态编排注入按步拆开。 +## 本件是 main._build_ui 的逐字分步搬运(成员访问改 root 前缀)——main 侧留同名薄委托,装配序与 z 序零改动。 +## 层序唯一事实源仍是 main.LAYER_CONTRACT + _apply_layer_contract(本件不持有层序逻辑)。 +extends RefCounted + +const UiAssembler := preload("res://presentation/ui_assembler.gd") +const RowPacer := preload("res://presentation/row_pacer.gd") +const TreasureInteractController := preload("res://presentation/treasure_interact_controller.gd") +const CurioInteractController := preload("res://presentation/curio_interact_controller.gd") +const CampShopPresenter := preload("res://presentation/camp_shop_presenter.gd") +const BoardResolversScript := preload("res://presentation/board_resolvers.gd") +const DebugSessionScript := preload("res://presentation/debug_session.gd") +const TuneSimController := preload("res://presentation/tune_sim_controller.gd") +const MenuScreenScene := preload("res://presentation/scenes/menu_screen.tscn") +const CampScreenScene := preload("res://presentation/scenes/camp_screen.tscn") +const AdventureScreenScene := preload("res://presentation/scenes/adventure_screen.tscn") + + + +static func build(root: Variant) -> void: + _build_screens(root) + _build_battle_wiring(root) + _build_overlays(root) + _build_aux_panels(root) + _build_curio(root) + _build_menu_wiring(root) + _build_debug_and_resolvers(root) + _build_modal_flow(root) + +static func _build_screens(root: Variant) -> void: + # 三屏壳实例化:冒险屏先挂、菜单屏后挂(启动态菜单在顶层=玩家可见—— + # 装配序纪律:菜单先挂会被冒险屏盖死,F5 见战斗界面);最终树序+登记序以 LAYER_CONTRACT + # 为唯一事实源,_apply_layer_contract 在装配尾统一落位。 + # 屏内 level_screen 与 battle_root 无同时可见态(屏级互斥+模态栈遮挡),层内先后零视觉差; + # 视觉共存对逐对验算:legend/battle(图例战斗中开——legend 在冒险屏壳之后=其上 ✓)、 + # debug/level_screen(浮窗最后子=其上 ✓)、shop/level_screen(商店节点域弹出——模态均在屏壳上 ✓)。 + root._adventure_screen = AdventureScreenScene.instantiate() + root.add_child(root._adventure_screen) + root._menu_screen = MenuScreenScene.instantiate() + root.add_child(root._menu_screen) + root._start_overlay = root._menu_screen.start_overlay + root._battle_root = root._adventure_screen.battle_root + root.stage = root._adventure_screen.stage + root.log_view = root._adventure_screen.log_view + root.info_panel = root._adventure_screen.info_panel + root.board_zone = root._adventure_screen.board_zone + root.board_view = root._adventure_screen.board_view # 探针兼容别名(= board_zone.board_view) + root.ap_bar = root._adventure_screen.ap_bar # 探针兼容别名(= board_zone.ap_bar) + root.settlement_fx = root._adventure_screen.settlement_fx + root.level_screen = root._adventure_screen.level_screen + +static func _build_battle_wiring(root: Variant) -> void: + # 结算行排播泵(本体在 row_pacer.gd;本类注入视图句柄与呈现刷新回调,零信号连接) + root._row_pacer = RowPacer.new() + root._row_pacer.log_view = root.log_view + root._row_pacer.stage = root.stage + root._row_pacer.board_zone = root.board_zone + root._row_pacer.settlement_fx = root.settlement_fx + root._row_pacer.board_view = root.board_view + root._row_pacer.refresh_request = root._refresh_presentations + root._row_pacer.pump_ended = root._on_pump_ended # 演播完边沿统一派发(挂起 modal 优先,其次锁期点击重放) + root.add_child(root._row_pacer) + # 秘宝两步交互(本体在 treasure_interact_controller.gd;settlement/runner 随关卡更替, + # 经现值回调注入——仿 identity_resolver 只读回调先例) + root._treasure_ui = TreasureInteractController.new(root.db, root.meta, root.info_panel, root.board_zone.toast, root.board_view, + func() -> Variant: return root.run_scope.settlement, func() -> Variant: return root.run_scope.runner) + + root.board_zone.action_requested.connect(root._on_board_action) + root.board_zone.status_badge.pressed.connect(root._on_status_badge_pressed) # 状态面板入口(战斗屏 HUD 角标) + root.info_panel.treasure_slot_clicked.connect(root._treasure_ui.on_slot_clicked) # 两步交互第一步 + root.board_zone.stop_pressed.connect(root._on_stop_pressed) + root.board_zone.refresh_confirmed.connect(root._on_refresh_confirmed) + root.board_zone.legend_toggled.connect(root._on_legend_toggled) + root.board_zone.hover_forbidden.connect(root._on_hover_forbidden) + root.board_zone.chord_attempted.connect(root._on_chord_attempted) + + root.logger = PlaytestLogger.new() + root.logger.ap_per_round = int(root.db.action_economy.ap_per_round) + +static func _build_overlays(root: Variant) -> void: + # 模态覆盖层(登记序 = 树序 = z 遮挡序:图例 < 明细回看 < 节点 < 关卡画面 < 三选一 < 终局 + # < 商店 < 营地 < 设置 < 状态面板(只读) < 生涯页(只读) < 确认弹窗 < 调试浮窗; + # 关卡画面住冒险屏壳(树序在模态之下)——登记序仍夹在节点层与三选一层之间,遮挡语义零改动) + var overlays: Dictionary[Variant, Variant] = UiAssembler.build_overlays(root, root.db) + root.legend_overlay = overlays.legend_overlay + root.node_overlay = overlays.node_overlay + root._end_overlay = overlays.end_overlay + root.shop_overlay = overlays.shop_overlay + root._camp_screen = CampScreenScene.instantiate() + root.add_child(root._camp_screen) + root.camp_overlay = root._camp_screen.camp_overlay + # 营地屏壳插位(夹商店层与设置层之间)由 LAYER_CONTRACT 营地槽统一落位——禁散落 move_child + root.legend_overlay.closed.connect(root._on_modal_closed.bind(root.legend_overlay)) + root.node_overlay.continue_pressed.connect(root._on_node_continue) + root.level_screen.route_chosen.connect(root._on_route_chosen) + root.level_screen.status_badge.pressed.connect(root._on_status_badge_pressed) # 状态面板入口(地图屏顶栏角标) + root.level_screen.abandon_requested.connect(root._on_abandon_requested) # 放弃入口 + root._end_overlay.continue_pressed.connect(root._on_battle_end_continue) + +static func _build_aux_panels(root: Variant) -> void: + root.settings_overlay = SettingsOverlay.new(root.settings_store) # 最小设置面(音量/全屏/减动效) + root.add_child(root.settings_overlay) + root.settings_overlay.closed.connect(root._on_settings_closed) + # 局内状态面板·只读(登记设置层同档带 dim 遮罩,确认弹窗之前; + # 数据=组合根开层实时读 MetaProgression 访问器喂入,本层零写通路) + root.status_panel = StatusPanel.new() + root.add_child(root.status_panel) + root.status_panel.closed.connect(root._on_status_closed) + # 生涯页·只读全屏页(登记序=营地同档,设置层旁、确认弹窗之前; + # 树序同步=addChild 同位(登记序=树序=z 序纪律);数据=组合根开层实时读 SaveManager 计数 + # 访问器+MetaProgression 访问器喂入,本层零写通路零跳转;入口=开始层「营地与系统」组生涯钮) + root.career_overlay = CareerOverlay.new() + root.add_child(root.career_overlay) + root.career_overlay.closed.connect(root._on_career_closed) + # 选档界面(营地交互重做卡③):登记序=生涯同档(树序同步=addChild 同位);三动作只发意图信号 + #(激活/创建/删除+确认分派归组合根——视图零数据层依赖);入口=「进入存档」钮(卡② mn3 接线)。 + root.save_select_overlay = SaveSelectOverlay.new() + root.add_child(root.save_select_overlay) + root.save_select_overlay.closed.connect(root._on_save_select_closed) + root.save_select_overlay.new_slot_requested.connect(root._on_save_select_new) + root.save_select_overlay.load_slot_requested.connect(root._on_save_select_load) + root.save_select_overlay.delete_slot_requested.connect(root._on_save_select_delete) + +static func _build_curio(root: Variant) -> void: + # 提升三选一覆盖层(z 序 = 节点层之上、终局层之下;确认后同拍推进; + # 插位由 LAYER_CONTRACT curio 槽统一落位——禁散落 move_child) + root.curio_overlay = CurioPickOverlay.new() + root.add_child(root.curio_overlay) + # 提升三选一与替换子交互(本体在 curio_interact_controller.gd;本类只接线与回调注入, + # 模态栈/演播后排窗/呈现刷新/fatal 经回调注入,adventure/runner 现值回调) + root._bonus = CurioInteractController.new(CurioFlowContext.new(root.db, func() -> Variant: return root.meta, root.curio_overlay, root._end_overlay, root.board_view, + root.board_zone.toast, func() -> Variant: return root.save_manager, func() -> Variant: return root.run_scope.adventure, + func() -> Variant: return root.run_scope.runner, func() -> Variant: return root.run_scope.is_adventure, + root._open_modal, root._open_modal_after_playback, root._close_modal, root._refresh_presentations, + root._fatal_screen, func() -> String: return root._gp_summary_text({"gp_dropped": root.run_scope.last_gain_amount}))) # 构造参数收拢 context + root.curio_overlay.pick_confirmed.connect(root._bonus.on_pick_confirmed) + root.curio_overlay.continue_pressed.connect(root._bonus.on_continue) + root.curio_overlay.refresh_requested.connect(root._bonus.on_refresh) + +static func _build_menu_wiring(root: Variant) -> void: + # 菜单屏接线(句柄经屏壳暴露;三入口化——进入存档/设置/退出游戏,卡② mn3: + # 进入存档钮落点=卡③选档层 _open_save_select,点击链在此闭环) + root._menu_screen.entersavebtn.pressed.connect(root._open_save_select) + # 调试入口=UI 最顶层全局浮层角标(任何 UI 不遮挡——菜单/战斗/地图三枚 + # 屏内角标由全局浮层承载;连接归组合根,树序/登记面见 LAYER_CONTRACT 末位) + root.debug_badge = UiAssembler.build_debug_badge(root) + root.add_child(root.debug_badge) + root.debug_badge.pressed.connect(root._on_debug_badge_pressed) + # 营地/商店投影(本体在 camp_shop_presenter.gd;本类只接线、探针缝保缝转发; + # 购买写调用唯一经 MetaProgression/LevelRunner core 单口——组合根 lambda 现值注入) + root._camp_pres = CampShopPresenter.new(CampFlowContext.new(root.db, func() -> Variant: return root.meta, func() -> Variant: return root.save_manager, root.camp_overlay, root.shop_overlay, + root.board_zone.toast, root._start_overlay, root.logger, func() -> Variant: return root.run_scope.runner, + root._open_modal, root._close_modal, root._refresh_presentations)) # 构造参数收拢 context + root.shop_overlay.buy_requested.connect(root._camp_pres.on_shop_buy) + root.shop_overlay.refresh_requested.connect(root._camp_pres.on_shop_refresh) # MC-1 + root.shop_overlay.leave_pressed.connect(root._camp_pres.on_shop_leave) + root.camp_overlay.buy_requested.connect(root._camp_pres.on_camp_buy) + root.camp_overlay.attune_toggled.connect(root._camp_pres.on_camp_attune_toggle) + root.camp_overlay.tree_buy_requested.connect(root._camp_pres.on_camp_tree_buy) + root.camp_overlay.tree_fork_toggled.connect(root._camp_pres.on_camp_fork_toggle) + root.camp_overlay.forge_buy_requested.connect(root._camp_pres.on_camp_forge_buy) # 锻造三意图(写口唯一归 MetaProgression) + root.camp_overlay.forge_unlock_requested.connect(root._camp_pres.on_camp_forge_unlock) + root.camp_overlay.forge_select_requested.connect(root._camp_pres.on_camp_forge_select) + root.camp_overlay.closed.connect(root._camp_pres.on_camp_closed) + root._menu_screen.settingsbtn.pressed.connect(root._open_settings) # 最小设置面入口 + root._menu_screen.quitbtn.pressed.connect(root._quit_game) # 退出游戏提级一级菜单 + +static func _build_debug_and_resolvers(root: Variant) -> void: + var inter: Dictionary[Variant, Variant] = UiAssembler.build_inter_overlay(root) + root._inter_overlay = inter.overlay + root._inter_label = inter.label + inter.next_btn.pressed.connect(root._on_inter_next_pressed) + + # 确认弹窗(新冒险互斥确认弹窗化):树序落在开始层/过场层之后 = 子级最末 + #(纪律「登记序 = 树序 = z 序」——confirm_dialog.open 禁运行时 move_child 自提抬层; + # 弹窗只在开始菜单域弹出,绘制序恒最上; + # 诊断转储入口在调试浮窗) + root.confirm_dialog = ConfirmDialog.new() + root.add_child(root.confirm_dialog) + # 消费方编排(卡③ 契约 3/sl8——禁双常驻静态接线):open 前设 _confirm_consumer, + # confirmed/cancelled 后按该状态量分派;分派面可容纳后续消费方(卡④ 到位直接接入不返工) + root.confirm_dialog.confirmed.connect(root._on_confirm_accepted) + root.confirm_dialog.cancelled.connect(root._on_confirm_declined) + + # 调试浮窗(主菜单三散件收一:随机种子/数值调整/批量模拟/两层调试入口; + # 全局可开浮窗:调试会话恒开=启动即开〔_ready〕+两屏角标再开+当局数据只读区块; + # 诊断转储入口在本浮窗,玩家画面零常驻调试件)。 + # 树序在开始层与确认弹窗之后 = 浮于菜单之上的 Dialog(不隐藏开始层——与确认弹窗同款形态); + # 开窗唯一入口 = _show_debug_overlay(C6 单口——角标/探针/启动路径全收口) + root.debug_overlay = DebugOverlay.new(root.db, int(root.db.hero.atk), int(root.db.hero.max_hp)) + root.add_child(root.debug_overlay) + root._seed_edit = root.debug_overlay.seed_edit # 种子读取面在浮窗(_seed_value 唯一消费点不变) + root.debug_overlay.closed.connect(root._on_modal_closed.bind(root.debug_overlay)) + root.debug_overlay.apply_requested.connect(root._on_tune_apply) + root.debug_overlay.simulate_requested.connect(root._on_tune_simulate) + root.debug_overlay.debug_levels_requested.connect(root._start_debug_levels) + + # 棋盘解析回调族(有态绑定件——board_resolvers.gd 承接;组合根只接线;局部持有不占类成员位) + var resolvers = BoardResolversScript.new(root.db, root.debug_overlay, func() -> Variant: return root.run_scope.settlement) + # 奖励徽章数据源:地图战斗资源 身份解析只读回调(已开格身份 icon——挖掘期 Active 相位内有效) + root.board_view.set_identity_resolver(resolvers.identity_lookup) + # 数字遮蔽数据源:地形变异 hide_number 解析只读回调(信息型呈现——board_view 渲染面读取) + root.board_view.set_variant_mask_resolver(resolvers.variant_mask_lookup) + root.board_view.set_variant_mark_resolver(resolvers.variant_mark_lookup) # flat_damage 未开格角标 + # 调试会话(英雄生效值预览回调绑同一解析件——创建晚于浮窗、随 _build_ui 一次接线) + root._debug = DebugSessionScript.new(root.db, root.get_node(root.AUTOLOAD_PATH_RNG), + func() -> Dictionary: return resolvers.debug_hero_override(), root._seed_value) + root._debug.battle_created.connect(root._on_debug_battle_created) + root._debug.battle_ready.connect(root._on_debug_battle_ready) + +static func _build_modal_flow(root: Variant) -> void: + # 模态登记(缺陷⑤纪律——登记序 = 树序 = z 序):层序唯一事实源=LAYER_CONTRACT, + # _apply_layer_contract 统一落位(关卡画面宿住冒险屏壳、营地屏壳夹商店与设置层之间、 + # 三选一对位终局层之下等特例均在合同数据内表达,禁散落 move_child) + # 模态编排注入(ModalFlow 纯逻辑类:依赖仅 ModalStack+查询 Callable) + root._modal_flow = ModalFlow.new() + root._modal_flow.start_visible_get = func() -> bool: return root._start_overlay.visible + root._modal_flow.inter_visible_get = func() -> bool: return root._inter_overlay.visible + root._modal_flow.settlement_get = func() -> Variant: return root.run_scope.settlement + root._modal_flow.rows_empty_get = func() -> bool: return root._row_pacer.pending_rows().is_empty() + root._modal_flow.flush_rows = root._flush_row_queue + root._modal_flow.on_open_sync = func() -> void: + root._sync_status_entry() # 状态面板入口角标可见性/置灰随开态同步 + root._sync_debug_overlay() # 浮窗当局数据/按钮态随开态同步 + root._modal_flow.on_close_sync = func() -> void: + root._sync_status_entry() # 关闭恢复角标可点态(无残留——同源口径) + root._sync_debug_overlay() # 他层关闭复活浮窗时同步刷新(关后自动恢复语义配套) + root._modal_flow.replay_click = root._replay_locked_click + root._modals = root._modal_flow.stack # 探针 _list 登记序断言缝(ui_struct 登记序=z 序纪律断言) + # 视觉层合同装配(唯一事实源=LAYER_CONTRACT:树序+登记序同源派生——改序只改合同表) + root._apply_layer_contract() + root._tune_sim = TuneSimController.new(root.db, root.debug_overlay, int(root.db.hero.max_hp)) + +static func fatal_screen(root: Variant, msg: String) -> void: + var l := UiKit.label(msg,UiKit.FS_SUB, true, UiKit.RED_ALERT) + l.set_anchors_preset(Control.PRESET_CENTER) + root.add_child(l) diff --git a/game/presentation/main_ui_build.gd.uid b/game/presentation/main_ui_build.gd.uid new file mode 100644 index 00000000..82f16e03 --- /dev/null +++ b/game/presentation/main_ui_build.gd.uid @@ -0,0 +1 @@ +uid://qlirkl1kvqo6 diff --git a/game/presentation/modal_stack.gd b/game/presentation/modal_stack.gd index dd8f0ec4..bb0d1d2c 100644 --- a/game/presentation/modal_stack.gd +++ b/game/presentation/modal_stack.gd @@ -19,18 +19,33 @@ func register(m: Control, tags: Array = []) -> void: func open(m: Control) -> void: - var hidden: Array[Control] = [] var idx := _list.find(m) + if idx < 0: + push_error("ModalStack.open: 未登记层不可打开(register 先行)") + return + var hidden: Array[Control] = [] for i in range(idx + 1, _list.size()): var higher: Control = _list[i] if is_instance_valid(higher) and higher.visible: higher.visible = false hidden.append(higher) - _occluded[m] = hidden + # 遮挡账合并非覆盖:同层重复 open 时上方已隐、hidden 为空,旧账须保留 + # (空表覆盖会让先前被隐层在本层 close 后永不恢复) + var merged: Array[Control] = [] + for h in _occluded.get(m, []): + if not merged.has(h): + merged.append(h) + for h in hidden: + if not merged.has(h): + merged.append(h) + _occluded[m] = merged m.visible = true func close(m: Control) -> void: + if _list.find(m) < 0: + push_error("ModalStack.close: 未登记层不可关闭(register 先行)") + return m.visible = false for h in _occluded.get(m, []): if is_instance_valid(h): diff --git a/game/presentation/row_pacer.gd b/game/presentation/row_pacer.gd index fb70ea59..9791fa8d 100644 --- a/game/presentation/row_pacer.gd +++ b/game/presentation/row_pacer.gd @@ -108,11 +108,12 @@ func _play_settlement_mark(row: Dictionary) -> void: if mark_type != "effect" and mark_type != "damage" and mark_type != "heal": return var target := String(row.get("target", "")) + var target_id := String(row.get("target_id", "")) var center: Vector2 if target == "hero": center = stage.hero_standee_center() else: - var v = stage.enemy_standee_center(target) if target != "" and target != "all_enemies" else null + var v = stage.enemy_standee_center(target_id, target) if target != "" and target != "all_enemies" else null center = v if v != null else board_view.get_global_rect().get_center() match mark_type: "effect": @@ -133,6 +134,7 @@ func _play_cross_zone_fx(row: Dictionary) -> void: match String(row.get("type", "")): "damage": var target := String(row.get("target", "")) + var target_id := String(row.get("target_id", "")) var crit := bool(row.get("crit", false)) if target == "" or target == "hero": # 主角受击弹道(禁「敌→主角零反馈」早退):⚔ 危险红飞向主角站桩; @@ -140,7 +142,7 @@ func _play_cross_zone_fx(row: Dictionary) -> void: settlement_fx.play_projectile(from_v, stage.hero_standee_center(), "⚔", UiKit.RED_MINE, crit) return - var to_v = stage.enemy_standee_center(target) + var to_v = stage.enemy_standee_center(target_id, target) if to_v != null: settlement_fx.play_projectile(from_v, to_v, "⚔", UiKit.GOLD if crit else UiKit.RED_MINE, crit) diff --git a/game/presentation/tune_sim_controller.gd b/game/presentation/tune_sim_controller.gd index 07867c87..051a1f53 100644 --- a/game/presentation/tune_sim_controller.gd +++ b/game/presentation/tune_sim_controller.gd @@ -11,6 +11,7 @@ var db var panel # 调试浮窗 setter 面:atk_pct/hp_pct/set_sim_running/set_sim_progress/set_sim_report var _base_hp := 0 # 报告中的"基础 HP"口径注记值 var _driver = null # 关卡自动策略驱动(调用方注入;null = 旧行为运行时 load 兜底) +var _running := false # 运行态(run 全程 true——组合根互查守卫用,与面板置灰解耦) func _init(db_ref, panel_ref, base_hp: int, driver = null) -> void: @@ -27,7 +28,13 @@ func _autoplay() -> Variant: return load("res://sim/level_autoplay.gd") # 与 headless 扫描同口径的自动策略(P3 兜底路径) +# 运行态查询出口(组合根互查守卫:模拟在跑期禁开调试对局/二次模拟) +func is_running() -> bool: + return _running + + func run(n: int) -> void: + _running = true # 共享日志会话(模拟运行与真人试玩同日志合规——操作/轮次/战斗/通关全量 JSONL; # 一文件多局,run 行携带种子切分局次) var logger = PlaytestLogger.new() @@ -60,3 +67,4 @@ func run(n: int) -> void: panel.set_sim_report(HudPresenter.tune_sim_report(n, panel.atk_pct(), panel.hp_pct(), wins, dmg_all, hp_left_win, hp_boss, rounds_all, death_nodes, _base_hp)) panel.set_sim_running(false) + _running = false diff --git a/game/presentation/views/battle_end_overlay.gd b/game/presentation/views/battle_end_overlay.gd index 19a28ce0..bfb40e2e 100644 --- a/game/presentation/views/battle_end_overlay.gd +++ b/game/presentation/views/battle_end_overlay.gd @@ -158,6 +158,10 @@ func close() -> void: _seal.visible = false _seal.modulate = Color.WHITE _cele.visible = false + var tw: Tween = get_meta(&"celebrate_tw", null) # 循环句柄随节点存(跨 open/close,close 必杀) + if tw != null and tw.is_valid(): + tw.kill() + remove_meta(&"celebrate_tw") _clear_crest_bricks() @@ -281,7 +285,11 @@ func _play_dust_sadle() -> void: func _play_celebrate() -> void: if UiKit.motion_scale < 1.0: return + var prev: Tween = get_meta(&"celebrate_tw", null) + if prev != null and prev.is_valid(): + prev.kill() var tw := _cele.create_tween() + set_meta(&"celebrate_tw", tw) tw.set_loops() tw.tween_property(_cele, "position:y", _cele.position.y - 4.0, UiKit.motion(0.6)).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) tw.tween_property(_cele, "position:y", _cele.position.y, UiKit.motion(0.6)).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) diff --git a/game/presentation/views/battle_stage_view.gd b/game/presentation/views/battle_stage_view.gd index 29b6db38..78ab8b5e 100644 --- a/game/presentation/views/battle_stage_view.gd +++ b/game/presentation/views/battle_stage_view.gd @@ -305,7 +305,9 @@ func on_step_row_fx(row: Dictionary) -> void: _schedule_arrival_fx(hero_standee, bool(row.get("killed", false))) return hero_standee.set_hero_state("attack") # 我方攻击行——attack 态(短驻自回) - var card: Node2D = _card_by_name(target) + var card: Node2D = _card_by_id(String(row.get("target_id", ""))) + if card == null: + card = _card_by_name(target) # 旧行无 target_id 时名兜底 if card == null: return _schedule_arrival_fx(card, bool(row.get("killed", false))) @@ -338,9 +340,24 @@ func _card_by_name(display_name: String) -> Node2D: return null +# 按敌单位 id 精确解析站桩(同名敌主键——空 id 恒不命中,由调用点名兜底) +func _card_by_id(enemy_id: String) -> Node2D: + if enemy_id == "": + return null + for c in _cards: + if not is_instance_valid(c): + continue + if c.enemy_id() == enemy_id: + return c + return null + + # 跨区弹道目标坐标(结算演出链批——组合根传给 SettlementFxView 的全局锚点;壳层口径 C4) -func enemy_standee_center(display_name: String) -> Variant: - var card := _card_by_name(display_name) +# 解析 id 先、display_name 兜底(旧行无 target_id) +func enemy_standee_center(target_id: String, display_name: String) -> Variant: + var card := _card_by_id(target_id) + if card == null: + card = _card_by_name(display_name) if card == null or not is_instance_valid(card): return null return world_to_shell(card.position + card.box * 0.5) diff --git a/game/presentation/views/board_view.gd b/game/presentation/views/board_view.gd index ba9208a3..0be268d6 100644 --- a/game/presentation/views/board_view.gd +++ b/game/presentation/views/board_view.gd @@ -4,12 +4,14 @@ class_name BoardView extends Control -signal action_requested(cell: Vector2i, kind: String) # kind ∈ {dig, move, seal_place, seal_pull} -signal hover_forbidden(cell: Vector2i) # 非相邻未开格悬停(首遇 toast 归组合根去重) -signal chord_attempted(cell: Vector2i) # 双击快开尝试(chord 肌肉记忆提示锚) +signal action_requested(cell: Vector2i, kind: String) ## kind ∈ {dig, move, seal_place, seal_pull} +signal hover_forbidden(cell: Vector2i) ## 非相邻未开格悬停(首遇 toast 归组合根去重) +signal chord_attempted(cell: Vector2i) ## 双击快开尝试(chord 肌肉记忆提示锚) const _CELL_UNIT := preload("res://presentation/views/board_cell_unit.gd") const _BOARD_WORLD := preload("res://presentation/views/board_world.gd") +const _TILE_PALETTE := preload("res://presentation/views/tile_palette.gd") +const _HERO_SPRITE := preload("res://presentation/views/hero_sprite.gd") const COL_BRICK := UiKit.BRICK # 未开——掩尘完整地砖(色板原语 #C9AF83) const COL_FLOOR := UiKit.FLOOR # 已开——裸土 #B39A78(碎裂裸露地面) @@ -38,35 +40,11 @@ const DEPTH_BEVEL := 0.6 # 顶面倒角强度 # 立面/投影色参数在 CellUnit 常量——绘制侧单点) const WALL_PROJECTION_FRAC := 0.22 -# 英雄单位演出参数(计划书 §3.B/C 时长档) -const HERO_FRAME_PX := 32.0 # 英雄帧画布边长(素材 32×32 锁定——缩放比分母) -const HERO_SPRITE_SCALE := 1.5 # 1.5× 档(实机 1× 偏小故放大 1.5 倍——本档为圣经 §8.2「禁非整数缩放」 - # 一般规则的显式豁免;头可越格上沿不裁剪) -const HERO_GROUND_FRAC := 0.25 # 脚锚纵向落点 = 格底往上 25%(口径「脚踩格子 1/4 处·从下往上数」; - # 头越上格沿造立体感。⚠ 参照系纪律:本值一律自底边起算,禁改用「自顶 75%」 - # 一类的同线异名表述) -const HERO_MOVE_S := 0.14 # 位移时长(~140ms) -const HERO_STEP_S := 0.18 # 走路帧周期 = 原地撞墙帧闪时长(~180ms) -const HERO_DIG_S := 0.15 # 挖掘弹跳总时长(~150ms,半程压半程回) -const HERO_BOUNCE := 0.82 # 弹跳压缩比(scale.y 1→0.82→1 脚底收敛) -const HERO_SHADOW_PX := Vector2(32, 12) # 脚下椭圆影屏幕尺寸(Godot 组件合成——径向渐变纹理压扁, - # 贴图零改动,走引擎侧) +# 英雄单位演出参数单点 = HeroSprite 常量族(本处只保留探针直读面别名,数值禁二写) +const HERO_SPRITE_SCALE := _HERO_SPRITE.SPRITE_SCALE # 1.5× 档(探针 case⑨/⑩ 直读面) +const HERO_GROUND_FRAC := _HERO_SPRITE.GROUND_FRAC # 脚锚纵向落点 25%(探针 case⑦/⑩ 直读面) -# ── 高墙/地面瓦片纹理路径单点 ── -# 素材 PNG 不入 git——一律 UiKit.load_tex(exists 守卫)装载,禁 preload; -# 缺位返回 null,整批任一缺位 = 弃用瓦片走程序化平色回退(BRICK/FLOOR,fresh checkout 冒烟不炸)。 -# 路径常量落本文件单点。词表 = -# design/research/board-tile-vocab-plan-a-2026-09-26.md(16 唯一墙瓦片 + 5 地面变体; -# 东/东北/东南/end_e/in_ne/in_se 六镜像件运行时 flip_x 合成,不出文件——词表 §二)。 -# 路径按 board_type 后缀参数化——terrain_<件名>_.png; -# 词表 §一 16 唯一墙瓦片名单单点在本常量数组。underdark/deep_underdark 两套 = -# tools/recolor_terrain_tiles.py 程序换色产;任一套缺位 → 整批回落 -# dungeon 套(禁同板混渲染);dungeon 套亦缺位 → 程序化平色回退(fresh checkout 冒烟不炸)。 -const TILE_WALL_NAMES := ["wall_center", "wall_center_crack", "wall_center_speckle", "wall_n", "wall_s", "wall_w", "wall_nw", "wall_sw", "wall_hrun", "wall_vrun", "wall_end_n", "wall_end_s", "wall_end_w", "wall_lone", "wall_in_nw", "wall_in_sw"] -const TILE_WALL_FMT := "res://assets/art/board/terrain_%s_%s.png" # %s=件名 %s=board_type -const TILE_SEAL_MARK := "res://assets/art/board/seal_mark.png" # 封印印记纹理(独立可选件) -const TILE_FLOOR_FMT := "res://assets/art/board/terrain_floor_%s_%d.png" # %s=board_type 1..%d 变体 -const TILE_FLOOR_VARIANTS := 5 # 地面瓦片变体数(词表 §一 floor_1..5) +# 瓦片纹理路径与 16 墙瓦片词表单点 = TilePalette 常量族(装载/查表同件;词表注释见该件) var _wrap: CenterContainer var _svp_container: SubViewportContainer @@ -96,26 +74,54 @@ var _desat_mat: ShaderMaterial var _desat_tween: Tween var _cell := 46.0 # 当前格边长(set_cell_area 按容器尺寸重算——battle.md 格尺寸公式) -# ── 英雄单位精灵状态(素材 PNG 不入 git——load_tex 守卫装载,缺位=字形回退)── -var _hero_tex_idle: Texture2D # 站立帧(null = 素材缺位——零精灵,字形「人」原路径) -var _hero_tex_walk: Texture2D # 跨步帧 -var _hero_last := Vector2i(-99, -99) # 上拍英雄格(refresh diff 基准;哨兵 = 未落位/随 rebuild 复位) -var _hero_face_left := true # 朝向(素材只入库朝左帧——右向 = flip_h,计划书 §2 约定) -var _hero_bump_pending := false # 受理零位移移动回执(refresh diff 只认位置变化,原地移动须外部告知) -var _hero_move_tw: Tween -var _hero_bump_tw: Tween -var _hero_dig_tw: Tween +# ── 拆出件(状态单点在件内)── +var _palette = _TILE_PALETTE.new() # 瓦片件:装载缓存+查表+明度/墨族静态算法 +var _hero_sprite = _HERO_SPRITE.new() # 英雄精灵件:_init 挂为本件子级(tween 需在树上) -# ── 瓦片状态(素材缺位 = 全 false/空——程序化平色回退)── -var _tiles := {} # 词表键 → Texture2D(镜像件已预翻转;装载见 _load_board_tiles) -var _floor_tiles: Array[Texture2D] = [] # 地面瓦片五变体(下标 0..4 = floor_1..5) -var _tiles_ok := false # false = 素材缺位/未装载——bg_sb 程序化平色原路径 -var _seal_tex: Texture2D = null # 封印印记纹理(缺盘=null=字形「封印」回退——独立可选件不连瓦片整批) -var _tiles_board_type := "dungeon" # 当前装载瓦片套的 board_type(rebuild 按板切换) -var _digit_dark_ink := true # 数字墨族按板自适应(true=深墨三档)。缺盘平色回退态 #B39A78 裸土 - # 为中明度底——深墨族 CR 4.96 达标、亮墨族仅 2.2,故缺盘默认深墨;瓦片装载后按实测分位数覆写 -var _digit_plate := false # 数字色牌垫底模式(双峰纹理板——直画两族皆不过门槛时落牌, - # 素牌+深墨 12:1 纹理免疫;渲染帧取证 underdark 系深墨直画隐形,故启用色牌) +# ── 件成员同名转发(探针直读面零改动:读 _seal_tex、读写 _hero_tex_idle;其余为随迁状态单点再导出)── +var _tiles: Dictionary[String, Texture2D]: + get: + return _palette.tiles +var _floor_tiles: Array[Texture2D]: + get: + return _palette.floor_tiles +var _tiles_ok: bool: + get: + return _palette.tiles_ok +var _seal_tex: Texture2D: + get: + return _palette.seal_tex +var _tiles_board_type: String: + get: + return _palette.tiles_board_type +var _digit_dark_ink: bool: + get: + return _palette.digit_dark_ink +var _digit_plate: bool: + get: + return _palette.digit_plate +var _hero_tex_idle: Texture2D: + get: + return _hero_sprite.tex_idle + set(v): + _hero_sprite.tex_idle = v +var _hero_tex_walk: Texture2D: + get: + return _hero_sprite.tex_walk +var _hero_last: Vector2i: + get: + return _hero_sprite.last + set(v): + _hero_sprite.last = v +var _hero_move_tw: Tween: + get: + return _hero_sprite.move_tw +var _hero_bump_tw: Tween: + get: + return _hero_sprite.bump_tw +var _hero_dig_tw: Tween: + get: + return _hero_sprite.dig_tw # ---- 视野迷雾(纯呈现层;现行事实源=HUD框架「视野迷雾」节 v0.13,渲染规格=art-bible §6.5 v3.5)---- const FOG_RADIUS := 8 # 菱形灯光区基准半径(曼哈顿距离 ≤R——与微光环「周围 8 格」同圈起步;半径基准值改动=非白名单须用户) @@ -149,12 +155,15 @@ func _init() -> void: _svp_container.add_child(_viewport) _world = _BOARD_WORLD.new() _viewport.add_child(_world) + _hero_sprite.bind(_world, _svp_container) + _hero_sprite.set_cell_size(_cell, float(SEP)) + add_child(_hero_sprite) _desat_mat = ShaderMaterial.new() _desat_mat.shader = load("res://presentation/views/desaturation.gdshader") _desat_mat.set_shader_parameter("strength", 0.0) _hero_tex_idle = UiKit.load_tex(UiKit.TEX_HERO_IDLE) _hero_tex_walk = UiKit.load_tex(UiKit.TEX_HERO_WALK) - _load_board_tiles() # 高墙/地面瓦片整批守卫装载(缺位静默回退——英雄同款纪律,禁刷屏) + _palette.load_board_tiles() # 高墙/地面瓦片整批守卫装载(缺位静默回退——英雄同款纪律,禁刷屏) func board() -> BoardData: @@ -232,6 +241,7 @@ func set_cell_area(avail: Vector2) -> void: var reserve := 24.0 + d # 垂直总预留(上 12 + 下 12 + 悬崖外延——悬崖占预算防溢出) var cell := int(floor((minf(avail.x - 12.0 - d, avail.y - reserve) - float((n - 1) * sep)) / float(n))) _cell = float(clampi(cell, 26, 46)) + _hero_sprite.set_cell_size(_cell, float(SEP)) var stride := _cell + float(SEP) for c in _cells: var u: Node2D = _cells[c] @@ -258,6 +268,7 @@ func _refresh_slab() -> void: func rebuild(board: BoardData, minesweeper: MinesweeperRules) -> void: _board = board _minesweeper = minesweeper + _palette.bind_board(_board, _minesweeper) # 查表族棋盘态随板同步(换板后首拍 refresh 前置) for child in _world.get_children(): child.queue_free() _world.hero = null # 英雄精灵随清场复位——下拍 refresh 经 ensure_hero 重建(首建即时落位) @@ -274,7 +285,7 @@ func rebuild(board: BoardData, minesweeper: MinesweeperRules) -> void: # 同图型重复 rebuild 零重载,_tiles_board_type 守卫) var bt: String = _board.board_type if _board.board_type != "" else "dungeon" if bt != _tiles_board_type: - _load_board_tiles(bt) + _palette.load_board_tiles(bt) var stride := _cell + float(SEP) for y in _board.height: for x in _board.width: @@ -380,91 +391,27 @@ func _install_entrance_fx() -> void: _ripple_tws.append(tw) -# ---- 英雄单位精灵(计划书 §3.A/B;格面五 texture 槽零改动) ---- +# ---- 英雄单位精灵(件 = hero_sprite.gd;本视图保留调用面与探针直读转发) ---- -# refresh diff 驱动:位置变化 = 位移演出(140ms Tween + 走路帧);零位移但有受理移动回执 = 只播帧 -# 不位移(撞墙语义——排雷玩法「目标=当前格」幂等受理)。素材缺位 = 零精灵,字形「人」回退路径。 +# refresh diff 驱动(件内实现:位置变化 = 位移演出;零位移但受理移动回执 = 只播帧闪) func _update_hero(hero: Vector2i) -> void: - if _hero_tex_idle == null: - return - if _world.hero == null: - var sp: Sprite2D = _world.ensure_hero(_hero_tex_idle, HERO_SHADOW_PX, HERO_SPRITE_SCALE) - sp.scale = Vector2.ONE * HERO_SPRITE_SCALE - sp.position = _hero_feet(hero) - _hero_last = hero # 首建/随 rebuild 重建——即时落位无演出 - return - if hero == _hero_last and not _hero_bump_pending: - return - if hero != _hero_last and hero.x != _hero_last.x: - _hero_face_left = hero.x < _hero_last.x - _place_hero(_hero_last, hero, hero == _hero_last) - if hero == _hero_last: - _hero_frame_flash() - _hero_bump_pending = false - _hero_last = hero + _hero_sprite.update_hero(hero) -# 英雄落位(instant = 即位无演出——首建/格边长变更;否则 140ms 位移 + 走路帧起手、到位回站帧。 -# 帧权威单一化:Sprite2D 换纹理路线(计划书 §3.B)——换纹理+flip_h 同拍写定即生效, -# 无 AnimatedSprite2D 的 play()/advance(0) 错向面) +# 英雄落位(instant = 即位无演出——首建/格边长变更;否则 140ms 位移 + 走路帧起手、到位回站帧) func _place_hero(from: Vector2i, to: Vector2i, instant: bool) -> void: - var sp: Sprite2D = _world.hero - if sp == null: - return - var s := HERO_SPRITE_SCALE - var feet := _hero_feet(to) - if _hero_move_tw != null and _hero_move_tw.is_valid(): - _hero_move_tw.kill() - if instant: - sp.scale = Vector2(s, s) - sp.position = feet - sp.texture = _hero_tex_idle - return - sp.scale = Vector2(s, s) # 位移重置弹跳残形(scale.y 由 play_hero_dig_bounce 独立回收) - sp.texture = _hero_tex_walk - sp.flip_h = not _hero_face_left # 素材基准朝左 - _hero_move_tw = create_tween() - _hero_move_tw.tween_property(sp, "position", feet, UiKit.motion(HERO_MOVE_S)).set_trans(Tween.TRANS_SINE) - _hero_move_tw.tween_callback(func() -> void: - if is_instance_valid(sp): - sp.texture = _hero_tex_idle) - - -# 原地移动帧闪(撞墙语义):跨步帧 ~180ms 后回站帧,零位移 -func _hero_frame_flash() -> void: - var sp: Sprite2D = _world.hero - if sp == null: - return - if _hero_bump_tw != null and _hero_bump_tw.is_valid(): - _hero_bump_tw.kill() - sp.texture = _hero_tex_walk - sp.flip_h = not _hero_face_left - _hero_bump_tw = create_tween() - _hero_bump_tw.tween_interval(UiKit.motion(HERO_STEP_S)) - _hero_bump_tw.tween_callback(func() -> void: - if is_instance_valid(sp): - sp.texture = _hero_tex_idle) + _hero_sprite.place_hero(from, to, instant) # 受理移动指令回执(组合根 settle 受理后、refresh 前调用)——原地移动经此触发帧闪; # 目标异格时本封印标被 diff 位移路径自然吞掉(置位即清) func notify_hero_move() -> void: - _hero_bump_pending = true + _hero_sprite.notify_move() -# 挖掘角色弹跳(计划书 §3.C:scale.y 1→0.82→1,脚底锚收敛不悬空;与位移/帧闪 tween 分开管理 -# 避免属性争抢——旧弹跳先 kill) +# 挖掘角色弹跳(计划书 §3.C:scale.y 1→0.82→1,脚底锚收敛不悬空;旧弹跳先 kill) func play_hero_dig_bounce() -> void: - var sp: Sprite2D = _world.hero - if sp == null or _hero_tex_idle == null: - return - if _hero_dig_tw != null and _hero_dig_tw.is_valid(): - _hero_dig_tw.kill() - var s := HERO_SPRITE_SCALE - sp.scale = Vector2(s, s) - _hero_dig_tw = create_tween() - _hero_dig_tw.tween_property(sp, "scale:y", s * HERO_BOUNCE, UiKit.motion(HERO_DIG_S * 0.5)).set_trans(Tween.TRANS_SINE) - _hero_dig_tw.tween_property(sp, "scale:y", s, UiKit.motion(HERO_DIG_S * 0.5)).set_trans(Tween.TRANS_SINE) + _hero_sprite.play_dig_bounce() # 格左上角(board_area 全幅坐标系)——fx 同层演出定位出口(本视图与 fx 同为 board_area 全幅子件, @@ -473,25 +420,15 @@ func cell_origin(c: Vector2i) -> Vector2: return _svp_container.position + Vector2(c) * (_cell + float(SEP)) -# 英雄脚底锚点——**世界坐标**(精灵在 SubViewport 世界内:格原点 = 格×步距,禁带壳层居中偏移—— -# cell_origin 含 _svp_container.position,世界内精灵定位误用会整体右偏 ~7 格+偏离格心)。 -# 纵向 = 格底往上 25% 线(脚踩格子 1/4 处);横向格中线。身体向上延伸, -# 头越上格沿不裁剪(造立体感);锚点与精灵缩放解耦(缩放只改身体向上长多少)。 -func _hero_feet(c: Vector2i) -> Vector2: - return Vector2(c) * (_cell + float(SEP)) + Vector2(_cell * 0.5, _cell * (1.0 - HERO_GROUND_FRAC)) - - -# 英雄持握手位(fx 同层坐标系 = 壳层坐标——fx 在 SubViewport 之外,须含 _svp_container.position -# 居中偏移:世界脚底锚 + 壳层偏移即得)。手 = 盒底往上 1/4 盒高、水平居中( -# 盒高随 HERO_SPRITE_SCALE——手位钉在身体上不随缩放漂移) +# 英雄持握手位(fx 同层坐标系 = 壳层坐标——件内换算含 _svp_container.position 居中偏移: +# 世界脚底锚 + 壳层偏移即得) func hero_hand_point() -> Vector2: - return _svp_container.position + _hero_feet(_hero_last) \ - + Vector2(0.0, -HERO_FRAME_PX * 0.25 * HERO_SPRITE_SCALE) + return _hero_sprite.hand_point() # 英雄精灵缩放档(fx 挥铲同档出口——两演出比例恒一致) func hero_sprite_scale() -> float: - return HERO_SPRITE_SCALE + return _hero_sprite.sprite_scale() # 真实输入路径(行为探针复用):合成点击 → 与真实 gui_input 同一翻译出口 @@ -690,7 +627,7 @@ func _badge_glyph(category: String) -> String: # 数字三档色温(按板自适应双族——v3.5 冻结④):亮墨族 1-3 冷亮/4-5 暖白/6-8 金亮 #(dungeon 瓦片实测达标维持);深墨族 1-3 青墨/4-5 赭墨/6-8 锈墨(色温语义同构——underdark/ -# deep_underdark 亮白筋纹瓦片上亮墨对亮斑 CR 1.0–1.9 融字,深墨族 5.7–11.1;选族判据见 _digit_ink_mode) +# deep_underdark 亮白筋纹瓦片上亮墨对亮斑 CR 1.0–1.9 融字,深墨族 5.7–11.1;选族判据见 TilePalette.digit_ink_mode) func _digit_ink(d: int) -> Color: if _digit_dark_ink: if d <= 3: @@ -924,236 +861,16 @@ func _reset_breath() -> void: _contact_unit.queue_redraw() -# ---- 高墙/地面瓦片接线(词表 = design/research/board-tile-vocab-plan-a-2026-09-26.md) ---- +# ---- 瓦片装载与查表(件 = tile_palette.gd;本视图仅保留格面渲染消费面) ---- -# 瓦片整批守卫装载(_init 单次调用):21 枚全成才启用——任一缺位按整批缺位处理(禁同板混渲染; -# fresh checkout 无素材目录 → 静默全回退)。缺位不 push_error——可选素材缺位是预期路径非故障 -# (英雄 load_tex 纪律同款,防冒烟刷屏)。镜像件运行时 flip_x 合成,不出文件。 -func _load_board_tiles(board_type: String = "dungeon") -> void: - if board_type == "": - board_type = "dungeon" - # 16 墙瓦片装载循环节(词表 TILE_WALL_NAMES 单点; - # 词表本身被 terrain_tile_sets_test 直读,保留) - var t := {} - for wall_name in TILE_WALL_NAMES: - t[wall_name] = UiKit.load_tex(TILE_WALL_FMT % [wall_name, board_type]) - var floors: Array[Texture2D] = [] - for i in range(1, TILE_FLOOR_VARIANTS + 1): - floors.append(UiKit.load_tex(TILE_FLOOR_FMT % [board_type, i])) - for k in t: - if t[k] == null: - _request_dungeon_fallback(board_type) - return - for tex in floors: - if tex == null: - _request_dungeon_fallback(board_type) - return - # 地板归一(圣经 v3.5 冻结①/§1.4——已开格底归一世界底层档 L≈0.10,带 0.08–0.14): - # 方向稿 _v3_darken_floors 同式移植;现行瓦片 L≈0.036 低于带下沿故实为提亮; - # 本归一为程序代役(逐批装载时对 freshly-loaded 原图测光,幂等)。归一后逐像素明度顺路采集, - # 驱动数字墨族按板自适应(_digit_ink_mode——均值分位同源同像素,禁二次解码) - var lumas: Array[Variant] = [] - for i in range(floors.size()): - floors[i] = _normalize_floor_luma(floors[i], 0.10, lumas) - var ink_mode := _digit_ink_mode(lumas) - _digit_dark_ink = bool(ink_mode.dark) - _digit_plate = bool(ink_mode.plate) - # 六镜像件(词表 §二:wall_e←wall_w / wall_ne←wall_nw / wall_se←wall_sw / - # wall_end_e←wall_end_w / wall_in_ne←wall_in_nw / wall_in_se←wall_in_sw;只水平翻转禁旋转 - # ——北受光不受损) - t["wall_e"] = _flip_tex(t["wall_w"] as Texture2D) - t["wall_ne"] = _flip_tex(t["wall_nw"] as Texture2D) - t["wall_se"] = _flip_tex(t["wall_sw"] as Texture2D) - t["wall_end_e"] = _flip_tex(t["wall_end_w"] as Texture2D) - t["wall_in_ne"] = _flip_tex(t["wall_in_nw"] as Texture2D) - t["wall_in_se"] = _flip_tex(t["wall_in_sw"] as Texture2D) - _tiles = t - _floor_tiles = floors - _tiles_board_type = board_type - _tiles_ok = true - # 封印印记纹理(独立可选件:缺盘=字形回退,不触发瓦片整批回落语义) - _seal_tex = UiKit.load_tex(TILE_SEAL_MARK) - - -# 整批回落:请求套缺位 → 回落 dungeon 套(禁同板混渲染);dungeon 本身缺位 = -# 维持程序化平色回退原语义(_tiles_ok 恒 false,缺位不刷屏纪律不变) -func _request_dungeon_fallback(requested: String) -> void: - if requested != "dungeon": - _load_board_tiles("dungeon") - - -# 水平镜像贴图运行时合成(flip_h 语义):Image 级逐位翻转后重建 ImageTexture——CellUnit 零改动。 -# VRAM 压缩源先解压(flip_x 不支持压缩格式);32×32 原尺寸逐位翻转,无 ±1px 取整问题 -static func _flip_tex(tex: Texture2D) -> Texture2D: - var img := tex.get_image() - if img.is_compressed(): - img.decompress() - img.flip_x() - return ImageTexture.create_from_image(img) - - -# 地板瓦片明度归一(方向稿 _v3_darken_floors 同式移植):8×8 双线性重采样均值测光 -# (sRGB→线性域,Rec.709 系数),增益 k=(目标/实测)^(1/2.2) 钳 [0.6,1.8],逐通道 sRGB 域线性拉伸。 -# 输入恒为 freshly-loaded 原图(load_tex 每次盘读)——幂等可重入;32×32 五变体开销可忽略 -static func _normalize_floor_luma(tex: Texture2D, target_l: float = 0.10, lumas_out: Variant = null) -> Texture2D: - var img: Image = tex.get_image() - if img.is_compressed(): - img.decompress() - img.convert(Image.FORMAT_RGBA8) - var small: Image = img.duplicate() - small.resize(8, 8, Image.INTERPOLATE_BILINEAR) - var sum := 0.0 - for y in range(8): - for x in range(8): - var p := small.get_pixel(x, y) - sum += 0.2126 * _srgb_lin(p.r) + 0.7152 * _srgb_lin(p.g) + 0.0722 * _srgb_lin(p.b) - var k := pow(target_l / maxf(sum / 64.0, 0.001), 1.0 / 2.2) - k = clampf(k, 0.6, 1.8) - for y in range(img.get_height()): - for x in range(img.get_width()): - var c := img.get_pixel(x, y) - var nr := minf(c.r * k, 1.0) - var ng := minf(c.g * k, 1.0) - var nb := minf(c.b * k, 1.0) - img.set_pixel(x, y, Color(nr, ng, nb, c.a)) - if lumas_out != null: - lumas_out.append(0.2126 * _srgb_lin(nr) + 0.7152 * _srgb_lin(ng) + 0.0722 * _srgb_lin(nb)) - return ImageTexture.create_from_image(img) - - -static func _srgb_lin(c: float) -> float: - return c / 12.92 if c <= 0.04045 else pow((c + 0.055) / 1.055, 2.4) - - -# ---- 数字墨族按板自适应(墨色按关自适应+三关对比断言)---- - -const DIGIT_INK_CR_P90 := 4.5 # 墨族对地板 p90 亮斑对比度门槛(失效模式=亮白筋纹冲字,WCAG 数字体同源) -const DIGIT_INK_CR_MEAN_BRIGHT := 2.5 # 亮族对地板均值门槛(中调防吃字) -const DIGIT_INK_CR_MEAN_DARK := 3.0 # 深族对地板均值门槛(大字 AA 级——2.4 的实测观感=隐形) - -# 归一后地板逐像素明度 → 选数字呈现模式。三态:亮族直画(暗纹理板)/深族直画(中暗板)/ -# 色牌垫底(双峰纹理板——近黑斑与近白筋并存时任何墨族直画必融一峰,渲染帧取证 -# 实证 underdark 系深墨隐形,牌上深墨 12:1 纹理免疫)。家族最弱成员(亮族=金亮/深族=锈墨) -# 对 p90 与均值双门槛;亮族过选亮族,否则深族(均值门 3.0),两族皆不过落牌。 -# 实测:dungeon p90=0.121 亮族 4.79/5.80 ✓ 直画;underdark p90=0.406 亮族 ✗、 -# 深族 5.34/**2.41**(均值 <3.0 ✗)→ 落牌;deep_underdark 同→落牌 -static func _digit_ink_mode(lumas: Array) -> Dictionary: - var sorted := lumas.duplicate() - sorted.sort() - var n := sorted.size() - if n == 0: - return {"dark": false, "plate": false, "cr_p90_bright": 0.0, "cr_mean_bright": 0.0, "cr_p90_dark": 0.0, "cr_mean_dark": 0.0} - var p90: float = sorted[int(n * 0.9)] - var mean := 0.0 - for v in sorted: - mean += v - mean /= n - var cr_p90_b := _contrast_y(UiKit.INK_L_GOLD, p90) - var cr_mean_b := _contrast_y(UiKit.INK_L_GOLD, mean) - var cr_p90_d := _contrast_y(UiKit.INK_RUST, p90) - var cr_mean_d := _contrast_y(UiKit.INK_RUST, mean) - var bright_ok := cr_p90_b >= DIGIT_INK_CR_P90 and cr_mean_b >= DIGIT_INK_CR_MEAN_BRIGHT - var dark_ok := cr_p90_d >= DIGIT_INK_CR_P90 and cr_mean_d >= DIGIT_INK_CR_MEAN_DARK - var plate := false - var dark := false - if bright_ok: - dark = false - elif dark_ok: - dark = true - else: - plate = true # 双峰纹理:直画不可救,色牌兜底(素牌+深墨 12:1) - return {"dark": dark, "plate": plate, "cr_p90_bright": cr_p90_b, "cr_mean_bright": cr_mean_b, - "cr_p90_dark": cr_p90_d, "cr_mean_dark": cr_mean_d} - - -static func _contrast_y(ink: Color, bg: float) -> float: - var y := _ink_y(ink) - return (maxf(y, bg) + 0.05) / (minf(y, bg) + 0.05) - - -static func _ink_y(c: Color) -> float: - return 0.2126 * _srgb_lin(c.r) + 0.7152 * _srgb_lin(c.g) + 0.0722 * _srgb_lin(c.b) - - -# 格坐标确定性散列(禁 RNG/禁 randomize 纪律——「初始化随机落定禁逐轮重掷」圣经原则三落点: -# 同格坐标恒同值,refresh/重绘/逐轮不变)。乘加混合 + 移位雪崩;64 位整型乘法回绕即哈希语义 -#(GDScript int 溢出静默回绕),板域 10×10/12×12 内 mod 10/mod 5 分布均匀 -static func _cell_hash(c: Vector2i) -> int: - var h := c.x * 374761393 + c.y * 668265263 - h = (h ^ (h >> 13)) * 1274126177 - return h ^ (h >> 16) - - -# 四正交邻墙判定(词表 §三.1:板外视为墙;未开格=墙、已开格=地板——世界观口径「未开=墙、 -# 已开=挖出的空间」)。唯一调用面 _paint_cell(refresh 对 _minesweeper 非空守卫在先) -func _is_wall(c: Vector2i) -> bool: - if c.x < 0 or c.y < 0 or c.x >= _board.width or c.y >= _board.height: - return true - return not _minesweeper.is_revealed(c) - - -# 墙瓦片查表(词表 §一 16 正交情形映射 + §三.2 凹角回落):nf/ef/sf/wf = 北/东/南/西邻是地板。 -# 16 情形 = 0 地板(center 族)+ 3 地板×4(端头)+ 2 地板×6(条带/凸角)+ 1 地板×4(单缘) -# + 4 地板(lone),逐分支穷尽 +# 墙瓦片查表(词表 §一 16 正交情形映射 + §三.2 凹角回落;查表用棋盘态由件持 bind_board 同步) func _wall_tile_for(c: Vector2i) -> Texture2D: - var nf := not _is_wall(c + Vector2i(0, -1)) - var ef := not _is_wall(c + Vector2i(1, 0)) - var sf := not _is_wall(c + Vector2i(0, 1)) - var wf := not _is_wall(c + Vector2i(-1, 0)) - if nf and ef and sf and wf: - return _wall_center_tile(c) # 四邻皆墙 → 对角凹角/中心三变体(下位函数) - if nf and ef and sf: - return _tiles["wall_end_w"] # 仅西墙(端头·东连墙) - if ef and sf and wf: - return _tiles["wall_end_s"] # 仅北墙(端头·南连地板、北连墙) - if nf and sf and wf: - return _tiles["wall_end_e"] # 仅东墙(= end_w 镜像) - if nf and ef and wf: - return _tiles["wall_end_n"] # 仅南墙(端头·北向死头、南连墙) - if nf and sf: - return _tiles["wall_hrun"] # 上下地板 = 横条带中段 - if ef and wf: - return _tiles["wall_vrun"] # 左右地板 = 竖条带中段 - if nf and wf: - return _tiles["wall_nw"] # 凸角(上+左地板) - if nf and ef: - return _tiles["wall_ne"] # 凸角镜像 - if sf and wf: - return _tiles["wall_sw"] # 凸角(下+左地板) - if sf and ef: - return _tiles["wall_se"] # 凸角镜像 - if nf: - return _tiles["wall_n"] # 北缘(上方地板) - if ef: - return _tiles["wall_e"] # 东缘(镜像件) - if sf: - return _tiles["wall_s"] # 南缘(下方地板) - return _tiles["wall_w"] # 西缘(仅西地板——16 情形穷尽收尾) + return _palette.wall_tile_for(c) -# 四邻皆墙格:对角凹角优先 nw > ne > sw > se(词表 §三.2——双凹角不做专件,多对角地板回落 -# 首中单凹角件,残留 1px 缝为可接受限);无对角地板 → 中心三变体按格坐标哈希 60/20/20 -func _wall_center_tile(c: Vector2i) -> Texture2D: - if not _is_wall(c + Vector2i(-1, -1)): - return _tiles["wall_in_nw"] # 西北对角是地板 - if not _is_wall(c + Vector2i(1, -1)): - return _tiles["wall_in_ne"] # 东北对角是地板(镜像件) - if not _is_wall(c + Vector2i(-1, 1)): - return _tiles["wall_in_sw"] # 西南对角是地板 - if not _is_wall(c + Vector2i(1, 1)): - return _tiles["wall_in_se"] # 东南对角是地板(镜像件) - var m := posmod(_cell_hash(c), 10) - if m < 6: - return _tiles["wall_center"] # 6/10 = 60% - if m < 8: - return _tiles["wall_center_crack"] # 2/10 = 20% - return _tiles["wall_center_speckle"] # 2/10 = 20% - - -# 地面瓦片五变体按格坐标哈希均匀分配(词表 §一 floor_1..5 / §三.3「同法哈希分配」; -# posmod 保非负下标) +# 地面瓦片五变体按格坐标哈希分配(词表 §三.3「同法哈希分配」) func _floor_tile_for(c: Vector2i) -> Texture2D: - return _floor_tiles[posmod(_cell_hash(c), _floor_tiles.size())] + return _palette.floor_tile_for(c) # ---- 自绘光标纹理(镐/封印——引擎内置光标无此语义;程序化占位,headless 跳过) ---- diff --git a/game/presentation/views/board_world.gd b/game/presentation/views/board_world.gd index d3eaa73f..1145a970 100644 --- a/game/presentation/views/board_world.gd +++ b/game/presentation/views/board_world.gd @@ -15,7 +15,7 @@ var hero: Sprite2D = null # 英雄单位精灵(移动实体独立于 CellU #(结算期与 CellUnit 同口径)。返回值便于 BoardView 即时落位。 # 脚下椭圆影(走 Godot 组件合成、贴图零改动):径向渐变纹理 ×黑色半透明 # modulate 压扁,挂英雄子件(z_index=-1 相对回落格面之上、英雄之下——位移/翻转/弹跳随父件 -# 同步免费拿;尺寸换算归 BoardView.HERO_SHADOW_PX 常量)。纹理 filter 用 LINEAR 保渐变平滑 +# 同步免费拿;尺寸换算归 HeroSprite.SHADOW_PX 常量)。纹理 filter 用 LINEAR 保渐变平滑 #(NEAREST 会把渐变打回条带)。 func ensure_hero(tex: Texture2D, shadow_px: Vector2, scale: float) -> Sprite2D: if hero != null: diff --git a/game/presentation/views/camp_overlay_view.gd b/game/presentation/views/camp_overlay_view.gd index 543c0380..36d95259 100644 --- a/game/presentation/views/camp_overlay_view.gd +++ b/game/presentation/views/camp_overlay_view.gd @@ -7,12 +7,12 @@ extends Control signal buy_requested(attr: String) signal closed -signal attune_toggled(item_id: String) # 同调装配切换意图(写口唯一归 MetaProgression.toggle_attunement) -signal tree_buy_requested(node_id: String) # 天赋树购买意图(写口唯一归 MetaProgression.tree_buy) -signal tree_fork_toggled(group: String, side: String) # 双态切换意图(一节点双态——零币切态) -signal forge_buy_requested(route_id: String) # 锻造路线购买意图(写口唯一归 MetaProgression.forge_buy_level) -signal forge_unlock_requested(route_id: String) # 锻造路线解锁意图(写口唯一归 MetaProgression.forge_buy_unlock) -signal forge_select_requested(route_id: String) # 锻造路线选中意图(写口唯一归 MetaProgression.forge_select_weapon/armor) +signal attune_toggled(item_id: String) ## 同调装配切换意图(写口唯一归 MetaProgression.toggle_attunement) +signal tree_buy_requested(node_id: String) ## 天赋树购买意图(写口唯一归 MetaProgression.tree_buy) +signal tree_fork_toggled(group: String, side: String) ## 双态切换意图(一节点双态——零币切态) +signal forge_buy_requested(route_id: String) ## 锻造路线购买意图(写口唯一归 MetaProgression.forge_buy_level) +signal forge_unlock_requested(route_id: String) ## 锻造路线解锁意图(写口唯一归 MetaProgression.forge_buy_unlock) +signal forge_select_requested(route_id: String) ## 锻造路线选中意图(写口唯一归 MetaProgression.forge_select_weapon/armor) # 方向稿取色(柜台头带深棕/行板暖米;双色墨=亮板配深墨)——值单点收 UiKit const HEADER_BAND_COL := UiKit.CAMP_HEADER_BAND diff --git a/game/presentation/views/debug_overlay.gd b/game/presentation/views/debug_overlay.gd index cf46d111..19128e92 100644 --- a/game/presentation/views/debug_overlay.gd +++ b/game/presentation/views/debug_overlay.gd @@ -4,8 +4,8 @@ class_name DebugOverlay extends Control -signal apply_requested(atk_pct: int, hp_pct: int) # 应用并关闭——加成落表归组合根单一应用路径 -signal simulate_requested(count: int) # 批量模拟编排归组合根(表基线复位 + 单路径施加) +signal apply_requested(atk_pct: int, hp_pct: int) ## 应用并关闭——加成落表归组合根单一应用路径 +signal simulate_requested(count: int) ## 批量模拟编排归组合根(表基线复位 + 单路径施加) signal debug_levels_requested # 两层调试对局入口(编排归 debug_session.gd) signal diagnose_requested # 诊断转储入口(卡死取证编排归组合根) signal closed @@ -81,7 +81,6 @@ func set_sim_running(running: bool) -> void: # 在活跃对局并存态不开放(长程异步模拟/并发对局编排与活跃结算并存态破坏状态机) func set_context_locked(locked: bool) -> void: _ctx_locked = locked - _dbg_btn.disabled = _ctx_locked _sync_sim_button() @@ -97,9 +96,12 @@ func run_data_probe() -> Dictionary: return _run_data.duplicate() +# 两钮同判据(模拟运行 / 对局上下文锁定对「模拟」「两层调试」两入口等价)—— +# 判据单点:两 setter 都经此同步,禁在调用点各写一份 func _sync_sim_button() -> void: _sim_btn.disabled = _sim_running or _ctx_locked _sim_btn.text = "模拟中…" if _sim_running else "运行模拟" + _dbg_btn.disabled = _sim_running or _ctx_locked func set_sim_progress(text: String) -> void: diff --git a/game/presentation/views/hero_info_panel_view.gd b/game/presentation/views/hero_info_panel_view.gd index bc020fff..7b1c5ca5 100644 --- a/game/presentation/views/hero_info_panel_view.gd +++ b/game/presentation/views/hero_info_panel_view.gd @@ -8,7 +8,7 @@ const COL_INK := UiKit.INK var _atk_label: Label var _armor_label: Label -signal treasure_slot_clicked(handle: int) # 槽点击(组合根消费:AP 类即用/空间类进选中态) +signal treasure_slot_clicked(handle: int) ## 槽点击(组合根消费:AP 类即用/空间类进选中态) var item_slots: HBoxContainer # 秘宝槽(库存条常读+两步交互第一步) var _slot_panels: Array[Variant] = [] # 槽 PanelContainer 引用(选中高亮/置灰重刷) var _selected_handle := 0 # 选中态(0=无;组合根回写维持单源) diff --git a/game/presentation/views/hero_sprite.gd b/game/presentation/views/hero_sprite.gd new file mode 100644 index 00000000..c1f93cf3 --- /dev/null +++ b/game/presentation/views/hero_sprite.gd @@ -0,0 +1,152 @@ +## VERTICAL SLICE - NOT FOR PRODUCTION +## 战斗屏英雄小人精灵件:落位与位移演出、撞墙帧闪、挖掘弹跳、脚底锚与持握手位出口。 +## Node2D 件(create_tween 系 Node 方法,RefCounted 无此 API)——由棋盘视图在 _init 建为子件, +## 注入世界根与壳层容器引用;精灵本体经世界根 ensure_hero 建于 SubViewport 世界内,本件只驱动其状态。 +## 素材缺位 = 零精灵(棋盘视图字形「人」回退路径在役)。 +class_name HeroSprite +extends Node2D + +# 英雄单位演出参数(计划书 §3.B/C 时长档) +const FRAME_PX := 32.0 # 英雄帧画布边长(素材 32×32 锁定——缩放比分母) +const SPRITE_SCALE := 1.5 # 1.5× 档(实机 1× 偏小故放大 1.5 倍——本档为圣经 §8.2「禁非整数缩放」 + # 一般规则的显式豁免;头可越格上沿不裁剪) +const GROUND_FRAC := 0.25 # 脚锚纵向落点 = 格底往上 25%(口径「脚踩格子 1/4 处·从下往上数」; + # 头越上格沿造立体感。⚠ 参照系纪律:本值一律自底边起算,禁改用「自顶 75%」 + # 一类的同线异名表述) +const MOVE_S := 0.14 # 位移时长(~140ms) +const STEP_S := 0.18 # 走路帧周期 = 原地撞墙帧闪时长(~180ms) +const DIG_S := 0.15 # 挖掘弹跳总时长(~150ms,半程压半程回) +const BOUNCE := 0.82 # 弹跳压缩比(scale.y 1→0.82→1 脚底收敛) +const SHADOW_PX := Vector2(32, 12) # 脚下椭圆影屏幕尺寸(Godot 组件合成——径向渐变纹理压扁, + # 贴图零改动,走引擎侧) + +# ── 精灵状态(素材 PNG 不入 git——装载在棋盘视图 _init,缺位=字形回退)── +var tex_idle: Texture2D # 站立帧(null = 素材缺位——零精灵,字形「人」原路径) +var tex_walk: Texture2D # 跨步帧 +var last := Vector2i(-99, -99) # 上拍英雄格(refresh diff 基准;哨兵 = 未落位/随 rebuild 复位) +var face_left := true # 朝向(素材只入库朝左帧——右向 = flip_h,计划书 §2 约定) +var bump_pending := false # 受理零位移移动回执(refresh diff 只认位置变化,原地移动须外部告知) +var move_tw: Tween +var bump_tw: Tween +var dig_tw: Tween + +# ── 几何注入(棋盘视图 set_cell_area 下发;世界脚底锚公式与壳层手位换算同源)── +var cell := 0.0 # 当前格边长 +var sep := 0.0 # 格间距 +var world: Node2D = null # 世界根(ensure_hero/hero 所在) +var svp: SubViewportContainer = null # 壳层容器(手位须含其居中偏移) + + +func bind(world_ref: Node2D, svp_ref: SubViewportContainer) -> void: + world = world_ref + svp = svp_ref + + +func set_cell_size(cell_size: float, sep_px: float) -> void: + cell = cell_size + sep = sep_px + + +# refresh diff 驱动:位置变化 = 位移演出(140ms Tween + 走路帧);零位移但有受理移动回执 = 只播帧 +# 不位移(撞墙语义——排雷玩法「目标=当前格」幂等受理)。素材缺位 = 零精灵,字形「人」回退路径。 +func update_hero(hero: Vector2i) -> void: + if tex_idle == null: + return + if world.hero == null: + var sp: Sprite2D = world.ensure_hero(tex_idle, SHADOW_PX, SPRITE_SCALE) + sp.scale = Vector2.ONE * SPRITE_SCALE + sp.position = feet(hero) + last = hero # 首建/随 rebuild 重建——即时落位无演出 + return + if hero == last and not bump_pending: + return + if hero != last and hero.x != last.x: + face_left = hero.x < last.x + place_hero(last, hero, hero == last) + if hero == last: + frame_flash() + bump_pending = false + last = hero + + +# 英雄落位(instant = 即位无演出——首建/格边长变更;否则 140ms 位移 + 走路帧起手、到位回站帧。 +# 帧权威单一化:Sprite2D 换纹理路线(计划书 §3.B)——换纹理+flip_h 同拍写定即生效, +# 无 AnimatedSprite2D 的 play()/advance(0) 错向面) +func place_hero(from: Vector2i, to: Vector2i, instant: bool) -> void: + var sp: Sprite2D = world.hero + if sp == null: + return + var s := SPRITE_SCALE + var feet_px := feet(to) + if move_tw != null and move_tw.is_valid(): + move_tw.kill() + if instant: + sp.scale = Vector2(s, s) + sp.position = feet_px + sp.texture = tex_idle + return + sp.scale = Vector2(s, s) # 位移重置弹跳残形(scale.y 由 play_dig_bounce 独立回收) + sp.texture = tex_walk + sp.flip_h = not face_left # 素材基准朝左 + move_tw = create_tween() + move_tw.tween_property(sp, "position", feet_px, UiKit.motion(MOVE_S)).set_trans(Tween.TRANS_SINE) + move_tw.tween_callback(func() -> void: + if is_instance_valid(sp): + sp.texture = tex_idle) + + +# 原地移动帧闪(撞墙语义):跨步帧 ~180ms 后回站帧,零位移 +func frame_flash() -> void: + var sp: Sprite2D = world.hero + if sp == null: + return + if bump_tw != null and bump_tw.is_valid(): + bump_tw.kill() + sp.texture = tex_walk + sp.flip_h = not face_left + bump_tw = create_tween() + bump_tw.tween_interval(UiKit.motion(STEP_S)) + bump_tw.tween_callback(func() -> void: + if is_instance_valid(sp): + sp.texture = tex_idle) + + +# 受理移动指令回执(组合根 settle 受理后、refresh 前调用)——原地移动经此触发帧闪; +# 目标异格时本封印标被 diff 位移路径自然吞掉(置位即清) +func notify_move() -> void: + bump_pending = true + + +# 挖掘角色弹跳(计划书 §3.C:scale.y 1→0.82→1,脚底锚收敛不悬空;与位移/帧闪 tween 分开管理 +# 避免属性争抢——旧弹跳先 kill) +func play_dig_bounce() -> void: + var sp: Sprite2D = world.hero + if sp == null or tex_idle == null: + return + if dig_tw != null and dig_tw.is_valid(): + dig_tw.kill() + var s := SPRITE_SCALE + sp.scale = Vector2(s, s) + dig_tw = create_tween() + dig_tw.tween_property(sp, "scale:y", s * BOUNCE, UiKit.motion(DIG_S * 0.5)).set_trans(Tween.TRANS_SINE) + dig_tw.tween_property(sp, "scale:y", s, UiKit.motion(DIG_S * 0.5)).set_trans(Tween.TRANS_SINE) + + +# 英雄脚底锚点——**世界坐标**(精灵在 SubViewport 世界内:格原点 = 格×步距,禁带壳层居中偏移—— +# cell_origin 含 svp.position,世界内精灵定位误用会整体右偏 ~7 格+偏离格心)。 +# 纵向 = 格底往上 25% 线(脚踩格子 1/4 处);横向格中线。身体向上延伸, +# 头越上格沿不裁剪(造立体感);锚点与精灵缩放解耦(缩放只改身体向上长多少)。 +func feet(c: Vector2i) -> Vector2: + return Vector2(c) * (cell + sep) + Vector2(cell * 0.5, cell * (1.0 - GROUND_FRAC)) + + +# 英雄持握手位(fx 同层坐标系 = 壳层坐标——fx 在 SubViewport 之外,须含 svp.position +# 居中偏移:世界脚底锚 + 壳层偏移即得)。手 = 盒底往上 1/4 盒高、水平居中( +# 盒高随 SPRITE_SCALE——手位钉在身体上不随缩放漂移) +func hand_point() -> Vector2: + return svp.position + feet(last) + Vector2(0.0, -FRAME_PX * 0.25 * SPRITE_SCALE) + + +# 英雄精灵缩放档(fx 挥铲同档出口——两演出比例恒一致) +func sprite_scale() -> float: + return SPRITE_SCALE diff --git a/game/presentation/views/hero_sprite.gd.uid b/game/presentation/views/hero_sprite.gd.uid new file mode 100644 index 00000000..4926a3ea --- /dev/null +++ b/game/presentation/views/hero_sprite.gd.uid @@ -0,0 +1 @@ +uid://b2tr6ar845ogx diff --git a/game/presentation/views/settlement_fx_view.gd b/game/presentation/views/settlement_fx_view.gd index 6f02d2b3..0f429a83 100644 --- a/game/presentation/views/settlement_fx_view.gd +++ b/game/presentation/views/settlement_fx_view.gd @@ -92,6 +92,8 @@ func play_damage_number(pos: Vector2, text: String, col: Color, crit: bool = fal lab.z_index = 6 add_child(lab) await get_tree().process_frame # 等一帧取实际尺寸(文本度量) + if not is_instance_valid(lab): + return # 等待帧间节点已释放(父层收起/清场)——守卫后立即退出,禁触已释放实例 lab.position = pos - lab.size * 0.5 var tw := lab.create_tween() tw.tween_property(lab, "position:y", lab.position.y - 40.0, UiKit.motion(1.1)).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT) diff --git a/game/presentation/views/shop_overlay_view.gd b/game/presentation/views/shop_overlay_view.gd index 3b905b40..2db37149 100644 --- a/game/presentation/views/shop_overlay_view.gd +++ b/game/presentation/views/shop_overlay_view.gd @@ -4,7 +4,7 @@ class_name ShopOverlay extends Control -signal buy_requested(product_id: String) # legacy=product_id / pool=kind_id(组合根按模式分发) +signal buy_requested(product_id: String) ## legacy=product_id / pool=kind_id(组合根按模式分发) signal leave_pressed const ATTR_LABELS := {"atk": "攻击", "max_hp": "生命上限", "armor_class": "护甲"} # 档位文案轴(数据契约 attr 词表) diff --git a/game/presentation/views/standee_body.gd b/game/presentation/views/standee_body.gd index 046f44ce..d946c735 100644 --- a/game/presentation/views/standee_body.gd +++ b/game/presentation/views/standee_body.gd @@ -19,7 +19,6 @@ var slot_row: StatusSlotRow # 状态效果唯一展示面 = 壳层伴随 var _kind := "enemy" var _portrait_sprite: Sprite2D = null # 资产挂点(null = 未落位,字形在位) var _portrait_tex: Texture2D = null # 落位纹理引用(set_portrait_texture 装载;字形回退判据) -var _portrait_flip := false # 主角三态切换:state 名 → 纹理(battle_stage_view 组合点注册);缺态=保持现纹理 # (缺位回退链延伸——三态素材缺任一不炸不空)。敌站桩不注册不消费。 var _hero_tex := {} @@ -31,6 +30,7 @@ var _state_revert_tw: Tween var _order := 1 var _intent_glyph := "" var _intent_col := Color.WHITE +var _intent_charge := false # 蓄力档(pid ∈ CHARGE_SKILL_IDS——意图三档 ③ 分支判据) var _intent_visible := false var _intent_scale := 1.0: # 意图弹入动画参数 set(v): @@ -66,6 +66,11 @@ func display_name() -> String: return _display_name +# 敌单位身份出口(d12 站桩解析主键——同名敌按 id 精确匹配;主角站桩恒空串) +func enemy_id() -> String: + return _enemy_id + + # 血条存在性探针出口(世界直角血条常在) var has_world_hp_bar := true @@ -90,7 +95,7 @@ func set_data(d: Dictionary) -> void: _armor = String(d.get("armor_type", "light")) _hp_max = float(maxi(1, int(d.get("max_hp", 1)))) _set_hp(int(d.get("hp", 0))) - set_order(int(d.get("order", 1))) + _set_order(int(d.get("order", 1))) set_pending(String(d.get("pending_type", "")), String(d.get("pending_skill", ""))) set_effects(d.get("effects", [])) _refresh_portrait_glyph() @@ -143,7 +148,7 @@ func set_effects_cleared_feedback() -> void: tw.tween_callback(note.queue_free) -func set_order(i: int) -> void: +func _set_order(i: int) -> void: # 顺位徽标 = 行主序生成序(固定顺位——敌人系统 顺位标记义务) _order = i queue_redraw() @@ -153,10 +158,12 @@ func set_order(i: int) -> void: # "" = 未摇定/死亡残位)。不显示技能名——类型级图标 + 一次性动作动画。 func set_pending(ptype: String, pid: String) -> void: if _dead: + _intent_charge = false _intent_visible = false queue_redraw() return if ptype == "" or ptype == "basic": + _intent_charge = false _intent_visible = false # 普通攻击零表示(完全不可预测——§2.4) queue_redraw() return @@ -168,7 +175,8 @@ func set_pending(ptype: String, pid: String) -> void: elif ptype == "debuff": glyph = "☠" # 妨害系 col = UiKit.DEBUFF_PURPLE - if pid in CHARGE_SKILL_IDS: + _intent_charge = pid in CHARGE_SKILL_IDS + if _intent_charge: col = UiKit.GOLD_INK # 重击类蓄力档(深金墨——浅垫牌上 ≥4.5:1) _intent_glyph = glyph _intent_col = col @@ -185,7 +193,7 @@ func _play_intent_motion() -> void: tw.tween_property(self, "_intent_scale", 1.0, UiKit.motion(0.16)) tw.parallel().tween_property(self, "_glyph_rot", 4.0, UiKit.motion(0.10)) tw.tween_property(self, "_glyph_rot", 0.0, UiKit.motion(0.14)) - if _intent_col == UiKit.GOLD_DEEP: + if _intent_charge: tw.tween_property(self, "_intent_scale", 1.22, UiKit.motion(0.28)).set_trans(Tween.TRANS_SINE) # ③蓄力缓推 tw.tween_interval(UiKit.motion(0.40)) # 定格微顿 tw.tween_property(self, "_intent_scale", 1.0, UiKit.motion(0.18)) @@ -198,7 +206,6 @@ func _play_intent_motion() -> void: # flip = 立绘朝向翻转(素材基准朝左时传 true 呈现朝右)。盒高 96px = 48px 立绘源整数 2× # (圣经 §8.2 禁非整数缩放)——源换代时随源重算。 func set_portrait_texture(tex: Texture2D, flip: bool = false) -> void: - _portrait_flip = flip if tex == null: if _portrait_sprite != null: _portrait_sprite.visible = false diff --git a/game/presentation/views/tile_palette.gd b/game/presentation/views/tile_palette.gd new file mode 100644 index 00000000..bedb7c69 --- /dev/null +++ b/game/presentation/views/tile_palette.gd @@ -0,0 +1,261 @@ +## VERTICAL SLICE - NOT FOR PRODUCTION +## 棋盘瓦片件:高墙/地面瓦片整批装载与运行时镜像合成、地板明度归一、数字墨族按板自适应(静态算法族)、 +## 格坐标确定性散列与十六正交情形墙瓦片查表。装载缓存七成员持于件内(同板型零重载、换板重载时序同源), +## 查表族读件持棋盘引用(棋盘视图 rebuild 经 bind_board 同步)。零绘制面、零玩法数据写入。 +class_name TilePalette +extends RefCounted + +# ── 高墙/地面瓦片纹理路径单点 ── +# 素材 PNG 不入 git——一律 UiKit.load_tex(exists 守卫)装载,禁 preload; +# 缺位返回 null,整批任一缺位 = 弃用瓦片走程序化平色回退(棋盘视图 BRICK/FLOOR,fresh checkout 冒烟不炸)。 +# 词表 = design/research/board-tile-vocab-plan-a-2026-09-26.md(16 唯一墙瓦片 + 5 地面变体; +# 东/东北/东南/end_e/in_ne/in_se 六镜像件运行时 flip_x 合成,不出文件——词表 §二)。 +# 路径按 board_type 后缀参数化——terrain_<件名>_.png; +# 词表 §一 16 唯一墙瓦片名单单点在本常量数组。underdark/deep_underdark 两套 = +# tools/recolor_terrain_tiles.py 程序换色产;任一套缺位 → 整批回落 +# dungeon 套(禁同板混渲染);dungeon 套亦缺位 → 程序化平色回退(fresh checkout 冒烟不炸)。 +const TILE_WALL_NAMES := ["wall_center", "wall_center_crack", "wall_center_speckle", "wall_n", "wall_s", "wall_w", "wall_nw", "wall_sw", "wall_hrun", "wall_vrun", "wall_end_n", "wall_end_s", "wall_end_w", "wall_lone", "wall_in_nw", "wall_in_sw"] +const TILE_WALL_FMT := "res://assets/art/board/terrain_%s_%s.png" # %s=件名 %s=board_type +const TILE_SEAL_MARK := "res://assets/art/board/seal_mark.png" # 封印印记纹理(独立可选件) +const TILE_FLOOR_FMT := "res://assets/art/board/terrain_floor_%s_%d.png" # %s=board_type 1..%d 变体 +const TILE_FLOOR_VARIANTS := 5 # 地面瓦片变体数(词表 §一 floor_1..5) + +# ---- 数字墨族按板自适应(墨色按关自适应+三关对比断言)---- + +const DIGIT_INK_CR_P90 := 4.5 # 墨族对地板 p90 亮斑对比度门槛(失效模式=亮白筋纹冲字,WCAG 数字体同源) +const DIGIT_INK_CR_MEAN_BRIGHT := 2.5 # 亮族对地板均值门槛(中调防吃字) +const DIGIT_INK_CR_MEAN_DARK := 3.0 # 深族对地板均值门槛(大字 AA 级——2.4 的实测观感=隐形) + +# 墙瓦片 16 正交情形映射(下标 = nf<<3|ef<<2|sf<<1|wf,nf/ef/sf/wf = 北/东/南/西邻是地板)。 +# 词表 §一 16 情形 = 0 地板(center 族,键 15 另走对角凹角/中心三变体)+ 3 地板×4(端头)+ +# 2 地板×6(条带/凸角)+ 1 地板×4(单缘)+ 4 地板(对角凹角/中心三变体);lone 件与原实现同址 +# 不入查表(0 地板收尾落 wall_w) +const WALL_MASK_TILE := ["wall_w", "wall_w", "wall_s", "wall_sw", "wall_e", "wall_vrun", "wall_se", + "wall_end_s", "wall_n", "wall_nw", "wall_hrun", "wall_end_e", "wall_ne", "wall_end_n", + "wall_end_w", "wall_center"] +# 四邻皆墙格:对角凹角优先 nw > ne > sw > se(词表 §三.2——双凹角不做专件,多对角地板回落 +# 首中单凹角件,残留 1px 缝为可接受限) +const WALL_DIAG_DIRS := [Vector2i(-1, -1), Vector2i(1, -1), Vector2i(-1, 1), Vector2i(1, 1)] +const WALL_DIAG_TILES := ["wall_in_nw", "wall_in_ne", "wall_in_sw", "wall_in_se"] +# 无对角地板 → 中心三变体按格坐标哈希 60/20/20(posmod 保非负下标,10 槽展开免分支) +const WALL_CENTER_PICK := ["wall_center", "wall_center", "wall_center", "wall_center", "wall_center", + "wall_center", "wall_center_crack", "wall_center_crack", "wall_center_speckle", "wall_center_speckle"] + +# ── 装载缓存(原棋盘视图同名成员随迁;缺位 = 全 false/空——程序化平色回退)── +var tiles: Dictionary[String, Texture2D] = {} # 词表键 → Texture2D(镜像件已预翻转) +var floor_tiles: Array[Texture2D] = [] # 地面瓦片五变体(下标 0..4 = floor_1..5) +var tiles_ok := false # false = 素材缺位/未装载——调用方走程序化平色原路径 +var seal_tex: Texture2D = null # 封印印记纹理(缺盘=null=字形回退——独立可选件不连瓦片整批) +var tiles_board_type := "dungeon" # 当前装载瓦片套的 board_type(rebuild 按板切换) +var digit_dark_ink := true # 数字墨族按板自适应(true=深墨三档)。缺盘平色回退态 #B39A78 裸土 + # 为中明度底——深墨族 CR 4.96 达标、亮墨族仅 2.2,故缺盘默认深墨;瓦片装载后按实测分位数覆写 +var digit_plate := false # 数字色牌垫底模式(双峰纹理板——直画两族皆不过门槛时落牌, + # 素牌+深墨 12:1 纹理免疫;渲染帧取证 underdark 系深墨直画隐形,故启用色牌) + +# ── 查表用棋盘态(rebuild 换板时经 bind_board 同步)── +var board: BoardData = null +var minesweeper: MinesweeperRules = null + + +func bind_board(board_ref: BoardData, minesweeper_ref: MinesweeperRules) -> void: + board = board_ref + minesweeper = minesweeper_ref + + +# 瓦片整批守卫装载(首次建板与换板型各一次):21 枚全成才启用——任一缺位按整批缺位处理 +#(禁同板混渲染;fresh checkout 无素材目录 → 静默全回退)。缺位不 push_error——可选素材缺位是 +# 预期路径非故障(英雄 load_tex 纪律同款,防冒烟刷屏)。镜像件运行时 flip_x 合成,不出文件。 +func load_board_tiles(board_type: String = "dungeon") -> void: + if board_type == "": + board_type = "dungeon" + # 16 墙瓦片装载循环节(词表 TILE_WALL_NAMES 单点; + # 词表本身被 terrain_tile_sets_test 直读,保留) + var t: Dictionary[String, Texture2D] = {} + for wall_name in TILE_WALL_NAMES: + t[wall_name] = UiKit.load_tex(TILE_WALL_FMT % [wall_name, board_type]) + var floors: Array[Texture2D] = [] + for i in range(1, TILE_FLOOR_VARIANTS + 1): + floors.append(UiKit.load_tex(TILE_FLOOR_FMT % [board_type, i])) + for k in t: + if t[k] == null: + _request_dungeon_fallback(board_type) + return + for tex in floors: + if tex == null: + _request_dungeon_fallback(board_type) + return + # 地板归一(圣经 v3.5 冻结①/§1.4——已开格底归一世界底层档 L≈0.10,带 0.08–0.14): + # 方向稿 _v3_darken_floors 同式移植;现行瓦片 L≈0.036 低于带下沿故实为提亮; + # 本归一为程序代役(逐批装载时对 freshly-loaded 原图测光,幂等)。归一后逐像素明度顺路采集, + # 驱动数字墨族按板自适应(digit_ink_mode——均值分位同源同像素,禁二次解码) + var lumas: Array[Variant] = [] + for i in range(floors.size()): + floors[i] = normalize_floor_luma(floors[i], 0.10, lumas) + var ink_mode := digit_ink_mode(lumas) + digit_dark_ink = bool(ink_mode.dark) + digit_plate = bool(ink_mode.plate) + # 六镜像件(词表 §二:wall_e←wall_w / wall_ne←wall_nw / wall_se←wall_sw / + # wall_end_e←wall_end_w / wall_in_ne←wall_in_nw / wall_in_se←wall_in_sw;只水平翻转禁旋转 + # ——北受光不受损) + t["wall_e"] = flip_tex(t["wall_w"]) + t["wall_ne"] = flip_tex(t["wall_nw"]) + t["wall_se"] = flip_tex(t["wall_sw"]) + t["wall_end_e"] = flip_tex(t["wall_end_w"]) + t["wall_in_ne"] = flip_tex(t["wall_in_nw"]) + t["wall_in_se"] = flip_tex(t["wall_in_sw"]) + tiles = t + floor_tiles = floors + tiles_board_type = board_type + tiles_ok = true + # 封印印记纹理(独立可选件:缺盘=字形回退,不触发瓦片整批回落语义) + seal_tex = UiKit.load_tex(TILE_SEAL_MARK) + + +# 整批回落:请求套缺位 → 回落 dungeon 套(禁同板混渲染);dungeon 本身缺位 = +# 维持程序化平色回退原语义(tiles_ok 恒 false,缺位不刷屏纪律不变) +func _request_dungeon_fallback(requested: String) -> void: + if requested != "dungeon": + load_board_tiles("dungeon") + + +# 水平镜像贴图运行时合成(flip_h 语义):Image 级逐位翻转后重建 ImageTexture——CellUnit 零改动。 +# VRAM 压缩源先解压(flip_x 不支持压缩格式);32×32 原尺寸逐位翻转,无 ±1px 取整问题 +static func flip_tex(tex: Texture2D) -> Texture2D: + var img := tex.get_image() + if img.is_compressed(): + img.decompress() + img.flip_x() + return ImageTexture.create_from_image(img) + + +# 地板瓦片明度归一(方向稿 _v3_darken_floors 同式移植):8×8 双线性重采样均值测光 +# (sRGB→线性域,Rec.709 系数),增益 k=(目标/实测)^(1/2.2) 钳 [0.6,1.8],逐通道 sRGB 域线性拉伸。 +# 输入恒为 freshly-loaded 原图(load_tex 每次盘读)——幂等可重入;32×32 五变体开销可忽略 +static func normalize_floor_luma(tex: Texture2D, target_l: float = 0.10, lumas_out: Variant = null) -> Texture2D: + var img: Image = tex.get_image() + if img.is_compressed(): + img.decompress() + img.convert(Image.FORMAT_RGBA8) + var small: Image = img.duplicate() + small.resize(8, 8, Image.INTERPOLATE_BILINEAR) + var sum := 0.0 + for y in range(8): + for x in range(8): + var p := small.get_pixel(x, y) + sum += 0.2126 * srgb_lin(p.r) + 0.7152 * srgb_lin(p.g) + 0.0722 * srgb_lin(p.b) + var k := pow(target_l / maxf(sum / 64.0, 0.001), 1.0 / 2.2) + k = clampf(k, 0.6, 1.8) + for y in range(img.get_height()): + for x in range(img.get_width()): + var c := img.get_pixel(x, y) + var nr := minf(c.r * k, 1.0) + var ng := minf(c.g * k, 1.0) + var nb := minf(c.b * k, 1.0) + img.set_pixel(x, y, Color(nr, ng, nb, c.a)) + if lumas_out != null: + lumas_out.append(0.2126 * srgb_lin(nr) + 0.7152 * srgb_lin(ng) + 0.0722 * srgb_lin(nb)) + return ImageTexture.create_from_image(img) + + +static func srgb_lin(c: float) -> float: + return c / 12.92 if c <= 0.04045 else pow((c + 0.055) / 1.055, 2.4) + + +# 归一后地板逐像素明度 → 选数字呈现模式。三态:亮族直画(暗纹理板)/深族直画(中暗板)/ +# 色牌垫底(双峰纹理板——近黑斑与近白筋并存时任何墨族直画必融一峰,渲染帧取证 +# 实证 underdark 系深墨隐形,牌上深墨 12:1 纹理免疫)。家族最弱成员(亮族=金亮/深族=锈墨) +# 对 p90 与均值双门槛;亮族过选亮族,否则深族(均值门 3.0),两族皆不过落牌。 +# 实测:dungeon p90=0.121 亮族 4.79/5.80 ✓ 直画;underdark p90=0.406 亮族 ✗、 +# 深族 5.34/**2.41**(均值 <3.0 ✗)→ 落牌;deep_underdark 同→落牌 +static func digit_ink_mode(lumas: Array) -> Dictionary: + var sorted := lumas.duplicate() + sorted.sort() + var n := sorted.size() + if n == 0: + return {"dark": false, "plate": false, "cr_p90_bright": 0.0, "cr_mean_bright": 0.0, "cr_p90_dark": 0.0, "cr_mean_dark": 0.0} + var p90: float = sorted[int(n * 0.9)] + var crs := _family_contrasts(p90, _mean_of(sorted)) + var bright_ok: bool = crs.x >= DIGIT_INK_CR_P90 and crs.z >= DIGIT_INK_CR_MEAN_BRIGHT + var dark_ok: bool = crs.y >= DIGIT_INK_CR_P90 and crs.w >= DIGIT_INK_CR_MEAN_DARK + return {"dark": not bright_ok and dark_ok, "plate": not bright_ok and not dark_ok, + "cr_p90_bright": crs.x, "cr_mean_bright": crs.z, "cr_p90_dark": crs.y, "cr_mean_dark": crs.w} + + +# 均值(明度集合逐元素算术平均) +static func _mean_of(values: Array) -> float: + var sum := 0.0 + for v in values: + sum += v + return sum / float(values.size()) + + +# 两族对比度四元组:x=亮族 p90 / y=深族 p90 / z=亮族均值 / w=深族均值 +static func _family_contrasts(p90: float, mean: float) -> Vector4: + return Vector4(contrast_y(UiKit.INK_L_GOLD, p90), contrast_y(UiKit.INK_RUST, p90), + contrast_y(UiKit.INK_L_GOLD, mean), contrast_y(UiKit.INK_RUST, mean)) + + +static func contrast_y(ink: Color, bg: float) -> float: + var y := ink_y(ink) + return (maxf(y, bg) + 0.05) / (minf(y, bg) + 0.05) + + +static func ink_y(c: Color) -> float: + return 0.2126 * srgb_lin(c.r) + 0.7152 * srgb_lin(c.g) + 0.0722 * srgb_lin(c.b) + + +# 格坐标确定性散列(禁 RNG/禁 randomize 纪律——「初始化随机落定禁逐轮重掷」圣经原则三落点: +# 同格坐标恒同值,refresh/重绘/逐轮不变)。乘加混合 + 移位雪崩;64 位整型乘法回绕即哈希语义 +#(GDScript int 溢出静默回绕),板域 10×10/12×12 内 mod 10/mod 5 分布均匀 +static func cell_hash(c: Vector2i) -> int: + var h := c.x * 374761393 + c.y * 668265263 + h = (h ^ (h >> 13)) * 1274126177 + return h ^ (h >> 16) + + +# 四正交邻墙判定(词表 §三.1:板外视为墙;未开格=墙、已开格=地板——世界观口径「未开=墙、 +# 已开=挖出的空间」)。唯一调用面 wall_tile_for/wall_center_tile(棋盘视图 refresh 对 +# 排雷玩法非空守卫在先) +func is_wall(c: Vector2i) -> bool: + if c.x < 0 or c.y < 0 or c.x >= board.width or c.y >= board.height: + return true + return not minesweeper.is_revealed(c) + + +# 四邻地板掩码(位序 = nf<<3|ef<<2|sf<<1|wf,见 WALL_MASK_TILE) +func _neighbor_mask(c: Vector2i) -> int: + var m := 0 + if not is_wall(c + Vector2i(0, -1)): + m |= 8 + if not is_wall(c + Vector2i(1, 0)): + m |= 4 + if not is_wall(c + Vector2i(0, 1)): + m |= 2 + if not is_wall(c + Vector2i(-1, 0)): + m |= 1 + return m + + +# 墙瓦片查表(词表 §一 16 正交情形映射 + §三.2 凹角回落):nf/ef/sf/wf = 北/东/南/西邻是地板。 +# 16 情形 = 0 地板(center 族)+ 3 地板×4(端头)+ 2 地板×6(条带/凸角)+ 1 地板×4(单缘) +# + 4 地板(对角凹角/中心三变体),逐键穷尽 +func wall_tile_for(c: Vector2i) -> Texture2D: + var mask := _neighbor_mask(c) + if mask == 15: + return wall_center_tile(c) # 四邻皆地板 → 对角凹角/中心三变体(下位函数) + return tiles[WALL_MASK_TILE[mask]] + + +# 四邻皆地板格:对角凹角优先 nw > ne > sw > se;无对角地板 → 中心三变体按格坐标哈希 60/20/20 +func wall_center_tile(c: Vector2i) -> Texture2D: + for i in WALL_DIAG_DIRS.size(): + var d: Vector2i = WALL_DIAG_DIRS[i] + if not is_wall(c + d): + return tiles[WALL_DIAG_TILES[i]] + return tiles[WALL_CENTER_PICK[posmod(cell_hash(c), 10)]] + + +# 地面瓦片五变体按格坐标哈希均匀分配(词表 §一 floor_1..5 / §三.3「同法哈希分配」; +# posmod 保非负下标) +func floor_tile_for(c: Vector2i) -> Texture2D: + return floor_tiles[posmod(cell_hash(c), floor_tiles.size())] diff --git a/game/presentation/views/tile_palette.gd.uid b/game/presentation/views/tile_palette.gd.uid new file mode 100644 index 00000000..fcf6e9a7 --- /dev/null +++ b/game/presentation/views/tile_palette.gd.uid @@ -0,0 +1 @@ +uid://cp4wdqe0v1l32 diff --git a/game/presentation/views/ui_kit.gd b/game/presentation/views/ui_kit.gd index eb2441dd..7431323b 100644 --- a/game/presentation/views/ui_kit.gd +++ b/game/presentation/views/ui_kit.gd @@ -10,7 +10,7 @@ const INK_OCHRE := Color8(60, 37, 12) # 赭墨 4-5 #3C250C(数字墨 const INK_RUST := Color8(74, 30, 20) # 锈墨 6-8 #4A1E14(数字墨三档·暖=密集;亮瓦自适应深墨族档兼色牌后备路径) # ── 亮墨三档(圣经 v3.5 冻结④——数字直画已开格底:色温承袭密度语义,冷亮/暖白/金亮; # 实测对比 6.0–6.3:1(A3a);金亮低饱和避奖励金语义;色牌后备组用深墨族上牌〔12:1〕。 -# 亮墨族限暗纹理板(dungeon),亮筋纹板按 _digit_ink_mode 自适应切深墨三档)── +# 亮墨族限暗纹理板(dungeon),亮筋纹板按 TilePalette.digit_ink_mode 自适应切深墨三档)── const INK_L_COLD := Color8(216, 236, 240) # 冷亮 1-3 #D8ECF0 const INK_L_WARM := Color8(240, 226, 200) # 暖白 4-5 #F0E2C8(关卡屏 WARM 同族) const INK_L_GOLD := Color8(248, 227, 168) # 金亮 6-8 #F8E3A8 @@ -25,7 +25,7 @@ const PANEL_BORDER := Color8(120, 110, 88) # 面板细线框;兼熄灭点 const GOLD := Color8(240, 168, 40) # 奖励金 const GOLD_DEEP := Color8(176, 122, 20) # 深金(金框描边/达意金) const GOLD_INK := Color8(116, 80, 11) # 深金墨(意图蓄力徽标墨——PANEL_DATA 垫牌上 4.9:1) -const GOLD_BRIGHT := Color8(224, 184, 96) # 金族亮档(敌顺位徽标①②③/重甲徽标对暗舞台底 6.5:1;值=收益金 e0b860 同门;GOLD_DEEP 语义岗〔standee:199 意图判据/board_view:35/level_map:237〕在役不动) +const GOLD_BRIGHT := Color8(224, 184, 96) # 金族亮档(敌顺位徽标①②③/重甲徽标对暗舞台底 6.5:1;值=收益金 e0b860 同门) const GOLD_LIT := Color8(230, 190, 90) # 亮金(候选高亮) const GOLD_DIM := Color8(140, 116, 60) # 暗金(已过节点) const GOLD_DONE := Color8(104, 86, 44) # 已过节点暗金·关卡画面呈现态 #68562C(GOLD_DIM #8C743C 对双色墨均 <4.5:1,对比硬指标逼守此值;GOLD_DIM 他用不动) @@ -510,7 +510,7 @@ static func page_shell(host: Control, bright := true) -> Dictionary: return {"page": page, "base": base, "top_row": top_row, "box": box} -# 属性百分比合成(main._debug_hero_override 与调试浮窗生效值预览共用单点——两处同式实现收口) +# 属性百分比合成(board_resolvers.debug_hero_override 与调试浮窗生效值预览共用单点——两处同式实现收口) static func scaled(base: int, pct: int) -> int: return maxi(1, int(round(float(base) * (100.0 + float(pct)) / 100.0))) diff --git a/game/probe/ui_contrast_probe.gd b/game/probe/ui_contrast_probe.gd index bfe6403c..652fb04e 100644 --- a/game/probe/ui_contrast_probe.gd +++ b/game/probe/ui_contrast_probe.gd @@ -136,18 +136,18 @@ func _fog_derived_check() -> Array: # 迷雾测点派生助手(迭代36 wc5 契约 3——全仓唯一「按板型算归一后均值」判据面):纹理路径经 -# BoardView.TILE_FLOOR_FMT 常量(禁复制字面量)、归一经 BoardView._normalize_floor_luma +# TilePalette.TILE_FLOOR_FMT 常量(禁复制字面量)、归一经 TilePalette.normalize_floor_luma # 生产静态函数直呼(真渲染同源——禁探针内复制 k 公式);逐像素归一后均值再五变体平均。 static func _norm_floor_avg(board_type: String) -> Color: var sum_r := 0.0 var sum_g := 0.0 var sum_b := 0.0 var px := 0 - for i in range(1, BoardView.TILE_FLOOR_VARIANTS + 1): - var tex := UiKit.load_tex(BoardView.TILE_FLOOR_FMT % [board_type, i]) + for i in range(1, TilePalette.TILE_FLOOR_VARIANTS + 1): + var tex := UiKit.load_tex(TilePalette.TILE_FLOOR_FMT % [board_type, i]) if tex == null: continue - var img: Image = BoardView._normalize_floor_luma(tex, 0.10, null).get_image() + var img: Image = TilePalette.normalize_floor_luma(tex, 0.10, null).get_image() if img.is_compressed(): img.decompress() img.convert(Image.FORMAT_RGBA8) diff --git a/game/tests/evidence/parity/anchor.json b/game/tests/evidence/parity/anchor.json index 78b2a231..5786fdfb 100644 --- a/game/tests/evidence/parity/anchor.json +++ b/game/tests/evidence/parity/anchor.json @@ -2,163 +2,163 @@ "variants": { "base": { "runsum": [ - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":5,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":13,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":6,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"excellent\"},{\"kind\":\"gp\",\"tier\":\"excellent\"},{\"kind\":\"ap\",\"tier\":\"rare\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"ap\",\"tier\":\"rare\",\"value\":1}}],\"rounds\":11,\"route\":\"min\",\"seal_hit\":39,\"seal_hit_gain\":208,\"seal_miss\":8,\"seal_miss_penalty\":16,\"seal_placed\":51,\"seed\":71000,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":29,\"visited_count\":7,\"wallet_count\":6}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":7,\"choices\":7,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":30,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":1,\"mine_dmg\":16,\"mine_hits\":6,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[\"perfect_insurance\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"gp\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"gp\",\"tier\":\"excellent\",\"value\":50}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"perfect_insurance\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":79,\"heal_gained\":5,\"hp_after\":17,\"hp_before\":12,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"perfect_insurance\",\"feat_name\":\"完美保险\",\"hp_after\":17,\"hp_before\":17,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":29,\"route\":\"min\",\"seal_hit\":69,\"seal_hit_gain\":397,\"seal_miss\":6,\"seal_miss_penalty\":14,\"seal_placed\":81,\"seed\":71001,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":46,\"visited_count\":9,\"wallet_count\":9}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":6,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":31,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"窄廊短支\"],\"levels_cleared\":1,\"mine_dmg\":14,\"mine_hits\":7,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[\"venom_edge\",\"trap_instinct\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"feat_ticket\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"feat_archetype\":\"\",\"feat_id\":\"venom_edge\",\"feat_name\":\"淬毒刃纹\",\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"trap_instinct\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"chain_traps\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":26,\"heal_gained\":5,\"hp_after\":7,\"hp_before\":2,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"trap_instinct\",\"feat_name\":\"陷阱本能\",\"hp_after\":7,\"hp_before\":7,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":27,\"route\":\"min\",\"seal_hit\":70,\"seal_hit_gain\":521,\"seal_miss\":9,\"seal_miss_penalty\":20,\"seal_placed\":84,\"seed\":71002,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":45,\"visited_count\":8,\"wallet_count\":7}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":1,\"choices\":0,\"cleared\":false,\"encounter_max_hp_seq\":[26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":4,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·赏战整\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":5,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":3,\"route\":\"min\",\"seal_hit\":6,\"seal_hit_gain\":-9,\"seal_miss\":7,\"seal_miss_penalty\":8,\"seal_placed\":13,\"seed\":71003,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":20,\"visited_count\":1,\"wallet_count\":2}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":1,\"choices\":0,\"cleared\":false,\"encounter_max_hp_seq\":[26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":4,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·主线洞道(现行)\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":3,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":1,\"route\":\"min\",\"seal_hit\":3,\"seal_hit_gain\":65,\"seal_miss\":2,\"seal_miss_penalty\":16,\"seal_placed\":5,\"seed\":71004,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":12,\"visited_count\":1,\"wallet_count\":2}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":6,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":26,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"三干宽门\"],\"levels_cleared\":1,\"mine_dmg\":8,\"mine_hits\":8,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[\"blood_insight\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"heal\",\"tier\":\"excellent\"},{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"heal\",\"tier\":\"excellent\",\"value\":6}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":42,\"heal_gained\":5,\"hp_after\":10,\"hp_before\":5,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blood_insight\",\"feat_name\":\"见血开窍\",\"hp_after\":10,\"hp_before\":10,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":25,\"route\":\"min\",\"seal_hit\":78,\"seal_hit_gain\":72,\"seal_miss\":8,\"seal_miss_penalty\":24,\"seal_placed\":91,\"seed\":71005,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":34,\"visited_count\":8,\"wallet_count\":7}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":6,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":28,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"宽廊斜岔\"],\"levels_cleared\":1,\"mine_dmg\":14,\"mine_hits\":8,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[\"trap_instinct\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"heal\",\"tier\":\"excellent\"},{\"kind\":\"max_hp\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"heal\",\"tier\":\"excellent\",\"value\":6}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"trap_instinct\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"hard_hat\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":33,\"heal_gained\":5,\"hp_after\":14,\"hp_before\":9,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"trap_instinct\",\"feat_name\":\"陷阱本能\",\"hp_after\":14,\"hp_before\":14,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":25,\"route\":\"min\",\"seal_hit\":65,\"seal_hit_gain\":286,\"seal_miss\":9,\"seal_miss_penalty\":22,\"seal_placed\":81,\"seed\":71006,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":42,\"visited_count\":8,\"wallet_count\":7}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":5,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":16,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\"],\"levels_cleared\":0,\"mine_dmg\":14,\"mine_hits\":10,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"gp\",\"tier\":\"excellent\"},{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"feat_ticket\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"gp\",\"tier\":\"excellent\",\"value\":50}}],\"rounds\":16,\"route\":\"min\",\"seal_hit\":48,\"seal_hit_gain\":319,\"seal_miss\":11,\"seal_miss_penalty\":8,\"seal_placed\":65,\"seed\":71007,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":30,\"visited_count\":7,\"wallet_count\":7}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":5,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":23,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\"],\"levels_cleared\":0,\"mine_dmg\":0,\"mine_hits\":5,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"armor\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"armor\",\"tier\":\"excellent\",\"value\":2}}],\"rounds\":18,\"route\":\"min\",\"seal_hit\":48,\"seal_hit_gain\":-32,\"seal_miss\":4,\"seal_miss_penalty\":14,\"seal_placed\":56,\"seed\":71008,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":23,\"visited_count\":7,\"wallet_count\":6}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":5,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":29,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":12,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[\"trap_burst\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"feat_ticket\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"feat_archetype\":\"\",\"feat_id\":\"trap_burst\",\"feat_name\":\"陷阱爆发\",\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":20,\"route\":\"min\",\"seal_hit\":49,\"seal_hit_gain\":334,\"seal_miss\":14,\"seal_miss_penalty\":16,\"seal_placed\":65,\"seed\":71009,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":45,\"visited_count\":7,\"wallet_count\":6}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":5,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":31,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\"],\"levels_cleared\":0,\"mine_dmg\":14,\"mine_hits\":6,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[\"skip_mining\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"feat_ticket\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"feat_archetype\":\"\",\"feat_id\":\"skip_mining\",\"feat_name\":\"跳格采掘\",\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":20,\"route\":\"min\",\"seal_hit\":52,\"seal_hit_gain\":19,\"seal_miss\":7,\"seal_miss_penalty\":-2,\"seal_placed\":62,\"seed\":71010,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":45,\"visited_count\":7,\"wallet_count\":6}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":2,\"choices\":1,\"cleared\":false,\"encounter_max_hp_seq\":[26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":4,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\"],\"levels_cleared\":0,\"mine_dmg\":24,\"mine_hits\":5,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":4,\"route\":\"min\",\"seal_hit\":10,\"seal_hit_gain\":178,\"seal_miss\":6,\"seal_miss_penalty\":6,\"seal_placed\":16,\"seed\":71011,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":28,\"visited_count\":2,\"wallet_count\":3}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":3,\"choices\":2,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":6,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\"],\"levels_cleared\":0,\"mine_dmg\":24,\"mine_hits\":4,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":4,\"route\":\"min\",\"seal_hit\":10,\"seal_hit_gain\":79,\"seal_miss\":5,\"seal_miss_penalty\":6,\"seal_placed\":17,\"seed\":71012,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":30,\"visited_count\":3,\"wallet_count\":4}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":4,\"choices\":4,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":11,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\"],\"levels_cleared\":0,\"mine_dmg\":30,\"mine_hits\":6,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":7,\"route\":\"min\",\"seal_hit\":23,\"seal_hit_gain\":64,\"seal_miss\":6,\"seal_miss_penalty\":8,\"seal_placed\":31,\"seed\":71013,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":41,\"visited_count\":5,\"wallet_count\":5}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":5,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":21,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":9,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[\"trap_instinct\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"feat_ticket\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"feat_archetype\":\"\",\"feat_id\":\"trap_instinct\",\"feat_name\":\"陷阱本能\",\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":18,\"route\":\"min\",\"seal_hit\":52,\"seal_hit_gain\":389,\"seal_miss\":8,\"seal_miss_penalty\":20,\"seal_placed\":62,\"seed\":71014,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":37,\"visited_count\":7,\"wallet_count\":6}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":4,\"choices\":3,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":12,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":5,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":10,\"route\":\"min\",\"seal_hit\":36,\"seal_hit_gain\":891,\"seal_miss\":3,\"seal_miss_penalty\":0,\"seal_placed\":46,\"seed\":71015,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":28,\"visited_count\":4,\"wallet_count\":5}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":4,\"choices\":5,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":19,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\"],\"levels_cleared\":0,\"mine_dmg\":6,\"mine_hits\":5,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[\"frontier_miner\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"feat_ticket\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"feat_archetype\":\"\",\"feat_id\":\"frontier_miner\",\"feat_name\":\"边陲矿工\",\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":12,\"route\":\"min\",\"seal_hit\":38,\"seal_hit_gain\":-87,\"seal_miss\":7,\"seal_miss_penalty\":22,\"seal_placed\":46,\"seed\":71016,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":25,\"visited_count\":6,\"wallet_count\":5}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":4,\"choices\":5,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,36],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":24,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":4,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"max_hp\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"max_hp\",\"tier\":\"excellent\",\"value\":10}}],\"rounds\":10,\"route\":\"min\",\"seal_hit\":31,\"seal_hit_gain\":545,\"seal_miss\":5,\"seal_miss_penalty\":8,\"seal_placed\":42,\"seed\":71017,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":40,\"visited_count\":6,\"wallet_count\":5}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":5,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":25,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\"],\"levels_cleared\":0,\"mine_dmg\":14,\"mine_hits\":4,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"excellent\"},{\"kind\":\"gp\",\"tier\":\"excellent\"},{\"kind\":\"rap\",\"tier\":\"rare\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"rap\",\"tier\":\"rare\",\"value\":3}}],\"rounds\":16,\"route\":\"min\",\"seal_hit\":37,\"seal_hit_gain\":186,\"seal_miss\":6,\"seal_miss_penalty\":24,\"seal_placed\":49,\"seed\":71018,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":39,\"visited_count\":7,\"wallet_count\":6}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":1,\"choices\":0,\"cleared\":false,\"encounter_max_hp_seq\":[26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":9,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·先赏后整\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":3,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":2,\"route\":\"min\",\"seal_hit\":4,\"seal_hit_gain\":5,\"seal_miss\":3,\"seal_miss_penalty\":14,\"seal_placed\":11,\"seed\":71019,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":17,\"visited_count\":1,\"wallet_count\":2}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":8,\"choices\":9,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,36,36,36,36],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":88,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"executioner\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"excellent\"},{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"heal\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"max_hp\",\"tier\":\"excellent\",\"value\":10}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":34,\"heal_gained\":0,\"hp_after\":36,\"hp_before\":36,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"executioner\",\"feat_name\":\"处刑台\",\"hp_after\":36,\"hp_before\":36,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":36,\"route\":\"min\",\"seal_hit\":117,\"seal_hit_gain\":29,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":127,\"seed\":71020,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":88,\"visited_count\":11,\"wallet_count\":9}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":8,\"choices\":9,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":70,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"breaker_strike\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"excellent\"},{\"kind\":\"gp\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"attack\",\"tier\":\"excellent\",\"value\":2}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"breaker_strike\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":50,\"heal_gained\":5,\"hp_after\":17,\"hp_before\":12,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"breaker_strike\",\"feat_name\":\"破封一击\",\"hp_after\":17,\"hp_before\":17,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":34,\"route\":\"min\",\"seal_hit\":123,\"seal_hit_gain\":740,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":130,\"seed\":71021,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":70,\"visited_count\":11,\"wallet_count\":9}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":8,\"choices\":8,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":66,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"chain_traps\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"gp\",\"tier\":\"excellent\"},{\"kind\":\"heal\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"gp\",\"tier\":\"excellent\",\"value\":50}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"chain_traps\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":92,\"heal_gained\":5,\"hp_after\":25,\"hp_before\":20,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"chain_traps\",\"feat_name\":\"连环陷阱\",\"hp_after\":25,\"hp_before\":25,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":36,\"route\":\"min\",\"seal_hit\":124,\"seal_hit_gain\":-92,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":130,\"seed\":71022,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":66,\"visited_count\":10,\"wallet_count\":10}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":16,\"choices\":17,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26,26,38,38,38,38,38,41],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":89,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"twin_pick\",\"geologist\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"excellent\"},{\"kind\":\"heal\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"attack\",\"tier\":\"excellent\",\"value\":2}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":35,\"heal_gained\":0,\"hp_after\":26,\"hp_before\":26,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"twin_pick\",\"feat_name\":\"双管镐\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":16,\"heal_gained\":0,\"hp_after\":38,\"hp_before\":38,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"geologist\",\"feat_name\":\"地质学家\",\"hp_after\":38,\"hp_before\":38,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":67,\"route\":\"min\",\"seal_hit\":287,\"seal_hit_gain\":469,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":314,\"seed\":71023,\"shop_gp_seq\":[89,51],\"stopped_at\":0,\"taken\":89,\"visited_count\":20,\"wallet_count\":31}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":14,\"choices\":16,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,38,38,38,38,38,44],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":104,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"blast_accounting\",\"chain_traps\",\"frontier_miner\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"feat_ticket\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"feat_archetype\":\"\",\"feat_id\":\"blast_accounting\",\"feat_name\":\"爆破会计\",\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"chain_traps\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":37,\"heal_gained\":5,\"hp_after\":24,\"hp_before\":19,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"chain_traps\",\"feat_name\":\"连环陷阱\",\"hp_after\":24,\"hp_before\":24,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":32,\"heal_gained\":2,\"hp_after\":38,\"hp_before\":36,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"frontier_miner\",\"feat_name\":\"边陲矿工\",\"hp_after\":38,\"hp_before\":38,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":68,\"route\":\"min\",\"seal_hit\":246,\"seal_hit_gain\":1362,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":264,\"seed\":71024,\"shop_gp_seq\":[75,107],\"stopped_at\":0,\"taken\":104,\"visited_count\":19,\"wallet_count\":31}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":14,\"choices\":16,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,38,38,38,38,38,41],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":100,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"executioner\",\"stone_skin\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"ap\",\"tier\":\"rare\"},{\"kind\":\"gp\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"ap\",\"tier\":\"rare\",\"value\":1}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":45,\"heal_gained\":3,\"hp_after\":26,\"hp_before\":23,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"executioner\",\"feat_name\":\"处刑台\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"stone_skin\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":25,\"heal_gained\":6,\"hp_after\":38,\"hp_before\":32,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"stone_skin\",\"feat_name\":\"石肤\",\"hp_after\":38,\"hp_before\":38,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":75,\"route\":\"min\",\"seal_hit\":245,\"seal_hit_gain\":-652,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":263,\"seed\":71025,\"shop_gp_seq\":[78,53],\"stopped_at\":0,\"taken\":100,\"visited_count\":19,\"wallet_count\":28}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":13,\"choices\":14,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,35,35,35,35,35],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":133,\"hp_pct\":0,\"keystone_total\":6,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"pain_memory\",\"stone_skin\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"ap\",\"tier\":\"rare\"},{\"kind\":\"armor\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"ap\",\"tier\":\"rare\",\"value\":1}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"pain_memory\",\"kind\":\"relic\",\"tier\":\"rare\"}],\"gp\":37,\"heal_gained\":4,\"hp_after\":26,\"hp_before\":22,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"pain_memory\",\"feat_name\":\"疼痛记忆\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"stone_skin\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":14,\"heal_gained\":7,\"hp_after\":32,\"hp_before\":25,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"stone_skin\",\"feat_name\":\"石肤\",\"hp_after\":32,\"hp_before\":32,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":63,\"route\":\"min\",\"seal_hit\":208,\"seal_hit_gain\":568,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":225,\"seed\":71026,\"shop_gp_seq\":[58],\"stopped_at\":3,\"taken\":133,\"visited_count\":17,\"wallet_count\":22}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":14,\"choices\":16,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,35,35,35,35,35,41],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":123,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"hard_hat\",\"stone_skin\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"rap\",\"tier\":\"rare\"},{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"time_sand\",\"tier\":\"rare\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"rap\",\"tier\":\"rare\",\"value\":3}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"hard_hat\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":32,\"heal_gained\":5,\"hp_after\":18,\"hp_before\":13,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"hard_hat\",\"feat_name\":\"安全帽\",\"hp_after\":18,\"hp_before\":18,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"stone_skin\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":23,\"heal_gained\":7,\"hp_after\":33,\"hp_before\":26,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"stone_skin\",\"feat_name\":\"石肤\",\"hp_after\":33,\"hp_before\":33,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":75,\"route\":\"min\",\"seal_hit\":249,\"seal_hit_gain\":944,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":270,\"seed\":71027,\"shop_gp_seq\":[52,68],\"stopped_at\":0,\"taken\":123,\"visited_count\":19,\"wallet_count\":27}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":8,\"choices\":8,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":58,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"fuse_wire\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"attack\",\"tier\":\"excellent\",\"value\":2}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"fuse_wire\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":38,\"heal_gained\":4,\"hp_after\":26,\"hp_before\":22,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"fuse_wire\",\"feat_name\":\"保险丝\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":28,\"route\":\"min\",\"seal_hit\":107,\"seal_hit_gain\":841,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":118,\"seed\":71028,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":58,\"visited_count\":10,\"wallet_count\":9}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":8,\"choices\":9,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":75,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"execution_scholar\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"gp\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"gp\",\"tier\":\"excellent\",\"value\":50}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":97,\"heal_gained\":1,\"hp_after\":26,\"hp_before\":25,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"execution_scholar\",\"feat_name\":\"斩首学者\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":35,\"route\":\"min\",\"seal_hit\":119,\"seal_hit_gain\":557,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":127,\"seed\":71029,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":75,\"visited_count\":11,\"wallet_count\":10}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":14,\"choices\":16,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,38,38,38,38,38,41],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":63,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"seal_burst\",\"map_exchange\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"heal\",\"tier\":\"excellent\"},{\"kind\":\"attack\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"heal\",\"tier\":\"excellent\",\"value\":6}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_burst\",\"kind\":\"relic\",\"tier\":\"rare\"}],\"gp\":49,\"heal_gained\":5,\"hp_after\":25,\"hp_before\":20,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"seal_burst\",\"feat_name\":\"封印爆裂\",\"hp_after\":25,\"hp_before\":25,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"map_exchange\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"stone_skin\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":16,\"heal_gained\":2,\"hp_after\":38,\"hp_before\":36,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"map_exchange\",\"feat_name\":\"弃图兑换\",\"hp_after\":38,\"hp_before\":38,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":49,\"route\":\"min\",\"seal_hit\":191,\"seal_hit_gain\":1182,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":204,\"seed\":71030,\"shop_gp_seq\":[66,41],\"stopped_at\":0,\"taken\":63,\"visited_count\":19,\"wallet_count\":27}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":8,\"choices\":8,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":63,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"三干中廊\",\"三干中廊\",\"三干中廊\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"last_stand\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"ap\",\"tier\":\"rare\"},{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"rap\",\"tier\":\"rare\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"ap\",\"tier\":\"rare\",\"value\":1}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"last_stand\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":27,\"heal_gained\":5,\"hp_after\":20,\"hp_before\":15,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"last_stand\",\"feat_name\":\"背水\",\"hp_after\":20,\"hp_before\":20,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":31,\"route\":\"min\",\"seal_hit\":124,\"seal_hit_gain\":736,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":132,\"seed\":71031,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":63,\"visited_count\":10,\"wallet_count\":9}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":15,\"choices\":16,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,36,36,36,36,36,48,48,48,48,48,51],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":82,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"trap_instinct\",\"blood_tax\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"max_hp\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"max_hp\",\"tier\":\"excellent\",\"value\":10}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"trap_instinct\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":43,\"heal_gained\":7,\"hp_after\":35,\"hp_before\":28,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"trap_instinct\",\"feat_name\":\"陷阱本能\",\"hp_after\":35,\"hp_before\":35,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_tax\",\"kind\":\"relic\",\"tier\":\"rare\"}],\"gp\":21,\"heal_gained\":9,\"hp_after\":47,\"hp_before\":38,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blood_tax\",\"feat_name\":\"血税\",\"hp_after\":47,\"hp_before\":47,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":66,\"route\":\"min\",\"seal_hit\":214,\"seal_hit_gain\":981,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":242,\"seed\":71032,\"shop_gp_seq\":[73,53],\"stopped_at\":0,\"taken\":82,\"visited_count\":19,\"wallet_count\":28}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":14,\"choices\":17,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,38,38,38,38,38,41],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":136,\"hp_pct\":0,\"keystone_total\":6,\"level_templates\":[\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":2,\"relics\":[\"executioner\",\"seal_smith\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"attack\",\"tier\":\"excellent\",\"value\":2}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":33,\"heal_gained\":5,\"hp_after\":16,\"hp_before\":11,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"executioner\",\"feat_name\":\"处刑台\",\"hp_after\":16,\"hp_before\":16,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_smith\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":21,\"heal_gained\":7,\"hp_after\":13,\"hp_before\":6,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"seal_smith\",\"feat_name\":\"封印匠\",\"hp_after\":13,\"hp_before\":13,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":66,\"route\":\"min\",\"seal_hit\":239,\"seal_hit_gain\":1653,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":250,\"seed\":71033,\"shop_gp_seq\":[74,69],\"stopped_at\":3,\"taken\":136,\"visited_count\":20,\"wallet_count\":28}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":15,\"choices\":17,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26,26,38,38,38,38,41],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":126,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"skip_mining\",\"execution_scholar\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"armor\",\"tier\":\"excellent\"},{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"feat_ticket\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"armor\",\"tier\":\"excellent\",\"value\":2}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"skip_mining\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":34,\"heal_gained\":5,\"hp_after\":21,\"hp_before\":16,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"skip_mining\",\"feat_name\":\"跳格采掘\",\"hp_after\":21,\"hp_before\":21,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":16,\"heal_gained\":7,\"hp_after\":26,\"hp_before\":19,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"execution_scholar\",\"feat_name\":\"斩首学者\",\"hp_after\":26,\"hp_before\":26,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":82,\"route\":\"min\",\"seal_hit\":270,\"seal_hit_gain\":783,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":286,\"seed\":71034,\"shop_gp_seq\":[80,57],\"stopped_at\":0,\"taken\":126,\"visited_count\":20,\"wallet_count\":29}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":14,\"choices\":15,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26,38,38,38,38,38],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":134,\"hp_pct\":0,\"keystone_total\":6,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"field_dressing\",\"blood_insight\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"ap\",\"tier\":\"rare\"},{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"gp\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"ap\",\"tier\":\"rare\",\"value\":1}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":59,\"heal_gained\":1,\"hp_after\":26,\"hp_before\":25,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"field_dressing\",\"feat_name\":\"战地包扎\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":37,\"heal_gained\":7,\"hp_after\":24,\"hp_before\":17,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blood_insight\",\"feat_name\":\"见血开窍\",\"hp_after\":24,\"hp_before\":24,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":68,\"route\":\"min\",\"seal_hit\":228,\"seal_hit_gain\":646,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":242,\"seed\":71035,\"shop_gp_seq\":[93],\"stopped_at\":3,\"taken\":134,\"visited_count\":18,\"wallet_count\":26}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":8,\"choices\":9,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":74,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"venom_edge\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"armor\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"armor\",\"tier\":\"excellent\",\"value\":2}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"venom_edge\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"seal_echo\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"map_exchange\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":48,\"heal_gained\":5,\"hp_after\":25,\"hp_before\":20,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"venom_edge\",\"feat_name\":\"淬毒刃纹\",\"hp_after\":25,\"hp_before\":25,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":36,\"route\":\"min\",\"seal_hit\":117,\"seal_hit_gain\":428,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":127,\"seed\":71036,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":74,\"visited_count\":11,\"wallet_count\":9}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":8,\"choices\":8,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":60,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"trap_instinct\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"rap\",\"tier\":\"rare\"},{\"kind\":\"feat_ticket\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"rap\",\"tier\":\"rare\",\"value\":3}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"trap_instinct\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":37,\"heal_gained\":2,\"hp_after\":26,\"hp_before\":24,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"trap_instinct\",\"feat_name\":\"陷阱本能\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":35,\"route\":\"min\",\"seal_hit\":121,\"seal_hit_gain\":1364,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":124,\"seed\":71037,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":60,\"visited_count\":10,\"wallet_count\":9}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":13,\"choices\":16,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,36,36,36,36,36,45,45,45,45,51],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":108,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"trap_instinct\",\"breaker_strike\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"max_hp\",\"tier\":\"excellent\"},{\"kind\":\"feat_ticket\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"max_hp\",\"tier\":\"excellent\",\"value\":10}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"trap_instinct\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":39,\"heal_gained\":7,\"hp_after\":32,\"hp_before\":25,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"trap_instinct\",\"feat_name\":\"陷阱本能\",\"hp_after\":32,\"hp_before\":32,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"breaker_strike\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"skip_mining\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":29,\"heal_gained\":9,\"hp_after\":31,\"hp_before\":22,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"breaker_strike\",\"feat_name\":\"破封一击\",\"hp_after\":31,\"hp_before\":31,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":73,\"route\":\"min\",\"seal_hit\":233,\"seal_hit_gain\":328,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":242,\"seed\":71038,\"shop_gp_seq\":[56,66],\"stopped_at\":0,\"taken\":108,\"visited_count\":19,\"wallet_count\":27}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":14,\"choices\":17,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,38,38,38,38,38,44],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":116,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":3,\"relics\":[\"blood_insight\",\"seal_smith\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"rap\",\"tier\":\"rare\"},{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"time_sand\",\"tier\":\"rare\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"rap\",\"tier\":\"rare\",\"value\":3}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":49,\"heal_gained\":5,\"hp_after\":24,\"hp_before\":19,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blood_insight\",\"feat_name\":\"见血开窍\",\"hp_after\":24,\"hp_before\":24,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_smith\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":26,\"heal_gained\":7,\"hp_after\":27,\"hp_before\":20,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"seal_smith\",\"feat_name\":\"封印匠\",\"hp_after\":27,\"hp_before\":27,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":86,\"route\":\"min\",\"seal_hit\":254,\"seal_hit_gain\":-155,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":275,\"seed\":71039,\"shop_gp_seq\":[78,78],\"stopped_at\":0,\"taken\":116,\"visited_count\":20,\"wallet_count\":30}" + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":3,\"choices\":2,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":8,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#71000\",\"1#71000\",\"1#71000\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":4,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":5,\"route\":\"min\",\"seal_hit\":22,\"seal_hit_gain\":79,\"seal_miss\":3,\"seal_miss_penalty\":8,\"seal_placed\":25,\"seed\":71000,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":24,\"visited_count\":3,\"wallet_count\":4}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":3,\"choices\":2,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":10,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#71001\",\"1#71001\",\"1#71001\"],\"levels_cleared\":0,\"mine_dmg\":0,\"mine_hits\":6,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":6,\"route\":\"min\",\"seal_hit\":21,\"seal_hit_gain\":-296,\"seal_miss\":7,\"seal_miss_penalty\":16,\"seal_placed\":31,\"seed\":71001,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":10,\"visited_count\":3,\"wallet_count\":4}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":2,\"choices\":1,\"cleared\":false,\"encounter_max_hp_seq\":[26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":5,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#71002\",\"1#71002\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":2,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":5,\"route\":\"min\",\"seal_hit\":17,\"seal_hit_gain\":391,\"seal_miss\":1,\"seal_miss_penalty\":8,\"seal_placed\":19,\"seed\":71002,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":21,\"visited_count\":2,\"wallet_count\":3}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":5,\"choices\":4,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":15,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#71003\",\"1#71003\",\"1#71003\",\"1#71003\",\"1#71003\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":6,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":13,\"route\":\"min\",\"seal_hit\":28,\"seal_hit_gain\":317,\"seal_miss\":9,\"seal_miss_penalty\":24,\"seal_placed\":44,\"seed\":71003,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":23,\"visited_count\":5,\"wallet_count\":6}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":6,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,32],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":39,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#71004\",\"1#71004\",\"1#71004\",\"1#71004\",\"1#71004\",\"1#71004\",\"1#71004\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":17,\"policy\":\"normal\",\"refreshes\":1,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":28,\"route\":\"min\",\"seal_hit\":70,\"seal_hit_gain\":23,\"seal_miss\":17,\"seal_miss_penalty\":8,\"seal_placed\":93,\"seed\":71004,\"shop_gp_seq\":[19],\"stopped_at\":1,\"taken\":47,\"visited_count\":7,\"wallet_count\":11}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":5,\"choices\":4,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":26,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#71005\",\"1#71005\",\"1#71005\",\"1#71005\",\"1#71005\"],\"levels_cleared\":0,\"mine_dmg\":22,\"mine_hits\":8,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":15,\"route\":\"min\",\"seal_hit\":39,\"seal_hit_gain\":284,\"seal_miss\":11,\"seal_miss_penalty\":0,\"seal_placed\":56,\"seed\":71005,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":48,\"visited_count\":5,\"wallet_count\":6}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":5,\"choices\":4,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":17,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#71006\",\"1#71006\",\"1#71006\",\"1#71006\",\"1#71006\"],\"levels_cleared\":0,\"mine_dmg\":6,\"mine_hits\":6,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":13,\"route\":\"min\",\"seal_hit\":39,\"seal_hit_gain\":291,\"seal_miss\":5,\"seal_miss_penalty\":6,\"seal_placed\":50,\"seed\":71006,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":23,\"visited_count\":5,\"wallet_count\":6}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":6,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,35],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":28,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#71007\",\"1#71007\",\"1#71007\",\"1#71007\",\"1#71007\",\"1#71007\",\"1#71007\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":8,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":24,\"route\":\"min\",\"seal_hit\":59,\"seal_hit_gain\":-70,\"seal_miss\":11,\"seal_miss_penalty\":12,\"seal_placed\":74,\"seed\":71007,\"shop_gp_seq\":[39],\"stopped_at\":1,\"taken\":44,\"visited_count\":7,\"wallet_count\":14}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":7,\"choices\":7,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,32,32,32],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":38,\"hp_pct\":0,\"keystone_total\":3,\"level_maps\":[\"1#71008\",\"1#71008\",\"1#71008\",\"1#71008\",\"1#71008\",\"1#71008\",\"1#71008\",\"2#71008\",\"2#71008\"],\"levels_cleared\":1,\"mine_dmg\":16,\"mine_hits\":18,\"policy\":\"normal\",\"refreshes\":1,\"relics\":[\"hard_hat\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":6,\"candidates\":[{\"feat_id\":\"hard_hat\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":22,\"heal_gained\":6,\"hp_after\":11,\"hp_before\":5,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"hard_hat\",\"feat_name\":\"安全帽\",\"hp_after\":11,\"hp_before\":11,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":33,\"route\":\"min\",\"seal_hit\":83,\"seal_hit_gain\":483,\"seal_miss\":14,\"seal_miss_penalty\":8,\"seal_placed\":102,\"seed\":71008,\"shop_gp_seq\":[19],\"stopped_at\":2,\"taken\":54,\"visited_count\":9,\"wallet_count\":12}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":4,\"choices\":3,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":18,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#71009\",\"1#71009\",\"1#71009\",\"1#71009\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":3,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":9,\"route\":\"min\",\"seal_hit\":30,\"seal_hit_gain\":-28,\"seal_miss\":4,\"seal_miss_penalty\":8,\"seal_placed\":35,\"seed\":71009,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":26,\"visited_count\":4,\"wallet_count\":5}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":9,\"choices\":8,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,35,35,35,35],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":60,\"hp_pct\":0,\"keystone_total\":3,\"level_maps\":[\"1#71010\",\"1#71010\",\"1#71010\",\"1#71010\",\"1#71010\",\"1#71010\",\"1#71010\",\"2#71010\",\"2#71010\",\"2#71010\"],\"levels_cleared\":1,\"mine_dmg\":24,\"mine_hits\":13,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[\"hard_hat\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":9,\"candidates\":[{\"feat_id\":\"hard_hat\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"breaker_strike\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":13,\"heal_gained\":7,\"hp_after\":18,\"hp_before\":11,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"hard_hat\",\"feat_name\":\"安全帽\",\"hp_after\":18,\"hp_before\":18,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":47,\"route\":\"min\",\"seal_hit\":109,\"seal_hit_gain\":309,\"seal_miss\":14,\"seal_miss_penalty\":8,\"seal_placed\":129,\"seed\":71010,\"shop_gp_seq\":[27],\"stopped_at\":2,\"taken\":84,\"visited_count\":10,\"wallet_count\":16}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":5,\"choices\":4,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":18,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#71011\",\"1#71011\",\"1#71011\",\"1#71011\",\"1#71011\"],\"levels_cleared\":0,\"mine_dmg\":0,\"mine_hits\":7,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":15,\"route\":\"min\",\"seal_hit\":47,\"seal_hit_gain\":-119,\"seal_miss\":9,\"seal_miss_penalty\":24,\"seal_placed\":61,\"seed\":71011,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":18,\"visited_count\":5,\"wallet_count\":6}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":4,\"choices\":3,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":15,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#71012\",\"1#71012\",\"1#71012\",\"1#71012\"],\"levels_cleared\":0,\"mine_dmg\":0,\"mine_hits\":8,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":11,\"route\":\"min\",\"seal_hit\":34,\"seal_hit_gain\":89,\"seal_miss\":10,\"seal_miss_penalty\":14,\"seal_placed\":47,\"seed\":71012,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":15,\"visited_count\":4,\"wallet_count\":5}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":2,\"choices\":1,\"cleared\":false,\"encounter_max_hp_seq\":[26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":6,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#71013\",\"1#71013\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":4,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":3,\"route\":\"min\",\"seal_hit\":10,\"seal_hit_gain\":-21,\"seal_miss\":2,\"seal_miss_penalty\":8,\"seal_placed\":15,\"seed\":71013,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":22,\"visited_count\":2,\"wallet_count\":3}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":5,\"choices\":4,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":24,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#71014\",\"1#71014\",\"1#71014\",\"1#71014\",\"1#71014\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":6,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":11,\"route\":\"min\",\"seal_hit\":32,\"seal_hit_gain\":-62,\"seal_miss\":2,\"seal_miss_penalty\":0,\"seal_placed\":40,\"seed\":71014,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":32,\"visited_count\":5,\"wallet_count\":6}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":5,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,32],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":24,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#71015\",\"1#71015\",\"1#71015\",\"1#71015\",\"1#71015\",\"1#71015\",\"1#71015\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":8,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":17,\"route\":\"min\",\"seal_hit\":51,\"seal_hit_gain\":8,\"seal_miss\":9,\"seal_miss_penalty\":14,\"seal_placed\":68,\"seed\":71015,\"shop_gp_seq\":[25],\"stopped_at\":1,\"taken\":40,\"visited_count\":7,\"wallet_count\":11}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":5,\"choices\":4,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":25,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#71016\",\"1#71016\",\"1#71016\",\"1#71016\",\"1#71016\"],\"levels_cleared\":0,\"mine_dmg\":6,\"mine_hits\":4,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":14,\"route\":\"min\",\"seal_hit\":50,\"seal_hit_gain\":421,\"seal_miss\":6,\"seal_miss_penalty\":4,\"seal_placed\":61,\"seed\":71016,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":31,\"visited_count\":5,\"wallet_count\":6}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":1,\"choices\":0,\"cleared\":false,\"encounter_max_hp_seq\":[26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":1,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#71017\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":2,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":2,\"route\":\"min\",\"seal_hit\":7,\"seal_hit_gain\":7,\"seal_miss\":4,\"seal_miss_penalty\":16,\"seal_placed\":11,\"seed\":71017,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":17,\"visited_count\":1,\"wallet_count\":2}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":4,\"choices\":3,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":33,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#71018\",\"1#71018\",\"1#71018\",\"1#71018\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":3,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":11,\"route\":\"min\",\"seal_hit\":40,\"seal_hit_gain\":416,\"seal_miss\":2,\"seal_miss_penalty\":-2,\"seal_placed\":45,\"seed\":71018,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":41,\"visited_count\":4,\"wallet_count\":5}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":2,\"choices\":1,\"cleared\":false,\"encounter_max_hp_seq\":[26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":7,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#71019\",\"1#71019\"],\"levels_cleared\":0,\"mine_dmg\":0,\"mine_hits\":10,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":5,\"route\":\"min\",\"seal_hit\":11,\"seal_hit_gain\":-85,\"seal_miss\":10,\"seal_miss_penalty\":24,\"seal_placed\":22,\"seed\":71019,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":7,\"visited_count\":2,\"wallet_count\":3}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":20,\"choices\":28,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,35,35,35,35,35,35,41,41,41,41,41,41,41,41,44],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":154,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#71020\",\"1#71020\",\"1#71020\",\"1#71020\",\"1#71020\",\"1#71020\",\"1#71020\",\"2#71020\",\"2#71020\",\"2#71020\",\"2#71020\",\"2#71020\",\"2#71020\",\"2#71020\",\"2#71020\",\"2#71020\",\"2#71020\",\"3#71020\",\"3#71020\",\"3#71020\",\"3#71020\",\"3#71020\",\"3#71020\",\"3#71020\",\"3#71020\",\"3#71020\",\"3#71020\",\"3#71020\",\"3#71020\",\"3#71020\",\"3#71020\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":2,\"relics\":[\"pain_memory\",\"map_exchange\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":9,\"candidates\":[{\"feat_id\":\"pain_memory\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":20,\"heal_gained\":7,\"hp_after\":25,\"hp_before\":18,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"pain_memory\",\"feat_name\":\"疼痛记忆\",\"hp_after\":25,\"hp_before\":25,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":15,\"candidates\":[{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"map_exchange\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":27,\"heal_gained\":6,\"hp_after\":41,\"hp_before\":35,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"map_exchange\",\"feat_name\":\"弃图兑换\",\"hp_after\":41,\"hp_before\":41,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":102,\"route\":\"min\",\"seal_hit\":345,\"seal_hit_gain\":1539,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":362,\"seed\":71020,\"shop_gp_seq\":[27,62,86],\"stopped_at\":0,\"taken\":154,\"visited_count\":31,\"wallet_count\":38}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":19,\"choices\":23,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,35,35,35,35,35,38,38,38,38,38,38,38,38,38],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":139,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#71021\",\"1#71021\",\"1#71021\",\"1#71021\",\"1#71021\",\"1#71021\",\"1#71021\",\"2#71021\",\"2#71021\",\"2#71021\",\"2#71021\",\"2#71021\",\"2#71021\",\"2#71021\",\"2#71021\",\"2#71021\",\"2#71021\",\"3#71021\",\"3#71021\",\"3#71021\",\"3#71021\",\"3#71021\",\"3#71021\",\"3#71021\",\"3#71021\",\"3#71021\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":2,\"relics\":[\"executioner\",\"seal_echo\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":9,\"candidates\":[{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":16,\"heal_gained\":3,\"hp_after\":35,\"hp_before\":32,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"executioner\",\"feat_name\":\"处刑台\",\"hp_after\":35,\"hp_before\":35,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":12,\"candidates\":[{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_echo\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":16,\"heal_gained\":0,\"hp_after\":38,\"hp_before\":38,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"seal_echo\",\"feat_name\":\"封印回响\",\"hp_after\":38,\"hp_before\":38,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":85,\"route\":\"min\",\"seal_hit\":282,\"seal_hit_gain\":1672,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":307,\"seed\":71021,\"shop_gp_seq\":[34,47],\"stopped_at\":3,\"taken\":139,\"visited_count\":26,\"wallet_count\":31}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":17,\"choices\":19,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,35,35,35,35,35,35,41,41,41,41,41,41],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":117,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#71022\",\"1#71022\",\"1#71022\",\"1#71022\",\"1#71022\",\"1#71022\",\"1#71022\",\"2#71022\",\"2#71022\",\"2#71022\",\"2#71022\",\"2#71022\",\"2#71022\",\"2#71022\",\"2#71022\",\"2#71022\",\"2#71022\",\"3#71022\",\"3#71022\",\"3#71022\",\"3#71022\",\"3#71022\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"breaker_strike\",\"trap_burst\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":9,\"candidates\":[{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"breaker_strike\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":25,\"heal_gained\":3,\"hp_after\":35,\"hp_before\":32,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"breaker_strike\",\"feat_name\":\"破封一击\",\"hp_after\":35,\"hp_before\":35,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":15,\"candidates\":[{\"feat_id\":\"trap_burst\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":23,\"heal_gained\":2,\"hp_after\":41,\"hp_before\":39,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"trap_burst\",\"feat_name\":\"陷阱爆发\",\"hp_after\":41,\"hp_before\":41,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":78,\"route\":\"min\",\"seal_hit\":269,\"seal_hit_gain\":1054,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":288,\"seed\":71022,\"shop_gp_seq\":[39,61],\"stopped_at\":3,\"taken\":117,\"visited_count\":22,\"wallet_count\":30}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":16,\"choices\":19,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,35,35,35,35,35,35,38,38,38,38,38,38],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":131,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#71023\",\"1#71023\",\"1#71023\",\"1#71023\",\"1#71023\",\"1#71023\",\"1#71023\",\"2#71023\",\"2#71023\",\"2#71023\",\"2#71023\",\"2#71023\",\"2#71023\",\"2#71023\",\"2#71023\",\"2#71023\",\"2#71023\",\"3#71023\",\"3#71023\",\"3#71023\",\"3#71023\",\"3#71023\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"blood_honey\",\"skip_mining\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":9,\"candidates\":[{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":10,\"heal_gained\":5,\"hp_after\":35,\"hp_before\":30,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blood_honey\",\"feat_name\":\"血蜜\",\"hp_after\":35,\"hp_before\":35,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":12,\"candidates\":[{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"skip_mining\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":16,\"heal_gained\":0,\"hp_after\":38,\"hp_before\":38,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"skip_mining\",\"feat_name\":\"跳格采掘\",\"hp_after\":38,\"hp_before\":38,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":80,\"route\":\"min\",\"seal_hit\":255,\"seal_hit_gain\":650,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":274,\"seed\":71023,\"shop_gp_seq\":[26,37],\"stopped_at\":3,\"taken\":131,\"visited_count\":22,\"wallet_count\":26}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":19,\"choices\":28,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,32,32,32,32,32,32,38,38,38,38,38,38,38,38,44],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":142,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#71024\",\"1#71024\",\"1#71024\",\"1#71024\",\"1#71024\",\"1#71024\",\"1#71024\",\"2#71024\",\"2#71024\",\"2#71024\",\"2#71024\",\"2#71024\",\"2#71024\",\"2#71024\",\"2#71024\",\"2#71024\",\"2#71024\",\"3#71024\",\"3#71024\",\"3#71024\",\"3#71024\",\"3#71024\",\"3#71024\",\"3#71024\",\"3#71024\",\"3#71024\",\"3#71024\",\"3#71024\",\"3#71024\",\"3#71024\",\"3#71024\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":2,\"relics\":[\"chain_traps\",\"map_exchange\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":6,\"candidates\":[{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"chain_traps\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":21,\"heal_gained\":1,\"hp_after\":32,\"hp_before\":31,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"chain_traps\",\"feat_name\":\"连环陷阱\",\"hp_after\":32,\"hp_before\":32,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":12,\"candidates\":[{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"map_exchange\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"seal_echo\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":16,\"heal_gained\":5,\"hp_after\":38,\"hp_before\":33,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"map_exchange\",\"feat_name\":\"弃图兑换\",\"hp_after\":38,\"hp_before\":38,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":102,\"route\":\"min\",\"seal_hit\":338,\"seal_hit_gain\":1807,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":354,\"seed\":71024,\"shop_gp_seq\":[16,68,68],\"stopped_at\":0,\"taken\":142,\"visited_count\":31,\"wallet_count\":35}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":19,\"choices\":28,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,35,35,35,35,35,35,41,41,41,41,41,41,41,47],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":139,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#71025\",\"1#71025\",\"1#71025\",\"1#71025\",\"1#71025\",\"1#71025\",\"1#71025\",\"2#71025\",\"2#71025\",\"2#71025\",\"2#71025\",\"2#71025\",\"2#71025\",\"2#71025\",\"2#71025\",\"2#71025\",\"2#71025\",\"3#71025\",\"3#71025\",\"3#71025\",\"3#71025\",\"3#71025\",\"3#71025\",\"3#71025\",\"3#71025\",\"3#71025\",\"3#71025\",\"3#71025\",\"3#71025\",\"3#71025\",\"3#71025\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"market_close\",\"warning_beacon\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":9,\"candidates\":[{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":16,\"heal_gained\":5,\"hp_after\":35,\"hp_before\":30,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"market_close\",\"feat_name\":\"收盘\",\"hp_after\":35,\"hp_before\":35,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":15,\"candidates\":[{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":42,\"heal_gained\":8,\"hp_after\":25,\"hp_before\":17,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"warning_beacon\",\"feat_name\":\"示警烟火\",\"hp_after\":25,\"hp_before\":25,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":96,\"route\":\"min\",\"seal_hit\":329,\"seal_hit_gain\":1701,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":350,\"seed\":71025,\"shop_gp_seq\":[33,79,101],\"stopped_at\":0,\"taken\":139,\"visited_count\":31,\"wallet_count\":38}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":20,\"choices\":28,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,32,32,32,32,32,32,41,41,41,41,41,41,41,41,47],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":131,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#71026\",\"1#71026\",\"1#71026\",\"1#71026\",\"1#71026\",\"1#71026\",\"1#71026\",\"2#71026\",\"2#71026\",\"2#71026\",\"2#71026\",\"2#71026\",\"2#71026\",\"2#71026\",\"2#71026\",\"2#71026\",\"2#71026\",\"3#71026\",\"3#71026\",\"3#71026\",\"3#71026\",\"3#71026\",\"3#71026\",\"3#71026\",\"3#71026\",\"3#71026\",\"3#71026\",\"3#71026\",\"3#71026\",\"3#71026\",\"3#71026\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"market_close\",\"execution_scholar\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":6,\"candidates\":[{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":19,\"heal_gained\":4,\"hp_after\":32,\"hp_before\":28,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"market_close\",\"feat_name\":\"收盘\",\"hp_after\":32,\"hp_before\":32,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":15,\"candidates\":[{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":19,\"heal_gained\":7,\"hp_after\":41,\"hp_before\":34,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"execution_scholar\",\"feat_name\":\"斩首学者\",\"hp_after\":41,\"hp_before\":41,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":92,\"route\":\"min\",\"seal_hit\":299,\"seal_hit_gain\":1397,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":323,\"seed\":71026,\"shop_gp_seq\":[22,72,114],\"stopped_at\":0,\"taken\":131,\"visited_count\":31,\"wallet_count\":39}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":16,\"choices\":18,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,32,32,32,32,32,32,38,38,38,38,38],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":119,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#71027\",\"1#71027\",\"1#71027\",\"1#71027\",\"1#71027\",\"1#71027\",\"1#71027\",\"2#71027\",\"2#71027\",\"2#71027\",\"2#71027\",\"2#71027\",\"2#71027\",\"2#71027\",\"2#71027\",\"2#71027\",\"2#71027\",\"3#71027\",\"3#71027\",\"3#71027\",\"3#71027\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"hard_hat\",\"fuse_wire\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":6,\"candidates\":[{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"hard_hat\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"chain_traps\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":12,\"heal_gained\":6,\"hp_after\":20,\"hp_before\":14,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"hard_hat\",\"feat_name\":\"安全帽\",\"hp_after\":20,\"hp_before\":20,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":12,\"candidates\":[{\"feat_id\":\"fuse_wire\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"venom_edge\",\"kind\":\"relic\",\"tier\":\"rare\"}],\"gp\":15,\"heal_gained\":7,\"hp_after\":35,\"hp_before\":28,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"fuse_wire\",\"feat_name\":\"保险丝\",\"hp_after\":35,\"hp_before\":35,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":75,\"route\":\"min\",\"seal_hit\":255,\"seal_hit_gain\":1770,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":272,\"seed\":71027,\"shop_gp_seq\":[20,50],\"stopped_at\":3,\"taken\":119,\"visited_count\":21,\"wallet_count\":27}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":5,\"choices\":4,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":38,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#71028\",\"1#71028\",\"1#71028\",\"1#71028\",\"1#71028\"],\"levels_cleared\":0,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":16,\"route\":\"min\",\"seal_hit\":60,\"seal_hit_gain\":-8,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":62,\"seed\":71028,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":38,\"visited_count\":5,\"wallet_count\":6}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":15,\"choices\":18,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,32,32,32,32,32,38,38,38,38,38],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":105,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#71029\",\"1#71029\",\"1#71029\",\"1#71029\",\"1#71029\",\"1#71029\",\"1#71029\",\"2#71029\",\"2#71029\",\"2#71029\",\"2#71029\",\"2#71029\",\"2#71029\",\"2#71029\",\"2#71029\",\"2#71029\",\"2#71029\",\"3#71029\",\"3#71029\",\"3#71029\",\"3#71029\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"last_drop\",\"twin_pick\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":6,\"candidates\":[{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":24,\"heal_gained\":4,\"hp_after\":32,\"hp_before\":28,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"last_drop\",\"feat_name\":\"一滴不剩\",\"hp_after\":32,\"hp_before\":32,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":12,\"candidates\":[{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":29,\"heal_gained\":7,\"hp_after\":37,\"hp_before\":30,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"twin_pick\",\"feat_name\":\"双管镐\",\"hp_after\":37,\"hp_before\":37,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":64,\"route\":\"min\",\"seal_hit\":244,\"seal_hit_gain\":1307,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":262,\"seed\":71029,\"shop_gp_seq\":[24,57],\"stopped_at\":3,\"taken\":105,\"visited_count\":21,\"wallet_count\":26}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":15,\"choices\":19,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,32,32,32,32,32,38,38,38,38,38],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":114,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#71030\",\"1#71030\",\"1#71030\",\"1#71030\",\"1#71030\",\"1#71030\",\"1#71030\",\"2#71030\",\"2#71030\",\"2#71030\",\"2#71030\",\"2#71030\",\"2#71030\",\"2#71030\",\"2#71030\",\"2#71030\",\"2#71030\",\"3#71030\",\"3#71030\",\"3#71030\",\"3#71030\",\"3#71030\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"market_close\",\"chain_traps\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":6,\"candidates\":[{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":13,\"heal_gained\":2,\"hp_after\":32,\"hp_before\":30,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"market_close\",\"feat_name\":\"收盘\",\"hp_after\":32,\"hp_before\":32,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":12,\"candidates\":[{\"feat_id\":\"chain_traps\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":42,\"heal_gained\":7,\"hp_after\":35,\"hp_before\":28,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"chain_traps\",\"feat_name\":\"连环陷阱\",\"hp_after\":35,\"hp_before\":35,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":71,\"route\":\"min\",\"seal_hit\":234,\"seal_hit_gain\":1765,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":255,\"seed\":71030,\"shop_gp_seq\":[23,54],\"stopped_at\":3,\"taken\":114,\"visited_count\":22,\"wallet_count\":26}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":19,\"choices\":23,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,32,32,32,32,32,32,38,38,38,38,38,38,38,38,38],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":142,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#71031\",\"1#71031\",\"1#71031\",\"1#71031\",\"1#71031\",\"1#71031\",\"1#71031\",\"2#71031\",\"2#71031\",\"2#71031\",\"2#71031\",\"2#71031\",\"2#71031\",\"2#71031\",\"2#71031\",\"2#71031\",\"2#71031\",\"3#71031\",\"3#71031\",\"3#71031\",\"3#71031\",\"3#71031\",\"3#71031\",\"3#71031\",\"3#71031\",\"3#71031\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":2,\"relics\":[\"chain_traps\",\"seal_echo\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":6,\"candidates\":[{\"feat_id\":\"chain_traps\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":28,\"heal_gained\":6,\"hp_after\":31,\"hp_before\":25,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"chain_traps\",\"feat_name\":\"连环陷阱\",\"hp_after\":31,\"hp_before\":31,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":12,\"candidates\":[{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_echo\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"skip_mining\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":23,\"heal_gained\":7,\"hp_after\":37,\"hp_before\":30,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"seal_echo\",\"feat_name\":\"封印回响\",\"hp_after\":37,\"hp_before\":37,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":89,\"route\":\"min\",\"seal_hit\":303,\"seal_hit_gain\":1540,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":324,\"seed\":71031,\"shop_gp_seq\":[21,69],\"stopped_at\":3,\"taken\":142,\"visited_count\":26,\"wallet_count\":31}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":19,\"choices\":28,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,35,35,35,35,35,35,41,41,41,41,41,41,41,44],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":144,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#71032\",\"1#71032\",\"1#71032\",\"1#71032\",\"1#71032\",\"1#71032\",\"1#71032\",\"2#71032\",\"2#71032\",\"2#71032\",\"2#71032\",\"2#71032\",\"2#71032\",\"2#71032\",\"2#71032\",\"2#71032\",\"2#71032\",\"3#71032\",\"3#71032\",\"3#71032\",\"3#71032\",\"3#71032\",\"3#71032\",\"3#71032\",\"3#71032\",\"3#71032\",\"3#71032\",\"3#71032\",\"3#71032\",\"3#71032\",\"3#71032\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":3,\"relics\":[\"executioner\",\"market_close\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":9,\"candidates\":[{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":19,\"heal_gained\":5,\"hp_after\":35,\"hp_before\":30,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"executioner\",\"feat_name\":\"处刑台\",\"hp_after\":35,\"hp_before\":35,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":15,\"candidates\":[{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":16,\"heal_gained\":3,\"hp_after\":41,\"hp_before\":38,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"market_close\",\"feat_name\":\"收盘\",\"hp_after\":41,\"hp_before\":41,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":105,\"route\":\"min\",\"seal_hit\":330,\"seal_hit_gain\":1031,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":362,\"seed\":71032,\"shop_gp_seq\":[27,64,78],\"stopped_at\":0,\"taken\":144,\"visited_count\":31,\"wallet_count\":36}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":16,\"choices\":19,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,32,32,32,32,32,32,38,38,38,38,38,38],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":117,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#71033\",\"1#71033\",\"1#71033\",\"1#71033\",\"1#71033\",\"1#71033\",\"1#71033\",\"2#71033\",\"2#71033\",\"2#71033\",\"2#71033\",\"2#71033\",\"2#71033\",\"2#71033\",\"2#71033\",\"2#71033\",\"2#71033\",\"3#71033\",\"3#71033\",\"3#71033\",\"3#71033\",\"3#71033\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"map_exchange\",\"skip_mining\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":6,\"candidates\":[{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"map_exchange\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":16,\"heal_gained\":5,\"hp_after\":32,\"hp_before\":27,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"map_exchange\",\"feat_name\":\"弃图兑换\",\"hp_after\":32,\"hp_before\":32,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":12,\"candidates\":[{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"skip_mining\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":23,\"heal_gained\":5,\"hp_after\":38,\"hp_before\":33,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"skip_mining\",\"feat_name\":\"跳格采掘\",\"hp_after\":38,\"hp_before\":38,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":78,\"route\":\"min\",\"seal_hit\":240,\"seal_hit_gain\":654,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":255,\"seed\":71033,\"shop_gp_seq\":[20,45],\"stopped_at\":3,\"taken\":117,\"visited_count\":22,\"wallet_count\":26}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":19,\"choices\":28,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,35,35,35,35,35,35,41,41,41,41,41,41,41,44],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":151,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#71034\",\"1#71034\",\"1#71034\",\"1#71034\",\"1#71034\",\"1#71034\",\"1#71034\",\"2#71034\",\"2#71034\",\"2#71034\",\"2#71034\",\"2#71034\",\"2#71034\",\"2#71034\",\"2#71034\",\"2#71034\",\"2#71034\",\"3#71034\",\"3#71034\",\"3#71034\",\"3#71034\",\"3#71034\",\"3#71034\",\"3#71034\",\"3#71034\",\"3#71034\",\"3#71034\",\"3#71034\",\"3#71034\",\"3#71034\",\"3#71034\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"executioner\",\"blood_insight\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":9,\"candidates\":[{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":22,\"heal_gained\":7,\"hp_after\":30,\"hp_before\":23,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"executioner\",\"feat_name\":\"处刑台\",\"hp_after\":30,\"hp_before\":30,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":15,\"candidates\":[{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":13,\"heal_gained\":5,\"hp_after\":41,\"hp_before\":36,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blood_insight\",\"feat_name\":\"见血开窍\",\"hp_after\":41,\"hp_before\":41,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":106,\"route\":\"min\",\"seal_hit\":332,\"seal_hit_gain\":1732,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":351,\"seed\":71034,\"shop_gp_seq\":[29,74,76],\"stopped_at\":0,\"taken\":151,\"visited_count\":31,\"wallet_count\":37}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":19,\"choices\":28,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,35,35,35,35,35,35,41,41,41,41,41,41,41,47],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":144,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#71035\",\"1#71035\",\"1#71035\",\"1#71035\",\"1#71035\",\"1#71035\",\"1#71035\",\"2#71035\",\"2#71035\",\"2#71035\",\"2#71035\",\"2#71035\",\"2#71035\",\"2#71035\",\"2#71035\",\"2#71035\",\"2#71035\",\"3#71035\",\"3#71035\",\"3#71035\",\"3#71035\",\"3#71035\",\"3#71035\",\"3#71035\",\"3#71035\",\"3#71035\",\"3#71035\",\"3#71035\",\"3#71035\",\"3#71035\",\"3#71035\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"trap_instinct\",\"blast_accounting\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":9,\"candidates\":[{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"trap_instinct\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":13,\"heal_gained\":4,\"hp_after\":35,\"hp_before\":31,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"trap_instinct\",\"feat_name\":\"陷阱本能\",\"hp_after\":35,\"hp_before\":35,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":15,\"candidates\":[{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":33,\"heal_gained\":6,\"hp_after\":41,\"hp_before\":35,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blast_accounting\",\"feat_name\":\"爆破会计\",\"hp_after\":41,\"hp_before\":41,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":96,\"route\":\"min\",\"seal_hit\":316,\"seal_hit_gain\":1841,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":340,\"seed\":71035,\"shop_gp_seq\":[32,58,100],\"stopped_at\":0,\"taken\":144,\"visited_count\":31,\"wallet_count\":38}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":19,\"choices\":28,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,32,32,32,32,32,32,38,38,38,38,38,38,38,44],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":148,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#71036\",\"1#71036\",\"1#71036\",\"1#71036\",\"1#71036\",\"1#71036\",\"1#71036\",\"2#71036\",\"2#71036\",\"2#71036\",\"2#71036\",\"2#71036\",\"2#71036\",\"2#71036\",\"2#71036\",\"2#71036\",\"2#71036\",\"3#71036\",\"3#71036\",\"3#71036\",\"3#71036\",\"3#71036\",\"3#71036\",\"3#71036\",\"3#71036\",\"3#71036\",\"3#71036\",\"3#71036\",\"3#71036\",\"3#71036\",\"3#71036\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"skip_mining\",\"frontier_miner\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":6,\"candidates\":[{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"skip_mining\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":26,\"heal_gained\":6,\"hp_after\":23,\"hp_before\":17,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"skip_mining\",\"feat_name\":\"跳格采掘\",\"hp_after\":23,\"hp_before\":23,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":12,\"candidates\":[{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":23,\"heal_gained\":7,\"hp_after\":38,\"hp_before\":31,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"frontier_miner\",\"feat_name\":\"边陲矿工\",\"hp_after\":38,\"hp_before\":38,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":100,\"route\":\"min\",\"seal_hit\":331,\"seal_hit_gain\":1293,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":354,\"seed\":71036,\"shop_gp_seq\":[18,68,74],\"stopped_at\":0,\"taken\":148,\"visited_count\":31,\"wallet_count\":35}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":17,\"choices\":28,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,29,29,29,29,29,35,35,35,35,35,35,35,41],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":155,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#71037\",\"1#71037\",\"1#71037\",\"1#71037\",\"1#71037\",\"1#71037\",\"1#71037\",\"2#71037\",\"2#71037\",\"2#71037\",\"2#71037\",\"2#71037\",\"2#71037\",\"2#71037\",\"2#71037\",\"2#71037\",\"2#71037\",\"3#71037\",\"3#71037\",\"3#71037\",\"3#71037\",\"3#71037\",\"3#71037\",\"3#71037\",\"3#71037\",\"3#71037\",\"3#71037\",\"3#71037\",\"3#71037\",\"3#71037\",\"3#71037\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":2,\"relics\":[\"breaker_strike\",\"field_dressing\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":3,\"candidates\":[{\"feat_id\":\"breaker_strike\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":15,\"heal_gained\":1,\"hp_after\":29,\"hp_before\":28,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"breaker_strike\",\"feat_name\":\"破封一击\",\"hp_after\":29,\"hp_before\":29,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":9,\"candidates\":[{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":35,\"heal_gained\":7,\"hp_after\":35,\"hp_before\":28,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"field_dressing\",\"feat_name\":\"战地包扎\",\"hp_after\":35,\"hp_before\":35,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":95,\"route\":\"min\",\"seal_hit\":290,\"seal_hit_gain\":191,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":310,\"seed\":71037,\"shop_gp_seq\":[9,43,95],\"stopped_at\":0,\"taken\":155,\"visited_count\":31,\"wallet_count\":32}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":19,\"choices\":28,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,35,35,35,35,35,35,38,38,38,38,38,38,38,44],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":127,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#71038\",\"1#71038\",\"1#71038\",\"1#71038\",\"1#71038\",\"1#71038\",\"1#71038\",\"2#71038\",\"2#71038\",\"2#71038\",\"2#71038\",\"2#71038\",\"2#71038\",\"2#71038\",\"2#71038\",\"2#71038\",\"2#71038\",\"3#71038\",\"3#71038\",\"3#71038\",\"3#71038\",\"3#71038\",\"3#71038\",\"3#71038\",\"3#71038\",\"3#71038\",\"3#71038\",\"3#71038\",\"3#71038\",\"3#71038\",\"3#71038\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"last_stand\",\"blast_accounting\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":9,\"candidates\":[{\"feat_id\":\"last_stand\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":19,\"heal_gained\":6,\"hp_after\":35,\"hp_before\":29,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"last_stand\",\"feat_name\":\"背水\",\"hp_after\":35,\"hp_before\":35,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":12,\"candidates\":[{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":20,\"heal_gained\":6,\"hp_after\":38,\"hp_before\":32,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blast_accounting\",\"feat_name\":\"爆破会计\",\"hp_after\":38,\"hp_before\":38,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":97,\"route\":\"min\",\"seal_hit\":316,\"seal_hit_gain\":941,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":335,\"seed\":71038,\"shop_gp_seq\":[30,60,95],\"stopped_at\":0,\"taken\":127,\"visited_count\":31,\"wallet_count\":37}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":19,\"choices\":28,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,35,35,35,35,35,35,41,41,41,41,41,41,41,47],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":141,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#71039\",\"1#71039\",\"1#71039\",\"1#71039\",\"1#71039\",\"1#71039\",\"1#71039\",\"2#71039\",\"2#71039\",\"2#71039\",\"2#71039\",\"2#71039\",\"2#71039\",\"2#71039\",\"2#71039\",\"2#71039\",\"2#71039\",\"3#71039\",\"3#71039\",\"3#71039\",\"3#71039\",\"3#71039\",\"3#71039\",\"3#71039\",\"3#71039\",\"3#71039\",\"3#71039\",\"3#71039\",\"3#71039\",\"3#71039\",\"3#71039\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"skip_mining\",\"last_drop\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":9,\"candidates\":[{\"feat_id\":\"skip_mining\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"map_exchange\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":16,\"heal_gained\":7,\"hp_after\":23,\"hp_before\":16,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"skip_mining\",\"feat_name\":\"跳格采掘\",\"hp_after\":23,\"hp_before\":23,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":15,\"candidates\":[{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":34,\"heal_gained\":8,\"hp_after\":36,\"hp_before\":28,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"last_drop\",\"feat_name\":\"一滴不剩\",\"hp_after\":36,\"hp_before\":36,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":91,\"route\":\"min\",\"seal_hit\":316,\"seal_hit_gain\":955,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":332,\"seed\":71039,\"shop_gp_seq\":[39,76,84],\"stopped_at\":0,\"taken\":141,\"visited_count\":31,\"wallet_count\":38}" ], "jsonl": { "normal": { - "sha256": "7c5fd8e5a3299bcb029d984f40a635f80ed99d6956ce1c02e679c9167394ae0a", - "rows": 8945 + "sha256": "19ee7ccdfb3780f5152b30f830c1168852c4fb4df73e2c27463a3a4695ef35ed", + "rows": 9216 }, "perfect": { - "sha256": "e20be1dc4da38679c1dc0649a308db9579c5b8aad2214ab6194d806650318f0f", - "rows": 39811 + "sha256": "a37ba2ca47f8619f22aa3dd8a761fffc71916d6c3172f5ff0aeb2b0557aba7e5", + "rows": 60774 } } }, "attune": { "runsum": [ - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":5,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":17,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":6,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"excellent\"},{\"kind\":\"ap\",\"tier\":\"rare\"},{\"kind\":\"armor\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"ap\",\"tier\":\"rare\",\"value\":1}}],\"rounds\":15,\"route\":\"min\",\"seal_hit\":48,\"seal_hit_gain\":600,\"seal_miss\":7,\"seal_miss_penalty\":24,\"seal_placed\":58,\"seed\":72000,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":33,\"visited_count\":7,\"wallet_count\":6}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":5,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":25,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\"],\"levels_cleared\":0,\"mine_dmg\":6,\"mine_hits\":9,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"time_sand\",\"tier\":\"rare\"},{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"heal\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"time_sand\",\"tier\":\"rare\",\"value\":2}}],\"rounds\":17,\"route\":\"min\",\"seal_hit\":57,\"seal_hit_gain\":416,\"seal_miss\":9,\"seal_miss_penalty\":16,\"seal_placed\":73,\"seed\":72001,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":31,\"visited_count\":7,\"wallet_count\":6}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":4,\"choices\":4,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":23,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":11,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":15,\"route\":\"min\",\"seal_hit\":37,\"seal_hit_gain\":-73,\"seal_miss\":10,\"seal_miss_penalty\":14,\"seal_placed\":52,\"seed\":72002,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":31,\"visited_count\":5,\"wallet_count\":5}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":5,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":19,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":5,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"time_sand\",\"tier\":\"rare\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"time_sand\",\"tier\":\"rare\",\"value\":2}}],\"rounds\":12,\"route\":\"min\",\"seal_hit\":47,\"seal_hit_gain\":292,\"seal_miss\":5,\"seal_miss_penalty\":8,\"seal_placed\":55,\"seed\":72003,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":35,\"visited_count\":7,\"wallet_count\":6}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":3,\"choices\":2,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":10,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":7,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":6,\"route\":\"min\",\"seal_hit\":19,\"seal_hit_gain\":-202,\"seal_miss\":5,\"seal_miss_penalty\":0,\"seal_placed\":27,\"seed\":72004,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":26,\"visited_count\":3,\"wallet_count\":4}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":4,\"choices\":3,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":17,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\"],\"levels_cleared\":0,\"mine_dmg\":0,\"mine_hits\":3,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":9,\"route\":\"min\",\"seal_hit\":34,\"seal_hit_gain\":-7,\"seal_miss\":3,\"seal_miss_penalty\":14,\"seal_placed\":39,\"seed\":72005,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":17,\"visited_count\":4,\"wallet_count\":5}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":7,\"choices\":7,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":40,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":1,\"mine_dmg\":8,\"mine_hits\":9,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[\"executioner\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"attack\",\"tier\":\"excellent\",\"value\":2}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":27,\"heal_gained\":3,\"hp_after\":26,\"hp_before\":23,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"executioner\",\"feat_name\":\"处刑台\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":31,\"route\":\"min\",\"seal_hit\":85,\"seal_hit_gain\":309,\"seal_miss\":11,\"seal_miss_penalty\":4,\"seal_placed\":104,\"seed\":72006,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":48,\"visited_count\":9,\"wallet_count\":8}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":7,\"choices\":7,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":27,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"宽廊双干(现行)\",\"宽廊双干(现行)\"],\"levels_cleared\":1,\"mine_dmg\":8,\"mine_hits\":9,\"policy\":\"normal\",\"refreshes\":1,\"relics\":[\"skip_mining\",\"seal_echo\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"feat_ticket\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"feat_archetype\":\"\",\"feat_id\":\"skip_mining\",\"feat_name\":\"跳格采掘\",\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_echo\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":29,\"heal_gained\":5,\"hp_after\":12,\"hp_before\":7,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"seal_echo\",\"feat_name\":\"封印回响\",\"hp_after\":12,\"hp_before\":12,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":22,\"route\":\"min\",\"seal_hit\":72,\"seal_hit_gain\":270,\"seal_miss\":10,\"seal_miss_penalty\":14,\"seal_placed\":86,\"seed\":72007,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":35,\"visited_count\":9,\"wallet_count\":8}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":3,\"choices\":2,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":8,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":2,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":5,\"route\":\"min\",\"seal_hit\":15,\"seal_hit_gain\":184,\"seal_miss\":3,\"seal_miss_penalty\":8,\"seal_placed\":23,\"seed\":72008,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":24,\"visited_count\":3,\"wallet_count\":4}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":2,\"choices\":1,\"cleared\":false,\"encounter_max_hp_seq\":[26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":5,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\"],\"levels_cleared\":0,\"mine_dmg\":14,\"mine_hits\":3,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":3,\"route\":\"min\",\"seal_hit\":11,\"seal_hit_gain\":-8,\"seal_miss\":2,\"seal_miss_penalty\":8,\"seal_placed\":15,\"seed\":72009,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":19,\"visited_count\":2,\"wallet_count\":3}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":5,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":16,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\"],\"levels_cleared\":0,\"mine_dmg\":22,\"mine_hits\":10,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"heal\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"heal\",\"tier\":\"excellent\",\"value\":6}}],\"rounds\":13,\"route\":\"min\",\"seal_hit\":39,\"seal_hit_gain\":494,\"seal_miss\":12,\"seal_miss_penalty\":6,\"seal_placed\":55,\"seed\":72010,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":38,\"visited_count\":7,\"wallet_count\":6}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":4,\"choices\":5,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":11,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\"],\"levels_cleared\":0,\"mine_dmg\":14,\"mine_hits\":5,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[\"trap_burst\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"feat_ticket\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"feat_archetype\":\"\",\"feat_id\":\"trap_burst\",\"feat_name\":\"陷阱爆发\",\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":7,\"route\":\"min\",\"seal_hit\":24,\"seal_hit_gain\":303,\"seal_miss\":4,\"seal_miss_penalty\":14,\"seal_placed\":31,\"seed\":72011,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":25,\"visited_count\":6,\"wallet_count\":5}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":3,\"choices\":2,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":14,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":5,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":7,\"route\":\"min\",\"seal_hit\":21,\"seal_hit_gain\":116,\"seal_miss\":5,\"seal_miss_penalty\":8,\"seal_placed\":29,\"seed\":72012,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":30,\"visited_count\":3,\"wallet_count\":4}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":5,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":22,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\"],\"levels_cleared\":0,\"mine_dmg\":14,\"mine_hits\":5,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"attack\",\"tier\":\"excellent\",\"value\":2}}],\"rounds\":18,\"route\":\"min\",\"seal_hit\":58,\"seal_hit_gain\":191,\"seal_miss\":5,\"seal_miss_penalty\":6,\"seal_placed\":74,\"seed\":72013,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":36,\"visited_count\":7,\"wallet_count\":6}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":6,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":25,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"三干中廊\"],\"levels_cleared\":1,\"mine_dmg\":16,\"mine_hits\":7,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[\"seal_smith\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"gp\",\"tier\":\"excellent\"},{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"gp\",\"tier\":\"excellent\",\"value\":50}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_smith\",\"kind\":\"relic\",\"tier\":\"rare\"}],\"gp\":78,\"heal_gained\":5,\"hp_after\":6,\"hp_before\":1,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"seal_smith\",\"feat_name\":\"封印匠\",\"hp_after\":6,\"hp_before\":6,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":18,\"route\":\"min\",\"seal_hit\":53,\"seal_hit_gain\":413,\"seal_miss\":6,\"seal_miss_penalty\":14,\"seal_placed\":64,\"seed\":72014,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":41,\"visited_count\":8,\"wallet_count\":8}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":7,\"choices\":7,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":41,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":1,\"mine_dmg\":14,\"mine_hits\":9,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[\"perfect_insurance\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"gp\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"gp\",\"tier\":\"excellent\",\"value\":50}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"perfect_insurance\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"skip_mining\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":90,\"heal_gained\":5,\"hp_after\":18,\"hp_before\":13,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"perfect_insurance\",\"feat_name\":\"完美保险\",\"hp_after\":18,\"hp_before\":18,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":30,\"route\":\"min\",\"seal_hit\":88,\"seal_hit_gain\":688,\"seal_miss\":10,\"seal_miss_penalty\":8,\"seal_placed\":102,\"seed\":72015,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":55,\"visited_count\":9,\"wallet_count\":9}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":5,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":14,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\"],\"levels_cleared\":0,\"mine_dmg\":14,\"mine_hits\":9,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"refresh\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"refresh\",\"tier\":\"excellent\",\"value\":1}}],\"rounds\":11,\"route\":\"min\",\"seal_hit\":43,\"seal_hit_gain\":149,\"seal_miss\":7,\"seal_miss_penalty\":24,\"seal_placed\":55,\"seed\":72016,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":28,\"visited_count\":7,\"wallet_count\":6}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":3,\"choices\":2,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":14,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":9,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":10,\"route\":\"min\",\"seal_hit\":26,\"seal_hit_gain\":-23,\"seal_miss\":8,\"seal_miss_penalty\":8,\"seal_placed\":35,\"seed\":72017,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":22,\"visited_count\":3,\"wallet_count\":4}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":5,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":22,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":5,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"time_sand\",\"tier\":\"rare\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"time_sand\",\"tier\":\"rare\",\"value\":2}}],\"rounds\":17,\"route\":\"min\",\"seal_hit\":50,\"seal_hit_gain\":-84,\"seal_miss\":5,\"seal_miss_penalty\":8,\"seal_placed\":62,\"seed\":72018,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":38,\"visited_count\":7,\"wallet_count\":6}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":4,\"choices\":3,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":5,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\"],\"levels_cleared\":0,\"mine_dmg\":0,\"mine_hits\":4,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":7,\"route\":\"min\",\"seal_hit\":21,\"seal_hit_gain\":257,\"seal_miss\":4,\"seal_miss_penalty\":22,\"seal_placed\":32,\"seed\":72019,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":5,\"visited_count\":4,\"wallet_count\":5}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":8,\"choices\":9,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":85,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"breaker_strike\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"armor\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"armor\",\"tier\":\"excellent\",\"value\":2}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"breaker_strike\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":51,\"heal_gained\":5,\"hp_after\":25,\"hp_before\":20,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"breaker_strike\",\"feat_name\":\"破封一击\",\"hp_after\":25,\"hp_before\":25,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":37,\"route\":\"min\",\"seal_hit\":132,\"seal_hit_gain\":975,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":143,\"seed\":72020,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":85,\"visited_count\":11,\"wallet_count\":9}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":8,\"choices\":8,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":60,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"三干中廊\",\"三干中廊\",\"三干中廊\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"twin_pick\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"excellent\"},{\"kind\":\"max_hp\",\"tier\":\"excellent\"},{\"kind\":\"ap\",\"tier\":\"rare\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"ap\",\"tier\":\"rare\",\"value\":1}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":27,\"heal_gained\":2,\"hp_after\":26,\"hp_before\":24,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"twin_pick\",\"feat_name\":\"双管镐\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":30,\"route\":\"min\",\"seal_hit\":140,\"seal_hit_gain\":645,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":154,\"seed\":72021,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":60,\"visited_count\":10,\"wallet_count\":9}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":10,\"choices\":10,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,36,36,36,36,36,36,36],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":82,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"fuse_wire\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"excellent\"},{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"armor\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"max_hp\",\"tier\":\"excellent\",\"value\":10}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"fuse_wire\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"skip_mining\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"seal_burst\",\"kind\":\"relic\",\"tier\":\"rare\"}],\"gp\":39,\"heal_gained\":7,\"hp_after\":31,\"hp_before\":24,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"fuse_wire\",\"feat_name\":\"保险丝\",\"hp_after\":31,\"hp_before\":31,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":45,\"route\":\"min\",\"seal_hit\":153,\"seal_hit_gain\":795,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":174,\"seed\":72022,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":82,\"visited_count\":12,\"wallet_count\":11}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":15,\"choices\":16,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26,38,38,38,38,38,44],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":87,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"perfect_insurance\",\"trap_instinct\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"heal\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"heal\",\"tier\":\"excellent\",\"value\":6}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"perfect_insurance\",\"kind\":\"relic\",\"tier\":\"rare\"}],\"gp\":35,\"heal_gained\":4,\"hp_after\":26,\"hp_before\":22,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"perfect_insurance\",\"feat_name\":\"完美保险\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"trap_instinct\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"chain_traps\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":19,\"heal_gained\":7,\"hp_after\":32,\"hp_before\":25,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"trap_instinct\",\"feat_name\":\"陷阱本能\",\"hp_after\":32,\"hp_before\":32,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":75,\"route\":\"min\",\"seal_hit\":269,\"seal_hit_gain\":1246,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":292,\"seed\":72023,\"shop_gp_seq\":[95,64],\"stopped_at\":0,\"taken\":87,\"visited_count\":19,\"wallet_count\":31}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":13,\"choices\":15,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,35,35,35,35,35],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":87,\"hp_pct\":0,\"keystone_total\":6,\"level_templates\":[\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"executioner\",\"perfect_insurance\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"time_sand\",\"tier\":\"rare\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"time_sand\",\"tier\":\"rare\",\"value\":2}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"stone_skin\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":28,\"heal_gained\":5,\"hp_after\":24,\"hp_before\":19,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"executioner\",\"feat_name\":\"处刑台\",\"hp_after\":24,\"hp_before\":24,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"perfect_insurance\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"venom_edge\",\"kind\":\"relic\",\"tier\":\"rare\"}],\"gp\":19,\"heal_gained\":7,\"hp_after\":20,\"hp_before\":13,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"perfect_insurance\",\"feat_name\":\"完美保险\",\"hp_after\":20,\"hp_before\":20,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":58,\"route\":\"min\",\"seal_hit\":218,\"seal_hit_gain\":1956,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":242,\"seed\":72024,\"shop_gp_seq\":[52],\"stopped_at\":3,\"taken\":87,\"visited_count\":18,\"wallet_count\":21}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":8,\"choices\":9,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":79,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"executioner\",\"map_exchange\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"feat_ticket\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"feat_archetype\":\"\",\"feat_id\":\"executioner\",\"feat_name\":\"处刑台\",\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"map_exchange\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":36,\"heal_gained\":5,\"hp_after\":26,\"hp_before\":21,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"map_exchange\",\"feat_name\":\"弃图兑换\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":35,\"route\":\"min\",\"seal_hit\":129,\"seal_hit_gain\":131,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":145,\"seed\":72025,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":79,\"visited_count\":11,\"wallet_count\":9}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":16,\"choices\":17,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26,35,35,35,35,35,35,41],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":60,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"blood_tax\",\"last_drop\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"attack\",\"tier\":\"excellent\",\"value\":2}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_tax\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":35,\"heal_gained\":5,\"hp_after\":24,\"hp_before\":19,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blood_tax\",\"feat_name\":\"血税\",\"hp_after\":24,\"hp_before\":24,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":11,\"heal_gained\":2,\"hp_after\":35,\"hp_before\":33,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"last_drop\",\"feat_name\":\"一滴不剩\",\"hp_after\":35,\"hp_before\":35,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":46,\"route\":\"min\",\"seal_hit\":181,\"seal_hit_gain\":1268,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":210,\"seed\":72026,\"shop_gp_seq\":[58,59],\"stopped_at\":0,\"taken\":60,\"visited_count\":20,\"wallet_count\":29}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":8,\"choices\":9,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":62,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"venom_edge\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"heal\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"heal\",\"tier\":\"excellent\",\"value\":6}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"venom_edge\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":48,\"heal_gained\":1,\"hp_after\":26,\"hp_before\":25,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"venom_edge\",\"feat_name\":\"淬毒刃纹\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":31,\"route\":\"min\",\"seal_hit\":114,\"seal_hit_gain\":503,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":125,\"seed\":72027,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":62,\"visited_count\":11,\"wallet_count\":9}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":8,\"choices\":9,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":72,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"executioner\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"max_hp\",\"tier\":\"excellent\"},{\"kind\":\"rap\",\"tier\":\"rare\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"rap\",\"tier\":\"rare\",\"value\":3}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":38,\"heal_gained\":0,\"hp_after\":26,\"hp_before\":26,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"executioner\",\"feat_name\":\"处刑台\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":34,\"route\":\"min\",\"seal_hit\":128,\"seal_hit_gain\":1196,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":141,\"seed\":72028,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":72,\"visited_count\":11,\"wallet_count\":9}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":8,\"choices\":8,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":70,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"chain_traps\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"refresh\",\"tier\":\"excellent\"},{\"kind\":\"feat_ticket\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"refresh\",\"tier\":\"excellent\",\"value\":1}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"chain_traps\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":26,\"heal_gained\":1,\"hp_after\":26,\"hp_before\":25,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"chain_traps\",\"feat_name\":\"连环陷阱\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":33,\"route\":\"min\",\"seal_hit\":116,\"seal_hit_gain\":-149,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":124,\"seed\":72029,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":70,\"visited_count\":10,\"wallet_count\":9}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":8,\"choices\":9,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":53,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"hard_hat\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"refresh\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"refresh\",\"tier\":\"excellent\",\"value\":1}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"hard_hat\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":42,\"heal_gained\":5,\"hp_after\":26,\"hp_before\":21,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"hard_hat\",\"feat_name\":\"安全帽\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":34,\"route\":\"min\",\"seal_hit\":122,\"seal_hit_gain\":184,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":136,\"seed\":72030,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":53,\"visited_count\":11,\"wallet_count\":9}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":8,\"choices\":9,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":82,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":3,\"relics\":[\"trap_burst\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"rap\",\"tier\":\"rare\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"rap\",\"tier\":\"rare\",\"value\":3}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"trap_burst\",\"kind\":\"relic\",\"tier\":\"rare\"}],\"gp\":33,\"heal_gained\":0,\"hp_after\":26,\"hp_before\":26,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"trap_burst\",\"feat_name\":\"陷阱爆发\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":38,\"route\":\"min\",\"seal_hit\":128,\"seal_hit_gain\":554,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":138,\"seed\":72031,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":82,\"visited_count\":11,\"wallet_count\":9}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":14,\"choices\":16,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26,41,41,41,41,41],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":99,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"seal_stake\",\"execution_scholar\",\"trap_instinct\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"feat_ticket\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"feat_archetype\":\"\",\"feat_id\":\"seal_stake\",\"feat_name\":\"封印桩\",\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":64,\"heal_gained\":5,\"hp_after\":23,\"hp_before\":18,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"execution_scholar\",\"feat_name\":\"斩首学者\",\"hp_after\":23,\"hp_before\":23,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"trap_instinct\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"chain_traps\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":9,\"heal_gained\":8,\"hp_after\":34,\"hp_before\":26,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"trap_instinct\",\"feat_name\":\"陷阱本能\",\"hp_after\":34,\"hp_before\":34,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":68,\"route\":\"min\",\"seal_hit\":256,\"seal_hit_gain\":1884,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":271,\"seed\":72032,\"shop_gp_seq\":[102,42],\"stopped_at\":0,\"taken\":99,\"visited_count\":19,\"wallet_count\":29}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":8,\"choices\":8,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":72,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"trap_burst\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"heal\",\"tier\":\"excellent\"},{\"kind\":\"armor\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"heal\",\"tier\":\"excellent\",\"value\":6}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"trap_burst\",\"kind\":\"relic\",\"tier\":\"rare\"}],\"gp\":42,\"heal_gained\":4,\"hp_after\":26,\"hp_before\":22,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"trap_burst\",\"feat_name\":\"陷阱爆发\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":36,\"route\":\"min\",\"seal_hit\":127,\"seal_hit_gain\":456,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":138,\"seed\":72033,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":72,\"visited_count\":10,\"wallet_count\":9}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":8,\"choices\":8,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":67,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"breaker_strike\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"armor\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"armor\",\"tier\":\"excellent\",\"value\":2}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"breaker_strike\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":49,\"heal_gained\":5,\"hp_after\":24,\"hp_before\":19,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"breaker_strike\",\"feat_name\":\"破封一击\",\"hp_after\":24,\"hp_before\":24,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":34,\"route\":\"min\",\"seal_hit\":120,\"seal_hit_gain\":84,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":132,\"seed\":72034,\"shop_gp_seq\":[],\"stopped_at\":2,\"taken\":67,\"visited_count\":10,\"wallet_count\":9}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":13,\"choices\":16,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,35,35,35,35,38],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":98,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"last_stand\",\"twin_pick\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"heal\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"heal\",\"tier\":\"excellent\",\"value\":6}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"last_stand\",\"kind\":\"relic\",\"tier\":\"rare\"}],\"gp\":23,\"heal_gained\":1,\"hp_after\":26,\"hp_before\":25,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"last_stand\",\"feat_name\":\"背水\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":29,\"heal_gained\":7,\"hp_after\":25,\"hp_before\":18,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"twin_pick\",\"feat_name\":\"双管镐\",\"hp_after\":25,\"hp_before\":25,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":67,\"route\":\"min\",\"seal_hit\":270,\"seal_hit_gain\":206,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":294,\"seed\":72035,\"shop_gp_seq\":[60,51],\"stopped_at\":0,\"taken\":98,\"visited_count\":19,\"wallet_count\":25}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":13,\"choices\":16,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,35,35,35,35,38],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":90,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"seal_smith\",\"blood_honey\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"armor\",\"tier\":\"excellent\"},{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"rap\",\"tier\":\"rare\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"rap\",\"tier\":\"rare\",\"value\":3}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"seal_smith\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_echo\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":47,\"heal_gained\":4,\"hp_after\":26,\"hp_before\":22,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"seal_smith\",\"feat_name\":\"封印匠\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":17,\"heal_gained\":2,\"hp_after\":35,\"hp_before\":33,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blood_honey\",\"feat_name\":\"血蜜\",\"hp_after\":35,\"hp_before\":35,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":65,\"route\":\"min\",\"seal_hit\":239,\"seal_hit_gain\":474,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":260,\"seed\":72036,\"shop_gp_seq\":[63,42],\"stopped_at\":0,\"taken\":90,\"visited_count\":19,\"wallet_count\":25}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":14,\"choices\":16,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,35,35,35,35,35,41],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":92,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"warning_beacon\",\"pain_memory\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"ap\",\"tier\":\"rare\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"ap\",\"tier\":\"rare\",\"value\":1}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":43,\"heal_gained\":5,\"hp_after\":23,\"hp_before\":18,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"warning_beacon\",\"feat_name\":\"示警烟火\",\"hp_after\":23,\"hp_before\":23,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"breaker_strike\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"pain_memory\",\"kind\":\"relic\",\"tier\":\"rare\"}],\"gp\":20,\"heal_gained\":5,\"hp_after\":35,\"hp_before\":30,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"pain_memory\",\"feat_name\":\"疼痛记忆\",\"hp_after\":35,\"hp_before\":35,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":66,\"route\":\"min\",\"seal_hit\":249,\"seal_hit_gain\":205,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":269,\"seed\":72037,\"shop_gp_seq\":[62,66],\"stopped_at\":0,\"taken\":92,\"visited_count\":19,\"wallet_count\":28}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":16,\"choices\":17,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26,38,38,38,38,38,38,44],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":116,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"stone_skin\",\"executioner\",\"perfect_insurance\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"feat_ticket\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"feat_archetype\":\"\",\"feat_id\":\"stone_skin\",\"feat_name\":\"石肤\",\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":39,\"heal_gained\":2,\"hp_after\":26,\"hp_before\":24,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"executioner\",\"feat_name\":\"处刑台\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"perfect_insurance\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":29,\"heal_gained\":4,\"hp_after\":38,\"hp_before\":34,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"perfect_insurance\",\"feat_name\":\"完美保险\",\"hp_after\":38,\"hp_before\":38,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":78,\"route\":\"min\",\"seal_hit\":282,\"seal_hit_gain\":1163,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":301,\"seed\":72038,\"shop_gp_seq\":[93,68],\"stopped_at\":0,\"taken\":116,\"visited_count\":20,\"wallet_count\":32}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":15,\"choices\":17,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,35,35,35,35,35,35,41],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":111,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"pain_memory\",\"last_drop\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"excellent\"},{\"kind\":\"max_hp\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"attack\",\"tier\":\"excellent\",\"value\":2}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"pain_memory\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"trap_instinct\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"breaker_strike\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":40,\"heal_gained\":4,\"hp_after\":26,\"hp_before\":22,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"pain_memory\",\"feat_name\":\"疼痛记忆\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":23,\"heal_gained\":1,\"hp_after\":35,\"hp_before\":34,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"last_drop\",\"feat_name\":\"一滴不剩\",\"hp_after\":35,\"hp_before\":35,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":63,\"route\":\"min\",\"seal_hit\":246,\"seal_hit_gain\":1051,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":266,\"seed\":72039,\"shop_gp_seq\":[65,75],\"stopped_at\":0,\"taken\":111,\"visited_count\":20,\"wallet_count\":29}" + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":8,\"choices\":7,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,35,35,35],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":38,\"hp_pct\":0,\"keystone_total\":3,\"level_maps\":[\"1#72000\",\"1#72000\",\"1#72000\",\"1#72000\",\"1#72000\",\"1#72000\",\"1#72000\",\"2#72000\",\"2#72000\"],\"levels_cleared\":1,\"mine_dmg\":22,\"mine_hits\":11,\"policy\":\"normal\",\"refreshes\":1,\"relics\":[\"seal_stake\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":9,\"candidates\":[{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":18,\"heal_gained\":7,\"hp_after\":14,\"hp_before\":7,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"seal_stake\",\"feat_name\":\"封印桩\",\"hp_after\":14,\"hp_before\":14,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":32,\"route\":\"min\",\"seal_hit\":91,\"seal_hit_gain\":738,\"seal_miss\":9,\"seal_miss_penalty\":-2,\"seal_placed\":108,\"seed\":72000,\"shop_gp_seq\":[29],\"stopped_at\":2,\"taken\":60,\"visited_count\":9,\"wallet_count\":15}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":5,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,32,32],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":39,\"hp_pct\":0,\"keystone_total\":3,\"level_maps\":[\"1#72001\",\"1#72001\",\"1#72001\",\"1#72001\",\"1#72001\",\"1#72001\",\"1#72001\",\"2#72001\"],\"levels_cleared\":1,\"mine_dmg\":14,\"mine_hits\":10,\"policy\":\"normal\",\"refreshes\":1,\"relics\":[\"skip_mining\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":6,\"candidates\":[{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"skip_mining\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"stone_skin\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":27,\"heal_gained\":6,\"hp_after\":9,\"hp_before\":3,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"skip_mining\",\"feat_name\":\"跳格采掘\",\"hp_after\":9,\"hp_before\":9,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":23,\"route\":\"min\",\"seal_hit\":63,\"seal_hit_gain\":556,\"seal_miss\":9,\"seal_miss_penalty\":20,\"seal_placed\":78,\"seed\":72001,\"shop_gp_seq\":[17],\"stopped_at\":2,\"taken\":53,\"visited_count\":8,\"wallet_count\":10}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":3,\"choices\":3,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":9,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#72002\",\"1#72002\",\"1#72002\",\"1#72002\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":4,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":5,\"route\":\"min\",\"seal_hit\":11,\"seal_hit_gain\":-127,\"seal_miss\":5,\"seal_miss_penalty\":8,\"seal_placed\":23,\"seed\":72002,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":25,\"visited_count\":4,\"wallet_count\":4}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":5,\"choices\":4,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":28,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#72003\",\"1#72003\",\"1#72003\",\"1#72003\",\"1#72003\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":7,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":16,\"route\":\"min\",\"seal_hit\":60,\"seal_hit_gain\":234,\"seal_miss\":6,\"seal_miss_penalty\":8,\"seal_placed\":70,\"seed\":72003,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":36,\"visited_count\":5,\"wallet_count\":6}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":4,\"choices\":3,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":13,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#72004\",\"1#72004\",\"1#72004\",\"1#72004\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":9,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":8,\"route\":\"min\",\"seal_hit\":21,\"seal_hit_gain\":323,\"seal_miss\":5,\"seal_miss_penalty\":6,\"seal_placed\":28,\"seed\":72004,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":29,\"visited_count\":4,\"wallet_count\":5}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":4,\"choices\":3,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":21,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#72005\",\"1#72005\",\"1#72005\",\"1#72005\"],\"levels_cleared\":0,\"mine_dmg\":6,\"mine_hits\":7,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":12,\"route\":\"min\",\"seal_hit\":35,\"seal_hit_gain\":321,\"seal_miss\":3,\"seal_miss_penalty\":4,\"seal_placed\":42,\"seed\":72005,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":27,\"visited_count\":4,\"wallet_count\":5}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":5,\"choices\":4,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":17,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#72006\",\"1#72006\",\"1#72006\",\"1#72006\",\"1#72006\"],\"levels_cleared\":0,\"mine_dmg\":6,\"mine_hits\":8,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":16,\"route\":\"min\",\"seal_hit\":48,\"seal_hit_gain\":316,\"seal_miss\":7,\"seal_miss_penalty\":14,\"seal_placed\":68,\"seed\":72006,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":23,\"visited_count\":5,\"wallet_count\":6}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":3,\"choices\":2,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":10,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#72007\",\"1#72007\",\"1#72007\"],\"levels_cleared\":0,\"mine_dmg\":0,\"mine_hits\":2,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":7,\"route\":\"min\",\"seal_hit\":26,\"seal_hit_gain\":363,\"seal_miss\":3,\"seal_miss_penalty\":14,\"seal_placed\":34,\"seed\":72007,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":10,\"visited_count\":3,\"wallet_count\":4}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":3,\"choices\":2,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":8,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#72008\",\"1#72008\",\"1#72008\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":3,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":8,\"route\":\"min\",\"seal_hit\":27,\"seal_hit_gain\":-20,\"seal_miss\":3,\"seal_miss_penalty\":14,\"seal_placed\":34,\"seed\":72008,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":16,\"visited_count\":3,\"wallet_count\":4}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":5,\"choices\":4,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":35,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#72009\",\"1#72009\",\"1#72009\",\"1#72009\",\"1#72009\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":11,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":16,\"route\":\"min\",\"seal_hit\":51,\"seal_hit_gain\":281,\"seal_miss\":8,\"seal_miss_penalty\":0,\"seal_placed\":63,\"seed\":72009,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":43,\"visited_count\":5,\"wallet_count\":6}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":4,\"choices\":3,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":11,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#72010\",\"1#72010\",\"1#72010\",\"1#72010\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":4,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":8,\"route\":\"min\",\"seal_hit\":21,\"seal_hit_gain\":-53,\"seal_miss\":4,\"seal_miss_penalty\":8,\"seal_placed\":30,\"seed\":72010,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":19,\"visited_count\":4,\"wallet_count\":5}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":7,\"choices\":7,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,32,32,32],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":44,\"hp_pct\":0,\"keystone_total\":3,\"level_maps\":[\"1#72011\",\"1#72011\",\"1#72011\",\"1#72011\",\"1#72011\",\"1#72011\",\"1#72011\",\"2#72011\",\"2#72011\"],\"levels_cleared\":1,\"mine_dmg\":30,\"mine_hits\":16,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[\"map_exchange\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":6,\"candidates\":[{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"map_exchange\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":21,\"heal_gained\":6,\"hp_after\":17,\"hp_before\":11,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"map_exchange\",\"feat_name\":\"弃图兑换\",\"hp_after\":17,\"hp_before\":17,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":34,\"route\":\"min\",\"seal_hit\":80,\"seal_hit_gain\":200,\"seal_miss\":15,\"seal_miss_penalty\":24,\"seal_placed\":101,\"seed\":72011,\"shop_gp_seq\":[16],\"stopped_at\":2,\"taken\":74,\"visited_count\":9,\"wallet_count\":12}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":3,\"choices\":2,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":5,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#72012\",\"1#72012\",\"1#72012\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":3,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":6,\"route\":\"min\",\"seal_hit\":18,\"seal_hit_gain\":107,\"seal_miss\":5,\"seal_miss_penalty\":14,\"seal_placed\":25,\"seed\":72012,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":13,\"visited_count\":3,\"wallet_count\":4}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":4,\"choices\":4,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":14,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#72013\",\"1#72013\",\"1#72013\",\"1#72013\",\"1#72013\"],\"levels_cleared\":0,\"mine_dmg\":14,\"mine_hits\":10,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":11,\"route\":\"min\",\"seal_hit\":38,\"seal_hit_gain\":289,\"seal_miss\":14,\"seal_miss_penalty\":16,\"seal_placed\":54,\"seed\":72013,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":28,\"visited_count\":5,\"wallet_count\":5}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":7,\"choices\":7,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,32,32,32],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":34,\"hp_pct\":0,\"keystone_total\":3,\"level_maps\":[\"1#72014\",\"1#72014\",\"1#72014\",\"1#72014\",\"1#72014\",\"1#72014\",\"1#72014\",\"2#72014\",\"2#72014\"],\"levels_cleared\":1,\"mine_dmg\":16,\"mine_hits\":7,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[\"seal_echo\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":6,\"candidates\":[{\"feat_id\":\"seal_echo\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"trap_instinct\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":18,\"heal_gained\":6,\"hp_after\":22,\"hp_before\":16,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"seal_echo\",\"feat_name\":\"封印回响\",\"hp_after\":22,\"hp_before\":22,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":32,\"route\":\"min\",\"seal_hit\":85,\"seal_hit_gain\":373,\"seal_miss\":11,\"seal_miss_penalty\":12,\"seal_placed\":104,\"seed\":72014,\"shop_gp_seq\":[19],\"stopped_at\":2,\"taken\":50,\"visited_count\":9,\"wallet_count\":12}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":3,\"choices\":2,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":7,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#72015\",\"1#72015\",\"1#72015\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":4,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":5,\"route\":\"min\",\"seal_hit\":22,\"seal_hit_gain\":243,\"seal_miss\":3,\"seal_miss_penalty\":8,\"seal_placed\":26,\"seed\":72015,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":23,\"visited_count\":3,\"wallet_count\":4}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":6,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,32,32],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":28,\"hp_pct\":0,\"keystone_total\":3,\"level_maps\":[\"1#72016\",\"1#72016\",\"1#72016\",\"1#72016\",\"1#72016\",\"1#72016\",\"1#72016\",\"2#72016\"],\"levels_cleared\":1,\"mine_dmg\":16,\"mine_hits\":11,\"policy\":\"normal\",\"refreshes\":1,\"relics\":[\"perfect_insurance\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":6,\"candidates\":[{\"feat_id\":\"perfect_insurance\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":29,\"heal_gained\":6,\"hp_after\":11,\"hp_before\":5,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"perfect_insurance\",\"feat_name\":\"完美保险\",\"hp_after\":11,\"hp_before\":11,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":25,\"route\":\"min\",\"seal_hit\":78,\"seal_hit_gain\":740,\"seal_miss\":10,\"seal_miss_penalty\":24,\"seal_placed\":92,\"seed\":72016,\"shop_gp_seq\":[15],\"stopped_at\":2,\"taken\":44,\"visited_count\":8,\"wallet_count\":11}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":4,\"choices\":3,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":18,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#72017\",\"1#72017\",\"1#72017\",\"1#72017\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":9,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":9,\"route\":\"min\",\"seal_hit\":30,\"seal_hit_gain\":-413,\"seal_miss\":9,\"seal_miss_penalty\":8,\"seal_placed\":40,\"seed\":72017,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":26,\"visited_count\":4,\"wallet_count\":5}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":1,\"choices\":0,\"cleared\":false,\"encounter_max_hp_seq\":[26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":2,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#72018\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":2,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":1,\"route\":\"min\",\"seal_hit\":2,\"seal_hit_gain\":64,\"seal_miss\":3,\"seal_miss_penalty\":8,\"seal_placed\":7,\"seed\":72018,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":18,\"visited_count\":1,\"wallet_count\":2}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":3,\"choices\":3,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":9,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#72019\",\"1#72019\",\"1#72019\",\"1#72019\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":4,\"policy\":\"normal\",\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":7,\"route\":\"min\",\"seal_hit\":24,\"seal_hit_gain\":319,\"seal_miss\":5,\"seal_miss_penalty\":22,\"seal_placed\":33,\"seed\":72019,\"shop_gp_seq\":[],\"stopped_at\":1,\"taken\":17,\"visited_count\":4,\"wallet_count\":4}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":17,\"choices\":20,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,32,32,32,32,32,32,38,38,38,38,38,38],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":128,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#72020\",\"1#72020\",\"1#72020\",\"1#72020\",\"1#72020\",\"1#72020\",\"1#72020\",\"2#72020\",\"2#72020\",\"2#72020\",\"2#72020\",\"2#72020\",\"2#72020\",\"2#72020\",\"2#72020\",\"2#72020\",\"2#72020\",\"3#72020\",\"3#72020\",\"3#72020\",\"3#72020\",\"3#72020\",\"3#72020\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"geologist\",\"skip_mining\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":6,\"candidates\":[{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":19,\"heal_gained\":6,\"hp_after\":32,\"hp_before\":26,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"geologist\",\"feat_name\":\"地质学家\",\"hp_after\":32,\"hp_before\":32,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":12,\"candidates\":[{\"feat_id\":\"skip_mining\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":19,\"heal_gained\":1,\"hp_after\":38,\"hp_before\":37,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"skip_mining\",\"feat_name\":\"跳格采掘\",\"hp_after\":38,\"hp_before\":38,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":79,\"route\":\"min\",\"seal_hit\":267,\"seal_hit_gain\":884,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":285,\"seed\":72020,\"shop_gp_seq\":[14,63],\"stopped_at\":3,\"taken\":128,\"visited_count\":23,\"wallet_count\":28}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":19,\"choices\":28,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,32,32,32,32,32,32,41,41,41,41,41,41,41,44],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":160,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#72021\",\"1#72021\",\"1#72021\",\"1#72021\",\"1#72021\",\"1#72021\",\"1#72021\",\"2#72021\",\"2#72021\",\"2#72021\",\"2#72021\",\"2#72021\",\"2#72021\",\"2#72021\",\"2#72021\",\"2#72021\",\"2#72021\",\"3#72021\",\"3#72021\",\"3#72021\",\"3#72021\",\"3#72021\",\"3#72021\",\"3#72021\",\"3#72021\",\"3#72021\",\"3#72021\",\"3#72021\",\"3#72021\",\"3#72021\",\"3#72021\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"breaker_strike\",\"map_exchange\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":6,\"candidates\":[{\"feat_id\":\"breaker_strike\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"hard_hat\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":33,\"heal_gained\":6,\"hp_after\":27,\"hp_before\":21,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"breaker_strike\",\"feat_name\":\"破封一击\",\"hp_after\":27,\"hp_before\":27,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":15,\"candidates\":[{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"map_exchange\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":21,\"heal_gained\":4,\"hp_after\":41,\"hp_before\":37,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"map_exchange\",\"feat_name\":\"弃图兑换\",\"hp_after\":41,\"hp_before\":41,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":97,\"route\":\"min\",\"seal_hit\":352,\"seal_hit_gain\":1330,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":373,\"seed\":72021,\"shop_gp_seq\":[22,77,90],\"stopped_at\":0,\"taken\":160,\"visited_count\":31,\"wallet_count\":37}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":17,\"choices\":19,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,32,32,32,32,32,32,38,38,38,38,38,38],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":124,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#72022\",\"1#72022\",\"1#72022\",\"1#72022\",\"1#72022\",\"1#72022\",\"1#72022\",\"2#72022\",\"2#72022\",\"2#72022\",\"2#72022\",\"2#72022\",\"2#72022\",\"2#72022\",\"2#72022\",\"2#72022\",\"2#72022\",\"3#72022\",\"3#72022\",\"3#72022\",\"3#72022\",\"3#72022\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":2,\"relics\":[\"hard_hat\",\"frontier_miner\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":6,\"candidates\":[{\"feat_id\":\"hard_hat\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":26,\"heal_gained\":1,\"hp_after\":32,\"hp_before\":31,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"hard_hat\",\"feat_name\":\"安全帽\",\"hp_after\":32,\"hp_before\":32,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":12,\"candidates\":[{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":29,\"heal_gained\":7,\"hp_after\":37,\"hp_before\":30,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"frontier_miner\",\"feat_name\":\"边陲矿工\",\"hp_after\":37,\"hp_before\":37,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":81,\"route\":\"min\",\"seal_hit\":300,\"seal_hit_gain\":1123,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":329,\"seed\":72022,\"shop_gp_seq\":[17,73],\"stopped_at\":3,\"taken\":124,\"visited_count\":22,\"wallet_count\":29}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":17,\"choices\":19,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,35,35,35,35,35,35,38,38,38,38,38,38],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":115,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#72023\",\"1#72023\",\"1#72023\",\"1#72023\",\"1#72023\",\"1#72023\",\"1#72023\",\"2#72023\",\"2#72023\",\"2#72023\",\"2#72023\",\"2#72023\",\"2#72023\",\"2#72023\",\"2#72023\",\"2#72023\",\"2#72023\",\"3#72023\",\"3#72023\",\"3#72023\",\"3#72023\",\"3#72023\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"blood_honey\",\"executioner\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":9,\"candidates\":[{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":11,\"heal_gained\":5,\"hp_after\":35,\"hp_before\":30,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blood_honey\",\"feat_name\":\"血蜜\",\"hp_after\":35,\"hp_before\":35,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":12,\"candidates\":[{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":31,\"heal_gained\":4,\"hp_after\":38,\"hp_before\":34,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"executioner\",\"feat_name\":\"处刑台\",\"hp_after\":38,\"hp_before\":38,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":75,\"route\":\"min\",\"seal_hit\":286,\"seal_hit_gain\":643,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":304,\"seed\":72023,\"shop_gp_seq\":[34,48],\"stopped_at\":3,\"taken\":115,\"visited_count\":22,\"wallet_count\":29}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":20,\"choices\":28,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,35,35,35,35,35,35,38,38,38,38,38,38,38,38,41],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":75,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#72024\",\"1#72024\",\"1#72024\",\"1#72024\",\"1#72024\",\"1#72024\",\"1#72024\",\"2#72024\",\"2#72024\",\"2#72024\",\"2#72024\",\"2#72024\",\"2#72024\",\"2#72024\",\"2#72024\",\"2#72024\",\"2#72024\",\"3#72024\",\"3#72024\",\"3#72024\",\"3#72024\",\"3#72024\",\"3#72024\",\"3#72024\",\"3#72024\",\"3#72024\",\"3#72024\",\"3#72024\",\"3#72024\",\"3#72024\",\"3#72024\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"seal_burst\",\"perfect_insurance\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":9,\"candidates\":[{\"feat_id\":\"seal_burst\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":11,\"heal_gained\":7,\"hp_after\":32,\"hp_before\":25,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"seal_burst\",\"feat_name\":\"封印爆裂\",\"hp_after\":32,\"hp_before\":32,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":12,\"candidates\":[{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"perfect_insurance\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":20,\"heal_gained\":2,\"hp_after\":38,\"hp_before\":36,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"perfect_insurance\",\"feat_name\":\"完美保险\",\"hp_after\":38,\"hp_before\":38,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":55,\"route\":\"min\",\"seal_hit\":229,\"seal_hit_gain\":-27,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":256,\"seed\":72024,\"shop_gp_seq\":[34,42,59],\"stopped_at\":0,\"taken\":75,\"visited_count\":31,\"wallet_count\":35}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":23,\"choices\":26,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,32,32,32,32,32,32,38,38,38,38,38,38,38,38,38,38,38,47],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":150,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#72025\",\"1#72025\",\"1#72025\",\"1#72025\",\"1#72025\",\"1#72025\",\"1#72025\",\"2#72025\",\"2#72025\",\"2#72025\",\"2#72025\",\"2#72025\",\"2#72025\",\"2#72025\",\"2#72025\",\"2#72025\",\"2#72025\",\"3#72025\",\"3#72025\",\"3#72025\",\"3#72025\",\"3#72025\",\"3#72025\",\"3#72025\",\"3#72025\",\"3#72025\",\"3#72025\",\"3#72025\",\"3#72025\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"field_dressing\",\"hard_hat\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":6,\"candidates\":[{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":15,\"heal_gained\":6,\"hp_after\":29,\"hp_before\":23,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"field_dressing\",\"feat_name\":\"战地包扎\",\"hp_after\":29,\"hp_before\":29,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":12,\"candidates\":[{\"feat_id\":\"hard_hat\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":27,\"heal_gained\":0,\"hp_after\":38,\"hp_before\":38,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"hard_hat\",\"feat_name\":\"安全帽\",\"hp_after\":38,\"hp_before\":38,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":103,\"route\":\"min\",\"seal_hit\":408,\"seal_hit_gain\":2142,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":435,\"seed\":72025,\"shop_gp_seq\":[22,64,116],\"stopped_at\":0,\"taken\":150,\"visited_count\":29,\"wallet_count\":42}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":15,\"choices\":17,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,35,35,35,35,35,35,38,38,38,38],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":121,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#72026\",\"1#72026\",\"1#72026\",\"1#72026\",\"1#72026\",\"1#72026\",\"1#72026\",\"2#72026\",\"2#72026\",\"2#72026\",\"2#72026\",\"2#72026\",\"2#72026\",\"2#72026\",\"2#72026\",\"2#72026\",\"2#72026\",\"3#72026\",\"3#72026\",\"3#72026\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"frontier_miner\",\"perfect_insurance\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":9,\"candidates\":[{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":15,\"heal_gained\":7,\"hp_after\":23,\"hp_before\":16,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"frontier_miner\",\"feat_name\":\"边陲矿工\",\"hp_after\":23,\"hp_before\":23,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":12,\"candidates\":[{\"feat_id\":\"perfect_insurance\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"trap_instinct\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":26,\"heal_gained\":3,\"hp_after\":38,\"hp_before\":35,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"perfect_insurance\",\"feat_name\":\"完美保险\",\"hp_after\":38,\"hp_before\":38,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":64,\"route\":\"min\",\"seal_hit\":227,\"seal_hit_gain\":370,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":245,\"seed\":72026,\"shop_gp_seq\":[35,51],\"stopped_at\":3,\"taken\":121,\"visited_count\":20,\"wallet_count\":27}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":18,\"choices\":21,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,32,32,32,32,32,32,38,38,38,38,38,38,38],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":125,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#72027\",\"1#72027\",\"1#72027\",\"1#72027\",\"1#72027\",\"1#72027\",\"1#72027\",\"2#72027\",\"2#72027\",\"2#72027\",\"2#72027\",\"2#72027\",\"2#72027\",\"2#72027\",\"2#72027\",\"2#72027\",\"2#72027\",\"3#72027\",\"3#72027\",\"3#72027\",\"3#72027\",\"3#72027\",\"3#72027\",\"3#72027\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"geologist\",\"last_drop\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":6,\"candidates\":[{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":20,\"heal_gained\":2,\"hp_after\":32,\"hp_before\":30,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"geologist\",\"feat_name\":\"地质学家\",\"hp_after\":32,\"hp_before\":32,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":12,\"candidates\":[{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":24,\"heal_gained\":6,\"hp_after\":38,\"hp_before\":32,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"last_drop\",\"feat_name\":\"一滴不剩\",\"hp_after\":38,\"hp_before\":38,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":78,\"route\":\"min\",\"seal_hit\":277,\"seal_hit_gain\":1113,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":301,\"seed\":72027,\"shop_gp_seq\":[23,55],\"stopped_at\":3,\"taken\":125,\"visited_count\":24,\"wallet_count\":29}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":15,\"choices\":17,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,35,35,35,35,35,35,38,38,38,38],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":102,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#72028\",\"1#72028\",\"1#72028\",\"1#72028\",\"1#72028\",\"1#72028\",\"1#72028\",\"2#72028\",\"2#72028\",\"2#72028\",\"2#72028\",\"2#72028\",\"2#72028\",\"2#72028\",\"2#72028\",\"2#72028\",\"2#72028\",\"3#72028\",\"3#72028\",\"3#72028\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"executioner\",\"seal_echo\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":9,\"candidates\":[{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":14,\"heal_gained\":7,\"hp_after\":28,\"hp_before\":21,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"executioner\",\"feat_name\":\"处刑台\",\"hp_after\":28,\"hp_before\":28,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":12,\"candidates\":[{\"feat_id\":\"seal_echo\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"breaker_strike\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":27,\"heal_gained\":4,\"hp_after\":38,\"hp_before\":34,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"seal_echo\",\"feat_name\":\"封印回响\",\"hp_after\":38,\"hp_before\":38,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":67,\"route\":\"min\",\"seal_hit\":244,\"seal_hit_gain\":1907,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":262,\"seed\":72028,\"shop_gp_seq\":[26,50],\"stopped_at\":3,\"taken\":102,\"visited_count\":20,\"wallet_count\":27}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":19,\"choices\":28,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,32,32,32,32,32,32,38,38,38,38,38,38,38,44],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":124,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#72029\",\"1#72029\",\"1#72029\",\"1#72029\",\"1#72029\",\"1#72029\",\"1#72029\",\"2#72029\",\"2#72029\",\"2#72029\",\"2#72029\",\"2#72029\",\"2#72029\",\"2#72029\",\"2#72029\",\"2#72029\",\"2#72029\",\"3#72029\",\"3#72029\",\"3#72029\",\"3#72029\",\"3#72029\",\"3#72029\",\"3#72029\",\"3#72029\",\"3#72029\",\"3#72029\",\"3#72029\",\"3#72029\",\"3#72029\",\"3#72029\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"frontier_miner\",\"warning_beacon\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":6,\"candidates\":[{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":22,\"heal_gained\":2,\"hp_after\":32,\"hp_before\":30,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"frontier_miner\",\"feat_name\":\"边陲矿工\",\"hp_after\":32,\"hp_before\":32,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":12,\"candidates\":[{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":22,\"heal_gained\":7,\"hp_after\":37,\"hp_before\":30,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"warning_beacon\",\"feat_name\":\"示警烟火\",\"hp_after\":37,\"hp_before\":37,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":87,\"route\":\"min\",\"seal_hit\":345,\"seal_hit_gain\":1813,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":369,\"seed\":72029,\"shop_gp_seq\":[13,77,90],\"stopped_at\":0,\"taken\":124,\"visited_count\":31,\"wallet_count\":36}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":19,\"choices\":28,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,35,35,35,35,35,35,41,41,41,41,41,41,41,47],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":125,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#72030\",\"1#72030\",\"1#72030\",\"1#72030\",\"1#72030\",\"1#72030\",\"1#72030\",\"2#72030\",\"2#72030\",\"2#72030\",\"2#72030\",\"2#72030\",\"2#72030\",\"2#72030\",\"2#72030\",\"2#72030\",\"2#72030\",\"3#72030\",\"3#72030\",\"3#72030\",\"3#72030\",\"3#72030\",\"3#72030\",\"3#72030\",\"3#72030\",\"3#72030\",\"3#72030\",\"3#72030\",\"3#72030\",\"3#72030\",\"3#72030\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"executioner\",\"trap_instinct\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":9,\"candidates\":[{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":16,\"heal_gained\":7,\"hp_after\":34,\"hp_before\":27,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"executioner\",\"feat_name\":\"处刑台\",\"hp_after\":34,\"hp_before\":34,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":15,\"candidates\":[{\"feat_id\":\"trap_instinct\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":30,\"heal_gained\":2,\"hp_after\":41,\"hp_before\":39,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"trap_instinct\",\"feat_name\":\"陷阱本能\",\"hp_after\":41,\"hp_before\":41,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":92,\"route\":\"min\",\"seal_hit\":335,\"seal_hit_gain\":1855,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":358,\"seed\":72030,\"shop_gp_seq\":[32,90,103],\"stopped_at\":0,\"taken\":125,\"visited_count\":31,\"wallet_count\":39}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":16,\"choices\":18,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,32,32,32,32,32,32,38,38,38,38,38],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":121,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#72031\",\"1#72031\",\"1#72031\",\"1#72031\",\"1#72031\",\"1#72031\",\"1#72031\",\"2#72031\",\"2#72031\",\"2#72031\",\"2#72031\",\"2#72031\",\"2#72031\",\"2#72031\",\"2#72031\",\"2#72031\",\"2#72031\",\"3#72031\",\"3#72031\",\"3#72031\",\"3#72031\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":2,\"relics\":[\"hard_hat\",\"perfect_insurance\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":6,\"candidates\":[{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"hard_hat\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":17,\"heal_gained\":1,\"hp_after\":32,\"hp_before\":31,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"hard_hat\",\"feat_name\":\"安全帽\",\"hp_after\":32,\"hp_before\":32,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":12,\"candidates\":[{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"perfect_insurance\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":23,\"heal_gained\":7,\"hp_after\":35,\"hp_before\":28,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"perfect_insurance\",\"feat_name\":\"完美保险\",\"hp_after\":35,\"hp_before\":35,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":72,\"route\":\"min\",\"seal_hit\":249,\"seal_hit_gain\":1056,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":273,\"seed\":72031,\"shop_gp_seq\":[20,50],\"stopped_at\":3,\"taken\":121,\"visited_count\":21,\"wallet_count\":27}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":19,\"choices\":28,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,32,32,32,32,32,32,38,38,38,38,38,38,38,44],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":176,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#72032\",\"1#72032\",\"1#72032\",\"1#72032\",\"1#72032\",\"1#72032\",\"1#72032\",\"2#72032\",\"2#72032\",\"2#72032\",\"2#72032\",\"2#72032\",\"2#72032\",\"2#72032\",\"2#72032\",\"2#72032\",\"2#72032\",\"3#72032\",\"3#72032\",\"3#72032\",\"3#72032\",\"3#72032\",\"3#72032\",\"3#72032\",\"3#72032\",\"3#72032\",\"3#72032\",\"3#72032\",\"3#72032\",\"3#72032\",\"3#72032\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":3,\"relics\":[\"seal_echo\",\"executioner\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":6,\"candidates\":[{\"feat_id\":\"seal_echo\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":18,\"heal_gained\":6,\"hp_after\":22,\"hp_before\":16,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"seal_echo\",\"feat_name\":\"封印回响\",\"hp_after\":22,\"hp_before\":22,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":12,\"candidates\":[{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":27,\"heal_gained\":5,\"hp_after\":38,\"hp_before\":33,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"executioner\",\"feat_name\":\"处刑台\",\"hp_after\":38,\"hp_before\":38,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":104,\"route\":\"min\",\"seal_hit\":406,\"seal_hit_gain\":523,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":434,\"seed\":72032,\"shop_gp_seq\":[12,58,112],\"stopped_at\":0,\"taken\":176,\"visited_count\":31,\"wallet_count\":36}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":12,\"choices\":15,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,35,35,35,35,35,35,38],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":105,\"hp_pct\":0,\"keystone_total\":3,\"level_maps\":[\"1#72033\",\"1#72033\",\"1#72033\",\"1#72033\",\"1#72033\",\"1#72033\",\"1#72033\",\"2#72033\",\"2#72033\",\"2#72033\",\"2#72033\",\"2#72033\",\"2#72033\",\"2#72033\",\"2#72033\",\"2#72033\",\"2#72033\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"geologist\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":9,\"candidates\":[{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":13,\"heal_gained\":7,\"hp_after\":34,\"hp_before\":27,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"geologist\",\"feat_name\":\"地质学家\",\"hp_after\":34,\"hp_before\":34,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":51,\"route\":\"min\",\"seal_hit\":181,\"seal_hit_gain\":512,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":195,\"seed\":72033,\"shop_gp_seq\":[36,46],\"stopped_at\":2,\"taken\":105,\"visited_count\":17,\"wallet_count\":24}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":23,\"choices\":26,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,32,32,32,32,32,32,41,41,41,41,41,41,41,41,41,41,41,47],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":127,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#72034\",\"1#72034\",\"1#72034\",\"1#72034\",\"1#72034\",\"1#72034\",\"1#72034\",\"2#72034\",\"2#72034\",\"2#72034\",\"2#72034\",\"2#72034\",\"2#72034\",\"2#72034\",\"2#72034\",\"2#72034\",\"2#72034\",\"3#72034\",\"3#72034\",\"3#72034\",\"3#72034\",\"3#72034\",\"3#72034\",\"3#72034\",\"3#72034\",\"3#72034\",\"3#72034\",\"3#72034\",\"3#72034\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":0,\"relics\":[\"map_exchange\",\"seal_burst\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":6,\"candidates\":[{\"feat_id\":\"map_exchange\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":11,\"heal_gained\":6,\"hp_after\":32,\"hp_before\":26,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"map_exchange\",\"feat_name\":\"弃图兑换\",\"hp_after\":32,\"hp_before\":32,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":15,\"candidates\":[{\"feat_id\":\"chain_traps\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_burst\",\"kind\":\"relic\",\"tier\":\"rare\"}],\"gp\":23,\"heal_gained\":7,\"hp_after\":41,\"hp_before\":34,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"seal_burst\",\"feat_name\":\"封印爆裂\",\"hp_after\":41,\"hp_before\":41,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":80,\"route\":\"min\",\"seal_hit\":312,\"seal_hit_gain\":2332,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":350,\"seed\":72034,\"shop_gp_seq\":[25,72,101],\"stopped_at\":0,\"taken\":127,\"visited_count\":29,\"wallet_count\":42}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":17,\"choices\":19,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,35,35,35,35,35,35,41,41,41,41,41,41],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":125,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#72035\",\"1#72035\",\"1#72035\",\"1#72035\",\"1#72035\",\"1#72035\",\"1#72035\",\"2#72035\",\"2#72035\",\"2#72035\",\"2#72035\",\"2#72035\",\"2#72035\",\"2#72035\",\"2#72035\",\"2#72035\",\"2#72035\",\"3#72035\",\"3#72035\",\"3#72035\",\"3#72035\",\"3#72035\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"seal_echo\",\"perfect_insurance\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":9,\"candidates\":[{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_echo\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":12,\"heal_gained\":7,\"hp_after\":32,\"hp_before\":25,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"seal_echo\",\"feat_name\":\"封印回响\",\"hp_after\":32,\"hp_before\":32,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":15,\"candidates\":[{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"perfect_insurance\",\"kind\":\"relic\",\"tier\":\"rare\"}],\"gp\":23,\"heal_gained\":1,\"hp_after\":41,\"hp_before\":40,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"perfect_insurance\",\"feat_name\":\"完美保险\",\"hp_after\":41,\"hp_before\":41,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":73,\"route\":\"min\",\"seal_hit\":281,\"seal_hit_gain\":2343,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":308,\"seed\":72035,\"shop_gp_seq\":[28,71],\"stopped_at\":3,\"taken\":125,\"visited_count\":22,\"wallet_count\":30}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":19,\"choices\":28,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,35,35,35,35,35,38,38,38,38,38,38,38,38,44],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":137,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#72036\",\"1#72036\",\"1#72036\",\"1#72036\",\"1#72036\",\"1#72036\",\"1#72036\",\"2#72036\",\"2#72036\",\"2#72036\",\"2#72036\",\"2#72036\",\"2#72036\",\"2#72036\",\"2#72036\",\"2#72036\",\"2#72036\",\"3#72036\",\"3#72036\",\"3#72036\",\"3#72036\",\"3#72036\",\"3#72036\",\"3#72036\",\"3#72036\",\"3#72036\",\"3#72036\",\"3#72036\",\"3#72036\",\"3#72036\",\"3#72036\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"stone_skin\",\"executioner\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":9,\"candidates\":[{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"stone_skin\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":19,\"heal_gained\":4,\"hp_after\":35,\"hp_before\":31,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"stone_skin\",\"feat_name\":\"石肤\",\"hp_after\":35,\"hp_before\":35,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":12,\"candidates\":[{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":21,\"heal_gained\":7,\"hp_after\":36,\"hp_before\":29,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"executioner\",\"feat_name\":\"处刑台\",\"hp_after\":36,\"hp_before\":36,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":97,\"route\":\"min\",\"seal_hit\":358,\"seal_hit_gain\":948,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":385,\"seed\":72036,\"shop_gp_seq\":[38,48,70],\"stopped_at\":0,\"taken\":137,\"visited_count\":31,\"wallet_count\":35}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":23,\"choices\":26,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,32,32,32,32,32,32,38,38,38,38,38,38,38,38,38,38,38,47],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":108,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#72037\",\"1#72037\",\"1#72037\",\"1#72037\",\"1#72037\",\"1#72037\",\"1#72037\",\"2#72037\",\"2#72037\",\"2#72037\",\"2#72037\",\"2#72037\",\"2#72037\",\"2#72037\",\"2#72037\",\"2#72037\",\"2#72037\",\"3#72037\",\"3#72037\",\"3#72037\",\"3#72037\",\"3#72037\",\"3#72037\",\"3#72037\",\"3#72037\",\"3#72037\",\"3#72037\",\"3#72037\",\"3#72037\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"market_close\",\"blood_tax\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":6,\"candidates\":[{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":18,\"heal_gained\":6,\"hp_after\":22,\"hp_before\":16,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"market_close\",\"feat_name\":\"收盘\",\"hp_after\":22,\"hp_before\":22,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":12,\"candidates\":[{\"feat_id\":\"blood_tax\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"skip_mining\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"map_exchange\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":17,\"heal_gained\":2,\"hp_after\":38,\"hp_before\":36,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blood_tax\",\"feat_name\":\"血税\",\"hp_after\":38,\"hp_before\":38,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":86,\"route\":\"min\",\"seal_hit\":346,\"seal_hit_gain\":1916,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":387,\"seed\":72037,\"shop_gp_seq\":[17,70,125],\"stopped_at\":0,\"taken\":108,\"visited_count\":29,\"wallet_count\":42}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":20,\"choices\":28,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,35,35,35,35,35,35,41,41,41,41,41,41,41,41,47],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":159,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#72038\",\"1#72038\",\"1#72038\",\"1#72038\",\"1#72038\",\"1#72038\",\"1#72038\",\"2#72038\",\"2#72038\",\"2#72038\",\"2#72038\",\"2#72038\",\"2#72038\",\"2#72038\",\"2#72038\",\"2#72038\",\"2#72038\",\"3#72038\",\"3#72038\",\"3#72038\",\"3#72038\",\"3#72038\",\"3#72038\",\"3#72038\",\"3#72038\",\"3#72038\",\"3#72038\",\"3#72038\",\"3#72038\",\"3#72038\",\"3#72038\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"map_exchange\",\"skip_mining\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":9,\"candidates\":[{\"feat_id\":\"map_exchange\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":10,\"heal_gained\":5,\"hp_after\":35,\"hp_before\":30,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"map_exchange\",\"feat_name\":\"弃图兑换\",\"hp_after\":35,\"hp_before\":35,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":15,\"candidates\":[{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"skip_mining\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":22,\"heal_gained\":6,\"hp_after\":41,\"hp_before\":35,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"skip_mining\",\"feat_name\":\"跳格采掘\",\"hp_after\":41,\"hp_before\":41,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":97,\"route\":\"min\",\"seal_hit\":378,\"seal_hit_gain\":2246,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":401,\"seed\":72038,\"shop_gp_seq\":[32,55,100],\"stopped_at\":0,\"taken\":159,\"visited_count\":31,\"wallet_count\":39}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"on\",\"battles\":15,\"choices\":17,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,35,35,35,35,35,35,41,41,41,41],\"equipped\":[\"morning_order\",\"wide_scout_band\",\"trial_pick\"],\"feat_mode\":\"off\",\"hero_dmg\":100,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#72039\",\"1#72039\",\"1#72039\",\"1#72039\",\"1#72039\",\"1#72039\",\"1#72039\",\"2#72039\",\"2#72039\",\"2#72039\",\"2#72039\",\"2#72039\",\"2#72039\",\"2#72039\",\"2#72039\",\"2#72039\",\"2#72039\",\"3#72039\",\"3#72039\",\"3#72039\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"refreshes\":1,\"relics\":[\"geologist\",\"pain_memory\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":9,\"candidates\":[{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":20,\"heal_gained\":6,\"hp_after\":35,\"hp_before\":29,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"geologist\",\"feat_name\":\"地质学家\",\"hp_after\":35,\"hp_before\":35,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":15,\"candidates\":[{\"feat_id\":\"stone_skin\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"pain_memory\",\"kind\":\"relic\",\"tier\":\"rare\"}],\"gp\":29,\"heal_gained\":6,\"hp_after\":41,\"hp_before\":35,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"pain_memory\",\"feat_name\":\"疼痛记忆\",\"hp_after\":41,\"hp_before\":41,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":62,\"route\":\"min\",\"seal_hit\":224,\"seal_hit_gain\":711,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":244,\"seed\":72039,\"shop_gp_seq\":[32,63],\"stopped_at\":3,\"taken\":100,\"visited_count\":20,\"wallet_count\":28}" ], "jsonl": { "normal": { - "sha256": "26bf0165d88e456ce841269c7d4ebcdb03f0a57c8cb5a7e39d73c500d2e1873b", - "rows": 9620 + "sha256": "b732bfeda9521de4896ebc11936670bd015032131bc871036896c1761ded12c8", + "rows": 9670 }, "perfect": { - "sha256": "2dba73a106bedc8d692e8f887785cb92cbffc65ad21c984a71948f40ad9603fa", - "rows": 38435 + "sha256": "f6bb9fd5ba5fc183fd29adbb00ae526a5c6852e7f52f9f597a0b4d853baf05d6", + "rows": 62765 } } }, "shop": { "runsum": [ - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":3,\"choices\":2,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":5,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":3,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":4,\"route\":\"min\",\"seal_hit\":16,\"seal_hit_gain\":192,\"seal_miss\":3,\"seal_miss_penalty\":8,\"seal_placed\":22,\"seed\":73000,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":21,\"visited_count\":3,\"wallet_count\":4}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":4,\"choices\":4,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":20,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":6,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":9,\"route\":\"min\",\"seal_hit\":25,\"seal_hit_gain\":0,\"seal_miss\":6,\"seal_miss_penalty\":6,\"seal_placed\":32,\"seed\":73001,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":28,\"visited_count\":5,\"wallet_count\":5}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":4,\"choices\":3,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":30,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\"],\"levels_cleared\":0,\"mine_dmg\":0,\"mine_hits\":2,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":13,\"route\":\"min\",\"seal_hit\":35,\"seal_hit_gain\":-362,\"seal_miss\":2,\"seal_miss_penalty\":8,\"seal_placed\":44,\"seed\":73002,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":30,\"visited_count\":4,\"wallet_count\":5}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":5,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":25,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":5,\"policy\":\"normal\",\"pool_stock\":{\"ap\":2,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"ap\",\"tier\":\"rare\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"ap\",\"tier\":\"rare\",\"value\":1}}],\"rounds\":17,\"route\":\"min\",\"seal_hit\":51,\"seal_hit_gain\":223,\"seal_miss\":5,\"seal_miss_penalty\":16,\"seal_placed\":61,\"seed\":73003,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":33,\"visited_count\":7,\"wallet_count\":6}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":4,\"choices\":4,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":15,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":8,\"policy\":\"normal\",\"pool_stock\":{\"ap\":2,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"ap\",\"tier\":\"rare\"},{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"armor\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"ap\",\"tier\":\"rare\",\"value\":1}}],\"rounds\":11,\"route\":\"min\",\"seal_hit\":27,\"seal_hit_gain\":275,\"seal_miss\":8,\"seal_miss_penalty\":6,\"seal_placed\":38,\"seed\":73004,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":23,\"visited_count\":5,\"wallet_count\":5}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":2,\"choices\":1,\"cleared\":false,\"encounter_max_hp_seq\":[26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":5,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\"],\"levels_cleared\":0,\"mine_dmg\":22,\"mine_hits\":5,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":4,\"route\":\"min\",\"seal_hit\":8,\"seal_hit_gain\":-91,\"seal_miss\":5,\"seal_miss_penalty\":8,\"seal_placed\":18,\"seed\":73005,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":27,\"visited_count\":2,\"wallet_count\":3}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":5,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":18,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":5,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":22,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"excellent\"},{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"feat_ticket\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"attack\",\"tier\":\"excellent\",\"value\":2}}],\"rounds\":15,\"route\":\"min\",\"seal_hit\":48,\"seal_hit_gain\":399,\"seal_miss\":4,\"seal_miss_penalty\":0,\"seal_placed\":57,\"seed\":73006,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":34,\"visited_count\":7,\"wallet_count\":6}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":4,\"choices\":4,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":10,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":4,\"policy\":\"normal\",\"pool_stock\":{\"ap\":2,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"ap\",\"tier\":\"rare\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"ap\",\"tier\":\"rare\",\"value\":1}}],\"rounds\":8,\"route\":\"min\",\"seal_hit\":34,\"seal_hit_gain\":53,\"seal_miss\":2,\"seal_miss_penalty\":6,\"seal_placed\":41,\"seed\":73007,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":18,\"visited_count\":5,\"wallet_count\":5}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":6,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":22,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"三干宽门\"],\"levels_cleared\":1,\"mine_dmg\":16,\"mine_hits\":9,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":22,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[\"blood_honey\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"attack\",\"tier\":\"excellent\",\"value\":2}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":37,\"heal_gained\":5,\"hp_after\":12,\"hp_before\":7,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blood_honey\",\"feat_name\":\"血蜜\",\"hp_after\":12,\"hp_before\":12,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":19,\"route\":\"min\",\"seal_hit\":56,\"seal_hit_gain\":559,\"seal_miss\":5,\"seal_miss_penalty\":22,\"seal_placed\":67,\"seed\":73008,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":2,\"taken\":38,\"visited_count\":8,\"wallet_count\":7}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":4,\"choices\":3,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":18,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\"],\"levels_cleared\":0,\"mine_dmg\":14,\"mine_hits\":3,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":7,\"route\":\"min\",\"seal_hit\":27,\"seal_hit_gain\":-267,\"seal_miss\":3,\"seal_miss_penalty\":0,\"seal_placed\":37,\"seed\":73009,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":32,\"visited_count\":4,\"wallet_count\":5}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":4,\"choices\":3,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":12,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":6,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":9,\"route\":\"min\",\"seal_hit\":28,\"seal_hit_gain\":-83,\"seal_miss\":5,\"seal_miss_penalty\":4,\"seal_placed\":37,\"seed\":73010,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":20,\"visited_count\":4,\"wallet_count\":5}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":3,\"choices\":2,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":6,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":4,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":7,\"route\":\"min\",\"seal_hit\":20,\"seal_hit_gain\":9,\"seal_miss\":1,\"seal_miss_penalty\":6,\"seal_placed\":22,\"seed\":73011,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":22,\"visited_count\":3,\"wallet_count\":4}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":5,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":12,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":3,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":22,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"excellent\"},{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"feat_ticket\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"attack\",\"tier\":\"excellent\",\"value\":2}}],\"rounds\":9,\"route\":\"min\",\"seal_hit\":29,\"seal_hit_gain\":106,\"seal_miss\":5,\"seal_miss_penalty\":12,\"seal_placed\":41,\"seed\":73012,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":20,\"visited_count\":7,\"wallet_count\":6}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":2,\"choices\":1,\"cleared\":false,\"encounter_max_hp_seq\":[26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":2,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·赏战整\",\"地精洞窟·赏战整\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":4,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":2,\"route\":\"min\",\"seal_hit\":5,\"seal_hit_gain\":73,\"seal_miss\":3,\"seal_miss_penalty\":8,\"seal_placed\":10,\"seed\":73013,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":18,\"visited_count\":2,\"wallet_count\":3}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":4,\"choices\":5,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":15,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\"],\"levels_cleared\":0,\"mine_dmg\":0,\"mine_hits\":4,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"rap\",\"tier\":\"rare\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"rap\",\"tier\":\"rare\",\"value\":3}}],\"rounds\":10,\"route\":\"min\",\"seal_hit\":26,\"seal_hit_gain\":184,\"seal_miss\":8,\"seal_miss_penalty\":20,\"seal_placed\":35,\"seed\":73014,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":15,\"visited_count\":6,\"wallet_count\":5}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":4,\"choices\":4,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":15,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":6,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"gp\",\"tier\":\"excellent\"},{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"feat_ticket\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"gp\",\"tier\":\"excellent\",\"value\":50}}],\"rounds\":11,\"route\":\"min\",\"seal_hit\":23,\"seal_hit_gain\":15,\"seal_miss\":8,\"seal_miss_penalty\":24,\"seal_placed\":32,\"seed\":73015,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":23,\"visited_count\":5,\"wallet_count\":6}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":4,\"choices\":4,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":13,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\",\"地精洞窟·赏战整\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":5,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"heal\",\"tier\":\"excellent\"},{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"rap\",\"tier\":\"rare\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"rap\",\"tier\":\"rare\",\"value\":3}}],\"rounds\":8,\"route\":\"min\",\"seal_hit\":23,\"seal_hit_gain\":134,\"seal_miss\":5,\"seal_miss_penalty\":8,\"seal_placed\":34,\"seed\":73016,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":29,\"visited_count\":5,\"wallet_count\":5}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":7,\"choices\":7,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":37,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":1,\"mine_dmg\":30,\"mine_hits\":12,\"policy\":\"normal\",\"pool_stock\":{\"ap\":2,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[\"twin_pick\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"ap\",\"tier\":\"rare\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"ap\",\"tier\":\"rare\",\"value\":1}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":48,\"heal_gained\":5,\"hp_after\":20,\"hp_before\":15,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"twin_pick\",\"feat_name\":\"双管镐\",\"hp_after\":20,\"hp_before\":20,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":34,\"route\":\"min\",\"seal_hit\":89,\"seal_hit_gain\":406,\"seal_miss\":12,\"seal_miss_penalty\":8,\"seal_placed\":107,\"seed\":73017,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":2,\"taken\":67,\"visited_count\":9,\"wallet_count\":8}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":4,\"choices\":5,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":17,\"hp_pct\":0,\"keystone_total\":0,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\"],\"levels_cleared\":0,\"mine_dmg\":20,\"mine_hits\":7,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":2,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"heal\",\"tier\":\"excellent\"},{\"kind\":\"armor\",\"tier\":\"excellent\"},{\"kind\":\"gp\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"heal\",\"tier\":\"excellent\",\"value\":6}}],\"rounds\":10,\"route\":\"min\",\"seal_hit\":30,\"seal_hit_gain\":182,\"seal_miss\":5,\"seal_miss_penalty\":8,\"seal_placed\":37,\"seed\":73018,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":37,\"visited_count\":6,\"wallet_count\":5}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":6,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":35,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"宽廊斜岔\"],\"levels_cleared\":1,\"mine_dmg\":14,\"mine_hits\":6,\"policy\":\"normal\",\"pool_stock\":{\"ap\":2,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[\"execution_scholar\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"refresh\",\"tier\":\"excellent\"},{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"ap\",\"tier\":\"rare\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"ap\",\"tier\":\"rare\",\"value\":1}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":55,\"heal_gained\":5,\"hp_after\":18,\"hp_before\":13,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"execution_scholar\",\"feat_name\":\"斩首学者\",\"hp_after\":18,\"hp_before\":18,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":26,\"route\":\"min\",\"seal_hit\":72,\"seal_hit_gain\":357,\"seal_miss\":8,\"seal_miss_penalty\":14,\"seal_placed\":88,\"seed\":73019,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":2,\"taken\":49,\"visited_count\":8,\"wallet_count\":7}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":14,\"choices\":16,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26,31,31,31,31,36],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":71,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":21,\"attack\":17,\"feat_ticket\":0,\"heal\":3,\"max_hp\":20},\"refreshes\":0,\"relics\":[\"seal_burst\",\"pain_memory\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"gp\",\"tier\":\"excellent\"},{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"heal\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"gp\",\"tier\":\"excellent\",\"value\":50}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_burst\",\"kind\":\"relic\",\"tier\":\"rare\"}],\"gp\":106,\"heal_gained\":5,\"hp_after\":24,\"hp_before\":19,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"seal_burst\",\"feat_name\":\"封印爆裂\",\"hp_after\":24,\"hp_before\":24,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"pain_memory\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"trap_instinct\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":37,\"heal_gained\":6,\"hp_after\":23,\"hp_before\":17,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"pain_memory\",\"feat_name\":\"疼痛记忆\",\"hp_after\":23,\"hp_before\":23,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":44,\"route\":\"min\",\"seal_hit\":165,\"seal_hit_gain\":540,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":177,\"seed\":73020,\"shop_buys\":9,\"shop_gp_seq\":[123,60],\"shop_mode\":\"pool\",\"stopped_at\":0,\"taken\":71,\"visited_count\":19,\"wallet_count\":25}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":9,\"choices\":9,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":62,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":22,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[\"stone_skin\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"attack\",\"tier\":\"excellent\",\"value\":2}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"stone_skin\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":44,\"heal_gained\":4,\"hp_after\":26,\"hp_before\":22,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"stone_skin\",\"feat_name\":\"石肤\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":33,\"route\":\"min\",\"seal_hit\":119,\"seal_hit_gain\":809,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":134,\"seed\":73021,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":2,\"taken\":62,\"visited_count\":11,\"wallet_count\":10}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":16,\"choices\":17,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,36,36,36,36,36,36,36,36,36,36,36,36,41],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":128,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":19,\"attack\":19,\"feat_ticket\":0,\"heal\":3,\"max_hp\":20},\"refreshes\":2,\"relics\":[\"twin_pick\",\"frontier_miner\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"excellent\"},{\"kind\":\"armor\",\"tier\":\"excellent\"},{\"kind\":\"gp\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"max_hp\",\"tier\":\"excellent\",\"value\":10}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":34,\"heal_gained\":0,\"hp_after\":36,\"hp_before\":36,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"twin_pick\",\"feat_name\":\"双管镐\",\"hp_after\":36,\"hp_before\":36,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":57,\"heal_gained\":7,\"hp_after\":27,\"hp_before\":20,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"frontier_miner\",\"feat_name\":\"边陲矿工\",\"hp_after\":27,\"hp_before\":27,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":75,\"route\":\"min\",\"seal_hit\":310,\"seal_hit_gain\":-239,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":336,\"seed\":73022,\"shop_buys\":8,\"shop_gp_seq\":[91,96],\"shop_mode\":\"pool\",\"stopped_at\":0,\"taken\":128,\"visited_count\":20,\"wallet_count\":25}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":16,\"choices\":18,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26,26,41,41,41,41,41,46],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":122,\"hp_pct\":0,\"keystone_total\":6,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":17,\"attack\":22,\"feat_ticket\":0,\"heal\":3,\"max_hp\":19},\"refreshes\":2,\"relics\":[\"twin_pick\",\"hard_hat\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"gp\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"gp\",\"tier\":\"excellent\",\"value\":50}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":84,\"heal_gained\":5,\"hp_after\":21,\"hp_before\":16,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"twin_pick\",\"feat_name\":\"双管镐\",\"hp_after\":21,\"hp_before\":21,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"hard_hat\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":43,\"heal_gained\":8,\"hp_after\":26,\"hp_before\":18,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"hard_hat\",\"feat_name\":\"安全帽\",\"hp_after\":26,\"hp_before\":26,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":74,\"route\":\"min\",\"seal_hit\":300,\"seal_hit_gain\":328,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":323,\"seed\":73023,\"shop_buys\":9,\"shop_gp_seq\":[130,80],\"shop_mode\":\"pool\",\"stopped_at\":3,\"taken\":122,\"visited_count\":21,\"wallet_count\":27}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":12,\"choices\":12,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26,36,36,36],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":119,\"hp_pct\":0,\"keystone_total\":6,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"三干中廊\",\"三干中廊\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":19,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":20},\"refreshes\":4,\"relics\":[\"execution_scholar\",\"skip_mining\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"armor\",\"tier\":\"excellent\"},{\"kind\":\"attack\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"armor\",\"tier\":\"excellent\",\"value\":2}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":26,\"heal_gained\":3,\"hp_after\":26,\"hp_before\":23,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"execution_scholar\",\"feat_name\":\"斩首学者\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"skip_mining\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":36,\"heal_gained\":7,\"hp_after\":14,\"hp_before\":7,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"skip_mining\",\"feat_name\":\"跳格采掘\",\"hp_after\":14,\"hp_before\":14,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":72,\"route\":\"min\",\"seal_hit\":241,\"seal_hit_gain\":131,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":258,\"seed\":73024,\"shop_buys\":4,\"shop_gp_seq\":[62],\"shop_mode\":\"pool\",\"stopped_at\":3,\"taken\":119,\"visited_count\":15,\"wallet_count\":17}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":13,\"choices\":16,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,36,36,36,36,36,46,46,46,46,51],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":117,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":18,\"attack\":22,\"feat_ticket\":0,\"heal\":3,\"max_hp\":18},\"refreshes\":1,\"relics\":[\"stone_skin\",\"map_exchange\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"max_hp\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"max_hp\",\"tier\":\"excellent\",\"value\":10}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"stone_skin\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":44,\"heal_gained\":7,\"hp_after\":23,\"hp_before\":16,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"stone_skin\",\"feat_name\":\"石肤\",\"hp_after\":23,\"hp_before\":23,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"map_exchange\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":39,\"heal_gained\":9,\"hp_after\":36,\"hp_before\":27,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"map_exchange\",\"feat_name\":\"弃图兑换\",\"hp_after\":36,\"hp_before\":36,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":81,\"route\":\"min\",\"seal_hit\":263,\"seal_hit_gain\":1233,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":276,\"seed\":73025,\"shop_buys\":8,\"shop_gp_seq\":[75,63],\"shop_mode\":\"pool\",\"stopped_at\":0,\"taken\":117,\"visited_count\":19,\"wallet_count\":22}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":14,\"choices\":16,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26,26,26,26,26,31],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":96,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":18,\"attack\":21,\"feat_ticket\":0,\"heal\":3,\"max_hp\":21},\"refreshes\":2,\"relics\":[\"twin_pick\",\"trap_instinct\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"refresh\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"refresh\",\"tier\":\"excellent\",\"value\":1}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":30,\"heal_gained\":5,\"hp_after\":25,\"hp_before\":20,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"twin_pick\",\"feat_name\":\"双管镐\",\"hp_after\":25,\"hp_before\":25,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"trap_instinct\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":50,\"heal_gained\":5,\"hp_after\":26,\"hp_before\":21,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"trap_instinct\",\"feat_name\":\"陷阱本能\",\"hp_after\":26,\"hp_before\":26,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":68,\"route\":\"min\",\"seal_hit\":277,\"seal_hit_gain\":1000,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":294,\"seed\":73026,\"shop_buys\":7,\"shop_gp_seq\":[46,85],\"shop_mode\":\"pool\",\"stopped_at\":0,\"taken\":96,\"visited_count\":19,\"wallet_count\":22}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":14,\"choices\":16,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,36,36,36,36,36,41],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":89,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":21,\"attack\":18,\"feat_ticket\":0,\"heal\":3,\"max_hp\":19},\"refreshes\":0,\"relics\":[\"warning_beacon\",\"executioner\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"attack\",\"tier\":\"excellent\",\"value\":2}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":42,\"heal_gained\":3,\"hp_after\":26,\"hp_before\":23,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"warning_beacon\",\"feat_name\":\"示警烟火\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":39,\"heal_gained\":7,\"hp_after\":30,\"hp_before\":23,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"executioner\",\"feat_name\":\"处刑台\",\"hp_after\":30,\"hp_before\":30,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":66,\"route\":\"min\",\"seal_hit\":240,\"seal_hit_gain\":758,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":252,\"seed\":73027,\"shop_buys\":8,\"shop_gp_seq\":[72,77],\"shop_mode\":\"pool\",\"stopped_at\":0,\"taken\":89,\"visited_count\":19,\"wallet_count\":23}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":11,\"choices\":12,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":93,\"hp_pct\":0,\"keystone_total\":6,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"三干中廊\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":20,\"attack\":22,\"feat_ticket\":0,\"heal\":2,\"max_hp\":22},\"refreshes\":0,\"relics\":[\"chain_traps\",\"last_drop\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"heal\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"heal\",\"tier\":\"excellent\",\"value\":6}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"chain_traps\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"trap_instinct\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":23,\"heal_gained\":1,\"hp_after\":26,\"hp_before\":25,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"chain_traps\",\"feat_name\":\"连环陷阱\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":41,\"heal_gained\":5,\"hp_after\":13,\"hp_before\":8,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"last_drop\",\"feat_name\":\"一滴不剩\",\"hp_after\":13,\"hp_before\":13,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":58,\"route\":\"min\",\"seal_hit\":193,\"seal_hit_gain\":-40,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":207,\"seed\":73028,\"shop_buys\":3,\"shop_gp_seq\":[47],\"shop_mode\":\"pool\",\"stopped_at\":3,\"taken\":93,\"visited_count\":15,\"wallet_count\":15}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":13,\"choices\":16,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,41,41,41,41,46],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":125,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":20,\"attack\":20,\"feat_ticket\":0,\"heal\":3,\"max_hp\":19},\"refreshes\":2,\"relics\":[\"hard_hat\",\"seal_echo\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"excellent\"},{\"kind\":\"rap\",\"tier\":\"rare\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"rap\",\"tier\":\"rare\",\"value\":3}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"hard_hat\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":45,\"heal_gained\":1,\"hp_after\":26,\"hp_before\":25,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"hard_hat\",\"feat_name\":\"安全帽\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_echo\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":29,\"heal_gained\":8,\"hp_after\":15,\"hp_before\":7,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"seal_echo\",\"feat_name\":\"封印回响\",\"hp_after\":15,\"hp_before\":15,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":83,\"route\":\"min\",\"seal_hit\":269,\"seal_hit_gain\":-646,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":279,\"seed\":73029,\"shop_buys\":8,\"shop_gp_seq\":[68,69],\"shop_mode\":\"pool\",\"stopped_at\":0,\"taken\":125,\"visited_count\":19,\"wallet_count\":22}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":14,\"choices\":16,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26,31,31,31,31,36],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":97,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":20,\"attack\":19,\"feat_ticket\":0,\"heal\":3,\"max_hp\":20},\"refreshes\":1,\"relics\":[\"trap_instinct\",\"blast_accounting\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"heal\",\"tier\":\"excellent\"},{\"kind\":\"gp\",\"tier\":\"excellent\"},{\"kind\":\"time_sand\",\"tier\":\"rare\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"time_sand\",\"tier\":\"rare\",\"value\":2}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"trap_instinct\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":58,\"heal_gained\":5,\"hp_after\":18,\"hp_before\":13,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"trap_instinct\",\"feat_name\":\"陷阱本能\",\"hp_after\":18,\"hp_before\":18,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":56,\"heal_gained\":6,\"hp_after\":19,\"hp_before\":13,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blast_accounting\",\"feat_name\":\"爆破会计\",\"hp_after\":19,\"hp_before\":19,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":71,\"route\":\"min\",\"seal_hit\":243,\"seal_hit_gain\":1583,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":260,\"seed\":73030,\"shop_buys\":8,\"shop_gp_seq\":[92,83],\"shop_mode\":\"pool\",\"stopped_at\":0,\"taken\":97,\"visited_count\":19,\"wallet_count\":23}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":12,\"choices\":12,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26,31,31,31],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":109,\"hp_pct\":0,\"keystone_total\":6,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"三干宽门\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":20,\"attack\":22,\"feat_ticket\":0,\"heal\":3,\"max_hp\":21},\"refreshes\":1,\"relics\":[\"seal_echo\",\"perfect_insurance\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"refresh\",\"tier\":\"excellent\"},{\"kind\":\"heal\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"refresh\",\"tier\":\"excellent\",\"value\":1}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_echo\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":36,\"heal_gained\":5,\"hp_after\":26,\"hp_before\":21,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"seal_echo\",\"feat_name\":\"封印回响\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"perfect_insurance\",\"kind\":\"relic\",\"tier\":\"rare\"}],\"gp\":41,\"heal_gained\":6,\"hp_after\":17,\"hp_before\":11,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"perfect_insurance\",\"feat_name\":\"完美保险\",\"hp_after\":17,\"hp_before\":17,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":68,\"route\":\"min\",\"seal_hit\":218,\"seal_hit_gain\":621,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":231,\"seed\":73031,\"shop_buys\":4,\"shop_gp_seq\":[71],\"shop_mode\":\"pool\",\"stopped_at\":3,\"taken\":109,\"visited_count\":15,\"wallet_count\":17}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":12,\"choices\":12,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,36,36,36,36,36,36,46,46,46],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":109,\"hp_pct\":0,\"keystone_total\":6,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"三干宽门\",\"三干宽门\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":21,\"attack\":22,\"feat_ticket\":0,\"heal\":3,\"max_hp\":19},\"refreshes\":2,\"relics\":[\"chain_traps\",\"skip_mining\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"max_hp\",\"tier\":\"excellent\"},{\"kind\":\"gp\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"max_hp\",\"tier\":\"excellent\",\"value\":10}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"chain_traps\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":36,\"heal_gained\":7,\"hp_after\":25,\"hp_before\":18,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"chain_traps\",\"feat_name\":\"连环陷阱\",\"hp_after\":25,\"hp_before\":25,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"skip_mining\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":47,\"heal_gained\":9,\"hp_after\":12,\"hp_before\":3,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"skip_mining\",\"feat_name\":\"跳格采掘\",\"hp_after\":12,\"hp_before\":12,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":61,\"route\":\"min\",\"seal_hit\":189,\"seal_hit_gain\":1018,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":214,\"seed\":73032,\"shop_buys\":4,\"shop_gp_seq\":[85],\"shop_mode\":\"pool\",\"stopped_at\":3,\"taken\":109,\"visited_count\":15,\"wallet_count\":17}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":10,\"choices\":11,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26,31],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":81,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":20,\"attack\":22,\"feat_ticket\":0,\"heal\":3,\"max_hp\":21},\"refreshes\":1,\"relics\":[\"executioner\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"heal\",\"tier\":\"excellent\"},{\"kind\":\"rap\",\"tier\":\"rare\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"rap\",\"tier\":\"rare\",\"value\":3}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":39,\"heal_gained\":5,\"hp_after\":20,\"hp_before\":15,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"executioner\",\"feat_name\":\"处刑台\",\"hp_after\":20,\"hp_before\":20,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":53,\"route\":\"min\",\"seal_hit\":165,\"seal_hit_gain\":1026,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":176,\"seed\":73033,\"shop_buys\":4,\"shop_gp_seq\":[93],\"shop_mode\":\"pool\",\"stopped_at\":2,\"taken\":81,\"visited_count\":13,\"wallet_count\":15}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":10,\"choices\":10,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":86,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[\"skip_mining\",\"blood_honey\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"feat_ticket\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"feat_archetype\":\"\",\"feat_id\":\"skip_mining\",\"feat_name\":\"跳格采掘\",\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":59,\"heal_gained\":5,\"hp_after\":23,\"hp_before\":18,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blood_honey\",\"feat_name\":\"血蜜\",\"hp_after\":23,\"hp_before\":23,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":46,\"route\":\"min\",\"seal_hit\":149,\"seal_hit_gain\":940,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":155,\"seed\":73034,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":2,\"taken\":86,\"visited_count\":12,\"wallet_count\":11}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":13,\"choices\":16,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,31,31,31,31,51],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":108,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":20,\"attack\":21,\"feat_ticket\":0,\"heal\":3,\"max_hp\":17},\"refreshes\":2,\"relics\":[\"last_stand\",\"venom_edge\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"attack\",\"tier\":\"common\"},{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"gp\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"gp\",\"tier\":\"excellent\",\"value\":50}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"hard_hat\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"last_stand\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":98,\"heal_gained\":5,\"hp_after\":22,\"hp_before\":17,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"last_stand\",\"feat_name\":\"背水\",\"hp_after\":22,\"hp_before\":22,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"venom_edge\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":43,\"heal_gained\":6,\"hp_after\":29,\"hp_before\":23,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"venom_edge\",\"feat_name\":\"淬毒刃纹\",\"hp_after\":29,\"hp_before\":29,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":75,\"route\":\"min\",\"seal_hit\":242,\"seal_hit_gain\":1007,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":260,\"seed\":73035,\"shop_buys\":9,\"shop_gp_seq\":[120,64],\"shop_mode\":\"pool\",\"stopped_at\":0,\"taken\":108,\"visited_count\":19,\"wallet_count\":24}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":13,\"choices\":16,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,41,41,41,41,41],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":106,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"地精洞窟·主线洞道(现行)\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊斜岔\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":19,\"attack\":21,\"feat_ticket\":0,\"heal\":3,\"max_hp\":19},\"refreshes\":2,\"relics\":[\"frontier_miner\",\"blood_honey\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"armor\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"excellent\"},{\"kind\":\"gp\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"attack\",\"tier\":\"excellent\",\"value\":2}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":30,\"heal_gained\":1,\"hp_after\":26,\"hp_before\":25,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"frontier_miner\",\"feat_name\":\"边陲矿工\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":22,\"heal_gained\":8,\"hp_after\":22,\"hp_before\":14,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blood_honey\",\"feat_name\":\"血蜜\",\"hp_after\":22,\"hp_before\":22,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":65,\"route\":\"min\",\"seal_hit\":218,\"seal_hit_gain\":1402,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":245,\"seed\":73036,\"shop_buys\":7,\"shop_gp_seq\":[59,49],\"shop_mode\":\"pool\",\"stopped_at\":0,\"taken\":106,\"visited_count\":19,\"wallet_count\":21}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":11,\"choices\":12,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,31,31,31],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":82,\"hp_pct\":0,\"keystone_total\":6,\"level_templates\":[\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"地精洞窟·先赏后整\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":20,\"feat_ticket\":0,\"heal\":3,\"max_hp\":21},\"refreshes\":1,\"relics\":[\"market_close\",\"perfect_insurance\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"excellent\"},{\"kind\":\"gp\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"attack\",\"tier\":\"excellent\",\"value\":2}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":26,\"heal_gained\":1,\"hp_after\":26,\"hp_before\":25,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"market_close\",\"feat_name\":\"收盘\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"map_exchange\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"perfect_insurance\",\"kind\":\"relic\",\"tier\":\"rare\"}],\"gp\":34,\"heal_gained\":6,\"hp_after\":21,\"hp_before\":15,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"perfect_insurance\",\"feat_name\":\"完美保险\",\"hp_after\":21,\"hp_before\":21,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":51,\"route\":\"min\",\"seal_hit\":166,\"seal_hit_gain\":867,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":184,\"seed\":73037,\"shop_buys\":3,\"shop_gp_seq\":[51],\"shop_mode\":\"pool\",\"stopped_at\":3,\"taken\":82,\"visited_count\":15,\"wallet_count\":15}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":10,\"choices\":10,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":58,\"hp_pct\":0,\"keystone_total\":3,\"level_templates\":[\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"地精洞窟·先整后赏\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":22,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[\"seal_echo\"],\"result\":\"failed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"gp\",\"tier\":\"common\"},{\"kind\":\"max_hp\",\"tier\":\"common\"},{\"kind\":\"attack\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"attack\",\"tier\":\"excellent\",\"value\":2}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_echo\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":33,\"heal_gained\":1,\"hp_after\":26,\"hp_before\":25,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"seal_echo\",\"feat_name\":\"封印回响\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":37,\"route\":\"min\",\"seal_hit\":120,\"seal_hit_gain\":485,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":133,\"seed\":73038,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":2,\"taken\":58,\"visited_count\":12,\"wallet_count\":11}", - "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":16,\"choices\":17,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26,26,31,31,31,31,31,36],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":45,\"hp_pct\":0,\"keystone_total\":9,\"level_templates\":[\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"地精洞窟·整战赏\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"窄廊短支\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\",\"宽廊双干(现行)\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":20,\"attack\":19,\"feat_ticket\":0,\"heal\":3,\"max_hp\":20},\"refreshes\":1,\"relics\":[\"blood_tax\",\"hard_hat\"],\"result\":\"completed\",\"reward_events\":[{\"node_curio_pick\":[{\"kind\":\"rap\",\"tier\":\"rare\"},{\"kind\":\"max_hp\",\"tier\":\"excellent\"},{\"kind\":\"attack\",\"tier\":\"excellent\"}]},{\"curio_resolved\":{\"context\":\"reward_node\",\"level_index\":1,\"picked\":\"rap\",\"tier\":\"rare\",\"value\":3}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"breaker_strike\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"blood_tax\",\"kind\":\"relic\",\"tier\":\"rare\"}],\"gp\":35,\"heal_gained\":5,\"hp_after\":25,\"hp_before\":20,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blood_tax\",\"feat_name\":\"血税\",\"hp_after\":25,\"hp_before\":25,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"candidates\":[{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"hard_hat\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":28,\"heal_gained\":6,\"hp_after\":30,\"hp_before\":24,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"hard_hat\",\"feat_name\":\"安全帽\",\"hp_after\":30,\"hp_before\":30,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":46,\"route\":\"min\",\"seal_hit\":164,\"seal_hit_gain\":1568,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":196,\"seed\":73039,\"shop_buys\":8,\"shop_gp_seq\":[75,61],\"shop_mode\":\"pool\",\"stopped_at\":0,\"taken\":45,\"visited_count\":20,\"wallet_count\":25}" + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":3,\"choices\":2,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":9,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#73000\",\"1#73000\",\"1#73000\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":4,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":5,\"route\":\"min\",\"seal_hit\":19,\"seal_hit_gain\":-30,\"seal_miss\":5,\"seal_miss_penalty\":8,\"seal_placed\":27,\"seed\":73000,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":17,\"visited_count\":3,\"wallet_count\":4}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":5,\"choices\":4,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":19,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#73001\",\"1#73001\",\"1#73001\",\"1#73001\",\"1#73001\"],\"levels_cleared\":0,\"mine_dmg\":0,\"mine_hits\":6,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":15,\"route\":\"min\",\"seal_hit\":43,\"seal_hit_gain\":437,\"seal_miss\":8,\"seal_miss_penalty\":6,\"seal_placed\":56,\"seed\":73001,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":19,\"visited_count\":5,\"wallet_count\":6}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":5,\"choices\":4,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":15,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#73002\",\"1#73002\",\"1#73002\",\"1#73002\",\"1#73002\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":6,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":9,\"route\":\"min\",\"seal_hit\":35,\"seal_hit_gain\":-120,\"seal_miss\":6,\"seal_miss_penalty\":8,\"seal_placed\":46,\"seed\":73002,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":23,\"visited_count\":5,\"wallet_count\":6}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":3,\"choices\":2,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":6,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#73003\",\"1#73003\",\"1#73003\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":3,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":5,\"route\":\"min\",\"seal_hit\":16,\"seal_hit_gain\":-158,\"seal_miss\":4,\"seal_miss_penalty\":14,\"seal_placed\":22,\"seed\":73003,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":14,\"visited_count\":3,\"wallet_count\":4}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":4,\"choices\":3,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":13,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#73004\",\"1#73004\",\"1#73004\",\"1#73004\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":8,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":10,\"route\":\"min\",\"seal_hit\":30,\"seal_hit_gain\":-37,\"seal_miss\":8,\"seal_miss_penalty\":8,\"seal_placed\":47,\"seed\":73004,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":29,\"visited_count\":4,\"wallet_count\":5}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":5,\"choices\":4,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":20,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#73005\",\"1#73005\",\"1#73005\",\"1#73005\",\"1#73005\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":5,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":14,\"route\":\"min\",\"seal_hit\":36,\"seal_hit_gain\":596,\"seal_miss\":3,\"seal_miss_penalty\":6,\"seal_placed\":43,\"seed\":73005,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":28,\"visited_count\":5,\"wallet_count\":6}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":6,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,41],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":19,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#73006\",\"1#73006\",\"1#73006\",\"1#73006\",\"1#73006\",\"1#73006\",\"1#73006\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":12,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":19},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":21,\"route\":\"min\",\"seal_hit\":61,\"seal_hit_gain\":393,\"seal_miss\":14,\"seal_miss_penalty\":22,\"seal_placed\":81,\"seed\":73006,\"shop_buys\":3,\"shop_gp_seq\":[36],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":27,\"visited_count\":7,\"wallet_count\":10}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":3,\"choices\":2,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":9,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#73007\",\"1#73007\",\"1#73007\"],\"levels_cleared\":0,\"mine_dmg\":0,\"mine_hits\":5,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":6,\"route\":\"min\",\"seal_hit\":20,\"seal_hit_gain\":-92,\"seal_miss\":5,\"seal_miss_penalty\":22,\"seal_placed\":25,\"seed\":73007,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":9,\"visited_count\":3,\"wallet_count\":4}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":5,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,31],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":25,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#73008\",\"1#73008\",\"1#73008\",\"1#73008\",\"1#73008\",\"1#73008\",\"1#73008\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":7,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":21,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":21},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":21,\"route\":\"min\",\"seal_hit\":63,\"seal_hit_gain\":398,\"seal_miss\":8,\"seal_miss_penalty\":14,\"seal_placed\":73,\"seed\":73008,\"shop_buys\":2,\"shop_gp_seq\":[23],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":41,\"visited_count\":7,\"wallet_count\":8}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":4,\"choices\":3,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":23,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#73009\",\"1#73009\",\"1#73009\",\"1#73009\"],\"levels_cleared\":0,\"mine_dmg\":0,\"mine_hits\":2,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":11,\"route\":\"min\",\"seal_hit\":34,\"seal_hit_gain\":4,\"seal_miss\":3,\"seal_miss_penalty\":6,\"seal_placed\":40,\"seed\":73009,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":23,\"visited_count\":4,\"wallet_count\":5}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":3,\"choices\":2,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":11,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#73010\",\"1#73010\",\"1#73010\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":7,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":10,\"route\":\"min\",\"seal_hit\":25,\"seal_hit_gain\":469,\"seal_miss\":6,\"seal_miss_penalty\":14,\"seal_placed\":37,\"seed\":73010,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":19,\"visited_count\":3,\"wallet_count\":4}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":7,\"choices\":7,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":39,\"hp_pct\":0,\"keystone_total\":3,\"level_maps\":[\"1#73011\",\"1#73011\",\"1#73011\",\"1#73011\",\"1#73011\",\"1#73011\",\"1#73011\",\"2#73011\",\"2#73011\"],\"levels_cleared\":1,\"mine_dmg\":8,\"mine_hits\":6,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":21,\"attack\":22,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[\"blood_honey\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":0,\"candidates\":[{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":17,\"heal_gained\":5,\"hp_after\":21,\"hp_before\":16,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blood_honey\",\"feat_name\":\"血蜜\",\"hp_after\":21,\"hp_before\":21,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":28,\"route\":\"min\",\"seal_hit\":79,\"seal_hit_gain\":0,\"seal_miss\":6,\"seal_miss_penalty\":16,\"seal_placed\":89,\"seed\":73011,\"shop_buys\":2,\"shop_gp_seq\":[17],\"shop_mode\":\"pool\",\"stopped_at\":2,\"taken\":47,\"visited_count\":9,\"wallet_count\":10}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":5,\"choices\":6,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,31],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":23,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#73012\",\"1#73012\",\"1#73012\",\"1#73012\",\"1#73012\",\"1#73012\",\"1#73012\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":8,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":21},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":21,\"route\":\"min\",\"seal_hit\":57,\"seal_hit_gain\":-348,\"seal_miss\":10,\"seal_miss_penalty\":4,\"seal_placed\":70,\"seed\":73012,\"shop_buys\":1,\"shop_gp_seq\":[10],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":39,\"visited_count\":7,\"wallet_count\":7}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":1,\"choices\":0,\"cleared\":false,\"encounter_max_hp_seq\":[26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":4,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#73013\"],\"levels_cleared\":0,\"mine_dmg\":14,\"mine_hits\":4,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":2,\"route\":\"min\",\"seal_hit\":7,\"seal_hit_gain\":114,\"seal_miss\":2,\"seal_miss_penalty\":14,\"seal_placed\":9,\"seed\":73013,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":18,\"visited_count\":1,\"wallet_count\":2}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":2,\"choices\":1,\"cleared\":false,\"encounter_max_hp_seq\":[26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":4,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#73014\",\"1#73014\"],\"levels_cleared\":0,\"mine_dmg\":22,\"mine_hits\":4,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":3,\"route\":\"min\",\"seal_hit\":10,\"seal_hit_gain\":240,\"seal_miss\":3,\"seal_miss_penalty\":0,\"seal_placed\":15,\"seed\":73014,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":26,\"visited_count\":2,\"wallet_count\":3}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":4,\"choices\":3,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":19,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#73015\",\"1#73015\",\"1#73015\",\"1#73015\"],\"levels_cleared\":0,\"mine_dmg\":8,\"mine_hits\":8,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":8,\"route\":\"min\",\"seal_hit\":24,\"seal_hit_gain\":127,\"seal_miss\":6,\"seal_miss_penalty\":12,\"seal_placed\":32,\"seed\":73015,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":27,\"visited_count\":4,\"wallet_count\":5}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":3,\"choices\":2,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":14,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#73016\",\"1#73016\",\"1#73016\"],\"levels_cleared\":0,\"mine_dmg\":14,\"mine_hits\":5,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":10,\"route\":\"min\",\"seal_hit\":20,\"seal_hit_gain\":86,\"seal_miss\":4,\"seal_miss_penalty\":-2,\"seal_placed\":26,\"seed\":73016,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":28,\"visited_count\":3,\"wallet_count\":4}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":2,\"choices\":1,\"cleared\":false,\"encounter_max_hp_seq\":[26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":5,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#73017\",\"1#73017\"],\"levels_cleared\":0,\"mine_dmg\":16,\"mine_hits\":3,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":4,\"route\":\"min\",\"seal_hit\":10,\"seal_hit_gain\":320,\"seal_miss\":2,\"seal_miss_penalty\":8,\"seal_placed\":14,\"seed\":73017,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":21,\"visited_count\":2,\"wallet_count\":3}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":4,\"choices\":3,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":13,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#73018\",\"1#73018\",\"1#73018\",\"1#73018\"],\"levels_cleared\":0,\"mine_dmg\":0,\"mine_hits\":3,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":10,\"route\":\"min\",\"seal_hit\":24,\"seal_hit_gain\":509,\"seal_miss\":5,\"seal_miss_penalty\":20,\"seal_placed\":33,\"seed\":73018,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":13,\"visited_count\":4,\"wallet_count\":5}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":4,\"choices\":3,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":17,\"hp_pct\":0,\"keystone_total\":0,\"level_maps\":[\"1#73019\",\"1#73019\",\"1#73019\",\"1#73019\"],\"levels_cleared\":0,\"mine_dmg\":14,\"mine_hits\":2,\"policy\":\"normal\",\"pool_stock\":{\"ap\":3,\"armor\":22,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[],\"result\":\"failed\",\"reward_events\":[],\"rounds\":8,\"route\":\"min\",\"seal_hit\":26,\"seal_hit_gain\":387,\"seal_miss\":3,\"seal_miss_penalty\":6,\"seal_placed\":35,\"seed\":73019,\"shop_buys\":0,\"shop_gp_seq\":[],\"shop_mode\":\"pool\",\"stopped_at\":1,\"taken\":31,\"visited_count\":4,\"wallet_count\":5}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":17,\"choices\":19,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26,26,26,36,36,36,36,36,36],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":131,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#73020\",\"1#73020\",\"1#73020\",\"1#73020\",\"1#73020\",\"1#73020\",\"1#73020\",\"2#73020\",\"2#73020\",\"2#73020\",\"2#73020\",\"2#73020\",\"2#73020\",\"2#73020\",\"2#73020\",\"2#73020\",\"2#73020\",\"3#73020\",\"3#73020\",\"3#73020\",\"3#73020\",\"3#73020\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":20,\"attack\":20,\"feat_ticket\":0,\"heal\":3,\"max_hp\":20},\"refreshes\":0,\"relics\":[\"executioner\",\"field_dressing\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":0,\"candidates\":[{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"chain_traps\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":30,\"heal_gained\":5,\"hp_after\":21,\"hp_before\":16,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"executioner\",\"feat_name\":\"处刑台\",\"hp_after\":21,\"hp_before\":21,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":10,\"candidates\":[{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":19,\"heal_gained\":7,\"hp_after\":35,\"hp_before\":28,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"field_dressing\",\"feat_name\":\"战地包扎\",\"hp_after\":35,\"hp_before\":35,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":79,\"route\":\"min\",\"seal_hit\":263,\"seal_hit_gain\":1207,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":278,\"seed\":73020,\"shop_buys\":7,\"shop_gp_seq\":[43,60],\"shop_mode\":\"pool\",\"stopped_at\":3,\"taken\":131,\"visited_count\":22,\"wallet_count\":25}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":16,\"choices\":18,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,31,31,31,31,31,31,36,36,36,36,36],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":141,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#73021\",\"1#73021\",\"1#73021\",\"1#73021\",\"1#73021\",\"1#73021\",\"1#73021\",\"2#73021\",\"2#73021\",\"2#73021\",\"2#73021\",\"2#73021\",\"2#73021\",\"2#73021\",\"2#73021\",\"2#73021\",\"2#73021\",\"3#73021\",\"3#73021\",\"3#73021\",\"3#73021\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":20,\"attack\":21,\"feat_ticket\":0,\"heal\":3,\"max_hp\":20},\"refreshes\":2,\"relics\":[\"skip_mining\",\"venom_edge\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":5,\"candidates\":[{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"skip_mining\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":24,\"heal_gained\":1,\"hp_after\":31,\"hp_before\":30,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"skip_mining\",\"feat_name\":\"跳格采掘\",\"hp_after\":31,\"hp_before\":31,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":10,\"candidates\":[{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"venom_edge\",\"kind\":\"relic\",\"tier\":\"rare\"}],\"gp\":40,\"heal_gained\":6,\"hp_after\":36,\"hp_before\":30,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"venom_edge\",\"feat_name\":\"淬毒刃纹\",\"hp_after\":36,\"hp_before\":36,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":83,\"route\":\"min\",\"seal_hit\":291,\"seal_hit_gain\":2396,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":316,\"seed\":73021,\"shop_buys\":6,\"shop_gp_seq\":[22,79],\"shop_mode\":\"pool\",\"stopped_at\":3,\"taken\":141,\"visited_count\":21,\"wallet_count\":23}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":19,\"choices\":21,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,31,31,31,31,31,31,41,41,41,41,41,41,41,41],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":141,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#73022\",\"1#73022\",\"1#73022\",\"1#73022\",\"1#73022\",\"1#73022\",\"1#73022\",\"2#73022\",\"2#73022\",\"2#73022\",\"2#73022\",\"2#73022\",\"2#73022\",\"2#73022\",\"2#73022\",\"2#73022\",\"2#73022\",\"3#73022\",\"3#73022\",\"3#73022\",\"3#73022\",\"3#73022\",\"3#73022\",\"3#73022\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":19,\"attack\":22,\"feat_ticket\":0,\"heal\":3,\"max_hp\":20},\"refreshes\":2,\"relics\":[\"breaker_strike\",\"trap_instinct\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":5,\"candidates\":[{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"breaker_strike\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":23,\"heal_gained\":6,\"hp_after\":23,\"hp_before\":17,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"breaker_strike\",\"feat_name\":\"破封一击\",\"hp_after\":23,\"hp_before\":23,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":15,\"candidates\":[{\"feat_id\":\"trap_instinct\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":26,\"heal_gained\":8,\"hp_after\":39,\"hp_before\":31,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"trap_instinct\",\"feat_name\":\"陷阱本能\",\"hp_after\":39,\"hp_before\":39,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":101,\"route\":\"min\",\"seal_hit\":359,\"seal_hit_gain\":1765,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":374,\"seed\":73022,\"shop_buys\":6,\"shop_gp_seq\":[17,63],\"shop_mode\":\"pool\",\"stopped_at\":3,\"taken\":141,\"visited_count\":24,\"wallet_count\":26}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":19,\"choices\":21,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26,26,26,36,36,36,36,36,36,36,36],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":152,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#73023\",\"1#73023\",\"1#73023\",\"1#73023\",\"1#73023\",\"1#73023\",\"1#73023\",\"2#73023\",\"2#73023\",\"2#73023\",\"2#73023\",\"2#73023\",\"2#73023\",\"2#73023\",\"2#73023\",\"2#73023\",\"2#73023\",\"3#73023\",\"3#73023\",\"3#73023\",\"3#73023\",\"3#73023\",\"3#73023\",\"3#73023\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":20,\"attack\":19,\"feat_ticket\":0,\"heal\":3,\"max_hp\":21},\"refreshes\":0,\"relics\":[\"map_exchange\",\"perfect_insurance\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":0,\"candidates\":[{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"map_exchange\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":21,\"heal_gained\":5,\"hp_after\":21,\"hp_before\":16,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"map_exchange\",\"feat_name\":\"弃图兑换\",\"hp_after\":21,\"hp_before\":21,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":10,\"candidates\":[{\"feat_id\":\"seal_echo\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"perfect_insurance\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":29,\"heal_gained\":7,\"hp_after\":35,\"hp_before\":28,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"perfect_insurance\",\"feat_name\":\"完美保险\",\"hp_after\":35,\"hp_before\":35,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":87,\"route\":\"min\",\"seal_hit\":285,\"seal_hit_gain\":1841,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":308,\"seed\":73023,\"shop_buys\":7,\"shop_gp_seq\":[30,71],\"shop_mode\":\"pool\",\"stopped_at\":3,\"taken\":152,\"visited_count\":24,\"wallet_count\":27}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":16,\"choices\":19,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,36,36,36,36,36,46,46,46,46,46,46],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":100,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#73024\",\"1#73024\",\"1#73024\",\"1#73024\",\"1#73024\",\"1#73024\",\"1#73024\",\"2#73024\",\"2#73024\",\"2#73024\",\"2#73024\",\"2#73024\",\"2#73024\",\"2#73024\",\"2#73024\",\"2#73024\",\"2#73024\",\"3#73024\",\"3#73024\",\"3#73024\",\"3#73024\",\"3#73024\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":21,\"attack\":20,\"feat_ticket\":0,\"heal\":3,\"max_hp\":19},\"refreshes\":2,\"relics\":[\"blood_honey\",\"seal_burst\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":10,\"candidates\":[{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":9,\"heal_gained\":7,\"hp_after\":36,\"hp_before\":29,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blood_honey\",\"feat_name\":\"血蜜\",\"hp_after\":36,\"hp_before\":36,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":20,\"candidates\":[{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_burst\",\"kind\":\"relic\",\"tier\":\"rare\"}],\"gp\":11,\"heal_gained\":9,\"hp_after\":32,\"hp_before\":23,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"seal_burst\",\"feat_name\":\"封印爆裂\",\"hp_after\":32,\"hp_before\":32,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":65,\"route\":\"min\",\"seal_hit\":218,\"seal_hit_gain\":416,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":241,\"seed\":73024,\"shop_buys\":7,\"shop_gp_seq\":[25,55],\"shop_mode\":\"pool\",\"stopped_at\":3,\"taken\":100,\"visited_count\":22,\"wallet_count\":24}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":19,\"choices\":28,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,36,36,36,36,36,36,41,41,41,41,41,41,41,51],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":157,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#73025\",\"1#73025\",\"1#73025\",\"1#73025\",\"1#73025\",\"1#73025\",\"1#73025\",\"2#73025\",\"2#73025\",\"2#73025\",\"2#73025\",\"2#73025\",\"2#73025\",\"2#73025\",\"2#73025\",\"2#73025\",\"2#73025\",\"3#73025\",\"3#73025\",\"3#73025\",\"3#73025\",\"3#73025\",\"3#73025\",\"3#73025\",\"3#73025\",\"3#73025\",\"3#73025\",\"3#73025\",\"3#73025\",\"3#73025\",\"3#73025\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":19,\"attack\":19,\"feat_ticket\":0,\"heal\":3,\"max_hp\":17},\"refreshes\":3,\"relics\":[\"last_stand\",\"seal_stake\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":10,\"candidates\":[{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"last_stand\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":26,\"heal_gained\":7,\"hp_after\":30,\"hp_before\":23,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"last_stand\",\"feat_name\":\"背水\",\"hp_after\":30,\"hp_before\":30,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":15,\"candidates\":[{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":44,\"heal_gained\":8,\"hp_after\":38,\"hp_before\":30,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"seal_stake\",\"feat_name\":\"封印桩\",\"hp_after\":38,\"hp_before\":38,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":118,\"route\":\"min\",\"seal_hit\":367,\"seal_hit_gain\":1643,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":385,\"seed\":73025,\"shop_buys\":12,\"shop_gp_seq\":[33,72,116],\"shop_mode\":\"pool\",\"stopped_at\":0,\"taken\":157,\"visited_count\":31,\"wallet_count\":32}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":15,\"choices\":17,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,36,36,36,36,36,36,36,36,36,36],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":140,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#73026\",\"1#73026\",\"1#73026\",\"1#73026\",\"1#73026\",\"1#73026\",\"1#73026\",\"2#73026\",\"2#73026\",\"2#73026\",\"2#73026\",\"2#73026\",\"2#73026\",\"2#73026\",\"2#73026\",\"2#73026\",\"2#73026\",\"3#73026\",\"3#73026\",\"3#73026\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":19,\"attack\":21,\"feat_ticket\":0,\"heal\":3,\"max_hp\":20},\"refreshes\":1,\"relics\":[\"geologist\",\"seal_echo\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":10,\"candidates\":[{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":19,\"heal_gained\":7,\"hp_after\":31,\"hp_before\":24,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"geologist\",\"feat_name\":\"地质学家\",\"hp_after\":31,\"hp_before\":31,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":10,\"candidates\":[{\"feat_id\":\"seal_echo\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"trap_instinct\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":29,\"heal_gained\":7,\"hp_after\":23,\"hp_before\":16,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"seal_echo\",\"feat_name\":\"封印回响\",\"hp_after\":23,\"hp_before\":23,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":81,\"route\":\"min\",\"seal_hit\":267,\"seal_hit_gain\":1267,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":280,\"seed\":73026,\"shop_buys\":7,\"shop_gp_seq\":[29,72],\"shop_mode\":\"pool\",\"stopped_at\":3,\"taken\":140,\"visited_count\":20,\"wallet_count\":23}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":15,\"choices\":18,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26,26,36,36,36,36,36],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":126,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#73027\",\"1#73027\",\"1#73027\",\"1#73027\",\"1#73027\",\"1#73027\",\"1#73027\",\"2#73027\",\"2#73027\",\"2#73027\",\"2#73027\",\"2#73027\",\"2#73027\",\"2#73027\",\"2#73027\",\"2#73027\",\"2#73027\",\"3#73027\",\"3#73027\",\"3#73027\",\"3#73027\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":19,\"attack\":22,\"feat_ticket\":0,\"heal\":3,\"max_hp\":20},\"refreshes\":2,\"relics\":[\"last_drop\",\"executioner\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":0,\"candidates\":[{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":12,\"heal_gained\":1,\"hp_after\":26,\"hp_before\":25,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"last_drop\",\"feat_name\":\"一滴不剩\",\"hp_after\":26,\"hp_before\":26,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":10,\"candidates\":[{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":60,\"heal_gained\":7,\"hp_after\":33,\"hp_before\":26,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"executioner\",\"feat_name\":\"处刑台\",\"hp_after\":33,\"hp_before\":33,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":78,\"route\":\"min\",\"seal_hit\":276,\"seal_hit_gain\":954,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":289,\"seed\":73027,\"shop_buys\":6,\"shop_gp_seq\":[18,82],\"shop_mode\":\"pool\",\"stopped_at\":3,\"taken\":126,\"visited_count\":21,\"wallet_count\":22}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":15,\"choices\":17,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,31,31,31,31,31,31,31,31,31,31],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":127,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#73028\",\"1#73028\",\"1#73028\",\"1#73028\",\"1#73028\",\"1#73028\",\"1#73028\",\"2#73028\",\"2#73028\",\"2#73028\",\"2#73028\",\"2#73028\",\"2#73028\",\"2#73028\",\"2#73028\",\"2#73028\",\"2#73028\",\"3#73028\",\"3#73028\",\"3#73028\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":18,\"attack\":21,\"feat_ticket\":0,\"heal\":3,\"max_hp\":21},\"refreshes\":0,\"relics\":[\"geologist\",\"market_close\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":5,\"candidates\":[{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":17,\"heal_gained\":5,\"hp_after\":31,\"hp_before\":26,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"geologist\",\"feat_name\":\"地质学家\",\"hp_after\":31,\"hp_before\":31,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":5,\"candidates\":[{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":30,\"heal_gained\":6,\"hp_after\":28,\"hp_before\":22,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"market_close\",\"feat_name\":\"收盘\",\"hp_after\":28,\"hp_before\":28,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":87,\"route\":\"min\",\"seal_hit\":225,\"seal_hit_gain\":1838,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":244,\"seed\":73028,\"shop_buys\":7,\"shop_gp_seq\":[27,67],\"shop_mode\":\"pool\",\"stopped_at\":3,\"taken\":127,\"visited_count\":20,\"wallet_count\":23}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":15,\"choices\":17,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,31,31,31,31,31,31,31,31,31,31],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":107,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#73029\",\"1#73029\",\"1#73029\",\"1#73029\",\"1#73029\",\"1#73029\",\"1#73029\",\"2#73029\",\"2#73029\",\"2#73029\",\"2#73029\",\"2#73029\",\"2#73029\",\"2#73029\",\"2#73029\",\"2#73029\",\"2#73029\",\"3#73029\",\"3#73029\",\"3#73029\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":18,\"attack\":21,\"feat_ticket\":0,\"heal\":3,\"max_hp\":21},\"refreshes\":1,\"relics\":[\"breaker_strike\",\"stone_skin\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":5,\"candidates\":[{\"feat_id\":\"breaker_strike\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":28,\"heal_gained\":6,\"hp_after\":12,\"hp_before\":6,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"breaker_strike\",\"feat_name\":\"破封一击\",\"hp_after\":12,\"hp_before\":12,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":5,\"candidates\":[{\"feat_id\":\"stone_skin\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":16,\"heal_gained\":6,\"hp_after\":25,\"hp_before\":19,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"stone_skin\",\"feat_name\":\"石肤\",\"hp_after\":25,\"hp_before\":25,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":66,\"route\":\"min\",\"seal_hit\":233,\"seal_hit_gain\":1341,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":248,\"seed\":73029,\"shop_buys\":7,\"shop_gp_seq\":[40,58],\"shop_mode\":\"pool\",\"stopped_at\":3,\"taken\":107,\"visited_count\":20,\"wallet_count\":23}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":16,\"choices\":18,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,31,31,31,31,31,31,51,51,51,51,51],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":156,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#73030\",\"1#73030\",\"1#73030\",\"1#73030\",\"1#73030\",\"1#73030\",\"1#73030\",\"2#73030\",\"2#73030\",\"2#73030\",\"2#73030\",\"2#73030\",\"2#73030\",\"2#73030\",\"2#73030\",\"2#73030\",\"2#73030\",\"3#73030\",\"3#73030\",\"3#73030\",\"3#73030\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":20,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":17},\"refreshes\":2,\"relics\":[\"blood_insight\",\"seal_smith\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":5,\"candidates\":[{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":37,\"heal_gained\":6,\"hp_after\":19,\"hp_before\":13,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blood_insight\",\"feat_name\":\"见血开窍\",\"hp_after\":19,\"hp_before\":19,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":25,\"candidates\":[{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"seal_smith\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":57,\"heal_gained\":10,\"hp_after\":39,\"hp_before\":29,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"seal_smith\",\"feat_name\":\"封印匠\",\"hp_after\":39,\"hp_before\":39,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":93,\"route\":\"min\",\"seal_hit\":300,\"seal_hit_gain\":1749,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":313,\"seed\":73030,\"shop_buys\":7,\"shop_gp_seq\":[38,89],\"shop_mode\":\"pool\",\"stopped_at\":3,\"taken\":156,\"visited_count\":21,\"wallet_count\":24}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":15,\"choices\":18,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,31,31,31,31,31,41,41,41,41,41],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":138,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#73031\",\"1#73031\",\"1#73031\",\"1#73031\",\"1#73031\",\"1#73031\",\"1#73031\",\"2#73031\",\"2#73031\",\"2#73031\",\"2#73031\",\"2#73031\",\"2#73031\",\"2#73031\",\"2#73031\",\"2#73031\",\"2#73031\",\"3#73031\",\"3#73031\",\"3#73031\",\"3#73031\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":19,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":19},\"refreshes\":1,\"relics\":[\"perfect_insurance\",\"market_close\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":5,\"candidates\":[{\"feat_id\":\"perfect_insurance\",\"kind\":\"relic\",\"tier\":\"rare\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"executioner\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":31,\"heal_gained\":6,\"hp_after\":31,\"hp_before\":25,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"perfect_insurance\",\"feat_name\":\"完美保险\",\"hp_after\":31,\"hp_before\":31,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":15,\"candidates\":[{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":38,\"heal_gained\":8,\"hp_after\":34,\"hp_before\":26,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"market_close\",\"feat_name\":\"收盘\",\"hp_after\":34,\"hp_before\":34,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":85,\"route\":\"min\",\"seal_hit\":281,\"seal_hit_gain\":635,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":295,\"seed\":73031,\"shop_buys\":6,\"shop_gp_seq\":[21,66],\"shop_mode\":\"pool\",\"stopped_at\":3,\"taken\":138,\"visited_count\":21,\"wallet_count\":22}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":17,\"choices\":19,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,31,31,31,31,31,31,36,36,36,36,36,36],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":124,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#73032\",\"1#73032\",\"1#73032\",\"1#73032\",\"1#73032\",\"1#73032\",\"1#73032\",\"2#73032\",\"2#73032\",\"2#73032\",\"2#73032\",\"2#73032\",\"2#73032\",\"2#73032\",\"2#73032\",\"2#73032\",\"2#73032\",\"3#73032\",\"3#73032\",\"3#73032\",\"3#73032\",\"3#73032\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":19,\"attack\":21,\"feat_ticket\":0,\"heal\":3,\"max_hp\":20},\"refreshes\":0,\"relics\":[\"execution_scholar\",\"venom_edge\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":5,\"candidates\":[{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":21,\"heal_gained\":6,\"hp_after\":23,\"hp_before\":17,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"execution_scholar\",\"feat_name\":\"斩首学者\",\"hp_after\":23,\"hp_before\":23,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":10,\"candidates\":[{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"venom_edge\",\"kind\":\"relic\",\"tier\":\"rare\"}],\"gp\":49,\"heal_gained\":7,\"hp_after\":31,\"hp_before\":24,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"venom_edge\",\"feat_name\":\"淬毒刃纹\",\"hp_after\":31,\"hp_before\":31,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}}],\"rounds\":82,\"route\":\"min\",\"seal_hit\":284,\"seal_hit_gain\":931,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":309,\"seed\":73032,\"shop_buys\":7,\"shop_gp_seq\":[28,90],\"shop_mode\":\"pool\",\"stopped_at\":3,\"taken\":124,\"visited_count\":22,\"wallet_count\":25}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":19,\"choices\":23,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,41,41,41,41,41,41,41,41,41,41,41,41,41,41],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":164,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#73033\",\"1#73033\",\"1#73033\",\"1#73033\",\"1#73033\",\"1#73033\",\"1#73033\",\"2#73033\",\"2#73033\",\"2#73033\",\"2#73033\",\"2#73033\",\"2#73033\",\"2#73033\",\"2#73033\",\"2#73033\",\"2#73033\",\"3#73033\",\"3#73033\",\"3#73033\",\"3#73033\",\"3#73033\",\"3#73033\",\"3#73033\",\"3#73033\",\"3#73033\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":18,\"attack\":22,\"feat_ticket\":0,\"heal\":3,\"max_hp\":20},\"refreshes\":1,\"relics\":[\"blood_insight\",\"last_drop\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":15,\"candidates\":[{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":24,\"heal_gained\":8,\"hp_after\":28,\"hp_before\":20,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blood_insight\",\"feat_name\":\"见血开窍\",\"hp_after\":28,\"hp_before\":28,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":15,\"candidates\":[{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":30,\"heal_gained\":1,\"hp_after\":41,\"hp_before\":40,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"last_drop\",\"feat_name\":\"一滴不剩\",\"hp_after\":41,\"hp_before\":41,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":99,\"route\":\"min\",\"seal_hit\":336,\"seal_hit_gain\":1256,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":352,\"seed\":73033,\"shop_buys\":7,\"shop_gp_seq\":[35,66],\"shop_mode\":\"pool\",\"stopped_at\":3,\"taken\":164,\"visited_count\":26,\"wallet_count\":27}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":17,\"choices\":20,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,36,36,36,36,36,36,36,36,36,36,36,36],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":142,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#73034\",\"1#73034\",\"1#73034\",\"1#73034\",\"1#73034\",\"1#73034\",\"1#73034\",\"2#73034\",\"2#73034\",\"2#73034\",\"2#73034\",\"2#73034\",\"2#73034\",\"2#73034\",\"2#73034\",\"2#73034\",\"2#73034\",\"3#73034\",\"3#73034\",\"3#73034\",\"3#73034\",\"3#73034\",\"3#73034\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":18,\"attack\":21,\"feat_ticket\":0,\"heal\":3,\"max_hp\":21},\"refreshes\":1,\"relics\":[\"last_drop\",\"field_dressing\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":10,\"candidates\":[{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":30,\"heal_gained\":7,\"hp_after\":31,\"hp_before\":24,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"last_drop\",\"feat_name\":\"一滴不剩\",\"hp_after\":31,\"hp_before\":31,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":10,\"candidates\":[{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_insight\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"market_close\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":38,\"heal_gained\":2,\"hp_after\":36,\"hp_before\":34,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"field_dressing\",\"feat_name\":\"战地包扎\",\"hp_after\":36,\"hp_before\":36,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":83,\"route\":\"min\",\"seal_hit\":286,\"seal_hit_gain\":1785,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":302,\"seed\":73034,\"shop_buys\":7,\"shop_gp_seq\":[38,75],\"shop_mode\":\"pool\",\"stopped_at\":3,\"taken\":142,\"visited_count\":23,\"wallet_count\":25}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":17,\"choices\":21,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,31,31,31,31,31,31,31,31,31,31,31,31],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":124,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#73035\",\"1#73035\",\"1#73035\",\"1#73035\",\"1#73035\",\"1#73035\",\"1#73035\",\"2#73035\",\"2#73035\",\"2#73035\",\"2#73035\",\"2#73035\",\"2#73035\",\"2#73035\",\"2#73035\",\"2#73035\",\"2#73035\",\"3#73035\",\"3#73035\",\"3#73035\",\"3#73035\",\"3#73035\",\"3#73035\",\"3#73035\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":21,\"attack\":18,\"feat_ticket\":0,\"heal\":3,\"max_hp\":21},\"refreshes\":0,\"relics\":[\"hard_hat\",\"blast_accounting\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":5,\"candidates\":[{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"hard_hat\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"seal_stake\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":17,\"heal_gained\":6,\"hp_after\":20,\"hp_before\":14,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"hard_hat\",\"feat_name\":\"安全帽\",\"hp_after\":20,\"hp_before\":20,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":5,\"candidates\":[{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"geologist\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":17,\"heal_gained\":6,\"hp_after\":30,\"hp_before\":24,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blast_accounting\",\"feat_name\":\"爆破会计\",\"hp_after\":30,\"hp_before\":30,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":80,\"route\":\"min\",\"seal_hit\":273,\"seal_hit_gain\":304,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":284,\"seed\":73035,\"shop_buys\":7,\"shop_gp_seq\":[34,56],\"shop_mode\":\"pool\",\"stopped_at\":3,\"taken\":124,\"visited_count\":24,\"wallet_count\":25}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":18,\"choices\":28,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,36,36,36,36,36,36,51,51,51,51,51,51,56],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":131,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#73036\",\"1#73036\",\"1#73036\",\"1#73036\",\"1#73036\",\"1#73036\",\"1#73036\",\"2#73036\",\"2#73036\",\"2#73036\",\"2#73036\",\"2#73036\",\"2#73036\",\"2#73036\",\"2#73036\",\"2#73036\",\"2#73036\",\"3#73036\",\"3#73036\",\"3#73036\",\"3#73036\",\"3#73036\",\"3#73036\",\"3#73036\",\"3#73036\",\"3#73036\",\"3#73036\",\"3#73036\",\"3#73036\",\"3#73036\",\"3#73036\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":19,\"attack\":19,\"feat_ticket\":0,\"heal\":3,\"max_hp\":17},\"refreshes\":0,\"relics\":[\"frontier_miner\",\"blood_honey\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":10,\"candidates\":[{\"feat_id\":\"frontier_miner\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":17,\"heal_gained\":7,\"hp_after\":35,\"hp_before\":28,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"frontier_miner\",\"feat_name\":\"边陲矿工\",\"hp_after\":35,\"hp_before\":35,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":25,\"candidates\":[{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"twin_pick\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":26,\"heal_gained\":10,\"hp_after\":38,\"hp_before\":28,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blood_honey\",\"feat_name\":\"血蜜\",\"hp_after\":38,\"hp_before\":38,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":93,\"route\":\"min\",\"seal_hit\":302,\"seal_hit_gain\":720,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":328,\"seed\":73036,\"shop_buys\":12,\"shop_gp_seq\":[26,106,76],\"shop_mode\":\"pool\",\"stopped_at\":0,\"taken\":131,\"visited_count\":31,\"wallet_count\":31}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":16,\"choices\":18,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":108,\"hp_pct\":0,\"keystone_total\":6,\"level_maps\":[\"1#73037\",\"1#73037\",\"1#73037\",\"1#73037\",\"1#73037\",\"1#73037\",\"1#73037\",\"2#73037\",\"2#73037\",\"2#73037\",\"2#73037\",\"2#73037\",\"2#73037\",\"2#73037\",\"2#73037\",\"2#73037\",\"2#73037\",\"3#73037\",\"3#73037\",\"3#73037\",\"3#73037\"],\"levels_cleared\":2,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":17,\"attack\":21,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":1,\"relics\":[\"blood_honey\",\"stone_skin\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":0,\"candidates\":[{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":14,\"heal_gained\":5,\"hp_after\":19,\"hp_before\":14,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"blood_honey\",\"feat_name\":\"血蜜\",\"hp_after\":19,\"hp_before\":19,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":0,\"candidates\":[{\"feat_id\":\"execution_scholar\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"stone_skin\",\"kind\":\"relic\",\"tier\":\"excellent\"}],\"gp\":51,\"heal_gained\":5,\"hp_after\":21,\"hp_before\":16,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"stone_skin\",\"feat_name\":\"石肤\",\"hp_after\":21,\"hp_before\":21,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":78,\"route\":\"min\",\"seal_hit\":250,\"seal_hit_gain\":1410,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":265,\"seed\":73037,\"shop_buys\":7,\"shop_gp_seq\":[27,73],\"shop_mode\":\"pool\",\"stopped_at\":3,\"taken\":108,\"visited_count\":21,\"wallet_count\":24}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":20,\"choices\":28,\"cleared\":true,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26,26,26,41,41,41,41,41,41,41,41,41],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":177,\"hp_pct\":0,\"keystone_total\":9,\"level_maps\":[\"1#73038\",\"1#73038\",\"1#73038\",\"1#73038\",\"1#73038\",\"1#73038\",\"1#73038\",\"2#73038\",\"2#73038\",\"2#73038\",\"2#73038\",\"2#73038\",\"2#73038\",\"2#73038\",\"2#73038\",\"2#73038\",\"2#73038\",\"3#73038\",\"3#73038\",\"3#73038\",\"3#73038\",\"3#73038\",\"3#73038\",\"3#73038\",\"3#73038\",\"3#73038\",\"3#73038\",\"3#73038\",\"3#73038\",\"3#73038\",\"3#73038\"],\"levels_cleared\":3,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":19,\"attack\":18,\"feat_ticket\":0,\"heal\":3,\"max_hp\":19},\"refreshes\":2,\"relics\":[\"trap_burst\",\"field_dressing\"],\"result\":\"completed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":0,\"candidates\":[{\"feat_id\":\"trap_instinct\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"seal_echo\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"trap_burst\",\"kind\":\"relic\",\"tier\":\"rare\"}],\"gp\":27,\"heal_gained\":5,\"hp_after\":25,\"hp_before\":20,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"trap_burst\",\"feat_name\":\"陷阱爆发\",\"hp_after\":25,\"hp_before\":25,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"rare\",\"value\":0}},{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":15,\"candidates\":[{\"feat_id\":\"field_dressing\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blood_honey\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"warning_beacon\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":32,\"heal_gained\":8,\"hp_after\":37,\"hp_before\":29,\"keystone_gained\":3,\"keystone_total\":6,\"level_index\":2}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"field_dressing\",\"feat_name\":\"战地包扎\",\"hp_after\":37,\"hp_before\":37,\"level_index\":2,\"picked\":\"relic\",\"tier\":\"common\",\"value\":0}}],\"rounds\":111,\"route\":\"min\",\"seal_hit\":364,\"seal_hit_gain\":1711,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":388,\"seed\":73038,\"shop_buys\":11,\"shop_gp_seq\":[26,70,106],\"shop_mode\":\"pool\",\"stopped_at\":0,\"taken\":177,\"visited_count\":31,\"wallet_count\":32}", + "RUNSUM {\"atk_pct\":0,\"attune\":\"off\",\"battles\":9,\"choices\":8,\"cleared\":false,\"encounter_max_hp_seq\":[26,26,26,26,26,26,26,26,26],\"equipped\":[],\"feat_mode\":\"off\",\"hero_dmg\":58,\"hp_pct\":0,\"keystone_total\":3,\"level_maps\":[\"1#73039\",\"1#73039\",\"1#73039\",\"1#73039\",\"1#73039\",\"1#73039\",\"1#73039\",\"2#73039\",\"2#73039\",\"2#73039\"],\"levels_cleared\":1,\"mine_dmg\":0,\"mine_hits\":0,\"policy\":\"perfect\",\"pool_stock\":{\"ap\":3,\"armor\":20,\"attack\":23,\"feat_ticket\":0,\"heal\":3,\"max_hp\":22},\"refreshes\":0,\"relics\":[\"breaker_strike\"],\"result\":\"failed\",\"reward_events\":[{\"curio_pick\":{\"aux_cleared\":0,\"bonus_max_hp\":0,\"candidates\":[{\"feat_id\":\"breaker_strike\",\"kind\":\"relic\",\"tier\":\"excellent\"},{\"feat_id\":\"last_drop\",\"kind\":\"relic\",\"tier\":\"common\"},{\"feat_id\":\"blast_accounting\",\"kind\":\"relic\",\"tier\":\"common\"}],\"gp\":21,\"heal_gained\":5,\"hp_after\":21,\"hp_before\":16,\"keystone_gained\":3,\"keystone_total\":3,\"level_index\":1}},{\"curio_resolved\":{\"feat_archetype\":\"\",\"feat_id\":\"breaker_strike\",\"feat_name\":\"破封一击\",\"hp_after\":21,\"hp_before\":21,\"level_index\":1,\"picked\":\"relic\",\"tier\":\"excellent\",\"value\":0}}],\"rounds\":43,\"route\":\"min\",\"seal_hit\":139,\"seal_hit_gain\":2048,\"seal_miss\":0,\"seal_miss_penalty\":0,\"seal_placed\":149,\"seed\":73039,\"shop_buys\":2,\"shop_gp_seq\":[23],\"shop_mode\":\"pool\",\"stopped_at\":2,\"taken\":58,\"visited_count\":10,\"wallet_count\":12}" ], "jsonl": { "normal": { - "sha256": "681084ee53aa14af51dfbf87f9462f8dbed5937e62da3f7002b00aea95fb8e6e", - "rows": 7499 + "sha256": "b39d5a5a659776e6e3a25a7a1f24af4d8a98b138b2c52cec846bfd7d394364fb", + "rows": 7124 }, "perfect": { - "sha256": "af09655e0d476892f2e19bba36cb9c9cb4a1d2f8df2e90dc4812fc426078db0c", - "rows": 45132 + "sha256": "7d16b4efb2bb48355a7919011b265de83ff907bd9c83818dc66e69108967d5a3", + "rows": 59435 } } } diff --git a/game/tests/golden/README.md b/game/tests/golden/README.md index a0f3b8d4..ebbb813c 100644 --- a/game/tests/golden/README.md +++ b/game/tests/golden/README.md @@ -21,3 +21,9 @@ - 2026-10-16(迭代 29 T7):**判有效**。三代换代面(棋盘三图 + 启动态菜单)实测 changed = 0.00% / 0.00% / 0.00% / 0.15%,均远低于 2% 阈值 ⇒ 基线留在位,不重制。 (本批零视觉改动,判定属常规复核。) +- 2026-10-25(迭代 37 卡① T3):**判失效 → 显式重制 + 复闸**。实测棋盘三图 + changed = 0.13% / 0.11% / 0.10%,差异像素全落 900×760 棋盘裁剪区内、裁剪区外 0, + 按加严口径不判有效;成因定位为 `2f3a87e9`(魔法道具 HUD)注入 `REWARD_BADGE` 预读格 + 字形——晚于 2026-10-08 建基提交,与本批零因果。呈报后用户裁「重制三张基线」, + 经 `tools/golden_baseline_refresh.sh` 换代;复闸 changed = 0.00% / 0.00% / 0.00%, + 启动态菜单 0.00%(未重制)。 diff --git a/game/tests/golden/board_L1_dungeon.png b/game/tests/golden/board_L1_dungeon.png index 795e7b93..834352fd 100644 Binary files a/game/tests/golden/board_L1_dungeon.png and b/game/tests/golden/board_L1_dungeon.png differ diff --git a/game/tests/golden/board_L2_underdark.png b/game/tests/golden/board_L2_underdark.png index 93e1ea64..2f87adc6 100644 Binary files a/game/tests/golden/board_L2_underdark.png and b/game/tests/golden/board_L2_underdark.png differ diff --git a/game/tests/golden/board_L3_deep_underdark.png b/game/tests/golden/board_L3_deep_underdark.png index f4b25ace..ab50d9bf 100644 Binary files a/game/tests/golden/board_L3_deep_underdark.png and b/game/tests/golden/board_L3_deep_underdark.png differ diff --git a/game/tests/run_all.gd b/game/tests/run_all.gd index eb1844cf..90ef459f 100644 --- a/game/tests/run_all.gd +++ b/game/tests/run_all.gd @@ -59,6 +59,7 @@ func _suites() -> Array: {"path": "res://tests/unit/core/curio_refresh_paid_test.gd", "expect": 0}, # 三选一刷新免费双源+GP 付费档(对齐商店 MC-1:价曲线/优先序/停点清零/钱包单口) {"path": "res://tests/unit/core/level_map_type_vis_test.gd", "expect": 0}, # 信息迷雾——类型可见性(层距 N+Boss/候选豁免+单圈+N 读表+续玩同值) {"path": "res://tests/unit/presentation/pacing_queue_test.gd", "expect": 0}, # AC⑤/T9——排空零分叉/零丢行/延迟双态 + {"path": "res://tests/unit/presentation/standee_fx_sweep_test.gd", "expect": 0}, # 呈现域缺陷扫掠(d2 蓄力档/d4 庆祝句柄/d5+d8 互斥/d12 站桩按 id 解析) {"path": "res://tests/unit/presentation/reward_overlay_test.gd", "expect": 0}, # 奖励节点 UI 直验(盲区补漏) {"path": "res://tests/unit/presentation/level_map_info_fog_test.gd", "expect": 0}, # 信息迷雾——呈现显隐/Boss 豁免/详情零显示/大图拟合 {"path": "res://tests/unit/presentation/forge_workbench_test.gd", "expect": 0}, # 工作间两区六路线 UI 直验(V13 自动化前置面) diff --git a/game/tests/system_test.gd b/game/tests/system_test.gd index 673c8dfb..47baa467 100644 --- a/game/tests/system_test.gd +++ b/game/tests/system_test.gd @@ -40,6 +40,7 @@ func _split_tail() -> void: await _smoke_scene_split() # 协程函数必须 await(无 await=发射后不管,首个 await 后即被 quit 截断) await _smoke_boot_chain() await _smoke_run_lifecycle() + await _smoke_sim_guards() await _smoke_lifecycle_matrix() else: check(false, "V2/V3 前置:autoload 链未就绪(ConfigDB/SaveManager)") @@ -267,6 +268,47 @@ func _smoke_run_lifecycle() -> void: DirAccess.remove_absolute(p) +# ---- d6/d7 批量模拟互斥守卫(wd6/wd7——白盒置态直调入口,以守卫早退的可见后果为断言面)---- +func _smoke_sim_guards() -> void: + print("--- 卡① T2:批量模拟互斥两守卫(d6/d7)---") + var main: Node = load("res://presentation/main.tscn").instantiate() + root.add_child(main) + await process_frame + var sm = main.get_node("/root/SaveManager") + for p in ["user://smoke_sim_guards.json", "user://smoke_sim_guards.json.tmp"]: + if FileAccess.file_exists(p): + DirAccess.remove_absolute(p) + sm.attach_path("user://smoke_sim_guards.json") + sm.mark_first_adventure_done() + # wd6:闲时态 + 模拟运行态 → 调试两层入口守卫早退(开始层不收起、无结算对象) + check(main.run_scope.settlement == null and not main.run_scope.is_adventure, + "前置:闲时会话(settlement 空 + is_adventure=false)") + main._start_overlay.visible = true + main._tune_sim._running = true + main._start_debug_levels() + check(main.run_scope.settlement == null and main._start_overlay.visible, + "模拟运行态下调试两层入口守卫早退(开始层不收起)〔wd6〕") + main._tune_sim._running = false + # wd7:真实冒险关间态(is_adventure=true、settlement==null)→ 模拟入口守卫早退 + main.debug_overlay.seed_edit.text = "42" + main._start_adventure_from_menu() + await process_frame + check(main.run_scope.is_adventure and main.run_scope.settlement == null, + "前置:真实冒险关间态(选路停点,非战斗)") + main._on_tune_simulate(3) + check(not main._tune_sim.is_running(), + "冒险关间态下批量模拟入口守卫早退(d7 补 is_adventure)〔wd7〕") + main._tune_sim._running = false # 红态清理(守卫缺失时 run 已启动) + main.run_scope.adventure.abandon() + for i in range(8): + await process_frame + main.queue_free() + await process_frame + for p in ["user://smoke_sim_guards.json", "user://smoke_sim_guards.json.tmp"]: + if FileAccess.file_exists(p): + DirAccess.remove_absolute(p) + + # 引用面快照(五承载物的会话相关面+恒活面——逐个调用现值回调,与 run_scope 成员并采) # (自 game/smoke/smoke_all.gd 剪切迁入——label 与 cond 逐字原样,断言迁移批 B G1) func _face_snapshot(main: Node) -> Dictionary: diff --git a/game/tests/unit/battle_settlement/damage_controller_test.gd b/game/tests/unit/battle_settlement/damage_controller_test.gd index 2aba0e3c..168ca0fb 100644 --- a/game/tests/unit/battle_settlement/damage_controller_test.gd +++ b/game/tests/unit/battle_settlement/damage_controller_test.gd @@ -29,6 +29,8 @@ func _init() -> void: _test_five_recipes_and_replay(db) _test_policy_immediate_mode(db) _test_source_death_void(db) + _test_identical_content_dual_enemy(db) + _test_target_id_payload(db) _test_requires_gate_and_zero(db) _test_hero_death_halt(db) _test_ac_merge(db) @@ -211,8 +213,70 @@ func _test_source_death_void(db) -> void: Kit.check("作废单零行零扣血", int(h.units.hero_unit().hp) == hero_before and _rows.is_empty()) +# ---- 同内容双敌:单据归属按实例身份判定(生产 handle_id 使内容全同不可达——夹具显式钉住语义面) ---- + +# 单位字典键集复刻(剥离 handle_id:生产键集含句柄,`==` 误命的可达条件被破坏,夹具刻意不携句柄) +func _unit_fixture() -> Dictionary: + return { + "enemy_id": "fixture_enemy", + "display_name": "哥布林", + "hp": 5, "max_hp": 5, "atk": 1, + "pool": [], "pending_skill": "", "roll_seq": 0, "basic_streak": 0, + "armor_type": "light", "armor_class": 0, "attack_type": "normal", + "alive": true, "is_hero": false, + } + + +func _test_identical_content_dual_enemy(db: ConfigDb) -> void: + var a := _unit_fixture() + var b := _unit_fixture() + Kit.check("前置:双敌字典内容全同且非同一实例", a == b and not is_same(a, b)) + var h = _harness(db) + if not h.spawn_ok: + return + var ticket: Dictionary = h.ctrl.open_ticket(DamageControllerScript.KIND_BOARD_FLAT_HERO, "mine_hit", + "同容双敌单", "棋盘兑付", 1, DamageControllerScript.TARGET_HERO, 1) + ticket["source_unit"] = a + var t: int = h.ctrl.submit(ticket) + h.units.unit_died.emit(b) # 真实死亡事件(单位自行宣布)——同容异实例 + h.ctrl.flush() + Kit.check("同内容异实例敌死亡不误废他人单据〔wd1〕", h.ctrl.outcome(t) == "redeemed") + var h2 = _harness(db) + if not h2.spawn_ok: + return + var ticket2: Dictionary = h2.ctrl.open_ticket(DamageControllerScript.KIND_BOARD_FLAT_HERO, "mine_hit", + "同容双敌单·自亡", "棋盘兑付", 1, DamageControllerScript.TARGET_HERO, 1) + ticket2["source_unit"] = a + var t2: int = h2.ctrl.submit(ticket2) + h2.units.unit_died.emit(a) + h2.ctrl.flush() + Kit.check("来源敌自身死亡单据作废(正向对照)〔wd1〕", h2.ctrl.outcome(t2) == "voided") + + # ---- requires 前置门:前置单未实际有效兑付 → 后继单静默作废;无存活敌 blast = 合法零兑付 ---- +# d12 产发端增键:目标身份随行/随载荷下发(同名敌按 id 精确解析的产发半) +func _test_target_id_payload(db: ConfigDb) -> void: + _rows = [] + var h = _harness(db) + if not h.spawn_ok: + Kit.check("前置:units.spawn(1,0) 成功(target_id 断言)", false) + return + var target = h.units.default_target() + var expect_id := String(target.get("enemy_id", "")) + Kit.check("前置:目标敌携非空 enemy_id", expect_id != "") + var ticket: Dictionary = h.ctrl.open_ticket(DamageControllerScript.KIND_HERO_ATTACK, "hero_attack", + "T6目标id", "战斗段", int(db.hero.atk), DamageControllerScript.TARGET_FRONT_ALIVE, 1) + var fx_payload: Dictionary = ticket.context.get("fx_attack_payload", {}) + Kit.check("attack_commit fx 载荷携 target_id〔wd12〕", + String(fx_payload.get("target_id", "")) == expect_id and String(fx_payload.get("target", "")) != "") + h.ctrl.submit(ticket) + h.ctrl.flush() + var row := _row_by_display_prefix("T6目标id") + Kit.check("伤害行携 target_id == 目标敌 enemy_id〔wd12〕", + not row.is_empty() and String(row.extra.get("target_id", "")) == expect_id) + + func _test_requires_gate_and_zero(db) -> void: _rows = [] var h = _harness(db) diff --git a/game/tests/unit/core/adventure_feats_test.gd b/game/tests/unit/core/adventure_feats_test.gd index 379390e1..7bc13ec9 100644 --- a/game/tests/unit/core/adventure_feats_test.gd +++ b/game/tests/unit/core/adventure_feats_test.gd @@ -215,7 +215,7 @@ func _smoke_adventure() -> void: var seen := {} for c in cands: # 身份键 = kind|tier|feat_id(TP0 v2.2 relic 按 feat_id 分列 - # 不参与同 kind 去重〔_token_identity relic: 前缀〕,同 tier 三枚不同流派件=三个不同选项合法态) + # 不参与同 kind 去重〔CurioDeck.token_identity 的 relic: 前缀〕,同 tier 三枚不同流派件=三个不同选项合法态) var ckey := "%s|%s|%s" % [String((c as Dictionary).get("kind", "")), String((c as Dictionary).get("tier", "")), String((c as Dictionary).get("feat_id", ""))] if seen.has(ckey): cand_ok = false diff --git a/game/tests/unit/core/board_gen_reward_test.gd b/game/tests/unit/core/board_gen_reward_test.gd index e26e23a6..270f6335 100644 --- a/game/tests/unit/core/board_gen_reward_test.gd +++ b/game/tests/unit/core/board_gen_reward_test.gd @@ -157,7 +157,7 @@ func _smoke_board_generator() -> void: if b1 == null: continue Kit.check("L%d clearable(安全格 8 邻连通)" % L, board_generator.is_clearable(b1)) - Kit.check("L%d legality 全过" % L, board_generator.legality(b1, row, _db)) + Kit.check("L%d legality 全过" % L, board_generator.legality(b1, row)) var inf := float(b1.metrics.get("inferability", 0.0)) var mode := String(b1.metrics.get("accept_mode", "")) print(" L%d 实测:inferability=%.3f / attempts_used=%d / accept_mode=%s / target=%.2f" % [ diff --git a/game/tests/unit/core/board_generator_test.gd b/game/tests/unit/core/board_generator_test.gd index d35262af..3e7cc062 100644 --- a/game/tests/unit/core/board_generator_test.gd +++ b/game/tests/unit/core/board_generator_test.gd @@ -18,6 +18,7 @@ func _init() -> void: quit(Kit.finish("board_generator_test")) return _test_generate_shape_and_mines() + _test_target_mine_count_single_source() _test_digits_equal_neighbor_mine_count() _test_same_seed_bit_identical() _test_clearable_and_inferability_selfconsistent() @@ -54,6 +55,34 @@ func _test_generate_shape_and_mines() -> void: Kit.check("生成期指标集已落盘(metrics 非空)", not b.metrics.is_empty()) +# 目标雷数公式单点(d14):同参同值对拍原式 + 真实产图指标读数同源 +func _test_target_mine_count_single_source() -> void: + var bad := 0 + var checked := 0 + for L in [1, 2, 3]: + var row: Dictionary = _db.resolve_board_gen(L, 0) + if row.is_empty(): + continue + var params := { + "w": int(row.board_width), "h": int(row.board_height), + "density": float(row.mine_density), + } + var from_fn: int = BoardGeneratorScript.target_mine_count(params) + var from_expr: int = int(round(float(params.density) * float(params.w * params.h - BoardData.SPAWN_AREA_CELLS))) + checked += 1 + if from_fn != from_expr: + bad += 1 + Kit.check("target_mine_count 与原式同参同值(%d 档对拍全等)〔wd14〕" % checked, checked > 0 and bad == 0) + var b := _generate(1, 0, 74000) + if b == null: + Kit.check("生成返回棋盘(雷数单点段)", false) + return + var row1: Dictionary = _db.resolve_board_gen(1, 0) + Kit.check("产物断言读数 = target_mine_count 同源(metrics.target_mines)〔wd14〕", + int(b.metrics.get("target_mines", -1)) == BoardGeneratorScript.target_mine_count({ + "w": int(row1.board_width), "h": int(row1.board_height), "density": float(row1.mine_density)})) + + func _test_digits_equal_neighbor_mine_count() -> void: var b := _generate(1, 1, 72000) if b == null: diff --git a/game/tests/unit/core/breakpoint_resume_test.gd b/game/tests/unit/core/breakpoint_resume_test.gd index e6eba55c..8ce75e20 100644 --- a/game/tests/unit/core/breakpoint_resume_test.gd +++ b/game/tests/unit/core/breakpoint_resume_test.gd @@ -181,8 +181,32 @@ func _init() -> void: Kit.check_quiet(adr_n.start_adventure(910042, params) and adr_n.runner().nodes() == graph_j, "P7 同种子重开局同图(开局入口复现承载)〔v7〕") + # ---- Part 8:断点字段登记单源(登记键集 == 写侧实拼字典键集;键名增删即红)---- + var fields: Dictionary = AdventureRunnerScript.BREAKPOINT_FIELDS + var reg_keys: Array = fields.keys() + reg_keys.sort() + var bp_keys: Array = bp_j.keys() + bp_keys.sort() + Kit.check_quiet(reg_keys == bp_keys, + "P8 登记单源键集 == 写侧实拼断点键集(%d 键逐一对齐)〔wr2〕" % reg_keys.size()) + var frozen_keys := ["adventure_counters", "attunement_charges", "attune_refresh_y", "aux_items", + "bonus", "bonus_deck", "bonus_pending", "bonus_result", "bonus_taken", "feat_ticket_pity", + "feat_ticket_sold", "gp", "graph_gen_v2", "hero_hp", "keystone", "keystone_drops", + "level_index", "master_seed", "node_id", "refresh_stock", "relics", "rng_counter", + "shop_purchased", "shop_shelf", "shop_visit_buys", "visited_ids", "vision_counter_used"] + frozen_keys.sort() + Kit.check_quiet(reg_keys == frozen_keys, + "P8 键集与冻结清单逐位一致(27 键契约;增删键即红)〔wr2〕") + var vocab := ["plain", "clamp", "keep_on_null", "dual_merge", "params_fallback", "token_migration"] + var stray: Array = [] + for k in fields.keys(): + var strat := String((fields[k] as Dictionary).get("strategy", "")) + if not vocab.has(strat): + stray.append("%s=%s" % [k, strat]) + Kit.check_quiet(stray.is_empty(), "P8 策略标签全落六类词表(越界=%s)〔wr2〕" % str(stray)) + _clear_disk() - quit(Kit.finish("breakpoint_resume_test", "PASS — 断点往返/续玩确定性/幂等/退出清断点/装配存档回归/生成图换代 全绿")) + quit(Kit.finish("breakpoint_resume_test", "PASS — 断点往返/续玩确定性/幂等/退出清断点/装配存档回归/生成图换代/字段单源 全绿")) # 句柄寻址夹具辅助:取生成图中指定句柄节点的首个出边目标(构造中途停点落档形态) diff --git a/game/tests/unit/core/level_shop_economy_test.gd b/game/tests/unit/core/level_shop_economy_test.gd index 9f4049e7..0288f789 100644 --- a/game/tests/unit/core/level_shop_economy_test.gd +++ b/game/tests/unit/core/level_shop_economy_test.gd @@ -242,6 +242,7 @@ func _init() -> void: if d.severity == "error" or d.severity == "fatal": push_error(" [%s] %s: %s" % [d.severity, d.category, d.message]) # 套执行序沿源编排(基点 _run_core_smoke 调用序——本组套的相对序) + _smoke_table_migration() _smoke_level() _smoke_level_graph() _smoke_gp_wallet() @@ -254,6 +255,20 @@ func _init() -> void: quit(Kit.finish("level_shop_economy_test")) +# ---- 数值入表等价证明(d11/d16a——原字面量迁移:表值 == 迁移前字面量,消费端读表)---- + +func _smoke_table_migration() -> void: + print("--- 数值入表(shop 两 knob + hero 显示名 · 原值迁移)---") + Kit.check("shop.relic_base_price 表值 == 迁移前字面量(15.0)〔wd11〕", _db.shop.relic_base_price == 15.0) + Kit.check("shop.spend_discount 表值 == 迁移前字面量(0.8)〔wd11〕", _db.shop.spend_discount == 0.8) + Kit.check("hero.display_name 表值 == 迁移前字面量(探险者)〔wd16〕", _db.hero.display_name == "探险者") + # 消费端读表:spawn 真实构造链的英雄单位显示名与表值同 + var units = load("res://core/unit_container.gd").new(_db) + var spawned: bool = units.spawn(1, 0, int(_db.hero.max_hp)) + Kit.check("spawn 后英雄单位 display_name 读表同值(探险者)〔wd16〕", + spawned and String(units.hero_unit().get("display_name", "")) == String(_db.hero.display_name)) + + # ---- 生成图深度扩面覆盖断言(迭代 35 卡② S5/V11——节点源=卡① 生成器产图, # 敌侧/板侧解析一律走 ConfigDB 真实消费链,禁直读行集自算)---- @@ -1274,15 +1289,19 @@ func _smoke_shop_pool() -> void: var r8 = scen["r"] # ② 货架投影形制:kind 词表封闭 + 现价公式推导(ceil(base×b^(关序−1)×A^0)——核心现算,呈现零复算) var l2 := int(r8.level_row.level_index) + # 现价等价证明(d11——消费端读表推导 vs 迁移前硬编码字面量推导:同输入逐位一致) + var relic_price_now: int = r8.pool_price("relic") + var relic_price_pre: int = int(ceil(15.0 * pow(_db.shop_price_b(), float(l2 - 1)) * 1.0)) + Kit.check("relic 现价 = ceil(15.0×b^(关序−1))(读表与迁移前字面量逐位一致,%d)〔wd11〕" % relic_price_now, relic_price_now == relic_price_pre) for c in r8.shop_shelf(): # T6 商店保底后 relic 卡合法入架(保底占位)——词表断言放行 relic;价格断言分叉 - # (数值卡走 shop_pool_row 推导 / relic 卡走 15 固定锚——同公式不同基价) + # (数值卡走 shop_pool_row 推导 / relic 卡走表 knob relic_base_price——同公式不同基价) var ckind := String(c.get("kind_id", "")) var is_relic := ckind == "relic" Kit.check("货架 kind 在封闭词表或保底 relic 卡(%s)" % ckind, is_relic or shelf_kinds.has(ckind)) var expect_p: int if is_relic: - expect_p = int(ceil(15.0 * pow(_db.shop_price_b(), float(l2 - 1)) * 1.0)) + expect_p = int(ceil(float(_db.shop.relic_base_price) * pow(_db.shop_price_b(), float(l2 - 1)) * 1.0)) else: var prow: Dictionary = _db.shop_pool_row(ckind) expect_p = int(ceil(float(int(prow.get("base_price", 0))) * pow(_db.shop_price_b(), float(l2 - 1)) * 1.0)) diff --git a/game/tests/unit/presentation/modal_flow_test.gd b/game/tests/unit/presentation/modal_flow_test.gd index 08cd4b33..97177d67 100644 --- a/game/tests/unit/presentation/modal_flow_test.gd +++ b/game/tests/unit/presentation/modal_flow_test.gd @@ -87,6 +87,7 @@ func _init() -> void: # V6-4 演播后排窗挂起+边沿派发(T17 语义) var end_ov := Control.new() end_ov.visible = false + flow.register(end_ov) # d9 fail-fast 后:未登记层不可进入编排(夹具按 register 先行契约登记) rows["empty"] = false var sync_before1: int = calls["open_sync"] flow.open_after_playback(end_ov) @@ -105,9 +106,34 @@ func _init() -> void: flow.open_after_playback(end_ov) rows["empty"] = true var other := Control.new() + flow.register(other) var sync_before: int = calls["open_sync"] flow.open(other) Kit.check_quiet(end_ov.visible and flow._deferred == null and calls["open_sync"] == sync_before + 2, "V6 挂起兜底:开新层先结挂起(T17 防永不兑现——挂起层先开+新层后开两拍)〔迭代27 T4〕") + # V6-5 遮挡失败快+遮挡账合而非覆盖(d9) + var ms = ModalStackScript.new() + var ma := Control.new() + var mb := Control.new() + var stranger := Control.new() + stranger.visible = false # Control 默认 visible=true——先隐,才能断言 fail-fast 未把它照上屏 + ms.register(ma) + ms.register(mb) + ms.open(ma) + ms.open(stranger) # 未登记层:push_error + 早退(零副作用) + Kit.check_quiet(not stranger.visible and ma.visible, + "V6 未登记层 open fail-fast(该层不可见、已开层不变)〔wd9〕") + ms.close(stranger) + Kit.check_quiet(not stranger.visible and ma.visible, + "V6 未登记层 close fail-fast(已开层不受影响)〔wd9〕") + ms.open(mb) + ms.open(ma) # 开低层隐其上方可见层 + Kit.check_quiet(not mb.visible, "V6 开低层隐其上方层(重复 open 前态)〔wd9〕") + ms.open(ma) # 同层重复 open:上方已隐,hidden 空表——旧账须保留 + Kit.check_quiet(not mb.visible, "V6 重复 open 保持遮挡(V6 在役语义不变)〔wd9〕") + ms.close(ma) + Kit.check_quiet(mb.visible, "V6 重复 open 不吞遮挡账(close 低层恢复 mb——合并非覆盖)〔wd9〕") + ms.close(mb) + quit(Kit.finish("modal_flow")) diff --git a/game/tests/unit/presentation/pacing_queue_test.gd b/game/tests/unit/presentation/pacing_queue_test.gd index e60e5778..aa1a3dfa 100644 --- a/game/tests/unit/presentation/pacing_queue_test.gd +++ b/game/tests/unit/presentation/pacing_queue_test.gd @@ -7,9 +7,35 @@ # 运行:godot --headless --path game --script res://tests/unit/presentation/pacing_queue_test.gd(expect 0) extends SceneTree +const RowPacerScript := preload("res://presentation/row_pacer.gd") + var _problems: Array = [] +# 记参假站桩(d12 排播半):继承真类以过类型面,只记解析入参 +class FakeStage extends BattleStageView: + var calls: Array[Array] = [] + + func enemy_standee_center(target_id: String, display_name: String) -> Variant: + calls.append([target_id, display_name]) + return Vector2(10, 10) + + +# 记参假演出层(同上——play_* 全部只记不演) +class FakeFx extends SettlementFxView: + var calls: Array[Array] = [] + + func play_projectile(_from: Vector2, _to: Vector2, _glyph: String, _col: Color, _crit: bool = false) -> void: + calls.append(["projectile", _to]) + + func play_damage_number(_pos: Vector2, _text: String, _col: Color, _crit: bool = false) -> void: + calls.append(["mark", _pos]) + + func play_status_pulse(_pos: Vector2, _col: Color) -> void: + calls.append(["pulse", _pos]) + + + func check(cond: bool, msg: String) -> void: if not cond: _problems.append(msg) @@ -98,6 +124,21 @@ func _init() -> void: UiKit.motion_scale = 0.5 check(main.pace_delay_for("damage", "heal") == 0.0, "减动效段间延迟归零(行不跳过——同帧直落快路)") UiKit.motion_scale = 1.0 + # ---- 断言④:排播两处取用按行 target_id 解析站桩(d12 排播半——假 stage 记参)---- + var rp = RowPacerScript.new() + var fstage := FakeStage.new(null) + var ffx := FakeFx.new() + rp.stage = fstage + rp.board_view = main.board_view + rp.settlement_fx = ffx + rp._play_settlement_mark({"type": "damage", "target": "哥布林", "target_id": "goblin_b", "value": 3}) + check(fstage.calls.size() == 1 and fstage.calls[0] == ["goblin_b", "哥布林"], + "结算标出按行 target_id 解析站桩(id 与名同传——d12)〔wd12〕") + rp._play_cross_zone_fx({"type": "damage", "target": "哥布林", "target_id": "goblin_b", "value": 3}) + check(fstage.calls.size() == 2 and fstage.calls[1] == ["goblin_b", "哥布林"], + "跨区弹道按行 target_id 解析站桩(id 与名同传——d12)〔wd12〕") + check(ffx.calls.size() == 2, "两路解析成功后演出层各接一拍(记参面非空)〔wd12〕") + rp.free() _finish() diff --git a/game/tests/unit/presentation/standee_fx_sweep_test.gd b/game/tests/unit/presentation/standee_fx_sweep_test.gd new file mode 100644 index 00000000..b533a59b --- /dev/null +++ b/game/tests/unit/presentation/standee_fx_sweep_test.gd @@ -0,0 +1,134 @@ +# VERTICAL SLICE - NOT FOR PRODUCTION +# 呈现域缺陷扫掠断言(行为缺陷定点修批卡① T2 五族): +# ① 意图蓄力档可达(d2——_intent_charge 分档,旧 GOLD_DEEP 判据恒不可达); +# ② 庆祝 tween 句柄纪律(d4——重开不叠加、close 必杀); +# ③ 互斥四口(d5 调试钮同判据 / d8 控制器运行态可查); +# ⑤ 站桩按敌 id 解析(d12 两口:on_step_row_fx 闪白落点 + enemy_standee_center 弹道落点)。 +# d6/d7 两守卫断言宿主 = system_test;d3/d17/d18 属非行为面(实现报告等价申报)。 +# 运行:godot --headless --path game --script res://tests/unit/presentation/standee_fx_sweep_test.gd(expect 0) +extends SceneTree + +const Kit := preload("res://tests/unit/test_kit.gd") +const StandeeBodyScript := preload("res://presentation/views/standee_body.gd") +const BattleEndScript := preload("res://presentation/views/battle_end_overlay.gd") +const DebugOverlayScript := preload("res://presentation/views/debug_overlay.gd") +const TuneSimScript := preload("res://presentation/tune_sim_controller.gd") +const StageScript := preload("res://presentation/views/battle_stage_view.gd") + + +# 假调试面板(TuneSimController 所需 setter 面 + 树内节点供给 get_tree) +class FakePanel extends Node: + var running := false + var progress := "" + var report := "" + var atk := 0 + var hp := 0 + + func atk_pct() -> int: + return atk + + func hp_pct() -> int: + return hp + + func set_sim_running(r: bool) -> void: + running = r + + func set_sim_progress(t: String) -> void: + progress = t + + func set_sim_report(t: String) -> void: + report = t + + +# 假自动策略(run_level 签名同 level_autoplay——回调内读控制器运行态) +class FakeDriver extends RefCounted: + var ctrl + var seen: Array[bool] = [] + var calls := 0 + + func run_level(_db: Variant, _seed: int, _level: int, _atk: int, _hp: int, _other: bool, _logger: Variant) -> Dictionary: + calls += 1 + seen.append(ctrl.is_running()) + if calls == 1: + return {"hero_dmg": 12.0, "rounds": 4, "result": "victory", "hp_left": 3.0, "hp_at_boss": 5.0} + return {"hero_dmg": 6.0, "rounds": 2, "result": "defeat", + "defeat_node_index": 3, "defeat_node_type": "battle", "hp_left": 0.0, "hp_at_boss": 0.0} + + +func _init() -> void: + UiKit.motion_scale = 1.0 + + # ---- ⑤ 站桩按 id 解析(两口共用同名字敌载体)---- + var stage = StageScript.new(null) + root.add_child(stage) + var s1 = StandeeBodyScript.new("enemy") + var s2 = StandeeBodyScript.new("enemy") + stage.world.add_child(s1) + stage.world.add_child(s2) + s1.set_data({"display_name": "哥布林", "enemy_id": "goblin_a", "hp": 3, "max_hp": 3, "order": 1}) + s2.set_data({"display_name": "哥布林", "enemy_id": "goblin_b", "hp": 3, "max_hp": 3, "order": 2}) + s1.position = Vector2(10, 20) + s2.position = Vector2(200, 20) + stage._cards.clear() + stage._cards.append(s1) + stage._cards.append(s2) + Kit.check("站桩 enemy_id 访问器读装载值〔wd12〕", s1.enemy_id() == "goblin_a" and s2.enemy_id() == "goblin_b") + Kit.check("同名敌按 id 精确解析(不做名子串撞车)〔wd12〕", + stage._card_by_id("goblin_b") == s2 and stage._card_by_id("goblin_a") == s1) + Kit.check("空 id / 未登记 id 恒不命中(名兜底归调用点)〔wd12〕", + stage._card_by_id("") == null and stage._card_by_id("no_such") == null) + # 口一:on_step_row_fx —— 伤害行携 target_id,闪白落点须在 id 指向的站桩 + stage.on_step_row_fx({"type": "damage", "target": "哥布林", "target_id": "goblin_b"}) + await create_timer(0.6).timeout # 弹道到达拍 0.33s + 余量 + Kit.check("伤害行按 target_id 命中正确站桩(闪白落点)〔wd12〕", + s2._flash_count == 1 and s1._flash_count == 0) + # 口二:enemy_standee_center —— id 先、display_name 兜底 + var c_b = stage.enemy_standee_center("goblin_b", "哥布林") + var c_b_expect: Vector2 = stage.world_to_shell(s2.position + s2.box * 0.5) + Kit.check("弹道落点按 target_id 取站桩中心〔wd12〕", c_b != null and c_b == c_b_expect) + var c_fallback = stage.enemy_standee_center("", "哥布林") + var c_fallback_expect: Vector2 = stage.world_to_shell(s1.position + s1.box * 0.5) + Kit.check("无 target_id 时按名兜底同旧行为(首命中)〔wd12〕", c_fallback != null and c_fallback == c_fallback_expect) + + # ---- ① 意图蓄力档可达(d2)---- + s1.set_pending("damage", "heavy") + Kit.check("蓄力技能(heavy)置 _intent_charge=true〔wd2〕", s1._intent_charge) + s1.set_pending("damage", "goblin_slash") + Kit.check("非蓄力攻击技(goblin_slash)零置位〔wd2〕", not s1._intent_charge) + s1.set_pending("buff", "goblin_rage") + Kit.check("增益系零置位〔wd2〕", not s1._intent_charge) + s1.set_pending("damage", "basic") + Kit.check("普攻零表示路径复位 _intent_charge〔wd2〕", not s1._intent_charge) + + # ---- ② 庆祝 tween 句柄纪律(d4)---- + var ov = BattleEndScript.new() + root.add_child(ov) + ov.open_victory("判定行", "继续", Rect2(0, 0, 100, 100)) + var first: Tween = ov.get_meta(&"celebrate_tw", null) + Kit.check("首开胜利画面创建庆祝句柄〔wd4〕", first != null and first.is_valid()) + ov.open_victory("判定行", "继续", Rect2(0, 0, 100, 100)) + var second: Tween = ov.get_meta(&"celebrate_tw", null) + Kit.check("重开胜利画面:旧句柄已 kill、新句柄在位(至多一个活跃)〔wd4〕", + first != null and not first.is_valid() and second != null and second.is_valid()) + ov.close() + Kit.check("close 必杀庆祝句柄(句柄清空)〔wd4〕", ov.get_meta(&"celebrate_tw", null) == null) + + # ---- ③ 互斥四口(d5/d8)---- + var dbg = DebugOverlayScript.new(null, 0, 0) + dbg.set_sim_running(true) + Kit.check("模拟运行期调试入口钮同置灰〔wd5〕", dbg._dbg_btn.disabled) + dbg.set_sim_running(false) + Kit.check("模拟收尾调试入口钮恢复(未锁定)〔wd5〕", not dbg._dbg_btn.disabled) + dbg.free() + + var panel := FakePanel.new() + root.add_child(panel) + var drv := FakeDriver.new() + var ctrl = TuneSimScript.new({"action_economy": {"ap_per_round": 2}}, panel, 100, drv) + drv.ctrl = ctrl + await ctrl.run(2) # 假 driver 回调内读 is_running()(两局各一读) + Kit.check("控制器运行中 is_running()=true(驱动回调内读数)〔wd8〕", drv.seen == [true, true]) + Kit.check("控制器收尾 is_running()=false〔wd8〕", not ctrl.is_running() and not panel.running) + Kit.check("两局报告全文注入面板(run 收尾链路完整)〔wd8〕", String(panel.report) != "") + + quit(Kit.finish("standee_fx_sweep")) diff --git a/game/tests/unit/presentation/standee_fx_sweep_test.gd.uid b/game/tests/unit/presentation/standee_fx_sweep_test.gd.uid new file mode 100644 index 00000000..35ce3080 --- /dev/null +++ b/game/tests/unit/presentation/standee_fx_sweep_test.gd.uid @@ -0,0 +1 @@ +uid://b3vqjio51r2nf diff --git a/game/tests/unit/presentation/terrain_tile_sets_test.gd b/game/tests/unit/presentation/terrain_tile_sets_test.gd index ddaa0454..94599bc5 100644 --- a/game/tests/unit/presentation/terrain_tile_sets_test.gd +++ b/game/tests/unit/presentation/terrain_tile_sets_test.gd @@ -8,7 +8,7 @@ extends SceneTree const ConfigDBScript := preload("res://core/config_db.gd") -const BoardViewScript := preload("res://presentation/views/board_view.gd") +const TilePaletteScript := preload("res://presentation/views/tile_palette.gd") const Kit := preload("res://tests/unit/test_kit.gd") # T6 收口:_check/汇总退出样板单点 @@ -41,8 +41,8 @@ func _init() -> void: # 套完整性:词表每个 board_type 的 16 墙 + 5 地面变体全部在盘(缺位 → BoardView 整批回落 dungeon 的 # 缺位回退断言由 ui_input_probe 覆盖;本断言管「入包完整」面) func _test_set_completeness(board_dir: String, types: Array) -> void: - var wall_names: Array = BoardViewScript.TILE_WALL_NAMES - var variants: int = int(BoardViewScript.TILE_FLOOR_VARIANTS) + var wall_names: Array = TilePaletteScript.TILE_WALL_NAMES + var variants: int = int(TilePaletteScript.TILE_FLOOR_VARIANTS) for bt in types: var missing: Array = [] for w in wall_names: @@ -119,10 +119,10 @@ func _load_stats(board_dir: String, fname: String, top_half: bool) -> Dictionary # 每个图型走 BoardView 同一归一+测光+选模式管线(禁测试侧另写算法),断言: # ①dungeon=亮墨族直画(对 p90 ≥4.5 且对均值 ≥2.5);②underdark/deep_underdark=色牌垫底 # (双峰纹理直画不可救——渲染帧取证深墨隐形实证,牌上深墨 12:1);③直画模式的双门槛值照抄 -# BoardView 判据常量(防判据漂移) +# TilePalette 判据常量(防判据漂移) func _test_digit_ink_modes(board_dir: String, types: Array) -> void: var expect_plate := {"dungeon": false, "underdark": true, "deep_underdark": true} - var variants: int = int(BoardViewScript.TILE_FLOOR_VARIANTS) + var variants: int = int(TilePaletteScript.TILE_FLOOR_VARIANTS) for bt in types: var lumas: Array = [] var readable := true @@ -132,11 +132,11 @@ func _test_digit_ink_modes(board_dir: String, types: Array) -> void: readable = false break var tex: Texture2D = ImageTexture.create_from_image(Image.load_from_file(p)) - BoardViewScript._normalize_floor_luma(tex, 0.10, lumas) + TilePaletteScript.normalize_floor_luma(tex, 0.10, lumas) if not readable: Kit.check("图型 %s 数字墨族断言样本可读" % String(bt), false) continue - var mode: Dictionary = BoardViewScript._digit_ink_mode(lumas) + var mode: Dictionary = TilePaletteScript.digit_ink_mode(lumas) var plate: bool = bool(mode["plate"]) var dark: bool = bool(mode["dark"]) var mode_name := "色牌垫底" if plate else ("深墨族直画" if dark else "亮墨族直画") @@ -144,14 +144,14 @@ func _test_digit_ink_modes(board_dir: String, types: Array) -> void: Kit.check("图型 %s 双峰纹理落牌(亮族 p90 %.2f/均值 %.2f,深族 p90 %.2f/均值 %.2f 均未过门)" % [ String(bt), float(mode["cr_p90_bright"]), float(mode["cr_mean_bright"]), float(mode["cr_p90_dark"]), float(mode["cr_mean_dark"])], - float(mode["cr_mean_dark"]) < float(BoardViewScript.DIGIT_INK_CR_MEAN_DARK)) + float(mode["cr_mean_dark"]) < float(TilePaletteScript.DIGIT_INK_CR_MEAN_DARK)) else: var cr_p90 := float(mode["cr_p90_bright"]) if not dark else float(mode["cr_p90_dark"]) var cr_mean := float(mode["cr_mean_bright"]) if not dark else float(mode["cr_mean_dark"]) Kit.check("图型 %s 所选%s 对 p90 ≥%.1f(实测 %.2f)" % [String(bt), mode_name, - float(BoardViewScript.DIGIT_INK_CR_P90), cr_p90], - cr_p90 >= float(BoardViewScript.DIGIT_INK_CR_P90)) - var mean_gate := float(BoardViewScript.DIGIT_INK_CR_MEAN_BRIGHT) if not dark else float(BoardViewScript.DIGIT_INK_CR_MEAN_DARK) + float(TilePaletteScript.DIGIT_INK_CR_P90), cr_p90], + cr_p90 >= float(TilePaletteScript.DIGIT_INK_CR_P90)) + var mean_gate := float(TilePaletteScript.DIGIT_INK_CR_MEAN_BRIGHT) if not dark else float(TilePaletteScript.DIGIT_INK_CR_MEAN_DARK) Kit.check("图型 %s 所选%s 对均值 ≥%.1f(实测 %.2f)" % [String(bt), mode_name, mean_gate, cr_mean], cr_mean >= mean_gate) if expect_plate.has(bt): diff --git a/game/tests/unit/presentation/theme_projection_test.gd b/game/tests/unit/presentation/theme_projection_test.gd index dd71c5b6..09bb9178 100644 --- a/game/tests/unit/presentation/theme_projection_test.gd +++ b/game/tests/unit/presentation/theme_projection_test.gd @@ -690,8 +690,8 @@ func _smoke_contrast_inks() -> void: sb_src.contains("HORIZONTAL_ALIGNMENT_LEFT, -1, 13, UiKit.GOLD_BRIGHT")) Kit.check("V4 魔法护甲徽标墨=HERO_BLUE_BRIGHT", sb_src.contains("col = UiKit.HERO_BLUE_BRIGHT")) Kit.check("V4 重甲徽标墨=GOLD_BRIGHT", sb_src.contains("col = UiKit.GOLD_BRIGHT")) - Kit.check("V4 GOLD_DEEP 语义岗不动(standee:199 意图动画判据)", - sb_src.contains("if _intent_col == UiKit.GOLD_DEEP:")) + Kit.check("V4 意图蓄力档判据=分档布尔(standee_body:179 CHARGE_SKILL_IDS 判定;旧 GOLD_DEEP 恒不可达判据清零)〔wd2〕", + sb_src.contains("_intent_charge = pid in CHARGE_SKILL_IDS") and not sb_src.contains("if _intent_col == UiKit.GOLD_DEEP:")) var ap_src: String = FileAccess.get_file_as_string("res://presentation/views/ap_bar_view.gd") Kit.check("V4 RAP 反馈行墨=STONE_DEEP(原位行锚定)", ap_src.contains('UiKit.label("",UiKit.FS_CAPTION, false, UiKit.STONE_DEEP)')) @@ -807,7 +807,7 @@ func _all_labels(root: Node) -> Array: # ---- 生涯页只读投影一致性(汇总段六类九项逐行 == SaveManager 计数访问器/ -# MetaProgression 访问器读数;投影拼装与 main._career_data 同构(同访问器族同序)。 +# MetaProgression 访问器读数;投影拼装与 HudPresenter.career_data 同构(同访问器族同序)。 # 只读性连接巡检与零动效断言归 ui_struct_probe 呈现侧,本段只管数据面+旧档载入显示 # (v5 样档升 v6——完成定义 6「计数域缺省补零、页正常显示」))---- func _career_overlay_projection() -> void: @@ -822,7 +822,7 @@ func _career_overlay_projection() -> void: sv.apply_adventure_summary({"result": "abandoned", "cleared": false, "levels_cleared": 1, "stopped_at": 2, "keystone_total": 0, "master_seed": 22}) sv.write_to_disk() - # 投影拼装(main._career_data 同构)+ 视图喂入 + # 投影拼装(HudPresenter.career_data 同构)+ 视图喂入 var data := {"total": sv.adventure_total(), "cleared": sv.adventure_cleared(), "clears": [], "boss_first_kills": 0, "boss_first_total": _db.level_count(), "boss_kills": sv.boss_kill_count(), "keystone": mp.keystone_balance()} diff --git a/reports/iter37-代拍板-2026-10-25.md b/reports/iter37-代拍板-2026-10-25.md new file mode 100644 index 00000000..dc699163 --- /dev/null +++ b/reports/iter37-代拍板-2026-10-25.md @@ -0,0 +1,31 @@ +# iter37 代拍板追认单(2026-10-25) + +迭代 37 实现批(分支 `iter37`,四卡:①行为缺陷定点修 ②main 判离 ③boardview 拆件 ④runner 拆件与断点 schema 单点)批内由会话替你拍的决定,等你**追认或推翻**。授权发生在卡片审问阶段的只列「备案」;卡片没写清、会话自己拍的列「待追认」;会话拍错了的列「认错」。 + +## 一、待追认(卡片未写清,会话自拍) + +| # | 决定 | 依据与后果 | 落点 | +|---|---|---|---| +| B-1 | **计数守恒装置:三处宿主文件共 25 条转发属性/常量别名**(board_view 13 条 :82-122、adventure_runner 4 条 :60-77、level_runner 8 条 :50-64),件内状态以宿主同名属性露出,只为让 S13 基线消息里的「类变量数」逐字不变 | 用户令「只改代码不改基线」+卡片契约 1「公开面零变化」+旧基线键含计数 ⇒ 数学上只剩"持平/达标/停卡"三途,会话选了持平。**已判定为处置错误**:本应停卡呈报(实现段零裁断)。且 master `37625317` 基线键治本后(改小/持平不红),该装置的存在理由已消失 | 已立 P0 待办条目「AdventureRunner 职责真拆分」(待办清单.md:5,2026-10-25 登记) | +| B-2 | 卡② **范围扩展**:r1-1~r1-4 之外多做 `_build_ui` 八步拆分 → `main_ui_build`,冒险/浮层/调试三流程族 → `main_adventure_flow`/`main_overlay_flow`/`main_debug_flow` | 卡片只列四族,但 main.gd 不降到 999 行内就冒新红;属"只改代码"令的直接推论。逐字搬运,成员访问改 root 前缀 | commit `b0d92524` | +| B-3 | 卡④ **件接口形态**:`CurioDeck._init(db_ref, rng_ref)`/`bind_handlers`/`reset()`;`ShopSession.bind(refs, hooks)`/`open_pool()`/`leave()`、件内状态全部写成公有 var(`deck`/`taken`/`shelf`/`visit_buys`…) | 卡片接口块只给 `_init(db)` 与 `set_shop_pool` 四参沿用;实际形态由"零新造协议+逐字可核"反推。公有 var 是为让宿主转发属性与既有 Callable 注入面零成本对接——代价是件自身无封装 | commit `e5ee4d04` | +| B-4 | 卡④ **登记项只含策略标签,不含"写侧取值器"**:`BREAKPOINT_FIELDS` 值=`{"strategy": ...}`,写侧取值由 `_bp_parts()` 的键名字典承载(未登记键即 `parts[key]` 越界 fail-fast) | 卡片 §104 写的是「键→{写侧取值键路径+读侧回退策略标签}」;GDScript 里 27 个取值 callable 只能写 lambda(S6 禁捕获 self)或字符串反射,两者都比现状差。读侧两处(`resume_adventure` 与 `_start_current_level` 二次消费)已同源派生 | [game/core/adventure_runner.gd:857](file:///D:/code/g-plan2-iter37/game/core/adventure_runner.gd#L857) | +| B-5 | 卡④ `CurioDeck.reset()` **不清凭证三计数**(`ticket_pity`/`ticket_sold`/`ticket_appeared`) | 宿主原 `_reset_curio_state` 就只重置池与 kind 计数;清零会改限购/保语义=行为变更。等价性优先于"看起来更合理" | curio_deck.gd `reset()` | +| B-6 | 卡① d18 参数注解形态:卡片字面 `lumas_out: Array[Variant] = null` 在 Godot 4.7 解析报错(typed array 不可默认 null),改用 `: Variant = null` | 唯一合法注法,零行为变化 | commit `0529e169` | +| B-7 | 卡① T1 三处代拍板:`target_mine_count` 签名(照接口块)、信号契约措辞与载荷键并集(按发射位实查)、`int()` 读侧包裹写法 | 卡片白名单内预授权面 | commit `1b9736f0` | +| B-8 | 卡③ r2-1 的**瓦片查表族与墨族判据改为表驱动重构**(16 掩码查表+`_family_contrasts`/`_mean_of` 辅助式),非逐字迁移 | 独立验收审计 F-3 抓出本批漏报;动机=把 `wall_tile_for`/`wall_center_tile`/`digit_ink_mode` 从 S13 函数级超标(分支 14/返回 15/局部 12)压回阈值内,兑现「只改代码不改基线」;等价性由审计穷尽验证(掩码/档位/三态真值表零差)+golden 三图 0.00%。**违卡③ 契约 1「禁顺手改算法」字面,未违语义**——请追认;不回退 | commit `5c9c5f80`+实现报告 §3.1b | + +## 二、认错(会话处置错误,已补账) + +| # | 事实 | 已做的补救 | +|---|---|---| +| E-1 | 卡④ 遇到「卡片冻结公开面」×「S13 基线键含计数」互斥时,**没停卡呈报,自造了第三条路**(转发属性糊数)。按五段流水线实现段零裁断,这属上游缺陷该退回编排/写卡段 | 已在待办清单登记 P0 条目(含撤除依据与四件待裁缺口);本批按用户令「先按现状收口」不返工 | +| E-2 | P0 条目把同类装置只写成 12 条(adventure_runner 4+level_runner 8),**漏了 board_view 的 13 条**(卡③ 同款),覆盖面偏窄 | 已在本批随登记更正为「三宿主共 25 条」并补第⑤件缺口(待办清单.md:5,pipeline_gate 单件自查 exit=0);反哺点第 4 条改记为已执行 | + +## 三、备案(用户当场裁决,非代拍板) + +1. 「全部都改代码,不允许改基线」——批内基线零改动贯穿全批(`tools/gdstyle_baseline.txt` 未动;commit `2ed81668` 九条漂移全用代码压回)。 +2. golden 棋盘三图判失效 → 用户裁「重制三张基线」(成因 `2f3a87e9` 与本批零因果),复闸 changed=0.00%。 +3. 「行为验收只走撤销证明,禁注入造红」(P0 令)——本批撤销证明全部为"回退本批 hunk → 断言红 → 还原复绿",无变异开关、无改值造红。 +4. 本批直写根 `待办清单.md` 登记 P0 条目——用户本轮明示授权,越批性质禁写面的豁免仅限这一条登记。 +5. 「本批按现状收口,真拆分另立 P0 卡」——用户裁。 diff --git a/reports/iter37-反哺点-2026-10-25.md b/reports/iter37-反哺点-2026-10-25.md new file mode 100644 index 00000000..84008e2d --- /dev/null +++ b/reports/iter37-反哺点-2026-10-25.md @@ -0,0 +1,61 @@ +# iter37 反哺点清单(2026-10-25) + +迭代 37 实现批发现、需要写进 `design/` 权威文档或规约的点。**本批不落笔**(批性质禁令),逐条写清「哪个文件哪一节 → 改成什么 → 依据」。用户在会话里裁决后归文档批(doc-design 技能)落文,落完本条即删。 + +## 1. architecture 模块归属表 —— 登记本批十一个新件 + +- **文件/节**:[design/engineering/architecture/architecture.md](file:///D:/code/g-plan2/design/engineering/architecture/architecture.md) 核心层/功能层模块归属表 +- **改成什么**:新增行——`core/curio_deck.gd`(CurioDeck:令牌多重集/抽签/cap/专长凭证三计数与判定)、`core/shop_session.gd`(ShopSession:商店双模式货架/定价/购买/刷新)、`presentation/views/tile_palette.gd`、`presentation/views/hero_sprite.gd`、`presentation/views/board_world.gd`、`presentation/board_resolvers.gd`、`presentation/main_ui_build.gd`、`presentation/main_adventure_flow.gd`、`presentation/main_overlay_flow.gd`、`presentation/main_debug_flow.gd`;并在 HudPresenter 既有行补「数据投影+文案拼装静态族」。 +- **依据**:本批 commits `b0d92524`/`5c9c5f80`/`e5ee4d04` 的 diff。与待办清单「架构与规范对齐批」条目④(模块归属对齐)**并批落文**,避免两次改同一张表。 + +## 2. 存档序列化文档 —— 断点键登记载体址变化 + +- **文件/节**:[design/gdd/systems/表现与支撑/存档序列化.md](file:///D:/code/g-plan2/design/gdd/systems/表现与支撑/存档序列化.md) 断点域(SCHEMA_VERSION+快照锚)节 +- **改成什么**:加注一句「断点 27 顶层键的登记单源=`core/adventure_runner.gd` 内 `const BREAKPOINT_FIELDS`(键→回退策略标签),写侧由 `_bp_parts()` 拼装、读侧由 `_bp_read`/`_bp_read_int` 两处派生」。若该文档逐键列了清单,逐键表本身**不改**(键名/缺省值/回退策略零变化)。 +- **依据**:卡④ r3-3 单源化;wr2 断言(`breakpoint_resume_test` P8)已把「登记键集==写侧实拼键集」钉进回归面。 + +## 3. 秘宝/商店相关 GDD —— 实现载体址注记(机制零变化) + +- **文件/节**:[design/gdd/systems/跨局循环/秘宝.md](file:///D:/code/g-plan2/design/gdd/systems/跨局循环/秘宝.md)(若记令牌池结构)与商店/经济相关篇目的实现落点段 +- **改成什么**:只把「池与判定」逻辑的载体从 `adventure_runner` 私有族改注为 `CurioDeck`、「货架/定价/购买/刷新」载体改注为 `ShopSession`;四口径契约、池消耗即上限、A 链定价等**规则文字一字不动**。 +- **依据**:卡④ 契约 1 零行为变更;等价三证之逐字迁移核对表(实现报告 §3)。 + +## 4. 待办清单 P0 条目覆盖面补记(已执行,留痕用) + +- **文件/节**:[待办清单.md](file:///D:/code/g-plan2/待办清单.md):5 「AdventureRunner 职责真拆分(优先级=P0)」条目的**实况**属性 +- **改成什么**:已在本批随登记更正(commit 待填)——标题与实况改为「三宿主共 25 条同类装置」(board_view 13 条 :82-122+adventure_runner 4 条+level_runner 8 条),读数补 level_runner 38/54、board_view 45/27 同状,缺口加第⑤件「卡③ 拆件件与 board_view 的字段归属重撤面」。**本条不需下游再动**,留作落文凭据;`python tools/pipeline_gate.py` 单件自查 exit=0。 +- **依据**:本批盘点读数(登记时只数了卡④ 两文件,漏了卡③——见代拍板单 E-2)。 + +## 5. 验证集清单(断言索引)陈旧 + +- **文件/节**:[game/tests/验证集清单.md](file:///D:/code/g-plan2/game/tests/验证集清单.md) +- **改成什么**:由 `python tools/extract_checks.py` 重生成一次(本批新增带尾缀断言=〔wd1/2/4/5/6/7/8/9/11/12/14/16〕四十五行+〔wr2〕三行,均不在册;`wc` 族系迭代 36 旧族,本批零新增)。 +- **依据**:该文件只由脚本手工重生成、不受任何闸覆盖(在册已知盲区),换代批后必陈旧。 + +## 6. gdstyle 基线陈旧键清理(禁自动刷) + +- **文件/节**:`tools/gdstyle_baseline.txt` +- **改成什么**:下次**人工**发起 `--refresh` 时一并清掉已不命中盘面的条目——`adventure_runner` 的 `_draw_curio_candidates`(85 行/26 局部/11 分支)、`resume_adventure`(108 行/13 局部/11 分支)、`_start_current_level`(53 行);`level_runner` 迁出函数名对应条目;`board_view` 已压回阈值内函数对应条目。 +- **依据**:本批基线零改动令;新键形态(文件|规则|函数名)下陈旧键无害但属噪声。骤降守卫在 refresh 路径已内建(`37625317`)。 + +## 7. 规约反哺:重构卡必须显式裁"计数守恒装置" + +- **文件/节**:[design/engineering/code-conventions.md](file:///D:/code/g-plan2/design/engineering/code-conventions.md) §1(一文件一职责)或 [design/engineering/test-conventions.md](file:///D:/code/g-plan2/design/engineering/test-conventions.md) 撤销证明段+项目技能 `.qoder/skills/req-card/SKILL.md` +- **改成什么**:加一条判据——「重构类需求卡片必须写明:**禁止为迎合机检消息文本(含计数)而保留或新增无效成员/别名**;遇机检键与卡片契约互斥,实现段一律停卡呈报,不得自造第三条路」。 +- **依据**:本批 E-1 实证——卡片契约 1「公开面零变化」×旧基线键含计数=数学上只剩"持平/达标/停卡",实现段自拍糊数即违实现段零裁断。规则修订须先在真实任务上评估(本条已评估:本批三处踩同一坑)。 + +## 9. 单点读取口径需核一次:行访问器直读是否等同「ConfigDB 单点」 + +- **文件/节**:[design/engineering/code-conventions.md](file:///D:/code/g-plan2/design/engineering/code-conventions.md) 数值单点纪律条(+AGENTS.md 同一条红线) +- **改成什么**:明确「经 `db.shop.relic_base_price` 这类**表行访问器**直读」是否满足「玩法数值只存 .tres 经 ConfigDB 单点读取」。本批 `2ed81668` 退役了 `ConfigDB.shop_relic_base_price()`/`shop_spend_discount()`/`hero_display_name()` 三个包装访问器,消费面改直读行访问器(理由=配置值写死进代码再运行时追加=双重真值);独立审计 F-4 指出该改法与卡片钉值表文本不一致,需口径裁断而非默认放行。 +- **依据**:审计 F-4;commit `2ed81668`;代拍板单 F-4 行。 + +## 8. 观感/交互类无(本批零行为变更) + +本批四卡全部为缺陷定点修+等价重构,无 GDD 数值与玩法语义变化;数值表新增两 knob(`relic_base_price`/`spend_discount`,卡① d11)与 `display_name`(d16a)已随卡① 提交入库,其**表值即事实源**,GDD 若已列同值则无需改动——文档批按现行 GDD 核一次即可,未核前不落笔。 + +## 10. 重锚批的义务清单(本批实测新凭据,须并入待办清单:118 那条) + +- **文件/节**:`待办清单.md:118` 「旧行为 MC 行为锚全数作废——重锚归数值批」条目的**实况**属性;以及 `sim-data/README.md` 的 MC 采集档位表说明 +- **改成什么**:给该条目补一手凭据与新增义务——①2026-10-25 迭代 37 实测:`parity_anchor.py check` 失败 9 项,其中 3 票 RUNSUM 与 JSONL 行数面属锚早于迭代 35 卡② `9ee393a1`(MC 出数字段 `level_templates`→`level_maps`)的历史失配,对 master 同样成立;②另 6 票=迭代 37 卡① d12 新增明细行键 `target_id`(双树原始面实测:行数全同、零值差、唯一差异=该新键),**重锚时必须把 `target_id` 纳入锚的 JSONL 口径**,否则重锚后仍报 6 票;③锚重制属数值标定窗口动作,禁在行为重构批内顺手做(本批即按此未重锚)。 +- **依据**:实现报告附录 E(含原始面留盘位置 `sim-data/parity-master`、`sim-data/parity-iter37`,gitignored);commit `0170071d`/`9ee393a1`;待办清单:118 现行条文。 diff --git a/reports/iter37-实现报告-2026-10-25.md b/reports/iter37-实现报告-2026-10-25.md new file mode 100644 index 00000000..01a5d37e --- /dev/null +++ b/reports/iter37-实现报告-2026-10-25.md @@ -0,0 +1,281 @@ +# iter37 实现报告(2026-10-25) + +迭代 37=**代码实现批**(分支 `iter37`,工作区 `D:/code/g-plan2-iter37`),四卡: + +| 卡 | 需求卡片 | 收口 commit | 状态 | +|---|---|---|---| +| ① 行为缺陷定点修(19 单元 d1–d19) | reports/需求卡片-game行为缺陷定点修批-2026-10-24.md | `1b9736f0`/`0529e169`/`7389618f` | 收口 | +| ② main 组合根判离(r1-1–r1-4+范围扩展) | reports/需求卡片-重构main判离批-2026-10-24.md | `b0d92524`+`c99c23fb`(.uid 边车) | 收口 | +| ③ boardview 拆件(r2-1–r2-3) | reports/需求卡片-重构boardview拆件批-2026-10-24.md | `5c9c5f80` | 收口 | +| ④ runner 拆件与断点 schema 单点(r3-1–r3-3) | reports/需求卡片-重构runner拆件与断点schema单点批-2026-10-24.md | `e5ee4d04`+件核 `48248b21` | 收口 | + +批内另两笔:`2ed81668`(九条 S13 基线外漂移全部以代码压回,基线零改动)、`84dc3e4b`(P0 重构条目登记,用户授权)。合回前 master 已并入(`d2c19791`,带入 S13 基线键治本 `37625317`)。 + +## 1. 完成定义读数(卡片「读数项非停机闸」如实申报) + +| 件 | master | 本批 HEAD | 卡片成文读数预期(非停机闸) | 达成判定 | +|---|---|---|---|---| +| `main.gd` | 1506 | **946** | ≈1370–1380(卡② §1 成文读数预期) | **超出卡面 424 行**(代拍板 B-2 扩面所致,非 r1 清单量;卡② 自注「读数项非停机闸,判离量以函数清单为准」);S13 文件线 ≤1000 另达成 | +| `views/board_view.gd` | 1219 | **937** | ≈880(卡③ §1 成文) | **未达成**(超 57 行=13 条转发属性+常量别名回填 ≈70 行);S13 文件线 ≤1000 达成 | +| `core/adventure_runner.gd` | 1050 | **989** | ≈850±10 | **未达成**(−61;委托层注释、`BREAKPOINT_FIELDS` 登记段与六小函数回填 ≈150 行,抵掉迁出 ≈183 行。公开面冻结+停点编排留宿主使迁出面本就 ≈183 行,卡片 850 预期系按 v1.0 旧边界算,读数项非停机闸) | +| `core/level_runner.gd` | 999 | **857** | ≈780 | **未达成**(−142;17 个同签名委托+2 个私有委托+8 条转发属性回填 ≈70 行) | +| 新件 `core/curio_deck.gd` | — | 259 | — | 净增 | +| 新件 `core/shop_session.gd` | — | 339 | — | 净增 | +| 新件(卡②③)`tile_palette` 261/`hero_sprite` 152/`board_resolvers` 67/`main_ui_build` 238/`main_adventure_flow` 213/`main_overlay_flow` 155/`main_debug_flow` 105 | — | 合计 1191 | — | 净增 | + +四宿主文件合计 4774→3729 行(−1045),逻辑迁入 9 个新件;**行数不是本批的验收判据,等价与单源才是**(下两节)。 + +## 2. 改动—验收对照表(实际形态) + +列义:落点=HEAD 盘面实际位置;真实入口=从生产入口进(非下层直调);撤销证明=回退本批 hunk → 断言红 → 还原复绿,等价类单元如实申报「撤不红属预期」并以等价断言承载。 + +### 卡① 行为缺陷定点修(d1–d19) + +| 单元 | 落点(实际) | 真实入口断言 | 撤销证明/等价申报 | +|---|---|---|---| +| d1 单据归属 `==`→`is_same` | core/damage_controller.gd 来源敌作废判定 | damage_controller_test 双敌两断言〔wd1〕 | 回退该 hunk → 同容异实例断言红(A 单据被 B 死亡误废),还原复绿 | +| d2 蓄力档分档 | presentation/views/standee_body.gd `_intent_charge` | standee_fx_sweep_test(新增 18 项)+theme_projection_test V4 | 回退分档 → 三档判据红;V4 断言随卡① 换代补齐 | +| d3 注释订正 | presentation/views/ui_kit.gd:28(注释面,非行为改动) | 走查 | 不适用(随 d2 复核) | +| d4 庆祝 tween 句柄 kill | battle_end_overlay.gd:281-287/156-161 | 断言②〔wd4〕standee_fx_sweep_test(`open_victory`/`close` 真实开合链) | 回退 kill 段 → 红(T2④) | +| d5–d8 互斥四口 | presentation/views/debug_overlay.gd `_sync_sim_button`+main 两守卫+`is_running()` | system_test〔wd6〕〔wd7〕 | 回退守卫 → system_test 对应行红 | +| d9 ModalStack fail-fast+遮挡账合并 | presentation/modal_stack.gd | modal_flow_test V6-5(5 项〔wd9〕) | 回退 fail-fast → V6-5 红;V6-1~4 原样同绿 | +| d10 两信号契约成文 | core/minesweeper_rules.gd 头注+全库 28 处 `#`→`##` | 契约在位扫描(信号注释载体机检) | 回退契约段 → `undocumented=2` 红,还原归零 | +| d11 数值入表(relic 基价/散折) | data/schemas/shop_table.gd:15-16 字段+:132-135 域检+data/tables/shop.tres:95-96;消费面直读 `db.shop.*`(ConfigDB 两 getter 后由 `2ed81668` 退役,读点现随 r3-2 落在 shop_session.gd:62/:192) | level_shop_economy_test 三表值断言+现价逐位等价〔wd11〕 | 等价申报:原值迁移,撤销读表 → 〔wd11〕红(−1 读数) | +| d12 target_id 产发+站桩按 id 解析 | damage_controller 四处产发/standee_body 两口 | damage_controller_test+pacing_queue_test〔wd12〕 | 回退产发 → target_id 断言红 | +| d14 目标雷数公式单点 | core/board_generator.gd `target_mine_count`+两调用点、legality 去 db 参 | board_generator_test/board_gen_reward_test〔wd14〕 | 等价申报(同式单点),撤销 → 公式断言红 | +| d15 常量化(蓄力层上限) | core/action_economy.gd:185/191-192 `CHARGE_LAYER_CAP`(两处字面量+注释随动) | 全量闸回归线〔wd15〕(finish_round_stop 真实结算链既有套件覆盖) | 等价申报(同 d14 性质) | +| d16a HeroTable.display_name 入表 | data/schemas/hero_table.gd:11+:22-23 非空域检+hero.tres:10;unit_container.gd:55 直读 `db.hero.display_name`(`hero_display_name()` 访问器已由 `2ed81668` 退役) | spawn 真实链断言〔wd16〕 | 撤销读表 → 断言红 | +| d16b 六处显式 int() 包裹 | unit_container/status_container 读侧 | 既有系统测试覆盖 | 等价申报 | +| d13 孤儿注释 | core/treasure_items.gd:55-62(注释面,非行为改动) | 走查 | 不适用(非行为改动) | +| d17 await 守卫 | presentation/views/settlement_fx_view.gd:94-95 | 走查+惯例一致性〔wd17〕(如实申报:无独立断言) | 申报豁免(T2 标注) | +| d18 参数类型注解 | `lumas_out: Variant = null` 现址 views/tile_palette.gd:134(卡① 时在 board_view:998,卡③ 随函数迁件) | S9 类型闸 `audit_type_hints.py` 该行零命中〔wd18〕 | 回退类型 → S9 R3 命中红 | +| d19 产出物 | 本批三份 reports/ 产出物(实现报告/代拍板/反哺点) | 件核对〔wd19〕 | 删一落点 → 件核红 | + +### 卡② main 组合根判离(r1-1–r1-4+范围扩展) + +| 单元 | 落点(实际) | 真实入口断言 | 撤销证明 | +|---|---|---|---| +| r1-1 数据投影五函数出根 | presentation/hud_presenter.gd:230/:245/:257/:280/:293 静态族(status_data/status_curios/career_data/save_select_data/camp_data) | **真实入口=探针开层链**:ui_struct_probe.gd:60/:94 与 ui_click_probe.gd:789 经 main 薄委托(main.gd:317/main_overlay_flow.gd:25/:39/:57/:133 直引 HudPresenter.*)。**如实更正(审计 F-1):hud_presenter_test 零引用这五个静态件,`career_data`/`save_select_data`/`bonus_summary_text` 至今无单元级断言**,建回归断言属下一批 | 删承接体 → Parse Error 红→还原复绿 | +| r1-2 只读解析回调四函数 | **新件** presentation/board_resolvers.gd(db+浮窗+结算三参注入) | ui_click_probe/system_test 棋盘读数面 | 撤销注入 → 解析 Callable 断言红 | +| r1-3 文案拼装三件 | hud_presenter.gd(gp_summary_text/bonus_summary_text/phase_text) | pacing_queue_test/player_copy_sweep | 删 `gp_summary_text` 承接体 → Parse Error+error_scan exit 1 红,还原复绿(**在案那条**) | +| r1-4 探针直引零改动 | main.gd `_camp_data`/`_status_data`/`_gp_summary_text` 三薄委托 | 探针原签名直调 | 等价申报(委托层零语义) | +| 范围扩展:`_build_ui` 八步+三流程族 | main_ui_build/main_adventure_flow/main_overlay_flow/main_debug_flow | 冒烟 17/17+probe --click --full | 逐字搬运等价;诊断转储 source 扫描断言零改动 | + +### 卡③ boardview 拆件(r2-1–r2-3) + +| 单元 | 落点(实际) | 真实入口断言 | 撤销证明 | +|---|---|---|---| +| r2-1 瓦片族出根 | **新件** views/tile_palette.gd(261 行:排版+装载+墨族判据+查表) | terrain_tile_sets_test 19 检查+ui_contrast_probe | 测试改引回退旧址符号 → `SCRIPT ERROR: Static function "_normalize_floor_luma()" not found`+error_scan exit 1,还原后 19 检查复绿 | +| r2-2 英雄族出根 | **新件** views/hero_sprite.gd(152 行,Node2D) | ui_input_probe 英雄落位/朝向/缩格重排断言 | probe --click 十八断言全绿为等价证据;撤销装配 → 落位断言红 | +| r2-3 board_view 改引 | board_view.gd 937 行(13 条转发属性+2 件引用压回 45 变量/27 公有方法) | render_gate 棋盘三图+菜单 golden changed=0.00% | 逐像素等价(golden 换代后复闸 0.00%) | + +### 卡④ runner 拆件与断点 schema 单点(r3-1–r3-3) + +| 单元 | 落点(实际) | 真实入口断言 | 撤销证明 | +|---|---|---|---| +| r3-1 令牌池与判定出根 | **新件** core/curio_deck.gd(259 行:`build/reset/token_identity/find_token/pool_stock/stock_snapshot/consume_deck_token/consume_pool_purchase/cap_ok/feat_ticket_consider/draw_candidates`);宿主 8 个同签名委托(adventure_runner 实址 :357/:444/:456/:461/:466/:635/:640/:645)+4 条转发属性 getter(:61/:64/:73/:76) | adventure_feats_test 138 项/pool_case_d 17 项/feat_vouchers 49 项/curio_refresh_paid 21 项/level_shop_economy 186 项(全部经 `start_adventure`→真链进入) | **P-C**:`_find_token` 委托体回退成 `return {}` → adventure_feats_test 成片红(「通关三选一恰呈现 2 次、恰兑付 2 次」等 6 条),还原复绿 | +| r3-2 商店族出根 | **新件** core/shop_session.gd(339 行,21 函数同形);level_runner 17 公开委托+2 私有委托(`_spend_discounted` :801、`_shop_draw_shelf` :812,测试直引面保号) | level_shop_economy_test/meta_progression_forge_test(`lr._spend_discounted(100)==80` 走件内折价) | **P-A**:`pool_price` 委托体回退成 `return -1` → `[FAIL] relic 现价 = ceil(15.0×b^(关序−1))…〔wd11〕` 红,`git checkout --` 还原后 186 检查复绿 | +| r3-3 断点字段登记单源 | adventure_runner :864 `const BREAKPOINT_FIELDS`(27 键)+写侧 `_bp_parts()` :907 +读侧 `_bp_read`/`_bp_strategy`/`_bp_read_int`;`resume_adventure` 由 108 行巨型体拆成六小函数(`_resume_ledger`/`_resume_curio_state`/`_resume_params`/`_resume_counters`/`_resume_pending` 及主体 36 行);二次消费面(`node_id`/`visited_ids`/`aux_items`)改走 `_bp_read` | **wr2 新增断言**:breakpoint_resume_test:191/:199/:206 三行——登记键集==写侧实拼键集、键集==冻结 27 键清单、策略标签全落六类词表〔wr2〕。**承力实测(审计):三条里只有「键集==冻结 27 键」在增删键时变红**,另两条因写侧与登记同源派生,删键时同步变化而不红;主线回归=breakpoint_resume_test 59 项+iter36 wc2 续玩断言(ui_click_probe「续玩重建开场行带后缀 bonus.atk=5」) | **P-B**:从登记里删 `"refresh_stock"` 一项 → `[breakpoint_resume_test] FAIL — P8 键集与冻结清单逐位一致(27 键契约;增删键即红)〔wr2〕`(1/59 失败),还原复绿 | +| 契约 5 必改接线行 | adventure_runner :745 `_runner.set_shop_pool(_curio_deck, _consume_pool_purchase, _feat_ticket_consider, _curio_cap_ok)` 四标识符改指委托/件读面,参数序不变 | **真链**=level_shop_economy_test.gd:1251(经 start_adventure→关卡推进进店);pool_case_d_test:45 自构 LevelRunner 直调 `set_shop_pool`,属下层直调,**不算本行验收入口**(审计更正) | 在 r3-1/r3-2 撤销证明覆盖面内 | + +**卡④ 撤销证明共三条**(P-A/P-B/P-C,各覆盖一单元),满足并超出卡片 T3「至少两条」。三条全为「回退本批 hunk → 断言红 → 还原复绿」,无注入式变异开关(用户 P0 令口径)。 + +## 3. 等价三证(卡④ 完成定义 2) + +### 3.1 逐字迁移核对表(两拆件族) + +- **CurioDeck**:11 个方法体逐字对齐迁出前宿主实现,仅接收者化(`_curio_deck`→`deck`、`_curio_taken`→`taken`、`_feat_ticket_*`→`ticket_*`)与 `db.adventure`/`db.feat_*` 访问面不变。**RNG 消费序逐位核对**:`draw_candidates` 内 `rng.open_stream("reward_pick")` 取流点、`_draw_exposure` 两次 `_shuffle`(全库→优秀子池)顺序、`_collect_one` 续取、`feat_ticket_consider` 的 `randi_range(1,100)` 与 pity 记账先后,全部与迁出前同一顺序同一分支;`_draw_weighted`(Boss 渠道)的 `_pool_weight`/`_weighted_index` 掷点次数与剔牌重掷路径同序。 +- **ShopSession**:21 函数同形;`set_shop_pool` 四参签名与缺省值 `Callable()` 逐字对齐(卡片 §103)。钱包与加算区经 `bind(refs, hooks)` 持**宿主对象引用**:`wallet`(`_ledger`)、`run_ledger`(账类,件内每次重读 `.bonus`,防宿主 `run_ledger.bonus = {...}` 换代失真)、`level_row`(原地 `assign`)、`tree_rules`/`attunement`;五个换代点全部重绑——level_runner :165/:205/:646/:670/:688 调 `_bind_shop()`。 +- **未迁面**:停点编排与兑付推进留宿主(`skip_empty_curio`/`pick_curio`/`_apply_curio_pick`/`_finish_curio_pick`/`present_node_curio`/`apply_node_curio`/refresh 族/`_redeem_feat_ticket`),断点五时点集零增减零迁移(A4 核:`:506/:634/:803` 现址仍在同三个函数体内)。 + +### 3.1b 卡②③ 逐字迁移核对(交付物补位,审计 F-5/F-3) + +- **交付物指认**:卡②③ 的「逐函数:原行界→新落点→逐字差异」核对结果由独立验收审计机检补上,本批不再重复制表——见 [iter37-验收审计-四卡-2026-10-25.md](file:///D:/code/g-plan2-iter37/reports/iter37-%E9%AA%8C%E6%94%B6%E5%AE%A1%E8%AE%A1-%E5%9B%9B%E5%8D%A1-2026-10-25.md) §2.2(卡② 逐函数 diff 结论)、§2.3(卡③)、§5.5(查表族与墨族真值表穷尽验证)。 +- **须申报的偏差(审计 F-3,本批此前漏报)**:卡③ r2-1 的**瓦片查表族与墨族判据不是逐字迁移,而是表驱动化重构**——`wall_tile_for`/`wall_center_tile` 改 16 掩码查表、`digit_ink_mode` 改 `_family_contrasts`/`_mean_of` 辅助式。动机=把这三个函数从 S13 函数级超标(分支 14/返回 15/局部 12)压回阈值内,属"改代码不改基线"令的推论。**等价性**:16 掩码逐位、60/20/20 档、三态真值表由审计穷尽验证零差,棋盘 golden 三图 changed=0.00%。**处置**:不回退,补代拍板 B-8 追认(卡片契约 1 字面写「禁顺手改算法」,此项越字面但未越语义)。 + +### 3.2 断点 27 键单源对照表(改前读侧实义 → 改后策略标签,逐键一致) + +| 键 | 标签 | 改前读侧实义(原 `resume_adventure`/二次消费) | 一致 | +|---|---|---|---| +| level_index | plain | `int(bp.get("level_index", 1))` | ✓ | +| node_id | plain | 二次消费 `int(_resume_bp.get("node_id", -1))` | ✓(改走 `_bp_read`) | +| hero_hp | clamp | `int(bp.get(...,0))` 入 ledger,随后越界钳到生效上限 | ✓ | +| bonus | clamp | 四子键各 `maxi(0, int(...))` | ✓ | +| gp / shop_purchased / shop_shelf / shop_visit_buys | plain | `int(...)`/`as Dictionary` duplicate/`shop_bp_*()` 取数 | ✓ | +| attunement_charges | keep_on_null | `bp.get(k, null)` + `is Dictionary` 才覆盖 | ✓ | +| graph_gen_v2 | plain | 缺键≠1 显式拒绝(代差检查) | ✓ | +| master_seed / rng_counter | plain | 直取 + `rng.restore_adventure(种子, 计数器)` | ✓ | +| keystone / visited_ids / aux_items / relics | plain | 直取/`as Array`/`restore_treasure_items(...)`/for-append | ✓ | +| bonus_taken | dual_merge | 既有 kind 键按快照清零回填+快照身份维键全量还原 | ✓ | +| bonus_deck | keep_on_null | `is Array` 才还原+relic 令牌 `feat_id` 随迁 | ✓ | +| bonus_pending | token_migration | 候选字符串→普通档令牌防御归一(relic 三段身份键随迁) | ✓ | +| feat_ticket_pity / feat_ticket_sold | plain | `int(bp.get(...,0))`(旧档缺键缺省 0) | ✓ | +| adventure_counters | keep_on_null | `is Dictionary` 才覆盖 | ✓ | +| vision_counter_used / refresh_stock | clamp | `maxi(0, int(...))` | ✓ | +| attune_refresh_y | params_fallback | `maxi(0, int(bp.get(k, int(params.get(k, 0)))))` | ✓ | +| bonus_result / keystone_drops | plain | `as Dictionary` 分支/`duplicate(true)` | ✓ | + +键序=写侧拼装序=`_bp_parts()` 键集=wr2 冻结清单,三者同源(`_snapshot_breakpoint` 按登记顺序取值,未登记键 `parts[key]` 越界 fail-fast)。 + +### 3.3 读数对拍与闸 + +见 §5。 + +## 4. 披露(本批不体面之处,逐条如实写) + +1. **计数守恒装置 25 条**:board_view 13+adventure_runner 4+level_runner 8 条转发属性,唯一作用是让 S13 基线消息里的类变量数逐字不变。**master `37625317` 基线键治本后(改小/持平不红)其存在理由已消失**,本批按用户令「先按现状收口」未返工;已立 P0 待办条目(登记时只算 12 条,漏 board_view 13 条——反哺点第 4 条补记)。判定:**处置错误**,遇「卡片契约×机检键」互斥应停卡呈报而非自拍第三条路(详见代拍板单 B-1/E-1)。 +2. **卡的行数预期未达**(adventure_runner 850±10→989、level_runner 780→857):预期按 v1.0 旧边界算,实际委托层与登记段回填 ≈220 行。卡片自注「读数项非停机闸」,不构成停机条件,但差距必须留痕。 +3. **件封装是名义的**:测试仍从宿主转发面直改件内多重集(adventure_feats_test :390-393、feat_vouchers_test :92-93、level_shop_economy_test :1351-1353)。真拆分卡片的验收面必须包含这三处直引改法。 +4. **件↔宿主方法 Callable 注入成环**(`bind_handlers`/`bind(refs,hooks)`):沿用批前既有形态(原 `set_shop_pool` 即如此),RefCounted 环不报 fatal,但件生命周期由宿主持有,属已知欠账。 +5. **golden 基线换代**(用户裁「重制三张」):成因 `2f3a87e9` 与本批零因果,判失效依据与复闸读数在 commit `7389618f`。 +6. **待办清单.md 由本批直写**:属越批性质禁写面,用户本轮明示授权且仅限这一条登记(commit `84dc3e4b`)。 +7. **等价类单元撤销不红**(卡① d11/d14/d15/d16、卡②③④ 全部委托层):属预期,以等价断言+读数对拍承载,已逐行标注。 +8. **断点「单源」的覆盖面比标称窄(审计发现,此前未披露)**:27 键里读侧真正经 `BREAKPOINT_FIELDS` 策略派生的只有 9 键(keep_on_null 三键+clamp 四键中的 `vision_counter_used`/`refresh_stock`+params_fallback 一键+二次消费三键走 `_bp_read` 默认通道)。`hero_hp` 与 `bonus` 四子键**登记为 clamp 但实际钳制写在 `_resume_ledger`/`_resume_params` 体内**(bonus 子键就地 `maxi(0,…)`、hero_hp 越界钳制依赖 `effective_max_hp` 时机),未从登记派生;`keep_on_null` 的类型守卫也部分靠调用点自身 `is Dictionary`/`is Array`。语义逐键与改前一致(审计逐表达式对拍零差),但"改登记即改行为"这条只在 9/27 上成立——拆卡重裁 BreakpointSchema 时按此读数定边界。 +9. **双写面比此前申报的多**:除 §4 第 3 条列的三处测试直引外,**生产续玩链也经转发属性写件内状态**(`_resume_curio_state` 里 `_curio_taken[id]=…` 与 `_curio_deck = restored`)。它是单点存储(属性 setter 指回件内唯一份),非双源,但"件封装名义化"的范围因此比原报更大。 +10. **wr2 三条断言只有一条承力**(见 §2 卡④ r3-3 行内实测):键名增删由「键集==冻结 27 键清单」拦住;「登记键集==写侧实拼键集」与「策略标签落六类词表」结构上与登记同源,删键不红。契约 3 的「单源键集断言」实质由冻结清单承担。 + +## 5. 实机目检总清单(真人检测只登记不阻塞) + +本批四卡零玩法语义变化,但动了呈现装配与商店/三选一接线,需真人试玩确认的项(**不写进根体验验证清单——该件属本批禁写面,是否入册由用户裁**): + +1. 棋盘观感逐像素:普通/优秀/稀有档瓦片、封印纹、数字墨族、英雄待机/行走/撞击/挖掘四态与朝向、缩格重排与缺位回退(卡③ 拆件后同链)。 +2. 商店:进店货架绘制、刷新(免费额度/付费价)、购买扣款与文案、离店再进店(货架入断点)。 +3. 三选一停点:关底通关三选一呈现、刷新钮、空池跳过、专长凭证替换、确认后回血与加算区读数。 +4. 断点续玩:中途关退出→「继续上局」→停在原节点、GP/生命/道具库存/特性/凭证限购读数一致。 +5. 卡① 指定目检两项(卡片 §8.2 末段明文要求写进本清单):蓄力敌(heavy 技能敌)意图三档观感;同名敌受击闪白与弹道落点是否落对个体。 +6. 蓄力三档观感分档、战斗结束庆祝浮动不叠加、批量模拟与调试浮窗互斥(连点不并发)。 +7. 模态层重复 open 不再吞旧账(多层叠加关闭顺序)。 + +## 6. 遗留读数义务 + +- 全量域闸(两域档)回执 `game/tests/.gates/receipt.json`:读数见 §7 与验收审计报告。 +- 本批**未跑 MC**(零数值改动,除卡① d11/d16a 入表为等值迁移),按红线「无数值改动零跑」不补跑;parity/对拍档位未动。 +- `game/tests/验证集清单.md` 断言索引未重生成(不受闸覆盖)→ 反哺点第 5 条。 +- gdstyle 基线陈旧键待人工 refresh 清理 → 反哺点第 6 条。 + +## 附录 A:断言尾缀索引(只读取证代理自 `git diff master...HEAD -- game/tests game/probe` 抽出的**本批新增行**) + +尾缀族核实:卡①=`〔wdN〕`;卡②③④ 共用 `〔wrN〕` 族,其中卡面对照表里的 wr1/wr3/wr4 是**行号标识**(非行为改动/等价行),盘面新增的带尾缀断言只有 `〔wr2〕` 三条;`wc` 族系迭代 36 旧族,本批零新增。 + +| 尾缀 | 文件:行 | 套件 | 测什么 | +|---|---|---|---| +| wd1 | unit/battle_settlement/damage_controller_test.gd:243/:253 | damage_controller_test | 双敌不误废单据/自身死亡作废对照 | +| wd2 | unit/presentation/standee_fx_sweep_test.gd:95/:97/:99/:101 | standee_fx_sweep | heavy 置蓄力档/非蓄力技零置位/增益系零置位/普攻路径复位 | +| wd2 | unit/presentation/theme_projection_test.gd:693 | theme_projection | 分档布尔判据(换代后) | +| wd4 | standee_fx_sweep_test.gd:108/:111/:114 | 同上 | 首开建句柄/重开旧句柄已 kill/close 必杀 | +| wd5 | 同上:119/:121 | 同上 | 模拟期调试钮灰/收尾恢复 | +| wd6 | tests/system_test.gd:290 | system_test | 模拟态调试守卫早退 | +| wd7 | tests/system_test.gd:300 | system_test | 关间态模拟守卫早退 | +| wd8 | standee_fx_sweep_test.gd:130/:131/:132 | 同上 | 运行中 is_running/收尾 is_running/报告注入链路 | +| wd9 | unit/presentation/modal_flow_test.gd:126/:129/:132/:134/:136 | modal_flow | open/close 未登记 fail-fast+重复 open 遮挡账三半 | +| wd11 | unit/core/level_shop_economy_test.gd:262/:263/:1295 | level_shop_economy | relic 基价表值/散折表值/现价逐位等价 | +| wd12 | damage_controller_test.gd:271/:276 | damage_controller_test | fx 载荷携 target_id/伤害行携 target_id | +| wd12 | standee_fx_sweep_test.gd:75/:76/:78/:83/:88/:91 | standee_fx_sweep | enemy_id 访问器/同名敌按 id 命中/空 id 恒不命中/闪白落点/弹道落点/无 id 名兜底 | +| wd12 | unit/presentation/pacing_queue_test.gd:136/:139/:140 | pacing_queue | 标出传 id/弹道传 id/两路各接一拍 | +| wd14 | unit/core/board_generator_test.gd:75/:81 | board_generator | 公式单点同值对拍/metrics 读数同源 | +| wd16 | level_shop_economy_test.gd:264/:268 | level_shop_economy | 英雄名表值/spawn 读表同值 | +| **wr2** | unit/core/breakpoint_resume_test.gd:191/:199/:206 | breakpoint_resume | 单源键集==写侧实拼键集/键集==27 键冻结清单/策略标签落六类词表 | + +无落码断言的卡面单元(与卡① §8.2 分行申报一致):wd3/wd10/wd13/wd15/wd17/wd18/wd19(走查、grep 静态扫描、全量闸回归线、件核对四类)。 +接线行无尾缀:tests/run_all.gd:62(standee_fx_sweep 套件登记)。注释级标记:system_test.gd:271/283/292(wd6/wd7 段头注)、adventure_feats_test.gd:218(退役符号注记,commit `48248b21`)。 + +## 附录 B:撤销证明原始读数(`[FAIL]`/`SCRIPT ERROR` 原行+还原复绿) + +| 单元 | 撤销动作 | 原始红读数(原样) | 还原复绿 | +|---|---|---|---| +| d1 | 回退 `is_same` hunk | `[FAIL] 同内容异实例敌死亡不误废他人单据〔wd1〕`+`[damage_controller_test] 1/44 项失败` | `damage_controller_test] all green (44 checks)` | +| d2 | 回退布尔分档 | `SCRIPT ERROR: Parse Error: Identifier "_intent_charge" not declared in the current scope.`(standee_body:161/166/178/179/196)+`Failed to load script ...standee_fx_sweep_test.gd with error "Compilation failed"` | `[standee_fx_sweep] all green (18 checks)` | +| d4 | 回退 kill 段 | `[FAIL] 首开胜利画面创建庆祝句柄〔wd4〕`+`[FAIL] 重开胜利画面:旧句柄已 kill、新句柄在位(至多一个活跃)〔wd4〕`+`1/18 项失败` | 同上 | +| d5 | 回退 disabled 半 | `[FAIL] 模拟运行期调试入口钮同置灰〔wd5〕`+`1/18 项失败` | 同上 | +| d8 | 回退 `is_running()` | `SCRIPT ERROR: Invalid call. Nonexistent function 'is_running' in base 'RefCounted (tune_sim_controller.gd)'.`(两次)+`[FAIL] 控制器运行中 is_running()=true(驱动回调内读数)〔wd8〕` | 同上 | +| d9 | 回退合并段 | `[modal_flow] FAIL — V6 重复 open 不吞遮挡账(close 低层恢复 mb——合并非覆盖)〔wd9〕`+`1/24 项失败` | `[modal_flow] all green (24 checks)` | +| d12 | 回退站桩按 id 半 | `[FAIL] 伤害行按 target_id 命中正确站桩(闪白落点)〔wd12〕`+`[FAIL] 弹道落点按 target_id 取站桩中心〔wd12〕`+`2/18 项失败` | 同上 | +| d10 | 回退信号契约段 | 契约在位扫描 `undocumented=2` 红 | 还原归零 | +| 卡③ r2-3 | 测试改引回退旧址符号 | `SCRIPT ERROR: Parse Error: Static function "_normalize_floor_luma()" not found in base "BoardView".` | `[terrain_tile_sets_test] all green (19 checks)` | +| 卡② r1-3 | 删 `HudPresenter.gp_summary_text` 承接体 | `SCRIPT ERROR: Parse Error: Static function "gp_summary_text()" not found in base "HudPresenter".`(main.gd:493)+冒烟 `[run_smoke] FAIL—— 退出码 = 1/冒烟失败 8 项 / 共 17 项` | **缺该撤销的复绿日志**——复绿以同批后续绿记录指认(冒烟 17/17、probe --click/--full 全绿在 commit `b0d92524` 读数段),如实申报为取证缺口 | +| 卡④ r3-2(P-A) | `pool_price` 委托体改 `return -1` | `[FAIL] relic 现价 = ceil(15.0×b^(关序−1))(读表与迁移前字面量逐位一致,-1)〔wd11〕` | `git checkout --` 后 `[level_shop_economy_test] all green (186 checks)` | +| 卡④ r3-3(P-B) | 登记里删 `"refresh_stock"` 一项 | `[breakpoint_resume_test] FAIL — P8 键集与冻结清单逐位一致(27 键契约;增删键即红)〔wr2〕`(1/59 失败) | 还原后 `PASS — …字段单源 全绿` | +| 卡④ r3-1(P-C) | `_find_token` 委托体改 `return {}` | 六条成片红:`[FAIL] 满配通关…`、`[FAIL] 通关几关 = 3`、`[FAIL] 钥石合计 = ΣBoss 首杀行值`、`[FAIL] 逐杀掉落明细恰 3 笔`、`[FAIL] 摘要携带主种子(42)`、`[FAIL] 通关三选一恰呈现 2 次、恰兑付 2 次` | 还原后 `[adventure_feats_test] all green (138 checks)` | + +## 附录 C:合回前闸况变化与修法(本次全量档首跑 84dc3e4b,四条新红已全部处置并留痕) + +| 闸 | 首跑读数 | 成因归属 | 处置 | +|---|---|---|---| +| 09 静态扫描 S11 | 禁用词命中 2 处=minesweeper_rules.gd:9/:10「雷格」 | **本批新增**(卡① d10 补信号契约注释时用词违术语表退役词对照 :143「雷格/惩罚格 → 危险格」) | 两处改「危险格」,`static_scan.sh` 单跑 `S11 ok(扫描面 237 个文件,词表 19 词)` | +| 02 单元与集成测试 | `[FAIL] V8 字符串 Callable 产品码零命中〔迭代27 T6〕`(source_guard_test 1/8) | **本批新增**(卡④ `_bind_shop()` 用三处 `Callable(self, "…")` 字符串形态,违迭代 27 T6 方法引用换装闸) | 三处改裸方法引用(Godot 4 方法名即值),source_guard_test 8/8 绿+level_shop_economy 186/curio_refresh_paid 21 绿 | +| 10 熵增闸 | 图像越界 11 个,全在 `.iter37-logs/golden_diag/*.png` | **本批在途件**(黄金图诊断临时件落仓库根临时目录,未入库) | 处置完删除整个 `.iter37-logs/` 临时目录(取证读数已转录本附录) | +| 08d 引用可核 | 红项 1=`design/gdd/systems/跨局循环/生涯.md:55` 指 `adventure_runner.gd:1007-1018`,目标现 989 行 | **本批引起**(adventure_runner 1050→989 使文档行号引用超界),但修复位在被批性质禁写的 `design/` | **待用户裁**:授权本批同步该行号(一处)/或由文档批落文(则本批无法清红)。其余八处同类漂移因同句带日期/退役说明自动降为建议级 | +| 07 doc_link | 基线红 → 现绿 | 本批无关(合并 master 带入) | 记为改善,无需处置 | + +## 附录 D:独立验收审计八项处置(审计文件=`reports/iter37-验收审计-四卡-2026-10-25.md`,判定「可以合并」) + +| # | 审计事项 | 本批处置 | 落点 | +|---|---|---|---| +| F-1 | 卡② r1-1 真实入口指认不实(hud_presenter_test 零引用五静态件) | 已更正为探针开层链+组合根直引,并记「三件至今无单元级断言」 | §2 卡② 行 | +| F-2 | 卡④ 委托/const 行号指针 8 处不准;契约 5 入口用下层直调冒充 | 委托行号订正为实址八条+四条 getter;`:857→:864`、`:909→:907`;契约 5 改指 `level_shop_economy_test.gd:1251` 真链,`pool_case_d:45` 降级为非验收入口 | §2 卡④ 行 | +| F-3 | 卡③ 查表族/墨族表驱动化重构未申报(违契约 1 字面) | 补 §3.1b 逐字偏差说明+代拍板 B-8 追认;等价性采信审计穷尽验证与 golden 0.00%,不回退 | §3.1b/代拍板 B-8 | +| F-4 | 卡① d11/d16a「ConfigDB 三访问器」被 `2ed81668` 撤销改直读行访问器 | 括注已在案(`2ed81668` 退役理由=运行时追加=双重真值);「行访问器直读是否等同单点口径」列反哺点第 9 条待文档批核 | §2 卡① 行/反哺点 9 | +| F-5 | 卡②③ 缺逐字迁移核对表交付物 | 交付物指回审计 §2.2/§2.3/§5.5(本批不重复制表),并补 §3.1b | §3.1b | +| F-6 | 读数表用机检线冒充卡片预期 | 列名改「卡片成文读数预期(非停机闸)」,main 946 判「超出卡面 424 行」、board_view 937 判「未达成(超 57 行)」 | §1 | +| F-7 | 卡① §8.3「唯一既有测试改动」与 d2×契约 5 互斥(卡片自身 spec-AC 矛盾) | 不属本批可修:V4 换代只增强不放宽(审计证实);矛盾退回写卡段,列反哺点第 7 条 | 反哺点 7 | +| F-8 | 回执钉 `9e3e2667` 落后 HEAD(纯 reports/ 追加) | 随合并 master 后的全量档复跑消解(复跑原因=master 推进,合法复跑况②) | §5 闸读数 | +| 用户裁 | 11 号回放确定性对拍 | **已获批准**:复跑带 `PARITY_APPROVED=1` 实跑三变体×20 局同种子逐字节比 | 本次复跑 | + +## 附录 E:11 号回放确定性对拍的根因定性(用户批准发起,2026-10-25 19:06–20:43 实测) + +你批准发起后跑了 `tools/parity_anchor.py check`:三票 RUNSUM 漂移+六票 JSONL 逐位漂移=**失败 9 项**,回执因此非绿。为分清"锚本来就错/需求改了没配套/实现写坏了",按脚本自带取证法(头注 15 行)做了双树原始面比对: + +| 步骤 | 读数 | +|---|---| +| A:master 树跑同参批落原始面 | `sim-data/parity-master/`(9 件,gitignored) | +| B:iter37 树跑同参批落原始面 | `sim-data/parity-iter37/`(9 件) | +| C:A vs B 逐字节比 runsum | **三份 runsum 全同** | +| D:A vs B 比 JSONL | 六份文件**行数两两全同**(9671/62766/9217/60775/7125/59436);差异行合计 17222,**全部是 `kind:"step"` 行**;差异形态只有一种——B 侧多一个键 `target_id`;**删键 0、值差 0** | + +定性结论(三类各判一次): + +1. **不是"实现不正确"**:若实现有偏差,表现为数值差或行数差。实测两树零值差、零行数差,runsum 逐字节同——本批(含卡④ 拆件与断点单源、卡②③ 判离拆件)行为侧零漂移,这比审计的静态逐表达式对拍更强一层。工具确定性也一并验过:同种子两树各跑,无任何顺序抖动。 +2. **是"原本就有问题"(历史欠账,占 3 票 RUNSUM+行数面)**:锚最后重制是 `0170071d`(2026-10-19),其 RUNSUM 行仍含 `level_templates`、不含 `level_maps`——该字段改名出自**迭代 35 卡② `9ee393a1`**(提交信息④明写「MC 出数字段 `level_templates`→`level_maps`」)。待办清单:118「旧行为 MC 行为锚全数作废——重锚归数值批」(2026-10-06 登记,状态=待文档设计)已明文规定"重锚不在改动批执行、重锚前旧读数禁止与新行为逐项比对"。⇒ 这个红对 master 一样成立,与本批无因果。 +3. **也含"需求改了出数面、锚与口径没配套"(本批侧,占 6 票 JSONL)**:17222 行差异 100% 是卡① d12 按设计新增的 `target_id` 键(damage_controller 四处产发)。卡① §8.2 只要求单元级断言(已在:damage_controller_test:271/:276 等〔wd12〕),未要求也不该在缺陷修批内重锚——但**重锚批必须把 `target_id` 纳入锚面**,否则下一次 `check` 还会因同一形态报 6 票。已列反哺点第 10 条。 + +处置建议(待你选一,见对话):A=按在册条目闭合(重跑全量档不带 PARITY_APPROVED,回执回 NEED-APPROVAL 形态,本附录与合回提交信息留归因凭据);B=批准本批重锚(`freeze` 重写 tracked 锚文件,与待办条文"重锚不在改动批执行"相违)。 + +**用户裁=B(批准本批重锚),已执行**:`python tools/parity_anchor.py freeze` 以现行行为重写 `game/tests/evidence/parity/anchor.json`。重锚后盘面对旧锚的实测差: + +| 项 | 旧锚(0170071d/2026-10-19) | 新锚(本批重制) | +|---|---|---| +| RUNSUM 出数字段 | 含 `level_templates` | 含 `level_maps`(迭代 35 卡② 改名的追认) | +| JSONL 行数面 base | normal 8945 / perfect 39811 | normal 9216 / perfect 60774 | +| JSONL 行数面 attune | normal 9620 / perfect 38435 | normal 9670 / perfect 62765 | +| JSONL 行数面 shop | normal 7499 / perfect 45132 | normal 7124 / perfect 59435 | +| 与新锚 sha 关系 | — | 六项全部与旧锚不同(`target_id` 已入锚口径) | + +新锚的行数面与附录 E 步骤 B(iter37 树原始面)逐项吻合,说明重锚取自同一确定性盘面。待办清单:118「旧行为 MC 行为锚全数作废」条已补记本次实测凭据与出账一项(对拍锚部分;T6 四带复算义务不变)。重跑全量档时 11 号将对新锚做 `check`。 + +## 附录 F:合回前终局读数(回执 commit `eb8d6073`,domain=full,all_green=true,dirty_game=false,时间 2026-10-25 21:45:47) + +| 闸 | 退出码 | +|---|---| +| 00 冒烟/01 系统测试/02 单元与集成 | 0 / 0 / 0 | +| 03 探针 --click/04 探针 --full/05 渲染闸 | 0 / 0 / 0 | +| 06 gdd_lint/06b 变更记录节/07 doc_link/08 doc_term | 0 / 0 / 0 / 0 | +| 08c 流水线闸/08d 引用可核 | 0 / 0 | +| 09 静态扫描(含合并带入的新增 S6/S8 段)/10 熵增闸 | 0 / 0 | +| **11 回放确定性对拍** | **0(✓ 全同:base/attune/shop 三变体 RUNSUM+JSONL 双面逐位一致)** | + +闸况变化记录:`07 doc_link` 基线红→现绿(合并带入,非本批)、`11 对拍` 基线红→现绿(重锚后转绿)。 + +### 收口期三次并源与一次白跑(如实留痕) + +| 时点 | 事件 | 处置 | +|---|---|---| +| 17:55 | 首次全量档(回执 `9e3e2667`) | 四条新红(S11 退役词/V8 字符串 Callable/熵增临时图/08d 行号超界),三条改码+一条经用户授权改文档 | +| 18:18 | 第二次全量档(回执 `9e3e2667` 复算) | 两域全绿,11 号未批准故 NEED-APPROVAL | +| 19:06 | 用户批准实跑 11 号 | 失败 9 项 → 归因为锚陈旧(历史)+卡① d12 新键未入锚,双树原始面实测排除实现不正确(附录 E) | +| 20:45–21:04 | 用户裁 B=本批重锚 | `freeze` 重写锚(含 `level_maps`+`target_id`),commit `bd71917a` | +| 21:16–21:20 | **白跑一次**:另一会话合入 S6/S8 闸批使 master 推进,我先误将旧跑批外壳 kill(子进程仍在)→ 两轮并发写同一 `.gates/` 盘面 | 清掉两轮共 6 个本工作区进程(另一会话进程未碰),并源 `eb8d6073` 后起唯一一轮 | +| 21:20–21:45 | 终局全量档 | 15 闸全绿,11 号对新锚逐位全同 | + +域判定依据(供合回闸核):本批 diff 同时触及 `game/`(代码域)与 `design/`·根流程清单·`reports/`(文档域),故取**全量档**(两域全跑)回执,domain=full。 + +两处越批性质禁写面均已获用户当场授权并在提交信息留痕:`待办清单.md`(P0 条目登记与更正,commits `84dc3e4b`/`947664b6`/`bd71917a`)、`design/gdd/systems/跨局循环/生涯.md`(一处行号同步,commit `9e3e2667`)。 diff --git a/reports/iter37-拆分卡缺口-2026-10-25.md b/reports/iter37-拆分卡缺口-2026-10-25.md new file mode 100644 index 00000000..15b01328 --- /dev/null +++ b/reports/iter37-拆分卡缺口-2026-10-25.md @@ -0,0 +1,47 @@ +# iter37 → P0 拆分卡缺口补记(2026-10-25) + +给写卡段(`req-card`)的输入。**不是需求卡片**,也**不是本批返工清单**——本批四卡已按用户令收口,这份只把 P0 条目「AdventureRunner 职责真拆分」的四件缺口补成可写卡形态。落点读数以 HEAD `9e3e2667` 盘面为准。 + +## 1. 现状读数(为什么不能只删转发属性) + +| 面 | 现值 | S13 阈值 | 只删转发属性后 | +|---|---|---|---| +| `adventure_runner` 类变量 | 42 | ≤15(超出走基线登记) | 38(−4)——**仍远高于阈值**,红不红取决于登记值,债没还 | +| `adventure_runner` 公开方法 | 23 | ≤20 | 23(**一件没减**,委托层全是公开/私有方法) | +| `level_runner` 类变量 | 38 | ≤15 | 30 | +| `level_runner` 公开方法 | 54 | ≤20 | 54 | +| `board_view` 类变量 | 45 | ≤15 | 32(board_view 侧同类转发属性 13 条,卡③ 产物,同一债种) | + +结论:**撤装置=还一半债(计数改善,新键形态不判红),但职责混合一点没解**。真拆分才可能压到阈值内。 + +## 2. 按变化原因的件边界(待裁案的起点,非结论) + +| 件 | 应收的成员(现宿主址) | 应收的方法 | 变化原因 | +|---|---|---|---| +| `AdventureFlow`(留名 adventure_runner) | `state`/`level_index`/`levels_total`/`_runner`/`_pending_next_level`/`_last_level_payload` | `start_adventure`/`resume_adventure`/`_start_current_level`/`_bind_level_runner`/`_inject_level_params`/`_present_level_entry`/`_on_level_finished`/`_settle`/`continue_to_next_level`/`abandon`/`runner` | 关序推进与结算受理一次 | +| `RewardStop` | `_curio_pending`/`_curio_result`/`_node_curio_pending`/`_attune_refresh_x`/`_refresh_stock`/`_pending_rap`/`_time_sand_queue`/`_curio_paid_refreshes` | `pick_curio`/`apply_node_curio`/`present_node_curio`/`node_curio_candidates`/`skip_empty_curio`/`_apply_curio_pick`/`_finish_curio_pick`/`_finish_node_bonus`/`_redeem_feat_ticket`/refresh 族五件/`_feat_payload_keys` | 奖励怎么给、怎么刷、怎么确认 | +| `CurioDeck`(已建) | 池与凭证计数(已入件) | 已入件 | 池规则 | +| `HeroLedger` | `ledger`/`run_ledger`/`_base_override`/`_override`/`purchased`/`keystone`/`_feats` | `effective_max_hp`/`apply_rest`/`set_hero_override`/`feats_held`/`_seed_attune_charges` | 数值叠加与生命口径 | +| `BreakpointSchema` | `BREAKPOINT_FIELDS`(const 可留宿主,派生段出根) | `_snapshot_breakpoint`/`_bp_parts`/`_bp_read`/`_bp_read_int`/`_bp_strategy`/六个 `_resume_*` | 存档格式 | +| 呈现接线 | 无 | `_forward_*` 六件+信号面 | UI 事件形状(候选下沉装配层,见缺口④) | + +## 3. 写卡前必须裁的五件 + +1. **RewardStop ↔ CurioDeck 分界**:停点三态归 RewardStop 还是 CurioDeck?`_redeem_feat_ticket`(兑付推进,且体内有断点时点)在谁侧——现方案是宿主用 `bind_handlers` 把 `_apply_curio_pick`/`_redeem_feat_ticket` 反向插进 CurioDeck,件与宿主双向拉扯,必须定一个方向。 +2. **HeroLedger 与既有 `RunLedger` 的关系**:`run_ledger` 已存在且被 LevelRunner 共享引用(`set_run_ledger` 下传)。是把 `ledger`(hero_hp/gp)并进 RunLedger,还是新立 HeroLedger 包两只?并=共享引用面重划;新立=多一层。 +3. **BreakpointSchema 独立成件的取舍要重裁**:卡④ §11 以「27 键多数取宿主态、开洞面>收益」淘汰过独立件。真拆分后键值来源收敛到三个件(Flow/RewardStop/Ledger),该论据部分失效——重裁时须给「开洞面」的新读数。 +4. **公开面收缩与调用方改动一并算进验收**:main.gd(`start_adventure`/`resume_adventure`/`continue_to_next_level`/`pick_curio`/`apply_node_curio`/refresh 面/`runner`/`feats_held`)、`camp_shop_presenter`、`curio_interact_controller`、`ui_click_probe`(iter36 wc2 续玩断言)、五套测试的直引点(含从转发面直改件内状态的三处:`adventure_feats_test.gd:390-393`、`feat_vouchers_test.gd:92-93`、`level_shop_economy_test.gd:1351-1353`)。 +5. **撤装置必须与拆件同批**:单独撤 25 条转发属性=测试成片红(它们靠转发面写件内状态)。卡片需把「测试改法」列为改动单元并各配真实入口断言,否则该批无法闭合。 + +## 4. 验收面预案(供卡片 §8.2 直接展开) + +- 每新件至少一条真实入口断言:`RewardStop`=走 `start_adventure`→通关三选一→选一件→断言加算区/池余量/停点态清零;`HeroLedger`=rest 与 heal 封顶走真链;`BreakpointSchema`=已有〔wr2〕三条+续玩真链(`ui_click_probe` wc2 段)。 +- 撤销证明:每单元至少一条「回退本批 hunk → 断言红 → 还原复绿」;禁注入式变异(P0 令)。 +- 等价面:断点 27 键键名/缺省/回退策略逐键冻结;RNG 流名与消费序(`reward_pick`)零漂移;MC/11 对拍是否发起由用户裁(触及存档/RNG/结算面)。 +- 基线:`tools/gdstyle_baseline.txt` 禁改;计数下降属改善,新键形态下不判红(`37625317` 已治本)。 + +## 5. 本批随批留痕的相关条目 + +- 待办清单 P0 条目(`84dc3e4b`)——覆盖面待补 board_view 13 条(反哺点第 4 条)。 +- 代拍板单 B-1/E-1——转发属性装置的成因与认错。 +- 实现报告 §4 第 1/3 条——装置清单与「件封装是名义的」实证。 diff --git a/reports/iter37-验收审计-四卡-2026-10-25.md b/reports/iter37-验收审计-四卡-2026-10-25.md new file mode 100644 index 00000000..91e02f3b --- /dev/null +++ b/reports/iter37-验收审计-四卡-2026-10-25.md @@ -0,0 +1,273 @@ +# 验收审计:iter37 四卡(独立审计方,2026-10-25) + +> **性质**:合回前独立验收审计(test-conventions 撤销证明与验收闭合计口径;卡① §8.3/卡②③④ §8 末段规定的合并前置件)。审计方未参与实现。 +> **被审对象**:worktree `D:/code/g-plan2-iter37`(分支 `iter37`),`git diff master...HEAD`=15 commits/75 文件。开工指令记为 14 commits/74 文件;审计期间并行会话追加 `356029d0`(只写 `reports/iter37-拆分卡缺口-2026-10-25.md`,零代码面)。 +> **输入**:四张需求卡片+`reports/iter37-实现报告-2026-10-25.md`(§2 对照表/§3 等价三证/附录 A/B/C)+代拍板单+反哺点清单+闸回执 `game/tests/.gates/receipt.json`(commit `9e3e2667`,domain=full,all_green=true,dirty_game=false)。 +> **方式**:全部结论自己取证——逐行 grep 盘面、`git show master:` 逐函数对拍(含 python 机检对键序/取值表达式/掩码表穷尽比对)、单件直跑 5 个套件+一次自跑撤销证明。报告里写「绿」不采信。零全量闸重跑、零生产码改动。 + +--- + +## 一、总判定 + +**可以合并**——四卡验收面闭合:卡① 19 单元全部在盘且有断言,卡②③④ 等价迁移经本审计独立证实(含 RNG 调用序与断点 27 键逐表达式对拍),零测试削弱,卡④ P-B 撤销证明本审计实跑复现成功且还原复绿。 + +合回前三件事必须做(都不动生产码): + +1. **回执与 HEAD 不同位**:回执钉 `9e3e2667`,HEAD 现为 `356029d0`(追加的 reports/ 文档件)。合回前按域重跑所选档一次,或由 guard 明确接受「回执 commit 落后一个纯文档提交」。 +2. **实现报告三处指认须更正/补申报**:见第六节 F-1/F-2/F-3(一处真实入口指认不实、一处机检线冒充卡片预期、一处未申报的查表算法重构需追加代拍板追认)。 +3. **呈报 11 号闸(回放确定性对拍)是否发起**:它是卡④「RNG 消费序不漂」的最强独立判据,回执记 NEED-APPROVAL 未跑;本审计用静态对拍替代(结论=同序),但档位发起权归用户。 + +--- + +## 二、检查一:改动—验收对照表逐行核对 + +### 2.1 卡① d1–d19(实现报告 §2 表 + 附录 A 尾缀索引) + +判定列=盘面实据是否存在+入口是否真链。行号一律给 HEAD 实测值。 + +| 单元 | 盘面实据 | 判定 | +|---|---|---| +| d1 `is_same` | `game/core/damage_controller.gd:216` 判定符已改;断言 `game/tests/unit/battle_settlement/damage_controller_test.gd:243`/`:253`〔wd1〕,链路 `open_ticket→submit→units.unit_died.emit→flush→outcome()` 全真链 | 通过 | +| d2 蓄力分档 | `game/presentation/views/standee_body.gd:33`(`_intent_charge` 成员)/`:178-179`(`_intent_charge = pid in CHARGE_SKILL_IDS`)/`:196`(`if _intent_charge:`);断言 `standee_fx_sweep_test.gd:95/97/99/101`+`theme_projection_test.gd:693` | 通过(入口深度见 4.3) | +| d3 注释订正 | `game/presentation/views/ui_kit.gd:28` 失真引述已整段删除 | 通过(注释面,无断言属预期) | +| d4 庆祝句柄 | `battle_end_overlay.gd:161-164`(close kill+remove_meta)/`:288-292`(先 kill 后建);断言 `standee_fx_sweep_test.gd:108/111/114`〔wd4〕绑 Tween 实例 `is_valid()` | 通过(报告记 `:281-287/156-161`,现址 `:285-295/158-164`=指针漂移) | +| d5–d8 互斥四口 | `debug_overlay.gd:104`(`_dbg_btn.disabled` 并入 `_sync_sim_button`,且 `:84` 原直写已删);`tune_sim_controller.gd:14/32-33/36/70`(`_running`+`is_running()`+run 首尾置位);两守卫在 `main_debug_flow.gd:84`(d6 补 `is_running()`)与 `:72-79`(d7 补 `is_adventure`),main 留同签名委托 `main.gd:423-424/928-929`;断言 `standee_fx_sweep_test.gd:119/121`〔wd5〕`:130/131/132`〔wd8〕+`system_test.gd:290`〔wd6〕`:300`〔wd7〕 | 通过。system_test 从 `main._start_debug_levels()`/`main._on_tune_simulate(3)` 进入=信号实际接线的入口(`main_ui_build.gd:196-197`),非下层直调 | +| d9 模态 fail-fast+合并 | `modal_stack.gd:22-25`(open 未登记 push_error+return)/`:28-38`(旧账∪新隐合并)/`:41-44`(close 未登记守卫);断言 `modal_flow_test.gd:126/129/132/134/136`〔wd9〕;V6-1~4 在役序列 `:40-48` 原样保留(diff 零触碰该段) | 通过 | +| d10 信号契约 | `minesweeper_rules.gd:8-16` 两契约按 §2.1 三要素成文;全库扫描复现:`grep -rnP "^\s*signal\s+\w+\(.*\)\s*#(?!#)" game/` =**零命中** | 通过 | +| d11 数值入表 | `data/schemas/shop_table.gd:15-16` 两 knob+`:132-135` 两域检(报告记 `:132-135`=精确);`data/tables/shop.tres:95-96` 值 15.0/0.8;消费点 `core/shop_session.gd:62`(spend_discount)/`:192`(relic_base_price),报告注「读点随 r3-2 落在 shop_session:62/:192」=精确 | 通过(ConfigDB 三访问器被 `2ed81668` 退役=卡片 d11 改法偏离,见 F-4) | +| d12 target_id | 产发四处 `damage_controller.gd:169/371/401/505`;两口解析 `battle_stage_view.gd:308-310`(on_step_row_fx,id 先名兜底)/`:357-358`(enemy_standee_center 改签名);`row_pacer.gd:111/116/137/145`;`standee_body.gd:70` `enemy_id()`;断言三宿主共 11 行〔wd12〕 | 通过(报告/卡片记 :168/370/400/504/:342 全系 ±1~15 漂移) | +| d13 孤儿注释 | `treasure_items.gd:51-56` 现仅 `inventory_count()` 与 `_usable_now` 头注,master :55-62 三段孤儿注释(持有数/同调位守卫谓词/持有件记录/丢弃)已删 | 通过 | +| d14 公式单点 | `board_generator.gd:14` `static func target_mine_count`;两调用点 `:102`/`:339`;`:286 legality(b, params)` 去 db 参;调用点两处同批改(`:53` 内部+`board_gen_reward_test.gd:160`) | 通过(卡片记 test:145=master 行号,现 160) | +| d15 常量化 | `action_economy.gd:14` `const CHARGE_LAYER_CAP := 3`;`:192/193` 两处改引;`:186` 注释随动 | 通过 | +| d16a 英雄名入表 | `hero_table.gd:11` 字段+`:22-23` 非空域检;`hero.tres:10`;消费 `unit_container.gd:55` 直读 `String(db.hero.display_name)` | 通过 | +| d16b int() 包裹 | `unit_container.gd`/`status_container.gd` 读侧包裹在位,由全量闸回归线承载(等价申报) | 通过(等价类,卡片即如此申报) | +| d17 await 守卫 | `settlement_fx_view.gd:95-96` | 通过(无独立断言=卡片 §8.2 已申报豁免) | +| d18 参数类型 | `tile_palette.gd:134 lumas_out: Variant = null`(报告记现址 :134=精确);本审计实跑 `python tools/audit_type_hints.py` → `[audit_type_hints] PASS——产品码类型闸归零(R1/R2/R3)` | 通过 | +| d19 产出物 | `reports/` 三件在盘(实现报告/代拍板/反哺点) | 通过 | + +### 2.2 卡② r1-1–r1-4+范围扩展 + +| 单元 | 盘面实据 | 判定 | +|---|---|---| +| r1-1 投影五函数 | `hud_presenter.gd:230/245/257/280/293` 五静态函数在盘;逐行对拍 master `main.gd` 原函数体=除接收者/参数化外**逐字一致**(本审计机检逐函数 diff:`status_curios` 仅成员→参、`career_data`/`save_select_data` 仅签名行变化、`status_data` 仅 `run_scope.runner`→`runner` 等 7 处参数化) | 断言入口不实:报告记「hud_presenter_test+ui_struct_probe 直调面」,`tests/unit/presentation/hud_presenter_test.gd` 全文零引用这五个新静态件(只测 outcome_text/board_variant_lines)。真实承载面=`probe/ui_click_probe.gd:789`(sv7 `_status_data()` 八键对拍)+`probe/ui_struct_probe.gd:60/94`(`_open_career`/`_open_save_select` 真开层链)+冒烟/系统测试。判 **通过(附更正)**,见 F-1 | +| r1-2 解析回调四函数 | 新件 `presentation/board_resolvers.gd`(67 行)四函数齐(`debug_hero_override`/`identity_lookup`/`variant_mask_lookup`/`variant_mark_lookup`);对拍 master `main.gd:753-797`=除 `run_scope.settlement`→`_settlement_getter.call()` 外逐字一致;注入点 `main_ui_build.gd:200-205`(三参注入,与卡片 v1.2 接口块一致) | 通过 | +| r1-3 文案三件 | `hud_presenter.gd:302/310/325`;逐字对拍同上(`phase_text` 仅四成员改参);main 消费点 `main.gd:493/470/476/858` | 通过 | +| r1-4 探针直引零改动 | `main.gd:492`(`_gp_summary_text`→HudPresenter)、`:659`(`_status_data`→**MainOverlayFlow**)、`:726`(`_camp_data`→**MainOverlayFlow**);五处探针直引原行号原样在盘(ui_click_probe:74/101/112/789、ui_struct_probe:244),两探针文件本批零改动(不在 diff 内) | 通过(形态偏差:卡片接口块写「三件薄委托零参转发 HudPresenter」,实为两跳委托 main→main_overlay_flow→HudPresenter;语义等价,卡片代拍板白名单外但 commit b0d92524 与 §2 已记「三薄委托」,判为措辞不精确) | +| 范围扩展四件 | `main_ui_build.gd` 238/`main_adventure_flow.gd` 213/`main_overlay_flow.gd` 155/`main_debug_flow.gd` 105 行;main 1506→946;冒烟 17/17 与 probe --click/--full 读数在 commit `b0d92524` 消息在案,回执 00/03/04 号闸 green | 通过(属代拍板 B-2,卡片 §5「不做」明写 `_build_ui` 留驻——超出卡片范围已由代拍板单申报待追认,本审计不判越权) | + +### 2.3 卡③ r2-1–r2-3 + +| 单元 | 盘面实据 | 判定 | +|---|---|---| +| r2-1 瓦片族出根 | `views/tile_palette.gd`(261 行):装载缓存 7 成员(`:45-53`)+六 static(`flip_tex:123`/`normalize_floor_luma:134`/`srgb_lin:160`/`digit_ink_mode:170`/`contrast_y:198`/`ink_y:203`)+查表族(`is_wall:219`/`wall_tile_for:242`/`wall_center_tile:250`/`floor_tile_for:260`)+棋盘态注入 `bind_board:60`;`terrain_tile_sets_test.gd` 19 检查本审计实跑绿;`ui_contrast_probe.gd:140-150` 改引 `TilePalette.*` | 通过,**附未申报的算法重构**(见 5.5 与 F-3) | +| r2-2 英雄族出根 | `views/hero_sprite.gd`(152 行,`extends Node2D` 符合卡片契约 4 硬约束)八函数齐(`update_hero:52`/`place_hero:75`/`frame_flash:99`/`notify_move:116`/`play_dig_bounce:122`/`feet:139`/`hand_point:146`/`sprite_scale:151`);board_view `:158-160` bind+`add_child`,`:397/403/409/414/426/431` 同签名委托;真实入口断言在 `probe/ui_input_probe.gd:293-352`(case⑦ 落位 ≤0.5px/case⑧ 材质链/case⑨ 纹理朝向/case⑩ 缩格重排与复位),该文件本批零改动 | 通过 | +| r2-3 board_view 改引 | `board_view.gd` 937 行;13 条转发属性实测在 `:84-124`(7 条指 `_palette`、6 条指 `_hero_sprite`);`HERO_SPRITE_SCALE:44`/`HERO_GROUND_FRAC:45` 为指向件常量的**别名**(非重复字面值,无双源);旧址符号 `_normalize_floor_luma`/`_digit_ink_mode`/`_wall_center_tile`/`_hero_feet` 在 board_view 已不存在(本审计 grep 证实)→ 卡③ T2 规定的撤销形态成立 | 通过 | + +### 2.4 卡④ r3-1–r3-3+契约 5 + +| 单元 | 盘面实据 | 判定 | +|---|---|---| +| r3-1 令牌池出根 | `core/curio_deck.gd` 259 行,卡片点名的 11 方法全在盘(`build:39`/`reset:31`/`token_identity:53`/`find_token:60`/`pool_stock:73`/`stock_snapshot:83`/`consume_deck_token:92`/`consume_pool_purchase:107`/`cap_ok:118`/`feat_ticket_consider:132`/`draw_candidates:150`)+四个内部helper;宿主委托实测 8 处:`adventure_runner.gd:356/443/455/460/465/634/639/644`(报告记 `:355/:443/:457/:465/:471/:641/:647/:654`,其中 :471 指向 `_finish_curio_pick`=非委托,:457/:641/:647 为空行,:654 在 `_redeem_feat_ticket` 体内);套件实跑 `adventure_feats_test`=138 checks 全绿,入口为 `start_adventure` 真链 | 通过(委托行号指针不实,见 F-2) | +| r3-2 商店族出根 | `core/shop_session.gd` 339 行 21 函数同形;宿主委托 17 公开+2 私有=`level_runner.gd:780/784/788/792/796/801/807/812/816/820/824/828/832/836/840/844/848/852/856`(报告记 `_spend_discounted:801`、`_shop_draw_shelf:812`=精确);`set_shop_pool` 四参签名与缺省 `Callable()` 逐字对齐;实跑 `level_shop_economy_test`=186 checks 全绿;`meta_progression_forge_test.gd:733/736` 经宿主私有委托走件内折价(`=80`/`=1` 下限)在盘 | 通过 | +| r3-3 断点单源 | `adventure_runner.gd:864 const BREAKPOINT_FIELDS`(27 键)+`_bp_parts():907`+`_bp_read():951`/`_bp_strategy():961`/`_bp_read_int():966`;`resume_adventure` 现 36 行(`:149-184`)+五小函数(`:188/204/230/252/264`);报告记 `:857`/`:909`=漂移 7/2 行。二次消费面 `:705-707` 与 `:762` 改走 `_bp_read`(node_id/visited_ids/aux_items)在盘。断言 `breakpoint_resume_test.gd:191/199/206`〔wr2〕三行在盘 | 通过(单源覆盖面见 5.2) | +| 契约 5 接线行 | `adventure_runner.gd:745` `_runner.set_shop_pool(_curio_deck, _consume_pool_purchase, _feat_ticket_consider, _curio_cap_ok)`——报告记 `:745`=精确,四标识符全指委托/转发面,参数序不变 | 通过(入口标签不精确:报告写「pool_case_d_test 直调 set_shop_pool」,该处 `tests/unit/core/pool_case_d_test.gd:42-44` 自构 `LevelRunner` 直调=下层直调;接线行的真链覆盖在 `tests/unit/core/level_shop_economy_test.gd:1251 start_adventure(seed, {"shop_mode":"pool"})`。判 **通过(附更正)**) | + +### 2.5 附录 A 断言尾缀索引核对 + +本审计用 `git diff master...HEAD -- game/tests game/probe` 抽出全部新增尾缀,与附录 A 逐行对: + +- 新增带尾缀行计数=wd1×2/wd2×5/wd4×3/wd5×2/wd6×1/wd7×1/wd8×3/wd9×5/wd11×3/wd12×11/wd14×2/wd16×2/wr2×3,共 44 行;**附录 A 的 44 个 `文件:行` 指针全部命中,无一空指**(盘面 grep 复算见第二节各行)。 +- 删除行中的尾缀计数=**0**(`git diff ... | grep "^-" | grep 〔w*〕` 空输出)→ 无断言被删。 +- 「盘面新增带尾缀断言只有〔wr2〕三条」=属实(全库 grep 〔wr1〕/〔wr3〕/〔wr4〕 零命中)。推论:卡②③④ §8 表里挂在 wr1/wr3/wr4 行的「验收测试」从未以尾缀断言落码,由闸读数与件核承载——报告如实说明其语义,但卡片把闸读数写成尾缀断言属上游口径混用,记入第七节未覆盖面旁的观察。 + +--- + +## 三、检查二:撤销证明抽查(四条) + +### 3.1 卡① d1(证据复核) + +commit `1b9736f0` 消息明文记录「回退 d1 hunk → 同容异实例断言红(A 单据被 B 死亡误废),还原复绿」,与实现报告附录 B 首行读数(`[FAIL] …〔wd1〕`+`1/44 项失败`→`all green (44 checks)`)一致。可核性独立成立:判定符现值在 `damage_controller.gd:216`,回退即回到 `==`;夹具断言的判别力由 `damage_controller_test.gd:231` 前置检查(`a == b and not is_same(a, b)`)保证扫描面非空——非空转绿结构成立。 + +**判定:通过(取证级,本审计未重跑)。** + +### 3.2 卡③ r2-3(证据复核+结构可核性) + +commit `5c9c5f80` 消息逐字记录了与报告附录 B 相同的红行与复绿读数(19 检查复绿+error_scan exit 0)。本审计独立核其结构前提:`BoardView` 现盘面**不存在** `_normalize_floor_luma` 静态函数(grep 证实),故「测试改引回退旧址」必然产生 `Parse Error: Static function "_normalize_floor_luma()" not found in base "BoardView"` → 该红不由注入造成,属回退本批 hunk 的自然后果。 + +**判定:通过。** 性质提示:它证的是「旧址符号已消亡」,等价性判据本体在 render_gate(棋盘三图 changed=0.00%,且基线由卡① T3 `7389618f` 换代、早于卡②③——所以卡③ 的 0.00% 读数是相对自身改动量的有效对拍)。 + +### 3.3 卡② r1-3(证据复核+取证缺口属实) + +结构前提本审计已核:`main.gd:493` 直呼 `HudPresenter.gp_summary_text(...)`,删承接体必致 Parse Error(报告记红行指向 main.gd:493=精确)。commit `b0d92524` 消息把撤销证明写作「→Parse Error 红→还原复绿」在案,而报告附录 B 该行自报「**缺该撤销的复绿日志**」。两处口径不一致,报告的下调比 commit 消息诚实。 + +缺口现由批级回执补上一半:回执 `00 冒烟`/`03 探针 --click`/`04 探针 --full` 在 HEAD 全绿,可作「还原后盘面复绿」的指认,但不是逐 hunk 复绿原始日志。 + +**判定:通过(附取证缺口申报,报告已自报)。** + +### 3.4 卡④ P-B(本审计实跑复现) + +动作:删 `game/core/adventure_runner.gd` 第 888 行 `"refresh_stock": {"strategy": "clamp"},` 一行 → 单件直跑 → `git checkout --` 还原 → 再跑。 + +变异后原始输出(红行原样,未加未减): + +``` +[breakpoint_resume_test] FAIL — P8 键集与冻结清单逐位一致(27 键契约;增删键即红)〔wr2〕 +ERROR: [breakpoint_resume_test] FAIL — P8 键集与冻结清单逐位一致(27 键契约;增删键即红)〔wr2〕 +ERROR: [breakpoint_resume_test] 1/59 项失败 +``` + +还原后(同命令再跑): + +``` +[breakpoint_resume_test] PASS — 断点往返/续玩确定性/幂等/退出清断点/装配存档回归/生成图换代/字段单源 全绿 +``` + +`git status --short` 输出为空(本审计自身零残留)。还原前变异态未提交,还原用 `git checkout --` 只回退该一个文件,未触碰其他改动。 + +读数与报告附录 B 记的红行/计数(1/59)**逐字一致**,且形态合规:回退本批 hunk、非注入式开关。 + +附带观察(不判缺陷,供写卡段参考):三条 wr2 断言里只有「键集==冻结 27 清单」承力。删一个登记项时,「登记键集==写侧实拼键集」不红——因为 `bp[key]` 由遍历 `BREAKPOINT_FIELDS` 生成,两侧同源,该断言结构上只能发现「登记了但 `_bp_parts` 没拼」的单向偏差;「策略标签落词表」同理对已登记键恒真。 + +**判定:通过(实跑复现)。** + +--- + +## 四、检查三:测试削弱审查 + +### 4.1 删除行全清单(`git diff master...HEAD -- game/tests game/probe`,24 行删除逐条归位) + +| 文件 | 删行 | 性质 | 判定 | +|---|---|---|---| +| `game/probe/ui_contrast_probe.gd` | 4 | `BoardView.*`→`TilePalette.*` 换址(常量×2+注释+静态调用),判据零变 | 非削弱 | +| `tests/unit/core/adventure_feats_test.gd` | 1 | 注释级退役符号注记(`_token_identity`→`CurioDeck.token_identity`),commit `48248b21` 申报 | 非削弱 | +| `tests/unit/core/board_gen_reward_test.gd` | 1 | `legality(b1, row, _db)`→`legality(b1, row)`,卡片 §8.3 申报在案 | 非削弱 | +| `tests/unit/core/breakpoint_resume_test.gd` | 1 | `Kit.finish` 的 PASS 消息串追加「字段单源」四字 | 非削弱 | +| `tests/unit/core/level_shop_economy_test.gd` | 2 | ①注释「15 固定锚」→「表 knob」;②`expect_p = int(ceil(15.0*…))`→`int(ceil(float(_db.shop.relic_base_price)*…))` | 判据口径改动,**非削弱**:同文件新增 `:1293-1295` 用硬编码 15.0 反算 `relic_price_pre` 并与件内 `pool_price("relic")` 逐位对拍〔wd11〕+`:262` 钉表值==15.0,硬锚未丢 | +| `tests/unit/presentation/terrain_tile_sets_test.gd` | 11 | 全部是 `BoardViewScript.`→`TilePaletteScript.` 换址(含 `TILE_*`/`DIGIT_INK_CR_*` 常量直读与两次静态调用),断言表达式逐字不变 | 非削弱 | +| `tests/unit/presentation/theme_projection_test.gd` | 4 | ①`:693` V4 断言判据换代;②`:810/:825` 两处注释同构引用改址 | 见 4.2 | +| 三张 `golden/board_L*.png` | 二进制 | 基线换代:成因 `2f3a87e9`(与本批零因果)+用户裁「重制三张」+`tests/golden/README.md` 追加判定记录(6 行,卡① 契约 6 要求的显式呈报在案) | 非削弱(申报齐) | + +阈值/扫描面类改动零处:`run_all.gd` 只加一行套件登记(`:62`);`error_scan`/`audit_type_hints`/`gdstyle_baseline.txt`/`tools/` 全目录零改动(`git diff --name-only master...HEAD -- tools/` 空输出)。 + +### 4.2 卡① §8.3 那句话的属实性 + +卡片原文:「零削弱;唯一既有测试改动=board_gen_reward_test:145/board_generator_test 的 `legality` 调用签名随 d14 同批改;既有断言零删除零放宽」。 + +- 「零削弱」「既有断言零删除」——**属实**(见 4.1 全清单+删除行尾缀计数 0)。 +- 「唯一既有测试改动」——**不成立**。卡① 自己触发的既有断言改动还有一处:`theme_projection_test.gd:693` V4 由「断言旧 GOLD_DEEP 判据在位」换成「断言分档布尔在位**且**旧判据消失」。该改动在 commit `2ed81668` 消息与实现报告 §2 d2 行(「V4 断言随卡① 换代补齐」)有申报,但不在卡片 §8.3 的申报句里。 +- 深层原因是卡片自相矛盾:d2 改法明写「颜色比较判据删除」,契约 5 又写「既有断言零删除零放宽」,而 V4 断言的正是那条待删判据的存在——两条约束在同一个输入上给不出同一个答案(agents.md「spec-AC 一致性」红线)。实现段的处理(判据换代、只增强不放宽、留痕)是这一矛盾下唯一不破红线的走法。 + +**未申报的其余改动:无。** 除上表 8 个文件面外,`game/tests`/`game/probe` 无其他既有件被触碰。 + +### 4.3 `standee_fx_sweep_test.gd` 断言绑面审查 + +| 断言族 | 绑的面 | 判定 | +|---|---|---| +| wd12 站桩两口(`:81-91`) | 经真实解析口 `stage.on_step_row_fx(...)`/`stage.enemy_standee_center(...)` 驱动,判据=**被闪白的站桩是哪一枚**(`s2._flash_count==1 and s1._flash_count==0`)与中心点等值(`world_to_shell(s2.position+s2.box*0.5)`)。`_flash_count` 是 master 既有的探针出口计数(`standee_body.gd:260`,非本批植入的观测桩) | 绑提交面——兜底路径(名匹配)若胜出,闪白必落 s1,断言即红 | +| wd2 蓄力四断言(`:95-101`) | 读 `set_pending` 写入的判定标志位 `_intent_charge` | 绑决策落点,**未绑运动学后果**(③蓄力缓推分支实际入 tween 未取证);配合 `theme_projection_test:693` 的源码文本扫描,d2 的行为面缺一条可观测断言。卡片 §8.2 wd2 规定的是「真实调用链+回退红」,实现按卡执行——记为上游判据设计面,非实现段削弱 | +| wd4 句柄三断言(`:108-114`) | 绑真 Tween 实例生命周期(首建 `is_valid()`/重开后旧句柄 `not is_valid()`/close 后元数据清空),经真实 `open_victory`/`close` 开合链 | 绑提交面,判别力强 | +| wd5/wd8 互斥断言(`:119-121`、`:130-132`) | wd5 读真控件属性 `_dbg_btn.disabled`;wd8 在注入 driver 的回调内取 `is_running()` 读数(`drv.seen == [true, true]`)+收尾面板态 | 绑提交面;driver 注入位是 master 既有面(`tune_sim_controller.gd:_init(..., driver = null)`),非本批造桩 | + +--- + +## 五、检查四:卡④ 等价性专查(本批最大风险面) + +### 5.1 两拆件族逐字对比(对 `git show master:` 原实现) + +- **CurioDeck**:11 个迁出方法与 master `adventure_runner.gd` 同族实现**逐表达式一致**(接收者化:`_curio_deck`→`deck`、`_curio_taken`→`taken`、`_feat_ticket_*`→`ticket_*`)。`consume_pool_purchase` 的未知 kind 拒绝分支(`curio_deck.gd:110-112` push_error+false)与 master `:478-480` 逐字同;`feat_ticket_consider`(`:132-143`)与 master `:775-786` 同——限购先返→配额先返→`pity >= step or rg.randi_range(1,100) <= chance`(短路保留:保底命中时**不掷点**)→命中清零 pity 并 `appeared += 1`/否则 `pity += 1`,记账先后零变。 +- **`reset()` 不清凭证三计数**:属实且正确——master `_reset_curio_state`(`:250-265`)本身就不清 `_feat_ticket_pity/_sold/_appeared`,`_feat_ticket_appeared` 在 master 亦不入断点、不在任何处复位;改后由 `CurioDeck.ticket_appeared` 同样只由 `_init` 归零。宿主 `_reset_curio_state`(`adventure_runner.gd:282-292`)与 master 除首两行改 `_curio.reset()` 外逐字同。**代拍板 B-5 的申报成立。** +- **RNG 调用次数与顺序**(非 Boss 渠道):`_candidate_pool`(过滤,零掷点)→ `rng.open_stream("reward_pick")` 取流点同位 → `_shuffle(pool)`=`pool.size()` 次 `randi_range(i, size-1)`(与 master 内联循环同式同次)→ 由已洗的 pool 生成 sub(tier_rank≥excellent)→ `_shuffle(sub)`=`sub.size()` 次 → `_draw_exposure` 收集循环零掷点 → `feat_ticket_consider` 至多一次 `randi_range`。Boss 渠道:取流后直接 `_draw_weighted`,每轮 1 次 `randi_range(1, total)`,剔牌重掷走同一次数(`continue` 回循环再掷),与 master 内联加权段逐次同。**逐位核对通过。** +- **ShopSession**:21 函数同形,成员→`_refs/_hooks` 读取(`_gp/_set_gp` 落 `wallet`=宿主 `_ledger` 同一字典对象,`level_runner.gd:33-35` 的 `gp` 属性也读写 `_ledger["gp"]`,无双账本)。`buy_product` 的出账→加算区→购血 hook→计数→emit 次序与 master 逐行同;`_emit` 取的是** mutation 后的 `_gp()`**=master 的 `wallet_changed.emit("buy", gp, …)` 同值。`_draw_shelf` 的内联洗牌抽出为 `_shuffle`,次数同;`pool_ticket.call(stream)` 仍复用同一 `shop_draw` stream(零新 scope)。 +- **换代重绑面核实**:`_bind_shop()` 五处(`:165/:205/:646/:670/:688`)恰好覆盖宿主引用被**整体替换**的全部位点——`_ledger` 重赋值(`:151`、`:202`)、`_attunement` 重赋值(`:645`)、`_tree_rules` 重赋值(`:669`)、`run_ledger` 重赋值(`:687`);`level_row` 为原地 `assign`(`:329`)故不需重绑,件内 `_level_index()` 每次现读。`:162 gp = 0` 落在 `_bind_shop()` 之前,但窗口内无件读点(件读点在 `_enter_node` 之后),无失真。**核对通过。** +- **一处需知情的既有怪癖(非本批引入)**:`set_shop_pool` 捕获的是数组**引用**,之后 `_curio_deck = restored`(续玩)与 `reset()` 都是整数组替换,会使 `ShopSession.pool_deck` 指向旧数组——master 的 `_pool_deck` 同样如此,改后逐位不变(本批未「顺手修」=正确)。 + +### 5.2 `BREAKPOINT_FIELDS` 27 键逐键核(机检+代码验,未照抄报告 §3.2) + +机检结果(本审计 python 对拍,别名词表归一后): + +- 写侧键**序**:master `_snapshot_breakpoint` 字面量 27 键 = HEAD `_bp_parts()` 27 键 = HEAD `BREAKPOINT_FIELDS` 27 键,**三者序列完全相同**(`mk==hk: True`/`reg==hk: True`,对称差集为空)。 +- 写侧**取值表达式**:27 键逐键字符串比对(把四个转发属性名归一后)**零不一致**——含 `bonus` 四子键、`shop_shelf`/`shop_visit_buys` 的 `_runner != null` 三元式、`rng_counter: rng.counter()`、`graph_gen_v2: 1`。 +- 读侧覆盖面:master 读 25 键(另 2 键 `shop_shelf`/`shop_visit_buys` 写侧专用、两侧都不读,改后同);HEAD=16 键仍由 `_resume_*` 段内手写 `bp.get(...)`+9 键改走 `_bp_read`/`_bp_read_int`(attunement_charges/bonus_deck/adventure_counters/attune_refresh_y/refresh_stock/vision_counter_used/node_id/visited_ids/aux_items),两集合并集=master 的 25 键,**无增无减**。 + +逐键语义判定(与报告 §3.2 对照表交叉): + +| 键类 | 判定 | +|---|---| +| keep_on_null 三键(`attunement_charges`/`bonus_deck`/`adventure_counters`) | **等价成立,但靠调用点守卫而非派生段**。`_bp_read:955` 的类型判别写作 `not (v is Dictionary or v is Array)`——比改前各站单一守卫**宽**(Dictionary 值喂给 `is Array` 站、Array 值喂给 `is Dictionary` 站,改前都会被拒、改后 `_bp_read` 都原样返回)。三处调用点各自保留了原 `is Dictionary`/`is Array` 判定(`:181`、`:214`、`:257`),净行为逐位不变。若日后某调用点删掉自身守卫,宽判别就成漏洞——登记标签与派生段职责边界不清,属可修的实现级技术债(不阻塞合回) | +| clamp 四处 | `vision_counter_used`(`:260`)与 `refresh_stock`(`:259`)经 `_bp_read_int` → `maxi(0, int(bp.get(k, 0)))`=master 逐字同 ✓。**`hero_hp` 与 `bonus` 四子键不经派生段**:`:189` 仍写 `int(bp.get("hero_hp", 0))`(master 同)、`:191-194` 仍写四行 `maxi(0, int(...))`(master 同)。改前改后同义 ✓,但 `hero_hp` 的 `clamp` 标签名不副实(其 clamp 实为 `_resume_params:239` 的 `mini` 上限钳制,非 `maxi(0,…)`),且两键未真正单源化。**报告 §3.2 的 ✓ 列在「语义一致」义上成立,在「由登记派生」义上仅 9/25 键成立**——「单源」是半单的,报告未披露这一层 | +| params_fallback 一处(`attune_refresh_y`) | `_bp_read_int("attune_refresh_y", bp, 0, params)` → `maxi(0, int(bp.get(k, int(params.get(k, 0)))))` = master `:205` 那行**逐字同式** ✓ | +| plain 类(余 18 键) | 语义逐键同义;报告把 `shop_shelf`/`shop_visit_buys` 的「读侧实义」写成「`shop_bp_*()` 取数」——那是写侧取数,两键两侧都无读点(轻微表述错置,不影响结论) | +| `dual_merge`/`token_migration` | `bonus_taken` 双向清零回填+身份维键(`:206-210`)与 master 逐字同;`bonus_pending` 候选字符串→普通档令牌归一+relic 三段键(`:264-279`)逐字同 ✓ | +| 未登记键 fail-fast | `_bp_read:952-953` 对未登记键 `push_error` 后**继续按 plain 读**,`_bp_strategy` 缺省回 `"plain"`——注释称「fail-fast 不静默按 plain 兜」,码实为「报错+软兜」。行为无差(wr2 键集断言保证不可达),但注释与码不符,且与红线「fail-fast=致命中止无软回退」的口径有张力 | +| `resume_adventure` 拆分 | 段序核实:ledger→curio 态→参数→计数器→pending/result→`_resume_bp`+attunement_charges→`_start_current_level`。与 master 唯一差异=`bp_pending` 的**读取点**从计数器段之前挪到之后(`:167`),其间无对 `bp` 的写点,纯局部变量取值时点移动,零行为差 ✓ | + +### 5.3 断点五时点零增减零迁移 + +`git grep -n _snapshot_breakpoint` 两侧逐一对所属函数: + +| master | 所属函数 | HEAD | 所属函数 | +|---|---|---|---| +| :509 | `_finish_curio_pick` | :483 | `_finish_curio_pick` ✓ | +| :637 | `_refresh_curio_common` | :611 | `_refresh_curio_common` ✓ | +| :806 | `_redeem_feat_ticket` | :665 | `_redeem_feat_ticket` ✓ | +| :934 | `_on_level_finished` | :806 | `_on_level_finished` ✓ | +| :983 | `_forward_choices_presented` | :855 | `_forward_choices_presented` ✓ | + +计数 5↔5,所属函数一一对应,且 `_finish_curio_pick`/`_refresh_curio_common` 两函数体与 master 逐字节相同(`diff` rc=0);全仓无第六处调用(`shop_session.gd:303` 只是注释提及)。**契约 3 后半成立。** + +### 5.4 转发属性读写面与双写面 + +| 宿主 | 条数 | 只读转发 | 可写回件内 | +|---|---|---|---| +| `adventure_runner.gd:60-77` | 4 | 0 | **4**(`_curio_taken`、`_curio_deck`、`_feat_ticket_pity`、`_feat_ticket_sold` 全带 set) | +| `level_runner.gd:50-66` | 8 | **7** | 1(`_shop_purchased`;其 set 用于 `:164/:204` 两处把冒险级 `purchased` 字典别名进来,功能必需) | +| `board_view.gd:84-124` | 13 | 11 | 2(`_hero_tex_idle`、`_hero_last`——探针与 battle_stage_view 的既有写点保号,`board_view.gd:164-165` 装载时即写) | + +双写面核查:报告 §4 第 3 条「测试仍从宿主转发面直改件内多重集」三处指认**逐处属实**——`adventure_feats_test.gd:390-393`(`a11._curio_deck.remove_at(di)`,经 getter 拿到活数组下标写回)、`feat_vouchers_test.gd:92/98/108`(`a._curio_deck = [...]`,走 setter)、`level_shop_economy_test.gd:1351-1353`(`a8._curio_deck.remove_at(...)`)。 + +但该条**披露不完整**:生产码同样经宿主面写件内状态——`adventure_runner.gd:207-208`(`_curio_taken[id] = …`)、`:210`、`:222`(`_curio_deck = restored` 整数组替换)、`:253-254`(`_feat_ticket_pity/_sold` 赋值)、`:652-657`(`_redeem_feat_ticket` 内 `+=`/`-=`);`level_runner.gd:164/204`。即「件封装是名义的」不止测试面,续玩与兑付主链也是。存储仍是单点(件内一份,无副本),所以**不构成双源**,但「谁写件内状态」的写者数在宿主侧,P0 待办条目应据实扩面。 + +一处须留证的疑点(本审计实跑消解):`board_view.gd:165` 对**只读**转发属性 `_hero_tex_walk`(`:108-110` 仅 get)赋值。若该写不落件内,英雄行走帧永不切换。实跑 `res://probe/ui_input_probe.tscn` → 十八断言全绿、零 `SCRIPT ERROR`/零 setter 报错,其中 case⑨ 断言「移动后 `sp.texture != bv._hero_tex_idle`」——判据成立说明写点确实落进了 `HeroSprite.tex_walk`,行为无异常。留此记录供后续批知悉:只读转发属性上存在宿主写点,属计数守恒装置的连带面(P0 条目扩面时一并裁)。 + +### 5.5 卡③ 查表族与墨族被重构(非改名级变化) + +对 master `board_view.gd` 逐函数比对,四个函数不是搬运而是改写: + +- `_wall_tile_for`:master 18 段 if-链 → `WALL_MASK_TILE[16]` 查表+`_neighbor_mask` 位掩码(`:226-246`)。 +- `_wall_center_tile`:master 四连 if+三段阈值 → `WALL_DIAG_DIRS/WALL_DIAG_TILES/WALL_CENTER_PICK` 表驱动(`:250-255`)。 +- `_digit_ink_mode`:master 内联均值+四对比度+标志位 if/elif/else → 抽出 `_mean_of`/`_family_contrasts`(Vector4 四元组)+布尔重导出(`:170-195`)。 +- `load_board_tiles`:镜像件翻转六行去掉 `as Texture2D` 冗余转换,`var t := {}` → `Dictionary[String, Texture2D]`。 + +等价性本审计穷尽验证:16 个掩码组合逐一对拍 master if-链输出,**零不一致**(位序 `nf<<3|ef<<2|sf<<1|wf` 与表下标吻合;0/1 两块都落 `wall_w`,与 master 收尾分支同);中心三变体 `WALL_CENTER_PICK` 分布=6/2/2,与 master `m<6`/`m<8`/else 的 60/20/20 同;`crs.x/y/z/w` 与 master 的 p90_bright/p90_dark/mean_bright/mean_dark 及返回字典键一一对应,`dark`/`plate` 三态重导出真值表与 if/elif/else 同。 + +**但卡片契约 1 写的是「禁顺手改算法、禁合并/删函数、装载器/查表/英雄族除接收者化与缓存成员随迁外逐字一致」,代拍板白名单只给到「命名与函数名前缀取舍」**——新增三张 const 表与两个 helper 属白名单外的结构裁断,实现报告与代拍板单均未申报(grep 全套 reports/ 无掩码表/查表重构字样)。等价性成立 ⇒ 不必回退代码,但**须补追认**。 + +--- + +## 六、未通过项与例外清单 + +| # | 事项 | 缺什么 | 归属 | 建议处置(非裁断,供用户决) | +|---|---|---|---|---| +| F-1 | 实现报告 §2 卡② r1-1 行「真实入口=hud_presenter_test」 | `hud_presenter_test.gd` 零引用五个新迁静态件;真实承载面是 `ui_click_probe.gd:789`/`ui_struct_probe.gd:60/94` | **实现段可修**(只改 reports/ 一句) | 把该格改为指认实际承载面;顺带记:`career_data`/`save_select_data`/`bonus_summary_text` 至今无单元级断言,只有探针开层链与冒烟,若要为「投影」建回归断言属下一批 | +| F-2 | 卡④ r3-1 委托行号 8 处中 5 处不指委托(`:471` 指 `_finish_curio_pick`、`:457/:641/:647` 为空行、`:654` 在 `_redeem_feat_ticket` 体内);r3-3 记 `const` 在 `:857`(实 864)、`_bp_parts` 在 `:909`(实 907);卡① d4/d12/d15/d17 行号漂移 1~15 行 | 取证指针精度——实体全部在盘,签名与语义本审计已逐个核实 | **实现段可修**(指针订正,零代码面) | 附录 A 的 44 个指针零错,可只校 §2 生产面指针 | +| F-3 | 卡③ r2-1 查表族/墨族四处**算法重构**未在报告/代拍板/反哺点任一处申报 | 违卡③ 契约 1「禁顺手改算法、禁合并/删函数」字面;等价性本审计穷尽证实(16 掩码+60/20/20+三态真值表零差) | **实现段可修**(补代拍板追认行+实现报告逐字迁移核对表补注) | 追加 B-8 记「为压 S13 函数阈值而表驱动化,判据零变」,请用户追认;不建议回退 | +| F-4 | 卡① d11/d16a 改法「ConfigDB 新增三访问器」被 `2ed81668` 撤销,消费面改直读 `db.shop.*`/`db.hero.*` | 卡片钉值表与盘面不符(契约 1「钉值表逐字」被后续批内提交推翻);红线「数值只经 ConfigDB 单点读取」义上仍守(`db` 即 ConfigDB,无自建 `load()` 表路径);已在报告 §2 两行括注+commit 消息申报 | **实现段已修(申报在案)**,卡片文本属历史件不改 | 记入反哺点第 8 条随文档批核一次「行访问器直读」是否等同「ConfigDB 单点」 | +| F-5 | 卡②③ 缺「逐字迁移核对表(逐函数:原行界→新落点)」交付物 | 两卡各自完成定义 2 证①明文要求;报告 §3.1 只给卡④ 两拆件族。本审计已用机检补上证据(§2.2/2.3 的逐函数 diff 结论) | **实现段可修**(补一节表,或按吸收即死并入本审计) | 不必重做,把本审计 §2.2/2.3/5.5 的比对结果作为交付物指回本文件即可 | +| F-6 | 实现报告 §1 读数表「卡片预期」列混记机检线 | 卡② 成文预期是 ≈1370–1380、卡③ 是 ≈880,表中分别填「≤999(S13 文件线)」「≤1000」并判「达成」;按卡片口径 main 946 是**超出**预期 424 行(因代拍板 B-2 扩面)、board_view 937 是**超出** 57 行 | **实现段可修**(改列名或加一行如实注记) | 与卡④ 两行「未达成」的写法对齐,同一把尺 | +| F-7 | 卡① §8.3「唯一既有测试改动」与 V4 换代冲突(卡片自身 d2 改法×契约 5 互斥) | 上游卡片 spec-AC 不一致;实现处理正确且已留痕 | **须退回上游**(写卡段反哺,不在本批修) | 反哺点第 7 条同源:卡片钉「删除某判据」时必须同时列「该判据的在役断言如何换代」 | +| F-8 | 闸回执 commit(`9e3e2667`)落后 HEAD(`356029d0`)一个纯文档提交 | 域覆盖:追加件只在 `reports/`,代码域零变化;但 guard 按 diff 推域校验回执时可能对不上位 | **工程/流程面**(非四卡缺陷) | 合回前重跑一次所选域档,或确认 guard 接受文档件后追加 | + +无一项属「断言缺失/阈值放宽/入口造假」级;F-1 与 F-2 是取证指针不实,F-3 是白名单外结构裁断未申报(等价已证),其余为口径与交付物完整度。 + +--- + +## 七、本审计未覆盖的面(如实列出) + +1. **未重跑全量闸**——遵指令。09 静态扫描(S11/S13)、10 熵增闸、06/07/08/08c/08d 文档域各闸、05 渲染闸、00-04 冒烟/系统/单元/probe,均**只采信回执**+卡③/卡② commit 消息读数。例外:本审计自跑了 `tools/audit_type_hints.py`(PASS)与六件单跑——`breakpoint_resume`(59,含变异复现两次)/`level_shop_economy`(186)/`adventure_feats`(138)/`standee_fx_sweep`(18)/`terrain_tile_sets`(19)/`theme_projection`(162)+探针单件 `ui_input_probe`(十八断言)——全部与报告读数一致、零 `SCRIPT ERROR`。系统测试/冒烟/渲染闸/其余探针段未自跑。 +2. **11 号回放确定性对拍未跑**(回执 NEED-APPROVAL)。它恰是卡④「拆件后 RNG 消费序不漂」最强的独立判据;本审计的替代证据是静态逐调用点序比对(§5.1)+同种子既有断言,**未做改前改后回放逐位对拍**。发起与否归用户。 +3. **未跑 MC/未动 `sim-data/` 锚**——遵「无数值改动零跑」;因此卡④ 的数值面等价只到「现算公式与读表值同义」这一层。 +4. **撤销证明只实跑了卡④ P-B 一条**。卡① d1/d2/d4/d5/d8/d9/d10/d12、卡② r1-3、卡③ r2-3、卡④ P-A/P-C 共 11 条=取证复核(commit 消息+报告附录 B 原文+结构前提独立核实),未逐条重演。原始日志目录 `.iter37-logs/` 已由 `6b23bc46` 删除,取证只能靠转录读数。 +5. **人工目检未做**(真人试玩项登记在实现报告 §5 七条,按红线只登记不阻塞)。呈现层拆件的观感等价证据止于 golden 逐像素 0.00% 与 probe 断言,未看实拍图。 +6. **未审计非本批文件**:`battle_orchestrator.gd`(卡④ 契约 5 明写不碰,diff 亦无该文件)、`save_manager.gd`(卡④ 论定 save 侧零键登记,diff 亦无)——两者本批零改动,未复核卡片论断本身。 +7. **未核 `game/tests/验证集清单.md` 陈旧度**(反哺点第 5 条在册,不受闸覆盖);未核 gdstyle 基线陈旧键(反哺点第 6 条)。 +8. **`iter37-拆分卡缺口-2026-10-25.md`(`356029d0`)与 P0 待办条目的内容一致性未审**——属另一会话产出,不在四卡验收面内;只核了待办条目里「adventure_runner :60-77 四条/level_runner :50-64 八条」的行号指认(属实)。 diff --git a/待办清单.md b/待办清单.md index f88f2ea4..3981b06b 100644 --- a/待办清单.md +++ b/待办清单.md @@ -2,6 +2,21 @@ 本清单是流水线的唯一登记处:要做的事先在此登记一条,随流水线段推进改状态,做完整条删除;各系统文档的「待办」节只留一行指路存根,不再自建条目。条目格式(标题=登记元数据含可选优先级,正文=实况/缺口/改动面/状态四属性)与状态机以 [doc-conventions §12.1](design/engineering/doc-conventions.md) 为权威。 +#### AdventureRunner 职责真拆分——三宿主共 25 条转发属性属 S13 计数迎合装置,须按变化原因重立协作件(2026-10-25 登记,来源=批次移交,优先级=P0) + +**实况**: +- 迎合装置本体:迭代 37 卡④ 实现(commit e5ee4d04)在 game/core/adventure_runner.gd:60-77 留 4 条、game/core/level_runner.gd:50-64 留 8 条转发属性(get/set 直指 CurioDeck/ShopSession 件内同名状态);卡③ 在 game/presentation/views/board_view.gd:82-122 留 13 条同类件(直指 TilePalette/HeroSprite 件内纹理与 tween 句柄)。三处同债种,唯一理由=让 S13 基线消息里的类变量数逐字保持 42/38/45;实现报告已按「指标迎合债」登记。 +- 存在理由已消失:S13 基线键治本已入库(master 37625317,键=文件|规则|函数名,判定=改小/持平不红、仅较登记值恶化判红)⇒ 撤除这 25 条属纯代码改动、不碰基线(但须与拆件同批——单撤会让三处测试直引与探针直读面成片红,见缺口⑤)。 +- 本体问题:adventure_runner 现 42 成员/23 公开方法/65 函数/989 行(level_runner 38/54、board_view 45/27 同状——只撤装置到不了阈值),按变化原因数混着七件事(生命周期与状态机/英雄账本与生命/三选一停点兑付/刷新 X 与 Y 双源/断点 schema 写读/呈现事件转发/商店池装配接线),属 God Class+Data Clump;卡④ 只搬数据未搬决策,件的封装是名义的——测试仍从宿主面直改件内多重集(game/tests/unit/core/adventure_feats_test.gd:390-393、feat_vouchers_test.gd:92-93、level_shop_economy_test.gd:1351-1353)。 +- 目标切法(待裁,非结论):AdventureFlow(状态机+关序推进+结算受理一次)/RewardStop(停点态+兑付推进+刷新双源)/CurioDeck(已在册)/HeroLedger(ledger+run_ledger+effective_max_hp+rest 公式)/BreakpointSchema(27 键单源+写读两派生);呈现转名下沉装配层。判据=件拥有状态与决策、宿主不留同名数据槽、件→宿主只传值不回调宿主私有族。 +- 相关文档:design/engineering/architecture/architecture.md(模块归属表须随拆分登记新件)· design/gdd/systems/表现与支撑/存档序列化.md(断点域契约,27 键冻结面)· reports/需求卡片-重构runner拆件与断点schema单点批-2026-10-24.md(卡④ 契约 1 冻结公开面,正是本条无法在批内完成的原因) + +**缺口**:拆分边界未裁四件——①RewardStop 与 CurioDeck 的分界(停点态 `_curio_pending`/`_curio_result`/`_node_curio_pending` 归谁、凭证兑付 `_redeem_feat_ticket` 在哪侧);②HeroLedger 与既有 RunLedger 的关系(合并还是分层,RunLedger 已被 LevelRunner 共享引用);③断点 schema 是否独立成件(卡④ §11 以「27 键多数取宿主态、开洞面>收益」淘汰过独立件,真拆分后该论据失效,需重裁);④公开面收缩后 main.gd/camp_shop_presenter/curio_interact_controller 与三处测试直引点的改法与验收面;⑤board_view 13 条同类装置撤除时探针面与组合根直读面的改法(terrain_tile_sets_test/ui_contrast_probe/ui_input_probe 直引在案)。裁完才进写卡段。 + +**改动面**:游戏代码 + +**状态**:待文档设计 + #### 超预算图策略升级路径——等比缩放与滚动视口两案待裁(2026-10-24 登记,来源=批次移交) **实况**:迭代 34 卡④(信息迷雾可见性批,已合回 master,提交 16a7ec80)按「参数限图」交付,图包络约 10 层×8 槽即当前冻结画布的屏预算上限。若后续定值批需要更大的完整可见图,两条升级路径都要动冻结件,均未设计:①等比缩放——动「节点图标 20px 烘焙 1:1」与「通道折线冻结 2/3/4px」两条冻结值;②滚动视口——新增视口交互范式。迭代 35 生成图实测深度域 L3 达 0–13,已超出该上限(level-map 已登记随动缺口,本批不裁方案)。 @@ -99,6 +114,7 @@ #### 旧行为 MC 行为锚全数作废——重锚归数值批(2026-10-06 登记,来源=批次移交) **实况**:案C 地图伤害账殿后批(git 7ef18ac,状态记录 371)改变每轮结算行为序——授予段不再兑付任何伤害(伤害形态单据暂存至战斗段末,段内序=主角先手→逐敌还手→地图账),既有全部 MC 行为锚与基线逐项比对锚所锚行为已不存在,**全数作废**;新段内序断言已入冒烟闸。按 sim-data MC 缓存层规则(无数值调整禁重跑),重锚不在改动批执行;重锚前一切 MC 旧读数只作历史存档,禁止与新行为逐项比对。M4 数值批卡 v1.2 已收该义务(F19:对拍锚按案C 新序重推导、T6 四带复算按新行为首采立新锚)。 +- 2026-10-25 更新(迭代 37 实测凭据+用户裁):该批发起 `parity_anchor.py check` 失败 9 项,双树原始面实测归因=①历史失配(锚内 RUNSUM 仍含 `level_templates`,迭代 35 卡② commit 9ee393a1 已把 MC 出数字段改名 `level_maps`;锚上次重制为 commit 0170071d/2026-10-19)+②迭代 37 卡① d12 给明细行新增 `target_id` 键未入锚口径(17222 行差异全为该新键,零值差零行数差)。**用户裁「批准本批重锚」**——对拍锚已按现行行为(含 `level_maps`+`target_id`)重制入 game/tests/evidence/parity/anchor.json,本条的"对拍锚作废待重推导"义务就此出账;T6 四带复算与数值标定窗口的设计段重走不变。 **缺口**:M4 数值批卡片已随 2026-10-16 删卡令退役;2026-10-18 用户裁=本条留待办清单、未设计未启动、不另行归属,未来数值标定窗口重开时重走设计段再写卡。