spec counter: add spec threshold notifications
This allows configuring spec thresholds which a notification will be sent for when reached. Additionally it colors the infobox text red or green based on if the threshold has been reached. Co-authored-by: Dylan <dylanhe@gmail.com> Co-authored-by: jgozon <47003557+jgozon@users.noreply.github.com>
This commit is contained in:
@@ -24,6 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.runelite.client.plugins.specialcounter;
|
package net.runelite.client.plugins.specialcounter;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -34,13 +35,15 @@ import net.runelite.client.ui.overlay.infobox.Counter;
|
|||||||
class SpecialCounter extends Counter
|
class SpecialCounter extends Counter
|
||||||
{
|
{
|
||||||
private final SpecialWeapon weapon;
|
private final SpecialWeapon weapon;
|
||||||
|
private final SpecialCounterConfig config;
|
||||||
@Getter(AccessLevel.PACKAGE)
|
@Getter(AccessLevel.PACKAGE)
|
||||||
private final Map<String, Integer> partySpecs = new HashMap<>();
|
private final Map<String, Integer> 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);
|
super(image, plugin, hitValue);
|
||||||
this.weapon = weapon;
|
this.weapon = weapon;
|
||||||
|
this.config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addHits(double hit)
|
void addHits(double hit)
|
||||||
@@ -90,4 +93,16 @@ class SpecialCounter extends Counter
|
|||||||
return weapon.getName() + " special has hit " + hitValue + " total.";
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Dylan <dylanhe@umich.edu>
|
||||||
|
* Copyright (c) 2020, Jacob <jgozon@umich.edu>
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@
|
|||||||
package net.runelite.client.plugins.specialcounter;
|
package net.runelite.client.plugins.specialcounter;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import com.google.inject.Provides;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
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.InteractingChanged;
|
||||||
import net.runelite.api.events.NpcDespawned;
|
import net.runelite.api.events.NpcDespawned;
|
||||||
import net.runelite.api.events.VarbitChanged;
|
import net.runelite.api.events.VarbitChanged;
|
||||||
|
import net.runelite.client.Notifier;
|
||||||
import net.runelite.client.callback.ClientThread;
|
import net.runelite.client.callback.ClientThread;
|
||||||
|
import net.runelite.client.config.ConfigManager;
|
||||||
import net.runelite.client.eventbus.Subscribe;
|
import net.runelite.client.eventbus.Subscribe;
|
||||||
import net.runelite.client.game.ItemManager;
|
import net.runelite.client.game.ItemManager;
|
||||||
import net.runelite.client.plugins.Plugin;
|
import net.runelite.client.plugins.Plugin;
|
||||||
@@ -96,6 +99,18 @@ public class SpecialCounterPlugin extends Plugin
|
|||||||
@Inject
|
@Inject
|
||||||
private ItemManager itemManager;
|
private ItemManager itemManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Notifier notifier;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private SpecialCounterConfig config;
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
SpecialCounterConfig getConfig(ConfigManager configManager)
|
||||||
|
{
|
||||||
|
return configManager.getConfig(SpecialCounterConfig.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void startUp()
|
protected void startUp()
|
||||||
{
|
{
|
||||||
@@ -313,7 +328,7 @@ public class SpecialCounterPlugin extends Plugin
|
|||||||
|
|
||||||
if (counter == null)
|
if (counter == null)
|
||||||
{
|
{
|
||||||
counter = new SpecialCounter(itemManager.getImage(specialWeapon.getItemID()), this,
|
counter = new SpecialCounter(itemManager.getImage(specialWeapon.getItemID()), this, config,
|
||||||
hit, specialWeapon);
|
hit, specialWeapon);
|
||||||
infoBoxManager.addInfoBox(counter);
|
infoBoxManager.addInfoBox(counter);
|
||||||
specialCounter[specialWeapon.ordinal()] = counter;
|
specialCounter[specialWeapon.ordinal()] = counter;
|
||||||
@@ -323,6 +338,9 @@ public class SpecialCounterPlugin extends Plugin
|
|||||||
counter.addHits(hit);
|
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
|
// If in a party, add hit to partySpecs for the infobox tooltip
|
||||||
Map<String, Integer> partySpecs = counter.getPartySpecs();
|
Map<String, Integer> partySpecs = counter.getPartySpecs();
|
||||||
if (!party.getMembers().isEmpty())
|
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()
|
private void removeCounters()
|
||||||
{
|
{
|
||||||
interactedNpcIds.clear();
|
interactedNpcIds.clear();
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.runelite.client.plugins.specialcounter;
|
package net.runelite.client.plugins.specialcounter;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import net.runelite.api.ItemID;
|
import net.runelite.api.ItemID;
|
||||||
@@ -32,13 +33,14 @@ import net.runelite.api.ItemID;
|
|||||||
@Getter
|
@Getter
|
||||||
enum SpecialWeapon
|
enum SpecialWeapon
|
||||||
{
|
{
|
||||||
DRAGON_WARHAMMER("Dragon Warhammer", ItemID.DRAGON_WARHAMMER, false),
|
DRAGON_WARHAMMER("Dragon Warhammer", ItemID.DRAGON_WARHAMMER, false, SpecialCounterConfig::dragonWarhammerThreshold),
|
||||||
ARCLIGHT("Arclight", ItemID.ARCLIGHT, false),
|
ARCLIGHT("Arclight", ItemID.ARCLIGHT, false, SpecialCounterConfig::arclightThreshold),
|
||||||
DARKLIGHT("Darklight", ItemID.DARKLIGHT, false),
|
DARKLIGHT("Darklight", ItemID.DARKLIGHT, false, SpecialCounterConfig::darklightThreshold),
|
||||||
BANDOS_GODSWORD("Bandos Godsword", ItemID.BANDOS_GODSWORD, true),
|
BANDOS_GODSWORD("Bandos Godsword", ItemID.BANDOS_GODSWORD, true, SpecialCounterConfig::bandosGodswordThreshold),
|
||||||
BANDOS_GODSWORD_OR("Bandos Godsword", ItemID.BANDOS_GODSWORD_OR, true);
|
BANDOS_GODSWORD_OR("Bandos Godsword", ItemID.BANDOS_GODSWORD_OR, true, SpecialCounterConfig::bandosGodswordThreshold);
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
private final int itemID;
|
private final int itemID;
|
||||||
private final boolean damage;
|
private final boolean damage;
|
||||||
|
private final Function<SpecialCounterConfig, Integer> threshold;
|
||||||
}
|
}
|
||||||
@@ -42,6 +42,7 @@ import net.runelite.api.VarPlayer;
|
|||||||
import net.runelite.api.events.HitsplatApplied;
|
import net.runelite.api.events.HitsplatApplied;
|
||||||
import net.runelite.api.events.InteractingChanged;
|
import net.runelite.api.events.InteractingChanged;
|
||||||
import net.runelite.api.events.VarbitChanged;
|
import net.runelite.api.events.VarbitChanged;
|
||||||
|
import net.runelite.client.Notifier;
|
||||||
import net.runelite.client.game.ItemManager;
|
import net.runelite.client.game.ItemManager;
|
||||||
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
|
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
|
||||||
import net.runelite.client.ws.PartyService;
|
import net.runelite.client.ws.PartyService;
|
||||||
@@ -76,6 +77,14 @@ public class SpecialCounterPluginTest
|
|||||||
@Bind
|
@Bind
|
||||||
private ItemManager itemManager;
|
private ItemManager itemManager;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
@Bind
|
||||||
|
private Notifier notifier;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
@Bind
|
||||||
|
private SpecialCounterConfig specialCounterConfig;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private SpecialCounterPlugin specialCounterPlugin;
|
private SpecialCounterPlugin specialCounterPlugin;
|
||||||
|
|
||||||
@@ -227,4 +236,62 @@ public class SpecialCounterPluginTest
|
|||||||
|
|
||||||
verify(infoBoxManager).removeInfoBox(any(SpecialCounter.class));
|
verify(infoBoxManager).removeInfoBox(any(SpecialCounter.class));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user