timers plugin: Improve imbued heart detection
As is the case with other graphics-based timers, the imbued heart timer will not fire if 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 of the appropriate amount after recently clicking on an imbued heart. Because the magic level boost, combined with an imbued heart click check, is a reliable way to detect usage of an imbued heart, the graphics check is removed.
This commit is contained in:
@@ -44,7 +44,6 @@ public class GraphicID
|
||||
public static final int BOOK_HOME_TELEPORT_3 = 803;
|
||||
public static final int BOOK_HOME_TELEPORT_4 = 804;
|
||||
public static final int STAFF_OF_THE_DEAD = 1228;
|
||||
public static final int IMBUED_HEART = 1316;
|
||||
public static final int FLYING_FISH = 1387;
|
||||
public static final int NPC_CONTACT = 728;
|
||||
public static final int POT_SHARE = 733;
|
||||
|
||||
@@ -54,7 +54,7 @@ enum GameTimer
|
||||
ICEBURST(SpriteID.SPELL_ICE_BURST, GameTimerImageType.SPRITE, "Ice burst", GraphicID.ICE_BURST, 16, GAME_TICKS, true),
|
||||
ICEBLITZ(SpriteID.SPELL_ICE_BLITZ, GameTimerImageType.SPRITE, "Ice blitz", GraphicID.ICE_BLITZ, 24, GAME_TICKS, true),
|
||||
ICEBARRAGE(SpriteID.SPELL_ICE_BARRAGE, GameTimerImageType.SPRITE, "Ice barrage", GraphicID.ICE_BARRAGE, 32, GAME_TICKS, true),
|
||||
IMBUEDHEART(ItemID.IMBUED_HEART, GameTimerImageType.ITEM, "Imbued heart", GraphicID.IMBUED_HEART, 420, ChronoUnit.SECONDS, true),
|
||||
IMBUEDHEART(ItemID.IMBUED_HEART, GameTimerImageType.ITEM, "Imbued heart", 420, ChronoUnit.SECONDS, true),
|
||||
VENGEANCE(SpriteID.SPELL_VENGEANCE, GameTimerImageType.SPRITE, "Vengeance", 30, ChronoUnit.SECONDS),
|
||||
EXSUPERANTIFIRE(ItemID.EXTENDED_SUPER_ANTIFIRE4, GameTimerImageType.ITEM, "Extended Super AntiFire", 6, ChronoUnit.MINUTES),
|
||||
OVERLOAD_RAID(ItemID.OVERLOAD_4_20996, GameTimerImageType.ITEM, "Overload", 5, ChronoUnit.MINUTES, true),
|
||||
|
||||
@@ -39,6 +39,7 @@ import net.runelite.api.ChatMessageType;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Constants;
|
||||
import net.runelite.api.EquipmentInventorySlot;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.InventoryID;
|
||||
import net.runelite.api.Item;
|
||||
import net.runelite.api.ItemContainer;
|
||||
@@ -48,11 +49,13 @@ import static net.runelite.api.ItemID.INFERNAL_CAPE;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.api.NpcID;
|
||||
import net.runelite.api.Player;
|
||||
import net.runelite.api.Skill;
|
||||
import net.runelite.api.VarPlayer;
|
||||
import net.runelite.api.Varbits;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import net.runelite.api.events.ActorDeath;
|
||||
import net.runelite.api.events.AnimationChanged;
|
||||
import net.runelite.api.events.StatChanged;
|
||||
import net.runelite.api.events.ChatMessage;
|
||||
import net.runelite.api.events.GameStateChanged;
|
||||
import net.runelite.api.events.GameTick;
|
||||
@@ -141,6 +144,8 @@ public class TimersPlugin extends Plugin
|
||||
private int lastAnimation;
|
||||
private boolean widgetHiddenChangedOnPvpWorld;
|
||||
private ElapsedTimer tzhaarTimer;
|
||||
private int imbuedHeartClickTick = -1;
|
||||
private int lastBoostedMagicLevel = -1;
|
||||
|
||||
@Inject
|
||||
private ItemManager itemManager;
|
||||
@@ -163,6 +168,15 @@ public class TimersPlugin extends Plugin
|
||||
return configManager.getConfig(TimersConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startUp()
|
||||
{
|
||||
if (client.getGameState() == GameState.LOGGED_IN)
|
||||
{
|
||||
lastBoostedMagicLevel = client.getBoostedSkillLevel(Skill.MAGIC);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown() throws Exception
|
||||
{
|
||||
@@ -176,6 +190,8 @@ public class TimersPlugin extends Plugin
|
||||
nextPoisonTick = 0;
|
||||
removeTzhaarTimer();
|
||||
staminaTimer = null;
|
||||
imbuedHeartClickTick = -1;
|
||||
lastBoostedMagicLevel = -1;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
@@ -435,6 +451,12 @@ public class TimersPlugin extends Plugin
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getMenuOption().contains("Invigorate")
|
||||
&& event.getId() == ItemID.IMBUED_HEART)
|
||||
{
|
||||
imbuedHeartClickTick = client.getTickCount();
|
||||
}
|
||||
|
||||
TeleportWidget teleportWidget = TeleportWidget.of(event.getWidgetId());
|
||||
if (teleportWidget != null)
|
||||
{
|
||||
@@ -796,8 +818,10 @@ public class TimersPlugin extends Plugin
|
||||
config.tzhaarLastTime(null);
|
||||
}
|
||||
break;
|
||||
case HOPPING:
|
||||
case LOGIN_SCREEN:
|
||||
lastBoostedMagicLevel = -1;
|
||||
// fall through
|
||||
case HOPPING:
|
||||
// pause tzhaar timer if logged out without pausing
|
||||
if (config.tzhaarStartTime() != null && config.tzhaarLastTime() == null)
|
||||
{
|
||||
@@ -856,11 +880,6 @@ public class TimersPlugin extends Plugin
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.showImbuedHeart() && actor.getGraphic() == IMBUEDHEART.getGraphicId())
|
||||
{
|
||||
createGameTimer(IMBUEDHEART);
|
||||
}
|
||||
|
||||
if (config.showFreezes())
|
||||
{
|
||||
if (actor.getGraphic() == BIND.getGraphicId())
|
||||
@@ -978,6 +997,37 @@ public class TimersPlugin extends Plugin
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onStatChanged(StatChanged statChanged)
|
||||
{
|
||||
if (statChanged.getSkill() != Skill.MAGIC)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final int boostedMagicLevel = statChanged.getBoostedLevel();
|
||||
|
||||
if (imbuedHeartClickTick < 0
|
||||
|| client.getTickCount() > imbuedHeartClickTick + 3 // allow for 2 ticks of lag
|
||||
|| !config.showImbuedHeart())
|
||||
{
|
||||
lastBoostedMagicLevel = boostedMagicLevel;
|
||||
return;
|
||||
}
|
||||
|
||||
final int boostAmount = boostedMagicLevel - statChanged.getLevel();
|
||||
final int boostChange = boostedMagicLevel - lastBoostedMagicLevel;
|
||||
final int heartBoost = 1 + (statChanged.getLevel() / 10);
|
||||
|
||||
if ((boostAmount == heartBoost || (lastBoostedMagicLevel != -1 && boostChange == heartBoost))
|
||||
&& boostChange > 0)
|
||||
{
|
||||
createGameTimer(IMBUEDHEART);
|
||||
}
|
||||
|
||||
lastBoostedMagicLevel = boostedMagicLevel;
|
||||
}
|
||||
|
||||
private void createStaminaTimer()
|
||||
{
|
||||
Duration duration = Duration.ofMinutes(wasWearingEndurance ? 4 : 2);
|
||||
|
||||
@@ -32,10 +32,15 @@ import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import net.runelite.api.ChatMessageType;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Experience;
|
||||
import net.runelite.api.InventoryID;
|
||||
import net.runelite.api.ItemContainer;
|
||||
import net.runelite.api.ItemID;
|
||||
import net.runelite.api.Skill;
|
||||
import net.runelite.api.events.ChatMessage;
|
||||
import net.runelite.api.events.ItemContainerChanged;
|
||||
import net.runelite.api.events.MenuOptionClicked;
|
||||
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;
|
||||
@@ -53,9 +58,11 @@ import static org.mockito.ArgumentMatchers.nullable;
|
||||
import org.mockito.Mock;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.mockito.stubbing.Answer;
|
||||
@@ -369,4 +376,125 @@ public class TimersPluginTest
|
||||
ElapsedTimer timer = (ElapsedTimer) captor.getValue();
|
||||
assertEquals("00:06", timer.getText());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImbuedHeartBoost()
|
||||
{
|
||||
when(timersConfig.showImbuedHeart()).thenReturn(true);
|
||||
when(client.getTickCount()).thenReturn(100);
|
||||
StatChanged event;
|
||||
|
||||
final MenuOptionClicked imbuedHeartClick = new MenuOptionClicked();
|
||||
imbuedHeartClick.setMenuOption("Invigorate");
|
||||
imbuedHeartClick.setId(ItemID.IMBUED_HEART);
|
||||
timersPlugin.onMenuOptionClicked(imbuedHeartClick);
|
||||
|
||||
when(client.getTickCount()).thenReturn(101);
|
||||
|
||||
for (int level = 1, i = 0; level <= Experience.MAX_REAL_LEVEL; level++, i++)
|
||||
{
|
||||
event = new StatChanged(Skill.MAGIC, 0, level, heartBoostedLevel(level));
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImbuedHeartBoostFromDrained()
|
||||
{
|
||||
when(timersConfig.showImbuedHeart()).thenReturn(true);
|
||||
when(client.getTickCount()).thenReturn(100);
|
||||
|
||||
final MenuOptionClicked imbuedHeartClick = new MenuOptionClicked();
|
||||
imbuedHeartClick.setMenuOption("Invigorate");
|
||||
imbuedHeartClick.setId(ItemID.IMBUED_HEART);
|
||||
timersPlugin.onMenuOptionClicked(imbuedHeartClick);
|
||||
|
||||
when(client.getTickCount()).thenReturn(101);
|
||||
|
||||
for (int level = 1, i = 0; level <= Experience.MAX_REAL_LEVEL; level++, i++)
|
||||
{
|
||||
timersPlugin.onStatChanged(new StatChanged(Skill.MAGIC, 0, level, level - 1));
|
||||
timersPlugin.onStatChanged(new StatChanged(Skill.MAGIC, 0, level, heartBoostedLevel(level) - 1));
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImbuedHeartBoostFromPartialBoost()
|
||||
{
|
||||
when(timersConfig.showImbuedHeart()).thenReturn(true);
|
||||
when(client.getTickCount()).thenReturn(100);
|
||||
|
||||
final MenuOptionClicked imbuedHeartClick = new MenuOptionClicked();
|
||||
imbuedHeartClick.setMenuOption("Invigorate");
|
||||
imbuedHeartClick.setId(ItemID.IMBUED_HEART);
|
||||
timersPlugin.onMenuOptionClicked(imbuedHeartClick);
|
||||
|
||||
when(client.getTickCount()).thenReturn(101);
|
||||
|
||||
for (int level = 10, i = 0; level <= Experience.MAX_REAL_LEVEL; level++, i++)
|
||||
{
|
||||
timersPlugin.onStatChanged(new StatChanged(Skill.MAGIC, 0, level, level + 1));
|
||||
timersPlugin.onStatChanged(new StatChanged(Skill.MAGIC, 0, level, heartBoostedLevel(level)));
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonImbuedHeartBoost()
|
||||
{
|
||||
lenient().when(timersConfig.showImbuedHeart()).thenReturn(true);
|
||||
timersPlugin.onStatChanged(new StatChanged(Skill.MAGIC, 0, 1, 1));
|
||||
|
||||
// Simulate stat changes of imbued heart boost amount without having clicked the imbued heart
|
||||
timersPlugin.onStatChanged(new StatChanged(Skill.MAGIC, 0, 29, 34)); // equal to magic essence
|
||||
timersPlugin.onStatChanged(new StatChanged(Skill.MAGIC, 0, 39, 43)); // equal to magic potion
|
||||
timersPlugin.onStatChanged(new StatChanged(Skill.MAGIC, 0, 49, 54)); // equal to spicy stew
|
||||
timersPlugin.onStatChanged(new StatChanged(Skill.MAGIC, 0, 99, 109));
|
||||
|
||||
verifyNoInteractions(infoBoxManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMagicLevelDrain()
|
||||
{
|
||||
lenient().when(timersConfig.showImbuedHeart()).thenReturn(true);
|
||||
timersPlugin.onStatChanged(new StatChanged(Skill.MAGIC, 0, 1, 1));
|
||||
when(client.getTickCount()).thenReturn(100);
|
||||
|
||||
final MenuOptionClicked imbuedHeartClick = new MenuOptionClicked();
|
||||
imbuedHeartClick.setMenuOption("Invigorate");
|
||||
imbuedHeartClick.setId(ItemID.IMBUED_HEART);
|
||||
timersPlugin.onMenuOptionClicked(imbuedHeartClick);
|
||||
|
||||
when(client.getTickCount()).thenReturn(101);
|
||||
|
||||
// Simulate stat changes draining to the imbued heart boost amount
|
||||
for (int level = 1; level <= Experience.MAX_REAL_LEVEL; level++)
|
||||
{
|
||||
timersPlugin.onStatChanged(new StatChanged(Skill.MAGIC, 0, level, level));
|
||||
timersPlugin.onStatChanged(new StatChanged(Skill.MAGIC, 0, level, heartBoostedLevel(level) + 1));
|
||||
timersPlugin.onStatChanged(new StatChanged(Skill.MAGIC, 0, level, heartBoostedLevel(level)));
|
||||
}
|
||||
|
||||
verifyNoInteractions(infoBoxManager);
|
||||
}
|
||||
|
||||
private static int heartBoostedLevel(final int level)
|
||||
{
|
||||
return level + 1 + (level / 10);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user