///
enum Direction {
NORTH, NORTHEAST, EAST,
SOUTHEAST, SOUTH, SOUTHWEST,
WEST, NORTHWEST, UP, DOWN
}
var DirectionNames : {[id: string] : string} = (() => {
enum names {
NORTH = "North",
SOUTH = "South",
EAST = "East",
WEST = "West",
SOUTHEAST = "Southeast",
NORTHWEST = "Northwest",
SOUTHWEST = "Southwest",
NORTHEAST = "Northeast",
UP = "Down",
DOWN = "Up"
};
let obj : {[id: string] : string} = {};
for (let name in names) {
obj[name] = names[name];
obj[Direction[name]] = names[name];
}
return obj;
})();
var OppositeDirection : {[id : number] : Direction} = (() => {
let obj = {};
obj[Direction.NORTH] = Direction.SOUTH;
obj[Direction.SOUTH] = Direction.NORTH;
obj[Direction.EAST] = Direction.WEST;
obj[Direction.WEST] = Direction.EAST;
obj[Direction.SOUTHEAST] = Direction.NORTHWEST;
obj[Direction.NORTHWEST] = Direction.SOUTHEAST;
obj[Direction.SOUTHWEST] = Direction.NORTHEAST;
obj[Direction.NORTHEAST] = Direction.SOUTHWEST;
obj[Direction.UP] = Direction.DOWN;
obj[Direction.DOWN] = Direction.UP;
// Make it work with the name too
for (let i = 0; i < Object.keys(Direction).length / 2; i++) {
obj[Direction[i]] = obj[i];
}
return obj;
})();
class Room implements Printable {
protected name : string;
public connections : Array;
public description : Say = new Say();
public fodder : boolean;
public visited : boolean = false;
public constructor (id? : string, fodder? : boolean) {
this.name = id == undefined ? "Room" : id;
this.connections = new Array(Room.DIRECTIONS.length); // Array the same size as directions, but filled with undefined
this.fodder = fodder;
if (fodder != true) {
Room.addRoom(this);
}
}
public getName () {
return this.name;
}
public place (thing : Thing) {
Thing.InsideRoomRelation.setRelation(this, thing);
}
public remove (thing : Thing) {
// Don't remove stuff from other rooms
if (Thing.InsideRoomRelation.getLeft(thing) == this) {
Thing.InsideRoomRelation.unsetRight(thing);
}
}
public getContained () : Array {
return > Thing.InsideRoomRelation.getRight(this);
}
public getContainedAndVisibleTo (observer : Thing) : Array {
let contained = this.getContained();
let result = [];
contained.forEach((value) => {
if (value.visible && value !== observer) {
result.push(value);
}
});
return result;
}
public static DIRECTIONS : Array = (() => {
let directions : Array = [];
for (let i = 0; i < Object.keys(Direction).length / 2; i++) {
directions.push(i);
}
return directions;
})();
public getContainedAndVisible () : Array {
return this.getContainedAndVisibleTo(WorldState.player);
}
public mapRoom (r : Room, direction : Direction) {
let oppositeDirection = OppositeDirection[direction];
if (this.connections[direction] != undefined) {
console.warn("Replacing a connected room.", this, " connected through ", direction, " to ", this.connections[direction]);
this.connections[direction].unmapRoom(oppositeDirection);
}
this.connections[direction] = r;
if (r.connections[oppositeDirection] != undefined) {
console.warn("Replacing a connected room.", r, " connected through ", oppositeDirection, " to ", r.connections[oppositeDirection]);
r.unmapRoom(oppositeDirection);
}
r.connections[oppositeDirection] = this;
}
public unmapRoom (direction : Direction) {
if (this.connections[direction] != undefined) {
let r = this.connections[direction];
this.connections[direction] = undefined;
r.unmapRoom(OppositeDirection[direction]);
}
}
public getPrintedName () {
return this.name;
}
public getConnectedRooms () : Array {
let rooms = [];
this.connections.forEach(room => {
if (room != undefined) {
rooms.push(room);
}
});
return rooms;
}
/**
* This returns the best direction to follow if going from the current room to another room.
* THIS CODE IS REALLY EXPENSIVE AND SHOULD ONLY BE USED AS A LAST RESORT
* WARNING: THIS CODE WILL FAIL IF THE TARGET ROOM IS TOO FAR FROM THE CURRENT ROOM (FOR SECURITY REASONS).
* This code runs through every. single. room. to find the best route to take.
* If you need an NPC to stick to a region, don't let it go out of it in the first place!
* @param room
* @param validityCode
* @returns {any}
*/
// TODO: Make this shit fast
public bestDirectionTo (room : Room, validityCode? : (room : Room) => boolean) {
if (validityCode == undefined) validityCode = () => {return true};
/**
* Maximum amount of steps that will be considered for a route.
* This is useful to prevent the code from spanning too many rooms, as each room can have Room.DIRECTIONS.length directions to make another call, etc.
*
* @type {number}
*/
let maxSteps = 10;
maxSteps = maxSteps > WorldState.getMaximumRememberedRooms() ? maxSteps : WorldState.getMaximumRememberedRooms();
let recursiveBestPath = (cPath : Array, cRoom : Room, destination : Room) => {
// Ignore "bad" rooms
if (!validityCode(cRoom)) {
return;
}
// Prevent the code from straying too far
if (cRoom == destination) {
maxSteps = maxSteps > cPath.length ? cPath.length : maxSteps; // Stop considering worse routes
return cPath;
} else if (cPath.length > maxSteps) {
return undefined;
} else {
let paths = [];
for (let index in Room.DIRECTIONS) {
let direction = Room.DIRECTIONS[index];
let nextRoom = cRoom.connections[direction];
if (nextRoom != undefined && cPath.indexOf(nextRoom) == -1 && validityCode(nextRoom)) {
let path = recursiveBestPath(cPath.concat([nextRoom]), nextRoom, destination);
if (path != undefined) {
paths.push(path);
}
}
}
let shortestIndex = 0;
paths.forEach((value, index, array) => {
if (value.length < paths[shortestIndex].length) {
shortestIndex = index;
}
});
return paths[shortestIndex];
}
};
let paths = Array(Room.DIRECTIONS.length);
let shortestIndex;
for (let index in Room.DIRECTIONS) {
let direction = Room.DIRECTIONS[index];
let nextRoom = this.connections[direction];
if (nextRoom != undefined) {
paths[direction] = recursiveBestPath([this, nextRoom], nextRoom, room);
if (paths[direction] != undefined && (shortestIndex == undefined || paths[shortestIndex].length > paths[direction].length)) {
shortestIndex = direction;
}
}
}
return shortestIndex;
}
public static getDirectionXYZ (direction : Direction) {
var y = 0;
if ([Direction.NORTH, Direction.NORTHEAST, Direction.NORTHWEST].indexOf(direction) != -1) {
y = 1;
} else if ([Direction.SOUTH, Direction.SOUTHEAST, Direction.SOUTHWEST].indexOf(direction) != -1) {
y = -1;
}
var x = 0;
if ([Direction.EAST, Direction.SOUTHEAST, Direction.NORTHEAST].indexOf(direction) != -1) {
x = 1;
} else if ([Direction.WEST, Direction.SOUTHWEST, Direction.NORTHWEST].indexOf(direction) != -1) {
x = -1;
}
var z = direction == Direction.UP ? 1 :
direction == Direction.DOWN ? -1 :
0;
return [x, y, z];
}
public static shift (coordinates : Array, direction : number) {
let coordinatesVector = Room.getDirectionXYZ(direction);
coordinates.forEach((value, index, array) => {
coordinatesVector[index] += coordinates[index];
});
return coordinatesVector;
}
protected static rooms : {[id : string] : Room} = {};
protected static addRoom (room : Room) {
Room.rooms[room.name] = room;
}
public static getRooms () : Array {
let rooms = [];
for (let name in Room.rooms) {
rooms.push(Room.rooms[name]);
}
return rooms;
}
public static getRoom (id : string) {
return Room.rooms[id];
}
}