- 删除 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 引用),未动
219 lines
9.8 KiB
GDScript
219 lines
9.8 KiB
GDScript
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||
# 营地/商店投影控制器(规范 1.1 单一职责拆件):
|
||
# 营地面开合/快照投影/锻造·同调·天赋树·Fork 购买与切换 + 商店房购买/离开(pool 与 product 双面)。
|
||
# 装配纪律(仿 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(ctx: CampFlowContext) -> void:
|
||
# 迭代 27 T4——构造参数收拢 context 对象(P1-5:13 参长签名收一处命名面;字段逐一同名迁移)
|
||
db = ctx.db
|
||
_meta_get = ctx.meta_get
|
||
_save_get = ctx.save_get
|
||
camp_overlay = ctx.camp_overlay
|
||
shop_overlay = ctx.shop_overlay
|
||
toast = ctx.toast
|
||
start_overlay = ctx.start_overlay
|
||
logger = ctx.logger
|
||
_runner_get = ctx.runner_get
|
||
_open_modal = ctx.open_modal
|
||
_close_modal = ctx.close_modal
|
||
_refresh_presentations = ctx.refresh_presentations
|
||
_refresh_resume_button = ctx.refresh_resume_button
|
||
|
||
|
||
# 打开营地(主菜单入口——开合与开始层互斥仿设置面先例:开始覆盖层非模态且树序在后,
|
||
# 规范 2.3 遮挡纪律;打开时先刷条件解锁——达标未解锁就地入账)
|
||
func open_camp() -> void:
|
||
var meta = _meta_get.call()
|
||
if meta == null:
|
||
return
|
||
meta.refresh_treasure_unlocks() # 迭代 22 T2——条件解锁刷新换代(旧工坊面退役)
|
||
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() # 关闭后刷新顶栏(工坊解锁可能变了余额侧读数面)
|
||
|
||
|
||
# 营地快照投影(纯读——秘宝匣 12 件/同调装配/树面/锻造工坊两区六路线/余额/最近冒险;拼句归
|
||
# CampOverlay,账面归 MetaProgression。属性锻造三行投影已删——迭代 21 T4 柜台退役;工坊六行换代
|
||
# = 迭代 22 T2 秘宝匣;forge 键=迭代 23 T6 锻造工坊路线行板投影)
|
||
func camp_data() -> Dictionary:
|
||
var meta = _meta_get.call()
|
||
return {
|
||
"balance": meta.keystone_balance() if meta != null else 0,
|
||
"workshop": meta.treasure_shop_status() if meta != null else [], # 键名沿 workshop(视图消费键不变——行内容=秘宝匣 12 件解锁投影)
|
||
"forge": meta.forge_shop_status() if meta != null else [], # 迭代 23 T6——锻造工坊两区六路线投影
|
||
"attune": meta.attune_camp_status() if meta != null else {}, # 迭代 11 第①批 T4——同调装配面投影
|
||
"tree": meta.tree_camp_rows() if meta != null else [], # 树面投影(含 forge_workshop/ks_workshop 钥匙门判据行)
|
||
"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
|
||
# 秘宝钥石购买(迭代 22 秘宝批 T4——秘宝匣柜台购买钮意图;写口唯一=buy_treasure_unlock,
|
||
# 余额闸/重复购/条件件误购三重拒绝零副作用)
|
||
if attr != "attune_refresh":
|
||
if not meta.buy_treasure_unlock(attr):
|
||
toast.show_toast("购买被拒(余额不足或不可购买)", "warn")
|
||
return
|
||
camp_overlay.refresh_camp(camp_data())
|
||
_refresh_presentations.call()
|
||
return
|
||
# Y 刷新升级行购买(迭代 11 第①批 T4——P1 甲;双入口之一 Q1 裁乙——树上 ks_refresh 同账;
|
||
# 按钮禁用为常态防线,兜底拒绝可见 pattern 1。属性锻造面已退役——buy_forge 分支随批删除)
|
||
if not meta.buy_attune_refresh():
|
||
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()
|
||
|
||
|
||
# ---- 锻造工坊三意图(迭代 23 T6——契约 9:写口唯一归 MetaProgression 锻造写口族;UI 只发意图)----
|
||
# 路线购买(几何价;余额不足/未解锁/满级=核心拒绝零副作用,兜底拒绝可见 pattern 1)
|
||
func on_camp_forge_buy(route_id: String) -> void:
|
||
var meta = _meta_get.call()
|
||
if meta == null:
|
||
return
|
||
if not meta.forge_buy_level(route_id):
|
||
toast.show_toast("锻造被拒——钥石不足或路线不可锻造", "warn")
|
||
return
|
||
camp_overlay.refresh_camp(camp_data())
|
||
_refresh_presentations.call()
|
||
|
||
|
||
# 一次性解锁价直购(并联双通道②;已解锁=拒绝零收费)
|
||
func on_camp_forge_unlock(route_id: String) -> void:
|
||
var meta = _meta_get.call()
|
||
if meta == null:
|
||
return
|
||
if not meta.forge_buy_unlock(route_id):
|
||
toast.show_toast("解锁被拒——钥石不足或已解锁", "warn")
|
||
return
|
||
camp_overlay.refresh_camp(camp_data())
|
||
_refresh_presentations.call()
|
||
|
||
|
||
# 当前武器/甲选中切换(免费;未解锁拒选)
|
||
func on_camp_forge_select(route_id: String) -> void:
|
||
var meta = _meta_get.call()
|
||
if meta == null:
|
||
return
|
||
var ok: bool = meta.forge_select_weapon(route_id) if String(db.forge_route(route_id).get("kind", "")) == "weapon" \
|
||
else meta.forge_select_armor(route_id)
|
||
if not ok:
|
||
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()
|