Honey Village: six model builds + hub frontend, Dockerized for Dokploy
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { readdirSync, mkdirSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
const root = resolve(import.meta.dirname, '..');
|
||||
const sourceDir = resolve(root, 'public/maps/source');
|
||||
const outDir = resolve(root, 'public/maps');
|
||||
const tiled = process.env.TILED ?? '/Applications/Tiled.app/Contents/MacOS/Tiled';
|
||||
mkdirSync(outDir, { recursive: true });
|
||||
for (const file of readdirSync(sourceDir).filter((name) => name.endsWith('.tmx'))) {
|
||||
execFileSync(tiled, ['--export-map', 'json', resolve(sourceDir, file), resolve(outDir, file.replace('.tmx', '.json'))], { stdio: 'inherit' });
|
||||
}
|
||||
console.log('Exported 10 Tiled JSON maps.');
|
||||
@@ -0,0 +1,179 @@
|
||||
local root = app.params["root"]
|
||||
local assetDir = root .. "/public/assets"
|
||||
local sourceDir = assetDir .. "/source"
|
||||
app.fs.makeDirectory(root .. "/public")
|
||||
app.fs.makeDirectory(assetDir)
|
||||
app.fs.makeDirectory(sourceDir)
|
||||
|
||||
local C = {
|
||||
transparent = Color{ r=0, g=0, b=0, a=0 },
|
||||
grass = Color{ r=104, g=190, b=82, a=255 },
|
||||
grass2 = Color{ r=76, g=158, b=70, a=255 },
|
||||
cream = Color{ r=255, g=233, b=164, a=255 },
|
||||
honey = Color{ r=239, g=151, b=49, a=255 },
|
||||
orange = Color{ r=199, g=97, b=43, a=255 },
|
||||
sky = Color{ r=93, g=190, b=224, a=255 },
|
||||
water = Color{ r=68, g=171, b=216, a=255 },
|
||||
water2 = Color{ r=139, g=224, b=231, a=255 },
|
||||
stone = Color{ r=206, g=199, b=171, a=255 },
|
||||
stone2 = Color{ r=162, g=155, b=133, a=255 },
|
||||
brown = Color{ r=91, g=54, b=43, a=255 },
|
||||
wood = Color{ r=151, g=91, b=48, a=255 },
|
||||
leaf = Color{ r=51, g=130, b=60, a=255 },
|
||||
pink = Color{ r=237, g=111, b=150, a=255 },
|
||||
red = Color{ r=194, g=66, b=64, a=255 },
|
||||
white = Color{ r=247, g=244, b=226, a=255 },
|
||||
gray = Color{ r=194, g=199, b=199, a=255 },
|
||||
gray2 = Color{ r=132, g=142, b=145, a=255 },
|
||||
black = Color{ r=48, g=38, b=37, a=255 },
|
||||
yellow = Color{ r=255, g=213, b=62, a=255 },
|
||||
purple = Color{ r=133, g=89, b=159, a=255 },
|
||||
}
|
||||
|
||||
local function fill(img, x, y, w, h, color)
|
||||
for yy = y, y + h - 1 do
|
||||
for xx = x, x + w - 1 do img:drawPixel(xx, yy, color) end
|
||||
end
|
||||
end
|
||||
|
||||
local function pixel(img, x, y, color) img:drawPixel(x, y, color) end
|
||||
|
||||
local function outlineRect(img, x, y, w, h, color)
|
||||
fill(img, x, y, w, 1, color); fill(img, x, y+h-1, w, 1, color)
|
||||
fill(img, x, y, 1, h, color); fill(img, x+w-1, y, 1, h, color)
|
||||
end
|
||||
|
||||
local function save(sprite, sourceName, pngName)
|
||||
sprite:saveAs(sourceDir .. "/" .. sourceName)
|
||||
sprite:saveCopyAs(assetDir .. "/" .. pngName)
|
||||
sprite:close()
|
||||
end
|
||||
|
||||
local function makeTileset()
|
||||
local sprite = Sprite(256, 64, ColorMode.RGB)
|
||||
sprite.filename = "tileset"
|
||||
sprite.cels[1].image:clear(C.transparent)
|
||||
local img = sprite.cels[1].image
|
||||
local function tile(index, base)
|
||||
local x = (index % 16) * 16; local y = math.floor(index / 16) * 16
|
||||
fill(img, x, y, 16, 16, base)
|
||||
return x, y
|
||||
end
|
||||
|
||||
for i=0,63 do
|
||||
local palette = {C.grass, C.stone, C.water, C.cream, C.wood, C.gray}
|
||||
local x,y = tile(i, palette[(i % #palette)+1])
|
||||
if i % 6 == 0 then
|
||||
pixel(img,x+3,y+5,C.grass2); pixel(img,x+11,y+3,C.cream); pixel(img,x+12,y+3,C.pink)
|
||||
elseif i % 6 == 1 then
|
||||
outlineRect(img,x+1,y+1,7,6,C.stone2); outlineRect(img,x+8,y+7,7,8,C.stone2)
|
||||
elseif i % 6 == 2 then
|
||||
fill(img,x,y+3,16,2,C.water2); fill(img,x+5,y+10,9,1,C.water2)
|
||||
elseif i % 6 == 3 then
|
||||
fill(img,x,y,16,4,C.honey); fill(img,x+2,y+7,4,3,C.orange); fill(img,x+10,y+6,4,4,C.orange)
|
||||
elseif i % 6 == 4 then
|
||||
fill(img,x,y+4,16,2,C.brown); fill(img,x+4,y,2,16,C.brown); fill(img,x+12,y,1,16,C.brown)
|
||||
else
|
||||
outlineRect(img,x+1,y+1,14,14,C.gray2); fill(img,x+3,y+3,10,10,C.white)
|
||||
end
|
||||
end
|
||||
|
||||
local x,y = tile(0,C.grass); pixel(img,x+3,y+5,C.grass2); pixel(img,x+11,y+9,C.grass2)
|
||||
x,y = tile(1,C.stone); outlineRect(img,x,y,8,8,C.stone2); outlineRect(img,x+8,y+8,8,8,C.stone2)
|
||||
x,y = tile(2,C.water); fill(img,x,y+4,12,2,C.water2); fill(img,x+5,y+11,11,1,C.water2)
|
||||
x,y = tile(3,C.cream); fill(img,x,y,16,3,C.honey); fill(img,x,y+13,16,3,C.honey)
|
||||
x,y = tile(4,C.wood); fill(img,x,y+4,16,2,C.brown); fill(img,x,y+11,16,1,C.brown)
|
||||
x,y = tile(5,C.white); outlineRect(img,x,y,16,16,C.gray)
|
||||
x,y = tile(6,C.grass); fill(img,x+2,y+3,12,10,C.leaf); pixel(img,x+6,y+6,C.red); pixel(img,x+11,y+9,C.red)
|
||||
x,y = tile(7,C.grass); fill(img,x+1,y+2,14,12,C.leaf); fill(img,x+4,y+4,3,3,C.pink); fill(img,x+10,y+7,3,3,C.yellow)
|
||||
x,y = tile(8,C.honey); fill(img,x,y+6,16,10,C.cream); outlineRect(img,x,y+6,16,10,C.orange)
|
||||
x,y = tile(9,C.orange); fill(img,x,y+6,16,10,C.cream); fill(img,x+6,y+9,4,7,C.brown)
|
||||
x,y = tile(10,C.cream); fill(img,x+2,y+5,12,8,C.wood); outlineRect(img,x+2,y+5,12,8,C.brown)
|
||||
x,y = tile(11,C.grass); fill(img,x+6,y,4,16,C.wood); fill(img,x+1,y,14,8,C.leaf)
|
||||
x,y = tile(12,C.grass); fill(img,x+2,y+2,12,12,C.pink); fill(img,x+5,y+5,6,6,C.yellow)
|
||||
x,y = tile(13,C.wood); fill(img,x+4,y+2,8,14,C.black); fill(img,x+6,y+3,4,11,C.water)
|
||||
x,y = tile(14,C.sky); fill(img,x+4,y,8,16,C.white); fill(img,x+1,y+4,14,8,C.white)
|
||||
x,y = tile(15,C.white); fill(img,x,y,16,3,C.gray); fill(img,x,y+13,16,3,C.gray)
|
||||
x,y = tile(16,C.grass); fill(img,x+3,y+5,10,7,C.white); outlineRect(img,x+3,y+5,10,7,C.brown); pixel(img,x+8,y+8,C.red)
|
||||
save(sprite, "tileset.aseprite", "tileset.png")
|
||||
end
|
||||
|
||||
local speciesColors = {C.cream,C.pink,C.brown,C.orange,C.gray,C.honey,C.grass2,C.purple,C.red,C.sky,C.yellow,C.wood,C.white}
|
||||
|
||||
local function drawCharacter(img, ox, oy, body, facing, step, expression, species)
|
||||
fill(img,ox,oy,16,24,C.transparent)
|
||||
local skin = body
|
||||
fill(img,ox+4,oy+3,8,8,skin)
|
||||
fill(img,ox+3,oy+5,10,5,skin)
|
||||
if species == "rabbit" then fill(img,ox+4,oy,2,5,skin); fill(img,ox+10,oy,2,5,skin) end
|
||||
if species == "deer" then pixel(img,ox+3,oy+2,C.brown); pixel(img,ox+12,oy+2,C.brown) end
|
||||
if species == "bird" then fill(img,ox+3,oy+6,10,8,skin); pixel(img,ox+13,oy+8,C.honey) end
|
||||
local eyeY = oy+6
|
||||
pixel(img,ox+6,eyeY,C.black); pixel(img,ox+10,eyeY + (expression == "off" and 1 or 0),C.black)
|
||||
if expression == "smile" then pixel(img,ox+7,oy+9,C.red); pixel(img,ox+8,oy+10,C.red); pixel(img,ox+9,oy+9,C.red)
|
||||
elseif expression == "flat" then fill(img,ox+7,oy+9,3,1,C.black)
|
||||
else pixel(img,ox+7,oy+9,C.black); fill(img,ox+8,oy+10,3,1,C.black) end
|
||||
fill(img,ox+4,oy+12,8,8,body)
|
||||
fill(img,ox+3,oy+13,2,6,C.cream); fill(img,ox+11,oy+13,2,6,C.cream)
|
||||
local foot = step % 2
|
||||
if facing == "left" or facing == "right" then
|
||||
fill(img,ox+4-foot,oy+20,3,3,C.black); fill(img,ox+9+foot,oy+20,3,3,C.black)
|
||||
else
|
||||
fill(img,ox+4+foot,oy+20,3,3,C.black); fill(img,ox+9-foot,oy+20,3,3,C.black)
|
||||
end
|
||||
if facing == "up" then fill(img,ox+5,oy+5,6,4,skin); fill(img,ox+5,oy+6,6,2,C.brown) end
|
||||
if facing == "left" then pixel(img,ox+5,eyeY,C.black); fill(img,ox+10,eyeY,2,2,skin) end
|
||||
if facing == "right" then pixel(img,ox+11,eyeY,C.black); fill(img,ox+4,eyeY,2,2,skin) end
|
||||
end
|
||||
|
||||
local function makeCharacters()
|
||||
local cols, rows = 16, 4
|
||||
local sprite = Sprite(cols*16, rows*24, ColorMode.RGB)
|
||||
sprite.cels[1].image:clear(C.transparent)
|
||||
local img = sprite.cels[1].image
|
||||
local dirs = {"down","up","left","right"}
|
||||
for d=0,3 do for f=0,3 do drawCharacter(img,f*16,d*24,C.sky,dirs[d+1],f,"smile","human") end end
|
||||
local npc = {
|
||||
{C.honey,"dog"},{C.white,"rabbit"},{C.brown,"bear"},{C.orange,"cat"},
|
||||
{C.cream,"deer"},{C.sky,"bird"},{C.grass2,"turtle"}
|
||||
}
|
||||
for n=0,6 do for e=0,2 do
|
||||
local index = 16 + n*3 + e; local ox=(index%cols)*16; local oy=math.floor(index/cols)*24
|
||||
local exp=({"smile","flat","off"})[e+1]
|
||||
drawCharacter(img,ox,oy,npc[n+1][1],"down",0,exp,npc[n+1][2])
|
||||
end end
|
||||
for n=0,4 do
|
||||
local index=37+n; local ox=(index%cols)*16; local oy=math.floor(index/cols)*24
|
||||
drawCharacter(img,ox,oy,speciesColors[n+8],"down",n%2,"smile","human")
|
||||
end
|
||||
for n=0,3 do
|
||||
local index=42+n; local ox=(index%cols)*16; local oy=math.floor(index/cols)*24
|
||||
drawCharacter(img,ox,oy,(n%2==0 and C.pink or C.sky),"down",n%2,"smile","bird")
|
||||
if n%2==1 then fill(img,ox+7,oy+9,4,2,C.black) end
|
||||
end
|
||||
save(sprite,"characters.aseprite","characters.png")
|
||||
end
|
||||
|
||||
local function drawPortrait(img, ox, body, expression, real)
|
||||
fill(img,ox,0,48,48,C.cream)
|
||||
fill(img,ox+7,6,34,34,C.brown)
|
||||
fill(img,ox+9,8,30,30,body)
|
||||
fill(img,ox+14,17,4,5,C.black); fill(img,ox+30,17 + (expression=="off" and 2 or 0),4,5,C.black)
|
||||
if real then
|
||||
fill(img,ox+15,31,18,2,C.black); fill(img,ox+13,13,8,2,C.gray2); fill(img,ox+27,13,8,2,C.gray2)
|
||||
elseif expression == "smile" then
|
||||
fill(img,ox+17,29,4,3,C.red); fill(img,ox+21,32,8,3,C.red); fill(img,ox+29,29,3,3,C.red)
|
||||
else fill(img,ox+18,30,14,2,C.black) end
|
||||
end
|
||||
|
||||
local function makePortraits()
|
||||
local sprite=Sprite(9*48,48,ColorMode.RGB); sprite.cels[1].image:clear(C.transparent)
|
||||
local img=sprite.cels[1].image
|
||||
drawPortrait(img,0,C.sky,"smile",false); drawPortrait(img,48,C.gray,"flat",true)
|
||||
local colors={C.honey,C.white,C.brown,C.orange,C.cream,C.sky,C.grass2}
|
||||
for i=0,6 do drawPortrait(img,(i+2)*48,colors[i+1],"smile",false) end
|
||||
save(sprite,"portraits.aseprite","portraits.png")
|
||||
end
|
||||
|
||||
makeTileset(); makeCharacters(); makePortraits()
|
||||
print("Generated original Aseprite assets in " .. assetDir)
|
||||
@@ -0,0 +1,129 @@
|
||||
import { mkdir, writeFile } from 'node:fs/promises';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
const root = resolve(import.meta.dirname, '..');
|
||||
const sourceDir = resolve(root, 'public/maps/source');
|
||||
await mkdir(sourceDir, { recursive: true });
|
||||
|
||||
const maps = [
|
||||
['HouseScene', '小满小屋', 12, 8, 4, [
|
||||
['spawn', 96, 88, { id: 'default' }], ['door', 96, 112, { target: 'PlazaScene', spawn: 'house' }],
|
||||
['interact', 42, 34, { id: 'calendar', label: '床头日历' }], ['interact', 64, 42, { id: 'bed', label: '床' }],
|
||||
]],
|
||||
['PlazaScene', '村广场', 30, 20, 0, [
|
||||
['spawn', 240, 260, { id: 'default' }], ['spawn', 240, 280, { id: 'house' }], ['spawn', 32, 160, { id: 'bakery' }], ['spawn', 448, 160, { id: 'shop' }], ['spawn', 240, 32, { id: 'clinic' }], ['spawn', 32, 48, { id: 'lake' }], ['spawn', 448, 48, { id: 'field' }], ['spawn', 32, 280, { id: 'well' }], ['spawn', 448, 280, { id: 'mist' }],
|
||||
['door', 240, 304, { target: 'HouseScene', spawn: 'default' }], ['door', 0, 160, { target: 'BakeryScene', spawn: 'default' }], ['door', 464, 160, { target: 'ShopScene', spawn: 'default' }], ['door', 240, 0, { target: 'ClinicScene', spawn: 'default' }], ['door', 0, 48, { target: 'LakeScene', spawn: 'default' }], ['door', 464, 48, { target: 'FieldScene', spawn: 'default' }], ['door', 0, 280, { target: 'WellScene', spawn: 'default' }], ['door', 464, 280, { target: 'MistScene', spawn: 'default' }],
|
||||
['npc', 240, 132, { id: 'afu', label: '阿福' }], ['npc', 180, 162, { id: 'dabao', label: '大宝' }],
|
||||
['npc', 232, 76, { id: 'birds', label: '双子鸟' }], ['npc', 124, 210, { id: 'extra1', label: '村民' }], ['npc', 352, 224, { id: 'extra2', label: '村民' }],
|
||||
['interact', 240, 146, { id: 'board', label: '公告板' }], ['interact', 92, 148, { id: 'mail', label: '散落信件' }], ['interact', 200, 184, { id: 'flower1', label: '花坛' }], ['interact', 240, 200, { id: 'flower2', label: '花坛' }], ['interact', 280, 184, { id: 'flower3', label: '花坛' }],
|
||||
['interact', 160, 100, { id: 'ribbon1', label: '彩带架' }], ['interact', 224, 92, { id: 'ribbon2', label: '彩带架' }], ['interact', 288, 92, { id: 'ribbon3', label: '彩带架' }], ['interact', 352, 100, { id: 'ribbon4', label: '彩带架' }], ['interact', 240, 224, { id: 'festival', label: '庆典场地' }],
|
||||
]],
|
||||
['BakeryScene', '面包房', 15, 10, 3, [
|
||||
['spawn', 120, 128, { id: 'default' }], ['door', 120, 144, { target: 'PlazaScene', spawn: 'bakery' }], ['npc', 120, 72, { id: 'yuanyuan', label: '圆圆' }], ['interact', 48, 46, { id: 'recipe', label: '配方纸' }], ['interact', 184, 50, { id: 'oven', label: '烤炉' }],
|
||||
]],
|
||||
['ShopScene', '杂货店', 15, 10, 3, [
|
||||
['spawn', 120, 128, { id: 'default' }], ['door', 120, 144, { target: 'PlazaScene', spawn: 'shop' }], ['npc', 120, 72, { id: 'mimi', label: '咪咪' }], ['interact', 48, 52, { id: 'shelf', label: '货架' }],
|
||||
]],
|
||||
['ClinicScene', '诊所', 12, 8, 5, [
|
||||
['spawn', 96, 96, { id: 'default' }], ['door', 96, 112, { target: 'PlazaScene', spawn: 'clinic' }], ['npc', 96, 50, { id: 'doctor', label: '芙医生' }], ['interact', 48, 42, { id: 'form', label: '桌上表格' }],
|
||||
]],
|
||||
['LakeScene', '湖畔', 24, 16, 2, [
|
||||
['spawn', 368, 128, { id: 'default' }], ['door', 368, 128, { target: 'PlazaScene', spawn: 'lake' }], ['npc', 142, 132, { id: 'turtle', label: '龟爷爷' }], ['npc', 284, 68, { id: 'extra4', label: '村民' }], ['interact', 190, 148, { id: 'picnic', label: '野餐布' }], ['interact', 110, 68, { id: 'lake', label: '湖面' }],
|
||||
]],
|
||||
['FieldScene', '花田', 20, 14, 0, [
|
||||
['spawn', 16, 112, { id: 'default' }], ['door', 0, 112, { target: 'PlazaScene', spawn: 'field' }], ['npc', 244, 148, { id: 'extra3', label: '村民' }],
|
||||
['interact', 80, 72, { id: 'berry1', label: '莓果丛' }], ['interact', 128, 56, { id: 'berry2', label: '莓果丛' }], ['interact', 176, 88, { id: 'berry3', label: '莓果丛' }], ['interact', 224, 64, { id: 'berry4', label: '莓果丛' }], ['interact', 272, 96, { id: 'berry5', label: '莓果丛' }],
|
||||
]],
|
||||
['WellScene', '古井空地', 14, 10, 0, [
|
||||
['spawn', 208, 80, { id: 'default' }], ['door', 208, 80, { target: 'PlazaScene', spawn: 'well' }], ['npc', 46, 116, { id: 'extra5', label: '村民' }], ['interact', 104, 72, { id: 'well', label: '古井' }],
|
||||
]],
|
||||
['MistScene', '雾墙边界', 20, 8, 0, [
|
||||
['spawn', 16, 64, { id: 'default' }], ['door', 0, 64, { target: 'PlazaScene', spawn: 'mist' }], ['interact', 288, 64, { id: 'mist', label: '雾墙' }],
|
||||
]],
|
||||
['WhiteScene', '白色层', 30, 8, 5, [
|
||||
['spawn', 24, 64, { id: 'default' }], ['interact', 400, 64, { id: 'record', label: '病历' }],
|
||||
]],
|
||||
];
|
||||
|
||||
function createTileData(key, width, height, ground) {
|
||||
const data = Array(width * height).fill(ground + 1);
|
||||
const set = (x, y, gid) => { if (x >= 0 && y >= 0 && x < width && y < height) data[y * width + x] = gid; };
|
||||
const rect = (x, y, w, h, gid) => { for (let yy = y; yy < y + h; yy += 1) for (let xx = x; xx < x + w; xx += 1) set(xx, yy, gid); };
|
||||
const building = (x, y, w, h) => {
|
||||
rect(x, y, w, 2, 9); rect(x, y + 2, w, h - 2, 4);
|
||||
set(x + Math.floor(w / 2), y + h - 1, 10);
|
||||
for (let xx = x + 1; xx < x + w - 1; xx += 3) set(xx, y + 3, 15);
|
||||
};
|
||||
|
||||
if (key === 'PlazaScene') {
|
||||
rect(13, 0, 4, height, 2); rect(0, 8, width, 4, 2);
|
||||
building(2, 1, 7, 6); building(21, 1, 7, 6); building(2, 14, 7, 5); building(21, 14, 7, 5);
|
||||
rect(10, 2, 3, 3, 12); set(15, 8, 11);
|
||||
set(5, 9, 17);
|
||||
[[12, 11], [13, 12], [14, 11], [16, 11], [17, 12], [18, 11]].forEach(([x, y]) => set(x, y, 8));
|
||||
[[1, 3], [9, 6], [19, 5], [28, 3], [4, 12], [25, 12], [10, 16], [19, 16]].forEach(([x, y]) => set(x, y, 13));
|
||||
} else if (key === 'HouseScene') {
|
||||
rect(0, 0, width, height, 4); rect(0, 0, width, 1, 5); rect(0, height - 1, width, 1, 5);
|
||||
rect(2, 2, 3, 2, 5); set(8, 2, 11); rect(6, 5, 2, 1, 8); set(6, 7, 10);
|
||||
} else if (key === 'BakeryScene') {
|
||||
rect(0, 0, width, height, 4); rect(1, 1, width - 2, 2, 5); set(11, 2, 13); rect(2, 5, 11, 1, 5); set(7, 9, 10);
|
||||
} else if (key === 'ShopScene') {
|
||||
rect(0, 0, width, height, 4); for (let x = 1; x < width - 1; x += 3) rect(x, 1, 2, 4, 5); set(7, 9, 10);
|
||||
for (let y = 2; y <= 4; y += 1) for (const x of [2, 5, 8]) set(x, y, 7);
|
||||
} else if (key === 'ClinicScene') {
|
||||
rect(0, 0, width, height, 6); rect(0, 0, width, 1, 16); rect(2, 2, 5, 2, 5); set(6, 7, 10); set(9, 2, 15);
|
||||
} else if (key === 'LakeScene') {
|
||||
rect(0, 0, 10, height, 3); rect(10, 0, width - 10, height, 1); rect(10, 7, width - 10, 3, 2);
|
||||
rect(11, 9, 3, 2, 5); rect(14, 9, 3, 2, 8); for (let y = 2; y < height - 2; y += 4) set(20, y, 12);
|
||||
} else if (key === 'FieldScene') {
|
||||
rect(0, 6, width, 3, 2);
|
||||
for (let y = 1; y < height - 1; y += 3) for (let x = 2; x < width - 1; x += 3) set(x, y, (x + y) % 2 ? 7 : 8);
|
||||
} else if (key === 'WellScene') {
|
||||
rect(3, 2, 8, 6, 2); rect(5, 3, 4, 4, 14); set(13, 5, 10);
|
||||
} else if (key === 'MistScene') {
|
||||
rect(0, 3, width - 3, 3, 2); rect(width - 4, 0, 4, height, 15);
|
||||
} else if (key === 'WhiteScene') {
|
||||
rect(0, 0, width, height, 6); rect(0, 0, width, 1, 16); rect(0, height - 1, width, 1, 16); rect(19, 1, 1, height - 2, 16); set(25, 4, 5);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
const collisionSpecs = {
|
||||
PlazaScene: [[32, 16, 112, 96], [336, 16, 112, 96], [32, 224, 112, 76], [336, 224, 112, 76], [160, 32, 48, 48], [232, 128, 32, 24]],
|
||||
BakeryScene: [[16, 16, 208, 32], [176, 32, 32, 30], [32, 80, 176, 12]],
|
||||
ShopScene: [[16, 16, 32, 64], [64, 16, 32, 64], [112, 16, 32, 64], [160, 16, 32, 64], [208, 16, 16, 64]],
|
||||
ClinicScene: [[32, 32, 80, 32]],
|
||||
LakeScene: [[0, 0, 150, 256]],
|
||||
WellScene: [[80, 48, 64, 64]],
|
||||
WhiteScene: [[304, 16, 16, 96]],
|
||||
};
|
||||
|
||||
const propertyXml = (properties) => Object.entries(properties).map(([name, value]) => ` <property name="${name}" value="${String(value).replaceAll('&', '&').replaceAll('"', '"')}"/>`).join('\n');
|
||||
|
||||
for (const [key, displayName, width, height, ground, objects] of maps) {
|
||||
const data = createTileData(key, width, height, ground);
|
||||
const borders = [
|
||||
`<object id="900" x="0" y="0" width="${width * 16}" height="4"><properties><property name="collides" type="bool" value="true"/></properties></object>`,
|
||||
`<object id="901" x="0" y="${height * 16 - 4}" width="${width * 16}" height="4"><properties><property name="collides" type="bool" value="true"/></properties></object>`,
|
||||
`<object id="902" x="0" y="0" width="4" height="${height * 16}"><properties><property name="collides" type="bool" value="true"/></properties></object>`,
|
||||
`<object id="903" x="${width * 16 - 4}" y="0" width="4" height="${height * 16}"><properties><property name="collides" type="bool" value="true"/></properties></object>`,
|
||||
];
|
||||
(collisionSpecs[key] ?? []).forEach(([x, y, w, h], index) => borders.push(`<object id="${910 + index}" x="${x}" y="${y}" width="${w}" height="${h}"><properties><property name="collides" type="bool" value="true"/></properties></object>`));
|
||||
const objectXml = objects.map(([type, x, y, properties], index) => ` <object id="${index + 1}" name="${properties.label ?? properties.id}" type="${type}" x="${x}" y="${y}" width="16" height="16">\n <properties>\n${propertyXml(properties)}\n </properties>\n </object>`).join('\n');
|
||||
const tmx = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.10" tiledversion="1.12.2" orientation="orthogonal" renderorder="right-down" width="${width}" height="${height}" tilewidth="16" tileheight="16" infinite="0" nextlayerid="4" nextobjectid="1000">
|
||||
<properties><property name="sceneKey" value="${key}"/><property name="displayName" value="${displayName}"/></properties>
|
||||
<tileset firstgid="1" name="honey-village" tilewidth="16" tileheight="16" tilecount="64" columns="16"><image source="../../assets/tileset.png" width="256" height="64"/></tileset>
|
||||
<layer id="1" name="ground" width="${width}" height="${height}"><data encoding="csv">${data.join(',')}</data></layer>
|
||||
<objectgroup id="2" name="obstacles">${borders.join('\n')}</objectgroup>
|
||||
<objectgroup id="3" name="objects">\n${objectXml}\n </objectgroup>
|
||||
</map>`;
|
||||
await writeFile(resolve(sourceDir, `${key}.tmx`), tmx);
|
||||
}
|
||||
|
||||
await writeFile(resolve(sourceDir, 'tileset.tsx'), `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tileset version="1.10" tiledversion="1.12.2" name="honey-village" tilewidth="16" tileheight="16" tilecount="64" columns="16">
|
||||
<image source="../../assets/tileset.png" width="256" height="64"/>
|
||||
</tileset>`);
|
||||
|
||||
console.log(`Generated ${maps.length} original Tiled source maps in ${sourceDir}`);
|
||||
Reference in New Issue
Block a user