Add native Durak, poker, blackjack, roulette, Buckshot and billiards scenes with animation assets and regression tests. Improve arrival, city, hall, profiles, shops, audio and exit handling; remove the portable C game core and refresh shipping executables.
52 lines
2.8 KiB
Python
52 lines
2.8 KiB
Python
"""Check/export the real MASM startup frames, not a substitute renderer."""
|
|
import hashlib
|
|
import json
|
|
import struct
|
|
from pathlib import Path
|
|
|
|
from PIL import Image, ImageChops, ImageDraw, ImageStat
|
|
|
|
|
|
def main():
|
|
root = Path(__file__).resolve().parents[2]
|
|
output = root / "build/actions"
|
|
times = [0, 240, 700, 1300, 2500, 6500]
|
|
frames = [Image.open(output / f"studio-{time}.bmp").convert("RGB") for time in times]
|
|
assert all(frame.size == (640, 480) for frame in frames)
|
|
hashes = [hashlib.sha256(frame.tobytes()).hexdigest() for frame in frames]
|
|
assert len(set(hashes)) == len(hashes), "Ident animation frames are identical"
|
|
means = [sum(ImageStat.Stat(frame).mean) / 3 for frame in frames]
|
|
assert means[0] > 1, "The first visible frame is blank"
|
|
assert means[3] > means[2] > means[1] > means[0], "The reveal did not brighten progressively"
|
|
packed = (root / "assets/animations/intro/eblan-games.bin").read_bytes()
|
|
magic, width, height, size = struct.unpack_from("<4sIII", packed)
|
|
assert (magic, width, height, size) == (b"EBG1", 640, 480, 1228800)
|
|
assert len(packed) == size + 16
|
|
original = Image.frombytes("RGBA", (640, 480), packed[16:], "raw", "BGRA").convert("RGB")
|
|
difference = ImageStat.Stat(ImageChops.difference(original, frames[3]).crop((0, 0, 640, 420)))
|
|
assert max(difference.mean) < 8, "The settled ident is distorted or corrupted"
|
|
originals = [Image.open(output / f"intro-{time}.bmp").convert("RGB") for time in times]
|
|
original_hashes = [hashlib.sha256(frame.tobytes()).hexdigest() for frame in originals]
|
|
assert len(set(original_hashes)) == len(original_hashes), "Original animation is frozen"
|
|
assert sum(ImageStat.Stat(originals[3]).mean) > sum(ImageStat.Stat(originals[1]).mean)
|
|
sequence = (root / "assets/animations/intro/eblan-sequence.bin").read_bytes()
|
|
header = struct.unpack_from("<4s8I", sequence)
|
|
assert header == (b"EBGA", 72, 21, 100, 36, 45, 55, 307520, 1)
|
|
assert len(sequence) == 36 + header[7]
|
|
fallback = Image.open(output / "intro-fallback.bmp").convert("RGB")
|
|
assert len(fallback.getcolors(640 * 480)) >= 3, "Fallback branding is missing"
|
|
sheet = Image.new("RGB", (960, 1048), "#121718")
|
|
draw = ImageDraw.Draw(sheet)
|
|
for index, frame in enumerate(originals + frames):
|
|
x, y = (index % 3) * 320, (index // 3) * 262
|
|
sheet.paste(frame.resize((320, 240), Image.Resampling.NEAREST), (x, y))
|
|
variant = "Original" if index < 6 else "Studio / F2"
|
|
draw.text((x + 8, y + 244), f"MASM {variant} / {times[index % 6]} ms", fill="#e8d2ae")
|
|
sheet.save(output / "intro-contact-sheet.png")
|
|
originals[4].save(output / "intro-ready.png")
|
|
print(json.dumps({"result": "PASS", "original_frames": 100, "rendered_variants": 2,
|
|
"frame_mean_brightness": means, "snapshots": 12}))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|