A* path finding, room & corridor generation, pathfinding mouse control

v5stable
Ondřej Hruška 11 years ago
parent 69f55dcc12
commit eef373eb24
  1. BIN
      res/img/tiles16.png
  2. 3
      src/mightypork/gamecore/render/DisplaySystem.java
  3. 37
      src/mightypork/rogue/Res.java
  4. 4
      src/mightypork/rogue/screens/gamescreen/ScreenGame.java
  5. 5
      src/mightypork/rogue/screens/gamescreen/WorldLayer.java
  6. 38
      src/mightypork/rogue/screens/gamescreen/world/MIPClickPathfWalk.java
  7. 4
      src/mightypork/rogue/screens/gamescreen/world/MIPKeyWalk.java
  8. 6
      src/mightypork/rogue/screens/gamescreen/world/MIPMouseWalk.java
  9. 2
      src/mightypork/rogue/screens/gamescreen/world/MapInteractionPlugin.java
  10. 4
      src/mightypork/rogue/screens/gamescreen/world/MapView.java
  11. 40
      src/mightypork/rogue/world/Coord.java
  12. 7
      src/mightypork/rogue/world/PathStep.java
  13. 46
      src/mightypork/rogue/world/PlayerControl.java
  14. 7
      src/mightypork/rogue/world/World.java
  15. 4
      src/mightypork/rogue/world/WorldCreator.java
  16. 10
      src/mightypork/rogue/world/WorldPos.java
  17. 6
      src/mightypork/rogue/world/entity/Entity.java
  18. 25
      src/mightypork/rogue/world/gen/LevelGenerator.java
  19. 15
      src/mightypork/rogue/world/gen/RoomBuilder.java
  20. 14
      src/mightypork/rogue/world/gen/RoomDesc.java
  21. 177
      src/mightypork/rogue/world/gen/ScratchMap.java
  22. 16
      src/mightypork/rogue/world/gen/Theme.java
  23. 49
      src/mightypork/rogue/world/gen/rooms/IntersectionRoom.java
  24. 35
      src/mightypork/rogue/world/gen/rooms/SimpleRectRoom.java
  25. 98
      src/mightypork/rogue/world/gen/rooms/SquareRoom.java
  26. 5
      src/mightypork/rogue/world/gen/themes/ThemeDungeon.java
  27. 14
      src/mightypork/rogue/world/pathfinding/DiagonalHeuristic.java
  28. 17
      src/mightypork/rogue/world/pathfinding/Heuristic.java
  29. 14
      src/mightypork/rogue/world/pathfinding/ManhattanHeuristic.java
  30. 30
      src/mightypork/rogue/world/pathfinding/PathCostProvider.java
  31. 205
      src/mightypork/rogue/world/pathfinding/PathFinder.java
  32. 6
      src/mightypork/rogue/world/tile/Tile.java
  33. 14
      src/mightypork/rogue/world/tile/TileRenderData.java
  34. 14
      src/mightypork/rogue/world/tile/Tiles.java
  35. 4
      src/mightypork/rogue/world/tile/models/AbstractNullTile.java
  36. 5
      src/mightypork/rogue/world/tile/models/Floor.java
  37. 5
      src/mightypork/rogue/world/tile/models/NullFloor.java
  38. 6
      src/mightypork/rogue/world/tile/models/NullWall.java
  39. 34
      src/mightypork/rogue/world/tile/models/SimpleDoor.java
  40. 10
      src/mightypork/rogue/world/tile/models/SimpleTile.java
  41. 17
      src/mightypork/rogue/world/tile/models/TileModel.java
  42. 5
      src/mightypork/rogue/world/tile/models/Wall.java
  43. 58
      src/mightypork/rogue/world/tile/renderers/BasicTileRenderer.java
  44. 38
      src/mightypork/rogue/world/tile/renderers/DoorRenderer.java
  45. 81
      src/mightypork/rogue/world/tile/renderers/FloorRenderer.java
  46. 11
      src/mightypork/rogue/world/tile/renderers/WallRenderer.java
  47. 33
      src/mightypork/test/TestPerlin.java
  48. 4
      src/mightypork/util/math/constraints/rect/builders/TiledRect.java

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

