timers plugin: add support for ring of endurance to stam timer

This commit is contained in:
Adam
2020-06-11 23:02:03 -04:00
parent 9535f066b9
commit 51f0b7752d
4 changed files with 114 additions and 31 deletions

View File

@@ -40,7 +40,7 @@ import static net.runelite.client.util.RSTimeUnit.GAME_TICKS;
@Getter(AccessLevel.PACKAGE) @Getter(AccessLevel.PACKAGE)
enum GameTimer enum GameTimer
{ {
STAMINA(ItemID.STAMINA_POTION4, GameTimerImageType.ITEM, "Stamina", 2, ChronoUnit.MINUTES, true), STAMINA(ItemID.STAMINA_POTION4, GameTimerImageType.ITEM, "Stamina", true),
ANTIFIRE(ItemID.ANTIFIRE_POTION4, GameTimerImageType.ITEM, "Antifire", 6, ChronoUnit.MINUTES), ANTIFIRE(ItemID.ANTIFIRE_POTION4, GameTimerImageType.ITEM, "Antifire", 6, ChronoUnit.MINUTES),
EXANTIFIRE(ItemID.EXTENDED_ANTIFIRE4, GameTimerImageType.ITEM, "Extended antifire", 12, ChronoUnit.MINUTES), EXANTIFIRE(ItemID.EXTENDED_ANTIFIRE4, GameTimerImageType.ITEM, "Extended antifire", 12, ChronoUnit.MINUTES),
OVERLOAD(ItemID.OVERLOAD_4, GameTimerImageType.ITEM, "Overload", 5, ChronoUnit.MINUTES, true), OVERLOAD(ItemID.OVERLOAD_4, GameTimerImageType.ITEM, "Overload", 5, ChronoUnit.MINUTES, true),
@@ -78,8 +78,8 @@ enum GameTimer
DIVINE_MAGIC(ItemID.DIVINE_MAGIC_POTION4, GameTimerImageType.ITEM, "Divine Magic", 5, ChronoUnit.MINUTES), DIVINE_MAGIC(ItemID.DIVINE_MAGIC_POTION4, GameTimerImageType.ITEM, "Divine Magic", 5, ChronoUnit.MINUTES),
DIVINE_BASTION(ItemID.DIVINE_BASTION_POTION4, GameTimerImageType.ITEM, "Divine Bastion", 5, ChronoUnit.MINUTES), DIVINE_BASTION(ItemID.DIVINE_BASTION_POTION4, GameTimerImageType.ITEM, "Divine Bastion", 5, ChronoUnit.MINUTES),
DIVINE_BATTLEMAGE(ItemID.DIVINE_BATTLEMAGE_POTION4, GameTimerImageType.ITEM, "Divine Battlemage", 5, ChronoUnit.MINUTES), DIVINE_BATTLEMAGE(ItemID.DIVINE_BATTLEMAGE_POTION4, GameTimerImageType.ITEM, "Divine Battlemage", 5, ChronoUnit.MINUTES),
ANTIPOISON(ItemID.ANTIPOISON4, GameTimerImageType.ITEM, "Antipoison"), ANTIPOISON(ItemID.ANTIPOISON4, GameTimerImageType.ITEM, "Antipoison", false),
ANTIVENOM(ItemID.ANTIVENOM4, GameTimerImageType.ITEM, "Anti-venom"); ANTIVENOM(ItemID.ANTIVENOM4, GameTimerImageType.ITEM, "Anti-venom", false);
@Nullable @Nullable
private final Duration duration; private final Duration duration;
@@ -110,12 +110,12 @@ enum GameTimer
this(imageId, idType, description, null, time, unit, false); this(imageId, idType, description, null, time, unit, false);
} }
GameTimer(int imageId, GameTimerImageType idType, String description) GameTimer(int imageId, GameTimerImageType idType, String description, boolean removedOnDeath)
{ {
this.duration = null; this.duration = null;
this.graphicId = null; this.graphicId = null;
this.description = description; this.description = description;
this.removedOnDeath = false; this.removedOnDeath = removedOnDeath;
this.imageId = imageId; this.imageId = imageId;
this.imageType = idType; this.imageType = idType;
} }

View File

