- T1 表与schema:attunement_growth.tres 谓词表(m1/m2/m3+Σgrant≤slot_cap 校验+slots() 求值口径)+道具表扩列(form/charges/slot_cost/unlock_requires/effective_rounds/charge_n+effect_kinds 九kind+KEY_WHITELIST 零裸flat护栏)+七同调行 - T2 save v4(boss_kill_count 收束入账+attunement_equipped+attune_refresh_level+断点 attunement_charges+迁移链 v3→v4) - T3 Attunement 求值服务(纯函数零 await 零 RNG) - T4 营地装配覆盖层(工坊扩容程序占位+Y 升级行 attune_refresh_upgrade 行族) - T5 被动六件接线+装配注入链+刷新 X/Y 双源(BonusPickOverlay 刷新钮,autoplay 默认不刷) - T6 掘进蓄能全链+摆钟能力面(骑库存面差额注入+断点充能键);炸格 P5 甲影响评估破面大→用户裁拆后批,blast_kit deferred 留位 - T7 退役面五件(BONUS_IDS 除名 aux_item+transitional_sync_slots 过渡键退役=迭代10欠账③清偿+授予路/四容量闸拆除+nest_pendulum 换装 active_charged) - T8 冒烟六新断言(645/0)+MC attune 开关+贪心画像分层 off/on 各 N=60 双策略(on 画像功率显著带平移归调参批;off 新锚=对拍正主留盘主区 sim-data,81000 降批入口基准) - T9 连带修订九件(魔法道具定位重写/元进度v1.6/地图战斗资源v0.16/行动经济v0.7/05不做v1.3/关卡节点地图v1.4/node-reward v2.2/registry 三处/06 收口+炸格拆后批新条) - T10 重闸全绿:冒烟 645/0+熵增 PASS+doc 三闸零新增+MC off 复跑同种子逐位一致 PASS;出口人闸后移迭代 12(用户 2026-10-03 令)
173 lines
7.3 KiB
GDScript
173 lines
7.3 KiB
GDScript
# 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(兑付反馈→继续)。
|
||
class_name BonusPickOverlay
|
||
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 态)
|
||
var _replace_box: VBoxContainer # 替换子面板(replace 态)
|
||
var _confirm_btn: Button # 确认钮(点选后点亮——两段式第二拍)
|
||
var _continue_btn: Button # 继续钮(兑付反馈态唯一出口)
|
||
var _refresh_btn: Button # 刷新钮(迭代 11 第①批 T5b——余量>0 才显示)
|
||
var _refresh_l: Label # 刷新余量读数(X 优先 + Y 冒险池)
|
||
var _refresh_allowance := 0 # 组合根投喂余量(本视图零核心引用)
|
||
var _selected := "" # 点选暂存(确认前可改选)
|
||
var _header := "" # 停点头部文案(选择态保持显示)
|
||
var _card_meta: Array = [] # open_pick 缓存 [{id, text}]——高亮还原读面
|
||
|
||
|
||
func _init() -> void:
|
||
var box: VBoxContainer = UiKit.modal_shell(self, Color(0, 0, 0, 0.6)).box
|
||
_label = UiKit.label("", 20, true, UiKit.WARM)
|
||
_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
box.add_child(_label)
|
||
_choice_box = HBoxContainer.new()
|
||
_choice_box.add_theme_constant_override("separation", 12)
|
||
_choice_box.alignment = BoxContainer.ALIGNMENT_CENTER
|
||
box.add_child(_choice_box)
|
||
_confirm_btn = UiKit.button("确认选择")
|
||
_confirm_btn.pressed.connect(_on_confirm_pressed)
|
||
_confirm_btn.visible = false
|
||
box.add_child(_confirm_btn)
|
||
# 三选一刷新面(迭代 11 第①批 T5b——程序占位用 UiKit 既有件;余量 0 = 隐藏)
|
||
_refresh_btn = UiKit.button("刷新候选")
|
||
_refresh_btn.pressed.connect(_emit_refresh_requested)
|
||
_refresh_btn.visible = false
|
||
box.add_child(_refresh_btn)
|
||
_refresh_l = UiKit.label("", 13, false, UiKit.STONE)
|
||
box.add_child(_refresh_l)
|
||
_replace_box = VBoxContainer.new()
|
||
_replace_box.add_theme_constant_override("separation", 8)
|
||
_replace_box.alignment = BoxContainer.ALIGNMENT_CENTER
|
||
box.add_child(_replace_box)
|
||
_continue_btn = UiKit.button("继续")
|
||
_continue_btn.pressed.connect(_emit_continue_pressed)
|
||
_continue_btn.visible = false
|
||
box.add_child(_continue_btn)
|
||
visible = false
|
||
|
||
|
||
# 选择态:header = 停点头部(回血+钥石增量行,Narrator 装配);cards = [{id, text}]
|
||
#(组合根组装——视图纯呈现)。确认前可改选;确认经 pick_confirmed 外发。
|
||
func open_pick(header: String, cards: Array) -> void:
|
||
_header = header
|
||
_label.text = header
|
||
_selected = ""
|
||
_card_meta = (cards as Array).duplicate(true)
|
||
for c in _choice_box.get_children():
|
||
c.queue_free()
|
||
for ch in cards:
|
||
var cid := String(ch.get("id", ""))
|
||
var btn := UiKit.button(String(ch.get("text", "")))
|
||
btn.custom_minimum_size = Vector2(170, 88)
|
||
btn.pressed.connect(_select_card.bind(cid)) # 具名方法 bind(仓内惯例——内联 lambda 退役)
|
||
_choice_box.add_child(btn)
|
||
_choice_box.visible = true
|
||
_confirm_btn.visible = false
|
||
_replace_box.visible = false
|
||
_continue_btn.visible = false
|
||
_refresh_btn.visible = false # 刷新余量随 set_refresh_info 投喂后现形(open 先归零防残留)
|
||
_refresh_l.text = ""
|
||
visible = true
|
||
|
||
|
||
# 三选一刷新余量投喂(组合根读 adventure.attune_refresh_left()——0 = 刷新面隐藏)
|
||
func set_refresh_info(allowance: int) -> void:
|
||
_refresh_allowance = maxi(0, int(allowance))
|
||
_refresh_btn.visible = _refresh_allowance > 0
|
||
_refresh_l.text = "可刷新 %d 次(装配刷新件 X 优先消耗,Y 为冒险池)" % _refresh_allowance if _refresh_allowance > 0 else ""
|
||
|
||
|
||
func _emit_refresh_requested() -> void:
|
||
refresh_requested.emit()
|
||
|
||
|
||
func _select_card(id: String) -> void:
|
||
_selected = id
|
||
for i in _choice_box.get_child_count():
|
||
var btn := _choice_box.get_child(i) as Button
|
||
var meta: Dictionary = _card_meta[i] if i < _card_meta.size() else {}
|
||
var base_text := String(meta.get("text", ""))
|
||
btn.text = ("✓ " + base_text) if String(meta.get("id", "")) == id else base_text
|
||
_confirm_btn.visible = true
|
||
|
||
|
||
func _on_confirm_pressed() -> void:
|
||
if _selected != "":
|
||
pick_confirmed.emit(_selected)
|
||
|
||
|
||
# 探针/测试复用:以真实按钮路径触发点选、确认与继续(树序遮挡纪律——断言走真实信号路径)
|
||
func press_card(index: int) -> void:
|
||
if index >= 0 and index < _choice_box.get_child_count():
|
||
(_choice_box.get_child(index) as Button).pressed.emit()
|
||
|
||
|
||
func press_confirm() -> void:
|
||
_confirm_btn.pressed.emit()
|
||
|
||
|
||
func press_continue() -> void:
|
||
_continue_btn.pressed.emit()
|
||
|
||
|
||
func selected_id() -> String:
|
||
return _selected
|
||
|
||
|
||
# 兑付反馈态(bonus_resolved 到达——结果行追加在头部之后;唯一出口 = 继续)
|
||
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
|
||
visible = true
|
||
|
||
|
||
# 同调位替换子态(aux_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
|
||
visible = true
|
||
|
||
|
||
# 收屏(组合根在 continue_pressed 处理毕调——推进下一关同拍)
|
||
func close_pick() -> void:
|
||
_choice_box.visible = false
|
||
_replace_box.visible = false
|
||
_continue_btn.visible = false
|
||
_confirm_btn.visible = false
|
||
visible = false
|
||
|
||
|
||
func _emit_continue_pressed() -> void:
|
||
continue_pressed.emit()
|
||
|
||
|
||
func _emit_replace_chosen(tag: int) -> void:
|
||
replace_chosen.emit(tag)
|