Merge remote-tracking branch 'runelite/master'

This commit is contained in:
Owain van Brakel
2021-08-11 21:39:01 +02:00
24 changed files with 800 additions and 250 deletions

View File

@@ -25,7 +25,7 @@
object ProjectVersions { object ProjectVersions {
const val launcherVersion = "2.2.0" 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" const val openosrsVersion = "4.9.10"

View File

@@ -30,7 +30,6 @@ import lombok.Value;
@Value @Value
public class ItemStats public class ItemStats
{ {
private boolean quest;
private boolean equipable; private boolean equipable;
private double weight; private double weight;
@SerializedName("ge_limit") @SerializedName("ge_limit")
@@ -79,7 +78,7 @@ public class ItemStats
newEquipment = equipment; newEquipment = equipment;
} }
return new ItemStats(quest, equipable, newWeight, 0, newEquipment); return new ItemStats(equipable, newWeight, 0, newEquipment);
} }
} }

View File

@@ -854,6 +854,7 @@ public class WidgetID
static class Pvp static class Pvp
{ {
static final int KILLDEATH_RATIO = 26; static final int KILLDEATH_RATIO = 26;
static final int WILDERNESS_SKULL_CONTAINER = 44;
static final int SKULL_CONTAINER = 45; static final int SKULL_CONTAINER = 45;
static final int SAFE_ZONE = 47; static final int SAFE_ZONE = 47;
static final int WILDERNESS_LEVEL = 50; // this can also be the Deadman Mode "Protection" text static final int WILDERNESS_LEVEL = 50; // this can also be the Deadman Mode "Protection" text

View File

@@ -503,6 +503,7 @@ public enum WidgetInfo
SPELL_ARCEUUS_HOME_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.ArceuusSpellBook.ARCEUUS_HOME_TELEPORT), 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), 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_SKULL_CONTAINER(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.SKULL_CONTAINER),
PVP_WORLD_SAFE_ZONE(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.SAFE_ZONE), PVP_WORLD_SAFE_ZONE(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.SAFE_ZONE),

View File

@@ -393,7 +393,7 @@ public class RuneLite
if (!isOutdated) if (!isOutdated)
{ {
// Add core overlays // Add core overlays
WidgetOverlay.createOverlays(client).forEach(overlayManager::add); WidgetOverlay.createOverlays(overlayManager, client).forEach(overlayManager::add);
overlayManager.add(worldMapOverlay.get()); overlayManager.add(worldMapOverlay.get());
eventBus.register(worldMapOverlay.get()); eventBus.register(worldMapOverlay.get());
overlayManager.add(tooltipOverlay.get()); overlayManager.add(tooltipOverlay.get());

View File

@@ -59,7 +59,7 @@ class InteractHighlightOverlay extends Overlay
this.modelOutlineRenderer = modelOutlineRenderer; this.modelOutlineRenderer = modelOutlineRenderer;
setPosition(OverlayPosition.DYNAMIC); setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.ABOVE_SCENE); setLayer(OverlayLayer.ABOVE_SCENE);
setPriority(OverlayPriority.LOW); setPriority(OverlayPriority.HIGH);
} }
@Override @Override

View File

