216 lines
7.2 KiB
Lua
216 lines
7.2 KiB
Lua
-- Aseprite Lua: player + NPCs + portraits + extras
|
|
-- Run: aseprite -b -script scripts/generate_sprites.lua
|
|
|
|
local function newSprite(w, h)
|
|
local s = Sprite(w, h, ColorMode.RGB)
|
|
app.activeSprite = s
|
|
return s
|
|
end
|
|
|
|
local function clear(img, w, h)
|
|
for y = 0, h - 1 do
|
|
for x = 0, w - 1 do
|
|
img:drawPixel(x, y, app.pixelColor.rgba(0, 0, 0, 0))
|
|
end
|
|
end
|
|
end
|
|
|
|
local function P(img, x, y, r, g, b, a)
|
|
a = a or 255
|
|
if x < 0 or y < 0 then return end
|
|
img:drawPixel(x, y, app.pixelColor.rgba(r, g, b, a))
|
|
end
|
|
|
|
local function fillRect(img, x0, y0, x1, y1, r, g, b)
|
|
for y = y0, y1 do
|
|
for x = x0, x1 do
|
|
P(img, x, y, r, g, b)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Draw a 16x24 character into img at offset (ox, oy)
|
|
-- face: 0 smile, 1 flat, 2 off (eye lower 1px)
|
|
local function drawChar(img, ox, oy, bodyR, bodyG, bodyB, headR, headG, headB, face, walkFrame, dir)
|
|
-- shadow
|
|
fillRect(img, ox + 3, oy + 22, ox + 12, oy + 23, 0, 0, 0)
|
|
for x = ox + 3, ox + 12 do
|
|
P(img, x, oy + 22, 0, 0, 0, 50)
|
|
P(img, x, oy + 23, 0, 0, 0, 30)
|
|
end
|
|
-- legs
|
|
local legOff = 0
|
|
if walkFrame == 1 then legOff = 1 end
|
|
if walkFrame == 3 then legOff = -1 end
|
|
fillRect(img, ox + 4, oy + 16, ox + 6, oy + 21, 60, 50, 80)
|
|
fillRect(img, ox + 9, oy + 16, ox + 11, oy + 21, 60, 50, 80)
|
|
if walkFrame == 1 then
|
|
fillRect(img, ox + 4, oy + 16, ox + 6, oy + 21, 0, 0, 0)
|
|
fillRect(img, ox + 3, oy + 16, ox + 5, oy + 21, 60, 50, 80)
|
|
elseif walkFrame == 3 then
|
|
fillRect(img, ox + 9, oy + 16, ox + 11, oy + 21, 0, 0, 0)
|
|
fillRect(img, ox + 10, oy + 16, ox + 12, oy + 21, 60, 50, 80)
|
|
end
|
|
-- body
|
|
fillRect(img, ox + 3, oy + 10, ox + 12, oy + 17, bodyR, bodyG, bodyB)
|
|
-- head
|
|
fillRect(img, ox + 3, oy + 2, ox + 12, oy + 10, headR, headG, headB)
|
|
-- ears/species hint for animals - small top nubs
|
|
if headR > 200 and headG > 180 then
|
|
-- rabbit ears
|
|
fillRect(img, ox + 4, oy + 0, ox + 5, oy + 3, headR, headG, headB)
|
|
fillRect(img, ox + 10, oy + 0, ox + 11, oy + 3, headR, headG, headB)
|
|
end
|
|
-- face
|
|
local eyeY = 5
|
|
local eyeY2 = 5
|
|
if face == 2 then eyeY2 = 6 end -- off: one eye 1px lower
|
|
if dir == 0 then -- down
|
|
P(img, ox + 5, oy + eyeY, 30, 30, 40)
|
|
P(img, ox + 10, oy + eyeY2, 30, 30, 40)
|
|
if face == 0 then
|
|
P(img, ox + 6, oy + 8, 200, 80, 100)
|
|
P(img, ox + 7, oy + 8, 200, 80, 100)
|
|
P(img, ox + 8, oy + 8, 200, 80, 100)
|
|
P(img, ox + 9, oy + 8, 200, 80, 100)
|
|
elseif face == 1 then
|
|
P(img, ox + 6, oy + 8, 80, 60, 70)
|
|
P(img, ox + 7, oy + 8, 80, 60, 70)
|
|
P(img, ox + 8, oy + 8, 80, 60, 70)
|
|
P(img, ox + 9, oy + 8, 80, 60, 70)
|
|
else
|
|
P(img, ox + 6, oy + 8, 200, 80, 100)
|
|
P(img, ox + 7, oy + 8, 200, 80, 100)
|
|
P(img, ox + 8, oy + 8, 200, 80, 100)
|
|
P(img, ox + 9, oy + 9, 200, 80, 100) -- mouth 1px off
|
|
end
|
|
elseif dir == 1 then -- up: no face
|
|
fillRect(img, ox + 3, oy + 2, ox + 12, oy + 6, headR - 10, headG - 10, headB - 10)
|
|
elseif dir == 2 then -- left
|
|
P(img, ox + 5, oy + eyeY, 30, 30, 40)
|
|
if face == 0 then P(img, ox + 4, oy + 8, 200, 80, 100) end
|
|
else -- right
|
|
P(img, ox + 10, oy + eyeY2, 30, 30, 40)
|
|
if face == 0 then P(img, ox + 11, oy + 8, 200, 80, 100) end
|
|
end
|
|
end
|
|
|
|
-- Player sheet: 4 dirs x 4 frames = 16 frames, each 16x24. Layout 4 cols x 4 rows
|
|
local pw, ph = 16 * 4, 24 * 4
|
|
local ps = newSprite(pw, ph)
|
|
local pimg = ps.cels[1].image
|
|
clear(pimg, pw, ph)
|
|
for dir = 0, 3 do
|
|
for frame = 0, 3 do
|
|
drawChar(pimg, frame * 16, dir * 24, 100, 170, 220, 250, 220, 190, 0, frame, dir)
|
|
end
|
|
end
|
|
ps:saveAs("public/assets/sprites/player.aseprite")
|
|
ps:saveCopyAs("public/assets/sprites/player.png")
|
|
|
|
-- Player portraits: smile + real face, 32x32 each side by side
|
|
local port = newSprite(64, 32)
|
|
local po = port.cels[1].image
|
|
clear(po, 64, 32)
|
|
-- smile portrait
|
|
fillRect(po, 4, 4, 27, 28, 250, 220, 190)
|
|
P(po, 11, 14, 30, 30, 40)
|
|
P(po, 20, 14, 30, 30, 40)
|
|
fillRect(po, 12, 20, 19, 21, 200, 80, 100)
|
|
-- real tired face
|
|
fillRect(po, 36, 4, 59, 28, 240, 210, 185)
|
|
P(po, 43, 14, 30, 30, 40)
|
|
P(po, 52, 15, 30, 30, 40)
|
|
fillRect(po, 44, 21, 51, 21, 100, 80, 90)
|
|
P(po, 42, 18, 200, 160, 160)
|
|
P(po, 53, 18, 200, 160, 160)
|
|
port:saveAs("public/assets/sprites/portraits.aseprite")
|
|
port:saveCopyAs("public/assets/sprites/portraits.png")
|
|
|
|
-- Named NPCs: 7 npcs x 3 faces, each 16x24. Grid: 3 faces per row, 7 rows
|
|
local npcs = {
|
|
{ name = "afu", br = 200, bg = 160, bb = 100, hr = 180, hg = 130, hb = 70 }, -- dog
|
|
{ name = "yuanyuan", br = 255, bg = 200, bb = 210, hr = 255, hg = 230, hb = 230 }, -- rabbit
|
|
{ name = "dabao", br = 160, bg = 110, bb = 70, hr = 180, hg = 130, hb = 80 }, -- bear
|
|
{ name = "mimi", br = 255, bg = 180, bb = 100, hr = 255, hg = 160, hb = 80 }, -- cat
|
|
{ name = "fu", br = 200, bg = 220, bb = 160, hr = 230, hg = 210, hb = 140 }, -- deer
|
|
{ name = "twinbirds", br = 100, bg = 180, bb = 220, hr = 255, hg = 220, hb = 80 },
|
|
{ name = "gui", br = 100, bg = 150, bb = 90, hr = 120, hg = 160, hb = 100 }, -- turtle
|
|
}
|
|
local ns = newSprite(16 * 3, 24 * 7)
|
|
local ni = ns.cels[1].image
|
|
clear(ni, 16 * 3, 24 * 7)
|
|
for i, n in ipairs(npcs) do
|
|
for face = 0, 2 do
|
|
drawChar(ni, face * 16, (i - 1) * 24, n.br, n.bg, n.bb, n.hr, n.hg, n.hb, face, 0, 0)
|
|
end
|
|
end
|
|
ns:saveAs("public/assets/sprites/npcs.aseprite")
|
|
ns:saveCopyAs("public/assets/sprites/npcs.png")
|
|
|
|
-- Twin birds beak anim 2 frames 16x16
|
|
local tb = newSprite(32, 16)
|
|
local ti = tb.cels[1].image
|
|
clear(ti, 32, 16)
|
|
-- frame 0 closed
|
|
fillRect(ti, 2, 4, 13, 12, 100, 180, 220)
|
|
P(ti, 5, 7, 30, 30, 40)
|
|
fillRect(ti, 10, 8, 12, 9, 255, 100, 80)
|
|
-- frame 1 open
|
|
fillRect(ti, 18, 4, 29, 12, 100, 180, 220)
|
|
P(ti, 21, 7, 30, 30, 40)
|
|
fillRect(ti, 26, 7, 29, 10, 255, 100, 80)
|
|
tb:saveAs("public/assets/sprites/birds.aseprite")
|
|
tb:saveCopyAs("public/assets/sprites/birds.png")
|
|
|
|
-- Extras x5, 2 frames each, 16x24
|
|
local ex = newSprite(16 * 2, 24 * 5)
|
|
local ei = ex.cels[1].image
|
|
clear(ei, 32, 120)
|
|
local colors = {
|
|
{ 200, 140, 80 },
|
|
{ 160, 100, 60 },
|
|
{ 220, 160, 100 },
|
|
{ 180, 120, 70 },
|
|
{ 140, 100, 50 },
|
|
}
|
|
for i = 1, 5 do
|
|
local c = colors[i]
|
|
for f = 0, 1 do
|
|
drawChar(ei, f * 16, (i - 1) * 24, c[1], c[2], c[3], c[1] + 20, c[2] + 20, c[3] + 10, 0, f * 2, 0)
|
|
end
|
|
end
|
|
ex:saveAs("public/assets/sprites/extras.aseprite")
|
|
ex:saveCopyAs("public/assets/sprites/extras.png")
|
|
|
|
-- Sunflower HUD petals sprite sheet 16x16 x 14 (12 petals + center + seeds)
|
|
local sf = newSprite(16 * 14, 16)
|
|
local si = sf.cels[1].image
|
|
clear(si, 16 * 14, 16)
|
|
for i = 0, 11 do
|
|
local ox = i * 16
|
|
fillRect(si, ox + 4, 2, ox + 11, 13, 255, 210, 50)
|
|
fillRect(si, ox + 6, 0, ox + 9, 15, 255, 200, 40)
|
|
end
|
|
-- center
|
|
fillRect(si, 12 * 16 + 4, 4, 12 * 16 + 11, 11, 180, 120, 40)
|
|
-- black seeds center
|
|
fillRect(si, 13 * 16 + 4, 4, 13 * 16 + 11, 11, 40, 30, 20)
|
|
for k = 0, 5 do
|
|
P(si, 13 * 16 + 5 + (k % 3) * 2, 5 + math.floor(k / 3) * 3, 20, 15, 10)
|
|
end
|
|
sf:saveAs("public/assets/sprites/sunflower.aseprite")
|
|
sf:saveCopyAs("public/assets/sprites/sunflower.png")
|
|
|
|
-- UI dialog panel 320x48
|
|
local ui = newSprite(320, 48)
|
|
local uiImg = ui.cels[1].image
|
|
clear(uiImg, 320, 48)
|
|
fillRect(uiImg, 0, 0, 319, 47, 40, 30, 25)
|
|
fillRect(uiImg, 2, 2, 317, 45, 250, 240, 220)
|
|
fillRect(uiImg, 4, 4, 315, 43, 255, 250, 235)
|
|
ui:saveAs("public/assets/sprites/dialog_panel.aseprite")
|
|
ui:saveCopyAs("public/assets/sprites/dialog_panel.png")
|
|
|
|
print("sprites generated")
|