- 删除 design/dev/architecture/adr-0001~0007 共 7 份文件(内容留 git 历史可查) - architecture.md:删「ADR 审计」「ADR 立项清单」两节;清除全文 ADR-000X 注记、QQ 表/S 表 ADR 引用;未决表关闭已落定项(QQ-01/02/03),立项清单在途事项转登记 QQ-11~14;补 A1 生成姿态事实、autoload 四件清单、board_generator 性能姿态;文档状态立「事实源地位」条 - CLAUDE.md:恢复 RNG 单点硬性红线(去 ADR-0007 引用);新增「架构事实唯一源」红线;规范导航架构行标注唯一事实源 - doc-conventions:§5 增「架构事实唯一源」行;§11 活文档清单补架构文档 - registry:architecture.yaml 删 18 处 adr 字段、autoload_set 补 SaveManager/MetaProgression、board_rending 更新为 Node2D 已落地事实;entities.yaml 12 处引用清除 - game/ 代码注释约 60 处清 ADR-000X 引用(事实与日期保留);豁免标记 ADR-0007-exempt → rng-seed-exempt(main.gd/static_scan.sh/architecture.md/06/iter27 五处同步) - GDD/qa 实现报告/research 卡片/roadmap/tests README 引用全清;死链校验 1206 链接 0 死链、gdd_lint 0 不通过 - 代码中 adr/adr5 等为 adventure_runner 既有局部变量名(非 ADR 引用),未动
166 lines
7.7 KiB
GDScript
166 lines
7.7 KiB
GDScript
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||
# 秘宝两步交互控制器(规范 1.1 单一职责拆件):
|
||
# C 区槽位选中/取消(toggle)→ 棋盘点格使用目标拾取的两步交互状态机 + 库存/叠加层只读投影装配。
|
||
# 数据源 = runner 级库存只读面(query_inventory/query_overlays)+ 表行投影(display_name/target/effect_kinds);
|
||
# 写调用唯一经 战斗结算.use_treasure(core 侧四步编排——拒绝零消耗 toast 可辨认)。
|
||
# 装配纪律:settlement/runner 随关卡逐关更替——经组合根现值回调注入(仿 identity_resolver 只读回调
|
||
# 先例);呈现句柄(信息面板/toast/棋盘叠加层)由组合根注入;信号连接不在此发生,
|
||
# info_panel.treasure_slot_clicked 由组合根接至 on_slot_clicked。
|
||
# 无目标使用显式化:AP 类(target=none)无坐标可给——
|
||
# 调用面一律 attempt_use(handle)(cell 缺省=null=显式「无目标」),core 契约的
|
||
# Vector2i 形参收口于本类常量(treasure_items.validate_use 对 ap 行忽略目标值,哨兵不出本类)。
|
||
extends RefCounted
|
||
|
||
const NO_TARGET := Vector2i(-9, -9) # core use_treasure 形参占位(仅 target=none 行到达——validate_use 忽略该值)
|
||
|
||
var db
|
||
var meta # 解锁门控数据源(工坊解锁×活行交集——冒险开始组装;组合根 _ready 建立后注入)
|
||
var info_panel: HeroInfoPanelView
|
||
var toast: ToastStack # 反馈面(board_zone.toast 注入)
|
||
var board_view: BoardView
|
||
var _settlement_get: Callable # () → 当前战斗结算现值(组合根 lambda)
|
||
var _runner_get: Callable # () → 当前关卡推进器现值(组合根 lambda)
|
||
var _use_selection := 0 # 选中句柄(迭代 7 T4——0=未选中;选中期棋盘点击=使用目标拾取)
|
||
|
||
|
||
func _init(db_ref, meta_ref, info_panel_ref: HeroInfoPanelView, toast_ref: ToastStack,
|
||
board_view_ref: BoardView, settlement_get: Callable, runner_get: Callable) -> void:
|
||
db = db_ref
|
||
meta = meta_ref
|
||
info_panel = info_panel_ref
|
||
toast = toast_ref
|
||
board_view = board_view_ref
|
||
_settlement_get = settlement_get
|
||
_runner_get = runner_get
|
||
|
||
|
||
# aux 解锁门控集组装(迭代 7 T5——拍板③甲:工坊解锁 ∩ 过渡活行词表;罗盘退役/失义件/同调行不入集
|
||
# ——迭代 11 第①批 T1 切 treasure_transitional_rows 口径,同调行永不入过渡渠道)
|
||
func unlocked_set() -> Dictionary:
|
||
var out := {}
|
||
if meta == null:
|
||
return out
|
||
for row in db.treasure_transitional_rows():
|
||
var iid := String(row.get("item_id", ""))
|
||
if meta.is_item_unlocked(iid):
|
||
out[iid] = true
|
||
return out
|
||
|
||
|
||
# 选中态读数(组合根棋盘点击劫持判据——0=未选中)
|
||
func active_selection() -> int:
|
||
return _use_selection
|
||
|
||
|
||
# 槽点击(第一步):AP 类(target=none)点击即用(无目标);空间类进/出选中态(toggle 取消);
|
||
# 自动兑付件展示行(handle<0)无使用动作——点击给说明提示(反哺批⑤)。
|
||
func on_slot_clicked(slot_handle: int) -> void:
|
||
var settlement = _settlement_get.call()
|
||
var runner = _runner_get.call()
|
||
if settlement == null or runner == null or runner.treasure_inventory == null:
|
||
return
|
||
if slot_handle == 0:
|
||
_use_selection = 0
|
||
refresh_slots()
|
||
return
|
||
if slot_handle < 0:
|
||
toast.show_toast("自动兑付件:满足条件自动生效,无需手动使用", "info")
|
||
return
|
||
var rec: Dictionary[Variant, Variant] = {}
|
||
for r in inventory_projection():
|
||
if int(r.get("handle", 0)) == slot_handle:
|
||
rec = r
|
||
break
|
||
if rec.is_empty() or bool(rec.get("used", false)):
|
||
return
|
||
var arow: Dictionary[Variant, Variant] = db.treasure_item_row(String(rec.get("item_id", "")))
|
||
if String(arow.get("target", "none")) == "none":
|
||
attempt_use(slot_handle) # AP 类:显式无目标即用(不再伪造哨兵坐标)
|
||
return
|
||
if not bool(rec.get("usable", true)):
|
||
toast.show_toast("当前无可预读的未开格", "warn")
|
||
return
|
||
_use_selection = 0 if _use_selection == slot_handle else slot_handle # toggle 取消
|
||
refresh_slots()
|
||
if _use_selection != 0:
|
||
toast.show_toast("%s:点击目标格使用(9 宫窗口),再点道具取消" % String(rec.get("name", "")), "info")
|
||
|
||
|
||
# 使用受理(第二步;cell=空间类目标格,null=无目标;core 侧 use_treasure 四步编排——拒绝零消耗 toast 可辨认)
|
||
func attempt_use(handle: int, cell: Variant = null) -> void:
|
||
var settlement = _settlement_get.call()
|
||
if settlement == null:
|
||
return
|
||
var target: Vector2i = cell if cell != null else NO_TARGET
|
||
var v: Dictionary[Variant, Variant] = settlement.use_treasure(handle, target)
|
||
if not bool(v.ok):
|
||
toast.show_toast(String(v.get("reason", "无法使用")), "warn")
|
||
return
|
||
_use_selection = 0
|
||
refresh_slots()
|
||
refresh_overlays()
|
||
board_view.refresh() # 迭代 13 T8/D6——移除格即时落图(无移除态的件 diff 零变化幂等)
|
||
var arow: Dictionary[Variant, Variant] = db.treasure_item_row(String(v.get("item_id", "")))
|
||
toast.show_toast("已使用 %s" % String(arow.get("display_name", "")), "info")
|
||
|
||
|
||
# 库存投影(C 区四槽取数面——字形=名首字;usable=板级零信息预判回调接排雷玩法只读面)
|
||
func inventory_projection() -> Array:
|
||
var runner = _runner_get.call()
|
||
if runner == null or runner.treasure_inventory == null:
|
||
return []
|
||
var settlement = _settlement_get.call()
|
||
var ms = settlement.minesweeper if settlement != null else null
|
||
var cb := Callable()
|
||
if ms != null:
|
||
cb = func(_c: Vector2i) -> bool:
|
||
return ms.has_unrevealed() # 只读查询面(排雷玩法 has_unrevealed——手搓全板双循环收口)
|
||
var out: Array[Variant] = []
|
||
var cs: Dictionary[Variant, Variant] = settlement.attune_convert_state() if settlement != null else {}
|
||
var auto_seen := false # 自动兑付件是否已随骑库存出现(恒否——charges=0 不入骑库存;守合成行重复)
|
||
for rec in runner.treasure_inventory.query_inventory(cb):
|
||
var arow: Dictionary[Variant, Variant] = db.treasure_item_row(String(rec.get("item_id", "")))
|
||
var dn := String(arow.get("display_name", ""))
|
||
# 积点进度浮窗行(反哺批⑤——自动兑付件 C 区悬停 tooltip 补 N/M 进度;件 id 锁定=装配行唯一)
|
||
var note := ""
|
||
if bool(cs.get("assembled", false)) and String(cs.get("item_id", "")) == String(rec.get("item_id", "")):
|
||
note = "积点 %d/%d" % [int(cs.get("points", 0)), int(cs.get("need", 0))]
|
||
auto_seen = true
|
||
out.append({
|
||
"handle": int(rec.get("handle", 0)),
|
||
"item_id": String(rec.get("item_id", "")),
|
||
"glyph": dn.left(1) if dn != "" else "?",
|
||
"name": dn,
|
||
"used": bool(rec.get("used", false)),
|
||
"usable": bool(rec.get("usable", true)),
|
||
"note": note,
|
||
})
|
||
# 自动兑付件展示行(反哺批⑤补——charges=0 不入骑库存〔_reseed_attune_charges 跳过注入〕,
|
||
# C 区四槽末尾补合成行承载悬停进度;handle=-1 非库存句柄:槽点击走说明 toast 分支不进两步交互)
|
||
if bool(cs.get("assembled", false)) and not auto_seen:
|
||
var adn := String(cs.get("display_name", ""))
|
||
if adn != "":
|
||
out.append({
|
||
"handle": -1,
|
||
"item_id": String(cs.get("item_id", "")),
|
||
"glyph": adn.left(1),
|
||
"name": adn,
|
||
"used": false,
|
||
"usable": true,
|
||
"note": "积点 %d/%d" % [int(cs.get("points", 0)), int(cs.get("need", 0))],
|
||
})
|
||
return out
|
||
|
||
|
||
func refresh_slots() -> void:
|
||
info_panel.set_treasure_inventory(inventory_projection(), _use_selection)
|
||
|
||
|
||
# 叠加层投影注入(use 成功/三清后——棋盘标记与预读徽章重算)
|
||
func refresh_overlays() -> void:
|
||
var runner = _runner_get.call()
|
||
if runner == null or runner.treasure_inventory == null or board_view == null:
|
||
return
|
||
var ov: Dictionary[Variant, Variant] = runner.treasure_inventory.query_overlays()
|
||
board_view.set_treasure_overlays(ov.get("mine_reveal", []), ov.get("reward_preview", []))
|