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

189 lines
8.3 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
# 营地/商店投影控制器(迭代 13 T4/D4——二期家族③ 自 main.gd 迁出;规范 1.1 单一职责):
# 营地面开合/快照投影/锻造·同调·天赋树·Fork 购买与切换 + 商店房购买/离开(pool 与 product 双面)。
# 装配纪律(ADR-0001,仿 treasure_interact_controller 先例):信号连接不在此发生——campbtn/
# camp_overlay 四信号/shop_overlay 两信号由组合根接至本类 on_* 入口;模态栈/呈现刷新/续玩按钮
# 刷新经组合根回调注入;runner 随局更替——现值回调注入;写调用唯一经 MetaProgression 购买原语
# 与 LevelRunner.buy_*(core 侧单口,本类零直写——拒绝可见 pattern 1 兜底 toast 保留原地)。
extends RefCounted
var db
var _meta_get: Callable # () → 元进度账本现值(组合根 lambda——探针 isolate_save 换注面,迭代13 T4 实证)
var _save_get: Callable # () → 存档承载现值(同上——营地 recent 摘要读数面)
var camp_overlay: CampOverlay
var shop_overlay: ShopOverlay
var toast: ToastStack
var start_overlay: Control # 开始层互斥面(开合仿设置面先例——非模态且树序在后,规范 2.3)
var logger # 试玩日志(ux_reject 取数面)
var _runner_get: Callable # () → 关卡推进器现值(组合根 lambda——随局更替)
var _open_modal: Callable
var _close_modal: Callable
var _refresh_presentations: Callable
var _refresh_resume_button: Callable
func _init(db_ref, meta_get: Callable, save_get: Callable, camp_overlay_ref: CampOverlay,
shop_overlay_ref: ShopOverlay, toast_ref: ToastStack, start_overlay_ref: Control,
logger_ref, runner_get: Callable, open_modal: Callable, close_modal: Callable,
refresh_presentations: Callable, refresh_resume_button: Callable) -> void:
db = db_ref
_meta_get = meta_get
_save_get = save_get
camp_overlay = camp_overlay_ref
shop_overlay = shop_overlay_ref
toast = toast_ref
start_overlay = start_overlay_ref
logger = logger_ref
_runner_get = runner_get
_open_modal = open_modal
_close_modal = close_modal
_refresh_presentations = refresh_presentations
_refresh_resume_button = refresh_resume_button
# 打开营地(主菜单入口——开合与开始层互斥仿设置面先例:开始覆盖层非模态且树序在后,
# 规范 2.3 遮挡纪律;打开时先刷工坊解锁——达标未解锁就地入账)
func open_camp() -> void:
var meta = _meta_get.call()
if meta == null:
return
meta.refresh_workshop_unlocks()
start_overlay.visible = false
_open_modal.call(camp_overlay) # 模态栈单口(T3 收口批——登记层开合一律走栈)
camp_overlay.open_camp(camp_data())
_refresh_presentations.call()
func on_camp_closed() -> void:
_close_modal.call(camp_overlay) # 模态栈单口(T3 收口批)
start_overlay.visible = true
_refresh_resume_button.call() # 迭代 8 卡②——回菜单按断点在档态刷新
_refresh_presentations.call() # 关闭后刷新顶栏(工坊解锁可能变了余额侧读数面)
# 营地快照投影(纯读——锻造三行/工坊六行/余额/最近冒险;拼句归 CampOverlay,账面归 MetaProgression)
func camp_data() -> Dictionary:
var meta = _meta_get.call()
var forge_rows: Array[Dictionary] = []
if meta != null:
for attr in MetaTable.FORGE_ATTRS:
var row: Dictionary = db.meta_forge_row(attr)
forge_rows.append({
"attr": attr,
"display_name": String(row.get("display_name", "")),
"level": meta.forge_level(attr),
"flat": int(row.get("flat_value", 0)),
"price": meta.forge_price(attr),
})
return {
"balance": meta.keystone_balance() if meta != null else 0,
"forge": forge_rows,
"workshop": meta.workshop_status() if meta != null else [],
"attune": meta.attune_camp_status() if meta != null else {}, # 迭代 11 第①批 T4——同调装配面投影
"tree": meta.tree_camp_rows() if meta != null else [], # 迭代 12 T9——天赋树面投影
"fork_groups": meta.tree_fork_groups() if meta != null else [],
"recent": _save_get.call().recent_summaries() if _save_get.call() != null else [],
}
func on_camp_buy(attr: String) -> void:
var meta = _meta_get.call()
if meta == null:
return
if attr == "attune_refresh":
# Y 刷新升级行购买(迭代 11 第①批 T4——P1 甲;按钮禁用为常态防线,兜底拒绝可见 pattern 1)
if not meta.buy_attune_refresh():
toast.show_toast("钥石不足——买不起这次升级", "warn")
return
camp_overlay.refresh_camp(camp_data())
_refresh_presentations.call()
return
if not meta.buy_forge(attr):
# 余额不足(按钮禁用为常态防线——兜底拒绝可见,pattern 1)
toast.show_toast("钥石不足——买不起这件强化", "warn")
return
camp_overlay.refresh_camp(camp_data())
_refresh_presentations.call()
# 同调装配切换(迭代 11 第①批 T4——装配关系唯一写口经 MetaProgression.toggle_attunement;满位兜底拒绝可见)
func on_camp_attune_toggle(item_id: String) -> void:
var meta = _meta_get.call()
if meta == null:
return
if not meta.toggle_attunement(item_id):
toast.show_toast("同调位已满——先卸下再装配", "warn")
return
camp_overlay.refresh_camp(camp_data())
# 天赋树购买(迭代 12 T9——写口唯一归 MetaProgression.tree_buy/tree_buy_fork;Fork 节点购=激活侧)
func on_camp_tree_buy(node_id: String) -> void:
var meta = _meta_get.call()
if meta == null:
return
var ok: bool = meta.tree_buy_fork(node_id) if String(db.tree_node(node_id).get("kind", "")) == "fork" else meta.tree_buy(node_id)
if not ok:
toast.show_toast("购买被拒——前置/行门槛/余额不足", "warn")
return
camp_overlay.refresh_camp(camp_data())
_refresh_presentations.call()
# A/B 免费切换(迭代 12 T8——同组换侧零币;未激活组走购买面)
func on_camp_fork_toggle(group: String, side: String) -> void:
var meta = _meta_get.call()
if meta == null:
return
if not meta.tree_toggle_fork(group, side):
toast.show_toast("切换被拒——未激活组请先购买", "warn")
return
camp_overlay.refresh_camp(camp_data())
_refresh_presentations.call()
func on_shop_buy(product_id: String) -> void:
var runner = _runner_get.call()
if runner == null:
return
if runner.is_shop_pool():
# pool 面:买 1 → 核心全重抽——货架整架重投喂(重抽反馈;余额不足核心拒绝零副作用)
if not runner.buy_pool_item(product_id):
logger.log_ux("ux_reject", {"source": "wallet", "sub_reason": "insufficient_gp"})
toast.show_toast("金币不足——买不起这件商品", "warn")
return
shop_overlay.open_shelf(runner.shop_shelf(), int(runner.gp))
shop_overlay.set_visit_info(int(runner.shop_bp_visit_buys()), float(db.shop_price_a())) # T6 爬价规则行
shop_overlay.set_refresh_display(runner.shop_refresh_free_left(), runner.shop_refresh_cost(), int(runner.gp) >= runner.shop_refresh_cost())
_refresh_presentations.call()
return
if not runner.buy_product(product_id):
# 余额不足(按钮禁用为常态防线——兜底拒绝可见,pattern 1)
logger.log_ux("ux_reject", {"source": "wallet", "sub_reason": "insufficient_gp"})
toast.show_toast("金币不足——买不起这件商品", "warn")
return
shop_overlay.refresh_state(runner.shop_products(), int(runner.gp))
_refresh_presentations.call()
# MC-1:刷新意图(shop_overlay.refresh_requested——核心 shop_refresh 受理后整架重投喂=重抽反馈)
func on_shop_refresh() -> void:
var runner = _runner_get.call()
if runner == null or not runner.is_shop_pool():
return
if not runner.shop_refresh():
toast.show_toast("金币不足——刷新失败", "warn")
return
shop_overlay.open_shelf(runner.shop_shelf(), int(runner.gp))
shop_overlay.set_visit_info(int(runner.shop_bp_visit_buys()), float(db.shop_price_a())) # T6 爬价规则行
shop_overlay.set_refresh_display(runner.shop_refresh_free_left(), runner.shop_refresh_cost(), int(runner.gp) >= runner.shop_refresh_cost())
_refresh_presentations.call()
func on_shop_leave() -> void:
_close_modal.call(shop_overlay)
var runner = _runner_get.call()
if runner != null:
# 复用兑付确认纪律(接口漂移清偿):离开 ≠ 推进——确认后经 choices_presented 弹出选路口
runner.continue_after_node()