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 c5fc4cf988..8b80351c29 100644 --- a/runelite-api/src/main/java/net/runelite/api/Actor.java +++ b/runelite-api/src/main/java/net/runelite/api/Actor.java @@ -226,4 +226,11 @@ public interface Actor extends Renderable * @return the world area */ WorldArea getWorldArea(); + + /** + * Gets the overhead text that is displayed above the actor + * + * @return the overhead text + */ + String getOverhead(); } diff --git a/runelite-api/src/main/java/net/runelite/api/NpcID.java b/runelite-api/src/main/java/net/runelite/api/NpcID.java index 34f1d804c9..7085ac9fc1 100644 --- a/runelite-api/src/main/java/net/runelite/api/NpcID.java +++ b/runelite-api/src/main/java/net/runelite/api/NpcID.java @@ -6221,6 +6221,7 @@ public final class NpcID public static final int GALLOW = 6775; public static final int MAN_6776 = 6776; public static final int MAZE_GUARDIAN = 6777; + public static final int MAZE_GUARDIAN_MOVING = 6778; public static final int MAZE_GUARDIAN_6779 = 6779; public static final int PILIAR = 6780; public static final int SHAYDA = 6781; diff --git a/runelite-api/src/main/java/net/runelite/api/ProjectileID.java b/runelite-api/src/main/java/net/runelite/api/ProjectileID.java index 4c53fbf423..34d5c54f3a 100644 --- a/runelite-api/src/main/java/net/runelite/api/ProjectileID.java +++ b/runelite-api/src/main/java/net/runelite/api/ProjectileID.java @@ -34,6 +34,8 @@ public class ProjectileID public static final int CANNONBALL = 53; public static final int GRANITE_CANNONBALL = 1443; + public static final int TELEKINETIC_SPELL = 143; + public static final int LIZARDMAN_SHAMAN_AOE = 1293; public static final int CRAZY_ARCHAEOLOGIST_AOE = 1260; public static final int ICE_DEMON_RANGED_AOE = 1324; diff --git a/runelite-api/src/main/java/net/runelite/api/coords/WorldArea.java b/runelite-api/src/main/java/net/runelite/api/coords/WorldArea.java index f8536941c4..ab5b32c81d 100644 --- a/runelite-api/src/main/java/net/runelite/api/coords/WorldArea.java +++ b/runelite-api/src/main/java/net/runelite/api/coords/WorldArea.java @@ -24,6 +24,8 @@ */ package net.runelite.api.coords; +import java.util.ArrayList; +import java.util.List; import java.util.function.Predicate; import lombok.Getter; import net.runelite.api.Client; @@ -643,4 +645,23 @@ public class WorldArea { return new WorldPoint(x, y, plane); } + + /** + * Accumulates all the WorldPoints that this WorldArea contains and returns them as a list + * + * @return Returns the WorldPoints in this WorldArea + */ + public List toWorldPointList() + { + List list = new ArrayList<>(width * height); + for (int x = 0; x < width; x++) + { + for (int y = 0; y < height; y++) + { + list.add(new WorldPoint(getX() + x, getY() + y, getPlane())); + } + } + + return list; + } } \ No newline at end of file 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 396629fe1b..8b965eab31 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 @@ -96,6 +96,10 @@ public class WidgetID public static final int KINGDOM_GROUP_ID = 392; public static final int BARROWS_GROUP_ID = 24; public static final int BLAST_MINE_GROUP_ID = 598; + public static final int MTA_ALCHEMY_GROUP_ID = 194; + public static final int MTA_ENCHANTMENT_GROUP_ID = 195; + public static final int MTA_GRAVEYARD_GROUP_ID = 196; + public static final int MTA_TELEKINETIC_GROUP_ID = 198; static class WorldMap { @@ -507,4 +511,10 @@ public class WidgetID static final int BARROWS_POTENTIAL = 9; static final int BARROWS_REWARD_INVENTORY = 3; } + + static class MTA + { + static final int BONUS_COMPONENT = 7; + static final int BONUS_TEXT_COMPONENT = 12; + } } 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 8caa62a3e7..cf1e172051 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 @@ -327,8 +327,11 @@ public enum WidgetInfo BARROWS_BROTHERS(WidgetID.BARROWS_GROUP_ID, WidgetID.Barrows.BARROWS_BROTHERS), BARROWS_POTENTIAL(WidgetID.BARROWS_GROUP_ID, WidgetID.Barrows.BARROWS_POTENTIAL), BARROWS_REWARD_INVENTORY(WidgetID.BARROWS_REWARD_GROUP_ID, WidgetID.Barrows.BARROWS_REWARD_INVENTORY), - - BLAST_MINE(WidgetID.BLAST_MINE_GROUP_ID, 0); + + BLAST_MINE(WidgetID.BLAST_MINE_GROUP_ID, 0), + + MTA_ENCHANTMENT_BONUS_TEXT(WidgetID.MTA_ENCHANTMENT_GROUP_ID, WidgetID.MTA.BONUS_TEXT_COMPONENT), + MTA_ENCHANTMENT_BONUS(WidgetID.MTA_ENCHANTMENT_GROUP_ID, WidgetID.MTA.BONUS_COMPONENT); private final int groupId; private final int childId; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/mta/MTAConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/mta/MTAConfig.java new file mode 100644 index 0000000000..d5e356d579 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/mta/MTAConfig.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018, Jasper Ketelaar + * 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.mta; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup( + keyName = "mta", + name = "Mage Training Arena", + description = "Configuration for the Mage Training Arena plugin" +) +public interface MTAConfig extends Config +{ + @ConfigItem( + keyName = "alchemy", + name = "Enable alchemy room", + description = "Configures whether or not the alchemy room overlay is enabled.", + position = 0 + ) + default boolean alchemy() + { + return true; + } + + @ConfigItem( + keyName = "graveyard", + name = "Enable graveyard room", + description = "Configures whether or not the graveyard room overlay is enabled.", + position = 1 + ) + default boolean graveyard() + { + return true; + } + + @ConfigItem( + keyName = "telekinetic", + name = "Enable telekinetic room", + description = "Configures whether or not the telekinetic room overlay is enabled.", + position = 2 + ) + default boolean telekinetic() + { + return true; + } + + @ConfigItem( + keyName = "enchantment", + name = "Enable enchantment room", + description = "Configures whether or not the enchantment room overlay is enabled.", + position = 3 + ) + default boolean enchantment() + { + return true; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/mta/MTAInventoryOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/mta/MTAInventoryOverlay.java new file mode 100644 index 0000000000..590b0ff65e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/mta/MTAInventoryOverlay.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018, Jasper Ketelaar + * 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.mta; + +import java.awt.Dimension; +import java.awt.Graphics2D; +import javax.inject.Inject; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; + +public class MTAInventoryOverlay extends Overlay +{ + private final MTAPlugin plugin; + + @Inject + public MTAInventoryOverlay(MTAPlugin plugin) + { + this.plugin = plugin; + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_WIDGETS); + } + + @Override + public Dimension render(Graphics2D graphics) + { + for (MTARoom room : plugin.getRooms()) + { + if (room.inside()) + { + graphics.setFont(FontManager.getRunescapeBoldFont()); + room.over(graphics); + } + } + + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/mta/MTAPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/mta/MTAPlugin.java new file mode 100644 index 0000000000..3e6ec44818 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/mta/MTAPlugin.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2018, Jasper Ketelaar + * 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.mta; + +import com.google.common.eventbus.EventBus; +import com.google.inject.Provides; +import javax.inject.Inject; +import lombok.AccessLevel; +import lombok.Getter; +import net.runelite.api.Client; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.mta.alchemy.AlchemyRoom; +import net.runelite.client.plugins.mta.enchantment.EnchantmentRoom; +import net.runelite.client.plugins.mta.graveyard.GraveyardRoom; +import net.runelite.client.plugins.mta.telekinetic.TelekineticRoom; +import net.runelite.client.ui.overlay.OverlayManager; + +@PluginDescriptor(name = "Mage Training Arena") +public class MTAPlugin extends Plugin +{ + @Inject + private Client client; + + @Inject + private OverlayManager overlayManager; + + @Inject + private AlchemyRoom alchemyRoom; + @Inject + private GraveyardRoom graveyardRoom; + @Inject + private TelekineticRoom telekineticRoom; + @Inject + private EnchantmentRoom enchantmentRoom; + + @Inject + private EventBus eventBus; + @Inject + private MTASceneOverlay sceneOverlay; + @Inject + private MTAInventoryOverlay inventoryOverlay; + + @Getter(AccessLevel.PROTECTED) + private MTARoom[] rooms; + + @Provides + public MTAConfig getConfig(ConfigManager manager) + { + return manager.getConfig(MTAConfig.class); + } + + @Override + public void startUp() + { + overlayManager.add(sceneOverlay); + overlayManager.add(inventoryOverlay); + + this.rooms = new MTARoom[]{alchemyRoom, graveyardRoom, telekineticRoom, enchantmentRoom}; + + for (MTARoom room : rooms) + { + eventBus.register(room); + } + } + + @Override + public void shutDown() + { + overlayManager.remove(sceneOverlay); + overlayManager.remove(inventoryOverlay); + + for (MTARoom room : rooms) + { + eventBus.unregister(room); + } + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/mta/MTARoom.java b/runelite-client/src/main/java/net/runelite/client/plugins/mta/MTARoom.java new file mode 100644 index 0000000000..a77f8170d8 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/mta/MTARoom.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018, Jasper Ketelaar + * 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.mta; + +import java.awt.Graphics2D; +import javax.inject.Inject; +import lombok.AccessLevel; +import lombok.Getter; + +public abstract class MTARoom +{ + @Getter(AccessLevel.PROTECTED) + protected final MTAConfig config; + + @Inject + protected MTARoom(MTAConfig config) + { + this.config = config; + } + + public abstract boolean inside(); + + public void under(Graphics2D graphics2D) + { + } + + public void over(Graphics2D graphics2D) + { + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/mta/MTASceneOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/mta/MTASceneOverlay.java new file mode 100644 index 0000000000..717bd86060 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/mta/MTASceneOverlay.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018, Jasper Ketelaar + * 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.mta; + +import java.awt.Dimension; +import java.awt.Graphics2D; +import javax.inject.Inject; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; + +public class MTASceneOverlay extends Overlay +{ + private final MTAPlugin plugin; + + @Inject + public MTASceneOverlay(MTAPlugin plugin) + { + this.plugin = plugin; + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + } + + @Override + public Dimension render(Graphics2D graphics) + { + for (MTARoom room : plugin.getRooms()) + { + if (room.inside()) + { + graphics.setFont(FontManager.getRunescapeFont()); + room.under(graphics); + } + } + + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/mta/alchemy/AlchemyItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/mta/alchemy/AlchemyItem.java new file mode 100644 index 0000000000..b059de0242 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/mta/alchemy/AlchemyItem.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018, Jasper Ketelaar + * 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.mta.alchemy; + +import lombok.Getter; +import net.runelite.api.ItemID; + +public enum AlchemyItem +{ + LEATHER_BOOTS("Leather Boots", ItemID.LEATHER_BOOTS_6893), + ADAMANT_KITESHIELD("Adamant Kiteshield", ItemID.ADAMANT_KITESHIELD_6894), + ADAMANT_MED_HELM("Helm", ItemID.ADAMANT_MED_HELM_6895), + EMERALD("Emerald", ItemID.EMERALD_6896), + RUNE_LONGSWORD("Rune Longsword", ItemID.RUNE_LONGSWORD_6897), + EMPTY("", -1), + POSSIBLY_EMPTY("", ItemID.CAKE_OF_GUIDANCE), + UNKNOWN("Unknown", ItemID.CAKE_OF_GUIDANCE); + + @Getter + private final int id; + @Getter + private final String name; + + AlchemyItem(String name, int id) + { + this.id = id; + this.name = name; + } + + public static AlchemyItem find(String item) + { + for (AlchemyItem alchemyItem : values()) + { + if (item.toLowerCase().contains(alchemyItem.name.toLowerCase())) + { + return alchemyItem; + } + } + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/mta/alchemy/AlchemyRoom.java b/runelite-client/src/main/java/net/runelite/client/plugins/mta/alchemy/AlchemyRoom.java new file mode 100644 index 0000000000..dd7f1965ea --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/mta/alchemy/AlchemyRoom.java @@ -0,0 +1,475 @@ +/* + * Copyright (c) 2018, Jasper Ketelaar + * Copyright (c) 2018, 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.mta.alchemy; + +import com.google.common.eventbus.Subscribe; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.util.Arrays; +import java.util.Objects; +import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.ChatMessageType; +import net.runelite.api.Client; +import net.runelite.api.GameObject; +import net.runelite.api.GameState; +import static net.runelite.api.ObjectID.CUPBOARD_23678; +import static net.runelite.api.ObjectID.CUPBOARD_23679; +import static net.runelite.api.ObjectID.CUPBOARD_23680; +import static net.runelite.api.ObjectID.CUPBOARD_23681; +import static net.runelite.api.ObjectID.CUPBOARD_23682; +import static net.runelite.api.ObjectID.CUPBOARD_23683; +import static net.runelite.api.ObjectID.CUPBOARD_23684; +import static net.runelite.api.ObjectID.CUPBOARD_23685; +import static net.runelite.api.ObjectID.CUPBOARD_23686; +import static net.runelite.api.ObjectID.CUPBOARD_23687; +import static net.runelite.api.ObjectID.CUPBOARD_23688; +import static net.runelite.api.ObjectID.CUPBOARD_23689; +import static net.runelite.api.ObjectID.CUPBOARD_23690; +import static net.runelite.api.ObjectID.CUPBOARD_23691; +import static net.runelite.api.ObjectID.CUPBOARD_23692; +import static net.runelite.api.ObjectID.CUPBOARD_23693; +import net.runelite.api.Perspective; +import net.runelite.api.Player; +import net.runelite.api.Point; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.GameObjectSpawned; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; +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.game.ItemManager; +import net.runelite.client.plugins.mta.MTAConfig; +import net.runelite.client.plugins.mta.MTAPlugin; +import net.runelite.client.plugins.mta.MTARoom; +import net.runelite.client.ui.overlay.infobox.InfoBoxManager; + +@Slf4j +public class AlchemyRoom extends MTARoom +{ + private static final int MTA_ALCH_REGION = 13462; + + private static final int IMAGE_Z_OFFSET = 150; + private static final int NUM_CUPBOARDS = 8; + private static final int INFO_START = 5; + private static final int BEST_POINTS = 30; + + private static final String YOU_FOUND = "You found:"; + private static final String EMPTY = "The cupboard is empty."; + + private final Cupboard[] cupboards = new Cupboard[NUM_CUPBOARDS]; + + private final MTAPlugin plugin; + private final Client client; + private final ItemManager itemManager; + private final InfoBoxManager infoBoxManager; + + private AlchemyItem best; + private Cupboard suggestion; + + @Inject + private AlchemyRoom(Client client, MTAConfig config, MTAPlugin plugin, ItemManager itemManager, InfoBoxManager infoBoxManager) + { + super(config); + this.client = client; + this.plugin = plugin; + this.itemManager = itemManager; + this.infoBoxManager = infoBoxManager; + } + + @Subscribe + public void onGameTick(GameTick event) + { + if (!inside() || !config.alchemy()) + { + return; + } + + AlchemyItem bestItem = getBest(); + if (best == null || best != bestItem) + { + if (best != null) + { + infoBoxManager.removeIf(e -> e instanceof AlchemyRoomTimer); + infoBoxManager.addInfoBox(new AlchemyRoomTimer(plugin)); + } + + log.debug("Item change to {}!", best); + + best = bestItem; + // Reset items to unknown + Arrays.stream(cupboards) + .filter(Objects::nonNull) + .forEach(e -> e.alchemyItem = AlchemyItem.UNKNOWN); + } + + Cupboard newSuggestion = getSuggestion(); + if (suggestion == null || newSuggestion == null || suggestion.alchemyItem != newSuggestion.alchemyItem) + { + suggestion = newSuggestion; + } + } + + + @Subscribe + public void onGameObjectSpawned(GameObjectSpawned event) + { + if (!inside()) + { + return; + } + + GameObject spawn = event.getGameObject(); + int cupboardId; + + switch (spawn.getId()) + { + // Closed and opened versions of each + case CUPBOARD_23678: + case CUPBOARD_23679: + cupboardId = 0; + break; + + case CUPBOARD_23680: + case CUPBOARD_23681: + cupboardId = 1; + break; + + case CUPBOARD_23682: + case CUPBOARD_23683: + cupboardId = 2; + break; + + case CUPBOARD_23684: + case CUPBOARD_23685: + cupboardId = 3; + break; + + case CUPBOARD_23686: + case CUPBOARD_23687: + cupboardId = 4; + break; + + case CUPBOARD_23688: + case CUPBOARD_23689: + cupboardId = 5; + break; + + case CUPBOARD_23690: + case CUPBOARD_23691: + cupboardId = 6; + break; + + case CUPBOARD_23692: + case CUPBOARD_23693: + cupboardId = 7; + break; + + default: + return; + + } + Cupboard cupboard = cupboards[cupboardId]; + if (cupboard != null) + { + cupboard.gameObject = spawn; + } + else + { + cupboard = new Cupboard(); + cupboard.gameObject = spawn; + cupboard.alchemyItem = AlchemyItem.UNKNOWN; + cupboards[cupboardId] = cupboard; + } + } + + @Subscribe + public void onGameStateChanged(GameStateChanged gameStateChanged) + { + if (gameStateChanged.getGameState() == GameState.LOGGED_IN) + { + if (!inside()) + { + reset(); + } + } + } + + @Subscribe + public void onChatMessage(ChatMessage wrapper) + { + if (!inside() || !config.alchemy()) + { + return; + } + + String message = wrapper.getMessage(); + + if (wrapper.getType() == ChatMessageType.SERVER) + { + if (message.contains(YOU_FOUND)) + { + String item = message.replace(YOU_FOUND, "").trim(); + AlchemyItem alchemyItem = AlchemyItem.find(item); + Cupboard clicked = getClicked(); + if (clicked.alchemyItem != alchemyItem) + { + fill(clicked, alchemyItem); + } + } + else if (message.equals(EMPTY)) + { + Cupboard clicked = getClicked(); + + int idx = Arrays.asList(cupboards).indexOf(clicked); + for (int i = -2; i <= 2; ++i) + { + int j = (idx + i) % 8; + if (j < 0) + { + j = 8 + j; + } + + Cupboard cupboard = cupboards[j]; + if (cupboard != null && cupboard.alchemyItem == AlchemyItem.UNKNOWN) + { + cupboard.alchemyItem = AlchemyItem.POSSIBLY_EMPTY; + } + } + + clicked.alchemyItem = AlchemyItem.EMPTY; + } + } + } + + + private void reset() + { + Arrays.fill(cupboards, null); + } + + @Override + public boolean inside() + { + Player player = client.getLocalPlayer(); + return player != null && player.getWorldLocation().getRegionID() == MTA_ALCH_REGION + && player.getWorldLocation().getPlane() == 2; + } + + private AlchemyItem getBest() + { + for (int i = 0; i < INFO_START; i++) + { + int index = i + INFO_START; + + Widget textWidget = client.getWidget(WidgetID.MTA_ALCHEMY_GROUP_ID, index); + + if (textWidget == null) + { + return null; + } + + String item = textWidget.getText().replace(":", ""); + Widget pointsWidget = client.getWidget(WidgetID.MTA_ALCHEMY_GROUP_ID, index + INFO_START); + int points = Integer.parseInt(pointsWidget.getText()); + + if (points == BEST_POINTS) + { + return AlchemyItem.find(item); + } + } + + return null; + } + + private Cupboard getClicked() + { + Cupboard nearest = null; + double distance = Double.MAX_VALUE; + + WorldPoint mine = client.getLocalPlayer().getWorldLocation(); + + for (Cupboard cupboard : cupboards) + { + if (cupboard == null) + { + continue; + } + + double objectDistance = cupboard.gameObject.getWorldLocation().distanceTo(mine); + + if (nearest == null || objectDistance < distance) + { + nearest = cupboard; + distance = objectDistance; + } + } + + return nearest; + } + + private void fill(Cupboard cupboard, AlchemyItem alchemyItem) + { + int idx = Arrays.asList(cupboards).indexOf(cupboard); + assert idx != -1; + + int itemIdx = alchemyItem.ordinal(); + + log.debug("Filling cupboard {} with {}", idx, alchemyItem); + + for (int i = 0; i < NUM_CUPBOARDS; ++i) + { + int cupIdx = (idx + i) % NUM_CUPBOARDS; + int itemIndex = (itemIdx + i) % NUM_CUPBOARDS; + cupboards[cupIdx].alchemyItem = itemIndex <= 4 ? AlchemyItem.values()[itemIndex] : AlchemyItem.EMPTY; + } + } + + @Override + public void under(Graphics2D graphics) + { + if (!getConfig().alchemy() || best == null || !inside()) + { + return; + } + + boolean found = false; + + for (Cupboard cupboard : cupboards) + { + if (cupboard == null) + { + continue; + } + + GameObject object = cupboard.gameObject; + AlchemyItem alchemyItem = cupboard.alchemyItem; + + if (alchemyItem == AlchemyItem.EMPTY) + { + continue; + } + + if (alchemyItem == best) + { + client.setHintArrow(object.getWorldLocation()); + found = true; + } + + BufferedImage image = itemManager.getImage(alchemyItem.getId()); + Point canvasLoc = Perspective.getCanvasImageLocation(client, graphics, object.getLocalLocation(), image, IMAGE_Z_OFFSET); + + if (canvasLoc != null) + { + graphics.drawImage(image, canvasLoc.getX(), canvasLoc.getY(), null); + } + } + + if (!found && suggestion != null) + { + client.setHintArrow(suggestion.gameObject.getWorldLocation()); + } + + } + + private Cupboard getSuggestion() + { + // check if a cupboard has the best item in it + if (best != null) + { + for (Cupboard cupboard : cupboards) + { + if (cupboard != null && cupboard.alchemyItem == best) + { + return cupboard; + } + } + } + + // otherwise find the closest cupboard which can not be empty + Cupboard nearest = null; + int distance = -1; + + WorldPoint mine = client.getLocalPlayer().getWorldLocation(); + + for (Cupboard cupboard : cupboards) + { + if (cupboard == null || cupboard.alchemyItem == AlchemyItem.EMPTY || cupboard.alchemyItem == AlchemyItem.POSSIBLY_EMPTY) + { + continue; + } + + int objectDistance = (int) cupboard.gameObject.getWorldLocation().distanceTo(mine); + + if (nearest == null || objectDistance < distance) + { + nearest = cupboard; + distance = objectDistance; + } + } + + return nearest; + } + + + @Override + public void over(Graphics2D graphics) + { + if (!inside() || !config.alchemy() || best == null) + { + return; + } + + Widget inventory = client.getWidget(WidgetInfo.INVENTORY); + if (inventory.isHidden()) + { + return; + } + + for (WidgetItem item : inventory.getWidgetItems()) + { + if (item.getId() != best.getId()) + { + continue; + } + + drawItem(graphics, item, Color.GREEN); + } + } + + private void drawItem(Graphics2D graphics, WidgetItem item, Color border) + { + Rectangle bounds = item.getCanvasBounds(); + graphics.setColor(border); + graphics.draw(bounds); + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/mta/alchemy/AlchemyRoomTimer.java b/runelite-client/src/main/java/net/runelite/client/plugins/mta/alchemy/AlchemyRoomTimer.java new file mode 100644 index 0000000000..1cde50d6f3 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/mta/alchemy/AlchemyRoomTimer.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018, Jasper Ketelaar + * 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.mta.alchemy; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.time.temporal.ChronoUnit; +import javax.imageio.ImageIO; +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.ui.overlay.infobox.Timer; + +@Slf4j +public class AlchemyRoomTimer extends Timer +{ + private static final int RESET_PERIOD = 42; + private static BufferedImage image; + + public AlchemyRoomTimer(Plugin plugin) + { + super(RESET_PERIOD, ChronoUnit.SECONDS, getResetImage(), plugin); + this.setTooltip("Time until items swap"); + } + + private static BufferedImage getResetImage() + { + if (image != null) + { + return image; + } + + try + { + synchronized (ImageIO.class) + { + image = ImageIO.read(AlchemyRoomTimer.class.getResourceAsStream("reset.png")); + } + } + catch (IOException ex) + { + log.warn(null, ex); + } + + return image; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/mta/alchemy/Cupboard.java b/runelite-client/src/main/java/net/runelite/client/plugins/mta/alchemy/Cupboard.java new file mode 100644 index 0000000000..3467e3a188 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/mta/alchemy/Cupboard.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018, 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.mta.alchemy; + +import net.runelite.api.GameObject; + +class Cupboard +{ + GameObject gameObject; + AlchemyItem alchemyItem; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/mta/enchantment/EnchantmentRoom.java b/runelite-client/src/main/java/net/runelite/client/plugins/mta/enchantment/EnchantmentRoom.java new file mode 100644 index 0000000000..b74c696b6d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/mta/enchantment/EnchantmentRoom.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2018, Jasper Ketelaar + * 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.mta.enchantment; + +import com.google.common.eventbus.Subscribe; +import java.util.ArrayList; +import java.util.List; +import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.Item; +import net.runelite.api.ItemID; +import net.runelite.api.ItemLayer; +import net.runelite.api.Player; +import net.runelite.api.Tile; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.ItemLayerChanged; +import net.runelite.client.plugins.mta.MTAConfig; +import net.runelite.client.plugins.mta.MTARoom; + +@Slf4j +public class EnchantmentRoom extends MTARoom +{ + private static final int MTA_ENCHANT_REGION = 13462; + + private final Client client; + private final List dragonstones = new ArrayList<>(); + + @Inject + private EnchantmentRoom(MTAConfig config, Client client) + { + super(config); + this.client = client; + } + + @Subscribe + public void onGameStateChanged(GameStateChanged gameStateChanged) + { + if (gameStateChanged.getGameState() == GameState.LOGGED_IN) + { + if (!inside()) + { + dragonstones.clear(); + } + } + } + + @Subscribe + public void onGameTick(GameTick event) + { + if (!inside() || !config.enchantment()) + { + return; + } + + WorldPoint nearest = findNearestStone(); + if (nearest != null) + { + client.setHintArrow(nearest); + } + } + + private WorldPoint findNearestStone() + { + WorldPoint nearest = null; + double dist = Double.MAX_VALUE; + WorldPoint local = client.getLocalPlayer().getWorldLocation(); + for (WorldPoint worldPoint : dragonstones) + { + double currDist = local.distanceTo(worldPoint); + if (nearest == null || currDist < dist) + { + dist = currDist; + nearest = worldPoint; + } + } + return nearest; + } + + @Subscribe + public void onItemLayerChanged(ItemLayerChanged event) + { + if (!inside()) + { + return; + } + + Tile changed = event.getTile(); + ItemLayer itemLayer = changed.getItemLayer(); + WorldPoint worldPoint = changed.getWorldLocation(); + + List groundItems = changed.getGroundItems(); + if (groundItems == null) + { + boolean removed = dragonstones.remove(worldPoint); + if (removed) + { + log.debug("Removed dragonstone at {}", worldPoint); + } + return; + } + + for (Item item : changed.getGroundItems()) + { + if (item.getId() == ItemID.DRAGONSTONE_6903) + { + log.debug("Adding dragonstone at {}", worldPoint); + dragonstones.add(worldPoint); + return; + } + } + + boolean removed = dragonstones.remove(worldPoint); + if (removed) + { + log.debug("Removed dragonstone at {}", worldPoint); + } + } + + @Override + public boolean inside() + { + Player player = client.getLocalPlayer(); + return player != null && player.getWorldLocation().getRegionID() == MTA_ENCHANT_REGION + && player.getWorldLocation().getPlane() == 0; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/mta/graveyard/GraveyardCounter.java b/runelite-client/src/main/java/net/runelite/client/plugins/mta/graveyard/GraveyardCounter.java new file mode 100644 index 0000000000..83e74cc6d6 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/mta/graveyard/GraveyardCounter.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018, Jasper Ketelaar + * 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.mta.graveyard; + +import java.awt.Color; +import java.awt.image.BufferedImage; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.ui.overlay.infobox.Counter; + +public class GraveyardCounter extends Counter +{ + private int count; + + public GraveyardCounter(BufferedImage image, Plugin plugin) + { + super(image, plugin, "0"); + } + + public void setCount(int count) + { + this.count = count; + this.setText(String.valueOf(count)); + } + + @Override + public Color getTextColor() + { + if (count >= GraveyardRoom.MIN_SCORE) + { + return Color.GREEN; + } + else if (count == 0) + { + return Color.RED; + } + else + { + return Color.ORANGE; + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/mta/graveyard/GraveyardRoom.java b/runelite-client/src/main/java/net/runelite/client/plugins/mta/graveyard/GraveyardRoom.java new file mode 100644 index 0000000000..861174a799 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/mta/graveyard/GraveyardRoom.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2018, Jasper Ketelaar + * 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.mta.graveyard; + +import com.google.common.eventbus.Subscribe; +import java.awt.image.BufferedImage; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.InventoryID; +import net.runelite.api.Item; +import net.runelite.api.ItemContainer; +import static net.runelite.api.ItemID.ANIMALS_BONES; +import static net.runelite.api.ItemID.ANIMALS_BONES_6905; +import static net.runelite.api.ItemID.ANIMALS_BONES_6906; +import static net.runelite.api.ItemID.ANIMALS_BONES_6907; +import net.runelite.api.Player; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.ItemContainerChanged; +import net.runelite.client.game.ItemManager; +import net.runelite.client.plugins.mta.MTAConfig; +import net.runelite.client.plugins.mta.MTAPlugin; +import net.runelite.client.plugins.mta.MTARoom; +import net.runelite.client.ui.overlay.infobox.InfoBoxManager; + +public class GraveyardRoom extends MTARoom +{ + private static final int MTA_GRAVEYARD_REGION = 13462; + + static final int MIN_SCORE = 16; + + private final Client client; + private final MTAPlugin plugin; + private final ItemManager itemManager; + private final InfoBoxManager infoBoxManager; + private int score; + + private GraveyardCounter counter; + + @Inject + private GraveyardRoom(MTAConfig config, Client client, MTAPlugin plugin, + ItemManager itemManager, InfoBoxManager infoBoxManager) + { + super(config); + this.client = client; + this.plugin = plugin; + this.itemManager = itemManager; + this.infoBoxManager = infoBoxManager; + } + + @Override + public boolean inside() + { + Player player = client.getLocalPlayer(); + return player != null && player.getWorldLocation().getRegionID() == MTA_GRAVEYARD_REGION + && player.getWorldLocation().getPlane() == 1; + } + + @Subscribe + public void onGameTick(GameTick tick) + { + if (!inside() || !config.graveyard()) + { + if (this.counter != null) + { + infoBoxManager.removeIf(e -> e instanceof GraveyardCounter); + this.counter = null; + } + } + } + + @Subscribe + public void itemContainerChanged(ItemContainerChanged event) + { + if (!inside()) + { + return; + } + + ItemContainer container = event.getItemContainer(); + + if (container == client.getItemContainer(InventoryID.INVENTORY)) + { + this.score = score(container.getItems()); + + if (counter == null) + { + BufferedImage image = itemManager.getImage(ANIMALS_BONES); + counter = new GraveyardCounter(image, plugin); + infoBoxManager.addInfoBox(counter); + } + counter.setCount(score); + } + } + + private int score(Item[] items) + { + int score = 0; + + if (items == null) + { + return score; + } + + for (Item item : items) + { + score += getPoints(item.getId()); + } + + return score; + } + + private int getPoints(int id) + { + switch (id) + { + case ANIMALS_BONES: + return 1; + case ANIMALS_BONES_6905: + return 2; + case ANIMALS_BONES_6906: + return 3; + case ANIMALS_BONES_6907: + return 4; + default: + return 0; + } + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/mta/telekinetic/Maze.java b/runelite-client/src/main/java/net/runelite/client/plugins/mta/telekinetic/Maze.java new file mode 100644 index 0000000000..7e884dffc1 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/mta/telekinetic/Maze.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018, Jasper Ketelaar + * 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.mta.telekinetic; + +import net.runelite.api.coords.LocalPoint; + +public enum Maze +{ + MAZE_1(100, new LocalPoint(6848, 3904)), + MAZE_2(124, new LocalPoint(4928, 6848)), + MAZE_3(129, new LocalPoint(7104, 5312)), + MAZE_4(53, new LocalPoint(6208, 4928)), + MAZE_5(108, new LocalPoint(5056, 5184)), + MAZE_6(121, new LocalPoint(3648, 5440)), + MAZE_7(71, new LocalPoint(6080, 5696)), + MAZE_8(98, new LocalPoint(5952, 7360)), + MAZE_9(87, new LocalPoint(5184, 6208)), + MAZE_10(91, new LocalPoint(5440, 9024)); + + private final int walls; + private final LocalPoint start; + + Maze(int walls, LocalPoint start) + { + this.walls = walls; + this.start = start; + } + + public static Maze fromWalls(int walls) + { + for (Maze maze : values()) + { + if (maze.getWalls() == walls) + { + return maze; + } + } + + return null; + } + + public int getWalls() + { + return walls; + } + + public LocalPoint getStart() + { + return start; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/mta/telekinetic/TelekineticRoom.java b/runelite-client/src/main/java/net/runelite/client/plugins/mta/telekinetic/TelekineticRoom.java new file mode 100644 index 0000000000..5b2f4a42b6 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/mta/telekinetic/TelekineticRoom.java @@ -0,0 +1,512 @@ +/* + * Copyright (c) 2018, Jasper Ketelaar + * 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.mta.telekinetic; + +import com.google.common.eventbus.Subscribe; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Polygon; +import java.awt.Rectangle; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; +import java.util.Set; +import java.util.Stack; +import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.GroundObject; +import net.runelite.api.NPC; +import net.runelite.api.NpcID; +import net.runelite.api.NullObjectID; +import net.runelite.api.Perspective; +import net.runelite.api.Projectile; +import net.runelite.api.ProjectileID; +import net.runelite.api.WallObject; +import net.runelite.api.coords.Angle; +import net.runelite.api.coords.Direction; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.coords.WorldArea; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.NpcDespawned; +import net.runelite.api.events.NpcSpawned; +import net.runelite.api.queries.GroundObjectQuery; +import net.runelite.api.queries.WallObjectQuery; +import net.runelite.api.widgets.WidgetID; +import net.runelite.client.plugins.mta.MTAConfig; +import net.runelite.client.plugins.mta.MTARoom; + +@Slf4j +public class TelekineticRoom extends MTARoom +{ + private static final int TELEKINETIC_WALL = NullObjectID.NULL_10755; + private static final int TELEKINETIC_FINISH = NullObjectID.NULL_23672; + + private final Client client; + + private Stack moves = new Stack<>(); + private LocalPoint destination; + private WorldPoint location; + private Rectangle bounds; + private NPC guardian; + private Maze maze; + + @Inject + private TelekineticRoom(MTAConfig config, Client client) + { + super(config); + this.client = client; + } + + @Subscribe + public void onGameTick(GameTick event) + { + if (!config.telekinetic() + || !inside() + || client.getGameState() != GameState.LOGGED_IN) + { + maze = null; + moves.clear(); + return; + } + + WallObjectQuery qry = new WallObjectQuery() + .idEquals(TELEKINETIC_WALL); + WallObject[] result = qry.result(client); + int length = result.length; + + if (maze == null || length != maze.getWalls()) + { + bounds = getBounds(result); + maze = Maze.fromWalls(length); + client.clearHintArrow(); + } + else if (guardian != null) + { + WorldPoint current; + if (guardian.getId() == NpcID.MAZE_GUARDIAN_MOVING) + { + destination = getGuardianDestination(); + current = WorldPoint.fromLocal(client, destination); + } + else + { + destination = null; + current = guardian.getWorldLocation(); + } + + //Prevent unnecessary updating when the guardian has not moved + if (current.equals(location)) + { + return; + } + + log.debug("Updating guarding location {} -> {}", location, current); + + location = current; + + if (location.equals(finish())) + { + client.clearHintArrow(); + } + else + { + for (Projectile projectile : client.getProjectiles()) + { + if (projectile.getId() == ProjectileID.TELEKINETIC_SPELL) + { + return; + } + } + + log.debug("Rebuilding moves due to guardian move"); + this.moves = build(); + } + + } + else + { + client.clearHintArrow(); + moves.clear(); + } + } + + @Subscribe + public void onNpcSpawned(NpcSpawned event) + { + NPC npc = event.getNpc(); + + if (npc.getId() == NpcID.MAZE_GUARDIAN) + { + guardian = npc; + } + } + + @Subscribe + public void onNpcDespawned(NpcDespawned event) + { + NPC npc = event.getNpc(); + + if (npc == guardian) + { + guardian = null; + } + } + + @Override + public boolean inside() + { + return client.getWidget(WidgetID.MTA_TELEKINETIC_GROUP_ID, 0) != null; + } + + @Override + public void under(Graphics2D graphics2D) + { + if (inside() && maze != null && guardian != null) + { + if (destination != null) + { + graphics2D.setColor(Color.ORANGE); + renderLocalPoint(graphics2D, destination); + } + if (!moves.isEmpty()) + { + if (moves.peek() == getPosition()) + { + graphics2D.setColor(Color.GREEN); + } + else + { + graphics2D.setColor(Color.RED); + } + + Polygon tile = Perspective.getCanvasTilePoly(client, guardian.getLocalLocation()); + if (tile != null) + { + graphics2D.drawPolygon(tile); + } + + WorldPoint optimal = optimal(); + + if (optimal != null) + { + client.setHintArrow(optimal); + renderWorldPoint(graphics2D, optimal); + } + } + } + } + + private WorldPoint optimal() + { + WorldPoint current = client.getLocalPlayer().getWorldLocation(); + + Direction next = moves.pop(); + WorldArea areaNext = getIndicatorLine(next); + WorldPoint nearestNext = nearest(areaNext, current); + + if (moves.isEmpty()) + { + moves.push(next); + + return nearestNext; + } + + Direction after = moves.peek(); + moves.push(next); + WorldArea areaAfter = getIndicatorLine(after); + WorldPoint nearestAfter = nearest(areaAfter, nearestNext); + + return nearest(areaNext, nearestAfter); + } + + private static int manhattan(WorldPoint point1, WorldPoint point2) + { + return Math.abs(point1.getX() - point2.getX()) + Math.abs(point2.getY() - point1.getY()); + } + + private WorldPoint nearest(WorldArea area, WorldPoint worldPoint) + { + int dist = Integer.MAX_VALUE; + WorldPoint nearest = null; + + for (WorldPoint areaPoint : area.toWorldPointList()) + { + int currDist = manhattan(areaPoint, worldPoint); + if (nearest == null || dist > currDist) + { + nearest = areaPoint; + dist = currDist; + } + } + + return nearest; + } + + private void renderWorldPoint(Graphics2D graphics, WorldPoint worldPoint) + { + renderLocalPoint(graphics, LocalPoint.fromWorld(client, worldPoint)); + } + + private void renderLocalPoint(Graphics2D graphics, LocalPoint local) + { + if (local != null) + { + Polygon canvasTilePoly = Perspective.getCanvasTilePoly(client, local); + if (canvasTilePoly != null) + { + graphics.drawPolygon(canvasTilePoly); + } + } + } + + private Stack build() + { + if (guardian.getId() == NpcID.MAZE_GUARDIAN_MOVING) + { + WorldPoint converted = WorldPoint.fromLocal(client, getGuardianDestination()); + return build(converted); + } + else + { + return build(guardian.getWorldLocation()); + } + } + + private LocalPoint getGuardianDestination() + { + Angle angle = new Angle(guardian.getOrientation()); + Direction facing = angle.getNearestDirection(); + return neighbour(guardian.getLocalLocation(), facing); + } + + private Stack build(WorldPoint start) + { + LocalPoint finish = finish(); + + Queue visit = new LinkedList<>(); + Set closed = new HashSet<>(); + Map scores = new HashMap<>(); + Map edges = new HashMap<>(); + scores.put(start, 0); + visit.add(start); + + while (!visit.isEmpty()) + { + WorldPoint next = visit.poll(); + closed.add(next); + + LocalPoint localNext = LocalPoint.fromWorld(client, next); + LocalPoint[] neighbours = neighbours(localNext); + + for (LocalPoint neighbour : neighbours) + { + if (neighbour == null) + { + continue; + } + + WorldPoint nghbWorld = WorldPoint.fromLocal(client, neighbour); + + if (!nghbWorld.equals(next) + && !closed.contains(nghbWorld)) + { + int score = scores.get(next) + 1; + + if (!scores.containsKey(nghbWorld) || scores.get(nghbWorld) > score) + { + scores.put(nghbWorld, score); + edges.put(nghbWorld, next); + visit.add(nghbWorld); + } + } + } + } + + return build(edges, WorldPoint.fromLocal(client, finish)); + } + + private Stack build(Map edges, WorldPoint finish) + { + Stack path = new Stack<>(); + WorldPoint current = finish; + + while (edges.containsKey(current)) + { + WorldPoint next = edges.get(current); + + if (next.getX() > current.getX()) + { + path.add(Direction.WEST); + } + else if (next.getX() < current.getX()) + { + path.add(Direction.EAST); + } + else if (next.getY() > current.getY()) + { + path.add(Direction.SOUTH); + } + else + { + path.add(Direction.NORTH); + } + + current = next; + } + + return path; + } + + private LocalPoint[] neighbours(LocalPoint point) + { + return new LocalPoint[] + { + neighbour(point, Direction.NORTH), neighbour(point, Direction.SOUTH), + neighbour(point, Direction.EAST), neighbour(point, Direction.WEST) + }; + } + + private LocalPoint neighbour(LocalPoint point, Direction direction) + { + WorldPoint worldPoint = WorldPoint.fromLocal(client, point); + WorldArea area = new WorldArea(worldPoint, 1, 1); + + int dx, dy; + + switch (direction) + { + case NORTH: + dx = 0; + dy = 1; + break; + case SOUTH: + dx = 0; + dy = -1; + break; + case EAST: + dx = 1; + dy = 0; + break; + case WEST: + dx = -1; + dy = 0; + break; + default: + throw new IllegalStateException(); + } + + while (area.canTravelInDirection(client, dx, dy)) + { + worldPoint = area.toWorldPoint() + .dx(dx) + .dy(dy); + area = new WorldArea(worldPoint, 1, 1); + } + + return LocalPoint.fromWorld(client, worldPoint); + } + + private LocalPoint finish() + { + GroundObjectQuery qry = new GroundObjectQuery() + .idEquals(TELEKINETIC_FINISH); + + GroundObject[] result = qry.result(client); + + if (result.length > 0) + { + return result[0].getLocalLocation(); + } + + return null; + } + + private Rectangle getBounds(WallObject[] walls) + { + int minX = Integer.MAX_VALUE; + int minY = Integer.MAX_VALUE; + + int maxX = Integer.MIN_VALUE; + int maxY = Integer.MIN_VALUE; + + for (WallObject wall : walls) + { + WorldPoint point = wall.getWorldLocation(); + minX = Math.min(minX, point.getX()); + minY = Math.min(minY, point.getY()); + + maxX = Math.max(maxX, point.getX()); + maxY = Math.max(maxY, point.getY()); + } + + return new Rectangle(minX, minY, maxX - minX, maxY - minY); + } + + private Direction getPosition() + { + WorldPoint mine = client.getLocalPlayer().getWorldLocation(); + + if (mine.getY() >= bounds.getMaxY() && mine.getX() < bounds.getMaxX() && mine.getX() > bounds.getX()) + { + return Direction.NORTH; + } + else if (mine.getY() <= bounds.getY() && mine.getX() < bounds.getMaxX() && mine.getX() > bounds.getX()) + { + return Direction.SOUTH; + } + else if (mine.getX() >= bounds.getMaxX() && mine.getY() < bounds.getMaxY() && mine.getY() > bounds.getY()) + { + return Direction.EAST; + } + else if (mine.getX() <= bounds.getX() && mine.getY() < bounds.getMaxY() && mine.getY() > bounds.getY()) + { + return Direction.WEST; + } + + return null; + } + + private WorldArea getIndicatorLine(Direction direction) + { + switch (direction) + { + case NORTH: + return new WorldArea(bounds.x + 1, (int) bounds.getMaxY(), bounds.width - 1, 1, 0); + case SOUTH: + return new WorldArea(bounds.x + 1, bounds.y, bounds.width - 1, 1, 0); + case WEST: + return new WorldArea(bounds.x, bounds.y + 1, 1, bounds.height - 1, 0); + case EAST: + return new WorldArea((int) bounds.getMaxX(), bounds.y + 1, 1, bounds.height - 1, 0); + } + + return null; + } +} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/mta/alchemy/reset.png b/runelite-client/src/main/resources/net/runelite/client/plugins/mta/alchemy/reset.png new file mode 100644 index 0000000000..e94f6102a5 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/mta/alchemy/reset.png differ diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSActor.java b/runescape-api/src/main/java/net/runelite/rs/api/RSActor.java index d159fb1462..efc8b820df 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSActor.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSActor.java @@ -33,6 +33,7 @@ public interface RSActor extends RSRenderable, Actor int getRSInteracting(); @Import("overhead") + @Override String getOverhead(); @Import("x")