diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java index 559cbee86f..b18c99ed5e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java @@ -180,6 +180,22 @@ public class DevToolsPanel extends PluginPanel }); container.add(mapSquaresBtn); + final JButton validMovementBtn = new JButton("Valid Moves"); + validMovementBtn.addActionListener(e -> + { + highlightButton(validMovementBtn); + plugin.toggleValidMovement(); + }); + container.add(validMovementBtn); + + final JButton lineOfSightBtn = new JButton("Line of Sight"); + lineOfSightBtn.addActionListener(e -> + { + highlightButton(lineOfSightBtn); + plugin.toggleLineOfSight(); + }); + container.add(lineOfSightBtn); + return container; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java index ca61efec6e..deba2b4f9a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java @@ -67,7 +67,7 @@ public class DevToolsPlugin extends Plugin private LocationOverlay locationOverlay; @Inject - private BorderOverlay borderOverlay; + private SceneOverlay sceneOverlay; @Inject private EventBus eventBus; @@ -84,6 +84,8 @@ public class DevToolsPlugin extends Plugin private boolean toggleLocation; private boolean toggleChunkBorders; private boolean toggleMapSquares; + private boolean toggleValidMovement; + private boolean toggleLineOfSight; Widget currentWidget; int itemIndex = -1; @@ -129,7 +131,7 @@ public class DevToolsPlugin extends Plugin @Override public Collection getOverlays() { - return Arrays.asList(overlay, locationOverlay, borderOverlay); + return Arrays.asList(overlay, locationOverlay, sceneOverlay); } @Subscribe @@ -223,6 +225,16 @@ public class DevToolsPlugin extends Plugin toggleMapSquares = !toggleMapSquares; } + void toggleValidMovement() + { + toggleValidMovement = !toggleValidMovement; + } + + void toggleLineOfSight() + { + toggleLineOfSight = !toggleLineOfSight; + } + boolean isTogglePlayers() { return togglePlayers; @@ -282,4 +294,14 @@ public class DevToolsPlugin extends Plugin { return toggleMapSquares; } + + boolean isToggleValidMovement() + { + return toggleValidMovement; + } + + boolean isToggleLineOfSight() + { + return toggleLineOfSight; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/BorderOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/SceneOverlay.java similarity index 55% rename from runelite-client/src/main/java/net/runelite/client/plugins/devtools/BorderOverlay.java rename to runelite-client/src/main/java/net/runelite/client/plugins/devtools/SceneOverlay.java index e2ee297881..cc8a703d2a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/BorderOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/SceneOverlay.java @@ -28,33 +28,44 @@ import java.awt.BasicStroke; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics2D; +import java.awt.Polygon; import java.awt.geom.GeneralPath; +import java.util.List; import javax.inject.Inject; +import net.runelite.api.Actor; import net.runelite.api.Client; +import net.runelite.api.NPC; import net.runelite.api.Perspective; +import net.runelite.api.Player; import net.runelite.api.Point; import net.runelite.api.coords.LocalPoint; +import net.runelite.api.coords.WorldArea; import net.runelite.api.coords.WorldPoint; import net.runelite.client.ui.overlay.Overlay; import net.runelite.client.ui.overlay.OverlayLayer; import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayUtil; -public class BorderOverlay extends Overlay +public class SceneOverlay extends Overlay { private static final Color MAP_SQUARE_COLOR = Color.GREEN; private static final Color CHUNK_BORDER_COLOR = Color.BLUE; + private static final Color LOCAL_VALID_MOVEMENT_COLOR = new Color(141, 220, 26); + private static final Color VALID_MOVEMENT_COLOR = new Color(73, 122, 18); + private static final Color LINE_OF_SIGHT_COLOR = new Color(204, 42, 219); private static final int LOCAL_TILE_SIZE = Perspective.LOCAL_TILE_SIZE; private static final int CHUNK_SIZE = 8; private static final int MAP_SQUARE_SIZE = CHUNK_SIZE * CHUNK_SIZE; // 64 - private static final int CULL_RANGE = 16; + private static final int CULL_CHUNK_BORDERS_RANGE = 16; private static final int STROKE_WIDTH = 4; + private static final int CULL_LINE_OF_SIGHT_RANGE = 10; private final Client client; private final DevToolsPlugin plugin; @Inject - public BorderOverlay(Client client, DevToolsPlugin plugin) + public SceneOverlay(Client client, DevToolsPlugin plugin) { setPosition(OverlayPosition.DYNAMIC); setLayer(OverlayLayer.ABOVE_SCENE); @@ -75,16 +86,26 @@ public class BorderOverlay extends Overlay renderMapSquares(graphics); } + if (plugin.isToggleLineOfSight()) + { + renderLineOfSight(graphics); + } + + if (plugin.isToggleValidMovement()) + { + renderValidMovement(graphics); + } + return null; } private void renderChunkBorders(Graphics2D graphics) { WorldPoint wp = client.getLocalPlayer().getWorldLocation(); - int startX = (wp.getX() - CULL_RANGE + CHUNK_SIZE - 1) / CHUNK_SIZE * CHUNK_SIZE; - int startY = (wp.getY() - CULL_RANGE + CHUNK_SIZE - 1) / CHUNK_SIZE * CHUNK_SIZE; - int endX = (wp.getX() + CULL_RANGE) / CHUNK_SIZE * CHUNK_SIZE; - int endY = (wp.getY() + CULL_RANGE) / CHUNK_SIZE * CHUNK_SIZE; + int startX = (wp.getX() - CULL_CHUNK_BORDERS_RANGE + CHUNK_SIZE - 1) / CHUNK_SIZE * CHUNK_SIZE; + int startY = (wp.getY() - CULL_CHUNK_BORDERS_RANGE + CHUNK_SIZE - 1) / CHUNK_SIZE * CHUNK_SIZE; + int endX = (wp.getX() + CULL_CHUNK_BORDERS_RANGE) / CHUNK_SIZE * CHUNK_SIZE; + int endY = (wp.getY() + CULL_CHUNK_BORDERS_RANGE) / CHUNK_SIZE * CHUNK_SIZE; graphics.setStroke(new BasicStroke(STROKE_WIDTH)); graphics.setColor(CHUNK_BORDER_COLOR); @@ -92,8 +113,8 @@ public class BorderOverlay extends Overlay GeneralPath path = new GeneralPath(); for (int x = startX; x <= endX; x += CHUNK_SIZE) { - LocalPoint lp1 = LocalPoint.fromWorld(client, x, wp.getY() - CULL_RANGE); - LocalPoint lp2 = LocalPoint.fromWorld(client, x, wp.getY() + CULL_RANGE); + LocalPoint lp1 = LocalPoint.fromWorld(client, x, wp.getY() - CULL_CHUNK_BORDERS_RANGE); + LocalPoint lp2 = LocalPoint.fromWorld(client, x, wp.getY() + CULL_CHUNK_BORDERS_RANGE); boolean first = true; for (int y = lp1.getY(); y <= lp2.getY(); y += LOCAL_TILE_SIZE) @@ -118,8 +139,8 @@ public class BorderOverlay extends Overlay } for (int y = startY; y <= endY; y += CHUNK_SIZE) { - LocalPoint lp1 = LocalPoint.fromWorld(client, wp.getX() - CULL_RANGE, y); - LocalPoint lp2 = LocalPoint.fromWorld(client, wp.getX() + CULL_RANGE, y); + LocalPoint lp1 = LocalPoint.fromWorld(client, wp.getX() - CULL_CHUNK_BORDERS_RANGE, y); + LocalPoint lp2 = LocalPoint.fromWorld(client, wp.getX() + CULL_CHUNK_BORDERS_RANGE, y); boolean first = true; for (int x = lp1.getX(); x <= lp2.getX(); x += LOCAL_TILE_SIZE) @@ -148,10 +169,10 @@ public class BorderOverlay extends Overlay private void renderMapSquares(Graphics2D graphics) { WorldPoint wp = client.getLocalPlayer().getWorldLocation(); - int startX = (wp.getX() - CULL_RANGE + MAP_SQUARE_SIZE - 1) / MAP_SQUARE_SIZE * MAP_SQUARE_SIZE; - int startY = (wp.getY() - CULL_RANGE + MAP_SQUARE_SIZE - 1) / MAP_SQUARE_SIZE * MAP_SQUARE_SIZE; - int endX = (wp.getX() + CULL_RANGE) / MAP_SQUARE_SIZE * MAP_SQUARE_SIZE; - int endY = (wp.getY() + CULL_RANGE) / MAP_SQUARE_SIZE * MAP_SQUARE_SIZE; + int startX = (wp.getX() - CULL_CHUNK_BORDERS_RANGE + MAP_SQUARE_SIZE - 1) / MAP_SQUARE_SIZE * MAP_SQUARE_SIZE; + int startY = (wp.getY() - CULL_CHUNK_BORDERS_RANGE + MAP_SQUARE_SIZE - 1) / MAP_SQUARE_SIZE * MAP_SQUARE_SIZE; + int endX = (wp.getX() + CULL_CHUNK_BORDERS_RANGE) / MAP_SQUARE_SIZE * MAP_SQUARE_SIZE; + int endY = (wp.getY() + CULL_CHUNK_BORDERS_RANGE) / MAP_SQUARE_SIZE * MAP_SQUARE_SIZE; graphics.setStroke(new BasicStroke(STROKE_WIDTH)); graphics.setColor(MAP_SQUARE_COLOR); @@ -159,8 +180,8 @@ public class BorderOverlay extends Overlay GeneralPath path = new GeneralPath(); for (int x = startX; x <= endX; x += MAP_SQUARE_SIZE) { - LocalPoint lp1 = LocalPoint.fromWorld(client, x, wp.getY() - CULL_RANGE); - LocalPoint lp2 = LocalPoint.fromWorld(client, x, wp.getY() + CULL_RANGE); + LocalPoint lp1 = LocalPoint.fromWorld(client, x, wp.getY() - CULL_CHUNK_BORDERS_RANGE); + LocalPoint lp2 = LocalPoint.fromWorld(client, x, wp.getY() + CULL_CHUNK_BORDERS_RANGE); boolean first = true; for (int y = lp1.getY(); y <= lp2.getY(); y += LOCAL_TILE_SIZE) @@ -185,8 +206,8 @@ public class BorderOverlay extends Overlay } for (int y = startY; y <= endY; y += MAP_SQUARE_SIZE) { - LocalPoint lp1 = LocalPoint.fromWorld(client, wp.getX() - CULL_RANGE, y); - LocalPoint lp2 = LocalPoint.fromWorld(client, wp.getX() + CULL_RANGE, y); + LocalPoint lp1 = LocalPoint.fromWorld(client, wp.getX() - CULL_CHUNK_BORDERS_RANGE, y); + LocalPoint lp2 = LocalPoint.fromWorld(client, wp.getX() + CULL_CHUNK_BORDERS_RANGE, y); boolean first = true; for (int x = lp1.getX(); x <= lp2.getX(); x += LOCAL_TILE_SIZE) @@ -212,4 +233,125 @@ public class BorderOverlay extends Overlay graphics.draw(path); } + private void renderTileIfValidForMovement(Graphics2D graphics, Actor actor, int dx, int dy) + { + WorldArea area = actor.getWorldArea(); + if (area == null) + { + return; + } + + if (area.canTravelInDirection(client, dx, dy)) + { + LocalPoint lp = actor.getLocalLocation(); + if (lp == null) + { + return; + } + + lp = new LocalPoint( + lp.getX() + dx * Perspective.LOCAL_TILE_SIZE + dx * Perspective.LOCAL_TILE_SIZE * (area.getWidth() - 1) / 2, + lp.getY() + dy * Perspective.LOCAL_TILE_SIZE + dy * Perspective.LOCAL_TILE_SIZE * (area.getHeight() - 1) / 2); + if (lp == null) + { + return; + } + + Polygon poly = Perspective.getCanvasTilePoly(client, lp); + if (poly == null) + { + return; + } + + if (actor == client.getLocalPlayer()) + { + OverlayUtil.renderPolygon(graphics, poly, LOCAL_VALID_MOVEMENT_COLOR); + } + else + { + OverlayUtil.renderPolygon(graphics, poly, VALID_MOVEMENT_COLOR); + } + } + } + + private void renderValidMovement(Graphics2D graphics) + { + Player player = client.getLocalPlayer(); + List npcs = client.getNpcs(); + for (NPC npc : npcs) + { + if (player.getInteracting() != npc && npc.getInteracting() != player) + { + continue; + } + for (int dx = -1; dx <= 1; dx++) + { + for (int dy = -1; dy <= 1; dy++) + { + if (dx == 0 && dy == 0) + { + continue; + } + renderTileIfValidForMovement(graphics, npc, dx, dy); + } + } + } + + for (int dx = -1; dx <= 1; dx++) + { + for (int dy = -1; dy <= 1; dy++) + { + if (dx == 0 && dy == 0) + { + continue; + } + renderTileIfValidForMovement(graphics, player, dx, dy); + } + } + } + + private void renderTileIfHasLineOfSight(Graphics2D graphics, WorldArea start, int targetX, int targetY) + { + WorldPoint targetLocation = new WorldPoint(targetX, targetY, start.getPlane()); + if (targetLocation == null) + { + return; + } + + // Running the line of sight algorithm 100 times per frame doesn't + // seem to use much CPU time, however rendering 100 tiles does + if (start.hasLineOfSightTo(client, targetLocation)) + { + LocalPoint lp = LocalPoint.fromWorld(client, targetLocation); + if (lp == null) + { + return; + } + + Polygon poly = Perspective.getCanvasTilePoly(client, lp); + if (poly == null) + { + return; + } + + OverlayUtil.renderPolygon(graphics, poly, LINE_OF_SIGHT_COLOR); + } + } + + private void renderLineOfSight(Graphics2D graphics) + { + WorldArea area = client.getLocalPlayer().getWorldArea(); + for (int x = area.getX() - CULL_LINE_OF_SIGHT_RANGE; x <= area.getX() + CULL_LINE_OF_SIGHT_RANGE; x++) + { + for (int y = area.getY() - CULL_LINE_OF_SIGHT_RANGE; y <= area.getY() + CULL_LINE_OF_SIGHT_RANGE; y++) + { + if (x == area.getX() && y == area.getY()) + { + continue; + } + renderTileIfHasLineOfSight(graphics, area, x, y); + } + } + } + }