diff --git a/runelite-api/src/main/java/net/runelite/api/Varbits.java b/runelite-api/src/main/java/net/runelite/api/Varbits.java
index bea1e9b830..b9b8a7a4ed 100644
--- a/runelite-api/src/main/java/net/runelite/api/Varbits.java
+++ b/runelite-api/src/main/java/net/runelite/api/Varbits.java
@@ -219,7 +219,14 @@ public enum Varbits
HB_TRAIL_31372(5750),
HB_FINISH(5766),
- HB_STARTED(5767), //not working
+
+ /**
+ * Started hunting Herbiboar.
+ *
+ * NOTE: This value remains at 0 even after starting a Herbiboar trail up until searching the first object along the
+ * hunting path.
+ */
+ HB_STARTED(5767),
/**
* Barbarian Assault
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarMinimapOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarMinimapOverlay.java
index fff22cbc39..acbdbdfb89 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarMinimapOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarMinimapOverlay.java
@@ -35,7 +35,7 @@ import net.runelite.client.ui.overlay.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.OverlayUtil;
-public class HerbiboarMinimapOverlay extends Overlay
+class HerbiboarMinimapOverlay extends Overlay
{
private final HerbiboarPlugin plugin;
private final HerbiboarConfig config;
@@ -52,29 +52,30 @@ public class HerbiboarMinimapOverlay extends Overlay
@Override
public Dimension render(Graphics2D graphics)
{
- if (config.isTrailShown() && plugin.isInHerbiboarArea())
+ if (!config.isTrailShown() || !plugin.isInHerbiboarArea())
{
- HerbiboarTrail currentTrail = plugin.getCurrentTrail();
- int finishId = plugin.getFinishId();
- Set shownTrailIds = plugin.getShownTrails();
-
- for (TileObject tileObject : plugin.getTrails().values())
- {
- int id = tileObject.getId();
- Point minimapLocation = tileObject.getMinimapLocation();
-
- if (minimapLocation == null)
- {
- continue;
- }
-
- if (shownTrailIds.contains(id) && (finishId > 0 || (currentTrail != null && currentTrail.getTrailId() != id && currentTrail.getTrailId() + 1 != id)))
- {
- OverlayUtil.renderMinimapLocation(graphics, minimapLocation, config.getTrailColor());
- }
- }
+ return null;
}
+ TrailToSpot nextTrail = plugin.getNextTrail();
+ int finishId = plugin.getFinishId();
+ Set shownTrailIds = plugin.getShownTrails();
+
+ for (TileObject tileObject : plugin.getTrails().values())
+ {
+ int id = tileObject.getId();
+ Point minimapLocation = tileObject.getMinimapLocation();
+
+ if (minimapLocation == null)
+ {
+ continue;
+ }
+
+ if (shownTrailIds.contains(id) && (finishId > 0 || nextTrail != null && !nextTrail.getFootprintIds().contains(id)))
+ {
+ OverlayUtil.renderMinimapLocation(graphics, minimapLocation, config.getTrailColor());
+ }
+ }
return null;
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarOverlay.java
index a97f5d9d49..5389cd6fd0 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarOverlay.java
@@ -24,6 +24,7 @@
*/
package net.runelite.client.plugins.herbiboars;
+import com.google.common.collect.Iterables;
import com.google.inject.Inject;
import java.awt.Color;
import java.awt.Dimension;
@@ -59,17 +60,14 @@ class HerbiboarOverlay extends Overlay
return null;
}
- HerbiboarTrail currentTrail = plugin.getCurrentTrail();
-
+ HerbiboarSearchSpot.Group currentGroup = plugin.getCurrentGroup();
+ TrailToSpot nextTrail = plugin.getNextTrail();
int finishId = plugin.getFinishId();
// Draw start objects
- if (config.isStartShown() && (currentTrail == null && finishId == 0))
+ if (config.isStartShown() && (currentGroup == null && finishId == 0))
{
- plugin.getStarts().values().forEach((obj) ->
- {
- OverlayUtil.renderTileOverlay(graphics, obj, "", config.getStartColor());
- });
+ plugin.getStarts().values().forEach((obj) -> OverlayUtil.renderTileOverlay(graphics, obj, "", config.getStartColor()));
}
// Draw trails
@@ -79,7 +77,7 @@ class HerbiboarOverlay extends Overlay
plugin.getTrails().values().forEach((x) ->
{
int id = x.getId();
- if (shownTrailIds.contains(id) && (finishId > 0 || (currentTrail != null && currentTrail.getTrailId() != id && currentTrail.getTrailId() + 1 != id)))
+ if (shownTrailIds.contains(id) && (finishId > 0 || nextTrail != null && !nextTrail.getFootprintIds().contains(id)))
{
OverlayUtil.renderTileOverlay(graphics, x, "", config.getTrailColor());
}
@@ -87,35 +85,20 @@ class HerbiboarOverlay extends Overlay
}
// Draw trail objects (mushrooms, mud, etc)
- if (config.isObjectShown() && currentTrail != null)
+ if (config.isObjectShown() && !(finishId > 0 || currentGroup == null))
{
- int currentPath = plugin.getCurrentPath();
- WorldPoint[] trailLocs = currentTrail.getObjectLocs(currentPath);
- for (WorldPoint trailLoc : trailLocs)
+ if (plugin.isRuleApplicable())
{
- if (trailLoc == null)
+ WorldPoint correct = Iterables.getLast(plugin.getCurrentPath()).getLocation();
+ TileObject object = plugin.getTrailObjects().get(correct);
+ drawObjectLocation(graphics, object, config.getObjectColor());
+ }
+ else
+ {
+ for (WorldPoint trailLoc : HerbiboarSearchSpot.getGroupLocations(plugin.getCurrentGroup()))
{
- continue;
- }
-
- TileObject object = plugin.getTrailObjects().get(trailLoc);
- if (object != null)
- {
- if (config.showClickBoxes())
- {
- Shape clickbox = object.getClickbox();
- if (clickbox != null)
- {
- graphics.setColor(config.getObjectColor());
- graphics.draw(clickbox);
- graphics.setColor(new Color(255, 0, 255, 20));
- graphics.fill(clickbox);
- }
- }
- else
- {
- OverlayUtil.renderTileOverlay(graphics, object, "", config.getObjectColor());
- }
+ TileObject object = plugin.getTrailObjects().get(trailLoc);
+ drawObjectLocation(graphics, object, config.getObjectColor());
}
}
}
@@ -125,27 +108,35 @@ class HerbiboarOverlay extends Overlay
{
WorldPoint finishLoc = plugin.getEndLocations().get(finishId - 1);
TileObject object = plugin.getTunnels().get(finishLoc);
- if (object != null)
- {
- if (config.showClickBoxes())
- {
- Shape clickbox = object.getClickbox();
- if (clickbox != null)
- {
- Color col = config.getObjectColor();
- graphics.setColor(col);
- graphics.draw(clickbox);
- graphics.setColor(new Color(col.getRed(), col.getGreen(), col.getBlue(), 20));
- graphics.fill(clickbox);
- }
- }
- else
- {
- OverlayUtil.renderTileOverlay(graphics, object, "", config.getTunnelColor());
- }
- }
+ drawObjectLocation(graphics, object, config.getTunnelColor());
}
return null;
}
-}
\ No newline at end of file
+
+ private void drawObjectLocation(Graphics2D graphics, TileObject object, Color color)
+ {
+ if (object == null)
+ {
+ return;
+ }
+
+ if (config.showClickBoxes())
+ {
+ Shape clickbox = object.getClickbox();
+ if (clickbox != null)
+ {
+ Color clickBoxColor = new Color(color.getRed(), color.getGreen(), color.getBlue(), 20);
+
+ graphics.setColor(color);
+ graphics.draw(clickbox);
+ graphics.setColor(clickBoxColor);
+ graphics.fill(clickbox);
+ }
+ }
+ else
+ {
+ OverlayUtil.renderTileOverlay(graphics, object, "", color);
+ }
+ }
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarPlugin.java
index 83e59c19a4..02288075c5 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarPlugin.java
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2017, Tyler
+ * Copyright (c) 2020, dekvall
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,8 +25,10 @@
*/
package net.runelite.client.plugins.herbiboars;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
import com.google.inject.Provides;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -33,14 +36,15 @@ import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import lombok.Getter;
-import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
+import net.runelite.api.GameState;
+import net.runelite.api.MenuAction;
import static net.runelite.api.ObjectID.DRIFTWOOD_30523;
import static net.runelite.api.ObjectID.MUSHROOM_30520;
import static net.runelite.api.ObjectID.ROCK_30519;
import static net.runelite.api.ObjectID.ROCK_30521;
import static net.runelite.api.ObjectID.ROCK_30522;
-import net.runelite.api.Tile;
import net.runelite.api.TileObject;
import net.runelite.api.Varbits;
import net.runelite.api.coords.WorldPoint;
@@ -51,21 +55,27 @@ import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GroundObjectChanged;
import net.runelite.api.events.GroundObjectDespawned;
import net.runelite.api.events.GroundObjectSpawned;
+import net.runelite.api.events.MenuOptionClicked;
import net.runelite.api.events.VarbitChanged;
+import net.runelite.client.callback.ClientThread;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.ui.overlay.OverlayManager;
+import net.runelite.client.util.Text;
+import org.apache.commons.lang3.ArrayUtils;
@PluginDescriptor(
name = "Herbiboar",
description = "Highlight starting rocks, trails, and the objects to search at the end of each trail",
tags = {"herblore", "hunter", "skilling", "overlay"}
)
+@Slf4j
+@Getter
public class HerbiboarPlugin extends Plugin
{
- private static final List END_LOCATIONS = Arrays.asList(
+ private static final List END_LOCATIONS = ImmutableList.of(
new WorldPoint(3693, 3798, 0),
new WorldPoint(3702, 3808, 0),
new WorldPoint(3703, 3826, 0),
@@ -77,7 +87,7 @@ public class HerbiboarPlugin extends Plugin
new WorldPoint(3681, 3863, 0)
);
- private static final List START_OBJECT_IDS = Arrays.asList(
+ private static final Set START_OBJECT_IDS = ImmutableSet.of(
ROCK_30519,
MUSHROOM_30520,
ROCK_30521,
@@ -85,16 +95,19 @@ public class HerbiboarPlugin extends Plugin
DRIFTWOOD_30523
);
- private static final int[] HERBIBOAR_REGIONS = {
+ private static final List HERBIBOAR_REGIONS = ImmutableList.of(
14652,
14651,
14908,
14907
- };
+ );
@Inject
private Client client;
+ @Inject
+ private ClientThread clientThread;
+
@Inject
private OverlayManager overlayManager;
@@ -104,39 +117,43 @@ public class HerbiboarPlugin extends Plugin
@Inject
private HerbiboarMinimapOverlay minimapOverlay;
- @Getter
+ /**
+ * Objects which appear at the beginning of Herbiboar hunting trails
+ */
+ private final Map starts = new HashMap<>();
+ /**
+ * Herbiboar hunting "footstep" trail objects
+ */
+ private final Map trails = new HashMap<>();
+ /**
+ * Objects which trigger next trail (mushrooms, mud, seaweed, etc)
+ */
+ private final Map trailObjects = new HashMap<>();
+ /**
+ * Tunnel where the Herbiboar is hiding at the end of a trail
+ */
+ private final Map tunnels = new HashMap<>();
+ /**
+ * Trail object IDs which should be highlighted
+ */
+ private final Set shownTrails = new HashSet<>();
+ /**
+ * Sequence of herbiboar spots searched along the current trail
+ */
+ private final List currentPath = Lists.newArrayList();
+
private boolean inHerbiboarArea;
-
- @Getter
- private Map trails = new HashMap<>();
-
- @Getter
- private Map tunnels = new HashMap<>();
-
- @Getter
- private Map starts = new HashMap<>();
-
- @Getter
- private Map trailObjects = new HashMap<>();
-
- @Getter
- @Setter
- private Set shownTrails = new HashSet<>();
-
- @Getter
- @Setter
- private HerbiboarTrail currentTrail;
-
- @Getter
- @Setter
- private int currentPath;
-
- @Getter
- @Setter
+ private TrailToSpot nextTrail;
+ private HerbiboarSearchSpot.Group currentGroup;
private int finishId;
+ private boolean started;
+ private WorldPoint startPoint;
+ private HerbiboarStart startSpot;
+ private boolean ruleApplicable;
+
@Provides
- HerbiboarConfig getConfig(ConfigManager configManager)
+ HerbiboarConfig provideConfig(ConfigManager configManager)
{
return configManager.getConfig(HerbiboarConfig.class);
}
@@ -146,7 +163,15 @@ public class HerbiboarPlugin extends Plugin
{
overlayManager.add(overlay);
overlayManager.add(minimapOverlay);
- inHerbiboarArea = checkArea();
+
+ if (client.getGameState() == GameState.LOGGED_IN)
+ {
+ clientThread.invokeLater(() ->
+ {
+ inHerbiboarArea = checkArea();
+ updateTrailData();
+ });
+ }
}
@Override
@@ -154,61 +179,105 @@ public class HerbiboarPlugin extends Plugin
{
overlayManager.remove(overlay);
overlayManager.remove(minimapOverlay);
+ resetTrailData();
+ clearCache();
+ inHerbiboarArea = false;
}
private void updateTrailData()
{
- currentTrail = null;
- currentPath = -1;
+ if (!isInHerbiboarArea())
+ {
+ return;
+ }
+
+ boolean pathActive = false;
+ boolean wasStarted = started;
// Get trail data
- for (HerbiboarTrail trail : HerbiboarTrail.values())
+ for (HerbiboarSearchSpot spot : HerbiboarSearchSpot.values())
{
- int trailId = trail.getTrailId();
- int value = client.getVar(trail.getVarbit());
+ for (TrailToSpot trail : spot.getTrails())
+ {
+ int value = client.getVar(trail.getVarbit());
- if (value > 0)
- {
- shownTrails.add(trailId);
- shownTrails.add(trailId + 1);
- }
- if (value == 1 || value == 2)
- {
- currentTrail = trail;
- currentPath = value;
+ if (value == trail.getValue())
+ {
+ // The trail after you have searched the spot
+ currentGroup = spot.getGroup();
+ nextTrail = trail;
+
+ // You never visit the same spot twice
+ if (!currentPath.contains(spot))
+ {
+ currentPath.add(spot);
+ }
+ }
+ else if (value > 0)
+ {
+ // The current trail
+ shownTrails.addAll(trail.getFootprintIds());
+ pathActive = true;
+ }
}
}
- // Get finish data
finishId = client.getVar(Varbits.HB_FINISH);
- if (finishId > 0 && currentTrail != null)
+
+ // The started varbit doesn't get set until the first spot of the rotation has been searched
+ // so we need to use the current group as an indicator of the rotation being started
+ started = client.getVar(Varbits.HB_STARTED) > 0 || currentGroup != null;
+ boolean finished = !pathActive && started;
+
+ if (!wasStarted && started)
{
- shownTrails.add(currentTrail.getTrailId());
- shownTrails.add(currentTrail.getTrailId() + 1);
- currentTrail = null;
- currentPath = -1;
+ startSpot = HerbiboarStart.from(startPoint);
}
- int started = client.getVar(Varbits.HB_STARTED);
- if (currentPath == -1 && finishId == 0 && started == 0)
+ ruleApplicable = HerbiboarRule.canApplyRule(startSpot, currentPath);
+
+ if (finished)
{
resetTrailData();
}
}
+ @Subscribe
+ public void onMenuOptionClicked(MenuOptionClicked menuOpt)
+ {
+ if (!inHerbiboarArea || started || MenuAction.GAME_OBJECT_FIRST_OPTION != menuOpt.getMenuAction())
+ {
+ return;
+ }
+
+ switch (Text.removeTags(menuOpt.getMenuTarget()))
+ {
+ case "Rock":
+ case "Mushroom":
+ case "Driftwood":
+ startPoint = WorldPoint.fromScene(client, menuOpt.getActionParam(), menuOpt.getWidgetId(), client.getPlane());
+ }
+ }
+
private void resetTrailData()
{
- currentPath = 0;
- currentTrail = null;
- finishId = 0;
+ log.debug("Reset trail data");
shownTrails.clear();
+ currentPath.clear();
+ nextTrail = null;
+ currentGroup = null;
+ finishId = 0;
+ started = false;
+ startPoint = null;
+ startSpot = null;
+ ruleApplicable = false;
}
private void clearCache()
{
starts.clear();
- trailObjects.clear();
trails.clear();
+ trailObjects.clear();
tunnels.clear();
}
@@ -233,57 +302,55 @@ public class HerbiboarPlugin extends Plugin
@Subscribe
public void onVarbitChanged(VarbitChanged event)
{
- if (isInHerbiboarArea())
- {
- updateTrailData();
- }
+ updateTrailData();
}
@Subscribe
public void onGameObjectSpawned(GameObjectSpawned event)
{
- onGameObject(event.getTile(), null, event.getGameObject());
+ onTileObject(null, event.getGameObject());
}
@Subscribe
public void onGameObjectChanged(GameObjectChanged event)
{
- onGameObject(event.getTile(), event.getPrevious(), event.getGameObject());
+ onTileObject(event.getPrevious(), event.getGameObject());
}
@Subscribe
public void onGameObjectDespawned(GameObjectDespawned event)
{
- onGameObject(event.getTile(), event.getGameObject(), null);
+ onTileObject(event.getGameObject(), null);
}
@Subscribe
public void onGroundObjectSpawned(GroundObjectSpawned event)
{
- onGroundObject(event.getTile(), null, event.getGroundObject());
+ onTileObject(null, event.getGroundObject());
}
@Subscribe
public void onGroundObjectChanged(GroundObjectChanged event)
{
- onGroundObject(event.getTile(), event.getPrevious(), event.getGroundObject());
+ onTileObject(event.getPrevious(), event.getGroundObject());
}
@Subscribe
public void onGroundObjectDespawned(GroundObjectDespawned event)
{
- onGroundObject(event.getTile(), event.getGroundObject(), null);
+ onTileObject(event.getGroundObject(), null);
}
- // Store relevant GameObjects (starts, objects used to trigger next trails, and some tunnels)
- private void onGameObject(Tile tile, TileObject oldObject, TileObject newObject)
+ // Store relevant GameObjects (starts, tracks on trails, objects used to trigger next trails, and tunnels)
+ private void onTileObject(TileObject oldObject, TileObject newObject)
{
if (oldObject != null)
{
WorldPoint oldLocation = oldObject.getWorldLocation();
+ starts.remove(oldLocation);
+ trails.remove(oldLocation);
trailObjects.remove(oldLocation);
tunnels.remove(oldLocation);
- starts.remove(oldLocation);
}
if (newObject == null)
@@ -298,8 +365,15 @@ public class HerbiboarPlugin extends Plugin
return;
}
+ // Trails
+ if (HerbiboarSearchSpot.isTrail(newObject.getId()))
+ {
+ trails.put(newObject.getWorldLocation(), newObject);
+ return;
+ }
+
// GameObject to trigger next trail (mushrooms, mud, seaweed, etc)
- if (HerbiboarTrail.getAllObjectLocs().contains(newObject.getWorldLocation()))
+ if (HerbiboarSearchSpot.isSearchSpot(newObject.getWorldLocation()))
{
trailObjects.put(newObject.getWorldLocation(), newObject);
return;
@@ -312,43 +386,20 @@ public class HerbiboarPlugin extends Plugin
}
}
- // Store relevant GroundObjects (tracks on trails, and some tunnels)
- private void onGroundObject(Tile tile, TileObject oldObject, TileObject newObject)
- {
- if (oldObject != null)
- {
- WorldPoint oldLocation = oldObject.getWorldLocation();
- trails.remove(oldLocation);
- tunnels.remove(oldLocation);
- }
-
- if (newObject == null)
- {
- return;
- }
-
- //Trails
- if (HerbiboarTrail.getTrailIds().contains(newObject.getId()))
- {
- trails.put(newObject.getWorldLocation(), newObject);
- return;
- }
-
- //Herbiboar tunnel
- if (END_LOCATIONS.contains(newObject.getWorldLocation()))
- {
- tunnels.put(newObject.getWorldLocation(), newObject);
- }
- }
-
private boolean checkArea()
{
- return client.getMapRegions() != null && Arrays.stream(client.getMapRegions())
- .filter(x -> Arrays.stream(HERBIBOAR_REGIONS).anyMatch(y -> y == x))
- .toArray().length > 0;
+ final int[] mapRegions = client.getMapRegions();
+ for (int region : HERBIBOAR_REGIONS)
+ {
+ if (ArrayUtils.contains(mapRegions, region))
+ {
+ return true;
+ }
+ }
+ return false;
}
- public List getEndLocations()
+ List getEndLocations()
{
return END_LOCATIONS;
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarRule.java b/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarRule.java
new file mode 100644
index 0000000000..b904e05d3a
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarRule.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2020, dekvall
+ * Copyright (c) 2020, Jordan
+ * 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.herbiboars;
+
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+enum HerbiboarRule
+{
+ A_SOUTH(HerbiboarSearchSpot.Group.A, HerbiboarStart.MIDDLE),
+ C_WEST(HerbiboarSearchSpot.Group.C, HerbiboarStart.MIDDLE),
+ D_WEST_1(HerbiboarSearchSpot.Group.D, HerbiboarStart.MIDDLE),
+ D_WEST_2(HerbiboarSearchSpot.Group.D, HerbiboarSearchSpot.Group.C),
+ E_NORTH(HerbiboarSearchSpot.Group.E, HerbiboarSearchSpot.Group.A),
+ F_EAST(HerbiboarSearchSpot.Group.F, HerbiboarSearchSpot.Group.G),
+ G_NORTH(HerbiboarSearchSpot.Group.G, HerbiboarSearchSpot.Group.F),
+ H_NORTH(HerbiboarSearchSpot.Group.H, HerbiboarSearchSpot.Group.D),
+ H_EAST(HerbiboarSearchSpot.Group.H, HerbiboarStart.DRIFTWOOD),
+ I_EAST(HerbiboarSearchSpot.Group.I, HerbiboarStart.LEPRECHAUN),
+ I_SOUTH_1(HerbiboarSearchSpot.Group.I, HerbiboarStart.GHOST_MUSHROOM),
+ I_SOUTH_2(HerbiboarSearchSpot.Group.I, HerbiboarStart.CAMP_ENTRANCE),
+ I_WEST(HerbiboarSearchSpot.Group.I, HerbiboarSearchSpot.Group.E),
+ ;
+
+ private final HerbiboarSearchSpot.Group to;
+ private final HerbiboarStart fromStart;
+ private final HerbiboarSearchSpot.Group fromGroup;
+
+ HerbiboarRule(HerbiboarSearchSpot.Group to, HerbiboarSearchSpot.Group from)
+ {
+ this(to, null, from);
+ }
+
+ HerbiboarRule(HerbiboarSearchSpot.Group to, HerbiboarStart fromStart)
+ {
+ this(to, fromStart, null);
+ }
+
+ /**
+ * Returns whether the next {@link HerbiboarSearchSpot} can be deterministically selected based on the starting
+ * location and the path taken so far, based on the rules defined on the OSRS wiki.
+ *
+ * {@see https://oldschool.runescape.wiki/w/Herbiboar#Guaranteed_tracks}
+ *
+ * @param start Herbiboar's starting spot where the tracking path begins
+ * @param currentPath A list of {@link HerbiboarSearchSpot}s which have been searched thus far, and the next one to search
+ * @return {@code true} if a rule can be applied, {@code false} otherwise
+ */
+ static boolean canApplyRule(HerbiboarStart start, List currentPath)
+ {
+ if (start == null || currentPath.isEmpty())
+ {
+ return false;
+ }
+
+ int lastIndex = currentPath.size() - 1;
+ HerbiboarSearchSpot.Group goingTo = currentPath.get(lastIndex).getGroup();
+
+ for (HerbiboarRule rule : values())
+ {
+ if (lastIndex > 0 && rule.matches(currentPath.get(lastIndex - 1).getGroup(), goingTo)
+ || lastIndex == 0 && rule.matches(start, goingTo))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ boolean matches(HerbiboarStart from, HerbiboarSearchSpot.Group to)
+ {
+ return this.matches(from, null, to);
+ }
+
+ boolean matches(HerbiboarSearchSpot.Group from, HerbiboarSearchSpot.Group to)
+ {
+ return this.matches(null, from, to);
+ }
+
+ boolean matches(HerbiboarStart fromStart, HerbiboarSearchSpot.Group fromGroup, HerbiboarSearchSpot.Group to)
+ {
+ return this.to == to
+ && (fromStart != null && this.fromStart == fromStart || fromGroup != null && this.fromGroup == fromGroup);
+ }
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarSearchSpot.java b/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarSearchSpot.java
new file mode 100644
index 0000000000..9e324f5a2b
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarSearchSpot.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2020, dekvall
+ * Copyright (c) 2020, Jordan
+ * 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.herbiboars;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import lombok.Getter;
+import static net.runelite.api.NullObjectID.*;
+import net.runelite.api.Varbits;
+import net.runelite.api.coords.WorldPoint;
+
+@Getter
+enum HerbiboarSearchSpot
+{
+ // Wiki A location
+ A_MUSHROOM(Group.A, new WorldPoint(3670, 3889, 0),
+ new TrailToSpot(Varbits.HB_TRAIL_31318, 1, NULL_31318),
+ new TrailToSpot(Varbits.HB_TRAIL_31321, 1, NULL_31321)),
+ A_PATCH(Group.A, new WorldPoint(3672, 3890, 0),
+ new TrailToSpot(Varbits.HB_TRAIL_31306, 2, NULL_31306)),
+
+ // Wiki B location
+ B_SEAWEED(Group.B, new WorldPoint(3728, 3893, 0),
+ new TrailToSpot(Varbits.HB_TRAIL_31315, 2, NULL_31315),
+ new TrailToSpot(Varbits.HB_TRAIL_31318, 2, NULL_31318),
+ new TrailToSpot(Varbits.HB_TRAIL_31336, 1, NULL_31336),
+ new TrailToSpot(Varbits.HB_TRAIL_31339, 1, NULL_31339)),
+
+ // Wiki C location
+ C_MUSHROOM(Group.C, new WorldPoint(3697, 3875, 0),
+ new TrailToSpot(Varbits.HB_TRAIL_31303, 2, NULL_31303)),
+ C_PATCH(Group.C, new WorldPoint(3699, 3875, 0),
+ new TrailToSpot(Varbits.HB_TRAIL_31312, 1, NULL_31312),
+ new TrailToSpot(Varbits.HB_TRAIL_31315, 1, NULL_31315)),
+
+ // Wiki D location
+ D_PATCH(Group.D, new WorldPoint(3708, 3876, 0),
+ new TrailToSpot(Varbits.HB_TRAIL_31330, 1, NULL_31330),
+ new TrailToSpot(Varbits.HB_TRAIL_31333, 1, NULL_31333)),
+ D_SEAWEED(Group.D, new WorldPoint(3710, 3877, 0),
+ new TrailToSpot(Varbits.HB_TRAIL_31312, 2, NULL_31312),
+ new TrailToSpot(Varbits.HB_TRAIL_31339, 2, NULL_31339)),
+
+ // Wiki E location
+ E_MUSHROOM(Group.E, new WorldPoint(3668, 3865, 0),
+ new TrailToSpot(Varbits.HB_TRAIL_31342, 1, NULL_31342),
+ new TrailToSpot(Varbits.HB_TRAIL_31345, 1, NULL_31345)),
+ E_PATCH(Group.E, new WorldPoint(3667, 3862, 0),
+ new TrailToSpot(Varbits.HB_TRAIL_31321, 2, NULL_31321)),
+
+ // Wiki F location
+ F_MUSHROOM(Group.F, new WorldPoint(3681, 3860, 0),
+ new TrailToSpot(Varbits.HB_TRAIL_31324, 1, NULL_31324),
+ new TrailToSpot(Varbits.HB_TRAIL_31327, 1, NULL_31327),
+ new TrailToSpot(Varbits.HB_TRAIL_31342, 2, NULL_31342)),
+ F_PATCH(Group.F, new WorldPoint(3681, 3859, 0),
+ new TrailToSpot(Varbits.HB_TRAIL_31309, 2, NULL_31309)),
+
+ // Wiki G location
+ G_MUSHROOM(Group.G, new WorldPoint(3694, 3847, 0),
+ new TrailToSpot(Varbits.HB_TRAIL_31333, 2, NULL_31333),
+ new TrailToSpot(Varbits.HB_TRAIL_31354, 1, NULL_31354)),
+ G_PATCH(Group.G, new WorldPoint(3698, 3847, 0),
+ new TrailToSpot(Varbits.HB_TRAIL_31327, 2, NULL_31327)),
+
+ // Wiki H location
+ H_SEAWEED_EAST(Group.H, new WorldPoint(3715, 3851, 0),
+ new TrailToSpot(Varbits.HB_TRAIL_31357, 1, NULL_31357),
+ new TrailToSpot(Varbits.HB_TRAIL_31360, 1, NULL_31360)),
+ H_SEAWEED_WEST(Group.H, new WorldPoint(3713, 3850, 0),
+ new TrailToSpot(Varbits.HB_TRAIL_31330, 2, NULL_31330),
+ new TrailToSpot(Varbits.HB_TRAIL_31363, 1, NULL_31363)),
+
+ // Wiki I location
+ I_MUSHROOM(Group.I, new WorldPoint(3680, 3838, 0),
+ new TrailToSpot(Varbits.HB_TRAIL_31348, 1, NULL_31348),
+ new TrailToSpot(Varbits.HB_TRAIL_31351, 1, NULL_31351)),
+ I_PATCH(Group.I, new WorldPoint(3680, 3836, 0),
+ new TrailToSpot(Varbits.HB_TRAIL_31324, 2, NULL_31324),
+ new TrailToSpot(Varbits.HB_TRAIL_31345, 2, NULL_31345)),
+
+ // Wiki J location
+ J_PATCH(Group.J, new WorldPoint(3713, 3840, 0),
+ new TrailToSpot(Varbits.HB_TRAIL_31357, 2, NULL_31357),
+ new TrailToSpot(Varbits.HB_TRAIL_31372, 1, NULL_31372)),
+
+ // Wiki K location
+ K_PATCH(Group.K, new WorldPoint(3706, 3811, 0),
+ new TrailToSpot(Varbits.HB_TRAIL_31348, 2, NULL_31348),
+ new TrailToSpot(Varbits.HB_TRAIL_31366, 1, NULL_31366),
+ new TrailToSpot(Varbits.HB_TRAIL_31369, 1, NULL_31369)),
+ ;
+
+ private static final ImmutableMultimap GROUPS;
+ private static final Set SPOTS;
+ private static final Set TRAILS;
+
+ static
+ {
+ ImmutableMultimap.Builder groupBuilder = new ImmutableMultimap.Builder<>();
+ ImmutableSet.Builder spotBuilder = new ImmutableSet.Builder<>();
+ ImmutableSet.Builder trailBuilder = new ImmutableSet.Builder<>();
+
+ for (HerbiboarSearchSpot spot : values())
+ {
+ groupBuilder.put(spot.getGroup(), spot);
+ spotBuilder.add(spot.getLocation());
+
+ for (TrailToSpot trail : spot.getTrails())
+ {
+ trailBuilder.addAll(trail.getFootprintIds());
+ }
+ }
+
+ GROUPS = groupBuilder.build();
+ SPOTS = spotBuilder.build();
+ TRAILS = trailBuilder.build();
+ }
+
+ private final Group group;
+ private final WorldPoint location;
+ private final List trails;
+
+ HerbiboarSearchSpot(Group group, WorldPoint location, TrailToSpot... trails)
+ {
+ this.group = group;
+ this.location = location;
+ this.trails = ImmutableList.copyOf(trails);
+ }
+
+ /**
+ * Spots are placed in groups of two
+ */
+ enum Group
+ {
+ A, B, C, D, E, F, G, H, I, J, K
+ }
+
+ static boolean isTrail(int id)
+ {
+ return TRAILS.contains(id);
+ }
+
+ static boolean isSearchSpot(WorldPoint location)
+ {
+ return SPOTS.contains(location);
+ }
+
+ static List getGroupLocations(Group group)
+ {
+ return GROUPS.get(group).stream().map(HerbiboarSearchSpot::getLocation).collect(Collectors.toList());
+ }
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarStart.java b/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarStart.java
new file mode 100644
index 0000000000..3aa6485450
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarStart.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2020, dekvall
+ * 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.herbiboars;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import net.runelite.api.coords.WorldPoint;
+
+@Getter
+@RequiredArgsConstructor
+enum HerbiboarStart
+{
+ MIDDLE(new WorldPoint(3686, 3870, 0)),
+ LEPRECHAUN(new WorldPoint(3705, 3830, 0)),
+ CAMP_ENTRANCE(new WorldPoint(3704, 3810, 0)),
+ GHOST_MUSHROOM(new WorldPoint(3695, 3800, 0)),
+ DRIFTWOOD(new WorldPoint(3751, 3850, 0)),
+ ;
+
+ private final WorldPoint location;
+
+ static HerbiboarStart from(WorldPoint location)
+ {
+ for (final HerbiboarStart start : values())
+ {
+ if (start.getLocation().equals(location))
+ {
+ return start;
+ }
+ }
+ return null;
+ }
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarTrail.java b/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarTrail.java
deleted file mode 100644
index 6f1dba41fb..0000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/HerbiboarTrail.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (c) 2017, Tyler
- * 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.herbiboars;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import net.runelite.api.Varbits;
-import net.runelite.api.coords.WorldPoint;
-
-//Location of GameObjects which show TRAIL_xxxxx when used
-@AllArgsConstructor
-public enum HerbiboarTrail
-{
- TRAIL_31303(31303, Varbits.HB_TRAIL_31303, null, new WorldPoint(3697, 3875, 0), null, new WorldPoint(3699, 3875, 0)),
- TRAIL_31306(31306, Varbits.HB_TRAIL_31306, null, new WorldPoint(3672, 3890, 0), null, new WorldPoint(3670, 3889, 0)),
- TRAIL_31309(31309, Varbits.HB_TRAIL_31309, null, new WorldPoint(3681, 3859, 0), null, new WorldPoint(3681, 3860, 0)),
- TRAIL_31312(31312, Varbits.HB_TRAIL_31312, new WorldPoint(3699, 3875, 0), new WorldPoint(3710, 3877, 0), new WorldPoint(3697, 3875, 0), new WorldPoint(3708, 3876, 0)),
- TRAIL_31315(31315, Varbits.HB_TRAIL_31315, new WorldPoint(3699, 3875, 0), new WorldPoint(3728, 3893, 0), new WorldPoint(3697, 3875, 0), null),
- TRAIL_31318(31318, Varbits.HB_TRAIL_31318, new WorldPoint(3670, 3889, 0), new WorldPoint(3728, 3893, 0), new WorldPoint(3672, 3890, 0), null),
- TRAIL_31321(31321, Varbits.HB_TRAIL_31321, new WorldPoint(3670, 3889, 0), new WorldPoint(3667, 3862, 0), new WorldPoint(3672, 3890, 0), new WorldPoint(3668, 3865, 0)),
- TRAIL_31324(31324, Varbits.HB_TRAIL_31324, new WorldPoint(3681, 3860, 0), new WorldPoint(3680, 3836, 0), new WorldPoint(3681, 3859, 0), new WorldPoint(3680, 3838, 0)),
- TRAIL_31327(31327, Varbits.HB_TRAIL_31327, new WorldPoint(3681, 3860, 0), new WorldPoint(3698, 3847, 0), new WorldPoint(3681, 3859, 0), new WorldPoint(3694, 3847, 0)),
- TRAIL_31330(31330, Varbits.HB_TRAIL_31330, new WorldPoint(3708, 3876, 0), new WorldPoint(3713, 3850, 0), new WorldPoint(3710, 3877, 0), new WorldPoint(3715, 3851, 0)),
- TRAIL_31333(31333, Varbits.HB_TRAIL_31333, new WorldPoint(3708, 3876, 0), new WorldPoint(3694, 3847, 0), new WorldPoint(3710, 3877, 0), new WorldPoint(3698, 3847, 0)),
- TRAIL_31336(31336, Varbits.HB_TRAIL_31336, new WorldPoint(3728, 3893, 0), null, null, null),
- TRAIL_31339(31339, Varbits.HB_TRAIL_31339, new WorldPoint(3728, 3893, 0), new WorldPoint(3710, 3877, 0), null, new WorldPoint(3708, 3876, 0)),
- TRAIL_31342(31342, Varbits.HB_TRAIL_31342, new WorldPoint(3668, 3865, 0), new WorldPoint(3681, 3860, 0), new WorldPoint(3667, 3862, 0), new WorldPoint(3681, 3859, 0)),
- TRAIL_31345(31345, Varbits.HB_TRAIL_31345, new WorldPoint(3668, 3865, 0), new WorldPoint(3680, 3836, 0), new WorldPoint(3667, 3862, 0), new WorldPoint(3680, 3838, 0)),
- TRAIL_31348(31348, Varbits.HB_TRAIL_31348, new WorldPoint(3680, 3838, 0), new WorldPoint(3706, 3811, 0), new WorldPoint(3680, 3836, 0), null),
- TRAIL_31351(31351, Varbits.HB_TRAIL_31351, new WorldPoint(3680, 3838, 0), null, new WorldPoint(3680, 3836, 0), null),
- TRAIL_31354(31354, Varbits.HB_TRAIL_31354, new WorldPoint(3694, 3847, 0), null, new WorldPoint(3698, 3847, 0), null),
- TRAIL_31357(31357, Varbits.HB_TRAIL_31357, new WorldPoint(3715, 3851, 0), new WorldPoint(3713, 3840, 0), new WorldPoint(3713, 3850, 0), null),
- TRAIL_31360(31360, Varbits.HB_TRAIL_31360, new WorldPoint(3715, 3851, 0), null, new WorldPoint(3713, 3850, 0), null),
- TRAIL_31363(31363, Varbits.HB_TRAIL_31363, new WorldPoint(3713, 3850, 0), null, new WorldPoint(3715, 3851, 0), null),
- TRAIL_31366(31366, Varbits.HB_TRAIL_31366, null, null, null, null),
- TRAIL_31369(31369, Varbits.HB_TRAIL_31369, new WorldPoint(3706, 3811, 0), null, null, null),
- TRAIL_31372(31372, Varbits.HB_TRAIL_31372, new WorldPoint(3713, 3840, 0), null, null, null);
-
- @Getter
- private final int trailId;
- @Getter
- private final Varbits varbit;
- private final WorldPoint objectLoc1;
- private final WorldPoint objectLoc2;
- private final WorldPoint objectLoc3;
- private final WorldPoint objectLoc4;
-
- @Getter
- private static Set trailIds = new HashSet<>();
- @Getter
- private static Set allObjectLocs = new HashSet<>();
-
- static
- {
- for (HerbiboarTrail trail : values())
- {
- trailIds.add(trail.trailId);
- trailIds.add(trail.trailId + 1);
-
- allObjectLocs.addAll(Arrays.asList(trail.getObjectLocs(1)));
- allObjectLocs.addAll(Arrays.asList(trail.getObjectLocs(2)));
- }
- }
-
- public WorldPoint[] getObjectLocs(int varbitValue)
- {
- switch (varbitValue)
- {
- case 1:
- return new WorldPoint[]{objectLoc1, objectLoc3};
- case 2:
- return new WorldPoint[]{objectLoc2, objectLoc4};
- case 0:
- default:
- return new WorldPoint[]{};
- }
- }
-
- @Override
- public String toString()
- {
- return String.format("trailId=%s obj1=%s obj2=%s obj3=%s obj4=%s", trailId, objectLoc1, objectLoc2, objectLoc3, objectLoc4);
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/TrailToSpot.java b/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/TrailToSpot.java
new file mode 100644
index 0000000000..17c0b923cb
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/herbiboars/TrailToSpot.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020, dekvall
+ * Copyright (c) 2020, Jordan
+ * 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.herbiboars;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Set;
+import lombok.Value;
+import net.runelite.api.Varbits;
+
+/**
+ * A representation of a trail of footsteps which appears when hunting for the Herbiboar.
+ */
+@Value
+class TrailToSpot
+{
+ /**
+ * The Varbit associated with the trail. When inactive, this Varbit's value should be less than
+ * {@link TrailToSpot#getValue()}. When this trail appears after searching a spot, this Varbit's value should be
+ * equal to that of {@link TrailToSpot#getValue()}. Once the next object along the trail has been searched, this
+ * Varbit's value will be greater than that of {@link TrailToSpot#getValue()}.
+ */
+ private final Varbits varbit;
+ /**
+ * The cutoff reference value to compare against the value of {@link TrailToSpot#getVarbit()} to determine its state
+ * along the current trail.
+ */
+ private final int value;
+ /**
+ * The object ID of the footprints which appear when the trail is made visible.
+ */
+ private final int footprint;
+
+ Set getFootprintIds()
+ {
+ return ImmutableSet.of(footprint, footprint + 1);
+ }
+}