@ -63,7 +63,8 @@ public class DisplaySystem extends AppModule implements RectBound {
@Override @Override
protected void deinit() protected void deinit()
{ {
Display.destroy(); // causes weird visual glitch
// Display.destroy();
} }

@ -79,20 +79,6 @@ public final class Res {
textures.addQuad("xp_off", gui.makeQuad(.5, 1.5, .5, .5)); textures.addQuad("xp_off", gui.makeQuad(.5, 1.5, .5, .5));
textures.addQuad("panel", gui.makeQuad(0, 3.75, 4, .25)); textures.addQuad("panel", gui.makeQuad(0, 3.75, 4, .25));
// huge sheet
// texture = textures.loadTexture("tiles", "/res/img/tiles.png", FilterMode.NEAREST, WrapMode.CLAMP);
// tiles = texture.grid(32, 32);
// textures.addSheet("tile.wall.mossy_bricks", tiles.makeSheet(4, 0, 7, 1));
// textures.addSheet("tile.wall.small_bricks", tiles.makeSheet(0, 0, 4, 1));
// textures.addSheet("tile.floor.mossy_bricks", tiles.makeSheet(16, 5, 7, 1));
// textures.addSheet("tile.floor.rect_bricks", tiles.makeSheet(23, 5, 4, 1));
// textures.addSheet("tile.wall.sandstone", tiles.makeSheet(0, 3, 10, 1));
// textures.addSheet("tile.floor.sandstone", tiles.makeSheet(0, 6, 10, 1));
// textures.addSheet("tile.wall.brown_cobble", tiles.makeSheet(0, 8, 8, 1));
// textures.addSheet("tile.floor.brown_cobble", tiles.makeSheet(0, 11, 9, 1));
// textures.addSheet("tile.floor.crystal", tiles.makeSheet(4, 5, 6, 1));
// textures.addSheet("tile.wall.crystal", tiles.makeSheet(12, 2, 14, 1));
// sprites // sprites
texture = textures.loadTexture("mob", "/res/img/dudes.png", FilterMode.NEAREST, WrapMode.CLAMP); texture = textures.loadTexture("mob", "/res/img/dudes.png", FilterMode.NEAREST, WrapMode.CLAMP);
tiles = texture.grid(8, 8); tiles = texture.grid(8, 8);
@ -102,18 +88,21 @@ public final class Res {
texture = textures.loadTexture("tiles16", "/res/img/tiles16.png", FilterMode.NEAREST, WrapMode.CLAMP); texture = textures.loadTexture("tiles16", "/res/img/tiles16.png", FilterMode.NEAREST, WrapMode.CLAMP);
tiles = texture.grid(8, 8); tiles = texture.grid(8, 8);
textures.addSheet("tile16.floor.dark", tiles.makeSheet(0, 1, 5, 1)); textures.addSheet("tile.floor.dark", tiles.makeSheet(0, 1, 5, 1));
textures.addSheet("tile16.wall.brick", tiles.makeSheet(0, 0, 5, 1)); textures.addSheet("tile.wall.brick", tiles.makeSheet(0, 0, 5, 1));
textures.addQuad("tile.door.closed", tiles.makeQuad(1, 2));
textures.addQuad("tile.door.open", tiles.makeQuad(2, 2));
textures.addQuad("tile16.shadow.n", tiles.makeQuad(0, 7)); textures.addQuad("tile.shadow.n", tiles.makeQuad(0, 7));
textures.addQuad("tile16.shadow.s", tiles.makeQuad(0, 7).flipY()); textures.addQuad("tile.shadow.s", tiles.makeQuad(0, 7).flipY());
textures.addQuad("tile16.shadow.w", tiles.makeQuad(2, 7)); textures.addQuad("tile.shadow.w", tiles.makeQuad(2, 7));
textures.addQuad("tile16.shadow.e", tiles.makeQuad(2, 7).flipX()); textures.addQuad("tile.shadow.e", tiles.makeQuad(2, 7).flipX());
textures.addQuad("tile16.shadow.nw", tiles.makeQuad(1, 7)); textures.addQuad("tile.shadow.nw", tiles.makeQuad(1, 7));
textures.addQuad("tile16.shadow.ne", tiles.makeQuad(1, 7).flipX()); textures.addQuad("tile.shadow.ne", tiles.makeQuad(1, 7).flipX());
textures.addQuad("tile16.shadow.sw", tiles.makeQuad(1, 7).flipY()); textures.addQuad("tile.shadow.sw", tiles.makeQuad(1, 7).flipY());
textures.addQuad("tile16.shadow.se", tiles.makeQuad(1, 7).flipY().flipX()); textures.addQuad("tile.shadow.se", tiles.makeQuad(1, 7).flipY().flipX());
} }

@ -8,8 +8,8 @@ import java.util.Random;
import mightypork.gamecore.control.AppAccess; import mightypork.gamecore.control.AppAccess;
import mightypork.gamecore.gui.screens.LayeredScreen; import mightypork.gamecore.gui.screens.LayeredScreen;
import mightypork.rogue.Paths; import mightypork.rogue.Paths;
import mightypork.rogue.world.MapGenerator;
import mightypork.rogue.world.World; import mightypork.rogue.world.World;
import mightypork.rogue.world.WorldCreator;
import mightypork.util.files.ion.Ion; import mightypork.util.files.ion.Ion;
@ -34,7 +34,7 @@ public class ScreenGame extends LayeredScreen {
// SAVE // SAVE
final World world = MapGenerator.createWorld(rand.nextLong()); final World world = WorldCreator.createWorld(rand.nextLong());
addChildClient(world); addChildClient(world);
try { try {

@ -3,8 +3,8 @@ package mightypork.rogue.screens.gamescreen;
import mightypork.gamecore.gui.screens.Screen; import mightypork.gamecore.gui.screens.Screen;
import mightypork.gamecore.gui.screens.ScreenLayer; import mightypork.gamecore.gui.screens.ScreenLayer;
import mightypork.rogue.screens.gamescreen.world.MIPClickPathfWalk;
import mightypork.rogue.screens.gamescreen.world.MIPKeyWalk; import mightypork.rogue.screens.gamescreen.world.MIPKeyWalk;
import mightypork.rogue.screens.gamescreen.world.MIPMouseWalk;
import mightypork.rogue.screens.gamescreen.world.MapView; import mightypork.rogue.screens.gamescreen.world.MapView;
import mightypork.rogue.world.World; import mightypork.rogue.world.World;
import mightypork.util.math.constraints.num.Num; import mightypork.util.math.constraints.num.Num;
@ -25,7 +25,8 @@ public class WorldLayer extends ScreenLayer {
// map input plugins // map input plugins
worldView.addPlugin(new MIPKeyWalk()); worldView.addPlugin(new MIPKeyWalk());
worldView.addPlugin(new MIPMouseWalk()); worldView.addPlugin(new MIPClickPathfWalk());
//worldView.addPlugin(new MIPMouseWalk());
// size of lower navbar // size of lower navbar
final Num lownav = root.width().min(root.height()).max(700).perc(7); final Num lownav = root.width().min(root.height()).max(700).perc(7);

@ -0,0 +1,38 @@
package mightypork.rogue.screens.gamescreen.world;
import mightypork.gamecore.input.InputSystem;
import mightypork.rogue.world.PlayerControl;
import mightypork.rogue.world.WorldPos;
import mightypork.util.math.constraints.vect.Vect;
public class MIPClickPathfWalk implements MapInteractionPlugin {
@Override
public void onStepEnd(MapView wv, PlayerControl player)
{
if (InputSystem.isMouseButtonDown(0)) {
final WorldPos clicked = wv.toWorldPos(InputSystem.getMousePos());
player.navigateTo(clicked);
}
}
@Override
public void onClick(MapView wv, PlayerControl player, Vect mouse, int button, boolean down)
{
if (!down) return;
final WorldPos clicked = wv.toWorldPos(mouse);
player.navigateTo(clicked);
}
@Override
public void onKey(MapView wv, PlayerControl player, int key, boolean down)
{
}
}

@ -17,7 +17,7 @@ public class MIPKeyWalk implements MapInteractionPlugin {
@Override @Override
public void onClick(MapView wv, PlayerControl player, Vect mouse, boolean down) public void onClick(MapView wv, PlayerControl player, Vect mouse, int button, boolean down)
{ {
} }
@ -25,7 +25,7 @@ public class MIPKeyWalk implements MapInteractionPlugin {
@Override @Override
public void onKey(MapView wv, PlayerControl player, int key, boolean down) public void onKey(MapView wv, PlayerControl player, int key, boolean down)
{ {
if(down) walkByKey(player); if (down) walkByKey(player);
} }

@ -16,15 +16,15 @@ public class MIPMouseWalk implements MapInteractionPlugin {
{ {
if (InputSystem.isMouseButtonDown(0)) { if (InputSystem.isMouseButtonDown(0)) {
// walk by holding btn // walk by holding btn
onClick(wv, player, InputSystem.getMousePos(), true); onClick(wv, player, InputSystem.getMousePos(), 0, true);
} }
} }
@Override @Override
public void onClick(MapView wv, PlayerControl player, Vect mouse, boolean down) public void onClick(MapView wv, PlayerControl player, Vect mouse, int button, boolean down)
{ {
if(!down) return; if (!down) return;
final WorldPos plpos = player.getPos(); final WorldPos plpos = player.getPos();
final WorldPos clicked = wv.toWorldPos(mouse); final WorldPos clicked = wv.toWorldPos(mouse);

@ -10,7 +10,7 @@ public interface MapInteractionPlugin {
void onStepEnd(MapView wv, PlayerControl player); void onStepEnd(MapView wv, PlayerControl player);
void onClick(MapView wv, PlayerControl player, Vect mouse, boolean down); void onClick(MapView wv, PlayerControl player, Vect mouse, int button, boolean down);
void onKey(MapView wv, PlayerControl player, int key, boolean down); void onKey(MapView wv, PlayerControl player, int key, boolean down);

@ -31,7 +31,7 @@ public class MapView extends InputComponent implements KeyListener, MouseButtonL
public MapView(World world) public MapView(World world)
{ {
this.world = world; this.world = world;
this.worldRenderer = new WorldRenderer(world, this, 8, 8, 64); this.worldRenderer = new WorldRenderer(world, this, 12, 8, 40);//8, 8, 64
pc = world.getPlayerControl(); pc = world.getPlayerControl();
pc.addMoveListener(this); pc.addMoveListener(this);
} }
@ -96,7 +96,7 @@ public class MapView extends InputComponent implements KeyListener, MouseButtonL
if (!event.isOver(this)) return; if (!event.isOver(this)) return;
for (final MapInteractionPlugin p : plugins) { for (final MapInteractionPlugin p : plugins) {
p.onClick(this, pc, event.getPos(), event.isDown()); p.onClick(this, pc, event.getPos(), event.getButton(), event.isDown());
} }
event.consume(); // only our clicks. event.consume(); // only our clicks.

@ -1,4 +1,4 @@
package mightypork.rogue.world.gen; package mightypork.rogue.world;
import mightypork.util.annotations.FactoryMethod; import mightypork.util.annotations.FactoryMethod;
@ -16,13 +16,15 @@ public class Coord {
{ {
return new Coord(x, y); return new Coord(x, y);
} }
@FactoryMethod @FactoryMethod
public static Coord make(Coord other) public static Coord make(Coord other)
{ {
return new Coord(other); return new Coord(other);
} }
public Coord(int x, int y) public Coord(int x, int y)
{ {
super(); super();
@ -44,14 +46,46 @@ public class Coord {
} }
public Coord add(Coord other)
{
return add(other.x, other.y);
}
public Coord copy() public Coord copy()
{ {
return make(this); return make(this);
} }
@Override @Override
public String toString() public String toString()
{ {
return "Coord("+x+","+y+")"; return "Coord(" + x + "," + y + ")";
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj == null) return false;
if (!(obj instanceof Coord)) return false;
final Coord other = (Coord) obj;
if (x != other.x) return false;
if (y != other.y) return false;
return true;
} }
} }

@ -65,4 +65,11 @@ public class PathStep implements IonBinary {
return ION_MARK; return ION_MARK;
} }
@Override
public String toString()
{
return "(" + x + "|" + y + ")";
}
} }

@ -1,14 +1,43 @@
package mightypork.rogue.world; package mightypork.rogue.world;
import java.util.List;
import mightypork.rogue.world.entity.models.EntityMoveListener; import mightypork.rogue.world.entity.models.EntityMoveListener;
import mightypork.rogue.world.level.Level; import mightypork.rogue.world.level.Level;
import mightypork.rogue.world.pathfinding.PathCostProvider;
import mightypork.rogue.world.pathfinding.PathFinder;
public class PlayerControl { public class PlayerControl {
private final World world; private final World world;
PathCostProvider costProvider = new PathCostProvider() {
@Override
public boolean isAccessible(Coord pos)
{
return getLevel().canWalkInto(pos.x, pos.y);
}
@Override
public int getMinCost()
{
return 10;
}
@Override
public int getCost(Coord from, Coord to)
{
return 10;
}
};
private final PathFinder pf = new PathFinder(costProvider, PathFinder.CORNER_HEURISTIC);
public PlayerControl(World w) public PlayerControl(World w)
{ {
@ -18,28 +47,45 @@ public class PlayerControl {
public void goNorth() public void goNorth()
{ {
world.getPlayerEntity().cancelPath();
world.getPlayerEntity().addStep(PathStep.NORTH); world.getPlayerEntity().addStep(PathStep.NORTH);
} }
public void goSouth() public void goSouth()
{ {
world.getPlayerEntity().cancelPath();
world.getPlayerEntity().addStep(PathStep.SOUTH); world.getPlayerEntity().addStep(PathStep.SOUTH);
} }
public void goEast() public void goEast()
{ {
world.getPlayerEntity().cancelPath();
world.getPlayerEntity().addStep(PathStep.EAST); world.getPlayerEntity().addStep(PathStep.EAST);
} }
public void goWest() public void goWest()
{ {
world.getPlayerEntity().cancelPath();
world.getPlayerEntity().addStep(PathStep.WEST); world.getPlayerEntity().addStep(PathStep.WEST);
} }
public void navigateTo(WorldPos where)
{
final Coord start = world.getPlayerEntity().getPosition().toCoord();
final Coord end = where.toCoord();
final List<PathStep> path = pf.findPathRelative(start, end);
if (path == null) return;
world.getPlayerEntity().cancelPath();
world.getPlayerEntity().addSteps(path);
}
public void addMoveListener(EntityMoveListener eml) public void addMoveListener(EntityMoveListener eml)
{ {
world.getPlayerEntity().addMoveListener(eml); world.getPlayerEntity().addMoveListener(eml);

@ -84,13 +84,14 @@ public class World implements IonBundled, Updateable {
{ {
return eid++; return eid++;
} }
public void createPlayer(WorldPos pos, int level) public void createPlayer(WorldPos pos, int level)
{ {
createPlayer(pos.x, pos.y, level); createPlayer(pos.x, pos.y, level);
} }
public void createPlayer(int x, int y, int level) public void createPlayer(int x, int y, int level)
{ {
if (playerInfo.isInitialized()) { if (playerInfo.isInitialized()) {
@ -125,5 +126,5 @@ public class World implements IonBundled, Updateable {
{ {
return playerEntity; return playerEntity;
} }
} }

@ -5,11 +5,9 @@ import java.util.Random;
import mightypork.rogue.world.gen.LevelGenerator; import mightypork.rogue.world.gen.LevelGenerator;
import mightypork.rogue.world.level.Level; 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(); public static final Random rand = new Random();

@ -107,12 +107,14 @@ public class WorldPos implements IonBundled, Updateable {
setTo(other.x, other.y); setTo(other.x, other.y);
} }
@Override @Override
public String toString() public String toString()
{ {
return "WorldPos("+x+","+y+")"; return "WorldPos(" + x + "," + y + ")";
} }
@Override @Override
public int hashCode() public int hashCode()
{ {
@ -157,4 +159,10 @@ public class WorldPos implements IonBundled, Updateable {
return walkOffset.isFinished(); return walkOffset.isFinished();
} }
public Coord toCoord()
{
return new Coord(x, y);
}
} }

@ -275,4 +275,10 @@ public final class Entity implements IonBinary, IonBundled, EntityMoveListener {
{ {
moveListeners.add(listener); moveListeners.add(listener);
} }
public void addSteps(List<PathStep> path)
{
this.path.addAll(path);
}
} }

@ -3,43 +3,38 @@ package mightypork.rogue.world.gen;
import java.util.Random; import java.util.Random;
import mightypork.rogue.world.WorldPos; import mightypork.rogue.world.Coord;
import mightypork.rogue.world.gen.rooms.IntersectionRoom; import mightypork.rogue.world.gen.rooms.SimpleRectRoom;
import mightypork.rogue.world.gen.rooms.SquareRoom;
import mightypork.rogue.world.gen.themes.ThemeDungeon; import mightypork.rogue.world.gen.themes.ThemeDungeon;
import mightypork.rogue.world.level.Level; 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 class LevelGenerator {
public static final Theme DUNGEON_THEME = new ThemeDungeon(); public static final Theme DUNGEON_THEME = new ThemeDungeon();
public static final RoomBuilder ROOM_SQUARE = new SquareRoom(); public static final RoomBuilder ROOM_SQUARE = new SimpleRectRoom();
public static final RoomBuilder ROOM_INTERSECTION = new IntersectionRoom();
public static Level build(long seed, Theme theme) 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 // start
map.addRoom(ROOM_SQUARE); 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); map.addRoom(ROOM_SQUARE);
for(int j=0;j<4;j++) map.addRoom(ROOM_INTERSECTION);
} }
map.buildCorridors();
Coord size = map.getNeededSize(); final Coord size = map.getNeededSize();
Level lvl = new Level(size.x, size.y); final Level lvl = new Level(size.x, size.y);
map.writeToLevel(lvl); map.writeToLevel(lvl);

@ -1,12 +1,17 @@
package mightypork.rogue.world.gen; package mightypork.rogue.world.gen;
import java.util.List;
import java.util.Random; 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); RoomDesc buildToFit(ScratchMap map, Theme theme, Random rand, Coord center);
} }

@ -1,25 +1,25 @@
package mightypork.rogue.world.gen; package mightypork.rogue.world.gen;
import java.util.ArrayList; import mightypork.rogue.world.Coord;
import java.util.List;
// room info /**
* Room description
*
* @author MightyPork
*/
public class RoomDesc { public class RoomDesc {
final List<Coord> doors = new ArrayList<>();
final Coord min; final Coord min;
final Coord max; final Coord max;
public RoomDesc(Coord min, Coord max, List<Coord> doors) public RoomDesc(Coord min, Coord max)
{ {
super(); super();
this.min = min; this.min = min;
this.max = max; this.max = max;
this.doors.addAll(doors);
} }

@ -5,25 +5,60 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import mightypork.rogue.world.Coord;
import mightypork.rogue.world.WorldPos; import mightypork.rogue.world.WorldPos;
import mightypork.rogue.world.level.Level; 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.Tile;
import mightypork.rogue.world.tile.Tiles; import mightypork.rogue.world.tile.Tiles;
import mightypork.rogue.world.tile.models.TileModel; import mightypork.rogue.world.tile.models.TileModel;
import mightypork.util.logging.Log;
public class ScratchMap { public class ScratchMap {
private Tile[][] map; private final Tile[][] map;
private int width; private final int width;
private int height; private final int height;
private List<RoomDesc> rooms = new ArrayList<>(); private final List<RoomDesc> rooms = new ArrayList<>();
private final List<Coord> 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 genMin;
Coord genMax; Coord genMax;
private Theme theme; private final Theme theme;
private Random rand; private final Random rand;
private Coord enterPoint; private Coord enterPoint;
@ -31,7 +66,7 @@ public class ScratchMap {
{ {
map = new Tile[max_size][max_size]; 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); genMax = genMin.add(1, 1);
width = max_size; width = max_size;
@ -45,15 +80,33 @@ public class ScratchMap {
public void addRoom(RoomBuilder rb) public void addRoom(RoomBuilder rb)
{ {
Coord center = Coord.make(0, 0); final Coord center = Coord.make(0, 0);
int failed = 0; int failed = 0;
while (true) { 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 (rd != null) {
if (rooms.isEmpty()) { if (rooms.isEmpty()) {
enterPoint = center.copy(); enterPoint = center.copy();
@ -67,19 +120,15 @@ public class ScratchMap {
genMax.x = Math.max(genMax.x, rd.max.x); genMax.x = Math.max(genMax.x, rd.max.x);
genMax.y = Math.max(genMax.y, rd.max.y); genMax.y = Math.max(genMax.y, rd.max.y);
nodes.add(center);
Log.f3("placed room: " + rd.min + " -> " + rd.max);
return; return;
} else { } else {
failed++; 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; return;
} }
} }
@ -97,7 +146,7 @@ public class ScratchMap {
public Tile get(Coord pos) public Tile get(Coord pos)
{ {
if (!isIn(pos)) { if (!isIn(pos)) {
return Tiles.NULL_SOLID.createTile(); throw new IndexOutOfBoundsException("Tile not in map: " + pos);
} }
return map[pos.y][pos.x]; return map[pos.y][pos.x];
@ -113,7 +162,7 @@ public class ScratchMap {
public boolean set(Coord pos, Tile tile) public boolean set(Coord pos, Tile tile)
{ {
if (!isIn(pos)) { if (!isIn(pos)) {
return false; throw new IndexOutOfBoundsException("Tile not in map: " + pos);
} }
map[pos.y][pos.x] = tile; 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) public boolean isClear(Coord min, Coord max)
{ {
if (!isIn(min)) return false; if (!isIn(min) || !isIn(max)) return false;
if (!isIn(max)) return false; for (final RoomDesc r : rooms) {
for (RoomDesc r : rooms) {
if (r.intersectsWith(min, max)) return false; if (r.intersectsWith(min, max)) return false;
} }
@ -144,7 +183,9 @@ public class ScratchMap {
public void fill(Coord min, Coord max, TileModel tm) 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.y = min.y; c.y <= max.y; c.y++)
for (c.x = min.x; c.x <= max.x; c.x++) for (c.x = min.x; c.x <= max.x; c.x++)
set(c, tm.createTile()); set(c, tm.createTile());
@ -153,7 +194,9 @@ public class ScratchMap {
public void border(Coord min, Coord max, TileModel tm) 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.y = min.y; c.y <= max.y; c.y++) {
for (c.x = min.x; c.x <= max.x; c.x++) { 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<Coord> 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() public Coord getNeededSize()
{ {
return Coord.make(genMax.x - genMin.x + 1, genMax.y - genMin.y + 1); 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) public void writeToLevel(Level level)
{ {
final Coord c1 = Coord.make(0, 0);
Coord c1 = Coord.make(0, 0); final Coord c = 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.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++) { for (c.y = genMin.y, c1.y = 0; c.y <= genMax.y; c.y++, c1.y++) {
level.setTile(get(c), c1.x, 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); level.setEnterPoint(p);
} }
} }

@ -1,15 +1,21 @@
package mightypork.rogue.world.gen; package mightypork.rogue.world.gen;
import mightypork.rogue.world.tile.models.TileModel; import mightypork.rogue.world.tile.models.TileModel;
// map theme
/**
* Map theme to use for building
*
* @author MightyPork
*/
public interface Theme { public interface Theme {
TileModel wall(); TileModel wall();
TileModel floor(); TileModel floor();
TileModel door(); TileModel door();
} }

@ -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
}
}

@ -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));
}
}

@ -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<Coord> 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;
}
}

@ -1,9 +1,11 @@
package mightypork.rogue.world.gen.themes; package mightypork.rogue.world.gen.themes;
import mightypork.rogue.world.gen.Theme; import mightypork.rogue.world.gen.Theme;
import mightypork.rogue.world.tile.Tiles; import mightypork.rogue.world.tile.Tiles;
import mightypork.rogue.world.tile.models.TileModel; import mightypork.rogue.world.tile.models.TileModel;
// basic dungeon theme // basic dungeon theme
public class ThemeDungeon implements Theme { public class ThemeDungeon implements Theme {
@ -20,10 +22,11 @@ public class ThemeDungeon implements Theme {
return Tiles.FLOOR_DARK; return Tiles.FLOOR_DARK;
} }
@Override @Override
public TileModel door() public TileModel door()
{ {
return floor(); // TODO return floor(); // TODO
} }
} }

@ -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));
}
}

@ -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);
}

@ -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);
}
}

@ -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();
}

@ -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<PathStep> findPathRelative(Coord start, Coord end)
{
final List<Coord> path = findPath(start, end);
if (path == null) return null;
final List<PathStep> 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<Coord> findPath(Coord start, Coord end)
{
final LinkedList<Node> open = new LinkedList<>();
final LinkedList<Node> 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<Coord> 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<Node> {
@Override
public int compare(Node n1, Node n2)
{
return n1.fCost() - n2.fCost();
}
}
}

@ -34,13 +34,11 @@ public final class Tile implements IonBinary {
/** persistent field for model, reflected by renderer */ /** persistent field for model, reflected by renderer */
public final IonBundle metadata = new IonBundle(); public final IonBundle metadata = new IonBundle();
public final TileRenderData renderData = new TileRenderData();
// temporary flag for map. // temporary flag for map.
private boolean occupied; private boolean occupied;
// for renderer of AO shadows
public byte shadows;
public boolean shadowsComputed;
public Tile(int id) public Tile(int id)
{ {

@ -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;
}

@ -1,11 +1,9 @@
package mightypork.rogue.world.tile; package mightypork.rogue.world.tile;
import mightypork.rogue.world.tile.models.Floor; import mightypork.rogue.world.tile.models.*;
import mightypork.rogue.world.tile.models.NullFloor; import mightypork.rogue.world.tile.renderers.FloorRenderer;
import mightypork.rogue.world.tile.models.NullWall; import mightypork.rogue.world.tile.renderers.WallRenderer;
import mightypork.rogue.world.tile.models.TileModel;
import mightypork.rogue.world.tile.models.Wall;
/** /**
@ -21,8 +19,10 @@ public final class Tiles {
public static final TileModel NULL_EMPTY = new NullFloor(1); public static final TileModel NULL_EMPTY = new NullFloor(1);
public static final TileModel NULL_EMPTY_RESERVED = new NullFloor(2); 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 FLOOR_DARK = new Floor(10).setRenderer(new FloorRenderer("tile.floor.dark"));
public static final TileModel WALL_BRICK = new Wall(11).setTexture("tile16.wall.brick"); 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"); // public static final TileModel BRICK_FLOOR_VINES = new Floor(2).setTexture("tile.floor.mossy_bricks");

@ -52,10 +52,6 @@ public abstract class AbstractNullTile extends SimpleTile {
} }
@Override
public abstract boolean isWalkable();
@Override @Override
public boolean doesCastShadow() public boolean doesCastShadow()
{ {

@ -1,6 +1,9 @@
package mightypork.rogue.world.tile.models; package mightypork.rogue.world.tile.models;
import mightypork.rogue.world.tile.Tile;
/** /**
* Template for floor tiles with no metadata * Template for floor tiles with no metadata
* *
@ -15,7 +18,7 @@ public class Floor extends SimpleTile {
@Override @Override
public boolean isWalkable() public boolean isWalkable(Tile tile)
{ {
return true; return true;
} }

@ -1,6 +1,9 @@
package mightypork.rogue.world.tile.models; package mightypork.rogue.world.tile.models;
import mightypork.rogue.world.tile.Tile;
public class NullFloor extends AbstractNullTile { public class NullFloor extends AbstractNullTile {
public NullFloor(int id) public NullFloor(int id)
@ -10,7 +13,7 @@ public class NullFloor extends AbstractNullTile {
@Override @Override
public boolean isWalkable() public boolean isWalkable(Tile tile)
{ {
return true; return true;
} }

@ -1,6 +1,9 @@
package mightypork.rogue.world.tile.models; package mightypork.rogue.world.tile.models;
import mightypork.rogue.world.tile.Tile;
public class NullWall extends AbstractNullTile { public class NullWall extends AbstractNullTile {
public NullWall(int id) public NullWall(int id)
@ -10,9 +13,8 @@ public class NullWall extends AbstractNullTile {
@Override @Override
public boolean isWalkable() public boolean isWalkable(Tile tile)
{ {
return false; return false;
} }
} }

@ -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;
}
}

@ -20,14 +20,14 @@ public abstract class SimpleTile extends TileModel {
@Override @Override
public boolean isWalkable(Tile tile) public abstract boolean isWalkable(Tile tile);
{
return isWalkable();
}
@Override @Override
public abstract boolean isWalkable(); public boolean isDoor()
{
return false;
}
@Override @Override

@ -4,7 +4,6 @@ package mightypork.rogue.world.tile.models;
import mightypork.rogue.world.level.Level; import mightypork.rogue.world.level.Level;
import mightypork.rogue.world.tile.Tile; import mightypork.rogue.world.tile.Tile;
import mightypork.rogue.world.tile.Tiles; import mightypork.rogue.world.tile.Tiles;
import mightypork.rogue.world.tile.renderers.BasicTileRenderer;
import mightypork.rogue.world.tile.renderers.TileRenderer; 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. * @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); public abstract boolean isWalkable(Tile tile);
/** public abstract boolean isDoor();
* @return true if walkable at some conditions
*/
public abstract boolean isWalkable();
public abstract boolean doesCastShadow(); public abstract boolean doesCastShadow();

@ -1,6 +1,9 @@
package mightypork.rogue.world.tile.models; package mightypork.rogue.world.tile.models;
import mightypork.rogue.world.tile.Tile;
/** /**
* Template for wall tiles with no metadata * Template for wall tiles with no metadata
* *
@ -15,7 +18,7 @@ public class Wall extends SimpleTile {
@Override @Override
public boolean isWalkable() public boolean isWalkable(Tile tile)
{ {
return false; return false;
} }

@ -2,11 +2,9 @@ package mightypork.rogue.world.tile.renderers;
import mightypork.gamecore.render.Render; import mightypork.gamecore.render.Render;
import mightypork.gamecore.render.textures.TxQuad;
import mightypork.gamecore.render.textures.TxSheet; import mightypork.gamecore.render.textures.TxSheet;
import mightypork.rogue.Res; import mightypork.rogue.Res;
import mightypork.rogue.world.level.render.TileRenderContext; import mightypork.rogue.world.level.render.TileRenderContext;
import mightypork.rogue.world.tile.Tile;
import mightypork.util.math.constraints.rect.Rect; import mightypork.util.math.constraints.rect.Rect;
@ -14,24 +12,10 @@ public class BasicTileRenderer extends TileRenderer {
private final TxSheet sheet; 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) public BasicTileRenderer(String sheetKey)
{ {
this.sheet = Res.getTxSheet(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(); final Rect rect = context.getRect();
Render.quadTextured(rect, sheet.getRandomQuad(context.getTileNoise())); 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);
} }
} }

@ -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);
}
}
}

@ -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);
}
}

@ -0,0 +1,11 @@
package mightypork.rogue.world.tile.renderers;
public class WallRenderer extends BasicTileRenderer {
public WallRenderer(String sheetKey)
{
super(sheetKey);
}
}

@ -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();
}
}
}

@ -59,11 +59,11 @@ public class TiledRect extends RectProxy {
public Rect tile(int x, int y) public Rect tile(int x, int y)
{ {
if (x >= tilesX || x < 0) { 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) { 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)); return aTile.move(perCol.mul(x), perRow.mul(y));

Loading…
Cancel
Save