diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounter.java b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounter.java index eaeb0a0cda..736ae0677c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounter.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounter.java @@ -24,6 +24,7 @@ */ package net.runelite.client.plugins.specialcounter; +import java.awt.Color; import java.awt.image.BufferedImage; import java.util.HashMap; import java.util.Map; @@ -34,13 +35,15 @@ import net.runelite.client.ui.overlay.infobox.Counter; class SpecialCounter extends Counter { private final SpecialWeapon weapon; + private final SpecialCounterConfig config; @Getter(AccessLevel.PACKAGE) private final Map partySpecs = new HashMap<>(); - SpecialCounter(BufferedImage image, SpecialCounterPlugin plugin, int hitValue, SpecialWeapon weapon) + SpecialCounter(BufferedImage image, SpecialCounterPlugin plugin, SpecialCounterConfig config, int hitValue, SpecialWeapon weapon) { super(image, plugin, hitValue); this.weapon = weapon; + this.config = config; } void addHits(double hit) @@ -90,4 +93,16 @@ class SpecialCounter extends Counter return weapon.getName() + " special has hit " + hitValue + " total."; } } + + @Override + public Color getTextColor() + { + int threshold = weapon.getThreshold().apply(config); + if (threshold > 0) + { + int count = getCount(); + return count >= threshold ? Color.GREEN : Color.RED; + } + return super.getTextColor(); + } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterConfig.java new file mode 100644 index 0000000000..4320b59e6c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterConfig.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2020, Dylan + * Copyright (c) 2020, Jacob + * 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.specialcounter; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("specialcounter") +public interface SpecialCounterConfig extends Config +{ + @ConfigItem( + position = 0, + keyName = "thresholdNotification", + name = "Threshold Notifications", + description = "Sends a notification when your special attack counter exceeds the threshold" + ) + default boolean thresholdNotification() + { + return false; + } + + @ConfigItem( + position = 1, + keyName = "dragonWarhammerThreshold", + name = "Dragon Warhammer", + description = "Threshold for Dragon Warhammer (0 to disable)" + ) + default int dragonWarhammerThreshold() + { + return 0; + } + + @ConfigItem( + position = 2, + keyName = "arclightThreshold", + name = "Arclight", + description = "Threshold for Arclight (0 to disable)" + ) + default int arclightThreshold() + { + return 0; + } + + @ConfigItem( + position = 3, + keyName = "darklightThreshold", + name = "Darklight", + description = "Threshold for Darklight (0 to disable)" + ) + default int darklightThreshold() + { + return 0; + } + + @ConfigItem( + position = 4, + keyName = "bandosGodswordThreshold", + name = "Bandos Godsword", + description = "Threshold for Bandos Godsword (0 to disable)" + ) + default int bandosGodswordThreshold() + { + return 0; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java index 5ca87940ff..799dd098ef 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java @@ -25,6 +25,7 @@ package net.runelite.client.plugins.specialcounter; import com.google.common.collect.ImmutableSet; +import com.google.inject.Provides; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -46,7 +47,9 @@ import net.runelite.api.events.HitsplatApplied; import net.runelite.api.events.InteractingChanged; import net.runelite.api.events.NpcDespawned; import net.runelite.api.events.VarbitChanged; +import net.runelite.client.Notifier; import net.runelite.client.callback.ClientThread; +import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.game.ItemManager; import net.runelite.client.plugins.Plugin; @@ -96,6 +99,18 @@ public class SpecialCounterPlugin extends Plugin @Inject private ItemManager itemManager; + @Inject + private Notifier notifier; + + @Inject + private SpecialCounterConfig config; + + @Provides + SpecialCounterConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(SpecialCounterConfig.class); + } + @Override protected void startUp() { @@ -313,7 +328,7 @@ public class SpecialCounterPlugin extends Plugin if (counter == null) { - counter = new SpecialCounter(itemManager.getImage(specialWeapon.getItemID()), this, + counter = new SpecialCounter(itemManager.getImage(specialWeapon.getItemID()), this, config, hit, specialWeapon); infoBoxManager.addInfoBox(counter); specialCounter[specialWeapon.ordinal()] = counter; @@ -323,6 +338,9 @@ public class SpecialCounterPlugin extends Plugin counter.addHits(hit); } + // Display a notification if special attack thresholds are met + sendNotification(specialWeapon, counter); + // If in a party, add hit to partySpecs for the infobox tooltip Map partySpecs = counter.getPartySpecs(); if (!party.getMembers().isEmpty()) @@ -338,6 +356,15 @@ public class SpecialCounterPlugin extends Plugin } } + private void sendNotification(SpecialWeapon weapon, SpecialCounter counter) + { + int threshold = weapon.getThreshold().apply(config); + if (threshold > 0 && counter.getCount() >= threshold && config.thresholdNotification()) + { + notifier.notify(weapon.getName() + " special attack threshold reached!"); + } + } + private void removeCounters() { interactedNpcIds.clear(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialWeapon.java b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialWeapon.java index 69d8d68193..e3cd82cfc6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialWeapon.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialWeapon.java @@ -24,6 +24,7 @@ */ package net.runelite.client.plugins.specialcounter; +import java.util.function.Function; import lombok.AllArgsConstructor; import lombok.Getter; import net.runelite.api.ItemID; @@ -32,13 +33,14 @@ import net.runelite.api.ItemID; @Getter enum SpecialWeapon { - DRAGON_WARHAMMER("Dragon Warhammer", ItemID.DRAGON_WARHAMMER, false), - ARCLIGHT("Arclight", ItemID.ARCLIGHT, false), - DARKLIGHT("Darklight", ItemID.DARKLIGHT, false), - BANDOS_GODSWORD("Bandos Godsword", ItemID.BANDOS_GODSWORD, true), - BANDOS_GODSWORD_OR("Bandos Godsword", ItemID.BANDOS_GODSWORD_OR, true); + DRAGON_WARHAMMER("Dragon Warhammer", ItemID.DRAGON_WARHAMMER, false, SpecialCounterConfig::dragonWarhammerThreshold), + ARCLIGHT("Arclight", ItemID.ARCLIGHT, false, SpecialCounterConfig::arclightThreshold), + DARKLIGHT("Darklight", ItemID.DARKLIGHT, false, SpecialCounterConfig::darklightThreshold), + BANDOS_GODSWORD("Bandos Godsword", ItemID.BANDOS_GODSWORD, true, SpecialCounterConfig::bandosGodswordThreshold), + BANDOS_GODSWORD_OR("Bandos Godsword", ItemID.BANDOS_GODSWORD_OR, true, SpecialCounterConfig::bandosGodswordThreshold); private final String name; private final int itemID; private final boolean damage; + private final Function threshold; } \ No newline at end of file diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/specialcounter/SpecialCounterPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/specialcounter/SpecialCounterPluginTest.java index e25f75b6cf..e865688cd4 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/specialcounter/SpecialCounterPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/specialcounter/SpecialCounterPluginTest.java @@ -42,6 +42,7 @@ import net.runelite.api.VarPlayer; import net.runelite.api.events.HitsplatApplied; import net.runelite.api.events.InteractingChanged; import net.runelite.api.events.VarbitChanged; +import net.runelite.client.Notifier; import net.runelite.client.game.ItemManager; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import net.runelite.client.ws.PartyService; @@ -76,6 +77,14 @@ public class SpecialCounterPluginTest @Bind private ItemManager itemManager; + @Mock + @Bind + private Notifier notifier; + + @Mock + @Bind + private SpecialCounterConfig specialCounterConfig; + @Inject private SpecialCounterPlugin specialCounterPlugin; @@ -227,4 +236,62 @@ public class SpecialCounterPluginTest verify(infoBoxManager).removeInfoBox(any(SpecialCounter.class)); } -} \ No newline at end of file + + @Test + public void testNotification() + { + // Create an enemy + NPC target = mock(NPC.class); + + // Create player + Player player = mock(Player.class); + when(client.getLocalPlayer()).thenReturn(player); + when(specialCounterConfig.bandosGodswordThreshold()).thenReturn(2); + when(specialCounterConfig.thresholdNotification()).thenReturn(true); + + // Attack enemy + when(player.getInteracting()).thenReturn(target); + specialCounterPlugin.onInteractingChanged(new InteractingChanged(player, target)); + + // First special attack + when(client.getVar(VarPlayer.SPECIAL_ATTACK_PERCENT)).thenReturn(50); + specialCounterPlugin.onVarbitChanged(new VarbitChanged()); + specialCounterPlugin.onHitsplatApplied(hitsplat(target, Hitsplat.HitsplatType.DAMAGE_ME)); + + // Second special attack + when(client.getVar(VarPlayer.SPECIAL_ATTACK_PERCENT)).thenReturn(0); + specialCounterPlugin.onVarbitChanged(new VarbitChanged()); + specialCounterPlugin.onHitsplatApplied(hitsplat(target, Hitsplat.HitsplatType.DAMAGE_ME)); + + verify(notifier).notify("Bandos Godsword special attack threshold reached!"); + } + + @Test + public void testNotificationNotThreshold() + { + // Create an enemy + NPC target = mock(NPC.class); + + // Create player + Player player = mock(Player.class); + when(client.getLocalPlayer()).thenReturn(player); + when(specialCounterConfig.bandosGodswordThreshold()).thenReturn(3); + lenient().when(specialCounterConfig.thresholdNotification()).thenReturn(true); + + // Attack enemy + when(player.getInteracting()).thenReturn(target); + specialCounterPlugin.onInteractingChanged(new InteractingChanged(player, target)); + + // First special attack + when(client.getVar(VarPlayer.SPECIAL_ATTACK_PERCENT)).thenReturn(50); + specialCounterPlugin.onVarbitChanged(new VarbitChanged()); + specialCounterPlugin.onHitsplatApplied(hitsplat(target, Hitsplat.HitsplatType.DAMAGE_ME)); + + // Second special attack + when(client.getVar(VarPlayer.SPECIAL_ATTACK_PERCENT)).thenReturn(0); + specialCounterPlugin.onVarbitChanged(new VarbitChanged()); + specialCounterPlugin.onHitsplatApplied(hitsplat(target, Hitsplat.HitsplatType.DAMAGE_ME)); + + verify(notifier, never()).notify(any()); + } +}