#!/usr/bin/env python3 """Compose the local-only sprite atlases from the raw asset packs. Both inputs and outputs are licence-restricted and therefore untracked (see docs/ASSETS.md), which is why this is a build step rather than a committed file: anyone with the packs can regenerate, and nobody without them is blocked. pip install pillow && python3 tools/build_local_assets.py The bullet pack's layout is not obvious and cost a wrong guess once: * Each of the 8 PNGs is one ANIMATION FRAME, not one sprite sheet. * Within a file, a row is a bullet shape and a column is a COLOUR variant. So animating means cycling files, not walking columns -- walking columns just cycles through palettes while the shape sits still. Output is one tidy atlas: 8 frames across, one row per SimConfig.KIND_*. """ import os import sys try: from PIL import Image except ImportError: sys.exit("needs pillow: pip install pillow") ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) SRC = os.path.join(ROOT, "assetpacks", "New_All_Fire_Bullet_Pixel_16x16", "All_Fire_Bullet_Pixel_16x16_%02d.png") OUT = os.path.join(ROOT, "assets", "local", "bullets.png") CELL = 16 FRAMES = 8 # (source row, source column) per bullet kind, in SimConfig.KIND_* order. # Column selects the colour: 0 red, 1 orange, 2 amber, 3 gold, 4 bright yellow. KINDS = [ ("player_shot", 11, 4), # small bright dot ("orb", 2, 1), # round orange orb ("needle", 11, 6), # horizontal dart, drawn rotated to velocity ("heavy", 1, 0), # big red ball ] def main() -> None: for f in range(FRAMES): if not os.path.exists(SRC % f): sys.exit("missing source frame: %s\n" "The raw pack is untracked; see assets/local/README.md." % (SRC % f)) out = Image.new("RGBA", (CELL * FRAMES, CELL * len(KINDS)), (0, 0, 0, 0)) for f in range(FRAMES): src = Image.open(SRC % f).convert("RGBA") for k, (_name, row, col) in enumerate(KINDS): box = (col * CELL, row * CELL, (col + 1) * CELL, (row + 1) * CELL) out.paste(src.crop(box), (f * CELL, k * CELL)) os.makedirs(os.path.dirname(OUT), exist_ok=True) out.save(OUT) print("wrote %s (%dx%d): %d kinds x %d frames" % (OUT, out.width, out.height, len(KINDS), FRAMES)) for k, (name, row, col) in enumerate(KINDS): print(" row %d = %-12s (source row %2d, colour col %d)" % (k, name, row, col)) if __name__ == "__main__": main()