From d430c502464fc1fd31a6a90ab5d5bb8fac1ada9f Mon Sep 17 00:00:00 2001 From: Kyleeld <48519776+Kyleeld@users.noreply.github.com> Date: Thu, 15 Aug 2019 10:25:39 +0100 Subject: [PATCH] banktags: search for valued items (#1336) * banktags: search for valued items https://user-images.githubusercontent.com/22979513/62487848-7c476600-b788-11e9-9330-c423f6b906e2.gif FULL credit to @raiyni, great work! no horrible overlays. easy to use awesome plugin, lets hope upstream bring it in also! * ItemValueSearchTest: Fix test * banktags: set correct access level --- .../plugins/banktags/BankTagsPlugin.java | 105 ++++++++++++- .../plugins/banktags/ItemValueSearchTest.java | 147 ++++++++++++++++++ 2 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 runelite-client/src/test/java/net/runelite/client/plugins/banktags/ItemValueSearchTest.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/BankTagsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/BankTagsPlugin.java index b7bbb97a3c..d4ab4b0cb3 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/BankTagsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/BankTagsPlugin.java @@ -27,21 +27,28 @@ */ package net.runelite.client.plugins.banktags; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Multiset; import com.google.inject.Provides; import java.awt.event.KeyEvent; import java.awt.event.MouseWheelEvent; +import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.inject.Inject; import javax.inject.Singleton; +import static net.runelite.api.Constants.HIGH_ALCHEMY_MULTIPLIER; import net.runelite.api.Client; import net.runelite.api.InventoryID; import net.runelite.api.Item; import net.runelite.api.ItemDefinition; import net.runelite.api.ItemContainer; +import net.runelite.api.ItemID; import net.runelite.api.MenuAction; import net.runelite.api.MenuEntry; import net.runelite.api.VarClientInt; @@ -50,6 +57,7 @@ import net.runelite.api.events.ConfigChanged; import net.runelite.api.events.DraggingWidgetChanged; import net.runelite.api.events.FocusChanged; import net.runelite.api.events.GameTick; +import net.runelite.api.events.ItemContainerChanged; import net.runelite.api.events.MenuEntryAdded; import net.runelite.api.events.MenuOptionClicked; import net.runelite.api.events.ScriptCallbackEvent; @@ -75,6 +83,7 @@ import net.runelite.client.plugins.banktags.tabs.BankSearch; import net.runelite.client.plugins.banktags.tabs.TabInterface; import net.runelite.client.plugins.banktags.tabs.TabSprites; import net.runelite.client.plugins.cluescrolls.ClueScrollPlugin; +import net.runelite.client.util.StackFormatter; import net.runelite.client.util.Text; @PluginDescriptor( @@ -91,6 +100,7 @@ public class BankTagsPlugin extends Plugin implements MouseWheelListener, KeyLis private static final String EDIT_TAGS_MENU_OPTION = "Edit-tags"; public static final String ICON_SEARCH = "icon_"; public static final String VAR_TAG_SUFFIX = "*"; + private static final String NUMBER_REGEX = "[0-9]+(\\.[0-9]+)?[kmb]?"; private static final String SEARCH_BANK_INPUT_TEXT = "Show items whose names or tags contain the following text:
" + @@ -98,6 +108,10 @@ public class BankTagsPlugin extends Plugin implements MouseWheelListener, KeyLis private static final String SEARCH_BANK_INPUT_TEXT_FOUND = "Show items whose names or tags contain the following text: (%d found)
" + "(To show only tagged items, start your search with 'tag:')"; + private static final Pattern VALUE_SEARCH_PATTERN = Pattern.compile("^(?ge|ha|alch)?" + + " *(((?[<>=]|>=|<=) *(?" + NUMBER_REGEX + "))|" + + "((?" + NUMBER_REGEX + ") *- *(?" + NUMBER_REGEX + ")))$", Pattern.CASE_INSENSITIVE); + @Inject private ItemManager itemManager; @@ -137,6 +151,8 @@ public class BankTagsPlugin extends Plugin implements MouseWheelListener, KeyLis private boolean shiftPressed = false; private int nextRowIndex = 0; + @VisibleForTesting + Multiset itemQuantities = HashMultiset.create(); @Provides BankTagsConfig getConfig(ConfigManager configManager) @@ -166,6 +182,7 @@ public class BankTagsPlugin extends Plugin implements MouseWheelListener, KeyLis spriteManager.removeSpriteOverrides(TabSprites.values()); shiftPressed = false; + itemQuantities.clear(); } private void addSubscriptions() @@ -178,6 +195,7 @@ public class BankTagsPlugin extends Plugin implements MouseWheelListener, KeyLis eventBus.subscribe(DraggingWidgetChanged.class, this, this::onDraggingWidgetChanged); eventBus.subscribe(WidgetLoaded.class, this, this::onWidgetLoaded); eventBus.subscribe(FocusChanged.class, this, this::onFocusChanged); + eventBus.subscribe(ItemContainerChanged.class, this, this::onItemContainerChanged); } private boolean isSearching() @@ -222,7 +240,7 @@ public class BankTagsPlugin extends Plugin implements MouseWheelListener, KeyLis search = search.substring(TAG_SEARCH.length()).trim(); } - if (tagManager.findTag(itemId, search)) + if (tagManager.findTag(itemId, search) || valueSearch(itemId, search)) { if (!config.hidePlaceholders()) { @@ -409,6 +427,21 @@ public class BankTagsPlugin extends Plugin implements MouseWheelListener, KeyLis } } + private void onItemContainerChanged(ItemContainerChanged event) + { + if (event.getContainerId() == InventoryID.BANK.getId()) + { + itemQuantities.clear(); + for (Item item : event.getItemContainer().getItems()) + { + if (item.getId() != ItemID.BANK_FILLER) + { + itemQuantities.add(item.getId(), item.getQuantity()); + } + } + } + } + private void onConfigChanged(ConfigChanged configChanged) { if (configChanged.getGroup().equals("banktags") && configChanged.getKey().equals("useTabs")) @@ -479,4 +512,74 @@ public class BankTagsPlugin extends Plugin implements MouseWheelListener, KeyLis shiftPressed = false; } } + + @VisibleForTesting + boolean valueSearch(final int itemId, final String str) + { + final Matcher matcher = VALUE_SEARCH_PATTERN.matcher(str); + if (!matcher.matches()) + { + return false; + } + + final ItemDefinition itemComposition = itemManager.getItemDefinition(itemId); + long gePrice = (long) itemManager.getItemPrice(itemId) * (long) itemQuantities.count(itemId); + long haPrice = (long) (itemComposition.getPrice() * HIGH_ALCHEMY_MULTIPLIER) * (long) itemQuantities.count(itemId); + + long value = Math.max(gePrice, haPrice); + + final String mode = matcher.group("mode"); + if (mode != null) + { + value = mode.toLowerCase().equals("ge") ? gePrice : haPrice; + } + + final String op = matcher.group("op"); + if (op != null) + { + long compare; + try + { + compare = StackFormatter.stackSizeToQuantity(matcher.group("num")); + } + catch (ParseException e) + { + return false; + } + + switch (op) + { + case ">": + return value > compare; + case "<": + return value < compare; + case "=": + return value == compare; + case ">=": + return value >= compare; + case "<=": + return value <= compare; + } + } + + final String num1 = matcher.group("num1"); + final String num2 = matcher.group("num2"); + if (num1 != null && num2 != null) + { + long compare1, compare2; + try + { + compare1 = StackFormatter.stackSizeToQuantity(num1); + compare2 = StackFormatter.stackSizeToQuantity(num2); + } + catch (ParseException e) + { + return false; + } + + return compare1 <= value && compare2 >= value; + } + + return false; + } } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/banktags/ItemValueSearchTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/banktags/ItemValueSearchTest.java new file mode 100644 index 0000000000..01251cb9a4 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/banktags/ItemValueSearchTest.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2019, Ron Young + * 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.banktags; + +import com.google.inject.Guice; +import com.google.inject.testing.fieldbinder.Bind; +import com.google.inject.testing.fieldbinder.BoundFieldModule; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.ItemDefinition; +import net.runelite.api.ItemID; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.config.RuneLiteConfig; +import net.runelite.client.game.ItemManager; +import net.runelite.client.game.SpriteManager; +import net.runelite.client.game.chatbox.ChatboxPanelManager; +import net.runelite.client.input.KeyManager; +import net.runelite.client.input.MouseManager; +import net.runelite.client.plugins.banktags.tabs.BankSearch; +import net.runelite.client.plugins.banktags.tabs.TabInterface; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class ItemValueSearchTest +{ + @Mock + @Bind + private Client client; + + @Mock + @Bind + private ItemManager itemManager; + + @Inject + private BankTagsPlugin bankTagsPlugin; + + @Mock + @Bind + private ClientThread clientThread; + + @Mock + @Bind + private ChatboxPanelManager chatboxPanelManager; + + @Mock + @Bind + private MouseManager mouseManager; + + @Mock + @Bind + private BankTagsConfig config; + + @Mock + @Bind + private TagManager tagManager; + + @Mock + @Bind + private TabInterface tabInterface; + + @Mock + @Bind + private BankSearch bankSearch; + + @Mock + @Bind + private KeyManager keyManager; + + @Mock + @Bind + private SpriteManager spriteManager; + + @Mock + @Bind + private RuneLiteConfig runeLiteConfig; + + @Before + public void before() + { + Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this); + } + + @Test + public void testCalculate() + { + int itemId = ItemID.ABYSSAL_WHIP; + + bankTagsPlugin.itemQuantities.add(itemId, 30); + + ItemDefinition comp = mock(ItemDefinition.class); + + // 60k HA price * 30 = 1.8m + when(comp.getPrice()) + .thenReturn(100_000); + + // 400k GE Price * 30 = 12m + when(itemManager.getItemPrice(itemId)) + .thenReturn(400_000); + when(itemManager.getItemDefinition(itemId)) + .thenReturn(comp); + + assertTrue(bankTagsPlugin.valueSearch(itemId, ">500k")); + assertTrue(bankTagsPlugin.valueSearch(itemId, "< 5.5b")); + assertTrue(bankTagsPlugin.valueSearch(itemId, "500k - 20.6m")); + + assertTrue(bankTagsPlugin.valueSearch(itemId, "ha=1.8m")); + assertTrue(bankTagsPlugin.valueSearch(itemId, "ha 500k - 20.6m")); + assertTrue(bankTagsPlugin.valueSearch(itemId, "ha > 940k")); + + assertFalse(bankTagsPlugin.valueSearch(itemId, "<500k")); + assertFalse(bankTagsPlugin.valueSearch(itemId, "ha >2m")); + assertFalse(bankTagsPlugin.valueSearch(itemId, "ge > 0.02b")); + + assertFalse(bankTagsPlugin.valueSearch(itemId, "1000k")); + } +} \ No newline at end of file