From 64af016b99c2a496a232aeef4f5b19a2a6b26d1c Mon Sep 17 00:00:00 2001 From: Kyleeld <48519776+Kyleeld@users.noreply.github.com> Date: Thu, 1 Aug 2019 12:35:23 +0100 Subject: [PATCH] gauntlet: redesign (#1200) * gauntlet * . * Update GauntletOverlay.java * Update GauntletUtils.java * gauntlet: Update, streamline, optimize, and refactor. * Various Fixes * Fix player attack incrementing. * Actually fix player attack incrementing this time. :3head: * Revert "Actually fix player attack incrementing this time. :3head:" This reverts commit eaa12234 * Fix double increment bug. * Various Refactoring and updates. * Add sound back in * Update projectile api with proper name * Format Config, add Widget helper, and cleanup access. * Update GauntletPlugin.java * Update GauntletOverlay.java * Update GauntletOverlay.java * Update GauntletConfig.java * Update GauntletPlugin.java * Update GauntletTimer.java * Update GauntletTimer.java * Update GauntletTimer.java * Update GauntletTimer.java * Fixup widget helper. * Use varbits instead of hidden. * Fixup prayer overlay. * gauntlet: include tick timers, and various fixes. * gauntlet: various fixes, and qol changes. * gauntlet: give each tornado their own ticks, and show true location. * gauntlet: hunllef spelling fixes * Update ProjectileID.java --- .../java/net/runelite/api/ProjectileID.java | 13 +- .../main/java/net/runelite/api/Varbits.java | 7 +- .../client/graphics/ModelOutlineRenderer.java | 6 +- .../plugins/gauntlet/GauntletConfig.java | 272 +++++++++++ .../plugins/gauntlet/GauntletCounter.java | 82 ---- .../plugins/gauntlet/GauntletInfoBox.java | 133 ------ .../plugins/gauntlet/GauntletOverlay.java | 416 +++++++++++++++++ .../plugins/gauntlet/GauntletPlugin.java | 438 +++++++++++++----- .../plugins/gauntlet/GauntletTimer.java | 313 +++++++++++++ .../client/plugins/gauntlet/Hunllef.java | 149 ++++++ .../client/plugins/gauntlet/Missiles.java | 87 ++++ .../client/plugins/gauntlet/Resources.java | 72 +++ ...GauntletPluginConfig.java => Tornado.java} | 34 +- .../client/ui/overlay/OverlayUtil.java | 3 +- 14 files changed, 1660 insertions(+), 365 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletConfig.java delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletCounter.java delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletInfoBox.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletTimer.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/Hunllef.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/Missiles.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/Resources.java rename runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/{GauntletPluginConfig.java => Tornado.java} (78%) diff --git a/runelite-api/src/main/java/net/runelite/api/ProjectileID.java b/runelite-api/src/main/java/net/runelite/api/ProjectileID.java index 8d733e7a74..63e462843b 100644 --- a/runelite-api/src/main/java/net/runelite/api/ProjectileID.java +++ b/runelite-api/src/main/java/net/runelite/api/ProjectileID.java @@ -102,11 +102,12 @@ public class ProjectileID public static final int HYDRA_LIGHTNING_2 = 1665; public static final int DRAKE_BREATH = 1637; - public static final int HUNLEFF_MAGE_ATTACK = 1707; - public static final int HUNLEFF_CORRUPTED_MAGE_ATTACK = 1708; - public static final int HUNLEFF_RANGE_ATTACK = 1711; - public static final int HUNLEFF_CORRUPTED_RANGE_ATTACK = 1712; - + public static final int HUNLLEF_MAGE_ATTACK = 1707; + public static final int HUNLLEF_CORRUPTED_MAGE_ATTACK = 1708; + public static final int HUNLLEF_RANGE_ATTACK = 1711; + public static final int HUNLLEF_CORRUPTED_RANGE_ATTACK = 1712; + public static final int HUNLLEF_PRAYER_ATTACK = 1713; + public static final int HUNLLEF_CORRUPTED_PRAYER_ATTACK = 1714; + public static final int ZALCANO_PROJECTILE = 1728; - } 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 0ceac97105..b66f73e078 100644 --- a/runelite-api/src/main/java/net/runelite/api/Varbits.java +++ b/runelite-api/src/main/java/net/runelite/api/Varbits.java @@ -692,7 +692,12 @@ public enum Varbits /** * 1 is true, 0 is false. */ - GAUNTLET_FINAL_ROOM_ENTERED(9177); + GAUNTLET_FINAL_ROOM_ENTERED(9177), + + /** + * 1 is true, 0 is false. + */ + GAUNTLET_ENTERED(9178); /** * The raw varbit ID. diff --git a/runelite-client/src/main/java/net/runelite/client/graphics/ModelOutlineRenderer.java b/runelite-client/src/main/java/net/runelite/client/graphics/ModelOutlineRenderer.java index 9e9d14aca9..2d115b0855 100644 --- a/runelite-client/src/main/java/net/runelite/client/graphics/ModelOutlineRenderer.java +++ b/runelite-client/src/main/java/net/runelite/client/graphics/ModelOutlineRenderer.java @@ -37,13 +37,13 @@ import net.runelite.api.Client; import net.runelite.api.DecorativeObject; import net.runelite.api.GameObject; import net.runelite.api.GroundObject; -import net.runelite.api.TileItemPile; import net.runelite.api.MainBufferProvider; import net.runelite.api.Model; import net.runelite.api.NPC; import net.runelite.api.NPCDefinition; import net.runelite.api.Perspective; import net.runelite.api.Player; +import net.runelite.api.TileItemPile; import net.runelite.api.TileObject; import net.runelite.api.WallObject; import net.runelite.api.coords.LocalPoint; @@ -968,7 +968,7 @@ public class ModelOutlineRenderer } } - private void drawOutline(GameObject gameObject, int outlineWidth, Color innerColor, Color outerColor) + public void drawOutline(GameObject gameObject, int outlineWidth, Color innerColor, Color outerColor) { LocalPoint lp = gameObject.getLocalLocation(); if (lp != null) @@ -979,7 +979,7 @@ public class ModelOutlineRenderer } } - private void drawOutline(GroundObject groundObject, int outlineWidth, Color innerColor, Color outerColor) + public void drawOutline(GroundObject groundObject, int outlineWidth, Color innerColor, Color outerColor) { LocalPoint lp = groundObject.getLocalLocation(); if (lp != null) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletConfig.java new file mode 100644 index 0000000000..328fda2afc --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletConfig.java @@ -0,0 +1,272 @@ +/* + * THIS SOFTWARE WRITTEN BY A KEYBOARD-WIELDING MONKEY BOI + * No rights reserved. Use, redistribute, and modify at your own discretion, + * and in accordance with Yagex and RuneLite guidelines. + * However, aforementioned monkey would prefer if you don't sell this plugin for profit. + * Good luck on your raids! + */ + +package net.runelite.client.plugins.gauntlet; + +import java.awt.Color; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.Range; +import net.runelite.client.config.Stub; + +@ConfigGroup("Gauntlet") + +public interface GauntletConfig extends Config +{ + @ConfigItem( + position = 0, + keyName = "resources", + name = "Resources", + description = "" + ) + default Stub resources() + { + return new Stub(); + } + + @ConfigItem( + position = 1, + keyName = "highlightResources", + name = "Highlight Resources", + description = "Highlights all the resources in each room with a color.", + parent = "resources" + ) + default boolean highlightResources() + { + return true; + } + + @ConfigItem( + position = 2, + keyName = "highlightResourcesColor", + name = "Highlight Color", + description = "Highlights all the resources in each room with this color.", + parent = "resources", + hidden = true, + unhide = "highlightResources" + ) + default Color highlightResourcesColor() + { + return Color.YELLOW; + } + + @ConfigItem( + position = 3, + keyName = "highlightResourcesIcons", + name = "Highlight Resources with an Icon", + description = "Highlights all the icons in each room with an icon.", + parent = "resources", + hidden = true, + unhide = "highlightResources" + ) + default boolean highlightResourcesIcons() + { + return true; + } + + @Range( + min = 1, + max = 50 + ) + @ConfigItem( + position = 4, + keyName = "resourceIconSize", + name = "Resource Icon Size", + description = " change the size of resource icons.", + hidden = true, + unhide = "highlightResources", + parent = "resources" + ) + default int resourceIconSize() + { + return 20; + } + + @ConfigItem( + position = 5, + keyName = "boss", + name = "Boss", + description = "" + ) + default Stub boss() + { + return new Stub(); + } + + @ConfigItem( + position = 6, + keyName = "countBossAttacks", + name = "Count Boss Attacks", + description = "Count the attacks until the boss switches their style.", + parent = "boss" + ) + default boolean countBossAttacks() + { + return true; + } + + @ConfigItem( + position = 7, + keyName = "countPlayerAttacks", + name = "Count Player Attacks", + description = "Count the player attacks until the boss switches their prayer.", + parent = "boss" + ) + default boolean countPlayerAttacks() + { + return true; + } + + @ConfigItem( + position = 8, + keyName = "highlightWidget", + name = "Highlight Correct Prayer", + description = "Highlights correct prayer in your prayer book.", + parent = "boss" + ) + default boolean highlightWidget() + { + return true; + } + + @ConfigItem( + position = 8, + keyName = "flashOnWrongAttack", + name = "Flash on Wrong Attack", + description = "This will flash your screen if you attack with the wrong stlye.", + parent = "boss" + ) + default boolean flashOnWrongAttack() + { + return true; + } + + @ConfigItem( + position = 9, + keyName = "uniquePrayerAudio", + name = "Unique Prayer Audio", + description = "Plays a unique sound whenever the boss is about to shut down your prayer.", + parent = "boss" + ) + default boolean uniquePrayerAudio() + { + return true; + } + + @ConfigItem( + position = 10, + keyName = "uniquePrayerVisual", + name = "Unique Prayer Visual", + description = "Prayer attacks will have a unique overlay visual.", + parent = "boss" + ) + default boolean uniquePrayerVisual() + { + return true; + } + + @ConfigItem( + position = 11, + keyName = "uniqueAttackVisual", + name = "Unique Magic & Range Visuals", + description = "Magic and Range attacks will have a unique overlay visual.", + parent = "boss" + ) + default boolean uniqueAttackVisual() + { + return false; + } + + @ConfigItem( + position = 12, + keyName = "overlayBoss", + name = "Overlay the Boss (Color)", + description = "Overlay the boss with an color denoting it's current attack style.", + parent = "boss" + ) + default boolean overlayBoss() + { + return true; + } + + @ConfigItem( + position = 13, + keyName = "overlayBossPrayer", + name = "Overlay the Boss (Icon)", + description = "Overlay the boss with an icon denoting it's current attack style.", + parent = "boss" + ) + default boolean overlayBossPrayer() + { + return false; + } + + @ConfigItem( + position = 14, + keyName = "overlayTornadoes", + name = "Show Tornado Decay", + description = "Display the amount of ticks left until the tornadoes decay.", + parent = "boss" + ) + default boolean overlayTornadoes() + { + return true; + } + + @Range( + min = 1, + max = 50 + ) + @ConfigItem( + position = 15, + keyName = "projectileIconSize", + name = "Boss Projectile Icon Size", + description = " change the size of Projectile icons.", + parent = "boss" + ) + default int projectileIconSize() + { + return 20; + } + + @ConfigItem( + position = 16, + keyName = "timer", + name = "Timer", + description = "" + ) + default Stub timer() + { + return new Stub(); + } + + @ConfigItem( + position = 17, + keyName = "displayTimerWidget", + name = "Show Custom Timer (Widget)", + description = "Display a timer widget that tracks your gauntlet progress.", + parent = "timer" + ) + default boolean displayTimerWidget() + { + return true; + } + + @ConfigItem( + position = 18, + keyName = "displayTimerChat", + name = "Show Custom Timer (Chat)", + description = "Display a chat message that tracks your gauntlet progress.", + parent = "timer" + ) + default boolean displayTimerChat() + { + return true; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletCounter.java b/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletCounter.java deleted file mode 100644 index d16684fb66..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletCounter.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2019, ganom - * 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.gauntlet; - -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics2D; -import javax.inject.Inject; -import javax.inject.Singleton; -import lombok.extern.slf4j.Slf4j; -import net.runelite.client.ui.overlay.Overlay; -import net.runelite.client.ui.overlay.OverlayPosition; -import net.runelite.client.ui.overlay.OverlayPriority; -import net.runelite.client.ui.overlay.components.PanelComponent; -import net.runelite.client.ui.overlay.components.TitleComponent; -import net.runelite.client.ui.overlay.components.table.TableAlignment; -import net.runelite.client.ui.overlay.components.table.TableComponent; -import net.runelite.client.util.ColorUtil; - -@Singleton -@Slf4j -public class GauntletCounter extends Overlay -{ - private static final Color NOT_ACTIVATED_BACKGROUND_COLOR = new Color(150, 0, 0, 150); - private final GauntletPlugin plugin; - private final PanelComponent panelComponent = new PanelComponent(); - - @Inject - GauntletCounter(final GauntletPlugin plugin) - { - this.plugin = plugin; - setPosition(OverlayPosition.ABOVE_CHATBOX_RIGHT); - setPriority(OverlayPriority.HIGH); - } - - @Override - public Dimension render(Graphics2D graphics) - { - panelComponent.getChildren().clear(); - - if (plugin.getHunllef() == null || !plugin.isInRoom()) - { - return null; - } - - panelComponent.getChildren().add(TitleComponent.builder() - .text("Hunllef") - .color(Color.pink) - .build()); - - Color color = plugin.getPlayerAttacks() == 1 ? Color.RED : Color.WHITE; - final String pHits = ColorUtil.prependColorTag(Integer.toString(plugin.getPlayerAttacks()), color); - - TableComponent tableComponent = new TableComponent(); - tableComponent.setColumnAlignments(TableAlignment.LEFT, TableAlignment.RIGHT); - tableComponent.addRow("Hunllef Hits: ", Integer.toString(plugin.getAttacks())); - tableComponent.addRow("Player Hits Left: ", pHits); - panelComponent.getChildren().add(tableComponent); - return panelComponent.render(graphics); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletInfoBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletInfoBox.java deleted file mode 100644 index c07d509a3d..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletInfoBox.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2019, ganom - * 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.gauntlet; - -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Rectangle; -import java.awt.image.BufferedImage; -import javax.annotation.Nullable; -import javax.inject.Inject; -import javax.inject.Singleton; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.Client; -import net.runelite.api.Prayer; -import net.runelite.api.SpriteID; -import net.runelite.client.game.SpriteManager; -import net.runelite.client.ui.overlay.Overlay; -import net.runelite.client.ui.overlay.OverlayPosition; -import net.runelite.client.ui.overlay.OverlayPriority; -import net.runelite.client.ui.overlay.components.ComponentConstants; -import net.runelite.client.ui.overlay.components.InfoBoxComponent; -import net.runelite.client.ui.overlay.components.PanelComponent; - -@Singleton -@Slf4j -public class GauntletInfoBox extends Overlay -{ - private static final Color NOT_ACTIVATED_BACKGROUND_COLOR = new Color(150, 0, 0, 150); - private final Client client; - private final GauntletPlugin plugin; - private final PanelComponent panelComponent = new PanelComponent(); - private final SpriteManager spriteManager; - - @Inject - GauntletInfoBox(final @Nullable Client client, final GauntletPlugin plugin, final SpriteManager spriteManager) - { - this.client = client; - this.plugin = plugin; - this.spriteManager = spriteManager; - setPosition(OverlayPosition.BOTTOM_RIGHT); - setPriority(OverlayPriority.HIGH); - } - - @Override - public Dimension render(Graphics2D graphics) - { - panelComponent.getChildren().clear(); - - if (plugin.getHunllef() == null || !plugin.isInRoom()) - { - return null; - } - - Prayer prayer = plugin.getNextPrayer(); - - if (prayer == null) - { - return null; - } - - InfoBoxComponent prayComponent = new InfoBoxComponent(); - BufferedImage prayImg = scaleImg(getPrayerImage(prayer)); - prayComponent.setImage(prayImg); - prayComponent.setColor(Color.WHITE); - prayComponent.setBackgroundColor(client.isPrayerActive(prayer) - ? ComponentConstants.STANDARD_BACKGROUND_COLOR - : NOT_ACTIVATED_BACKGROUND_COLOR); - prayComponent.setPreferredSize(new Dimension(40, 40)); - panelComponent.getChildren().add(prayComponent); - panelComponent.setPreferredSize(new Dimension(40, 40)); - panelComponent.setBorder(new Rectangle(0, 0, 0, 0)); - - return panelComponent.render(graphics); - } - - private BufferedImage getPrayerImage(Prayer prayer) - { - switch (prayer) - { - case PROTECT_FROM_MAGIC: - return spriteManager.getSprite(SpriteID.PRAYER_PROTECT_FROM_MAGIC, 0); - case PROTECT_FROM_MELEE: - return spriteManager.getSprite(SpriteID.PRAYER_PROTECT_FROM_MELEE, 0); - case PROTECT_FROM_MISSILES: - return spriteManager.getSprite(SpriteID.PRAYER_PROTECT_FROM_MISSILES, 0); - } - return null; - } - - private static BufferedImage scaleImg(final BufferedImage img) - { - if (img == null) - { - return null; - } - final double width = img.getWidth(null); - final double height = img.getHeight(null); - final double size = 36; // Limit size to 2 as that is minimum size not causing breakage - final double scalex = size / width; - final double scaley = size / height; - final double scale = Math.min(scalex, scaley); - final int newWidth = (int) (width * scale); - final int newHeight = (int) (height * scale); - final BufferedImage scaledImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB); - final Graphics g = scaledImage.createGraphics(); - g.drawImage(img, 0, 0, newWidth, newHeight, null); - g.dispose(); - return scaledImage; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletOverlay.java new file mode 100644 index 0000000000..915d636b78 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletOverlay.java @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2019, kThisIsCvpv + * Copyright (c) 2019, ganom + * 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.gauntlet; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.Model; +import net.runelite.api.NPC; +import net.runelite.api.Perspective; +import net.runelite.api.Point; +import net.runelite.api.Projectile; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.model.Jarvis; +import net.runelite.api.model.Vertex; +import net.runelite.client.graphics.ModelOutlineRenderer; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayPriority; +import net.runelite.client.ui.overlay.OverlayUtil; +import static net.runelite.client.util.ImageUtil.resizeImage; + +public class GauntletOverlay extends Overlay +{ + private static final Color FLASH_COLOR = new Color(255, 0, 0, 70); + private static final int MAX_DISTANCE = 2400; + private final Client client; + private final GauntletPlugin plugin; + private final ModelOutlineRenderer outlineRenderer; + private int timeout; + + @Inject + private GauntletOverlay(Client client, GauntletPlugin plugin, ModelOutlineRenderer outlineRenderer) + { + this.client = client; + this.plugin = plugin; + this.outlineRenderer = outlineRenderer; + + setPosition(OverlayPosition.DYNAMIC); + setPriority(OverlayPriority.HIGH); + setLayer(OverlayLayer.ALWAYS_ON_TOP); + } + + @Override + public Dimension render(Graphics2D graphics) + { + // Save resources. There's nothing to render if the user is not in a raid. + + if (!plugin.startedGauntlet()) + { + return null; + } + + if (plugin.fightingBoss()) + { + // This section handles the visuals when the player is in the boss room. + // This section handles the projectile overlays. + Set projectiles = plugin.getProjectiles(); + projectiles.forEach(projectile -> + { + BufferedImage icon = resizeImage(projectile.getImage(), plugin.getProjectileIconSize(), plugin.getProjectileIconSize()); + Color color = projectile.getColor(); + + Polygon polygon = boundProjectile(projectile.getProjectile()); + if (polygon == null) + { + int x = (int) projectile.getProjectile().getX(); + int y = (int) projectile.getProjectile().getY(); + + LocalPoint point = new LocalPoint(x, y); + Point loc = Perspective.getCanvasImageLocation(client, point, icon, -(int) projectile.getProjectile().getZ()); + + if (loc == null) + { + return; + } + + if (plugin.isUniqueAttackVisual()) + { + graphics.drawImage(icon, loc.getX(), loc.getY(), null); + } + } + else + { + graphics.setColor(color); + graphics.draw(polygon); + graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 50)); + graphics.fill(polygon); + if (plugin.isUniqueAttackVisual()) + { + Rectangle bounds = polygon.getBounds(); + int x = (int) bounds.getCenterX() - (icon.getWidth() / 2); + int y = (int) bounds.getCenterY() - (icon.getHeight() / 2); + graphics.drawImage(icon, x, y, null); + } + } + }); + projectiles.removeIf(proj -> proj.getProjectile().getRemainingCycles() <= 0); + + plugin.getTornadoes().forEach(tornado -> + { + if (plugin.isOverlayTornadoes()) + { + if (tornado.getTimeLeft() <= 0) + { + return; + } + + final String textOverlay = Integer.toString(tornado.getTimeLeft()); + final Point textLoc = Perspective.getCanvasTextLocation(client, graphics, tornado.getNpc().getLocalLocation(), textOverlay, 0); + final LocalPoint lp = LocalPoint.fromWorld(client, tornado.getNpc().getWorldLocation()); + + if (lp == null) + { + return; + } + + final Polygon tilePoly = Perspective.getCanvasTilePoly(client, lp); + OverlayUtil.renderPolygon(graphics, tilePoly, Color.YELLOW); + + if (textLoc == null) + { + return; + } + + Font oldFont = graphics.getFont(); + graphics.setFont(new Font("Arial", Font.BOLD, 20)); + Point pointShadow = new Point(textLoc.getX() + 1, textLoc.getY() + 1); + OverlayUtil.renderTextLocation(graphics, pointShadow, textOverlay, Color.BLACK); + OverlayUtil.renderTextLocation(graphics, textLoc, textOverlay, Color.YELLOW); + graphics.setFont(oldFont); + } + }); + + if (plugin.getHunllef() != null) + { + final Hunllef hunllef = plugin.getHunllef(); + final Hunllef.BossAttackPhase phase = hunllef.getCurrentPhase(); + final NPC boss = hunllef.getNpc(); + final LocalPoint point = boss.getLocalLocation(); + + if (plugin.isFlash()) + { + final Color flash = graphics.getColor(); + graphics.setColor(FLASH_COLOR); + graphics.fill(new Rectangle(client.getCanvas().getSize())); + graphics.setColor(flash); + timeout++; + if (timeout >= 15) + { + timeout = 0; + plugin.setFlash(false); + } + } + + if (plugin.isOverlayBoss()) + { + Polygon polygon = boss.getConvexHull(); + + if (polygon == null) + { + return null; + } + + if (phase.getPrayer() != null && !client.isPrayerActive(phase.getPrayer())) + { + Color color = phase.getColor(); + outlineRenderer.drawOutline(boss, 12, color, new Color(0, 0, 0, 0)); + } + } + + if (plugin.isOverlayBossPrayer()) + { + BufferedImage attackIcon = null; + + switch (phase) + { + case MAGIC: + attackIcon = resizeImage(hunllef.getMage(), plugin.getProjectileIconSize(), plugin.getProjectileIconSize()); + break; + case RANGE: + attackIcon = resizeImage(hunllef.getRange(), plugin.getProjectileIconSize(), plugin.getProjectileIconSize()); + break; + default: + break; + } + + if (attackIcon == null) + { + return null; + } + + Point imageLoc = Perspective.getCanvasImageLocation(client, point, attackIcon, boss.getLogicalHeight() / 2); + + if (imageLoc == null) + { + return null; + } + + graphics.drawImage(attackIcon, imageLoc.getX(), imageLoc.getY(), null); + } + + if (plugin.isHighlightWidget()) + { + if (phase.getPrayer() == null) + { + return null; + } + + final Rectangle bounds = OverlayUtil.renderPrayerOverlay(graphics, client, phase.getPrayer(), phase.getColor()); + + if (bounds != null) + { + final Color color = hunllef.getTicksUntilAttack() == 1 ? Color.WHITE : phase.getColor(); + renderTextLocation(graphics, Integer.toString(hunllef.getTicksUntilAttack()), 16, Font.BOLD, color, centerPoint(bounds), false); + } + } + + // This section handles any text overlays. + String textOverlay = ""; + + // Handles the counter for the boss. + if (plugin.isCountBossAttacks()) + { + textOverlay = Integer.toString(hunllef.getBossAttacks()); + } + + // Handles the counter for the player. + if (plugin.isCountPlayerAttacks()) + { + if (textOverlay.length() > 0) + { + textOverlay += " | "; + } + textOverlay += Integer.toString(hunllef.getPlayerAttacks()); + } + + // Handles drawing the text onto the boss. + if (textOverlay.length() > 0) + { + Point textLoc = Perspective.getCanvasTextLocation(client, graphics, point, textOverlay, boss.getLogicalHeight() / 2); + + if (textLoc == null) + { + return null; + } + + textLoc = new Point(textLoc.getX(), textLoc.getY() + 35); + + Font oldFont = graphics.getFont(); + + graphics.setFont(new Font("Arial", Font.BOLD, 20)); + Point pointShadow = new Point(textLoc.getX() + 1, textLoc.getY() + 1); + + OverlayUtil.renderTextLocation(graphics, pointShadow, textOverlay, Color.BLACK); + OverlayUtil.renderTextLocation(graphics, textLoc, textOverlay, phase.getColor()); + + graphics.setFont(oldFont); + } + } + } + else + { + // This section overlays all resources. + final LocalPoint playerLocation = client.getLocalPlayer().getLocalLocation(); + + final Set resources = plugin.getResources(); + resources.forEach(object -> + { + if (object.getGameObject().getLocalLocation().distanceTo(playerLocation) < MAX_DISTANCE) + { + + // Don't use Convex Hull click box. As the room start to fill up, your FPS will dip. + Polygon polygon = object.getGameObject().getConvexHull(); + + if (polygon == null) + { + return; + } + // This section will highlight the resource with color. + if (plugin.isHighlightResources()) + { + outlineRenderer.drawOutline(object.getGameObject(), 2, plugin.getHighlightResourcesColor()); + } + + // This section will overlay the resource with an icon. + if (plugin.isHighlightResourcesIcons()) + { + BufferedImage icon = resizeImage(object.getImage(), plugin.getResourceIconSize(), plugin.getResourceIconSize()); + + if (icon != null) + { + Rectangle bounds = polygon.getBounds(); + int startX = (int) bounds.getCenterX() - (icon.getWidth() / 2); + int startY = (int) bounds.getCenterY() - (icon.getHeight() / 2); + graphics.drawImage(icon, startX, startY, null); + } + } + } + }); + } + return null; + } + + private Polygon boundProjectile(Projectile proj) + { + if (proj == null || proj.getModel() == null) + { + return null; + } + + Model model = proj.getModel(); + LocalPoint point = new LocalPoint((int) proj.getX(), (int) proj.getY()); + int tileHeight = Perspective.getTileHeight(client, point, client.getPlane()); + + double angle = Math.atan(proj.getVelocityY() / proj.getVelocityX()); + angle = Math.toDegrees(angle) + (proj.getVelocityX() < 0 ? 180 : 0); + angle = angle < 0 ? angle + 360 : angle; + angle = 360 - angle - 90; + + double ori = angle * (512d / 90d); + ori = ori < 0 ? ori + 2048 : ori; + + int orientation = (int) Math.round(ori); + + List vertices = model.getVertices(); + for (int i = 0; i < vertices.size(); ++i) + { + vertices.set(i, vertices.get(i).rotate(orientation)); + } + + List list = new ArrayList<>(); + + for (Vertex vertex : vertices) + { + final Point localToCanvas = Perspective.localToCanvas(client, point.getX() - vertex.getX(), point.getY() - vertex.getZ(), tileHeight + vertex.getY() + (int) proj.getZ()); + if (localToCanvas != null) + { + list.add(localToCanvas); + } + } + + final List convexHull = Jarvis.convexHull(list); + if (convexHull == null) + { + return null; + } + + final Polygon polygon = new Polygon(); + for (final Point hullPoint : convexHull) + { + polygon.addPoint(hullPoint.getX(), hullPoint.getY()); + } + + return polygon; + } + + private void renderTextLocation(Graphics2D graphics, String txtString, int fontSize, int fontStyle, Color fontColor, Point canvasPoint, boolean shadows) + { + graphics.setFont(new Font("Arial", fontStyle, fontSize)); + if (canvasPoint != null) + { + final Point canvasCenterPoint = new Point( + canvasPoint.getX() - 3, + canvasPoint.getY() + 6); + final Point canvasCenterPoint_shadow = new Point( + canvasPoint.getX() - 2, + canvasPoint.getY() + 7); + if (shadows) + { + OverlayUtil.renderTextLocation(graphics, canvasCenterPoint_shadow, txtString, Color.BLACK); + } + OverlayUtil.renderTextLocation(graphics, canvasCenterPoint, txtString, fontColor); + } + } + + private Point centerPoint(Rectangle rect) + { + int x = (int) (rect.getX() + rect.getWidth() / 2); + int y = (int) (rect.getY() + rect.getHeight() / 2); + return new Point(x, y); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletPlugin.java index 7bffc75c7d..2bae9afb99 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletPlugin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, xperiaclash + * Copyright (c) 2019, kThisIsCvpv * Copyright (c) 2019, ganom * All rights reserved. * @@ -22,244 +22,430 @@ * (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.gauntlet; import com.google.common.collect.ImmutableSet; +import com.google.inject.Provides; +import java.awt.Color; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; import java.util.Set; import javax.inject.Inject; -import javax.inject.Singleton; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.AnimationID; +import net.runelite.api.Actor; import net.runelite.api.Client; +import net.runelite.api.GameObject; +import net.runelite.api.GameState; import net.runelite.api.HeadIcon; import net.runelite.api.NPC; import net.runelite.api.NPCDefinition; -import net.runelite.api.Prayer; +import net.runelite.api.NpcID; +import net.runelite.api.ObjectID; +import net.runelite.api.Player; +import net.runelite.api.Projectile; import net.runelite.api.ProjectileID; +import net.runelite.api.SoundEffectID; import net.runelite.api.Varbits; import net.runelite.api.events.AnimationChanged; +import net.runelite.api.events.ConfigChanged; +import net.runelite.api.events.GameObjectDespawned; +import net.runelite.api.events.GameObjectSpawned; +import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.GameTick; import net.runelite.api.events.NpcDespawned; import net.runelite.api.events.NpcSpawned; import net.runelite.api.events.ProjectileSpawned; +import net.runelite.api.events.VarbitChanged; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.EventBus; +import net.runelite.client.game.SkillIconManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.PluginType; +import static net.runelite.client.plugins.gauntlet.Hunllef.BossAttack.LIGHTNING; +import static net.runelite.client.plugins.gauntlet.Hunllef.BossAttack.MAGIC; +import static net.runelite.client.plugins.gauntlet.Hunllef.BossAttack.PRAYER; +import static net.runelite.client.plugins.gauntlet.Hunllef.BossAttack.RANGE; import net.runelite.client.ui.overlay.OverlayManager; @PluginDescriptor( - name = "Gauntlet Boss Helper", - description = "Prayer Overlay For The Gauntlet Boss", - tags = {"gauntlet"}, - type = PluginType.PVM, - enabledByDefault = false + name = "Gauntlet", + description = "All-in-one plugin for the Gauntlet.", + tags = {"Gauntlet"}, + enabledByDefault = false, + type = PluginType.PVM ) - -@Singleton -@Slf4j @Getter(AccessLevel.PACKAGE) -@Setter(AccessLevel.PACKAGE) public class GauntletPlugin extends Plugin { - private static final Set PLAYER_ANIMATIONS = ImmutableSet.of(426, 1167, 422, 423, 440, 428); - private static final Set HUNLEFF_ANIMATIONS = ImmutableSet.of(AnimationID.HUNLEFF_ATTACK, AnimationID.HUNLEFF_TORNADO); - private static final Set HUNLEFF_MAGE_PROJECTILES = ImmutableSet.of(ProjectileID.HUNLEFF_MAGE_ATTACK, ProjectileID.HUNLEFF_CORRUPTED_MAGE_ATTACK); - private static final Set HUNLEFF_RANGE_PROJECTILES = ImmutableSet.of(ProjectileID.HUNLEFF_RANGE_ATTACK, ProjectileID.HUNLEFF_CORRUPTED_RANGE_ATTACK); + private static final int BOW_ATTACK = 426; + private static final int STAFF_ATTACK = 1167; + private static final int LIGHTNING_ANIMATION = 8418; + private static final Set TORNADO_NPC_IDS = ImmutableSet.of(9025, 9039); + private static final Set MELEE_ANIMATIONS = ImmutableSet.of(395, 401, 400, 401, 386, 390, 422, 423, 401, 428, 440); + private static final Set PLAYER_ANIMATIONS = ImmutableSet.of(395, 401, 400, 401, 386, 390, 422, 423, 401, 428, 440, 426, 1167); + private static final Set HUNLLEF_MAGE_PROJECTILES = ImmutableSet.of(ProjectileID.HUNLLEF_MAGE_ATTACK, ProjectileID.HUNLLEF_CORRUPTED_MAGE_ATTACK); + private static final Set HUNLLEF_RANGE_PROJECTILES = ImmutableSet.of(ProjectileID.HUNLLEF_RANGE_ATTACK, ProjectileID.HUNLLEF_CORRUPTED_RANGE_ATTACK); + private static final Set HUNLLEF_PRAYER_PROJECTILES = ImmutableSet.of(ProjectileID.HUNLLEF_PRAYER_ATTACK, ProjectileID.HUNLLEF_CORRUPTED_PRAYER_ATTACK); + private static final Set HUNLLEF_PROJECTILES = ImmutableSet.of(ProjectileID.HUNLLEF_PRAYER_ATTACK, ProjectileID.HUNLLEF_CORRUPTED_PRAYER_ATTACK, + ProjectileID.HUNLLEF_RANGE_ATTACK, ProjectileID.HUNLLEF_CORRUPTED_RANGE_ATTACK, ProjectileID.HUNLLEF_MAGE_ATTACK, ProjectileID.HUNLLEF_CORRUPTED_MAGE_ATTACK + ); + private static final Set HUNLLEF_NPC_IDS = ImmutableSet.of(NpcID.CRYSTALLINE_HUNLLEF, NpcID.CRYSTALLINE_HUNLLEF_9022, NpcID.CRYSTALLINE_HUNLLEF_9023, + NpcID.CRYSTALLINE_HUNLLEF_9024, NpcID.CORRUPTED_HUNLLEF, NpcID.CORRUPTED_HUNLLEF_9036, NpcID.CORRUPTED_HUNLLEF_9037, NpcID.CORRUPTED_HUNLLEF_9038 + ); + private static final Set RESOURCES = ImmutableSet.of(ObjectID.CRYSTAL_DEPOSIT, ObjectID.CORRUPT_DEPOSIT, ObjectID.PHREN_ROOTS, + ObjectID.PHREN_ROOTS_36066, ObjectID.FISHING_SPOT_36068, ObjectID.FISHING_SPOT_35971, ObjectID.GRYM_ROOT, ObjectID.GRYM_ROOT_36070, + ObjectID.LINUM_TIRINUM, ObjectID.LINUM_TIRINUM_36072 + ); + @Inject @Getter(AccessLevel.NONE) - @Setter(AccessLevel.NONE) private Client client; @Inject @Getter(AccessLevel.NONE) - @Setter(AccessLevel.NONE) - private EventBus eventBus; + private ClientThread clientThread; @Inject @Getter(AccessLevel.NONE) - @Setter(AccessLevel.NONE) private OverlayManager overlayManager; @Inject @Getter(AccessLevel.NONE) - @Setter(AccessLevel.NONE) - private GauntletInfoBox GauntletInfoBox; + private GauntletOverlay overlay; @Inject @Getter(AccessLevel.NONE) - @Setter(AccessLevel.NONE) - private GauntletCounter GauntletCounter; - private int attacks = 0; - private int playerAttacks = 0; - private Prayer nextPrayer; - private NPC hunllef; - private boolean firstHitDetected; - private HeadIcon currentPrayer; + private GauntletConfig config; + @Inject + @Getter(AccessLevel.NONE) + private EventBus eventBus; + @Inject + @Getter(AccessLevel.NONE) + private GauntletTimer timer; + @Inject + @Getter(AccessLevel.NONE) + private SkillIconManager skillIconManager; + @Setter(AccessLevel.PACKAGE) + private Hunllef hunllef; + private final Set resources = new HashSet<>(); + private final Set projectiles = new HashSet<>(); + private final Map items = new HashMap<>(); + private Set tornadoes = new HashSet<>(); + private boolean completeStartup = false; + private boolean countBossAttacks; + private boolean countPlayerAttacks; + private boolean displayTimerChat; + private boolean highlightResources; + private boolean highlightResourcesIcons; + private boolean highlightWidget; + private boolean overlayBoss; + private boolean overlayBossPrayer; + private boolean overlayTornadoes; + @Setter(AccessLevel.PACKAGE) + private boolean flash; + private boolean flashOnWrongAttack; + private Color highlightResourcesColor; + private boolean displayTimerWidget; + private boolean timerVisible = true; + private boolean uniqueAttackVisual; + private boolean uniquePrayerAudio; + private boolean uniquePrayerVisual; + private int resourceIconSize; + private int projectileIconSize; - @Override - protected void startUp() throws Exception + @Provides + GauntletConfig getConfig(ConfigManager configManager) { - addSubscriptions(); - overlayManager.add(GauntletInfoBox); - overlayManager.add(GauntletCounter); - reset(); + return configManager.getConfig(GauntletConfig.class); } @Override - protected void shutDown() throws Exception + protected void startUp() + { + addSubscriptions(); + updateConfig(); + timerVisible = this.displayTimerWidget; + timer.resetStates(); + if (timerVisible) + { + overlayManager.add(timer); + } + overlayManager.add(overlay); + if (client.getGameState() != GameState.STARTING && client.getGameState() != GameState.UNKNOWN) + { + completeStartup = false; + clientThread.invoke(() -> { + timer.initStates(); + completeStartup = true; + }); + } + else + { + completeStartup = true; + } + } + + @Override + protected void shutDown() { eventBus.unregister(this); - overlayManager.remove(GauntletInfoBox); - overlayManager.remove(GauntletCounter); - reset(); + timer.resetStates(); + if (timerVisible) + { + overlayManager.remove(timer); + timerVisible = false; + } + overlayManager.remove(overlay); + resources.clear(); + projectiles.clear(); + tornadoes.clear(); + setHunllef(null); } private void addSubscriptions() { eventBus.subscribe(AnimationChanged.class, this, this::onAnimationChanged); - eventBus.subscribe(NpcSpawned.class, this, this::onNpcSpawned); - eventBus.subscribe(NpcDespawned.class, this, this::onNpcDespawned); - eventBus.subscribe(ProjectileSpawned.class, this, this::onProjectileSpawned); + eventBus.subscribe(ConfigChanged.class, this, this::onConfigChanged); + eventBus.subscribe(GameObjectDespawned.class, this, this::onGameObjectDespawned); + eventBus.subscribe(GameObjectSpawned.class, this, this::onGameObjectSpawned); + eventBus.subscribe(GameStateChanged.class, this, this::onGameStateChanged); eventBus.subscribe(GameTick.class, this, this::onGameTick); + eventBus.subscribe(NpcDespawned.class, this, this::onNpcDespawned); + eventBus.subscribe(NpcSpawned.class, this, this::onNpcSpawned); + eventBus.subscribe(ProjectileSpawned.class, this, this::onProjectileSpawned); + eventBus.subscribe(VarbitChanged.class, this, this::onVarbitChanged); } private void onAnimationChanged(AnimationChanged event) { - if (getHunllef() == null || !isInRoom()) + final Actor actor = event.getActor(); + + // This section handles the player counter. + if (actor instanceof Player && fightingBoss()) + { + final Player player = (Player) actor; + final int anim = player.getAnimation(); + + if (!player.getName().equals(client.getLocalPlayer().getName()) || anim == -1 || !PLAYER_ANIMATIONS.contains(anim)) + { + return; + } + + NPCDefinition comp = hunllef.getNpc().getDefinition(); + + if (comp == null || comp.getOverheadIcon() == null) + { + return; + } + + final HeadIcon prayer = comp.getOverheadIcon(); + + switch (prayer) + { + case MELEE: + if (MELEE_ANIMATIONS.contains(anim)) + { + setFlash(true); + return; + } + hunllef.updatePlayerAttack(); + break; + case RANGED: + if (BOW_ATTACK == anim) + { + setFlash(true); + return; + } + hunllef.updatePlayerAttack(); + break; + case MAGIC: + if (STAFF_ATTACK == anim) + { + setFlash(true); + return; + } + hunllef.updatePlayerAttack(); + break; + } + } + + // This section handles the boss attack counter if they perform a lightning attack. + if (actor instanceof NPC) + { + final NPC npc = (NPC) actor; + + if (npc.getAnimation() == LIGHTNING_ANIMATION) + { + hunllef.updateAttack(LIGHTNING); + } + } + } + + private void onConfigChanged(ConfigChanged event) + { + if (!event.getGroup().equals("Gauntlet")) { return; } - final int anim = event.getActor().getAnimation(); + updateConfig(); - if (HUNLEFF_ANIMATIONS.contains(anim)) + if (event.getKey().equals("displayTimerWidget")) { - setAttacks(getAttacks() + 1); - - if (getAttacks() == 4) + if (this.displayTimerWidget && !timerVisible) { - if (getNextPrayer() == Prayer.PROTECT_FROM_MISSILES) - { - log.debug("Attacks are: {}, switching to prot mage", getAttacks()); - setNextPrayer(Prayer.PROTECT_FROM_MAGIC); - } - else if (getNextPrayer() == Prayer.PROTECT_FROM_MAGIC) - { - log.debug("Attacks are: {}, switching to prot missiles", getAttacks()); - setNextPrayer(Prayer.PROTECT_FROM_MISSILES); - } - setAttacks(0); + overlayManager.add(timer); + timerVisible = true; + } + else if (!this.displayTimerWidget && timerVisible) + { + overlayManager.remove(timer); + timerVisible = false; } } - else if (PLAYER_ANIMATIONS.contains(anim)) + } + + private void onGameObjectDespawned(GameObjectDespawned event) + { + final GameObject obj = event.getGameObject(); + if (RESOURCES.contains(obj.getId())) { - setPlayerAttacks(getPlayerAttacks() - 1); - if (getPlayerAttacks() == 0) - { - setPlayerAttacks(6); - } + resources.removeIf(object -> object.getGameObject() == obj); + } + } + + private void onGameObjectSpawned(GameObjectSpawned event) + { + final GameObject obj = event.getGameObject(); + if (RESOURCES.contains(obj.getId())) + { + resources.add(new Resources(obj, event.getTile(), skillIconManager)); + } + } + + private void onGameStateChanged(GameStateChanged event) + { + if (event.getGameState() == GameState.LOADING) + { + resources.clear(); } } private void onGameTick(GameTick event) { - if (getHunllef() == null || !isInRoom()) + // This handles the timer based on player health. + if (this.completeStartup) { - return; + timer.checkStates(false); } - - HeadIcon overhead = getOverheadIcon(getHunllef()); - - if (overhead == null) + if (!tornadoes.isEmpty()) { - return; + tornadoes.forEach(Tornado::updateTimeLeft); } - - switch (overhead) + if (hunllef != null) { - case MELEE: - case MAGIC: - case RANGED: - if (currentPrayer == overhead) - { - return; - } - currentPrayer = overhead; - setPlayerAttacks(6); - break; + if (hunllef.getTicksUntilAttack() > 0) + { + hunllef.setTicksUntilAttack(hunllef.getTicksUntilAttack() - 1); + } } } private void onNpcDespawned(NpcDespawned event) { final NPC npc = event.getNpc(); - - if (npc.getName() == null || !npc.getName().toLowerCase().contains("hunllef")) + if (HUNLLEF_NPC_IDS.contains(npc.getId())) { - return; + setHunllef(null); + } + else if (TORNADO_NPC_IDS.contains(npc.getId())) + { + tornadoes.removeIf(tornado -> tornado.getNpc() == npc); } - - reset(); } private void onNpcSpawned(NpcSpawned event) { final NPC npc = event.getNpc(); - - if (npc.getName() == null || !npc.getName().toLowerCase().contains("hunllef")) + if (HUNLLEF_NPC_IDS.contains(npc.getId())) { - return; + setHunllef(new Hunllef(npc, skillIconManager)); + } + else if (TORNADO_NPC_IDS.contains(npc.getId())) + { + tornadoes.add(new Tornado(npc)); } - - setPlayerAttacks(6); - setAttacks(0); - setHunllef(npc); } private void onProjectileSpawned(ProjectileSpawned event) { - if (getHunllef() == null || !isInRoom() || isFirstHitDetected()) + if (hunllef == null) { return; } - final int projectileID = event.getProjectile().getId(); + final Projectile proj = event.getProjectile(); - if (HUNLEFF_MAGE_PROJECTILES.contains(projectileID)) + if (HUNLLEF_PROJECTILES.contains(proj.getId())) { - setNextPrayer(Prayer.PROTECT_FROM_MAGIC); - setFirstHitDetected(true); - } - else if (HUNLEFF_RANGE_PROJECTILES.contains(projectileID)) - { - setNextPrayer(Prayer.PROTECT_FROM_MISSILES); - setFirstHitDetected(true); + projectiles.add(new Missiles(proj, skillIconManager)); + if (HUNLLEF_MAGE_PROJECTILES.contains(proj.getId())) + { + hunllef.updateAttack(MAGIC); + } + else if (HUNLLEF_PRAYER_PROJECTILES.contains(proj.getId())) + { + hunllef.updateAttack(PRAYER); + if (this.uniquePrayerAudio) + { + client.playSoundEffect(SoundEffectID.MAGIC_SPLASH_BOING); + } + } + else if (HUNLLEF_RANGE_PROJECTILES.contains(proj.getId())) + { + hunllef.updateAttack(RANGE); + } } } - - private void reset() + private void onVarbitChanged(VarbitChanged event) { - setHunllef(null); - setNextPrayer(null); - setCurrentPrayer(null); - setFirstHitDetected(false); - setAttacks(0); - setPlayerAttacks(6); - } - - private HeadIcon getOverheadIcon(NPC npc) - { - NPCDefinition composition = npc.getDefinition(); - if (composition != null) + if (this.completeStartup) { - return composition.getOverheadIcon(); + timer.checkStates(true); } - return null; } - boolean isInRoom() + boolean fightingBoss() { return client.getVar(Varbits.GAUNTLET_FINAL_ROOM_ENTERED) == 1; } + + boolean startedGauntlet() + { + return client.getVar(Varbits.GAUNTLET_ENTERED) == 1; + } + + private void updateConfig() + { + this.highlightResources = config.highlightResources(); + this.highlightResourcesColor = config.highlightResourcesColor(); + this.highlightResourcesIcons = config.highlightResourcesIcons(); + this.flashOnWrongAttack = config.flashOnWrongAttack(); + this.highlightWidget = config.highlightWidget(); + this.resourceIconSize = config.resourceIconSize(); + this.projectileIconSize = config.projectileIconSize(); + this.countBossAttacks = config.countBossAttacks(); + this.countPlayerAttacks = config.countPlayerAttacks(); + this.uniquePrayerAudio = config.uniquePrayerAudio(); + this.uniquePrayerVisual = config.uniquePrayerVisual(); + this.uniqueAttackVisual = config.uniqueAttackVisual(); + this.overlayBoss = config.overlayBoss(); + this.overlayBossPrayer = config.overlayBossPrayer(); + this.overlayTornadoes = config.overlayTornadoes(); + this.displayTimerWidget = config.displayTimerWidget(); + this.displayTimerChat = config.displayTimerChat(); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletTimer.java b/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletTimer.java new file mode 100644 index 0000000000..1737bbec6e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletTimer.java @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2018, 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.gauntlet; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import javax.inject.Inject; +import net.runelite.api.ChatMessageType; +import net.runelite.api.Client; +import static net.runelite.api.MenuAction.RUNELITE_OVERLAY_CONFIG; +import net.runelite.api.Player; +import net.runelite.client.chat.ChatColorType; +import net.runelite.client.chat.ChatMessageBuilder; +import net.runelite.client.chat.ChatMessageManager; +import net.runelite.client.chat.QueuedMessage; +import static net.runelite.client.plugins.gauntlet.GauntletTimer.RaidState.IN_BOSS; +import static net.runelite.client.plugins.gauntlet.GauntletTimer.RaidState.IN_RAID; +import static net.runelite.client.plugins.gauntlet.GauntletTimer.RaidState.UNKNOWN; +import net.runelite.client.ui.overlay.Overlay; +import static net.runelite.client.ui.overlay.OverlayManager.OPTION_CONFIGURE; +import net.runelite.client.ui.overlay.OverlayMenuEntry; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayPriority; +import net.runelite.client.ui.overlay.components.PanelComponent; +import net.runelite.client.ui.overlay.components.TitleComponent; +import net.runelite.client.ui.overlay.components.table.TableAlignment; +import net.runelite.client.ui.overlay.components.table.TableComponent; + +class GauntletTimer extends Overlay +{ + private final Client client; + private final GauntletPlugin plugin; + private final PanelComponent panelComponent = new PanelComponent(); + @Inject + private ChatMessageManager chatMessageManager; + private long timeRaidStart = -1L; + private long timeBossEnter = -1L; + private RaidState currentState = UNKNOWN; + + @Inject + public GauntletTimer(Client client, GauntletPlugin plugin) + { + super(plugin); + + setPosition(OverlayPosition.ABOVE_CHATBOX_RIGHT); + setPriority(OverlayPriority.HIGH); + + this.client = client; + this.plugin = plugin; + + getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY_CONFIG, OPTION_CONFIGURE, "Gauntlet Timer Overlay")); + } + + /** + * Resets the timer. + */ + void resetStates() + { + timeRaidStart = -1L; + timeBossEnter = -1L; + + currentState = UNKNOWN; + } + + /** + * This is called when the player resets the plugin mid-raid. We do not want to confuse the timer. + *

+ * TODO: Originally, this function will disable the timer if the plugin is started mid raid. + * Unfortunately, VARBITS can't be checked unless you're on the client thread. + * I've no idea how to access RL's task handler. + * Good luck to you. If you restart plugin mid raid, oh well. Your timer's going to be inaccurate. + */ + void initStates() + { + timeRaidStart = -1L; + timeBossEnter = -1L; + + if (plugin.startedGauntlet()) + { + currentState = IN_RAID; + if (plugin.fightingBoss()) + { + currentState = IN_BOSS; + } + } + else + { + currentState = UNKNOWN; + } + } + + /** + * Converts the different between two epoch times into minutes:seconds format. + * + * @param epochA long + * @param epochB long + * @return String + */ + private String calculateElapsedTime(long epochA, long epochB) + { + long max = Math.max(epochA, epochB); + long min = Math.min(epochA, epochB); + + long elapsedEpoch = max - min; + long seconds = elapsedEpoch / 1000L; + + long minutes = seconds / 60L; + seconds = seconds % 60; + + if (seconds == 0) + { + return minutes + ":00"; + } + + if (seconds < 10) + { + return minutes + ":0" + seconds; + } + + return minutes + ":" + seconds; + } + + /** + * Called when varbit changes. See if the the raid state has changed. + */ + void checkStates(boolean checkVarps) + { + final Player p = client.getLocalPlayer(); + + if (p == null || !plugin.isCompleteStartup()) + { + return; + } + + if (checkVarps) + { + switch (currentState) + { + case UNKNOWN: + if (plugin.startedGauntlet() && p.getHealthRatio() != 0) + { + // Player has started a new raid. + if (!plugin.fightingBoss()) + { + currentState = IN_RAID; + timeRaidStart = System.currentTimeMillis(); + return; + } + currentState = IN_RAID; + timeRaidStart = timeBossEnter = System.currentTimeMillis(); + } + break; + case IN_RAID: + if (!plugin.startedGauntlet()) + { + printPrepTime(); + resetStates(); + return; + } + if (plugin.fightingBoss()) + { + // Player has begun the boss fight. + printPrepTime(); + currentState = IN_BOSS; + timeBossEnter = System.currentTimeMillis(); + } + break; + case IN_BOSS: + if (!plugin.fightingBoss() || !plugin.startedGauntlet()) + { + // Player has killed the boss. + resetStates(); + } + break; + } + } + else + { + if (currentState == IN_BOSS && p.getHealthRatio() == 0) + { + printBossTime(); + resetStates(); + } + } + } + + private void printPrepTime() + { + if (!plugin.isDisplayTimerChat() || timeRaidStart == -1L) + { + return; + } + + String elapsedTime = calculateElapsedTime(System.currentTimeMillis(), timeRaidStart); + + final ChatMessageBuilder prepmessage = new ChatMessageBuilder() + .append(ChatColorType.NORMAL) + .append("Preparation time: ") + .append(ChatColorType.HIGHLIGHT) + .append(elapsedTime); + + chatMessageManager.queue(QueuedMessage.builder() + .type(ChatMessageType.FRIENDSCHATNOTIFICATION) + .runeLiteFormattedMessage(prepmessage.build()) + .build()); + } + + private void printBossTime() + { + if (!plugin.isDisplayTimerChat() || timeRaidStart == -1L || timeBossEnter == -1L) + { + return; + } + + String elapsedBossTime = calculateElapsedTime(System.currentTimeMillis(), timeBossEnter); + String elapsedPrepTime = calculateElapsedTime(timeRaidStart, timeBossEnter); + String elapsedTotalTime = calculateElapsedTime(System.currentTimeMillis(), timeRaidStart); + + final ChatMessageBuilder challengedurationmessage = new ChatMessageBuilder() + .append(ChatColorType.NORMAL) + .append("Challenge duration: ") + .append(ChatColorType.HIGHLIGHT) + .append(elapsedTotalTime); + + chatMessageManager.queue(QueuedMessage.builder() + .type(ChatMessageType.FRIENDSCHATNOTIFICATION) + .runeLiteFormattedMessage(challengedurationmessage.build()) + .build()); + + final ChatMessageBuilder prepdeathmessage = new ChatMessageBuilder() + .append(ChatColorType.NORMAL) + .append("Preparation time: ") + .append(ChatColorType.HIGHLIGHT) + .append(elapsedPrepTime) + .append(ChatColorType.NORMAL) + .append(" player/boss death time: ") + .append(ChatColorType.HIGHLIGHT) + .append(elapsedBossTime); + + chatMessageManager.queue(QueuedMessage.builder() + .type(ChatMessageType.FRIENDSCHATNOTIFICATION) + .runeLiteFormattedMessage(prepdeathmessage.build()) + .build()); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (currentState == UNKNOWN) + { + return null; + } + + panelComponent.getChildren().clear(); + panelComponent.getChildren().add(TitleComponent.builder().text("Gauntlet Timer").color(Color.WHITE).build()); + TableComponent tableComponent = new TableComponent(); + tableComponent.setColumnAlignments(TableAlignment.LEFT, TableAlignment.RIGHT); + + if (timeRaidStart == -1L) + { // User restarted the plugin mid raid. Timer is inaccurate. + tableComponent.addRow("Inactive", "0:00"); + } + else + { + String elapsedPrepTime, elapsedBossTime, elapsedTotalTime; + elapsedTotalTime = calculateElapsedTime(System.currentTimeMillis(), timeRaidStart); + + if (currentState == IN_RAID) + { + elapsedPrepTime = calculateElapsedTime(timeRaidStart, System.currentTimeMillis()); + elapsedBossTime = "0:00"; + } + else + { + elapsedPrepTime = calculateElapsedTime(timeRaidStart, timeBossEnter); + elapsedBossTime = calculateElapsedTime(System.currentTimeMillis(), timeBossEnter); + } + tableComponent.addRow("Preparation", elapsedPrepTime); + tableComponent.addRow("Boss Fight", elapsedBossTime); + tableComponent.addRow("Total Time", elapsedTotalTime); + panelComponent.getChildren().add(tableComponent); + } + return panelComponent.render(graphics); + } + + public enum RaidState + { + UNKNOWN, IN_RAID, IN_BOSS + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/Hunllef.java b/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/Hunllef.java new file mode 100644 index 0000000000..215c061923 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/Hunllef.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2019, ganom + * 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.gauntlet; + +import java.awt.Color; +import java.awt.image.BufferedImage; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import net.runelite.api.NPC; +import net.runelite.api.Prayer; +import net.runelite.api.Skill; +import net.runelite.client.game.SkillIconManager; + +@Getter(AccessLevel.PACKAGE) +@Setter(AccessLevel.PACKAGE) +public class Hunllef +{ + private NPC npc; + private int bossAttacks; + private int playerAttacks; + private int ticksUntilAttack; + private BufferedImage mage; + private BufferedImage range; + private BossAttackPhase currentPhase; + + Hunllef(NPC npc, SkillIconManager skillIconManager) + { + this.npc = npc; + this.bossAttacks = 0; + this.playerAttacks = 6; + this.ticksUntilAttack = 0; + this.mage = skillIconManager.getSkillImage(Skill.MAGIC); + this.range = skillIconManager.getSkillImage(Skill.RANGED); + this.currentPhase = BossAttackPhase.UNKNOWN; + } + + void updatePlayerAttack() + { + playerAttacks--; + if (playerAttacks <= 0) + { + playerAttacks = 6; + } + } + + void updateAttack(BossAttack style) + { + ticksUntilAttack = 6; + if (style == BossAttack.PRAYER) + { + style = BossAttack.MAGIC; + } + + if (style == BossAttack.LIGHTNING) + { + bossAttacks--; + } + else if (style == BossAttack.RANGE) + { + if (currentPhase != BossAttackPhase.RANGE) + { + currentPhase = BossAttackPhase.RANGE; + bossAttacks = 3; + } + else + { + bossAttacks--; + } + } + else if (style == BossAttack.MAGIC) + { + if (currentPhase != BossAttackPhase.MAGIC) + { + currentPhase = BossAttackPhase.MAGIC; + bossAttacks = 3; + } + else + { + bossAttacks--; + } + } + + if (bossAttacks <= 0) + { + BossAttackPhase nextPhase; + + switch (currentPhase) + { + case MAGIC: + bossAttacks = 4; + nextPhase = BossAttackPhase.RANGE; + break; + case RANGE: + bossAttacks = 4; + nextPhase = BossAttackPhase.MAGIC; + break; + default: + bossAttacks = 0; + nextPhase = BossAttackPhase.UNKNOWN; + break; + } + currentPhase = nextPhase; + } + } + + @AllArgsConstructor + @Getter(AccessLevel.PACKAGE) + enum BossAttackPhase + { + MAGIC(Color.CYAN, Prayer.PROTECT_FROM_MAGIC), + RANGE(Color.GREEN, Prayer.PROTECT_FROM_MISSILES), + UNKNOWN(Color.WHITE, null); + + private Color color; + private Prayer prayer; + } + + enum BossAttack + { + MAGIC, + RANGE, + PRAYER, + LIGHTNING + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/Missiles.java b/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/Missiles.java new file mode 100644 index 0000000000..445d50a77a --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/Missiles.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2019, ganom + * 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.gauntlet; + +import java.awt.Color; +import java.awt.image.BufferedImage; +import lombok.AccessLevel; +import lombok.Getter; +import net.runelite.api.Projectile; +import net.runelite.api.ProjectileID; +import net.runelite.api.Skill; +import net.runelite.client.game.SkillIconManager; + +@Getter(AccessLevel.PACKAGE) +class Missiles +{ + private Projectile projectile; + private int id; + private BufferedImage image; + private Color color; + + Missiles(Projectile projectile, SkillIconManager skillIconManager) + { + this.projectile = projectile; + this.id = projectile.getId(); + this.image = assignedImage(skillIconManager, id); + this.color = assignedColor(id); + } + + private Color assignedColor(int id) + { + switch (id) + { + case ProjectileID.HUNLLEF_MAGE_ATTACK: + case ProjectileID.HUNLLEF_CORRUPTED_MAGE_ATTACK: + return Color.CYAN; + case ProjectileID.HUNLLEF_RANGE_ATTACK: + case ProjectileID.HUNLLEF_CORRUPTED_RANGE_ATTACK: + return Color.GREEN; + case ProjectileID.HUNLLEF_PRAYER_ATTACK: + case ProjectileID.HUNLLEF_CORRUPTED_PRAYER_ATTACK: + return Color.MAGENTA; + default: + return null; + } + } + + private BufferedImage assignedImage(SkillIconManager SkillIconManager, int id) + { + switch (id) + { + case ProjectileID.HUNLLEF_MAGE_ATTACK: + case ProjectileID.HUNLLEF_CORRUPTED_MAGE_ATTACK: + return SkillIconManager.getSkillImage(Skill.MAGIC); + case ProjectileID.HUNLLEF_RANGE_ATTACK: + case ProjectileID.HUNLLEF_CORRUPTED_RANGE_ATTACK: + return SkillIconManager.getSkillImage(Skill.RANGED); + case ProjectileID.HUNLLEF_PRAYER_ATTACK: + case ProjectileID.HUNLLEF_CORRUPTED_PRAYER_ATTACK: + return SkillIconManager.getSkillImage(Skill.PRAYER); + default: + return null; + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/Resources.java b/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/Resources.java new file mode 100644 index 0000000000..517a393ca8 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/Resources.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2019, ganom + * 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.gauntlet; + +import java.awt.image.BufferedImage; +import lombok.AccessLevel; +import lombok.Getter; +import net.runelite.api.GameObject; +import net.runelite.api.ObjectID; +import net.runelite.api.Skill; +import net.runelite.api.Tile; +import net.runelite.client.game.SkillIconManager; + +@Getter(AccessLevel.PACKAGE) +class Resources +{ + private GameObject gameObject; + private Tile tile; + private BufferedImage image; + + Resources(GameObject object, Tile tile, SkillIconManager skillIconManager) + { + this.gameObject = object; + this.tile = tile; + this.image = assignedImage(skillIconManager, object.getId()); + } + + private BufferedImage assignedImage(SkillIconManager SkillIconManager, int id) + { + switch (id) + { + case ObjectID.CRYSTAL_DEPOSIT: + case ObjectID.CORRUPT_DEPOSIT: + return SkillIconManager.getSkillImage(Skill.MINING); + case ObjectID.PHREN_ROOTS: + case ObjectID.PHREN_ROOTS_36066: + return SkillIconManager.getSkillImage(Skill.WOODCUTTING); + case ObjectID.FISHING_SPOT_36068: + case ObjectID.FISHING_SPOT_35971: + return SkillIconManager.getSkillImage(Skill.FISHING); + case ObjectID.GRYM_ROOT: + case ObjectID.GRYM_ROOT_36070: + return SkillIconManager.getSkillImage(Skill.HERBLORE); + case ObjectID.LINUM_TIRINUM: + case ObjectID.LINUM_TIRINUM_36072: + return SkillIconManager.getSkillImage(Skill.FARMING); + default: + return null; + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletPluginConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/Tornado.java similarity index 78% rename from runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletPluginConfig.java rename to runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/Tornado.java index d714001209..a9d2dee2b2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/GauntletPluginConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gauntlet/Tornado.java @@ -1,5 +1,4 @@ /* - * Copyright (c) 2019, xperiaclash * Copyright (c) 2019, ganom * All rights reserved. * @@ -22,21 +21,30 @@ * (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.gauntlet; -import net.runelite.client.config.Config; -import net.runelite.client.config.ConfigItem; +import lombok.AccessLevel; +import lombok.Getter; +import net.runelite.api.NPC; -public interface GauntletPluginConfig extends Config +@Getter(AccessLevel.PACKAGE) +class Tornado { - @ConfigItem( - position = 0, - keyName = "gauntletEnable", - name = "Enable gauntlet", - description = "gauntlet boss prayer" - ) - default boolean enableGauntlet() + private NPC npc; + private int timeLeft; + + Tornado(NPC npc) { - return true; + this.npc = npc; + this.timeLeft = 20; } -} + + void updateTimeLeft() + { + if (timeLeft > 0) + { + timeLeft--; + } + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayUtil.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayUtil.java index 49a18caba6..6c8debf59d 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayUtil.java @@ -45,6 +45,7 @@ import net.runelite.api.Perspective; import net.runelite.api.Point; import net.runelite.api.Prayer; import net.runelite.api.TileObject; +import net.runelite.api.VarClientInt; import net.runelite.api.coords.LocalPoint; import net.runelite.api.coords.WorldArea; import net.runelite.api.coords.WorldPoint; @@ -348,7 +349,7 @@ public class OverlayUtil { Widget widget = client.getWidget(prayer.getWidgetInfo()); - if (widget == null || widget.isHidden()) + if (widget == null || client.getVar(VarClientInt.PLAYER_INTERFACE_CONTAINER_OPENED) != 5) { return null; }