@@ -55,7 +55,7 @@ public class ItemStatOverlay extends Overlay
{ {
// Unarmed attack speed is 4 // Unarmed attack speed is 4
@VisibleForTesting @VisibleForTesting
static final ItemStats UNARMED = new ItemStats(false, true, 0, 0, static final ItemStats UNARMED = new ItemStats(true, 0, 0,
ItemEquipmentStats.builder() ItemEquipmentStats.builder()
.aspeed(4) .aspeed(4)
.build()); .build());

View File

@@ -147,6 +147,7 @@ public class LootTrackerPlugin extends Plugin
// Chest loot handling // Chest loot handling
private static final String CHEST_LOOTED_MESSAGE = "You find some treasure in the chest!"; 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 .*"); 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 // 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."; 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(7323, "Grubby Chest").
put(8593, "Isle of Souls Chest"). put(8593, "Isle of Souls Chest").
put(7827, "Dark Chest"). put(7827, "Dark Chest").
put(13117, "Rogues' Chest").
build(); build();
// Shade chest loot handling // 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) 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) || 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(); final int regionID = client.getLocalPlayer().getWorldLocation().getRegionID();
if (!CHEST_EVENT_TYPES.containsKey(regionID)) if (!CHEST_EVENT_TYPES.containsKey(regionID))

View File

@@ -27,6 +27,7 @@ package net.runelite.client.plugins.npchighlight;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.inject.Binder;
import com.google.inject.Provides; import com.google.inject.Provides;
import java.awt.Color; import java.awt.Color;
import java.time.Instant; import java.time.Instant;
@@ -38,6 +39,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Predicate;
import javax.inject.Inject; import javax.inject.Inject;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
@@ -77,7 +79,7 @@ import net.runelite.client.util.WildcardMatcher;
tags = {"highlight", "minimap", "npcs", "overlay", "respawn", "tags"} tags = {"highlight", "minimap", "npcs", "overlay", "respawn", "tags"}
) )
@Slf4j @Slf4j
public class NpcIndicatorsPlugin extends Plugin public class NpcIndicatorsPlugin extends Plugin implements NpcIndicatorsService
{ {
private static final int MAX_ACTOR_VIEW_RANGE = 15; private static final int MAX_ACTOR_VIEW_RANGE = 15;
@@ -173,12 +175,20 @@ public class NpcIndicatorsPlugin extends Plugin
*/ */
private boolean skipNextSpawnCheck = false; private boolean skipNextSpawnCheck = false;
private final List<Predicate<NPC>> higlightPredicates = new ArrayList<>();
@Provides @Provides
NpcIndicatorsConfig provideConfig(ConfigManager configManager) NpcIndicatorsConfig provideConfig(ConfigManager configManager)
{ {
return configManager.getConfig(NpcIndicatorsConfig.class); return configManager.getConfig(NpcIndicatorsConfig.class);
} }
@Override
public void configure(Binder binder)
{
binder.bind(NpcIndicatorsService.class).toInstance(this);
}
@Override @Override
protected void startUp() throws Exception protected void startUp() throws Exception
{ {
@@ -187,7 +197,7 @@ public class NpcIndicatorsPlugin extends Plugin
clientThread.invoke(() -> clientThread.invoke(() ->
{ {
skipNextSpawnCheck = true; skipNextSpawnCheck = true;
rebuildAllNpcs(); rebuild();
}); });
} }
@@ -230,7 +240,7 @@ public class NpcIndicatorsPlugin extends Plugin
return; return;
} }
clientThread.invoke(this::rebuildAllNpcs); clientThread.invoke(this::rebuild);
} }
@Subscribe @Subscribe
@@ -394,7 +404,23 @@ public class NpcIndicatorsPlugin extends Plugin
memorizeNpc(npc); memorizeNpc(npc);
spawnedNpcsThisTick.add(npc); spawnedNpcsThisTick.add(npc);
} }
return;
} }
for (Predicate<NPC> predicate : higlightPredicates)
{
if (predicate.test(npc))
{
highlightedNpcs.add(npc);
if (!client.isInInstancedRegion())
{
memorizeNpc(npc);
spawnedNpcsThisTick.add(npc);
}
return;
}
}
} }
@Subscribe @Subscribe
@@ -534,8 +560,8 @@ public class NpcIndicatorsPlugin extends Plugin
return Text.fromCSV(configNpcs); return Text.fromCSV(configNpcs);
} }
@VisibleForTesting @Override
void rebuildAllNpcs() public void rebuild()
{ {
highlights = getHighlights(); highlights = getHighlights();
highlightedNpcs.clear(); highlightedNpcs.clear();
@@ -548,6 +574,7 @@ public class NpcIndicatorsPlugin extends Plugin
return; return;
} }
outer:
for (NPC npc : client.getNpcs()) for (NPC npc : client.getNpcs())
{ {
final String npcName = npc.getName(); final String npcName = npc.getName();
@@ -573,6 +600,19 @@ public class NpcIndicatorsPlugin extends Plugin
continue; continue;
} }
for (Predicate<NPC> predicate : higlightPredicates)
{
if (predicate.test(npc))
{
if (!client.isInInstancedRegion())
{
memorizeNpc(npc);
}
highlightedNpcs.add(npc);
continue outer;
}
}
// NPC is not highlighted // NPC is not highlighted
memorizedNpcs.remove(npc.getIndex()); memorizedNpcs.remove(npc.getIndex());
} }
@@ -680,4 +720,16 @@ public class NpcIndicatorsPlugin extends Plugin
despawnedNpcsThisTick.clear(); despawnedNpcsThisTick.clear();
teleportGraphicsObjectSpawnedThisTick.clear(); teleportGraphicsObjectSpawnedThisTick.clear();
} }
@Override
public void registerHighlighter(Predicate<NPC> p)
{
higlightPredicates.add(p);
}
@Override
public void unregisterHighlighter(Predicate<NPC> p)
{
higlightPredicates.remove(p);
}
} }

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2021, Adam <Adam@sigterm.info>
* 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<NPC> p);
void unregisterHighlighter(Predicate<NPC> p);
void rebuild();
}

