-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Starting a template-based DungeonGenerator
- Loading branch information
Showing
5 changed files
with
388 additions
and
0 deletions.
There are no files selected for viewing
47 changes: 47 additions & 0 deletions
47
javelin/controller/generator/dungeon/DungeonGenerator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package javelin.controller.generator.dungeon; | ||
|
||
import java.io.IOException; | ||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
|
||
import javelin.controller.db.Preferences; | ||
import javelin.controller.generator.dungeon.template.Irregular; | ||
import javelin.controller.generator.dungeon.template.Template; | ||
|
||
public class DungeonGenerator { | ||
/** Procedurally generated templates only. */ | ||
static final Template[] TEMPLATES = new Template[] { new Irregular() }; | ||
/** | ||
* How many times to generate each procedurally-generated {@link Template}. | ||
*/ | ||
static final int PERMUTATIONS = 10; | ||
|
||
ArrayList<Template> pool = new ArrayList<Template>(); | ||
String log = ""; | ||
|
||
public DungeonGenerator() { | ||
for (Template t : TEMPLATES) { | ||
for (int i = 0; i < PERMUTATIONS; i++) { | ||
pool.add(t.create()); | ||
} | ||
} | ||
Collections.shuffle(pool); | ||
for (Template t : pool) { | ||
log += t + "\n"; | ||
} | ||
write(); | ||
} | ||
|
||
void write() { // debug | ||
try { | ||
Preferences.write(log, "/tmp/dungeon.txt"); | ||
} catch (IOException e) { | ||
throw new RuntimeException(); | ||
} | ||
log = ""; | ||
} | ||
|
||
public static void main(String[] args) { | ||
new DungeonGenerator(); | ||
} | ||
} |
70 changes: 70 additions & 0 deletions
70
javelin/controller/generator/dungeon/template/Direction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package javelin.controller.generator.dungeon.template; | ||
|
||
import java.util.ArrayList; | ||
|
||
import javelin.controller.Point; | ||
import tyrant.mikera.engine.RPG; | ||
|
||
public abstract class Direction { | ||
public static final Direction NORTH = new Direction("North", 0, -1) { | ||
@Override | ||
ArrayList<Point> getborder(Template t) { | ||
ArrayList<Point> border = new ArrayList<Point>(); | ||
for (int x = 0; x < t.width; x++) { | ||
border.add(new Point(x, 0)); | ||
} | ||
return border; | ||
} | ||
}; | ||
public static final Direction EAST = new Direction("East", +1, 0) { | ||
@Override | ||
ArrayList<Point> getborder(Template t) { | ||
ArrayList<Point> border = new ArrayList<Point>(); | ||
for (int y = 0; y < t.width; y++) { | ||
border.add(new Point(t.width - 1, y)); | ||
} | ||
return border; | ||
} | ||
}; | ||
public static final Direction SOUTH = new Direction("South", 0, +1) { | ||
@Override | ||
ArrayList<Point> getborder(Template t) { | ||
ArrayList<Point> border = new ArrayList<Point>(); | ||
for (int x = 0; x < t.width; x++) { | ||
border.add(new Point(x, t.height - 1)); | ||
} | ||
return border; | ||
} | ||
}; | ||
public static final Direction WEST = new Direction("West", -1, 0) { | ||
@Override | ||
ArrayList<Point> getborder(Template t) { | ||
ArrayList<Point> border = new ArrayList<Point>(); | ||
for (int y = 0; y < t.width; y++) { | ||
border.add(new Point(0, y)); | ||
} | ||
return border; | ||
} | ||
}; | ||
public static final Direction[] DIRECTIONS = new Direction[] { NORTH, SOUTH, | ||
WEST, EAST }; | ||
|
||
public Point delta; | ||
public String name; | ||
|
||
private Direction(String name, int x, int y) { | ||
this.name = name; | ||
delta = new Point(x, y); | ||
} | ||
|
||
abstract ArrayList<Point> getborder(Template t); | ||
|
||
public static Direction getrandom() { | ||
return DIRECTIONS[RPG.r(0, 3)]; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return name; | ||
} | ||
} |
76 changes: 76 additions & 0 deletions
76
javelin/controller/generator/dungeon/template/Irregular.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package javelin.controller.generator.dungeon.template; | ||
|
||
import java.util.Collections; | ||
import java.util.LinkedList; | ||
|
||
import javelin.controller.Point; | ||
import tyrant.mikera.engine.RPG; | ||
|
||
public class Irregular extends Template { | ||
private static final Point[] ADJACENT = new Point[] { new Point(-1, 0), | ||
new Point(+1, 0), new Point(0, -1), new Point(0, +1) }; | ||
private static final int PERCENTMIN = 30; | ||
private static final int PERCENTMAX = 70; | ||
|
||
@Override | ||
public void generate() { | ||
width = 0; | ||
while (width < 3 || height < 3) { | ||
initrandom(); | ||
} | ||
double ratio = RPG.r(PERCENTMIN, PERCENTMAX) / 100.0; | ||
LinkedList<Point> expand = getborders(); | ||
Collections.shuffle(expand); | ||
for (int i = 0; i < expand.size() * ratio; i++) { | ||
Point border = expand.get(i); | ||
tiles[border.x][border.y] = WALL; | ||
} | ||
for (int count = count(WALL); !expand.isEmpty() | ||
&& count < getarea() * ratio; count = count(WALL)) { | ||
Point p = RPG.pick(expand); | ||
expand.remove(p); | ||
if (checkblock(p)) { | ||
continue; | ||
} | ||
tiles[p.x][p.y] = WALL; | ||
for (Point adjacent : new Point[] { new Point(p.x - 1, p.y), | ||
new Point(p.x + 1, p.y), new Point(p.x, p.y - 1), | ||
new Point(p.x, p.y + 1) }) { | ||
if (adjacent.validate(0, 0, width, height) | ||
&& tiles[adjacent.x][adjacent.y] != WALL) { | ||
expand.add(adjacent); | ||
} | ||
} | ||
} | ||
} | ||
|
||
void initrandom() { | ||
init(RPG.r(3, 7), RPG.r(1, 6)); | ||
} | ||
|
||
boolean checkblock(Point p) { | ||
int walls = 0; | ||
for (Point wall : ADJACENT) { | ||
wall = new Point(p.x + wall.x, p.y + wall.y); | ||
if (!wall.validate(0, 0, width, height) | ||
|| tiles[wall.x][wall.y] == WALL) { | ||
walls += 1; | ||
} | ||
} | ||
return walls >= 2; | ||
} | ||
|
||
LinkedList<Point> getborders() { | ||
final LinkedList<Point> borders = new LinkedList<Point>(); | ||
iterate(new Iterator() { | ||
@Override | ||
public void iterate(TemplateTile t) { | ||
if (isborder(t.x, t.y)) { | ||
borders.add(new Point(t.x, t.y)); | ||
} | ||
} | ||
|
||
}); | ||
return borders; | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
javelin/controller/generator/dungeon/template/Iterator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package javelin.controller.generator.dungeon.template; | ||
|
||
public interface Iterator { | ||
public class TemplateTile { | ||
public int x; | ||
public int y; | ||
public char c; | ||
|
||
public TemplateTile(int x, int y, char c) { | ||
this.x = x; | ||
this.y = y; | ||
this.c = c; | ||
} | ||
} | ||
|
||
void iterate(TemplateTile t); | ||
} |
178 changes: 178 additions & 0 deletions
178
javelin/controller/generator/dungeon/template/Template.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
package javelin.controller.generator.dungeon.template; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.Collections; | ||
|
||
import javelin.controller.Point; | ||
import javelin.controller.generator.dungeon.template.Iterator.TemplateTile; | ||
import tyrant.mikera.engine.RPG; | ||
|
||
public abstract class Template implements Cloneable { | ||
public static final char FLOOR = '.'; | ||
public static final char WALL = '#'; | ||
public static final char DECORATION = '!'; | ||
public static final char DOOR = '+'; | ||
|
||
public char[][] tiles = null; | ||
public int width = 0; | ||
public int height = 0; | ||
// public boolean hasdecoration; | ||
|
||
void init(int width, int height) { | ||
this.width = width; | ||
this.height = height; | ||
tiles = new char[width][height]; | ||
for (int x = 0; x < width; x++) { | ||
for (int y = 0; y < height; y++) { | ||
tiles[x][y] = FLOOR; | ||
} | ||
} | ||
} | ||
|
||
public abstract void generate(); | ||
|
||
public void modify() { | ||
if (RPG.chancein(2)) { | ||
rotate(); | ||
} | ||
if (RPG.chancein(2)) { | ||
mirrorhorizontally(); | ||
} | ||
if (RPG.chancein(2)) { | ||
mirrorvertically(); | ||
} | ||
} | ||
|
||
private void mirrorvertically() { | ||
for (int x = 0; x < width; x++) { | ||
char[] original = Arrays.copyOf(tiles[x], height); | ||
for (int y = 0; y < height; y++) { | ||
tiles[x][height - 1 - y] = original[y]; | ||
} | ||
} | ||
} | ||
|
||
void mirrorhorizontally() { | ||
char[][] original = Arrays.copyOf(tiles, width); | ||
for (int x = 0; x < width; x++) { | ||
tiles[width - x - 1] = original[x]; | ||
} | ||
} | ||
|
||
void rotate() { | ||
char[][] rotated = new char[height][width]; | ||
for (int x = 0; x < width; x++) { | ||
for (int y = 0; y < height; y++) { | ||
rotated[y][x] = tiles[x][y]; | ||
} | ||
} | ||
tiles = rotated; | ||
Point dimensions = new Point(width, height); | ||
width = dimensions.y; | ||
height = dimensions.x; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
String s = ""; | ||
for (int x = 0; x < width; x++) { | ||
for (int y = 0; y < height; y++) { | ||
s += tiles[x][y]; | ||
} | ||
s += "\n"; | ||
} | ||
return s; | ||
} | ||
|
||
public void iterate(Iterator i) { | ||
for (int x = 0; x < width; x++) { | ||
for (int y = 0; y < height; y++) { | ||
i.iterate(new TemplateTile(x, y, tiles[x][y])); | ||
} | ||
} | ||
} | ||
|
||
protected double getarea() { | ||
return width * height; | ||
} | ||
|
||
protected int count(char tile) { | ||
int count = 0; | ||
int w = width; | ||
int h = height; | ||
for (int x = 0; x < w; x++) { | ||
for (int y = 0; y < h; y++) { | ||
if (tiles[x][y] == tile) { | ||
count += 1; | ||
} | ||
} | ||
} | ||
return count; | ||
} | ||
|
||
public Template create() { | ||
generate(); | ||
modify(); | ||
close(); | ||
makedoors(); | ||
return clone(); | ||
} | ||
|
||
void makedoors() { | ||
int doors = RPG.r(1, 4); | ||
for (int i = 0; i < doors; i++) { | ||
Direction direction = Direction.getrandom(); | ||
Point door = findentry(direction); | ||
if (door != null) { | ||
tiles[door.x][door.y] = DOOR; | ||
continue; | ||
} | ||
if (count(DOOR) != 0) { | ||
return; | ||
} | ||
i -= 1; | ||
} | ||
} | ||
|
||
Point findentry(Direction d) { | ||
ArrayList<Point> doors = d.getborder(this); | ||
Collections.shuffle(doors); | ||
for (Point door : doors) { | ||
if (tiles[door.x - d.delta.x][door.y - d.delta.y] == FLOOR) { | ||
return door; | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
@Override | ||
protected Template clone() { | ||
try { | ||
return (Template) super.clone(); | ||
} catch (CloneNotSupportedException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
public void close() { | ||
width += 2; | ||
height += 2; | ||
char[][] closed = new char[width + 2][height + 2]; | ||
for (int x = 0; x < width; x++) { | ||
for (int y = 0; y < height; y++) { | ||
if (isborder(x, y)) { | ||
closed[x][y] = WALL; | ||
} else { | ||
closed[x][y] = tiles[x - 1][y - 1]; | ||
} | ||
} | ||
} | ||
tiles = closed; | ||
} | ||
|
||
protected boolean isborder(int x, int y) { | ||
return x == 0 || y == 0 || x == width - 1 || y == height - 1; | ||
} | ||
|
||
} |