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

110 lines
4.6 KiB
GDScript

extends SceneTree
const View = preload("res://entry/views/contract_choice_page_view.gd")
const InkCheck = preload("res://tools/ui_ink_check.gd")
const FIXTURES := [Vector2i(1280, 720)]
const LOCALES := ["zh_CN", "en"]
# Accessibility pass: 1.0 plus the shipped 130% text scale (M73).
const TEXT_SCALES := [1.0, 1.3]
var ink_scanned := 0
var probe_text_scale := 1.0
var ink_violations: Array = []
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
var out := OS.get_environment("ABG_EVIDENCE_DIR")
if out.is_empty():
push_error("requires ABG_EVIDENCE_DIR")
quit(1)
return
DirAccess.make_dir_recursive_absolute(out)
var ink_self := InkCheck.self_test(root)
if ink_self.size() < 3:
push_error("M62 ink self-test expected at least 3 violations, got %d" % ink_self.size())
quit(1)
return
print("ABG_M62_INK_SELF_TEST_OK violations=%d" % ink_self.size())
ink_scanned = 0
ink_violations = []
for fixture in FIXTURES:
for locale in LOCALES:
for text_scale in TEXT_SCALES:
probe_text_scale = float(text_scale)
await _capture(fixture, locale, out)
var report_path := OS.get_environment("ABG_EVIDENCE_DIR").path_join("ink-violations-%s.json" % _probe_id())
var file := FileAccess.open(report_path, FileAccess.WRITE)
if file != null:
file.store_string(JSON.stringify(ink_violations, " "))
file.close()
if ink_violations.is_empty():
print("ABG_M54_CONTRACT_INK_OK scanned=%d violations=0" % ink_scanned)
else:
for violation in ink_violations:
push_error("M54 ink violation %s: text=%s slot=%s font=%s" % [String(violation.get("node", "?")), String(violation.get("text_width", "?")), String(violation.get("slot_width", "?")), String(violation.get("font_size", "?"))])
print("ABG_M54_CONTRACT_INK_FAIL scanned=%d violations=%d" % [ink_scanned, ink_violations.size()])
quit(1)
return
print("ABG_M54_CONTRACT_VISUAL_PROBE_OK locales=%d scans=%d views=static,dynamic,final" % [LOCALES.size(), ink_scanned])
quit(0)
func _probe_id() -> String:
return "m54-contract"
func _scan_ink(view: Control, locale: String, label: String) -> void:
# Legacy-chrome pages (pre-M62) record their ink defects as a baseline for
# the W5 migration; migrated pages (Market, Readiness once moved) must be
# strict. The report file is the machine-readable work list.
var violations := InkCheck.scan(view)
ink_scanned += 1
for violation in violations:
violation["locale"] = locale
violation["fixture"] = label
ink_violations.append(violation)
func _capture(fixture: Vector2i, locale: String, out: String) -> void:
var viewport := SubViewport.new()
viewport.size = fixture
viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS
root.add_child(viewport)
var view := View.new()
view.name = "ContractVisualProbeView"
view.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
view.configure_presentation({"safe_area": 24, "text_scale": probe_text_scale, "locale": locale})
viewport.add_child(view)
view.render(_fixture(locale))
await _settle()
_scan_ink(view, locale, "%dx%d" % [fixture.x, fixture.y])
_save(viewport, out, fixture, locale, "final")
var overlay := view.find_child("ContractChoiceFrame", true, false) as Control
if overlay != null:
overlay.visible = false
await _settle()
_save(viewport, out, fixture, locale, "static")
if overlay != null:
overlay.visible = true
var world := view.find_child("WorldBackground", true, false) as Control
if world != null:
world.visible = false
await _settle()
_save(viewport, out, fixture, locale, "dynamic")
view.queue_free()
viewport.queue_free()
await process_frame
func _settle() -> void:
for _i in range(3):
await process_frame
func _save(viewport: SubViewport, out: String, fixture: Vector2i, locale: String, kind: String) -> void:
var image := viewport.get_texture().get_image()
if image != null:
image.save_png(out.path_join("contract-%dx%d-%s-%s.png" % [fixture.x, fixture.y, locale, kind]))
func _fixture(locale: String) -> Dictionary:
var zh := locale == "zh_CN"
return {"ok": true, "locale": locale, "company": {"owned_count": 2, "recruits": [{"id":"a"},{"id":"b"}]}, "selected_difficulty":"standard", "difficulties":[{"id":"standard","name":"标准" if zh else "Standard","copy":"稳妥的首次远征" if zh else "A steady first expedition."}], "contract":{"route_title":"风镜初渡" if zh else "FIRST WINDGLASS CROSSING", "route_copy":"穿越悬浮风镜,建立安全通路。" if zh else "Cross the suspended windglass and establish a safe route.", "starting_coin":38,"line_capacity":4,"market_counts":{"recruit":3,"equipment":2,"battle":1}}, "confirm":{"eligible":true}, "notice":""}