118 lines
6.1 KiB
GDScript
118 lines
6.1 KiB
GDScript
## 同调配装件:装备中的秘宝件对象集唯一所有者(每件内含充能单位与重置周期——冒险域拆分姊妹批自共享充能账字典轨迁移)。
|
||
## 同调充能单源化:物化行(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.crossrun.treasure_items.row_by_id(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.crossrun.treasure_items.row_by_id(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.crossrun.treasure_items.row_by_id(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)
|