File perms
This commit is contained in:
0
.gitignore
vendored
Normal file → Executable file
0
.gitignore
vendored
Normal file → Executable file
@@ -149,14 +149,19 @@ export class TileMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
behavesLikeFloor(x, y) {
|
behavesLikeFloor(x, y) {
|
||||||
|
console.log("behavesLikeFloor???", { x, y });
|
||||||
x |= 0;
|
x |= 0;
|
||||||
y |= 0;
|
y |= 0;
|
||||||
|
|
||||||
if (x < 0 || x >= this.width || y < 0 || y >= this.height) {
|
if (x < 0 || x >= this.width || y < 0 || y >= this.height) {
|
||||||
|
console.log(" behavesLikeFloor: YES");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.tiles[y][x].isFloorlike();
|
const result = this.tiles[y][x].isFloorlike();
|
||||||
|
console.log(result ? " YES" : " NOPE");
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { mustBe, mustBeString } from "../utils/mustbe.js";
|
import { mustBe, mustBeString } from "../utils/mustbe.js";
|
||||||
import shallowCopy from "../utils/shallowCopy.js";
|
import shallowCopy from "../utils/shallowCopy.js";
|
||||||
import { TileOptions } from "../utils/tileOptionsParser.js";
|
import { TileOptions } from "../utils/tileOptionsParser.js";
|
||||||
import { Orientation, Vector2i } from "./ascii_types.js";
|
import { Orientation } from "./ascii_types.js";
|
||||||
|
|
||||||
/** @typedef {string} TileTypeId - a string with a length of 1 */
|
/** @typedef {string} TileTypeId - a string with a length of 1 */
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ export class Tile {
|
|||||||
"REQUIRED_ symbol encountered in Tile constructor. ",
|
"REQUIRED_ symbol encountered in Tile constructor. ",
|
||||||
"REQUIRED_ is a placeholder, and cannot be used as a value directly",
|
"REQUIRED_ is a placeholder, and cannot be used as a value directly",
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
{ key, val, options: properties },
|
{ key, val, properties },
|
||||||
);
|
);
|
||||||
throw new Error("Incomplete data in constructor. Args may not contain a data placeholder");
|
throw new Error("Incomplete data in constructor. Args may not contain a data placeholder");
|
||||||
}
|
}
|
||||||
@@ -198,7 +198,6 @@ export class Tile {
|
|||||||
|
|
||||||
//
|
//
|
||||||
// Normalize Orientation.
|
// Normalize Orientation.
|
||||||
//
|
|
||||||
if (this.orientation !== undefined && typeof this.orientation === "string") {
|
if (this.orientation !== undefined && typeof this.orientation === "string") {
|
||||||
const valueMap = {
|
const valueMap = {
|
||||||
north: Orientation.NORTH,
|
north: Orientation.NORTH,
|
||||||
@@ -210,7 +209,7 @@ export class Tile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Tiles are not necessarily required to have an ID, but if they have one, it must be string or number
|
// Tiles are not required to have IDs, but IDs must be numbers or strings
|
||||||
if (this.id !== undefined) {
|
if (this.id !== undefined) {
|
||||||
mustBe(this.id, "number", "string");
|
mustBe(this.id, "number", "string");
|
||||||
}
|
}
|
||||||
@@ -234,8 +233,8 @@ export class Tile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {Tile} */
|
/** @returns {Tile} */
|
||||||
static createEncounterStartPoint() {
|
static createEncounterStartPoint(encounterId) {
|
||||||
return this.fromChar(TileChars.ENCOUNTER_START_POINT);
|
return this.fromChar(TileChars.ENCOUNTER_START_POINT, { encounterId });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {Tile} */
|
/** @returns {Tile} */
|
||||||
@@ -270,7 +269,7 @@ export class Tile {
|
|||||||
// Normalize options into a TileOptions object,
|
// Normalize options into a TileOptions object,
|
||||||
//
|
//
|
||||||
if (!(options instanceof TileOptions)) {
|
if (!(options instanceof TileOptions)) {
|
||||||
options = TileOptions.fromObject(options);
|
options = TileOptions.fromObject(typeId, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
let optionPos = 0;
|
let optionPos = 0;
|
||||||
@@ -278,7 +277,7 @@ export class Tile {
|
|||||||
const getOption = (name) => options.getValue(name, optionPos++);
|
const getOption = (name) => options.getValue(name, optionPos++);
|
||||||
for (let [key, val] of Object.entries(typeInfo)) {
|
for (let [key, val] of Object.entries(typeInfo)) {
|
||||||
//
|
//
|
||||||
const fetchFromOption = typeof val === "symbol" && val.descript.startsWith("REQUIRED_");
|
const fetchFromOption = typeof val === "symbol" && val.description.startsWith("REQUIRED_");
|
||||||
|
|
||||||
creationArgs[key] = fetchFromOption ? getOption(key) : shallowCopy(val);
|
creationArgs[key] = fetchFromOption ? getOption(key) : shallowCopy(val);
|
||||||
}
|
}
|
||||||
@@ -287,7 +286,7 @@ export class Tile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
clone() {
|
clone() {
|
||||||
return new this.constructor(this);
|
return new Tile(this.typeId, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
isWallLike() {
|
isWallLike() {
|
||||||
@@ -303,6 +302,10 @@ export class Tile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isFloorlike() {
|
isFloorlike() {
|
||||||
|
if (this.typeId === TileChars.FLOOR) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.is === TileChars.FLOOR) {
|
if (this.is === TileChars.FLOOR) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -318,7 +321,3 @@ export class Tile {
|
|||||||
return this.typeId === TileChars.FLOOR;
|
return this.typeId === TileChars.FLOOR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Math.PI < 0 && TileOptions && Orientation && Vector2i) {
|
|
||||||
("STFU Linda");
|
|
||||||
}
|
|
||||||
|
|||||||
0
frontend/cellular_automata_map_generator.html
Normal file → Executable file
0
frontend/cellular_automata_map_generator.html
Normal file → Executable file
@@ -1,5 +1,5 @@
|
|||||||
import { CharType, TileMap } from "./ascii_tile_map";
|
import { CharType, TileMap } from "./ascii_tile_map";
|
||||||
import { Tile } from "./ascii_tile_types";
|
import { Tile, TileChars } from "./ascii_tile_types";
|
||||||
import { Orientation } from "./ascii_types";
|
import { Orientation } from "./ascii_types";
|
||||||
|
|
||||||
class DungeonGenerator {
|
class DungeonGenerator {
|
||||||
@@ -312,7 +312,16 @@ class DungeonGenerator {
|
|||||||
addPortals() {
|
addPortals() {
|
||||||
let traversableTileCount = this.map.getFloorlikeTileCount();
|
let traversableTileCount = this.map.getFloorlikeTileCount();
|
||||||
|
|
||||||
const result = this.map.getAllTraversableTilesConnectedTo(/** TODO PlayerPos */);
|
//
|
||||||
|
// Find the player's start point, and let this be the
|
||||||
|
// bases of area 0
|
||||||
|
const [x, y] = this.map.forEach((tile, x, y) => {
|
||||||
|
if (tile.typeId === TileChars.PLAYER_START_POINT) {
|
||||||
|
return [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
|
||||||
@@ -381,7 +390,7 @@ class DungeonGenerator {
|
|||||||
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();
|
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.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -401,7 +410,8 @@ class DungeonGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let currentDungeon = "";
|
/** @type {string} */
|
||||||
|
window.currentDungeon = "";
|
||||||
|
|
||||||
window.generateDungeon = () => {
|
window.generateDungeon = () => {
|
||||||
const width = parseInt(document.getElementById("width").value);
|
const width = parseInt(document.getElementById("width").value);
|
||||||
@@ -409,9 +419,9 @@ window.generateDungeon = () => {
|
|||||||
const roomCount = parseInt(document.getElementById("roomCount").value);
|
const roomCount = parseInt(document.getElementById("roomCount").value);
|
||||||
|
|
||||||
const generator = new DungeonGenerator(width, height, roomCount);
|
const generator = new DungeonGenerator(width, height, roomCount);
|
||||||
currentDungeon = generator.generate();
|
window.currentDungeon = generator.generate();
|
||||||
|
|
||||||
document.getElementById("dungeonDisplay").textContent = currentDungeon;
|
document.getElementById("dungeonDisplay").textContent = window.currentDungeon;
|
||||||
};
|
};
|
||||||
|
|
||||||
window.downloadDungeon = () => {
|
window.downloadDungeon = () => {
|
||||||
|
|||||||
0
frontend/progen.scss
Normal file → Executable file
0
frontend/progen.scss
Normal file → Executable file
@@ -56,7 +56,8 @@ export class Game {
|
|||||||
this._random = new Xorshift32(rngSeed);
|
this._random = new Xorshift32(rngSeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPlayer(username) {
|
getPlayerByUsername(username) {
|
||||||
|
console.log("GETTING PLAYER: `%s`", username);
|
||||||
return this._players.get(username);
|
return this._players.get(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Portal } from "./portal";
|
/** @typedef {import("./portal.js").Portal} Portal */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Location in the world.
|
* Location in the world.
|
||||||
@@ -9,19 +9,19 @@ import { Portal } from "./portal";
|
|||||||
* or magical portals to distant locations.
|
* or magical portals to distant locations.
|
||||||
*/
|
*/
|
||||||
export class Location {
|
export class Location {
|
||||||
/** @protected @type string */
|
/** @protected @type {string} */
|
||||||
_id;
|
_id;
|
||||||
get id() {
|
get id() {
|
||||||
return this._id;
|
return this._id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @protected @type string */
|
/** @protected @type {string} */
|
||||||
_name;
|
_name;
|
||||||
get name() {
|
get name() {
|
||||||
return this._name;
|
return this._name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @protected @type string */
|
/** @protected @type {string} */
|
||||||
_description;
|
_description;
|
||||||
get description() {
|
get description() {
|
||||||
return this._description;
|
return this._description;
|
||||||
@@ -34,6 +34,9 @@ export class Location {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param {string} id
|
||||||
|
* @param {string} name
|
||||||
|
* @param {string} description
|
||||||
*/
|
*/
|
||||||
constructor(id, name, description) {
|
constructor(id, name, description) {
|
||||||
this._id = id;
|
this._id = id;
|
||||||
@@ -41,5 +44,3 @@ export class Location {
|
|||||||
this._description = description;
|
this._description = description;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const l = new Location("foo", "bar", "baz");
|
|
||||||
|
|||||||
@@ -10,35 +10,27 @@ export class Session {
|
|||||||
_websocket;
|
_websocket;
|
||||||
|
|
||||||
/** @protected @type {Scene} */
|
/** @protected @type {Scene} */
|
||||||
_scene;
|
_currentScene;
|
||||||
|
|
||||||
/** @readonly @constant @type {Scene} */
|
/** @readonly @type {Scene} */
|
||||||
get scene() {
|
get scene() {
|
||||||
return this._scene;
|
return this._currentScene;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {Player} */
|
/** @constant @type {Player} */
|
||||||
_player;
|
_player;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The player that owns this session.
|
||||||
|
* This value is undefined until the player has logged in,
|
||||||
|
* and after that it is _constant_
|
||||||
|
*
|
||||||
|
* @constant @type {Player}
|
||||||
|
*/
|
||||||
get player() {
|
get player() {
|
||||||
return this._player;
|
return this._player;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param {Player} player */
|
|
||||||
set player(player) {
|
|
||||||
if (player instanceof Player) {
|
|
||||||
this._player = player;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (player === null) {
|
|
||||||
this._player = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw Error(`Can only set player to null or instance of Player, but received ${typeof player}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {WebSocket} websocket
|
* @param {WebSocket} websocket
|
||||||
*/
|
*/
|
||||||
@@ -54,10 +46,30 @@ export class Session {
|
|||||||
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}<<`);
|
||||||
}
|
}
|
||||||
this._scene = scene;
|
this._currentScene = scene;
|
||||||
scene.execute(this);
|
scene.execute(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* May only be called when the current player property has not yet been populated.
|
||||||
|
* May only called once.
|
||||||
|
*
|
||||||
|
* @param {Player} player
|
||||||
|
*/
|
||||||
|
setPlayer(player) {
|
||||||
|
if (player instanceof Player) {
|
||||||
|
this._player = player;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player === null) {
|
||||||
|
this._player = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Error(`Can only set player to null or instance of Player, but received ${typeof player}`);
|
||||||
|
}
|
||||||
|
|
||||||
/** Close the session and websocket */
|
/** Close the session and websocket */
|
||||||
close() {
|
close() {
|
||||||
if (this._websocket) {
|
if (this._websocket) {
|
||||||
@@ -65,7 +77,7 @@ export class Session {
|
|||||||
this._websocket = null;
|
this._websocket = null;
|
||||||
}
|
}
|
||||||
this._player = null;
|
this._player = null;
|
||||||
this._scene = null;
|
this._currentScene = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -29,10 +29,9 @@ export class AuthenticationScene extends Scene {
|
|||||||
|
|
||||||
passwordAccepted() {
|
passwordAccepted() {
|
||||||
this.player.loggedIn = true;
|
this.player.loggedIn = true;
|
||||||
this.session.player = this.player;
|
|
||||||
|
|
||||||
|
this.session.setPlayer(this.player);
|
||||||
this.session.sendText(["= Success!", "((but I don't know what to do now...))"]);
|
this.session.sendText(["= Success!", "((but I don't know what to do now...))"]);
|
||||||
|
|
||||||
this.session.setScene(new GameScene());
|
this.session.setScene(new GameScene());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,11 +30,11 @@ export class UsernamePrompt extends Prompt {
|
|||||||
|
|
||||||
//
|
//
|
||||||
// User replied to our prompt
|
// User replied to our prompt
|
||||||
onReply(text) {
|
onReply(username) {
|
||||||
//
|
//
|
||||||
// do basic syntax checks on usernames
|
// do basic syntax checks on usernames
|
||||||
if (!security.isUsernameSane(text)) {
|
if (!security.isUsernameSane(username)) {
|
||||||
console.info("Someone entered insane username: '%s'", text);
|
console.info("Someone entered insane username: '%s'", username);
|
||||||
this.sendError("Incorrect username, try again");
|
this.sendError("Incorrect username, try again");
|
||||||
this.execute();
|
this.execute();
|
||||||
return;
|
return;
|
||||||
@@ -42,12 +42,12 @@ export class UsernamePrompt extends Prompt {
|
|||||||
|
|
||||||
//
|
//
|
||||||
// try and fetch the player object from the game
|
// try and fetch the player object from the game
|
||||||
const player = gGame.getPlayer(text);
|
const player = gGame.getPlayerByUsername(username);
|
||||||
|
|
||||||
//
|
//
|
||||||
// handle invalid username
|
// handle invalid username
|
||||||
if (!player) {
|
if (!player) {
|
||||||
console.info("Someone entered incorrect username: '%s'", text);
|
console.info("Someone entered incorrect username: '%s'", username);
|
||||||
this.sendError("Incorrect username, try again");
|
this.sendError("Incorrect username, try again");
|
||||||
this.execute();
|
this.execute();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export class CreateUsernamePrompt extends Prompt {
|
|||||||
|
|
||||||
//
|
//
|
||||||
// try and fetch the player object from the game
|
// try and fetch the player object from the game
|
||||||
const player = gGame.getPlayer(username);
|
const player = gGame.getPlayerByUsername(username);
|
||||||
|
|
||||||
//
|
//
|
||||||
// handle invalid username
|
// handle invalid username
|
||||||
|
|||||||
0
scenes/playerCreation/playerCreationScene.js
Normal file → Executable file
0
scenes/playerCreation/playerCreationScene.js
Normal file → Executable file
@@ -1,8 +1,7 @@
|
|||||||
import { Scene } from "./scene.js";
|
|
||||||
|
|
||||||
/** @typedef {import("../models/session.js").Session} Session */
|
/** @typedef {import("../models/session.js").Session} Session */
|
||||||
/** @typedef {import("../utils/message.js").MessageType} MessageType */
|
/** @typedef {import("../utils/message.js").MessageType} MessageType */
|
||||||
/** @typedef {import("../utils/message.js").WebsocketMessage} WebsocketMessage */
|
/** @typedef {import("../utils/message.js").WebsocketMessage} WebsocketMessage */
|
||||||
|
/** @typedef {import("./scene.js").Scene} Scene */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {object} PromptMethods
|
* @typedef {object} PromptMethods
|
||||||
@@ -66,9 +65,6 @@ export class Prompt {
|
|||||||
|
|
||||||
/** @param {Scene} scene */
|
/** @param {Scene} scene */
|
||||||
constructor(scene) {
|
constructor(scene) {
|
||||||
if (!(scene instanceof Scene)) {
|
|
||||||
throw new Error("Expected an instance of >>Scene<< but got " + typeof scene);
|
|
||||||
}
|
|
||||||
this._scene = scene;
|
this._scene = scene;
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -103,6 +99,8 @@ export class Prompt {
|
|||||||
*
|
*
|
||||||
* @param {string} command
|
* @param {string} command
|
||||||
* @param {any[]} args
|
* @param {any[]} args
|
||||||
|
*
|
||||||
|
* @returns {boolean} true if the command was handled in the prompt.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
onColon(command, args) {
|
onColon(command, args) {
|
||||||
@@ -113,21 +111,21 @@ export class Prompt {
|
|||||||
// Default: we have no handler for the Foo command,
|
// Default: we have no handler for the Foo command,
|
||||||
// So let's see if daddy can handle it.
|
// So let's see if daddy can handle it.
|
||||||
if (property === undefined) {
|
if (property === undefined) {
|
||||||
return this.scene.onColon(command, args);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// If the prompt has a method called onColon_foo() =>
|
// If the prompt has a method called onColon_foo() =>
|
||||||
if (typeof property === "function") {
|
if (typeof property === "function") {
|
||||||
property.call(this, args);
|
property.call(this, args);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// If the prompt has a _string_ called onColon_foo =>
|
// If the prompt has a _string_ called onColon_foo =>
|
||||||
if (typeof property === "string") {
|
if (typeof property === "string") {
|
||||||
this.sendText(property);
|
this.sendText(property);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
100
scenes/scene.js
100
scenes/scene.js
@@ -1,6 +1,8 @@
|
|||||||
import { sprintf } from "sprintf-js";
|
import { sprintf } from "sprintf-js";
|
||||||
import { Session } from "../models/session.js";
|
|
||||||
import { Prompt } from "./prompt.js";
|
/** @typedef {import("../utils/messages.js").WebsocketMessage} WebsocketMessage */
|
||||||
|
/** @typedef {import("../models/session.js").Session} Session */
|
||||||
|
/** @typedef {import("./prompt.js").Prompt } Prompt */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scene - a class for showing one or more prompts in a row.
|
* Scene - a class for showing one or more prompts in a row.
|
||||||
@@ -19,7 +21,7 @@ export class Scene {
|
|||||||
*/
|
*/
|
||||||
introText = "";
|
introText = "";
|
||||||
|
|
||||||
/** @readonly @constant @type {Session} */
|
/** @readonly @constant @protected @type {Session} */
|
||||||
_session;
|
_session;
|
||||||
get session() {
|
get session() {
|
||||||
return this._session;
|
return this._session;
|
||||||
@@ -32,9 +34,9 @@ export class Scene {
|
|||||||
* @readonly
|
* @readonly
|
||||||
* @type {Prompt}
|
* @type {Prompt}
|
||||||
*/
|
*/
|
||||||
_prompt;
|
_currentPrompt;
|
||||||
get prompt() {
|
get currentPrompt() {
|
||||||
return this._prompt;
|
return this._currentPrompt;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
@@ -57,7 +59,7 @@ export class Scene {
|
|||||||
* @param {Prompt} prompt
|
* @param {Prompt} prompt
|
||||||
*/
|
*/
|
||||||
showPrompt(prompt) {
|
showPrompt(prompt) {
|
||||||
this._prompt = prompt;
|
this._currentPrompt = prompt;
|
||||||
prompt.execute();
|
prompt.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,6 +68,55 @@ export class Scene {
|
|||||||
this.showPrompt(new promptClassReference(this));
|
this.showPrompt(new promptClassReference(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user has been prompted, and has replied.
|
||||||
|
*
|
||||||
|
* We route that message to the current prompt.
|
||||||
|
*
|
||||||
|
* It should not be necessary to override this function
|
||||||
|
*
|
||||||
|
* @param {WebsocketMessage} message
|
||||||
|
*/
|
||||||
|
onReply(message) {
|
||||||
|
console.log("REPLY", {
|
||||||
|
message,
|
||||||
|
type: typeof message,
|
||||||
|
});
|
||||||
|
this.currentPrompt.onReply(message.text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user has declared their intention to quit.
|
||||||
|
*
|
||||||
|
* We route that message to the current prompt.
|
||||||
|
*
|
||||||
|
* It should may be necessary to override this method
|
||||||
|
* in case you want to trigger specific behavior before
|
||||||
|
* quitting.
|
||||||
|
*
|
||||||
|
* Default behavior is to route this message to the current prompt.
|
||||||
|
*/
|
||||||
|
onQuit() {
|
||||||
|
this.currentPrompt.onQuit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user has typed :help [topic]
|
||||||
|
*
|
||||||
|
* We route that message to the current prompt.
|
||||||
|
*
|
||||||
|
* It should not be necessary to override this function
|
||||||
|
* unless you want some special prompt-agnostic event
|
||||||
|
* to be triggered - however, scenes should not have too
|
||||||
|
* many prompts, so handling this behavior inside the prompt
|
||||||
|
* should be the primary choice.
|
||||||
|
*
|
||||||
|
* @param {WebsocketMessage} message
|
||||||
|
*/
|
||||||
|
onHelp(message) {
|
||||||
|
this.currentPrompt.onHelp(message.text);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggered when a user types a :command that begins with a colon
|
* Triggered when a user types a :command that begins with a colon
|
||||||
* and the current Prompt cannot handle that command.
|
* and the current Prompt cannot handle that command.
|
||||||
@@ -73,7 +124,7 @@ export class Scene {
|
|||||||
* @param {string} command
|
* @param {string} command
|
||||||
* @param {any[]} args
|
* @param {any[]} args
|
||||||
*/
|
*/
|
||||||
onColon(command, args) {
|
onColonFallback(command, args) {
|
||||||
const propertyName = "onColon__" + command;
|
const propertyName = "onColon__" + command;
|
||||||
const property = this[propertyName];
|
const property = this[propertyName];
|
||||||
|
|
||||||
@@ -85,14 +136,14 @@ export class Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// If the prompt has a method called onColon_foo() =>
|
// If this scene has a method called onColon_foo() =>
|
||||||
if (typeof property === "function") {
|
if (typeof property === "function") {
|
||||||
property.call(this, args);
|
property.call(this, args);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// If the prompt has a _string_ called onColon_foo =>
|
// If this scene has a string property called onColon_foo =>
|
||||||
if (typeof property === "string") {
|
if (typeof property === "string") {
|
||||||
this.session.sendText(property);
|
this.session.sendText(property);
|
||||||
return;
|
return;
|
||||||
@@ -108,9 +159,28 @@ export class Scene {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user has typed :help [topic]
|
||||||
|
*
|
||||||
|
* We route that message to the current prompt.
|
||||||
|
*
|
||||||
|
* There is no need to override this method.
|
||||||
|
*
|
||||||
|
* onColonFallback will be called if the current prompt
|
||||||
|
* cannot handle the :colon command.
|
||||||
|
*
|
||||||
|
* @param {WebsocketMessage} message
|
||||||
|
*/
|
||||||
|
onColon(message) {
|
||||||
|
const handledByPrompt = this.currentPrompt.onColon(message.command, message.args);
|
||||||
|
|
||||||
|
if (!handledByPrompt) {
|
||||||
|
this.onColonFallback(message.command, message.args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Easter ægg
|
// Example dynamic colon handler (also easter egg)
|
||||||
// Example dynamic colon handler
|
|
||||||
/** @param {any[]} args */
|
/** @param {any[]} args */
|
||||||
onColon__imperial(args) {
|
onColon__imperial(args) {
|
||||||
if (args.length === 0) {
|
if (args.length === 0) {
|
||||||
@@ -124,9 +194,5 @@ export class Scene {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onColon__hi = "Hoe";
|
onColon__hi = "Ho!";
|
||||||
}
|
|
||||||
|
|
||||||
if (Math.PI < 0 && Session && Prompt) {
|
|
||||||
("STFU Linda");
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export class GameSeeder {
|
|||||||
gGame.rngSeed = Config.rngSeed;
|
gGame.rngSeed = Config.rngSeed;
|
||||||
new PlayerSeeder().seed(); // Create debug players
|
new PlayerSeeder().seed(); // Create debug players
|
||||||
new ItemSeeder().seed(); // Create items, etc.
|
new ItemSeeder().seed(); // Create items, etc.
|
||||||
new CharacterSeeder().createParty(gGame.getPlayer("user"), 3); // Create debug characters.
|
new CharacterSeeder().createParty(gGame.getPlayerByUsername("user"), 3); // Create debug characters.
|
||||||
|
|
||||||
//
|
//
|
||||||
// Done
|
// Done
|
||||||
|
|||||||
@@ -174,15 +174,15 @@ class MudServer {
|
|||||||
const msgObj = new WebsocketMessage(data.toString());
|
const msgObj = new WebsocketMessage(data.toString());
|
||||||
|
|
||||||
//
|
//
|
||||||
// Handle replies to prompts. The main workhorse of the game.
|
// Route reply-messages to the current scene
|
||||||
if (msgObj.isReply()) {
|
if (msgObj.isReply()) {
|
||||||
return session.scene.prompt.onReply(msgObj.text);
|
return session.scene.onReply(msgObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Handle :help commands
|
// Handle :help commands
|
||||||
if (msgObj.isHelp()) {
|
if (msgObj.isHelp()) {
|
||||||
return session.scene.prompt.onHelp(msgObj.text);
|
return session.scene.onHelp(msgObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -196,7 +196,7 @@ class MudServer {
|
|||||||
//
|
//
|
||||||
// Handle any text that starts with ":" that isn't :help or :quit
|
// Handle any text that starts with ":" that isn't :help or :quit
|
||||||
if (msgObj.isColon()) {
|
if (msgObj.isColon()) {
|
||||||
return session.scene.prompt.onColon(msgObj.command, msgObj.args);
|
return session.scene.onColon(msgObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
0
utils/crackdown.js
Normal file → Executable file
0
utils/crackdown.js
Normal file → Executable file
2
utils/security.js
Normal file → Executable file
2
utils/security.js
Normal file → Executable file
@@ -54,5 +54,5 @@ export function isUsernameSane(candidate) {
|
|||||||
export function isPasswordSane(candidate) {
|
export function isPasswordSane(candidate) {
|
||||||
// We know the password must adhere to one of our client-side-hashed crypto schemes,
|
// We know the password must adhere to one of our client-side-hashed crypto schemes,
|
||||||
// so we can be fairly strict with the allowed passwords
|
// so we can be fairly strict with the allowed passwords
|
||||||
return Config.passwordSanityRegex.test(candidate);
|
return Config.passwordHashSanityRegex.test(candidate);
|
||||||
}
|
}
|
||||||
|
|||||||
0
utils/tui.js
Normal file → Executable file
0
utils/tui.js
Normal file → Executable file
Reference in New Issue
Block a user