loot tracker: add view for grouping loot by npc/event
This commit is contained in:
@@ -29,6 +29,10 @@ import com.google.common.base.Strings;
|
|||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.GridLayout;
|
import java.awt.GridLayout;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.SwingConstants;
|
import javax.swing.SwingConstants;
|
||||||
@@ -39,14 +43,26 @@ import net.runelite.client.ui.ColorScheme;
|
|||||||
import net.runelite.client.ui.FontManager;
|
import net.runelite.client.ui.FontManager;
|
||||||
import net.runelite.client.util.StackFormatter;
|
import net.runelite.client.util.StackFormatter;
|
||||||
|
|
||||||
@Getter
|
|
||||||
class LootTrackerBox extends JPanel
|
class LootTrackerBox extends JPanel
|
||||||
{
|
{
|
||||||
private static final int ITEMS_PER_ROW = 5;
|
private static final int ITEMS_PER_ROW = 5;
|
||||||
private final long totalPrice;
|
private final JPanel itemContainer = new JPanel();
|
||||||
|
private final JLabel priceLabel = new JLabel();
|
||||||
|
private final JLabel subTitleLabel = new JLabel();
|
||||||
|
private final ItemManager itemManager;
|
||||||
|
private final String id;
|
||||||
|
|
||||||
LootTrackerBox(final ItemManager itemManager, final String title, final String subTitle, final LootTrackerItemEntry[] items)
|
@Getter
|
||||||
|
private final List<LootTrackerRecord> records = new ArrayList<>();
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private long totalPrice;
|
||||||
|
|
||||||
|
LootTrackerBox(final ItemManager itemManager, final String id, @Nullable final String subtitle)
|
||||||
{
|
{
|
||||||
|
this.id = id;
|
||||||
|
this.itemManager = itemManager;
|
||||||
|
|
||||||
setLayout(new BorderLayout(0, 1));
|
setLayout(new BorderLayout(0, 1));
|
||||||
setBorder(new EmptyBorder(5, 0, 0, 0));
|
setBorder(new EmptyBorder(5, 0, 0, 0));
|
||||||
|
|
||||||
@@ -54,43 +70,140 @@ class LootTrackerBox extends JPanel
|
|||||||
logTitle.setBorder(new EmptyBorder(7, 7, 7, 7));
|
logTitle.setBorder(new EmptyBorder(7, 7, 7, 7));
|
||||||
logTitle.setBackground(ColorScheme.DARKER_GRAY_COLOR.darker());
|
logTitle.setBackground(ColorScheme.DARKER_GRAY_COLOR.darker());
|
||||||
|
|
||||||
final JLabel titleLabel = new JLabel(title);
|
final JLabel titleLabel = new JLabel(id);
|
||||||
titleLabel.setFont(FontManager.getRunescapeSmallFont());
|
titleLabel.setFont(FontManager.getRunescapeSmallFont());
|
||||||
titleLabel.setForeground(Color.WHITE);
|
titleLabel.setForeground(Color.WHITE);
|
||||||
|
|
||||||
logTitle.add(titleLabel, BorderLayout.WEST);
|
logTitle.add(titleLabel, BorderLayout.WEST);
|
||||||
|
|
||||||
// If we have subtitle, add it
|
// If we have subtitle, add it
|
||||||
if (!Strings.isNullOrEmpty(subTitle))
|
if (!Strings.isNullOrEmpty(subtitle))
|
||||||
{
|
{
|
||||||
final JLabel subTitleLabel = new JLabel(subTitle);
|
subTitleLabel.setText(subtitle);
|
||||||
subTitleLabel.setFont(FontManager.getRunescapeSmallFont());
|
subTitleLabel.setFont(FontManager.getRunescapeSmallFont());
|
||||||
subTitleLabel.setForeground(ColorScheme.LIGHT_GRAY_COLOR);
|
subTitleLabel.setForeground(ColorScheme.LIGHT_GRAY_COLOR);
|
||||||
logTitle.add(subTitleLabel, BorderLayout.CENTER);
|
logTitle.add(subTitleLabel, BorderLayout.CENTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
totalPrice = calculatePrice(items);
|
priceLabel.setFont(FontManager.getRunescapeSmallFont());
|
||||||
|
priceLabel.setForeground(ColorScheme.LIGHT_GRAY_COLOR);
|
||||||
|
logTitle.add(priceLabel, BorderLayout.EAST);
|
||||||
|
|
||||||
if (totalPrice > 0)
|
add(logTitle, BorderLayout.NORTH);
|
||||||
|
add(itemContainer, BorderLayout.CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getTotalKills()
|
||||||
|
{
|
||||||
|
return records.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this box matches specified record
|
||||||
|
* @param record loot record
|
||||||
|
* @return true if match is made
|
||||||
|
*/
|
||||||
|
boolean matches(final LootTrackerRecord record)
|
||||||
|
{
|
||||||
|
return record.getTitle().equals(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this box matches specified id
|
||||||
|
* @param id other record id
|
||||||
|
* @return true if match is made
|
||||||
|
*/
|
||||||
|
boolean matches(final String id)
|
||||||
|
{
|
||||||
|
if (id == null)
|
||||||
{
|
{
|
||||||
final JLabel priceLabel = new JLabel(StackFormatter.quantityToStackSize(totalPrice) + " gp");
|
return true;
|
||||||
priceLabel.setFont(FontManager.getRunescapeSmallFont());
|
|
||||||
priceLabel.setForeground(ColorScheme.LIGHT_GRAY_COLOR);
|
|
||||||
logTitle.add(priceLabel, BorderLayout.EAST);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.id.equals(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an record's data into a loot box.
|
||||||
|
* This will add new items to the list, re-calculating price and kill count.
|
||||||
|
*/
|
||||||
|
void combine(final LootTrackerRecord record)
|
||||||
|
{
|
||||||
|
if (!matches(record))
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException(record.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
records.add(record);
|
||||||
|
buildItems();
|
||||||
|
|
||||||
|
priceLabel.setText(StackFormatter.quantityToStackSize(totalPrice) + " gp");
|
||||||
|
if (records.size() > 1)
|
||||||
|
{
|
||||||
|
subTitleLabel.setText("x " + records.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method creates stacked items from the item list, calculates total price and then
|
||||||
|
* displays all the items in the UI.
|
||||||
|
*/
|
||||||
|
private void buildItems()
|
||||||
|
{
|
||||||
|
final List<LootTrackerItem> allItems = new ArrayList<>();
|
||||||
|
final List<LootTrackerItem> items = new ArrayList<>();
|
||||||
|
totalPrice = 0;
|
||||||
|
|
||||||
|
for (LootTrackerRecord records : records)
|
||||||
|
{
|
||||||
|
allItems.addAll(Arrays.asList(records.getItems()));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final LootTrackerItem entry : allItems)
|
||||||
|
{
|
||||||
|
totalPrice += entry.getPrice();
|
||||||
|
|
||||||
|
int quantity = 0;
|
||||||
|
for (final LootTrackerItem i : items)
|
||||||
|
{
|
||||||
|
if (i.getId() == entry.getId())
|
||||||
|
{
|
||||||
|
quantity = i.getQuantity();
|
||||||
|
items.remove(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (quantity > 0)
|
||||||
|
{
|
||||||
|
int newQuantity = entry.getQuantity() + quantity;
|
||||||
|
long pricePerItem = entry.getPrice() == 0 ? 0 : (entry.getPrice() / entry.getQuantity());
|
||||||
|
|
||||||
|
items.add(new LootTrackerItem(entry.getId(), entry.getName(), newQuantity, pricePerItem * newQuantity));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
items.add(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
items.sort((i1, i2) -> Long.compare(i2.getPrice(), i1.getPrice()));
|
||||||
|
|
||||||
// Calculates how many rows need to be display to fit all items
|
// Calculates how many rows need to be display to fit all items
|
||||||
final int rowSize = ((items.length % ITEMS_PER_ROW == 0) ? 0 : 1) + items.length / ITEMS_PER_ROW;
|
final int rowSize = ((items.size() % ITEMS_PER_ROW == 0) ? 0 : 1) + items.size() / ITEMS_PER_ROW;
|
||||||
final JPanel itemContainer = new JPanel(new GridLayout(rowSize, ITEMS_PER_ROW, 1, 1));
|
|
||||||
|
itemContainer.removeAll();
|
||||||
|
itemContainer.setLayout(new GridLayout(rowSize, ITEMS_PER_ROW, 1, 1));
|
||||||
|
|
||||||
for (int i = 0; i < rowSize * ITEMS_PER_ROW; i++)
|
for (int i = 0; i < rowSize * ITEMS_PER_ROW; i++)
|
||||||
{
|
{
|
||||||
final JPanel slotContainer = new JPanel();
|
final JPanel slotContainer = new JPanel();
|
||||||
slotContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
slotContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||||
|
|
||||||
if (i < items.length)
|
if (i < items.size())
|
||||||
{
|
{
|
||||||
final LootTrackerItemEntry item = items[i];
|
final LootTrackerItem item = items.get(i);
|
||||||
final JLabel imageLabel = new JLabel();
|
final JLabel imageLabel = new JLabel();
|
||||||
imageLabel.setToolTipText(buildToolTip(item));
|
imageLabel.setToolTipText(buildToolTip(item));
|
||||||
imageLabel.setVerticalAlignment(SwingConstants.CENTER);
|
imageLabel.setVerticalAlignment(SwingConstants.CENTER);
|
||||||
@@ -102,26 +215,14 @@ class LootTrackerBox extends JPanel
|
|||||||
itemContainer.add(slotContainer);
|
itemContainer.add(slotContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
add(logTitle, BorderLayout.NORTH);
|
itemContainer.repaint();
|
||||||
add(itemContainer, BorderLayout.CENTER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String buildToolTip(LootTrackerItemEntry item)
|
private static String buildToolTip(LootTrackerItem item)
|
||||||
{
|
{
|
||||||
final String name = item.getName();
|
final String name = item.getName();
|
||||||
final int quantity = item.getQuantity();
|
final int quantity = item.getQuantity();
|
||||||
final long price = item.getPrice();
|
final long price = item.getPrice();
|
||||||
|
|
||||||
return name + " x " + quantity + " (" + StackFormatter.quantityToStackSize(price) + ")";
|
return name + " x " + quantity + " (" + StackFormatter.quantityToStackSize(price) + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long calculatePrice(final LootTrackerItemEntry[] itemStacks)
|
|
||||||
{
|
|
||||||
long total = 0;
|
|
||||||
for (LootTrackerItemEntry itemStack : itemStacks)
|
|
||||||
{
|
|
||||||
total += itemStack.getPrice();
|
|
||||||
}
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ package net.runelite.client.plugins.loottracker;
|
|||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
|
|
||||||
@Value
|
@Value
|
||||||
class LootTrackerItemEntry
|
class LootTrackerItem
|
||||||
{
|
{
|
||||||
private final int id;
|
private final int id;
|
||||||
private final String name;
|
private final String name;
|
||||||
@@ -26,8 +26,15 @@
|
|||||||
package net.runelite.client.plugins.loottracker;
|
package net.runelite.client.plugins.loottracker;
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
import java.awt.GridLayout;
|
import java.awt.GridLayout;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.swing.Box;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
@@ -41,17 +48,27 @@ import net.runelite.client.ui.FontManager;
|
|||||||
import net.runelite.client.ui.PluginPanel;
|
import net.runelite.client.ui.PluginPanel;
|
||||||
import net.runelite.client.ui.components.PluginErrorPanel;
|
import net.runelite.client.ui.components.PluginErrorPanel;
|
||||||
import net.runelite.client.util.ColorUtil;
|
import net.runelite.client.util.ColorUtil;
|
||||||
|
import net.runelite.client.util.ImageUtil;
|
||||||
import net.runelite.client.util.StackFormatter;
|
import net.runelite.client.util.StackFormatter;
|
||||||
|
|
||||||
class LootTrackerPanel extends PluginPanel
|
class LootTrackerPanel extends PluginPanel
|
||||||
{
|
{
|
||||||
|
private static final ImageIcon SINGLE_LOOT_VIEW;
|
||||||
|
private static final ImageIcon SINGLE_LOOT_VIEW_FADED;
|
||||||
|
private static final ImageIcon SINGLE_LOOT_VIEW_HOVER;
|
||||||
|
private static final ImageIcon GROUPED_LOOT_VIEW;
|
||||||
|
private static final ImageIcon GROUPED_LOOT_VIEW_FADED;
|
||||||
|
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 String HTML_LABEL_TEMPLATE =
|
private static final String HTML_LABEL_TEMPLATE =
|
||||||
"<html><body style='color:%s'>%s<span style='color:white'>%s</span></body></html>";
|
"<html><body style='color:%s'>%s<span style='color:white'>%s</span></body></html>";
|
||||||
|
|
||||||
// When there is no loot, display this
|
// When there is no loot, display this
|
||||||
private final PluginErrorPanel errorPanel = new PluginErrorPanel();
|
private final PluginErrorPanel errorPanel = new PluginErrorPanel();
|
||||||
|
|
||||||
// Handle loot logs
|
// Handle loot boxes
|
||||||
private final JPanel logsContainer = new JPanel();
|
private final JPanel logsContainer = new JPanel();
|
||||||
|
|
||||||
// Handle overall session data
|
// Handle overall session data
|
||||||
@@ -59,9 +76,39 @@ class LootTrackerPanel extends PluginPanel
|
|||||||
private final JLabel overallKillsLabel = new JLabel();
|
private final JLabel overallKillsLabel = new JLabel();
|
||||||
private final JLabel overallGpLabel = new JLabel();
|
private final JLabel overallGpLabel = new JLabel();
|
||||||
private final JLabel overallIcon = new JLabel();
|
private final JLabel overallIcon = new JLabel();
|
||||||
|
|
||||||
|
// Details and navigation
|
||||||
|
private final JPanel actionsContainer = new JPanel();
|
||||||
|
private final JLabel detailsTitle = new JLabel();
|
||||||
|
private final JLabel backBtn = new JLabel();
|
||||||
|
private final JLabel singleLootBtn = new JLabel();
|
||||||
|
private final JLabel groupedLootBtn = new JLabel();
|
||||||
|
|
||||||
|
// Log collection
|
||||||
|
private final List<LootTrackerRecord> records = new ArrayList<>();
|
||||||
|
private final List<LootTrackerBox> boxes = new ArrayList<>();
|
||||||
|
|
||||||
private final ItemManager itemManager;
|
private final ItemManager itemManager;
|
||||||
private int overallKills;
|
private boolean groupLoot;
|
||||||
private int overallGp;
|
private String currentView;
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
|
||||||
|
SINGLE_LOOT_VIEW = new ImageIcon(singleLootImg);
|
||||||
|
SINGLE_LOOT_VIEW_FADED = new ImageIcon(ImageUtil.alphaOffset(singleLootImg, -180));
|
||||||
|
SINGLE_LOOT_VIEW_HOVER = new ImageIcon(ImageUtil.alphaOffset(singleLootImg, -220));
|
||||||
|
|
||||||
|
GROUPED_LOOT_VIEW = new ImageIcon(groupedLootImg);
|
||||||
|
GROUPED_LOOT_VIEW_FADED = new ImageIcon(ImageUtil.alphaOffset(groupedLootImg, -180));
|
||||||
|
GROUPED_LOOT_VIEW_HOVER = new ImageIcon(ImageUtil.alphaOffset(groupedLootImg, -220));
|
||||||
|
|
||||||
|
BACK_ARROW_ICON = new ImageIcon(backArrowImg);
|
||||||
|
BACK_ARROW_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(backArrowImg, -180));
|
||||||
|
}
|
||||||
|
|
||||||
LootTrackerPanel(final ItemManager itemManager)
|
LootTrackerPanel(final ItemManager itemManager)
|
||||||
{
|
{
|
||||||
@@ -75,8 +122,104 @@ class LootTrackerPanel extends PluginPanel
|
|||||||
layoutPanel.setLayout(new BoxLayout(layoutPanel, BoxLayout.Y_AXIS));
|
layoutPanel.setLayout(new BoxLayout(layoutPanel, BoxLayout.Y_AXIS));
|
||||||
add(layoutPanel, BorderLayout.NORTH);
|
add(layoutPanel, BorderLayout.NORTH);
|
||||||
|
|
||||||
|
actionsContainer.setLayout(new BorderLayout());
|
||||||
|
actionsContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||||
|
actionsContainer.setPreferredSize(new Dimension(0, 30));
|
||||||
|
actionsContainer.setBorder(new EmptyBorder(5, 5, 5, 10));
|
||||||
|
actionsContainer.setVisible(false);
|
||||||
|
|
||||||
|
final JPanel viewControls = new JPanel(new GridLayout(1, 2, 10, 0));
|
||||||
|
viewControls.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||||
|
|
||||||
|
singleLootBtn.setIcon(SINGLE_LOOT_VIEW);
|
||||||
|
singleLootBtn.setToolTipText("Show each kill separately");
|
||||||
|
singleLootBtn.addMouseListener(new MouseAdapter()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void mousePressed(MouseEvent mouseEvent)
|
||||||
|
{
|
||||||
|
changeGrouping(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseExited(MouseEvent mouseEvent)
|
||||||
|
{
|
||||||
|
singleLootBtn.setIcon(groupLoot ? SINGLE_LOOT_VIEW_FADED : SINGLE_LOOT_VIEW);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseEntered(MouseEvent mouseEvent)
|
||||||
|
{
|
||||||
|
singleLootBtn.setIcon(groupLoot ? SINGLE_LOOT_VIEW_HOVER : SINGLE_LOOT_VIEW);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
groupedLootBtn.setIcon(GROUPED_LOOT_VIEW);
|
||||||
|
groupedLootBtn.setToolTipText("Group loot by source");
|
||||||
|
groupedLootBtn.addMouseListener(new MouseAdapter()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void mousePressed(MouseEvent mouseEvent)
|
||||||
|
{
|
||||||
|
changeGrouping(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseExited(MouseEvent mouseEvent)
|
||||||
|
{
|
||||||
|
groupedLootBtn.setIcon(groupLoot ? GROUPED_LOOT_VIEW : GROUPED_LOOT_VIEW_FADED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseEntered(MouseEvent mouseEvent)
|
||||||
|
{
|
||||||
|
groupedLootBtn.setIcon(groupLoot ? GROUPED_LOOT_VIEW : GROUPED_LOOT_VIEW_HOVER);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
viewControls.add(groupedLootBtn);
|
||||||
|
viewControls.add(singleLootBtn);
|
||||||
|
changeGrouping(true);
|
||||||
|
|
||||||
|
final JPanel leftTitleContainer = new JPanel(new BorderLayout(5, 0));
|
||||||
|
leftTitleContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||||
|
|
||||||
|
detailsTitle.setForeground(Color.WHITE);
|
||||||
|
|
||||||
|
backBtn.setIcon(BACK_ARROW_ICON);
|
||||||
|
backBtn.setVisible(false);
|
||||||
|
backBtn.addMouseListener(new MouseAdapter()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void mousePressed(MouseEvent mouseEvent)
|
||||||
|
{
|
||||||
|
currentView = null;
|
||||||
|
backBtn.setVisible(false);
|
||||||
|
detailsTitle.setText("");
|
||||||
|
rebuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseExited(MouseEvent mouseEvent)
|
||||||
|
{
|
||||||
|
backBtn.setIcon(BACK_ARROW_ICON);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseEntered(MouseEvent mouseEvent)
|
||||||
|
{
|
||||||
|
backBtn.setIcon(BACK_ARROW_ICON_HOVER);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
leftTitleContainer.add(backBtn, BorderLayout.WEST);
|
||||||
|
leftTitleContainer.add(detailsTitle, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
actionsContainer.add(leftTitleContainer, BorderLayout.WEST);
|
||||||
|
actionsContainer.add(viewControls, BorderLayout.EAST);
|
||||||
|
|
||||||
// Create panel that will contain overall data
|
// Create panel that will contain overall data
|
||||||
overallPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
overallPanel.setBorder(new EmptyBorder(8, 10, 8, 10));
|
||||||
overallPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
overallPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||||
overallPanel.setLayout(new BorderLayout());
|
overallPanel.setLayout(new BorderLayout());
|
||||||
overallPanel.setVisible(false);
|
overallPanel.setVisible(false);
|
||||||
@@ -85,7 +228,7 @@ class LootTrackerPanel extends PluginPanel
|
|||||||
final JPanel overallInfo = new JPanel();
|
final JPanel overallInfo = new JPanel();
|
||||||
overallInfo.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
overallInfo.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||||
overallInfo.setLayout(new GridLayout(2, 1));
|
overallInfo.setLayout(new GridLayout(2, 1));
|
||||||
overallInfo.setBorder(new EmptyBorder(0, 10, 0, 0));
|
overallInfo.setBorder(new EmptyBorder(2, 10, 2, 0));
|
||||||
overallKillsLabel.setFont(FontManager.getRunescapeSmallFont());
|
overallKillsLabel.setFont(FontManager.getRunescapeSmallFont());
|
||||||
overallGpLabel.setFont(FontManager.getRunescapeSmallFont());
|
overallGpLabel.setFont(FontManager.getRunescapeSmallFont());
|
||||||
overallInfo.add(overallKillsLabel);
|
overallInfo.add(overallKillsLabel);
|
||||||
@@ -97,8 +240,9 @@ class LootTrackerPanel extends PluginPanel
|
|||||||
final JMenuItem reset = new JMenuItem("Reset All");
|
final JMenuItem reset = new JMenuItem("Reset All");
|
||||||
reset.addActionListener(e ->
|
reset.addActionListener(e ->
|
||||||
{
|
{
|
||||||
overallKills = 0;
|
// If not in detailed view, remove all, otherwise only remove for the currently detailed title
|
||||||
overallGp = 0;
|
records.removeIf(r -> r.matches(currentView));
|
||||||
|
boxes.removeIf(b -> b.matches(currentView));
|
||||||
updateOverall();
|
updateOverall();
|
||||||
logsContainer.removeAll();
|
logsContainer.removeAll();
|
||||||
logsContainer.repaint();
|
logsContainer.repaint();
|
||||||
@@ -110,8 +254,10 @@ class LootTrackerPanel extends PluginPanel
|
|||||||
popupMenu.add(reset);
|
popupMenu.add(reset);
|
||||||
overallPanel.setComponentPopupMenu(popupMenu);
|
overallPanel.setComponentPopupMenu(popupMenu);
|
||||||
|
|
||||||
// Create loot logs wrapper
|
// Create loot boxes wrapper
|
||||||
logsContainer.setLayout(new BoxLayout(logsContainer, BoxLayout.Y_AXIS));
|
logsContainer.setLayout(new BoxLayout(logsContainer, BoxLayout.Y_AXIS));
|
||||||
|
layoutPanel.add(actionsContainer);
|
||||||
|
layoutPanel.add(Box.createRigidArea(new Dimension(0, 5)));
|
||||||
layoutPanel.add(overallPanel);
|
layoutPanel.add(overallPanel);
|
||||||
layoutPanel.add(logsContainer);
|
layoutPanel.add(logsContainer);
|
||||||
|
|
||||||
@@ -125,50 +271,129 @@ class LootTrackerPanel extends PluginPanel
|
|||||||
overallIcon.setIcon(new ImageIcon(img));
|
overallIcon.setIcon(new ImageIcon(img));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String htmlLabel(String key, long value)
|
/**
|
||||||
|
* Adds a new entry to the plugin.
|
||||||
|
* Creates a subtitle, adds a new entry and then passes off to the render methods, that will decide
|
||||||
|
* how to display this new data.
|
||||||
|
*/
|
||||||
|
void add(final String eventName, final int actorLevel, LootTrackerItem[] items)
|
||||||
{
|
{
|
||||||
final String valueStr = StackFormatter.quantityToStackSize(value);
|
final String subTitle = actorLevel > -1 ? "(lvl-" + actorLevel + ")" : "";
|
||||||
return String.format(HTML_LABEL_TEMPLATE, ColorUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR), key, valueStr);
|
final LootTrackerRecord record = new LootTrackerRecord(eventName, subTitle, items, System.currentTimeMillis());
|
||||||
|
records.add(record);
|
||||||
|
buildBox(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addLog(final String eventName, final int actorLevel, LootTrackerItemEntry[] items)
|
/**
|
||||||
|
* Changes grouping mode of panel
|
||||||
|
* @param group if loot should be grouped or not
|
||||||
|
*/
|
||||||
|
private void changeGrouping(boolean group)
|
||||||
{
|
{
|
||||||
// Remove error and show overall
|
groupLoot = group;
|
||||||
|
rebuild();
|
||||||
|
groupedLootBtn.setIcon(group ? GROUPED_LOOT_VIEW : GROUPED_LOOT_VIEW_FADED);
|
||||||
|
singleLootBtn.setIcon(group ? SINGLE_LOOT_VIEW_FADED : SINGLE_LOOT_VIEW);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rebuilds all the boxes from scratch using existing listed records, depending on the grouping mode.
|
||||||
|
*/
|
||||||
|
private void rebuild()
|
||||||
|
{
|
||||||
|
logsContainer.removeAll();
|
||||||
|
boxes.clear();
|
||||||
|
records.forEach(this::buildBox);
|
||||||
|
updateOverall();
|
||||||
|
logsContainer.revalidate();
|
||||||
|
logsContainer.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method decides what to do with a new record, if a similar log exists, it will
|
||||||
|
* add its items to it, updating the log's overall price and kills. If not, a new log will be created
|
||||||
|
* to hold this entry's information.
|
||||||
|
*/
|
||||||
|
private void buildBox(LootTrackerRecord record)
|
||||||
|
{
|
||||||
|
// If this record is not part of current view, return
|
||||||
|
if (!record.matches(currentView))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group all similar loot together
|
||||||
|
if (groupLoot)
|
||||||
|
{
|
||||||
|
for (LootTrackerBox box : boxes)
|
||||||
|
{
|
||||||
|
if (box.matches(record))
|
||||||
|
{
|
||||||
|
box.combine(record);
|
||||||
|
updateOverall();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show main view
|
||||||
remove(errorPanel);
|
remove(errorPanel);
|
||||||
|
actionsContainer.setVisible(true);
|
||||||
overallPanel.setVisible(true);
|
overallPanel.setVisible(true);
|
||||||
|
|
||||||
// Create box
|
// Create box
|
||||||
final String subTitle = actorLevel > -1 ? "(lvl-" + actorLevel + ")" : "";
|
final LootTrackerBox box = new LootTrackerBox(itemManager, record.getTitle(), record.getSubTitle());
|
||||||
final LootTrackerBox box = new LootTrackerBox(itemManager, eventName, subTitle, items);
|
box.combine(record);
|
||||||
logsContainer.add(box, 0);
|
|
||||||
logsContainer.repaint();
|
|
||||||
|
|
||||||
// Update overall
|
// Create popup menu
|
||||||
overallGp += box.getTotalPrice();
|
final JPopupMenu popupMenu = new JPopupMenu();
|
||||||
overallKills += 1;
|
popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||||||
updateOverall();
|
box.setComponentPopupMenu(popupMenu);
|
||||||
|
|
||||||
// Create reset menu
|
// Create reset menu
|
||||||
final JMenuItem reset = new JMenuItem("Reset");
|
final JMenuItem reset = new JMenuItem("Reset");
|
||||||
reset.addActionListener(e ->
|
reset.addActionListener(e ->
|
||||||
{
|
{
|
||||||
overallGp -= box.getTotalPrice();
|
records.removeAll(box.getRecords());
|
||||||
overallKills -= 1;
|
boxes.remove(box);
|
||||||
updateOverall();
|
updateOverall();
|
||||||
logsContainer.remove(box);
|
logsContainer.remove(box);
|
||||||
logsContainer.repaint();
|
logsContainer.repaint();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create popup menu
|
|
||||||
final JPopupMenu popupMenu = new JPopupMenu();
|
|
||||||
popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5));
|
|
||||||
popupMenu.add(reset);
|
popupMenu.add(reset);
|
||||||
box.setComponentPopupMenu(popupMenu);
|
|
||||||
|
// Create details menu
|
||||||
|
final JMenuItem details = new JMenuItem("View details");
|
||||||
|
details.addActionListener(e ->
|
||||||
|
{
|
||||||
|
currentView = record.getTitle();
|
||||||
|
detailsTitle.setText(currentView);
|
||||||
|
backBtn.setVisible(true);
|
||||||
|
rebuild();
|
||||||
|
});
|
||||||
|
|
||||||
|
popupMenu.add(details);
|
||||||
|
|
||||||
|
// Add box to panel
|
||||||
|
boxes.add(box);
|
||||||
|
logsContainer.add(box, 0);
|
||||||
|
|
||||||
|
// Update overall
|
||||||
|
updateOverall();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateOverall()
|
private void updateOverall()
|
||||||
{
|
{
|
||||||
|
final long overallGp = boxes.stream().mapToLong(LootTrackerBox::getTotalPrice).sum();
|
||||||
|
final int overallKills = boxes.stream().mapToInt(LootTrackerBox::getTotalKills).sum();
|
||||||
overallKillsLabel.setText(htmlLabel("Total count: ", overallKills));
|
overallKillsLabel.setText(htmlLabel("Total count: ", overallKills));
|
||||||
overallGpLabel.setText(htmlLabel("Total value: ", overallGp));
|
overallGpLabel.setText(htmlLabel("Total value: ", overallGp));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String htmlLabel(String key, long value)
|
||||||
|
{
|
||||||
|
final String valueStr = StackFormatter.quantityToStackSize(value);
|
||||||
|
return String.format(HTML_LABEL_TEMPLATE, ColorUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR), key, valueStr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,8 +150,8 @@ public class LootTrackerPlugin extends Plugin
|
|||||||
final Collection<ItemStack> items = npcLootReceived.getItems();
|
final Collection<ItemStack> items = npcLootReceived.getItems();
|
||||||
final String name = npc.getName();
|
final String name = npc.getName();
|
||||||
final int combat = npc.getCombatLevel();
|
final int combat = npc.getCombatLevel();
|
||||||
final LootTrackerItemEntry[] entries = buildEntries(stack(items));
|
final LootTrackerItem[] entries = buildEntries(stack(items));
|
||||||
SwingUtilities.invokeLater(() -> panel.addLog(name, combat, entries));
|
SwingUtilities.invokeLater(() -> panel.add(name, combat, entries));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
@@ -161,8 +161,8 @@ public class LootTrackerPlugin extends Plugin
|
|||||||
final Collection<ItemStack> items = playerLootReceived.getItems();
|
final Collection<ItemStack> items = playerLootReceived.getItems();
|
||||||
final String name = player.getName();
|
final String name = player.getName();
|
||||||
final int combat = player.getCombatLevel();
|
final int combat = player.getCombatLevel();
|
||||||
final LootTrackerItemEntry[] entries = buildEntries(stack(items));
|
final LootTrackerItem[] entries = buildEntries(stack(items));
|
||||||
SwingUtilities.invokeLater(() -> panel.addLog(name, combat, entries));
|
SwingUtilities.invokeLater(() -> panel.add(name, combat, entries));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
@@ -209,8 +209,8 @@ public class LootTrackerPlugin extends Plugin
|
|||||||
|
|
||||||
if (!items.isEmpty())
|
if (!items.isEmpty())
|
||||||
{
|
{
|
||||||
final LootTrackerItemEntry[] entries = buildEntries(stack(items));
|
final LootTrackerItem[] entries = buildEntries(stack(items));
|
||||||
SwingUtilities.invokeLater(() -> panel.addLog(eventType, -1, entries));
|
SwingUtilities.invokeLater(() -> panel.add(eventType, -1, entries));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -252,19 +252,19 @@ public class LootTrackerPlugin extends Plugin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private LootTrackerItemEntry[] buildEntries(final Collection<ItemStack> itemStacks)
|
private LootTrackerItem[] buildEntries(final Collection<ItemStack> itemStacks)
|
||||||
{
|
{
|
||||||
return itemStacks.stream().map(itemStack ->
|
return itemStacks.stream().map(itemStack ->
|
||||||
{
|
{
|
||||||
final ItemComposition itemComposition = itemManager.getItemComposition(itemStack.getId());
|
final ItemComposition itemComposition = itemManager.getItemComposition(itemStack.getId());
|
||||||
final int realItemId = itemComposition.getNote() != -1 ? itemComposition.getLinkedNoteId() : itemStack.getId();
|
final int realItemId = itemComposition.getNote() != -1 ? itemComposition.getLinkedNoteId() : itemStack.getId();
|
||||||
final long price = (long)itemManager.getItemPrice(realItemId) * (long)itemStack.getQuantity();
|
final long price = (long) itemManager.getItemPrice(realItemId) * (long) itemStack.getQuantity();
|
||||||
|
|
||||||
return new LootTrackerItemEntry(
|
return new LootTrackerItem(
|
||||||
itemStack.getId(),
|
itemStack.getId(),
|
||||||
itemComposition.getName(),
|
itemComposition.getName(),
|
||||||
itemStack.getQuantity(),
|
itemStack.getQuantity(),
|
||||||
price);
|
price);
|
||||||
}).toArray(LootTrackerItemEntry[]::new);
|
}).toArray(LootTrackerItem[]::new);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 lombok.Value;
|
||||||
|
|
||||||
|
@Value
|
||||||
|
class LootTrackerRecord
|
||||||
|
{
|
||||||
|
private final String title;
|
||||||
|
private final String subTitle;
|
||||||
|
private final LootTrackerItem[] items;
|
||||||
|
private final long timestamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this record matches specified id
|
||||||
|
* @param id other record id
|
||||||
|
* @return true if match is made
|
||||||
|
*/
|
||||||
|
boolean matches(final String id)
|
||||||
|
{
|
||||||
|
if (id == null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return title.equals(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
Reference in New Issue
Block a user