"""Pack generated tarot-room sprites. Offline tooling, never a game runtime.""" from pathlib import Path import struct from PIL import Image ROOT = Path(__file__).resolve().parents[2] def sprites(name, row_split, bottom_split=.5, top_split=.5): source = ROOT / "assets/sprites/hall" / name sheet = Image.open(source).convert("RGBA") w, h = sheet.size frames = [] # The source has tightly spaced silhouettes. Bounds are checked on alpha. # Each cell is independently fitted, but the two idle poses share one scale. # The generated first row ends above the bookcase's crescent crest. split = round(h * row_split) top = round(w * top_split) cells = [sheet.crop((0,0,top,split)), sheet.crop((top,0,w,split)), sheet.crop((0,split,round(w*bottom_split),h)), sheet.crop((round(w*bottom_split),split,w,h))] bounds = [cell.getchannel("A").point(lambda a: 255 if a > 32 else 0).getbbox() for cell in cells] assert all(bounds), "missing transparent sprite" cropped = [cell.crop(box) for cell, box in zip(cells, bounds)] scale = min(242/max(c.width for c in cropped[:2]), 252/max(c.height for c in cropped[:2])) for i, cell in enumerate(cropped): factor = scale if i < 2 else min(242/cell.width, 252/cell.height) cell = cell.resize((round(cell.width*factor), round(cell.height*factor)), Image.Resampling.LANCZOS) frame = Image.new("RGBA", (256,256)) frame.alpha_composite(cell, ((256-cell.width)//2, 256-cell.height)) frames.append(frame) return frames def pack(name, frames): payload = b"".join(frame.tobytes("raw", "BGRA") for frame in frames) target = ROOT / "assets/animations/hall" / name target.write_bytes(struct.pack("<4s4I",b"ANM1",256,256,len(frames),len(payload))+payload) def main(): frames = sprites("fortune-room-v2.png", .47) frames += sprites("fortune-decor-v2.png", .52, .60) frames += sprites("fortune-wall-v2.png", .46, top_split=.55) frames += sprites("fortune-cabinets-v2.png", .58, top_split=.47) frames += sprites("fortune-den-v3.png", .575, .61, top_split=.64) frames += sprites("fortune-clutter-v3.png", .53, .55, top_split=.515) pack("fortune-room-v3.bin", frames) sheet = Image.open(ROOT / "assets/sprites/hall/fortune-materials-v3.png").convert("RGBA") w, h = sheet.size materials = [sheet.crop(box).resize((256,256), Image.Resampling.LANCZOS) for box in ((0,0,w//2,h//2), (w//2,0,w,h//2), (0,h//2,w//2,h), (w//2,h//2,w,h))] pack("fortune-materials-v3.bin", materials) preview = Image.new("RGBA", (1024,1536), (22,30,48,255)) for i, frame in enumerate(frames): preview.alpha_composite(frame,((i%4)*256,(i//4)*256)) preview.save(ROOT / "build/hall-expansion/fortune-art-preview.png") print("Packed twenty-four den sprites and four worn cloth/stone materials.") if __name__ == "__main__": main()