From 9553d6b182faba5b2f2deefbf21ca175d6dacb15 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Tue, 29 Sep 2026 07:43:51 +0800 Subject: [PATCH] =?UTF-8?q?feat(t5):=20=E5=A5=96=E5=8A=B1=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E4=B8=89=E9=80=89=E4=B8=80+=E5=B7=A5=E5=9D=8A?= =?UTF-8?q?=E8=A7=A3=E9=94=81=E9=97=A8=E6=8E=A7+=E5=B0=81=E5=AD=98?= =?UTF-8?q?=E5=91=88=E7=8E=B0+node-reward=20spec=20=E5=87=BA=E7=94=9F=20?= =?UTF-8?q?=E2=91=A0node=5Foverlay:show=5Freward=5Fitems=20=E4=B8=89?= =?UTF-8?q?=E9=80=89=E4=B8=80(=E6=B1=A0=E2=89=A43=20=E5=85=A8=E5=88=97?= =?UTF-8?q?=E2=80=94=E2=80=94=E5=BD=93=E5=89=8D=E4=B8=89=E4=BB=B6=E5=8D=B3?= =?UTF-8?q?=E5=85=A8=E5=88=97;=E6=B1=A0=E7=A9=BA=E7=9B=B4=E9=80=9A=3D?= =?UTF-8?q?=E5=88=87=E7=89=87=E6=9C=9F=E5=90=88=E6=B3=95=E7=A9=BA=E6=80=81?= =?UTF-8?q?=E4=BF=9D=E7=95=99;=E9=80=89=E6=8B=A9=E5=89=8D=E7=BB=A7?= =?UTF-8?q?=E7=BB=AD=E9=9A=90=E8=97=8F=3D=E5=BC=BA=E5=88=B6=E6=9E=84?= =?UTF-8?q?=E7=AD=91=E7=82=B9=E8=AF=AD=E4=B9=89)+item=5Fchosen=20=E4=BF=A1?= =?UTF-8?q?=E5=8F=B7+close=5Freward=20=E6=94=B6=E5=B1=8F=20=E2=91=A1main:?= =?UTF-8?q?=E5=A5=96=E5=8A=B1=E5=88=86=E6=94=AF=E6=8E=A5=E4=B8=89=E9=80=89?= =?UTF-8?q?=E4=B8=80(=E6=B4=BB=E8=A1=8C=E2=88=A9=E8=A7=A3=E9=94=81?= =?UTF-8?q?=E9=9B=86,=E6=B3=A8=E5=86=8C=E5=BA=8F=E5=89=8D=203,=E5=91=88?= =?UTF-8?q?=E7=8E=B0=E4=BE=A7=E9=9B=B6=20RNG)+=5Fon=5Freward=5Fitem=5Fchos?= =?UTF-8?q?en(=E5=85=A5=E5=BA=93=E5=AD=98=E6=9D=A5=E6=BA=90=E5=BD=92?= =?UTF-8?q?=E5=9B=A0=20reward=5Fnode=E2=86=92toast=E2=86=92=5Fon=5Fnode=5F?= =?UTF-8?q?continue=20=E5=85=91=E4=BB=98=E7=A1=AE=E8=AE=A4=E5=9B=9E?= =?UTF-8?q?=E9=80=89=E8=B7=AF=E9=9B=B6=E8=87=AA=E5=8A=A8=E6=8E=A8=E8=BF=9B?= =?UTF-8?q?)+=5Freward=5Fnode=5Fchoices=20=E2=91=A2=E8=A7=A3=E9=94=81?= =?UTF-8?q?=E9=97=A8=E6=8E=A7=E9=93=BE(=E6=8B=8D=E6=9D=BF=E2=91=A2?= =?UTF-8?q?=E7=94=B2=3D=E8=A7=A3=E9=94=81=E8=BF=9B=E6=8A=95=E6=94=BE?= =?UTF-8?q?=E6=B1=A0):main=20=5Faux=5Funlocked=5Fset(=E5=B7=A5=E5=9D=8A?= =?UTF-8?q?=E8=A7=A3=E9=94=81=E2=88=A9=E6=B4=BB=E8=A1=8C;=E7=BD=97?= =?UTF-8?q?=E7=9B=98=E9=80=80=E5=BD=B9/=E5=A4=B1=E4=B9=89=E4=BB=B6?= =?UTF-8?q?=E4=B8=8D=E5=85=A5=E9=9B=86)=E2=86=92adventure=20params.aux=5Fu?= =?UTF-8?q?nlocked=E2=86=92LevelRunner.set=5Faux=5Funlocked=E2=86=92Battle?= =?UTF-8?q?Orchestrator=20=E5=85=AC=E6=9C=89=20setter=E2=86=92RewardEconom?= =?UTF-8?q?y.set=5Faux=5Funlocked+=5Frollable=5Frows=20aux=20=E8=A1=8C?= =?UTF-8?q?=E8=BF=87=E6=BB=A4=E2=80=94=E2=80=94=E7=A9=BA=E9=9B=86=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E9=9B=B6=E6=8A=95=E6=94=BE=3D=E7=99=BD=E6=9D=BF/?= =?UTF-8?q?=E5=A4=B9=E5=85=B7=E6=80=81=E6=A8=A1=E6=8B=9F=E8=AF=BB=E6=95=B0?= =?UTF-8?q?=E9=9B=B6=E6=89=B0=E5=8A=A8=20=E2=91=A3=E5=B7=A5=E5=9D=8A?= =?UTF-8?q?=E5=91=88=E7=8E=B0:workshop=5Fstatus=20=E6=8A=95=E5=BD=B1+defer?= =?UTF-8?q?red/retired=20=E7=8A=B6=E6=80=81=E9=9D=A2;camp=20=E6=96=87?= =?UTF-8?q?=E6=A1=88=3D=E5=B7=B2=E8=A7=A3=E9=94=81=C2=B7=E8=83=BD=E5=8A=9B?= =?UTF-8?q?=E5=B0=81=E5=AD=98=E4=B8=AD(=E5=A4=B1=E4=B9=89=E4=BB=B6)/?= =?UTF-8?q?=E5=B7=B2=E9=80=80=E5=BD=B9(=E7=BD=97=E7=9B=98)=20=E2=91=A4node?= =?UTF-8?q?-reward.md=20spec=20v1.0=20=E5=87=BA=E7=94=9F(06=20=E6=9D=A1?= =?UTF-8?q?=E7=9B=AE=E5=85=91=E7=8E=B0)=20=E5=9D=91=E4=B8=80=E8=BD=AE:Batt?= =?UTF-8?q?leOrchestrator=20=E6=97=A0=E5=85=AC=E6=9C=89=20reward=5Feconomy?= =?UTF-8?q?(E1/E3=20=E5=B0=81=E8=A3=85)=E2=80=94=E2=80=94=E5=85=AC?= =?UTF-8?q?=E6=9C=89=20setter=20=E8=BD=AC=E5=8F=91;=E7=AB=96=E7=BA=BF?= =?UTF-8?q?=E9=94=9A=E5=9B=9B=E5=A4=84=E6=8B=BC=E6=8E=A5=E7=97=85=E8=87=AA?= =?UTF-8?q?=E6=84=88=20=E9=97=B8:=E5=86=92=E7=83=9F=20577/0+=E5=9B=9B?= =?UTF-8?q?=E6=8E=A2=E9=92=88=20PASS(click=20=E9=97=AE=E9=A2=98=3D0)+?= =?UTF-8?q?=E5=8D=95=E6=B5=8B=200=20FAIL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- design/gdd/ux/node-reward.md | 29 +++++++++++ game/core/adventure_runner.gd | 2 + game/core/battle_orchestrator.gd | 5 ++ game/core/level_runner.gd | 17 +++++++ game/core/meta_progression.gd | 3 ++ game/core/reward_economy.gd | 13 +++++ game/presentation/main.gd | 53 +++++++++++++++++++- game/presentation/views/camp_overlay_view.gd | 16 +++++- game/presentation/views/node_overlay.gd | 12 +++++ 9 files changed, 146 insertions(+), 4 deletions(-) create mode 100644 design/gdd/ux/node-reward.md diff --git a/design/gdd/ux/node-reward.md b/design/gdd/ux/node-reward.md new file mode 100644 index 00000000..4f133fc5 --- /dev/null +++ b/design/gdd/ux/node-reward.md @@ -0,0 +1,29 @@ +# 节点奖励屏 UX spec(node-reward) + +> 状态:v1.0(2026-09-29 迭代 7 T5 随批出生——06「节点奖励屏 UX spec」条目兑现;上游拍板=奖励节点三选一〔用户 2026-09-29 拍板②丙〕) +> 事实源:`game/presentation/views/node_overlay.gd`(show_reward_items/close_reward)+ `main.gd`(_reward_node_choices/_on_reward_item_chosen) +> 相邻:[level-clear](level-clear.md)(关卡结算屏)/ [level-map](level-map.md)(关卡画面)/ [魔法道具](../systems/排雷循环/魔法道具.md)(内容权威) + +## 定位 + +奖励节点(reward node)到达即开的**模态构筑点**:从可用道具池取一件加入关卡背包。「节点奖励 = 功能道具的正式渠道」(关卡节点地图 v1.0 ⑤ 定稿)的呈现承载。 + +## 布局与交互 + +- 全屏变暗层 + 居中标题「关卡奖励点 / 取一件魔法道具(加入关卡背包——关卡结束清空)」。 +- 道具按钮横排三枚(150×72:字形大字=道具名首字 + 全名两行);**选择前不给出口**(继续按钮隐藏——强制三选一的构筑点语义)。 +- 池 ≤3 全列(现行有效道具三件:工兵令/探巢摆钟/爆破工兵包——选一即全列);池扩充后按注册序取前 3(呈现侧零 RNG 消费纪律)。 +- 池空(全未解锁/表空合法态)= 直通节点确认「此行无道具可取——继续前行」(切片期空态语义保留)。 +- 选择 → 道具入关卡域库存(来源归因 `reward_node`)→ toast「获得 X(入关卡背包)」→ C 区槽位刷新 → 兑付确认回选路(零自动推进同款)。 + +## 口径 + +- 可用池 = aux 活行 ∩ 工坊解锁集(拍板③甲「解锁→进投放池」同源门控——未解锁不入奖励池与掉落池)。 +- 退役件(探宝罗盘)/失义件(显影尘/地穴全图卷)不入池;营地呈现「已退役 / 已解锁·能力封存中」。 +- 库存语义=关卡域(跨战斗延续、关卡末清空+快照反馈);使用=挖掘期两步交互(C 区槽选中→棋盘目标格)。 + +## 版本历史 + +| 版本 | 日期 | 改动 | +|---|---|---| +| v1.0 | 2026-09-29 | 迭代 7 T5 随批出生(三选一构筑点形态——用户拍板②丙) | diff --git a/game/core/adventure_runner.gd b/game/core/adventure_runner.gd index 1a629109..967acbfc 100644 --- a/game/core/adventure_runner.gd +++ b/game/core/adventure_runner.gd @@ -65,6 +65,7 @@ var _override := [0, 0] # TunePanel 百分比通道 [atk_pct, var _base_override := {"atk": 0, "max_hp": 0, "armor_class": 0} var _first_script := false # 首次脚本模式(关卡节点图生成 v0.4——true = 全关零抽签取池内模板 0;测量夹具恒 false) # 永久强化 flat 注入(元进度实现批——冒险开始参数面 params.hero_override 存入,共享给 LevelRunner;全零 = 白板) var _last_aux_cleared := 0 # additive(迭代 7 T4——最近一次关卡末清空计数,summary 取数面) +var _aux_unlocked := {} # aux 解锁门控集(迭代 7 T5——params.aux_unlocked 接收面) func _init(db_ref, rng_ref) -> void: @@ -94,6 +95,7 @@ func start_adventure(seed_val: int, params: Dictionary = {}) -> bool: _pending_next_level = false _base_override = _normalize_override(params.get("hero_override", {})) _first_script = bool(params.get("first_script", false)) # 缺省 = 非首次(抽签路径;真实侧由呈现层读存档布尔位传入) + _aux_unlocked = params.get("aux_unlocked", {}) if params.get("aux_unlocked", {}) is Dictionary else {} # additive(迭代 7 T5) ledger["hero_hp"] = int(db.hero.max_hp) + int(_base_override["max_hp"]) # 生命账本置满(表值+永久强化 flat——加算区刚归零) state = "running" return _start_current_level() diff --git a/game/core/battle_orchestrator.gd b/game/core/battle_orchestrator.gd index 5cd9bae6..0894f940 100644 --- a/game/core/battle_orchestrator.gd +++ b/game/core/battle_orchestrator.gd @@ -401,6 +401,11 @@ func use_aux(handle: int, target: Vector2i) -> Dictionary: return {"ok": true, "item_id": v.item_id, "resolved": v.resolved, "record": rec} +# aux 解锁门控转发(迭代 7 T5——LevelRunner 注入;E1/E3 封装:外部经公有面,禁直读 _reward_economy) +func set_aux_unlocked(ids: Dictionary) -> void: + _reward_economy.set_aux_unlocked(ids) + + func _minesweeper_ref() -> Variant: return minesweeper diff --git a/game/core/level_runner.gd b/game/core/level_runner.gd index 1f3deaac..0f2cb401 100644 --- a/game/core/level_runner.gd +++ b/game/core/level_runner.gd @@ -63,6 +63,7 @@ var _shop_purchased := {} # product_id → 已购档数(现价 # 协作对象私有(E1/E3 封装收口 2026-09-13):settlement 读取经 current_battle() 成文出口 var _settlement = null # 当前战斗编排器(关卡结束置 null) var aux_inventory = null # 魔法道具 关卡域库存(迭代 7 T3——AuxItems;跨战斗延续, +var _aux_unlocked := {} # aux 解锁门控集(迭代 7 T5——拍板③甲;空集=零投放) # 关卡末清空+快照计数入 level_finished 载荷) var hero_atk_pct := 0 # 测试面板覆盖值(切片专用) @@ -369,6 +370,8 @@ func _start_battle(node: Dictionary) -> bool: if aux_inventory == null: aux_inventory = AuxItems.new(db) # 惰性建——关卡域单实例(跨战斗延续) _settlement.aux_inventory = aux_inventory # 迭代 7 T3——库存注入(授予路由+use_aux 编排消费面) + if _settlement != null: + _settlement.set_aux_unlocked(_aux_unlocked) # 迭代 7 T5——解锁门控下传(装板摇号前) # core↔core 接线只许方法 Callable(ADR-0001 静态规则 S6——lambda 捕获 self 会延长 # RefCounted 生命周期乃至成环:settlement 的信号持捕获 runner 的 lambda,runner 又持 settlement) _settlement.step_row.connect(_forward_step_row) @@ -472,6 +475,20 @@ func pick_next_node(node_id: int) -> bool: func current_battle(): return _settlement +# aux 解锁门控集注入(迭代 7 T5——AdventureRunner 自 params 传递) +func set_aux_unlocked(ids: Dictionary) -> void: + _aux_unlocked = ids + if _settlement != null: + _settlement.set_aux_unlocked(ids) + +# 奖励节点兑付透传(迭代 7 T5——呈现层三选一 → 关卡域库存;来源归因 reward_node) +func grant_aux_item(item_id: String, source: String) -> Dictionary: + if aux_inventory == null: + aux_inventory = AuxItems.new(db) + return aux_inventory.add_item(item_id, source, Vector2i(-1, -1), 0) + + return _settlement + # ---- 玩家输入转发(呈现层只与 runner 对话)---- diff --git a/game/core/meta_progression.gd b/game/core/meta_progression.gd index 3f8ae992..a70ee5c8 100644 --- a/game/core/meta_progression.gd +++ b/game/core/meta_progression.gd @@ -86,12 +86,15 @@ func workshop_status() -> Array: var out: Array = [] for row in db.meta_workshop_items(): var iid := String(row.get("item_id", "")) + var arow: Dictionary = db.aux_item_row(iid) # 迭代 7 T5——失义/退役状态面(呈现「能力封存/已退役」) out.append({ "item_id": iid, "display_name": String(row.get("display_name", "")), "threshold": int(row.get("clear_threshold", 0)), "unlocked": save.item_unlocked(iid), "progress": _workshop_progress(String(row.get("channel", "total_clears"))), + "deferred": bool(arow.get("deferred", false)), + "retired": bool(arow.get("retired", false)), }) return out diff --git a/game/core/reward_economy.gd b/game/core/reward_economy.gd index 990b2b92..61d23e1c 100644 --- a/game/core/reward_economy.gd +++ b/game/core/reward_economy.gd @@ -204,10 +204,23 @@ func weighted_pick(rows: Array, r: int) -> String: return "" +# aux 解锁门控集(迭代 7 T5——拍板③甲「解锁→进投放池」:未解锁 aux 行不入摇号池。 +# 空集默认 = 全部 aux 不投放(白板/夹具态零投放——模拟读数零扰动;真实侧 main 组装工坊解锁集传入) +var _aux_unlocked := {} + + +func set_aux_unlocked(ids: Dictionary) -> void: + _aux_unlocked = ids + + func _rollable_rows() -> Array: # 摇号池 = cell 池正向行 weight>0(负向行纯字典不消费权重;封锁格不在本函数职责域——清单由调用方裁剪) var out: Array = [] for row in db.reward.rows: + if String(row.get("cell_type", "")) == RewardTable.AUX_ITEM_TYPE: + var iid := String(row.get("effect_id", "")) + if iid == "" or not _aux_unlocked.has(iid): + continue if String(row.get("pool_tag", "")) == "cell" and String(row.get("polarity", "")) == "positive" and int(row.weight) > 0: out.append(row) return out diff --git a/game/presentation/main.gd b/game/presentation/main.gd index f1db2415..ea8b8d4e 100644 --- a/game/presentation/main.gd +++ b/game/presentation/main.gd @@ -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: diff --git a/game/presentation/views/camp_overlay_view.gd b/game/presentation/views/camp_overlay_view.gd index 0341fc43..15a3c66b 100644 --- a/game/presentation/views/camp_overlay_view.gd +++ b/game/presentation/views/camp_overlay_view.gd @@ -138,8 +138,20 @@ func refresh_camp(data: Dictionary) -> void: var prog := int(row.get("progress", 0)) var unlocked := bool(row.get("unlocked", false)) card.progress_l.text = "累计通关 %d / %d 次" % [prog, th] - card.status_l.text = "已解锁" if unlocked else "未解锁" - card.status_l.add_theme_color_override("font_color", UiKit.GOLD if unlocked else UiKit.WARM_FAINT) + # 迭代 7 T5——失义件(显影尘/地穴全图卷 Alpha 重定)与退役件(探宝罗盘 2026-09-29 拍板①乙)的呈现口径 + var st_text := "未解锁" + var st_col: Color = UiKit.WARM_FAINT + if bool(row.get("retired", false)): + st_text = "已退役" + st_col = UiKit.WARM_FAINT + elif bool(row.get("deferred", false)) and unlocked: + st_text = "已解锁 · 能力封存中" + st_col = UiKit.GOLD + elif unlocked: + st_text = "已解锁" + st_col = UiKit.GOLD + card.status_l.text = st_text + card.status_l.add_theme_color_override("font_color", st_col) func _rebuild_forge(data: Dictionary) -> void: diff --git a/game/presentation/views/node_overlay.gd b/game/presentation/views/node_overlay.gd index dcdd64ca..ab9dcd34 100644 --- a/game/presentation/views/node_overlay.gd +++ b/game/presentation/views/node_overlay.gd @@ -6,9 +6,11 @@ class_name NodeOverlay extends Control signal continue_pressed +signal item_chosen(item_id: String) # 迭代 7 T5——奖励节点三选一兑付(拍板②丙:奖励屏=小构筑点) var _label: Label var _btn: Button +var _choice_box: HBoxContainer # 道具三选一按钮区(show_reward_items 时呈现) func _init() -> void: @@ -27,6 +29,10 @@ func _init() -> void: _label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER box.add_child(_label) _btn = UiKit.button("继续") + _choice_box = HBoxContainer.new() + _choice_box.add_theme_constant_override("separation", 12) + _choice_box.alignment = BoxContainer.ALIGNMENT_CENTER + box.add_child(_choice_box) _btn.pressed.connect(func(): continue_pressed.emit()) box.add_child(_btn) visible = false @@ -46,6 +52,12 @@ func show_rest(healed: int, hp_after: int, max_hp: int) -> void: visible = true +# 选择完成收屏(组合根在 item_chosen 处理毕调——入库存+确认后回选路由同拍) +func close_reward() -> void: + _choice_box.visible = false + _btn.visible = true + visible = false + # 探针复用:以真实按钮路径触发继续 func press_continue() -> void: _btn.pressed.emit()