iter48-卡3:卡③(战斗区背景替换批,含审计补差V6源码锚): ground_h单源回填100+schema地面校验拆辅助+背景三关变体层(TEX_STAGE_BG_FMT+set_board_type选图单点,空串归dungeon,缺图回退链)+连续地面层(TEX_STAGE_GROUND底贴B区底高读表)+两处接板同步守卫行+四柱光柱层与BEAM_LIGHT/STAGE_BG退役(菜单_beams_rect不动)+两对比锚改读SLOT_BACKDROP(阈值不变)+stage_background_test 20checks全绿(V1–V6含审计补差两源码锚断言)+layout_probe追加断言两档PASS+ui_contrast 36测点绿

This commit is contained in:
DKChan committed 2026-11-02 01:58:24 +08:00
1 parent 4c3f0f6082
commit 3b60048b1a
13 files changed
+262 -39

No files matched your search

+12
View File
@@ -5,6 +5,7 @@ class_name BattleLayoutTable
extends Resource
@export var stage_h: int = 252 # B 区高(全宽战斗画面,含底部明细带)
@export var ground_h: int = 100 # B 区连续地面层高(源 = 基准屏宽 × 本值)
@export var info_panel_w: int = 240 # C 区宽(左下角色信息列)
@export var ap_bar_h: int = 44 # D 区 AP 条高
@export var log_band_h_dig: int = 24 # 明细带挖掘期收起高(单行上轮摘要)
@@ -27,4 +28,15 @@ func validate() -> Array:
errs.append({"category": "cross_field", "message": "log_band_h_settle(%d) < log_band_h_dig(%d)——展开态不得矮于收起态" % [log_band_h_settle, log_band_h_dig]})
if log_band_h_settle >= stage_h:
errs.append({"category": "cross_field", "message": "log_band_h_settle(%d) ≥ stage_h(%d)——明细带不得占满 B 区" % [log_band_h_settle, stage_h]})
errs.append_array(_ground_field_errors())
return errs
## 地面层域校验(拆私有辅助——validate 分支预算内;ground_h 单源 = 布局表)
func _ground_field_errors() -> Array:
var errs: Array[Dictionary] = []
if ground_h < 16 or ground_h > stage_h / 2:
errs.append({"category": "domain", "message": "ground_h 越域 [16, stage_h/2=%d]: %d" % [stage_h / 2, ground_h]})
if ground_h <= log_band_h_dig:
errs.append({"category": "cross_field", "message": "ground_h(%d) ≤ log_band_h_dig(%d)——地面层不得被收起态明细带整条盖住" % [ground_h, log_band_h_dig]})
return errs
+1
View File
@@ -5,6 +5,7 @@
[resource]
script = ExtResource("1")
stage_h = 252
ground_h = 100
info_panel_w = 240
ap_bar_h = 44
log_band_h_dig = 24
+4 -1
View File
@@ -382,7 +382,10 @@ func _on_board_changed() -> void:
run_scope.settlement = run_scope.runner.current_battle()
if run_scope.settlement == null:
return
ui.battle.board_zone.board_view.rebuild(run_scope.settlement.minesweeper.current_board(), run_scope.settlement.minesweeper)
var board = run_scope.settlement.minesweeper.current_board()
ui.battle.board_zone.board_view.rebuild(board, run_scope.settlement.minesweeper)
if board != null:
ui.battle.stage.set_board_type(String(board.board_type)) # 舞台背景选图随板(C8——既有重建点单行同步)
_check_board_hints()
_refresh_presentations()
+4 -1
View File
@@ -100,6 +100,9 @@ static func _on_debug_battle_created(root: Variant, settlement_ref: BattleOrches
static func _on_debug_battle_ready(root: Variant, settlement_ref: BattleOrchestrator, _level: int, _carry: int) -> void:
root.run_scope.settlement = settlement_ref
root.ui.battle.board_zone.board_view.rebuild(root.run_scope.settlement.minesweeper.current_board(), root.run_scope.settlement.minesweeper)
var board = root.run_scope.settlement.minesweeper.current_board()
root.ui.battle.board_zone.board_view.rebuild(board, root.run_scope.settlement.minesweeper)
if board != null:
root.ui.battle.stage.set_board_type(String(board.board_type)) # 舞台背景选图随板(C8——调试就绪重建点单行同步)
root._check_board_hints()
root._refresh_presentations()
+53 -31
View File
@@ -1,5 +1,6 @@
## VERTICAL SLICE - NOT FOR PRODUCTION
## B 区战斗画面:主角/敌方站桩舞台(SubViewport 世界)+ 轮次·阶段指示 + 结算明细带宿主。
## B 区战斗画面:舞台场景构成(§6.1——背景三关变体→连续地面→世界站桩/小石台)+ 轮次·阶段指示 + 结算明细带宿主。
## 光柱层已退役,不迁入背景;平色缺图回退走合并目标 SLOT_BACKDROP(art-bible §4.1 换色合并③)。
## 本类零文案装配:结算流水格式化归 BattleLogView、核心状态→文案装配归 HudPresenter——禁在此拼文案字符串。
class_name BattleStageView
extends Control
@@ -16,44 +17,80 @@ var round_label: Label
var stage_center: Label
var enemy_box: Control # 站桩壳层伴随件容器(T3 语义改写:HBoxContainer → Control)
var log_view: BattleLogView
var bg_texture: TextureRect # 三关背景纹理层(set_board_type 按 board_type 选图)
var ground_layer: TextureRect # 连续地面层(三关共用——高度读布局表 ground_h)
var _cards: Array[Node2D] = [] # StandeeBody(世界本体,敌)
var _row: HBoxContainer # 文本半区行(stage_center 载体 + 世界布局基准)
var _companion_layer: Control # 壳层伴随件层(主角 slot_row 宿主;敌伴随件宿主 = enemy_box)
var _svp_container: SubViewportContainer
var _viewport: SubViewport
var _bg_board_type := "" # 当前背景板型(set_board_type 归一后缓存——同型早退)
var world: Node2D # StageWorld(preload 脚本实例——Node2D 世界根/字体挂点)
func _init(db_ref, layout: BattleLayoutTable) -> void:
custom_minimum_size = Vector2(0, layout.stage_h)
_build_backdrop()
_build_ground(layout)
_build_world_stage()
_build_text_half(db_ref, layout)
_build_companion_layer(db_ref)
## 背景域(圣经 v3.5 §6.1 舞台正面目标:B 区=地窟看台,世界底层暗背景
## =方向稿 render_battle_screen 取值(值单点收 UiKit.STAGE_BG);描边=PANEL_BORDER 同方向稿)
## 背景域(§6.1 舞台场景构成:背景三关变体 → 连续地面 → 世界站桩;平色 Panel 保留为
## 缺图回退底(改指合并目标 SLOT_BACKDROP——art-bible §4.1 换色合并③);描边=PANEL_BORDER。
## 光柱层已退役,不迁入背景)
func _build_backdrop() -> void:
var bg := Panel.new()
var sb := UiKit.panel_flat(UiKit.STAGE_BG, UiKit.PANEL_BORDER, 0, 1)
var sb := UiKit.panel_flat(UiKit.SLOT_BACKDROP, UiKit.PANEL_BORDER, 0, 1)
bg.add_theme_stylebox_override("panel", sb)
bg.set_anchors_preset(Control.PRESET_FULL_RECT)
bg.mouse_filter = Control.MOUSE_FILTER_IGNORE
add_child(bg)
_build_beams_layer()
bg_texture = TextureRect.new()
bg_texture.set_anchors_preset(Control.PRESET_FULL_RECT)
bg_texture.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
bg_texture.stretch_mode = TextureRect.STRETCH_SCALE
bg_texture.mouse_filter = Control.MOUSE_FILTER_IGNORE
bg_texture.visible = false
add_child(bg_texture)
set_board_type("dungeon")
## 顶光柱氛围层域(§6.1 光源氛围——方向稿四柱:x 基准 110/900/1055/1210@1280,暖光(值单点收
## UiKit.BEAM_LIGHT——T4 裸色收口)自顶向下 60 段渐宽窄渐隐〔顶 α26→0〕;绘制在暗底之上/
## 世界站桩之下——层序=bg < 光柱 < 世界)
func _build_beams_layer() -> void:
var beams := Control.new()
beams.set_anchors_preset(Control.PRESET_FULL_RECT)
beams.mouse_filter = Control.MOUSE_FILTER_IGNORE
beams.draw.connect(_draw_light_beams.bind(beams))
beams.resized.connect(beams.queue_redraw)
add_child(beams)
## 背景选图单点(C1——只按 board_type 三值选图,禁按关卡序号/节点深度另造选择逻辑;
## 空串归 dungeon;目标缺图先回 dungeon,再缺保留平色底;缺图不崩不刷产品日志)
func set_board_type(board_type: String) -> void:
var bt := "dungeon" if board_type.is_empty() else board_type
if bt == _bg_board_type:
return
_bg_board_type = bt
var tex := _load_stage_texture(UiKit.TEX_STAGE_BG_FMT % bt)
if tex == null and bt != "dungeon":
tex = _load_stage_texture(UiKit.TEX_STAGE_BG_FMT % "dungeon")
bg_texture.texture = tex
bg_texture.visible = tex != null
## 缺图回退可测单点(背景与地面装载唯一经此——load_tex exists 守卫承载)
func _load_stage_texture(path: String) -> Texture2D:
return UiKit.load_tex(path)
## 连续地面层(C3——底边贴 B 区底、全宽、高度读布局表 ground_h;三关共用;
## 位于背景之上/世界之下——小石台在世界内站桩底绘制,天然在地面之上)
func _build_ground(layout: BattleLayoutTable) -> void:
ground_layer = TextureRect.new()
ground_layer.set_anchors_preset(Control.PRESET_BOTTOM_WIDE)
ground_layer.offset_left = 0
ground_layer.offset_right = 0
ground_layer.offset_bottom = 0
ground_layer.offset_top = -float(layout.ground_h)
ground_layer.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
ground_layer.stretch_mode = TextureRect.STRETCH_SCALE
ground_layer.mouse_filter = Control.MOUSE_FILTER_IGNORE
ground_layer.texture = _load_stage_texture(UiKit.TEX_STAGE_GROUND)
ground_layer.visible = ground_layer.texture != null
add_child(ground_layer)
## Node2D 舞台半区域(战斗舞台迁移批 Phase B 同构;stretch 1:1 无缩放系数——C2/C5)
@@ -131,7 +168,7 @@ func _build_companion_layer(db_ref) -> void: # type-hint: ignore(db 鸭子型
add_child(_companion_layer)
hero_standee = _STANDEE_BODY.new("hero")
var hero_tex := UiKit.load_tex(UiKit.TEX_HERO_PORTRAIT)
hero_standee.set_portrait_texture(hero_tex, true) # B 区立绘 48px 档·normal 态朝右(素材缺位 null 回退字形「人」)
hero_standee.set_portrait_texture(hero_tex, true) # B 区立绘显示档 hero=144(§8.2 三档)·normal 态朝右(素材缺位 null 回退字形「人」)
# 主角三态注册:attack/hit 缺位不注册=该态保持现纹理,缺位回退链延伸
hero_standee.register_hero_state("idle", hero_tex)
hero_standee.register_hero_state("attack", UiKit.load_tex(UiKit.TEX_HERO_ATTACK))
@@ -140,21 +177,6 @@ func _build_companion_layer(db_ref) -> void: # type-hint: ignore(db 鸭子型
hero_standee.slot_row = _make_slot_companion(_companion_layer)
# 顶光柱绘制(§6.1——方向稿同构:每柱 60 段叠层矩形,宽度 ±46px 随 i//3 收窄、高度 40+3i 向下延伸、
# α 26→0 渐隐;同柱相邻段重叠区 α 自然叠加=顶部亮下部渐隐的光井观感;位置随层宽等比例)
func _draw_light_beams(c: Control) -> void:
var w := c.size.x
if w <= 0.0:
return
for bx: float in [110.0, 900.0, 1055.0, 1210.0]:
var x0: float = bx / 1280.0 * w
for i in 60:
var a := int(26.0 * (1.0 - float(i) / 60.0))
var half := 46.0 - float(i) / 3.0
c.draw_rect(Rect2(x0 - half, 0.0, half * 2.0, 40.0 + float(i) * 3.0),
Color(UiKit.BEAM_LIGHT, float(a) / 255.0), true)
func _make_slot_companion(parent: Control) -> StatusSlotRow:
# 状态效果唯一展示面——compact 档伴随件,tooltip 全明细
var comp := StatusSlotRow.new()
+4 -2
View File
@@ -182,8 +182,6 @@ const TOAST_BG := Color(0.94, 0.91, 0.84, 0.95) # toast 面底
const TIP_BG := Color(0.16, 0.12, 0.09, 0.96) # tooltip 深底
# 呈现杂色档(各视图裸 Color8 字面量单点化)
const STAGE_BG := Color8(62, 52, 42) # B 区舞台暗底(方向稿 render_battle_screen——battle_stage_view 面板底)
const BEAM_LIGHT := Color8(255, 236, 200) # B 区顶光柱暖光(用时乘 alpha 渐隐——battle_stage_view 光柱层)
const STAND_STONE := Color8(150, 130, 100) # 站桩石板地台·板身(standee_body 方向稿取色)
const PLAZA_EDGE := Color8(76, 64, 56) # 小石台描边(art-bible §4.1 换色合并②合并目标——贴图已落此值,缺位回退同值)
const CAMP_HEADER_BAND := Color8(110, 69, 38) # 营地柜台头带深棕 #6E4526(4-camp.png 方向稿取色;对齐 demo --wood-deep)
@@ -247,6 +245,10 @@ const TEX_ENEMY_HOOK_HORROR := "res://assets/art/character/character_enemy_hook_
const TEX_ENEMY_DUERGAR_KING := "res://assets/art/character/character_enemy_duergar_king.png" # 灰矮人石君王
# 近景地台独立件(§6.1——源 48×12 九宫格横向拉伸、顶缘受光线 2px 不随拉伸变形;跨三关恒定不吃 wall_ambient tint)
const TEX_STAGE_PLAZA := "res://assets/art/scene/scene_stage_plaza.png"
# 三关舞台背景(§6.1——按 board_type 选图、不走 tint;源 = 基准屏宽 × stage_h)
const TEX_STAGE_BG_FMT := "res://assets/art/scene/scene_stage_bg_%s.png"
# 连续地面层(§6.1——三关共用、无 board_type 后缀、不吃 tint;源 = 基准屏宽 × ground_h)
const TEX_STAGE_GROUND := "res://assets/art/scene/scene_stage_ground.png"
# 营地场景十层(art-bible §6.2——z0 bg 不透明全幅+z1 围墙透明细条+八物件透明层;布局矩形/命中矩形/z 序
# 唯一事实源 = data/tables/camp_scene_layout.tres;素材名跟本组常量走(先定常量防断链);
# 缺位 load_tex→null→程序化占位,冒烟安全)
+14 -1
View File
@@ -117,4 +117,17 @@ func _check_one(label: String, main: Node) -> void:
"13": BoardViewScript._cell_for(13, bv.size),
"14": BoardViewScript._cell_for(14, bv.size),
"avail": str(bv.size),
}
}
# 舞台背景与地面层(真实选路入口进战斗屏后的装载断言——迭代 48 卡③ V2/V4/V6)
var stage: Object = battle_ui.get("stage")
var bg_tex: TextureRect = stage.get("bg_texture")
if bg_tex == null or bg_tex.texture == null:
_problems.append("[%s] 舞台背景纹理未装载(bg_texture 缺位或空纹理)〔舞台背景 V2〕" % label)
elif not String(bg_tex.texture.resource_path).ends_with("_dungeon.png"):
_problems.append("[%s] 舞台背景路径非 dungeon 变体:%s〔舞台背景 V2/V6〕" % [label, String(bg_tex.texture.resource_path)])
var ground: TextureRect = stage.get("ground_layer")
var layout = main.db.presentation.battle_layout
if ground == null or not ground.visible:
_problems.append("[%s] 地面层缺位或不可见〔舞台背景 V4〕" % label)
elif absf(ground.size.y - float(layout.ground_h)) > 0.51:
_problems.append("[%s] 地面层高 %s ≠ 布局表 ground_h %d〔舞台背景 V4〕" % [label, str(ground.size.y), int(layout.ground_h)])
+1 -1
View File
@@ -27,7 +27,7 @@ func _pairs() -> Array:
["RAP标签", UiKit.INK_LABEL, UiKit.PANEL_DATA, 1.0], # N1-7(T0 实测已合规,显式化)
["锚·正文墨/数据板", UiKit.INK, UiKit.PANEL_DATA, 1.0], # 全站锚点:正文墨对数据板
["锚·暖白/屏根", UiKit.WARM, UiKit.BG_ROOT, 1.0], # 全站锚点:暗底正文
["锚·暖白弱/舞台底", UiKit.WARM_FAINT, UiKit.STAGE_BG, 1.0], # 全站锚点:B区辅助墨
["锚·暖白弱/舞台底", UiKit.WARM_FAINT, UiKit.SLOT_BACKDROP, 1.0], # 全站锚点:B区辅助墨(合并目标——art-bible §4.1 换色合并③)
["钥石徽·深墨/烛光黄", UiKit.INK_ON_GOLD, UiKit.CANDLE, 1.0], # badge 工厂件文字(营地徽+状态面板余额行同形制)
["生涯页·行名墨/暖亮页底", UiKit.WARM, UiKit.BG_BRIGHT, 1.0], # 生涯页行名列/提示文(WARM_SOFT 同族亮墨)
["生涯页·行值墨/暖亮页底", UiKit.CANDLE, UiKit.BG_BRIGHT, 1.0], # 生涯页数值列(烛光黄 bold)
+1
View File
@@ -193,6 +193,7 @@ func _suites() -> Array:
{"path": "res://tests/unit/presentation/level_map_info_fog_test.gd", "expect": 0}, # 信息迷雾——呈现显隐/Boss 豁免/详情零显示/大图拟合
{"path": "res://tests/unit/presentation/forge_workbench_test.gd", "expect": 0}, # 工作间两区六路线 UI 直验(V13 自动化前置面)
{"path": "res://tests/unit/presentation/camp_scene_layout_test.gd", "expect": 0}, # 营地场景布局数据件校验(V14——fail-fast 判据+七命中区几何红线)
{"path": "res://tests/unit/presentation/stage_background_test.gd", "expect": 0}, # B 区舞台背景三关变体与连续地面层(V1–V5/V8——ground_h 表单源/board_type 选图/缺图回退/地面几何/光柱退役)
{"path": "res://tests/unit/battle_settlement/damage_math_test.gd", "expect": 0},
{"path": "res://tests/unit/battle_settlement/damage_controller_test.gd", "expect": 0},
{"path": "res://tests/unit/battle_settlement/roll_multiplier_test.gd", "expect": 0}, # 掷点与乘区宿主(敌攻击类型轴/护甲掷点行为面/乘区接线/伤害类型克制与标错/fresh 池攻击链——批 B 自 smoke_all 迁入)
@@ -0,0 +1,165 @@
# VERTICAL SLICE - NOT FOR PRODUCTION
# B 区舞台背景与连续地面层单元套件(迭代 48 卡③)——布局字段/选图/缺图回退/地面几何/光柱退役。
# 判据(改动—验收对照表 V1–V5/V8):ground_h 表单源与校验拒绝、board_type 三值选图与空串归一、
# 缺图 dungeon 回退与平色兜底、地面层底贴 B 区底读表、光柱层与两源常量退役且菜单斜光带在位。
# headless 直建 BattleStageView(null db——背景/地面/文本半区不消费 db;选路真实入口归 layout_probe)。
# 缺图观测 = 子类覆写 _load_stage_texture(不改包内资产、不注入破坏)。
# 运行:godot --headless --path game --script res://tests/unit/presentation/stage_background_test.gd(expect 0)
extends SceneTree
const Kit := preload("res://tests/unit/test_kit.gd")
const StageViewScript := preload("res://presentation/views/battle_stage_view.gd")
# 缺图观测子类一:仅 underdark 路径缺图(deep_underdark/地面/其余走真实 load_tex)
class MissingUnderdarkStage extends BattleStageView:
func _load_stage_texture(path: String) -> Texture2D:
if path.ends_with("scene_stage_bg_underdark.png"):
return null
return UiKit.load_tex(path)
# 缺图观测子类二:全缺图(任何板型与地面都缺 → 平色兜底面)
class AllMissingStage extends BattleStageView:
func _load_stage_texture(_path: String) -> Texture2D:
return null
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
_test_layout_ground_field()
await _test_background_selection()
await _test_background_fallback()
await _test_ground_layer_geometry()
_test_beams_retired()
quit(Kit.finish("stage_background_test"))
# ---- V1 ground_h 字段与校验(表值/默认表 validate 空/三态拒绝族) ----
func _test_layout_ground_field() -> void:
var layout = load("res://data/tables/battle_layout.tres")
Kit.check("布局表 ground_h = 100 且默认表 validate 为空〔舞台背景 V1〕",
int(layout.ground_h) == 100 and layout.validate().is_empty())
var t := BattleLayoutTable.new()
t.ground_h = 20 # ≤ log_band_h_dig=24(收起态明细带整条盖住)
var errs_low: Array = []
for e in t.validate():
if String(e.get("message", "")).contains("ground_h"):
errs_low.append(e)
Kit.check("ground_h=20 → 恰一条含 ground_h 的错误(≤收起带盖住)〔舞台背景 V1〕", errs_low.size() == 1)
t.ground_h = 200 # > stage_h/2 = 126
var errs_high: Array = []
for e in t.validate():
if String(e.get("message", "")).contains("ground_h"):
errs_high.append(e)
Kit.check("ground_h=200 → 恰一条含 ground_h 的错误(超 B 区一半)〔舞台背景 V1〕", errs_high.size() == 1)
t.ground_h = 100
var errs_ok: Array = []
for e in t.validate():
if String(e.get("message", "")).contains("ground_h"):
errs_ok.append(e)
Kit.check("ground_h=100 → 零 ground 相关错误〔舞台背景 V1〕", errs_ok.is_empty())
func _make_stage() -> Node:
var stage: Node = StageViewScript.new(null, load("res://data/tables/battle_layout.tres"))
root.add_child(stage)
return stage
# ---- V2 背景按 board_type 选图(直建默认 dungeon + 三值路径逐值对应 + 层序在世界之下) ----
func _test_background_selection() -> void:
var layout = load("res://data/tables/battle_layout.tres")
var stage: Node = _make_stage()
await process_frame
var bg: TextureRect = stage.bg_texture
Kit.check("直建默认装载 dungeon 背景(纹理非空且 1280×stage_h)〔舞台背景 V2〕",
bg != null and bg.texture != null
and bg.texture.get_width() == 1280 and bg.texture.get_height() == int(layout.stage_h))
for bt: String in ["underdark", "deep_underdark", "dungeon"]:
stage.set_board_type(bt)
var tex: Texture2D = bg.texture
Kit.check("set_board_type(%s) → 纹理路径以 _%s.png 结尾且尺寸不变〔舞台背景 V2〕" % [bt, bt],
tex != null and String(tex.resource_path).ends_with("_%s.png" % bt)
and tex.get_width() == 1280 and tex.get_height() == 252)
var world_idx: int = stage.world.get_parent().get_parent().get_index()
Kit.check("背景层序在世界之下(bg index < world 容器 index)〔舞台背景 V2〕",
bg.get_index() < world_idx)
# V6 源码锚:两处接板重建点(main 既有链+调试就绪链)均同步舞台板型
for src_path: String in ["res://presentation/main.gd", "res://presentation/main_debug_flow.gd"]:
var sf := FileAccess.open(src_path, FileAccess.READ)
var ssrc := sf.get_as_text() if sf != null else ""
Kit.check("接板同步源码锚 %s 含 stage.set_board_type(〔舞台背景 V6〕" % src_path.get_file(),
ssrc.contains("stage.set_board_type("))
stage.queue_free()
await process_frame
# ---- V3 缺图回退(目标缺→dungeon;全缺→平色兜底不崩不刷产品日志) ----
func _test_background_fallback() -> void:
var s1: Node = MissingUnderdarkStage.new(null, load("res://data/tables/battle_layout.tres"))
root.add_child(s1)
await process_frame
s1.set_board_type("underdark")
var bg1: TextureRect = s1.bg_texture
Kit.check("目标缺图 → 回落 dungeon 路径〔舞台背景 V3〕",
bg1.texture != null and String(bg1.texture.resource_path).ends_with("_dungeon.png"))
s1.queue_free()
await process_frame
# 全 null 子类——任何板型都缺图 → 平色兜底(texture null、visible false、无异常)
var s2: Node = AllMissingStage.new(null, load("res://data/tables/battle_layout.tres"))
root.add_child(s2)
await process_frame
s2.set_board_type("dungeon")
var bg2: TextureRect = s2.bg_texture
Kit.check("全缺图 → texture null 且 visible false(平色兜底无异常)〔舞台背景 V3〕",
bg2.texture == null and not bg2.visible)
s2.queue_free()
await process_frame
# ---- V4 连续地面层几何(底贴 B 区底、高读表、全宽、位于背景与世界之间) ----
func _test_ground_layer_geometry() -> void:
var layout = load("res://data/tables/battle_layout.tres")
var stage: Node = _make_stage()
await process_frame
var ground: TextureRect = stage.ground_layer
Kit.check("地面纹理可加载且 (1280, ground_h)〔舞台背景 V4〕",
ground.texture != null and ground.texture.get_width() == 1280
and ground.texture.get_height() == int(layout.ground_h))
Kit.check("地面锚点 0/1/1/1 且 offset 四位归零顶=-ground_h〔舞台背景 V4〕",
ground.anchor_left == 0.0 and ground.anchor_top == 1.0
and ground.anchor_right == 1.0 and ground.anchor_bottom == 1.0
and ground.offset_left == 0.0 and ground.offset_right == 0.0
and ground.offset_bottom == 0.0 and int(ground.offset_top) == -int(layout.ground_h))
stage.size = Vector2(1280, int(layout.stage_h))
await process_frame
Kit.check("1280 档地面实读 size=(1280, ground_h)、position.y=stage_h-ground_h〔舞台背景 V4〕",
ground.size == Vector2(1280, float(layout.ground_h))
and absf(ground.position.y - (float(layout.stage_h) - float(layout.ground_h))) < 0.51)
var world_idx: int = stage.world.get_parent().get_parent().get_index()
Kit.check("图层序 bg < ground < world〔舞台背景 V4〕",
ground.get_index() > stage.bg_texture.get_index() and ground.get_index() < world_idx)
stage.queue_free()
await process_frame
# ---- V5 光柱层与舞台底源常量退役(源码锚 + 常量表 + 菜单斜光带在位) ----
func _test_beams_retired() -> void:
var f := FileAccess.open("res://presentation/views/battle_stage_view.gd", FileAccess.READ)
var src := f.get_as_text() if f != null else ""
Kit.check("舞台源码零光柱残留且平色回退含合并目标 SLOT_BACKDROP〔舞台背景 V5〕",
not src.is_empty()
and not src.contains("_build_beams_layer") and not src.contains("_draw_light_beams")
and not src.contains("BEAM_LIGHT") and src.contains("UiKit.SLOT_BACKDROP"))
var uikit_s: Script = load("res://presentation/views/ui_kit.gd")
var cmap: Dictionary = uikit_s.get_script_constant_map()
Kit.check("UiKit 常量表退役 BEAM_LIGHT/STAGE_BG 零键〔舞台背景 V5〕",
not cmap.has("BEAM_LIGHT") and not cmap.has("STAGE_BG"))
var uf := FileAccess.open("res://presentation/views/ui_kit.gd", FileAccess.READ)
var usrc := uf.get_as_text() if uf != null else ""
Kit.check("菜单斜光带 _beams_rect 仍在(不在退役面)〔舞台背景 V5〕",
usrc.contains("_beams_rect"))
@@ -0,0 +1 @@
uid://dskho0aqpongv
@@ -665,7 +665,7 @@ func _smoke_texture_filter() -> void:
func _smoke_contrast_inks() -> void:
print("--- UI 残余 <4.5:1 文字位换墨(V4 对比度锚定)---")
var uikit_s = load("res://presentation/views/ui_kit.gd")
var stage: Color = uikit_s.STAGE_BG
var stage: Color = uikit_s.SLOT_BACKDROP # 舞台底对比锚改读合并目标(art-bible §4.1 换色合并③——阈值与判据不变)
var r_order := _theme_contrast(uikit_s.GOLD_BRIGHT, stage)
Kit.check("V4 顺位徽标/重甲徽标 GOLD_BRIGHT 对舞台底 %.2f:1 ≥4.5:1" % r_order, r_order >= 4.5)
var r_ward := _theme_contrast(uikit_s.HERO_BLUE_BRIGHT, stage)
+1 -1
View File
@@ -9,7 +9,7 @@ import re
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
TABLE_PATH = os.path.join(ROOT, "game", "data", "tables", "battle_layout.tres")
FIELDS = ("stage_h", "info_panel_w", "ap_bar_h", "log_band_h_dig", "log_band_h_settle")
FIELDS = ("stage_h", "ground_h", "info_panel_w", "ap_bar_h", "log_band_h_dig", "log_band_h_settle")
def load(path=TABLE_PATH):