From 24a6357bafba6edf43d09817fcc3e5983dc5c976 Mon Sep 17 00:00:00 2001 From: se7enAte9 <50936458+se7enAte9@users.noreply.github.com> Date: Wed, 26 Jun 2019 20:50:39 -0400 Subject: [PATCH] Barbarianassault merge (#579) * widgets: update name to more accurately represent widget * BarbarianAssault: WIP batools/barbarianassult merge * widgetinfo: add weapon name widget * healer codes: semi-fixed * menu prioritization: walk here here now takes priority over incorrect eggs and any type of bait if enabled in config * add cntl healer * add slider config option for prayer metronome volume, and fix some more menu options * add shift overstock and cleanup imports/config * add death timers and update copyrights and config * Remove unused class * add mode class * add ProjectileSpawn event * add tagging * add reset before wave starts * improve end of wave handling and disabling/enabling plugin mid game * fix horn of glory pt. 1 * fix horn of glory pt. 2 * fix call timer and finish horn of glory * fix :pray: healer codes * cleanup and move certain methods to client thread * cleanup --- .../api/events/ProjectileSpawned.java | 26 +- .../net/runelite/api/widgets/WidgetID.java | 15 +- .../net/runelite/api/widgets/WidgetInfo.java | 12 +- .../barbarianassault/AboveSceneOverlay.java | 219 +++ .../barbarianassault/AboveWidgetsOverlay.java | 181 ++ .../BarbarianAssaultConfig.java | 446 ++++- .../BarbarianAssaultMenu.java | 227 +++ .../BarbarianAssaultOverlay.java | 247 --- .../BarbarianAssaultPlugin.java | 1714 +++++++++++++---- .../plugins/barbarianassault/Calls.java | 83 - .../{HealerTeam.java => DeathTimesMode.java} | 35 +- .../plugins/barbarianassault/Healer.java | 96 + .../plugins/barbarianassault/Menus.java | 130 ++ .../client/plugins/barbarianassault/Role.java | 208 +- .../{Game.java => Scorecard.java} | 62 +- .../{GameTimer.java => Timer.java} | 36 +- .../{Round.java => TimerBox.java} | 84 +- .../client/plugins/barbarianassault/Wave.java | 193 +- .../client/plugins/barbarianassault/clock.png | Bin 197 -> 0 bytes .../client/plugins/batools/BAToolsConfig.java | 214 -- .../plugins/batools/BAToolsOverlay.java | 101 - .../client/plugins/batools/BAToolsPlugin.java | 862 --------- .../client/plugins/batools/Calls.java | 76 - .../client/plugins/batools/Healer.java | 102 - .../client/plugins/batools/HealerCode.java | 58 - .../pestcontrol/PestControlPlugin.java | 2 +- .../plugins/barbarianassault/fighter.png | Bin 0 -> 2033 bytes .../plugins/barbarianassault/healer.png | Bin 0 -> 1575 bytes .../plugins/barbarianassault/ranger.png | Bin 0 -> 1776 bytes .../plugins/barbarianassault/runner.png | Bin 0 -> 1708 bytes .../runelite/mixins/RSProjectileMixin.java | 9 + 31 files changed, 3022 insertions(+), 2416 deletions(-) rename runelite-client/src/main/java/net/runelite/client/plugins/batools/CycleCounter.java => runelite-api/src/main/java/net/runelite/api/events/ProjectileSpawned.java (78%) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/AboveSceneOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/AboveWidgetsOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/BarbarianAssaultMenu.java delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/BarbarianAssaultOverlay.java delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Calls.java rename runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/{HealerTeam.java => DeathTimesMode.java} (71%) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Healer.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Menus.java rename runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/{Game.java => Scorecard.java} (80%) rename runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/{GameTimer.java => Timer.java} (76%) rename runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/{Round.java => TimerBox.java} (59%) delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/clock.png delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsConfig.java delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsOverlay.java delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsPlugin.java delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/batools/Calls.java delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/batools/Healer.java delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/batools/HealerCode.java create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/barbarianassault/fighter.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/barbarianassault/healer.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/barbarianassault/ranger.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/barbarianassault/runner.png diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/batools/CycleCounter.java b/runelite-api/src/main/java/net/runelite/api/events/ProjectileSpawned.java similarity index 78% rename from runelite-client/src/main/java/net/runelite/client/plugins/batools/CycleCounter.java rename to runelite-api/src/main/java/net/runelite/api/events/ProjectileSpawned.java index e6c026fe05..f5ba810a5f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/batools/CycleCounter.java +++ b/runelite-api/src/main/java/net/runelite/api/events/ProjectileSpawned.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2018, https://runelitepl.us + * Copyright (c) 2019, 7ate9 + * Copyright (c) 2019, https://runelitepl.us * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,16 +23,19 @@ * (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.batools; +package net.runelite.api.events; -import java.awt.image.BufferedImage; -import net.runelite.client.plugins.Plugin; -import net.runelite.client.ui.overlay.infobox.Counter; +import net.runelite.api.Projectile; +import lombok.Data; -class CycleCounter extends Counter +/** + * An event called whenever a {@link Projectile} has spawned. + */ +@Data +public class ProjectileSpawned { - CycleCounter(BufferedImage img, Plugin plugin, int tick) - { - super(img, plugin, tick); - } -} \ No newline at end of file + /** + * The spawned projectile. + */ + private Projectile projectile; +} diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java index 672dc24324..931004b869 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java @@ -57,7 +57,7 @@ public class WidgetID public static final int PEST_CONTROL_BOAT_GROUP_ID = 407; public static final int PEST_CONTROL_GROUP_ID = 408; public static final int PEST_CONTROL_EXCHANGE_WINDOW_GROUP_ID = 243; - public static final int PEST_CONTROL_DIALOG_GROUP_ID = 229; + public static final int DIALOG_MINIGAME_GROUP_ID = 229; public static final int CLAN_CHAT_GROUP_ID = 7; public static final int MINIMAP_GROUP_ID = 160; public static final int LOGIN_CLICK_TO_PLAY_GROUP_ID = 378; @@ -84,6 +84,7 @@ public class WidgetID public static final int BA_DEFENDER_GROUP_ID = 487; public static final int BA_HEALER_GROUP_ID = 488; public static final int BA_REWARD_GROUP_ID = 497; + public static final int BA_HORN_OF_GLORY = 484; public static final int LEVEL_UP_GROUP_ID = 233; public static final int DIALOG_SPRITE_GROUP_ID = 193; public static final int QUEST_COMPLETED_GROUP_ID = 277; @@ -197,7 +198,7 @@ public class WidgetID static final int CONFIRM_BUTTON = 6; } - static class PestControlDialog + static class MinigameDialog { static final int TEXT = 1; static final int CONTINUE = 2; @@ -594,7 +595,8 @@ public class WidgetID { static class ATK { - static final int LISTEN = 8; + static final int LISTEN_TOP = 7; + static final int LISTEN_BOTTOM = 8; static final int TO_CALL_WIDGET = 9; static final int TO_CALL = 10; static final int ROLE_SPRITE = 11; @@ -607,6 +609,13 @@ public class WidgetID static final int TEAMMATE3 = 26; static final int TEAMMATE4 = 30; } + static class HORN_GLORY + { + static final int ATTACKER = 5; + static final int DEFENDER = 6; + static final int COLLECTOR = 7; + static final int HEALER = 8; + } static class REWARD_VALUES { static final int RUNNERS_PASSED = 14; diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java index 4a99733ec3..99fa691985 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java @@ -85,8 +85,8 @@ public enum WidgetInfo DIARY_QUEST_WIDGET_TITLE(WidgetID.DIARY_QUEST_GROUP_ID, WidgetID.Diary.DIARY_TITLE), DIARY_QUEST_WIDGET_TEXT(WidgetID.DIARY_QUEST_GROUP_ID, WidgetID.Diary.DIARY_TEXT), - PEST_CONTROL_DIALOG(WidgetID.PEST_CONTROL_DIALOG_GROUP_ID, 0), - PEST_CONTROL_DIALOG_TEXT(WidgetID.PEST_CONTROL_DIALOG_GROUP_ID, WidgetID.PestControlDialog.TEXT), + MINIGAME_DIALOG(WidgetID.DIALOG_MINIGAME_GROUP_ID, 0), + MINIGAME_DIALOG_TEXT(WidgetID.DIALOG_MINIGAME_GROUP_ID, WidgetID.MinigameDialog.TEXT), PEST_CONTROL_EXCHANGE_WINDOW(WidgetID.PEST_CONTROL_EXCHANGE_WINDOW_GROUP_ID, 0), PEST_CONTROL_EXCHANGE_WINDOW_POINTS(WidgetID.PEST_CONTROL_EXCHANGE_WINDOW_GROUP_ID, WidgetID.PestControlExchangeWindow.POINTS), PEST_CONTROL_BOAT_INFO(WidgetID.PEST_CONTROL_BOAT_GROUP_ID, WidgetID.PestControlBoat.INFO), @@ -310,6 +310,7 @@ public enum WidgetInfo QUICK_PRAYER_PRAYERS(WidgetID.QUICK_PRAYERS_GROUP_ID, WidgetID.QuickPrayer.PRAYERS), COMBAT_LEVEL(WidgetID.COMBAT_GROUP_ID, WidgetID.Combat.LEVEL), + COMBAT_WEAPON(WidgetID.COMBAT_GROUP_ID, WidgetID.Combat.WEAPON_NAME), COMBAT_STYLE_ONE(WidgetID.COMBAT_GROUP_ID, WidgetID.Combat.STYLE_ONE), COMBAT_STYLE_TWO(WidgetID.COMBAT_GROUP_ID, WidgetID.Combat.STYLE_TWO), COMBAT_STYLE_THREE(WidgetID.COMBAT_GROUP_ID, WidgetID.Combat.STYLE_THREE), @@ -364,6 +365,7 @@ public enum WidgetInfo BA_HEAL_WAVE_TEXT(WidgetID.BA_HEALER_GROUP_ID, WidgetID.BarbarianAssault.CURRENT_WAVE), BA_HEAL_CALL_TEXT(WidgetID.BA_HEALER_GROUP_ID, WidgetID.BarbarianAssault.TO_CALL), BA_HEAL_LISTEN_TEXT(WidgetID.BA_HEALER_GROUP_ID, WidgetID.BarbarianAssault.LISTEN), + BA_HEAL_HORN_LISTEN_TEXT(WidgetID.BA_HORN_OF_GLORY, WidgetID.BarbarianAssault.HORN_GLORY.HEALER), BA_HEAL_ROLE_TEXT(WidgetID.BA_HEALER_GROUP_ID, WidgetID.BarbarianAssault.ROLE), BA_HEAL_ROLE_SPRITE(WidgetID.BA_HEALER_GROUP_ID, WidgetID.BarbarianAssault.ROLE_SPRITE), @@ -375,18 +377,22 @@ public enum WidgetInfo BA_COLL_WAVE_TEXT(WidgetID.BA_COLLECTOR_GROUP_ID, WidgetID.BarbarianAssault.CURRENT_WAVE), BA_COLL_CALL_TEXT(WidgetID.BA_COLLECTOR_GROUP_ID, WidgetID.BarbarianAssault.TO_CALL), BA_COLL_LISTEN_TEXT(WidgetID.BA_COLLECTOR_GROUP_ID, WidgetID.BarbarianAssault.LISTEN), + BA_COLL_HORN_LISTEN_TEXT(WidgetID.BA_HORN_OF_GLORY, WidgetID.BarbarianAssault.HORN_GLORY.COLLECTOR), BA_COLL_ROLE_TEXT(WidgetID.BA_COLLECTOR_GROUP_ID, WidgetID.BarbarianAssault.ROLE), BA_COLL_ROLE_SPRITE(WidgetID.BA_COLLECTOR_GROUP_ID, WidgetID.BarbarianAssault.ROLE_SPRITE), BA_ATK_WAVE_TEXT(WidgetID.BA_ATTACKER_GROUP_ID, WidgetID.BarbarianAssault.CURRENT_WAVE), BA_ATK_CALL_TEXT(WidgetID.BA_ATTACKER_GROUP_ID, WidgetID.BarbarianAssault.ATK.TO_CALL), - BA_ATK_LISTEN_TEXT(WidgetID.BA_ATTACKER_GROUP_ID, WidgetID.BarbarianAssault.ATK.LISTEN), + BA_ATK_LISTEN_TOP_TEXT(WidgetID.BA_ATTACKER_GROUP_ID, WidgetID.BarbarianAssault.ATK.LISTEN_TOP), + BA_ATK_LISTEN_BOTTOM_TEXT(WidgetID.BA_ATTACKER_GROUP_ID, WidgetID.BarbarianAssault.ATK.LISTEN_BOTTOM), + BA_ATK_HORN_LISTEN_TEXT(WidgetID.BA_HORN_OF_GLORY, WidgetID.BarbarianAssault.HORN_GLORY.ATTACKER), BA_ATK_ROLE_TEXT(WidgetID.BA_ATTACKER_GROUP_ID, WidgetID.BarbarianAssault.ATK.ROLE), BA_ATK_ROLE_SPRITE(WidgetID.BA_ATTACKER_GROUP_ID, WidgetID.BarbarianAssault.ATK.ROLE_SPRITE), BA_DEF_WAVE_TEXT(WidgetID.BA_DEFENDER_GROUP_ID, WidgetID.BarbarianAssault.CURRENT_WAVE), BA_DEF_CALL_TEXT(WidgetID.BA_DEFENDER_GROUP_ID, WidgetID.BarbarianAssault.TO_CALL), BA_DEF_LISTEN_TEXT(WidgetID.BA_DEFENDER_GROUP_ID, WidgetID.BarbarianAssault.LISTEN), + BA_DEF_HORN_LISTEN_TEXT(WidgetID.BA_HORN_OF_GLORY, WidgetID.BarbarianAssault.HORN_GLORY.DEFENDER), BA_DEF_ROLE_TEXT(WidgetID.BA_DEFENDER_GROUP_ID, WidgetID.BarbarianAssault.ROLE), BA_DEF_ROLE_SPRITE(WidgetID.BA_DEFENDER_GROUP_ID, WidgetID.BarbarianAssault.ROLE_SPRITE), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/AboveSceneOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/AboveSceneOverlay.java new file mode 100644 index 0000000000..ecfb8251ce --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/AboveSceneOverlay.java @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2018, Jacob M + * Copyright (c) 2019, 7ate9 + * Copyright (c) 2019, https://runelitepl.us + * 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.barbarianassault; + +import com.google.common.collect.ImmutableMap; +import net.runelite.api.Client; +import net.runelite.api.Perspective; +import net.runelite.api.Point; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +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.OverlayUtil; + +import javax.inject.Inject; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Stroke; +import java.awt.BasicStroke; +import java.util.Map; + + +class AboveSceneOverlay extends Overlay +{ + private static final int HEALTH_BAR_HEIGHT = 20; + private static final int HEALTH_BAR_WIDTH = 115; + private static final int CENTER_OFFSET = Perspective.LOCAL_HALF_TILE_SIZE / 8; + private static final int EGG_DIAMETER = Perspective.LOCAL_HALF_TILE_SIZE / 4; + private static final Color HEALTH_BAR_COLOR = new Color(225, 35, 0, 125); + private static final ImmutableMap TEAMMATES = ImmutableMap.of( + WidgetInfo.BA_HEAL_TEAMMATE1, new Point(28, 2), + WidgetInfo.BA_HEAL_TEAMMATE2, new Point(26, 2), + WidgetInfo.BA_HEAL_TEAMMATE3, new Point(26, 2), + WidgetInfo.BA_HEAL_TEAMMATE4, new Point(25, 2)); + + private final Client client; + private final BarbarianAssaultPlugin game; + private final BarbarianAssaultConfig config; + + + @Inject + private AboveSceneOverlay(Client client, BarbarianAssaultPlugin game, BarbarianAssaultConfig config) + { + super(game); + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + this.client = client; + this.game = game; + this.config = config; + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (!game.isInGame() || game.getRole() == null || game.isUsingGloryHorn()) + { + return null; + } + + switch (game.getRole()) + { + + case HEALER: + if (config.showTeammateHealthbars()) + { + renderHealthBars(graphics); + } + if (config.healerCodes()) + { + renderHealerCodes(graphics); + } + break; + + + case COLLECTOR: + if (config.highlightCollectorEggs()) + { + renderEggs(graphics); + } + break; + } + return null; + } + + //TODO add poison color change or low health color change + private void renderHealthBars(Graphics2D graphics) + { + for (Map.Entry teammate : TEAMMATES.entrySet()) + { + Widget widget = client.getWidget(teammate.getKey()); + if (widget == null) + { + continue; + } + + // This will give us two elements, the first will be current health, and the second will be max health + String[] teammateHealth = widget.getText().split(" / "); + + graphics.setColor(HEALTH_BAR_COLOR); + graphics.fillRect((widget.getCanvasLocation().getX() - teammate.getValue().getX()), + (widget.getCanvasLocation().getY() - teammate.getValue().getY()), + getBarWidth(Integer.parseInt(teammateHealth[1]), Integer.parseInt(teammateHealth[0])), + HEALTH_BAR_HEIGHT); + } + } + + private int getBarWidth(int base, int current) + { + final double ratio = (double) current / base; + + if (ratio >= 1) + { + return HEALTH_BAR_WIDTH; + } + + return (int) Math.round(ratio * HEALTH_BAR_WIDTH); + } + + private void renderHealerCodes(Graphics2D graphics) + { + for (Healer healer : game.getHealers().values()) + { + Color color = Color.GREEN; + int timeLeft = 0; + + if (game.getWave() != null) + { + timeLeft = healer.getLastFoodTime() - (int) game.getWave().getWaveTimer().getElapsedTime(); + } + + timeLeft = timeLeft < 1 ? 0 : timeLeft; + + if (timeLeft > 0) + { + color = Color.RED; + } + + String text = String.format("%d %d", healer.getFoodRemaining(), timeLeft); + + OverlayUtil.renderActorOverlay(graphics, healer.getNpc(), text, color); + } + } + + private void renderEggs(Graphics2D graphics) + { + final Color color = graphics.getColor(); + final Stroke originalStroke = graphics.getStroke(); + String listen = game.getLastListenText(); + if (listen != null && !listen.equals("- - -")) + { + graphics.setStroke(new BasicStroke(2)); + //TODO Render quantity text as well + //TODO add config options for overlay colors + switch (listen) + { + case "Red eggs": + graphics.setColor(new Color(Color.RED.getRed(), Color.RED.getGreen(), Color.RED.getBlue(), 150)); + game.getRedEggs().forEach((point, quantity) -> drawCircle(graphics, LocalPoint.fromWorld(client, point))); + break; + case "Green eggs": + graphics.setColor(new Color(Color.GREEN.getRed(), Color.GREEN.getGreen(), Color.GREEN.getBlue(), 150)); + game.getGreenEggs().forEach((point, quantity) -> drawCircle(graphics, LocalPoint.fromWorld(client, point))); + break; + case "Blue eggs": + graphics.setColor(new Color(Color.BLUE.getRed(), Color.BLUE.getGreen(), Color.BLUE.getBlue(), 150)); + game.getBlueEggs().forEach((point, quantity) -> drawCircle(graphics, LocalPoint.fromWorld(client, point))); + break; + } + } + graphics.setColor(new Color(Color.YELLOW.getRed(), Color.YELLOW.getGreen(), Color.YELLOW.getBlue(), 150)); + game.getYellowEggs().forEach((point, quantity) -> drawCircle(graphics, LocalPoint.fromWorld(client, point))); + graphics.setColor(color); + graphics.setStroke(originalStroke); + } + + private void drawCircle(Graphics2D graphics, LocalPoint point) + { + if (point == null) + { + return; + } + + Point canvasPoint = Perspective.localToCanvas(client, point, 0); + if (canvasPoint == null) + { + return; + } + + //TODO rendering a model would be better / more accurate + graphics.fillOval(canvasPoint.getX() - CENTER_OFFSET, canvasPoint.getY() - CENTER_OFFSET, EGG_DIAMETER, EGG_DIAMETER); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/AboveWidgetsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/AboveWidgetsOverlay.java new file mode 100644 index 0000000000..d114bc8d8d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/AboveWidgetsOverlay.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2019, 7ate9 + * Copyright (c) 2019, https://runelitepl.us + * 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.barbarianassault; + +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 net.runelite.api.Client; +import net.runelite.api.Point; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.api.widgets.WidgetItem; +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.OverlayUtil; +import net.runelite.client.util.ImageUtil; + + +class AboveWidgetsOverlay extends Overlay +{ + private static final int OFFSET_X_TEXT_QUANTITY = 0; + private static final int OFFSET_Y_TEXT_QUANTITY = 10; + + private final Client client; + private final BarbarianAssaultPlugin game; + private final BarbarianAssaultConfig config; + + + @Inject + private AboveWidgetsOverlay(Client client, BarbarianAssaultPlugin game, BarbarianAssaultConfig config) + { + super(game); + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_WIDGETS); + this.client = client; + this.game = game; + this.config = config; + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (!game.isInGame() || game.getRole() == null || game.isUsingGloryHorn()) + { + return null; + } + + Role role = game.getRole(); + + if (config.showTimer()) + { + renderTimer(graphics, role); + } + + switch (role) + { + case ATTACKER: + if (config.highlightArrows()) + { + renderInventoryHighlights(graphics, game.getRole().getListenItem(game.getLastListenText()), config.highlightArrowColor()); + } + break; + + case DEFENDER: + if (config.highlightBait()) + { + renderInventoryHighlights(graphics, game.getRole().getListenItem(game.getLastListenText()), config.highlightBaitColor()); + } + break; + + case HEALER: + if (config.highlightPoison()) + { + renderInventoryHighlights(graphics, game.getRole().getListenItem(game.getLastListenText()), config.highlightPoisonColor()); + } + } + return null; + } + + private void renderTimer(Graphics2D graphics, Role role) + { + Widget roleText = client.getWidget(role.getRoleText()); + Widget roleSprite = client.getWidget(role.getRoleSprite()); + + if (roleText == null || roleSprite == null) + { + return; + } + + if (role == Role.COLLECTOR && config.showEggCountOverlay() && game.getWave() != null) + { + roleText.setText("(" + game.getWave().getCollectedEggCount() + ") " + formatClock()); + } + else if (role == Role.HEALER && config.showHpCountOverlay() && game.getWave() != null) + { + roleText.setText("(" + game.getWave().getHpHealed() + ") " + formatClock()); + } + else + { + roleText.setText(formatClock()); + } + + Rectangle spriteBounds = roleSprite.getBounds(); + graphics.drawImage(game.getClockImage(), spriteBounds.x, spriteBounds.y, null); + } + + private void renderInventoryHighlights(Graphics2D graphics, int itemID, Color color) + { + Widget inventory = client.getWidget(WidgetInfo.INVENTORY); + + if (inventory == null || inventory.isHidden() || itemID == -1) + { + return; + } + + Color highlight = new Color(color.getRed(), color.getGreen(), color.getBlue(), 150); + BufferedImage image = ImageUtil.fillImage(client.createItemSprite(itemID, 300, 2, 0, 0, true, 710).toBufferedImage(), highlight); + for (WidgetItem item : inventory.getWidgetItems()) + { + if (item.getId() == itemID) + { + OverlayUtil.renderImageLocation(graphics, item.getCanvasLocation(), image); + //The item's text quantity is rendered after the sprite's image is rendered so that the text appears on top + if (item.getQuantity() > 1) + { + OverlayUtil.renderTextLocation(graphics, + new Point(item.getCanvasLocation().getX() + OFFSET_X_TEXT_QUANTITY, item.getCanvasLocation().getY() + OFFSET_Y_TEXT_QUANTITY), + String.valueOf(item.getQuantity()), Color.YELLOW); + } + } + } + } + + private String formatClock() + { + if (game.getCallTimer() == null) + { + return "- - -"; + } + else + { + long timeLeft = game.getTimeToChange(); + if (timeLeft < 0) + { + return "00:00"; + } + else + { + return String.format("00:%02d", timeLeft); + } + } + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/BarbarianAssaultConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/BarbarianAssaultConfig.java index f2d1269bb7..31fd31cbf0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/BarbarianAssaultConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/BarbarianAssaultConfig.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2018, https://runelitepl.us + * Copyright (c) 2019, 7ate9 + * Copyright (c) 2019, https://runelitepl.us * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,26 +29,112 @@ 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; @ConfigGroup("barbarianAssault") public interface BarbarianAssaultConfig extends Config { + @ConfigItem( + keyName = "swapLadder", + name = "Swap quick-start", + description = "Enables swapping of 'Climb-down' and 'Quick-start' in the wave lobby", + position = 0 + ) + default boolean swapLadder() + { + return true; + } @ConfigItem( keyName = "showTimer", name = "Show call change timer", - description = "Show time to next call change", - position = 0 + description = "Shows time to next call change", + position = 1 ) default boolean showTimer() { return true; } + @ConfigItem( + keyName = "removeIncorrectCalls", + name = "Remove incorrect calls", + description = "Removes incorrect 'Tell' menu options from horn", + position = 2 + ) + default boolean removeIncorrectCalls() + { + return true; + } + + @ConfigItem( + keyName = "removeUnusedMenus", + name = "Remove unused menus", + description = "Removes unnecessary menu options" + + "
Example: Attack options are removed when not attacker", + position = 3 + ) + default boolean removeUnusedMenus() + { + return true; + } + + @ConfigItem( + keyName = "prayerMetronome", + name = "Enable prayer metronome", + description = "Turns on a metronome sync'd to the game's tick rate when any prayer is active", + position = 4 + ) + default boolean prayerMetronome() + { + return false; + } + + @Range( + min = 1, + max = 50 + ) + @ConfigItem( + keyName = "prayerMetronomeVolume", + name = "Metronome volume", + description = "Adjusts the metronome's volume", + position = 5, + hidden = true, + unhide = "prayerMetronome" + ) + default int prayerMetronomeVolume() + { + return 10; + } + + @ConfigItem( + keyName = "showDeathTimes", + name = "Show death times", + description = "Shows the time all penance monsters of a certain type are killed in the chat box, an info box, or both", + position = 6 + ) + default boolean showDeathTimes() + { + return true; + } + + @ConfigItem( + keyName = "showDeathTimesMode", + name = "Mode", + description = "", + position = 7, + hidden = true, + unhide = "showDeathTimes" + ) + default DeathTimesMode showDeathTimesMode() + { + return DeathTimesMode.BOTH; + } + @ConfigItem( keyName = "waveTimes", name = "Show wave and game duration", - description = "Displays wave and game duration", - position = 1 + description = "Displays wave duration after each wave and total game time after wave 10", + position = 8 ) default boolean waveTimes() { @@ -55,112 +142,329 @@ public interface BarbarianAssaultConfig extends Config } @ConfigItem( - keyName = "showEggCountMessage", - name = "Show count of eggs collected as collector.", - description = "Display egg count as collector after each wave", - position = 2 + keyName = "showTotalRewards", + name = "Summarize total reward points", + description = "Gives summary of advanced points breakdown in chat log", + position = 9 ) - default boolean showEggCount() + default boolean showTotalRewards() + { + return true; + } + + + /*///************///*/ + /*/// Attacker ///*/ + /*///************///*/ + + @ConfigItem( + keyName = "highlightArrows", + name = "Highlight called arrows", + description = "Highlights arrows called by your teammate", + position = 0, + group = "Attacker" + ) + default boolean highlightArrows() + { + return true; + } + + @ConfigItem( + keyName = "highlightArrowColor", + name = "Arrow color", + description = "Configures the color to highlight the called arrows", + position = 1, + group = "Attacker", + hidden = true, + unhide = "highlightArrows" + ) + default Color highlightArrowColor() + { + return Color.GREEN; + } + + @ConfigItem( + keyName = "attackStyles", + name = "Remove incorrect attack styles", + description = "Hide attack styles depending on weapon.", + position = 2, + group = "Attacker" + ) + default boolean attackStyles() { return false; } @ConfigItem( - keyName = "showEggCountOverlay", - name = "Overlay of eggs counted", - description = "Display current egg count as collector", - position = 3 + keyName = "tagging", + name = "Enable tagging", + description = "Highlights the menu entry of an attacker/ranger that has not been tagged.", + position = 3, + group = "Attacker" ) - default boolean showEggCountOverlay() + default boolean tagging() { return false; } + + /*///************///*/ + /*/// Defender ///*/ + /*///************///*/ + @ConfigItem( - keyName = "showHpCountMessage", - name = "Show count of Hp healed as healer.", - description = "Display healed count as healer after each wave", - position = 4 + keyName = "highlightBait", + name = "Highlight called bait", + description = "Highlights bait called by your teammate", + position = 0, + group = "Defender" ) - default boolean showHpCount() + default boolean highlightBait() { - return false; + return true; + } + + @ConfigItem( + keyName = "highlightBaitColor", + name = "Bait color", + description = "Configures the color to highlight the called bait", + position = 1, + group = "Defender", + hidden = true, + unhide = "highlightBait" + ) + default Color highlightBaitColor() + { + return Color.GREEN; + } + + @ConfigItem( + keyName = "showDefTimer", + name = "Show defender tick timer", + description = "Shows the current cycle tick of runners", + position = 2, + group = "Defender" + ) + default boolean showDefTimer() + { + return true; + } + + @ConfigItem( + keyName = "deprioritizeBait", + name = "Deprioritize bait", + description = "Moves 'Take' menu option for all bait below 'Walk Here'", + position = 3, + group = "Defender" + ) + default boolean deprioritizeBait() + { + return true; + } + + @ConfigItem( + keyName = "removePenanceCave", + name = "Remove penance cave", + description = "Removes 'Block' menu option from penance cave", + position = 4, + group = "Defender" + ) + default boolean removePenanceCave() + { + return true; + } + + + /*///**********///*/ + /*/// Healer ///*/ + /*///**********///*/ + + @ConfigItem( + keyName = "highlightPoison", + name = "Highlight called poison", + description = "Highlights poison called by your teammate", + position = 0, + group = "Healer" + ) + default boolean highlightPoison() + { + return true; + } + + @ConfigItem( + keyName = "highlightPoisonColor", + name = "Poison color", + description = "Configures the color to highlight the called poison", + position = 1, + group = "Healer", + hidden = true, + unhide = "highlightPoison" + ) + default Color highlightPoisonColor() + { + return Color.GREEN; + } + + @ConfigItem( + keyName = "highlightNotification", + name = "Highlight incorrect notification", + description = "Highlights incorrect poison chat notification", + position = 2, + group = "Healer" + ) + default boolean highlightNotification() + { + return true; + } + + @ConfigItem( + keyName = "highlightNotificationColor", + name = "Notification color", + description = "Configures the color to highlight the notification text", + position = 3, + group = "Healer", + hidden = true, + unhide = "highlightNotification" + ) + default Color highlightNotificationColor() + { + return Color.RED; } @ConfigItem( keyName = "showHpCountOverlay", - name = "Overlay of Hp counted", - description = "Display current healed count as healer", - position = 5 + name = "Show number of hitpoints healed", + description = "Displays current number of hitpoints healed", + position = 4, + group = "Healer" ) default boolean showHpCountOverlay() + { + return true; + } + + @ConfigItem( + keyName = "showTeammateHealthbars", + name = "Show health bars", + description = "Displays a health bar where a teammate's remaining health is located", + position = 5, + group = "Healer" + ) + default boolean showTeammateHealthbars() + { + return true; + } + + @ConfigItem( + keyName = "healerCodes", + name = "Show healer codes", + description = "Overlay to show healer codes", + position = 6, + group = "Healer" + ) + default boolean healerCodes() { return false; } + @ConfigItem( + keyName = "healerMenuOption", + name = "Show healer menu options", + description = "Shows tick count in healer menu options", + position = 7, + group = "Healer" + ) + default boolean healerMenuOption() + { + return false; + } + + @ConfigItem( + keyName = "shiftOverstock", + name = "Enable shift overstock", + description = "Enables overstocking by pressing shift", + position = 8, + group = "Healer" + ) + default boolean shiftOverstock() + { + return true; + } + + @ConfigItem( + keyName = "controlHealer", + name = "Control Healer", + description = "Hold ctrl to put last healer clicked on top", + position = 9, + group = "Healer" + ) + default boolean controlHealer() + { + return true; + } + + + /*///*************///*/ + /*/// Collector ///*/ + /*///*************///*/ + + @ConfigItem( + keyName = "swapCollectorBag", + name = "Swap empty", + description = "Enables swapping of 'Look-in' and 'Empty' on the collector's bag", + position = 0, + group = "Collector" + ) + default boolean swapCollectorBag() + { + return true; + } + + @ConfigItem( + keyName = "swapDestroyEggs", + name = "Swap destroy", + description = "Enables swapping of 'Use' and 'Destroy' on collector eggs; this does not affect yellow/omega eggs", + position = 1, + group = "Collector" + ) + default boolean swapDestroyEggs() + { + return true; + } + @ConfigItem( keyName = "highlightCollectorEggs", name = "Highlight collector eggs", description = "Highlight called egg colors", - position = 6 + position = 2, + group = "Collector" ) default boolean highlightCollectorEggs() { - return false; + return true; } @ConfigItem( - keyName = "showTotalRewards", - name = "Summarize total reward points", - description = "Displays total eggs/healed hp and missed attacks/lost runners", - position = 7 + keyName = "deprioritizeIncorrectEggs", + name = "Deprioritize incorrect eggs", + description = "Moves 'Take' menu option for incorrect eggs below 'Walk Here'", + position = 3, + group = "Collector" ) - default boolean showTotalRewards() + default boolean deprioritizeIncorrectEggs() { - return false; + return true; } @ConfigItem( - keyName = "showSummaryOfPoints", - name = "Display summary of advanced points", - description = "Gives summary of advanced points breakdown in chat log", - position = 8 + keyName = "showEggCountOverlay", + name = "Show number of eggs collected", + description = "Displays current number of eggs collected", + position = 4, + group = "Collector" ) - default boolean showSummaryOfPoints() + default boolean showEggCountOverlay() { - return false; - } - - @ConfigItem( - keyName = "wrongPoisonFoodTextColor", - name = "Change healer wrong poison pack color", - description = "Change healer wrong poison pack color", - position = 9 - ) - default Color wrongPoisonFoodTextColor() - { - return Color.BLACK; - } - - @ConfigItem( - keyName = "highlightItems", - name = "Highlight called poison/bait", - description = "Highlights the poison or bait that was called by your teammate", - position = 10 - ) - default boolean highlightItems() - { - return false; - } - - @ConfigItem( - keyName = "highlightColor", - name = "Highlight color", - description = "Configures the color to highlight the called poison/bait", - position = 11 - ) - default Color highlightColor() - { - return Color.GREEN; + return true; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/BarbarianAssaultMenu.java b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/BarbarianAssaultMenu.java new file mode 100644 index 0000000000..8f852846e4 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/BarbarianAssaultMenu.java @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2019, 7ate9 + * Copyright (c) 2019, https://runelitepl.us + * 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.barbarianassault; + +import com.google.common.collect.Sets; +import lombok.Getter; +import lombok.Setter; +import net.runelite.client.menus.ComparableEntry; +import net.runelite.client.menus.MenuManager; + +import javax.inject.Inject; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; + +class BarbarianAssaultMenu +{ + private final MenuManager menuManager; + private final BarbarianAssaultPlugin game; + private final BarbarianAssaultConfig config; + private final ArrayList tracker = new ArrayList<>(); + @Getter @Setter + private boolean hornUpdated = false; + @Getter @Setter + private boolean rebuildForced = false; + + @Inject + BarbarianAssaultMenu(MenuManager menuManager, BarbarianAssaultPlugin game, BarbarianAssaultConfig config) + { + this.menuManager = menuManager; + this.game = game; + this.config = config; + } + + private boolean isHornOptionHidden(String option) + { + if (game.isInGame() && game.getRole() != null && game.getRole().getTell(game.getLastCallText()).toLowerCase().equals(option)) + { + // This will force the menu to be rebuilt after the correct tell is found + // medic will be added to the menu if it wasn't there before + if (!hornUpdated) + { + rebuildForced = true; + } + hornUpdated = true; + return false; + } + return true; + } + + void clearHiddenMenus() + { + // Clears menus from MenuManager and tracker + for (Iterator iterator = tracker.iterator(); iterator.hasNext();) + { + menuManager.removeHiddenEntry(iterator.next()); + iterator.remove(); + } + } + + //TODO add omega egg use on? + void validateHiddenMenus(Role role) + { + clearHiddenMenus(); + + HashSet hiddenMenus = Sets.newHashSet(Menus.getMenus()); + HashSet conditionalMenus = Sets.newHashSet(Menus.getMenus()); + + // Any option left in this set will not be hidden + // Checking each option for the correct role prevents MenuManager from + // iterating over off role menu entry options that are not possible + conditionalMenus.removeIf(entry -> + { + switch (entry) + { + // Attacker role options + case TELL_BLUE_ATTACKER_HORN: + case TELL_GREEN_ATTACKER_HORN: + case TELL_RED_ATTACKER_HORN: + return ((role == Role.ATTACKER && isHornOptionHidden(entry.getOption())) || role == null) && config.removeIncorrectCalls(); + + case ATTACK_PENANCE_FIGHTER: + case ATTACK_PENANCE_RANGER: + case GET_SPIKES_PETRIFIED_MUSHROOM: + case TAKE_ATTACKER_ITEM_MACHINE: + return (role != Role.ATTACKER && role != null) && config.removeUnusedMenus(); + + + // Defender role Options + case TELL_MEAT_DEFENDER_HORN: + case TELL_TOFU_DEFENDER_HORN: + case TELL_WORMS_DEFENDER_HORN: + return ((role == Role.DEFENDER && isHornOptionHidden(entry.getOption())) || role == null) && config.removeIncorrectCalls(); + + case BLOCK_PENANCE_CAVE: + return ((role != Role.DEFENDER && role != null) && config.removeUnusedMenus()) + || (role == Role.DEFENDER && config.removePenanceCave()); + + case DUNK_LAVA_CRATER: + case FIX: + case STOCK_UP_DEFENDER_ITEM_MACHINE: + case TAKE_DEFENDER_ITEM_MACHINE: + case TAKE_HAMMER: + case TAKE_LOGS: + return (role != Role.DEFENDER && role != null) && config.removeUnusedMenus(); + + + // Collector role options + case TELL_ACCURATE_COLLECTOR_HORN: + case TELL_AGGRESSIVE_COLLECTOR_HORN: + case TELL_CONTROLLED_COLLECTOR_HORN: + case TELL_DEFENSIVE_COLLECTOR_HORN: + return ((role == Role.COLLECTOR && isHornOptionHidden(entry.getOption())) || role == null) && config.removeIncorrectCalls(); + + case CONVERT_COLLECTOR_CONVERTER: + case LOAD_EGG_HOPPER: + case TAKE_BLUE_EGG: + case TAKE_GREEN_EGG: + case TAKE_RED_EGG: + case TAKE_YELLOW_EGG: + return (role != Role.COLLECTOR && role != null) && config.removeUnusedMenus(); + + + // Healer role options + case TELL_CRACKERS_HEALER_HORN: + case TELL_TOFU_HEALER_HORN: + case TELL_WORMS_HEALER_HORN: + return ((role == Role.HEALER && isHornOptionHidden(entry.getOption())) || role == null) && config.removeIncorrectCalls(); + + case DUNK_POISON_CRATER: + case STOCK_UP_HEALER_ITEM_MACHINE: + case TAKE_HEALER_ITEM_MACHINE: + case TAKE_FROM_HEALER_SPRING: + case DRINK_FROM_HEALER_SPRING: + return (role != Role.HEALER && role != null) && config.removeUnusedMenus(); + + case USE_VIAL_GROUND: + case USE_VIAL_ITEM: + case USE_VIAL_NPC: + case USE_VIAL_WIDGET: + return role == Role.HEALER && config.removeUnusedMenus(); + + + // Any role options + case DROP_HORN: + case EXAMINE_HORN: + case USE_HORN: + return config.removeIncorrectCalls(); + + case MEDIC_HORN: + return config.removeIncorrectCalls() && !hornUpdated; + + default: + return config.removeUnusedMenus(); + } + }); + + hiddenMenus.removeAll(conditionalMenus); + + for (Menus entry : hiddenMenus) + { + menuManager.addHiddenEntry(entry.getEntry()); + tracker.add(entry.getEntry()); + } + } + + void enableSwaps() + { + if (config.swapLadder()) + { + menuManager.addSwap("climb-down", "ladder", "quick-start", "ladder"); + } + if (config.swapCollectorBag()) + { + menuManager.addSwap("look-in", "collection bag", "empty", "collection bag"); + } + if (config.swapDestroyEggs()) + { + menuManager.addSwap("use", "blue egg", "destroy", "blue egg"); + menuManager.addSwap("use", "green egg", "destroy", "green egg"); + menuManager.addSwap("use", "red egg", "destroy", "red egg"); + } + } + + void disableSwaps(boolean force) + { + if (!config.swapLadder() || force) + { + menuManager.removeSwap("climb-down", "ladder", "quick-start", "ladder"); + } + + if (!config.swapCollectorBag() || force) + { + menuManager.removeSwap("look-in", "collection bag", "empty", "collection bag"); + } + + if (!config.swapDestroyEggs() || force) + { + menuManager.removeSwap("use", "blue egg", "destroy", "blue egg"); + menuManager.removeSwap("use", "green egg", "destroy", "green egg"); + menuManager.removeSwap("use", "red egg", "destroy", "red egg"); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/BarbarianAssaultOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/BarbarianAssaultOverlay.java deleted file mode 100644 index 98ab9da951..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/BarbarianAssaultOverlay.java +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (c) 2018, https://runelitepl.us - * 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.barbarianassault; - -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics2D; -import java.awt.Polygon; -import java.awt.Rectangle; -import java.awt.Stroke; -import java.awt.image.BufferedImage; -import java.util.Map; -import javax.inject.Inject; -import lombok.Getter; -import lombok.Setter; -import net.runelite.api.Client; -import net.runelite.api.GameState; -import static net.runelite.api.MenuAction.RUNELITE_OVERLAY_CONFIG; -import net.runelite.api.Perspective; -import net.runelite.api.Player; -import net.runelite.api.Point; -import net.runelite.api.coords.LocalPoint; -import net.runelite.api.coords.WorldPoint; -import net.runelite.api.widgets.Widget; -import net.runelite.api.widgets.WidgetInfo; -import net.runelite.api.widgets.WidgetItem; -import net.runelite.client.game.ItemManager; -import net.runelite.client.ui.overlay.Overlay; -import net.runelite.client.ui.overlay.OverlayLayer; -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.OverlayUtil; -import net.runelite.client.util.ImageUtil; - - -class BarbarianAssaultOverlay extends Overlay -{ - private static final int MAX_EGG_DISTANCE = 2500; - private static final int HEALTH_BAR_HEIGHT = 20; - private final Color HEALTH_BAR_COLOR = new Color(225, 35, 0, 125); - private static final Color BACKGROUND = new Color(0, 0, 0, 150); - private static final int OFFSET_Z = 20; - - private final Client client; - private final ItemManager itemManager; - private final BarbarianAssaultPlugin plugin; - private final BarbarianAssaultConfig config; - - @Getter - @Setter - private Round currentRound; - - - @Inject - private BarbarianAssaultOverlay(Client client, ItemManager itemManager, BarbarianAssaultPlugin plugin, BarbarianAssaultConfig config) - { - super(plugin); - setPosition(OverlayPosition.DYNAMIC); - setLayer(OverlayLayer.ABOVE_WIDGETS); - this.client = client; - this.itemManager = itemManager; - this.plugin = plugin; - this.config = config; - getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY_CONFIG, OPTION_CONFIGURE, "B.A. overlay")); - } - - @Override - public Dimension render(Graphics2D graphics) - { - if (client.getGameState() != GameState.LOGGED_IN || currentRound == null) - { - return null; - } - - Role role = currentRound.getRoundRole(); - if (role == null) - { - return null; - } - - Widget roleText = client.getWidget(role.getRoleText()); - Widget roleSprite = client.getWidget(role.getRoleSprite()); - - if (config.showTimer() && roleText != null && roleSprite != null) - { - if (config.showEggCountOverlay() && role.equals(Role.COLLECTOR)) - { - roleText.setText(String.format("(%d) 00:%02d", plugin.getCollectedEggCount(), currentRound.getTimeToChange())); - } - else if (config.showHpCountOverlay() && role.equals(Role.HEALER)) - { - roleText.setText(String.format("(%d) 00:%02d", plugin.getHpHealed(), currentRound.getTimeToChange())); - } - else - { - roleText.setText(String.format("00:%02d", currentRound.getTimeToChange())); - } - Rectangle spriteBounds = roleSprite.getBounds(); - roleSprite.setHidden(true); - graphics.drawImage(plugin.getClockImage(), spriteBounds.x, spriteBounds.y, null); - } - - if (role == Role.COLLECTOR && config.highlightCollectorEggs()) - { - String heardCall = plugin.getCollectorHeardCall(); - Color highlightColor = BarbarianAssaultPlugin.getEggColor(heardCall); - Map calledEggMap = plugin.getCalledEggMap(); - Map yellowEggMap = plugin.getYellowEggs(); - - if (calledEggMap != null) - { - renderEggLocations(graphics, calledEggMap, highlightColor); - } - - // Always show yellow eggs - renderEggLocations(graphics, yellowEggMap, Color.YELLOW); - } - Widget inventory = client.getWidget(WidgetInfo.INVENTORY); - - if (config.highlightItems() && inventory != null && !inventory.isHidden() && ((role == Role.DEFENDER || role == Role.HEALER))) - { - int listenItemId = plugin.getListenItemId(role.getListen()); - - if (listenItemId != -1) - { - Color color = config.highlightColor(); - BufferedImage highlight = ImageUtil.fillImage(itemManager.getImage(listenItemId), new Color(color.getRed(), color.getGreen(), color.getBlue(), 150)); - - for (WidgetItem item : inventory.getWidgetItems()) - { - if (item.getId() == listenItemId) - { - OverlayUtil.renderImageLocation(graphics, item.getCanvasLocation(), highlight); - } - } - } - } - - if (role == Role.HEALER) - { - for (HealerTeam teammate : HealerTeam.values()) - { - Widget widget = client.getWidget(teammate.getTeammate()); - if (widget == null) - { - continue; - } - - String[] teammateHealth = widget.getText().split(" / "); - final int curHealth = Integer.parseInt(teammateHealth[0]); - final int maxHealth = Integer.parseInt(teammateHealth[1]); - - int width = teammate.getWidth(); - final int filledWidth = getBarWidth(maxHealth, curHealth, width); - - int offsetX = teammate.getOffset().getX(); - int offsetY = teammate.getOffset().getY(); - int x = widget.getCanvasLocation().getX() - offsetX; - int y = widget.getCanvasLocation().getY() - offsetY; - - graphics.setColor(HEALTH_BAR_COLOR); - graphics.fillRect(x, y, filledWidth, HEALTH_BAR_HEIGHT); - } - } - - return null; - } - - private static int getBarWidth(int base, int current, int size) - { - final double ratio = (double) current / base; - - if (ratio >= 1) - { - return size; - } - - return (int) Math.round(ratio * size); - } - - private void renderEggLocations(Graphics2D graphics, Map eggMap, Color color) - { - Player player = client.getLocalPlayer(); - - if (player == null) - { - return; - } - - final Stroke originalStroke = graphics.getStroke(); - - for (WorldPoint worldPoint : eggMap.keySet()) - { - LocalPoint groundPoint = LocalPoint.fromWorld(client, worldPoint); - - if (groundPoint == null) - { - continue; - } - if (player.getLocalLocation().distanceTo(groundPoint) > MAX_EGG_DISTANCE) - { - continue; - } - - Polygon poly = Perspective.getCanvasTilePoly(client, groundPoint); - - if (poly == null) - { - continue; - } - - int quantity = eggMap.get(worldPoint); - String quantityText = "x" + quantity; - Point textPoint = Perspective.getCanvasTextLocation(client, graphics, groundPoint, quantityText, OFFSET_Z); - graphics.setColor(color); - graphics.setStroke(new BasicStroke(2)); - graphics.drawPolygon(poly); - OverlayUtil.renderTextLocation(graphics, textPoint, quantityText, Color.WHITE); - } - - graphics.setStroke(originalStroke); - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/BarbarianAssaultPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/BarbarianAssaultPlugin.java index bf2eb52807..ec0fd18543 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/BarbarianAssaultPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/BarbarianAssaultPlugin.java @@ -1,5 +1,8 @@ /* - * Copyright (c) 2018, https://runelitepl.us + * Copyright (c) 2018, Cameron + * Copyright (c) 2018, Jacob M + * Copyright (c) 2019, 7ate9 + * Copyright (c) 2019, https://runelitepl.us * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,108 +27,103 @@ */ package net.runelite.client.plugins.barbarianassault; +import com.google.common.collect.ImmutableMap; import com.google.inject.Provides; -import java.awt.Color; + import java.awt.Font; -import java.awt.Image; +import java.awt.event.KeyEvent; +import java.awt.image.BufferedImage; +import java.time.Instant; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Objects; import javax.inject.Inject; -import lombok.AccessLevel; + import lombok.Getter; import lombok.extern.slf4j.Slf4j; + +import net.runelite.api.Actor; import net.runelite.api.ChatMessageType; import net.runelite.api.Client; -import net.runelite.api.GameState; import net.runelite.api.ItemID; import net.runelite.api.MenuEntry; import net.runelite.api.MessageNode; +import net.runelite.api.NPC; import net.runelite.api.Player; -import net.runelite.api.Tile; +import net.runelite.api.Prayer; +import net.runelite.api.Projectile; +import net.runelite.api.SoundEffectID; import net.runelite.api.Varbits; import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.BeforeRender; import net.runelite.api.events.ChatMessage; -import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.ConfigChanged; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.InteractingChanged; import net.runelite.api.events.ItemDespawned; import net.runelite.api.events.ItemSpawned; import net.runelite.api.events.MenuEntryAdded; +import net.runelite.api.events.MenuOptionClicked; +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.api.events.WidgetLoaded; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetID; import net.runelite.api.widgets.WidgetInfo; +import net.runelite.api.widgets.WidgetItem; +import net.runelite.client.callback.ClientThread; 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 net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.game.ItemManager; +import net.runelite.client.input.KeyListener; +import net.runelite.client.input.KeyManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.PluginType; import net.runelite.client.ui.FontManager; import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.ui.overlay.infobox.InfoBox; +import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import net.runelite.client.util.ColorUtil; import net.runelite.client.util.ImageUtil; import net.runelite.client.util.Text; +import org.apache.commons.lang3.StringUtils; + @Slf4j - - @PluginDescriptor( - name = "Barbarian Assault+", - description = "Custom barbarian assault plugin, use along with BA Tools", - tags = {"minigame", "overlay", "timer"}, - type = PluginType.PVM // don't remove this, added this because our barbarian assault plugin is big time modified + name = "Barbarian Assault", + description = "Custom barbarian assault plugin, use along with BA Tools", + tags = {"minigame", "overlay", "timer"}, + type = PluginType.PVM // don't remove this, added this because our barbarian assault plugin is big time modified ) -public class BarbarianAssaultPlugin extends Plugin +public class BarbarianAssaultPlugin extends Plugin implements KeyListener { - private static final int BA_WAVE_NUM_INDEX = 2; - private static final String START_WAVE = "1"; private static final String ENDGAME_REWARD_NEEDLE_TEXT = "
5"; - @Getter - private int collectedEggCount = 0; + private static final String[] STYLES = {"Defensive", "Aggressive", "Controlled", "Accurate"}; - @Getter - private int positiveEggCount = 0; + private static final int COLOR_CALL_UPDATED = 16316664; - @Getter - private int wrongEggs = 0; + private static final int COLOR_CALL_CALLED = 16291864; - @Getter - private int HpHealed = 0; - - @Getter - private int totalCollectedEggCount = 0; - - @Getter - private int totalHpHealed = 0; - - private boolean hasAnnounced; - private Font font; - private final Image clockImage = ImageUtil.getResourceStreamFromClass(getClass(), "clock.png"); - private int inGameBit = 0; - private String currentWave = START_WAVE; - private GameTimer gameTime; - - @Getter(AccessLevel.PACKAGE) - private final HashMap redEggs = new HashMap<>(); - - @Getter(AccessLevel.PACKAGE) - private final HashMap greenEggs = new HashMap<>(); - - @Getter(AccessLevel.PACKAGE) - private final HashMap blueEggs = new HashMap<>(); - - @Getter(AccessLevel.PACKAGE) - private final HashMap yellowEggs = new HashMap<>(); + private static final int BA_WAVE_NUM_INDEX = 2; @Inject - @Getter private Client client; + @Inject + private ClientThread clientThread; + @Inject private ChatMessageManager chatMessageManager; @@ -136,7 +134,112 @@ public class BarbarianAssaultPlugin extends Plugin private BarbarianAssaultConfig config; @Inject - private BarbarianAssaultOverlay overlay; + private AboveWidgetsOverlay widgetsOverlay; + + @Inject + private AboveSceneOverlay sceneOverlay; + + @Inject + private BarbarianAssaultMenu menu; + + @Inject + private ItemManager itemManager; + + @Inject + private InfoBoxManager infoBoxManager; + + @Inject + private KeyManager keyManager; + + @Getter + private boolean inGame = false; + + @Getter + private Wave wave = null; + + @Getter + private Role role = null; + + @Getter + private Scorecard scorecard = null; + + @Getter + private Timer gameTimer = null; + + @Getter + private Timer callTimer = null; + + @Getter + private int stage = -1; + + @Getter + private BufferedImage clockImage; + + @Getter + private Font font = null; + + @Getter + private final HashMap redEggs = new HashMap<>(); + + @Getter + private final HashMap greenEggs = new HashMap<>(); + + @Getter + private final HashMap blueEggs = new HashMap<>(); + + @Getter + private final HashMap yellowEggs = new HashMap<>(); + + @Getter + private final Map healers = new HashMap<>(); + + @Getter + private String lastCallText = null; + + @Getter + private String lastListenText = null; + + private String lastClickedTell = null; + + private int lastCallColor = -1; + + private int lastInteracted = -1; + + private int lastHealerPoisoned = -1; + + private int tickNum = 0; + + private int gameTick = -1; + + private int inGameBit = 0; + + private boolean syncd = true; + + private boolean tickReset = false; + + private boolean hornCalled = false; + + private boolean hornListened = false; + + @Getter + private boolean usingGloryHorn = false; + + private boolean shiftDown = false; + + private boolean controlDown = false; + + private BufferedImage torsoImage, fighterImage, healerImage, rangerImage, runnerImage; + + private ImmutableMap originalAttackStyles = null; + + private ArrayList deathTimes = new ArrayList<>(); + + private HashMap projectiles = new HashMap<>(); + + private TimerBox tickCounter; + + private String poisonUsed = null; + @Provides BarbarianAssaultConfig provideConfig(ConfigManager configManager) @@ -144,37 +247,143 @@ public class BarbarianAssaultPlugin extends Plugin return configManager.getConfig(BarbarianAssaultConfig.class); } - private Game game; - private Wave wave; - @Override protected void startUp() throws Exception { - overlayManager.add(overlay); - font = FontManager.getRunescapeFont() - .deriveFont(Font.BOLD, 24); + font = FontManager.getRunescapeFont().deriveFont(Font.BOLD, 24); + torsoImage = itemManager.getImage(ItemID.FIGHTER_TORSO); + clockImage = ImageUtil.getResourceStreamFromClass(getClass(), "clock.png"); + fighterImage = ImageUtil.getResourceStreamFromClass(getClass(), "fighter.png"); + healerImage = ImageUtil.getResourceStreamFromClass(getClass(), "healer.png"); + rangerImage = ImageUtil.getResourceStreamFromClass(getClass(), "ranger.png"); + runnerImage = ImageUtil.getResourceStreamFromClass(getClass(), "runner.png"); + overlayManager.add(widgetsOverlay); + overlayManager.add(sceneOverlay); + keyManager.registerKeyListener(this); + clientThread.invoke(this::validateGame); } @Override protected void shutDown() throws Exception { - overlayManager.remove(overlay); - gameTime = null; - currentWave = START_WAVE; - inGameBit = 0; - collectedEggCount = 0; - positiveEggCount = 0; - wrongEggs = 0; - HpHealed = 0; - clearAllEggMaps(); + overlayManager.remove(widgetsOverlay); + overlayManager.remove(sceneOverlay); + keyManager.unregisterKeyListener(this); + showRoleSprite(); + font = null; + torsoImage = null; + clockImage = null; + fighterImage = null; + healerImage = null; + rangerImage = null; + runnerImage = null; + shiftDown = false; + controlDown = false; + resetWave(); + wave = null; + gameTick = client.getTickCount(); + menu.disableSwaps(true); + menu.clearHiddenMenus(); + } + + @Override + public void keyTyped(KeyEvent e) + { + } + + @Override + public void keyPressed(KeyEvent e) + { + if (e.getKeyCode() == KeyEvent.VK_SHIFT) + { + shiftDown = true; + } + + if (e.getKeyCode() == KeyEvent.VK_CONTROL) + { + controlDown = true; + } + } + + @Override + public void keyReleased(KeyEvent e) + { + if (e.getKeyCode() == KeyEvent.VK_SHIFT) + { + shiftDown = false; + } + + if (e.getKeyCode() == KeyEvent.VK_CONTROL) + { + controlDown = false; + } } @Subscribe - public void onGameStateChanged(final GameStateChanged event) + public void onConfigChanged(ConfigChanged configChanged) { - if (event.getGameState() == GameState.LOADING) + //not client thread be careful + if (!configChanged.getGroup().equals("barbarianAssault")) { - clearAllEggMaps(); + return; + } + + switch (configChanged.getKey()) + { + case "showTimer": + if (config.showTimer()) + { + hideRoleSprite(); + } + else + { + showRoleSprite(); + } + break; + + case "swapLadder": + case "swapCollectorBag": + case "swapDestroyEggs": + if (Boolean.valueOf(configChanged.getNewValue())) + { + menu.enableSwaps(); + } + else + { + menu.disableSwaps(false); + } + break; + + case "showDefTimer": + if (config.showDefTimer() && getRole() == Role.DEFENDER) + { + addTickTimer(); + } + else + { + removeTickTimer(); + } + break; + + case "showDeathTimes": + case "showDeathTimesMode": + if (config.showDeathTimes() + && (config.showDeathTimesMode() == DeathTimesMode.INFO_BOX + || config.showDeathTimesMode() == DeathTimesMode.BOTH)) + { + addAllDeathTimes(); + } + else + { + removeAllDeathTimes(false); + } + break; + + case "removePenanceCave": + case "removeUnusedMenus": + case "removeWrongPoison": + clientThread.invoke(() -> menu.validateHiddenMenus(getRole())); + break; } } @@ -184,175 +393,162 @@ public class BarbarianAssaultPlugin extends Plugin switch (event.getGroupId()) { case WidgetID.BA_REWARD_GROUP_ID: - { + announceWaveTime(); + Widget rewardWidget = client.getWidget(WidgetInfo.BA_REWARD_TEXT); - Widget pointsWidget = client.getWidget(WidgetInfo.BA_RUNNERS_PASSED); - if (!rewardWidget.getText().contains(ENDGAME_REWARD_NEEDLE_TEXT) && pointsWidget != null - && !hasAnnounced && client.getVar(Varbits.IN_GAME_BA) == 0) + if (rewardWidget == null) { - wave = new Wave(client); - wave.setWaveAmounts(); - wave.setWavePoints(); - game.getWaves().add(wave); - if (config.showSummaryOfPoints()) - { - announceSomething(wave.getWaveSummary()); - } - } - if (config.waveTimes() && rewardWidget != null && rewardWidget.getText().contains(ENDGAME_REWARD_NEEDLE_TEXT) && gameTime != null) - { - announceTime("Game finished, duration: ", gameTime.getTime(false)); - gameTime = null; - if (config.showTotalRewards()) - { - announceSomething(game.getGameSummary()); - } + break; } - } - break; + Widget pointsWidget = client.getWidget(WidgetInfo.BA_REWARD_TEXT); + if (!rewardWidget.getText().contains(ENDGAME_REWARD_NEEDLE_TEXT)) + { + if (config.showTotalRewards() && pointsWidget != null) + { + // The wave will be null if the plugin is disabled mid game, but + // the wave points will still be accurate if it is re-enabled + if (wave == null) + { + wave = new Wave(client); + } + + wave.setAmounts(); + wave.setPoints(); + + announce(wave.getSummary()); + + // The scorecard will be null if the client is restart mid game + if (scorecard != null) + { + scorecard.addWave(wave); + } + } + } + else + { + announceGameTime(); + + if (config.showTotalRewards() && scorecard != null && scorecard.getNumberOfWaves() == 9) + { + announce(scorecard.getGameSummary()); + } + } + break; + case WidgetID.BA_ATTACKER_GROUP_ID: - { - setOverlayRound(Role.ATTACKER); + startWave(Role.ATTACKER); + menu.validateHiddenMenus(Role.ATTACKER); break; - } + case WidgetID.BA_DEFENDER_GROUP_ID: - { - setOverlayRound(Role.DEFENDER); + startWave(Role.DEFENDER); + menu.validateHiddenMenus(Role.DEFENDER); break; - } + case WidgetID.BA_HEALER_GROUP_ID: - { - setOverlayRound(Role.HEALER); + startWave(Role.HEALER); + menu.validateHiddenMenus(Role.HEALER); break; - } + case WidgetID.BA_COLLECTOR_GROUP_ID: - { - setOverlayRound(Role.COLLECTOR); + startWave(Role.COLLECTOR); + menu.validateHiddenMenus(Role.COLLECTOR); break; - } } } @Subscribe public void onChatMessage(ChatMessage chatMessage) { - if (chatMessage.getMessage().toLowerCase().contains("testing")) - { - ArrayList waves = new ArrayList<>(); - for (int i = 0; i < 1; i++) - { - Wave wave1 = new Wave(client); - int[] amounts = {4, 0, 30, 10, 1, 38}; - int[] points = {-3, -2, 6, -4, -8, -11}; - int[] otherPoints = {38, 35, 33, 30}; - wave1.setWaveAmounts(amounts); - wave1.setWavePoints(points, otherPoints); - waves.add(wave1); - announceSomething(wave1.getWaveSummary()); - } - Game game1 = new Game(client, waves); - announceSomething(game1.getGameSummary()); - } - if (chatMessage.getMessage().toLowerCase().startsWith("wave points")) - { - hasAnnounced = true; - } if (!chatMessage.getType().equals(ChatMessageType.GAMEMESSAGE)) { return; } - int inGame = client.getVar(Varbits.IN_GAME_BA); - if (inGameBit != inGame) + + final String message = chatMessage.getMessage(); + if (message.startsWith("---- Wave:")) { - return; - } - final String message = chatMessage.getMessage().toLowerCase(); - final MessageNode messageNode = chatMessage.getMessageNode(); - final String nodeValue = Text.removeTags(messageNode.getValue()); - String recolored = null; - if (chatMessage.getMessage().startsWith("---- Wave:")) - { - String[] tempMessage = chatMessage.getMessage().split(" "); - currentWave = tempMessage[BA_WAVE_NUM_INDEX]; - collectedEggCount = 0; - HpHealed = 0; - positiveEggCount = 0; - wrongEggs = 0; - if (currentWave.equals(START_WAVE)) + stage = Integer.parseInt(message.split(" ")[BA_WAVE_NUM_INDEX]); + if (stage == 1) { - gameTime = new GameTimer(); - totalHpHealed = 0; - totalCollectedEggCount = 0; - game = new Game(client); - } - else if (gameTime != null) - { - gameTime.setWaveStartTime(); + scorecard = new Scorecard(this); + gameTimer = new Timer(); } } - if (chatMessage.getMessage().contains("exploded")) + else if (isInGame()) { - wrongEggs++; - positiveEggCount--; - } - if (chatMessage.getMessage().contains("You healed")) - { - String[] tokens = message.split(" "); - if (Integer.parseInt(tokens[2]) > 0) + if (message.contains("exploded") && wave != null) { - int Hp = Integer.parseInt(tokens[2]); - HpHealed += Hp; + wave.setWrongEggs(wave.getWrongEggs() + 1); + wave.setPositiveEggCount(wave.getPositiveEggCount() - 1); } - } - - if (message.contains("the wrong type of poisoned food to use")) - { - recolored = ColorUtil.wrapWithColorTag(nodeValue, config.wrongPoisonFoodTextColor()); - } - if (recolored != null) - { - messageNode.setValue(recolored); - chatMessageManager.update(messageNode); - } - } - - @Subscribe - public void onVarbitChanged(VarbitChanged event) - { - int inGame = client.getVar(Varbits.IN_GAME_BA); - - if (inGameBit != inGame) - { - if (inGameBit == 1) + else if (message.contains("You healed") && wave != null) { - overlay.setCurrentRound(null); - - // Use an instance check to determine if this is exiting a game or a tutorial - // After exiting tutorials there is a small delay before changing IN_GAME_BA back to - // 0 whereas when in a real wave it changes while still in the instance. - if (config.waveTimes() && gameTime != null && client.isInInstancedRegion()) + String[] tokens = message.split(" "); + if (Integer.parseInt(tokens[2]) > 0) { - announceTime("Wave " + currentWave + " duration: ", gameTime.getTime(true)); + int health = Integer.parseInt(tokens[2]); + wave.setHpHealed(wave.getHpHealed() + health); } } - else + else if (message.contains("the wrong type of poisoned food to use") && config.highlightNotification()) { - hasAnnounced = false; + final MessageNode messageNode = chatMessage.getMessageNode(); + final String nodeValue = Text.removeTags(messageNode.getValue()); + messageNode.setValue(ColorUtil.wrapWithColorTag(nodeValue, config.highlightNotificationColor())); + chatMessageManager.update(messageNode); + } + else if (message.startsWith("All of the Penance")) + { + String[] tokens = message.split(" "); + + int time = wave == null ? -1 : (int)wave.getWaveTimer().getElapsedTime(); + + switch (tokens[4]) + { + case "Fighters": + addDeathTimes(new TimerBox(fighterImage, this, time)); + break; + + case "Healers": + addDeathTimes(new TimerBox(healerImage, this, time)); + break; + + case "Rangers": + addDeathTimes(new TimerBox(rangerImage, this, time)); + break; + + case "Runners": + addDeathTimes(new TimerBox(runnerImage, this, time)); + break; + } + + if (config.showDeathTimes() && wave != null + && (config.showDeathTimesMode() == DeathTimesMode.CHAT_BOX + || config.showDeathTimesMode() == DeathTimesMode.BOTH)) + { + final MessageNode node = chatMessage.getMessageNode(); + final String nodeValue = Text.removeTags(node.getValue()); + node.setValue(nodeValue + " (" + wave.getWaveTimer().getElapsedTime() + "s)"); + chatMessageManager.update(node); + } } } - - inGameBit = inGame; } @Subscribe public void onItemSpawned(ItemSpawned itemSpawned) { - int itemId = itemSpawned.getItem().getId(); - WorldPoint worldPoint = itemSpawned.getTile().getWorldLocation(); - HashMap eggMap = getEggMap(itemId); + if (!isInGame()) + { + return; + } + + HashMap eggMap = getEggMap(itemSpawned.getItem().getId()); if (eggMap != null) { + WorldPoint worldPoint = itemSpawned.getTile().getWorldLocation(); Integer existingQuantity = eggMap.putIfAbsent(worldPoint, 1); if (existingQuantity != null) { @@ -364,226 +560,983 @@ public class BarbarianAssaultPlugin extends Plugin @Subscribe public void onItemDespawned(ItemDespawned itemDespawned) { - int itemId = itemDespawned.getItem().getId(); - WorldPoint worldPoint = itemDespawned.getTile().getWorldLocation(); - HashMap eggMap = getEggMap(itemId); - - if (eggMap != null && eggMap.containsKey(worldPoint)) - { - int quantity = eggMap.get(worldPoint); - if (quantity > 1) - { - eggMap.put(worldPoint, quantity - 1); - } - else - { - eggMap.remove(worldPoint); - } - } - if (client.getVar(Varbits.IN_GAME_BA) == 0 || !isEgg(itemDespawned.getItem().getId())) + if (!isInGame()) { return; } - if (isUnderPlayer(itemDespawned.getTile())) + + int itemId = itemDespawned.getItem().getId(); + + if (!isItemEgg(itemId)) { - if (overlay.getCurrentRound().getRoundRole() == Role.COLLECTOR) + return; + } + + // If an egg despawns due to time and the collector is standing over it, + // a point will added as if the player picked it up + HashMap eggMap = getEggMap(itemId); + if (eggMap != null) + { + WorldPoint worldPoint = itemDespawned.getTile().getWorldLocation(); + if (eggMap.containsKey(worldPoint)) { - positiveEggCount++; - if (positiveEggCount > 60) + int quantity = eggMap.get(worldPoint); + + if (quantity > 1) { - positiveEggCount = 60; + eggMap.put(worldPoint, quantity - 1); } - collectedEggCount = positiveEggCount - wrongEggs; //true positive - negative egg count + else + { + eggMap.remove(worldPoint); + } + } + } + + if (getRole() == Role.COLLECTOR + && wave != null + && itemDespawned.getTile().getWorldLocation().equals(client.getLocalPlayer().getWorldLocation())) + { + wave.setPositiveEggCount(wave.getPositiveEggCount() + 1); + + if (wave.getPositiveEggCount() > 60) + { + wave.setPositiveEggCount(60); + } + + wave.setCollectedEggCount(wave.getPositiveEggCount() - wave.getWrongEggs()); + } + } + + @Subscribe + public void onGameTick(GameTick event) + { + // Keep in mind isInGame is delayed by a tick when a wave ends + if (!isInGame() || getRole() == null) + { + return; + } + + tickNum++; + + if (tickNum > 9 || tickReset) + { + tickNum = 0; + tickReset = false; + projectiles.entrySet().removeIf(projectile -> projectile.getValue().getRemainingCycles() < 1); + } + + if (tickCounter != null) + { + tickCounter.setCount(tickNum); + } + + Widget weapon = client.getWidget(WidgetInfo.COMBAT_WEAPON); + + if (config.attackStyles() + && isInGame() + && getRole() == Role.ATTACKER + && weapon != null + && (weapon.getText().contains("Crystal halberd") || weapon.getText().contains("Dragon claws"))) + { + if (originalAttackStyles == null) + { + ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); + + builder.put(WidgetInfo.COMBAT_STYLE_ONE, client.getWidget(WidgetInfo.COMBAT_STYLE_ONE).isHidden()); + builder.put(WidgetInfo.COMBAT_STYLE_TWO, client.getWidget(WidgetInfo.COMBAT_STYLE_TWO).isHidden()); + builder.put(WidgetInfo.COMBAT_STYLE_THREE, client.getWidget(WidgetInfo.COMBAT_STYLE_THREE).isHidden()); + builder.put(WidgetInfo.COMBAT_STYLE_FOUR, client.getWidget(WidgetInfo.COMBAT_STYLE_FOUR).isHidden()); + + originalAttackStyles = builder.build(); + } + int i; + for (i = 0; i < STYLES.length; i++) + { + if (lastListenText.contains(STYLES[i])) + { + break; + } + } + switch (i) + { + case 0: //Defensive + client.getWidget(WidgetInfo.COMBAT_STYLE_ONE).setHidden(true); + client.getWidget(WidgetInfo.COMBAT_STYLE_TWO).setHidden(true); + client.getWidget(WidgetInfo.COMBAT_STYLE_THREE).setHidden(true); + client.getWidget(WidgetInfo.COMBAT_STYLE_FOUR).setHidden(false); + break; + case 1: // Aggressive + client.getWidget(WidgetInfo.COMBAT_STYLE_ONE).setHidden(true); + client.getWidget(WidgetInfo.COMBAT_STYLE_TWO).setHidden(false); + client.getWidget(WidgetInfo.COMBAT_STYLE_THREE).setHidden(true); + client.getWidget(WidgetInfo.COMBAT_STYLE_FOUR).setHidden(true); + break; + case 2: // Controlled + if (weapon.getText().contains("Crystal halberd")) + { + client.getWidget(WidgetInfo.COMBAT_STYLE_ONE).setHidden(false); + client.getWidget(WidgetInfo.COMBAT_STYLE_THREE).setHidden(true); + } + else + { + client.getWidget(WidgetInfo.COMBAT_STYLE_ONE).setHidden(true); + client.getWidget(WidgetInfo.COMBAT_STYLE_THREE).setHidden(false); + } + client.getWidget(WidgetInfo.COMBAT_STYLE_TWO).setHidden(true); + client.getWidget(WidgetInfo.COMBAT_STYLE_FOUR).setHidden(true); + break; + case 3: // Accurate + if (weapon.getText().contains("Dragon claws")) + { + client.getWidget(WidgetInfo.COMBAT_STYLE_ONE).setHidden(false); + client.getWidget(WidgetInfo.COMBAT_STYLE_TWO).setHidden(true); + client.getWidget(WidgetInfo.COMBAT_STYLE_THREE).setHidden(true); + client.getWidget(WidgetInfo.COMBAT_STYLE_FOUR).setHidden(true); + } + else + { + client.getWidget(WidgetInfo.COMBAT_STYLE_ONE).setHidden(false); + client.getWidget(WidgetInfo.COMBAT_STYLE_TWO).setHidden(false); + client.getWidget(WidgetInfo.COMBAT_STYLE_THREE).setHidden(false); + client.getWidget(WidgetInfo.COMBAT_STYLE_FOUR).setHidden(false); + } + break; + default: + client.getWidget(WidgetInfo.COMBAT_STYLE_ONE).setHidden(false); + client.getWidget(WidgetInfo.COMBAT_STYLE_TWO).setHidden(false); + client.getWidget(WidgetInfo.COMBAT_STYLE_THREE).setHidden(false); + client.getWidget(WidgetInfo.COMBAT_STYLE_FOUR).setHidden(false); + break; + } + } + else if (originalAttackStyles != null) + { + originalAttackStyles.forEach((w, b) -> client.getWidget(w).setHidden(b)); + } + + if (config.prayerMetronome() && isAnyPrayerActive()) + { + for (int i = 0; i < config.prayerMetronomeVolume(); i++) + { + client.playSoundEffect(SoundEffectID.GE_INCREMENT_PLOP); } } } @Subscribe - public void onMenuEntryAdded(MenuEntryAdded event) + public void onNpcSpawned(NpcSpawned event) { - if (!config.highlightCollectorEggs()) - { - return; - } - if (overlay.getCurrentRound() == null) - { - return; - } - if (overlay.getCurrentRound().getRoundRole() != Role.COLLECTOR) + if (!isInGame()) { return; } - String calledEgg = getCollectorHeardCall(); - String target = event.getTarget(); - String option = event.getOption(); - String targetClean = target.substring(target.indexOf('>') + 1); - String optionClean = option.substring(option.indexOf('>') + 1); + NPC npc = event.getNpc(); - if ("Take".equals(optionClean)) + if (npc == null) { - Color highlightColor = null; + return; + } - if (calledEgg != null && calledEgg.startsWith(targetClean)) - { - highlightColor = getEggColor(targetClean); - } - else if ("Yellow egg".equals(targetClean)) - { - // Always show yellow egg - highlightColor = Color.YELLOW; - } + String name = event.getNpc().getName(); - if (highlightColor != null) + if (name.equals("Penance Healer")) + { + if (!healers.containsKey(npc.getIndex())) { - MenuEntry[] menuEntries = client.getMenuEntries(); - MenuEntry last = menuEntries[menuEntries.length - 1]; - last.setTarget(ColorUtil.prependColorTag(targetClean, highlightColor)); - client.setMenuEntries(menuEntries); + healers.put(npc.getIndex(), new Healer(npc, healers.size(), stage)); } } } - private void setOverlayRound(Role role) + @Subscribe + public void onNpcDespawned(NpcDespawned event) { - if (overlay.getCurrentRound() != null) + if (!isInGame()) { return; } - overlay.setCurrentRound(new Round(role)); + healers.remove(event.getNpc().getIndex()); } - private void announceSomething(final ChatMessageBuilder chatMessage) + + // So this is a mess, but it works and the horn of glory doesn't mess up anything. + // This was almost certainly a waste of time to get working, because almost nobody + // actually uses the horn of glory. At least now there shouldn't be anyone complaining + // about the horn of glory breaking anything and everything that should never break. + @Subscribe + public void onBeforeRender(BeforeRender event) { - chatMessageManager.queue(QueuedMessage.builder() - .type(ChatMessageType.CONSOLE) - .runeLiteFormattedMessage(chatMessage.build()) - .build()); - } - - String getCollectorHeardCall() - { - Widget widget = client.getWidget(WidgetInfo.BA_COLL_LISTEN_TEXT); - String call = null; - - if (widget != null) + if (!isInGame()) { - call = widget.getText(); + return; } - return call; - } + boolean rebuild = false; - Map getCalledEggMap() - { - Map map; - String calledEgg = getCollectorHeardCall(); - - if (calledEgg == null) + Widget callWidget = getRole() == null ? null : client.getWidget(getRole().getGloryCall()); + if (callWidget == null) { - return null; + callWidget = getRole() == null ? null : client.getWidget(getRole().getCall()); } - switch (calledEgg) - { - case "Red eggs": - map = redEggs; - break; - case "Green eggs": - map = greenEggs; - break; - case "Blue eggs": - map = blueEggs; - break; - default: - map = null; - } + String newCallText = getRole() == null ? lastCallText : getRole().getCall(client); + int newCallColor = callWidget == null ? lastCallColor : callWidget.getTextColor(); - return map; - } - - static Color getEggColor(String str) - { - Color color; - - if (str == null) + Widget listenWidget = getRole() == null ? null : client.getWidget(getRole().getGloryListen()); + if (listenWidget == null) { - return null; - } - - if (str.startsWith("Red")) - { - color = Color.RED; - } - else if (str.startsWith("Green")) - { - color = Color.GREEN; - } - else if (str.startsWith("Blue")) - { - color = Color.CYAN; - } - else if (str.startsWith("Yellow")) - { - color = Color.YELLOW; + listenWidget = getRole() == null ? null : client.getWidget(getRole().getListen()); + usingGloryHorn = false; } else { - color = null; + usingGloryHorn = true; } - return color; - } + String newListenText = getRole() == null ? lastListenText : getRole().getListen(client); - private HashMap getEggMap(int itemID) - { - switch (itemID) + if (!Objects.equals(newCallText, lastCallText)) { - case ItemID.RED_EGG: - return redEggs; - case ItemID.GREEN_EGG: - return greenEggs; - case ItemID.BLUE_EGG: - return blueEggs; - case ItemID.YELLOW_EGG: - return yellowEggs; - default: - return null; + rebuild = true; + lastCallText = newCallText; + callTimer = new Timer(); + tickReset = true; + hornCalled = false; + hornListened = false; + menu.setHornUpdated(false); + syncd = true; + + if (tickCounter != null) + { + tickCounter.setInSync(true); + } + } + + if (!Objects.equals(newListenText, lastListenText)) + { + if (newListenText != null && !newListenText.equals("- - -")) + { + hornListened = true; + rebuild = true; + } + else if (hornListened) + { + newListenText = lastListenText; + + // If the player uses the horn of glory, the listen text will be taken from there and set to the + // normal widget when the player exits the interface + if (listenWidget != null && !usingGloryHorn) + { + // Attacker has two widgets for it's listen text, the top widget is the one that is kept track of + if (getRole() == Role.ATTACKER) + { + listenWidget.setText(newListenText); + client.getWidget(WidgetInfo.BA_ATK_LISTEN_BOTTOM_TEXT).setText(Role.getMissingListen(newListenText)); + } + else + { + listenWidget.setText(newListenText); + } + + } + } + + lastListenText = newListenText; + } + + // Disabled for now, will need to also check either chat or verify a busy animation is not happening + /*if (newCallColor == COLOR_CALL_CALLED && !Objects.equals(lastClickedTell, lastCallText)) + { + hornCalled = false; + rebuild = true; + + if (callWidget != null) + { + callWidget.setTextColor(COLOR_CALL_UPDATED); + lastCallColor = COLOR_CALL_UPDATED; + } + } + + else if (newCallColor == COLOR_CALL_UPDATED && Objects.equals(lastClickedTell, lastCallText)) + { + hornCalled = true; + rebuild = true; + + if (callWidget != null) + { + callWidget.setTextColor(COLOR_CALL_CALLED); + lastCallColor = COLOR_CALL_CALLED; + } + }*/ + + // Horn of glory will switch text back to white color before call change + if (newCallColor != lastCallColor) + { + if (newCallColor == COLOR_CALL_CALLED) + { + hornCalled = true; + rebuild = true; + } + else if (hornCalled) + { + newCallColor = COLOR_CALL_CALLED; + if (callWidget != null) + { + callWidget.setTextColor(newCallColor); + } + } + + lastCallColor = newCallColor; + } + + if (rebuild || menu.isRebuildForced()) + { + menu.setRebuildForced(false); + menu.validateHiddenMenus(role); } } + // onMenuEntryAdded is being used for conditional entry changes that are not + // easily achievable using MenuManager, all other changes use MenuManager in + // the BarbarianAssaultMenu/Menus classes + @Subscribe + public void onMenuEntryAdded(MenuEntryAdded event) + { + if (!isInGame()) + { + return; + } + + final List menu = new ArrayList<>(); + final List selected = new ArrayList<>(); + final List priority = new ArrayList<>(); + MenuEntry walk = null; + boolean prioritizeWalk = false; + + String listen = lastListenText != null ? StringUtils.remove(lastListenText, "Pois. ").toLowerCase() : ""; + + for (MenuEntry entry : client.getMenuEntries()) + { + String option = Text.removeTags(entry.getOption()).toLowerCase(); + String target = Text.removeTags(entry.getTarget()).toLowerCase(); + int identifier = entry.getIdentifier(); + + if (option.equals("walk here")) + { + walk = entry; + } + else + { + switch (getRole()) + { + case ATTACKER: + if (config.tagging() && option.equals("attack") && (target.startsWith("penance fighter") || target.startsWith("penance ranger"))) + { + String tag = StringUtils.substringBefore(entry.getTarget(), ")"); + + NPC[] npcs = client.getCachedNPCs(); + + if (identifier >= 0 && identifier < npcs.length) + { + NPC npc = npcs[identifier]; + if (npc != null) + { + Projectile projectile = projectiles.get(identifier); + if (npc.getInteracting() == null && projectile == null) + { + entry.setTarget((tag + ") (" + (10 - (tickNum + 1)) + ")").replace("", "")); + selected.add(entry); + continue; + } + // TODO add check to see if other attacker is attacking fighter/ranger + // Possibly just add it to a list when the projectile is spawned + // Also maybe add order by health option + else if (npc.getInteracting() == client.getLocalPlayer() || (projectile != null && projectile.getInteracting() == npc)) + { + entry.setTarget((tag + ") (" + (10 - (tickNum)) + ")").replace("", "")); + } + else + { + entry.setTarget((tag + ") (" + (10 - (tickNum)) + ")").replace("", "")); + } + } + } + } + break; + + case COLLECTOR: + // Take option for yellow eggs should always be the first option + if (option.equals("take") && target.equals("yellow egg")) + { + priority.add(entry); + continue; + } + else if (config.deprioritizeIncorrectEggs() + && option.equals("take") + && (target.equals("blue egg") || target.equals("green egg") || target.equals("red egg"))) + { + prioritizeWalk = true; + + if (listen.startsWith(target)) + { + selected.add(entry); + continue; + } + } + break; + + case DEFENDER: + // Take option for logs and hammer should always be the first option + if (option.equals("take") + && (target.equals("logs") || target.equals("hammer"))) + { + priority.add(entry); + continue; + } + else if (config.deprioritizeBait() + && option.equals("take") + && (target.equals("tofu") || target.equals("crackers") || target.equals("worms"))) + { + prioritizeWalk = true; + } + break; + + case HEALER: + if (config.healerMenuOption() && target.contains("penance healer") && healers.containsKey(identifier)) + { + String tag = StringUtils.substringBefore(entry.getTarget(), " ("); + int time = healers.get(identifier).timeToPoison(); + + if (time != -1) + { + entry.setTarget(tag + " (" + (healers.get(identifier).timeToPoison()) + ")"); + target = Text.removeTags(entry.getTarget()).toLowerCase(); + } + } + + if ((target.startsWith("poisoned meat ->") || target.startsWith("poisoned tofu ->") || target.startsWith("poisoned worms ->"))) + { + // Poison should only be used on healers + if (config.removeUnusedMenus() && !target.contains("penance healer")) + { + continue; + } + else if (config.controlHealer() && controlDown && identifier == lastHealerPoisoned && target.contains("penance healer")) + { + selected.add(entry); + continue; + } + } + else if (config.shiftOverstock() && target.equals("healer item machine") && shiftDown) + { + if (option.contains(listen)) + { + selected.add(entry); + continue; + } + } + else if (config.removeUnusedMenus()) + { + // Vials that are empty should only be used on spring + if (target.startsWith("healing vial ->") && !target.endsWith("healer spring")) + { + continue; + } + // Vials that are full should only be used on players + else if (target.startsWith("healing vial(4) ->")) + { + Player[] players = client.getCachedPlayers(); + + if (!(identifier >= 0 && identifier < players.length && players[identifier] != null)) + { + continue; + } + } + // Vials that are not full or empty can be used on either players or spring + else if (target.startsWith("healing vial(") && target.contains("->")) + { + Player[] players = client.getCachedPlayers(); + + if ((!(identifier >= 0 && identifier < players.length && players[identifier] != null)) + && !target.endsWith("healer spring")) + { + continue; + } + } + } + break; + } + } + + menu.add(entry); + } + + if (prioritizeWalk && walk != null) + { + menu.remove(walk); + menu.add(walk); + } + + if (!selected.isEmpty()) + { + menu.addAll(selected); + } + + if (!priority.isEmpty()) + { + menu.addAll(priority); + } + + client.setMenuEntries(menu.toArray(new MenuEntry[0])); + } + + @Subscribe + public void onMenuOptionClicked(MenuOptionClicked event) + { + if (!isInGame() && getRole() != null) + { + return; + } + + String target = Text.removeTags(event.getTarget()).toLowerCase(); + + if (getRole() == Role.HEALER) + { + if (target.startsWith("poisoned meat -> penance healer") + || target.startsWith("poisoned tofu -> penance healer") + || target.startsWith("poisoned worms -> penance healer")) + { + lastHealerPoisoned = event.getIdentifier(); + poisonUsed = StringUtils.substringBefore(target.replace("oned", "."), " ->"); + return; + } + } + + // INW + /* + String option = Text.removeTags(event.getMenuOption()); + if (option.startsWith("Tell-")) + { + lastClickedTell = getRole().getCallFromTell(option); + } + else if (usingGloryHorn && option.equals("Call")) + { + + } + */ + } + + // Interacting changed has a slight delay until after the hitsplat is applied + @Subscribe + public void onInteractingChanged(InteractingChanged event) + { + if (!isInGame() || getRole() != Role.HEALER) + { + return; + } + + Actor source = event.getSource(); + + if (source != client.getLocalPlayer()) + { + return; + } + + Actor opponent = event.getTarget(); + + if (opponent == null) + { + if (lastInteracted != -1) + { + if (StringUtils.equalsIgnoreCase(poisonUsed, getRole().getListen(client)) && healers.containsKey(lastInteracted)) + { + Healer healer = healers.get(lastInteracted); + healer.setFoodRemaining(healer.getFoodRemaining() - 1); + healer.setTimeLastPoisoned(Instant.now()); + } + } + + lastInteracted = -1; + poisonUsed = null; + } + else if (StringUtils.equals(opponent.getName(), "Penance Healer")) + { + lastInteracted = ((NPC)opponent).getIndex(); + } + + } + + @Subscribe + public void onProjectileSpawned(ProjectileSpawned event) + { + if (!isInGame()) + { + return; + } + + Actor target = event.getProjectile().getInteracting(); + if (target == null) + { + return; + } + + String name = target.getName(); + if ("Penance Fighter".equals(name) || "Penance Ranger".equals(name)) + { + projectiles.put(((NPC)target).getIndex(), event.getProjectile()); + } + } + + @Subscribe + public void onVarbitChanged(VarbitChanged event) + { + int newInGameBit = client.getVar(Varbits.IN_GAME_BA); + + if (inGameBit != newInGameBit) + { + if (newInGameBit == 0) + { + resetWave(); + callTimer = null; + hornCalled = false; + hornListened = false; + lastListenText = null; + lastCallText = null; + lastCallColor = -1; + lastClickedTell = null; + } + else + { + wave = new Wave(client); + } + + inGameBit = newInGameBit; + } + } + + private void resetWave() + { + inGame = false; + menu.setHornUpdated(false); + menu.setRebuildForced(false); + removeTickTimer(); + removeAllDeathTimes(true); + projectiles.clear(); + clearAllEggMaps(); + healers.clear(); + role = null; + lastInteracted = -1; + poisonUsed = null; + lastHealerPoisoned = -1; + tickNum = 0; + usingGloryHorn = false; + menu.validateHiddenMenus(null); + } + + private void startWave(Role role) + { + inGame = true; + this.role = role; + + validateWidgets(); + } + + // Role widgets are not accurate if a player is using horn of glory + private void validateGame() + { + Role role = null; + + Widget inventory = client.getWidget(WidgetInfo.INVENTORY); + + if (inventory != null) + { + Collection items = inventory.getWidgetItems(); + + for (WidgetItem item : items) + { + int id = item.getId(); + if (id == ItemID.COLLECTOR_HORN) + { + role = Role.COLLECTOR; + } + else if (id == ItemID.DEFENDER_HORN) + { + role = Role.DEFENDER; + } + else if (id == ItemID.ATTACKER_HORN || id == ItemID.ATTACKER_HORN_10517 || id == ItemID.ATTACKER_HORN_10518 + || id == ItemID.ATTACKER_HORN_10519 || id == ItemID.ATTACKER_HORN_10520) + { + role = Role.ATTACKER; + } + else if (id == ItemID.HEALER_HORN || id == ItemID.HEALER_HORN_10527 || id == ItemID.HEALER_HORN_10528 + || id == ItemID.HEALER_HORN_10529 || id == ItemID.HEALER_HORN_10530) + { + role = Role.HEALER; + } + } + } + + if (role != null) + { + inGame = true; + this.role = role; + + inGameBit = 1; + + usingGloryHorn = client.getWidget(role.getGloryListen()) != null; + + Widget stage = client.getWidget(role.getWave()); + + if (stage != null) + { + this.stage = Integer.parseInt(StringUtils.substringAfter(stage.getText(), " ")); + } + + lastCallText = role.getCall(client); + lastListenText = role.getListen(client); + + if (callTimer != null && callTimer.getElapsedTime() > 30) + { + callTimer = null; + + } + + // TODO getTickCount() does not work as expected + // tickNum = (client.getTickCount() - gameTick) % 10; + + syncd = false; + + if (tickCounter != null) + { + tickCounter.setInSync(false); + } + + // If the horn is currently called, or if the call timer is still in sync + // the text will be updated to the last called color + Widget callWidget = getRole() == null ? null : client.getWidget(getRole().getGloryCall()); + if (callWidget == null) + { + callWidget = getRole() == null ? null : client.getWidget(getRole().getCall()); + } + + int newCallColor = callWidget == null ? lastCallColor : callWidget.getTextColor(); + + if (newCallColor == COLOR_CALL_CALLED) + { + lastCallColor = COLOR_CALL_CALLED; + lastClickedTell = lastCallText; + } + else if (callTimer == null) + { + lastCallColor = COLOR_CALL_UPDATED; + lastClickedTell = null; + } + + if (callWidget != null) + { + callWidget.setTextColor(lastCallColor); + } + } + else + { + inGameBit = 0; + } + + validateWidgets(); + + menu.enableSwaps(); + menu.validateHiddenMenus(getRole()); + } + + private void validateWidgets() + { + if (config.showTimer()) + { + hideRoleSprite(); + } + else + { + showRoleSprite(); + } + + if (config.showDefTimer() && getRole() == Role.DEFENDER) + { + addTickTimer(); + } + else + { + removeTickTimer(); + } + + if (config.showDeathTimes() + && (config.showDeathTimesMode() == DeathTimesMode.INFO_BOX + || config.showDeathTimesMode() == DeathTimesMode.BOTH)) + { + addAllDeathTimes(); + } + else + { + removeAllDeathTimes(false); + } + } + + private void addTickTimer() + { + if (!isInGame() || tickCounter != null) + { + return; + } + + tickCounter = new TimerBox(torsoImage, this, tickNum); + + tickCounter.setInSync(syncd); + + tickCounter.setTooltipEnabled(true); + + removeAllDeathTimes(false); + + infoBoxManager.addInfoBox(tickCounter); + + addAllDeathTimes(); + } + + private void removeTickTimer() + { + if (tickCounter != null) + { + infoBoxManager.removeInfoBox(tickCounter); + tickCounter = null; + } + } + + private void addDeathTimes(TimerBox box) + { + if (!isInGame()) + { + return; + } + + deathTimes.add(box); + + if (config.showDeathTimes() && (config.showDeathTimesMode() == DeathTimesMode.INFO_BOX || config.showDeathTimesMode() == DeathTimesMode.BOTH)) + { + infoBoxManager.addInfoBox(box); + } + } + + private void addAllDeathTimes() + { + if (!isInGame()) + { + return; + } + + List boxes = infoBoxManager.getInfoBoxes(); + + for (TimerBox box : deathTimes) + { + if (!boxes.contains(box)) + { + infoBoxManager.addInfoBox(box); + } + } + } + + private void removeAllDeathTimes(boolean clear) + { + for (InfoBox box : infoBoxManager.getInfoBoxes().toArray(new InfoBox[0])) + { + if (box instanceof TimerBox && box.getImage() != torsoImage) + { + infoBoxManager.removeInfoBox(box); + } + } + if (clear) + { + deathTimes.clear(); + } + } + + private void hideRoleSprite() + { + if (getRole() == null || !isInGame()) + { + return; + } + + Widget roleSprite = client.getWidget(role.getRoleSprite()); + + if (roleSprite != null) + { + roleSprite.setHidden(true); + } + } + + private void showRoleSprite() + { + if (getRole() == null || !isInGame()) + { + return; + } + + Widget roleText = client.getWidget(role.getRoleText()); + Widget roleSprite = client.getWidget(role.getRoleSprite()); + + if (roleSprite != null) + { + roleSprite.setHidden(false); + } + if (roleText != null) + { + roleText.setText(getRole().name()); + } + } + + private void announceWaveTime() + { + if (config.waveTimes() && wave != null) + { + announceTime("Wave " + getStage() + " duration: ", wave.getWaveTimer().getElapsedTimeFormatted()); + } + } + + private void announceGameTime() + { + if (config.waveTimes() && gameTimer != null) + { + announceTime("Game finished, duration: ", gameTimer.getElapsedTimeFormatted()); + } + } private void announceTime(String preText, String time) { final String chatMessage = new ChatMessageBuilder() - .append(ChatColorType.NORMAL) - .append(preText) - .append(ChatColorType.HIGHLIGHT) - .append(time) - .build(); + .append(ChatColorType.NORMAL) + .append(preText) + .append(ChatColorType.HIGHLIGHT) + .append(time) + .build(); chatMessageManager.queue(QueuedMessage.builder() - .type(ChatMessageType.CONSOLE) - .runeLiteFormattedMessage(chatMessage) - .build()); + .type(ChatMessageType.CONSOLE) + .runeLiteFormattedMessage(chatMessage) + .build()); } - private boolean isEgg(int itemID) + private void announce(final ChatMessageBuilder chatMessage) { - return itemID == ItemID.RED_EGG || itemID == ItemID.GREEN_EGG - || itemID == ItemID.BLUE_EGG || itemID == ItemID.YELLOW_EGG; + chatMessageManager.queue(QueuedMessage.builder() + .type(ChatMessageType.CONSOLE) + .runeLiteFormattedMessage(chatMessage.build()) + .build()); } - private boolean isUnderPlayer(Tile tile) + long getTimeToChange() { - Player local = client.getLocalPlayer(); - if (local == null) - { - return false; - } - - return (tile.getWorldLocation().equals(local.getWorldLocation())); + return 30 - callTimer.getElapsedTime(); } private void clearAllEggMaps() @@ -594,39 +1547,44 @@ public class BarbarianAssaultPlugin extends Plugin yellowEggs.clear(); } - public Font getFont() + private HashMap getEggMap(int itemID) { - return font; - } - - Image getClockImage() - { - return clockImage; - } - - int getListenItemId(WidgetInfo listenInfo) - { - Widget listenWidget = client.getWidget(listenInfo); - - if (listenWidget != null) + switch (itemID) { - switch (listenWidget.getText()) + case ItemID.RED_EGG: + return redEggs; + + case ItemID.GREEN_EGG: + return greenEggs; + + case ItemID.BLUE_EGG: + return blueEggs; + + case ItemID.YELLOW_EGG: + return yellowEggs; + } + + return null; + } + + private boolean isItemEgg(int itemId) + { + return itemId == ItemID.RED_EGG || + itemId == ItemID.GREEN_EGG || + itemId == ItemID.BLUE_EGG || + itemId == ItemID.YELLOW_EGG; + } + + private boolean isAnyPrayerActive() + { + for (Prayer pray : Prayer.values()) + { + if (client.isPrayerActive(pray)) { - case "Tofu": - return ItemID.TOFU; - case "Crackers": - return ItemID.CRACKERS; - case "Worms": - return ItemID.WORMS; - case "Pois. Worms": - return ItemID.POISONED_WORMS; - case "Pois. Tofu": - return ItemID.POISONED_TOFU; - case "Pois. Meat": - return ItemID.POISONED_MEAT; + return true; } } - return -1; + return false; } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Calls.java b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Calls.java deleted file mode 100644 index 5cba373d95..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Calls.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2018, https://runelitepl.us - * 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.barbarianassault; - -import java.util.HashMap; -import java.util.Map; - -public enum Calls -{ //Attacker Calls - RED_EGG("Red egg", "Tell-red"), - GREEN_EGG("Green egg", "Tell-green"), - BLUE_EGG("Blue egg", "Tell-blue"), - //Collector Calls - CONTROLLED("Controlled/Bullet/Wind", "Tell-controlled"), - ACCURATE("Accurate/Field/Water", "Tell-accurate"), - AGGRESSIVE("Aggressive/Blunt/Earth", "Tell-aggressive"), - DEFENSIVE("Defensive/Barbed/Fire", "Tell-defensive"), - //Healer Calls - TOFU("Tofu", "Tell-tofu"), - CRACKERS("Crackers", "Tell-crackers"), - WORMS("Worms", "Tell-worms"), - //Defender Calls - POIS_WORMS("Pois. Worms", "Tell-worms"), - POIS_TOFU("Pois. Tofu", "Tell-tofu"), - POIS_MEAT("Pois. Meat", "Tell-meat"); - - private final String call; - private final String option; - - private static final Map CALL_MENU = new HashMap<>(); - - static - { - for (Calls s : values()) - { - CALL_MENU.put(s.getCall(), s.getOption()); - } - } - - Calls(String call, String option) - { - this.call = call; - this.option = option; - } - - public String getCall() - { - return call; - } - - public String getOption() - { - return option; - } - - public static String getOption(String call) - { - return CALL_MENU.get(call); - } - -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/HealerTeam.java b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/DeathTimesMode.java similarity index 71% rename from runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/HealerTeam.java rename to runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/DeathTimesMode.java index 909dea212b..4083ce8ba4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/HealerTeam.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/DeathTimesMode.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2018, whartd + * Copyright (c) 2019, 7ate9 + * Copyright (c) 2019, https://runelitepl.us * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,21 +25,23 @@ */ package net.runelite.client.plugins.barbarianassault; -import lombok.AllArgsConstructor; -import lombok.Getter; -import net.runelite.api.Point; -import net.runelite.api.widgets.WidgetInfo; -@Getter -@AllArgsConstructor -enum HealerTeam +public enum DeathTimesMode { - TEAMMATE1(WidgetInfo.BA_HEAL_TEAMMATE1, new Point(28, 2), 115), - TEAMMATE2(WidgetInfo.BA_HEAL_TEAMMATE2, new Point(26, 2), 115), - TEAMMATE3(WidgetInfo.BA_HEAL_TEAMMATE3, new Point(26, 2), 115), - TEAMMATE4(WidgetInfo.BA_HEAL_TEAMMATE4, new Point(25, 2), 115); + BOTH("Both"), + CHAT_BOX("Chat Box"), + INFO_BOX("Info Box"); - private WidgetInfo teammate; - private Point offset; - private int width; -} + private final String name; + + DeathTimesMode(String name) + { + this.name = name; + } + + @Override + public String toString() + { + return name; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Healer.java b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Healer.java new file mode 100644 index 0000000000..8a2154889c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Healer.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2019, 7ate9 + * Copyright (c) 2019, https://runelitepl.us + * 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.barbarianassault; + +import com.google.common.collect.ImmutableList; +import lombok.AccessLevel; +import lombok.Data; +import lombok.Getter; +import net.runelite.api.NPC; + +import java.time.Duration; +import java.time.Instant; +import java.util.List; + + +@Data +class Healer +{ + @Getter(AccessLevel.NONE) + private static final List> CODES = ImmutableList.of( + // ImmutableList.of(firstCallFood, secondCallFood, lastFoodTime), + ImmutableList.of(new int[]{1, 1}, new int[]{0, 0}, new int[]{0, 0}), + ImmutableList.of(new int[]{1, 1, 2}, new int[]{0, 0, 0}, new int[]{0, 0, 21}), + ImmutableList.of(new int[]{1, 6, 2}, new int[]{0, 0, 0}, new int[]{0, 0, 0}), + ImmutableList.of(new int[]{2, 5, 2, 0}, new int[]{0, 0, 7, 10}, new int[]{0, 0, 0, 0}), + ImmutableList.of(new int[]{2, 5, 2, 3, 0}, new int[]{0, 0, 0, 0, 7}, new int[]{0, 0, 21, 30, 0}), + ImmutableList.of(new int[]{3, 5, 2, 2, 0, 0}, new int[]{0, 0, 0, 2, 9, 10}, new int[]{12, 18, 21, 0, 0, 0}), + ImmutableList.of(new int[]{3, 7, 1, 1, 0, 0, 0}, new int[]{2, 0, 1, 1, 2, 4, 10}, new int[]{0, 21, 0, 0, 30, 45, 0}), + ImmutableList.of(new int[]{1, 9, 1, 1, 0, 0, 0}, new int[]{1, 0, 1, 1, 2, 2, 10}, new int[]{0, 0, 0, 0, 33, 42, 0}), + ImmutableList.of(new int[]{2, 8, 1, 1, 0, 0, 0, 0}, new int[]{1, 0, 1, 1, 2, 1, 1, 10}, new int[]{0, 21, 0, 0, 0, 0, 0, 0, 0}), + ImmutableList.of(new int[]{2, 5, 1, 1, 0, 0, 0}, new int[]{1, 0, 1, 1, 4, 4, 8}, new int[]{21, 33, 0, 33, 30, 45, 0})); + + private final NPC npc; + + private int wave; + + private int spawnNumber; + + private int foodRemaining; + + private int lastFoodTime; + + private int firstCallFood; + + private int secondCallFood; + + private Instant timeLastPoisoned = null; + + Healer(NPC npc, int spawnNumber, int wave) + { + this.npc = npc; + this.wave = wave; + this.spawnNumber = spawnNumber; + List code = CODES.get(wave - 1); + this.firstCallFood = code.get(0)[spawnNumber]; + this.secondCallFood = code.get(1)[spawnNumber]; + this.lastFoodTime = code.get(2)[spawnNumber]; + this.foodRemaining = firstCallFood + secondCallFood; + } + + int timeToPoison() + { + if (timeLastPoisoned == null) + { + return -1; + } + else + { + long time = Duration.between(timeLastPoisoned, Instant.now()).getSeconds(); + return time > 20 ? 0 : (int)(20 - time); + } + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Menus.java b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Menus.java new file mode 100644 index 0000000000..4f18e25578 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Menus.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2019, 7ate9 + * Copyright (c) 2019, https://runelitepl.us + * 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.barbarianassault; + +import com.google.common.collect.ImmutableSet; +import lombok.AllArgsConstructor; +import lombok.Getter; +import net.runelite.api.MenuAction; +import net.runelite.client.menus.ComparableEntry; + +@AllArgsConstructor +public enum Menus +{ + ATTACK_PENANCE_FIGHTER(Role.ATTACKER, new ComparableEntry("attack", "penance fighter", -1, -1, true, false)), + ATTACK_PENANCE_RANGER(Role.ATTACKER, new ComparableEntry("attack", "penance ranger", -1, -1, true, false)), + GET_SPIKES_PETRIFIED_MUSHROOM(Role.ATTACKER, new ComparableEntry("get-spikes", "petrified mushroom", -1, -1, true, true)), + TAKE_ATTACKER_ITEM_MACHINE(Role.ATTACKER, new ComparableEntry("take", "attacker item machine", -1, -1, false, true)), + TELL_RED_ATTACKER_HORN(Role.ATTACKER, new ComparableEntry("tell-red", "attacker horn", -1, -1, true, true)), + TELL_GREEN_ATTACKER_HORN(Role.ATTACKER, new ComparableEntry("tell-green", "attacker horn", -1, -1, true, true)), + TELL_BLUE_ATTACKER_HORN(Role.ATTACKER, new ComparableEntry("tell-blue", "attacker horn", -1, -1, true, true)), + + BLOCK_PENANCE_CAVE(Role.DEFENDER, new ComparableEntry("block", "penance cave", -1, -1, true, true)), + DUNK_LAVA_CRATER(Role.DEFENDER, new ComparableEntry("dunk", "lava crater", -1, -1, true, true)), + FIX(Role.DEFENDER, new ComparableEntry("fix", "", -1, -1, true, false)), + STOCK_UP_DEFENDER_ITEM_MACHINE(Role.DEFENDER, new ComparableEntry("stock-up", "defender item machine", -1, -1, true, true)), + TAKE_DEFENDER_ITEM_MACHINE(Role.DEFENDER, new ComparableEntry("take", "defender item machine", -1, -1, false, true)), + TAKE_HAMMER(Role.DEFENDER, new ComparableEntry("take", "hammer", -1, -1, true, true)), + TAKE_LOGS(Role.DEFENDER, new ComparableEntry("take", "logs", -1, -1, true, true)), + TELL_WORMS_DEFENDER_HORN(Role.DEFENDER, new ComparableEntry("tell-worms", "defender horn", -1, -1, true, true)), + TELL_TOFU_DEFENDER_HORN(Role.DEFENDER, new ComparableEntry("tell-tofu", "defender horn", -1, -1, true, true)), + TELL_MEAT_DEFENDER_HORN(Role.DEFENDER, new ComparableEntry("tell-meat", "defender horn", -1, -1, true, true)), + + DRINK_FROM_HEALER_SPRING(Role.HEALER, new ComparableEntry("drink-from", "healer spring", -1, -1, true, true)), + DUNK_POISON_CRATER(Role.HEALER, new ComparableEntry("dunk", "poison crater", -1, -1, true, true)), + STOCK_UP_HEALER_ITEM_MACHINE(Role.HEALER, new ComparableEntry("stock-up", "healer item machine", -1, -1, true, true)), + TAKE_HEALER_ITEM_MACHINE(Role.HEALER, new ComparableEntry("take", "healer item machine", -1, -1, false, true)), + TAKE_FROM_HEALER_SPRING(Role.HEALER, new ComparableEntry("take-from", "healer spring", -1, -1, true, true)), + TELL_TOFU_HEALER_HORN(Role.HEALER, new ComparableEntry("tell-tofu", "healer horn", -1, -1, true, true)), + TELL_CRACKERS_HEALER_HORN(Role.HEALER, new ComparableEntry("tell-crackers", "healer horn", -1, -1, true, true)), + TELL_WORMS_HEALER_HORN(Role.HEALER, new ComparableEntry("tell-worms", "healer horn", -1, -1, true, true)), + USE_VIAL_GROUND(Role.HEALER, new ComparableEntry("use", "healing vial", -1, MenuAction.ITEM_USE_ON_GROUND_ITEM.getId(), true, false)), + USE_VIAL_ITEM(Role.HEALER, new ComparableEntry("use", "healing vial", -1, MenuAction.ITEM_USE_ON_WIDGET_ITEM.getId(), true, false)), + USE_VIAL_NPC(Role.HEALER, new ComparableEntry("use", "healing vial", -1, MenuAction.ITEM_USE_ON_NPC.getId(), true, false)), + USE_VIAL_WIDGET(Role.HEALER, new ComparableEntry("use", "healing vial", -1, MenuAction.ITEM_USE_ON_WIDGET.getId(), true, false)), + + CONVERT_COLLECTOR_CONVERTER(Role.COLLECTOR, new ComparableEntry("convert", "collector converter", -1, -1, true, true)), + LOAD_EGG_HOPPER(Role.COLLECTOR, new ComparableEntry("load", "egg hopper", -1, -1, true, true)), + TAKE_BLUE_EGG(Role.COLLECTOR, new ComparableEntry("take", "blue egg", -1, -1, true, true)), + TAKE_GREEN_EGG(Role.COLLECTOR, new ComparableEntry("take", "green egg", -1, -1, true, true)), + TAKE_RED_EGG(Role.COLLECTOR, new ComparableEntry("take", "red egg", -1, -1, true, true)), + TAKE_YELLOW_EGG(Role.COLLECTOR, new ComparableEntry("take", "yellow egg", -1, -1, true, true)), + TELL_CONTROLLED_COLLECTOR_HORN(Role.COLLECTOR, new ComparableEntry("tell-controlled", "collector horn", -1, -1, true, true)), + TELL_ACCURATE_COLLECTOR_HORN(Role.COLLECTOR, new ComparableEntry("tell-accurate", "collector horn", -1, -1, true, true)), + TELL_AGGRESSIVE_COLLECTOR_HORN(Role.COLLECTOR, new ComparableEntry("tell-aggressive", "collector horn", -1, -1, true, true)), + TELL_DEFENSIVE_COLLECTOR_HORN(Role.COLLECTOR, new ComparableEntry("tell-defensive", "collector horn", -1, -1, true, true)), + + ATTACK_PENANCE_QUEEN(null, new ComparableEntry("attack", "penance queen", -1, -1, true, false)), + ATTACK_QUEEN_SPAWN(null, new ComparableEntry("attack", "queen spawn", -1, -1, true, false)), + DROP_HORN(null, new ComparableEntry("drop", "horn", -1, -1, true, false)), + EXAMINE_HORN(null, new ComparableEntry("examine", "horn", -1, -1, true, false)), + LIGHT_LOGS(null, new ComparableEntry("light", "logs", -1, -1, true, true)), + MEDIC_HORN(null, new ComparableEntry("medic", "horn", -1, -1, true, false)), + USE_HORN(null, new ComparableEntry("use", "horn", -1, -1, true, false)); + + @Getter + private final Role role; + + @Getter + private final ComparableEntry entry; + + private static final ImmutableSet ALL = ImmutableSet.copyOf(Menus.values()); + + public String getOption() + { + return entry.getOption(); + } + + public String getTarget() + { + return entry.getTarget(); + } + + public int getId() + { + return entry.getId(); + } + + public int getType() + { + return entry.getType(); + } + + public boolean isStrictOption() + { + return entry.isStrictOption(); + } + + public boolean isStrictTarget() + { + return entry.isStrictTarget(); + } + + public static ImmutableSet getMenus() + { + return ALL; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Role.java b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Role.java index 8bc864b4a2..3b3260f559 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Role.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Role.java @@ -1,5 +1,7 @@ /* - * Copyright (c) 2018, https://runelitepl.us + * Copyright (c) 2018, Cameron + * Copyright (c) 2019, 7ate9 + * Copyright (c) 2019, https://runelitepl.us * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,39 +26,211 @@ */ package net.runelite.client.plugins.barbarianassault; +import com.google.common.collect.ImmutableMap; +import lombok.AllArgsConstructor; import lombok.Getter; +import net.runelite.api.Client; +import net.runelite.api.ItemID; +import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; + +@AllArgsConstructor enum Role { - ATTACKER(WidgetInfo.BA_ATK_LISTEN_TEXT, WidgetInfo.BA_ATK_CALL_TEXT, WidgetInfo.BA_ATK_ROLE_TEXT, WidgetInfo.BA_ATK_ROLE_SPRITE), - DEFENDER(WidgetInfo.BA_DEF_LISTEN_TEXT, WidgetInfo.BA_DEF_CALL_TEXT, WidgetInfo.BA_DEF_ROLE_TEXT, WidgetInfo.BA_DEF_ROLE_SPRITE), - COLLECTOR(WidgetInfo.BA_COLL_LISTEN_TEXT, WidgetInfo.BA_COLL_CALL_TEXT, WidgetInfo.BA_COLL_ROLE_TEXT, WidgetInfo.BA_COLL_ROLE_SPRITE), - HEALER(WidgetInfo.BA_HEAL_LISTEN_TEXT, WidgetInfo.BA_HEAL_CALL_TEXT, WidgetInfo.BA_HEAL_ROLE_TEXT, WidgetInfo.BA_HEAL_ROLE_SPRITE); + ATTACKER(WidgetInfo.BA_ATK_WAVE_TEXT, WidgetInfo.BA_ATK_LISTEN_TOP_TEXT, WidgetInfo.BA_ATK_HORN_LISTEN_TEXT, + WidgetInfo.BA_ATK_CALL_TEXT, WidgetInfo.BA_COLL_HORN_LISTEN_TEXT, WidgetInfo.BA_ATK_ROLE_TEXT, + WidgetInfo.BA_ATK_ROLE_SPRITE), + DEFENDER(WidgetInfo.BA_DEF_WAVE_TEXT, WidgetInfo.BA_DEF_LISTEN_TEXT, WidgetInfo.BA_DEF_HORN_LISTEN_TEXT, + WidgetInfo.BA_DEF_CALL_TEXT, WidgetInfo.BA_HEAL_HORN_LISTEN_TEXT, WidgetInfo.BA_DEF_ROLE_TEXT, + WidgetInfo.BA_DEF_ROLE_SPRITE), + COLLECTOR(WidgetInfo.BA_COLL_WAVE_TEXT, WidgetInfo.BA_COLL_LISTEN_TEXT, WidgetInfo.BA_COLL_HORN_LISTEN_TEXT, + WidgetInfo.BA_COLL_CALL_TEXT, WidgetInfo.BA_ATK_HORN_LISTEN_TEXT, WidgetInfo.BA_COLL_ROLE_TEXT, + WidgetInfo.BA_COLL_ROLE_SPRITE), + HEALER(WidgetInfo.BA_HEAL_WAVE_TEXT, WidgetInfo.BA_HEAL_LISTEN_TEXT, WidgetInfo.BA_DEF_HORN_LISTEN_TEXT, + WidgetInfo.BA_HEAL_CALL_TEXT, WidgetInfo.BA_DEF_HORN_LISTEN_TEXT, WidgetInfo.BA_HEAL_ROLE_TEXT, + WidgetInfo.BA_HEAL_ROLE_SPRITE); + @Getter + private final WidgetInfo wave; @Getter private final WidgetInfo listen; - + @Getter + private final WidgetInfo gloryListen; @Getter private final WidgetInfo call; - + @Getter + private final WidgetInfo gloryCall; @Getter private final WidgetInfo roleText; - @Getter private final WidgetInfo roleSprite; - Role(WidgetInfo listen, WidgetInfo call, WidgetInfo role, WidgetInfo roleSprite) + // Duplicate* entries are to catch instances where the horn of glory has + // text different than the normal horn + private static final ImmutableMap TELLS = ImmutableMap.builder() + .put("Red egg", "Tell-red") + .put("Green egg", "Tell-green") + .put("Blue egg", "Tell-blue") + .put("Controlled/Bullet/Wind", "Tell-controlled") + .put("Accurate/Field/Water", "Tell-accurate") + .put("Aggressive/Blunt/Earth", "Tell-aggressive") + .put("Defensive/Barbed/Fire", "Tell-defensive") + .put("Tofu", "Tell-tofu") + .put("Crackers", "Tell-crackers") + .put("Worms", "Tell-worms") + .put("Poison Worms", "Tell-worms") + .put("Pois. Worms", "Tell-worms") + .put("Poison Tofu", "Tell-tofu") + .put("Pois. Tofu", "Tell-tofu") + .put("Poison Meat", "Tell-meat") + .put("Pois. Meat", "Tell-meat") + .build(); + private static final ImmutableMap GLORY_CALLS = ImmutableMap.builder() + .put("Controlled/Bullet/Wind", "Controlled/") + .put("Accurate/Field/Water", "Accurate/") + .put("Aggressive/Blunt/Earth", "Aggressive/") + .put("Defensive/Barbed/Fire", "Defensive/") + .put("Tofu", "Tofu") + .put("Crackers", "Crackers") + .put("Worms", "Worms") + .put("Poison worms", "Pois. Worms") + .put("Poison tofu", "Pois. Tofu") + .put("Poison meat", "Pois. Meat") + .put("Red egg", "Red egg") + .put("Green egg", "Green egg") + .put("Blue egg", "Blue egg") + .build(); + private static final ImmutableMap ITEMS = ImmutableMap.builder() + .put("Tofu", ItemID.TOFU) + .put("Crackers", ItemID.CRACKERS) + .put("Worms", ItemID.WORMS) + .put("Pois. Worms", ItemID.POISONED_WORMS) + .put("Pois. Tofu", ItemID.POISONED_TOFU) + .put("Pois. Meat", ItemID.POISONED_MEAT) + .put("Defensive/", ItemID.BARBED_ARROW) + .put("Aggressive/", ItemID.BLUNT_ARROW) + .put("Accurate/", ItemID.FIELD_ARROW) + .put("Controlled/", ItemID.BULLET_ARROW) + .build(); + private static final ImmutableMap SPLIT_LISTENS = ImmutableMap.builder() + .put("Controlled/", "Bullet/Wind") + .put("Bullet/Wind", "Controlled/") + .put("Accurate/", "Field/Water") + .put("Field/Water", "Accurate/") + .put("Aggressive/", "Blunt/Earth") + .put("Blunt/Earth", "Aggressive/") + .put("Defensive/", "Barbed/Fire") + .put("Barbed/Fire", "Defensive/") + .build(); + + + int getListenItem(String listen) { - this.listen = listen; - this.call = call; - this.roleText = role; - this.roleSprite = roleSprite; + return ITEMS.getOrDefault(listen, -1); } - @Override - public String toString() + String getTell(String call) { - return name(); + return TELLS.getOrDefault(call, ""); } -} \ No newline at end of file + + String getCall(Client client) + { + // Do not reverse these if statements to be more efficient + // The normal widgets are no longer null/hidden after you + // click one time in the horn, and the values are incorrect + Widget callWidget = client.getWidget(getGloryCall()); + if (callWidget != null) + { + return GLORY_CALLS.get(callWidget.getText()); + } + + callWidget = client.getWidget(getCall()); + if (callWidget != null) + { + return callWidget.getText(); + } + + return null; + } + + String getListen(Client client) + { + // See the comment in getCall(Client client), before editing + Widget listenWidget = client.getWidget(getGloryListen()); + if (listenWidget != null) + { + return GLORY_CALLS.get(listenWidget.getText()); + } + + listenWidget = client.getWidget(getListen()); + if (listenWidget != null) + { + return listenWidget.getText(); + } + + return null; + } + + static String getMissingListen(String listen) + { + return SPLIT_LISTENS.getOrDefault(listen, "- - -"); + } + + // I call it "Switchception" :wutwedoin: + // Should probably switch to using an interface instead of an enum at this point + String getCallFromTell(String listen) + { + switch (this) + { + case COLLECTOR: + switch (listen) + { + case "Tell-controlled": + return "Controlled/"; + case "Tell-accurate": + return "Accurate/"; + case "Tell-aggressive": + return "Aggressive/"; + case "Tell-defensive": + return "Defensive/"; + } + break; + case ATTACKER: + switch (listen) + { + case "Tell-red": + return "Red egg"; + case "Tell-green": + return "Green egg"; + case "Tell-blue": + return "Blue egg"; + } + break; + case HEALER: + switch (listen) + { + case "Tell-tofu": + return "Tofu"; + case "Tell-crackers": + return "Crackers"; + case "Tell-worms": + return "Worms"; + } + break; + case DEFENDER: + switch (listen) + { + case "Tell-meat": + return "Pois. Meat"; + case "Tell-tofu": + return "Pois. Tofu"; + case "Tell-worms": + return "Pois. Worms"; + } + break; + } + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Game.java b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Scorecard.java similarity index 80% rename from runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Game.java rename to runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Scorecard.java index e7ae5af8be..e33c798a83 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Game.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Scorecard.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2018, https://runelitepl.us + * Copyright (c) 2018, Jacob M + * Copyright (c) 2019, https://runelitepl.us * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,18 +27,21 @@ package net.runelite.client.plugins.barbarianassault; import java.awt.Color; import java.util.ArrayList; + +import lombok.AccessLevel; import lombok.Getter; -import net.runelite.api.Client; import net.runelite.api.events.ChatMessage; import net.runelite.client.chat.ChatMessageBuilder; import net.runelite.client.eventbus.Subscribe; + @Getter -public class Game +public class Scorecard { - private Client client; - private String currentWave; - private ArrayList Waves = new ArrayList<>(); + private BarbarianAssaultPlugin game; + + @Getter(AccessLevel.NONE) + private ArrayList waves = new ArrayList<>(); private String[] totalDescriptions = { "A: ", "; D: ", @@ -55,46 +59,46 @@ public class Game private int[] totalAmounts = new int[6]; private int[] otherRolesPoints = new int[4]; - Game(Client client) + Scorecard(BarbarianAssaultPlugin game) { - this.client = client; - } - - Game(Client client, ArrayList waves) - { - this.client = client; - this.Waves = waves; + this.game = game; } @Subscribe public void onChatMessage(ChatMessage chatMessage) { - if (chatMessage.getMessage().startsWith("---- Wave:")) + if (chatMessage.getMessage().startsWith("---- Points:")) { - String[] tempMessage = chatMessage.getMessage().split(" "); - currentWave = tempMessage[2]; - String[] temp = currentWave.split(" "); - } - if (currentWave.equals("1")) - { - Waves = null; - totalPoints = new int[6]; - totalAmounts = new int[6]; + if (game.getStage() == 1) + { + totalPoints = new int[6]; + totalAmounts = new int[6]; + } } } + void addWave(Wave wave) + { + this.waves.add(wave); + } + + int getNumberOfWaves() + { + return waves.size(); + } + ChatMessageBuilder getGameSummary() { int[] amountsList; int[] pointsList; int[] otherRolesPointsList; ChatMessageBuilder message = new ChatMessageBuilder(); - message.append("Round points: "); - for (Wave w : Waves) + message.append("Game points: "); + for (Wave wave : waves) { - amountsList = w.getWaveAmounts(); - pointsList = w.getWavePoints(); - otherRolesPointsList = w.getOtherRolesPointsList(); + amountsList = wave.getAmounts(); + pointsList = wave.getPoints(); + otherRolesPointsList = wave.getOtherRolesPointsList(); for (int j = 0; j < totalAmounts.length; j++) { totalAmounts[j] += amountsList[j]; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/GameTimer.java b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Timer.java similarity index 76% rename from runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/GameTimer.java rename to runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Timer.java index 948ab8bc85..69347915a6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/GameTimer.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Timer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, https://runelitepl.us + * Copyright (c) 2019, https://runelitepl.us * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,37 +24,31 @@ */ package net.runelite.client.plugins.barbarianassault; +import lombok.Getter; + import java.time.Duration; import java.time.Instant; import java.time.LocalTime; import java.time.format.DateTimeFormatter; -import net.runelite.api.Constants; -class GameTimer +class Timer { - final private Instant startTime = Instant.now(); - private Instant prevWave = startTime; + @Getter + private final Instant startTime; - String getTime(boolean waveTime) + Timer() { - final Instant now = Instant.now(); - final Duration elapsed; - - if (waveTime) - { - elapsed = Duration.between(prevWave, now); - } - else - { - elapsed = Duration.between(startTime, now).minusMillis(Constants.GAME_TICK_LENGTH); - } - - return formatTime(LocalTime.ofSecondOfDay(elapsed.getSeconds())); + this.startTime = Instant.now(); } - void setWaveStartTime() + long getElapsedTime() { - prevWave = Instant.now(); + return Duration.between(startTime, Instant.now()).getSeconds(); + } + + String getElapsedTimeFormatted() + { + return formatTime(LocalTime.ofSecondOfDay(getElapsedTime())); } private static String formatTime(LocalTime time) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Round.java b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/TimerBox.java similarity index 59% rename from runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Round.java rename to runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/TimerBox.java index 119392ed9e..8efa4fe917 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Round.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/TimerBox.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2018, Cameron + * Copyright (c) 2019, 7ate9 + * Copyright (c) 2019, https://runelitepl.us * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,50 +25,65 @@ */ package net.runelite.client.plugins.barbarianassault; -import java.time.Duration; -import java.time.Instant; -import javax.inject.Inject; -import lombok.Getter; -import lombok.Setter; -import net.runelite.api.Constants; +import lombok.Data; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.ui.overlay.infobox.InfoBox; -class Round +import java.awt.Color; +import java.awt.image.BufferedImage; + +@Data +public class TimerBox extends InfoBox { - private final Instant roundStartTime; + private int count; - @Getter - private final Role roundRole; + private boolean inSync = true; - @Getter - @Setter - private boolean runnersKilled; + private boolean tooltipEnabled = false; - @Getter - @Setter - private boolean rangersKilled; - - @Getter - @Setter - private boolean healersKilled; - - @Getter - @Setter - private boolean fightersKilled; - - @Inject - public Round(Role role) + TimerBox(BufferedImage image, Plugin plugin, int count) { - this.roundRole = role; - this.roundStartTime = Instant.now().plusMillis(2 * Constants.GAME_TICK_LENGTH); + super(image, plugin); + this.count = count; } - public long getRoundTime() + @Override + public String getText() { - return Duration.between(roundStartTime, Instant.now()).getSeconds(); + if (count == -1) + { + return ""; + } + return Integer.toString(getCount()); } - long getTimeToChange() + @Override + public Color getTextColor() { - return 30 + (Duration.between(Instant.now(), roundStartTime).getSeconds() % 30); + if (inSync) + { + return Color.WHITE; + } + else + { + return Color.RED; + } + } + + @Override + public String getTooltip() + { + if (!tooltipEnabled) + { + return ""; + } + else if (inSync) + { + return "Valid"; + } + else + { + return "Invalid"; + } } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Wave.java b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Wave.java index 4a15f0b9de..a392312b75 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Wave.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/Wave.java @@ -1,5 +1,7 @@ /* - * Copyright (c) 2018, https://runelitepl.us + * Copyright (c) 2018, Jacob M + * Copyright (c) 2019, 7ate9 + * Copyright (c) 2019, https://runelitepl.us * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,90 +27,105 @@ package net.runelite.client.plugins.barbarianassault; import com.google.common.collect.ImmutableList; -import java.awt.Color; +import lombok.AccessLevel; +import lombok.Data; import lombok.Getter; import net.runelite.api.Client; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.chat.ChatMessageBuilder; -@Getter -class Wave -{ - private Client client; - private final ImmutableList WIDGETS = ImmutableList.of( - WidgetInfo.BA_FAILED_ATTACKER_ATTACKS, - WidgetInfo.BA_RUNNERS_PASSED, - WidgetInfo.BA_EGGS_COLLECTED, - WidgetInfo.BA_HITPOINTS_REPLENISHED, - WidgetInfo.BA_WRONG_POISON_PACKS, - WidgetInfo.BA_HONOUR_POINTS_REWARD - ); - private final ImmutableList POINTSWIDGETS = ImmutableList.of( -//base - WidgetInfo.BA_BASE_POINTS, -//att - WidgetInfo.BA_FAILED_ATTACKER_ATTACKS_POINTS, - WidgetInfo.BA_RANGERS_KILLED, - WidgetInfo.BA_FIGHTERS_KILLED, -//def - WidgetInfo.BA_RUNNERS_PASSED_POINTS, - WidgetInfo.BA_RUNNERS_KILLED, -//coll - WidgetInfo.BA_EGGS_COLLECTED_POINTS, -//heal - WidgetInfo.BA_HEALERS_KILLED, - WidgetInfo.BA_HITPOINTS_REPLENISHED_POINTS, - WidgetInfo.BA_WRONG_POISON_PACKS_POINTS - ); - private int[] waveAmounts = new int[6]; - private int[] allPointsList = new int[10]; - private int[] wavePoints = new int[6]; - private int[] otherRolesPointsList = new int[4]; - private String[] descriptions = { - " A: ", - "; D: ", - "; C: ", - "; Vial: ", - "; H packs: ", - "; Total: "}; +import java.awt.Color; - private String[] otherPointsDescriptions = { - " A: ", - " D: ", - " C: ", - " H: " - }; + +@Data +public class Wave +{ + @Getter(AccessLevel.NONE) + private static final ImmutableList WIDGETS = ImmutableList.of( + WidgetInfo.BA_FAILED_ATTACKER_ATTACKS, + WidgetInfo.BA_RUNNERS_PASSED, + WidgetInfo.BA_EGGS_COLLECTED, + WidgetInfo.BA_HITPOINTS_REPLENISHED, + WidgetInfo.BA_WRONG_POISON_PACKS, + WidgetInfo.BA_HONOUR_POINTS_REWARD + ); + + @Getter(AccessLevel.NONE) + private static final ImmutableList POINTSWIDGETS = ImmutableList.of( + //Base + WidgetInfo.BA_BASE_POINTS, + //Attacker + WidgetInfo.BA_FAILED_ATTACKER_ATTACKS_POINTS, + WidgetInfo.BA_RANGERS_KILLED, + WidgetInfo.BA_FIGHTERS_KILLED, + //Defender + WidgetInfo.BA_RUNNERS_PASSED_POINTS, + WidgetInfo.BA_RUNNERS_KILLED, + //Collector + WidgetInfo.BA_EGGS_COLLECTED_POINTS, + //Healer + WidgetInfo.BA_HEALERS_KILLED, + WidgetInfo.BA_HITPOINTS_REPLENISHED_POINTS, + WidgetInfo.BA_WRONG_POISON_PACKS_POINTS + ); + + @Getter(AccessLevel.NONE) + private final Client client; + + private final Timer waveTimer; + + private boolean runnersKilled; + + private boolean rangersKilled; + + private boolean healersKilled; + + private boolean fightersKilled; + + private int collectedEggCount = 0; + + private int positiveEggCount = 0; + + private int wrongEggs = 0; + + private int hpHealed = 0; + + private int totalCollectedEggCount = 0; + + private int totalHpHealed = 0; + + private int[] amounts = new int[6]; + + private int[] allPointsList = new int[10]; + + private int[] points = new int[6]; + + private int[] otherRolesPointsList = new int[4]; + + private String[] descriptions = {" A: ", "; D: ", "; C: ", "; Vial: ", "; H packs: ", "; Total: "}; + + private String[] otherPointsDescriptions = {" A: ", " D: ", " C: ", " H: "}; Wave(Client client) { this.client = client; + this.waveTimer = new Timer(); } - void setWaveAmounts(int[] amounts) - { - System.arraycopy(amounts, 0, waveAmounts, 0, amounts.length); - } - - void setWavePoints(int[] points, int[] otherRolesPoints) - { - System.arraycopy(points, 0, wavePoints, 0, points.length); - System.arraycopy(otherRolesPoints, 0, otherRolesPointsList, 0, otherRolesPoints.length); - } - - void setWaveAmounts() + void setAmounts() { for (int i = 0; i < WIDGETS.size(); i++) { Widget w = client.getWidget(WIDGETS.get(i)); if (w != null) { - waveAmounts[i] = Integer.parseInt(w.getText()); + amounts[i] = Integer.parseInt(w.getText()); } } } - void setWavePoints() + void setPoints() { for (int i = 0; i < POINTSWIDGETS.size(); i++) { @@ -117,26 +134,26 @@ class Wave switch (i) { case 1: - wavePoints[0] += allPointsList[i]; + points[0] += allPointsList[i]; break; case 4: - wavePoints[1] += allPointsList[i]; + points[1] += allPointsList[i]; break; case 6: - wavePoints[2] += allPointsList[i]; + points[2] += allPointsList[i]; break; case 8: case 9: - wavePoints[3] += allPointsList[i]; + points[3] += allPointsList[i]; break; default: break; } } - wavePoints[5] = 0; - for (int i = 0; i < wavePoints.length - 1; i++) + points[5] = 0; + for (int i = 0; i < points.length - 1; i++) { - wavePoints[5] += wavePoints[i]; + points[5] += points[i]; } for (int i = 0; i < POINTSWIDGETS.size(); i++) { @@ -166,37 +183,35 @@ class Wave case 9: otherRolesPointsList[3] += Integer.parseInt(w.getText()); break; - default: - break; } } } - ChatMessageBuilder getWaveSummary() + ChatMessageBuilder getSummary() { ChatMessageBuilder message = new ChatMessageBuilder(); message.append("Wave points:"); for (int i = 0; i < descriptions.length; i++) { - if (i != 4) + message.append(descriptions[i]); + if (i != 5) { - message.append(descriptions[i]); - message.append(String.valueOf(waveAmounts[i])); - message.append("("); - if (wavePoints[i] < 0) - { - message.append(Color.RED, String.valueOf(wavePoints[i])); - } - else if (wavePoints[i] > 0) - { - message.append(Color.BLUE, String.valueOf(wavePoints[i])); - } - else - { - message.append(String.valueOf(wavePoints[i])); - } - message.append(")"); + message.append(String.valueOf(amounts[i])); } + message.append("("); + if (points[i] < 0) + { + message.append(Color.RED, String.valueOf(points[i])); + } + else if (points[i] > 0) + { + message.append(Color.BLUE, String.valueOf(points[i])); + } + else + { + message.append(String.valueOf(points[i])); + } + message.append(")"); } message.append(System.getProperty("line.separator")); message.append("All roles points this wave: "); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/clock.png b/runelite-client/src/main/java/net/runelite/client/plugins/barbarianassault/clock.png deleted file mode 100644 index c0f39269e1aaae46a617aad042f9ddf1cdabda41..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 197 zcmeAS@N?(olHy`uVBq!ia0vp^f*{Pn1|+R>-G2co&H|6fVg?3oVGw3ym^DWNC|K_4 z;uvDldv=nc*Z~C&XJ>c4D`%~%S?0W1`QqBbmQ}T~vrc|jiJ!nwKauBM?ZPstEq6GL zFaP3s^lqY}(v{6;nHUzVN|R+{2o2qPPWghs2d*M^AIDvhRzYlu^4*KKE^lwT(Y1Fr sBg40#MU87L&vG)STK-ho`2QlOUg8Rsl(YYL1D(L&>FVdQ&MBb@0G}E~kpKVy diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsConfig.java deleted file mode 100644 index 5ffce8db02..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsConfig.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (c) 2018, https://runelitepl.us - * 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.batools; - -import net.runelite.client.config.Config; -import net.runelite.client.config.ConfigGroup; -import net.runelite.client.config.ConfigItem; - -@ConfigGroup("BATools") -public interface BAToolsConfig extends Config -{ - @ConfigItem( - keyName = "defTimer", - name = "Defender Tick Timer", - description = "Shows the current cycle tick of runners." - ) - default boolean defTimer() - { - return false; - } - - @ConfigItem( - keyName = "calls", - name = "Remove Incorrect Calls", - description = "Remove incorrect calls." - ) - default boolean calls() - { - return false; - } - - @ConfigItem( - keyName = "swapLadder", - name = "Swap ladder option", - description = "Swap Climb-down with Quick-start in the wave lobbies" - ) - default boolean swapLadder() - { - return true; - } - - @ConfigItem( - keyName = "swapCollectorBag", - name = "swaps collector bag in ba to empty left click", - description = "Make empty the left-click option on collector bag" - ) - default boolean swapCollectorBag() - { - return false; - } - - @ConfigItem( - keyName = "swapDestroyEggs", - name = "Left click destroy eggs in BA", - description = "Make destroy the left-click option for collector eggs" - ) - default boolean swapDestroyEggs() - { - return false; - } - - @ConfigItem( - keyName = "healerCodes", - name = "Healer Codes", - description = "Overlay to show healer codes" - ) - default boolean healerCodes() - { - return false; - } - - @ConfigItem( - keyName = "healerMenuOption", - name = "Healer menu options", - description = "Shows time since last food placed on healer" - ) - default boolean healerMenuOption() - { - return false; - } - - @ConfigItem( - keyName = "eggBoi", - name = "Collector helper", - description = "Hold shift to collect the correct egg" - ) - default boolean eggBoi() - { - return false; - } - - @ConfigItem( - keyName = "osHelp", - name = "Shift OS", - description = "Hold shift to only pick up correct eggs" - ) - default boolean osHelp() - { - return false; - } - - @ConfigItem( - keyName = "prayerMetronome", - name = "Prayer Metronome", - description = "Similar to metronome plugin but only activates when a prayer is active" - ) - default boolean prayerMetronome() - { - return false; - } - - @ConfigItem( - keyName = "prayerMetronomeVolume", - name = "Prayer Metronome Volume", - description = "Volume level" - ) - default int prayerMetronomeVolume() - { - return 1; - } - - @ConfigItem( - keyName = "attackStyles", - name = "Attack Styles", - description = "Hide attack styles depending on weapon." - ) - default boolean attackStyles() - { - return false; - } - - @ConfigItem( - keyName = "removeBA", - name = "*Barbarian Assault Helper*", - description = "Remove unnecessary menu options in Barbarian Assault depending on role
Examples: Remove attack options when not attacker
Remove take options when not collector" - ) - default boolean removeBA() - { - return true; - } - - @ConfigItem( - keyName = "removeWrongEggs", - name = "Remove wrong eggs - *Barbarian Assault Helper*", - description = "Remove unnecessary menu options in Barbarian Assault depending on role
Examples: Remove attack options when not attacker
Remove take options when not collector" - ) - default boolean removeWrongEggs() - { - return false; - } - - @ConfigItem( - keyName = "removeWrongHealFood", - name = "Remove wrong Heal Food - *Barbarian Assault Helper*", - description = "Remove unnecessary menu options in Barbarian Assault depending on role
Examples: Remove attack options when not attacker
Remove take options when not collector" - ) - default boolean removeHealWrongFood() - { - return false; - } - - @ConfigItem( - keyName = "tagging", - name = "Attack Tags", - description = "Highlights the menu entry of an attacker/ranger that has not been tagged." - ) - default boolean tagging() - { - return false; - } - - @ConfigItem( - keyName = "ctrlHealer", - name = "Control Healer", - description = "Hold ctrl to put last healer clicked on top" - ) - default boolean ctrlHealer() - { - return false; - } - - @ConfigItem( - keyName = "removePenanceCave", - name = "Remove Block Penance Cave", - description = "Removes unnecessary menu option, however Moon wanted it back" - ) - default boolean removePenanceCave() - { - return false; - } - -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsOverlay.java deleted file mode 100644 index 3fcf59ba34..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsOverlay.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2018, https://runelitepl.us - * 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.batools; - -import java.awt.Color; -import static java.awt.Color.GREEN; -import static java.awt.Color.RED; -import java.awt.Dimension; -import java.awt.Graphics2D; -import java.time.Duration; -import java.time.Instant; -import javax.inject.Inject; -import lombok.extern.slf4j.Slf4j; -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.OverlayUtil; - -@Slf4j - -public class BAToolsOverlay extends Overlay -{ - private final BAToolsConfig config; - private BAToolsPlugin plugin; - - @Inject - public BAToolsOverlay(BAToolsPlugin plugin, BAToolsConfig config) - { - setPosition(OverlayPosition.DYNAMIC); - setLayer(OverlayLayer.ABOVE_SCENE); - this.config = config; - this.plugin = plugin; - } - - - @Override - public Dimension render(Graphics2D graphics) - { - if (!config.healerCodes()) - { - return null; - } - - for (Healer healer : plugin.getHealers().values()) - { - Color color; - int timeLeft = healer.getLastFoodTime() - (int) Duration.between(plugin.getWave_start(), Instant.now()).getSeconds(); - timeLeft = timeLeft < 1 ? 0 : timeLeft; - - if (healer.getFoodRemaining() > 1) - { - color = GREEN; - } - else if (healer.getFoodRemaining() == 1) - { - if (timeLeft > 0) - { - color = RED; - } - else - { - color = GREEN; - } - } - else - { - continue; - } - - String text = String.format("%d %d", - healer.getFoodRemaining(), - timeLeft); - - - OverlayUtil.renderActorOverlay(graphics, healer.getNpc(), text, color); - } - return null; - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsPlugin.java deleted file mode 100644 index d7ca339124..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/batools/BAToolsPlugin.java +++ /dev/null @@ -1,862 +0,0 @@ -/* - * Copyright (c) 2018, https://runelitepl.us - * 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.batools; - -import com.google.common.collect.ImmutableMap; -import com.google.inject.Provides; -import java.awt.event.KeyEvent; -import java.awt.image.BufferedImage; -import java.time.Duration; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.inject.Inject; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.Actor; -import net.runelite.api.ChatMessageType; -import net.runelite.api.Client; -import net.runelite.api.ItemID; -import net.runelite.api.MenuEntry; -import net.runelite.api.NPC; -import net.runelite.api.NpcID; -import net.runelite.api.Prayer; -import net.runelite.api.SoundEffectID; -import net.runelite.api.Varbits; -import net.runelite.api.coords.WorldPoint; -import net.runelite.api.events.ChatMessage; -import net.runelite.api.events.ConfigChanged; -import net.runelite.api.events.GameTick; -import net.runelite.api.events.HitsplatApplied; -import net.runelite.api.events.InteractingChanged; -import net.runelite.api.events.MenuEntryAdded; -import net.runelite.api.events.MenuOptionClicked; -import net.runelite.api.events.NpcDespawned; -import net.runelite.api.events.NpcSpawned; -import net.runelite.api.events.VarbitChanged; -import net.runelite.api.events.WidgetLoaded; -import net.runelite.api.widgets.Widget; - - -import net.runelite.api.widgets.WidgetID; -import net.runelite.api.widgets.WidgetInfo; -import static net.runelite.api.widgets.WidgetInfo.BA_ATK_CALL_TEXT; -import static net.runelite.api.widgets.WidgetInfo.BA_ATK_LISTEN_TEXT; -import static net.runelite.api.widgets.WidgetInfo.BA_ATK_ROLE_TEXT; -import static net.runelite.api.widgets.WidgetInfo.BA_COLL_CALL_TEXT; -import static net.runelite.api.widgets.WidgetInfo.BA_COLL_LISTEN_TEXT; -import static net.runelite.api.widgets.WidgetInfo.BA_COLL_ROLE_TEXT; -import static net.runelite.api.widgets.WidgetInfo.BA_DEF_CALL_TEXT; -import static net.runelite.api.widgets.WidgetInfo.BA_DEF_ROLE_TEXT; -import static net.runelite.api.widgets.WidgetInfo.BA_HEAL_CALL_TEXT; -import static net.runelite.api.widgets.WidgetInfo.BA_HEAL_LISTEN_TEXT; -import static net.runelite.api.widgets.WidgetInfo.COMBAT_STYLE_FOUR; -import static net.runelite.api.widgets.WidgetInfo.COMBAT_STYLE_ONE; -import static net.runelite.api.widgets.WidgetInfo.COMBAT_STYLE_THREE; -import static net.runelite.api.widgets.WidgetInfo.COMBAT_STYLE_TWO; -import net.runelite.client.config.ConfigManager; -import net.runelite.client.eventbus.Subscribe; -import net.runelite.client.game.ItemManager; -import net.runelite.client.input.KeyListener; -import net.runelite.client.input.KeyManager; -import net.runelite.client.plugins.Plugin; -import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.plugins.PluginType; -import net.runelite.client.ui.overlay.OverlayManager; -import net.runelite.client.ui.overlay.infobox.InfoBoxManager; -import net.runelite.client.util.Text; -import org.apache.commons.lang3.ArrayUtils; -import net.runelite.client.menus.MenuManager; - - -@Slf4j -@PluginDescriptor( - name = "BA Tools", - description = "Custom tools for Barbarian Assault", - tags = {"minigame", "overlay", "timer"}, - type = PluginType.PVM, - enabledByDefault = false -) - -public class BAToolsPlugin extends Plugin implements KeyListener -{ - private boolean inGame; - private int tickNum; - private int pastCall = 0; - private int currentWave = 1; - private int lastHealer; - private static final int BA_WAVE_NUM_INDEX = 2; - private static final WorldPoint healerSpawnPoint = new WorldPoint(1898, 1586, 0); - private final List entries = new ArrayList<>(); - private ImmutableMap originalAttackStyles; - private HashMap foodPressed = new HashMap<>(); - private CycleCounter counter; - private Actor lastInteracted; - - private boolean shiftDown; - private boolean ctrlDown; - - @Inject - private Client client; - - @Inject - private OverlayManager overlayManager; - - @Inject - private BAToolsConfig config; - - @Inject - private ItemManager itemManager; - - @Inject - private InfoBoxManager infoBoxManager; - - @Inject - private BAToolsOverlay overlay; - - @Getter - private Map healers; - - @Getter - private Instant wave_start; - - @Inject - private MenuManager menuManager; - - @Inject - private KeyManager keyManager; - - - @Provides - BAToolsConfig provideConfig(ConfigManager configManager) - { - return configManager.getConfig(BAToolsConfig.class); - } - - @Override - protected void startUp() throws Exception - { - overlayManager.add(overlay); - healers = new HashMap<>(); - wave_start = Instant.now(); - //lastInteracted = null; - foodPressed.clear(); - keyManager.registerKeyListener(this); - } - - @Override - protected void shutDown() throws Exception - { - removeCounter(); - healers.clear(); - inGame = false; - lastInteracted = null; - overlayManager.remove(overlay); - keyManager.unregisterKeyListener(this); - shiftDown = false; - } - - @Subscribe - public void onWidgetLoaded(WidgetLoaded event) - { - switch (event.getGroupId()) - { - case WidgetID.BA_REWARD_GROUP_ID: - { - Widget rewardWidget = client.getWidget(WidgetInfo.BA_REWARD_TEXT); - - if (rewardWidget != null && rewardWidget.getText().contains("
5")) - { - tickNum = 0; - } - } - } - } - - @Subscribe - public void onGameTick(GameTick event) - { - Widget callWidget = getWidget(); - - if (callWidget != null) - { - if (callWidget.getTextColor() != pastCall && callWidget.getTextColor() == 16316664) - { - tickNum = 0; - } - pastCall = callWidget.getTextColor(); - } - if (inGame && config.defTimer()) - { - if (tickNum > 9) - { - tickNum = 0; - } - - if (counter == null) - { - addCounter(); - lastHealer = 0; - } - counter.setCount(tickNum); - - tickNum++; - } - - Widget weapon = client.getWidget(593, 1); - - if (config.attackStyles() - && weapon != null - && inGame - && weapon.getText().contains("Crystal halberd") || weapon.getText().contains("Dragon claws") - && client.getWidget(BA_ATK_LISTEN_TEXT) != null) - { - if (originalAttackStyles == null) - { - ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); - - builder.put(COMBAT_STYLE_ONE, client.getWidget(COMBAT_STYLE_ONE).isHidden()); - builder.put(COMBAT_STYLE_TWO, client.getWidget(COMBAT_STYLE_TWO).isHidden()); - builder.put(COMBAT_STYLE_THREE, client.getWidget(COMBAT_STYLE_THREE).isHidden()); - builder.put(COMBAT_STYLE_FOUR, client.getWidget(COMBAT_STYLE_FOUR).isHidden()); - - originalAttackStyles = builder.build(); - } - - String style = client.getWidget(BA_ATK_LISTEN_TEXT).getText(); - - if (style.contains("Defensive")) - { - client.getWidget(COMBAT_STYLE_ONE).setHidden(true); - client.getWidget(COMBAT_STYLE_TWO).setHidden(true); - client.getWidget(COMBAT_STYLE_THREE).setHidden(true); - client.getWidget(COMBAT_STYLE_FOUR).setHidden(false); - } - else if (style.contains("Aggressive")) - { - client.getWidget(COMBAT_STYLE_ONE).setHidden(true); - client.getWidget(COMBAT_STYLE_TWO).setHidden(false); - client.getWidget(COMBAT_STYLE_THREE).setHidden(true); - client.getWidget(COMBAT_STYLE_FOUR).setHidden(true); - } - else if (style.contains("Controlled")) - { - if (weapon.getText().contains("Crystal halberd")) - { - client.getWidget(COMBAT_STYLE_ONE).setHidden(false); - client.getWidget(COMBAT_STYLE_THREE).setHidden(true); - } - else - { - client.getWidget(COMBAT_STYLE_ONE).setHidden(true); - client.getWidget(COMBAT_STYLE_THREE).setHidden(false); - } - client.getWidget(COMBAT_STYLE_TWO).setHidden(true); - client.getWidget(COMBAT_STYLE_FOUR).setHidden(true); - } - else if (style.contains("Accurate") && weapon.getText().contains("Dragon claws")) - { - client.getWidget(COMBAT_STYLE_ONE).setHidden(false); - client.getWidget(COMBAT_STYLE_TWO).setHidden(true); - client.getWidget(COMBAT_STYLE_THREE).setHidden(true); - client.getWidget(COMBAT_STYLE_FOUR).setHidden(true); - } - else - { - client.getWidget(COMBAT_STYLE_ONE).setHidden(false); - client.getWidget(COMBAT_STYLE_TWO).setHidden(false); - client.getWidget(COMBAT_STYLE_THREE).setHidden(false); - client.getWidget(COMBAT_STYLE_FOUR).setHidden(false); - } - - } - else if (originalAttackStyles != null) - { - originalAttackStyles.forEach((w, b) -> client.getWidget(w).setHidden(b)); - } - - if (config.prayerMetronome() && isAnyPrayerActive()) - { - for (int i = 0; i < config.prayerMetronomeVolume(); i++) - { - client.playSoundEffect(SoundEffectID.GE_INCREMENT_PLOP); - } - } - } - - private Widget getWidget() - { - if (client.getWidget(BA_DEF_CALL_TEXT) != null) - { - return client.getWidget(BA_DEF_CALL_TEXT); - } - else if (client.getWidget(BA_ATK_CALL_TEXT) != null) - { - return client.getWidget(BA_ATK_CALL_TEXT); - } - else if (client.getWidget(BA_COLL_CALL_TEXT) != null) - { - return client.getWidget(BA_COLL_CALL_TEXT); - } - else if (client.getWidget(BA_HEAL_CALL_TEXT) != null) - { - return client.getWidget(BA_HEAL_CALL_TEXT); - } - return null; - } - - @Subscribe - public void onVarbitChanged(VarbitChanged event) - { - int inGameBit = client.getVar(Varbits.IN_GAME_BA); - - if (inGameBit == 1 && !inGame || - inGameBit == 0 && inGame) - { - inGame = inGameBit == 1; - - if (!inGame) - { - pastCall = 0; - removeCounter(); - foodPressed.clear(); - } - else - { - addCounter(); - } - } - } - - @Subscribe - public void onChatMessage(ChatMessage event) - { - if (event.getType() == ChatMessageType.GAMEMESSAGE - && event.getMessage().startsWith("---- Wave:")) - { - String[] message = event.getMessage().split(" "); - currentWave = Integer.parseInt(message[BA_WAVE_NUM_INDEX]); - wave_start = Instant.now(); - healers.clear(); - } - } - - @Subscribe - public void onNpcSpawned(NpcSpawned event) - { - NPC npc = event.getNpc(); - - if (inGame && isNpcHealer(npc.getId())) - { - if (checkNewSpawn(npc) || Duration.between(wave_start, Instant.now()).getSeconds() < 16) - { - int spawnNumber = healers.size(); - healers.put(npc, new Healer(npc, spawnNumber, currentWave)); - } - } - } - - @Subscribe - public void onHitsplatApplied(HitsplatApplied hitsplatApplied) - { - Actor actor = hitsplatApplied.getActor(); - - if (healers.isEmpty() && !(actor instanceof NPC) && lastInteracted == null) - { - return; - } - - for (Healer healer : healers.values()) - { - if (healer.getNpc() == actor && actor == lastInteracted) - { - healer.setFoodRemaining(healer.getFoodRemaining() - 1); - } - } - } - - @Subscribe - public void onNpcDespawned(NpcDespawned event) - { - healers.remove(event.getNpc()); - } - - @Subscribe - public void onInteractingChanged(InteractingChanged event) - { - Actor opponent = event.getTarget(); - - if (opponent instanceof NPC && isNpcHealer(((NPC) opponent).getId()) && event.getSource() != client.getLocalPlayer()) - { - lastInteracted = opponent; - } - } - - private static boolean isNpcHealer(int npcId) - { - return npcId == NpcID.PENANCE_HEALER || - npcId == NpcID.PENANCE_HEALER_5766 || - npcId == NpcID.PENANCE_HEALER_5767 || - npcId == NpcID.PENANCE_HEALER_5768 || - npcId == NpcID.PENANCE_HEALER_5769 || - npcId == NpcID.PENANCE_HEALER_5770 || - npcId == NpcID.PENANCE_HEALER_5771 || - npcId == NpcID.PENANCE_HEALER_5772 || - npcId == NpcID.PENANCE_HEALER_5773 || - npcId == NpcID.PENANCE_HEALER_5774; - } - - @Subscribe - public void onMenuEntryAdded(MenuEntryAdded event) - { - - final int itemId = event.getIdentifier(); - String option = Text.removeTags(event.getOption()).toLowerCase(); - String target = Text.removeTags(event.getTarget()).toLowerCase(); - - if (config.swapDestroyEggs() & (target.equals("red egg") || target.equals("green egg") || target.equals("blue egg"))) - { - menuManager.addSwap("destroy", option, target); - } - - if (config.swapCollectorBag() & target.equals("collection bag")) - { - menuManager.addSwap("empty", option, target); - } - - if (config.swapLadder() && option.equals("climb-down") && target.equals("ladder")) - { - menuManager.addSwap("Quick-start", option, target); - } - else if (config.removeBA() && client.getVar(Varbits.IN_GAME_BA) == 1 && !option.contains("tell-"))//if in barbarian assault and menu isnt from a horn - { - if (itemId == ItemID.LOGS && !target.contains("healing vial")) - { - if (client.getWidget(BA_DEF_ROLE_TEXT) == null) - { - remove(new String[]{"take", "light"}, target); - } - else //remove "Light" option (and "Take" option if not defender). - { - remove("light", target); - } - } - else if (option.equals("use")) - { - if (config.removeHealWrongFood()) - { - Widget healer = client.getWidget(BA_HEAL_LISTEN_TEXT); - if (healer != null) - { - String item = target.split("-")[0].trim(); - List poison = Arrays.asList("poisoned tofu", "poisoned meat", "poisoned worms"); - List vials = Arrays.asList("healing vial", "healing vial(1)", "healing vial(2)", "healing vial(3)", "healing vial(4)");//"healing vial(4)" - if (poison.contains(item)) - { - //if item is a poison item - int calledPoison = 0; - switch (healer.getText())//choose which poison to hide the use/destroy option for - { - case "Pois. Tofu": - calledPoison = ItemID.POISONED_TOFU; - break; - case "Pois. Meat": - calledPoison = ItemID.POISONED_MEAT; - break; - case "Pois. Worms": - calledPoison = ItemID.POISONED_WORMS; - break; - } - if (target.equals(item))//if targeting the item itself - { - if (calledPoison != 0 && itemId != calledPoison)//if no call or chosen item is not the called one - { - remove(new String[]{"use", "destroy", "examine"}, target);//remove options - } - } - else if (!target.contains("penance healer")) - { - remove(option, target); - } - } - else if (vials.contains(item))//if item is the healer's healing vial - { - - if (!target.equals(item))//if target is not the vial itself - { - - if (!target.contains("level") || target.contains("penance") || target.contains("queen spawn"))//if someone has "penance" or "queen spawn" in their name, gg... - { - remove(option, target); - } - } - } - } - } - } - else if (option.equals("attack") && client.getWidget(BA_ATK_ROLE_TEXT) == null && !target.equals("queen spawn"))//if not attacker - { - //remove attack option from everything but queen spawns - remove(option, target); - } - else if ((option.equals("fix")) && client.getWidget(WidgetInfo.BA_DEF_ROLE_TEXT) == null)//if not defender - { - remove(option, target); - } - else if ((option.equals("block") && target.equals("penance cave") && config.removePenanceCave())) - { - remove(option, target); - } - - else if ((option.equals("load")) && client.getWidget(BA_COLL_ROLE_TEXT) == null)//if not collector, remove hopper options - { - remove(new String[]{option, "look-in"}, target); - } - else if (config.removeWrongEggs() && option.equals("take")) - { - Widget eggToColl = client.getWidget(BA_COLL_LISTEN_TEXT); - if (eggToColl != null)//if we're a collector - { - List eggsToHide = new ArrayList<>(); - eggsToHide.add(ItemID.HAMMER); - switch (eggToColl.getText())//choose which eggs to hide take option for - { - case "Red eggs": - eggsToHide.add(ItemID.BLUE_EGG); - eggsToHide.add(ItemID.GREEN_EGG); - break; - case "Blue eggs": - eggsToHide.add(ItemID.RED_EGG); - eggsToHide.add(ItemID.GREEN_EGG); - break; - case "Green eggs": - eggsToHide.add(ItemID.RED_EGG); - eggsToHide.add(ItemID.BLUE_EGG); - break; - } - if (eggsToHide.contains(itemId)) - { - remove(option, target);//hide wrong eggs - } - } - else - { - List defenderItems = Arrays.asList(ItemID.HAMMER, ItemID.TOFU, ItemID.CRACKERS, ItemID.WORMS);//logs are handled separately due to hiding "light" option too. - if (client.getWidget(BA_DEF_ROLE_TEXT) == null || !defenderItems.contains(itemId))//if not defender, or item is not a defenderItem - { - remove(option, target);//hide everything except hammer/logs and bait if Defender - } - } - } - } - - - if (client.getWidget(WidgetInfo.BA_HEAL_CALL_TEXT) == getWidget() && lastHealer != 0 && inGame && config.ctrlHealer() && ctrlDown) - { - MenuEntry[] menuEntries = client.getMenuEntries(); - MenuEntry correctHealer = null; - entries.clear(); - - for (MenuEntry entry : menuEntries) - { - - if (( entry.getIdentifier() == lastHealer && entry.getOption().equals("Use")) - || - ( - (entry.getTarget().equals("Poisoned meat") || entry.getTarget().equals("Poisoned worms") || entry.getTarget().equals("Poisoned tofu")) - && - entry.getOption().equals("Use") - ) - ) - { - correctHealer = entry; - } - else - { - log.info((entry.getIdentifier() == lastHealer && entry.getOption().equals("Use")) + " " + ((entry.getTarget().equals("Poisoned meat") || entry.getTarget().equals("Poisoned worms") || entry.getTarget().equals("Poisoned tofu")) && entry.getOption().equals("Use")) ); - } - } - if (correctHealer != null) - { - entries.add(correctHealer); - } - client.setMenuEntries(entries.toArray(new MenuEntry[entries.size()])); - } - - - if ((event.getTarget().contains("Penance Healer") || event.getTarget().contains("Penance Fighter") || event.getTarget().contains("Penance Ranger"))) - { - - MenuEntry[] menuEntries = client.getMenuEntries(); - MenuEntry lastEntry = menuEntries[menuEntries.length - 1]; - String targett = lastEntry.getTarget(); - - if (foodPressed.containsKey(lastEntry.getIdentifier())) - { - lastEntry.setTarget(lastEntry.getTarget().split("\\(")[0] + "(" + Duration.between(foodPressed.get(lastEntry.getIdentifier()), Instant.now()).getSeconds() + ")"); - if (Duration.between(foodPressed.get(lastEntry.getIdentifier()), Instant.now()).getSeconds() > 20) - { - lastEntry.setTarget(lastEntry.getTarget().replace("", "")); - } - } - else - { - lastEntry.setTarget(targett.replace("", "")); - - } - - client.setMenuEntries(menuEntries); - } - - if (client.getWidget(BA_COLL_LISTEN_TEXT) != null && inGame && config.eggBoi() && event.getTarget().endsWith("egg") && shiftDown) - { - String[] currentCall = client.getWidget(BA_COLL_LISTEN_TEXT).getText().split(" "); - - MenuEntry[] menuEntries = client.getMenuEntries(); - MenuEntry correctEgg = null; - entries.clear(); - - for (MenuEntry entry : menuEntries) - { - if (entry.getTarget().contains(currentCall[0]) && entry.getOption().equals("Take")) - { - correctEgg = entry; - } - else if (!entry.getOption().startsWith("Take")) - { - entries.add(entry); - } - } - if (correctEgg != null) - { - entries.add(correctEgg); - } - client.setMenuEntries(entries.toArray(new MenuEntry[0])); - } - - if (client.getWidget(WidgetInfo.BA_ATK_LISTEN_TEXT) != null && inGame && config.attackStyles() && shiftDown) - { - MenuEntry[] menuEntries = client.getMenuEntries(); - MenuEntry correctEgg = null; - entries.clear(); - - for (MenuEntry entry : menuEntries) - { - if (entry.getOption().contains("Walk here")) - { - entries.add(entry); - } - } - client.setMenuEntries(entries.toArray(new MenuEntry[entries.size()])); - } - - if (client.getWidget(BA_HEAL_LISTEN_TEXT) != null && inGame && config.osHelp() && event.getTarget().equals("Healer item machine") && shiftDown) - { - String[] currentCall = client.getWidget(BA_HEAL_LISTEN_TEXT).getText().split(" "); - - if (!currentCall[0].contains("Pois.")) - { - return; - } - - MenuEntry[] menuEntries = client.getMenuEntries(); - MenuEntry correctEgg = null; - entries.clear(); - - for (MenuEntry entry : menuEntries) - { - if (entry.getOption().equals("Take-" + currentCall[1])) - { - correctEgg = entry; - } - } - if (correctEgg != null) - { - entries.add(correctEgg); - client.setMenuEntries(entries.toArray(new MenuEntry[0])); - } - } - - } - - @Subscribe - public void onMenuOptionClicked(MenuOptionClicked event) - { - String target = event.getTarget(); - - if (config.tagging() && (event.getTarget().contains("Penance Ranger") || event.getTarget().contains("Penance Fighter"))) - { - if (event.getOption().contains("Attack")) - { - foodPressed.put(event.getIdentifier(), Instant.now()); - } - log.info(target); - } - - if (config.healerMenuOption() && target.contains("Penance Healer") && target.contains("Poisoned") && target.contains("->")) - { - foodPressed.put(event.getIdentifier(), Instant.now()); - lastHealer = event.getIdentifier(); - log.info("Last healer changed: " + lastHealer); - } - } - - - public void onConfigChanged(ConfigChanged event) - { - if (counter != null && !config.defTimer()) - { - removeCounter(); - } - } - - private void addCounter() - { - if (!config.defTimer() || counter != null) - { - return; - } - - int itemSpriteId = ItemID.FIGHTER_TORSO; - - BufferedImage taskImg = itemManager.getImage(itemSpriteId); - counter = new CycleCounter(taskImg, this, tickNum); - - infoBoxManager.addInfoBox(counter); - } - - private void removeCounter() - { - if (counter == null) - { - return; - } - - infoBoxManager.removeInfoBox(counter); - counter = null; - } - - private void remove(String option, String target) - { - MenuEntry[] entries = client.getMenuEntries(); - int idx = searchIndex(entries, option, target); - if (idx >= 0 && entries[idx] != null) - { - entries = ArrayUtils.removeElement(entries, entries[idx]); - client.setMenuEntries(entries); - } - } - - private void remove(String[] options, String target) - { - MenuEntry[] entries = client.getMenuEntries(); - for (String option : options) - { - int idx = searchIndex(entries, option, target); - if (idx >= 0 && entries[idx] != null) - { - entries = ArrayUtils.removeElement(entries, entries[idx]); - } - } - - client.setMenuEntries(entries); - } - - private int searchIndex(MenuEntry[] entries, String option, String target) - { - for (int i = entries.length - 1; i >= 0; i--) - { - MenuEntry entry = entries[i]; - String entryOption = Text.removeTags(entry.getOption()).toLowerCase(); - String entryTarget = Text.removeTags(entry.getTarget()).toLowerCase(); - - if (entryOption.equals(option) && entryTarget.equals(target)) - { - return i; - } - } - - return -1; - } - - private boolean checkNewSpawn(NPC npc) - { - for (WorldPoint p : WorldPoint.toLocalInstance(client, healerSpawnPoint)) - { - if (p.distanceTo(npc.getWorldLocation()) < 5) - { - return true; - } - } - return false; - } - - @Override - public void keyTyped(KeyEvent e) - { - } - - @Override - public void keyPressed(KeyEvent e) - { - if (e.getKeyCode() == KeyEvent.VK_SHIFT) - { - shiftDown = true; - } - if (e.getKeyCode() == KeyEvent.VK_CONTROL) - { - ctrlDown = true; - } - } - - @Override - public void keyReleased(KeyEvent e) - { - if (e.getKeyCode() == KeyEvent.VK_SHIFT) - { - shiftDown = false; - } - if (e.getKeyCode() == KeyEvent.VK_CONTROL) - { - ctrlDown = false; - } - } - - private boolean isAnyPrayerActive() - { - for (Prayer pray : Prayer.values())//Check if any prayers are active - { - if (client.isPrayerActive(pray)) - { - return true; - } - } - - return false; - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/batools/Calls.java b/runelite-client/src/main/java/net/runelite/client/plugins/batools/Calls.java deleted file mode 100644 index 0fb721d7a2..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/batools/Calls.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2018, https://runelitepl.us - * 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.batools; - -import java.util.HashMap; -import java.util.Map; -import lombok.Getter; - -@Getter -public enum Calls -{ - //Attacker Calls - RED_EGG("Red egg", "Tell-red"), - GREEN_EGG("Green egg", "Tell-green"), - BLUE_EGG("Blue egg", "Tell-blue"), - //Collector Calls - CONTROLLED("Controlled/Bullet/Wind", "Tell-controlled"), - ACCURATE("Accurate/Field/Water", "Tell-accurate"), - AGGRESSIVE("Aggressive/Blunt/Earth", "Tell-aggressive"), - DEFENSIVE("Defensive/Barbed/Fire", "Tell-defensive"), - //Healer Calls - TOFU("Tofu", "Tell-tofu"), - CRACKERS("Crackers", "Tell-crackers"), - WORMS("Worms", "Tell-worms"), - //Defender Calls - POIS_WORMS("Pois. Worms", "Tell-worms"), - POIS_TOFU("Pois. Tofu", "Tell-tofu"), - POIS_MEAT("Pois. Meat", "Tell-meat"); - - private final String call; - private final String option; - - private static final Map CALL_MENU = new HashMap<>(); - - static - { - for (Calls s : values()) - { - CALL_MENU.put(s.getCall(), s.getOption()); - } - } - - Calls(String call, String option) - { - this.call = call; - this.option = option; - } - - public static String getOption(String call) - { - return CALL_MENU.get(call); - } - -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/batools/Healer.java b/runelite-client/src/main/java/net/runelite/client/plugins/batools/Healer.java deleted file mode 100644 index 93382e57c0..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/batools/Healer.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2018, https://runelitepl.us - * 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.batools; - - -import lombok.Getter; -import lombok.Setter; -import net.runelite.api.NPC; - - -class Healer -{ - - @Getter - private NPC npc; - - @Getter - @Setter - private int wave; - - @Getter - @Setter - private int spawnNumber; - - @Getter - @Setter - private int foodRemaining; - - @Getter - @Setter - private int lastFoodTime; - - @Getter - @Setter - private int firstCallFood; - - @Getter - @Setter - private int secondCallFood; - - Healer(NPC npc, int spawnNumber, int wave) - { - this.npc = npc; - this.wave = wave; - this.spawnNumber = spawnNumber; - this.firstCallFood = getCode(wave).getFirstCallFood()[spawnNumber]; - this.secondCallFood = getCode(wave).getSecondCallFood()[spawnNumber]; - this.foodRemaining = firstCallFood + secondCallFood; - this.lastFoodTime = getCode(wave).getSpacing()[spawnNumber]; - } - - private HealerCode getCode(int wave) - { - switch (wave) - { - case 1: - return HealerCode.WAVEONE; - case 2: - return HealerCode.WAVETWO; - case 3: - return HealerCode.WAVETHREE; - case 4: - return HealerCode.WAVEFOUR; - case 5: - return HealerCode.WAVEFIVE; - case 6: - return HealerCode.WAVESIX; - case 7: - return HealerCode.WAVESEVEN; - case 8: - return HealerCode.WAVEEIGHT; - case 9: - return HealerCode.WAVENINE; - case 10: - return HealerCode.WAVETEN; - default: - return null; - } - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/batools/HealerCode.java b/runelite-client/src/main/java/net/runelite/client/plugins/batools/HealerCode.java deleted file mode 100644 index a604239045..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/batools/HealerCode.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2018, https://runelitepl.us - * 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.batools; - -import lombok.Getter; - - -enum HealerCode -{ - - WAVEONE(new int[]{1, 1}, new int[]{0, 0}, new int[]{0, 0}), - WAVETWO(new int[]{1, 1, 2}, new int[]{0, 0, 0}, new int[]{0, 0, 21}), - WAVETHREE(new int[]{1, 6, 2}, new int[]{0, 0, 0}, new int[]{0, 0, 0}), - WAVEFOUR(new int[]{2, 5, 2, 0}, new int[]{0, 0, 7, 10}, new int[]{0, 0, 0, 0}), - WAVEFIVE(new int[]{2, 5, 2, 3, 0}, new int[]{0, 0, 0, 0, 7}, new int[]{0, 0, 21, 30, 0}), - WAVESIX(new int[]{3, 5, 2, 2, 0, 0}, new int[]{0, 0, 0, 2, 9, 10}, new int[]{12, 18, 21, 0, 0, 0}), - WAVESEVEN(new int[]{3, 7, 1, 1, 0, 0, 0}, new int[]{2, 0, 1, 1, 2, 4, 10}, new int[]{0, 21, 0, 0, 30, 45, 0}), - WAVEEIGHT(new int[]{1, 9, 1, 1, 0, 0, 0}, new int[]{1, 0, 1, 1, 2, 2, 10}, new int[]{0, 0, 0, 0, 33, 42, 0}), - WAVENINE(new int[]{2, 8, 1, 1, 0, 0, 0, 0}, new int[]{1, 0, 1, 1, 2, 1, 1, 10}, new int[]{0, 21, 0, 0, 0, 0, 0, 0, 0}), - WAVETEN(new int[]{2, 5, 1, 1, 0, 0, 0}, new int[]{1, 0, 1, 1, 4, 4, 8}, new int[]{21, 33, 0, 33, 30, 45, 0}); - - - @Getter - private final int[] firstCallFood; - @Getter - private final int[] secondCallFood; - @Getter - private final int[] spacing; - - HealerCode(int[] firstCallFood, int[] secondCallFood, int[] spacing) - { - this.firstCallFood = firstCallFood; - this.secondCallFood = secondCallFood; - this.spacing = spacing; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlPlugin.java index 272ec5d6ab..d3095afd5a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/pestcontrol/PestControlPlugin.java @@ -396,7 +396,7 @@ public class PestControlPlugin extends Plugin } // Get points from dialog after purchase - Widget pestControlDialog = client.getWidget(WidgetInfo.PEST_CONTROL_DIALOG_TEXT); + Widget pestControlDialog = client.getWidget(WidgetInfo.MINIGAME_DIALOG_TEXT); if (pestControlDialog != null) { String pestControlDialogText = Text.sanitizeMultilineText(pestControlDialog.getText()); diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/barbarianassault/fighter.png b/runelite-client/src/main/resources/net/runelite/client/plugins/barbarianassault/fighter.png new file mode 100644 index 0000000000000000000000000000000000000000..dc3f909ee1c657a1eb699d8bc17860bbdfee1910 GIT binary patch literal 2033 zcmZ{lYdF)58^?c6+Z-b2jcQJ*g~t}vnw8UPD68g>L*+1;Lu`(dLrH{;9;Y5Ma!mN9AG=pD2fe%lqc; zKUy(-w8GgJPFHtz!d1D`o4xpn4=Q<{Zq2zR8kz{j@@}mQVWpm-IR6%O5uaKbBZ(BV zSZZDo#C{dI$s0Ci;hRr*0-Kz7N<6n`Qz{XPvk-yU+hKWEZ5+Jzwf>J5?Wry_KYn2r zU6#E5z|2`0^{N95|0RQ++3G+w(wk*aRAhf4gl9_5U$i+$iz#~~u_BA|EWFV06iH)L z!pd?{^!EowXfly1G6Z4!4o)HDHv?PWa^Jxi&C`sbLgL|bOwT22_D$i?_UF57C11l& zXH)7Vd#?A4xFF_qxIN&GmxC~Jqm9^(J=6WM(BK;+?XDZ>TRfsiTr`r|*4D<>ouVGv z*q9IueE7@Fek{ymWkpo?geTg|Hb_tHk8Wp$gNt^eI}MKQq;j|R)pT?m?u6wT* zr;rZ6I2#aHSd<-dvCJ&UTa~$w(Y(yW^zWBB7MO}kMNzrx*9T0St@tg{^auYS9w&{}^M^*b{RH@u?596$P&x$8igNd5VU^6Q=-NLUy<&@zBJ0k%G z)HXlnIUu&E+d0gSYc@BU6T+{x*Ae2`Hs@Xl;icKut5D|lFFesUTMx5xZG11Rx536X zNq2{2&QT_?N$3q)I@C`7)4}n2(b*c7V)*(FIx441%h(>;8Il`4_WV=-;p4~YGn`vxYq*?Ehq;AJiJX=1 zXc&G-rBfCD&#JqAlVs(hI6vC?y~ua0G38Jv0kKmdfWwG_XW*->Fqy5NEdwG$b=#DC z-(?pDy|GSgx@E5w<&A%wdsyczcT& z&KYguV|>moQ|{iQ1>T$J$p3!zBr6p*c{SPpr%r?aMrz6rir7@)jK=UK5=?0NR(OJe zs)zslXG7ya-{_8wxv67GR%#~+3vYox>{ z;l#1xkwdTU;|szT8RB2A2P{(rca2ZB!@X~QiLlngt9`LWr^u6D#Bk@UhfFBrj%+9= ziJEFb%WL6**fmx+$0%lV0AVGAr#O3&``+ZMo6fYxomVM0o=Pa*&wjlZ(3ZY`PW5w` z1rid1n@FuA{Yt3{x|a#I)$V%+0czw#am(9i^MP${n?@lCksU2$1CXrZQp)^OFsb&2 z^2$_OVBZQqYTO?l0+E|F{a_SR9hXgcml)tEJw~kW1 zJQ2Gq19;OT!}AF-9lZaTgCF>;aBg?{9bZYqT%$m{yOAd6To7W|Ginc=UEU2lKf$YO z)g|>3Znz-6ShBPmiN0UgkV>AqIm6BP+DWQzZF4>`v3T|HV_uxM@Pk|jhl5Nn@=ddg zw798MfMS^sj8)1FY8!q~`B6dfRFfn5A=!e-Fw^Hy&4LPb;HT(zrho1rBc=i;&wSCI zk#nuu{W`*x4{Vi}-8BXEQ}ickkgLoGi}yvE(&U^|e`!()YJB_J~((P(bW? z3@Q4&)PTK6)Ikg6ehcJ5cO)9K7m3-o*8*vcK_a)7!9xEBh@b?824DRDKtNadD=7dt MA0goC9Q@P%1FGA!p8x;= literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/barbarianassault/healer.png b/runelite-client/src/main/resources/net/runelite/client/plugins/barbarianassault/healer.png new file mode 100644 index 0000000000000000000000000000000000000000..1938a5abdc384ca80cae24b35da298afaebb4cdf GIT binary patch literal 1575 zcmZ{ke>~H99LGOn%nVsZepM@BV)<@<#8Ub3#aJAfGt&9FHD(XXrY=ACK*f}Dob#&~ zrXLTklOG)}6(K)6R6^yD$jYf4m)mmd&->#Z@5k%?d^}#S$Lrr$j-Rimrn;dz06^1= z3LjMv|5a+LiqB6KQA#w&00cTxnZku*;jY<|7UyVnX74w^>|<^zDy`6>h`E!S6=D1n!cJ8Di9t)^@7 zl<_SK02Mti*oDsMYW^vh9m>S@ZZ5Q+UbeUKV*S91DYI?hMk*5-#L2cN-j;hM-V#?A z9PF~wiY?hq6#bELs7ZGtvSBs@Yq69IBP~|l;W`mbLK|(m=tG`hRkWur{Eg3uzmn6p zF4(EHAnTa^>~NmOUUB;HB+B8W%v9n@P7dqGX&i5}9Lk+mC*IP$A{#hlF=LIG>R`Gldqni`?JF&D#|2&P*P^rpJvzJ!a~uyN5{~XBe&Z& zRWq}{?*mtp*SgaWFCdK5WrRxxREZ8E?Xyg;GSRo z25rmA2hUrsNbP)X$fw%aZcpv3YxeQ4{It5*ZxI?@Z~%X=xG)I@WhL(Uh6D3&P7d^F z(lES_ud#ayl}IgSNBHN(xVV8T`skFKYstjKghYjEgeWy$f^@f&3-j$B38l}eNyEcU z9H;l1aSuAocQOoxGvdtb{xbpY&GLP*ryX&fcDAE=u4XZW-ooDM#_2WEz$kdwa1NAZ zKuKgRWwJn+g@hc1UWOvJv+(>e8;f@@Qx|5HQVt5#N>!}%HL4wSI39Hd5jEH>QYWN} z$qe2>NlyC>eSO+BH8Wc{5|(yfWT(uj^%zzT*Z18z$;00Fz;PauGE3TX2m>La+*oJE ziYz}MHeofgBFt)+y6qeDS}2`~Yo%?&4{BrCL&Vb7<9xYM>pn1a;c(jLhxJAM1APhL zfTl^ahA$1(^Z24m5ax3FtW*6NmHogk2vckj5Ok|HYp$g)$B8ggjU#VG>x{LMH*T8V z=RX+qhPP^mgaQN}Z?9=OK76UV_TV5#3Ac-&>PN}ixW)>!F!YrFCFLLxkLnidYmSxj zTM+tn`4#(ipJwU>(;nEP&xtDw`;EMP3^e1q+cemENbw@Q*6!V6f0g;z*(VYM+(+G} zGE)XI*jXo=V49)j?4kEm$Som+erdFR`K;sT`fBu*fyU?G55(5J@2hM!LC@P|s!@wQ za+ziAqZZqePjQ2dmAl+fE--!u^**|-J&|edG$p(e>mGAzE3X|fb~Xw{P12I#C&`Xi zzA-z}c$)zHYj$ZL!@l%^sfm9@Zp;Q}=@bZk*vogZab|dJ94!#y@3A_Pc<7<@i$T2+ zySKToq)_Vc4S2$RzN0SAe6W_un`*1HGCEn~Z#53>59>)0mzP-%rUu@mi=^X;GqaMC z%@g6PA06Odr(5b?i-u1DBv!QmZB`ljA>W$rM+o$XCHstIXQjlfY}CbB7gxn2?tv_TLI9Rb9mSP(04P?9^(uEMF)VSQ~1YA@mAf$qV14uIN54l2PWC z9->w4m)U5)Z6l+g?$s}S>Yj$WZQ0)eC!XN7)XORzGM$~2t2QWLlVmWaZI?75b<70JI}rP;vG71qNQbl`;$(xs-Ooie6E|f z5;utU!lh8thpPsgSue>l4JD-Yd;8P-u~Ns*msdS{WVf>&YhqMf_l^QRFclNrjhee` z2d1-rs`oabnh%cBDtF5;lzha;!&w(gIgb6pT#ZlJyp*)bjD)rr6P2t#YMEJ$jl{&y zi{4>_3}tVIFmMXFRf}`4FQ`>E)nF^+B79W$XU3&# z*W;FVf6JWt(OH2UL8p!wc2Y8jf@Fk+=&MzuoDG}`+kE2>Skp1ZAhKO0=0&BVJ3BaQ zZ@`4LPHJ8*3`UqyYV}cL__{FN%F)m|Z|m+3tC`+9?>kCI+t1r<+R!kNCpk#2&M%L{ z>eNQPe4mbF$Hc^h)s9v)(H2WaT4M9w!fqXteg%`!u<**^#PHVE`mU+>5X`toN5!{2gV) z9+YdoW}Y2dCt)|>?il9VSRV7^eXYHq6FVRz@H8?Q&Dr#taGn-w9^k=%05mHH(bABZ@MLGqgcnpz6evkK!Uszuy?ET&j;YDQ| zLx&FG`?Ll1aN-TQ#)?2vh>j4krU4-cEdAiB?^m3Uz$F)+J7)IkkH(I7} z2<8{-R$r^=&E6l=uHt?*{yl@y_2o{Ards6- zSX-}X0szm$$=jNFx|7_U>8Z|tsmaNfE2cujvKoW2>DKidrp5D*&nb=xh@>Ub&2!)@ zY_+uZczy-pgsP1)BjBC4i6Yu4bBoe7lVV)eo|22DY6|62mNJiWO7iK5#vodZUsylW zRK`7M;U{;JmbSRD2`LKxh|RM1o$iZt;VWbF@m#SVV$$B$0zOM2mwqpxL;)V%YE zaaXWaM;PV@P|mjZ`P?4+cGC^NI-DUle7u&aCUlx2E0u5)bdI0GESN}?E%n@LH0u{u zutlmnGTF4y;QNXOti=A4v0v9F_|Kp6Z+6KQ!$AUi0=uPkMi4JJJQx8^16Ry zkXhiZAU=UJa0F5hepU~T#KKL@&LH@!2RAZ004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 zVoOIv0RI600RN!9r;`8x010qNS#tmY3ljhU3ljkVnw%H_000McNliru;|2)`6*^Jo z7q|cb1)E7kK~z}7wU%3~okbaj-xG!?2L)f*K{V%3n4(8NTLL?D$~@B$GFx_iKuLU;F}Ypwq@=NT^w zhLE`ST07Zq@?XuD_j~evZ)SdiMfAX3<7CYQkFU)c?76=L033PfE^wZ}7>A}+kjkK} zBzEsTgoEFl1Q0WpK!5d~UR=zdE>_UVlPjeP@7}UL#bZBPgU5gWZQOFx`w(N?7=axz zVtWX2lJNxJ+PxF++LR#Wbi1)+VlnTb00bvm2y%-L*ufH8gy7d5J^s?(BG3MmrThsY z*b_-Y9|?hca^Hc6$mDp^5uAKfo0tv*z!ocLRah7+$PvKrj&y26p2jPg5buvMf;oy* zF!(&XG5YelhwtC|s}ra6+S!Hh{69|0*ZL5IKo>DO@8=8pg?Zj>zFv;ktnZCO6G z1pth7(8fU<3u8S)*LokmE0w+eV>fKWk^vG*w@^xN zj)-v1!@hOw#v+6W!5NBNpwr191V@rwwsbz^btAvzec*$73=E-E<~@S5DaI0LW1+N# z4*`S_FqS|F2F@zr>Wr7M*-Xj+9BfuhL48AtF2 zgb+jkF#^^)v{FNBd-*f=!lho_EO`Z&det$h%!5_kx~i6_YKeZ;fM;v5{vFpN&BqZ! zfVB>n0X3vFP}-tt6{=cIzi{HLS@H@}!Bs2ucdAA$7-R3$+Kz`9accTy%+Hh%nF1vS z-bWbgpp-!)HL64@CdBvw&XS2V zr2uK5bql2|s#>C~T9ma!w^w7KTgh&(`CYeH|Kh@IuNu^2;63oB_U^eAF$UyW`q>pl zdMHc97$F44I<&1uS-0r*TaBb7NVr9LE;`OU?-zIyTHa|68>eG35K`}aKzM1qx_ z{12S7Lq3GqN{yybXq86YC|sQH#nb<}xUbu5K5@f`uKgt;M9t6l7xf-$0&nWT-M51# zY>T$xF=OmzYYeQhs2hdzGu^YV&h$Qh#j0Yul^TEk%Rh(mOopm@LytfCcWk@q=58U# zI|E6!8&9BIfYcsUYYv~E?H@k=?1}iwxuM=607JZnZ`Ug3W5CKeFbkFmBtC(O4kieG z?#ydpRKKAz@K%klUPq0?)lvgx*9Vt>v1MuEYh8WgKrO?#P(KJh+7lKDZNpr_nZeM-?qxE`c-IQ!4q?Au(X?mP- zPK+_1f9lDj`LY98Yqwf!JJwn#rPms3dz2CgF1F@HF}CagTAPm6hC1&NLg*N6&N%A~ zp=3e`KDq1w-urXTIqRJB0G`gW^btbIqmlSt=-5uAJ2jq@nr?5tNQA)sz1H3 z&^;}cx+lhX0RVoq@4#~hetdA(2e-fX)TjU>t)SoQztZcMUnu*fxoXqKlP6w$2>`I` z>tDugJHLp2S&rI&wEux!-?#&H-JtB3n44R`Ge`eAa>KXlzk2L5B7Dq>PXGV_C3Hnt zbYx+4WjbSWWnpw>05UK!IV~_YEigG$FgQ9gF*-FkEig1XFfdyzEg1j+03~!qSaf7z zbY(hiZ)9m^c>ppnF*z+THZ3qYR4_O?GBG+eI4v+VIxsLQah0V20000