A* path finding, room & corridor generation, pathfinding mouse control
This commit is contained in:
@@ -63,7 +63,8 @@ public class DisplaySystem extends AppModule implements RectBound {
|
||||
@Override
|
||||
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("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
|
||||
texture = textures.loadTexture("mob", "/res/img/dudes.png", FilterMode.NEAREST, WrapMode.CLAMP);
|
||||
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);
|
||||
tiles = texture.grid(8, 8);
|
||||
|
||||
textures.addSheet("tile16.floor.dark", tiles.makeSheet(0, 1, 5, 1));
|
||||
textures.addSheet("tile16.wall.brick", tiles.makeSheet(0, 0, 5, 1));
|
||||
textures.addSheet("tile.floor.dark", tiles.makeSheet(0, 1, 5, 1));
|
||||
textures.addSheet("tile.wall.brick", tiles.makeSheet(0, 0, 5, 1));
|
||||
|
||||
textures.addQuad("tile16.shadow.n", tiles.makeQuad(0, 7));
|
||||
textures.addQuad("tile16.shadow.s", tiles.makeQuad(0, 7).flipY());
|
||||
textures.addQuad("tile16.shadow.w", tiles.makeQuad(2, 7));
|
||||
textures.addQuad("tile16.shadow.e", tiles.makeQuad(2, 7).flipX());
|
||||
textures.addQuad("tile.door.closed", tiles.makeQuad(1, 2));
|
||||
textures.addQuad("tile.door.open", tiles.makeQuad(2, 2));
|
||||
|
||||
textures.addQuad("tile16.shadow.nw", tiles.makeQuad(1, 7));
|
||||
textures.addQuad("tile16.shadow.ne", tiles.makeQuad(1, 7).flipX());
|
||||
textures.addQuad("tile16.shadow.sw", tiles.makeQuad(1, 7).flipY());
|
||||
textures.addQuad("tile16.shadow.se", tiles.makeQuad(1, 7).flipY().flipX());
|
||||
textures.addQuad("tile.shadow.n", tiles.makeQuad(0, 7));
|
||||
textures.addQuad("tile.shadow.s", tiles.makeQuad(0, 7).flipY());
|
||||
textures.addQuad("tile.shadow.w", tiles.makeQuad(2, 7));
|
||||
textures.addQuad("tile.shadow.e", tiles.makeQuad(2, 7).flipX());
|
||||
|
||||
textures.addQuad("tile.shadow.nw", tiles.makeQuad(1, 7));
|
||||
textures.addQuad("tile.shadow.ne", tiles.makeQuad(1, 7).flipX());
|
||||
textures.addQuad("tile.shadow.sw", tiles.makeQuad(1, 7).flipY());
|
||||
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.gui.screens.LayeredScreen;
|
||||
import mightypork.rogue.Paths;
|
||||
import mightypork.rogue.world.MapGenerator;
|
||||
import mightypork.rogue.world.World;
|
||||
import mightypork.rogue.world.WorldCreator;
|
||||
import mightypork.util.files.ion.Ion;
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ public class ScreenGame extends LayeredScreen {
|
||||
|
||||
// SAVE
|
||||
|
||||
final World world = MapGenerator.createWorld(rand.nextLong());
|
||||
final World world = WorldCreator.createWorld(rand.nextLong());
|
||||
addChildClient(world);
|
||||
|
||||
try {
|
||||
|
||||
@@ -3,8 +3,8 @@ package mightypork.rogue.screens.gamescreen;
|
||||
|
||||
import mightypork.gamecore.gui.screens.Screen;
|
||||
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.MIPMouseWalk;
|
||||
import mightypork.rogue.screens.gamescreen.world.MapView;
|
||||
import mightypork.rogue.world.World;
|
||||
import mightypork.util.math.constraints.num.Num;
|
||||
@@ -25,7 +25,8 @@ public class WorldLayer extends ScreenLayer {
|
||||
|
||||
// map input plugins
|
||||
worldView.addPlugin(new MIPKeyWalk());
|
||||
worldView.addPlugin(new MIPMouseWalk());
|
||||
worldView.addPlugin(new MIPClickPathfWalk());
|
||||
//worldView.addPlugin(new MIPMouseWalk());
|
||||
|
||||
// size of lower navbar
|
||||
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
|
||||
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
|
||||
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)) {
|
||||
// walk by holding btn
|
||||
onClick(wv, player, InputSystem.getMousePos(), true);
|
||||
onClick(wv, player, InputSystem.getMousePos(), 0, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@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 clicked = wv.toWorldPos(mouse);
|
||||
|
||||
@@ -10,7 +10,7 @@ public interface MapInteractionPlugin {
|
||||
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);
|
||||
|
||||
@@ -31,7 +31,7 @@ public class MapView extends InputComponent implements KeyListener, MouseButtonL
|
||||
public MapView(World world)
|
||||
{
|
||||
this.world = world;
|
||||
this.worldRenderer = new WorldRenderer(world, this, 8, 8, 64);
|
||||
this.worldRenderer = new WorldRenderer(world, this, 12, 8, 40);//8, 8, 64
|
||||
pc = world.getPlayerControl();
|
||||
pc.addMoveListener(this);
|
||||
}
|
||||
@@ -96,7 +96,7 @@ public class MapView extends InputComponent implements KeyListener, MouseButtonL
|
||||
if (!event.isOver(this)) return;
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package mightypork.rogue.world.gen;
|
||||
package mightypork.rogue.world;
|
||||
|
||||
|
||||
import mightypork.util.annotations.FactoryMethod;
|
||||
@@ -16,13 +16,15 @@ public class Coord {
|
||||
{
|
||||
return new Coord(x, y);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@FactoryMethod
|
||||
public static Coord make(Coord other)
|
||||
{
|
||||
return new Coord(other);
|
||||
}
|
||||
|
||||
|
||||
public Coord(int x, int y)
|
||||
{
|
||||
super();
|
||||
@@ -44,14 +46,46 @@ public class Coord {
|
||||
}
|
||||
|
||||
|
||||
public Coord add(Coord other)
|
||||
{
|
||||
return add(other.x, other.y);
|
||||
}
|
||||
|
||||
|
||||
public Coord copy()
|
||||
{
|
||||
return make(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "(" + x + "|" + y + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,14 +1,43 @@
|
||||
package mightypork.rogue.world;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import mightypork.rogue.world.entity.models.EntityMoveListener;
|
||||
import mightypork.rogue.world.level.Level;
|
||||
import mightypork.rogue.world.pathfinding.PathCostProvider;
|
||||
import mightypork.rogue.world.pathfinding.PathFinder;
|
||||
|
||||
|
||||
public class PlayerControl {
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -18,28 +47,45 @@ public class PlayerControl {
|
||||
|
||||
public void goNorth()
|
||||
{
|
||||
world.getPlayerEntity().cancelPath();
|
||||
world.getPlayerEntity().addStep(PathStep.NORTH);
|
||||
}
|
||||
|
||||
|
||||
public void goSouth()
|
||||
{
|
||||
world.getPlayerEntity().cancelPath();
|
||||
world.getPlayerEntity().addStep(PathStep.SOUTH);
|
||||
}
|
||||
|
||||
|
||||
public void goEast()
|
||||
{
|
||||
world.getPlayerEntity().cancelPath();
|
||||
world.getPlayerEntity().addStep(PathStep.EAST);
|
||||
}
|
||||
|
||||
|
||||
public void goWest()
|
||||
{
|
||||
world.getPlayerEntity().cancelPath();
|
||||
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)
|
||||
{
|
||||
world.getPlayerEntity().addMoveListener(eml);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+1
-3
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -275,4 +275,10 @@ public final class Entity implements IonBinary, IonBundled, EntityMoveListener {
|
||||
{
|
||||
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 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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Coord> doors = new ArrayList<>();
|
||||
final Coord min;
|
||||
final Coord max;
|
||||
|
||||
|
||||
public RoomDesc(Coord min, Coord max, List<Coord> doors)
|
||||
public RoomDesc(Coord min, Coord max)
|
||||
{
|
||||
super();
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
|
||||
this.doors.addAll(doors);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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<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 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<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()
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
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");
|
||||
|
||||
@@ -52,10 +52,6 @@ public abstract class AbstractNullTile extends SimpleTile {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public abstract boolean isWalkable();
|
||||
|
||||
|
||||
@Override
|
||||
public boolean doesCastShadow()
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
public boolean isWalkable(Tile tile)
|
||||
{
|
||||
return isWalkable();
|
||||
}
|
||||
public abstract boolean isWalkable(Tile tile);
|
||||
|
||||
|
||||
@Override
|
||||
public abstract boolean isWalkable();
|
||||
public boolean isDoor()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
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));
|
||||
|
||||
Reference in New Issue
Block a user