import type { TileMapData, TallSpec } from '@rpg/engine';
/** id тайлов луга «Пепельные луга». */
export const TILES = {
GRASS: 0,
PATH: 1,
WATER: 2,
TREE: 3,
ASH: 4,
BELLFLOWER: 5
} as const;
const BLOCKED = [TILES.WATER, TILES.TREE];
/** Дерево — высокий объект 48px, под ним рисуется трава. */
const TALL_OBJECTS: Record<number, TallSpec> = {
[TILES.TREE]: { height: 48, ground: TILES.GRASS }
};
/** Детерминированный ГПСЧ, чтобы локация выглядела одинаково между запусками. */
function mulberry32(seed: number): () => number {
return () => {
seed |= 0;
seed = (seed + 0x6d2b79f5) | 0;
let t = Math.imul(seed ^ (seed >>> 15), 1 | seed);
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
/**
* Локация «Выжженные луга»: поляны травы, тропа через карту,
* пара прудов и рощи деревьев по краям.
*/
export function buildMeadowsMap(width = 28, height = 28): TileMapData {
const rng = mulberry32(20260905);
const tiles: number[] = new Array(width * height).fill(TILES.GRASS);
const at = (x: number, y: number) => y * width + x;
// Пятна пепла (декоративные).
for (let i = 0; i < 40; i++) {
const x = Math.floor(rng() * width);
const y = Math.floor(rng() * height);
const r = 1 + Math.floor(rng() * 2);
for (let dy = -r; dy <= r; dy++) {
for (let dx = -r; dx <= r; dx++) {
const nx = x + dx;
const ny = y + dy;
if (nx >= 0 && ny >= 0 && nx < width && ny < height && rng() < 0.7) {
tiles[at(nx, ny)] = TILES.ASH;
}
}
}
}
// Тропа: ломаная слева-сверху вниз-вправо.
let px = 2;
let py = 2;
while (px < width - 3 || py < height - 3) {
tiles[at(px, py)] = TILES.PATH;
if (rng() < 0.5 && px < width - 3) px++;
else if (py < height - 3) py++;
else px++;
}
// Пруды.
for (const [cx, cy] of [
[6, 16],
[19, 7]
]) {
for (let dy = 0; dy < 3; dy++) {
for (let dx = 0; dx < 4; dx++) {
if (rng() < 0.85) tiles[at(cx + dx, cy + dy)] = TILES.WATER;
}
}
}
// Лунные колокольчики у берегов прудов (квестовый декор, проходимы).
for (const [fx, fy] of [
[10, 17],
[11, 19],
[18, 5],
[22, 9]
]) {
if (tiles[at(fx, fy)] === TILES.GRASS || tiles[at(fx, fy)] === TILES.ASH) {
tiles[at(fx, fy)] = TILES.BELLFLOWER;
}
}
// Рощи деревьев по краям.
for (let i = 0; i < 60; i++) {
const edge = rng() < 0.5;
const x = edge ? Math.floor(rng() * 4) : width - 1 - Math.floor(rng() * 4);
const y = Math.floor(rng() * height);
if (tiles[at(x, y)] === TILES.GRASS || tiles[at(x, y)] === TILES.ASH) {
tiles[at(x, y)] = TILES.TREE;
}
}
// Стартовая поляна вокруг центра всегда свободна.
const cx = Math.floor(width / 2);
const cy = Math.floor(height / 2);
for (let dy = -1; dy <= 1; dy++) {
for (let dx = -1; dx <= 1; dx++) {
tiles[at(cx + dx, cy + dy)] = TILES.GRASS;
}
}
return {
width,
height,
tiles,
blocked: [...BLOCKED],
tall: TALL_OBJECTS
};
}