feat(t5): 奖励节点三选一+工坊解锁门控+封存呈现+node-reward spec 出生

①node_overlay:show_reward_items 三选一(池≤3 全列——当前三件即全列;池空直通=切片期合法空态保留;选择前继续隐藏=强制构筑点语义)+item_chosen 信号+close_reward 收屏
②main:奖励分支接三选一(活行∩解锁集,注册序前 3,呈现侧零 RNG)+_on_reward_item_chosen(入库存来源归因 reward_node→toast→_on_node_continue 兑付确认回选路零自动推进)+_reward_node_choices
③解锁门控链(拍板③甲=解锁进投放池):main _aux_unlocked_set(工坊解锁∩活行;罗盘退役/失义件不入集)→adventure params.aux_unlocked→LevelRunner.set_aux_unlocked→BattleOrchestrator 公有 setter→RewardEconomy.set_aux_unlocked+_rollable_rows aux 行过滤——空集默认零投放=白板/夹具态模拟读数零扰动
④工坊呈现:workshop_status 投影+deferred/retired 状态面;camp 文案=已解锁·能力封存中(失义件)/已退役(罗盘)
⑤node-reward.md spec v1.0 出生(06 条目兑现)
坑一轮:BattleOrchestrator 无公有 reward_economy(E1/E3 封装)——公有 setter 转发;竖线锚四处拼接病自愈
闸:冒烟 577/0+四探针 PASS(click 问题=0)+单测 0 FAIL
This commit is contained in:
DKChan committed 2026-09-29 07:43:51 +08:00
1 parent 2f3a87e98f
commit 9553d6b182
9 files changed
+146 -4

No files matched your search

+51 -2
View File
@@ -68,6 +68,7 @@ var _adventure_gp_view := 0 # GP 冒险钱包呈现侧影子(迭
# wallet_changed gain/buy 载荷=入账后总额 level_runner.gd:482/539)
var _adventure_gp_final := 0 # 收束清零拍暂存的最终合计("clear" 到达时自影子转存)
var _aux_use_selection := 0 # 魔法道具选中句柄(迭代 7 T4——0=未选中;选中期棋盘点击=使用目标拾取)
var _aux_unlocked := {} # aux 解锁门控集(迭代 7 T5——工坊解锁×活行交集,冒险开始组装)
var camp_overlay: CampOverlay # 营地覆盖层(元进度实现批——主菜单营地入口兑付呈现)
var tune_panel: TunePanel
@@ -149,6 +150,7 @@ func _build_ui() -> void:
legend_overlay.closed.connect(_on_modal_closed.bind(legend_overlay))
review_overlay.closed.connect(_on_modal_closed.bind(review_overlay))
node_overlay.continue_pressed.connect(_on_node_continue)
# 迭代 7 T5——item_chosen 连接在奖励分支首唤时防重接线(上方)
level_screen.route_chosen.connect(_on_route_chosen)
level_screen.abandon_requested.connect(_on_abandon_requested) # 放弃入口(2026-09-21 用户拍板——台账 119)
shop_overlay.buy_requested.connect(_on_shop_buy)
@@ -250,7 +252,7 @@ func _start_adventure() -> void:
# 永久强化注入(元进度实现批):冒险开始参数面——meta 现算 override 显式传参(全零 = 白板)
# 首次冒险脚本(关卡节点图生成 v0.4):存档布尔位 false = 全关固定模板 0(教学重放);置位后逐关抽签
var first_script: bool = not save_manager.is_first_adventure_done()
if not adventure.start_adventure(seed_val, {"hero_override": meta.hero_override() if meta != null else {}, "first_script": first_script}):
if not adventure.start_adventure(seed_val, {"hero_override": meta.hero_override() if meta != null else {}, "first_script": first_script, "aux_unlocked": _aux_unlocked_set()}):
_fatal_screen("冒险启动失败——见输出日志")
return
@@ -284,7 +286,9 @@ func _on_runner_node(node: Dictionary, info: Dictionary) -> void:
log_view.append_bb(Narrator.node_banner(runner, node, t))
elif t == "reward":
_open_modal(node_overlay)
node_overlay.show_reward()
node_overlay.item_chosen.connect(_on_reward_item_chosen) if not node_overlay.item_chosen.is_connected(_on_reward_item_chosen) else null
node_overlay.show_reward_items(_reward_node_choices())
log_view.append_bb(Narrator.reward_line())
log_view.append_bb(Narrator.reward_line())
elif t == "rest":
var healed: int = int(info.get("healed", 0))
@@ -728,6 +732,51 @@ func _on_shop_leave() -> void:
# ---- 魔法道具 HUD 接线(迭代 7 T4——库存条+两步交互+叠加层呈现;数据源=runner 级库存只读面)----
# ---- 奖励节点三选一(迭代 7 T5——拍板②丙:奖励屏=小构筑点;「节点奖励=功能道具正式渠道」兑现)----
# aux 解锁门控集组装(迭代 7 T5——拍板③甲:工坊解锁 ∩ 活行词表;罗盘退役/失义件不入集)
func _aux_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
# 摇取面:活行 ∩ 工坊解锁集(拍板③甲——解锁进投放池同源口径);池 ≤3 全列(当前三件=选一即全列),
# 池扩充后加权摇三(确定性摇取:读 seed 流外——呈现侧零 RNG 消费纪律 → 全列前 3 按注册序)。
func _reward_node_choices() -> Array:
if runner == null or runner.aux_inventory == null:
return []
var out: Array = []
for row in db.aux_active_rows():
var iid := String(row.get("item_id", ""))
if not _aux_unlocked.has(iid):
continue # 未解锁不入奖励池(与掉落投放同门控)
out.append({
"item_id": iid,
"display_name": String(row.get("display_name", "")),
"glyph": String(row.get("display_name", "?")).left(1),
})
if out.size() >= 3:
break
return out
# 选择兑付:入关卡域库存(来源归因 reward_node)→ 收屏 → 兑付确认回选路(零自动推进同款)
func _on_reward_item_chosen(item_id: String) -> void:
if runner == null or runner.aux_inventory == null:
return
runner.grant_aux_item(item_id, "reward_node")
node_overlay.close_reward()
_refresh_aux_slots()
board_zone.toast.show_toast("获得 %s(入关卡背包)" % String(db.aux_item_row(item_id).get("display_name", item_id)), "info")
_on_node_continue()
# 槽点击(第一步):AP 类(target=none)点击即用(无目标);空间类进/出选中态(toggle 取消)。
func _on_aux_slot_clicked(slot_handle: int) -> void:
if settlement == null or runner == null or runner.aux_inventory == null: