T5(迭代22秘宝批): 存档面+死通道拆除——treasure_inventory保留注记(主动件骑库存承载在役:线香/天眼handle+断点aux_items快照+set_attune_charges差额注入全链保留); 替换子交互整体拆除(CURIO_KINDS除名后不可达死通道=装配不可变契约1实现面保证): adventure_runner六函数删(_pick_treasure_item/resolve_treasure_replace/_resolve_node_replace/_replace_in_transit/_draw_pending_treasure/resolve_curio_replace)+_bonus_treasure_carry跨关携带/_last_draw_treasure掷定链/_roll_from/_bonus_treasure_pool/bonus_aux_carry断点键; level_runner.grant_treasure_item/treasure_items.holding_records删; 呈现面=controller替换子态三函数+aux_detail卡片直显链/curio_pick_overlay replace状态机(信号+面板+open_replace)/main两接线/level_autoplay替换策略; smoke与ui_click_probe替换分支摘除+reward_overlay_test替换段摘除; 修正则贪吃误删=apply_node_curio普通兑付主体补回。冒烟895/0+run_all 22套+探针全绿〔秘宝批 V15/V16拆除路线/V13〕

This commit is contained in:
DKChan committed 2026-10-12 19:32:29 +08:00
1 parent b7a2eadd2c
commit 73db171cb4
13 files changed
+42 -346

No files matched your search

