秘宝批T7组E-2:内部标识符全族换词(inventory/unlocked/replace/overlays/slot/settlement use_aux等60+token)+存档键零触碰专项核通过(37文件六键普查零差异,点号访问零存在)+行为探针四件全绿+任务书§1.2基线回填(755/0实测)

This commit is contained in:
DKChan committed 2026-10-05 08:46:51 +08:00
1 parent f8c6db49c6
commit cd73e62346
21 files changed
+205 -203

No files matched your search

+68 -68
View File
@@ -43,7 +43,7 @@ extends RefCounted
signal level_started(level_index: int, display_name: String) # 每关开始(含冒险首关)——呈现层重取 runner()、刷新图条与层名行
signal bonus_choice_presented(payload: Dictionary) # 提升三选一停点(非末关通关恰一次;迭代 10 大奖励重构——big_reward_paid 退役)——{level_index, candidates, heal_gained, hp_before, hp_after, keystone_gained, keystone_total, gp, aux_cleared}
signal bonus_resolved(result: Dictionary) # 三选一兑付完成(pick_bonus/替换受理后恰一次)——{picked, value, hp_before, hp_after, level_index}
signal aux_replace_presented(items: Array, incoming: Dictionary) # 同调位满位替换子交互(pick_bonus 道具件满位时)——items=现有持有件记录、incoming=新件记录
signal treasure_replace_presented(items: Array, incoming: Dictionary) # 同调位满位替换子交互(pick_bonus 道具件满位时)——items=现有持有件记录、incoming=新件记录
signal adventure_finished(summary: Dictionary) # 冒险结算(completed/failed/abandoned)——summary 五件事:打穿没有/通关几关/止步第几关/钥石合计/主种子(+additive keystone_drops 迭代 10)
# ---- 转发族(签名与 LevelRunner 同款——呈现层对接透明;ADR-0001 S6 经 _forward_* 方法)----
signal node_entered(node: Dictionary, info: Dictionary)
@@ -70,12 +70,12 @@ var _last_level_payload := {} # 最近一次关卡结束载荷(失
var _override := [0, 0] # 调试浮窗百分比通道 [atk_pct, hp_pct](每关新 runner 都要应用)
var _base_override := {"atk": 0, "max_hp": 0, "armor_class": 0}
var _first_script := false # 首次脚本模式(关卡节点图生成 v0.4——true = 全关零抽签取池内模板 0;测量夹具恒 false) # 永久强化 flat 注入(元进度实现批——冒险开始参数面 params.hero_override 存入,共享给 LevelRunner;全零 = 白板)
var _last_aux_cleared := 0 # additive(迭代 7 T4——最近一次关卡末清空计数,summary 取数面)
var _aux_unlocked := {} # aux 解锁门控集(迭代 7 T5——params.aux_unlocked 接收面)
var _last_treasure_cleared := 0 # additive(迭代 7 T4——最近一次关卡末清空计数,summary 取数面)
var _treasure_unlocked := {} # aux 解锁门控集(迭代 7 T5——params.treasure_unlocked 接收面)
var _attunement: Array = [] # 同调装配集(迭代 11 第①批 T5——params.attunement 接收面,start_level 外下传 LevelRunner)
var _attune_refresh_x := 0 # 刷新件余量(迭代 11 第①批 T5b P1 甲——每碰三选一重置=X 表值;优先消耗、不结转)
var _attune_refresh_y := 0 # Y 冒险周期池(开局参数面注入,用完即无——营地升级行表值)
var attune_charges := {} # 同调主动件冒险充能账本(迭代 11 第①批 T6——跨关卡共享 dict;settlement use_aux 扣减直改同引用;断点随 attunement_charges 键入档)
var attune_charges := {} # 同调主动件冒险充能账本(迭代 11 第①批 T6——跨关卡共享 dict;settlement use_treasure 扣减直改同引用;断点随 attunement_charges 键入档)
var _save_manager = null # 断点承载注入(迭代 8 卡①——null = 不写断点;模拟/冒烟缺省)
var _resume_bp := {} # 续玩快照暂存(resume_adventure → _start_current_level 单跳传递)
# ---- 三选一停点态(迭代 10 奖励重构批)----
@@ -85,9 +85,9 @@ var _relics: Array[String] = [] # 特性持有集(迭代 12 T1——
var _tree_rules: Array = [] # 树 rule 键集(迭代 12 T10——Fork 激活侧 effects 透传;下传 enc.tree_rules 消费面)
var _bonus_pending := {} # 停点态:{"candidates": Array, "payload": Dictionary, "chosen"/"incoming": 替换在途}——空 = 无停点
var _bonus_result := {} # 已兑付待推进态(bonus_resolved 载荷副本——中断续玩不重兑付)
var _bonus_aux_carry: Array[String] = [] # 道具件跨关携带(兑付后注入下一关库存——清库后授予的体验窗)
var _bonus_treasure_carry: Array[String] = [] # 道具件跨关携带(兑付后注入下一关库存——清库后授予的体验窗)
var _keystone_drops: Array = [] # Boss 逐杀掉落明细(additive 摘要键 keystone_drops——结算明细/复盘归因数据源)
var _last_draw_aux: Array[String] = [] # 最近一次抽签掷定的道具件(掷定前置——卡片直显具体道具;追加批二)
var _last_draw_treasure: Array[String] = [] # 最近一次抽签掷定的道具件(掷定前置——卡片直显具体道具;追加批二)
var _node_bonus_pending := {} # 奖励节点三选一停点态(迭代 10 追加批——共享池;断点不载:续玩重建到选路停点重走,恢复的计数器保证同候选重抽)
var _shop_mode := "legacy" # 商店模式(迭代 11 第②批 T3——"legacy"=固定三件加定价〔off 对照承载,缺省零扰动〕/"pool"=共享池抽 3 买 1 重抽;真实入口 main.gd 显式投 pool)
@@ -121,7 +121,7 @@ func start_adventure(seed_val: int, params: Dictionary = {}) -> bool:
_base_override = _normalize_override(params.get("hero_override", {}))
_first_script = bool(params.get("first_script", false)) # 缺省 = 非首次(抽签路径;真实侧由呈现层读存档布尔位传入)
_shop_mode = "pool" if String(params.get("shop_mode", "legacy")) == "pool" else "legacy" # 迭代 11 第②批 T3——缺省 legacy(off 对照零扰动)
_aux_unlocked = params.get("aux_unlocked", {}) if params.get("aux_unlocked", {}) is Dictionary else {}
_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(迭代 11 第①批 T5)
_attune_refresh_y = maxi(0, int(params.get("attune_refresh_y", 0))) # additive(迭代 11 第①批 T5b——Y 冒险周期池)
_seed_attune_charges() # 迭代 11 第①批 T6——冒险开始充能账本按装备主动件重置(每冒险充能语义) # additive(迭代 7 T5)
@@ -195,7 +195,7 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool:
for r in (bp.get("relics", []) as Array):
_relics.append(String(r))
for v in (bp.get("bonus_aux_carry", []) as Array):
_bonus_aux_carry.append(String(v))
_bonus_treasure_carry.append(String(v))
_keystone_drops = (bp.get("keystone_drops", []) as Array).duplicate(true)
_base_override = _normalize_override(params.get("hero_override", {}))
# 生命账本越界钳制(T17——spawn 守卫 EC16 免疫):断点可能由旧口径版本写出(如调试上限
@@ -207,7 +207,7 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool:
int(round(float(int(db.hero.max_hp) + int(_base_override.get("max_hp", 0))) * (100.0 + float(_override[1])) / 100.0))))
_first_script = false # 续玩模板已随快照强制(已抽模板零重抽)——脚本模式不重放
_shop_mode = "pool" if String(params.get("shop_mode", "legacy")) == "pool" else "legacy" # 迭代 11 第②批 T3——续玩参数面同 start_adventure(缺省 legacy)
_aux_unlocked = params.get("aux_unlocked", {}) if params.get("aux_unlocked", {}) is Dictionary else {}
_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(迭代 11 第①批 T5)
_attune_refresh_y = maxi(0, int(params.get("attune_refresh_y", 0))) # additive(迭代 11 第①批 T5b——Y 冒险周期池)
_seed_attune_charges() # 迭代 11 第①批 T6——冒险开始充能账本按装备主动件重置(每冒险充能语义)
@@ -234,7 +234,7 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool:
state = "running"
bonus_choice_presented.emit(payload)
# 替换在途续发(呈现层直落替换子态——持有件记录按恢复后 carry 重建)
aux_replace_presented.emit(_carry_records(),
treasure_replace_presented.emit(_carry_records(),
{"index": -1, "item_id": String(incoming[0]) if not incoming.is_empty() else "", "count": incoming.size()})
return true
state = "running"
@@ -262,7 +262,7 @@ func _reset_bonus_state() -> void:
_relics = []
_bonus_pending = {}
_bonus_result = {}
_bonus_aux_carry = []
_bonus_treasure_carry = []
_keystone_drops = []
_node_bonus_pending = {}
@@ -364,7 +364,7 @@ func pick_bonus(kind: String, tier: String = "", relic_id: String = "") -> bool:
var value: int = int(db.adventure.feature_value(kind, tier))
var hp_before := int(ledger.get("hero_hp", 0))
if kind == "treasure_item":
return _pick_aux_item(value) # 道具件分派通关渠道(持有容器 = 跨关携带——与节点渠道分轨;池种退役后不可达)
return _pick_treasure_item(value) # 道具件分派通关渠道(持有容器 = 跨关携带——与节点渠道分轨;池种退役后不可达)
if not _apply_bonus_pick(kind, tier, value, relic_id):
push_error("[冒险结构] pick_bonus 拒绝——未知件: %s|%s|%s" % [kind, tier, relic_id])
return false
@@ -473,39 +473,39 @@ func _pool_stock_snapshot() -> Dictionary:
return out
# 道具件兑付(同调位约束):有位直接入跨关携带;满位转替换子交互(incoming 暂存、等 resolve_aux_replace)。
# 掷定前置(追加批二):具体道具随候选抽定(_last_draw_aux / _bonus_pending.aux_detail)——本函数消费预掷值,
# 道具件兑付(同调位约束):有位直接入跨关携带;满位转替换子交互(incoming 暂存、等 resolve_treasure_replace)。
# 掷定前置(追加批二):具体道具随候选抽定(_last_draw_treasure / _bonus_pending.aux_detail)——本函数消费预掷值,
# 断点续玩/无预掷值时回退当场掷定(流序不同但消费数不变,确定性由调用序保证)。
# 持有容器 = 跨关携带 _bonus_aux_carry(与节点渠道的关卡域库存分轨——容器差异保守不合,注记在案)。
func _pick_aux_item(count: int) -> bool:
var picked: Array = _draw_pending_aux(_bonus_pending, count, true)
# 持有容器 = 跨关携带 _bonus_treasure_carry(与节点渠道的关卡域库存分轨——容器差异保守不合,注记在案)。
func _pick_treasure_item(count: int) -> bool:
var picked: Array = _draw_pending_treasure(_bonus_pending, count, true)
if picked.is_empty():
push_error("[冒险结构] pick_bonus(aux_item) 拒绝——解锁道具池空集(白板/门控态不入牌库的防御兜底)")
push_error("[冒险结构] pick_bonus(treasure_item) 拒绝——解锁道具池空集(白板/门控态不入牌库的防御兜底)")
return false
var hp_before := int(ledger.get("hero_hp", 0))
# 迭代 11 第①批 T7——同调位守卫随过渡键退役拆除(aux 卡出池后本路径不可达;能力件容量闸
# 正主 = 营地装配面 Attunement.slots 满位禁选)
for iid in picked:
_bonus_aux_carry.append(String(iid))
_bonus_treasure_carry.append(String(iid))
_finish_bonus_pick("treasure_item", picked.size(), hp_before)
return true
# 替换子交互受理(discard_index = -1 放弃新件不获取;否则丢该件腾位收新)——终态恒 ≤ 同调位
# 持有容器 = 跨关携带(序号寻址);与节点渠道(库存句柄寻址)的丢弃/授予/收尾三步保守不合。
func resolve_aux_replace(discard_index: int) -> bool:
func resolve_treasure_replace(discard_index: int) -> bool:
if not _replace_in_transit(_bonus_pending):
push_error("[冒险结构] resolve_aux_replace 拒绝——无替换在途")
push_error("[冒险结构] resolve_treasure_replace 拒绝——无替换在途")
return false
var incoming: Array = _bonus_pending.get("incoming", []) as Array
var hp_before := int(ledger.get("hero_hp", 0))
if discard_index >= 0:
if discard_index >= _bonus_aux_carry.size():
push_error("[冒险结构] resolve_aux_replace 拒绝——序号不在持有集: %d" % discard_index)
if discard_index >= _bonus_treasure_carry.size():
push_error("[冒险结构] resolve_treasure_replace 拒绝——序号不在持有集: %d" % discard_index)
return false
_bonus_aux_carry.remove_at(discard_index)
_bonus_treasure_carry.remove_at(discard_index)
for iid in incoming:
_bonus_aux_carry.append(String(iid))
_bonus_treasure_carry.append(String(iid))
# 放弃新件(discard_index < 0)= 原持有集不动、新件不获取
_finish_bonus_pick("treasure_item", incoming.size(), hp_before)
return true
@@ -535,12 +535,12 @@ func _finish_bonus_pick(picked: String, value: int, hp_before: int, tier: String
# 掷定前置(追加批二):返回 dict 带 aux_detail(道具件具体道具)供卡片直显。
func node_bonus_candidates() -> Dictionary:
var cands: Array = _draw_bonus_candidates()
return {"candidates": cands, "aux_detail": _last_draw_aux.duplicate()}
return {"candidates": cands, "aux_detail": _last_draw_treasure.duplicate()}
func present_node_bonus(candidates: Array, aux_detail: Array = []) -> void:
func present_node_bonus(candidates: Array, treasure_detail: Array = []) -> void:
_reset_refresh_x() # 迭代 11 第①批 T5b——碰三选一重置 X 余量(不结转)
_node_bonus_pending = {"candidates": candidates.duplicate(true), "aux_detail": aux_detail.duplicate(true)}
_node_bonus_pending = {"candidates": candidates.duplicate(true), "aux_detail": treasure_detail.duplicate(true)}
# 点选兑付(节点域——无部分回血无钥石;令牌消费与牌库摘牌与通关三选一同库;relic 同权入持有集)
@@ -554,7 +554,7 @@ func apply_node_bonus(kind: String, tier: String = "", relic_id: String = "") ->
return false
var value: int = int(db.adventure.feature_value(kind, tier))
if kind == "treasure_item":
return _apply_node_aux(value) # 道具件分派节点渠道(持有容器 = 关卡域库存——与通关渠道分轨;池种退役后不可达)
return _apply_node_treasure(value) # 道具件分派节点渠道(持有容器 = 关卡域库存——与通关渠道分轨;池种退役后不可达)
if not _apply_bonus_pick(kind, tier, value, relic_id):
push_error("[冒险结构] apply_node_bonus 拒绝——未知件: %s|%s|%s" % [kind, tier, relic_id])
return false
@@ -565,19 +565,19 @@ func apply_node_bonus(kind: String, tier: String = "", relic_id: String = "") ->
# 节点渠道道具件(同调位约束):持有=关卡域库存+跨关携带,有位直接入本关库存(当关可用);
# 满位转替换子交互(丢弃目标=库存件 handle——与通关渠道的 carry 序号分轨,容器语义保守不合)。
# 掷定前置:消费 _node_bonus_pending.aux_detail 预掷值(缺省回退当场掷定)。
func _apply_node_aux(count: int) -> bool:
var picked: Array = _draw_pending_aux(_node_bonus_pending, count, false)
func _apply_node_treasure(count: int) -> bool:
var picked: Array = _draw_pending_treasure(_node_bonus_pending, count, false)
if picked.is_empty():
push_error("[冒险结构] apply_node_bonus(aux_item) 拒绝——解锁道具池空集(白板/门控态不入牌库的防御兜底)")
push_error("[冒险结构] apply_node_bonus(treasure_item) 拒绝——解锁道具池空集(白板/门控态不入牌库的防御兜底)")
return false
var records: Array = []
if _runner != null and _runner.aux_inventory != null:
records = _runner.aux_inventory.holding_records()
# 迭代 11 第①批 T7——守卫随过渡键退役拆除(FEATURE_KINDS 已除名 aux_item,本分支不可达;
if _runner != null and _runner.treasure_inventory != null:
records = _runner.treasure_inventory.holding_records()
# 迭代 11 第①批 T7——守卫随过渡键退役拆除(FEATURE_KINDS 已除名 treasure_item,本分支不可达;
# 替换子交互路由随容量闸一并退役——能力件容量闸正主 = 营地装配面满位禁选)
if _runner != null:
for iid in picked:
var rec: Dictionary = _runner.grant_aux_item(String(iid), "reward_node")
var rec: Dictionary = _runner.grant_treasure_item(String(iid), "reward_node")
if rec.is_empty():
push_warning("[冒险结构] 奖励节点道具授予被拒(防御兜底留痕): %s" % String(iid))
_finish_node_bonus("treasure_item", picked.size())
@@ -590,11 +590,11 @@ func _resolve_node_replace(discard_handle: int) -> bool:
return false
var incoming: Array = _node_bonus_pending.get("incoming", []) as Array
if discard_handle >= 0:
if _runner == null or _runner.aux_inventory == null or not _runner.aux_inventory.discard_item(discard_handle):
if _runner == null or _runner.treasure_inventory == null or not _runner.treasure_inventory.discard_item(discard_handle):
push_error("[冒险结构] resolve_node_replace 拒绝——句柄不在持有集: %d" % discard_handle)
return false
for iid in incoming:
var rec: Dictionary = _runner.grant_aux_item(String(iid), "reward_node")
var rec: Dictionary = _runner.grant_treasure_item(String(iid), "reward_node")
if rec.is_empty():
push_warning("[冒险结构] 替换子道具授予被拒(防御兜底留痕): %s" % String(iid))
# 放弃新件(discard_handle < 0)= 原持有集不动、新件不获取
@@ -648,10 +648,10 @@ func refresh_bonus() -> bool:
_attune_refresh_y -= 1
var cands: Array = _draw_bonus_candidates()
_bonus_pending["candidates"] = cands.duplicate(true)
_bonus_pending["aux_detail"] = _last_draw_aux.duplicate()
_bonus_pending["aux_detail"] = _last_draw_treasure.duplicate()
var payload: Dictionary = (_bonus_pending.get("payload", {}) as Dictionary).duplicate(true)
payload["candidates"] = cands
payload["aux_detail"] = _last_draw_aux.duplicate()
payload["aux_detail"] = _last_draw_treasure.duplicate()
bonus_choice_presented.emit(payload) # 同信号重 present(零自动推进契约不破——停点仍在途)
_snapshot_breakpoint()
return true
@@ -662,34 +662,34 @@ func refresh_bonus() -> bool:
_attune_refresh_y -= 1
var cands2: Array = _draw_bonus_candidates()
_node_bonus_pending["candidates"] = cands2.duplicate(true)
_node_bonus_pending["aux_detail"] = _last_draw_aux.duplicate()
_node_bonus_pending["aux_detail"] = _last_draw_treasure.duplicate()
return true
return false
# 替换在途守卫(2026-10-02 修复批——双渠道替换受理的共用前置谓词:停点态非空且 chosen=aux_item)
# 替换在途守卫(2026-10-02 修复批——双渠道替换受理的共用前置谓词:停点态非空且 chosen=treasure_item)
func _replace_in_transit(pending: Dictionary) -> bool:
return not pending.is_empty() and String(pending.get("chosen", "")) == "treasure_item"
# 双渠道道具件掷定共享体(2026-10-02 修复批——_pick_aux_item/_apply_node_aux 复制债收口一步):
# 双渠道道具件掷定共享体(2026-10-02 修复批——_pick_treasure_item/_apply_node_treasure 复制债收口一步):
# 解锁道具池空集守卫 + N 件掷定(先消费停点预掷 aux_detail,不足部分当场经 reward_pick 封闭流补掷——
# 取数序与拆分前逐位一致)。返回空数组 = 池空集(调用方按渠道名报错)。
# 保守不合处:通关渠道允许回退 _last_draw_aux(allow_detail_fallback)、节点渠道不回退——掷定回退
# 保守不合处:通关渠道允许回退 _last_draw_treasure(allow_detail_fallback)、节点渠道不回退——掷定回退
# 语义渠道各异,参数化保留差异;持有容器与兑付收尾仍归调用方渠道。
func _draw_pending_aux(pending: Dictionary, count: int, allow_detail_fallback: bool) -> Array:
var pool: Array = _bonus_aux_pool()
func _draw_pending_treasure(pending: Dictionary, count: int, allow_detail_fallback: bool) -> Array:
var pool: Array = _bonus_treasure_pool()
if pool.is_empty():
return []
var picked: Array = []
var detail: Array = (pending.get("aux_detail", []) as Array).duplicate()
if detail.is_empty() and allow_detail_fallback:
detail = _last_draw_aux.duplicate()
detail = _last_draw_treasure.duplicate()
for i in maxi(1, count):
if i < detail.size():
picked.append(String(detail[i]))
else:
picked.append(_roll_bonus_aux_item(pool))
picked.append(_roll_bonus_treasure_item(pool))
return picked
@@ -697,7 +697,7 @@ func _draw_pending_aux(pending: Dictionary, count: int, allow_detail_fallback: b
func resolve_bonus_replace(discard: int) -> bool:
if not _node_bonus_pending.is_empty() and _node_bonus_pending.has("incoming"):
return _resolve_node_replace(discard)
return resolve_aux_replace(discard)
return resolve_treasure_replace(discard)
func _finish_node_bonus(picked: String, value: int, tier: String = "", relic_id: String = "") -> void:
@@ -718,9 +718,9 @@ func _finish_node_bonus(picked: String, value: int, tier: String = "", relic_id:
# kind 出现率∝剩余令牌数〔档位配额即分布——无独立权重 knob〕;抽签不摘牌、兑付才摘牌=未选
# 自动回池;池耗即枯竭——余库不足 N kind 时出齐剩余,空库 = 零候选合法态)。候选 = 令牌字典
# {"kind","tier"}。消费 reward_pick 封闭流——一次停点恰一 scope,同种子同候选;道具件掷定前置
# 随池种退役拆除(_last_draw_aux 恒空集,机械惰性承载)。
# 随池种退役拆除(_last_draw_treasure 恒空集,机械惰性承载)。
func _draw_bonus_candidates() -> Array:
_last_draw_aux = []
_last_draw_treasure = []
var pool := _bonus_deck.duplicate()
var rg: RandomNumberGenerator = rng.open_stream("reward_pick")
for i in pool.size():
@@ -745,29 +745,29 @@ func _draw_bonus_candidates() -> Array:
func _roll_from(rg: RandomNumberGenerator) -> String:
var pool: Array = _bonus_aux_pool()
var pool: Array = _bonus_treasure_pool()
return String(pool[rg.randi_range(0, pool.size() - 1)])
# 三选一道具件随机池(与投放门控同源:aux 活行 ∩ 解锁门控集——空集 = 道具件不入牌库)
func _bonus_aux_pool() -> Array:
func _bonus_treasure_pool() -> Array:
var out: Array = []
var live: Dictionary = db.treasure_live_ids()
for iid in live.keys():
if _aux_unlocked.has(String(iid)):
if _treasure_unlocked.has(String(iid)):
out.append(String(iid))
return out
func _roll_bonus_aux_item(pool: Array) -> String:
func _roll_bonus_treasure_item(pool: Array) -> String:
var rg: RandomNumberGenerator = rng.open_stream("reward_pick")
return String(pool[rg.randi_range(0, pool.size() - 1)])
func _carry_records() -> Array:
var out: Array = []
for i in _bonus_aux_carry.size():
out.append({"index": i, "item_id": _bonus_aux_carry[i]})
for i in _bonus_treasure_carry.size():
out.append({"index": i, "item_id": _bonus_treasure_carry[i]})
return out
@@ -837,11 +837,11 @@ func _start_current_level() -> bool:
# 注入面随停点渠道退役拆除(容器字段保留=替换子交互机械惰性承载,零投放零行为)
level_started.emit(level_index, String(_runner.level_row.get("display_name", "")))
# 入口停点断点(node_id=-1)与 defer_entry 同态(node_index=-1+单候选)——走入口呈现;
# 中途停点(node_id>=0)走停点呈现(restore_aux_items 先于呈现=C 区库存就位)。
# 中途停点(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_aux_items(_resume_bp.get("aux_items", []) as Array)
_runner.restore_treasure_items(_resume_bp.get("aux_items", []) as Array)
_runner.present_resumed_choice()
_resume_bp = {}
return ok
@@ -861,7 +861,7 @@ func _on_level_finished(payload: Dictionary) -> void:
if result != "victory":
push_error("[冒险结构] 关卡结束结果出域: %s" % result)
return
_last_aux_cleared = int(payload.get("aux_cleared", 0)) # additive(迭代 7 T4——末关 summary 取数面)
_last_treasure_cleared = int(payload.get("aux_cleared", 0)) # additive(迭代 7 T4——末关 summary 取数面)
# Boss 钥石逐杀掉落(迭代 10——渠道改击杀掉落,末关照常入账;首杀/复杀读 meta.tres boss_keystone)
_boss_keystone_drop(level_index)
if level_index < levels_total:
@@ -877,7 +877,7 @@ func _on_level_finished(payload: Dictionary) -> void:
var pick_payload: Dictionary = {
"level_index": level_index,
"candidates": _draw_bonus_candidates(),
"aux_detail": _last_draw_aux.duplicate(), # 掷定前置(追加批二——道具件具体道具随候选抽定,卡片直显)
"aux_detail": _last_draw_treasure.duplicate(), # 掷定前置(追加批二——道具件具体道具随候选抽定,卡片直显)
"heal_gained": heal,
"hp_before": hp_before,
"hp_after": int(ledger.get("hero_hp", 0)),
@@ -887,7 +887,7 @@ func _on_level_finished(payload: Dictionary) -> void:
"aux_cleared": int(payload.get("aux_cleared", 0)), # additive 扩签(迭代 7 T4——关卡末清空反馈取数)
}
_bonus_pending = {"candidates": (pick_payload["candidates"] as Array).duplicate(true),
"aux_detail": _last_draw_aux.duplicate(),
"aux_detail": _last_draw_treasure.duplicate(),
"payload": pick_payload.duplicate(true)}
bonus_choice_presented.emit(pick_payload)
_snapshot_breakpoint() # 选择停点可中断(契约 5——续玩不重抽不静默丢失)
@@ -905,7 +905,7 @@ func _settle(result: String) -> void:
"stopped_at": 0 if result == "completed" else level_index,
"keystone_total": keystone,
"master_seed": master_seed,
"aux_cleared_last": _last_aux_cleared, # additive 扩签(迭代 7 T4——末关清空反馈;非末关走 bonus 载荷)
"aux_cleared_last": _last_treasure_cleared, # additive 扩签(迭代 7 T4——末关清空反馈;非末关走 bonus 载荷)
"keystone_drops": _keystone_drops.duplicate(true), # additive 扩签(迭代 10——逐杀掉落明细;settle 入账明细/复盘归因数据源)
"pool_stock": _pool_stock_snapshot(), # additive 扩签(迭代 11 第②批 T5——终局池读数:清账前快照,商店买走 vs 奖励领走 vs 枯竭的终局面)
"relics": _relics.duplicate(), # additive 扩签(迭代 12 T1——特性终局持有集:清账前快照,成型判读取数面)
@@ -948,9 +948,9 @@ func _forward_choices_presented(candidates: Array) -> void:
func _snapshot_breakpoint() -> void:
if _save_manager == null or state != "running":
return
var aux_items: Array = []
if _runner != null and _runner.aux_inventory != null:
aux_items = _runner.aux_inventory.snapshot_inventory()
var treasure_snapshot: Array = []
if _runner != null and _runner.treasure_inventory != null:
treasure_snapshot = _runner.treasure_inventory.snapshot_inventory()
# 三选一停点续玩分支 _runner 为 null(关卡已收束、下一关未开)——节点域字段按缺省落档,
# 该两态的续玩只消费 bonus_* 附随件(选择停点/已兑付停点快照,迭代 10)
var bp: Dictionary = {
@@ -973,13 +973,13 @@ func _snapshot_breakpoint() -> void:
"rng_counter": rng.counter(),
"keystone": keystone,
"visited_ids": _runner.visited_ids() if _runner != null else [],
"aux_items": aux_items,
"aux_items": treasure_snapshot,
"bonus_taken": _bonus_taken.duplicate(true), # 三选一件获取次数(kind 维度派生账本——断点附随件与读数承载)
"bonus_deck": _bonus_deck.duplicate(true), # 令牌池余库(TP0 v2.2——池消耗即上限,续玩余库逐枚还原)
"relics": _relics.duplicate(), # 特性持有集(迭代 12 T1——局内随机渠道获得态续玩防丢失)
"bonus_pending": _bonus_pending.duplicate(true), # 三选一停点态(空 = 无停点——选择停点可中断契约)
"bonus_result": _bonus_result.duplicate(true), # 已兑付待推进态(空 = 无——中断续玩不重兑付)
"bonus_aux_carry": _bonus_aux_carry.duplicate(true), # 道具件跨关携带(注入前中断防静默丢失)
"bonus_aux_carry": _bonus_treasure_carry.duplicate(true), # 道具件跨关携带(注入前中断防静默丢失)
"keystone_drops": _keystone_drops.duplicate(true), # 逐杀掉落明细(摘要归因续接——迭代 10)
}
_save_manager.set_breakpoint(bp)