iter10 追加批: 奖励节点三选一共享池(用户拍板)——AdventureRunner 节点API+AuxItems 句柄面+main 共用面分派+node_overlay 道具面退役+autoplay 节点驱动+reward_overlay_test 随面迁移+a裁备选(L3复杀3)+g裁semver(v2兼容断言+存档序列化v1.3)+f裁调研待办(06新条)+文档五件+任务书(冒烟628/0+run_all 15套+五探针+三闸零新增+熵增PASS)
This commit is contained in:
1 parent
93e4764ca0
commit
4f62a489b6
19 files changed
+377
-154
No files matched your search
@@ -121,6 +121,11 @@ static func bonus_keystone_line(payload: Dictionary) -> String:
|
||||
return "钥石 +%d · 累计 %d" % [int(payload.get("keystone_gained", 0)), int(payload.get("keystone_total", 0))]
|
||||
|
||||
|
||||
# 奖励节点三选一头部行(迭代 10 追加批——与通关三选一共享同池;node-reward.md v2)
|
||||
static func node_bonus_header() -> String:
|
||||
return "关卡奖励点\n从提升池抽三选一(与通关三选一同池同权重同上限):\n选择一件带走:"
|
||||
|
||||
|
||||
# 冒险结算屏文案(摘要五件事:打穿没有 / 通关几关 / 止步第几关 / 钥石合计 / 主种子)
|
||||
static func adventure_summary_text(summary: Dictionary) -> String:
|
||||
var verdict := "冒险失败 · 未打穿"
|
||||
|
||||
+21
-47
@@ -45,7 +45,8 @@ var _settlement_stop_pending := false # 卡⑥ 战斗胜利总结结算框挂起
|
||||
var _pending_candidates: Array = [] # 结算框挂起期的选路候选暂存(choices_presented 载荷)
|
||||
var _last_gain_amount := 0 # 最近一笔钱包入账(卡⑥ GP 增量取数面——wallet_changed gain 载荷)
|
||||
var _pending_bonus := {} # 三选一停点载荷暂存(迭代 10——战斗总结结算确认后开三选一面;续玩停点复用)
|
||||
var bonus_overlay: BonusPickOverlay # 提升三选一覆盖层(迭代 10——level-clear.md v2 选择面承载)
|
||||
var bonus_overlay: BonusPickOverlay # 提升三选一覆盖层(迭代 10——level-clear.md v2 选择面承载;追加批起奖励节点共用)
|
||||
var _bonus_context := "level_clear" # 三选一面上下文(level_clear=通关停点→推进下一关 / reward_node=奖励节点→回图选路)
|
||||
var _start_overlay: Control
|
||||
var _end_overlay: BattleEndOverlay
|
||||
var _inter_overlay: Control
|
||||
@@ -174,7 +175,6 @@ 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)
|
||||
@@ -394,10 +394,13 @@ func _on_runner_node(node: Dictionary, info: Dictionary) -> void:
|
||||
_show_board_fx_for_node(t)
|
||||
log_view.append_bb(Narrator.node_banner(runner, node, t))
|
||||
elif t == "reward":
|
||||
_open_modal(node_overlay)
|
||||
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())
|
||||
# 奖励节点三选一(迭代 10 追加批——与通关三选一共享同池同权重同上限;BonusPickOverlay 共用面,
|
||||
# 道具件满位替换子交互同构)。原「魔法道具投放渠道」三选一(迭代 7 过渡形态)随批退役。
|
||||
_bonus_context = "reward_node"
|
||||
var node_cands: Array = adventure.node_bonus_candidates()
|
||||
_open_modal(bonus_overlay)
|
||||
bonus_overlay.open_pick(Narrator.node_bonus_header(), _bonus_card_texts(node_cands))
|
||||
adventure.present_node_bonus(node_cands)
|
||||
log_view.append_bb(Narrator.reward_line())
|
||||
elif t == "rest":
|
||||
var healed: int = int(info.get("healed", 0))
|
||||
@@ -422,6 +425,7 @@ func _on_bonus_presented(payload: Dictionary) -> void:
|
||||
if int(payload.get("aux_cleared", 0)) > 0:
|
||||
board_zone.toast.show_toast("上关剩余 %d 件魔法道具已随关卡结束清空" % int(payload.get("aux_cleared", 0)), "info") # 迭代 7 T4 清空反馈
|
||||
_pending_bonus = payload.duplicate(true)
|
||||
_bonus_context = "level_clear"
|
||||
if bool(payload.get("replace_pending", false)):
|
||||
# 续玩:替换子交互在途中断——重建三选一面后直落替换子态(候选不重抽)
|
||||
_show_bonus_pick()
|
||||
@@ -452,7 +456,10 @@ func _bonus_card_texts(candidates: Array) -> Array:
|
||||
|
||||
|
||||
func _on_bonus_pick_confirmed(item_id: String) -> void:
|
||||
if adventure == null or not adventure.pick_bonus(item_id):
|
||||
if adventure == null:
|
||||
return
|
||||
var ok: bool = adventure.apply_node_bonus(item_id) if _bonus_context == "reward_node" else adventure.pick_bonus(item_id)
|
||||
if not ok:
|
||||
push_error("[组合根] 三选一兑付被拒——见输出日志")
|
||||
|
||||
|
||||
@@ -474,8 +481,8 @@ func _open_bonus_replace(items: Array, incoming: Dictionary) -> void:
|
||||
named.size(), int(db.adventure.transitional_sync_slots))
|
||||
|
||||
|
||||
func _on_bonus_replace_chosen(discard_index: int) -> void:
|
||||
if adventure == null or not adventure.resolve_aux_replace(discard_index):
|
||||
func _on_bonus_replace_chosen(discard: int) -> void:
|
||||
if adventure == null or not adventure.resolve_bonus_replace(discard):
|
||||
push_error("[组合根] 同调位替换受理被拒——见输出日志")
|
||||
|
||||
|
||||
@@ -486,10 +493,14 @@ func _on_bonus_resolved(result: Dictionary) -> void:
|
||||
_refresh_presentations()
|
||||
|
||||
|
||||
# 继续钮:收三选一面 → 推进下一关(零自动推进——兑付后受理)
|
||||
# 继续钮:收三选一面 → 上下文分派(通关停点=推进下一关 / 奖励节点=兑付确认回图选路——零自动推进)
|
||||
func _on_bonus_continue() -> void:
|
||||
_close_modal(bonus_overlay)
|
||||
bonus_overlay.close_pick()
|
||||
if _bonus_context == "reward_node":
|
||||
if runner != null:
|
||||
runner.continue_after_node()
|
||||
return
|
||||
if _adventure_mode and adventure != null:
|
||||
if not adventure.continue_to_next_level():
|
||||
_fatal_screen("关卡推进失败——见输出日志")
|
||||
@@ -1039,43 +1050,6 @@ func _aux_unlocked_set() -> Dictionary:
|
||||
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
|
||||
var rec: Dictionary = runner.grant_aux_item(item_id, "reward_node")
|
||||
if rec.is_empty():
|
||||
# 同调位满位拒绝(迭代 10 过渡承载——奖励节点渠道满位不授予;替换交互归三选一渠道,
|
||||
# 全渠道统一收口随同调实施批)。覆盖层保持开、零入账零关闭(玩家可改选继续/离开)
|
||||
board_zone.toast.show_toast("同调位已满——「%s」未获取" % String(db.aux_item_row(item_id).get("display_name", item_id)), "warn")
|
||||
return
|
||||
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:
|
||||
|
||||
@@ -127,9 +127,9 @@ func open_replace(items: Array, incoming_name: String, holding: int, slots: int)
|
||||
for c in _replace_box.get_children():
|
||||
c.queue_free()
|
||||
for it in items:
|
||||
var idx := int(it.get("index", -1))
|
||||
var tag: int = int(it.get("index", it.get("handle", -1))) # 通关渠道=携带序号 / 节点渠道=库存句柄(核心分发面裁决)
|
||||
var btn := UiKit.button("丢弃「%s」换新" % String(it.get("display_name", it.get("item_id", ""))))
|
||||
btn.pressed.connect(func(): replace_chosen.emit(idx))
|
||||
btn.pressed.connect(func(): replace_chosen.emit(tag))
|
||||
_replace_box.add_child(btn)
|
||||
var decline := UiKit.button("放弃新件(不获取)")
|
||||
decline.pressed.connect(func(): replace_chosen.emit(-1))
|
||||
|
||||
@@ -6,11 +6,9 @@ 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:
|
||||
@@ -30,56 +28,16 @@ func _init() -> void:
|
||||
box.add_child(_label)
|
||||
_btn = UiKit.button("继续")
|
||||
_btn.pressed.connect(_emit_continue_pressed)
|
||||
_choice_box = HBoxContainer.new()
|
||||
_choice_box.add_theme_constant_override("separation", 12)
|
||||
_choice_box.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
box.add_child(_choice_box)
|
||||
box.add_child(_btn)
|
||||
visible = false
|
||||
|
||||
|
||||
# 奖励节点兑付(迭代 7 T5——「节点奖励=功能道具正式渠道」关卡节点地图.md:110 定稿兑现;
|
||||
# 拍板②丙=三选一呈现:池 ≤3 全列(当前有效道具三件——选一即全列),池扩充后加权摇三)。
|
||||
# choices = [{item_id, display_name, glyph}](组合根组装——视图纯呈现,选择经信号外发)。
|
||||
# 选择前「继续」隐藏(强制选择);选择即入库存+屏收。
|
||||
func show_reward_items(choices: Array) -> void:
|
||||
for c in _choice_box.get_children():
|
||||
c.queue_free()
|
||||
if choices.is_empty():
|
||||
# 池空(全未解锁/表空合法态)——直通节点确认(切片期合法空态语义保留)
|
||||
_label.text = "关卡奖励点
|
||||
(此行无道具可取——继续前行)"
|
||||
_btn.text = "继续"
|
||||
_btn.visible = true
|
||||
_choice_box.visible = false
|
||||
visible = true
|
||||
return
|
||||
_label.text = "关卡奖励点
|
||||
取一件魔法道具(加入关卡背包——关卡结束清空)"
|
||||
for ch in choices:
|
||||
var cid := String(ch.get("item_id", ""))
|
||||
var btn := UiKit.button("%s %s" % [String(ch.get("glyph", "?")), String(ch.get("display_name", ""))])
|
||||
btn.custom_minimum_size = Vector2(150, 72)
|
||||
btn.pressed.connect(func(): item_chosen.emit(cid))
|
||||
_choice_box.add_child(btn)
|
||||
_btn.visible = false # 选择前不给出口(强制三选一——构筑点语义)
|
||||
_choice_box.visible = true
|
||||
visible = true
|
||||
|
||||
|
||||
func show_rest(healed: int, hp_after: int, max_hp: int) -> void:
|
||||
_label.text = "血量恢复点\n休息回复 %d HP(%d → %d / %d)" % [
|
||||
healed, hp_after - healed, hp_after, max_hp]
|
||||
_btn.text = "休息完毕,继续"
|
||||
visible = true
|
||||
|
||||
|
||||
# 选择完成收屏(组合根在 item_chosen 处理毕调——入库存+确认后回选路由同拍)
|
||||
func close_reward() -> void:
|
||||
_choice_box.visible = false
|
||||
_btn.visible = true
|
||||
visible = false
|
||||
|
||||
# 探针复用:以真实按钮路径触发继续
|
||||
func press_continue() -> void:
|
||||
_btn.pressed.emit()
|
||||
|
||||
Reference in new issue
Block a user