Skip to content

Commit

Permalink
Starting a template-based DungeonGenerator
Browse files Browse the repository at this point in the history
  • Loading branch information
tukkek committed Feb 13, 2018
1 parent 285d452 commit d331265
Show file tree
Hide file tree
Showing 5 changed files with 388 additions and 0 deletions.
47 changes: 47 additions & 0 deletions javelin/controller/generator/dungeon/DungeonGenerator.java
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 javelin/controller/generator/dungeon/template/Direction.java
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 javelin/controller/generator/dungeon/template/Irregular.java
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 javelin/controller/generator/dungeon/template/Iterator.java
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 javelin/controller/generator/dungeon/template/Template.java
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;
}

}

0 comments on commit d331265

Please sign in to comment.