diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/BankedCalculator.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/BankedCalculator.java new file mode 100644 index 0000000000..a0f0da78a5 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/BankedCalculator.java @@ -0,0 +1,666 @@ +/* + * Copyright (c) 2018, 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.skillcalculator; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.Experience; +import net.runelite.api.Skill; +import net.runelite.client.game.ItemManager; +import net.runelite.client.plugins.skillcalculator.banked.CriticalItem; +import net.runelite.client.plugins.skillcalculator.banked.beans.Activity; +import net.runelite.client.plugins.skillcalculator.banked.beans.SecondaryItem; +import net.runelite.client.plugins.skillcalculator.banked.ui.CriticalItemPanel; +import net.runelite.client.plugins.skillcalculator.beans.SkillDataBonus; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.DynamicGridLayout; +import net.runelite.client.ui.FontManager; + +@Slf4j +public class BankedCalculator extends JPanel +{ + private static final DecimalFormat XP_FORMAT_COMMA = new DecimalFormat("#,###.#"); + + private final SkillCalculatorPanel parent; + private final Client client; + private final UICalculatorInputArea uiInput; + private final SkillCalculatorConfig config; + private final ItemManager itemManager; + + private final CacheSkillData skillData = new CacheSkillData(); + private final List bonusCheckBoxes = new ArrayList<>(); + + // UI Input data + private float xpFactor = 1.0f; + private CalculatorType currentCalc; + private Skill currentSkill; + + private double totalBankedXp = 0.0f; + private JLabel totalLabel = new JLabel(); + private JPanel detailConfigContainer; + private JPanel detailContainer; + + // Banked Experience magic + private Map bankMap = new HashMap<>(); + private Map categoryMap = new HashMap<>(); // Check if CriticalItem Category is enabled + private Map panelMap = new HashMap<>(); + private Map criticalMap = new HashMap<>(); // Quantity of CriticalItem inside bankMap + private Map activityMap = new HashMap<>(); // Selected Activity used for calculating xp + private Map linkedMap = new HashMap<>(); // ItemID of item that links to the CriticalItem + + BankedCalculator( + SkillCalculatorPanel parent, + Client client, + UICalculatorInputArea uiInput, + SkillCalculatorConfig config, + ItemManager itemManager) + { + this.parent = parent; + this.client = client; + this.uiInput = uiInput; + this.config = config; + this.itemManager = itemManager; + + setLayout(new DynamicGridLayout(0, 1, 0, 5)); + + detailContainer = new JPanel(); + detailContainer.setLayout(new BoxLayout(detailContainer, BoxLayout.Y_AXIS)); + + detailConfigContainer = new JPanel(); + detailConfigContainer.setLayout(new BoxLayout(detailConfigContainer, BoxLayout.Y_AXIS)); + } + + private void reset() + { + criticalMap.clear(); + linkedMap.clear(); + xpFactor = 1f; + } + + /** + * Update target Xp and Level inputs to match current Xp + total banked XP + */ + private void syncInputFields() + { + // Update Target XP & Level to include total banked xp + int newTotal = (int) (uiInput.getCurrentXPInput() + totalBankedXp); + uiInput.setTargetXPInput(newTotal); + uiInput.setTargetLevelInput(Experience.getLevelForXp(newTotal)); + } + + /* + * Banked Experience Logic + */ + + /** + * Shows the Banked Xp tab for the CalculatorType + * @param calculatorType Selected Calculator Type + */ + void openBanked(CalculatorType calculatorType) + { + // clean slate for creating the required panel + removeAll(); + reset(); + if (calculatorType.getSkill() != currentSkill) + { + // Only clear Category and Activity map on skill change. + activityMap.clear(); + categoryMap.clear(); + } + currentCalc = calculatorType; + currentSkill = calculatorType.getSkill(); + bankMap = parent.getBankMap(); + + uiInput.setCurrentLevelInput(client.getRealSkillLevel(currentSkill)); + uiInput.setCurrentXPInput(client.getSkillExperience(currentSkill)); + + // Only adds Banked Experience portion if enabled for this SkillCalc and have seen their bank + if (!calculatorType.isBankedXpFlag()) + { + add(new JLabel("
Banked Experience is not enabled for this skill.
", JLabel.CENTER)); + } + else if (bankMap.size() <= 0) + { + add(new JLabel( "Please visit a bank!", JLabel.CENTER)); + } + else + { + // Prevent editing of the target level/exp since we automagically adjust them + uiInput.getUiFieldTargetLevel().setEditable(false); + uiInput.getUiFieldTargetXP().setEditable(false); + + // Now we can actually show the Banked Experience Panel + // Adds Config Options for this panel + renderBankedXpOptions(); + + renderBonusXpOptions(); + + // sprite 202 + calculatedBankedMaps(); + + // Calculate total banked experience and create detail container + refreshDetailContainer(); + + // Add back all necessary content + add(detailConfigContainer); + add(totalLabel); + add(detailContainer); + } + + revalidate(); + repaint(); + + // Update the input fields. + syncInputFields(); + } + + /** + * Add the config options for toggling each Item Category + */ + private void renderBankedXpOptions() + { + Set categories = CriticalItem.getSkillCategories(currentSkill); + if (categories == null) + { + return; + } + + add(new JLabel("Configs:")); + + for (String category : categories) + { + JPanel uiOption = new JPanel(new BorderLayout()); + JLabel uiLabel = new JLabel(category); + JCheckBox uiCheckbox = new JCheckBox(); + + uiLabel.setForeground(Color.WHITE); + uiLabel.setFont(FontManager.getRunescapeSmallFont()); + + uiOption.setBorder(BorderFactory.createEmptyBorder(3, 7, 3, 0)); + uiOption.setBackground(ColorScheme.DARKER_GRAY_COLOR); + + // Everything is enabled by default + uiCheckbox.setSelected(true); + categoryMap.put(category, true); + + // Adjust Total Banked XP check-state of the box. + uiCheckbox.addActionListener(e -> toggleCategory(category, uiCheckbox.isSelected())); + uiCheckbox.setBackground(ColorScheme.MEDIUM_GRAY_COLOR); + + uiOption.add(uiLabel, BorderLayout.WEST); + uiOption.add(uiCheckbox, BorderLayout.EAST); + + add(uiOption); + add(Box.createRigidArea(new Dimension(0, 5))); + } + } + + /** + * Used to toggle Categories of Items inside the Banked Xp tab + * @param category Category Name + * @param enabled is enabled + */ + private void toggleCategory(String category, boolean enabled) + { + categoryMap.put(category, enabled); + refreshDetailContainer(); + } + + + /** + * Creates the Maps used for easy access when calculating Banked Xp + */ + private void calculatedBankedMaps() + { + // Grab all CriticalItems for this skill + ArrayList items = CriticalItem.getBySkillName(currentSkill); + + // Loop over all Critical Items for this skill and determine how many are in the bank + for (CriticalItem item : items) + { + Integer qty = bankMap.get(item.getItemID()); + if (qty != null && qty > 0) + { + if (criticalMap.containsKey(item)) + { + criticalMap.put(item, criticalMap.get(item) + qty); + } + else + { + criticalMap.put(item, qty); + } + + // Ensure the item this is linked to maps back to us. + if (item.getLinkedItemId() != -1) + { + CriticalItem i = CriticalItem.getByItemId(item.getLinkedItemId()); + if (i != null) + { + linkedMap.put(i, item.getItemID()); + } + } + } + } + } + + /** + * Populates the detailContainer with the necessary CriticalItemPanels + */ + private void refreshDetailContainer() + { + detailContainer.removeAll(); + panelMap.clear(); + + Map map = getBankedXpBreakdown(); + for (Map.Entry entry : map.entrySet()) + { + CriticalItem item = entry.getKey(); + createItemPanel(item); + } + + detailContainer.revalidate(); + detailContainer.repaint(); + + calculateBankedXpTotal(); + } + + /** + * Creates an Individual Item Panel if it should be displayed + * @param item CriticalItem this information is tied too + */ + private void createItemPanel(CriticalItem item) + { + // Category Included? + if (categoryMap.get(item.getCategory())) + { + // Get possible activities limited to current level + List activities = Activity.getByCriticalItem(item, uiInput.getCurrentLevelInput()); + + // Check if this should count as another item. + if (item.getLinkedItemId() != -1) + { + // Ensure the linked item panel is created even if there are none in bank. + CriticalItem linked = CriticalItem.getByItemId(item.getLinkedItemId()); + if (!criticalMap.containsKey(linked)) + { + createItemPanel(linked); + } + + // One activity and rewards no xp ignore. + if (activities.size() == 1) + { + if (activities.get(0).getXp() <= 0) + { + return; + } + } + } + + // If it doesn't have any activities ignore it in the breakdown. + if (activities.size() <= 0) + { + return; + } + // Either this item has multiple activities or the single activity rewards xp, create the item panel. + + // Determine xp rate for this item + Activity a = getSelectedActivity(item); + double activityXp = a == null ? 0 : a.getXp(); + double xp = activityXp * (item.isIgnoreBonus() ? 1.0f : xpFactor); + int amount = 0; + + // If it has linked items figure out the working total. + Map links = getLinkedTotalMap(item); + for (Integer num : links.values()) + { + amount += num; + } + + // Actually create the panel displaying banked experience for this item + CriticalItemPanel panel = new CriticalItemPanel(this, itemManager, item, xp, amount, links); + + // Limit to Banked Secondaries + if (config.limitedBankedSecondaries() && a != null) + { + panel.updateAmount(limitToActivitySecondaries(a, amount), true); + panel.recalculate(); + } + panelMap.put(item, panel); + detailContainer.add(panel); + } + + } + + /** + * Return the Activity the player selected for this Item. Defaults to First activity + * @param i CriticalItem to check for + * @return selected Activity + */ + public Activity getSelectedActivity(CriticalItem i) + { + // Pull from memory if available + Activity a = activityMap.get(i); + if (a != null) + { + return a; + } + + // If not in memory select the first Activity and add to memory + List activities = Activity.getByCriticalItem(i); + if (activities.size() == 0) + { + // If you can't find an activity it means this item must link to one and give 0 xp + return null; + } + + Activity selected = activities.get(0); + activityMap.put(i, selected); + return selected; + } + + /** + * Creates a Map of Item ID and QTY for this Skill by Category. Keeps order for better UI display + * @return Map of Item ID and QTY for this Skill by Category + */ + private Map getBankedXpBreakdown() + { + Map map = new LinkedHashMap<>(); + + for (String category : CriticalItem.getSkillCategories(currentSkill)) + { + ArrayList items = CriticalItem.getItemsForSkillCategories(currentSkill, category); + for (CriticalItem item : items) + { + Integer amount = bankMap.get(item.getItemID()); + if (amount != null && amount > 0) + { + map.put(item, amount); + } + } + } + + return map; + } + + /** + * Used to select an Activity for an item + * @param i CriticalItem + * @param a Activity selected + */ + public void activitySelected(CriticalItem i, Activity a) + { + // This is triggered on every click so don't update if activity didn't actually change + Activity cur = activityMap.get(i); + if (cur == a) + { + return; + } + + // Update selected activity in map + activityMap.put(i, a); + + // If had a previous selection and this item links to another check for item prevention change. + // If there are changes adjust the Linked panel quantity as well + if (cur != null && i.getLinkedItemId() != -1) + { + if (cur.isPreventLinked() != a.isPreventLinked()) + { + CriticalItem linked = CriticalItem.getByItemId(i.getLinkedItemId()); + CriticalItemPanel l = panelMap.get(linked); + if (l != null) + { + l.updateLinkedMap(getLinkedTotalMap(linked)); + int amount = config.limitedBankedSecondaries() ? limitToActivitySecondaries(a, l.getAmount()) : l.getAmount(); + l.updateAmount(amount, false); + l.recalculate(); + } + } + } + + // Total banked experience + CriticalItemPanel p = panelMap.get(i); + if (p != null) + { + p.updateLinkedMap(getLinkedTotalMap(i)); + int amount = config.limitedBankedSecondaries() ? limitToActivitySecondaries(a, p.getAmount()) : p.getAmount(); + p.updateAmount(amount, true); + p.updateXp(a.getXp() * (i.isIgnoreBonus() ? 1.0f : xpFactor)); + } + + // Update total banked xp value based on updated panels + calculateBankedXpTotal(); + } + + private Map getLinkedTotalMap(CriticalItem i) + { + return getLinkedTotalMap(i, true); + } + + /** + * Creates a Map of CriticalItem and Qty for all items that link to the passed CriticalItem + * @param i CriticalItem to base Map off of + * @param first Since this is called recursively we want to ensure the original CriticalItem is always added + * @return Map of Linked CriticalItems and their Qty + */ + private Map getLinkedTotalMap(CriticalItem i, boolean first) + { + Map map = new LinkedHashMap<>(); + if (!categoryMap.get(i.getCategory())) + { + return map; + } + + // This item has an activity selected and its preventing linked functionality? + Activity selected = activityMap.get(i); + if (selected != null && selected.isPreventLinked()) + { + // If initial request is for this item + if (!first) + { + return map; + } + } + + // Add self to map + int amount = criticalMap.getOrDefault(i, 0); + if (amount > 0) + { + map.put(i, amount); + } + + // This item doesn't link to anything, all done. + if (linkedMap.get(i) == null) + { + return map; + } + + CriticalItem item = CriticalItem.getByItemId(linkedMap.get(i)); + if (item == null) + { + log.warn("Error finding Critical Item for Item ID: {}", linkedMap.get(i)); + return map; + } + + map.putAll(getLinkedTotalMap(item, false)); + + return map; + } + + /** + * SkillCalculatorPlugin sends the Bank Map when the bank contents change + * @param map Map of Item IDs and Quantity + */ + void updateBankMap(Map map) + { + boolean oldMapFlag = (bankMap.size() <= 0); + bankMap = map; + // Refresh entire panel if old map was empty + if (oldMapFlag) + { + CalculatorType calc = CalculatorType.getBySkill(currentSkill); + SwingUtilities.invokeLater(() -> openBanked(calc)); + return; + } + + // recalculate all data related to banked experience except for activity selections + criticalMap.clear(); + linkedMap.clear(); + calculatedBankedMaps(); + + // Update the Total XP banked and the details panel + SwingUtilities.invokeLater(this::refreshDetailContainer); + } + + /** + * Loops over all ItemPanels too sum their total xp and updates the label with the new value + */ + private void calculateBankedXpTotal() + { + double total = 0.0; + for (CriticalItemPanel p : panelMap.values()) + { + total += p.getTotal(); + } + + totalBankedXp = total; + + syncBankedXp(); + } + + /** + * Used to update the UI to reflect the new Banked XP amount + */ + private void syncBankedXp() + { + totalLabel.setText("Total Banked xp: " + XP_FORMAT_COMMA.format(totalBankedXp)); + + syncInputFields(); + + revalidate(); + repaint(); + } + + /** + * Check Bank for Activity Secondaries and Limits to possible Activity amounts + * @param a Selected Activity + * @param possible Amount of Critical Item available + * @return possible Limited to Banked Secondaries + */ + private int limitToActivitySecondaries(Activity a, int possible) + { + for (SecondaryItem i : a.getSecondaries()) + { + int banked = bankMap.getOrDefault(i.getId(), 0); + int newPossible = banked / i.getQty(); + possible = newPossible < possible ? newPossible : possible; + } + + return possible; + } + + /** + * Renders the Xp Modifier options + */ + + private void renderBonusXpOptions() + { + SkillDataBonus[] bonuses = skillData.getSkillData(currentCalc.getDataFile()).getBonuses(); + if (bonuses != null) + { + add(new JLabel("Bonus Experience:")); + for (SkillDataBonus bonus : bonuses) + { + JPanel checkboxPanel = buildCheckboxPanel(bonus); + + add(checkboxPanel); + add(Box.createRigidArea(new Dimension(0, 5))); + } + } + } + + private JPanel buildCheckboxPanel(SkillDataBonus bonus) + { + JPanel uiOption = new JPanel(new BorderLayout()); + JLabel uiLabel = new JLabel(bonus.getName()); + JCheckBox uiCheckbox = new JCheckBox(); + + uiLabel.setForeground(Color.WHITE); + uiLabel.setFont(FontManager.getRunescapeSmallFont()); + + uiOption.setBorder(BorderFactory.createEmptyBorder(3, 7, 3, 0)); + uiOption.setBackground(ColorScheme.DARKER_GRAY_COLOR); + + // Adjust XP bonus depending on check-state of the boxes. + uiCheckbox.addActionListener(event -> adjustCheckboxes(uiCheckbox, bonus)); + + uiCheckbox.setBackground(ColorScheme.MEDIUM_GRAY_COLOR); + + uiOption.add(uiLabel, BorderLayout.WEST); + uiOption.add(uiCheckbox, BorderLayout.EAST); + bonusCheckBoxes.add(uiCheckbox); + + return uiOption; + } + + private void adjustCheckboxes(JCheckBox target, SkillDataBonus bonus) + { + adjustXPBonus(0); + bonusCheckBoxes.forEach(otherSelectedCheckbox -> + { + if (otherSelectedCheckbox != target) + { + otherSelectedCheckbox.setSelected(false); + } + }); + + if (target.isSelected()) + { + adjustXPBonus(bonus.getValue()); + } + } + + private void adjustXPBonus(float value) + { + xpFactor = 1f + value; + refreshDetailContainer(); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CalculatorType.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CalculatorType.java index e102988f50..1b3cbf818b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CalculatorType.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CalculatorType.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Kruithne + * Copyright (c) 2018, TheStonedTurtle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,26 +31,40 @@ import net.runelite.api.Skill; @AllArgsConstructor @Getter -enum CalculatorType +public enum CalculatorType { - MINING(Skill.MINING, "skill_mining.json"), - AGILITY(Skill.AGILITY, "skill_agility.json"), - SMITHING(Skill.SMITHING, "skill_smithing.json"), - HERBLORE(Skill.HERBLORE, "skill_herblore.json"), - FISHING(Skill.FISHING, "skill_fishing.json"), - THIEVING(Skill.THIEVING, "skill_thieving.json"), - COOKING(Skill.COOKING, "skill_cooking.json"), - PRAYER(Skill.PRAYER, "skill_prayer.json"), - CRAFTING(Skill.CRAFTING, "skill_crafting.json"), - FIREMAKING(Skill.FIREMAKING, "skill_firemaking.json"), - MAGIC(Skill.MAGIC, "skill_magic.json"), - FLETCHING(Skill.FLETCHING, "skill_fletching.json"), - WOODCUTTING(Skill.WOODCUTTING, "skill_woodcutting.json"), - RUNECRAFT(Skill.RUNECRAFT, "skill_runecraft.json"), - FARMING(Skill.FARMING, "skill_farming.json"), - CONSTRUCTION(Skill.CONSTRUCTION, "skill_construction.json"), - HUNTER(Skill.HUNTER, "skill_hunter.json"); + AGILITY(Skill.AGILITY, "skill_agility.json", false), + CONSTRUCTION(Skill.CONSTRUCTION, "skill_construction.json", true), + COOKING(Skill.COOKING, "skill_cooking.json", true), + CRAFTING(Skill.CRAFTING, "skill_crafting.json", true), + FARMING(Skill.FARMING, "skill_farming.json", true), + FIREMAKING(Skill.FIREMAKING, "skill_firemaking.json", false), + FLETCHING(Skill.FLETCHING, "skill_fletching.json", false), + FISHING(Skill.FISHING, "skill_fishing.json", false), + HERBLORE(Skill.HERBLORE, "skill_herblore.json", true), + HUNTER(Skill.HUNTER, "skill_hunter.json", false), + MAGIC(Skill.MAGIC, "skill_magic.json", false), + MINING(Skill.MINING, "skill_mining.json", false), + PRAYER(Skill.PRAYER, "skill_prayer.json", true), + RUNECRAFT(Skill.RUNECRAFT, "skill_runecraft.json", false), + SMITHING(Skill.SMITHING, "skill_smithing.json", true), + THIEVING(Skill.THIEVING, "skill_thieving.json", false), + WOODCUTTING(Skill.WOODCUTTING, "skill_woodcutting.json", false); private final Skill skill; private final String dataFile; + private final boolean bankedXpFlag; + + public static CalculatorType getBySkill(Skill skill) + { + for (CalculatorType c : values()) + { + if (c.getSkill().equals(skill)) + { + return c; + } + } + + return null; + } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java index e2114a6a95..35fc1b2029 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java @@ -34,13 +34,17 @@ import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; +import lombok.AccessLevel; +import lombok.Getter; import net.runelite.api.Client; import net.runelite.api.Experience; +import net.runelite.api.Skill; import net.runelite.client.game.ItemManager; import net.runelite.client.game.SpriteManager; import net.runelite.client.plugins.skillcalculator.beans.SkillData; @@ -51,6 +55,8 @@ import net.runelite.client.ui.DynamicGridLayout; import net.runelite.client.ui.FontManager; import net.runelite.client.ui.PluginPanel; import net.runelite.client.ui.components.IconTextField; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; class SkillCalculator extends JPanel { @@ -63,7 +69,9 @@ class SkillCalculator extends JPanel private final ItemManager itemManager; private final List uiActionSlots = new ArrayList<>(); private final CacheSkillData cacheSkillData = new CacheSkillData(); + @Getter(AccessLevel.PACKAGE) private final UICombinedActionSlot combinedActionSlot; + @Getter(AccessLevel.PACKAGE) private final ArrayList combinedActionSlots = new ArrayList<>(); private final List bonusCheckBoxes = new ArrayList<>(); private final IconTextField searchBar = new IconTextField(); @@ -74,6 +82,8 @@ class SkillCalculator extends JPanel private int targetLevel = currentLevel + 1; private int targetXP = Experience.getXpForLevel(targetLevel); private float xpFactor = 1.0f; + private float lastBonus = 0.0f; + private CalculatorType calculatorType; SkillCalculator(Client client, UICalculatorInputArea uiInput, SpriteManager spriteManager, ItemManager itemManager) { @@ -111,6 +121,8 @@ class SkillCalculator extends JPanel void openCalculator(CalculatorType calculatorType) { + this.calculatorType = calculatorType; + // Load the skill data. skillData = cacheSkillData.getSkillData(calculatorType.getDataFile()); @@ -118,10 +130,11 @@ class SkillCalculator extends JPanel xpFactor = 1.0f; // Update internal skill/XP values. - currentXP = client.getSkillExperience(calculatorType.getSkill()); - currentLevel = Experience.getLevelForXp(currentXP); - targetLevel = enforceSkillBounds(currentLevel + 1); - targetXP = Experience.getXpForLevel(targetLevel); + updateInternalValues(); + + // BankedCalculator prevents these from being editable so just ensure they are editable. + uiInput.getUiFieldTargetLevel().setEditable(true); + uiInput.getUiFieldTargetXP().setEditable(true); // Remove all components (action slots) from this panel. removeAll(); @@ -129,6 +142,9 @@ class SkillCalculator extends JPanel // Clear the search bar searchBar.setText(null); + // Clear the search bar + searchBar.setText(null); + // Add in checkboxes for available skill bonuses. renderBonusOptions(); @@ -145,6 +161,23 @@ class SkillCalculator extends JPanel updateInputFields(); } + private void updateInternalValues() + { + updateCurrentValues(); + updateTargetValues(); + } + + private void updateCurrentValues() + { + currentXP = client.getSkillExperience(calculatorType.getSkill()); + currentLevel = Experience.getLevelForXp(currentXP); + } + private void updateTargetValues() + { + targetLevel = enforceSkillBounds(currentLevel + 1); + targetXP = Experience.getXpForLevel(targetLevel); + } + private void updateCombinedAction() { int size = combinedActionSlots.size(); @@ -195,17 +228,23 @@ class SkillCalculator extends JPanel { if (skillData.getBonuses() != null) { + List uiCheckBoxList = new ArrayList<>(); + lastBonus = 0.0f; + for (SkillDataBonus bonus : skillData.getBonuses()) { - JPanel checkboxPanel = buildCheckboxPanel(bonus); + Pair> combinedCheckboxPanel = buildCheckboxPanel(bonus, uiCheckBoxList); + JPanel checkboxPanel = combinedCheckboxPanel.getKey(); + uiCheckBoxList = combinedCheckboxPanel.getValue(); add(checkboxPanel); - add(Box.createRigidArea(new Dimension(0, 5))); } + + add(Box.createRigidArea(new Dimension(0, 5))); } } - private JPanel buildCheckboxPanel(SkillDataBonus bonus) + private Pair> buildCheckboxPanel(SkillDataBonus bonus, List uiCheckBoxList) { JPanel uiOption = new JPanel(new BorderLayout()); JLabel uiLabel = new JLabel(bonus.getName()); @@ -217,33 +256,41 @@ class SkillCalculator extends JPanel uiOption.setBorder(BorderFactory.createEmptyBorder(3, 7, 3, 0)); uiOption.setBackground(ColorScheme.DARKER_GRAY_COLOR); - // Adjust XP bonus depending on check-state of the boxes. - uiCheckbox.addActionListener(event -> adjustCheckboxes(uiCheckbox, bonus)); - - uiCheckbox.setBackground(ColorScheme.MEDIUM_GRAY_COLOR); - - uiOption.add(uiLabel, BorderLayout.WEST); - uiOption.add(uiCheckbox, BorderLayout.EAST); - bonusCheckBoxes.add(uiCheckbox); - - return uiOption; - } - - private void adjustCheckboxes(JCheckBox target, SkillDataBonus bonus) - { - adjustXPBonus(0); - bonusCheckBoxes.forEach(otherSelectedCheckbox -> + JCheckBox uiCheckBox = new JCheckBox(); + uiCheckBox.setBackground(ColorScheme.MEDIUM_GRAY_COLOR); + uiCheckBox.addActionListener(e -> { - if (otherSelectedCheckbox != target) + if (uiCheckBox.isSelected()) { - otherSelectedCheckbox.setSelected(false); + adjustXPBonus(uiCheckBox.isSelected(), bonus.getValue()); + lastBonus = bonus.getValue(); + + for (JCheckBox checkBox : uiCheckBoxList) + { + if (checkBox != uiCheckBox) + { + checkBox.setSelected(false); + } + } } + else if (xpFactor > 1.0) + { + xpFactor = 1.0f; + lastBonus = 0.0f; + calculate(); + } + + updateCombinedAction(); }); - if (target.isSelected()) - { - adjustXPBonus(bonus.getValue()); - } + uiCheckBoxList.add(uiCheckBox); + + uiOption.add(uiCheckBox, BorderLayout.EAST); + + uiOption.add(uiLabel, BorderLayout.WEST); + bonusCheckBoxes.add(uiCheckbox); + + return new ImmutablePair<>(uiOption, uiCheckBoxList); } private void renderActionSlots() @@ -342,9 +389,16 @@ class SkillCalculator extends JPanel calculate(); } - private void adjustXPBonus(float value) + private void adjustXPBonus(boolean addBonus, float value) { - xpFactor = 1f + value; + clearLastBonus(); + xpFactor += addBonus ? value : -value; + calculate(); + } + + private void clearLastBonus() + { + xpFactor -= lastBonus; calculate(); } @@ -409,4 +463,25 @@ class SkillCalculator extends JPanel return slot.getAction().getName().toLowerCase().contains(text.toLowerCase()); } -} + /** + * Updates the current skill calculator (if present) + *

+ * This method is invoked by the {@link SkillCalculatorPlugin} event subscriber + * when an {@link ExperienceChanged} object is posted to the event bus + */ + void updateSkillCalculator(Skill skill) + { + // If the user has selected a calculator, update its fields + Optional.ofNullable(calculatorType).ifPresent(calc -> + { + if (skill.equals(calculatorType.getSkill())) + { + // Update our model "current" values + updateCurrentValues(); + + // Update the UI to reflect our new model + updateInputFields(); + } + }); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorConfig.java new file mode 100644 index 0000000000..ce8a4c2ddd --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorConfig.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018, 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.skillcalculator; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("skillCalculator") +public interface SkillCalculatorConfig extends Config +{ + @ConfigItem( + keyName = "showBankedXp", + name = "Show Banked xp Tab", + description = "Shows the Banked xp tab inside the Calculator Panel", + position = 0 + ) + default boolean showBankedXp() + { + return true; + } + + @ConfigItem( + keyName = "limitedBankedSecondaries", + name = "Limit Banked xp to Secondaries", + description = "Limits the Banked xp shown based on secondaries banked as well", + position = 1 + ) + default boolean limitedBankedSecondaries() + { + return false; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPanel.java index b6abe4577f..30ab0b8486 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPanel.java @@ -1,6 +1,7 @@ /* * Copyright (c) 2018, Kruithne * Copyright (c) 2018, Psikoi + * Copyright (c) 2018, TheStonedTurtle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,10 +30,18 @@ package net.runelite.client.plugins.skillcalculator; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.GridLayout; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; import javax.swing.ImageIcon; import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; +import net.runelite.api.Skill; import net.runelite.client.game.ItemManager; import net.runelite.client.game.SkillIconManager; import net.runelite.client.game.SpriteManager; @@ -41,30 +50,42 @@ import net.runelite.client.ui.PluginPanel; import net.runelite.client.ui.components.materialtabs.MaterialTab; import net.runelite.client.ui.components.materialtabs.MaterialTabGroup; +@Slf4j class SkillCalculatorPanel extends PluginPanel { private final SkillCalculator uiCalculator; private final SkillIconManager iconManager; - private final MaterialTabGroup tabGroup; + private final SkillCalculatorConfig config; + private final BankedCalculator bankedCalculator; - SkillCalculatorPanel(SkillIconManager iconManager, Client client, SpriteManager spriteManager, ItemManager itemManager) + private CalculatorType currentCalc; + private final MaterialTabGroup skillGroup; + private final MaterialTabGroup tabGroup; + private String currentTab; + private ArrayList tabs = new ArrayList<>(); + @Getter + private Map bankMap = new HashMap<>(); + private GridBagConstraints c; + + SkillCalculatorPanel(SkillIconManager iconManager, Client client, SkillCalculatorConfig config, SpriteManager spriteManager, ItemManager itemManager) { super(); getScrollPane().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); this.iconManager = iconManager; + this.config = config; setBorder(new EmptyBorder(10, 10, 10, 10)); setLayout(new GridBagLayout()); - GridBagConstraints c = new GridBagConstraints(); + c = new GridBagConstraints(); c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 1; c.gridx = 0; c.gridy = 0; - tabGroup = new MaterialTabGroup(); - tabGroup.setLayout(new GridLayout(0, 6, 7, 7)); + skillGroup = new MaterialTabGroup(); + skillGroup.setLayout(new GridLayout(0, 6, 7, 7)); addCalculatorButtons(); @@ -73,14 +94,23 @@ class SkillCalculatorPanel extends PluginPanel uiInput.setBackground(ColorScheme.DARK_GRAY_COLOR); uiCalculator = new SkillCalculator(client, uiInput, spriteManager, itemManager); - add(tabGroup, c); + bankedCalculator = new BankedCalculator(this, client, uiInput, config, itemManager); + + tabGroup = new MaterialTabGroup(); + tabGroup.setBorder(new EmptyBorder(0, 0, 10, 0)); + + addTabButtons(); + + add(skillGroup, c); c.gridy++; add(uiInput, c); c.gridy++; - add(uiCalculator, c); + add(tabGroup, c); c.gridy++; + + add(uiCalculator, c); } private void addCalculatorButtons() @@ -88,14 +118,126 @@ class SkillCalculatorPanel extends PluginPanel for (CalculatorType calculatorType : CalculatorType.values()) { ImageIcon icon = new ImageIcon(iconManager.getSkillImage(calculatorType.getSkill(), true)); - MaterialTab tab = new MaterialTab(icon, tabGroup, null); + MaterialTab tab = new MaterialTab(icon, skillGroup, null); tab.setOnSelectEvent(() -> { - uiCalculator.openCalculator(calculatorType); + if (currentCalc == calculatorType) + { + return true; + } + currentCalc = calculatorType; + selectedTab(currentTab, true); return true; }); - tabGroup.addTab(tab); + skillGroup.addTab(tab); } } + + private void addTabButtons() + { + tabGroup.removeAll(); + tabs.clear(); + + tabs.add("Calculator"); + if (config.showBankedXp()) + { + tabs.add("Banked Xp"); + } + // Only show if both options are visible + tabGroup.setVisible(tabs.size() > 1); + + tabGroup.setLayout(new GridLayout(0, tabs.size(), 7, 7)); + + for (String s : tabs) + { + MaterialTab matTab = new MaterialTab(s, tabGroup, null); + + matTab.setHorizontalAlignment(SwingUtilities.CENTER); + + // Ensure Background is applied + matTab.setOpaque(true); + matTab.setBackground(ColorScheme.DARKER_GRAY_COLOR); + + // When Clicked + matTab.setOnSelectEvent(() -> + { + selectedTab(s, false); + return true; + }); + + tabGroup.addTab(matTab); + } + + MaterialTab selected = tabGroup.getTab(0); + if (tabs.contains(currentTab)) + { + selected = tabGroup.getTab(tabs.indexOf(currentTab)); + } + + tabGroup.select(selected); + currentTab = selected.getText(); + } + + private void selectedTab(String s, boolean force) + { + // Do not refresh the panel if they clicked the same tab, unless they selected a new skill + if (Objects.equals(currentTab, s) && !force) + { + return; + } + + currentTab = s; + + // Only open a panel if a skill is selected + if (currentCalc == null) + { + return; + } + + switch (s) + { + case "Calculator": + remove(bankedCalculator); + add(uiCalculator, c); + uiCalculator.openCalculator(currentCalc); + break; + case "Banked Xp": + remove(uiCalculator); + add(bankedCalculator, c); + bankedCalculator.openBanked(currentCalc); + break; + } + + this.revalidate(); + this.repaint(); + } + + // Refresh entire panel + void refreshPanel() + { + // Recreate Tabs (in case of Config change) and selects the first tab + addTabButtons(); + + // Ensure reload + selectedTab(currentTab, true); + + this.revalidate(); + this.repaint(); + } + + // Wrapper function for updating SkillCalculator's bankMap + void updateBankMap(Map bank) + { + bankMap = bank; + if (currentCalc != null & currentTab.equals("Banked Xp")) + { + bankedCalculator.updateBankMap(bankMap); + } + } + + void updateSkillCalculator(Skill skill) + { + uiCalculator.updateSkillCalculator(skill); + } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPlugin.java index 1deb71d093..121badf693 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorPlugin.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2018, Kruithne + * Copyright (c) 2018, TheStonedTurtle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,14 +26,31 @@ package net.runelite.client.plugins.skillcalculator; +import com.google.inject.Provides; import java.awt.image.BufferedImage; +import java.util.HashMap; +import java.util.Map; import javax.inject.Inject; +import javax.swing.SwingUtilities; +import lombok.Getter; import net.runelite.api.Client; +import net.runelite.api.InventoryID; +import net.runelite.api.Item; +import net.runelite.api.ItemContainer; +import net.runelite.api.events.ConfigChanged; +import net.runelite.api.events.ExperienceChanged; +import net.runelite.api.events.GameTick; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; import net.runelite.client.game.ItemManager; import net.runelite.client.game.SkillIconManager; import net.runelite.client.game.SpriteManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.skillcalculator.banked.CriticalItem; import net.runelite.client.ui.ClientToolbar; import net.runelite.client.ui.NavigationButton; import net.runelite.client.util.ImageUtil; @@ -47,6 +65,9 @@ public class SkillCalculatorPlugin extends Plugin @Inject private Client client; + @Inject + private ClientThread clientThread; + @Inject private SkillIconManager skillIconManager; @@ -59,13 +80,28 @@ public class SkillCalculatorPlugin extends Plugin @Inject private ClientToolbar clientToolbar; + @Inject + private SkillCalculatorConfig skillCalculatorConfig; + private NavigationButton uiNavigationButton; + private SkillCalculatorPanel uiPanel; + + @Getter + private Map bankMap = new HashMap<>(); + + private int bankHash; + + @Provides + SkillCalculatorConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(SkillCalculatorConfig.class); + } @Override protected void startUp() throws Exception { final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "calc.png"); - final SkillCalculatorPanel uiPanel = new SkillCalculatorPanel(skillIconManager, client, spriteManager, itemManager); + this.uiPanel = new SkillCalculatorPanel(skillIconManager, client, skillCalculatorConfig, spriteManager, itemManager); uiNavigationButton = NavigationButton.builder() .tooltip("Skill Calculator") @@ -75,11 +111,112 @@ public class SkillCalculatorPlugin extends Plugin .build(); clientToolbar.addNavigation(uiNavigationButton); + + clientThread.invokeLater(() -> + { + switch (client.getGameState()) + { + case STARTING: + case UNKNOWN: + return false; + } + + CriticalItem.prepareItemCompositions(itemManager); + return true; + }); } @Override protected void shutDown() throws Exception { clientToolbar.removeNavigation(uiNavigationButton); + bankMap.clear(); + bankHash = -1; + } + + @Subscribe + public void onConfigChanged(ConfigChanged event) + { + if (event.getGroup().equals("skillCalculator")) + { + if (event.getKey().equals("showBankedXp")) + { + bankMap.clear(); + bankHash = -1; + } + + SwingUtilities.invokeLater(() -> uiPanel.refreshPanel()); + } + } + + // Pulled from bankvalue plugin to check if bank is open + @Subscribe + public void onGameTick(GameTick event) + { + if (!skillCalculatorConfig.showBankedXp()) + { + return; + } + + Widget widgetBankTitleBar = client.getWidget(WidgetInfo.BANK_TITLE_BAR); + + // Don't update on a search because rs seems to constantly update the title + if (widgetBankTitleBar == null || widgetBankTitleBar.isHidden() || widgetBankTitleBar.getText().contains("Showing")) + { + return; + } + + updateBankItems(); + } + + // Check if bank contents changed and if so send to UI + private void updateBankItems() + { + ItemContainer c = client.getItemContainer(InventoryID.BANK); + Item[] widgetItems = (c == null ? new Item[0] : c.getItems()); + + // Couldn't find any items in bank, do nothing. + if (widgetItems == null || widgetItems.length == 0) + { + return; + } + + Map newBankMap = getBankMapIfDiff(widgetItems); + + // Bank didn't change + if (newBankMap.size() == 0) + { + return; + } + + bankMap = newBankMap; + // send updated bank map to ui + uiPanel.updateBankMap(bankMap); + } + + // Recreates the bankMap and checks if the hashCode is different (the map has changed). Sends an empty map if no changes + private Map getBankMapIfDiff(Item[] widgetItems) + { + Map mapCheck = new HashMap<>(); + for (Item widgetItem : widgetItems) + { + mapCheck.put(widgetItem.getId(), widgetItem.getQuantity()); + } + + int curHash = mapCheck.hashCode(); + + if (curHash != bankHash) + { + bankHash = curHash; + return mapCheck; + } + + return new HashMap<>(); + } + + @Subscribe + public void onExperienceChanged(ExperienceChanged changeEvent) + { + uiPanel.updateSkillCalculator(changeEvent.getSkill()); } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/CriticalItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/CriticalItem.java new file mode 100644 index 0000000000..9cc6633746 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/CriticalItem.java @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2018, 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.skillcalculator.banked; + +import lombok.Getter; +import net.runelite.api.ItemComposition; +import net.runelite.api.ItemID; +import net.runelite.api.Skill; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import net.runelite.client.game.ItemManager; + +public enum CriticalItem +{ + /** + * Construction Items + */ + // Planks + PLANK(ItemID.PLANK, "Planks", Skill.CONSTRUCTION), + OAK_PLANK(ItemID.OAK_PLANK, "Planks", Skill.CONSTRUCTION), + TEAK_PLANK(ItemID.TEAK_PLANK, "Planks", Skill.CONSTRUCTION), + MAHOGANY_PLANK(ItemID.MAHOGANY_PLANK, "Planks", Skill.CONSTRUCTION), + // Logs + LOGS(ItemID.LOGS, "Logs", Skill.CONSTRUCTION, ItemID.PLANK), + OAK_LOGS(ItemID.OAK_LOGS, "Logs", Skill.CONSTRUCTION, ItemID.OAK_PLANK), + TEAK_LOGS(ItemID.TEAK_LOGS, "Logs", Skill.CONSTRUCTION, ItemID.TEAK_PLANK), + MAHOGANY_LOGS(ItemID.MAHOGANY_LOGS, "Logs", Skill.CONSTRUCTION, ItemID.MAHOGANY_PLANK), + + /** + * Herblore Items + */ + // Grimy Herbs + GRIMY_GUAM_LEAF(ItemID.GRIMY_GUAM_LEAF, "Grimy Herbs", Skill.HERBLORE, ItemID.GUAM_LEAF), + GRIMY_MARRENTILL(ItemID.GRIMY_MARRENTILL, "Grimy Herbs", Skill.HERBLORE, ItemID.MARRENTILL), + GRIMY_TARROMIN(ItemID.GRIMY_TARROMIN, "Grimy Herbs", Skill.HERBLORE, ItemID.TARROMIN), + GRIMY_HARRALANDER(ItemID.GRIMY_HARRALANDER, "Grimy Herbs", Skill.HERBLORE, ItemID.HARRALANDER), + GRIMY_RANARR_WEED(ItemID.GRIMY_RANARR_WEED, "Grimy Herbs", Skill.HERBLORE, ItemID.RANARR_WEED), + GRIMY_TOADFLAX(ItemID.GRIMY_TOADFLAX, "Grimy Herbs", Skill.HERBLORE, ItemID.TOADFLAX), + GRIMY_IRIT_LEAF(ItemID.GRIMY_IRIT_LEAF, "Grimy Herbs", Skill.HERBLORE, ItemID.IRIT_LEAF), + GRIMY_AVANTOE(ItemID.GRIMY_AVANTOE, "Grimy Herbs", Skill.HERBLORE, ItemID.AVANTOE), + GRIMY_KWUARM(ItemID.GRIMY_KWUARM, "Grimy Herbs", Skill.HERBLORE, ItemID.KWUARM), + GRIMY_SNAPDRAGON(ItemID.GRIMY_SNAPDRAGON, "Grimy Herbs", Skill.HERBLORE, ItemID.SNAPDRAGON), + GRIMY_CADANTINE(ItemID.GRIMY_CADANTINE, "Grimy Herbs", Skill.HERBLORE, ItemID.CADANTINE), + GRIMY_LANTADYME(ItemID.GRIMY_LANTADYME, "Grimy Herbs", Skill.HERBLORE, ItemID.LANTADYME), + GRIMY_DWARF_WEED(ItemID.GRIMY_DWARF_WEED, "Grimy Herbs", Skill.HERBLORE, ItemID.DWARF_WEED), + GRIMY_TORSTOL(ItemID.GRIMY_TORSTOL, "Grimy Herbs", Skill.HERBLORE, ItemID.TORSTOL), + // Clean Herbs + GUAM_LEAF(ItemID.GUAM_LEAF, "Cleaned Herbs", Skill.HERBLORE, ItemID.GUAM_POTION_UNF), + MARRENTILL(ItemID.MARRENTILL, "Cleaned Herbs", Skill.HERBLORE, ItemID.MARRENTILL_POTION_UNF), + TARROMIN(ItemID.TARROMIN, "Cleaned Herbs", Skill.HERBLORE, ItemID.TARROMIN_POTION_UNF), + HARRALANDER(ItemID.HARRALANDER, "Cleaned Herbs", Skill.HERBLORE, ItemID.HARRALANDER_POTION_UNF), + RANARR_WEED(ItemID.RANARR_WEED, "Cleaned Herbs", Skill.HERBLORE, ItemID.RANARR_POTION_UNF), + TOADFLAX(ItemID.TOADFLAX, "Cleaned Herbs", Skill.HERBLORE, ItemID.TOADFLAX_POTION_UNF), + IRIT_LEAF(ItemID.IRIT_LEAF, "Cleaned Herbs", Skill.HERBLORE, ItemID.IRIT_POTION_UNF), + AVANTOE(ItemID.AVANTOE, "Cleaned Herbs", Skill.HERBLORE, ItemID.AVANTOE_POTION_UNF), + KWUARM(ItemID.KWUARM, "Cleaned Herbs", Skill.HERBLORE, ItemID.KWUARM_POTION_UNF), + SNAPDRAGON(ItemID.SNAPDRAGON, "Cleaned Herbs", Skill.HERBLORE, ItemID.SNAPDRAGON_POTION_UNF), + CADANTINE(ItemID.CADANTINE, "Cleaned Herbs", Skill.HERBLORE, ItemID.CADANTINE_POTION_UNF), + LANTADYME(ItemID.LANTADYME, "Cleaned Herbs", Skill.HERBLORE, ItemID.LANTADYME_POTION_UNF), + DWARF_WEED(ItemID.DWARF_WEED, "Cleaned Herbs", Skill.HERBLORE, ItemID.DWARF_WEED_POTION_UNF), + TORSTOL(ItemID.TORSTOL, "Cleaned Herbs", Skill.HERBLORE, ItemID.TORSTOL_POTION_UNF), + // Unfinished Potions + GUAM_LEAF_POTION_UNF(ItemID.GUAM_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), + MARRENTILL_POTION_UNF(ItemID.MARRENTILL_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), + TARROMIN_POTION_UNF(ItemID.TARROMIN_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), + HARRALANDER_POTION_UNF(ItemID.HARRALANDER_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), + RANARR_POTION_UNF(ItemID.RANARR_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), + TOADFLAX_POTION_UNF(ItemID.TOADFLAX_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), + IRIT_POTION_UNF(ItemID.IRIT_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), + AVANTOE_POTION_UNF(ItemID.AVANTOE_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), + KWUARM_POTION_UNF(ItemID.KWUARM_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), + SNAPDRAGON_POTION_UNF(ItemID.SNAPDRAGON_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), + CADANTINE_POTION_UNF(ItemID.CADANTINE_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), + LANTADYME_POTION_UNF(ItemID.LANTADYME_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), + DWARF_WEED_POTION_UNF(ItemID.DWARF_WEED_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), + TORSTOL_POTION_UNF(ItemID.TORSTOL_POTION_UNF, "Unfinished Potions", Skill.HERBLORE), + + /** + * Prayer Items + */ + // Bones + BONES(ItemID.BONES, "Bones", Skill.PRAYER), + WOLF_BONES(ItemID.WOLF_BONES, "Bones", Skill.PRAYER), + BURNT_BONES(ItemID.BURNT_BONES, "Bones", Skill.PRAYER), + MONKEY_BONES(ItemID.MONKEY_BONES, "Bones", Skill.PRAYER), + BAT_BONES(ItemID.BAT_BONES, "Bones", Skill.PRAYER), + JOGRE_BONES(ItemID.JOGRE_BONES, "Bones", Skill.PRAYER), + BIG_BONES(ItemID.BIG_BONES, "Bones", Skill.PRAYER), + ZOGRE_BONES(ItemID.ZOGRE_BONES, "Bones", Skill.PRAYER), + SHAIKAHAN_BONES(ItemID.SHAIKAHAN_BONES, "Bones", Skill.PRAYER), + BABYDRAGON_BONES(ItemID.BABYDRAGON_BONES, "Bones", Skill.PRAYER), + WYVERN_BONES(ItemID.WYVERN_BONES, "Bones", Skill.PRAYER), + DRAGON_BONES(ItemID.DRAGON_BONES, "Bones", Skill.PRAYER), + FAYRG_BONES(ItemID.FAYRG_BONES, "Bones", Skill.PRAYER), + LAVA_DRAGON_BONES(ItemID.LAVA_DRAGON_BONES, "Bones", Skill.PRAYER), + RAURG_BONES(ItemID.RAURG_BONES, "Bones", Skill.PRAYER), + DAGANNOTH_BONES(ItemID.DAGANNOTH_BONES, "Bones", Skill.PRAYER), + OURG_BONES(ItemID.OURG_BONES, "Bones", Skill.PRAYER), + SUPERIOR_DRAGON_BONES(ItemID.SUPERIOR_DRAGON_BONES, "Bones", Skill.PRAYER), + // Shade Remains (Pyre Logs) + LOAR_REMAINS(ItemID.LOAR_REMAINS, "Shades", Skill.PRAYER, true), + PHRIN_REMAINS(ItemID.PHRIN_REMAINS, "Shades", Skill.PRAYER, true), + RIYL_REMAINS(ItemID.RIYL_REMAINS, "Shades", Skill.PRAYER, true), + ASYN_REMAINS(ItemID.ASYN_REMAINS, "Shades", Skill.PRAYER, true), + FIYR_REMAINS(ItemID.FIYR_REMAINS, "Shades", Skill.PRAYER, true), + // Ensouled Heads + ENSOULED_GOBLIN_HEAD(ItemID.ENSOULED_GOBLIN_HEAD_13448, "Ensouled Heads", Skill.PRAYER, true), + ENSOULED_MONKEY_HEAD(ItemID.ENSOULED_MONKEY_HEAD_13451, "Ensouled Heads", Skill.PRAYER, true), + ENSOULED_IMP_HEAD(ItemID.ENSOULED_IMP_HEAD_13454, "Ensouled Heads", Skill.PRAYER, true), + ENSOULED_MINOTAUR_HEAD(ItemID.ENSOULED_MINOTAUR_HEAD_13457, "Ensouled Heads", Skill.PRAYER, true), + ENSOULED_SCORPION_HEAD(ItemID.ENSOULED_SCORPION_HEAD_13460, "Ensouled Heads", Skill.PRAYER, true), + ENSOULED_BEAR_HEAD(ItemID.ENSOULED_BEAR_HEAD_13463, "Ensouled Heads", Skill.PRAYER, true), + ENSOULED_UNICORN_HEAD(ItemID.ENSOULED_UNICORN_HEAD_13466, "Ensouled Heads", Skill.PRAYER, true), + ENSOULED_DOG_HEAD(ItemID.ENSOULED_DOG_HEAD_13469, "Ensouled Heads", Skill.PRAYER, true), + ENSOULED_CHAOS_DRUID_HEAD(ItemID.ENSOULED_CHAOS_DRUID_HEAD_13472, "Ensouled Heads", Skill.PRAYER, true), + ENSOULED_GIANT_HEAD(ItemID.ENSOULED_GIANT_HEAD_13475, "Ensouled Heads", Skill.PRAYER, true), + ENSOULED_OGRE_HEAD(ItemID.ENSOULED_OGRE_HEAD_13478, "Ensouled Heads", Skill.PRAYER, true), + ENSOULED_ELF_HEAD(ItemID.ENSOULED_ELF_HEAD_13481, "Ensouled Heads", Skill.PRAYER, true), + ENSOULED_TROLL_HEAD(ItemID.ENSOULED_TROLL_HEAD_13484, "Ensouled Heads", Skill.PRAYER, true), + ENSOULED_HORROR_HEAD(ItemID.ENSOULED_HORROR_HEAD_13487, "Ensouled Heads", Skill.PRAYER, true), + ENSOULED_KALPHITE_HEAD(ItemID.ENSOULED_KALPHITE_HEAD_13490, "Ensouled Heads", Skill.PRAYER, true), + ENSOULED_DAGANNOTH_HEAD(ItemID.ENSOULED_DAGANNOTH_HEAD_13493, "Ensouled Heads", Skill.PRAYER, true), + ENSOULED_BLOODVELD_HEAD(ItemID.ENSOULED_BLOODVELD_HEAD_13496, "Ensouled Heads", Skill.PRAYER, true), + ENSOULED_TZHAAR_HEAD(ItemID.ENSOULED_TZHAAR_HEAD_13499, "Ensouled Heads", Skill.PRAYER, true), + ENSOULED_DEMON_HEAD(ItemID.ENSOULED_DEMON_HEAD_13502, "Ensouled Heads", Skill.PRAYER, true), + ENSOULED_AVIANSIE_HEAD(ItemID.ENSOULED_AVIANSIE_HEAD_13505, "Ensouled Heads", Skill.PRAYER, true), + ENSOULED_ABYSSAL_HEAD(ItemID.ENSOULED_ABYSSAL_HEAD_13508, "Ensouled Heads", Skill.PRAYER, true), + ENSOULED_DRAGON_HEAD(ItemID.ENSOULED_DRAGON_HEAD_13511, "Ensouled Heads", Skill.PRAYER, true), + + /** + * Cooking Items + */ + RAW_HERRING(ItemID.RAW_HERRING, "Fish", Skill.COOKING), + RAW_MACKEREL(ItemID.RAW_MACKEREL, "Fish", Skill.COOKING), + RAW_TROUT(ItemID.RAW_TROUT, "Fish", Skill.COOKING), + RAW_COD(ItemID.RAW_COD, "Fish", Skill.COOKING), + RAW_PIKE(ItemID.RAW_PIKE, "Fish", Skill.COOKING), + RAW_SALMON(ItemID.RAW_SALMON, "Fish", Skill.COOKING), + RAW_TUNA(ItemID.RAW_TUNA, "Fish", Skill.COOKING), + RAW_KARAMBWAN(ItemID.RAW_KARAMBWAN, "Fish", Skill.COOKING), + RAW_LOBSTER(ItemID.RAW_LOBSTER, "Fish", Skill.COOKING), + RAW_BASS(ItemID.RAW_BASS, "Fish", Skill.COOKING), + RAW_SWORDFISH(ItemID.RAW_SWORDFISH, "Fish", Skill.COOKING), + RAW_MONKFISH(ItemID.RAW_MONKFISH, "Fish", Skill.COOKING), + RAW_SHARK(ItemID.RAW_SHARK, "Fish", Skill.COOKING), + RAW_SEA_TURTLE(ItemID.RAW_SEA_TURTLE, "Fish", Skill.COOKING), + RAW_ANGLERFISH(ItemID.RAW_ANGLERFISH, "Fish", Skill.COOKING), + RAW_DARK_CRAB(ItemID.RAW_DARK_CRAB, "Fish", Skill.COOKING), + RAW_MANTA_RAY(ItemID.RAW_MANTA_RAY, "Fish", Skill.COOKING), + + GRAPES(ItemID.GRAPES, "Other", Skill.COOKING), + + /** + * Crafting Items + */ + WOOL(ItemID.WOOL, "Misc", Skill.CRAFTING), + FLAX(ItemID.FLAX, "Misc", Skill.CRAFTING), + MOLTEN_GLASS(ItemID.MOLTEN_GLASS, "Misc", Skill.CRAFTING), + BATTLESTAFF(ItemID.BATTLESTAFF, "Misc", Skill.CRAFTING), + + // D'hide/Dragon Leather + GREEN_DRAGONHIDE(ItemID.GREEN_DRAGONHIDE, "D'hide", Skill.CRAFTING, ItemID.GREEN_DRAGON_LEATHER), + GREEN_DRAGON_LEATHER(ItemID.GREEN_DRAGON_LEATHER, "D'hide", Skill.CRAFTING), + BLUE_DRAGONHIDE(ItemID.BLUE_DRAGONHIDE, "D'hide", Skill.CRAFTING, ItemID.BLUE_DRAGON_LEATHER), + BLUE_DRAGON_LEATHER(ItemID.BLUE_DRAGON_LEATHER, "D'hide", Skill.CRAFTING), + RED_DRAGONHIDE(ItemID.RED_DRAGONHIDE, "D'hide", Skill.CRAFTING, ItemID.RED_DRAGON_LEATHER), + RED_DRAGON_LEATHER(ItemID.RED_DRAGON_LEATHER, "D'hide", Skill.CRAFTING), + BLACK_DRAGONHIDE(ItemID.BLACK_DRAGONHIDE, "D'hide", Skill.CRAFTING, ItemID.BLACK_DRAGON_LEATHER), + BLACK_DRAGON_LEATHER(ItemID.BLACK_DRAGON_LEATHER, "D'hide", Skill.CRAFTING), + + // Uncut Gems + UNCUT_OPAL(ItemID.UNCUT_OPAL, "Gems", Skill.CRAFTING, ItemID.OPAL), + UNCUT_JADE(ItemID.UNCUT_JADE, "Gems", Skill.CRAFTING, ItemID.JADE), + UNCUT_RED_TOPAZ(ItemID.UNCUT_RED_TOPAZ, "Gems", Skill.CRAFTING, ItemID.RED_TOPAZ), + UNCUT_SAPPHIRE(ItemID.UNCUT_SAPPHIRE, "Gems", Skill.CRAFTING, ItemID.SAPPHIRE), + UNCUT_EMERALD(ItemID.UNCUT_EMERALD, "Gems", Skill.CRAFTING, ItemID.EMERALD), + UNCUT_RUBY(ItemID.UNCUT_RUBY, "Gems", Skill.CRAFTING, ItemID.RUBY), + UNCUT_DIAMOND(ItemID.UNCUT_DIAMOND, "Gems", Skill.CRAFTING, ItemID.DIAMOND), + UNCUT_DRAGONSTONE(ItemID.UNCUT_DRAGONSTONE, "Gems", Skill.CRAFTING, ItemID.DRAGONSTONE), + UNCUT_ONYX(ItemID.UNCUT_ONYX, "Gems", Skill.CRAFTING, ItemID.ONYX), + UNCUT_ZENYTE(ItemID.UNCUT_ZENYTE, "Gems", Skill.CRAFTING, ItemID.ZENYTE), + + // Cut Gems + OPAL(ItemID.OPAL, "Gems", Skill.CRAFTING), + JADE(ItemID.JADE, "Gems", Skill.CRAFTING), + RED_TOPAZ(ItemID.RED_TOPAZ, "Gems", Skill.CRAFTING), + SAPPHIRE(ItemID.SAPPHIRE, "Gems", Skill.CRAFTING), + EMERALD(ItemID.EMERALD, "Gems", Skill.CRAFTING), + RUBY(ItemID.RUBY, "Gems", Skill.CRAFTING), + DIAMOND(ItemID.DIAMOND, "Gems", Skill.CRAFTING), + DRAGONSTONE(ItemID.DRAGONSTONE, "Gems", Skill.CRAFTING), + ONYX(ItemID.ONYX, "Gems", Skill.CRAFTING), + ZENYTE(ItemID.ZENYTE, "Gems", Skill.CRAFTING), + + /** + * Smithing Items + */ + + // Ores + IRON_ORE(ItemID.IRON_ORE, "Ore", Skill.SMITHING), + SILVER_ORE(ItemID.SILVER_ORE, "Ore", Skill.SMITHING), + GOLD_ORE(ItemID.GOLD_ORE, "Ore", Skill.SMITHING), + MITHRIL_ORE(ItemID.MITHRIL_ORE, "Ore", Skill.SMITHING), + ADAMANTITE_ORE(ItemID.ADAMANTITE_ORE, "Ore", Skill.SMITHING), + RUNITE_ORE(ItemID.RUNITE_ORE, "Ore", Skill.SMITHING), + + // Bars + BRONZE_BAR(ItemID.BRONZE_BAR, "Bars", Skill.SMITHING), + IRON_BAR(ItemID.IRON_BAR, "Bars", Skill.SMITHING), + STEEL_BAR(ItemID.STEEL_BAR, "Bars", Skill.SMITHING), + MITHRIL_BAR(ItemID.MITHRIL_BAR, "Bars", Skill.SMITHING), + ADAMANTITE_BAR(ItemID.ADAMANTITE_BAR, "Bars", Skill.SMITHING), + RUNITE_BAR(ItemID.RUNITE_BAR, "Bars", Skill.SMITHING), + + /** + * Farming Items + */ + // Seeds + ACORN(ItemID.ACORN, "Seeds", Skill.FARMING), + WILLOW_SEED(ItemID.WILLOW_SEED, "Seeds", Skill.FARMING), + MAPLE_SEED(ItemID.MAPLE_SEED, "Seeds", Skill.FARMING), + YEW_SEED(ItemID.YEW_SEED, "Seeds", Skill.FARMING), + MAGIC_SEED(ItemID.MAGIC_SEED, "Seeds", Skill.FARMING), + APPLE_TREE_SEED(ItemID.APPLE_TREE_SEED, "Seeds", Skill.FARMING), + BANANA_TREE_SEED(ItemID.BANANA_TREE_SEED, "Seeds", Skill.FARMING), + ORANGE_TREE_SEED(ItemID.ORANGE_TREE_SEED, "Seeds", Skill.FARMING), + CURRY_TREE_SEED(ItemID.CURRY_TREE_SEED, "Seeds", Skill.FARMING), + PINEAPPLE_SEED(ItemID.PINEAPPLE_SEED, "Seeds", Skill.FARMING), + PAPAYA_TREE_SEED(ItemID.PAPAYA_TREE_SEED, "Seeds", Skill.FARMING), + PALM_TREE_SEED(ItemID.PALM_TREE_SEED, "Seeds", Skill.FARMING), + CALQUAT_TREE_SEED(ItemID.CALQUAT_TREE_SEED, "Seeds", Skill.FARMING), + TEAK_SEED(ItemID.TEAK_SEED, "Seeds", Skill.FARMING), + MAHOGANY_SEED(ItemID.MAHOGANY_SEED, "Seeds", Skill.FARMING), + SPIRIT_SEED(ItemID.SPIRIT_SEED, "Seeds", Skill.FARMING), + + // Saplings + OAK_SAPLING(ItemID.OAK_SAPLING, "Saplings", Skill.FARMING, ItemID.ACORN), + WILLOW_SAPLING(ItemID.WILLOW_SAPLING, "Saplings", Skill.FARMING, ItemID.WILLOW_SEED), + MAPLE_SAPLING(ItemID.MAPLE_SAPLING, "Saplings", Skill.FARMING, ItemID.MAPLE_SEED), + YEW_SAPLING(ItemID.YEW_SAPLING, "Saplings", Skill.FARMING, ItemID.YEW_SEED), + MAGIC_SAPLING(ItemID.MAGIC_SAPLING, "Saplings", Skill.FARMING, ItemID.MAGIC_SEED), + APPLE_TREE_SAPLING(ItemID.APPLE_SAPLING, "Saplings", Skill.FARMING, ItemID.APPLE_TREE_SEED), + BANANA_TREE_SAPLING(ItemID.BANANA_SAPLING, "Saplings", Skill.FARMING, ItemID.BANANA_TREE_SEED), + ORANGE_TREE_SAPLING(ItemID.ORANGE_SAPLING, "Saplings", Skill.FARMING, ItemID.ORANGE_TREE_SEED), + CURRY_TREE_SAPLING(ItemID.CURRY_SAPLING, "Saplings", Skill.FARMING, ItemID.CURRY_TREE_SEED), + PINEAPPLE_SAPLING(ItemID.PINEAPPLE_SAPLING, "Saplings", Skill.FARMING, ItemID.PINEAPPLE_SEED), + PAPAYA_TREE_SAPLING(ItemID.PAPAYA_SAPLING, "Saplings", Skill.FARMING, ItemID.PAPAYA_TREE_SEED), + PALM_TREE_SAPLING(ItemID.PALM_SAPLING, "Saplings", Skill.FARMING, ItemID.PALM_TREE_SEED), + CALQUAT_TREE_SAPLING(ItemID.CALQUAT_SAPLING, "Saplings", Skill.FARMING, ItemID.CALQUAT_TREE_SEED), + TEAK_SAPLING(ItemID.TEAK_SAPLING, "Saplings", Skill.FARMING, ItemID.TEAK_SEED), + MAHOGANY_SAPLING(ItemID.MAHOGANY_SAPLING, "Saplings", Skill.FARMING, ItemID.MAHOGANY_SEED), + SPIRIT_SAPLING(ItemID.SPIRIT_SAPLING, "Saplings", Skill.FARMING, ItemID.SPIRIT_SEED), + ; + + @Getter + private final int itemID; + @Getter + private final String category; + @Getter + private final Skill skill; + + /** + * Should be operated on and then treated like this item or does nothing if null. + * Used mostly for things like herblore where you want Grimy, Clean, and UNF to count for creating potions. + * To do this GRIMY links to CLEAN which links to UNFINISHED which links to null + */ + @Getter + private final int linkedItemId; + + @Getter + private boolean ignoreBonus; + + @Getter + private ItemComposition composition; + + CriticalItem(int itemID, String category, Skill skill, int linkedItem) + { + this.itemID = itemID; + this.category = category; + this.skill = skill; + this.linkedItemId = linkedItem; + this.composition = null; + this.ignoreBonus = false; + } + + CriticalItem(int itemID, String category, Skill skill) + { + this(itemID, category, skill, -1); + } + + CriticalItem(int itemID, String category, Skill skill, boolean ignoreBonusXp) + { + this(itemID, category, skill, -1); + this.ignoreBonus = ignoreBonusXp; + } + + // Builds a Map to reduce looping frequency + private static Map> buildSkillItemMap() + { + Map> map = new HashMap<>(); + for (CriticalItem item : values()) + { + map.computeIfAbsent(item.getSkill(), e -> new ArrayList<>()).add(item); + } + + return map; + } + private static final Map> bySkillName = buildSkillItemMap(); + public static ArrayList getBySkillName(Skill skill) + { + return bySkillName.get(skill); + } + + // Builds a Map to reduce looping frequency + private static Map> buildSkillCategoryMap() + { + Map> map = new HashMap<>(); + for (CriticalItem item : values()) + { + map.computeIfAbsent(item.getSkill(), k -> new HashSet<>()).add(item.category); + } + + return map; + } + private static final Map> bySkillCategory = buildSkillCategoryMap(); + public static Set getSkillCategories(Skill skill) + { + return bySkillCategory.get(skill); + } + + // Builds a Map to reduce looping frequency + private static Map> buildItemSkillCategoryMap() + { + Map> map = new HashMap<>(); + for (CriticalItem item : values()) + { + String key = item.getCategory() + item.skill.getName(); + map.computeIfAbsent(key, e -> new ArrayList<>()).add(item); + } + + return map; + } + private static final Map> itemsBySkillCategory = buildItemSkillCategoryMap(); + public static ArrayList getItemsForSkillCategories(Skill skill, String category) + { + return itemsBySkillCategory.get(category + skill.getName()); + } + + // Builds a Map to reduce looping frequency + private static Map buildItemsByIdMap() + { + Map map = new HashMap<>(); + for (CriticalItem item : values()) + { + map.put(item.getItemID(), item); + } + + return map; + } + private static final Map itemsById = buildItemsByIdMap(); + public static CriticalItem getByItemId(int id) + { + return itemsById.get(id); + } + + /** + * Attaches the Item Composition to each Critical Item on client initial load + * @param m ItemManager + */ + public static void prepareItemCompositions(ItemManager m) + { + for (CriticalItem i : values()) + { + i.composition = m.getItemComposition(i.getItemID()); + } + } + + @Override + public String toString() + { + return "CriticalItem=(name=" + this.name() + ",id=" + this.itemID + ",category=" + this.category + ")"; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/Activity.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/Activity.java new file mode 100644 index 0000000000..7d82ad720b --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/Activity.java @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2018, 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.skillcalculator.banked.beans; + +import lombok.Getter; +import net.runelite.api.ItemID; +import net.runelite.api.Skill; +import net.runelite.client.plugins.skillcalculator.banked.CriticalItem; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +@Getter +public enum Activity +{ + /** + * Herblore Activities + */ + // Creating Potions + // Guam + GUAM_POTION_UNF(ItemID.GUAM_POTION_UNF, "Unfinished Potion", Skill.HERBLORE, 1, 0, CriticalItem.GUAM_LEAF, ActivitySecondaries.UNFINISHED_POTION), + GUAM_TAR(ItemID.GUAM_TAR, "Guam tar", Skill.HERBLORE, 19, 30, CriticalItem.GUAM_LEAF, ActivitySecondaries.SWAMP_TAR, true), + + ATTACK_POTION(ItemID.ATTACK_POTION4, "Attack Potion", Skill.HERBLORE, 3, 25, CriticalItem.GUAM_LEAF_POTION_UNF, ActivitySecondaries.ATTACK_POTION), + // Marrentil + MARRENTILL_POTION_UNF(ItemID.MARRENTILL_POTION_UNF, "Unfinished Potion", Skill.HERBLORE, 1, 0, CriticalItem.MARRENTILL, ActivitySecondaries.UNFINISHED_POTION), + MARRENTILL_TAR(ItemID.MARRENTILL_TAR, "Marrentill tar", Skill.HERBLORE, 31, 42.5, CriticalItem.MARRENTILL, ActivitySecondaries.SWAMP_TAR, true), + + ANTIPOISON(ItemID.ANTIPOISON4, "Antipoison", Skill.HERBLORE, 5, 37.5, CriticalItem.MARRENTILL_POTION_UNF, ActivitySecondaries.ANTIPOISON), + // Tarromin + TARROMIN_POTION_UNF(ItemID.TARROMIN_POTION_UNF, "Unfinished Potion", Skill.HERBLORE, 1, 0, CriticalItem.TARROMIN, ActivitySecondaries.UNFINISHED_POTION), + TARROMIN_TAR(ItemID.TARROMIN_TAR, "Tarromin tar", Skill.HERBLORE, 39, 55, CriticalItem.TARROMIN, ActivitySecondaries.SWAMP_TAR, true), + + STRENGTH_POTION(ItemID.STRENGTH_POTION4, "Strength potion", Skill.HERBLORE, 12, 50, CriticalItem.TARROMIN_POTION_UNF, ActivitySecondaries.STRENGTH_POTION), + SERUM_207(ItemID.SERUM_207_4, "Serum 207", Skill.HERBLORE, 15, 50, CriticalItem.TARROMIN_POTION_UNF, ActivitySecondaries.SERUM_207), + // Harralander + HARRALANDER_POTION_UNF(ItemID.HARRALANDER_POTION_UNF, "Unfinished Potion", Skill.HERBLORE, 1, 0, CriticalItem.HARRALANDER, ActivitySecondaries.UNFINISHED_POTION), + HARRALANDER_TAR(ItemID.HARRALANDER_TAR, "Harralander tar", Skill.HERBLORE, 44, 72.5, CriticalItem.HARRALANDER, ActivitySecondaries.SWAMP_TAR, true), + + COMPOST_POTION(ItemID.COMPOST_POTION4, "Compost potion", Skill.HERBLORE, 21, 60, CriticalItem.HARRALANDER_POTION_UNF, ActivitySecondaries.COMPOST_POTION), + RESTORE_POTION(ItemID.RESTORE_POTION4, "Restore potion", Skill.HERBLORE, 22, 62.5, CriticalItem.HARRALANDER_POTION_UNF, ActivitySecondaries.RESTORE_POTION), + ENERGY_POTION(ItemID.ENERGY_POTION4, "Energy potion", Skill.HERBLORE, 26, 67.5, CriticalItem.HARRALANDER_POTION_UNF, ActivitySecondaries.ENERGY_POTION), + COMBAT_POTION(ItemID.COMBAT_POTION4, "Combat potion", Skill.HERBLORE, 36, 84, CriticalItem.HARRALANDER_POTION_UNF, ActivitySecondaries.COMBAT_POTION), + // Ranarr Weed + DEFENCE_POTION(ItemID.DEFENCE_POTION4, "Defence potion", Skill.HERBLORE, 30, 75, CriticalItem.RANARR_POTION_UNF, ActivitySecondaries.DEFENCE_POTION), + PRAYER_POTION(ItemID.PRAYER_POTION4, "Prayer potion", Skill.HERBLORE, 38, 87.5, CriticalItem.RANARR_POTION_UNF, ActivitySecondaries.PRAYER_POTION), + // Toadflax + AGILITY_POTION(ItemID.AGILITY_POTION4, "Agility potion", Skill.HERBLORE, 34, 80, CriticalItem.TOADFLAX_POTION_UNF, ActivitySecondaries.AGILITY_POTION), + SARADOMIN_BREW(ItemID.SARADOMIN_BREW4, "Saradomin brew", Skill.HERBLORE, 81, 180, CriticalItem.TOADFLAX_POTION_UNF, ActivitySecondaries.SARADOMIN_BREW), + // Irit + SUPER_ATTACK(ItemID.SUPER_ATTACK4, "Super attack", Skill.HERBLORE, 45, 100, CriticalItem.IRIT_POTION_UNF, ActivitySecondaries.SUPER_ATTACK), + SUPERANTIPOISON(ItemID.SUPERANTIPOISON4, "Superantipoison", Skill.HERBLORE, 48, 106.3, CriticalItem.IRIT_POTION_UNF, ActivitySecondaries.SUPERANTIPOISON), + // Avantoe + FISHING_POTION(ItemID.FISHING_POTION4, "Fishing potion", Skill.HERBLORE, 50, 112.5, CriticalItem.AVANTOE_POTION_UNF, ActivitySecondaries.FISHING_POTION), + SUPER_ENERGY_POTION(ItemID.SUPER_ENERGY3_20549, "Super energy potion", Skill.HERBLORE, 52, 117.5, CriticalItem.AVANTOE_POTION_UNF, ActivitySecondaries.SUPER_ENERGY_POTION), + HUNTER_POTION(ItemID.HUNTER_POTION4, "Hunter potion", Skill.HERBLORE, 53, 120, CriticalItem.AVANTOE_POTION_UNF, ActivitySecondaries.HUNTER_POTION), + // Kwuarm + SUPER_STRENGTH(ItemID.SUPER_STRENGTH4, "Super strength", Skill.HERBLORE, 55, 125, CriticalItem.KWUARM_POTION_UNF, ActivitySecondaries.SUPER_STRENGTH), + // Snapdragon + SUPER_RESTORE(ItemID.SUPER_RESTORE4, "Super restore", Skill.HERBLORE, 63, 142.5, CriticalItem.SNAPDRAGON_POTION_UNF, ActivitySecondaries.SUPER_RESTORE), + SANFEW_SERUM(ItemID.SANFEW_SERUM4, "Sanfew serum", Skill.HERBLORE, 65, 160, CriticalItem.SNAPDRAGON_POTION_UNF, ActivitySecondaries.SANFEW_SERUM), + // Cadantine + SUPER_DEFENCE_POTION(ItemID.SUPER_DEFENCE4, "Super defence", Skill.HERBLORE, 66, 150, CriticalItem.CADANTINE_POTION_UNF, ActivitySecondaries.SUPER_DEFENCE_POTION), + // Lantadyme + ANTIFIRE_POTION(ItemID.ANTIFIRE_POTION4, "Anti-fire potion", Skill.HERBLORE, 69, 157.5, CriticalItem.LANTADYME_POTION_UNF, ActivitySecondaries.ANTIFIRE_POTION), + MAGIC_POTION(ItemID.MAGIC_POTION4, "Magic potion", Skill.HERBLORE, 76, 172.5, CriticalItem.LANTADYME_POTION_UNF, ActivitySecondaries.MAGIC_POTION), + // Dwarf Weed + RANGING_POTION(ItemID.RANGING_POTION4, "Ranging potion", Skill.HERBLORE, 72, 162.5, CriticalItem.DWARF_WEED_POTION_UNF, ActivitySecondaries.RANGING_POTION), + // Torstol + TORSTOL_POTION_UNF(ItemID.TORSTOL_POTION_UNF, "Unfinished Potion", Skill.HERBLORE, 78, 0, CriticalItem.TORSTOL, ActivitySecondaries.UNFINISHED_POTION), + SUPER_COMBAT_POTION(ItemID.SUPER_COMBAT_POTION4, "Super combat", Skill.HERBLORE, 90, 150, CriticalItem.TORSTOL, ActivitySecondaries.SUPER_COMBAT_POTION, true), + ANTIVENOM_PLUS(ItemID.ANTIVENOM4_12913, "Anti-venom+", Skill.HERBLORE, 94, 125, CriticalItem.TORSTOL, ActivitySecondaries.ANTIVENOM_PLUS, true), + + ZAMORAK_BREW(ItemID.ZAMORAK_BREW4, "Zamorak brew", Skill.HERBLORE, 78, 175, CriticalItem.TORSTOL_POTION_UNF, ActivitySecondaries.ZAMORAK_BREW), + + // Cleaning Grimy Herbs + CLEAN_GUAM(ItemID.GUAM_LEAF, "Clean guam", Skill.HERBLORE, 3, 2.5, CriticalItem.GRIMY_GUAM_LEAF), + CLEAN_MARRENTILL(ItemID.MARRENTILL, "Clean marrentill", Skill.HERBLORE, 5, 3.8, CriticalItem.GRIMY_MARRENTILL), + CLEAN_TARROMIN(ItemID.TARROMIN, "Clean tarromin", Skill.HERBLORE, 11, 5, CriticalItem.GRIMY_TARROMIN), + CLEAN_HARRALANDER(ItemID.HARRALANDER, "Clean harralander", Skill.HERBLORE, 20, 6.3, CriticalItem.GRIMY_HARRALANDER), + CLEAN_RANARR_WEED(ItemID.RANARR_WEED, "Clean ranarr weed", Skill.HERBLORE, 25, 7.5, CriticalItem.GRIMY_RANARR_WEED), + CLEAN_TOADFLAX(ItemID.TOADFLAX, "Clean toadflax", Skill.HERBLORE, 30, 8, CriticalItem.GRIMY_TOADFLAX), + CLEAN_IRIT_LEAF(ItemID.IRIT_LEAF, "Clean irit leaf", Skill.HERBLORE, 40, 8.8, CriticalItem.GRIMY_IRIT_LEAF), + CLEAN_AVANTOE(ItemID.AVANTOE, "Clean avantoe", Skill.HERBLORE, 48, 10, CriticalItem.GRIMY_AVANTOE), + CLEAN_KWUARM(ItemID.KWUARM, "Clean kwuarm", Skill.HERBLORE, 54, 11.3, CriticalItem.GRIMY_KWUARM), + CLEAN_SNAPDRAGON(ItemID.SNAPDRAGON, "Clean snapdragon", Skill.HERBLORE, 59, 11.8, CriticalItem.GRIMY_SNAPDRAGON), + CLEAN_CADANTINE(ItemID.CADANTINE, "Clean cadantine", Skill.HERBLORE, 65, 12.5, CriticalItem.GRIMY_CADANTINE), + CLEAN_LANTADYME(ItemID.LANTADYME, "Clean lantadyme", Skill.HERBLORE, 67, 13.1, CriticalItem.GRIMY_LANTADYME), + CLEAN_DWARF_WEED(ItemID.DWARF_WEED, "Clean dwarf weed", Skill.HERBLORE, 70, 13.8, CriticalItem.GRIMY_DWARF_WEED), + CLEAN_TORSTOL(ItemID.TORSTOL, "Clean torstol", Skill.HERBLORE, 75, 15, CriticalItem.GRIMY_TORSTOL), + + /** + * Construction Options + */ + PLANKS(ItemID.PLANK , "Normal Plank Products", Skill.CONSTRUCTION, 1, 29, CriticalItem.PLANK), + OAK_PLANKS(ItemID.OAK_PLANK, "Normal Oak Products", Skill.CONSTRUCTION, 1, 60, CriticalItem.OAK_PLANK), + TEAK_PLANKS(ItemID.TEAK_PLANK, "Normal Teak Products", Skill.CONSTRUCTION, 1, 90, CriticalItem.TEAK_PLANK), + MYTHICAL_CAPE(ItemID.MYTHICAL_CAPE, "Mythical Cape Rakes", Skill.CONSTRUCTION, 1, 123.33, CriticalItem.TEAK_PLANK), + MAHOGANY_PLANKS(ItemID.MAHOGANY_PLANK, "Normal Mahogany Products", Skill.CONSTRUCTION, 1, 140, CriticalItem.MAHOGANY_PLANK), + + /** + * Prayer Options + */ + BONES(ItemID.BONES, "Bones", Skill.PRAYER, 1, 4.5, CriticalItem.BONES), + WOLF_BONES(ItemID.WOLF_BONES, "Bones", Skill.PRAYER, 1, 4.5, CriticalItem.WOLF_BONES), + BURNT_BONES(ItemID.BURNT_BONES, "Bones", Skill.PRAYER, 1, 4.5, CriticalItem.BURNT_BONES), + MONKEY_BONES(ItemID.MONKEY_BONES, "Bones", Skill.PRAYER, 1, 5.0, CriticalItem.MONKEY_BONES), + BAT_BONES(ItemID.BAT_BONES, "Bones", Skill.PRAYER, 1, 5.3, CriticalItem.BAT_BONES), + JOGRE_BONES(ItemID.JOGRE_BONES, "Bones", Skill.PRAYER, 1, 15.0, CriticalItem.JOGRE_BONES), + BIG_BONES(ItemID.BIG_BONES, "Bones", Skill.PRAYER, 1, 15.0, CriticalItem.BIG_BONES), + ZOGRE_BONES(ItemID.ZOGRE_BONES, "Bones", Skill.PRAYER, 1, 22.5, CriticalItem.ZOGRE_BONES), + SHAIKAHAN_BONES(ItemID.SHAIKAHAN_BONES, "Bones", Skill.PRAYER, 1, 25.0, CriticalItem.SHAIKAHAN_BONES), + BABYDRAGON_BONES(ItemID.BABYDRAGON_BONES, "Bones", Skill.PRAYER, 1, 30.0, CriticalItem.BABYDRAGON_BONES), + WYVERN_BONES(ItemID.WYVERN_BONES, "Bones", Skill.PRAYER, 1, 72.0, CriticalItem.WYVERN_BONES), + DRAGON_BONES(ItemID.DRAGON_BONES, "Bones", Skill.PRAYER, 1, 72.0, CriticalItem.DRAGON_BONES), + FAYRG_BONES(ItemID.FAYRG_BONES, "Bones", Skill.PRAYER, 1, 84.0, CriticalItem.FAYRG_BONES), + LAVA_DRAGON_BONES(ItemID.LAVA_DRAGON_BONES, "Bones", Skill.PRAYER, 1, 85.0, CriticalItem.LAVA_DRAGON_BONES), + RAURG_BONES(ItemID.RAURG_BONES, "Bones", Skill.PRAYER, 1, 96.0, CriticalItem.RAURG_BONES), + DAGANNOTH_BONES(ItemID.DAGANNOTH_BONES, "Bones", Skill.PRAYER, 1, 125.0, CriticalItem.DAGANNOTH_BONES), + OURG_BONES(ItemID.OURG_BONES, "Bones", Skill.PRAYER, 1, 140.0, CriticalItem.OURG_BONES), + SUPERIOR_DRAGON_BONES(ItemID.SUPERIOR_DRAGON_BONES, "Bones", Skill.PRAYER, 1, 150.0, CriticalItem.SUPERIOR_DRAGON_BONES), + // Shade Remains (Pyre Logs) + LOAR_REMAINS(ItemID.LOAR_REMAINS, "Shades", Skill.PRAYER, 1, 33.0, CriticalItem.LOAR_REMAINS), + PHRIN_REMAINS(ItemID.PHRIN_REMAINS, "Shades", Skill.PRAYER, 1, 46.5, CriticalItem.PHRIN_REMAINS), + RIYL_REMAINS(ItemID.RIYL_REMAINS, "Shades", Skill.PRAYER, 1, 59.5, CriticalItem.RIYL_REMAINS), + ASYN_REMAINS(ItemID.ASYN_REMAINS, "Shades", Skill.PRAYER, 1, 82.5, CriticalItem.ASYN_REMAINS), + FIYR_REMAINS(ItemID.FIYR_REMAINS, "Shades", Skill.PRAYER, 1, 84.0, CriticalItem.FIYR_REMAINS), + // Ensouled Heads + ENSOULED_GOBLIN_HEAD(ItemID.ENSOULED_GOBLIN_HEAD_13448, "Ensouled Heads", Skill.PRAYER, 1, 130.0, CriticalItem.ENSOULED_GOBLIN_HEAD), + ENSOULED_MONKEY_HEAD(ItemID.ENSOULED_MONKEY_HEAD_13451, "Ensouled Heads", Skill.PRAYER, 1, 182.0, CriticalItem.ENSOULED_MONKEY_HEAD), + ENSOULED_IMP_HEAD(ItemID.ENSOULED_IMP_HEAD_13454, "Ensouled Heads", Skill.PRAYER, 1, 286.0, CriticalItem.ENSOULED_IMP_HEAD), + ENSOULED_MINOTAUR_HEAD(ItemID.ENSOULED_MINOTAUR_HEAD_13457, "Ensouled Heads", Skill.PRAYER, 1, 364.0, CriticalItem.ENSOULED_MINOTAUR_HEAD), + ENSOULED_SCORPION_HEAD(ItemID.ENSOULED_SCORPION_HEAD_13460, "Ensouled Heads", Skill.PRAYER, 1, 454.0, CriticalItem.ENSOULED_SCORPION_HEAD), + ENSOULED_BEAR_HEAD(ItemID.ENSOULED_BEAR_HEAD_13463, "Ensouled Heads", Skill.PRAYER, 1, 480.0, CriticalItem.ENSOULED_BEAR_HEAD), + ENSOULED_UNICORN_HEAD(ItemID.ENSOULED_UNICORN_HEAD_13466, "Ensouled Heads", Skill.PRAYER, 1, 494.0, CriticalItem.ENSOULED_UNICORN_HEAD), + ENSOULED_DOG_HEAD(ItemID.ENSOULED_DOG_HEAD_13469, "Ensouled Heads", Skill.PRAYER, 1, 520.0, CriticalItem.ENSOULED_DOG_HEAD), + ENSOULED_CHAOS_DRUID_HEAD(ItemID.ENSOULED_CHAOS_DRUID_HEAD_13472, "Ensouled Heads", Skill.PRAYER, 1, 584.0, CriticalItem.ENSOULED_CHAOS_DRUID_HEAD), + ENSOULED_GIANT_HEAD(ItemID.ENSOULED_GIANT_HEAD_13475, "Ensouled Heads", Skill.PRAYER, 1, 650.0, CriticalItem.ENSOULED_GIANT_HEAD), + ENSOULED_OGRE_HEAD(ItemID.ENSOULED_OGRE_HEAD_13478, "Ensouled Heads", Skill.PRAYER, 1, 716.0, CriticalItem.ENSOULED_OGRE_HEAD), + ENSOULED_ELF_HEAD(ItemID.ENSOULED_ELF_HEAD_13481, "Ensouled Heads", Skill.PRAYER, 1, 754.0, CriticalItem.ENSOULED_ELF_HEAD), + ENSOULED_TROLL_HEAD(ItemID.ENSOULED_TROLL_HEAD_13484, "Ensouled Heads", Skill.PRAYER, 1, 780.0, CriticalItem.ENSOULED_TROLL_HEAD), + ENSOULED_HORROR_HEAD(ItemID.ENSOULED_HORROR_HEAD_13487, "Ensouled Heads", Skill.PRAYER, 1, 832.0, CriticalItem.ENSOULED_HORROR_HEAD), + ENSOULED_KALPHITE_HEAD(ItemID.ENSOULED_KALPHITE_HEAD_13490, "Ensouled Heads", Skill.PRAYER, 1, 884.0, CriticalItem.ENSOULED_KALPHITE_HEAD), + ENSOULED_DAGANNOTH_HEAD(ItemID.ENSOULED_DAGANNOTH_HEAD_13493, "Ensouled Heads", Skill.PRAYER, 1, 936.0, CriticalItem.ENSOULED_DAGANNOTH_HEAD), + ENSOULED_BLOODVELD_HEAD(ItemID.ENSOULED_BLOODVELD_HEAD_13496, "Ensouled Heads", Skill.PRAYER, 1, 1040.0, CriticalItem.ENSOULED_BLOODVELD_HEAD), + ENSOULED_TZHAAR_HEAD(ItemID.ENSOULED_TZHAAR_HEAD_13499, "Ensouled Heads", Skill.PRAYER, 1, 1104.0, CriticalItem.ENSOULED_TZHAAR_HEAD), + ENSOULED_DEMON_HEAD(ItemID.ENSOULED_DEMON_HEAD_13502, "Ensouled Heads", Skill.PRAYER, 1, 1170.0, CriticalItem.ENSOULED_DEMON_HEAD), + ENSOULED_AVIANSIE_HEAD(ItemID.ENSOULED_AVIANSIE_HEAD_13505, "Ensouled Heads", Skill.PRAYER, 1, 1234.0, CriticalItem.ENSOULED_AVIANSIE_HEAD), + ENSOULED_ABYSSAL_HEAD(ItemID.ENSOULED_ABYSSAL_HEAD_13508, "Ensouled Heads", Skill.PRAYER, 1, 1300.0, CriticalItem.ENSOULED_ABYSSAL_HEAD), + ENSOULED_DRAGON_HEAD(ItemID.ENSOULED_DRAGON_HEAD_13511, "Ensouled Heads", Skill.PRAYER, 1, 1560.0, CriticalItem.ENSOULED_DRAGON_HEAD), + + /* + * Cooking Items + */ + RAW_HERRING(ItemID.RAW_HERRING, "Fish", Skill.COOKING, 5, 50.0, CriticalItem.RAW_HERRING), + RAW_MACKEREL(ItemID.RAW_MACKEREL, "Fish", Skill.COOKING, 10, 60.0, CriticalItem.RAW_MACKEREL), + RAW_TROUT(ItemID.RAW_TROUT, "Fish", Skill.COOKING, 15, 70.0, CriticalItem.RAW_TROUT), + RAW_COD(ItemID.RAW_COD, "Fish", Skill.COOKING, 18, 75.0, CriticalItem.RAW_COD), + RAW_PIKE(ItemID.RAW_PIKE, "Fish", Skill.COOKING, 20, 80.0, CriticalItem.RAW_PIKE), + RAW_SALMON(ItemID.RAW_SALMON, "Fish", Skill.COOKING, 25, 90.0, CriticalItem.RAW_SALMON), + RAW_TUNA(ItemID.RAW_TUNA, "Fish", Skill.COOKING, 30, 100.0, CriticalItem.RAW_TUNA), + RAW_KARAMBWAN(ItemID.RAW_KARAMBWAN, "Fish", Skill.COOKING, 30, 190.0, CriticalItem.RAW_KARAMBWAN), + RAW_LOBSTER(ItemID.RAW_LOBSTER, "Fish", Skill.COOKING, 40, 120.0, CriticalItem.RAW_LOBSTER), + RAW_BASS(ItemID.RAW_BASS, "Fish", Skill.COOKING, 43, 130.0, CriticalItem.RAW_BASS), + RAW_SWORDFISH(ItemID.RAW_SWORDFISH, "Fish", Skill.COOKING, 45, 140.0, CriticalItem.RAW_SWORDFISH), + RAW_MONKFISH(ItemID.RAW_MONKFISH, "Fish", Skill.COOKING, 62, 150.0, CriticalItem.RAW_MONKFISH), + RAW_SHARK(ItemID.RAW_SHARK, "Fish", Skill.COOKING, 80, 210.0, CriticalItem.RAW_SHARK), + RAW_SEA_TURTLE(ItemID.RAW_SEA_TURTLE, "Fish", Skill.COOKING, 82, 211.3, CriticalItem.RAW_SEA_TURTLE), + RAW_ANGLERFISH(ItemID.RAW_ANGLERFISH, "Fish", Skill.COOKING, 84, 230.0, CriticalItem.RAW_ANGLERFISH), + RAW_DARK_CRAB(ItemID.RAW_DARK_CRAB, "Fish", Skill.COOKING, 90, 215.0, CriticalItem.RAW_DARK_CRAB), + RAW_MANTA_RAY(ItemID.RAW_MANTA_RAY, "Fish", Skill.COOKING, 91, 216.2, CriticalItem.RAW_MANTA_RAY), + + WINE(ItemID.JUG_OF_WINE, "Other", Skill.COOKING, 35, 200, CriticalItem.GRAPES, ActivitySecondaries.JUG_OF_WATER), + + /* + * Crafting Items + */ + // Spinning + BALL_OF_WOOL(ItemID.WOOL, "Misc", Skill.CRAFTING, 1, 2.5, CriticalItem.WOOL), + BOW_STRING(ItemID.BOW_STRING, "Misc", Skill.CRAFTING, 1, 15, CriticalItem.FLAX), + // Glass Blowing + BEER_GLASS(ItemID.BEER_GLASS, "Beer Glass", Skill.CRAFTING, 1, 17.5, CriticalItem.MOLTEN_GLASS), + CANDLE_LANTERN(ItemID.CANDLE_LANTERN, "Candle Lantern", Skill.CRAFTING, 4, 19, CriticalItem.MOLTEN_GLASS), + OIL_LAMP(ItemID.OIL_LAMP, "Oil Lamp", Skill.CRAFTING, 12, 25, CriticalItem.MOLTEN_GLASS), + VIAL(ItemID.VIAL, "Vial", Skill.CRAFTING, 33, 35, CriticalItem.MOLTEN_GLASS), + EMPTY_FISHBOWL(ItemID.EMPTY_FISHBOWL, "Empty fishbowl", Skill.CRAFTING, 42, 42.5, CriticalItem.MOLTEN_GLASS), + UNPOWERED_ORB(ItemID.UNPOWERED_ORB, "Unpowered orb", Skill.CRAFTING, 46, 52.5, CriticalItem.MOLTEN_GLASS), + LANTERN_LENS(ItemID.LANTERN_LENS, "Lantern lens", Skill.CRAFTING, 49, 55, CriticalItem.MOLTEN_GLASS), + LIGHT_ORB(ItemID.LIGHT_ORB, "Light orb", Skill.CRAFTING, 87, 70, CriticalItem.MOLTEN_GLASS), + // D'hide/Dragon Leather + GREEN_DRAGON_LEATHER(ItemID.GREEN_DRAGON_LEATHER, "D'hide", Skill.CRAFTING, 57, 62.0, CriticalItem.GREEN_DRAGON_LEATHER), + BLUE_DRAGON_LEATHER(ItemID.BLUE_DRAGON_LEATHER, "D'hide", Skill.CRAFTING, 66, 70.0, CriticalItem.BLUE_DRAGON_LEATHER), + RED_DRAGON_LEATHER(ItemID.RED_DRAGON_LEATHER, "D'hide", Skill.CRAFTING, 73, 78.0, CriticalItem.RED_DRAGON_LEATHER), + BLACK_DRAGON_LEATHER(ItemID.BLACK_DRAGON_LEATHER, "D'hide", Skill.CRAFTING, 79, 86.0, CriticalItem.BLACK_DRAGON_LEATHER), + // Uncut Gems + UNCUT_OPAL(ItemID.UNCUT_OPAL, "Gems", Skill.CRAFTING, 1, 15.0, CriticalItem.UNCUT_OPAL), + UNCUT_JADE(ItemID.UNCUT_JADE, "Gems", Skill.CRAFTING, 13, 20.0, CriticalItem.UNCUT_JADE), + UNCUT_RED_TOPAZ(ItemID.UNCUT_RED_TOPAZ, "Gems", Skill.CRAFTING, 16, 25.0, CriticalItem.UNCUT_RED_TOPAZ), + UNCUT_SAPPHIRE(ItemID.UNCUT_SAPPHIRE, "Gems", Skill.CRAFTING, 20, 50.0, CriticalItem.UNCUT_SAPPHIRE), + UNCUT_EMERALD(ItemID.UNCUT_EMERALD, "Gems", Skill.CRAFTING, 27, 67.5, CriticalItem.UNCUT_EMERALD), + UNCUT_RUBY(ItemID.UNCUT_RUBY, "Gems", Skill.CRAFTING, 34, 85, CriticalItem.UNCUT_RUBY), + UNCUT_DIAMOND(ItemID.UNCUT_DIAMOND, "Gems", Skill.CRAFTING, 43, 107.5, CriticalItem.UNCUT_DIAMOND), + UNCUT_DRAGONSTONE(ItemID.UNCUT_DRAGONSTONE, "Gems", Skill.CRAFTING, 55, 137.5, CriticalItem.UNCUT_DRAGONSTONE), + UNCUT_ONYX(ItemID.UNCUT_ONYX, "Gems", Skill.CRAFTING, 67, 167.5, CriticalItem.UNCUT_ONYX), + UNCUT_ZENYTE(ItemID.UNCUT_ZENYTE, "Gems", Skill.CRAFTING, 89, 200.0, CriticalItem.UNCUT_ZENYTE), + // Silver Jewelery + OPAL_RING(ItemID.OPAL_RING, "Opal ring", Skill.CRAFTING, 1 , 10, CriticalItem.OPAL, ActivitySecondaries.SILVER_BAR), + OPAL_NECKLACE(ItemID.OPAL_NECKLACE, "Opal necklace", Skill.CRAFTING, 16 , 35, CriticalItem.OPAL, ActivitySecondaries.SILVER_BAR), + OPAL_BRACELET(ItemID.OPAL_BRACELET, "Opal bracelet", Skill.CRAFTING, 22 , 45, CriticalItem.OPAL, ActivitySecondaries.SILVER_BAR), + OPAL_AMULET(ItemID.OPAL_AMULET, "Opal amulet", Skill.CRAFTING, 27 , 55, CriticalItem.OPAL, ActivitySecondaries.SILVER_BAR), + JADE_RING(ItemID.JADE_RING, "Jade ring", Skill.CRAFTING, 13 , 32, CriticalItem.JADE, ActivitySecondaries.SILVER_BAR), + JADE_NECKLACE(ItemID.JADE_NECKLACE, "Jade necklace", Skill.CRAFTING, 25 , 54, CriticalItem.JADE, ActivitySecondaries.SILVER_BAR), + JADE_BRACELET(ItemID.JADE_BRACELET, "Jade bracelet", Skill.CRAFTING, 29 , 60, CriticalItem.JADE, ActivitySecondaries.SILVER_BAR), + JADE_AMULET(ItemID.JADE_AMULET, "Jade amulet", Skill.CRAFTING, 34 , 70, CriticalItem.JADE, ActivitySecondaries.SILVER_BAR), + TOPAZ_RING(ItemID.TOPAZ_RING, "Topaz ring", Skill.CRAFTING, 16 , 35, CriticalItem.RED_TOPAZ, ActivitySecondaries.SILVER_BAR), + TOPAZ_NECKLACE(ItemID.TOPAZ_NECKLACE, "Topaz necklace", Skill.CRAFTING, 32 , 70, CriticalItem.RED_TOPAZ, ActivitySecondaries.SILVER_BAR), + TOPAZ_BRACELET(ItemID.TOPAZ_BRACELET, "Topaz bracelet", Skill.CRAFTING, 38 , 75, CriticalItem.RED_TOPAZ, ActivitySecondaries.SILVER_BAR), + TOPAZ_AMULET(ItemID.TOPAZ_AMULET, "Topaz amulet", Skill.CRAFTING, 45 , 80, CriticalItem.RED_TOPAZ, ActivitySecondaries.SILVER_BAR), + // Gold Jewelery + SAPPHIRE_RING(ItemID.SAPPHIRE_RING, "Sapphire ring", Skill.CRAFTING, 20 , 40, CriticalItem.SAPPHIRE, ActivitySecondaries.GOLD_BAR), + SAPPHIRE_NECKLACE(ItemID.SAPPHIRE_NECKLACE, "Sapphire necklace", Skill.CRAFTING, 22 , 55, CriticalItem.SAPPHIRE, ActivitySecondaries.GOLD_BAR), + SAPPHIRE_BRACELET(ItemID.SAPPHIRE_BRACELET, "Sapphire bracelet", Skill.CRAFTING, 23 , 60, CriticalItem.SAPPHIRE, ActivitySecondaries.GOLD_BAR), + SAPPHIRE_AMULET(ItemID.SAPPHIRE_AMULET, "Sapphire amulet", Skill.CRAFTING, 24 , 65, CriticalItem.SAPPHIRE, ActivitySecondaries.GOLD_BAR), + EMERALD_RING(ItemID.EMERALD_RING, "Emerald ring", Skill.CRAFTING, 27 , 55, CriticalItem.EMERALD, ActivitySecondaries.GOLD_BAR), + EMERALD_NECKLACE(ItemID.EMERALD_NECKLACE, "Emerald necklace", Skill.CRAFTING, 29 , 60, CriticalItem.EMERALD, ActivitySecondaries.GOLD_BAR), + EMERALD_BRACELET(ItemID.EMERALD_BRACELET, "Emerald bracelet", Skill.CRAFTING, 30 , 65, CriticalItem.EMERALD, ActivitySecondaries.GOLD_BAR), + EMERALD_AMULET(ItemID.EMERALD_AMULET, "Emerald amulet", Skill.CRAFTING, 31 , 70, CriticalItem.EMERALD, ActivitySecondaries.GOLD_BAR), + RUBY_RING(ItemID.RUBY_RING, "Ruby ring", Skill.CRAFTING, 34 , 70, CriticalItem.RUBY, ActivitySecondaries.GOLD_BAR), + RUBY_NECKLACE(ItemID.RUBY_NECKLACE, "Ruby necklace", Skill.CRAFTING, 40 , 75, CriticalItem.RUBY, ActivitySecondaries.GOLD_BAR), + RUBY_BRACELET(ItemID.RUBY_BRACELET, "Ruby bracelet", Skill.CRAFTING, 42 , 80, CriticalItem.RUBY, ActivitySecondaries.GOLD_BAR), + RUBY_AMULET(ItemID.RUBY_AMULET, "Ruby amulet", Skill.CRAFTING, 50 , 85, CriticalItem.RUBY, ActivitySecondaries.GOLD_BAR), + DIAMOND_RING(ItemID.DIAMOND_RING, "Diamond ring", Skill.CRAFTING, 43 , 85, CriticalItem.DIAMOND, ActivitySecondaries.GOLD_BAR), + DIAMOND_NECKLACE(ItemID.DIAMOND_NECKLACE, "Diamond necklace", Skill.CRAFTING, 56 , 90, CriticalItem.DIAMOND, ActivitySecondaries.GOLD_BAR), + DIAMOND_BRACELET(ItemID.DIAMOND_BRACELET, "Diamond bracelet", Skill.CRAFTING, 58 , 95, CriticalItem.DIAMOND, ActivitySecondaries.GOLD_BAR), + DIAMOND_AMULET(ItemID.DIAMOND_AMULET, "Diamond amulet", Skill.CRAFTING, 70 , 100, CriticalItem.DIAMOND, ActivitySecondaries.GOLD_BAR), + DRAGONSTONE_RING(ItemID.DRAGONSTONE_RING, "Dragonstone ring", Skill.CRAFTING, 55 , 100, CriticalItem.DRAGONSTONE, ActivitySecondaries.GOLD_BAR), + DRAGON_NECKLACE(ItemID.DRAGON_NECKLACE, "Dragon necklace", Skill.CRAFTING, 72 , 105, CriticalItem.DRAGONSTONE, ActivitySecondaries.GOLD_BAR), + DRAGONSTONE_BRACELET(ItemID.DRAGONSTONE_BRACELET, "Dragonstone bracelet", Skill.CRAFTING, 74 , 110, CriticalItem.DRAGONSTONE, ActivitySecondaries.GOLD_BAR), + DRAGONSTONE_AMULET(ItemID.DRAGONSTONE_AMULET, "Dragonstone amulet", Skill.CRAFTING, 80 , 150, CriticalItem.DRAGONSTONE, ActivitySecondaries.GOLD_BAR), + ONYX_RING(ItemID.ONYX_RING, "Onyx ring", Skill.CRAFTING, 67 , 115, CriticalItem.ONYX, ActivitySecondaries.GOLD_BAR), + ONYX_NECKLACE(ItemID.ONYX_NECKLACE, "Onyx necklace", Skill.CRAFTING, 82 , 120, CriticalItem.ONYX, ActivitySecondaries.GOLD_BAR), + REGEN_BRACELET(ItemID.REGEN_BRACELET, "Regen bracelet", Skill.CRAFTING, 84 , 125, CriticalItem.ONYX, ActivitySecondaries.GOLD_BAR), + ONYX_AMULET(ItemID.ONYX_AMULET, "Onyx amulet", Skill.CRAFTING, 90 , 165, CriticalItem.ONYX, ActivitySecondaries.GOLD_BAR), + ZENYTE_RING(ItemID.ZENYTE_RING, "Zenyte ring", Skill.CRAFTING, 89 , 150, CriticalItem.ZENYTE, ActivitySecondaries.GOLD_BAR), + ZENYTE_NECKLACE(ItemID.ZENYTE_NECKLACE, "Zenyte necklace", Skill.CRAFTING, 92 , 165, CriticalItem.ZENYTE, ActivitySecondaries.GOLD_BAR), + ZENYTE_BRACELET(ItemID.ZENYTE_BRACELET, "Zenyte bracelet", Skill.CRAFTING, 95 , 180, CriticalItem.ZENYTE, ActivitySecondaries.GOLD_BAR), + ZENYTE_AMULET(ItemID.ZENYTE_AMULET, "Zenyte amulet", Skill.CRAFTING, 98 , 200 , CriticalItem.ZENYTE, ActivitySecondaries.GOLD_BAR), + // Battle Staves + WATER_BATTLESTAFF(ItemID.WATER_BATTLESTAFF, "Water battlestaff", Skill.CRAFTING, 54, 100, CriticalItem.BATTLESTAFF, ActivitySecondaries.WATER_ORB), + EARTH_BATTLESTAFF(ItemID.EARTH_BATTLESTAFF, "Earth battlestaff", Skill.CRAFTING, 58, 112.5, CriticalItem.BATTLESTAFF, ActivitySecondaries.EARTH_ORB), + FIRE_BATTLESTAFF(ItemID.FIRE_BATTLESTAFF, "Fire battlestaff", Skill.CRAFTING, 62, 125, CriticalItem.BATTLESTAFF, ActivitySecondaries.FIRE_ORB), + AIR_BATTLESTAFF(ItemID.AIR_BATTLESTAFF, "Air battlestaff", Skill.CRAFTING, 66, 137.5, CriticalItem.BATTLESTAFF, ActivitySecondaries.AIR_ORB), + + /* + * Smithing Items + */ + + // Smelting ores (Furnace) + IRON_ORE(ItemID.IRON_BAR, "Iron Bars", Skill.SMITHING, 15, 12.5, CriticalItem.IRON_ORE, ActivitySecondaries.COAL_ORE), + STEEL_ORE(ItemID.STEEL_BAR, "Steel Bars", Skill.SMITHING, 30, 17.5, CriticalItem.IRON_ORE, ActivitySecondaries.COAL_ORE_2), + SILVER_ORE(ItemID.SILVER_ORE, "Bar", Skill.SMITHING, 20, 13.67, CriticalItem.SILVER_ORE), + GOLD_ORE(ItemID.GOLD_BAR, "Regular exp", Skill.SMITHING, 40, 22.5, CriticalItem.GOLD_ORE), + GOLD_ORE_GAUNTLETS(ItemID.GOLDSMITH_GAUNTLETS, "Goldsmith Gauntlets", Skill.SMITHING, 40, 56.2, CriticalItem.GOLD_ORE), + MITHRIL_ORE(ItemID.MITHRIL_ORE, "Bar", Skill.SMITHING, 50, 30, CriticalItem.MITHRIL_ORE, ActivitySecondaries.COAL_ORE_4), + ADAMANTITE_ORE(ItemID.ADAMANTITE_ORE, "Bar", Skill.SMITHING, 70, 37.5, CriticalItem.ADAMANTITE_ORE, ActivitySecondaries.COAL_ORE_6), + RUNITE_ORE(ItemID.RUNITE_ORE, "Bar", Skill.SMITHING, 85, 50, CriticalItem.RUNITE_ORE, ActivitySecondaries.COAL_ORE_8), + + // Smelting bars (Anvil) + BRONZE_BAR(ItemID.BRONZE_BAR, "Bars", Skill.SMITHING, 1, 12.5, CriticalItem.BRONZE_BAR), + IRON_BAR(ItemID.IRON_BAR, "Bars", Skill.SMITHING, 15, 25.0, CriticalItem.IRON_BAR), + STEEL_BAR(ItemID.STEEL_BAR, "Steel Products", Skill.SMITHING, 30, 37.5, CriticalItem.STEEL_BAR), + CANNONBALLS(ItemID.CANNONBALL, "Cannonballs", Skill.SMITHING, 35, 25.5, CriticalItem.STEEL_BAR), + MITHRIL_BAR(ItemID.MITHRIL_BAR, "Bars", Skill.SMITHING, 50, 50.0, CriticalItem.MITHRIL_BAR), + ADAMANTITE_BAR(ItemID.ADAMANTITE_BAR, "Bars", Skill.SMITHING, 70, 62.5, CriticalItem.ADAMANTITE_BAR), + RUNITE_BAR(ItemID.RUNITE_BAR, "Bars", Skill.SMITHING, 85, 75.0, CriticalItem.RUNITE_BAR), + + /** + * Farming Items + */ + ACORN(ItemID.ACORN, "Seeds", Skill.FARMING, 15, 481.3, CriticalItem.ACORN), + WILLOW_SEED(ItemID.WILLOW_SEED, "Seeds", Skill.FARMING, 30, 1481.5, CriticalItem.WILLOW_SEED), + MAPLE_SEED(ItemID.MAPLE_SEED, "Seeds", Skill.FARMING, 45, 3448.4, CriticalItem.MAPLE_SEED), + YEW_SEED(ItemID.YEW_SEED, "Seeds", Skill.FARMING, 60, 7150.9, CriticalItem.YEW_SEED), + MAGIC_SEED(ItemID.MAGIC_SEED, "Seeds", Skill.FARMING, 75, 13913.8, CriticalItem.MAGIC_SEED), + APPLE_TREE_SEED(ItemID.APPLE_TREE_SEED, "Seeds", Skill.FARMING, 27, 1272.5, CriticalItem.APPLE_TREE_SEED), + BANANA_TREE_SEED(ItemID.BANANA_TREE_SEED, "Seeds", Skill.FARMING, 33, 1841.5, CriticalItem.BANANA_TREE_SEED), + ORANGE_TREE_SEED(ItemID.ORANGE_TREE_SEED, "Seeds", Skill.FARMING, 39, 2586.7, CriticalItem.ORANGE_TREE_SEED), + CURRY_TREE_SEED(ItemID.CURRY_TREE_SEED, "Seeds", Skill.FARMING, 42, 3036.9, CriticalItem.CURRY_TREE_SEED), + PINEAPPLE_SEED(ItemID.PINEAPPLE_SEED, "Seeds", Skill.FARMING, 51, 4791.7, CriticalItem.PINEAPPLE_SEED), + PAPAYA_TREE_SEED(ItemID.PAPAYA_TREE_SEED, "Seeds", Skill.FARMING, 57, 6380.4, CriticalItem.PAPAYA_TREE_SEED), + PALM_TREE_SEED(ItemID.PALM_TREE_SEED, "Seeds", Skill.FARMING, 68, 10509.6, CriticalItem.PALM_TREE_SEED), + CALQUAT_TREE_SEED(ItemID.CALQUAT_TREE_SEED, "Seeds", Skill.FARMING, 72, 12516.5, CriticalItem.CALQUAT_TREE_SEED), + TEAK_SEED(ItemID.TEAK_SEED, "Seeds", Skill.FARMING, 35, 7325, CriticalItem.TEAK_SEED), + MAHOGANY_SEED(ItemID.MAHOGANY_SEED, "Seeds", Skill.FARMING, 55, 15783, CriticalItem.MAHOGANY_SEED), + SPIRIT_SEED(ItemID.SPIRIT_SEED, "Seeds", Skill.FARMING, 83, 19500, CriticalItem.SPIRIT_SEED), + ; + + private final int icon; + private final String name; + private final Skill skill; + private final int level; + private final double xp; + private final SecondaryItem[] secondaries; + private final CriticalItem criticalItem; + private final boolean preventLinked; + + Activity(int Icon, String name, Skill skill, int level, double xp, CriticalItem criticalItem) + { + this.icon = Icon; + this.name = name; + this.skill = skill; + this.level = level; + this.xp = xp; + this.criticalItem = criticalItem; + this.secondaries = new SecondaryItem[0]; + this.preventLinked = false; + } + + Activity(int Icon, String name, Skill skill, int level, double xp, CriticalItem criticalItem, ActivitySecondaries secondaries) + { + this.icon = Icon; + this.name = name; + this.skill = skill; + this.level = level; + this.xp = xp; + this.criticalItem = criticalItem; + this.secondaries = secondaries == null ? new SecondaryItem[0] : secondaries.getItems(); + this.preventLinked = false; + } + + Activity(int Icon, String name, Skill skill, int level, double xp, CriticalItem criticalItem, ActivitySecondaries secondaries, boolean preventLinked) + { + this.icon = Icon; + this.name = name; + this.skill = skill; + this.level = level; + this.xp = xp; + this.criticalItem = criticalItem; + this.secondaries = secondaries == null ? new SecondaryItem[0] : secondaries.getItems(); + this.preventLinked = preventLinked; + } + + // Builds a Map to reduce looping frequency + private static Map> buildItemMap() + { + Map> map = new HashMap<>(); + for (Activity item : values()) + { + map.computeIfAbsent(item.getCriticalItem(), e -> new ArrayList()).add(item); + } + + return map; + } + private static final Map> byCriticalItem = buildItemMap(); + public static ArrayList getByCriticalItem(CriticalItem item) + { + + return byCriticalItem.getOrDefault(item, new ArrayList<>()); + } + + /** + * Get all Activities for this CriticalItem + * @param item CriticalItem to check for + * @param limitLevel Level to check Activitiy requirements against. -1 or 0 value disables limits + * @return an empty list if no activities + */ + public static ArrayList getByCriticalItem(CriticalItem item, int limitLevel) + { + ArrayList activities = getByCriticalItem(item); + ArrayList l = new ArrayList<>(); + if (limitLevel <= 0) + { + return l; + } + + for (Activity a : activities) + { + if (!(a.getLevel() > limitLevel)) + { + l.add(a); + } + } + + return l; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/ActivitySecondaries.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/ActivitySecondaries.java new file mode 100644 index 0000000000..94a74bd6fd --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/ActivitySecondaries.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2018, 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.skillcalculator.banked.beans; + +import lombok.Getter; +import net.runelite.api.ItemID; + +@Getter +public enum ActivitySecondaries +{ + /** + * Herblore + */ + UNFINISHED_POTION(new SecondaryItem(ItemID.VIAL_OF_WATER, 1)), + SWAMP_TAR(new SecondaryItem(ItemID.SWAMP_TAR, 15)), + + // Guam + ATTACK_POTION(new SecondaryItem(ItemID.EYE_OF_NEWT)), + // Marrentil + ANTIPOISON(new SecondaryItem(ItemID.UNICORN_HORN_DUST)), + // Tarromin + STRENGTH_POTION(new SecondaryItem(ItemID.LIMPWURT_ROOT)), + SERUM_207(new SecondaryItem(ItemID.ASHES)), + // Harralander + COMPOST_POTION(new SecondaryItem(ItemID.VOLCANIC_ASH)), + RESTORE_POTION(new SecondaryItem(ItemID.RED_SPIDERS_EGGS)), + ENERGY_POTION(new SecondaryItem(ItemID.CHOCOLATE_DUST)), + COMBAT_POTION(new SecondaryItem(ItemID.GOAT_HORN_DUST)), + // Ranarr Weed + DEFENCE_POTION(new SecondaryItem(ItemID.WHITE_BERRIES)), + PRAYER_POTION(new SecondaryItem(ItemID.SNAPE_GRASS)), + // Toadflax + AGILITY_POTION(new SecondaryItem(ItemID.TOADS_LEGS)), + SARADOMIN_BREW(new SecondaryItem(ItemID.CRUSHED_NEST)), + // Irit + SUPER_ATTACK(new SecondaryItem(ItemID.EYE_OF_NEWT)), + SUPERANTIPOISON(new SecondaryItem(ItemID.UNICORN_HORN_DUST)), + // Avantoe + FISHING_POTION(new SecondaryItem(ItemID.SNAPE_GRASS)), + SUPER_ENERGY_POTION(new SecondaryItem(ItemID.MORT_MYRE_FUNGUS)), + HUNTER_POTION(new SecondaryItem(ItemID.KEBBIT_TEETH_DUST)), + // Kwuarm + SUPER_STRENGTH(new SecondaryItem(ItemID.LIMPWURT_ROOT)), + // Snapdragon + SUPER_RESTORE(new SecondaryItem(ItemID.RED_SPIDERS_EGGS)), + SANFEW_SERUM(new SecondaryItem(ItemID.SNAKE_WEED), new SecondaryItem(ItemID.UNICORN_HORN_DUST), new SecondaryItem(ItemID.SUPER_RESTORE4), new SecondaryItem(ItemID.NAIL_BEAST_NAILS)), + // Cadantine + SUPER_DEFENCE_POTION(new SecondaryItem(ItemID.WHITE_BERRIES)), + // Lantadyme + ANTIFIRE_POTION(new SecondaryItem(ItemID.DRAGON_SCALE_DUST)), + MAGIC_POTION(new SecondaryItem(ItemID.POTATO_CACTUS)), + // Dwarf Weed + RANGING_POTION(new SecondaryItem(ItemID.WINE_OF_ZAMORAK)), + // Torstol + ZAMORAK_BREW(new SecondaryItem(ItemID.JANGERBERRIES)), + SUPER_COMBAT_POTION(new SecondaryItem(ItemID.SUPER_ATTACK3), new SecondaryItem(ItemID.SUPER_STRENGTH3), new SecondaryItem(ItemID.SUPER_DEFENCE3)), + ANTIVENOM_PLUS(new SecondaryItem(ItemID.ANTIVENOM4)), + + /** + * Smithing + */ + COAL_ORE(new SecondaryItem(ItemID.COAL)), + COAL_ORE_2(new SecondaryItem(ItemID.COAL, 2)), + COAL_ORE_4(new SecondaryItem(ItemID.COAL, 4)), + COAL_ORE_6(new SecondaryItem(ItemID.COAL, 6)), + COAL_ORE_8(new SecondaryItem(ItemID.COAL, 8)), + + /** + * Crafting + */ + GOLD_BAR(new SecondaryItem(ItemID.GOLD_BAR)), + SILVER_BAR(new SecondaryItem(ItemID.SILVER_BAR)), + WATER_ORB(new SecondaryItem(ItemID.WATER_ORB)), + EARTH_ORB(new SecondaryItem(ItemID.EARTH_ORB)), + FIRE_ORB(new SecondaryItem(ItemID.FIRE_ORB)), + AIR_ORB(new SecondaryItem(ItemID.AIR_ORB)), + + /** + * Cooking + */ + JUG_OF_WATER(new SecondaryItem(ItemID.JUG_OF_WATER)), + ; + private final SecondaryItem[] items; + + ActivitySecondaries(SecondaryItem... items) + { + this.items = items; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/SecondaryItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/SecondaryItem.java new file mode 100644 index 0000000000..68f0042fbf --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/beans/SecondaryItem.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018, 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.skillcalculator.banked.beans; + +import lombok.Getter; + +@Getter +public class SecondaryItem +{ + private final int id; + private final int qty; + + public SecondaryItem(int id, int qty) + { + this.id = id; + this.qty = qty; + } + + public SecondaryItem(int id) + { + this(id, 1); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/ui/CriticalItemPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/ui/CriticalItemPanel.java new file mode 100644 index 0000000000..7214956e80 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/banked/ui/CriticalItemPanel.java @@ -0,0 +1,401 @@ +/* + * Copyright (c) 2018, 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.skillcalculator.banked.ui; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import javax.imageio.ImageIO; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import javax.swing.border.MatteBorder; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.game.AsyncBufferedImage; +import net.runelite.client.game.ItemManager; +import net.runelite.client.plugins.skillcalculator.BankedCalculator; +import net.runelite.client.plugins.skillcalculator.banked.CriticalItem; +import net.runelite.client.plugins.skillcalculator.banked.beans.Activity; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.components.materialtabs.MaterialTab; +import net.runelite.client.ui.components.materialtabs.MaterialTabGroup; +import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; +import net.runelite.client.util.StackFormatter; + +public class CriticalItemPanel extends JPanel +{ + private static final Dimension ICON_SIZE = new Dimension(36, 36); + private static final DecimalFormat FORMAT_COMMA = new DecimalFormat("#,###.#"); + + private static final BufferedImage ICON_SETTINGS; + + private static final Border PANEL_BORDER = new EmptyBorder(3, 0, 3, 0); + private final static Color BACKGROUND_COLOR = ColorScheme.DARKER_GRAY_COLOR; + private final static Color BUTTON_HOVER_COLOR = ColorScheme.DARKER_GRAY_HOVER_COLOR; + + static + { + BufferedImage i1; + try + { + synchronized (ImageIO.class) + { + i1 = ImageIO.read(BankedCalculator.class.getResourceAsStream("view-more-white.png")); + } + } + catch (IOException e) + { + throw new RuntimeException(e); + } + ICON_SETTINGS = i1; + } + + private final BankedCalculator bankedCalculator; + private final CriticalItem item; + private final ItemManager itemManager; + private double xp; + @Getter + private int amount; + @Getter + private double total; + private Map linkedMap; + private JShadowedLabel labelValue; + + private final JPanel infoContainer; + private final JLabel image; + private boolean infoVisibility = false; + + public CriticalItemPanel(BankedCalculator bankedCalculator, ItemManager itemManager, CriticalItem item, double xp, int amount, Map linkedMap) + { + this.bankedCalculator = bankedCalculator; + this.item = item; + this.xp = xp; + this.amount = amount; + this.total = xp * amount; + this.itemManager = itemManager; + this.linkedMap = linkedMap; + + this.setLayout(new GridBagLayout()); + this.setBorder(PANEL_BORDER); + this.setBackground(ColorScheme.DARK_GRAY_COLOR); + this.setVisible(this.amount > 0); + + infoContainer = new JPanel(); + infoContainer.setLayout(new GridBagLayout()); + infoContainer.setVisible(false); + infoContainer.setBackground(BACKGROUND_COLOR); + infoContainer.setBorder(new MatteBorder(1, 0, 0, 0, Color.GRAY)); + + // Icon + AsyncBufferedImage icon = itemManager.getImage(item.getItemID(), amount, item.getComposition().isStackable() || amount > 1); + image = new JLabel(); + image.setMinimumSize(ICON_SIZE); + image.setMaximumSize(ICON_SIZE); + image.setPreferredSize(ICON_SIZE); + image.setHorizontalAlignment(SwingConstants.CENTER); + image.setBorder(new EmptyBorder(0, 8, 0, 0)); + + Runnable resize = () -> + image.setIcon(new ImageIcon(icon.getScaledInstance((int)ICON_SIZE.getWidth(), (int)ICON_SIZE.getHeight(), Image.SCALE_SMOOTH))); + icon.onChanged(resize); + resize.run(); + + // Container for Info + JPanel uiInfo = new JPanel(new GridLayout(2, 1)); + uiInfo.setBorder(new EmptyBorder(0, 5, 0, 0)); + uiInfo.setBackground(BACKGROUND_COLOR); + + JShadowedLabel labelName = new JShadowedLabel(item.getComposition().getName()); + labelName.setForeground(Color.WHITE); + labelName.setVerticalAlignment(SwingUtilities.BOTTOM); + + labelValue = new JShadowedLabel(); + labelValue.setFont(FontManager.getRunescapeSmallFont()); + labelValue.setVerticalAlignment(SwingUtilities.TOP); + updateXp(xp); + + uiInfo.add(labelName); + uiInfo.add(labelValue); + + // Settings Button + JLabel settingsButton = new JLabel(); + settingsButton.setBorder(new EmptyBorder(0, 5, 0, 5)); + settingsButton.setIcon(new ImageIcon(ICON_SETTINGS)); + settingsButton.setOpaque(true); + settingsButton.setBackground(BACKGROUND_COLOR); + + settingsButton.addMouseListener(new MouseAdapter() + { + @Override + public void mouseEntered(MouseEvent e) + { + settingsButton.setBackground(BUTTON_HOVER_COLOR); + } + + @Override + public void mouseExited(MouseEvent e) + { + settingsButton.setBackground(BACKGROUND_COLOR); + } + + @Override + public void mouseClicked(MouseEvent e) + { + toggleInfo(); + } + }); + + // Create and append elements to container panel + JPanel panel = new JPanel(); + panel.setLayout(new BorderLayout()); + panel.setBackground(BACKGROUND_COLOR); + + panel.add(image, BorderLayout.LINE_START); + panel.add(uiInfo, BorderLayout.CENTER); + + // Only add button if has activity selection options or linked items + List activities = Activity.getByCriticalItem(item); + // If linked map has 1 item and it isn't this item still show breakdown (cleaned herbs into unfinished) + if ( (linkedMap.size() > 1 || (linkedMap.size() == 1 && linkedMap.get(item) == null)) + || activities.size() > 1) + { + panel.add(settingsButton, BorderLayout.LINE_END); + } + + panel.setToolTipText("" + item.getComposition().getName() + + "
xp: " + xp + + "
Total: " + StackFormatter.quantityToStackSize((long) total) + " 1 || (linkedMap.size() == 1 && linkedMap.get(item) == null)) + { + JLabel l = new JLabel("Item Breakdown"); + l.setBorder(new EmptyBorder(3, 0, 3, 0)); + l.setHorizontalAlignment(JLabel.CENTER); + infoContainer.add(l, c); + c.gridy++; + + JPanel con = new JPanel(); + con.setLayout(new GridBagLayout()); + con.setBackground(BACKGROUND_COLOR); + for (Map.Entry e : linkedMap.entrySet()) + { + // Icon + AsyncBufferedImage icon = itemManager.getImage(e.getKey().getItemID(), e.getValue(), e.getKey().getComposition().isStackable() || e.getValue() > 1); + JLabel image = new JLabel(); + image.setMinimumSize(ICON_SIZE); + image.setMaximumSize(ICON_SIZE); + image.setPreferredSize(ICON_SIZE); + image.setHorizontalAlignment(SwingConstants.CENTER); + image.setBorder(new EmptyBorder(0, 8, 0, 0)); + + Runnable resize = () -> + image.setIcon(new ImageIcon(icon.getScaledInstance((int)ICON_SIZE.getWidth(), (int)ICON_SIZE.getHeight(), Image.SCALE_SMOOTH))); + icon.onChanged(resize); + resize.run(); + + image.setToolTipText(e.getKey().getComposition().getName()); + + con.add(image, c); + c.gridx++; + } + c.gridx = 0; + infoContainer.add(con, c); + } + + } + + private JPanel createActivitiesPanel() + { + ArrayList activities = Activity.getByCriticalItem(item); + if (activities == null || activities.size() == 1) + { + return null; + } + + JPanel p = new JPanel(); + p.setBackground(BACKGROUND_COLOR); + p.setLayout(new BorderLayout()); + + JLabel label = new JLabel("Possible training methods"); + + MaterialTabGroup group = new MaterialTabGroup(); + group.setLayout(new GridLayout(0, 6, 0, 2)); + group.setBorder(new MatteBorder(1, 1, 1, 1, Color.BLACK)); + + Activity selected = this.bankedCalculator.getSelectedActivity(this.item); + boolean s = false; + + for (Activity option : activities) + { + AsyncBufferedImage icon = itemManager.getImage(option.getIcon()); + MaterialTab matTab = new MaterialTab("", group, null); + matTab.setHorizontalAlignment(SwingUtilities.RIGHT); + matTab.setToolTipText(option.getName()); + + Runnable resize = () -> + matTab.setIcon(new ImageIcon(icon.getScaledInstance(24, 24, Image.SCALE_SMOOTH))); + icon.onChanged(resize); + resize.run(); + + group.addTab(matTab); + + // Select first option by default + if (!s) + { + s = true; + group.select(matTab); + } + + // Select the option if its their selected activity + if (option.equals(selected)) + { + group.select(matTab); + } + + // Add click event handler now to prevent above code from triggering it. + matTab.setOnSelectEvent(() -> + { + bankedCalculator.activitySelected(item, option); + return true; + }); + } + + p.add(label, BorderLayout.NORTH); + p.add(group, BorderLayout.SOUTH); + + return p; + } + + public void updateXp(double newXpRate) + { + xp = newXpRate; + total = xp * amount; + labelValue.setText(FORMAT_COMMA.format(total) + "xp"); + } + + public void updateAmount(int newAmount, boolean forceVisible) + { + this.setVisible(newAmount > 0 || forceVisible); + this.amount = newAmount; + AsyncBufferedImage icon = itemManager.getImage(item.getItemID(), amount, item.getComposition().isStackable() || amount > 1); + Runnable resize = () -> + image.setIcon(new ImageIcon(icon.getScaledInstance((int)ICON_SIZE.getWidth(), (int)ICON_SIZE.getHeight(), Image.SCALE_SMOOTH))); + icon.onChanged(resize); + resize.run(); + } + + public void updateLinkedMap(Map newLinkedMap) + { + this.linkedMap = newLinkedMap; + + int sum = 0; + for (Integer v : newLinkedMap.values()) + { + sum += v; + } + this.updateAmount(sum, false); + + this.updateXp(xp); + + // Refresh info panel if visible + if (infoVisibility) + { + createInfoPanel(); + } + } + + public void recalculate() + { + updateXp(xp); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/materialtabs/MaterialTabGroup.java b/runelite-client/src/main/java/net/runelite/client/ui/components/materialtabs/MaterialTabGroup.java index e98b520fdc..a25019ad8f 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/materialtabs/MaterialTabGroup.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/materialtabs/MaterialTabGroup.java @@ -127,4 +127,11 @@ public class MaterialTabGroup extends JPanel return true; } + + @Override + public void removeAll() + { + super.removeAll(); + tabs.clear(); + } } \ No newline at end of file diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/view-more-white.png b/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/view-more-white.png new file mode 100644 index 0000000000..44f0180d18 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/view-more-white.png differ