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