From eef373eb2495eb37027462073f04c39c81f03ffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Thu, 24 Apr 2014 21:08:54 +0200 Subject: [PATCH] A* path finding, room & corridor generation, pathfinding mouse control --- res/img/tiles16.png | Bin 3744 -> 4614 bytes .../gamecore/render/DisplaySystem.java | 3 +- src/mightypork/rogue/Res.java | 37 ++-- .../rogue/screens/gamescreen/ScreenGame.java | 4 +- .../rogue/screens/gamescreen/WorldLayer.java | 5 +- .../gamescreen/world/MIPClickPathfWalk.java | 38 ++++ .../screens/gamescreen/world/MIPKeyWalk.java | 4 +- .../gamescreen/world/MIPMouseWalk.java | 6 +- .../world/MapInteractionPlugin.java | 2 +- .../screens/gamescreen/world/MapView.java | 4 +- .../rogue/world/{gen => }/Coord.java | 40 +++- src/mightypork/rogue/world/PathStep.java | 7 + src/mightypork/rogue/world/PlayerControl.java | 46 ++++ src/mightypork/rogue/world/World.java | 7 +- .../{MapGenerator.java => WorldCreator.java} | 4 +- src/mightypork/rogue/world/WorldPos.java | 10 +- src/mightypork/rogue/world/entity/Entity.java | 6 + .../rogue/world/gen/LevelGenerator.java | 25 +-- .../rogue/world/gen/RoomBuilder.java | 15 +- src/mightypork/rogue/world/gen/RoomDesc.java | 14 +- .../rogue/world/gen/ScratchMap.java | 177 +++++++++++---- src/mightypork/rogue/world/gen/Theme.java | 16 +- .../world/gen/rooms/IntersectionRoom.java | 49 ----- .../rogue/world/gen/rooms/SimpleRectRoom.java | 35 +++ .../rogue/world/gen/rooms/SquareRoom.java | 98 --------- .../rogue/world/gen/themes/ThemeDungeon.java | 5 +- .../world/pathfinding/DiagonalHeuristic.java | 14 ++ .../rogue/world/pathfinding/Heuristic.java | 17 ++ .../world/pathfinding/ManhattanHeuristic.java | 14 ++ .../world/pathfinding/PathCostProvider.java | 30 +++ .../rogue/world/pathfinding/PathFinder.java | 205 ++++++++++++++++++ src/mightypork/rogue/world/tile/Tile.java | 6 +- .../rogue/world/tile/TileRenderData.java | 14 ++ src/mightypork/rogue/world/tile/Tiles.java | 14 +- .../world/tile/models/AbstractNullTile.java | 4 - .../rogue/world/tile/models/Floor.java | 5 +- .../rogue/world/tile/models/NullFloor.java | 5 +- .../rogue/world/tile/models/NullWall.java | 6 +- .../rogue/world/tile/models/SimpleDoor.java | 34 +++ .../rogue/world/tile/models/SimpleTile.java | 10 +- .../rogue/world/tile/models/TileModel.java | 17 +- .../rogue/world/tile/models/Wall.java | 5 +- .../tile/renderers/BasicTileRenderer.java | 58 ----- .../world/tile/renderers/DoorRenderer.java | 38 ++++ .../world/tile/renderers/FloorRenderer.java | 81 +++++++ .../world/tile/renderers/WallRenderer.java | 11 + src/mightypork/test/TestPerlin.java | 33 --- .../constraints/rect/builders/TiledRect.java | 4 +- 48 files changed, 881 insertions(+), 401 deletions(-) create mode 100644 src/mightypork/rogue/screens/gamescreen/world/MIPClickPathfWalk.java rename src/mightypork/rogue/world/{gen => }/Coord.java (51%) rename src/mightypork/rogue/world/{MapGenerator.java => WorldCreator.java} (82%) delete mode 100644 src/mightypork/rogue/world/gen/rooms/IntersectionRoom.java create mode 100644 src/mightypork/rogue/world/gen/rooms/SimpleRectRoom.java delete mode 100644 src/mightypork/rogue/world/gen/rooms/SquareRoom.java create mode 100644 src/mightypork/rogue/world/pathfinding/DiagonalHeuristic.java create mode 100644 src/mightypork/rogue/world/pathfinding/Heuristic.java create mode 100644 src/mightypork/rogue/world/pathfinding/ManhattanHeuristic.java create mode 100644 src/mightypork/rogue/world/pathfinding/PathCostProvider.java create mode 100644 src/mightypork/rogue/world/pathfinding/PathFinder.java create mode 100644 src/mightypork/rogue/world/tile/TileRenderData.java create mode 100644 src/mightypork/rogue/world/tile/models/SimpleDoor.java create mode 100644 src/mightypork/rogue/world/tile/renderers/DoorRenderer.java create mode 100644 src/mightypork/rogue/world/tile/renderers/FloorRenderer.java create mode 100644 src/mightypork/rogue/world/tile/renderers/WallRenderer.java delete mode 100644 src/mightypork/test/TestPerlin.java diff --git a/res/img/tiles16.png b/res/img/tiles16.png index c972b97d7e07ea94a6d896f420c72acaeb52b44c..b32c953a40e6dd9d67ab9218bddaf635a5df3942 100644 GIT binary patch literal 4614 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 path = pf.findPathRelative(start, end); + + if (path == null) return; + + world.getPlayerEntity().cancelPath(); + world.getPlayerEntity().addSteps(path); + } + + public void addMoveListener(EntityMoveListener eml) { world.getPlayerEntity().addMoveListener(eml); diff --git a/src/mightypork/rogue/world/World.java b/src/mightypork/rogue/world/World.java index 85e90e2..e49a0fe 100644 --- a/src/mightypork/rogue/world/World.java +++ b/src/mightypork/rogue/world/World.java @@ -84,13 +84,14 @@ 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) { if (playerInfo.isInitialized()) { @@ -125,5 +126,5 @@ public class World implements IonBundled, Updateable { { return playerEntity; } - + } diff --git a/src/mightypork/rogue/world/MapGenerator.java b/src/mightypork/rogue/world/WorldCreator.java similarity index 82% rename from src/mightypork/rogue/world/MapGenerator.java rename to src/mightypork/rogue/world/WorldCreator.java index 12dc893..d7bc212 100644 --- a/src/mightypork/rogue/world/MapGenerator.java +++ b/src/mightypork/rogue/world/WorldCreator.java @@ -5,11 +5,9 @@ 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; -public class MapGenerator { +public class WorldCreator { public static final Random rand = new Random(); diff --git a/src/mightypork/rogue/world/WorldPos.java b/src/mightypork/rogue/world/WorldPos.java index 05b1a79..85b14da 100644 --- a/src/mightypork/rogue/world/WorldPos.java +++ b/src/mightypork/rogue/world/WorldPos.java @@ -107,12 +107,14 @@ public class WorldPos implements IonBundled, Updateable { setTo(other.x, other.y); } + @Override public String toString() { - return "WorldPos("+x+","+y+")"; + return "WorldPos(" + x + "," + y + ")"; } + @Override public int hashCode() { @@ -157,4 +159,10 @@ public class WorldPos implements IonBundled, Updateable { return walkOffset.isFinished(); } + + public Coord toCoord() + { + return new Coord(x, y); + } + } diff --git a/src/mightypork/rogue/world/entity/Entity.java b/src/mightypork/rogue/world/entity/Entity.java index 218b1a6..1e418fb 100644 --- a/src/mightypork/rogue/world/entity/Entity.java +++ b/src/mightypork/rogue/world/entity/Entity.java @@ -275,4 +275,10 @@ public final class Entity implements IonBinary, IonBundled, EntityMoveListener { { moveListeners.add(listener); } + + + public void addSteps(List path) + { + this.path.addAll(path); + } } diff --git a/src/mightypork/rogue/world/gen/LevelGenerator.java b/src/mightypork/rogue/world/gen/LevelGenerator.java index 6317d6c..525c1b6 100644 --- a/src/mightypork/rogue/world/gen/LevelGenerator.java +++ b/src/mightypork/rogue/world/gen/LevelGenerator.java @@ -3,43 +3,38 @@ 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.Coord; +import mightypork.rogue.world.gen.rooms.SimpleRectRoom; 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 final RoomBuilder ROOM_SQUARE = new SimpleRectRoom(); public static Level build(long seed, Theme theme) { - Random rand = new Random(seed + 47); + final Random rand = new Random(seed + 13); - final int max_size = 500; + final int max_size = 512; - ScratchMap map = new ScratchMap(max_size, theme, rand); + final ScratchMap map = new ScratchMap(max_size, theme, rand); // start map.addRoom(ROOM_SQUARE); - for (int i = 0; i < 5+rand.nextInt(4); i++) { + for (int i = 0; i < 6 + rand.nextInt(6); i++) { map.addRoom(ROOM_SQUARE); - for(int j=0;j<4;j++) map.addRoom(ROOM_INTERSECTION); } + map.buildCorridors(); - Coord size = map.getNeededSize(); - Level lvl = new Level(size.x, size.y); + final Coord size = map.getNeededSize(); + final Level lvl = new Level(size.x, size.y); map.writeToLevel(lvl); diff --git a/src/mightypork/rogue/world/gen/RoomBuilder.java b/src/mightypork/rogue/world/gen/RoomBuilder.java index 67222aa..fd5c57c 100644 --- a/src/mightypork/rogue/world/gen/RoomBuilder.java +++ b/src/mightypork/rogue/world/gen/RoomBuilder.java @@ -1,12 +1,17 @@ package mightypork.rogue.world.gen; -import java.util.List; + import java.util.Random; -import mightypork.rogue.world.tile.Tile; +import mightypork.rogue.world.Coord; + -// room builder interface -public interface RoomBuilder { +/** + * Room model + * + * @author MightyPork + */ +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 index 44decba..c1d3313 100644 --- a/src/mightypork/rogue/world/gen/RoomDesc.java +++ b/src/mightypork/rogue/world/gen/RoomDesc.java @@ -1,25 +1,25 @@ package mightypork.rogue.world.gen; -import java.util.ArrayList; -import java.util.List; +import mightypork.rogue.world.Coord; -// room info +/** + * Room description + * + * @author MightyPork + */ public class RoomDesc { - final List doors = new ArrayList<>(); final Coord min; final Coord max; - public RoomDesc(Coord min, Coord max, List doors) + public RoomDesc(Coord min, Coord max) { super(); this.min = min; this.max = max; - - this.doors.addAll(doors); } diff --git a/src/mightypork/rogue/world/gen/ScratchMap.java b/src/mightypork/rogue/world/gen/ScratchMap.java index 4d6c600..be475a7 100644 --- a/src/mightypork/rogue/world/gen/ScratchMap.java +++ b/src/mightypork/rogue/world/gen/ScratchMap.java @@ -5,25 +5,60 @@ import java.util.ArrayList; import java.util.List; import java.util.Random; +import mightypork.rogue.world.Coord; import mightypork.rogue.world.WorldPos; import mightypork.rogue.world.level.Level; +import mightypork.rogue.world.pathfinding.PathCostProvider; +import mightypork.rogue.world.pathfinding.PathFinder; import mightypork.rogue.world.tile.Tile; import mightypork.rogue.world.tile.Tiles; import mightypork.rogue.world.tile.models.TileModel; +import mightypork.util.logging.Log; public class ScratchMap { - private Tile[][] map; - private int width; - private int height; + private final Tile[][] map; + private final int width; + private final int height; - private List rooms = new ArrayList<>(); + private final List rooms = new ArrayList<>(); + private final List nodes = new ArrayList<>(); // points to connect with corridors + + private final PathCostProvider pcp = new PathCostProvider() { + + @Override + public boolean isAccessible(Coord pos) + { + return isIn(pos); // suffices for now + } + + + @Override + public int getCost(Coord last, Coord pos) + { + final Tile t = get(pos); + + if (t.isWalkable()) return 10; + + return 100; // wall + } + + + @Override + public int getMinCost() + { + return 10; + } + + }; + + private final PathFinder pathFinder = new PathFinder(pcp, PathFinder.CORNER_HEURISTIC); Coord genMin; Coord genMax; - private Theme theme; - private Random rand; + private final Theme theme; + private final Random rand; private Coord enterPoint; @@ -31,7 +66,7 @@ public class ScratchMap { { map = new Tile[max_size][max_size]; - genMin = Coord.make(max_size / 2, max_size / 2); + genMin = Coord.make((max_size / 2) - 1, (max_size / 2) - 1); genMax = genMin.add(1, 1); width = max_size; @@ -45,15 +80,33 @@ public class ScratchMap { public void addRoom(RoomBuilder rb) { - Coord center = Coord.make(0, 0); + final 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); + final int sizeX = genMax.x - genMin.x; + final int sizeY = genMax.y - genMin.y; + + center.x = genMin.x + rand.nextInt(sizeX + 1); + center.y = genMin.y + rand.nextInt(sizeY + 1); + + switch (rand.nextInt(4)) { + case 0: + center.x += 1 + rand.nextInt(3); + break; + case 1: + center.x -= 1 + rand.nextInt(3); + break; + case 2: + center.y += 1 + rand.nextInt(3); + break; + case 3: + center.y -= 1 + rand.nextInt(3); + } + + final RoomDesc rd = rb.buildToFit(this, theme, rand, center); if (rd != null) { if (rooms.isEmpty()) { enterPoint = center.copy(); @@ -67,19 +120,15 @@ public class ScratchMap { genMax.x = Math.max(genMax.x, rd.max.x); genMax.y = Math.max(genMax.y, rd.max.y); + nodes.add(center); + Log.f3("placed room: " + rd.min + " -> " + rd.max); + 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) { + if (failed > 40) { + Log.w("Faild to build room."); return; } } @@ -97,7 +146,7 @@ public class ScratchMap { public Tile get(Coord pos) { if (!isIn(pos)) { - return Tiles.NULL_SOLID.createTile(); + throw new IndexOutOfBoundsException("Tile not in map: " + pos); } return map[pos.y][pos.x]; @@ -113,7 +162,7 @@ public class ScratchMap { public boolean set(Coord pos, Tile tile) { if (!isIn(pos)) { - return false; + throw new IndexOutOfBoundsException("Tile not in map: " + pos); } map[pos.y][pos.x] = tile; @@ -121,20 +170,10 @@ public class ScratchMap { } - 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 (!isIn(min) || !isIn(max)) return false; + for (final RoomDesc r : rooms) { if (r.intersectsWith(min, max)) return false; } @@ -144,7 +183,9 @@ public class ScratchMap { public void fill(Coord min, Coord max, TileModel tm) { - Coord c = Coord.make(0, 0); + if (!isIn(min) || !isIn(max)) throw new IndexOutOfBoundsException("Tile(s) not in map: " + min + " , " + max); + + final 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()); @@ -153,7 +194,9 @@ public class ScratchMap { public void border(Coord min, Coord max, TileModel tm) { - Coord c = Coord.make(0, 0); + if (!isIn(min) || !isIn(max)) throw new IndexOutOfBoundsException("Tile(s) not in map: " + min + " , " + max); + + final 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++) { @@ -165,6 +208,61 @@ public class ScratchMap { } + public void buildCorridors() + { + for (final Coord door1 : nodes) { + for (final Coord door2 : nodes) { + if (door1 == door2) continue; + + buildCorridor(door1, door2); + } + } + } + + + private void buildCorridor(Coord node1, Coord node2) + { + + final List steps = pathFinder.findPath(node1, node2); + + if (steps == null) { + Log.w("Could not build corridor " + node1 + "->" + node2); + return; + } + + for (final Coord c : steps) { + buildCorridorPiece(c); + } + } + + + private void buildCorridorPiece(Coord pos) + { + final Coord c = Coord.make(0, 0); + int i, j; + for (i = -1, c.x = pos.x - 1; c.x <= pos.x + 1; c.x++, i++) { + for (j = -1, c.y = pos.y - 1; c.y <= pos.y + 1; c.y++, j++) { + if (!isIn(c)) continue; + + genMin.x = Math.min(genMin.x, c.x); + genMin.y = Math.min(genMin.y, c.y); + + genMax.x = Math.max(genMax.x, c.x); + genMax.y = Math.max(genMax.y, c.y); + + final Tile current = get(c); + if (!current.getModel().isNullTile() && current.isWalkable()) continue; // floor already, let it be + + if (i == 0 && j == 0) { + set(c, theme.floor()); + } else { + set(c, theme.wall()); + } + } + } + } + + public Coord getNeededSize() { return Coord.make(genMax.x - genMin.x + 1, genMax.y - genMin.y + 1); @@ -173,16 +271,15 @@ public class ScratchMap { public void writeToLevel(Level level) { - - Coord c1 = Coord.make(0, 0); - Coord c = Coord.make(0, 0); + final Coord c1 = Coord.make(0, 0); + final 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); + final 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 index 31432d5..4a4c7c2 100644 --- a/src/mightypork/rogue/world/gen/Theme.java +++ b/src/mightypork/rogue/world/gen/Theme.java @@ -1,15 +1,21 @@ package mightypork.rogue.world.gen; + import mightypork.rogue.world.tile.models.TileModel; -// map theme + +/** + * Map theme to use for building + * + * @author MightyPork + */ public interface Theme { TileModel wall(); TileModel floor(); - - - TileModel door(); -} \ No newline at end of file + + + TileModel door(); +} diff --git a/src/mightypork/rogue/world/gen/rooms/IntersectionRoom.java b/src/mightypork/rogue/world/gen/rooms/IntersectionRoom.java deleted file mode 100644 index ca52c73..0000000 --- a/src/mightypork/rogue/world/gen/rooms/IntersectionRoom.java +++ /dev/null @@ -1,49 +0,0 @@ -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/SimpleRectRoom.java b/src/mightypork/rogue/world/gen/rooms/SimpleRectRoom.java new file mode 100644 index 0000000..db5a329 --- /dev/null +++ b/src/mightypork/rogue/world/gen/rooms/SimpleRectRoom.java @@ -0,0 +1,35 @@ +package mightypork.rogue.world.gen.rooms; + + +import java.util.Random; + +import mightypork.rogue.world.Coord; +import mightypork.rogue.world.gen.RoomBuilder; +import mightypork.rogue.world.gen.RoomDesc; +import mightypork.rogue.world.gen.ScratchMap; +import mightypork.rogue.world.gen.Theme; + + +public class SimpleRectRoom implements RoomBuilder { + + @Override + public RoomDesc buildToFit(ScratchMap map, Theme theme, Random rand, Coord center) + { + // half width, half height actually + final int width = 2 + rand.nextInt(2); + final int height = 2 + rand.nextInt(2); + + final Coord min = new Coord(center.x - width, center.y - height); + final Coord max = new Coord(center.x + height, center.y + height); + + if (!map.isClear(min, max)) return null; + + map.fill(min, max, theme.floor()); + map.border(min, max, theme.wall()); + + // TODO place some doors + + return new RoomDesc(min.add(-1, -1), max.add(1, 1)); + } + +} diff --git a/src/mightypork/rogue/world/gen/rooms/SquareRoom.java b/src/mightypork/rogue/world/gen/rooms/SquareRoom.java deleted file mode 100644 index ff93e62..0000000 --- a/src/mightypork/rogue/world/gen/rooms/SquareRoom.java +++ /dev/null @@ -1,98 +0,0 @@ -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 index e41e8d7..7376e3d 100644 --- a/src/mightypork/rogue/world/gen/themes/ThemeDungeon.java +++ b/src/mightypork/rogue/world/gen/themes/ThemeDungeon.java @@ -1,9 +1,11 @@ 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 { @@ -20,10 +22,11 @@ public class ThemeDungeon implements Theme { return Tiles.FLOOR_DARK; } + @Override public TileModel door() { return floor(); // TODO } -} \ No newline at end of file +} diff --git a/src/mightypork/rogue/world/pathfinding/DiagonalHeuristic.java b/src/mightypork/rogue/world/pathfinding/DiagonalHeuristic.java new file mode 100644 index 0000000..adffef5 --- /dev/null +++ b/src/mightypork/rogue/world/pathfinding/DiagonalHeuristic.java @@ -0,0 +1,14 @@ +package mightypork.rogue.world.pathfinding; + + +import mightypork.rogue.world.Coord; + + +public class DiagonalHeuristic extends Heuristic { + + @Override + public double getCost(Coord pos, Coord target) + { + return Math.sqrt(Math.pow(pos.x - target.x, 2) + Math.pow(pos.y - target.y, 2)); + } +} diff --git a/src/mightypork/rogue/world/pathfinding/Heuristic.java b/src/mightypork/rogue/world/pathfinding/Heuristic.java new file mode 100644 index 0000000..2062c4c --- /dev/null +++ b/src/mightypork/rogue/world/pathfinding/Heuristic.java @@ -0,0 +1,17 @@ +package mightypork.rogue.world.pathfinding; + + +import mightypork.rogue.world.Coord; + + +public abstract class Heuristic { + + /** + * Get tile cost (estimate of how many tiles remain to the target) + * + * @param pos current pos + * @param target target pos + * @return estimated number of tiles + */ + public abstract double getCost(Coord pos, Coord target); +} diff --git a/src/mightypork/rogue/world/pathfinding/ManhattanHeuristic.java b/src/mightypork/rogue/world/pathfinding/ManhattanHeuristic.java new file mode 100644 index 0000000..fa26a30 --- /dev/null +++ b/src/mightypork/rogue/world/pathfinding/ManhattanHeuristic.java @@ -0,0 +1,14 @@ +package mightypork.rogue.world.pathfinding; + + +import mightypork.rogue.world.Coord; + + +public class ManhattanHeuristic extends Heuristic { + + @Override + public double getCost(Coord pos, Coord target) + { + return Math.abs(target.x - pos.x) + Math.abs(target.y - pos.y); + } +} diff --git a/src/mightypork/rogue/world/pathfinding/PathCostProvider.java b/src/mightypork/rogue/world/pathfinding/PathCostProvider.java new file mode 100644 index 0000000..1a0272c --- /dev/null +++ b/src/mightypork/rogue/world/pathfinding/PathCostProvider.java @@ -0,0 +1,30 @@ +package mightypork.rogue.world.pathfinding; + + +import mightypork.rogue.world.Coord; + + +public interface PathCostProvider { + + /** + * @param pos tile pos + * @return true if the tile is walkable + */ + boolean isAccessible(Coord pos); + + + /** + * Cost of walking onto a tile. It's useful to use ie. 10 for basic step. + * + * @param from last tile + * @param to current tile + * @return cost + */ + int getCost(Coord from, Coord to); + + + /** + * @return lowest cost. Used to multiply heuristics. + */ + int getMinCost(); +} diff --git a/src/mightypork/rogue/world/pathfinding/PathFinder.java b/src/mightypork/rogue/world/pathfinding/PathFinder.java new file mode 100644 index 0000000..734aeca --- /dev/null +++ b/src/mightypork/rogue/world/pathfinding/PathFinder.java @@ -0,0 +1,205 @@ +package mightypork.rogue.world.pathfinding; + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; + +import mightypork.rogue.world.Coord; +import mightypork.rogue.world.PathStep; + + +/** + * A* pathfinder + * + * @author MightyPork + */ +public class PathFinder { + + private static final FComparator F_COMPARATOR = new FComparator(); + public static final Heuristic CORNER_HEURISTIC = new ManhattanHeuristic(); + public static final Heuristic DIAGONAL_HEURISTIC = new DiagonalHeuristic(); + + private final PathCostProvider costProvider; + private final Heuristic heuristic; + + + public PathFinder(PathCostProvider costProvider, Heuristic heuristic) + { + this.costProvider = costProvider; + this.heuristic = heuristic; + } + + + public List findPathRelative(Coord start, Coord end) + { + final List path = findPath(start, end); + if (path == null) return null; + + final List out = new ArrayList<>(); + + final Coord current = start; + for (final Coord c : path) { + if (c.equals(current)) continue; + out.add(PathStep.make(c.x - current.x, c.y - current.y)); + current.x = c.x; + current.y = c.y; + } + + return out; + } + + + public List findPath(Coord start, Coord end) + { + final LinkedList open = new LinkedList<>(); + final LinkedList closed = new LinkedList<>(); + + // add first node + { + final Node n = new Node(start); + n.h_cost = (int) (heuristic.getCost(start, end) * costProvider.getMinCost()); + n.g_cost = 0; + open.add(n); + } + + //@formatter:off + final Coord[] walkDirs = { + Coord.make(0, -1), + Coord.make(0, 1), + Coord.make(-1, 0), + Coord.make(1, 0) + }; + //@formatter:on + + Node current = null; + + while (true) { + current = open.poll(); + + if (current == null) { + break; + } + + closed.add(current); + + if (current.pos.equals(end)) { + break; + } + + for (final Coord go : walkDirs) { + + final Coord c = current.pos.add(go); + if (!costProvider.isAccessible(c)) continue; + final Node a = new Node(c); + a.g_cost = current.g_cost + costProvider.getCost(c, a.pos); + a.h_cost = (int) (heuristic.getCost(a.pos, end) * costProvider.getMinCost()); + a.parent = current; + + if (costProvider.isAccessible(a.pos)) { + if (!closed.contains(a)) { + + if (open.contains(a)) { + + boolean needSort = false; + + // find where it is + for (final Node n : open) { + if (n.pos.equals(a.pos)) { // found it + if (n.g_cost > a.g_cost) { + n.parent = current; + n.g_cost = a.g_cost; + needSort = true; + } + break; + } + } + + if (needSort) Collections.sort(open, F_COMPARATOR); + + } else { + open.add(a); + } + } + } + } + + } + + if (current == null) { + return null; // no path found + } + + final LinkedList path = new LinkedList<>(); + + // extract path elements + while (current != null) { + path.addFirst(current.pos); + current = current.parent; + } + + return path; + } + + private static class Node { + + Coord pos; + int g_cost; // to get there + int h_cost; // to target + Node parent; + + + public Node(Coord pos) + { + this.pos = pos; + } + + + int fCost() + { + return g_cost + h_cost; + } + + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((pos == null) ? 0 : pos.hashCode()); + return result; + } + + + @Override + public boolean equals(Object obj) + { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof Node)) return false; + final Node other = (Node) obj; + if (pos == null) { + if (other.pos != null) return false; + } else if (!pos.equals(other.pos)) return false; + return true; + } + + + @Override + public String toString() + { + return "N " + pos + ", G =" + g_cost + ", H = " + h_cost; + } + } + + private static class FComparator implements Comparator { + + @Override + public int compare(Node n1, Node n2) + { + return n1.fCost() - n2.fCost(); + } + } +} diff --git a/src/mightypork/rogue/world/tile/Tile.java b/src/mightypork/rogue/world/tile/Tile.java index a6a180f..dac0fce 100644 --- a/src/mightypork/rogue/world/tile/Tile.java +++ b/src/mightypork/rogue/world/tile/Tile.java @@ -34,13 +34,11 @@ public final class Tile implements IonBinary { /** persistent field for model, reflected by renderer */ public final IonBundle metadata = new IonBundle(); + public final TileRenderData renderData = new TileRenderData(); + // temporary flag for map. private boolean occupied; - // for renderer of AO shadows - public byte shadows; - public boolean shadowsComputed; - public Tile(int id) { diff --git a/src/mightypork/rogue/world/tile/TileRenderData.java b/src/mightypork/rogue/world/tile/TileRenderData.java new file mode 100644 index 0000000..797e593 --- /dev/null +++ b/src/mightypork/rogue/world/tile/TileRenderData.java @@ -0,0 +1,14 @@ +package mightypork.rogue.world.tile; + + +/** + * Data storage for renderer / entity. + * + * @author MightyPork + */ +public class TileRenderData { + + public byte shadows; + public boolean shadowsComputed; + +} diff --git a/src/mightypork/rogue/world/tile/Tiles.java b/src/mightypork/rogue/world/tile/Tiles.java index 3706f78..9308eb5 100644 --- a/src/mightypork/rogue/world/tile/Tiles.java +++ b/src/mightypork/rogue/world/tile/Tiles.java @@ -1,11 +1,9 @@ package mightypork.rogue.world.tile; -import mightypork.rogue.world.tile.models.Floor; -import mightypork.rogue.world.tile.models.NullFloor; -import mightypork.rogue.world.tile.models.NullWall; -import mightypork.rogue.world.tile.models.TileModel; -import mightypork.rogue.world.tile.models.Wall; +import mightypork.rogue.world.tile.models.*; +import mightypork.rogue.world.tile.renderers.FloorRenderer; +import mightypork.rogue.world.tile.renderers.WallRenderer; /** @@ -21,8 +19,10 @@ public final class Tiles { 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(10).setTexture("tile16.floor.dark"); - public static final TileModel WALL_BRICK = new Wall(11).setTexture("tile16.wall.brick"); + public static final TileModel FLOOR_DARK = new Floor(10).setRenderer(new FloorRenderer("tile.floor.dark")); + public static final TileModel WALL_BRICK = new Wall(11).setRenderer(new WallRenderer("tile.wall.brick")); + + public static final TileModel DOOR = new SimpleDoor(12); // public static final TileModel BRICK_FLOOR_VINES = new Floor(2).setTexture("tile.floor.mossy_bricks"); diff --git a/src/mightypork/rogue/world/tile/models/AbstractNullTile.java b/src/mightypork/rogue/world/tile/models/AbstractNullTile.java index d9cc8a9..d9610b9 100644 --- a/src/mightypork/rogue/world/tile/models/AbstractNullTile.java +++ b/src/mightypork/rogue/world/tile/models/AbstractNullTile.java @@ -52,10 +52,6 @@ public abstract class AbstractNullTile extends SimpleTile { } - @Override - public abstract boolean isWalkable(); - - @Override public boolean doesCastShadow() { diff --git a/src/mightypork/rogue/world/tile/models/Floor.java b/src/mightypork/rogue/world/tile/models/Floor.java index 636bc45..9c287d7 100644 --- a/src/mightypork/rogue/world/tile/models/Floor.java +++ b/src/mightypork/rogue/world/tile/models/Floor.java @@ -1,6 +1,9 @@ package mightypork.rogue.world.tile.models; +import mightypork.rogue.world.tile.Tile; + + /** * Template for floor tiles with no metadata * @@ -15,7 +18,7 @@ public class Floor extends SimpleTile { @Override - public boolean isWalkable() + public boolean isWalkable(Tile tile) { return true; } diff --git a/src/mightypork/rogue/world/tile/models/NullFloor.java b/src/mightypork/rogue/world/tile/models/NullFloor.java index 308f52c..ff45067 100644 --- a/src/mightypork/rogue/world/tile/models/NullFloor.java +++ b/src/mightypork/rogue/world/tile/models/NullFloor.java @@ -1,6 +1,9 @@ package mightypork.rogue.world.tile.models; +import mightypork.rogue.world.tile.Tile; + + public class NullFloor extends AbstractNullTile { public NullFloor(int id) @@ -10,7 +13,7 @@ public class NullFloor extends AbstractNullTile { @Override - public boolean isWalkable() + public boolean isWalkable(Tile tile) { return true; } diff --git a/src/mightypork/rogue/world/tile/models/NullWall.java b/src/mightypork/rogue/world/tile/models/NullWall.java index f7ea586..72bcc14 100644 --- a/src/mightypork/rogue/world/tile/models/NullWall.java +++ b/src/mightypork/rogue/world/tile/models/NullWall.java @@ -1,6 +1,9 @@ package mightypork.rogue.world.tile.models; +import mightypork.rogue.world.tile.Tile; + + public class NullWall extends AbstractNullTile { public NullWall(int id) @@ -10,9 +13,8 @@ public class NullWall extends AbstractNullTile { @Override - public boolean isWalkable() + public boolean isWalkable(Tile tile) { return false; } - } diff --git a/src/mightypork/rogue/world/tile/models/SimpleDoor.java b/src/mightypork/rogue/world/tile/models/SimpleDoor.java new file mode 100644 index 0000000..bb40bec --- /dev/null +++ b/src/mightypork/rogue/world/tile/models/SimpleDoor.java @@ -0,0 +1,34 @@ +package mightypork.rogue.world.tile.models; + + +import mightypork.rogue.world.tile.Tile; +import mightypork.rogue.world.tile.renderers.DoorRenderer; + + +/** + * Template for floor tiles with no metadata + * + * @author MightyPork + */ +public class SimpleDoor extends Wall { + + public SimpleDoor(int id) + { + super(id); + setRenderer(new DoorRenderer("tile.door.closed", "tile.door.open")); + } + + + @Override + public boolean isWalkable(Tile tile) + { + return true; + } + + + @Override + public boolean isDoor() + { + return true; + } +} diff --git a/src/mightypork/rogue/world/tile/models/SimpleTile.java b/src/mightypork/rogue/world/tile/models/SimpleTile.java index 3c961fb..a78ef2b 100644 --- a/src/mightypork/rogue/world/tile/models/SimpleTile.java +++ b/src/mightypork/rogue/world/tile/models/SimpleTile.java @@ -20,14 +20,14 @@ public abstract class SimpleTile extends TileModel { @Override - public boolean isWalkable(Tile tile) - { - return isWalkable(); - } + public abstract boolean isWalkable(Tile tile); @Override - public abstract boolean isWalkable(); + public boolean isDoor() + { + return false; + } @Override diff --git a/src/mightypork/rogue/world/tile/models/TileModel.java b/src/mightypork/rogue/world/tile/models/TileModel.java index f3bfef2..66d7b3c 100644 --- a/src/mightypork/rogue/world/tile/models/TileModel.java +++ b/src/mightypork/rogue/world/tile/models/TileModel.java @@ -4,7 +4,6 @@ package mightypork.rogue.world.tile.models; import mightypork.rogue.world.level.Level; import mightypork.rogue.world.tile.Tile; import mightypork.rogue.world.tile.Tiles; -import mightypork.rogue.world.tile.renderers.BasicTileRenderer; import mightypork.rogue.world.tile.renderers.TileRenderer; @@ -34,13 +33,6 @@ public abstract class TileModel { } - public TileModel setTexture(String sheetKey) - { - this.renderer = new BasicTileRenderer(sheetKey); - return this; - } - - /** * @return new tile of this type; if 100% invariant, can return cached one. */ @@ -50,17 +42,10 @@ public abstract class TileModel { } - /** - * @param tile - * @return true if walkable right now - */ public abstract boolean isWalkable(Tile tile); - /** - * @return true if walkable at some conditions - */ - public abstract boolean isWalkable(); + public abstract boolean isDoor(); public abstract boolean doesCastShadow(); diff --git a/src/mightypork/rogue/world/tile/models/Wall.java b/src/mightypork/rogue/world/tile/models/Wall.java index 36ce59e..cffd915 100644 --- a/src/mightypork/rogue/world/tile/models/Wall.java +++ b/src/mightypork/rogue/world/tile/models/Wall.java @@ -1,6 +1,9 @@ package mightypork.rogue.world.tile.models; +import mightypork.rogue.world.tile.Tile; + + /** * Template for wall tiles with no metadata * @@ -15,7 +18,7 @@ public class Wall extends SimpleTile { @Override - public boolean isWalkable() + public boolean isWalkable(Tile tile) { return false; } diff --git a/src/mightypork/rogue/world/tile/renderers/BasicTileRenderer.java b/src/mightypork/rogue/world/tile/renderers/BasicTileRenderer.java index 25d008a..48a0381 100644 --- a/src/mightypork/rogue/world/tile/renderers/BasicTileRenderer.java +++ b/src/mightypork/rogue/world/tile/renderers/BasicTileRenderer.java @@ -2,11 +2,9 @@ package mightypork.rogue.world.tile.renderers; import mightypork.gamecore.render.Render; -import mightypork.gamecore.render.textures.TxQuad; import mightypork.gamecore.render.textures.TxSheet; import mightypork.rogue.Res; import mightypork.rogue.world.level.render.TileRenderContext; -import mightypork.rogue.world.tile.Tile; import mightypork.util.math.constraints.rect.Rect; @@ -14,24 +12,10 @@ public class BasicTileRenderer extends TileRenderer { private final TxSheet sheet; - private static boolean inited; - private static TxQuad SH_N, SH_S, SH_E, SH_W, SH_NW, SH_NE, SH_SW, SH_SE; - public BasicTileRenderer(String sheetKey) { this.sheet = Res.getTxSheet(sheetKey); - - if (!inited) { - SH_N = Res.getTxQuad("tile16.shadow.n"); - SH_S = Res.getTxQuad("tile16.shadow.s"); - SH_E = Res.getTxQuad("tile16.shadow.e"); - SH_W = Res.getTxQuad("tile16.shadow.w"); - SH_NW = Res.getTxQuad("tile16.shadow.nw"); - SH_NE = Res.getTxQuad("tile16.shadow.ne"); - SH_SW = Res.getTxQuad("tile16.shadow.sw"); - SH_SE = Res.getTxQuad("tile16.shadow.se"); - } } @@ -40,47 +24,5 @@ public class BasicTileRenderer extends TileRenderer { { final Rect rect = context.getRect(); Render.quadTextured(rect, sheet.getRandomQuad(context.getTileNoise())); - - final Tile t = context.getTile(); - - if (t.getModel().doesCastShadow()) return; // no shadows for wall - - Tile t2; - - if (!t.shadowsComputed) { - // no shadows computed yet - - t.shadows = 0; // reset the mask - - int move = 0; - for (int y = -1; y <= 1; y++) { - for (int x = -1; x <= 1; x++) { - if (x == 0 && y == 0) continue; - - t2 = context.getAdjacentTile(x, y); - - if (t2.getModel().doesCastShadow()) { - t.shadows |= 1 << move; - } - - move++; - } - } - - t.shadowsComputed = true; - } - - if (t.shadows == 0) return; - - if ((t.shadows & (1 << 0)) != 0) Render.quadTextured(rect, SH_NW); - if ((t.shadows & (1 << 1)) != 0) Render.quadTextured(rect, SH_N); - if ((t.shadows & (1 << 2)) != 0) Render.quadTextured(rect, SH_NE); - - if ((t.shadows & (1 << 3)) != 0) Render.quadTextured(rect, SH_W); - if ((t.shadows & (1 << 4)) != 0) Render.quadTextured(rect, SH_E); - - if ((t.shadows & (1 << 5)) != 0) Render.quadTextured(rect, SH_SW); - if ((t.shadows & (1 << 6)) != 0) Render.quadTextured(rect, SH_S); - if ((t.shadows & (1 << 7)) != 0) Render.quadTextured(rect, SH_SE); } } diff --git a/src/mightypork/rogue/world/tile/renderers/DoorRenderer.java b/src/mightypork/rogue/world/tile/renderers/DoorRenderer.java new file mode 100644 index 0000000..b19e77b --- /dev/null +++ b/src/mightypork/rogue/world/tile/renderers/DoorRenderer.java @@ -0,0 +1,38 @@ +package mightypork.rogue.world.tile.renderers; + + +import mightypork.gamecore.render.Render; +import mightypork.gamecore.render.textures.TxQuad; +import mightypork.rogue.Res; +import mightypork.rogue.world.level.render.TileRenderContext; +import mightypork.rogue.world.tile.Tile; +import mightypork.util.math.constraints.rect.Rect; + + +public class DoorRenderer extends TileRenderer { + + private final TxQuad closed; + private final TxQuad open; + + + public DoorRenderer(String quadClosed, String quadOpen) + { + this.closed = Res.getTxQuad(quadClosed); + this.open = Res.getTxQuad(quadOpen); + } + + + @Override + public void render(TileRenderContext context) + { + final Tile t = context.getTile(); + final Rect rect = context.getRect(); + + if (t.isOccupied()) { + Render.quadTextured(rect, closed); + } else { + Render.quadTextured(rect, open); + } + } + +} diff --git a/src/mightypork/rogue/world/tile/renderers/FloorRenderer.java b/src/mightypork/rogue/world/tile/renderers/FloorRenderer.java new file mode 100644 index 0000000..e57d432 --- /dev/null +++ b/src/mightypork/rogue/world/tile/renderers/FloorRenderer.java @@ -0,0 +1,81 @@ +package mightypork.rogue.world.tile.renderers; + + +import mightypork.gamecore.render.Render; +import mightypork.gamecore.render.textures.TxQuad; +import mightypork.rogue.Res; +import mightypork.rogue.world.level.render.TileRenderContext; +import mightypork.rogue.world.tile.Tile; +import mightypork.rogue.world.tile.TileRenderData; +import mightypork.util.math.constraints.rect.Rect; + + +public class FloorRenderer extends BasicTileRenderer { + + private static boolean inited; + private static TxQuad SH_N, SH_S, SH_E, SH_W, SH_NW, SH_NE, SH_SW, SH_SE; + + + public FloorRenderer(String sheetKey) + { + super(sheetKey); + + if (!inited) { + SH_N = Res.getTxQuad("tile.shadow.n"); + SH_S = Res.getTxQuad("tile.shadow.s"); + SH_E = Res.getTxQuad("tile.shadow.e"); + SH_W = Res.getTxQuad("tile.shadow.w"); + SH_NW = Res.getTxQuad("tile.shadow.nw"); + SH_NE = Res.getTxQuad("tile.shadow.ne"); + SH_SW = Res.getTxQuad("tile.shadow.sw"); + SH_SE = Res.getTxQuad("tile.shadow.se"); + } + } + + + @Override + public void render(TileRenderContext context) + { + super.render(context); + + final Rect rect = context.getRect(); + + final TileRenderData trd = context.getTile().renderData; + + if (!trd.shadowsComputed) { + // no shadows computed yet + + trd.shadows = 0; // reset the mask + + int move = 0; + for (int y = -1; y <= 1; y++) { + for (int x = -1; x <= 1; x++) { + if (x == 0 && y == 0) continue; + + final Tile t2 = context.getAdjacentTile(x, y); + + if (t2.getModel().doesCastShadow()) { + trd.shadows |= 1 << move; + } + + move++; + } + } + + trd.shadowsComputed = true; + } + + if (trd.shadows == 0) return; + + if ((trd.shadows & (1 << 0)) != 0) Render.quadTextured(rect, SH_NW); + if ((trd.shadows & (1 << 1)) != 0) Render.quadTextured(rect, SH_N); + if ((trd.shadows & (1 << 2)) != 0) Render.quadTextured(rect, SH_NE); + + if ((trd.shadows & (1 << 3)) != 0) Render.quadTextured(rect, SH_W); + if ((trd.shadows & (1 << 4)) != 0) Render.quadTextured(rect, SH_E); + + if ((trd.shadows & (1 << 5)) != 0) Render.quadTextured(rect, SH_SW); + if ((trd.shadows & (1 << 6)) != 0) Render.quadTextured(rect, SH_S); + if ((trd.shadows & (1 << 7)) != 0) Render.quadTextured(rect, SH_SE); + } +} diff --git a/src/mightypork/rogue/world/tile/renderers/WallRenderer.java b/src/mightypork/rogue/world/tile/renderers/WallRenderer.java new file mode 100644 index 0000000..3a440f4 --- /dev/null +++ b/src/mightypork/rogue/world/tile/renderers/WallRenderer.java @@ -0,0 +1,11 @@ +package mightypork.rogue.world.tile.renderers; + + +public class WallRenderer extends BasicTileRenderer { + + public WallRenderer(String sheetKey) + { + super(sheetKey); + } + +} diff --git a/src/mightypork/test/TestPerlin.java b/src/mightypork/test/TestPerlin.java deleted file mode 100644 index 732932d..0000000 --- a/src/mightypork/test/TestPerlin.java +++ /dev/null @@ -1,33 +0,0 @@ -package mightypork.test; - - -import java.util.Locale; - -import mightypork.util.math.noise.NoiseGen; - - -public class TestPerlin { - - public static void main(String[] args) - { - Locale.setDefault(Locale.ENGLISH); - - final int w = 50, h = 50; - - final NoiseGen ng = new NoiseGen(0.12, 0, 2.5, 5, (long) (Math.random() * 100)); - - final double[][] map = ng.buildMap(w, h); - - final char[] colors = { ' ', '░', '▒', '▓', '█' }; - - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - // "pixels" two-thick - System.out.print(colors[(int) Math.floor(map[y][x])]); - System.out.print(colors[(int) Math.floor(map[y][x])]); - } - System.out.println(); - } - } - -} diff --git a/src/mightypork/util/math/constraints/rect/builders/TiledRect.java b/src/mightypork/util/math/constraints/rect/builders/TiledRect.java index 66a95e2..5f60928 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: "+x); + throw new IndexOutOfBoundsException("X coordinate out fo range: " + x); } if (y >= tilesY || y < 0) { - throw new IndexOutOfBoundsException("Y coordinate out of range: "+y); + throw new IndexOutOfBoundsException("Y coordinate out of range: " + y); } return aTile.move(perCol.mul(x), perRow.mul(y));