diff --git a/src/mightypork/rogue/screens/gamescreen/world/MapView.java b/src/mightypork/rogue/screens/gamescreen/world/MapView.java index c2ddcb8..a62715c 100644 --- a/src/mightypork/rogue/screens/gamescreen/world/MapView.java +++ b/src/mightypork/rogue/screens/gamescreen/world/MapView.java @@ -31,7 +31,7 @@ public class MapView extends InputComponent implements KeyListener, MouseButtonL public MapView(World world) { this.world = world; - this.worldRenderer = new WorldRenderer(world, this, 8, 6, 72); + this.worldRenderer = new WorldRenderer(world, this, 8, 8, 64); pc = world.getPlayerControl(); pc.addMoveListener(this); } diff --git a/src/mightypork/rogue/world/MapGenerator.java b/src/mightypork/rogue/world/MapGenerator.java index 6e9fc48..12dc893 100644 --- a/src/mightypork/rogue/world/MapGenerator.java +++ b/src/mightypork/rogue/world/MapGenerator.java @@ -3,6 +3,7 @@ package mightypork.rogue.world; import java.util.Random; +import mightypork.rogue.world.gen.LevelGenerator; import mightypork.rogue.world.level.Level; import mightypork.rogue.world.tile.Tiles; import mightypork.rogue.world.tile.models.TileModel; @@ -21,33 +22,15 @@ public class MapGenerator { final World w = new World(); w.setSeed(seed); - w.addLevel(createLevel(rand.nextLong(), Tiles.FLOOR_DARK, Tiles.WALL_BRICK)); - //w.addLevel(createLevel(rand.nextLong(), Tiles.BRCOBBLE_FLOOR, Tiles.BRCOBBLE_WALL)); + Level l; + + // first level + l = LevelGenerator.build(rand.nextLong(), LevelGenerator.DUNGEON_THEME); + w.addLevel(l); + w.createPlayer(l.getEnterPoint(), 0); - // TODO place on start position - w.createPlayer(10, 10, 0); return w; } } - - private static Level createLevel(long seed, TileModel floor, TileModel wall) - { - // TODO - - final Level lm = new Level(20, 20); - lm.setSeed(seed); - - lm.fill(floor); - - final Random rand = new Random(); - rand.setSeed(seed); - - for (int i = 0; i < 150; i++) { - lm.setTile(wall, rand.nextInt(20), rand.nextInt(20)); - } - - return lm; - } - } diff --git a/src/mightypork/rogue/world/World.java b/src/mightypork/rogue/world/World.java index 98f6392..85e90e2 100644 --- a/src/mightypork/rogue/world/World.java +++ b/src/mightypork/rogue/world/World.java @@ -84,7 +84,12 @@ public class World implements IonBundled, Updateable { { return eid++; } - + + + public void createPlayer(WorldPos pos, int level) + { + createPlayer(pos.x, pos.y, level); + } public void createPlayer(int x, int y, int level) { @@ -120,4 +125,5 @@ public class World implements IonBundled, Updateable { { return playerEntity; } + } diff --git a/src/mightypork/rogue/world/WorldPos.java b/src/mightypork/rogue/world/WorldPos.java index 44a38df..05b1a79 100644 --- a/src/mightypork/rogue/world/WorldPos.java +++ b/src/mightypork/rogue/world/WorldPos.java @@ -107,6 +107,11 @@ public class WorldPos implements IonBundled, Updateable { setTo(other.x, other.y); } + @Override + public String toString() + { + return "WorldPos("+x+","+y+")"; + } @Override public int hashCode() diff --git a/src/mightypork/rogue/world/gen/Coord.java b/src/mightypork/rogue/world/gen/Coord.java new file mode 100644 index 0000000..cc75210 --- /dev/null +++ b/src/mightypork/rogue/world/gen/Coord.java @@ -0,0 +1,57 @@ +package mightypork.rogue.world.gen; + + +import mightypork.util.annotations.FactoryMethod; + + +// coord +public class Coord { + + public int x; + public int y; + + + @FactoryMethod + public static Coord make(int x, int y) + { + return new Coord(x, y); + } + + @FactoryMethod + public static Coord make(Coord other) + { + return new Coord(other); + } + + public Coord(int x, int y) + { + super(); + this.x = x; + this.y = y; + } + + + public Coord(Coord other) + { + this.x = other.x; + this.y = other.y; + } + + + public Coord add(int addX, int addY) + { + return new Coord(x + addX, y + addY); + } + + + public Coord copy() + { + return make(this); + } + + @Override + public String toString() + { + return "Coord("+x+","+y+")"; + } +} diff --git a/src/mightypork/rogue/world/gen/LevelGenerator.java b/src/mightypork/rogue/world/gen/LevelGenerator.java new file mode 100644 index 0000000..6317d6c --- /dev/null +++ b/src/mightypork/rogue/world/gen/LevelGenerator.java @@ -0,0 +1,48 @@ +package mightypork.rogue.world.gen; + + +import java.util.Random; + +import mightypork.rogue.world.WorldPos; +import mightypork.rogue.world.gen.rooms.IntersectionRoom; +import mightypork.rogue.world.gen.rooms.SquareRoom; +import mightypork.rogue.world.gen.themes.ThemeDungeon; +import mightypork.rogue.world.level.Level; +import mightypork.rogue.world.tile.Tile; +import mightypork.rogue.world.tile.Tiles; +import mightypork.util.logging.Log; + + +public class LevelGenerator { + + public static final Theme DUNGEON_THEME = new ThemeDungeon(); + + public static final RoomBuilder ROOM_SQUARE = new SquareRoom(); + public static final RoomBuilder ROOM_INTERSECTION = new IntersectionRoom(); + + + public static Level build(long seed, Theme theme) + { + Random rand = new Random(seed + 47); + + final int max_size = 500; + + ScratchMap map = new ScratchMap(max_size, theme, rand); + + // start + map.addRoom(ROOM_SQUARE); + + for (int i = 0; i < 5+rand.nextInt(4); i++) { + map.addRoom(ROOM_SQUARE); + for(int j=0;j<4;j++) map.addRoom(ROOM_INTERSECTION); + } + + + Coord size = map.getNeededSize(); + Level lvl = new Level(size.x, size.y); + + map.writeToLevel(lvl); + + return lvl; + } +} diff --git a/src/mightypork/rogue/world/gen/RoomBuilder.java b/src/mightypork/rogue/world/gen/RoomBuilder.java new file mode 100644 index 0000000..67222aa --- /dev/null +++ b/src/mightypork/rogue/world/gen/RoomBuilder.java @@ -0,0 +1,12 @@ +package mightypork.rogue.world.gen; + +import java.util.List; +import java.util.Random; + +import mightypork.rogue.world.tile.Tile; + +// room builder interface +public interface RoomBuilder { + + RoomDesc buildToFit(ScratchMap map, Theme theme, Random rand, Coord center); +} \ No newline at end of file diff --git a/src/mightypork/rogue/world/gen/RoomDesc.java b/src/mightypork/rogue/world/gen/RoomDesc.java new file mode 100644 index 0000000..44decba --- /dev/null +++ b/src/mightypork/rogue/world/gen/RoomDesc.java @@ -0,0 +1,56 @@ +package mightypork.rogue.world.gen; + + +import java.util.ArrayList; +import java.util.List; + + +// room info +public class RoomDesc { + + final List doors = new ArrayList<>(); + final Coord min; + final Coord max; + + + public RoomDesc(Coord min, Coord max, List doors) + { + super(); + this.min = min; + this.max = max; + + this.doors.addAll(doors); + } + + + public boolean intersectsWith(Coord amin, Coord amax) + { + int tw = max.x - min.x; + int th = max.y - min.y; + int rw = amax.x - amin.x; + int rh = amax.y - amin.y; + + if (rw <= 0 || rh <= 0 || tw <= 0 || th <= 0) { + return false; + } + + final int tx = min.x; + final int ty = min.y; + final int rx = amin.x; + final int ry = amin.y; + + rw += rx; + rh += ry; + tw += tx; + th += ty; + + return ((rw <= rx || rw >= tx) && (rh <= ry || rh >= ty) && (tw <= tx || tw >= rx) && (th <= ty || th >= ry)); + } + + + @Override + public String toString() + { + return "Room [" + min.x + "," + min.y + " .. " + max.x + "," + max.y + "]"; + } +} diff --git a/src/mightypork/rogue/world/gen/ScratchMap.java b/src/mightypork/rogue/world/gen/ScratchMap.java new file mode 100644 index 0000000..4d6c600 --- /dev/null +++ b/src/mightypork/rogue/world/gen/ScratchMap.java @@ -0,0 +1,188 @@ +package mightypork.rogue.world.gen; + + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import mightypork.rogue.world.WorldPos; +import mightypork.rogue.world.level.Level; +import mightypork.rogue.world.tile.Tile; +import mightypork.rogue.world.tile.Tiles; +import mightypork.rogue.world.tile.models.TileModel; + + +public class ScratchMap { + + private Tile[][] map; + private int width; + private int height; + + private List rooms = new ArrayList<>(); + + Coord genMin; + Coord genMax; + private Theme theme; + private Random rand; + private Coord enterPoint; + + + public ScratchMap(int max_size, Theme theme, Random rand) + { + map = new Tile[max_size][max_size]; + + genMin = Coord.make(max_size / 2, max_size / 2); + genMax = genMin.add(1, 1); + + width = max_size; + height = max_size; + this.rand = rand; + this.theme = theme; + + fill(Coord.make(0, 0), Coord.make(width - 1, height - 1), Tiles.NULL_EMPTY); + } + + + public void addRoom(RoomBuilder rb) + { + Coord center = Coord.make(0, 0); + + int failed = 0; + + while (true) { + center.x = genMin.x + rand.nextInt(genMax.x - genMin.x); + center.y = genMin.y + rand.nextInt(genMax.y - genMin.y); + + RoomDesc rd = rb.buildToFit(this, theme, rand, center); + if (rd != null) { + if (rooms.isEmpty()) { + enterPoint = center.copy(); + } + + rooms.add(rd); + + genMin.x = Math.min(genMin.x, rd.min.x); + genMin.y = Math.min(genMin.y, rd.min.y); + + genMax.x = Math.max(genMax.x, rd.max.x); + genMax.y = Math.max(genMax.y, rd.max.y); + + return; + } else { + failed++; + if (failed % 5 == 0) { + switch(rand.nextInt(4)) { + case 0: genMin.x--; break; + case 1: genMin.y--; break; + case 2: genMax.x++; break; + case 3: genMax.y++; break; + } + } + + if (failed > 200) { + return; + } + } + } + + } + + + public boolean isIn(Coord pos) + { + return pos.x >= 0 && pos.x < width && pos.y >= 0 && pos.y < height; + } + + + public Tile get(Coord pos) + { + if (!isIn(pos)) { + return Tiles.NULL_SOLID.createTile(); + } + + return map[pos.y][pos.x]; + } + + + public boolean set(Coord pos, TileModel tm) + { + return set(pos, tm.createTile()); + } + + + public boolean set(Coord pos, Tile tile) + { + if (!isIn(pos)) { + return false; + } + + map[pos.y][pos.x] = tile; + return true; + } + + + public boolean canBuild(Coord pos) + { + if (!isIn(pos)) return false; + TileModel tm = get(pos).getModel(); + return tm.isNullTile() && tm.isWalkable(); + } + + + public boolean isClear(Coord min, Coord max) + { + if (!isIn(min)) return false; + if (!isIn(max)) return false; + + for (RoomDesc r : rooms) { + if (r.intersectsWith(min, max)) return false; + } + + return true; + } + + + public void fill(Coord min, Coord max, TileModel tm) + { + Coord c = Coord.make(0, 0); + for (c.y = min.y; c.y <= max.y; c.y++) + for (c.x = min.x; c.x <= max.x; c.x++) + set(c, tm.createTile()); + } + + + public void border(Coord min, Coord max, TileModel tm) + { + Coord c = Coord.make(0, 0); + for (c.y = min.y; c.y <= max.y; c.y++) { + for (c.x = min.x; c.x <= max.x; c.x++) { + + if (c.y > min.y && c.y < max.y && c.x > min.x && c.x < max.x) continue; + + set(c, tm.createTile()); + } + } + } + + + public Coord getNeededSize() + { + return Coord.make(genMax.x - genMin.x + 1, genMax.y - genMin.y + 1); + } + + + public void writeToLevel(Level level) + { + + Coord c1 = Coord.make(0, 0); + Coord c = Coord.make(0, 0); + for (c.x = genMin.x, c1.x = 0; c.x <= genMax.x; c.x++, c1.x++) { + for (c.y = genMin.y, c1.y = 0; c.y <= genMax.y; c.y++, c1.y++) { + level.setTile(get(c), c1.x, c1.y); + } + } + + WorldPos p = new WorldPos(enterPoint.x - genMin.x, enterPoint.y - genMin.y); + level.setEnterPoint(p); + } +} diff --git a/src/mightypork/rogue/world/gen/Theme.java b/src/mightypork/rogue/world/gen/Theme.java new file mode 100644 index 0000000..31432d5 --- /dev/null +++ b/src/mightypork/rogue/world/gen/Theme.java @@ -0,0 +1,15 @@ +package mightypork.rogue.world.gen; + +import mightypork.rogue.world.tile.models.TileModel; + +// map theme +public interface Theme { + + TileModel wall(); + + + TileModel floor(); + + + TileModel door(); +} \ No newline at end of file diff --git a/src/mightypork/rogue/world/gen/rooms/IntersectionRoom.java b/src/mightypork/rogue/world/gen/rooms/IntersectionRoom.java new file mode 100644 index 0000000..ca52c73 --- /dev/null +++ b/src/mightypork/rogue/world/gen/rooms/IntersectionRoom.java @@ -0,0 +1,49 @@ +package mightypork.rogue.world.gen.rooms; + + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import mightypork.rogue.world.gen.Coord; +import mightypork.rogue.world.gen.RoomBuilder; +import mightypork.rogue.world.gen.RoomDesc; +import mightypork.rogue.world.gen.Theme; +import mightypork.rogue.world.gen.ScratchMap; + + +public class IntersectionRoom extends SquareRoom { + + @Override + protected int getMinHalfSide() + { + return 1; + } + + + @Override + protected int getMaxHalfSide() + { + return 1; + } + + + protected int[] getDoorTypes() + { + //@formatter:off + return new int[] { + // dead ends + 0b1000,0b0100,0b0010,0b0001, + + // corridor pieces + 0b0011, 0b0101, 0b0110, 0b1010, 0b1100, 0b1001, + + // crossings + 0b0111, 0b1101, 0b1011, 0b1110, 0b1111, + + // repeat to get more + 0b0111, 0b1101, 0b1011, 0b1110, 0b1111, + }; + //@formatter:on + } +} diff --git a/src/mightypork/rogue/world/gen/rooms/SquareRoom.java b/src/mightypork/rogue/world/gen/rooms/SquareRoom.java new file mode 100644 index 0000000..ff93e62 --- /dev/null +++ b/src/mightypork/rogue/world/gen/rooms/SquareRoom.java @@ -0,0 +1,98 @@ +package mightypork.rogue.world.gen.rooms; + + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import mightypork.rogue.world.gen.Coord; +import mightypork.rogue.world.gen.RoomBuilder; +import mightypork.rogue.world.gen.RoomDesc; +import mightypork.rogue.world.gen.Theme; +import mightypork.rogue.world.gen.ScratchMap; + + +public class SquareRoom implements RoomBuilder { + + @Override + public RoomDesc buildToFit(ScratchMap map, Theme theme, Random rand, Coord center) + { + int hside = getMinHalfSide(); + if (getMaxHalfSide() > getMinHalfSide()) hside += rand.nextInt(getMaxHalfSide() - getMinHalfSide()); + + Coord min = new Coord(center.x - hside, center.y - hside); + Coord max = new Coord(center.x + hside, center.y + hside); + + for (;; hside--) { + if (hside < getMinHalfSide()) return null; + if (map.isClear(min, max)) break; + } + + map.fill(min, max, theme.floor()); + map.border(min, max, theme.wall()); + + List doors = new ArrayList<>(); + + int door_types[] = getDoorTypes(); + + int drs = door_types[rand.nextInt(door_types.length)]; + + Coord door; + + if ((drs & 1) != 0) { + door = min.add(hside, 0); + map.set(door, theme.door()); + doors.add(door); + } + + if ((drs & 2) != 0) { + door = max.add(-hside, 0); + map.set(door, theme.door()); + } + + if ((drs & 4) != 0) { + door = min.add(0, hside); + map.set(door, theme.door()); + } + + if ((drs & 8) != 0) { + door = max.add(0, -hside); + map.set(door, theme.door()); + } + + return new RoomDesc(min.add(-1, -1), max.add(1, 1), doors); + } + + + protected int[] getDoorTypes() + { + //@formatter:off + return new int[] { + // one + 0b0001, 0b0010, 0b0100, 0b1000, + + // two + 0b0011, 0b0101, 0b0110, 0b1010, 0b1100, 0b1001, + 0b0011, 0b0101, 0b0110, 0b1010, 0b1100, 0b1001, + + //three+four + 0b0111, 0b1101, 0b1011, 0b1110, 0b1111, + 0b0111, 0b1101, 0b1011, 0b1110, 0b1111, + 0b0111, 0b1101, 0b1011, 0b1110, 0b1111 + }; + //@formatter:on + } + + + protected int getMinHalfSide() + { + return 2; + } + + + protected int getMaxHalfSide() + { + return 4; + } + +} diff --git a/src/mightypork/rogue/world/gen/themes/ThemeDungeon.java b/src/mightypork/rogue/world/gen/themes/ThemeDungeon.java new file mode 100644 index 0000000..e41e8d7 --- /dev/null +++ b/src/mightypork/rogue/world/gen/themes/ThemeDungeon.java @@ -0,0 +1,29 @@ +package mightypork.rogue.world.gen.themes; + +import mightypork.rogue.world.gen.Theme; +import mightypork.rogue.world.tile.Tiles; +import mightypork.rogue.world.tile.models.TileModel; + +// basic dungeon theme +public class ThemeDungeon implements Theme { + + @Override + public TileModel wall() + { + return Tiles.WALL_BRICK; + } + + + @Override + public TileModel floor() + { + return Tiles.FLOOR_DARK; + } + + @Override + public TileModel door() + { + return floor(); // TODO + } + +} \ No newline at end of file diff --git a/src/mightypork/rogue/world/level/Level.java b/src/mightypork/rogue/world/level/Level.java index 1687d08..286dc46 100644 --- a/src/mightypork/rogue/world/level/Level.java +++ b/src/mightypork/rogue/world/level/Level.java @@ -9,6 +9,7 @@ import java.util.Map; import java.util.Set; import mightypork.rogue.world.World; +import mightypork.rogue.world.WorldPos; import mightypork.rogue.world.entity.Entity; import mightypork.rogue.world.tile.Tile; import mightypork.rogue.world.tile.Tiles; @@ -31,6 +32,8 @@ public class Level implements MapAccess, IonBinary { private int width, height; + private WorldPos enterPoint; + /** Array of tiles [y][x] */ private Tile[][] tiles; @@ -278,4 +281,16 @@ public class Level implements MapAccess, IonBinary { { return entity_set; } + + + public void setEnterPoint(WorldPos enterPoint) + { + this.enterPoint = enterPoint; + } + + + public WorldPos getEnterPoint() + { + return enterPoint; + } } diff --git a/src/mightypork/rogue/world/tile/Tiles.java b/src/mightypork/rogue/world/tile/Tiles.java index d3bcb06..3706f78 100644 --- a/src/mightypork/rogue/world/tile/Tiles.java +++ b/src/mightypork/rogue/world/tile/Tiles.java @@ -19,9 +19,10 @@ public final class Tiles { public static final TileModel NULL_SOLID = new NullWall(0); public static final TileModel NULL_EMPTY = new NullFloor(1); + public static final TileModel NULL_EMPTY_RESERVED = new NullFloor(2); - public static final TileModel FLOOR_DARK = new Floor(2).setTexture("tile16.floor.dark"); - public static final TileModel WALL_BRICK = new Wall(3).setTexture("tile16.wall.brick"); + public static final TileModel FLOOR_DARK = new Floor(10).setTexture("tile16.floor.dark"); + public static final TileModel WALL_BRICK = new Wall(11).setTexture("tile16.wall.brick"); // public static final TileModel BRICK_FLOOR_VINES = new Floor(2).setTexture("tile.floor.mossy_bricks"); diff --git a/src/mightypork/util/math/constraints/rect/builders/TiledRect.java b/src/mightypork/util/math/constraints/rect/builders/TiledRect.java index cd269a1..66a95e2 100644 --- a/src/mightypork/util/math/constraints/rect/builders/TiledRect.java +++ b/src/mightypork/util/math/constraints/rect/builders/TiledRect.java @@ -59,11 +59,11 @@ public class TiledRect extends RectProxy { public Rect tile(int x, int y) { if (x >= tilesX || x < 0) { - throw new IndexOutOfBoundsException("X coordinate out fo range."); + throw new IndexOutOfBoundsException("X coordinate out fo range: "+x); } if (y >= tilesY || y < 0) { - throw new IndexOutOfBoundsException("Y coordinate out of range."); + throw new IndexOutOfBoundsException("Y coordinate out of range: "+y); } return aTile.move(perCol.mul(x), perRow.mul(y));