Files
gplan-2/game/core/attune_loadout.gd
T
DKChan 80fe4b8f42 副卡(配装批wr1-wr3): AttuneLoadout建件+充能单源化+RunLedger整类退役——attune_loadout.gd新件(纯无状态:种行seed_for_adventure全量不筛周期/reset_cycle按周期差额补行/consume_charge行标used幂等/charges_left行派生唯一读法/断点片段键值形状不变);三处事后差额对账退役(adv._seed_attune_charges/level.sync_attune_inventory/battle._reseed_attune_charges循环体→reset_cycle);行承载面升级=treasure_inventory提冒险级共享(adv建件经ctx注入level,关末清空改选择性保留——同调未用行跨关存活=原字典轨跨关记忆的行化等价,已耗行作废=C区投影与剩余读数同构);battle充能面改指loadout(encounter+loadout/−ledger键,:387类型检查删,直构null=零装配同语义);RunLedger整类删除(.gd+.uid,grep零引用;effect_engine/level_runner/hero_ledger注释面随批清);测试直构改址(meta_forge充能读行派生/feat_vouchers种行幂等反例/round_flow V1桶宿主字典/treasure_effects enc adv_bucket);跨版续玩兜底(改前档关卡完成停点行已被关末清空——键在行缺,按键值差额补行=与字典轨续玩逐位等价,新档行集为源差额零操作)。代拍板:①treasure_inventory提冒险级共享(卡片文件结构图treasure_items零触碰保持,关卡末清空语义调用点改选择性保留——「同调行始终留在treasure_inventory」的承载前提,玩家可见C区投影零变化)②跨版续玩键值兜底补行(契约3「行集为源」的跨版例外——al2对拍兜底)。验收:bp1对拍292dumps×27键——attunement_charges全部逐位一致(单源对拍达成);aux_items关卡完成停点24处留存同调行(「同调行始终留库存」的直接承载——副卡契约3「aux_items键值形态逐位不变」声明与行留存在该停点形态互斥,grilling⑨K4实查边界漏判,反哺点呈报);六套件零红+冒烟17项0失败〔配装批 V1〕〔配装批 V2〕〔配装批 V3〕
2026-10-27 04:36:18 +08:00

