# VERTICAL SLICE - NOT FOR PRODUCTION # 战斗一屏样张批 V5–V12 断言面(2026-10-29 迭代 43 立五项;2026-10-31 迭代 46 卡B 增 V10–V12)—— # 实装定值的真实入口断言: # V5 血条填充 = 危险红(UiKit.HP_BAR_FILL 值单点 + 绘制点源码锚:全仓零 #689CE8 与零裸 # Color(0.41,0.61,0.91);条底收 HP_BAR_TRACK 常量); # V6 站桩名字行垫 HP_BAR_BACK 素底且不压地台(名字行基线 y < 地台顶 y——name_line_probe 几何出口); # V7 顺位徽标提档 UiKit.FS_HINT(值断言 + 绘制点源码锚,§7.4 清单外 11–13px 提档); # V8 护甲图标三键(light/ward/heavy)绑对应 24×24 PNG(ResourceLoader 装载结果 + name_line_probe # 绑定面); # V9 地台件 scene_stage_plaza.png + 护甲三枚入包可加载且尺寸 48×12 / 24×24(§5.2/§5.3 工单); # V10 名字行提档 FS_HINT 14(探针 name_font_size 实读 + 两绘制点源码锚 + FS_CAPTION 计数锚==1); # V11 退役常量不在册 + 掩尘 BRICK 逐位 + 顶缘线/口沿高光两绘制点改指源码锚; # V12 PLAZA_EDGE 逐位 #4C4038 + 回退描边改指源码锚。 # V13–V18 立绘显示档(2026-11-01 迭代 48 卡②)——显示档 = 三档绝对高逐键钉值(96/144/188)+ # 入包件 = 原稿紧裁零降采样(源高 == 原稿表值)+纵横倍率恒等+主角三态显示 144+ # 跨语言显示档表逐键对账+键集恰九键(§8.2 显示档定值单点)。 # headless 无渲染面:像素采样以常量单点 + 源码锚 + 探针几何承载(观测面注记,验收对照表 V5 行)。 # 运行:godot --headless --path game --script res://tests/unit/presentation/standee_present_values_test.gd(expect 0) extends SceneTree const Kit := preload("res://tests/unit/test_kit.gd") const StandeeBodyScript := preload("res://presentation/views/standee_body.gd") # 源码锚读面(静态判据——源文件即提交面) func _read_source(rel_path: String) -> String: var f := FileAccess.open(rel_path, FileAccess.READ) if f == null: return "" return f.get_as_text() func _init() -> void: # oracle: design/gdd/ux/battle.md:154 (站桩呈现定值与素材绑定组) # ---- V5 血条填充 = 危险红 #A8321F(§4.2 语义物件显式落法) ---- Kit.check("血条填充常量 = 危险红 #A8321F〔样张批 V5〕", UiKit.HP_BAR_FILL.to_html(false) == "a8321f") Kit.check("血条条底收常量 HP_BAR_TRACK(视图原裸值原样入册)〔样张批 V5〕", UiKit.HP_BAR_TRACK.is_equal_approx(Color(0.10, 0.10, 0.11))) var body_src := _read_source("res://presentation/views/standee_body.gd") Kit.check("血条绘制点引用 UiKit.HP_BAR_FILL(源码锚)〔样张批 V5〕", body_src.contains("UiKit.HP_BAR_FILL")) Kit.check("全源零违例值 #689CE8 与零裸 Color(0.41,0.61,0.91)(源码锚)〔样张批 V5〕", not body_src.contains("689CE8") and not body_src.contains("689ce8") and not body_src.contains("0.41, 0.61, 0.91")) # ---- V6/V8 站桩实例面(名字行几何 + 护甲绑定) ---- var standee: Node2D = StandeeBodyScript.new("enemy") root.add_child(standee) standee.set_data({"display_name": "地精", "enemy_id": "goblin", "hp": 3, "max_hp": 3, "order": 1, "armor_type": "light"}) var nl: Dictionary = standee.name_line_probe() Kit.check("名字行基线 y < 地台顶 y(文字禁压地台——V6 判据坐标面)〔样张批 V6〕", float(nl.baseline_y) < float(nl.plinth_top_y)) Kit.check("素底牌底沿 ≤ 地台顶(整行不越地台)〔样张批 V6〕", Rect2(nl.plate).end.y <= float(nl.plinth_top_y) + 0.01) Kit.check("名字行素底 = HP_BAR_BACK 深档 #282220(挂载底色口径)〔样张批 V6〕", Color(nl.plate_color).to_html(false) == "282220") Kit.check("基线读数与实装几何同源(基线 = 地台顶上方 8px 档:150 < 158——14px 档牌顶+18)〔样张批 V6〕", absf(float(nl.baseline_y) - 150.0) < 0.51 and absf(float(nl.plinth_top_y) - 158.0) < 0.51) # ---- V7 顺位徽标提档 FS_HINT ---- Kit.check("FS_HINT = 14(§7.4 六档)〔样张批 V7〕", UiKit.FS_HINT == 14) Kit.check("顺位徽标绘制点取 UiKit.FS_HINT(源码锚)〔样张批 V7〕", body_src.contains("HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT")) # ---- V10 名字行提档 FS_HINT 14(迭代 46 卡B——五取值点+基线随档联动) ---- Kit.check("名字行探针字号实读 = FS_HINT(几何单点出口 name_font_size)〔名字行 V10〕", int(nl.name_font_size) == UiKit.FS_HINT) Kit.check("名字与护甲两绘制点取 FS_HINT(源码锚——沿 V7 先例)〔名字行 V10〕", body_src.contains("HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT, UiKit.WARM") and body_src.contains("HORIZONTAL_ALIGNMENT_LEFT, -1, UiKit.FS_HINT, col")) Kit.check("全文件 FS_CAPTION 仅剩消散提示一处(计数锚==1——三处宽度测量点强制换代)〔名字行 V10〕", body_src.count("UiKit.FS_CAPTION") == 1) # ---- V11/V12 地台色合并(迭代 46 卡B——常量面载体=Script 常量表,源码锚正证改指目标色) ---- var uikit_script: Script = load("res://presentation/views/ui_kit.gd") var cmap: Dictionary = uikit_script.get_script_constant_map() var cell_src := _read_source("res://presentation/views/cell_base_layer.gd") Kit.check("退役常量不在册(STAND_STONE_RIM/STAND_STONE_EDGE 零键)〔名字行 V11〕", not cmap.has("STAND_STONE_RIM") and not cmap.has("STAND_STONE_EDGE")) Kit.check("掩尘 BRICK 值逐位 #C9AF83(合并目标①)〔名字行 V11〕", UiKit.BRICK.to_html(false) == "c9af83") Kit.check("顶缘受光线与口沿高光两绘制点改指 BRICK(源码锚)〔名字行 V11〕", body_src.contains("ped.size.x, 2.0), UiKit.BRICK, true") and cell_src.contains("FACADE_HIGHLIGHT_PX), UiKit.BRICK, true")) Kit.check("PLAZA_EDGE 值逐位 #4C4038(合并目标②)〔名字行 V12〕", UiKit.PLAZA_EDGE.to_html(false) == "4c4038") Kit.check("回退描边绘制点改指 PLAZA_EDGE(源码锚)〔名字行 V12〕", body_src.contains("UiKit.PLAZA_EDGE")) # ---- V8 护甲图标三键绑 24×24 PNG ---- var armor_paths := {"light": UiKit.TEX_ARMOR_LIGHT, "ward": UiKit.TEX_ARMOR_WARD, "heavy": UiKit.TEX_ARMOR_HEAVY} for key: String in armor_paths: var tex := UiKit.load_tex(armor_paths[key]) Kit.check("护甲 %s 键绑对应 PNG 且 24×24(§5.3 工单)〔样张批 V8〕" % key, tex != null and tex.get_width() == 24 and tex.get_height() == 24) var ward_standee: Node2D = StandeeBodyScript.new("enemy") root.add_child(ward_standee) ward_standee.set_data({"display_name": "蛛化卓尔精灵", "enemy_id": "drider", "hp": 5, "max_hp": 5, "order": 1, "armor_type": "ward"}) var nl_ward: Dictionary = ward_standee.name_line_probe() Kit.check("甲档键 → 纹理路径绑定(name_line_probe 出 armor_icon 路径)〔样张批 V8〕", bool(nl_ward.armor_tex_bound) and String(nl_ward.armor_icon) == UiKit.TEX_ARMOR_WARD) # ---- V9 地台件与护甲三枚入包可载且尺寸对 ---- var plaza := UiKit.load_tex(UiKit.TEX_STAGE_PLAZA) Kit.check("地台件 scene_stage_plaza.png 可加载且 48×12(§5.2 工单)〔样张批 V9〕", plaza != null and plaza.get_width() == 48 and plaza.get_height() == 12) for key: String in armor_paths: var tex9 := UiKit.load_tex(armor_paths[key]) Kit.check("护甲三枚入包(24×24)且路径常量在 UiKit 单点〔样张批 V9〕" if key == "light" else "护甲三枚入包(24×24)〔样张批 V9〕", tex9 != null and tex9.get_width() == 24 and tex9.get_height() == 24) # ---- V13–V18 立绘显示档(迭代 48 卡②) ---- _check_display_tier() quit(Kit.finish("standee_present_values")) # ---- V13–V18 立绘显示档(迭代 48 卡②——显示档 = 三档绝对高,入包件 = 原稿紧裁零降采样) ---- # 规格钉值(显示档定值单点 = art-bible §8.2;源高 = 原稿表值——旧降采样稿必红; # 显示宽 = round(源宽×档高/源高),食人魔酋长 157 为已接受超宽项): # 键 → [源高, 显示档高, 显示宽] const _DISPLAY_SPEC := { "goblin": [90, 96, 95], "giant_spider": [81, 96, 121], "duergar": [126, 96, 71], "hook_horror": [94, 144, 113], "bugbear": [123, 144, 119], "hero": [92, 144, 138], "ogre_chief": [189, 188, 157], "drider": [189, 188, 139], "duergar_king": [189, 188, 132], } func _enemy_tex_path(key: String) -> String: return "res://assets/art/character/character_enemy_%s.png" % key func _check_display_tier() -> void: # oracle: design/gdd/ux/battle.md:154 (显示档与分辨率绑定组) # V13/V15 九键逐一:真实入口 StandeeBody → set_data(enemy_id) → set_portrait_texture(真纹理) → portrait_probe for key: String in _DISPLAY_SPEC: var spec: Array = _DISPLAY_SPEC[key] var body: Node2D = StandeeBodyScript.new("hero" if key == "hero" else "enemy") root.add_child(body) var tex: Texture2D = UiKit.load_tex(UiKit.TEX_HERO_PORTRAIT if key == "hero" else _enemy_tex_path(key)) if key == "hero": body.set_portrait_texture(tex, true) else: body.set_data({"display_name": key, "enemy_id": key, "hp": 3, "max_hp": 3, "order": 1, "armor_type": "light"}) body.set_portrait_texture(tex, false) var pp: Dictionary = body.portrait_probe() Kit.check("显示档 %s:源高 %d(原稿表值——旧降采样稿必红)+显示高 %d 逐键钉值〔立绘显示档 V13〕" % [key, int(spec[0]), int(spec[1])], tex != null and int(tex.get_height()) == int(spec[0]) and is_equal_approx(float(pp.display_size.y), float(spec[1])) and is_equal_approx(float(pp.display_scale) * float(tex.get_height()), float(spec[1]))) Kit.check("显示档 %s:纵横倍率恒等(双轴同倍率)+显示宽随源比例(round 档 %d)〔立绘显示档 V15〕" % [key, int(spec[2])], is_equal_approx(float(pp.display_size.x), float(tex.get_width()) * float(pp.display_scale)) and is_equal_approx(float(pp.display_size.y), float(tex.get_height()) * float(pp.display_scale)) and absf(float(pp.display_size.x) - float(spec[2])) < 0.51) body.queue_free() # V14 静态源锚:显示档表读表单点 + 零硬编码 2× 倍率 var motion_src := _read_source("res://presentation/views/standee_motion.gd") Kit.check("显示档表单点在位(_place_portrait 读 PORTRAIT_DISPLAY_H)且零 Vector2(2.0, 2.0) 字面倍率〔立绘显示档 V14〕", motion_src.contains("PORTRAIT_DISPLAY_H") and not motion_src.contains("Vector2(2.0, 2.0)")) # V16 主角三态:组合根装载(hero_tex 注册链)→ 逐态 apply_hero_state 复读探针 var stage_view: Node = load("res://presentation/views/battle_stage_view.gd").new(null, load("res://data/tables/battle_layout.tres")) root.add_child(stage_view) var hs: Node2D = stage_view.hero_standee var pp0: Dictionary = hs.portrait_probe() Kit.check("主角 normal 态:源高 ≥92 且显示高 144(组合根真实装载链)〔立绘显示档 V16〕", bool(pp0.has_tex) and is_equal_approx(float(pp0.display_size.y), 144.0)) for hstate: String in ["attack", "hit"]: hs.set_hero_state(hstate) var pph: Dictionary = hs.portrait_probe() Kit.check("主角 %s 态:源高 ≥92 且显示高 144(三态同档)〔立绘显示档 V16〕" % hstate, bool(pph.has_tex) and is_equal_approx(float(pph.display_size.y), 144.0)) stage_view.queue_free() # V17 跨语言对账:Python 显示档表(STANDEE_DISPLAY_H 八敌 + STANDEE_SLOT_H 主角)逐键 == GDScript 表 # (读不到工具源 = L2 记录退回双端字面钉值+独立审计对账——禁当红亦禁当绿) var py_src := "" for py_path: String in ["../tools/pixelize_candidates.py", "tools/pixelize_candidates.py"]: var pf := FileAccess.open(py_path, FileAccess.READ) if pf != null: py_src = pf.get_as_text() break if py_src.is_empty(): print("[L2] V17 工具源不可读(../tools/ 路径不通)——退回双端字面钉值+独立审计对账(记录不判红)") else: var py_ok := true for key: String in _DISPLAY_SPEC: if key == "hero": py_ok = py_ok and py_src.contains("STANDEE_SLOT_H = %d" % int(_DISPLAY_SPEC[key][1])) else: py_ok = py_ok and py_src.contains("\"%s\": %d" % [key, int(_DISPLAY_SPEC[key][1])]) Kit.check("Python 显示档表与 GDScript PORTRAIT_DISPLAY_H 九键逐键相等(源文本对账)〔立绘显示档 V17〕", py_ok) # V18 键集恰九键(八敌 ∪ hero,无多余键——防新敌种漏登记被 push_error 静默) var motion_script: Script = load("res://presentation/views/standee_motion.gd") var mcmap: Dictionary = motion_script.get_script_constant_map() var disp_tbl: Dictionary = mcmap.get("PORTRAIT_DISPLAY_H", {}) var keys_ok: bool = disp_tbl.size() == 9 for k: String in ["bugbear", "drider", "duergar", "duergar_king", "giant_spider", "goblin", "hook_horror", "ogre_chief", "hero"]: keys_ok = keys_ok and disp_tbl.has(k) Kit.check("显示档表键集 = 八敌 ∪ hero 恰九键(无多余键)〔立绘显示档 V18〕", keys_ok)