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:
Adam
2021-01-08 14:24:37 -05:00
committed by Adam
parent cb22bdd026
commit 4a214f9807
5 changed files with 208 additions and 8 deletions

View File

@@ -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<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);
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();
}
}

View File

@@ -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;
}
}

View File

@@ -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<String, Integer> 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();

View File

@@ -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<SpecialCounterConfig, Integer> threshold;
}

View File

@@ -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));
}
}
@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());
}
}