118 lines
6.1 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 同调配装件:装备中的秘宝件对象集唯一所有者(每件内含充能单位与重置周期——冒险域拆分姊妹批自 RunLedger 充能字典轨迁移)。
## 同调充能单源化:物化行(treasure_inventory 内 source_class="attunement" 行)为使用面唯一事实源——
## 剩余次数由行的未用数派生(唯一读法,无影子账);字典轨与三处事后差额对账退役,改「写时种行/补行」。
## 种行/补行写时同步:冒险开始全量种行(不筛周期——battle/level 周期件开局即有行,C 区投影行数不变);
## battle/level 周期回满走 reset_cycle(已耗 handle 不回收=作废、差额补新——语义逐字承原编排器重置);
## 同调未用行跨关存活(关卡域库存提为冒险级共享、关末清空改选择性保留——单源化的行承载前提)。
## 旧档读侧以行集为源恢复使用面,attunement_charges 键读值仅作一致性校验读数(不符记日志不 fatal——键值本就派生自行)。
## Core 层零 UI;零随机(充能域无 RNG 消费);随机无关、数值只读 db。
class_name AttuneLoadout
extends RefCounted
var db: ConfigDb
var _ids: Array[Variant] = [] # 装配集(活引用注入——宿主 start/resume 时点下传)
func _init(db_ref: ConfigDb, attunement_ids: Array) -> void:
db = db_ref
set_attunement(attunement_ids)
# 装配集注入(活引用——宿主 start_adventure/resume 参数面锁定后下传)
func set_attunement(attunement_ids: Array) -> void:
_ids = attunement_ids
func item_ids() -> Array:
return _ids.duplicate()
# ---- 写时种行/补行(替代三处事后差额对账)----
# 冒险开始:全部 active_charged 件种行(**不筛周期**——承现 _seed_attune_charges 全量种账+sync 全量投行
# 语义;battle/level 周期件开局即有行,C 区投影行数不变)
func seed_for_adventure(inventory: TreasureItems) -> void:
for iid_v in _ids:
var iid := String(iid_v)
var arow: Dictionary[Variant, Variant] = db.treasure_item_row(iid)
if String(arow.get("form", "")) != "active_charged":
continue
_ensure_rows(iid, maxi(0, int(arow.get("charges_per_adventure", 0))), inventory)
# battle/level 周期回满(**按周期筛**;已耗行作废、差额补新——语义逐字承原编排器 _reseed_attune_charges;
# charges_per_adventure=0=自动兑付件不入使用面〔试炼镐——不走骑库存面〕)
func reset_cycle(cycle: String, inventory: TreasureItems) -> void:
for iid_v in _ids:
var iid := String(iid_v)
var arow: Dictionary[Variant, Variant] = db.treasure_item_row(iid)
if String(arow.get("form", "")) != "active_charged":
continue
if String(arow.get("charge_reset", "adventure")) != cycle:
continue
var want := int(arow.get("charges_per_adventure", 0))
if want < 1:
continue # charges=0=自动兑付件(不走骑库存面,兑付=积点段自动 grant_ap)
_ensure_rows(iid, want, inventory)
# 差额补行单点(未用行数不足 want 即补——「差额注入防续玩双份」现役语义逐字)
func _ensure_rows(iid: String, want: int, inventory: TreasureItems) -> void:
var existing := 0
for r in inventory.snapshot_inventory():
if String(r.item_id) == iid and String(r.source_class) == "attunement" and not bool(r.used):
existing += 1
for i in range(maxi(0, want - existing)):
inventory.add_item(iid, "attunement", Vector2i(-1, -1), 0)
# ---- 使用面(战斗路径改指)----
# 充能消耗(承原 attune_charge_consume「扣账 -1」语义——单源化后=行标 used 幂等承载:
# 使用管线 consume 句柄已标行 used,本口兜底标未用行〔双入口同点防御——行 used 不可逆幂等〕)
func consume_charge(item_id: String, inventory: TreasureItems) -> void:
for r in inventory.snapshot_inventory():
if String(r.item_id) == item_id and String(r.source_class) == "attunement" and not bool(r.used):
inventory.consume(int(r.handle)) # 行标 used(与使用管线 consume 同口——幂等兜底)
return
# ---- 读面(行派生——唯一读法)----
# 剩余次数(未用行数派生——原字典轨读数逐位等价)
func charges_left(item_id: String, inventory: TreasureItems) -> int:
var n := 0
for r in inventory.snapshot_inventory():
if String(r.item_id) == item_id and String(r.source_class) == "attunement" and not bool(r.used):
n += 1
return n
# ---- 断点自交片段(应用层拼盘口收件——键名与策略不变)----
# attunement_charges 键(值形状=原字典轨 {item_id:剩余}——由行派生,键名/策略不变)
func breakpoint_fragment(inventory: TreasureItems) -> Dictionary[Variant, Variant]:
var charges: Dictionary[Variant, Variant] = {}
for iid_v in _ids:
var iid := String(iid_v)
var arow: Dictionary[Variant, Variant] = db.treasure_item_row(iid)
if String(arow.get("form", "")) == "active_charged":
charges[iid] = charges_left(iid, inventory)
return {"attunement_charges": charges}
# 旧档读侧(以行集为源恢复使用面——键值仅校验读数:不符记日志不 fatal;缺键保持种子态=原语义)。
# 调用时点=行集已随 aux_items 回填(恢复链序:库存回填先于本口)。
func restore_fragment(frag: Dictionary, inventory: TreasureItems) -> void:
var stored: Variant = frag.get("attunement_charges", null)
if not (stored is Dictionary) or (stored as Dictionary).is_empty():
return # 缺键 = 旧档/无断点,保持种子态(原语义)
for iid in (stored as Dictionary).keys():
var derived := charges_left(String(iid), inventory)
var recorded := int((stored as Dictionary)[iid])
if derived != recorded:
# 跨版续玩兜底:改前档关卡完成停点的同调行已被关末清空(键在行缺)——按键值差额补行,
# 使用面与改前字典轨续玩逐位等价;新档行集为源时差额=0 零操作(行集仍为读数唯一来源)
push_warning("[同调配装] 充能校验读数与行派生不一致(%s 档=%d 行派生=%d——按键值差额补行)"
% [String(iid), recorded, derived])
_ensure_rows(String(iid), recorded, inventory)