"""Compare real MASM lightmap frames; this script is offline QA, not game code.""" import json import struct from pathlib import Path from PIL import Image, ImageChops, ImageStat def main(): root = Path(__file__).resolve().parents[2] output = root / "build/actions" off = Image.open(output / "street-light-off.bmp").convert("RGB") on = Image.open(output / "street-light-on.bmp").convert("RGB") assert on.size == off.size == (640, 480) changed_ground = 0 warm_ground = 0 saturated = 0 for y in range(280, 480): for x in range(640): before, after = off.getpixel((x, y)), on.getpixel((x, y)) if before != after: changed_ground += 1 if after[0] - before[0] > after[2] - before[2]: warm_ground += 1 saturated += min(after) == 255 assert changed_ground > 2000, "Lamp/window spill is not visible on the ground" assert warm_ground > changed_ground * .7, "Light is not warm/localized" assert saturated < 1000, "Lighting washed out the pavement" assert sum(ImageStat.Stat(on).stddev) > 30, "Texture detail disappeared" on.save(output / "street-light-on.png") off.save(output / "street-light-off.png") ImageChops.difference(on, off).save(output / "street-light-difference.png") for name in ("poker-exit", "blackjack-exit"): frame = Image.open(output / f"{name}.bmp").convert("RGB") assert frame.size == (640, 480) assert frame.getpixel((40, 128)) == (213, 199, 158), name assert frame.getpixel((64, 300)) == (213, 199, 158), name assert frame.getpixel((328, 300)) == (85, 101, 85), name frame.save(output / f"{name}.png") elapsed = struct.unpack("<2I", (output / "street-light-perf.bin").read_bytes()) report = { "result": "PASS", "changed_ground_pixels": changed_ground, "warm_ground_pixels": warm_ground, "saturated_ground_pixels": saturated, "frame_ms_light_off": round(elapsed[0] / 12, 2), "frame_ms_light_on": round(elapsed[1] / 12, 2), "note": "Headless native render timing, not a whole-game FPS benchmark", } (output / "city-lighting-report.json").write_text(json.dumps(report, indent=2)) print(json.dumps(report)) if __name__ == "__main__": main()