diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerConfig.java index bc2a65f0d9..21969ec2c3 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerConfig.java @@ -23,6 +23,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.runelite.client.plugins.groundmarkers; import java.awt.Color; @@ -30,6 +31,7 @@ import net.runelite.client.config.Alpha; import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.Range; @ConfigGroup("groundMarker") public interface GroundMarkerConfig extends Config @@ -37,31 +39,68 @@ public interface GroundMarkerConfig extends Config @Alpha @ConfigItem( keyName = "markerColor", - name = "Color of the tile", - description = "Configures the color of marked tile" + name = "Default Marked tile Color", + description = "Configures the default color of marked tiles" ) default Color markerColor() { return Color.YELLOW; } + @Alpha @ConfigItem( - keyName = "rememberTileColors", - name = "Remember color per tile", - description = "Color tiles using the color from time of placement" + keyName = "markerColor2", + name = "Group 2 tile color", + description = "Configures the color of the 2nd group of marked tiles" ) - default boolean rememberTileColors() + default Color markerColor2() + { + return Color.RED; + } + + @Alpha + @ConfigItem( + keyName = "markerColor3", + name = "Group 3 tile color", + description = "Configures the color of the 3rd group of marked tiles" + ) + default Color markerColor3() + { + return Color.BLUE; + } + + @Alpha + @ConfigItem( + keyName = "markerColor4", + name = "Group 4 tile color", + description = "Configures the color of the 4th group of marked tiles" + ) + default Color markerColor4() + { + return Color.GREEN; + } + + @ConfigItem( + keyName = "showMinimap", + name = "Show on minimap", + description = "Shows marked tiles on the minimap" + ) + default boolean showMinimap() { return false; } - @ConfigItem( - keyName = "drawOnMinimap", - name = "Draw tiles on minimap", - description = "Configures whether marked tiles should be drawn on minimap" + @Range( + min = 1, + max = 100 ) - default boolean drawTileOnMinimmap() + @ConfigItem( + keyName = "minimapOpacity", + name = "Minimap opacity", + description = "The opacity of the minimap markers" + ) + default int minimapOverlayOpacity() { - return false; + return 100; } -} +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerMinimapOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerMinimapOverlay.java index 04a762242e..4e53a7d966 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerMinimapOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerMinimapOverlay.java @@ -27,7 +27,8 @@ package net.runelite.client.plugins.groundmarkers; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics2D; -import java.util.Collection; +import static java.lang.Math.floor; +import java.util.List; import javax.inject.Inject; import net.runelite.api.Client; import net.runelite.api.Perspective; @@ -64,13 +65,13 @@ class GroundMarkerMinimapOverlay extends Overlay @Override public Dimension render(Graphics2D graphics) { - if (!config.drawTileOnMinimmap()) + if (!config.showMinimap()) { return null; } - final Collection points = plugin.getPoints(); - for (final ColorTileMarker point : points) + final List points = plugin.getPoints(); + for (final GroundMarkerWorldPoint point : points) { WorldPoint worldPoint = point.getWorldPoint(); if (worldPoint.getPlane() != client.getPlane()) @@ -78,13 +79,22 @@ class GroundMarkerMinimapOverlay extends Overlay continue; } - Color tileColor = point.getColor(); - if (tileColor == null || !config.rememberTileColors()) + Color color = config.markerColor(); + switch (point.getGroundMarkerPoint().getGroup()) { - // If this is an old tile which has no color, or rememberTileColors is off, use marker color - tileColor = config.markerColor(); + case 2: + color = config.markerColor2(); + break; + case 3: + color = config.markerColor3(); + break; + case 4: + color = config.markerColor4(); } + int opacity = (int) floor(config.minimapOverlayOpacity() * 2.55); + Color tileColor = new Color(color.getRed(), color.getGreen(), color.getBlue(), opacity); + drawOnMinimap(graphics, worldPoint, tileColor); } @@ -114,4 +124,4 @@ class GroundMarkerMinimapOverlay extends Overlay OverlayUtil.renderMinimapRect(client, graphics, posOnMinimap, TILE_WIDTH, TILE_HEIGHT, color); } -} +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerOverlay.java index ec1814bde4..f2760fc287 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerOverlay.java @@ -29,7 +29,7 @@ import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Polygon; -import java.util.Collection; +import java.util.List; import javax.inject.Inject; import net.runelite.api.Client; import net.runelite.api.Perspective; @@ -43,6 +43,7 @@ import net.runelite.client.ui.overlay.OverlayUtil; public class GroundMarkerOverlay extends Overlay { + private static final int MAX_DRAW_DISTANCE = 32; private final Client client; private final GroundMarkerConfig config; @@ -62,30 +63,28 @@ public class GroundMarkerOverlay extends Overlay @Override public Dimension render(Graphics2D graphics) { - final Collection points = plugin.getPoints(); - for (final ColorTileMarker point : points) + List points = plugin.getPoints(); + for (GroundMarkerWorldPoint groundMarkerWorldPoint : points) { - WorldPoint worldPoint = point.getWorldPoint(); - if (worldPoint.getPlane() != client.getPlane()) - { - continue; - } - - Color tileColor = point.getColor(); - if (tileColor == null || !config.rememberTileColors()) - { - // If this is an old tile which has no color, or rememberTileColors is off, use marker color - tileColor = config.markerColor(); - } - - drawTile(graphics, worldPoint, tileColor); + drawTile(graphics, groundMarkerWorldPoint); } return null; } - private void drawTile(Graphics2D graphics, WorldPoint point, Color color) + private void drawTile(Graphics2D graphics, GroundMarkerWorldPoint groundMarkerWorldPoint) { + WorldPoint point = groundMarkerWorldPoint.getWorldPoint(); + if (point.getPlane() != client.getPlane()) + { + return; + } + + WorldPoint playerLocation = client.getLocalPlayer().getWorldLocation(); + if (point.distanceTo(playerLocation) >= MAX_DRAW_DISTANCE) + { + return; + } LocalPoint lp = LocalPoint.fromWorld(client, point); if (lp == null) @@ -99,6 +98,18 @@ public class GroundMarkerOverlay extends Overlay return; } + Color color = config.markerColor(); + switch (groundMarkerWorldPoint.getGroundMarkerPoint().getGroup()) + { + case 2: + color = config.markerColor2(); + break; + case 3: + color = config.markerColor3(); + break; + case 4: + color = config.markerColor4(); + } OverlayUtil.renderPolygon(graphics, poly, color); } -} +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPlugin.java index 180fdc6bfc..e1282fd160 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPlugin.java @@ -34,13 +34,16 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.inject.Inject; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; +import static net.runelite.api.Constants.CHUNK_SIZE; import net.runelite.api.GameState; import net.runelite.api.MenuAction; import net.runelite.api.MenuEntry; @@ -68,6 +71,8 @@ public class GroundMarkerPlugin extends Plugin { private static final String CONFIG_GROUP = "groundMarker"; private static final String MARK = "Mark tile"; + private static final Pattern GROUP_MATCHER = Pattern.compile(".*ark tile \\(Group (\\d)\\)"); + private static final String UNMARK = "Unmark tile"; private static final String WALK_HERE = "Walk here"; private static final String REGION_PREFIX = "region_"; @@ -78,14 +83,11 @@ public class GroundMarkerPlugin extends Plugin private boolean hotKeyPressed; @Getter(AccessLevel.PACKAGE) - private final List points = new ArrayList<>(); + private final List points = new ArrayList<>(); @Inject private Client client; - @Inject - private GroundMarkerConfig config; - @Inject private GroundMarkerInputListener inputListener; @@ -123,12 +125,11 @@ public class GroundMarkerPlugin extends Plugin { return Collections.emptyList(); } + return GSON.fromJson(json, new GroundMarkerListTypeToken().getType()); + } - // CHECKSTYLE:OFF - return GSON.fromJson(json, new TypeToken>() - { - }.getType()); - // CHECKSTYLE:ON + private static class GroundMarkerListTypeToken extends TypeToken> + { } @Provides @@ -153,35 +154,100 @@ public class GroundMarkerPlugin extends Plugin // load points for region log.debug("Loading points for region {}", regionId); Collection regionPoints = getPoints(regionId); - Collection colorTileMarkers = translateToColorTileMarker(regionPoints); - points.addAll(colorTileMarkers); + Collection worldPoints = translateToWorld(regionPoints); + points.addAll(worldPoints); } } /** - * Translate a collection of ground marker points to color tile markers, accounting for instances + * Translate a collection of ground marker points to world points, accounting for instances * - * @param points {@link GroundMarkerPoint}s to be converted to {@link ColorTileMarker}s - * @return A collection of color tile markers, converted from the passed ground marker points, accounting for local - * instance points. See {@link WorldPoint#toLocalInstance(Client, WorldPoint)} + * @param points + * @return */ - private Collection translateToColorTileMarker(Collection points) + private Collection translateToWorld(Collection points) { if (points.isEmpty()) { - return Collections.emptyList(); + return Collections.EMPTY_LIST; } - return points.stream() - .map(point -> new ColorTileMarker( - WorldPoint.fromRegion(point.getRegionId(), point.getRegionX(), point.getRegionY(), point.getZ()), - point.getColor())) - .flatMap(colorTile -> + List worldPoints = new ArrayList<>(); + for (GroundMarkerPoint point : points) + { + int regionId = point.getRegionId(); + int regionX = point.getRegionX(); + int regionY = point.getRegionY(); + int z = point.getZ(); + + WorldPoint worldPoint = WorldPoint.fromRegion(regionId, regionX, regionY, z); + + if (!client.isInInstancedRegion()) { - final Collection localWorldPoints = WorldPoint.toLocalInstance(client, colorTile.getWorldPoint()); - return localWorldPoints.stream().map(wp -> new ColorTileMarker(wp, colorTile.getColor())); - }) - .collect(Collectors.toList()); + worldPoints.add(new GroundMarkerWorldPoint(point, worldPoint)); + continue; + } + + // find instance chunks using the template point. there might be more than one. + int[][][] instanceTemplateChunks = client.getInstanceTemplateChunks(); + for (int x = 0; x < instanceTemplateChunks[z].length; ++x) + { + for (int y = 0; y < instanceTemplateChunks[z][x].length; ++y) + { + int chunkData = instanceTemplateChunks[z][x][y]; + int rotation = chunkData >> 1 & 0x3; + int templateChunkY = (chunkData >> 3 & 0x7FF) * CHUNK_SIZE; + int templateChunkX = (chunkData >> 14 & 0x3FF) * CHUNK_SIZE; + if (worldPoint.getX() >= templateChunkX && worldPoint.getX() < templateChunkX + CHUNK_SIZE + && worldPoint.getY() >= templateChunkY && worldPoint.getY() < templateChunkY + CHUNK_SIZE) + { + WorldPoint p = new WorldPoint(client.getBaseX() + x * CHUNK_SIZE + (worldPoint.getX() & (CHUNK_SIZE - 1)), + client.getBaseY() + y * CHUNK_SIZE + (worldPoint.getY() & (CHUNK_SIZE - 1)), + worldPoint.getPlane()); + p = rotate(p, rotation); + worldPoints.add(new GroundMarkerWorldPoint(point, p)); + } + } + } + } + return worldPoints; + } + + /** + * Rotate the chunk containing the given point to rotation 0 + * + * @param point point + * @param rotation rotation + * @return world point + */ + private static WorldPoint rotateInverse(WorldPoint point, int rotation) + { + return rotate(point, 4 - rotation); + } + + /** + * Rotate the coordinates in the chunk according to chunk rotation + * + * @param point point + * @param rotation rotation + * @return world point + */ + private static WorldPoint rotate(WorldPoint point, int rotation) + { + int chunkX = point.getX() & ~(CHUNK_SIZE - 1); + int chunkY = point.getY() & ~(CHUNK_SIZE - 1); + int x = point.getX() & (CHUNK_SIZE - 1); + int y = point.getY() & (CHUNK_SIZE - 1); + switch (rotation) + { + case 1: + return new WorldPoint(chunkX + y, chunkY + (CHUNK_SIZE - 1 - x), point.getPlane()); + case 2: + return new WorldPoint(chunkX + (CHUNK_SIZE - 1 - x), chunkY + (CHUNK_SIZE - 1 - y), point.getPlane()); + case 3: + return new WorldPoint(chunkX + (CHUNK_SIZE - 1 - y), chunkY + x, point.getPlane()); + } + return point; } @Subscribe @@ -210,34 +276,59 @@ public class GroundMarkerPlugin extends Plugin { if (hotKeyPressed && event.getOption().equals(WALK_HERE)) { - // Can't insert into abstract list you get from Arrays.asList() - List menuEntries = new ArrayList<>(Arrays.asList(client.getMenuEntries())); + MenuEntry[] menuEntries = client.getMenuEntries(); + int lastIndex = menuEntries.length; + menuEntries = Arrays.copyOf(menuEntries, lastIndex + 4); - MenuEntry menuEntry = new MenuEntry(); + final Tile tile = client.getSelectedSceneTile(); + if (tile == null) + { + return; + } + final WorldPoint loc = WorldPoint.fromLocalInstance(client, tile.getLocalLocation()); + final int regionId = loc.getRegionID(); - menuEntry.setOption(MARK); - menuEntry.setTarget(event.getTarget()); - menuEntry.setType(MenuAction.CANCEL.getId()); + for (int i = 4; i > 0; i--) + { + MenuEntry menuEntry = menuEntries[lastIndex] = new MenuEntry(); - menuEntries.add(menuEntries.size() - 1, menuEntry); - client.setMenuEntries(menuEntries.toArray(new MenuEntry[0])); + final GroundMarkerPoint point = new GroundMarkerPoint(regionId, loc.getRegionX(), loc.getRegionY(), client.getPlane(), i); + final Optional stream = getPoints(regionId).stream().filter(x -> x.equals(point)).findAny(); + final String option = (stream.isPresent() && stream.get().getGroup() == i) ? UNMARK : MARK; + + menuEntry.setOption(option + (i == 1 ? "" : " (Group " + i + ")")); + menuEntry.setTarget(event.getTarget()); + menuEntry.setType(MenuAction.CANCEL.getId()); + + lastIndex++; + } + + client.setMenuEntries(menuEntries); } } @Subscribe public void onMenuOptionClicked(MenuOptionClicked event) { - if (!event.getMenuOption().equals(MARK)) + if (!event.getMenuOption().contains(MARK) && !event.getMenuOption().contains(UNMARK)) { return; } + int group = 1; + Matcher m = GROUP_MATCHER.matcher(event.getMenuOption()); + if (m.matches()) + { + group = Integer.parseInt(m.group(1)); + } + Tile target = client.getSelectedSceneTile(); + if (target == null) { return; } - markTile(target.getLocalLocation()); + markTile(target.getLocalLocation(), group); } @Override @@ -258,7 +349,7 @@ public class GroundMarkerPlugin extends Plugin points.clear(); } - private void markTile(LocalPoint localPoint) + protected void markTile(LocalPoint localPoint, int group) { if (localPoint == null) { @@ -268,21 +359,27 @@ public class GroundMarkerPlugin extends Plugin WorldPoint worldPoint = WorldPoint.fromLocalInstance(client, localPoint); int regionId = worldPoint.getRegionID(); - GroundMarkerPoint point = new GroundMarkerPoint(regionId, worldPoint.getRegionX(), worldPoint.getRegionY(), client.getPlane(), config.markerColor()); + GroundMarkerPoint point = new GroundMarkerPoint(regionId, worldPoint.getRegionX(), worldPoint.getRegionY(), client.getPlane(), group); log.debug("Updating point: {} - {}", point, worldPoint); - List groundMarkerPoints = new ArrayList<>(getPoints(regionId)); - if (groundMarkerPoints.contains(point)) + List points = new ArrayList<>(getPoints(regionId)); + if (points.contains(point)) { - groundMarkerPoints.remove(point); + GroundMarkerPoint old = points.get(points.indexOf(point)); + points.remove(point); + + if (old.getGroup() != group) + { + points.add(point); + } } else { - groundMarkerPoints.add(point); + points.add(point); } - savePoints(regionId, groundMarkerPoints); + savePoints(regionId, points); loadPoints(); } -} +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPoint.java index 53dce275ff..247c1e17dd 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPoint.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPoint.java @@ -23,22 +23,34 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + package net.runelite.client.plugins.groundmarkers; -import java.awt.Color; -import lombok.EqualsAndHashCode; import lombok.Value; -/** - * Used for serialization of ground marker points. - */ @Value -@EqualsAndHashCode(exclude = {"color"}) -class GroundMarkerPoint +public class GroundMarkerPoint { private int regionId; private int regionX; private int regionY; private int z; - private Color color; -} + private int group; + + @Override + public String toString() + { + return "GroundMarkerPoint(regionId=" + regionId + ",regionX=" + regionX + ",=regionY" + regionY + ",z=" + z + ")"; + } + + @Override + public boolean equals(Object o) + { + if (!(o instanceof GroundMarkerPoint)) + { + return false; + } + + return o.toString().equals(this.toString()); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/ColorTileMarker.java b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerWorldPoint.java similarity index 84% rename from runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/ColorTileMarker.java rename to runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerWorldPoint.java index 6a6a30a065..9ea9915459 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/ColorTileMarker.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerWorldPoint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Jordan Atwood + * Copyright (c) 2018, TheStonedTurtle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,17 +24,12 @@ */ package net.runelite.client.plugins.groundmarkers; -import java.awt.Color; import lombok.Value; import net.runelite.api.coords.WorldPoint; -/** - * Used to denote marked tiles and their colors. - * Note: This is not used for serialization of ground markers; see {@link GroundMarkerPoint} - */ @Value -class ColorTileMarker +public class GroundMarkerWorldPoint { + private GroundMarkerPoint groundMarkerPoint; private WorldPoint worldPoint; - private Color color; }