迭代11第①批:魔法道具同调实施批 T0-T10 全落(重闸 645/0 + MC off 逐位 PASS)

- 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 令)
This commit is contained in:
DKChan committed 2026-10-03 15:41:30 +08:00
1 parent 02cb92a972
commit a6a2bf885e
37 files changed
+1075 -173

No files matched your search

+3 -2
View File
@@ -34,12 +34,13 @@ func _init(db_ref, meta_ref, info_panel_ref: HeroInfoPanelView, toast_ref: Toast
_runner_get = runner_get
# aux 解锁门控集组装(迭代 7 T5——拍板③甲:工坊解锁 ∩ 活行词表;罗盘退役/失义件不入集)
# aux 解锁门控集组装(迭代 7 T5——拍板③甲:工坊解锁 ∩ 过渡活行词表;罗盘退役/失义件/同调行不入集
# ——迭代 11 第①批 T1 切 aux_transitional_rows 口径,同调行永不入过渡渠道)
func unlocked_set() -> Dictionary:
var out := {}
if meta == null:
return out
for row in db.aux_active_rows():
for row in db.aux_transitional_rows():
var iid := String(row.get("item_id", ""))
if meta.is_item_unlocked(iid):
out[iid] = true
+37 -1
View File
@@ -254,6 +254,7 @@ func _build_ui() -> void:
shop_overlay.buy_requested.connect(_on_shop_buy)
shop_overlay.leave_pressed.connect(_on_shop_leave)
camp_overlay.buy_requested.connect(_on_camp_buy)
camp_overlay.attune_toggled.connect(_on_camp_attune_toggle)
camp_overlay.closed.connect(_on_camp_closed)
_end_overlay.review_pressed.connect(_on_end_review)
_end_overlay.continue_pressed.connect(_on_battle_end_continue)
@@ -269,6 +270,7 @@ func _build_ui() -> void:
bonus_overlay.pick_confirmed.connect(_on_bonus_pick_confirmed)
bonus_overlay.replace_chosen.connect(_on_bonus_replace_chosen)
bonus_overlay.continue_pressed.connect(_on_bonus_continue)
bonus_overlay.refresh_requested.connect(_on_bonus_refresh)
move_child(bonus_overlay, _end_overlay.get_index())
var start: Dictionary = UiAssembler.build_start_overlay(self, db)
@@ -486,6 +488,7 @@ func _on_runner_node(node: Dictionary, info: Dictionary) -> void:
_open_modal(bonus_overlay)
bonus_overlay.open_pick(Narrator.node_bonus_header(), _bonus_card_texts(node_cands, node_draw.get("aux_detail", []) as Array))
adventure.present_node_bonus(node_cands, node_draw.get("aux_detail", []) as Array)
bonus_overlay.set_refresh_info(adventure.attune_refresh_left()) # 迭代 11 第①批 T5b——刷新余量投喂
log_view.append_bb(Narrator.reward_line())
elif t == "rest":
var healed: int = int(info.get("healed", 0))
@@ -527,6 +530,7 @@ func _show_bonus_pick() -> void:
var payload: Dictionary = _pending_bonus
_open_modal(bonus_overlay)
bonus_overlay.open_pick(Narrator.bonus_pick_header(payload), _bonus_card_texts(payload.get("candidates", []) as Array, payload.get("aux_detail", []) as Array))
bonus_overlay.set_refresh_info(adventure.attune_refresh_left() if adventure != null else 0) # 迭代 11 第①批 T5b——刷新余量投喂
_refresh_presentations()
var _last_replace_items: Array = [] # 替换子态续玩重建缓存(aux_replace_presented 载荷)
@@ -572,6 +576,19 @@ func _aux_card_line(iid: String) -> String:
return "%s · %s" % [String(row.get("display_name", iid)), " ".join(parts)]
# 三选一刷新(迭代 11 第①批 T5b——P1 甲 X/Y 双源;通关停点经重发 bonus_choice_presented 自然重建,
# 节点停点在此读在途态重 present——零自动推进契约不破,停点仍在途)
func _on_bonus_refresh() -> void:
if adventure == null:
return
if not adventure.refresh_bonus():
return
if _bonus_context == "reward_node":
var st: Dictionary = adventure.node_bonus_pending_state()
bonus_overlay.open_pick(Narrator.node_bonus_header(), _bonus_card_texts(st.get("candidates", []) as Array, st.get("aux_detail", []) as Array))
bonus_overlay.set_refresh_info(adventure.attune_refresh_left())
func _on_bonus_pick_confirmed(item_id: String) -> void:
if adventure == null:
return
@@ -595,7 +612,7 @@ func _open_bonus_replace(items: Array, incoming: Dictionary) -> void:
rec["display_name"] = String(_item_name_lookup().get(String(rec.get("item_id", "")), String(rec.get("item_id", ""))))
named.append(rec)
bonus_overlay.open_replace(named, String(_item_name_lookup().get(String(incoming.get("item_id", "")), String(incoming.get("item_id", "")))),
named.size(), int(db.adventure.transitional_sync_slots))
named.size(), Attunement.slots(db, save_manager)) # 迭代 11 第①批 T7——呈现读数源切谓词表求值(过渡键退役)
func _on_bonus_replace_chosen(discard: int) -> void:
@@ -1028,6 +1045,7 @@ func _camp_data() -> Dictionary:
"balance": meta.keystone_balance() if meta != null else 0,
"forge": forge_rows,
"workshop": meta.workshop_status() if meta != null else [],
"attune": meta.attune_camp_status() if meta != null else {}, # 迭代 11 第①批 T4——同调装配面投影
"recent": save_manager.recent_summaries() if save_manager != null else [],
}
@@ -1035,6 +1053,14 @@ func _camp_data() -> Dictionary:
func _on_camp_buy(attr: String) -> void:
if meta == null:
return
if attr == "attune_refresh":
# Y 刷新升级行购买(迭代 11 第①批 T4——P1 甲;按钮禁用为常态防线,兜底拒绝可见 pattern 1)
if not meta.buy_attune_refresh():
board_zone.toast.show_toast("钥石不足——买不起这次升级", "warn")
return
camp_overlay.refresh_camp(_camp_data())
_refresh_presentations()
return
if not meta.buy_forge(attr):
# 余额不足(按钮禁用为常态防线——兜底拒绝可见,pattern 1)
board_zone.toast.show_toast("钥石不足——买不起这件强化", "warn")
@@ -1043,6 +1069,16 @@ func _on_camp_buy(attr: String) -> void:
_refresh_presentations()
# 同调装配切换(迭代 11 第①批 T4——装配关系唯一写口经 MetaProgression.toggle_attunement;满位兜底拒绝可见)
func _on_camp_attune_toggle(item_id: String) -> void:
if meta == null:
return
if not meta.toggle_attunement(item_id):
board_zone.toast.show_toast("同调位已满——先卸下再装配", "warn")
return
camp_overlay.refresh_camp(_camp_data())
# ---- 商店覆盖层与 GP 钱包(GP+商店房实施批——写调用唯一归组合根)----
func _on_shop_buy(product_id: String) -> void:
@@ -10,12 +10,16 @@ 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}]——高亮还原读面
@@ -34,6 +38,13 @@ func _init() -> void:
_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
@@ -64,9 +75,22 @@ func open_pick(header: String, cards: Array) -> void:
_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():
+71 -3
View File
@@ -13,6 +13,7 @@ extends Control
signal buy_requested(attr: String)
signal closed
signal attune_toggled(item_id: String) # 同调装配切换意图(迭代 11 第①批 T4——写口唯一归 MetaProgression.toggle_attunement)
const ATTR_LABELS := {"atk": "攻击", "max_hp": "生命上限", "armor_class": "护甲"} # 档位文案轴(数据契约 attr 词表,与 ShopOverlay 同款)
# 方向稿取色(4-camp.png——柜台头带深棕/行板暖米;双色墨=亮板配深墨)——值单点收 UiKit(T4 裸色收口批)
@@ -25,6 +26,11 @@ var _forge_holder: VBoxContainer
var _workshop_holder: VBoxContainer
var _forge_rows: Array[Dictionary] = [] # [{attr, level_l, price_l, btn}]——refresh_camp 就地重刷(零重建回流)
var _workshop_rows: Array[Dictionary] = [] # [{item_id, progress_l, status_l}]
var _attune_header_l: Label # 同调装配段(迭代 11 第①批 T4 工坊扩容——Slots 点亮呈现)
var _attune_btns: Array[Dictionary] = [] # [{item_id, btn}]——装配/卸下切换钮(满位禁选随刷)
var _attune_nodes: Array[Control] = [] # 段内节点册(open_camp 重建时定向清理,不扰工坊六行)
var _refresh_l: Label # Y 刷新升级行(P1 甲——每冒险开局给 Y 次、冒险周期池)
var _refresh_btn: Button
var _recent_list: Label # 最近冒险(逐行拼接 label——只读账面,随 open_camp 重建)
@@ -91,9 +97,15 @@ func _init() -> void:
_forge_holder = VBoxContainer.new()
_forge_holder.add_theme_constant_override("separation", 6)
mid.add_child(_counter_panel("属性锻造", UiKit.TEX_ICON_FORGE, _forge_holder, 356))
# 迭代 11 第①批 T4 工坊扩容(P2 甲程序占位——同调装配段+Y 升级行入工坊柜台,行板复用;行数增长以
# 柜台内滚承载,冻结三柜台版式与另两柜台零触碰,视觉终稿归营地批)
var ws_scroll := ScrollContainer.new()
ws_scroll.custom_minimum_size = Vector2(340, 336)
ws_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
_workshop_holder = VBoxContainer.new()
_workshop_holder.add_theme_constant_override("separation", 6)
mid.add_child(_counter_panel("道具工坊", UiKit.TEX_ICON_WORKSHOP, _workshop_holder, 356))
ws_scroll.add_child(_workshop_holder)
mid.add_child(_counter_panel("道具工坊", UiKit.TEX_ICON_WORKSHOP, ws_scroll, 356))
var atlas_holder := VBoxContainer.new()
mid.add_child(_counter_panel("奖励格图鉴", UiKit.TEX_ICON_ATLAS, atlas_holder, 356))
var lock_l := UiKit.label(" —— 柜台结构位预留:内容排 Alpha(地图战斗资源差异化批)", 13, false, UiKit.STONE)
@@ -121,7 +133,7 @@ func _init() -> void:
# 柜台列面板(方向稿同构):PanelContainer 米板身 + 头带深棕(图标+标题暖白)
func _counter_panel(title_text: String, icon_path: String, holder: VBoxContainer, min_w: int) -> PanelContainer:
func _counter_panel(title_text: String, icon_path: String, holder: Control, min_w: int) -> PanelContainer:
var panel := PanelContainer.new()
panel.custom_minimum_size = Vector2(min_w, 0)
panel.add_theme_stylebox_override("panel",
@@ -147,10 +159,11 @@ func _counter_panel(title_text: String, icon_path: String, holder: VBoxContainer
# ---- 柜台行构建(open_camp 重建;行结构仿 ShopOverlay 商品卡——亮板双色墨制)----
# 打开营地(全量重建三柜台行 + 最近冒险——数据 = 组合根 _camp_data() 投影)
# 打开营地(全量重建三柜台行 + 同调装配段 + 最近冒险——数据 = 组合根 _camp_data() 投影)
func open_camp(data: Dictionary) -> void:
_rebuild_forge(data)
_rebuild_workshop(data)
_rebuild_attune(data)
_rebuild_recent(data)
refresh_camp(data)
visible = true
@@ -196,6 +209,24 @@ func refresh_camp(data: Dictionary) -> void:
st_col = UiKit.GOLD_DEEP
card.status_l.text = st_text
card.status_l.add_theme_color_override("font_color", st_col)
# 同调装配段状态就地刷(迭代 11 第①批 T4——Slots 点亮/满位禁选/Y 升级行余额联动)
var att: Dictionary = data.get("attune", {})
if _attune_header_l != null:
var slots_n := int(att.get("slots", 0))
var eq: Array = att.get("equipped", [])
_attune_header_l.text = "── 同调装配 Slots %d/%d ──" % [eq.size(), slots_n]
var eq_set := {}
for v in eq:
eq_set[String(v)] = true
for card2 in _attune_btns:
var on: bool = eq_set.has(String(card2.item_id))
card2.btn.text = "卸下" if on else "装配"
card2.btn.disabled = (not on) and eq.size() >= slots_n # 满位禁选
if _refresh_l != null:
var rf: Dictionary = att.get("refresh", {})
_refresh_l.text = "三选一刷新 +%d 次/冒险 · Lv%d" % [int(rf.get("charges", 0)), int(rf.get("level", 0))]
_refresh_btn.text = "已满级" if bool(rf.get("maxed", false)) else "升级 %d 钥石" % int(rf.get("price", 0))
_refresh_btn.disabled = bool(rf.get("maxed", false)) or balance < int(rf.get("price", 0))
func _rebuild_forge(data: Dictionary) -> void:
@@ -252,6 +283,43 @@ func _rebuild_workshop(data: Dictionary) -> void:
_workshop_rows.append({"item_id": String(row.get("item_id", "")), "progress_l": progress_l, "status_l": status_l})
# 同调装配段重建(迭代 11 第①批 T4——工坊柜台尾部追加;Slots 点亮+九件行板+Y 升级行,程序占位面)
func _rebuild_attune(data: Dictionary) -> void:
for n in _attune_nodes:
n.queue_free()
_attune_nodes.clear()
_attune_btns.clear()
var att: Dictionary = data.get("attune", {})
_attune_header_l = UiKit.label("", 13, true, UiKit.INK)
_workshop_holder.add_child(_attune_header_l)
_attune_nodes.append(_attune_header_l)
for row in att.get("items", []):
var iid := String(row.get("item_id", ""))
var line := HBoxContainer.new()
line.add_theme_constant_override("separation", 6)
var name_l := UiKit.label(String(row.get("display_name", "")), 13, true, UiKit.INK)
name_l.custom_minimum_size = Vector2(96, 0)
line.add_child(name_l)
var form_l := UiKit.label("被动" if String(row.get("form", "")) == "passive" else "主动·充能", 12, false, UiKit.STONE)
form_l.custom_minimum_size = Vector2(58, 0)
line.add_child(form_l)
var btn := UiKit.button("装配")
btn.pressed.connect(attune_toggled.emit.bind(iid))
line.add_child(btn)
_workshop_holder.add_child(line)
_attune_nodes.append(line)
_attune_btns.append({"item_id": iid, "btn": btn})
var refresh_line := HBoxContainer.new()
refresh_line.add_theme_constant_override("separation", 6)
_refresh_l = UiKit.label("", 12, false, UiKit.STONE)
refresh_line.add_child(_refresh_l)
_refresh_btn = UiKit.button("升级")
_refresh_btn.pressed.connect(_emit_buy_requested.bind("attune_refresh"))
refresh_line.add_child(_refresh_btn)
_workshop_holder.add_child(refresh_line)
_attune_nodes.append(refresh_line)
func _rebuild_recent(data: Dictionary) -> void:
var lines: Array[String] = []
var recent: Array = data.get("recent", [])