timers plugin: Improve imbued heart detection

As is the case with other graphics-based timers, the imbued heart timer
will not fire if enough other graphics animations are triggered (such as
those created when fighting the Dagannoth Kings). To add this timer more
reliably, this commit will add the timer when a Magic stat boost occurs
which is large enough that it can only be triggered by the heart.

Because the heart's boost scales with the player's level, it gives an
equal boost to the Magic essence and Magic potion boosts, depending on
the base magic level. To account for this, it will only be applied if
the user's base magic level is high enough to assuredly identify that a
potion was not used to trigger the boost.

Fixes runelite/runelite#3516

Co-authored-by: Lucas <lws.ned1260@gmail.com>
This commit is contained in:
Jordan Atwood
2020-02-02 22:24:21 -08:00
committed by Adam
parent 1a9efe885e
commit 45c5df3379
2 changed files with 71 additions and 1 deletions

View File

@@ -25,6 +25,7 @@
*/
package net.runelite.client.plugins.timers;
import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Provides;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -43,10 +44,12 @@ import net.runelite.api.ItemID;
import net.runelite.api.NPC;
import net.runelite.api.NpcID;
import net.runelite.api.Player;
import net.runelite.api.Skill;
import net.runelite.api.Varbits;
import net.runelite.api.WorldType;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.AnimationChanged;
import net.runelite.api.events.StatChanged;
import net.runelite.api.events.ChatMessage;
import net.runelite.client.events.ConfigChanged;
import net.runelite.api.events.GameStateChanged;
@@ -112,6 +115,9 @@ public class TimersPlugin extends Plugin
private static final Pattern HALF_TELEBLOCK_PATTERN = Pattern.compile("<col=4f006f>A Tele Block spell has been cast on you by (.+)\\. It will expire in 2 minutes, 30 seconds\\.</col>");
private static final Pattern DIVINE_POTION_PATTERN = Pattern.compile("You drink some of your divine (.+) potion\\.");
@VisibleForTesting
static final int IMBUED_HEART_MIN_CERTAIN_BOOST_LEVEL = 40; // Before this level, other effects can grant boosts of equal amounts
private TimerTimer freezeTimer;
private int freezeTime = -1; // time frozen, in game ticks
@@ -892,6 +898,24 @@ public class TimersPlugin extends Plugin
}
}
@Subscribe
public void onStatChanged(StatChanged statChanged)
{
if (statChanged.getSkill() != Skill.MAGIC || !config.showImbuedHeart())
{
return;
}
final int magicLevel = statChanged.getLevel();
final int boostAmount = statChanged.getBoostedLevel() - magicLevel;
final int heartBoost = 1 + (magicLevel / 10);
if (magicLevel >= IMBUED_HEART_MIN_CERTAIN_BOOST_LEVEL && boostAmount == heartBoost)
{
createGameTimer(IMBUEDHEART);
}
}
private TimerTimer createGameTimer(final GameTimer timer)
{
removeGameTimer(timer);

View File

@@ -31,8 +31,11 @@ import com.google.inject.testing.fieldbinder.BoundFieldModule;
import java.util.EnumSet;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.Experience;
import net.runelite.api.Skill;
import net.runelite.api.WorldType;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.StatChanged;
import net.runelite.client.game.ItemManager;
import net.runelite.client.game.SpriteManager;
import net.runelite.client.ui.overlay.infobox.InfoBox;
@@ -42,7 +45,10 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import static org.mockito.ArgumentMatchers.any;
import org.mockito.Mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.mockito.junit.MockitoJUnitRunner;
@@ -136,4 +142,44 @@ public class TimersPluginTest
TimerTimer infoBox = (TimerTimer) captor.getValue();
assertEquals(GameTimer.DMM_FULLTB, infoBox.getTimer());
}
}
@Test
public void testImbuedHeartBoost()
{
when(timersConfig.showImbuedHeart()).thenReturn(true);
StatChanged event;
// The following simulates imbued heart boosts at low magic levels, but should not create an imbued heart timer
// because it is ambiguous what caused the boost. (Magic essences and potions can create similar boost amounts)
for (int level = 1; level < TimersPlugin.IMBUED_HEART_MIN_CERTAIN_BOOST_LEVEL; level++)
{
event = new StatChanged(Skill.MAGIC, 0, level, level + 1 + (level / 10));
timersPlugin.onStatChanged(event);
verify(infoBoxManager, never()).addInfoBox(any());
}
// The following simulates magic essence and magic potion boosts and should not create an imbued heart timer
for (int level = TimersPlugin.IMBUED_HEART_MIN_CERTAIN_BOOST_LEVEL; level <= Experience.MAX_REAL_LEVEL; level++)
{
event = new StatChanged(Skill.MAGIC, 0, level, level + 3); // Magic essence
timersPlugin.onStatChanged(event);
verify(infoBoxManager, never()).addInfoBox(any());
event = new StatChanged(Skill.MAGIC, 0, level, level + 4);
timersPlugin.onStatChanged(event);
verify(infoBoxManager, never()).addInfoBox(any());
}
// The following simulates a real imbued heart magic boost and should create imbued heart timers
for (int level = TimersPlugin.IMBUED_HEART_MIN_CERTAIN_BOOST_LEVEL, i = 0; level <= Experience.MAX_REAL_LEVEL; level++, i++)
{
event = new StatChanged(Skill.MAGIC, 0, level, level + 1 + (level / 10));
timersPlugin.onStatChanged(event);
ArgumentCaptor<InfoBox> captor = ArgumentCaptor.forClass(InfoBox.class);
verify(infoBoxManager, times(i + 1)).addInfoBox(captor.capture());
TimerTimer infoBox = (TimerTimer) captor.getValue();
assertEquals(GameTimer.IMBUEDHEART, infoBox.getTimer());
}
}
}