View File

@@ -25,8 +25,6 @@
*/ */
package net.runelite.client.plugins.slayer; 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.Config;
import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem; import net.runelite.client.config.ConfigItem;
@@ -101,18 +99,6 @@ public interface SlayerConfig extends Config
return false; return false;
} }
@Alpha
@ConfigItem(
position = 6,
keyName = "targetColor",
name = "Target Color",
description = "Color of the highlighted targets"
)
default Color getTargetColor()
{
return Color.RED;
}
@ConfigItem( @ConfigItem(
position = 7, position = 7,
keyName = "weaknessPrompt", keyName = "weaknessPrompt",

View File

@@ -39,9 +39,11 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Predicate;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named;
import joptsimple.internal.Strings; import joptsimple.internal.Strings;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
@@ -60,6 +62,7 @@ import static net.runelite.api.Skill.SLAYER;
import net.runelite.api.coords.WorldPoint; import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.ActorDeath; import net.runelite.api.events.ActorDeath;
import net.runelite.api.events.ChatMessage; import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.CommandExecuted;
import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick; import net.runelite.api.events.GameTick;
import net.runelite.api.events.HitsplatApplied; 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.events.ConfigChanged;
import net.runelite.client.game.ItemManager; import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDependency;
import net.runelite.client.plugins.PluginDescriptor; 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.OverlayManager;
import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
import net.runelite.client.util.ColorUtil; import net.runelite.client.util.ColorUtil;
import net.runelite.client.util.Text; import net.runelite.client.util.Text;
import net.runelite.http.api.chat.ChatClient; import net.runelite.http.api.chat.ChatClient;
import org.apache.commons.lang3.ArrayUtils;
@PluginDescriptor( @PluginDescriptor(
name = "Slayer", name = "Slayer",
description = "Show additional slayer task related information", description = "Show additional slayer task related information",
tags = {"combat", "notifications", "overlay", "tasks"} tags = {"combat", "notifications", "overlay", "tasks"}
) )
@PluginDependency(NpcIndicatorsPlugin.class)
@Slf4j @Slf4j
public class SlayerPlugin extends Plugin public class SlayerPlugin extends Plugin
{ {
@@ -151,15 +159,9 @@ public class SlayerPlugin extends Plugin
@Inject @Inject
private ClientThread clientThread; private ClientThread clientThread;
@Inject
private TargetClickboxOverlay targetClickboxOverlay;
@Inject @Inject
private TargetWeaknessOverlay targetWeaknessOverlay; private TargetWeaknessOverlay targetWeaknessOverlay;
@Inject
private TargetMinimapOverlay targetMinimapOverlay;
@Inject @Inject
private ChatMessageManager chatMessageManager; private ChatMessageManager chatMessageManager;
@@ -172,8 +174,15 @@ public class SlayerPlugin extends Plugin
@Inject @Inject
private ChatClient chatClient; private ChatClient chatClient;
@Inject
private NpcIndicatorsService npcIndicatorsService;
@Getter(AccessLevel.PACKAGE) @Getter(AccessLevel.PACKAGE)
private List<NPC> highlightedTargets = new ArrayList<>(); private final List<NPC> targets = new ArrayList<>();
@Inject
@Named("developerMode")
boolean developerMode;
private final Set<NPC> taggedNpcs = new HashSet<>(); private final Set<NPC> taggedNpcs = new HashSet<>();
private int taggedNpcsDiedPrevTick; private int taggedNpcsDiedPrevTick;
@@ -201,13 +210,16 @@ public class SlayerPlugin extends Plugin
private boolean loginFlag; private boolean loginFlag;
private final List<String> targetNames = new ArrayList<>(); private final List<String> targetNames = new ArrayList<>();
public final Predicate<NPC> isTarget = (n) -> config.highlightTargets() && targets.contains(n);
@Override @Override
protected void startUp() throws Exception protected void startUp() throws Exception
{ {
chatCommandManager.registerCommandAsync(TASK_COMMAND_STRING, this::taskLookup, this::taskSubmit);
npcIndicatorsService.registerHighlighter(isTarget);
overlayManager.add(overlay); overlayManager.add(overlay);
overlayManager.add(targetClickboxOverlay);
overlayManager.add(targetWeaknessOverlay); overlayManager.add(targetWeaknessOverlay);
overlayManager.add(targetMinimapOverlay);
if (client.getGameState() == GameState.LOGGED_IN) if (client.getGameState() == GameState.LOGGED_IN)
{ {
@@ -224,23 +236,21 @@ public class SlayerPlugin extends Plugin
getStringProfileConfig(SlayerConfig.TASK_LOC_KEY), false)); getStringProfileConfig(SlayerConfig.TASK_LOC_KEY), false));
} }
} }
chatCommandManager.registerCommandAsync(TASK_COMMAND_STRING, this::taskLookup, this::taskSubmit);
} }
@Override @Override
protected void shutDown() throws Exception protected void shutDown() throws Exception
{ {
chatCommandManager.unregisterCommand(TASK_COMMAND_STRING);
npcIndicatorsService.unregisterHighlighter(isTarget);
npcIndicatorsService.rebuild();
overlayManager.remove(overlay); overlayManager.remove(overlay);
overlayManager.remove(targetClickboxOverlay);
overlayManager.remove(targetWeaknessOverlay); overlayManager.remove(targetWeaknessOverlay);
overlayManager.remove(targetMinimapOverlay);
removeCounter(); removeCounter();
highlightedTargets.clear(); targets.clear();
taggedNpcs.clear(); taggedNpcs.clear();
cachedXp = -1; cachedXp = -1;
chatCommandManager.unregisterCommand(TASK_COMMAND_STRING);
} }
@Provides @Provides
@@ -260,7 +270,7 @@ public class SlayerPlugin extends Plugin
taskName = ""; taskName = "";
amount = 0; amount = 0;
loginFlag = true; loginFlag = true;
highlightedTargets.clear(); targets.clear();
taggedNpcs.clear(); taggedNpcs.clear();
break; break;
case LOGGED_IN: 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 @VisibleForTesting
int getIntProfileConfig(String key) int getIntProfileConfig(String key)
{ {
@@ -313,13 +333,16 @@ public class SlayerPlugin extends Plugin
setProfileConfig(SlayerConfig.TASK_LOC_KEY, taskLocation); 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) public void onNpcSpawned(NpcSpawned npcSpawned)
{ {
NPC npc = npcSpawned.getNpc(); NPC npc = npcSpawned.getNpc();
if (isTarget(npc)) if (isTarget(npc))
{ {
highlightedTargets.add(npc); targets.add(npc);
} }
} }
@@ -328,7 +351,7 @@ public class SlayerPlugin extends Plugin
{ {
NPC npc = npcDespawned.getNpc(); NPC npc = npcDespawned.getNpc();
taggedNpcs.remove(npc); taggedNpcs.remove(npc);
highlightedTargets.remove(npc); targets.remove(npc);
} }
@Subscribe @Subscribe
@@ -541,7 +564,7 @@ public class SlayerPlugin extends Plugin
{ {
Actor actor = hitsplatApplied.getActor(); Actor actor = hitsplatApplied.getActor();
Hitsplat hitsplat = hitsplatApplied.getHitsplat(); 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 // If the actor is in highlightedTargets it must be an NPC and also a task assignment
taggedNpcs.add((NPC) actor); taggedNpcs.add((NPC) actor);
@@ -562,18 +585,25 @@ public class SlayerPlugin extends Plugin
@Subscribe @Subscribe
private void onConfigChanged(ConfigChanged event) private void onConfigChanged(ConfigChanged event)
{ {
if (!event.getGroup().equals(SlayerConfig.GROUP_NAME) || !event.getKey().equals("infobox")) if (!event.getGroup().equals(SlayerConfig.GROUP_NAME))
{ {
return; return;
} }
if (config.showInfobox()) if (event.getKey().equals("infobox"))
{ {
clientThread.invoke(this::addCounter); if (config.showInfobox())
{
clientThread.invoke(this::addCounter);
}
else
{
removeCounter();
}
} }
else else
{ {
removeCounter(); npcIndicatorsService.rebuild();
} }
} }
@@ -619,27 +649,25 @@ public class SlayerPlugin extends Plugin
return false; return false;
} }
String name = npc.getName(); final NPCComposition composition = npc.getTransformedComposition();
if (name == null) if (composition == null)
{ {
return false; return false;
} }
name = name.toLowerCase(); final String name = composition.getName()
.replace('\u00A0', ' ')
.toLowerCase();
for (String target : targetNames) for (String target : targetNames)
{ {
if (name.contains(target)) if (name.contains(target))
{ {
NPCComposition composition = npc.getTransformedComposition(); if (ArrayUtils.contains(composition.getActions(), "Attack")
// Pick action is for zygomite-fungi
if (composition != null) || ArrayUtils.contains(composition.getActions(), "Pick"))
{ {
List<String> actions = Arrays.asList(composition.getActions()); return true;
if (actions.contains("Attack") || actions.contains("Pick")) //Pick action is for zygomite-fungi
{
return true;
}
} }
} }
} }
@@ -662,13 +690,13 @@ public class SlayerPlugin extends Plugin
private void rebuildTargetList() private void rebuildTargetList()
{ {
highlightedTargets.clear(); targets.clear();
for (NPC npc : client.getNpcs()) for (NPC npc : client.getNpcs())
{ {
if (isTarget(npc)) if (isTarget(npc))
{ {
highlightedTargets.add(npc); targets.add(npc);
} }
} }
} }
@@ -701,6 +729,7 @@ public class SlayerPlugin extends Plugin
Task task = Task.getTask(name); Task task = Task.getTask(name);
rebuildTargetNames(task); rebuildTargetNames(task);
rebuildTargetList(); rebuildTargetList();
npcIndicatorsService.rebuild();
} }
private void addCounter() private void addCounter()

