loot tracker: store aggregated drops
This modifies both the service and the client plugin to no longer store loot per-kill, with the exception of the current sessions kill log. The amount of loot data returned now from the service defaults to the last 1024 unique events
This commit is contained in:
@@ -33,9 +33,11 @@ import java.awt.Dimension;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.ToLongFunction;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.BoxLayout;
|
||||
@@ -48,10 +50,10 @@ import javax.swing.SwingConstants;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import net.runelite.client.util.AsyncBufferedImage;
|
||||
import net.runelite.client.game.ItemManager;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
import net.runelite.client.ui.FontManager;
|
||||
import net.runelite.client.util.AsyncBufferedImage;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
import net.runelite.client.util.QuantityFormatter;
|
||||
import net.runelite.client.util.Text;
|
||||
@@ -65,15 +67,15 @@ class LootTrackerBox extends JPanel
|
||||
private final JLabel priceLabel = new JLabel();
|
||||
private final JLabel subTitleLabel = new JLabel();
|
||||
private final JPanel logTitle = new JPanel();
|
||||
private final JLabel titleLabel = new JLabel();
|
||||
private final ItemManager itemManager;
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private final String id;
|
||||
private final LootTrackerPriceType priceType;
|
||||
private final boolean showPriceType;
|
||||
|
||||
private int kills;
|
||||
@Getter
|
||||
private final List<LootTrackerRecord> records = new ArrayList<>();
|
||||
private final List<LootTrackerItem> items = new ArrayList<>();
|
||||
|
||||
private long totalPrice;
|
||||
private boolean hideIgnoredItems;
|
||||
@@ -102,6 +104,7 @@ class LootTrackerBox extends JPanel
|
||||
logTitle.setBorder(new EmptyBorder(7, 7, 7, 7));
|
||||
logTitle.setBackground(ColorScheme.DARKER_GRAY_COLOR.darker());
|
||||
|
||||
JLabel titleLabel = new JLabel();
|
||||
titleLabel.setText(Text.removeTags(id));
|
||||
titleLabel.setFont(FontManager.getRunescapeSmallFont());
|
||||
titleLabel.setForeground(Color.WHITE);
|
||||
@@ -131,15 +134,13 @@ class LootTrackerBox extends JPanel
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns total amount of kills, removing ignored kills when necessary
|
||||
* Returns total amount of kills
|
||||
*
|
||||
* @return total amount of kills
|
||||
*/
|
||||
private long getTotalKills()
|
||||
private int getTotalKills()
|
||||
{
|
||||
return hideIgnoredItems
|
||||
? records.stream().filter(r -> !Arrays.stream(r.getItems()).allMatch(LootTrackerItem::isIgnored)).count()
|
||||
: records.size();
|
||||
return kills;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,16 +172,32 @@ class LootTrackerBox extends JPanel
|
||||
|
||||
/**
|
||||
* 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)
|
||||
void addKill(final LootTrackerRecord record)
|
||||
{
|
||||
if (!matches(record))
|
||||
{
|
||||
throw new IllegalArgumentException(record.toString());
|
||||
}
|
||||
|
||||
records.add(record);
|
||||
kills += record.getKills();
|
||||
|
||||
outer:
|
||||
for (LootTrackerItem item : record.getItems())
|
||||
{
|
||||
// Combine it into an existing item if one already exists
|
||||
for (int idx = 0; idx < items.size(); ++idx)
|
||||
{
|
||||
LootTrackerItem i = items.get(idx);
|
||||
if (item.getId() == i.getId())
|
||||
{
|
||||
items.set(idx, new LootTrackerItem(i.getId(), i.getName(), i.getQuantity() + item.getQuantity(), i.getGePrice(), i.getHaPrice(), i.isIgnored()));
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
|
||||
items.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
void rebuild()
|
||||
@@ -245,69 +262,31 @@ class LootTrackerBox extends JPanel
|
||||
*/
|
||||
private void buildItems()
|
||||
{
|
||||
final List<LootTrackerItem> allItems = new ArrayList<>();
|
||||
final List<LootTrackerItem> items = new ArrayList<>();
|
||||
totalPrice = 0;
|
||||
|
||||
for (LootTrackerRecord record : records)
|
||||
{
|
||||
allItems.addAll(Arrays.asList(record.getItems()));
|
||||
}
|
||||
|
||||
List<LootTrackerItem> items = this.items;
|
||||
if (hideIgnoredItems)
|
||||
{
|
||||
/* If all the items in this box are ignored */
|
||||
boolean hideBox = allItems.stream().allMatch(LootTrackerItem::isIgnored);
|
||||
setVisible(!hideBox);
|
||||
|
||||
if (hideBox)
|
||||
{
|
||||
return;
|
||||
}
|
||||
items = items.stream().filter(item -> !item.isIgnored()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
for (final LootTrackerItem entry : allItems)
|
||||
boolean isHidden = items.isEmpty();
|
||||
setVisible(!isHidden);
|
||||
|
||||
if (isHidden)
|
||||
{
|
||||
if (entry.isIgnored() && hideIgnoredItems)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
totalPrice += priceType == LootTrackerPriceType.HIGH_ALCHEMY ? entry.getHaPrice() : entry.getGePrice();
|
||||
|
||||
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 gePricePerItem = entry.getGePrice() == 0 ? 0 : (entry.getGePrice() / entry.getQuantity());
|
||||
long haPricePerItem = entry.getHaPrice() == 0 ? 0 : (entry.getHaPrice() / entry.getQuantity());
|
||||
|
||||
items.add(new LootTrackerItem(entry.getId(), entry.getName(), newQuantity, gePricePerItem * newQuantity, haPricePerItem * newQuantity, entry.isIgnored()));
|
||||
}
|
||||
else
|
||||
{
|
||||
items.add(entry);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (priceType == LootTrackerPriceType.HIGH_ALCHEMY)
|
||||
{
|
||||
items.sort((i1, i2) -> Long.compare(i2.getHaPrice(), i1.getHaPrice()));
|
||||
}
|
||||
else
|
||||
{
|
||||
items.sort((i1, i2) -> Long.compare(i2.getGePrice(), i1.getGePrice()));
|
||||
}
|
||||
ToLongFunction<LootTrackerItem> getPrice = priceType == LootTrackerPriceType.HIGH_ALCHEMY
|
||||
? LootTrackerItem::getTotalHaPrice
|
||||
: LootTrackerItem::getTotalGePrice;
|
||||
|
||||
totalPrice = items.stream()
|
||||
.mapToLong(getPrice)
|
||||
.sum();
|
||||
|
||||
items.sort(Comparator.comparingLong(getPrice).reversed());
|
||||
|
||||
// Calculates how many rows need to be display to fit all items
|
||||
final int rowSize = ((items.size() % ITEMS_PER_ROW == 0) ? 0 : 1) + items.size() / ITEMS_PER_ROW;
|
||||
@@ -372,8 +351,8 @@ class LootTrackerBox extends JPanel
|
||||
{
|
||||
final String name = item.getName();
|
||||
final int quantity = item.getQuantity();
|
||||
final long gePrice = item.getGePrice();
|
||||
final long haPrice = item.getHaPrice();
|
||||
final long gePrice = item.getTotalGePrice();
|
||||
final long haPrice = item.getTotalHaPrice();
|
||||
final String ignoredLabel = item.isIgnored() ? " - Ignored" : "";
|
||||
return "<html>" + name + " x " + quantity + ignoredLabel
|
||||
+ "<br>GE: " + QuantityFormatter.quantityToStackSize(gePrice)
|
||||
|
||||
@@ -29,19 +29,24 @@ import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
class LootTrackerItem
|
||||
{
|
||||
@Getter
|
||||
private final int id;
|
||||
@Getter
|
||||
private final String name;
|
||||
@Getter
|
||||
private final int quantity;
|
||||
@Getter
|
||||
private final long gePrice;
|
||||
@Getter
|
||||
private final long haPrice;
|
||||
@Getter
|
||||
private int quantity;
|
||||
private final int gePrice;
|
||||
private final int haPrice;
|
||||
@Setter
|
||||
private boolean ignored;
|
||||
|
||||
long getTotalGePrice()
|
||||
{
|
||||
return (long) gePrice * quantity;
|
||||
}
|
||||
|
||||
long getTotalHaPrice()
|
||||
{
|
||||
return (long) haPrice * quantity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
*/
|
||||
package net.runelite.client.plugins.loottracker;
|
||||
|
||||
import static com.google.common.collect.Iterables.concat;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
@@ -35,6 +36,7 @@ import java.awt.image.BufferedImage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.ImageIcon;
|
||||
@@ -101,8 +103,10 @@ class LootTrackerPanel extends PluginPanel
|
||||
private final JLabel groupedLootBtn = new JLabel();
|
||||
private final JLabel collapseBtn = new JLabel();
|
||||
|
||||
// Log collection
|
||||
private final List<LootTrackerRecord> records = new ArrayList<>();
|
||||
// Aggregate of all kills
|
||||
private final List<LootTrackerRecord> aggregateRecords = new ArrayList<>();
|
||||
// Individual records for the individual kills this session
|
||||
private final List<LootTrackerRecord> sessionRecords = new ArrayList<>();
|
||||
private final List<LootTrackerBox> boxes = new ArrayList<>();
|
||||
|
||||
private final ItemManager itemManager;
|
||||
@@ -330,7 +334,8 @@ class LootTrackerPanel extends PluginPanel
|
||||
}
|
||||
|
||||
// If not in detailed view, remove all, otherwise only remove for the currently detailed title
|
||||
records.removeIf(r -> r.matches(currentView));
|
||||
sessionRecords.removeIf(r -> r.matches(currentView));
|
||||
aggregateRecords.removeIf(r -> r.matches(currentView));
|
||||
boxes.removeIf(b -> b.matches(currentView));
|
||||
updateOverall();
|
||||
logsContainer.removeAll();
|
||||
@@ -377,7 +382,7 @@ class LootTrackerPanel extends PluginPanel
|
||||
private boolean isAllCollapsed()
|
||||
{
|
||||
return boxes.stream()
|
||||
.filter(i -> i.isCollapsed())
|
||||
.filter(LootTrackerBox::isCollapsed)
|
||||
.count() == boxes.size();
|
||||
}
|
||||
|
||||
@@ -394,8 +399,9 @@ class LootTrackerPanel extends PluginPanel
|
||||
void add(final String eventName, final int actorLevel, LootTrackerItem[] items)
|
||||
{
|
||||
final String subTitle = actorLevel > -1 ? "(lvl-" + actorLevel + ")" : "";
|
||||
final LootTrackerRecord record = new LootTrackerRecord(eventName, subTitle, items);
|
||||
records.add(record);
|
||||
final LootTrackerRecord record = new LootTrackerRecord(eventName, subTitle, items, 1);
|
||||
sessionRecords.add(record);
|
||||
|
||||
LootTrackerBox box = buildBox(record);
|
||||
if (box != null)
|
||||
{
|
||||
@@ -409,7 +415,7 @@ class LootTrackerPanel extends PluginPanel
|
||||
*/
|
||||
void addRecords(Collection<LootTrackerRecord> recs)
|
||||
{
|
||||
records.addAll(recs);
|
||||
aggregateRecords.addAll(recs);
|
||||
rebuild();
|
||||
}
|
||||
|
||||
@@ -466,14 +472,11 @@ class LootTrackerPanel extends PluginPanel
|
||||
*/
|
||||
void updateIgnoredRecords()
|
||||
{
|
||||
for (LootTrackerRecord r : records)
|
||||
for (LootTrackerRecord record : concat(aggregateRecords, sessionRecords))
|
||||
{
|
||||
for (LootTrackerItem item : r.getItems())
|
||||
for (LootTrackerItem item : record.getItems())
|
||||
{
|
||||
if (plugin.isIgnored(item.getName()) != item.isIgnored())
|
||||
{
|
||||
item.setIgnored(plugin.isIgnored(item.getName()));
|
||||
}
|
||||
item.setIgnored(plugin.isIgnored(item.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,15 +490,25 @@ class LootTrackerPanel extends PluginPanel
|
||||
{
|
||||
logsContainer.removeAll();
|
||||
boxes.clear();
|
||||
int start = 0;
|
||||
if (!groupLoot && records.size() > MAX_LOOT_BOXES)
|
||||
|
||||
if (groupLoot)
|
||||
{
|
||||
start = records.size() - MAX_LOOT_BOXES;
|
||||
aggregateRecords.forEach(this::buildBox);
|
||||
sessionRecords.forEach(this::buildBox);
|
||||
}
|
||||
for (int i = start; i < records.size(); i++)
|
||||
else
|
||||
{
|
||||
buildBox(records.get(i));
|
||||
int start = 0;
|
||||
if (sessionRecords.size() > MAX_LOOT_BOXES)
|
||||
{
|
||||
start = sessionRecords.size() - MAX_LOOT_BOXES;
|
||||
}
|
||||
for (int i = start; i < sessionRecords.size(); i++)
|
||||
{
|
||||
buildBox(sessionRecords.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
boxes.forEach(LootTrackerBox::rebuild);
|
||||
updateOverall();
|
||||
logsContainer.revalidate();
|
||||
@@ -522,7 +535,7 @@ class LootTrackerPanel extends PluginPanel
|
||||
{
|
||||
if (box.matches(record))
|
||||
{
|
||||
box.combine(record);
|
||||
box.addKill(record);
|
||||
return box;
|
||||
}
|
||||
}
|
||||
@@ -536,7 +549,7 @@ class LootTrackerPanel extends PluginPanel
|
||||
// Create box
|
||||
final LootTrackerBox box = new LootTrackerBox(itemManager, record.getTitle(), record.getSubTitle(),
|
||||
hideIgnoredItems, config.priceType(), config.showPriceType(), plugin::toggleItem);
|
||||
box.combine(record);
|
||||
box.addKill(record);
|
||||
|
||||
// Create popup menu
|
||||
final JPopupMenu popupMenu = new JPopupMenu();
|
||||
@@ -568,7 +581,13 @@ class LootTrackerPanel extends PluginPanel
|
||||
final JMenuItem reset = new JMenuItem("Reset");
|
||||
reset.addActionListener(e ->
|
||||
{
|
||||
records.removeAll(box.getRecords());
|
||||
Predicate<LootTrackerRecord> match = groupLoot
|
||||
// With grouped loot, remove any record with this title
|
||||
? r -> r.matches(record.getTitle())
|
||||
// Otherwise remove specifically this entry
|
||||
: r -> r.equals(record);
|
||||
sessionRecords.removeIf(match);
|
||||
aggregateRecords.removeIf(match);
|
||||
boxes.remove(box);
|
||||
updateOverall();
|
||||
logsContainer.remove(box);
|
||||
@@ -614,7 +633,7 @@ class LootTrackerPanel extends PluginPanel
|
||||
long overallGe = 0;
|
||||
long overallHa = 0;
|
||||
|
||||
for (LootTrackerRecord record : records)
|
||||
for (LootTrackerRecord record : concat(aggregateRecords, sessionRecords))
|
||||
{
|
||||
if (!record.matches(currentView))
|
||||
{
|
||||
@@ -631,13 +650,13 @@ class LootTrackerPanel extends PluginPanel
|
||||
continue;
|
||||
}
|
||||
|
||||
overallGe += item.getGePrice();
|
||||
overallHa += item.getHaPrice();
|
||||
overallGe += item.getTotalGePrice();
|
||||
overallHa += item.getTotalHaPrice();
|
||||
}
|
||||
|
||||
if (present > 0)
|
||||
{
|
||||
overallKills++;
|
||||
overallKills += record.getKills();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,6 +90,7 @@ import net.runelite.client.ui.NavigationButton;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
import net.runelite.client.util.Text;
|
||||
import net.runelite.http.api.loottracker.GameItem;
|
||||
import net.runelite.http.api.loottracker.LootAggregate;
|
||||
import net.runelite.http.api.loottracker.LootRecord;
|
||||
import net.runelite.http.api.loottracker.LootRecordType;
|
||||
import net.runelite.http.api.loottracker.LootTrackerClient;
|
||||
@@ -276,13 +277,12 @@ public class LootTrackerPlugin extends Plugin
|
||||
|
||||
executor.submit(() ->
|
||||
{
|
||||
Collection<LootRecord> lootRecords;
|
||||
|
||||
if (!config.syncPanel())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Collection<LootAggregate> lootRecords;
|
||||
try
|
||||
{
|
||||
lootRecords = lootTrackerClient.get();
|
||||
@@ -624,8 +624,8 @@ public class LootTrackerPlugin extends Plugin
|
||||
{
|
||||
final ItemComposition itemComposition = itemManager.getItemComposition(itemId);
|
||||
final int realItemId = itemComposition.getNote() != -1 ? itemComposition.getLinkedNoteId() : itemId;
|
||||
final long gePrice = (long) itemManager.getItemPrice(realItemId) * (long) quantity;
|
||||
final long haPrice = (long) Math.round(itemComposition.getPrice() * Constants.HIGH_ALCHEMY_MULTIPLIER) * (long) quantity;
|
||||
final int gePrice = itemManager.getItemPrice(realItemId);
|
||||
final int haPrice = Math.round(itemComposition.getPrice() * Constants.HIGH_ALCHEMY_MULTIPLIER);
|
||||
final boolean ignored = ignoredItems.contains(itemComposition.getName());
|
||||
|
||||
return new LootTrackerItem(
|
||||
@@ -651,17 +651,17 @@ public class LootTrackerPlugin extends Plugin
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private Collection<LootTrackerRecord> convertToLootTrackerRecord(final Collection<LootRecord> records)
|
||||
private Collection<LootTrackerRecord> convertToLootTrackerRecord(final Collection<LootAggregate> records)
|
||||
{
|
||||
return records.stream()
|
||||
.sorted(Comparator.comparing(LootRecord::getTime))
|
||||
.sorted(Comparator.comparing(LootAggregate::getLast_time))
|
||||
.map(record ->
|
||||
{
|
||||
LootTrackerItem[] drops = record.getDrops().stream().map(itemStack ->
|
||||
buildLootTrackerItem(itemStack.getId(), itemStack.getQty())
|
||||
).toArray(LootTrackerItem[]::new);
|
||||
|
||||
return new LootTrackerRecord(record.getEventId(), "", drops);
|
||||
return new LootTrackerRecord(record.getEventId(), "", drops, record.getAmount());
|
||||
})
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
}
|
||||
|
||||
@@ -32,9 +32,11 @@ class LootTrackerRecord
|
||||
private final String title;
|
||||
private final String subTitle;
|
||||
private final LootTrackerItem[] items;
|
||||
private final int kills;
|
||||
|
||||
/**
|
||||
* Checks if this record matches specified id
|
||||
*
|
||||
* @param id other record id
|
||||
* @return true if match is made
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user