merge: 迭代14.5调试整合批补遗(46dadbb——main.gd接线+ui_assembler散件撤除与角标+探针改接+注释口径,11件收编)

This commit is contained in:
DKChan committed 2026-10-05 00:51:44 +08:00
commit 10c301b230
11 files changed
+112 -110

No files matched your search

+6 -6
View File
@@ -67,7 +67,7 @@ var summary := {} # 最近一次结算的冒险摘要(
var _runner: LevelRunner # 当前关卡行进器(每关新建——关卡域状态天然隔离) var _runner: LevelRunner # 当前关卡行进器(每关新建——关卡域状态天然隔离)
var _pending_next_level := false # 大奖励已兑付、等 continue_to_next_level 确认 var _pending_next_level := false # 大奖励已兑付、等 continue_to_next_level 确认
var _last_level_payload := {} # 最近一次关卡结束载荷(失败结算的败退节点明细来源——数值验证取数面) var _last_level_payload := {} # 最近一次关卡结束载荷(失败结算的败退节点明细来源——数值验证取数面)
var _override := [0, 0] # TunePanel 百分比通道 [atk_pct, hp_pct](每关新 runner 都要应用) var _override := [0, 0] # 调试浮窗百分比通道 [atk_pct, hp_pct](每关新 runner 都要应用)
var _base_override := {"atk": 0, "max_hp": 0, "armor_class": 0} 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 _first_script := false # 首次脚本模式(关卡节点图生成 v0.4——true = 全关零抽签取池内模板 0;测量夹具恒 false) # 永久强化 flat 注入(元进度实现批——冒险开始参数面 params.hero_override 存入,共享给 LevelRunner;全零 = 白板)
var _last_aux_cleared := 0 # additive(迭代 7 T4——最近一次关卡末清空计数,summary 取数面) var _last_aux_cleared := 0 # additive(迭代 7 T4——最近一次关卡末清空计数,summary 取数面)
@@ -136,7 +136,7 @@ func start_adventure(seed_val: int, params: Dictionary = {}) -> bool:
_relics = [] _relics = []
for r in relic_seed: for r in relic_seed:
_relics.append(String(r)) _relics.append(String(r))
ledger["hero_hp"] = effective_max_hp() # 生命账本置满(生效上限口径——T17 修复:含 TunePanel pct,与战斗内同源) ledger["hero_hp"] = effective_max_hp() # 生命账本置满(生效上限口径——T17 修复:含调试浮窗 pct,与战斗内同源)
state = "running" state = "running"
return _start_current_level() return _start_current_level()
@@ -201,7 +201,7 @@ func resume_adventure(bp: Dictionary, params: Dictionary = {}) -> bool:
# 生命账本越界钳制(T17——spawn 守卫 EC16 免疫):断点可能由旧口径版本写出(如调试上限 # 生命账本越界钳制(T17——spawn 守卫 EC16 免疫):断点可能由旧口径版本写出(如调试上限
# 调高期带出的账本值 > 新口径生效上限,实例:hero_hp=28 > 基础 26)——恢复后钳到生效上限, # 调高期带出的账本值 > 新口径生效上限,实例:hero_hp=28 > 基础 26)——恢复后钳到生效上限,
# 否则首战 spawn「hero_hp 入参越界」拒收 → 「敌人系统 spawn 被拒」fatal。上限 = 表值+永久 # 否则首战 spawn「hero_hp 入参越界」拒收 → 「敌人系统 spawn 被拒」fatal。上限 = 表值+永久
# 强化 flat+加算区 flat 后乘 TunePanel pct(effective_max_hp 同式——此刻加算区尚未恢复, # 强化 flat+加算区 flat 后乘调试浮窗 pct(effective_max_hp 同式——此刻加算区尚未恢复,
# 钳制用 flat 已含的保守口径即可,加算区恢复后其余战斗照常)。 # 钳制用 flat 已含的保守口径即可,加算区恢复后其余战斗照常)。
ledger["hero_hp"] = mini(int(ledger.get("hero_hp", 0)), maxi(1, ledger["hero_hp"] = mini(int(ledger.get("hero_hp", 0)), maxi(1,
int(round(float(int(db.hero.max_hp) + int(_base_override.get("max_hp", 0))) * (100.0 + float(_override[1])) / 100.0)))) int(round(float(int(db.hero.max_hp) + int(_base_override.get("max_hp", 0))) * (100.0 + float(_override[1])) / 100.0))))
@@ -289,7 +289,7 @@ func _token_identity(tok: Dictionary) -> String:
return String(tok.get("kind", "")) return String(tok.get("kind", ""))
# TunePanel 百分比通道(测试面板覆盖——每关新 runner 都要应用;正式侧归元进度) # 调试浮窗百分比通道(测试工具覆盖——每关新 runner 都要应用;正式侧归元进度)
func set_hero_override(atk_pct: int, hp_pct: int) -> void: func set_hero_override(atk_pct: int, hp_pct: int) -> void:
_override = [atk_pct, hp_pct] _override = [atk_pct, hp_pct]
if _runner != null: if _runner != null:
@@ -297,7 +297,7 @@ func set_hero_override(atk_pct: int, hp_pct: int) -> void:
# 冒险账本侧生效上限(T17 修复——账本置满/回血封顶与战斗内上限同式同源): # 冒险账本侧生效上限(T17 修复——账本置满/回血封顶与战斗内上限同式同源):
# (表值 + 永久强化 flat + 加算区 flat)后乘 TunePanel 百分比——叠加次序与 LevelRunner.effective_max_hp # (表值 + 永久强化 flat + 加算区 flat)后乘调试浮窗百分比——叠加次序与 LevelRunner.effective_max_hp
# 逐字一致。旧口径两处只算 flat 不乘 pct:调生命上限 26→104 时开局血仍 26(账本 26 / 战斗内 104)、 # 逐字一致。旧口径两处只算 flat 不乘 pct:调生命上限 26→104 时开局血仍 26(账本 26 / 战斗内 104)、
# 通关 20% 回血封顶按 26 算。pct=0(模拟/冒烟默认)时乘法恒等——MC 锚零漂移。 # 通关 20% 回血封顶按 26 算。pct=0(模拟/冒烟默认)时乘法恒等——MC 锚零漂移。
func effective_max_hp() -> int: func effective_max_hp() -> int:
@@ -868,7 +868,7 @@ func _on_level_finished(payload: Dictionary) -> void:
# 通关兑付(非末关恰一次;迭代 10 三层奖励框架):部分回血先行 → 加权洗牌掷 3 → 三选一停点 # 通关兑付(非末关恰一次;迭代 10 三层奖励框架):部分回血先行 → 加权洗牌掷 3 → 三选一停点
#(顺序即契约——回血按当刻账本与上限结算,选择停点零自动推进) #(顺序即契约——回血按当刻账本与上限结算,选择停点零自动推进)
var hp_before := int(ledger.get("hero_hp", 0)) var hp_before := int(ledger.get("hero_hp", 0))
var max_eff := effective_max_hp() # T17 修复:回血封顶口径含 TunePanel pct(同战斗内上限) var max_eff := effective_max_hp() # T17 修复:回血封顶口径含调试浮窗 pct(同战斗内上限)
var heal := mini(int(floor(float(max_eff) * float(int(db.adventure.clear_heal_pct)) / 100.0)), max_eff - hp_before) var heal := mini(int(floor(float(max_eff) * float(int(db.adventure.clear_heal_pct)) / 100.0)), max_eff - hp_before)
ledger["hero_hp"] = hp_before + heal ledger["hero_hp"] = hp_before + heal
# 置位先于 emit(信号同步派发——处理器内立即确认时 _pending_next_level 必须已就位) # 置位先于 emit(信号同步派发——处理器内立即确认时 _pending_next_level 必须已就位)
+3 -3
View File
@@ -15,7 +15,7 @@
# 对应 GDD:关卡节点地图 v1.0(图版结构——本文件为 P1 批实现位;命名规范见 CLAUDE.md) # 对应 GDD:关卡节点地图 v1.0(图版结构——本文件为 P1 批实现位;命名规范见 CLAUDE.md)
# GP+商店房实施批(2026-09-21):GP 冒险钱包(战斗完成入账 clamp ≥0 · 冒险收束清零)+ 统一冒险 # GP+商店房实施批(2026-09-21):GP 冒险钱包(战斗完成入账 clamp ≥0 · 冒险收束清零)+ 统一冒险
# 加算区 bonus{atk,max_hp,armor_class}(商店购买与关卡大奖励共用账本——战斗开始参数 = 基础表值 + # 加算区 bonus{atk,max_hp,armor_class}(商店购买与关卡大奖励共用账本——战斗开始参数 = 基础表值 +
# 加算区,TunePanel 百分比在加算之后乘)+ 商店房节点兑付(开覆盖层/购买出账/离开回选路确认—— # 加算区,调试浮窗百分比在加算之后乘)+ 商店房节点兑付(开覆盖层/购买出账/离开回选路确认——
# 复用 continue_after_node 兑付确认纪律,零自动推进)。 # 复用 continue_after_node 兑付确认纪律,零自动推进)。
# 冒险结构实现批(2026-09-21)——接口漂移清偿登记(06 清单「切片行进代码与定稿的接口漂移」①②③⑥⑦): # 冒险结构实现批(2026-09-21)——接口漂移清偿登记(06 清单「切片行进代码与定稿的接口漂移」①②③⑥⑦):
# ① 种子域归冒险结构:start_level 旧路径(单关测试模式)保留自调 rng.new_adventure;新路径 # ① 种子域归冒险结构:start_level 旧路径(单关测试模式)保留自调 rng.new_adventure;新路径
@@ -105,7 +105,7 @@ func set_hero_override(atk_pct: int, hp_pct: int) -> void:
# 叠加次序(元进度实现批钉死):表值 → 永久强化 flat(base_override)→ 加算区 flat(bonus)先加 # 叠加次序(元进度实现批钉死):表值 → 永久强化 flat(base_override)→ 加算区 flat(bonus)先加
# → TunePanel 百分比后乘(调整板 = 测试工具恒乘最后——代拍板 6,与 GP 批既有次序兼容) # → 调试浮窗百分比后乘(调试数值工具恒乘最后——代拍板 6,与 GP 批既有次序兼容)
func effective_atk() -> int: func effective_atk() -> int:
var base: int = int(db.hero.atk) + int(base_override.get("atk", 0)) + int(bonus.get("atk", 0)) var base: int = int(db.hero.atk) + int(base_override.get("atk", 0)) + int(bonus.get("atk", 0))
return maxi(1, int(round(float(base) * (100.0 + float(hero_atk_pct)) / 100.0))) return maxi(1, int(round(float(base) * (100.0 + float(hero_atk_pct)) / 100.0)))
@@ -116,7 +116,7 @@ func effective_max_hp() -> int:
return maxi(1, int(round(float(base) * (100.0 + float(hero_hp_pct)) / 100.0))) return maxi(1, int(round(float(base) * (100.0 + float(hero_hp_pct)) / 100.0)))
# 护甲等级生效值(永久强化 flat + 加算区 armor_class 轴——无 TunePanel 百分比通道,纯 flat) # 护甲等级生效值(永久强化 flat + 加算区 armor_class 轴——无调试浮窗百分比通道,纯 flat)
func effective_ac() -> int: func effective_ac() -> int:
return maxi(0, int(db.hero.armor_class) + int(base_override.get("armor_class", 0)) + int(bonus.get("armor_class", 0))) return maxi(0, int(db.hero.armor_class) + int(base_override.get("armor_class", 0)) + int(bonus.get("armor_class", 0)))
+2 -2
View File
@@ -13,7 +13,7 @@ var _save_get: Callable # () → 存档承载现值(同上——营
var camp_overlay: CampOverlay var camp_overlay: CampOverlay
var shop_overlay: ShopOverlay var shop_overlay: ShopOverlay
var toast: ToastStack var toast: ToastStack
var start_overlay: Control # 开始层互斥面(开合仿 TunePanel 先例——非模态且树序在后,规范 2.3) var start_overlay: Control # 开始层互斥面(开合仿设置面先例——非模态且树序在后,规范 2.3)
var logger # 试玩日志(ux_reject 取数面) var logger # 试玩日志(ux_reject 取数面)
var _runner_get: Callable # () → 关卡推进器现值(组合根 lambda——随局更替) var _runner_get: Callable # () → 关卡推进器现值(组合根 lambda——随局更替)
var _open_modal: Callable var _open_modal: Callable
@@ -41,7 +41,7 @@ func _init(db_ref, meta_get: Callable, save_get: Callable, camp_overlay_ref: Cam
_refresh_resume_button = refresh_resume_button _refresh_resume_button = refresh_resume_button
# 打开营地(主菜单入口——开合与开始层互斥仿 TunePanel 先例:开始覆盖层非模态且树序在后, # 打开营地(主菜单入口——开合与开始层互斥仿设置面先例:开始覆盖层非模态且树序在后,
# 规范 2.3 遮挡纪律;打开时先刷工坊解锁——达标未解锁就地入账) # 规范 2.3 遮挡纪律;打开时先刷工坊解锁——达标未解锁就地入账)
func open_camp() -> void: func open_camp() -> void:
var meta = _meta_get.call() var meta = _meta_get.call()
+42 -34
View File
@@ -118,10 +118,10 @@ var shop_overlay: ShopOverlay # 商店覆盖层(GP+商店房实施
var camp_overlay: CampOverlay # 营地覆盖层(元进度实现批——主菜单营地入口兑付呈现) var camp_overlay: CampOverlay # 营地覆盖层(元进度实现批——主菜单营地入口兑付呈现)
var settings_store # 系统级设置独立存储(迭代 8 卡④——音量/全屏/减动效) var settings_store # 系统级设置独立存储(迭代 8 卡④——音量/全屏/减动效)
var settings_overlay: SettingsOverlay # 设置覆盖层(迭代 8 卡④——最小设置面) var settings_overlay: SettingsOverlay # 设置覆盖层(迭代 8 卡④——最小设置面)
var tune_panel: TunePanel var debug_overlay: DebugOverlay # 调试浮窗(迭代 14.5 调试整合批——种子/数值调整/批量模拟/两层调试入口收一)
var _modals = null # 模态层级状态机(modal_stack.gd——缺陷⑤纪律载体) var _modals = null # 模态层级状态机(modal_stack.gd——缺陷⑤纪律载体)
var _tune_sim = null # 调整板批量模拟控制器(tune_sim_controller.gd) var _tune_sim = null # 调试浮窗批量模拟控制器(tune_sim_controller.gd)
var _hints_shown := {} # 一次性非模态提示去重(每 run 一次:chord/no_adjacent) var _hints_shown := {} # 一次性非模态提示去重(每 run 一次:chord/no_adjacent)
var _refresh_used_round := 0 # 本轮已受理刷新次数(遥测 round_refresh_index——每轮 round_started 归零) var _refresh_used_round := 0 # 本轮已受理刷新次数(遥测 round_refresh_index——每轮 round_started 归零)
var _boss_active := false # Boss 遭遇变体(阶段指示附 Boss 名) var _boss_active := false # Boss 遭遇变体(阶段指示附 Boss 名)
@@ -242,15 +242,14 @@ func _build_ui() -> void:
logger.ap_per_round = int(db.action_economy.ap_per_round) logger.ap_per_round = int(db.action_economy.ap_per_round)
# 模态覆盖层(登记序 = 树序 = z 遮挡序:图例 < 明细回看 < 节点 < 关卡画面 < 三选一 < 终局 # 模态覆盖层(登记序 = 树序 = z 遮挡序:图例 < 明细回看 < 节点 < 关卡画面 < 三选一 < 终局
# < 商店 < 营地 < 调整板 < 设置 < 确认弹窗) # < 商店 < 营地 < 设置 < 确认弹窗 < 调试浮窗)
var overlays: Dictionary = UiAssembler.build_overlays(self, db, int(db.hero.atk), int(db.hero.max_hp)) var overlays: Dictionary = UiAssembler.build_overlays(self, db)
legend_overlay = overlays.legend_overlay legend_overlay = overlays.legend_overlay
review_overlay = overlays.review_overlay review_overlay = overlays.review_overlay
node_overlay = overlays.node_overlay node_overlay = overlays.node_overlay
_end_overlay = overlays.end_overlay _end_overlay = overlays.end_overlay
shop_overlay = overlays.shop_overlay shop_overlay = overlays.shop_overlay
camp_overlay = overlays.camp_overlay camp_overlay = overlays.camp_overlay
tune_panel = overlays.tune_panel
level_screen = LevelMapScreen.new() level_screen = LevelMapScreen.new()
add_child(level_screen) add_child(level_screen)
# 树序 = 模态登记序 = z 遮挡序(缺陷⑤纪律)——关卡画面须夹在节点层与商店层之间 # 树序 = 模态登记序 = z 遮挡序(缺陷⑤纪律)——关卡画面须夹在节点层与商店层之间
@@ -263,9 +262,6 @@ func _build_ui() -> void:
level_screen.abandon_requested.connect(_on_abandon_requested) # 放弃入口(2026-09-21 用户拍板——台账 119) level_screen.abandon_requested.connect(_on_abandon_requested) # 放弃入口(2026-09-21 用户拍板——台账 119)
_end_overlay.review_pressed.connect(_on_end_review) _end_overlay.review_pressed.connect(_on_end_review)
_end_overlay.continue_pressed.connect(_on_battle_end_continue) _end_overlay.continue_pressed.connect(_on_battle_end_continue)
tune_panel.closed.connect(_close_tune_panel)
tune_panel.apply_requested.connect(_on_tune_apply)
tune_panel.simulate_requested.connect(_on_tune_simulate)
settings_overlay = SettingsOverlay.new(settings_store) # 迭代 8 卡④——最小设置面(音量/全屏/减动效) settings_overlay = SettingsOverlay.new(settings_store) # 迭代 8 卡④——最小设置面(音量/全屏/减动效)
add_child(settings_overlay) add_child(settings_overlay)
settings_overlay.closed.connect(_on_settings_closed) settings_overlay.closed.connect(_on_settings_closed)
@@ -287,12 +283,10 @@ func _build_ui() -> void:
var start: Dictionary = UiAssembler.build_start_overlay(self, db) var start: Dictionary = UiAssembler.build_start_overlay(self, db)
_start_overlay = start.overlay _start_overlay = start.overlay
_seed_edit = start.seed_edit
_resume_btn = start.resumebtn _resume_btn = start.resumebtn
start.lvbtn.pressed.connect(_start_adventure_from_menu) start.lvbtn.pressed.connect(_start_adventure_from_menu)
start.resumebtn.pressed.connect(_resume_from_menu) # 迭代 8 卡②——续玩入口 start.resumebtn.pressed.connect(_resume_from_menu) # 迭代 8 卡②——续玩入口
start.tunebtn.pressed.connect(_show_tune_panel) start.dbgbtn.pressed.connect(_show_debug_overlay) # 迭代 14.5——调试角标入口(浮窗,非平级主按钮)
start.dbgbtn.pressed.connect(_start_debug_levels)
start.dbgbtn.visible = OS.get_environment("DS_DEBUG") == "1" # 迭代 8 卡④——调试入口默认隐藏门控(DS_DEBUG=1 点亮) start.dbgbtn.visible = OS.get_environment("DS_DEBUG") == "1" # 迭代 8 卡④——调试入口默认隐藏门控(DS_DEBUG=1 点亮)
# 营地/商店投影(T4/D4 拆件批——迁 camp_shop_presenter.gd;本类只接线、探针缝保缝转发; # 营地/商店投影(T4/D4 拆件批——迁 camp_shop_presenter.gd;本类只接线、探针缝保缝转发;
# 购买写调用唯一经 MetaProgression/LevelRunner core 单口——组合根 lambda 现值注入) # 购买写调用唯一经 MetaProgression/LevelRunner core 单口——组合根 lambda 现值注入)
@@ -326,11 +320,22 @@ func _build_ui() -> void:
confirm_dialog.confirmed.connect(_on_confirm_new_adventure) confirm_dialog.confirmed.connect(_on_confirm_new_adventure)
confirm_dialog.cancelled.connect(_on_confirm_cancelled) confirm_dialog.cancelled.connect(_on_confirm_cancelled)
# 调试浮窗(迭代 14.5 调试整合批——主菜单三散件收一:随机种子/数值调整/批量模拟/两层调试入口)。
# 树序在开始层与确认弹窗之后 = 浮于菜单之上的 Dialog(不隐藏开始层——与确认弹窗同款形态,
# 旧 TunePanel「与开始层互斥」形态退役);入口仅开始覆盖层角标(DS_DEBUG=1 门控)
debug_overlay = DebugOverlay.new(db, int(db.hero.atk), int(db.hero.max_hp))
add_child(debug_overlay)
_seed_edit = debug_overlay.seed_edit # 种子读取面随浮窗迁入(_seed_value 唯一消费点不变)
debug_overlay.closed.connect(_on_modal_closed.bind(debug_overlay))
debug_overlay.apply_requested.connect(_on_tune_apply)
debug_overlay.simulate_requested.connect(_on_tune_simulate)
debug_overlay.debug_levels_requested.connect(_start_debug_levels)
# 模态登记(缺陷⑤纪律——登记序 = 树序:含 move_child 调位后的关卡画面/三选一终位) # 模态登记(缺陷⑤纪律——登记序 = 树序:含 move_child 调位后的关卡画面/三选一终位)
_modals = ModalStack.new() _modals = ModalStack.new()
for m: Control in [legend_overlay, review_overlay, node_overlay, level_screen, bonus_overlay, _end_overlay, shop_overlay, camp_overlay, tune_panel, settings_overlay, confirm_dialog]: for m: Control in [legend_overlay, review_overlay, node_overlay, level_screen, bonus_overlay, _end_overlay, shop_overlay, camp_overlay, settings_overlay, confirm_dialog, debug_overlay]:
_register_modal(m) _register_modal(m)
_tune_sim = TuneSimController.new(db, tune_panel, int(db.hero.max_hp)) _tune_sim = TuneSimController.new(db, debug_overlay, int(db.hero.max_hp))
func _fatal_screen(msg: String) -> void: func _fatal_screen(msg: String) -> void:
@@ -432,7 +437,7 @@ func _resume_from_menu() -> void:
_reset_run_scope() _reset_run_scope()
log_view.clear() log_view.clear()
logger.start_session(int(bp.get("master_seed", 0))) logger.start_session(int(bp.get("master_seed", 0)))
adventure.set_hero_override(tune_panel.atk_pct(), tune_panel.hp_pct()) adventure.set_hero_override(debug_overlay.atk_pct(), debug_overlay.hp_pct())
if not adventure.resume_adventure(bp, {"hero_override": meta.hero_override() if meta != null else {}, "aux_unlocked": _aux.unlocked_set()}): if not adventure.resume_adventure(bp, {"hero_override": meta.hero_override() if meta != null else {}, "aux_unlocked": _aux.unlocked_set()}):
_fatal_screen("续玩恢复失败——见输出日志") _fatal_screen("续玩恢复失败——见输出日志")
return return
@@ -467,7 +472,7 @@ func _start_adventure() -> void:
log_view.clear() log_view.clear()
var seed_val := _seed_value() var seed_val := _seed_value()
logger.start_session(seed_val) logger.start_session(seed_val)
adventure.set_hero_override(tune_panel.atk_pct(), tune_panel.hp_pct()) adventure.set_hero_override(debug_overlay.atk_pct(), debug_overlay.hp_pct())
# 永久强化注入(元进度实现批):冒险开始参数面——meta 现算 override 显式传参(全零 = 白板) # 永久强化注入(元进度实现批):冒险开始参数面——meta 现算 override 显式传参(全零 = 白板)
# 首次冒险脚本(关卡节点图生成 v0.4):存档布尔位 false = 全关固定模板 0(教学重放);置位后逐关抽签 # 首次冒险脚本(关卡节点图生成 v0.4):存档布尔位 false = 全关固定模板 0(教学重放);置位后逐关抽签
var first_script: bool = not save_manager.is_first_adventure_done() var first_script: bool = not save_manager.is_first_adventure_done()
@@ -482,7 +487,7 @@ func _on_adventure_level_started(level_index: int, display_name: String) -> void
runner = adventure.runner() runner = adventure.runner()
log_view.append_bb(Narrator.level_open_line(display_name, int(adventure.master_seed), log_view.append_bb(Narrator.level_open_line(display_name, int(adventure.master_seed),
int(db.hero.atk), int(db.hero.max_hp), int(db.hero.atk), int(db.hero.max_hp),
tune_panel.atk_pct() > 0 or tune_panel.hp_pct() > 0)) debug_overlay.atk_pct() > 0 or debug_overlay.hp_pct() > 0))
func _reset_run_scope() -> void: func _reset_run_scope() -> void:
@@ -580,7 +585,7 @@ func _on_battle_end_continue() -> void:
settlement = null settlement = null
adventure = null adventure = null
_adventure_mode = false _adventure_mode = false
tune_panel.reset_sliders() debug_overlay.reset_sliders()
_start_overlay.visible = true _start_overlay.visible = true
_refresh_resume_button() # 迭代 8 卡②——回菜单按断点在档态刷新(收束后已清=隐藏) _refresh_resume_button() # 迭代 8 卡②——回菜单按断点在档态刷新(收束后已清=隐藏)
_refresh_presentations() _refresh_presentations()
@@ -593,13 +598,15 @@ func _on_end_review() -> void:
# ==================== 调试模式(连续两层——编排归 debug_session.gd; # ==================== 调试模式(连续两层——编排归 debug_session.gd;
# 单场遭遇用户入口已删(2026-09-13 用户裁决)——debug_session.start_single 原语仅探针使用) # 单场遭遇用户入口已删(2026-09-13 用户裁决)——debug_session.start_single 原语仅探针使用;
# 入口 2026-10-04 迁调试浮窗(debug_levels_requested 接线))
# T4/D3 收口注记:本节处理器(battle_created/battle_ready/override/seed)系组合根绑定面—— # T4/D3 收口注记:本节处理器(battle_created/battle_ready/override/seed)系组合根绑定面——
# settlement 写与 _wire_battle=ADR-0001 组合根 essence,override/seed 取数面在组合根 UI 态 # settlement 写与 _wire_battle=ADR-0001 组合根 essence,override/seed 取数面在组合根 UI 态
#(tune_panel/_seed_edit)——不迁移,信号连接留本根(契约 3)。==================== #(debug_overlay/_seed_edit)——不迁移,信号连接留本根(契约 3)。====================
func _start_debug_levels() -> void: func _start_debug_levels() -> void:
tune_panel.reset_sliders() debug_overlay.reset_sliders()
_close_modal(debug_overlay) # 浮窗内入口发起——随开随收(对局期不可再开:门禁在 _show_debug_overlay)
_start_overlay.visible = false _start_overlay.visible = false
log_view.clear() log_view.clear()
log_view.append_bb(Narrator.debug_open_line(_seed_value(), 1, true)) log_view.append_bb(Narrator.debug_open_line(_seed_value(), 1, true))
@@ -633,12 +640,12 @@ func _on_inter_next_pressed() -> void:
_fatal_screen("战斗启动失败——见输出日志") _fatal_screen("战斗启动失败——见输出日志")
# E4:加成生效值供给(debug_session 注入 Callable——每次从面板百分比现算,经 encounter 传参零表写; # E4:加成生效值供给(debug_session 注入 Callable——每次从浮窗百分比现算,经 encounter 传参零表写;
# 原表写路径 _apply_hero_override/_scaled_override 已废除,双写叠加 6→18→54 整类隐患随之移除; # 原表写路径 _apply_hero_override/_scaled_override 已废除,双写叠加 6→18→54 整类隐患随之移除;
# 合成式单点在 UiKit.scaled——与 TunePanel 生效值预览共用,本类不再持有同式私有实现) # 合成式单点在 UiKit.scaled——与调试浮窗生效值预览共用,本类不再持有同式私有实现)
func _debug_hero_override() -> Dictionary: func _debug_hero_override() -> Dictionary:
return {"atk": UiKit.scaled(int(db.hero.atk), tune_panel.atk_pct()), return {"atk": UiKit.scaled(int(db.hero.atk), debug_overlay.atk_pct()),
"max_hp": UiKit.scaled(int(db.hero.max_hp), tune_panel.hp_pct())} "max_hp": UiKit.scaled(int(db.hero.max_hp), debug_overlay.hp_pct())}
# 奖励徽章身份解析(组合根守卫:仅挖掘期 Active 相位内查询——地图战斗资源 Ready 域外零读、零诊断噪音; # 奖励徽章身份解析(组合根守卫:仅挖掘期 Active 相位内查询——地图战斗资源 Ready 域外零读、零诊断噪音;
@@ -1096,22 +1103,23 @@ func _on_diagnose_pressed() -> void:
board_zone.toast.show_toast("已转储诊断: " + path, "info") board_zone.toast.show_toast("已转储诊断: " + path, "info")
# ==================== 数值调整板(加成应用/批量模拟编排——组合根承接,视图纯 UI)==================== # ==================== 调试浮窗(迭代 14.5 整合批——原数值调整板节收编;开合/应用/模拟编排组合根承接)====================
func _show_tune_panel() -> void: func _show_debug_overlay() -> void:
_start_overlay.visible = false # 门禁(用户裁 2026-10-04:仅开始菜单可开,对局中不可达):入口角标宿主即开始层,
_open_modal(tune_panel) # 模态栈单口(T3 收口批) # 此护栏防御未来调用面(含探针直呼);浮窗树序在开始层之后 = 悬浮其上,不隐藏菜单
if not _start_overlay.visible:
return
_open_modal(debug_overlay) # 模态栈单口
func _close_tune_panel() -> void: func _close_debug_overlay() -> void:
_close_modal(tune_panel) # 模态栈单口(T3 收口批) _close_modal(debug_overlay) # 模态栈单口——开始层全程未隐藏,无需恢复
_start_overlay.visible = true
_refresh_resume_button() # 迭代 8 卡②——回菜单按断点在档态刷新
func _on_tune_apply(atk_pct: int, hp_pct: int) -> void: func _on_tune_apply(atk_pct: int, hp_pct: int) -> void:
# E4:加成无持久态可"落"——各战斗开局经 _debug_hero_override/runner 现算生效(面板仅关闭回开始层) # E4:加成无持久态可"落"——各战斗开局经 _debug_hero_override/runner 现算生效(应用=关闭浮窗)
_close_tune_panel() _close_debug_overlay()
func _on_tune_simulate(n: int) -> void: func _on_tune_simulate(n: int) -> void:
+1 -1
View File
@@ -13,7 +13,7 @@ const SEED_BASE := 4200 # 模拟种子基(与历史实现同源——同种
const PlaytestLogger := preload("res://presentation/playtest_logger.gd") # 试玩日志(同层依赖——运行时 load 收敛为 preload 常量) const PlaytestLogger := preload("res://presentation/playtest_logger.gd") # 试玩日志(同层依赖——运行时 load 收敛为 preload 常量)
var db var db
var panel # TunePanel setter 面:atk_pct/hp_pct/set_sim_running/set_sim_progress/set_sim_report var panel # 调试浮窗 setter 面:atk_pct/hp_pct/set_sim_running/set_sim_progress/set_sim_report
var _base_hp := 0 # 报告中的"基础 HP"口径注记值 var _base_hp := 0 # 报告中的"基础 HP"口径注记值
var _driver = null # 关卡自动策略驱动(调用方注入;null = 旧行为运行时 load 兜底) var _driver = null # 关卡自动策略驱动(调用方注入;null = 旧行为运行时 load 兜底)
+18 -28
View File
@@ -61,9 +61,10 @@ static func build_battle_ui(root: Control, db) -> Dictionary:
} }
# 模态覆盖层组(z 序 = 登记序:图例 < 明细回看 < 节点 < 终局 < 商店 < 调整板)。 # 模态覆盖层组(z 序 = 登记序:图例 < 明细回看 < 节点 < 终局 < 商店)。
# 返回 {legend_overlay, review_overlay, node_overlay, end_overlay, shop_overlay, tune_panel};调用方按本序 addChild。 # 返回 {legend_overlay, review_overlay, node_overlay, end_overlay, shop_overlay, camp_overlay};调用方按本序 addChild。
static func build_overlays(host: Control, db, base_atk: int, base_hp: int) -> Dictionary: # (调整板已随迭代 14.5 调试整合批退役——调试浮窗由组合根直构挂树序最末,见 main._build_ui)
static func build_overlays(host: Control, db) -> Dictionary:
var legend_overlay := LegendOverlay.new(int(db.action_economy.rap_refresh_cost)) # 打磨包 2026-09-29——RAP 行表值注入 var legend_overlay := LegendOverlay.new(int(db.action_economy.rap_refresh_cost)) # 打磨包 2026-09-29——RAP 行表值注入
host.add_child(legend_overlay) host.add_child(legend_overlay)
@@ -80,14 +81,10 @@ static func build_overlays(host: Control, db, base_atk: int, base_hp: int) -> Di
var shop_overlay := ShopOverlay.new() var shop_overlay := ShopOverlay.new()
host.add_child(shop_overlay) host.add_child(shop_overlay)
# 营地覆盖层(元进度实现批 2026-09-21——主菜单营地入口兑付呈现;开合仿 TunePanel 与开始层互斥) # 营地覆盖层(元进度实现批 2026-09-21——主菜单营地入口兑付呈现;开合与开始层互斥仿设置面先例)
var camp_overlay := CampOverlay.new() var camp_overlay := CampOverlay.new()
host.add_child(camp_overlay) host.add_child(camp_overlay)
var tune_panel := TunePanel.new(db, base_atk, base_hp)
tune_panel.visible = false # 显式初始化——原版依赖"被不透明开始层遮蔽"的树序巧合(缺陷⑤家族);模态锁输入以 visible 为准
host.add_child(tune_panel)
return { return {
"legend_overlay": legend_overlay, "legend_overlay": legend_overlay,
"review_overlay": review_overlay, "review_overlay": review_overlay,
@@ -95,12 +92,12 @@ static func build_overlays(host: Control, db, base_atk: int, base_hp: int) -> Di
"end_overlay": end_overlay, "end_overlay": end_overlay,
"shop_overlay": shop_overlay, "shop_overlay": shop_overlay,
"camp_overlay": camp_overlay, "camp_overlay": camp_overlay,
"tune_panel": tune_panel,
} }
# 开始覆盖层(返回 {overlay, box, seed_edit, lvbtn, tunebtn, dbgbtn}——按钮接线归组合根; # 开始覆盖层(返回 {overlay, box, lvbtn, resumebtn, campbtn, settingsbtn, quitbtn, dbgbtn}——按钮接线归组合根;
# 单场遭遇用户入口已删(2026-09-13 用户裁决——单场原语仅探针经 debug_session.start_single 使用)) # 迭代 14.5 调试整合批:随机种子/数值调整板/两层调试三散件撤出主按钮组,收进调试浮窗——
# 本层只留右下角「调试」角标入口(DS_DEBUG=1 门控归组合根),dbgbtn 即该角标)
static func build_start_overlay(host: Control, db = null) -> Dictionary: static func build_start_overlay(host: Control, db = null) -> Dictionary:
var overlay := overlay_bg(host, UiKit.BG_OVERLAY) var overlay := overlay_bg(host, UiKit.BG_OVERLAY)
# 石纹底噪底层(样张 v2 冻结批 T2——z 序=dim 之上/内容之下;缺位不挂即回退平色) # 石纹底噪底层(样张 v2 冻结批 T2——z 序=dim 之上/内容之下;缺位不挂即回退平色)
@@ -121,26 +118,12 @@ static func build_start_overlay(host: Control, db = null) -> Dictionary:
box.add_child(sub) box.add_child(sub)
box.add_child(HSeparator.new()) box.add_child(HSeparator.new())
var seed_row := HBoxContainer.new()
box.add_child(seed_row)
# 字号下限(v3.5 批 B3——正文 ≥16)+暗底显式暖白
var seed_label := UiKit.label("随机种子:", 16, false, UiKit.WARM)
seed_row.add_child(seed_label)
var seed_edit := LineEdit.new()
seed_edit.text = "42"
seed_edit.custom_minimum_size = Vector2(120, 0)
seed_row.add_child(seed_edit)
# 唯一试玩入口 = 正式「开始冒险」(三关序列);单关测试直入口(L2/L3)退役(2026-09-23 用户拍板) # 唯一试玩入口 = 正式「开始冒险」(三关序列);单关测试直入口(L2/L3)退役(2026-09-23 用户拍板)
var resumebtn := UiKit.button("▶ 继续上局(自最近停点恢复)") # 迭代 8 卡②——有断点才亮(可见性归组合根刷新) var resumebtn := UiKit.button("▶ 继续上局(自最近停点恢复)") # 迭代 8 卡②——有断点才亮(可见性归组合根刷新)
resumebtn.visible = false resumebtn.visible = false
box.add_child(resumebtn) box.add_child(resumebtn)
var lvbtn := UiKit.button("▶ 开始冒险(三关连探:地精洞窟 → 幽暗地域 → 灰矮人矿城)") var lvbtn := UiKit.button("▶ 开始冒险(三关连探:地精洞窟 → 幽暗地域 → 灰矮人矿城)")
box.add_child(lvbtn) box.add_child(lvbtn)
var tunebtn := UiKit.button("测试用 · 角色数值调整板(模拟天赋树属性提升)")
box.add_child(tunebtn)
var dbgbtn := UiKit.button("调试 · 连续关卡两层(HP 关内延续)")
box.add_child(dbgbtn)
var campbtn := UiKit.button("营地 · 永久强化(钥石换永久属性——跨局成长账本)") # 元进度实现批——主菜单营地入口(双入口框架的主菜单一侧) var campbtn := UiKit.button("营地 · 永久强化(钥石换永久属性——跨局成长账本)") # 元进度实现批——主菜单营地入口(双入口框架的主菜单一侧)
box.add_child(campbtn) box.add_child(campbtn)
var settingsbtn := UiKit.button("设置(音量 / 全屏 / 减动效)") # 迭代 8 卡④——最小设置面入口 var settingsbtn := UiKit.button("设置(音量 / 全屏 / 减动效)") # 迭代 8 卡④——最小设置面入口
@@ -149,6 +132,13 @@ static func build_start_overlay(host: Control, db = null) -> Dictionary:
var quitbtn := UiKit.button("退出游戏") var quitbtn := UiKit.button("退出游戏")
box.add_child(quitbtn) box.add_child(quitbtn)
# 迭代 14.5 调试整合批——调试入口收右下角标(与主按钮组非平级;DS_DEBUG=1 点亮,可见性归组合根)。
# margin 16px 角距;字号降档与主按钮拉开层级
var dbgbtn := UiKit.button("调试")
dbgbtn.add_theme_font_size_override("font_size", 13)
dbgbtn.set_anchors_and_offsets_preset(Control.PRESET_BOTTOM_RIGHT, Control.PRESET_MODE_MINSIZE, 16)
overlay.add_child(dbgbtn)
# 玩法帮助文案(表值驱动——2026-09-16 AP 压速批遗留硬编码 8 随手修正;护甲等级掷点批同步"减伤"→"护甲强化") # 玩法帮助文案(表值驱动——2026-09-16 AP 压速批遗留硬编码 8 随手修正;护甲等级掷点批同步"减伤"→"护甲强化")
var help_text := "玩法:每轮 %d AP——挖开(1AP)/插旗(1AP)/移动(免费)/连锁(免费)。\n每个非封锁格都藏一种格面资源(增伤/护甲强化/回血/爆裂/削弱)。开安全格、标中雷 = 正向兑付;\n开雷格、标错安全格 = 负向兑付——全部在轮末结算逐行兑现;吃掷点的基础攻击按护甲等级判定,无 miss、最低保底 1 点。\n停手:剩余 AP 转存 RAP;弃图刷新:RAP−%d 当场换图继续挖(每轮剩 %d 次)。\n敌人全灭胜、主角倒下负。悬停可看三态光标与可挖域微光。\n地形变异:第二关起 Boss 图带整图变异(贫矿脉——回复与护甲资源减少、攻击资源增多;开战横幅与 HUD 角标常驻提示)。" % [ var help_text := "玩法:每轮 %d AP——挖开(1AP)/插旗(1AP)/移动(免费)/连锁(免费)。\n每个非封锁格都藏一种格面资源(增伤/护甲强化/回血/爆裂/削弱)。开安全格、标中雷 = 正向兑付;\n开雷格、标错安全格 = 负向兑付——全部在轮末结算逐行兑现;吃掷点的基础攻击按护甲等级判定,无 miss、最低保底 1 点。\n停手:剩余 AP 转存 RAP;弃图刷新:RAP−%d 当场换图继续挖(每轮剩 %d 次)。\n敌人全灭胜、主角倒下负。悬停可看三态光标与可挖域微光。\n地形变异:第二关起 Boss 图带整图变异(贫矿脉——回复与护甲资源减少、攻击资源增多;开战横幅与 HUD 角标常驻提示)。" % [
int(db.action_economy.ap_per_round), int(db.action_economy.rap_refresh_cost), int(db.action_economy.refresh_cap_per_round)] int(db.action_economy.ap_per_round), int(db.action_economy.rap_refresh_cost), int(db.action_economy.refresh_cap_per_round)]
@@ -156,9 +146,9 @@ static func build_start_overlay(host: Control, db = null) -> Dictionary:
var help := UiKit.label(help_text, 16, false, UiKit.WARM) var help := UiKit.label(help_text, 16, false, UiKit.WARM)
box.add_child(help) box.add_child(help)
return {"overlay": overlay, "box": box, "seed_edit": seed_edit, return {"overlay": overlay, "box": box,
"lvbtn": lvbtn, "resumebtn": resumebtn, "tunebtn": tunebtn, "dbgbtn": dbgbtn, "lvbtn": lvbtn, "resumebtn": resumebtn,
"campbtn": campbtn, "settingsbtn": settingsbtn, "quitbtn": quitbtn} "campbtn": campbtn, "settingsbtn": settingsbtn, "quitbtn": quitbtn, "dbgbtn": dbgbtn}
# 调试两层模式的层间过渡覆盖层(历史入口专用)。返回 {overlay, label, next_btn}——接线归组合根 # 调试两层模式的层间过渡覆盖层(历史入口专用)。返回 {overlay, label, next_btn}——接线归组合根
+1 -1
View File
@@ -4,7 +4,7 @@
# 三柜台列(属性锻造/道具工坊/奖励格图鉴,头带深棕+米色板身+内嵌行板,暗底→亮板双色墨制)+ # 三柜台列(属性锻造/道具工坊/奖励格图鉴,头带深棕+米色板身+内嵌行板,暗底→亮板双色墨制)+
# 底栏关闭钮/操作注记;最近冒险收底栏条带。信号面/投影契约零变化:buy_requested(attr)/closed、 # 底栏关闭钮/操作注记;最近冒险收底栏条带。信号面/投影契约零变化:buy_requested(attr)/closed、
# open_camp(data)/refresh_camp(data)、_forge_rows 行结构 {attr,level_l,price_l,btn}(探针依赖)。 # open_camp(data)/refresh_camp(data)、_forge_rows 行结构 {attr,level_l,price_l,btn}(探针依赖)。
# 挂接方式:开合与开始层互斥仿 TunePanel 先例(开始覆盖层非模态且树序在后——打开本层前由组合根 # 挂接方式:开合与开始层互斥仿设置面先例(开始覆盖层非模态且树序在后——打开本层前由组合根
# 隐藏开始层,规范 2.3 遮挡纪律)。 # 隐藏开始层,规范 2.3 遮挡纪律)。
# 高内聚边界:纯视图 setter 面——账本快照经组合根喂入(MetaProgression 只读投影),购买/关闭 # 高内聚边界:纯视图 setter 面——账本快照经组合根喂入(MetaProgression 只读投影),购买/关闭
# 意图经信号外发——零核心写调用、零 MetaProgression 持有。 # 意图经信号外发——零核心写调用、零 MetaProgression 持有。
+2 -2
View File
@@ -104,7 +104,7 @@ const DOT_OFF := PANEL_BORDER
# ── 减动效(battle.md §Transitions 内建承诺:开启时时长减半;结算逐行兑现为信息载体降速不跳过)── # ── 减动效(battle.md §Transitions 内建承诺:开启时时长减半;结算逐行兑现为信息载体降速不跳过)──
# 切片期入口 = TunePanel 开关(设置独立于存档两分制——正式设置层载体随设置批落位) # 唯一入口 = 设置面开关(迭代 8 卡④ 转正;切片期调整板开关已随其退役,调整板本体 14.5 收进调试浮窗)
static var motion_scale := 1.0 # 1.0 常态 / 0.5 减动效 static var motion_scale := 1.0 # 1.0 常态 / 0.5 减动效
# 全部呈现层 tween 时长经本函数包装——新增动效一律 UiKit.motion(秒),禁再写裸时长字面量 # 全部呈现层 tween 时长经本函数包装——新增动效一律 UiKit.motion(秒),禁再写裸时长字面量
@@ -232,7 +232,7 @@ static func modal_shell(parent: Control, dim_color: Color, min_size := Vector2.Z
return {"dim": dim, "center": center, "panel": panel, "box": box} return {"dim": dim, "center": center, "panel": panel, "box": box}
# 属性百分比合成(main._debug_hero_override 与 TunePanel 生效值预览共用单点——两处同式实现收口) # 属性百分比合成(main._debug_hero_override 与调试浮窗生效值预览共用单点——两处同式实现收口)
static func scaled(base: int, pct: int) -> int: static func scaled(base: int, pct: int) -> int:
return maxi(1, int(round(float(base) * (100.0 + float(pct)) / 100.0))) return maxi(1, int(round(float(base) * (100.0 + float(pct)) / 100.0)))
+4 -4
View File
@@ -368,7 +368,7 @@ func _smoke_adventure() -> void:
var hp_before: int = int(p.get("hp_before", -1)) var hp_before: int = int(p.get("hp_before", -1))
var hp_after: int = int(p.get("hp_after", -1)) var hp_after: int = int(p.get("hp_after", -1))
# 模拟档 v3.4 商店贪心购血合法抬升(L2/L3 店在 Boss 前)——回血口径窗口 = 加算区件 + 购血余量 0..15 # 模拟档 v3.4 商店贪心购血合法抬升(L2/L3 店在 Boss 前)——回血口径窗口 = 加算区件 + 购血余量 0..15
# T17 账本口径修复随批:置满/回血封顶改生效上限口径(flat 后乘 TunePanel pct)——窗口同步 ×5(本段 pct=400) # T17 账本口径修复随批:置满/回血封顶改生效上限口径(flat 后乘调试浮窗 pct)——窗口同步 ×5(本段 pct=400)
var heal_ok_i := false var heal_ok_i := false
for extra in 46: for extra in 46:
var m := int(round(float(int(_db.hero.max_hp) + hp_acc + extra) * 5.0)) var m := int(round(float(int(_db.hero.max_hp) + hp_acc + extra) * 5.0))
@@ -393,7 +393,7 @@ func _smoke_adventure() -> void:
check(ks_gain_ok, "钥石逐杀入账(首杀档位 + 累计逐位)") check(ks_gain_ok, "钥石逐杀入账(首杀档位 + 累计逐位)")
check(resolve_ok, "点选恰兑付选中一件(值=令牌档位表值;未选项零生效)") check(resolve_ok, "点选恰兑付选中一件(值=令牌档位表值;未选项零生效)")
var seq: Array = st["encounter_max_hp_seq"] var seq: Array = st["encounter_max_hp_seq"]
var k := 5.0 # hp+400 ⇒ TunePanel 百分比后乘 ×5(加算先加、百分比后乘——生效次序口径) var k := 5.0 # hp+400 ⇒ 调试浮窗百分比后乘 ×5(加算先加、百分比后乘——生效次序口径)
var exp_base := maxi(1, int(round(float(int(_db.hero.max_hp)) * k))) var exp_base := maxi(1, int(round(float(int(_db.hero.max_hp)) * k)))
# 模拟档 v3.4 商店贪心购血(数值修复批)合法抬升——上限窗口 = 基础 + 加算区 3 件 + 购血余量; # 模拟档 v3.4 商店贪心购血(数值修复批)合法抬升——上限窗口 = 基础 + 加算区 3 件 + 购血余量;
# TP0 数值对后 max_hp 件有大生命 +10(rare 档)——上界按大件最坏构成 3×10+购血 15=+45(TP0 放宽) # TP0 数值对后 max_hp 件有大生命 +10(rare 档)——上界按大件最坏构成 3×10+购血 15=+45(TP0 放宽)
@@ -512,7 +512,7 @@ func _smoke_adventure() -> void:
var expect_acc: int = acc_shop + (int(a8._bonus_result.get("value", 0)) if picked2 == "max_hp" else 0) var expect_acc: int = acc_shop + (int(a8._bonus_result.get("value", 0)) if picked2 == "max_hp" else 0)
check(int(a8.bonus.get("max_hp", -1)) == expect_acc, check(int(a8.bonus.get("max_hp", -1)) == expect_acc,
"三选一属性件与商店购买同账累加(max_hp=%d = %d + 三选一%s)" % [int(a8.bonus.get("max_hp", -1)), acc_shop, picked2]) "三选一属性件与商店购买同账累加(max_hp=%d = %d + 三选一%s)" % [int(a8.bonus.get("max_hp", -1)), acc_shop, picked2])
var max_eff8: int = a8.effective_max_hp() # T17 口径修复随批:账本生效上限含 TunePanel pct(a8 已 set 400) var max_eff8: int = a8.effective_max_hp() # T17 口径修复随批:账本生效上限含调试浮窗 pct(a8 已 set 400)
check(int(a8.ledger["hero_hp"]) <= max_eff8, check(int(a8.ledger["hero_hp"]) <= max_eff8,
"通关部分回血封顶(hp %d ≤ 生效上限 %d——血回满语义退役)" % [int(a8.ledger["hero_hp"]), max_eff8]) "通关部分回血封顶(hp %d ≤ 生效上限 %d——血回满语义退役)" % [int(a8.ledger["hero_hp"]), max_eff8])
check(a8.continue_to_next_level(), "兑付后推进受理(下一关开始)") check(a8.continue_to_next_level(), "兑付后推进受理(下一关开始)")
@@ -3599,7 +3599,7 @@ func _smoke_shop_room() -> void:
by_id = _shop_products_by_id(runner.shop_products()) by_id = _shop_products_by_id(runner.shop_products())
check(int(by_id["atk_up"]["price"]) == 8 and int(by_id["atk_up"]["purchased"]) == 1, check(int(by_id["atk_up"]["price"]) == 8 and int(by_id["atk_up"]["purchased"]) == 1,
"atk_up 二购现价 = 4+2+2=8(步长 2 × 已购 1 档,现价现算)") "atk_up 二购现价 = 4+2+2=8(步长 2 × 已购 1 档,现价现算)")
# 行进段百分比归零(购买/生效断言按加算区裸值口径——TunePanel 百分比后乘;hp 快照钳到新口径上限防 spawn 越界,测试通道) # 行进段百分比归零(购买/生效断言按加算区裸值口径——调试浮窗百分比后乘;hp 快照钳到新口径上限防 spawn 越界,测试通道)
runner.set_hero_override(0, 0) runner.set_hero_override(0, 0)
runner.hero_hp = mini(runner.hero_hp, runner.effective_max_hp()) runner.hero_hp = mini(runner.hero_hp, runner.effective_max_hp())
# hp_up:上限与当前生命同步 +3(封顶新上限) # hp_up:上限与当前生命同步 +3(封顶新上限)
+3 -3
View File
@@ -141,8 +141,8 @@ func _ready() -> void:
await get_tree().process_frame await get_tree().process_frame
if main.settings_overlay.visible or not main._start_overlay.visible: if main.settings_overlay.visible or not main._start_overlay.visible:
_problems.append("设置面关闭后未恢复开始层") _problems.append("设置面关闭后未恢复开始层")
# 旧入口退役断言(卡④ AC③——测试调整板不再含减动效开关) # 旧入口退役断言(卡④ AC③——测试调整板不再含减动效开关;14.5 起载体=调试浮窗)
var legacy_motion = ProbeUiUtil.find_check_button(main.tune_panel, "减动效") var legacy_motion = ProbeUiUtil.find_check_button(main.debug_overlay, "减动效")
if legacy_motion != null: if legacy_motion != null:
_problems.append("调整板减动效旧入口未退役(设置面唯一入口失守——卡④ AC③)") _problems.append("调整板减动效旧入口未退役(设置面唯一入口失守——卡④ AC③)")
# D1 断言(二轮):退出游戏只在一级菜单——开始菜单有、设置面无 # D1 断言(二轮):退出游戏只在一级菜单——开始菜单有、设置面无
@@ -157,7 +157,7 @@ func _ready() -> void:
_problems.append("继续上局按钮缺失(开始层 resumebtn——装配器)") _problems.append("继续上局按钮缺失(开始层 resumebtn——装配器)")
elif resume_btn0.visible: elif resume_btn0.visible:
_problems.append("无断点态继续上局按钮未隐藏(卡② AC①——新档合法空态)") _problems.append("无断点态继续上局按钮未隐藏(卡② AC①——新档合法空态)")
main.tune_panel.set_sliders(400, 200) # 高加成保 L1 可通关——L2 岔路选路段只能经冒险推进到达(单关直入口退役) main.debug_overlay.set_sliders(400, 200) # 高加成保 L1 可通关——L2 岔路选路段只能经冒险推进到达(单关直入口退役)
var t1 := Time.get_ticks_msec() var t1 := Time.get_ticks_msec()
main._start_adventure_from_menu() main._start_adventure_from_menu()
await get_tree().process_frame await get_tree().process_frame
+30 -26
View File
@@ -1,7 +1,8 @@
# VERTICAL SLICE - NOT FOR PRODUCTION # VERTICAL SLICE - NOT FOR PRODUCTION
# Date: 2026-09-11(2026-09-12 code-review 修复批重写——加成应用契约改为组合根单一应用路径) # Date: 2026-09-11(2026-09-12 code-review 修复批重写——加成应用契约改为组合根单一应用路径)
# 数值调整板场景探针(playtest 缺陷⑤回归载体——面板此前被不透明开始覆盖层遮挡 = "点击无反应"): # 调试浮窗场景探针(2026-10-04 迭代 14.5 调试整合批随批改接——原数值调整板整体收进 debug_overlay,
# 驱动真实 UI 路径——打开可见性 / 滑条联动 / 应用(apply_requested → 组合根)/ # 浮窗形态悬浮于开始菜单之上不隐藏菜单;playtest 缺陷⑤回归载体沿袭):
# 驱动真实 UI 路径——打开可见性(浮于菜单之上+树序在后)/ 滑条联动 / 应用(apply_requested → 组合根)/
# 重置 / 批量模拟全链路(N=10)/ E4 回归断言(hero 表运行期不可变 + 单次加成经 encounter 生效)。 # 重置 / 批量模拟全链路(N=10)/ E4 回归断言(hero 表运行期不可变 + 单次加成经 encounter 生效)。
# 运行:Godot_console.exe --headless --path . res://smoke/ui_tune_probe.tscn # 运行:Godot_console.exe --headless --path . res://smoke/ui_tune_probe.tscn
extends Node extends Node
@@ -22,41 +23,44 @@ func _ready() -> void:
var base_hp := int(main.db.hero.max_hp) var base_hp := int(main.db.hero.max_hp)
var boosted_atk := base_atk * 2 # 攻击滑条 100 → ×2 var boosted_atk := base_atk * 2 # 攻击滑条 100 → ×2
var boosted_hp := int(round(base_hp * 1.5)) # 生命滑条 50 → +50% var boosted_hp := int(round(base_hp * 1.5)) # 生命滑条 50 → +50%
# ① 打开可见性(缺陷⑤回归:打开面板必须隐藏不透明的开始覆盖层) # ① 打开可见性(14.5 浮窗形态:悬浮于菜单之上——开始层不隐藏,树序必须在开始层之后 = 绘制在上;
main._show_tune_panel() # 旧「打开必须隐藏开始层」断言随互斥形态退役)
if not main.tune_panel.visible: main._show_debug_overlay()
problems.append("面板未打开") if not main.debug_overlay.visible:
if main._start_overlay.visible: problems.append("调试浮窗未打开")
problems.append("开始覆盖层未隐藏——面板被遮挡(缺陷⑤复发)") if not main._start_overlay.visible:
problems.append("开始覆盖层被隐藏——浮窗应悬浮于菜单之上(14.5 形态失守)")
if main.debug_overlay.get_index() < main._start_overlay.get_index():
problems.append("调试浮窗树序在开始层之前——遮挡关系失守")
# ② 滑条 → 生效预览联动 # ② 滑条 → 生效预览联动
main.tune_panel.set_sliders(100, 50) main.debug_overlay.set_sliders(100, 50)
if str(boosted_atk) not in main.tune_panel.effect_text() or str(boosted_hp) not in main.tune_panel.effect_text(): if str(boosted_atk) not in main.debug_overlay.effect_text() or str(boosted_hp) not in main.debug_overlay.effect_text():
problems.append("生效预览未联动(%s)" % main.tune_panel.effect_text()) problems.append("生效预览未联动(%s)" % main.debug_overlay.effect_text())
# ③ 应用(真实信号路径:apply_requested → 组合根)——E4 后加成零表写,生效断言移至 ⑧ 战斗快照 # ③ 应用(真实信号路径:apply_requested → 组合根)——E4 后加成零表写,生效断言移至 ⑧ 战斗快照
main.tune_panel.press_apply_for_probe() main.debug_overlay.press_apply_for_probe()
# ④ 重置(重开面板 → 重置按钮真实路径;面板百分比归零——下一场战斗生效值回到基线) # ④ 重置(重开浮窗 → 重置按钮真实路径;面板百分比归零——下一场战斗生效值回到基线)
main._show_tune_panel() main._show_debug_overlay()
main.tune_panel.press_reset_for_probe() main.debug_overlay.press_reset_for_probe()
# ⑤ 批量模拟全链路(N=10——走 level_autoplay 与 headless 扫描同口径;表基线由组合根复位) # ⑤ 批量模拟全链路(N=10——走 level_autoplay 与 headless 扫描同口径;表基线由组合根复位)
main.tune_panel.set_sliders(100, 0) main.debug_overlay.set_sliders(100, 0)
main.tune_panel.set_sim_count(10) main.debug_overlay.set_sim_count(10)
main.tune_panel.press_simulate_for_probe() main.debug_overlay.press_simulate_for_probe()
var guard := SIM_GUARD_FRAMES var guard := SIM_GUARD_FRAMES
while main.tune_panel.is_running() and guard > 0: while main.debug_overlay.is_running() and guard > 0:
guard -= 1 guard -= 1
await get_tree().process_frame await get_tree().process_frame
if main.tune_panel.is_running(): if main.debug_overlay.is_running():
problems.append("模拟未在预算帧内完成(疑似挂死)") problems.append("模拟未在预算帧内完成(疑似挂死)")
var out: String = main.tune_panel.output_text() var out: String = main.debug_overlay.output_text()
if not out.contains("通关率"): if not out.contains("通关率"):
problems.append("模拟输出缺通关率") problems.append("模拟输出缺通关率")
if not out.contains("败北节点"): if not out.contains("败北节点"):
problems.append("模拟输出缺败北节点") problems.append("模拟输出缺败北节点")
if "N=10" not in out: if "N=10" not in out:
problems.append("模拟输出 N 标注错误(%s)" % out.substr(0, 40)) problems.append("模拟输出 N 标注错误(%s)" % out.substr(0, 40))
# ⑥ 关闭 → 回开始界面 # ⑥ 关闭 → 浮窗收起、开始层全程在屏
main._close_tune_panel() main._close_debug_overlay()
if main.tune_panel.visible or not main._start_overlay.visible: if main.debug_overlay.visible or not main._start_overlay.visible:
problems.append("关闭后层级状态错误") problems.append("关闭后层级状态错误")
# ⑦ hero 表运行期不可变(E4 收口回归断言:任何代码 reintroduce 表写在此被抓获——原"表值残留"断言语义升级) # ⑦ hero 表运行期不可变(E4 收口回归断言:任何代码 reintroduce 表写在此被抓获——原"表值残留"断言语义升级)
if int(main.db.hero.atk) != base_atk or int(main.db.hero.max_hp) != base_hp: if int(main.db.hero.atk) != base_atk or int(main.db.hero.max_hp) != base_hp:
@@ -66,8 +70,8 @@ func _ready() -> void:
# 存档隔离(迭代 8 卡①——停点断点写盘随 main.save_manager;探针禁碰真实档,同族探针隔离纪律; # 存档隔离(迭代 8 卡①——停点断点写盘随 main.save_manager;探针禁碰真实档,同族探针隔离纪律;
# isolate_save 先删后建——上次运行的残留断点会触发互斥确认早退) # isolate_save 先删后建——上次运行的残留断点会触发互斥确认早退)
ProbeUiUtil.isolate_save(main, "smoke_tune_probe_save", true, false, false) ProbeUiUtil.isolate_save(main, "smoke_tune_probe_save", true, false, false)
main.tune_panel.set_sliders(100, 50) main.debug_overlay.set_sliders(100, 50)
main.tune_panel.press_apply_for_probe() main.debug_overlay.press_apply_for_probe()
main._start_adventure_from_menu() # 完整冒险路径(唯一试玩入口;_start_single 按语义清零滑条 = 恒基线,不承载加成断言) main._start_adventure_from_menu() # 完整冒险路径(唯一试玩入口;_start_single 按语义清零滑条 = 恒基线,不承载加成断言)
await get_tree().process_frame await get_tree().process_frame
await get_tree().process_frame await get_tree().process_frame