# Conflicts:
#	runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java
#	runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java
#	runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/EmoteClue.java
#	runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/MapClue.java
#	runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java
#	runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java
#	runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java
#	runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java
#	runelite-client/src/main/java/net/runelite/client/plugins/opponentinfo/HitpointsDisplayStyle.java
#	runelite-client/src/main/java/net/runelite/client/plugins/questlist/QuestListPlugin.java
#	runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTracker.java
#	runelite-client/src/main/java/net/runelite/client/ui/overlay/components/ProgressBarComponent.java
This commit is contained in:
Ganom
2019-05-30 13:26:28 -04:00
53 changed files with 2088 additions and 1856 deletions

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2018, Magic fTail
* Copyright (c) 2019, osrs-music-map <osrs-music-map@users.noreply.github.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -63,4 +64,26 @@ public interface ChatFilterConfig extends Config
{
return "";
}
@ConfigItem(
keyName = "filterFriends",
name = "Filter Friends",
description = "Filter your friends' messages",
position = 4
)
default boolean filterFriends()
{
return false;
}
@ConfigItem(
keyName = "filterClan",
name = "Filter Clan Chat Members",
description = "Filter your clan chat members' messages",
position = 5
)
default boolean filterClan()
{
return false;
}
}

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2018, Magic fTail
* Copyright (c) 2019, osrs-music-map <osrs-music-map@users.noreply.github.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -35,6 +36,7 @@ import java.util.regex.PatternSyntaxException;
import javax.inject.Inject;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.MessageNode;
import net.runelite.api.Player;
import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.OverheadTextChanged;
@@ -97,7 +99,10 @@ public class ChatFilterPlugin extends Plugin
int[] intStack = client.getIntStack();
int intStackSize = client.getIntStackSize();
ChatMessageType chatMessageType = ChatMessageType.of(intStack[intStackSize - 1]);
int messageType = intStack[intStackSize - 2];
int messageId = intStack[intStackSize - 1];
ChatMessageType chatMessageType = ChatMessageType.of(messageType);
// Only filter public chat and private messages
switch (chatMessageType)
@@ -114,6 +119,13 @@ public class ChatFilterPlugin extends Plugin
return;
}
MessageNode messageNode = (MessageNode) client.getMessages().get(messageId);
String name = messageNode.getName();
if (!shouldFilterPlayerMessage(name))
{
return;
}
String[] stringStack = client.getStringStack();
int stringStackSize = client.getStringStackSize();
@@ -123,7 +135,7 @@ public class ChatFilterPlugin extends Plugin
if (censoredMessage == null)
{
// Block the message
intStack[intStackSize - 2] = 0;
intStack[intStackSize - 3] = 0;
}
else
{
@@ -135,7 +147,7 @@ public class ChatFilterPlugin extends Plugin
@Subscribe
public void onOverheadTextChanged(OverheadTextChanged event)
{
if (!(event.getActor() instanceof Player))
if (!(event.getActor() instanceof Player) || !shouldFilterPlayerMessage(event.getActor().getName()))
{
return;
}
@@ -150,6 +162,14 @@ public class ChatFilterPlugin extends Plugin
event.getActor().setOverheadText(message);
}
boolean shouldFilterPlayerMessage(String playerName)
{
boolean isMessageFromSelf = playerName.equals(client.getLocalPlayer().getName());
return !isMessageFromSelf &&
(config.filterFriends() || !client.isFriended(playerName, false)) &&
(config.filterClan() || !client.isClanMember(playerName));
}
String censorMessage(final String message)
{
String strippedMessage = jagexPrintableCharMatcher.retainFrom(message)

View File

@@ -126,4 +126,15 @@ public interface ClanChatConfig extends Config
{
return false;
}
@ConfigItem(
keyName = "clanTabChat",
name = "Clan Tab Chat",
description = "Allows clan chat without prepending '/' to messages when on clan tab",
position = 8
)
default boolean clanTabChat()
{
return false;
}
}

View File

@@ -61,6 +61,7 @@ import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.PlayerDespawned;
import net.runelite.api.events.PlayerSpawned;
import net.runelite.api.events.ScriptCallbackEvent;
import net.runelite.api.events.VarClientStrChanged;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo;
@@ -498,6 +499,19 @@ public class ClanChatPlugin extends Plugin
activityBuffer.clear();
}
@Subscribe
public void onScriptCallbackEvent(ScriptCallbackEvent scriptCallbackEvent)
{
if (!scriptCallbackEvent.getEventName().equalsIgnoreCase("clanchatInput"))
{
return;
}
final int[] intStack = client.getIntStack();
final int size = client.getIntStackSize();
intStack[size - 1] = config.clanTabChat() ? 1 : 0;
}
int getClanAmount()
{
return clanMembers.size();

View File

@@ -66,7 +66,9 @@ import net.runelite.api.events.ItemContainerChanged;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.api.events.NpcDespawned;
import net.runelite.api.events.NpcSpawned;
import net.runelite.api.events.WidgetLoaded;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetID;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
@@ -74,6 +76,7 @@ import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.cluescrolls.clues.AnagramClue;
import net.runelite.client.plugins.cluescrolls.clues.BeginnerMapClue;
import net.runelite.client.plugins.cluescrolls.clues.CipherClue;
import net.runelite.client.plugins.cluescrolls.clues.ClueScroll;
import net.runelite.client.plugins.cluescrolls.clues.CoordinateClue;
@@ -386,6 +389,18 @@ public class ClueScrollPlugin extends Plugin
updateClue(findClueScroll());
}
@Subscribe
public void onWidgetLoaded(WidgetLoaded event)
{
if (event.getGroupId() < WidgetID.BEGINNER_CLUE_MAP_CHAMPIONS_GUILD
|| event.getGroupId() > WidgetID.BEGINNER_CLUE_MAP_WIZARDS_TOWER)
{
return;
}
updateClue(BeginnerMapClue.forWidgetID(event.getGroupId()));
}
public BufferedImage getClueScrollImage()
{
return itemManager.getImage(ItemID.CLUE_SCROLL_MASTER);

View File

@@ -0,0 +1,66 @@
/*
* Copyright (c) 2019, Hydrox6 <ikada@protonmail.ch>
* 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.cluescrolls.clues;
import com.google.common.collect.ImmutableList;
import lombok.Getter;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.widgets.WidgetID;
@Getter
public class BeginnerMapClue extends MapClue implements LocationClueScroll
{
private static final ImmutableList<BeginnerMapClue> CLUES = ImmutableList.of(
new BeginnerMapClue(WidgetID.BEGINNER_CLUE_MAP_CHAMPIONS_GUILD, new WorldPoint(3166, 3361, 0), "South West of the Champion's Guild"),
new BeginnerMapClue(WidgetID.BEGINNER_CLUE_MAP_VARROCK_EAST_MINE, new WorldPoint(3290, 3374, 0), "Outside Varrock East Mine"),
new BeginnerMapClue(WidgetID.BEGINNER_CLUE_MAP_DRAYNOR, new WorldPoint(3093, 3226, 0), "South of Draynor Village Bank"),
new BeginnerMapClue(WidgetID.BEGINNER_CLUE_MAP_NORTH_OF_FALADOR, new WorldPoint(3043, 3398, 0), "In the standing stones north of Falador"),
new BeginnerMapClue(WidgetID.BEGINNER_CLUE_MAP_WIZARDS_TOWER, new WorldPoint(3110, 3152, 0), "On the south side of the Wizard's Tower")
);
private final int widgetGroupID;
private BeginnerMapClue(int widgetGroupID, WorldPoint location, String description)
{
super(-1, location, description);
this.widgetGroupID = widgetGroupID;
setRequiresSpade(true);
}
// Beginner Map Clues all use the same ItemID, but the WidgetID used to display them is unique
public static BeginnerMapClue forWidgetID(int widgetGroupID)
{
for (BeginnerMapClue clue : CLUES)
{
if (clue.widgetGroupID == widgetGroupID)
{
return clue;
}
}
return null;
}
}

View File

@@ -148,7 +148,7 @@ public class CoordinateClue extends ClueScroll implements TextClueScroll, Locati
.put(new WorldPoint(3573, 3425, 0), "North of Dessous's tomb from Desert Treasure.")
.put(new WorldPoint(3828, 2848, 0), "East of Harmony Island.")
.put(new WorldPoint(3225, 2838, 0), "South of Desert Treasure pyramid.")
.put(new WorldPoint(1773, 3510, 0), "Between magic trees South of Tithe Farm.")
.put(new WorldPoint(1773, 3510, 0), "Ruins north of the Hosidius mine.")
.put(new WorldPoint(3822, 3562, 0), "North-east of Dragontooth Island.")
.put(new WorldPoint(3603, 3564, 0), "North of the wrecked ship, outside of Port Phasmatys.")
.put(new WorldPoint(2936, 2721, 0), "Eastern shore of Crash Island.")

View File

@@ -32,56 +32,7 @@ import lombok.Getter;
import net.runelite.api.NPC;
import static net.runelite.api.NullObjectID.NULL_1293;
import net.runelite.api.ObjectComposition;
import static net.runelite.api.ObjectID.BOOKCASE_12539;
import static net.runelite.api.ObjectID.BOOKCASE_380;
import static net.runelite.api.ObjectID.BOOKCASE_394;
import static net.runelite.api.ObjectID.BOOKCASE_9523;
import static net.runelite.api.ObjectID.BOXES;
import static net.runelite.api.ObjectID.BOXES_360;
import static net.runelite.api.ObjectID.BOXES_361;
import static net.runelite.api.ObjectID.BOXES_3686;
import static net.runelite.api.ObjectID.BOXES_5111;
import static net.runelite.api.ObjectID.BOXES_6176;
import static net.runelite.api.ObjectID.BUCKET_9568;
import static net.runelite.api.ObjectID.BUSH_2357;
import static net.runelite.api.ObjectID.CLOSED_CHEST_25592;
import static net.runelite.api.ObjectID.CLOSED_CHEST_375;
import static net.runelite.api.ObjectID.CLOSED_CHEST_5108;
import static net.runelite.api.ObjectID.COFFIN;
import static net.runelite.api.ObjectID.CRATES_11600;
import static net.runelite.api.ObjectID.CRATES_24088;
import static net.runelite.api.ObjectID.CRATE_11485;
import static net.runelite.api.ObjectID.CRATE_12963;
import static net.runelite.api.ObjectID.CRATE_14934;
import static net.runelite.api.ObjectID.CRATE_18204;
import static net.runelite.api.ObjectID.CRATE_18506;
import static net.runelite.api.ObjectID.CRATE_18889;
import static net.runelite.api.ObjectID.CRATE_24344;
import static net.runelite.api.ObjectID.CRATE_25775;
import static net.runelite.api.ObjectID.CRATE_27532;
import static net.runelite.api.ObjectID.CRATE_27533;
import static net.runelite.api.ObjectID.CRATE_354;
import static net.runelite.api.ObjectID.CRATE_355;
import static net.runelite.api.ObjectID.CRATE_356;
import static net.runelite.api.ObjectID.CRATE_357;
import static net.runelite.api.ObjectID.CRATE_358;
import static net.runelite.api.ObjectID.CRATE_366;
import static net.runelite.api.ObjectID.CRATE_5106;
import static net.runelite.api.ObjectID.CRATE_5107;
import static net.runelite.api.ObjectID.CRATE_5113;
import static net.runelite.api.ObjectID.CRATE_9534;
import static net.runelite.api.ObjectID.DRAWERS;
import static net.runelite.api.ObjectID.DRAWERS_25766;
import static net.runelite.api.ObjectID.DRAWERS_350;
import static net.runelite.api.ObjectID.DRAWERS_352;
import static net.runelite.api.ObjectID.DRAWERS_5618;
import static net.runelite.api.ObjectID.DRAWERS_7194;
import static net.runelite.api.ObjectID.HAYSTACK;
import static net.runelite.api.ObjectID.JUNA;
import static net.runelite.api.ObjectID.MINE_CART_6045;
import static net.runelite.api.ObjectID.STONES_26633;
import static net.runelite.api.ObjectID.WARDROBE_5622;
import static net.runelite.api.ObjectID.WHEELBARROW_9625;
import static net.runelite.api.ObjectID.*;
import net.runelite.api.TileObject;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
@@ -347,7 +298,7 @@ public class CrypticClue extends ClueScroll implements TextClueScroll, NpcClueSc
new CrypticClue("I would make a chemistry joke, but I'm afraid I wouldn't get a reaction.", "Chemist", new WorldPoint(2932, 3212, 0), "Talk to the Chemist in Rimmington"),
new CrypticClue("Show this to Hazelmere.", "Hazelmere", new WorldPoint(2677, 3088, 1), "Hazelmere is found upstairs on the island located just east of Yanille."),
new CrypticClue("Does one really need a fire to stay warm here?", new WorldPoint(3816, 3810, 0), "Dig next to the fire near the Volcanic Mine entrance."),
new CrypticClue("Search the open crate found in a small farmhouse in Hosidius. Cabbages grow outside.", CRATE_27533, new WorldPoint(1687, 3628, 0), "The house is north-east of the general store in Hosidius."),
new CrypticClue("Search the open crate found in the Hosidius kitchens.", CRATE_27533, new WorldPoint(1683, 3616, 0), "The kitchens are north-west of the town in Hosidius."),
new CrypticClue("Dig under Ithoi's cabin.", new WorldPoint(2529, 2838, 0), "Dig under Ithoi's cabin in the Corsair Cove."),
new CrypticClue("Search the drawers, upstairs in the bank to the East of Varrock.", DRAWERS_7194, new WorldPoint(3250, 3420, 1), "Search the drawers upstairs in Varrock east bank."),
new CrypticClue("Speak to Hazelmere.", "Hazelmere", new WorldPoint(2677, 3088, 1), "Located upstairs in the house to the north of fairy ring CLS. Answer: 6859"),

View File

@@ -27,399 +27,19 @@ package net.runelite.client.plugins.cluescrolls.clues;
import com.google.common.collect.ImmutableSet;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.util.Set;
import javax.annotation.Nonnull;
import lombok.Getter;
import net.runelite.api.Client;
import net.runelite.api.EquipmentInventorySlot;
import static net.runelite.api.EquipmentInventorySlot.AMMO;
import static net.runelite.api.EquipmentInventorySlot.AMULET;
import static net.runelite.api.EquipmentInventorySlot.BODY;
import static net.runelite.api.EquipmentInventorySlot.BOOTS;
import static net.runelite.api.EquipmentInventorySlot.CAPE;
import static net.runelite.api.EquipmentInventorySlot.GLOVES;
import static net.runelite.api.EquipmentInventorySlot.HEAD;
import static net.runelite.api.EquipmentInventorySlot.LEGS;
import static net.runelite.api.EquipmentInventorySlot.RING;
import static net.runelite.api.EquipmentInventorySlot.SHIELD;
import static net.runelite.api.EquipmentInventorySlot.WEAPON;
import static net.runelite.api.EquipmentInventorySlot.*;
import net.runelite.api.Item;
import net.runelite.api.ItemID;
import static net.runelite.api.ItemID.ABYSSAL_WHIP;
import static net.runelite.api.ItemID.ADAMANT_2H_SWORD;
import static net.runelite.api.ItemID.ADAMANT_BOOTS;
import static net.runelite.api.ItemID.ADAMANT_DAGGER;
import static net.runelite.api.ItemID.ADAMANT_FULL_HELM;
import static net.runelite.api.ItemID.ADAMANT_HALBERD;
import static net.runelite.api.ItemID.ADAMANT_MED_HELM;
import static net.runelite.api.ItemID.ADAMANT_PLATEBODY;
import static net.runelite.api.ItemID.ADAMANT_PLATELEGS;
import static net.runelite.api.ItemID.ADAMANT_PLATESKIRT;
import static net.runelite.api.ItemID.ADAMANT_SQ_SHIELD;
import static net.runelite.api.ItemID.ADAMANT_SWORD;
import static net.runelite.api.ItemID.ADAMANT_WARHAMMER;
import static net.runelite.api.ItemID.AHRIMS_HOOD_0;
import static net.runelite.api.ItemID.AHRIMS_HOOD_100;
import static net.runelite.api.ItemID.AHRIMS_ROBESKIRT_0;
import static net.runelite.api.ItemID.AHRIMS_ROBESKIRT_100;
import static net.runelite.api.ItemID.AHRIMS_ROBETOP_0;
import static net.runelite.api.ItemID.AHRIMS_ROBETOP_100;
import static net.runelite.api.ItemID.AHRIMS_STAFF_0;
import static net.runelite.api.ItemID.AHRIMS_STAFF_100;
import static net.runelite.api.ItemID.AIR_TIARA;
import static net.runelite.api.ItemID.AMULET_OF_GLORY;
import static net.runelite.api.ItemID.AMULET_OF_GLORY1;
import static net.runelite.api.ItemID.AMULET_OF_GLORY2;
import static net.runelite.api.ItemID.AMULET_OF_GLORY3;
import static net.runelite.api.ItemID.AMULET_OF_GLORY4;
import static net.runelite.api.ItemID.AMULET_OF_GLORY5;
import static net.runelite.api.ItemID.AMULET_OF_GLORY6;
import static net.runelite.api.ItemID.AMULET_OF_POWER;
import static net.runelite.api.ItemID.AMULET_OF_STRENGTH;
import static net.runelite.api.ItemID.AMULET_OF_THE_DAMNED;
import static net.runelite.api.ItemID.AMULET_OF_THE_DAMNED_FULL;
import static net.runelite.api.ItemID.ANCIENT_CROZIER;
import static net.runelite.api.ItemID.ANCIENT_MITRE;
import static net.runelite.api.ItemID.ANCIENT_STOLE;
import static net.runelite.api.ItemID.ARCLIGHT;
import static net.runelite.api.ItemID.ARMADYL_CROZIER;
import static net.runelite.api.ItemID.ARMADYL_MITRE;
import static net.runelite.api.ItemID.ARMADYL_STOLE;
import static net.runelite.api.ItemID.BANDOS_BOOTS;
import static net.runelite.api.ItemID.BANDOS_CLOAK;
import static net.runelite.api.ItemID.BANDOS_CROZIER;
import static net.runelite.api.ItemID.BANDOS_GODSWORD;
import static net.runelite.api.ItemID.BANDOS_MITRE;
import static net.runelite.api.ItemID.BANDOS_PLATEBODY;
import static net.runelite.api.ItemID.BANDOS_STOLE;
import static net.runelite.api.ItemID.BARROWS_GLOVES;
import static net.runelite.api.ItemID.BLACK_AXE;
import static net.runelite.api.ItemID.BLACK_BOATER;
import static net.runelite.api.ItemID.BLACK_CAPE;
import static net.runelite.api.ItemID.BLACK_DHIDE_BODY;
import static net.runelite.api.ItemID.BLACK_DHIDE_CHAPS;
import static net.runelite.api.ItemID.BLACK_DHIDE_VAMB;
import static net.runelite.api.ItemID.BLACK_DRAGON_MASK;
import static net.runelite.api.ItemID.BLACK_PLATEBODY;
import static net.runelite.api.ItemID.BLACK_SALAMANDER;
import static net.runelite.api.ItemID.BLACK_SLAYER_HELMET;
import static net.runelite.api.ItemID.BLACK_SLAYER_HELMET_I;
import static net.runelite.api.ItemID.BLUE_BOATER;
import static net.runelite.api.ItemID.BLUE_BOOTS;
import static net.runelite.api.ItemID.BLUE_DHIDE_BODY;
import static net.runelite.api.ItemID.BLUE_DHIDE_CHAPS;
import static net.runelite.api.ItemID.BLUE_DHIDE_VAMB;
import static net.runelite.api.ItemID.BLUE_ROBE_TOP;
import static net.runelite.api.ItemID.BLUE_WIZARD_HAT;
import static net.runelite.api.ItemID.BLUE_WIZARD_ROBE;
import static net.runelite.api.ItemID.BOBS_PURPLE_SHIRT;
import static net.runelite.api.ItemID.BOBS_RED_SHIRT;
import static net.runelite.api.ItemID.BODY_TIARA;
import static net.runelite.api.ItemID.BONE_DAGGER;
import static net.runelite.api.ItemID.BONE_SPEAR;
import static net.runelite.api.ItemID.BOOK_OF_BALANCE;
import static net.runelite.api.ItemID.BOOK_OF_DARKNESS;
import static net.runelite.api.ItemID.BOOK_OF_LAW;
import static net.runelite.api.ItemID.BOOK_OF_WAR;
import static net.runelite.api.ItemID.BRINE_SABRE;
import static net.runelite.api.ItemID.BRONZE_2H_SWORD;
import static net.runelite.api.ItemID.BRONZE_AXE;
import static net.runelite.api.ItemID.BRONZE_CHAINBODY;
import static net.runelite.api.ItemID.BRONZE_DAGGER;
import static net.runelite.api.ItemID.BRONZE_FULL_HELM;
import static net.runelite.api.ItemID.BRONZE_PLATELEGS;
import static net.runelite.api.ItemID.BRONZE_SPEAR;
import static net.runelite.api.ItemID.BRONZE_SQ_SHIELD;
import static net.runelite.api.ItemID.BROWN_APRON;
import static net.runelite.api.ItemID.BROWN_HEADBAND;
import static net.runelite.api.ItemID.BRUISE_BLUE_SNELM_3343;
import static net.runelite.api.ItemID.CAPE_OF_LEGENDS;
import static net.runelite.api.ItemID.CASTLE_WARS_BRACELET1;
import static net.runelite.api.ItemID.CASTLE_WARS_BRACELET3;
import static net.runelite.api.ItemID.CHEFS_HAT;
import static net.runelite.api.ItemID.CLIMBING_BOOTS;
import static net.runelite.api.ItemID.COIF;
import static net.runelite.api.ItemID.COMBAT_BRACELET;
import static net.runelite.api.ItemID.COMBAT_BRACELET4;
import static net.runelite.api.ItemID.CREAM_ROBE_TOP;
import static net.runelite.api.ItemID.CRYSTAL_BOW_110_I;
import static net.runelite.api.ItemID.CRYSTAL_BOW_210_I;
import static net.runelite.api.ItemID.CRYSTAL_BOW_310_I;
import static net.runelite.api.ItemID.CRYSTAL_BOW_410_I;
import static net.runelite.api.ItemID.CRYSTAL_BOW_510_I;
import static net.runelite.api.ItemID.CRYSTAL_BOW_610_I;
import static net.runelite.api.ItemID.CRYSTAL_BOW_710_I;
import static net.runelite.api.ItemID.CRYSTAL_BOW_810_I;
import static net.runelite.api.ItemID.CRYSTAL_BOW_910_I;
import static net.runelite.api.ItemID.CRYSTAL_BOW_FULL_I;
import static net.runelite.api.ItemID.DEATH_TIARA;
import static net.runelite.api.ItemID.DESERT_SHIRT;
import static net.runelite.api.ItemID.DHAROKS_GREATAXE_0;
import static net.runelite.api.ItemID.DHAROKS_GREATAXE_100;
import static net.runelite.api.ItemID.DHAROKS_HELM_0;
import static net.runelite.api.ItemID.DHAROKS_HELM_100;
import static net.runelite.api.ItemID.DHAROKS_PLATEBODY_0;
import static net.runelite.api.ItemID.DHAROKS_PLATEBODY_100;
import static net.runelite.api.ItemID.DHAROKS_PLATELEGS_0;
import static net.runelite.api.ItemID.DHAROKS_PLATELEGS_100;
import static net.runelite.api.ItemID.DIAMOND_BRACELET;
import static net.runelite.api.ItemID.DIAMOND_RING;
import static net.runelite.api.ItemID.DRAGONSTONE_AMULET;
import static net.runelite.api.ItemID.DRAGONSTONE_RING;
import static net.runelite.api.ItemID.DRAGON_2H_SWORD;
import static net.runelite.api.ItemID.DRAGON_AXE;
import static net.runelite.api.ItemID.DRAGON_BATTLEAXE;
import static net.runelite.api.ItemID.DRAGON_BOOTS;
import static net.runelite.api.ItemID.DRAGON_CHAINBODY_3140;
import static net.runelite.api.ItemID.DRAGON_DEFENDER;
import static net.runelite.api.ItemID.DRAGON_MED_HELM;
import static net.runelite.api.ItemID.DRAGON_NECKLACE;
import static net.runelite.api.ItemID.DRAGON_PICKAXE;
import static net.runelite.api.ItemID.DRAGON_PICKAXE_12797;
import static net.runelite.api.ItemID.DRAGON_PLATESKIRT;
import static net.runelite.api.ItemID.DRAGON_SPEAR;
import static net.runelite.api.ItemID.DRAGON_SQ_SHIELD;
import static net.runelite.api.ItemID.ELEMENTAL_SHIELD;
import static net.runelite.api.ItemID.EMERALD_AMULET;
import static net.runelite.api.ItemID.EMERALD_RING;
import static net.runelite.api.ItemID.FIRE_BATTLESTAFF;
import static net.runelite.api.ItemID.FIRE_CAPE;
import static net.runelite.api.ItemID.FIRE_MAX_CAPE;
import static net.runelite.api.ItemID.FLARED_TROUSERS;
import static net.runelite.api.ItemID.FROZEN_ABYSSAL_WHIP;
import static net.runelite.api.ItemID.GOLD_NECKLACE;
import static net.runelite.api.ItemID.GOLD_RING;
import static net.runelite.api.ItemID.GRANITE_SHIELD;
import static net.runelite.api.ItemID.GREEN_BOATER;
import static net.runelite.api.ItemID.GREEN_BOOTS;
import static net.runelite.api.ItemID.GREEN_DHIDE_BODY;
import static net.runelite.api.ItemID.GREEN_DHIDE_CHAPS;
import static net.runelite.api.ItemID.GREEN_HAT;
import static net.runelite.api.ItemID.GREEN_HEADBAND;
import static net.runelite.api.ItemID.GREEN_ROBE_BOTTOMS;
import static net.runelite.api.ItemID.GREEN_ROBE_TOP;
import static net.runelite.api.ItemID.GREEN_SLAYER_HELMET;
import static net.runelite.api.ItemID.GREEN_SLAYER_HELMET_I;
import static net.runelite.api.ItemID.GUTHANS_CHAINSKIRT_0;
import static net.runelite.api.ItemID.GUTHANS_CHAINSKIRT_100;
import static net.runelite.api.ItemID.GUTHANS_HELM_0;
import static net.runelite.api.ItemID.GUTHANS_HELM_100;
import static net.runelite.api.ItemID.GUTHANS_PLATEBODY_0;
import static net.runelite.api.ItemID.GUTHANS_PLATEBODY_100;
import static net.runelite.api.ItemID.GUTHANS_WARSPEAR_0;
import static net.runelite.api.ItemID.GUTHANS_WARSPEAR_100;
import static net.runelite.api.ItemID.GUTHIX_MITRE;
import static net.runelite.api.ItemID.GUTHIX_STOLE;
import static net.runelite.api.ItemID.HAM_BOOTS;
import static net.runelite.api.ItemID.HAM_ROBE;
import static net.runelite.api.ItemID.HARDLEATHER_BODY;
import static net.runelite.api.ItemID.HELM_OF_NEITIZNOT;
import static net.runelite.api.ItemID.HOLY_BOOK;
import static net.runelite.api.ItemID.HYDRA_SLAYER_HELMET;
import static net.runelite.api.ItemID.HYDRA_SLAYER_HELMET_I;
import static net.runelite.api.ItemID.IBANS_STAFF;
import static net.runelite.api.ItemID.IBANS_STAFF_U;
import static net.runelite.api.ItemID.INFERNAL_PICKAXE;
import static net.runelite.api.ItemID.INFERNAL_PICKAXE_UNCHARGED;
import static net.runelite.api.ItemID.IRON_2H_SWORD;
import static net.runelite.api.ItemID.IRON_CHAINBODY;
import static net.runelite.api.ItemID.IRON_FULL_HELM;
import static net.runelite.api.ItemID.IRON_KITESHIELD;
import static net.runelite.api.ItemID.IRON_MED_HELM;
import static net.runelite.api.ItemID.IRON_PICKAXE;
import static net.runelite.api.ItemID.IRON_PLATEBODY;
import static net.runelite.api.ItemID.IRON_PLATELEGS;
import static net.runelite.api.ItemID.IRON_PLATESKIRT;
import static net.runelite.api.ItemID.IRON_SCIMITAR;
import static net.runelite.api.ItemID.IRON_WARHAMMER;
import static net.runelite.api.ItemID.KARILS_COIF_0;
import static net.runelite.api.ItemID.KARILS_COIF_100;
import static net.runelite.api.ItemID.KARILS_CROSSBOW_0;
import static net.runelite.api.ItemID.KARILS_CROSSBOW_100;
import static net.runelite.api.ItemID.KARILS_LEATHERSKIRT_0;
import static net.runelite.api.ItemID.KARILS_LEATHERSKIRT_100;
import static net.runelite.api.ItemID.KARILS_LEATHERTOP_0;
import static net.runelite.api.ItemID.KARILS_LEATHERTOP_100;
import static net.runelite.api.ItemID.LAVA_BATTLESTAFF;
import static net.runelite.api.ItemID.LEATHER_BOOTS;
import static net.runelite.api.ItemID.LEATHER_CHAPS;
import static net.runelite.api.ItemID.LEATHER_COWL;
import static net.runelite.api.ItemID.LEATHER_GLOVES;
import static net.runelite.api.ItemID.LOCKPICK;
import static net.runelite.api.ItemID.LONGBOW;
import static net.runelite.api.ItemID.MAGIC_LONGBOW;
import static net.runelite.api.ItemID.MAPLE_LONGBOW;
import static net.runelite.api.ItemID.MAPLE_SHORTBOW;
import static net.runelite.api.ItemID.MENAPHITE_PURPLE_HAT;
import static net.runelite.api.ItemID.MENAPHITE_PURPLE_KILT;
import static net.runelite.api.ItemID.MENAPHITE_PURPLE_ROBE;
import static net.runelite.api.ItemID.MENAPHITE_PURPLE_TOP;
import static net.runelite.api.ItemID.MENAPHITE_RED_HAT;
import static net.runelite.api.ItemID.MENAPHITE_RED_KILT;
import static net.runelite.api.ItemID.MENAPHITE_RED_ROBE;
import static net.runelite.api.ItemID.MENAPHITE_RED_TOP;
import static net.runelite.api.ItemID.MIND_SHIELD;
import static net.runelite.api.ItemID.MITHRIL_BOOTS;
import static net.runelite.api.ItemID.MITHRIL_CHAINBODY;
import static net.runelite.api.ItemID.MITHRIL_FULL_HELM;
import static net.runelite.api.ItemID.MITHRIL_MED_HELM;
import static net.runelite.api.ItemID.MITHRIL_PICKAXE;
import static net.runelite.api.ItemID.MITHRIL_PLATEBODY;
import static net.runelite.api.ItemID.MITHRIL_PLATELEGS;
import static net.runelite.api.ItemID.MITHRIL_PLATESKIRT;
import static net.runelite.api.ItemID.MITHRIL_SCIMITAR;
import static net.runelite.api.ItemID.MYSTIC_FIRE_STAFF;
import static net.runelite.api.ItemID.MYSTIC_GLOVES;
import static net.runelite.api.ItemID.MYSTIC_HAT;
import static net.runelite.api.ItemID.MYSTIC_ROBE_BOTTOM;
import static net.runelite.api.ItemID.MYSTIC_ROBE_BOTTOM_DARK;
import static net.runelite.api.ItemID.MYSTIC_ROBE_TOP;
import static net.runelite.api.ItemID.MYSTIC_ROBE_TOP_DARK;
import static net.runelite.api.ItemID.NEW_CRYSTAL_BOW_I;
import static net.runelite.api.ItemID.OAK_LONGBOW;
import static net.runelite.api.ItemID.OAK_SHORTBOW;
import static net.runelite.api.ItemID.OBSIDIAN_CAPE;
import static net.runelite.api.ItemID.ORANGE_BOATER;
import static net.runelite.api.ItemID.PHARAOHS_SCEPTRE;
import static net.runelite.api.ItemID.PHARAOHS_SCEPTRE_1;
import static net.runelite.api.ItemID.PHARAOHS_SCEPTRE_2;
import static net.runelite.api.ItemID.PHARAOHS_SCEPTRE_3;
import static net.runelite.api.ItemID.PHARAOHS_SCEPTRE_4;
import static net.runelite.api.ItemID.PHARAOHS_SCEPTRE_5;
import static net.runelite.api.ItemID.PHARAOHS_SCEPTRE_6;
import static net.runelite.api.ItemID.PHARAOHS_SCEPTRE_7;
import static net.runelite.api.ItemID.PHARAOHS_SCEPTRE_8;
import static net.runelite.api.ItemID.PINK_BOATER;
import static net.runelite.api.ItemID.PINK_ROBE_TOP;
import static net.runelite.api.ItemID.PINK_SKIRT;
import static net.runelite.api.ItemID.PIRATE_BANDANA;
import static net.runelite.api.ItemID.PIRATE_BANDANA_7124;
import static net.runelite.api.ItemID.PIRATE_BANDANA_7130;
import static net.runelite.api.ItemID.PIRATE_BANDANA_7136;
import static net.runelite.api.ItemID.PROSELYTE_HAUBERK;
import static net.runelite.api.ItemID.PURPLE_BOATER;
import static net.runelite.api.ItemID.PURPLE_GLOVES;
import static net.runelite.api.ItemID.PURPLE_SLAYER_HELMET;
import static net.runelite.api.ItemID.PURPLE_SLAYER_HELMET_I;
import static net.runelite.api.ItemID.RED_BOATER;
import static net.runelite.api.ItemID.RED_CAPE;
import static net.runelite.api.ItemID.RED_DHIDE_CHAPS;
import static net.runelite.api.ItemID.RED_HEADBAND;
import static net.runelite.api.ItemID.RED_SLAYER_HELMET;
import static net.runelite.api.ItemID.RED_SLAYER_HELMET_I;
import static net.runelite.api.ItemID.RING_OF_DUELING1;
import static net.runelite.api.ItemID.RING_OF_DUELING2;
import static net.runelite.api.ItemID.RING_OF_DUELING3;
import static net.runelite.api.ItemID.RING_OF_DUELING4;
import static net.runelite.api.ItemID.RING_OF_DUELING5;
import static net.runelite.api.ItemID.RING_OF_DUELING6;
import static net.runelite.api.ItemID.RING_OF_DUELING7;
import static net.runelite.api.ItemID.RING_OF_DUELING8;
import static net.runelite.api.ItemID.RING_OF_FORGING;
import static net.runelite.api.ItemID.RING_OF_LIFE;
import static net.runelite.api.ItemID.RING_OF_VISIBILITY;
import static net.runelite.api.ItemID.RING_OF_WEALTH;
import static net.runelite.api.ItemID.RING_OF_WEALTH_1;
import static net.runelite.api.ItemID.RING_OF_WEALTH_2;
import static net.runelite.api.ItemID.RING_OF_WEALTH_3;
import static net.runelite.api.ItemID.RING_OF_WEALTH_4;
import static net.runelite.api.ItemID.RING_OF_WEALTH_5;
import static net.runelite.api.ItemID.RING_OF_WEALTH_I;
import static net.runelite.api.ItemID.RING_OF_WEALTH_I1;
import static net.runelite.api.ItemID.RING_OF_WEALTH_I2;
import static net.runelite.api.ItemID.RING_OF_WEALTH_I3;
import static net.runelite.api.ItemID.RING_OF_WEALTH_I4;
import static net.runelite.api.ItemID.RING_OF_WEALTH_I5;
import static net.runelite.api.ItemID.ROLLING_PIN;
import static net.runelite.api.ItemID.RUBY_AMULET;
import static net.runelite.api.ItemID.RUBY_RING;
import static net.runelite.api.ItemID.RUNE_AXE;
import static net.runelite.api.ItemID.RUNE_BOOTS;
import static net.runelite.api.ItemID.RUNE_CROSSBOW;
import static net.runelite.api.ItemID.RUNE_FULL_HELM;
import static net.runelite.api.ItemID.RUNE_HALBERD;
import static net.runelite.api.ItemID.RUNE_HELM_H1;
import static net.runelite.api.ItemID.RUNE_HELM_H5;
import static net.runelite.api.ItemID.RUNE_KITESHIELD;
import static net.runelite.api.ItemID.RUNE_LONGSWORD;
import static net.runelite.api.ItemID.RUNE_PICKAXE;
import static net.runelite.api.ItemID.RUNE_PLATEBODY;
import static net.runelite.api.ItemID.RUNE_PLATELEGS;
import static net.runelite.api.ItemID.RUNE_PLATESKIRT;
import static net.runelite.api.ItemID.RUNE_SHIELD_H1;
import static net.runelite.api.ItemID.RUNE_SHIELD_H2;
import static net.runelite.api.ItemID.RUNE_SHIELD_H3;
import static net.runelite.api.ItemID.RUNE_SHIELD_H4;
import static net.runelite.api.ItemID.RUNE_SHIELD_H5;
import static net.runelite.api.ItemID.RUNE_SPEAR;
import static net.runelite.api.ItemID.RUNE_WARHAMMER;
import static net.runelite.api.ItemID.SAPPHIRE_AMULET;
import static net.runelite.api.ItemID.SAPPHIRE_NECKLACE;
import static net.runelite.api.ItemID.SAPPHIRE_RING;
import static net.runelite.api.ItemID.SARADOMIN_CROZIER;
import static net.runelite.api.ItemID.SARADOMIN_MITRE;
import static net.runelite.api.ItemID.SARADOMIN_STOLE;
import static net.runelite.api.ItemID.SEERCULL;
import static net.runelite.api.ItemID.SHADOW_SWORD;
import static net.runelite.api.ItemID.SILVER_SICKLE;
import static net.runelite.api.ItemID.SLAYER_HELMET;
import static net.runelite.api.ItemID.SLAYER_HELMET_I;
import static net.runelite.api.ItemID.SLED_4084;
import static net.runelite.api.ItemID.SNAKESKIN_BOOTS;
import static net.runelite.api.ItemID.SNAKESKIN_CHAPS;
import static net.runelite.api.ItemID.SPINED_CHAPS;
import static net.runelite.api.ItemID.SPLITBARK_BODY;
import static net.runelite.api.ItemID.SPLITBARK_LEGS;
import static net.runelite.api.ItemID.SPOTTED_CAPE;
import static net.runelite.api.ItemID.SPOTTED_CAPE_10073;
import static net.runelite.api.ItemID.STAFF;
import static net.runelite.api.ItemID.STAFF_OF_AIR;
import static net.runelite.api.ItemID.STAFF_OF_WATER;
import static net.runelite.api.ItemID.STEEL_AXE;
import static net.runelite.api.ItemID.STEEL_FULL_HELM;
import static net.runelite.api.ItemID.STEEL_KITESHIELD;
import static net.runelite.api.ItemID.STEEL_LONGSWORD;
import static net.runelite.api.ItemID.STEEL_MACE;
import static net.runelite.api.ItemID.STEEL_MED_HELM;
import static net.runelite.api.ItemID.STEEL_PICKAXE;
import static net.runelite.api.ItemID.STEEL_PLATEBODY;
import static net.runelite.api.ItemID.STEEL_PLATESKIRT;
import static net.runelite.api.ItemID.STEEL_SQ_SHIELD;
import static net.runelite.api.ItemID.STUDDED_BODY;
import static net.runelite.api.ItemID.STUDDED_CHAPS;
import static net.runelite.api.ItemID.TEAM1_CAPE;
import static net.runelite.api.ItemID.TEAM50_CAPE;
import static net.runelite.api.ItemID.TIARA;
import static net.runelite.api.ItemID.TOKTZKETXIL;
import static net.runelite.api.ItemID.TOKTZXILUL;
import static net.runelite.api.ItemID.TORAGS_HAMMERS_0;
import static net.runelite.api.ItemID.TORAGS_HAMMERS_100;
import static net.runelite.api.ItemID.TORAGS_HELM_0;
import static net.runelite.api.ItemID.TORAGS_HELM_100;
import static net.runelite.api.ItemID.TORAGS_PLATEBODY_0;
import static net.runelite.api.ItemID.TORAGS_PLATEBODY_100;
import static net.runelite.api.ItemID.TORAGS_PLATELEGS_0;
import static net.runelite.api.ItemID.TORAGS_PLATELEGS_100;
import static net.runelite.api.ItemID.TURQUOISE_ROBE_BOTTOMS;
import static net.runelite.api.ItemID.TURQUOISE_SLAYER_HELMET;
import static net.runelite.api.ItemID.TURQUOISE_SLAYER_HELMET_I;
import static net.runelite.api.ItemID.UNHOLY_BOOK;
import static net.runelite.api.ItemID.VERACS_BRASSARD_0;
import static net.runelite.api.ItemID.VERACS_BRASSARD_100;
import static net.runelite.api.ItemID.VERACS_FLAIL_0;
import static net.runelite.api.ItemID.VERACS_FLAIL_100;
import static net.runelite.api.ItemID.VERACS_HELM_0;
import static net.runelite.api.ItemID.VERACS_HELM_100;
import static net.runelite.api.ItemID.VERACS_PLATESKIRT_0;
import static net.runelite.api.ItemID.VERACS_PLATESKIRT_100;
import static net.runelite.api.ItemID.VOLCANIC_ABYSSAL_WHIP;
import static net.runelite.api.ItemID.WHITE_APRON;
import static net.runelite.api.ItemID.WHITE_BOATER;
import static net.runelite.api.ItemID.WHITE_HEADBAND;
import static net.runelite.api.ItemID.ZAMORAK_CROZIER;
import static net.runelite.api.ItemID.ZAMORAK_FULL_HELM;
import static net.runelite.api.ItemID.ZAMORAK_GODSWORD;
import static net.runelite.api.ItemID.ZAMORAK_MITRE;
import static net.runelite.api.ItemID.ZAMORAK_STOLE;
import static net.runelite.api.ItemID.*;
import net.runelite.api.Perspective;
import net.runelite.api.ScriptID;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import static net.runelite.client.plugins.cluescrolls.ClueScrollOverlay.TITLED_CONTENT_COLOR;
@@ -427,143 +47,13 @@ import net.runelite.client.plugins.cluescrolls.ClueScrollPlugin;
import net.runelite.client.plugins.cluescrolls.clues.emote.AllRequirementsCollection;
import net.runelite.client.plugins.cluescrolls.clues.emote.AnyRequirementCollection;
import net.runelite.client.plugins.cluescrolls.clues.emote.Emote;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.ANGRY;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.BECKON;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.BLOW_KISS;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.BOW;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.BULL_ROARER;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.CHEER;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.CLAP;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.CRY;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.DANCE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.FLAP;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.GOBLIN_SALUTE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.HEADBANG;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.JIG;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.JUMP_FOR_JOY;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.LAUGH;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.NO;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.PANIC;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.PUSH_UP;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.RASPBERRY;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.SALUTE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.SHRUG;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.SLAP_HEAD;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.SPIN;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.STOMP;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.THINK;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.WAVE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.YAWN;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.YES;
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.*;
import net.runelite.client.plugins.cluescrolls.clues.emote.ItemRequirement;
import net.runelite.client.plugins.cluescrolls.clues.emote.RangeItemRequirement;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.AGILITY_PYRAMID;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.AL_KHARID_SCORPION_MINE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.AUBURYS_SHOP_IN_VARROCK;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.BARBARIAN_OUTPOST_OBSTACLE_COURSE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.BARROWS_CHEST;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.BEHIND_MISS_SCHISM_IN_DRAYNOR_VILLAGE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.BOB_AXES_ENTRANCE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.BY_THE_BEAR_CAGE_IN_VARROCK_PALACE_GARDENS;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.CASTLE_WARS_BANK;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.CATHERBY_BEEHIVE_FIELD;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.CENTRE_OF_CANIFIS;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.CENTRE_OF_THE_CATACOMBS_OF_KOUREND;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.CHAOS_TEMPLE_IN_THE_SOUTHEASTERN_WILDERNESS;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.CHAPEL_IN_WEST_ARDOUGNE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.CROSSROADS_NORTH_OF_DRAYNOR_VILLAGE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.DEATH_ALTAR;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.DIGSITE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.DRAYNOR_MANOR_BY_THE_FOUNTAIN;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.DRAYNOR_VILLAGE_MARKET;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.EAST_OF_THE_BARBARIAN_VILLAGE_BRIDGE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.ENTRANA_CHAPEL;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.ENTRANCE_OF_THE_ARCEUUS_LIBRARY;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.ENTRANCE_OF_THE_CAVERN_UNDER_THE_WHIRLPOOL;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.ENTRANCE_OF_THE_CAVE_OF_DAMIS;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.FINE_CLOTHES_ENTRANCE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.FISHING_GUILD_BANK;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.FOUNTAIN_OF_HEROES;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.GNOME_GLIDER_ON_WHITE_WOLF_MOUNTAIN;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.GNOME_STRONGHOLD_BALANCING_ROPE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.GYPSY_TENT_ENTRANCE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.HALFWAY_DOWN_TROLLWEISS_MOUNTAIN;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.HICKTONS_ARCHERY_EMPORIUM;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.HOSIDIUS_MESS;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.INSIDE_THE_DIGSITE_EXAM_CENTRE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.IN_THE_MIDDLE_OF_JIGGIG;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.KING_BLACK_DRAGONS_LAIR;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.LIMESTONE_MINE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.LUMBRIDGE_SWAMP_CAVES;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.MAUSOLEUM_OFF_THE_MORYTANIA_COAST;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.MOUNTAIN_CAMP_GOAT_ENCLOSURE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.MUBARIZS_ROOM_AT_THE_DUEL_ARENA;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.MUDSKIPPER_POINT;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.NEAR_A_LADDER_IN_THE_WILDERNESS_LAVA_MAZE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.NEAR_A_RUNITE_ROCK_IN_THE_FREMENNIK_ISLES;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.NEAR_A_SHED_IN_LUMBRIDGE_SWAMP;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.NEAR_HERQUINS_SHOP_IN_FALADOR;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.NEAR_THE_ENTRANA_FERRY_IN_PORT_SARIM;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.NEAR_THE_GEM_STALL_IN_ARDOUGNE_MARKET;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.NEAR_THE_PARROTS_IN_ARDOUGNE_ZOO;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.NEAR_THE_PIER_IN_ZULANDRA;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.NEAR_THE_SAWMILL_OPERATORS_BOOTH;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.NORTHEAST_CORNER_OF_THE_KHARAZI_JUNGLE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.NORTHERN_WALL_OF_CASTLE_DRAKAN;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.NORTHWESTERN_CORNER_OF_THE_ENCHANTED_VALLEY;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.NORTH_OF_EVIL_DAVES_HOUSE_IN_EDGEVILLE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.NORTH_OF_MOUNT_KARUULM;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.NOTERAZZOS_SHOP_IN_THE_WILDERNESS;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.OBSERVATORY;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.OGRE_CAGE_IN_KING_LATHAS_TRAINING_CAMP;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.ON_THE_BRIDGE_TO_THE_MISTHALIN_WIZARDS_TOWER;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.ON_TOP_OF_TROLLHEIM_MOUNTAIN;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.OUTSIDE_CATHERBY_BANK;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.OUTSIDE_DRAYNOR_VILLAGE_JAIL;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.OUTSIDE_HARRYS_FISHING_SHOP_IN_CATHERBY;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.OUTSIDE_KEEP_LE_FAYE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.OUTSIDE_KRIL_TSUTSAROTHS_ROOM;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.OUTSIDE_MUDKNUCKLES_HUT;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.OUTSIDE_THE_BAR_BY_THE_FIGHT_ARENA;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.OUTSIDE_THE_DIGSITE_EXAM_CENTRE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.OUTSIDE_THE_FALADOR_PARTY_ROOM;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.OUTSIDE_THE_FISHING_GUILD;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.OUTSIDE_THE_GREAT_PYRAMID_OF_SOPHANEM;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.OUTSIDE_THE_LEGENDS_GUILD_DOOR;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.OUTSIDE_THE_LEGENDS_GUILD_GATES;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.OUTSIDE_THE_SEERS_VILLAGE_COURTHOUSE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.OUTSIDE_THE_SLAYER_TOWER_GARGOYLE_ROOM;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.OUTSIDE_THE_WILDERNESS_AXE_HUT;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.OUTSIDE_VARROCK_PALACE_COURTYARD;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.OUTSIDE_YANILLE_BANK;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.RIMMINGTON_MINE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.ROAD_JUNCTION_NORTH_OF_RIMMINGTON;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.ROAD_JUNCTION_SOUTH_OF_SINCLAIR_MANSION;
import net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.SHANTAY_PASS;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.SHAYZIEN_WAR_TENT;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.SHILO_VILLAGE_BANK;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.SOUL_ALTAR;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.SOUTHEAST_CORNER_OF_LAVA_DRAGON_ISLE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.SOUTHEAST_CORNER_OF_THE_FISHING_PLATFORM;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.SOUTHEAST_CORNER_OF_THE_MONASTERY;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.SOUTH_OF_THE_GRAND_EXCHANGE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.SOUTH_OF_THE_SHRINE_IN_TAI_BWO_WANNAI_VILLAGE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.TAVERLEY_STONE_CIRCLE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.TENT_IN_LORD_IORWERTHS_ENCAMPMENT;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.TOP_FLOOR_OF_THE_LIGHTHOUSE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.TOP_FLOOR_OF_THE_YANILLE_WATCHTOWER;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.TZHAAR_GEM_STORE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.TZHAAR_WEAPONS_STORE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.UPSTAIRS_IN_THE_ARDOUGNE_WINDMILL;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.VARROCK_PALACE_LIBRARY;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.VOLCANO_IN_THE_NORTHEASTERN_WILDERNESS;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.WARRIORS_GUILD_BANK;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.WARRIORS_GUILD_BANK_29047;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.WELL_OF_VOYAGE;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.WEST_OF_THE_SHAYZIEN_COMBAT_RING;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.WEST_SIDE_OF_THE_KARAMJA_BANANA_PLANTATION;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.WHEAT_FIELD_NEAR_THE_LUMBRIDGE_WINDMILL;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit._7TH_CHAMBER_OF_JALSAVRAH;
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.*;
import net.runelite.client.plugins.cluescrolls.clues.emote.SingleItemRequirement;
import net.runelite.client.plugins.cluescrolls.clues.emote.SlotLimitationRequirement;
import net.runelite.client.ui.overlay.OverlayUtil;
@@ -690,6 +180,9 @@ public class EmoteClue extends ClueScroll implements TextClueScroll, LocationClu
new EmoteClue("Panic at Al Kharid mine.", null, new WorldPoint(3300, 3314, 0), PANIC),
new EmoteClue("Spin at Flynn's Mace Shop.", null, new WorldPoint(2950, 3387, 0), SPIN));
private static final String UNICODE_CHECK_MARK = "\u2713";
private static final String UNICODE_BALLOT_X = "\u2717";
private static SingleItemRequirement item(int itemId)
{
return new SingleItemRequirement(itemId);
@@ -721,19 +214,18 @@ public class EmoteClue extends ClueScroll implements TextClueScroll, LocationClu
}
private final String text;
private final Integer stashUnit;
private final STASHUnit stashUnit;
private final WorldPoint location;
private final Emote firstEmote;
private final Emote secondEmote;
@Nonnull
private final ItemRequirement[] itemRequirements;
private EmoteClue(String text, Integer stashUnit, WorldPoint location, Emote firstEmote, @Nonnull ItemRequirement... itemRequirements)
private EmoteClue(String text, STASHUnit stashUnit, WorldPoint location, Emote firstEmote, @Nonnull ItemRequirement... itemRequirements)
{
this(text, stashUnit, location, firstEmote, null, itemRequirements);
}
private EmoteClue(String text, Integer stashUnit, WorldPoint location, Emote firstEmote, Emote secondEmote, @Nonnull ItemRequirement... itemRequirements)
private EmoteClue(String text, STASHUnit stashUnit, WorldPoint location, Emote firstEmote, Emote secondEmote, @Nonnull ItemRequirement... itemRequirements)
{
this.text = text;
this.stashUnit = stashUnit;
@@ -763,6 +255,17 @@ public class EmoteClue extends ClueScroll implements TextClueScroll, LocationClu
if (itemRequirements.length > 0)
{
Client client = plugin.getClient();
client.runScript(ScriptID.WATSON_STASH_UNIT_CHECK, stashUnit.getObjectId(), 0, 0, 0);
int[] intStack = client.getIntStack();
boolean stashUnitBuilt = intStack[0] == 1;
panelComponent.getChildren().add(LineComponent.builder()
.left("STASH Unit:")
.right(stashUnitBuilt ? UNICODE_CHECK_MARK : UNICODE_BALLOT_X)
.rightColor(stashUnitBuilt ? Color.GREEN : Color.RED)
.build());
panelComponent.getChildren().add(LineComponent.builder().left("Equip:").build());
Item[] equipment = plugin.getEquippedItems();
@@ -790,9 +293,9 @@ public class EmoteClue extends ClueScroll implements TextClueScroll, LocationClu
boolean combinedFulfilled = requirement.fulfilledBy(combined);
panelComponent.getChildren().add(LineComponent.builder()
.left(requirement.getCollectiveName(plugin.getClient()))
.left(requirement.getCollectiveName(client))
.leftColor(TITLED_CONTENT_COLOR)
.right(combinedFulfilled ? "\u2713" : "\u2717")
.right(combinedFulfilled ? UNICODE_CHECK_MARK : UNICODE_BALLOT_X)
.rightColor(equipmentFulfilled ? Color.GREEN : (combinedFulfilled ? Color.ORANGE : Color.RED))
.build());
}
@@ -802,14 +305,28 @@ public class EmoteClue extends ClueScroll implements TextClueScroll, LocationClu
@Override
public void makeWorldOverlayHint(Graphics2D graphics, ClueScrollPlugin plugin)
{
LocalPoint localLocation = LocalPoint.fromWorld(plugin.getClient(), getLocation());
LocalPoint localPoint = LocalPoint.fromWorld(plugin.getClient(), getLocation());
if (localLocation == null)
if (localPoint != null)
{
return;
OverlayUtil.renderTileOverlay(plugin.getClient(), graphics, localPoint, plugin.getEmoteImage(), Color.ORANGE);
}
OverlayUtil.renderTileOverlay(plugin.getClient(), graphics, localLocation, plugin.getEmoteImage(), Color.ORANGE);
final WorldPoint[] worldPoints = stashUnit.getWorldPoints();
for (final WorldPoint worldPoint : worldPoints)
{
final LocalPoint stashUnitLocalPoint = LocalPoint.fromWorld(plugin.getClient(), worldPoint);
if (stashUnitLocalPoint != null)
{
final Polygon poly = Perspective.getCanvasTilePoly(plugin.getClient(), stashUnitLocalPoint);
if (poly != null)
{
OverlayUtil.renderPolygon(graphics, poly, Color.RED);
}
}
}
}
public static EmoteClue forText(String text)

View File

@@ -29,38 +29,7 @@ import java.awt.Color;
import java.awt.Graphics2D;
import java.util.Set;
import lombok.Getter;
import static net.runelite.api.ItemID.CLUE_SCROLL_EASY_12179;
import static net.runelite.api.ItemID.CLUE_SCROLL_EASY_2713;
import static net.runelite.api.ItemID.CLUE_SCROLL_EASY_2716;
import static net.runelite.api.ItemID.CLUE_SCROLL_EASY_2719;
import static net.runelite.api.ItemID.CLUE_SCROLL_EASY_3516;
import static net.runelite.api.ItemID.CLUE_SCROLL_EASY_3518;
import static net.runelite.api.ItemID.CLUE_SCROLL_EASY_7236;
import static net.runelite.api.ItemID.CLUE_SCROLL_ELITE_12130;
import static net.runelite.api.ItemID.CLUE_SCROLL_ELITE_19782;
import static net.runelite.api.ItemID.CLUE_SCROLL_ELITE_19783;
import static net.runelite.api.ItemID.CLUE_SCROLL_ELITE_19784;
import static net.runelite.api.ItemID.CLUE_SCROLL_ELITE_19785;
import static net.runelite.api.ItemID.CLUE_SCROLL_ELITE_19786;
import static net.runelite.api.ItemID.CLUE_SCROLL_HARD;
import static net.runelite.api.ItemID.CLUE_SCROLL_HARD_2729;
import static net.runelite.api.ItemID.CLUE_SCROLL_HARD_3520;
import static net.runelite.api.ItemID.CLUE_SCROLL_HARD_3522;
import static net.runelite.api.ItemID.CLUE_SCROLL_HARD_3524;
import static net.runelite.api.ItemID.CLUE_SCROLL_HARD_3525;
import static net.runelite.api.ItemID.CLUE_SCROLL_HARD_7239;
import static net.runelite.api.ItemID.CLUE_SCROLL_HARD_7241;
import static net.runelite.api.ItemID.CLUE_SCROLL_MEDIUM_2827;
import static net.runelite.api.ItemID.CLUE_SCROLL_MEDIUM_3596;
import static net.runelite.api.ItemID.CLUE_SCROLL_MEDIUM_3598;
import static net.runelite.api.ItemID.CLUE_SCROLL_MEDIUM_3599;
import static net.runelite.api.ItemID.CLUE_SCROLL_MEDIUM_3601;
import static net.runelite.api.ItemID.CLUE_SCROLL_MEDIUM_3602;
import static net.runelite.api.ItemID.CLUE_SCROLL_MEDIUM_7286;
import static net.runelite.api.ItemID.CLUE_SCROLL_MEDIUM_7288;
import static net.runelite.api.ItemID.CLUE_SCROLL_MEDIUM_7290;
import static net.runelite.api.ItemID.CLUE_SCROLL_MEDIUM_7292;
import static net.runelite.api.ItemID.CLUE_SCROLL_MEDIUM_7294;
import static net.runelite.api.ItemID.*;
import net.runelite.api.ObjectComposition;
import static net.runelite.api.ObjectID.CRATE_18506;
import static net.runelite.api.ObjectID.CRATE_2620;
@@ -133,7 +102,7 @@ public class MapClue extends ClueScroll implements ObjectClueScroll
this(itemId, location, objectId, null);
}
private MapClue(int itemId, WorldPoint location, String description)
MapClue(int itemId, WorldPoint location, String description)
{
this(itemId, location, -1, description);
}

View File

@@ -24,115 +24,127 @@
*/
package net.runelite.client.plugins.cluescrolls.clues.emote;
import lombok.Getter;
import net.runelite.api.NullObjectID;
import net.runelite.api.coords.WorldPoint;
public final class STASHUnit
@Getter
public enum STASHUnit
{
public static final int NEAR_A_SHED_IN_LUMBRIDGE_SWAMP = NullObjectID.NULL_28958;
public static final int ON_THE_BRIDGE_TO_THE_MISTHALIN_WIZARDS_TOWER = NullObjectID.NULL_28959;
public static final int DRAYNOR_VILLAGE_MARKET = NullObjectID.NULL_28960;
public static final int LIMESTONE_MINE = NullObjectID.NULL_28961;
public static final int OUTSIDE_THE_LEGENDS_GUILD_GATES = NullObjectID.NULL_28962;
public static final int MUDSKIPPER_POINT = NullObjectID.NULL_28963;
public static final int NEAR_THE_ENTRANA_FERRY_IN_PORT_SARIM = NullObjectID.NULL_28964;
public static final int AL_KHARID_SCORPION_MINE = NullObjectID.NULL_28965;
public static final int DRAYNOR_MANOR_BY_THE_FOUNTAIN = NullObjectID.NULL_28966;
public static final int WHEAT_FIELD_NEAR_THE_LUMBRIDGE_WINDMILL = NullObjectID.NULL_28967;
public static final int CROSSROADS_NORTH_OF_DRAYNOR_VILLAGE = NullObjectID.NULL_28968;
public static final int RIMMINGTON_MINE = NullObjectID.NULL_28969;
public static final int VARROCK_PALACE_LIBRARY = NullObjectID.NULL_28970;
public static final int UPSTAIRS_IN_THE_ARDOUGNE_WINDMILL = NullObjectID.NULL_28971;
public static final int OUTSIDE_THE_FALADOR_PARTY_ROOM = NullObjectID.NULL_28972;
public static final int TAVERLEY_STONE_CIRCLE = NullObjectID.NULL_28973;
public static final int CATHERBY_BEEHIVE_FIELD = NullObjectID.NULL_28974;
public static final int NEAR_THE_PARROTS_IN_ARDOUGNE_ZOO = NullObjectID.NULL_28975;
public static final int ROAD_JUNCTION_NORTH_OF_RIMMINGTON = NullObjectID.NULL_28976;
public static final int OUTSIDE_THE_FISHING_GUILD = NullObjectID.NULL_28977;
public static final int OUTSIDE_KEEP_LE_FAYE = NullObjectID.NULL_28978;
public static final int ROAD_JUNCTION_SOUTH_OF_SINCLAIR_MANSION = NullObjectID.NULL_28979;
public static final int OUTSIDE_THE_DIGSITE_EXAM_CENTRE = NullObjectID.NULL_28980;
public static final int NEAR_THE_SAWMILL_OPERATORS_BOOTH = NullObjectID.NULL_28981;
public static final int MUBARIZS_ROOM_AT_THE_DUEL_ARENA = NullObjectID.NULL_28982;
public static final int OUTSIDE_VARROCK_PALACE_COURTYARD = NullObjectID.NULL_28983;
public static final int NEAR_HERQUINS_SHOP_IN_FALADOR = NullObjectID.NULL_28984;
public static final int SOUTH_OF_THE_GRAND_EXCHANGE = NullObjectID.NULL_28985;
public static final int AUBURYS_SHOP_IN_VARROCK = NullObjectID.NULL_28986;
public static final int CENTRE_OF_CANIFIS = NullObjectID.NULL_28987;
public static final int MAUSOLEUM_OFF_THE_MORYTANIA_COAST = NullObjectID.NULL_28988;
public static final int EAST_OF_THE_BARBARIAN_VILLAGE_BRIDGE = NullObjectID.NULL_28989;
public static final int SOUTH_OF_THE_SHRINE_IN_TAI_BWO_WANNAI_VILLAGE = NullObjectID.NULL_28990;
public static final int CASTLE_WARS_BANK = NullObjectID.NULL_28991;
public static final int BARBARIAN_OUTPOST_OBSTACLE_COURSE = NullObjectID.NULL_28992;
public static final int GNOME_STRONGHOLD_BALANCING_ROPE = NullObjectID.NULL_28993;
public static final int OUTSIDE_YANILLE_BANK = NullObjectID.NULL_28994;
public static final int OBSERVATORY = NullObjectID.NULL_28995;
public static final int OGRE_CAGE_IN_KING_LATHAS_TRAINING_CAMP = NullObjectID.NULL_28996;
public static final int DIGSITE = NullObjectID.NULL_28997;
public static final int HICKTONS_ARCHERY_EMPORIUM = NullObjectID.NULL_28998;
public static final int SHANTAY_PASS = NullObjectID.NULL_28999;
public static final int LUMBRIDGE_SWAMP_CAVES = NullObjectID.NULL_29000;
public static final int OUTSIDE_CATHERBY_BANK = NullObjectID.NULL_29001;
public static final int OUTSIDE_THE_SEERS_VILLAGE_COURTHOUSE = NullObjectID.NULL_29002;
public static final int OUTSIDE_HARRYS_FISHING_SHOP_IN_CATHERBY = NullObjectID.NULL_29003;
public static final int TZHAAR_WEAPONS_STORE = NullObjectID.NULL_29004;
public static final int NORTH_OF_EVIL_DAVES_HOUSE_IN_EDGEVILLE = NullObjectID.NULL_29005;
public static final int WEST_OF_THE_SHAYZIEN_COMBAT_RING = NullObjectID.NULL_29006;
public static final int ENTRANCE_OF_THE_ARCEUUS_LIBRARY = NullObjectID.NULL_29007;
public static final int OUTSIDE_DRAYNOR_VILLAGE_JAIL = NullObjectID.NULL_29008;
public static final int CHAOS_TEMPLE_IN_THE_SOUTHEASTERN_WILDERNESS = NullObjectID.NULL_29009;
public static final int FISHING_GUILD_BANK = NullObjectID.NULL_29010;
public static final int TOP_FLOOR_OF_THE_LIGHTHOUSE = NullObjectID.NULL_29011;
public static final int OUTSIDE_THE_GREAT_PYRAMID_OF_SOPHANEM = NullObjectID.NULL_29012;
public static final int NOTERAZZOS_SHOP_IN_THE_WILDERNESS = NullObjectID.NULL_29013;
public static final int WEST_SIDE_OF_THE_KARAMJA_BANANA_PLANTATION = NullObjectID.NULL_29014;
public static final int MOUNTAIN_CAMP_GOAT_ENCLOSURE = NullObjectID.NULL_29015;
public static final int GNOME_GLIDER_ON_WHITE_WOLF_MOUNTAIN = NullObjectID.NULL_29016;
public static final int SHILO_VILLAGE_BANK = NullObjectID.NULL_29017;
public static final int INSIDE_THE_DIGSITE_EXAM_CENTRE = NullObjectID.NULL_29018;
public static final int NORTHEAST_CORNER_OF_THE_KHARAZI_JUNGLE = NullObjectID.NULL_29019;
public static final int VOLCANO_IN_THE_NORTHEASTERN_WILDERNESS = NullObjectID.NULL_29020;
public static final int IN_THE_MIDDLE_OF_JIGGIG = NullObjectID.NULL_29021;
public static final int AGILITY_PYRAMID = NullObjectID.NULL_29022;
public static final int HOSIDIUS_MESS = NullObjectID.NULL_29023;
public static final int CHAPEL_IN_WEST_ARDOUGNE = NullObjectID.NULL_29024;
public static final int NEAR_A_RUNITE_ROCK_IN_THE_FREMENNIK_ISLES = NullObjectID.NULL_29025;
public static final int NEAR_A_LADDER_IN_THE_WILDERNESS_LAVA_MAZE = NullObjectID.NULL_29026;
public static final int ENTRANCE_OF_THE_CAVE_OF_DAMIS = NullObjectID.NULL_29027;
public static final int WARRIORS_GUILD_BANK = NullObjectID.NULL_29028;
public static final int SOUTHEAST_CORNER_OF_THE_MONASTERY = NullObjectID.NULL_29029;
public static final int SOUTHEAST_CORNER_OF_THE_FISHING_PLATFORM = NullObjectID.NULL_29030;
public static final int OUTSIDE_THE_SLAYER_TOWER_GARGOYLE_ROOM = NullObjectID.NULL_29031;
public static final int ON_TOP_OF_TROLLHEIM_MOUNTAIN = NullObjectID.NULL_29032;
public static final int FOUNTAIN_OF_HEROES = NullObjectID.NULL_29033;
public static final int ENTRANCE_OF_THE_CAVERN_UNDER_THE_WHIRLPOOL = NullObjectID.NULL_29034;
public static final int HALFWAY_DOWN_TROLLWEISS_MOUNTAIN = NullObjectID.NULL_29035;
public static final int SHAYZIEN_WAR_TENT = NullObjectID.NULL_29036;
public static final int OUTSIDE_THE_LEGENDS_GUILD_DOOR = NullObjectID.NULL_29037;
public static final int NEAR_THE_GEM_STALL_IN_ARDOUGNE_MARKET = NullObjectID.NULL_29038;
public static final int OUTSIDE_THE_BAR_BY_THE_FIGHT_ARENA = NullObjectID.NULL_29039;
public static final int SOUTHEAST_CORNER_OF_LAVA_DRAGON_ISLE = NullObjectID.NULL_29040;
public static final int NEAR_THE_PIER_IN_ZULANDRA = NullObjectID.NULL_29041;
public static final int BARROWS_CHEST = NullObjectID.NULL_29042;
public static final int WELL_OF_VOYAGE = NullObjectID.NULL_29043;
public static final int NORTHERN_WALL_OF_CASTLE_DRAKAN = NullObjectID.NULL_29044;
public static final int _7TH_CHAMBER_OF_JALSAVRAH = NullObjectID.NULL_29045;
public static final int SOUL_ALTAR = NullObjectID.NULL_29046;
public static final int WARRIORS_GUILD_BANK_29047 = NullObjectID.NULL_29047;
public static final int ENTRANA_CHAPEL = NullObjectID.NULL_29048;
public static final int TZHAAR_GEM_STORE = NullObjectID.NULL_29049;
public static final int TENT_IN_LORD_IORWERTHS_ENCAMPMENT = NullObjectID.NULL_29050;
public static final int OUTSIDE_MUDKNUCKLES_HUT = NullObjectID.NULL_29051;
public static final int CENTRE_OF_THE_CATACOMBS_OF_KOUREND = NullObjectID.NULL_29052;
public static final int KING_BLACK_DRAGONS_LAIR = NullObjectID.NULL_29053;
public static final int OUTSIDE_KRIL_TSUTSAROTHS_ROOM = NullObjectID.NULL_29054;
public static final int BY_THE_BEAR_CAGE_IN_VARROCK_PALACE_GARDENS = NullObjectID.NULL_29055;
public static final int OUTSIDE_THE_WILDERNESS_AXE_HUT = NullObjectID.NULL_29056;
public static final int TOP_FLOOR_OF_THE_YANILLE_WATCHTOWER = NullObjectID.NULL_29057;
public static final int DEATH_ALTAR = NullObjectID.NULL_29058;
public static final int BEHIND_MISS_SCHISM_IN_DRAYNOR_VILLAGE = NullObjectID.NULL_29059;
public static final int NORTHWESTERN_CORNER_OF_THE_ENCHANTED_VALLEY = NullObjectID.NULL_29060;
public static final int NORTH_OF_MOUNT_KARUULM = NullObjectID.NULL_34647;
public static final int GYPSY_TENT_ENTRANCE = NullObjectID.NULL_34736;
public static final int FINE_CLOTHES_ENTRANCE = NullObjectID.NULL_34737;
public static final int BOB_AXES_ENTRANCE = NullObjectID.NULL_34738;
NEAR_A_SHED_IN_LUMBRIDGE_SWAMP(NullObjectID.NULL_28958, new WorldPoint(3201, 3171, 0)),
ON_THE_BRIDGE_TO_THE_MISTHALIN_WIZARDS_TOWER(NullObjectID.NULL_28959, new WorldPoint(3115, 3194, 1)),
DRAYNOR_VILLAGE_MARKET(NullObjectID.NULL_28960, new WorldPoint(3083, 3254, 0)),
LIMESTONE_MINE(NullObjectID.NULL_28961, new WorldPoint(3373, 3498, 0)),
OUTSIDE_THE_LEGENDS_GUILD_GATES(NullObjectID.NULL_28962, new WorldPoint(2735, 3350, 0)),
MUDSKIPPER_POINT(NullObjectID.NULL_28963, new WorldPoint(2988, 3111, 0)),
NEAR_THE_ENTRANA_FERRY_IN_PORT_SARIM(NullObjectID.NULL_28964, new WorldPoint(3050, 3237, 1)),
AL_KHARID_SCORPION_MINE(NullObjectID.NULL_28965, new WorldPoint(3303, 3289, 0)),
DRAYNOR_MANOR_BY_THE_FOUNTAIN(NullObjectID.NULL_28966, new WorldPoint(3089, 3331, 0)),
WHEAT_FIELD_NEAR_THE_LUMBRIDGE_WINDMILL(NullObjectID.NULL_28967, new WorldPoint(3163, 3297, 0)),
CROSSROADS_NORTH_OF_DRAYNOR_VILLAGE(NullObjectID.NULL_28968, new WorldPoint(3111, 3289, 0)),
RIMMINGTON_MINE(NullObjectID.NULL_28969, new WorldPoint(2976, 3239, 0)),
VARROCK_PALACE_LIBRARY(NullObjectID.NULL_28970, new WorldPoint(3214, 3490, 0)),
UPSTAIRS_IN_THE_ARDOUGNE_WINDMILL(NullObjectID.NULL_28971, new WorldPoint(2635, 3386, 2)),
OUTSIDE_THE_FALADOR_PARTY_ROOM(NullObjectID.NULL_28972, new WorldPoint(3043, 3371, 0)),
TAVERLEY_STONE_CIRCLE(NullObjectID.NULL_28973, new WorldPoint(2924, 3477, 0)),
CATHERBY_BEEHIVE_FIELD(NullObjectID.NULL_28974, new WorldPoint(2764, 3438, 0)),
NEAR_THE_PARROTS_IN_ARDOUGNE_ZOO(NullObjectID.NULL_28975, new WorldPoint(2608, 3284, 0)),
ROAD_JUNCTION_NORTH_OF_RIMMINGTON(NullObjectID.NULL_28976, new WorldPoint(2981, 3278, 0)),
OUTSIDE_THE_FISHING_GUILD(NullObjectID.NULL_28977, new WorldPoint(2608, 3393, 0)),
OUTSIDE_KEEP_LE_FAYE(NullObjectID.NULL_28978, new WorldPoint(2756, 3399, 0)),
ROAD_JUNCTION_SOUTH_OF_SINCLAIR_MANSION(NullObjectID.NULL_28979, new WorldPoint(2735, 3534, 0)),
OUTSIDE_THE_DIGSITE_EXAM_CENTRE(NullObjectID.NULL_28980, new WorldPoint(3353, 3343, 0)),
NEAR_THE_SAWMILL_OPERATORS_BOOTH(NullObjectID.NULL_28981, new WorldPoint(3298, 3490, 0)),
MUBARIZS_ROOM_AT_THE_DUEL_ARENA(NullObjectID.NULL_28982, new WorldPoint(3316, 3242, 0)),
OUTSIDE_VARROCK_PALACE_COURTYARD(NullObjectID.NULL_28983, new WorldPoint(3211, 3456, 0)),
NEAR_HERQUINS_SHOP_IN_FALADOR(NullObjectID.NULL_28984, new WorldPoint(2941, 3339, 0)),
SOUTH_OF_THE_GRAND_EXCHANGE(NullObjectID.NULL_28985, new WorldPoint(3159, 3464, 0)),
AUBURYS_SHOP_IN_VARROCK(NullObjectID.NULL_28986, new WorldPoint(3252, 3404, 0)),
CENTRE_OF_CANIFIS(NullObjectID.NULL_28987, new WorldPoint(3491, 3489, 0)),
MAUSOLEUM_OFF_THE_MORYTANIA_COAST(NullObjectID.NULL_28988, new WorldPoint(3500, 3575, 0)),
EAST_OF_THE_BARBARIAN_VILLAGE_BRIDGE(NullObjectID.NULL_28989, new WorldPoint(3110, 3422, 0)),
SOUTH_OF_THE_SHRINE_IN_TAI_BWO_WANNAI_VILLAGE(NullObjectID.NULL_28990, new WorldPoint(2802, 3081, 0)),
CASTLE_WARS_BANK(NullObjectID.NULL_28991, new WorldPoint(2444, 3093, 0)),
BARBARIAN_OUTPOST_OBSTACLE_COURSE(NullObjectID.NULL_28992, new WorldPoint(2541, 3550, 0)),
GNOME_STRONGHOLD_BALANCING_ROPE(NullObjectID.NULL_28993, new WorldPoint(2473, 3418, 2)),
OUTSIDE_YANILLE_BANK(NullObjectID.NULL_28994, new WorldPoint(2603, 3091, 0)),
OBSERVATORY(NullObjectID.NULL_28995, new WorldPoint(2439, 3166, 0)),
OGRE_CAGE_IN_KING_LATHAS_TRAINING_CAMP(NullObjectID.NULL_28996, new WorldPoint(2533, 3377, 0)),
DIGSITE(NullObjectID.NULL_28997, new WorldPoint(3370, 3420, 0)),
HICKTONS_ARCHERY_EMPORIUM(NullObjectID.NULL_28998, new WorldPoint(2825, 3441, 0)),
SHANTAY_PASS(NullObjectID.NULL_28999, new WorldPoint(3308, 3125, 0)),
LUMBRIDGE_SWAMP_CAVES(NullObjectID.NULL_29000, new WorldPoint(3222, 9584, 0), new WorldPoint(3167, 9570, 0)),
OUTSIDE_CATHERBY_BANK(NullObjectID.NULL_29001, new WorldPoint(2807, 3437, 0)),
OUTSIDE_THE_SEERS_VILLAGE_COURTHOUSE(NullObjectID.NULL_29002, new WorldPoint(2731, 3475, 0)),
OUTSIDE_HARRYS_FISHING_SHOP_IN_CATHERBY(NullObjectID.NULL_29003, new WorldPoint(2837, 3436, 0)),
TZHAAR_WEAPONS_STORE(NullObjectID.NULL_29004, new WorldPoint(2479, 5146, 0)),
NORTH_OF_EVIL_DAVES_HOUSE_IN_EDGEVILLE(NullObjectID.NULL_29005, new WorldPoint(3077, 3503, 0)),
WEST_OF_THE_SHAYZIEN_COMBAT_RING(NullObjectID.NULL_29006, new WorldPoint(1534, 3591, 0)),
ENTRANCE_OF_THE_ARCEUUS_LIBRARY(NullObjectID.NULL_29007, new WorldPoint(1642, 3809, 0)),
OUTSIDE_DRAYNOR_VILLAGE_JAIL(NullObjectID.NULL_29008, new WorldPoint(3130, 3250, 0)),
CHAOS_TEMPLE_IN_THE_SOUTHEASTERN_WILDERNESS(NullObjectID.NULL_29009, new WorldPoint(3245, 3609, 0)),
FISHING_GUILD_BANK(NullObjectID.NULL_29010, new WorldPoint(2593, 3409, 0)),
TOP_FLOOR_OF_THE_LIGHTHOUSE(NullObjectID.NULL_29011, new WorldPoint(2512, 3640, 2)),
OUTSIDE_THE_GREAT_PYRAMID_OF_SOPHANEM(NullObjectID.NULL_29012, new WorldPoint(3291, 2780, 0)),
NOTERAZZOS_SHOP_IN_THE_WILDERNESS(NullObjectID.NULL_29013, new WorldPoint(3027, 3699, 0)),
WEST_SIDE_OF_THE_KARAMJA_BANANA_PLANTATION(NullObjectID.NULL_29014, new WorldPoint(2909, 3169, 0)),
MOUNTAIN_CAMP_GOAT_ENCLOSURE(NullObjectID.NULL_29015, new WorldPoint(2810, 3677, 0)),
GNOME_GLIDER_ON_WHITE_WOLF_MOUNTAIN(NullObjectID.NULL_29016, new WorldPoint(2849, 3496, 0)),
SHILO_VILLAGE_BANK(NullObjectID.NULL_29017, new WorldPoint(2853, 2952, 0)),
INSIDE_THE_DIGSITE_EXAM_CENTRE(NullObjectID.NULL_29018, new WorldPoint(3356, 3333, 0)),
NORTHEAST_CORNER_OF_THE_KHARAZI_JUNGLE(NullObjectID.NULL_29019, new WorldPoint(2952, 2932, 0)),
VOLCANO_IN_THE_NORTHEASTERN_WILDERNESS(NullObjectID.NULL_29020, new WorldPoint(3368, 3930, 0)),
IN_THE_MIDDLE_OF_JIGGIG(NullObjectID.NULL_29021, new WorldPoint(2478, 3048, 0)),
AGILITY_PYRAMID(NullObjectID.NULL_29022, new WorldPoint(3357, 2830, 0)),
HOSIDIUS_MESS(NullObjectID.NULL_29023, new WorldPoint(1648, 3631, 0)),
CHAPEL_IN_WEST_ARDOUGNE(NullObjectID.NULL_29024, new WorldPoint(2527, 3294, 0)),
NEAR_A_RUNITE_ROCK_IN_THE_FREMENNIK_ISLES(NullObjectID.NULL_29025, new WorldPoint(2374, 3847, 0)),
NEAR_A_LADDER_IN_THE_WILDERNESS_LAVA_MAZE(NullObjectID.NULL_29026, new WorldPoint(3069, 3862, 0)),
ENTRANCE_OF_THE_CAVE_OF_DAMIS(NullObjectID.NULL_29027, new WorldPoint(2629, 5070, 0)),
WARRIORS_GUILD_BANK(NullObjectID.NULL_29028, new WorldPoint(2844, 3537, 0)),
SOUTHEAST_CORNER_OF_THE_MONASTERY(NullObjectID.NULL_29029, new WorldPoint(3056, 3482, 0)),
SOUTHEAST_CORNER_OF_THE_FISHING_PLATFORM(NullObjectID.NULL_29030, new WorldPoint(2787, 3277, 1)),
OUTSIDE_THE_SLAYER_TOWER_GARGOYLE_ROOM(NullObjectID.NULL_29031, new WorldPoint(3423, 3534, 2)),
ON_TOP_OF_TROLLHEIM_MOUNTAIN(NullObjectID.NULL_29032, new WorldPoint(2886, 3676, 0)),
FOUNTAIN_OF_HEROES(NullObjectID.NULL_29033, new WorldPoint(2916, 9891, 0)),
ENTRANCE_OF_THE_CAVERN_UNDER_THE_WHIRLPOOL(NullObjectID.NULL_29034, new WorldPoint(1764, 5367, 1), new WorldPoint(1636, 5367, 1)),
HALFWAY_DOWN_TROLLWEISS_MOUNTAIN(NullObjectID.NULL_29035, new WorldPoint(2782, 3787, 0)),
SHAYZIEN_WAR_TENT(NullObjectID.NULL_29036, new WorldPoint(1550, 3541, 0)),
OUTSIDE_THE_LEGENDS_GUILD_DOOR(NullObjectID.NULL_29037, new WorldPoint(2727, 3371, 0)),
NEAR_THE_GEM_STALL_IN_ARDOUGNE_MARKET(NullObjectID.NULL_29038, new WorldPoint(2672, 3302, 0)),
OUTSIDE_THE_BAR_BY_THE_FIGHT_ARENA(NullObjectID.NULL_29039, new WorldPoint(2571, 3150, 0)),
SOUTHEAST_CORNER_OF_LAVA_DRAGON_ISLE(NullObjectID.NULL_29040, new WorldPoint(3228, 3830, 0)),
NEAR_THE_PIER_IN_ZULANDRA(NullObjectID.NULL_29041, new WorldPoint(2203, 3059, 0)),
BARROWS_CHEST(NullObjectID.NULL_29042, new WorldPoint(3547, 9690, 0)),
WELL_OF_VOYAGE(NullObjectID.NULL_29043, new WorldPoint(2006, 4709, 1)),
NORTHERN_WALL_OF_CASTLE_DRAKAN(NullObjectID.NULL_29044, new WorldPoint(3559, 3385, 1)),
_7TH_CHAMBER_OF_JALSAVRAH(NullObjectID.NULL_29045, new WorldPoint(1951, 4431, 0)),
SOUL_ALTAR(NullObjectID.NULL_29046, new WorldPoint(1810, 3855, 0)),
WARRIORS_GUILD_BANK_29047(NullObjectID.NULL_29047, new WorldPoint(2845, 3545, 0)),
ENTRANA_CHAPEL(NullObjectID.NULL_29048, new WorldPoint(2851, 3355, 0)),
TZHAAR_GEM_STORE(NullObjectID.NULL_29049, new WorldPoint(2466, 5150, 0)),
TENT_IN_LORD_IORWERTHS_ENCAMPMENT(NullObjectID.NULL_29050, new WorldPoint(2198, 3257, 0)),
OUTSIDE_MUDKNUCKLES_HUT(NullObjectID.NULL_29051, new WorldPoint(2959, 3502, 0)),
CENTRE_OF_THE_CATACOMBS_OF_KOUREND(NullObjectID.NULL_29052, new WorldPoint(1661, 10045, 0)),
KING_BLACK_DRAGONS_LAIR(NullObjectID.NULL_29053, new WorldPoint(2286, 4680, 0)),
OUTSIDE_KRIL_TSUTSAROTHS_ROOM(NullObjectID.NULL_29054, new WorldPoint(2931, 5337, 2)),
BY_THE_BEAR_CAGE_IN_VARROCK_PALACE_GARDENS(NullObjectID.NULL_29055, new WorldPoint(3232, 3494, 0)),
OUTSIDE_THE_WILDERNESS_AXE_HUT(NullObjectID.NULL_29056, new WorldPoint(3186, 3958, 0)),
TOP_FLOOR_OF_THE_YANILLE_WATCHTOWER(NullObjectID.NULL_29057, new WorldPoint(2930, 4718, 2)),
DEATH_ALTAR(NullObjectID.NULL_29058, new WorldPoint(2210, 4842, 0)),
BEHIND_MISS_SCHISM_IN_DRAYNOR_VILLAGE(NullObjectID.NULL_29059, new WorldPoint(3095, 3254, 0)),
NORTHWESTERN_CORNER_OF_THE_ENCHANTED_VALLEY(NullObjectID.NULL_29060, new WorldPoint(3022, 4517, 0)),
NORTH_OF_MOUNT_KARUULM(NullObjectID.NULL_34647, new WorldPoint(1308, 3840, 0)),
GYPSY_TENT_ENTRANCE(NullObjectID.NULL_34736, new WorldPoint(3206, 3422, 0)),
FINE_CLOTHES_ENTRANCE(NullObjectID.NULL_34737, new WorldPoint(3209, 3416, 0)),
BOB_AXES_ENTRANCE(NullObjectID.NULL_34738, new WorldPoint(3233, 3200, 0));
private final int objectId;
private final WorldPoint[] worldPoints;
STASHUnit(int objectId, WorldPoint... worldPoints)
{
this.objectId = objectId;
this.worldPoints = worldPoints;
}
}

View File

@@ -31,7 +31,11 @@ import java.time.Instant;
import javax.inject.Inject;
import lombok.AccessLevel;
import lombok.Getter;
import static net.runelite.api.AnimationID.COOKING_WINE;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.Player;
import net.runelite.api.events.AnimationChanged;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.GameTick;
import net.runelite.client.config.ConfigManager;
@@ -50,7 +54,8 @@ import net.runelite.client.ui.overlay.OverlayManager;
@PluginDependency(XpTrackerPlugin.class)
public class CookingPlugin extends Plugin
{
private static final String WINE_MESSAGE = "You squeeze the grapes into the jug";
@Inject
private Client client;
@Inject
private CookingConfig config;
@@ -124,6 +129,27 @@ public class CookingPlugin extends Plugin
}
}
@Subscribe
public void onAnimationChanged(AnimationChanged animationChanged)
{
Player localPlayer = client.getLocalPlayer();
if (localPlayer != animationChanged.getActor())
{
return;
}
if (localPlayer.getAnimation() == COOKING_WINE && config.fermentTimer())
{
if (fermentTimerSession == null)
{
fermentTimerSession = new FermentTimerSession();
}
fermentTimerSession.updateLastWineMakingAction();
}
}
@Subscribe
public void onChatMessage(ChatMessage event)
{
@@ -134,22 +160,11 @@ public class CookingPlugin extends Plugin
final String message = event.getMessage();
if (message.startsWith(WINE_MESSAGE) && config.fermentTimer())
{
if (fermentTimerSession == null)
{
fermentTimerSession = new FermentTimerSession();
}
fermentTimerSession.updateLastWineMakingAction();
}
if (message.startsWith("You successfully cook")
|| message.startsWith("You successfully bake")
|| message.startsWith("You manage to cook")
|| message.startsWith("You roast a")
|| message.startsWith("You cook")
|| message.startsWith(WINE_MESSAGE))
|| message.startsWith("You cook"))
{
if (cookingSession == null)
{

View File

@@ -1008,7 +1008,10 @@ public class MenuEntrySwapperPlugin extends Plugin
{
swap(client, "pay-toll(10gp)", option, target, true);
}
else if (config.swapHardWoodGrove() && option.equals("open") && target.equals("hardwood grove doors"))
{
swap(client, "quick-pay(100)", option, target, true);
}
else if (config.swapTravel() && option.equals("inspect") && target.equals("trapdoor"))
{
swap(client, "travel", option, target, true);

View File

@@ -0,0 +1,286 @@
/*
* Copyright (c) 2019, Anthony Chen <https://github.com/achencoms>
* 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.musiclist;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.stream.Collectors;
import javax.inject.Inject;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.ScriptID;
import net.runelite.api.SoundEffectID;
import net.runelite.api.SpriteID;
import net.runelite.api.VarClientInt;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.VarClientIntChanged;
import net.runelite.api.events.WidgetLoaded;
import net.runelite.api.widgets.JavaScriptCallback;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetID;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.api.widgets.WidgetPositionMode;
import net.runelite.api.widgets.WidgetType;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.game.chatbox.ChatboxPanelManager;
import net.runelite.client.game.chatbox.ChatboxTextInput;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
@PluginDescriptor(
name = "Music List",
description = "Adds search and filter for the music list"
)
public class MusicListPlugin extends Plugin
{
@Inject
private Client client;
@Inject
private ClientThread clientThread;
@Inject
private ChatboxPanelManager chatboxPanelManager;
private ChatboxTextInput searchInput;
private Widget musicSearchButton;
private Widget musicFilterButton;
private Collection<Widget> tracks;
private MusicState currentMusicFilter;
@Override
protected void startUp()
{
clientThread.invoke(this::addMusicButtons);
}
@Override
protected void shutDown()
{
Widget header = client.getWidget(WidgetInfo.MUSIC_WINDOW);
if (header != null)
{
header.deleteAllChildren();
}
tracks = null;
}
@Subscribe
public void onGameStateChanged(GameStateChanged gameStateChanged)
{
if (gameStateChanged.getGameState() == GameState.LOGIN_SCREEN)
{
currentMusicFilter = MusicState.ALL;
tracks = null;
}
}
@Subscribe
public void onWidgetLoaded(WidgetLoaded widgetLoaded)
{
if (widgetLoaded.getGroupId() == WidgetID.MUSIC_GROUP_ID)
{
addMusicButtons();
}
}
private void addMusicButtons()
{
Widget header = client.getWidget(WidgetInfo.MUSIC_WINDOW);
if (header == null)
{
return;
}
//Creation of the search and toggle status buttons
musicSearchButton = header.createChild(-1, WidgetType.GRAPHIC);
musicSearchButton.setSpriteId(SpriteID.GE_SEARCH);
musicSearchButton.setOriginalWidth(18);
musicSearchButton.setOriginalHeight(17);
musicSearchButton.setXPositionMode(WidgetPositionMode.ABSOLUTE_RIGHT);
musicSearchButton.setOriginalX(5);
musicSearchButton.setOriginalY(32);
musicSearchButton.setHasListener(true);
musicSearchButton.setAction(1, "Open");
musicSearchButton.setOnOpListener((JavaScriptCallback) e -> openSearch());
musicSearchButton.setName("Search");
musicSearchButton.revalidate();
musicFilterButton = header.createChild(-1, WidgetType.GRAPHIC);
musicFilterButton.setSpriteId(SpriteID.MINIMAP_ORB_HITPOINTS);
musicFilterButton.setOriginalWidth(15);
musicFilterButton.setOriginalHeight(15);
musicFilterButton.setXPositionMode(WidgetPositionMode.ABSOLUTE_RIGHT);
musicFilterButton.setOriginalX(25);
musicFilterButton.setOriginalY(34);
musicFilterButton.setHasListener(true);
musicFilterButton.setAction(1, "Toggle");
musicFilterButton.setOnOpListener((JavaScriptCallback) e -> toggleStatus());
musicFilterButton.setName("All");
musicFilterButton.revalidate();
}
@Subscribe
public void onVarClientIntChanged(VarClientIntChanged varClientIntChanged)
{
if (isChatboxOpen() && !isOnMusicTab())
{
chatboxPanelManager.close();
}
}
private boolean isOnMusicTab()
{
return client.getVar(VarClientInt.INVENTORY_TAB) == 13;
}
private boolean isChatboxOpen()
{
return searchInput != null && chatboxPanelManager.getCurrentInput() == searchInput;
}
private String getChatboxInput()
{
return isChatboxOpen() ? searchInput.getValue() : "";
}
private void toggleStatus()
{
MusicState[] states = MusicState.values();
currentMusicFilter = states[(currentMusicFilter.ordinal() + 1) % states.length];
musicFilterButton.setSpriteId(currentMusicFilter.getSpriteID());
musicFilterButton.setName(currentMusicFilter.getName());
updateFilter(getChatboxInput());
client.playSoundEffect(SoundEffectID.UI_BOOP);
}
private void openSearch()
{
updateFilter("");
client.playSoundEffect(SoundEffectID.UI_BOOP);
musicSearchButton.setAction(1, "Close");
musicSearchButton.setOnOpListener((JavaScriptCallback) e -> closeSearch());
searchInput = chatboxPanelManager.openTextInput("Search music list")
.onChanged(s -> clientThread.invokeLater(() -> updateFilter(s.trim())))
.onClose(() ->
{
clientThread.invokeLater(() -> updateFilter(""));
musicSearchButton.setOnOpListener((JavaScriptCallback) e -> openSearch());
musicSearchButton.setAction(1, "Open");
})
.build();
}
private void closeSearch()
{
updateFilter("");
chatboxPanelManager.close();
client.playSoundEffect(SoundEffectID.UI_BOOP);
}
private void updateFilter(String input)
{
final Widget container = client.getWidget(WidgetInfo.MUSIC_WINDOW);
final Widget musicList = client.getWidget(WidgetInfo.MUSIC_TRACK_LIST);
if (container == null || musicList == null)
{
return;
}
String filter = input.toLowerCase();
updateList(musicList, filter);
}
private void updateList(Widget musicList, String filter)
{
if (tracks == null)
{
tracks = Arrays.stream(musicList.getDynamicChildren())
.sorted(Comparator.comparing(Widget::getRelativeY))
.collect(Collectors.toList());
}
tracks.forEach(w -> w.setHidden(true));
Collection<Widget> relevantTracks = tracks.stream()
.filter(w -> w.getText().toLowerCase().contains(filter))
.filter(w -> currentMusicFilter == MusicState.ALL || w.getTextColor() == currentMusicFilter.getColor())
.collect(Collectors.toList());
// Original music track list has a little offset
int y = 3;
for (Widget track : relevantTracks)
{
track.setHidden(false);
track.setOriginalY(y);
track.revalidate();
y += track.getHeight();
}
y += 3;
int newHeight = 0;
if (musicList.getScrollHeight() > 0)
{
newHeight = (musicList.getScrollY() * y) / musicList.getScrollHeight();
}
musicList.setScrollHeight(y);
musicList.revalidateScroll();
client.runScript(
ScriptID.UPDATE_SCROLLBAR,
WidgetInfo.MUSIC_TRACK_SCROLLBAR.getId(),
WidgetInfo.MUSIC_TRACK_LIST.getId(),
newHeight
);
}
@AllArgsConstructor
@Getter
private enum MusicState
{
NOT_FOUND(0xff0000, "Locked", SpriteID.MINIMAP_ORB_HITPOINTS),
FOUND(0xdc10d, "Unlocked", SpriteID.MINIMAP_ORB_HITPOINTS_POISON),
ALL(0, "All", SpriteID.MINIMAP_ORB_PRAYER);
private final int color;
private final String name;
private final int spriteID;
}
}

View File

@@ -1,398 +1,419 @@
/*
* Copyright (c) 2019 Spudjb <https://github.com/spudjb>
* 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.questlist;
import com.google.common.collect.ImmutableList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.EnumMap;
import java.util.List;
import java.util.stream.Collectors;
import javax.inject.Inject;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.ScriptID;
import net.runelite.api.SoundEffectID;
import net.runelite.api.SpriteID;
import net.runelite.api.VarClientInt;
import net.runelite.api.Varbits;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.ScriptCallbackEvent;
import net.runelite.api.events.VarClientIntChanged;
import net.runelite.api.events.VarbitChanged;
import net.runelite.api.widgets.JavaScriptCallback;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.api.widgets.WidgetPositionMode;
import net.runelite.api.widgets.WidgetType;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.game.chatbox.ChatboxPanelManager;
import net.runelite.client.game.chatbox.ChatboxTextInput;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.util.Text;
@PluginDescriptor(
name = "Quest List",
description = "Adds searching and filtering to the quest list"
)
public class QuestListPlugin extends Plugin
{
private static final int ENTRY_PADDING = 8;
private static final List<String> QUEST_HEADERS = ImmutableList.of("Free Quests", "Members' Quests", "Miniquests");
private static final String MENU_OPEN = "Open";
private static final String MENU_CLOSE = "Close";
private static final String MENU_TOGGLE = "Toggle";
private static final String MENU_SEARCH = "Search";
private static final String MENU_SHOW = "Show";
@Inject
private Client client;
@Inject
private ChatboxPanelManager chatboxPanelManager;
@Inject
private ClientThread clientThread;
private ChatboxTextInput searchInput;
private Widget questSearchButton;
private Widget questHideButton;
private EnumMap<QuestContainer, Collection<QuestWidget>> questSet;
private QuestState currentFilterState;
@Subscribe
public void onGameStateChanged(GameStateChanged e)
{
if (e.getGameState() == GameState.LOGGING_IN)
{
currentFilterState = QuestState.ALL;
}
}
@Subscribe
public void onScriptCallbackEvent(ScriptCallbackEvent event)
{
if (!event.getEventName().equals("questProgressUpdated"))
{
return;
}
Widget header = client.getWidget(WidgetInfo.QUESTLIST_BOX);
if (header != null)
{
questSearchButton = header.createChild(-1, WidgetType.GRAPHIC);
questSearchButton.setSpriteId(SpriteID.GE_SEARCH);
questSearchButton.setOriginalWidth(18);
questSearchButton.setOriginalHeight(17);
questSearchButton.setXPositionMode(WidgetPositionMode.ABSOLUTE_RIGHT);
questSearchButton.setOriginalX(5);
questSearchButton.setOriginalY(0);
questSearchButton.setHasListener(true);
questSearchButton.setAction(1, MENU_OPEN);
questSearchButton.setOnOpListener((JavaScriptCallback) e -> openSearch());
questSearchButton.setName(MENU_SEARCH);
questSearchButton.revalidate();
questHideButton = header.createChild(-1, WidgetType.GRAPHIC);
redrawHideButton();
questHideButton.setOriginalWidth(13);
questHideButton.setOriginalHeight(13);
questHideButton.setXPositionMode(WidgetPositionMode.ABSOLUTE_RIGHT);
questHideButton.setOriginalX(24);
questHideButton.setOriginalY(2);
questHideButton.setHasListener(true);
questHideButton.setOnOpListener((JavaScriptCallback) e -> toggleHidden());
questHideButton.setAction(1, MENU_TOGGLE);
questHideButton.revalidate();
questSet = new EnumMap<>(QuestContainer.class);
updateFilter();
}
}
@Subscribe
public void onVarbitChanged(VarbitChanged varbitChanged)
{
if (isChatboxOpen() && !isOnQuestTab())
{
chatboxPanelManager.close();
}
}
@Subscribe
public void onVarClientIntChanged(VarClientIntChanged varClientIntChanged)
{
if (varClientIntChanged.getIndex() == VarClientInt.INVENTORY_TAB.getIndex())
{
if (isChatboxOpen() && !isOnQuestTab())
{
chatboxPanelManager.close();
}
}
}
private void toggleHidden()
{
QuestState[] questStates = QuestState.values();
int nextState = (currentFilterState.ordinal() + 1) % questStates.length;
currentFilterState = questStates[nextState];
redrawHideButton();
updateFilter();
client.playSoundEffect(SoundEffectID.UI_BOOP);
}
private void redrawHideButton()
{
questHideButton.setSpriteId(currentFilterState.getSpriteId());
questHideButton.setName(MENU_SHOW + " " + currentFilterState.getName());
}
private boolean isOnQuestTab()
{
return client.getVar(Varbits.QUEST_TAB) == 0 && client.getVar(VarClientInt.INVENTORY_TAB) == 2;
}
private boolean isChatboxOpen()
{
return searchInput != null && chatboxPanelManager.getCurrentInput() == searchInput;
}
private void closeSearch()
{
updateFilter("");
chatboxPanelManager.close();
client.playSoundEffect(SoundEffectID.UI_BOOP);
}
private void openSearch()
{
updateFilter("");
client.playSoundEffect(SoundEffectID.UI_BOOP);
questSearchButton.setAction(1, MENU_CLOSE);
questSearchButton.setOnOpListener((JavaScriptCallback) e -> closeSearch());
searchInput = chatboxPanelManager.openTextInput("Search quest list")
.onChanged(s -> clientThread.invokeLater(() -> updateFilter(s)))
.onClose(() ->
{
clientThread.invokeLater(() -> updateFilter(""));
questSearchButton.setOnOpListener((JavaScriptCallback) e -> openSearch());
questSearchButton.setAction(1, MENU_OPEN);
})
.build();
}
private void updateFilter()
{
String filter = "";
if (isChatboxOpen())
{
filter = searchInput.getValue();
}
updateFilter(filter);
}
private void updateFilter(String filter)
{
filter = filter.toLowerCase();
final Widget container = client.getWidget(WidgetInfo.QUESTLIST_CONTAINER);
final Widget freeList = client.getWidget(QuestContainer.FREE_QUESTS.widgetInfo);
final Widget memberList = client.getWidget(QuestContainer.MEMBER_QUESTS.widgetInfo);
final Widget miniList = client.getWidget(QuestContainer.MINI_QUESTS.widgetInfo);
if (container == null || freeList == null || memberList == null || miniList == null)
{
return;
}
updateList(QuestContainer.FREE_QUESTS, filter);
updateList(QuestContainer.MEMBER_QUESTS, filter);
updateList(QuestContainer.MINI_QUESTS, filter);
memberList.setOriginalY(freeList.getOriginalY() + freeList.getOriginalHeight() + ENTRY_PADDING);
miniList.setOriginalY(memberList.getOriginalY() + memberList.getOriginalHeight() + ENTRY_PADDING);
// originalHeight is changed within updateList so revalidate all lists
freeList.revalidate();
memberList.revalidate();
miniList.revalidate();
int y = miniList.getRelativeY() + miniList.getHeight() + 10;
int newHeight;
if (container.getScrollHeight() > 0)
{
newHeight = (container.getScrollY() * y) / container.getScrollHeight();
}
else
{
newHeight = 0;
}
container.setScrollHeight(y);
container.revalidateScroll();
clientThread.invokeLater(() ->
client.runScript(
ScriptID.UPDATE_SCROLLBAR,
WidgetInfo.QUESTLIST_SCROLLBAR.getId(),
WidgetInfo.QUESTLIST_CONTAINER.getId(),
newHeight
));
}
private void updateList(QuestContainer questContainer, String filter)
{
Widget list = client.getWidget(questContainer.widgetInfo);
if (list == null)
{
return;
}
Collection<QuestWidget> quests = questSet.get(questContainer);
if (quests != null)
{
// Check to make sure the list hasn't been rebuild since we were last her
// Do this by making sure the list's dynamic children are the same as when we last saw them
if (quests.stream().noneMatch(w ->
{
Widget codeWidget = w.getQuest();
if (codeWidget == null)
{
return false;
}
return list.getChild(codeWidget.getIndex()) == codeWidget;
}))
{
quests = null;
}
}
if (quests == null)
{
// Find all of the widgets that we care about, sorting by their Y value
quests = Arrays.stream(list.getDynamicChildren())
.sorted(Comparator.comparing(Widget::getRelativeY))
.filter(w -> !QUEST_HEADERS.contains(w.getText()))
.map(w -> new QuestWidget(w, Text.removeTags(w.getText()).toLowerCase()))
.collect(Collectors.toList());
questSet.put(questContainer, quests);
}
// offset because of header
int y = 20;
for (QuestWidget questInfo : quests)
{
Widget quest = questInfo.getQuest();
QuestState questState = QuestState.getByColor(quest.getTextColor());
boolean hidden;
if (!filter.isEmpty())
{
// If searching, show result regardless of filtered state
hidden = !questInfo.getTitle().contains(filter);
}
else
{
// Otherwise hide if it doesn't match the filter state
hidden = currentFilterState != QuestState.ALL && questState != currentFilterState;
}
quest.setHidden(hidden);
quest.setOriginalY(y);
quest.revalidate();
if (!hidden)
{
y += quest.getHeight();
}
}
list.setOriginalHeight(y);
}
@AllArgsConstructor
@Getter
private enum QuestContainer
{
FREE_QUESTS(WidgetInfo.QUESTLIST_FREE_CONTAINER),
MEMBER_QUESTS(WidgetInfo.QUESTLIST_MEMBERS_CONTAINER),
MINI_QUESTS(WidgetInfo.QUESTLIST_MINIQUEST_CONTAINER);
private final WidgetInfo widgetInfo;
}
@AllArgsConstructor
@Getter
private enum QuestState
{
NOT_STARTED(0xff0000, "Not started", SpriteID.MINIMAP_ORB_HITPOINTS),
IN_PROGRESS(0xffff00, "In progress", SpriteID.MINIMAP_ORB_HITPOINTS_DISEASE),
COMPLETE(0xdc10d, "Completed", SpriteID.MINIMAP_ORB_HITPOINTS_POISON),
ALL(0, "All", SpriteID.MINIMAP_ORB_PRAYER);
private final int color;
private final String name;
private final int spriteId;
static QuestState getByColor(int color)
{
for (QuestState value : values())
{
if (value.getColor() == color)
{
return value;
}
}
return null;
}
}
@Data
@AllArgsConstructor
private static class QuestWidget
{
private Widget quest;
private String title;
}
}
/*
* Copyright (c) 2019 Spudjb <https://github.com/spudjb>
* 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.questlist;
import com.google.common.collect.ImmutableList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.EnumMap;
import java.util.List;
import java.util.stream.Collectors;
import javax.inject.Inject;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.ScriptID;
import net.runelite.api.SoundEffectID;
import net.runelite.api.SpriteID;
import net.runelite.api.VarClientInt;
import net.runelite.api.Varbits;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.ScriptCallbackEvent;
import net.runelite.api.events.VarClientIntChanged;
import net.runelite.api.events.VarbitChanged;
import net.runelite.api.widgets.JavaScriptCallback;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.api.widgets.WidgetPositionMode;
import net.runelite.api.widgets.WidgetType;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.game.chatbox.ChatboxPanelManager;
import net.runelite.client.game.chatbox.ChatboxTextInput;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.util.Text;
@PluginDescriptor(
name = "Quest List",
description = "Adds searching and filtering to the quest list"
)
public class QuestListPlugin extends Plugin
{
private static final int ENTRY_PADDING = 8;
private static final List<String> QUEST_HEADERS = ImmutableList.of("Free Quests", "Members' Quests", "Miniquests");
private static final String MENU_OPEN = "Open";
private static final String MENU_CLOSE = "Close";
private static final String MENU_TOGGLE = "Toggle";
private static final String MENU_SEARCH = "Search";
private static final String MENU_SHOW = "Show";
@Inject
private Client client;
@Inject
private ChatboxPanelManager chatboxPanelManager;
@Inject
private ClientThread clientThread;
private ChatboxTextInput searchInput;
private Widget questSearchButton;
private Widget questHideButton;
private EnumMap<QuestContainer, Collection<QuestWidget>> questSet;
private QuestState currentFilterState;
@Override
protected void startUp()
{
clientThread.invoke(this::addQuestButtons);
}
@Override
protected void shutDown()
{
Widget header = client.getWidget(WidgetInfo.QUESTLIST_BOX);
if (header != null)
{
header.deleteAllChildren();
}
}
@Subscribe
public void onGameStateChanged(GameStateChanged e)
{
if (e.getGameState() == GameState.LOGGING_IN)
{
currentFilterState = QuestState.ALL;
}
}
@Subscribe
public void onScriptCallbackEvent(ScriptCallbackEvent event)
{
if (!event.getEventName().equals("questProgressUpdated"))
{
return;
}
addQuestButtons();
}
private void addQuestButtons()
{
Widget header = client.getWidget(WidgetInfo.QUESTLIST_BOX);
if (header != null)
{
questSearchButton = header.createChild(-1, WidgetType.GRAPHIC);
questSearchButton.setSpriteId(SpriteID.GE_SEARCH);
questSearchButton.setOriginalWidth(18);
questSearchButton.setOriginalHeight(17);
questSearchButton.setXPositionMode(WidgetPositionMode.ABSOLUTE_RIGHT);
questSearchButton.setOriginalX(5);
questSearchButton.setOriginalY(0);
questSearchButton.setHasListener(true);
questSearchButton.setAction(1, MENU_OPEN);
questSearchButton.setOnOpListener((JavaScriptCallback) e -> openSearch());
questSearchButton.setName(MENU_SEARCH);
questSearchButton.revalidate();
questHideButton = header.createChild(-1, WidgetType.GRAPHIC);
redrawHideButton();
questHideButton.setOriginalWidth(13);
questHideButton.setOriginalHeight(13);
questHideButton.setXPositionMode(WidgetPositionMode.ABSOLUTE_RIGHT);
questHideButton.setOriginalX(24);
questHideButton.setOriginalY(2);
questHideButton.setHasListener(true);
questHideButton.setOnOpListener((JavaScriptCallback) e -> toggleHidden());
questHideButton.setAction(1, MENU_TOGGLE);
questHideButton.revalidate();
questSet = new EnumMap<>(QuestContainer.class);
updateFilter();
}
}
@Subscribe
public void onVarbitChanged(VarbitChanged varbitChanged)
{
if (isChatboxOpen() && !isOnQuestTab())
{
chatboxPanelManager.close();
}
}
@Subscribe
public void onVarClientIntChanged(VarClientIntChanged varClientIntChanged)
{
if (varClientIntChanged.getIndex() == VarClientInt.INVENTORY_TAB.getIndex())
{
if (isChatboxOpen() && !isOnQuestTab())
{
chatboxPanelManager.close();
}
}
}
private void toggleHidden()
{
QuestState[] questStates = QuestState.values();
int nextState = (currentFilterState.ordinal() + 1) % questStates.length;
currentFilterState = questStates[nextState];
redrawHideButton();
updateFilter();
client.playSoundEffect(SoundEffectID.UI_BOOP);
}
private void redrawHideButton()
{
questHideButton.setSpriteId(currentFilterState.getSpriteId());
questHideButton.setName(MENU_SHOW + " " + currentFilterState.getName());
}
private boolean isOnQuestTab()
{
return client.getVar(Varbits.QUEST_TAB) == 0 && client.getVar(VarClientInt.INVENTORY_TAB) == 2;
}
private boolean isChatboxOpen()
{
return searchInput != null && chatboxPanelManager.getCurrentInput() == searchInput;
}
private void closeSearch()
{
updateFilter("");
chatboxPanelManager.close();
client.playSoundEffect(SoundEffectID.UI_BOOP);
}
private void openSearch()
{
updateFilter("");
client.playSoundEffect(SoundEffectID.UI_BOOP);
questSearchButton.setAction(1, MENU_CLOSE);
questSearchButton.setOnOpListener((JavaScriptCallback) e -> closeSearch());
searchInput = chatboxPanelManager.openTextInput("Search quest list")
.onChanged(s -> clientThread.invokeLater(() -> updateFilter(s)))
.onClose(() ->
{
clientThread.invokeLater(() -> updateFilter(""));
questSearchButton.setOnOpListener((JavaScriptCallback) e -> openSearch());
questSearchButton.setAction(1, MENU_OPEN);
})
.build();
}
private void updateFilter()
{
String filter = "";
if (isChatboxOpen())
{
filter = searchInput.getValue();
}
updateFilter(filter);
}
private void updateFilter(String filter)
{
filter = filter.toLowerCase();
final Widget container = client.getWidget(WidgetInfo.QUESTLIST_CONTAINER);
final Widget freeList = client.getWidget(QuestContainer.FREE_QUESTS.widgetInfo);
final Widget memberList = client.getWidget(QuestContainer.MEMBER_QUESTS.widgetInfo);
final Widget miniList = client.getWidget(QuestContainer.MINI_QUESTS.widgetInfo);
if (container == null || freeList == null || memberList == null || miniList == null)
{
return;
}
updateList(QuestContainer.FREE_QUESTS, filter);
updateList(QuestContainer.MEMBER_QUESTS, filter);
updateList(QuestContainer.MINI_QUESTS, filter);
memberList.setOriginalY(freeList.getOriginalY() + freeList.getOriginalHeight() + ENTRY_PADDING);
miniList.setOriginalY(memberList.getOriginalY() + memberList.getOriginalHeight() + ENTRY_PADDING);
// originalHeight is changed within updateList so revalidate all lists
freeList.revalidate();
memberList.revalidate();
miniList.revalidate();
int y = miniList.getRelativeY() + miniList.getHeight() + 10;
int newHeight;
if (container.getScrollHeight() > 0)
{
newHeight = (container.getScrollY() * y) / container.getScrollHeight();
}
else
{
newHeight = 0;
}
container.setScrollHeight(y);
container.revalidateScroll();
clientThread.invokeLater(() ->
client.runScript(
ScriptID.UPDATE_SCROLLBAR,
WidgetInfo.QUESTLIST_SCROLLBAR.getId(),
WidgetInfo.QUESTLIST_CONTAINER.getId(),
newHeight
));
}
private void updateList(QuestContainer questContainer, String filter)
{
Widget list = client.getWidget(questContainer.widgetInfo);
if (list == null)
{
return;
}
Collection<QuestWidget> quests = questSet.get(questContainer);
if (quests != null)
{
// Check to make sure the list hasn't been rebuild since we were last her
// Do this by making sure the list's dynamic children are the same as when we last saw them
if (quests.stream().noneMatch(w ->
{
Widget codeWidget = w.getQuest();
if (codeWidget == null)
{
return false;
}
return list.getChild(codeWidget.getIndex()) == codeWidget;
}))
{
quests = null;
}
}
if (quests == null)
{
// Find all of the widgets that we care about, sorting by their Y value
quests = Arrays.stream(list.getDynamicChildren())
.sorted(Comparator.comparing(Widget::getRelativeY))
.filter(w -> !QUEST_HEADERS.contains(w.getText()))
.map(w -> new QuestWidget(w, Text.removeTags(w.getText()).toLowerCase()))
.collect(Collectors.toList());
questSet.put(questContainer, quests);
}
// offset because of header
int y = 20;
for (QuestWidget questInfo : quests)
{
Widget quest = questInfo.getQuest();
QuestState questState = QuestState.getByColor(quest.getTextColor());
boolean hidden;
if (!filter.isEmpty())
{
// If searching, show result regardless of filtered state
hidden = !questInfo.getTitle().contains(filter);
}
else
{
// Otherwise hide if it doesn't match the filter state
hidden = currentFilterState != QuestState.ALL && questState != currentFilterState;
}
quest.setHidden(hidden);
quest.setOriginalY(y);
quest.revalidate();
if (!hidden)
{
y += quest.getHeight();
}
}
list.setOriginalHeight(y);
}
@AllArgsConstructor
@Getter
private enum QuestContainer
{
FREE_QUESTS(WidgetInfo.QUESTLIST_FREE_CONTAINER),
MEMBER_QUESTS(WidgetInfo.QUESTLIST_MEMBERS_CONTAINER),
MINI_QUESTS(WidgetInfo.QUESTLIST_MINIQUEST_CONTAINER);
private final WidgetInfo widgetInfo;
}
@AllArgsConstructor
@Getter
private enum QuestState
{
NOT_STARTED(0xff0000, "Not started", SpriteID.MINIMAP_ORB_HITPOINTS),
IN_PROGRESS(0xffff00, "In progress", SpriteID.MINIMAP_ORB_HITPOINTS_DISEASE),
COMPLETE(0xdc10d, "Completed", SpriteID.MINIMAP_ORB_HITPOINTS_POISON),
ALL(0, "All", SpriteID.MINIMAP_ORB_PRAYER);
private final int color;
private final String name;
private final int spriteId;
static QuestState getByColor(int color)
{
for (QuestState value : values())
{
if (value.getColor() == color)
{
return value;
}
}
return null;
}
}
@Data
@AllArgsConstructor
private static class QuestWidget
{
private Widget quest;
private String title;
}
}

View File

@@ -36,6 +36,7 @@ import net.runelite.api.InventoryID;
import net.runelite.api.Item;
import net.runelite.api.ItemContainer;
import net.runelite.api.NPC;
import net.runelite.api.NPCComposition;
import net.runelite.api.Player;
import net.runelite.api.Skill;
import net.runelite.api.VarPlayer;
@@ -51,6 +52,7 @@ import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
import net.runelite.client.ws.PartyService;
import net.runelite.client.ws.WSClient;
import org.apache.commons.lang3.ArrayUtils;
@PluginDescriptor(
name = "Special Attack Counter",
@@ -181,7 +183,16 @@ public class SpecialCounterPlugin extends Plugin
if (interacting instanceof NPC)
{
int interactingId = ((NPC) interacting).getId();
NPC npc = (NPC) interacting;
NPCComposition composition = npc.getComposition();
int interactingId = npc.getId();
if (!ArrayUtils.contains(composition.getActions(), "Attack"))
{
// Skip over non attackable npcs so that eg. talking to bankers doesn't reset
// the counters.
return -1;
}
if (!interactedNpcIds.contains(interactingId))
{

View File

@@ -1,309 +1,314 @@
/*
* Copyright (c) 2018 Abex
* 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.timetracking.farming;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.time.Instant;
import java.util.EnumMap;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import net.runelite.api.Client;
import net.runelite.api.Varbits;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.vars.Autoweed;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.timetracking.SummaryState;
import net.runelite.client.plugins.timetracking.Tab;
import net.runelite.client.plugins.timetracking.TimeTrackingConfig;
@Singleton
public class FarmingTracker
{
private final Client client;
private final ItemManager itemManager;
private final ConfigManager configManager;
private final TimeTrackingConfig config;
private final FarmingWorld farmingWorld;
private final Map<Tab, SummaryState> summaries = new EnumMap<>(Tab.class);
/**
* The time at which all patches of a particular type will be ready to be harvested,
* or {@code -1} if we have no data about any patch of the given type.
*/
private final Map<Tab, Long> completionTimes = new EnumMap<>(Tab.class);
@Inject
private FarmingTracker(Client client, ItemManager itemManager, ConfigManager configManager,
TimeTrackingConfig config, FarmingWorld farmingWorld)
{
this.client = client;
this.itemManager = itemManager;
this.configManager = configManager;
this.config = config;
this.farmingWorld = farmingWorld;
}
public FarmingTabPanel createTabPanel(Tab tab)
{
return new FarmingTabPanel(this, itemManager, config, farmingWorld.getTabs().get(tab));
}
/**
* Updates tracker data for the current region. Returns true if any data was changed.
*/
public boolean updateData(WorldPoint location)
{
boolean changed = false;
{
String group = TimeTrackingConfig.CONFIG_GROUP + "." + client.getUsername();
String autoweed = Integer.toString(client.getVar(Varbits.AUTOWEED));
if (!autoweed.equals(configManager.getConfiguration(group, TimeTrackingConfig.AUTOWEED)))
{
configManager.setConfiguration(group, TimeTrackingConfig.AUTOWEED, autoweed);
changed = true;
}
}
FarmingRegion region = farmingWorld.getRegions().get(location.getRegionID());
if (region != null && region.isInBounds(location))
{
// Write config with new varbits
// timetracking.<login-username>.<regionID>.<VarbitID>=<varbitValue>:<unix time>
String group = TimeTrackingConfig.CONFIG_GROUP + "." + client.getUsername() + "." + region.getRegionID();
long unixNow = Instant.now().getEpochSecond();
for (FarmingPatch patch : region.getPatches())
{
// Write the config value if it doesn't match what is current, or it is more than 5 minutes old
Varbits varbit = patch.getVarbit();
String key = Integer.toString(varbit.getId());
String strVarbit = Integer.toString(client.getVar(varbit));
String storedValue = configManager.getConfiguration(group, key);
if (storedValue != null)
{
String[] parts = storedValue.split(":");
if (parts.length == 2 && parts[0].equals(strVarbit))
{
long unixTime = 0;
try
{
unixTime = Long.parseLong(parts[1]);
}
catch (NumberFormatException e)
{
// ignored
}
if (unixTime + (5 * 60) > unixNow && unixNow + 30 > unixTime)
{
continue;
}
}
}
String value = strVarbit + ":" + unixNow;
configManager.setConfiguration(group, key, value);
changed = true;
}
}
if (changed)
{
updateCompletionTime();
}
return changed;
}
@Nullable
public PatchPrediction predictPatch(FarmingPatch patch)
{
long unixNow = Instant.now().getEpochSecond();
boolean autoweed;
{
String group = TimeTrackingConfig.CONFIG_GROUP + "." + client.getUsername();
autoweed = Integer.toString(Autoweed.ON.ordinal())
.equals(configManager.getConfiguration(group, TimeTrackingConfig.AUTOWEED));
}
String group = TimeTrackingConfig.CONFIG_GROUP + "." + client.getUsername() + "." + patch.getRegion().getRegionID();
String key = Integer.toString(patch.getVarbit().getId());
String storedValue = configManager.getConfiguration(group, key);
if (storedValue == null)
{
return null;
}
long unixTime = 0;
int value = 0;
{
String[] parts = storedValue.split(":");
if (parts.length == 2)
{
try
{
value = Integer.parseInt(parts[0]);
unixTime = Long.parseLong(parts[1]);
}
catch (NumberFormatException e)
{
}
}
}
if (unixTime <= 0)
{
return null;
}
PatchState state = patch.getImplementation().forVarbitValue(value);
int stage = state.getStage();
int stages = state.getStages();
int tickrate = state.getTickRate() * 60;
if (autoweed && state.getProduce() == Produce.WEEDS)
{
stage = 0;
stages = 1;
tickrate = 0;
}
long doneEstimate = 0;
if (tickrate > 0)
{
long tickNow = (unixNow + (5 * 60)) / tickrate;
long tickTime = (unixTime + (5 * 60)) / tickrate;
int delta = (int) (tickNow - tickTime);
doneEstimate = ((stages - 1 - stage) + tickTime) * tickrate + (5 * 60);
stage += delta;
if (stage >= stages)
{
stage = stages - 1;
}
}
return new PatchPrediction(
state.getProduce(),
state.getCropState(),
doneEstimate,
stage,
stages
);
}
public void loadCompletionTimes()
{
summaries.clear();
completionTimes.clear();
updateCompletionTime();
}
public SummaryState getSummary(Tab patchType)
{
SummaryState summary = summaries.get(patchType);
return summary == null ? SummaryState.UNKNOWN : summary;
}
/**
* Gets the overall completion time for the given patch type.
*
* @see #completionTimes
*/
public long getCompletionTime(Tab patchType)
{
Long completionTime = completionTimes.get(patchType);
return completionTime == null ? -1 : completionTime;
}
/**
* Updates the overall completion time for the given patch type.
*
* @see #completionTimes
*/
private void updateCompletionTime()
{
for (Map.Entry<Tab, Set<FarmingPatch>> tab : farmingWorld.getTabs().entrySet())
{
long maxCompletionTime = 0;
boolean allUnknown = true;
boolean allEmpty = true;
for (FarmingPatch patch : tab.getValue())
{
PatchPrediction prediction = predictPatch(patch);
if (prediction == null || prediction.getProduce().getItemID() < 0)
{
continue; // unknown state
}
allUnknown = false;
if (prediction.getProduce() != Produce.WEEDS && prediction.getProduce() != Produce.SCARECROW)
{
allEmpty = false;
// update max duration if this patch takes longer to grow
maxCompletionTime = Math.max(maxCompletionTime, prediction.getDoneEstimate());
}
}
final SummaryState state;
final long completionTime;
if (allUnknown)
{
state = SummaryState.UNKNOWN;
completionTime = -1L;
}
else if (allEmpty)
{
state = SummaryState.EMPTY;
completionTime = -1L;
}
else if (maxCompletionTime <= Instant.now().getEpochSecond())
{
state = SummaryState.COMPLETED;
completionTime = 0;
}
else
{
state = SummaryState.IN_PROGRESS;
completionTime = maxCompletionTime;
}
summaries.put(tab.getKey(), state);
completionTimes.put(tab.getKey(), completionTime);
}
}
}
/*
* Copyright (c) 2018 Abex
* 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.timetracking.farming;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.time.Instant;
import java.util.EnumMap;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import net.runelite.api.Client;
import net.runelite.api.Varbits;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.vars.Autoweed;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.timetracking.SummaryState;
import net.runelite.client.plugins.timetracking.Tab;
import net.runelite.client.plugins.timetracking.TimeTrackingConfig;
@Singleton
public class FarmingTracker
{
private final Client client;
private final ItemManager itemManager;
private final ConfigManager configManager;
private final TimeTrackingConfig config;
private final FarmingWorld farmingWorld;
private final Map<Tab, SummaryState> summaries = new EnumMap<>(Tab.class);
/**
* The time at which all patches of a particular type will be ready to be harvested,
* or {@code -1} if we have no data about any patch of the given type.
*/
private final Map<Tab, Long> completionTimes = new EnumMap<>(Tab.class);
@Inject
private FarmingTracker(Client client, ItemManager itemManager, ConfigManager configManager,
TimeTrackingConfig config, FarmingWorld farmingWorld)
{
this.client = client;
this.itemManager = itemManager;
this.configManager = configManager;
this.config = config;
this.farmingWorld = farmingWorld;
}
public FarmingTabPanel createTabPanel(Tab tab)
{
return new FarmingTabPanel(this, itemManager, config, farmingWorld.getTabs().get(tab));
}
/**
* Updates tracker data for the current region. Returns true if any data was changed.
*/
public boolean updateData(WorldPoint location)
{
boolean changed = false;
{
String group = TimeTrackingConfig.CONFIG_GROUP + "." + client.getUsername();
String autoweed = Integer.toString(client.getVar(Varbits.AUTOWEED));
if (!autoweed.equals(configManager.getConfiguration(group, TimeTrackingConfig.AUTOWEED)))
{
configManager.setConfiguration(group, TimeTrackingConfig.AUTOWEED, autoweed);
changed = true;
}
}
FarmingRegion region = farmingWorld.getRegions().get(location.getRegionID());
if (region != null && region.isInBounds(location))
{
// Write config with new varbits
// timetracking.<login-username>.<regionID>.<VarbitID>=<varbitValue>:<unix time>
String group = TimeTrackingConfig.CONFIG_GROUP + "." + client.getUsername() + "." + region.getRegionID();
long unixNow = Instant.now().getEpochSecond();
for (FarmingPatch patch : region.getPatches())
{
// Write the config value if it doesn't match what is current, or it is more than 5 minutes old
Varbits varbit = patch.getVarbit();
String key = Integer.toString(varbit.getId());
String strVarbit = Integer.toString(client.getVar(varbit));
String storedValue = configManager.getConfiguration(group, key);
if (storedValue != null)
{
String[] parts = storedValue.split(":");
if (parts.length == 2 && parts[0].equals(strVarbit))
{
long unixTime = 0;
try
{
unixTime = Long.parseLong(parts[1]);
}
catch (NumberFormatException e)
{
// ignored
}
if (unixTime + (5 * 60) > unixNow && unixNow + 30 > unixTime)
{
continue;
}
}
}
String value = strVarbit + ":" + unixNow;
configManager.setConfiguration(group, key, value);
changed = true;
}
}
if (changed)
{
updateCompletionTime();
}
return changed;
}
@Nullable
public PatchPrediction predictPatch(FarmingPatch patch)
{
long unixNow = Instant.now().getEpochSecond();
boolean autoweed;
{
String group = TimeTrackingConfig.CONFIG_GROUP + "." + client.getUsername();
autoweed = Integer.toString(Autoweed.ON.ordinal())
.equals(configManager.getConfiguration(group, TimeTrackingConfig.AUTOWEED));
}
String group = TimeTrackingConfig.CONFIG_GROUP + "." + client.getUsername() + "." + patch.getRegion().getRegionID();
String key = Integer.toString(patch.getVarbit().getId());
String storedValue = configManager.getConfiguration(group, key);
if (storedValue == null)
{
return null;
}
long unixTime = 0;
int value = 0;
{
String[] parts = storedValue.split(":");
if (parts.length == 2)
{
try
{
value = Integer.parseInt(parts[0]);
unixTime = Long.parseLong(parts[1]);
}
catch (NumberFormatException e)
{
}
}
}
if (unixTime <= 0)
{
return null;
}
PatchState state = patch.getImplementation().forVarbitValue(value);
if (state == null)
{
return null;
}
int stage = state.getStage();
int stages = state.getStages();
int tickrate = state.getTickRate() * 60;
if (autoweed && state.getProduce() == Produce.WEEDS)
{
stage = 0;
stages = 1;
tickrate = 0;
}
long doneEstimate = 0;
if (tickrate > 0)
{
long tickNow = (unixNow + (5 * 60)) / tickrate;
long tickTime = (unixTime + (5 * 60)) / tickrate;
int delta = (int) (tickNow - tickTime);
doneEstimate = ((stages - 1 - stage) + tickTime) * tickrate + (5 * 60);
stage += delta;
if (stage >= stages)
{
stage = stages - 1;
}
}
return new PatchPrediction(
state.getProduce(),
state.getCropState(),
doneEstimate,
stage,
stages
);
}
public void loadCompletionTimes()
{
summaries.clear();
completionTimes.clear();
updateCompletionTime();
}
public SummaryState getSummary(Tab patchType)
{
SummaryState summary = summaries.get(patchType);
return summary == null ? SummaryState.UNKNOWN : summary;
}
/**
* Gets the overall completion time for the given patch type.
*
* @see #completionTimes
*/
public long getCompletionTime(Tab patchType)
{
Long completionTime = completionTimes.get(patchType);
return completionTime == null ? -1 : completionTime;
}
/**
* Updates the overall completion time for the given patch type.
*
* @see #completionTimes
*/
private void updateCompletionTime()
{
for (Map.Entry<Tab, Set<FarmingPatch>> tab : farmingWorld.getTabs().entrySet())
{
long maxCompletionTime = 0;
boolean allUnknown = true;
boolean allEmpty = true;
for (FarmingPatch patch : tab.getValue())
{
PatchPrediction prediction = predictPatch(patch);
if (prediction == null || prediction.getProduce().getItemID() < 0)
{
continue; // unknown state
}
allUnknown = false;
if (prediction.getProduce() != Produce.WEEDS && prediction.getProduce() != Produce.SCARECROW)
{
allEmpty = false;
// update max duration if this patch takes longer to grow
maxCompletionTime = Math.max(maxCompletionTime, prediction.getDoneEstimate());
}
}
final SummaryState state;
final long completionTime;
if (allUnknown)
{
state = SummaryState.UNKNOWN;
completionTime = -1L;
}
else if (allEmpty)
{
state = SummaryState.EMPTY;
completionTime = -1L;
}
else if (maxCompletionTime <= Instant.now().getEpochSecond())
{
state = SummaryState.COMPLETED;
completionTime = 0;
}
else
{
state = SummaryState.IN_PROGRESS;
completionTime = maxCompletionTime;
}
summaries.put(tab.getKey(), state);
completionTimes.put(tab.getKey(), completionTime);
}
}
}

View File

@@ -139,28 +139,28 @@ class FarmingWorld
new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.HERB)
));
add(new FarmingRegion("Kourend", 7222,
add(new FarmingRegion("Kourend", 6967,
new FarmingPatch("North East", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT),
new FarmingPatch("South West", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT),
new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER),
new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB)
));
add(new FarmingRegion("Kourend", 6711,
new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.SPIRIT_TREE)
new FarmingPatch("", Varbits.FARMING_7904, PatchImplementation.SPIRIT_TREE)
));
add(new FarmingRegion("Kourend", 7223,
new FarmingPatch("West 1", Varbits.GRAPES_4953, PatchImplementation.GRAPES),
new FarmingPatch("West 2", Varbits.GRAPES_4954, PatchImplementation.GRAPES),
new FarmingPatch("West 3", Varbits.GRAPES_4955, PatchImplementation.GRAPES),
new FarmingPatch("West 4", Varbits.GRAPES_4956, PatchImplementation.GRAPES),
new FarmingPatch("West 5", Varbits.GRAPES_4957, PatchImplementation.GRAPES),
new FarmingPatch("West 6", Varbits.GRAPES_4958, PatchImplementation.GRAPES),
new FarmingPatch("East 1", Varbits.GRAPES_4959, PatchImplementation.GRAPES),
new FarmingPatch("East 2", Varbits.GRAPES_4960, PatchImplementation.GRAPES),
new FarmingPatch("East 3", Varbits.GRAPES_4961, PatchImplementation.GRAPES),
new FarmingPatch("East 4", Varbits.GRAPES_4962, PatchImplementation.GRAPES),
new FarmingPatch("East 5", Varbits.GRAPES_4963, PatchImplementation.GRAPES),
new FarmingPatch("East 6", Varbits.GRAPES_4964, PatchImplementation.GRAPES)
new FarmingPatch("East 1", Varbits.GRAPES_4953, PatchImplementation.GRAPES),
new FarmingPatch("East 2", Varbits.GRAPES_4954, PatchImplementation.GRAPES),
new FarmingPatch("East 3", Varbits.GRAPES_4955, PatchImplementation.GRAPES),
new FarmingPatch("East 4", Varbits.GRAPES_4956, PatchImplementation.GRAPES),
new FarmingPatch("East 5", Varbits.GRAPES_4957, PatchImplementation.GRAPES),
new FarmingPatch("East 6", Varbits.GRAPES_4958, PatchImplementation.GRAPES),
new FarmingPatch("West 1", Varbits.GRAPES_4959, PatchImplementation.GRAPES),
new FarmingPatch("West 2", Varbits.GRAPES_4960, PatchImplementation.GRAPES),
new FarmingPatch("West 3", Varbits.GRAPES_4961, PatchImplementation.GRAPES),
new FarmingPatch("West 4", Varbits.GRAPES_4962, PatchImplementation.GRAPES),
new FarmingPatch("West 5", Varbits.GRAPES_4963, PatchImplementation.GRAPES),
new FarmingPatch("West 6", Varbits.GRAPES_4964, PatchImplementation.GRAPES)
));
add(new FarmingRegion("Lletya", 9265,

View File

@@ -24,6 +24,7 @@
*/
package net.runelite.client.plugins.timetracking.farming;
import javax.annotation.Nullable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.runelite.client.plugins.timetracking.Tab;
@@ -2576,6 +2577,7 @@ public enum PatchImplementation
}
};
@Nullable
abstract PatchState forVarbitValue(int value);
private final Tab tab;

