diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerBox.java index 6ed843e12e..b4ffdcb44e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerBox.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerBox.java @@ -29,23 +29,31 @@ import com.google.common.base.Strings; import java.awt.BorderLayout; import java.awt.Color; import java.awt.GridLayout; +import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.function.BiConsumer; import javax.annotation.Nullable; +import javax.swing.ImageIcon; import javax.swing.JLabel; +import javax.swing.JMenuItem; import javax.swing.JPanel; +import javax.swing.JPopupMenu; import javax.swing.SwingConstants; import javax.swing.border.EmptyBorder; import lombok.Getter; +import net.runelite.client.game.AsyncBufferedImage; import net.runelite.client.game.ItemManager; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.FontManager; +import net.runelite.client.util.ImageUtil; import net.runelite.client.util.StackFormatter; class LootTrackerBox extends JPanel { private static final int ITEMS_PER_ROW = 5; + private final JPanel itemContainer = new JPanel(); private final JLabel priceLabel = new JLabel(); private final JLabel subTitleLabel = new JLabel(); @@ -58,10 +66,20 @@ class LootTrackerBox extends JPanel @Getter private long totalPrice; - LootTrackerBox(final ItemManager itemManager, final String id, @Nullable final String subtitle) + private boolean hideIgnoredItems; + private BiConsumer onItemToggle; + + LootTrackerBox( + final ItemManager itemManager, + final String id, + @Nullable final String subtitle, + final boolean hideIgnoredItems, + final BiConsumer onItemToggle) { this.id = id; this.itemManager = itemManager; + this.onItemToggle = onItemToggle; + this.hideIgnoredItems = hideIgnoredItems; setLayout(new BorderLayout(0, 1)); setBorder(new EmptyBorder(5, 0, 0, 0)); @@ -93,13 +111,21 @@ class LootTrackerBox extends JPanel add(itemContainer, BorderLayout.CENTER); } - int getTotalKills() + /** + * Returns total amount of kills, removing ignored kills when necessary + * + * @return total amount of kills + */ + long getTotalKills() { - return records.size(); + return hideIgnoredItems + ? records.stream().filter(r -> !Arrays.stream(r.getItems()).allMatch(LootTrackerItem::isIgnored)).count() + : records.size(); } /** * Checks if this box matches specified record + * * @param record loot record * @return true if match is made */ @@ -110,6 +136,7 @@ class LootTrackerBox extends JPanel /** * Checks if this box matches specified id + * * @param id other record id * @return true if match is made */ @@ -156,13 +183,30 @@ class LootTrackerBox extends JPanel final List items = new ArrayList<>(); totalPrice = 0; - for (LootTrackerRecord records : records) + for (LootTrackerRecord record : records) { - allItems.addAll(Arrays.asList(records.getItems())); + allItems.addAll(Arrays.asList(record.getItems())); + } + + if (hideIgnoredItems) + { + /* If all the items in this box are ignored */ + boolean hideBox = allItems.stream().allMatch(LootTrackerItem::isIgnored); + setVisible(!hideBox); + + if (hideBox) + { + return; + } } for (final LootTrackerItem entry : allItems) { + if (entry.isIgnored() && hideIgnoredItems) + { + continue; + } + totalPrice += entry.getPrice(); int quantity = 0; @@ -175,12 +219,13 @@ class LootTrackerBox extends JPanel break; } } + if (quantity > 0) { int newQuantity = entry.getQuantity() + quantity; long pricePerItem = entry.getPrice() == 0 ? 0 : (entry.getPrice() / entry.getQuantity()); - items.add(new LootTrackerItem(entry.getId(), entry.getName(), newQuantity, pricePerItem * newQuantity)); + items.add(new LootTrackerItem(entry.getId(), entry.getName(), newQuantity, pricePerItem * newQuantity, entry.isIgnored())); } else { @@ -208,8 +253,39 @@ class LootTrackerBox extends JPanel imageLabel.setToolTipText(buildToolTip(item)); imageLabel.setVerticalAlignment(SwingConstants.CENTER); imageLabel.setHorizontalAlignment(SwingConstants.CENTER); - itemManager.getImage(item.getId(), item.getQuantity(), item.getQuantity() > 1).addTo(imageLabel); + + AsyncBufferedImage itemImage = itemManager.getImage(item.getId(), item.getQuantity(), item.getQuantity() > 1); + + if (item.isIgnored()) + { + Runnable addTransparency = () -> + { + BufferedImage transparentImage = ImageUtil.alphaOffset(itemImage, .3f); + imageLabel.setIcon(new ImageIcon(transparentImage)); + }; + itemImage.onChanged(addTransparency); + addTransparency.run(); + } + else + { + itemImage.addTo(imageLabel); + } + slotContainer.add(imageLabel); + + // Create popup menu + final JPopupMenu popupMenu = new JPopupMenu(); + popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5)); + slotContainer.setComponentPopupMenu(popupMenu); + + final JMenuItem toggle = new JMenuItem("Toggle item"); + toggle.addActionListener(e -> + { + item.setIgnored(!item.isIgnored()); + onItemToggle.accept(item.getName(), item.isIgnored()); + }); + + popupMenu.add(toggle); } itemContainer.add(slotContainer); @@ -223,6 +299,7 @@ class LootTrackerBox extends JPanel final String name = item.getName(); final int quantity = item.getQuantity(); final long price = item.getPrice(); - return name + " x " + quantity + " (" + StackFormatter.quantityToStackSize(price) + ")"; + final String ignoredLabel = item.isIgnored() ? " - Ignored" : ""; + return name + " x " + quantity + " (" + StackFormatter.quantityToStackSize(price) + ") " + ignoredLabel; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java new file mode 100644 index 0000000000..5776543b81 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018, Psikoi + * 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.loottracker; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("loottracker") +public interface LootTrackerConfig extends Config +{ + @ConfigItem( + keyName = "ignoredItems", + name = "Ignored items", + description = "Configures which items should be ignored when calculating loot prices." + ) + default String getIgnoredItems() + { + return ""; + } + + @ConfigItem( + keyName = "ignoredItems", + name = "", + description = "" + ) + void setIgnoredItems(String key); +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerItem.java index 9e3a36ad86..feb1504681 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerItem.java @@ -24,13 +24,22 @@ */ package net.runelite.client.plugins.loottracker; -import lombok.Value; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; -@Value +@AllArgsConstructor class LootTrackerItem { + @Getter private final int id; + @Getter private final String name; + @Getter private final int quantity; + @Getter private final long price; + @Getter + @Setter + private boolean ignored; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java index 2df6499d18..8764ad79c5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java @@ -61,6 +61,10 @@ class LootTrackerPanel extends PluginPanel private static final ImageIcon GROUPED_LOOT_VIEW_HOVER; private static final ImageIcon BACK_ARROW_ICON; private static final ImageIcon BACK_ARROW_ICON_HOVER; + private static final ImageIcon VISIBLE_ICON; + private static final ImageIcon VISIBLE_ICON_HOVER; + private static final ImageIcon INVISIBLE_ICON; + private static final ImageIcon INVISIBLE_ICON_HOVER; private static final String HTML_LABEL_TEMPLATE = "%s%s"; @@ -81,6 +85,7 @@ class LootTrackerPanel extends PluginPanel private final JPanel actionsContainer = new JPanel(); private final JLabel detailsTitle = new JLabel(); private final JLabel backBtn = new JLabel(); + private final JLabel viewHiddenBtn = new JLabel(); private final JLabel singleLootBtn = new JLabel(); private final JLabel groupedLootBtn = new JLabel(); @@ -89,7 +94,10 @@ class LootTrackerPanel extends PluginPanel private final List boxes = new ArrayList<>(); private final ItemManager itemManager; + private final LootTrackerPlugin plugin; + private boolean groupLoot; + private boolean hideIgnoredItems; private String currentView; static @@ -97,6 +105,8 @@ class LootTrackerPanel extends PluginPanel final BufferedImage singleLootImg = ImageUtil.getResourceStreamFromClass(LootTrackerPlugin.class, "single_loot_icon.png"); final BufferedImage groupedLootImg = ImageUtil.getResourceStreamFromClass(LootTrackerPlugin.class, "grouped_loot_icon.png"); final BufferedImage backArrowImg = ImageUtil.getResourceStreamFromClass(LootTrackerPlugin.class, "back_icon.png"); + final BufferedImage visibleImg = ImageUtil.getResourceStreamFromClass(LootTrackerPlugin.class, "visible_icon.png"); + final BufferedImage invisibleImg = ImageUtil.getResourceStreamFromClass(LootTrackerPlugin.class, "invisible_icon.png"); SINGLE_LOOT_VIEW = new ImageIcon(singleLootImg); SINGLE_LOOT_VIEW_FADED = new ImageIcon(ImageUtil.alphaOffset(singleLootImg, -180)); @@ -108,11 +118,20 @@ class LootTrackerPanel extends PluginPanel BACK_ARROW_ICON = new ImageIcon(backArrowImg); BACK_ARROW_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(backArrowImg, -180)); + + VISIBLE_ICON = new ImageIcon(visibleImg); + VISIBLE_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(visibleImg, -220)); + + INVISIBLE_ICON = new ImageIcon(invisibleImg); + INVISIBLE_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(invisibleImg, -220)); } - LootTrackerPanel(final ItemManager itemManager) + LootTrackerPanel(final LootTrackerPlugin plugin, final ItemManager itemManager) { this.itemManager = itemManager; + this.plugin = plugin; + this.hideIgnoredItems = true; + setBorder(new EmptyBorder(6, 6, 6, 6)); setBackground(ColorScheme.DARK_GRAY_COLOR); setLayout(new BorderLayout()); @@ -128,7 +147,7 @@ class LootTrackerPanel extends PluginPanel actionsContainer.setBorder(new EmptyBorder(5, 5, 5, 10)); actionsContainer.setVisible(false); - final JPanel viewControls = new JPanel(new GridLayout(1, 2, 10, 0)); + final JPanel viewControls = new JPanel(new GridLayout(1, 3, 10, 0)); viewControls.setBackground(ColorScheme.DARKER_GRAY_COLOR); singleLootBtn.setIcon(SINGLE_LOOT_VIEW); @@ -177,9 +196,34 @@ class LootTrackerPanel extends PluginPanel } }); + viewHiddenBtn.setIcon(VISIBLE_ICON); + viewHiddenBtn.setToolTipText("Show ignored items"); + viewHiddenBtn.addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent mouseEvent) + { + changeItemHiding(!hideIgnoredItems); + } + + @Override + public void mouseExited(MouseEvent mouseEvent) + { + viewHiddenBtn.setIcon(hideIgnoredItems ? INVISIBLE_ICON : VISIBLE_ICON); + } + + @Override + public void mouseEntered(MouseEvent mouseEvent) + { + viewHiddenBtn.setIcon(hideIgnoredItems ? INVISIBLE_ICON_HOVER : VISIBLE_ICON_HOVER); + } + }); + viewControls.add(groupedLootBtn); viewControls.add(singleLootBtn); + viewControls.add(viewHiddenBtn); changeGrouping(true); + changeItemHiding(true); final JPanel leftTitleContainer = new JPanel(new BorderLayout(5, 0)); leftTitleContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR); @@ -286,6 +330,7 @@ class LootTrackerPanel extends PluginPanel /** * Changes grouping mode of panel + * * @param group if loot should be grouped or not */ private void changeGrouping(boolean group) @@ -296,6 +341,38 @@ class LootTrackerPanel extends PluginPanel singleLootBtn.setIcon(group ? SINGLE_LOOT_VIEW_FADED : SINGLE_LOOT_VIEW); } + /** + * Changes item hiding mode of panel + * + * @param hide if ignored items should be hidden or not + */ + private void changeItemHiding(boolean hide) + { + hideIgnoredItems = hide; + rebuild(); + viewHiddenBtn.setIcon(hideIgnoredItems ? VISIBLE_ICON : INVISIBLE_ICON); + } + + /** + * After an item changed it's ignored state, iterate all the records and make + * sure all items of the same name also get updated + */ + void updateIgnoredRecords() + { + for (LootTrackerRecord r : records) + { + for (LootTrackerItem item : r.getItems()) + { + if (plugin.isIgnored(item.getName()) != item.isIgnored()) + { + item.setIgnored(plugin.isIgnored(item.getName())); + } + } + } + + rebuild(); + } + /** * Rebuilds all the boxes from scratch using existing listed records, depending on the grouping mode. */ @@ -342,7 +419,7 @@ class LootTrackerPanel extends PluginPanel overallPanel.setVisible(true); // Create box - final LootTrackerBox box = new LootTrackerBox(itemManager, record.getTitle(), record.getSubTitle()); + final LootTrackerBox box = new LootTrackerBox(itemManager, record.getTitle(), record.getSubTitle(), hideIgnoredItems, plugin::toggleItem); box.combine(record); // Create popup menu @@ -386,7 +463,7 @@ class LootTrackerPanel extends PluginPanel private void updateOverall() { final long overallGp = boxes.stream().mapToLong(LootTrackerBox::getTotalPrice).sum(); - final int overallKills = boxes.stream().mapToInt(LootTrackerBox::getTotalKills).sum(); + final long overallKills = boxes.stream().mapToLong(LootTrackerBox::getTotalKills).sum(); overallKillsLabel.setText(htmlLabel("Total count: ", overallKills)); overallGpLabel.setText(htmlLabel("Total value: ", overallGp)); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java index 1d7a1befcf..4aa68399c1 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java @@ -25,12 +25,17 @@ */ package net.runelite.client.plugins.loottracker; +import com.google.common.base.Joiner; +import com.google.common.base.Splitter; import com.google.common.eventbus.Subscribe; +import com.google.inject.Provides; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -47,8 +52,10 @@ import net.runelite.api.Player; import net.runelite.api.SpriteID; import net.runelite.api.coords.WorldPoint; import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.ConfigChanged; import net.runelite.api.events.WidgetLoaded; import net.runelite.api.widgets.WidgetID; +import net.runelite.client.config.ConfigManager; import net.runelite.client.events.NpcLootReceived; import net.runelite.client.events.PlayerLootReceived; import net.runelite.client.game.ItemManager; @@ -74,6 +81,13 @@ public class LootTrackerPlugin extends Plugin private static final Pattern CLUE_SCROLL_PATTERN = Pattern.compile("You have completed [0-9]+ ([a-z]+) Treasure Trails."); private static final int THEATRE_OF_BLOOD_REGION = 12867; + private static final Splitter COMMA_SPLITTER = Splitter + .on(",") + .omitEmptyStrings() + .trimResults(); + + private static final Joiner COMMA_JOINER = Joiner.on(",").skipNulls(); + @Inject private ClientToolbar clientToolbar; @@ -83,6 +97,9 @@ public class LootTrackerPlugin extends Plugin @Inject private SpriteManager spriteManager; + @Inject + private LootTrackerConfig config; + @Inject private Client client; @@ -90,6 +107,8 @@ public class LootTrackerPlugin extends Plugin private NavigationButton navButton; private String eventType; + private List ignoredItems = new ArrayList<>(); + private static Collection stack(Collection items) { final List list = new ArrayList<>(); @@ -119,10 +138,27 @@ public class LootTrackerPlugin extends Plugin return list; } + @Provides + LootTrackerConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(LootTrackerConfig.class); + } + + @Subscribe + public void onConfigChanged(ConfigChanged event) + { + if (event.getGroup().equals("loottracker")) + { + ignoredItems = COMMA_SPLITTER.splitToList(config.getIgnoredItems()); + panel.updateIgnoredRecords(); + } + } + @Override protected void startUp() throws Exception { - panel = new LootTrackerPanel(itemManager); + ignoredItems = COMMA_SPLITTER.splitToList(config.getIgnoredItems()); + panel = new LootTrackerPanel(this, itemManager); spriteManager.getSpriteAsync(SpriteID.TAB_INVENTORY, 0, panel::loadHeaderIcon); final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "panel_icon.png"); @@ -253,6 +289,28 @@ public class LootTrackerPlugin extends Plugin } } + void toggleItem(String name, boolean ignore) + { + final Set ignoredItemSet = new HashSet<>(ignoredItems); + + if (ignore) + { + ignoredItemSet.add(name); + } + else + { + ignoredItemSet.remove(name); + } + + config.setIgnoredItems(COMMA_JOINER.join(ignoredItemSet)); + panel.updateIgnoredRecords(); + } + + boolean isIgnored(String name) + { + return ignoredItems.contains(name); + } + private LootTrackerItem[] buildEntries(final Collection itemStacks) { return itemStacks.stream().map(itemStack -> @@ -260,12 +318,14 @@ public class LootTrackerPlugin extends Plugin final ItemComposition itemComposition = itemManager.getItemComposition(itemStack.getId()); final int realItemId = itemComposition.getNote() != -1 ? itemComposition.getLinkedNoteId() : itemStack.getId(); final long price = (long) itemManager.getItemPrice(realItemId) * (long) itemStack.getQuantity(); + final boolean ignored = ignoredItems.contains(itemComposition.getName()); return new LootTrackerItem( itemStack.getId(), itemComposition.getName(), itemStack.getQuantity(), - price); + price, + ignored); }).toArray(LootTrackerItem[]::new); } } diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/loottracker/invisible_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/loottracker/invisible_icon.png new file mode 100644 index 0000000000..a52cbfe0a7 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/loottracker/invisible_icon.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/loottracker/visible_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/loottracker/visible_icon.png new file mode 100644 index 0000000000..5c4232c808 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/loottracker/visible_icon.png differ