T3 呈现层结构段: main.gd 1395→1237三件迁出(RowPacer排播泵/AuxInteractController两步交互含-9,-9哨兵改显式无目标/GP影子账本独立件)+模态栈收口(settings/camp/tune改走_open_modal+_input_locked改ModalStack.has_open查询+三例外)+HUD单通道(收尾反馈/结算视觉进presenter,play_round_sweep时序敏感留组合根注记)+链式穿透清除(variation回调注入/phase经snapshot)+confirm_dialog构建序复位删move_child创可贴+登记序树序漂移复位;保留面=main._row_queue委托/pace_delay_for/_flush_row_queue/_adventure_gp_view写面全数核对过;后续卡注=main到900还需迁三选一编排与debug接线(06登记)

This commit is contained in:
DKChan committed 2026-10-02 08:46:37 +08:00
1 parent d1460327a4
commit 0691a188fd
10 files changed
+473 -283

No files matched your search

@@ -0,0 +1,141 @@
# VERTICAL SLICE - NOT FOR PRODUCTION
# 魔法道具两步交互控制器(迭代 7 T4;T3 拆件批 2026-10-02 自 main.gd 迁出——规范 1.1 单一职责):
# C 区槽位选中/取消(toggle)→ 棋盘点格使用目标拾取的两步交互状态机 + 库存/叠加层只读投影装配。
# 数据源 = runner 级库存只读面(query_inventory/query_overlays)+ 表行投影(display_name/target/effect_kinds);
# 写调用唯一经 战斗结算.use_aux(core 侧四步编排——拒绝零消耗 toast 可辨认)。
# 装配纪律:settlement/runner 随关卡逐关更替——经组合根现值回调注入(仿 identity_resolver 只读回调
# 先例);呈现句柄(信息面板/toast/棋盘叠加层)由组合根注入;ADR-0001——信号连接不在此发生,
# info_panel.aux_slot_clicked 由组合根接至 on_slot_clicked。
# 无目标使用显式化(T3 收口):AP 类(target=none)本无坐标可给——原调用面伪造 Vector2i(-9,-9)
# 哨兵坐标;现调用面一律 attempt_use(handle)(cell 缺省=null=显式「无目标」),core 契约的
# Vector2i 形参收口于本类常量(aux_items.validate_use 对 ap 行忽略目标值,哨兵不出本类)。
extends RefCounted
const NO_TARGET := Vector2i(-9, -9) # core use_aux 形参占位(仅 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——拍板③甲:工坊解锁 ∩ 活行词表;罗盘退役/失义件不入集)
func unlocked_set() -> Dictionary:
var out := {}
if meta == null:
return out
for row in db.aux_active_rows():
var iid := String(row.get("item_id", ""))
if meta.save.item_unlocked(iid):
out[iid] = true
return out
# 选中态读数(组合根棋盘点击劫持判据——0=未选中)
func active_selection() -> int:
return _use_selection
# 槽点击(第一步):AP 类(target=none)点击即用(无目标);空间类进/出选中态(toggle 取消)。
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.aux_inventory == null:
return
if slot_handle == 0:
_use_selection = 0
refresh_slots()
return
var rec: Dictionary = {}
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 = db.aux_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_aux 四步编排——拒绝零消耗 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 = settlement.use_aux(handle, target)
if not bool(v.ok):
toast.show_toast(String(v.get("reason", "无法使用")), "warn")
return
_use_selection = 0
refresh_slots()
refresh_overlays()
var arow: Dictionary = db.aux_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.aux_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:
for y in ms.current_board().height:
for x in ms.current_board().width:
if not ms.is_revealed(Vector2i(x, y)):
return true
return false
var out: Array = []
for rec in runner.aux_inventory.query_inventory(cb):
var arow: Dictionary = db.aux_item_row(String(rec.get("item_id", "")))
var dn := String(arow.get("display_name", ""))
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)),
})
return out
func refresh_slots() -> void:
info_panel.set_aux_inventory(inventory_projection(), _use_selection)
# 叠加层投影注入(use 成功/三清后——棋盘标记与预读徽章重算)
func refresh_overlays() -> void:
var runner = _runner_get.call()
if runner == null or runner.aux_inventory == null or board_view == null:
return
var ov: Dictionary = runner.aux_inventory.query_overlays()
board_view.set_aux_overlays(ov.get("mine_reveal", []), ov.get("reward_preview", []))