Improve ground items plugin
- Reduce amout of iterations per frame by pre-collecting the ground items on game tick - Collect ground items in ground item plugins and pre-compute some easy-to-get values - Replace the looping over all tiles with looping over collected ground items - Change GroundItemOverlay logic to support single list of items instead of multi-dimensional array Signed-off-by: Tomas Slusny <slusnucky@gmail.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
|
||||||
|
* 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.grounditems;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.Value;
|
||||||
|
import net.runelite.api.coords.WorldPoint;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
class GroundItem
|
||||||
|
{
|
||||||
|
private int index;
|
||||||
|
private int id;
|
||||||
|
private int itemId;
|
||||||
|
private String name;
|
||||||
|
private int quantity;
|
||||||
|
private WorldPoint location;
|
||||||
|
private int haPrice;
|
||||||
|
private int gePrice;
|
||||||
|
|
||||||
|
@Value
|
||||||
|
static class GroundItemKey
|
||||||
|
{
|
||||||
|
private int itemId;
|
||||||
|
private WorldPoint location;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,72 +29,50 @@ import java.awt.Dimension;
|
|||||||
import java.awt.FontMetrics;
|
import java.awt.FontMetrics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import static java.lang.Math.max;
|
import java.util.HashMap;
|
||||||
import static java.lang.Math.min;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import net.runelite.api.Client;
|
import net.runelite.api.Client;
|
||||||
import net.runelite.api.Item;
|
import net.runelite.api.Perspective;
|
||||||
import net.runelite.api.ItemComposition;
|
|
||||||
import net.runelite.api.ItemID;
|
|
||||||
import net.runelite.api.ItemLayer;
|
|
||||||
import net.runelite.api.Node;
|
|
||||||
import net.runelite.api.Player;
|
import net.runelite.api.Player;
|
||||||
import net.runelite.api.Point;
|
import net.runelite.api.Point;
|
||||||
import net.runelite.api.Region;
|
|
||||||
import net.runelite.api.Tile;
|
|
||||||
import net.runelite.api.coords.LocalPoint;
|
import net.runelite.api.coords.LocalPoint;
|
||||||
import net.runelite.client.game.ItemManager;
|
import net.runelite.api.coords.WorldPoint;
|
||||||
import net.runelite.client.ui.FontManager;
|
import net.runelite.client.ui.FontManager;
|
||||||
import net.runelite.client.ui.overlay.Overlay;
|
import net.runelite.client.ui.overlay.Overlay;
|
||||||
import net.runelite.client.ui.overlay.OverlayLayer;
|
import net.runelite.client.ui.overlay.OverlayLayer;
|
||||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||||
|
import net.runelite.client.ui.overlay.components.TextComponent;
|
||||||
import net.runelite.client.util.StackFormatter;
|
import net.runelite.client.util.StackFormatter;
|
||||||
import net.runelite.http.api.item.ItemPrice;
|
|
||||||
|
|
||||||
public class GroundItemsOverlay extends Overlay
|
public class GroundItemsOverlay extends Overlay
|
||||||
{
|
{
|
||||||
private static final int REGION_SIZE = 104;
|
private static final int MAX_DISTANCE = 2500;
|
||||||
// We must offset the text on the z-axis such that
|
// We must offset the text on the z-axis such that
|
||||||
// it doesn't obscure the ground items below it.
|
// it doesn't obscure the ground items below it.
|
||||||
private static final int OFFSET_Z = 20;
|
private static final int OFFSET_Z = 20;
|
||||||
// The game won't send anything higher than this value to the plugin -
|
// The game won't send anything higher than this value to the plugin -
|
||||||
// so we replace any item quantity higher with "Lots" instead.
|
// so we replace any item quantity higher with "Lots" instead.
|
||||||
private static final int MAX_QUANTITY = 65535;
|
private static final int MAX_QUANTITY = 65535;
|
||||||
// The max distance in tiles between the player and the item.
|
|
||||||
private static final int MAX_RANGE = 18;
|
|
||||||
// The 15 pixel gap between each drawn ground item.
|
// The 15 pixel gap between each drawn ground item.
|
||||||
private static final int STRING_GAP = 15;
|
private static final int STRING_GAP = 15;
|
||||||
// Threshold for highlighting items as blue.
|
// Threshold for highlighting items as blue.
|
||||||
static final int LOW_VALUE = 20_000;
|
private static final int LOW_VALUE = 20_000;
|
||||||
// Threshold for highlighting items as green.
|
// Threshold for highlighting items as green.
|
||||||
private static final int MEDIUM_VALUE = 100_000;
|
private static final int MEDIUM_VALUE = 100_000;
|
||||||
// Threshold for highlighting items as amber.
|
// Threshold for highlighting items as amber.
|
||||||
private static final int HIGH_VALUE = 1_000_000;
|
private static final int HIGH_VALUE = 1_000_000;
|
||||||
// Threshold for highlighting items as pink.
|
// Threshold for highlighting items as pink.
|
||||||
private static final int INSANE_VALUE = 10_000_000;
|
private static final int INSANE_VALUE = 10_000_000;
|
||||||
// Used when getting High Alchemy value - multiplied by general store price.
|
|
||||||
private static final float HIGH_ALCHEMY_CONSTANT = 0.6f;
|
|
||||||
// ItemID for coins
|
|
||||||
private static final int COINS = ItemID.COINS_995;
|
|
||||||
|
|
||||||
// Size of the hidden/highlight boxes
|
// Size of the hidden/highlight boxes
|
||||||
private static final int RECTANGLE_SIZE = 8;
|
private static final int RECTANGLE_SIZE = 8;
|
||||||
|
|
||||||
private Rectangle itemHiddenBox;
|
|
||||||
private Rectangle itemHighlightBox;
|
|
||||||
|
|
||||||
private final Client client;
|
private final Client client;
|
||||||
private final GroundItemsPlugin plugin;
|
private final GroundItemsPlugin plugin;
|
||||||
private final GroundItemsConfig config;
|
private final GroundItemsConfig config;
|
||||||
private final StringBuilder itemStringBuilder = new StringBuilder();
|
private final StringBuilder itemStringBuilder = new StringBuilder();
|
||||||
|
private final TextComponent textComponent = new TextComponent();
|
||||||
@Inject
|
private final Map<WorldPoint, Integer> offsetMap = new HashMap<>();
|
||||||
private ItemManager itemManager;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public GroundItemsOverlay(Client client, GroundItemsPlugin plugin, GroundItemsConfig config)
|
public GroundItemsOverlay(Client client, GroundItemsPlugin plugin, GroundItemsConfig config)
|
||||||
@@ -109,239 +87,156 @@ public class GroundItemsOverlay extends Overlay
|
|||||||
@Override
|
@Override
|
||||||
public Dimension render(Graphics2D graphics)
|
public Dimension render(Graphics2D graphics)
|
||||||
{
|
{
|
||||||
Region region = client.getRegion();
|
final FontMetrics fm = graphics.getFontMetrics();
|
||||||
Tile[][][] tiles = region.getTiles();
|
final Player player = client.getLocalPlayer();
|
||||||
FontMetrics fm = graphics.getFontMetrics();
|
|
||||||
|
|
||||||
Player player = client.getLocalPlayer();
|
|
||||||
if (player == null || client.getViewportWidget() == null)
|
if (player == null || client.getViewportWidget() == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
graphics.setFont(FontManager.getRunescapeSmallFont());
|
graphics.setFont(FontManager.getRunescapeSmallFont());
|
||||||
|
offsetMap.clear();
|
||||||
|
final LocalPoint localLocation = player.getLocalLocation();
|
||||||
|
|
||||||
int z = client.getPlane();
|
plugin.getCollectedGroundItems().forEach(item ->
|
||||||
LocalPoint from = player.getLocalLocation();
|
|
||||||
|
|
||||||
int lowerX = max(0, from.getRegionX() - MAX_RANGE);
|
|
||||||
int lowerY = max(0, from.getRegionY() - MAX_RANGE);
|
|
||||||
|
|
||||||
int upperX = min(from.getRegionX() + MAX_RANGE, REGION_SIZE - 1);
|
|
||||||
int upperY = min(from.getRegionY() + MAX_RANGE, REGION_SIZE - 1);
|
|
||||||
|
|
||||||
// Clear boxes
|
|
||||||
if (plugin.isHotKeyPressed())
|
|
||||||
{
|
{
|
||||||
plugin.getHiddenBoxes().clear();
|
final LocalPoint groundPoint = LocalPoint.fromWorld(client, item.getLocation());
|
||||||
plugin.getHighlightBoxes().clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int x = lowerX; x <= upperX; ++x)
|
if (groundPoint == null || localLocation.distanceTo(groundPoint) > MAX_DISTANCE)
|
||||||
{
|
|
||||||
for (int y = lowerY; y <= upperY; ++y)
|
|
||||||
{
|
{
|
||||||
Tile tile = tiles[z][x][y];
|
return;
|
||||||
if (tile == null)
|
}
|
||||||
|
|
||||||
|
final boolean highlighted = plugin.isHighlighted(item.getName());
|
||||||
|
|
||||||
|
if (!plugin.isHotKeyPressed() && !highlighted
|
||||||
|
&& ((item.getGePrice() > 0 && item.getGePrice() < config.getHideUnderGeValue())
|
||||||
|
|| item.getHaPrice() < config.getHideUnderHAValue()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean hidden = plugin.isHidden(item.getName());
|
||||||
|
final Color color = getCostColor(item.getGePrice() > 0 ? item.getGePrice() : item.getHaPrice(), highlighted);
|
||||||
|
itemStringBuilder.append(item.getName());
|
||||||
|
|
||||||
|
if (item.getQuantity() > 1)
|
||||||
|
{
|
||||||
|
if (item.getQuantity() >= MAX_QUANTITY)
|
||||||
{
|
{
|
||||||
continue;
|
itemStringBuilder.append(" (Lots!)");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
ItemLayer itemLayer = tile.getItemLayer();
|
|
||||||
if (itemLayer == null)
|
|
||||||
{
|
{
|
||||||
continue;
|
itemStringBuilder.append(" (").append(item.getQuantity()).append(")");
|
||||||
}
|
|
||||||
|
|
||||||
Node current = itemLayer.getBottom();
|
|
||||||
Map<Integer, Integer> items = new LinkedHashMap<>();
|
|
||||||
// adds the items on the ground to the ArrayList to be drawn
|
|
||||||
while (current instanceof Item)
|
|
||||||
{
|
|
||||||
Item item = (Item) current;
|
|
||||||
int itemId = item.getId();
|
|
||||||
int itemQuantity = item.getQuantity();
|
|
||||||
ItemComposition itemDefinition = itemManager.getItemComposition(itemId);
|
|
||||||
|
|
||||||
Integer currentQuantity = items.get(itemId);
|
|
||||||
|
|
||||||
if ((config.showHighlightedOnly() ? plugin.isHighlighted(itemDefinition.getName()) : !plugin.isHidden(itemDefinition.getName())) || plugin.isHotKeyPressed())
|
|
||||||
{
|
|
||||||
if (itemDefinition.getNote() != -1)
|
|
||||||
{
|
|
||||||
itemId = itemDefinition.getLinkedNoteId();
|
|
||||||
}
|
|
||||||
|
|
||||||
int quantity = currentQuantity == null
|
|
||||||
? itemQuantity
|
|
||||||
: currentQuantity + itemQuantity;
|
|
||||||
|
|
||||||
ItemPrice itemPrice = itemManager.getItemPriceAsync(itemId);
|
|
||||||
|
|
||||||
int gePrice, alchPrice;
|
|
||||||
|
|
||||||
if (itemId == COINS)
|
|
||||||
{
|
|
||||||
gePrice = quantity;
|
|
||||||
alchPrice = quantity;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
gePrice = itemPrice == null ? 0 : itemPrice.getPrice() * quantity;
|
|
||||||
alchPrice = Math.round(itemDefinition.getPrice() * HIGH_ALCHEMY_CONSTANT) * quantity;
|
|
||||||
}
|
|
||||||
if (plugin.isHighlighted(itemDefinition.getName()) ||
|
|
||||||
gePrice == 0 || ((gePrice >= config.getHideUnderGeValue()) &&
|
|
||||||
(alchPrice >= config.getHideUnderHAValue())))
|
|
||||||
{
|
|
||||||
items.put(itemId, quantity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
current = current.getNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
// The bottom item is drawn first
|
|
||||||
List<Integer> itemIds = new ArrayList<>(items.keySet());
|
|
||||||
Collections.reverse(itemIds);
|
|
||||||
|
|
||||||
for (int i = 0; i < itemIds.size(); ++i)
|
|
||||||
{
|
|
||||||
Point point = itemLayer.getCanvasLocation(itemLayer.getHeight() + OFFSET_Z);
|
|
||||||
// if the item is offscreen, don't bother drawing it
|
|
||||||
if (point == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int itemId = itemIds.get(i);
|
|
||||||
int quantity = items.get(itemId);
|
|
||||||
ItemComposition item = itemManager.getItemComposition(itemId);
|
|
||||||
|
|
||||||
if (item == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
itemStringBuilder.append(item.getName());
|
|
||||||
if (quantity > 1)
|
|
||||||
{
|
|
||||||
if (quantity >= MAX_QUANTITY)
|
|
||||||
{
|
|
||||||
itemStringBuilder.append(" (Lots!)");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
itemStringBuilder.append(" (").append(quantity).append(")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sets item ID to unnoted version, if noted
|
|
||||||
if (item.getNote() != -1)
|
|
||||||
{
|
|
||||||
itemId = item.getLinkedNoteId();
|
|
||||||
}
|
|
||||||
|
|
||||||
Color textColor = config.defaultColor(); // Color to use when drawing the ground item
|
|
||||||
ItemPrice itemPrice = itemManager.getItemPriceAsync(itemId);
|
|
||||||
if (itemPrice != null && config.showGEPrice())
|
|
||||||
{
|
|
||||||
int cost = itemPrice.getPrice() * quantity;
|
|
||||||
|
|
||||||
textColor = getCostColor(cost);
|
|
||||||
|
|
||||||
itemStringBuilder.append(" (EX: ")
|
|
||||||
.append(StackFormatter.quantityToStackSize(cost))
|
|
||||||
.append(" gp)");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.showHAValue())
|
|
||||||
{
|
|
||||||
itemStringBuilder.append(" (HA: ")
|
|
||||||
.append(Math.round(item.getPrice() * HIGH_ALCHEMY_CONSTANT) * quantity)
|
|
||||||
.append(" gp)");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (plugin.isHighlighted(item.getName()))
|
|
||||||
{
|
|
||||||
textColor = config.highlightedColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
String itemString = itemStringBuilder.toString();
|
|
||||||
itemStringBuilder.setLength(0);
|
|
||||||
|
|
||||||
int screenX = point.getX() + 2 - (fm.stringWidth(itemString) / 2);
|
|
||||||
int screenY = point.getY() - (STRING_GAP * i);
|
|
||||||
|
|
||||||
// Drawing the shadow for the text, 1px on both x and y
|
|
||||||
graphics.setColor(Color.BLACK);
|
|
||||||
graphics.drawString(itemString, screenX + 1, screenY + 1);
|
|
||||||
// Drawing the text itself
|
|
||||||
graphics.setColor(textColor);
|
|
||||||
graphics.drawString(itemString, screenX, screenY);
|
|
||||||
|
|
||||||
if (plugin.isHotKeyPressed())
|
|
||||||
{
|
|
||||||
// Hidden box
|
|
||||||
itemHiddenBox = new Rectangle
|
|
||||||
(
|
|
||||||
screenX + fm.stringWidth(itemString),
|
|
||||||
screenY - (fm.getHeight() / 2) - fm.getDescent(),
|
|
||||||
RECTANGLE_SIZE,
|
|
||||||
fm.getHeight() / 2
|
|
||||||
);
|
|
||||||
plugin.getHiddenBoxes().put(itemHiddenBox, item.getName());
|
|
||||||
|
|
||||||
// Highlight box
|
|
||||||
itemHighlightBox = new Rectangle
|
|
||||||
(
|
|
||||||
screenX + fm.stringWidth(itemString) + RECTANGLE_SIZE + 2,
|
|
||||||
screenY - (fm.getHeight() / 2) - fm.getDescent(),
|
|
||||||
RECTANGLE_SIZE,
|
|
||||||
fm.getHeight() / 2
|
|
||||||
);
|
|
||||||
plugin.getHighlightBoxes().put(itemHighlightBox, item.getName());
|
|
||||||
|
|
||||||
Point mousePos = client.getMouseCanvasPosition();
|
|
||||||
boolean mouseInHiddenBox = itemHiddenBox.contains(mousePos.getX(), mousePos.getY());
|
|
||||||
boolean mouseInHighlightBox = itemHighlightBox.contains(mousePos.getX(), mousePos.getY());
|
|
||||||
|
|
||||||
// Draw hidden box
|
|
||||||
drawRectangle(graphics, itemHiddenBox, mouseInHiddenBox ? Color.RED : textColor, plugin.isHidden(item.getName()), true);
|
|
||||||
|
|
||||||
// Draw highlight box
|
|
||||||
drawRectangle(graphics, itemHighlightBox, mouseInHighlightBox ? Color.GREEN : textColor, plugin.isHighlighted(item.getName()), false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (config.showGEPrice() && item.getGePrice() > 0)
|
||||||
|
{
|
||||||
|
itemStringBuilder.append(" (EX: ")
|
||||||
|
.append(StackFormatter.quantityToStackSize(item.getGePrice()))
|
||||||
|
.append(" gp)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.showHAValue() && item.getHaPrice() > 0)
|
||||||
|
{
|
||||||
|
itemStringBuilder.append(" (HA: ")
|
||||||
|
.append(StackFormatter.quantityToStackSize(item.getHaPrice()))
|
||||||
|
.append(" gp)");
|
||||||
|
}
|
||||||
|
|
||||||
|
final String itemString = itemStringBuilder.toString();
|
||||||
|
itemStringBuilder.setLength(0);
|
||||||
|
|
||||||
|
final Point textPoint = Perspective.getCanvasTextLocation(client,
|
||||||
|
graphics,
|
||||||
|
groundPoint,
|
||||||
|
itemString, OFFSET_Z);
|
||||||
|
|
||||||
|
if (textPoint == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int offset = offsetMap.compute(item.getLocation(), (k, v) -> v != null ? v + 1 : 0);
|
||||||
|
final int textX = textPoint.getX();
|
||||||
|
final int textY = textPoint.getY() - (STRING_GAP * offset);
|
||||||
|
|
||||||
|
textComponent.setText(itemString);
|
||||||
|
textComponent.setColor(color);
|
||||||
|
textComponent.setPosition(new java.awt.Point(textX, textY));
|
||||||
|
textComponent.render(graphics);
|
||||||
|
|
||||||
|
if (plugin.isHotKeyPressed())
|
||||||
|
{
|
||||||
|
final int stringWidth = fm.stringWidth(itemString);
|
||||||
|
final int stringHeight = fm.getHeight();
|
||||||
|
final int descent = fm.getDescent();
|
||||||
|
|
||||||
|
// Hidden box
|
||||||
|
final Rectangle itemHiddenBox = new Rectangle(
|
||||||
|
textX + stringWidth,
|
||||||
|
textY - (stringHeight / 2) - descent,
|
||||||
|
RECTANGLE_SIZE,
|
||||||
|
stringHeight / 2);
|
||||||
|
|
||||||
|
plugin.getHiddenBoxes().put(itemHiddenBox, item.getName());
|
||||||
|
|
||||||
|
// Highlight box
|
||||||
|
final Rectangle itemHighlightBox = new Rectangle(
|
||||||
|
textX + stringWidth + RECTANGLE_SIZE + 2,
|
||||||
|
textY - (stringHeight / 2) - descent,
|
||||||
|
RECTANGLE_SIZE,
|
||||||
|
stringHeight / 2);
|
||||||
|
|
||||||
|
plugin.getHighlightBoxes().put(itemHighlightBox, item.getName());
|
||||||
|
|
||||||
|
final Point mousePos = client.getMouseCanvasPosition();
|
||||||
|
boolean mouseInHiddenBox = itemHiddenBox.contains(mousePos.getX(), mousePos.getY());
|
||||||
|
boolean mouseInHighlightBox = itemHighlightBox.contains(mousePos.getX(), mousePos.getY());
|
||||||
|
|
||||||
|
// Draw hidden box
|
||||||
|
drawRectangle(graphics, itemHiddenBox, mouseInHiddenBox ? Color.RED : color, hidden, true);
|
||||||
|
|
||||||
|
// Draw highlight box
|
||||||
|
drawRectangle(graphics, itemHighlightBox, mouseInHighlightBox ? Color.GREEN : color, highlighted, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Color getCostColor(int cost)
|
Color getCostColor(int cost, boolean highlighted)
|
||||||
{
|
{
|
||||||
|
if (highlighted)
|
||||||
|
{
|
||||||
|
return config.highlightedColor();
|
||||||
|
}
|
||||||
|
|
||||||
// set the color according to rarity, if possible
|
// set the color according to rarity, if possible
|
||||||
if (cost >= INSANE_VALUE) // 10,000,000 gp
|
if (cost >= INSANE_VALUE) // 10,000,000 gp
|
||||||
{
|
{
|
||||||
return config.insaneValueColor();
|
return config.insaneValueColor();
|
||||||
}
|
}
|
||||||
else if (cost >= HIGH_VALUE) // 1,000,000 gp
|
|
||||||
|
if (cost >= HIGH_VALUE) // 1,000,000 gp
|
||||||
{
|
{
|
||||||
return config.highValueColor();
|
return config.highValueColor();
|
||||||
}
|
}
|
||||||
else if (cost >= MEDIUM_VALUE) // 100,000 gp
|
|
||||||
|
if (cost >= MEDIUM_VALUE) // 100,000 gp
|
||||||
{
|
{
|
||||||
return config.mediumValueColor();
|
return config.mediumValueColor();
|
||||||
}
|
}
|
||||||
else if (cost >= LOW_VALUE) // 20,000 gp
|
|
||||||
|
if (cost >= LOW_VALUE) // 20,000 gp
|
||||||
{
|
{
|
||||||
return config.lowValueColor();
|
return config.lowValueColor();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
return config.defaultColor();
|
||||||
return config.defaultColor();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawRectangle(Graphics2D graphics, Rectangle rect, Color color, boolean inList, boolean hiddenBox)
|
private void drawRectangle(Graphics2D graphics, Rectangle rect, Color color, boolean inList, boolean hiddenBox)
|
||||||
@@ -360,23 +255,23 @@ public class GroundItemsOverlay extends Overlay
|
|||||||
graphics.setColor(Color.WHITE);
|
graphics.setColor(Color.WHITE);
|
||||||
// Minus symbol
|
// Minus symbol
|
||||||
graphics.drawLine
|
graphics.drawLine
|
||||||
(
|
(
|
||||||
rect.x + 2,
|
rect.x + 2,
|
||||||
rect.y + (RECTANGLE_SIZE / 2),
|
rect.y + (RECTANGLE_SIZE / 2),
|
||||||
rect.x + RECTANGLE_SIZE - 2,
|
rect.x + RECTANGLE_SIZE - 2,
|
||||||
rect.y + (RECTANGLE_SIZE / 2)
|
rect.y + (RECTANGLE_SIZE / 2)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!hiddenBox)
|
if (!hiddenBox)
|
||||||
{
|
{
|
||||||
// Plus symbol
|
// Plus symbol
|
||||||
graphics.drawLine
|
graphics.drawLine
|
||||||
(
|
(
|
||||||
rect.x + (RECTANGLE_SIZE / 2),
|
rect.x + (RECTANGLE_SIZE / 2),
|
||||||
rect.y + 2,
|
rect.y + 2,
|
||||||
rect.x + (RECTANGLE_SIZE / 2),
|
rect.x + (RECTANGLE_SIZE / 2),
|
||||||
rect.y + RECTANGLE_SIZE - 2
|
rect.y + RECTANGLE_SIZE - 2
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,22 +38,33 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collector;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.runelite.api.Client;
|
import net.runelite.api.Client;
|
||||||
|
import net.runelite.api.GameState;
|
||||||
import net.runelite.api.Item;
|
import net.runelite.api.Item;
|
||||||
import net.runelite.api.ItemComposition;
|
import net.runelite.api.ItemComposition;
|
||||||
|
import net.runelite.api.ItemID;
|
||||||
import net.runelite.api.ItemLayer;
|
import net.runelite.api.ItemLayer;
|
||||||
import net.runelite.api.MenuAction;
|
import net.runelite.api.MenuAction;
|
||||||
import net.runelite.api.MenuEntry;
|
import net.runelite.api.MenuEntry;
|
||||||
import net.runelite.api.Node;
|
import net.runelite.api.Node;
|
||||||
|
import net.runelite.api.Player;
|
||||||
import net.runelite.api.Region;
|
import net.runelite.api.Region;
|
||||||
import net.runelite.api.Tile;
|
import net.runelite.api.Tile;
|
||||||
|
import net.runelite.api.coords.LocalPoint;
|
||||||
import net.runelite.api.events.ConfigChanged;
|
import net.runelite.api.events.ConfigChanged;
|
||||||
import net.runelite.api.events.FocusChanged;
|
import net.runelite.api.events.FocusChanged;
|
||||||
|
import net.runelite.api.events.GameStateChanged;
|
||||||
|
import net.runelite.api.events.GameTick;
|
||||||
import net.runelite.api.events.MenuEntryAdded;
|
import net.runelite.api.events.MenuEntryAdded;
|
||||||
import net.runelite.client.config.ConfigManager;
|
import net.runelite.client.config.ConfigManager;
|
||||||
import net.runelite.client.game.ItemManager;
|
import net.runelite.client.game.ItemManager;
|
||||||
@@ -67,8 +78,32 @@ import net.runelite.http.api.item.ItemPrice;
|
|||||||
@PluginDescriptor(
|
@PluginDescriptor(
|
||||||
name = "Ground Items"
|
name = "Ground Items"
|
||||||
)
|
)
|
||||||
|
@Slf4j
|
||||||
public class GroundItemsPlugin extends Plugin
|
public class GroundItemsPlugin extends Plugin
|
||||||
{
|
{
|
||||||
|
//Size of one region
|
||||||
|
private static final int REGION_SIZE = 104;
|
||||||
|
// The max distance in tiles between the player and the item.
|
||||||
|
private static final int MAX_RANGE = 18;
|
||||||
|
// Used when getting High Alchemy value - multiplied by general store price.
|
||||||
|
private static final float HIGH_ALCHEMY_CONSTANT = 0.6f;
|
||||||
|
// ItemID for coins
|
||||||
|
private static final int COINS = ItemID.COINS_995;
|
||||||
|
// Collects similar ground items
|
||||||
|
private static final Collector<GroundItem, ?, Map<GroundItem.GroundItemKey, GroundItem>> GROUND_ITEM_MAP_COLLECTOR = Collectors
|
||||||
|
.toMap
|
||||||
|
((item) -> new GroundItem.GroundItemKey(item.getItemId(), item.getLocation()), Function.identity(), ((a, b) ->
|
||||||
|
GroundItem.builder()
|
||||||
|
.index(b.getIndex())
|
||||||
|
.id(b.getId())
|
||||||
|
.itemId(b.getItemId())
|
||||||
|
.name(b.getName())
|
||||||
|
.location(b.getLocation())
|
||||||
|
.haPrice(a.getHaPrice() + b.getHaPrice())
|
||||||
|
.gePrice(a.getGePrice() + b.getGePrice())
|
||||||
|
.quantity(a.getQuantity() + b.getQuantity())
|
||||||
|
.build()));
|
||||||
|
|
||||||
@Getter(AccessLevel.PACKAGE)
|
@Getter(AccessLevel.PACKAGE)
|
||||||
private final Map<Rectangle, String> hiddenBoxes = new HashMap<>();
|
private final Map<Rectangle, String> hiddenBoxes = new HashMap<>();
|
||||||
|
|
||||||
@@ -94,9 +129,6 @@ public class GroundItemsPlugin extends Plugin
|
|||||||
@Inject
|
@Inject
|
||||||
private Client client;
|
private Client client;
|
||||||
|
|
||||||
@Inject
|
|
||||||
private ConfigManager configManager;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private ItemManager itemManager;
|
private ItemManager itemManager;
|
||||||
|
|
||||||
@@ -106,6 +138,9 @@ public class GroundItemsPlugin extends Plugin
|
|||||||
@Inject
|
@Inject
|
||||||
private GroundItemsOverlay overlay;
|
private GroundItemsOverlay overlay;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final List<GroundItem> collectedGroundItems = new ArrayList<>();
|
||||||
|
private final List<GroundItem> groundItems = new ArrayList<>();
|
||||||
private LoadingCache<String, Boolean> highlightedItems;
|
private LoadingCache<String, Boolean> highlightedItems;
|
||||||
private LoadingCache<String, Boolean> hiddenItems;
|
private LoadingCache<String, Boolean> hiddenItems;
|
||||||
|
|
||||||
@@ -125,7 +160,6 @@ public class GroundItemsPlugin extends Plugin
|
|||||||
protected void startUp()
|
protected void startUp()
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
mouseManager.registerMouseListener(inputListener);
|
mouseManager.registerMouseListener(inputListener);
|
||||||
keyManager.registerKeyListener(inputListener);
|
keyManager.registerKeyListener(inputListener);
|
||||||
}
|
}
|
||||||
@@ -135,6 +169,14 @@ public class GroundItemsPlugin extends Plugin
|
|||||||
{
|
{
|
||||||
mouseManager.unregisterMouseListener(inputListener);
|
mouseManager.unregisterMouseListener(inputListener);
|
||||||
keyManager.unregisterKeyListener(inputListener);
|
keyManager.unregisterKeyListener(inputListener);
|
||||||
|
groundItems.clear();
|
||||||
|
collectedGroundItems.clear();
|
||||||
|
highlightedItems.invalidateAll();
|
||||||
|
highlightedItems = null;
|
||||||
|
hiddenItems.invalidateAll();
|
||||||
|
hiddenItems = null;
|
||||||
|
hiddenItemList = null;
|
||||||
|
highlightedItemsList = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
@@ -146,6 +188,139 @@ public class GroundItemsPlugin extends Plugin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onGameStateChanged(final GameStateChanged event)
|
||||||
|
{
|
||||||
|
if (event.getGameState() == GameState.LOGGED_IN)
|
||||||
|
{
|
||||||
|
groundItems.clear();
|
||||||
|
collectedGroundItems.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onGameTick(final GameTick event)
|
||||||
|
{
|
||||||
|
final Player player = client.getLocalPlayer();
|
||||||
|
|
||||||
|
if (player == null || client.getViewportWidget() == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Region region = client.getRegion();
|
||||||
|
final Tile[][][] tiles = region.getTiles();
|
||||||
|
final int z = client.getPlane();
|
||||||
|
final LocalPoint from = player.getLocalLocation();
|
||||||
|
|
||||||
|
final int lowerX = Math.max(0, from.getRegionX() - MAX_RANGE);
|
||||||
|
final int lowerY = Math.max(0, from.getRegionY() - MAX_RANGE);
|
||||||
|
|
||||||
|
final int upperX = Math.min(from.getRegionX() + MAX_RANGE, REGION_SIZE - 1);
|
||||||
|
final int upperY = Math.min(from.getRegionY() + MAX_RANGE, REGION_SIZE - 1);
|
||||||
|
|
||||||
|
groundItems.clear();
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
for (int x = lowerX; x <= upperX; ++x)
|
||||||
|
{
|
||||||
|
for (int y = lowerY; y <= upperY; ++y)
|
||||||
|
{
|
||||||
|
Tile tile = tiles[z][x][y];
|
||||||
|
if (tile == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemLayer itemLayer = tile.getItemLayer();
|
||||||
|
if (itemLayer == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node current = itemLayer.getBottom();
|
||||||
|
|
||||||
|
// adds the items on the ground to the ArrayList to be drawn
|
||||||
|
while (current instanceof Item)
|
||||||
|
{
|
||||||
|
final Item item = (Item) current;
|
||||||
|
|
||||||
|
// Continue iteration
|
||||||
|
current = current.getNext();
|
||||||
|
|
||||||
|
// Build ground item
|
||||||
|
final GroundItem groundItem = buildGroundItem(tile, item);
|
||||||
|
|
||||||
|
if (groundItem != null)
|
||||||
|
{
|
||||||
|
groundItems.add(groundItem);
|
||||||
|
|
||||||
|
// Required for preserving the correct order later
|
||||||
|
groundItem.setIndex(index);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group ground items together and sort them properly
|
||||||
|
collectedGroundItems.clear();
|
||||||
|
groundItems.stream()
|
||||||
|
.collect(GROUND_ITEM_MAP_COLLECTOR)
|
||||||
|
.values()
|
||||||
|
.stream()
|
||||||
|
.sorted((left, right) -> Integer.compare(right.getIndex(), left.getIndex()))
|
||||||
|
.collect(Collectors.toCollection(() -> collectedGroundItems));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private GroundItem buildGroundItem(final Tile tile, final Item item)
|
||||||
|
{
|
||||||
|
// Collect the data for the item
|
||||||
|
final int itemId = item.getId();
|
||||||
|
final ItemComposition itemComposition = itemManager.getItemComposition(itemId);
|
||||||
|
final int realItemId = itemComposition.getNote() != -1 ? itemComposition.getLinkedNoteId() : itemId;
|
||||||
|
final int alchPrice = Math.round(itemComposition.getPrice() * HIGH_ALCHEMY_CONSTANT);
|
||||||
|
final String name = itemComposition.getName();
|
||||||
|
|
||||||
|
final boolean hidden = isHidden(name);
|
||||||
|
|
||||||
|
if (!isHotKeyPressed() && hidden)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean highlighted = isHighlighted(name);
|
||||||
|
|
||||||
|
if (config.showHighlightedOnly() && !isHotKeyPressed() && !highlighted)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final GroundItem groundItem = GroundItem.builder()
|
||||||
|
.id(itemId)
|
||||||
|
.location(tile.getWorldLocation())
|
||||||
|
.itemId(realItemId)
|
||||||
|
.quantity(item.getQuantity())
|
||||||
|
.name(itemComposition.getName())
|
||||||
|
.haPrice(alchPrice * item.getQuantity())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Set the correct item price
|
||||||
|
if (realItemId == COINS)
|
||||||
|
{
|
||||||
|
groundItem.setHaPrice(item.getQuantity());
|
||||||
|
groundItem.setGePrice(item.getQuantity());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
final ItemPrice itemPrice = itemManager.getItemPriceAsync(realItemId);
|
||||||
|
groundItem.setGePrice(itemPrice != null ? itemPrice.getPrice() * item.getQuantity() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return groundItem;
|
||||||
|
}
|
||||||
|
|
||||||
private void reset()
|
private void reset()
|
||||||
{
|
{
|
||||||
Splitter COMMA_SPLITTER = Splitter.on(Pattern.compile("\\s*,\\s*"));
|
Splitter COMMA_SPLITTER = Splitter.on(Pattern.compile("\\s*,\\s*"));
|
||||||
@@ -219,20 +394,9 @@ public class GroundItemsPlugin extends Plugin
|
|||||||
ItemPrice itemPrice = getItemPrice(itemComposition);
|
ItemPrice itemPrice = getItemPrice(itemComposition);
|
||||||
int price = itemPrice == null ? itemComposition.getPrice() : itemPrice.getPrice();
|
int price = itemPrice == null ? itemComposition.getPrice() : itemPrice.getPrice();
|
||||||
int cost = quantity * price;
|
int cost = quantity * price;
|
||||||
|
Color color = overlay.getCostColor(cost, isHighlighted(itemComposition.getName()));
|
||||||
|
|
||||||
Color color = null;
|
if (!color.equals(config.defaultColor()))
|
||||||
|
|
||||||
if (cost >= GroundItemsOverlay.LOW_VALUE)
|
|
||||||
{
|
|
||||||
color = overlay.getCostColor(cost);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isHighlighted(itemComposition.getName()))
|
|
||||||
{
|
|
||||||
color = config.highlightedColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (color != null)
|
|
||||||
{
|
{
|
||||||
String hexColor = Integer.toHexString(color.getRGB() & 0xFFFFFF);
|
String hexColor = Integer.toHexString(color.getRGB() & 0xFFFFFF);
|
||||||
String colTag = "<col=" + hexColor + ">";
|
String colTag = "<col=" + hexColor + ">";
|
||||||
|
|||||||
Reference in New Issue
Block a user