157 lines
8.2 KiB
JavaScript
157 lines
8.2 KiB
JavaScript
(function (root) {
|
|
"use strict";
|
|
|
|
const Data = root.PrincessLimaData;
|
|
const TILE = 32;
|
|
const REGION_ROW = Object.freeze({
|
|
village: 0, forest: 1, ruins: 2, mountain: 3, camp: 4,
|
|
fortressExterior: 5, fortressInterior: 6, bossArena: 7, chamber: 8
|
|
});
|
|
|
|
const ART = Object.freeze({
|
|
village: {
|
|
paths: [[1, 10, 38, 4], [17, 7, 6, 15], [3, 16, 34, 3]],
|
|
decor: [["garden", 1120, 575], ["cart", 955, 350], ["smoke", 180, 62], ["forge", 520, 250], ["flowers", 785, 610]]
|
|
},
|
|
forest: {
|
|
paths: [[1, 9, 37, 4], [8, 4, 4, 16], [16, 8, 4, 14], [24, 3, 4, 16], [31, 2, 5, 19]],
|
|
decor: [["stream", 1040, 300], ["bridge", 1030, 375], ["log", 760, 500], ["flowers", 520, 110], ["mushrooms", 1010, 610]]
|
|
},
|
|
ruins: {
|
|
paths: [[3, 16, 33, 4], [15, 4, 5, 13], [25, 4, 5, 13]],
|
|
decor: [["mosaic", 575, 350], ["statue", 230, 190], ["statue", 930, 190], ["arch", 570, 115], ["rubble", 1080, 520]]
|
|
},
|
|
mountain: {
|
|
paths: [[1, 16, 38, 4], [11, 5, 5, 13], [23, 5, 5, 13], [31, 10, 6, 9]],
|
|
decor: [["ice", 560, 580], ["pine", 110, 160], ["pine", 1140, 150], ["icicles", 630, 70], ["cave", 1080, 335]]
|
|
},
|
|
camp: {
|
|
paths: [[1, 16, 38, 4], [17, 5, 6, 14], [5, 10, 30, 4]],
|
|
decor: [["fire", 640, 345], ["crates", 1120, 570], ["training", 210, 385], ["lookout", 1120, 150], ["banner", 640, 55]]
|
|
},
|
|
fortressExterior: {
|
|
paths: [[1, 17, 38, 4], [18, 4, 5, 17], [10, 9, 20, 5]],
|
|
decor: [["moat", 640, 625], ["stairs", 640, 465], ["banner", 640, 120], ["siege", 220, 620], ["siege", 1060, 620]]
|
|
},
|
|
fortressInterior: {
|
|
paths: [[18, 2, 5, 20], [5, 14, 30, 5], [7, 5, 27, 5]],
|
|
decor: [["carpet", 640, 360], ["torch", 225, 230], ["torch", 1055, 230], ["crates", 1000, 610], ["banner", 640, 80]]
|
|
},
|
|
bossArena: {
|
|
paths: [[5, 3, 30, 18]],
|
|
decor: [["throne", 640, 90], ["rune", 640, 360], ["brazier", 330, 155], ["brazier", 950, 155], ["chains", 640, 45]]
|
|
},
|
|
chamber: {
|
|
paths: [[4, 3, 32, 18]],
|
|
decor: [["carpet", 640, 410], ["bed", 1000, 230], ["flowers", 220, 210], ["fountain", 300, 560], ["table", 985, 515]]
|
|
}
|
|
});
|
|
|
|
function tileIndex(regionId, kind) {
|
|
return REGION_ROW[regionId] * 4 + kind;
|
|
}
|
|
|
|
function createTilemap(scene, regionId) {
|
|
const config = ART[regionId];
|
|
const rows = Math.ceil(Data.HEIGHT / TILE);
|
|
const columns = Math.ceil(Data.WIDTH / TILE);
|
|
const base = tileIndex(regionId, 0);
|
|
const detail = tileIndex(regionId, 1);
|
|
const path = tileIndex(regionId, 2);
|
|
const data = Array.from({ length: rows }, (_, y) =>
|
|
Array.from({ length: columns }, (_, x) => ((x * 7 + y * 11) % 13 === 0 ? detail : base)));
|
|
config.paths.forEach(([left, top, width, height]) => {
|
|
for (let y = top; y < Math.min(rows, top + height); y += 1) {
|
|
for (let x = left; x < Math.min(columns, left + width); x += 1) data[y][x] = path;
|
|
}
|
|
});
|
|
const map = scene.make.tilemap({ data, tileWidth: TILE, tileHeight: TILE });
|
|
const tiles = map.addTilesetImage("lima-world-tiles", "lima-world-tiles", TILE, TILE, 0, 0);
|
|
const layer = map.createLayer(0, tiles, 0, 0).setDepth(0);
|
|
return { map, layer };
|
|
}
|
|
|
|
function drawObstacle(scene, regionId, shape) {
|
|
const frame = tileIndex(regionId, shape.kind === "water" || shape.kind === "chasm" ? 3 : 1);
|
|
if (shape.shape === "circle") {
|
|
const color = Phaser.Display.Color.HexStringToColor(Data.MAPS[regionId].palette[2]).color;
|
|
const edge = Phaser.Display.Color.HexStringToColor(Data.MAPS[regionId].palette[3]).color;
|
|
const object = scene.add.circle(shape.x, shape.y, shape.radius, color, 0.98).setStrokeStyle(5, edge).setDepth(8);
|
|
scene.add.circle(shape.x - shape.radius * 0.22, shape.y - shape.radius * 0.28, Math.max(4, shape.radius * 0.18), 0xffffff, 0.18).setDepth(9);
|
|
return object;
|
|
}
|
|
const object = scene.add.tileSprite(
|
|
shape.x + shape.width / 2, shape.y + shape.height / 2,
|
|
shape.width, shape.height, "lima-world-tiles", frame
|
|
).setDepth(8);
|
|
const graphics = scene.add.graphics().setDepth(9);
|
|
const outline = Phaser.Display.Color.HexStringToColor(Data.MAPS[regionId].palette[3]).color;
|
|
graphics.lineStyle(3, outline, 0.95).strokeRect(shape.x, shape.y, shape.width, shape.height);
|
|
addSilhouette(scene, shape);
|
|
return object;
|
|
}
|
|
|
|
function addSilhouette(scene, shape) {
|
|
const x = shape.x;
|
|
const y = shape.y;
|
|
const width = shape.width;
|
|
const height = shape.height;
|
|
const ink = 0x0b0d13;
|
|
if (shape.kind === "house" || shape.kind === "tent") {
|
|
scene.add.triangle(x + width / 2, y + 6, 4, Math.min(50, height * 0.35), width / 2, 0, width - 4, Math.min(50, height * 0.35), 0x3e2430, 0.92).setDepth(10);
|
|
scene.add.rectangle(x + width / 2, y + height - 14, Math.min(34, width * 0.2), 28, ink, 0.85).setDepth(10);
|
|
} else if (shape.kind === "trees") {
|
|
for (let py = y + 18; py < y + height; py += 44) {
|
|
scene.add.circle(x + width / 2 + ((py / 44) % 2 ? -12 : 12), py, 22, 0x173f2b, 0.96).setStrokeStyle(2, 0x82a957).setDepth(10);
|
|
}
|
|
} else if (/wall|cell|fence|barricade|cage/.test(shape.kind || "")) {
|
|
for (let px = x + 8; px < x + width; px += 18) scene.add.rectangle(px, y + height / 2, 5, height - 5, ink, 0.32).setDepth(10);
|
|
} else if (shape.kind === "cliff") {
|
|
for (let py = y + 14; py < y + height; py += 34) scene.add.triangle(x + width - 12, py, 0, 0, 22, 8, 4, 22, 0xdce6ee, 0.5).setDepth(10);
|
|
}
|
|
}
|
|
|
|
function drawDecor(scene, regionId) {
|
|
ART[regionId].decor.forEach(([type, x, y], index) => {
|
|
const depth = y + 5;
|
|
if (["flowers", "mushrooms"].includes(type)) {
|
|
const colors = type === "flowers" ? [0xffd26b, 0xd6759e, 0xf6f1cf] : [0xd95c69, 0xf0c66c];
|
|
colors.forEach((color, offset) => scene.add.circle(x + offset * 10, y + (offset % 2) * 5, 4, color).setDepth(depth));
|
|
} else if (["fire", "torch", "brazier"].includes(type)) {
|
|
scene.add.circle(x, y, type === "fire" ? 18 : 10, 0xff9d3d, 0.92).setStrokeStyle(3, 0xffe18b).setDepth(depth);
|
|
scene.add.rectangle(x, y + 15, type === "fire" ? 34 : 8, 7, 0x5b3324).setDepth(depth - 1);
|
|
} else if (["stream", "ice", "moat"].includes(type)) {
|
|
scene.add.ellipse(x, y, type === "moat" ? 420 : 150, type === "moat" ? 38 : 64, type === "ice" ? 0x9dd9e8 : 0x28758b, 0.72).setStrokeStyle(3, 0xb7eff1).setDepth(3);
|
|
} else if (["bridge", "stairs", "carpet"].includes(type)) {
|
|
scene.add.rectangle(x, y, type === "carpet" ? 100 : 130, type === "stairs" ? 48 : 40, type === "carpet" ? 0x7f2940 : 0x9a7548, 0.95).setStrokeStyle(3, 0xe0bd75).setDepth(depth);
|
|
} else if (["pine", "smoke", "statue", "arch", "lookout", "throne", "fountain"].includes(type)) {
|
|
const color = { pine: 0x275437, smoke: 0x70737c, statue: 0x8f9990, arch: 0x767d72, lookout: 0x68452d, throne: 0x613152, fountain: 0x4c99b5 }[type];
|
|
scene.add.triangle(x, y, 0, 42, 26, 0, 52, 42, color, 0.94).setStrokeStyle(2, 0xe4c779).setDepth(depth);
|
|
} else {
|
|
const color = ["rune", "chains"].includes(type) ? 0x8c4aa0 : 0x73513a;
|
|
scene.add.rectangle(x, y, 48 + index % 2 * 16, 30, color, 0.88).setStrokeStyle(2, 0xd1ac66).setDepth(depth);
|
|
}
|
|
});
|
|
}
|
|
|
|
function render(scene, regionId, obstacles, dynamicObstacles, unlockedRoutes) {
|
|
createTilemap(scene, regionId);
|
|
scene.add.image(Data.WIDTH / 2, Data.HEIGHT / 2, "lima-region-atlas", REGION_ROW[regionId])
|
|
.setDisplaySize(Data.WIDTH, Data.HEIGHT)
|
|
.setAlpha(regionId === "chamber" ? 0.52 : 0.42)
|
|
.setDepth(1);
|
|
obstacles.forEach((shape) => drawObstacle(scene, regionId, shape));
|
|
(dynamicObstacles || []).forEach((shape) => {
|
|
if (!unlockedRoutes.includes(shape.opensWith)) drawObstacle(scene, regionId, shape);
|
|
else if (shape.id === "bridge") {
|
|
scene.add.tileSprite(shape.x + shape.width / 2, shape.y + shape.height / 2, shape.width, 70, "lima-world-tiles", tileIndex(regionId, 2)).setDepth(7);
|
|
}
|
|
});
|
|
drawDecor(scene, regionId);
|
|
}
|
|
|
|
const api = Object.freeze({ ART, REGION_ROW, createTilemap, drawObstacle, render, tileIndex });
|
|
if (typeof module === "object" && module.exports) module.exports = api;
|
|
root.PrincessLimaMaps = api;
|
|
}(typeof globalThis !== "undefined" ? globalThis : this));
|