package com.kartoflane.ftl.floorgen;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
import com.kartoflane.ftl.layout.DoorLayoutObject;
import com.kartoflane.ftl.layout.RoomLayoutObject;
import com.kartoflane.ftl.layout.ShipLayout;
class Layout {
private final Graph tileGraph;
/** List of all airlocks in this layout */
private final List<Door> airlocks;
/** Width of the layout, in pixels */
private final int width;
/** Height of the layout, in pixels */
private final int height;
Layout(List<Rectangle> rooms, List<Door> airlocks) {
this.airlocks = airlocks;
tileGraph = new Graph();
int minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE, maxX = 0, maxY = 0;
for (Rectangle r : rooms) {
// Find the min and max points of the layout to determine its size
if (r.x < minX)
minX = r.x;
if (r.y < minY)
minY = r.y;
if (r.x + r.width > maxX)
maxX = r.x + r.width;
if (r.y + r.height > maxY)
maxY = r.y + r.height;
// Add the tiles constituting the room to the graph
for (int x = r.x; x < r.x + r.width; x++) {
for (int y = r.y; y < r.y + r.height; y++) {
addTile(x, y);
}
}
}
// Remove airlocks that somehow are located between/inside rooms
Door[] doors = airlocks.toArray(new Door[0]);
for (Door d : doors) {
int x = d.getX(), y = d.getY();
// If the airlock is located on an edge that connects with
// a node with 8 connections, then it is between/inside a room
if (isInner(x, y) || (d.isHorizontal() && isInner(x + 1, y)) ||
(!d.isHorizontal() && isInner(x, y + 1))) {
airlocks.remove(d);
}
}
width = maxX - minX;
height = maxY - minY;
if (width < 0 || height < 0)
throw new IllegalArgumentException("Error -- ship dimensions are negative");
}
Layout(ShipLayout layout) {
airlocks = new ArrayList<Door>();
for (DoorLayoutObject door : layout.listDoors()) {
if (door.getLeftIndex() == -1 || door.getRightIndex() == -1) {
airlocks.add(new Door(door.getX(), door.getY(), door.getHorizontal() == 0));
}
}
tileGraph = new Graph();
int minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE, maxX = 0, maxY = 0;
for (RoomLayoutObject room : layout.listRooms()) {
// Find the min and max points of the layout to determine its size
if (room.getX() < minX)
minX = room.getX();
if (room.getY() < minY)
minY = room.getY();
if (room.getX() + room.getW() > maxX)
maxX = room.getX() + room.getW();
if (room.getY() + room.getH() > maxY)
maxY = room.getY() + room.getH();
// Add the tiles constituting the room to the graph
for (int x = room.getX(); x < room.getX() + room.getW(); x++) {
for (int y = room.getY(); y < room.getY() + room.getH(); y++) {
addTile(x, y);
}
}
}
// Remove airlocks that somehow are located between/inside rooms
Door[] doors = airlocks.toArray(new Door[0]);
for (Door d : doors) {
int x = d.getX(), y = d.getY();
// If the airlock is located on an edge that connects with
// a node with 8 connections, then it is between/inside a room
if (isInner(x, y) || (d.isHorizontal() && isInner(x + 1, y)) ||
(!d.isHorizontal() && isInner(x, y + 1))) {
airlocks.remove(d);
}
}
width = maxX - minX;
height = maxY - minY;
if (width < 0 || height < 0)
throw new IllegalArgumentException("Error -- ship dimensions are negative");
}
/*
* Getters
*/
/**
* @return a point graph representing all tiles in this layout.
*/
protected Graph getTileGraph() {
return tileGraph;
}
/**
* @return a collection of all airlocks in this layout.
*/
protected List<Door> getAirlocks() {
return airlocks;
}
/**
* @return size of the layout in grid cells / tiles.
*/
protected Dimension getSize() {
return new Dimension(width, height);
}
/**
* @return width of the layout in grid cells / tiles.
*/
protected int getWidth() {
return width;
}
/**
* @return height of the layout in grid cells / tiles.
*/
protected int getHeight() {
return height;
}
/*
* =============
* Other methods
*/
/**
* @param x
* x coordinate of the tile at which the door is located
* @param y
* y coordinate of the tile at which the door is located
* @param horizontal
* whether the airlock has to be horizontal or vertical
*
* @return true if an airlock matching the conditions is found, false otherwise.
*/
protected boolean hasAirlockAt(int x, int y, boolean horizontal) {
for (Door d : airlocks) {
if (d.isHorizontal() == horizontal && d.getX() == x && d.getY() == y)
return true;
}
return false;
}
/**
* <pre>
* o --- N <-- corner node (N)
* / |
* / |
* o o
* </pre>
*/
protected boolean isCorner(Point p) {
return tileGraph.countEdges(p) == 3;
}
/** @see #isCorner(Point) */
protected boolean isCorner(int x, int y) {
return isCorner(new Point(x, y));
}
/**
* <pre>
* o o o
* \ | /
* \ | /
* o --- N --- o <-- dent node (N)
* / |
* / |
* o o
* </pre>
*/
protected boolean isDent(Point p) {
return tileGraph.countEdges(p) == 7;
}
/** @see #isDent(Point) */
protected boolean isDent(int x, int y) {
return isDent(new Point(x, y));
}
/**
* <pre>
* o o
* | /
* | /
* o --- N --- o <-- double dent node (N)
* / |
* / |
* o o
* </pre>
*/
protected boolean isDoubleDent(Point p) {
return tileGraph.countEdges(p) == 6;
}
/** @see #isDoubleDent(Point) */
protected boolean isDoubleDent(int x, int y) {
return isDoubleDent(new Point(x, y));
}
/**
* <pre>
* o o o
* \ | /
* \ | /
* o --- N --- o <-- inner node (N)
* / | \
* / | \
* o o o
* </pre>
*/
protected boolean isInner(Point p) {
return tileGraph.countEdges(p) == 8;
}
/** @see #isInner(Point) */
protected boolean isInner(int x, int y) {
return isInner(new Point(x, y));
}
/**
* <pre>
* o o
* \ |
* \ |
* o --- N <-- wall node (N)
* / |
* / |
* o o
* </pre>
*/
protected boolean isWall(Point p) {
return tileGraph.countEdges(p) == 5;
}
/** @see #isWall(Point) */
protected boolean isWall(int x, int y) {
return isWall(new Point(x, y));
}
/**
* Adds 6 edges representing a tile to the graph. The arguments specify top left corner of the tile.
*/
private void addTile(int x, int y) {
Point[] points = new Point[4];
points[0] = new Point(x, y); // topLeft
points[1] = new Point(x + 1, y); // topRight
points[2] = new Point(x, y + 1); // botLeft
points[3] = new Point(x + 1, y + 1); // botRight
tileGraph.addEdge(points[0], points[1]);
tileGraph.addEdge(points[0], points[2]);
tileGraph.addEdge(points[3], points[1]);
tileGraph.addEdge(points[3], points[2]);
tileGraph.addEdge(points[0], points[3]);
tileGraph.addEdge(points[1], points[2]);
}
}