refactor
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
|||||||
|
resources
|
||||||
|
|
||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
*.log
|
*.log
|
||||||
|
|||||||
@@ -13,35 +13,47 @@ import { Orientation } from "./ascii_types";
|
|||||||
/** Dungeon Generator - generates TileMaps populated with rooms, traps, encounters, etc. */
|
/** Dungeon Generator - generates TileMaps populated with rooms, traps, encounters, etc. */
|
||||||
class DungeonFactory {
|
class DungeonFactory {
|
||||||
/** @type {number} */
|
/** @type {number} */
|
||||||
roomCount;
|
#roomCount;
|
||||||
|
|
||||||
/** @type {RoomConfig[]} */
|
/** @type {RoomConfig[]} */
|
||||||
rooms;
|
#rooms;
|
||||||
|
|
||||||
/** @type {TileMap} */
|
/** @type {TileMap} */
|
||||||
map;
|
#map;
|
||||||
|
|
||||||
|
get roomCount() {
|
||||||
|
return this.#roomCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
get rooms() {
|
||||||
|
return this.#rooms;
|
||||||
|
}
|
||||||
|
|
||||||
|
get map() {
|
||||||
|
return this.#map;
|
||||||
|
}
|
||||||
|
|
||||||
get width() {
|
get width() {
|
||||||
return this.map.width;
|
return this.#map.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
get height() {
|
get height() {
|
||||||
return this.map.height;
|
return this.#map.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} width
|
* @param {number} width
|
||||||
* @param {number} height
|
* @param {number} height
|
||||||
* @param {number} roomCount
|
* @param {number} #roomCount
|
||||||
*/
|
*/
|
||||||
constructor(width, height, roomCount) {
|
constructor(width, height, roomCount) {
|
||||||
this.roomCount = roomCount | 0;
|
this.#roomCount = roomCount | 0;
|
||||||
this.rooms = [];
|
this.#rooms = [];
|
||||||
|
|
||||||
// 2d array of pure wall tiles
|
// 2d array of pure wall tiles
|
||||||
const tiles = new Array(height | 0).fill().map(() => Array(width | 0).fill(Tile.createWall()));
|
const tiles = new Array(height | 0).fill().map(() => Array(width | 0).fill(Tile.createWall()));
|
||||||
|
|
||||||
this.map = new TileMap(tiles);
|
this.#map = new TileMap(tiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
generate() {
|
generate() {
|
||||||
@@ -52,18 +64,18 @@ class DungeonFactory {
|
|||||||
this.addFeatures();
|
this.addFeatures();
|
||||||
this.addPlayerStart();
|
this.addPlayerStart();
|
||||||
this.addPortals();
|
this.addPortals();
|
||||||
return this.map.toString(CharType.TYPE_ID);
|
return this.#map.toString(CharType.TYPE_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
generateRooms() {
|
generateRooms() {
|
||||||
this.rooms = [];
|
this.#rooms = [];
|
||||||
const maxAttempts = this.roomCount * 10;
|
const maxAttempts = this.#roomCount * 10;
|
||||||
let attempts = 0;
|
let attempts = 0;
|
||||||
|
|
||||||
while (this.rooms.length < this.roomCount && attempts < maxAttempts) {
|
while (this.#rooms.length < this.#roomCount && attempts < maxAttempts) {
|
||||||
const room = this.generateRoom();
|
const room = this.generateRoom();
|
||||||
if (room && !this.roomOverlaps(room)) {
|
if (room && !this.roomOverlaps(room)) {
|
||||||
this.rooms.push(room);
|
this.#rooms.push(room);
|
||||||
this.carveRoom(room);
|
this.carveRoom(room);
|
||||||
}
|
}
|
||||||
attempts++;
|
attempts++;
|
||||||
@@ -83,7 +95,7 @@ class DungeonFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
roomOverlaps(newRoom) {
|
roomOverlaps(newRoom) {
|
||||||
return this.rooms.some(
|
return this.#rooms.some(
|
||||||
(room) =>
|
(room) =>
|
||||||
newRoom.x < room.x + room.width + 2 &&
|
newRoom.x < room.x + room.width + 2 &&
|
||||||
newRoom.x + newRoom.width + 2 > room.x &&
|
newRoom.x + newRoom.width + 2 > room.x &&
|
||||||
@@ -95,26 +107,26 @@ class DungeonFactory {
|
|||||||
carveRoom(room) {
|
carveRoom(room) {
|
||||||
for (let y = room.y; y < room.y + room.height; y++) {
|
for (let y = room.y; y < room.y + room.height; y++) {
|
||||||
for (let x = room.x; x < room.x + room.width; x++) {
|
for (let x = room.x; x < room.x + room.width; x++) {
|
||||||
this.map.tiles[y][x] = Tile.createFloor();
|
this.#map.tiles[y][x] = Tile.createFloor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connectRooms() {
|
connectRooms() {
|
||||||
if (this.rooms.length < 2) return;
|
if (this.#rooms.length < 2) return;
|
||||||
|
|
||||||
// Connect each room to at least one other room
|
// Connect each room to at least one other room
|
||||||
for (let i = 1; i < this.rooms.length >> 1; i++) {
|
for (let i = 1; i < this.#rooms.length >> 1; i++) {
|
||||||
const roomA = this.rooms[i - 1];
|
const roomA = this.#rooms[i - 1];
|
||||||
const roomB = this.rooms[i];
|
const roomB = this.#rooms[i];
|
||||||
this.createCorridor(roomA, roomB);
|
this.createCorridor(roomA, roomB);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add some extra connections for more interesting layouts
|
// Add some extra connections for more interesting layouts
|
||||||
const extraConnections = Math.floor(this.rooms.length / 3);
|
const extraConnections = Math.floor(this.#rooms.length / 3);
|
||||||
for (let i = 0; i < extraConnections; i++) {
|
for (let i = 0; i < extraConnections; i++) {
|
||||||
const roomA = this.rooms[this.random(0, this.rooms.length - 1)];
|
const roomA = this.#rooms[this.random(0, this.#rooms.length - 1)];
|
||||||
const roomB = this.rooms[this.random(0, this.rooms.length - 1)];
|
const roomB = this.#rooms[this.random(0, this.#rooms.length - 1)];
|
||||||
if (roomA !== roomB) {
|
if (roomA !== roomB) {
|
||||||
this.createCorridor(roomA, roomB);
|
this.createCorridor(roomA, roomB);
|
||||||
}
|
}
|
||||||
@@ -138,7 +150,7 @@ class DungeonFactory {
|
|||||||
|
|
||||||
for (let x = 0; x < this.width; x++) {
|
for (let x = 0; x < this.width; x++) {
|
||||||
//
|
//
|
||||||
if (this.map.get(x, y).isWall()) {
|
if (this.#map.get(x, y).isWall()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,7 +192,7 @@ class DungeonFactory {
|
|||||||
row.push(Tile.createWall()); // Initial wall tile on this row
|
row.push(Tile.createWall()); // Initial wall tile on this row
|
||||||
for (let x = dungeonStartX; x <= dungeonEndX; x++) {
|
for (let x = dungeonStartX; x <= dungeonEndX; x++) {
|
||||||
/**/
|
/**/
|
||||||
const tile = this.map.get(x, y);
|
const tile = this.#map.get(x, y);
|
||||||
row.push(tile);
|
row.push(tile);
|
||||||
}
|
}
|
||||||
row.push(Tile.createWall()); // Final wall tile on this row
|
row.push(Tile.createWall()); // Final wall tile on this row
|
||||||
@@ -190,7 +202,7 @@ class DungeonFactory {
|
|||||||
// Final row is all walls
|
// Final row is all walls
|
||||||
newTiles.push(new Array(newWidth).fill(Tile.createWall()));
|
newTiles.push(new Array(newWidth).fill(Tile.createWall()));
|
||||||
|
|
||||||
this.map = new TileMap(newTiles);
|
this.#map = new TileMap(newTiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
createCorridor(roomA, roomB) {
|
createCorridor(roomA, roomB) {
|
||||||
@@ -220,7 +232,7 @@ class DungeonFactory {
|
|||||||
|
|
||||||
while (x !== x2 || y !== y2) {
|
while (x !== x2 || y !== y2) {
|
||||||
if (x >= 0 && x < this.width && y >= 0 && y < this.height) {
|
if (x >= 0 && x < this.width && y >= 0 && y < this.height) {
|
||||||
this.map.tiles[y][x] = Tile.createFloor();
|
this.#map.tiles[y][x] = Tile.createFloor();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x !== x2) x += dx;
|
if (x !== x2) x += dx;
|
||||||
@@ -229,7 +241,7 @@ class DungeonFactory {
|
|||||||
|
|
||||||
// Ensure endpoint is carved
|
// Ensure endpoint is carved
|
||||||
if (x2 >= 0 && x2 < this.width && y2 >= 0 && y2 < this.height) {
|
if (x2 >= 0 && x2 < this.width && y2 >= 0 && y2 < this.height) {
|
||||||
this.map.tiles[y2][x2] = Tile.createFloor();
|
this.#map.tiles[y2][x2] = Tile.createFloor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,14 +250,14 @@ class DungeonFactory {
|
|||||||
for (let y = 1; y < this.height - 1; y++) {
|
for (let y = 1; y < this.height - 1; y++) {
|
||||||
//
|
//
|
||||||
for (let x = 1; x < this.width - 1; x++) {
|
for (let x = 1; x < this.width - 1; x++) {
|
||||||
const cell = this.map.get(x, y);
|
const cell = this.#map.get(x, y);
|
||||||
|
|
||||||
if (!cell) {
|
if (!cell) {
|
||||||
console.warn("out of bounds [%d, %d] (%s)", x, y, typeof cell);
|
console.warn("out of bounds [%d, %d] (%s)", x, y, typeof cell);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.map.get(x, y).isFloor()) {
|
if (this.#map.get(x, y).isFloor()) {
|
||||||
walkabilityCache.push([x, y]);
|
walkabilityCache.push([x, y]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,7 +275,7 @@ class DungeonFactory {
|
|||||||
|
|
||||||
for (let [x, y] of walkabilityCache) {
|
for (let [x, y] of walkabilityCache) {
|
||||||
//
|
//
|
||||||
const walkable = (offsetX, offsetY) => this.map.isFloorLike(x + offsetX, y + offsetY);
|
const walkable = (offsetX, offsetY) => this.#map.isFloorLike(x + offsetX, y + offsetY);
|
||||||
|
|
||||||
const surroundingFloorCount =
|
const surroundingFloorCount =
|
||||||
0 +
|
0 +
|
||||||
@@ -283,7 +295,7 @@ class DungeonFactory {
|
|||||||
|
|
||||||
if (surroundingFloorCount >= 7) {
|
if (surroundingFloorCount >= 7) {
|
||||||
// MAGIC NUMBER 7
|
// MAGIC NUMBER 7
|
||||||
this.map.tiles[y][x] = Tile.createWall();
|
this.#map.tiles[y][x] = Tile.createWall();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -293,14 +305,14 @@ class DungeonFactory {
|
|||||||
for (let y = 1; y < this.height - 1; y++) {
|
for (let y = 1; y < this.height - 1; y++) {
|
||||||
//
|
//
|
||||||
for (let x = 1; x < this.width - 1; x++) {
|
for (let x = 1; x < this.width - 1; x++) {
|
||||||
const cell = this.map.get(x, y);
|
const cell = this.#map.get(x, y);
|
||||||
|
|
||||||
if (!cell) {
|
if (!cell) {
|
||||||
console.warn("out of bounds [%d, %d] (%s)", x, y, typeof cell);
|
console.warn("out of bounds [%d, %d] (%s)", x, y, typeof cell);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.map.isFloorLike(x, y)) {
|
if (this.#map.isFloorLike(x, y)) {
|
||||||
walkabilityCache.push([x, y]);
|
walkabilityCache.push([x, y]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -309,7 +321,7 @@ class DungeonFactory {
|
|||||||
const idx = this.random(0, walkabilityCache.length - 1);
|
const idx = this.random(0, walkabilityCache.length - 1);
|
||||||
const [x, y] = walkabilityCache[idx];
|
const [x, y] = walkabilityCache[idx];
|
||||||
|
|
||||||
const walkable = (offsetX, offsetY) => this.map.isFloorLike(x + offsetX, y + offsetY);
|
const walkable = (offsetX, offsetY) => this.#map.isFloorLike(x + offsetX, y + offsetY);
|
||||||
|
|
||||||
//
|
//
|
||||||
// When spawning in, which direction should the player be oriented?
|
// When spawning in, which direction should the player be oriented?
|
||||||
@@ -324,23 +336,23 @@ class DungeonFactory {
|
|||||||
// they don't face a wall upon spawning.
|
// they don't face a wall upon spawning.
|
||||||
const dirIdx = this.random(0, directions.length - 1);
|
const dirIdx = this.random(0, directions.length - 1);
|
||||||
|
|
||||||
this.map.tiles[y][x] = Tile.createPlayerStart(directions[dirIdx]);
|
this.#map.tiles[y][x] = Tile.createPlayerStart(directions[dirIdx]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add portals to isolated areas
|
// Add portals to isolated areas
|
||||||
addPortals() {
|
addPortals() {
|
||||||
let traversableTileCount = this.map.getFloorlikeTileCount();
|
let traversableTileCount = this.#map.getFloorlikeTileCount();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Find the player's start point, and let this be the
|
// Find the player's start point, and let this be the
|
||||||
// bases of area 0
|
// bases of area 0
|
||||||
const [x, y] = this.map.forEach((tile, x, y) => {
|
const [x, y] = this.#map.forEach((tile, x, y) => {
|
||||||
if (tile.typeId === TileChars.PLAYER_START_POINT) {
|
if (tile.typeId === TileChars.PLAYER_START_POINT) {
|
||||||
return [x, y];
|
return [x, y];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = this.map.getAllTraversableTilesConnectedTo(x, y);
|
const result = this.#map.getAllTraversableTilesConnectedTo(x, y);
|
||||||
|
|
||||||
if (result.size === traversableTileCount) {
|
if (result.size === traversableTileCount) {
|
||||||
// There are no isolated areas, return
|
// There are no isolated areas, return
|
||||||
@@ -385,7 +397,7 @@ class DungeonFactory {
|
|||||||
const floorTiles = [];
|
const floorTiles = [];
|
||||||
for (let y = 0; y < this.height; y++) {
|
for (let y = 0; y < this.height; y++) {
|
||||||
for (let x = 0; x < this.width; x++) {
|
for (let x = 0; x < this.width; x++) {
|
||||||
if (this.map.get(x, y).isFloor()) {
|
if (this.#map.get(x, y).isFloor()) {
|
||||||
floorTiles.push({ x, y });
|
floorTiles.push({ x, y });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -405,11 +417,11 @@ class DungeonFactory {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// Add monsters
|
// Add monsters
|
||||||
const encouterCount = Math.min(5, this.rooms.length);
|
const encouterCount = Math.min(5, this.#rooms.length);
|
||||||
for (let i = 0; i < encouterCount; i++) {
|
for (let i = 0; i < encouterCount; i++) {
|
||||||
const pos = floorTiles[this.random(0, floorTiles.length - 1)];
|
const pos = floorTiles[this.random(0, floorTiles.length - 1)];
|
||||||
if (this.map.tiles[pos.y][pos.x].isFloor()) {
|
if (this.#map.tiles[pos.y][pos.x].isFloor()) {
|
||||||
this.map.tiles[pos.y][pos.x] = Tile.createEncounterStartPoint("PLACEHOLDER_ENCOUNTER_ID");
|
this.#map.tiles[pos.y][pos.x] = Tile.createEncounterStartPoint("PLACEHOLDER_ENCOUNTER_ID");
|
||||||
// TODO: Add encounter to the dungeon's "roaming entities" array.
|
// TODO: Add encounter to the dungeon's "roaming entities" array.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import WebSocket from "ws";
|
|
||||||
import { Character } from "./character.js";
|
|
||||||
import { Config } from "./../config.js";
|
import { Config } from "./../config.js";
|
||||||
import { Scene } from "../scenes/scene.js";
|
|
||||||
|
/** @typedef {import("../scenes/scene.js").Scene} Scene */
|
||||||
|
/** @typedef {import("./characer.js").Character} Character */
|
||||||
|
/** @typedef {import("ws").Websocket} Websocket */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Player Account.
|
* Player Account.
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export class Session {
|
|||||||
* @param {Scene} scene
|
* @param {Scene} scene
|
||||||
*/
|
*/
|
||||||
setScene(scene) {
|
setScene(scene) {
|
||||||
|
this.frankofil = stil;
|
||||||
console.debug("Changing scene", { scene: scene.constructor.name });
|
console.debug("Changing scene", { scene: scene.constructor.name });
|
||||||
if (!(scene instanceof Scene)) {
|
if (!(scene instanceof Scene)) {
|
||||||
throw new Error(`Expected instance of Scene, got a ${typeof scene}: >>${scene}<<`);
|
throw new Error(`Expected instance of Scene, got a ${typeof scene}: >>${scene}<<`);
|
||||||
|
|||||||
@@ -256,8 +256,10 @@ export class CharacterSeeder {
|
|||||||
//
|
//
|
||||||
// Stats
|
// Stats
|
||||||
c.maxHitPoints = c.currentHitPoints = 15;
|
c.maxHitPoints = c.currentHitPoints = 15;
|
||||||
|
|
||||||
c.meleeCombat = Math.max(c.meleeCombat, 10);
|
c.meleeCombat = Math.max(c.meleeCombat, 10);
|
||||||
c.magic = Math.min(c.magic, 10);
|
c.awareness = Math.max(c.awareness, 10)
|
||||||
|
c.skulduggery = Math.min(c.skulduggery, 10);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Skills
|
// Skills
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { gGame } from "./models/globals.js";
|
|||||||
import { AuthenticationScene } from "./scenes/authentication/authenticationScene.js";
|
import { AuthenticationScene } from "./scenes/authentication/authenticationScene.js";
|
||||||
import { MessageType, WebsocketMessage, formatMessage } from "./utils/messages.js";
|
import { MessageType, WebsocketMessage, formatMessage } from "./utils/messages.js";
|
||||||
|
|
||||||
|
|
||||||
// __ __ _ _ ____ ____
|
// __ __ _ _ ____ ____
|
||||||
// | \/ | | | | _ \ / ___| ___ _ ____ _____ _ __
|
// | \/ | | | | _ \ / ___| ___ _ ____ _____ _ __
|
||||||
// | |\/| | | | | | | | \___ \ / _ \ '__\ \ / / _ \ '__|
|
// | |\/| | | | | | | | \___ \ / _ \ '__\ \ / / _ \ '__|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import * as regex from "./regex.js";
|
import * as regex from "./regex.js";
|
||||||
|
|
||||||
const MINI_UID_REGEX = regex.pretty(
|
const MINI_UID_REGEX = regex.compileMultilineRegex(
|
||||||
"\.uid\.", // Mini-uids always begin with ".uid."
|
"\.uid\.", // Mini-uids always begin with ".uid."
|
||||||
"[a-z0-9]{6,}$", // Terminated by 6 or more random numbers and lowercase letters.
|
"[a-z0-9]{6,}$", // Terminated by 6 or more random numbers and lowercase letters.
|
||||||
);
|
);
|
||||||
const ID_SANITY_REGEX = regex.pretty(
|
const ID_SANITY_REGEX = regex.compileMultilineRegex(
|
||||||
"^:", // All ids start with a colon
|
"^:", // All ids start with a colon
|
||||||
"([a-z0-9]+\.)*?", // Middle -optional- part :myid.gogle.thing.thang.thong
|
"([a-z0-9]+\.)*?", // Middle -optional- part :myid.gogle.thing.thang.thong
|
||||||
"[a-z0-9_]+$", // The terminating part of the id is numbers, lowercase letters, and -notably- underscores.
|
"[a-z0-9_]+$", // The terminating part of the id is numbers, lowercase letters, and -notably- underscores.
|
||||||
@@ -33,7 +33,7 @@ export function isIdSane(id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {string} crypto-unsafe pseudo random numbe"r.
|
* @returns {string} crypto-unsafe pseudo random number.
|
||||||
*
|
*
|
||||||
* Generate a random number, convert it to base36, and return it as a string with 7-8 characters.
|
* Generate a random number, convert it to base36, and return it as a string with 7-8 characters.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* @param {...string} args
|
* @param {...string} args
|
||||||
* @returns {Regexp}
|
* @returns {Regexp}
|
||||||
*/
|
*/
|
||||||
export function pretty(...args) {
|
export function compileMultilineRegex(...args) {
|
||||||
const regexprStr = args.join("");
|
const regexprStr = args.join("");
|
||||||
return new RegExp(regexprStr);
|
return new RegExp(regexprStr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ export class FramingOptions {
|
|||||||
frameChars = FrameType.values.Double;
|
frameChars = FrameType.values.Double;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {object} o
|
* @param {FramingOptions} o
|
||||||
* @returns {FramingOptions}
|
* @returns {FramingOptions}
|
||||||
*/
|
*/
|
||||||
static fromObject(o) {
|
static fromObject(o) {
|
||||||
|
|||||||
Reference in New Issue
Block a user