Dev Tools - Add Inventory Inspector
This commit is contained in:
@@ -51,6 +51,7 @@ class DevToolsPanel extends PluginPanel
|
||||
private final WidgetInspector widgetInspector;
|
||||
private final VarInspector varInspector;
|
||||
private final ScriptInspector scriptInspector;
|
||||
private final InventoryInspector inventoryInspector;
|
||||
private final InfoBoxManager infoBoxManager;
|
||||
private final ScheduledExecutorService scheduledExecutorService;
|
||||
|
||||
@@ -61,6 +62,7 @@ class DevToolsPanel extends PluginPanel
|
||||
WidgetInspector widgetInspector,
|
||||
VarInspector varInspector,
|
||||
ScriptInspector scriptInspector,
|
||||
InventoryInspector inventoryInspector,
|
||||
Notifier notifier,
|
||||
InfoBoxManager infoBoxManager,
|
||||
ScheduledExecutorService scheduledExecutorService)
|
||||
@@ -70,6 +72,7 @@ class DevToolsPanel extends PluginPanel
|
||||
this.plugin = plugin;
|
||||
this.widgetInspector = widgetInspector;
|
||||
this.varInspector = varInspector;
|
||||
this.inventoryInspector = inventoryInspector;
|
||||
this.scriptInspector = scriptInspector;
|
||||
this.notifier = notifier;
|
||||
this.infoBoxManager = infoBoxManager;
|
||||
@@ -180,6 +183,19 @@ class DevToolsPanel extends PluginPanel
|
||||
clearInfoboxBtn.addActionListener(e -> infoBoxManager.removeIf(i -> true));
|
||||
container.add(clearInfoboxBtn);
|
||||
|
||||
container.add(plugin.getInventoryInspector());
|
||||
plugin.getInventoryInspector().addActionListener((ev) ->
|
||||
{
|
||||
if (plugin.getInventoryInspector().isActive())
|
||||
{
|
||||
inventoryInspector.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
inventoryInspector.open();
|
||||
}
|
||||
});
|
||||
|
||||
return container;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,6 +140,7 @@ public class DevToolsPlugin extends Plugin
|
||||
private DevToolsButton varInspector;
|
||||
private DevToolsButton soundEffects;
|
||||
private DevToolsButton scriptInspector;
|
||||
private DevToolsButton inventoryInspector;
|
||||
private NavigationButton navButton;
|
||||
|
||||
@Provides
|
||||
@@ -182,6 +183,7 @@ public class DevToolsPlugin extends Plugin
|
||||
varInspector = new DevToolsButton("Var Inspector");
|
||||
soundEffects = new DevToolsButton("Sound Effects");
|
||||
scriptInspector = new DevToolsButton("Script Inspector");
|
||||
inventoryInspector = new DevToolsButton("Inventory Inspector");
|
||||
|
||||
overlayManager.add(overlay);
|
||||
overlayManager.add(locationOverlay);
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright (c) 2020, TheStonedTurtle <https://github.com/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.devtools;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.ComponentAdapter;
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.text.DecimalFormat;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.Scrollable;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import net.runelite.api.Constants;
|
||||
import net.runelite.api.Item;
|
||||
import net.runelite.client.game.ItemManager;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
import net.runelite.client.ui.FontManager;
|
||||
|
||||
class InventoryDeltaPanel extends JPanel implements Scrollable
|
||||
{
|
||||
private static final DecimalFormat COMMA_FORMAT = new DecimalFormat("#,###");
|
||||
private static final Dimension ITEM_SIZE = new Dimension(Constants.ITEM_SPRITE_WIDTH + 4, Constants.ITEM_SPRITE_HEIGHT);
|
||||
|
||||
private final ItemManager itemManager;
|
||||
private final JPanel addedGrid = new JPanel();
|
||||
private final JPanel removedGrid = new JPanel();
|
||||
private final JPanel currentGrid = new JPanel();
|
||||
|
||||
InventoryDeltaPanel(final ItemManager itemManager)
|
||||
{
|
||||
this.itemManager = itemManager;
|
||||
|
||||
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
|
||||
|
||||
final EmptyBorder border = new EmptyBorder(2, 2, 2, 2);
|
||||
setBorder(border);
|
||||
addedGrid.setBorder(border);
|
||||
removedGrid.setBorder(border);
|
||||
currentGrid.setBorder(border);
|
||||
|
||||
final GridLayout layout = new GridLayout(0, 1, 1, 1);
|
||||
addedGrid.setLayout(layout);
|
||||
removedGrid.setLayout(layout);
|
||||
currentGrid.setLayout(layout);
|
||||
|
||||
// Listen for resize events
|
||||
addComponentListener(new ComponentAdapter()
|
||||
{
|
||||
public void componentResized(final ComponentEvent componentEvent)
|
||||
{
|
||||
// Account for container and slot padding
|
||||
final int cols = Math.max((getWidth() - 4) / (ITEM_SIZE.width + 1), 1);
|
||||
final GridLayout layout = new GridLayout(0, cols, 1, 1);
|
||||
addedGrid.setLayout(layout);
|
||||
removedGrid.setLayout(layout);
|
||||
currentGrid.setLayout(layout);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
addedGrid.removeAll();
|
||||
removedGrid.removeAll();
|
||||
currentGrid.removeAll();
|
||||
removeAll();
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
|
||||
void displayItems(final InventoryItem[] items, @Nullable final InventoryItem[] added, @Nullable final InventoryItem[] removed)
|
||||
{
|
||||
clear();
|
||||
|
||||
if (added != null && added.length > 0)
|
||||
{
|
||||
final JLabel label = new JLabel("Items Added:", JLabel.CENTER);
|
||||
label.setAlignmentX(JLabel.CENTER_ALIGNMENT);
|
||||
add(label);
|
||||
add(addedGrid);
|
||||
|
||||
for (final InventoryItem item : added)
|
||||
{
|
||||
addItemToPanel(item, addedGrid).setBackground(new Color(0, 100, 0));
|
||||
}
|
||||
}
|
||||
|
||||
if (removed != null && removed.length > 0)
|
||||
{
|
||||
final JLabel label = new JLabel("Items Removed:", JLabel.CENTER);
|
||||
label.setAlignmentX(JLabel.CENTER_ALIGNMENT);
|
||||
add(label);
|
||||
add(removedGrid);
|
||||
|
||||
for (final InventoryItem item : removed)
|
||||
{
|
||||
addItemToPanel(item, removedGrid).setBackground(new Color(120, 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
final JLabel label = new JLabel("Items in Inventory:", JLabel.CENTER);
|
||||
label.setAlignmentX(JLabel.CENTER_ALIGNMENT);
|
||||
add(label);
|
||||
add(currentGrid);
|
||||
|
||||
for (final InventoryItem item : items)
|
||||
{
|
||||
final JLabel gridItem = addItemToPanel(item, currentGrid);
|
||||
// Add hover effect
|
||||
gridItem.addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e)
|
||||
{
|
||||
final JLabel label = (JLabel) e.getSource();
|
||||
label.setBackground(ColorScheme.DARKER_GRAY_HOVER_COLOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e)
|
||||
{
|
||||
final JLabel label = (JLabel) e.getSource();
|
||||
label.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
}
|
||||
});
|
||||
gridItem.setToolTipText("<html>Name: " + item.getName()
|
||||
+ "<br/>Item ID: " + item.getItem().getId()
|
||||
+ "<br/>Quantity: " + COMMA_FORMAT.format(item.getItem().getQuantity())
|
||||
+ "<br/>Slot: " + item.getSlot()
|
||||
+ "</html>");
|
||||
}
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
|
||||
private JLabel addItemToPanel(final InventoryItem inventoryItem, final JPanel panel)
|
||||
{
|
||||
final JLabel gridItem = new JLabel();
|
||||
gridItem.setOpaque(true);
|
||||
gridItem.setPreferredSize(ITEM_SIZE);
|
||||
gridItem.setVerticalAlignment(SwingConstants.CENTER);
|
||||
gridItem.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
gridItem.setFont(FontManager.getRunescapeSmallFont());
|
||||
|
||||
final Item item = inventoryItem.getItem();
|
||||
if (item.getId() == -1)
|
||||
{
|
||||
gridItem.setText("EMPTY");
|
||||
}
|
||||
else
|
||||
{
|
||||
itemManager.getImage(item.getId(), item.getQuantity(), item.getQuantity() > 1).addTo(gridItem);
|
||||
gridItem.setToolTipText("<html>Name: " + inventoryItem.getName()
|
||||
+ "<br/>Item ID: " + item.getId()
|
||||
+ "<br/>Quantity: " + COMMA_FORMAT.format(item.getQuantity())
|
||||
+ "</html>");
|
||||
}
|
||||
|
||||
panel.add(gridItem);
|
||||
return gridItem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredScrollableViewportSize()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction)
|
||||
{
|
||||
return 1 + (orientation == SwingConstants.VERTICAL ? ITEM_SIZE.height : ITEM_SIZE.width);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction)
|
||||
{
|
||||
return 1 + (orientation == SwingConstants.VERTICAL ? ITEM_SIZE.height : ITEM_SIZE.width);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getScrollableTracksViewportWidth()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getScrollableTracksViewportHeight()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
* Copyright (c) 2020, TheStonedTurtle <https://github.com/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.devtools;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.AdjustmentEvent;
|
||||
import java.awt.event.AdjustmentListener;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollBar;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.DefaultTreeModel;
|
||||
import javax.swing.tree.TreeNode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.InventoryID;
|
||||
import net.runelite.api.Item;
|
||||
import net.runelite.api.ItemComposition;
|
||||
import net.runelite.api.events.ItemContainerChanged;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
import net.runelite.client.eventbus.EventBus;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.game.ItemManager;
|
||||
import net.runelite.client.ui.ClientUI;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
|
||||
@Slf4j
|
||||
@Singleton
|
||||
class InventoryInspector extends JFrame
|
||||
{
|
||||
private static final int MAX_LOG_ENTRIES = 25;
|
||||
|
||||
private final Client client;
|
||||
private final EventBus eventBus;
|
||||
private final ItemManager itemManager;
|
||||
|
||||
private final Map<Integer, InventoryTreeNode> nodeMap = new HashMap<>();
|
||||
private final Map<Integer, InventoryLog> logMap = new HashMap<>();
|
||||
private final DefaultMutableTreeNode trackerRootNode = new DefaultMutableTreeNode();
|
||||
private final JTree tree = new JTree(trackerRootNode);
|
||||
private final InventoryDeltaPanel deltaPanel;
|
||||
|
||||
@Inject
|
||||
InventoryInspector(Client client, EventBus eventBus, DevToolsPlugin plugin, ItemManager itemManager, ClientThread clientThread)
|
||||
{
|
||||
this.client = client;
|
||||
this.eventBus = eventBus;
|
||||
this.itemManager = itemManager;
|
||||
|
||||
this.deltaPanel = new InventoryDeltaPanel(itemManager);
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
setTitle("RuneLite Inventory Inspector");
|
||||
setIconImage(ClientUI.ICON);
|
||||
|
||||
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
|
||||
// Reset highlight on close
|
||||
addWindowListener(new WindowAdapter()
|
||||
{
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e)
|
||||
{
|
||||
close();
|
||||
plugin.getInventoryInspector().setActive(false);
|
||||
}
|
||||
});
|
||||
|
||||
tree.setBorder(new EmptyBorder(2, 2, 2, 2));
|
||||
tree.setRootVisible(false);
|
||||
tree.setShowsRootHandles(true);
|
||||
tree.addTreeSelectionListener(e ->
|
||||
{
|
||||
if (e.getNewLeadSelectionPath() == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final Object node = e.getNewLeadSelectionPath().getLastPathComponent();
|
||||
if (node instanceof InventoryLogNode)
|
||||
{
|
||||
clientThread.invoke(() -> displayItemSnapshot((InventoryLogNode) node));
|
||||
}
|
||||
});
|
||||
tree.setModel(new DefaultTreeModel(trackerRootNode));
|
||||
|
||||
final JPanel leftSide = new JPanel();
|
||||
leftSide.setLayout(new BorderLayout());
|
||||
|
||||
final JScrollPane trackerScroller = new JScrollPane(tree);
|
||||
trackerScroller.setPreferredSize(new Dimension(200, 400));
|
||||
|
||||
final JScrollBar vertical = trackerScroller.getVerticalScrollBar();
|
||||
vertical.addAdjustmentListener(new AdjustmentListener()
|
||||
{
|
||||
int lastMaximum = actualMax();
|
||||
|
||||
private int actualMax()
|
||||
{
|
||||
return vertical.getMaximum() - vertical.getModel().getExtent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void adjustmentValueChanged(AdjustmentEvent e)
|
||||
{
|
||||
if (vertical.getValue() >= lastMaximum)
|
||||
{
|
||||
vertical.setValue(actualMax());
|
||||
}
|
||||
lastMaximum = actualMax();
|
||||
}
|
||||
});
|
||||
|
||||
leftSide.add(trackerScroller, BorderLayout.CENTER);
|
||||
|
||||
final JButton refreshBtn = new JButton("Refresh");
|
||||
refreshBtn.setFocusable(false);
|
||||
refreshBtn.addActionListener(e -> refreshTracker());
|
||||
|
||||
final JButton clearBtn = new JButton("Clear");
|
||||
clearBtn.setFocusable(false);
|
||||
clearBtn.addActionListener(e -> clearTracker());
|
||||
|
||||
final JPanel bottomRow = new JPanel();
|
||||
bottomRow.add(refreshBtn);
|
||||
bottomRow.add(clearBtn);
|
||||
|
||||
leftSide.add(bottomRow, BorderLayout.SOUTH);
|
||||
|
||||
final JScrollPane gridScroller = new JScrollPane(deltaPanel);
|
||||
gridScroller.getViewport().setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||
gridScroller.setPreferredSize(new Dimension(200, 400));
|
||||
|
||||
final JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftSide, gridScroller);
|
||||
add(split, BorderLayout.CENTER);
|
||||
|
||||
pack();
|
||||
}
|
||||
|
||||
public void open()
|
||||
{
|
||||
eventBus.register(this);
|
||||
setVisible(true);
|
||||
toFront();
|
||||
repaint();
|
||||
}
|
||||
|
||||
public void close()
|
||||
{
|
||||
eventBus.unregister(this);
|
||||
clearTracker();
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onItemContainerChanged(ItemContainerChanged event)
|
||||
{
|
||||
final int id = event.getContainerId();
|
||||
final InventoryLog log = new InventoryLog(id, getNameForInventoryID(id), event.getItemContainer().getItems(), client.getTickCount());
|
||||
|
||||
// Delay updates until refresh button is pressed
|
||||
logMap.put(id, log);
|
||||
}
|
||||
|
||||
private void addLog(final InventoryLog invLog)
|
||||
{
|
||||
final InventoryTreeNode node = nodeMap.computeIfAbsent(invLog.getContainerId(), (k) -> new InventoryTreeNode(invLog.getContainerId(), invLog.getContainerName()));
|
||||
node.add(new InventoryLogNode(invLog));
|
||||
|
||||
// Cull very old stuff
|
||||
for (; node.getChildCount() > MAX_LOG_ENTRIES; )
|
||||
{
|
||||
node.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void clearTracker()
|
||||
{
|
||||
logMap.clear();
|
||||
nodeMap.clear();
|
||||
deltaPanel.clear();
|
||||
trackerRootNode.removeAllChildren();
|
||||
tree.setModel(new DefaultTreeModel(trackerRootNode));
|
||||
}
|
||||
|
||||
private void refreshTracker()
|
||||
{
|
||||
deltaPanel.clear();
|
||||
|
||||
if (logMap.size() > 0)
|
||||
{
|
||||
logMap.values().forEach(this::addLog);
|
||||
logMap.clear();
|
||||
}
|
||||
|
||||
SwingUtilities.invokeLater(() ->
|
||||
{
|
||||
trackerRootNode.removeAllChildren();
|
||||
nodeMap.values().forEach(trackerRootNode::add);
|
||||
tree.setModel(new DefaultTreeModel(trackerRootNode));
|
||||
});
|
||||
}
|
||||
|
||||
private void displayItemSnapshot(final InventoryLogNode logNode)
|
||||
{
|
||||
final InventoryTreeNode treeNode = nodeMap.get(logNode.getLog().getContainerId());
|
||||
if (treeNode == null)
|
||||
{
|
||||
log.warn("Clicked on a JTree node that doesn't map anywhere: {}", logNode);
|
||||
return;
|
||||
}
|
||||
|
||||
final Item[] curItems = logNode.getLog().getItems();
|
||||
final InventoryItem[] curInventory = convertToInventoryItems(curItems);
|
||||
|
||||
InventoryItem[][] deltas = null;
|
||||
// Compare against previous snapshot
|
||||
if (treeNode.getIndex(logNode) > 0)
|
||||
{
|
||||
final TreeNode prevNode = treeNode.getChildBefore(logNode);
|
||||
if (prevNode instanceof InventoryLogNode)
|
||||
{
|
||||
final InventoryLogNode prevLogNode = (InventoryLogNode) prevNode;
|
||||
deltas = compareItemSnapshots(prevLogNode.getLog().getItems(), curItems);
|
||||
}
|
||||
}
|
||||
|
||||
final InventoryItem[] added = deltas == null ? null : deltas[0];
|
||||
final InventoryItem[] removed = deltas == null ? null : deltas[1];
|
||||
|
||||
SwingUtilities.invokeLater(() -> deltaPanel.displayItems(curInventory, added, removed));
|
||||
}
|
||||
|
||||
private InventoryItem[] convertToInventoryItems(final Item[] items)
|
||||
{
|
||||
final InventoryItem[] out = new InventoryItem[items.length];
|
||||
for (int i = 0; i < items.length; i++)
|
||||
{
|
||||
final Item item = items[i];
|
||||
final ItemComposition c = itemManager.getItemComposition(item.getId());
|
||||
out[i] = new InventoryItem(i, item, c.getName(), c.isStackable());
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the current inventory to the old one returning the InventoryItems that were added and removed.
|
||||
* @param previous old snapshot
|
||||
* @param current new snapshot
|
||||
* @return The first InventoryItem[] contains the items that were added and the second contains the items that were removed
|
||||
*/
|
||||
private InventoryItem[][] compareItemSnapshots(final Item[] previous, final Item[] current)
|
||||
{
|
||||
final Map<Integer, Integer> qtyMap = new HashMap<>();
|
||||
|
||||
// ItemContainers shouldn't become smaller over time, but just in case
|
||||
final int maxSlots = Math.max(previous.length, current.length);
|
||||
for (int i = 0; i < maxSlots; i++)
|
||||
{
|
||||
final Item prev = previous.length > i ? previous[i] : null;
|
||||
final Item cur = current.length > i ? current[i] : null;
|
||||
|
||||
if (prev != null)
|
||||
{
|
||||
qtyMap.merge(prev.getId(), -1 * prev.getQuantity(), Integer::sum);
|
||||
}
|
||||
if (cur != null)
|
||||
{
|
||||
qtyMap.merge(cur.getId(), cur.getQuantity(), Integer::sum);
|
||||
}
|
||||
}
|
||||
|
||||
final Map<Boolean, List<InventoryItem>> result = qtyMap.entrySet().stream()
|
||||
.filter(e -> e.getValue() != 0)
|
||||
.flatMap(e ->
|
||||
{
|
||||
final int id = e.getKey();
|
||||
final int qty = e.getValue();
|
||||
final ItemComposition c = itemManager.getItemComposition(e.getKey());
|
||||
|
||||
InventoryItem[] items = new InventoryItem[]{
|
||||
new InventoryItem(-1, new Item(id, qty), c.getName(), c.isStackable())
|
||||
};
|
||||
if (!c.isStackable() && (qty > 1 || qty < -1))
|
||||
{
|
||||
items = new InventoryItem[Math.abs(qty)];
|
||||
for (int i = 0; i < Math.abs(qty); i++)
|
||||
{
|
||||
final Item item = new Item(id, Integer.signum(qty));
|
||||
items[i] = new InventoryItem(-1, item, c.getName(), c.isStackable());
|
||||
}
|
||||
}
|
||||
|
||||
return Arrays.stream(items);
|
||||
})
|
||||
.collect(Collectors.partitioningBy(item -> item.getItem().getQuantity() > 0));
|
||||
|
||||
final InventoryItem[] added = result.get(true).toArray(new InventoryItem[0]);
|
||||
final InventoryItem[] removed = result.get(false).stream()
|
||||
// Make quantities positive now that its been sorted.
|
||||
.peek(i -> i.setItem(new Item(i.getItem().getId(), -i.getItem().getQuantity())))
|
||||
.toArray(InventoryItem[]::new);
|
||||
|
||||
return new InventoryItem[][]{
|
||||
added, removed
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getNameForInventoryID(final int id)
|
||||
{
|
||||
for (final InventoryID inv : InventoryID.values())
|
||||
{
|
||||
if (inv.getId() == id)
|
||||
{
|
||||
return inv.name();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2020, TheStonedTurtle <https://github.com/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.devtools;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import net.runelite.api.Item;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
class InventoryItem
|
||||
{
|
||||
private final int slot;
|
||||
private Item item;
|
||||
private final String name;
|
||||
private final boolean stackable;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2020, TheStonedTurtle <https://github.com/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.devtools;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.Value;
|
||||
import net.runelite.api.Item;
|
||||
|
||||
@Value
|
||||
class InventoryLog
|
||||
{
|
||||
int containerId;
|
||||
@Nullable
|
||||
String containerName;
|
||||
Item[] items;
|
||||
int tick;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2020, TheStonedTurtle <https://github.com/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.devtools;
|
||||
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
class InventoryLogNode extends DefaultMutableTreeNode
|
||||
{
|
||||
private final InventoryLog log;
|
||||
|
||||
InventoryLogNode(final InventoryLog log)
|
||||
{
|
||||
super();
|
||||
|
||||
this.log = log;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "Tick: " + log.getTick();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) 2020, TheStonedTurtle <https://github.com/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.devtools;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
class InventoryTreeNode extends DefaultMutableTreeNode
|
||||
{
|
||||
final int id;
|
||||
@Nullable
|
||||
final String name;
|
||||
|
||||
InventoryTreeNode(final int id, @Nullable final String name)
|
||||
{
|
||||
super();
|
||||
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return id + (name == null ? "" : " - " + name);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user