///
///
interface MapCoordinates {
x : number;
y : number;
z : number;
}
class RoomRandomMap {
private positionTable : {[x : number] : {[y : number] : RoomRandom}} = {};
private roomMap = new Map>();
public lowestX = 0;
public lowestY = 0;
public highestX = 0;
public highestY = 0;
public limitsInvalid = false;
/**
* This is a count of how many connections are actually available, assuming rooms that can connect through some direction.
* @type {number}
*/
public availableConnections : Array>;
/**
* This is a count of rooms since the last cache clear. Used to mean more, but now is just a count of how many rooms were placed from the current region.
* @type {number}
*/
public rooms;
/**
* Clears the current room cache and creates a new one that is empty
*/
public emptyCache () {
this.availableConnections = [];
Room.DIRECTIONS.forEach(() => {
this.availableConnections.push([]);
});
this.rooms = 0;
}
public getAvailableConnections () {
return this.availableConnections.reduce((previousValue, currentValue, currentIndex, array) => {
return previousValue + (currentValue.length);
}, 0);
}
public removeFromCache (coordinatesToRemove : MapCoordinates, direction : number) {
let directionArray = this.availableConnections[direction];
for (let k = 0, coordinates = directionArray[k]; coordinates != undefined; coordinates = directionArray[++k]) {
if (coordinates.x == coordinatesToRemove.x && coordinates.y == coordinatesToRemove.y) {
directionArray.splice(k, 1);
return;
}
}
}
public addToCache (coordinatesToAdd : MapCoordinates, direction : number) {
let directionArray = this.availableConnections[direction];
for (let k = 0, coordinates = directionArray[k]; coordinates != undefined; coordinates = directionArray[++k]) {
if (coordinates.x == coordinatesToAdd.x && coordinates.y == coordinatesToAdd.y) {
return;
}
}
directionArray.push(coordinatesToAdd);
}
public getAnyFromCache (direction : number) : RoomRandom {
let array = this.availableConnections[direction];
if (array.length > 0) {
let randomIndex = Math.floor(Math.random() * (array.length));
let randomCoordinates = array[randomIndex];
if (randomCoordinates != undefined) {
return this.getRoom(randomCoordinates.x, randomCoordinates.y);
}
}
}
public isFree (x : number, y : number) {
return (this.positionTable[x] == undefined || this.positionTable[x][y] === undefined);
}
public isFreeSquare (centerX : number, centerY : number, distanceFromCenter : number) {
for (let x = centerX - distanceFromCenter; x <= centerX + distanceFromCenter; x++) {
for (let y = centerY - distanceFromCenter; y <= centerY + distanceFromCenter; y++) {
if (!this.isFree(x,y)) {
return false;
}
}
}
return true;
}
public block (x : number, y : number) {
if (this.isFree(x, y)) {
if (this.positionTable[x] == undefined) {
this.positionTable[x] = {};
}
this.positionTable[x][y] = null;
this.updateCacheOnPosition(x, y);
this.updateLimits(x, y);
}
}
public updateAllLimits () {
this.highestX = 0;
this.highestY = 0;
this.lowestX = 0;
this.lowestY = 0;
for (let x in this.positionTable) {
for (let y in this.positionTable[x]) {
if (this.positionTable[x][y] != undefined && this.positionTable[x][y] != null) {
this.updateLimits(parseInt(x), parseInt(y));
}
}
}
this.limitsInvalid = false;
}
public updateLimits (x, y) {
if (x > this.highestX) this.highestX = x;
if (y > this.highestY) this.highestY = y;
if (y < this.lowestY) this.lowestY = y;
if (x < this.lowestX) this.lowestX = x;
}
public map (room : RoomRandom, x : number, y : number) {
if (this.positionTable[x] == undefined) {
this.positionTable[x] = {};
}
this.positionTable[x][y] = room;
this.roomMap.set(room, [x, y]);
this.updateCacheOnPosition(x, y);
// If we ever want to draw maps, this will be useful
this.updateLimits(x, y);
room.placed = true;
room.lastMap = this;
this.rooms++;
}
public unmap (x : number, y : number) {
if (this.positionTable[x] != undefined && this.positionTable[x][y] != undefined) {
let room = this.positionTable[x][y];
room.placed = false;
this.roomMap.delete(this.positionTable[x][y]);
delete (this.positionTable[x][y]);
this.rooms--;
this.updateCacheOnPosition(x, y);
this.limitsInvalid = true;
}
}
private updateCacheOnPosition (x : number, y : number) {
let coordinates = [x, y, 0];
let coordinatesMap = {x : x, y : y};
let coordinatesBlocked = !this.isFree(x, y);
let coordinatesRoom = this.getRoom(x, y);
Room.DIRECTIONS.forEach(direction => {
let oppositeDirection = OppositeDirection[Direction[direction]];
let shifted = Room.shift(coordinates, direction);
let shiftedMap = {x : shifted[0], y : shifted[1]};
let shiftedBlocked = !this.isFree(shifted[0], shifted[1]);
let shiftedRoom = this.getRoom(shifted[0], shifted[1]);
if (coordinatesRoom != undefined) {
if (shiftedBlocked) {
this.removeFromCache(coordinatesMap, direction);
} else if (coordinatesRoom.isConnectableOn(direction)) {
this.addToCache(coordinatesMap, direction);
}
} else {
this.removeFromCache(coordinatesMap, direction);
}
if (shiftedRoom != undefined) {
if (coordinatesBlocked) {
this.removeFromCache(shiftedMap, oppositeDirection);
} else if (shiftedRoom.isConnectableOn(oppositeDirection)) {
this.addToCache(shiftedMap, oppositeDirection);
}
} else {
this.removeFromCache(shiftedMap, oppositeDirection);
}
});
}
public getRoom (x : number, y : number) {
if (this.positionTable[x] != undefined) {
if (this.positionTable[x][y] != null) {
return this.positionTable[x][y];
}
}
return undefined;
}
public getCoordinates (room : Room) : Array {
return this.roomMap.get(room);
}
public getRoomCount () {
return this.roomMap.size;
}
public getWidth () {
return this.highestX - this.lowestX;
}
public getHeight () {
return this.highestY - this.lowestY;
}
public static PREFERRED_GROWTH_HORIZONTAL = 0;
public static PREFERRED_GROWTH_VERTICAL = 1;
public static PREFERRED_GROWTH_ANY = 2;
public getPreferredGrowth () {
let ratio = this.getHeight() / this.getWidth();
let idealRatio = 2.5; // This means we'd prefer a tall map that'd look good on our screen
let difference = Math.abs(ratio - idealRatio);
if (difference < 0.5) {
return RoomRandomMap.PREFERRED_GROWTH_ANY;
} else {
if (ratio < idealRatio) {
return RoomRandomMap.PREFERRED_GROWTH_VERTICAL;
} else {
return RoomRandomMap.PREFERRED_GROWTH_HORIZONTAL;
}
}
}
public static isDirectionPreferred (direction : number, growth : number) {
if (growth == RoomRandomMap.PREFERRED_GROWTH_ANY) {
return true;
} else if (direction == Direction.NORTH || direction == Direction.SOUTH) {
return growth == RoomRandomMap.PREFERRED_GROWTH_VERTICAL;
} else {
return growth == RoomRandomMap.PREFERRED_GROWTH_HORIZONTAL;
}
}
}