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

+2
View File
@@ -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()
+5
View File
@@ -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
+17
View File
@@ -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 对话)----
+3
View File
@@ -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
+13
View File
@@ -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