diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 2faa6f92ad..23787cd7ab 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -25,7 +25,7 @@ object ProjectVersions { const val launcherVersion = "2.2.0" - const val rlVersion = "1.7.19" + const val rlVersion = "1.7.20-SNAPSHOT" const val openosrsVersion = "4.9.10" diff --git a/http-api/src/main/java/net/runelite/http/api/item/ItemStats.java b/http-api/src/main/java/net/runelite/http/api/item/ItemStats.java index 55a1bf76dd..5b86dd336d 100644 --- a/http-api/src/main/java/net/runelite/http/api/item/ItemStats.java +++ b/http-api/src/main/java/net/runelite/http/api/item/ItemStats.java @@ -30,7 +30,6 @@ import lombok.Value; @Value public class ItemStats { - private boolean quest; private boolean equipable; private double weight; @SerializedName("ge_limit") @@ -79,7 +78,7 @@ public class ItemStats newEquipment = equipment; } - return new ItemStats(quest, equipable, newWeight, 0, newEquipment); + return new ItemStats(equipable, newWeight, 0, newEquipment); } } diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java index 2a4248a432..bc261ad235 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java @@ -854,6 +854,7 @@ public class WidgetID static class Pvp { static final int KILLDEATH_RATIO = 26; + static final int WILDERNESS_SKULL_CONTAINER = 44; static final int SKULL_CONTAINER = 45; static final int SAFE_ZONE = 47; static final int WILDERNESS_LEVEL = 50; // this can also be the Deadman Mode "Protection" text diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java index 000290fbf6..616da55b1f 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java @@ -503,6 +503,7 @@ public enum WidgetInfo SPELL_ARCEUUS_HOME_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.ArceuusSpellBook.ARCEUUS_HOME_TELEPORT), SPELL_KOUREND_HOME_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.StandardSpellBook.KOUREND_HOME_TELEPORT), + PVP_WILDERNESS_SKULL_CONTAINER(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.WILDERNESS_SKULL_CONTAINER), PVP_SKULL_CONTAINER(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.SKULL_CONTAINER), PVP_WORLD_SAFE_ZONE(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.SAFE_ZONE), diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLite.java b/runelite-client/src/main/java/net/runelite/client/RuneLite.java index c96f644625..ef7de6c3b9 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -393,7 +393,7 @@ public class RuneLite if (!isOutdated) { // Add core overlays - WidgetOverlay.createOverlays(client).forEach(overlayManager::add); + WidgetOverlay.createOverlays(overlayManager, client).forEach(overlayManager::add); overlayManager.add(worldMapOverlay.get()); eventBus.register(worldMapOverlay.get()); overlayManager.add(tooltipOverlay.get()); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/interacthighlight/InteractHighlightOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/interacthighlight/InteractHighlightOverlay.java index 664d274f5a..a5dd9c95da 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/interacthighlight/InteractHighlightOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/interacthighlight/InteractHighlightOverlay.java @@ -59,7 +59,7 @@ class InteractHighlightOverlay extends Overlay this.modelOutlineRenderer = modelOutlineRenderer; setPosition(OverlayPosition.DYNAMIC); setLayer(OverlayLayer.ABOVE_SCENE); - setPriority(OverlayPriority.LOW); + setPriority(OverlayPriority.HIGH); } @Override diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatOverlay.java index ff3922fd92..4465c0b338 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatOverlay.java @@ -55,7 +55,7 @@ public class ItemStatOverlay extends Overlay { // Unarmed attack speed is 4 @VisibleForTesting - static final ItemStats UNARMED = new ItemStats(false, true, 0, 0, + static final ItemStats UNARMED = new ItemStats(true, 0, 0, ItemEquipmentStats.builder() .aspeed(4) .build()); 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 bdc4d738d7..634a1fcc77 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 @@ -147,6 +147,7 @@ public class LootTrackerPlugin extends Plugin // Chest loot handling private static final String CHEST_LOOTED_MESSAGE = "You find some treasure in the chest!"; + private static final Pattern ROGUES_CHEST_PATTERN = Pattern.compile("You find (a|some)([a-z\\s]*) inside."); private static final Pattern LARRAN_LOOTED_PATTERN = Pattern.compile("You have opened Larran's (big|small) chest .*"); // Used by Stone Chest, Isle of Souls chest, Dark Chest private static final String OTHER_CHEST_LOOTED_MESSAGE = "You steal some loot from the chest."; @@ -167,6 +168,7 @@ public class LootTrackerPlugin extends Plugin put(7323, "Grubby Chest"). put(8593, "Isle of Souls Chest"). put(7827, "Dark Chest"). + put(13117, "Rogues' Chest"). build(); // Shade chest loot handling @@ -646,7 +648,7 @@ public class LootTrackerPlugin extends Plugin if (message.equals(CHEST_LOOTED_MESSAGE) || message.equals(OTHER_CHEST_LOOTED_MESSAGE) || message.equals(DORGESH_KAAN_CHEST_LOOTED_MESSAGE) || message.startsWith(GRUBBY_CHEST_LOOTED_MESSAGE) - || LARRAN_LOOTED_PATTERN.matcher(message).matches()) + || LARRAN_LOOTED_PATTERN.matcher(message).matches() || ROGUES_CHEST_PATTERN.matcher(message).matches()) { final int regionID = client.getLocalPlayer().getWorldLocation().getRegionID(); if (!CHEST_EVENT_TYPES.containsKey(regionID)) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsPlugin.java index 95f102267c..581ae7191f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsPlugin.java @@ -27,6 +27,7 @@ package net.runelite.client.plugins.npchighlight; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableSet; +import com.google.inject.Binder; import com.google.inject.Provides; import java.awt.Color; import java.time.Instant; @@ -38,6 +39,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Predicate; import javax.inject.Inject; import lombok.AccessLevel; import lombok.Getter; @@ -77,7 +79,7 @@ import net.runelite.client.util.WildcardMatcher; tags = {"highlight", "minimap", "npcs", "overlay", "respawn", "tags"} ) @Slf4j -public class NpcIndicatorsPlugin extends Plugin +public class NpcIndicatorsPlugin extends Plugin implements NpcIndicatorsService { private static final int MAX_ACTOR_VIEW_RANGE = 15; @@ -173,12 +175,20 @@ public class NpcIndicatorsPlugin extends Plugin */ private boolean skipNextSpawnCheck = false; + private final List> higlightPredicates = new ArrayList<>(); + @Provides NpcIndicatorsConfig provideConfig(ConfigManager configManager) { return configManager.getConfig(NpcIndicatorsConfig.class); } + @Override + public void configure(Binder binder) + { + binder.bind(NpcIndicatorsService.class).toInstance(this); + } + @Override protected void startUp() throws Exception { @@ -187,7 +197,7 @@ public class NpcIndicatorsPlugin extends Plugin clientThread.invoke(() -> { skipNextSpawnCheck = true; - rebuildAllNpcs(); + rebuild(); }); } @@ -230,7 +240,7 @@ public class NpcIndicatorsPlugin extends Plugin return; } - clientThread.invoke(this::rebuildAllNpcs); + clientThread.invoke(this::rebuild); } @Subscribe @@ -394,7 +404,23 @@ public class NpcIndicatorsPlugin extends Plugin memorizeNpc(npc); spawnedNpcsThisTick.add(npc); } + return; } + + for (Predicate predicate : higlightPredicates) + { + if (predicate.test(npc)) + { + highlightedNpcs.add(npc); + if (!client.isInInstancedRegion()) + { + memorizeNpc(npc); + spawnedNpcsThisTick.add(npc); + } + return; + } + } + } @Subscribe @@ -534,8 +560,8 @@ public class NpcIndicatorsPlugin extends Plugin return Text.fromCSV(configNpcs); } - @VisibleForTesting - void rebuildAllNpcs() + @Override + public void rebuild() { highlights = getHighlights(); highlightedNpcs.clear(); @@ -548,6 +574,7 @@ public class NpcIndicatorsPlugin extends Plugin return; } + outer: for (NPC npc : client.getNpcs()) { final String npcName = npc.getName(); @@ -573,6 +600,19 @@ public class NpcIndicatorsPlugin extends Plugin continue; } + for (Predicate predicate : higlightPredicates) + { + if (predicate.test(npc)) + { + if (!client.isInInstancedRegion()) + { + memorizeNpc(npc); + } + highlightedNpcs.add(npc); + continue outer; + } + } + // NPC is not highlighted memorizedNpcs.remove(npc.getIndex()); } @@ -680,4 +720,16 @@ public class NpcIndicatorsPlugin extends Plugin despawnedNpcsThisTick.clear(); teleportGraphicsObjectSpawnedThisTick.clear(); } + + @Override + public void registerHighlighter(Predicate p) + { + higlightPredicates.add(p); + } + + @Override + public void unregisterHighlighter(Predicate p) + { + higlightPredicates.remove(p); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsService.java b/runelite-client/src/main/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsService.java new file mode 100644 index 0000000000..43a3b91694 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsService.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021, Adam + * 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.npchighlight; + +import java.util.function.Predicate; +import net.runelite.api.NPC; + +public interface NpcIndicatorsService +{ + void registerHighlighter(Predicate p); + void unregisterHighlighter(Predicate p); + void rebuild(); +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java index d16f96fa58..258ea51b17 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java @@ -25,8 +25,6 @@ */ package net.runelite.client.plugins.slayer; -import java.awt.Color; -import net.runelite.client.config.Alpha; import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; @@ -101,18 +99,6 @@ public interface SlayerConfig extends Config return false; } - @Alpha - @ConfigItem( - position = 6, - keyName = "targetColor", - name = "Target Color", - description = "Color of the highlighted targets" - ) - default Color getTargetColor() - { - return Color.RED; - } - @ConfigItem( position = 7, keyName = "weaknessPrompt", diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java index 8cc18aa550..729debeb25 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java @@ -39,9 +39,11 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.inject.Inject; +import javax.inject.Named; import joptsimple.internal.Strings; import lombok.AccessLevel; import lombok.Getter; @@ -60,6 +62,7 @@ import static net.runelite.api.Skill.SLAYER; import net.runelite.api.coords.WorldPoint; import net.runelite.api.events.ActorDeath; import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.CommandExecuted; import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.GameTick; import net.runelite.api.events.HitsplatApplied; @@ -81,18 +84,23 @@ import net.runelite.client.events.ChatInput; import net.runelite.client.events.ConfigChanged; import net.runelite.client.game.ItemManager; import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDependency; import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.npchighlight.NpcIndicatorsPlugin; +import net.runelite.client.plugins.npchighlight.NpcIndicatorsService; import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import net.runelite.client.util.ColorUtil; import net.runelite.client.util.Text; import net.runelite.http.api.chat.ChatClient; +import org.apache.commons.lang3.ArrayUtils; @PluginDescriptor( name = "Slayer", description = "Show additional slayer task related information", tags = {"combat", "notifications", "overlay", "tasks"} ) +@PluginDependency(NpcIndicatorsPlugin.class) @Slf4j public class SlayerPlugin extends Plugin { @@ -151,15 +159,9 @@ public class SlayerPlugin extends Plugin @Inject private ClientThread clientThread; - @Inject - private TargetClickboxOverlay targetClickboxOverlay; - @Inject private TargetWeaknessOverlay targetWeaknessOverlay; - @Inject - private TargetMinimapOverlay targetMinimapOverlay; - @Inject private ChatMessageManager chatMessageManager; @@ -172,8 +174,15 @@ public class SlayerPlugin extends Plugin @Inject private ChatClient chatClient; + @Inject + private NpcIndicatorsService npcIndicatorsService; + @Getter(AccessLevel.PACKAGE) - private List highlightedTargets = new ArrayList<>(); + private final List targets = new ArrayList<>(); + + @Inject + @Named("developerMode") + boolean developerMode; private final Set taggedNpcs = new HashSet<>(); private int taggedNpcsDiedPrevTick; @@ -201,13 +210,16 @@ public class SlayerPlugin extends Plugin private boolean loginFlag; private final List targetNames = new ArrayList<>(); + public final Predicate isTarget = (n) -> config.highlightTargets() && targets.contains(n); + @Override protected void startUp() throws Exception { + chatCommandManager.registerCommandAsync(TASK_COMMAND_STRING, this::taskLookup, this::taskSubmit); + npcIndicatorsService.registerHighlighter(isTarget); + overlayManager.add(overlay); - overlayManager.add(targetClickboxOverlay); overlayManager.add(targetWeaknessOverlay); - overlayManager.add(targetMinimapOverlay); if (client.getGameState() == GameState.LOGGED_IN) { @@ -224,23 +236,21 @@ public class SlayerPlugin extends Plugin getStringProfileConfig(SlayerConfig.TASK_LOC_KEY), false)); } } - - chatCommandManager.registerCommandAsync(TASK_COMMAND_STRING, this::taskLookup, this::taskSubmit); } @Override protected void shutDown() throws Exception { + chatCommandManager.unregisterCommand(TASK_COMMAND_STRING); + npcIndicatorsService.unregisterHighlighter(isTarget); + npcIndicatorsService.rebuild(); + overlayManager.remove(overlay); - overlayManager.remove(targetClickboxOverlay); overlayManager.remove(targetWeaknessOverlay); - overlayManager.remove(targetMinimapOverlay); removeCounter(); - highlightedTargets.clear(); + targets.clear(); taggedNpcs.clear(); cachedXp = -1; - - chatCommandManager.unregisterCommand(TASK_COMMAND_STRING); } @Provides @@ -260,7 +270,7 @@ public class SlayerPlugin extends Plugin taskName = ""; amount = 0; loginFlag = true; - highlightedTargets.clear(); + targets.clear(); taggedNpcs.clear(); break; case LOGGED_IN: @@ -279,6 +289,16 @@ public class SlayerPlugin extends Plugin } } + @Subscribe + public void onCommandExecuted(CommandExecuted commandExecuted) + { + if (developerMode && commandExecuted.getCommand().equals("task")) + { + setTask(commandExecuted.getArguments()[0], 42, 42); + log.debug("Set task to {}", commandExecuted.getArguments()[0]); + } + } + @VisibleForTesting int getIntProfileConfig(String key) { @@ -313,13 +333,16 @@ public class SlayerPlugin extends Plugin setProfileConfig(SlayerConfig.TASK_LOC_KEY, taskLocation); } - @Subscribe + @Subscribe( + // Run prior to npc indicators plugin so targets is populated before the isTarget predicate is checked + priority = 1 + ) public void onNpcSpawned(NpcSpawned npcSpawned) { NPC npc = npcSpawned.getNpc(); if (isTarget(npc)) { - highlightedTargets.add(npc); + targets.add(npc); } } @@ -328,7 +351,7 @@ public class SlayerPlugin extends Plugin { NPC npc = npcDespawned.getNpc(); taggedNpcs.remove(npc); - highlightedTargets.remove(npc); + targets.remove(npc); } @Subscribe @@ -541,7 +564,7 @@ public class SlayerPlugin extends Plugin { Actor actor = hitsplatApplied.getActor(); Hitsplat hitsplat = hitsplatApplied.getHitsplat(); - if (hitsplat.getHitsplatType() == Hitsplat.HitsplatType.DAMAGE_ME && highlightedTargets.contains(actor)) + if (hitsplat.getHitsplatType() == Hitsplat.HitsplatType.DAMAGE_ME && targets.contains(actor)) { // If the actor is in highlightedTargets it must be an NPC and also a task assignment taggedNpcs.add((NPC) actor); @@ -562,18 +585,25 @@ public class SlayerPlugin extends Plugin @Subscribe private void onConfigChanged(ConfigChanged event) { - if (!event.getGroup().equals(SlayerConfig.GROUP_NAME) || !event.getKey().equals("infobox")) + if (!event.getGroup().equals(SlayerConfig.GROUP_NAME)) { return; } - if (config.showInfobox()) + if (event.getKey().equals("infobox")) { - clientThread.invoke(this::addCounter); + if (config.showInfobox()) + { + clientThread.invoke(this::addCounter); + } + else + { + removeCounter(); + } } else { - removeCounter(); + npcIndicatorsService.rebuild(); } } @@ -619,27 +649,25 @@ public class SlayerPlugin extends Plugin return false; } - String name = npc.getName(); - if (name == null) + final NPCComposition composition = npc.getTransformedComposition(); + if (composition == null) { return false; } - name = name.toLowerCase(); + final String name = composition.getName() + .replace('\u00A0', ' ') + .toLowerCase(); for (String target : targetNames) { if (name.contains(target)) { - NPCComposition composition = npc.getTransformedComposition(); - - if (composition != null) + if (ArrayUtils.contains(composition.getActions(), "Attack") + // Pick action is for zygomite-fungi + || ArrayUtils.contains(composition.getActions(), "Pick")) { - List actions = Arrays.asList(composition.getActions()); - if (actions.contains("Attack") || actions.contains("Pick")) //Pick action is for zygomite-fungi - { - return true; - } + return true; } } } @@ -662,13 +690,13 @@ public class SlayerPlugin extends Plugin private void rebuildTargetList() { - highlightedTargets.clear(); + targets.clear(); for (NPC npc : client.getNpcs()) { if (isTarget(npc)) { - highlightedTargets.add(npc); + targets.add(npc); } } } @@ -701,6 +729,7 @@ public class SlayerPlugin extends Plugin Task task = Task.getTask(name); rebuildTargetNames(task); rebuildTargetList(); + npcIndicatorsService.rebuild(); } private void addCounter() diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetClickboxOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetClickboxOverlay.java deleted file mode 100644 index 441f1c6d85..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetClickboxOverlay.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2018, James Swindle - * Copyright (c) 2018, Adam - * Copyright (c) 2018, Shaun Dreclin - * 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.slayer; - -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics2D; -import java.awt.Shape; -import java.util.List; -import javax.inject.Inject; -import net.runelite.api.NPC; -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.util.ColorUtil; - -public class TargetClickboxOverlay extends Overlay -{ - private final SlayerConfig config; - private final SlayerPlugin plugin; - - @Inject - TargetClickboxOverlay(SlayerConfig config, SlayerPlugin plugin) - { - this.config = config; - this.plugin = plugin; - setPosition(OverlayPosition.DYNAMIC); - setLayer(OverlayLayer.ABOVE_SCENE); - } - - @Override - public Dimension render(Graphics2D graphics) - { - if (!config.highlightTargets()) - { - return null; - } - - List targets = plugin.getHighlightedTargets(); - for (NPC target : targets) - { - renderTargetOverlay(graphics, target, config.getTargetColor()); - } - - return null; - } - - private void renderTargetOverlay(Graphics2D graphics, NPC actor, Color color) - { - Shape objectClickbox = actor.getConvexHull(); - if (objectClickbox != null) - { - graphics.setColor(color); - graphics.setStroke(new BasicStroke(2)); - graphics.draw(objectClickbox); - graphics.setColor(ColorUtil.colorWithAlpha(color, color.getAlpha() / 12)); - graphics.fill(objectClickbox); - } - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetMinimapOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetMinimapOverlay.java deleted file mode 100644 index ea12fde38a..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetMinimapOverlay.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2018, James Swindle - * Copyright (c) 2018, Adam - * Copyright (c) 2018, Shaun Dreclin - * 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.slayer; - -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics2D; -import java.util.List; -import javax.inject.Inject; -import net.runelite.api.NPC; -import net.runelite.api.Point; -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.OverlayUtil; - -public class TargetMinimapOverlay extends Overlay -{ - private final SlayerConfig config; - private final SlayerPlugin plugin; - - @Inject - TargetMinimapOverlay(SlayerConfig config, SlayerPlugin plugin) - { - this.config = config; - this.plugin = plugin; - setPosition(OverlayPosition.DYNAMIC); - setLayer(OverlayLayer.ABOVE_WIDGETS); - } - - @Override - public Dimension render(Graphics2D graphics) - { - if (!config.highlightTargets()) - { - return null; - } - - List targets = plugin.getHighlightedTargets(); - for (NPC target : targets) - { - renderTargetOverlay(graphics, target, config.getTargetColor()); - } - - return null; - } - - private void renderTargetOverlay(Graphics2D graphics, NPC actor, Color color) - { - Point minimapLocation = actor.getMinimapLocation(); - if (minimapLocation != null) - { - OverlayUtil.renderMinimapLocation(graphics, minimapLocation, color); - } - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetWeaknessOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetWeaknessOverlay.java index 91f331d06b..cc20cab485 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetWeaknessOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetWeaknessOverlay.java @@ -64,7 +64,7 @@ class TargetWeaknessOverlay extends Overlay @Override public Dimension render(Graphics2D graphics) { - final List targets = plugin.getHighlightedTargets(); + final List targets = plugin.getTargets(); if (targets.isEmpty() || !config.weaknessPrompt()) { diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/Overlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/Overlay.java index f946e62741..7bf9672c87 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/Overlay.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/Overlay.java @@ -128,4 +128,11 @@ public abstract class Overlay implements LayoutableRenderableEntity { return null; } + + public void reset() + { + setPreferredPosition(null); + setPreferredSize(null); + setPreferredLocation(null); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayManager.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayManager.java index 6e3c770b37..21cb834ca1 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayManager.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayManager.java @@ -289,9 +289,7 @@ public class OverlayManager */ public synchronized void resetOverlay(final Overlay overlay) { - overlay.setPreferredPosition(null); - overlay.setPreferredSize(null); - overlay.setPreferredLocation(null); + overlay.reset(); saveOverlay(overlay); } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetOverlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetOverlay.java index d470bbb242..67d20cf8bd 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetOverlay.java @@ -30,20 +30,23 @@ import java.awt.Rectangle; import java.util.Arrays; import java.util.Collection; import java.util.Objects; +import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; import net.runelite.api.Varbits; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; +@Slf4j public class WidgetOverlay extends Overlay { - public static Collection createOverlays(final Client client) + public static Collection createOverlays(final OverlayManager overlayManager, final Client client) { return Arrays.asList( new WidgetOverlay(client, WidgetInfo.RESIZABLE_MINIMAP_WIDGET, OverlayPosition.CANVAS_TOP_RIGHT), new WidgetOverlay(client, WidgetInfo.RESIZABLE_MINIMAP_STONES_WIDGET, OverlayPosition.CANVAS_TOP_RIGHT), - new WidgetOverlay(client, WidgetInfo.FOSSIL_ISLAND_OXYGENBAR, OverlayPosition.TOP_CENTER), - new XpTrackerWidgetOverlay(client, WidgetInfo.EXPERIENCE_TRACKER_WIDGET, OverlayPosition.TOP_RIGHT), + // The client forces the oxygen bar below the xp tracker, so set its priority lower + new WidgetOverlay(client, WidgetInfo.FOSSIL_ISLAND_OXYGENBAR, OverlayPosition.TOP_CENTER, OverlayPriority.HIGH), + new XpTrackerWidgetOverlay(overlayManager, client, WidgetInfo.EXPERIENCE_TRACKER_WIDGET, OverlayPosition.TOP_RIGHT), new WidgetOverlay(client, WidgetInfo.RAIDS_POINTS_INFOBOX, OverlayPosition.TOP_RIGHT), new WidgetOverlay(client, WidgetInfo.TOB_PARTY_INTERFACE, OverlayPosition.TOP_LEFT), new WidgetOverlay(client, WidgetInfo.TOB_PARTY_STATS, OverlayPosition.TOP_LEFT), @@ -61,7 +64,8 @@ public class WidgetOverlay extends Overlay new WidgetOverlay(client, WidgetInfo.LMS_KDA, OverlayPosition.TOP_RIGHT), new WidgetOverlay(client, WidgetInfo.GAUNTLET_TIMER_CONTAINER, OverlayPosition.TOP_LEFT), new WidgetOverlay(client, WidgetInfo.HALLOWED_SEPULCHRE_TIMER_CONTAINER, OverlayPosition.TOP_LEFT), - new WidgetOverlay(client, WidgetInfo.HEALTH_OVERLAY_BAR, OverlayPosition.TOP_CENTER), + // The client forces the health overlay bar below the xp tracker, so set its priority lower + new WidgetOverlay(client, WidgetInfo.HEALTH_OVERLAY_BAR, OverlayPosition.TOP_CENTER, OverlayPriority.HIGH), new WidgetOverlay(client, WidgetInfo.TOB_HEALTH_BAR, OverlayPosition.TOP_CENTER), new WidgetOverlay(client, WidgetInfo.NIGHTMARE_PILLAR_HEALTH, OverlayPosition.TOP_LEFT), new WidgetOverlay(client, WidgetInfo.VOLCANIC_MINE_VENTS_INFOBOX_GROUP, OverlayPosition.BOTTOM_RIGHT), @@ -71,19 +75,26 @@ public class WidgetOverlay extends Overlay new WidgetOverlay(client, WidgetInfo.MULTICOMBAT_RESIZEABLE_CLASSIC, OverlayPosition.CANVAS_TOP_RIGHT), new WidgetOverlay(client, WidgetInfo.TEMPOROSS_STATUS_INDICATOR, OverlayPosition.TOP_LEFT), new WidgetOverlay(client, WidgetInfo.BA_HEAL_TEAMMATES, OverlayPosition.BOTTOM_LEFT), - new WidgetOverlay(client, WidgetInfo.BA_TEAM, OverlayPosition.TOP_RIGHT) + new WidgetOverlay(client, WidgetInfo.BA_TEAM, OverlayPosition.TOP_RIGHT), + new WidgetOverlay(client, WidgetInfo.PVP_WILDERNESS_SKULL_CONTAINER, OverlayPosition.DETACHED) ); } protected final Client client; private final WidgetInfo widgetInfo; private final Rectangle parentBounds = new Rectangle(); + private boolean revalidate; private WidgetOverlay(final Client client, final WidgetInfo widgetInfo, final OverlayPosition overlayPosition) + { + this(client, widgetInfo, overlayPosition, OverlayPriority.HIGHEST); + } + + private WidgetOverlay(final Client client, final WidgetInfo widgetInfo, final OverlayPosition overlayPosition, final OverlayPriority overlayPriority) { this.client = client; this.widgetInfo = widgetInfo; - setPriority(OverlayPriority.HIGHEST); + setPriority(overlayPriority); setLayer(OverlayLayer.UNDER_WIDGETS); setPosition(overlayPosition); // It's almost possible to drawAfterInterface(widgetInfo.getGroupId()) here, but that fires @@ -107,10 +118,34 @@ public class WidgetOverlay extends Overlay return null; } + assert widget != null; + final Rectangle bounds = getBounds(); - // The widget relative pos is relative to the parent - widget.setRelativeX(bounds.x - parent.x); - widget.setRelativeY(bounds.y - parent.y); + // OverlayRenderer sets the overlay bounds to the preferred location if one is set prior to calling render() + // for detached overlays. + if (getPosition() != OverlayPosition.DETACHED || getPreferredLocation() != null) + { + // The widget relative pos is relative to the parent + widget.setRelativeX(bounds.x - parent.x); + widget.setRelativeY(bounds.y - parent.y); + } + else + { + if (revalidate) + { + revalidate = false; + log.debug("Revalidating {}", widgetInfo); + // Revalidate the widget to reposition it back to its normal location after an overlay reset + widget.revalidate(); + } + + // Update the overlay bounds to the widget bounds so the drag overlay renders correctly. + // Note OverlayManager uses original bounds reference to render managing mode and for + // onMouseOver, so update the existing bounds vs. replacing the reference. + Rectangle widgetBounds = widget.getBounds(); + bounds.setBounds(widgetBounds.x, widgetBounds.y, widgetBounds.width, widgetBounds.height); + } + return new Dimension(widget.getWidth(), widget.getHeight()); } @@ -152,11 +187,22 @@ public class WidgetOverlay extends Overlay return getParentBounds(widget); } + @Override + public void reset() + { + super.reset(); + // Revalidate must be called on the client thread, so defer til next frame + revalidate = true; + } + private static class XpTrackerWidgetOverlay extends WidgetOverlay { - private XpTrackerWidgetOverlay(Client client, WidgetInfo widgetInfo, OverlayPosition overlayPosition) + private final OverlayManager overlayManager; + + private XpTrackerWidgetOverlay(OverlayManager overlayManager, Client client, WidgetInfo widgetInfo, OverlayPosition overlayPosition) { super(client, widgetInfo, overlayPosition); + this.overlayManager = overlayManager; } /** @@ -188,7 +234,13 @@ public class WidgetOverlay extends Overlay position = OverlayPosition.TOP_LEFT; break; } - setPosition(position); + + if (position != super.getPosition()) + { + log.debug("Xp tracker moved position"); + setPosition(position); + overlayManager.rebuildOverlayLayers(); + } return position; } } diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/skill_herblore.json b/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/skill_herblore.json index 6e3c4c088b..d91f14eccf 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/skill_herblore.json +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/skillcalculator/skill_herblore.json @@ -192,6 +192,12 @@ "name": "Super Strength (3)", "xp": 125 }, + { + "level": 57, + "icon": 9022, + "name": "Magic Essence Potion (3)", + "xp": 130 + }, { "level": 59, "icon": 3000, diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/PluginManagerTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/PluginManagerTest.java new file mode 100644 index 0000000000..50692a915a --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/PluginManagerTest.java @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2016-2017, Adam + * 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; + +import com.google.common.reflect.ClassPath; +import com.google.common.reflect.ClassPath.ClassInfo; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.grapher.graphviz.GraphvizGrapher; +import com.google.inject.grapher.graphviz.GraphvizModule; +import com.google.inject.testing.fieldbinder.Bind; +import com.google.inject.testing.fieldbinder.BoundFieldModule; +import com.google.inject.util.Modules; +import java.applet.Applet; +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import net.runelite.api.Client; +import net.runelite.client.RuneLite; +import net.runelite.client.RuneLiteModule; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigItem; +import net.runelite.client.eventbus.EventBus; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import static org.junit.Assert.assertEquals; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import static org.mockito.ArgumentMatchers.any; +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 PluginManagerTest +{ + private static final String PLUGIN_PACKAGE = "net.runelite.client.plugins"; + + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + + @Mock + @Bind + public Applet applet; + + @Mock + @Bind + public Client client; + + private Set> pluginClasses; + private Set> configClasses; + + @Before + public void before() throws IOException + { + OkHttpClient okHttpClient = mock(OkHttpClient.class); + when(okHttpClient.newCall(any(Request.class))) + .thenThrow(new RuntimeException("in plugin manager test")); + + Injector injector = Guice.createInjector(Modules + .override(new RuneLiteModule(okHttpClient, () -> null, true, false, + RuneLite.DEFAULT_SESSION_FILE, + RuneLite.DEFAULT_CONFIG_FILE)) + .with(BoundFieldModule.of(this))); + + RuneLite.setInjector(injector); + + // Find plugins and configs we expect to have + pluginClasses = new HashSet<>(); + configClasses = new HashSet<>(); + Set classes = ClassPath.from(getClass().getClassLoader()).getTopLevelClassesRecursive(PLUGIN_PACKAGE); + for (ClassInfo classInfo : classes) + { + Class clazz = classInfo.load(); + PluginDescriptor pluginDescriptor = clazz.getAnnotation(PluginDescriptor.class); + if (pluginDescriptor != null) + { + pluginClasses.add(clazz); + continue; + } + + if (Config.class.isAssignableFrom(clazz)) + { + configClasses.add(clazz); + } + } + } + + @Test + public void testLoadPlugins() throws Exception + { + PluginManager pluginManager = new PluginManager(false, false, null, null, null, null); + pluginManager.setOutdated(true); + pluginManager.loadCorePlugins(); + Collection plugins = pluginManager.getPlugins(); + long expected = pluginClasses.stream() + .map(cl -> cl.getAnnotation(PluginDescriptor.class)) + .filter(Objects::nonNull) + .filter(PluginDescriptor::loadWhenOutdated) + .count(); + assertEquals(expected, plugins.size()); + + pluginManager = new PluginManager(false, false, null, null, null, null); + pluginManager.loadCorePlugins(); + plugins = pluginManager.getPlugins(); + + // Check that the plugins register with the eventbus without errors + EventBus eventBus = new EventBus(); + plugins.forEach(eventBus::register); + + expected = pluginClasses.stream() + .map(cl -> cl.getAnnotation(PluginDescriptor.class)) + .filter(Objects::nonNull) + .filter(pd -> !pd.developerPlugin()) + .count(); + assertEquals(expected, plugins.size()); + } + + @Test + public void dumpGraph() throws Exception + { + PluginManager pluginManager = new PluginManager(true, false, null, null, null, null); + pluginManager.loadCorePlugins(); + + Injector graphvizInjector = Guice.createInjector(new GraphvizModule()); + GraphvizGrapher graphvizGrapher = graphvizInjector.getInstance(GraphvizGrapher.class); + + File dotFolder = folder.newFolder(); + try (PrintWriter out = new PrintWriter(new File(dotFolder, "runelite.dot"), "UTF-8")) + { + graphvizGrapher.setOut(out); + graphvizGrapher.setRankdir("TB"); + graphvizGrapher.graph(RuneLite.getInjector()); + } + + for (Plugin p : pluginManager.getPlugins()) + { + try (PrintWriter out = new PrintWriter(new File(dotFolder, p.getName() + ".dot"), "UTF-8")) + { + graphvizGrapher.setOut(out); + graphvizGrapher.setRankdir("TB"); + graphvizGrapher.graph(p.getInjector()); + } + } + } + + @Test + public void ensureNoDuplicateConfigKeyNames() + { + for (final Class clazz : configClasses) + { + final Set configKeyNames = new HashSet<>(); + + for (final Method method : clazz.getMethods()) + { + if (!method.isDefault()) + { + continue; + } + + final ConfigItem annotation = method.getAnnotation(ConfigItem.class); + + if (annotation == null) + { + continue; + } + + final String configKeyName = annotation.keyName(); + + if (configKeyNames.contains(configKeyName)) + { + throw new IllegalArgumentException("keyName " + configKeyName + " is duplicated in " + clazz); + } + + configKeyNames.add(configKeyName); + } + } + } + +} diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/itemstats/ItemStatOverlayTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/itemstats/ItemStatOverlayTest.java new file mode 100644 index 0000000000..f9bc39ba80 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/itemstats/ItemStatOverlayTest.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2020 Jordan + * 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.itemstats; + +import com.google.inject.Guice; +import com.google.inject.Inject; +import com.google.inject.testing.fieldbinder.Bind; +import com.google.inject.testing.fieldbinder.BoundFieldModule; +import java.awt.Color; +import net.runelite.api.Client; +import net.runelite.api.EquipmentInventorySlot; +import net.runelite.api.InventoryID; +import net.runelite.api.ItemContainer; +import net.runelite.client.game.ItemManager; +import net.runelite.client.util.Text; +import net.runelite.http.api.item.ItemEquipmentStats; +import net.runelite.http.api.item.ItemStats; +import org.apache.commons.lang3.StringUtils; +import static org.junit.Assert.assertEquals; +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 ItemStatOverlayTest +{ + // Weapon definitions + private static final ItemStats ABYSSAL_DAGGER = new ItemStats(true, 0.453, 8, + ItemEquipmentStats.builder() + .slot(EquipmentInventorySlot.WEAPON.getSlotIdx()) + .isTwoHanded(false) + .astab(75) + .aslash(40) + .acrush(-4) + .amagic(1) + .dmagic(1) + .str(75) + .aspeed(4) + .build()); + private static final ItemStats KATANA = new ItemStats(true, 0, 8, + ItemEquipmentStats.builder() + .slot(EquipmentInventorySlot.WEAPON.getSlotIdx()) + .isTwoHanded(true) + .astab(7) + .aslash(45) + .dstab(3) + .dslash(7) + .dcrush(7) + .drange(-3) + .str(40) + .aspeed(4) + .build()); + private static final ItemStats BLOWPIPE = new ItemStats(true, 0, 0, + ItemEquipmentStats.builder() + .slot(EquipmentInventorySlot.WEAPON.getSlotIdx()) + .isTwoHanded(true) + .arange(60) + .rstr(40) + .aspeed(3) + .build()); + private static final ItemStats HEAVY_BALLISTA = new ItemStats(true, 4, 8, + ItemEquipmentStats.builder() + .slot(EquipmentInventorySlot.WEAPON.getSlotIdx()) + .isTwoHanded(true) + .arange(110) + .aspeed(7) + .build()); + + @Inject + ItemStatOverlay overlay; + + @Mock + @Bind + Client client; + + @Mock + @Bind + ItemStatConfig config; + + @Mock + @Bind + ItemManager itemManager; + + @Before + public void before() + { + Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this); + + when(config.colorBetterUncapped()).thenReturn(new Color(0)); + when(config.colorWorse()).thenReturn(new Color(0)); + } + + @Test + public void testUnarmedAttackSpeed() + { + assertEquals(ItemStatOverlay.UNARMED.getEquipment().getAspeed(), ABYSSAL_DAGGER.getEquipment().getAspeed()); + assertEquals(ItemStatOverlay.UNARMED.getEquipment().getAspeed(), KATANA.getEquipment().getAspeed()); + assertEquals(-1, BLOWPIPE.getEquipment().getAspeed() - ItemStatOverlay.UNARMED.getEquipment().getAspeed()); + assertEquals(3, HEAVY_BALLISTA.getEquipment().getAspeed() - ItemStatOverlay.UNARMED.getEquipment().getAspeed()); + } + + @Test + public void testBuildStatBonusString() + { + // Empty equipment (fully unarmed) + final ItemContainer equipment = mock(ItemContainer.class); + when(client.getItemContainer(InventoryID.EQUIPMENT)).thenReturn(equipment); + + String tooltip; + String sanitizedTooltip; + + tooltip = overlay.buildStatBonusString(ABYSSAL_DAGGER); + sanitizedTooltip = Text.sanitizeMultilineText(tooltip); + assertTrue(sanitizedTooltip.contains("Stab: +75")); + assertTrue(sanitizedTooltip.contains("Slash: +40")); + assertTrue(sanitizedTooltip.contains("Crush: -4")); + assertEquals(2, StringUtils.countMatches(sanitizedTooltip, "Magic: +1")); // Attack and defense + assertTrue(sanitizedTooltip.contains("Melee Str: +75")); + assertFalse(sanitizedTooltip.contains("Speed:")); + + tooltip = overlay.buildStatBonusString(KATANA); + sanitizedTooltip = Text.sanitizeMultilineText(tooltip); + assertTrue(sanitizedTooltip.contains("Stab: +7")); + assertTrue(sanitizedTooltip.contains("Slash: +45")); + assertTrue(sanitizedTooltip.contains("Stab: +3")); // Defense + assertTrue(sanitizedTooltip.contains("Slash: +7")); // Defense + assertTrue(sanitizedTooltip.contains("Crush: +7")); // Defense + assertTrue(sanitizedTooltip.contains("Range: -3")); // Defense + assertTrue(sanitizedTooltip.contains("Melee Str: +40")); + assertFalse(sanitizedTooltip.contains("Speed:")); + + tooltip = overlay.buildStatBonusString(BLOWPIPE); + sanitizedTooltip = Text.sanitizeMultilineText(tooltip); + assertTrue(sanitizedTooltip.contains("Range: +60")); + assertTrue(sanitizedTooltip.contains("Range Str: +40")); + assertTrue(sanitizedTooltip.contains("Speed: -1")); + assertFalse(sanitizedTooltip.contains("Stab:")); + + tooltip = overlay.buildStatBonusString(HEAVY_BALLISTA); + sanitizedTooltip = Text.sanitizeMultilineText(tooltip); + assertTrue(sanitizedTooltip.contains("Range: +110")); + assertTrue(sanitizedTooltip.contains("Speed: +3")); + assertFalse(sanitizedTooltip.contains("Stab:")); + } +} diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsPluginTest.java index c629f441a0..107f5aeb59 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsPluginTest.java @@ -101,7 +101,7 @@ public class NpcIndicatorsPluginTest when(npcIndicatorsConfig.getNpcToHighlight()).thenReturn("goblin"); when(npcIndicatorsConfig.deadNpcMenuColor()).thenReturn(Color.RED); - npcIndicatorsPlugin.rebuildAllNpcs(); + npcIndicatorsPlugin.rebuild(); NPC npc = mock(NPC.class); when(npc.getName()).thenReturn("Goblin"); @@ -127,7 +127,7 @@ public class NpcIndicatorsPluginTest when(npcIndicatorsConfig.highlightMenuNames()).thenReturn(true); when(npcIndicatorsConfig.getHighlightColor()).thenReturn(Color.BLUE); - npcIndicatorsPlugin.rebuildAllNpcs(); + npcIndicatorsPlugin.rebuild(); NPC npc = mock(NPC.class); when(npc.getName()).thenReturn("Goblin"); @@ -149,7 +149,7 @@ public class NpcIndicatorsPluginTest { when(npcIndicatorsConfig.getNpcToHighlight()).thenReturn("Joseph"); - npcIndicatorsPlugin.rebuildAllNpcs(); + npcIndicatorsPlugin.rebuild(); NPC npc = mock(NPC.class); when(npc.getName()).thenReturn("Joseph"); @@ -168,7 +168,7 @@ public class NpcIndicatorsPluginTest { when(npcIndicatorsConfig.getNpcToHighlight()).thenReturn("Werewolf"); - npcIndicatorsPlugin.rebuildAllNpcs(); + npcIndicatorsPlugin.rebuild(); NPC npc = mock(NPC.class); when(npc.getName()).thenReturn("Joseph"); diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/prayer/PrayerPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/prayer/PrayerPluginTest.java new file mode 100644 index 0000000000..db2581bc90 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/prayer/PrayerPluginTest.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2020, Landy Chan + * 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.prayer; + +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.EquipmentInventorySlot; +import net.runelite.api.InventoryID; +import net.runelite.api.Item; +import net.runelite.api.ItemContainer; +import net.runelite.api.Prayer; +import net.runelite.api.Skill; +import net.runelite.api.events.ItemContainerChanged; +import net.runelite.client.game.ItemManager; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.ui.overlay.infobox.InfoBoxManager; +import net.runelite.http.api.item.ItemEquipmentStats; +import net.runelite.http.api.item.ItemStats; +import static org.junit.Assert.assertEquals; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +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 PrayerPluginTest +{ + private static final ItemStats HIGH_PRAYER_BONUS_WEAPON = new ItemStats(false, 0, 0, + ItemEquipmentStats.builder() + .slot(EquipmentInventorySlot.WEAPON.getSlotIdx()) + .prayer(50) + .build()); + + @Inject + private PrayerPlugin prayerPlugin; + + @Mock + @Bind + private Client client; + + @Mock + @Bind + private PrayerConfig config; + + @Mock + @Bind + private OverlayManager overlayManager; + + @Mock + @Bind + private InfoBoxManager infoBoxManager; + + @Mock + @Bind + private ItemManager itemManager; + + @Before + public void before() + { + Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this); + } + + @Test + public void testGetEstimatedTimeRemainingOverOneHour() + { + ItemContainer itemContainer = mock(ItemContainer.class); + when(itemContainer.getItems()).thenReturn(new Item[]{new Item(-1, 1)}); + when(itemManager.getItemStats(anyInt(), anyBoolean())).thenReturn(HIGH_PRAYER_BONUS_WEAPON); + + when(client.isPrayerActive(Prayer.PRESERVE)).thenReturn(true); + when(client.getBoostedSkillLevel(Skill.PRAYER)).thenReturn(99); + when(client.getItemContainer(InventoryID.EQUIPMENT)).thenReturn(itemContainer); + + prayerPlugin.onItemContainerChanged(new ItemContainerChanged(InventoryID.EQUIPMENT.getId(), itemContainer)); + + assertEquals("1:19:12", prayerPlugin.getEstimatedTimeRemaining(false)); + } + + @Test + public void testGetEstimatedTimeRemainingUnderOneHour() + { + ItemContainer itemContainer = mock(ItemContainer.class); + when(itemContainer.getItems()).thenReturn(new Item[]{}); + + when(client.isPrayerActive(Prayer.PRESERVE)).thenReturn(true); + when(client.getBoostedSkillLevel(Skill.PRAYER)).thenReturn(99); + when(client.getItemContainer(InventoryID.EQUIPMENT)).thenReturn(itemContainer); + + prayerPlugin.onItemContainerChanged(new ItemContainerChanged(InventoryID.EQUIPMENT.getId(), itemContainer)); + + assertEquals("29:42", prayerPlugin.getEstimatedTimeRemaining(false)); + } + + @Test + public void testGetEstimatedTimeRemainingFormatForOrbUnderOneHour() + { + ItemContainer itemContainer = mock(ItemContainer.class); + when(itemContainer.getItems()).thenReturn(new Item[]{}); + + when(client.isPrayerActive(Prayer.PRESERVE)).thenReturn(true); + when(client.getBoostedSkillLevel(Skill.PRAYER)).thenReturn(99); + when(client.getItemContainer(InventoryID.EQUIPMENT)).thenReturn(itemContainer); + + prayerPlugin.onItemContainerChanged(new ItemContainerChanged(InventoryID.EQUIPMENT.getId(), itemContainer)); + + assertEquals("29m", prayerPlugin.getEstimatedTimeRemaining(true)); + } + + @Test + public void testGetEstimatedTimeRemainingFormatForOrbOverOneHour() + { + ItemContainer itemContainer = mock(ItemContainer.class); + when(itemContainer.getItems()).thenReturn(new Item[]{new Item(-1, 1)}); + when(itemManager.getItemStats(anyInt(), anyBoolean())).thenReturn(HIGH_PRAYER_BONUS_WEAPON); + + when(client.isPrayerActive(Prayer.PRESERVE)).thenReturn(true); + when(client.getBoostedSkillLevel(Skill.PRAYER)).thenReturn(99); + when(client.getItemContainer(InventoryID.EQUIPMENT)).thenReturn(itemContainer); + + prayerPlugin.onItemContainerChanged(new ItemContainerChanged(InventoryID.EQUIPMENT.getId(), itemContainer)); + + assertEquals("79m", prayerPlugin.getEstimatedTimeRemaining(true)); + } +} diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/slayer/SlayerPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/slayer/SlayerPluginTest.java index 6785721349..d28aefd7c2 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/slayer/SlayerPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/slayer/SlayerPluginTest.java @@ -31,6 +31,7 @@ import java.io.IOException; import java.util.Arrays; import java.util.concurrent.ScheduledExecutorService; import javax.inject.Inject; +import javax.inject.Named; import net.runelite.api.ChatMessageType; import static net.runelite.api.ChatMessageType.GAMEMESSAGE; import net.runelite.api.Client; @@ -55,6 +56,7 @@ import net.runelite.client.chat.ChatCommandManager; import net.runelite.client.chat.ChatMessageManager; import net.runelite.client.config.ConfigManager; import net.runelite.client.game.ItemManager; +import net.runelite.client.plugins.npchighlight.NpcIndicatorsService; import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import net.runelite.http.api.chat.ChatClient; @@ -168,6 +170,14 @@ public class SlayerPluginTest @Bind ChatClient chatClient; + @Bind + @Named("developerMode") + boolean developerMode; + + @Mock + @Bind + NpcIndicatorsService npcIndicatorsService; + @Inject SlayerPlugin slayerPlugin; @@ -871,14 +881,13 @@ public class SlayerPluginTest slayerPlugin.onStatChanged(statChanged); NPCComposition npcComposition = mock(NPCComposition.class); + when(npcComposition.getName()).thenReturn("Suqah"); when(npcComposition.getActions()).thenReturn(new String[]{"Attack"}); NPC npc1 = mock(NPC.class); - when(npc1.getName()).thenReturn("Suqah"); when(npc1.getTransformedComposition()).thenReturn(npcComposition); NPC npc2 = mock(NPC.class); - when(npc2.getName()).thenReturn("Suqah"); when(npc2.getTransformedComposition()).thenReturn(npcComposition); when(client.getNpcs()).thenReturn(Arrays.asList(npc1, npc2));