@@ -27,6 +27,7 @@ package net.runelite.client.plugins.timers;
import com.google.inject.Provides; import com.google.inject.Provides;
import java.time.Duration; import java.time.Duration;
import java.time.Instant;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.inject.Inject; import javax.inject.Inject;
@@ -107,6 +108,7 @@ public class TimersPlugin extends Plugin
private static final String SUPER_ANTIFIRE_EXPIRED_MESSAGE = "<col=7f007f>Your super antifire potion has expired.</col>"; private static final String SUPER_ANTIFIRE_EXPIRED_MESSAGE = "<col=7f007f>Your super antifire potion has expired.</col>";
private static final String KILLED_TELEBLOCK_OPPONENT_TEXT = "Your Tele Block has been removed because you killed "; private static final String KILLED_TELEBLOCK_OPPONENT_TEXT = "Your Tele Block has been removed because you killed ";
private static final String PRAYER_ENHANCE_EXPIRED = "<col=ff0000>Your prayer enhance effect has worn off.</col>"; private static final String PRAYER_ENHANCE_EXPIRED = "<col=ff0000>Your prayer enhance effect has worn off.</col>";
private static final String ENDURANCE_EFFECT_MESSAGE = "Your Ring of endurance doubles the duration of your stamina potion's effect.";
private static final Pattern DEADMAN_HALF_TELEBLOCK_PATTERN = Pattern.compile("A Tele Block spell has been cast on you by (.+)\\. It will expire in 1 minute, 15 seconds\\.</col>"); private static final Pattern DEADMAN_HALF_TELEBLOCK_PATTERN = Pattern.compile("A Tele Block spell has been cast on you by (.+)\\. It will expire in 1 minute, 15 seconds\\.</col>");
private static final Pattern FULL_TELEBLOCK_PATTERN = Pattern.compile("A Tele Block spell has been cast on you by (.+)\\. It will expire in 5 minutes\\.</col>"); private static final Pattern FULL_TELEBLOCK_PATTERN = Pattern.compile("A Tele Block spell has been cast on you by (.+)\\. It will expire in 5 minutes\\.</col>");
@@ -118,6 +120,9 @@ public class TimersPlugin extends Plugin
private TimerTimer freezeTimer; private TimerTimer freezeTimer;
private int freezeTime = -1; // time frozen, in game ticks private int freezeTime = -1; // time frozen, in game ticks
private TimerTimer staminaTimer;
private boolean wasWearingEndurance;
private int lastRaidVarb; private int lastRaidVarb;
private int lastWildernessVarb; private int lastWildernessVarb;
private int lastVengCooldownVarb; private int lastVengCooldownVarb;
@@ -163,6 +168,7 @@ public class TimersPlugin extends Plugin
widgetHiddenChangedOnPvpWorld = false; widgetHiddenChangedOnPvpWorld = false;
lastPoisonVarp = 0; lastPoisonVarp = 0;
nextPoisonTick = 0; nextPoisonTick = 0;
staminaTimer = null;
} }
@Subscribe @Subscribe
@@ -379,7 +385,7 @@ public class TimersPlugin extends Plugin
|| event.getId() == ItemID.EGNIOL_POTION_4)) || event.getId() == ItemID.EGNIOL_POTION_4))
{ {
// Needs menu option hook because mixes use a common drink message, distinct from their standard potion messages // Needs menu option hook because mixes use a common drink message, distinct from their standard potion messages
createGameTimer(STAMINA); createStaminaTimer();
return; return;
} }
@@ -438,14 +444,20 @@ public class TimersPlugin extends Plugin
return; return;
} }
if (config.showStamina() && (event.getMessage().equals(STAMINA_DRINK_MESSAGE) || event.getMessage().equals(STAMINA_SHARED_DRINK_MESSAGE))) if (event.getMessage().equals(ENDURANCE_EFFECT_MESSAGE))
{ {
createGameTimer(STAMINA); wasWearingEndurance = true;
} }
if (config.showStamina() && (event.getMessage().equals(STAMINA_EXPIRED_MESSAGE) || event.getMessage().equals(GAUNTLET_ENTER_MESSAGE))) if (config.showStamina() && (event.getMessage().equals(STAMINA_DRINK_MESSAGE) || event.getMessage().equals(STAMINA_SHARED_DRINK_MESSAGE)))
{
createStaminaTimer();
}
if (event.getMessage().equals(STAMINA_EXPIRED_MESSAGE) || event.getMessage().equals(GAUNTLET_ENTER_MESSAGE))
{ {
removeGameTimer(STAMINA); removeGameTimer(STAMINA);
staminaTimer = null;
} }
if (config.showAntiFire() && event.getMessage().equals(ANTIFIRE_DRINK_MESSAGE)) if (config.showAntiFire() && event.getMessage().equals(ANTIFIRE_DRINK_MESSAGE))
@@ -797,34 +809,50 @@ public class TimersPlugin extends Plugin
} }
/** /**
* remove SOTD timer when weapon is changed * Remove SOTD timer and update stamina timer when equipment is changed.
*
* @param itemContainerChanged
*/ */
@Subscribe @Subscribe
public void onItemContainerChanged(ItemContainerChanged itemContainerChanged) public void onItemContainerChanged(ItemContainerChanged itemContainerChanged)
{ {
ItemContainer container = itemContainerChanged.getItemContainer(); if (itemContainerChanged.getContainerId() != InventoryID.EQUIPMENT.getId())
if (container == client.getItemContainer(InventoryID.EQUIPMENT))
{ {
Item weapon = container.getItem(EquipmentInventorySlot.WEAPON.getSlotIdx()); return;
}
if (weapon == null) ItemContainer container = itemContainerChanged.getItemContainer();
{
removeGameTimer(STAFF_OF_THE_DEAD);
return;
}
switch (weapon.getId()) Item weapon = container.getItem(EquipmentInventorySlot.WEAPON.getSlotIdx());
if (weapon == null ||
(weapon.getId() != ItemID.STAFF_OF_THE_DEAD &&
weapon.getId() != ItemID.TOXIC_STAFF_OF_THE_DEAD &&
weapon.getId() != ItemID.STAFF_OF_LIGHT &&
weapon.getId() != ItemID.TOXIC_STAFF_UNCHARGED))
{
// remove sotd timer if the staff has been unwielded
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))
{ {
case ItemID.STAFF_OF_THE_DEAD: wasWearingEndurance = false;
case ItemID.TOXIC_STAFF_OF_THE_DEAD: if (staminaTimer != null)
case ItemID.STAFF_OF_LIGHT: {
case ItemID.TOXIC_STAFF_UNCHARGED: // Remaining duration gets divided by 2
// don't reset timer if still wielding staff Duration remainingDuration = Duration.between(Instant.now(), staminaTimer.getEndTime()).dividedBy(2);
return; // This relies on the chat message to be removed, which could be after the timer has been culled;
default: // so check there is still remaining time
removeGameTimer(STAFF_OF_THE_DEAD); if (!remainingDuration.isNegative() && !remainingDuration.isZero())
{
log.debug("Halving stamina timer");
staminaTimer.setDuration(remainingDuration);
}
}
} }
} }
} }
@@ -856,6 +884,12 @@ 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) private TimerTimer createGameTimer(final GameTimer timer)
{ {
if (timer.getDuration() == null) if (timer.getDuration() == null)

View File

@@ -39,8 +39,8 @@ import net.runelite.client.plugins.Plugin;
public class Timer extends InfoBox public class Timer extends InfoBox
{ {
private final Instant startTime; private final Instant startTime;
private final Instant endTime; private Instant endTime;
private final Duration duration; private Duration duration;
public Timer(long period, ChronoUnit unit, BufferedImage image, Plugin plugin) public Timer(long period, ChronoUnit unit, BufferedImage image, Plugin plugin)
{ {
@@ -94,4 +94,9 @@ public class Timer extends InfoBox
return timeLeft.isZero() || timeLeft.isNegative(); return timeLeft.isZero() || timeLeft.isNegative();
} }
public void setDuration(Duration duration)
{
this.duration = duration;
endTime = startTime.plus(duration);
}
} }

View File

@@ -28,16 +28,21 @@ import com.google.inject.Guice;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.testing.fieldbinder.Bind; import com.google.inject.testing.fieldbinder.Bind;
import com.google.inject.testing.fieldbinder.BoundFieldModule; import com.google.inject.testing.fieldbinder.BoundFieldModule;
import java.time.Duration;
import java.util.EnumSet; import java.util.EnumSet;
import net.runelite.api.ChatMessageType; import net.runelite.api.ChatMessageType;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.InventoryID;
import net.runelite.api.ItemContainer;
import net.runelite.api.WorldType; import net.runelite.api.WorldType;
import net.runelite.api.events.ChatMessage; import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.ItemContainerChanged;
import net.runelite.client.game.ItemManager; import net.runelite.client.game.ItemManager;
import net.runelite.client.game.SpriteManager; import net.runelite.client.game.SpriteManager;
import net.runelite.client.ui.overlay.infobox.InfoBox; import net.runelite.client.ui.overlay.infobox.InfoBox;
import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -45,6 +50,7 @@ import org.mockito.ArgumentCaptor;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import org.mockito.Mock; import org.mockito.Mock;
import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
@@ -189,4 +195,42 @@ public class TimersPluginTest
verify(infoBoxManager, atLeastOnce()).removeIf(any()); verify(infoBoxManager, atLeastOnce()).removeIf(any());
} }
@Test
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);
ArgumentCaptor<InfoBox> captor = ArgumentCaptor.forClass(InfoBox.class);
verify(infoBoxManager).addInfoBox(captor.capture());
TimerTimer infoBox = (TimerTimer) captor.getValue();
assertEquals(GameTimer.STAMINA, infoBox.getTimer());
assertEquals(Duration.ofMinutes(2), infoBox.getDuration());
}
@Test
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);
ArgumentCaptor<InfoBox> captor = ArgumentCaptor.forClass(InfoBox.class);
verify(infoBoxManager).addInfoBox(captor.capture());
TimerTimer infoBox = (TimerTimer) captor.getValue();
assertEquals(GameTimer.STAMINA, infoBox.getTimer());
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
int mins = (int) infoBox.getDuration().toMinutes();
assertTrue(mins == 1 || mins == 2);
}
} }