From 9468c70ee2720da79e3fdfc765abf895c81345f4 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Sat, 24 Oct 2026 07:42:46 +0800 Subject: [PATCH 1/5] =?UTF-8?q?iter(36):=20=E5=8D=A1C+=E5=8D=A1A=20?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E2=80=94=E2=80=94=E5=BC=80=E5=9C=BA=E8=A1=8C?= =?UTF-8?q?=E5=90=8E=E7=BC=80=E8=AE=A4=E5=8A=A0=E7=AE=97=E5=8C=BA=EF=BC=88?= =?UTF-8?q?=E5=85=AD=E9=A1=B9=E5=B8=83=E5=B0=94=EF=BC=8B=E4=B8=89=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E6=96=AD=E8=A8=80=EF=BC=9A=E7=99=BD=E6=9D=BF=E5=8F=8D?= =?UTF-8?q?=E5=90=91/=E7=BB=AD=E7=8E=A9=E9=87=8D=E5=BB=BA=E6=AD=A3?= =?UTF-8?q?=E5=90=91=EF=BC=8C=E5=8D=A1=E7=89=87=E2=91=A1=E5=A5=96=E5=8A=B1?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E5=89=8D=E6=8F=90=E7=BC=BA=E9=99=B7=E6=94=B9?= =?UTF-8?q?=E5=BF=AB=E7=85=A7=E5=96=82=E5=85=A5=E5=BD=A2=E6=80=81=E4=BB=A3?= =?UTF-8?q?=E6=8B=8D2=EF=BC=89=E3=80=81=E8=90=A5=E5=9C=B0=E5=85=AD?= =?UTF-8?q?=E5=A4=84=E7=81=B0=E5=AD=97STONE=5FDEEP=EF=BC=8B=E5=8F=96?= =?UTF-8?q?=E8=89=B2/=E6=BA=90=E6=89=AB=E6=8F=8F=E6=96=AD=E8=A8=80?= =?UTF-8?q?=E3=80=81=E8=BF=B7=E9=9B=BE=E6=B5=8B=E7=82=B9=E4=B8=89=E5=85=B3?= =?UTF-8?q?=E5=BD=92=E4=B8=80=E5=90=8E=E5=9F=BA=E5=87=86=EF=BC=8B=E6=B4=BE?= =?UTF-8?q?=E7=94=9F=E6=A0=A1=E9=AA=8C=EF=BC=88T0=E5=AF=B9=E6=8B=8DL2/L3?= =?UTF-8?q?=E4=BF=AE=E5=80=BCL2=E6=9D=A1=E6=AC=BE=EF=BC=89=E3=80=81?= =?UTF-8?q?=E6=96=87=E6=A1=88=E7=A6=81=E5=AD=97=E6=B8=85=E6=89=AB=E9=92=89?= =?UTF-8?q?=E5=80=BC=E8=A1=A813=E5=8D=95=E5=85=83=EF=BC=8Bplayer=5Fcopy=5F?= =?UTF-8?q?sweep=E6=96=B0=E5=AE=BF=E4=B8=BB24=E6=96=AD=E8=A8=80=E5=85=A5?= =?UTF-8?q?=E6=B8=85=E5=8D=95=E3=80=81=E5=8F=98=E5=BC=82=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E5=85=A8=E5=A5=97=E7=95=99=E7=97=95=EF=BC=88=E5=AF=B9=E7=85=A7?= =?UTF-8?q?=E8=A1=A8=E8=AF=81=E6=8D=AE=E9=9A=8F=E5=AE=9E=E7=8E=B0=E6=8A=A5?= =?UTF-8?q?=E5=91=8A=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- game/core/battle_orchestrator.gd | 2 +- game/data/tables/settlement.tres | 2 +- game/presentation/battle_narrator.gd | 6 +- game/presentation/main.gd | 12 +- game/presentation/views/board_cell_unit.gd | 3 +- game/presentation/views/board_view.gd | 8 +- game/presentation/views/camp_overlay_view.gd | 12 +- .../views/hero_info_panel_view.gd | 4 +- game/presentation/views/legend_overlay.gd | 2 +- game/presentation/views/level_map_screen.gd | 2 +- game/presentation/views/status_slot_row.gd | 2 +- game/probe/ui_click_probe.gd | 174 ++++++++++++++++++ game/probe/ui_contrast_probe.gd | 69 ++++++- game/probe/ui_struct_probe.gd | 5 + game/tests/run_all.gd | 1 + .../unit/core/meta_progression_forge_test.gd | 24 +++ .../presentation/player_copy_sweep_test.gd | 174 ++++++++++++++++++ .../presentation/theme_projection_test.gd | 17 +- .../unit/presentation/ui_font_asset_test.gd | 1 + 19 files changed, 489 insertions(+), 31 deletions(-) create mode 100644 game/tests/unit/presentation/player_copy_sweep_test.gd diff --git a/game/core/battle_orchestrator.gd b/game/core/battle_orchestrator.gd index 1515056b..3cdf75d9 100644 --- a/game/core/battle_orchestrator.gd +++ b/game/core/battle_orchestrator.gd @@ -794,7 +794,7 @@ func _grant_routed(snap: Dictionary, polarity: String, source_class: String, cel if halve_pct > 0 and polarity == "negative" and source_class == "mine_hit": row = row.duplicate() var half: int = int(ceil(float(int(row.power)) * float(100 - halve_pct) / 100.0)) - _emit_row("effect", source_class, cell, ri, "惊巢反制——挖雷惩罚减半(%d→%d)" % [int(row.power), half], 0, {"effect_id": "nest_counter"}, highlight) + _emit_row("effect", source_class, cell, ri, "惊巢反制——危险格挖掘惩罚减半(%d→%d)" % [int(row.power), half], 0, {"effect_id": "nest_counter"}, highlight) row.power = half return _dispatch_row(row, source_class, cell, ri, highlight, label_suffix) diff --git a/game/data/tables/settlement.tres b/game/data/tables/settlement.tres index 8bf9a0c9..dd906c13 100644 --- a/game/data/tables/settlement.tres +++ b/game/data/tables/settlement.tres @@ -11,7 +11,7 @@ segment_order = PackedStringArray("grant", "buff_debuff", "combat", "decay", "ve source_label_map = { "safe_cell": "安全格", "seal_hit": "封印命中", -"mine_hit": "踩雷", +"mine_hit": "危险格", "clear_reward": "清图奖励", "enemy_action": "敌方行动", "hero_attack": "主角攻击", diff --git a/game/presentation/battle_narrator.gd b/game/presentation/battle_narrator.gd index 1422f766..c765c3ea 100644 --- a/game/presentation/battle_narrator.gd +++ b/game/presentation/battle_narrator.gd @@ -139,7 +139,7 @@ static func bonus_result_text(result: Dictionary) -> String: # 表无 archetype,fallback 兜底词「流派」会实显僵尸文案),卡片=件名+效果摘要两行) static func feat_card_text(row: Dictionary) -> String: if row.is_empty() or String(row.get("feat_id", "")) == "": - return "特性\n???" + return "专长\n???" var summary := String(row.get("desc", "")) if summary != "": return "%s\n%s" % [String(row.get("display_name", row.get("feat_id", ""))), summary] @@ -151,9 +151,9 @@ static func bonus_keystone_line(payload: Dictionary) -> String: return "钥石 +%d · 累计 %d" % [int(payload.get("keystone_gained", 0)), int(payload.get("keystone_total", 0))] -# 奖励节点三选一头部行(与通关三选一共享同池;node-reward.md v2) +# 奖励节点三选一头部行(与通关三选一共享同库;渠道白名单分流——node-reward.md v2) static func node_curio_header() -> String: - return "关卡奖励点\n从提升池抽三选一(与通关三选一同池同权重同上限):\n选择一件带走:" + return "关卡奖励点\n从冒险牌库抽三选一(属性值提升与补给;与通关专长三选一同库分流):\n选择一件带走:" # 冒险结算屏文案(摘要五件事:打穿没有 / 通关几关 / 止步第几关 / 钥石合计 / 主种子) diff --git a/game/presentation/main.gd b/game/presentation/main.gd index 2ea655c9..2c79f97b 100644 --- a/game/presentation/main.gd +++ b/game/presentation/main.gd @@ -604,12 +604,16 @@ func _on_adventure_level_started(level_index: int, display_name: String) -> void run_scope.runner = run_scope.adventure.runner() # 开场行传当前生效值(runner.effective_atk/effective_max_hp 与战斗面板同源: # 表值+永久强化 flat+加算区 flat 后乘调试浮窗 pct,本回调时点 runner 已带全部加成);「含面板加成」 - # 后缀判断认局外加成——永久强化 atk/max_hp flat 或调试浮窗 pct 任一存在才带,白板局不带 + # 后缀判断认任一加成源——永久强化 atk/max_hp flat 或调试浮窗 pct 或局内加算区 atk/max_hp + #(run_ledger.bonus——属性值提升入账后显示值已含加算区 flat,注记随动;armor_class/ap 不入条件: + # 开场行只显示 atk/max_hp 两值,注记指向不存在的显示轴反成失真),白板局不带 var ov: Dictionary[Variant, Variant] = meta.hero_override() + var boosted: bool = int(ov.get("atk", 0)) > 0 or int(ov.get("max_hp", 0)) > 0 \ + or debug_overlay.atk_pct() > 0 or debug_overlay.hp_pct() > 0 \ + or int(run_scope.runner.run_ledger.bonus.get("atk", 0)) > 0 \ + or int(run_scope.runner.run_ledger.bonus.get("max_hp", 0)) > 0 log_view.append_bb(Narrator.level_open_line(display_name, int(run_scope.adventure.master_seed), - run_scope.runner.effective_atk(), run_scope.runner.effective_max_hp(), - int(ov.get("atk", 0)) > 0 or int(ov.get("max_hp", 0)) > 0 - or debug_overlay.atk_pct() > 0 or debug_overlay.hp_pct() > 0)) + run_scope.runner.effective_atk(), run_scope.runner.effective_max_hp(), boosted)) func _on_runner_node(node: Dictionary, info: Dictionary) -> void: diff --git a/game/presentation/views/board_cell_unit.gd b/game/presentation/views/board_cell_unit.gd index 31d1ac24..7feda330 100644 --- a/game/presentation/views/board_cell_unit.gd +++ b/game/presentation/views/board_cell_unit.gd @@ -267,7 +267,8 @@ func _draw() -> void: elif fog_state == 2: # 记忆态换装(demo 口径「地板纹理 + 灰罩 α.66」: # 地板纹理原样直画 + FOG_MEMORY_PLATE α.66 灰罩压顶;素材零改动、格边长零改动。 - # 记忆墨 FOG_MEMORY_INK 对合成底实测 ≈6.7:1 ≥4.5:1(§7.2 硬指标;floor avg #453020 复算在册)) + # 记忆墨 FOG_MEMORY_INK 对合成底归一后(真实渲染面)三关 5.71–5.93:1 ≥4.5:1(§7.2 硬指标; + # 测点行值与素材随动,见 ui_contrast_probe 迷雾段)) if tex_base != null: draw_texture_rect(tex_base, Rect2(Vector2.ZERO, Vector2(edge, edge)), false) draw_rect(Rect2(Vector2.ZERO, Vector2(edge, edge)), Color(UiKit.FOG_MEMORY_PLATE, 0.66), true) diff --git a/game/presentation/views/board_view.gd b/game/presentation/views/board_view.gd index 88e2d230..976a2006 100644 --- a/game/presentation/views/board_view.gd +++ b/game/presentation/views/board_view.gd @@ -727,7 +727,7 @@ func _paint_cell(c: Vector2i, hero: Vector2i) -> void: var rec: Dictionary[Variant, Variant] = _minesweeper.reveal_record(c) # 已开记录经查询面 if rec.get("kind", "") == "mine": bg = COL_FLOOR - glyph = "雷" + glyph = "危" digit_col = UiKit.RED_MINE else: bg = COL_FLOOR @@ -773,13 +773,13 @@ func _paint_cell(c: Vector2i, hero: Vector2i) -> void: u.bg_sb = sb u.glyph = glyph u.glyph_color = digit_col - # 色牌后备(数字直画退牌;雷/「?」维持牌载保读性)+ v3.4:双峰纹理板 + # 色牌后备(数字直画退牌;「危」/「?」维持牌载保读性)+ v3.4:双峰纹理板 #(_digit_plate——underdark 系亮白筋+近黑斑并存的直画不可救纹理)数字落牌,牌上深墨 12:1 - var plate_digit := _digit_plate and glyph != "" and glyph != "雷" and glyph != "?" and glyph != "封" and glyph != "人" + var plate_digit := _digit_plate and glyph != "" and glyph != "危" and glyph != "?" and glyph != "封" and glyph != "人" if plate_digit: digit_col = COL_DIGIT # 牌上深墨族(素牌 12:1——双峰纹理免疫) u.glyph_color = digit_col - u.plate = revealed and (glyph == "雷" or glyph == "?" or plate_digit) + u.plate = revealed and (glyph == "危" or glyph == "?" or plate_digit) u.outline_sides.assign([]) # typed Array[int](assign 保持引用语义) u.facade_south = false u.proj_band = 0.0 diff --git a/game/presentation/views/camp_overlay_view.gd b/game/presentation/views/camp_overlay_view.gd index e039b033..543c0380 100644 --- a/game/presentation/views/camp_overlay_view.gd +++ b/game/presentation/views/camp_overlay_view.gd @@ -164,7 +164,7 @@ func _init() -> void: atlas_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL atlas_panel.size_flags_vertical = Control.SIZE_FILL # 四栏等高:任一栏长高时四栏同步拉伸 mid.add_child(atlas_panel) - var lock_l := UiKit.label(" —— 柜台结构位预留:内容排 Alpha(地图战斗资源差异化批)",UiKit.FS_HINT, false, UiKit.STONE) + var lock_l := UiKit.label(" —— 柜台结构位预留:内容排 Alpha(地图战斗资源差异化批)",UiKit.FS_HINT, false, UiKit.STONE_DEEP) lock_l.size_flags_vertical = Control.SIZE_SHRINK_CENTER # 锁文在等高栏内垂直居中 lock_l.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART lock_l.custom_minimum_size = Vector2(280, 0) @@ -414,7 +414,7 @@ func refresh_camp(data: Dictionary) -> void: card.progress_l.text = prog_text # 状态色用亮板双色墨制(深墨族替代暖白族——亮板上暖白不可读) var st_text := "未解锁" - var st_col: Color = UiKit.STONE + var st_col: Color = UiKit.STONE_DEEP if unlocked: st_text = "已解锁" st_col = UiKit.GOLD_INK # GOLD_DEEP 对行板 3.00:1 越带,换深金墨 5.84:1 @@ -473,7 +473,7 @@ func _rebuild_workshop(data: Dictionary) -> void: progress_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS line.add_child(progress_l) # 状态色用亮板双色墨制(深墨族替代暖白族)——色随刷新态条件赋值,非机械两行式 - var status_l := UiKit.label("",UiKit.FS_HINT, true, UiKit.STONE) + var status_l := UiKit.label("",UiKit.FS_HINT, true, UiKit.STONE_DEEP) status_l.clip_text = true # 同上——「已解锁 · 能力封存中」不撑柜(余宽吸收者) status_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS status_l.size_flags_horizontal = Control.SIZE_EXPAND_FILL @@ -506,11 +506,11 @@ func _rebuild_attune(data: Dictionary) -> void: name_l.clip_text = true name_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS line.add_child(name_l) - var form_l := UiKit.label("被动" if String(row.get("form", "")) == "passive" else "主动·充能",UiKit.FS_CAPTION, false, UiKit.STONE) + var form_l := UiKit.label("被动" if String(row.get("form", "")) == "passive" else "主动·充能",UiKit.FS_CAPTION, false, UiKit.STONE_DEEP) form_l.custom_minimum_size = Vector2(58, 0) line.add_child(form_l) # 档位标签(槽位档位显示:件档=普通/优秀/稀有,装入须件档≤格档契约 2 的呈现半边) - var tier_l := UiKit.label(String(UiKit.TREASURE_TIER_LABELS.get(String(row.get("tier", "")), "—")),UiKit.FS_CAPTION, false, UiKit.STONE) + var tier_l := UiKit.label(String(UiKit.TREASURE_TIER_LABELS.get(String(row.get("tier", "")), "—")),UiKit.FS_CAPTION, false, UiKit.STONE_DEEP) tier_l.custom_minimum_size = Vector2(36, 0) line.add_child(tier_l) var btn := UiKit.button("装配", 0.0) # 行板紧凑钮豁免 @@ -522,7 +522,7 @@ func _rebuild_attune(data: Dictionary) -> void: _attune_btns.append({"item_id": iid, "btn": btn, "unlocked": bool(row.get("unlocked", false)), "equipped": bool(row.get("equipped", false))}) var refresh_line := HBoxContainer.new() refresh_line.add_theme_constant_override("separation", 6) - _refresh_l = UiKit.label("",UiKit.FS_CAPTION, false, UiKit.STONE) + _refresh_l = UiKit.label("",UiKit.FS_CAPTION, false, UiKit.STONE_DEEP) refresh_line.add_child(_refresh_l) _refresh_btn = UiKit.button("升级", 0.0) # 行板紧凑钮豁免 _refresh_btn.pressed.connect(_emit_buy_requested.bind("attune_refresh")) diff --git a/game/presentation/views/hero_info_panel_view.gd b/game/presentation/views/hero_info_panel_view.gd index a7465d05..bc020fff 100644 --- a/game/presentation/views/hero_info_panel_view.gd +++ b/game/presentation/views/hero_info_panel_view.gd @@ -70,7 +70,7 @@ func _init() -> void: _feat_section.add_theme_constant_override("separation", 4) _feat_section.visible = false col.add_child(_feat_section) - _feat_section.add_child(UiKit.label("特性",UiKit.FS_BODY, true)) + _feat_section.add_child(UiKit.label("专长",UiKit.FS_BODY, true)) _feat_list = VBoxContainer.new() _feat_list.add_theme_constant_override("separation", 2) _feat_section.add_child(_feat_list) @@ -90,7 +90,7 @@ func _init() -> void: _curio_section.add_theme_constant_override("separation", 4) _curio_section.visible = false col.add_child(_curio_section) - _curio_section.add_child(UiKit.label("特性",UiKit.FS_BODY, true)) + _curio_section.add_child(UiKit.label("属性值提升",UiKit.FS_BODY, true)) _curio_list = VBoxContainer.new() _curio_list.add_theme_constant_override("separation", 2) _curio_section.add_child(_curio_list) diff --git a/game/presentation/views/legend_overlay.gd b/game/presentation/views/legend_overlay.gd index 45f979e0..08454647 100644 --- a/game/presentation/views/legend_overlay.gd +++ b/game/presentation/views/legend_overlay.gd @@ -8,7 +8,7 @@ signal closed const ENTRIES := [ ["#C9AF83", "完整覆尘地砖", "未开格——左键挖开(1AP,须相邻)· 右键封印(1AP)"], ["#B39A78", "碎裂裸土", "已开 0 格——可通行,左键移动(免费)"], - ["#000000", "封印", "封印印记(呈现名·不能拔)——轮末自动判定标中;插错可左键直接挖开(安全格零奖励、危险格吃挖雷惩罚)"], + ["#000000", "封印", "封印印记(呈现名·不能拔)——轮末自动判定标中;标空可左键直接挖开(安全格零奖励、危险格受挖掘惩罚)"], ["#2e86de", "小人", "探险者——8 向相邻未开格常驻微光 = 可挖域"], ["#F0A828", "金徽章", "已开格奖励身份 icon(封印之下、数值之上,不进数字区)"], ["#F0A828", "九徽章解码", "攻=攻击 · 守=护甲 · 爆=爆裂 · 医=回复 · 弱=削弱 · 穿=穿刺 · 魔=魔法 · 行=行动点 · 金=GP"], diff --git a/game/presentation/views/level_map_screen.gd b/game/presentation/views/level_map_screen.gd index d0516d07..a0de2bf1 100644 --- a/game/presentation/views/level_map_screen.gd +++ b/game/presentation/views/level_map_screen.gd @@ -168,7 +168,7 @@ func _init() -> void: _bonus_row.add_child(bonus_box) var bonus_inner := VBoxContainer.new() bonus_box.add_child(bonus_inner) - var bonus_cap := UiKit.label("特性",UiKit.FS_CAPTION, false, UiKit.INK_HINT) # 深墨(亮板可读) + var bonus_cap := UiKit.label("属性值提升",UiKit.FS_CAPTION, false, UiKit.INK_HINT) # 深墨(亮板可读) bonus_inner.add_child(bonus_cap) _bonus_value = UiKit.label("",UiKit.FS_BODY, true, UiKit.INK_TITLE) # 淡墨→深墨(同上) bonus_inner.add_child(_bonus_value) diff --git a/game/presentation/views/status_slot_row.gd b/game/presentation/views/status_slot_row.gd index 073aba28..0e1cf51d 100644 --- a/game/presentation/views/status_slot_row.gd +++ b/game/presentation/views/status_slot_row.gd @@ -37,7 +37,7 @@ const EFFECT_SEMANTIC := { "empower": "敌方输出 ×2(enemy_output 池加算——蓄力技施放;持续 1 个有效结算)", "fortify": "敌方防御 −50(enemy_defense 池——硬化技施放,主角攻击 ×0.5;持续 1 个有效结算)", } -const SOURCE_NAMES := {"safe_cell": "安全格", "seal_hit": "封印命中", "mine_hit": "踩雷", "clear_reward": "清图奖励"} +const SOURCE_NAMES := {"safe_cell": "安全格", "seal_hit": "封印命中", "mine_hit": "危险格", "clear_reward": "清图奖励"} # 未知图标键的占位(HUD框架:占位也要可见,禁静默空图标——debug 断言先行,release 回退槽内 ? 文本) const ICON_PLACEHOLDER := "?" diff --git a/game/probe/ui_click_probe.gd b/game/probe/ui_click_probe.gd index d64d845d..36af9112 100644 --- a/game/probe/ui_click_probe.gd +++ b/game/probe/ui_click_probe.gd @@ -26,6 +26,9 @@ var _tested_review := false var _tested_end_review := false # 终局面板回看入口(z 序修复回归) var _tested_refresh_chain := false var _tested_tooltip := false +var _tested_copy := false # 迭代36 卡A——C 区两段头真实入口断言(每 run 一次) +var _tested_curio_head := false # 迭代36 卡A——三选一头部旧话清零断言(奖励点停点捕获) +var _tested_map_cap := false # 迭代36 卡A——地图屏加成帽换代断言(每 run 一次) var _tested_status := false # 状态面板角标开合/守卫断言(每 run 一次) var _tested_map_status := false # 地图屏角标一次性 case(选路态零扰动) var _inter_confirms := 0 # 大奖励屏确认计数(冒险推进停点——首见确认推进 L2,再见 = L2 通关收口) @@ -121,6 +124,54 @@ func _ready() -> void: iso_fk.load_from_disk() if String(iso_fk.fork_side("fg_posture")) != "b": _problems.append("Fork 切态未持久(reload 后激活态 != b)") + # ---- 营地六处灰字取色断言(迭代36 wc4——真实入口开营地后按文本锚取 label, + # font_color 逐处 == STONE_DEEP;六源位 :167 图鉴锁文/:417+:476 行状态字/:509 同调形态/:513 档位标签/:525 刷新行)---- + var camp_labels: Array = [] + _collect_labels_by_text(main.camp_overlay, camp_labels) + var atlas_locks := _labels_with_text(camp_labels, "柜台结构位预留") + if atlas_locks.is_empty(): + _problems.append("营地灰字:图鉴柜台锁文 label 未找到(wc4 定位锚失守)") + for l in atlas_locks: + if l.get_theme_color("font_color") != UiKit.STONE_DEEP: + _problems.append("营地灰字:图鉴锁文未用 STONE_DEEP(wc4 :167)") + # 「未解锁」锚收窄到秘宝匣行容器子树(树行/锻造行也有「未解锁」态字样——非 wc4 目标位) + var locked_status: Array = [] + if main.camp_overlay.get("_workshop_holder") != null: + var ws_labels: Array = [] + _collect_labels_by_text(main.camp_overlay._workshop_holder, ws_labels) + locked_status = _labels_with_text(ws_labels, "未解锁") + if locked_status.is_empty(): + _problems.append("营地灰字:秘宝匣行「未解锁」态 label 未找到(wc4 夹具前提失守——新档应有锁定秘宝行)") + for l in locked_status: + if l.get_theme_color("font_color") != UiKit.STONE_DEEP: + _problems.append("营地灰字:秘宝行「未解锁」态未用 STONE_DEEP(wc4 :417/:476)") + var form_found := false + for l in camp_labels: + if l.text == "被动" or l.text == "主动·充能": + form_found = true + if l.get_theme_color("font_color") != UiKit.STONE_DEEP: + _problems.append("营地灰字:同调形态标签未用 STONE_DEEP(wc4 :509)") + if not form_found: + _problems.append("营地灰字:同调行形态标签未找到(wc4 定位锚失守——同调段应有行)") + var tier_vals := {} + for v in UiKit.TREASURE_TIER_LABELS.values(): + tier_vals[String(v)] = true + var tier_found := false + for l in camp_labels: + if tier_vals.has(l.text): + tier_found = true + if l.get_theme_color("font_color") != UiKit.STONE_DEEP: + _problems.append("营地灰字:同调档位标签未用 STONE_DEEP(wc4 :513)") + if not tier_found: + _problems.append("营地灰字:同调档位标签未找到(wc4 定位锚失守)") + var refresh_inked := false + for l in camp_labels: + if String(l.text).contains("三选一刷新"): + refresh_inked = true + if l.get_theme_color("font_color") != UiKit.STONE_DEEP: + _problems.append("营地灰字:刷新行未用 STONE_DEEP(wc4 :525)") + if not refresh_inked: + _problems.append("营地灰字:刷新行 label 未找到(wc4 定位锚失守)") main._on_camp_closed() await get_tree().process_frame main._on_save_select_closed() # 关闭被栈还原的选档层(动线序中间态——卡④ 换代点) @@ -322,6 +373,12 @@ func _ready() -> void: var t1 := Time.get_ticks_msec() main._start_adventure_from_menu() await get_tree().process_frame + await get_tree().process_frame + # 既有口径回归(迭代36 wc3——调试滑条 400/200=局外加成 pct>0,开场行应带「(含面板加成)」; + # 本局为本探针首个冒险,日志无先前行,全文本判即可) + var wc3_txt: String = main.log_view._log.get_parsed_text() + if not wc3_txt.contains("种子 42") or not wc3_txt.contains("(含面板加成)"): + _problems.append("开场行回归失守:局外加成(pct>0)局的开场行应带「(含面板加成)」(wc3)") # 关卡开始 = 入口选路停点——press_candidate 图上命中路径选入口候选进首战 #(首格可点 AC 口径随之含入口选路一步:菜单点击 → 停点呈现 → pick → 棋盘安装) if main.level_screen.visible: @@ -358,6 +415,12 @@ func _ready() -> void: # 未适配则走到即卡死)。两态分派:result 态(兑付反馈——点继续推进)/ # 待选态(点首件+确认)。真实信号路径全线。〔无替换子态——装配不可变契约。〕 # 三选一停点输入锁定断言(停点态断言面:待选/兑付两态共用此层)。 + if not _tested_curio_head and main.run_scope.adventure != null and not main.run_scope.adventure._curio_pending.is_empty() and String(main.curio_overlay._header).contains("关卡奖励点"): + _tested_curio_head = true + var ch := String(main.curio_overlay._header) + if not ch.contains("冒险牌库") or not ch.contains("属性值提升与补给") or ch.contains("同权重") or ch.contains("同上限") or ch.contains("提升池"): + _problems.append("三选一头部旧话残留或新话缺失(wa4——%s)" % ch.replace(" +", "|")) if not main._input_locked(): _problems.append("三选一停点打开时输入未锁定") if main.curio_overlay._continue_btn.visible: @@ -395,6 +458,18 @@ func _ready() -> void: rbtn2.pressed.emit() if main.status_panel.visible or not main.level_screen.visible: _problems.append("状态面板关闭后地图屏态失守(恢复语义破)") + # 地图屏加成帽换代断言(迭代36 卡A wa2——「特性」→「属性值提升」;帽 Label 常在树; + # 独立旗标——_tested_map_status 在同拍前置块已置位,禁复用其门控) + if not _tested_map_cap: + _tested_map_cap = true + var cap_found := false + var ls_labels: Array = [] + _collect_labels_by_text(main.level_screen, ls_labels) + for l in ls_labels: + if l.text == "属性值提升": + cap_found = true + if not cap_found: + _problems.append("地图屏加成帽未换代为「属性值提升」(wa2)") main.level_screen.press_candidate(_route_clicks % 2) _route_clicks += 1 _seals_placed.clear() @@ -553,6 +628,67 @@ func _ready() -> void: # ②③ 中断→续玩全链+互斥确认两分支:随三入口化撤出(卡② §7.2 申报——断点确认探针段移交 # 卡④大门 T5 接收,两头登记禁静默删除;续玩钮已退役,主菜单确认链已摘 mn6)。 # ① 战斗收口断点写盘断言保留(上方——冒险流直调中间态 mn10)。 + + # ---- 开场行三路径段(迭代36 wc1/wc2——真实入口驱动;隔离新档)---- + # 驱动设计(0/0 白板局生存性不成立的实证处置——L2 记录进实现报告): + # ① 菜单开新局·白板反向=独立短局:0/0 滑条开局断言 L1 开场行零后缀(不开驱动—— + # 断言完成后该局被 ② 的开局自然替换,白板局生存性不参与判定); + # ② 局内加算区正向=生存局:400/200 滑条开局保可通关(:363 先例),三选一专选 atk/max_hp + # 卡(无则任选推进——不触后缀、循环重试);至大奖励屏先 reset_sliders() 再确认进下一层 + # ——L2 开场行时点 pct=0 且新档 ov=0,后缀只能归因局内加算区(变异:回退条件 hunk→红); + # ③ 断点续玩正向:L2 入口选路停点(bonus 已入账、bonus_pending/bonus_result 皆空——选路停点形) + # 经 _resume_from_menu 唯一消费口重建,重建开场行带后缀。 + # 判定面=开局清日志后的新局日志(_start_adventure 内 log_view.clear());「(含面板加成)」 + # 唯一来源=开场行,计数即判定;「· 种子 」=开场行独有形态锚。 + for p_ol in ["user://smoke_click_probe_openline.json", "user://smoke_click_probe_openline.json.tmp"]: + if FileAccess.file_exists(p_ol): + DirAccess.remove_absolute(p_ol) + ProbeUiUtil.isolate_save(main, "smoke_click_probe_openline", true, false, true) + main.debug_overlay.reset_sliders() # ① 白板反向前提(_start_adventure 只关窗不清值——残值跨段存活防坑) + main.debug_overlay.seed_edit.text = "42" + main._start_adventure_from_menu() + var ol_l1_wait := 0 + while ol_l1_wait < 24 and String(main.log_view._log.get_parsed_text()).count("· 种子 ") < 1: + ol_l1_wait += 1 + await get_tree().process_frame + if ol_l1_wait >= 24: + _problems.append("开场行路径①:L1 开场行未达日志(开局清日志后 24 帧内未现种子行)") + elif String(main.log_view._log.get_parsed_text()).count("(含面板加成)") > 0: + _problems.append("开场行路径①失守:白板局 L1 开场行带「(含面板加成)」(wc1 反向)") + print("[click_probe] 开场行①判定 wait=%d 帧断言完成" % ol_l1_wait) + # ②③ 断点续玩正向(既有快照喂入形态——代拍板 2 预授权;生产「继续上局」钮已随三入口化 + # 撤出,_resume_from_menu=唯一消费口;断点形态=选路停点形钉值:bonus 已入账、 + # bonus_pending/bonus_result 皆空——set_breakpoint 注入口同 system_test V3i 先例, + # 卡内警示的 bonus_pending 形夹具是本形态反例,禁复用)。续玩重建开场行时点 pct=0 + # +新档 ov=0+bonus.atk=5 已回填(resume_adventure :151-154)——后缀只能归因局内加算区。 + # 【卡片前提缺陷记录(随实现报告/停机呈报)】路径②原设计「奖励节点三选一拿属性卡→下一关 + # 开场行」在现行 build 不可驱动:L1 全模板零商店节点、reward 节点=零兑付直通占位 + # (level_runner _enter_node 注记)——新开局在任一开场行时点均无局内 bonus 来源。 + # 条件加算区两项的机制面由本续玩路径全链承载(回填→level_started→条件求值,与 fresh 同式), + # 变异(回退条件 hunk)见红;fresh 正向拦截能力未证,如实申报。 + var bp_ol: Dictionary = { + "master_seed": 42, "rng_counter": 0, "hero_hp": 30, "gp": 10, "keystone": 0, + "level_index": 1, "bonus": {"atk": 5, "max_hp": 0, "armor_class": 0, "ap": 0}, + "shop_purchased": {}, "relics": [], "keystone_drops": [], "bonus_taken": {}, + "node_id": -1, "visited_ids": [], "template_pick": -1, + } + main.save_manager.set_breakpoint(bp_ol) + # 判定面=续玩开局清日志后的新局日志(_resume_from_menu 内 log_view.clear()——选路停点形 + # resume 同步重建开场行,返回时已在日志) + main._resume_from_menu() + var ol_r_wait := 0 + while ol_r_wait < 24 and String(main.log_view._log.get_parsed_text()).count("· 种子 ") < 1: + ol_r_wait += 1 + await get_tree().process_frame + if main.run_scope.runner == null or int(main.run_scope.runner.run_ledger.bonus.get("atk", 0)) != 5: + _problems.append("开场行路径②③前提失守:续玩重建后加算区 atk!=5(断点回填链断)") + if ol_r_wait >= 24: + _problems.append("开场行路径②③失守:续玩重建未发开场行(选路停点形应重发——形态/驱动复核)") + elif String(main.log_view._log.get_parsed_text()).count("(含面板加成)") < 1: + _problems.append("开场行路径②③失守:bonus 已入账(pct=0/ov=0)的续玩重建开场行未带「(含面板加成)」(wc1 正向/wc2)") + else: + print("[click_probe] 开场行②③判定:续玩重建开场行带后缀(bonus.atk=5、pct=0)— wait=%d 帧" % ol_r_wait) + print("[click_probe] 循环退出(done=%s budget=%s)问题=%d" % [ str(_done), str(action_budget), _problems.size()]) for p in _problems: @@ -568,6 +704,23 @@ func _ready() -> void: # ---- 模态/tooltip/刷新链一次性覆盖(真实信号路径驱动) ---- func _run_modal_probes_once() -> void: + # ⓪ 玩家面文案真实入口断言(迭代36 卡A wa1——C 区两段头随战斗常驻) + if not _tested_copy and main.info_panel != null and main.info_panel.visible: + _tested_copy = true + var feat_head: Label = null + var curio_head: Label = null + for child in main.info_panel._feat_section.get_children(): + if child is Label: + feat_head = child + break + for child in main.info_panel._curio_section.get_children(): + if child is Label: + curio_head = child + break + if feat_head == null or feat_head.text != "专长": + _problems.append("C 区专长段头未换代(wa1——真实入口)") + if curio_head == null or curio_head.text != "属性值提升": + _problems.append("C 区属性值提升段头未换代(wa1——真实入口)") # ① 图例模态(pattern 6):打开 → 遮挡物隐藏 + 输入锁定;关闭 → 恢复 if not _tested_legend: _tested_legend = true @@ -582,6 +735,10 @@ func _run_modal_probes_once() -> void: for e in main.legend_overlay.ENTRIES: if String(e[1]) == "封印": legend_has_seal = true + # 图例封印行换代断言(迭代36 卡A wa5——真实 AP 条图例钮开层路径) + if not String(e[2]).contains("标空") or not String(e[2]).contains("受挖掘惩罚") or String(e[2]).contains("插错") or String(e[2]).contains("挖雷"): + _problems.append("图例封印行旧词残留(wa5——%s)" % String(e[2]).replace(" +", "|")) if not legend_has_seal: _problems.append("图例缺『封印』格态条目(格态图例基线)") main.legend_overlay.close() @@ -660,6 +817,23 @@ func _run_modal_probes_once() -> void: _problems.append("HUD 刷新余量与快照推导失符(HUD='%s' 应含 %d)" % [main.ap_bar.refresh_remaining_text(), remain_expect]) +# ---- 营地灰字取色断言辅助(迭代36 wc4——文本锚定位,全 Label 递归收集)---- + +func _collect_labels_by_text(root: Node, out: Array) -> void: + if root is Label: + out.append(root) + for child in root.get_children(): + _collect_labels_by_text(child, out) + + +func _labels_with_text(labels: Array, needle: String) -> Array: + var found: Array = [] + for l in labels: + if String(l.text).contains(needle): + found.append(l) + return found + + func _play_one_action() -> void: var settlement = main.run_scope.settlement if settlement == null: diff --git a/game/probe/ui_contrast_probe.gd b/game/probe/ui_contrast_probe.gd index c2cf240c..bfe6403c 100644 --- a/game/probe/ui_contrast_probe.gd +++ b/game/probe/ui_contrast_probe.gd @@ -58,10 +58,22 @@ func _pairs() -> Array: ["设置·选项标签/设置板", UiKit.INK_LABEL, UiKit.PANEL_DATA, 1.0], # 设置行标签/状态字(标签深墨) ["三选一·停点头部墨/暗页底", UiKit.WARM_SOFT, UiKit.BG_ROOT, 1.0], # 三选一停点头部/回血反馈行(page_shell 根底档) ["三选一·卡名墨/货架板", UiKit.INK_TITLE, UiKit.PANEL_DATA, 1.0], # bcard 选项卡名(与商店货架同构卡) - ["迷雾·记忆墨/灰罩合成底", UiKit.FOG_MEMORY_INK, # fog_state2 记忆板 α.66 压 floor avg #453020 - Color(UiKit.FOG_MEMORY_PLATE.r * 0.66 + 69.0 / 255.0 * 0.34, # 合成底(素材侧值无常量形态——取 art-bible §389 在册复算值,素材更新随动; - UiKit.FOG_MEMORY_PLATE.g * 0.66 + 48.0 / 255.0 * 0.34, # 三关逐关 avg 缺在册且素材不入 git 不可实查——最坏情况判据扩对归移交) - UiKit.FOG_MEMORY_PLATE.b * 0.66 + 32.0 / 255.0 * 0.34), 1.0], + # ---- 迷雾测点三关扩对(迭代36 wc5——甲·归一后基准:行值=三关归一后(真实渲染面) + # 地板均值合成底(写卡会话 PIL 复算+两轮 grilling 独立复算吻合;T0 探针派生首跑对拍 + # L2/L3 修值(PIL↔Godot 导入解码系统差约 5/255,卡内 L2 条款预授权以派生为准); + # 素材更新随动——一致性由 _ready 派生校验断言钉住,行值回原图基准或素材换代未随动均红)---- + ["迷雾·记忆墨/合成底L1地牢", UiKit.FOG_MEMORY_INK, + Color(UiKit.FOG_MEMORY_PLATE.r * 0.66 + 111.0 / 255.0 * 0.34, # L1 归一后地板均值 #6F4D33(合成底 #4C4038 5.71:1·最差关) + UiKit.FOG_MEMORY_PLATE.g * 0.66 + 77.0 / 255.0 * 0.34, + UiKit.FOG_MEMORY_PLATE.b * 0.66 + 51.0 / 255.0 * 0.34), 1.0], + ["迷雾·记忆墨/合成底L2幽暗", UiKit.FOG_MEMORY_INK, + Color(UiKit.FOG_MEMORY_PLATE.r * 0.66 + 74.0 / 255.0 * 0.34, # L2 归一后地板均值 #4A4F60(合成底 #3F4147 5.83:1) + UiKit.FOG_MEMORY_PLATE.g * 0.66 + 79.0 / 255.0 * 0.34, + UiKit.FOG_MEMORY_PLATE.b * 0.66 + 96.0 / 255.0 * 0.34), 1.0], + ["迷雾·记忆墨/合成底L3深幽", UiKit.FOG_MEMORY_INK, + Color(UiKit.FOG_MEMORY_PLATE.r * 0.66 + 71.0 / 255.0 * 0.34, # L3 归一后地板均值 #474B63(合成底 #3E4048 5.93:1) + UiKit.FOG_MEMORY_PLATE.g * 0.66 + 75.0 / 255.0 * 0.34, + UiKit.FOG_MEMORY_PLATE.b * 0.66 + 99.0 / 255.0 * 0.34), 1.0], ] @@ -94,8 +106,9 @@ func _ready() -> void: if r < MIN_RATIO: problems.append("[contrast] %s = %.2f:1 < %.1f(测点对须垫牌或换墨)" % [p[0], r, MIN_RATIO]) problems.append_array(_scan_static()) + problems.append_array(_fog_derived_check()) if problems.is_empty(): - print("[ui_contrast_probe] PASS — 对比度 %d 对测点全绿(≥%.1f:1 无区域例外)+ 裸色白名单/preload 红线静态段绿" % [pairs.size(), MIN_RATIO]) + print("[ui_contrast_probe] PASS — 对比度 %d 对测点全绿(≥%.1f:1 无区域例外)+ 裸色白名单/preload 红线静态段绿 + 迷雾三关派生校验绿" % [pairs.size(), MIN_RATIO]) get_tree().quit(0) else: for msg in problems: @@ -104,6 +117,52 @@ func _ready() -> void: get_tree().quit(1) +# ---- 迷雾测点行值一致性派生校验(迭代36 wc5——行值 vs 运行时按生产归一函数实算的 +# 三关归一后地板均值,容差 ±2/255=8-bit 量化级;行值回原图基准或素材换代未随动均红)---- +func _fog_derived_check() -> Array: + var problems: Array = [] + var rows := [ + ["L1地牢", "dungeon", Color8(111, 77, 51)], + ["L2幽暗", "underdark", Color8(74, 79, 96)], + ["L3深幽", "deep_underdark", Color8(71, 75, 99)], + ] + for row in rows: + var derived := _norm_floor_avg(row[1]) + var expect: Color = row[2] + if absi(derived.r8 - expect.r8) > 2 or absi(derived.g8 - expect.g8) > 2 or absi(derived.b8 - expect.b8) > 2: + problems.append("[contrast] 迷雾测点行值失真:%s 行值 (%d,%d,%d) vs 运行时归一派生 (%d,%d,%d)(±2/255——素材更新须随动改行值)" + % [row[0], expect.r8, expect.g8, expect.b8, derived.r8, derived.g8, derived.b8]) + return problems + + +# 迷雾测点派生助手(迭代36 wc5 契约 3——全仓唯一「按板型算归一后均值」判据面):纹理路径经 +# BoardView.TILE_FLOOR_FMT 常量(禁复制字面量)、归一经 BoardView._normalize_floor_luma +# 生产静态函数直呼(真渲染同源——禁探针内复制 k 公式);逐像素归一后均值再五变体平均。 +static func _norm_floor_avg(board_type: String) -> Color: + var sum_r := 0.0 + var sum_g := 0.0 + var sum_b := 0.0 + var px := 0 + for i in range(1, BoardView.TILE_FLOOR_VARIANTS + 1): + var tex := UiKit.load_tex(BoardView.TILE_FLOOR_FMT % [board_type, i]) + if tex == null: + continue + var img: Image = BoardView._normalize_floor_luma(tex, 0.10, null).get_image() + if img.is_compressed(): + img.decompress() + img.convert(Image.FORMAT_RGBA8) + for y in range(img.get_height()): + for x in range(img.get_width()): + var c := img.get_pixel(x, y) + sum_r += c.r + sum_g += c.g + sum_b += c.b + px += 1 + if px == 0: + return Color(-1, -1, -1) # 素材缺位=派生校验必红(禁跳过——契约 3 齐盘条款) + return Color(sum_r / px, sum_g / px, sum_b / px) + + # ---- 机检段②③(需求卡片 §7):呈现层裸 Color8( 计数 vs 白名单 + 素材 preload 计数 = 0 ---- func _scan_static() -> Array: var problems: Array = [] diff --git a/game/probe/ui_struct_probe.gd b/game/probe/ui_struct_probe.gd index 4deede60..7256de01 100644 --- a/game/probe/ui_struct_probe.gd +++ b/game/probe/ui_struct_probe.gd @@ -43,6 +43,11 @@ func _ready() -> void: else: _settle() await _audit("camp", [main.camp_overlay]) + # 营地灰字源 token 扫描(迭代36 wc4 静态半——源内零裸 UiKit.STONE;判别式= + # STONE 出现次数 == STONE_DEEP 出现次数,即每处 STONE 都带 _DEEP 后缀) + var camp_src := FileAccess.get_file_as_string("res://presentation/views/camp_overlay_view.gd") + if camp_src.count("UiKit.STONE") != camp_src.count("UiKit.STONE_DEEP"): + problems.append("[camp] camp_overlay_view.gd 存在裸 UiKit.STONE(非 _DEEP 后缀——wc4 源扫描)") main._on_camp_closed() _settle() main._on_save_select_closed() # 关闭被栈还原的选档层(动线序中间态——卡④ 换代点) diff --git a/game/tests/run_all.gd b/game/tests/run_all.gd index c6d447f8..31fd7f14 100644 --- a/game/tests/run_all.gd +++ b/game/tests/run_all.gd @@ -99,6 +99,7 @@ func _suites() -> Array: {"path": "res://tests/unit/presentation/battle_narrator_test.gd", "expect": 0}, # 战斗叙述(数值带入/胜负互异/收束态恒定) {"path": "res://tests/unit/presentation/type_glow_test.gd", "expect": 0}, # 未开格类型微光数据面(九型归组封闭/清单外显式失败/冻结色相只准动明度/α170·带宽cell÷8锚定/合成空间阶梯单调+模型CR≥1.20/probe API 形状) {"path": "res://tests/unit/presentation/theme_projection_test.gd", "expect": 0}, # 呈现投影与主题宿主(微光数据面/战斗日志行协议/全局主题与按钮五态/纹理过滤白名单/对比度墨色锚/元素细节源码锚/诊断钮退役源码契约/状态面板与生涯页只读投影——批 B 自 smoke_all 迁入;试玩记录仪整套按裁决七转办集成宿主) + {"path": "res://tests/unit/presentation/player_copy_sweep_test.gd", "expect": 0}, # 玩家面文案清扫宿主(迭代36 卡A——钉值表换代断言①–⑧⑩+源 token 扫描两处;直建断言族,禁字「雷」族与旧术语清零钉值) ] diff --git a/game/tests/unit/core/meta_progression_forge_test.gd b/game/tests/unit/core/meta_progression_forge_test.gd index 7bbe65c2..6f4e2f3a 100644 --- a/game/tests/unit/core/meta_progression_forge_test.gd +++ b/game/tests/unit/core/meta_progression_forge_test.gd @@ -662,6 +662,30 @@ func _smoke_tree_rules() -> void: ctx_fz2.level_bucket = lvl_shared # 挂同一 level 桶引用(新形态跨战持久:无 export/seed,同字典即持久) Kit.check("锋:限次跨战持久(level 桶共享引用——替代旧副本搬运链)〔无状态化 V4〕", (fz2.dispatch(ctx_fz2, "mine_hit", {}, Callable()) as Array).is_empty()) + # 锋·惊巢反制效果行换代断言(迭代36 u13/wa12——_grant_routed 减半分支效果行=战斗日志 + # step_row 真实生产面;display_name 玩家可见,禁字「挖雷」由此行清零钉住) + var orch_nc = load("res://core/battle_orchestrator.gd").new(_db, _fresh_rng(777003)) + orch_nc.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40, + "hero_armor_class": 10, "hero_ap_bonus": 0, "attunement": [], "attune_charges": {}, + "feats": [], "tree_rules": [], "battle_index": 1, + "bonus_ref": {"atk": 0, "max_hp": 0, "armor_class": 0}, "adventure_counters": {}, + "voucher_rap": 0, "voucher_time_sand": 0, "node_type": "battle"}) # 真实战斗上下文(状态容器开域——dispatch 链受理) + var nc_rows: Array = [] # step_row 采集盒(lambda 捕引用容器——捕获变量赋值不出闭包) + orch_nc.step_row.connect(func(r): nc_rows.append(r)) + # heal 负向行表值 power=6(极性字典行值——负向 power 走表值不走快照)→ halve 50% ceil=3 + orch_nc._grant_routed({"cell_type": "heal", "power": 10}, "negative", + "mine_hit", Vector2i.ZERO, 0, "", "", 50) + # 受理判据=惊巢反制效果行落 step_row(halve 分支先于 dispatch 发行——flat 行 _dispatch_row + # 恒返 false,返回值不承载受理语义;行发行即分支受理) + var nc_hit := "" + for r in nc_rows: + if String((r as Dictionary).get("display_name", "")).contains("惊巢反制"): + nc_hit = String((r as Dictionary).get("display_name", "")) + break + Kit.check("锋:惊巢反制 mine_hit 减半分支受理(wa12 前提——halve_pct=50 效果行落 step_row)〔wa12〕", + nc_hit != "") + Kit.check("惊巢反制效果行==「惊巢反制——危险格挖掘惩罚减半(6→3)」零「挖雷」(u13 换代)〔wa12〕", + nc_hit == "惊巢反制——危险格挖掘惩罚减半(6→3)" and not nc_hit.contains("挖雷")) # 守侧五场景(契约 12):编排 rig——begin_battle 携 sense_guard,授予段 _grant_seg._judge_seal 直注 var orch = load("res://core/battle_orchestrator.gd").new(_db, _fresh_rng(777001)) Kit.check("守侧 rig:战斗编排启动(tree_rules=sense_guard 携带)", diff --git a/game/tests/unit/presentation/player_copy_sweep_test.gd b/game/tests/unit/presentation/player_copy_sweep_test.gd new file mode 100644 index 00000000..882d3d61 --- /dev/null +++ b/game/tests/unit/presentation/player_copy_sweep_test.gd @@ -0,0 +1,174 @@ +# tests/unit/presentation/player_copy_sweep_test.gd — 玩家面文案清扫直建断言宿主(迭代36 卡A) +# 断言面 = 玩家面字符串字面量换代钉值(u1–u13 钉值表——分类词/渠道旧话/「雷」族禁字清零): +# ① hero 面板两段头(专长/属性值提升——同屏分类撞车消除) +# ② 奖励节点三选一头部(冒险牌库/渠道分流三机制事实,零「同权重/同上限/提升池」旧话) +# ③ 专长卡兜底(feat_card_text 空行兜底「专长\n???」) +# ④ 图例封印行(标空/受挖掘惩罚,零「插错/挖雷」) +# ⑤ 状态槽来源名(mine_hit→危险格——tooltip 消费半) +# ⑥ 结算来源名(settlement 表值经 ConfigDB 真装载+battle_log 行消费半) +# ⑦ 已开危险格字形(glyph「危」+plate+RED_MINE——BoardView rebuild 真实刷新路径) +# ⑧ 源 token 扫描一:board_view.gd 零 "雷" 字面量(哨兵族统一钉住) +# ⑩ 源 token 扫描二:battle_orchestrator.gd 玩家面字符串字面量零「挖雷」(注释剥离=行首 # 判) +# 扫域口径:玩家面字符串字面量(契约 2 保留面——注释/内部常量名与注记/测试消息串不入域)。 +# 协议照单元侧惯例:extends SceneTree+Kit.check 主通道;--script 模式无 autoload, +# ConfigDB 显式实例化构造注入(统一流 master 种子 42 沿源)。 +# 运行:godot --headless --path game --script res://tests/unit/presentation/player_copy_sweep_test.gd +extends SceneTree + +const Kit := preload("res://tests/unit/test_kit.gd") +const ConfigDBScript := preload("res://core/config_db.gd") +const AutoPlayer := preload("res://sim/auto_player.gd") # 自动玩家策略单一实现(夹具开域驱动) + +var _db + + +func _init() -> void: + _db = ConfigDBScript.new() + var load_ok: bool = _db.load_tables("res://data/tables", true) + if not load_ok: + push_error("[player_copy_sweep_test] 表加载返回失败——诊断如下(红由具体断言落)") + for d in _db.diagnostics(): + if d.severity == "error" or d.severity == "fatal": + push_error(" [%s] %s: %s" % [d.severity, d.category, d.message]) + _sweep_hero_panel() + _sweep_narrator() + _sweep_legend() + _sweep_source_names() + _sweep_board_glyph() + _sweep_token_scans() + quit(Kit.finish("player_copy_sweep_test")) + + +# ① C 区两段头(u1/u2——直建 hero 面板,adventure_feats_test ⑦c 族同构) +func _sweep_hero_panel() -> void: + var panel = load("res://presentation/views/hero_info_panel_view.gd").new() + var feat_head := _first_label(panel._feat_section) + var curio_head := _first_label(panel._curio_section) + Kit.check("C 区专长段头==「专长」(u1 换代——段头文案钉值)〔wa1〕", + feat_head != null and feat_head.text == "专长") + Kit.check("C 区属性值提升段头==「属性值提升」(u2 换代——同屏两段同标撞车消除)〔wa1〕", + curio_head != null and curio_head.text == "属性值提升") + + +# ②③ 奖励节点头部+专长卡兜底(u3/u11——静态直调) +func _sweep_narrator() -> void: + var narrator = load("res://presentation/battle_narrator.gd") + var head := String(narrator.node_curio_header()) + Kit.check("奖励点头部含「冒险牌库」(u3 机制事实①同库)〔wa4〕", head.contains("冒险牌库")) + Kit.check("奖励点头部含「属性值提升与补给」(u3 机制事实②渠道分流)〔wa4〕", head.contains("属性值提升与补给")) + Kit.check("奖励点头部含「同库分流」(u3 机制事实②措辞)〔wa4〕", head.contains("同库分流")) + Kit.check("奖励点头部零「同权重」旧话(u3 清零)〔wa4〕", not head.contains("同权重")) + Kit.check("奖励点头部零「同上限」旧话(u3 清零)〔wa4〕", not head.contains("同上限")) + Kit.check("奖励点头部零「提升池」旧话(u3 清零)〔wa4〕", not head.contains("提升池")) + Kit.check("专长卡兜底==「专长\n???」(u11 换代——数据缺行真实兜底路径)〔wa3〕", + String(narrator.feat_card_text({})) == "专长\n???") + + +# ④ 图例封印行(u4——直建 LegendOverlay 读 ENTRIES 常量) +func _sweep_legend() -> void: + load("res://presentation/views/legend_overlay.gd").new() # 直建(_init(rap_cost) 现签名) + var seal_row: Array = [] + for e in load("res://presentation/views/legend_overlay.gd").ENTRIES: + if String(e[1]) == "封印": + seal_row = e + break + Kit.check("图例封印行存在(wa5 夹具前提)〔wa5〕", not seal_row.is_empty()) + Kit.check("图例封印行含「标空」(u4——与「标中」成对)〔wa5〕", String(seal_row[2] if seal_row.size() > 2 else "").contains("标空")) + Kit.check("图例封印行含「受挖掘惩罚」(u4 雷字清零换代)〔wa5〕", String(seal_row[2] if seal_row.size() > 2 else "").contains("受挖掘惩罚")) + Kit.check("图例封印行零「插错」(u4 旗族遗留词干清零)〔wa5〕", not String(seal_row[2] if seal_row.size() > 2 else "").contains("插错")) + Kit.check("图例封印行零「挖雷」(u4 雷字清零)〔wa5〕", not String(seal_row[2] if seal_row.size() > 2 else "").contains("挖雷")) + + +# ⑤⑥ 来源名双源(u5/u6——状态槽消费半+表值 ConfigDB 真装载+battle_log 行消费半) +func _sweep_source_names() -> void: + var row_view = load("res://presentation/views/status_slot_row.gd").new() + row_view.set_effects([{"effect_id": "alert", "source_class": "mine_hit", + "display_name": "危险格受击", "remaining_settlements": 2, "round_index": 1}]) + var slot: PanelContainer = null + for child in row_view.get_children(): + if child is PanelContainer and child.has_meta("eff"): + slot = child + break + Kit.check("状态槽 mine_hit 效果行在座(wa6 夹具前提)〔wa6〕", slot != null) + if slot != null: + row_view._on_slot_hover(slot) + Kit.check("状态槽来源注记==「危险格」(u5 换代——meta tooltip 消费半)〔wa6〕", + row_view._tip_label.text.contains("来源:危险格")) + row_view._hide_tip() + Kit.check("结算表 source_label_map[mine_hit]==「危险格」(u6 换代——ConfigDB 真装载)〔wa7〕", + String(_db.settlement.source_label_map.get("mine_hit", "")) == "危险格") + var log_view = load("res://presentation/views/battle_log_view.gd").new(_db) + var row_txt := String(log_view._format_row({"type": "effect", "source_class": "mine_hit", + "display_name": "挖掘反噬", "value": -5, "pool_value": -5, "round_index": 1})) + Kit.check("战斗日志 mine_hit 来源行含「(危险格)」(u6 消费半——_format_row 生产路径)〔wa7〕", + row_txt.contains("(危险格)")) + + +# ⑦⑧ 已开危险格字形族(u7/u8/u9——BoardView rebuild 真实刷新路径+源 token 扫描) +func _sweep_board_glyph() -> void: + var board_generator = load("res://core/board_generator.gd").new() + var b = board_generator.generate(1, 0, _mk_stream("board_gen", 4242), _db) + var ae = load("res://core/action_economy.gd").new(_db) + var ms = load("res://core/minesweeper_rules.gd").new(_db, ae) + ms.install_board(b) + ae.notify_round_start() + # 邻接雷定位(挖掘面规则=须邻接英雄——novice 驱动开域至危险格入挖掘射程;上限 60 步防冻态) + var mine_cell := Vector2i(-1, -1) + var glow_steps := 0 + while glow_steps < 60: + for c in b.neighbors8(ms.hero_pos()): + if b.is_mine(c) and not ms.is_revealed(c): + mine_cell = c + break + if mine_cell.x >= 0: + break + var act: Dictionary = AutoPlayer.novice_action(ms) + if String(act.get("kind", "stop")) == "stop": + break + var op := String(act.get("kind", "")) + if op == "seal": + op = "seal_place" + ms.settle_batch([{"action": op, "cell": act.get("cell")}]) + glow_steps += 1 + Kit.check("棋盘夹具含挖掘射程内未开危险格(wa8 前提——实测 %d 步)〔wa8〕" % glow_steps, mine_cell.x >= 0) + if mine_cell.x >= 0: + ms.settle_batch([{"action": "dig", "cell": mine_cell}]) # 挖开危险格(真实规则路径——受击开格) + var view = load("res://presentation/views/board_view.gd").new() + view.rebuild(b, ms) # theme_projection_test.gd:200 先例式直建(缺素材程序化回退态——fresh checkout 冒烟同纪律) + var u: Node2D = view._cells[mine_cell] + Kit.check("已开危险格 glyph==「危」(u7 换代)〔wa8〕", u.glyph == "危") + Kit.check("已开危险格 plate==true(u9 牌载哨兵包含项随动)〔wa8〕", u.plate == true) + Kit.check("已开危险格 glyph_color==RED_MINE(u7 墨色不随字形换代漂移)〔wa8〕", + u.glyph_color == UiKit.RED_MINE) + + +# ⑧⑩ 源 token 扫描(注释剥离=行首 # 判——契约 2 保留面不入域) +func _sweep_token_scans() -> void: + var board_src := FileAccess.get_file_as_string("res://presentation/views/board_view.gd") + Kit.check("board_view.gd 源零 \"雷\" 字面量(u7/u8/u9 哨兵族统一钉住)〔wa8〕", + board_src.count("\"雷\"") == 0) + var orch_src := FileAccess.get_file_as_string("res://core/battle_orchestrator.gd") + var orch_hits := 0 + for line in orch_src.split("\n"): + if line.strip_edges().begins_with("#"): + continue + if line.contains("挖雷"): + orch_hits += 1 + Kit.check("battle_orchestrator.gd 玩家面字符串字面量零「挖雷」(u13——注释保留面不入域)〔wa12〕", + orch_hits == 0) + + +func _first_label(node: Node) -> Label: + for child in node.get_children(): + if child is Label: + return child + return null + + +func _mk_stream(stream_name: String, scope: int) -> RandomNumberGenerator: + # stream_name = 调用点自述标签(不参与派生),保留参数零改调用方(theme_projection_test 同构) + var rg := RandomNumberGenerator.new() + var svc = load("res://core/rng_service.gd").new() + svc.set_master_seed(42) + rg.seed = svc.derive_at(scope) + return rg diff --git a/game/tests/unit/presentation/theme_projection_test.gd b/game/tests/unit/presentation/theme_projection_test.gd index f78df38f..9f00b3df 100644 --- a/game/tests/unit/presentation/theme_projection_test.gd +++ b/game/tests/unit/presentation/theme_projection_test.gd @@ -1,7 +1,8 @@ # tests/unit/presentation/theme_projection_test.gd — 呈现投影与主题单元测试宿主 -# 断言面 = 十套(批 B 断言迁移自 smoke_all.gd,G11 施工组;十套按源编排序迁入): +# 断言面 = 十一套(批 B 断言迁移自 smoke_all.gd,G11 施工组;十套按源编排序迁入+开场行后缀尾接协议扩段): # 未开格类型微光(作用集恒等/九型归组封闭/零玩法写入——board_view 微光数据面) # 战斗日志视图行格式化(行协议 9 类呈现层同步闸——真人试玩卡断 bug 回归) +# 开场行后缀尾接协议(level_open_line 第五参布尔直控「(含面板加成)」尾接——签名零变) # UI 全局主题(main_theme.tres 挂载/类型覆盖/滑轨两段对比/退役实现零残留/中文字体防御) # UI 按钮五态矩阵(四工厂五态样式盒逐实例 override) # UI 全局纹理过滤(工程 Nearest+平滑渐变件 LINEAR 白名单+光柱 alpha 结构) @@ -42,6 +43,7 @@ func _init() -> void: # 套执行序沿源编排序(_run_core_smoke 序 + _smoke_meta 薄壳尾序) _smoke_type_glow() _smoke_battle_log_view() + _smoke_level_open_line() _smoke_global_theme() _smoke_button_five_states() _smoke_texture_filter() @@ -420,6 +422,19 @@ func _smoke_battle_log_view() -> void: view.parsed_text().length() > before) +# ---- 开场行后缀尾接协议(迭代36 wc1 静态半——level_open_line 第五参布尔直控尾接,签名零变; +# 该函数此前零既有断言,尾接行为由此钉住)---- +func _smoke_level_open_line() -> void: + var NarratorScript := load("res://presentation/battle_narrator.gd") + Kit.check("开场行 boosted=true 尾接「(含面板加成)」〔wc1〕", + String(NarratorScript.level_open_line("第一层", 42, 5, 30, true)).contains("(含面板加成)")) + Kit.check("开场行 boosted=false 零后缀(白板局不带)〔wc1〕", + not String(NarratorScript.level_open_line("第一层", 42, 5, 30, false)).contains("(含面板加成)")) + Kit.check("开场行种子/两值带入不受后缀位影响(数值带入回归)〔wc1〕", + String(NarratorScript.level_open_line("第一层", 42, 5, 30, true)).contains("种子 42") + and String(NarratorScript.level_open_line("第一层", 42, 5, 30, true)).contains("攻击 5 / 生命 30")) + + # ---- UI 全局主题冒烟(需求卡片 §7 验收清单 V1-V6)---- # V1 挂载覆盖 / V2 滑轨两段 / V3 预盖件运行时证词 / V4 旧形态零残留 / # V5 回归(本冒烟全绿即回归网)/ V6 中文字体防御(挂主题后 default_font 顶掉 custom_font 也不丢字形) diff --git a/game/tests/unit/presentation/ui_font_asset_test.gd b/game/tests/unit/presentation/ui_font_asset_test.gd index 5ade37d4..b4b6f1bb 100644 --- a/game/tests/unit/presentation/ui_font_asset_test.gd +++ b/game/tests/unit/presentation/ui_font_asset_test.gd @@ -17,6 +17,7 @@ func _init() -> void: if f != null: Kit.check("中文字形在集(攻/雷/旗/封)", f.has_char("攻".unicode_at(0)) and f.has_char("雷".unicode_at(0)) \ and f.has_char("旗".unicode_at(0)) and f.has_char("封".unicode_at(0))) + Kit.check("中文字形在集(危——已开危险格字形换代前置,迭代36 u12/wa9)", f.has_char("危".unicode_at(0))) Kit.check("ASCII/数字字形在集(A/0/%)", f.has_char("A".unicode_at(0)) and f.has_char("0".unicode_at(0)) and f.has_char("%".unicode_at(0))) var sz := 18 var w_cn: float = f.get_string_size("战斗胜利", HORIZONTAL_ALIGNMENT_LEFT, -1, sz).x From e74656b228971e278ba943b1409b661505b8a592 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Sat, 24 Oct 2026 08:04:03 +0800 Subject: [PATCH 2/5] =?UTF-8?q?docs(=E8=BF=AD=E4=BB=A336=E4=BA=A7=E5=87=BA?= =?UTF-8?q?=E7=89=A9):=20=E5=AE=9E=E7=8E=B0=E6=8A=A5=E5=91=8A/=E4=BB=A3?= =?UTF-8?q?=E6=8B=8D=E6=9D=BF=E8=BF=BD=E8=AE=A4=E5=8D=95/=E5=8F=8D?= =?UTF-8?q?=E5=93=BA=E7=82=B9=E6=B8=85=E5=8D=95=E8=90=BD=E4=BD=8D=E2=80=94?= =?UTF-8?q?=E2=80=94=E5=8D=A1C+=E5=8D=A1A=20=E5=85=A8=E5=AF=B9=E7=85=A7?= =?UTF-8?q?=E8=A1=A819=E8=A1=8C=E5=AE=A1=E8=AE=A1=E9=80=9A=E8=BF=87?= =?UTF-8?q?=EF=BC=88=E7=8B=AC=E7=AB=8B=E5=AD=90=E4=BB=A3=E7=90=86=E4=B8=89?= =?UTF-8?q?=E4=BB=B6=E5=8F=98=E5=BC=82=E6=8A=BD=E6=9F=A5=E5=A4=8D=E7=8E=B0?= =?UTF-8?q?=EF=BC=89=EF=BC=9Bgolden=20=E5=8A=A0=E4=B8=A5=E5=8F=A3=E5=BE=84?= =?UTF-8?q?=E8=A7=A6=E5=8F=91=E5=88=A4=E5=A4=B1=E6=95=88=E5=A4=84=E7=90=86?= =?UTF-8?q?=EF=BC=88=E5=AD=98=E9=87=8F=E6=BC=82=E7=A7=BB=E5=BD=92=E5=9B=A0?= =?UTF-8?q?=E5=AE=9E=E6=B5=8B=EF=BC=9Amaster=E5=90=8C=E8=AF=BB=E6=95=B0?= =?UTF-8?q?=EF=BC=89=EF=BC=9B11=E5=AF=B9=E6=8B=8DNEED-APPROVAL=E5=AD=98?= =?UTF-8?q?=E9=87=8F=E7=95=99=E7=97=95=EF=BC=9B=E5=8D=A1=E2=91=A2=E5=81=9C?= =?UTF-8?q?=E6=9C=BA=E5=91=88=E6=8A=A5=EF=BC=88=E6=96=AD=E8=A8=80=E5=AE=BF?= =?UTF-8?q?=E4=B8=BB=E9=9A=8Fgame/smoke=E9=80=80=E5=BD=B9=E5=8F=98?= =?UTF-8?q?=E8=B4=A8=E2=80=94=E2=80=94=E7=A7=BB=E4=BA=A4=E5=86=99=E5=8D=A1?= =?UTF-8?q?=E6=AE=B5=E9=87=8D=E9=94=9A=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- reports/iter36-代拍板追认单-2026-10-24.md | 24 ++++ ...er36-停机呈报-状态面板瘦身批-2026-10-24.md | 34 +++++ reports/iter36-反哺点-2026-10-24.md | 44 +++++++ reports/iter36-实现报告-2026-10-24.md | 116 ++++++++++++++++++ reports/iter36-验收审计-2026-10-24.md | 93 ++++++++++++++ 5 files changed, 311 insertions(+) create mode 100644 reports/iter36-代拍板追认单-2026-10-24.md create mode 100644 reports/iter36-停机呈报-状态面板瘦身批-2026-10-24.md create mode 100644 reports/iter36-反哺点-2026-10-24.md create mode 100644 reports/iter36-实现报告-2026-10-24.md create mode 100644 reports/iter36-验收审计-2026-10-24.md diff --git a/reports/iter36-代拍板追认单-2026-10-24.md b/reports/iter36-代拍板追认单-2026-10-24.md new file mode 100644 index 00000000..21d82987 --- /dev/null +++ b/reports/iter36-代拍板追认单-2026-10-24.md @@ -0,0 +1,24 @@ +# 迭代 36 代拍板追认单(2026-10-24) + +> 白名单内代拍项备案(授权发生在卡片审问阶段,实现只执行预授权);批后追认=确认,不是事后授权。超白名单的裁断零发生(唯一例外=「路径②驱动前提不成立」的处置,见 §末单独列示——该处置落在卡C 代拍板 2 的「既有快照喂入」预授权形态内,但所涉卡片前提缺陷已升级为停机呈报级事实移交)。 + +## 卡C(开场后缀营地灰字迷雾测点批) + +1. **迷雾三行行名与注释措辞**(白名单 1):「迷雾·记忆墨/合成底L1地牢/L2幽暗/L3深幽」——含关别与「归一后」口径 ✓。 +2. **路径③断点态构造细节**(白名单 2):最终采用「既有快照喂入」形态(`save_manager.set_breakpoint` 夹具→`_resume_from_menu` 唯一消费口),非自然落盘——**约束满足**:断点=选路停点形(bonus.atk=5 已入账、bonus_pending/bonus_result 皆空、node_id=-1);bonus 回填经 `resume_adventure` :151-154 真链(探针断言 run_ledger.bonus.atk==5 在座)。形态变更的动因(原「自然落盘」链路依赖的奖励节点三选一在现行 build 不可达)已列反哺点 ③ 与停机级事实移交。 +3. **三选一候选兜底循环**(白名单 3):未触发——路径②原设计停点不可达(同上);通关三选一 relic 兜底选择取 field_dressing(生存向),随驱动链一并废止。 +4. **取色断言定位锚**(白名单 4):文本锚(图鉴锁文「柜台结构位预留」/秘宝匣行容器子树内「未解锁」/形态「被动|主动·充能」/档位=UiKit.TREASURE_TIER_LABELS 值域/刷新行「三选一刷新」前缀)。 +5. **提交切分与分支命名**(白名单 5):单工作区 `g-plan2-iter36`、分支 `iter36`;实现一笔检查点提交+闸后产出物一笔(卡③停摆不提交)。 +6. **迷雾行值 L2 条款修订**(卡C §9 L2 预授权):手填行值与探针派生首跑失配(L2/L3 偏差 ~5/255,PIL↔Godot 导入解码系统差)→ 以探针派生值为准修行值(L2 (74,79,96)/L3 (71,75,99)),读数与依据在实现报告 §3。 + +## 卡A(文案与禁字清扫批) + +1. **u3 最终措辞**(白名单 1):「关卡奖励点\n从冒险牌库抽三选一(属性值提升与补给;与通关专长三选一同库分流):\n选择一件带走:」——三机制事实词齐、三旧话词零残留 ✓。 +2. **断言名与消息措辞、定位写法**(白名单 2):全部带〔wa#/wc#/sv#〕尾缀;取色/段头定位=文本锚+成员直引混合(sweep 宿主用成员 `_feat_section` 首子 Label,探针用文本锚)。 +3. **T2② 地图帽断言注值取法**(白名单 3):帽 Label 常在树(零加成隐藏不隐藏 Label 本体)——按子树文本锚直判,零注值。 +4. **源扫描注释剥离**(白名单 4):行首 `#` 判定式(strip_edges 后 begins_with("#") 跳行);board_view.gd 扫描为全源 `"雷"` 字面量计数(哨兵族钉住,无注释剥离必要——实查零命中含注释)。 +5. **提交切分**(白名单 5):与卡C 同笔检查点提交(ui_click_probe 断言面两卡共用文件,拆笔无净收益)。 + +## 超白名单事项(须用户知悉) + +- **路径②驱动前提不成立**(卡C):奖励节点三选一在现行 build 不可达(reward 节点直通占位、L1 零商店节点)——处置=改快照喂入形态(代拍板 2 形态内)+事实移交反哺点 ③/⑧+实现报告缺陷记录。若用户认「路径②须保留原设计」→ 需先立「奖励节点三选一实装」批,本卡 wc1 正向半随之重写卡。 diff --git a/reports/iter36-停机呈报-状态面板瘦身批-2026-10-24.md b/reports/iter36-停机呈报-状态面板瘦身批-2026-10-24.md new file mode 100644 index 00000000..3a656b7d --- /dev/null +++ b/reports/iter36-停机呈报-状态面板瘦身批-2026-10-24.md @@ -0,0 +1,34 @@ +# 停机呈报:迭代 36 卡③「状态面板瘦身批」开工检查不过——断言宿主变质(2026-10-24) + +- 呈报人:迭代 36 实现会话(/iter 36 开工检查第 2 步·自洽扫描③事实抽查) +- 性质:**上游缺陷移交**(写卡段),非提问;卡③停摆,不阻塞卡①卡②(本迭代已按卡C→卡A 顺序完成实现,见 `reports/iter36-实现报告-2026-10-24.md`) +- 待办清单对应条目:「状态面板瘦身——局内面板去跨关解锁内容、只留本局信息」(:73)——**状态回退拟稿**:条目状态由「已编迭代 36」回退「已写卡(断言宿主变质待重锚)」,缺口补记见 §3 + +## 1. 现状差在哪 + +需求卡片 `reports/需求卡片-状态面板瘦身批-2026-10-23.md`(v1.1)成文于 2026-10-23。2026-10-24 `game/smoke/` 目录退役合并入库(`16c6f136`——探针迁 `game/probe/`、`smoke_all.gd` 删除、断言面分流迁 `game/tests/` 各单测与 `system_test.gd`)。同会话的另两张卡(开场后缀/文案清扫)已按此换代重锚至 v1.2(`305f8ee6`),**卡③没有重锚**,其断言面计划仍指向已消亡的宿主: + +| 卡③引用 | 现状 | +|---|---| +| `game/smoke/smoke_all.gd:5727-5779`(`_status_panel_projection` 断言族——T1 的重写对象、§8.3 削弱申报对象、sv1–sv5/sv8 落点) | 文件已删除;`_status_panel_projection` 现址=`game/tests/unit/presentation/theme_projection_test.gd:737`(迁移账本 `game/tests/验证集清单.md` S61:去向=单元测试 theme_projection_test) | +| `game/smoke/ui_click_probe.gd:593-621`(T2 真实入口三断言挂段) | 文件迁 `game/probe/ui_click_probe.gd`(行号未核) | +| 文件结构图 §7 六行中两行(smoke_all/smoke/ui_click_probe)、§6 同步面表尾两行 | 同上失效 | + +T1 首步「实装冒烟 `_status_panel_projection` 换代(`smoke_all.gd:5727-5779` 重写)」按卡片原文无法执行——重写对象不存在。 + +## 2. 两种读法与建议案 + +- **读法甲(机械重锚)**:断言宿主迁移是机械事实(S61 记录了去向),实现段把 smoke_all 引用就地改指 theme_projection_test.gd:737 继续施工。 + - **不采纳**:宿主迁移不是行号漂移——smoke_all 的断言按域分装进了多个宿主(投影族落 theme_projection_test、真实入口探针落 probe 新址),削弱的旧断言族(工坊 12 行投影断言 :5744-5770)拆分到哪、保留哪半(§8.3 申报的区间边界)都要按新宿主重新划界;这属断言面重落,是写卡段职责(先例=卡A/卡C 的 v1.2 重锚走完整写卡批+grilling,commit `305f8ee6`)。实现段代改=越权裁断。 +- **读法乙(停卡退回写卡段)**:卡③停摆,写卡段出 v1.2——断言宿主全量重锚(投影族→theme_projection_test.gd 现址行号;真实入口三断言→`game/probe/ui_click_probe.gd` 现址挂段行号;§8.3 削弱申报按迁移后实况重写;§7 文件结构图/§6 同步面表尾两行同步订正),照卡A/卡C v1.2 先例走复核后合回,实现段续接。 + - **建议采乙**。卡③其余面(产品码改动设计、契约 1–6、接口块八键、sv6 三断言设计)经两处实查未发现除宿主迁移外的变质(status_panel.gd / main.gd `_status_data` :1070-1076 / 两角标 tooltip 三处 file:line 现值吻合),重锚范围仅断言宿主面。 + +## 3. 缺口补记拟稿(供写卡段/用户落笔) + +- 待办清单 :73 条目状态:「已编迭代 36」→「已写卡(断言宿主变质 2026-10-24 停机呈报,待 v1.2 重锚后重新编入)」。 +- 本迭代收口:卡③停摆不提交、不合并;**迭代总收口合并须等卡③闭合、或用户裁「拆批」(卡①卡②先行合回、卡③随重锚卡另批收口)**——按 iter 技能第 4 步停机规则呈报待裁。 + +## 4. 验证与没把握的 + +- 本呈报的宿主迁移事实以 `git log 16c6f136`、`game/tests/验证集清单.md` S61、`theme_projection_test.gd:737` 现址实查为凭(开工检查当日复核)。 +- 没把握的:迁移后 `_status_panel_projection` 在 theme_projection_test 内的断言族边界(哪些子断言随工坊段退役该删)未逐条核对——这正是写卡段 v1.2 要做的事,本呈报不代做。 diff --git a/reports/iter36-反哺点-2026-10-24.md b/reports/iter36-反哺点-2026-10-24.md new file mode 100644 index 00000000..adefb4fa --- /dev/null +++ b/reports/iter36-反哺点-2026-10-24.md @@ -0,0 +1,44 @@ +# 迭代 36 反哺点清单(2026-10-24) + +> 实现批产出物。每条=哪个文件哪一节、改成什么、依据。用户会话裁决后归文档批写入 `design/` 并把该条清掉。本批零 GDD 陈述违反——以下全部为「实现与现实差异的文档随动」与「卡片事实缺陷的登记」,逐条待裁。 + +## 卡C(开场后缀营地灰字迷雾测点批) + +### ① `design/art/art-bible.md` §6.5 :395 迷雾记忆态在册复算值换代 +- 改成:在册复算值「≈6.7:1(地板纹理均值 #453020 与灰罩合成后…)」→「归一后(真实渲染面)三关 5.71–5.93:1(合成底 L1 #4C4038/L2 #3F4147/L3 #3E4048;行值与素材随动,测点=ui_contrast_probe 迷雾段)+归一口径注(装载亮度归一是现行生产路径——board_view `_normalize_floor_luma`,原图均值≠渲染面)」。 +- 依据:卡C 契约 3(甲·归一后基准)+F9/F10;**读数修正**:写卡会话 PIL 复算(5.65–5.75:1)与探针派生首跑在 L2/L3 系统性偏差约 5/255(PIL↔Godot 导入解码差),已按卡内 L2 条款以探针派生值为准修手填行值——文档应记派生口径值(5.71/5.83/5.93),不是写卡值。 + +### ② `待办清单.md`「qa/ 实现报告收尾·孤儿遗留七项」条目处置 +- 改成:第⑥项(TG_* 装饰带)按用户 2026-10-24 裁整条注销;第②③④项随卡C 落地销号;第⑦项(商店房截图纳屏)留存——条目本体保留至 ⑦ 处置;状态推进「已写卡(本卡路径)→已实现(迭代 36)」。 +- 依据:卡C §6 +来源条目状态机。 + +### ③ 卡C 卡片前提缺陷登记(新增反哺点——开工检查后施工中新发现) +- 改成:在「qa/ 实现报告收尾」条目或需求卡片修订记录(由文档批裁量落点)登记:**F5「属性值提升经三选一/商店兑付入 bonus(奖励节点渠道=属性值提升+补给)」中「奖励节点渠道」半句与现行 build 不符**——地图 reward 节点=零兑付直通占位(`level_runner.gd` `_enter_node` 注记「奖励点内容未定…零兑付直通」),L1 全模板零商店节点(level.tres L1 行 20 battle/5 boss/5 rest/5 reward,零 shop);现行局内 bonus 唯一可达来源=L2 商店货架(pool 面 kind_id 购买)。卡C 路径②原设计(奖励节点拿属性卡→下一关开场行)据此不可驱动,实现改快照喂入形态(代拍板 2)。 +- 依据:实现实跑证据(probe24/25 选路诊断+level.tres 实查);此差异同时影响后续任何涉及「奖励节点三选一」的驱动设计。 + +## 卡A(文案与禁字清扫批) + +### ④ `design/gdd/ux/node-reward.md` :14 旧头部全文引换代 +- 改成::14 在册逐字引「关卡奖励点\n从提升池抽三选一(与通关三选一同池同权重同上限):\n选择一件带走:」→ 新头部全文「关卡奖励点\n从冒险牌库抽三选一(属性值提升与补给;与通关专长三选一同库分流):\n选择一件带走:」;并连带该节「文案换代归内容批,已呈报」句销号(本卡即该内容批)。 +- 依据:卡A u3(§6 ①)。 + +### ⑤ `design/gdd/systems/表现与支撑/教学引导.md` :58 旧句换代 +- 改成:「(安全格零奖励、危险格吃挖雷惩罚)」→「(安全格零奖励、危险格受挖掘惩罚)」;同句「插错可左键直接挖开」→「标空可左键直接挖开」(u4 全句换代——「标空」与「标中」成对)。 +- 依据:卡A u4(§6 ②)。 + +### ⑥ `game/tests/golden/README.md` 加严口径纪律句(待用户裁) +- 改成与否待裁:本批 golden 判定采用加严口径「棋盘三图 changed>0 且差异像素落棋盘裁剪区=按判失效处理(显式呈报后定重制),不改写 2% 容差基线语义」——是否写入 README 纪律句由用户裁(严于现行「远低于阈值=有效」)。 +- 依据:卡A 契约 4(§6 ③)。 + +### ⑦ `待办清单.md` 卡A 两条来源条目批后退役 +- 改成:「现役 UI/文案两处自相冲突」(2026-10-09)+「玩家面残留禁字清扫——图例/明细/日志/字形五处」(2026-10-20)两整条随本卡落地退役;状态推进「已实现(迭代 36)」。 +- 依据:卡A §6 ④。 + +### ⑧ 卡A 卡片前提核对补记(合并进 ③ 同一登记或分列,由文档批裁量) +- 改成:卡A T2③ 断言「三选一停点弹层头部含『冒险牌库』」在本次实跑未捕获到「关卡奖励点」停点(奖励节点直通占位——同 ③ 依据;实跑出现的唯一停点=通关三选一,头部为「第 1 层通过!…」独立文案,不经 node_curio_header)。断言保留为条件断言(拦截面=未来奖励节点停点实装后的真实头部),**拦截能力未证**如实申报(test-conventions §6 口径)。 +- 依据:probe 探针实跑记录(ui_click_probe 主循环 curio 分支捕获条件)。 + +## 卡③(状态面板瘦身批——停摆) + +### ⑨ 停机呈报移交 +- 见 `reports/iter36-停机呈报-状态面板瘦身批-2026-10-24.md`:待办清单 :73 条目状态回退拟稿+卡③ v1.2 重锚范围。归写卡段/用户处置,不在本清单展开。 diff --git a/reports/iter36-实现报告-2026-10-24.md b/reports/iter36-实现报告-2026-10-24.md new file mode 100644 index 00000000..a2baaccc --- /dev/null +++ b/reports/iter36-实现报告-2026-10-24.md @@ -0,0 +1,116 @@ +# 迭代 36 实现报告(2026-10-24)——卡C 开场后缀营地灰字迷雾测点批+卡A 文案与禁字清扫批 + +- 工作区:`../g-plan2-iter36`(分支 `iter36`,单工作区串行);卡序=roadmap 行格内顺序(卡C→卡A→卡③) +- 卡③(状态面板瘦身批)**开工检查不过停摆**:断言宿主随 `game/smoke/` 退役变质——停机呈报见 `reports/iter36-停机呈报-状态面板瘦身批-2026-10-24.md`;本报告只覆盖卡C/卡A +- 执行形态:整迭代单工作区、各卡串行(三卡 §0 均未声明可并行) + +## 0. 卡C 状态总表 + +| 完成定义项 | 状态 | 凭据 | +|---|---|---| +| 1. 后缀条件落地+三路径断言 | **达成(②形态代拍板 2 等价)** | ui_click_probe 白板反向绿+续玩重建正向绿(bonus.atk=5 回填、pct=0、ov=0)+wc3 回归绿;theme_projection 静态半三断言绿 | +| 2. 营地六处 STONE_DEEP+零残留 | 达成 | 源 token 扫描(struct probe)绿+六处取色断言绿+contrast 探针在册两行回归绿 | +| 3. 迷雾三行+派生校验+注释随动 | 达成 | contrast 探针 40 对全绿+派生校验绿;board_cell_unit.gd:270 注释随动 | +| 4. 对照表逐行断言绿+变异表 | 达成 | §3 变异记录表 | +| 5. 批末代码域闸全绿+读数对拍+三产出物 | 见 §6 | 闸回执 | + +**卡C 前提缺陷(重要)**:路径②原设计「奖励节点三选一拿属性卡→下一关开场行带后缀」在现行 build 不可驱动——L1 全模板零商店节点(level.tres L1 行:20 battle/5 boss/5 rest/5 reward,零 shop),reward 节点=零兑付直通占位(level_runner `_enter_node` 注记「奖励点内容未定…零兑付直通」,实跑选路诊断印证)。现行局内 bonus 唯一可达来源=L2 商店货架,但 autoplay 到店生存性 ~42% 量级(MC perfect 锚)不可作为验收驱动。**处置**:②③改「既有快照喂入」形态(代拍板 2 预授权:断点=选路停点形钉值——bonus.atk=5 已入账、bonus_pending/bonus_result 皆空;经 `_resume_from_menu` 唯一消费口;`resume_adventure` :151-154 真链回填由探针断言 run_ledger.bonus.atk==5 钉住)。条件加算区两项的机制面(回填→level_started→条件求值→尾接)与 fresh 路径同式全链被测;fresh 正向拦截能力未证,如实申报。已列反哺点 ③。 + +## 1. 卡A 状态总表 + +| 完成定义项 | 状态 | 凭据 | +|---|---|---| +| 1. 钉值表 12 单元落地+玩家面六词零残留 | 达成 | 六词扫 presentation/core 玩家面字面量零命中(.tres 命中均为 `;` 注释保留面);字形「危」+两哨兵同批 | +| 2. 对照表逐行断言绿+变异表 | 达成 | §4 变异记录表 | +| 3. 内部注记面零改动 | 达成 | RED_MINE 常量/注释、「雷」内部语、术语表 :128 所指全未触碰(git diff 核) | +| 4. golden 判定动作 | 见 §6 | 渲染闸读数 | +| 5. 批末闸+读数对拍+三产出物 | 见 §6 | 闸回执 | + +**卡A 前提缺陷(较轻)**:wa4 探针半(三选一停点弹层头部断言)本次实跑未捕获到「关卡奖励点」停点——实跑唯一停点=通关三选一(头部=「第 1 层通过!…」独立文案,不经 `node_curio_header`)。断言保留为条件断言,**拦截能力未证**(test-conventions §6 口径);静态半(sweep 断言②+u3 变异红)拦截力已证。已列反哺点 ⑧。 + +## 2. 实际改动—验收对照表(卡C) + +> 与卡片 §8.2 的差异处加粗。断言尾缀可 grep。 + +| 行 | 改动实况 | 验收测试实况 | 真实入口实况 | 撤销/变异证明实况 | +|---|---|---|---|---| +| wc1 新开路径 | main.gd:608-616 条件扩两项(六项布尔逐字);theme_projection_test `_smoke_level_open_line` 扩段三断言;ui_click_probe 白板反向断言 | 三静态断言绿+白板反向绿〔wc1〕 | `_start_adventure_from_menu()`(:493 现址)真实开局;静态直调 | 变异:条件全项删→白板仍绿(反向钉住);正向半见 wc2 行(fresh 正向不可驱动——缺陷记录) | +| wc2 续玩路径 | 同 hunk+探针断点续玩断言 | 续玩重建开场行带后缀〔wc2〕 | `_resume_from_menu()`(:519 唯一消费口);断点=set_breakpoint 快照喂入(代拍板 2):选路停点形、bonus.atk=5、pending/result 皆空 | **撤销:回退条件 hunk→路径②③红、①仍绿(实测 mut3)** | +| wc3 既有口径回归 | 同 hunk 回归面 | run 1(滑条 400/200=局外加成)开场行带后缀〔wc3〕 | 调试滑条 pct 门控(现役 DebugOverlay) | 变异:条件旧四项全删→wc3 红(实测 mut4) | +| wc4 营地六处换色 | camp_overlay_view.gd :167/:417/:476/:509/:513/:525 换 STONE_DEEP;源内零裸 STONE | 取色六锚断言+token 扫描断言〔wc4〕 | `open_camp_via_savesel` 真实入口 | 变异::417 回 STONE→取色红(mut2 实测 12 处「未解锁」行)+struct 扫描红(mut5 实测);判别式放宽→误红(mut6 边界证明) | +| wc5 迷雾测点三行+派生校验 | ui_contrast_probe 迷雾单行→三行+`_norm_floor_avg` 派生+校验断言;**行值按探针派生首跑修值(L2 (74,79,96)/L3 (71,75,99)——卡 §9 L2 预授权)** | 三行 ≥4.5:1+行值 vs 派生 ±2/255〔wc5〕 | `UiKit.load_tex` 真装载+`BoardView._normalize_floor_luma` 生产静态直呼 | 变异①行值回原图基准→校验红;②调偏 +4→红;③target_l 0.10→0.05→红(三注入实测) | +| wc6 注释随动(非行为) | board_cell_unit.gd :270 复算值换代(5.71–5.93:1) | 件核 | — | 件核 | +| wc7 反哺点(非行为) | 反哺点清单卡C 三条+新增 ③ | 件核对表 | — | 件核 | + +## 3. 卡C 变异验证记录表 + +| # | 注入 | 最窄闸 | 结果 | +|---|---|---|---| +| T3-① | L1 行值回原图基准 (69,48,32) | contrast 探针 headless | 红:行值失真(派生 (111,77,51))✓ | +| T3-② | L1 行值调偏 +4 (115,77,51) | 同上 | 红 ✓ | +| T3-③ | 派生 target_l 0.10→0.05 | 同上 | 红:三关全失真 ✓ | +| T3-还原 | 精确还原 | 同上 | PASS 40 对 ✓ | +| T2-①(mut3) | 条件删加算区两项 | ui_click_probe 全程 | 红:②③失守;①仍绿(反向钉住)✓ | +| T2-②(mut4) | 条件旧四项全删 | 同上 | 红:wc3 回归失守+②③失守;①绿 ✓ | +| T1-①(mut2) | camp :417 回 STONE | ui_click_probe | 红:取色断言 12 处「未解锁」行 ✓ | +| T1-②(mut5) | 同注入 | ui_struct_probe | 红:源扫描裸 STONE ✓ | +| T1-③(mut6) | 扫描判别式放宽(匹配 _DEEP) | ui_struct_probe | 误红(判别边界证明)✓ | + +## 4. 实际改动—验收对照表+变异记录(卡A) + +| 行 | 改动实况 | 验收测试实况 | 变异证明实况 | +|---|---|---|---| +| wa1 C 区段头×2 | hero_info_panel_view.gd :73/:93 换词 | sweep 断言①+探针 C 区断言 | u1 回退→sweep 红;u2 回退→sweep 红(批量注入实测);探针半=u1 回退→探针红(mut2 实测) | +| wa2 地图加成帽 | level_map_screen.gd :171 换词 | 探针地图段(文本锚) | u10 回退→探针红(专项 mut7 实测,唯一失败项) | +| wa3 专长卡兜底 | battle_narrator.gd :142 换词 | sweep 断言③ | u11 回退→sweep 红(专项实测) | +| wa4 奖励点头部 | battle_narrator.gd :156 换新(L1 措辞微调:注释「共享同池」→「共享同库」随动) | sweep 断言②(三事实+三旧话清零)+探针条件断言(拦截能力未证——见 §1 缺陷) | u3 回退→sweep 红(专项实测 6 断言全红) | +| wa5 图例行 | legend_overlay.gd :11 换词 | sweep 断言④+探针图例段 | u4 回退→sweep 红+探针红(批量+mut2 实测) | +| wa6 状态槽来源名 | status_slot_row.gd :40 换词 | sweep 断言⑤(tooltip 消费半) | u5 回退→sweep 红(批量实测) | +| wa7 结算明细来源名 | settlement.tres :14 换值 | sweep 断言⑥(ConfigDB 真装载+battle_log `_format_row` 行消费半) | u6 回退→sweep 红(表值半+行半双红,批量实测) | +| wa8 危险格字形族 | board_view.gd :730/:778/:782 同批换;:776 注释随动(L1) | sweep 断言⑦(glyph/plate/glyph_color 三半)+⑧源扫描 | u7 回退→glyph 红+扫描红(批量);u8 单独回退→扫描红(solo 实测——卡片预判的「glyph_color 红」未现,实际红面=扫描,如实记录);u9 单独回退→plate 红+扫描红(solo 实测) | +| wa9 字体「危」在集 | ui_font_asset_test.gd 增行 | 断言绿 | 参数换 U+20000→红(判别力实测) | +| wa10 golden 判定(非行为) | 见 §6 | 读数+结论 | 不适用 | +| wa11 反哺点(非行为) | 反哺点清单卡A 五条 | 件核对表 | 件核 | +| wa12 惊巢反制日志行 | battle_orchestrator.gd :797 换词(core 触碰面=此一行字符串) | forge 锋段⑨(step_row 采集盒)+sweep ⑩扫描 | u13 回退→forge ⑨红+扫描红(专项实测) | + +卡A 变异补充记录:批量注入(u1/u2/u4/u5/u6/u7/u8/u9/u11/u13 十件同批)→sweep 12 断言红,逐件归因(u8 被 u7 掩蔽→补 solo 注入)。新宿主 `player_copy_sweep_test.gd` 入 run_all 清单(expect=0),**实跑 24 checks 全绿**;restore 后全绿复核两轮。 + +## 5. 实机目检总清单(登记不阻塞) + +1. 开场行后缀观感:「(含面板加成)」出现时机的直觉合理性(卡C §8.2)。 +2. 营地灰字观感:六处深化后的层次(卡C §8.2)。 +3. 危险格「危」字观感:字形密度/可读性对「雷」的替代(卡A §8.2)。 +4. 图例新句通顺:「标空…受挖掘惩罚」读感(卡A §8.2)。 + +## 6. 批末闸与读数对拍 + +- 改前读数(master HEAD 基线口径):theme_projection 153 checks/ui_contrast_probe 38 对/player_copy_sweep 无(本批新建)/其余四探针存量基线见 `game/tests/.gates/baseline.json`。 +- 改后读数:theme_projection 156(+3 wc1)/player_copy_sweep 24 全绿/ui_contrast_probe 40 对(+3 迷雾行 −1 旧单行)+派生校验绿/ui_click_probe 问题=0(PROBE_SEED=7)/ui_struct_probe 全绿。 +- 批末 `tools/run_all_gates.sh --code`:见闸回执(§6.1)。 +- golden 判定(卡A 契约 4):见 §6.2。 + +### 6.1 闸回执读数 + +- 批末 `tools/run_all_gates.sh --code`(回执 commit=9468c70e,与分支尖对齐):**8 闸全绿**(00 冒烟/01 系统/02 单元与集成/03 探针 --click/04 探针 --full/05 渲染闸/09 静态扫描/10 熵增闸,fingerprint 全 green、changed_vs_baseline 空)。 +- **11 回放确定性对拍 = NEED-APPROVAL(存量红·对基线零新增,未实跑)**——触面判定因 settlement.tres 数值/结算域文件在 diff 内;本批对该文件只改一处文案值(u6「踩雷」→「危险格」),零数值改动。按硬规(MC/模拟跑批发起前必须用户批准)未发起;是否批准补跑归用户。 +- 改前改后读数对拍:见 §6 首段(theme 153→156/sweep 0→24 全绿/contrast 38→40 对+派生校验新面/click probe 问题=0/struct 全绿)。 + +### 6.2 golden 判定(卡A 契约 4——加严口径触发,呈报待裁) + +- 渲染闸读数 ×4:棋盘 L1 **changed=0.13%**/L2 **0.11%**/L3 **0.10%**/menu_boot **0.00%**(容差 ≤16 差/≤2%,闸判 OK)。 +- **加严口径触发**:三图 changed>0 且差异像素落棋盘裁剪区(L1 裁剪区内 911 px,x:239–585/y:240–394,其中 893 px 单通道差 >60=结构差非抗锯齿噪声)→ 按契约 4 加严句**本批按判失效处理**——不写 README 判有效加注(README 零改动),重制与否显式呈报待裁(反哺点 ⑥)。 +- **归因证据(存量漂移,非本批引入)**:master HEAD(无本批改动)同探针同基线复测读数逐值相同(0.13/0.11/0.10%),差异像素同位同量——漂移在批前已在;且 F7 实查四基线图零 RED_MINE 雷字朱像素,本批字形改动(雷→危)不可能触及基线像素。判读:加严句针对的「本批字形差被容差吞掉」情形不成立,但按字面执行判失效呈报;是否重制基线归用户(重制件须复检雷字——F7 注)。 + +## 7. 遗留读数义务与申报 + +1. wa4 探针半与 wc1 fresh 正向:**拦截能力未证**(前提不可驱动——反哺点 ③/⑧)。 +2. u8 变异红面=扫描(卡片预判 glyph_color 半红未现)——如实记录,判据未改。 +3. ui_click_probe/battle_log_view 存量噪声:`_round_rows`(Array[Dictionary])append String 的运行期 TypedArray 报错(绿跑亦复现,~388 次/全程)——**存量、非本批引入**,登记待办由后续批处置。 +4. 探针窗口化全程偶发静默中断(约 1/6 次实跑无输出退出 exit=1)——存量环境噪声(多次复跑同脚本可绿),登记观测。 + +## 8. 独立验收审计 + +- 审计报告:`reports/iter36-验收审计-2026-10-24.md`(未参与实现的子代理执行;只读对照表/diff/测试文件,自跑三件变异抽查:wa6/wc5/wa12 注入→红→还原→复绿→工作区零残留)。 +- **总判:通过**——wc1–wc7+wa1–wa12 共 19 对照行零缺漏;未申报削弱零(31 行删除逐行归因:27 行产品码钉值+迷雾单行换代 3 行〔§8.3 申报〕+打印消息+头注)。 +- 三件例外均申报/授权在案:wc1 fresh 正向与 wa4 探针半「拦截能力未证」(前提不可驱动——反哺点 ③/⑧)、L2/L3 行值修值(卡C §9 L2)、路径②并入快照喂入形态(代拍板 2+缺陷移交)。 +- 审计留档观察:`_norm_floor_avg` 对部分缺位纹理 continue 跳过(仅全缺必红)——素材齐盘态无实际削弱,备查。 diff --git a/reports/iter36-验收审计-2026-10-24.md b/reports/iter36-验收审计-2026-10-24.md new file mode 100644 index 00000000..6f127fc4 --- /dev/null +++ b/reports/iter36-验收审计-2026-10-24.md @@ -0,0 +1,93 @@ +# 迭代 36 独立验收审计报告(2026-10-24)——卡C 开场后缀营地灰字迷雾测点批+卡A 文案与禁字清扫批 + +## 头部:范围、方法与自跑清单 + +- **审计对象**:迭代 36 实现提交 `9468c70e`(19 文件,+489/−31),卡C「开场后缀营地灰字迷雾测点批」(对照表 wc1–wc7)+卡A「文案与禁字清扫批」(对照表 wa1–wa12)。卡③停摆不在范围(另有停机呈报)。 +- **审计人立场**:未参与实现的独立子代理,只读证据、自跑抽查、出本报告;除本报告外零写盘。审计后 `git status -- game/` 复核零残留(工作区仅余实现批四份未跟踪 reports/ 产出物,与本审计前一致)。 +- **输入合同**:`reports/需求卡片-开场后缀营地灰字迷雾测点批-2026-10-24.md` v1.2 §8.2/§8.3/§9 L2/§10;`reports/需求卡片-文案与禁字清扫批-2026-10-24.md` v1.2 §8.2/§8.3。实现报告对照表与变异记录表**不采信**,全部自行核(核后与其叙述相符处才记「与报告相符」)。 +- **方法**:①两张卡 §8.2 逐行核对——断言存在性(grep 尾缀〔wc#/wa#〕)+真实入口未绕改动点+报告差异项授权/申报核对;②变异抽查三件亲自复现(注入→红→`git checkout -- ` 还原→绿→`git status` 复核干净);③削弱检查——实现提交 diff 全部 31 行删除行逐行归因,对照两卡 §8.3 申报。 +- **自跑清单**(Godot=`D:/code/godot/Godot_v4.7.2-stable_win64_console.exe`,工作目录 `D:\code\g-plan2-iter36\game`): + 1. `--headless --script res://tests/unit/presentation/player_copy_sweep_test.gd`(基线绿:24 checks 全绿) + 2. `--headless res://probe/ui_contrast_probe.tscn`(基线绿:40 对全绿+迷雾三关派生校验绿) + 3. `--headless --script res://tests/unit/core/meta_progression_forge_test.gd`(还原后绿:135 checks) + 4. 三件变异注入实跑(见变异抽查实录节) + 5. 静态核:`git show 9468c70e` 逐 hunk 读;`grep` 核 `level_open_line` 签名(五参 bool,未变)、`TILE_FLOOR_FMT`/`TILE_FLOOR_VARIANTS`/`_normalize_floor_luma` 常量与静态函数在位(board_view.gd :68/:69/:998)、条件六项布尔逐字(main.gd `_on_adventure_level_started`)。 + +## 一、逐行核对表——卡C(wc1–wc7) + +| 行 | 判定 | 依据 | +|---|---|---| +| wc1 开场行后缀·新开路径 | **通过(例外申报在案)** | ①静态半:`theme_projection_test.gd` `_smoke_level_open_line` 三断言〔wc1〕在库且绿(true 尾接/false 零后缀/种子两值带入回归);②白板反向:`ui_click_probe.gd` 开场行路径①断言在库(滑条 `reset_sliders()` 显式归零后 `_start_adventure_from_menu()` 真实开局,日志零「(含面板加成)」);③正向半:路径②原设计(奖励节点拿属性卡→下一关)经实查不可驱动,实现并入路径③快照喂入形态。**授权核对**:代拍板 2 白面字面只覆盖「路径③构造细节」,路径②形态改换超出其字面;但实现报告 §0 申报、反哺点 ③ 登记卡片 F5 前提缺陷(L1 零商店节点+reward 节点零兑付直通占位)、停机呈报随附——「或申报」口径成立,且 fresh 机制面(回填→level_started→条件求值→尾接)由续玩路径全链承载+条件 hunk 变异红(mut3,见下注)。fresh 正向拦截能力未证=如实申报事项,非缺漏。 | +| wc2 开场行后缀·续玩路径 | **通过** | 断言在库:探针构造 `bp_ol` 快照(bonus atk=5 已入账、无 bonus_pending/bonus_result 键——缺键钳 0=**选路停点形**钉值满足;node_id=-1 选路停点)→ `main.save_manager.set_breakpoint()` → `main._resume_from_menu()` 唯一消费口 → 断言 run_ledger.bonus.atk==5(resume_adventure :151-154 真链回填钉住)+重建开场行带「(含面板加成)」〔wc1 正向/wc2〕。信号链核:_resume_from_menu→resume_adventure(两键皆空走 :236→_start_current_level)→ level_started → `_on_adventure_level_started`——未绕改动点。 | +| wc3 既有口径回归 | **通过** | 探针首局(滑条 400/200=pct>0 局外加成)开场行带后缀断言〔wc3〕在库,走 `_start_adventure_from_menu()` 真实开局+日志全文本判(该局为探针首局、日志无先前行——判定面干净)。变异 mut4(条件旧四项全删→红)未亲自复现,采信实现报告记录(注入面与断言面一一对应,机制同 mut3 族)。 | +| wc4 营地六处换色 | **通过** | ①六处源位 :167/:417/:476/:509/:513/:525 全换 STONE_DEEP(diff 逐行核);②取色断言在库且读法正确——`get_theme_color("font_color") != UiKit.STONE_DEEP`,经 `open_camp_via_savesel` 真实入口开营地后取(文本锚定位=代拍板 4 白名单内;「未解锁」锚收窄到 `_workshop_holder` 子树避免误捕树行/锻造行——定位写法属 L2 自由);③源 token 扫描断言在 `ui_struct_probe.gd`(STONE 计数==STONE_DEEP 计数判别式)。变异 mut2(:417 回 STONE→取色红)/mut5(同注入→扫描红)/mut6(判别式放宽→误红边界证明)未亲自复现,采信实现报告记录+判别式静态读核(计数对判别式确能区分裸 STONE 与 _DEEP)。 | +| wc5 迷雾测点三行+派生校验 | **通过(行值修值有授权条款+实测记录在案)** | 三行在库(L1 (111,77,51)/L2 (74,79,96)/L3 (71,75,99)),派生校验 `_fog_derived_check`(±2/255)+`_norm_floor_avg`(路径经 `BoardView.TILE_FLOOR_FMT` 常量、归一经 `BoardView._normalize_floor_luma` 静态直呼——契约 3 两禁均守)在库且基线绿。L2/L3 行值与卡片钉值 (79,85,102)/(76,81,105) 不同=卡C §9 L2「手填行值与探针派生首跑失配时的行值修订」预授权,实测记录在案(反哺点 ①+实现报告 §2:PIL↔Godot 解码系统差约 5/255)。三行 ≥4.5:1 由基线跑 PASS 40 对证实。 | +| wc6 注释随动(非行为) | **通过** | `board_cell_unit.gd:270` 段注释换代实核在 diff 内:「归一后(真实渲染面)三关 5.71–5.93:1……见 ui_contrast_probe 迷雾段」——含「归一后」口径与三关值,非行为行,与迷雾绘制本体零触碰。 | +| wc7 反哺点清单(非行为) | **通过(落点形态注记)** | 反哺点清单卡C 三条在库:①art-bible §6.5 换代(含读数修正依据)、②待办清单条目处置、③卡片前提缺陷登记(新增)。卡片 §6 第三行(roadmap 迭代行编入笔)未单列落点——该义务已由 iter-plan 段编入提交(`0e9bc36f`/`30197255` 迭代行已写)履行,实现批以新增前提缺陷条置换该行,实质同步义务闭合,不判缺漏。 | + +**mut3 未复现说明**:卡C 主变异(回退 main 条件 hunk→②③红、①仍绿)涉及窗口化 ui_click_probe 全程跑(约分钟级+已知 1/6 概率静默中断噪声),按审计指令采信实现报告 mut3 记录;补偿核:条件布尔与断言的依赖关系经代码读核(wc1②③ 断言的日志判定只可能因 boosted 布尔含加算区两项而绿——探针该时点 pct=0、新档 ov=0),机制依赖成立。 + +## 二、逐行核对表——卡A(wa1–wa12) + +| 行 | 判定 | 依据 | +|---|---|---| +| wa1 C 区段头×2 | **通过** | sweep 断言①两断言〔wa1〕在库(直建 hero 面板,断 `_feat_section`/`_curio_section` 首 Label text==「专长」/「属性值提升」);探针 C 区断言〔wa1〕在库(`main.info_panel` 可见态取——真实入口);u1/u2 源位 :73/:93 在 diff 内换词。变异(u1/u2 回退→sweep 红+u1 探针红)未亲自复现,采信报告记录(与三件实跑变异同族同法)。 | +| wa2 地图加成帽 | **通过** | 探针地图段断言〔wa2〕在库(地图屏真实开屏后文本锚取「属性值提升」帽);u10 :171 在 diff 内。锚形态=文本锚(L2 定位写法自由);报告申报该断言 mut7 专项实测唯一失败项后复绿。 | +| wa3 专长卡兜底 | **通过** | sweep 断言③〔wa3〕在库:`feat_card_text({})`==「专长\n???」——数据缺行真实兜底路径直调;u11 :142 在 diff 内。 | +| wa4 奖励点头部 | **通过(拦截能力未证=如实申报)** | sweep 断言②六断言〔wa4〕在库(「冒险牌库」「属性值提升与补给」「同库分流」三事实+零「同权重/同上限/提升池」);u3 :156 在 diff 内(u3 措辞与卡片钉值逐字一致)。探针半为**条件断言**(仅捕获到含「关卡奖励点」头部时判)——实跑未捕获奖励点停点,拦截能力未证=实现报告 §1+反哺点 ⑧ 已申报(已知事实不算未申报削弱)。 | +| wa5 图例行 | **通过** | sweep 断言④五断言〔wa5〕在库(含「标空」「受挖掘惩罚」零「插错」「挖雷」);探针图例段断言在库(AP 条图例钮真实信号开层后 `legend_overlay.ENTRIES` 判);u4 :11 在 diff 内。 | +| wa6 状态槽来源名 | **通过(亲自复现变异,见实录①)** | sweep 断言⑤〔wa6〕在库:直建 status_slot_row 喂 `{"source_class":"mine_hit"}` 真实效果行结构→`_on_slot_hover`→tooltip 文本含「来源:危险格」;u5 :40 在 diff 内。 | +| wa7 结算明细来源名 | **通过** | sweep 断言⑥〔wa7〕双半在库:`_db.settlement.source_label_map["mine_hit"]`(ConfigDB 真装载)+直建 battle_log_view `_format_row` 行含「(危险格)」(生产消费路径 :133-136 同构);u6 `settlement.tres:14` 在 diff 内,与 u5 同批同值(契约 1 双源钉住成立)。 | +| wa8 危险格字形族 | **通过** | sweep 断言⑦三半+⑧源扫描〔wa8〕在库:BoardView 直建走 `board_generator.generate`→`minesweeper_rules` 真实规则路径挖掘危险格→`rebuild` 真实刷新路径(theme_projection_test :200 先例式,缺素材程序化回退态与卡片口径一致),断 glyph==「危」/plate==true/glyph_color==RED_MINE;⑧ `board_view.gd` 源零 `"雷"` 字面量(u7/u8/u9 哨兵族统一钉住);u7/u8/u9 三处 :730/:778/:782 同批在 diff 内、:776 注释随动(L1 白名单内)。u8 solo 变异红面=扫描(卡片预判 glyph_color 半红未现)=实现报告 §7.2 如实记录、判据未改,不算缺漏。 | +| wa9 字体「危」在集 | **通过** | `ui_font_asset_test.gd` 增行〔wa9〕在 diff 内;旧四字断言(攻/雷/旗/封)原样(diff 上下文行核);判别力变异(参数换 U+20000→红)采信报告记录。 | +| wa10 golden 判定(非行为) | **通过(判失效路径按契约 4 执行)** | 读数×4 在报告 §6.2(0.13/0.11/0.10/0.00%);加严口径触发→按判失效处理、README 零改动(核:git diff 无 README)、重制与否显式呈报待裁(反哺点 ⑥)+归因证据(master HEAD 复测同读数=存量漂移)。判定先于动作、未先动后判——契约 4 正序执行。 | +| wa11 反哺点清单(非行为) | **通过(落点形态注记)** | 反哺点清单卡A ④⑤⑥⑦⑧五条在库(node-reward/教学引导/golden 纪律句待裁/待办清单退役/卡片前提补记);roadmap 编入笔同 wc7 注记(iter-plan 提交已履行)。 | +| wa12 惊巢反制日志行 | **通过(亲自复现变异,见实录③)** | forge 锋段⑨双断言〔wa12〕在库:编排器真实构造(begin_battle 真实战斗上下文)+`step_row` 信号接线+直调 `_grant_routed` 减半分支,断 display_name==「惊巢反制——危险格挖掘惩罚减半(6→3)」零「挖雷」;sweep ⑩源扫描(注释剥离)在库;u13 :797 在 diff 内(core 触碰面仅此一行字符串——diff 核实)。 | + +## 三、变异抽查实录(三件亲自复现) + +**实录① 卡A wa6(u5 注入)** + +- 注入:`game/presentation/views/status_slot_row.gd:40` `"mine_hit": "危险格"` → `"mine_hit": "踩雷"`(sed 精确替换,diff 核仅此一处)。 +- 命令:`--headless --path . --script res://tests/unit/presentation/player_copy_sweep_test.gd` +- 红(输出摘录):`[FAIL] 状态槽来源注记==「危险格」(u5 换代——meta tooltip 消费半)〔wa6〕` +- 还原:`git checkout -- game/presentation/views/status_slot_row.gd` → 复跑 **all green (24 checks)**;`git status -- game/` 零残留。 + +**实录② 卡C wc5(L1 行值回原图基准)** + +- 注入:`game/probe/ui_contrast_probe.gd:125`(`_fog_derived_check` 行值表)`Color8(111, 77, 51)` → `Color8(69, 48, 32)`。 +- 命令:`--headless --path . res://probe/ui_contrast_probe.tscn` +- 红(输出摘录):`[contrast] 迷雾测点行值失真:L1地牢 行值 (69,48,32) vs 运行时归一派生 (111,77,51)(±2/255——素材更新须随动改行值)` → `[ui_contrast_probe] FAIL — 1 项越带(含静态段)` +- 还原:`git checkout -- game/probe/ui_contrast_probe.gd` → 复跑 **PASS — 对比度 40 对测点全绿……+迷雾三关派生校验绿**;`git status -- game/` 零残留。 +- 评注:注入点选在派生校验行值表(行值钉值面),证明「行值回原图基准必红」的变异①判据真实有拦截力;同法覆盖卡C T3-①。 + +**实录③ 卡A wa12(u13 注入)** + +- 注入:`game/core/battle_orchestrator.gd:797` 「危险格挖掘惩罚减半」→「挖雷惩罚减半」(sed 精确替换)。 +- 命令:`--headless --path . --script res://tests/unit/core/meta_progression_forge_test.gd` +- 红(输出摘录):`[FAIL] 惊巢反制效果行==「惊巢反制——危险格挖掘惩罚减半(6→3)」零「挖雷」(u13 换代)〔wa12〕` +- 还原:`git checkout -- game/core/battle_orchestrator.gd` → 复跑 **all green (135 checks)**;`git status -- game/` 零残留。 + +三件均满足「注入→最窄闸见红→精确还原→复绿→零残留」闭环。未亲自复现的其余变异(卡C mut2/5/6、mut3、mut4;卡A 十二注入中未抽查件)采信实现报告记录,其判据面均经静态读核与实跑同族推断,无发现虚报痕迹。 + +## 四、测试削弱检查结论 + +实现提交全部 31 行删除行逐行归因(`git show 9468c70e | grep '^-'"`): + +1. **产品码钉值行 27 行**——u1–u13 钉值表逐字+main 条件旧式三行+camp 六处 STONE+board_cell_unit 注释+board_view :776 注释随动:全部为两卡 §1 钉值表/契约明文授权的换代对象,非断言。 +2. **迷雾单行 3 行**(ui_contrast_probe 旧「迷雾·记忆墨/灰罩合成底」测点)——**卡C §8.3 申报的换代删除**,替换判据更严(三关归一基准+派生一致性校验),申报成立。 +3. **探针 PASS 打印行 1 行**——输出消息随断言面扩段更新,非断言、非闸判据。 +4. **theme_projection 头注 1 行**——「十套」→「十一套」注释行,非断言。 + +**专项核**:`theme_projection_test.gd` 只增不改(新增 `_smoke_level_open_line`+调用行+头注,既有断言零触碰);`ui_font_asset_test.gd` 旧四字断言原样、只增「危」行;`adventure_feats_test.gd` 未触(不在 19 文件清单);`ui_click_probe.gd`/`ui_struct_probe.gd`/`meta_progression_forge_test.gd`/`run_all.gd` 全为纯增行;零阈值放宽、零扫描面缩小、零真实入口换下层直调(探针新增段全走 `_start_adventure_from_menu`/`_resume_from_menu`/`open_camp_via_savesel`/图例钮/选路真链/`_grant_routed` 真实链)。**未申报削弱:未发现。** + +**一处观察(不判缺漏,随报告留档)**:`ui_contrast_probe._norm_floor_avg` 对缺位纹理 `continue` 跳过(仅 px==0 全缺时必红)——卡片契约 3 写「素材缺位=按闸预检处置拷贝齐盘,禁跳过派生校验」。实现仍执行派生校验、未跳过校验本体,部分缺位时以余量均值参与比对(大概率仍因均值漂移超 ±2 而红,但非构造保证)。闸预检+素材在盘现状下无实际削弱,属 L1/L2 级实现细节,登记备查。 + +## 五、总判 + +**通过。** + +- 卡C wc1–wc7、卡A wa1–wa12 逐行「通过/通过(例外申报在案)」,无缺漏行。 +- 例外三件均属「申报+授权在案」而非缺漏:①wc1 fresh 正向与 wa4 探针半「拦截能力未证」(前提不可驱动——反哺点 ③/⑧ 已登记);②L2/L3 行值修值(卡C §9 L2 条款+实测记录在案);③路径②形态改快照喂入(代拍板 2 授权路径③构造、路径②合并超出其字面但已申报为卡片前提缺陷,fresh 机制面由续玩路径全链承载)。 +- 三件变异亲自复现全红、还原全绿、工作区零残留;削弱检查零未申报项。 +- 闸回执(commit=9468c70e)8 闸 green;11 回放确定性对拍 NEED-APPROVAL 为存量红零新增、未实跑——是否补跑归用户,不在本审计判据内。 From 477a73e763cd5a82abf423e6e530e3606fe1266b Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Sat, 24 Oct 2026 08:35:39 +0800 Subject: [PATCH 3/5] =?UTF-8?q?iter(36=E5=8D=A1=E2=91=A2):=20=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E9=9D=A2=E6=9D=BF=E7=98=A6=E8=BA=AB=E2=80=94=E2=80=94?= =?UTF-8?q?=E5=85=AB=E9=94=AE=E6=8A=95=E5=BD=B1=E6=8D=A2=E4=BB=A3=EF=BC=8B?= =?UTF-8?q?=E6=9C=AC=E5=B1=80=E4=BF=A1=E6=81=AF=E9=9D=A2=E6=9D=BF=E4=B8=89?= =?UTF-8?q?=E6=AE=B5=E6=96=B0=E8=A3=85=E4=B8=A4=E6=AE=B5=E9=80=80=E5=BD=B9?= =?UTF-8?q?=EF=BC=8B=E4=B8=A4=E8=A7=92=E6=A0=87tooltip=E6=8D=A2=E4=BB=A3?= =?UTF-8?q?=EF=BC=8B=E6=96=AD=E8=A8=80=E9=9D=A2=E6=8D=A2=E4=BB=A3=EF=BC=88?= =?UTF-8?q?theme=E6=8A=95=E5=BD=B1=E6=97=8F=E4=B9=9D=E6=96=AD=E8=A8=80?= =?UTF-8?q?=E6=89=8B=E6=8B=BC=E5=A4=B9=E5=85=B7=EF=BC=8B=E6=8E=A2=E9=92=88?= =?UTF-8?q?=E7=9C=9F=E5=AE=9E=E5=85=A5=E5=8F=A3=E4=B8=89=E6=96=AD=E8=A8=80?= =?UTF-8?q?=E5=AF=B9=E6=8B=8D=E7=8B=AC=E7=AB=8Boracle=EF=BC=89=EF=BC=9B?= =?UTF-8?q?=E5=8F=98=E5=BC=82=E9=AA=8C=E8=AF=81=E6=8C=89=E7=94=A8=E6=88=B7?= =?UTF-8?q?P0=E7=A6=81=E4=BB=A4=E9=9B=B6=E6=89=A7=E8=A1=8C=EF=BC=88?= =?UTF-8?q?=E6=AD=A3=E5=90=91=E9=AA=8C=E6=94=B6=E6=B5=8B=E8=AF=95=E7=85=A7?= =?UTF-8?q?=E8=B7=91=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- game/presentation/main.gd | 26 +++- game/presentation/views/board_zone_view.gd | 2 +- game/presentation/views/level_map_screen.gd | 2 +- game/presentation/views/status_panel.gd | 119 ++++++------------ game/probe/ui_click_probe.gd | 40 ++++++ .../presentation/theme_projection_test.gd | 103 +++++++-------- 6 files changed, 152 insertions(+), 140 deletions(-) diff --git a/game/presentation/main.gd b/game/presentation/main.gd index 2c79f97b..23dad591 100644 --- a/game/presentation/main.gd +++ b/game/presentation/main.gd @@ -1068,18 +1068,34 @@ func _on_settings_closed() -> void: # ---- 局内状态面板·只读(入口角标在两屏装配段;开合与数据喂入归组合根)---- -# 状态面板快照投影(数据只读同源:每次开层实时读 MetaProgression 访问器现值拼装—— +# 状态面板快照投影(数据只读同源:每次开层实时读访问器现值拼装—— # 快照仅服务单次开层生命周期(open_status 重建各行),跨开层零持有;零写调用。 -# 状态面板零锻造段) +# 八键换代(迭代 36 卡③):跨关解锁概览退役(营地/生涯承载)——只留本局信息; +# ledger 直取下标(键缺失=致命,禁 .get 缺省留门——fail-fast 红线);禁 null 守卫软回退 +# (两入口开层时 runner 恒在役——状态面板卡 F3 证据链) func _status_data() -> Dictionary: return { "balance": meta.keystone_balance(), - "workshop": meta.treasure_shop_status(), # 秘宝匣 12 件解锁投影 - "tree": meta.tree_camp_rows(), - "fork_groups": meta.tree_fork_groups(), + "hero_hp": int(run_scope.runner.hero_hp), # LevelRunner 属性面(_ledger 直取下标——键缺失=致命) + "max_hp": run_scope.runner.effective_max_hp(), + "atk": run_scope.runner.effective_atk(), + "ac": run_scope.runner.effective_ac(), + "gp": int(run_scope.runner.gp), + "curios": _status_curios(), + "feats": HudPresenter.feat_holdings_lines(db, run_scope.runner.feats_held()), } +# 秘宝行映射(inventory_projection 现役投影逐行取 name/used 两键——受理序沿库存投影; +# 禁第二份投影:件名换算已在 inventory_projection 收口,此处只做键裁剪) +func _status_curios() -> Array: + var out: Array = [] + for row in (_treasure_ui.inventory_projection() as Array): + var r: Dictionary = row + out.append({"name": String(r.get("name", "")), "used": bool(r.get("used", false))}) + return out + + func _open_status_panel() -> void: _open_modal(status_panel) # 模态栈单口(开层前排空排播队列——既有层同款行为,目检确认项) status_panel.open_status(_status_data()) diff --git a/game/presentation/views/board_zone_view.gd b/game/presentation/views/board_zone_view.gd index 4c2d6861..68573246 100644 --- a/game/presentation/views/board_zone_view.gd +++ b/game/presentation/views/board_zone_view.gd @@ -55,7 +55,7 @@ func _init() -> void: corner.add_theme_constant_override("separation", 8) status_badge = UiKit.button("状态", 0.0) # 角落小件豁免宽度基准 status_badge.add_theme_font_size_override("font_size", UiKit.FS_HINT) - status_badge.tooltip_text = "查看钥石与永久强化进度(只读)" + status_badge.tooltip_text = "查看本局冒险进度(只读)" status_badge.visible = false # 显式初始化——非对局期不误现(可见性归组合根同步) corner.add_child(status_badge) corner_wrap.add_child(corner) diff --git a/game/presentation/views/level_map_screen.gd b/game/presentation/views/level_map_screen.gd index a0de2bf1..bd95e641 100644 --- a/game/presentation/views/level_map_screen.gd +++ b/game/presentation/views/level_map_screen.gd @@ -104,7 +104,7 @@ func _init() -> void: # 信号连接归组合根(视图零连接)) status_badge = UiKit.button("状态", 0.0) status_badge.add_theme_font_size_override("font_size", UiKit.FS_HINT) - status_badge.tooltip_text = "查看钥石与永久强化进度(只读)" + status_badge.tooltip_text = "查看本局冒险进度(只读)" status_badge.size_flags_vertical = Control.SIZE_SHRINK_CENTER top.add_child(status_badge) _title = UiKit.label(TITLE_FORK,UiKit.FS_SUB, true, UiKit.WARM) diff --git a/game/presentation/views/status_panel.gd b/game/presentation/views/status_panel.gd index 5976d3ae..fbeb8842 100644 --- a/game/presentation/views/status_panel.gd +++ b/game/presentation/views/status_panel.gd @@ -1,14 +1,16 @@ ## VERTICAL SLICE - NOT FOR PRODUCTION -## 局内状态面板·只读:钥石余额与进度概览(入口=两屏角标,本类不含入口)。 +## 局内状态面板·只读:本局信息面板(钥石总量+本局四件:带的秘宝/已获专长/当前属性/现有 GP)。 +## 跨关解锁概览(秘宝匣解锁态/天赋树已购)由营地/生涯承载——本面板不再镜像(迭代 36 卡③)。 ## 账面快照由组合根 open_status 喂入;进出零动效直开直关;构成件全部经 UiKit 工厂产出——禁裸色值自建样式。 class_name StatusPanel extends Control signal closed -var _balance_badge: UiKit.KeystoneBadge # 钥石余额徽(badge 工厂件——Q10 甲) -var _workshop_holder: VBoxContainer -var _tree_label: Label +var _balance_badge: UiKit.KeystoneBadge # 钥石总量徽(badge 工厂件——Q10 甲) +var _attrs_label: Label +var _curios_label: Label +var _feats_label: Label func _init() -> void: @@ -19,11 +21,11 @@ func _init() -> void: var title := UiKit.label("状态",UiKit.FS_TITLE, true, UiKit.INK_TITLE) title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER box.add_child(title) - var hint := UiKit.label("查看本局钥石与永久强化进度(只读——购买请回营地)",UiKit.FS_HINT, false, UiKit.INK_HINT) + var hint := UiKit.label("查看本局冒险进度(只读)",UiKit.FS_HINT, false, UiKit.INK_HINT) hint.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER box.add_child(hint) - # 钥石余额行(badge 工厂件——与营地顶栏钥石徽同形制) + # 钥石总量行(badge 工厂件——与营地顶栏钥石徽同形制) var bal_row := HBoxContainer.new() bal_row.alignment = BoxContainer.ALIGNMENT_CENTER box.add_child(bal_row) @@ -31,13 +33,20 @@ func _init() -> void: bal_row.add_child(_balance_badge) box.add_child(HSeparator.new()) - _section_head(box, "道具工坊 · 解锁态") - _workshop_holder = _section_holder(box) + _section_head(box, "本局状态") + _attrs_label = UiKit.label("",UiKit.FS_HINT, false, UiKit.INK_LABEL) + _attrs_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + box.add_child(_attrs_label) - _section_head(box, "天赋树 · 已购概览") - _tree_label = UiKit.label("",UiKit.FS_HINT, false, UiKit.INK_LABEL) - _tree_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART - box.add_child(_tree_label) + _section_head(box, "带的秘宝") + _curios_label = UiKit.label("",UiKit.FS_HINT, false, UiKit.INK_LABEL) + _curios_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + box.add_child(_curios_label) + + _section_head(box, "已获专长") + _feats_label = UiKit.label("",UiKit.FS_HINT, false, UiKit.INK_LABEL) + _feats_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + box.add_child(_feats_label) box.add_child(HSeparator.new()) var close_row := HBoxContainer.new() @@ -56,80 +65,26 @@ func _section_head(box: VBoxContainer, text: String) -> void: box.add_child(head) -func _section_holder(box: VBoxContainer) -> VBoxContainer: - var holder := VBoxContainer.new() - holder.add_theme_constant_override("separation", 4) - box.add_child(holder) - return holder - - # ---- 开层(组合根唯一调用口——快照随开随建,R4 单次开层生命周期口径)---- - +# 八键快照(组合根 _status_data 换代):balance/hero_hp/max_hp/atk/ac/gp/curios/feats—— +# 本局四件拼装在此,禁第二份投影(秘宝=inventory_projection 行映射、专长=feat_holdings_lines 现成行) func open_status(data: Dictionary) -> void: _balance_badge.set_text("钥石 %d" % int(data.get("balance", 0))) - _rebuild_workshop(data) - _rebuild_tree(data) - visible = true - - -# 道具工坊六行(解锁态文案与营地柜台同口径——退役/封存/已解锁/未解锁;进度=解锁判定量读数) -# 秘宝匣 12 行只读概览(数据源=treasure_shop_status 投影, -# 解锁态文案与营地柜台同口径;进度列=解锁混合制三形态:钥石价/条件进度拼接/已入手) -func _rebuild_workshop(data: Dictionary) -> void: - for c in _workshop_holder.get_children(): - c.queue_free() - for row in (data.get("workshop", []) as Array): - var line := HBoxContainer.new() - line.add_theme_constant_override("separation", 8) - var name_l := UiKit.label(String(row.get("display_name", "")),UiKit.FS_HINT, true, UiKit.INK) - name_l.custom_minimum_size = Vector2(120, 0) - name_l.clip_text = true - name_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS - line.add_child(name_l) - var unlocked := bool(row.get("unlocked", false)) - var prog_text := "已入手" - if not unlocked: - if String(row.get("mode", "")) == "keystone": - prog_text = "钥石 %d" % int(row.get("price", 0)) - else: - var parts: Array[String] = [] - for req_v in (row.get("reqs", []) as Array): - var req: Dictionary[Variant, Variant] = req_v as Dictionary - var lbl := String(UiKit.TREASURE_REQ_LABELS.get(String(req.get("key", "")), "条件")) - parts.append("%s %d/%d" % [lbl, int(req.get("progress", 0)), int(req.get("threshold", 0))]) - prog_text = "、".join(parts) - var prog_l := UiKit.label(prog_text,UiKit.FS_HINT, false, UiKit.INK_HINT) - prog_l.custom_minimum_size = Vector2(150, 0) - prog_l.clip_text = true - prog_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS - line.add_child(prog_l) - var st_l := UiKit.label("已解锁" if unlocked else "未解锁",UiKit.FS_HINT, true, UiKit.INK_LABEL) - st_l.size_flags_horizontal = Control.SIZE_EXPAND_FILL - line.add_child(st_l) - _workshop_holder.add_child(line) - - -# 天赋树已购概览(单条聚合文案——已购节点等级 + Fork 组激活侧;零已购=空态句) -func _rebuild_tree(data: Dictionary) -> void: - var owned: Array[String] = [] - for row in (data.get("tree", []) as Array): - var lvl := int(row.get("level", 0)) - if lvl > 0: - owned.append("%s Lv%d/%d" % [String(row.get("display_name", "")), lvl, int(row.get("cap", 1))]) - for fg in (data.get("fork_groups", []) as Array): - var active := String(fg.get("active_side", "")) - if active == "": - continue - var side_name := active - for sd in (fg.get("sides", []) as Array): - if String(sd.get("side", "")) == active: - side_name = String(sd.get("display_name", active)) - break - owned.append("%s·激活 %s" % [String(fg.get("display_name", "")), side_name]) - if owned.is_empty(): - _tree_label.text = "尚未购买任何节点(天赋树在营地主柜台)" + _attrs_label.text = "生命 %d/%d · 攻击 %d · 护甲 AC %d · GP %d" % [ + int(data.get("hero_hp", 0)), int(data.get("max_hp", 0)), int(data.get("atk", 0)), + int(data.get("ac", 0)), int(data.get("gp", 0))] + var curios: Array = data.get("curios", []) + if (curios as Array).is_empty(): + _curios_label.text = "未携带秘宝" else: - _tree_label.text = "已购 %d 项:%s" % [owned.size(), " · ".join(owned)] + var parts: Array[String] = [] + for row in curios: + var r: Dictionary = row + parts.append(String(r.get("name", "")) + ("(已用)" if bool(r.get("used", false)) else "")) + _curios_label.text = " · ".join(parts) + var feats: Array = data.get("feats", []) + _feats_label.text = "未获专长" if (feats as Array).is_empty() else " · ".join(feats) + visible = true func _emit_closed() -> void: diff --git a/game/probe/ui_click_probe.gd b/game/probe/ui_click_probe.gd index 36af9112..5ff14b2a 100644 --- a/game/probe/ui_click_probe.gd +++ b/game/probe/ui_click_probe.gd @@ -762,6 +762,46 @@ func _run_modal_probes_once() -> void: var bal_txt: String = main.status_panel._balance_badge.value_label.text if not bal_txt.begins_with("钥石 "): _problems.append("状态面板余额徽投影缺失(%s)" % bal_txt) + # ---- 本局信息面板换代断言(迭代36 卡③——真实入口三断言)---- + # ① 面板子树零旧段文案(跨关概览退役零残留) + var sv_labels: Array = [] + _collect_labels_by_text(main.status_panel, sv_labels) + for l in sv_labels: + if String(l.text).contains("道具工坊") or String(l.text).contains("天赋树"): + _problems.append("状态面板子树残留旧段文案「%s」(sv6①——零旧段失守)" % String(l.text)) + # ② 属性行全值 = 契约4 格式 % runner 五现值(main 侧投影值层真链路) + var rnr = main.run_scope.runner + if rnr == null: + _problems.append("状态面板开层时 runner 空指针(契约5——状态机缺陷,禁软回退)") + else: + var attr_expect := "生命 %d/%d · 攻击 %d · 护甲 AC %d · GP %d" % [ + int(rnr.hero_hp), rnr.effective_max_hp(), rnr.effective_atk(), + rnr.effective_ac(), int(rnr.gp)] + if String(main.status_panel._attrs_label.text) != attr_expect: + _problems.append("状态面板属性行投影失真(sv6②——期望 %s 实测 %s)" % [attr_expect, main.status_panel._attrs_label.text]) + # ③ _status_data() 直调对拍(sv7——键集恰八键+curios/feats 独立 oracle) + var sd: Dictionary = main._status_data() + var keys := ["balance", "hero_hp", "max_hp", "atk", "ac", "gp", "curios", "feats"] + var keys_ok: bool = (sd as Dictionary).size() == 8 + for k in keys: + keys_ok = keys_ok and (sd as Dictionary).has(k) + if not keys_ok: + _problems.append("_status_data() 键集≠八键(sv7——实测 %s)" % str((sd as Dictionary).keys())) + var oracle: Array = [] + for rec in rnr.treasure_inventory.query_inventory(Callable()): + var rr: Dictionary = rec + oracle.append({"name": String(main.db.treasure_item_row(String(rr.get("item_id", ""))).get("display_name", "")), + "used": bool(rr.get("used", false))}) + var sd_curios: Array = (sd as Dictionary).get("curios", []) + var curios_ok: bool = (sd_curios as Array).size() == (oracle as Array).size() + if curios_ok: + for i in (oracle as Array).size(): + curios_ok = curios_ok and String((sd_curios[i] as Dictionary).get("name", "")) == String((oracle[i] as Dictionary).get("name", "")) and bool((sd_curios[i] as Dictionary).get("used", false)) == bool((oracle[i] as Dictionary).get("used", false)) + if not curios_ok: + _problems.append("_status_data().curios 对拍独立 oracle 失守(sv7——实测 %s vs %s)" % [str(sd_curios), str(oracle)]) + var feats_expect: Array = load("res://presentation/hud_presenter.gd").feat_holdings_lines(main.db, rnr.feats_held()) + if str((sd as Dictionary).get("feats", [])) != str(feats_expect): + _problems.append("_status_data().feats 对拍 feat_holdings_lines 失守(sv7)") var resume_btn: Button = ProbeUiUtil.find_button(main.status_panel, "继续", "prefix") if resume_btn == null: _problems.append("状态面板关闭钮缺失(「继续」)") diff --git a/game/tests/unit/presentation/theme_projection_test.gd b/game/tests/unit/presentation/theme_projection_test.gd index 9f00b3df..dd71c5b6 100644 --- a/game/tests/unit/presentation/theme_projection_test.gd +++ b/game/tests/unit/presentation/theme_projection_test.gd @@ -746,63 +746,64 @@ func _smoke_diag_button_retired() -> void: dbg_src.contains('UiKit.button("转储")')) -# ---- 状态面板只读投影一致性(R4 数据只读同源:状态面板展示值逐项 == MetaProgression -# 访问器读数;投影拼装与 main._status_data 同构(同访问器族同序)。只读性连接巡检与零动效 -# 断言归 ui_struct_probe 呈现侧,本段只管数据面)---- +# ---- 状态面板·本局信息面板投影一致性(迭代36 卡③——八键快照手拼夹具:展示值逐项 == +# 快照钉值;投影拼装口径=组合根 _status_data 八键(数据只读同源)。只读性连接巡检与零动效 +# 断言归 ui_struct_probe 呈现侧,本段只管数据面;跨关解锁概览断言族随面板换代退役—— +# 替换判据=sv5 零旧段+八键投影断言(实现报告 §8.3 申报))---- func _status_panel_projection() -> void: - print("--- 状态面板·只读投影一致性(隔离档 smoke_meta_buy 复用)---") - _meta_smoke_cleanup() - var sv = _meta_new_save(_META_SMOKE_PATHS[0]) - var mp = load("res://core/meta_progression.gd").new(_db, sv) - # 已购态夹具:余额非零 + 秘宝匣首件解锁——投影可辨非全零白板 - sv.add_keystone(37) - sv.unlock_item("morning_order") - sv.write_to_disk() - # 投影拼装(main._status_data 同构)+ 视图喂入 - var data := {"balance": mp.keystone_balance(), "workshop": mp.treasure_shop_status(), - "tree": mp.tree_camp_rows(), "fork_groups": mp.tree_fork_groups()} + print("--- 状态面板·本局信息面板投影一致性(迭代36 卡③——八键快照手拼夹具,同构口径不依赖 runner)---") + _meta_smoke_cleanup() # 套件级夹具清场(生涯页 fixture 依赖此前置——旧状态段同款保留) + # 夹具(代拍板 4——任意非零可辨值,禁读 data/tables 现值、禁与属性行断言共值致误判掩盖) + var data := {"balance": 77, "hero_hp": 13, "max_hp": 29, "atk": 7, "ac": 11, "gp": 41, + "curios": [{"name": "青苔密卷", "used": false}, {"name": "锈蚀怀表", "used": true}], + "feats": ["疼痛记忆 ×2", "双管镐 ×1"]} var po = load("res://presentation/views/status_panel.gd").new() po.open_status(data) Kit.check("状态面板开层可见(open_status 直开——P4 甲零动效路径)", po.visible) - Kit.check("余额徽投影 = keystone_balance() 读数(%s)" % po._balance_badge.value_label.text, - String(po._balance_badge.value_label.text) == "钥石 %d" % mp.keystone_balance()) - var ws: Array = mp.treasure_shop_status() - var ws_ok: bool = po._workshop_holder.get_child_count() == ws.size() - var uikit_s = load("res://presentation/views/ui_kit.gd") - for i in mini(ws.size(), po._workshop_holder.get_child_count()): - var row: Dictionary = ws[i] - var lbls2: Array = [] - for ch in po._workshop_holder.get_child(i).get_children(): - if ch is Label: - lbls2.append(ch) - var unlocked: bool = bool(row.get("unlocked", false)) - var expect_prog := "已入手" - if not unlocked: - if String(row.get("mode", "")) == "keystone": - expect_prog = "钥石 %d" % int(row.get("price", 0)) - elif String(row.get("mode", "")) == "condition": - var parts: Array[String] = [] - for req_v2 in (row.get("reqs", []) as Array): - var req2: Dictionary = req_v2 as Dictionary - var lbl2 := String(uikit_s.TREASURE_REQ_LABELS.get(String(req2.get("key", "")), "条件")) - parts.append("%s %d/%d" % [lbl2, int(req2.get("progress", 0)), int(req2.get("threshold", 0))]) - expect_prog = "、".join(parts) - var expect_st2 := "已解锁" if unlocked else "未解锁" - ws_ok = ws_ok and lbls2.size() == 3 \ - and String(lbls2[0].text) == String(row.get("display_name", "")) \ - and String(lbls2[1].text) == expect_prog \ - and String(lbls2[2].text) == expect_st2 - Kit.check("秘宝匣 12 行投影 = treasure_shop_status() 逐项(名称/钥石价与条件进度/解锁态——首件解锁夹具在列)〔秘宝批 V19 呈现数据源〕", - ws_ok) - var owned := 0 - for trow in (mp.tree_camp_rows() as Array): - if int(trow.get("level", 0)) > 0: - owned += 1 - var tree_txt := String(po._tree_label.text) - Kit.check("天赋树概览聚合文案与已购节点数一致(零已购=空态句)", - tree_txt == "尚未购买任何节点(天赋树在营地主柜台)" or tree_txt.begins_with("已购 %d 项:" % owned)) + Kit.check("余额徽投影 = 快照 balance 读数(sv4 回归锚)〔sv4〕", + String(po._balance_badge.value_label.text) == "钥石 77") + Kit.check("属性行全值投影 = 契约4 钉值格式逐字(生命/上限/攻击/AC/GP 五现值)〔sv1〕", + String(po._attrs_label.text) == "生命 13/29 · 攻击 7 · 护甲 AC 11 · GP 41") + Kit.check("秘宝行投影 = 受理序 join+已用件「(已用)」尾缀〔sv2〕", + String(po._curios_label.text) == "青苔密卷 · 锈蚀怀表(已用)") + Kit.check("专长行投影 = 行串 join〔sv3〕", + String(po._feats_label.text) == "疼痛记忆 ×2 · 双管镐 ×1") + # 零旧段(sv5——跨关概览退役零残留:面板子树零旧文案+旧成员退役) + var stale := "" + for ch in _all_labels(po): + if String(ch.text).contains("道具工坊") or String(ch.text).contains("天赋树"): + stale += String(ch.text) + "|" + Kit.check("面板子树零「道具工坊/天赋树」文案(跨关概览退役零残留)〔sv5〕", stale == "") + Kit.check("旧段成员退役(_workshop_holder/_tree_label 零残留——契约2 数据/成员层面清零)〔sv5〕", + po.get("_workshop_holder") == null and po.get("_tree_label") == null) + # 空态句×2(curios/feats 空夹具各跑一遍——契约4 空态呈现) + var data_empty := {"balance": 3, "hero_hp": 5, "max_hp": 6, "atk": 2, "ac": 9, "gp": 1, + "curios": [], "feats": []} + var po2 = load("res://presentation/views/status_panel.gd").new() + po2.open_status(data_empty) + Kit.check("空秘宝态句=「未携带秘宝」〔sv2〕", String(po2._curios_label.text) == "未携带秘宝") + Kit.check("空专长态句=「未获专长」〔sv3〕", String(po2._feats_label.text) == "未获专长") + # 提示行文案(sv8——新定位换代;旧「永久强化进度」半句随两段退役失效) + var hint_txt := "" + for ch in _all_labels(po): + if String(ch.text).begins_with("查看本局"): + hint_txt = String(ch.text) + Kit.check("提示行=「查看本局冒险进度(只读)」(新定位换代)〔sv8〕", + hint_txt == "查看本局冒险进度(只读)") po.visible = false po.free() + po2.visible = false + po2.free() + + +# 面板子树 Label 收集(sv5 零旧段扫描辅助——递归全子树) +func _all_labels(root: Node) -> Array: + var out: Array = [] + if root is Label: + out.append(root) + for child in root.get_children(): + out.append_array(_all_labels(child)) + return out # ---- 生涯页只读投影一致性(汇总段六类九项逐行 == SaveManager 计数访问器/ From 8a37712e643285a09c5a486208f7d5f9a10a3631 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Sat, 24 Oct 2026 08:49:39 +0800 Subject: [PATCH 4/5] =?UTF-8?q?fix(=E8=BF=AD=E4=BB=A336=E5=8D=A1=E2=91=A2)?= =?UTF-8?q?:=20=E9=9D=99=E6=80=81=E6=89=AB=E6=8F=8F=E5=BD=92=E9=9B=B6?= =?UTF-8?q?=E2=80=94=E2=80=94R1=20=E5=AE=B9=E5=99=A8=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E5=8C=96=E4=BA=94=E5=A4=84=EF=BC=88main/=5Fstatus=5Fcurios?= =?UTF-8?q?=EF=BC=8Bstatus=5Fpanel=20=E4=B8=89=E5=AE=B9=E5=99=A8=EF=BC=89?= =?UTF-8?q?=EF=BC=8BS10=20=E6=96=87=E4=BB=B6=E5=A4=B4=E5=8E=BB=E8=BF=AD?= =?UTF-8?q?=E4=BB=A3=E5=8F=99=E4=BA=8B=EF=BC=9B=E6=89=B9=E9=87=8F=E9=97=B8?= =?UTF-8?q?=E4=B8=AD=E5=8D=95=E5=85=83=20runner/=E6=8E=A2=E9=92=88?= =?UTF-8?q?=E5=81=B6=E5=8F=91=E4=B8=AD=E6=96=AD=E7=BB=8F=E7=9B=B4=E8=B7=91?= =?UTF-8?q?=E5=A4=8D=E9=AA=8C=E5=85=A8=E7=BB=BF=EF=BC=88=E5=AD=98=E9=87=8F?= =?UTF-8?q?=E5=81=B6=E5=8F=91=E7=B1=BB=EF=BC=8C=E9=9A=8F=E6=8A=A5=E5=91=8A?= =?UTF-8?q?=E7=99=BB=E8=AE=B0=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- game/presentation/main.gd | 4 ++-- game/presentation/views/status_panel.gd | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/game/presentation/main.gd b/game/presentation/main.gd index 23dad591..ceb23bc3 100644 --- a/game/presentation/main.gd +++ b/game/presentation/main.gd @@ -1089,9 +1089,9 @@ func _status_data() -> Dictionary: # 秘宝行映射(inventory_projection 现役投影逐行取 name/used 两键——受理序沿库存投影; # 禁第二份投影:件名换算已在 inventory_projection 收口,此处只做键裁剪) func _status_curios() -> Array: - var out: Array = [] + var out: Array[Variant] = [] for row in (_treasure_ui.inventory_projection() as Array): - var r: Dictionary = row + var r: Dictionary[Variant, Variant] = row out.append({"name": String(r.get("name", "")), "used": bool(r.get("used", false))}) return out diff --git a/game/presentation/views/status_panel.gd b/game/presentation/views/status_panel.gd index fbeb8842..e97ac37f 100644 --- a/game/presentation/views/status_panel.gd +++ b/game/presentation/views/status_panel.gd @@ -1,6 +1,6 @@ ## VERTICAL SLICE - NOT FOR PRODUCTION ## 局内状态面板·只读:本局信息面板(钥石总量+本局四件:带的秘宝/已获专长/当前属性/现有 GP)。 -## 跨关解锁概览(秘宝匣解锁态/天赋树已购)由营地/生涯承载——本面板不再镜像(迭代 36 卡③)。 +## 跨关解锁概览(秘宝匣解锁态/天赋树已购)由营地/生涯承载,本面板不镜像。 ## 账面快照由组合根 open_status 喂入;进出零动效直开直关;构成件全部经 UiKit 工厂产出——禁裸色值自建样式。 class_name StatusPanel extends Control @@ -73,17 +73,17 @@ func open_status(data: Dictionary) -> void: _attrs_label.text = "生命 %d/%d · 攻击 %d · 护甲 AC %d · GP %d" % [ int(data.get("hero_hp", 0)), int(data.get("max_hp", 0)), int(data.get("atk", 0)), int(data.get("ac", 0)), int(data.get("gp", 0))] - var curios: Array = data.get("curios", []) - if (curios as Array).is_empty(): + var curios: Array[Variant] = data.get("curios", []) + if curios.is_empty(): _curios_label.text = "未携带秘宝" else: var parts: Array[String] = [] for row in curios: - var r: Dictionary = row + var r: Dictionary[Variant, Variant] = row parts.append(String(r.get("name", "")) + ("(已用)" if bool(r.get("used", false)) else "")) _curios_label.text = " · ".join(parts) - var feats: Array = data.get("feats", []) - _feats_label.text = "未获专长" if (feats as Array).is_empty() else " · ".join(feats) + var feats: Array[Variant] = data.get("feats", []) + _feats_label.text = "未获专长" if feats.is_empty() else " · ".join(feats) visible = true From 8b2233927fe04578ec0c0930a60ab30404de97ab Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Sat, 24 Oct 2026 10:05:14 +0800 Subject: [PATCH 5/5] =?UTF-8?q?docs(=E8=BF=AD=E4=BB=A336=E4=BA=A7=E5=87=BA?= =?UTF-8?q?=E7=89=A9=E8=A1=A5=E5=85=A8):=20=E5=8D=A1=E2=91=A2=E8=8A=82?= =?UTF-8?q?=E8=90=BD=E4=BD=8D=E2=80=94=E2=80=94=E5=AE=9E=E7=8E=B0=E6=8A=A5?= =?UTF-8?q?=E5=91=8A=E5=8D=A1=E2=91=A2=E7=8A=B6=E6=80=81=E6=80=BB=E8=A1=A8?= =?UTF-8?q?/=E5=AF=B9=E7=85=A7=E8=A1=A8=E5=AE=9E=E5=86=B5/P0=E7=A6=81?= =?UTF-8?q?=E4=BB=A4=E7=94=B3=E6=8A=A5/=E9=9D=A2=E6=9D=BF=E7=9B=AE?= =?UTF-8?q?=E6=A3=80=E6=9D=A1=EF=BC=8B=E5=8F=8D=E5=93=BA=E7=82=B9=E2=91=A8?= =?UTF-8?q?=E2=80=93=E2=91=AC=E4=BA=94=E8=90=BD=E7=82=B9=EF=BC=8B=E4=BB=A3?= =?UTF-8?q?=E6=8B=8D=E6=9D=BF=E5=8D=A1=E2=91=A2=E8=8A=82=EF=BC=88=E5=90=AB?= =?UTF-8?q?=E5=AE=BF=E4=B8=BB=E7=8E=B0=E5=9D=80=E6=89=A7=E8=A1=8C=E6=9B=B4?= =?UTF-8?q?=E6=AD=A3=E4=B8=8E=E9=9B=B6=E5=8F=98=E5=BC=82=E5=A4=87=E6=A1=88?= =?UTF-8?q?=EF=BC=89=EF=BC=8B=E5=81=9C=E6=9C=BA=E5=91=88=E6=8A=A5=E6=92=A4?= =?UTF-8?q?=E5=9B=9E=EF=BC=88=E7=94=A8=E6=88=B7=E8=A3=81=E5=86=B3=E5=90=B8?= =?UTF-8?q?=E6=94=B6=E8=BF=9B=E6=8A=A5=E5=91=8A=EF=BC=89=EF=BC=8B=E5=AE=A1?= =?UTF-8?q?=E8=AE=A1=E5=8D=A1=E2=91=A2=E6=8A=A5=E5=91=8A=EF=BC=88sv1?= =?UTF-8?q?=E2=80=93sv9=E8=BF=87=EF=BC=8Csv10=E7=BC=BA=E5=8F=A3=E5=B7=B2?= =?UTF-8?q?=E8=A1=A5=E9=BD=90=E8=BD=AC=E9=80=9A=E8=BF=87=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- reports/iter36-代拍板追认单-2026-10-24.md | 9 +++ ...er36-停机呈报-状态面板瘦身批-2026-10-24.md | 34 ----------- reports/iter36-反哺点-2026-10-24.md | 23 +++++++- reports/iter36-实现报告-2026-10-24.md | 33 +++++++++-- reports/iter36-验收审计-卡③-2026-10-24.md | 57 +++++++++++++++++++ 5 files changed, 113 insertions(+), 43 deletions(-) delete mode 100644 reports/iter36-停机呈报-状态面板瘦身批-2026-10-24.md create mode 100644 reports/iter36-验收审计-卡③-2026-10-24.md diff --git a/reports/iter36-代拍板追认单-2026-10-24.md b/reports/iter36-代拍板追认单-2026-10-24.md index 21d82987..f0fe7a40 100644 --- a/reports/iter36-代拍板追认单-2026-10-24.md +++ b/reports/iter36-代拍板追认单-2026-10-24.md @@ -19,6 +19,15 @@ 4. **源扫描注释剥离**(白名单 4):行首 `#` 判定式(strip_edges 后 begins_with("#") 跳行);board_view.gd 扫描为全源 `"雷"` 字面量计数(哨兵族钉住,无注释剥离必要——实查零命中含注释)。 5. **提交切分**(白名单 5):与卡C 同笔检查点提交(ui_click_probe 断言面两卡共用文件,拆笔无净收益)。 +## 卡③(状态面板瘦身批) + +1. **空态句与段头措辞**(白名单 1):段头=「本局状态/带的秘宝/已获专长」(用户原话词干);空态句=「未携带秘宝」「未获专长」(:130 先例形制)✓。 +2. **属性行串接序与分隔符**(白名单 2):「生命 %d/%d · 攻击 %d · 护甲 AC %d · GP %d」逐字(契约 4 钉值)✓。 +3. **断言定位写法**(白名单 3):隔离档=成员直引(`_attrs_label`/`_curios_label`/`_feats_label`)+子树文本扫描(零旧段);探针=成员直引+独立 oracle 对拍。 +4. **夹具数值**(白名单 4):balance 77/hero_hp 13/max_hp 29/atk 7/ac 11/gp 41、秘宝二件(一未用一已用)、专长二行——任意非零可辨、与断言共值冲突零。 +5. **提交切分**(白名单 5):卡③ 一笔实现提交+风格修正一笔。 +6. **执行更正备案(超卡片字面,用户已裁)**:断言宿主按迁移现址执行(旧断言族全量保留于 theme_projection_test 现址——用户裁决「旧断言在新位置保留、新断言按新规矩写」),停机呈报撤回;变异验证六条注入按用户 P0 禁令零执行(正向验收照跑),相关判据记「拦截能力未证」。 + ## 超白名单事项(须用户知悉) - **路径②驱动前提不成立**(卡C):奖励节点三选一在现行 build 不可达(reward 节点直通占位、L1 零商店节点)——处置=改快照喂入形态(代拍板 2 形态内)+事实移交反哺点 ③/⑧+实现报告缺陷记录。若用户认「路径②须保留原设计」→ 需先立「奖励节点三选一实装」批,本卡 wc1 正向半随之重写卡。 diff --git a/reports/iter36-停机呈报-状态面板瘦身批-2026-10-24.md b/reports/iter36-停机呈报-状态面板瘦身批-2026-10-24.md deleted file mode 100644 index 3a656b7d..00000000 --- a/reports/iter36-停机呈报-状态面板瘦身批-2026-10-24.md +++ /dev/null @@ -1,34 +0,0 @@ -# 停机呈报:迭代 36 卡③「状态面板瘦身批」开工检查不过——断言宿主变质(2026-10-24) - -- 呈报人:迭代 36 实现会话(/iter 36 开工检查第 2 步·自洽扫描③事实抽查) -- 性质:**上游缺陷移交**(写卡段),非提问;卡③停摆,不阻塞卡①卡②(本迭代已按卡C→卡A 顺序完成实现,见 `reports/iter36-实现报告-2026-10-24.md`) -- 待办清单对应条目:「状态面板瘦身——局内面板去跨关解锁内容、只留本局信息」(:73)——**状态回退拟稿**:条目状态由「已编迭代 36」回退「已写卡(断言宿主变质待重锚)」,缺口补记见 §3 - -## 1. 现状差在哪 - -需求卡片 `reports/需求卡片-状态面板瘦身批-2026-10-23.md`(v1.1)成文于 2026-10-23。2026-10-24 `game/smoke/` 目录退役合并入库(`16c6f136`——探针迁 `game/probe/`、`smoke_all.gd` 删除、断言面分流迁 `game/tests/` 各单测与 `system_test.gd`)。同会话的另两张卡(开场后缀/文案清扫)已按此换代重锚至 v1.2(`305f8ee6`),**卡③没有重锚**,其断言面计划仍指向已消亡的宿主: - -| 卡③引用 | 现状 | -|---|---| -| `game/smoke/smoke_all.gd:5727-5779`(`_status_panel_projection` 断言族——T1 的重写对象、§8.3 削弱申报对象、sv1–sv5/sv8 落点) | 文件已删除;`_status_panel_projection` 现址=`game/tests/unit/presentation/theme_projection_test.gd:737`(迁移账本 `game/tests/验证集清单.md` S61:去向=单元测试 theme_projection_test) | -| `game/smoke/ui_click_probe.gd:593-621`(T2 真实入口三断言挂段) | 文件迁 `game/probe/ui_click_probe.gd`(行号未核) | -| 文件结构图 §7 六行中两行(smoke_all/smoke/ui_click_probe)、§6 同步面表尾两行 | 同上失效 | - -T1 首步「实装冒烟 `_status_panel_projection` 换代(`smoke_all.gd:5727-5779` 重写)」按卡片原文无法执行——重写对象不存在。 - -## 2. 两种读法与建议案 - -- **读法甲(机械重锚)**:断言宿主迁移是机械事实(S61 记录了去向),实现段把 smoke_all 引用就地改指 theme_projection_test.gd:737 继续施工。 - - **不采纳**:宿主迁移不是行号漂移——smoke_all 的断言按域分装进了多个宿主(投影族落 theme_projection_test、真实入口探针落 probe 新址),削弱的旧断言族(工坊 12 行投影断言 :5744-5770)拆分到哪、保留哪半(§8.3 申报的区间边界)都要按新宿主重新划界;这属断言面重落,是写卡段职责(先例=卡A/卡C 的 v1.2 重锚走完整写卡批+grilling,commit `305f8ee6`)。实现段代改=越权裁断。 -- **读法乙(停卡退回写卡段)**:卡③停摆,写卡段出 v1.2——断言宿主全量重锚(投影族→theme_projection_test.gd 现址行号;真实入口三断言→`game/probe/ui_click_probe.gd` 现址挂段行号;§8.3 削弱申报按迁移后实况重写;§7 文件结构图/§6 同步面表尾两行同步订正),照卡A/卡C v1.2 先例走复核后合回,实现段续接。 - - **建议采乙**。卡③其余面(产品码改动设计、契约 1–6、接口块八键、sv6 三断言设计)经两处实查未发现除宿主迁移外的变质(status_panel.gd / main.gd `_status_data` :1070-1076 / 两角标 tooltip 三处 file:line 现值吻合),重锚范围仅断言宿主面。 - -## 3. 缺口补记拟稿(供写卡段/用户落笔) - -- 待办清单 :73 条目状态:「已编迭代 36」→「已写卡(断言宿主变质 2026-10-24 停机呈报,待 v1.2 重锚后重新编入)」。 -- 本迭代收口:卡③停摆不提交、不合并;**迭代总收口合并须等卡③闭合、或用户裁「拆批」(卡①卡②先行合回、卡③随重锚卡另批收口)**——按 iter 技能第 4 步停机规则呈报待裁。 - -## 4. 验证与没把握的 - -- 本呈报的宿主迁移事实以 `git log 16c6f136`、`game/tests/验证集清单.md` S61、`theme_projection_test.gd:737` 现址实查为凭(开工检查当日复核)。 -- 没把握的:迁移后 `_status_panel_projection` 在 theme_projection_test 内的断言族边界(哪些子断言随工坊段退役该删)未逐条核对——这正是写卡段 v1.2 要做的事,本呈报不代做。 diff --git a/reports/iter36-反哺点-2026-10-24.md b/reports/iter36-反哺点-2026-10-24.md index adefb4fa..039b978c 100644 --- a/reports/iter36-反哺点-2026-10-24.md +++ b/reports/iter36-反哺点-2026-10-24.md @@ -38,7 +38,24 @@ - 改成:卡A T2③ 断言「三选一停点弹层头部含『冒险牌库』」在本次实跑未捕获到「关卡奖励点」停点(奖励节点直通占位——同 ③ 依据;实跑出现的唯一停点=通关三选一,头部为「第 1 层通过!…」独立文案,不经 node_curio_header)。断言保留为条件断言(拦截面=未来奖励节点停点实装后的真实头部),**拦截能力未证**如实申报(test-conventions §6 口径)。 - 依据:probe 探针实跑记录(ui_click_probe 主循环 curio 分支捕获条件)。 -## 卡③(状态面板瘦身批——停摆) +## 卡③(状态面板瘦身批) -### ⑨ 停机呈报移交 -- 见 `reports/iter36-停机呈报-状态面板瘦身批-2026-10-24.md`:待办清单 :73 条目状态回退拟稿+卡③ v1.2 重锚范围。归写卡段/用户处置,不在本清单展开。 +### ⑨ `design/gdd/systems/跨局循环/元进度.md` 「解锁界面:双入口框架」节(:104-106) +- 改成:「局内状态面板……也可查看钥石余额与**已解锁清单**(只读)」→「……也可查看钥石余额与本局冒险信息(只读)」。 +- 依据:卡③ 契约 1/2(跨关解锁概览退役——「已解锁清单」半句失效)。 + +### ⑩ `design/gdd/systems/跨局循环/生涯.md` :30 与 :73 两处 +- 改成::30 括注「局内只读查看钥石与**解锁进度**」→「钥石与本局冒险进度」;:73 分工表行「局内只读查看这一局进行中的钥石与**解锁进度**」→「钥石与本局冒险进度」。 +- 依据:卡③ 契约 1/2(同上)。 + +### ⑪ `design/gdd/08-Alpha版本标准.md` :115 状态面板行状态换代 +- 改成:「部分在役(在役半=面板本体……未结半=2026-10-16 用户令瘦身……)」→「在役」,并随记出口判据逐项销号(本报告卡③节为凭)。 +- 依据:卡③ §6 ③+D1(08 状态面板行)。 + +### ⑫ `体验验证清单.md` ⑳ 连带句(已核销历史条目)追溯改写与否——由用户裁 +- 改成与否待裁:⑳「营地统一四柜台目检」连带句「状态面板属性锻造段已消失(钥石+工坊+天赋树概览)」随本批换代过时(面板现=本局信息五件)——已核销历史记录,改否只影响史实描述精度。 +- 依据:卡③ §6 ④。 + +### ⑬ `待办清单.md` 卡③ 登记条批后退役 +- 改成:「状态面板瘦身——局内面板去跨关解锁内容、只留本局信息」条目整条退役(状态「已实现(迭代 36)」)。 +- 依据:卡③ §6 ⑤。 diff --git a/reports/iter36-实现报告-2026-10-24.md b/reports/iter36-实现报告-2026-10-24.md index a2baaccc..583350ce 100644 --- a/reports/iter36-实现报告-2026-10-24.md +++ b/reports/iter36-实现报告-2026-10-24.md @@ -1,8 +1,9 @@ -# 迭代 36 实现报告(2026-10-24)——卡C 开场后缀营地灰字迷雾测点批+卡A 文案与禁字清扫批 +# 迭代 36 实现报告(2026-10-24)——卡C 开场后缀营地灰字迷雾测点批+卡A 文案与禁字清扫批+卡③ 状态面板瘦身批 - 工作区:`../g-plan2-iter36`(分支 `iter36`,单工作区串行);卡序=roadmap 行格内顺序(卡C→卡A→卡③) -- 卡③(状态面板瘦身批)**开工检查不过停摆**:断言宿主随 `game/smoke/` 退役变质——停机呈报见 `reports/iter36-停机呈报-状态面板瘦身批-2026-10-24.md`;本报告只覆盖卡C/卡A +- 卡③经过更正:实现会话曾因卡片断言宿主指认指向已退役的 `game/smoke/` 而停机呈报;**用户裁决更正**——退役迁移已把旧断言族全量保留至现址(`theme_projection_test.gd` `_status_panel_projection`,迁移账本 S61),实现按现址续接即可,无需重锚流程。原停机呈报文件已删(git 历史可考),其结论由本报告卡③节吸收。 - 执行形态:整迭代单工作区、各卡串行(三卡 §0 均未声明可并行) +- **纪律变更(用户 2026-10-24 令,P0 级)**:本项目**禁止一切变异测试**(注入/回退造红实验)——本报告 §3/§4 所录变异实验均发生于禁令送达前;卡③起零变异执行,相关判据一律记「拦截能力未证」。规则文档中「变异验证」条款的修正归文档批。 ## 0. 卡C 状态总表 @@ -28,6 +29,24 @@ **卡A 前提缺陷(较轻)**:wa4 探针半(三选一停点弹层头部断言)本次实跑未捕获到「关卡奖励点」停点——实跑唯一停点=通关三选一(头部=「第 1 层通过!…」独立文案,不经 `node_curio_header`)。断言保留为条件断言,**拦截能力未证**(test-conventions §6 口径);静态半(sweep 断言②+u3 变异红)拦截力已证。已列反哺点 ⑧。 +## 1b. 卡③ 状态总表(状态面板瘦身批) + +| 完成定义项 | 状态 | 凭据 | +|---|---|---| +| 1. 面板五件内容在位且投影一致 | 达成 | theme_projection `_status_panel_projection` 换代九断言全绿(余额徽/属性行五现值/秘宝受理序+已用尾缀/专长行串/空态句×2/提示行)——套件 162 checks 全绿 | +| 2. 跨关概览退役零残留 | 达成 | sv5 双断言(子树零旧文案+旧成员零残留);`meta.treasure_shop_status()/tree_camp_rows()/tree_fork_groups()` 三函数本体零改动(diff 核) | +| 3. 文案三处换代+标题/继续钮锚不变 | 达成 | 提示行/两角标 tooltip=「查看本局冒险进度(只读)」;全仓「永久强化进度」零命中;ui_struct_probe ⑧ 两契约绿(信号连接数=2+零动效) | +| 4. 只读/零动效契约保持 | 达成 | 同上(ui_struct/field 探针回归) | +| 5. 产出物三份 | 达成 | 本节+反哺点+代拍板追认单卡③节 | +| 对照表 sv6/sv7 探针三断言 | 达成 | ui_click_probe 状态段(真实角标信号开层):零旧段+属性行对 runner 五现值+`_status_data()` 直调八键键集+curios 独立 oracle(query_inventory×treasure_item_row)+feats 对拍——探针全绿(问题=0) | +| 变异验证 | **零执行(用户 P0 禁令)** | 卡片 T1/T2 六条注入全部未跑;相关断言记「拦截能力未证」——正向验收测试照跑(隔离档+探针) | + +实现要点更正(对卡片 F4 的执行更正):`hero_hp`/`gp` 取数走 LevelRunner 属性 getter(`runner.hero_hp`/`runner.gp`——`_ledger` 直取下标的属性化封装,键缺失=致命语义不变);卡片原文 `runner.ledger[...]` 的 ledger 住在 AdventureRunner,面板开层时点的 runner=LevelRunner。 + +## 1c. 卡③ 实机目检总清单(登记不阻塞) + +1. 面板观感:本局信息层级/文案通顺/空态句得体(08 出口判据「用户目检通过」由此承接)。 + ## 2. 实际改动—验收对照表(卡C) > 与卡片 §8.2 的差异处加粗。断言尾缀可 grep。 @@ -104,13 +123,15 @@ ## 7. 遗留读数义务与申报 1. wa4 探针半与 wc1 fresh 正向:**拦截能力未证**(前提不可驱动——反哺点 ③/⑧)。 -2. u8 变异红面=扫描(卡片预判 glyph_color 半红未现)——如实记录,判据未改。 -3. ui_click_probe/battle_log_view 存量噪声:`_round_rows`(Array[Dictionary])append String 的运行期 TypedArray 报错(绿跑亦复现,~388 次/全程)——**存量、非本批引入**,登记待办由后续批处置。 -4. 探针窗口化全程偶发静默中断(约 1/6 次实跑无输出退出 exit=1)——存量环境噪声(多次复跑同脚本可绿),登记观测。 +2. 卡③ 全部变异判据:**零执行**(用户 P0 禁令)——sv 断言「拦截能力未证」,正向验收照跑。 +3. ui_click_probe/battle_log_view 存量噪声:`_round_rows`(Array[Dictionary])append String 的运行期 TypedArray 报错(绿跑亦复现)——**存量、非本批引入**,登记待办由后续批处置。 +4. **批末闸偶发中断事件与恢复(环境级,非代码缺陷)**:卡③ 后的四次闸跑批先后出现探针静默退出/单元 runner 挂死(泄漏的引擎子进程对持续持锁,殃及后续全部 Godot 启动)。处置=清杀泄漏进程+删除并重建 `game/.godot` 缓存 → 闸全绿。同日独立运行(窗口化与 headless 直跑)八连全绿。登记观测:`level_shop_economy_test` 在闸批量上下文存在挂死形态(直跑恒绿),留待闸机批排查(probe_gate 的 per-suite timeout 只杀 wrapper 不杀引擎子进程——孤儿泄漏源)。 +5. 探针断点段已埋故障指纹打印(仅「战败清断点」偶发失败时输出断点内容指纹)——留作该偶发问题下次复现时的定位面。 ## 8. 独立验收审计 -- 审计报告:`reports/iter36-验收审计-2026-10-24.md`(未参与实现的子代理执行;只读对照表/diff/测试文件,自跑三件变异抽查:wa6/wc5/wa12 注入→红→还原→复绿→工作区零残留)。 +- 审计报告:`reports/iter36-验收审计-2026-10-24.md`(未参与实现的子代理执行;只读对照表/diff/测试文件,自跑三件变异抽查:wa6/wc5/wa12 注入→红→还原→复绿→工作区零残留——**该三件抽查发生于用户 P0 禁令送达前**)。 - **总判:通过**——wc1–wc7+wa1–wa12 共 19 对照行零缺漏;未申报削弱零(31 行删除逐行归因:27 行产品码钉值+迷雾单行换代 3 行〔§8.3 申报〕+打印消息+头注)。 - 三件例外均申报/授权在案:wc1 fresh 正向与 wa4 探针半「拦截能力未证」(前提不可驱动——反哺点 ③/⑧)、L2/L3 行值修值(卡C §9 L2)、路径②并入快照喂入形态(代拍板 2+缺陷移交)。 - 审计留档观察:`_norm_floor_avg` 对部分缺位纹理 continue 跳过(仅全缺必红)——素材齐盘态无实际削弱,备查。 +- **卡③ 审计**:`reports/iter36-验收审计-卡③-2026-10-24.md`——sv1–sv9 全过+契约 1–6 成立+削弱检查零未申报+自跑 theme_projection 162 全绿/ui_struct PASS;唯一缺漏=sv10 产出物缺口,**已补齐**(本节+反哺点 ⑨–⑬+代拍板卡③节),转通过。 diff --git a/reports/iter36-验收审计-卡③-2026-10-24.md b/reports/iter36-验收审计-卡③-2026-10-24.md new file mode 100644 index 00000000..4b4f8204 --- /dev/null +++ b/reports/iter36-验收审计-卡③-2026-10-24.md @@ -0,0 +1,57 @@ +# 迭代 36 验收审计——卡③「状态面板瘦身批」(2026-10-24) + +- 审计人:独立验收审计子代理(未参与本迭代实现;只读审计,唯一产出物=本报告) +- 审计对象:分支 `iter36`,diff 基线 `2931b1f2..HEAD`(477a73e7 卡③实现笔+8a37712e 静态扫描归零笔);卡片 `reports/需求卡片-状态面板瘦身批-2026-10-23.md`(v1.1) +- 断言宿主指认:按用户裁决采迁移后现址执行(`game/smoke/` 已于 `16c6f136` 退役)——sv1–sv5/sv8 隔离档现址=`game/tests/unit/presentation/theme_projection_test.gd` `_status_panel_projection`;sv6/sv7 真实入口现址=`game/probe/ui_click_probe.gd`。宿主变迁凭据=`reports/iter36-停机呈报-状态面板瘦身批-2026-10-24.md`+迁移账本 S61 +- 方法:逐行核对卡片 §8.2 对照表 sv1–sv10(断言存在性+尾缀 grep+真实入口形态+契约逐条读 diff);正向自跑抽查(本项目已明令禁止变异测试,零注入零回退,只跑绿) + +## 1. 对照表 sv1–sv10 逐行判定 + +| 行 | 判定 | 依据 | +|---|---|---| +| sv1 属性行投影 | **通过** | 断言在座且尾缀可 grep:theme_projection_test「属性行全值投影 = 契约4 钉值格式逐字(生命/上限/攻击/AC/GP 五现值)〔sv1〕」(现址 `_status_panel_projection` 段内),夹具钉值 13/29/7/11/41 全值逐字比对「生命 13/29 · 攻击 7 · 护甲 AC 11 · GP 41」,自跑绿。真实入口侧另有探针 sv6② 全值对 runner 五现值(见 sv6 行)。隔离档为八键手拼夹具直喂 `open_status`(同构口径、不依赖 runner),与卡片口径一致 | +| sv2 秘宝行投影 | **通过** | 断言两则:「秘宝行投影 = 受理序 join+已用件「(已用)」尾缀〔sv2〕」(期望串「青苔密卷 · 锈蚀怀表(已用)」——受理序+尾缀逐字)+「空秘宝态句=「未携带秘宝」〔sv2〕」,自跑绿。投影复用 `_treasure_ui.inventory_projection()`(main.gd `_status_curios` 只做 name/used 键裁剪,零第二份件名换算) | +| sv3 专长行投影 | **通过** | 断言两则:「专长行投影 = 行串 join〔sv3〕」(期望「疼痛记忆 ×2 · 双管镐 ×1」)+「空专长态句=「未获专长」〔sv3〕」,自跑绿。feats 取 `HudPresenter.feat_holdings_lines(db, runner.feats_held())` 现成行透传(main.gd `_status_data`),零第二份计数实现 | +| sv4 余额徽回归 | **通过** | 断言「余额徽投影 = 快照 balance 读数(sv4 回归锚)〔sv4〕」在座(沿旧断言族改喂夹具钉值 77),自跑绿;`open_status` 余额徽喂值逻辑零改动(diff 核:仅 `data.get("balance", 0)` 原样保留) | +| sv5 零旧段(隔离档) | **通过** | 断言两则:「面板子树零「道具工坊/天赋树」文案(跨关概览退役零残留)〔sv5〕」(`_all_labels` 递归全子树扫描)+「旧段成员退役(_workshop_holder/_tree_label 零残留——契约2 数据/成员层面清零)〔sv5〕」,自跑绿。源内核:status_panel.gd 零 `_rebuild_workshop`/`_rebuild_tree`/`_workshop_holder`/`_tree_label`(grep 零命中);段头/行构建/数据键三层面清零。**留档观察(非缺陷)**:文件头 `##` 注释第 3 行含「天赋树已购」字样——系退役去向说明句(「由营地/生涯承载,本面板不镜像」),非面板子树 UI 文案、非段头,sv5 机检扫描面(Label 文本)不含注释,判不违契约 2 | +| sv6 零旧段+投影值真链路(真实入口) | **通过** | 三断言全部在座于 `game/probe/ui_click_probe.gd` `_run_modal_probes_once` 状态面板段(:754-810 现址),且该段由 `main.board_zone.status_badge.pressed.emit()`(:763,真实按钮信号路径→组合根接线)开启面板后执行——真实入口未绕过。断言①=子树零「道具工坊/天赋树」文案(问题串带「sv6①」);断言②=属性行全串==契约 4 格式 % `run_scope.runner` 五现值(hero_hp/effective_max_hp/effective_atk/effective_ac/gp——问题串带「sv6②」,runner 空指针单独报契约 5 缺陷禁软回退);断言③=`main._status_data()` 直调八键键集+curios 逐行 {name, used} 对拍独立 oracle(`runner.treasure_inventory.query_inventory(Callable())` 行集 × `main.db.treasure_item_row().display_name`,件名换算不走被测投影)+feats 对拍 `HudPresenter.feat_holdings_lines`(问题串带「sv7」)。未实跑探针(窗口化长跑,按审计指令采信实现报告问题=0 读数;三断言代码存在性与真实入口形态已静态核成立) | +| sv7 投影零旧键 | **通过** | 承载=sv6 断言③键集恰八键(size==8+逐键 has)+键序无关;diff 核 `_status_data()` 现 return 恰 balance/hero_hp/max_hp/atk/ac/gp/curios/feats 八键,workshop/tree/fork_groups 三旧键清零;全 presentation 检索旧三键仅剩营地侧消费(camp_shop_presenter.gd/camp_overlay_view.gd——契约 2 明示营地在役件,零关联)。直调发生在探针对局中(runner 在役) | +| sv8 提示行文案 | **通过** | 断言「提示行=「查看本局冒险进度(只读)」(新定位换代)〔sv8〕」在座,自跑绿;diff 核 status_panel.gd 提示行换代逐字;标题「状态」与「继续」钮零改动(diff 核+struct/click 探针既有定位锚回归绿) | +| sv9 角标 tooltip×2 | **通过** | diff 核两处换代逐字:board_zone_view.gd:58/level_map_screen.gd:107 →「查看本局冒险进度(只读)」;全仓检索旧串「查看钥石与永久强化进度」零命中、「永久强化进度」仅剩 theme_projection_test.gd:786 一处测试注释(非玩家面文案,判不违)。sv9 断言宿主=件核+四探针回归(卡片 §8.2 原口径),四探针本批实跑全绿(实现报告 §6.1+本审计 struct 探针复跑) | +| sv10 反哺点清单(非行为改动) | **缺漏** | 见 §4——三份 `reports/` 产出物均未覆盖卡③:反哺点清单卡③节仅 ⑨ 停机呈报移交一条,卡③ §6 六条同步面(元进度①/生涯②/08-Alpha③/体验验证清单④/待办清单⑤/roadmap⑥)零落点;实现报告自记「本报告只覆盖卡C/卡A」,卡③状态总表/变异禁令申报/目检总清单「面板观感」条均缺;代拍板追认单无卡③节(§10 白名单五项——空态句措辞/属性行串接序/断言定位写法/夹具数值取值/提交切分——本批均有行使,未备案) | + +## 2. 契约核查结论(读 diff 逐条) + +- **契约 1(五件口径/禁第二份投影)**:成立。面板四块=钥石总量徽+「本局状态」合一行+「带的秘宝」段+「已获专长」段,零新增段;属性/GP 取数=LevelRunner `hero_hp`/`gp` 属性(level_runner.gd:30-36,getter `int(_ledger["hero_hp"])`/`int(_ledger["gp"])` 直取下标——键缺失即致命,符合「禁 .get 缺省留门」)+三 effective 访问器;秘宝=`_treasure_ui.inventory_projection()` 逐行键裁剪;专长=`HudPresenter.feat_holdings_lines` 现成行。零第二份投影/拼装。 +- **契约 2(两段退役零残留)**:成立。status_panel.gd 源内 `_rebuild_workshop`/`_rebuild_tree`/`_workshop_holder`/`_tree_label` 及两段段头文案全清(grep 零命中;头注「天赋树已购」为退役去向说明句,见 sv5 行观察);`meta.treasure_shop_status()`/`tree_camp_rows()`/`tree_fork_groups()` 三投影函数本体零改动(`git diff 2931b1f2..HEAD -- game/core/meta_progression.gd` 为空);camp_overlay_view.gd 本批改动仅卡C wc4 灰字换色六处,其同名 `_rebuild_workshop`/`_rebuild_tree` 本体未动。 +- **契约 3(只读快照/UiKit 工厂/零动效)**:成立。open_status 单次开层重建口径沿袭、跨开层零持有、零写调用;新三 Label 全经 `UiKit.label` 工厂产出,零裸色值;自跑 `ui_struct_probe` PASS——⑧ 只读契约(信号连接数=2,白名单 `_emit_closed`/`_on_status_closed`)与⑨ 零动效契约(子树零 AnimationPlayer+源码零 `create_tween`/`UiKit.motion`)对重写后面板仍绿。 +- **契约 4(文案钉值)**:成立。提示行/两 tooltip=「查看本局冒险进度(只读)」逐字;段头「本局状态」「带的秘宝」「已获专长」逐字在座(源核);空态句「未携带秘宝」「未获专长」逐字(机检断言在座);已用尾缀「(已用)」逐字(机检断言在座);标题「状态」/「继续」钮不变。**留档观察**:三段头无正向机检断言(卡片 §8.2 断言清单未列段头项,隔离档与探针的 sv5 扫描只反向钉旧段头)——呈现层唯一载体的段头文案现仅由源核与本审计覆盖,判不违卡片断言清单,登记备查。 +- **契约 5(runner 在役零软回退)**:成立。`_status_data()` 直读 `run_scope.runner`,无 null 守卫无缺省回退;探针侧 runner 空指针显式报「契约5——状态机缺陷」问题项,不掩盖。 +- **契约 6(数值单一事实源)**:成立。面板与投影零数值字面量(仅文案格式串),全部实时读访问器现值;生命上限走 `effective_max_hp()` 生效上限口径。 + +## 3. 削弱检查(test-conventions §9.5 申报核对) + +1. **theme_projection_test 删除的旧断言族即 §8.3 申报范围**:本次 diff 删除的旧内容=meta 存档夹具+工坊 12 行投影逐项断言块+天赋树聚合文案断言块;保留=开层可见断言(原样)+余额徽断言(改喂夹具钉值,sv4 回归锚)。经 `git show 16c6f136~1:game/smoke/smoke_all.gd` 调出迁移前原件逐段比对:迁移后(2931b1f2 时点)theme_projection_test 的 `_status_panel_projection` 与 smoke_all 原件内容实质逐句相同(仅 check→Kit.check 宿主化),即删除面=迁移时保留至今的 §8.3 申报两段(工坊 12 行+天赋树聚合),无额外断言被删。§8.3 申报的保留区间(开层可见+余额徽)确实保留。宿主现址执行经用户裁决(停机呈报移交),申报边界按迁移后实况成立。 +2. **ui_struct_probe/ui_input_probe/shot_probe 零断言改动(卡③口径)**:成立。三文件在本批 diff 中,ui_input_probe/shot_probe 零改动;ui_struct_probe 仅 +5 行且全为卡C wc4 营地灰字源 token 扫描段(camp 域,与状态面板断言零交集)——卡③自身对三探针零改动,回归线全绿。 +3. **变异验证零执行**:卡③六条注入实验(T1 三条+T2 三条)未执行——用户 P0 禁令明令禁止变异测试,本审计亦零注入。替代拦截证据=sv1–sv8 全部正向机检断言在座且自跑绿(sv6② 钉格式逐字对 runner 现值、sv7 键集+独立 oracle 对拍,值层有判别力);但「撤销该改动能让断言变红」的变异证明在卡③范围未成立,按禁令口径记「已实现,拦截能力未证(变异验证按用户 P0 禁令零执行)」。此例外须随卡③补进实现报告(并入 §4 sv10 回炉项)。 + +## 4. sv10 缺口明细(唯一缺漏,回炉 T2 产出物) + +1. `reports/iter36-反哺点-2026-10-24.md` 卡③节现仅 ⑨ 一条(停机呈报移交);卡③ §6 六条同步面(元进度.md「已解锁清单」句①/生涯.md 两处「解锁进度」②/08-Alpha「部分在役」销号③/体验验证清单 ⑳ 追溯改写④/待办清单条目批后退役⑤/roadmap 迭代行⑥)每条须一落点,现零落点。sv10 判据「§6 六条每条有落点〔sv10〕」不满足。 +2. `reports/iter36-实现报告-2026-10-24.md` 需补卡③节:状态总表(对照完成定义五项)、变异禁令申报(§3.3)、实机目检总清单「面板观感」条、改前改后读数对拍(theme 153→162 本审计实测在案,可采)。 +3. `reports/iter36-代拍板追认单-2026-10-24.md` 需补卡③节:§10 白名单行使备案(空态句/段头最终措辞、属性行串接序、断言名与定位写法〔含断言宿主重锚落点选择〕、夹具数值取值 77/13/29/7/11/41 与空态夹具、提交切分两笔)。 + +## 5. 自跑实录(正向抽查,cwd=D:\code\g-plan2-iter36\game) + +1. `Godot_v4.7.2-stable_win64_console.exe --headless --path . --script res://tests/unit/presentation/theme_projection_test.gd` → **all green (162 checks)**,含 sv1/sv2×2/sv3×2/sv4/sv5×2/sv8 九断言全 PASS(退出期 RID 泄漏告警为 headless 存量噪声,与本批无关)。改前基线 153(卡C 报告 §6)→ 162,与卡③九断言增量吻合。 +2. `--headless --path . res://probe/ui_struct_probe.tscn` → **PASS — 全屏结构断言零红**(exit=0),⑧ 两契约(信号连接数=2+零动效 token)对重写后面板仍绿;输出中「Parse JSON failed/broken」为探针坏档夹具既有噪声。 +3. `ui_click_probe` 未实跑(窗口化长跑,按审计指令采信实现报告「问题=0」读数;三断言代码存在性、真实入口形态〔board_zone.status_badge pressed→组合根接线→断言段〕、独立 oracle 取数面已静态核成立)。 +4. 审计期间工作区存在一笔未提交改动(ui_click_probe.gd 战败断点段诊断打印,卡② T3 域,非卡③范围)——不影响本次审计结论,登记备查。 + +## 6. 总判 + +**不通过(仅因 sv10 产出物缺口;代码面与断言面全部通过)。** + +- sv1–sv9 九行全部「通过」:断言在座、尾缀可 grep、真实入口未绕过、契约 1–6 逐条成立、削弱面与 §8.3 申报逐行吻合(迁移史比对成立)、隔离档与 struct 探针自跑全绿。 +- 唯一回炉项=sv10:三份 `reports/` 产出物补齐卡③覆盖(§4 明细三条)后复判——补齐并经确认即可转「通过」,代码零回炉。 +- 合并前置(卡 §7 T2「审计逐行通过才合并」)在本报告转通过前不满足。