#!/usr/bin/env python3 """Deterministically package imagegen artwork and synthesize mission PCM cues. Source artwork is generated by the built-in image tool. This packaging step only resizes/quantizes it and slices a shared terrain canvas; it paints no art. """ import argparse import hashlib import json import math import random import struct import wave from pathlib import Path from PIL import Image, ImageOps ROOT = Path(__file__).resolve().parent def palette_image(path): raw = path.read_bytes() if len(raw) != 768: raise ValueError("expected 768-byte original VGA palette") colors = [(min(v, 63) << 2) | (min(v, 63) >> 4) for v in raw] palette = Image.new("P", (1, 1)) palette.putpalette(colors) return palette, colors def sd(source, size, palette): resized = source.convert("RGB").resize(size, Image.Resampling.LANCZOS) return resized.quantize(palette=palette, dither=Image.Dither.NONE).convert("RGBA") def synth(name, duration, notes, alarm=False): rate = 22050 rng = random.Random(70404 + sum(map(ord, name))) samples = bytearray() for i in range(int(rate * duration)): t = i / rate segment = min(len(notes) - 1, int(t / duration * len(notes))) local = t / duration * len(notes) - segment freq = notes[segment] envelope = min(1.0, local * 30) * (1 - local) ** 1.3 tone = math.sin(2 * math.pi * freq * t) tone += 0.23 * math.sin(2 * math.pi * freq * 2 * t) if alarm: tone += 0.14 * math.sin(2 * math.pi * freq * 0.5 * t) tone += 0.07 * rng.uniform(-1, 1) * math.exp(-local * 8) value = int(tone * envelope * 7800) samples.extend(struct.pack("