parent
69f55dcc12
commit
eef373eb24
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 4.5 KiB |
@ -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) |
||||
{ |
||||
} |
||||
|
||||
} |
@ -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; |
||||
} |
||||
|
||||
} |
@ -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(); |
||||
} |
||||
} |
||||
} |
@ -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; |
||||
|
||||
} |
@ -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; |
||||
} |
||||
} |
@ -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(); |
||||
} |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue