A* path finding, room & corridor generation, pathfinding mouse control
This commit is contained in:
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("tile16.shadow.n", tiles.makeQuad(0, 7));
|
textures.addQuad("tile.door.closed", tiles.makeQuad(1, 2));
|
||||||
textures.addQuad("tile16.shadow.s", tiles.makeQuad(0, 7).flipY());
|
textures.addQuad("tile.door.open", tiles.makeQuad(2, 2));
|
||||||
textures.addQuad("tile16.shadow.w", tiles.makeQuad(2, 7));
|
|
||||||
textures.addQuad("tile16.shadow.e", tiles.makeQuad(2, 7).flipX());
|
|
||||||
|
|
||||||
textures.addQuad("tile16.shadow.nw", tiles.makeQuad(1, 7));
|
textures.addQuad("tile.shadow.n", tiles.makeQuad(0, 7));
|
||||||
textures.addQuad("tile16.shadow.ne", tiles.makeQuad(1, 7).flipX());
|
textures.addQuad("tile.shadow.s", tiles.makeQuad(0, 7).flipY());
|
||||||
textures.addQuad("tile16.shadow.sw", tiles.makeQuad(1, 7).flipY());
|
textures.addQuad("tile.shadow.w", tiles.makeQuad(2, 7));
|
||||||
textures.addQuad("tile16.shadow.se", tiles.makeQuad(1, 7).flipY().flipX());
|
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.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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -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));
|
||||||
|
|||||||
Reference in New Issue
Block a user