///
interface TrickierOptions {
region : RegionRandom;
map : RoomRandomMap;
otherRoom : RoomRandom;
}
interface TrickyOptions extends TrickierOptions {
otherRoomDirection : number;
trickyRoomDirection : number;
x : number;
y : number;
}
interface RoomRandomNode {
room : RoomRandom;
coordinates : Array;
distance : number;
}
class RoomRandom extends Room {
public connectableOn : Array = [Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST];
public randomizable = true; // non-randomizable rooms don't get placed automatically
public placed = false;
public appearChance = 75;
public extraConnectionChance = 75;
public backgroundImage = "tomato";
public lastMap : RoomRandomMap;
public constructor (id? : string, fodder? : boolean, forbidden? : boolean) {
super(id, fodder);
this.randomizable = forbidden != true;
}
public getBackgroundClass () {
if (this.isImageDefined()) {
return this.backgroundImage;
}
return "tomato";
}
private isImageDefined () {
try {
for (var i = 0; i < document.styleSheets.length; i++) {
var rules = document.styleSheets[i]['rules'] || document.styleSheets[i]['cssRules'];
for (var x in rules) {
if (typeof rules[x].selectorText == 'string' && rules[x].selectorText == "." + this.backgroundImage) {
return true;
}
}
}
return false;
} catch (e) {
console.warn("Unable to read image");
return true;
}
}
public isConnectableOn (oppositeDirection : number) {
return this.connectableOn.indexOf(oppositeDirection) != -1;
}
public trickyCode : (options : TrickyOptions) => boolean;
public getAnyDirection (options : TrickierOptions) : TrickyOptions {
let directionShuffler = new Shuffler(this.connectableOn);
for (let direction = directionShuffler.getOne(); direction != undefined; direction = directionShuffler.getOne()) {
let oppositeDirection = OppositeDirection[Direction[direction]];
let otherCoordinates = options.map.getCoordinates(options.otherRoom);
let wouldbeCoordinates = Room.shift(otherCoordinates, oppositeDirection);
let trickyOptions = {
otherRoom : options.otherRoom,
otherRoomDirection : oppositeDirection,
trickyRoomDirection : direction,
map : options.map,
region : options.region,
x : wouldbeCoordinates[0],
y : wouldbeCoordinates[1]
};
if (this.isPlaceable(trickyOptions)) {
return trickyOptions;
}
}
}
public isPlaceable (options : TrickyOptions) {
if (!this.isConnectableOn(options.trickyRoomDirection) || !options.map.isFree(options.x, options.y)) {
// This can't connect through that!
// That coordinate isn't free!
return false;
}
if (options.otherRoom == undefined || !options.otherRoom.isConnectableOn(options.otherRoomDirection)) {
// There is no other room there?
// The other room doesn't like it this way
return false;
}
// Do I have my own tricky code?
if (this.trickyCode != undefined) {
return this.trickyCode(options);
}
return true;
}
public getDistanceTo (room : RoomRandom) {
let otherCoordinates = this.lastMap.getCoordinates(room);
return this.getDistanceToCoordinates(otherCoordinates);
}
public getDistanceToCoordinates (otherCoordinates) {
let myCoordinates = this.lastMap.getCoordinates(this);
if (myCoordinates != undefined && otherCoordinates != undefined) {
let c1 = myCoordinates;
let c2 = otherCoordinates;
return Math.abs(c1[0] - c2[0]) + Math.abs(c1[1] - c2[1]);
}
}
/**
* This implementation is sufficiently fast for constant use.
* @param pathEnd
* @param map
* @param availableRooms
* @returns {Array}
*/
public findPathTo (pathEnd : RoomRandom, validRoom? : (room : RoomRandom) => boolean) {
validRoom = validRoom == undefined ? () => {return true;} : validRoom;
let map = this.lastMap;
let endPosition = map.getCoordinates(pathEnd);
let open = [];
let distance = (c1, c2) => {
return Math.abs(c1[0] - c2[0]) + Math.abs(c1[1] - c2[1]);
};
let neighbors = (room : RoomRandom, x : number, y : number) => {
let neighs = [];
for (let direction = 0; direction < room.connections.length; direction++) {
let otherRoom = room.connections[direction];
if (otherRoom != undefined && open.indexOf(otherRoom) == -1 && validRoom( otherRoom)) {
let dirCoordinates = Room.shift([x, y], direction);
let dir = [otherRoom, dirCoordinates, distance(endPosition, dirCoordinates)];
neighs.push(dir);
}
}
return neighs.sort((a,b) => { return ( a[2]) - ( b[2]);});
};
let shortestPath = {
length : map.getRoomCount()
};
let noPath = shortestPath;
let cPath = [];
let findPath = (myArray) => {
let room = myArray[0];
cPath.push(myArray);
if (room == pathEnd) {
if (shortestPath.length >= cPath.length) {
shortestPath = cPath.slice(0);
}
} else if (shortestPath.length > (cPath.length)) {
open.push(room);
let otherRooms = neighbors(room, myArray[1][0], myArray[1][1]);
for (let i = 0; i < otherRooms.length; i++) {
if ((cPath.length + 1) < shortestPath.length) {
findPath(otherRooms[i]);
}
}
open.pop();
}
cPath.pop();
};
findPath([this, map.getCoordinates(this)]);
return shortestPath != noPath ? shortestPath : undefined;
}
public getBestDirectionTo (otherRoom : RoomRandom, validRoom? : (room : RoomRandom) => boolean) {
let path = this.findPathTo(otherRoom, validRoom);
if (path != undefined) {
if (path.length == 1) {
return undefined;
}
return this.connections.indexOf(path[1][0]);
}
}
public getAStarPathTo (otherRoom : RoomRandom, validRoom? : (room : RoomRandom) => boolean) {
validRoom = validRoom != undefined ? validRoom : () => {return true};
let distance = (c1, c2) => {
return Math.abs(c1[0] - c2[0]) + Math.abs(c1[1] - c2[1]);
};
let isVisited = (room) => {
return visited.indexOf(room) != -1;
};
let getNeighbors = (node : RoomRandomNode) => {
let neighbors : Array = [];
for (let direction = 0; direction < node.room.connections.length; direction++) {
if (node.room.connections[direction] != undefined && !isVisited(node.room.connections[direction]) && validRoom( node.room.connections[direction])) {
let coordinates = Room.shift(node.coordinates, direction);
neighbors.push({
room : node.room.connections[direction],
coordinates : coordinates,
distance : distance(coordinates, endNode.coordinates)
});
visited.push( node.room.connections[direction]);
}
}
return neighbors;
};
let getClosestPath = () => {
let shortest = 0;
for (let i = 1; i < open.length; i++) {
let lastPoint = open[i][open[i].length - 1];
if (lastPoint.distance < open[shortest][open[shortest].length - 1].distance) {
shortest = i;
}
}
return shortest;
};
let endNode = {room : otherRoom, coordinates : this.lastMap.getCoordinates(otherRoom), distance : 0};
let startCoordinates = this.lastMap.getCoordinates(this);
let startNode = {room : this, coordinates : startCoordinates, distance : distance(startCoordinates, endNode.coordinates)};
let open : Array> = [[startNode]];
let closed : Array> = [];
let shortestPath = this.lastMap.getRoomCount();
let shortestIndex;
let myPath;
let closest = 0;
let visited : Array = [this];
while (open.length > 0) {
myPath = open.splice(closest, 1)[0];
if (myPath[myPath.length - 1].distance == 0) {
let push = closed.push(myPath);
if (myPath.length < shortestPath) {
shortestPath = myPath.length;
shortestIndex = push - 1;
}
// lazy, first path is very likely to be the best
break;
} else {
let neighbors = getNeighbors(myPath[myPath.length - 1]);
for (let i = 0; i < neighbors.length; i++) {
open.push(myPath.concat([neighbors[i]]));
}
}
for (let i = open.length - 1; i >= 0; i--) {
if (open[i].length >= shortestPath) {
open.splice(i, 1);
}
}
closest = getClosestPath();
}
return closed[shortestIndex];
}
public getAStarBestDirectionTo (otherRoom : RoomRandom, validRoom? : (room : RoomRandom) => boolean) {
let path = this.getAStarPathTo(otherRoom, validRoom);
if (path != undefined) {
if (path.length == 1) {
return undefined;
}
return this.connections.indexOf(path[1].room);
}
}
public getConnectedDirection () {
let shuffler = new Shuffler(Room.DIRECTIONS);
for (let direction = shuffler.getOne(); direction != undefined; direction = shuffler.getOne()) {
if (this.connections[direction] != undefined) {
return direction;
}
}
}
// TODO: Return all things of type that are in placed RoomRandom.
public static getActive (type : typeof Thing) {
}
}
// random = new RoomRandom...
// random.connectableOn = [Room.DIRECTION_NORTH, Room.DIRECTION_SOUTH...]