import { Board } from "../core/board.js";
import { Registry } from "../core/registry.js";
import { Terrain } from "../core/terrain.js";
import { Agent } from "../core/agent.js";
import { Item } from "../core/item.js";
import { ROWS, COLUMNS } from "../core/board.js";
const BASE_URL = window.location.href.split("/game/")[0] + "/public/scenarios/";
/**
* Load a board from a scenario JSON file.
*
* @param {string} boardPath - path stem, e.g. "tutorial/start"
* @param {string} [baseURL] - base URL, default "/scenarios/"
* @returns {Promise<{board: Board, startX: number, startY: number, startInv: string[]}>}
*/
export async function loadBoard(boardPath, baseURL = BASE_URL) {
const url = `${baseURL}${boardPath}.json`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to load board: ${url} (${response.status})`);
}
const contentType = response.headers.get("content-type") ?? "";
if (
!contentType.includes("application/json") &&
!contentType.includes("text/json")
) {
throw new Error(
`Failed to load board: ${url} (response was not JSON — board file may be missing)`,
);
}
const data = await response.json();
return buildBoard(data, boardPath);
}
/**
* Build a Board from a deserialized scenario data object.
* Also used to restore boards from player.unsavedMaps.
*
* @param {object} data - raw JSON or deserialized save data
* @param {string} boardPath
* @returns {{board: Board, startX: number, startY: number, startInv: string[]}}
*/
export function buildBoard(data, boardPath) {
const board = new Board();
board.outside = data.outside ?? false;
board.startX = data.startX ?? 0;
board.startY = data.startY ?? 0;
board.scenarioName = data.scenarioName ?? data.name ?? null;
board.creator = data.creator ?? null;
board.description = data.description ?? null;
board.startInv = data.startInv ?? null;
board.folder = data.folder ?? null;
board.boardID = boardPath;
// Parse diagram: diagram[y][x] gives the char for cell (x, y)
const terrainMap = data.terrain ?? {};
for (let y = 0; y < ROWS; y++) {
const row = data.diagram?.[y] ?? "";
for (let x = 0; x < COLUMNS; x++) {
const ch = row[x] ?? " ";
const terrainKey = terrainMap[ch];
if (terrainKey) {
const piece = Registry.get(terrainKey);
if (piece instanceof Terrain) {
board.cells[x][y].setTerrain(piece);
}
}
}
}
// Place pieces (agents/items) from the pieces array
for (const pd of data.pieces ?? []) {
if (pd.key == null) continue;
const piece = Registry.get(pd.key);
const cell = board.getCellAt(pd.x, pd.y);
if (!cell || !piece) continue;
if (piece instanceof Agent) {
if (!cell.agent) cell.setAgent(piece);
} else if (piece instanceof Item) {
cell.addItem(piece);
} else if (piece instanceof Terrain) {
cell.setTerrain(piece);
}
}
// Register adjacent board paths
const boardDir = boardPath.includes("/")
? boardPath.split("/").slice(0, -1).join("/")
: "";
for (const dirName of ["north", "south", "east", "west", "up", "down"]) {
const stem = data[dirName];
if (stem) {
const fullPath = boardDir ? `${boardDir}/${stem}` : stem;
board.setAdjacentBoard(dirName, fullPath);
}
}
const startInventory = data.startInv
? data.startInv.split(",").map((s) => s.trim())
: [];
return {
board,
startX: data.startX ?? 0,
startY: data.startY ?? 0,
startInv: startInventory,
};
}