View File

@@ -1,85 +0,0 @@
/*
* Copyright (c) 2018, James Swindle <wilingua@gmail.com>
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* Copyright (c) 2018, Shaun Dreclin <shaundreclin@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.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<NPC> 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);
}
}
}

View File

@@ -1,80 +0,0 @@
/*
* Copyright (c) 2018, James Swindle <wilingua@gmail.com>
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* Copyright (c) 2018, Shaun Dreclin <shaundreclin@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.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<NPC> 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);
}
}
}

View File

@@ -64,7 +64,7 @@ class TargetWeaknessOverlay extends Overlay
@Override @Override
public Dimension render(Graphics2D graphics) public Dimension render(Graphics2D graphics)
{ {
final List<NPC> targets = plugin.getHighlightedTargets(); final List<NPC> targets = plugin.getTargets();
if (targets.isEmpty() || !config.weaknessPrompt()) if (targets.isEmpty() || !config.weaknessPrompt())
{ {

View File

@@ -128,4 +128,11 @@ public abstract class Overlay implements LayoutableRenderableEntity
{ {
return null; return null;
} }
public void reset()
{
setPreferredPosition(null);
setPreferredSize(null);
setPreferredLocation(null);
}
} }

View File

@@ -289,9 +289,7 @@ public class OverlayManager
*/ */
public synchronized void resetOverlay(final Overlay overlay) public synchronized void resetOverlay(final Overlay overlay)
{ {
overlay.setPreferredPosition(null); overlay.reset();
overlay.setPreferredSize(null);
overlay.setPreferredLocation(null);
saveOverlay(overlay); saveOverlay(overlay);
} }

