主卡T4(拆件批wr6): 断点片段化+获得日记键——level_runner关卡域自交片段(breakpoint_fragment五键node_id/visited_ids/aux_items/shop_shelf/shop_visit_buys+restore_fragment收编_resume_bp直读面含道具库存回填);adv拼盘口重写(_bp_parts=三片段收件:主角账账本键+获得日记/停点件停点态+刷新三源+凭证/关卡件节点域五键——池域taken/deck与桶关序应用层自拼,拼盘完整性自断言fail-fast未登记键中止);BREAKPOINT_FIELDS扩列gain_journal键(28键——契约2唯一声明扩展,旧档缺键=hero.restore_fragment汇总直载跳过比对,日记在档=明细推导比对不符拒载);_resume_curio_state relics段去双载(随主角账片段)。验收:bp1对拍292dumps×27键逐位零漂移(五时点一致)+gain_journal单列在档核对全过+P8冻结清单换代28键+bp2旧档回退与推导比对断言(hero_ledger_test hr2段)+bp3续玩真链(breakpoint_resume现役)+十套件零红〔拆件批 V6〕

This commit is contained in:
DKChan committed 2026-10-27 03:55:55 +08:00
1 parent 4da7f3e2f2
commit fa1409f491
4 files changed
+333011 -15

No files matched your search

