diff --git a/core/src/gdx/diablo/entity/Entity.java b/core/src/gdx/diablo/entity/Entity.java index eb0f7912..e2e1658e 100644 --- a/core/src/gdx/diablo/entity/Entity.java +++ b/core/src/gdx/diablo/entity/Entity.java @@ -3,8 +3,6 @@ package gdx.diablo.entity; import android.support.annotation.CallSuper; import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.ai.pfa.DefaultGraphPath; -import com.badlogic.gdx.ai.pfa.GraphPath; import com.badlogic.gdx.assets.AssetDescriptor; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.g2d.Batch; @@ -12,11 +10,14 @@ import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.utils.Align; +import com.badlogic.gdx.utils.Array; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; import gdx.diablo.Diablo; import gdx.diablo.codec.Animation; @@ -29,7 +30,7 @@ import gdx.diablo.graphics.PaletteIndexedColorDrawable; import gdx.diablo.map.DS1; import gdx.diablo.map.DT1.Tile; import gdx.diablo.map.Map; -import gdx.diablo.map.Point2; +import gdx.diablo.map.MapGraph; import gdx.diablo.widget.Label; public class Entity { @@ -40,6 +41,8 @@ public class Entity { private static final boolean DEBUG_DIRTY = DEBUG && true; private static final boolean DEBUG_ASSETS = DEBUG && true; private static final boolean DEBUG_STATE = DEBUG && true; + private static final boolean DEBUG_PATH = DEBUG && !true; + private static final boolean DEBUG_TARGET = DEBUG && true; protected enum EntType { OBJECT("OBJECTS"), @@ -130,7 +133,8 @@ public class Entity { Label label; String name; Vector3 target = new Vector3(); - GraphPath path = new DefaultGraphPath<>(); + MapGraph.MapGraphPath path = new MapGraph.MapGraphPath(); + Iterator targets = Collections.emptyIterator(); public static Entity create(DS1 ds1, DS1.Object obj) { final int type = obj.type; @@ -224,12 +228,57 @@ public class Entity { return target; } - public GraphPath path() { + public MapGraph.MapGraphPath path() { return path; } - public void updatePath(Map map, Vector3 target) { - map.path(position, target, path); + public void setPath(Map map, Vector3 dst) { + boolean success = map.findPath(position, dst, path); + if (!success) return; + if (DEBUG_PATH) Gdx.app.debug(TAG, "path=" + path); + map.smoothPath(path); + targets = new Array.ArrayIterator<>(path.nodes); + targets.next(); // consume src position + if (targets.hasNext()) { + MapGraph.Point2 firstDst = targets.next(); + target.set(firstDst.x, firstDst.y, 0); + } else { + target.set(position); + } + + //if (DEBUG_TARGET) Gdx.app.debug(TAG, "target=" + target); + } + + public void update(float delta) { + if (target.equals(Vector3.Zero)) return; + if (position.epsilonEquals(target)) { + if (!targets.hasNext()) { + path.clear(); + setMode("NU"); + return; + } + } + + setMode("RN"); + //float targetLen = target.len(); + float speed = 9f * 2f; + float distance = speed * delta; + float traveled = 0; + while (traveled < distance) { + float targetLen = position.dst(target); + float part = Math.min(distance - traveled, targetLen); + if (part == 0) break; + position.lerp(target, part / targetLen); + traveled += part; + if (part == targetLen) { + if (targets.hasNext()) { + MapGraph.Point2 next = targets.next(); + target.set(next.x, next.y, 0); + } else { + break; + } + } + } } public float getAngle() { @@ -406,24 +455,6 @@ public class Entity { label.draw(batch, 1); } - public boolean move() { - int x = Direction.getOffX(angle); - int y = Direction.getOffY(angle); - position.add(x, y, 0); - //if (position.epsilonEquals(target) || target.equals(Vector3.Zero)) { - // if (path.getCount() > 0) { - // Point2 point = path.get(0); - // target.set(point.x, point.y, 0); - // } else { - // setMode("NU"); - // } - //} - - //position.lerp(target, 1f); - //position.set(target); - return true; - } - public boolean contains(Vector3 coords) { if (animation == null) return false; BBox box = animation.getBox(); diff --git a/core/src/gdx/diablo/entity/Player.java b/core/src/gdx/diablo/entity/Player.java index a96b0786..b1516f25 100644 --- a/core/src/gdx/diablo/entity/Player.java +++ b/core/src/gdx/diablo/entity/Player.java @@ -296,6 +296,7 @@ public class Player extends Entity { setArmType(Component.SH, SH != null ? SH.base.alternateGfx : ""); } + /* @Override public boolean move() { if (!mode.equalsIgnoreCase("WL") @@ -306,6 +307,7 @@ public class Player extends Entity { return super.move(); } + */ private void notifySlotChanged(BodyLoc bodyLoc, Item oldItem, Item item) { for (SlotListener l : SLOT_LISTENERS) l.onChanged(this, bodyLoc, oldItem, item); diff --git a/core/src/gdx/diablo/map/Map.java b/core/src/gdx/diablo/map/Map.java index ef4fdf51..206c0be0 100644 --- a/core/src/gdx/diablo/map/Map.java +++ b/core/src/gdx/diablo/map/Map.java @@ -4,11 +4,14 @@ import com.google.common.base.Preconditions; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.ai.pfa.GraphPath; +import com.badlogic.gdx.ai.pfa.SmoothableGraphPath; +import com.badlogic.gdx.ai.pfa.indexed.IndexedAStarPathFinder; import com.badlogic.gdx.assets.AssetDescriptor; import com.badlogic.gdx.assets.AssetManager; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.math.GridPoint2; import com.badlogic.gdx.math.MathUtils; +import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Bits; @@ -545,13 +548,15 @@ public class Map implements Disposable { } } - public boolean path(Vector3 src, Vector3 dst, GraphPath path) { - //return new MapGraph(this).path(src, dst); - //return MapUtils.path(this, src, dst, new DefaultGraphPath()); - //long start = System.currentTimeMillis(); - //... - //System.out.println("time = " + (System.currentTimeMillis() - start) + "ms"); - return new MapPather(this).path(src, dst, path); + private MapGraph mapGraph = new MapGraph(this); + private IndexedAStarPathFinder pathFinder = new IndexedAStarPathFinder<>(mapGraph, true); + + public boolean findPath(Vector3 src, Vector3 dst, GraphPath path) { + return mapGraph.searchNodePath(pathFinder, src, dst, path); + } + + public void smoothPath(SmoothableGraphPath path) { + mapGraph.smoothPath(path); } static class Zone { diff --git a/core/src/gdx/diablo/map/MapGraph.java b/core/src/gdx/diablo/map/MapGraph.java new file mode 100644 index 00000000..2cfc3fcf --- /dev/null +++ b/core/src/gdx/diablo/map/MapGraph.java @@ -0,0 +1,301 @@ +package gdx.diablo.map; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.ai.pfa.Connection; +import com.badlogic.gdx.ai.pfa.DefaultConnection; +import com.badlogic.gdx.ai.pfa.DefaultGraphPath; +import com.badlogic.gdx.ai.pfa.GraphPath; +import com.badlogic.gdx.ai.pfa.Heuristic; +import com.badlogic.gdx.ai.pfa.PathFinder; +import com.badlogic.gdx.ai.pfa.PathSmoother; +import com.badlogic.gdx.ai.pfa.SmoothableGraphPath; +import com.badlogic.gdx.ai.pfa.indexed.IndexedAStarPathFinder; +import com.badlogic.gdx.ai.pfa.indexed.IndexedGraph; +import com.badlogic.gdx.ai.utils.Collision; +import com.badlogic.gdx.ai.utils.Ray; +import com.badlogic.gdx.ai.utils.RaycastCollisionDetector; +import com.badlogic.gdx.math.Vector2; +import com.badlogic.gdx.math.Vector3; +import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.IntMap; + +public class MapGraph implements IndexedGraph { + private static final String TAG = "MapGraph"; + private static final boolean DEBUG = true; + private static final boolean DEBUG_METRICS = DEBUG && !true; + + Heuristic heuristic = new EuclideanDistanceHeuristic(); + + Map map; + IntMap points = new IntMap<>(); + PathSmoother pathSmoother; + + public MapGraph(Map map) { + this.map = map; + pathSmoother = new PathSmoother<>(new MapRaycastCollisionDetector(this)); + } + + public GraphPath path(Vector3 src, Vector3 dst, GraphPath path) { + Map.Zone zone = map.getZone((int) dst.x, (int) dst.y); + if (zone != null && zone.flags((int) dst.x, (int) dst.y) != 0) { + return path; + } + + int hash = Point2.hash(src); + Point2 srcP = points.get(hash); + if (srcP == null) { + srcP = new Point2(src); + points.put(hash, srcP); + } + hash = Point2.hash(dst); + Point2 dstP = points.get(hash); + if (dstP == null) { + dstP = new Point2(dst); + points.put(hash, dstP); + } + return path(srcP, dstP, path); + } + + public GraphPath path(Point2 src, Point2 dst, GraphPath path) { + path.clear(); + new IndexedAStarPathFinder<>(this).searchNodePath(src, dst, heuristic, path); + return path; + } + + public boolean searchNodePath(PathFinder pathFinder, Vector3 src, Vector3 dst, GraphPath path) { + path.clear(); + if (dst == null) return false; + Map.Zone zone = map.getZone((int) dst.x, (int) dst.y); + if (zone != null && zone.flags((int) dst.x, (int) dst.y) != 0) { + return false; + } + + int hash = Point2.hash(src); + Point2 srcP = points.get(hash); + if (srcP == null) { + srcP = new Point2(src); + points.put(hash, srcP); + } + hash = Point2.hash(dst); + Point2 dstP = points.get(hash); + if (dstP == null) { + dstP = new Point2(dst); + points.put(hash, dstP); + } + return searchNodePath(pathFinder, srcP, dstP, path); + } + + public boolean searchNodePath(PathFinder pathFinder, Point2 src, Point2 dst, GraphPath path) { + boolean success = pathFinder.searchNodePath(src, dst, heuristic, path); + if (DEBUG_METRICS && pathFinder instanceof IndexedAStarPathFinder) { + IndexedAStarPathFinder.Metrics metrics = ((IndexedAStarPathFinder) pathFinder).metrics; + Gdx.app.debug(TAG, String.format("visitedNodes=%d, openListAdditions=%d, openListPeak=%d", + metrics.visitedNodes, metrics.openListAdditions, metrics.openListPeak)); + } + + return success; + } + + public void smoothPath(SmoothableGraphPath path) { + pathSmoother.smoothPath(path); + } + + @Override + public int getIndex(Point2 node) { + return node.index; + } + + @Override + public int getNodeCount() { + return 2 << 20; + } + + @Override + public Array> getConnections(Point2 src) { + Array> connections = src.connections; + if (connections == null) { + connections = src.connections = new Array<>(8); + tryConnect(src, src.x - 1, src.y - 1); + tryConnect(src, src.x - 1, src.y ); + tryConnect(src, src.x - 1, src.y + 1); + tryConnect(src, src.x , src.y - 1); + tryConnect(src, src.x , src.y + 1); + tryConnect(src, src.x + 1, src.y - 1); + tryConnect(src, src.x + 1, src.y ); + tryConnect(src, src.x + 1, src.y + 1); + } + + return connections; + } + + private void tryConnect(Point2 src, int x, int y) { + Map.Zone zone = map.getZone(x, y); + if (zone != null && zone.flags(x, y) == 0) { + final int hash = Point2.hash(x, y); + Point2 dst = points.get(hash); + if (dst == null) { + dst = new Point2(x, y); + points.put(hash, dst); + } + src.connections.add(new Path(src, dst)); + } + } + + public static class Point2 { + public int x; + public int y; + int index; + Array> connections; + + static int indexes = 0; + + Point2(int x, int y) { + this.x = x; + this.y = y; + index = indexes++; + } + + Point2(Vector3 src) { + this((int) src.x, (int) src.y); + } + + @Override + public int hashCode() { + return hash(x, y); + } + + static int hash(Vector3 src) { + return hash((int) src.x, (int) src.y); + } + + static int hash(int x, int y) { + return (x * 73856093) ^ (y * 83492791); + } + + @Override + public String toString() { + return "(" + x + "," + y + ")"; + } + } + + static class Path extends DefaultConnection { + static final float DIAGONAL_COST = (float)Math.sqrt(2); + + Path(Point2 src, Point2 dst) { + super(src, dst); + } + + @Override + public float getCost() { + return fromNode.x != toNode.x && fromNode.y != toNode.y ? DIAGONAL_COST : 1; + } + } + + static class ManhattanDistanceHeuristic implements Heuristic { + @Override + public float estimate(Point2 src, Point2 dst) { + return Math.abs(dst.x - src.x) + Math.abs(dst.y - src.y); + } + } + + static class EuclideanDistanceHeuristic implements Heuristic { + @Override + public float estimate(Point2 src, Point2 dst) { + return Vector2.dst(src.x, src.y, dst.x, dst.y); + } + } + + public static class MapGraphPath extends DefaultGraphPath implements SmoothableGraphPath { + private Vector2 tmp = new Vector2(); + + @Override + public Vector2 getNodePosition(int index) { + Point2 src = nodes.get(index); + return tmp.set(src.x, src.y); + } + + @Override + public void swapNodes(int index1, int index2) { + nodes.set(index1, nodes.get(index2)); + } + + @Override + public void truncatePath(int newLength) { + nodes.truncate(newLength); + } + + @Override + public String toString() { + return nodes.toString(); + } + } + + static class MapRaycastCollisionDetector implements RaycastCollisionDetector { + Map map; + MapGraph mapGraph; + + public MapRaycastCollisionDetector(MapGraph mapGraph) { + this.mapGraph = mapGraph; + this.map = mapGraph.map; + } + + @Override + public boolean collides(Ray ray) { + int x0 = (int) ray.start.x; + int y0 = (int) ray.start.y; + int x1 = (int) ray.end.x; + int y1 = (int) ray.end.y; + + int tmp; + boolean steep = Math.abs(y1 - y0) > Math.abs(x1 - x0); + if (steep) { + // Swap x0 and y0 + tmp = x0; + x0 = y0; + y0 = tmp; + // Swap x1 and y1 + tmp = x1; + x1 = y1; + y1 = tmp; + } + if (x0 > x1) { + // Swap x0 and x1 + tmp = x0; + x0 = x1; + x1 = tmp; + // Swap y0 and y1 + tmp = y0; + y0 = y1; + y1 = tmp; + } + + int deltax = x1 - x0; + int deltay = Math.abs(y1 - y0); + int error = 0; + int y = y0; + int ystep = (y0 < y1 ? 1 : -1); + for (int x = x0; x <= x1; x++) { + // TODO: Why is this distinction needed? + if (steep) { + Map.Zone zone = map.getZone(y, x); + if (zone == null || zone.flags(y, x) != 0) return true; // We've hit a wall + } else { + Map.Zone zone = map.getZone(x, y); + if (zone == null || zone.flags(x, y) != 0) return true; // We've hit a wall + } + error += deltay; + if (error + error >= deltax) { + y += ystep; + error -= deltax; + } + } + + return false; + } + + @Override + public boolean findCollision(Collision outputCollision, Ray inputRay) { + throw new UnsupportedOperationException(); + } + } +} diff --git a/core/src/gdx/diablo/map/MapPather.java b/core/src/gdx/diablo/map/MapPather.java deleted file mode 100644 index 3a8ab70f..00000000 --- a/core/src/gdx/diablo/map/MapPather.java +++ /dev/null @@ -1,122 +0,0 @@ -package gdx.diablo.map; - -import com.badlogic.gdx.ai.pfa.GraphPath; -import com.badlogic.gdx.math.Vector3; -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.ObjectFloatMap; -import com.badlogic.gdx.utils.ObjectMap; - -public class MapPather { - Map map; - Heuristic heuristic = new DiagonalHeuristic(); - - public MapPather(Map map) { - this.map = map; - } - - public boolean path(Vector3 src, Vector3 dst, GraphPath path) { - if (src.dst(dst) > 25f) return false; - return path(new Point2(src), new Point2(dst), path); - } - - public boolean path(Point2 src, Point2 dst, GraphPath path) { - path.clear(); - Map.Zone zone = map.getZone(dst.x, dst.y); - if (zone == null || zone.flags(dst.x, dst.y) != 0) return false; - - BinaryHeap closedSet = new BinaryHeap<>(); - BinaryHeap openSet = new BinaryHeap<>(); - openSet.add(src); - - closedSet.contains(dst, false); - - ObjectMap cameFrom = new ObjectMap<>(); - - ObjectFloatMap gScore = new ObjectFloatMap<>(); - gScore.put(src, 0); - - ObjectFloatMap fScore = new ObjectFloatMap<>(); - fScore.put(src, heuristic.estimate(src, dst)); - - Array neighbors = new Array<>(8); - - while (openSet.size > 0) { - Point2 current = openSet.pop(); - closedSet.add(current); - if (current.equals(dst)) { - buildPath(current, cameFrom, path); - return true; - } - - getNeighbors(current, neighbors); - for (Point2 neighbor : neighbors) { - if (closedSet.contains(neighbor, false)) { - continue; - } - - float tent_gScore = gScore.get(current, Float.POSITIVE_INFINITY) + Point2.dst(current, neighbor); - if (!openSet.contains(neighbor, false)) { - openSet.add(neighbor); - } else if (tent_gScore >= gScore.get(neighbor, Float.POSITIVE_INFINITY)) { - continue; - } - - cameFrom.put(neighbor, current); - gScore.put(neighbor, tent_gScore); - fScore.put(neighbor, gScore.get(neighbor, Float.POSITIVE_INFINITY) + heuristic.estimate(neighbor, dst)); - } - } - - return false; - } - - private void buildPath(Point2 src, ObjectMap cameFrom, GraphPath path) { - path.add(src); - while (cameFrom.containsKey(src)) { - src = cameFrom.get(src); - path.add(src); - } - } - - private void getNeighbors(Point2 src, Array dst) { - dst.size = 0; - addNeighbor(src, src.x - 1, src.y - 1, dst); - addNeighbor(src, src.x - 1, src.y , dst); - addNeighbor(src, src.x - 1, src.y + 1, dst); - addNeighbor(src, src.x , src.y - 1, dst); - addNeighbor(src, src.x , src.y + 1, dst); - addNeighbor(src, src.x + 1, src.y - 1, dst); - addNeighbor(src, src.x + 1, src.y , dst); - addNeighbor(src, src.x + 1, src.y + 1, dst); - } - - private void addNeighbor(Point2 src, int x, int y, Array dst) { - Map.Zone zone = map.getZone(x, y); - if (zone == null) return; - if (zone.flags(x, y) == 0) { - float cost = src.getValue() + ((x != src.x && y != src.y) ? 1.414213562373095f : 1f); - Point2 point = new Point2(x, y, cost); - dst.add(point); - } - } - - interface Heuristic { - float estimate(Point2 src, Point2 dst); - } - - static class DiagonalHeuristic implements Heuristic { - @Override - public float estimate(Point2 src, Point2 dst) { - int dx = Math.abs(src.x - dst.x); - int dy = Math.abs(src.y - dst.y); - return 1f * Math.max(dx, dy) + (1.414213562373095f-1f) * Math.min(dx, dy); - } - } - - static class EuclideanHeuristic implements Heuristic { - @Override - public float estimate(Point2 src, Point2 dst) { - return Point2.dst(src, dst); - } - } -} diff --git a/core/src/gdx/diablo/map/MapRenderer.java b/core/src/gdx/diablo/map/MapRenderer.java index 5d0cba91..c87928b0 100644 --- a/core/src/gdx/diablo/map/MapRenderer.java +++ b/core/src/gdx/diablo/map/MapRenderer.java @@ -1,7 +1,6 @@ package gdx.diablo.map; import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.ai.pfa.GraphPath; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.g2d.BitmapFont; @@ -9,12 +8,16 @@ import com.badlogic.gdx.graphics.g2d.GlyphLayout; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.math.GridPoint2; +import com.badlogic.gdx.math.MathUtils; +import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.utils.Align; +import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Bits; import com.badlogic.gdx.utils.IntMap; import java.util.Arrays; +import java.util.Iterator; import gdx.diablo.Diablo; import gdx.diablo.entity.Entity; @@ -35,7 +38,7 @@ public class MapRenderer { private static final boolean DEBUG_SPECIAL = DEBUG && true; private static final boolean DEBUG_MOUSE = DEBUG && true; private static final boolean DEBUG_PATHS = DEBUG && true; - private static final boolean DEBUG_POPPADS = DEBUG && true; + private static final boolean DEBUG_POPPADS = DEBUG && !true; public static boolean RENDER_DEBUG_SUBTILE = DEBUG_SUBTILE; public static boolean RENDER_DEBUG_TILE = DEBUG_TILE; @@ -51,10 +54,24 @@ public class MapRenderer { private static final Color RENDER_DEBUG_GRID_COLOR_3 = new Color(0x0000ff3f); public static int DEBUG_GRID_MODES = 3; + // Extra padding to ensure proper overscan, should be odd value + private static final int TILES_PADDING_X = 3; + private static final int TILES_PADDING_Y = 3; + + private final Vector3 tmpVec3 = new Vector3(); + private final Vector2 tmpVec2a = new Vector2(); + private final Vector2 tmpVec2b = new Vector2(); + private final GridPoint2 tmpVec2i = new GridPoint2(); + PaletteIndexedBatch batch; - OrthographicCamera camera; - Map map; - int[] viewBuffer; + OrthographicCamera camera; + Map map; + int viewBuffer[]; + //int viewBuffer2[]; // lower walls + //int viewBuffer3[]; // upper walls + + Entity src; + Vector3 currentPos = new Vector3(); // sub-tile index in world-space int x, y; @@ -84,74 +101,31 @@ public class MapRenderer { // tpx and tpy of startX, startY tile in world-space int startPx, startPy; + // camera bounds + int renderMinX, renderMinY; + int renderMaxX, renderMaxY; + // DT1 mainIndexes to not draw final Bits popped = new Bits(); IntMap entities; - public MapRenderer(PaletteIndexedBatch batch, OrthographicCamera camera) { - this.batch = batch; - this.camera = camera; + public MapRenderer(PaletteIndexedBatch batch) { + this.batch = batch; + this.camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); + camera.setToOrtho(false, 480f * 16 / 9, 480); + setClipPlane(-1000, 1000); + } - // This adjusts clip plane - camera.near = -1000; - camera.far = 1000; + // This adjusts clip plane for debugging purposes (some elements rotated to map grid) + private void setClipPlane(float near, float far) { + camera.near = near; + camera.far = far; camera.update(); } - public void setEntities(IntMap entities) { - this.entities = entities; - } - - public Entity hit() { - Vector3 coords = new Vector3(); - coords.set(Gdx.input.getX(), Gdx.input.getY(), 0); - camera.unproject(coords); - float adjustX = (int) coords.x; - float adjustY = (int) coords.y - Tile.SUBTILE_HEIGHT50; - - float selectX = ( adjustX / Tile.SUBTILE_WIDTH50 - adjustY / Tile.SUBTILE_HEIGHT50) / 2; - float selectY = (-adjustX / Tile.SUBTILE_WIDTH50 - adjustY / Tile.SUBTILE_HEIGHT50) / 2; - if (selectX < 0) selectX--; - if (selectY < 0) selectY--; - - int mx = -Tile.SUBTILE_WIDTH50 + ((int)selectX * Tile.SUBTILE_WIDTH50) - ((int)selectY * Tile.SUBTILE_WIDTH50); - int my = -Tile.SUBTILE_HEIGHT50 - ((int)selectX * Tile.SUBTILE_HEIGHT50) - ((int)selectY * Tile.SUBTILE_HEIGHT50); - - Map.Zone zone = map.getZone((int) selectX, (int) selectY); - if (zone != null) { - for (Entity entity : zone.entities) { - entity.over = entity.contains(coords); - /*Vector3 position = entity.position(); - float x = +(position.x * Tile.SUBTILE_WIDTH50) - (position.y * Tile.SUBTILE_WIDTH50); - float y = -(position.x * Tile.SUBTILE_HEIGHT50) - (position.y * Tile.SUBTILE_HEIGHT50); - if (x < coords.x && coords.x < x + 50 - && y < coords.y && coords.y < y + 50) { - entity.over = true; - return entity; - } else { - entity.over = false; - }*/ - } - } - - return null; - } - - public Vector3 getCursor() { - Vector3 coords = new Vector3(); - coords.set(Gdx.input.getX(), Gdx.input.getY(), 0); - camera.unproject(coords); - float adjustX = (int) coords.x; - float adjustY = (int) coords.y - Tile.SUBTILE_HEIGHT50; - - float selectX = ( adjustX / Tile.SUBTILE_WIDTH50 - adjustY / Tile.SUBTILE_HEIGHT50) / 2; - float selectY = (-adjustX / Tile.SUBTILE_WIDTH50 - adjustY / Tile.SUBTILE_HEIGHT50) / 2; - if (selectX < 0) selectX--; - if (selectY < 0) selectY--; - coords.x = (int) selectX; - coords.y = (int) selectY; - return coords; + public Map getMap() { + return map; } public void setMap(Map map) { @@ -160,24 +134,95 @@ public class MapRenderer { } } - public Map getMap() { - return map; + public Entity getSrc() { + return src; } - public GridPoint2 toWorldSpace(int x, int y) {// So final actual commands are: - x -= (camera.viewportWidth / 2); - y -= (camera.viewportHeight / 2); - x += stx; - y += sty; - return new GridPoint2( - (x / Tile.SUBTILE_WIDTH50 + y / Tile.SUBTILE_HEIGHT50) / 2, - (y / Tile.SUBTILE_HEIGHT50 - x / Tile.SUBTILE_WIDTH50 ) / 2); + public void setSrc(Entity src) { + if (this.src != src) { + this.src = src; + } + } + + public void setEntities(IntMap entities) { + this.entities = entities; + } + + public float zoom() { + return camera.zoom; + } + + public void zoom(float zoom) { + zoom(zoom, false); + } + + public void zoom(float zoom, boolean resize) { + if (camera.zoom != zoom) { + camera.zoom = zoom; + update(true); + if (resize) resize(); + } + } + + public Vector2 project(Vector2 dst) { + return project(dst.x, dst.y, dst); + } + + public Vector2 project(float x, float y, Vector2 dst) { + dst.x = +(x * Tile.SUBTILE_WIDTH50) - (y * Tile.SUBTILE_WIDTH50); + dst.y = -(x * Tile.SUBTILE_HEIGHT50) - (y * Tile.SUBTILE_HEIGHT50); + return dst; + } + + public Vector2 unproject(Vector2 dst) { + return unproject(dst.x, dst.y, dst); + } + + public Vector2 unproject(float x, float y) { + return unproject(x, y, new Vector2()); + } + + public Vector2 unproject(float x, float y, Vector2 dst) { + tmpVec3.set(x, y, 0); + camera.unproject(tmpVec3); + return dst.set(tmpVec3.x, tmpVec3.y); + } + + public GridPoint2 coords() { + return coords(new GridPoint2()); + } + + public GridPoint2 coords(GridPoint2 dst) { + tmpVec2a.set(Gdx.input.getX(), Gdx.input.getY()); + unproject(tmpVec2a); + return coords(tmpVec2a.x, tmpVec2a.y, dst); + } + + public GridPoint2 coords(float x, float y) { + return coords(x, y, new GridPoint2()); + } + + public GridPoint2 coords(float x, float y, GridPoint2 dst) { + float adjustX = (int) x; + float adjustY = (int) y - Tile.SUBTILE_HEIGHT50; + float selectX = ( adjustX / Tile.SUBTILE_WIDTH50 - adjustY / Tile.SUBTILE_HEIGHT50) / 2; + float selectY = (-adjustX / Tile.SUBTILE_WIDTH50 - adjustY / Tile.SUBTILE_HEIGHT50) / 2; + if (selectX < 0) selectX--; + if (selectY < 0) selectY--; + return dst.set((int) selectX, (int) selectY); + } + + public float angle(Vector3 src, Vector3 dst) { + project(tmpVec2a.set(src.x, src.y)); + project(tmpVec2b.set(dst.x, dst.y)); + tmpVec2b.sub(tmpVec2a); + return MathUtils.atan2(tmpVec2b.y, tmpVec2b.x); } public void resize() { updateBounds(); final int viewBufferLen = tilesX + tilesY - 1; - final int viewBufferMax = tilesX * 2 - 1; + final int viewBufferMax = tilesY * 2 - 1; // was tilesX, but seems better if bound to height viewBuffer = new int[viewBufferLen]; int x, y; for (x = 0, y = 1; y < viewBufferMax; x++, y += 2) @@ -188,80 +233,91 @@ public class MapRenderer { } private void updateBounds() { - width = (int) camera.viewportWidth; - height = (int) camera.viewportHeight; + width = (int) (camera.viewportWidth * camera.zoom); + height = (int) (camera.viewportHeight * camera.zoom); + + renderMinX = (int) camera.position.x - (width >>> 1); + renderMinY = (int) camera.position.y - (height >>> 1); + renderMaxX = renderMinX + width; + renderMaxY = renderMinY + height; int minTilesX = ((width + Tile.WIDTH - 1) / Tile.WIDTH); int minTilesY = ((height + Tile.HEIGHT - 1) / Tile.HEIGHT); if ((minTilesX & 1) == 1) minTilesX++; if ((minTilesY & 1) == 1) minTilesY++; - tilesX = minTilesX + 3; // pad width comfortably - tilesY = minTilesY + 7; // pad height for lower walls / upper walls + tilesX = minTilesX + TILES_PADDING_X; + tilesY = minTilesY + TILES_PADDING_Y; renderWidth = tilesX * Tile.WIDTH; renderHeight = tilesY * Tile.HEIGHT; assert (tilesX & 1) == 1 && (tilesY & 1) == 1; } - public void setPosition(GridPoint2 origin) { - setPosition(origin.x, origin.y); + public void update() { + update(false); } - public void setPosition(int x, int y) { - setPosition(x, y, false); - } + public void update(boolean force) { + hitAll(); + if (src == null) return; + Vector3 pos = src.position(); + if (pos.epsilonEquals(currentPos) && !force) return; + currentPos.set(pos); + this.x = (int) pos.x; + this.y = (int) pos.y; + spx = (x * Tile.SUBTILE_WIDTH50) - (y * Tile.SUBTILE_WIDTH50); + spy = -(x * Tile.SUBTILE_HEIGHT50) - (y * Tile.SUBTILE_HEIGHT50); + float spxf = (pos.x * Tile.SUBTILE_WIDTH50) - (pos.y * Tile.SUBTILE_WIDTH50); + float spyf = -(pos.x * Tile.SUBTILE_HEIGHT50) - (pos.y * Tile.SUBTILE_HEIGHT50); + camera.position.set(spxf, spyf, 0); + camera.update(); - public void setPosition(int x, int y, boolean force) { - if (this.x != x || this.y != y || force) { - this.x = x; - this.y = y; - spx = (x * Tile.SUBTILE_WIDTH50) - (y * Tile.SUBTILE_WIDTH50); - spy = -(x * Tile.SUBTILE_HEIGHT50) - (y * Tile.SUBTILE_HEIGHT50); - camera.position.set(spx, spy, 0); - camera.update(); - // subtile index in tile-space - stx = x < 0 - ? (x + 1) % Tile.SUBTILE_SIZE + (Tile.SUBTILE_SIZE - 1) - : x % Tile.SUBTILE_SIZE; - sty = y < 0 - ? (y + 1) % Tile.SUBTILE_SIZE + (Tile.SUBTILE_SIZE - 1) - : y % Tile.SUBTILE_SIZE; - t = Tile.SUBTILE_INDEX[stx][sty]; + // subtile index in tile-space + stx = x < 0 + ? (x + 1) % Tile.SUBTILE_SIZE + (Tile.SUBTILE_SIZE - 1) + : x % Tile.SUBTILE_SIZE; + sty = y < 0 + ? (y + 1) % Tile.SUBTILE_SIZE + (Tile.SUBTILE_SIZE - 1) + : y % Tile.SUBTILE_SIZE; + t = Tile.SUBTILE_INDEX[stx][sty]; - // pixel offset of subtile in world-space - spx = -Tile.SUBTILE_WIDTH50 + (x * Tile.SUBTILE_WIDTH50) - (y * Tile.SUBTILE_WIDTH50); - spy = -Tile.SUBTILE_HEIGHT50 - (x * Tile.SUBTILE_HEIGHT50) - (y * Tile.SUBTILE_HEIGHT50); + // pixel offset of subtile in world-space + spx = -Tile.SUBTILE_WIDTH50 + (x * Tile.SUBTILE_WIDTH50) - (y * Tile.SUBTILE_WIDTH50); + spy = -Tile.SUBTILE_HEIGHT50 - (x * Tile.SUBTILE_HEIGHT50) - (y * Tile.SUBTILE_HEIGHT50); - // tile index in world-space - tx = x < 0 - ? ((x + 1) / Tile.SUBTILE_SIZE) - 1 - : (x / Tile.SUBTILE_SIZE); - ty = y < 0 - ? ((y + 1) / Tile.SUBTILE_SIZE) - 1 - : (y / Tile.SUBTILE_SIZE); + // tile index in world-space + tx = x < 0 + ? ((x + 1) / Tile.SUBTILE_SIZE) - 1 + : (x / Tile.SUBTILE_SIZE); + ty = y < 0 + ? ((y + 1) / Tile.SUBTILE_SIZE) - 1 + : (y / Tile.SUBTILE_SIZE); - tpx = spx - Tile.SUBTILE_OFFSET[t][0]; - tpy = spy - Tile.SUBTILE_OFFSET[t][1]; + tpx = spx - Tile.SUBTILE_OFFSET[t][0]; + tpy = spy - Tile.SUBTILE_OFFSET[t][1]; - updateBounds(); + //updateBounds(); + renderMinX = (int) camera.position.x - (width >>> 1); + renderMinY = (int) camera.position.y - (height >>> 1); + renderMaxX = renderMinX + width; + renderMaxY = renderMinY + height; - final int offX = tilesX >>> 1; - final int offY = tilesY >>> 1; - startX = tx + offX - offY; - startY = ty - offX - offY; - startPx = tpx + renderWidth / 2 - Tile.WIDTH50; - startPy = tpy + renderHeight / 2 - Tile.HEIGHT50; + final int offX = tilesX >>> 1; + final int offY = tilesY >>> 1; + startX = tx + offX - offY; + startY = ty - offX - offY; + startPx = tpx + renderWidth / 2 - Tile.WIDTH50; + startPy = tpy + renderHeight / 2 - Tile.HEIGHT50; - if (DEBUG_MATH) { - Gdx.app.debug(TAG, - String.format("(%2d,%2d){%d,%d}[%2d,%2d](%dx%d)[%dx%d] %d,%d", - x, y, stx, sty, tx, ty, width, height, tilesX, tilesY, spx, spy)); - } + if (DEBUG_MATH) { + Gdx.app.debug(TAG, + String.format("(%2d,%2d){%d,%d}[%2d,%2d](%dx%d)[%dx%d] %d,%d", + x, y, stx, sty, tx, ty, width, height, tilesX, tilesY, spx, spy)); + } - map.updatePopPads(popped, x, y, tx, ty, stx, sty); - if (DEBUG_POPPADS) { - String popPads = getPopPads(); - if (!popPads.isEmpty()) Gdx.app.debug(TAG, "PopPad IDs: " + popPads); - } + map.updatePopPads(popped, x, y, tx, ty, stx, sty); + if (DEBUG_POPPADS) { + String popPads = getPopPads(); + if (!popPads.isEmpty()) Gdx.app.debug(TAG, "PopPad IDs: " + popPads); } } @@ -278,8 +334,18 @@ public class MapRenderer { return builder.toString(); } - // TODO: render will overscan image in y-axis to accommodate walls, should change to wall only instead of entire frame - public void render() { + private void hitAll() { + coords(tmpVec2i); // also sets tmpVec3 to unprojected coords -- TODO: tmpVec3 should be passed in explicitly + Map.Zone zone = map.getZone(tmpVec2i.x, tmpVec2i.y); + if (zone != null) { + for (Entity entity : zone.entities) { + entity.over = entity.contains(tmpVec3); + } + } + } + + public void draw(float delta) { + batch.setProjectionMatrix(camera.combined); for (int i = 0, x, y; i < Map.MAX_LAYERS; i++) { int startX2 = startX; int startY2 = startY; @@ -301,6 +367,9 @@ public class MapRenderer { Map.Tile tile = zone.get(i, tx, ty); switch (i) { case Map.FLOOR_OFFSET: case Map.FLOOR_OFFSET + 1: + // TODO: Expand upon this idea... Good idea to limit upper/lower walls too! + if (px > renderMaxX || py > renderMaxY) break; + if (px + Tile.WIDTH < renderMinX || py + Tile.HEIGHT < renderMinY) break; //drawWalls(batch, tile, px, py, false); drawFloor(batch, tile, px, py); break; @@ -312,6 +381,7 @@ public class MapRenderer { case Map.WALL_OFFSET: drawEntities(batch, stx, sty); drawObjects(batch, zone, stx, sty); + // fall-through case Map.WALL_OFFSET + 1: case Map.WALL_OFFSET + 2: case Map.WALL_OFFSET + 3: drawWall(batch, tile, px, py); break; @@ -397,16 +467,18 @@ public class MapRenderer { } } - public void renderDebug(ShapeRenderer shapes) { + public void drawDebug(ShapeRenderer shapes) { + batch.setProjectionMatrix(camera.combined); + shapes.setProjectionMatrix(camera.combined); if (RENDER_DEBUG_GRID > 0) - renderDebugGrid(shapes); + drawDebugGrid(shapes); if (RENDER_DEBUG_WALKABLE > 0) - renderDebugWalkable(shapes); + drawDebugWalkable(shapes); - if (RENDER_DEBUG_SPECIAL) { - renderDebugSpecial(shapes); - } + + if (RENDER_DEBUG_SPECIAL) + drawDebugSpecial(shapes); if (RENDER_DEBUG_TILE) { shapes.setColor(Color.OLIVE); @@ -418,200 +490,48 @@ public class MapRenderer { drawDiamond(shapes, spx, spy, Tile.SUBTILE_WIDTH, Tile.SUBTILE_HEIGHT); } - if (RENDER_DEBUG_PATHS) { - renderDebugPaths(shapes); - } + if (RENDER_DEBUG_PATHS) + drawDebugPaths(shapes); if (RENDER_DEBUG_CAMERA) { + float viewportWidth = width; + float viewportHeight = height; shapes.setColor(Color.GREEN); shapes.rect( - camera.position.x - camera.viewportWidth / 2, - camera.position.y - camera.viewportHeight / 2, - camera.viewportWidth + 1, camera.viewportHeight + 1); + camera.position.x - MathUtils.ceil(viewportWidth / 2), + camera.position.y - MathUtils.ceil(viewportHeight / 2), + viewportWidth + 2, viewportHeight + 2); } if (RENDER_DEBUG_OVERSCAN) { - shapes.setColor(Color.GRAY); + /*shapes.setColor(Color.LIGHT_GRAY); + shapes.rect( + tpx - renderWidth / 2 + Tile.WIDTH50, + tpy - renderHeight / 2 + Tile.HEIGHT50 + renderHeight, + renderWidth, 96); + shapes.setColor(Color.GRAY);*/ shapes.rect( tpx - renderWidth / 2 + Tile.WIDTH50, tpy - renderHeight / 2 + Tile.HEIGHT50, renderWidth, renderHeight); + /*shapes.setColor(Color.DARK_GRAY); + shapes.rect( + tpx - renderWidth / 2 + Tile.WIDTH50, + tpy - renderHeight / 2 + Tile.HEIGHT50 - 96, + renderWidth, 96);*/ } if (DEBUG_MOUSE) { - //int screenX = (int) camera.position.x - (int) camera.viewportWidth / 2 + Gdx.input.getX(); - //int screenY = (int) camera.position.y + (int) camera.viewportHeight / 2 - Gdx.input.getY(); - //int screenX = (int) (((int) camera.position.x - (int) camera.viewportWidth / 2 + Gdx.input.getX()) * camera.zoom); - //int screenY = (int) (((int) camera.position.y + (int) camera.viewportHeight / 2 - Gdx.input.getY()) * camera.zoom); - //int adjustX = screenX; - //int adjustY = screenY - Tile.SUBTILE_HEIGHT50; - Vector3 coords = new Vector3(); - coords.set(Gdx.input.getX(), Gdx.input.getY(), 0); - camera.unproject(coords); - float adjustX = (int) coords.x; - float adjustY = (int) coords.y - Tile.SUBTILE_HEIGHT50; + coords(tmpVec2i); + int mx = -Tile.SUBTILE_WIDTH50 + (tmpVec2i.x * Tile.SUBTILE_WIDTH50) - (tmpVec2i.y * Tile.SUBTILE_WIDTH50); + int my = -Tile.SUBTILE_HEIGHT50 - (tmpVec2i.x * Tile.SUBTILE_HEIGHT50) - (tmpVec2i.y * Tile.SUBTILE_HEIGHT50); - //shapes.setColor(Color.VIOLET); - //shapes.line(screenX - 10, screenY, screenX + 10, screenY); - //shapes.line(screenX, screenY - 10, screenX, screenY + 10); - - float selectX = ( adjustX / Tile.SUBTILE_WIDTH50 - adjustY / Tile.SUBTILE_HEIGHT50) / 2; - float selectY = (-adjustX / Tile.SUBTILE_WIDTH50 - adjustY / Tile.SUBTILE_HEIGHT50) / 2; - if (selectX < 0) selectX--; - if (selectY < 0) selectY--; - - int mx = -Tile.SUBTILE_WIDTH50 + ((int)selectX * Tile.SUBTILE_WIDTH50) - ((int)selectY * Tile.SUBTILE_WIDTH50); - int my = -Tile.SUBTILE_HEIGHT50 - ((int)selectX * Tile.SUBTILE_HEIGHT50) - ((int)selectY * Tile.SUBTILE_HEIGHT50); - - //shapes.end(); - //batch.begin(); - //batch.setShader(null); - //batch.setProjectionMatrix(camera.combined); - //Diablo.fonts.consolas16.draw(batch, - // String.format("%3.0f,%3.0f%n%3.0f,%3.0f%n%3.0f,%3.0f%n%3d,%3d", - // coords.x, coords.y, adjustX, adjustY, selectX, selectY, mx + Tile.SUBTILE_WIDTH50, my + Tile.SUBTILE_HEIGHT50), - // coords.x + 10, coords.y - 10); - //batch.end(); - //batch.getProjectionMatrix().idt(); - //shapes.begin(); shapes.setColor(Color.VIOLET); drawDiamond(shapes, mx, my, Tile.SUBTILE_WIDTH, Tile.SUBTILE_HEIGHT); } } - private void renderDebugWalkable(ShapeRenderer shapes) { - final int[] WALKABLE_ID = { - 20, 21, 22, 23, 24, - 15, 16, 17, 18, 19, - 10, 11, 12, 13, 14, - 5, 6, 7, 8, 9, - 0, 1, 2, 3, 4 - }; - - ShapeRenderer.ShapeType shapeType = shapes.getCurrentType(); - shapes.set(ShapeRenderer.ShapeType.Filled); - - int startX2 = startX; - int startY2 = startY; - int startPx2 = startPx; - int startPy2 = startPy; - int x, y; - for (y = 0; y < viewBuffer.length; y++) { - int tx = startX2; - int ty = startY2; - int px = startPx2; - int py = startPy2; - int size = viewBuffer[y]; - for (x = 0; x < size; x++) { - Map.Zone zone = map.getZone(tx * Tile.SUBTILE_SIZE, ty * Tile.SUBTILE_SIZE); - if (zone != null) { - if (RENDER_DEBUG_WALKABLE == 1) { - for (int sty = 0, t = 0; sty < Tile.SUBTILE_SIZE; sty++) { - for (int stx = 0; stx < Tile.SUBTILE_SIZE; stx++, t++) { - int flags = zone.flags[zone.getLocalTX(tx) * Tile.SUBTILE_SIZE + stx][zone.getLocalTY(ty) * Tile.SUBTILE_SIZE + sty] & 0xFF; - if (flags == 0) continue; - renderDebugWalkableTiles(shapes, px, py, t, flags); - } - } - } else { - //Map.Tile[][] tiles = zone.tiles[RENDER_DEBUG_WALKABLE - 1]; - //if (tiles != null) { - Map.Tile tile = zone.get(RENDER_DEBUG_WALKABLE - 2, tx, ty); - for (int t = 0; tile != null && tile.tile != null && t < Tile.NUM_SUBTILES; t++) { - int flags = tile.tile.flags[WALKABLE_ID[t]] & 0xFF; - if (flags == 0) continue; - renderDebugWalkableTiles(shapes, px, py, t, flags); - } - //} - } - } - - tx++; - px += Tile.WIDTH50; - py -= Tile.HEIGHT50; - } - - startY2++; - if (y >= tilesX - 1) { - startX2++; - startPy2 -= Tile.HEIGHT; - } else { - startX2--; - startPx2 -= Tile.WIDTH; - } - } - - shapes.set(shapeType); - } - - private void renderDebugWalkableTiles(ShapeRenderer shapes, int px, int py, int t, int flags) { - int offX = px + Tile.SUBTILE_OFFSET[t][0]; - int offY = py + Tile.SUBTILE_OFFSET[t][1]; - - shapes.setColor(Color.CORAL); - drawDiamond(shapes, offX, offY, Tile.SUBTILE_WIDTH, Tile.SUBTILE_HEIGHT); - - offY += Tile.SUBTILE_HEIGHT50; - - if ((flags & Tile.FLAG_BLOCK_WALK) != 0) { - shapes.setColor(Color.FIREBRICK); - shapes.triangle( - offX + 16, offY, - offX + 16, offY + 8, - offX + 24, offY + 4); - } - if ((flags & Tile.FLAG_BLOCK_LIGHT_LOS) != 0) { - shapes.setColor(Color.FOREST); - shapes.triangle( - offX + 16, offY, - offX + 32, offY, - offX + 24, offY + 4); - } - if ((flags & Tile.FLAG_BLOCK_JUMP) != 0) { - shapes.setColor(Color.ROYAL); - shapes.triangle( - offX + 16, offY, - offX + 32, offY, - offX + 24, offY - 4); - } - if ((flags & Tile.FLAG_BLOCK_PLAYER_WALK) != 0) { - shapes.setColor(Color.VIOLET); - shapes.triangle( - offX + 16, offY, - offX + 16, offY - 8, - offX + 24, offY - 4); - } - if ((flags & Tile.FLAG_BLOCK_UNKNOWN1) != 0) { - shapes.setColor(Color.GOLD); - shapes.triangle( - offX + 16, offY, - offX + 16, offY - 8, - offX + 8, offY - 4); - } - if ((flags & Tile.FLAG_BLOCK_LIGHT) != 0) { - shapes.setColor(Color.SKY); - shapes.triangle( - offX, offY, - offX + 16, offY, - offX + 8, offY - 4); - } - if ((flags & Tile.FLAG_BLOCK_UNKNOWN2) != 0) { - shapes.setColor(Color.WHITE); - shapes.triangle( - offX, offY, - offX + 16, offY, - offX + 8, offY + 4); - } - if ((flags & Tile.FLAG_BLOCK_UNKNOWN3) != 0) { - shapes.setColor(Color.SLATE); - shapes.triangle( - offX + 16, offY, - offX + 16, offY + 8, - offX + 8, offY + 4); - } - } - - private void renderDebugGrid(ShapeRenderer shapes) { + private void drawDebugGrid(ShapeRenderer shapes) { int x, y; switch (RENDER_DEBUG_GRID) { case 1: @@ -747,7 +667,139 @@ public class MapRenderer { } } - private void renderDebugSpecial(ShapeRenderer shapes) { + private void drawDebugWalkable(ShapeRenderer shapes) { + final int[] WALKABLE_ID = { + 20, 21, 22, 23, 24, + 15, 16, 17, 18, 19, + 10, 11, 12, 13, 14, + 5, 6, 7, 8, 9, + 0, 1, 2, 3, 4 + }; + + ShapeRenderer.ShapeType shapeType = shapes.getCurrentType(); + shapes.set(ShapeRenderer.ShapeType.Filled); + + int startX2 = startX; + int startY2 = startY; + int startPx2 = startPx; + int startPy2 = startPy; + int x, y; + for (y = 0; y < viewBuffer.length; y++) { + int tx = startX2; + int ty = startY2; + int px = startPx2; + int py = startPy2; + int size = viewBuffer[y]; + for (x = 0; x < size; x++) { + Map.Zone zone = map.getZone(tx * Tile.SUBTILE_SIZE, ty * Tile.SUBTILE_SIZE); + if (zone != null) { + if (RENDER_DEBUG_WALKABLE == 1) { + for (int sty = 0, t = 0; sty < Tile.SUBTILE_SIZE; sty++) { + for (int stx = 0; stx < Tile.SUBTILE_SIZE; stx++, t++) { + int flags = zone.flags[zone.getLocalTX(tx) * Tile.SUBTILE_SIZE + stx][zone.getLocalTY(ty) * Tile.SUBTILE_SIZE + sty] & 0xFF; + if (flags == 0) continue; + drawDebugWalkableTiles(shapes, px, py, t, flags); + } + } + } else { + //Map.Tile[][] tiles = zone.tiles[RENDER_DEBUG_WALKABLE - 1]; + //if (tiles != null) { + Map.Tile tile = zone.get(RENDER_DEBUG_WALKABLE - 2, tx, ty); + for (int t = 0; tile != null && tile.tile != null && t < Tile.NUM_SUBTILES; t++) { + int flags = tile.tile.flags[WALKABLE_ID[t]] & 0xFF; + if (flags == 0) continue; + drawDebugWalkableTiles(shapes, px, py, t, flags); + } + //} + } + } + + tx++; + px += Tile.WIDTH50; + py -= Tile.HEIGHT50; + } + + startY2++; + if (y >= tilesX - 1) { + startX2++; + startPy2 -= Tile.HEIGHT; + } else { + startX2--; + startPx2 -= Tile.WIDTH; + } + } + + shapes.set(shapeType); + } + + private void drawDebugWalkableTiles(ShapeRenderer shapes, int px, int py, int t, int flags) { + int offX = px + Tile.SUBTILE_OFFSET[t][0]; + int offY = py + Tile.SUBTILE_OFFSET[t][1]; + + shapes.setColor(Color.CORAL); + drawDiamond(shapes, offX, offY, Tile.SUBTILE_WIDTH, Tile.SUBTILE_HEIGHT); + + offY += Tile.SUBTILE_HEIGHT50; + + if ((flags & Tile.FLAG_BLOCK_WALK) != 0) { + shapes.setColor(Color.FIREBRICK); + shapes.triangle( + offX + 16, offY, + offX + 16, offY + 8, + offX + 24, offY + 4); + } + if ((flags & Tile.FLAG_BLOCK_LIGHT_LOS) != 0) { + shapes.setColor(Color.FOREST); + shapes.triangle( + offX + 16, offY, + offX + 32, offY, + offX + 24, offY + 4); + } + if ((flags & Tile.FLAG_BLOCK_JUMP) != 0) { + shapes.setColor(Color.ROYAL); + shapes.triangle( + offX + 16, offY, + offX + 32, offY, + offX + 24, offY - 4); + } + if ((flags & Tile.FLAG_BLOCK_PLAYER_WALK) != 0) { + shapes.setColor(Color.VIOLET); + shapes.triangle( + offX + 16, offY, + offX + 16, offY - 8, + offX + 24, offY - 4); + } + if ((flags & Tile.FLAG_BLOCK_UNKNOWN1) != 0) { + shapes.setColor(Color.GOLD); + shapes.triangle( + offX + 16, offY, + offX + 16, offY - 8, + offX + 8, offY - 4); + } + if ((flags & Tile.FLAG_BLOCK_LIGHT) != 0) { + shapes.setColor(Color.SKY); + shapes.triangle( + offX, offY, + offX + 16, offY, + offX + 8, offY - 4); + } + if ((flags & Tile.FLAG_BLOCK_UNKNOWN2) != 0) { + shapes.setColor(Color.WHITE); + shapes.triangle( + offX, offY, + offX + 16, offY, + offX + 8, offY + 4); + } + if ((flags & Tile.FLAG_BLOCK_UNKNOWN3) != 0) { + shapes.setColor(Color.SLATE); + shapes.triangle( + offX + 16, offY, + offX + 16, offY + 8, + offX + 8, offY + 4); + } + } + + private void drawDebugSpecial(ShapeRenderer shapes) { for (int i = Map.WALL_OFFSET, x, y; i < Map.WALL_OFFSET + Map.MAX_WALLS; i++) { int startX2 = startX; int startY2 = startY; @@ -831,7 +883,7 @@ public class MapRenderer { } } - private void renderDebugPaths(ShapeRenderer shapes) { + private void drawDebugPaths(ShapeRenderer shapes) { shapes.set(ShapeRenderer.ShapeType.Filled); int startX2 = startX; int startY2 = startY; @@ -869,89 +921,6 @@ public class MapRenderer { shapes.set(ShapeRenderer.ShapeType.Line); } - public void renderDebugPath(ShapeRenderer shapes, Vector3 src, Vector3 dst) { - shapes.setColor(Color.TAN); - shapes.set(ShapeRenderer.ShapeType.Filled); - if (Math.abs(dst.y - src.y) < Math.abs(dst.x - src.x)) { - if (src.x > dst.x) { - plotLineLow(shapes, dst, src); - } else { - plotLineLow(shapes, src, dst); - } - } else { - if (src.y > dst.y) { - plotLineHigh(shapes, dst, src); - } else { - plotLineHigh(shapes, src, dst); - } - } - - shapes.set(ShapeRenderer.ShapeType.Line); - } - - private void plotLineLow(ShapeRenderer shapes, Vector3 src, Vector3 dst) { - float dx = dst.x - src.x; - float dy = dst.y - src.y; - int yi = 1; - if (dy < 0) { - yi = -1; - dy = -dy; - } - - float D = 2*dy - dx; - float y = src.y; - for (float x = src.x; x <= dst.x; x++) { - float px = +((int) x * Tile.SUBTILE_WIDTH50) - ((int) y * Tile.SUBTILE_WIDTH50) - Tile.SUBTILE_WIDTH50; - float py = -((int) x * Tile.SUBTILE_HEIGHT50) - ((int) y * Tile.SUBTILE_HEIGHT50) - Tile.SUBTILE_HEIGHT50; - drawDiamondSolid(shapes, px, py, Tile.SUBTILE_WIDTH, Tile.SUBTILE_HEIGHT); - if (D > 0) { - y = y + yi; - D = D - 2*dx; - } - - D = D + 2*dy; - } - } - - private void plotLineHigh(ShapeRenderer shapes, Vector3 src, Vector3 dst) { - float dx = dst.x - src.x; - float dy = dst.y - src.y; - int xi = 1; - if (dx < 0) { - xi = -1; - dx = -dx; - } - - float D = 2*dx - dy; - float x = src.x; - for (float y = src.y; y <= dst.y; y++) { - float px = +((int) x * Tile.SUBTILE_WIDTH50) - ((int) y * Tile.SUBTILE_WIDTH50) - Tile.SUBTILE_WIDTH50; - float py = -((int) x * Tile.SUBTILE_HEIGHT50) - ((int) y * Tile.SUBTILE_HEIGHT50) - Tile.SUBTILE_HEIGHT50; - drawDiamondSolid(shapes, px, py, Tile.SUBTILE_WIDTH, Tile.SUBTILE_HEIGHT); - if (D > 0) { - x = x + xi; - D = D - 2*dy; - } - - D = D + 2*dx; - } - } - - public void renderDebugPath2(ShapeRenderer shapes, GraphPath path) { - if (path == null) return; - shapes.setColor(Color.TAN); - shapes.set(ShapeRenderer.ShapeType.Filled); - final int size = path.getCount(); - for (int i = 0; i < size; i++) { - Point2 point = path.get(i); - float px = +(point.x * Tile.SUBTILE_WIDTH50) - (point.y * Tile.SUBTILE_WIDTH50) - Tile.SUBTILE_WIDTH50; - float py = -(point.x * Tile.SUBTILE_HEIGHT50) - (point.y * Tile.SUBTILE_HEIGHT50) - Tile.SUBTILE_HEIGHT50; - drawDiamondSolid(shapes, px, py, Tile.SUBTILE_WIDTH, Tile.SUBTILE_HEIGHT); - } - - shapes.set(ShapeRenderer.ShapeType.Line); - } - private static void drawDiamond(ShapeRenderer shapes, float x, float y, int width, int height) { int hw = width >>> 1; int hh = height >>> 1; @@ -961,6 +930,27 @@ public class MapRenderer { shapes.line(x + hw , y , x , y + hh ); } + public void drawDebugPath(ShapeRenderer shapes, MapGraph.MapGraphPath path) { + drawDebugPath(shapes, path, Color.RED); + } + + public void drawDebugPath(ShapeRenderer shapes, MapGraph.MapGraphPath path, Color color) { + if (path == null || path.getCount() < 2) return; + shapes.setProjectionMatrix(camera.combined); + shapes.setColor(color); + shapes.set(ShapeRenderer.ShapeType.Line); + Iterator it = new Array.ArrayIterator<>(path.nodes); + MapGraph.Point2 src = it.next(); + for (MapGraph.Point2 dst; it.hasNext(); src = dst) { + dst = it.next(); + float px1 = +(src.x * Tile.SUBTILE_WIDTH50) - (src.y * Tile.SUBTILE_WIDTH50); + float py1 = -(src.x * Tile.SUBTILE_HEIGHT50) - (src.y * Tile.SUBTILE_HEIGHT50); + float px2 = +(dst.x * Tile.SUBTILE_WIDTH50) - (dst.y * Tile.SUBTILE_WIDTH50); + float py2 = -(dst.x * Tile.SUBTILE_HEIGHT50) - (dst.y * Tile.SUBTILE_HEIGHT50); + shapes.line(px1, py1, px2, py2); + } + } + private static void drawDiamondSolid(ShapeRenderer shapes, float x, float y, int width, int height) { int hw = width >>> 1; int hh = height >>> 1; diff --git a/core/src/gdx/diablo/map/MapUtils.java b/core/src/gdx/diablo/map/MapUtils.java deleted file mode 100644 index e9379ff4..00000000 --- a/core/src/gdx/diablo/map/MapUtils.java +++ /dev/null @@ -1,193 +0,0 @@ -package gdx.diablo.map; - -import com.badlogic.gdx.ai.pfa.GraphPath; -import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.math.Vector3; -import com.badlogic.gdx.utils.Array; - -public class MapUtils { - - private MapUtils() {} - - public static GraphPath path(Map map, Vector3 src, Vector3 dst, GraphPath path) { - Point2 srcP = new Point2(src); - Point2 dstP = new Point2(dst); - Array coords = new Array<>(); - Array children = new Array<>(8); - coords.add(dstP); - boolean found = false; - do { - for (Point2 point : coords) { - found = expand(map, srcP, point, coords, children); - coords.addAll(children); - if (found) break; - } - } while (!found); - - if (!found) { - return path; - } - - Point2 next = null; - Point2 last = coords.removeIndex(coords.size - 1); - path.add(last); - while (true) { - for (Point2 coord : coords) { - if (last.adjacent(coord)) { - if (next == null || coord.cost < next.cost) { - next = coord; - } - } - } - - path.add(next); - last = next; - next = null; - if (dstP.x == last.x && dstP.y == last.y) break; - } - - return path; - } - - private static boolean expand(Map map, Point2 dst, Point2 src, Array coords, Array children) { - children.clear(); - if (check(map, dst, src, src.x - 1, src.y - 1, coords, children)) return true; - if (check(map, dst, src, src.x - 1, src.y , coords, children)) return true; - if (check(map, dst, src, src.x - 1, src.y + 1, coords, children)) return true; - if (check(map, dst, src, src.x , src.y - 1, coords, children)) return true; - if (check(map, dst, src, src.x , src.y , coords, children)) return true; - if (check(map, dst, src, src.x , src.y + 1, coords, children)) return true; - if (check(map, dst, src, src.x + 1, src.y - 1, coords, children)) return true; - if (check(map, dst, src, src.x + 1, src.y , coords, children)) return true; - if (check(map, dst, src, src.x + 1, src.y + 1, coords, children)) return true; - return false; - } - - private static boolean check(Map map, Point2 dst, Point2 src, int x, int y, Array coords, Array children) { - Map.Zone zone = map.getZone(x, y); - if (zone != null) { - if (zone.flags(x, y) == 0) { - float cost = src.cost; - cost += (x != src.x && y != src.y) ? 1.414213562373095f : 1; - if (!contains(coords, x, y, cost)) { - Point2 point = new Point2(x, y, cost); - children.add(point); - if (dst.x == x && dst.y == y) return true; - } - } - } - - return false; - } - - private static boolean contains(Array coords, int x, int y, float cost) { - for (Point2 point : new Array.ArrayIterator<>(coords)) { - if (point.x == x && point.y == y && point.cost <= cost) { - return true; - } - } - - return false; - } - - static class Point2 { - int x; - int y; - float cost; - - Point2(int x, int y, float cost) { - this.x = x; - this.y = y; - this.cost = cost; - } - - Point2(Vector3 src) { - x = (int) src.x; - y = (int) src.y; - } - - boolean adjacent(Point2 other) { - return Vector2.dst2(x, y, other.x, other.y) < 2; - } - } - - /* - public static Array path(Map map, Vector3 src, Vector3 dst) { - ObjectSet open = new ObjectSet<>(); - ObjectSet closed = new ObjectSet<>(); - open.add(src); - - int x, y; - while (!open.isEmpty()) { - Node cur = null; - if (cur.data.equals(dst)) { - return null; - } - - x = (int) cur.data.x; - y = (int) cur.data.y; - - Array neighbors = getNeighbors(map, cur, x, y); - for (Node neighbor : neighbors) { - float score = cur.cost + neighbor.cost; - } - } - - return null; - } - - private static Array getNeighbors(Map map, Node n, int x, int y) { - Array neighbors = new Array<>(8); - addIfValid(neighbors, map, n, x - 1, y - 1); - addIfValid(neighbors, map, n, x - 1, y ); - addIfValid(neighbors, map, n, x - 1, y + 1); - addIfValid(neighbors, map, n, x , y - 1); - addIfValid(neighbors, map, n, x , y ); - addIfValid(neighbors, map, n, x , y + 1); - addIfValid(neighbors, map, n, x + 1, y - 1); - addIfValid(neighbors, map, n, x + 1, y ); - addIfValid(neighbors, map, n, x + 1, y + 1); - return neighbors; - } - - private static boolean addIfValid(Array arr, Map map, Node n, int x, int y) { - Map.Zone zone = map.getZone(x, y); - if (zone != null) { - if (zone.flags(x, y) == 0) { - Node node = new Node(); - node.prev = n; - node.data = new Vector3(x, y, 0); - if (x != node.data.x && y != node.data.y) { - node.cost = 1.414213562373095f; - } else { - node.cost = 1; - } - - arr.add(node); - } - } - - return false; - } - - static class Node { - Node prev; - Vector3 data; - float cost; - float score; - - @Override - public boolean equals(Object obj) { - if (obj == null) return false; - if (obj == this) return true; - if (!(obj instanceof Node)) return false; - return data.equals(((Node) obj).data); - } - - @Override - public int hashCode() { - return data.hashCode(); - } - } - */ -} diff --git a/core/src/gdx/diablo/map/Point2.java b/core/src/gdx/diablo/map/Point2.java deleted file mode 100644 index d97efbc1..00000000 --- a/core/src/gdx/diablo/map/Point2.java +++ /dev/null @@ -1,48 +0,0 @@ -package gdx.diablo.map; - -import com.badlogic.gdx.math.Vector3; - -public class Point2 extends BinaryHeap.Node { - public final int x; - public final int y; - final int hash; - - Point2(int x, int y, float cost) { - super(cost); - this.x = x; - this.y = y; - this.hash = hash(); - } - - Point2(Vector3 src) { - this((int) src.x, (int) src.y, 0); - } - - private int hash() { - return 31 * x + y; - } - - @Override - public int hashCode() { - return hash; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) return false; - if (obj == this) return true; - if (!(obj instanceof Point2)) return false; - Point2 other = (Point2) obj; - return x == other.x && y == other.y; - } - - public boolean equals(int x, int y) { - return this.x == x && this.y == y; - } - - public static float dst(Point2 src, Point2 dst) { - final float dx = dst.x - src.x; - final float dy = dst.y - src.y; - return (float) Math.sqrt(dx * dx + dy * dy); - } -} diff --git a/core/src/gdx/diablo/screen/GameScreen.java b/core/src/gdx/diablo/screen/GameScreen.java index b4e7bbe5..7350d3a7 100644 --- a/core/src/gdx/diablo/screen/GameScreen.java +++ b/core/src/gdx/diablo/screen/GameScreen.java @@ -8,7 +8,6 @@ import com.badlogic.gdx.InputProcessor; import com.badlogic.gdx.ScreenAdapter; import com.badlogic.gdx.assets.AssetDescriptor; import com.badlogic.gdx.audio.Sound; -import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.glutils.ShapeRenderer; import com.badlogic.gdx.math.GridPoint2; @@ -19,7 +18,6 @@ import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.Stage; import com.badlogic.gdx.scenes.scene2d.Touchable; import com.badlogic.gdx.scenes.scene2d.ui.Touchpad; -import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable; import com.badlogic.gdx.scenes.scene2d.utils.UIUtils; import com.badlogic.gdx.utils.Align; @@ -36,6 +34,8 @@ import java.io.PrintWriter; import gdx.diablo.Diablo; import gdx.diablo.Keys; +import gdx.diablo.entity.Direction; +import gdx.diablo.entity.Entity; import gdx.diablo.entity.Player; import gdx.diablo.graphics.PaletteIndexedBatch; import gdx.diablo.graphics.PaletteIndexedColorDrawable; @@ -62,7 +62,7 @@ import gdx.diablo.widget.TextArea; public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable { private static final String TAG = "GameScreen"; - private static final boolean DEBUG_TOUCHPAD = true; + private static final boolean DEBUG_TOUCHPAD = !true; private static final boolean DEBUG_MOBILE = true; final AssetDescriptor windowopenDescriptor = new AssetDescriptor<>("data\\global\\sfx\\cursor\\windowopen.wav", Sound.class); @@ -87,7 +87,6 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable final AssetDescriptor mapDescriptor = new AssetDescriptor<>("Act 1", Map.class, MapLoader.MapParameters.of(0, 0, 0)); Map map; MapRenderer mapRenderer; - OrthographicCamera camera; InputProcessor inputProcessorTest; public TextArea input; @@ -216,25 +215,6 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable }}); touchpad.setSize(164, 164); touchpad.setPosition(0, 0); - touchpad.addListener(new ChangeListener() { - @Override - public void changed(ChangeEvent event, Actor actor) { - float x = touchpad.getKnobPercentX(); - float y = touchpad.getKnobPercentY(); - if (x == 0 && y == 0) { - player.setMode("TN"); - return; - //} else if (-0.5f < x && x < 0.5f - // && -0.5f < y && y < 0.5f) { - // character.setMode("tw"); - } else { - player.setMode("RN"); - } - - float rad = MathUtils.atan2(y, x); - player.setAngle(rad); - } - }); stage.addActor(touchpad); } @@ -295,20 +275,19 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable switch (amount) { case -1: if (UIUtils.ctrl()) { - camera.zoom = Math.max(0.50f, camera.zoom - ZOOM_AMOUNT); + mapRenderer.zoom(Math.max(0.25f, mapRenderer.zoom() - ZOOM_AMOUNT)); } break; case 1: if (UIUtils.ctrl()) { - camera.zoom = Math.min(5.0f, camera.zoom + ZOOM_AMOUNT); + mapRenderer.zoom(Math.min(2.50f, mapRenderer.zoom() + ZOOM_AMOUNT)); } break; default: } - camera.update(); return true; } @@ -364,9 +343,10 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable case Packets.MOVETO: MoveTo moveTo = packet.readValue(MoveTo.class); Player p = entities.get(moveTo.id); + //if (p == player) break; // Disable forced update positions for now if (p != null) { - p.position().set(moveTo.x, moveTo.y, 0); - p.setAngle(moveTo.angle); + p.setPath(map, new Vector3(moveTo.x, moveTo.y, 0)); + //p.setAngle(moveTo.angle); } break; case Packets.CONNECT_RESPONSE: @@ -382,64 +362,47 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable PaletteIndexedBatch b = Diablo.batch; b.setPalette(Diablo.palettes.act1); - mapRenderer.hit(); - if (Gdx.input.isButtonPressed(Input.Buttons.LEFT)) { - // FIXME: should block click events on UI panels, bugged right now - //Actor hit = stage.hit(Gdx.input.getX(), Gdx.input.getY(), true); - //if (hit != null) { - Vector3 coords = mapRenderer.getCursor(); - //player.target().set(coords); - //player.updatePath(map, coords); - //if (player.path().getCount() > 0) { - // player.setMode("RN"); - //} - //} else { - // System.out.println(hit); - //} + if (DEBUG_TOUCHPAD || Gdx.app.getType() == Application.ApplicationType.Android) { + float x = touchpad.getKnobPercentX(); + float y = touchpad.getKnobPercentY(); + if (x == 0 && y == 0) { + player.setPath(map, null); + } else { + float rad = MathUtils.atan2(y, x); + x = Direction.getOffX(rad); + y = Direction.getOffY(rad); + player.setPath(map, new Vector3(x, y, 0).add(player.position())); + } + } else { + if (Gdx.input.isButtonPressed(Input.Buttons.LEFT)) { + GridPoint2 coords = mapRenderer.coords(); + player.setPath(map, new Vector3(coords.x, coords.y, 0)); + } } - b.setProjectionMatrix(camera.combined); + for (Entity entity : entities.values()) { + entity.update(delta); + if (!entity.position().epsilonEquals(entity.target())) { + float angle = mapRenderer.angle(entity.position(), entity.target()); + entity.setAngle(angle); + } + } + + mapRenderer.update(); + b.begin(); - //map.draw(b, 0, 0, 30, 13, Diablo.VIRTUAL_WIDTH, Diablo.VIRTUAL_HEIGHT, 1.5f); - mapRenderer.render(); + mapRenderer.draw(delta); b.end(); - // pixel offset of subtile in world-space - //int spx = + (character.x * Tile.SUBTILE_WIDTH50) - (character.y * Tile.SUBTILE_WIDTH50); - //int spy = - (character.x * Tile.SUBTILE_HEIGHT50) - (character.y * Tile.SUBTILE_HEIGHT50); - //character.draw(b, spx, spy); - //int spx = + (player.getOrigin().x * Tile.SUBTILE_WIDTH50) - (player.getOrigin().y * Tile.SUBTILE_WIDTH50); - //int spy = - (player.getOrigin().x * Tile.SUBTILE_HEIGHT50) - (player.getOrigin().y * Tile.SUBTILE_HEIGHT50); - //player.draw(b, spx, spy); - //player.draw(b); - Diablo.shapes.setAutoShapeType(true); - Diablo.shapes.setProjectionMatrix(camera.combined); Diablo.shapes.begin(ShapeRenderer.ShapeType.Line); - mapRenderer.renderDebug(Diablo.shapes); - //player.drawDebug(Diablo.shapes, spx, spy); - mapRenderer.renderDebugPath2(Diablo.shapes, player.path()); + mapRenderer.drawDebug(Diablo.shapes); + mapRenderer.drawDebugPath(Diablo.shapes, player.path()); player.drawDebug(Diablo.shapes); Diablo.shapes.end(); - //b.setProjectionMatrix(camera.combined); - //b.begin(); - - //for (Player p : entities.values()) { - // p.draw(b); - //} - - //b.end(); b.setProjectionMatrix(Diablo.viewport.getCamera().combined); - //Diablo.shapes.setAutoShapeType(true); - //Diablo.shapes.setProjectionMatrix(camera.combined); - //Diablo.shapes.begin(ShapeRenderer.ShapeType.Line); - //mapRenderer.renderDebug(Diablo.shapes); - ////player.drawDebug(Diablo.shapes, spx, spy); - //player.drawDebug(Diablo.shapes); - //Diablo.shapes.end(); - stage.act(); stage.draw(); } @@ -450,23 +413,16 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable Diablo.assets.get(windowopenDescriptor).play(); map = Diablo.assets.get(mapDescriptor); - // FIXME: Below causes bug with debug text in MapRenderer, setting camera to screen dims fixes, but renders far too much on mobile - camera = new OrthographicCamera(Diablo.VIRTUAL_WIDTH, Diablo.VIRTUAL_HEIGHT); - //camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); - mapRenderer = new MapRenderer(Diablo.batch, camera); + mapRenderer = new MapRenderer(Diablo.batch); mapRenderer.setMap(map); + mapRenderer.setSrc(player); mapRenderer.setEntities(entities); + if (Gdx.app.getType() == Application.ApplicationType.Android) { + mapRenderer.zoom(0.80f); + } mapRenderer.resize(); GridPoint2 origin = map.find(Map.ID.TOWN_ENTRY_1); - mapRenderer.setPosition(origin); - if (Gdx.app.getType() == Application.ApplicationType.Android) { - camera.zoom = 0.80f; - camera.update(); - } - - //character.x = origin.x; - //character.y = origin.y; player.position().set(origin.x, origin.y, 0); Keys.Esc.addStateListener(mappedKeyStateListener); @@ -496,11 +452,8 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable @Override public void run() { - if (UIUtils.shift()) return; - boolean moved = player.move(); - position.set((int) player.position().x, (int) player.position().y); - mapRenderer.setPosition(position); - if (!moved) return; + Vector3 pos = player.target(); + position.set((int) pos.x, (int) pos.y); String moveTo = Packets.build(new MoveTo(position, player.getAngle())); out.println(moveTo); } diff --git a/mapbuilder/src/gdx/diablo/map/MapViewer.java b/mapbuilder/src/gdx/diablo/map/MapViewer.java index fe4373d7..689fcef1 100644 --- a/mapbuilder/src/gdx/diablo/map/MapViewer.java +++ b/mapbuilder/src/gdx/diablo/map/MapViewer.java @@ -6,14 +6,11 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input; import com.badlogic.gdx.InputAdapter; import com.badlogic.gdx.InputMultiplexer; -import com.badlogic.gdx.ai.pfa.DefaultGraphPath; -import com.badlogic.gdx.ai.pfa.GraphPath; import com.badlogic.gdx.assets.AssetManager; import com.badlogic.gdx.backends.lwjgl.LwjglApplication; import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.GL20; -import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.glutils.ShaderProgram; @@ -24,6 +21,7 @@ import com.badlogic.gdx.scenes.scene2d.utils.UIUtils; import com.badlogic.gdx.utils.GdxRuntimeException; import gdx.diablo.COFs; +import gdx.diablo.CharClass; import gdx.diablo.Colors; import gdx.diablo.Diablo; import gdx.diablo.Files; @@ -37,6 +35,8 @@ import gdx.diablo.codec.FontTBL; import gdx.diablo.codec.Palette; import gdx.diablo.codec.TXT; import gdx.diablo.codec.excel.Excel; +import gdx.diablo.entity.Entity; +import gdx.diablo.entity.Player; import gdx.diablo.graphics.PaletteIndexedBatch; import gdx.diablo.loader.BitmapFontLoader; import gdx.diablo.loader.COFLoader; @@ -54,7 +54,7 @@ public class MapViewer extends ApplicationAdapter { LwjglApplicationConfiguration config = new LwjglApplicationConfiguration(); config.title = "Map Builder"; config.resizable = true; - config.width = 1280; + config.width = 1280; // 1280 config.height = 720; config.foregroundFPS = config.backgroundFPS = 144; MapViewer client = new MapViewer(); @@ -68,8 +68,8 @@ public class MapViewer extends ApplicationAdapter { Map map; DS1Types DS1Types; + Entity ent; MapRenderer mapRenderer; - OrthographicCamera camera; BitmapFont font; @@ -77,7 +77,8 @@ public class MapViewer extends ApplicationAdapter { Vector3 src; Vector3 dst; - GraphPath path = new DefaultGraphPath<>(); + MapGraph.MapGraphPath path = new MapGraph.MapGraphPath(); + MapGraph.MapGraphPath smoothedPath = new MapGraph.MapGraphPath(); boolean drawCrosshair; boolean drawGrid; @@ -128,10 +129,7 @@ public class MapViewer extends ApplicationAdapter { shapes = new ShapeRenderer(); Gdx.gl.glClearColor(0.3f, 0.3f, 0.3f, 1.0f); - camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); - camera.update(); - - mapRenderer = new MapRenderer(batch, camera); + mapRenderer = new MapRenderer(batch); mapRenderer.resize(); InputMultiplexer multiplexer = new InputMultiplexer(); @@ -140,7 +138,8 @@ public class MapViewer extends ApplicationAdapter { public boolean touchDown(int screenX, int screenY, int pointer, int button) { switch (button) { case Input.Buttons.LEFT: - src = mapRenderer.getCursor(); + GridPoint2 srcCoords = mapRenderer.coords(); + src = new Vector3(srcCoords.x, srcCoords.y, 0); dst = null; break; case Input.Buttons.RIGHT: @@ -153,7 +152,8 @@ public class MapViewer extends ApplicationAdapter { @Override public boolean touchDragged(int screenX, int screenY, int button) { if (src != null) { - dst = mapRenderer.getCursor(); + GridPoint2 dstCoords = mapRenderer.coords(); + dst = new Vector3(dstCoords.x, dstCoords.y, 0); } return true; } @@ -162,18 +162,13 @@ public class MapViewer extends ApplicationAdapter { public boolean touchUp(int screenX, int screenY, int pointer, int button) { switch (button) { case Input.Buttons.LEFT: - dst = mapRenderer.getCursor(); - System.out.println("src = " + src); - System.out.println("dst = " + dst); - - //float srcX = +(src.x * Tile.SUBTILE_WIDTH50) - (src.y * Tile.SUBTILE_WIDTH50); - //float srcY = -(src.x * Tile.SUBTILE_HEIGHT50) - (src.y * Tile.SUBTILE_HEIGHT50); - //float dstX = +(dst.x * Tile.SUBTILE_WIDTH50) - (dst.y * Tile.SUBTILE_WIDTH50); - //float dstY = -(dst.x * Tile.SUBTILE_HEIGHT50) - (dst.y * Tile.SUBTILE_HEIGHT50); - //System.out.println(new Vector2(dstX, dstY).dst(srcX, srcY)); - System.out.println(src.dst(dst)); - - map.path(src, dst, path); + GridPoint2 dstCoords = mapRenderer.coords(); + dst = new Vector3(dstCoords.x, dstCoords.y, 0); + map.findPath(src, dst, path); + smoothedPath.nodes.clear(); + smoothedPath.nodes.addAll(path.nodes); + map.smoothPath(smoothedPath); + System.out.println(path + "->" + smoothedPath); break; } return true; @@ -186,15 +181,14 @@ public class MapViewer extends ApplicationAdapter { public boolean scrolled(int amount) { switch (amount) { case -1: - camera.zoom = Math.max(0.50f, camera.zoom - ZOOM_AMOUNT); + mapRenderer.zoom(Math.max(0.25f, mapRenderer.zoom() - ZOOM_AMOUNT)); break; case 1: - camera.zoom = Math.min(5.0f, camera.zoom + ZOOM_AMOUNT); + mapRenderer.zoom(Math.min(2.50f, mapRenderer.zoom() + ZOOM_AMOUNT)); break; default: } - camera.update(); return true; } @@ -213,6 +207,9 @@ public class MapViewer extends ApplicationAdapter { MapRenderer.RENDER_DEBUG_WALKABLE = 0; } return true; + case Input.Keys.ALT_LEFT: + mapRenderer.resize(); + return true; case Input.Keys.F1: drawCrosshair = !drawCrosshair; return true; @@ -236,35 +233,31 @@ public class MapViewer extends ApplicationAdapter { return true; case Input.Keys.W: //case Input.Keys.UP: - int amount = UIUtils.ctrl() ? 1 : DT1.Tile.SUBTILE_SIZE; + int amount = UIUtils.ctrl() ? 1 : Tile.SUBTILE_SIZE; if (UIUtils.shift()) amount *= 8; y -= amount; - mapRenderer.setPosition(x, y); - //mapRenderer.runMath(x, y); + ent.position().set(x, y, 0); return true; case Input.Keys.S: //case Input.Keys.DOWN: - amount = UIUtils.ctrl() ? 1 : DT1.Tile.SUBTILE_SIZE; + amount = UIUtils.ctrl() ? 1 : Tile.SUBTILE_SIZE; if (UIUtils.shift()) amount *= 8; y += amount; - mapRenderer.setPosition(x, y); - //mapRenderer.runMath(x, y); + ent.position().set(x, y, 0); return true; case Input.Keys.A: //case Input.Keys.LEFT: - amount = UIUtils.ctrl() ? 1 : DT1.Tile.SUBTILE_SIZE; + amount = UIUtils.ctrl() ? 1 : Tile.SUBTILE_SIZE; if (UIUtils.shift()) amount *= 8; x -= amount; - mapRenderer.setPosition(x, y); - //mapRenderer.runMath(x, y); + ent.position().set(x, y, 0); return true; case Input.Keys.D: //case Input.Keys.RIGHT: - amount = UIUtils.ctrl() ? 1 : DT1.Tile.SUBTILE_SIZE; + amount = UIUtils.ctrl() ? 1 : Tile.SUBTILE_SIZE; if (UIUtils.shift()) amount *= 8; x += amount; - mapRenderer.setPosition(x, y); - //mapRenderer.runMath(x, y); + ent.position().set(x, y, 0); return true; case Input.Keys.UP: Gdx.input.setCursorPosition(Gdx.input.getX(), Gdx.input.getY() - 1); @@ -285,7 +278,7 @@ public class MapViewer extends ApplicationAdapter { @Override public boolean mouseMoved(int screenX, int screenY) { - GridPoint2 pt = mapRenderer.toWorldSpace(screenX, screenY); + //GridPoint2 pt = mapRenderer.toWorldSpace(screenX, screenY); //System.out.println(pt); return true; } @@ -336,19 +329,13 @@ public class MapViewer extends ApplicationAdapter { GridPoint2 origin = map.find(Map.ID.TOWN_ENTRY_1); if (origin != null) { - //x = origin.x; - //y = origin.y; - //mapRenderer.setPosition(x, y); - } else { - //x += DT1.Tile.SUBTILE_CENTER.x; - //y += DT1.Tile.SUBTILE_CENTER.y; - //mapRenderer.setPosition(x, y); + x = origin.x; + y = origin.y; } - x = 0; - y = 0; - mapRenderer.setPosition(x, y, true); - + ent = new Player("null", CharClass.BARBARIAN); + ent.position().set(x, y, 0); + mapRenderer.setSrc(ent); for (String asset : Diablo.assets.getAssetNames()) { Gdx.app.debug(TAG, Diablo.assets.getReferenceCount(asset) + " : " + asset); @@ -362,21 +349,19 @@ public class MapViewer extends ApplicationAdapter { public void render() { Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); - mapRenderer.hit(); + mapRenderer.update(); PaletteIndexedBatch batch = Diablo.batch; - batch.setProjectionMatrix(camera.combined); batch.begin(palette); - mapRenderer.render(); + mapRenderer.draw(Gdx.graphics.getDeltaTime()); batch.end(); - shapes.setProjectionMatrix(camera.combined); shapes.setAutoShapeType(true); shapes.begin(ShapeRenderer.ShapeType.Line); - mapRenderer.renderDebug(shapes); + mapRenderer.drawDebug(shapes); if (src != null && dst != null) { - //mapRenderer.renderDebugPath(shapes, src, dst); - mapRenderer.renderDebugPath2(shapes, path); + mapRenderer.drawDebugPath(shapes, path); + mapRenderer.drawDebugPath(shapes, smoothedPath, Color.GREEN); float srcX = +(src.x * Tile.SUBTILE_WIDTH50) - (src.y * Tile.SUBTILE_WIDTH50); float srcY = -(src.x * Tile.SUBTILE_HEIGHT50) - (src.y * Tile.SUBTILE_HEIGHT50); float dstX = +(dst.x * Tile.SUBTILE_WIDTH50) - (dst.y * Tile.SUBTILE_WIDTH50); @@ -391,6 +376,17 @@ public class MapViewer extends ApplicationAdapter { final int width = Gdx.graphics.getWidth(); final int height = Gdx.graphics.getHeight(); + + if (drawCrosshair) { + shapes.getProjectionMatrix().setToOrtho2D(0, 0, width, height); + shapes.updateMatrices(); + shapes.begin(ShapeRenderer.ShapeType.Line); + shapes.setColor(Color.GREEN); + shapes.line(0, height / 2, width, height / 2); + shapes.line(width / 2, 0, width / 2, height); + shapes.end(); + } + batch.getProjectionMatrix().setToOrtho2D(0, 0, width, height); batch.begin(); batch.setShader(null); @@ -407,16 +403,6 @@ public class MapViewer extends ApplicationAdapter { batch.end(); batch.setShader(Diablo.shader); - - /*final int width = Gdx.graphics.getWidth(); - final int height = Gdx.graphics.getHeight(); - shapes.getProjectionMatrix().setToOrtho2D(0, 0, width, height); - shapes.updateMatrices(); - shapes.begin(ShapeRenderer.ShapeType.Line); - shapes.setColor(Color.GREEN); - shapes.line(width >>> 1, 0, width >>> 1, height); - shapes.line(0, height >>> 1, width, height >>> 1); - shapes.end();*/ } @Override diff --git a/out/production/android/android.apk b/out/production/android/android.apk deleted file mode 100644 index 83bad2d6..00000000 Binary files a/out/production/android/android.apk and /dev/null differ diff --git a/out/production/android/android.unaligned.apk b/out/production/android/android.unaligned.apk deleted file mode 100644 index 6279a175..00000000 Binary files a/out/production/android/android.unaligned.apk and /dev/null differ