Merge pull request #1095 from Owain94/stonedloottracker

stonedloottracker: Update plugin
This commit is contained in:
Ganom
2019-07-23 18:55:14 -04:00
committed by GitHub
38 changed files with 2102 additions and 3047 deletions

View File

@@ -132,7 +132,7 @@ class LootTrackerPanel extends PluginPanel
final BufferedImage backArrowImg = ImageUtil.getResourceStreamFromClass(LootTrackerPlugin.class, "back_icon.png");
final BufferedImage visibleImg = ImageUtil.getResourceStreamFromClass(LootTrackerPlugin.class, "visible_icon.png");
final BufferedImage invisibleImg = ImageUtil.getResourceStreamFromClass(LootTrackerPlugin.class, "invisible_icon.png");
final BufferedImage resetImg = ImageUtil.getResourceStreamFromClass(LootTrackerPlugin.class, "reset.png");
final BufferedImage resetImg = ImageUtil.getResourceStreamFromClass(LootTrackerPlugin.class, "delete-white.png");
SINGLE_LOOT_VIEW = new ImageIcon(singleLootImg);
SINGLE_LOOT_VIEW_FADED = new ImageIcon(ImageUtil.alphaOffset(singleLootImg, -180));

View File

@@ -46,6 +46,7 @@ import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -101,6 +102,9 @@ import net.runelite.client.game.ItemStack;
import net.runelite.client.game.SpriteManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.loottracker.localstorage.LTItemEntry;
import net.runelite.client.plugins.loottracker.localstorage.LTRecord;
import net.runelite.client.plugins.loottracker.localstorage.LootRecordWriter;
import net.runelite.client.ui.ClientToolbar;
import net.runelite.client.ui.NavigationButton;
import net.runelite.client.util.ImageUtil;
@@ -126,6 +130,9 @@ public class LootTrackerPlugin extends Plugin
private static final Pattern CLUE_SCROLL_PATTERN = Pattern.compile("You have completed [0-9]+ ([a-z]+) Treasure Trails.");
private static final int THEATRE_OF_BLOOD_REGION = 12867;
private static final Pattern BOSS_NAME_NUMBER_PATTERN = Pattern.compile("Your (.*) kill count is: ([0-9]*).");
private static final Pattern NUMBER_PATTERN = Pattern.compile("([0-9]+)");
// Herbiboar loot handling
private static final String HERBIBOAR_LOOTED_MESSAGE = "You harvest herbs from the herbiboar, whereupon it escapes.";
private static final String HERBIBOR_EVENT = "Herbiboar";
@@ -174,6 +181,8 @@ public class LootTrackerPlugin extends Plugin
private ScheduledExecutorService executor;
@Inject
private EventBus eventBus;
@Inject
private LootRecordWriter writer;
private LootTrackerPanel panel;
private NavigationButton navButton;
private String eventType;
@@ -185,6 +194,8 @@ public class LootTrackerPlugin extends Plugin
@Getter(AccessLevel.PACKAGE)
private LootTrackerClient lootTrackerClient;
private Map<String, Integer> killCountMap = new HashMap<>();
private static Collection<ItemStack> stack(Collection<ItemStack> items)
{
final List<ItemStack> list = new ArrayList<>();
@@ -412,6 +423,35 @@ public class LootTrackerPlugin extends Plugin
{
chestLooted = false;
}
if (event.getGameState() == GameState.LOGGING_IN)
{
clientThread.invokeLater(() ->
{
switch (client.getGameState())
{
case LOGGED_IN:
break;
case LOGGING_IN:
case LOADING:
return false;
default:
// Quit running if any other state
return true;
}
String name = client.getLocalPlayer().getName();
if (name != null)
{
writer.setPlayerUsername(name);
return true;
}
else
{
return false;
}
});
}
}
private void onNpcLootReceived(final NpcLootReceived npcLootReceived)
@@ -423,6 +463,8 @@ public class LootTrackerPlugin extends Plugin
final LootTrackerItem[] entries = buildEntries(stack(items));
String localUsername = client.getLocalPlayer().getName();
final int killCount = killCountMap.getOrDefault(name.toUpperCase(), -1);
if (this.whitelistEnabled)
{
final String configNpcs = this.getWhitelist.toLowerCase();
@@ -454,6 +496,9 @@ public class LootTrackerPlugin extends Plugin
{
saveLocalLootRecord(lootRecord);
}
LTRecord record = new LTRecord(npc.getId(), npc.getName(), combat, killCount, convertToLTItemEntries(items));
writer.addLootTrackerRecord(record);
}
private void onPlayerSpawned(PlayerSpawned event)
@@ -496,6 +541,9 @@ public class LootTrackerPlugin extends Plugin
{
saveLocalLootRecord(lootRecord);
}
LTRecord record = new LTRecord(-1, name, combat, -1, convertToLTItemEntries(items));
writer.addLootTrackerRecord(record);
}
private void onWidgetLoaded(WidgetLoaded event)
@@ -590,6 +638,8 @@ public class LootTrackerPlugin extends Plugin
LootRecord lootRecord = new LootRecord(eventType, client.getLocalPlayer().getName(), LootRecordType.EVENT,
toGameItems(items), Instant.now());
final int killCount = killCountMap.getOrDefault(eventType.toUpperCase(), -1);
if (lootTrackerClient != null && this.saveLoot)
{
lootTrackerClient.submit(lootRecord);
@@ -598,6 +648,9 @@ public class LootTrackerPlugin extends Plugin
{
saveLocalLootRecord(lootRecord);
}
LTRecord record = new LTRecord(-1, eventType, -1, killCount, convertToLTItemEntries(items));
writer.addLootTrackerRecord(record);
}
private void onChatMessage(ChatMessage event)
@@ -631,8 +684,11 @@ public class LootTrackerPlugin extends Plugin
return;
}
// Remove all tags
final String chatMessage = Text.removeTags(message);
// Check if message is for a clue scroll reward
final Matcher m = CLUE_SCROLL_PATTERN.matcher(Text.removeTags(message));
final Matcher m = CLUE_SCROLL_PATTERN.matcher(chatMessage);
if (m.find())
{
final String type = m.group(1).toLowerCase();
@@ -657,6 +713,50 @@ public class LootTrackerPlugin extends Plugin
eventType = "Clue Scroll (Master)";
break;
}
int killCount = Integer.valueOf(m.group(1));
killCountMap.put(eventType.toUpperCase(), killCount);
return;
}
// Barrows KC
if (chatMessage.startsWith("Your Barrows chest count is"))
{
Matcher n = NUMBER_PATTERN.matcher(chatMessage);
if (n.find())
{
killCountMap.put("BARROWS", Integer.valueOf(n.group()));
return;
}
}
// Raids KC
if (chatMessage.startsWith("Your completed Chambers of Xeric count is"))
{
Matcher n = NUMBER_PATTERN.matcher(chatMessage);
if (n.find())
{
killCountMap.put("CHAMBERS OF XERIC", Integer.valueOf(n.group()));
return;
}
}
// Raids KC
if (chatMessage.startsWith("Your completed Theatre of Blood count is"))
{
Matcher n = NUMBER_PATTERN.matcher(chatMessage);
if (n.find())
{
killCountMap.put("THEATRE OF BLOOD", Integer.valueOf(n.group()));
return;
}
}
// Handle all other boss
Matcher boss = BOSS_NAME_NUMBER_PATTERN.matcher(chatMessage);
if (boss.find())
{
String bossName = boss.group(1);
int killCount = Integer.valueOf(boss.group(2));
killCountMap.put(bossName.toUpperCase(), killCount);
}
}
@@ -825,6 +925,9 @@ public class LootTrackerPlugin extends Plugin
saveLocalLootRecord(lootRecord);
}
LTRecord record = new LTRecord(-1, chestType, -1, -1, convertToLTItemEntries(items));
writer.addLootTrackerRecord(record);
inventorySnapshot = null;
}
}
@@ -931,6 +1034,17 @@ public class LootTrackerPlugin extends Plugin
return trackerRecords;
}
private Collection<LTItemEntry> convertToLTItemEntries(Collection<ItemStack> stacks)
{
return stacks.stream().map(i ->
{
final ItemDefinition c = itemManager.getItemDefinition(i.getId());
final int id = c.getNote() == -1 ? c.getId() : c.getLinkedNoteId();
final int price = itemManager.getItemPrice(id);
return new LTItemEntry(c.getName(), i.getId(), i.getQuantity(), price);
}).collect(Collectors.toList());
}
private void updateConfig()
{
this.getIgnoredItems = config.getIgnoredItems();

View File

@@ -23,38 +23,48 @@
* (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.stonedloottracker.data;
package net.runelite.client.plugins.loottracker.localstorage;
import lombok.AccessLevel;
import lombok.Getter;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
@Getter(AccessLevel.PUBLIC)
public class LootTrackerItemEntry
@Data
@AllArgsConstructor
public class LTItemEntry
{
private final String name;
private final int id;
private int quantity;
private final long price;
private final long haPrice;
private long total;
private long haTotal;
private final boolean stackable;
private long price;
public LootTrackerItemEntry(final String name, final int id, final int quantity, final long price, final long haPrice, final boolean stackable)
public long getTotal()
{
this.name = name;
this.id = id;
this.quantity = quantity;
this.price = price;
this.haPrice = haPrice;
this.stackable = stackable;
this.total = price * quantity;
this.haTotal = haPrice * quantity;
return this.quantity * this.price;
}
@Override
public String toString()
public static Map<Integer, LTItemEntry> consolidateItemEntires(final Collection<LTItemEntry> drops)
{
return "LootTrackerItemEntry(name=" + name + ",id=" + id + ",quantity=" + quantity + ",price=" + price + ",haprice=" + haPrice + ",total=" + total + ",haTotal=" + haTotal + ",stackable=" + stackable + ")";
// Store LootTrackerItemEntry by ItemID
final Map<Integer, LTItemEntry> itemMap = new HashMap<>();
for (final LTItemEntry e : drops)
{
final LTItemEntry oldEntry = itemMap.get(e.getId());
if (oldEntry != null)
{
// Use the most recent price
oldEntry.setPrice(e.getPrice());
oldEntry.setQuantity(oldEntry.getQuantity() + e.getQuantity());
}
else
{
// Create a new instance for consolidated records
itemMap.put(e.getId(), new LTItemEntry(e.getName(), e.getId(), e.getQuantity(), e.getPrice()));
}
}
return itemMap;
}
}

View File

@@ -22,14 +22,35 @@
* (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.stonedloottracker.data;
package net.runelite.client.plugins.loottracker.localstorage;
import lombok.Value;
import java.util.ArrayList;
import java.util.Collection;
import lombok.AllArgsConstructor;
import lombok.Data;
@Value
public class UniqueItemPrepared
@Data
@AllArgsConstructor
public class LTRecord
{
private int linkedID;
private int price;
private UniqueItem uniqueItem;
private final int id;
private final String name;
private final int level;
private final int killCount;
final Collection<LTItemEntry> drops;
public void addDropEntry(LTItemEntry itemEntry)
{
drops.add(itemEntry);
}
public static Collection<LTItemEntry> consolidateLTItemEntries(final Collection<LTRecord> records)
{
final Collection<LTItemEntry> recordEntries = new ArrayList<>();
for (final LTRecord r : records)
{
recordEntries.addAll(r.getDrops());
}
return recordEntries;
}
}

View File

@@ -22,7 +22,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.stonedloottracker.data;
package net.runelite.client.plugins.loottracker.localstorage;
import java.io.BufferedReader;
import java.io.BufferedWriter;
@@ -41,8 +41,8 @@ import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import static net.runelite.client.RuneLite.RUNELITE_DIR;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.plugins.stonedloottracker.data.events.LootTrackerNameChange;
import net.runelite.client.plugins.stonedloottracker.data.events.LootTrackerRecordStored;
import net.runelite.client.plugins.loottracker.localstorage.events.LTNameChange;
import net.runelite.client.plugins.loottracker.localstorage.events.LTRecordStored;
import net.runelite.http.api.RuneLiteAPI;
@Slf4j
@@ -52,38 +52,45 @@ public class LootRecordWriter
private static final String FILE_EXTENSION = ".log";
private static final File LOOT_RECORD_DIR = new File(RUNELITE_DIR, "loots");
private final EventBus bus;
// Data is stored in a folder with the players in-game username
private File playerFolder = LOOT_RECORD_DIR;
private final EventBus bus;
private String name;
@Inject
public LootRecordWriter(EventBus bus)
{
this.bus = bus;
playerFolder.mkdir();
LOOT_RECORD_DIR.mkdir();
}
private static String npcNameToFileName(String npcName)
private static String npcNameToFileName(final String npcName)
{
return npcName.toLowerCase().trim() + FILE_EXTENSION;
}
public void setPlayerUsername(String username)
public void setPlayerUsername(final String username)
{
if (username.equalsIgnoreCase(name))
{
return;
}
playerFolder = new File(LOOT_RECORD_DIR, username);
playerFolder.mkdir();
bus.post(LootTrackerNameChange.class, new LootTrackerNameChange());
name = username;
bus.post(LTNameChange.class, new LTNameChange());
}
public Set<String> getKnownFileNames()
{
Set<String> fileNames = new HashSet<>();
final Set<String> fileNames = new HashSet<>();
File[] files = playerFolder.listFiles((dir, name) -> name.endsWith(FILE_EXTENSION));
final File[] files = playerFolder.listFiles((dir, name) -> name.endsWith(FILE_EXTENSION));
if (files != null)
{
for (File f : files)
for (final File f : files)
{
fileNames.add(f.getName().replace(FILE_EXTENSION, ""));
}
@@ -92,25 +99,29 @@ public class LootRecordWriter
return fileNames;
}
public synchronized Collection<LootRecordCustom> loadLootTrackerRecords(String npcName)
public synchronized Collection<LTRecord> loadLootTrackerRecords(String npcName)
{
String fileName = npcNameToFileName(npcName);
File file = new File(playerFolder, fileName);
Collection<LootRecordCustom> data = new ArrayList<>();
final String fileName = npcNameToFileName(npcName);
final File file = new File(playerFolder, fileName);
final Collection<LTRecord> data = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader(file)))
StringBuilder sb = new StringBuilder();
try (final BufferedReader br = new BufferedReader(new FileReader(file)))
{
// read line by line
String line;
while ((line = br.readLine()) != null)
{
// Skip empty line at end of file
if (line.length() > 0)
{
LootRecordCustom r = RuneLiteAPI.GSON.fromJson(line, LootRecordCustom.class);
data.add(r);
}
sb.append(line).append("\n");
}
if (sb.length() > 0)
{
final LTRecord r = RuneLiteAPI.GSON.fromJson(sb.toString(), LTRecord.class);
data.add(r);
}
}
catch (FileNotFoundException e)
{
@@ -124,135 +135,85 @@ public class LootRecordWriter
return data;
}
public synchronized void addLootTrackerRecord(LootRecordCustom rec)
public synchronized boolean addLootTrackerRecord(LTRecord rec)
{
// Grab file
String fileName = npcNameToFileName(rec.getName());
File lootFile = new File(playerFolder, fileName);
final String fileName = npcNameToFileName(rec.getName());
final File lootFile = new File(playerFolder, fileName);
// Convert entry to JSON
String dataAsString = RuneLiteAPI.GSON.toJson(rec);
final String dataAsString = RuneLiteAPI.GSON.toJson(rec);
// Open File in append mode and write new data
try
{
BufferedWriter file = new BufferedWriter(new FileWriter(String.valueOf(lootFile), true));
final BufferedWriter file = new BufferedWriter(new FileWriter(String.valueOf(lootFile), true));
file.append(dataAsString);
file.newLine();
file.close();
bus.post(LootTrackerRecordStored.class, new LootTrackerRecordStored(rec));
bus.post(LTRecordStored.class, new LTRecordStored(rec));
return true;
}
catch (IOException ioe)
{
log.warn("Error writing loot data to file {}: {}", fileName, ioe.getMessage());
return false;
}
}
// Mostly used to adjust previous loot entries such as adding pet drops/abyssal sire drops
public synchronized void writeLootTrackerFile(String npcName, Collection<LootRecordCustom> loots)
public synchronized boolean writeLootTrackerFile(String npcName, Collection<LTRecord> loots)
{
String fileName = npcNameToFileName(npcName);
File lootFile = new File(playerFolder, fileName);
final String fileName = npcNameToFileName(npcName);
final File lootFile = new File(playerFolder, fileName);
try
{
BufferedWriter file = new BufferedWriter(new FileWriter(String.valueOf(lootFile), false));
for (LootRecordCustom rec : loots)
final BufferedWriter file = new BufferedWriter(new FileWriter(String.valueOf(lootFile), false));
for (final LTRecord rec : loots)
{
// Convert entry to JSON
String dataAsString = RuneLiteAPI.GSON.toJson(rec);
final String dataAsString = RuneLiteAPI.GSON.toJson(rec);
file.append(dataAsString);
file.newLine();
}
file.close();
return true;
}
catch (IOException ioe)
{
log.warn("Error rewriting loot data to file {}: {}", fileName, ioe.getMessage());
return false;
}
}
public synchronized void deleteLootTrackerRecords(String npcName)
public synchronized boolean deleteLootTrackerRecords(String npcName)
{
String fileName = npcNameToFileName(npcName);
File lootFile = new File(playerFolder, fileName);
final String fileName = npcNameToFileName(npcName);
final File lootFile = new File(playerFolder, fileName);
if (lootFile.delete())
{
log.debug("Deleted loot file: {}", fileName);
return true;
}
else
{
log.debug("Couldn't delete file: {}", fileName);
return false;
}
}
public Collection<LootRecordCustom> loadAllLootTrackerRecords()
public Collection<LTRecord> loadAllLootTrackerRecords()
{
List<LootRecordCustom> recs = new ArrayList<>();
final List<LTRecord> recs = new ArrayList<>();
for (String n : getKnownFileNames())
for (final String n : getKnownFileNames())
{
recs.addAll(loadLootTrackerRecords(n));
}
return recs;
}
public void convertFileFormats()
{
for (String f : getKnownFileNames())
{
updateFileFormat(npcNameToFileName(f));
}
log.info("Done converting");
}
private void updateFileFormat(String fileName)
{
File lootFile = new File(playerFolder, fileName);
Collection<LootRecordCustom> data = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader(lootFile)))
{
String line;
while ((line = br.readLine()) != null)
{
// Skip empty line at end of file
if (line.length() > 0)
{
LootRecordCustom r = RuneLiteAPI.GSON.fromJson(line, LootRecord.class).toNewFormat();
data.add(r);
}
}
}
catch (FileNotFoundException e)
{
log.info("File not found: {}", fileName);
}
catch (IOException e)
{
log.warn("IOException for file {}: {}", fileName, e.getMessage());
}
try
{
BufferedWriter file = new BufferedWriter(new FileWriter(String.valueOf(lootFile), false));
for (LootRecordCustom rec : data)
{
// Convert entry to JSON
String dataAsString = RuneLiteAPI.GSON.toJson(rec);
file.append(dataAsString);
file.newLine();
}
file.close();
}
catch (IOException ioe)
{
log.warn("Error rewriting loot data to file {}: {}", fileName, ioe.getMessage());
}
}
}

View File

@@ -22,8 +22,8 @@
* (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.stonedloottracker.data.events;
package net.runelite.client.plugins.loottracker.localstorage.events;
public class LootTrackerNameChange
public class LTNameChange
{
}

View File

@@ -22,13 +22,13 @@
* (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.stonedloottracker.data.events;
package net.runelite.client.plugins.loottracker.localstorage.events;
import lombok.Data;
import net.runelite.client.plugins.stonedloottracker.data.LootRecordCustom;
import net.runelite.client.plugins.loottracker.localstorage.LTRecord;
@Data
public class LootTrackerRecordStored
public class LTRecordStored
{
private final LootRecordCustom record;
private final LTRecord record;
}

View File

@@ -1,717 +0,0 @@
/*
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.stonedloottracker;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multiset;
import com.google.common.collect.Multisets;
import com.google.inject.Provides;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.swing.SwingUtilities;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.InventoryID;
import net.runelite.api.ItemDefinition;
import net.runelite.api.ItemContainer;
import net.runelite.api.NPC;
import net.runelite.api.NpcID;
import net.runelite.api.Player;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.ItemContainerChanged;
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.callback.ClientThread;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.events.NpcLootReceived;
import net.runelite.client.events.PlayerLootReceived;
import net.runelite.client.game.ItemManager;
import net.runelite.client.game.ItemStack;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.PluginType;
import net.runelite.client.plugins.stonedloottracker.data.LootRecordCustom;
import net.runelite.client.plugins.stonedloottracker.data.LootRecordWriter;
import net.runelite.client.plugins.stonedloottracker.data.LootTrackerItemEntry;
import net.runelite.client.plugins.stonedloottracker.data.Pet;
import net.runelite.client.plugins.stonedloottracker.data.UniqueItem;
import net.runelite.client.plugins.stonedloottracker.data.UniqueItemPrepared;
import net.runelite.client.plugins.stonedloottracker.data.events.LootTrackerNameChange;
import net.runelite.client.plugins.stonedloottracker.data.events.LootTrackerRecordStored;
import net.runelite.client.plugins.stonedloottracker.ui.LootTrackerPanel;
import net.runelite.client.ui.ClientToolbar;
import net.runelite.client.ui.NavigationButton;
import net.runelite.client.util.ImageUtil;
import net.runelite.client.util.Text;
@PluginDescriptor(
name = "Stoned Loot Tracker",
description = "Tracks loot from monsters and minigames",
tags = {"drops"},
type = PluginType.UTILITY,
enabledByDefault = false
)
@Singleton
@Slf4j
public class StonedLootTrackerPlugin extends Plugin
{
// Activity/Event loot handling
private static final Pattern CLUE_SCROLL_PATTERN = Pattern.compile("You have completed ([0-9]+) ([a-z]+) Treasure Trails.");
private static final Pattern BOSS_NAME_NUMBER_PATTERN = Pattern.compile("Your (.*) kill count is: ([0-9]*).");
private static final Pattern NUMBER_PATTERN = Pattern.compile("([0-9]+)");
private static final ImmutableList<String> PET_MESSAGES = ImmutableList.of("You have a funny feeling like you're being followed",
"You feel something weird sneaking into your backpack",
"You have a funny feeling like you would have been followed");
private static final int THEATRE_OF_BLOOD_REGION = 12867;
// Herbiboar loot handling
private static final String HERBIBOAR_LOOTED_MESSAGE = "Your herbiboar harvest count is";
private static final String HERBIBOR_EVENT = "Herbiboar";
// Chest loot handling
private static final String CHEST_LOOTED_MESSAGE = "You find some treasure in the chest!";
private static final Map<Integer, String> CHEST_EVENT_TYPES = ImmutableMap.of(
5179, "Brimstone Chest",
11573, "Crystal Chest"
);
@Inject
private ClientToolbar clientToolbar;
@Inject
private ItemManager itemManager;
@Inject
private Client client;
@Inject
public StonedLootTrackerConfig config;
@Inject
private ClientThread clientThread;
@Inject
private LootRecordWriter writer;
@Inject
private EventBus eventBus;
private Multiset<Integer> inventorySnapshot;
@Provides
StonedLootTrackerConfig provideConfig(ConfigManager configManager)
{
return configManager.getConfig(StonedLootTrackerConfig.class);
}
private void onConfigChanged(ConfigChanged event)
{
if (event.getGroup().equals("stonedloottracker"))
{
panel.refreshUI();
}
}
private LootTrackerPanel panel;
private NavigationButton navButton;
private String eventType;
private final Multimap<String, LootRecordCustom> lootRecordMultimap = ArrayListMultimap.create();
private final Multimap<String, UniqueItemPrepared> uniques = ArrayListMultimap.create();
private final Map<String, Integer> killCountMap = new HashMap<>();
// key = name, value=current killCount
private boolean loaded = false;
private boolean gotPet = false;
@Override
protected void startUp() throws Exception
{
addSubscriptions();
panel = new LootTrackerPanel(itemManager, this);
final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "panel_icon.png");
navButton = NavigationButton.builder()
.tooltip("Stoned Loot Tracker")
.icon(icon)
.priority(5)
.panel(panel)
.build();
clientToolbar.addNavigation(navButton);
if (!loaded)
{
clientThread.invokeLater(() ->
{
switch (client.getGameState())
{
case UNKNOWN:
case STARTING:
return false;
}
prepareUniqueItems();
return true;
});
}
}
private void prepareUniqueItems()
{
loaded = true;
for (UniqueItem i : UniqueItem.values())
{
ItemDefinition c = itemManager.getItemDefinition(i.getItemID());
for (String s : i.getActivities())
{
uniques.put(s.toUpperCase(), new UniqueItemPrepared(c.getLinkedNoteId(), itemManager.getItemPrice(i.getItemID()), i));
}
}
}
public Collection<UniqueItemPrepared> getUniques(String name)
{
return uniques.get(name.toUpperCase());
}
@Override
protected void shutDown()
{
eventBus.unregister(this);
clientToolbar.removeNavigation(navButton);
}
private void addSubscriptions()
{
eventBus.subscribe(ConfigChanged.class, this, this::onConfigChanged);
eventBus.subscribe(LootTrackerRecordStored.class, this, this::onLootTrackerRecordStored);
eventBus.subscribe(LootTrackerNameChange.class, this, this::onLootTrackerNameChange);
eventBus.subscribe(NpcLootReceived.class, this, this::onNpcLootReceived);
eventBus.subscribe(PlayerLootReceived.class, this, this::onPlayerLootReceived);
eventBus.subscribe(WidgetLoaded.class, this, this::onWidgetLoaded);
eventBus.subscribe(ChatMessage.class, this, this::onChatMessage);
eventBus.subscribe(ItemContainerChanged.class, this, this::onItemContainerChanged);
eventBus.subscribe(GameStateChanged.class, this, this::onGameStateChanged);
}
private void onLootTrackerRecordStored(LootTrackerRecordStored s)
{
SwingUtilities.invokeLater(() -> panel.addLog(s.getRecord()));
}
private void onLootTrackerNameChange(LootTrackerNameChange c)
{
refreshData();
SwingUtilities.invokeLater(() -> panel.updateNames());
}
private void onNpcLootReceived(final NpcLootReceived npcLootReceived)
{
final NPC npc = npcLootReceived.getNpc();
final Collection<ItemStack> items = npcLootReceived.getItems();
final String name = npc.getName().toLowerCase().contains("vet'ion reborn") ? "Vet'ion" : npc.getName();
final int combat = npc.getCombatLevel();
final int killCount = killCountMap.getOrDefault(name.toUpperCase(), -1);
final LootTrackerItemEntry[] entries = buildEntries(items);
if (gotPet)
{
ItemStack pet = handlePet(name);
if (pet == null)
{
log.warn("Error finding pet for npc name: {}", name);
}
else
{
items.add(pet);
}
}
LootRecordCustom rec = new LootRecordCustom(npc.getId(), name, combat, killCount, Arrays.asList(entries));
addLog(name, rec);
}
private void onPlayerLootReceived(final PlayerLootReceived playerLootReceived)
{
final Player player = playerLootReceived.getPlayer();
final Collection<ItemStack> items = playerLootReceived.getItems();
final String name = player.getName();
final int combat = player.getCombatLevel();
final int killCount = killCountMap.getOrDefault(name.toUpperCase(), -1);
final LootTrackerItemEntry[] entries = buildEntries(items);
LootRecordCustom rec = new LootRecordCustom(-1, name, combat, killCount, Arrays.asList(entries));
addLog(name, rec);
}
private void onWidgetLoaded(WidgetLoaded event)
{
final ItemContainer container;
switch (event.getGroupId())
{
case (WidgetID.BARROWS_REWARD_GROUP_ID):
eventType = "Barrows";
container = client.getItemContainer(InventoryID.BARROWS_REWARD);
break;
case (WidgetID.CHAMBERS_OF_XERIC_REWARD_GROUP_ID):
eventType = "Chambers of Xeric";
container = client.getItemContainer(InventoryID.CHAMBERS_OF_XERIC_CHEST);
break;
case (WidgetID.THEATRE_OF_BLOOD_REWARD_GROUP_ID):
int region = WorldPoint.fromLocalInstance(client, client.getLocalPlayer().getLocalLocation()).getRegionID();
if (region != THEATRE_OF_BLOOD_REGION)
{
return;
}
eventType = "Theatre of Blood";
container = client.getItemContainer(InventoryID.THEATRE_OF_BLOOD_CHEST);
break;
case (WidgetID.CLUE_SCROLL_REWARD_GROUP_ID):
// event type should be set via ChatMessage for clue scrolls.
// Clue Scrolls use same InventoryID as Barrows
container = client.getItemContainer(InventoryID.BARROWS_REWARD);
break;
// Unsired redemption tracking
case (WidgetID.DIALOG_SPRITE_GROUP_ID):
Widget text = client.getWidget(WidgetInfo.DIALOG_SPRITE_TEXT);
if ("you place the unsired into the font of consumption...".equals(text.getText().toLowerCase()))
{
checkUnsiredWidget();
}
return;
default:
return;
}
if (container == null)
{
return;
}
// Convert container items to array of ItemStack
final Collection<ItemStack> items = Arrays.stream(container.getItems())
.filter(item -> item.getId() > 0)
.map(item -> new ItemStack(item.getId(), item.getQuantity(), client.getLocalPlayer().getLocalLocation()))
.collect(Collectors.toList());
if (items.isEmpty())
{
log.debug("No items to find for Event: {} | Container: {}", eventType, container);
return;
}
final LootTrackerItemEntry[] entries = buildEntries(items);
final int killCount = killCountMap.getOrDefault(eventType.toUpperCase(), -1);
LootRecordCustom rec = new LootRecordCustom(-1, eventType, -1, killCount, Arrays.asList(entries));
addLog(eventType, rec);
}
private void addLog(String name, LootRecordCustom record)
{
lootRecordMultimap.put(name, record);
writer.addLootTrackerRecord(record);
}
private void onChatMessage(ChatMessage event)
{
if (event.getType() != ChatMessageType.GAMEMESSAGE && event.getType() != ChatMessageType.SPAM)
{
return;
}
String chatMessage = Text.removeTags(event.getMessage());
// Check if message is for a clue scroll reward
final Matcher m = CLUE_SCROLL_PATTERN.matcher(chatMessage);
if (m.find())
{
final String type = m.group(2).toLowerCase();
switch (type)
{
case "beginner":
eventType = "Clue Scroll (Beginner)";
break;
case "easy":
eventType = "Clue Scroll (Easy)";
break;
case "medium":
eventType = "Clue Scroll (Medium)";
break;
case "hard":
eventType = "Clue Scroll (Hard)";
break;
case "elite":
eventType = "Clue Scroll (Elite)";
break;
case "master":
eventType = "Clue Scroll (Master)";
break;
}
int killCount = Integer.valueOf(m.group(1));
killCountMap.put(eventType.toUpperCase(), killCount);
return;
}
// TODO: Figure out better way to handle Barrows and Raids/Raids 2
// Barrows KC
if (chatMessage.startsWith("Your Barrows chest count is"))
{
Matcher n = NUMBER_PATTERN.matcher(chatMessage);
if (n.find())
{
killCountMap.put("BARROWS", Integer.valueOf(n.group()));
return;
}
}
// Raids KC
if (chatMessage.startsWith("Your completed Chambers of Xeric count is"))
{
Matcher n = NUMBER_PATTERN.matcher(chatMessage);
if (n.find())
{
killCountMap.put("RAIDS", Integer.valueOf(n.group()));
return;
}
}
// Raids KC
if (chatMessage.startsWith("Your completed Theatre of Blood count is"))
{
Matcher n = NUMBER_PATTERN.matcher(chatMessage);
if (n.find())
{
killCountMap.put("THEATRE OF BLOOD", Integer.valueOf(n.group()));
return;
}
}
// Handle all other boss
Matcher boss = BOSS_NAME_NUMBER_PATTERN.matcher(chatMessage);
if (boss.find())
{
String bossName = boss.group(1);
int killCount = Integer.valueOf(boss.group(2));
killCountMap.put(bossName.toUpperCase(), killCount);
}
if (PET_MESSAGES.stream().anyMatch(chatMessage::contains))
{
gotPet = true;
}
if (chatMessage.equals(CHEST_LOOTED_MESSAGE))
{
final int regionID = client.getLocalPlayer().getWorldLocation().getRegionID();
if (!CHEST_EVENT_TYPES.containsKey(regionID))
{
return;
}
eventType = CHEST_EVENT_TYPES.get(regionID);
takeInventorySnapshot();
Matcher n = NUMBER_PATTERN.matcher(chatMessage);
if (n.find())
{
killCountMap.put(eventType, Integer.valueOf(n.group()));
}
}
if (chatMessage.startsWith(HERBIBOAR_LOOTED_MESSAGE))
{
eventType = HERBIBOR_EVENT;
takeInventorySnapshot();
Matcher n = NUMBER_PATTERN.matcher(chatMessage);
if (n.find())
{
killCountMap.put(HERBIBOR_EVENT, Integer.valueOf(n.group()));
}
}
}
private void onItemContainerChanged(ItemContainerChanged event)
{
if (eventType != null && (CHEST_EVENT_TYPES.containsValue(eventType) || HERBIBOR_EVENT.equals(eventType)))
{
if (event.getItemContainer() != client.getItemContainer(InventoryID.INVENTORY))
{
return;
}
processInventoryLoot(eventType, event.getItemContainer());
eventType = null;
}
}
private void takeInventorySnapshot()
{
final ItemContainer itemContainer = client.getItemContainer(InventoryID.INVENTORY);
if (itemContainer != null)
{
inventorySnapshot = HashMultiset.create();
Arrays.stream(itemContainer.getItems())
.forEach(item -> inventorySnapshot.add(item.getId(), item.getQuantity()));
}
}
private void processInventoryLoot(String eventType, ItemContainer inventoryContainer)
{
if (inventorySnapshot != null)
{
Multiset<Integer> currentInventory = HashMultiset.create();
Arrays.stream(inventoryContainer.getItems())
.forEach(item -> currentInventory.add(item.getId(), item.getQuantity()));
final Multiset<Integer> diff = Multisets.difference(currentInventory, inventorySnapshot);
List<ItemStack> items = diff.entrySet().stream()
.map(e -> new ItemStack(e.getElement(), e.getCount(), client.getLocalPlayer().getLocalLocation()))
.collect(Collectors.toList());
final LootTrackerItemEntry[] entries = buildEntries(items);
LootRecordCustom rec = new LootRecordCustom(-1, eventType, -1, killCountMap.getOrDefault(eventType, -1), Arrays.asList(entries));
addLog(eventType, rec);
inventorySnapshot = null;
}
}
private LootTrackerItemEntry[] buildEntries(final Collection<ItemStack> itemStacks)
{
return itemStacks.stream().map(itemStack ->
{
final ItemDefinition itemComposition = itemManager.getItemDefinition(itemStack.getId());
final int realItemId = itemComposition.getNote() != -1 ? itemComposition.getLinkedNoteId() : itemStack.getId();
final long price = itemManager.getItemPrice(realItemId);
final long haPrice = itemManager.getAlchValue(realItemId);
return new LootTrackerItemEntry(
itemComposition.getName(),
itemStack.getId(),
itemStack.getQuantity(),
price,
haPrice,
itemComposition.isStackable());
}).toArray(LootTrackerItemEntry[]::new);
}
public Collection<LootRecordCustom> getData()
{
return lootRecordMultimap.values();
}
public Collection<LootRecordCustom> getDataByName(String name)
{
return lootRecordMultimap.get(name);
}
private void refreshData()
{
// Pull data from files
lootRecordMultimap.clear();
Collection<LootRecordCustom> recs = writer.loadAllLootTrackerRecords();
for (LootRecordCustom r : recs)
{
lootRecordMultimap.put(r.getName(), r);
}
}
public void refreshDataByName(String name)
{
lootRecordMultimap.removeAll(name);
Collection<LootRecordCustom> recs = writer.loadLootTrackerRecords(name);
lootRecordMultimap.putAll(name, recs);
}
public void clearStoredDataByName(String name)
{
lootRecordMultimap.removeAll(name);
writer.deleteLootTrackerRecords(name);
}
public Set<String> getNames()
{
return new TreeSet<>(lootRecordMultimap.keySet());
}
// Pet Handling
private ItemStack handlePet(String name)
{
gotPet = false;
int petID = getPetId(name);
if (petID == -1)
{
return null;
}
return new ItemStack(petID, 1, client.getLocalPlayer().getLocalLocation());
}
private int getPetId(String name)
{
Pet pet = Pet.getByBossName(name);
if (pet != null)
{
return pet.getPetID();
}
return -1;
}
// Handles adding the unsired loot to the tracker
private void receivedUnsiredLoot(int itemID)
{
clientThread.invokeLater(() ->
{
Collection<LootRecordCustom> data = getDataByName("Abyssal sire");
ItemDefinition c = itemManager.getItemDefinition(itemID);
LootTrackerItemEntry itemEntry = new LootTrackerItemEntry(c.getName(), itemID, 1, 0, 0, false);
log.debug("Received Unsired item: {}", c.getName());
// Don't have data for sire, create a new record with just this data.
if (data == null)
{
log.debug("No previous Abyssal sire loot, creating new loot record");
LootRecordCustom r = new LootRecordCustom(NpcID.ABYSSAL_SIRE, "Abyssal sire", 350, -1, Collections.singletonList(itemEntry));
writer.addLootTrackerRecord(r);
return;
}
log.debug("Adding drop to last abyssal sire loot record");
// Add data to last kill count
List<LootRecordCustom> items = new ArrayList<>(data);
LootRecordCustom r = items.get(items.size() - 1);
r.addDropEntry(itemEntry);
writer.writeLootTrackerFile("Abyssal sire", items);
});
}
private boolean unsiredThreadRunning = false;
private int unsiredThreadTries = 0;
// Handles checking for unsired loot reclamation
private void checkUnsiredWidget()
{
if (unsiredThreadRunning)
{
return;
}
unsiredThreadRunning = true;
unsiredThreadTries = 0;
clientThread.invokeLater(() ->
{
log.debug("Checking for text widget change...");
Widget text = client.getWidget(WidgetInfo.DIALOG_SPRITE_TEXT);
if ("the font consumes the unsired and returns you a reward.".equals(text.getText().toLowerCase()))
{
Widget sprite = client.getWidget(WidgetInfo.DIALOG_SPRITE);
log.debug("Sprite: {}", sprite);
log.debug("Sprite Item ID: {}", sprite.getItemId());
log.debug("Sprite Model ID: {}", sprite.getModelId());
receivedUnsiredLoot(sprite.getItemId());
unsiredThreadRunning = false;
return true;
}
else
{
if (unsiredThreadTries >= 10)
{
log.debug("Tried 10 times, canceling...");
return true;
}
unsiredThreadTries++;
return false;
}
});
}
private void onGameStateChanged(GameStateChanged c)
{
if (c.getGameState().equals(GameState.LOGGED_IN))
{
clientThread.invokeLater(() ->
{
switch (client.getGameState())
{
case LOGGED_IN:
break;
case LOGGING_IN:
case LOADING:
return false;
default:
// Quit running if any other state
return true;
}
String name = client.getLocalPlayer().getName();
if (name != null)
{
writer.setPlayerUsername(name);
return true;
}
else
{
return false;
}
});
}
}
}

View File

@@ -1,62 +0,0 @@
/*
* Copyright (c) 2018, TheStonedTurtle <https://github.com/TheStonedTurtle>
* 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.stonedloottracker.data;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import lombok.AccessLevel;
import lombok.Getter;
@Getter(AccessLevel.PUBLIC)
@Deprecated
public class LootRecord
{
private final int id;
private final String name;
private final int level;
private final int killCount;
final Collection<LootTrackerItemEntry> drops;
public LootRecord(final int id, final String name, final int level, final int kc, final Collection<LootTrackerItemEntry> drops)
{
this.id = id;
this.name = name;
this.level = level;
this.killCount = kc;
this.drops = (drops == null ? new ArrayList<>() : drops);
}
LootRecordCustom toNewFormat()
{
List<LootTrackerItemEntry> drops = new ArrayList<>();
for (LootTrackerItemEntry e : this.getDrops())
{
drops.add(new LootTrackerItemEntry(e.getName(), e.getId(), e.getQuantity(), e.getPrice(), e.getHaPrice(), e.isStackable()));
}
return new LootRecordCustom(this.getId(), this.getName(), this.getLevel(), this.getKillCount(), drops);
}
}

View File

@@ -1,111 +0,0 @@
/*
* Copyright (c) 2018, TheStonedTurtle <www.github.com/TheStonedTurtle>
* 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.stonedloottracker.data;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import lombok.AccessLevel;
import lombok.Getter;
@Getter(AccessLevel.PUBLIC)
public class LootRecordCustom
{
private final int id;
private final String name;
private final int level;
private final int killCount;
final Collection<LootTrackerItemEntry> drops;
public LootRecordCustom(final int id, final String name, final int level, final int kc, final Collection<LootTrackerItemEntry> drops)
{
this.id = id;
this.name = name;
this.level = level;
this.killCount = kc;
this.drops = (drops == null ? new ArrayList<>() : drops);
}
/**
* Add the requested ItemStack to this LootRecord
* @param drop ItemStack to add
*/
public void addDropEntry(LootTrackerItemEntry drop)
{
drops.add(drop);
}
public static Map<Integer, LootTrackerItemEntry> consolidateLootTrackerItemEntries(Collection<LootRecordCustom> records)
{
// Store LootTrackerItemEntry by ItemID
Map<Integer, LootTrackerItemEntry> itemMap = new HashMap<>();
for (LootRecordCustom r : records)
{
for (LootTrackerItemEntry e : r.getDrops())
{
int old = 0;
if (itemMap.containsKey(e.getId()))
{
old = itemMap.get(e.getId()).getQuantity();
itemMap.remove(e.getId());
}
itemMap.put(e.getId(), new LootTrackerItemEntry(e.getName(), e.getId(), e.getQuantity() + old, e.getPrice(), e.getHaPrice(), e.isStackable()));
}
}
return itemMap;
}
@Override
public String toString()
{
StringBuilder m = new StringBuilder();
m.append("LootRecord{id=")
.append(id)
.append(",name=")
.append(name)
.append(",level=")
.append(level)
.append(",killCount=")
.append(killCount)
.append(",drops=[");
boolean addComma = false;
for (LootTrackerItemEntry d : drops)
{
if (addComma)
{
m.append(",");
}
m.append(d.toString());
addComma = true;
}
m.append("]}");
return m.toString();
}
}

View File

@@ -1,74 +0,0 @@
package net.runelite.client.plugins.stonedloottracker.data;
import java.util.HashMap;
import java.util.Map;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.api.ItemID;
@Getter(AccessLevel.PUBLIC)
public enum Pet
{
// GWD Pets
PET_GENERAL_GRAARDOR(ItemID.PET_GENERAL_GRAARDOR, "General graador"),
PET_KREEARRA(ItemID.PET_KREEARRA, "Kree'arra"),
PET_KRIL_TSUTSAROTH(ItemID.PET_KRIL_TSUTSAROTH, "K'ril tsutsaroth"),
PET_ZILYANA(ItemID.PET_ZILYANA, "Commander zilyana"),
// Wildy Pets
CALLISTO_CUB(ItemID.CALLISTO_CUB, "Callisto"),
PET_CHAOS_ELEMENTAL(ItemID.PET_CHAOS_ELEMENTAL, "Chaos Elemental", "Chaos Fanatic"),
SCORPIAS_OFFSPRING(ItemID.SCORPIAS_OFFSPRING, "Scorpia"),
VENENATIS_SPIDERLING(ItemID.VENENATIS_SPIDERLING, "Venenatis"),
VETION_JR(ItemID.VETION_JR, "Vet'ion"),
// KBD isn't really in wildy but meh
PRINCE_BLACK_DRAGON(ItemID.PRINCE_BLACK_DRAGON, "King Black Dragon"),
// Slayer Pets
ABYSSAL_ORPHAN(ItemID.ABYSSAL_ORPHAN, "Abyssal Sire"),
HELLPUPPY(ItemID.HELLPUPPY, "Cerberus"),
NOON(ItemID.NOON, "Noon", "Dusk"),
PET_KRAKEN(ItemID.PET_KRAKEN, "Kraken"),
PET_SMOKE_DEVIL(ItemID.PET_SMOKE_DEVIL, "Thermonuclear Smoke Devil"),
SKOTOS(ItemID.SKOTOS, "Skotizo"),
// Other Bosses
BABY_MOLE(ItemID.BABY_MOLE, "Giant Mole"),
KALPHITE_PRINCESS(ItemID.KALPHITE_PRINCESS, "Kalphite Queen"),
OLMLET(ItemID.OLMLET, "Chambers of Xeric"),
LIL_ZIK(ItemID.LIL_ZIK, "Theatre of Blood"),
PET_DARK_CORE(ItemID.PET_DARK_CORE, "Corporeal Beast"),
PET_SNAKELING(ItemID.PET_SNAKELING, "Zulrah"),
PET_DAGANNOTH_REX(ItemID.PET_DAGANNOTH_REX, "Dagannoth Rex"),
PET_DAGANNOTH_PRIME(ItemID.PET_DAGANNOTH_PRIME, "Dagannoth Prime"),
PET_DAGANNOTH_SUPREME(ItemID.PET_DAGANNOTH_SUPREME, "Dagannoth Supreme"),
VORKI(ItemID.VORKI, "Vorkath"),
BLOODHOUND(ItemID.BLOODHOUND, "Clue Scroll Master"),
HERBI(ItemID.HERBI, "Herbiboar");
private final int petID;
private final String[] bossNames;
private static final Map<String, Pet> byBossName = buildBossMap();
Pet(final int id, final String... bossNames)
{
this.petID = id;
this.bossNames = bossNames;
}
public static Pet getByBossName(String name)
{
return byBossName.get(name.toUpperCase());
}
private static Map<String, Pet> buildBossMap()
{
Map<String, Pet> byName = new HashMap<>();
for (Pet pet : values())
{
String[] droppingBosses = pet.getBossNames();
for (String bossName : droppingBosses)
{
byName.put(bossName.toUpperCase(), pet);
}
}
return byName;
}
}

View File

@@ -1,931 +0,0 @@
/*
* Copyright (c) 2018, TheStonedTurtle <www.github.com/TheStonedTurtle>
* 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.stonedloottracker.data;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.runelite.api.ItemID;
@RequiredArgsConstructor
@Getter(AccessLevel.PUBLIC)
public enum UniqueItem
{
// Shared "Uniques" (Dropped by multiple activities)
// God Wars
GODSWORD_SHARD_1("Godsword shard 1", ItemID.GODSWORD_SHARD_1, "Kree'arra", "General Graardor", "Commander Zilyana", "K'ril Tsutsaroth"),
GODSWORD_SHARD_2("Godsword shard 2", ItemID.GODSWORD_SHARD_2, "Kree'arra", "General Graardor", "Commander Zilyana", "K'ril Tsutsaroth"),
GODSWORD_SHARD_3("Godsword shard 3", ItemID.GODSWORD_SHARD_3, "Kree'arra", "General Graardor", "Commander Zilyana", "K'ril Tsutsaroth"),
// Wildy
PET_CHAOS_ELEMENTAL("Pet chaos elemental", ItemID.PET_CHAOS_ELEMENTAL, "Chaos Elemental", "Chaos Fanatic"),
// MYSTERIOUS_EMBLEM("Mysterious emblem", ItemID.MYSTERIOUS_EMBLEM , ""),
CURVED_BONE("Curved bone", ItemID.CURVED_BONE, "Callisto", "Vet'ion", "Venenatis", "Giant Mole"),
DRAGON_PICKAXE("Dragon pickaxe", ItemID.DRAGON_PICKAXE , "Callisto", "Vet'ion", "Venenatis", "King Black Dragon", "Chaos Elemental"),
DRAGON_2H_SWORD("Dragon 2h sword", ItemID.DRAGON_2H_SWORD , "Callisto", "Vet'ion", "Venenatis", "Kalphite Queen"),
// Other
DRAGON_CHAINBODY("Dragon chainbody", ItemID.DRAGON_CHAINBODY_3140, "Thermonuclear Smoke Devil", "Kalphite Queen"),
DRAGON_AXE("Dragon axe", ItemID.DRAGON_AXE, "Dagannoth Rex", "Dagannoth Prime", "Dagannoth Supreme"),
UNCUT_ONYX("Uncut onyx", ItemID.UNCUT_ONYX, "Zulrah", "Skotizo"),
DRACONIC_VISAGE("Draconic visage", ItemID.DRACONIC_VISAGE, "Vorkath", "King Black Dragon"),
// Unique Items
// Barrows Uniques
// Ahrim
AHRIMS_HOOD("Ahrim's hood", ItemID.AHRIMS_HOOD, "Barrows", "Ahrims", 0),
AHRIMS_ROBETOP("Ahrim's robetop", ItemID.AHRIMS_ROBETOP, "Barrows", "Ahrims", 0),
AHRIMS_ROBESKIRT("Ahrim's robeskirt", ItemID.AHRIMS_ROBESKIRT, "Barrows", "Ahrims", 0),
AHRIMS_STAFF("Ahrim's staff", ItemID.AHRIMS_STAFF, "Barrows", "Ahrims", 0),
// Dharok
DHAROKS_HELM("Dharok's helm", ItemID.DHAROKS_HELM, "Barrows", "Dharoks", 1),
DHAROKS_PLATEBODY("Dharok's platebody", ItemID.DHAROKS_PLATEBODY, "Barrows", "Dharoks", 1),
DHAROKS_PLATELEGS("Dharok's platelegs", ItemID.DHAROKS_PLATELEGS, "Barrows", "Dharoks", 1),
DHAROKS_GREATAXE("Dharok's greataxe", ItemID.DHAROKS_GREATAXE, "Barrows", "Dharoks", 1),
// Guthans
GUTHANS_HELM("Guthan's helm", ItemID.GUTHANS_HELM, "Barrows", "Guthans", 2),
GUTHANS_PLATEBODY("Guthan's platebody", ItemID.GUTHANS_PLATEBODY, "Barrows", "Guthans", 2),
GUTHANS_CHAINSKIRT("Guthan's Chainskirt", ItemID.GUTHANS_CHAINSKIRT, "Barrows", "Guthans", 2),
GUTHANS_WARSPEAR("Guthan's warspear", ItemID.GUTHANS_WARSPEAR, "Barrows", "Guthans", 2),
// Karils
KARILS_COIF("Karil's coif", ItemID.KARILS_COIF, "Barrows", "Karils", 3),
KARILS_LEATHERTOP("Karil's leathertop", ItemID.KARILS_LEATHERTOP, "Barrows", "Karils", 3),
KARILS_LEATHERSKIRT("Karil's leatherskirt", ItemID.KARILS_LEATHERSKIRT, "Barrows", "Karils", 3),
KARILS_CROSSBOW("Karil's crossbow ", ItemID.KARILS_CROSSBOW, "Barrows", "Karils", 3),
//Torag
TORAGS_HELM("Torag's helm", ItemID.TORAGS_HELM, "Barrows", "Torags", 4),
TORAGS_PLATEBODY("Torags's platebody", ItemID.TORAGS_PLATEBODY, "Barrows", "Torags", 4),
TORAGS_PLATELEGS("Torags's platelegs", ItemID.TORAGS_PLATELEGS, "Barrows", "Torags", 4),
TORAGS_HAMMERS("Torag's hammers", ItemID.TORAGS_HAMMERS, "Barrows", "Torags", 4),
// Veracs
VERACS_HELM("Verac's helm", ItemID.VERACS_HELM, "Barrows", "Veracs", 5),
VERACS_BRASSARD("Verac's brassard", ItemID.VERACS_BRASSARD, "Barrows", "Veracs", 5),
VERACS_PLATESKIRT("Verac's plateskirt", ItemID.VERACS_PLATESKIRT, "Barrows", "Veracs", 5),
VERACS_FLAIL("Verac's flail", ItemID.VERACS_FLAIL, "Barrows", "Veracs", 5),
// Raids Uniques
// Mage
KODAI_INSIGNIA("Kodai insignia", ItemID.KODAI_INSIGNIA, "Chambers of Xeric", "Mage", 0),
ANCESTRAL_HAT("Ancestral hat", ItemID.ANCESTRAL_HAT, "Chambers of Xeric", "Mage", 0),
ANCESTRAL_ROBE_TOP("Ancestral robe top", ItemID.ANCESTRAL_ROBE_TOP, "Chambers of Xeric", "Mage", 0),
ANCESTRAL_ROBE_BOTTOM("Ancestral robe bottom", ItemID.ANCESTRAL_ROBE_BOTTOM, "Chambers of Xeric", "Mage", 0),
// Range
DRAGON_THROWNAXE("Dragon thrownaxe", ItemID.DRAGON_THROWNAXE, "Chambers of Xeric", "Range", 1),
DRAGON_HUNTER_CROSSBOW("Dragon hunter crossbow", ItemID.DRAGON_HUNTER_CROSSBOW, "Chambers of Xeric", "Range", 1),
TWISTED_BUCKLER("Twisted buckler", ItemID.TWISTED_BUCKLER, "Chambers of Xeric", "Range", 1),
TWISTED_BOW("Twisted bow", ItemID.TWISTED_BOW, "Chambers of Xeric", "Range", 1),
// Melee
DRAGON_SWORD("Dragon sword", ItemID.DRAGON_SWORD, "Chambers of Xeric", "Melee", 2),
DRAGON_CLAWS("Dragon claws", ItemID.DRAGON_CLAWS, "Chambers of Xeric", "Melee", 2),
DINHS_BULWARK("Dinh's bulwark", ItemID.DINHS_BULWARK, "Chambers of Xeric", "Melee", 2),
ELDER_MAUL("Elder maul", ItemID.ELDER_MAUL, "Chambers of Xeric", "Melee", 2),
// Prayers
TORN_PRAYER_SCROLL("Torn prayer scroll", ItemID.TORN_PRAYER_SCROLL, "Chambers of Xeric", "Prayer", 3),
ARCANE_PRAYER_SCROLL("Arcane prayer scroll", ItemID.ARCANE_PRAYER_SCROLL, "Chambers of Xeric", "Prayer", 3),
DEXTEROUS_PRAYER_SCROLL("Dexterous prayer scroll", ItemID.DEXTEROUS_PRAYER_SCROLL, "Chambers of Xeric", "Prayer", 3),
// Other
DRAGON_HARPOON("Dragon harpoon", ItemID.DRAGON_HARPOON, "Chambers of Xeric", "Other", 4),
DARK_RELIC("Dark relic", ItemID.DARK_RELIC, "Chambers of Xeric", "Other", 4),
// Raids 2 Unqiues (Theater of Blood)
// Weapons / Pet
GHRAZI_RAPIER("Ghrazi rapier", ItemID.GHRAZI_RAPIER, "Theatre of Blood", "Weapons", 0),
SCYTHE_OF_VITUR_UNCHARGED("Scythe of vitur (uncharged)", ItemID.SCYTHE_OF_VITUR_UNCHARGED, "Theatre of Blood", "Weapons", 0),
SANGUINESTI_STAFF_UNCHAGRED("Sanguinesti staff (uncharged)", ItemID.SANGUINESTI_STAFF_UNCHARGED, "Theatre of Blood", "Weapons", 0),
LIL_ZIK("Lil' zik", ItemID.LIL_ZIK, "Theatre of Blood", "Weapons", 0),
// Armor
AVERNIC_DEFENDER_HILT("Avernic defender hilt", ItemID.AVERNIC_DEFENDER_HILT, "Theatre of Blood", "Armor", 1),
JUSTICIAR_FACEGUARD("Justiciar faceguard", ItemID.JUSTICIAR_FACEGUARD, "Theatre of Blood", "Armor", 1),
JUSTICIAR_CHESTGUARD("Justiciar chestguard", ItemID.JUSTICIAR_CHESTGUARD, "Theatre of Blood", "Armor", 1),
JUSTICIAR_LEGGUARDS("Justiciar legguards", ItemID.JUSTICIAR_LEGGUARDS, "Theatre of Blood", "Armor", 1),
// Zulrah
// Uniques
TANZANITE_FANG("Tanzanite fang", ItemID.TANZANITE_FANG, "Zulrah", "Uniques", -1),
MAGIC_FANG("Magic fang", ItemID.MAGIC_FANG, "Zulrah", "Uniques", -1),
SERPENTINE_VISAGE("Serpentine visage", ItemID.SERPENTINE_VISAGE, "Zulrah", "Uniques", -1),
// Rares
PET_SNAKELING("Pet snakeling", ItemID.PET_SNAKELING, "Zulrah", "Rares", 0),
TANZANITE_MUTAGEN("Tanzanite mutagen", ItemID.TANZANITE_MUTAGEN, "Zulrah", "Rares", 0),
MAGMA_MUTAGEN("Magma mutagen", ItemID.MAGMA_MUTAGEN, "Zulrah", "Rares", 0),
JAR_OF_SWAMP("Jar of swamp", ItemID.JAR_OF_SWAMP, "Zulrah", "Rares", 0),
// Vorkath
DRAGONBONE_NECKLACE("Dragonbone necklace", ItemID.DRAGONBONE_NECKLACE, "Vorkath", "Uniques", 0),
VORKATHS_HEAD("Vorkath's head", ItemID.VORKATHS_HEAD_21907, "Vorkath", "Uniques", 0),
JAR_OF_DECAY("Jar of decay", ItemID.JAR_OF_DECAY, "Vorkath", "Uniques", 0),
SKELETAL_VISAGE("Skeletal visage", ItemID.SKELETAL_VISAGE, "Vorkath", "Uniques", -1),
VORKI("Vorki", ItemID.VORKI, "Vorkath", "Uniques", -1),
// God Wars Dungeon
// Kree'arra (Armadyl)
ARMADYL_HELMET("Armadyl helmet", ItemID.ARMADYL_HELMET, "Kree'arra", "Uniques", 0),
ARMADYL_CHESTPLATE("Armadyl chestplate", ItemID.ARMADYL_CHESTPLATE, "Kree'arra", "Uniques", 0),
ARMADYL_CHAINSKIRT("Armadyl chainskirt", ItemID.ARMADYL_CHAINSKIRT, "Kree'arra", "Uniques", 0),
ARMADYL_HILT("Armadyl hilt", ItemID.ARMADYL_HILT, "Kree'arra", "Uniques", 0),
PET_KREEARRA("Pet Kree'arra", ItemID.PET_KREEARRA, "Kree'arra", "Pet", -1),
// General Graardor (Bandos)
BANDOS_CHESTPLATE("Bandos chestplate", ItemID.BANDOS_CHESTPLATE, "General Graardor", "Uniques", 0),
BANDOS_TASSETS("Bandos tassets", ItemID.BANDOS_TASSETS, "General Graardor", "Uniques", 0),
BANDOS_BOOTS("Bandos boots", ItemID.BANDOS_BOOTS, "General Graardor", "Uniques", 0),
BANDOS_HILT("Bandos hilt", ItemID.BANDOS_HILT, "General Graardor", "Uniques", 0),
PET_GENERAL_GRAARDOR("Pet general graardor", ItemID.PET_GENERAL_GRAARDOR, "General Graardor", "Pet", -1),
// Command Zilyana (Saradomin)
SARADOMIN_SWORD("Saradomin sword", ItemID.SARADOMIN_SWORD, "Commander Zilyana", "Uniques", 0),
ARMADYL_CROSSBOW("Armadyl crossbow", ItemID.ARMADYL_CROSSBOW, "Commander Zilyana", "Uniques", 0),
SARADOMINS_LIGHT("Saradomins light", ItemID.SARADOMINS_LIGHT, "Commander Zilyana", "Uniques", 0),
SARADOMIN_HILT("Saradomin hilt", ItemID.SARADOMIN_HILT, "Commander Zilyana", "Uniques", 0),
PET_ZILYANA("Pet zilyana", ItemID.PET_ZILYANA, "Commander Zilyana", "Pet", -1),
// K'ril Tsutsaroth (Zammy)
STEAM_BATTLESTAFF("Steam battlestaff", ItemID.STEAM_BATTLESTAFF, "K'ril Tsutsaroth", "Uniques", 0),
STAFF_OF_THE_DEAD("Staff of the dead", ItemID.STAFF_OF_THE_DEAD, "K'ril Tsutsaroth", "Uniques", 0),
ZAMORAKIAN_SPEAR("Zamorakian spear", ItemID.ZAMORAKIAN_SPEAR, "K'ril Tsutsaroth", "Uniques", 0),
ZAMORAK_HILT("Zamorak hilt", ItemID.ZAMORAK_HILT, "K'ril Tsutsaroth", "Uniques", 0),
PET_KRIL_TSUTSAROTH("Pet k'ril tsutsaroth", ItemID.PET_KRIL_TSUTSAROTH, "K'ril Tsutsaroth", "Pet", -1),
// Wildy Bosses
// Vet'ion
RING_OF_THE_GODS("Ring of the gods", ItemID.RING_OF_THE_GODS , "Vet'ion", "Uniques", 0),
VETION_JR("Vet'ion jr.", ItemID.VETION_JR , "Vet'ion", "Uniques", 0),
SKELETON_CHAMPION_SCROLL("Skeleton champion scroll", ItemID.SKELETON_CHAMPION_SCROLL , "Vet'ion", "Uniques", 0),
// Venenatis
TREASONOUS_RING("Treasonous ring", ItemID.TREASONOUS_RING , "Venenatis", "Uniques", -1),
VENENATIS_SPIDERLING("Venenatis spiderling", ItemID.VENENATIS_SPIDERLING , "Venenatis", "Uniques", -1),
// Callisto
TYRANNICAL_RING("Tyrannical ring", ItemID.TYRANNICAL_RING , "Callisto", "Uniques", -1),
CALLISTO_CUB("Callisto cub", ItemID.CALLISTO_CUB , "Callisto", "Uniques", -1),
// Chaos Elemental Uniques are all in Shared
// Chaos Fanatic
ODIUM_SHARD_1("Odium shard 1", ItemID.ODIUM_SHARD_1 , "Chaos Fanatic", "Uniques", -1),
MALEDICTION_SHARD_1("Malediction shard 1", ItemID.MALEDICTION_SHARD_1 , "Chaos Fanatic", "Uniques", -1),
// Crazy Archaeologist
ODIUM_SHARD_2("Odium shard 2", ItemID.ODIUM_SHARD_2 , "Crazy Archaeologist", "Uniques", 0),
MALEDICTION_SHARD_2("Malediction shard 2", ItemID.MALEDICTION_SHARD_2 , "Crazy Archaeologist", "Uniques", 0),
FEDORA("Fedora", ItemID.FEDORA , "Crazy Archaeologist", "Uniques", 0),
// Scorpia
ODIUM_SHARD_3("Odium shard 3", ItemID.ODIUM_SHARD_3 , "Scorpia", "Uniques", 0),
MALEDICTION_SHARD_3("Malediction shard 3", ItemID.MALEDICTION_SHARD_3 , "Scorpia", "Uniques", 0),
SCORPIAS_OFFSPRING("Scorpia's offspring", ItemID.SCORPIAS_OFFSPRING , "Scorpia", "Uniques", 0),
// King Black Dragon,
KBD_HEADS("Kbd heads", ItemID.KBD_HEADS , "King Black Dragon", "Uniques", -1),
PRINCE_BLACK_DRAGON("Prince black dragon", ItemID.PRINCE_BLACK_DRAGON , "King Black Dragon", "Uniques", -1),
// Slayer Bosses
// Skotizo
DARK_CLAW("Dark claw", ItemID.DARK_CLAW, "Skotizo", "Uniques", -1),
SKOTOS("Skotos", ItemID.SKOTOS, "Skotizo", "Uniques", -1),
JAR_OF_DARKNESS("Jar of darkness", ItemID.JAR_OF_DARKNESS, "Skotizo", "Uniques", -1),
// Grotesque Guardians
GRANITE_GLOVES("Granite gloves", ItemID.GRANITE_GLOVES, "Dusk", "Uniques", 0),
GRANITE_RING("Granite ring", ItemID.GRANITE_RING, "Dusk", "Uniques", 0),
GRANITE_HAMMER("Granite hammer", ItemID.GRANITE_HAMMER, "Dusk", "Uniques", 0),
BLACK_TOURMALINE_CORE("Black touramline core", ItemID.BLACK_TOURMALINE_CORE, "Dusk", "Uniques 2", 1),
NOON("Noon", ItemID.NOON, "Dusk", "Uniques 2", 1),
JAR_OF_STONE("Jar of stone", ItemID.JAR_OF_STONE, "Dusk", "Uniques 2", 1),
// Abyssal Sire
UNSIRED("Unsired", ItemID.UNSIRED, "Abyssal Sire", "Uniques", 0),
BLUDGEON_CLAW("Bludgeon claw", ItemID.BLUDGEON_CLAW, "Abyssal Sire", "Uniques", 0),
BLUDGEON_SPINE("Bludgeon spine", ItemID.BLUDGEON_SPINE, "Abyssal Sire", "Uniques", 0),
BLUDGEON_AXON("Bludgeon axon", ItemID.BLUDGEON_AXON, "Abyssal Sire", "Uniques", 0),
ABYSSAL_DAGGER("Abyssal dagger", ItemID.ABYSSAL_DAGGER, "Abyssal Sire", "Uniques 2", 1),
ABYSSAL_WHIP("Abyssal whip", ItemID.ABYSSAL_WHIP, "Abyssal Sire", "Uniques 2", 1),
ABYSSAL_ORPHAN("Abyssal orphan", ItemID.ABYSSAL_ORPHAN, "Abyssal Sire", "Uniques 2", 1),
JAR_OF_MIASMA("Jar of miasma", ItemID.JAR_OF_MIASMA, "Abyssal Sire", "Uniques 2", 1),
ABYSSAL_HEAD("Abyssal head", ItemID.ABYSSAL_HEAD, "Abyssal Sire", "Uniques 2", 1),
// Kraken
TRIDENT_OF_THE_SEAS_FULL("Trident of the seas (full)", ItemID.TRIDENT_OF_THE_SEAS_FULL, "Kraken", "Uniques", 0),
KRAKEN_TENTACLE("Kraken tentacle", ItemID.KRAKEN_TENTACLE, "Kraken", "Uniques", 0),
JAR_OF_DIRT("Jar of dirt", ItemID.JAR_OF_DIRT, "Kraken", "Uniques", 0),
PET_KRAKEN("Pet kraken", ItemID.PET_KRAKEN, "Kraken", "Uniques", 0),
// Cerberus
PRIMORDIAL_CRYSTAL("Primordial crystal", ItemID.PRIMORDIAL_CRYSTAL, "Cerberus", "Uniques", 0),
PEGASIAN_CRYSTAL("Pegasian crystal", ItemID.PEGASIAN_CRYSTAL, "Cerberus", "Uniques", 0),
ETERNAL_CRYSTAL("Eternal crystal", ItemID.ETERNAL_CRYSTAL, "Cerberus", "Uniques", 0),
SMOULDERING_STONE("Smouldering stone", ItemID.SMOULDERING_STONE, "Cerberus", "Uniques", 1),
JAR_OF_SOULS("Jar of souls", ItemID.JAR_OF_SOULS, "Cerberus", "Uniques", 1),
HELLPUPPY("Hellpuppy", ItemID.HELLPUPPY, "Cerberus", "Uniques", 1),
// Thermonuclear Smoke Devil
SMOKE_BATTLESTAFF("Smoke battlestaff", ItemID.SMOKE_BATTLESTAFF, "Thermonuclear Smoke Devil", "Uniques", -1),
OCCULT_NECKLACE("Occult necklace", ItemID.OCCULT_NECKLACE, "Thermonuclear Smoke Devil", "Uniques", -1),
PET_SMOKE_DEVIL("Pet smoke devil", ItemID.PET_SMOKE_DEVIL, "Thermonuclear Smoke Devil", "Uniques", -1),
// Other Bosses
// Giant Mole
BABY_MOLE("Baby Mole", ItemID.BABY_MOLE, "Giant Mole", "Uniques", -1),
// Kalphite Queen
KQ_HEAD("Kq head", ItemID.KQ_HEAD, "Kalphite Queen", "Uniques", -1),
JAR_OF_SAND("Jar of sand", ItemID.JAR_OF_SAND, "Kalphite Queen", "Uniques", -1),
KALPHITE_PRINCESS("Kalphite princess", ItemID.KALPHITE_PRINCESS, "Kalphite Queen", "Uniques", -1),
// Corporeal Beast
SPIRIT_SHIELD("Spirit shield", ItemID.SPIRIT_SHIELD, "Corporeal Beast", "Uniques", 0),
HOLY_ELIXIR("Holy elixir", ItemID.HOLY_ELIXIR, "Corporeal Beast", "Uniques", 0),
PET_DARK_CORE("Pet dark core", ItemID.PET_DARK_CORE, "Corporeal Beast", "Uniques", 0),
SPECTRAL_SIGIL("Spectral sigil", ItemID.SPECTRAL_SIGIL, "Corporeal Beast", "Sigil", 1),
ARCANE_SIGIL("Arcane sigil", ItemID.ARCANE_SIGIL, "Corporeal Beast", "Sigil", 1),
ELYSIAN_SIGIL("Elysian sigil", ItemID.ELYSIAN_SIGIL, "Corporeal Beast", "Sigil", 1),
// Dagannoth Rex
WARRIOR_RING("Warrior ring", ItemID.WARRIOR_RING, "Dagannoth Rex", "Uniques", -1),
BERSERKER_RING("Berserk ring", ItemID.BERSERKER_RING, "Dagannoth Rex", "Uniques", -1),
PET_DAGANNOTH_REX("Pet dagannoth rex", ItemID.PET_DAGANNOTH_REX, "Dagannoth Rex", "Uniques", -1),
// Dagannoth Prime
MUD_BATTLESTAFF("Mud battlestaff", ItemID.MUD_BATTLESTAFF, "Dagannoth Prime", "Uniques", -1),
SEERS_RING("Seers ring", ItemID.SEERS_RING, "Dagannoth Prime", "Uniques", -1),
PET_DAGANNOTH_PRIME("Pet dagannoth prime", ItemID.PET_DAGANNOTH_PRIME, "Dagannoth Prime", "Uniques", -1),
// Dagannoth Supreme
SEERCULL("Seercrull", ItemID.SEERCULL, "Dagannoth Supreme", "Uniques", -1),
ARCHERS_RING("Archers ring", ItemID.ARCHERS_RING, "Dagannoth Supreme", "Uniques", -1),
PET_DAGANNOTH_SUPREME("Pet dagannoth supreme", ItemID.PET_DAGANNOTH_SUPREME, "Dagannoth Supreme", "Uniques", -1),
// Beginner Clue Scrolls
MOLE_SLIPPERS("Mole slippers", ItemID.MOLE_SLIPPERS, "Clue Scroll (Beginner)", "Slippers", -1),
FROG_SLIPPERS("Frog slippers", ItemID.FROG_SLIPPERS, "Clue Scroll (Beginner)", "Slippers", -1),
BEAR_FEET("Bear slippers", ItemID.BEAR_FEET, "Clue Scroll (Beginner)", "Slippers", -1),
DEMON_FEET("Demon slippers", ItemID.DEMON_FEET, "Clue Scroll (Beginner)", "Slippers", -1),
SANDWICH_LADY_HAT("Sandwich lady hat", ItemID.SANDWICH_LADY_HAT, "Clue Scroll (Beginner)", "Sandwich Lady", 0),
SANDWICH_LADY_TOP("Sandwich lady top", ItemID.SANDWICH_LADY_TOP, "Clue Scroll (Beginner)", "Sandwich Lady", 0),
SANDWICH_LADY_BOTTOM("Sandwich lady bottom", ItemID.SANDWICH_LADY_BOTTOM, "Clue Scroll (Beginner)", "Sandwich Lady", 0),
MONKS_ROBE_TOP_T("Monk robe top (t)", ItemID.MONKS_ROBE_TOP_T, "Clue Scroll (Beginner)", "Monk Robes", 1),
MONKS_ROBE_T("Monk robe (t)", ItemID.MONKS_ROBE_T, "Clue Scroll (Beginner)", "Monk Robes", 1),
JESTER_CAPE("Jester cape", ItemID.JESTER_CAPE, "Clue Scroll (Beginner)", "Other", 2),
SHOULDER_PARROT("Shoulder parrot", ItemID.SHOULDER_PARROT, "Clue Scroll (Beginner)", "Other", 2),
AMULET_OF_DEFENCE_T("Amulet of Defence (t)", ItemID.AMULET_OF_DEFENCE_T, "Clue Scroll (Beginner)", "Other", 2),
RUNE_SCIMITAR_ORNAMENT_KIT_GUTHIX("Rune scimitar Guthix ornament kit", ItemID.RUNE_SCIMITAR_ORNAMENT_KIT_GUTHIX, "Clue Scroll (Beginner)", "God Ornament Kit", 3),
RUNE_SCIMITAR_ORNAMENT_KIT_SARADOMIN("Rune scimitar Saradomin ornament kit", ItemID.RUNE_SCIMITAR_ORNAMENT_KIT_SARADOMIN, "Clue Scroll (Beginner)", "God Ornament Kit", 3),
RUNE_SCIMITAR_ORNAMENT_KIT_ZAMORAK("Rune scimitar Zamorak ornament kit", ItemID.RUNE_SCIMITAR_ORNAMENT_KIT_ZAMORAK, "Clue Scroll (Beginner)", "God Ornament Kit", 3),
// Easy Clue Scrolls
BRONZE_FULL_HELM_T("Bronze full helm (t)", ItemID.BRONZE_FULL_HELM_T, "Clue Scroll (Easy)", "Armor", -1),
BRONZE_PLATEBODY_T("Bronze platebody (t)", ItemID.BRONZE_PLATEBODY_T, "Clue Scroll (Easy)", "Armor", -1),
BRONZE_PLATELEGS_T("Bronze platelegs (t)", ItemID.BRONZE_PLATELEGS_T, "Clue Scroll (Easy)", "Armor", -1),
BRONZE_PLATESKIRT_T("Bronze plateskirt (t)", ItemID.BRONZE_PLATESKIRT_T, "Clue Scroll (Easy)", "Armor", -1),
BRONZE_KITESHIELD_T("Bronze kiteshield (t)", ItemID.BRONZE_KITESHIELD_T, "Clue Scroll (Easy)", "Armor", -1),
BRONZE_FULL_HELM_G("Bronze full helm (g)", ItemID.BRONZE_FULL_HELM_G, "Clue Scroll (Easy)", "Armor", 0),
BRONZE_PLATEBODY_G("Bronze platebody (g)", ItemID.BRONZE_PLATEBODY_G, "Clue Scroll (Easy)", "Armor", 0),
BRONZE_PLATELEGS_G("Bronze platelegs (g)", ItemID.BRONZE_PLATELEGS_G, "Clue Scroll (Easy)", "Armor", 0),
BRONZE_PLATESKIRT_G("Bronze plateskirt (g)", ItemID.BRONZE_PLATESKIRT_G, "Clue Scroll (Easy)", "Armor", 0),
BRONZE_KITESHIELD_G("Bronze kiteshield (g)", ItemID.BRONZE_KITESHIELD_G, "Clue Scroll (Easy)", "Armor", 0),
IRON_FULL_HELM_T("Iron full helm (t)", ItemID.IRON_FULL_HELM_T, "Clue Scroll (Easy)", "Armor", 1),
IRON_PLATEBODY_T("Iron platebody (t)", ItemID.IRON_PLATEBODY_T, "Clue Scroll (Easy)", "Armor", 1),
IRON_PLATELEGS_T("Iron platelegs (t)", ItemID.IRON_PLATELEGS_T, "Clue Scroll (Easy)", "Armor", 1),
IRON_PLATESKIRT_T("Iron plateskirt (t)", ItemID.IRON_PLATESKIRT_T, "Clue Scroll (Easy)", "Armor", 1),
IRON_KITESHIELD_T("Iron kiteshield (t)", ItemID.IRON_KITESHIELD_T, "Clue Scroll (Easy)", "Armor", 1),
IRON_FULL_HELM_G("Iron full helm (g)", ItemID.IRON_FULL_HELM_G, "Clue Scroll (Easy)", "Armor", 2),
IRON_PLATEBODY_G("Iron platebody (g)", ItemID.IRON_PLATEBODY_G, "Clue Scroll (Easy)", "Armor", 2),
IRON_PLATELEGS_G("Iron platelegs (g)", ItemID.IRON_PLATELEGS_G, "Clue Scroll (Easy)", "Armor", 2),
IRON_PLATESKIRT_G("Iron plateskirt (g)", ItemID.IRON_PLATESKIRT_G, "Clue Scroll (Easy)", "Armor", 2),
IRON_KITESHIELD_G("Iron kiteshield (g)", ItemID.IRON_KITESHIELD_G, "Clue Scroll (Easy)", "Armor", 2),
STEEL_FULL_HELM_T("Steel full helm (t)", ItemID.STEEL_FULL_HELM_T, "Clue Scroll (Easy)", "Armor", 3),
STEEL_PLATEBODY_T("Steel platebody (t)", ItemID.STEEL_PLATEBODY_T, "Clue Scroll (Easy)", "Armor", 3),
STEEL_PLATELEGS_T("Steel platelegs (t)", ItemID.STEEL_PLATELEGS_T, "Clue Scroll (Easy)", "Armor", 3),
STEEL_PLATESKIRT_T("Steel plateskirt (t)", ItemID.STEEL_PLATESKIRT_T, "Clue Scroll (Easy)", "Armor", 3),
STEEL_KITESHIELD_T("Steel kiteshield (t)", ItemID.STEEL_KITESHIELD_T, "Clue Scroll (Easy)", "Armor", 3),
STEEL_FULL_HELM_G("Steel full helm (g)", ItemID.STEEL_FULL_HELM_G, "Clue Scroll (Easy)", "Armor", 4),
STEEL_PLATEBODY_G("Steel platebody (g)", ItemID.STEEL_PLATEBODY_G, "Clue Scroll (Easy)", "Armor", 4),
STEEL_PLATELEGS_G("Steel platelegs (g)", ItemID.STEEL_PLATELEGS_G, "Clue Scroll (Easy)", "Armor", 4),
STEEL_PLATESKIRT_G("Steel plateskirt (g)", ItemID.STEEL_PLATESKIRT_G, "Clue Scroll (Easy)", "Armor", 4),
STEEL_KITESHIELD_G("Steel kiteshield (g)", ItemID.STEEL_KITESHIELD_G, "Clue Scroll (Easy)", "Armor", 4),
BLACK_FULL_HELM_T("Black full helm (t)", ItemID.BLACK_FULL_HELM_T, "Clue Scroll (Easy)", "Armor", 5),
BLACK_PLATEBODY_T("Black platebody (t)", ItemID.BLACK_PLATEBODY_T, "Clue Scroll (Easy)", "Armor", 5),
BLACK_PLATELEGS_T("Black platelegs (t)", ItemID.BLACK_PLATELEGS_T, "Clue Scroll (Easy)", "Armor", 5),
BLACK_PLATESKIRT_T("Black plateskirt (t)", ItemID.BLACK_PLATESKIRT_T, "Clue Scroll (Easy)", "Armor", 5),
BLACK_KITESHIELD_T("Black kiteshield (t)", ItemID.BLACK_KITESHIELD_T, "Clue Scroll (Easy)", "Armor", 5),
BLACK_FULL_HELM_G("Black full helm (g)", ItemID.BLACK_FULL_HELM_G, "Clue Scroll (Easy)", "Armor", 6),
BLACK_PLATEBODY_G("Black platebody (g)", ItemID.BLACK_PLATEBODY_G, "Clue Scroll (Easy)", "Armor", 6),
BLACK_PLATELEGS_G("Black platelegs (g)", ItemID.BLACK_PLATELEGS_G, "Clue Scroll (Easy)", "Armor", 6),
BLACK_PLATESKIRT_G("Black plateskirt (g)", ItemID.BLACK_PLATESKIRT_G, "Clue Scroll (Easy)", "Armor", 6),
BLACK_KITESHIELD_G("Black kiteshield (g)", ItemID.BLACK_KITESHIELD_G, "Clue Scroll (Easy)", "Armor", 6),
BLUE_WIZARD_HAT_T("Blue wizard hat (t)", ItemID.BLUE_WIZARD_HAT_T, "Clue Scroll (Easy)", "Wizard Robes", 7),
BLUE_WIZARD_ROBE_T("Blue wizard robe (t)", ItemID.BLUE_WIZARD_ROBE_T, "Clue Scroll (Easy)", "Wizard Robes", 7),
BLUE_SKIRT_T("Blue skirt (t)", ItemID.BLUE_SKIRT_T, "Clue Scroll (Easy)", "Wizard Robes", 7),
BLUE_WIZARD_HAT_G("Blue wizard hat (g)", ItemID.BLUE_WIZARD_HAT_G, "Clue Scroll (Easy)", "Wizard Robes", 8),
BLUE_WIZARD_ROBE_G("Blue wizard robe (g)", ItemID.BLUE_WIZARD_ROBE_G, "Clue Scroll (Easy)", "Wizard Robes", 8),
BLUE_SKIRT_G("Blue skirt (g)", ItemID.BLUE_SKIRT_G, "Clue Scroll (Easy)", "Wizard Robes", 8),
BLACK_WIZARD_HAT_T("Black wizard hat (t)", ItemID.BLACK_WIZARD_HAT_T, "Clue Scroll (Easy)", "Wizard Robes", 9),
BLACK_WIZARD_ROBE_T("Black wizard robe (t)", ItemID.BLACK_WIZARD_ROBE_T, "Clue Scroll (Easy)", "Wizard Robes", 9),
BLACK_SKIRT_T("Black skirt (t)", ItemID.BLACK_SKIRT_T, "Clue Scroll (Easy)", "Wizard Robes", 9),
BLACK_WIZARD_HAT_G("Black wizard hat (g)", ItemID.BLACK_WIZARD_HAT_G, "Clue Scroll (Easy)", "Wizard Robes", 10),
BLACK_WIZARD_ROBE_G("Black wizard robe (g)", ItemID.BLACK_WIZARD_ROBE_G, "Clue Scroll (Easy)", "Wizard Robes", 10),
BLACK_SKIRT_G("Black skirt (g)", ItemID.BLACK_SKIRT_G, "Clue Scroll (Easy)", "Wizard Robes", 10),
STUDDED_BODY_T("Studded body (t)", ItemID.STUDDED_BODY_T, "Clue Scroll (Easy)", "Leather Armor", 11),
STUDDED_CHAPS_T("Studded chaps (t)", ItemID.STUDDED_CHAPS_T, "Clue Scroll (Easy)", "Leather Armor", 11),
STUDDED_BODY_G("Studded body (g)", ItemID.STUDDED_BODY_G, "Clue Scroll (Easy)", "Leather Armor", 11),
STUDDED_CHAPS_G("Studded chaps (g)", ItemID.STUDDED_CHAPS_G, "Clue Scroll (Easy)", "Leather Armor", 11),
LEATHER_CHAPS_G("Leather chaps (g)", ItemID.LEATHER_CHAPS_G, "Clue Scroll (Easy)", "Leather Armor", 12),
LEATHER_BODY_G("Leather chaps (g)", ItemID.LEATHER_BODY_G, "Clue Scroll (Easy)", "Leather Armor", 12),
BLACK_HELM_H1("Black helm (h1)", ItemID.BLACK_HELM_H1, "Clue Scroll (Easy)", "Heralid Helm", 13),
BLACK_HELM_H2("Black helm (h2)", ItemID.BLACK_HELM_H2, "Clue Scroll (Easy)", "Heralid Helm", 13),
BLACK_HELM_H3("Black helm (h3)", ItemID.BLACK_HELM_H3, "Clue Scroll (Easy)", "Heralid Helm", 13),
BLACK_HELM_H4("Black helm (h4)", ItemID.BLACK_HELM_H4, "Clue Scroll (Easy)", "Heralid Helm", 13),
BLACK_HELM_H5("Black helm (h5)", ItemID.BLACK_HELM_H5, "Clue Scroll (Easy)", "Heralid Helm", 13),
BLACK_PLATEBODY_H1("Black platebody (h1)", ItemID.BLACK_PLATEBODY_H1, "Clue Scroll (Easy)", "Heralid Platebody", 14),
BLACK_PLATEBODY_H2("Black platebody (h2)", ItemID.BLACK_PLATEBODY_H2, "Clue Scroll (Easy)", "Heralid Platebody", 14),
BLACK_PLATEBODY_H3("Black platebody (h3)", ItemID.BLACK_PLATEBODY_H3, "Clue Scroll (Easy)", "Heralid Platebody", 14),
BLACK_PLATEBODY_H4("Black platebody (h4)", ItemID.BLACK_PLATEBODY_H4, "Clue Scroll (Easy)", "Heralid Platebody", 14),
BLACK_PLATEBODY_H5("Black platebody (h5)", ItemID.BLACK_PLATEBODY_H5, "Clue Scroll (Easy)", "Heralid Platebody", 14),
BLACK_SHIELD_H1("Black shield (h1)", ItemID.BLACK_SHIELD_H1, "Clue Scroll (Easy)", "Heralid Shield", 15),
BLACK_SHIELD_H2("Black shield (h2)", ItemID.BLACK_SHIELD_H2, "Clue Scroll (Easy)", "Heralid Shield", 15),
BLACK_SHIELD_H3("Black shield (h3)", ItemID.BLACK_SHIELD_H3, "Clue Scroll (Easy)", "Heralid Shield", 15),
BLACK_SHIELD_H4("Black shield (h4)", ItemID.BLACK_SHIELD_H4, "Clue Scroll (Easy)", "Heralid Shield", 15),
BLACK_SHIELD_H5("Black shield (h5)", ItemID.BLACK_SHIELD_H5, "Clue Scroll (Easy)", "Heralid Shield", 15),
BLUE_ELEGANT_SHIRT("Blue elegant shirt", ItemID.BLUE_ELEGANT_SHIRT, "Clue Scroll (Easy)", "Elegant", 16),
BLUE_ELEGANT_LEGS("Blue elegant legs", ItemID.BLUE_ELEGANT_LEGS, "Clue Scroll (Easy)", "Elegant", 16),
BLUE_ELEGANT_BLOUSE("Blue elegant blouse", ItemID.BLUE_ELEGANT_BLOUSE, "Clue Scroll (Easy)", "Elegant", 16),
BLUE_ELEGANT_SKIRT("Blue elegant skirt", ItemID.BLUE_ELEGANT_SKIRT, "Clue Scroll (Easy)", "Elegant", 16),
GREEN_ELEGANT_SHIRT("Green elegant shirt", ItemID.GREEN_ELEGANT_SHIRT, "Clue Scroll (Easy)", "Elegant", 17),
GREEN_ELEGANT_LEGS("Green elegant legs", ItemID.GREEN_ELEGANT_LEGS, "Clue Scroll (Easy)", "Elegant", 17),
GREEN_ELEGANT_BLOUSE("Green elegant blouse", ItemID.GREEN_ELEGANT_BLOUSE, "Clue Scroll (Easy)", "Elegant", 17),
GREEN_ELEGANT_SKIRT("Green elegant skirt", ItemID.GREEN_ELEGANT_SKIRT, "Clue Scroll (Easy)", "Elegant", 17),
RED_ELEGANT_SHIRT("Red elegant shirt", ItemID.RED_ELEGANT_SHIRT, "Clue Scroll (Easy)", "Elegant", 18),
RED_ELEGANT_LEGS("Red elegant legs", ItemID.RED_ELEGANT_LEGS, "Clue Scroll (Easy)", "Elegant", 18),
RED_ELEGANT_BLOUSE("Red elegant blouse", ItemID.RED_ELEGANT_BLOUSE, "Clue Scroll (Easy)", "Elegant", 18),
RED_ELEGANT_SKIRT("Red elegant skirt", ItemID.RED_ELEGANT_SKIRT, "Clue Scroll (Easy)", "Elegant", 18),
BOBS_RED_SHIRT("Bob's red shirt", ItemID.BOBS_RED_SHIRT, "Clue Scroll (Easy)", "Bobs Shirt", 19),
BOBS_BLUE_SHIRT("Bob's blue shirt", ItemID.BOBS_BLUE_SHIRT, "Clue Scroll (Easy)", "Bobs Shirt", 19),
BOBS_GREEN_SHIRT("Bob's green shirt", ItemID.BOBS_GREEN_SHIRT, "Clue Scroll (Easy)", "Bobs Shirt", 19),
BOBS_BLACK_SHIRT("Bob's black shirt", ItemID.BOBS_BLACK_SHIRT, "Clue Scroll (Easy)", "Bobs Shirt", 19),
BOBS_PURPLE_SHIRT("Bob's purple shirt", ItemID.BOBS_PURPLE_SHIRT, "Clue Scroll (Easy)", "Bobs Shirt", 19),
A_POWDERED_WIG("A powdered wig", ItemID.A_POWDERED_WIG, "Clue Scroll (Easy)", "Emote Enhancers", 20),
FLARED_TROUSERS("Flared trousers", ItemID.FLARED_TROUSERS, "Clue Scroll (Easy)", "Emote Enhancers", 20),
PANTALOONS("Pantaloons", ItemID.PANTALOONS, "Clue Scroll (Easy)", "Emote Enhancers", 20),
SLEEPING_CAP("Sleeping cap", ItemID.SLEEPING_CAP, "Clue Scroll (Easy)", "Emote Enhancers", 20),
GUTHIX_ROBE_TOP("Guthix robe top", ItemID.GUTHIX_ROBE_TOP, "Clue Scroll (Easy)", "Vestment Robes", 21),
GUTHIX_ROBE_LEGS("Guthix robe legs", ItemID.GUTHIX_ROBE_LEGS, "Clue Scroll (Easy)", "Vestment Robes", 21),
SARADOMIN_ROBE_TOP("Saradomin robe top", ItemID.SARADOMIN_ROBE_TOP, "Clue Scroll (Easy)", "Vestment Robes", 21),
SARADOMIN_ROBE_LEGS("Saradomin robe legs", ItemID.SARADOMIN_ROBE_LEGS, "Clue Scroll (Easy)", "Vestment Robes", 21),
ZAMORAK_ROBE_TOP("Zamorak robe top", ItemID.ZAMORAK_ROBE_TOP, "Clue Scroll (Easy)", "Vestment Robes", 21),
ZAMORAK_ROBE_LEGS("Zamorak robe legs", ItemID.ZAMORAK_ROBE_LEGS, "Clue Scroll (Easy)", "Vestment Robes", 21),
ANCIENT_ROBE_TOP("Ancient robe top", ItemID.ANCIENT_ROBE_TOP, "Clue Scroll (Easy)", "Vestment Robes", 22),
ANCIENT_ROBE_LEGS("Ancient robe legs", ItemID.ANCIENT_ROBE_LEGS, "Clue Scroll (Easy)", "Vestment Robes", 22),
BANDOS_ROBE_TOP("Bandos robe top", ItemID.BANDOS_ROBE_TOP, "Clue Scroll (Easy)", "Vestment Robes", 22),
BANDOS_ROBE_LEGS("Bandos robe legs", ItemID.BANDOS_ROBE_LEGS, "Clue Scroll (Easy)", "Vestment Robes", 22),
ARMADYL_ROBE_TOP("Armadyl robe top", ItemID.ARMADYL_ROBE_TOP, "Clue Scroll (Easy)", "Vestment Robes", 22),
ARMADYL_ROBE_LEGS("Armadyl robe legs", ItemID.ARMADYL_ROBE_LEGS, "Clue Scroll (Easy)", "Vestment Robes", 22),
BLACK_BERET("Black beret", ItemID.BLACK_BERET, "Clue Scroll (Easy)", "Berets", 23),
BLUE_BERET("Blue beret", ItemID.BLUE_BERET, "Clue Scroll (Easy)", "Berets", 23),
WHITE_BERET("White beret", ItemID.WHITE_BERET, "Clue Scroll (Easy)", "Berets", 23),
RED_BERET("Red beret", ItemID.RED_BERET, "Clue Scroll (Easy)", "Berets", 23),
HIGHWAYMAN_MASK("Highwayman mask", ItemID.HIGHWAYMAN_MASK, "Clue Scroll (Easy)", "Masks", 24),
IMP_MASK("Imp mask", ItemID.IMP_MASK, "Clue Scroll (Easy)", "Masks", 24),
GOBLIN_MASK("Goblin mask", ItemID.GOBLIN_MASK, "Clue Scroll (Easy)", "Masks", 24),
BEANIE("Beanie", ItemID.BEANIE, "Clue Scroll (Easy)", "Masks", 24),
TEAM_CAPE_I("Team cape i", ItemID.TEAM_CAPE_I, "Clue Scroll (Easy)", "Cape", 25),
TEAM_CAPE_X("Team cape x", ItemID.TEAM_CAPE_X, "Clue Scroll (Easy)", "Cape", 25),
TEAM_CAPE_ZERO("Team cape zero", ItemID.TEAM_CAPE_ZERO, "Clue Scroll (Easy)", "Cape", 25),
CAPE_OF_SKULLS("Cape of Skulls", ItemID.CAPE_OF_SKULLS, "Clue Scroll (Easy)", "CApe", 25),
AMULET_OF_MAGIC_T("Amulet of magic (t)", ItemID.AMULET_OF_MAGIC_T, "Clue Scroll (Easy)", "Other", 26),
AMULET_OF_POWER_T("Power Amulet (t)", ItemID.AMULET_OF_POWER_T, "Clue Scroll (Easy)", "Other", 26),
BLACK_CANE("Black cane", ItemID.BLACK_CANE, "Clue Scroll (Easy)", "Other", 26),
BLACK_PICKAXE("Black pickaxe", ItemID.BLACK_PICKAXE, "Clue Scroll (Easy)", "Other", 26),
LARGE_SPADE("Large spade", ItemID.LARGE_SPADE, "Clue Scroll (Easy)", "Other", 26),
WOODEN_SHIELD_G("Wooden shield (g)", ItemID.WOODEN_SHIELD_G, "Clue Scroll (Easy)", "Golden", 27),
GOLDEN_CHEFS_HAT("Golden chef's hat", ItemID.GOLDEN_CHEFS_HAT, "Clue Scroll (Easy)", "Golden", 27),
GOLDEN_APRON("Golden apron", ItemID.GOLDEN_APRON, "Clue Scroll (Easy)", "Golden", 27),
MONKS_ROBE_TOP_G("Monk robe top (g)", ItemID.MONKS_ROBE_TOP_G, "Clue Scroll (Easy)", "Golden", 27),
MONKS_ROBE_G("Monk robe (g)", ItemID.MONKS_ROBE_G, "Clue Scroll (Easy)", "Golden", 27),
RAIN_BOW("Rain bow", ItemID.RAIN_BOW, "Clue Scroll (Easy)", "Other", 28),
HAM_JOINT("Ham joint", ItemID.HAM_JOINT, "Clue Scroll (Easy)", "Other", 28),
STAFF_OF_BOB_THE_CAT("Staff of Bob the Cat", ItemID.STAFF_OF_BOB_THE_CAT, "Clue Scroll (Easy)", "Other", 28),
// Medium Clue Scrolls
MITHRIL_FULL_HELM_T("Mithril full helm (t)", ItemID.MITHRIL_FULL_HELM_T, "Clue Scroll (Medium)", "Armor", -1),
MITHRIL_PLATEBODY_T("Mithril platebody (t)", ItemID.MITHRIL_PLATEBODY_T, "Clue Scroll (Medium)", "Armor", -1),
MITHRIL_PLATELEGS_T("Mithril platelegs (t)", ItemID.MITHRIL_PLATELEGS_T, "Clue Scroll (Medium)", "Armor", -1),
MITHRIL_PLATESKIRT_T("Mithril plateskirt (t)", ItemID.MITHRIL_PLATESKIRT_T, "Clue Scroll (Medium)", "Armor", -1),
MITHRIL_KITESHIELD_T("Mithril kiteshield (t)", ItemID.MITHRIL_KITESHIELD_T, "Clue Scroll (Medium)", "Armor", -1),
MITHRIL_FULL_HELM_G("Mithril full helm (g)", ItemID.MITHRIL_FULL_HELM_G, "Clue Scroll (Medium)", "Armor", 0),
MITHRIL_PLATEBODY_G("Mithril platebody (g)", ItemID.MITHRIL_PLATEBODY_G, "Clue Scroll (Medium)", "Armor", 0),
MITHRIL_PLATELEGS_G("Mithril platelegs (g)", ItemID.MITHRIL_PLATELEGS_G, "Clue Scroll (Medium)", "Armor", 0),
MITHRIL_PLATESKIRT_G("Mithril plateskirt (g)", ItemID.MITHRIL_PLATESKIRT_G, "Clue Scroll (Medium)", "Armor", 0),
MITHRIL_KITESHIELD_G("Mithril kiteshield (g)", ItemID.MITHRIL_KITESHIELD_G, "Clue Scroll (Medium)", "Armor", 0),
ADAMANT_FULL_HELM_T("Adamant full helm (t)", ItemID.ADAMANT_FULL_HELM_T, "Clue Scroll (Medium)", "Armor", 1),
ADAMANT_PLATEBODY_T("Adamant platebody (t)", ItemID.ADAMANT_PLATEBODY_T, "Clue Scroll (Medium)", "Armor", 1),
ADAMANT_PLATELEGS_T("Adamant platelegs (t)", ItemID.ADAMANT_PLATELEGS_T, "Clue Scroll (Medium)", "Armor", 1),
ADAMANT_PLATESKIRT_T("Adamant plateskirt (t)", ItemID.ADAMANT_PLATESKIRT_T, "Clue Scroll (Medium)", "Armor", 1),
ADAMANT_KITESHIELD_T("Adamant kiteshield (t)", ItemID.ADAMANT_KITESHIELD_T, "Clue Scroll (Medium)", "Armor", 1),
ADAMANT_FULL_HELM_G("Adamant full helm (g)", ItemID.ADAMANT_FULL_HELM_G, "Clue Scroll (Medium)", "Armor", 2),
ADAMANT_PLATEBODY_G("Adamant platebody (g)", ItemID.ADAMANT_PLATEBODY_G, "Clue Scroll (Medium)", "Armor", 2),
ADAMANT_PLATELEGS_G("Adamant platelegs (g)", ItemID.ADAMANT_PLATELEGS_G, "Clue Scroll (Medium)", "Armor", 2),
ADAMANT_PLATESKIRT_G("Adamant plateskirt (g)", ItemID.ADAMANT_PLATESKIRT_G, "Clue Scroll (Medium)", "Armor", 2),
ADAMANT_KITESHIELD_G("Adamant kiteshield (g)", ItemID.ADAMANT_KITESHIELD_G, "Clue Scroll (Medium)", "Armor", 2),
RANGER_BOOTS("Ranger boots", ItemID.RANGER_BOOTS, "Clue Scroll (Medium)", "Boots", 3),
HOLY_SANDALS("Holy sandals", ItemID.HOLY_SANDALS, "Clue Scroll (Medium)", "Boots", 3),
WIZARD_BOOTS("Wizard boots", ItemID.WIZARD_BOOTS, "Clue Scroll (Medium)", "Boots", 3),
RED_HEADBAND("Red headband", ItemID.RED_HEADBAND, "Clue Scroll (Medium)", "Headbands", 4),
BLACK_HEADBAND("Black headband", ItemID.BLACK_HEADBAND, "Clue Scroll (Medium)", "Headbands", 4),
BROWN_HEADBAND("Brown headband", ItemID.BROWN_HEADBAND, "Clue Scroll (Medium)", "Headbands", 4),
PINK_HEADBAND("Pink headband", ItemID.PINK_HEADBAND, "Clue Scroll (Medium)", "Headbands", 4),
GREEN_HEADBAND("Green headband", ItemID.GREEN_HEADBAND, "Clue Scroll (Medium)", "Headbands", 5),
BLUE_HEADBAND("Blue headband", ItemID.BLUE_HEADBAND, "Clue Scroll (Medium)", "Headbands", 5),
GOLD_HEADBAND("Gold headband", ItemID.GOLD_HEADBAND, "Clue Scroll (Medium)", "Headbands", 5),
WHITE_HEADBAND("White headband", ItemID.WHITE_HEADBAND, "Clue Scroll (Medium)", "Headbands", 5),
RED_BOATER("Red boater", ItemID.RED_BOATER, "Clue Scroll (Medium)", "Boater", 6),
ORANGE_BOATER("Orange boater", ItemID.ORANGE_BOATER, "Clue Scroll (Medium)", "Boater", 6),
GREEN_BOATER("Green boater", ItemID.GREEN_BOATER, "Clue Scroll (Medium)", "Boater", 6),
BLUE_BOATER("Blue boater", ItemID.BLUE_BOATER, "Clue Scroll (Medium)", "Boater", 6),
BLACK_BOATER("Black boater", ItemID.BLACK_BOATER, "Clue Scroll (Medium)", "Boater", 7),
PINK_BOATER("Pink boater", ItemID.PINK_BOATER, "Clue Scroll (Medium)", "Boater", 7),
PURPLE_BOATER("Purple boater", ItemID.PURPLE_BOATER, "Clue Scroll (Medium)", "Boater", 7),
WHITE_BOATER("White boater", ItemID.WHITE_BOATER, "Clue Scroll (Medium)", "Boater", 7),
ADAMANT_HELM_H1("Adamant helm (h1)", ItemID.ADAMANT_HELM_H1, "Clue Scroll (Medium)", "Heraldic", 8),
ADAMANT_HELM_H2("Adamant helm (h2)", ItemID.ADAMANT_HELM_H2, "Clue Scroll (Medium)", "Heraldic", 8),
ADAMANT_HELM_H3("Adamant helm (h3)", ItemID.ADAMANT_HELM_H3, "Clue Scroll (Medium)", "Heraldic", 8),
ADAMANT_HELM_H4("Adamant helm (h4)", ItemID.ADAMANT_HELM_H4, "Clue Scroll (Medium)", "Heraldic", 8),
ADAMANT_HELM_H5("Adamant helm (h5)", ItemID.ADAMANT_HELM_H5, "Clue Scroll (Medium)", "Heraldic", 8),
ADAMANT_SHIELD_H1("Adamant shield (h1)", ItemID.ADAMANT_SHIELD_H1, "Clue Scroll (Medium)", "Heraldic", 9),
ADAMANT_SHIELD_H2("Adamant shield (h2)", ItemID.ADAMANT_SHIELD_H2, "Clue Scroll (Medium)", "Heraldic", 9),
ADAMANT_SHIELD_H3("Adamant shield (h3)", ItemID.ADAMANT_SHIELD_H3, "Clue Scroll (Medium)", "Heraldic", 9),
ADAMANT_SHIELD_H4("Adamant shield (h4)", ItemID.ADAMANT_SHIELD_H4, "Clue Scroll (Medium)", "Heraldic", 9),
ADAMANT_SHIELD_H5("Adamant shield (h5)", ItemID.ADAMANT_SHIELD_H5, "Clue Scroll (Medium)", "Heraldic", 9),
ADAMANT_PLATEBODY_H1("Adamant platebody (h1)", ItemID.ADAMANT_PLATEBODY_H1, "Clue Scroll (Medium)", "Armor", 10),
ADAMANT_PLATEBODY_H2("Adamant platebody (h2)", ItemID.ADAMANT_PLATEBODY_H2, "Clue Scroll (Medium)", "Armor", 10),
ADAMANT_PLATEBODY_H3("Adamant platebody (h3)", ItemID.ADAMANT_PLATEBODY_H3, "Clue Scroll (Medium)", "Armor", 10),
ADAMANT_PLATEBODY_H4("Adamant platebody (h4)", ItemID.ADAMANT_PLATEBODY_H4, "Clue Scroll (Medium)", "Armor", 10),
ADAMANT_PLATEBODY_H5("Adamant platebody (h5)", ItemID.ADAMANT_PLATEBODY_H5, "Clue Scroll (Medium)", "Armor", 10),
GREEN_DHIDE_BODY_T("Green dragonhide body (t)", ItemID.GREEN_DHIDE_BODY_T, "Clue Scroll (Medium)", "Dhide", 11),
GREEN_DHIDE_CHAPS_T("Green dragonhide chaps (t)", ItemID.GREEN_DHIDE_CHAPS_T, "Clue Scroll (Medium)", "Dhide", 11),
GREEN_DHIDE_BODY_G("Green dragonhide body (g)", ItemID.GREEN_DHIDE_BODY_G, "Clue Scroll (Medium)", "Dhide", 11),
GREEN_DHIDE_CHAPS_G("Green dragonhide chaps (g)", ItemID.GREEN_DHIDE_CHAPS_G, "Clue Scroll (Medium)", "Dhide", 11),
BLACK_ELEGANT_SHIRT("Black elegant shirt", ItemID.BLACK_ELEGANT_SHIRT, "Clue Scroll (Medium)", "Elegeant", 12),
BLACK_ELEGANT_LEGS("Black elegant legs", ItemID.BLACK_ELEGANT_LEGS, "Clue Scroll (Medium)", "Elegeant", 12),
WHITE_ELEGANT_BLOUSE("White elegant blouse", ItemID.WHITE_ELEGANT_BLOUSE, "Clue Scroll (Medium)", "Elegeant", 12),
WHITE_ELEGANT_SKIRT("White elegant skirt", ItemID.WHITE_ELEGANT_SKIRT, "Clue Scroll (Medium)", "Elegeant", 12),
PURPLE_ELEGANT_SHIRT("Purple elegant shirt", ItemID.PURPLE_ELEGANT_SHIRT, "Clue Scroll (Medium)", "Elegeant", 13),
PURPLE_ELEGANT_LEGS("Purple elegant legs", ItemID.PURPLE_ELEGANT_LEGS, "Clue Scroll (Medium)", "Elegeant", 13),
PURPLE_ELEGANT_BLOUSE("Purple elegant blouse", ItemID.PURPLE_ELEGANT_BLOUSE, "Clue Scroll (Medium)", "Elegeant", 13),
PURPLE_ELEGANT_SKIRT("Purple elegant skirt", ItemID.PURPLE_ELEGANT_SKIRT, "Clue Scroll (Medium)", "Elegeant", 13),
PINK_ELEGANT_SHIRT("Pink elegant shirt", ItemID.PINK_ELEGANT_SHIRT, "Clue Scroll (Medium)", "Elegeant", 14),
PINK_ELEGANT_LEGS("Pink elegant legs", ItemID.PINK_ELEGANT_LEGS, "Clue Scroll (Medium)", "Elegeant", 14),
PINK_ELEGANT_BLOUSE("Pink elegant blouse", ItemID.PINK_ELEGANT_BLOUSE, "Clue Scroll (Medium)", "Elegeant", 14),
PINK_ELEGANT_SKIRT("Pink elegant skirt", ItemID.PINK_ELEGANT_SKIRT, "Clue Scroll (Medium)", "Elegeant", 14),
GOLD_ELEGANT_SHIRT("Gold elegant shirt", ItemID.GOLD_ELEGANT_SHIRT, "Clue Scroll (Medium)", "Elegeant", 15),
GOLD_ELEGANT_LEGS("Gold elegant legs", ItemID.GOLD_ELEGANT_LEGS, "Clue Scroll (Medium)", "Elegeant", 15),
GOLD_ELEGANT_BLOUSE("Gold elegant blouse", ItemID.GOLD_ELEGANT_BLOUSE, "Clue Scroll (Medium)", "Elegeant", 15),
GOLD_ELEGANT_SKIRT("Gold elegant skirt", ItemID.GOLD_ELEGANT_SKIRT, "Clue Scroll (Medium)", "Elegeant", 15),
GUTHIX_MITRE("Guthix mitre", ItemID.GUTHIX_MITRE, "Clue Scroll (Medium)", "Vestment", 16),
GUTHIX_CLOAK("Guthix cloak", ItemID.GUTHIX_CLOAK, "Clue Scroll (Medium)", "Vestment", 16),
SARADOMIN_MITRE("Saradomin mitre", ItemID.SARADOMIN_MITRE, "Clue Scroll (Medium)", "Vestment", 16),
SARADOMIN_CLOAK("Saradomin cloak", ItemID.SARADOMIN_CLOAK, "Clue Scroll (Medium)", "Vestment", 16),
ZAMORAK_MITRE("Zamorak mitre", ItemID.ZAMORAK_MITRE, "Clue Scroll (Medium)", "Vestment", 16),
ZAMORAK_CLOAK("Zamorak cloak", ItemID.ZAMORAK_CLOAK, "Clue Scroll (Medium)", "Vestment", 16),
ANCIENT_MITRE("Anicent mitre", ItemID.ANCIENT_MITRE, "Clue Scroll (Medium)", "Vestment", 17),
ANCIENT_CLOAK("Anicent cloak", ItemID.ANCIENT_CLOAK, "Clue Scroll (Medium)", "Vestment", 17),
BANDOS_MITRE("Bandos mitre", ItemID.BANDOS_MITRE, "Clue Scroll (Medium)", "Vestment", 17),
BANDOS_CLOAK("Bandos cloak", ItemID.BANDOS_CLOAK, "Clue Scroll (Medium)", "Vestment", 17),
ARMADYL_MITRE("Armadyl mitre", ItemID.ARMADYL_MITRE, "Clue Scroll (Medium)", "Vestment", 17),
ARMADYL_CLOAK("Armadyl cloak", ItemID.ARMADYL_CLOAK, "Clue Scroll (Medium)", "Vestment", 17),
ARMADYL_STOLE("Armadyl stole", ItemID.ARMADYL_STOLE, "Clue Scroll (Medium)", "Vestment", 18),
ARMADYL_CROZIER("Armadyl crozier", ItemID.ARMADYL_CROZIER, "Clue Scroll (Medium)", "Vestment", 18),
ANCIENT_STOLE("Anicent stole", ItemID.ANCIENT_STOLE, "Clue Scroll (Medium)", "Vestment", 18),
ANCIENT_CROZIER("Anicent crozier", ItemID.ANCIENT_CROZIER, "Clue Scroll (Medium)", "Vestment", 18),
BANDOS_STOLE("Bandos stole", ItemID.BANDOS_STOLE, "Clue Scroll (Medium)", "Vestment", 18),
BANDOS_CROZIER("Bandos crozier", ItemID.BANDOS_CROZIER, "Clue Scroll (Medium)", "Vestment", 18),
CAT_MASK("Cat mask", ItemID.CAT_MASK, "Clue Scroll (Medium)", "Masks", 19),
PENGUIN_MASK("Penguin mask", ItemID.PENGUIN_MASK, "Clue Scroll (Medium)", "Masks", 19),
BLACK_UNICORN_MASK("Black unicorn mask", ItemID.BLACK_UNICORN_MASK, "Clue Scroll (Medium)", "Masks", 19),
WHITE_UNICORN_MASK("White unicorn mask", ItemID.WHITE_UNICORN_MASK, "Clue Scroll (Medium)", "Masks", 19),
LEPRECHAUN_HAT("Leprechaun hat", ItemID.LEPRECHAUN_HAT, "Clue Scroll (Medium)", "Masks", 19),
BLACK_LEPRECHAUN_HAT("Black leprechaun hat", ItemID.BLACK_LEPRECHAUN_HAT, "Clue Scroll (Medium)", "Masks", 19),
CRIER_HAT("Crier hat", ItemID.CRIER_HAT, "Clue Scroll (Medium)", "Crier", 20),
CRIER_BELL("Crier bell", ItemID.CRIER_BELL, "Clue Scroll (Medium)", "Crier", 20),
CRIER_COAT("Crier coat", ItemID.CRIER_COAT, "Clue Scroll (Medium)", "Crier", 20),
ARCEUUS_BANNER("Arceeus banner", ItemID.ARCEUUS_BANNER, "Clue Scroll (Medium)", "Zeah Banners", 21),
HOSIDIUS_BANNER("Hosidius banner", ItemID.HOSIDIUS_BANNER, "Clue Scroll (Medium)", "Zeah Banners", 21),
LOVAKENGJ_BANNER("Lovakengj banner", ItemID.LOVAKENGJ_BANNER, "Clue Scroll (Medium)", "Zeah Banners", 21),
PISCARILIUS_BANNER("Picarilius banner", ItemID.PISCARILIUS_BANNER, "Clue Scroll (Medium)", "Zeah Banners", 21),
SHAYZIEN_BANNER("Shayzien banner", ItemID.SHAYZIEN_BANNER, "Clue Scroll (Medium)", "Zeah Banners", 21),
STRENGTH_AMULET_T("Strength amulet (t)", ItemID.STRENGTH_AMULET_T, "Clue Scroll (Medium)", "Other", 22),
ADAMANT_CANE("Adamant cane", ItemID.ADAMANT_CANE, "Clue Scroll (Medium)", "Other", 22),
GNOMISH_FIRELIGHTER("Gnomish firelighter", ItemID.GNOMISH_FIRELIGHTER, "Clue Scroll (Medium)", "Other", 22),
CABBAGE_ROUND_SHIELD("Cabbage round shield", ItemID.CABBAGE_ROUND_SHIELD, "Clue Scroll (Medium)", "Other", 22),
CLUELESS_SCROLL("Clueless scroll", ItemID.CLUELESS_SCROLL, "Clue Scroll (Medium)", "Other", 22),
SPIKED_MANACLES("Spiked manacles", ItemID.SPIKED_MANACLES, "Clue Scroll (Medium)", "Other", 23),
WOLF_MASK("Wolf mask", ItemID.WOLF_MASK, "Clue Scroll (Medium)", "Other", 23),
WOLF_CLOAK("Wolf cloak", ItemID.WOLF_CLOAK, "Clue Scroll (Medium)", "Other", 23),
CLIMBING_BOOTS_G("Climbing boots (g)", ItemID.CLIMBING_BOOTS_G, "Clue Scroll (Medium)", "Other", 23),
// Hard Clue Scrolls
RUNE_FULL_HELM_T("Rune full helm (t)", ItemID.RUNE_FULL_HELM_T, "Clue Scroll (Hard)", "Armor", -1),
RUNE_PLATEBODY_T("Rune platebody (t)", ItemID.RUNE_PLATEBODY_T, "Clue Scroll (Hard)", "Armor", -1),
RUNE_PLATELEGS_T("Rune platelegs (t)", ItemID.RUNE_PLATELEGS_T, "Clue Scroll (Hard)", "Armor", -1),
RUNE_PLATESKIRT_T("Rune plateskirt (t)", ItemID.RUNE_PLATESKIRT_T, "Clue Scroll (Hard)", "Armor", -1),
RUNE_KITESHIELD_T("Rune kiteshield (t)", ItemID.RUNE_KITESHIELD_T, "Clue Scroll (Hard)", "Armor", -1),
RUNE_FULL_HELM_G("Rune full helm (g)", ItemID.RUNE_FULL_HELM_G, "Clue Scroll (Hard)", "Armor", 0),
RUNE_PLATEBODY_G("Rune platebody (g)", ItemID.RUNE_PLATEBODY_G, "Clue Scroll (Hard)", "Armor", 0),
RUNE_PLATELEGS_G("Rune platelegs (g)", ItemID.RUNE_PLATELEGS_G, "Clue Scroll (Hard)", "Armor", 0),
RUNE_PLATESKIRT_G("Rune plateskirt (g)", ItemID.RUNE_PLATESKIRT_G, "Clue Scroll (Hard)", "Armor", 0),
RUNE_KITESHIELD_G("Rune kiteshield (g)", ItemID.RUNE_KITESHIELD_G, "Clue Scroll (Hard)", "Armor", 0),
GUTHIX_FULL_HELM("Guthix full helm", ItemID.GUTHIX_FULL_HELM, "Clue Scroll (Hard)", "Armor", 1),
GUTHIX_PLATEBODY("Guthix platebody", ItemID.GUTHIX_PLATEBODY, "Clue Scroll (Hard)", "Armor", 1),
GUTHIX_PLATELEGS("Guthix platelegs", ItemID.GUTHIX_PLATELEGS, "Clue Scroll (Hard)", "Armor", 1),
GUTHIX_PLATESKIRT("Guthix plateskirt", ItemID.GUTHIX_PLATESKIRT, "Clue Scroll (Hard)", "Armor", 1),
GUTHIX_KITESHIELD("Guthix kiteshield", ItemID.GUTHIX_KITESHIELD, "Clue Scroll (Hard)", "Armor", 1),
SARADOMIN_FULL_HELM("Saradomin full helm", ItemID.SARADOMIN_FULL_HELM, "Clue Scroll (Hard)", "Armor", 2),
SARADOMIN_PLATEBODY("Saradomin platebody", ItemID.SARADOMIN_PLATEBODY, "Clue Scroll (Hard)", "Armor", 2),
SARADOMIN_PLATELEGS("Saradomin platelegs", ItemID.SARADOMIN_PLATELEGS, "Clue Scroll (Hard)", "Armor", 2),
SARADOMIN_PLATESKIRT("Saradomin plateskirt", ItemID.SARADOMIN_PLATESKIRT, "Clue Scroll (Hard)", "Armor", 2),
SARADOMIN_KITESHIELD("Saradomin kiteshield", ItemID.SARADOMIN_KITESHIELD, "Clue Scroll (Hard)", "Armor", 2),
ZAMORAK_FULL_HELM("Zamorak full helm", ItemID.ZAMORAK_FULL_HELM, "Clue Scroll (Hard)", "Armor", 3),
ZAMORAK_PLATEBODY("Zamorak platebody", ItemID.ZAMORAK_PLATEBODY, "Clue Scroll (Hard)", "Armor", 3),
ZAMORAK_PLATELEGS("Zamorak platelegs", ItemID.ZAMORAK_PLATELEGS, "Clue Scroll (Hard)", "Armor", 3),
ZAMORAK_PLATESKIRT("Zamorak plateskirt", ItemID.ZAMORAK_PLATESKIRT, "Clue Scroll (Hard)", "Armor", 3),
ZAMORAK_KITESHIELD("Zamorak kiteshield", ItemID.ZAMORAK_KITESHIELD, "Clue Scroll (Hard)", "Armor", 3),
ANCIENT_FULL_HELM("Ancient full helm", ItemID.ANCIENT_FULL_HELM, "Clue Scroll (Hard)", "Armor", 4),
ANCIENT_PLATEBODY("Ancient platebody", ItemID.ANCIENT_PLATEBODY, "Clue Scroll (Hard)", "Armor", 4),
ANCIENT_PLATELEGS("Ancient platelegs", ItemID.ANCIENT_PLATELEGS, "Clue Scroll (Hard)", "Armor", 4),
ANCIENT_PLATESKIRT("Ancient plateskirt", ItemID.ANCIENT_PLATESKIRT, "Clue Scroll (Hard)", "Armor", 4),
ANCIENT_KITESHIELD("Ancient kiteshield", ItemID.ANCIENT_KITESHIELD, "Clue Scroll (Hard)", "Armor", 4),
BANDOS_FULL_HELM("Bandos full helm", ItemID.BANDOS_FULL_HELM, "Clue Scroll (Hard)", "Armor", 5),
BANDOS_PLATEBODY("Bandos platebody", ItemID.BANDOS_PLATEBODY, "Clue Scroll (Hard)", "Armor", 5),
BANDOS_PLATELEGS("Bandos platelegs", ItemID.BANDOS_PLATELEGS, "Clue Scroll (Hard)", "Armor", 5),
BANDOS_PLATESKIRT("Bandos plateskirt", ItemID.BANDOS_PLATESKIRT, "Clue Scroll (Hard)", "Armor", 5),
BANDOS_KITESHIELD("Bandos kiteshield", ItemID.BANDOS_KITESHIELD, "Clue Scroll (Hard)", "Armor", 5),
ARMADYL_FULL_HELM("Armadyl full helm", ItemID.ARMADYL_FULL_HELM, "Clue Scroll (Hard)", "Armor", 6),
ARMADYL_PLATEBODY("Armadyl platebody", ItemID.ARMADYL_PLATEBODY, "Clue Scroll (Hard)", "Armor", 6),
ARMADYL_PLATELEGS("Armadyl platelegs", ItemID.ARMADYL_PLATELEGS, "Clue Scroll (Hard)", "Armor", 6),
ARMADYL_PLATESKIRT("Armadyl plateskirt", ItemID.ARMADYL_PLATESKIRT, "Clue Scroll (Hard)", "Armor", 6),
ARMADYL_KITESHIELD("Armadyl kiteshield", ItemID.ARMADYL_KITESHIELD, "Clue Scroll (Hard)", "Armor", 6),
GILDED_FULL_HELM("Gilded full helm", ItemID.GILDED_FULL_HELM, "Clue Scroll (Hard)", "Gilded", 7),
GILDED_PLATEBODY("Gilded platebody", ItemID.GILDED_PLATEBODY, "Clue Scroll (Hard)", "Gilded", 7),
GILDED_PLATELEGS("Gilded platelegs", ItemID.GILDED_PLATELEGS, "Clue Scroll (Hard)", "Gilded", 7),
GILDED_PLATESKIRT("Gilded plateskirt", ItemID.GILDED_PLATESKIRT, "Clue Scroll (Hard)", "Gilded", 7),
GILDED_KITESHIELD("Gilded kiteshield", ItemID.GILDED_KITESHIELD, "Clue Scroll (Hard)", "Gilded", 7),
GILDED_MED_HELM("Gilded med helm", ItemID.GILDED_MED_HELM, "Clue Scroll (Hard)", "Gilded", 8),
GILDED_CHAINBODY("Gilded chainbody", ItemID.GILDED_CHAINBODY, "Clue Scroll (Hard)", "Gilded", 8),
GILDED_SQ_SHIELD("Gilded sq shield", ItemID.GILDED_SQ_SHIELD, "Clue Scroll (Hard)", "Gilded", 8),
GILDED_2H_SWORD("Gilded 2H sword", ItemID.GILDED_2H_SWORD, "Clue Scroll (Hard)", "Gilded", 8),
GILDED_SPEAR("Gilded spear", ItemID.GILDED_SPEAR, "Clue Scroll (Hard)", "Gilded", 8),
GILDED_HASTA("Gilded hasta", ItemID.GILDED_HASTA, "Clue Scroll (Hard)", "Gilded", 8),
RUNE_HELM_H1("Rune helm (h1)", ItemID.RUNE_HELM_H1, "Clue Scroll (Hard)", "Heraldic", 9),
RUNE_HELM_H2("Rune helm (h2)", ItemID.RUNE_HELM_H2, "Clue Scroll (Hard)", "Heraldic", 9),
RUNE_HELM_H3("Rune helm (h3)", ItemID.RUNE_HELM_H3, "Clue Scroll (Hard)", "Heraldic", 9),
RUNE_HELM_H4("Rune helm (h4)", ItemID.RUNE_HELM_H4, "Clue Scroll (Hard)", "Heraldic", 9),
RUNE_HELM_H5("Rune helm (h5)", ItemID.RUNE_HELM_H5, "Clue Scroll (Hard)", "Heraldic", 9),
RUNE_SHIELD_H1("Rune shield (h1)", ItemID.RUNE_SHIELD_H1, "Clue Scroll (Hard)", "Heraldic", 10),
RUNE_SHIELD_H2("Rune shield (h2)", ItemID.RUNE_SHIELD_H2, "Clue Scroll (Hard)", "Heraldic", 10),
RUNE_SHIELD_H3("Rune shield (h3)", ItemID.RUNE_SHIELD_H3, "Clue Scroll (Hard)", "Heraldic", 10),
RUNE_SHIELD_H4("Rune shield (h4)", ItemID.RUNE_SHIELD_H4, "Clue Scroll (Hard)", "Heraldic", 10),
RUNE_SHIELD_H5("Rune shield (h5)", ItemID.RUNE_SHIELD_H5, "Clue Scroll (Hard)", "Heraldic", 10),
RUNE_PLATEBODY_H1("Rune platebody (h1)", ItemID.RUNE_PLATEBODY_H1, "Clue Scroll (Hard)", "Heraldic", 11),
RUNE_PLATEBODY_H2("Rune platebody (h2)", ItemID.RUNE_PLATEBODY_H2, "Clue Scroll (Hard)", "Heraldic", 11),
RUNE_PLATEBODY_H3("Rune platebody (h3)", ItemID.RUNE_PLATEBODY_H3, "Clue Scroll (Hard)", "Heraldic", 11),
RUNE_PLATEBODY_H4("Rune platebody (h4)", ItemID.RUNE_PLATEBODY_H4, "Clue Scroll (Hard)", "Heraldic", 11),
RUNE_PLATEBODY_H5 ("Rune platebody (h5)", ItemID.RUNE_PLATEBODY_H5, "Clue Scroll (Hard)", "Heraldic", 11),
BLUE_DHIDE_BODY_T("Blue dhide body (t)", ItemID.BLUE_DHIDE_BODY_T, "Clue Scroll (Hard)", "Dhide", 12),
BLUE_DHIDE_CHAPS_T("Blue dhide chaps (t)", ItemID.BLUE_DHIDE_CHAPS_T, "Clue Scroll (Hard)", "Dhide", 12),
BLUE_DHIDE_BODY_G("Blue dhide body (g)", ItemID.BLUE_DHIDE_BODY_G, "Clue Scroll (Hard)", "Dhide", 12),
BLUE_DHIDE_CHAPS_G("Blue dhide chaps (g)", ItemID.BLUE_DHIDE_CHAPS_G, "Clue Scroll (Hard)", "Dhide", 12),
RED_DHIDE_BODY_T("Red dhide body (t)", ItemID.RED_DHIDE_BODY_T, "Clue Scroll (Hard)", "Dhide", 13),
RED_DHIDE_CHAPS_T("Red dhide chaps (t)", ItemID.RED_DHIDE_CHAPS_T, "Clue Scroll (Hard)", "Dhide", 13),
RED_DHIDE_BODY_G("Red dhide body (g)", ItemID.RED_DHIDE_BODY_G, "Clue Scroll (Hard)", "Dhide", 13),
RED_DHIDE_CHAPS_G("Red dhide chaps (g)", ItemID.RED_DHIDE_CHAPS_G, "Clue Scroll (Hard)", "Dhide", 13),
ENCHANTED_HAT("Enchanted hat", ItemID.ENCHANTED_HAT, "Clue Scroll (Hard)", "Enchanted", 14),
ENCHANTED_TOP("Enchanted top", ItemID.ENCHANTED_TOP, "Clue Scroll (Hard)", "Enchanted", 14),
ENCHANTED_ROBE("Enchanted robe", ItemID.ENCHANTED_ROBE, "Clue Scroll (Hard)", "Enchanted", 14),
AMULET_OF_GLORY_T("Amulet of glory (t)", ItemID.AMULET_OF_GLORY_T, "Clue Scroll (Hard)", "Enchanted", 14),
ROBIN_HOOD_HAT("Robin hood hat", ItemID.ROBIN_HOOD_HAT, "Clue Scroll (Hard)", "Hats", 15),
PIRATE_HAT("Pirate hat", ItemID.PIRATE_HAT, "Clue Scroll (Hard)", "Hats", 15),
PITH_HELMET("Pith helmet", ItemID.PITH_HELMET, "Clue Scroll (Hard)", "Hats", 15),
EXPLORER_BACKPACK("Explorer backpack", ItemID.EXPLORER_BACKPACK, "Clue Scroll (Hard)", "Hats", 15),
RED_CAVALIER("Red cavalier", ItemID.RED_CAVALIER, "Clue Scroll (Hard)", "Cavalier", 16),
TAN_CAVALIER("Tan cavalier", ItemID.TAN_CAVALIER, "Clue Scroll (Hard)", "Cavalier", 16),
DARK_CAVALIER("Dark cavalier", ItemID.DARK_CAVALIER, "Clue Scroll (Hard)", "Cavalier", 16),
BLACK_CAVALIER("Black cavalier", ItemID.BLACK_CAVALIER, "Clue Scroll (Hard)", "Cavalier", 16),
NAVY_CAVALIER("Navy cavalier", ItemID.NAVY_CAVALIER, "Clue Scroll (Hard)", "Cavalier", 16),
WHITE_CAVALIER("White cavalier", ItemID.WHITE_CAVALIER, "Clue Scroll (Hard)", "Cavalier", 16),
_3RD_AGE_FULL_HELMET("3rd age full helm", ItemID._3RD_AGE_FULL_HELMET, "Clue Scroll (Hard)", "Third age", 17),
_3RD_AGE_PLATEBODY("3rd age platebody", ItemID._3RD_AGE_PLATEBODY, "Clue Scroll (Hard)", "Third age", 17),
_3RD_AGE_PLATELEGS("3rd age platelegs", ItemID._3RD_AGE_PLATELEGS, "Clue Scroll (Hard)", "Third age", 17),
_3RD_AGE_PLATESKIRT("3rd age plateskirt", ItemID._3RD_AGE_PLATESKIRT, "Clue Scroll (Hard)", "Third age", 17),
_3RD_AGE_KITESHIELD("3rd age kiteshield", ItemID._3RD_AGE_KITESHIELD, "Clue Scroll (Hard)", "Third age", 17),
_3RD_AGE_RANGE_COIF("3rd age range coif", ItemID._3RD_AGE_RANGE_COIF, "Clue Scroll (Hard)", "Third age", 18),
_3RD_AGE_RANGE_TOP("3rd age range top", ItemID._3RD_AGE_RANGE_TOP, "Clue Scroll (Hard)", "Third age", 18),
_3RD_AGE_RANGE_LEGS("3rd age range legs", ItemID._3RD_AGE_RANGE_LEGS, "Clue Scroll (Hard)", "Third age", 18),
_3RD_AGE_VAMBRACES("3rd age vambraces", ItemID._3RD_AGE_VAMBRACES, "Clue Scroll (Hard)", "Third age", 18),
_3RD_AGE_MAGE_HAT("3rd age mage hat", ItemID._3RD_AGE_MAGE_HAT, "Clue Scroll (Hard)", "Third age", 19),
_3RD_AGE_ROBE_TOP("3rd age robe top", ItemID._3RD_AGE_ROBE_TOP, "Clue Scroll (Hard)", "Third age", 19),
_3RD_AGE_ROBE("3rd age robe", ItemID._3RD_AGE_ROBE, "Clue Scroll (Hard)", "Third age", 19),
_3RD_AGE_AMULET("3rd age amulet", ItemID._3RD_AGE_AMULET, "Clue Scroll (Hard)", "Third age", 19),
GUTHIX_COIF("Guthix coif", ItemID.GUTHIX_COIF, "Clue Scroll (Hard)", "God Dhide", 20),
GUTHIX_DHIDE("Guthix dragonhide", ItemID.GUTHIX_DRAGONHIDE, "Clue Scroll (Hard)", "God Dhide", 20),
GUTHIX_CHAPS("Guthix chaps", ItemID.GUTHIX_CHAPS, "Clue Scroll (Hard)", "God Dhide", 20),
GUTHIX_BRACERS("Guthix bracers", ItemID.GUTHIX_BRACERS, "Clue Scroll (Hard)", "God Dhide", 20),
GUTHIX_DHIDE_BOOTS("Guthix dhide boots", ItemID.GUTHIX_DHIDE_BOOTS, "Clue Scroll (Hard)", "God Dhide", 20),
GUTHIX_DHIDE_SHIELD("Guthix dhide shield", ItemID.GUTHIX_DHIDE_SHIELD, "Clue Scroll (Hard)", "God Dhide", 20),
SARADOMIN_COIF("Saradomin coif", ItemID.SARADOMIN_COIF, "Clue Scroll (Hard)", "God Dhide", 21),
SARADOMIN_DHIDE("Saradomin dragonhide", ItemID.SARADOMIN_DHIDE, "Clue Scroll (Hard)", "God Dhide", 21),
SARADOMIN_CHAPS("Saradomin chaps", ItemID.SARADOMIN_CHAPS, "Clue Scroll (Hard)", "God Dhide", 21),
SARADOMIN_BRACERS("Saradomin bracers", ItemID.SARADOMIN_BRACERS, "Clue Scroll (Hard)", "God Dhide", 21),
SARADOMIN_DHIDE_BOOTS("Saradomin dhide boots", ItemID.SARADOMIN_DHIDE_BOOTS, "Clue Scroll (Hard)", "God Dhide", 21),
SARADOMIN_DHIDE_SHIELD("Saradomin dhide shield", ItemID.SARADOMIN_DHIDE_SHIELD, "Clue Scroll (Hard)", "God Dhide", 21),
ZAMORAK_COIF("Zamorak coif", ItemID.ZAMORAK_COIF, "Clue Scroll (Hard)", "God Dhide", 22),
ZAMORAK_DHIDE("Zamorak dragonhide", ItemID.ZAMORAK_DHIDE, "Clue Scroll (Hard)", "God Dhide", 22),
ZAMORAK_CHAPS("Zamorak chaps", ItemID.ZAMORAK_CHAPS, "Clue Scroll (Hard)", "God Dhide", 22),
ZAMORAK_BRACERS("Zamorak bracers", ItemID.ZAMORAK_BRACERS, "Clue Scroll (Hard)", "God Dhide", 22),
ZAMORAK_DHIDE_BOOTS("Zamorak dhide boots", ItemID.ZAMORAK_DHIDE_BOOTS, "Clue Scroll (Hard)", "God Dhide", 22),
ZAMORAK_DHIDE_SHIELD("Zamorak dhide shield", ItemID.ZAMORAK_DHIDE_SHIELD, "Clue Scroll (Hard)", "God Dhide", 22),
ANCIENT_COIF("Ancient coif", ItemID.ANCIENT_COIF, "Clue Scroll (Hard)", "God Dhide", 23),
ANCIENT_DHIDE("Ancient dragonhide", ItemID.ANCIENT_DHIDE, "Clue Scroll (Hard)", "God Dhide", 23),
ANCIENT_CHAPS("Ancient chaps", ItemID.ANCIENT_CHAPS, "Clue Scroll (Hard)", "God Dhide", 23),
ANCIENT_BRACERS("Ancient bracers", ItemID.ANCIENT_BRACERS, "Clue Scroll (Hard)", "God Dhide", 23),
ANCIENT_DHIDE_BOOTS("Ancient dhide boots", ItemID.ANCIENT_DHIDE_BOOTS, "Clue Scroll (Hard)", "God Dhide", 23),
ANCIENT_DHIDE_SHIELD("Ancient dhide shield", ItemID.ANCIENT_DHIDE_SHIELD, "Clue Scroll (Hard)", "God Dhide", 23),
BANDOS_COIF("Bandos coif", ItemID.BANDOS_COIF, "Clue Scroll (Hard)", "God Dhide", 24),
BANDOS_DHIDE("Bandos dragonhide", ItemID.BANDOS_DHIDE, "Clue Scroll (Hard)", "God Dhide", 24),
BANDOS_CHAPS("Bandos chaps", ItemID.BANDOS_CHAPS, "Clue Scroll (Hard)", "God Dhide", 24),
BANDOS_BRACERS("Bandos bracers", ItemID.BANDOS_BRACERS, "Clue Scroll (Hard)", "God Dhide", 24),
BANDOS_DHIDE_BOOTS("Bandos dhide boots", ItemID.BANDOS_DHIDE_BOOTS, "Clue Scroll (Hard)", "God Dhide", 24),
BANDOS_DHIDE_SHIELD("Bandos dhide shield", ItemID.BANDOS_DHIDE_SHIELD, "Clue Scroll (Hard)", "God Dhide", 24),
ARMADYL_COIF("Armadyl coif", ItemID.ARMADYL_COIF, "Clue Scroll (Hard)", "God Dhide", 25),
ARMADYL_DHIDE("Armadyl dragonhide", ItemID.ARMADYL_DHIDE, "Clue Scroll (Hard)", "God Dhide", 25),
ARMADYL_CHAPS("Armadyl chaps", ItemID.ARMADYL_CHAPS, "Clue Scroll (Hard)", "God Dhide", 25),
ARMADYL_BRACERS("Armadyl bracers", ItemID.ARMADYL_BRACERS, "Clue Scroll (Hard)", "God Dhide", 25),
ARMADYL_DHIDE_BOOTS("Armadyl dhide boots", ItemID.ARMADYL_DHIDE_BOOTS, "Clue Scroll (Hard)", "God Dhide", 25),
ARMADYL_DHIDE_SHIELD("Armadyl dhide shield", ItemID.ARMADYL_DHIDE_SHIELD, "Clue Scroll (Hard)", "God Dhide", 25),
GUTHIX_STOLE("Guthix stole", ItemID.GUTHIX_STOLE, "Clue Scroll (Hard)", "Vestment", 26),
GUTHIX_CROZIER("Guthix crozier", ItemID.GUTHIX_CROZIER, "Clue Scroll (Hard)", "Vestment", 26),
SARADOMIN_STOLE("Saradomin stole", ItemID.SARADOMIN_STOLE, "Clue Scroll (Hard)", "Vestment", 26),
SARADOMIN_CROZIER("Saradomin crozier", ItemID.SARADOMIN_CROZIER, "Clue Scroll (Hard)", "Vestment", 26),
ZAMORAK_STOLE("Zamorak stole", ItemID.ZAMORAK_STOLE, "Clue Scroll (Hard)", "Vestment", 26),
ZAMORAK_CROZIER("Zamorak crozier", ItemID.ZAMORAK_CROZIER, "Clue Scroll (Hard)", "Vestment", 26),
GREEN_DRAGON_MASK("Green dragon mask", ItemID.GREEN_DRAGON_MASK, "Clue Scroll (Hard)", "Masks", 27),
RED_DRAGON_MASK("Red dragon mask", ItemID.RED_DRAGON_MASK, "Clue Scroll (Hard)", "Masks", 27),
BLUE_DRAGON_MASK("Blue dragon mask", ItemID.BLUE_DRAGON_MASK, "Clue Scroll (Hard)", "Masks", 27),
BLACK_DRAGON_MASK("Black dragon mask", ItemID.BLACK_DRAGON_MASK, "Clue Scroll (Hard)", "Masks", 27),
RUNE_CANE("Rune cane", ItemID.RUNE_CANE, "Clue Scroll (Hard)", "Other", 28),
ZOMBIE_HEAD("Zombie head", ItemID.ZOMBIE_HEAD, "Clue Scroll (Hard)", "Other", 28),
CYCLOPS_HEAD("Cyclops head", ItemID.CYCLOPS_HEAD, "Clue Scroll (Hard)", "Other", 28),
NUNCHAKU("Nunchaku", ItemID.NUNCHAKU, "Clue Scroll (Hard)", "Other", 29),
DUAL_SAI("Dual sai", ItemID.DUAL_SAI, "Clue Scroll (Hard)", "Other", 29),
THIEVING_BAG("Thieving bag", ItemID.THIEVING_BAG, "Clue Scroll (Hard)", "Other", 29),
DRAGON_BOOTS_ORNAMENT_KIT("Dragon boots ornament kit", ItemID.DRAGON_BOOTS_ORNAMENT_KIT, "Clue Scroll (Hard)", "Ornament Kit", 30),
RUNE_DEFENDER_ORNAMENT_KIT("Rune defender ornament kit", ItemID.RUNE_DEFENDER_ORNAMENT_KIT, "Clue Scroll (Hard)", "Ornament Kit", 30),
TZHAARKETOM_ORNAMENT_KIT("Tzhaar-ket-om ornament kit", ItemID.TZHAARKETOM_ORNAMENT_KIT, "Clue Scroll (Hard)", "Ornament Kit", 30),
BERSERKER_NECKLACE_ORNAMENT_KIT("Berserker necklace ornament kit", ItemID.BERSERKER_NECKLACE_ORNAMENT_KIT, "Clue Scroll (Hard)", "Ornament Kit", 30),
// Elite Clue Scrolls
DRAGON_FULL_HELM_ORNAMENT_KIT("Dragon full helm ornament kit", ItemID.DRAGON_FULL_HELM_ORNAMENT_KIT, "Clue Scroll (Elite)", "Ornament Kits", -1),
DRAGON_CHAINBODY_ORNAMENT_KIT("Dragon chainbody ornament kit", ItemID.DRAGON_CHAINBODY_ORNAMENT_KIT, "Clue Scroll (Elite)", "Ornament Kits", -1),
DRAGON_LEGSSKIRT_ORNAMENT_KIT("Dragon legs/skirt ornament kit", ItemID.DRAGON_LEGSSKIRT_ORNAMENT_KIT, "Clue Scroll (Elite)", "Ornament Kits", -1),
DRAGON_SQ_SHIELD_ORNAMENT_KIT("Dragon sq shield ornament kit", ItemID.DRAGON_SQ_SHIELD_ORNAMENT_KIT, "Clue Scroll (Elite)", "Ornament Kits", -1),
DRAGON_SCIMITAR_ORNAMENT_KIT("Dragon scimitar ornament kit", ItemID.DRAGON_SCIMITAR_ORNAMENT_KIT, "Clue Scroll (Elite)", "Ornament Kits", -1),
LIGHT_INFINITY_COLOUR_KIT("Light infinity colour kit", ItemID.LIGHT_INFINITY_COLOUR_KIT, "Clue Scroll (Elite)", "Colour Kits", 0),
DARK_INFINITY_COLOUR_KIT("Dark infinity colour kit", ItemID.DARK_INFINITY_COLOUR_KIT, "Clue Scroll (Elite)", "Colour Kits", 0),
FURY_ORNAMENT_KIT("Fury ornament kit", ItemID.FURY_ORNAMENT_KIT, "Clue Scroll (Elite)", "Ornament Kits", 0),
MUSKETEER_HAT("Musketeer hat", ItemID.MUSKETEER_HAT, "Clue Scroll (Elite)", "Musketeer", 1),
MUSKETEER_TABARD("Musketeer tabard", ItemID.MUSKETEER_TABARD, "Clue Scroll (Elite)", "Musketeer", 1),
MUSKETEER_PANTS("Musketeer pants", ItemID.MUSKETEER_PANTS, "Clue Scroll (Elite)", "Musketeer", 1),
DRAGON_CANE("Dragon cane", ItemID.DRAGON_CANE, "Clue Scroll (Elite)", "Musketeer", 1),
TOP_HAT("Top hat", ItemID.TOP_HAT, "Clue Scroll (Elite)", "Fancy", 2),
MONOCLE("Monocle", ItemID.MONOCLE, "Clue Scroll (Elite)", "Fancy", 2),
BRIEFCASE("Briefcase", ItemID.BRIEFCASE, "Clue Scroll (Elite)", "Fancy", 2),
SAGACIOUS_SPECTACLES("Sagacious spectacles", ItemID.SAGACIOUS_SPECTACLES, "Clue Scroll (Elite)", "Fancy", 2),
BIG_PIRATE_HAT("Big pirate hat", ItemID.BIG_PIRATE_HAT, "Clue Scroll (Elite)", "Hats", 3),
DEERSTALKER("Deerstalker", ItemID.DEERSTALKER, "Clue Scroll (Elite)", "Hats", 3),
BLACKSMITHS_HELM("Blacksmith's helm", ItemID.BLACKSMITHS_HELM, "Clue Scroll (Elite)", "Hats", 3),
BUCKET_HELM("Bucket helm", ItemID.BUCKET_HELM, "Clue Scroll (Elite)", "Hats", 3),
AFRO("Afro", ItemID.AFRO, "Clue Scroll (Elite)", "Hats", 3),
GILDED_BOOTS("Gilded boots", ItemID.GILDED_BOOTS, "Clue Scroll (Elite)", "Rare", 4),
GILDED_SCIMITAR("Gilded scimitar", ItemID.GILDED_SCIMITAR, "Clue Scroll (Elite)", "Rare", 4),
RANGERS_TUNIC("Rangers' tunic", ItemID.RANGERS_TUNIC, "Clue Scroll (Elite)", "Rare", 4),
RANGERS_TIGHTS("Rangers' tights", ItemID.RANGERS_TIGHTS, "Clue Scroll (Elite)", "Rare", 4),
RANGER_GLOVES("Ranger gloves", ItemID.RANGER_GLOVES, "Clue Scroll (Elite)", "Rare", 4),
BRONZE_DRAGON_MASK("Bronze dragon mask", ItemID.BRONZE_DRAGON_MASK, "Clue Scroll (Elite)", "Masks", 5),
IRON_DRAGON_MASK("Iron dragon mask", ItemID.IRON_DRAGON_MASK, "Clue Scroll (Elite)", "Masks", 5),
STEEL_DRAGON_MASK("Steel dragon mask", ItemID.STEEL_DRAGON_MASK, "Clue Scroll (Elite)", "Masks", 5),
MITHRIL_DRAGON_MASK("Mithril dragon mask", ItemID.MITHRIL_DRAGON_MASK, "Clue Scroll (Elite)", "Masks", 5),
ADAMANT_DRAGON_MASK("Adamant dragon mask", ItemID.ADAMANT_DRAGON_MASK, "Clue Scroll (Elite)", "Masks", 6),
RUNE_DRAGON_MASK("Rune dragon mask", ItemID.RUNE_DRAGON_MASK, "Clue Scroll (Elite)", "Masks", 6),
LAVA_DRAGON_MASK("Lava dragon mask", ItemID.LAVA_DRAGON_MASK, "Clue Scroll (Elite)", "Masks", 6),
BLACK_DHIDE_BODY_T("Black dhide body (t)", ItemID.BLACK_DHIDE_BODY_T, "Clue Scroll (Elite)", "Dhide", 7),
BLACK_DHIDE_CHAPS_T("Black dhide chaps (t)", ItemID.BLACK_DHIDE_CHAPS_T, "Clue Scroll (Elite)", "Dhide", 7),
BLACK_DHIDE_BODY_G("Black dhide body (g)", ItemID.BLACK_DHIDE_BODY_G, "Clue Scroll (Elite)", "Dhide", 7),
BLACK_DHIDE_CHAPS_G("Black dhide chaps (g)", ItemID.BLACK_DHIDE_CHAPS_G, "Clue Scroll (Elite)", "Dhide", 7),
GILDED_COIF("Gilded coif", ItemID.GILDED_COIF, "Clue Scroll (Elite)", "Dhide", 8),
GILDED_DHIDE_BODY("Gilded dhide body", ItemID.GILDED_DHIDE_BODY, "Clue Scroll (Elite)", "Dhide", 8),
GILDED_DHIDE_CHAPS("Gilded dhide chaps", ItemID.GILDED_DHIDE_CHAPS, "Clue Scroll (Elite)", "Dhide", 8),
GILDED_DHIDE_VAMBS("Gilded dhide vambs", ItemID.GILDED_DHIDE_VAMBS, "Clue Scroll (Elite)", "Dhide", 8),
_3RD_AGE_CLOAK("3rd age cloak", ItemID._3RD_AGE_CLOAK, "Clue Scroll (Elite)", "Third Age", 9),
_3RD_AGE_WAND("3rd age wand", ItemID._3RD_AGE_WAND, "Clue Scroll (Elite)", "Third Age", 9),
_3RD_AGE_BOW("3rd age bow", ItemID._3RD_AGE_BOW, "Clue Scroll (Elite)", "Third Age", 9),
_3RD_AGE_LONGSWORD("3rd age longsword", ItemID._3RD_AGE_LONGSWORD, "Clue Scroll (Elite)", "Third Age", 9),
ROYAL_CROWN("Royal crown", ItemID.ROYAL_CROWN, "Clue Scroll (Elite)", "Royal", 10),
ROYAL_GOWN_TOP("Royal gown top", ItemID.ROYAL_GOWN_TOP, "Clue Scroll (Elite)", "Royal", 10),
ROYAL_GOWN_BOTTOM("Royal gown bottom", ItemID.ROYAL_GOWN_BOTTOM, "Clue Scroll (Elite)", "Royal", 10),
ROYAL_SCEPTRE("Royal sceptre", ItemID.ROYAL_SCEPTRE, "Clue Scroll (Elite)", "Royal", 10),
GILDED_PICKAXE("Gilded pickaxe", ItemID.GILDED_PICKAXE, "Clue Scroll (Elite)", "Gilded", 11),
GILDED_AXE("Gilded axe", ItemID.GILDED_AXE, "Clue Scroll (Elite)", "Gilded", 11),
GILDED_SPADE("Gilded spade", ItemID.GILDED_SPADE, "Clue Scroll (Elite)", "Gilded", 11),
ARCEUUS_SCARF("Arceuus house scarf", ItemID.ARCEUUS_SCARF, "Clue Scroll (Elite)", "Zeah Scarfs", 12),
HOSIDIUS_SCARF("Hosidius house scarf", ItemID.HOSIDIUS_SCARF, "Clue Scroll (Elite)", "Zeah Scarfs", 12),
LOVAKENGJ_SCARF("Lovakengj house scarf", ItemID.LOVAKENGJ_SCARF, "Clue Scroll (Elite)", "Zeah Scarfs", 12),
PISCARILIUS_SCARF("Piscarilius house scarf", ItemID.PISCARILIUS_SCARF, "Clue Scroll (Elite)", "Zeah Scarfs", 12),
SHAYZIEN_SCARF("Shayzien house scarf", ItemID.SHAYZIEN_SCARF, "Clue Scroll (Elite)", "Zeah Scarfs", 12),
DARK_BOW_TIE("Dark bow tie", ItemID.DARK_BOW_TIE, "Clue Scroll (Elite)", "Dark Tuxedo", 13),
DARK_TUXEDO_JACKET("Dark tuxedo jacket", ItemID.DARK_TUXEDO_JACKET, "Clue Scroll (Elite)", "Dark Tuxedo", 13),
DARK_TROUSERS("Dark trousers", ItemID.DARK_TROUSERS, "Clue Scroll (Elite)", "Dark Tuxedo", 13),
DARK_TUXEDO_CUFFS("Dark tuxedo cuffs", ItemID.DARK_TUXEDO_CUFFS, "Clue Scroll (Elite)", "Dark Tuxedo", 13),
DARK_TUXEDO_SHOES("Dark tuxedo shoes", ItemID.DARK_TUXEDO_SHOES, "Clue Scroll (Elite)", "Dark Tuxedo", 13),
LIGHT_BOW_TIE("Light bow tie", ItemID.LIGHT_BOW_TIE, "Clue Scroll (Elite)", "Light Tuxedo", 14),
LIGHT_TUXEDO_JACKET("Light tuxedo jacket", ItemID.LIGHT_TUXEDO_JACKET, "Clue Scroll (Elite)", "Light Tuxedo", 14),
LIGHT_TROUSERS("Light trousers", ItemID.LIGHT_TROUSERS, "Clue Scroll (Elite)", "Light Tuxedo", 14),
LIGHT_TUXEDO_CUFFS("Light tuxedo cuffs", ItemID.LIGHT_TUXEDO_CUFFS, "Clue Scroll (Elite)", "Light Tuxedo", 14),
LIGHT_TUXEDO_SHOES("Light tuxedo shoes", ItemID.LIGHT_TUXEDO_SHOES, "Clue Scroll (Elite)", "Light Tuxedo", 14),
HOLY_WRAPS("Holy wraps", ItemID.HOLY_WRAPS, "Clue Scroll (Elite)", "Other", 15),
RING_OF_NATURE("Ring of nature", ItemID.RING_OF_NATURE, "Clue Scroll (Elite)", "Other", 15),
HEAVY_CASKET("Heavy casket", ItemID.HEAVY_CASKET, "Clue Scroll (Elite)", "Other", 15),
KATANA("Katana", ItemID.KATANA, "Clue Scroll (Elite)", "Other", 16),
URIS_HAT("Uri's hat", ItemID.URIS_HAT, "Clue Scroll (Elite)", "Other", 16),
FREMENNIK_KILT("Fremennik Kilt", ItemID.FREMENNIK_KILT, "Clue Scroll (Elite)", "Other", 16),
// Master Clue Scrolls
DRAGON_PLATEBODY_ORNAMENT_KIT("Dragon platebody ornament kit", ItemID.DRAGON_PLATEBODY_ORNAMENT_KIT, "Clue Scroll (Master)", "Ornament Kits", -1),
DRAGON_KITESHIELD_ORNAMENT_KIT("Dragon kiteshield ornament kit", ItemID.DRAGON_KITESHIELD_ORNAMENT_KIT, "Clue Scroll (Master)", "Ornament Kits", -1),
DRAGON_DEFENDER_ORNAMENT_KIT("Dragon defender ornament kit", ItemID.DRAGON_DEFENDER_ORNAMENT_KIT, "Clue Scroll (Master)", "Ornament Kits", -1),
TORMENTED_ORNAMENT_KIT("Tormented bracelet ornament kit", ItemID.TORMENTED_ORNAMENT_KIT, "Clue Scroll (Master)", "Ornament Kits", -1),
ANGUISH_ORNAMENT_KIT("Anguish ornament kit", ItemID.ANGUISH_ORNAMENT_KIT, "Clue Scroll (Master)", "Ornament Kits", -1),
TORTURE_ORNAMENT_KIT("Torture ornament kit", ItemID.TORTURE_ORNAMENT_KIT, "Clue Scroll (Master)", "Ornament Kits", -1),
OCCULT_ORNAMENT_KIT("Occult ornament kit", ItemID.OCCULT_ORNAMENT_KIT, "Clue Scroll (Master)", "Ornament Kits", 0),
ARMADYL_GODSWORD_ORNAMENT_KIT("Armadyl godsword ornament kit", ItemID.ARMADYL_GODSWORD_ORNAMENT_KIT, "Clue Scroll (Master)", "Ornament Kits", 0),
BANDOS_GODSWORD_ORNAMENT_KIT("Bandos godsword ornament kit", ItemID.BANDOS_GODSWORD_ORNAMENT_KIT, "Clue Scroll (Master)", "Ornament Kits", 0),
SARADOMIN_GODSWORD_ORNAMENT_KIT("Saradomin godsword ornament kit", ItemID.SARADOMIN_GODSWORD_ORNAMENT_KIT, "Clue Scroll (Master)", "Ornament Kits", 0),
ZAMORAK_GODSWORD_ORNAMENT_KIT("Zamorak godsword ornament kit", ItemID.ZAMORAK_GODSWORD_ORNAMENT_KIT, "Clue Scroll (Master)", "Ornament Kits", 0),
_3RD_AGE_AXE("3rd age axe", ItemID._3RD_AGE_AXE, "Clue Scroll (Master)", "3rd age", 1),
_3RD_AGE_PICKAXE("3rd age pickaxe", ItemID._3RD_AGE_PICKAXE, "Clue Scroll (Master)", "3rd age", 1),
_3RD_AGE_DRUIDIC_STAFF("3rd age druidic staff", ItemID._3RD_AGE_DRUIDIC_STAFF, "Clue Scroll (Master)", "3rd age", 2),
_3RD_AGE_DRUIDIC_ROBE_TOP("3rd age druidic robe top", ItemID._3RD_AGE_DRUIDIC_ROBE_TOP, "Clue Scroll (Master)", "3rd age", 2),
_3RD_AGE_DRUIDIC_ROBE_BOTTOMS("3rd age druidic robe bottom", ItemID._3RD_AGE_DRUIDIC_ROBE_BOTTOMS, "Clue Scroll (Master)", "3rd age", 2),
_3RD_AGE_DRUIDIC_CLOAK("3rd age druidic cloak", ItemID._3RD_AGE_DRUIDIC_CLOAK, "Clue Scroll (Master)", "3rd age", 2),
LESSER_DEMON_MASK("Lesser demon mask", ItemID.LESSER_DEMON_MASK, "Clue Scroll (Master)", "Demon Masks", 3),
GREATER_DEMON_MASK("Greater demon mask", ItemID.GREATER_DEMON_MASK, "Clue Scroll (Master)", "Demon Masks", 3),
BLACK_DEMON_MASK("Black demon mask", ItemID.BLACK_DEMON_MASK, "Clue Scroll (Master)", "Demon Masks", 3),
JUNGLE_DEMON_MASK("Jungle demon mask", ItemID.JUNGLE_DEMON_MASK, "Clue Scroll (Master)", "Demon Masks", 3),
OLD_DEMON_MASK("Old demon mask", ItemID.OLD_DEMON_MASK, "Clue Scroll (Master)", "Demon Masks", 3),
ARCEUUS_HOOD("Arceuus house hood", ItemID.ARCEUUS_HOOD, "Clue Scroll (Master)", "House Hoods", 4),
HOSIDIUS_HOOD("Hosidius house hood", ItemID.HOSIDIUS_HOOD, "Clue Scroll (Master)", "House Hoods", 4),
LOVAKENGJ_HOOD("Lovakengj house hood", ItemID.LOVAKENGJ_HOOD, "Clue Scroll (Master)", "House Hoods", 4),
PISCARILIUS_HOOD("Piscarilius house hood", ItemID.PISCARILIUS_HOOD, "Clue Scroll (Master)", "House Hoods", 4),
SHAYZIEN_HOUSE_HOOD("Shayzien house hood", ItemID.SHAYZIEN_HOOD, "Clue Scroll (Master)", "House Hoods", 4),
SAMURAI_KASA("Samurai kasa", ItemID.SAMURAI_KASA, "Clue Scroll (Master)", "Samurai Outfit", 5),
SAMURAI_SHIRT("Samurai shirt", ItemID.SAMURAI_SHIRT, "Clue Scroll (Master)", "Samurai Outfit", 5),
SAMURAI_GREAVES("Samurai greaves", ItemID.SAMURAI_GREAVES, "Clue Scroll (Master)", "Samurai Outfit", 5),
SAMURAI_GLOVES("Samurai gloves", ItemID.SAMURAI_GLOVES, "Clue Scroll (Master)", "Samurai Outfit", 5),
SAMURAI_BOOTS("Samurai boots", ItemID.SAMURAI_BOOTS, "Clue Scroll (Master)", "Samurai Outfit", 5),
MUMMYS_HEAD("Mummy's head", ItemID.MUMMYS_HEAD, "Clue Scroll (Master)", "Mummy Outfit", 6),
MUMMYS_BODY("Mummy's body", ItemID.MUMMYS_BODY, "Clue Scroll (Master)", "Mummy Outfit", 6),
MUMMYS_LEGS("Mummy's legs", ItemID.MUMMYS_LEGS, "Clue Scroll (Master)", "Mummy Outfit", 6),
MUMMYS_HANDS("Mummy's hands", ItemID.MUMMYS_HANDS, "Clue Scroll (Master)", "Mummy Outfit", 6),
MUMMYS_FEET("Mummy's feet", ItemID.MUMMYS_FEET, "Clue Scroll (Master)", "Mummy Outfit", 6),
ANKOU_MASK("Ankou mask", ItemID.ANKOU_MASK, "Clue Scroll (Master)", "Ankou Outfit", 7),
ANKOU_TOP("Ankou top", ItemID.ANKOU_TOP, "Clue Scroll (Master)", "Ankou Outfit", 7),
ANKOUS_LEGGINGS("Ankou's leggings", ItemID.ANKOUS_LEGGINGS, "Clue Scroll (Master)", "Ankou Outfit", 7),
ANKOU_GLOVES("Ankou gloves", ItemID.ANKOU_GLOVES, "Clue Scroll (Master)", "Ankou Outfit", 7),
ANKOU_SOCKS("Ankou socks", ItemID.ANKOU_SOCKS, "Clue Scroll (Master)", "Ankou Outfit", 7),
HOOD_OF_DARKNESS("Hood of darkness", ItemID.HOOD_OF_DARKNESS, "Clue Scroll (Master)", "Robes of Darkness", 8),
ROBE_TOP_OF_DARKNESS("Robe top of darkness", ItemID.ROBE_TOP_OF_DARKNESS, "Clue Scroll (Master)", "Robes of Darkness", 8),
ROBE_BOTTOM_OF_DARKNESS("Robe bottom of darkness", ItemID.ROBE_BOTTOM_OF_DARKNESS, "Clue Scroll (Master)", "Robes of Darkness", 8),
BOOTS_OF_DARKNESS("Boots of darkness", ItemID.BOOTS_OF_DARKNESS, "Clue Scroll (Master)", "Robes of Darkness", 8),
GLOVES_OF_DARKNESS("Gloves of darkness", ItemID.GLOVES_OF_DARKNESS, "Clue Scroll (Master)", "Robes of Darkness", 8),
RING_OF_COINS("Ring of coins", ItemID.RING_OF_COINS, "Clue Scroll (Master)", "Other", 9),
LEFT_EYE_PATCH("Left eye patch", ItemID.LEFT_EYE_PATCH, "Clue Scroll (Master)", "Other", 9),
OBSIDIAN_CAPE_R("Obsidian cape (r)", ItemID.OBSIDIAN_CAPE_R, "Clue Scroll (Master)", "Other", 9),
FANCY_TIARA("Fancy tiara", ItemID.FANCY_TIARA, "Clue Scroll (Master)", "Other", 9),
HALF_MOON_SPECTACLES("Half moon spectacles", ItemID.HALF_MOON_SPECTACLES, "Clue Scroll (Master)", "Other", 9),
ALE_OF_THE_GODS("Ale of the gods", ItemID.ALE_OF_THE_GODS, "Clue Scroll (Master)", "Other 2", 10),
BUCKET_HELM_G("Bucket helm (g)", ItemID.BUCKET_HELM_G, "Clue Scroll (Master)", "Other 2", 10),
BOWL_WIG("Bowl wig", ItemID.BOWL_WIG, "Clue Scroll (Master)", "Other 2", 10),
BLOODHOUND("Bloodhound", ItemID.BLOODHOUND, "Clue Scroll (Master)", "Other 2", 10),
HERBI("Herbi", ItemID.HERBI, "Herbiboar", "Pet", -1);
// Shared Unique Items
UniqueItem(final String n, final int id, final String... activities)
{
this.name = n;
this.itemID = id;
this.activities = activities;
this.setName = "Shared";
this.position = -1;
}
// Non-Shared Unique Items
UniqueItem(final String n, final int id, final String a, final String set, final int position)
{
this.name = n;
this.itemID = id;
this.activities = new String[]{a};
this.setName = set;
this.position = position;
}
private final String name;
private final int itemID;
private final String[] activities;
private final String setName;
private final int position;
// Get a specific UniqueItem by Name
// Unused ATM
private static final Map<String, UniqueItem> byName = buildNameMap();
public static UniqueItem getByName(String name)
{
return byName.get(name.toUpperCase());
}
private static Map<String, UniqueItem> buildNameMap()
{
Map<String, UniqueItem> byName = new HashMap<>();
for (UniqueItem item : values())
{
byName.put(item.getName().toUpperCase(), item);
}
return byName;
}
// Returns an array of UniqueItems by an individual `activities` name
private static final Map<String, List<UniqueItem>> byActivityName = buildActivityMap();
public static List<UniqueItem> getByActivityName(String name)
{
return byActivityName.get(name.toUpperCase());
}
private static Map<String, List<UniqueItem>> buildActivityMap()
{
Map<String, List<UniqueItem>> byName = new HashMap<>();
for (UniqueItem item : values())
{
String[] activities = item.getActivities();
for (String activity : activities)
{
byName.computeIfAbsent(activity.toUpperCase(), e -> new ArrayList<>()).add(item);
}
}
return byName;
}
// Return an array of UniqueItems by `setName`
// Unused ATM
private static final Map<String, List<UniqueItem>> bySetName = buildSetMap();
public static List<UniqueItem> getBySetName(String name)
{
return bySetName.get(name.toUpperCase());
}
private static Map<String, List<UniqueItem>> buildSetMap()
{
Map<String, List<UniqueItem>> byName = new HashMap<>();
for (UniqueItem item : values())
{
byName.computeIfAbsent(item.getSetName().toUpperCase(), e -> new ArrayList<>()).add(item);
}
return byName;
}
// Takes a list of UniqueItems and maps them by Position to ensure adding in predefined order
public static Map<Integer, Collection<UniqueItemPrepared>> createPositionSetMap(Collection<UniqueItemPrepared> items)
{
Map<Integer, Collection<UniqueItemPrepared>> setNames = new HashMap<>();
for (UniqueItemPrepared item : items)
{
setNames.computeIfAbsent(item.getUniqueItem().getPosition(), e -> new ArrayList<>()).add(item);
}
// Sort them by Key
return setNames.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
}
}

View File

@@ -1,301 +0,0 @@
/*
* Copyright (c) 2018, TheStonedTurtle <https://github.com/TheStonedTurtle>
* 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.stonedloottracker.ui;
import com.google.common.collect.Iterators;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.inject.Singleton;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.stonedloottracker.ItemSortTypes;
import net.runelite.client.plugins.stonedloottracker.data.LootRecordCustom;
import net.runelite.client.plugins.stonedloottracker.data.LootTrackerItemEntry;
import net.runelite.client.plugins.stonedloottracker.data.UniqueItemPrepared;
import net.runelite.client.ui.ColorScheme;
@Slf4j
@Singleton
class LootPanel extends JPanel
{
private final Collection<LootRecordCustom> records;
private final Map<Integer, Collection<UniqueItemPrepared>> uniqueMap;
private final boolean hideUniques;
private final ItemSortTypes sortType;
private final boolean itemBreakdown;
private final ItemManager itemManager;
// Consolidate LTItemEntries stored by ItemID
private Map<Integer, LootTrackerItemEntry> consolidated;
@Getter
private boolean playbackPlaying = false;
private boolean cancelPlayback = false;
LootPanel(final Collection<LootRecordCustom> records, final Map<Integer, Collection<UniqueItemPrepared>> uniqueMap, final boolean hideUnqiues, final ItemSortTypes sort, final boolean itemBreakdown, final ItemManager itemManager)
{
this.records = (records == null ? new ArrayList<>() : records);
this.uniqueMap = (uniqueMap == null ? new HashMap<>() : uniqueMap);
this.hideUniques = hideUnqiues;
this.sortType = sort;
this.itemBreakdown = itemBreakdown;
this.itemManager = itemManager;
setLayout(new GridBagLayout());
setBorder(new EmptyBorder(0, 10, 0, 10));
setBackground(ColorScheme.DARK_GRAY_COLOR);
createConsolidatedArray(this.records);
createPanel(this.records);
}
private void createConsolidatedArray(Collection<LootRecordCustom> records)
{
// Consolidate all LootTrackerItemEntrys from each record for combined loot totals
// Sort them based on the config setting.
this.consolidated = LootRecordCustom.consolidateLootTrackerItemEntries(records)
.entrySet().stream()
.sorted((lt1, lt2) ->
{
LootTrackerItemEntry o1 = lt1.getValue();
LootTrackerItemEntry o2 = lt2.getValue();
switch (sortType)
{
case ITEM_ID:
return o1.getId() - o2.getId();
case PRICE:
if (o1.getPrice() != o2.getPrice())
{
return o1.getPrice() > o2.getPrice() ? -1 : 1;
}
break;
case VALUE:
if (o1.getTotal() != o2.getTotal())
{
return o1.getTotal() > o2.getTotal() ? -1 : 1;
}
break;
case ALPHABETICAL:
// Handled below
break;
default:
log.warn("Sort Type not being handled correctly, defaulting to alphabetical.");
break;
}
// Default to alphabetical
return o1.getName().compareTo(o2.getName());
})
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
}
private void createPanel(Collection<LootRecordCustom> records)
{
GridBagConstraints c = SelectionPanel.constrains();
// Create necessary helpers for the unique toggles
final Map<UniqueItemPrepared, Integer> uniqueQtys = new HashMap<>();
final Set<Integer> uniqueIds = new HashSet<>();
long totalValue = 0;
long totalHaValue = 0;
// Loop over all UniqueItems and check how many the player has received as a drop for each
// Also add all Item IDs for uniques to a Set for easy hiding later on.
for (Collection<UniqueItemPrepared> items : this.uniqueMap.values())
{
for (UniqueItemPrepared item : items)
{
int id = item.getUniqueItem().getItemID();
int linkedId = item.getLinkedID();
uniqueIds.add(id);
uniqueIds.add(linkedId);
LootTrackerItemEntry entry = this.consolidated.get(id);
LootTrackerItemEntry notedEntry = this.consolidated.get(linkedId);
int qty = (entry == null ? 0 : entry.getQuantity()) + (notedEntry == null ? 0 : notedEntry.getQuantity());
if (qty > 0)
{
totalValue += itemManager.getItemPrice(id);
totalHaValue += itemManager.getAlchValue(id);
uniqueQtys.put(item, qty);
}
}
}
// Attach all the Unique Items first
this.uniqueMap.forEach((setPosition, set) ->
{
UniqueItemPanel p = new UniqueItemPanel(set, uniqueQtys, this.itemManager);
this.add(p, c);
c.gridy++;
});
// Attach Kill Count Panel(s)
if (records.size() > 0)
{
int amount = records.size();
LootRecordCustom entry = Iterators.get(records.iterator(), (amount - 1));
if (entry.getKillCount() != -1)
{
TextPanel p = new TextPanel("Current Killcount:", entry.getKillCount());
this.add(p, c);
c.gridy++;
}
TextPanel p2 = new TextPanel("Kills Logged:", amount);
this.add(p2, c);
c.gridy++;
}
int totalValueIndex = c.gridy;
c.gridy += 2;
Collection<LootTrackerItemEntry> itemsToDisplay = new ArrayList<>();
List<LootTrackerItemEntry> items = LootTrackerBox.dedupeClues(new ArrayList<>(consolidated.values()));
for (LootTrackerItemEntry item : items)
{
totalValue += item.getTotal();
totalHaValue += item.getHaTotal();
if (hideUniques && uniqueIds.contains(item.getId()))
{
continue;
}
if (itemBreakdown)
{
ItemPanel p = new ItemPanel(item, itemManager);
this.add(p, c);
c.gridy++;
}
else
{
itemsToDisplay.add(item);
}
}
if (itemsToDisplay.size() > 0)
{
LootGrid grid = new LootGrid(itemsToDisplay.toArray(new LootTrackerItemEntry[0]), itemManager);
this.add(grid, c);
c.gridy++;
}
// Only add the total value element if it has something useful to display
if (totalValue > 0)
{
c.gridy = totalValueIndex;
TextPanel totalPanel = new TextPanel("Total Value:", totalValue);
this.add(totalPanel, c);
}
if (totalHaValue > 0)
{
c.gridy = totalValueIndex + 1;
TextPanel totalHaPanel = new TextPanel("Total HA Value:", totalHaValue);
this.add(totalHaPanel, c);
}
}
//void addedRecord(LootRecordCustom record)
void addedRecord()
{
//records.add(record);
// TODO: Smarter update system so it only repaints necessary Item and Text Panels
this.removeAll();
this.createConsolidatedArray(this.records);
this.createPanel(this.records);
this.revalidate();
this.repaint();
}
void playback()
{
playbackPlaying = true;
if (this.records.size() > 0)
{
Collection<LootRecordCustom> recs = new ArrayList<>();
for (LootRecordCustom r : this.records)
{
recs.add(r);
SwingUtilities.invokeLater(() -> refreshPlayback(recs));
try
{
if (cancelPlayback)
{
playbackPlaying = false;
cancelPlayback = false;
SwingUtilities.invokeLater(() -> refreshPlayback(this.records));
break;
}
Thread.sleep(250);
}
catch (InterruptedException e)
{
log.warn("Error in playback: " + e.getMessage());
}
}
}
playbackPlaying = false;
}
void cancelPlayback()
{
// Only cancel if actually playing
cancelPlayback = playbackPlaying;
}
private void refreshPlayback(Collection<LootRecordCustom> recs)
{
this.removeAll();
this.createConsolidatedArray(recs);
this.createPanel(recs);
this.revalidate();
this.repaint();
}
}

View File

@@ -1,293 +0,0 @@
/*
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* 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.stonedloottracker.ui;
import com.google.common.base.Strings;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import javax.annotation.Nullable;
import javax.inject.Singleton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.stonedloottracker.data.LootRecordCustom;
import net.runelite.client.plugins.stonedloottracker.data.LootTrackerItemEntry;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.FontManager;
import net.runelite.client.util.StackFormatter;
import net.runelite.client.util.Text;
@Singleton
@Getter(AccessLevel.PACKAGE)
class LootTrackerBox extends JPanel
{
private static final int ITEMS_PER_ROW = 5;
private final JPanel itemContainer = new JPanel();
private final JLabel priceLabel = new JLabel();
private final JLabel subTitleLabel = new JLabel();
private final ItemManager itemManager;
@Getter(AccessLevel.PACKAGE)
private final String id;
@Getter(AccessLevel.PACKAGE)
private final List<LootRecordCustom> records = new ArrayList<>();
private long totalPrice;
private long totalHaPrice;
LootTrackerBox(final ItemManager itemManager, final String id, @Nullable final String subtitle, LootRecordCustom record)
{
this.id = id;
this.itemManager = itemManager;
this.records.add(record);
setLayout(new BorderLayout(0, 1));
setBorder(new EmptyBorder(5, 0, 0, 0));
final JPanel logTitle = new JPanel(new BorderLayout(5, 0));
logTitle.setBorder(new EmptyBorder(7, 7, 7, 7));
logTitle.setBackground(ColorScheme.DARKER_GRAY_COLOR.darker());
final JLabel titleLabel = new JLabel(Text.removeTags(id));
titleLabel.setFont(FontManager.getSmallFont(getFont()));
titleLabel.setForeground(Color.WHITE);
logTitle.add(titleLabel, BorderLayout.WEST);
subTitleLabel.setFont(FontManager.getSmallFont(getFont()));
subTitleLabel.setForeground(ColorScheme.LIGHT_GRAY_COLOR);
logTitle.add(subTitleLabel, BorderLayout.CENTER);
if (!Strings.isNullOrEmpty(subtitle))
{
subTitleLabel.setText(subtitle);
}
priceLabel.setFont(FontManager.getSmallFont(getFont()));
priceLabel.setForeground(ColorScheme.LIGHT_GRAY_COLOR);
logTitle.add(priceLabel, BorderLayout.EAST);
add(logTitle, BorderLayout.NORTH);
add(itemContainer, BorderLayout.CENTER);
buildItems();
}
/**
* This method creates stacked items from the item list, calculates total price and then
* displays all the items in the UI.
*/
private void buildItems()
{
final List<LootTrackerItemEntry> allItems = new ArrayList<>();
final List<LootTrackerItemEntry> items = new ArrayList<>();
totalPrice = 0;
totalHaPrice = 0;
for (LootRecordCustom record : records)
{
allItems.addAll(record.getDrops());
}
for (final LootTrackerItemEntry entry : dedupeClues(allItems))
{
totalPrice += (entry.getPrice() * entry.getQuantity());
totalHaPrice += (entry.getHaPrice() * entry.getQuantity());
int quantity = 0;
for (final LootTrackerItemEntry i : items)
{
if (i.getId() == entry.getId())
{
quantity = i.getQuantity();
items.remove(i);
break;
}
}
if (quantity > 0)
{
int newQuantity = entry.getQuantity() + quantity;
long pricePerItem = entry.getPrice();
long haPricePerItem = entry.getHaPrice();
items.add(new LootTrackerItemEntry(entry.getName(), entry.getId(), newQuantity, pricePerItem, haPricePerItem, false));
}
else
{
items.add(entry);
}
}
items.sort((i1, i2) -> Long.compare(i2.getPrice(), i1.getPrice()));
// Calculates how many rows need to be display to fit all items
final int rowSize = rowSize(items.size());
resetContainer(itemContainer, rowSize);
for (int i = 0; i < rowSize * ITEMS_PER_ROW; i++)
{
final JPanel slotContainer = new JPanel();
slotContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR);
if (i < items.size())
{
final LootTrackerItemEntry item = items.get(i);
final JLabel imageLabel = buildimageLabel(itemManager, item);
slotContainer.add(imageLabel);
}
itemContainer.add(slotContainer);
}
itemContainer.repaint();
priceLabel.setText(StackFormatter.quantityToStackSize(totalPrice) + " gp (HA: " + StackFormatter.quantityToStackSize(totalHaPrice) + ")");
priceLabel.setToolTipText(StackFormatter.formatNumber(totalPrice) + " gp (HA: " + StackFormatter.formatNumber(totalHaPrice) + ")");
if (records.size() > 1)
{
subTitleLabel.setText("x " + records.size());
}
repaint();
}
static List<LootTrackerItemEntry> dedupeClues(List<LootTrackerItemEntry> items)
{
final List<LootTrackerItemEntry> newItems = new ArrayList<>();
int eliteClues = 0;
int hardClues = 0;
int mediumClues = 0;
int easyClues = 0;
int beginnerClues = 0;
LootTrackerItemEntry eliteClue = null;
LootTrackerItemEntry hardClue = null;
LootTrackerItemEntry mediumClue = null;
LootTrackerItemEntry easyClue = null;
LootTrackerItemEntry beginnerClue = null;
for (LootTrackerItemEntry lootEntry : items)
{
if (lootEntry == null)
{
continue;
}
String name = lootEntry.getName();
if (lootEntry.getName().contains("Clue scroll ("))
{
if (name.contains("elite"))
{
eliteClues += lootEntry.getQuantity();
eliteClue = lootEntry;
}
else if (name.contains("hard"))
{
hardClues += lootEntry.getQuantity();
hardClue = lootEntry;
}
else if (name.contains("medium"))
{
mediumClues += lootEntry.getQuantity();
mediumClue = lootEntry;
}
else if (name.contains("easy"))
{
easyClues += lootEntry.getQuantity();
easyClue = lootEntry;
}
else if (name.contains("beginner"))
{
beginnerClues += lootEntry.getQuantity();
beginnerClue = lootEntry;
}
continue;
}
newItems.add(lootEntry);
}
LootTrackerItemEntry[] clueArray = {beginnerClue, easyClue, mediumClue, hardClue, eliteClue};
int[] clueAmountArray = {beginnerClues, easyClues, mediumClues, hardClues, eliteClues};
for (int i = 0; i < 4; i++)
{
if (clueAmountArray[i] > 0)
{
newItems.add(createNewLootEntry(Objects.requireNonNull(clueArray[i]), clueAmountArray[i]));
}
}
return newItems;
}
private static LootTrackerItemEntry createNewLootEntry(LootTrackerItemEntry item, int amount)
{
return new LootTrackerItemEntry(
item.getName(),
item.getId(),
amount,
item.getPrice(),
item.getHaPrice(),
item.isStackable()
);
}
static int rowSize(int items)
{
return ((items % ITEMS_PER_ROW == 0) ? 0 : 1) + items / ITEMS_PER_ROW;
}
private static void resetContainer(JPanel itemContainer, int rowSize)
{
itemContainer.removeAll();
itemContainer.setLayout(new GridLayout(rowSize, ITEMS_PER_ROW, 1, 1));
}
private static JLabel buildimageLabel(ItemManager itemManager, LootTrackerItemEntry item)
{
final JLabel imageLabel = new JLabel();
imageLabel.setToolTipText(UniqueItemPanel.tooltipText(item.getPrice(), item.getHaPrice(), item.getQuantity(), item.getName()));
imageLabel.setVerticalAlignment(SwingConstants.CENTER);
imageLabel.setHorizontalAlignment(SwingConstants.CENTER);
itemManager.getImage(item.getId(), item.getQuantity(), item.getQuantity() > 1).addTo(imageLabel);
return imageLabel;
}
}

View File

@@ -1,18 +1,16 @@
package net.runelite.client.plugins.stonedloottracker;
package net.runelite.client.plugins.stonedtracker;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter(AccessLevel.PACKAGE)
@Getter
@RequiredArgsConstructor
public enum ItemSortTypes
{
ALPHABETICAL("Alphabetical"),
ITEM_ID("Item ID"),
VALUE("Value"),
PRICE("Price"),
HAPRICE("HA Price");
PRICE("Price");
private final String name;

View File

@@ -22,58 +22,20 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.stonedloottracker;
package net.runelite.client.plugins.stonedtracker;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
import net.runelite.client.config.Stub;
@ConfigGroup("stonedloottracker")
public interface StonedLootTrackerConfig extends Config
@ConfigGroup("stonedtracker")
public interface StonedTrackerConfig extends Config
{
@ConfigItem(
keyName = "mainStub",
name = "Main panel",
description = "",
position = 1
)
default Stub mainStub()
{
return new Stub();
}
@ConfigItem(
position = 2,
keyName = "bossButtons",
name = "Show boss icons",
description = "Toggles whether the selection screen will use the boss icons",
parent = "mainStub"
)
default boolean bossButtons()
{
return true;
}
@ConfigItem(
keyName = "itemsStub",
name = "Items",
description = "",
position = 3
)
default Stub itemsStub()
{
return new Stub();
}
// TODO Option is not working will look into it later
@ConfigItem(
position = 4,
position = 0,
keyName = "hideUniques",
name = "Hide uniques",
description = "Hides unique items from the item breakdown",
parent = "itemsStub",
hidden = true
description = "Hides unique items from the item breakdown"
)
default boolean hideUniques()
{
@@ -81,11 +43,10 @@ public interface StonedLootTrackerConfig extends Config
}
@ConfigItem(
position = 5,
position = 1,
keyName = "itemSortType",
name = "Sort Items by",
description = "Sorts items by the requested value inside the UI. (Doesn't effect session/box view)",
parent = "itemsStub"
description = "Sorts items by the requested value inside the UI. (Doesn't effect session/box view)"
)
default ItemSortTypes itemSortType()
{
@@ -93,14 +54,24 @@ public interface StonedLootTrackerConfig extends Config
}
@ConfigItem(
position = 6,
position = 2,
keyName = "itemBreakdown",
name = "Breakdown individual items",
description = "Toggles whether the Individual item UI should be used inside npc-specific tabs",
parent = "itemsStub"
description = "Toggles whether the Individual item UI should be used inside npc-specific tabs"
)
default boolean itemBreakdown()
{
return true;
}
@ConfigItem(
position = 3,
keyName = "bossButtons",
name = "Show boss icons",
description = "Toggles whether the selection screen will use the boss icons"
)
default boolean bossButtons()
{
return true;
}
}

View File

@@ -0,0 +1,271 @@
/*
* Copyright (c) 2018, Psikoi <https://github.com/psikoi>
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.stonedtracker;
import com.google.inject.Provides;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import javax.inject.Inject;
import javax.swing.SwingUtilities;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.ItemDefinition;
import net.runelite.api.NpcID;
import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.GameTick;
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.callback.ClientThread;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.loottracker.localstorage.LTItemEntry;
import net.runelite.client.plugins.loottracker.localstorage.LTRecord;
import net.runelite.client.plugins.loottracker.localstorage.LootRecordWriter;
import net.runelite.client.plugins.loottracker.localstorage.events.LTNameChange;
import net.runelite.client.plugins.loottracker.localstorage.events.LTRecordStored;
import net.runelite.client.plugins.stonedtracker.data.BossTab;
import net.runelite.client.plugins.stonedtracker.data.UniqueItem;
import net.runelite.client.plugins.stonedtracker.ui.LootTrackerPanel;
import net.runelite.client.ui.ClientToolbar;
import net.runelite.client.ui.NavigationButton;
import net.runelite.client.util.ImageUtil;
@PluginDescriptor(
name = "Stoned Tracker",
description = "Local data persistence and unique UI for the Loot Tracker.",
tags = {"Stoned", "Loot", "Tracker"}
)
@Slf4j
public class StonedTrackerPlugin extends Plugin
{
private static final String SIRE_FONT_TEXT = "you place the unsired into the font of consumption...";
private static final String SIRE_REWARD_TEXT = "the font consumes the unsired";
@Inject
private ClientToolbar clientToolbar;
@Inject
public StonedTrackerConfig config;
@Inject
private Client client;
@Inject
private ItemManager itemManager;
@Inject
private ClientThread clientThread;
@Inject
private LootRecordWriter writer;
@Inject
private EventBus eventBus;
private LootTrackerPanel panel;
private NavigationButton navButton;
@Getter
private Set<String> lootNames = new TreeSet<>();
private boolean loaded = false;
private boolean unsiredReclaiming = false;
@Provides
StonedTrackerConfig provideConfig(ConfigManager configManager)
{
return configManager.getConfig(StonedTrackerConfig.class);
}
private void onLTRecordStored(final LTRecordStored s)
{
final LTRecord record = s.getRecord();
lootNames.add(record.getName());
SwingUtilities.invokeLater(() -> panel.addLog(record));
}
private void onLTNameChange(final LTNameChange c)
{
lootNames = new TreeSet<>(writer.getKnownFileNames());
SwingUtilities.invokeLater(() -> panel.showSelectionView());
}
private void onConfigChanged(final ConfigChanged event)
{
if (event.getGroup().equals("stonedtracker"))
{
panel.refreshUI();
}
}
@Override
protected void startUp()
{
addSubscriptions();
panel = new LootTrackerPanel(itemManager, this);
final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "panel-icon.png");
navButton = NavigationButton.builder()
.tooltip("Stoned Tracker")
.icon(icon)
.priority(5)
.panel(panel)
.build();
clientToolbar.addNavigation(navButton);
// Attach necessary info from item manager on load
if (!loaded)
{
loaded = true;
clientThread.invokeLater(() ->
{
switch (client.getGameState())
{
case UNKNOWN:
case STARTING:
loaded = false;
return false;
}
UniqueItem.prepareUniqueItems(itemManager);
return true;
});
}
}
@Override
protected void shutDown()
{
eventBus.unregister(this);
clientToolbar.removeNavigation(navButton);
}
private void addSubscriptions()
{
this.eventBus.subscribe(LTRecordStored.class, this, this::onLTRecordStored);
this.eventBus.subscribe(LTNameChange.class, this, this::onLTNameChange);
this.eventBus.subscribe(ConfigChanged.class, this, this::onConfigChanged);
this.eventBus.subscribe(WidgetLoaded.class, this, this::onWidgetLoaded);
this.eventBus.subscribe(GameTick.class, this, this::onGameTick);
}
private void onWidgetLoaded(WidgetLoaded event)
{
if (event.getGroupId() != WidgetID.DIALOG_SPRITE_GROUP_ID)
{
return;
}
Widget text = client.getWidget(WidgetInfo.DIALOG_SPRITE_TEXT);
if (SIRE_FONT_TEXT.equals(text.getText().toLowerCase()))
{
unsiredReclaiming = true;
}
}
private void onGameTick(GameTick t)
{
if (unsiredReclaiming)
{
checkUnsiredWidget();
}
}
// Handles checking for unsired loot reclamation
private void checkUnsiredWidget()
{
log.info("Checking for text widget change...");
Widget text = client.getWidget(WidgetInfo.DIALOG_SPRITE_TEXT);
if (text.getText().toLowerCase().contains(SIRE_REWARD_TEXT))
{
unsiredReclaiming = false;
log.info("Text widget changed, reclaimed an item");
Widget sprite = client.getWidget(WidgetInfo.DIALOG_SPRITE);
log.info("Sprite Item ID: {}", sprite.getItemId());
receivedUnsiredLoot(sprite.getItemId());
}
else
{
log.info("Old text still...");
}
}
// Handles adding the unsired loot to the tracker
private void receivedUnsiredLoot(int itemID)
{
clientThread.invokeLater(() ->
{
Collection<LTRecord> data = getDataByName("Abyssal sire");
ItemDefinition c = itemManager.getItemDefinition(itemID);
LTItemEntry itemEntry = new LTItemEntry(c.getName(), itemID, 1, 0);
log.debug("Received Unsired item: {}", c.getName());
// Don't have data for sire, create a new record with just this data.
if (data == null)
{
log.debug("No previous Abyssal sire loot, creating new loot record");
LTRecord r = new LTRecord(NpcID.ABYSSAL_SIRE, "Abyssal sire", 350, -1, Collections.singletonList(itemEntry));
writer.addLootTrackerRecord(r);
return;
}
log.debug("Adding drop to last abyssal sire loot record");
// Add data to last kill count
List<LTRecord> items = new ArrayList<>(data);
LTRecord r = items.get(items.size() - 1);
r.addDropEntry(itemEntry);
writer.writeLootTrackerFile("Abyssal sire", items);
// Write will trigger event this plugin is subscribed too
});
}
public Collection<LTRecord> getDataByName(String name)
{
final BossTab tab = BossTab.getByName(name);
if (tab != null)
{
name = tab.getName();
}
return writer.loadLootTrackerRecords(name);
}
public boolean clearStoredDataByName(final String name)
{
return writer.deleteLootTrackerRecords(name);
}
}

View File

@@ -22,47 +22,50 @@
* (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.stonedloottracker.data;
package net.runelite.client.plugins.stonedtracker.data;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import lombok.AccessLevel;
import javax.annotation.Nullable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.runelite.api.ItemID;
@Getter(AccessLevel.PUBLIC)
@Getter
@AllArgsConstructor
public enum BossTab
{
// Chest Rewards
BARROWS("Barrows", ItemID.BARROWS_TELEPORT, "Other"),
RAIDS("Chambers of Xeric", ItemID.OLMLET, "Other"),
RAIDS_2("Theatre of Blood", ItemID.LIL_ZIK, "Other"),
CHAMBERS_OF_XERIC("Chambers of Xeric", ItemID.OLMLET, "Other"),
THEATRE_OF_BLOOD("Theatre of Blood", ItemID.LIL_ZIK, "Other"),
// Loot received on NPC death
ZULRAH("Zulrah", ItemID.PET_SNAKELING, "Other"),
VORKATH("Vorkath", ItemID.VORKI, "Other"),
// God wars dungeon
ARMADYL("Kree'arra", ItemID.PET_KREEARRA , "God Wars Dungeon"),
BANDOS("General Graardor", ItemID.PET_GENERAL_GRAARDOR , "God Wars Dungeon"),
SARADOMIN("Commander Zilyana", ItemID.PET_ZILYANA , "God Wars Dungeon"),
ZAMMY("K'ril Tsutsaroth", ItemID.PET_KRIL_TSUTSAROTH , "God Wars Dungeon"),
KREEARRA("Kree'arra", ItemID.PET_KREEARRA, "God Wars Dungeon"),
GENERAL_GRAARDOR("General Graardor", ItemID.PET_GENERAL_GRAARDOR, "God Wars Dungeon"),
COMMANDER_ZILYANA("Commander Zilyana", ItemID.PET_ZILYANA, "God Wars Dungeon"),
KRIL_TSUTSAROTH("K'ril Tsutsaroth", ItemID.PET_KRIL_TSUTSAROTH, "God Wars Dungeon"),
// Wildy Bosses
VETION("Vet'ion", ItemID.VETION_JR , "Wilderness"),
VENENATIS("Venenatis", ItemID.VENENATIS_SPIDERLING , "Wilderness"),
CALLISTO("Callisto", ItemID.CALLISTO_CUB , "Wilderness"),
CHAOS_ELEMENTAL("Chaos Elemental", ItemID.PET_CHAOS_ELEMENTAL , "Wilderness"),
VETION("Vet'ion Reborn", ItemID.VETION_JR, "Wilderness"),
VENENATIS("Venenatis", ItemID.VENENATIS_SPIDERLING, "Wilderness"),
CALLISTO("Callisto", ItemID.CALLISTO_CUB, "Wilderness"),
CHAOS_ELEMENTAL("Chaos Elemental", ItemID.PET_CHAOS_ELEMENTAL, "Wilderness"),
// Wildy Demi-Bosses
SCORPIA("Scorpia", ItemID.SCORPIAS_OFFSPRING, "Wilderness"),
CHAOS_FANATIC("Chaos Fanatic", ItemID.ANCIENT_STAFF , "Wilderness"),
CRAZY_ARCHAEOLOGIST("Crazy Archaeologist", ItemID.FEDORA , "Wilderness"),
CHAOS_FANATIC("Chaos Fanatic", ItemID.ANCIENT_STAFF, "Wilderness"),
CRAZY_ARCHAEOLOGIST("Crazy Archaeologist", ItemID.FEDORA, "Wilderness"),
// Wildy Other
KING_BLACK_DRAGON("King Black Dragon", ItemID.PRINCE_BLACK_DRAGON , "Wilderness"),
KING_BLACK_DRAGON("King Black Dragon", ItemID.PRINCE_BLACK_DRAGON, "Wilderness"),
// Slayer Bosses
KALPHITE_QUEEN("Kalphite Queen", ItemID.KALPHITE_PRINCESS, "Other"),
@@ -71,7 +74,8 @@ public enum BossTab
ABYSSAL_SIRE("Abyssal Sire", ItemID.ABYSSAL_ORPHAN, "Slayer"),
KRAKEN("Kraken", ItemID.PET_KRAKEN, "Slayer"),
CERBERUS("Cerberus", ItemID.HELLPUPPY, "Slayer"),
THERMONUCLEAR_SMOKE_DEVIL("Thermonuclear Smoke Devil", ItemID.PET_SMOKE_DEVIL, "Slayer"),
THERMONUCLEAR_SMOKE_DEVIL("Thermonuclear smoke devil", ItemID.PET_SMOKE_DEVIL, "Slayer"),
ALCHEMICAL_HYDRA("Alchemical Hydra", ItemID.IKKLE_HYDRA, "Slayer"),
// Other Bosses
GIANT_MOLE("Giant Mole", ItemID.BABY_MOLE, "Other"),
@@ -87,65 +91,43 @@ public enum BossTab
CLUE_SCROLL_MEDIUM("Clue Scroll (Medium)", ItemID.CLUE_SCROLL_MEDIUM, "Clue Scrolls"),
CLUE_SCROLL_HARD("Clue Scroll (Hard)", ItemID.CLUE_SCROLL_HARD, "Clue Scrolls"),
CLUE_SCROLL_ELITE("Clue Scroll (Elite)", ItemID.CLUE_SCROLL_ELITE, "Clue Scrolls"),
CLUE_SCROLL_MASTER("Clue Scroll (Master)", ItemID.CLUE_SCROLL_MASTER, "Clue Scrolls"),
CLUE_SCROLL_MASTER("Clue Scroll (Master)", ItemID.CLUE_SCROLL_MASTER, "Clue Scrolls");
// Hunter
HERBIBOAR("Herbiboar", ItemID.HERBI, "Hunter");
private static final Map<String, BossTab> NAME_MAP;
private static final Multimap<String, BossTab> CATEGORY_MAP;
BossTab(String name, int iconItem, String category)
static
{
this.name = name;
this.itemID = iconItem;
this.category = category;
final ImmutableMap.Builder<String, BossTab> byName = ImmutableMap.builder();
final ImmutableMultimap.Builder<String, BossTab> categoryMap = ImmutableMultimap.builder();
for (BossTab tab : values())
{
byName.put(tab.getName().toUpperCase(), tab);
categoryMap.put(tab.getCategory(), tab);
}
NAME_MAP = byName.build();
CATEGORY_MAP = categoryMap.build();
}
private final String name;
private final int itemID;
private final String category;
// By Boss Name
private static final Map<String, BossTab> byName = buildMap();
public static BossTab getByName(String name)
@Nullable
public static BossTab getByName(final String name)
{
return byName.get(name.toUpperCase());
}
private static Map<String, BossTab> buildMap()
{
Map<String, BossTab> byName = new HashMap<>();
for (BossTab tab : values())
{
byName.put(tab.getName().toUpperCase(), tab);
}
return byName;
return NAME_MAP.get(name.toUpperCase());
}
// By Category Name
private static final Map<String, List<BossTab>> byCategoryName = buildCategoryMap();
public static List<BossTab> getByCategoryName(String name)
public static Collection<BossTab> getByCategoryName(final String name)
{
return byCategoryName.get(name.toUpperCase());
}
private static Map<String, List<BossTab>> buildCategoryMap()
{
Map<String, List<BossTab>> map = new HashMap<>();
for (BossTab tab : values())
{
map.computeIfAbsent(tab.getCategory().toUpperCase(), e -> new ArrayList<>()).add(tab);
}
return map;
return CATEGORY_MAP.get(name);
}
// All Categories
public static final Set<String> categories = getCategories();
private static Set<String> getCategories()
public static Set<String> getCategories()
{
Set<String> s = new TreeSet<>();
for (BossTab tab : values())
{
s.add(tab.getCategory());
}
return s;
return new TreeSet<>(CATEGORY_MAP.keySet());
}
}

View File

@@ -0,0 +1,897 @@
/*
* Copyright (c) 2018, TheStonedTurtle <www.github.com/TheStonedTurtle>
* 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.stonedtracker.data;
import com.google.common.collect.ImmutableMultimap;
import java.util.Collection;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import net.runelite.api.ItemDefinition;
import net.runelite.api.ItemID;
import net.runelite.client.game.ItemManager;
@RequiredArgsConstructor
@Getter
public enum UniqueItem
{
// "Uniques" which are actually dropped by multiple bosses
// God Wars
GODSWORD_SHARD_1(ItemID.GODSWORD_SHARD_1, BossTab.KREEARRA, BossTab.GENERAL_GRAARDOR, BossTab.COMMANDER_ZILYANA, BossTab.KRIL_TSUTSAROTH),
GODSWORD_SHARD_2(ItemID.GODSWORD_SHARD_2, BossTab.KREEARRA, BossTab.GENERAL_GRAARDOR, BossTab.COMMANDER_ZILYANA, BossTab.KRIL_TSUTSAROTH),
GODSWORD_SHARD_3(ItemID.GODSWORD_SHARD_3, BossTab.KREEARRA, BossTab.GENERAL_GRAARDOR, BossTab.COMMANDER_ZILYANA, BossTab.KRIL_TSUTSAROTH),
// Wildy
PET_CHAOS_ELEMENTAL(ItemID.PET_CHAOS_ELEMENTAL, BossTab.CHAOS_ELEMENTAL, BossTab.CHAOS_FANATIC),
// MYSTERIOUS_EMBLEM(BossTab.MYSTERIOUS_EMBLEM, ItemID.MYSTERIOUS_EMBLEM, BossTab.),
CURVED_BONE(ItemID.CURVED_BONE, BossTab.CALLISTO, BossTab.VETION, BossTab.VENENATIS, BossTab.GIANT_MOLE),
DRAGON_PICKAXE(ItemID.DRAGON_PICKAXE, BossTab.CALLISTO, BossTab.VETION, BossTab.VENENATIS, BossTab.KING_BLACK_DRAGON),
DRAGON_2H_SWORD(ItemID.DRAGON_2H_SWORD, BossTab.CALLISTO, BossTab.VETION, BossTab.VENENATIS, BossTab.KALPHITE_QUEEN),
// Other
DRAGON_CHAINBODY(ItemID.DRAGON_CHAINBODY_3140, BossTab.THERMONUCLEAR_SMOKE_DEVIL, BossTab.KALPHITE_QUEEN),
DRAGON_AXE(ItemID.DRAGON_AXE, BossTab.DAGANNOTH_REX, BossTab.DAGANNOTH_PRIME, BossTab.DAGANNOTH_SUPREME),
UNCUT_ONYX(ItemID.UNCUT_ONYX, BossTab.ZULRAH, BossTab.SKOTIZO),
// Unique Items
// Barrows Uniques
// Ahrim
AHRIMS_HOOD(ItemID.AHRIMS_HOOD, BossTab.BARROWS, 0),
AHRIMS_ROBETOP(ItemID.AHRIMS_ROBETOP, BossTab.BARROWS, 0),
AHRIMS_ROBESKIRT(ItemID.AHRIMS_ROBESKIRT, BossTab.BARROWS, 0),
AHRIMS_STAFF(ItemID.AHRIMS_STAFF, BossTab.BARROWS, 0),
// Dharok
DHAROKS_HELM(ItemID.DHAROKS_HELM, BossTab.BARROWS, 1),
DHAROKS_PLATEBODY(ItemID.DHAROKS_PLATEBODY, BossTab.BARROWS, 1),
DHAROKS_PLATELEGS(ItemID.DHAROKS_PLATELEGS, BossTab.BARROWS, 1),
DHAROKS_GREATAXE(ItemID.DHAROKS_GREATAXE, BossTab.BARROWS, 1),
// Guthans
GUTHANS_HELM(ItemID.GUTHANS_HELM, BossTab.BARROWS, 2),
GUTHANS_PLATEBODY(ItemID.GUTHANS_PLATEBODY, BossTab.BARROWS, 2),
GUTHANS_CHAINSKIRT(ItemID.GUTHANS_CHAINSKIRT, BossTab.BARROWS, 2),
GUTHANS_WARSPEAR(ItemID.GUTHANS_WARSPEAR, BossTab.BARROWS, 2),
// Karils
KARILS_COIF(ItemID.KARILS_COIF, BossTab.BARROWS, 3),
KARILS_LEATHERTOP(ItemID.KARILS_LEATHERTOP, BossTab.BARROWS, 3),
KARILS_LEATHERSKIRT(ItemID.KARILS_LEATHERSKIRT, BossTab.BARROWS, 3),
KARILS_CROSSBOW(ItemID.KARILS_CROSSBOW, BossTab.BARROWS, 3),
//Torag
TORAGS_HELM(ItemID.TORAGS_HELM, BossTab.BARROWS, 4),
TORAGS_PLATEBODY(ItemID.TORAGS_PLATEBODY, BossTab.BARROWS, 4),
TORAGS_PLATELEGS(ItemID.TORAGS_PLATELEGS, BossTab.BARROWS, 4),
TORAGS_HAMMERS(ItemID.TORAGS_HAMMERS, BossTab.BARROWS, 4),
// Veracs
VERACS_HELM(ItemID.VERACS_HELM, BossTab.BARROWS, 5),
VERACS_BRASSARD(ItemID.VERACS_BRASSARD, BossTab.BARROWS, 5),
VERACS_PLATESKIRT(ItemID.VERACS_PLATESKIRT, BossTab.BARROWS, 5),
VERACS_FLAIL(ItemID.VERACS_FLAIL, BossTab.BARROWS, 5),
// Raids Uniques
// Mage
KODAI_INSIGNIA(ItemID.KODAI_INSIGNIA, BossTab.CHAMBERS_OF_XERIC, 0),
ANCESTRAL_HAT(ItemID.ANCESTRAL_HAT, BossTab.CHAMBERS_OF_XERIC, 0),
ANCESTRAL_ROBE_TOP(ItemID.ANCESTRAL_ROBE_TOP, BossTab.CHAMBERS_OF_XERIC, 0),
ANCESTRAL_ROBE_BOTTOM(ItemID.ANCESTRAL_ROBE_BOTTOM, BossTab.CHAMBERS_OF_XERIC, 0),
// Range
DRAGON_HUNTER_CROSSBOW(ItemID.DRAGON_HUNTER_CROSSBOW, BossTab.CHAMBERS_OF_XERIC, 1),
TWISTED_BUCKLER(ItemID.TWISTED_BUCKLER, BossTab.CHAMBERS_OF_XERIC, 1),
TWISTED_BOW(ItemID.TWISTED_BOW, BossTab.CHAMBERS_OF_XERIC, 1),
// Melee
DRAGON_CLAWS(ItemID.DRAGON_CLAWS, BossTab.CHAMBERS_OF_XERIC, 2),
DINHS_BULWARK(ItemID.DINHS_BULWARK, BossTab.CHAMBERS_OF_XERIC, 2),
ELDER_MAUL(ItemID.ELDER_MAUL, BossTab.CHAMBERS_OF_XERIC, 2),
// Prayers/Other
TORN_PRAYER_SCROLL(ItemID.TORN_PRAYER_SCROLL, BossTab.CHAMBERS_OF_XERIC, 3),
ARCANE_PRAYER_SCROLL(ItemID.ARCANE_PRAYER_SCROLL, BossTab.CHAMBERS_OF_XERIC, 3),
DEXTEROUS_PRAYER_SCROLL(ItemID.DEXTEROUS_PRAYER_SCROLL, BossTab.CHAMBERS_OF_XERIC, 3),
DARK_RELIC(ItemID.DARK_RELIC, BossTab.CHAMBERS_OF_XERIC, 3),
// Raids 2 Unqiues (Theater of Blood)
// Weapons / Pet
GHRAZI_RAPIER(ItemID.GHRAZI_RAPIER, BossTab.THEATRE_OF_BLOOD, 0),
SCYTHE_OF_VITUR_UNCHARGED(ItemID.SCYTHE_OF_VITUR_UNCHARGED, BossTab.THEATRE_OF_BLOOD, 0),
SANGUINESTI_STAFF_UNCHAGRED(ItemID.SANGUINESTI_STAFF_UNCHARGED, BossTab.THEATRE_OF_BLOOD, 0),
LIL_ZIK(ItemID.LIL_ZIK, BossTab.THEATRE_OF_BLOOD, 0),
// Armor
AVERNIC_DEFENDER_HILT(ItemID.AVERNIC_DEFENDER_HILT, BossTab.THEATRE_OF_BLOOD, 1),
JUSTICIAR_FACEGUARD(ItemID.JUSTICIAR_FACEGUARD, BossTab.THEATRE_OF_BLOOD, 1),
JUSTICIAR_CHESTGUARD(ItemID.JUSTICIAR_CHESTGUARD, BossTab.THEATRE_OF_BLOOD, 1),
JUSTICIAR_LEGGUARDS(ItemID.JUSTICIAR_LEGGUARDS, BossTab.THEATRE_OF_BLOOD, 1),
// Zulrah
// Uniques
TANZANITE_FANG(ItemID.TANZANITE_FANG, BossTab.ZULRAH, -1),
MAGIC_FANG(ItemID.MAGIC_FANG, BossTab.ZULRAH, -1),
SERPENTINE_VISAGE(ItemID.SERPENTINE_VISAGE, BossTab.ZULRAH, -1),
// Rares
PET_SNAKELING(ItemID.PET_SNAKELING, BossTab.ZULRAH, 0),
TANZANITE_MUTAGEN(ItemID.TANZANITE_MUTAGEN, BossTab.ZULRAH, 0),
MAGMA_MUTAGEN(ItemID.MAGMA_MUTAGEN, BossTab.ZULRAH, 0),
JAR_OF_SWAMP(ItemID.JAR_OF_SWAMP, BossTab.ZULRAH, 0),
// Vorkath
DRAGONBONE_NECKLACE(ItemID.DRAGONBONE_NECKLACE, BossTab.VORKATH, 0),
VORKATHS_HEAD(ItemID.VORKATHS_HEAD_21907, BossTab.VORKATH, 0),
JAR_OF_DECAY(ItemID.JAR_OF_DECAY, BossTab.VORKATH, 0),
VORKI(ItemID.VORKI, BossTab.VORKATH, 0),
SKELETAL_VISAGE(ItemID.SKELETAL_VISAGE, BossTab.VORKATH, 0),
// God Wars Dungeon
// Kreearra (Armadyl)
ARMADYL_HELMET(ItemID.ARMADYL_HELMET, BossTab.KREEARRA, 0),
ARMADYL_CHESTPLATE(ItemID.ARMADYL_CHESTPLATE, BossTab.KREEARRA, 0),
ARMADYL_CHAINSKIRT(ItemID.ARMADYL_CHAINSKIRT, BossTab.KREEARRA, 0),
ARMADYL_HILT(ItemID.ARMADYL_HILT, BossTab.KREEARRA, 0),
PET_KREEARRA(ItemID.PET_KREEARRA, BossTab.KREEARRA, -1),
// General Graardor (Bandos)
BANDOS_CHESTPLATE(ItemID.BANDOS_CHESTPLATE, BossTab.GENERAL_GRAARDOR, 0),
BANDOS_TASSETS(ItemID.BANDOS_TASSETS, BossTab.GENERAL_GRAARDOR, 0),
BANDOS_BOOTS(ItemID.BANDOS_BOOTS, BossTab.GENERAL_GRAARDOR, 0),
BANDOS_HILT(ItemID.BANDOS_HILT, BossTab.GENERAL_GRAARDOR, 0),
PET_GENERAL_GRAARDOR(ItemID.PET_GENERAL_GRAARDOR, BossTab.GENERAL_GRAARDOR, -1),
// Command Zilyana (Saradomin)
SARADOMIN_SWORD(ItemID.SARADOMIN_SWORD, BossTab.COMMANDER_ZILYANA, 0),
ARMADYL_CROSSBOW(ItemID.ARMADYL_CROSSBOW, BossTab.COMMANDER_ZILYANA, 0),
SARADOMINS_LIGHT(ItemID.SARADOMINS_LIGHT, BossTab.COMMANDER_ZILYANA, 0),
SARADOMIN_HILT(ItemID.SARADOMIN_HILT, BossTab.COMMANDER_ZILYANA, 0),
PET_ZILYANA(ItemID.PET_ZILYANA, BossTab.COMMANDER_ZILYANA, -1),
// Kril Tsutsaroth (Zammy)
STEAM_BATTLESTAFF(ItemID.STEAM_BATTLESTAFF, BossTab.KRIL_TSUTSAROTH, 0),
STAFF_OF_THE_DEAD(ItemID.STAFF_OF_THE_DEAD, BossTab.KRIL_TSUTSAROTH, 0),
ZAMORAKIAN_SPEAR(ItemID.ZAMORAKIAN_SPEAR, BossTab.KRIL_TSUTSAROTH, 0),
ZAMORAK_HILT(ItemID.ZAMORAK_HILT, BossTab.KRIL_TSUTSAROTH, 0),
PET_KRIL_TSUTSAROTH(ItemID.PET_KRIL_TSUTSAROTH, BossTab.KRIL_TSUTSAROTH, -1),
// Wildy Bosses
// Vetion
RING_OF_THE_GODS(ItemID.RING_OF_THE_GODS, BossTab.VETION, 0),
VETION_JR(ItemID.VETION_JR, BossTab.VETION, 0),
SKELETON_CHAMPION_SCROLL(ItemID.SKELETON_CHAMPION_SCROLL, BossTab.VETION, 0),
// Venenatis
TREASONOUS_RING(ItemID.TREASONOUS_RING, BossTab.VENENATIS, -1),
VENENATIS_SPIDERLING(ItemID.VENENATIS_SPIDERLING, BossTab.VENENATIS, -1),
// Callisto
TYRANNICAL_RING(ItemID.TYRANNICAL_RING, BossTab.CALLISTO, -1),
CALLISTO_CUB(ItemID.CALLISTO_CUB, BossTab.CALLISTO, -1),
// Chaos Elemental Uniques are all in Shared
// Chaos Fanatic
ODIUM_SHARD_1(ItemID.ODIUM_SHARD_1, BossTab.CHAOS_FANATIC, -1),
MALEDICTION_SHARD_1(ItemID.MALEDICTION_SHARD_1, BossTab.CHAOS_FANATIC, -1),
// Crazy Archaeologist
ODIUM_SHARD_2(ItemID.ODIUM_SHARD_2, BossTab.CRAZY_ARCHAEOLOGIST, 0),
MALEDICTION_SHARD_2(ItemID.MALEDICTION_SHARD_2, BossTab.CRAZY_ARCHAEOLOGIST, 0),
FEDORA(ItemID.FEDORA, BossTab.CRAZY_ARCHAEOLOGIST, 0),
// Scorpia
ODIUM_SHARD_3(ItemID.ODIUM_SHARD_3, BossTab.SCORPIA, 0),
MALEDICTION_SHARD_3(ItemID.MALEDICTION_SHARD_3, BossTab.SCORPIA, 0),
SCORPIAS_OFFSPRING(ItemID.SCORPIAS_OFFSPRING, BossTab.SCORPIA, 0),
// King Black Dragon,
KBD_HEADS(ItemID.KBD_HEADS, BossTab.KING_BLACK_DRAGON, -1),
DRACONIC_VISAGE(ItemID.DRACONIC_VISAGE, BossTab.KING_BLACK_DRAGON, -1),
PRINCE_BLACK_DRAGON(ItemID.PRINCE_BLACK_DRAGON, BossTab.KING_BLACK_DRAGON, -1),
// Slayer Bosses
// Skotizo
DARK_CLAW(ItemID.DARK_CLAW, BossTab.SKOTIZO, -1),
SKOTOS(ItemID.SKOTOS, BossTab.SKOTIZO, -1),
JAR_OF_DARKNESS(ItemID.JAR_OF_DARKNESS, BossTab.SKOTIZO, -1),
// Grotesque Guardians
GRANITE_GLOVES(ItemID.GRANITE_GLOVES, BossTab.GROTESQUE_GUARDIANS, 0),
GRANITE_RING(ItemID.GRANITE_RING, BossTab.GROTESQUE_GUARDIANS, 0),
GRANITE_HAMMER(ItemID.GRANITE_HAMMER, BossTab.GROTESQUE_GUARDIANS, 0),
BLACK_TOURMALINE_CORE(ItemID.BLACK_TOURMALINE_CORE, BossTab.GROTESQUE_GUARDIANS, 1),
NOON(ItemID.NOON, BossTab.GROTESQUE_GUARDIANS, 1),
JAR_OF_STONE(ItemID.JAR_OF_STONE, BossTab.GROTESQUE_GUARDIANS, 1),
// Abyssal Sire
UNSIRED(ItemID.UNSIRED, BossTab.ABYSSAL_SIRE, 0),
BLUDGEON_CLAW(ItemID.BLUDGEON_CLAW, BossTab.ABYSSAL_SIRE, 0),
BLUDGEON_SPINE(ItemID.BLUDGEON_SPINE, BossTab.ABYSSAL_SIRE, 0),
BLUDGEON_AXON(ItemID.BLUDGEON_AXON, BossTab.ABYSSAL_SIRE, 0),
ABYSSAL_DAGGER(ItemID.ABYSSAL_DAGGER, BossTab.ABYSSAL_SIRE, 1),
ABYSSAL_WHIP(ItemID.ABYSSAL_WHIP, BossTab.ABYSSAL_SIRE, 1),
ABYSSAL_ORPHAN(ItemID.ABYSSAL_ORPHAN, BossTab.ABYSSAL_SIRE, 1),
JAR_OF_MIASMA(ItemID.JAR_OF_MIASMA, BossTab.ABYSSAL_SIRE, 1),
ABYSSAL_HEAD(ItemID.ABYSSAL_HEAD, BossTab.ABYSSAL_SIRE, 1),
// Kraken
TRIDENT_OF_THE_SEAS_FULL(ItemID.TRIDENT_OF_THE_SEAS_FULL, BossTab.KRAKEN, 0),
KRAKEN_TENTACLE(ItemID.KRAKEN_TENTACLE, BossTab.KRAKEN, 0),
JAR_OF_DIRT(ItemID.JAR_OF_DIRT, BossTab.KRAKEN, 0),
PET_KRAKEN(ItemID.PET_KRAKEN, BossTab.KRAKEN, 0),
// Cerberus
PRIMORDIAL_CRYSTAL(ItemID.PRIMORDIAL_CRYSTAL, BossTab.CERBERUS, 0),
PEGASIAN_CRYSTAL(ItemID.PEGASIAN_CRYSTAL, BossTab.CERBERUS, 0),
ETERNAL_CRYSTAL(ItemID.ETERNAL_CRYSTAL, BossTab.CERBERUS, 0),
SMOULDERING_STONE(ItemID.SMOULDERING_STONE, BossTab.CERBERUS, 0),
JAR_OF_SOULS(ItemID.JAR_OF_SOULS, BossTab.CERBERUS, 0),
HELLPUPPY(ItemID.HELLPUPPY, BossTab.CERBERUS, 0),
// Thermonuclear Smoke Devil
SMOKE_BATTLESTAFF(ItemID.SMOKE_BATTLESTAFF, BossTab.THERMONUCLEAR_SMOKE_DEVIL, -1),
OCCULT_NECKLACE(ItemID.OCCULT_NECKLACE, BossTab.THERMONUCLEAR_SMOKE_DEVIL, -1),
PET_SMOKE_DEVIL(ItemID.PET_SMOKE_DEVIL, BossTab.THERMONUCLEAR_SMOKE_DEVIL, -1),
// Alchemical Hydra
HYDRAS_EYE(ItemID.HYDRAS_EYE, BossTab.ALCHEMICAL_HYDRA, 0),
HYDRAS_FANG(ItemID.HYDRAS_FANG, BossTab.ALCHEMICAL_HYDRA, 0),
HYDRAS_HEART(ItemID.HYDRAS_HEART, BossTab.ALCHEMICAL_HYDRA, 0),
HYDRA_TAIL(ItemID.HYDRA_TAIL, BossTab.ALCHEMICAL_HYDRA, 0),
HYDRA_LEATHER(ItemID.HYDRA_LEATHER, BossTab.ALCHEMICAL_HYDRA, 1),
HYDRAS_CLAW(ItemID.HYDRAS_CLAW, BossTab.ALCHEMICAL_HYDRA, 1),
DRAGON_THROWNAXE(ItemID.DRAGON_THROWNAXE, BossTab.ALCHEMICAL_HYDRA, 1),
DRAGON_KNIFE(ItemID.DRAGON_KNIFE, BossTab.ALCHEMICAL_HYDRA, 1),
IKKLE_HYDRA(ItemID.IKKLE_HYDRA, BossTab.ALCHEMICAL_HYDRA, 1),
// Other Bosses
// Giant Mole
BABY_MOLE(ItemID.BABY_MOLE, BossTab.GIANT_MOLE, -1),
// Kalphite Queen
KQ_HEAD(ItemID.KQ_HEAD, BossTab.KALPHITE_QUEEN, -1),
JAR_OF_SAND(ItemID.JAR_OF_SAND, BossTab.KALPHITE_QUEEN, -1),
KALPHITE_PRINCESS(ItemID.KALPHITE_PRINCESS, BossTab.KALPHITE_QUEEN, -1),
// Corporeal Beast
SPIRIT_SHIELD(ItemID.SPIRIT_SHIELD, BossTab.CORPOREAL_BEAST, 0),
HOLY_ELIXIR(ItemID.HOLY_ELIXIR, BossTab.CORPOREAL_BEAST, 0),
PET_DARK_CORE(ItemID.PET_DARK_CORE, BossTab.CORPOREAL_BEAST, 0),
SPECTRAL_SIGIL(ItemID.SPECTRAL_SIGIL, BossTab.CORPOREAL_BEAST, 1),
ARCANE_SIGIL(ItemID.ARCANE_SIGIL, BossTab.CORPOREAL_BEAST, 1),
ELYSIAN_SIGIL(ItemID.ELYSIAN_SIGIL, BossTab.CORPOREAL_BEAST, 1),
// Dagannoth Rex
RING_OF_LIFE(ItemID.RING_OF_LIFE, BossTab.DAGANNOTH_REX, -1),
WARRIOR_RING(ItemID.WARRIOR_RING, BossTab.DAGANNOTH_REX, -1),
BERSERKER_RING(ItemID.BERSERKER_RING, BossTab.DAGANNOTH_REX, -1),
PET_DAGANNOTH_REX(ItemID.PET_DAGANNOTH_REX, BossTab.DAGANNOTH_REX, -1),
// Dagannoth Prime
MUD_BATTLESTAFF(ItemID.MUD_BATTLESTAFF, BossTab.DAGANNOTH_PRIME, -1),
SEERS_RING(ItemID.SEERS_RING, BossTab.DAGANNOTH_PRIME, -1),
PET_DAGANNOTH_PRIME(ItemID.PET_DAGANNOTH_PRIME, BossTab.DAGANNOTH_PRIME, -1),
// Dagannoth Supreme
SEERCULL(ItemID.SEERCULL, BossTab.DAGANNOTH_SUPREME, -1),
ARCHERS_RING(ItemID.ARCHERS_RING, BossTab.DAGANNOTH_SUPREME, -1),
PET_DAGANNOTH_SUPREME(ItemID.PET_DAGANNOTH_SUPREME, BossTab.DAGANNOTH_SUPREME, -1),
// Beginner Clue Scrolls
BEAR_FEET(ItemID.BEAR_FEET, BossTab.CLUE_SCROLL_BEGINNER, -1),
FROG_SLIPPERS(ItemID.FROG_SLIPPERS, BossTab.CLUE_SCROLL_BEGINNER, -1),
DEMON_FEET(ItemID.DEMON_FEET, BossTab.CLUE_SCROLL_BEGINNER, -1),
MOLE_SLIPPERS(ItemID.MOLE_SLIPPERS, BossTab.CLUE_SCROLL_BEGINNER, -1),
SANDWICH_LADY_HAT(ItemID.SANDWICH_LADY_HAT, BossTab.CLUE_SCROLL_BEGINNER, 0),
SANDWICH_LADY_TOP(ItemID.SANDWICH_LADY_TOP, BossTab.CLUE_SCROLL_BEGINNER, 0),
SANDWICH_LADY_BOTTOM(ItemID.SANDWICH_LADY_BOTTOM, BossTab.CLUE_SCROLL_BEGINNER, 0),
JESTER_CAPE(ItemID.JESTER_CAPE, BossTab.CLUE_SCROLL_BEGINNER, 1),
SHOULDER_PARROT(ItemID.SHOULDER_PARROT, BossTab.CLUE_SCROLL_BEGINNER, 1),
AMULET_OF_DEFENCE_T(ItemID.AMULET_OF_DEFENCE_T, BossTab.CLUE_SCROLL_BEGINNER, 1),
MONKS_ROBE_TOP_T(ItemID.MONKS_ROBE_TOP_T, BossTab.CLUE_SCROLL_BEGINNER, 2),
MONKS_ROBE_T(ItemID.MONKS_ROBE_T, BossTab.CLUE_SCROLL_BEGINNER, 2),
RUNE_SCIMITAR_ORNAMENT_KIT_GUTHIX(ItemID.RUNE_SCIMITAR_ORNAMENT_KIT_GUTHIX, BossTab.CLUE_SCROLL_BEGINNER, 2),
RUNE_SCIMITAR_ORNAMENT_KIT_SARADOMIN(ItemID.RUNE_SCIMITAR_ORNAMENT_KIT_SARADOMIN, BossTab.CLUE_SCROLL_BEGINNER, 2),
RUNE_SCIMITAR_ORNAMENT_KIT_ZAMORAK(ItemID.RUNE_SCIMITAR_ORNAMENT_KIT_ZAMORAK, BossTab.CLUE_SCROLL_BEGINNER, 2),
// Easy Clue Scrolls
BRONZE_FULL_HELM_T(ItemID.BRONZE_FULL_HELM_T, BossTab.CLUE_SCROLL_EASY, -1),
BRONZE_PLATEBODY_T(ItemID.BRONZE_PLATEBODY_T, BossTab.CLUE_SCROLL_EASY, -1),
BRONZE_PLATELEGS_T(ItemID.BRONZE_PLATELEGS_T, BossTab.CLUE_SCROLL_EASY, -1),
BRONZE_PLATESKIRT_T(ItemID.BRONZE_PLATESKIRT_T, BossTab.CLUE_SCROLL_EASY, -1),
BRONZE_KITESHIELD_T(ItemID.BRONZE_KITESHIELD_T, BossTab.CLUE_SCROLL_EASY, -1),
BRONZE_FULL_HELM_G(ItemID.BRONZE_FULL_HELM_G, BossTab.CLUE_SCROLL_EASY, 0),
BRONZE_PLATEBODY_G(ItemID.BRONZE_PLATEBODY_G, BossTab.CLUE_SCROLL_EASY, 0),
BRONZE_PLATELEGS_G(ItemID.BRONZE_PLATELEGS_G, BossTab.CLUE_SCROLL_EASY, 0),
BRONZE_PLATESKIRT_G(ItemID.BRONZE_PLATESKIRT_G, BossTab.CLUE_SCROLL_EASY, 0),
BRONZE_KITESHIELD_G(ItemID.BRONZE_KITESHIELD_G, BossTab.CLUE_SCROLL_EASY, 0),
IRON_FULL_HELM_T(ItemID.IRON_FULL_HELM_T, BossTab.CLUE_SCROLL_EASY, 1),
IRON_PLATEBODY_T(ItemID.IRON_PLATEBODY_T, BossTab.CLUE_SCROLL_EASY, 1),
IRON_PLATELEGS_T(ItemID.IRON_PLATELEGS_T, BossTab.CLUE_SCROLL_EASY, 1),
IRON_PLATESKIRT_T(ItemID.IRON_PLATESKIRT_T, BossTab.CLUE_SCROLL_EASY, 1),
IRON_KITESHIELD_T(ItemID.IRON_KITESHIELD_T, BossTab.CLUE_SCROLL_EASY, 1),
IRON_FULL_HELM_G(ItemID.IRON_FULL_HELM_G, BossTab.CLUE_SCROLL_EASY, 2),
IRON_PLATEBODY_G(ItemID.IRON_PLATEBODY_G, BossTab.CLUE_SCROLL_EASY, 2),
IRON_PLATELEGS_G(ItemID.IRON_PLATELEGS_G, BossTab.CLUE_SCROLL_EASY, 2),
IRON_PLATESKIRT_G(ItemID.IRON_PLATESKIRT_G, BossTab.CLUE_SCROLL_EASY, 2),
IRON_KITESHIELD_G(ItemID.IRON_KITESHIELD_G, BossTab.CLUE_SCROLL_EASY, 2),
STEEL_FULL_HELM_T(ItemID.STEEL_FULL_HELM_T, BossTab.CLUE_SCROLL_EASY, 3),
STEEL_PLATEBODY_T(ItemID.STEEL_PLATEBODY_T, BossTab.CLUE_SCROLL_EASY, 3),
STEEL_PLATELEGS_T(ItemID.STEEL_PLATELEGS_T, BossTab.CLUE_SCROLL_EASY, 3),
STEEL_PLATESKIRT_T(ItemID.STEEL_PLATESKIRT_T, BossTab.CLUE_SCROLL_EASY, 3),
STEEL_KITESHIELD_T(ItemID.STEEL_KITESHIELD_T, BossTab.CLUE_SCROLL_EASY, 3),
STEEL_FULL_HELM_G(ItemID.STEEL_FULL_HELM_G, BossTab.CLUE_SCROLL_EASY, 4),
STEEL_PLATEBODY_G(ItemID.STEEL_PLATEBODY_G, BossTab.CLUE_SCROLL_EASY, 4),
STEEL_PLATELEGS_G(ItemID.STEEL_PLATELEGS_G, BossTab.CLUE_SCROLL_EASY, 4),
STEEL_PLATESKIRT_G(ItemID.STEEL_PLATESKIRT_G, BossTab.CLUE_SCROLL_EASY, 4),
STEEL_KITESHIELD_G(ItemID.STEEL_KITESHIELD_G, BossTab.CLUE_SCROLL_EASY, 4),
BLACK_FULL_HELM_T(ItemID.BLACK_FULL_HELM_T, BossTab.CLUE_SCROLL_EASY, 5),
BLACK_PLATEBODY_T(ItemID.BLACK_PLATEBODY_T, BossTab.CLUE_SCROLL_EASY, 5),
BLACK_PLATELEGS_T(ItemID.BLACK_PLATELEGS_T, BossTab.CLUE_SCROLL_EASY, 5),
BLACK_PLATESKIRT_T(ItemID.BLACK_PLATESKIRT_T, BossTab.CLUE_SCROLL_EASY, 5),
BLACK_KITESHIELD_T(ItemID.BLACK_KITESHIELD_T, BossTab.CLUE_SCROLL_EASY, 5),
BLACK_FULL_HELM_G(ItemID.BLACK_FULL_HELM_G, BossTab.CLUE_SCROLL_EASY, 6),
BLACK_PLATEBODY_G(ItemID.BLACK_PLATEBODY_G, BossTab.CLUE_SCROLL_EASY, 6),
BLACK_PLATELEGS_G(ItemID.BLACK_PLATELEGS_G, BossTab.CLUE_SCROLL_EASY, 6),
BLACK_PLATESKIRT_G(ItemID.BLACK_PLATESKIRT_G, BossTab.CLUE_SCROLL_EASY, 6),
BLACK_KITESHIELD_G(ItemID.BLACK_KITESHIELD_G, BossTab.CLUE_SCROLL_EASY, 6),
BLUE_WIZARD_HAT_T(ItemID.BLUE_WIZARD_HAT_T, BossTab.CLUE_SCROLL_EASY, 7),
BLUE_WIZARD_ROBE_T(ItemID.BLUE_WIZARD_ROBE_T, BossTab.CLUE_SCROLL_EASY, 7),
BLUE_SKIRT_T(ItemID.BLUE_SKIRT_T, BossTab.CLUE_SCROLL_EASY, 7),
BLUE_WIZARD_HAT_G(ItemID.BLUE_WIZARD_HAT_G, BossTab.CLUE_SCROLL_EASY, 8),
BLUE_WIZARD_ROBE_G(ItemID.BLUE_WIZARD_ROBE_G, BossTab.CLUE_SCROLL_EASY, 8),
BLUE_SKIRT_G(ItemID.BLUE_SKIRT_G, BossTab.CLUE_SCROLL_EASY, 8),
BLACK_WIZARD_HAT_T(ItemID.BLACK_WIZARD_HAT_T, BossTab.CLUE_SCROLL_EASY, 9),
BLACK_WIZARD_ROBE_T(ItemID.BLACK_WIZARD_ROBE_T, BossTab.CLUE_SCROLL_EASY, 9),
BLACK_SKIRT_T(ItemID.BLACK_SKIRT_T, BossTab.CLUE_SCROLL_EASY, 9),
BLACK_WIZARD_HAT_G(ItemID.BLACK_WIZARD_HAT_G, BossTab.CLUE_SCROLL_EASY, 10),
BLACK_WIZARD_ROBE_G(ItemID.BLACK_WIZARD_ROBE_G, BossTab.CLUE_SCROLL_EASY, 10),
BLACK_SKIRT_G(ItemID.BLACK_SKIRT_G, BossTab.CLUE_SCROLL_EASY, 10),
LEATHER_BODY_G(ItemID.LEATHER_BODY_G, BossTab.CLUE_SCROLL_EASY, 11),
LEATHER_CHAPS_G(ItemID.LEATHER_CHAPS_G, BossTab.CLUE_SCROLL_EASY, 11),
STUDDED_BODY_T(ItemID.STUDDED_BODY_T, BossTab.CLUE_SCROLL_EASY, 11),
STUDDED_CHAPS_T(ItemID.STUDDED_CHAPS_T, BossTab.CLUE_SCROLL_EASY, 11),
STUDDED_BODY_G(ItemID.STUDDED_BODY_G, BossTab.CLUE_SCROLL_EASY, 11),
STUDDED_CHAPS_G(ItemID.STUDDED_CHAPS_G, BossTab.CLUE_SCROLL_EASY, 11),
BLACK_HELM_H1(ItemID.BLACK_HELM_H1, BossTab.CLUE_SCROLL_EASY, 12),
BLACK_HELM_H2(ItemID.BLACK_HELM_H2, BossTab.CLUE_SCROLL_EASY, 12),
BLACK_HELM_H3(ItemID.BLACK_HELM_H3, BossTab.CLUE_SCROLL_EASY, 12),
BLACK_HELM_H4(ItemID.BLACK_HELM_H4, BossTab.CLUE_SCROLL_EASY, 12),
BLACK_HELM_H5(ItemID.BLACK_HELM_H5, BossTab.CLUE_SCROLL_EASY, 12),
BLACK_SHIELD_H1(ItemID.BLACK_SHIELD_H1, BossTab.CLUE_SCROLL_EASY, 13),
BLACK_SHIELD_H2(ItemID.BLACK_SHIELD_H2, BossTab.CLUE_SCROLL_EASY, 13),
BLACK_SHIELD_H3(ItemID.BLACK_SHIELD_H3, BossTab.CLUE_SCROLL_EASY, 13),
BLACK_SHIELD_H4(ItemID.BLACK_SHIELD_H4, BossTab.CLUE_SCROLL_EASY, 13),
BLACK_SHIELD_H5(ItemID.BLACK_SHIELD_H5, BossTab.CLUE_SCROLL_EASY, 13),
BLUE_ELEGANT_SHIRT(ItemID.BLUE_ELEGANT_SHIRT, BossTab.CLUE_SCROLL_EASY, 14),
BLUE_ELEGANT_LEGS(ItemID.BLUE_ELEGANT_LEGS, BossTab.CLUE_SCROLL_EASY, 14),
BLUE_ELEGANT_BLOUSE(ItemID.BLUE_ELEGANT_BLOUSE, BossTab.CLUE_SCROLL_EASY, 14),
BLUE_ELEGANT_SKIRT(ItemID.BLUE_ELEGANT_SKIRT, BossTab.CLUE_SCROLL_EASY, 14),
GREEN_ELEGANT_SHIRT(ItemID.GREEN_ELEGANT_SHIRT, BossTab.CLUE_SCROLL_EASY, 15),
GREEN_ELEGANT_LEGS(ItemID.GREEN_ELEGANT_LEGS, BossTab.CLUE_SCROLL_EASY, 15),
GREEN_ELEGANT_BLOUSE(ItemID.GREEN_ELEGANT_BLOUSE, BossTab.CLUE_SCROLL_EASY, 15),
GREEN_ELEGANT_SKIRT(ItemID.GREEN_ELEGANT_SKIRT, BossTab.CLUE_SCROLL_EASY, 15),
RED_ELEGANT_SHIRT(ItemID.RED_ELEGANT_SHIRT, BossTab.CLUE_SCROLL_EASY, 16),
RED_ELEGANT_LEGS(ItemID.RED_ELEGANT_LEGS, BossTab.CLUE_SCROLL_EASY, 16),
RED_ELEGANT_BLOUSE(ItemID.RED_ELEGANT_BLOUSE, BossTab.CLUE_SCROLL_EASY, 16),
RED_ELEGANT_SKIRT(ItemID.RED_ELEGANT_SKIRT, BossTab.CLUE_SCROLL_EASY, 16),
BOBS_RED_SHIRT(ItemID.BOBS_RED_SHIRT, BossTab.CLUE_SCROLL_EASY, 17),
BOBS_BLUE_SHIRT(ItemID.BOBS_BLUE_SHIRT, BossTab.CLUE_SCROLL_EASY, 17),
BOBS_GREEN_SHIRT(ItemID.BOBS_GREEN_SHIRT, BossTab.CLUE_SCROLL_EASY, 17),
BOBS_BLACK_SHIRT(ItemID.BOBS_BLACK_SHIRT, BossTab.CLUE_SCROLL_EASY, 17),
BOBS_PURPLE_SHIRT(ItemID.BOBS_PURPLE_SHIRT, BossTab.CLUE_SCROLL_EASY, 17),
STAFF_OF_BOB_THE_CAT(ItemID.STAFF_OF_BOB_THE_CAT, BossTab.CLUE_SCROLL_EASY, 17),
A_POWDERED_WIG(ItemID.A_POWDERED_WIG, BossTab.CLUE_SCROLL_EASY, 18),
FLARED_TROUSERS(ItemID.FLARED_TROUSERS, BossTab.CLUE_SCROLL_EASY, 18),
PANTALOONS(ItemID.PANTALOONS, BossTab.CLUE_SCROLL_EASY, 18),
SLEEPING_CAP(ItemID.SLEEPING_CAP, BossTab.CLUE_SCROLL_EASY, 18),
GUTHIX_ROBE_TOP(ItemID.GUTHIX_ROBE_TOP, BossTab.CLUE_SCROLL_EASY, 19),
GUTHIX_ROBE_LEGS(ItemID.GUTHIX_ROBE_LEGS, BossTab.CLUE_SCROLL_EASY, 19),
SARADOMIN_ROBE_TOP(ItemID.SARADOMIN_ROBE_TOP, BossTab.CLUE_SCROLL_EASY, 19),
SARADOMIN_ROBE_LEGS(ItemID.SARADOMIN_ROBE_LEGS, BossTab.CLUE_SCROLL_EASY, 19),
ZAMORAK_ROBE_TOP(ItemID.ZAMORAK_ROBE_TOP, BossTab.CLUE_SCROLL_EASY, 19),
ZAMORAK_ROBE_LEGS(ItemID.ZAMORAK_ROBE_LEGS, BossTab.CLUE_SCROLL_EASY, 19),
ANCIENT_ROBE_TOP(ItemID.ANCIENT_ROBE_TOP, BossTab.CLUE_SCROLL_EASY, 20),
ANCIENT_ROBE_LEGS(ItemID.ANCIENT_ROBE_LEGS, BossTab.CLUE_SCROLL_EASY, 20),
BANDOS_ROBE_TOP(ItemID.BANDOS_ROBE_TOP, BossTab.CLUE_SCROLL_EASY, 20),
BANDOS_ROBE_LEGS(ItemID.BANDOS_ROBE_LEGS, BossTab.CLUE_SCROLL_EASY, 20),
ARMADYL_ROBE_TOP(ItemID.ARMADYL_ROBE_TOP, BossTab.CLUE_SCROLL_EASY, 20),
ARMADYL_ROBE_LEGS(ItemID.ARMADYL_ROBE_LEGS, BossTab.CLUE_SCROLL_EASY, 20),
BLACK_BERET(ItemID.BLACK_BERET, BossTab.CLUE_SCROLL_EASY, 21),
BLUE_BERET(ItemID.BLUE_BERET, BossTab.CLUE_SCROLL_EASY, 21),
WHITE_BERET(ItemID.WHITE_BERET, BossTab.CLUE_SCROLL_EASY, 21),
RED_BERET(ItemID.RED_BERET, BossTab.CLUE_SCROLL_EASY, 21),
HIGHWAYMAN_MASK(ItemID.HIGHWAYMAN_MASK, BossTab.CLUE_SCROLL_EASY, 22),
IMP_MASK(ItemID.IMP_MASK, BossTab.CLUE_SCROLL_EASY, 22),
GOBLIN_MASK(ItemID.GOBLIN_MASK, BossTab.CLUE_SCROLL_EASY, 22),
BEANIE(ItemID.BEANIE, BossTab.CLUE_SCROLL_EASY, 22),
TEAM_CAPE_I(ItemID.TEAM_CAPE_I, BossTab.CLUE_SCROLL_EASY, 23),
TEAM_CAPE_X(ItemID.TEAM_CAPE_X, BossTab.CLUE_SCROLL_EASY, 23),
TEAM_CAPE_ZERO(ItemID.TEAM_CAPE_ZERO, BossTab.CLUE_SCROLL_EASY, 23),
CAPE_OF_SKULLS(ItemID.CAPE_OF_SKULLS, BossTab.CLUE_SCROLL_EASY, 23),
AMULET_OF_MAGIC_T(ItemID.AMULET_OF_MAGIC_T, BossTab.CLUE_SCROLL_EASY, 24),
AMULET_OF_POWER_T(ItemID.AMULET_OF_POWER_T, BossTab.CLUE_SCROLL_EASY, 24),
BLACK_CANE(ItemID.BLACK_CANE, BossTab.CLUE_SCROLL_EASY, 24),
BLACK_PICKAXE(ItemID.BLACK_PICKAXE, BossTab.CLUE_SCROLL_EASY, 24),
LARGE_SPADE(ItemID.LARGE_SPADE, BossTab.CLUE_SCROLL_EASY, 24),
RAIN_BOW(ItemID.RAIN_BOW, BossTab.CLUE_SCROLL_EASY, 24),
HAM_JOINT(ItemID.HAM_JOINT, BossTab.CLUE_SCROLL_EASY, 24),
WOODEN_SHIELD_G(ItemID.WOODEN_SHIELD_G, BossTab.CLUE_SCROLL_EASY, 25),
GOLDEN_CHEFS_HAT(ItemID.GOLDEN_CHEFS_HAT, BossTab.CLUE_SCROLL_EASY, 25),
GOLDEN_APRON(ItemID.GOLDEN_APRON, BossTab.CLUE_SCROLL_EASY, 25),
MONKS_ROBE_TOP_G(ItemID.MONKS_ROBE_TOP_G, BossTab.CLUE_SCROLL_EASY, 25),
MONKS_ROBE_G(ItemID.MONKS_ROBE_G, BossTab.CLUE_SCROLL_EASY, 25),
BLACK_PLATEBODY_H1(ItemID.BLACK_PLATEBODY_H1, BossTab.CLUE_SCROLL_EASY, 26),
BLACK_PLATEBODY_H2(ItemID.BLACK_PLATEBODY_H2, BossTab.CLUE_SCROLL_EASY, 26),
BLACK_PLATEBODY_H3(ItemID.BLACK_PLATEBODY_H3, BossTab.CLUE_SCROLL_EASY, 26),
BLACK_PLATEBODY_H4(ItemID.BLACK_PLATEBODY_H4, BossTab.CLUE_SCROLL_EASY, 26),
BLACK_PLATEBODY_H5(ItemID.BLACK_PLATEBODY_H5, BossTab.CLUE_SCROLL_EASY, 26),
// Medium Clue Scrolls
MITHRIL_FULL_HELM_T(ItemID.MITHRIL_FULL_HELM_T, BossTab.CLUE_SCROLL_MEDIUM, -1),
MITHRIL_PLATEBODY_T(ItemID.MITHRIL_PLATEBODY_T, BossTab.CLUE_SCROLL_MEDIUM, -1),
MITHRIL_PLATELEGS_T(ItemID.MITHRIL_PLATELEGS_T, BossTab.CLUE_SCROLL_MEDIUM, -1),
MITHRIL_PLATESKIRT_T(ItemID.MITHRIL_PLATESKIRT_T, BossTab.CLUE_SCROLL_MEDIUM, -1),
MITHRIL_KITESHIELD_T(ItemID.MITHRIL_KITESHIELD_T, BossTab.CLUE_SCROLL_MEDIUM, -1),
MITHRIL_FULL_HELM_G(ItemID.MITHRIL_FULL_HELM_G, BossTab.CLUE_SCROLL_MEDIUM, 0),
MITHRIL_PLATEBODY_G(ItemID.MITHRIL_PLATEBODY_G, BossTab.CLUE_SCROLL_MEDIUM, 0),
MITHRIL_PLATELEGS_G(ItemID.MITHRIL_PLATELEGS_G, BossTab.CLUE_SCROLL_MEDIUM, 0),
MITHRIL_PLATESKIRT_G(ItemID.MITHRIL_PLATESKIRT_G, BossTab.CLUE_SCROLL_MEDIUM, 0),
MITHRIL_KITESHIELD_G(ItemID.MITHRIL_KITESHIELD_G, BossTab.CLUE_SCROLL_MEDIUM, 0),
ADAMANT_FULL_HELM_T(ItemID.ADAMANT_FULL_HELM_T, BossTab.CLUE_SCROLL_MEDIUM, 1),
ADAMANT_PLATEBODY_T(ItemID.ADAMANT_PLATEBODY_T, BossTab.CLUE_SCROLL_MEDIUM, 1),
ADAMANT_PLATELEGS_T(ItemID.ADAMANT_PLATELEGS_T, BossTab.CLUE_SCROLL_MEDIUM, 1),
ADAMANT_PLATESKIRT_T(ItemID.ADAMANT_PLATESKIRT_T, BossTab.CLUE_SCROLL_MEDIUM, 1),
ADAMANT_KITESHIELD_T(ItemID.ADAMANT_KITESHIELD_T, BossTab.CLUE_SCROLL_MEDIUM, 1),
ADAMANT_FULL_HELM_G(ItemID.ADAMANT_FULL_HELM_G, BossTab.CLUE_SCROLL_MEDIUM, 2),
ADAMANT_PLATEBODY_G(ItemID.ADAMANT_PLATEBODY_G, BossTab.CLUE_SCROLL_MEDIUM, 2),
ADAMANT_PLATELEGS_G(ItemID.ADAMANT_PLATELEGS_G, BossTab.CLUE_SCROLL_MEDIUM, 2),
ADAMANT_PLATESKIRT_G(ItemID.ADAMANT_PLATESKIRT_G, BossTab.CLUE_SCROLL_MEDIUM, 2),
ADAMANT_KITESHIELD_G(ItemID.ADAMANT_KITESHIELD_G, BossTab.CLUE_SCROLL_MEDIUM, 2),
RANGER_BOOTS(ItemID.RANGER_BOOTS, BossTab.CLUE_SCROLL_MEDIUM, 3),
HOLY_SANDALS(ItemID.HOLY_SANDALS, BossTab.CLUE_SCROLL_MEDIUM, 3),
WIZARD_BOOTS(ItemID.WIZARD_BOOTS, BossTab.CLUE_SCROLL_MEDIUM, 3),
CLIMBING_BOOTS_G(ItemID.CLIMBING_BOOTS_G, BossTab.CLUE_SCROLL_MEDIUM, 3),
RED_HEADBAND(ItemID.RED_HEADBAND, BossTab.CLUE_SCROLL_MEDIUM, 4),
BLACK_HEADBAND(ItemID.BLACK_HEADBAND, BossTab.CLUE_SCROLL_MEDIUM, 4),
BROWN_HEADBAND(ItemID.BROWN_HEADBAND, BossTab.CLUE_SCROLL_MEDIUM, 4),
PINK_HEADBAND(ItemID.PINK_HEADBAND, BossTab.CLUE_SCROLL_MEDIUM, 4),
GREEN_HEADBAND(ItemID.GREEN_HEADBAND, BossTab.CLUE_SCROLL_MEDIUM, 5),
BLUE_HEADBAND(ItemID.BLUE_HEADBAND, BossTab.CLUE_SCROLL_MEDIUM, 5),
GOLD_HEADBAND(ItemID.GOLD_HEADBAND, BossTab.CLUE_SCROLL_MEDIUM, 5),
WHITE_HEADBAND(ItemID.WHITE_HEADBAND, BossTab.CLUE_SCROLL_MEDIUM, 5),
RED_BOATER(ItemID.RED_BOATER, BossTab.CLUE_SCROLL_MEDIUM, 6),
ORANGE_BOATER(ItemID.ORANGE_BOATER, BossTab.CLUE_SCROLL_MEDIUM, 6),
GREEN_BOATER(ItemID.GREEN_BOATER, BossTab.CLUE_SCROLL_MEDIUM, 6),
BLUE_BOATER(ItemID.BLUE_BOATER, BossTab.CLUE_SCROLL_MEDIUM, 6),
BLACK_BOATER(ItemID.BLACK_BOATER, BossTab.CLUE_SCROLL_MEDIUM, 7),
PINK_BOATER(ItemID.PINK_BOATER, BossTab.CLUE_SCROLL_MEDIUM, 7),
PURPLE_BOATER(ItemID.PURPLE_BOATER, BossTab.CLUE_SCROLL_MEDIUM, 7),
WHITE_BOATER(ItemID.WHITE_BOATER, BossTab.CLUE_SCROLL_MEDIUM, 7),
ADAMANT_HELM_H1(ItemID.ADAMANT_HELM_H1, BossTab.CLUE_SCROLL_MEDIUM, 8),
ADAMANT_HELM_H2(ItemID.ADAMANT_HELM_H2, BossTab.CLUE_SCROLL_MEDIUM, 8),
ADAMANT_HELM_H3(ItemID.ADAMANT_HELM_H3, BossTab.CLUE_SCROLL_MEDIUM, 8),
ADAMANT_HELM_H4(ItemID.ADAMANT_HELM_H4, BossTab.CLUE_SCROLL_MEDIUM, 8),
ADAMANT_HELM_H5(ItemID.ADAMANT_HELM_H5, BossTab.CLUE_SCROLL_MEDIUM, 8),
ADAMANT_SHIELD_H1(ItemID.ADAMANT_SHIELD_H1, BossTab.CLUE_SCROLL_MEDIUM, 9),
ADAMANT_SHIELD_H2(ItemID.ADAMANT_SHIELD_H2, BossTab.CLUE_SCROLL_MEDIUM, 9),
ADAMANT_SHIELD_H3(ItemID.ADAMANT_SHIELD_H3, BossTab.CLUE_SCROLL_MEDIUM, 9),
ADAMANT_SHIELD_H4(ItemID.ADAMANT_SHIELD_H4, BossTab.CLUE_SCROLL_MEDIUM, 9),
ADAMANT_SHIELD_H5(ItemID.ADAMANT_SHIELD_H5, BossTab.CLUE_SCROLL_MEDIUM, 9),
GREEN_DHIDE_BODY_T(ItemID.GREEN_DHIDE_BODY_T, BossTab.CLUE_SCROLL_MEDIUM, 10),
GREEN_DHIDE_CHAPS_T(ItemID.GREEN_DHIDE_CHAPS_T, BossTab.CLUE_SCROLL_MEDIUM, 10),
GREEN_DHIDE_BODY_G(ItemID.GREEN_DHIDE_BODY_G, BossTab.CLUE_SCROLL_MEDIUM, 10),
GREEN_DHIDE_CHAPS_G(ItemID.GREEN_DHIDE_CHAPS_G, BossTab.CLUE_SCROLL_MEDIUM, 10),
BLACK_ELEGANT_SHIRT(ItemID.BLACK_ELEGANT_SHIRT, BossTab.CLUE_SCROLL_MEDIUM, 11),
BLACK_ELEGANT_LEGS(ItemID.BLACK_ELEGANT_LEGS, BossTab.CLUE_SCROLL_MEDIUM, 11),
WHITE_ELEGANT_BLOUSE(ItemID.WHITE_ELEGANT_BLOUSE, BossTab.CLUE_SCROLL_MEDIUM, 11),
WHITE_ELEGANT_SKIRT(ItemID.WHITE_ELEGANT_SKIRT, BossTab.CLUE_SCROLL_MEDIUM, 11),
PURPLE_ELEGANT_SHIRT(ItemID.PURPLE_ELEGANT_SHIRT, BossTab.CLUE_SCROLL_MEDIUM, 12),
PURPLE_ELEGANT_LEGS(ItemID.PURPLE_ELEGANT_LEGS, BossTab.CLUE_SCROLL_MEDIUM, 12),
PURPLE_ELEGANT_BLOUSE(ItemID.PURPLE_ELEGANT_BLOUSE, BossTab.CLUE_SCROLL_MEDIUM, 12),
PURPLE_ELEGANT_SKIRT(ItemID.PURPLE_ELEGANT_SKIRT, BossTab.CLUE_SCROLL_MEDIUM, 12),
PINK_ELEGANT_SHIRT(ItemID.PINK_ELEGANT_SHIRT, BossTab.CLUE_SCROLL_MEDIUM, 13),
PINK_ELEGANT_LEGS(ItemID.PINK_ELEGANT_LEGS, BossTab.CLUE_SCROLL_MEDIUM, 13),
PINK_ELEGANT_BLOUSE(ItemID.PINK_ELEGANT_BLOUSE, BossTab.CLUE_SCROLL_MEDIUM, 13),
PINK_ELEGANT_SKIRT(ItemID.PINK_ELEGANT_SKIRT, BossTab.CLUE_SCROLL_MEDIUM, 13),
GOLD_ELEGANT_SHIRT(ItemID.GOLD_ELEGANT_SHIRT, BossTab.CLUE_SCROLL_MEDIUM, 14),
GOLD_ELEGANT_LEGS(ItemID.GOLD_ELEGANT_LEGS, BossTab.CLUE_SCROLL_MEDIUM, 14),
GOLD_ELEGANT_BLOUSE(ItemID.GOLD_ELEGANT_BLOUSE, BossTab.CLUE_SCROLL_MEDIUM, 14),
GOLD_ELEGANT_SKIRT(ItemID.GOLD_ELEGANT_SKIRT, BossTab.CLUE_SCROLL_MEDIUM, 14),
GUTHIX_MITRE(ItemID.GUTHIX_MITRE, BossTab.CLUE_SCROLL_MEDIUM, 15),
GUTHIX_CLOAK(ItemID.GUTHIX_CLOAK, BossTab.CLUE_SCROLL_MEDIUM, 15),
SARADOMIN_MITRE(ItemID.SARADOMIN_MITRE, BossTab.CLUE_SCROLL_MEDIUM, 15),
SARADOMIN_CLOAK(ItemID.SARADOMIN_CLOAK, BossTab.CLUE_SCROLL_MEDIUM, 15),
ZAMORAK_MITRE(ItemID.ZAMORAK_MITRE, BossTab.CLUE_SCROLL_MEDIUM, 15),
ZAMORAK_CLOAK(ItemID.ZAMORAK_CLOAK, BossTab.CLUE_SCROLL_MEDIUM, 15),
ANCIENT_MITRE(ItemID.ANCIENT_MITRE, BossTab.CLUE_SCROLL_MEDIUM, 16),
ANCIENT_CLOAK(ItemID.ANCIENT_CLOAK, BossTab.CLUE_SCROLL_MEDIUM, 16),
BANDOS_MITRE(ItemID.BANDOS_MITRE, BossTab.CLUE_SCROLL_MEDIUM, 16),
BANDOS_CLOAK(ItemID.BANDOS_CLOAK, BossTab.CLUE_SCROLL_MEDIUM, 16),
ARMADYL_MITRE(ItemID.ARMADYL_MITRE, BossTab.CLUE_SCROLL_MEDIUM, 16),
ARMADYL_CLOAK(ItemID.ARMADYL_CLOAK, BossTab.CLUE_SCROLL_MEDIUM, 16),
ARMADYL_STOLE(ItemID.ARMADYL_STOLE, BossTab.CLUE_SCROLL_MEDIUM, 17),
ARMADYL_CROZIER(ItemID.ARMADYL_CROZIER, BossTab.CLUE_SCROLL_MEDIUM, 17),
ANCIENT_STOLE(ItemID.ANCIENT_STOLE, BossTab.CLUE_SCROLL_MEDIUM, 17),
ANCIENT_CROZIER(ItemID.ANCIENT_CROZIER, BossTab.CLUE_SCROLL_MEDIUM, 17),
BANDOS_STOLE(ItemID.BANDOS_STOLE, BossTab.CLUE_SCROLL_MEDIUM, 17),
BANDOS_CROZIER(ItemID.BANDOS_CROZIER, BossTab.CLUE_SCROLL_MEDIUM, 17),
CAT_MASK(ItemID.CAT_MASK, BossTab.CLUE_SCROLL_MEDIUM, 18),
PENGUIN_MASK(ItemID.PENGUIN_MASK, BossTab.CLUE_SCROLL_MEDIUM, 18),
BLACK_UNICORN_MASK(ItemID.BLACK_UNICORN_MASK, BossTab.CLUE_SCROLL_MEDIUM, 18),
WHITE_UNICORN_MASK(ItemID.WHITE_UNICORN_MASK, BossTab.CLUE_SCROLL_MEDIUM, 18),
LEPRECHAUN_HAT(ItemID.LEPRECHAUN_HAT, BossTab.CLUE_SCROLL_MEDIUM, 18),
BLACK_LEPRECHAUN_HAT(ItemID.BLACK_LEPRECHAUN_HAT, BossTab.CLUE_SCROLL_MEDIUM, 18),
ARCEUUS_BANNER(ItemID.ARCEUUS_BANNER, BossTab.CLUE_SCROLL_MEDIUM, 19),
HOSIDIUS_BANNER(ItemID.HOSIDIUS_BANNER, BossTab.CLUE_SCROLL_MEDIUM, 19),
LOVAKENGJ_BANNER(ItemID.LOVAKENGJ_BANNER, BossTab.CLUE_SCROLL_MEDIUM, 19),
PISCARILIUS_BANNER(ItemID.PISCARILIUS_BANNER, BossTab.CLUE_SCROLL_MEDIUM, 19),
SHAYZIEN_BANNER(ItemID.SHAYZIEN_BANNER, BossTab.CLUE_SCROLL_MEDIUM, 19),
CRIER_HAT(ItemID.CRIER_HAT, BossTab.CLUE_SCROLL_MEDIUM, 20),
CRIER_BELL(ItemID.CRIER_BELL, BossTab.CLUE_SCROLL_MEDIUM, 20),
CRIER_COAT(ItemID.CRIER_COAT, BossTab.CLUE_SCROLL_MEDIUM, 20),
WOLF_MASK(ItemID.WOLF_MASK, BossTab.CLUE_SCROLL_MEDIUM, 20),
WOLF_CLOAK(ItemID.WOLF_CLOAK, BossTab.CLUE_SCROLL_MEDIUM, 20),
SPIKED_MANACLES(ItemID.SPIKED_MANACLES, BossTab.CLUE_SCROLL_MEDIUM, 21),
STRENGTH_AMULET_T(ItemID.STRENGTH_AMULET_T, BossTab.CLUE_SCROLL_MEDIUM, 21),
ADAMANT_CANE(ItemID.ADAMANT_CANE, BossTab.CLUE_SCROLL_MEDIUM, 21),
GNOMISH_FIRELIGHTER(ItemID.GNOMISH_FIRELIGHTER, BossTab.CLUE_SCROLL_MEDIUM, 21),
CABBAGE_ROUND_SHIELD(ItemID.CABBAGE_ROUND_SHIELD, BossTab.CLUE_SCROLL_MEDIUM, 21),
CLUELESS_SCROLL(ItemID.CLUELESS_SCROLL, BossTab.CLUE_SCROLL_MEDIUM, 21),
ADAMANT_PLATEBODY_H1(ItemID.ADAMANT_PLATEBODY_H1, BossTab.CLUE_SCROLL_MEDIUM, 22),
ADAMANT_PLATEBODY_H2(ItemID.ADAMANT_PLATEBODY_H2, BossTab.CLUE_SCROLL_MEDIUM, 22),
ADAMANT_PLATEBODY_H3(ItemID.ADAMANT_PLATEBODY_H3, BossTab.CLUE_SCROLL_MEDIUM, 22),
ADAMANT_PLATEBODY_H4(ItemID.ADAMANT_PLATEBODY_H4, BossTab.CLUE_SCROLL_MEDIUM, 22),
ADAMANT_PLATEBODY_H5(ItemID.ADAMANT_PLATEBODY_H5, BossTab.CLUE_SCROLL_MEDIUM, 22),
// Hard Clue Scrolls
RUNE_FULL_HELM_T(ItemID.RUNE_FULL_HELM_T, BossTab.CLUE_SCROLL_HARD, -1),
RUNE_PLATEBODY_T(ItemID.RUNE_PLATEBODY_T, BossTab.CLUE_SCROLL_HARD, -1),
RUNE_PLATELEGS_T(ItemID.RUNE_PLATELEGS_T, BossTab.CLUE_SCROLL_HARD, -1),
RUNE_PLATESKIRT_T(ItemID.RUNE_PLATESKIRT_T, BossTab.CLUE_SCROLL_HARD, -1),
RUNE_KITESHIELD_T(ItemID.RUNE_KITESHIELD_T, BossTab.CLUE_SCROLL_HARD, -1),
RUNE_FULL_HELM_G(ItemID.RUNE_FULL_HELM_G, BossTab.CLUE_SCROLL_HARD, 0),
RUNE_PLATEBODY_G(ItemID.RUNE_PLATEBODY_G, BossTab.CLUE_SCROLL_HARD, 0),
RUNE_PLATELEGS_G(ItemID.RUNE_PLATELEGS_G, BossTab.CLUE_SCROLL_HARD, 0),
RUNE_PLATESKIRT_G(ItemID.RUNE_PLATESKIRT_G, BossTab.CLUE_SCROLL_HARD, 0),
RUNE_KITESHIELD_G(ItemID.RUNE_KITESHIELD_G, BossTab.CLUE_SCROLL_HARD, 0),
GUTHIX_FULL_HELM(ItemID.GUTHIX_FULL_HELM, BossTab.CLUE_SCROLL_HARD, 1),
GUTHIX_PLATEBODY(ItemID.GUTHIX_PLATEBODY, BossTab.CLUE_SCROLL_HARD, 1),
GUTHIX_PLATELEGS(ItemID.GUTHIX_PLATELEGS, BossTab.CLUE_SCROLL_HARD, 1),
GUTHIX_PLATESKIRT(ItemID.GUTHIX_PLATESKIRT, BossTab.CLUE_SCROLL_HARD, 1),
GUTHIX_KITESHIELD(ItemID.GUTHIX_KITESHIELD, BossTab.CLUE_SCROLL_HARD, 1),
SARADOMIN_FULL_HELM(ItemID.SARADOMIN_FULL_HELM, BossTab.CLUE_SCROLL_HARD, 2),
SARADOMIN_PLATEBODY(ItemID.SARADOMIN_PLATEBODY, BossTab.CLUE_SCROLL_HARD, 2),
SARADOMIN_PLATELEGS(ItemID.SARADOMIN_PLATELEGS, BossTab.CLUE_SCROLL_HARD, 2),
SARADOMIN_PLATESKIRT(ItemID.SARADOMIN_PLATESKIRT, BossTab.CLUE_SCROLL_HARD, 2),
SARADOMIN_KITESHIELD(ItemID.SARADOMIN_KITESHIELD, BossTab.CLUE_SCROLL_HARD, 2),
ZAMORAK_FULL_HELM(ItemID.ZAMORAK_FULL_HELM, BossTab.CLUE_SCROLL_HARD, 3),
ZAMORAK_PLATEBODY(ItemID.ZAMORAK_PLATEBODY, BossTab.CLUE_SCROLL_HARD, 3),
ZAMORAK_PLATELEGS(ItemID.ZAMORAK_PLATELEGS, BossTab.CLUE_SCROLL_HARD, 3),
ZAMORAK_PLATESKIRT(ItemID.ZAMORAK_PLATESKIRT, BossTab.CLUE_SCROLL_HARD, 3),
ZAMORAK_KITESHIELD(ItemID.ZAMORAK_KITESHIELD, BossTab.CLUE_SCROLL_HARD, 3),
ANCIENT_FULL_HELM(ItemID.ANCIENT_FULL_HELM, BossTab.CLUE_SCROLL_HARD, 4),
ANCIENT_PLATEBODY(ItemID.ANCIENT_PLATEBODY, BossTab.CLUE_SCROLL_HARD, 4),
ANCIENT_PLATELEGS(ItemID.ANCIENT_PLATELEGS, BossTab.CLUE_SCROLL_HARD, 4),
ANCIENT_PLATESKIRT(ItemID.ANCIENT_PLATESKIRT, BossTab.CLUE_SCROLL_HARD, 4),
ANCIENT_KITESHIELD(ItemID.ANCIENT_KITESHIELD, BossTab.CLUE_SCROLL_HARD, 4),
BANDOS_FULL_HELM(ItemID.BANDOS_FULL_HELM, BossTab.CLUE_SCROLL_HARD, 5),
BANDOS_PLATEBODY(ItemID.BANDOS_PLATEBODY, BossTab.CLUE_SCROLL_HARD, 5),
BANDOS_PLATELEGS(ItemID.BANDOS_PLATELEGS, BossTab.CLUE_SCROLL_HARD, 5),
BANDOS_PLATESKIRT(ItemID.BANDOS_PLATESKIRT, BossTab.CLUE_SCROLL_HARD, 5),
BANDOS_KITESHIELD(ItemID.BANDOS_KITESHIELD, BossTab.CLUE_SCROLL_HARD, 5),
ARMADYL_FULL_HELM(ItemID.ARMADYL_FULL_HELM, BossTab.CLUE_SCROLL_HARD, 6),
ARMADYL_PLATEBODY(ItemID.ARMADYL_PLATEBODY, BossTab.CLUE_SCROLL_HARD, 6),
ARMADYL_PLATELEGS(ItemID.ARMADYL_PLATELEGS, BossTab.CLUE_SCROLL_HARD, 6),
ARMADYL_PLATESKIRT(ItemID.ARMADYL_PLATESKIRT, BossTab.CLUE_SCROLL_HARD, 6),
ARMADYL_KITESHIELD(ItemID.ARMADYL_KITESHIELD, BossTab.CLUE_SCROLL_HARD, 6),
GILDED_FULL_HELM(ItemID.GILDED_FULL_HELM, BossTab.CLUE_SCROLL_HARD, 7),
GILDED_PLATEBODY(ItemID.GILDED_PLATEBODY, BossTab.CLUE_SCROLL_HARD, 7),
GILDED_PLATELEGS(ItemID.GILDED_PLATELEGS, BossTab.CLUE_SCROLL_HARD, 7),
GILDED_PLATESKIRT(ItemID.GILDED_PLATESKIRT, BossTab.CLUE_SCROLL_HARD, 7),
GILDED_KITESHIELD(ItemID.GILDED_KITESHIELD, BossTab.CLUE_SCROLL_HARD, 7),
GILDED_MED_HELM(ItemID.GILDED_MED_HELM, BossTab.CLUE_SCROLL_HARD, 8),
GILDED_CHAINBODY(ItemID.GILDED_CHAINBODY, BossTab.CLUE_SCROLL_HARD, 8),
GILDED_SQ_SHIELD(ItemID.GILDED_SQ_SHIELD, BossTab.CLUE_SCROLL_HARD, 8),
GILDED_2H_SWORD(ItemID.GILDED_2H_SWORD, BossTab.CLUE_SCROLL_HARD, 8),
GILDED_SPEAR(ItemID.GILDED_SPEAR, BossTab.CLUE_SCROLL_HARD, 8),
GILDED_HASTA(ItemID.GILDED_HASTA, BossTab.CLUE_SCROLL_HARD, 8),
RUNE_HELM_H1(ItemID.RUNE_HELM_H1, BossTab.CLUE_SCROLL_HARD, 9),
RUNE_HELM_H2(ItemID.RUNE_HELM_H2, BossTab.CLUE_SCROLL_HARD, 9),
RUNE_HELM_H3(ItemID.RUNE_HELM_H3, BossTab.CLUE_SCROLL_HARD, 9),
RUNE_HELM_H4(ItemID.RUNE_HELM_H4, BossTab.CLUE_SCROLL_HARD, 9),
RUNE_HELM_H5(ItemID.RUNE_HELM_H5, BossTab.CLUE_SCROLL_HARD, 9),
RUNE_SHIELD_H1(ItemID.RUNE_SHIELD_H1, BossTab.CLUE_SCROLL_HARD, 10),
RUNE_SHIELD_H2(ItemID.RUNE_SHIELD_H2, BossTab.CLUE_SCROLL_HARD, 10),
RUNE_SHIELD_H3(ItemID.RUNE_SHIELD_H3, BossTab.CLUE_SCROLL_HARD, 10),
RUNE_SHIELD_H4(ItemID.RUNE_SHIELD_H4, BossTab.CLUE_SCROLL_HARD, 10),
RUNE_SHIELD_H5(ItemID.RUNE_SHIELD_H5, BossTab.CLUE_SCROLL_HARD, 10),
BLUE_DHIDE_BODY_T(ItemID.BLUE_DHIDE_BODY_T, BossTab.CLUE_SCROLL_HARD, 11),
BLUE_DHIDE_CHAPS_T(ItemID.BLUE_DHIDE_CHAPS_T, BossTab.CLUE_SCROLL_HARD, 11),
BLUE_DHIDE_BODY_G(ItemID.BLUE_DHIDE_BODY_G, BossTab.CLUE_SCROLL_HARD, 11),
BLUE_DHIDE_CHAPS_G(ItemID.BLUE_DHIDE_CHAPS_G, BossTab.CLUE_SCROLL_HARD, 11),
RED_DHIDE_BODY_T(ItemID.RED_DHIDE_BODY_T, BossTab.CLUE_SCROLL_HARD, 12),
RED_DHIDE_CHAPS_T(ItemID.RED_DHIDE_CHAPS_T, BossTab.CLUE_SCROLL_HARD, 12),
RED_DHIDE_BODY_G(ItemID.RED_DHIDE_BODY_G, BossTab.CLUE_SCROLL_HARD, 12),
RED_DHIDE_CHAPS_G(ItemID.RED_DHIDE_CHAPS_G, BossTab.CLUE_SCROLL_HARD, 12),
ENCHANTED_HAT(ItemID.ENCHANTED_HAT, BossTab.CLUE_SCROLL_HARD, 13),
ENCHANTED_TOP(ItemID.ENCHANTED_TOP, BossTab.CLUE_SCROLL_HARD, 13),
ENCHANTED_ROBE(ItemID.ENCHANTED_ROBE, BossTab.CLUE_SCROLL_HARD, 13),
AMULET_OF_GLORY_T(ItemID.AMULET_OF_GLORY_T, BossTab.CLUE_SCROLL_HARD, 13),
ROBIN_HOOD_HAT(ItemID.ROBIN_HOOD_HAT, BossTab.CLUE_SCROLL_HARD, 14),
PIRATE_HAT(ItemID.PIRATE_HAT, BossTab.CLUE_SCROLL_HARD, 14),
PITH_HELMET(ItemID.PITH_HELMET, BossTab.CLUE_SCROLL_HARD, 14),
EXPLORER_BACKPACK(ItemID.EXPLORER_BACKPACK, BossTab.CLUE_SCROLL_HARD, 14),
RED_CAVALIER(ItemID.RED_CAVALIER, BossTab.CLUE_SCROLL_HARD, 15),
TAN_CAVALIER(ItemID.TAN_CAVALIER, BossTab.CLUE_SCROLL_HARD, 15),
DARK_CAVALIER(ItemID.DARK_CAVALIER, BossTab.CLUE_SCROLL_HARD, 15),
BLACK_CAVALIER(ItemID.BLACK_CAVALIER, BossTab.CLUE_SCROLL_HARD, 15),
NAVY_CAVALIER(ItemID.NAVY_CAVALIER, BossTab.CLUE_SCROLL_HARD, 15),
WHITE_CAVALIER(ItemID.WHITE_CAVALIER, BossTab.CLUE_SCROLL_HARD, 15),
_3RD_AGE_FULL_HELMET(ItemID._3RD_AGE_FULL_HELMET, BossTab.CLUE_SCROLL_HARD, 16),
_3RD_AGE_PLATEBODY(ItemID._3RD_AGE_PLATEBODY, BossTab.CLUE_SCROLL_HARD, 16),
_3RD_AGE_PLATELEGS(ItemID._3RD_AGE_PLATELEGS, BossTab.CLUE_SCROLL_HARD, 16),
_3RD_AGE_PLATESKIRT(ItemID._3RD_AGE_PLATESKIRT, BossTab.CLUE_SCROLL_HARD, 16),
_3RD_AGE_KITESHIELD(ItemID._3RD_AGE_KITESHIELD, BossTab.CLUE_SCROLL_HARD, 16),
_3RD_AGE_RANGE_COIF(ItemID._3RD_AGE_RANGE_COIF, BossTab.CLUE_SCROLL_HARD, 17),
_3RD_AGE_RANGE_TOP(ItemID._3RD_AGE_RANGE_TOP, BossTab.CLUE_SCROLL_HARD, 17),
_3RD_AGE_RANGE_LEGS(ItemID._3RD_AGE_RANGE_LEGS, BossTab.CLUE_SCROLL_HARD, 17),
_3RD_AGE_VAMBRACES(ItemID._3RD_AGE_VAMBRACES, BossTab.CLUE_SCROLL_HARD, 17),
_3RD_AGE_MAGE_HAT(ItemID._3RD_AGE_MAGE_HAT, BossTab.CLUE_SCROLL_HARD, 18),
_3RD_AGE_ROBE_TOP(ItemID._3RD_AGE_ROBE_TOP, BossTab.CLUE_SCROLL_HARD, 18),
_3RD_AGE_ROBE(ItemID._3RD_AGE_ROBE, BossTab.CLUE_SCROLL_HARD, 18),
_3RD_AGE_AMULET(ItemID._3RD_AGE_AMULET, BossTab.CLUE_SCROLL_HARD, 18),
GUTHIX_COIF(ItemID.GUTHIX_COIF, BossTab.CLUE_SCROLL_HARD, 19),
GUTHIX_DHIDE(ItemID.GUTHIX_DRAGONHIDE, BossTab.CLUE_SCROLL_HARD, 19),
GUTHIX_CHAPS(ItemID.GUTHIX_CHAPS, BossTab.CLUE_SCROLL_HARD, 19),
GUTHIX_BRACERS(ItemID.GUTHIX_BRACERS, BossTab.CLUE_SCROLL_HARD, 19),
GUTHIX_DHIDE_BOOTS(ItemID.GUTHIX_DHIDE_BOOTS, BossTab.CLUE_SCROLL_HARD, 19),
GUTHIX_DHIDE_SHIELD(ItemID.GUTHIX_DHIDE_SHIELD, BossTab.CLUE_SCROLL_HARD, 19),
SARADOMIN_COIF(ItemID.SARADOMIN_COIF, BossTab.CLUE_SCROLL_HARD, 20),
SARADOMIN_DHIDE(ItemID.SARADOMIN_DHIDE, BossTab.CLUE_SCROLL_HARD, 20),
SARADOMIN_CHAPS(ItemID.SARADOMIN_CHAPS, BossTab.CLUE_SCROLL_HARD, 20),
SARADOMIN_BRACERS(ItemID.SARADOMIN_BRACERS, BossTab.CLUE_SCROLL_HARD, 20),
SARADOMIN_DHIDE_BOOTS(ItemID.SARADOMIN_DHIDE_BOOTS, BossTab.CLUE_SCROLL_HARD, 20),
SARADOMIN_DHIDE_SHIELD(ItemID.SARADOMIN_DHIDE_SHIELD, BossTab.CLUE_SCROLL_HARD, 20),
ZAMORAK_COIF(ItemID.ZAMORAK_COIF, BossTab.CLUE_SCROLL_HARD, 21),
ZAMORAK_DHIDE(ItemID.ZAMORAK_DHIDE, BossTab.CLUE_SCROLL_HARD, 21),
ZAMORAK_CHAPS(ItemID.ZAMORAK_CHAPS, BossTab.CLUE_SCROLL_HARD, 21),
ZAMORAK_BRACERS(ItemID.ZAMORAK_BRACERS, BossTab.CLUE_SCROLL_HARD, 21),
ZAMORAK_DHIDE_BOOTS(ItemID.ZAMORAK_DHIDE_BOOTS, BossTab.CLUE_SCROLL_HARD, 21),
ZAMORAK_DHIDE_SHIELD(ItemID.ZAMORAK_DHIDE_SHIELD, BossTab.CLUE_SCROLL_HARD, 21),
ANCIENT_COIF(ItemID.ANCIENT_COIF, BossTab.CLUE_SCROLL_HARD, 22),
ANCIENT_DHIDE(ItemID.ANCIENT_DHIDE, BossTab.CLUE_SCROLL_HARD, 22),
ANCIENT_CHAPS(ItemID.ANCIENT_CHAPS, BossTab.CLUE_SCROLL_HARD, 22),
ANCIENT_BRACERS(ItemID.ANCIENT_BRACERS, BossTab.CLUE_SCROLL_HARD, 22),
ANCIENT_DHIDE_BOOTS(ItemID.ANCIENT_DHIDE_BOOTS, BossTab.CLUE_SCROLL_HARD, 22),
ANCIENT_DHIDE_SHIELD(ItemID.ANCIENT_DHIDE_SHIELD, BossTab.CLUE_SCROLL_HARD, 22),
BANDOS_COIF(ItemID.BANDOS_COIF, BossTab.CLUE_SCROLL_HARD, 23),
BANDOS_DHIDE(ItemID.BANDOS_DHIDE, BossTab.CLUE_SCROLL_HARD, 23),
BANDOS_CHAPS(ItemID.BANDOS_CHAPS, BossTab.CLUE_SCROLL_HARD, 23),
BANDOS_BRACERS(ItemID.BANDOS_BRACERS, BossTab.CLUE_SCROLL_HARD, 23),
BANDOS_DHIDE_BOOTS(ItemID.BANDOS_DHIDE_BOOTS, BossTab.CLUE_SCROLL_HARD, 23),
BANDOS_DHIDE_SHIELD(ItemID.BANDOS_DHIDE_SHIELD, BossTab.CLUE_SCROLL_HARD, 23),
ARMADYL_COIF(ItemID.ARMADYL_COIF, BossTab.CLUE_SCROLL_HARD, 24),
ARMADYL_DHIDE(ItemID.ARMADYL_DHIDE, BossTab.CLUE_SCROLL_HARD, 24),
ARMADYL_CHAPS(ItemID.ARMADYL_CHAPS, BossTab.CLUE_SCROLL_HARD, 24),
ARMADYL_BRACERS(ItemID.ARMADYL_BRACERS, BossTab.CLUE_SCROLL_HARD, 24),
ARMADYL_DHIDE_BOOTS(ItemID.ARMADYL_DHIDE_BOOTS, BossTab.CLUE_SCROLL_HARD, 24),
ARMADYL_DHIDE_SHIELD(ItemID.ARMADYL_DHIDE_SHIELD, BossTab.CLUE_SCROLL_HARD, 24),
GUTHIX_STOLE(ItemID.GUTHIX_STOLE, BossTab.CLUE_SCROLL_HARD, 25),
GUTHIX_CROZIER(ItemID.GUTHIX_CROZIER, BossTab.CLUE_SCROLL_HARD, 25),
SARADOMIN_STOLE(ItemID.SARADOMIN_STOLE, BossTab.CLUE_SCROLL_HARD, 25),
SARADOMIN_CROZIER(ItemID.SARADOMIN_CROZIER, BossTab.CLUE_SCROLL_HARD, 25),
ZAMORAK_STOLE(ItemID.ZAMORAK_STOLE, BossTab.CLUE_SCROLL_HARD, 25),
ZAMORAK_CROZIER(ItemID.ZAMORAK_CROZIER, BossTab.CLUE_SCROLL_HARD, 25),
GREEN_DRAGON_MASK(ItemID.GREEN_DRAGON_MASK, BossTab.CLUE_SCROLL_HARD, 26),
RED_DRAGON_MASK(ItemID.RED_DRAGON_MASK, BossTab.CLUE_SCROLL_HARD, 26),
BLUE_DRAGON_MASK(ItemID.BLUE_DRAGON_MASK, BossTab.CLUE_SCROLL_HARD, 26),
BLACK_DRAGON_MASK(ItemID.BLACK_DRAGON_MASK, BossTab.CLUE_SCROLL_HARD, 26),
RUNE_CANE(ItemID.RUNE_CANE, BossTab.CLUE_SCROLL_HARD, 27),
ZOMBIE_HEAD(ItemID.ZOMBIE_HEAD, BossTab.CLUE_SCROLL_HARD, 27),
CYCLOPS_HEAD(ItemID.CYCLOPS_HEAD, BossTab.CLUE_SCROLL_HARD, 27),
NUNCHAKU(ItemID.NUNCHAKU, BossTab.CLUE_SCROLL_HARD, 27),
DUAL_SAI(ItemID.DUAL_SAI, BossTab.CLUE_SCROLL_HARD, 27),
THIEVING_BAG(ItemID.THIEVING_BAG, BossTab.CLUE_SCROLL_HARD, 27),
DRAGON_BOOTS_ORNAMENT_KIT(ItemID.DRAGON_BOOTS_ORNAMENT_KIT, BossTab.CLUE_SCROLL_HARD, 28),
RUNE_DEFENDER_ORNAMENT_KIT(ItemID.RUNE_DEFENDER_ORNAMENT_KIT, BossTab.CLUE_SCROLL_HARD, 28),
TZHAARKETOM_ORNAMENT_KIT(ItemID.TZHAARKETOM_ORNAMENT_KIT, BossTab.CLUE_SCROLL_HARD, 28),
BERSERKER_NECKLACE_ORNAMENT_KIT(ItemID.BERSERKER_NECKLACE_ORNAMENT_KIT, BossTab.CLUE_SCROLL_HARD, 28),
RUNE_PLATEBODY_H1(ItemID.RUNE_PLATEBODY_H1, BossTab.CLUE_SCROLL_HARD, 29),
RUNE_PLATEBODY_H2(ItemID.RUNE_PLATEBODY_H2, BossTab.CLUE_SCROLL_HARD, 29),
RUNE_PLATEBODY_H3(ItemID.RUNE_PLATEBODY_H3, BossTab.CLUE_SCROLL_HARD, 29),
RUNE_PLATEBODY_H4(ItemID.RUNE_PLATEBODY_H4, BossTab.CLUE_SCROLL_HARD, 29),
RUNE_PLATEBODY_H5(ItemID.RUNE_PLATEBODY_H5, BossTab.CLUE_SCROLL_HARD, 29),
// Elite Clue Scrolls
DRAGON_FULL_HELM_ORNAMENT_KIT(ItemID.DRAGON_FULL_HELM_ORNAMENT_KIT, BossTab.CLUE_SCROLL_ELITE, -1),
DRAGON_CHAINBODY_ORNAMENT_KIT(ItemID.DRAGON_CHAINBODY_ORNAMENT_KIT, BossTab.CLUE_SCROLL_ELITE, -1),
DRAGON_LEGSSKIRT_ORNAMENT_KIT(ItemID.DRAGON_LEGSSKIRT_ORNAMENT_KIT, BossTab.CLUE_SCROLL_ELITE, -1),
DRAGON_SQ_SHIELD_ORNAMENT_KIT(ItemID.DRAGON_SQ_SHIELD_ORNAMENT_KIT, BossTab.CLUE_SCROLL_ELITE, -1),
DRAGON_SCIMITAR_ORNAMENT_KIT(ItemID.DRAGON_SCIMITAR_ORNAMENT_KIT, BossTab.CLUE_SCROLL_ELITE, -1),
LIGHT_INFINITY_COLOUR_KIT(ItemID.LIGHT_INFINITY_COLOUR_KIT, BossTab.CLUE_SCROLL_ELITE, 0),
DARK_INFINITY_COLOUR_KIT(ItemID.DARK_INFINITY_COLOUR_KIT, BossTab.CLUE_SCROLL_ELITE, 0),
FURY_ORNAMENT_KIT(ItemID.FURY_ORNAMENT_KIT, BossTab.CLUE_SCROLL_ELITE, 0),
MUSKETEER_HAT(ItemID.MUSKETEER_HAT, BossTab.CLUE_SCROLL_ELITE, 1),
MUSKETEER_TABARD(ItemID.MUSKETEER_TABARD, BossTab.CLUE_SCROLL_ELITE, 1),
MUSKETEER_PANTS(ItemID.MUSKETEER_PANTS, BossTab.CLUE_SCROLL_ELITE, 1),
DRAGON_CANE(ItemID.DRAGON_CANE, BossTab.CLUE_SCROLL_ELITE, 1),
TOP_HAT(ItemID.TOP_HAT, BossTab.CLUE_SCROLL_ELITE, 2),
MONOCLE(ItemID.MONOCLE, BossTab.CLUE_SCROLL_ELITE, 2),
BRIEFCASE(ItemID.BRIEFCASE, BossTab.CLUE_SCROLL_ELITE, 2),
SAGACIOUS_SPECTACLES(ItemID.SAGACIOUS_SPECTACLES, BossTab.CLUE_SCROLL_ELITE, 2),
BIG_PIRATE_HAT(ItemID.BIG_PIRATE_HAT, BossTab.CLUE_SCROLL_ELITE, 3),
DEERSTALKER(ItemID.DEERSTALKER, BossTab.CLUE_SCROLL_ELITE, 3),
BLACKSMITHS_HELM(ItemID.BLACKSMITHS_HELM, BossTab.CLUE_SCROLL_ELITE, 3),
BUCKET_HELM(ItemID.BUCKET_HELM, BossTab.CLUE_SCROLL_ELITE, 3),
AFRO(ItemID.AFRO, BossTab.CLUE_SCROLL_ELITE, 3),
GILDED_COIF(ItemID.GILDED_COIF, BossTab.CLUE_SCROLL_ELITE, 4),
GILDED_DHIDE_BODY(ItemID.GILDED_DHIDE_BODY, BossTab.CLUE_SCROLL_ELITE, 4),
GILDED_DHIDE_CHAPS(ItemID.GILDED_DHIDE_CHAPS, BossTab.CLUE_SCROLL_ELITE, 4),
GILDED_DHIDE_VAMBS(ItemID.GILDED_DHIDE_VAMBS, BossTab.CLUE_SCROLL_ELITE, 4),
GILDED_BOOTS(ItemID.GILDED_BOOTS, BossTab.CLUE_SCROLL_ELITE, 5),
GILDED_SCIMITAR(ItemID.GILDED_SCIMITAR, BossTab.CLUE_SCROLL_ELITE, 5),
GILDED_PICKAXE(ItemID.GILDED_PICKAXE, BossTab.CLUE_SCROLL_ELITE, 5),
GILDED_SPADE(ItemID.GILDED_SPADE, BossTab.CLUE_SCROLL_ELITE, 5),
GILDED_AXE(ItemID.GILDED_AXE, BossTab.CLUE_SCROLL_ELITE, 5),
BRONZE_DRAGON_MASK(ItemID.BRONZE_DRAGON_MASK, BossTab.CLUE_SCROLL_ELITE, 6),
IRON_DRAGON_MASK(ItemID.IRON_DRAGON_MASK, BossTab.CLUE_SCROLL_ELITE, 6),
STEEL_DRAGON_MASK(ItemID.STEEL_DRAGON_MASK, BossTab.CLUE_SCROLL_ELITE, 6),
MITHRIL_DRAGON_MASK(ItemID.MITHRIL_DRAGON_MASK, BossTab.CLUE_SCROLL_ELITE, 6),
ADAMANT_DRAGON_MASK(ItemID.ADAMANT_DRAGON_MASK, BossTab.CLUE_SCROLL_ELITE, 6),
RUNE_DRAGON_MASK(ItemID.RUNE_DRAGON_MASK, BossTab.CLUE_SCROLL_ELITE, 6),
LAVA_DRAGON_MASK(ItemID.LAVA_DRAGON_MASK, BossTab.CLUE_SCROLL_ELITE, 6),
BLACK_DHIDE_BODY_T(ItemID.BLACK_DHIDE_BODY_T, BossTab.CLUE_SCROLL_ELITE, 7),
BLACK_DHIDE_CHAPS_T(ItemID.BLACK_DHIDE_CHAPS_T, BossTab.CLUE_SCROLL_ELITE, 7),
BLACK_DHIDE_BODY_G(ItemID.BLACK_DHIDE_BODY_G, BossTab.CLUE_SCROLL_ELITE, 7),
BLACK_DHIDE_CHAPS_G(ItemID.BLACK_DHIDE_CHAPS_G, BossTab.CLUE_SCROLL_ELITE, 7),
_3RD_AGE_CLOAK(ItemID._3RD_AGE_CLOAK, BossTab.CLUE_SCROLL_ELITE, 8),
_3RD_AGE_WAND(ItemID._3RD_AGE_WAND, BossTab.CLUE_SCROLL_ELITE, 8),
_3RD_AGE_BOW(ItemID._3RD_AGE_BOW, BossTab.CLUE_SCROLL_ELITE, 8),
_3RD_AGE_LONGSWORD(ItemID._3RD_AGE_LONGSWORD, BossTab.CLUE_SCROLL_ELITE, 8),
ROYAL_CROWN(ItemID.ROYAL_CROWN, BossTab.CLUE_SCROLL_ELITE, 9),
ROYAL_GOWN_TOP(ItemID.ROYAL_GOWN_TOP, BossTab.CLUE_SCROLL_ELITE, 9),
ROYAL_GOWN_BOTTOM(ItemID.ROYAL_GOWN_BOTTOM, BossTab.CLUE_SCROLL_ELITE, 9),
ROYAL_SCEPTRE(ItemID.ROYAL_SCEPTRE, BossTab.CLUE_SCROLL_ELITE, 9),
ARCEUUS_SCARF(ItemID.ARCEUUS_SCARF, BossTab.CLUE_SCROLL_ELITE, 10),
HOSIDIUS_SCARF(ItemID.HOSIDIUS_SCARF, BossTab.CLUE_SCROLL_ELITE, 10),
LOVAKENGJ_SCARF(ItemID.LOVAKENGJ_SCARF, BossTab.CLUE_SCROLL_ELITE, 10),
PISCARILIUS_SCARF(ItemID.PISCARILIUS_SCARF, BossTab.CLUE_SCROLL_ELITE, 10),
SHAYZIEN_SCARF(ItemID.SHAYZIEN_SCARF, BossTab.CLUE_SCROLL_ELITE, 10),
DARK_BOW_TIE(ItemID.DARK_BOW_TIE, BossTab.CLUE_SCROLL_ELITE, 11),
DARK_TUXEDO_JACKET(ItemID.DARK_TUXEDO_JACKET, BossTab.CLUE_SCROLL_ELITE, 11),
DARK_TROUSERS(ItemID.DARK_TROUSERS, BossTab.CLUE_SCROLL_ELITE, 11),
DARK_TUXEDO_CUFFS(ItemID.DARK_TUXEDO_CUFFS, BossTab.CLUE_SCROLL_ELITE, 11),
DARK_TUXEDO_SHOES(ItemID.DARK_TUXEDO_SHOES, BossTab.CLUE_SCROLL_ELITE, 11),
LIGHT_BOW_TIE(ItemID.LIGHT_BOW_TIE, BossTab.CLUE_SCROLL_ELITE, 12),
LIGHT_TUXEDO_JACKET(ItemID.LIGHT_TUXEDO_JACKET, BossTab.CLUE_SCROLL_ELITE, 12),
LIGHT_TROUSERS(ItemID.LIGHT_TROUSERS, BossTab.CLUE_SCROLL_ELITE, 12),
LIGHT_TUXEDO_CUFFS(ItemID.LIGHT_TUXEDO_CUFFS, BossTab.CLUE_SCROLL_ELITE, 12),
LIGHT_TUXEDO_SHOES(ItemID.LIGHT_TUXEDO_SHOES, BossTab.CLUE_SCROLL_ELITE, 12),
HOLY_WRAPS(ItemID.HOLY_WRAPS, BossTab.CLUE_SCROLL_ELITE, 13),
RING_OF_NATURE(ItemID.RING_OF_NATURE, BossTab.CLUE_SCROLL_ELITE, 13),
HEAVY_CASKET(ItemID.HEAVY_CASKET, BossTab.CLUE_SCROLL_ELITE, 13),
KATANA(ItemID.KATANA, BossTab.CLUE_SCROLL_ELITE, 13),
URIS_HAT(ItemID.URIS_HAT, BossTab.CLUE_SCROLL_ELITE, 13),
RANGERS_TUNIC(ItemID.RANGERS_TUNIC, BossTab.CLUE_SCROLL_ELITE, 14),
RANGERS_TIGHTS(ItemID.RANGERS_TIGHTS, BossTab.CLUE_SCROLL_ELITE, 14),
RANGER_GLOVES(ItemID.RANGER_GLOVES, BossTab.CLUE_SCROLL_ELITE, 14),
// Master Clue Scrolls
DRAGON_PLATEBODY_ORNAMENT_KIT(ItemID.DRAGON_PLATEBODY_ORNAMENT_KIT, BossTab.CLUE_SCROLL_MASTER, -1),
DRAGON_KITESHIELD_ORNAMENT_KIT(ItemID.DRAGON_KITESHIELD_ORNAMENT_KIT, BossTab.CLUE_SCROLL_MASTER, -1),
DRAGON_DEFENDER_ORNAMENT_KIT(ItemID.DRAGON_DEFENDER_ORNAMENT_KIT, BossTab.CLUE_SCROLL_MASTER, -1),
ANGUISH_ORNAMENT_KIT(ItemID.ANGUISH_ORNAMENT_KIT, BossTab.CLUE_SCROLL_MASTER, -1),
TORTURE_ORNAMENT_KIT(ItemID.TORTURE_ORNAMENT_KIT, BossTab.CLUE_SCROLL_MASTER, -1),
TORMENTED_ORNAMENT_KIT(ItemID.TORMENTED_ORNAMENT_KIT, BossTab.CLUE_SCROLL_MASTER, -1),
OCCULT_ORNAMENT_KIT(ItemID.OCCULT_ORNAMENT_KIT, BossTab.CLUE_SCROLL_MASTER, 0),
ARMADYL_GODSWORD_ORNAMENT_KIT(ItemID.ARMADYL_GODSWORD_ORNAMENT_KIT, BossTab.CLUE_SCROLL_MASTER, 0),
BANDOS_GODSWORD_ORNAMENT_KIT(ItemID.BANDOS_GODSWORD_ORNAMENT_KIT, BossTab.CLUE_SCROLL_MASTER, 0),
SARADOMIN_GODSWORD_ORNAMENT_KIT(ItemID.SARADOMIN_GODSWORD_ORNAMENT_KIT, BossTab.CLUE_SCROLL_MASTER, 0),
ZAMORAK_GODSWORD_ORNAMENT_KIT(ItemID.ZAMORAK_GODSWORD_ORNAMENT_KIT, BossTab.CLUE_SCROLL_MASTER, 0),
_3RD_AGE_DRUIDIC(ItemID._3RD_AGE_DRUIDIC_ROBE_TOP, BossTab.CLUE_SCROLL_MASTER, 1),
_3RD_AGE_DRUIDIC_CLOAK(ItemID._3RD_AGE_DRUIDIC_CLOAK, BossTab.CLUE_SCROLL_MASTER, 1),
_3RD_AGE_DRUIDIC_ROBE_BOTTOMS(ItemID._3RD_AGE_DRUIDIC_ROBE_BOTTOMS, BossTab.CLUE_SCROLL_MASTER, 1),
_3RD_AGE_DRUIDIC_STAFF(ItemID._3RD_AGE_DRUIDIC_STAFF, BossTab.CLUE_SCROLL_MASTER, 1),
_3RD_AGE_AXE(ItemID._3RD_AGE_AXE, BossTab.CLUE_SCROLL_MASTER, 1),
_3RD_AGE_PICKAXE(ItemID._3RD_AGE_PICKAXE, BossTab.CLUE_SCROLL_MASTER, 1),
LESSER_DEMON_MASK(ItemID.LESSER_DEMON_MASK, BossTab.CLUE_SCROLL_MASTER, 2),
GREATER_DEMON_MASK(ItemID.GREATER_DEMON_MASK, BossTab.CLUE_SCROLL_MASTER, 2),
BLACK_DEMON_MASK(ItemID.BLACK_DEMON_MASK, BossTab.CLUE_SCROLL_MASTER, 2),
JUNGLE_DEMON_MASK(ItemID.JUNGLE_DEMON_MASK, BossTab.CLUE_SCROLL_MASTER, 2),
OLD_DEMON_MASK(ItemID.OLD_DEMON_MASK, BossTab.CLUE_SCROLL_MASTER, 2),
ARCEUUS_HOOD(ItemID.ARCEUUS_HOOD, BossTab.CLUE_SCROLL_MASTER, 3),
HOSIDIUS_HOOD(ItemID.HOSIDIUS_HOOD, BossTab.CLUE_SCROLL_MASTER, 3),
LOVAKENGJ_HOOD(ItemID.LOVAKENGJ_HOOD, BossTab.CLUE_SCROLL_MASTER, 3),
PISCARILIUS_HOOD(ItemID.PISCARILIUS_HOOD, BossTab.CLUE_SCROLL_MASTER, 3),
SHAYZIEN_HOUSE_HOOD(ItemID.SHAYZIEN_HOOD, BossTab.CLUE_SCROLL_MASTER, 3),
SAMURAI_KASA(ItemID.SAMURAI_KASA, BossTab.CLUE_SCROLL_MASTER, 4),
SAMURAI_SHIRT(ItemID.SAMURAI_SHIRT, BossTab.CLUE_SCROLL_MASTER, 4),
SAMURAI_GREAVES(ItemID.SAMURAI_GREAVES, BossTab.CLUE_SCROLL_MASTER, 4),
SAMURAI_GLOVES(ItemID.SAMURAI_GLOVES, BossTab.CLUE_SCROLL_MASTER, 4),
SAMURAI_BOOTS(ItemID.SAMURAI_BOOTS, BossTab.CLUE_SCROLL_MASTER, 4),
MUMMYS_HEAD(ItemID.MUMMYS_HEAD, BossTab.CLUE_SCROLL_MASTER, 5),
MUMMYS_BODY(ItemID.MUMMYS_BODY, BossTab.CLUE_SCROLL_MASTER, 5),
MUMMYS_LEGS(ItemID.MUMMYS_LEGS, BossTab.CLUE_SCROLL_MASTER, 5),
MUMMYS_HANDS(ItemID.MUMMYS_HANDS, BossTab.CLUE_SCROLL_MASTER, 5),
MUMMYS_FEET(ItemID.MUMMYS_FEET, BossTab.CLUE_SCROLL_MASTER, 5),
ANKOU_MASK(ItemID.ANKOU_MASK, BossTab.CLUE_SCROLL_MASTER, 6),
ANKOU_TOP(ItemID.ANKOU_TOP, BossTab.CLUE_SCROLL_MASTER, 6),
ANKOUS_LEGGINGS(ItemID.ANKOUS_LEGGINGS, BossTab.CLUE_SCROLL_MASTER, 6),
ANKOU_GLOVES(ItemID.ANKOU_GLOVES, BossTab.CLUE_SCROLL_MASTER, 6),
ANKOU_SOCKS(ItemID.ANKOU_SOCKS, BossTab.CLUE_SCROLL_MASTER, 6),
HOOD_OF_DARKNESS(ItemID.HOOD_OF_DARKNESS, BossTab.CLUE_SCROLL_MASTER, 7),
ROBE_TOP_OF_DARKNESS(ItemID.ROBE_TOP_OF_DARKNESS, BossTab.CLUE_SCROLL_MASTER, 7),
ROBE_BOTTOM_OF_DARKNESS(ItemID.ROBE_BOTTOM_OF_DARKNESS, BossTab.CLUE_SCROLL_MASTER, 7),
BOOTS_OF_DARKNESS(ItemID.BOOTS_OF_DARKNESS, BossTab.CLUE_SCROLL_MASTER, 7),
GLOVES_OF_DARKNESS(ItemID.GLOVES_OF_DARKNESS, BossTab.CLUE_SCROLL_MASTER, 7),
RING_OF_COINS(ItemID.RING_OF_COINS, BossTab.CLUE_SCROLL_MASTER, 8),
LEFT_EYE_PATCH(ItemID.LEFT_EYE_PATCH, BossTab.CLUE_SCROLL_MASTER, 8),
OBSIDIAN_CAPE_R(ItemID.OBSIDIAN_CAPE_R, BossTab.CLUE_SCROLL_MASTER, 8),
FANCY_TIARA(ItemID.FANCY_TIARA, BossTab.CLUE_SCROLL_MASTER, 8),
HALF_MOON_SPECTACLES(ItemID.HALF_MOON_SPECTACLES, BossTab.CLUE_SCROLL_MASTER, 8),
ALE_OF_THE_GODS(ItemID.ALE_OF_THE_GODS, BossTab.CLUE_SCROLL_MASTER, 9),
BUCKET_HELM_G(ItemID.BUCKET_HELM_G, BossTab.CLUE_SCROLL_MASTER, 9),
BOWL_WIG(ItemID.BOWL_WIG, BossTab.CLUE_SCROLL_MASTER, 9),
BLOODHOUND(ItemID.BLOODHOUND, BossTab.CLUE_SCROLL_MASTER, 9);
private static final ImmutableMultimap<String, UniqueItem> BOSS_MAP;
static
{
final ImmutableMultimap.Builder<String, UniqueItem> map = ImmutableMultimap.builder();
for (UniqueItem item : values())
{
for (BossTab b : item.getBosses())
{
map.put(b.getName(), item);
}
}
BOSS_MAP = map.build();
}
private final int itemID;
private final BossTab[] bosses;
private final int position;
private String name;
private int price;
private int linkedID;
@Setter
// Used to store the qty being displayed on the sprite
private int qty;
// Non-Shared Unique Items
UniqueItem(int id, BossTab boss, int position)
{
this.itemID = id;
this.bosses = new BossTab[]{boss};
this.position = position;
}
// Shared Unique Items
UniqueItem(int id, BossTab... bosses)
{
this.itemID = id;
this.bosses = bosses;
this.position = -1;
}
public static void prepareUniqueItems(final ItemManager itemManager)
{
for (final UniqueItem item : values())
{
if (item.getName() != null)
{
return;
}
final ItemDefinition c = itemManager.getItemDefinition(item.getItemID());
item.name = c.getName();
item.linkedID = c.getLinkedNoteId();
item.price = itemManager.getItemPrice(c.getId());
}
}
public static Collection<UniqueItem> getUniquesForBoss(final String bossName)
{
return BOSS_MAP.get(bossName);
}
}

View File

@@ -22,7 +22,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.stonedloottracker.ui;
package net.runelite.client.plugins.stonedtracker.ui;
import java.awt.BorderLayout;
import java.awt.Color;
@@ -30,24 +30,21 @@ import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import javax.inject.Singleton;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.stonedloottracker.data.LootTrackerItemEntry;
import net.runelite.client.plugins.loottracker.localstorage.LTItemEntry;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.components.shadowlabel.JShadowedLabel;
import net.runelite.client.util.StackFormatter;
@Singleton
@Getter(AccessLevel.PACKAGE)
@Getter
class ItemPanel extends JPanel
{
private static final GridBagLayout LAYOUT = new GridBagLayout();
@@ -58,75 +55,81 @@ class ItemPanel extends JPanel
private static final Border CONTAINER_BORDER = BorderFactory.createMatteBorder(4, 15, 4, 15, PANEL_BACKGROUND_COLOR);
private LootTrackerItemEntry record;
private final LTItemEntry record;
private final long total;
ItemPanel(final LootTrackerItemEntry record, final ItemManager itemManager)
ItemPanel(final LTItemEntry record, final ItemManager itemManager)
{
setLayout(LAYOUT);
setBorder(PANEL_BORDER);
setBackground(PANEL_BACKGROUND_COLOR);
setPreferredSize(PANEL_SIZE);
this.record = record;
this.setLayout(LAYOUT);
this.setBorder(PANEL_BORDER);
this.setBackground(PANEL_BACKGROUND_COLOR);
this.setPreferredSize(PANEL_SIZE);
this.total = record.getPrice() * record.getQuantity();
// Item Image Icon
JLabel icon = new JLabel();
itemManager.getImage(record.getId(), record.getQuantity(), (record.isStackable() || record.getQuantity() > 1)).addTo(icon);
final JLabel icon = new JLabel();
final boolean stackable = record.getQuantity() > 1;
itemManager.getImage(record.getId(), record.getQuantity(), stackable).addTo(icon);
icon.setHorizontalAlignment(JLabel.CENTER);
// Container for Info
JPanel uiInfo = new JPanel(new GridLayout(2, 1));
final JPanel uiInfo = new JPanel(new GridLayout(2, 1));
uiInfo.setBorder(new EmptyBorder(0, 5, 0, 0));
uiInfo.setBackground(PANEL_BACKGROUND_COLOR);
JShadowedLabel labelName = new JShadowedLabel(this.record.getName());
labelName.setForeground(Color.WHITE);
colorLabel(labelName, this.record.getPrice());
final JShadowedLabel labelName = new JShadowedLabel(this.record.getName());
labelName.setFont(FontManager.getRunescapeSmallFont());
labelName.setForeground(getRSValueColor(this.record.getPrice()));
labelName.setVerticalAlignment(SwingUtilities.BOTTOM);
String gpLabel = StackFormatter.quantityToStackSize(this.record.getTotal()) + " gp";
if (this.record.getHaTotal() > 0 && this.record.getHaPrice() > 1)
{
gpLabel += " (HA: " + StackFormatter.quantityToStackSize(this.record.getHaTotal()) + " gp)";
}
JShadowedLabel labelValue = new JShadowedLabel(gpLabel);
labelValue.setFont(FontManager.getSmallFont(getFont()));
colorLabel(labelValue, this.record.getTotal());
final JShadowedLabel labelValue = new JShadowedLabel(StackFormatter.quantityToStackSize(total) + " gp");
labelValue.setFont(FontManager.getRunescapeSmallFont());
labelValue.setForeground(getRSValueColor(total));
labelValue.setVerticalAlignment(SwingUtilities.TOP);
uiInfo.add(labelName);
uiInfo.add(labelValue);
// Create and append elements to container panel
JPanel panel = createPanel();
final JPanel panel = createPanel();
panel.add(icon, BorderLayout.LINE_START);
panel.add(uiInfo, BorderLayout.CENTER);
GridBagConstraints c = new GridBagConstraints();
final GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.weightx = 1;
c.gridx = 0;
c.gridy = 0;
this.add(panel, c);
this.setToolTipText(UniqueItemPanel.tooltipText(this.record.getPrice(), this.record.getHaPrice(), this.record.getQuantity(), this.record.getName()));
this.setToolTipText(buildToolTip(this.record));
}
static JPanel createPanel()
private static String buildToolTip(final LTItemEntry record)
{
JPanel panel = new JPanel();
final String name = record.getName();
final int quantity = record.getQuantity();
final long price = record.getPrice();
return "<html>" + name + " x " + StackFormatter.formatNumber(quantity)
+ "<br/>Price: " + StackFormatter.quantityToStackSize(price)
+ "<br/>Total: " + StackFormatter.quantityToStackSize(quantity * price) + "</html>";
}
private static Color getRSValueColor(long val)
{
return (val >= 10000000) ? Color.GREEN : (val >= 100000) ? Color.WHITE : Color.YELLOW;
}
private JPanel createPanel()
{
final JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.setBorder(CONTAINER_BORDER);
panel.setBackground(PANEL_BACKGROUND_COLOR);
return panel;
}
// Color label to match RuneScape coloring
private void colorLabel(JLabel label, long val)
{
Color labelColor = (val >= 10000000) ? Color.GREEN : (val >= 100000) ? Color.WHITE : Color.YELLOW;
label.setForeground(labelColor);
}
}

View File

@@ -22,63 +22,41 @@
* (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.stonedloottracker.ui;
package net.runelite.client.plugins.stonedtracker.ui;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.util.Arrays;
import java.util.List;
import javax.inject.Singleton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.stonedloottracker.data.LootTrackerItemEntry;
import net.runelite.client.plugins.loottracker.localstorage.LTItemEntry;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.util.StackFormatter;
@Singleton
class LootGrid extends JPanel
{
private static final int ITEMS_PER_ROW = 5;
private static final Dimension ITEM_SIZE = new Dimension(40, 40);
private final LootTrackerItemEntry[] itemsToDisplay;
private final ItemManager itemManager;
LootGrid(final LootTrackerItemEntry[] itemsToDisplay, final ItemManager itemManager)
LootGrid(final LTItemEntry[] itemsToDisplay, final ItemManager itemManager)
{
this.itemsToDisplay = itemsToDisplay;
this.itemManager = itemManager;
setBorder(new EmptyBorder(5, 0, 5, 0));
buildItems();
}
/**
* This method creates stacked items from the item list, calculates total price and then
* displays all the items in the UI.
*/
private void buildItems()
{
List<LootTrackerItemEntry> items = LootTrackerBox.dedupeClues(Arrays.asList(itemsToDisplay));
// Calculates how many rows need to be display to fit all items
final int rowSize = LootTrackerBox.rowSize(items.size());
removeAll();
final int rowSize = ((itemsToDisplay.length % ITEMS_PER_ROW == 0) ? 0 : 1) + itemsToDisplay.length / ITEMS_PER_ROW;
setLayout(new GridLayout(rowSize, ITEMS_PER_ROW, 1, 1));
// Create stacked items from the item list, calculates total price and then displays all the items in the UI.
for (int i = 0; i < rowSize * ITEMS_PER_ROW; i++)
{
final JPanel slot = new JPanel();
slot.setLayout(new GridLayout(1, 1, 0, 0));
slot.setBackground(ColorScheme.DARKER_GRAY_COLOR);
slot.setPreferredSize(ITEM_SIZE);
if (i < items.size())
if (i < itemsToDisplay.length)
{
final LootTrackerItemEntry item = items.get(i);
final LTItemEntry item = itemsToDisplay[i];
if (item == null)
{
continue;
@@ -97,7 +75,7 @@ class LootGrid extends JPanel
repaint();
}
private static String buildToolTip(LootTrackerItemEntry item)
private static String buildToolTip(final LTItemEntry item)
{
final String name = item.getName();
final int quantity = item.getQuantity();
@@ -105,6 +83,6 @@ class LootGrid extends JPanel
return "<html>" + name + " x " + StackFormatter.formatNumber(quantity)
+ "<br/>Price: " + StackFormatter.quantityToStackSize(price)
+ "<br/>Total: " + StackFormatter.quantityToStackSize(quantity * price) + "</html";
+ "<br/>Total: " + StackFormatter.quantityToStackSize(quantity * price) + "</html>";
}
}

View File

@@ -0,0 +1,302 @@
/*
* Copyright (c) 2018, TheStonedTurtle <https://github.com/TheStonedTurtle>
* 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.stonedtracker.ui;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Iterators;
import com.google.common.collect.Multimap;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.loottracker.localstorage.LTItemEntry;
import net.runelite.client.plugins.loottracker.localstorage.LTRecord;
import net.runelite.client.plugins.stonedtracker.ItemSortTypes;
import net.runelite.client.plugins.stonedtracker.data.UniqueItem;
import net.runelite.client.ui.ColorScheme;
@Slf4j
class LootPanel extends JPanel
{
private final Collection<LTRecord> records;
private final Collection<UniqueItem> uniques;
private final boolean hideUniques;
private final ItemSortTypes sortType;
private final boolean itemBreakdown;
private final ItemManager itemManager;
// Consolidate LTItemEntries stored by ItemID
private final Map<Integer, LTItemEntry> consolidated = new HashMap<>();
private boolean playbackPlaying = false;
private boolean cancelPlayback = false;
LootPanel(
final Collection<LTRecord> records,
final Collection<UniqueItem> uniques,
final boolean hideUnqiues,
final ItemSortTypes sort,
final boolean itemBreakdown,
final ItemManager itemManager)
{
this.records = records;
this.uniques = uniques;
this.hideUniques = hideUnqiues;
this.sortType = sort;
this.itemBreakdown = itemBreakdown;
this.itemManager = itemManager;
setLayout(new GridBagLayout());
setBorder(new EmptyBorder(0, 10, 0, 10));
setBackground(ColorScheme.DARK_GRAY_COLOR);
createPanel(this.records, true);
}
/**
* Sorts the collection of LTItemEntry based on the selected {@link ItemSortTypes}
*
* @param sortType The {@link ItemSortTypes} describing how these entries should be sorted
* @return returns the sorted list
*/
private static Comparator<LTItemEntry> createLTItemEntryComparator(final ItemSortTypes sortType)
{
return (o1, o2) ->
{
switch (sortType)
{
case ITEM_ID:
return o1.getId() - o2.getId();
case PRICE:
if (o1.getPrice() != o2.getPrice())
{
return o1.getPrice() > o2.getPrice() ? -1 : 1;
}
break;
case VALUE:
if (o1.getTotal() != o2.getTotal())
{
return o1.getTotal() > o2.getTotal() ? -1 : 1;
}
break;
case ALPHABETICAL:
// Handled below
break;
default:
log.warn("Sort Type not being handled correctly, defaulting to alphabetical.");
break;
}
// Default to alphabetical
return o1.getName().compareTo(o2.getName());
};
}
private void createPanel(final Collection<LTRecord> records, final boolean reconsolidate)
{
final GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
c.gridx = 0;
c.gridy = 0;
if (reconsolidate)
{
this.consolidated.clear();
final Collection<LTItemEntry> consolidatedLTItemEntries = LTRecord.consolidateLTItemEntries(records);
final Map<Integer, LTItemEntry> itemMap = LTItemEntry.consolidateItemEntires(consolidatedLTItemEntries);
this.consolidated.putAll(itemMap);
}
// Create necessary helpers for the unique toggles
final Multimap<Integer, UniqueItem> positionMap = ArrayListMultimap.create();
final Set<Integer> uniqueIds = new HashSet<>();
// Loop over all UniqueItems and check how many the player has received as a drop for each
// Also add all Item IDs for uniques to a Set for easy hiding later on.
for (final UniqueItem item : this.uniques)
{
final int id = item.getItemID();
final int linkedId = item.getLinkedID();
uniqueIds.add(id);
uniqueIds.add(linkedId);
final LTItemEntry entry = this.consolidated.get(id);
final LTItemEntry notedEntry = this.consolidated.get(linkedId);
final int qty = (entry == null ? 0 : entry.getQuantity()) + (notedEntry == null ? 0 : notedEntry.getQuantity());
item.setQty(qty);
positionMap.put(item.getPosition(), item);
}
for (final int position : positionMap.keySet())
{
final Collection<UniqueItem> uniques = positionMap.get(position);
final UniqueItemPanel p = new UniqueItemPanel(uniques, this.itemManager);
this.add(p, c);
c.gridy++;
}
// Attach Kill Count Panel(s)
if (records.size() > 0)
{
final int amount = records.size();
final LTRecord entry = Iterators.get(records.iterator(), (amount - 1));
if (entry.getKillCount() != -1)
{
final TextPanel p = new TextPanel("Current Killcount:", entry.getKillCount());
this.add(p, c);
c.gridy++;
}
final TextPanel p2 = new TextPanel("Kills Logged:", amount);
this.add(p2, c);
c.gridy++;
}
// Track total price of all tracked items for this panel
// Also ensure it is placed in correct location by preserving its gridy value
long totalValue = 0;
final int totalValueIndex = c.gridy;
c.gridy++;
final Collection<LTItemEntry> itemsToDisplay = consolidated.values().stream()
.filter(e -> !(hideUniques && uniqueIds.contains(e.getId())))
.sorted(createLTItemEntryComparator(sortType))
.collect(Collectors.toList());
if (itemsToDisplay.size() > 0)
{
totalValue = itemsToDisplay.stream().mapToLong(e -> e.getPrice() * e.getQuantity()).sum();
if (itemBreakdown)
{
for (final LTItemEntry e : itemsToDisplay)
{
final ItemPanel p = new ItemPanel(e, itemManager);
this.add(p, c);
c.gridy++;
}
}
else
{
final LootGrid grid = new LootGrid(itemsToDisplay.toArray(new LTItemEntry[0]), itemManager);
this.add(grid, c);
c.gridy++;
}
}
// Only add the total value element if it has something useful to display
if (totalValue > 0)
{
c.gridy = totalValueIndex;
final TextPanel totalPanel = new TextPanel("Total Value:", totalValue);
this.add(totalPanel, c);
}
}
void addedRecord(final LTRecord record)
{
records.add(record);
for (final LTItemEntry entry : record.getDrops())
{
final LTItemEntry current = consolidated.get(entry.getId());
if (current != null)
{
entry.setQuantity(entry.getQuantity() + current.getQuantity());
}
consolidated.put(entry.getId(), entry);
}
// TODO: Smarter update system so it only repaints necessary Item and Text Panels
this.removeAll();
this.createPanel(this.records, false);
this.revalidate();
this.repaint();
}
void playback()
{
if (playbackPlaying)
{
cancelPlayback = true;
return;
}
playbackPlaying = true;
if (this.records.size() > 0)
{
final Collection<LTRecord> recs = new ArrayList<>();
for (final LTRecord r : this.records)
{
recs.add(r);
SwingUtilities.invokeLater(() -> refreshPlayback(recs));
try
{
if (cancelPlayback)
{
playbackPlaying = false;
cancelPlayback = false;
SwingUtilities.invokeLater(() -> refreshPlayback(this.records));
break;
}
// TODO: Allow this rate to be configurable?
Thread.sleep(250);
}
catch (InterruptedException e)
{
System.out.println(e.getMessage());
}
}
}
playbackPlaying = false;
}
private void refreshPlayback(final Collection<LTRecord> recs)
{
this.removeAll();
this.createPanel(recs, true);
this.revalidate();
this.repaint();
}
}

View File

@@ -22,7 +22,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.stonedloottracker.ui;
package net.runelite.client.plugins.stonedtracker.ui;
import java.awt.BorderLayout;
import java.awt.Color;
@@ -30,14 +30,13 @@ import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.imageio.ImageIO;
import javax.inject.Singleton;
import java.util.stream.Collectors;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
@@ -48,16 +47,15 @@ import javax.swing.border.EmptyBorder;
import javax.swing.border.MatteBorder;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.stonedloottracker.StonedLootTrackerPlugin;
import net.runelite.client.plugins.stonedloottracker.data.LootRecordCustom;
import net.runelite.client.plugins.stonedloottracker.data.UniqueItem;
import net.runelite.client.plugins.stonedloottracker.data.UniqueItemPrepared;
import net.runelite.client.plugins.loottracker.localstorage.LTRecord;
import net.runelite.client.plugins.stonedtracker.StonedTrackerPlugin;
import net.runelite.client.plugins.stonedtracker.data.UniqueItem;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.PluginPanel;
import net.runelite.client.ui.components.PluginErrorPanel;
import net.runelite.client.util.ImageUtil;
@Slf4j
@Singleton
public class LootTrackerPanel extends PluginPanel
{
private static final BufferedImage ICON_DELETE;
@@ -70,38 +68,20 @@ public class LootTrackerPanel extends PluginPanel
static
{
BufferedImage i1;
BufferedImage i2;
BufferedImage i3;
BufferedImage i4;
try
{
synchronized (ImageIO.class)
{
i1 = ImageIO.read(StonedLootTrackerPlugin.class.getResourceAsStream("delete-white.png"));
i2 = ImageIO.read(StonedLootTrackerPlugin.class.getResourceAsStream("refresh-white.png"));
i3 = ImageIO.read(StonedLootTrackerPlugin.class.getResourceAsStream("back-arrow-white.png"));
i4 = ImageIO.read(StonedLootTrackerPlugin.class.getResourceAsStream("replay-white.png"));
}
}
catch (IOException e)
{
throw new RuntimeException(e);
}
ICON_DELETE = i1;
ICON_REFRESH = i2;
ICON_BACK = i3;
ICON_REPLAY = i4;
ICON_DELETE = ImageUtil.getResourceStreamFromClass(StonedTrackerPlugin.class, "delete-white.png");
ICON_REFRESH = ImageUtil.getResourceStreamFromClass(StonedTrackerPlugin.class, "refresh-white.png");
ICON_BACK = ImageUtil.getResourceStreamFromClass(StonedTrackerPlugin.class, "back-arrow-white.png");
ICON_REPLAY = ImageUtil.getResourceStreamFromClass(StonedTrackerPlugin.class, "replay-white.png");
}
private final ItemManager itemManager;
private final StonedTrackerPlugin plugin;
// NPC name for current view or null if on selection screen
private String currentView = null;
private LootPanel lootPanel;
private final ItemManager itemManager;
private final StonedLootTrackerPlugin plugin;
public LootTrackerPanel(final ItemManager itemManager, final StonedLootTrackerPlugin plugin)
public LootTrackerPanel(final ItemManager itemManager, final StonedTrackerPlugin plugin)
{
super(false);
this.itemManager = itemManager;
@@ -114,17 +94,17 @@ public class LootTrackerPanel extends PluginPanel
}
// Loot Selection view
private void showSelectionView()
public void showSelectionView()
{
this.removeAll();
currentView = null;
lootPanel = null;
PluginErrorPanel errorPanel = new PluginErrorPanel();
final PluginErrorPanel errorPanel = new PluginErrorPanel();
errorPanel.setBorder(new EmptyBorder(10, 25, 10, 25));
errorPanel.setContent("Stoned Loot Tracker", "Please select the Activity, Player, or NPC you wish to view loot for");
errorPanel.setContent("Loot Tracker", "Please select the Activity, Player, or NPC you wish to view loot for");
SelectionPanel selection = new SelectionPanel(plugin.config.bossButtons(), plugin.getNames(), this, itemManager);
final SelectionPanel selection = new SelectionPanel(plugin.config.bossButtons(), plugin.getLootNames(), this, itemManager);
this.add(errorPanel, BorderLayout.NORTH);
this.add(wrapContainer(selection), BorderLayout.CENTER);
@@ -134,22 +114,24 @@ public class LootTrackerPanel extends PluginPanel
}
// Loot breakdown view
void showLootView(String name)
public void showLootView(final String name)
{
this.removeAll();
currentView = name;
Collection<LootRecordCustom> data = plugin.getDataByName(name);
final Collection<LTRecord> data = plugin.getDataByName(name);
// Grab all Uniques for this NPC/Activity
Collection<UniqueItemPrepared> uniques = plugin.getUniques(name);
Collection<UniqueItem> uniques = UniqueItem.getUniquesForBoss(name);
if (uniques == null)
{
uniques = new ArrayList<>();
}
JPanel title = createLootViewTitle(name);
lootPanel = new LootPanel(data, UniqueItem.createPositionSetMap(uniques), plugin.config.hideUniques(), plugin.config.itemSortType(), plugin.config.itemBreakdown(), itemManager);
uniques = uniques.stream().sorted(Comparator.comparingInt(UniqueItem::getPosition)).collect(Collectors.toList());
final JPanel title = createLootViewTitle(name);
lootPanel = new LootPanel(data, uniques, plugin.config.hideUniques(), plugin.config.itemSortType(), plugin.config.itemBreakdown(), itemManager);
this.add(title, BorderLayout.NORTH);
this.add(wrapContainer(lootPanel), BorderLayout.CENTER);
@@ -158,16 +140,10 @@ public class LootTrackerPanel extends PluginPanel
this.repaint();
}
private LootTrackerBox createLootTrackerBox(LootRecordCustom r)
{
final String subTitle = r.getLevel() > -1 ? "(lvl-" + r.getLevel() + ")" : "";
return new LootTrackerBox(itemManager, r.getName(), subTitle, r);
}
// Title element for Loot breakdown view
private JPanel createLootViewTitle(String name)
private JPanel createLootViewTitle(final String name)
{
JPanel title = new JPanel();
final JPanel title = new JPanel();
title.setBorder(new CompoundBorder(
new EmptyBorder(10, 8, 8, 8),
new MatteBorder(0, 0, 1, 0, Color.GRAY)
@@ -175,11 +151,11 @@ public class LootTrackerPanel extends PluginPanel
title.setLayout(new BorderLayout());
title.setBackground(BACKGROUND_COLOR);
JPanel first = new JPanel();
final JPanel first = new JPanel();
first.setBackground(BACKGROUND_COLOR);
// Back Button
JLabel back = createIconLabel(ICON_BACK);
final JLabel back = createIconLabel(ICON_BACK);
back.addMouseListener(new MouseAdapter()
{
@Override
@@ -188,31 +164,31 @@ public class LootTrackerPanel extends PluginPanel
showSelectionView();
}
});
back.setToolTipText("Back to selection screen");
// Plugin Name
JLabel text = new JLabel(name);
final JLabel text = new JLabel(name);
text.setForeground(Color.WHITE);
first.add(back);
first.add(text);
JPanel second = new JPanel();
final JPanel second = new JPanel();
second.setBackground(BACKGROUND_COLOR);
// Refresh Data button
JLabel refresh = createIconLabel(ICON_REFRESH);
final JLabel refresh = createIconLabel(ICON_REFRESH);
refresh.addMouseListener(new MouseAdapter()
{
@Override
public void mouseClicked(MouseEvent e)
{
refreshLootView(name);
showLootView(name);
}
});
refresh.setToolTipText("Refresh panel");
// Clear data button
JLabel clear = createIconLabel(ICON_DELETE);
final JLabel clear = createIconLabel(ICON_DELETE);
clear.addMouseListener(new MouseAdapter()
{
@Override
@@ -224,7 +200,7 @@ public class LootTrackerPanel extends PluginPanel
clear.setToolTipText("Clear stored data");
// Clear data button
JLabel replay = createIconLabel(ICON_REPLAY);
final JLabel replay = createIconLabel(ICON_REPLAY);
replay.addMouseListener(new MouseAdapter()
{
@Override
@@ -245,9 +221,9 @@ public class LootTrackerPanel extends PluginPanel
return title;
}
private JLabel createIconLabel(BufferedImage icon)
private JLabel createIconLabel(final BufferedImage icon)
{
JLabel label = new JLabel();
final JLabel label = new JLabel();
label.setIcon(new ImageIcon(icon));
label.setOpaque(true);
label.setBackground(BACKGROUND_COLOR);
@@ -271,13 +247,13 @@ public class LootTrackerPanel extends PluginPanel
}
// Wrap the panel inside a scroll pane
private JScrollPane wrapContainer(JPanel container)
private JScrollPane wrapContainer(final JPanel container)
{
JPanel wrapped = new JPanel(new BorderLayout());
final JPanel wrapped = new JPanel(new BorderLayout());
wrapped.add(container, BorderLayout.NORTH);
wrapped.setBackground(BACKGROUND_COLOR);
JScrollPane scroller = new JScrollPane(wrapped);
final JScrollPane scroller = new JScrollPane(wrapped);
scroller.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scroller.getVerticalScrollBar().setPreferredSize(new Dimension(8, 0));
scroller.setBackground(BACKGROUND_COLOR);
@@ -286,42 +262,33 @@ public class LootTrackerPanel extends PluginPanel
}
// Clear stored data and return to selection screen
private void clearData(String name)
private void clearData(final String name)
{
// Confirm delete action
int delete = JOptionPane.showConfirmDialog(this.getRootPane(), "<html>Are you sure you want to clear all data for this tab?<br/>There is no way to undo this action.</html>", "Warning", JOptionPane.YES_NO_OPTION);
final int delete = JOptionPane.showConfirmDialog(this.getRootPane(), "<html>Are you sure you want to clear all data for this tab?<br/>There is no way to undo this action.</html>", "Warning", JOptionPane.YES_NO_OPTION);
if (delete == JOptionPane.YES_OPTION)
{
plugin.clearStoredDataByName(name);
boolean deleted = plugin.clearStoredDataByName(name);
if (!deleted)
{
JOptionPane.showMessageDialog(this.getRootPane(), "Unable to clear stored data, please try again.");
return;
}
// Return to selection screen
showSelectionView();
}
}
public void addLog(LootRecordCustom r)
public void addLog(final LTRecord r)
{
if (currentView == null)
{
showLootView(r.getName());
}
else if (currentView.equals(r.getName()))
else if (currentView.equalsIgnoreCase(r.getName()))
{
//lootPanel.addedRecord(r);
lootPanel.addedRecord();
}
}
// Refresh panel when writer playerFolder is updated
public void updateNames()
{
if (currentView == null)
{
showSelectionView();
}
else
{
showLootView(currentView);
lootPanel.addedRecord(r);
}
}
@@ -339,12 +306,6 @@ public class LootTrackerPanel extends PluginPanel
}
}
private void refreshLootView(String name)
{
plugin.refreshDataByName(name);
showLootView(name); // Recreate the entire panel
}
private void playbackLoot()
{
if (lootPanel == null)
@@ -352,13 +313,8 @@ public class LootTrackerPanel extends PluginPanel
return;
}
if (lootPanel.isPlaybackPlaying())
{
lootPanel.cancelPlayback();
return;
}
ScheduledExecutorService ex = Executors.newSingleThreadScheduledExecutor();
// Create a new thread for this so it doesn't cause swing freezes
final ScheduledExecutorService ex = Executors.newSingleThreadScheduledExecutor();
if (currentView != null)
{
ex.schedule(lootPanel::playback, 0, TimeUnit.SECONDS);

View File

@@ -22,8 +22,9 @@
* (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.stonedloottracker.ui;
package net.runelite.client.plugins.stonedtracker.ui;
import com.google.common.base.Strings;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
@@ -33,37 +34,49 @@ import java.awt.Image;
import java.awt.Insets;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.List;
import java.util.Arrays;
import java.util.Collection;
import java.util.Set;
import java.util.TreeSet;
import javax.inject.Singleton;
import java.util.stream.Collectors;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import net.runelite.client.game.AsyncBufferedImage;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.stonedloottracker.data.BossTab;
import net.runelite.client.plugins.stonedtracker.data.BossTab;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.PluginPanel;
import net.runelite.client.ui.components.IconTextField;
import net.runelite.client.ui.components.materialtabs.MaterialTab;
import net.runelite.client.ui.components.materialtabs.MaterialTabGroup;
import org.apache.commons.text.similarity.JaroWinklerDistance;
@Singleton
class SelectionPanel extends JPanel
{
private final static Color BACKGROUND_COLOR = ColorScheme.DARK_GRAY_COLOR;
private final static Color BUTTON_COLOR = ColorScheme.DARKER_GRAY_COLOR;
private final static Color BUTTON_HOVER_COLOR = ColorScheme.DARKER_GRAY_HOVER_COLOR;
private static final JaroWinklerDistance DISTANCE = new JaroWinklerDistance();
private final Set<String> names;
private final LootTrackerPanel parent;
private final ItemManager itemManager;
private final static Color BACKGROUND_COLOR = ColorScheme.DARK_GRAY_COLOR;
private final static Color BUTTON_COLOR = ColorScheme.DARKER_GRAY_COLOR;
private final static Color BUTTON_HOVER_COLOR = ColorScheme.DARKER_GRAY_HOVER_COLOR;
private final IconTextField searchBar = new IconTextField();
private final JPanel namePanel = new JPanel();
private final boolean configToggle;
SelectionPanel(final boolean configToggle, final Set<String> names, final LootTrackerPanel parent, final ItemManager itemManager)
SelectionPanel(
final boolean configToggle,
final Set<String> names,
final LootTrackerPanel parent,
final ItemManager itemManager)
{
this.names = names == null ? new TreeSet<>() : names;
this.parent = parent;
@@ -73,46 +86,121 @@ class SelectionPanel extends JPanel
this.setLayout(new GridBagLayout());
this.setBackground(BACKGROUND_COLOR);
searchBar.setIcon(IconTextField.Icon.SEARCH);
searchBar.setPreferredSize(new Dimension(PluginPanel.PANEL_WIDTH - 20, 30));
searchBar.setBackground(ColorScheme.DARKER_GRAY_COLOR);
searchBar.setHoverBackgroundColor(ColorScheme.DARK_GRAY_HOVER_COLOR);
searchBar.getDocument().addDocumentListener(new DocumentListener()
{
@Override
public void insertUpdate(DocumentEvent e)
{
onSearchBarChanged();
}
@Override
public void removeUpdate(DocumentEvent e)
{
onSearchBarChanged();
}
@Override
public void changedUpdate(DocumentEvent e)
{
onSearchBarChanged();
}
});
namePanel.setLayout(new GridBagLayout());
createPanel();
}
private static boolean matchesSearchTerm(final String name, final String[] terms)
{
for (final String term : terms)
{
if (Arrays.stream(name.toLowerCase().split(" ")).noneMatch((nameTerm ->
nameTerm.contains(term) || DISTANCE.apply(nameTerm, term) > 0.9)))
{
return false;
}
}
return true;
}
private void createPanel()
{
GridBagConstraints c = constrains();
final GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
c.gridx = 0;
c.gridy = 0;
c.insets = new Insets(5, 0, 0, 0);
// Add the bosses tabs, by category, to tabGroup
if (configToggle)
{
Set<String> categories = BossTab.categories;
JPanel container = new JPanel(new GridBagLayout());
container.setBorder(new EmptyBorder(0, 0, 10, 0));
int oldc = c.gridy;
c.gridy = 0;
for (String categoryName : categories)
{
container.add(createTabCategory(categoryName), c);
c.gridy++;
}
c.gridy = oldc;
this.add(container, c);
this.add(createBossTabPanel(), c);
c.gridy++;
}
// Add all other names
for (String name : this.names)
this.add(searchBar, c);
c.gridy++;
addNamesToPanel(this.names);
this.add(namePanel, c);
}
private JPanel createBossTabPanel()
{
final GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
c.gridx = 0;
c.gridy = 0;
c.insets = new Insets(5, 0, 0, 0);
final JPanel container = new JPanel(new GridBagLayout());
container.setBorder(new EmptyBorder(0, 0, 10, 0));
for (final String categoryName : BossTab.getCategories())
{
container.add(createTabCategory(categoryName), c);
c.gridy++;
}
return container;
}
private void addNamesToPanel(final Collection<String> names)
{
namePanel.removeAll();
final GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
c.gridx = 0;
c.gridy = 0;
c.insets = new Insets(5, 0, 0, 0);
for (final String name : names)
{
// Ignore boss tabs based on config
if (!configToggle || BossTab.getByName(name) == null)
{
this.add(createNamePanel(name), c);
namePanel.add(createNamePanel(name), c);
c.gridy++;
}
}
namePanel.revalidate();
}
private JPanel createNamePanel(String name)
private JPanel createNamePanel(final String name)
{
JPanel p = new JPanel();
final JPanel p = new JPanel();
p.add(new JLabel(name));
p.setBackground(BUTTON_COLOR);
p.addMouseListener(new MouseAdapter()
@@ -132,47 +220,40 @@ class SelectionPanel extends JPanel
@Override
public void mouseClicked(MouseEvent e)
{
SwingUtilities.invokeLater(() -> parent.showLootView(name));
parent.showLootView(name);
}
});
return p;
}
static GridBagConstraints constrains()
// Creates all tabs for a specific category
private JPanel createTabCategory(final String categoryName)
{
GridBagConstraints c = new GridBagConstraints();
final JPanel container = new JPanel();
container.setLayout(new GridBagLayout());
container.setBorder(new EmptyBorder(0, 5, 0, 5));
final GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
c.gridx = 0;
c.gridy = 0;
return c;
}
// Creates all tabs for a specific category
private JPanel createTabCategory(String categoryName)
{
JPanel container = new JPanel();
container.setLayout(new GridBagLayout());
container.setBorder(new EmptyBorder(0, 5, 0, 5));
GridBagConstraints c = constrains();
MaterialTabGroup thisTabGroup = new MaterialTabGroup();
final MaterialTabGroup thisTabGroup = new MaterialTabGroup();
thisTabGroup.setLayout(new GridLayout(0, 4, 7, 7));
thisTabGroup.setBorder(new EmptyBorder(4, 0, 0, 0));
JLabel name = new JLabel(categoryName);
final JLabel name = new JLabel(categoryName);
name.setBorder(new EmptyBorder(8, 0, 0, 0));
name.setForeground(Color.WHITE);
name.setVerticalAlignment(SwingConstants.CENTER);
List<BossTab> categoryTabs = BossTab.getByCategoryName(categoryName);
for (BossTab tab : categoryTabs)
final Collection<BossTab> categoryTabs = BossTab.getByCategoryName(categoryName);
for (final BossTab tab : categoryTabs)
{
// Create tab (with hover effects/text)
MaterialTab materialTab = new MaterialTab("", thisTabGroup, null);
final MaterialTab materialTab = new MaterialTab("", thisTabGroup, null);
materialTab.setName(tab.getName());
materialTab.setToolTipText(tab.getName());
//materialTab.setSelectedBorder(new EmptyBorder(0, 0, 0, 0));
@@ -189,17 +270,17 @@ class SelectionPanel extends JPanel
{
materialTab.setBackground(BUTTON_COLOR);
}
});
@Override
public void mouseClicked(MouseEvent e)
{
SwingUtilities.invokeLater(() -> parent.showLootView(tab.getName()));
}
materialTab.setOnSelectEvent(() ->
{
parent.showLootView(tab.getName());
return true;
});
// Attach Icon to the Tab
AsyncBufferedImage image = itemManager.getImage(tab.getItemID());
Runnable resize = () ->
final AsyncBufferedImage image = itemManager.getImage(tab.getItemID());
final Runnable resize = () ->
{
materialTab.setIcon(new ImageIcon(image.getScaledInstance(35, 35, Image.SCALE_SMOOTH)));
materialTab.setOpaque(true);
@@ -220,4 +301,23 @@ class SelectionPanel extends JPanel
return container;
}
private void onSearchBarChanged()
{
final String text = searchBar.getText();
if (Strings.isNullOrEmpty(text))
{
addNamesToPanel(this.names);
}
else
{
addNamesToPanel(filterNames(this.names, text));
}
}
private Collection<String> filterNames(final Collection<String> names, final String searchText)
{
final String[] searchTerms = searchText.toLowerCase().split(" ");
return names.stream().filter(name -> matchesSearchTerm(name, searchTerms)).collect(Collectors.toList());
}
}

View File

@@ -22,26 +22,22 @@
* (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.stonedloottracker.ui;
package net.runelite.client.plugins.stonedtracker.ui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.inject.Singleton;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.util.StackFormatter;
@Singleton
@Getter(AccessLevel.PACKAGE)
@Getter
class TextPanel extends JPanel
{
private static final GridBagLayout LAYOUT = new GridBagLayout();
@@ -52,26 +48,26 @@ class TextPanel extends JPanel
private static final Border CONTAINER_BORDER = BorderFactory.createMatteBorder(0, 15, 0, 15, PANEL_BACKGROUND_COLOR);
// Long value should be for Total Value
TextPanel(String text, long totalValue)
TextPanel(final String text, final long totalValue)
{
this.setLayout(LAYOUT);
this.setBorder(PANEL_BORDER);
this.setBackground(PANEL_BACKGROUND_COLOR);
JLabel totalText = new JLabel(text, SwingConstants.LEFT);
final JLabel totalText = new JLabel(text, SwingConstants.LEFT);
totalText.setForeground(Color.WHITE);
// Item Values (Colored off Total Value of item)
JLabel total = new JLabel(StackFormatter.quantityToStackSize(totalValue) + " gp", SwingConstants.LEFT);
total.setBorder(new EmptyBorder(0, 5, 0, 0));
colorLabel(total, totalValue);
final JLabel total = new JLabel(StackFormatter.quantityToStackSize(totalValue) + " gp", SwingConstants.LEFT);
total.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0));
total.setForeground(getRSValueColor(totalValue));
JPanel panel = ItemPanel.createPanel();
final JPanel panel = createPanel();
panel.add(totalText, BorderLayout.LINE_START);
panel.add(total, BorderLayout.CENTER);
GridBagConstraints c = new GridBagConstraints();
final GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.weightx = 1;
c.gridx = 0;
@@ -83,23 +79,23 @@ class TextPanel extends JPanel
this.add(panel, c);
}
TextPanel(String text, int value)
TextPanel(final String text, final int value)
{
this.setLayout(LAYOUT);
this.setBorder(PANEL_BORDER);
this.setBackground(PANEL_BACKGROUND_COLOR);
JLabel textLabel = new JLabel(text, SwingConstants.LEFT);
final JLabel textLabel = new JLabel(text, SwingConstants.LEFT);
textLabel.setForeground(Color.WHITE);
JLabel valueLabel = new JLabel(String.valueOf(value), SwingConstants.LEFT);
valueLabel.setBorder(new EmptyBorder(0, 5, 0, 0));
final JLabel valueLabel = new JLabel(String.valueOf(value), SwingConstants.LEFT);
valueLabel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0));
JPanel panel = ItemPanel.createPanel();
final JPanel panel = createPanel();
panel.add(textLabel, BorderLayout.LINE_START);
panel.add(valueLabel, BorderLayout.CENTER);
GridBagConstraints c = new GridBagConstraints();
final GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.weightx = 1;
c.gridx = 0;
@@ -109,10 +105,18 @@ class TextPanel extends JPanel
this.add(panel, c);
}
// Color label to match RuneScape coloring
private void colorLabel(JLabel label, long val)
private static JPanel createPanel()
{
Color labelColor = (val >= 10000000) ? Color.GREEN : (val >= 100000) ? Color.WHITE : Color.YELLOW;
label.setForeground(labelColor);
final JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.setBorder(CONTAINER_BORDER);
panel.setBackground(PANEL_BACKGROUND_COLOR);
return panel;
}
private static Color getRSValueColor(long val)
{
return (val >= 10000000) ? Color.GREEN : (val >= 100000) ? Color.WHITE : Color.YELLOW;
}
}

View File

@@ -22,7 +22,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.stonedloottracker.ui;
package net.runelite.client.plugins.stonedtracker.ui;
import java.awt.BorderLayout;
import java.awt.Color;
@@ -31,29 +31,23 @@ import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.image.BufferedImage;
import java.util.Collection;
import java.util.Map;
import javax.inject.Singleton;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingConstants;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.client.game.AsyncBufferedImage;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.stonedloottracker.data.UniqueItemPrepared;
import net.runelite.client.plugins.stonedtracker.data.UniqueItem;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.util.ImageUtil;
import net.runelite.client.util.StackFormatter;
@Singleton
@Getter(AccessLevel.PACKAGE)
@Getter
class UniqueItemPanel extends JPanel
{
private ItemManager itemManager;
private final static float alphaMissing = 0.35f;
private final static float alphaHas = 1.0f;
@@ -61,11 +55,9 @@ class UniqueItemPanel extends JPanel
private static final Border panelBorder = new EmptyBorder(3, 0, 3, 0);
private static final Color panelBackgroundColor = ColorScheme.DARK_GRAY_COLOR;
UniqueItemPanel(final Collection<UniqueItemPrepared> items, final Map<UniqueItemPrepared, Integer> uniqueMap, final ItemManager itemManager)
UniqueItemPanel(final Collection<UniqueItem> items, final ItemManager itemManager)
{
this.itemManager = itemManager;
JPanel panel = new JPanel();
final JPanel panel = new JPanel();
panel.setLayout(new GridBagLayout());
panel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
panel.setBorder(new EmptyBorder(3, 0, 3, 0));
@@ -83,68 +75,52 @@ class UniqueItemPanel extends JPanel
c.ipady = 5;
// Add each Unique Item icon to the panel
for (UniqueItemPrepared l : items)
for (final UniqueItem l : items)
{
final int id = l.getUniqueItem().getItemID();
final int quantity = uniqueMap.getOrDefault(l, 0);
final int quantity = l.getQty();
final float alpha = (quantity > 0 ? alphaHas : alphaMissing);
AsyncBufferedImage image = itemManager.getImage(l.getUniqueItem().getItemID(), quantity, quantity > 1);
BufferedImage opaque = ImageUtil.alphaOffset(image, alpha);
final AsyncBufferedImage image = itemManager.getImage(l.getItemID(), quantity, quantity > 1);
final BufferedImage opaque = ImageUtil.alphaOffset(image, alpha);
final long price = itemManager.getItemPrice(id);
final long haPrice = itemManager.getAlchValue(id);
// Attach Image to Label and append label to Panel
ImageIcon o = new ImageIcon(opaque);
JLabel icon = new JLabel(o);
final JLabel icon = new JLabel();
icon.setToolTipText(buildToolTip(l, quantity));
icon.setIcon(new ImageIcon(opaque));
icon.setVerticalAlignment(SwingConstants.CENTER);
icon.setHorizontalAlignment(SwingConstants.CENTER);
panel.add(icon, c);
c.gridx++;
// in case the image is blank we will refresh it upon load
// Should only trigger if image hasn't been added
image.onChanged(() -> SwingUtilities.invokeLater(() -> refreshImage(icon, image, alpha)));
icon.setToolTipText(tooltipText(price, haPrice, quantity, l.getUniqueItem().getName()));
image.onChanged(() ->
{
icon.setIcon(new ImageIcon(ImageUtil.alphaOffset(image, alpha)));
icon.revalidate();
icon.repaint();
});
}
this.add(panel, BorderLayout.NORTH);
}
static String tooltipText(long price, long haPrice, int quantity, String name)
private static String buildToolTip(final UniqueItem item, final int qty)
{
String tooltipText = "<html>" + name;
if (quantity > 1)
String s = "<html>" + item.getName();
if (qty > 0)
{
tooltipText += "<br/>Price: " + StackFormatter.quantityToStackSize(price);
if (haPrice > 0)
s += " x " + StackFormatter.formatNumber(qty);
}
if (item.getPrice() > 0)
{
s += "<br/>Price: " + StackFormatter.quantityToStackSize(item.getPrice());
// Check for qty here as well as we should only show Total if the item has a value as well
if (qty > 0)
{
tooltipText += " (HA: " + StackFormatter.quantityToStackSize(haPrice) + ")";
s += "<br/>Total: " + StackFormatter.quantityToStackSize(qty * item.getPrice()) + "</html";
}
}
s += "</html>";
if (quantity > 0)
{
tooltipText += "<br/>Total: " + StackFormatter.quantityToStackSize(quantity * price);
if (haPrice > 0)
{
tooltipText += " (HA: " + StackFormatter.quantityToStackSize(quantity * haPrice) + ")";
}
}
tooltipText += "</html";
return tooltipText;
}
// Used to refresh the item icon if the image was still loading when attempting to create it earlier
private void refreshImage(JLabel label, AsyncBufferedImage image, float finalAlpha)
{
BufferedImage opaque = ImageUtil.alphaOffset(image, finalAlpha);
ImageIcon o = new ImageIcon(opaque);
label.setIcon(o);
label.revalidate();
label.repaint();
return s;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B