+10 -54
View File
@@ -5,7 +5,7 @@
# 装配纪律(ADR-0001,仿 treasure_interact_controller 先例):信号连接不在此发生——curio_overlay
# 四信号与 adventure 两信号由组合根接至本类 on_* 入口;模态栈/演播后排窗/呈现刷新/终局面/
# 推进失败 fatal 经组合根回调注入;adventure/runner 随局更替——现值回调注入;兑付写调用唯一
# 经 adventure.pick_curio/apply_node_curio/resolve_curio_replace(core 侧单口,本类零直写)。
# 经 adventure.pick_curio/apply_node_curio(core 侧单口,本类零直写;替换子交互随迭代 22 T5 拆除——装配不可变契约 1,冒险中零更换途径)。
extends RefCounted
const Narrator := preload("res://presentation/battle_narrator.gd")
@@ -29,8 +29,6 @@ var _gp_summary: Callable # () → 战斗总结 GP 行文案(组
var _pending_curio := {} # 三选一停点载荷暂存(迭代 10——战斗总结结算确认后开三选一面;续玩停点复用)
var _curio_context := "level_clear" # 三选一面上下文(level_clear=通关停点→推进下一关 / reward_node=奖励节点→回图选路)
var _last_replace_items: Array = [] # 替换子态续玩重建缓存(treasure_replace_presented 载荷)
var _last_replace_incoming := {} # 同上——incoming 记录
func _init(db_ref, meta_get: Callable, curio_overlay_ref: CurioPickOverlay, end_overlay_ref: BattleEndOverlay,
@@ -112,8 +110,7 @@ func on_presented(payload: Dictionary) -> void:
toast.show_toast("上关剩余 %d 件秘宝已随关卡结束清空" % int(payload.get("aux_cleared", 0)), "info") # 迭代 7 T4 清空反馈
_pending_curio = payload.duplicate(true)
_curio_context = "level_clear"
if bool(payload.get("replace_pending", false)) or bool(payload.get("refreshed", false)):
# 续玩:替换子交互在途中断——重建三选一面后直落替换子态(候选不重抽)
if bool(payload.get("refreshed", false)):
# 刷新重发(2026-10-06):refreshed 标直落选择面——不再重开胜利总结面
show_pick()
return
@@ -124,9 +121,8 @@ func on_presented(payload: Dictionary) -> void:
_refresh_presentations.call()
# 三选一面开出(战斗总结结算确认 / 续玩停点恢复;替换在途由 treasure_replace_presented 续发直落子态)
# 奖励节点三选一(迭代 10 追加批——与通关三选一共享同池同权重同上限;CurioPickOverlay 共用面,
# 道具件满位替换子交互同构)。原「秘宝投放渠道」三选一(迭代 7 过渡形态)随批退役。
# 奖励节点三选一(迭代 10 追加批——与通关三选一共享同池同权重同上限;CurioPickOverlay 共用面)。
# 原「秘宝投放渠道」三选一(迭代 7 过渡形态)随批退役;替换子交互随迭代 22 T5 拆除。
func present_node_curio() -> void:
var adventure = _adventure_get.call()
_curio_context = "reward_node"
@@ -141,16 +137,16 @@ func present_node_curio() -> void:
runner.continue_after_node()
return
_open_modal.call(curio_overlay)
curio_overlay.open_pick(Narrator.node_curio_header(), _curio_card_texts(node_cands, node_draw.get("aux_detail", []) as Array))
adventure.present_node_curio(node_cands, node_draw.get("aux_detail", []) as Array)
curio_overlay.open_pick(Narrator.node_curio_header(), _curio_card_texts(node_cands))
adventure.present_node_curio(node_cands)
_feed_refresh_display(adventure) # 迭代 11 第①批 T5b + 2026-10-06 对齐商店——刷新面投喂单点
# 三选一面开出(战斗总结结算确认 / 续玩停点恢复;替换在途由 treasure_replace_presented 续发直落子态)
# 三选一面开出(战斗总结结算确认 / 续玩停点恢复)
func show_pick() -> void:
var payload: Dictionary = _pending_curio
_open_modal.call(curio_overlay)
curio_overlay.open_pick(Narrator.curio_pick_header(payload), _curio_card_texts(payload.get("candidates", []) as Array, payload.get("aux_detail", []) as Array))
curio_overlay.open_pick(Narrator.curio_pick_header(payload), _curio_card_texts(payload.get("candidates", []) as Array))
var adventure = _adventure_get.call()
if adventure != null:
_feed_refresh_display(adventure) # 迭代 11 第①批 T5b + 2026-10-06 对齐商店——刷新面投喂单点
@@ -165,11 +161,10 @@ func _feed_refresh_display(adventure) -> void:
curio_overlay.set_refresh_display(adventure.attune_refresh_left(), cost, gp >= cost)
func _curio_card_texts(candidates: Array, treasure_detail: Array = []) -> Array:
func _curio_card_texts(candidates: Array) -> Array:
# TP0 令牌池——候选 = {"kind","tier"} 令牌字典;卡片 id = "kind|tier|feat_id" 复合键(同 kind
# 大小两档/同档多 relic 可同屏,kind 单键不构成唯一身份),确认回程拆键(on_pick_confirmed)
var out: Array = []
var treasure_i := 0
for tok in candidates:
var kind := String((tok as Dictionary).get("kind", ""))
var tier := String((tok as Dictionary).get("tier", ""))
@@ -181,10 +176,6 @@ func _curio_card_texts(candidates: Array, treasure_detail: Array = []) -> Array:
选定即开一次奇物三选一(T5 门票——拿到即开不进持有集)"
elif kind == "relic":
text = Narrator.feat_card_text(db.feat_row(rid))
elif kind == "treasure_item":
var iid := String(treasure_detail[treasure_i]) if treasure_i < treasure_detail.size() else ""
treasure_i += 1
text = Narrator.bonus_card_text(kind, tier, int(db.adventure.curio_value(kind, tier)), _treasure_card_line(iid))
else:
text = Narrator.bonus_card_text(kind, tier, int(db.adventure.curio_value(kind, tier)))
out.append({"id": cid, "text": text})
@@ -194,17 +185,6 @@ func _curio_card_texts(candidates: Array, treasure_detail: Array = []) -> Array:
# 道具件卡片行(掷定前置——具体道具名 + 效果摘要;空 iid = 无预掷回退态显示通用文案)。
# 内容数据驱动扩展批 T4 文案表值化:效果摘要=数据行 desc 字段直读(零 kind 硬编码映射——
# 新件写行即有名有姓)
func _treasure_card_line(iid: String) -> String:
if iid == "":
return ""
var row: Dictionary = db.treasure_item_row(iid)
if row.is_empty():
return ""
var desc := String(row.get("desc", ""))
if desc != "":
return "%s · %s" % [String(row.get("display_name", iid)), desc]
return String(row.get("display_name", iid))
# 三选一刷新(迭代 11 第①批 T5b X/Y 双源 + 2026-10-06 用户令对齐商店 MC-1——免费位用尽转 GP 付费档:
# 同 shop_refresh_cost_base/curve 旋钮、同冒险钱包 runner.gp;通关停点经重发 curio_choice_presented 直落
@@ -227,7 +207,7 @@ func on_refresh() -> void:
return
if _curio_context == "reward_node":
var st: Dictionary = adventure.node_curio_pending_state()
curio_overlay.open_pick(Narrator.node_curio_header(), _curio_card_texts(st.get("candidates", []) as Array, st.get("aux_detail", []) as Array))
curio_overlay.open_pick(Narrator.node_curio_header(), _curio_card_texts(st.get("candidates", []) as Array))
_feed_refresh_display(adventure)
@@ -251,30 +231,6 @@ func on_pick_confirmed(item_id: String) -> void:
curio_overlay.open_pick(Narrator.node_curio_header(), _curio_card_texts(st2.get("candidates", []) as Array))
func on_treasure_replace_presented(items: Array, incoming: Dictionary) -> void:
_last_replace_items = items.duplicate(true)
_last_replace_incoming = incoming.duplicate(true)
if not curio_overlay.visible:
_open_modal.call(curio_overlay) # 续玩替换在途直落——三选一面未开时先入栈(模态栈纪律)
_open_replace(items, incoming)
func _open_replace(items: Array, incoming: Dictionary) -> void:
var named: Array = []
for it in items:
var rec: Dictionary = it.duplicate()
rec["display_name"] = String(item_name_lookup().get(String(rec.get("item_id", "")), String(rec.get("item_id", ""))))
named.append(rec)
curio_overlay.open_replace(named, String(item_name_lookup().get(String(incoming.get("item_id", "")), String(incoming.get("item_id", "")))),
named.size(), Attunement.slots(db, _save_get.call())) # 迭代 11 第①批 T7——呈现读数源切谓词表求值(过渡键退役)
func on_replace_chosen(discard: int) -> void:
var adventure = _adventure_get.call()
if adventure == null or not adventure.resolve_curio_replace(discard):
push_error("[bonus_interact] 同调位替换受理被拒——见输出日志")
# 兑付反馈(curio_resolved 到达——结果行上屏,唯一出口 = 继续)
func on_resolved(result: Dictionary) -> void:
_pending_curio = {}
-2
View File
@@ -294,7 +294,6 @@ func _build_ui() -> void:
_open_modal, _open_modal_after_playback, _close_modal, _refresh_presentations,
_fatal_screen, func() -> String: return _gp_summary_text({"gp_dropped": _last_gain_amount}))
curio_overlay.pick_confirmed.connect(_bonus.on_pick_confirmed)
curio_overlay.replace_chosen.connect(_bonus.on_replace_chosen)
curio_overlay.continue_pressed.connect(_bonus.on_continue)
curio_overlay.refresh_requested.connect(_bonus.on_refresh)
move_child(curio_overlay, _end_overlay.get_index())
@@ -488,7 +487,6 @@ func _wire_adventure() -> void:
# 迭代 10 奖励重构批——big_reward_paid 退役,三选一信号族接线
adventure.curio_choice_presented.connect(_bonus.on_presented)
adventure.curio_resolved.connect(_bonus.on_resolved)
adventure.treasure_replace_presented.connect(_bonus.on_treasure_replace_presented)
adventure.adventure_finished.connect(_on_adventure_finished)
adventure.level_started.connect(_on_adventure_level_started)
adventure.set_save_manager(save_manager) # 断点承载注入(迭代 8 卡①——停点快照写盘;调试/模拟不经过此路径)
+7 -43
View File
@@ -1,20 +1,19 @@
# VERTICAL SLICE - NOT FOR PRODUCTION
# Date: 2026-10-01(迭代 10 奖励重构批——level-clear.md v2 三选一选择面承载)
# 提升三选一覆盖层(模态):非末关通关的选择停点呈现——部分回血反馈 + 三卡点选 + 确认兑付 +
# 同调位替换子交互。高内聚边界:文案拼装归 Narrator(组合根喂入),选择意图经信号外发;
# 视图零核心引用。交互范式 = node_overlay choice_box 按钮族 + 选路两段式(点选→确认)复用,零新原语。
# 状态机:pick(三卡点选→确认)→ [replace(满位 N+1 选 N)] → result(兑付反馈→继续)。
# 提升三选一覆盖层(模态):非末关通关的选择停点呈现——部分回血反馈 + 三卡点选 + 确认兑付。
# 高内聚边界:文案拼装归 Narrator(组合根喂入),选择意图经信号外发;视图零核心引用。
# 交互范式 = node_overlay choice_box 按钮族 + 选路两段式(点选→确认)复用,零新原语。
# 状态机:pick(三卡点选→确认)→ result(兑付反馈→继续)。〔replace 满位替换子态随迭代 22 T5
# 拆除——装配不可变契约 1(冒险中零更换途径),core 写口与组合根接线同批摘除。〕
class_name CurioPickOverlay
extends Control
signal pick_confirmed(item_id: String)
signal replace_chosen(discard_index: int) # -1 = 放弃新件不获取
signal continue_pressed
signal refresh_requested # 三选一刷新意图(迭代 11 第①批 T5b——P1 甲 X/Y 双源;重抽归组合根)
var _label: Label
var _choice_box: HBoxContainer # 三卡区(pick 态——M7 bcard 竖卡)
var _replace_box: VBoxContainer # 替换子面板(replace 态)
var _confirm_btn: Button # 确认钮(点选后点亮——两段式第二拍)
var _continue_btn: Button # 继续钮(兑付反馈态唯一出口)
var _refresh_btn: Button # 刷新钮(迭代 11 第①批 T5b——余量>0 才显示;M7 升顶条图标钮)
@@ -33,7 +32,7 @@ func _init() -> void:
# T7(UI 设计对齐批 2026-10-06):全屏页形态(demo ⑥ #v-bonus——与商店 T6 同壳 page_shell,
# 根底暗档)——56px 通栏顶条(标题+刷新图标钮/角标)+ 居中入账信息行(GP 汇总行承载)+
# bcard 三竖卡(D1 232×352)+ 底部确认主钮与免费选择注记。差异白名单=免费选择/✓章/无价签。
# 状态机不变:pick→[replace]→result。M7 同构裁决遗产保留。
# 状态机不变:pick→result。M7 同构裁决遗产保留。
var page: Dictionary = UiKit.page_shell(self, false)
var top: HBoxContainer = page.top_row
top.add_child(UiKit.label("战利品三选一",UiKit.FS_TITLE, true, UiKit.WARM))
@@ -79,11 +78,6 @@ func _init() -> void:
foot.add_child(_continue_btn)
_foot_hint = UiKit.label("免费选择 · 刷新不消耗 AP",UiKit.FS_HINT, false, UiKit.WARM_FAINT)
foot.add_child(_foot_hint)
_replace_box = VBoxContainer.new()
_replace_box.add_theme_constant_override("separation", 8)
_replace_box.alignment = BoxContainer.ALIGNMENT_CENTER
_replace_box.size_flags_vertical = Control.SIZE_EXPAND_FILL
box.add_child(_replace_box)
visible = false
@@ -115,9 +109,8 @@ func open_pick(header: String, cards: Array) -> void:
_bcards.append(card)
_choice_box.visible = true
_confirm_btn.visible = false
_replace_box.visible = false
_continue_btn.visible = false
_foot_hint.visible = true # T7 底注=pick 态专属(replace/result 态让位)
_foot_hint.visible = true # T7 底注=pick 态专属(result 态让位)
_refresh_btn.visible = false # 刷新面随 set_refresh_display 投喂后现形(open 先归零防残留;店内门票面不投喂=无刷新)
_refresh_l.text = ""
visible = true
@@ -183,40 +176,15 @@ func show_result(text: String) -> void:
_label.text = _header + "\n" + text
_choice_box.visible = false
_confirm_btn.visible = false
_replace_box.visible = false
_continue_btn.visible = true
_refresh_btn.visible = false # 刷新=选择态专属功能(结果态停点已兑付——残钮零意图可点)
_foot_hint.visible = false
visible = true
# 同调位替换子态(treasure_replace_presented 到达):items = [{index, item_id, display_name}](组合根
# 喂显示名)。两路出口:丢旧收新(逐件钮)/ 放弃新件——终态恒 ≤ 同调位由核心受理面保证。
func open_replace(items: Array, incoming_name: String, holding: int, slots: int) -> void:
_label.text = "同调位已满(%d/%d)——收下「%s」需丢弃一件已有道具,或放弃新件" % [holding, slots, incoming_name]
for c in _replace_box.get_children():
c.queue_free()
for it in items:
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(_emit_replace_chosen.bind(tag))
_replace_box.add_child(btn)
var decline := UiKit.button("放弃新件(不获取)")
decline.pressed.connect(_emit_replace_chosen.bind(-1))
_replace_box.add_child(decline)
_choice_box.visible = false
_confirm_btn.visible = false
_replace_box.visible = true
_continue_btn.visible = false
_refresh_btn.visible = false # 刷新=选择态专属功能(替换子态不换候选)
_foot_hint.visible = false
visible = true
# 收屏(组合根在 continue_pressed 处理毕调——推进下一关同拍)
func close_pick() -> void:
_choice_box.visible = false
_replace_box.visible = false
_continue_btn.visible = false
_confirm_btn.visible = false
_refresh_btn.visible = false
@@ -228,10 +196,6 @@ func _emit_continue_pressed() -> void:
continue_pressed.emit()
func _emit_replace_chosen(tag: int) -> void:
replace_chosen.emit(tag)
# M7:id → 手绘图标键(子串映射容错 id 词表演进;缺位=BCard 空图标槽不炸)
func _icon_for_id(idl: String) -> String:
if idl.contains("heal"):