+23 -13
View File
@@ -568,6 +568,7 @@ const BREAKPOINT_FIELDS := {
"attune_refresh_y": {"strategy": "params_fallback"},
"bonus_result": {"strategy": "plain"},
"keystone_drops": {"strategy": "plain"},
"gain_journal": {"strategy": "plain"}, # 获得日记(拆件批 T4 扩列——唯一声明的断点扩展;旧档缺键=汇总直载跳过比对)
}
@@ -584,29 +585,28 @@ func _snapshot_breakpoint() -> void:
# 断点写侧取值(键集以 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(关卡已收束、下一关未开)——节点域字段按缺省落档,
var hero_frag: Dictionary[Variant, Variant] = _hero.breakpoint_fragment()
var stop_frag: Dictionary[Variant, Variant] = _stop.breakpoint_fragment()
var level_frag: Dictionary[Variant, Variant] = _runner.breakpoint_fragment() if _runner != null else {
"node_id": -1, "visited_ids": [] as Array, "aux_items": [] as Array,
"shop_shelf": [] as Array, "shop_visit_buys": 0} # 三选一停点续玩分支 _runner 为 null——节点域按缺省落档
# 该两态的续玩只消费 bonus_* 附随件(选择停点/已兑付停点快照)
var hero_frag: Dictionary[Variant, Variant] = _hero.breakpoint_fragment() # 主角账片段(账本键+获得日记——T4 拼盘收口前先同源取数)
var stop_frag: Dictionary[Variant, Variant] = _stop.breakpoint_fragment() # 停点件片段(T4 拼盘收口前先同源取数)
return {
var parts: Dictionary[Variant, Variant] = {
"level_index": level_index,
"node_id": _runner.current_node_id() if _runner != null else -1,
"node_id": int(level_frag.get("node_id", -1)),
"hero_hp": int(hero_frag.get("hero_hp", 0)),
"bonus": hero_frag.get("bonus", {}),
"gp": int(hero_frag.get("gp", 0)),
"shop_purchased": hero_frag.get("shop_purchased", {}),
"shop_shelf": _runner.shop_bp_shelf() if _runner != null else [], # 货架入断点(离开店/非店停点 = 空架合法态)
"shop_visit_buys": _runner.shop_bp_visit_buys() if _runner != null else 0, # 本次进店已购次数(A 链定价输入)
"shop_shelf": level_frag.get("shop_shelf", []), # 货架入断点(离开店/非店停点 = 空架合法态)
"shop_visit_buys": int(level_frag.get("shop_visit_buys", 0)), # 本次进店已购次数(A 链定价输入)
"attunement_charges": run_ledger.attune_charges.duplicate(true), # 同调主动件充能余量随断点(save v4 域)
"graph_gen_v2": 1, # 生成图换代代差标记(缺键 = 换代前旧断点 → resume 显式拒绝)
"master_seed": master_seed,
"rng_counter": rng.counter(),
"keystone": int(hero_frag.get("keystone", 0)),
"visited_ids": _runner.visited_ids() if _runner != null else [],
"aux_items": treasure_snapshot,
"visited_ids": level_frag.get("visited_ids", []),
"aux_items": level_frag.get("aux_items", []),
"bonus_taken": _curio_taken.duplicate(true), # 三选一件获取次数(kind 维度派生账本——断点附随件与读数承载)
"bonus_deck": _curio_deck.duplicate(true), # 令牌池余库(TP0 v2.2——池消耗即上限,续玩余库逐枚还原)
"relics": hero_frag.get("relics", []), # 特性持有集(局内随机渠道获得态续玩防丢失)
@@ -616,10 +616,20 @@ func _bp_parts() -> Dictionary[Variant, Variant]:
"adventure_counters": adventure_counters.duplicate(true), # adventure 域计数器随断点(续玩跨战账不丢——桶归宿主字典)
"vision_counter_used": int(hero_frag.get("vision_counter_used", 0)), # 真视使用计数随断点(续玩跨战账不丢)
"refresh_stock": int(stop_frag.get("refresh_stock", 0)), # 刷新存量账随断点
"attune_refresh_y": int(stop_frag.get("attune_refresh_y", 0)), # Y 冒险周期池余量随断点(续玩不再自参数面整额重注入=堵「用完退菜单再续玩满血复活」可利用漏洞;扩列向后兼容——旧档缺键回退参数面)
"attune_refresh_y": int(stop_frag.get("attune_refresh_y", 0)), # Y 冒险周期池余量随断点(扩列向后兼容——旧档缺键回退参数面)
"bonus_result": stop_frag.get("bonus_result", {}), # 已兑付待推进态(空 = 无——中断续玩不重兑付)
"keystone_drops": hero_frag.get("keystone_drops", []), # 逐杀掉落明细(摘要归因续接)
"gain_journal": hero_frag.get("gain_journal", {}), # 获得日记(唯一声明的断点扩列——日记为正主)
}
# 拼盘完整性自断言(fail-fast——片段并集与登记键集不符即中止落盘)
var stray: Array[Variant] = []
for key in parts.keys():
if not BREAKPOINT_FIELDS.has(key):
stray.append(key)
if not stray.is_empty():
push_error("[冒险结构] 断点拼盘含未登记键: %s(fail-fast——片段与登记单源失同步)" % str(stray))
return parts
# 读侧单点(BREAKPOINT_FIELDS 策略标签分发;未登记键 fail-fast 不静默按 plain 兜)。
+27
View File
@@ -295,6 +295,33 @@ func restore_treasure_items(items: Array) -> void:
treasure_inventory.restore_inventory(items)
# ---- 断点自交片段(关卡域——拆件批 T4:应用层唯一拼盘落盘口的收件半边)----
# 关卡域片段(node_id/visited_ids/aux_items/shop_shelf/shop_visit_buys——五键自交;
# 停点续玩分支 _runner 为 null 时节点域按缺省落档〔与真机同构〕)
func breakpoint_fragment() -> Dictionary[Variant, Variant]:
var treasure_snapshot: Array[Variant] = []
if treasure_inventory != null:
treasure_snapshot = treasure_inventory.snapshot_inventory()
return {
"node_id": current_node_id(),
"visited_ids": visited_ids(),
"aux_items": treasure_snapshot,
"shop_shelf": _shop.shop_bp_shelf(),
"shop_visit_buys": _shop.shop_bp_visit_buys(),
}
# 断点重建参数面收口(原 _resume_bp 直读面收编——resume_state = {"node_id": int(-1 = 入口停点),
# "visited_ids": Array[int], "aux_items": Array};空字典 = 既有路径逐位不变)。
# 返回 resume_state 形态供 start_level_external 分派(_restore_to_stop / restore_treasure_items)。
func restore_fragment(resume_state: Dictionary) -> Dictionary:
var aux: Array = resume_state.get("aux_items", []) as Array
if not aux.is_empty():
restore_treasure_items(aux)
return resume_state
func _find_level_row(level_index: int) -> Dictionary:
return db.level_row(level_index) # 表访问收口(行集直扫归 LevelTable/ConfigDB 访问器)
@@ -191,12 +191,12 @@ func _init() -> void:
"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",
"feat_ticket_sold", "gain_journal", "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〕")
"P8 键集与冻结清单逐位一致(28 键契约——拆件批获得日记键为唯一声明扩展;增删键即红)〔wr2〕")
var vocab := ["plain", "clamp", "keep_on_null", "dual_merge", "params_fallback", "token_migration"]
var stray: Array = []
for k in fields.keys():
File diff suppressed because it is too large. Load diff