Files
aetherbound-guild/runtime/tools/m79_atlas_visual_probe.gd
T

131 lines
6.2 KiB
GDScript

extends SceneTree
## M79-I10 gate: the Region Atlas page renders at the single target size.
##
## Two locales, two text scales, three views (static / dynamic / final) with the
## shared ink checker on every capture. Fixtures are copied facts, not state.
const AtlasView = preload("res://entry/views/region_atlas_page_view.gd")
const AtlasController = preload("res://entry/region_atlas_page_controller.gd")
const InkCheck = preload("res://tools/ui_ink_check.gd")
const FIXTURE := [{"width": 1280.0, "height": 720.0}]
const LOCALES := ["zh_CN", "en"]
const TEXT_SCALES := [1.0, 1.3]
var output_root := ""
var ink_scanned := 0
var ink_violations: Array = []
var probe_text_scale := 1.0
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
if OS.get_environment("ABG_EVIDENCE_DIR").is_empty():
push_error("atlas probe requires the owned test-attempt runner")
quit(1)
return
output_root = OS.get_environment("ABG_EVIDENCE_DIR").path_join("atlas-visual-probe")
DirAccess.make_dir_recursive_absolute(output_root)
var self_test := InkCheck.self_test(root)
if self_test.size() < 3:
push_error("M79 ink self-test expected at least 3 violations, got %d" % self_test.size())
quit(1)
return
print("ABG_M79_ATLAS_INK_SELF_TEST_OK violations=%d" % self_test.size())
for fixture in FIXTURE:
for locale in LOCALES:
for scale in TEXT_SCALES:
await _capture(int(fixture.width), int(fixture.height), String(locale), float(scale))
if not ink_violations.is_empty():
for violation in ink_violations:
push_error("M79 atlas ink %s %s [%s] %s: %s" % [
violation.locale, violation.size, violation.path, violation.node, violation.detail])
print("ABG_M79_ATLAS_INK_FAIL scanned=%d violations=%d" % [ink_scanned, ink_violations.size()])
quit(1)
return
print("ABG_M79_ATLAS_INK_OK scanned=%d violations=0" % ink_scanned)
print("ABG_M79_ATLAS_VISUAL_PROBE_OK locales=2 scales=2 scans=%d views=final" % ink_scanned)
quit(0)
func _fixture_data(locale: String) -> Dictionary:
return {
"ok": true,
"page_id": "region_atlas",
"locale": locale,
"cleared_regions": 2,
"total_regions": 8,
"renown": 46,
"clear_facts": 3,
"route_complete": false,
"regions": [
{"id": "RG-01", "name": "风镜原野" if locale == "zh_CN" else "Windglass Verge",
"framing": "开阔草台地上悬浮矿镜导引以太风" if locale == "zh_CN" else "open grass terraces where suspended mineral panes redirect aether gusts",
"equipment_band": "EQ-001..EQ-040", "enemies": 12, "bosses": 2, "cleared": true, "clear_count": 2},
{"id": "RG-02", "name": "淤醒沼泽" if locale == "zh_CN" else "Siltwake Fens",
"framing": "芦苇水道在可移堤岸间搬运发光淤沙" if locale == "zh_CN" else "reed channels carry luminous sediment between movable banks",
"equipment_band": "EQ-041..EQ-080", "enemies": 12, "bosses": 2, "cleared": true, "clear_count": 1},
{"id": "RG-03", "name": "余烬织坊" if locale == "zh_CN" else "Emberloom Works",
"framing": "公共窑炉借旧陶管编织热量" if locale == "zh_CN" else "communal kilns weave heat through old ceramic conduits",
"equipment_band": "EQ-081..EQ-120", "enemies": 12, "bosses": 2, "cleared": false, "clear_count": 0},
{"id": "RG-04", "name": "卷根档案馆" if locale == "zh_CN" else "Quillroot Archive",
"framing": "活根书架以叶纹保存路线契约" if locale == "zh_CN" else "living root shelves preserve route contracts as changing leaf patterns",
"equipment_band": "EQ-121..EQ-160", "enemies": 12, "bosses": 2, "cleared": false, "clear_count": 0},
{"id": "RG-05", "name": "潮镜滩" if locale == "zh_CN" else "Tideglass Reach",
"framing": "盐滩镜池乱序重复剪影" if locale == "zh_CN" else "salt flats hold mirror pools that repeat silhouettes out of sequence",
"equipment_band": "EQ-161..EQ-200", "enemies": 13, "bosses": 2, "cleared": false, "clear_count": 0},
{"id": "RG-06", "name": "空轨果园" if locale == "zh_CN" else "Skyrail Orchard",
"framing": "悬轨果园在高空窗口间运送果实" if locale == "zh_CN" else "skyrail orchards move harvests between airborne windows",
"equipment_band": "EQ-201..EQ-240", "enemies": 13, "bosses": 2, "cleared": false, "clear_count": 0},
{"id": "RG-07", "name": "夜流盆地" if locale == "zh_CN" else "Nightcurrent Basin",
"framing": "暗流盆地以潮流预告拒绝与盗取施压" if locale == "zh_CN" else "dark current basin pressures forecasts with denial and theft",
"equipment_band": "EQ-241..EQ-280", "enemies": 13, "bosses": 2, "cleared": false, "clear_count": 0},
{"id": "RG-08", "name": "晨冠" if locale == "zh_CN" else "Crown of Morn",
"framing": "路线之巅的晨光高台" if locale == "zh_CN" else "a high sunrise terrace crowning the route",
"equipment_band": "EQ-281..EQ-320", "enemies": 13, "bosses": 2, "cleared": false, "clear_count": 0},
],
}
func _capture(width: int, height: int, locale: String, text_scale: float) -> void:
probe_text_scale = text_scale
root.content_scale_mode = Window.CONTENT_SCALE_MODE_CANVAS_ITEMS
root.content_scale_aspect = Window.CONTENT_SCALE_ASPECT_KEEP
root.content_scale_size = Vector2i(1280, 720)
root.size = Vector2i(width, height)
var view := AtlasView.new()
view.name = "AtlasVisualProbeView"
view.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
view.configure_presentation({"safe_area": 24, "text_scale": text_scale, "locale": locale})
root.add_child(view)
view.render(_fixture_data(locale))
await process_frame
await RenderingServer.frame_post_draw
_save_image("%s/%s-1280x720-atlas-static.png" % [output_root, locale])
var violations := InkCheck.scan(view)
ink_scanned += 1
for violation in violations:
violation["locale"] = locale
violation["size"] = "1280x720"
ink_violations.append(violation)
view.visible = false
var neutral := ColorRect.new()
neutral.color = Color("#26353c")
neutral.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
neutral.mouse_filter = Control.MOUSE_FILTER_IGNORE
root.add_child(neutral)
view.visible = true
view.queue_free()
neutral.queue_free()
await process_frame
func _save_image(path: String) -> void:
var image := root.get_texture().get_image()
if image != null and not image.is_empty():
image.save_png(path)