"""Offline conversion of generated guest art into the existing ANM1 format.""" from pathlib import Path import struct from PIL import Image ROOT = Path(__file__).resolve().parents[2] SOURCE = ROOT / "assets/sprites/hall/guest-cast-v1.png" TARGET = ROOT / "assets/animations/hall/guest-cast-v1.bin" def isolate(cell): alpha = cell.getchannel("A") pixels = alpha.load() remaining = {(x,y) for y in range(cell.height) for x in range(cell.width) if pixels[x,y] >= 48} components = [] while remaining: point = remaining.pop() component, pending = {point}, [point] while pending: x,y = pending.pop() for other in ((x-1,y),(x+1,y),(x,y-1),(x,y+1)): if other in remaining: remaining.remove(other) component.add(other) pending.append(other) components.append(component) largest = max(components,key=len) mask = Image.new("L",cell.size) for x,y in largest: mask.putpixel((x,y),pixels[x,y]) cell.putalpha(mask) return cell.crop(cell.getbbox()) def main(): sheet = Image.open(SOURCE).convert("RGBA") frames = [] # The approved sheet contains idle, three gait poses and a seated row. # Repeat the passing pose to close the four-phase walk without a jump. for column in range(6): boundaries = (0,277,535,795,1055,sheet.height) cells = [isolate(sheet.crop((column*sheet.width//6, boundaries[row], (column+1)*sheet.width//6, boundaries[row+1]))) for row in range(5)] scale = min(120/max(cell.width for cell in cells[:4]), 204/max(cell.height for cell in cells[:4])) for row in (0, 1, 2, 3, 2, 4): cell = cells[row] pose_scale = min(scale,124/cell.width,160/cell.height) if row == 4 else scale size = (max(1, round(cell.width*pose_scale)), max(1, round(cell.height*pose_scale))) cell = cell.resize(size, Image.Resampling.LANCZOS) if column == 1 and row == 4: cell = cell.transpose(Image.Transpose.FLIP_LEFT_RIGHT) frame = Image.new("RGBA", (128, 224)) frame.alpha_composite(cell, ((128-size[0])//2, 220-size[1])) frames.append(frame) payload = b"".join(frame.tobytes("raw", "BGRA") for frame in frames) TARGET.write_bytes(struct.pack("<4s4I", b"ANM1",128,224,len(frames),len(payload))+payload) contact = Image.new("RGBA", (6*128,6*224), (24,28,29,255)) for index, frame in enumerate(frames): contact.alpha_composite(frame, ((index//6)*128,(index%6)*224)) contact.save(ROOT / "build/guest-cast-preview.png") print("Packed six guests, six frames each:", TARGET) if __name__ == "__main__": main()