Honey Village: six model builds + hub frontend, Dockerized for Dokploy

This commit is contained in:
2026-07-15 10:50:12 -04:00
commit 67253bded5
523 changed files with 58879 additions and 0 deletions
+474
View File
@@ -0,0 +1,474 @@
// genmaps.js — 生成《崩坏童话:蜜糖村》全部 10 张 Tiled 地图 (.tmx)
// 用法: /Applications/Tiled.app/Contents/MacOS/Tiled -e tools/tiled/genmaps.js
// 说明: -e 模式无编辑器,tiled.open/TileMap 绘制 API 不可用,故用 TextFile 直接写 .tmx XML。
// 导出: Tiled --export-map --embed-tilesets in.tmx out.json (必须 --embed-tilesetsJSON 才有 image 字段)
//
// 约定(与 CONTRACT.md §3 一致):
// - 固定三层:ground(瓦片层)/ obstacles(瓦片层,图层属性 collides=true/ objects(点对象层)
// - 瓦片 gid = CONTRACT §2 瓦片 id + 1obstacles 中空格 = gid 0
// - 点对象坐标 = 所在瓦片中心像素 (tx*16+8, ty*16+8)
// - door 属性 target=目标地图 key, spawn=目标地图中的 spawn id= "from_"+本图 key),双向成对
var OUT = '/Users/leidianyayi/Documents/GitHub/ModelTest20260714/Kimi-K3/public/assets/maps/';
// ---- 瓦片 gid 表(id+1----
var G = {
grass:1, flowerA:2, flowerB:3, deepGrass:4, dirt:5, stone:6, stoneAlt:7, brick:8,
waterA:9, waterB:10, shoreN:11, shoreS:12, shoreW:13, shoreE:14,
bedPink:15, bedYellow:16, berry:17, berryEmpty:18,
treeTL:19, treeTR:20, treeBL:21, treeBR:22, bush:23, stump:24, rock:25, sign:26,
fenceH:27, fenceV:28, lamp:29, ribbonR:30, ribbonY:31, ribbonB:32,
creamWall:33, creamWallWin:34, woodWall:35, roofOL:36, roofOM:37, roofOR:38,
doorT:39, doorTop:40, window:41, chimney:42, wellTL:43, wellTR:44, wellBL:45, wellBR:46,
mailbox:47, board:48, floorA:49, floorB:50, wallIn:51, wallInWin:52,
bedTL:53, bedTR:54, bedBL:55, bedBR:56, counterL:57, counterR:58, oven:59,
shelfE:60, shelfF:61, table:62, chair:63, rug:64, calendar:65, recipe:66, fileP:67,
picTL:68, picTR:69, picBL:70, picBR:71, benchL:72, benchR:73, stool:74, cabinet:75,
fridge:76, sink:77, plant:78, barrel:79, crate:80, fogA:81, fogB:82, fogC:83,
wFloor:84, wWall:85, wPanel:86, wDoor:87, wBedTL:88, wBedTR:89, wBedBL:90, wBedBR:91,
wCab:92, wMon:93, wCur:94, wFloor2:95, wLamp:96, ladder:97, bridgeH:98, bridgeV:99,
mushroom:100, flR:101, flY:102, flB:103, honey:104, bread:105, letter:106, book:107,
candle:108, basket:109, bucket:110, broom:111, cake:112, roofRL:113, roofRM:114, roofRR:115,
roofBL:116, roofBM:117, roofBR:118, pinkWall:119, pinkWallWin:120, stoneWall:121,
stoneWallWin:122, awning:123, clock:124, flag:125, garC:126, garM:127, empty:128
};
// ---- 基础工具(ES5----
function grid(w, h, v) {
var a = [];
for (var y = 0; y < h; y++) {
var r = [];
for (var x = 0; x < w; x++) r.push(v);
a.push(r);
}
return a;
}
function newMap(w, h) { return { w: w, h: h, ground: grid(w, h, 0), obs: grid(w, h, 0), objs: [] }; }
function fill(l, x, y, w, h, g) {
for (var j = y; j < y + h; j++)
for (var i = x; i < x + w; i++) l[j][i] = g;
}
function put(l, x, y, g) { l[y][x] = g; }
// 确定性散点随机数(固定种子,结果可复现)
function rnd(x, y, s) {
var n = Math.sin(x * 127.1 + y * 311.7 + s * 74.7) * 43758.5453;
return n - Math.floor(n);
}
function obj(m, name, tx, ty, props) {
m.objs.push({ name: name, x: tx * 16 + 8, y: ty * 16 + 8, props: props });
}
function spawn(m, tx, ty, id) { obj(m, 'spawn', tx, ty, [['id', id]]); }
function door(m, tx, ty, target, sp) { obj(m, 'door', tx, ty, [['target', target], ['spawn', sp]]); }
function npc(m, tx, ty, id) { obj(m, 'npc', tx, ty, [['id', id]]); }
function inter(m, tx, ty, id) { obj(m, 'interact', tx, ty, [['id', id]]); }
function tree2x2(m, x, y) {
put(m.obs, x, y, G.treeTL); put(m.obs, x + 1, y, G.treeTR);
put(m.obs, x, y + 1, G.treeBL); put(m.obs, x + 1, y + 1, G.treeBR);
}
// 草地散点装饰(ground 层,无碰撞)
function scatter(m, seed, d) {
for (var y = 0; y < m.h; y++) {
for (var x = 0; x < m.w; x++) {
var r = rnd(x, y, seed);
var g = 0;
if (r < d[0]) g = G.flowerA;
else if (r < d[1]) g = G.flowerB;
else if (r < d[2]) g = G.deepGrass;
else if (r < d[3]) g = [G.flR, G.flY, G.flB][(x + y) % 3];
if (g) m.ground[y][x] = g;
}
}
}
// 室外房屋:5 宽 × 4 高,朝南单门,整体进 obstacles
function houseS(m, x, y, roofL, roofM, roofR, chimney) {
for (var i = 0; i < 5; i++) {
var t = (i === 0) ? roofL : (i === 4 ? roofR : roofM);
put(m.obs, x + i, y, t);
put(m.obs, x + i, y + 1, t);
}
if (chimney) put(m.obs, x + 1, y, G.chimney);
put(m.obs, x, y + 2, G.creamWall);
put(m.obs, x + 1, y + 2, G.creamWallWin);
put(m.obs, x + 2, y + 2, G.doorTop);
put(m.obs, x + 3, y + 2, G.creamWallWin);
put(m.obs, x + 4, y + 2, G.creamWall);
put(m.obs, x, y + 3, G.creamWall);
put(m.obs, x + 1, y + 3, G.creamWall);
put(m.obs, x + 2, y + 3, G.doorT);
put(m.obs, x + 3, y + 3, G.creamWall);
put(m.obs, x + 4, y + 3, G.creamWall);
}
// 室内外壳:木地板满铺(ground),墙围边(obstacles),底边留门(门瓦片在 ground)
function indoorShell(m, wins, doorX) {
for (var y = 0; y < m.h; y++)
for (var x = 0; x < m.w; x++)
m.ground[y][x] = ((x + y) % 2 === 0) ? G.floorA : G.floorB;
m.ground[m.h - 1][doorX] = G.doorT;
for (var x2 = 0; x2 < m.w; x2++) {
m.obs[0][x2] = G.wallIn;
if (x2 !== doorX) m.obs[m.h - 1][x2] = G.wallIn;
}
for (var i = 0; i < wins.length; i++) m.obs[0][wins[i]] = G.wallInWin;
for (var y2 = 1; y2 < m.h - 1; y2++) {
m.obs[y2][0] = G.wallIn;
m.obs[y2][m.w - 1] = G.wallIn;
}
}
// ================= 1. house 12×8 小满小屋 =================
function buildHouse() {
var m = newMap(12, 8);
indoorShell(m, [3, 8], 5);
// 床(睡觉过日)+ 床头日历(C1)
put(m.obs, 1, 1, G.bedTL); put(m.obs, 2, 1, G.bedTR);
put(m.obs, 1, 2, G.bedBL); put(m.obs, 2, 2, G.bedBR);
put(m.obs, 3, 1, G.calendar);
// 桌椅 + 盆栽 + 地毯
put(m.obs, 8, 4, G.table); put(m.obs, 7, 4, G.chair); put(m.obs, 9, 4, G.chair);
put(m.obs, 10, 1, G.plant);
put(m.ground, 5, 3, G.rug); put(m.ground, 6, 3, G.rug);
put(m.ground, 5, 4, G.rug); put(m.ground, 6, 4, G.rug);
spawn(m, 3, 3, 'default');
spawn(m, 5, 5, 'from_plaza');
door(m, 5, 6, 'plaza', 'from_house');
inter(m, 1, 3, 'bed');
inter(m, 3, 2, 'calendar');
return m;
}
// ================= 2. plaza 30×20 村广场(枢纽) =================
function buildPlaza() {
var m = newMap(30, 20);
fill(m.ground, 0, 0, 30, 20, G.grass);
scatter(m, 7, [0.05, 0.10, 0.15, 0.18]);
// 石路:南北 x=14/15,东西 y=9/10,通往四座房屋的小路
var y, x;
for (y = 0; y < 20; y++) {
put(m.ground, 14, y, (y % 3 === 0) ? G.stoneAlt : G.stone);
put(m.ground, 15, y, (y % 3 === 0) ? G.stoneAlt : G.stone);
}
for (x = 0; x < 30; x++) {
put(m.ground, x, 9, (x % 3 === 0) ? G.stoneAlt : G.stone);
put(m.ground, x, 10, (x % 3 === 0) ? G.stoneAlt : G.stone);
}
var stubs = [4, 11, 18, 25];
for (var s = 0; s < stubs.length; s++)
for (y = 5; y <= 9; y++) put(m.ground, stubs[s], y, G.stone);
// 中央广场砖(庆典场地核心)
fill(m.ground, 11, 8, 8, 5, G.brick);
// 庆典彩带(广场四角,装饰无碰撞;透明瓦片须放 obstacles 层,ground 层会露黑底)
put(m.obs, 11, 7, G.ribbonR); put(m.obs, 18, 7, G.ribbonY);
put(m.obs, 11, 13, G.ribbonB); put(m.obs, 18, 13, G.ribbonR);
// 四周房屋:小屋/杂货店=橙屋顶,面包房/诊所=红屋顶
houseS(m, 2, 1, G.roofOL, G.roofOM, G.roofOR, true); // house 门 x=4
houseS(m, 9, 1, G.roofRL, G.roofRM, G.roofRR, true); // bakery 门 x=11
houseS(m, 16, 1, G.roofOL, G.roofOM, G.roofOR, true); // shop 门 x=18
houseS(m, 23, 1, G.roofRL, G.roofRM, G.roofRR, false); // clinic 门 x=25
// 公告板 + 三个花坛 + 双子鸟大树
put(m.obs, 14, 8, G.board);
put(m.obs, 10, 8, G.bedPink); put(m.obs, 19, 8, G.bedYellow); put(m.obs, 14, 13, G.bedPink);
tree2x2(m, 12, 14);
// 村口邮筒(大宝)+ 路灯 + 装饰树/灌木
put(m.obs, 2, 8, G.mailbox);
put(m.obs, 10, 6, G.lamp); put(m.obs, 19, 6, G.lamp);
tree2x2(m, 1, 6); tree2x2(m, 27, 14); tree2x2(m, 1, 16);
put(m.obs, 8, 16, G.bush); put(m.obs, 21, 17, G.bush); put(m.obs, 16, 15, G.bush);
// 地图边缘装饰(留出 4 个路口)
function skip(x2, y2) {
return (y2 === 0 && (x2 === 14 || x2 === 15)) || (y2 === 19 && (x2 === 14 || x2 === 15)) ||
(x2 === 0 && (y2 === 9 || y2 === 10)) || (x2 === 29 && (y2 === 9 || y2 === 10));
}
function edge(x2, y2) {
var r = rnd(x2, y2, 9);
put(m.obs, x2, y2, r < 0.5 ? G.bush : (r < 0.75 ? G.rock : G.stump));
}
for (x = 0; x < 30; x++) { if (!skip(x, 0)) edge(x, 0); if (!skip(x, 19)) edge(x, 19); }
for (y = 1; y < 19; y++) { if (!skip(0, y)) edge(0, y); if (!skip(29, y)) edge(29, y); }
// 对象
spawn(m, 14, 11, 'default');
spawn(m, 4, 7, 'from_house'); // 落点距门 24px,防回弹
spawn(m, 11, 7, 'from_bakery');
spawn(m, 18, 7, 'from_shop');
spawn(m, 25, 7, 'from_clinic');
spawn(m, 1, 10, 'from_lake');
spawn(m, 28, 10, 'from_field');
spawn(m, 14, 2, 'from_well'); // 落点距门 24px,防回弹
spawn(m, 15, 18, 'from_fog');
door(m, 4, 5, 'house', 'from_plaza');
door(m, 11, 5, 'bakery', 'from_plaza');
door(m, 18, 5, 'shop', 'from_plaza');
door(m, 25, 5, 'clinic', 'from_plaza');
door(m, 0, 9, 'lake', 'from_plaza');
door(m, 29, 10, 'field', 'from_plaza');
door(m, 14, 0, 'well', 'from_plaza');
door(m, 15, 19, 'fog', 'from_plaza');
npc(m, 15, 8, 'afu'); // 公告板旁
npc(m, 12, 14, 'bird'); // 双子鸟,树上
npc(m, 7, 12, 'squirrel');
npc(m, 22, 14, 'fox');
npc(m, 3, 8, 'dabao'); // 村口邮筒旁(PRD §4
inter(m, 14, 9, 'noticeboard');
inter(m, 10, 9, 'flowerbed_1');
inter(m, 19, 9, 'flowerbed_2');
inter(m, 14, 14, 'flowerbed_3');
return m;
}
// ================= 3. bakery 15×10 面包房 =================
function buildBakery() {
var m = newMap(15, 10);
indoorShell(m, [3, 7, 11], 7);
fill(m.ground, 5, 5, 3, 2, G.rug);
// 两段柜台 + 烤炉(C2 配方纸在桌上)
put(m.obs, 4, 3, G.counterL); put(m.obs, 5, 3, G.counterR);
put(m.obs, 8, 3, G.counterL); put(m.obs, 9, 3, G.counterR);
put(m.obs, 12, 1, G.oven);
put(m.obs, 10, 6, G.table); put(m.obs, 11, 6, G.recipe);
put(m.obs, 13, 8, G.barrel); put(m.obs, 1, 8, G.plant);
spawn(m, 7, 6, 'default');
spawn(m, 7, 7, 'from_plaza');
door(m, 7, 8, 'plaza', 'from_bakery');
npc(m, 5, 2, 'yuanyuan'); // 柜台后
inter(m, 4, 4, 'counter_bread');
inter(m, 11, 7, 'recipe_paper');
return m;
}
// ================= 4. shop 15×10 杂货店 =================
function buildShop() {
var m = newMap(15, 10);
indoorShell(m, [3, 7, 11], 7);
fill(m.ground, 6, 6, 3, 2, G.rug);
// 上排 9 格满货架(C5:同一商品重复 9 格)+ 下排空满混排
var x;
for (x = 3; x <= 11; x++) put(m.obs, x, 1, G.shelfF);
for (x = 3; x <= 11; x++) put(m.obs, x, 4, (x % 3 === 0) ? G.shelfE : G.shelfF);
put(m.obs, 1, 7, G.crate); put(m.obs, 1, 8, G.barrel); put(m.obs, 13, 1, G.plant);
spawn(m, 7, 6, 'default');
spawn(m, 7, 7, 'from_plaza');
door(m, 7, 8, 'plaza', 'from_shop');
npc(m, 2, 5, 'mimi');
npc(m, 11, 7, 'hedgehog');
inter(m, 7, 2, 'shelf_count');
return m;
}
// ================= 5. clinic 12×8 诊所 =================
function buildClinic() {
var m = newMap(12, 8);
indoorShell(m, [3, 8], 5);
// 芙医生的桌 + 桌上文件(C8)
put(m.obs, 8, 2, G.table); put(m.obs, 9, 2, G.fileP);
put(m.obs, 9, 3, G.chair);
put(m.obs, 1, 1, G.cabinet); put(m.obs, 10, 6, G.plant);
spawn(m, 6, 5, 'default');
spawn(m, 5, 5, 'from_plaza');
door(m, 5, 6, 'plaza', 'from_clinic');
npc(m, 8, 1, 'fuyisheng'); // 桌后
inter(m, 8, 3, 'doctor_desk');
return m;
}
// ================= 6. lake 24×16 湖畔 =================
function buildLake() {
var m = newMap(24, 16);
fill(m.ground, 0, 0, 24, 16, G.grass);
scatter(m, 21, [0.06, 0.11, 0.15, 0.18]);
var x, y;
// 石路:东门(23,10)通向长椅与野餐区
for (x = 3; x <= 23; x++) put(m.ground, x, 10, (x % 3 === 0) ? G.stoneAlt : G.stone);
// 野餐布(T7,走上去触发,放 ground 可行走)
put(m.ground, 15, 10, G.picTL); put(m.ground, 16, 10, G.picTR);
put(m.ground, 15, 11, G.picBL); put(m.ground, 16, 11, G.picBR);
put(m.ground, 3, 12, G.mushroom);
// 湖水:北半边大片湖,水岸瓦片围边(全部进 obstacles)
for (y = 0; y <= 5; y++)
for (x = 2; x <= 21; x++)
put(m.obs, x, y, ((x + y) % 2 === 0) ? G.waterA : G.waterB);
for (x = 1; x <= 22; x++) put(m.obs, x, 6, G.shoreS);
for (y = 0; y <= 5; y++) { put(m.obs, 1, y, G.shoreW); put(m.obs, 22, y, G.shoreE); }
// 长椅(龟爷爷)+ 树木花草装饰
put(m.obs, 10, 8, G.benchL); put(m.obs, 11, 8, G.benchR);
tree2x2(m, 1, 13); tree2x2(m, 20, 12);
put(m.obs, 8, 14, G.bush); put(m.obs, 12, 13, G.bush);
put(m.obs, 5, 9, G.rock); put(m.obs, 14, 14, G.stump);
spawn(m, 21, 11, 'default');
spawn(m, 22, 10, 'from_plaza');
door(m, 23, 10, 'plaza', 'from_lake');
npc(m, 12, 8, 'guiyeye'); // 长椅旁
npc(m, 6, 12, 'pig');
inter(m, 16, 11, 'picnic_mat');
return m;
}
// ================= 7. field 20×14 花田 =================
function buildField() {
var m = newMap(20, 14);
fill(m.ground, 0, 0, 20, 14, G.grass);
scatter(m, 33, [0.10, 0.18, 0.24, 0.32]);
var x, y;
// 土路横穿(西门进出)
for (x = 0; x <= 19; x++) put(m.ground, x, 7, G.dirt);
// 浇花点花丛(T2/T12 用,ground 装饰)
put(m.ground, 15, 9, G.flR); put(m.ground, 16, 9, G.flY); put(m.ground, 17, 9, G.flB);
put(m.ground, 15, 10, G.flY); put(m.ground, 16, 10, G.flR); put(m.ground, 17, 10, G.flB);
// 栅栏围边,西门留口
for (x = 0; x <= 19; x++) { put(m.obs, x, 0, G.fenceH); put(m.obs, x, 13, G.fenceH); }
for (y = 1; y <= 12; y++) { if (y !== 7) put(m.obs, 0, y, G.fenceV); put(m.obs, 19, y, G.fenceV); }
// 5 丛莓果(T6
var berries = [[4, 4], [9, 4], [14, 4], [6, 9], [12, 9]];
for (var b = 0; b < berries.length; b++) put(m.obs, berries[b][0], berries[b][1], G.berry);
// 树木装饰
tree2x2(m, 16, 1);
put(m.obs, 2, 11, G.bush); put(m.obs, 10, 11, G.bush); put(m.obs, 2, 2, G.rock);
spawn(m, 2, 7, 'default');
spawn(m, 1, 7, 'from_plaza');
door(m, 0, 7, 'plaza', 'from_field');
npc(m, 10, 7, 'chick');
inter(m, 4, 5, 'berry_1');
inter(m, 9, 5, 'berry_2');
inter(m, 14, 5, 'berry_3');
inter(m, 6, 10, 'berry_4');
inter(m, 12, 10, 'berry_5');
inter(m, 16, 10, 'water_point');
return m;
}
// ================= 8. well 14×10 古井空地 =================
function buildWell() {
var m = newMap(14, 10);
fill(m.ground, 0, 0, 14, 10, G.grass);
scatter(m, 45, [0.07, 0.13, 0.17, 0.19]);
var y;
// 土场 + 南门土路
fill(m.ground, 5, 3, 4, 4, G.dirt);
for (y = 7; y <= 9; y++) { put(m.ground, 6, y, G.dirt); put(m.ground, 7, y, G.dirt); }
put(m.ground, 3, 4, G.mushroom); put(m.ground, 10, 6, G.mushroom);
// 中央古井(C7
put(m.obs, 6, 4, G.wellTL); put(m.obs, 7, 4, G.wellTR);
put(m.obs, 6, 5, G.wellBL); put(m.obs, 7, 5, G.wellBR);
// 四周树木
tree2x2(m, 1, 1); tree2x2(m, 11, 1); tree2x2(m, 1, 7); tree2x2(m, 11, 7);
put(m.obs, 4, 2, G.bush); put(m.obs, 9, 2, G.bush);
put(m.obs, 4, 8, G.bush); put(m.obs, 9, 8, G.bush);
put(m.obs, 4, 5, G.rock); put(m.obs, 9, 5, G.rock);
spawn(m, 6, 8, 'default');
spawn(m, 7, 8, 'from_plaza');
door(m, 7, 9, 'plaza', 'from_well');
inter(m, 6, 6, 'ancient_well');
return m;
}
// ================= 9. fog 20×8 雾墙边界 =================
function buildFog() {
var m = newMap(20, 8);
fill(m.ground, 0, 0, 20, 8, G.grass);
scatter(m, 57, [0.05, 0.09, 0.20, 0.20]);
var x, y;
// 雾前深草带 + 北门土路
for (x = 0; x <= 19; x++) put(m.ground, x, 4, G.deepGrass);
for (y = 0; y <= 4; y++) { put(m.ground, 9, y, G.dirt); put(m.ground, 10, y, G.dirt); }
// 远端(南侧)雾墙铺满,进 obstacles(E3 入口)
for (y = 5; y <= 7; y++)
for (x = 0; x <= 19; x++)
put(m.obs, x, y, [G.fogA, G.fogB, G.fogC][(x + y) % 3]);
// 两侧树木
tree2x2(m, 1, 1); tree2x2(m, 17, 1);
put(m.obs, 4, 3, G.bush); put(m.obs, 15, 3, G.bush);
put(m.obs, 0, 3, G.bush); put(m.obs, 19, 3, G.bush);
put(m.obs, 6, 2, G.rock);
spawn(m, 10, 2, 'default');
spawn(m, 9, 2, 'from_plaza'); // 落点距门 >24px,防回弹
door(m, 10, 0, 'plaza', 'from_fog');
inter(m, 10, 4, 'fog_wall'); // 雾前可达格
return m;
}
// ================= 10. white 20×14 白色层(走廊 + 病房) =================
function buildWhite() {
var m = newMap(20, 14);
var x, y;
// 默认白墙铺底;上半 20×6 走廊 + 左下 10×8 病房刷白地板
fill(m.ground, 0, 0, 20, 14, G.wWall);
for (y = 1; y <= 5; y++)
for (x = 1; x <= 18; x++)
m.ground[y][x] = (x % 4 === 0 && y % 2 === 0) ? G.wFloor2 : G.wFloor;
for (y = 7; y <= 12; y++)
for (x = 1; x <= 8; x++)
m.ground[y][x] = ((x + y) % 3 === 0) ? G.wFloor2 : G.wFloor;
put(m.ground, 4, 6, G.wFloor); put(m.ground, 5, 6, G.wFloor); // 走廊↔病房通道格
// 墙体(obstacles):北墙(含墙板/天花板灯)、东西墙、南墙、右下实心墙块、病房北/东墙
for (x = 0; x <= 19; x++) put(m.obs, x, 0, G.wWall);
put(m.obs, 5, 0, G.wPanel); put(m.obs, 10, 0, G.wPanel); put(m.obs, 15, 0, G.wPanel);
put(m.obs, 3, 0, G.wLamp); put(m.obs, 8, 0, G.wLamp);
put(m.obs, 13, 0, G.wLamp); put(m.obs, 18, 0, G.wLamp);
for (y = 1; y <= 13; y++) { put(m.obs, 0, y, G.wWall); put(m.obs, 19, y, G.wWall); }
for (x = 1; x <= 9; x++) put(m.obs, x, 13, G.wWall);
fill(m.obs, 10, 6, 10, 8, G.wWall); // 右下 10×8 实心(房间之外)
for (x = 1; x <= 9; x++) if (x !== 4 && x !== 5) put(m.obs, x, 6, G.wWall);
for (y = 6; y <= 13; y++) put(m.obs, 9, y, G.wWall);
put(m.obs, 9, 8, G.wCur); put(m.obs, 9, 9, G.wCur); // 白窗帘挂在东墙上
// 病房家具:白床 + 白监护仪 + 白柜
put(m.obs, 2, 8, G.wBedTL); put(m.obs, 3, 8, G.wBedTR);
put(m.obs, 2, 9, G.wBedBL); put(m.obs, 3, 9, G.wBedBR);
put(m.obs, 4, 8, G.wMon);
put(m.obs, 7, 12, G.wCab);
spawn(m, 17, 3, 'default'); // 走廊一端
return m;
}
// ================= 序列化 =================
function csv(l, w, h) {
var s = '';
for (var y = 0; y < h; y++) {
var row = '';
for (var x = 0; x < w; x++) {
row += l[y][x];
if (x < w - 1) row += ',';
}
s += row;
if (y < h - 1) s += ',\n';
}
return s;
}
function tmx(m) {
var s = '<?xml version="1.0" encoding="UTF-8"?>\n';
s += '<map version="1.10" tiledversion="1.12.2" orientation="orthogonal" renderorder="right-down"' +
' width="' + m.w + '" height="' + m.h + '" tilewidth="16" tileheight="16" infinite="0"' +
' nextlayerid="4" nextobjectid="' + (m.objs.length + 1) + '">\n';
s += ' <tileset firstgid="1" source="tileset.tsx"/>\n';
s += ' <layer id="1" name="ground" width="' + m.w + '" height="' + m.h + '">\n';
s += ' <data encoding="csv">\n' + csv(m.ground, m.w, m.h) + '\n </data>\n </layer>\n';
s += ' <layer id="2" name="obstacles" width="' + m.w + '" height="' + m.h + '">\n';
s += ' <properties>\n <property name="collides" type="bool" value="true"/>\n </properties>\n';
s += ' <data encoding="csv">\n' + csv(m.obs, m.w, m.h) + '\n </data>\n </layer>\n';
s += ' <objectgroup id="3" name="objects">\n';
for (var i = 0; i < m.objs.length; i++) {
var o = m.objs[i];
s += ' <object id="' + (i + 1) + '" name="' + o.name + '" x="' + o.x + '" y="' + o.y + '">\n';
s += ' <properties>\n';
for (var p = 0; p < o.props.length; p++)
s += ' <property name="' + o.props[p][0] + '" value="' + o.props[p][1] + '"/>\n';
s += ' </properties>\n <point/>\n </object>\n';
}
s += ' </objectgroup>\n</map>\n';
return s;
}
function writeMap(name, m) {
var f = new TextFile(OUT + name + '.tmx', TextFile.WriteOnly);
f.write(tmx(m));
f.commit();
tiled.log('wrote ' + name + '.tmx (' + m.w + 'x' + m.h + ', ' + m.objs.length + ' objects)');
}
writeMap('house', buildHouse());
writeMap('plaza', buildPlaza());
writeMap('bakery', buildBakery());
writeMap('shop', buildShop());
writeMap('clinic', buildClinic());
writeMap('lake', buildLake());
writeMap('field', buildField());
writeMap('well', buildWell());
writeMap('fog', buildFog());
writeMap('white', buildWhite());
tiled.log('all 10 maps done');