From b61eb75b06127b8c1f973acf7a1b20212bb02c66 Mon Sep 17 00:00:00 2001 From: Lucwousin Date: Sun, 6 Oct 2019 22:24:27 +0200 Subject: [PATCH] hydra: add configuration options and a useless stun timer which needed all the sprite things and kinda looks bad to begin with --- .../plugins/alchemicalhydra/HydraConfig.java | 185 ++++++++++++++++++ .../plugins/alchemicalhydra/HydraOverlay.java | 159 ++++++++++++--- .../plugins/alchemicalhydra/HydraPlugin.java | 123 +++++++++++- .../alchemicalhydra/HydraSceneOverlay.java | 28 ++- 4 files changed, 448 insertions(+), 47 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraConfig.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraConfig.java new file mode 100644 index 0000000000..4cf308f3e7 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraConfig.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2019, Lucas + * 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.alchemicalhydra; + +import java.awt.Color; +import net.runelite.client.config.Alpha; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.ConfigSection; + +@ConfigGroup("betterHydra") +public interface HydraConfig extends Config +{ + @ConfigSection( + keyName = "features", + name = "Features", + description = "Feathers. Jk, features", + position = 0 + ) + default boolean features() + { + return true; + } + + @ConfigItem( + keyName = "counting", + name = "Prayer helper", + description = "Basically everything this plugin is known for. Also has attacks between specs and poison overlay. Shouldn't NOT use this tbh", + position = 1, + section = "features" + ) + default boolean counting() + { + return true; + } + + @ConfigItem( + keyName = "fountain", + name = "Fountain helper", + description = "Indicates if hydra is on a fountain", + position = 2, + section = "features" + ) + default boolean fountain() + { + return true; + } + + @ConfigItem( + keyName = "stun", + name = "Stun timer", + description = "Shows when you can walk in fire phase", + position = 3, + section = "features" + ) + default boolean stun() + { + return false; + } + + @ConfigSection( + keyName = "colours", + name = "Colours", + description = "colours...", + position = 2 + ) + default boolean colours() + { + return false; + } + + @Alpha + @ConfigItem( + keyName = "safeCol", + name = "Safe colour", + description = "Colour overlay will be when there's >2 attacks left", + position = 1, + section = "colours" + ) + default Color safeCol() + { + return new Color(0, 156, 0, 156); + } + + @Alpha + @ConfigItem( + keyName = "medCol", + name = "Medium colour", + description = "Colour overlay will be when a input is coming up", + position = 2, + section = "colours" + ) + default Color medCol() + { + return new Color(200, 156, 0, 156); + } + + @Alpha + @ConfigItem( + keyName = "badCol", + name = "Bad colour", + description = "Colour overlay will be when you have to do something NOW", + position = 3, + section = "colours" + ) + default Color badCol() + { + return new Color(156, 0, 0, 156); + } + + @Alpha + @ConfigItem( + keyName = "poisonBorderCol", + name = "Poison border colour", + description = "Colour the edges of the area highlighted by poison special will be", + position = 4, + section = "colours" + ) + default Color poisonBorderCol() + { + return new Color(255, 0, 0, 100); + } + + @Alpha + @ConfigItem( + keyName = "poisonCol", + name = "Poison colour", + description = "Colour the fill of the area highlighted by poison special will be", + position = 5, + section = "colours" + ) + default Color poisonCol() + { + return new Color(255, 0, 0, 50); + } + + @Alpha + @ConfigItem( + keyName = "fountainColA", + name = "Fountain colour (not on top)", + description = "Fountain colour (not on top)", + position = 6, + section = "colours" + ) + default Color fountainColA() + { + return new Color(255, 0, 0, 100); + } + + @Alpha + @ConfigItem( + keyName = "fountainColB", + name = "Fountain colour (on top)", + description = "Fountain colour (on top)", + position = 7, + section = "colours" + ) + default Color fountainColB() + { + return new Color(0, 255, 0, 100); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraOverlay.java index a84ec94887..38572c9ee2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraOverlay.java @@ -28,24 +28,27 @@ import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.Rectangle; +import java.awt.image.BufferedImage; import javax.inject.Inject; import javax.inject.Singleton; +import lombok.AccessLevel; +import lombok.Setter; import net.runelite.api.Client; +import net.runelite.api.IndexDataBase; import net.runelite.api.Prayer; +import net.runelite.api.Sprite; +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.components.ComponentOrientation; import net.runelite.client.ui.overlay.components.InfoBoxComponent; import net.runelite.client.ui.overlay.components.PanelComponent; +import net.runelite.client.util.ImageUtil; @Singleton class HydraOverlay extends Overlay { - - private static final Color RED_BG_COL = new Color(156, 0, 0, 156); - private static final Color YEL_BG_COL = new Color(200, 156, 0, 156); - private static final Color GRN_BG_COL = new Color(0, 156, 0, 156); static final int IMGSIZE = 36; private final HydraPlugin plugin; @@ -53,50 +56,104 @@ class HydraOverlay extends Overlay private final SpriteManager spriteManager; private final PanelComponent panelComponent = new PanelComponent(); + private BufferedImage stunImg; + + @Setter(AccessLevel.PACKAGE) + private Color safeCol; + + @Setter(AccessLevel.PACKAGE) + private Color medCol; + + @Setter(AccessLevel.PACKAGE) + private Color badCol; + + @Setter(AccessLevel.PACKAGE) + private int stunTicks; + @Inject HydraOverlay(final HydraPlugin plugin, final Client client, final SpriteManager spriteManager) { this.plugin = plugin; this.client = client; this.spriteManager = spriteManager; - setPosition(OverlayPosition.BOTTOM_RIGHT); + this.setPosition(OverlayPosition.BOTTOM_RIGHT); panelComponent.setOrientation(ComponentOrientation.VERTICAL); } @Override public Dimension render(Graphics2D graphics2D) { - Hydra hydra = plugin.getHydra(); + final Hydra hydra = plugin.getHydra(); panelComponent.getChildren().clear(); - if (hydra == null || client == null) + if (hydra == null) { return null; } - //Add spec overlay first, to keep it above pray + // First add stunned thing if needed + if (stunTicks > 0) + { + addStunOverlay(); + } + + + if (plugin.isCounting()) + { + // Add spec box second, to keep it above pray + addSpecOverlay(hydra); + + // Finally add prayer box + addPrayOverlay(hydra); + } + + panelComponent.setPreferredSize(new Dimension(40, 0)); + panelComponent.setBorder(new Rectangle(0, 0, 0, 0)); + + return panelComponent.render(graphics2D); + } + + private void addStunOverlay() + { + final InfoBoxComponent stunComponent = new InfoBoxComponent(); + + stunComponent.setBackgroundColor(badCol); + stunComponent.setImage(getStunImg()); + stunComponent.setText(" " + stunTicks); + stunComponent.setPreferredSize(new Dimension(40, 40)); + + panelComponent.getChildren().add(stunComponent); + } + + private void addSpecOverlay(final Hydra hydra) + { final HydraPhase phase = hydra.getPhase(); final int nextSpec = hydra.getNextSpecialRelative(); - if (nextSpec <= 3) + if (nextSpec > 3) { - InfoBoxComponent specComponent = new InfoBoxComponent(); + return; + } + final InfoBoxComponent specComponent = new InfoBoxComponent(); - if (nextSpec == 0) - { - specComponent.setBackgroundColor(RED_BG_COL); - } - else if (nextSpec == 1) - { - specComponent.setBackgroundColor(YEL_BG_COL); - } - - specComponent.setImage(phase.getSpecImage(spriteManager)); - specComponent.setText(" " + (nextSpec)); //hacky way to not have to figure out how to move text - specComponent.setPreferredSize(new Dimension(40, 40)); - panelComponent.getChildren().add(specComponent); + if (nextSpec == 0) + { + specComponent.setBackgroundColor(badCol); + } + else if (nextSpec == 1) + { + specComponent.setBackgroundColor(medCol); } + specComponent.setImage(phase.getSpecImage(spriteManager)); + specComponent.setText(" " + nextSpec); // hacky way to not have to figure out how to move text + specComponent.setPreferredSize(new Dimension(40, 40)); + + panelComponent.getChildren().add(specComponent); + } + + private void addPrayOverlay(final Hydra hydra) + { final Prayer nextPrayer = hydra.getNextAttack().getPrayer(); final int nextSwitch = hydra.getNextSwitch(); @@ -104,21 +161,65 @@ class HydraOverlay extends Overlay if (nextSwitch == 1) { - prayComponent.setBackgroundColor(client.isPrayerActive(nextPrayer) ? YEL_BG_COL : RED_BG_COL); + prayComponent.setBackgroundColor(client.isPrayerActive(nextPrayer) ? medCol : badCol); } else { - prayComponent.setBackgroundColor(client.isPrayerActive(nextPrayer) ? GRN_BG_COL : RED_BG_COL); + prayComponent.setBackgroundColor(client.isPrayerActive(nextPrayer) ? safeCol : badCol); } prayComponent.setImage(hydra.getNextAttack().getImage(spriteManager)); prayComponent.setText(" " + nextSwitch); prayComponent.setColor(Color.white); prayComponent.setPreferredSize(new Dimension(40, 40)); - panelComponent.getChildren().add(prayComponent); - panelComponent.setPreferredSize(new Dimension(40, 0)); - panelComponent.setBorder(new Rectangle(0, 0, 0, 0)); - return panelComponent.render(graphics2D); + panelComponent.getChildren().add(prayComponent); + } + + boolean onGameTick() + { + return --stunTicks <= 0; + } + + private BufferedImage getStunImg() + { + if (stunImg == null) + { + stunImg = createStunImage(client); + } + + return stunImg; + } + + private static BufferedImage createStunImage(Client client) + { + final Sprite root = getSprite(client, SpriteID.BIG_ASS_GREY_ENTANGLE); + final Sprite mark = getSprite(client, SpriteID.TRADE_EXCLAMATION_MARK_ITEM_REMOVAL_WARNING); + + if (mark == null || root == null) + { + return null; + } + + final Sprite sprite = ImageUtil.mergeSprites(client, ImageUtil.resizeSprite(client, root, IMGSIZE, IMGSIZE), mark); + + return sprite.toBufferedImage(); + } + + private static Sprite getSprite(Client client, int id) + { + final IndexDataBase spriteDb = client.getIndexSprites(); + if (spriteDb == null) + { + return null; + } + + final Sprite[] sprites = client.getSprites(spriteDb, id, 0); + if (sprites == null) + { + return null; + } + + return sprites[0]; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraPlugin.java index b0ce57794b..dd3c30ac5b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraPlugin.java @@ -24,6 +24,7 @@ */ package net.runelite.client.plugins.alchemicalhydra; +import com.google.inject.Provides; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; @@ -44,9 +45,12 @@ import net.runelite.api.Projectile; import net.runelite.api.coords.LocalPoint; import net.runelite.api.events.AnimationChanged; import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.ConfigChanged; import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; import net.runelite.api.events.NpcSpawned; import net.runelite.api.events.ProjectileMoved; +import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.EventBus; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; @@ -71,6 +75,15 @@ public class HydraPlugin extends Plugin @Getter(AccessLevel.PACKAGE) private Hydra hydra; + @Getter(AccessLevel.PACKAGE) + private boolean counting; + + @Getter(AccessLevel.PACKAGE) + private boolean fountain; + + @Getter(AccessLevel.PACKAGE) + private boolean stun; + private boolean inHydraInstance; private int lastAttackTick; @@ -78,26 +91,40 @@ public class HydraPlugin extends Plugin 5279, 5280, 5535, 5536 }; + private static final int STUN_LENGTH = 7; @Inject private Client client; @Inject - private OverlayManager overlayManager; + private EventBus eventBus; + + @Inject + private HydraConfig config; @Inject private HydraOverlay overlay; @Inject - private HydraSceneOverlay poisonOverlay; + private HydraSceneOverlay sceneOverlay; @Inject - private EventBus eventBus; + private OverlayManager overlayManager; + + @Provides + HydraConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(HydraConfig.class); + } @Override protected void startUp() { + initConfig(); + + eventBus.subscribe(ConfigChanged.class, this, this::onConfigChanged); eventBus.subscribe(GameStateChanged.class, this, this::onGameStateChanged); + inHydraInstance = checkArea(); lastAttackTick = -1; poisonProjectiles.clear(); @@ -117,6 +144,20 @@ public class HydraPlugin extends Plugin lastAttackTick = -1; } + private void initConfig() + { + this.counting = config.counting(); + this.fountain = config.fountain(); + this.stun = config.stun(); + this.overlay.setSafeCol(config.safeCol()); + this.overlay.setMedCol(config.medCol()); + this.overlay.setBadCol(config.badCol()); + this.sceneOverlay.setPoisonBorder(config.poisonBorderCol()); + this.sceneOverlay.setPoisonFill(config.poisonCol()); + this.sceneOverlay.setBadFountain(config.fountainColA()); + this.sceneOverlay.setGoodFountain(config.fountainColB()); + } + private void addFightSubscriptions() { eventBus.subscribe(AnimationChanged.class, "fight", this::onAnimationChanged); @@ -124,6 +165,48 @@ public class HydraPlugin extends Plugin eventBus.subscribe(ChatMessage.class, "fight", this::onChatMessage); } + private void onConfigChanged(ConfigChanged event) + { + if (!event.getGroup().equals("betterHydra")) + { + return; + } + + switch (event.getKey()) + { + case "counting": + this.counting = config.counting(); + break; + case "fountain": + this.fountain = config.fountain(); + break; + case "stun": + this.stun = config.stun(); + break; + case "safeCol": + overlay.setSafeCol(config.safeCol()); + return; + case "medCol": + overlay.setMedCol(config.medCol()); + return; + case "badCol": + overlay.setBadCol(config.badCol()); + return; + case "poisonBorderCol": + sceneOverlay.setPoisonBorder(config.poisonBorderCol()); + break; + case "poisonCol": + sceneOverlay.setPoisonFill(config.poisonCol()); + break; + case "fountainColA": + sceneOverlay.setBadFountain(config.fountainColA()); + break; + case "fountainColB": + sceneOverlay.setGoodFountain(config.fountainColB()); + break; + } + } + private void onGameStateChanged(GameStateChanged state) { if (state.getGameState() != GameState.LOGGED_IN) @@ -270,12 +353,27 @@ public class HydraPlugin extends Plugin private void onChatMessage(ChatMessage event) { - if (!event.getMessage().equals("The chemicals neutralise the Alchemical Hydra's defences!")) + if (event.getMessage().equals("The chemicals neutralise the Alchemical Hydra's defences!")) { - return; + hydra.setWeakened(true); } + else if (event.getMessage().equals("The Alchemical Hydra temporarily stuns you.")) + { + if (isStun()) + { + overlay.setStunTicks(STUN_LENGTH); + eventBus.subscribe(GameTick.class, "hydraStun", this::onGameTick); + } + } + } - hydra.setWeakened(true); + private void onGameTick(GameTick tick) + { + if (overlay.onGameTick()) + { + // unregister self when 7 ticks have passed + eventBus.unregister("hydraStun"); + } } private boolean checkArea() @@ -285,13 +383,20 @@ public class HydraPlugin extends Plugin private void addOverlays() { - overlayManager.add(overlay); - overlayManager.add(poisonOverlay); + if (counting || stun) + { + overlayManager.add(overlay); + } + + if (counting || fountain) + { + overlayManager.add(sceneOverlay); + } } private void removeOverlays() { overlayManager.remove(overlay); - overlayManager.remove(poisonOverlay); + overlayManager.remove(sceneOverlay); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraSceneOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraSceneOverlay.java index a38fa4ecfc..257bf9f6b6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraSceneOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraSceneOverlay.java @@ -34,6 +34,8 @@ import java.util.Collection; import java.util.Map; import javax.inject.Inject; import javax.inject.Singleton; +import lombok.AccessLevel; +import lombok.Setter; import net.runelite.api.Client; import static net.runelite.api.Perspective.getCanvasTileAreaPoly; import net.runelite.api.Projectile; @@ -47,9 +49,17 @@ import net.runelite.client.ui.overlay.OverlayPosition; @Singleton class HydraSceneOverlay extends Overlay { - private static final Color GREEN = new Color(0, 255, 0, 100); - private static final Color RED = new Color(255, 0, 0, 100); - private static final Color REDFILL = new Color(255, 0, 0, 50); + @Setter(AccessLevel.PACKAGE) + private Color poisonBorder; + + @Setter(AccessLevel.PACKAGE) + private Color poisonFill; + + @Setter(AccessLevel.PACKAGE) + private Color goodFountain; + + @Setter(AccessLevel.PACKAGE) + private Color badFountain; private final HydraPlugin plugin; private final Client client; @@ -69,12 +79,12 @@ class HydraSceneOverlay extends Overlay Hydra hydra = plugin.getHydra(); final Map poisonProjectiles = plugin.getPoisonProjectiles(); - if (!poisonProjectiles.isEmpty()) + if (plugin.isCounting() && !poisonProjectiles.isEmpty()) { drawPoisonArea(graphics, poisonProjectiles); } - if (hydra.getPhase().getFountain() != null) + if (plugin.isFountain() && hydra.getPhase().getFountain() != null) { drawFountain(graphics, hydra); } @@ -103,9 +113,9 @@ class HydraSceneOverlay extends Overlay } graphics.setPaintMode(); - graphics.setColor(RED); + graphics.setColor(poisonBorder); graphics.draw(poisonTiles); - graphics.setColor(REDFILL); + graphics.setColor(poisonFill); graphics.fill(poisonTiles); } @@ -141,11 +151,11 @@ class HydraSceneOverlay extends Overlay if (hydra.getNpc().getWorldArea().intersectsWith(new WorldArea(wp, 1, 1))) // coords { // WHICH FUCKING RETARD DID X, Y, dX, dY, Z???? IT'S XYZdXdY REEEEEEEEEE - color = GREEN; + color = goodFountain; } else { - color = RED; + color = badFountain; } graphics.setColor(color);