diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerMapping.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerMapping.java new file mode 100644 index 0000000000..27b08ced63 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerMapping.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2020, Anthony + * 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.loottracker; + +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableMap; +import lombok.Getter; +import net.runelite.api.ItemComposition; +import net.runelite.api.ItemID; +import net.runelite.client.game.ItemManager; +import net.runelite.client.game.ItemStack; + +@Getter +public enum LootTrackerMapping +{ + CLUE_SCROLL_BEGINNER("Clue scroll (beginner)", ItemID.CLUE_SCROLL_BEGINNER), + CLUE_SCROLL_EASY("Clue scroll (easy)", ItemID.CLUE_SCROLL_EASY), + CLUE_SCROLL_MEDIUM("Clue scroll (medium)", ItemID.CLUE_SCROLL_MEDIUM), + CLUE_SCROLL_HARD("Clue scroll (hard)", ItemID.CLUE_SCROLL_HARD), + CLUE_SCROLL_ELITE("Clue scroll (elite)", ItemID.CLUE_SCROLL_ELITE), + CLUE_SCROLL_MASTER("Clue scroll (master)", ItemID.CLUE_SCROLL_MASTER); + + private final String name; + private final int baseId; + + LootTrackerMapping(String name, int baseId) + { + this.name = name; + this.baseId = baseId; + } + + private static final ImmutableMap MAPPINGS; + + static + { + ImmutableMap.Builder map = ImmutableMap.builder(); + for (LootTrackerMapping mapping : values()) + { + map.put(mapping.name, mapping); + } + MAPPINGS = map.build(); + } + + public static int map(int itemId, ItemManager itemManager) + { + ItemComposition itemComp = itemManager.getItemComposition(itemId); + if (itemComp == null || Strings.isNullOrEmpty(itemComp.getName())) + { + return itemId; + } + + if (!MAPPINGS.containsKey(itemComp.getName())) + { + return itemId; + } + + return MAPPINGS.get(itemComp.getName()).baseId; + } + + public static ItemStack map(ItemStack item, ItemManager itemManager) + { + int baseId = map(item.getId(), itemManager); + if (baseId == item.getId()) + { + return item; + } + + return new ItemStack(baseId, item.getQuantity(), item.getLocation()); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java index 2a4afec80a..27a9f82029 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java @@ -207,12 +207,14 @@ public class LootTrackerPlugin extends Plugin private LootTrackerClient lootTrackerClient; private final List queuedLoots = new ArrayList<>(); - private static Collection stack(Collection items) + @VisibleForTesting + Collection stack(Collection items) { final List list = new ArrayList<>(); - for (final ItemStack item : items) + for (ItemStack item : items) { + item = LootTrackerMapping.map(item, itemManager); int quantity = 0; for (final ItemStack i : list) { diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java index 2f30c315fc..235b8cf634 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java @@ -32,11 +32,14 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.concurrent.ScheduledExecutorService; import javax.inject.Inject; +import lombok.AllArgsConstructor; import net.runelite.api.ChatMessageType; import net.runelite.api.Client; +import net.runelite.api.ItemComposition; import net.runelite.api.ItemID; import net.runelite.api.IterableHashTable; import net.runelite.api.MessageNode; @@ -52,6 +55,7 @@ import net.runelite.http.api.item.ItemPrice; import net.runelite.http.api.loottracker.LootRecordType; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -64,6 +68,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; @RunWith(MockitoJUnitRunner.class) public class LootTrackerPluginTest @@ -145,6 +150,137 @@ public class LootTrackerPluginTest assertEquals(LootRecordType.EVENT, lootTrackerPlugin.lootRecordType); } + private static ItemStack is(int id, int q) + { + return new ItemStack(id, q, null); + } + + @Test + public void testClueStacks() + { + String beg = "Clue scroll (beginner)"; + String easy = "Clue scroll (easy)"; + String med = "Clue scroll (medium)"; + String hard = "Clue scroll (hard)"; + String elite = "Clue scroll (elite)"; + String master = "Clue scroll (master)"; + Map idsToName = ImmutableMap.builder() + .put(ItemID.CLUE_SCROLL_BEGINNER, beg) + .put(ItemID.CLUE_SCROLL_EASY, easy) + .put(ItemID.CLUE_SCROLL_EASY_2719, easy) + .put(ItemID.CLUE_SCROLL_EASY_23153, easy) + .put(ItemID.CLUE_SCROLL_MEDIUM, med) + .put(ItemID.CLUE_SCROLL_MEDIUM_3599, med) + .put(ItemID.CLUE_SCROLL_MEDIUM_2817, med) + .put(ItemID.CLUE_SCROLL_MEDIUM_3602, med) + .put(ItemID.CLUE_SCROLL_MEDIUM_12045, med) + .put(ItemID.CLUE_SCROLL_MEDIUM_12065, med) + .put(ItemID.CLUE_SCROLL_HARD, hard) + .put(ItemID.CLUE_SCROLL_HARD_3520, hard) + .put(ItemID.CLUE_SCROLL_HARD_3550, hard) + .put(ItemID.CLUE_SCROLL_HARD_23045, hard) + .put(ItemID.CLUE_SCROLL_ELITE, elite) + .put(ItemID.CLUE_SCROLL_ELITE_19783, elite) + .put(ItemID.CLUE_SCROLL_ELITE_21524, elite) + .put(ItemID.CLUE_SCROLL_ELITE_12096, elite) + .put(ItemID.CLUE_SCROLL_MASTER, master) + .put(ItemID.RUNE_PLATEBODY, "Rune platebody") + .put(ItemID.AMETHYST_ARROW, "Amethyst arrow") + .put(ItemID.GRACEFUL_HOOD_13579, "Graceful hood") + .put(ItemID.RUNITE_ORE, "Runite ore") + .put(ItemID.RUNITE_ORE + 1, "Runite ore") + .put(0, "null") + .build(); + + @AllArgsConstructor + class Case + { + private final List drops; + private final List expected; + } + + Case[] cases = { + new Case( + Arrays.asList( + is(ItemID.CLUE_SCROLL_MEDIUM, 1), + is(ItemID.CLUE_SCROLL_MEDIUM_3602, 1)), + Collections.singletonList( + is(ItemID.CLUE_SCROLL_MEDIUM, 2)) + ), + new Case( + Arrays.asList( + // graceful isn't a drop, but it is an item w/ variations that we're not tracking. + is(ItemID.GRACEFUL_HOOD_13579, 1), + is(ItemID.RUNE_PLATEBODY, 1), + is(ItemID.AMETHYST_ARROW, 125)), + Arrays.asList( + is(ItemID.GRACEFUL_HOOD_13579, 1), + is(ItemID.RUNE_PLATEBODY, 1), + is(ItemID.AMETHYST_ARROW, 125)) + ), + new Case( + Arrays.asList( + is(ItemID.CLUE_SCROLL_BEGINNER, 1), + is(ItemID.CLUE_SCROLL_ELITE_19783, 1), + is(ItemID.CLUE_SCROLL_MEDIUM_12045, 1), + is(ItemID.CLUE_SCROLL_MEDIUM_12065, 1), + is(ItemID.RUNITE_ORE, 25), + is(ItemID.RUNITE_ORE + 1, 10)), // noted rune ore + Arrays.asList( + is(ItemID.CLUE_SCROLL_BEGINNER, 1), + is(ItemID.CLUE_SCROLL_ELITE, 1), + is(ItemID.CLUE_SCROLL_MEDIUM, 2), + is(ItemID.RUNITE_ORE, 25), + is(ItemID.RUNITE_ORE + 1, 10)) // noted rune ore + ), + new Case( + Arrays.asList( + is(ItemID.CLUE_SCROLL_BEGINNER, 1), + is(ItemID.CLUE_SCROLL_BEGINNER, 1), + is(ItemID.CLUE_SCROLL_EASY, 1), + is(ItemID.CLUE_SCROLL_EASY_2719, 1), + is(ItemID.CLUE_SCROLL_EASY_23153, 1), + is(ItemID.CLUE_SCROLL_MEDIUM, 1), + is(ItemID.CLUE_SCROLL_MEDIUM_12065, 1), + is(ItemID.CLUE_SCROLL_MEDIUM_2817, 1), + is(ItemID.CLUE_SCROLL_HARD, 1), + is(ItemID.CLUE_SCROLL_HARD_3550, 1), + is(ItemID.CLUE_SCROLL_HARD_23045, 1), + is(ItemID.CLUE_SCROLL_ELITE, 1), + is(ItemID.CLUE_SCROLL_ELITE_21524, 1), + is(ItemID.CLUE_SCROLL_ELITE_12096, 1), + is(ItemID.CLUE_SCROLL_MASTER, 1), + is(ItemID.CLUE_SCROLL_MASTER, 1)), + Arrays.asList( + is(ItemID.CLUE_SCROLL_BEGINNER, 2), + is(ItemID.CLUE_SCROLL_EASY, 3), + is(ItemID.CLUE_SCROLL_MEDIUM, 3), + is(ItemID.CLUE_SCROLL_HARD, 3), + is(ItemID.CLUE_SCROLL_ELITE, 3), + is(ItemID.CLUE_SCROLL_MASTER, 2)) + ), + }; + + for (int i = 0; i < cases.length; i++) + { + Case tc = cases[i]; + when(itemManager.getItemComposition(anyInt())).thenAnswer((Answer) invocationOnMock -> + { + int itemId = invocationOnMock.getArgument(0); + if (!idsToName.containsKey(itemId)) + { + fail("item id not in names map: " + itemId); + } + + ItemComposition c = mock(ItemComposition.class); + when(c.getName()).thenReturn(idsToName.get(itemId)); + return c; + }); + + assertEquals("Test case: " + (i + 1), tc.expected, lootTrackerPlugin.stack(tc.drops)); + } + } + @Test public void testHerbiboarHerbSack() {