Files
gplan-2/game/presentation/treasure_interact_controller.gd
T

160 lines
7.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.
## VERTICAL SLICE - NOT FOR PRODUCTION
## 秘宝两步交互控制器:C 区槽位选中/取消(toggle)→ 棋盘点格使用目标拾取的状态机 + 库存/叠加层只读投影装配。
## 写调用唯一经 settlement.use_treasure;信号连接只归组合根(info_panel.treasure_slot_clicked 接至 on_slot_clicked)。
## settlement/runner 随关卡逐关更替——经组合根现值回调注入。
## AP 类(target=none)调用面一律 attempt_use(handle)(cell 缺省=null=显式无目标)——哨兵坐标收口本类,不出控制器。
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 # 选中句柄(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 解锁门控集组装:工坊解锁 ∩ 过渡活行词表(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() # 移除格即时落图(无移除态的件 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", []))