iter47-卡3:卡⑤(迭代47·Esc浮窗与营地顶栏批): Esc浮窗四态路由+CampHudBand顶栏栏+设置语境分支+断言批+审计补差+master取用+camp.md v2.3定稿吸收(半透明遮罩0.6场景根留底/Esc先关窗Q1甲换代)——终态六枚落满
This commit is contained in:
1 parent
bbeeea77e0
commit
6208a9cd1a
37 files changed
+1117
-107
No files matched your search
@@ -42,6 +42,7 @@ func open_camp() -> void:
|
||||
meta.refresh_treasure_unlocks() # 条件解锁刷新
|
||||
start_overlay.visible = false
|
||||
_open_modal.call(views.camp_scene) # 模态栈单口(场景根开——选档层被栈遮挡挂起)
|
||||
views.refresh_bus.refresh(camp_data()) # 进档动线尾显式刷新一闸(卡⑤ 契约 5 时机②——HUD 读数重读)
|
||||
_refresh_presentations.call()
|
||||
|
||||
|
||||
|
||||
@@ -528,6 +528,25 @@ func _on_camp_object_pressed(key: StringName) -> void:
|
||||
MainOverlayFlow._on_camp_object_pressed(self, key) # 场景点选路由(卡④ T3——薄委托,实现归覆盖层流程承接件)
|
||||
|
||||
|
||||
# Esc 输入入口(产品码首个键盘入口——卡⑤ T1 契约 9 接线定稿:事件驱动 _unhandled_input、
|
||||
# KEY_ESCAPE 键值判定配 physical_keycode、pressed 且非 echo、禁 Input 单例轮询、不挂 ui_cancel)
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event is InputEventKey and event.pressed and not event.echo and (event.keycode == KEY_ESCAPE or event.physical_keycode == KEY_ESCAPE):
|
||||
MainEscFlow.on_esc_input(self)
|
||||
|
||||
|
||||
func _on_esc_return() -> void:
|
||||
MainEscFlow._on_return_to_menu(self)
|
||||
|
||||
|
||||
func _on_esc_settings() -> void:
|
||||
MainEscFlow._on_settings(self)
|
||||
|
||||
|
||||
func _on_esc_quit() -> void:
|
||||
MainEscFlow._on_quit(self)
|
||||
|
||||
|
||||
func _camp_data() -> Dictionary:
|
||||
return MainOverlayFlow._camp_data(self)
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
## 组合根 Esc 流程承接件(产品码首个键盘输入入口——main._unhandled_input 同名薄委托落地):
|
||||
## Esc 四态路由(契约 9 判据单点=ModalStack 登记册现开集合,禁手枚名单)+三出口动线。
|
||||
## 静态承接件零成员态(仿 main_overlay_flow 形态——main 侧留同名薄委托,探针 main.X 路径零改动)。
|
||||
class_name MainEscFlow
|
||||
extends RefCounted
|
||||
|
||||
|
||||
## 营地语境判据(Q1 甲——场景根 visible;子界面开态抑制由契约 9① 现开集合承载,不并入本谓词)
|
||||
static func is_camp_context(root: Variant) -> bool:
|
||||
return root.ui.modals.camp.scene != null and root.ui.modals.camp.scene.visible
|
||||
|
||||
|
||||
## Esc 优先序(camp.md v2.3 定稿 2026-11-01 用户裁——有功能窗开着→先关最上层窗,覆盖卡⑤ 契约 9①
|
||||
## 的 Q1 甲无反馈;设置开在浮窗之上仍无反馈=契约 3 保留):
|
||||
## ①功能窗开着→close 最上层窗→①'设置开在浮窗之上→无反馈→②浮窗开→close(Q2 同键单义)→
|
||||
## ③营地语境→open→④非营地→无反馈
|
||||
static func on_esc_input(root: Variant) -> void:
|
||||
var esc: Control = root.ui.modals.camp.esc_overlay
|
||||
var window := _topmost_camp_window(root)
|
||||
if window != null:
|
||||
root.ui.modals.flow.close(window) # ① 先关最上层功能窗(v2.3 定稿)
|
||||
return
|
||||
if root.ui.modals.settings_overlay.visible:
|
||||
return # ①' 设置开在浮窗之上 → 无反馈(浮窗不隔层关闭——契约 3)
|
||||
if esc.visible:
|
||||
root.ui.modals.flow.close(esc) # ② 再按 Esc 关窗
|
||||
return
|
||||
if is_camp_context(root):
|
||||
root.ui.modals.flow.open(esc) # ③ 营地语境唤起
|
||||
# ④ 非营地语境 → 无反馈
|
||||
|
||||
|
||||
# 五功能窗现开查询(camp.md v2.3 五窗互斥单开——模态栈深恒 1,至多一窗在开;生涯页入窗集)
|
||||
static func _topmost_camp_window(root: Variant) -> Control:
|
||||
for w: Control in [root.ui.modals.camp.forge, root.ui.modals.camp.treasure, root.ui.modals.camp.tree,
|
||||
root.ui.modals.camp.codex, root.ui.modals.career_overlay]:
|
||||
if w != null and w.visible:
|
||||
return w
|
||||
return null
|
||||
|
||||
|
||||
|
||||
## 返回主菜单动线(契约 4):首步 close 浮窗(缺此步浮窗残留主菜单上方)→营地与选档层同收+开始层恢复
|
||||
static func _on_return_to_menu(root: Variant) -> void:
|
||||
root.ui.modals.flow.close(root.ui.modals.camp.esc_overlay)
|
||||
root._camp_pres.on_camp_closed() # 关层收闭单口(卡④ T3——营地层与选档层同收+开始层恢复)
|
||||
|
||||
|
||||
## 设置出口(浮窗登记在设置层之下——open 只隐上方层,浮窗由树序遮挡非属性隐藏;关后重现=栈语义)
|
||||
static func _on_settings(root: Variant) -> void:
|
||||
root.ui.modals.flow.open(root.ui.modals.settings_overlay)
|
||||
|
||||
|
||||
## 退出出口(_quit_game 原样复用——settings_store.save_settings+quit,零语义副作用)
|
||||
static func _on_quit(root: Variant) -> void:
|
||||
root._quit_game()
|
||||
@@ -0,0 +1 @@
|
||||
uid://caa868un7dnt4
|
||||
@@ -10,7 +10,11 @@ static func _open_settings(root: Variant) -> void:
|
||||
|
||||
static func _on_settings_closed(root: Variant) -> void:
|
||||
root.ui.modals.flow.close(root.ui.modals.settings_overlay) # 模态栈单口
|
||||
root.ui.screens.start_overlay.visible = true
|
||||
# 语境分支(卡⑤ 契约 3——谓词=camp_esc_overlay.visible,谁开的设置谁收):
|
||||
# 营地浮窗路径关设置回浮窗不回主菜单(栈语义:浮窗登记在下、由树序遮挡非属性隐藏);
|
||||
# 菜单路径保留开始层恢复硬副作用。
|
||||
if not root.ui.modals.camp.esc_overlay.visible:
|
||||
root.ui.screens.start_overlay.visible = true
|
||||
|
||||
|
||||
# ---- 局内状态面板·只读(入口角标在两屏装配段;开合与数据喂入归组合根)----
|
||||
|
||||
@@ -110,6 +110,15 @@ static func _build_overlays(root: Variant) -> void:
|
||||
var codex := CodexView.new()
|
||||
root.add_child(codex)
|
||||
root.ui.modals.camp.codex = codex
|
||||
# Esc 浮窗(卡⑤——登记于设置层之前的模态;三钮意图经 MainEscFlow 路由)
|
||||
var esc_overlay := CampEscOverlay.new()
|
||||
esc_overlay.setup_buttons()
|
||||
esc_overlay.bind_signals()
|
||||
root.add_child(esc_overlay)
|
||||
root.ui.modals.camp.esc_overlay = esc_overlay
|
||||
# 营地顶栏 HUD 栏(卡⑤——钥石余额常驻;挂卡④场景根顶栏预留位+入刷新单口目标册)
|
||||
var hud_band := CampHudBand.new()
|
||||
root.ui.modals.camp.scene.set_hud_band(hud_band)
|
||||
# 屏壳/子界面插位由 LAYER_CONTRACT 槽统一落位——禁散落 move_child
|
||||
root.ui.modals.legend_overlay.closed.connect(root.ui.modals.flow.close.bind(root.ui.modals.legend_overlay))
|
||||
root.ui.modals.node_overlay.continue_pressed.connect(root._on_node_continue)
|
||||
@@ -178,7 +187,8 @@ static func _build_menu_wiring(root: Variant) -> void:
|
||||
# 营地刷新单口(各写口尾→现开界面+场景根 HUD 一闸完成——卡④ T3;卡⑤ HUD band 随卡⑤挂册)
|
||||
var camp_bus := CampRefreshBus.new()
|
||||
camp_bus.bind_targets([root.ui.modals.camp.forge, root.ui.modals.camp.treasure,
|
||||
root.ui.modals.camp.tree, root.ui.modals.camp.codex] as Array[Control])
|
||||
root.ui.modals.camp.tree, root.ui.modals.camp.codex,
|
||||
root.ui.screens.camp_screen.camp_scene.hud_band] as Array[Control])
|
||||
root._camp_pres = CampShopPresenter.new(CampFlowContext.new(root.db,
|
||||
{"camp_scene": root.ui.modals.camp.scene, "forge_view": root.ui.modals.camp.forge,
|
||||
"treasure_view": root.ui.modals.camp.treasure, "tree_view": root.ui.modals.camp.tree,
|
||||
@@ -226,6 +236,10 @@ static func _build_debug_and_resolvers(root: Variant) -> void:
|
||||
root.ui.modals.confirm_dialog.confirmed.connect(root._on_confirm_accepted)
|
||||
root.ui.modals.confirm_dialog.secondary_confirmed.connect(root._on_confirm_secondary) # 三钮次确认(卡④ 大门「开始新冒险」出口)
|
||||
root.ui.modals.confirm_dialog.cancelled.connect(root._on_confirm_declined)
|
||||
# Esc 浮窗三出口接线(卡⑤——意图信号经薄委托归 MainEscFlow 动线;输入入口=main._unhandled_input)
|
||||
root.ui.modals.camp.esc_overlay.return_requested.connect(root._on_esc_return)
|
||||
root.ui.modals.camp.esc_overlay.settings_requested.connect(root._on_esc_settings)
|
||||
root.ui.modals.camp.esc_overlay.quit_requested.connect(root._on_esc_quit)
|
||||
|
||||
# 调试浮窗(主菜单三散件收一:随机种子/数值调整/批量模拟/两层调试入口;
|
||||
# 全局可开浮窗:调试会话恒开=启动即开〔_ready〕+两屏角标再开+当局数据只读区块;
|
||||
|
||||
@@ -9,3 +9,4 @@ var forge: ForgeView # 锻造工坊子界面
|
||||
var treasure: TreasureView # 秘宝匣子界面
|
||||
var tree: TreeView # 天赋树子界面
|
||||
var codex: CodexView # 奖励格图鉴子界面(占位挂起态)
|
||||
var esc_overlay: CampEscOverlay # Esc 浮窗(卡⑤——终态六枚落满)
|
||||
@@ -27,6 +27,7 @@ const LAYER_CONTRACT: Array[Dictionary] = [
|
||||
{"key": "treasure_view", "kind": "modal", "tags": []},
|
||||
{"key": "tree_view", "kind": "modal", "tags": []},
|
||||
{"key": "codex_view", "kind": "modal", "tags": []},
|
||||
{"key": "camp_esc_overlay", "kind": "modal", "tags": []}, # Esc 浮窗(卡⑤——登记于营地层与设置层之间,契约 3 栈语义正解)
|
||||
{"key": "settings_overlay", "kind": "modal", "tags": []},
|
||||
{"key": "status_panel", "kind": "modal", "tags": []},
|
||||
{"key": "career_overlay", "kind": "modal", "tags": []},
|
||||
@@ -67,6 +68,7 @@ func node(key: String) -> Node:
|
||||
"treasure_view": return modals.camp.treasure
|
||||
"tree_view": return modals.camp.tree
|
||||
"codex_view": return modals.camp.codex
|
||||
"camp_esc_overlay": return modals.camp.esc_overlay
|
||||
"settings_overlay": return modals.settings_overlay
|
||||
"status_panel": return modals.status_panel
|
||||
"career_overlay": return modals.career_overlay
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
## Esc 浮窗视图(营地语境 Esc 唤起):返回主菜单/设置/退出游戏三钮(裁决 9——三项且仅三项)。
|
||||
## 壳=UiKit.modal_shell(居中模态壳,ui_kit 工厂单点);退出钮=UiKit.danger_button;零新素材零新色值。
|
||||
## Q2 再按 Esc 关窗=组合根 Esc 路由直呼 flow.close(本视图无自关通路);三钮 pressed 外发意图不内含动线。
|
||||
## 零动效(契约 6——直开直关,子树零 AnimationPlayer、源码零动效令牌)。
|
||||
class_name CampEscOverlay
|
||||
extends Control
|
||||
|
||||
signal return_requested ## 返回主菜单意图(动线归 MainEscFlow._on_return_to_menu——首步 close 浮窗)
|
||||
signal settings_requested ## 设置意图(开设置层——浮窗登记在其下,栈语义关后重现)
|
||||
signal quit_requested ## 退出游戏意图(_quit_game 原样复用)
|
||||
|
||||
var _return_btn: Button
|
||||
var _settings_btn: Button
|
||||
var _quit_btn: Button
|
||||
|
||||
|
||||
func setup_buttons() -> void:
|
||||
var box: VBoxContainer = UiKit.modal_shell(self, Color(UiKit.DIM, 0.55), Vector2(360, 0), 12,
|
||||
UiKit.panel_skinned(Vector4(14, 12, 14, 12))).box
|
||||
_return_btn = UiKit.button("返回主菜单")
|
||||
box.add_child(_return_btn)
|
||||
_settings_btn = UiKit.button("设置")
|
||||
box.add_child(_settings_btn)
|
||||
_quit_btn = UiKit.danger_button("退出游戏")
|
||||
box.add_child(_quit_btn)
|
||||
visible = false # 登记模态初始隐(开合走栈——模态纪律)
|
||||
|
||||
|
||||
func bind_signals() -> void:
|
||||
_return_btn.pressed.connect(func() -> void: return_requested.emit())
|
||||
_settings_btn.pressed.connect(func() -> void: settings_requested.emit())
|
||||
_quit_btn.pressed.connect(func() -> void: quit_requested.emit())
|
||||
@@ -0,0 +1 @@
|
||||
uid://col25elh1utw5
|
||||
@@ -0,0 +1,27 @@
|
||||
## 营地顶栏小 HUD 栏(裁决 10——钥石余额常驻,最小集形态):承载 UiKit.keystone_badge(工厂单点)。
|
||||
## 读数只经 camp_data() 投影单口(禁第二账本、禁视图自算);刷新挂卡④ CampRefreshBus 单口
|
||||
## (bus 目标册成员——显式触发才变,禁 _ready 自刷/自持第二入口,契约 5);载体=PRESET_TOP_WIDE 48px 浮层。
|
||||
class_name CampHudBand
|
||||
extends Control
|
||||
|
||||
var _badge: PanelContainer
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
set_anchors_preset(Control.PRESET_TOP_WIDE)
|
||||
custom_minimum_size = Vector2(0, 48) # 高 48px 浮层覆盖、不挤压画面(camp.md §顶栏)
|
||||
mouse_filter = Control.MOUSE_FILTER_IGNORE # HUD 栏不挡场景命中区(浮层覆盖语义)
|
||||
_badge = UiKit.keystone_badge("钥石 0")
|
||||
_badge.set_anchors_preset(Control.PRESET_CENTER_RIGHT)
|
||||
_badge.position = Vector2(-160, 8) # 顶栏右位次(UiKit 现行件域内——代拍板 3)
|
||||
add_child(_badge)
|
||||
|
||||
|
||||
## 余额写入(格式串=既有消费面同款「钥石 %d」——badge 工厂约定单点,禁第二格式串)
|
||||
func set_balance(v: int) -> void:
|
||||
_badge.value_label.text = "钥石 %d" % v
|
||||
|
||||
|
||||
## 刷新单口落点(CampRefreshBus 目标册成员——显式触发才变,契约 5 刷新时机①②③同一闸)
|
||||
func refresh_camp(camp_data: Dictionary) -> void:
|
||||
set_balance(int(camp_data.get("balance", 0)))
|
||||
@@ -0,0 +1 @@
|
||||
uid://by6ugjfdicgud
|
||||
@@ -46,6 +46,7 @@ var _press_key := &"" # 当前按住命中键
|
||||
var _float_tween: Tween # 悬停浮动补间(逐级整像素吸附,禁 _process 逐帧移 UI)
|
||||
var _desat_mat: ShaderMaterial # 禁用态共享材质(strength 恒 1,材质挂/摘承载开关)
|
||||
var _hud_slot: Control # 顶栏预留位(卡⑤ set_hud_band 填值)
|
||||
var hud_band: Control # 顶栏 HUD 栏句柄(卡⑤ CampHudBand——装配后 bus 目标册取用面)
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
@@ -94,6 +95,7 @@ func set_feedback_state(key: StringName, state: FeedbackState) -> void:
|
||||
func set_hud_band(band: Control) -> void:
|
||||
if _hud_slot != null:
|
||||
_hud_slot.add_child(band)
|
||||
hud_band = band
|
||||
|
||||
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
|
||||
@@ -12,12 +12,8 @@ var _balance_label: Label # 钥石徽数值行(探针直读面——ui_probe
|
||||
|
||||
## 壳构建(子类 _init 调用一次):content 由子类先建好传入(列容器/滚动容器皆可)
|
||||
func _build_shell(title: String, icon_path: String, content: Control) -> void:
|
||||
# 实底暖亮档(半透 dim 会透出底下场景图——CampOverlay 同案;遮蔽语义由组合根输入锁兜底)
|
||||
var dim := UiKit._modal_dim(self, UiKit.BG_BRIGHT)
|
||||
var bd := UiKit.backdrop_tile()
|
||||
if bd != null:
|
||||
dim.add_child(bd)
|
||||
dim.add_child(UiKit.vignette_rect())
|
||||
# 半透明压暗遮罩 0.6(camp.md v2.3 定稿——场景根留底可见,窗口之下垫遮罩;交互聚焦由遮罩 STOP 兜底)
|
||||
var dim := UiKit._modal_dim(self, Color(UiKit.DIM, 0.6))
|
||||
var margin := MarginContainer.new()
|
||||
margin.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
margin.add_theme_constant_override("margin_left", 16)
|
||||
|
||||
@@ -18,6 +18,7 @@ var _keystone_badge: UiKit.KeystoneBadge # 钥石余额(badge 工厂件—
|
||||
func _init() -> void:
|
||||
# 全屏页壳(T6/T7 对齐批先例——暖亮底+石纹弱质感+暗角+56px 通栏顶条+内容列)
|
||||
var page: Dictionary[Variant, Variant] = UiKit.page_shell(self, true)
|
||||
page.base.color = Color(UiKit.DIM, 0.6) # 功能窗口化(camp.md v2.3 定稿——场景根留底+半透明压暗遮罩 0.6;观感随样张目检校准)
|
||||
var top: HBoxContainer = page.top_row
|
||||
top.add_child(UiKit.label("生 涯",UiKit.FS_TITLE, true, UiKit.WARM))
|
||||
var top_sp := Control.new()
|
||||
|
||||
@@ -27,6 +27,7 @@ func _ready() -> void:
|
||||
await _step_boot_menu()
|
||||
await _step_menu_to_camp()
|
||||
await _step_camp_back_to_menu()
|
||||
await _step_camp_esc_and_hud() # 卡⑤——Esc 四态路由/三出口/设置语境分支/HUD 读数
|
||||
await _step_save_select_open_back() # 迭代 33 卡③ sl10——选档开合+返回路径+ESC 语境不响应
|
||||
await _step_menu_to_adventure()
|
||||
await _step_level_to_battle_and_back()
|
||||
@@ -122,7 +123,9 @@ func _step_camp_back_to_menu() -> void:
|
||||
return
|
||||
main.ui.modals.camp.scene.object_pressed.emit(&"campfire")
|
||||
await _frames()
|
||||
main.ui.modals.confirm_dialog.confirmed.emit() # 二次确认离场(回主菜单——卡④ T4)
|
||||
if not main.ui.modals.confirm_dialog.visible:
|
||||
_problems.append("③篝火:二次确认弹窗未现(camp.md v2.2 必须先确认)〔营地图批 V7〕")
|
||||
main.ui.modals.confirm_dialog.confirmed.emit() # 二次确认离场(confirmed 先于关层——卡④ T4)
|
||||
await _frames()
|
||||
_expect_exclusive("③营地→菜单(回退)", "菜单")
|
||||
|
||||
@@ -229,3 +232,73 @@ func _step_end_back_to_menu() -> void:
|
||||
_expect_exclusive("⑦终局→菜单", "菜单")
|
||||
if main.run_scope != null and main.run_scope.is_adventure:
|
||||
_problems.append("⑦终局→菜单:冒险态未收束(run_scope 仍标 is_adventure)")
|
||||
|
||||
|
||||
# ---- ③' 营地 Esc 浮窗+HUD(卡⑤——真实键盘合成路径 push_input;四态路由+三出口+语境分支+余额单口)----
|
||||
func _step_camp_esc_and_hud() -> void:
|
||||
ProbeUiUtil.isolate_save(main, "smoke_screen_flow_esc", false, false, true)
|
||||
if not ProbeUiUtil.open_camp_via_savesel(main, "smoke_screen_flow_esc"):
|
||||
_problems.append("[esc] 营地入口缺失(选档层新档钮驱动中断)〔ESC批 V1〕")
|
||||
return
|
||||
await _frames()
|
||||
_push_esc()
|
||||
await _frames()
|
||||
if not main.ui.modals.camp.esc_overlay.visible:
|
||||
_problems.append("[esc] 营地根态 Esc 未唤起浮窗(契约 9③)〔ESC批 V1〕〔ESC批 B1〕")
|
||||
_push_esc()
|
||||
await _frames()
|
||||
if main.ui.modals.camp.esc_overlay.visible:
|
||||
_problems.append("[esc] 再按 Esc 未关浮窗(Q2 同键单义切换失守)〔ESC批 V1〕")
|
||||
_push_esc(true) # echo 连发(真实键盘长按形态)
|
||||
await _frames()
|
||||
if main.ui.modals.camp.esc_overlay.visible:
|
||||
_problems.append("[esc] echo 事件唤起浮窗(触发沿 pressed 且非 echo 失守)〔ESC批 V1〕")
|
||||
main.ui.modals.camp.scene.object_pressed.emit(&"dummy") # 子界面开态
|
||||
await _frames()
|
||||
_push_esc()
|
||||
await _frames()
|
||||
if main.ui.modals.camp.tree.visible:
|
||||
_problems.append("[esc] 子界面开态 Esc 未关窗(camp.md v2.3 定稿——先关最上层窗)〔ESC批 V1〕")
|
||||
if main.ui.modals.camp.esc_overlay.visible:
|
||||
_problems.append("[esc] 关窗同拍唤起浮窗(先关窗后浮窗——两拍应分离)〔ESC批 V1〕")
|
||||
await _frames()
|
||||
# HUD 余额(契约 5 时机③——夹具直写后显式触发才变;禁 band 自刷假设)
|
||||
main.meta.meta_state._add_keystone(9999)
|
||||
main._camp_pres.views.refresh_bus.refresh(main._camp_data()) # 显式触发刷新单口
|
||||
var band: Control = main.ui.modals.camp.scene.get_node("HudBandSlot").get_child(0)
|
||||
if not String(band._badge.value_label.text).begins_with("钥石 9999"):
|
||||
_problems.append("[esc] HUD 余额读数≠存档单口现值(直写+显式触发未变)〔ESC批 V6〕〔ESC批 B2〕")
|
||||
# 设置开在浮窗之上→Esc 无反馈且浮窗不误关(契约 9①);关设置回浮窗不回主菜单(契约 3 语境分支)
|
||||
_push_esc()
|
||||
await _frames()
|
||||
main._on_esc_settings()
|
||||
await _frames()
|
||||
if not main.ui.modals.settings_overlay.visible:
|
||||
_problems.append("[esc] 浮窗设置出口断裂〔ESC批 V4〕")
|
||||
_push_esc()
|
||||
await _frames()
|
||||
if not main.ui.modals.camp.esc_overlay.visible:
|
||||
_problems.append("[esc] 设置开态 Esc 误关浮窗(契约 9①——浮窗不隔层关闭)〔ESC批 V1〕")
|
||||
main._on_settings_closed()
|
||||
await _frames()
|
||||
if not main.ui.modals.camp.esc_overlay.visible or main.ui.screens.start_overlay.visible:
|
||||
_problems.append("[esc] 关设置后未回浮窗或回弹主菜单(契约 3 语境分支失守)〔ESC批 V4〕〔ESC批 B3〕")
|
||||
# 返回主菜单动线(契约 4——浮窗自收+营地隐+选档收+菜单显)
|
||||
main.ui.modals.camp.esc_overlay.return_requested.emit()
|
||||
await _frames()
|
||||
if main.ui.modals.camp.esc_overlay.visible or main.ui.modals.camp.scene.visible or main.ui.modals.save_select_overlay.visible or not main.ui.screens.start_overlay.visible:
|
||||
_problems.append("[esc] 返回主菜单动线失守(浮窗自收/营地隐/选档收/菜单显)〔ESC批 V3〕〔ESC批 B3〕")
|
||||
# 非营地语境(boot 菜单)Esc→无响应(契约 9④ 回归面)
|
||||
_push_esc()
|
||||
await _frames()
|
||||
if main.ui.modals.camp.esc_overlay.visible:
|
||||
_problems.append("[esc] 非营地语境 Esc 唤起浮窗(契约 9④ 失守)〔ESC批 V1〕")
|
||||
|
||||
|
||||
func _push_esc(echo := false) -> void:
|
||||
var ev := InputEventKey.new()
|
||||
ev.keycode = KEY_ESCAPE
|
||||
ev.physical_keycode = KEY_ESCAPE
|
||||
ev.pressed = true
|
||||
ev.echo = echo
|
||||
main.get_viewport().push_input(ev, true) # 真实键盘合成路径(:153 先例——push_input 不更新 Input 单例动作态)
|
||||
@@ -88,11 +88,11 @@ func _ready() -> void:
|
||||
buy_btn.pressed.emit() # 合成购买点击 → _on_camp_tree_buy → meta.tree_buy(写调用唯一归组合根)
|
||||
await get_tree().process_frame
|
||||
if int(iso_c.meta_state.keystone_balance()) != bal_before - price:
|
||||
_problems.append("营地树购买未出账(%d→%d 应 −%d)〔句柄子容器批 V1〕" % [bal_before, iso_c.meta_state.keystone_balance(), price])
|
||||
_problems.append("营地树购买未出账(%d→%d 应 −%d)〔句柄子容器批 V1〕〔营地图批 V4〕" % [bal_before, iso_c.meta_state.keystone_balance(), price])
|
||||
var iso_chk = load("res://core/save_manager.gd").new("user://smoke_click_probe_camp_slot_1.json")
|
||||
iso_chk.load_from_disk()
|
||||
if int(iso_chk.meta_state.tree_state().nodes.get("sp_hp", 0)) != 1:
|
||||
_problems.append("营地树购买未持久(reload 后级 != 1)〔句柄子容器批 V1〕")
|
||||
_problems.append("营地树购买未持久(reload 后级 != 1)〔句柄子容器批 V1〕〔营地图批 V4〕")
|
||||
# Fork 双态切换点击路径(V20——购=解锁默认 A,钮切 B;前置=同调位 1,先点亮)
|
||||
var socket_btn: Button = null
|
||||
for card in main.ui.modals.camp.tree._tree_buy_btns:
|
||||
@@ -177,10 +177,10 @@ func _ready() -> void:
|
||||
_collect_labels_by_text(main.ui.modals.camp.codex, codex_labels)
|
||||
var atlas_locks := _labels_with_text(codex_labels, "结构位预留")
|
||||
if atlas_locks.is_empty():
|
||||
_problems.append("营地灰字:图鉴锁文 label 未找到(wc4 定位锚失守——锚随形态订正去『柜台』)〔句柄子容器批 V1〕")
|
||||
_problems.append("营地灰字:图鉴锁文 label 未找到(wc4 定位锚失守——锚随形态订正去『柜台』)〔句柄子容器批 V1〕〔营地图批 V6〕")
|
||||
for l in atlas_locks:
|
||||
if l.get_theme_color("font_color") != UiKit.STONE_DEEP:
|
||||
_problems.append("营地灰字:图鉴锁文未用 STONE_DEEP(wc4 :167)〔句柄子容器批 V1〕")
|
||||
_problems.append("营地灰字:图鉴锁文未用 STONE_DEEP(wc4 :167)〔句柄子容器批 V1〕〔营地图批 V6〕")
|
||||
main.ui.modals.camp.codex.closed.emit()
|
||||
await get_tree().process_frame
|
||||
main._on_camp_closed() # 关层收闭(营地与选档层同收+开始层恢复——卡④ T3)
|
||||
@@ -702,7 +702,7 @@ func _ready() -> void:
|
||||
main.ui.modals.confirm_dialog.confirmed.emit() # 「是」→开始新冒险
|
||||
await get_tree().process_frame
|
||||
if not main.run_scope.is_adventure or main.ui.modals.camp.scene.visible:
|
||||
_problems.append("大门确认:冒险未发起或营地场景屏未隐藏〔营地图批 V3〕")
|
||||
_problems.append("大门确认:冒险未发起或营地场景屏未隐藏〔营地图批 V3〕〔营地图批 B1〕")
|
||||
# 有断点三出口(选路停点形注入——同开场行③ set_breakpoint 先例)
|
||||
main.save_manager.set_breakpoint({
|
||||
"master_seed": 42, "rng_counter": 0, "hero_hp": 30, "gp": 10, "keystone": 0,
|
||||
|
||||
@@ -104,6 +104,51 @@ func _ready() -> void:
|
||||
_settle()
|
||||
if main.ui.modals.career_overlay.visible or main.ui.screens.start_overlay.visible:
|
||||
problems.append("[career] 关闭后页未隐藏或开始层回弹(营地语境分支失守)〔营地图批 V5〕")
|
||||
# ---- Esc 浮窗结构段(卡⑤——三钮在位且仅三+零动效契约+登记序在册+debug_badge 不波及)----
|
||||
main._camp_pres.open_camp()
|
||||
_settle()
|
||||
main.ui.modals.camp.scene.object_pressed.emit(&"gate")
|
||||
_settle()
|
||||
main.ui.modals.confirm_dialog.cancelled.emit() # 关大门弹窗(清场——浮窗段前提)
|
||||
_settle()
|
||||
var push_esc := func() -> void:
|
||||
var ev := InputEventKey.new()
|
||||
ev.keycode = KEY_ESCAPE
|
||||
ev.physical_keycode = KEY_ESCAPE
|
||||
ev.pressed = true
|
||||
main.get_viewport().push_input(ev, true)
|
||||
push_esc.call()
|
||||
_settle()
|
||||
if not main.ui.modals.camp.esc_overlay.visible:
|
||||
problems.append("[esc] 浮窗未唤起(结构段前提——营地语境 Esc 路由)〔ESC批 V1〕")
|
||||
var esc_btns := _buttons(main.ui.modals.camp.esc_overlay).size()
|
||||
if esc_btns != 3:
|
||||
problems.append("[esc] 浮窗三钮在位且仅三(实得 %d——禁第四钮)〔ESC批 V2〕" % esc_btns)
|
||||
var esc_static := FileAccess.get_file_as_string("res://presentation/views/camp_esc_overlay.gd") + FileAccess.get_file_as_string("res://presentation/main_esc_flow.gd")
|
||||
if esc_static.contains("create_tween") or esc_static.contains("UiKit.motion"):
|
||||
problems.append("[esc] 浮窗双件源码存在动效令牌(契约 6 零动效——直开直关)〔ESC批 V7〕")
|
||||
var esc_anim := _count_animplayers(main.ui.modals.camp.esc_overlay)
|
||||
if esc_anim != 0:
|
||||
problems.append("[esc] 浮窗子树 AnimationPlayer 非零(契约 6 零动效)〔ESC批 V7〕")
|
||||
var esc_src := FileAccess.get_file_as_string("res://presentation/views/camp_esc_overlay.gd") + FileAccess.get_file_as_string("res://presentation/main_esc_flow.gd")
|
||||
if esc_src.is_empty():
|
||||
problems.append("[esc] 零动效扫描面为空(源文件读失败——非空前置失守)〔ESC批 V7〕")
|
||||
# 退出项接线断言(卡⑤ V5——绑信号连接与调用链;headless 不可真退=静态判据+观测面局限申报)
|
||||
var quit_wired := false
|
||||
for c in main.ui.modals.camp.esc_overlay.quit_requested.get_connections():
|
||||
if c.callable.get_method() == "_on_esc_quit":
|
||||
quit_wired = true
|
||||
if not quit_wired:
|
||||
problems.append("[esc] 退出钮接线断链(quit_requested 连接目标≠_on_esc_quit)〔ESC批 V5〕")
|
||||
var quit_chain := FileAccess.get_file_as_string("res://presentation/main.gd") + FileAccess.get_file_as_string("res://presentation/main_esc_flow.gd")
|
||||
if not quit_chain.contains("MainEscFlow._on_quit(self)") or not quit_chain.contains("root._quit_game()"):
|
||||
problems.append("[esc] 退出调用链断裂(_on_esc_quit→_on_quit→_quit_game)〔ESC批 V5〕")
|
||||
if main.ui.screens.debug_badge.visible:
|
||||
problems.append("[esc] 浮窗开态 debug_badge 可见(发布态恒隐——float 类不注册 ModalStack 应零波及)〔ESC批 V8〕")
|
||||
push_esc.call()
|
||||
_settle()
|
||||
if main.ui.modals.camp.esc_overlay.visible:
|
||||
problems.append("[esc] 再按 Esc 未关浮窗(结构段收尾——Q2 单义切换)〔ESC批 V1〕")
|
||||
main._on_camp_closed() # 关营地复归菜单语境(后续选档段前提)
|
||||
_settle()
|
||||
|
||||
@@ -756,3 +801,13 @@ func _start_columns_contract(main: Node) -> void:
|
||||
if box.get_global_rect().intersects(right.get_global_rect()):
|
||||
problems.append("[start][columns] 左列 %s 与右列 %s 相交(双列叠放回归——M2 缺陷闸)" % [
|
||||
box.get_global_rect(), right.get_global_rect()])
|
||||
|
||||
|
||||
# AnimationPlayer 全子树收集(零动效契约扫描面——_buttons 同款递归形态)
|
||||
func _count_animplayers(node: Node) -> int:
|
||||
var out := 0
|
||||
if node is AnimationPlayer:
|
||||
out += 1
|
||||
for child in node.get_children():
|
||||
out += _count_animplayers(child)
|
||||
return out
|
||||
@@ -134,6 +134,7 @@ tests/
|
||||
| 地形瓦片套与格面数字可读 | HUD框架 | `TilePalette.digit_ink_mode()`/`TilePalette.normalize_floor_luma()` | `terrain_tile_sets_test.gd:_test_set_completeness`;`terrain_tile_sets_test.gd:_test_hue_domains`;`terrain_tile_sets_test.gd:_test_digit_ink_modes` |
|
||||
| 站桩敌人呈现与结算演出落点 | 战斗表现 | `BattleStageView.enemy_standee_center()`/`BattleEndOverlay.open_victory()` | `standee_fx_sweep_test.gd:_init`;`pacing_queue_test.gd:_init` |
|
||||
| 站桩呈现定值与素材绑定 | 战斗表现 | `UiKit.load_tex()`/`StandeeBody.name_line_probe()` | `standee_present_values_test.gd:_init` |
|
||||
| 站桩显示档与素材分辨率绑定 | 战斗表现 | `StandeeBody.portrait_probe()`/`StandeeMotion.PORTRAIT_DISPLAY_H` | `standee_present_values_test.gd:_check_display_tier` |
|
||||
| B 区舞台背景三关变体与连续地面层(按 board_type 选图/地面层高度读布局表/四柱光柱退役) | HUD框架 | `BattleStageView.set_board_type()` | `stage_background_test.gd:_test_background_selection`;`stage_background_test.gd:_test_ground_layer_geometry`;`stage_background_test.gd:_test_beams_retired` |
|
||||
| 战斗屏三区几何与棋盘格边长(分辨率两档×四档板型) | HUD框架 | `BoardView.set_cell_area()`(D 区可用面积 → 格边长);读面 `BoardView.cell_size` | `layout_probe.gd:_ready`;`board_gen_reward_test.gd:_smoke_layout_cell_formula` |
|
||||
| 调参测速批量模拟(逐帧步进与统计读数) | 调参测量 | `TuneSimController.run()`(调试浮窗批量模拟入口) | `tune_sim_batch_parity_test.gd:_init`(配对对拍套件,随测速循环批建) |
|
||||
@@ -162,6 +163,7 @@ tests/
|
||||
| 难度修饰套乘:空表零行为/套乘生效/越域 clamp | 难度曲线与Boss | `UnitContainer.spawn()`/`ConfigDbResolver.resolve_difficulty_modifiers()` | `table_structure_guard_test.gd:_smoke_difficulty_table`;`config_db_resolver_test.gd:_test_difficulty_clamp` |
|
||||
| 锻造表 schema 正反与几何价格梯 | 锻造工坊 | `ForgeTable.level_price()`/`ForgeTable.route_rows()` | `forge_table_test.gd:_test_production_rows`;`forge_table_test.gd:_test_price_ladder`;`forge_table_test.gd:_test_negative_cases` |
|
||||
| 系统间契约词表与受理口径:五配方互异/掷点标签成员性/增益布尔受理 | 战斗结算×数据配置×增益状态 | `DamageController.open_ticket()`/`StatusContainer.apply_effect()` | `system_contracts_test.gd:_test_damage_controller_vocabularies`;`system_contracts_test.gd:_test_rng_tag_closure`;`system_contracts_test.gd:_test_status_boolean_acceptance` |
|
||||
| 同名状态合并策略(叠加/累加/并存)与剩余次数语义 | 战斗循环×增益状态 | `StatusContainer.apply_effect()`(编排宿主 begin_battle 链) | `roll_multiplier_test.gd:_smoke_armor_roll` |
|
||||
| 敌人技能池跨表链接校验 | 数据配置×敌人系统 | `ConfigDbCrossValidator.validate_skill_pool_links()` | `config_db_cross_validator_test.gd:_test_all_links_valid`;`config_db_cross_validator_test.gd:_test_dangling_skill_id`;`config_db_cross_validator_test.gd:_test_cast_side_mismatch` |
|
||||
|
||||
**覆盖域表**(`design/gdd/systems/` 逐篇对照——状态三档:已覆盖/部分覆盖/未覆盖;部分与未覆盖行的缺口指待办清单条目,覆盖面提升时同批改本表):
|
||||
@@ -174,9 +176,9 @@ tests/
|
||||
| 冒险循环 | 奇物.md | 专长(奇物)授予与凭证保底;奖励三选一候选/刷新付费/呈现 | 已覆盖 | — |
|
||||
| 冒险循环 | 难度曲线与Boss.md | Boss 半血解锁技门控;行集寻址、行序保序与难度乘数 clamp;难度修饰套乘:空表零行为/套乘生效/越域 clamp | 部分覆盖 | 难度曲线整链与 Boss 出场门控无专项断言;见待办清单「能力清单覆盖缺口补齐」条 |
|
||||
| 战斗循环 | 地图战斗资源.md | 清图奖励双份与零路径;GP 钱包/escrow/商店收口/共享池;封印与极性路由/不可逆 | 已覆盖 | — |
|
||||
| 战斗循环 | 增益状态.md | 系统间契约词表与受理口径:五配方互异/掷点标签成员性/增益布尔受理;封印与极性路由/不可逆 | 部分覆盖 | 增益叠层/衰减/清除结算链无断言;见待办清单「能力清单覆盖缺口补齐」条 |
|
||||
| 战斗循环 | 增益状态.md | 系统间契约词表与受理口径:五配方互异/掷点标签成员性/增益布尔受理;同名状态合并策略(叠加/累加/并存)与剩余次数语义;封印与极性路由/不可逆 | 部分覆盖 | 增益叠层/衰减/清除结算链无断言;见待办清单「能力清单覆盖缺口补齐」条 |
|
||||
| 战斗循环 | 战斗结算.md | 伤害公式与掷点乘区;伤害单据编排与回放;轮内流与结算五段全序 | 已覆盖 | — |
|
||||
| 战斗循环 | 战斗表现.md | 站桩敌人呈现与结算演出落点;站桩呈现定值与素材绑定 | 部分覆盖 | 死亡灰化与减动效演出档仅探针与人工目检;见待办清单「能力清单覆盖缺口补齐」条 |
|
||||
| 战斗循环 | 战斗表现.md | 站桩敌人呈现与结算演出落点;站桩呈现定值与素材绑定;站桩显示档与素材分辨率绑定 | 部分覆盖 | 死亡灰化与减动效演出档仅探针与人工目检;见待办清单「能力清单覆盖缺口补齐」条 |
|
||||
| 战斗循环 | 敌人系统.md | 技能池数据面与回放独立;Boss 半血解锁技门控;敌人技能池跨表链接校验 | 已覆盖 | — |
|
||||
| 排雷循环 | 地形变异.md | 变异布置门控;表结构守卫:域校验/行序键/跨字段互锁/配额锚 | 已覆盖 | — |
|
||||
| 排雷循环 | 排雷玩法.md | 装板与开局免费展开(封锁格无资源、计入清图);挖/封印合法性与可辨拒绝(拒绝零副作用);封印不可逆与挖开封印格放行(兑付改判四情况) | 已覆盖 | — |
|
||||
|
||||
Reference in new issue
Block a user