diff --git a/runelite-api/src/main/java/net/runelite/api/Varbits.java b/runelite-api/src/main/java/net/runelite/api/Varbits.java index b39df1a823..14fe41088d 100644 --- a/runelite-api/src/main/java/net/runelite/api/Varbits.java +++ b/runelite-api/src/main/java/net/runelite/api/Varbits.java @@ -47,6 +47,21 @@ public final class Varbits */ public static final int RUN_SLOWED_DEPLETION_ACTIVE = 25; + /** + * Stamina effect timer + * Number of game ticks remaining on stamina effect in intervals of 10; for a value X there are 10 * X game ticks remaining. + * The stamina effect expires once this reaches 0. + */ + public static final int STAMINA_EFFECT = 24; + + /** + * Ring of endurance effect timer, stamina duration extended from using the ring of endurance + * Number of game ticks remaining on ring of endurance effect in intervals of 10; for a value X there are 10 * X game ticks remaining. + * Unequipping the ring of endurance will cause this to change to 0. + * When this reaches 0, {@link #STAMINA_EFFECT} will begin counting down. + */ + public static final int RING_OF_ENDURANCE_EFFECT = 10385; + /** * If scrollbar in resizable mode chat is on the left */ diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java index 57da5b9b95..34713ea754 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java @@ -102,20 +102,15 @@ public class TimersPlugin extends Plugin private static final String EXTENDED_ANTIFIRE_DRINK_MESSAGE = "You drink some of your extended antifire potion."; private static final String EXTENDED_SUPER_ANTIFIRE_DRINK_MESSAGE = "You drink some of your extended super antifire potion."; private static final String FROZEN_MESSAGE = "You have been frozen!"; - private static final String GAUNTLET_ENTER_MESSAGE = "You enter the Gauntlet."; private static final String GOD_WARS_ALTAR_MESSAGE = "you recharge your prayer."; private static final String MAGIC_IMBUE_EXPIRED_MESSAGE = "Your Magic Imbue charge has ended."; private static final String MAGIC_IMBUE_MESSAGE = "You are charged to combine runes!"; private static final String STAFF_OF_THE_DEAD_SPEC_EXPIRED_MESSAGE = "Your protection fades away"; private static final String STAFF_OF_THE_DEAD_SPEC_MESSAGE = "Spirits of deceased evildoers offer you their protection"; - private static final String STAMINA_DRINK_MESSAGE = "You drink some of your stamina potion."; - private static final String STAMINA_SHARED_DRINK_MESSAGE = "You have received a shared dose of stamina potion."; - private static final String STAMINA_EXPIRED_MESSAGE = "Your stamina potion has expired."; private static final String SUPER_ANTIFIRE_DRINK_MESSAGE = "You drink some of your super antifire potion"; private static final String SUPER_ANTIFIRE_EXPIRED_MESSAGE = "Your super antifire potion has expired."; private static final String KILLED_TELEBLOCK_OPPONENT_TEXT = "Your Tele Block has been removed because you killed "; private static final String PRAYER_ENHANCE_EXPIRED = "Your prayer enhance effect has worn off."; - private static final String ENDURANCE_EFFECT_MESSAGE = "Your Ring of endurance doubles the duration of your stamina potion's effect."; private static final String SHADOW_VEIL_MESSAGE = ">Your thieving abilities have been enhanced."; private static final String DEATH_CHARGE_MESSAGE = ">Upon the death of your next foe, some of your special attack energy will be restored."; private static final String DEATH_CHARGE_ACTIVATE_MESSAGE = ">Some of your special attack energy has been restored."; @@ -144,7 +139,6 @@ public class TimersPlugin extends Plugin private int freezeTime = -1; // time frozen, in game ticks private TimerTimer staminaTimer; - private boolean wasWearingEndurance; private int lastRaidVarb; private int lastVengCooldownVarb; @@ -154,6 +148,7 @@ public class TimersPlugin extends Plugin private int lastCorruptionVarb; private int lastHomeTeleport; private int lastMinigameTeleport; + private int lastStaminaEffect; private int lastImbuedHeartVarb; private boolean imbuedHeartTimerActive; private int nextPoisonTick; @@ -209,6 +204,7 @@ public class TimersPlugin extends Plugin lastImbuedHeartVarb = 0; lastHomeTeleport = 0; lastMinigameTeleport = 0; + lastStaminaEffect = 0; } @Subscribe @@ -223,6 +219,11 @@ public class TimersPlugin extends Plugin int imbuedHeartCooldownVarb = client.getVarbitValue(Varbits.IMBUED_HEART_COOLDOWN); int homeTeleportVarp = client.getVar(VarPlayer.LAST_HOME_TELEPORT); int minigameTeleportVarp = client.getVar(VarPlayer.LAST_MINIGAME_TELEPORT); + int staminaEffectActive = client.getVarbitValue(Varbits.RUN_SLOWED_DEPLETION_ACTIVE); + int staminaPotionEffectVarb = client.getVarbitValue(Varbits.STAMINA_EFFECT); + int enduranceRingEffectVarb = client.getVarbitValue(Varbits.RING_OF_ENDURANCE_EFFECT); + + final int totalStaminaEffect = staminaPotionEffectVarb + enduranceRingEffectVarb; if (lastRaidVarb != raidVarb) { @@ -341,6 +342,33 @@ public class TimersPlugin extends Plugin checkTeleport(VarPlayer.LAST_MINIGAME_TELEPORT); lastMinigameTeleport = minigameTeleportVarp; } + + // staminaEffectActive is checked to match https://github.com/Joshua-F/cs2-scripts/blob/741271f0c3395048c1bad4af7881a13734516adf/scripts/%5Bproc%2Cbuff_bar_get_value%5D.cs2#L25 + if (staminaEffectActive == 1 && lastStaminaEffect != totalStaminaEffect && config.showStamina()) + { + final Duration staminaDuration = Duration.of(10L * totalStaminaEffect, RSTimeUnit.GAME_TICKS); + + if (staminaTimer == null && totalStaminaEffect > 0) + { + staminaTimer = createGameTimer(STAMINA, staminaDuration); + } + else if (totalStaminaEffect == 0) + { + removeGameTimer(STAMINA); + staminaTimer = null; + } + else + { + Instant endInstant = Instant.now().plus(staminaDuration); + int timeDifference = (int) Duration.between(staminaTimer.getEndTime(), endInstant).getSeconds(); + if (timeDifference != 0) + { + Duration remainingDuration = Duration.between(staminaTimer.getStartTime(), endInstant); + staminaTimer.setDuration(remainingDuration); + } + } + lastStaminaEffect = totalStaminaEffect; + } } @Subscribe @@ -372,6 +400,7 @@ public class TimersPlugin extends Plugin if (!config.showStamina()) { removeGameTimer(STAMINA); + staminaTimer = null; } if (!config.showOverload()) @@ -468,18 +497,6 @@ public class TimersPlugin extends Plugin { if (event.isItemOp() && event.getMenuOption().equals("Drink")) { - if ((event.getItemId() == ItemID.STAMINA_MIX1 - || event.getItemId() == ItemID.STAMINA_MIX2 - || event.getItemId() == ItemID.EGNIOL_POTION_1 - || event.getItemId() == ItemID.EGNIOL_POTION_2 - || event.getItemId() == ItemID.EGNIOL_POTION_3 - || event.getItemId() == ItemID.EGNIOL_POTION_4) - && config.showStamina()) - { - // Needs menu option hook because mixes use a common drink message, distinct from their standard potion messages - createStaminaTimer(); - return; - } if ((event.getItemId() == ItemID.ANTIFIRE_MIX1 || event.getItemId() == ItemID.ANTIFIRE_MIX2) @@ -550,22 +567,6 @@ public class TimersPlugin extends Plugin createGameTimer(ABYSSAL_SIRE_STUN); } - if (message.equals(ENDURANCE_EFFECT_MESSAGE)) - { - wasWearingEndurance = true; - } - - if (config.showStamina() && (message.equals(STAMINA_DRINK_MESSAGE) || message.equals(STAMINA_SHARED_DRINK_MESSAGE))) - { - createStaminaTimer(); - } - - if (message.equals(STAMINA_EXPIRED_MESSAGE) || message.equals(GAUNTLET_ENTER_MESSAGE)) - { - removeGameTimer(STAMINA); - staminaTimer = null; - } - if (config.showAntiFire() && message.equals(ANTIFIRE_DRINK_MESSAGE)) { createGameTimer(ANTIFIRE); @@ -1064,7 +1065,7 @@ public class TimersPlugin extends Plugin } /** - * Remove SOTD timer and update stamina timer when equipment is changed. + * Remove SOTD timer when equipment is changed. */ @Subscribe public void onItemContainerChanged(ItemContainerChanged itemContainerChanged) @@ -1087,29 +1088,6 @@ public class TimersPlugin extends Plugin removeGameTimer(STAFF_OF_THE_DEAD); } - if (wasWearingEndurance) - { - Item ring = container.getItem(EquipmentInventorySlot.RING.getSlotIdx()); - - // when using the last ring charge the ring changes to the uncharged version, ignore that and don't - // halve the timer - if (ring == null || (ring.getId() != ItemID.RING_OF_ENDURANCE && ring.getId() != ItemID.RING_OF_ENDURANCE_UNCHARGED_24844)) - { - wasWearingEndurance = false; - if (staminaTimer != null) - { - // Remaining duration gets divided by 2 - Duration remainingDuration = Duration.between(Instant.now(), staminaTimer.getEndTime()).dividedBy(2); - // This relies on the chat message to be removed, which could be after the timer has been culled; - // so check there is still remaining time - if (!remainingDuration.isNegative() && !remainingDuration.isZero()) - { - log.debug("Halving stamina timer"); - staminaTimer.setDuration(remainingDuration); - } - } - } - } } @Subscribe @@ -1139,12 +1117,6 @@ public class TimersPlugin extends Plugin } } - private void createStaminaTimer() - { - Duration duration = Duration.ofMinutes(wasWearingEndurance ? 4 : 2); - staminaTimer = createGameTimer(STAMINA, duration); - } - private TimerTimer createGameTimer(final GameTimer timer) { if (timer.getDuration() == null) diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/timers/TimersPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/timers/TimersPluginTest.java index 35a85c7034..9e6aea3616 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/timers/TimersPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/timers/TimersPluginTest.java @@ -33,12 +33,9 @@ import java.time.Instant; import java.util.function.Predicate; import net.runelite.api.ChatMessageType; import net.runelite.api.Client; -import net.runelite.api.InventoryID; -import net.runelite.api.ItemContainer; import net.runelite.api.Skill; import net.runelite.api.Varbits; import net.runelite.api.events.ChatMessage; -import net.runelite.api.events.ItemContainerChanged; import net.runelite.api.events.VarbitChanged; import net.runelite.client.game.ItemManager; import net.runelite.client.game.SpriteManager; @@ -59,7 +56,6 @@ 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.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -236,8 +232,10 @@ public class TimersPluginTest public void testStamina() { when(timersConfig.showStamina()).thenReturn(true); - ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.SPAM, "", "You drink some of your stamina potion.", "", 0); - timersPlugin.onChatMessage(chatMessage); + when(client.getVarbitValue(Varbits.RUN_SLOWED_DEPLETION_ACTIVE)).thenReturn(1); + when(client.getVarbitValue(Varbits.STAMINA_EFFECT)).thenReturn(20); + when(client.getVarbitValue(Varbits.RING_OF_ENDURANCE_EFFECT)).thenReturn(0); + timersPlugin.onVarbitChanged(new VarbitChanged()); ArgumentCaptor captor = ArgumentCaptor.forClass(InfoBox.class); verify(infoBoxManager).addInfoBox(captor.capture()); @@ -264,12 +262,10 @@ public class TimersPluginTest public void testEndurance() { when(timersConfig.showStamina()).thenReturn(true); - - ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.SPAM, "", "Your Ring of endurance doubles the duration of your stamina potion's effect.", "", 0); - timersPlugin.onChatMessage(chatMessage); - - chatMessage = new ChatMessage(null, ChatMessageType.SPAM, "", "You drink some of your stamina potion.", "", 0); - timersPlugin.onChatMessage(chatMessage); + when(client.getVarbitValue(Varbits.RUN_SLOWED_DEPLETION_ACTIVE)).thenReturn(1); + when(client.getVarbitValue(Varbits.STAMINA_EFFECT)).thenReturn(20); + when(client.getVarbitValue(Varbits.RING_OF_ENDURANCE_EFFECT)).thenReturn(20); + timersPlugin.onVarbitChanged(new VarbitChanged()); ArgumentCaptor captor = ArgumentCaptor.forClass(InfoBox.class); verify(infoBoxManager).addInfoBox(captor.capture()); @@ -278,10 +274,10 @@ public class TimersPluginTest assertEquals(Duration.ofMinutes(4), infoBox.getDuration()); // unwield ring - timersPlugin.onItemContainerChanged(new ItemContainerChanged(InventoryID.EQUIPMENT.getId(), mock(ItemContainer.class))); - // some time has elapsed in the test; this should be just under 2 mins + when(client.getVarbitValue(Varbits.RING_OF_ENDURANCE_EFFECT)).thenReturn(0); + timersPlugin.onVarbitChanged(new VarbitChanged()); int mins = (int) infoBox.getDuration().toMinutes(); - assertTrue(mins == 1 || mins == 2); + assertEquals(2, mins); } @Test