View File

@@ -115,7 +115,7 @@ enum TeleportLocationData
// Misc
XERICS_LOOKOUT(TeleportType.OTHER, "Xeric's Talisman", "Xeric's Lookout", new WorldPoint(1576, 3528, 0), "xerics_talisman_teleport_icon.png"),
XERICS_GLADE(TeleportType.OTHER, "Xeric's Talisman", "Xeric's Glade", new WorldPoint(1773, 3502, 0), "xerics_talisman_teleport_icon.png"),
XERICS_GLADE(TeleportType.OTHER, "Xeric's Talisman", "Xeric's Glade", new WorldPoint(1754, 3564, 0), "xerics_talisman_teleport_icon.png"),
XERICS_INFERNO(TeleportType.OTHER, "Xeric's Talisman", "Xeric's Inferno", new WorldPoint(1504, 3819, 0), "xerics_talisman_teleport_icon.png"),
XERICS_HEART(TeleportType.OTHER, "Xeric's Talisman", "Xeric's Heart", new WorldPoint(1641, 3670, 0), "xerics_talisman_teleport_icon.png"),
XERICS_HONOUR(TeleportType.OTHER, "Xeric's Talisman", "Xeric's Honour", new WorldPoint(1254, 3559, 0), "xerics_talisman_teleport_icon.png"),
@@ -138,6 +138,7 @@ enum TeleportLocationData
PHARAOHS_SCEPTRE_JALSAVRAH(TeleportType.OTHER, "Pharaoh's Sceptre", "Jalsavrah (Pyramid Plunder)", new WorldPoint(3288, 2795, 0), "pharaohs_sceptre_teleport_icon.png"),
PHARAOHS_SCEPTRE_JALEUSTROPHOS(TeleportType.OTHER, "Pharaoh's Sceptre", "Jaleustrophos (Agility Pyramid)", new WorldPoint(3341, 2827, 0), "pharaohs_sceptre_teleport_icon.png"),
PHARAOHS_SCEPTRE_JALDRAOCHT(TeleportType.OTHER, "Pharaoh's Sceptre", "Jaldraocht (Desert Treasure Pyramid)", new WorldPoint(3232, 2897, 0), "pharaohs_sceptre_teleport_icon.png"),
CAMULET_TEMPLE(TeleportType.OTHER, "Camulet", "Enakhra's Temple", new WorldPoint(3190, 2923, 0), "camulet_teleport_icon.png"),
// Wilderness
OBELISK_13(TeleportType.OTHER, "Obelisk", "13", new WorldPoint(3156, 3620, 0), "obelisk_icon.png"),