-- 《崩坏童话：蜜糖村》全部像素素材生成脚本（Aseprite Lua）
-- 运行：aseprite -b --script tools/gen_art.lua
-- 输出：public/assets/*.png + art/*.aseprite（源文件）
-- 全部素材为本脚本逐像素绘制的原创内容，无任何外部素材。

local pc = app.pixelColor
local function C(hex, a)
  a = a or 255
  local r = tonumber(hex:sub(1, 2), 16)
  local g = tonumber(hex:sub(3, 4), 16)
  local b = tonumber(hex:sub(5, 6), 16)
  return pc.rgba(r, g, b, a)
end

-- ============ 糖果色板（村庄）+ 灰白色板（白色层） ============
local PAL = {
  outline = C("5a4232"),
  black   = C("2a2420"),
  white   = C("ffffff"),
  -- 草地/植物
  grass   = C("8fd05a"), grassD = C("79b94a"), grassL = C("a5e070"),
  leaf    = C("5da53f"), leafD  = C("4a8a32"), trunk  = C("8a5a34"), trunkD = C("6e4526"),
  -- 路面/石
  path    = C("e8d5a8"), pathD  = C("cbb488"), stone  = C("c8c0a8"), stoneD = C("a8a088"),
  -- 水
  water   = C("7ec8e8"), waterD = C("5fb0d8"), waterL = C("b8e6f8"),
  -- 花
  pink    = C("ff9ecb"), red    = C("e05858"), yellow = C("ffd94a"), berry = C("e8497a"),
  -- 建筑
  wall    = C("fff1d0"), wallD  = C("e8d8b0"), roof   = C("f4924a"), roofD = C("d97733"),
  wood    = C("c98a4b"), woodD  = C("a86a34"), woodL  = C("e0aa6a"),
  -- 室内
  floor   = C("e6c896"), floorD = C("d4b47e"), clinic = C("eaf2ec"), clinicD = C("d0e0d6"),
  inwall  = C("f6e8c8"), inwallD = C("e0cca0"),
  -- 灰白层
  wgray   = C("e8e8ea"), wgrayD = C("cfcfd4"), wgrayDD = C("aeaeb6"), wdark = C("8a8a92"),
  -- 其他
  fog     = C("e8ecf0"), fogD   = C("cdd6de"),
  metal   = C("9aa4ac"), blue   = C("5a88c8"), sky = C("8ecae6"),
}

-- ============ 绘制助手 ============
local function makeCtx(img, ox, oy)
  local ctx = {}
  function ctx.px(x, y, c) img:putPixel(ox + x, oy + y, c) end
  function ctx.rect(x, y, w, h, c)
    for j = y, y + h - 1 do for i = x, x + w - 1 do img:putPixel(ox + i, oy + j, c) end end
  end
  function ctx.hline(x, y, w, c) ctx.rect(x, y, w, 1, c) end
  function ctx.vline(x, y, h, c) ctx.rect(x, y, 1, h, c) end
  function ctx.dither(x, y, w, h, c, step)
    step = step or 2
    for j = y, y + h - 1 do for i = x, x + w - 1 do
      if (i + j) % step == 0 then img:putPixel(ox + i, oy + j, c) end
    end end
  end
  return ctx
end

local function saveSheet(img, name)
  local spr = Sprite(img.width, img.height, ColorMode.RGB)
  spr.cels[1].image = img
  spr:saveAs("art/" .. name .. ".aseprite")
  spr:saveCopyAs("public/assets/" .. name .. ".png")
  spr:close()
end

-- ============ 瓦片集：10 列 × 8 行 = 80 瓦（16×16） ============
local T = {}

T[0] = function(c) -- 草地1
  c.rect(0, 0, 16, 16, PAL.grass)
  c.px(3, 4, PAL.grassL); c.px(11, 9, PAL.grassL); c.px(6, 13, PAL.grassD); c.px(13, 2, PAL.grassD)
end
T[1] = function(c) -- 草地2（草茎）
  c.rect(0, 0, 16, 16, PAL.grass)
  c.px(4, 5, PAL.grassD); c.px(4, 4, PAL.grassD); c.px(10, 10, PAL.grassD); c.px(10, 9, PAL.grassD)
  c.px(13, 5, PAL.grassL); c.px(2, 12, PAL.grassL)
end
T[2] = function(c) -- 草地+白花
  T[0](c); c.px(5, 6, PAL.white); c.px(4, 5, PAL.white); c.px(6, 5, PAL.white); c.px(5, 4, PAL.white); c.px(5, 5, PAL.yellow)
  c.px(12, 12, PAL.white)
end
T[3] = function(c) -- 草地+粉花
  T[1](c); c.px(11, 4, PAL.pink); c.px(10, 3, PAL.pink); c.px(12, 3, PAL.pink); c.px(11, 2, PAL.pink); c.px(11, 3, PAL.yellow)
  c.px(3, 10, PAL.pink)
end
T[4] = function(c) -- 石板路
  c.rect(0, 0, 16, 16, PAL.path)
  c.hline(0, 7, 16, PAL.pathD); c.vline(7, 0, 8, PAL.pathD); c.vline(3, 8, 8, PAL.pathD); c.vline(12, 8, 8, PAL.pathD)
  c.px(2, 3, PAL.pathD); c.px(10, 12, PAL.pathD)
end
T[5] = function(c) -- 石板路2
  c.rect(0, 0, 16, 16, PAL.path)
  c.hline(0, 3, 16, PAL.pathD); c.hline(0, 11, 16, PAL.pathD); c.vline(5, 4, 7, PAL.pathD); c.vline(11, 12, 4, PAL.pathD); c.vline(11, 0, 3, PAL.pathD)
end
T[6] = function(c) -- 路缘（草→路）
  c.rect(0, 0, 16, 8, PAL.grass); c.rect(0, 8, 16, 8, PAL.path)
  c.hline(0, 7, 16, PAL.grassD); c.hline(0, 8, 16, PAL.pathD)
  c.px(3, 6, PAL.grassL); c.px(11, 12, PAL.pathD)
end
T[7] = function(c) -- 湖水1
  c.rect(0, 0, 16, 16, PAL.water)
  c.hline(2, 4, 4, PAL.waterL); c.hline(9, 10, 4, PAL.waterL); c.hline(5, 13, 3, PAL.waterD)
end
T[8] = function(c) -- 湖水2
  c.rect(0, 0, 16, 16, PAL.water)
  c.hline(8, 3, 4, PAL.waterD); c.hline(1, 9, 4, PAL.waterL); c.hline(11, 14, 3, PAL.waterL)
end
T[9] = function(c) -- 岸边（上草下水）
  c.rect(0, 0, 16, 6, PAL.grass); c.rect(0, 6, 16, 10, PAL.water)
  c.hline(0, 5, 16, PAL.grassD); c.hline(0, 6, 16, PAL.waterL)
end
T[10] = function(c) -- 花丛
  c.rect(0, 0, 16, 16, PAL.grass)
  c.rect(1, 2, 14, 12, PAL.leaf)
  c.px(1, 2, PAL.grass); c.px(14, 2, PAL.grass); c.px(1, 13, PAL.grass); c.px(14, 13, PAL.grass)
  c.px(4, 5, PAL.pink); c.px(8, 4, PAL.yellow); c.px(12, 6, PAL.white); c.px(3, 9, PAL.yellow)
  c.px(7, 8, PAL.pink); c.px(11, 10, PAL.pink); c.px(5, 12, PAL.white); c.px(10, 12, PAL.yellow)
end
T[11] = function(c) -- 莓果丛（有果）
  c.rect(0, 0, 16, 16, PAL.grass)
  c.rect(2, 3, 12, 11, PAL.leaf); c.rect(3, 2, 10, 13, PAL.leaf)
  c.dither(3, 3, 10, 10, PAL.leafD, 3)
  c.px(5, 5, PAL.berry); c.px(10, 4, PAL.berry); c.px(7, 8, PAL.berry); c.px(11, 9, PAL.berry); c.px(4, 10, PAL.berry)
end
T[12] = function(c) -- 莓果丛（已采）
  c.rect(0, 0, 16, 16, PAL.grass)
  c.rect(2, 3, 12, 11, PAL.leaf); c.rect(3, 2, 10, 13, PAL.leaf)
  c.dither(3, 3, 10, 10, PAL.leafD, 3)
end
T[13] = function(c) -- 树·左上
  c.rect(0, 0, 16, 16, PAL.grass)
  c.rect(4, 2, 12, 14, PAL.leaf)
  c.px(4, 2, PAL.grass); c.px(5, 2, PAL.grass); c.px(4, 3, PAL.grass)
  c.dither(5, 4, 11, 12, PAL.leafD, 4); c.px(8, 5, PAL.grassL); c.px(12, 8, PAL.grassL)
end
T[14] = function(c) -- 树·右上
  c.rect(0, 0, 16, 16, PAL.grass)
  c.rect(0, 2, 12, 14, PAL.leaf)
  c.px(11, 2, PAL.grass); c.px(10, 2, PAL.grass); c.px(11, 3, PAL.grass)
  c.dither(0, 4, 11, 12, PAL.leafD, 4); c.px(4, 6, PAL.grassL)
end
T[15] = function(c) -- 树·左下
  c.rect(0, 0, 16, 16, PAL.grass)
  c.rect(4, 0, 12, 6, PAL.leaf); c.dither(4, 0, 12, 6, PAL.leafD, 4)
  c.px(4, 5, PAL.grass)
  c.rect(10, 6, 6, 10, PAL.trunk); c.vline(10, 6, 10, PAL.trunkD)
end
T[16] = function(c) -- 树·右下
  c.rect(0, 0, 16, 16, PAL.grass)
  c.rect(0, 0, 12, 6, PAL.leaf); c.dither(0, 0, 12, 6, PAL.leafD, 4)
  c.px(11, 5, PAL.grass)
  c.rect(0, 6, 6, 10, PAL.trunk); c.vline(5, 6, 10, PAL.trunkD)
end
T[17] = function(c) -- 小灌木
  c.rect(0, 0, 16, 16, PAL.grass)
  c.rect(3, 5, 10, 9, PAL.leaf); c.rect(4, 4, 8, 11, PAL.leaf)
  c.dither(4, 5, 8, 8, PAL.leafD, 3)
end
T[18] = function(c) -- 房屋外墙
  c.rect(0, 0, 16, 16, PAL.wall)
  c.hline(0, 15, 16, PAL.wallD); c.vline(0, 0, 16, PAL.wallD)
  c.px(4, 4, PAL.wallD); c.px(11, 9, PAL.wallD)
end
T[19] = function(c) -- 外墙+窗
  T[18](c)
  c.rect(4, 4, 8, 8, PAL.woodD); c.rect(5, 5, 6, 6, PAL.sky)
  c.vline(7, 5, 6, PAL.woodD); c.hline(5, 8, 6, PAL.woodD)
  c.px(6, 6, PAL.waterL)
end
T[20] = function(c) -- 门
  c.rect(0, 0, 16, 16, PAL.wall)
  c.rect(3, 1, 10, 15, PAL.wood); c.rect(4, 2, 8, 14, PAL.woodL)
  c.vline(4, 2, 14, PAL.wood); c.px(10, 9, PAL.woodD); c.hline(4, 2, 8, PAL.wood)
end
T[21] = function(c) -- 屋顶·左
  c.rect(0, 0, 16, 16, PAL.roof)
  c.hline(0, 0, 16, PAL.roofD); c.hline(0, 5, 16, PAL.roofD); c.hline(0, 10, 16, PAL.roofD); c.hline(0, 15, 16, PAL.roofD)
  c.vline(0, 0, 16, PAL.roofD)
end
T[22] = function(c) -- 屋顶·右
  c.rect(0, 0, 16, 16, PAL.roof)
  c.hline(0, 0, 16, PAL.roofD); c.hline(0, 5, 16, PAL.roofD); c.hline(0, 10, 16, PAL.roofD); c.hline(0, 15, 16, PAL.roofD)
  c.vline(15, 0, 16, PAL.roofD)
end
T[23] = function(c) -- 屋檐
  c.rect(0, 0, 16, 8, PAL.roof); c.hline(0, 7, 16, PAL.roofD); c.hline(0, 6, 16, PAL.roofD)
  c.rect(0, 8, 16, 8, PAL.wall); c.hline(0, 8, 16, PAL.wallD)
end
T[24] = function(c) -- 木地板
  c.rect(0, 0, 16, 16, PAL.floor)
  c.hline(0, 4, 16, PAL.floorD); c.hline(0, 9, 16, PAL.floorD); c.hline(0, 14, 16, PAL.floorD)
  c.px(5, 2, PAL.floorD); c.px(12, 7, PAL.floorD); c.px(3, 12, PAL.floorD)
end
T[25] = function(c) -- 木地板2
  c.rect(0, 0, 16, 16, PAL.floor)
  c.hline(0, 2, 16, PAL.floorD); c.hline(0, 7, 16, PAL.floorD); c.hline(0, 12, 16, PAL.floorD)
  c.px(9, 4, PAL.floorD); c.px(2, 9, PAL.floorD)
end
T[26] = function(c) -- 诊所地砖
  c.rect(0, 0, 16, 16, PAL.clinic)
  c.hline(0, 7, 16, PAL.clinicD); c.vline(7, 0, 16, PAL.clinicD); c.hline(0, 15, 16, PAL.clinicD); c.vline(15, 0, 16, PAL.clinicD)
end
T[27] = function(c) -- 室内墙
  c.rect(0, 0, 16, 16, PAL.inwall)
  c.dither(0, 0, 16, 16, PAL.inwallD, 8)
end
T[28] = function(c) -- 室内墙裙
  c.rect(0, 0, 16, 10, PAL.inwall)
  c.rect(0, 10, 16, 6, PAL.wood); c.hline(0, 10, 16, PAL.woodD)
end
T[29] = function(c) -- 地毯
  c.rect(0, 0, 16, 16, PAL.pink)
  c.rect(1, 1, 14, 14, C("ffc4de")); c.rect(3, 3, 10, 10, PAL.pink)
  c.px(7, 7, PAL.white); c.px(8, 8, PAL.white)
end
T[30] = function(c) -- 床头（枕头）
  c.rect(0, 0, 16, 16, PAL.wood)
  c.rect(1, 1, 14, 15, C("f8f4e8"))
  c.rect(3, 3, 10, 7, PAL.white); c.hline(3, 9, 10, C("d8d4c8"))
  c.hline(1, 0, 14, PAL.woodD)
end
T[31] = function(c) -- 床尾（被子）
  c.rect(0, 0, 16, 16, PAL.wood)
  c.rect(1, 0, 14, 15, C("8ec8e0"))
  c.hline(1, 3, 14, C("6fb0d0")); c.hline(1, 8, 14, C("6fb0d0")); c.hline(1, 13, 14, C("6fb0d0"))
  c.hline(1, 15, 14, PAL.woodD)
end
T[32] = function(c) -- 柜台
  c.rect(0, 0, 16, 16, PAL.wood)
  c.rect(0, 0, 16, 6, PAL.woodL); c.hline(0, 6, 16, PAL.woodD)
  c.vline(0, 0, 16, PAL.woodD); c.vline(15, 0, 16, PAL.woodD); c.hline(0, 15, 16, PAL.woodD)
end
T[33] = function(c) -- 烤炉
  c.rect(0, 0, 16, 16, PAL.stoneD)
  c.rect(1, 1, 14, 14, PAL.stone)
  c.rect(3, 6, 10, 7, PAL.black)
  c.rect(4, 8, 8, 4, C("f4924a")); c.px(6, 9, PAL.yellow); c.px(9, 10, PAL.yellow)
  c.hline(3, 3, 10, PAL.stoneD)
end
T[34] = function(c) -- 货架（空）
  c.rect(0, 0, 16, 16, PAL.wood)
  c.rect(1, 1, 14, 14, PAL.woodL)
  c.hline(1, 5, 14, PAL.woodD); c.hline(1, 10, 14, PAL.woodD); c.hline(1, 14, 14, PAL.woodD)
  c.vline(0, 0, 16, PAL.woodD); c.vline(15, 0, 16, PAL.woodD)
end
T[35] = function(c) -- 货架（同一商品重复排列，标签空白）
  T[34](c)
  for _, y in ipairs({ 2, 7, 11 }) do
    c.rect(2, y, 3, 3, PAL.berry); c.rect(6, y, 3, 3, PAL.berry); c.rect(10, y, 3, 3, PAL.berry)
    c.px(3, y, PAL.white); c.px(7, y, PAL.white); c.px(11, y, PAL.white)
  end
end
T[36] = function(c) -- 桌子
  c.rect(0, 0, 16, 16, PAL.floor)
  c.rect(2, 3, 12, 9, PAL.woodL); c.hline(2, 11, 12, PAL.woodD)
  c.vline(3, 12, 3, PAL.woodD); c.vline(12, 12, 3, PAL.woodD)
  c.hline(2, 3, 12, PAL.wood)
end
T[37] = function(c) -- 椅子
  c.rect(0, 0, 16, 16, PAL.floor)
  c.rect(4, 3, 8, 3, PAL.wood)
  c.rect(4, 6, 8, 6, PAL.woodL)
  c.vline(4, 12, 3, PAL.woodD); c.vline(11, 12, 3, PAL.woodD)
end
T[38] = function(c) -- 井·顶（小屋顶）
  c.rect(0, 0, 16, 16, PAL.grass)
  c.rect(2, 2, 12, 6, PAL.roofD); c.rect(3, 1, 10, 2, PAL.roof)
  c.vline(3, 8, 8, PAL.trunkD); c.vline(12, 8, 8, PAL.trunkD)
end
T[39] = function(c) -- 井·底（石圈）
  c.rect(0, 0, 16, 16, PAL.grass)
  c.rect(2, 2, 12, 12, PAL.stone)
  c.rect(4, 4, 8, 8, PAL.black)
  c.px(3, 3, PAL.stoneD); c.px(12, 3, PAL.stoneD); c.px(3, 12, PAL.stoneD); c.px(12, 12, PAL.stoneD)
  c.hline(2, 2, 12, PAL.stoneD)
end
T[40] = function(c) -- 雾（轻）
  c.rect(0, 0, 16, 16, PAL.fog)
  c.dither(0, 0, 16, 16, PAL.fogD, 3)
end
T[41] = function(c) -- 雾（浓）
  c.rect(0, 0, 16, 16, PAL.fogD)
  c.dither(0, 0, 16, 16, PAL.fog, 2); c.dither(1, 0, 16, 16, C("bcc8d2"), 5)
end
T[42] = function(c) -- 白色层·墙
  c.rect(0, 0, 16, 16, PAL.wgray)
  c.hline(0, 12, 16, PAL.wgrayD); c.rect(0, 13, 16, 3, PAL.wgrayDD)
end
T[43] = function(c) -- 白色层·地
  c.rect(0, 0, 16, 16, PAL.wgrayD)
  c.hline(0, 7, 16, PAL.wgrayDD); c.vline(7, 0, 16, PAL.wgrayDD)
end
T[44] = function(c) -- 白色层·门
  c.rect(0, 0, 16, 16, PAL.wgray)
  c.rect(3, 1, 10, 15, PAL.wgrayDD); c.rect(4, 2, 8, 14, PAL.wgrayD)
  c.px(10, 9, PAL.wdark)
end
T[45] = function(c) -- 长椅·左
  c.rect(0, 0, 16, 16, PAL.grass)
  c.rect(2, 4, 14, 3, PAL.woodL); c.rect(2, 7, 14, 4, PAL.wood)
  c.vline(3, 11, 3, PAL.woodD); c.hline(2, 7, 14, PAL.woodD)
end
T[46] = function(c) -- 长椅·右
  c.rect(0, 0, 16, 16, PAL.grass)
  c.rect(0, 4, 14, 3, PAL.woodL); c.rect(0, 7, 14, 4, PAL.wood)
  c.vline(12, 11, 3, PAL.woodD); c.hline(0, 7, 14, PAL.woodD)
end
T[47] = function(c) -- 公告板·左
  c.rect(0, 0, 16, 16, PAL.grass)
  c.rect(2, 1, 14, 12, PAL.wood); c.rect(4, 3, 12, 8, C("f8f0d8"))
  c.hline(5, 5, 8, PAL.stoneD); c.hline(5, 7, 6, PAL.stoneD); c.hline(5, 9, 9, PAL.stoneD)
  c.vline(4, 13, 3, PAL.trunkD)
end
T[48] = function(c) -- 公告板·右
  c.rect(0, 0, 16, 16, PAL.grass)
  c.rect(0, 1, 14, 12, PAL.wood); c.rect(0, 3, 12, 8, C("f8f0d8"))
  c.hline(1, 5, 8, PAL.stoneD); c.hline(1, 7, 9, PAL.stoneD); c.hline(1, 9, 5, PAL.stoneD)
  c.vline(11, 13, 3, PAL.trunkD)
end
T[49] = function(c) -- 邮筒
  c.rect(0, 0, 16, 16, PAL.grass)
  c.rect(4, 2, 8, 10, PAL.red); c.rect(5, 1, 6, 2, C("c04040"))
  c.rect(6, 5, 4, 2, PAL.black)
  c.vline(6, 12, 3, C("c04040")); c.vline(9, 12, 3, C("c04040"))
end
T[50] = function(c) -- 栅栏·横
  c.rect(0, 0, 16, 16, PAL.grass)
  c.hline(0, 6, 16, PAL.woodL); c.hline(0, 7, 16, PAL.wood); c.hline(0, 10, 16, PAL.woodL); c.hline(0, 11, 16, PAL.wood)
  c.rect(2, 4, 2, 10, PAL.wood); c.rect(12, 4, 2, 10, PAL.wood)
  c.px(2, 4, PAL.woodL); c.px(12, 4, PAL.woodL)
end
T[51] = function(c) -- 栅栏·柱
  c.rect(0, 0, 16, 16, PAL.grass)
  c.rect(6, 2, 3, 12, PAL.wood); c.px(6, 2, PAL.woodL); c.px(7, 2, PAL.woodL)
end
T[52] = function(c) -- 路牌
  c.rect(0, 0, 16, 16, PAL.grass)
  c.rect(2, 3, 12, 6, PAL.woodL); c.hline(3, 5, 10, PAL.woodD); c.hline(3, 7, 7, PAL.woodD)
  c.rect(7, 9, 2, 6, PAL.wood)
end
T[53] = function(c) -- 野餐布1
  c.rect(0, 0, 16, 16, C("f8f0e0"))
  for j = 0, 15 do for i = 0, 15 do
    if (math.floor(i / 4) + math.floor(j / 4)) % 2 == 0 then c.px(i, j, C("f0a0a8")) end
  end end
end
T[54] = function(c) -- 野餐布2（有食物）
  T[53](c)
  c.rect(5, 5, 6, 6, C("fff8e8")); c.px(7, 7, PAL.berry); c.px(9, 8, PAL.berry); c.px(6, 9, PAL.yellow)
end
T[55] = function(c) -- 花坛（盛开）
  c.rect(0, 0, 16, 16, PAL.grass)
  c.rect(1, 1, 14, 14, PAL.stoneD); c.rect(2, 2, 12, 12, C("8a6a48"))
  c.px(4, 4, PAL.pink); c.px(8, 5, PAL.yellow); c.px(11, 4, PAL.red); c.px(5, 8, PAL.yellow)
  c.px(9, 9, PAL.pink); c.px(12, 8, PAL.white); c.px(4, 11, PAL.red); c.px(8, 12, PAL.pink); c.px(11, 11, PAL.yellow)
end
T[56] = function(c) -- 花田·土
  c.rect(0, 0, 16, 16, C("a8825a"))
  c.hline(0, 3, 16, C("94714c")); c.hline(0, 8, 16, C("94714c")); c.hline(0, 13, 16, C("94714c"))
end
T[57] = function(c) -- 花田·苗
  T[56](c)
  c.px(3, 2, PAL.leaf); c.px(3, 1, PAL.leaf); c.px(9, 7, PAL.leaf); c.px(9, 6, PAL.leaf); c.px(13, 12, PAL.leaf); c.px(13, 11, PAL.leaf)
  c.px(4, 1, PAL.pink); c.px(10, 6, PAL.yellow)
end
T[58] = function(c) -- 彩带（挂起）
  c.rect(0, 0, 16, 16, pc.rgba(0, 0, 0, 0))
  for i = 0, 15 do
    local y = math.floor(2 + 2 * math.sin(i / 4))
    c.px(i, y + 1, PAL.woodD)
  end
  c.px(2, 4, PAL.red); c.px(2, 5, PAL.red); c.px(6, 5, PAL.yellow); c.px(6, 6, PAL.yellow)
  c.px(10, 4, PAL.pink); c.px(10, 5, PAL.pink); c.px(14, 5, PAL.sky); c.px(14, 6, PAL.sky)
end
T[59] = function(c) -- 灯笼杆
  c.rect(0, 0, 16, 16, PAL.grass)
  c.rect(7, 2, 2, 12, PAL.trunk)
  c.rect(5, 2, 6, 5, PAL.yellow); c.rect(6, 1, 4, 1, PAL.roofD); c.px(7, 4, C("fff0a0")); c.px(8, 4, C("fff0a0"))
end
T[60] = function(c) -- 湖水·波光
  T[7](c)
  c.px(4, 6, PAL.white); c.px(11, 8, PAL.white); c.hline(7, 2, 3, PAL.waterL)
end
T[61] = function(c) -- 室内窗
  T[27](c)
  c.rect(3, 3, 10, 8, PAL.woodD); c.rect(4, 4, 8, 6, PAL.sky)
  c.vline(7, 4, 6, PAL.woodD); c.px(5, 5, PAL.waterL)
end
T[62] = function(c) -- 白色层·病床头
  c.rect(0, 0, 16, 16, PAL.wgrayD)
  c.rect(1, 1, 14, 15, PAL.wgray)
  c.rect(3, 3, 10, 6, PAL.white); c.hline(3, 8, 10, PAL.wgrayD)
  c.hline(1, 0, 14, PAL.wgrayDD)
end
T[63] = function(c) -- 白色层·病床尾
  c.rect(0, 0, 16, 16, PAL.wgrayD)
  c.rect(1, 0, 14, 15, C("dfe2e6"))
  c.hline(1, 4, 14, PAL.wgrayD); c.hline(1, 9, 14, PAL.wgrayD)
  c.hline(1, 15, 14, PAL.wgrayDD)
end
T[64] = function(c) -- 桌（诊所/办公）
  c.rect(0, 0, 16, 16, PAL.clinic)
  c.rect(1, 3, 14, 9, PAL.woodL); c.hline(1, 11, 14, PAL.woodD); c.hline(1, 3, 14, PAL.wood)
  c.rect(3, 5, 6, 4, PAL.white); c.hline(4, 6, 4, PAL.stoneD); c.hline(4, 7, 3, PAL.stoneD)
  c.vline(2, 12, 3, PAL.woodD); c.vline(13, 12, 3, PAL.woodD)
end
T[65] = function(c) -- 日历（墙面）
  T[27](c)
  c.rect(4, 2, 8, 11, PAL.white); c.rect(4, 2, 8, 3, PAL.red)
  c.hline(5, 7, 6, PAL.stoneD); c.hline(5, 9, 6, PAL.stoneD); c.rect(7, 10, 3, 2, PAL.black)
end
T[66] = function(c) -- 柜台+配方纸
  T[32](c)
  c.rect(4, 1, 7, 5, C("f8f0d8")); c.hline(5, 2, 5, PAL.stoneD); c.hline(5, 4, 4, PAL.stoneD)
end
T[67] = function(c) -- 木箱
  c.rect(0, 0, 16, 16, PAL.grass)
  c.rect(2, 3, 12, 12, PAL.wood); c.rect(3, 4, 10, 10, PAL.woodL)
  c.hline(3, 8, 10, PAL.wood); c.vline(8, 4, 10, PAL.wood)
end
T[68] = function(c) -- 篮子
  c.rect(0, 0, 16, 16, PAL.floor)
  c.rect(3, 6, 10, 8, PAL.woodL); c.hline(3, 6, 10, PAL.woodD); c.hline(3, 13, 10, PAL.woodD)
  c.dither(4, 8, 8, 5, PAL.wood, 2)
  c.px(6, 5, PAL.berry); c.px(9, 5, PAL.berry)
end
T[69] = function(c) -- 花坛（干涸·待浇水）
  c.rect(0, 0, 16, 16, PAL.grass)
  c.rect(1, 1, 14, 14, PAL.stoneD); c.rect(2, 2, 12, 12, C("b09068"))
  c.px(5, 5, C("8a7a5a")); c.px(10, 7, C("8a7a5a")); c.px(6, 10, C("8a7a5a"))
end
T[70] = function(c) -- 白色层·窗
  T[42](c)
  c.rect(4, 2, 8, 7, PAL.wgrayDD); c.rect(5, 3, 6, 5, C("c8ccd4")); c.vline(7, 3, 5, PAL.wgrayDD)
end
T[71] = function(c) -- 门垫
  c.rect(0, 0, 16, 16, PAL.grass)
  c.rect(2, 4, 12, 8, C("c8a878")); c.rect(3, 5, 10, 6, C("dcc094"))
end
T[72] = function(c) -- 高草
  c.rect(0, 0, 16, 16, PAL.grass)
  for i = 1, 14, 3 do c.vline(i, 6, 8, PAL.grassD); c.vline(i + 1, 8, 6, PAL.grassL) end
end
T[73] = function(c) -- 广场中心砖
  c.rect(0, 0, 16, 16, PAL.path)
  c.rect(2, 2, 12, 12, C("f0c88a")); c.rect(5, 5, 6, 6, PAL.path)
  c.px(7, 7, PAL.roofD); c.px(8, 8, PAL.roofD)
end
T[74] = function(c) -- 白色层·帘
  c.rect(0, 0, 16, 16, PAL.wgray)
  for i = 0, 15, 3 do c.vline(i, 0, 16, C("d8dade")) end
  c.hline(0, 0, 16, PAL.wgrayDD)
end
T[75] = function(c) -- 白色层·监护仪
  c.rect(0, 0, 16, 16, PAL.wgrayD)
  c.rect(3, 2, 10, 9, PAL.wdark); c.rect(4, 3, 8, 6, C("354048"))
  c.px(5, 6, C("7ae0a0")); c.px(6, 5, C("7ae0a0")); c.px(7, 6, C("7ae0a0")); c.px(8, 6, C("7ae0a0")); c.px(9, 4, C("7ae0a0")); c.px(10, 6, C("7ae0a0"))
  c.rect(6, 11, 4, 4, PAL.wgrayDD)
end
T[76] = function(c) -- 石墙（村界）
  c.rect(0, 0, 16, 16, PAL.stone)
  c.hline(0, 5, 16, PAL.stoneD); c.hline(0, 11, 16, PAL.stoneD)
  c.vline(4, 0, 5, PAL.stoneD); c.vline(10, 6, 5, PAL.stoneD); c.vline(6, 12, 4, PAL.stoneD)
end
T[77] = function(c) -- 雾中草地（边缘）
  c.rect(0, 0, 16, 16, PAL.grass)
  c.dither(0, 0, 16, 16, PAL.fog, 2)
end
T[78] = function(c) -- 黑
  c.rect(0, 0, 16, 16, PAL.black)
end
T[79] = function(c) -- 纯白灰
  c.rect(0, 0, 16, 16, PAL.wgray)
end

local tilesImg = Image(160, 128, ColorMode.RGB)
for idx = 0, 79 do
  local col, row = idx % 10, math.floor(idx / 10)
  T[idx](makeCtx(tilesImg, col * 16, row * 16))
end
saveSheet(tilesImg, "tiles")

-- ============ 主角：16×24 × 16 帧（下/上/左/右 × 4） ============
local SKIN = C("f6d2a8")
local HAIR = C("6b4632")
local SHIRT = C("e87858")
local SHIRT_D = C("cc5f42")
local PANTS = C("4a6a9a")
local SHOE = C("7a4a2a")

-- 画一帧主角；dir: 0下 1上 2左 3右；f: 0..3 行走帧（0 兼待机）
local function paintPlayer(c, dir, f)
  -- 头（x4..11, y0..7）：发 0..2，脸 3..7
  c.rect(4, 0, 8, 3, HAIR)
  c.px(4, 0, pc.rgba(0, 0, 0, 0)); c.px(11, 0, pc.rgba(0, 0, 0, 0))
  if dir == 1 then
    c.rect(4, 3, 8, 5, HAIR) -- 背面全是头发
    c.px(4, 7, SKIN); c.px(11, 7, SKIN)
  else
    c.rect(4, 3, 8, 5, SKIN)
    c.vline(4, 3, 2, HAIR); c.vline(11, 3, 2, HAIR) -- 刘海两侧
    if dir == 0 then
      c.px(6, 5, PAL.black); c.px(9, 5, PAL.black)
      c.px(7, 7, PAL.black); c.px(8, 7, PAL.black) -- 永远的微笑
      c.px(6, 6, C("f8b8a0")); c.px(9, 6, C("f8b8a0"))
    elseif dir == 2 then
      c.rect(9, 3, 3, 5, HAIR) -- 侧脸：后脑
      c.px(6, 5, PAL.black); c.px(5, 7, PAL.black); c.px(6, 7, PAL.black)
    elseif dir == 3 then
      c.rect(4, 3, 3, 5, HAIR)
      c.px(9, 5, PAL.black); c.px(9, 7, PAL.black); c.px(10, 7, PAL.black)
    end
  end
  -- 身体（y8..16）
  c.rect(5, 8, 6, 9, SHIRT)
  c.hline(5, 16, 6, SHIRT_D)
  -- 手臂：随帧摆动
  local sway = ({ 0, 1, 0, -1 })[f + 1]
  if dir == 0 or dir == 1 then
    c.vline(4, 9 + (sway > 0 and 1 or 0), 5, SHIRT_D); c.px(4, 14 + (sway > 0 and 1 or 0), SKIN)
    c.vline(11, 9 + (sway < 0 and 1 or 0), 5, SHIRT_D); c.px(11, 14 + (sway < 0 and 1 or 0), SKIN)
  else
    c.vline(dir == 2 and 7 or 8, 9 + math.abs(sway), 5, SHIRT_D)
    c.px(dir == 2 and 7 or 8, 14 + math.abs(sway), SKIN)
  end
  -- 腿（y17..20）与鞋（y21..23）
  local lLift = (f == 1) and 1 or 0
  local rLift = (f == 3) and 1 or 0
  c.rect(5, 17, 3, 4 - lLift, PANTS)
  c.rect(8, 17, 3, 4 - rLift, PANTS)
  c.rect(5, 21 - lLift, 3, 2, SHOE)
  c.rect(8, 21 - rLift, 3, 2, SHOE)
end

local playerImg = Image(256, 24, ColorMode.RGB)
for dir = 0, 3 do
  for f = 0, 3 do
    paintPlayer(makeCtx(playerImg, (dir * 4 + f) * 16, 0), dir, f)
  end
end
saveSheet(playerImg, "player")

-- ============ 主角头像：24×24（微笑 / 真实的脸） ============
local function paintPortrait(c, real)
  c.rect(0, 0, 24, 24, C("f8ecd0")) -- 背景
  c.rect(4, 2, 16, 6, HAIR)         -- 头发
  c.px(4, 2, C("f8ecd0")); c.px(19, 2, C("f8ecd0"))
  c.rect(4, 8, 16, 12, SKIN)        -- 脸
  c.vline(4, 8, 4, HAIR); c.vline(5, 8, 2, HAIR)
  c.vline(19, 8, 4, HAIR); c.vline(18, 8, 2, HAIR)
  c.rect(6, 20, 12, 4, SHIRT)       -- 肩
  if real then
    -- 真实的脸：疲惫——平直的嘴、眼下阴影、微乱的头发
    c.px(3, 4, HAIR); c.px(20, 5, HAIR); c.px(2, 5, HAIR)
    c.rect(8, 12, 2, 2, PAL.black); c.rect(14, 12, 2, 2, PAL.black)
    c.hline(8, 15, 2, C("c8a080")); c.hline(14, 15, 2, C("c8a080")) -- 眼袋
    c.hline(10, 18, 4, C("9a6a52")) -- 平直疲惫的嘴
  else
    -- 永远的微笑
    c.rect(8, 12, 2, 2, PAL.black); c.rect(14, 12, 2, 2, PAL.black)
    c.px(7, 14, C("f8b8a0")); c.px(16, 14, C("f8b8a0")) -- 腮红
    c.px(9, 17, C("9a6a52")); c.px(14, 17, C("9a6a52"))
    c.hline(10, 18, 4, C("9a6a52")) -- 上弯的微笑
  end
end
local p1 = Image(24, 24, ColorMode.RGB); paintPortrait(makeCtx(p1, 0, 0), false); saveSheet(p1, "portrait_smile")
local p2 = Image(24, 24, ColorMode.RGB); paintPortrait(makeCtx(p2, 0, 0), true); saveSheet(p2, "portrait_real")

-- ============ 具名 NPC：16×24 × 4 帧（smile/flat/off/blink） ============
-- off 档与 smile 档仅差 1–2 像素（右眼低 1 像素），绝不画成鬼脸。
local function paintAnimal(c, spec, expr)
  local top = spec.headTop or 2
  -- 头
  c.rect(2, top, 12, 10, spec.fur)
  c.px(2, top, pc.rgba(0, 0, 0, 0)); c.px(13, top, pc.rgba(0, 0, 0, 0))
  c.px(2, top + 9, pc.rgba(0, 0, 0, 0)); c.px(13, top + 9, pc.rgba(0, 0, 0, 0))
  -- 口鼻
  c.rect(5, top + 5, 6, 4, spec.muzzle)
  -- 眼睛
  local ey = top + 4
  if expr == "blink" then
    c.px(5, ey + 1, PAL.black); c.px(10, ey + 1, PAL.black)
  else
    c.px(5, ey, PAL.black); c.px(5, ey + 1, PAL.black)
    local roff = (expr == "off") and 1 or 0 -- 崩坏档：右眼低 1 像素
    c.px(10, ey + roff, PAL.black); c.px(10, ey + 1 + roff, PAL.black)
  end
  -- 鼻子
  if spec.nose then c.px(7, top + 6, PAL.black); c.px(8, top + 6, PAL.black) end
  -- 嘴：微笑（小 U）或平直
  local my = top + 8
  if expr == "flat" then
    c.hline(6, my, 4, PAL.black)
  else
    c.px(6, my - 1, PAL.black); c.px(9, my - 1, PAL.black); c.px(7, my, PAL.black); c.px(8, my, PAL.black)
  end
  -- 身体
  c.rect(4, 12, 8, 8, spec.cloth)
  -- 手
  c.vline(3, 13, 4, spec.fur); c.vline(12, 13, 4, spec.fur)
  -- 脚
  c.rect(4, 20, 3, 3, spec.furD); c.rect(9, 20, 3, 3, spec.furD)
  if spec.extra then spec.extra(c, expr) end
end

local NPCS = {
  { file = "npc_afu", fur = C("c49058"), furD = C("a87840"), muzzle = C("ecd0a8"), cloth = C("e0b060"), nose = true,
    extra = function(c) -- 狗村长：垂耳+红领巾
      c.rect(1, 3, 2, 5, C("a87840")); c.rect(13, 3, 2, 5, C("a87840"))
      c.hline(4, 12, 8, PAL.red); c.px(7, 13, PAL.red); c.px(8, 13, PAL.red)
    end },
  { file = "npc_yuanyuan", fur = C("f7f2ea"), furD = C("ddd4c8"), muzzle = C("ffffff"), cloth = C("f8c8d8"), headTop = 4,
    extra = function(c) -- 兔面包师：长耳+围裙
      c.rect(4, 0, 2, 4, C("f7f2ea")); c.px(5, 1, C("f8b8c8")); c.px(5, 2, C("f8b8c8"))
      c.rect(10, 0, 2, 4, C("f7f2ea")); c.px(10, 1, C("f8b8c8")); c.px(10, 2, C("f8b8c8"))
      c.rect(5, 14, 6, 6, C("fff8f0")); c.px(6, 10, C("f0a0b0")) -- 围裙+腮红
      c.px(9, 10, C("f0a0b0"))
    end },
  { file = "npc_dabao", fur = C("9a6b42"), furD = C("7e5530"), muzzle = C("c89a68"), cloth = C("5a88c8"), nose = true,
    extra = function(c) -- 熊邮差：圆耳+挎包带
      c.rect(3, 0, 3, 2, C("9a6b42")); c.rect(10, 0, 3, 2, C("9a6b42"))
      c.px(4, 1, C("c89a68")); c.px(11, 1, C("c89a68"))
      for i = 0, 6 do c.px(4 + i, 13 + i > 19 and 19 or 13 + math.floor(i * 0.9), C("3a5a88")) end
    end },
  { file = "npc_mimi", fur = C("c8c0d8"), furD = C("a8a0c0"), muzzle = C("ece8f4"), cloth = C("e8a04a"), nose = true,
    extra = function(c) -- 猫店主：尖耳+胡须
      c.px(3, 1, C("c8c0d8")); c.rect(3, 2, 2, 1, C("c8c0d8")); c.px(4, 1, C("c8c0d8"))
      c.px(12, 1, C("c8c0d8")); c.rect(11, 2, 2, 1, C("c8c0d8")); c.px(11, 1, C("c8c0d8"))
      c.hline(1, 8, 2, C("8a84a0")); c.hline(13, 8, 2, C("8a84a0"))
    end },
  { file = "npc_fu", fur = C("d8b088"), furD = C("bc9268"), muzzle = C("f0d8b8"), cloth = C("f4f4f4"), nose = true,
    extra = function(c) -- 鹿医生：小角+白大褂
      c.px(4, 1, C("8a6a4a")); c.px(4, 0, C("8a6a4a")); c.px(3, 0, C("8a6a4a"))
      c.px(11, 1, C("8a6a4a")); c.px(11, 0, C("8a6a4a")); c.px(12, 0, C("8a6a4a"))
      c.vline(7, 12, 8, C("d8d8d8")) -- 大褂门襟
    end },
  { file = "npc_gui", fur = C("a8c078"), furD = C("88a058"), muzzle = C("c8d8a0"), cloth = C("7a9a4a"),
    extra = function(c) -- 龟爷爷：龟壳纹+老花镜
      c.hline(5, 14, 6, C("5f7c38")); c.hline(5, 17, 6, C("5f7c38")); c.vline(7, 13, 6, C("5f7c38"))
      local gy = 2 + 4
      c.px(4, gy, C("6a6a6a")); c.px(6, gy, C("6a6a6a")); c.px(9, gy, C("6a6a6a")); c.px(11, gy, C("6a6a6a"))
      c.px(7, gy, C("6a6a6a")); c.px(8, gy, C("6a6a6a"))
    end },
}

for _, spec in ipairs(NPCS) do
  local img = Image(64, 24, ColorMode.RGB)
  local exprs = { "smile", "flat", "off", "blink" }
  for i, e in ipairs(exprs) do
    paintAnimal(makeCtx(img, (i - 1) * 16, 0), spec, e)
  end
  saveSheet(img, spec.file)
end

-- ============ 双子鸟：16×16 × 2 帧（闭嘴/张嘴） ============
local function paintBird(c, open)
  local B = C("6ab0e8")
  local BD = C("4a90c8")
  c.rect(4, 5, 8, 7, B)   -- 身体
  c.rect(8, 2, 6, 6, B)   -- 头
  c.px(11, 4, PAL.black)  -- 眼
  c.rect(2, 6, 3, 4, BD)  -- 尾
  c.rect(5, 7, 4, 3, BD)  -- 翅
  if open then
    c.px(14, 5, PAL.yellow); c.px(15, 4, PAL.yellow); c.px(15, 6, PAL.yellow)
  else
    c.px(14, 5, PAL.yellow); c.px(15, 5, PAL.yellow)
  end
  c.vline(7, 12, 2, PAL.yellow); c.vline(10, 12, 2, PAL.yellow) -- 脚
end
local birdImg = Image(32, 16, ColorMode.RGB)
paintBird(makeCtx(birdImg, 0, 0), false)
paintBird(makeCtx(birdImg, 16, 0), true)
saveSheet(birdImg, "bird")

-- ============ 灰麻雀（E3 结局，一只真的鸟）：16×16 ============
local function paintSparrow(c)
  local G = C("9a9088")
  local GD = C("6e6660")
  c.rect(4, 6, 8, 6, G)
  c.rect(8, 3, 6, 5, G)
  c.px(11, 5, PAL.black)
  c.rect(2, 7, 3, 3, GD)
  c.rect(5, 8, 4, 2, GD)
  c.px(14, 6, C("7a6a58")); c.px(15, 6, C("7a6a58"))
  c.vline(7, 12, 2, GD); c.vline(10, 12, 2, GD)
end
local sparrowImg = Image(16, 16, ColorMode.RGB)
paintSparrow(makeCtx(sparrowImg, 0, 0))
saveSheet(sparrowImg, "sparrow")

-- ============ 群演 ×5：16×16 × 2 帧循环 ============
local function paintCritter(c, spec, f)
  local bob = f -- 第二帧整体下移 1px 形成循环
  c.rect(4, 6 + bob, 8, 6 - bob, spec.body)
  c.rect(5, 2 + bob, 6, 5, spec.body)
  c.px(6, 4 + bob, PAL.black); c.px(9, 4 + bob, PAL.black)
  c.px(7, 6 + bob, spec.snout); c.px(8, 6 + bob, spec.snout)
  c.rect(4, 12, 3, 2, spec.dark); c.rect(9, 12, 3, 2, spec.dark)
  if spec.extra then spec.extra(c, f) end
end
local CRITTERS = {
  { file = "extra1", body = C("e0904a"), dark = C("b87030"), snout = C("f8c890"), -- 松鼠：大尾巴
    extra = function(c, f) c.rect(12, 1 + f, 3, 9, C("b87030")); c.px(13, 2 + f, C("e0904a")) end },
  { file = "extra2", body = C("b09068"), dark = C("8a6a48"), snout = C("e8cca8"), -- 刺猬：背刺
    extra = function(c, f) for i = 4, 11, 2 do c.px(i, 1 + f + (i % 4 == 0 and 0 or 1), C("6e5236")) end end },
  { file = "extra3", body = C("f4e04a"), dark = C("d8b830"), snout = C("f4a030"), -- 鸭子：扁嘴
    extra = function(c, f) c.px(6, 5 + f, C("f4a030")); c.px(9, 5 + f, C("f4a030")) end },
  { file = "extra4", body = C("b8b8c4"), dark = C("8e8e9c"), snout = C("e8c8c8"), -- 老鼠：圆耳
    extra = function(c, f) c.rect(4, 0 + f, 2, 2, C("b8b8c4")); c.rect(10, 0 + f, 2, 2, C("b8b8c4")) end },
  { file = "extra5", body = C("7ac86a"), dark = C("58a048"), snout = C("aee89e"), -- 青蛙：凸眼
    extra = function(c, f) c.px(5, 1 + f, C("7ac86a")); c.px(10, 1 + f, C("7ac86a")) end },
}
for _, spec in ipairs(CRITTERS) do
  local img = Image(32, 16, ColorMode.RGB)
  paintCritter(makeCtx(img, 0, 0), spec, 0)
  paintCritter(makeCtx(img, 16, 0), spec, 1)
  saveSheet(img, spec.file)
end

-- ============ 向日葵表盘：24×24 × 14 帧 ============
-- 帧 0..12 = 点亮花瓣数；帧 13 = 满格+花心黑籽
local function paintSunflower(c, lit, seeds)
  local cx, cy = 11.5, 11.5
  for k = 0, 11 do
    local ang = (k / 12) * math.pi * 2 - math.pi / 2
    local col = (k < lit) and PAL.yellow or C("7a7264")
    local colD = (k < lit) and C("e0b830") or C("635c50")
    for r = 7, 10 do
      local x = math.floor(cx + math.cos(ang) * r + 0.5)
      local y = math.floor(cy + math.sin(ang) * r + 0.5)
      c.px(x, y, r > 9 and colD or col)
      local px2 = math.floor(cx + math.cos(ang + 0.14) * r + 0.5)
      local py2 = math.floor(cy + math.sin(ang + 0.14) * r + 0.5)
      c.px(px2, py2, col)
    end
  end
  -- 花心
  for j = -4, 4 do for i = -4, 4 do
    if i * i + j * j <= 18 then c.px(12 + i, 12 + j, C("9a6a2a")) end
  end end
  for j = -3, 3 do for i = -3, 3 do
    if i * i + j * j <= 9 then c.px(12 + i, 12 + j, C("b8843a")) end
  end end
  if seeds then -- 过熟的黑籽
    c.px(11, 11, PAL.black); c.px(13, 12, PAL.black); c.px(12, 14, PAL.black)
    c.px(10, 13, PAL.black); c.px(14, 10, PAL.black); c.px(12, 11, C("4a3a20"))
  end
end
local sunImg = Image(24 * 14, 24, ColorMode.RGB)
for f = 0, 12 do paintSunflower(makeCtx(sunImg, f * 24, 0), f, false) end
paintSunflower(makeCtx(sunImg, 13 * 24, 0), 12, true)
saveSheet(sunImg, "sunflower")

print("ALL ART GENERATED OK")
