"""Inspect actual MASM dialog frames and compare tested/shipping NPC procedures.""" import argparse import hashlib from pathlib import Path from PIL import Image, ImageDraw from check_card_exit_build import Coff ROOT = Path(__file__).resolve().parents[2] def captures(): sheet = Image.new("RGB", (640, 3 * 504), (20, 25, 29)) draw = ImageDraw.Draw(sheet) hashes = [] for index, name in enumerate(("open", "ready", "done")): path = ROOT / "build/actions" / f"npc-dialog-{name}.bmp" frame = Image.open(path).convert("RGB") assert frame.size == (640, 480) assert len(frame.getcolors(640 * 480)) > 1000, name assert frame.getpixel((60, 96)) == (186, 153, 85), "dialog border" hashes.append(hashlib.sha256(frame.crop((60, 96, 580, 398)).tobytes()).digest()) frame.save(path.with_suffix(".png")) sheet.paste(frame, (0, index * 504 + 24)) draw.text((8, index * 504 + 6), name, fill="white") assert len(set(hashes)) == 3, "Contract status/reward must visibly change" sheet.save(ROOT / "build/actions/npc-dialog-contact.png") print("PASS: three distinct native NPC dialog frames, bounded modal and changing contract status") def parity(): tested = Coff(ROOT / "build/actions/actions-test.obj") shipped = Coff(ROOT / "build/graphics/poker.obj") procedures = (("InitNpcLife3D", 0), ("UpdateNpcLife3D", 0), ("RenderNpcLife3D", 0), ("NpcPlanPath3D", 8), ("NpcTryCell3D", 8), ("NpcMove3D", 8), ("NpcCanStep3D", 12), ("CheckNpcInteractions3D", 0), ("OpenNpcDialog3D", 0), ("HandleNpcDialog3D", 0), ("RenderNpcDialog3D", 0), ("CloseNpcDialog3D", 0), ("NpcContractType3D", 4), ("NpcAcceptContract3D", 4), ("NpcClaimContract3D", 4), ("NpcRecordRound3D", 8), ("NpcSaveJournal3D", 0), ("NpcNameText3D", 4), ("AnimateCityPedestrians3D", 0), ("FinalizeSkullsGuess", 0)) for name, size in procedures: symbol = f"_{name}@{size}" assert tested.signature(symbol) == shipped.signature(symbol), name print("PASS: shipping NPC navigation, poses, dialogs, rewards and street movement match tested MASM") if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--parity", action="store_true") args = parser.parse_args() captures() if args.parity: parity()