diff --git a/runelite-api/src/main/java/net/runelite/api/Client.java b/runelite-api/src/main/java/net/runelite/api/Client.java index 7bf4c02360..52b85cb546 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -739,15 +739,6 @@ public interface Client extends GameShell */ String getVar(VarClientStr varClientStr); - /** - * Gets the value of a given VarPlayer. - * - * @param varpId the VarPlayer id - * @return the value - */ - @VisibleForExternalPlugins - int getVarpValue(int varpId); - /** * Gets the value of a given Varbit. * @@ -813,16 +804,6 @@ public interface Client extends GameShell */ int getVarpValue(int[] varps, int varpId); - /** - * Sets the value of a given VarPlayer. - * - * @param varps passed varps - * @param varpId the VarpPlayer id - * @param value the value - * @see VarPlayer#id - */ - void setVarpValue(int[] varps, int varpId, int value); - /** * Sets the value of a given variable. * @@ -2087,7 +2068,6 @@ public interface Client extends GameShell //TODO: Implement VarbitComposition getVarbit(Integer id); - //TODO: Implement Widget getWidget(int param1); //TODO: Implement diff --git a/runelite-api/src/main/java/net/runelite/api/events/SpotAnimationChanged.java b/runelite-api/src/main/java/net/runelite/api/events/GraphicChanged.java similarity index 91% rename from runelite-api/src/main/java/net/runelite/api/events/SpotAnimationChanged.java rename to runelite-api/src/main/java/net/runelite/api/events/GraphicChanged.java index 1d2721ad78..ee7b781c82 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/SpotAnimationChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/GraphicChanged.java @@ -19,7 +19,7 @@ import net.runelite.api.Actor; * @see net.runelite.api.GraphicID */ @Data -public class SpotAnimationChanged implements Event +public class GraphicChanged implements Event { /** * The actor that has had their graphic changed. diff --git a/runelite-api/src/main/java/net/runelite/api/events/NpcChanged.java b/runelite-api/src/main/java/net/runelite/api/events/NpcChanged.java index c21b62403e..606ebcbb7e 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/NpcChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/NpcChanged.java @@ -26,6 +26,7 @@ package net.runelite.api.events; import lombok.Value; import net.runelite.api.NPC; +import net.runelite.api.NPCComposition; /** * Fires after the composition of an {@link NPC} changes. @@ -37,4 +38,10 @@ public class NpcChanged implements Event * The NPC of which the composition changed. */ NPC npc; + + + /** + * The old composition of the NPC + */ + NPCComposition old; } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/ElapsedTimer.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/ElapsedTimer.java new file mode 100644 index 0000000000..55a06025d4 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/ElapsedTimer.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2019, winterdaze + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.timers; + +import lombok.Getter; +import net.runelite.client.ui.overlay.infobox.InfoBox; +import java.awt.image.BufferedImage; +import java.awt.Color; +import java.time.Duration; +import java.time.Instant; +import org.apache.commons.lang3.time.DurationFormatUtils; + +@Getter +class ElapsedTimer extends InfoBox +{ + private final Instant startTime; + private final Instant lastTime; + + // Creates a timer that counts up if lastTime is null, or a paused timer if lastTime is defined + ElapsedTimer(BufferedImage image, TimersPlugin plugin, Instant startTime, Instant lastTime) + { + super(image, plugin); + this.startTime = startTime; + this.lastTime = lastTime; + } + + @Override + public String getText() + { + if (startTime == null) + { + return null; + } + + Duration time = Duration.between(startTime, lastTime == null ? Instant.now() : lastTime); + final String formatString = "mm:ss"; + return DurationFormatUtils.formatDuration(time.toMillis(), formatString, true); + } + + @Override + public Color getTextColor() + { + return Color.WHITE; + } + + @Override + public String getTooltip() + { + Duration time = Duration.between(startTime, lastTime == null ? Instant.now() : lastTime); + return "Elapsed time: " + DurationFormatUtils.formatDuration(time.toMillis(), "HH:mm:ss", true); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/GameIndicator.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/GameIndicator.java new file mode 100644 index 0000000000..55151e340f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/GameIndicator.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018, Tyler + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.timers; + +import java.awt.Color; +import lombok.AccessLevel; +import lombok.Getter; +import net.runelite.api.SpriteID; + +@Getter(AccessLevel.PACKAGE) +enum GameIndicator +{ + VENGEANCE_ACTIVE(SpriteID.SPELL_VENGEANCE_OTHER, GameTimerImageType.SPRITE, "Vengeance active"); + + private final String description; + private String text; + private Color textColor; + private final int imageId; + private final GameTimerImageType imageType; + + GameIndicator(int imageId, GameTimerImageType idType, String description, String text, Color textColor) + { + this.imageId = imageId; + this.imageType = idType; + this.description = description; + this.text = text; + this.textColor = textColor; + } + + GameIndicator(int imageId, GameTimerImageType idType, String description) + { + this(imageId, idType, description, "", null); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/GameTimer.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/GameTimer.java new file mode 100644 index 0000000000..a751b7f3f9 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/GameTimer.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2017, Seth + * Copyright (c) 2017, Adam + * Copyright (c) 2018, Jordan Atwood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.timers; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalUnit; +import javax.annotation.Nullable; +import lombok.AccessLevel; +import lombok.Getter; +import net.runelite.api.GraphicID; +import net.runelite.api.ItemID; +import net.runelite.api.SpriteID; +import static net.runelite.client.util.RSTimeUnit.GAME_TICKS; + +@Getter(AccessLevel.PACKAGE) +enum GameTimer +{ + STAMINA(ItemID.STAMINA_POTION4, GameTimerImageType.ITEM, "Stamina", true), + ANTIFIRE(ItemID.ANTIFIRE_POTION4, GameTimerImageType.ITEM, "Antifire", 6, ChronoUnit.MINUTES), + EXANTIFIRE(ItemID.EXTENDED_ANTIFIRE4, GameTimerImageType.ITEM, "Extended antifire", 12, ChronoUnit.MINUTES), + OVERLOAD(ItemID.OVERLOAD_4, GameTimerImageType.ITEM, "Overload", 5, ChronoUnit.MINUTES, true), + CANNON(ItemID.CANNON_BARRELS, GameTimerImageType.ITEM, "Cannon", 25, ChronoUnit.MINUTES), + MAGICIMBUE(SpriteID.SPELL_MAGIC_IMBUE, GameTimerImageType.SPRITE, "Magic imbue", 21, GAME_TICKS), + SUPERANTIFIRE(ItemID.SUPER_ANTIFIRE_POTION4, GameTimerImageType.ITEM, "Super antifire", 3, ChronoUnit.MINUTES), + BIND(SpriteID.SPELL_BIND, GameTimerImageType.SPRITE, "Bind", GraphicID.BIND, 8, GAME_TICKS, true), + SNARE(SpriteID.SPELL_SNARE, GameTimerImageType.SPRITE, "Snare", GraphicID.SNARE, 16, GAME_TICKS, true), + ENTANGLE(SpriteID.SPELL_ENTANGLE, GameTimerImageType.SPRITE, "Entangle", GraphicID.ENTANGLE, 24, GAME_TICKS, true), + ICERUSH(SpriteID.SPELL_ICE_RUSH, GameTimerImageType.SPRITE, "Ice rush", GraphicID.ICE_RUSH, 8, GAME_TICKS, true), + 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), + 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), + PRAYER_ENHANCE(ItemID.PRAYER_ENHANCE_4, GameTimerImageType.ITEM, "Prayer enhance", 290, ChronoUnit.SECONDS, true), + GOD_WARS_ALTAR(SpriteID.SKILL_PRAYER, GameTimerImageType.SPRITE, "God wars altar", 10, ChronoUnit.MINUTES), + CHARGE(SpriteID.SPELL_CHARGE, GameTimerImageType.SPRITE, "Charge", 6, ChronoUnit.MINUTES), + STAFF_OF_THE_DEAD(ItemID.STAFF_OF_THE_DEAD, GameTimerImageType.ITEM, "Staff of the Dead", 1, ChronoUnit.MINUTES), + ABYSSAL_SIRE_STUN(ItemID.ABYSSAL_ORPHAN, GameTimerImageType.ITEM, "Abyssal Sire Stun", 30, ChronoUnit.SECONDS, true), + HOME_TELEPORT(SpriteID.SPELL_LUMBRIDGE_HOME_TELEPORT, GameTimerImageType.SPRITE, "Home Teleport", 30, ChronoUnit.MINUTES), + MINIGAME_TELEPORT(SpriteID.TAB_QUESTS_RED_MINIGAMES, GameTimerImageType.SPRITE, "Minigame Teleport", 20, ChronoUnit.MINUTES), + DRAGON_FIRE_SHIELD(ItemID.DRAGONFIRE_SHIELD_11284, GameTimerImageType.ITEM, "Dragonfire Shield Special", 115, ChronoUnit.SECONDS), + DIVINE_SUPER_ATTACK(ItemID.DIVINE_SUPER_ATTACK_POTION4, GameTimerImageType.ITEM, "Divine Super Attack", 5, ChronoUnit.MINUTES), + DIVINE_SUPER_STRENGTH(ItemID.DIVINE_SUPER_STRENGTH_POTION4, GameTimerImageType.ITEM, "Divine Super Strength", 5, ChronoUnit.MINUTES), + DIVINE_SUPER_DEFENCE(ItemID.DIVINE_SUPER_DEFENCE_POTION4, GameTimerImageType.ITEM, "Divine Super Defence", 5, ChronoUnit.MINUTES), + DIVINE_SUPER_COMBAT(ItemID.DIVINE_SUPER_COMBAT_POTION4, GameTimerImageType.ITEM, "Divine Super Combat", 5, ChronoUnit.MINUTES), + DIVINE_RANGING(ItemID.DIVINE_RANGING_POTION4, GameTimerImageType.ITEM, "Divine Ranging", 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_BATTLEMAGE(ItemID.DIVINE_BATTLEMAGE_POTION4, GameTimerImageType.ITEM, "Divine Battlemage", 5, ChronoUnit.MINUTES), + ANTIPOISON(ItemID.ANTIPOISON4, GameTimerImageType.ITEM, "Antipoison", false), + ANTIVENOM(ItemID.ANTIVENOM4, GameTimerImageType.ITEM, "Anti-venom", false), + TELEBLOCK(SpriteID.SPELL_TELE_BLOCK, GameTimerImageType.SPRITE, "Teleblock", true); + + @Nullable + private final Duration duration; + @Nullable + private final Integer graphicId; + private final String description; + private final boolean removedOnDeath; + private final int imageId; + private final GameTimerImageType imageType; + + GameTimer(int imageId, GameTimerImageType idType, String description, Integer graphicId, long time, TemporalUnit unit, boolean removedOnDeath) + { + this.description = description; + this.graphicId = graphicId; + this.duration = Duration.of(time, unit); + this.imageId = imageId; + this.imageType = idType; + this.removedOnDeath = removedOnDeath; + } + + GameTimer(int imageId, GameTimerImageType idType, String description, long time, TemporalUnit unit, boolean removeOnDeath) + { + this(imageId, idType, description, null, time, unit, removeOnDeath); + } + + GameTimer(int imageId, GameTimerImageType idType, String description, long time, TemporalUnit unit) + { + this(imageId, idType, description, null, time, unit, false); + } + + GameTimer(int imageId, GameTimerImageType idType, String description, boolean removedOnDeath) + { + this.duration = null; + this.graphicId = null; + this.description = description; + this.removedOnDeath = removedOnDeath; + this.imageId = imageId; + this.imageType = idType; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/GameTimerImageType.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/GameTimerImageType.java new file mode 100644 index 0000000000..1d51d05e34 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/GameTimerImageType.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018, Jordan Atwood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.timers; + +enum GameTimerImageType +{ + ITEM, + SPRITE +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/IndicatorIndicator.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/IndicatorIndicator.java new file mode 100644 index 0000000000..03990ce5ed --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/IndicatorIndicator.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018, Tyler + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.timers; + +import java.awt.Color; +import lombok.Getter; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.ui.overlay.infobox.InfoBox; +import net.runelite.client.ui.overlay.infobox.InfoBoxPriority; + +public class IndicatorIndicator extends InfoBox +{ + @Getter + private final GameIndicator indicator; + + IndicatorIndicator(GameIndicator indicator, Plugin plugin) + { + super(null, plugin); + this.indicator = indicator; + setPriority(InfoBoxPriority.MED); + } + + @Override + public String getText() + { + return indicator.getText(); + } + + @Override + public Color getTextColor() + { + return indicator.getTextColor(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TeleportWidget.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TeleportWidget.java new file mode 100644 index 0000000000..ea3eb66b6a --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TeleportWidget.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018, Jordan Atwood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.timers; + +import com.google.common.collect.ImmutableList; +import java.util.Collection; +import javax.annotation.Nullable; +import net.runelite.api.widgets.WidgetInfo; + +enum TeleportWidget +{ + HOME_TELEPORT, + MINIGAME_TELEPORT, + TRAILBLAZER_AREA_TELEPORT, + ; + + private static final Collection HOME_TELEPORT_IDS = ImmutableList.of( + WidgetInfo.SPELL_LUMBRIDGE_HOME_TELEPORT.getId(), + WidgetInfo.SPELL_EDGEVILLE_HOME_TELEPORT.getId(), + WidgetInfo.SPELL_LUNAR_HOME_TELEPORT.getId(), + WidgetInfo.SPELL_ARCEUUS_HOME_TELEPORT.getId(), + WidgetInfo.SPELL_KOUREND_HOME_TELEPORT.getId() + ); + private static final Collection MINIGAME_TELEPORT_IDS = ImmutableList.of( + WidgetInfo.MINIGAME_TELEPORT_BUTTON.getId() + ); + + @Nullable + static TeleportWidget of(int widgetId) + { + if (HOME_TELEPORT_IDS.contains(widgetId)) + { + return HOME_TELEPORT; + } + else if (MINIGAME_TELEPORT_IDS.contains(widgetId)) + { + return MINIGAME_TELEPORT; + } + else if (widgetId == WidgetInfo.TRAILBLAZER_AREA_TELEPORT.getId()) + { + return TRAILBLAZER_AREA_TELEPORT; + } + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimerTimer.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimerTimer.java new file mode 100644 index 0000000000..45728a4120 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimerTimer.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.timers; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.ui.overlay.infobox.InfoBoxPriority; +import net.runelite.client.ui.overlay.infobox.Timer; + +class TimerTimer extends Timer +{ + private final GameTimer timer; + + TimerTimer(GameTimer timer, Duration duration, Plugin plugin) + { + super(duration.toMillis(), ChronoUnit.MILLIS, null, plugin); + this.timer = timer; + setPriority(InfoBoxPriority.MED); + } + + public GameTimer getTimer() + { + return timer; + } + + @Override + public String getName() + { + return timer.name(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersConfig.java new file mode 100644 index 0000000000..e26b8cf473 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersConfig.java @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2017, Seth + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.timers; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; +import java.time.Instant; + +@ConfigGroup(TimersConfig.GROUP) +public interface TimersConfig extends Config +{ + String GROUP = "timers"; + + @ConfigItem( + keyName = "showHomeMinigameTeleports", + name = "Teleport cooldown timers", + description = "Configures whether timers for home and minigame teleport cooldowns are displayed" + ) + default boolean showHomeMinigameTeleports() + { + return true; + } + + @ConfigItem( + keyName = "showAntipoison", + name = "Antipoison/Venom timers", + description = "Configures whether timers for poison and venom protection are displayed" + ) + default boolean showAntiPoison() + { + return true; + } + + @ConfigItem( + keyName = "showAntiFire", + name = "Antifire timer", + description = "Configures whether antifire timer is displayed" + ) + default boolean showAntiFire() + { + return true; + } + + @ConfigItem( + keyName = "showStamina", + name = "Stamina timer", + description = "Configures whether stamina timer is displayed" + ) + default boolean showStamina() + { + return true; + } + + @ConfigItem( + keyName = "showOverload", + name = "Overload timer", + description = "Configures whether overload timer is displayed" + ) + default boolean showOverload() + { + return true; + } + + @ConfigItem( + keyName = "showPrayerEnhance", + name = "Prayer enhance timer", + description = "Configures whether prayer enhance timer is displayed" + ) + default boolean showPrayerEnhance() + { + return true; + } + + @ConfigItem( + keyName = "showDivine", + name = "Divine potion timer", + description = "Configures whether divine potion timer is displayed" + ) + default boolean showDivine() + { + return true; + } + + @ConfigItem( + keyName = "showCannon", + name = "Cannon timer", + description = "Configures whether cannon timer is displayed" + ) + default boolean showCannon() + { + return true; + } + + @ConfigItem( + keyName = "showMagicImbue", + name = "Magic imbue timer", + description = "Configures whether magic imbue timer is displayed" + ) + default boolean showMagicImbue() + { + return true; + } + + @ConfigItem( + keyName = "showCharge", + name = "Charge timer", + description = "Configures whether to show a timer for the Charge spell" + ) + default boolean showCharge() + { + return true; + } + + @ConfigItem( + keyName = "showImbuedHeart", + name = "Imbued heart timer", + description = "Configures whether imbued heart timer is displayed" + ) + default boolean showImbuedHeart() + { + return true; + } + + @ConfigItem( + keyName = "showVengeance", + name = "Vengeance timer", + description = "Configures whether vengeance and vengeance other timer is displayed" + ) + default boolean showVengeance() + { + return true; + } + + @ConfigItem( + keyName = "showVengeanceActive", + name = "Vengeance active", + description = "Configures whether an indicator for vengeance being active is displayed" + ) + default boolean showVengeanceActive() + { + return true; + } + + @ConfigItem( + keyName = "showTeleblock", + name = "Teleblock timer", + description = "Configures whether teleblock timer is displayed" + ) + default boolean showTeleblock() + { + return true; + } + + @ConfigItem( + keyName = "showFreezes", + name = "Freeze timer", + description = "Configures whether freeze timer is displayed" + ) + default boolean showFreezes() + { + return true; + } + + @ConfigItem( + keyName = "showGodWarsAltar", + name = "God wars altar timer", + description = "Configures whether god wars altar timer is displayed" + ) + default boolean showGodWarsAltar() + { + return true; + } + + @ConfigItem( + keyName = "showTzhaarTimers", + name = "Fight Caves and Inferno timers", + description = "Display elapsed time in the Fight Caves and Inferno" + ) + default boolean showTzhaarTimers() + { + return true; + } + + @ConfigItem( + keyName = "tzhaarStartTime", + name = "", + description = "", + hidden = true + ) + Instant tzhaarStartTime(); + + @ConfigItem( + keyName = "tzhaarStartTime", + name = "", + description = "" + ) + void tzhaarStartTime(Instant tzhaarStartTime); + + @ConfigItem( + keyName = "tzhaarLastTime", + name = "", + description = "", + hidden = true + ) + Instant tzhaarLastTime(); + + @ConfigItem( + keyName = "tzhaarLastTime", + name = "", + description = "" + ) + void tzhaarLastTime(Instant tzhaarLastTime); + + @ConfigItem( + keyName = "showStaffOfTheDead", + name = "Staff of the Dead timer", + description = "Configures whether staff of the dead timer is displayed" + ) + default boolean showStaffOfTheDead() + { + return true; + } + + @ConfigItem( + keyName = "showAbyssalSireStun", + name = "Abyssal Sire stun timer", + description = "Configures whether Abyssal Sire stun timer is displayed" + ) + default boolean showAbyssalSireStun() + { + return true; + } + + @ConfigItem( + keyName = "showDfsSpecial", + name = "Dragonfire Shield special timer", + description = "Configures whether the special attack cooldown timer for the Dragonfire Shield is displayed" + ) + default boolean showDFSSpecial() + { + return true; + } +} 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 new file mode 100644 index 0000000000..ec45039942 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java @@ -0,0 +1,1087 @@ +/* + * Copyright (c) 2017, Seth + * Copyright (c) 2018, Jordan Atwood + * Copyright (c) 2019, winterdaze + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.timers; + +import com.google.inject.Provides; +import java.time.Duration; +import java.time.Instant; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Actor; +import net.runelite.api.AnimationID; +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; +import net.runelite.api.ItemID; +import static net.runelite.api.ItemID.FIRE_CAPE; +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.VarPlayer; +import net.runelite.api.Varbits; +import net.runelite.api.WorldType; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.ActorDeath; +import net.runelite.api.events.AnimationChanged; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.GraphicChanged; +import net.runelite.api.events.ItemContainerChanged; +import net.runelite.api.events.MenuOptionClicked; +import net.runelite.api.events.NpcChanged; +import net.runelite.api.events.NpcDespawned; +import net.runelite.api.events.VarbitChanged; +import net.runelite.api.events.WidgetHiddenChanged; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetID; +import net.runelite.api.widgets.WidgetInfo; +import static net.runelite.api.widgets.WidgetInfo.PVP_WORLD_SAFE_ZONE; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ConfigChanged; +import net.runelite.client.game.ItemManager; +import net.runelite.client.game.SpriteManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import static net.runelite.client.plugins.timers.GameIndicator.VENGEANCE_ACTIVE; +import static net.runelite.client.plugins.timers.GameTimer.*; +import net.runelite.client.ui.overlay.infobox.InfoBoxManager; +import org.apache.commons.lang3.ArrayUtils; + +@PluginDescriptor( + name = "Timers", + description = "Show various timers in an infobox", + tags = {"combat", "items", "magic", "potions", "prayer", "overlay", "abyssal", "sire", "inferno", "fight", "caves", "cape", "timer", "tzhaar"} +) +@Slf4j +public class TimersPlugin extends Plugin +{ + private static final String ANTIFIRE_DRINK_MESSAGE = "You drink some of your antifire potion."; + private static final String ANTIFIRE_EXPIRED_MESSAGE = "Your antifire potion has expired."; + private static final String CANNON_FURNACE_MESSAGE = "You add the furnace."; + private static final String CANNON_PICKUP_MESSAGE = "You pick up the cannon. It's really heavy."; + private static final String CANNON_REPAIR_MESSAGE = "You repair your cannon, restoring it to working order."; + private static final String CHARGE_EXPIRED_MESSAGE = "Your magical charge fades away."; + private static final String CHARGE_MESSAGE = "You feel charged with magic power."; + 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 IMBUED_HEART_READY_MESSAGE = "Your imbued heart has regained its magical power."; + 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 Pattern TELEBLOCK_PATTERN = Pattern.compile("A Tele Block spell has been cast on you(?: by .+)?\\. It will expire in (?\\d+) minutes?(?:, (?\\d+) seconds?)?\\."); + private static final Pattern DIVINE_POTION_PATTERN = Pattern.compile("You drink some of your divine (.+) potion\\."); + private static final int VENOM_VALUE_CUTOFF = -40; // Antivenom < -40 <= Antipoison < 0 + private static final int POISON_TICK_LENGTH = 30; + + static final int FIGHT_CAVES_REGION_ID = 9551; + static final int INFERNO_REGION_ID = 9043; + private static final int NMZ_MAP_REGION_ID = 9033; + private static final Pattern TZHAAR_WAVE_MESSAGE = Pattern.compile("Wave: (\\d+)"); + private static final String TZHAAR_DEFEATED_MESSAGE = "You have been defeated!"; + private static final Pattern TZHAAR_COMPLETE_MESSAGE = Pattern.compile("Your (?:TzTok-Jad|TzKal-Zuk) kill count is:"); + private static final Pattern TZHAAR_PAUSED_MESSAGE = Pattern.compile("The (?:Inferno|Fight Cave) has been paused. You may now log out."); + + private TimerTimer freezeTimer; + private int freezeTime = -1; // time frozen, in game ticks + + private TimerTimer staminaTimer; + private boolean wasWearingEndurance; + + private int lastRaidVarb; + private int lastWildernessVarb; + private int lastVengCooldownVarb; + private int lastIsVengeancedVarb; + private int lastPoisonVarp; + private int nextPoisonTick; + private WorldPoint lastPoint; + private TeleportWidget lastTeleportClicked; + private int lastAnimation; + private boolean loggedInRace; + private boolean widgetHiddenChangedOnPvpWorld; + private ElapsedTimer tzhaarTimer; + + @Inject + private ItemManager itemManager; + + @Inject + private SpriteManager spriteManager; + + @Inject + private Client client; + + @Inject + private TimersConfig config; + + @Inject + private InfoBoxManager infoBoxManager; + + @Provides + TimersConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(TimersConfig.class); + } + + @Override + protected void shutDown() throws Exception + { + infoBoxManager.removeIf(t -> t instanceof TimerTimer); + lastRaidVarb = -1; + lastPoint = null; + lastTeleportClicked = null; + lastAnimation = -1; + loggedInRace = false; + widgetHiddenChangedOnPvpWorld = false; + lastPoisonVarp = 0; + nextPoisonTick = 0; + removeTzhaarTimer(); + staminaTimer = null; + } + + @Subscribe + public void onVarbitChanged(VarbitChanged event) + { + int raidVarb = client.getVar(Varbits.IN_RAID); + int vengCooldownVarb = client.getVar(Varbits.VENGEANCE_COOLDOWN); + int isVengeancedVarb = client.getVar(Varbits.VENGEANCE_ACTIVE); + int poisonVarp = client.getVar(VarPlayer.POISON); + + if (lastRaidVarb != raidVarb) + { + removeGameTimer(OVERLOAD_RAID); + removeGameTimer(PRAYER_ENHANCE); + lastRaidVarb = raidVarb; + } + + if (lastVengCooldownVarb != vengCooldownVarb && config.showVengeance()) + { + if (vengCooldownVarb == 1) + { + createGameTimer(VENGEANCE); + } + else + { + removeGameTimer(VENGEANCE); + } + + lastVengCooldownVarb = vengCooldownVarb; + } + + if (lastIsVengeancedVarb != isVengeancedVarb && config.showVengeanceActive()) + { + if (isVengeancedVarb == 1) + { + createGameIndicator(VENGEANCE_ACTIVE); + } + else + { + removeGameIndicator(VENGEANCE_ACTIVE); + } + + lastIsVengeancedVarb = isVengeancedVarb; + } + + int inWilderness = client.getVar(Varbits.IN_WILDERNESS); + + if (lastWildernessVarb != inWilderness + && client.getGameState() == GameState.LOGGED_IN + && !loggedInRace) + { + if (!WorldType.isPvpWorld(client.getWorldType()) + && inWilderness == 0) + { + log.debug("Left wilderness in non-PVP world, clearing Teleblock timer."); + removeGameTimer(TELEBLOCK); + } + + lastWildernessVarb = inWilderness; + } + + if (lastPoisonVarp != poisonVarp && config.showAntiPoison()) + { + final int tickCount = client.getTickCount(); + + if (nextPoisonTick - tickCount <= 0 || lastPoisonVarp == 0) + { + nextPoisonTick = tickCount + POISON_TICK_LENGTH; + } + + if (poisonVarp >= 0) + { + removeGameTimer(ANTIPOISON); + removeGameTimer(ANTIVENOM); + } + else if (poisonVarp >= VENOM_VALUE_CUTOFF) + { + Duration duration = Duration.ofMillis((long) Constants.GAME_TICK_LENGTH * (nextPoisonTick - tickCount + Math.abs((poisonVarp + 1) * POISON_TICK_LENGTH))); + removeGameTimer(ANTIVENOM); + createGameTimer(ANTIPOISON, duration); + } + else + { + Duration duration = Duration.ofMillis((long) Constants.GAME_TICK_LENGTH * (nextPoisonTick - tickCount + Math.abs((poisonVarp + 1 - VENOM_VALUE_CUTOFF) * POISON_TICK_LENGTH))); + removeGameTimer(ANTIPOISON); + createGameTimer(ANTIVENOM, duration); + } + + lastPoisonVarp = poisonVarp; + } + } + + @Subscribe + public void onWidgetHiddenChanged(WidgetHiddenChanged event) + { + Widget widget = event.getWidget(); + if (WorldType.isPvpWorld(client.getWorldType()) + && WidgetInfo.TO_GROUP(widget.getId()) == WidgetID.PVP_GROUP_ID) + { + widgetHiddenChangedOnPvpWorld = true; + } + } + + @Subscribe + public void onConfigChanged(ConfigChanged event) + { + if (!event.getGroup().equals(TimersConfig.GROUP)) + { + return; + } + + if (!config.showHomeMinigameTeleports()) + { + removeGameTimer(HOME_TELEPORT); + removeGameTimer(MINIGAME_TELEPORT); + } + + if (!config.showAntiFire()) + { + removeGameTimer(ANTIFIRE); + removeGameTimer(EXANTIFIRE); + removeGameTimer(SUPERANTIFIRE); + } + + if (!config.showStamina()) + { + removeGameTimer(STAMINA); + } + + if (!config.showOverload()) + { + removeGameTimer(OVERLOAD); + removeGameTimer(OVERLOAD_RAID); + } + + if (!config.showPrayerEnhance()) + { + removeGameTimer(PRAYER_ENHANCE); + } + + if (!config.showDivine()) + { + removeGameTimer(DIVINE_SUPER_ATTACK); + removeGameTimer(DIVINE_SUPER_STRENGTH); + removeGameTimer(DIVINE_SUPER_DEFENCE); + removeGameTimer(DIVINE_SUPER_COMBAT); + removeGameTimer(DIVINE_RANGING); + removeGameTimer(DIVINE_MAGIC); + } + + if (!config.showCannon()) + { + removeGameTimer(CANNON); + } + + if (!config.showMagicImbue()) + { + removeGameTimer(MAGICIMBUE); + } + + if (!config.showCharge()) + { + removeGameTimer(CHARGE); + } + + if (!config.showImbuedHeart()) + { + removeGameTimer(IMBUEDHEART); + } + + if (!config.showStaffOfTheDead()) + { + removeGameTimer(STAFF_OF_THE_DEAD); + } + + if (!config.showVengeance()) + { + removeGameTimer(VENGEANCE); + } + + if (!config.showVengeanceActive()) + { + removeGameIndicator(VENGEANCE_ACTIVE); + } + + if (!config.showTeleblock()) + { + removeGameTimer(TELEBLOCK); + } + + if (!config.showFreezes()) + { + removeGameTimer(BIND); + removeGameTimer(SNARE); + removeGameTimer(ENTANGLE); + removeGameTimer(ICERUSH); + removeGameTimer(ICEBURST); + removeGameTimer(ICEBLITZ); + removeGameTimer(ICEBARRAGE); + } + + if (!config.showAntiPoison()) + { + removeGameTimer(ANTIPOISON); + removeGameTimer(ANTIVENOM); + } + + if (!config.showTzhaarTimers()) + { + removeTzhaarTimer(); + } + else + { + createTzhaarTimer(); + } + } + + @Subscribe + public void onMenuOptionClicked(MenuOptionClicked event) + { + if (config.showStamina() + && event.getMenuOption().contains("Drink") + && (event.getId() == ItemID.STAMINA_MIX1 + || event.getId() == ItemID.STAMINA_MIX2 + || event.getId() == ItemID.EGNIOL_POTION_1 + || event.getId() == ItemID.EGNIOL_POTION_2 + || event.getId() == ItemID.EGNIOL_POTION_3 + || event.getId() == ItemID.EGNIOL_POTION_4)) + { + // Needs menu option hook because mixes use a common drink message, distinct from their standard potion messages + createStaminaTimer(); + return; + } + + if (config.showAntiFire() + && event.getMenuOption().contains("Drink") + && (event.getId() == ItemID.ANTIFIRE_MIX1 + || event.getId() == ItemID.ANTIFIRE_MIX2)) + { + // Needs menu option hook because mixes use a common drink message, distinct from their standard potion messages + createGameTimer(ANTIFIRE); + return; + } + + if (config.showAntiFire() + && event.getMenuOption().contains("Drink") + && (event.getId() == ItemID.EXTENDED_ANTIFIRE_MIX1 + || event.getId() == ItemID.EXTENDED_ANTIFIRE_MIX2)) + { + // Needs menu option hook because mixes use a common drink message, distinct from their standard potion messages + createGameTimer(EXANTIFIRE); + return; + } + + if (config.showAntiFire() + && event.getMenuOption().contains("Drink") + && (event.getId() == ItemID.SUPER_ANTIFIRE_MIX1 + || event.getId() == ItemID.SUPER_ANTIFIRE_MIX2)) + { + // Needs menu option hook because mixes use a common drink message, distinct from their standard potion messages + createGameTimer(SUPERANTIFIRE); + return; + } + + if (config.showAntiFire() + && event.getMenuOption().contains("Drink") + && (event.getId() == ItemID.EXTENDED_SUPER_ANTIFIRE_MIX1 + || event.getId() == ItemID.EXTENDED_SUPER_ANTIFIRE_MIX2)) + { + // Needs menu option hook because mixes use a common drink message, distinct from their standard potion messages + createGameTimer(EXSUPERANTIFIRE); + return; + } + + TeleportWidget teleportWidget = TeleportWidget.of(event.getWidgetId()); + if (teleportWidget != null) + { + lastTeleportClicked = teleportWidget; + } + } + + @Subscribe + public void onChatMessage(ChatMessage event) + { + final String message = event.getMessage(); + if (event.getType() != ChatMessageType.SPAM && event.getType() != ChatMessageType.GAMEMESSAGE) + { + return; + } + + 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); + } + + if (config.showAntiFire() && message.equals(EXTENDED_ANTIFIRE_DRINK_MESSAGE)) + { + createGameTimer(EXANTIFIRE); + } + + if (config.showGodWarsAltar() && message.equalsIgnoreCase(GOD_WARS_ALTAR_MESSAGE))//Normal altars are "You recharge your Prayer points." while gwd is "You recharge your Prayer." + { + createGameTimer(GOD_WARS_ALTAR); + } + + if (config.showAntiFire() && message.equals(EXTENDED_SUPER_ANTIFIRE_DRINK_MESSAGE)) + { + createGameTimer(EXSUPERANTIFIRE); + } + + if (config.showAntiFire() && message.equals(ANTIFIRE_EXPIRED_MESSAGE)) + { + //they have the same expired message + removeGameTimer(ANTIFIRE); + removeGameTimer(EXANTIFIRE); + } + + if (config.showOverload() && message.startsWith("You drink some of your") && message.contains("overload")) + { + if (client.getVar(Varbits.IN_RAID) == 1) + { + createGameTimer(OVERLOAD_RAID); + } + else + { + createGameTimer(OVERLOAD); + } + + } + + if (config.showCannon() && (message.equals(CANNON_FURNACE_MESSAGE) || message.contains(CANNON_REPAIR_MESSAGE))) + { + TimerTimer cannonTimer = createGameTimer(CANNON); + cannonTimer.setTooltip(cannonTimer.getTooltip() + " - World " + client.getWorld()); + } + + if (config.showCannon() && message.equals(CANNON_PICKUP_MESSAGE)) + { + removeGameTimer(CANNON); + } + + if (config.showMagicImbue() && message.equals(MAGIC_IMBUE_MESSAGE)) + { + createGameTimer(MAGICIMBUE); + } + + if (message.equals(MAGIC_IMBUE_EXPIRED_MESSAGE)) + { + removeGameTimer(MAGICIMBUE); + } + + if (config.showTeleblock()) + { + Matcher m = TELEBLOCK_PATTERN.matcher(message); + if (m.find()) + { + String minss = m.group("mins"); + String secss = m.group("secs"); + int mins = Integer.parseInt(minss); + int secs = secss != null ? Integer.parseInt(secss) : 0; + createGameTimer(TELEBLOCK, Duration.ofSeconds(mins * 60 + secs)); + } + else if (message.contains(KILLED_TELEBLOCK_OPPONENT_TEXT)) + { + removeGameTimer(TELEBLOCK); + } + } + + if (config.showAntiFire() && message.contains(SUPER_ANTIFIRE_DRINK_MESSAGE)) + { + createGameTimer(SUPERANTIFIRE); + } + + if (config.showAntiFire() && message.equals(SUPER_ANTIFIRE_EXPIRED_MESSAGE)) + { + removeGameTimer(SUPERANTIFIRE); + } + + if (config.showImbuedHeart() && message.equals(IMBUED_HEART_READY_MESSAGE)) + { + removeGameTimer(IMBUEDHEART); + } + + if (config.showPrayerEnhance() && message.startsWith("You drink some of your") && message.contains("prayer enhance")) + { + createGameTimer(PRAYER_ENHANCE); + } + + if (config.showPrayerEnhance() && message.equals(PRAYER_ENHANCE_EXPIRED)) + { + removeGameTimer(PRAYER_ENHANCE); + } + + if (config.showCharge() && message.equals(CHARGE_MESSAGE)) + { + createGameTimer(CHARGE); + } + + if (config.showCharge() && message.equals(CHARGE_EXPIRED_MESSAGE)) + { + removeGameTimer(CHARGE); + } + + if (config.showStaffOfTheDead() && message.contains(STAFF_OF_THE_DEAD_SPEC_MESSAGE)) + { + createGameTimer(STAFF_OF_THE_DEAD); + } + + if (config.showStaffOfTheDead() && message.contains(STAFF_OF_THE_DEAD_SPEC_EXPIRED_MESSAGE)) + { + removeGameTimer(STAFF_OF_THE_DEAD); + } + + if (config.showFreezes() && message.equals(FROZEN_MESSAGE)) + { + freezeTimer = createGameTimer(ICEBARRAGE); + freezeTime = client.getTickCount(); + } + + if (config.showDivine()) + { + Matcher mDivine = DIVINE_POTION_PATTERN.matcher(message); + if (mDivine.find()) + { + switch (mDivine.group(1)) + { + case "super attack": + createGameTimer(DIVINE_SUPER_ATTACK); + break; + + case "super strength": + createGameTimer(DIVINE_SUPER_STRENGTH); + break; + + case "super defence": + createGameTimer(DIVINE_SUPER_DEFENCE); + break; + + case "combat": + createGameTimer(DIVINE_SUPER_COMBAT); + break; + + case "ranging": + createGameTimer(DIVINE_RANGING); + break; + + case "magic": + createGameTimer(DIVINE_MAGIC); + break; + + case "bastion": + createGameTimer(DIVINE_BASTION); + break; + + case "battlemage": + createGameTimer(DIVINE_BATTLEMAGE); + break; + } + } + } + + if (message.equals(TZHAAR_DEFEATED_MESSAGE) || TZHAAR_COMPLETE_MESSAGE.matcher(message).matches()) + { + log.debug("Stopping tzhaar timer"); + removeTzhaarTimer(); + config.tzhaarStartTime(null); + config.tzhaarLastTime(null); + return; + } + + if (TZHAAR_PAUSED_MESSAGE.matcher(message).find()) + { + log.debug("Pausing tzhaar timer"); + config.tzhaarLastTime(Instant.now()); + if (config.showTzhaarTimers()) + { + createTzhaarTimer(); + } + return; + } + + Matcher matcher = TZHAAR_WAVE_MESSAGE.matcher(message); + if (matcher.find()) + { + int wave = Integer.parseInt(matcher.group(1)); + if (wave == 1) + { + log.debug("Starting tzhaar timer"); + + Instant now = Instant.now(); + if (isInInferno()) + { + // The first wave message of the inferno comes six seconds after the ingame timer starts counting + config.tzhaarStartTime(now.minus(Duration.ofSeconds(6))); + } + else + { + config.tzhaarStartTime(now); + } + config.tzhaarLastTime(null); + + if (config.showTzhaarTimers()) + { + createTzhaarTimer(); + } + } + else if (config.tzhaarStartTime() != null && config.tzhaarLastTime() != null) + { + log.debug("Unpausing tzhaar timer"); + + // Advance start time by how long it has been paused + Instant tzhaarStartTime = config.tzhaarStartTime(); + tzhaarStartTime = tzhaarStartTime.plus(Duration.between(config.tzhaarLastTime(), Instant.now())); + config.tzhaarStartTime(tzhaarStartTime); + + config.tzhaarLastTime(null); + if (config.showTzhaarTimers()) + { + createTzhaarTimer(); + } + } + } + } + + private boolean isInFightCaves() + { + return client.getMapRegions() != null && ArrayUtils.contains(client.getMapRegions(), FIGHT_CAVES_REGION_ID); + } + + private boolean isInInferno() + { + return client.getMapRegions() != null && ArrayUtils.contains(client.getMapRegions(), INFERNO_REGION_ID); + } + + private boolean isInNightmareZone() + { + return client.getLocalPlayer() != null && client.getLocalPlayer().getWorldLocation().getPlane() > 0 && ArrayUtils.contains(client.getMapRegions(), NMZ_MAP_REGION_ID); + } + + private void createTzhaarTimer() + { + removeTzhaarTimer(); + + int imageItem = isInFightCaves() ? FIRE_CAPE : (isInInferno() ? INFERNAL_CAPE : -1); + if (imageItem == -1) + { + return; + } + + tzhaarTimer = new ElapsedTimer(itemManager.getImage(imageItem), this, config.tzhaarStartTime(), config.tzhaarLastTime()); + infoBoxManager.addInfoBox(tzhaarTimer); + } + + private void removeTzhaarTimer() + { + if (tzhaarTimer != null) + { + infoBoxManager.removeInfoBox(tzhaarTimer); + tzhaarTimer = null; + } + } + + @Subscribe + public void onGameTick(GameTick event) + { + loggedInRace = false; + + Player player = client.getLocalPlayer(); + WorldPoint currentWorldPoint = player.getWorldLocation(); + + if (freezeTimer != null) + { + // assume movement means unfrozen + if (freezeTime != client.getTickCount() + && !currentWorldPoint.equals(lastPoint)) + { + removeGameTimer(freezeTimer.getTimer()); + freezeTimer = null; + } + } + + lastPoint = currentWorldPoint; + + if (!widgetHiddenChangedOnPvpWorld) + { + return; + } + + widgetHiddenChangedOnPvpWorld = false; + + Widget widget = client.getWidget(PVP_WORLD_SAFE_ZONE); + if (widget != null && !widget.isSelfHidden()) + { + log.debug("Entered safe zone in PVP world, clearing Teleblock timer."); + removeGameTimer(TELEBLOCK); + } + } + + @Subscribe + public void onGameStateChanged(GameStateChanged gameStateChanged) + { + switch (gameStateChanged.getGameState()) + { + case LOADING: + if (!isInNightmareZone()) + { + removeGameTimer(OVERLOAD); + } + + if (tzhaarTimer != null && !isInFightCaves() && !isInInferno()) + { + removeTzhaarTimer(); + config.tzhaarStartTime(null); + config.tzhaarLastTime(null); + } + break; + case HOPPING: + case LOGIN_SCREEN: + // pause tzhaar timer if logged out without pausing + if (config.tzhaarStartTime() != null && config.tzhaarLastTime() == null) + { + config.tzhaarLastTime(Instant.now()); + log.debug("Pausing tzhaar timer"); + } + + removeTzhaarTimer(); // will be readded by the wave message + removeGameTimer(TELEBLOCK); + break; + case LOGGED_IN: + loggedInRace = true; + break; + } + } + + @Subscribe + public void onNpcChanged(NpcChanged npcChanged) + { + int id = npcChanged.getNpc().getId(); + int oldId = npcChanged.getOld().getId(); + + if (id == NpcID.ABYSSAL_SIRE_5888) + { + // stunned npc type + log.debug("Sire is stunned"); + if (config.showAbyssalSireStun()) + { + createGameTimer(ABYSSAL_SIRE_STUN); + } + } + else if (oldId == NpcID.ABYSSAL_SIRE_5888) + { + // change from stunned sire to anything else + log.debug("Sire is unstunned"); + removeGameTimer(ABYSSAL_SIRE_STUN); + } + } + + @Subscribe + public void onAnimationChanged(AnimationChanged event) + { + Actor actor = event.getActor(); + + if (actor != client.getLocalPlayer()) + { + return; + } + + if (config.showHomeMinigameTeleports() + && client.getLocalPlayer().getAnimation() == AnimationID.IDLE + && (lastAnimation == AnimationID.BOOK_HOME_TELEPORT_5 + || lastAnimation == AnimationID.COW_HOME_TELEPORT_6 + || lastAnimation == AnimationID.LEAGUE_HOME_TELEPORT_6)) + { + if (lastTeleportClicked == TeleportWidget.HOME_TELEPORT) + { + createGameTimer(HOME_TELEPORT); + } + else if (lastTeleportClicked == TeleportWidget.MINIGAME_TELEPORT) + { + createGameTimer(MINIGAME_TELEPORT); + } + } + + if (config.showDFSSpecial() && lastAnimation == AnimationID.DRAGONFIRE_SHIELD_SPECIAL) + { + createGameTimer(DRAGON_FIRE_SHIELD); + } + + lastAnimation = client.getLocalPlayer().getAnimation(); + } + + @Subscribe + public void onGraphicChanged(GraphicChanged event) + { + Actor actor = event.getActor(); + + if (actor != client.getLocalPlayer()) + { + return; + } + + if (config.showImbuedHeart() && actor.getGraphic() == IMBUEDHEART.getGraphicId()) + { + createGameTimer(IMBUEDHEART); + } + + if (config.showFreezes()) + { + if (actor.getGraphic() == BIND.getGraphicId()) + { + createGameTimer(BIND); + } + + if (actor.getGraphic() == SNARE.getGraphicId()) + { + createGameTimer(SNARE); + } + + if (actor.getGraphic() == ENTANGLE.getGraphicId()) + { + createGameTimer(ENTANGLE); + } + + // downgrade freeze based on graphic, if at the same tick as the freeze message + if (freezeTime == client.getTickCount()) + { + if (actor.getGraphic() == ICERUSH.getGraphicId()) + { + removeGameTimer(ICEBARRAGE); + freezeTimer = createGameTimer(ICERUSH); + } + + if (actor.getGraphic() == ICEBURST.getGraphicId()) + { + removeGameTimer(ICEBARRAGE); + freezeTimer = createGameTimer(ICEBURST); + } + + if (actor.getGraphic() == ICEBLITZ.getGraphicId()) + { + removeGameTimer(ICEBARRAGE); + freezeTimer = createGameTimer(ICEBLITZ); + } + } + } + } + + /** + * Remove SOTD timer and update stamina timer when equipment is changed. + */ + @Subscribe + public void onItemContainerChanged(ItemContainerChanged itemContainerChanged) + { + if (itemContainerChanged.getContainerId() != InventoryID.EQUIPMENT.getId()) + { + return; + } + + ItemContainer container = itemContainerChanged.getItemContainer(); + + 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)) + { + 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 + public void onNpcDespawned(NpcDespawned npcDespawned) + { + NPC npc = npcDespawned.getNpc(); + + if (!npc.isDead()) + { + return; + } + + int npcId = npc.getId(); + + if (npcId == NpcID.ZOMBIFIED_SPAWN || npcId == NpcID.ZOMBIFIED_SPAWN_8063) + { + removeGameTimer(ICEBARRAGE); + } + } + + @Subscribe + public void onActorDeath(ActorDeath actorDeath) + { + if (actorDeath.getActor() == client.getLocalPlayer()) + { + infoBoxManager.removeIf(t -> t instanceof TimerTimer && ((TimerTimer) t).getTimer().isRemovedOnDeath()); + } + } + + private void createStaminaTimer() + { + Duration duration = Duration.ofMinutes(wasWearingEndurance ? 4 : 2); + staminaTimer = createGameTimer(STAMINA, duration); + } + + private TimerTimer createGameTimer(final GameTimer timer) + { + if (timer.getDuration() == null) + { + throw new IllegalArgumentException("Timer with no duration"); + } + return createGameTimer(timer, timer.getDuration()); + } + + private TimerTimer createGameTimer(final GameTimer timer, Duration duration) + { + removeGameTimer(timer); + + TimerTimer t = new TimerTimer(timer, duration, this); + switch (timer.getImageType()) + { + case SPRITE: + spriteManager.getSpriteAsync(timer.getImageId(), 0, t); + break; + case ITEM: + t.setImage(itemManager.getImage(timer.getImageId())); + break; + } + t.setTooltip(timer.getDescription()); + infoBoxManager.addInfoBox(t); + return t; + } + + private void removeGameTimer(GameTimer timer) + { + infoBoxManager.removeIf(t -> t instanceof TimerTimer && ((TimerTimer) t).getTimer() == timer); + } + + private IndicatorIndicator createGameIndicator(GameIndicator gameIndicator) + { + removeGameIndicator(gameIndicator); + + IndicatorIndicator indicator = new IndicatorIndicator(gameIndicator, this); + switch (gameIndicator.getImageType()) + { + case SPRITE: + spriteManager.getSpriteAsync(gameIndicator.getImageId(), 0, indicator); + break; + case ITEM: + indicator.setImage(itemManager.getImage(gameIndicator.getImageId())); + break; + } + indicator.setTooltip(gameIndicator.getDescription()); + infoBoxManager.addInfoBox(indicator); + + return indicator; + } + + private void removeGameIndicator(GameIndicator indicator) + { + infoBoxManager.removeIf(t -> t instanceof IndicatorIndicator && ((IndicatorIndicator) t).getIndicator() == indicator); + } +} diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSActorMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSActorMixin.java index 41deebfbfc..15000aee40 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSActorMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSActorMixin.java @@ -38,7 +38,7 @@ import net.runelite.api.coords.WorldPoint; import net.runelite.api.events.ActorDeath; import net.runelite.api.events.AnimationChanged; import net.runelite.api.events.HitsplatApplied; -import net.runelite.api.events.SpotAnimationChanged; +import net.runelite.api.events.GraphicChanged; import net.runelite.api.events.InteractingChanged; import net.runelite.api.events.OverheadTextChanged; import java.awt.Graphics2D; @@ -197,9 +197,9 @@ public abstract class RSActorMixin implements RSActor @Inject public void spotAnimationChanged(int idx) { - SpotAnimationChanged spotAnimationChanged = new SpotAnimationChanged(); - spotAnimationChanged.setActor(this); - client.getCallbacks().post(spotAnimationChanged); + GraphicChanged graphicChanged = new GraphicChanged(); + graphicChanged.setActor(this); + client.getCallbacks().post(graphicChanged); } @FieldHook("targetIndex") diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java index fc9d9d1cbd..5f68a65312 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java @@ -639,20 +639,6 @@ public abstract class RSClientMixin implements RSClient return varps[varpId]; } - @Inject - @Override - public int getVarpValue(int varpId) - { - return getVarpValue(getVarps(), varpId); - } - - @Inject - @Override - public void setVarpValue(int[] varps, int varpId, int value) - { - varps[varpId] = value; - } - @Inject @Override public boolean isPrayerActive(Prayer prayer) @@ -1975,5 +1961,11 @@ public abstract class RSClientMixin implements RSClient { return this.outdatedScripts; } + + @Override + public Widget getWidget(int i) + { + return getWidget(i, i); + } } diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSNPCMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSNPCMixin.java index ca2823af9b..16b4d28357 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSNPCMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSNPCMixin.java @@ -111,7 +111,7 @@ public abstract class RSNPCMixin implements RSNPC } else if (this.getId() != -1) { - client.getCallbacks().post(new NpcChanged(this)); + client.getCallbacks().post(new NpcChanged(this, composition)); } }