#!/usr/bin/env python3 """Batch-generate real sprites for every actor: flux_local -> bg_remove_local -> trim + downscale (Pillow). Overwrites the placeholder PNGs in sequences/assets. Full-res renders kept in assets_src/renders. Prompts appended to assets_src/PROMPTS.md. Single facing per actor — the Blender 16-facing turntable pipeline is the Phase D upgrade path in MEGAPLAN.md. """ import os, sys, datetime sys.path.insert(0, os.path.dirname(__file__)) from mbgen import run, upload # noqa: E402 from PIL import Image # noqa: E402 ROOT = os.path.join(os.path.dirname(__file__), '..') RENDERS = os.path.join(ROOT, 'assets_src', 'renders') ASSETS = os.path.join(ROOT, 'mods', 'vinylgod', 'sequences', 'assets') os.makedirs(RENDERS, exist_ok=True) MRP = 'rusty junkyard robot aesthetic, rust metal with hot pink trim, analog dials and speaker parts, ' STR = 'glossy seamless white tech aesthetic, soft blue ring lights, smooth rounded minimal design, ' SUF = ', isometric three-quarter view from above, single object centered, video game asset, plain flat grey background, no text' ACTORS = [ ('popupvan', 24, MRP + 'small delivery truck covered in band stickers and a roof-mounted loudspeaker'), ('popupshop', 64, MRP + 'record store shack built from shipping containers with neon OPEN sign and crane arm'), ('pressplant', 64, MRP + 'industrial hydraulic vinyl record pressing plant building with steam vents and conveyor of records'), ('ampstack', 32, MRP + 'wall of stacked guitar amplifiers crackling with pink energy'), ('vault', 32, MRP + 'squat bunker with a huge round bank vault door and stacked record crates'), ('merchtable', 32, MRP + 'folding merch table under a small tent with t-shirts and records'), ('garage', 64, MRP + 'corrugated iron vehicle workshop with engine hoist and band posters'), ('mastering', 64, MRP + 'pristine acoustic-panelled recording studio grafted onto a junk tower with satellite dish'), ('digger', 24, MRP + 'forklift monster robot with crate claw carrying milk crates of vinyl records'), ('spinner', 20, MRP + 'small agile one-wheeled robot whose wheel is a spinning vinyl record with blade tonearms'), ('stacktank', 28, MRP + 'two-legged walker mech made of stacked speaker cabinets with subwoofer chest'), ('basscannon', 26, MRP + 'six-wheeled flatbed truck hauling one colossal subwoofer cannon with fold-out legs'), ('djgrunt', 16, MRP + 'small hooded robot with a portable turntable chest rig holding a razor-sharp record'), ('roadie', 16, MRP + 'four-armed robot with gaffer tape bandoliers and a flight case backpack'), ('streamvan', 24, STR + 'autonomous white delivery van with a glowing blue play-button logo'), ('streamhub', 64, STR + 'white monolithic server hub building with one huge blue status ring'), ('datacentre', 32, STR + 'humming white server monolith with a single blue LED strip'), ('transcoder', 64, STR + 'white cube building that swallows records on a conveyor, receipts coming out'), ('podfarm', 32, STR + 'vending machine wall dispensing small white sphere robots'), ('printfarm', 64, STR + 'white 3d-printer factory building with robotic arms assembling drones'), ('scraper', 22, STR + 'autonomous white street sweeper vehicle inhaling black vinyl through a glowing blue intake'), ('bufferbot', 14, STR + 'knee-high white sphere robot with a loading spinner face'), ('drmwalker', 28, STR + 'tall faceless white mech with one blue ring-light eye and a glowing paywall screen chest'), ] def fit(im, w, h): im = im.crop(im.getbbox() or (0, 0, im.width, im.height)) im.thumbnail((w, h), Image.LANCZOS) out = Image.new('RGBA', (w, h), (0, 0, 0, 0)) out.paste(im, ((w - im.width) // 2, (h - im.height) // 2)) return out log = open(os.path.join(ROOT, 'assets_src', 'PROMPTS.md'), 'a') today = datetime.date.today().isoformat() only = sys.argv[1:] for name, size, prompt in ACTORS: if only and name not in only: continue raw = os.path.join(RENDERS, f'{name}_raw.png') cut = os.path.join(RENDERS, f'{name}.png') if not os.path.exists(cut): run('flux_local', {'prompt': prompt + SUF}, raw) run('bg_remove_local', {}, cut, asset_id=upload(raw)) log.write(f'| renders/{name}.png | {today} | {prompt + SUF} |\n') log.flush() im = Image.open(cut).convert('RGBA') fit(im, size, size).save(os.path.join(ASSETS, f'{name}.png')) fit(im, 56, 40).resize((56, 40)).save(os.path.join(ASSETS, f'_icon_tmp.png')) ic = Image.new('RGBA', (64, 48), (18, 16, 20, 255)) ic.paste(Image.open(os.path.join(ASSETS, '_icon_tmp.png')), (4, 4)) ic.save(os.path.join(ASSETS, f'icon_{name}.png')) print(f'[sprite] {name} done', flush=True) os.remove(os.path.join(ASSETS, '_icon_tmp.png')) print('ALL DONE')