View File

@@ -30,20 +30,23 @@ import java.awt.Rectangle;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Objects; import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.Varbits; import net.runelite.api.Varbits;
import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo; import net.runelite.api.widgets.WidgetInfo;
@Slf4j
public class WidgetOverlay extends Overlay public class WidgetOverlay extends Overlay
{ {
public static Collection<WidgetOverlay> createOverlays(final Client client) public static Collection<WidgetOverlay> createOverlays(final OverlayManager overlayManager, final Client client)
{ {
return Arrays.asList( return Arrays.asList(
new WidgetOverlay(client, WidgetInfo.RESIZABLE_MINIMAP_WIDGET, OverlayPosition.CANVAS_TOP_RIGHT), 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.RESIZABLE_MINIMAP_STONES_WIDGET, OverlayPosition.CANVAS_TOP_RIGHT),
new WidgetOverlay(client, WidgetInfo.FOSSIL_ISLAND_OXYGENBAR, OverlayPosition.TOP_CENTER), // The client forces the oxygen bar below the xp tracker, so set its priority lower
new XpTrackerWidgetOverlay(client, WidgetInfo.EXPERIENCE_TRACKER_WIDGET, OverlayPosition.TOP_RIGHT), 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.RAIDS_POINTS_INFOBOX, OverlayPosition.TOP_RIGHT),
new WidgetOverlay(client, WidgetInfo.TOB_PARTY_INTERFACE, OverlayPosition.TOP_LEFT), new WidgetOverlay(client, WidgetInfo.TOB_PARTY_INTERFACE, OverlayPosition.TOP_LEFT),
new WidgetOverlay(client, WidgetInfo.TOB_PARTY_STATS, 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.LMS_KDA, OverlayPosition.TOP_RIGHT),
new WidgetOverlay(client, WidgetInfo.GAUNTLET_TIMER_CONTAINER, OverlayPosition.TOP_LEFT), 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.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.TOB_HEALTH_BAR, OverlayPosition.TOP_CENTER),
new WidgetOverlay(client, WidgetInfo.NIGHTMARE_PILLAR_HEALTH, OverlayPosition.TOP_LEFT), new WidgetOverlay(client, WidgetInfo.NIGHTMARE_PILLAR_HEALTH, OverlayPosition.TOP_LEFT),
new WidgetOverlay(client, WidgetInfo.VOLCANIC_MINE_VENTS_INFOBOX_GROUP, OverlayPosition.BOTTOM_RIGHT), 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.MULTICOMBAT_RESIZEABLE_CLASSIC, OverlayPosition.CANVAS_TOP_RIGHT),
new WidgetOverlay(client, WidgetInfo.TEMPOROSS_STATUS_INDICATOR, OverlayPosition.TOP_LEFT), 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_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; protected final Client client;
private final WidgetInfo widgetInfo; private final WidgetInfo widgetInfo;
private final Rectangle parentBounds = new Rectangle(); private final Rectangle parentBounds = new Rectangle();
private boolean revalidate;
private WidgetOverlay(final Client client, final WidgetInfo widgetInfo, final OverlayPosition overlayPosition) 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.client = client;
this.widgetInfo = widgetInfo; this.widgetInfo = widgetInfo;
setPriority(OverlayPriority.HIGHEST); setPriority(overlayPriority);
setLayer(OverlayLayer.UNDER_WIDGETS); setLayer(OverlayLayer.UNDER_WIDGETS);
setPosition(overlayPosition); setPosition(overlayPosition);
// It's almost possible to drawAfterInterface(widgetInfo.getGroupId()) here, but that fires // It's almost possible to drawAfterInterface(widgetInfo.getGroupId()) here, but that fires
@@ -107,10 +118,34 @@ public class WidgetOverlay extends Overlay
return null; return null;
} }
assert widget != null;
final Rectangle bounds = getBounds(); final Rectangle bounds = getBounds();
// The widget relative pos is relative to the parent // OverlayRenderer sets the overlay bounds to the preferred location if one is set prior to calling render()
widget.setRelativeX(bounds.x - parent.x); // for detached overlays.
widget.setRelativeY(bounds.y - parent.y); 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()); return new Dimension(widget.getWidth(), widget.getHeight());
} }
@@ -152,11 +187,22 @@ public class WidgetOverlay extends Overlay
return getParentBounds(widget); 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 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); super(client, widgetInfo, overlayPosition);
this.overlayManager = overlayManager;
} }
/** /**
@@ -188,7 +234,13 @@ public class WidgetOverlay extends Overlay
position = OverlayPosition.TOP_LEFT; position = OverlayPosition.TOP_LEFT;
break; break;
} }
setPosition(position);
if (position != super.getPosition())
{
log.debug("Xp tracker moved position");
setPosition(position);
overlayManager.rebuildOverlayLayers();
}
return position; return position;
} }
} }

View File

@@ -192,6 +192,12 @@
"name": "Super Strength (3)", "name": "Super Strength (3)",
"xp": 125 "xp": 125
}, },
{
"level": 57,
"icon": 9022,
"name": "Magic Essence Potion (3)",
"xp": 130
},
{ {
"level": 59, "level": 59,
"icon": 3000, "icon": 3000,

View File

@@ -0,0 +1,211 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* 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<Class<?>> pluginClasses;
private Set<Class<?>> 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<ClassInfo> 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<Plugin> 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<String> 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);
}
}
}
}

View File

@@ -0,0 +1,173 @@
/*
* Copyright (c) 2020 Jordan <nightfirecat@protonmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.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:"));
}
}

View File

@@ -101,7 +101,7 @@ public class NpcIndicatorsPluginTest
when(npcIndicatorsConfig.getNpcToHighlight()).thenReturn("goblin"); when(npcIndicatorsConfig.getNpcToHighlight()).thenReturn("goblin");
when(npcIndicatorsConfig.deadNpcMenuColor()).thenReturn(Color.RED); when(npcIndicatorsConfig.deadNpcMenuColor()).thenReturn(Color.RED);
npcIndicatorsPlugin.rebuildAllNpcs(); npcIndicatorsPlugin.rebuild();
NPC npc = mock(NPC.class); NPC npc = mock(NPC.class);
when(npc.getName()).thenReturn("Goblin"); when(npc.getName()).thenReturn("Goblin");
@@ -127,7 +127,7 @@ public class NpcIndicatorsPluginTest
when(npcIndicatorsConfig.highlightMenuNames()).thenReturn(true); when(npcIndicatorsConfig.highlightMenuNames()).thenReturn(true);
when(npcIndicatorsConfig.getHighlightColor()).thenReturn(Color.BLUE); when(npcIndicatorsConfig.getHighlightColor()).thenReturn(Color.BLUE);
npcIndicatorsPlugin.rebuildAllNpcs(); npcIndicatorsPlugin.rebuild();
NPC npc = mock(NPC.class); NPC npc = mock(NPC.class);
when(npc.getName()).thenReturn("Goblin"); when(npc.getName()).thenReturn("Goblin");
@@ -149,7 +149,7 @@ public class NpcIndicatorsPluginTest
{ {
when(npcIndicatorsConfig.getNpcToHighlight()).thenReturn("Joseph"); when(npcIndicatorsConfig.getNpcToHighlight()).thenReturn("Joseph");
npcIndicatorsPlugin.rebuildAllNpcs(); npcIndicatorsPlugin.rebuild();
NPC npc = mock(NPC.class); NPC npc = mock(NPC.class);
when(npc.getName()).thenReturn("Joseph"); when(npc.getName()).thenReturn("Joseph");
@@ -168,7 +168,7 @@ public class NpcIndicatorsPluginTest
{ {
when(npcIndicatorsConfig.getNpcToHighlight()).thenReturn("Werewolf"); when(npcIndicatorsConfig.getNpcToHighlight()).thenReturn("Werewolf");
npcIndicatorsPlugin.rebuildAllNpcs(); npcIndicatorsPlugin.rebuild();
NPC npc = mock(NPC.class); NPC npc = mock(NPC.class);
when(npc.getName()).thenReturn("Joseph"); when(npc.getName()).thenReturn("Joseph");

View File

@@ -0,0 +1,154 @@
/*
* Copyright (c) 2020, Landy Chan <https://github.com/landychan>
* 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));
}
}

View File

@@ -31,6 +31,7 @@ import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named;
import net.runelite.api.ChatMessageType; import net.runelite.api.ChatMessageType;
import static net.runelite.api.ChatMessageType.GAMEMESSAGE; import static net.runelite.api.ChatMessageType.GAMEMESSAGE;
import net.runelite.api.Client; 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.chat.ChatMessageManager;
import net.runelite.client.config.ConfigManager; import net.runelite.client.config.ConfigManager;
import net.runelite.client.game.ItemManager; 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.OverlayManager;
import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
import net.runelite.http.api.chat.ChatClient; import net.runelite.http.api.chat.ChatClient;
@@ -168,6 +170,14 @@ public class SlayerPluginTest
@Bind @Bind
ChatClient chatClient; ChatClient chatClient;
@Bind
@Named("developerMode")
boolean developerMode;
@Mock
@Bind
NpcIndicatorsService npcIndicatorsService;
@Inject @Inject
SlayerPlugin slayerPlugin; SlayerPlugin slayerPlugin;
@@ -871,14 +881,13 @@ public class SlayerPluginTest
slayerPlugin.onStatChanged(statChanged); slayerPlugin.onStatChanged(statChanged);
NPCComposition npcComposition = mock(NPCComposition.class); NPCComposition npcComposition = mock(NPCComposition.class);
when(npcComposition.getName()).thenReturn("Suqah");
when(npcComposition.getActions()).thenReturn(new String[]{"Attack"}); when(npcComposition.getActions()).thenReturn(new String[]{"Attack"});
NPC npc1 = mock(NPC.class); NPC npc1 = mock(NPC.class);
when(npc1.getName()).thenReturn("Suqah");
when(npc1.getTransformedComposition()).thenReturn(npcComposition); when(npc1.getTransformedComposition()).thenReturn(npcComposition);
NPC npc2 = mock(NPC.class); NPC npc2 = mock(NPC.class);
when(npc2.getName()).thenReturn("Suqah");
when(npc2.getTransformedComposition()).thenReturn(npcComposition); when(npc2.getTransformedComposition()).thenReturn(npcComposition);
when(client.getNpcs()).thenReturn(Arrays.asList(npc1, npc2)); when(client.getNpcs()).thenReturn(Arrays.asList(npc1, npc2));