diff --git a/runelite-api/src/main/java/net/runelite/api/coords/WorldPoint.java b/runelite-api/src/main/java/net/runelite/api/coords/WorldPoint.java index a0a23e6674..a6db435a2b 100644 --- a/runelite-api/src/main/java/net/runelite/api/coords/WorldPoint.java +++ b/runelite-api/src/main/java/net/runelite/api/coords/WorldPoint.java @@ -326,4 +326,36 @@ public class WorldPoint { return ((x >> 6) << 8) | (y >> 6); } + + /** + * Converts the passed region ID and coordinates to a world coordinate + */ + public static WorldPoint fromRegion(int regionId, int regionX, int regionY, int plane) + { + return new WorldPoint( + ((regionId >>> 8) << 6) + regionX, + ((regionId & 0xff) << 6) + regionY, + plane); + } + + /** + * Gets the X-axis coordinate of the region coordinate + */ + public int getRegionX() + { + return getRegionOffset(x); + } + + /** + * Gets the Y-axis coordinate of the region coordinate + */ + public int getRegionY() + { + return getRegionOffset(y); + } + + private static int getRegionOffset(final int position) + { + return position & 0x3f; + } } 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/ColorTileMarker.java new file mode 100644 index 0000000000..8f45e9489a --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/ColorTileMarker.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019, Jordan Atwood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (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.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 +{ + private WorldPoint worldPoint; + private Color color; + + boolean sameTile(final ColorTileMarker other) + { + return worldPoint.equals(other.getWorldPoint()); + } +} 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 d77f3ab9f5..ba8cb98e14 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,7 +23,6 @@ * (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; @@ -45,4 +44,14 @@ public interface GroundMarkerConfig extends Config { return Color.YELLOW; } + + @ConfigItem( + keyName = "rememberTileColors", + name = "Remember color per tile", + description = "Color tiles using the color from time of placement" + ) + default boolean rememberTileColors() + { + return false; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerInputListener.java b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerInputListener.java index 3b87dd1802..a097d47d29 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerInputListener.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerInputListener.java @@ -64,4 +64,4 @@ public class GroundMarkerInputListener implements KeyListener plugin.setHotKeyPressed(false); } } -} \ 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 ccd6fd7483..153fe67154 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 @@ -25,10 +25,11 @@ */ package net.runelite.client.plugins.groundmarkers; +import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Polygon; -import java.util.List; +import java.util.Collection; import javax.inject.Inject; import net.runelite.api.Client; import net.runelite.api.Perspective; @@ -42,6 +43,8 @@ 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; private final GroundMarkerPlugin plugin; @@ -60,25 +63,26 @@ public class GroundMarkerOverlay extends Overlay @Override public Dimension render(Graphics2D graphics) { - List points = plugin.getPoints(); - for (WorldPoint point : points) + final Collection points = plugin.getPoints(); + for (final ColorTileMarker point : points) { - if (point.getPlane() != client.getPlane()) + if (point.getWorldPoint().getPlane() != client.getPlane()) { continue; } - drawTile(graphics, point); + final Color tileColor = config.rememberTileColors() ? point.getColor() : config.markerColor(); + drawTile(graphics, point.getWorldPoint(), tileColor); } return null; } - private void drawTile(Graphics2D graphics, WorldPoint point) + private void drawTile(Graphics2D graphics, WorldPoint point, Color color) { WorldPoint playerLocation = client.getLocalPlayer().getWorldLocation(); - if (point.distanceTo(playerLocation) >= 32) + if (point.distanceTo(playerLocation) >= MAX_DRAW_DISTANCE) { return; } @@ -95,6 +99,6 @@ public class GroundMarkerOverlay extends Overlay return; } - OverlayUtil.renderPolygon(graphics, poly, config.markerColor()); + 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 2cb7ebf1c2..4aa613af7c 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 @@ -29,11 +29,12 @@ import com.google.common.base.Strings; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import com.google.inject.Provides; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import javax.inject.Inject; import lombok.AccessLevel; @@ -69,19 +70,23 @@ public class GroundMarkerPlugin extends Plugin private static final String CONFIG_GROUP = "groundMarker"; private static final String MARK = "Mark tile"; private static final String WALK_HERE = "Walk here"; + private static final String REGION_PREFIX = "region_"; - private static final Gson gson = new Gson(); + private static final Gson GSON = new Gson(); @Getter(AccessLevel.PACKAGE) @Setter(AccessLevel.PACKAGE) private boolean hotKeyPressed; @Getter(AccessLevel.PACKAGE) - private final List points = new ArrayList<>(); + private final Set points = new HashSet<>(); @Inject private Client client; + @Inject + private GroundMarkerConfig config; + @Inject private GroundMarkerInputListener inputListener; @@ -97,28 +102,53 @@ public class GroundMarkerPlugin extends Plugin @Inject private KeyManager keyManager; + private void saveColorTileMarkers(int regionId, Collection points) + { + savePoints(regionId, translateFromColorTileMarker(points)); + } + private void savePoints(int regionId, Collection points) { if (points == null || points.isEmpty()) { - configManager.unsetConfiguration(CONFIG_GROUP, "region_" + regionId); + configManager.unsetConfiguration(CONFIG_GROUP, REGION_PREFIX + regionId); return; } - String json = gson.toJson(points); - configManager.setConfiguration(CONFIG_GROUP, "region_" + regionId, json); + String json = GSON.toJson(points); + configManager.setConfiguration(CONFIG_GROUP, REGION_PREFIX + regionId, json); } private Collection getPoints(int regionId) { - String json = configManager.getConfiguration(CONFIG_GROUP, "region_" + regionId); + String json = configManager.getConfiguration(CONFIG_GROUP, REGION_PREFIX + regionId); if (Strings.isNullOrEmpty(json)) { - return Collections.EMPTY_LIST; + return Collections.emptyList(); } - return gson.fromJson(json, new TypeToken>() + + Collection configPoints = GSON.fromJson(json, new GroundMarkerListTypeToken().getType()); + + if (configPoints.stream().anyMatch(point -> point.getColor() == null)) { - }.getType()); + log.debug("Adding color to old ground marker(s) of region " + regionId); + configPoints = configPoints.stream() + .map(point -> + { + if (point.getColor() != null) + { + return point; + } + return new GroundMarkerPoint(point.getRegionId(), point.getRegionX(), point.getRegionY(), point.getZ(), config.markerColor()); + }) + .collect(Collectors.toSet()); + savePoints(regionId, configPoints); + } + return configPoints; + } + + private static class GroundMarkerListTypeToken extends TypeToken> + { } @Provides @@ -132,46 +162,68 @@ public class GroundMarkerPlugin extends Plugin points.clear(); int[] regions = client.getMapRegions(); + + if (regions == null) + { + return; + } + for (int regionId : regions) { // load points for region log.debug("Loading points for region {}", regionId); - Collection regionPoints = getPoints(regionId); - Collection worldPoints = translateToWorld(regionPoints); - points.addAll(worldPoints); + final Collection configPoints = getPoints(regionId); + final Collection colorTileMarkers = translateToColorTileMarker(configPoints); + points.addAll(colorTileMarkers); } } /** - * Translate a collection of ground marker points to world points, accounting for instances + * Translate a collection of ground marker points to color tile markers, accounting for instances * - * @param points - * @return + * @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)} */ - private Collection translateToWorld(Collection points) + private Collection translateToColorTileMarker(Collection points) { - if (points.isEmpty()) + if (points == null || points.isEmpty()) { - return Collections.EMPTY_LIST; + return Collections.emptyList(); + } + + return points.stream() + .map(point -> new ColorTileMarker( + WorldPoint.fromRegion(point.getRegionId(), point.getRegionX(), point.getRegionY(), point.getZ()), + point.getColor())) + .flatMap(colorTile -> + { + final Collection localWorldPoints = WorldPoint.toLocalInstance(client, colorTile.getWorldPoint()); + return localWorldPoints.stream().map(wp -> new ColorTileMarker(wp, colorTile.getColor())); + }) + .collect(Collectors.toSet()); + } + + /** + * Translate a collection of color tile markers to a set of ground marker points + * + * @param points {@link ColorTileMarker}s to be converted to {@link GroundMarkerPoint}s + * @return A set of ground marker points, converted from the passed color tile markers + */ + private static Set translateFromColorTileMarker(Collection points) + { + if (points == null || points.isEmpty()) + { + return Collections.emptySet(); } return points.stream() .map(point -> { - int regionId = point.getRegionId(); - int regionX = point.getRegionX(); - int regionY = point.getRegionY(); - int z = point.getZ(); - - // world point of the tile marker - return new WorldPoint( - ((regionId >>> 8) << 6) + regionX, - ((regionId & 0xff) << 6) + regionY, - z - ); + final WorldPoint worldPoint = point.getWorldPoint(); + return new GroundMarkerPoint(worldPoint.getRegionID(), worldPoint.getRegionX(), worldPoint.getRegionY(), worldPoint.getPlane(), point.getColor()); }) - .flatMap(wp -> WorldPoint.toLocalInstance(client, wp).stream()) - .collect(Collectors.toList()); + .collect(Collectors.toSet()); } @Subscribe @@ -234,6 +286,7 @@ public class GroundMarkerPlugin extends Plugin { overlayManager.add(overlay); keyManager.registerKeyListener(inputListener); + loadPoints(); } @Override @@ -241,34 +294,39 @@ public class GroundMarkerPlugin extends Plugin { overlayManager.remove(overlay); keyManager.unregisterKeyListener(inputListener); + points.clear(); } - - protected void markTile(LocalPoint localPoint) + private void markTile(LocalPoint localPoint) { if (localPoint == null) { return; } - WorldPoint worldPoint = WorldPoint.fromLocalInstance(client, localPoint); - - int regionId = worldPoint.getRegionID(); - GroundMarkerPoint point = new GroundMarkerPoint(regionId, worldPoint.getX() & 0x3f, worldPoint.getY() & 0x3f, client.getPlane()); + final WorldPoint worldPoint = WorldPoint.fromLocalInstance(client, localPoint); + final ColorTileMarker point = new ColorTileMarker(worldPoint, config.markerColor()); log.debug("Updating point: {} - {}", point, worldPoint); - List points = new ArrayList<>(getPoints(regionId)); if (points.contains(point)) { points.remove(point); } else { - points.add(point); + // Remove any points on the same tile but are of a different color + points.removeIf(p -> p.sameTile(point)); + + // Add point back only if we are remembering tile colors, otherwise simply remove it + if (config.rememberTileColors()) + { + points.add(point); + } } - savePoints(regionId, points); + final int regionId = worldPoint.getRegionID(); + saveColorTileMarkers(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 b31db32222..ca3aaadb6d 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,16 +23,20 @@ * (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.Value; +/** + * Used for serialization of ground marker points. + */ @Value -public class GroundMarkerPoint +class GroundMarkerPoint { private int regionId; private int regionX; private int regionY; private int z; -} \ No newline at end of file + private Color color; +}