230 lines
8.1 KiB
Lua
230 lines
8.1 KiB
Lua
-- helpers.lua — shared palette + pixel primitives for asset generation
|
|
-- Usage: local H = dofile(ROOT .. "tools/aseprite/helpers.lua")
|
|
-- All draw helpers operate on an Aseprite Image via a "pen" table bound to an origin.
|
|
|
|
local H = {}
|
|
|
|
H.ROOT = "/Users/leidianyayi/Documents/GitHub/ModelTest20260714/Kimi-K3/"
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- Candy palette (CONTRACT §1) + white-layer grays + a few flat utility shades.
|
|
-- Stored as {r,g,b}. No gradients, no AA — every pixel is one of these.
|
|
-- ----------------------------------------------------------------------------
|
|
H.P = {
|
|
-- grass greens
|
|
grass = {125, 201, 94}, -- #7DC95E
|
|
grass_d = { 95, 179, 74}, -- #5FB34A
|
|
grass_dd = { 74, 148, 58},
|
|
grass_l = {158, 216, 128},
|
|
-- cream
|
|
cream = {255, 243, 214}, -- #FFF3D6
|
|
cream_d = {238, 220, 178},
|
|
cream_dd = {216, 196, 150},
|
|
-- honey orange
|
|
honey = {245, 166, 35}, -- #F5A623
|
|
honey_d = {224, 143, 18},
|
|
honey_dd = {196, 120, 10},
|
|
honey_l = {250, 193, 96},
|
|
-- sky blue
|
|
sky = {143, 211, 255}, -- #8FD3FF
|
|
sky_d = {110, 186, 240},
|
|
-- stone road
|
|
stone = {232, 220, 200}, -- #E8DCC8
|
|
stone_d = {206, 190, 162},
|
|
stone_dd = {178, 160, 130},
|
|
stone_l = {245, 237, 224},
|
|
-- lake water
|
|
water = {110, 193, 228}, -- #6EC1E4
|
|
water_d = { 79, 168, 216}, -- #4FA8D8
|
|
water_dd = { 56, 140, 190},
|
|
water_l = {158, 216, 242},
|
|
-- pink
|
|
pink = {247, 183, 203}, -- #F7B7CB
|
|
pink_d = {232, 150, 176},
|
|
pink_dd = {208, 120, 150},
|
|
pink_l = {252, 208, 222},
|
|
-- brown (trunk / wood dark)
|
|
brown = {139, 90, 43}, -- #8B5A2B
|
|
brown_d = {108, 68, 30},
|
|
brown_dd = { 84, 52, 22},
|
|
brown_l = {172, 122, 72},
|
|
-- dirt road
|
|
dirt = {205, 172, 122},
|
|
dirt_d = {182, 148, 98},
|
|
dirt_l = {220, 190, 146},
|
|
-- white-layer grays (CONTRACT §1)
|
|
w_hi = {245, 245, 245}, -- #F5F5F5
|
|
w_mid = {218, 218, 218}, -- #DADADA
|
|
w_lo = {189, 189, 189}, -- #BDBDBD
|
|
w_dk = {150, 150, 150},
|
|
w_dd = {110, 110, 110},
|
|
-- reds
|
|
red = {226, 87, 77},
|
|
red_d = {198, 66, 56},
|
|
red_l = {240, 130, 120},
|
|
-- blues
|
|
blue = { 91, 155, 213},
|
|
blue_d = { 62, 125, 190},
|
|
blue_l = {140, 190, 232},
|
|
-- yellows
|
|
yellow = {247, 214, 92},
|
|
yellow_d = {224, 186, 55},
|
|
yellow_l = {252, 228, 140},
|
|
-- foliage
|
|
leaf = { 86, 168, 78},
|
|
leaf_d = { 64, 138, 60},
|
|
leaf_dd = { 46, 108, 44},
|
|
leaf_l = {120, 192, 110},
|
|
-- wood
|
|
wood = {201, 152, 96},
|
|
wood_d = {176, 126, 74},
|
|
wood_dd = {150, 104, 58},
|
|
wood_l = {220, 178, 124},
|
|
-- stone wall (cool gray)
|
|
swall = {196, 190, 178},
|
|
swall_d = {164, 158, 146},
|
|
swall_dd = {134, 128, 116},
|
|
swall_l = {214, 209, 200},
|
|
-- berry
|
|
berry = {206, 62, 96},
|
|
berry_d = {172, 44, 76},
|
|
-- misc
|
|
skin = {255, 224, 190},
|
|
skin_d = {238, 198, 156},
|
|
hair = {120, 84, 52},
|
|
hair_d = { 96, 66, 40},
|
|
hair_l = {152, 112, 74},
|
|
black = { 54, 44, 40},
|
|
ink = { 40, 34, 30},
|
|
white = {255, 255, 255},
|
|
grey = {150, 150, 150},
|
|
grey_d = {108, 108, 108},
|
|
grey_l = {196, 196, 196},
|
|
screen = { 44, 58, 54},
|
|
screen_l = {120, 220, 160},
|
|
metal = {178, 184, 190},
|
|
metal_d = {140, 146, 152},
|
|
flame = {255, 214, 92},
|
|
flame_d = {245, 140, 40},
|
|
leaf2 = { 60, 150, 70},
|
|
}
|
|
|
|
-- make an Aseprite color from a palette name (or {r,g,b} table) + optional alpha
|
|
function H.col(name, a)
|
|
local t = name
|
|
if type(name) == "string" then t = H.P[name] end
|
|
return app.pixelColor.rgba(t[1], t[2], t[3], a or 255)
|
|
end
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- Pen: a table of draw methods bound to an image + origin (ox, oy).
|
|
-- All coordinates passed to pen methods are LOCAL to the origin.
|
|
-- ----------------------------------------------------------------------------
|
|
function H.at(img, ox, oy)
|
|
local t = { img = img, ox = ox, oy = oy }
|
|
function t.p(x, y, c) img:drawPixel(ox + x, oy + y, c) end
|
|
function t.pn(x, y, name, a) img:drawPixel(ox + x, oy + y, H.col(name, a)) end
|
|
function t.rect(x, y, w, h, c)
|
|
for j = 0, h - 1 do for i = 0, w - 1 do img:drawPixel(ox + x + i, oy + y + j, c) end end
|
|
end
|
|
function t.rectn(x, y, w, h, name, a)
|
|
local c = H.col(name, a)
|
|
for j = 0, h - 1 do for i = 0, w - 1 do img:drawPixel(ox + x + i, oy + y + j, c) end end
|
|
end
|
|
function t.hl(x, y, len, c) for i = 0, len - 1 do img:drawPixel(ox + x + i, oy + y, c) end end
|
|
function t.hln(x, y, len, name, a)
|
|
local c = H.col(name, a); for i = 0, len - 1 do img:drawPixel(ox + x + i, oy + y, c) end
|
|
end
|
|
function t.vl(x, y, len, c) for j = 0, len - 1 do img:drawPixel(ox + x, oy + y + j, c) end end
|
|
function t.vln(x, y, len, name, a)
|
|
local c = H.col(name, a); for j = 0, len - 1 do img:drawPixel(ox + x, oy + y + j, c) end
|
|
end
|
|
-- outline rectangle
|
|
function t.frame(x, y, w, h, c)
|
|
t.hl(x, y, w, c); t.hl(x, y + h - 1, w, c)
|
|
t.vl(x, y, h, c); t.vl(x + w - 1, y, h, c)
|
|
end
|
|
function t.framen(x, y, w, h, name, a)
|
|
t.frame(x, y, w, h, H.col(name, a))
|
|
end
|
|
-- filled disc (filled circle), integer radius
|
|
function t.disc(cx, cy, r, c)
|
|
for yy = -r, r do for xx = -r, r do
|
|
if xx * xx + yy * yy <= r * r then t.p(cx + xx, cy + yy, c) end
|
|
end end
|
|
end
|
|
function t.discn(cx, cy, r, name, a) t.disc(cx, cy, r, H.col(name, a)) end
|
|
-- filled ellipse
|
|
function t.ellipse(cx, cy, rx, ry, c)
|
|
for yy = -ry, ry do for xx = -rx, rx do
|
|
if (xx * xx) * (ry * ry) + (yy * yy) * (rx * rx) <= (rx * rx) * (ry * ry) then
|
|
t.p(cx + xx, cy + yy, c)
|
|
end
|
|
end end
|
|
end
|
|
function t.ellipsen(cx, cy, rx, ry, name, a) t.ellipse(cx, cy, rx, ry, H.col(name, a)) end
|
|
return t
|
|
end
|
|
|
|
-- pen for a 16x16 tile by id (tileset is 16 columns)
|
|
function H.tile(img, id)
|
|
local ox = (id % 16) * 16
|
|
local oy = math.floor(id / 16) * 16
|
|
return H.at(img, ox, oy)
|
|
end
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- Motif helpers (drawn through a pen `t`, local coords)
|
|
-- ----------------------------------------------------------------------------
|
|
|
|
-- a small 5-petal flower centered at (cx,cy)
|
|
function H.flower(t, cx, cy, petal, center)
|
|
local pc = H.col(petal); local cc = H.col(center or "yellow")
|
|
t.p(cx, cy - 1, pc); t.p(cx, cy + 1, pc)
|
|
t.p(cx - 1, cy, pc); t.p(cx + 1, cy, pc)
|
|
t.p(cx, cy, cc)
|
|
end
|
|
|
|
-- a window with brown frame + sky panes, top-left (x,y), size w x h
|
|
function H.window(t, x, y, w, h, glass)
|
|
glass = glass or "sky"
|
|
t.rectn(x, y, w, h, "brown_d")
|
|
t.rectn(x + 1, y + 1, w - 2, h - 2, glass)
|
|
-- mullions
|
|
local mx = x + math.floor(w / 2)
|
|
local my = y + math.floor(h / 2)
|
|
t.vln(mx, y + 1, h - 2, "brown_d")
|
|
t.hln(x + 1, my, w - 2, "brown_d")
|
|
-- glass highlight
|
|
t.pn(x + 2, y + 2, "sky_l" == glass and "white" or "water_l")
|
|
end
|
|
|
|
-- ----------------------------------------------------------------------------
|
|
-- Mirror pen: wraps a pen `t`, flipping x within a `w`-wide frame.
|
|
-- Lets one directional drawing serve both left and right facings.
|
|
-- ----------------------------------------------------------------------------
|
|
function H.mirrorX(t, w)
|
|
local m = {}
|
|
function m.p(x, y, c) t.p(w - 1 - x, y, c) end
|
|
function m.pn(x, y, name, a) t.pn(w - 1 - x, y, name, a) end
|
|
function m.rect(x, y, ww, h, c) t.rect(w - x - ww, y, ww, h, c) end
|
|
function m.rectn(x, y, ww, h, name, a) t.rectn(w - x - ww, y, ww, h, name, a) end
|
|
function m.hl(x, y, len, c) t.hl(w - x - len, y, len, c) end
|
|
function m.hln(x, y, len, name, a) t.hln(w - x - len, y, len, name, a) end
|
|
function m.vl(x, y, len, c) t.vl(w - 1 - x, y, len, c) end
|
|
function m.vln(x, y, len, name, a) t.vln(w - 1 - x, y, len, name, a) end
|
|
function m.framen(x, y, ww, h, name, a) t.framen(w - x - ww, y, ww, h, name, a) end
|
|
function m.discn(cx, cy, r, name, a) t.discn(w - 1 - cx, cy, r, name, a) end
|
|
function m.ellipsen(cx, cy, rx, ry, name, a) t.ellipsen(w - 1 - cx, cy, rx, ry, name, a) end
|
|
return m
|
|
end
|
|
|
|
-- create a fresh RGB sprite + return (sprite, image)
|
|
function H.new(w, h)
|
|
local spr = Sprite(w, h, ColorMode.RGB)
|
|
local img = app.activeImage
|
|
return spr, img
|
|
end
|
|
|
|
return H
|