Files
urm03/tools/aseprite/gen_sprites.lua
T
vh 651193674c feat(tools): Aseprite sprite pipeline — shared pixel maps, Lua generator, headless previewer
tools/aseprite/maps/ holds the source of truth: char-grid pixel maps +
shared palette for five _ph placeholder sprites (M03 32px per MOE.md
design law cues — round silhouette, mismatched eyes, bolted hardpoint,
pillar feet; grunt 16px untagged olive; burster 20px HEAT glow band;
KINETIC slug; scrap diamond). Two interchangeable renderers:
gen_sprites.lua (aseprite -b, emits .aseprite + .png — run on any
machine with Aseprite) and preview.py (Pillow, identical PNGs headless
+ nearest-neighbor zoom for review). Outputs gitignored; greybox
scenes untouched — art stays out of the game per the greybox rule.
2026-08-14 00:04:45 -07:00

74 lines
2.3 KiB
Lua

-- Headless Aseprite sprite generator for U R M03 placeholder sprites.
--
-- Run from the repo root:
-- aseprite -b --script tools/aseprite/gen_sprites.lua
-- Optional params:
-- aseprite -b --script-param out=assets/sprites_ph \
-- --script-param maps=tools/aseprite/maps \
-- --script tools/aseprite/gen_sprites.lua
--
-- Source of truth is the text pixel maps in tools/aseprite/maps/
-- (shared with tools/aseprite/preview.py, which renders identical
-- PNGs without Aseprite). Outputs .aseprite + .png per map.
-- All output is _ph placeholder-grade per the asset policy.
local params = app.params or {}
local MAPS_DIR = params["maps"] or "tools/aseprite/maps"
local OUT_DIR = params["out"] or "assets/sprites_ph"
local SPRITES = { "m03_ph", "grunt_ph", "burster_ph", "slug_ph", "scrap_ph" }
local function parse_palette(path)
local pal = {}
for line in io.lines(path) do
local ch, hex = line:match("^(%S)%s+(%x%x%x%x%x%x)%s*$")
if ch and ch ~= "#" then
local v = tonumber(hex, 16)
pal[ch] = {
r = math.floor(v / 65536) % 256,
g = math.floor(v / 256) % 256,
b = v % 256,
}
end
end
return pal
end
local function parse_map(path)
local rows = {}
for line in io.lines(path) do
if #line > 0 then
rows[#rows + 1] = line
assert(#line == #rows[1],
path .. ": row " .. #rows .. " width " .. #line .. " != " .. #rows[1])
end
end
return rows
end
if app.fs and app.fs.makeAllDirectories then
app.fs.makeAllDirectories(OUT_DIR)
end
local pal = parse_palette(MAPS_DIR .. "/palette.txt")
for _, name in ipairs(SPRITES) do
local rows = parse_map(MAPS_DIR .. "/" .. name .. ".txt")
local w, h = #rows[1], #rows
local spr = Sprite(w, h, ColorMode.RGB)
local img = spr.cels[1].image
for y, row in ipairs(rows) do
for x = 1, w do
local ch = row:sub(x, x)
if ch ~= "." then
local c = assert(pal[ch], name .. ": unknown palette char '" .. ch .. "'")
img:putPixel(x - 1, y - 1, app.pixelColor.rgba(c.r, c.g, c.b, 255))
end
end
end
spr:saveAs(OUT_DIR .. "/" .. name .. ".aseprite")
spr:saveCopyAs(OUT_DIR .. "/" .. name .. ".png")
spr:close()
print("wrote " .. OUT_DIR .. "/" .. name .. ".aseprite (+.png) " .. w .. "x" .. h)
end