From 8165975ba59fc34feda725bb00f638dc99262044 Mon Sep 17 00:00:00 2001 From: Devin Date: Thu, 2 Nov 2017 14:21:10 -0700 Subject: [PATCH 1/2] Add getWorldLocation mixin for Actor --- runelite-api/src/main/java/net/runelite/api/Actor.java | 2 ++ .../src/main/java/net/runelite/mixins/RSActorMixin.java | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/runelite-api/src/main/java/net/runelite/api/Actor.java b/runelite-api/src/main/java/net/runelite/api/Actor.java index 84dd389df7..301ffb97dc 100644 --- a/runelite-api/src/main/java/net/runelite/api/Actor.java +++ b/runelite-api/src/main/java/net/runelite/api/Actor.java @@ -40,6 +40,8 @@ public interface Actor extends Renderable int getHealth(); + Point getWorldLocation(); + Point getLocalLocation(); int getOrientation(); diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSActorMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSActorMixin.java index 364bd14941..361f91ff19 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSActorMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSActorMixin.java @@ -116,6 +116,14 @@ public abstract class RSActorMixin implements RSActor return -1; } + @Override + @Inject + public Point getWorldLocation() + { + Point localLocation = getLocalLocation(); + return Perspective.localToWorld(client, localLocation); + } + @Inject @Override public Point getLocalLocation() From 8dfc8c70b36fde093213e20b1f5c6007107f89dd Mon Sep 17 00:00:00 2001 From: Devin Date: Thu, 2 Nov 2017 21:02:29 -0700 Subject: [PATCH 2/2] Update Zulrah plugin --- .../client/plugins/zulrah/StatusOverlay.java | 115 ----- .../client/plugins/zulrah/TileOverlay.java | 126 ----- .../client/plugins/zulrah/Zulrah.java | 102 ++-- .../{ZulrahType.java => ZulrahConfig.java} | 24 +- .../client/plugins/zulrah/ZulrahInstance.java | 223 +++------ .../client/plugins/zulrah/ZulrahOverlay.java | 461 ++++++++++++++++++ .../zulrah/patterns/ZulrahPattern.java | 30 +- .../zulrah/patterns/ZulrahPatternA.java | 34 +- .../zulrah/patterns/ZulrahPatternB.java | 36 +- .../zulrah/patterns/ZulrahPatternC.java | 36 +- .../zulrah/patterns/ZulrahPatternD.java | 38 +- .../zulrah/{ => phase}/StandLocation.java | 7 +- .../{Fight.java => phase/ZulrahLocation.java} | 89 ++-- .../plugins/zulrah/phase/ZulrahPhase.java | 160 ++++++ .../ZulrahType.java} | 35 +- .../client/plugins/zulrah/zulrah_magic.png | Bin 0 -> 18929 bytes .../plugins/zulrah/zulrah_magic_small.png | Bin 0 -> 17552 bytes .../client/plugins/zulrah/zulrah_melee.png | Bin 0 -> 18838 bytes .../plugins/zulrah/zulrah_melee_small.png | Bin 0 -> 17530 bytes .../client/plugins/zulrah/zulrah_range.png | Bin 0 -> 18644 bytes .../plugins/zulrah/zulrah_range_small.png | Bin 0 -> 17476 bytes 21 files changed, 923 insertions(+), 593 deletions(-) delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/zulrah/StatusOverlay.java delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/zulrah/TileOverlay.java rename runelite-client/src/main/java/net/runelite/client/plugins/zulrah/{ZulrahType.java => ZulrahConfig.java} (74%) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahOverlay.java rename runelite-client/src/main/java/net/runelite/client/plugins/zulrah/{ => phase}/StandLocation.java (90%) rename runelite-client/src/main/java/net/runelite/client/plugins/zulrah/{Fight.java => phase/ZulrahLocation.java} (57%) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/zulrah/phase/ZulrahPhase.java rename runelite-client/src/main/java/net/runelite/client/plugins/zulrah/{ZulrahLocation.java => phase/ZulrahType.java} (62%) create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/zulrah/zulrah_magic.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/zulrah/zulrah_magic_small.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/zulrah/zulrah_melee.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/zulrah/zulrah_melee_small.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/zulrah/zulrah_range.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/zulrah/zulrah_range_small.png diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/StatusOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/StatusOverlay.java deleted file mode 100644 index 6160d714ea..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/StatusOverlay.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2017, Aria - * Copyright (c) 2017, Adam - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.runelite.client.plugins.zulrah; - -import com.google.common.collect.Ordering; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.FontMetrics; -import java.awt.Graphics2D; -import net.runelite.api.Client; -import net.runelite.api.GameState; -import net.runelite.client.RuneLite; -import net.runelite.client.plugins.zulrah.patterns.ZulrahPattern; -import net.runelite.client.ui.overlay.Overlay; -import net.runelite.client.ui.overlay.OverlayPosition; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -class StatusOverlay extends Overlay -{ - private static final Logger logger = LoggerFactory.getLogger(StatusOverlay.class); - - private final Zulrah plugin; - private final Client client = RuneLite.getClient(); - - StatusOverlay(Zulrah plugin) - { - super(OverlayPosition.TOP_RIGHT); - this.plugin = plugin; - } - - @Override - public Dimension render(Graphics2D graphics) - { - Fight fight = plugin.getFight(); - - if (client.getGameState() != GameState.LOGGED_IN || fight == null) - { - return null; - } - - //TODO: Add prayer checking and health warning - graphics.setColor(Color.WHITE); - - ZulrahPattern pattern = fight.getPattern(); - if (pattern == null) - { - // can draw at least the starting place here? - return null; - } - - // Show current type, next type, and jad - ZulrahInstance current = fight.getZulrah(); - ZulrahInstance next = pattern.get(fight.getStage() + 1); - - String currentStr = "Current: " + current.getType(); - String nextStr = "Next: " + (next != null ? next.getType() : "Restart"); - String jadStr; - - if (current.isJad()) - { - jadStr = "JAD: YES"; - } - else if (next != null && next.isJad()) - { - jadStr = "JAD: NEXT"; - } - else - { - jadStr = "JAD: NO"; - } - - FontMetrics metrics = graphics.getFontMetrics(); - int height = metrics.getHeight(); - int width = Ordering.natural().max( - metrics.stringWidth(currentStr), - metrics.stringWidth(nextStr), - metrics.stringWidth(jadStr) - ); - - graphics.drawString(currentStr, 0, height); - height += metrics.getHeight(); - - graphics.drawString(nextStr, 0, height); - height += metrics.getHeight(); - - graphics.drawString(jadStr, 0, height); - height += metrics.getHeight(); - - return new Dimension(width, height); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/TileOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/TileOverlay.java deleted file mode 100644 index 375da64e4b..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/TileOverlay.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2017, Aria - * Copyright (c) 2017, Adam - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.runelite.client.plugins.zulrah; - -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics2D; -import java.awt.Polygon; -import net.runelite.api.Client; -import net.runelite.api.GameState; -import net.runelite.api.Perspective; -import net.runelite.api.Point; -import net.runelite.client.RuneLite; -import net.runelite.client.plugins.zulrah.patterns.ZulrahPattern; -import net.runelite.client.ui.overlay.Overlay; -import net.runelite.client.ui.overlay.OverlayPosition; -import net.runelite.client.ui.overlay.OverlayUtil; - -public class TileOverlay extends Overlay -{ - private final Zulrah plugin; - private final Client client = RuneLite.getClient(); - - public TileOverlay(Zulrah plugin) - { - super(OverlayPosition.DYNAMIC); - this.plugin = plugin; - } - - @Override - public Dimension render(Graphics2D graphics) - { - Fight fight = plugin.getFight(); - - if (client.getGameState() != GameState.LOGGED_IN || fight == null) - { - return null; - } - - ZulrahPattern pattern = fight.getPattern(); - if (pattern == null) - { - return null; - } - - Point startLocationWorld = fight.getStartLocationWorld(); - int stage = fight.getStage(); - - ZulrahInstance current = pattern.get(stage); - if (current == null) - { - return null; - } - - renderTileOverlay(graphics, current.getStandLoc(startLocationWorld), Color.GREEN); - - ZulrahInstance next = pattern.get(stage + 1); - if (next == null) - { - return null; - } - - String str; - - if (next.isJad()) - { - str = "Next is JAD: " + next.getType(); - } - else - { - str = "Next: " + next.getType(); - } - - Point location = next.getZulrahLoc(startLocationWorld); - location = Perspective.worldToLocal(client, location); - location = Perspective.getCanvasTextLocation(client, graphics, location, str, 0); - - if (location != null) - { - graphics.setColor(Color.WHITE); - graphics.drawString(str, location.getX(), location.getY()); - } - - renderTileOverlay(graphics, next.getStandLoc(startLocationWorld), new Color(255, 0, 0, 150)); - - return null; - } - - private void renderTileOverlay(Graphics2D graphics, Point tile, Color outlineColor) - { - Point localTile = Perspective.worldToLocal(client, tile); - - //to make the centre of the tile on the point, rather than the tile the point resides in - localTile = new Point(localTile.getX() + Perspective.LOCAL_TILE_SIZE / 2, localTile.getY() + Perspective.LOCAL_TILE_SIZE / 2); - - Polygon poly = Perspective.getCanvasTilePoly(client, localTile); - if (poly != null) - { - OverlayUtil.renderPolygon(graphics, poly, outlineColor); - } - } - -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/Zulrah.java b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/Zulrah.java index 0c566f27f0..f2ebe9257c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/Zulrah.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/Zulrah.java @@ -1,6 +1,7 @@ /* * Copyright (c) 2017, Aria * Copyright (c) 2017, Adam + * Copyright (c) 2017, Devin French * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,14 +26,9 @@ */ package net.runelite.client.plugins.zulrah; -import java.time.temporal.ChronoUnit; -import java.util.Arrays; -import java.util.Collection; import net.runelite.api.Client; import net.runelite.api.GameState; import net.runelite.api.NPC; -import net.runelite.api.Perspective; -import net.runelite.api.Point; import net.runelite.api.Query; import net.runelite.api.queries.NPCQuery; import net.runelite.client.RuneLite; @@ -43,11 +39,14 @@ import net.runelite.client.plugins.zulrah.patterns.ZulrahPatternA; import net.runelite.client.plugins.zulrah.patterns.ZulrahPatternB; import net.runelite.client.plugins.zulrah.patterns.ZulrahPatternC; import net.runelite.client.plugins.zulrah.patterns.ZulrahPatternD; +import net.runelite.client.plugins.zulrah.phase.ZulrahPhase; import net.runelite.client.task.Schedule; import net.runelite.client.ui.overlay.Overlay; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.time.temporal.ChronoUnit; + @PluginDescriptor( name = "Zulrah plugin" ) @@ -56,35 +55,34 @@ public class Zulrah extends Plugin private static final Logger logger = LoggerFactory.getLogger(Zulrah.class); private final RuneLite runelite = RuneLite.getRunelite(); + private final ZulrahConfig config = RuneLite.getRunelite().getConfigManager().getConfig(ZulrahConfig.class); private final Client client = RuneLite.getClient(); - - private final StatusOverlay overlay = new StatusOverlay(this); - private final TileOverlay tileOverlay = new TileOverlay(this); - - private final ZulrahPattern[] patterns = new ZulrahPattern[] - { - new ZulrahPatternA(), - new ZulrahPatternB(), - new ZulrahPatternC(), - new ZulrahPatternD() + private final ZulrahOverlay overlay = new ZulrahOverlay(this); + private final ZulrahPattern[] patterns = new ZulrahPattern[]{ + new ZulrahPatternA(), + new ZulrahPatternB(), + new ZulrahPatternC(), + new ZulrahPatternD() }; - private Fight fight; - - @Override - public Collection getOverlays() - { - return Arrays.asList(overlay, tileOverlay); - } + private ZulrahInstance instance; @Override protected void startUp() throws Exception { + } @Override protected void shutDown() throws Exception { + + } + + @Override + public Overlay getOverlay() + { + return overlay; } @Schedule( @@ -93,51 +91,43 @@ public class Zulrah extends Plugin ) public void update() { - if (client == null || client.getGameState() != GameState.LOGGED_IN) + if (!config.enabled() || client == null || client.getGameState() != GameState.LOGGED_IN) { return; } NPC zulrah = findZulrah(); - if (zulrah == null) { - if (fight != null) + if (instance != null) { - logger.debug("Fight has ended!"); - - fight = null; + logger.debug("Zulrah encounter has ended."); + instance = null; } return; } - if (fight == null) + if (instance == null) { - Point startTile = zulrah.getLocalLocation(); - startTile = Perspective.localToWorld(client, startTile); - - fight = new Fight(startTile); - - logger.debug("Fight has begun!"); + instance = new ZulrahInstance(zulrah); + logger.debug("Zulrah encounter has started."); } - ZulrahInstance currentZulrah = ZulrahInstance.of(zulrah, fight.getStartLocationWorld()); - - if (fight.getZulrah() == null) + ZulrahPhase currentPhase = ZulrahPhase.valueOf(zulrah, instance.getStartLocation()); + if (instance.getPhase() == null) { - fight.setZulrah(currentZulrah); + instance.setPhase(currentPhase); } - else if (!fight.getZulrah().equals(currentZulrah)) + else if (!instance.getPhase().equals(currentPhase)) { - ZulrahInstance previousInstance = fight.getZulrah(); - fight.setZulrah(currentZulrah); - fight.nextStage(); + ZulrahPhase previousPhase = instance.getPhase(); + instance.setPhase(currentPhase); + instance.nextStage(); - logger.debug("Zulrah has moved from {} -> {}, index now {}", - previousInstance, currentZulrah, fight.getStage()); + logger.debug("Zulrah phase has moved from {} -> {}, stage: {}", previousPhase, currentPhase, instance.getStage()); } - ZulrahPattern pattern = fight.getPattern(); + ZulrahPattern pattern = instance.getPattern(); if (pattern == null) { int potential = 0; @@ -145,7 +135,7 @@ public class Zulrah extends Plugin for (ZulrahPattern p : patterns) { - if (p.stageMatches(fight.getStage(), fight.getZulrah())) + if (p.stageMatches(instance.getStage(), instance.getPhase())) { potential++; potentialPattern = p; @@ -156,20 +146,14 @@ public class Zulrah extends Plugin { logger.debug("Zulrah pattern identified: {}", potentialPattern); - fight.setPattern(potentialPattern); + instance.setPattern(potentialPattern); } } - else + else if (pattern.canReset(instance.getStage()) && (instance.getPhase() == null || instance.getPhase().equals(pattern.get(0)))) { - if (pattern.canReset(fight.getStage())) - { - if (fight.getZulrah().equals(pattern.get(0))) - { - logger.debug("Fight has reset"); + logger.debug("Zulrah pattern has reset."); - fight.reset(); - } - } + instance.reset(); } } @@ -180,8 +164,8 @@ public class Zulrah extends Plugin return result.length == 1 ? result[0] : null; } - public Fight getFight() + public ZulrahInstance getInstance() { - return fight; + return instance; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahType.java b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahConfig.java similarity index 74% rename from runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahType.java rename to runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahConfig.java index ccd03afdfd..b78ab2bb25 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahType.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Aria + * Copyright (c) 2017, Devin French * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,9 +24,23 @@ */ package net.runelite.client.plugins.zulrah; -public enum ZulrahType +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup( + keyName = "zulrah", + name = "Zulrah", + description = "Configuration for the zulrah plugin" +) +public interface ZulrahConfig { - RANGE, - MAGIC, - MELEE + @ConfigItem( + keyName = "enabled", + name = "Enabled", + description = "Configures whether or not zulrah overlays are displayed" + ) + default boolean enabled() + { + return true; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahInstance.java b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahInstance.java index 892eca0962..49905dd71d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahInstance.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahInstance.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, Aria + * Copyright (c) 2017, Devin French * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,196 +25,114 @@ */ package net.runelite.client.plugins.zulrah; -import java.util.Objects; import net.runelite.api.NPC; -import net.runelite.api.NpcID; -import net.runelite.api.Perspective; import net.runelite.api.Point; -import net.runelite.client.RuneLite; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import net.runelite.api.Prayer; +import net.runelite.client.plugins.zulrah.patterns.ZulrahPattern; +import net.runelite.client.plugins.zulrah.phase.StandLocation; +import net.runelite.client.plugins.zulrah.phase.ZulrahLocation; +import net.runelite.client.plugins.zulrah.phase.ZulrahPhase; +import net.runelite.client.plugins.zulrah.phase.ZulrahType; -/* - Original code: https://github.com/LoveLeAnon/OSLoader - */ public class ZulrahInstance { - private static final Logger logger = LoggerFactory.getLogger(ZulrahInstance.class); + private static final ZulrahPhase NO_PATTERN_MAGIC_PHASE = new ZulrahPhase( + ZulrahLocation.NORTH, + ZulrahType.MAGIC, + false, + StandLocation.PILLAR_WEST_OUTSIDE, + Prayer.PROTECT_FROM_MAGIC + ); + private static final ZulrahPhase NO_PATTERN_RANGE_PHASE = new ZulrahPhase( + ZulrahLocation.NORTH, + ZulrahType.RANGE, + false, + StandLocation.TOP_EAST, + Prayer.PROTECT_FROM_MISSILES + ); + private static final ZulrahPhase PATTERN_A_OR_B_RANGE_PHASE = new ZulrahPhase( + ZulrahLocation.NORTH, + ZulrahType.RANGE, + false, + StandLocation.PILLAR_WEST_OUTSIDE, + Prayer.PROTECT_FROM_MISSILES + ); - private static final int ZULRAH_RANGE = NpcID.ZULRAH; - private static final int ZULRAH_MELEE = NpcID.ZULRAH_2043; - private static final int ZULRAH_MAGIC = NpcID.ZULRAH_2044; + private final Point startLocation; + private ZulrahPattern pattern; + private int stage; + private ZulrahPhase phase; - private final ZulrahLocation loc; - private final ZulrahType type; - private final boolean jad; - private final StandLocation standLoc; - - public ZulrahInstance(ZulrahLocation loc, ZulrahType type, boolean jad, StandLocation standLoc) + public ZulrahInstance(NPC zulrah) { - this.loc = loc; - this.type = type; - this.jad = jad; - this.standLoc = standLoc; + this.startLocation = zulrah.getWorldLocation(); } - @Override - public String toString() + public Point getStartLocation() { - return "ZulrahInstance{" + "loc=" + loc + ", type=" + type + '}'; + return startLocation; } - public Point getZulrahLoc(Point startLoc) + public ZulrahPattern getPattern() { - // NORTH doesn't need changing because it is the start - switch (loc) - { - case SOUTH: - return new Point(startLoc.getX(), startLoc.getY() - 11); - case EAST: - return new Point(startLoc.getX() + 10, startLoc.getY() - 2); - case WEST: - return new Point(startLoc.getX() - 10, startLoc.getY() - 2); - } - return startLoc; + return pattern; } - public Point getStandLoc(Point startLoc) + public void setPattern(ZulrahPattern pattern) { - switch (standLoc) - { - case WEST: - return new Point(startLoc.getX() - 5, startLoc.getY() - 2); - case EAST: - return new Point(startLoc.getX() + 5, startLoc.getY() - 2); - case SOUTH: - return new Point(startLoc.getX(), startLoc.getY() - 6); - case TOP_EAST: - return new Point(startLoc.getX() + 6, startLoc.getY() + 2); - case TOP_WEST: - return new Point(startLoc.getX() - 4, startLoc.getY() + 3); - case PILLAR_WEST_INSIDE: - return new Point(startLoc.getX() - 3, startLoc.getY() - 5); - case PILLAR_WEST_OUTSIDE: - return new Point(startLoc.getX() - 4, startLoc.getY() - 3); - case PILLAR_EAST_INSIDE: - return new Point(startLoc.getX() + 3, startLoc.getY() - 5); - case PILLAR_EAST_OUTSIDE: - return new Point(startLoc.getX() + 4, startLoc.getY() - 3); - } - return startLoc; + this.pattern = pattern; } - public ZulrahLocation getLoc() + public int getStage() { - return loc; + return stage; } - public ZulrahType getType() + public void nextStage() { - return type; + ++stage; } - public boolean isJad() + public void reset() { - return jad; + pattern = null; + stage = 0; } - public StandLocation getStandLoc() + public ZulrahPhase getPhase() { - return standLoc; + ZulrahPhase patternPhase = null; + if (pattern != null) + { + patternPhase = pattern.get(stage); + } + return patternPhase != null ? patternPhase : phase; } - @Override - public int hashCode() + public void setPhase(ZulrahPhase phase) { - int hash = 3; - hash = 17 * hash + Objects.hashCode(this.loc); - hash = 17 * hash + Objects.hashCode(this.type); - hash = 17 * hash + (this.jad ? 1 : 0); - return hash; + this.phase = phase; } - @Override - public boolean equals(Object obj) + public ZulrahPhase getNextPhase() { - if (this == obj) + if (pattern != null) { - return true; + return pattern.get(stage + 1); } - if (obj == null) + else if (phase != null) { - return false; + ZulrahType type = phase.getType(); + StandLocation standLocation = phase.getStandLocation(); + if (type == ZulrahType.MELEE) + { + return standLocation == StandLocation.TOP_EAST ? NO_PATTERN_MAGIC_PHASE : NO_PATTERN_RANGE_PHASE; + } + if (type == ZulrahType.MAGIC) + { + return standLocation == StandLocation.TOP_EAST ? NO_PATTERN_RANGE_PHASE : PATTERN_A_OR_B_RANGE_PHASE; + } } - if (getClass() != obj.getClass()) - { - return false; - } - final ZulrahInstance other = (ZulrahInstance) obj; - if (this.jad != other.jad) - { - return false; - } - if (this.loc != other.loc) - { - return false; - } - if (this.type != other.type) - { - return false; - } - return true; - } - - public static ZulrahInstance of(NPC npc, Point start) - { - Point t = npc.getLocalLocation(); - t = Perspective.localToWorld(RuneLite.getClient(), t); - int dx = start.getX() - t.getX(); - int dy = start.getY() - t.getY(); - - ZulrahLocation loc; - ZulrahType type; - - if (dx == -10 && dy == 2) - { - loc = ZulrahLocation.EAST; - } - else if (dx == 10 && dy == 2) - { - loc = ZulrahLocation.WEST; - } - else if (dx == 0 && dy == 11) - { - loc = ZulrahLocation.SOUTH; - } - else if (dx == 0 && dy == 0) - { - loc = ZulrahLocation.NORTH; - } - else - { - logger.debug("Unknown zulrah location! dx: {}, dy: {}", dx, dy); - return null; - } - - int id = npc.getId(); - switch (id) - { - case ZULRAH_RANGE: - type = ZulrahType.RANGE; - break; - case ZULRAH_MELEE: - type = ZulrahType.MELEE; - break; - case ZULRAH_MAGIC: - type = ZulrahType.MAGIC; - break; - default: - logger.debug("Unknown Zulrah npc! {}", id); - return null; - } - - return new ZulrahInstance(loc, type, false, null); + return null; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahOverlay.java new file mode 100644 index 0000000000..8b3c864bf5 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahOverlay.java @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2017, Aria + * Copyright (c) 2017, Devin French + * 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.zulrah; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; +import javax.imageio.ImageIO; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.Perspective; +import net.runelite.api.Point; +import net.runelite.api.Prayer; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.RuneLite; +import net.runelite.client.plugins.zulrah.phase.ZulrahPhase; +import net.runelite.client.plugins.zulrah.phase.ZulrahType; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayPosition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ZulrahOverlay extends Overlay +{ + private static final Logger logger = LoggerFactory.getLogger(ZulrahOverlay.class); + + private static final int CURRENT_PHASE_WIDTH = 86; + private static final int NEXT_PHASE_WIDTH = 54; + private static final int SPACER = 6; + private static final int BOTTOM_BORDER = 4; + private static final Color TILE_BORDER_COLOR = new Color(0, 0, 0, 100); + private static final Color NEXT_TEXT_COLOR = new Color(255, 255, 255, 100); + private static final Color RANGE_BACKGROUND_COLOR = new Color(150, 255, 0, 100); + private static final Color MAGIC_BACKGROUND_COLOR = new Color(20, 170, 200, 100); + private static final Color MELEE_BACKGROUND_COLOR = new Color(180, 50, 20, 100); + private static final Color JAD_BACKGROUND_COLOR = new Color(255, 115, 0, 100); + + private final Client client = RuneLite.getClient(); + private final Zulrah plugin; + private final Image[] zulrahImages = new Image[3]; + private final Image[] smallZulrahImages = new Image[3]; + private final Image[] prayerImages = new Image[2]; + + ZulrahOverlay(Zulrah plugin) + { + super(OverlayPosition.DYNAMIC); + this.plugin = plugin; + } + + @Override + public Dimension render(Graphics2D graphics) + { + ZulrahInstance instance = plugin.getInstance(); + + if (instance == null || client.getGameState() != GameState.LOGGED_IN) + { + return null; + } + + Rectangle viewport = getViewportBounds(); + if (viewport == null) + { + return null; + } + + ZulrahPhase currentPhase = instance.getPhase(); + ZulrahPhase nextPhase = instance.getNextPhase(); + if (currentPhase == null) + { + return null; + } + + String pattern = instance.getPattern() != null ? instance.getPattern().toString() : "Unknown"; + Point startTile = instance.getStartLocation(); + if (nextPhase != null && currentPhase.getStandLocation() == nextPhase.getStandLocation()) + { + drawStandTiles(graphics, startTile, currentPhase, nextPhase); + } + else + { + drawStandTile(graphics, startTile, currentPhase, false); + drawStandTile(graphics, startTile, nextPhase, true); + } + drawZulrahTileMinimap(graphics, startTile, currentPhase, false); + drawZulrahTileMinimap(graphics, startTile, nextPhase, true); + drawCurrentPhaseBox(graphics, viewport, currentPhase, pattern); + drawNextPhaseBox(graphics, viewport, nextPhase); + drawPrayerOutline(graphics, currentPhase.getPrayer()); + + return null; + } + + private void drawStandTiles(Graphics2D graphics, Point startTile, ZulrahPhase currentPhase, ZulrahPhase nextPhase) + { + Point localTile = Perspective.worldToLocal(client, currentPhase.getStandTile(startTile)); + localTile = new Point(localTile.getX() + Perspective.LOCAL_TILE_SIZE / 2, localTile.getY() + Perspective.LOCAL_TILE_SIZE / 2); + Polygon northPoly = getCanvasTileNorthPoly(client, localTile); + Polygon southPoly = getCanvasTileSouthPoly(client, localTile); + Polygon poly = Perspective.getCanvasTilePoly(client, localTile); + Point textLoc = Perspective.getCanvasTextLocation(client, graphics, localTile, "Next", 0); + if (northPoly != null && southPoly != null && poly != null && textLoc != null) + { + Color northColor = getBackgroundColor(currentPhase.getType()); + Color southColor = getBackgroundColor(nextPhase.getType()); + graphics.setColor(northColor); + graphics.fillPolygon(northPoly); + graphics.setColor(southColor); + graphics.fillPolygon(southPoly); + graphics.setColor(TILE_BORDER_COLOR); + graphics.setStroke(new BasicStroke(2)); + graphics.drawPolygon(poly); + graphics.setColor(NEXT_TEXT_COLOR); + graphics.drawString("Next", textLoc.getX(), textLoc.getY()); + } + if (nextPhase.isJad()) + { + Image jadPrayerImg = getProtectionPrayerImage(nextPhase.getPrayer()); + if (jadPrayerImg != null) + { + Point imageLoc = Perspective.getCanvasImageLocation(client, graphics, localTile, (BufferedImage) jadPrayerImg, 0); + if (imageLoc != null) + { + graphics.drawImage(jadPrayerImg, imageLoc.getX(), imageLoc.getY(), null); + } + } + } + } + + private void drawStandTile(Graphics2D graphics, Point startTile, ZulrahPhase phase, boolean next) + { + if (phase == null) + { + return; + } + + Point localTile = Perspective.worldToLocal(client, phase.getStandTile(startTile)); + localTile = new Point(localTile.getX() + Perspective.LOCAL_TILE_SIZE / 2, localTile.getY() + Perspective.LOCAL_TILE_SIZE / 2); + Polygon poly = Perspective.getCanvasTilePoly(client, localTile); + Color color = getBackgroundColor(phase.getType()); + if (poly != null) + { + graphics.setColor(TILE_BORDER_COLOR); + graphics.setStroke(new BasicStroke(2)); + graphics.drawPolygon(poly); + graphics.setColor(color); + graphics.fillPolygon(poly); + } + if (next) + { + Point textLoc = Perspective.getCanvasTextLocation(client, graphics, localTile, "Next", 0); + if (textLoc != null) + { + graphics.setColor(NEXT_TEXT_COLOR); + graphics.drawString("Next", textLoc.getX(), textLoc.getY()); + } + if (phase.isJad()) + { + Image jadPrayerImg = getProtectionPrayerImage(phase.getPrayer()); + if (jadPrayerImg != null) + { + Point imageLoc = Perspective.getCanvasImageLocation(client, graphics, localTile, (BufferedImage) jadPrayerImg, 0); + if (imageLoc != null) + { + graphics.drawImage(jadPrayerImg, imageLoc.getX(), imageLoc.getY(), null); + } + } + } + } + } + + private void drawZulrahTileMinimap(Graphics2D graphics, Point startTile, ZulrahPhase phase, boolean next) + { + if (phase == null) + { + return; + } + Point zulrahLocalTile = Perspective.worldToLocal(client, phase.getZulrahTile(startTile)); + Point zulrahMinimapPoint = Perspective.worldToMiniMap(client, zulrahLocalTile.getX(), zulrahLocalTile.getY()); + Color color = getBackgroundColor(phase.getType()); + graphics.setColor(color); + graphics.fillOval(zulrahMinimapPoint.getX(), zulrahMinimapPoint.getY(), 5, 5); + graphics.setColor(TILE_BORDER_COLOR); + graphics.setStroke(new BasicStroke(1)); + graphics.drawOval(zulrahMinimapPoint.getX(), zulrahMinimapPoint.getY(), 5, 5); + if (next) + { + graphics.setColor(NEXT_TEXT_COLOR); + FontMetrics fm = graphics.getFontMetrics(); + graphics.drawString("Next", zulrahMinimapPoint.getX() - fm.stringWidth("Next") / 2, zulrahMinimapPoint.getY() - 2); + } + } + + private void drawCurrentPhaseBox(Graphics2D graphics, Rectangle viewport, ZulrahPhase phase, String pattern) + { + Image zulrahImg = getZulrahImage(phase.getType()); + if (zulrahImg == null) + { + return; + } + + FontMetrics fm = graphics.getFontMetrics(); + int height = fm.getHeight() * 2 + zulrahImg.getHeight(null) + SPACER + BOTTOM_BORDER; + int bgX = (int) (viewport.getX() + viewport.getWidth() - CURRENT_PHASE_WIDTH); + int bgY = (int) (viewport.getY() + viewport.getHeight() - height); + Color backgroundColor = phase.isJad() ? JAD_BACKGROUND_COLOR : getBackgroundColor(phase.getType()); + + graphics.setColor(backgroundColor); + graphics.fillRect(bgX, bgY, CURRENT_PHASE_WIDTH, height); + graphics.setColor(Color.WHITE); + graphics.drawString(pattern, bgX + (CURRENT_PHASE_WIDTH - fm.stringWidth(pattern)) / 2, bgY + fm.getHeight()); + graphics.drawImage(zulrahImg, bgX + (CURRENT_PHASE_WIDTH - zulrahImg.getWidth(null)) / 2, bgY + fm.getHeight() + SPACER, null); + if (phase.isJad()) + { + graphics.setColor(Color.RED.darker()); + graphics.drawString("JAD PHASE", bgX + (CURRENT_PHASE_WIDTH - fm.stringWidth("JAD PHASE")) / 2, bgY + height - BOTTOM_BORDER); + } + } + + private void drawNextPhaseBox(Graphics2D graphics, Rectangle viewport, ZulrahPhase phase) + { + if (phase == null) + { + return; + } + Image zulrahImg = getSmallZulrahImage(phase.getType()); + if (zulrahImg == null) + { + return; + } + + FontMetrics fm = graphics.getFontMetrics(); + int height = fm.getHeight() + zulrahImg.getHeight(null) + SPACER + BOTTOM_BORDER; + int bgX = (int) (viewport.getX() + viewport.getWidth() - NEXT_PHASE_WIDTH - CURRENT_PHASE_WIDTH); + int bgY = (int) (viewport.getY() + viewport.getHeight() - height); + Color backgroundColor = phase.isJad() ? JAD_BACKGROUND_COLOR : getBackgroundColor(phase.getType()); + graphics.setColor(backgroundColor); + graphics.fillRect(bgX, bgY, NEXT_PHASE_WIDTH, height); + graphics.drawImage(zulrahImg, bgX + (NEXT_PHASE_WIDTH - zulrahImg.getWidth(null)) / 2, bgY + fm.getHeight() + SPACER, null); + if (phase.isJad()) + { + Image jadFirstPrayerImg = getProtectionPrayerImage(phase.getPrayer()); + graphics.drawImage(jadFirstPrayerImg, bgX + (NEXT_PHASE_WIDTH - zulrahImg.getWidth(null)) / 2, bgY + fm.getHeight() + SPACER, null); + graphics.setColor(Color.RED.darker()); + graphics.drawString("Jad Next", bgX + (NEXT_PHASE_WIDTH - fm.stringWidth("Jad Next")) / 2, bgY + fm.getHeight()); + } + else + { + graphics.setColor(Color.WHITE); + graphics.drawString("Next", bgX + (NEXT_PHASE_WIDTH - fm.stringWidth("Next")) / 2, bgY + fm.getHeight()); + } + } + + private Rectangle getViewportBounds() + { + Widget viewport = client.getViewportWidget(); + return viewport != null ? viewport.getBounds() : null; + } + + private void drawPrayerOutline(Graphics2D graphics, Prayer prayer) + { + if (prayer == null || client.isPrayerActive(prayer)) + { + return; + } + Widget prayerWidget; + if (prayer == Prayer.PROTECT_FROM_MISSILES) + { + prayerWidget = client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MISSILES); + } + else + { + prayerWidget = client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MAGIC); + } + if (prayerWidget != null) + { + Rectangle prayerBounds = prayerWidget.getBounds(); + if (prayerBounds != null) + { + graphics.setColor(Color.RED.darker()); + graphics.draw(prayerBounds); + } + } + } + + private Polygon getCanvasTileNorthPoly(Client client, Point localLocation) + { + int plane = client.getPlane(); + int halfTile = Perspective.LOCAL_TILE_SIZE / 2; + + Point p1 = Perspective.worldToCanvas(client, localLocation.getX() - halfTile, localLocation.getY() - halfTile, plane); + Point p2 = Perspective.worldToCanvas(client, localLocation.getX() - halfTile, localLocation.getY() + halfTile, plane); + Point p3 = Perspective.worldToCanvas(client, localLocation.getX() + halfTile, localLocation.getY() + halfTile, plane); + + if (p1 == null || p2 == null || p3 == null) + { + return null; + } + + Polygon poly = new Polygon(); + poly.addPoint(p1.getX(), p1.getY()); + poly.addPoint(p2.getX(), p2.getY()); + poly.addPoint(p3.getX(), p3.getY()); + + return poly; + } + + private Polygon getCanvasTileSouthPoly(Client client, Point localLocation) + { + int plane = client.getPlane(); + int halfTile = Perspective.LOCAL_TILE_SIZE / 2; + + Point p1 = Perspective.worldToCanvas(client, localLocation.getX() - halfTile, localLocation.getY() - halfTile, plane); + Point p2 = Perspective.worldToCanvas(client, localLocation.getX() + halfTile, localLocation.getY() + halfTile, plane); + Point p3 = Perspective.worldToCanvas(client, localLocation.getX() + halfTile, localLocation.getY() - halfTile, plane); + + if (p1 == null || p2 == null || p3 == null) + { + return null; + } + + Polygon poly = new Polygon(); + poly.addPoint(p1.getX(), p1.getY()); + poly.addPoint(p2.getX(), p2.getY()); + poly.addPoint(p3.getX(), p3.getY()); + + return poly; + } + + private Color getBackgroundColor(ZulrahType type) + { + switch (type) + { + case RANGE: + return RANGE_BACKGROUND_COLOR; + case MAGIC: + return MAGIC_BACKGROUND_COLOR; + case MELEE: + return MELEE_BACKGROUND_COLOR; + } + return Color.DARK_GRAY; + } + + private Image getZulrahImage(ZulrahType type) + { + switch (type) + { + case RANGE: + if (zulrahImages[0] == null) + { + zulrahImages[0] = getImage("zulrah_range.png"); + } + return zulrahImages[0]; + case MAGIC: + if (zulrahImages[1] == null) + { + zulrahImages[1] = getImage("zulrah_magic.png"); + } + return zulrahImages[1]; + case MELEE: + if (zulrahImages[2] == null) + { + zulrahImages[2] = getImage("zulrah_melee.png"); + } + return zulrahImages[2]; + } + return null; + } + + private Image getSmallZulrahImage(ZulrahType type) + { + switch (type) + { + case RANGE: + if (smallZulrahImages[0] == null) + { + smallZulrahImages[0] = getImage("zulrah_range_small.png"); + } + return smallZulrahImages[0]; + case MAGIC: + if (smallZulrahImages[1] == null) + { + smallZulrahImages[1] = getImage("zulrah_magic_small.png"); + } + return smallZulrahImages[1]; + case MELEE: + if (smallZulrahImages[2] == null) + { + smallZulrahImages[2] = getImage("zulrah_melee_small.png"); + } + return smallZulrahImages[2]; + } + return null; + } + + private Image getProtectionPrayerImage(Prayer prayer) + { + switch (prayer) + { + case PROTECT_FROM_MAGIC: + if (prayerImages[0] == null) + { + prayerImages[0] = getImage("/prayers/protect_from_magic.png"); + } + return prayerImages[0]; + case PROTECT_FROM_MISSILES: + if (prayerImages[1] == null) + { + prayerImages[1] = getImage("/prayers/protect_from_missiles.png"); + } + return prayerImages[1]; + } + return null; + } + + private Image getImage(String path) + { + Image image = null; + try + { + InputStream in = ZulrahOverlay.class.getResourceAsStream(path); + image = ImageIO.read(in); + } + catch (IOException e) + { + logger.debug("Error loading image {}", e); + } + return image; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/patterns/ZulrahPattern.java b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/patterns/ZulrahPattern.java index 184b706b85..e846ad7b45 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/patterns/ZulrahPattern.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/patterns/ZulrahPattern.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, Aria + * Copyright (c) 2017, Devin French * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,31 +27,32 @@ package net.runelite.client.plugins.zulrah.patterns; import java.util.ArrayList; import java.util.List; -import net.runelite.client.plugins.zulrah.StandLocation; -import net.runelite.client.plugins.zulrah.ZulrahInstance; -import net.runelite.client.plugins.zulrah.ZulrahLocation; -import net.runelite.client.plugins.zulrah.ZulrahType; +import net.runelite.api.Prayer; +import net.runelite.client.plugins.zulrah.phase.StandLocation; +import net.runelite.client.plugins.zulrah.phase.ZulrahLocation; +import net.runelite.client.plugins.zulrah.phase.ZulrahPhase; +import net.runelite.client.plugins.zulrah.phase.ZulrahType; public abstract class ZulrahPattern { - private final List pattern = new ArrayList<>(); + private final List pattern = new ArrayList<>(); - protected final void add(ZulrahLocation loc, ZulrahType type, StandLocation standLoc) + protected final void add(ZulrahLocation loc, ZulrahType type, StandLocation standLocation, Prayer prayer) { - add(loc, type, standLoc, false); + add(loc, type, standLocation, false, prayer); } - protected final void addJad(ZulrahLocation loc, ZulrahType type, StandLocation standLoc) + protected final void addJad(ZulrahLocation loc, ZulrahType type, StandLocation standLocation, Prayer prayer) { - add(loc, type, standLoc, true); + add(loc, type, standLocation, true, prayer); } - private void add(ZulrahLocation loc, ZulrahType type, StandLocation standLoc, boolean jad) + private void add(ZulrahLocation loc, ZulrahType type, StandLocation standLocation, boolean jad, Prayer prayer) { - pattern.add(new ZulrahInstance(loc, type, jad, standLoc)); + pattern.add(new ZulrahPhase(loc, type, jad, standLocation, prayer)); } - public ZulrahInstance get(int index) + public ZulrahPhase get(int index) { if (index >= pattern.size()) { @@ -60,9 +62,9 @@ public abstract class ZulrahPattern return pattern.get(index); } - public boolean stageMatches(int index, ZulrahInstance instance) + public boolean stageMatches(int index, ZulrahPhase instance) { - ZulrahInstance patternInstance = get(index); + ZulrahPhase patternInstance = get(index); return patternInstance != null && patternInstance.equals(instance); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/patterns/ZulrahPatternA.java b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/patterns/ZulrahPatternA.java index edca161126..c494abd424 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/patterns/ZulrahPatternA.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/patterns/ZulrahPatternA.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, Aria + * Copyright (c) 2017, Devin French * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,23 +25,30 @@ */ package net.runelite.client.plugins.zulrah.patterns; -import net.runelite.client.plugins.zulrah.StandLocation; -import net.runelite.client.plugins.zulrah.ZulrahLocation; -import net.runelite.client.plugins.zulrah.ZulrahType; +import net.runelite.api.Prayer; +import net.runelite.client.plugins.zulrah.phase.StandLocation; +import net.runelite.client.plugins.zulrah.phase.ZulrahLocation; +import net.runelite.client.plugins.zulrah.phase.ZulrahType; public class ZulrahPatternA extends ZulrahPattern { public ZulrahPatternA() { - add(ZulrahLocation.NORTH, ZulrahType.RANGE, StandLocation.TOP_EAST); - add(ZulrahLocation.NORTH, ZulrahType.MELEE, StandLocation.TOP_EAST); - add(ZulrahLocation.NORTH, ZulrahType.MAGIC, StandLocation.WEST); - add(ZulrahLocation.SOUTH, ZulrahType.RANGE, StandLocation.PILLAR_WEST_OUTSIDE); - add(ZulrahLocation.NORTH, ZulrahType.MELEE, StandLocation.PILLAR_WEST_OUTSIDE); - add(ZulrahLocation.WEST, ZulrahType.MAGIC, StandLocation.PILLAR_WEST_OUTSIDE); - add(ZulrahLocation.SOUTH, ZulrahType.RANGE, StandLocation.EAST); - add(ZulrahLocation.SOUTH, ZulrahType.MAGIC, StandLocation.EAST); - addJad(ZulrahLocation.WEST, ZulrahType.RANGE, StandLocation.TOP_WEST); - add(ZulrahLocation.NORTH, ZulrahType.MELEE, StandLocation.TOP_WEST); + add(ZulrahLocation.NORTH, ZulrahType.RANGE, StandLocation.TOP_EAST, null); + add(ZulrahLocation.NORTH, ZulrahType.MELEE, StandLocation.TOP_EAST, null); + add(ZulrahLocation.NORTH, ZulrahType.MAGIC, StandLocation.PILLAR_WEST_INSIDE, Prayer.PROTECT_FROM_MAGIC); + add(ZulrahLocation.SOUTH, ZulrahType.RANGE, StandLocation.PILLAR_WEST_INSIDE, Prayer.PROTECT_FROM_MISSILES); + add(ZulrahLocation.NORTH, ZulrahType.MELEE, StandLocation.PILLAR_WEST_INSIDE, null); + add(ZulrahLocation.WEST, ZulrahType.MAGIC, StandLocation.PILLAR_WEST_INSIDE, Prayer.PROTECT_FROM_MAGIC); + add(ZulrahLocation.SOUTH, ZulrahType.RANGE, StandLocation.PILLAR_EAST_OUTSIDE, null); + add(ZulrahLocation.SOUTH, ZulrahType.MAGIC, StandLocation.SOUTH_WEST, Prayer.PROTECT_FROM_MAGIC); + addJad(ZulrahLocation.WEST, ZulrahType.RANGE, StandLocation.TOP_WEST, Prayer.PROTECT_FROM_MISSILES); + add(ZulrahLocation.NORTH, ZulrahType.MELEE, StandLocation.TOP_WEST, null); + } + + @Override + public String toString() + { + return "Pattern A"; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/patterns/ZulrahPatternB.java b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/patterns/ZulrahPatternB.java index 066a7a9382..7d1662a5c4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/patterns/ZulrahPatternB.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/patterns/ZulrahPatternB.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, Aria + * Copyright (c) 2017, Devin French * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,25 +25,30 @@ */ package net.runelite.client.plugins.zulrah.patterns; -import net.runelite.client.plugins.zulrah.StandLocation; -import net.runelite.client.plugins.zulrah.ZulrahLocation; -import net.runelite.client.plugins.zulrah.ZulrahType; +import net.runelite.api.Prayer; +import net.runelite.client.plugins.zulrah.phase.StandLocation; +import net.runelite.client.plugins.zulrah.phase.ZulrahLocation; +import net.runelite.client.plugins.zulrah.phase.ZulrahType; public class ZulrahPatternB extends ZulrahPattern { - public ZulrahPatternB() { - add(ZulrahLocation.NORTH, ZulrahType.RANGE, StandLocation.TOP_EAST); - add(ZulrahLocation.NORTH, ZulrahType.MELEE, StandLocation.TOP_EAST); - add(ZulrahLocation.NORTH, ZulrahType.MAGIC, StandLocation.WEST); - add(ZulrahLocation.WEST, ZulrahType.RANGE, StandLocation.PILLAR_WEST_OUTSIDE); - // there is an optional phase here? - add(ZulrahLocation.SOUTH, ZulrahType.MAGIC, StandLocation.PILLAR_WEST_INSIDE); - add(ZulrahLocation.NORTH, ZulrahType.MELEE, StandLocation.TOP_EAST); - add(ZulrahLocation.EAST, ZulrahType.RANGE, StandLocation.EAST); - add(ZulrahLocation.SOUTH, ZulrahType.MAGIC, StandLocation.PILLAR_WEST_INSIDE); - addJad(ZulrahLocation.WEST, ZulrahType.RANGE, StandLocation.TOP_WEST); - add(ZulrahLocation.NORTH, ZulrahType.MELEE, StandLocation.TOP_WEST); + add(ZulrahLocation.NORTH, ZulrahType.RANGE, StandLocation.TOP_EAST, null); + add(ZulrahLocation.NORTH, ZulrahType.MELEE, StandLocation.TOP_EAST, null); + add(ZulrahLocation.NORTH, ZulrahType.MAGIC, StandLocation.PILLAR_WEST_OUTSIDE, Prayer.PROTECT_FROM_MAGIC); + add(ZulrahLocation.WEST, ZulrahType.RANGE, StandLocation.PILLAR_WEST_OUTSIDE, null); + add(ZulrahLocation.SOUTH, ZulrahType.MAGIC, StandLocation.SOUTH_WEST, Prayer.PROTECT_FROM_MAGIC); // optional phase + add(ZulrahLocation.NORTH, ZulrahType.MELEE, StandLocation.PILLAR_WEST_INSIDE, null); + add(ZulrahLocation.EAST, ZulrahType.RANGE, StandLocation.SOUTH_EAST, Prayer.PROTECT_FROM_MISSILES); + add(ZulrahLocation.SOUTH, ZulrahType.MAGIC, StandLocation.SOUTH_WEST, Prayer.PROTECT_FROM_MAGIC); + addJad(ZulrahLocation.WEST, ZulrahType.RANGE, StandLocation.TOP_WEST, Prayer.PROTECT_FROM_MISSILES); + add(ZulrahLocation.NORTH, ZulrahType.MELEE, StandLocation.TOP_WEST, null); + } + + @Override + public String toString() + { + return "Pattern B"; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/patterns/ZulrahPatternC.java b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/patterns/ZulrahPatternC.java index 031521c146..152035e636 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/patterns/ZulrahPatternC.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/patterns/ZulrahPatternC.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, Aria + * Copyright (c) 2017, Devin French * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,24 +25,31 @@ */ package net.runelite.client.plugins.zulrah.patterns; -import net.runelite.client.plugins.zulrah.StandLocation; -import net.runelite.client.plugins.zulrah.ZulrahLocation; -import net.runelite.client.plugins.zulrah.ZulrahType; +import net.runelite.api.Prayer; +import net.runelite.client.plugins.zulrah.phase.StandLocation; +import net.runelite.client.plugins.zulrah.phase.ZulrahLocation; +import net.runelite.client.plugins.zulrah.phase.ZulrahType; public class ZulrahPatternC extends ZulrahPattern { public ZulrahPatternC() { - add(ZulrahLocation.NORTH, ZulrahType.RANGE, StandLocation.TOP_EAST); - add(ZulrahLocation.EAST, ZulrahType.RANGE, StandLocation.TOP_EAST); - add(ZulrahLocation.NORTH, ZulrahType.MELEE, StandLocation.TOP_WEST); - add(ZulrahLocation.WEST, ZulrahType.MAGIC, StandLocation.TOP_WEST); - add(ZulrahLocation.SOUTH, ZulrahType.RANGE, StandLocation.EAST); - add(ZulrahLocation.EAST, ZulrahType.MAGIC, StandLocation.PILLAR_EAST_OUTSIDE); - add(ZulrahLocation.NORTH, ZulrahType.RANGE, StandLocation.WEST); - add(ZulrahLocation.WEST, ZulrahType.RANGE, StandLocation.PILLAR_WEST_OUTSIDE); - add(ZulrahLocation.NORTH, ZulrahType.MAGIC, StandLocation.PILLAR_EAST_OUTSIDE); - addJad(ZulrahLocation.EAST, ZulrahType.MAGIC, StandLocation.PILLAR_EAST_OUTSIDE); - add(ZulrahLocation.NORTH, ZulrahType.MAGIC, StandLocation.EAST); + add(ZulrahLocation.NORTH, ZulrahType.RANGE, StandLocation.TOP_EAST, null); + add(ZulrahLocation.EAST, ZulrahType.RANGE, StandLocation.TOP_EAST, Prayer.PROTECT_FROM_MISSILES); + add(ZulrahLocation.NORTH, ZulrahType.MELEE, StandLocation.TOP_WEST, null); + add(ZulrahLocation.WEST, ZulrahType.MAGIC, StandLocation.WEST, Prayer.PROTECT_FROM_MAGIC); + add(ZulrahLocation.SOUTH, ZulrahType.RANGE, StandLocation.SOUTH_EAST, Prayer.PROTECT_FROM_MISSILES); + add(ZulrahLocation.EAST, ZulrahType.MAGIC, StandLocation.PILLAR_EAST_OUTSIDE, Prayer.PROTECT_FROM_MAGIC); + add(ZulrahLocation.NORTH, ZulrahType.RANGE, StandLocation.PILLAR_WEST_OUTSIDE, null); + add(ZulrahLocation.WEST, ZulrahType.RANGE, StandLocation.PILLAR_WEST_OUTSIDE, Prayer.PROTECT_FROM_MISSILES); + add(ZulrahLocation.NORTH, ZulrahType.MAGIC, StandLocation.TOP_EAST, Prayer.PROTECT_FROM_MAGIC); + addJad(ZulrahLocation.EAST, ZulrahType.MAGIC, StandLocation.TOP_EAST, Prayer.PROTECT_FROM_MAGIC); + add(ZulrahLocation.NORTH, ZulrahType.MAGIC, StandLocation.TOP_EAST, null); + } + + @Override + public String toString() + { + return "Pattern C"; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/patterns/ZulrahPatternD.java b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/patterns/ZulrahPatternD.java index a74febde89..1b047e3d07 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/patterns/ZulrahPatternD.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/patterns/ZulrahPatternD.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, Aria + * Copyright (c) 2017, Devin French * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,25 +25,32 @@ */ package net.runelite.client.plugins.zulrah.patterns; -import net.runelite.client.plugins.zulrah.StandLocation; -import net.runelite.client.plugins.zulrah.ZulrahLocation; -import net.runelite.client.plugins.zulrah.ZulrahType; +import net.runelite.api.Prayer; +import net.runelite.client.plugins.zulrah.phase.StandLocation; +import net.runelite.client.plugins.zulrah.phase.ZulrahLocation; +import net.runelite.client.plugins.zulrah.phase.ZulrahType; public class ZulrahPatternD extends ZulrahPattern { public ZulrahPatternD() { - add(ZulrahLocation.NORTH, ZulrahType.RANGE, StandLocation.TOP_EAST); - add(ZulrahLocation.EAST, ZulrahType.MAGIC, StandLocation.TOP_EAST); - add(ZulrahLocation.SOUTH, ZulrahType.RANGE, StandLocation.PILLAR_WEST_INSIDE); - add(ZulrahLocation.WEST, ZulrahType.MAGIC, StandLocation.WEST); - add(ZulrahLocation.NORTH, ZulrahType.MELEE, StandLocation.PILLAR_EAST_OUTSIDE); - add(ZulrahLocation.EAST, ZulrahType.RANGE, StandLocation.PILLAR_EAST_OUTSIDE); - add(ZulrahLocation.SOUTH, ZulrahType.RANGE, StandLocation.PILLAR_EAST_OUTSIDE); - add(ZulrahLocation.WEST, ZulrahType.MAGIC, StandLocation.PILLAR_WEST_OUTSIDE); - add(ZulrahLocation.NORTH, ZulrahType.RANGE, StandLocation.TOP_EAST); - add(ZulrahLocation.NORTH, ZulrahType.MAGIC, StandLocation.TOP_EAST); - addJad(ZulrahLocation.EAST, ZulrahType.MAGIC, StandLocation.TOP_EAST); - add(ZulrahLocation.NORTH, ZulrahType.MAGIC, StandLocation.TOP_EAST); + add(ZulrahLocation.NORTH, ZulrahType.RANGE, StandLocation.TOP_EAST, null); + add(ZulrahLocation.EAST, ZulrahType.MAGIC, StandLocation.TOP_EAST, Prayer.PROTECT_FROM_MAGIC); + add(ZulrahLocation.SOUTH, ZulrahType.RANGE, StandLocation.PILLAR_WEST_INSIDE, Prayer.PROTECT_FROM_MISSILES); + add(ZulrahLocation.WEST, ZulrahType.MAGIC, StandLocation.PILLAR_WEST_INSIDE, Prayer.PROTECT_FROM_MAGIC); + add(ZulrahLocation.NORTH, ZulrahType.MELEE, StandLocation.PILLAR_EAST_OUTSIDE, null); + add(ZulrahLocation.EAST, ZulrahType.RANGE, StandLocation.PILLAR_EAST_OUTSIDE, Prayer.PROTECT_FROM_MISSILES); + add(ZulrahLocation.SOUTH, ZulrahType.RANGE, StandLocation.PILLAR_WEST_OUTSIDE, null); + add(ZulrahLocation.WEST, ZulrahType.MAGIC, StandLocation.PILLAR_WEST_OUTSIDE, Prayer.PROTECT_FROM_MAGIC); + add(ZulrahLocation.NORTH, ZulrahType.RANGE, StandLocation.TOP_EAST, Prayer.PROTECT_FROM_MISSILES); + add(ZulrahLocation.NORTH, ZulrahType.MAGIC, StandLocation.TOP_EAST, Prayer.PROTECT_FROM_MAGIC); + addJad(ZulrahLocation.EAST, ZulrahType.MAGIC, StandLocation.TOP_EAST, Prayer.PROTECT_FROM_MAGIC); + add(ZulrahLocation.NORTH, ZulrahType.MAGIC, StandLocation.TOP_EAST, null); + } + + @Override + public String toString() + { + return "Pattern D"; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/StandLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/phase/StandLocation.java similarity index 90% rename from runelite-client/src/main/java/net/runelite/client/plugins/zulrah/StandLocation.java rename to runelite-client/src/main/java/net/runelite/client/plugins/zulrah/phase/StandLocation.java index d9eaa54ca0..6f2a757472 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/StandLocation.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/phase/StandLocation.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, Aria + * Copyright (c) 2017, Devin French * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,17 +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.zulrah; +package net.runelite.client.plugins.zulrah.phase; public enum StandLocation { WEST, EAST, SOUTH, + SOUTH_WEST, + SOUTH_EAST, TOP_EAST, TOP_WEST, PILLAR_WEST_INSIDE, PILLAR_WEST_OUTSIDE, PILLAR_EAST_INSIDE, - PILLAR_EAST_OUTSIDE; + PILLAR_EAST_OUTSIDE } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/Fight.java b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/phase/ZulrahLocation.java similarity index 57% rename from runelite-client/src/main/java/net/runelite/client/plugins/zulrah/Fight.java rename to runelite-client/src/main/java/net/runelite/client/plugins/zulrah/phase/ZulrahLocation.java index 137ba3b631..e93ce03c9e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/Fight.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/phase/ZulrahLocation.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2017, Adam + * Copyright (c) 2017, Aria + * Copyright (c) 2017, Devin French * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,68 +23,42 @@ * (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.zulrah; +package net.runelite.client.plugins.zulrah.phase; -import java.time.Instant; import net.runelite.api.Point; -import net.runelite.client.plugins.zulrah.patterns.ZulrahPattern; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -public class Fight +public enum ZulrahLocation { - private final Point startLocationWorld; - private final Instant startTime = Instant.now(); - private ZulrahPattern pattern; - private int stage; - private ZulrahInstance zulrah; + NORTH, SOUTH, EAST, WEST; - public Fight(Point startLocationWorld) - { - this.startLocationWorld = startLocationWorld; - } + private static final Logger logger = LoggerFactory.getLogger(ZulrahLocation.class); - public Point getStartLocationWorld() + public static ZulrahLocation valueOf(Point start, Point current) { - return startLocationWorld; - } - - public Instant getStartTime() - { - return startTime; - } - - public ZulrahPattern getPattern() - { - return pattern; - } - - public void setPattern(ZulrahPattern pattern) - { - this.pattern = pattern; - } - - public ZulrahInstance getZulrah() - { - return zulrah; - } - - public void setZulrah(ZulrahInstance zulrah) - { - this.zulrah = zulrah; - } - - public int getStage() - { - return stage; - } - - public void nextStage() - { - ++stage; - } - - public void reset() - { - pattern = null; - stage = 0; + int dx = start.getX() - current.getX(); + int dy = start.getY() - current.getY(); + if (dx == -10 && dy == 2) + { + return ZulrahLocation.EAST; + } + else if (dx == 10 && dy == 2) + { + return ZulrahLocation.WEST; + } + else if (dx == 0 && dy == 11) + { + return ZulrahLocation.SOUTH; + } + else if (dx == 0 && dy == 0) + { + return ZulrahLocation.NORTH; + } + else + { + logger.debug("Unknown Zulrah location dx: {}, dy: {}", dx, dy); + return null; + } } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/phase/ZulrahPhase.java b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/phase/ZulrahPhase.java new file mode 100644 index 0000000000..ce6c9fa0e2 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/phase/ZulrahPhase.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2017, Aria + * Copyright (c) 2017, Devin French + * 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.zulrah.phase; + +import net.runelite.api.NPC; +import net.runelite.api.Point; +import net.runelite.api.Prayer; + +public class ZulrahPhase +{ + private final ZulrahLocation zulrahLocation; + private final ZulrahType type; + private final boolean jad; + private final StandLocation standLocation; + private final Prayer prayer; + + public ZulrahPhase(ZulrahLocation zulrahLocation, ZulrahType type, boolean jad, StandLocation standLocation, Prayer prayer) + { + this.zulrahLocation = zulrahLocation; + this.type = type; + this.jad = jad; + this.standLocation = standLocation; + this.prayer = prayer; + } + + public static ZulrahPhase valueOf(NPC zulrah, Point start) + { + ZulrahLocation zulrahLocation = ZulrahLocation.valueOf(start, zulrah.getWorldLocation()); + ZulrahType zulrahType = ZulrahType.valueOf(zulrah.getId()); + if (zulrahLocation == null || zulrahType == null) + { + return null; + } + StandLocation standLocation = zulrahType == ZulrahType.MAGIC ? StandLocation.PILLAR_WEST_OUTSIDE : StandLocation.TOP_EAST; + Prayer prayer = zulrahType == ZulrahType.MAGIC ? Prayer.PROTECT_FROM_MAGIC : null; + return new ZulrahPhase(zulrahLocation, zulrahType, false, standLocation, prayer); + } + + @Override + public String toString() + { + return "ZulrahPhase{" + + "zulrahLocation=" + zulrahLocation + + ", type=" + type + + ", jad=" + jad + + ", standLocation=" + standLocation + + ", prayer=" + prayer + + '}'; + } + + // world location + public Point getZulrahTile(Point startTile) + { + // NORTH doesn't need changing because it is the start + switch (zulrahLocation) + { + case SOUTH: + return new Point(startTile.getX(), startTile.getY() - 11); + case EAST: + return new Point(startTile.getX() + 10, startTile.getY() - 2); + case WEST: + return new Point(startTile.getX() - 10, startTile.getY() - 2); + } + return startTile; + } + + // world location + public Point getStandTile(Point startTile) + { + switch (standLocation) + { + case WEST: + return new Point(startTile.getX() - 5, startTile.getY()); + case EAST: + return new Point(startTile.getX() + 5, startTile.getY() - 2); + case SOUTH: + return new Point(startTile.getX(), startTile.getY() - 6); + case SOUTH_WEST: + return new Point(startTile.getX() - 4, startTile.getY() - 4); + case SOUTH_EAST: + return new Point(startTile.getX() + 2, startTile.getY() - 6); + case TOP_EAST: + return new Point(startTile.getX() + 6, startTile.getY() + 2); + case TOP_WEST: + return new Point(startTile.getX() - 4, startTile.getY() + 3); + case PILLAR_WEST_INSIDE: + return new Point(startTile.getX() - 4, startTile.getY() - 3); + case PILLAR_WEST_OUTSIDE: + return new Point(startTile.getX() - 5, startTile.getY() - 3); + case PILLAR_EAST_INSIDE: + return new Point(startTile.getX() + 4, startTile.getY() - 3); + case PILLAR_EAST_OUTSIDE: + return new Point(startTile.getX() + 4, startTile.getY() - 4); + } + return startTile; + } + + public ZulrahLocation getZulrahLocation() + { + return zulrahLocation; + } + + public ZulrahType getType() + { + return type; + } + + public boolean isJad() + { + return jad; + } + + public StandLocation getStandLocation() + { + return standLocation; + } + + public Prayer getPrayer() + { + return prayer; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null || getClass() != obj.getClass()) + { + return false; + } + ZulrahPhase other = (ZulrahPhase) obj; + return this.jad == other.jad && this.zulrahLocation == other.zulrahLocation && this.type == other.type; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/phase/ZulrahType.java similarity index 62% rename from runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahLocation.java rename to runelite-client/src/main/java/net/runelite/client/plugins/zulrah/phase/ZulrahType.java index c324fe694e..9f5a8ea54c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/ZulrahLocation.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/zulrah/phase/ZulrahType.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2017, Aria + * Copyright (c) 2017, Devin French * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,12 +23,34 @@ * (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.zulrah; +package net.runelite.client.plugins.zulrah.phase; -public enum ZulrahLocation +import net.runelite.api.NpcID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public enum ZulrahType { - NORTH, - SOUTH, - EAST, - WEST + RANGE, MAGIC, MELEE; + + private static final Logger logger = LoggerFactory.getLogger(ZulrahType.class); + + private static final int ZULRAH_RANGE = NpcID.ZULRAH; + private static final int ZULRAH_MELEE = NpcID.ZULRAH_2043; + private static final int ZULRAH_MAGIC = NpcID.ZULRAH_2044; + + public static ZulrahType valueOf(int zulrahId) + { + switch (zulrahId) + { + case ZULRAH_RANGE: + return ZulrahType.RANGE; + case ZULRAH_MELEE: + return ZulrahType.MELEE; + case ZULRAH_MAGIC: + return ZulrahType.MAGIC; + } + logger.debug("Unknown Zulrah Id: {}", zulrahId); + return null; + } } diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/zulrah/zulrah_magic.png b/runelite-client/src/main/resources/net/runelite/client/plugins/zulrah/zulrah_magic.png new file mode 100644 index 0000000000000000000000000000000000000000..a0d06b0b956612828f947b1032652c6a60dd3c28 GIT binary patch literal 18929 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 literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/zulrah/zulrah_magic_small.png b/runelite-client/src/main/resources/net/runelite/client/plugins/zulrah/zulrah_magic_small.png new file mode 100644 index 0000000000000000000000000000000000000000..d6d3e580f1e6be92f8bdbbef3f2e79403e8f77ea GIT binary patch literal 17552 zcmeI4dpJ~k_rQ1L5(yPuWlZr(jk%dIGsYm2OEfM;7u8%gCUa|MT++$O?NF!7(S_0t zQHS1&qm&5U4&Bp{LMJ(=a8O7T&e6LEpSzYq0#J1S>qEakIz_-QvQ9KA9 zQb#aY;1VeVeIh-5d66MJIv;Dt(qcw1AO|8)!NEj`gkm`(!WKK|mjQiO4&$+y!6}Lm zTdadJAjaQ=jd7I7Kn%r_isKQ9M2t1vl1QPENtA^c5`jp;6X39MOPa@%<894bSv4RtU z6U*lgIr-?v36%3>0;xhE5o45oIb2Do!WN5F1{yhr?@J^d2_%*evx6w&BREn#(UO4w zk`bRbVj~Te2?sBY&%=X4Py~t$POMLTJ*L?Mv}OTH3vME%vkP)QdPSly zhL$T9hd~I2k$%xq?z>3};=Mt+Bvi%&7l%Q&GWSb&uMh-|Cg&4RO3hcV9gGls#Z0Lg zG8-&~p$svhdu2GvK#oEp^OZ=1tf4Ane?*9JbR4WIjJcmc%$J1A=QHtelhJ&SdIOy} z3XlcWqcu)hj5J>ofkCulSkZ9=YX*TZ#>vo3m=6yLUl6$IQy+9+$N`bUu%eCiF=i&* z2ef|p90liZc^Nx*j2}La5h#&~I0}|P#0diNQgILyKRP&O=15gB93?`D47x0kMP}l^ zG(5&?u+kW=V!47N=7FwGENH+|AmB4dL@O$ZMC0K=E|G*IQ6O+!0-ufxB=e|bYciEe zqYOdx@EB|UnQJErFI2f&AlE~Ug)iYj?nf_+Kp|6zBpwOJ;gg|U5kYGljZdZGXdstL zp;;3MbP{DSTTq}e&OQsYOeTQ(9!EH4j`CjkP_RHUiB97}j>#a6kKFN#5zApS!RP7xdkBX7k1_vxjPPJkjI_Ll zTn*0px)Zr1P!Z0Nfet}Xh5VhBKjigOGq`&uUimB#3jP-oA?Jks&(c7l(*ucg0#tu| zo;8k2v!dcSWG;v!2Xd{ft*BHAk35p*eld;FPI|^0{JZb#{&L1@c$`u zf3F|@+Y9KwGIK*K@zb98vCn?qLz&8L1q158P%q#N_B|pK|Jm4R%?!==SN2h1=-_;7 z5&=4(%_npWn~qh(O;;kDj#a}=M=h6&1qv2vI2vqtjFoclGxT8gfwt)&i%NkWdrUlh z7;eE9M1VqnCjs;Xl`EeFB>J!oT=7N2m7|7a2*_ZtLBlbMVZ)(RjoDBScF&h0F)0MRCD^Ad4t2 zWIix0iVFq=SwwLm^MPqmTreQWB8m%{4@`^Vf&oDmQC!G;U|JLx3<$D_;zH&F)1tUw zK#)Zg7cw817R3bvf-IuAkomy0C@vTfWD&)M%m=1LalwEfizqH+J}@nc3kC#PL~$YW zfoV}(Fd)bxiVK+!OpD@z0YMf~T*!Q2S`-%y2(pOcLgoY0qPSo{kVO<1G9Q>0#RUU` zETXuO`M|U&E*KDG5ygef2c|`F!GIu(C@y3^FfED;1_W6|#HBU*@+~Na-nb2iUafuE ziqU~y%f;|qygdLQ(i8x;#sWa^2k86<0E7|%pk+M(Fb)8~G|8TIH=F@Llke)};2TkU zIHgy<{N;@D(stRs!lu{Ob(d?V*n9B}^p-d$8&-1E4NmV`I_+dFW3e!(PkWMG)h^E? zp0C6q%-lBzG46owK*=J~zXhlA@Wo zo92h#d14CQdZ#hIi}q3NxHgWKhk4;YspeH_?82I~o#%fzKa*eWEZ@8!`A`0WrGLa6 z@~%prxHm0hpfIAP>Ui6_C5yE4HU_2NYVQbXsWd*;wzraezoMtMeE!ZR%`5%;eK%Bi^86{tuq^jRje>l;9`c7);dxI{sg|9b+ zNVaTf&TIQsx8&%-=9(#&8*hbBY{$3MoHn_(X3F<1uI&{oX$j7(f~Dq}>KJpA8R=_Y zra7qn8RK)bJ=P##UbrxR|B>Aeu_otj2+aDNl7On5zb3XeF7A4Du`SDCw(WaO3w%^- zult!2-rb6%3yh58SxLdg*@EWs{*t-zF7AFRm$cHS`{ibr-R+#iiw&R{-LJUizP38z zfbZpZQ`h^uT<0ZTR7r2#Fg3G`zlU36Y?|CQfpf2+LQH#h_pi4LGwpPr+_V7l4I*{>bf<~vnvFd-q05+%C)W=-c`l`juws$%D*?$~hP_Zh`c-c04{ zinkg_9QqzFThwiv3;Kl@^}jQ1agIIhe(SMGCIy?3kQTv;D13193boS=r|CRjXaX3o z_l|Krb8&HyiE)9Y(Ld-++=nC8PMZpEOl@9sWtLDS>__vt^~L%JJ6#v;F4egqYuIVn zu#b`Ad@^&(mfmO4eeBj^lQ8v3`Cf*T&Pmf!vnT$u_gNjrq|*x@t*>&(YlSS1mzf`= zReoRb$d255mCN(WKJzp;@5~eXwZTc z9iRAGX2^jhDOb!Vv+WYM|CX{~ks|kJ(S?Ag_W+@DyleKjYgD6?F*&Mg^LF-qsK_12 zVefzR=v;wV(Bby}nkU%2zp2RLcHKM8^ z)jRHK_CkJQI{haVH`;e|7(46>)lNnFac@-86Dl)HZQeT;_PIN#&70m3>^+%n9(FG= z!?yhLOoybHSsi+Zzn%ZQ=Ety9Vx!-B@8ov|t(~1unsF8{a{8u3hkmC|yI!j4WY6x@ z)->5+cG~Eu6l2ohp0sW9Pk2wqLz`-1cDP2AXzcqwxD&s`*JEeuKyxIwUsd04$wAd$ zvK;D^(sW%OIC_lx3njVI1t^P|z)Fj+n!Kdl=l-E~wwlq+31 z+h>1LQI?rHI}3B% zh85PeugdQBwAXIL*l6c={c`q&i}={ab*eiGm=Axwmg||UuUTci`FL;JNyqr{nq|Kc zn_Fu;dq`yuMSAJ$7UI>PtO~VmC$9K;W^`@;>+4G&uX9UJdf0QY4>)l){ee4ceNxD_ zx+Ffmk{IJ&>SOvURjC);I&Z&-Xh%-%mKeawi)ni%KqI3FdcgE-6FWG$w zTUyF?EdTB`JAFm7+2-25k~@ClAI_+G922(~eztJXT{?Pm5o3t$#1JI4;?GJzw6D@r!lN&TeBF z>+H)Z>T|tgCab4z7`U_Nl;&Js;X922p(tNNJ$=T+P@C6nL^p%!oxK=AO~wVrgbV!( zwf^C}HDcYt zn zdfi=>#h`_TVAk`l+SrB5bp%CC=`EMe=f~Er*s-pJ<{6;7cA4F(3ukWlE{iPkG9Ore zS;#rmn8LnySarLZN~_<1^Xl`OSU)EIcHiFqpd0hX9qmw;pPeD;S!)oC(WH_O*Y&tR zP;-~{T~&+8>d(0yu%w|#ao8#LG^VOhnj$;Z)m`HdYT6WbY>T&H@A9?sS<}#_*X&`(>}k2C&ufcbWWFqWx6z^FX>A?3=&jp?KWLz++hV#zN9C`T=j@7guGzJ0 z4Go)`_%pCrZB6@x1BUi?&oT~ISKpb`vKdp^T;Kb&*QRRwH}(Os9oJ7uuBKMRI8DK% zmt|M<*hHOOQyt~pxV=d?SDUT*Z5Z1GsJC96_eRc14*wUlh(s-%f7%r!+{tJjsBUZoivpHGsJbVr_-55>tp@}!JoyX literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/zulrah/zulrah_melee.png b/runelite-client/src/main/resources/net/runelite/client/plugins/zulrah/zulrah_melee.png new file mode 100644 index 0000000000000000000000000000000000000000..344abc8fd38e9cd3fefb45f0d2ec1317b0b55844 GIT binary patch literal 18838 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 KT+Stv+y4h_;acSY literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/zulrah/zulrah_melee_small.png b/runelite-client/src/main/resources/net/runelite/client/plugins/zulrah/zulrah_melee_small.png new file mode 100644 index 0000000000000000000000000000000000000000..46b5d17129cce5d0101d4dad98e9388d76dd10f0 GIT binary patch literal 17530 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 OxVg-8KH~U&%)bF4k)%Wb literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/zulrah/zulrah_range.png b/runelite-client/src/main/resources/net/runelite/client/plugins/zulrah/zulrah_range.png new file mode 100644 index 0000000000000000000000000000000000000000..a1c3a75f67becc43e5518f655f6e6c5f892c65ae GIT binary patch literal 18644 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