diff --git a/runelite-api/src/main/java/net/runelite/api/ItemComposition.java b/runelite-api/src/main/java/net/runelite/api/ItemComposition.java index 78bbed3e17..98ac7332be 100644 --- a/runelite-api/src/main/java/net/runelite/api/ItemComposition.java +++ b/runelite-api/src/main/java/net/runelite/api/ItemComposition.java @@ -104,6 +104,13 @@ public interface ItemComposition */ boolean isStackable(); + /** + * Returns whether or not the item can be traded to other players. + * + * @return true if tradeable, false otherwise + */ + boolean isTradeable(); + /** * Gets an array of possible right-click menu actions the item * has in a player inventory. diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItem.java index 4d69cd27b7..be06ab8163 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItem.java @@ -41,6 +41,8 @@ class GroundItem private int height; private int haPrice; private int gePrice; + private int offset; + private boolean tradeable; @Value static class GroundItemKey diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemInputListener.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemInputListener.java index 3319284b4a..12924221eb 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemInputListener.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemInputListener.java @@ -24,13 +24,11 @@ */ package net.runelite.client.plugins.grounditems; -import java.awt.Rectangle; +import java.awt.Point; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; -import java.util.Map; import javax.inject.Inject; -import net.runelite.api.Client; -import net.runelite.api.Point; +import javax.swing.SwingUtilities; import net.runelite.client.input.KeyListener; import net.runelite.client.input.MouseListener; @@ -38,9 +36,6 @@ public class GroundItemInputListener extends MouseListener implements KeyListene { private static final int HOTKEY = KeyEvent.VK_ALT; - @Inject - private Client client; - @Inject private GroundItemsPlugin plugin; @@ -65,39 +60,51 @@ public class GroundItemInputListener extends MouseListener implements KeyListene if (e.getKeyCode() == HOTKEY) { plugin.setHotKeyPressed(false); - plugin.getHighlightBoxes().clear(); - plugin.getHiddenBoxes().clear(); + plugin.setTextBoxBounds(null); + plugin.setHiddenBoxBounds(null); + plugin.setHighlightBoxBounds(null); } } @Override public MouseEvent mousePressed(MouseEvent e) { + final Point mousePos = e.getPoint(); + if (plugin.isHotKeyPressed()) { - // Check if left click - if (e.getButton() == MouseEvent.BUTTON1) + if (SwingUtilities.isLeftMouseButton(e)) { - Point mousePos = client.getMouseCanvasPosition(); - - for (Map.Entry entry : plugin.getHiddenBoxes().entrySet()) + // Process both click boxes for hidden and highlighted items + if (plugin.getHiddenBoxBounds() != null && plugin.getHiddenBoxBounds().getKey().contains(mousePos)) { - if (entry.getKey().contains(mousePos.getX(), mousePos.getY())) - { - plugin.updateList(entry.getValue(), true); - e.consume(); - return e; - } + plugin.updateList(plugin.getHiddenBoxBounds().getValue().getName(), true); + e.consume(); + return e; } - for (Map.Entry entry : plugin.getHighlightBoxes().entrySet()) + if (plugin.getHighlightBoxBounds() != null && plugin.getHighlightBoxBounds().getKey().contains(mousePos)) { - if (entry.getKey().contains(mousePos.getX(), mousePos.getY())) - { - plugin.updateList(entry.getValue(), false); - e.consume(); - return e; - } + plugin.updateList(plugin.getHighlightBoxBounds().getValue().getName(), false); + e.consume(); + return e; + } + + // There is one name click box for left click and one for right click + if (plugin.getTextBoxBounds() != null && plugin.getTextBoxBounds().getKey().contains(mousePos)) + { + plugin.updateList(plugin.getTextBoxBounds().getValue().getName(), false); + e.consume(); + return e; + } + } + else if (SwingUtilities.isRightMouseButton(e)) + { + if (plugin.getTextBoxBounds() != null && plugin.getTextBoxBounds().getKey().contains(mousePos)) + { + plugin.updateList(plugin.getTextBoxBounds().getValue().getName(), true); + e.consume(); + return e; } } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsConfig.java index 4c8ae5710d..e7e74bd2e2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsConfig.java @@ -25,13 +25,13 @@ package net.runelite.client.plugins.grounditems; +import java.awt.Color; import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; - -import java.awt.Color; import net.runelite.client.plugins.grounditems.config.ItemHighlightMode; import net.runelite.client.plugins.grounditems.config.MenuHighlightMode; +import net.runelite.client.plugins.grounditems.config.PriceDisplayMode; @ConfigGroup( keyName = "grounditems", @@ -44,7 +44,7 @@ public interface GroundItemsConfig extends Config keyName = "highlightedItems", name = "Highlighted Items", description = "Configures specifically highlighted ground items. Format: (item), (item)", - position = 1 + position = 0 ) default String getHighlightItems() { @@ -62,7 +62,7 @@ public interface GroundItemsConfig extends Config keyName = "hiddenItems", name = "Hidden Items", description = "Configures hidden ground items. Format: (item), (item)", - position = 2 + position = 1 ) default String getHiddenItems() { @@ -80,7 +80,7 @@ public interface GroundItemsConfig extends Config keyName = "showHighlightedOnly", name = "Show Highlighted items only", description = "Configures whether or not to draw items only on your highlighted list", - position = 3 + position = 2 ) default boolean showHighlightedOnly() { @@ -88,43 +88,43 @@ public interface GroundItemsConfig extends Config } @ConfigItem( - keyName = "showGEPrice", - name = "Show Grand Exchange Prices", - description = "Configures whether or not to draw GE prices alongside ground items", - position = 4 + keyName = "dontHideUntradeables", + name = "Do not hide untradeables", + description = "Configures whether or not untradeable items ignore hiding under settings", + position = 3 ) - default boolean showGEPrice() + default boolean dontHideUntradeables() { return true; } - @ConfigItem( - keyName = "showHAValue", - name = "Show High Alchemy Values", - description = "Configures whether or not to draw High Alchemy values alongside ground items", - position = 5 - ) - default boolean showHAValue() - { - return false; - } - @ConfigItem( keyName = "showMenuItemQuantities", name = "Show Menu Item Quantities", description = "Configures whether or not to show the item quantities in the menu", - position = 6 + position = 4 ) default boolean showMenuItemQuantities() { return true; } + @ConfigItem( + keyName = "priceDisplayMode", + name = "Price Display Mode", + description = "Configures what price types are shown alongside of ground item name", + position = 5 + ) + default PriceDisplayMode priceDisplayMode() + { + return PriceDisplayMode.BOTH; + } + @ConfigItem( keyName = "itemHighlightMode", name = "Item Highlight Mode", description = "Configures how ground items will be highlighted", - position = 7 + position = 6 ) default ItemHighlightMode itemHighlightMode() { @@ -135,7 +135,7 @@ public interface GroundItemsConfig extends Config keyName = "menuHighlightMode", name = "Menu Highlight Mode", description = "Configures what to highlight in right-click menu", - position = 8 + position = 7 ) default MenuHighlightMode menuHighlightMode() { @@ -143,23 +143,23 @@ public interface GroundItemsConfig extends Config } @ConfigItem( - keyName = "hideUnderGeValue", - name = "Hide < GE Value", - description = "Configures hidden ground items under GE value", - position = 9 + keyName = "highlightOverValue", + name = "Highlight > Value", + description = "Configures highlighted ground items over either GE or HA value", + position = 8 ) - default int getHideUnderGeValue() + default int getHighlightOverValue() { - return 0; + return 10000; } @ConfigItem( - keyName = "hideUnderHaValue", - name = "Hide < HA Value", - description = "Configures hidden ground items under High Alch value", - position = 10 + keyName = "hideUnderValue", + name = "Hide < Value", + description = "Configures hidden ground items under both GE and HA value", + position = 9 ) - default int getHideUnderHAValue() + default int getHideUnderValue() { return 0; } @@ -168,7 +168,7 @@ public interface GroundItemsConfig extends Config keyName = "defaultColor", name = "Default items color", description = "Configures the color for default, non-highlighted items", - position = 11 + position = 10 ) default Color defaultColor() { @@ -179,7 +179,7 @@ public interface GroundItemsConfig extends Config keyName = "highlightedColor", name = "Highlighted items color", description = "Configures the color for highlighted items", - position = 12 + position = 11 ) default Color highlightedColor() { @@ -190,7 +190,7 @@ public interface GroundItemsConfig extends Config keyName = "lowValueColor", name = "Low value items color", description = "Configures the color for low value items", - position = 13 + position = 12 ) default Color lowValueColor() { @@ -201,7 +201,7 @@ public interface GroundItemsConfig extends Config keyName = "lowValuePrice", name = "Low value price", description = "Configures the start price for low value items", - position = 14 + position = 13 ) default int lowValuePrice() { @@ -212,7 +212,7 @@ public interface GroundItemsConfig extends Config keyName = "mediumValueColor", name = "Medium value items color", description = "Configures the color for medium value items", - position = 15 + position = 14 ) default Color mediumValueColor() { @@ -223,7 +223,7 @@ public interface GroundItemsConfig extends Config keyName = "mediumValuePrice", name = "Medium value price", description = "Configures the start price for medium value items", - position = 16 + position = 15 ) default int mediumValuePrice() { @@ -234,7 +234,7 @@ public interface GroundItemsConfig extends Config keyName = "highValueColor", name = "High value items color", description = "Configures the color for high value items", - position = 17 + position = 16 ) default Color highValueColor() { @@ -245,7 +245,7 @@ public interface GroundItemsConfig extends Config keyName = "highValuePrice", name = "High value price", description = "Configures the start price for high value items", - position = 18 + position = 17 ) default int highValuePrice() { @@ -256,7 +256,7 @@ public interface GroundItemsConfig extends Config keyName = "insaneValueColor", name = "Insane value items color", description = "Configures the color for insane value items", - position = 19 + position = 18 ) default Color insaneValueColor() { @@ -267,7 +267,7 @@ public interface GroundItemsConfig extends Config keyName = "insaneValuePrice", name = "Insane value price", description = "Configures the start price for insane value items", - position = 20 + position = 19 ) default int insaneValuePrice() { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java index 9d5008b9cd..99b4110074 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java @@ -30,7 +30,10 @@ import java.awt.Dimension; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Rectangle; +import java.util.AbstractMap.SimpleEntry; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import javax.inject.Inject; import net.runelite.api.Client; @@ -41,9 +44,11 @@ import net.runelite.api.coords.LocalPoint; import net.runelite.api.coords.WorldPoint; import net.runelite.client.game.ItemManager; import static net.runelite.client.plugins.grounditems.config.ItemHighlightMode.MENU; +import net.runelite.client.plugins.grounditems.config.PriceDisplayMode; import net.runelite.client.ui.overlay.Overlay; import net.runelite.client.ui.overlay.OverlayLayer; import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.components.BackgroundComponent; import net.runelite.client.ui.overlay.components.TextComponent; import net.runelite.client.util.StackFormatter; import net.runelite.http.api.item.ItemPrice; @@ -66,6 +71,7 @@ public class GroundItemsOverlay extends Overlay private final GroundItemsPlugin plugin; private final GroundItemsConfig config; private final StringBuilder itemStringBuilder = new StringBuilder(); + private final BackgroundComponent backgroundComponent = new BackgroundComponent(); private final TextComponent textComponent = new TextComponent(); private final Map offsetMap = new HashMap<>(); private final ItemManager itemManager; @@ -101,8 +107,61 @@ public class GroundItemsOverlay extends Overlay offsetMap.clear(); final LocalPoint localLocation = player.getLocalLocation(); + final Point mousePos = client.getMouseCanvasPosition(); + final List groundItemList = new ArrayList<>(plugin.getCollectedGroundItems().values()); + GroundItem topGroundItem = null; - for (GroundItem item : plugin.getCollectedGroundItems().values()) + if (plugin.isHotKeyPressed()) + { + final java.awt.Point awtMousePos = new java.awt.Point(mousePos.getX(), mousePos.getY()); + GroundItem groundItem = null; + + for (GroundItem item : groundItemList) + { + item.setOffset(offsetMap.compute(item.getLocation(), (k, v) -> v != null ? v + 1 : 0)); + + if (groundItem != null) + { + continue; + } + + if (plugin.getTextBoxBounds() != null + && item.equals(plugin.getTextBoxBounds().getValue()) + && plugin.getTextBoxBounds().getKey().contains(awtMousePos)) + { + groundItem = item; + continue; + } + + if (plugin.getHiddenBoxBounds() != null + && item.equals(plugin.getHiddenBoxBounds().getValue()) + && plugin.getHiddenBoxBounds().getKey().contains(awtMousePos)) + { + groundItem = item; + continue; + } + + if (plugin.getHighlightBoxBounds() != null + && item.equals(plugin.getHighlightBoxBounds().getValue()) + && plugin.getHighlightBoxBounds().getKey().contains(awtMousePos)) + { + groundItem = item; + } + } + + if (groundItem != null) + { + groundItemList.remove(groundItem); + groundItemList.add(groundItem); + topGroundItem = groundItem; + } + } + + plugin.setTextBoxBounds(null); + plugin.setHiddenBoxBounds(null); + plugin.setHighlightBoxBounds(null); + + for (GroundItem item : groundItemList) { final LocalPoint groundPoint = LocalPoint.fromWorld(client, item.getLocation()); @@ -111,24 +170,6 @@ public class GroundItemsOverlay extends Overlay continue; } - final boolean highlighted = plugin.isHighlighted(item.getName()); - final boolean hidden = plugin.isHidden(item.getName()); - - if (!plugin.isHotKeyPressed()) - { - // Do not display hidden items - if (hidden) - { - continue; - } - - // Do not display non-highlighted items when only highlighted items should be shown - if (config.showHighlightedOnly() && !highlighted) - { - continue; - } - } - // Update GE price for item final ItemPrice itemPrice = itemManager.getItemPriceAsync(item.getItemId()); @@ -137,16 +178,25 @@ public class GroundItemsOverlay extends Overlay item.setGePrice(itemPrice.getPrice() * item.getQuantity()); } - // Do not display items that are under HA or GE price and are not highlighted - if (!plugin.isHotKeyPressed() && !highlighted - && ((item.getGePrice() > 0 && item.getGePrice() < config.getHideUnderGeValue()) - || item.getHaPrice() < config.getHideUnderHAValue())) + final Color highlighted = plugin.getHighlighted(item.getName(), item.getGePrice(), item.getHaPrice()); + final Color hidden = plugin.getHidden(item.getName(), item.getGePrice(), item.getHaPrice(), item.isTradeable()); + + if (highlighted == null && !plugin.isHotKeyPressed()) { - continue; + // Do not display hidden items + if (hidden != null) + { + continue; + } + + // Do not display non-highlighted items + if (config.showHighlightedOnly()) + { + continue; + } } - final Color color = getCostColor(item.getGePrice() > 0 ? item.getGePrice() : item.getHaPrice(), - highlighted, hidden); + final Color color = plugin.getItemColor(highlighted, hidden); itemStringBuilder.append(item.getName()); if (item.getQuantity() > 1) @@ -163,18 +213,35 @@ public class GroundItemsOverlay extends Overlay } } - if (config.showGEPrice() && item.getGePrice() > 0) + if (config.priceDisplayMode() == PriceDisplayMode.BOTH) { - itemStringBuilder.append(" (EX: ") - .append(StackFormatter.quantityToStackSize(item.getGePrice())) - .append(" gp)"); - } + if (item.getGePrice() > 0) + { + itemStringBuilder.append(" (EX: ") + .append(StackFormatter.quantityToStackSize(item.getGePrice())) + .append(" gp)"); + } - if (config.showHAValue() && item.getHaPrice() > 0) + if (item.getHaPrice() > 0) + { + itemStringBuilder.append(" (HA: ") + .append(StackFormatter.quantityToStackSize(item.getHaPrice())) + .append(" gp)"); + } + } + else if (config.priceDisplayMode() != PriceDisplayMode.OFF) { - itemStringBuilder.append(" (HA: ") - .append(StackFormatter.quantityToStackSize(item.getHaPrice())) - .append(" gp)"); + final int price = config.priceDisplayMode() == PriceDisplayMode.GE + ? item.getGePrice() + : item.getHaPrice(); + + if (price > 0) + { + itemStringBuilder + .append(" (") + .append(StackFormatter.quantityToStackSize(price)) + .append(" gp)"); + } } final String itemString = itemStringBuilder.toString(); @@ -191,89 +258,78 @@ public class GroundItemsOverlay extends Overlay continue; } - final int offset = offsetMap.compute(item.getLocation(), (k, v) -> v != null ? v + 1 : 0); + final int offset = plugin.isHotKeyPressed() + ? item.getOffset() + : 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(); - // Hidden box - final Rectangle itemHiddenBox = new Rectangle( - textX + stringWidth, - textY - (RECTANGLE_SIZE + stringHeight) / 2, - RECTANGLE_SIZE, - RECTANGLE_SIZE); + // Item bounds + int x = textX - 2; + int y = textY - stringHeight - 2; + int width = stringWidth + 4; + int height = stringHeight + 4; + final Rectangle itemBounds = new Rectangle(x, y, width, height); - plugin.getHiddenBoxes().put(itemHiddenBox, item.getName()); + // Hidden box + x += width + 2; + y = textY - (RECTANGLE_SIZE + stringHeight) / 2; + width = height = RECTANGLE_SIZE - 2; + final Rectangle itemHiddenBox = new Rectangle(x, y, width, height); // Highlight box - final Rectangle itemHighlightBox = new Rectangle( - textX + stringWidth + RECTANGLE_SIZE + 2, - textY - (RECTANGLE_SIZE + stringHeight) / 2, - RECTANGLE_SIZE, - RECTANGLE_SIZE); + x += width + 2; + final Rectangle itemHighlightBox = new Rectangle(x, y, width, height); - plugin.getHighlightBoxes().put(itemHighlightBox, item.getName()); - - final Point mousePos = client.getMouseCanvasPosition(); + boolean mouseInBox = itemBounds.contains(mousePos.getX(), mousePos.getY()); boolean mouseInHiddenBox = itemHiddenBox.contains(mousePos.getX(), mousePos.getY()); boolean mouseInHighlightBox = itemHighlightBox.contains(mousePos.getX(), mousePos.getY()); + if (mouseInBox) + { + plugin.setTextBoxBounds(new SimpleEntry<>(itemBounds, item)); + } + else if (mouseInHiddenBox) + { + plugin.setHiddenBoxBounds(new SimpleEntry<>(itemHiddenBox, item)); + + } + else if (mouseInHighlightBox) + { + plugin.setHighlightBoxBounds(new SimpleEntry<>(itemHighlightBox, item)); + } + + boolean topItem = topGroundItem == item; + + // Draw background if hovering + if (topItem && (mouseInBox || mouseInHiddenBox || mouseInHighlightBox)) + { + backgroundComponent.setRectangle(itemBounds); + backgroundComponent.render(graphics); + } + // Draw hidden box - drawRectangle(graphics, itemHiddenBox, mouseInHiddenBox ? Color.RED : color, hidden, true); + drawRectangle(graphics, itemHiddenBox, topItem && mouseInHiddenBox ? Color.RED : color, hidden != null, true); // Draw highlight box - drawRectangle(graphics, itemHighlightBox, mouseInHighlightBox ? Color.GREEN : color, highlighted, false); + drawRectangle(graphics, itemHighlightBox, topItem && mouseInHighlightBox ? Color.GREEN : color, highlighted != null, false); } + + textComponent.setText(itemString); + textComponent.setColor(color); + textComponent.setPosition(new java.awt.Point(textX, textY)); + textComponent.render(graphics); } return null; } - Color getCostColor(int cost, boolean highlighted, boolean hidden) - { - if (hidden) - { - return Color.GRAY; - } - - if (highlighted) - { - return config.highlightedColor(); - } - - // set the color according to rarity, if possible - if (cost >= config.insaneValuePrice()) - { - return config.insaneValueColor(); - } - - if (cost >= config.highValuePrice()) - { - return config.highValueColor(); - } - - if (cost >= config.mediumValuePrice()) - { - return config.mediumValueColor(); - } - - if (cost >= config.lowValuePrice()) - { - return config.lowValueColor(); - } - - return config.defaultColor(); - } - private void drawRectangle(Graphics2D graphics, Rectangle rect, Color color, boolean inList, boolean hiddenBox) { graphics.setColor(Color.BLACK); @@ -292,9 +348,9 @@ public class GroundItemsOverlay extends Overlay graphics.drawLine ( rect.x + 2, - rect.y + (RECTANGLE_SIZE / 2), - rect.x + RECTANGLE_SIZE - 2, - rect.y + (RECTANGLE_SIZE / 2) + rect.y + (rect.height / 2), + rect.x + rect.width - 2, + rect.y + (rect.height / 2) ); if (!hiddenBox) @@ -302,10 +358,10 @@ public class GroundItemsOverlay extends Overlay // Plus symbol graphics.drawLine ( - rect.x + (RECTANGLE_SIZE / 2), + rect.x + (rect.width / 2), rect.y + 2, - rect.x + (RECTANGLE_SIZE / 2), - rect.y + RECTANGLE_SIZE - 2 + rect.x + (rect.width / 2), + rect.y + rect.height - 2 ); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java index 8c3bd6d537..1e4513c5a9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java @@ -36,13 +36,14 @@ import java.awt.Color; import java.awt.Rectangle; import static java.lang.Boolean.TRUE; import java.util.ArrayList; -import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; import java.util.function.Function; -import java.util.regex.Pattern; import java.util.stream.Collector; import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -89,6 +90,12 @@ import net.runelite.http.api.item.ItemPrice; @Slf4j public class GroundItemsPlugin extends Plugin { + private static final Splitter COMMA_SPLITTER = Splitter + .on(",") + .omitEmptyStrings() + .trimResults(); + + private static final Joiner COMMA_JOINER = Joiner.on(",").skipNulls(); //Size of one region private static final int REGION_SIZE = 104; // The max distance in tiles between the player and the item. @@ -99,17 +106,23 @@ public class GroundItemsPlugin extends Plugin private static final int COINS = ItemID.COINS_995; @Getter(AccessLevel.PACKAGE) - private final Map hiddenBoxes = new HashMap<>(); + @Setter(AccessLevel.PACKAGE) + private Map.Entry textBoxBounds; @Getter(AccessLevel.PACKAGE) - private final Map highlightBoxes = new HashMap<>(); + @Setter(AccessLevel.PACKAGE) + private Map.Entry hiddenBoxBounds; + + @Getter(AccessLevel.PACKAGE) + @Setter(AccessLevel.PACKAGE) + private Map.Entry highlightBoxBounds; @Getter(AccessLevel.PACKAGE) @Setter(AccessLevel.PACKAGE) private boolean hotKeyPressed; - private List hiddenItemList = new ArrayList<>(); - private List highlightedItemsList = new ArrayList<>(); + private List hiddenItemList = new CopyOnWriteArrayList<>(); + private List highlightedItemsList = new CopyOnWriteArrayList<>(); private boolean dirty; @Inject @@ -136,6 +149,7 @@ public class GroundItemsPlugin extends Plugin @Getter private final Map collectedGroundItems = new LinkedHashMap<>(); private final List groundItems = new ArrayList<>(); + private final Map priceChecks = new LinkedHashMap<>(); private LoadingCache highlightedItems; private LoadingCache hiddenItems; @@ -293,6 +307,7 @@ public class GroundItemsPlugin extends Plugin .quantity(item.getQuantity()) .name(itemComposition.getName()) .haPrice(alchPrice * item.getQuantity()) + .tradeable(itemComposition.isTradeable()) .build(); @@ -308,13 +323,11 @@ public class GroundItemsPlugin extends Plugin private void reset() { - Splitter COMMA_SPLITTER = Splitter.on(Pattern.compile("\\s*,\\s*")); - // gets the hidden items from the text box in the config - hiddenItemList = COMMA_SPLITTER.splitToList(config.getHiddenItems().trim()); + hiddenItemList = COMMA_SPLITTER.splitToList(config.getHiddenItems()); // gets the highlighted items from the text box in the config - highlightedItemsList = COMMA_SPLITTER.splitToList(config.getHighlightItems().trim()); + highlightedItemsList = COMMA_SPLITTER.splitToList(config.getHighlightItems()); highlightedItems = CacheBuilder.newBuilder() .maximumSize(512L) @@ -327,18 +340,14 @@ public class GroundItemsPlugin extends Plugin .build(new WildcardMatchLoader(hiddenItemList)); dirty = true; - } - private ItemPrice getItemPrice(ItemComposition itemComposition) - { - if (itemComposition.getNote() != -1) - { - return itemManager.getItemPriceAsync(itemComposition.getLinkedNoteId()); - } - else - { - return itemManager.getItemPriceAsync(itemComposition.getId()); - } + // Cache colors + priceChecks.clear(); + priceChecks.put(config.insaneValuePrice(), config.insaneValueColor()); + priceChecks.put(config.highValuePrice(), config.highValueColor()); + priceChecks.put(config.mediumValuePrice(), config.mediumValueColor()); + priceChecks.put(config.lowValuePrice(), config.lowValueColor()); + priceChecks.put(config.getHighlightOverValue(), config.highlightedColor()); } @Subscribe @@ -349,16 +358,10 @@ public class GroundItemsPlugin extends Plugin && event.getType() == MenuAction.GROUND_ITEM_THIRD_OPTION.getId()) { int itemId = event.getIdentifier(); - ItemComposition itemComposition = client.getItemDefinition(itemId); - - if (isHidden(itemComposition.getName())) - { - return; - } - Region region = client.getRegion(); Tile tile = region.getTiles()[client.getPlane()][event.getActionParam0()][event.getActionParam1()]; ItemLayer itemLayer = tile.getItemLayer(); + if (itemLayer == null) { return; @@ -369,6 +372,7 @@ public class GroundItemsPlugin extends Plugin int quantity = 1; Node current = itemLayer.getBottom(); + while (current instanceof Item) { Item item = (Item) current; @@ -379,13 +383,17 @@ public class GroundItemsPlugin extends Plugin current = current.getNext(); } - ItemPrice itemPrice = getItemPrice(itemComposition); - int price = itemPrice == null ? (int)Math.floor(itemComposition.getPrice() * HIGH_ALCHEMY_CONSTANT) : itemPrice.getPrice(); - int cost = quantity * price; - Color color = overlay.getCostColor(cost, isHighlighted(itemComposition.getName()), - isHidden(itemComposition.getName())); + final ItemComposition itemComposition = itemManager.getItemComposition(itemId); + final int realItemId = itemComposition.getNote() != -1 ? itemComposition.getLinkedNoteId() : itemComposition.getId(); + final ItemPrice itemPrice = itemManager.getItemPriceAsync(realItemId); + final int price = itemPrice == null ? itemComposition.getPrice() : itemPrice.getPrice(); + final int haPrice = Math.round(itemComposition.getPrice() * HIGH_ALCHEMY_CONSTANT) * quantity; + final int gePrice = quantity * price; + final Color hidden = getHidden(itemComposition.getName(), haPrice, gePrice, itemComposition.isTradeable()); + final Color highlighted = getHighlighted(itemComposition.getName(), haPrice, gePrice); + final Color color = getItemColor(highlighted, hidden); - if (!color.equals(config.defaultColor())) + if (color != null && !color.equals(config.defaultColor())) { String hexColor = Integer.toHexString(color.getRGB() & 0xFFFFFF); String colTag = ""; @@ -414,34 +422,80 @@ public class GroundItemsPlugin extends Plugin void updateList(String item, boolean hiddenList) { - List items = new ArrayList<>((hiddenList) ? hiddenItemList : highlightedItemsList); + final Set hiddenItemSet = new HashSet<>(hiddenItemList); + final Set highlightedItemSet = new HashSet<>(highlightedItemsList); - items.removeIf(s -> s.isEmpty()); - if (!items.removeIf(s -> s.equalsIgnoreCase(item))) + if (hiddenList) + { + highlightedItemSet.removeIf(item::equalsIgnoreCase); + } + else + { + hiddenItemSet.removeIf(item::equalsIgnoreCase); + } + + final Set items = hiddenList ? hiddenItemSet : highlightedItemSet; + + if (!items.removeIf(item::equalsIgnoreCase)) { items.add(item); } - String newList = Joiner.on(", ").join(items); - // This triggers the config update which updates the list - if (hiddenList) - { - config.setHiddenItems(newList); - } - else - { - config.setHighlightedItem(newList); - } + config.setHiddenItems(COMMA_JOINER.join(hiddenItemSet)); + config.setHighlightedItem(COMMA_JOINER.join(highlightedItemSet)); } - public boolean isHighlighted(String item) + Color getHighlighted(String item, int gePrice, int haPrice) { - return TRUE.equals(highlightedItems.getUnchecked(item)); + if (TRUE.equals(highlightedItems.getUnchecked(item))) + { + return config.highlightedColor(); + } + + // Explicit hide takes priority over implicit highlight + if (TRUE.equals(hiddenItems.getUnchecked(item))) + { + return null; + } + + for (Map.Entry entry : priceChecks.entrySet()) + { + if (gePrice > entry.getKey() || haPrice > entry.getKey()) + { + return entry.getValue(); + } + } + + return null; } - public boolean isHidden(String item) + Color getHidden(String item, int gePrice, int haPrice, boolean isTradeable) { - return TRUE.equals(hiddenItems.getUnchecked(item)); + final boolean isExplicitHidden = TRUE.equals(hiddenItems.getUnchecked(item)); + final boolean isExplicitHighlight = TRUE.equals(highlightedItems.getUnchecked(item)); + final boolean canBeHidden = isTradeable || !config.dontHideUntradeables(); + final boolean underGe = gePrice < config.getHideUnderValue(); + final boolean underHa = haPrice < config.getHideUnderValue(); + + // Explicit highlight takes priority over implicit hide + return isExplicitHidden || (!isExplicitHighlight && canBeHidden && underGe && underHa) + ? Color.GRAY + : null; + } + + Color getItemColor(Color highlighted, Color hidden) + { + if (highlighted != null) + { + return highlighted; + } + + if (hidden != null) + { + return hidden; + } + + return config.defaultColor(); } @Subscribe diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/config/PriceDisplayMode.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/config/PriceDisplayMode.java new file mode 100644 index 0000000000..d98fecddeb --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/config/PriceDisplayMode.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018, Tomas Slusny + * 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.config; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum PriceDisplayMode +{ + HA("High Alchemy"), + GE("Grand Exchange"), + BOTH("Both"), + OFF("Off"); + + private final String name; + + @Override + public String toString() + { + return name; + } +} diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSItemComposition.java b/runescape-api/src/main/java/net/runelite/rs/api/RSItemComposition.java index 8e63e8e2ba..fcc649ca72 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSItemComposition.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSItemComposition.java @@ -66,6 +66,10 @@ public interface RSItemComposition extends ItemComposition @Override boolean isMembers(); + @Import("isTradable") + @Override + boolean isTradeable(); + /** * You probably want {@link #isStackable} *