diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/hunter/HunterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/hunter/HunterPlugin.java index 67d98d2987..bc6d7ec0ee 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/hunter/HunterPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/hunter/HunterPlugin.java @@ -28,10 +28,10 @@ import com.google.common.eventbus.Subscribe; import com.google.inject.Provides; import java.time.Instant; import java.util.Arrays; -import java.util.HashSet; +import java.util.HashMap; import java.util.Iterator; import java.util.List; -import java.util.Set; +import java.util.Map; import javax.inject.Inject; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -39,11 +39,12 @@ import net.runelite.api.Client; import net.runelite.api.GameObject; import net.runelite.api.ObjectID; import net.runelite.api.Player; +import net.runelite.api.Tile; +import net.runelite.api.coords.LocalPoint; import net.runelite.api.coords.WorldPoint; import net.runelite.api.events.ConfigChanged; import net.runelite.api.events.GameObjectSpawned; import net.runelite.api.events.GameTick; -import net.runelite.api.queries.GameObjectQuery; import net.runelite.api.queries.PlayerQuery; import net.runelite.client.Notifier; import net.runelite.client.config.ConfigManager; @@ -74,7 +75,7 @@ public class HunterPlugin extends Plugin private HunterConfig config; @Getter - private final Set traps = new HashSet<>(); + private final Map traps = new HashMap<>(); @Getter private Instant lastActionTime = Instant.ofEpochMilli(0); @@ -102,7 +103,7 @@ public class HunterPlugin extends Plugin public void onGameObjectSpawned(GameObjectSpawned event) { final GameObject gameObject = event.getGameObject(); - final HunterTrap myTrap = getTrapFromCollection(gameObject); + final HunterTrap myTrap = traps.get(gameObject.getWorldLocation()); final Player localPlayer = client.getLocalPlayer(); switch (gameObject.getId()) @@ -118,7 +119,7 @@ public class HunterPlugin extends Plugin if (localPlayer.getWorldLocation().distanceTo(gameObject.getWorldLocation()) <= 1) { log.debug("Trap placed by \"{}\" on {}", localPlayer.getName(), gameObject.getWorldLocation()); - traps.add(new HunterTrap(gameObject)); + traps.put(gameObject.getWorldLocation(), new HunterTrap(gameObject)); lastActionTime = Instant.now(); } @@ -138,7 +139,7 @@ public class HunterPlugin extends Plugin if (possiblePlayers.contains(localPlayer)) { log.debug("Trap placed by \"{}\" on {}", localPlayer.getName(), localPlayer.getWorldLocation()); - traps.add(new HunterTrap(gameObject)); + traps.put(gameObject.getWorldLocation(), new HunterTrap(gameObject)); lastActionTime = Instant.now(); } @@ -172,7 +173,7 @@ public class HunterPlugin extends Plugin myTrap.setState(HunterTrap.State.FULL); lastActionTime = Instant.now(); - if (config.maniacalMonkeyNotify() && myTrap.getGameObject().getId() == ObjectID.MONKEY_TRAP) + if (config.maniacalMonkeyNotify() && myTrap.getObjectId() == ObjectID.MONKEY_TRAP) { notifier.notify("You've caught part of a monkey's tail."); } @@ -267,35 +268,64 @@ public class HunterPlugin extends Plugin public void onGameTick(GameTick event) { // Check if all traps are still there, and remove the ones that are not. - // TODO: use despawn events - Iterator it = traps.iterator(); + Iterator> it = traps.entrySet().iterator(); + Tile[][][] tiles = client.getRegion().getTiles(); + + Instant expire = Instant.now().minus(HunterTrap.TRAP_TIME.multipliedBy(2)); + while (it.hasNext()) { - HunterTrap trap = it.next(); + Map.Entry entry = it.next(); + HunterTrap trap = entry.getValue(); + WorldPoint world = entry.getKey(); + LocalPoint local = LocalPoint.fromWorld(client, world); - // Look for gameobjects that are on the same location as the trap - GameObjectQuery goQuery = new GameObjectQuery() - .atWorldLocation(trap.getGameObject().getWorldLocation()); - // This is for placeable traps like box traps. There are no gameobjects on that location if the trap collapsed - if (queryRunner.runQuery(goQuery).length == 0) + // Not within the client's viewport + if (local == null) + { + // Cull very old traps + if (trap.getPlacedOn().isBefore(expire)) + { + log.debug("Trap removed from personal trap collection due to timeout, {} left", traps.size()); + it.remove(); + continue; + } + continue; + } + + Tile tile = tiles[world.getPlane()][local.getRegionX()][local.getRegionY()]; + GameObject[] objects = tile.getGameObjects(); + + boolean containsBoulder = false; + boolean containsAnything = false; + for (GameObject object : objects) + { + if (object != null) + { + containsAnything = true; + if (object.getId() == ObjectID.BOULDER_19215 || object.getId() == ObjectID.LARGE_BOULDER) + { + containsBoulder = true; + break; + } + } + } + + if (!containsAnything) { it.remove(); log.debug("Trap removed from personal trap collection, {} left", traps.size()); } - else // For traps like deadfalls. This is different because when the trap is gone, there is still a GameObject (boulder) + else if (containsBoulder) // For traps like deadfalls. This is different because when the trap is gone, there is still a GameObject (boulder) { - goQuery = goQuery.idEquals(ObjectID.BOULDER_19215, ObjectID.LARGE_BOULDER); - if (queryRunner.runQuery(goQuery).length != 0) - { - it.remove(); - log.debug("Special trap removed from personal trap collection, {} left", traps.size()); + it.remove(); + log.debug("Special trap removed from personal trap collection, {} left", traps.size()); - // Case we have notifications enabled and the action was not manual, throw notification - if (config.maniacalMonkeyNotify() && trap.getGameObject().getId() == ObjectID.MONKEY_TRAP && - !trap.getState().equals(HunterTrap.State.FULL) && !trap.getState().equals(HunterTrap.State.OPEN)) - { - notifier.notify("The monkey escaped."); - } + // Case we have notifications enabled and the action was not manual, throw notification + if (config.maniacalMonkeyNotify() && trap.getObjectId() == ObjectID.MONKEY_TRAP && + !trap.getState().equals(HunterTrap.State.FULL) && !trap.getState().equals(HunterTrap.State.OPEN)) + { + notifier.notify("The monkey escaped."); } } } @@ -310,27 +340,4 @@ public class HunterPlugin extends Plugin overlay.updateConfig(); } } - - /** - * Looks for a trap in the local players trap collection, on the same - * place as the given GameObject. - * - * @param gameObject game object - * @return A HunterTrap object if the player has a trap on the same - * location as the GameObject. Otherwise it returns null. - */ - private HunterTrap getTrapFromCollection(GameObject gameObject) - { - final WorldPoint gameObjectLocation = gameObject.getWorldLocation(); - - for (HunterTrap trap : traps) - { - if (gameObjectLocation.equals(trap.getGameObject().getWorldLocation())) - { - return trap; - } - } - - return null; - } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/hunter/HunterTrap.java b/runelite-client/src/main/java/net/runelite/client/plugins/hunter/HunterTrap.java index 10d9a3ba37..4a28288994 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/hunter/HunterTrap.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/hunter/HunterTrap.java @@ -26,10 +26,10 @@ package net.runelite.client.plugins.hunter; import java.time.Duration; import java.time.Instant; -import java.util.Objects; import lombok.Getter; import lombok.Setter; import net.runelite.api.GameObject; +import net.runelite.api.coords.WorldPoint; /** * Wrapper class for a GameObject that represents a hunter trap. @@ -39,7 +39,7 @@ class HunterTrap /** * A hunter trap stays up 1 minute before collapsing. */ - private static final Duration TRAP_TIME = Duration.ofMinutes(1); + static final Duration TRAP_TIME = Duration.ofMinutes(1); /** * The time in milliseconds when the trap was placed. @@ -55,10 +55,13 @@ class HunterTrap private State state; /** - * The gameobject thats corresponds with this trap. + * The ID of the game object this is representing */ @Getter - private final GameObject gameObject; + private int objectId; + + @Getter + private WorldPoint worldLocation; /** * The states a trap can be in. @@ -91,8 +94,9 @@ class HunterTrap HunterTrap(GameObject gameObject) { this.state = State.OPEN; - this.gameObject = gameObject; this.placedOn = Instant.now(); + this.objectId = gameObject.getId(); + this.worldLocation = gameObject.getWorldLocation(); } /** @@ -114,27 +118,4 @@ class HunterTrap { placedOn = Instant.now(); } - - @Override - public boolean equals(Object o) - { - if (o == this) - { - return true; - } - if (!(o instanceof HunterTrap)) - { - return false; - } - HunterTrap t = (HunterTrap) o; - return gameObject.getWorldLocation().equals(t.getGameObject().getWorldLocation()); - } - - @Override - public int hashCode() - { - int hash = 7; - hash = 97 * hash + Objects.hashCode(this.gameObject.getWorldLocation()); - return hash; - } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/hunter/TrapOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/hunter/TrapOverlay.java index 993226e9a6..530f710307 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/hunter/TrapOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/hunter/TrapOverlay.java @@ -30,9 +30,13 @@ import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Point; import java.awt.geom.Arc2D; +import java.util.Iterator; +import java.util.Map; import javax.inject.Inject; import net.runelite.api.Client; +import net.runelite.api.Perspective; import net.runelite.api.coords.LocalPoint; +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; @@ -43,8 +47,6 @@ import net.runelite.client.ui.overlay.OverlayPosition; */ public class TrapOverlay extends Overlay { - private static final int MAX_DISTANCE = 2500; - /** * Size of the trap timer. */ @@ -109,27 +111,26 @@ public class TrapOverlay extends Overlay */ private void drawTraps(Graphics2D graphics) { - LocalPoint localLocation = client.getLocalPlayer().getLocalLocation(); - for (HunterTrap trap : plugin.getTraps()) + Iterator> it = plugin.getTraps().entrySet().iterator(); + while (it.hasNext()) { - LocalPoint trapLocation = trap.getGameObject().getLocalLocation(); - if (trapLocation != null && localLocation.distanceTo(trapLocation) <= MAX_DISTANCE) + Map.Entry entry = it.next(); + HunterTrap trap = entry.getValue(); + + switch (trap.getState()) { - switch (trap.getState()) - { - case OPEN: - drawTimerOnTrap(graphics, trap, colorOpen, colorOpenBorder, colorEmpty, colorOpenBorder); - break; - case EMPTY: - drawTimerOnTrap(graphics, trap, colorEmpty, colorEmptyBorder, colorEmpty, colorEmptyBorder); - break; - case FULL: - drawCircleOnTrap(graphics, trap, colorFull, colorFullBorder); - break; - case TRANSITION: - drawCircleOnTrap(graphics, trap, colorTrans, colorTransBorder); - break; - } + case OPEN: + drawTimerOnTrap(graphics, trap, colorOpen, colorOpenBorder, colorEmpty, colorOpenBorder); + break; + case EMPTY: + drawTimerOnTrap(graphics, trap, colorEmpty, colorEmptyBorder, colorEmpty, colorEmptyBorder); + break; + case FULL: + drawCircleOnTrap(graphics, trap, colorFull, colorFullBorder); + break; + case TRANSITION: + drawCircleOnTrap(graphics, trap, colorTrans, colorTransBorder); + break; } } } @@ -146,7 +147,16 @@ public class TrapOverlay extends Overlay */ private void drawTimerOnTrap(Graphics2D graphics, HunterTrap trap, Color fill, Color border, Color fillTimeLow, Color borderTimeLow) { - net.runelite.api.Point loc = trap.getGameObject().getCanvasLocation(); + if (trap.getWorldLocation().getPlane() != client.getPlane()) + { + return; + } + LocalPoint localLoc = LocalPoint.fromWorld(client, trap.getWorldLocation()); + if (localLoc == null) + { + return; + } + net.runelite.api.Point loc = Perspective.worldToCanvas(client, localLoc.getX(), localLoc.getY(), trap.getWorldLocation().getPlane()); //Construct the arc Arc2D.Float arc = new Arc2D.Float(Arc2D.PIE); @@ -175,7 +185,16 @@ public class TrapOverlay extends Overlay */ private void drawCircleOnTrap(Graphics2D graphics, HunterTrap trap, Color fill, Color border) { - net.runelite.api.Point loc = trap.getGameObject().getCanvasLocation(); + if (trap.getWorldLocation().getPlane() != client.getPlane()) + { + return; + } + LocalPoint localLoc = LocalPoint.fromWorld(client, trap.getWorldLocation()); + if (localLoc == null) + { + return; + } + net.runelite.api.Point loc = Perspective.worldToCanvas(client, localLoc.getX(), localLoc.getY(), trap.getWorldLocation().getPlane()); //Draw the inside of the arc graphics.setColor(fill);