UNPKG

rot-js

Version:

A roguelike toolkit in JavaScript

319 lines (318 loc) 10.1 kB
import RNG from "../rng.js"; ; /** * @class Dungeon feature; has own .create() method */ class Feature { } /** * @class Room * @augments ROT.Map.Feature * @param {int} x1 * @param {int} y1 * @param {int} x2 * @param {int} y2 * @param {int} [doorX] * @param {int} [doorY] */ export class Room extends Feature { constructor(x1, y1, x2, y2, doorX, doorY) { super(); this._x1 = x1; this._y1 = y1; this._x2 = x2; this._y2 = y2; this._doors = {}; if (doorX !== undefined && doorY !== undefined) { this.addDoor(doorX, doorY); } } ; /** * Room of random size, with a given doors and direction */ static createRandomAt(x, y, dx, dy, options) { let min = options.roomWidth[0]; let max = options.roomWidth[1]; let width = RNG.getUniformInt(min, max); min = options.roomHeight[0]; max = options.roomHeight[1]; let height = RNG.getUniformInt(min, max); if (dx == 1) { /* to the right */ let y2 = y - Math.floor(RNG.getUniform() * height); return new this(x + 1, y2, x + width, y2 + height - 1, x, y); } if (dx == -1) { /* to the left */ let y2 = y - Math.floor(RNG.getUniform() * height); return new this(x - width, y2, x - 1, y2 + height - 1, x, y); } if (dy == 1) { /* to the bottom */ let x2 = x - Math.floor(RNG.getUniform() * width); return new this(x2, y + 1, x2 + width - 1, y + height, x, y); } if (dy == -1) { /* to the top */ let x2 = x - Math.floor(RNG.getUniform() * width); return new this(x2, y - height, x2 + width - 1, y - 1, x, y); } throw new Error("dx or dy must be 1 or -1"); } /** * Room of random size, positioned around center coords */ static createRandomCenter(cx, cy, options) { let min = options.roomWidth[0]; let max = options.roomWidth[1]; let width = RNG.getUniformInt(min, max); min = options.roomHeight[0]; max = options.roomHeight[1]; let height = RNG.getUniformInt(min, max); let x1 = cx - Math.floor(RNG.getUniform() * width); let y1 = cy - Math.floor(RNG.getUniform() * height); let x2 = x1 + width - 1; let y2 = y1 + height - 1; return new this(x1, y1, x2, y2); } /** * Room of random size within a given dimensions */ static createRandom(availWidth, availHeight, options) { let min = options.roomWidth[0]; let max = options.roomWidth[1]; let width = RNG.getUniformInt(min, max); min = options.roomHeight[0]; max = options.roomHeight[1]; let height = RNG.getUniformInt(min, max); let left = availWidth - width - 1; let top = availHeight - height - 1; let x1 = 1 + Math.floor(RNG.getUniform() * left); let y1 = 1 + Math.floor(RNG.getUniform() * top); let x2 = x1 + width - 1; let y2 = y1 + height - 1; return new this(x1, y1, x2, y2); } addDoor(x, y) { this._doors[x + "," + y] = 1; return this; } /** * @param {function} */ getDoors(cb) { for (let key in this._doors) { let parts = key.split(","); cb(parseInt(parts[0]), parseInt(parts[1])); } return this; } clearDoors() { this._doors = {}; return this; } addDoors(isWallCallback) { let left = this._x1 - 1; let right = this._x2 + 1; let top = this._y1 - 1; let bottom = this._y2 + 1; for (let x = left; x <= right; x++) { for (let y = top; y <= bottom; y++) { if (x != left && x != right && y != top && y != bottom) { continue; } if (isWallCallback(x, y)) { continue; } this.addDoor(x, y); } } return this; } debug() { console.log("room", this._x1, this._y1, this._x2, this._y2); } isValid(isWallCallback, canBeDugCallback) { let left = this._x1 - 1; let right = this._x2 + 1; let top = this._y1 - 1; let bottom = this._y2 + 1; for (let x = left; x <= right; x++) { for (let y = top; y <= bottom; y++) { if (x == left || x == right || y == top || y == bottom) { if (!isWallCallback(x, y)) { return false; } } else { if (!canBeDugCallback(x, y)) { return false; } } } } return true; } /** * @param {function} digCallback Dig callback with a signature (x, y, value). Values: 0 = empty, 1 = wall, 2 = door. Multiple doors are allowed. */ create(digCallback) { let left = this._x1 - 1; let right = this._x2 + 1; let top = this._y1 - 1; let bottom = this._y2 + 1; let value = 0; for (let x = left; x <= right; x++) { for (let y = top; y <= bottom; y++) { if (x + "," + y in this._doors) { value = 2; } else if (x == left || x == right || y == top || y == bottom) { value = 1; } else { value = 0; } digCallback(x, y, value); } } } getCenter() { return [Math.round((this._x1 + this._x2) / 2), Math.round((this._y1 + this._y2) / 2)]; } getLeft() { return this._x1; } getRight() { return this._x2; } getTop() { return this._y1; } getBottom() { return this._y2; } } /** * @class Corridor * @augments ROT.Map.Feature * @param {int} startX * @param {int} startY * @param {int} endX * @param {int} endY */ export class Corridor extends Feature { constructor(startX, startY, endX, endY) { super(); this._startX = startX; this._startY = startY; this._endX = endX; this._endY = endY; this._endsWithAWall = true; } static createRandomAt(x, y, dx, dy, options) { let min = options.corridorLength[0]; let max = options.corridorLength[1]; let length = RNG.getUniformInt(min, max); return new this(x, y, x + dx * length, y + dy * length); } debug() { console.log("corridor", this._startX, this._startY, this._endX, this._endY); } isValid(isWallCallback, canBeDugCallback) { let sx = this._startX; let sy = this._startY; let dx = this._endX - sx; let dy = this._endY - sy; let length = 1 + Math.max(Math.abs(dx), Math.abs(dy)); if (dx) { dx = dx / Math.abs(dx); } if (dy) { dy = dy / Math.abs(dy); } let nx = dy; let ny = -dx; let ok = true; for (let i = 0; i < length; i++) { let x = sx + i * dx; let y = sy + i * dy; if (!canBeDugCallback(x, y)) { ok = false; } if (!isWallCallback(x + nx, y + ny)) { ok = false; } if (!isWallCallback(x - nx, y - ny)) { ok = false; } if (!ok) { length = i; this._endX = x - dx; this._endY = y - dy; break; } } /** * If the length degenerated, this corridor might be invalid */ /* not supported */ if (length == 0) { return false; } /* length 1 allowed only if the next space is empty */ if (length == 1 && isWallCallback(this._endX + dx, this._endY + dy)) { return false; } /** * We do not want the corridor to crash into a corner of a room; * if any of the ending corners is empty, the N+1th cell of this corridor must be empty too. * * Situation: * #######1 * .......? * #######2 * * The corridor was dug from left to right. * 1, 2 - problematic corners, ? = N+1th cell (not dug) */ let firstCornerBad = !isWallCallback(this._endX + dx + nx, this._endY + dy + ny); let secondCornerBad = !isWallCallback(this._endX + dx - nx, this._endY + dy - ny); this._endsWithAWall = isWallCallback(this._endX + dx, this._endY + dy); if ((firstCornerBad || secondCornerBad) && this._endsWithAWall) { return false; } return true; } /** * @param {function} digCallback Dig callback with a signature (x, y, value). Values: 0 = empty. */ create(digCallback) { let sx = this._startX; let sy = this._startY; let dx = this._endX - sx; let dy = this._endY - sy; let length = 1 + Math.max(Math.abs(dx), Math.abs(dy)); if (dx) { dx = dx / Math.abs(dx); } if (dy) { dy = dy / Math.abs(dy); } for (let i = 0; i < length; i++) { let x = sx + i * dx; let y = sy + i * dy; digCallback(x, y, 0); } return true; } createPriorityWalls(priorityWallCallback) { if (!this._endsWithAWall) { return; } let sx = this._startX; let sy = this._startY; let dx = this._endX - sx; let dy = this._endY - sy; if (dx) { dx = dx / Math.abs(dx); } if (dy) { dy = dy / Math.abs(dy); } let nx = dy; let ny = -dx; priorityWallCallback(this._endX + dx, this._endY + dy); priorityWallCallback(this._endX + nx, this._endY + ny); priorityWallCallback(this._endX - nx, this._endY - ny); } }