Loot tracker - Add ability to ignore items (#5483)

This adds the ability to toggle items in the Loot Tracker, when an item is toggled, its price won't count towards the price check, and if the "Show ignored items" config option is not enabled, it won't be displayed on the loot tracker UI either.

Fixes #5060
Fixes #4661
This commit is contained in:
Ruben Amendoeira
2018-10-19 09:02:16 +01:00
committed by Tomas Slusny
parent ef9013faa8
commit 31d8e88099
7 changed files with 290 additions and 16 deletions

View File

@@ -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<String, Boolean> onItemToggle;
LootTrackerBox(
final ItemManager itemManager,
final String id,
@Nullable final String subtitle,
final boolean hideIgnoredItems,
final BiConsumer<String, Boolean> 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<LootTrackerItem> 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;
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2018, Psikoi <https://github.com/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);
}

View File

@@ -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;
}

View File

@@ -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 =
"<html><body style='color:%s'>%s<span style='color:white'>%s</span></body></html>";
@@ -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<LootTrackerBox> 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));
}

View File

@@ -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<String> ignoredItems = new ArrayList<>();
private static Collection<ItemStack> stack(Collection<ItemStack> items)
{
final List<ItemStack> 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<String> 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<ItemStack> 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);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B