From f627c9224fcedbad1e62c07953c836feee1f318a Mon Sep 17 00:00:00 2001 From: Shaun Dreclin Date: Mon, 23 Jul 2018 13:07:35 -0400 Subject: [PATCH] client: Add Tarn's Lair plugin --- .../client/plugins/tarnslair/Obstacles.java | 131 ++++++++++++++ .../plugins/tarnslair/TarnsLairOverlay.java | 107 +++++++++++ .../plugins/tarnslair/TarnsLairPlugin.java | 171 ++++++++++++++++++ 3 files changed, 409 insertions(+) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/tarnslair/Obstacles.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/tarnslair/TarnsLairOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/tarnslair/TarnsLairPlugin.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/tarnslair/Obstacles.java b/runelite-client/src/main/java/net/runelite/client/plugins/tarnslair/Obstacles.java new file mode 100644 index 0000000000..b26823c4a8 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/tarnslair/Obstacles.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2018, Shaun Dreclin + * 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.tarnslair; + +import com.google.common.collect.Sets; +import java.util.Set; +import static net.runelite.api.NullObjectID.NULL_20575; +import static net.runelite.api.ObjectID.*; + +class Obstacles +{ + static final Set STAIRCASE_IDS = Sets.newHashSet( + PASSAGEWAY_15770, /*Wall staircase*/ + PASSAGEWAY_15771, /*Wall staircase*/ + PASSAGEWAY_15772, /*Wall staircase*/ + PASSAGEWAY_15773, /*Wall staircase*/ + PASSAGEWAY_15774, /*Wall staircase*/ + PASSAGEWAY_16129, /*Wall staircase*/ + PASSAGEWAY_16130, /*Wall staircase*/ + PASSAGEWAY_16131, /*Wall staircase*/ + PASSAGEWAY_16132, /*Wall staircase*/ + PASSAGEWAY_16133, /*Wall staircase*/ + PASSAGEWAY_16134, /*Wall staircase*/ + PASSAGEWAY_18307, /*Wall staircase*/ + PASSAGEWAY_18308, /*Wall staircase*/ + PASSAGEWAY_18309, /*Wall staircase*/ + PASSAGEWAY_18310, /*Wall staircase*/ + PASSAGEWAY_18311, /*Wall staircase*/ + PASSAGEWAY_20488, /*Wall staircase*/ + PASSAGEWAY_20489, /*Wall staircase*/ + PASSAGEWAY_20490, /*Wall staircase*/ + PASSAGEWAY_20491, /*Wall staircase*/ + PASSAGEWAY_20492, /*Wall staircase*/ + PASSAGEWAY_20493, /*Wall staircase*/ + PASSAGEWAY_20495, /*Wall staircase*/ + PASSAGEWAY_20497, /*Wall staircase*/ + PASSAGEWAY_20498, /*Wall staircase*/ + PASSAGEWAY_20499, /*Wall staircase*/ + PASSAGEWAY_20500, /*Wall staircase*/ + PASSAGEWAY_20501, /*Wall staircase*/ + PASSAGEWAY_20502, /*Wall staircase*/ + PASSAGEWAY_20503, /*Wall staircase*/ + PASSAGEWAY_20504, /*Wall staircase*/ + PASSAGEWAY_20505, /*Wall staircase*/ + PASSAGEWAY_20506, /*Wall staircase*/ + PASSAGEWAY_20506, /*Wall staircase*/ + PASSAGEWAY_20507, /*Wall staircase*/ + PASSAGEWAY_20509, /*Wall staircase*/ + PASSAGEWAY_20510, /*Wall staircase*/ + PASSAGEWAY_20511, /*Wall staircase*/ + PASSAGEWAY_20512, /*Wall staircase*/ + PASSAGEWAY_20513, /*Wall staircase*/ + PASSAGEWAY_20514, /*Wall staircase*/ + PASSAGEWAY_20515, /*Wall staircase*/ + PASSAGEWAY_20516, /*Wall staircase*/ + PASSAGEWAY_20517, /*Wall staircase*/ + PASSAGEWAY_20518, /*Wall staircase*/ + PASSAGEWAY_20519, /*Wall staircase*/ + PASSAGEWAY_20520, /*Wall staircase*/ + PASSAGEWAY_20521, /*Wall staircase*/ + PASSAGEWAY_20522, /*Wall staircase*/ + PASSAGEWAY_20523, /*Wall staircase*/ + PASSAGEWAY_20524, /*Wall staircase*/ + PASSAGEWAY_20525, /*Wall staircase*/ + PASSAGEWAY_20526, /*Wall staircase*/ + PASSAGEWAY_20527, /*Wall staircase*/ + PASSAGEWAY_20528, /*Wall staircase*/ + PASSAGEWAY_20529, /*Wall staircase*/ + PASSAGEWAY_20530, /*Wall staircase*/ + PASSAGEWAY_20531, /*Wall staircase*/ + PASSAGEWAY_20532, /*Wall staircase*/ + PASSAGEWAY_20533, /*Wall staircase*/ + PASSAGEWAY_20534, /*Wall staircase*/ + PASSAGEWAY_20535, /*Wall staircase*/ + PASSAGEWAY_20536, /*Wall staircase*/ + PASSAGEWAY_20537, /*Wall staircase*/ + PASSAGEWAY_20538, /*Wall staircase*/ + PASSAGEWAY_20539, /*Wall staircase*/ + STAIRS_17098, /*Floor staircase*/ + STAIRS_17099, /*Floor staircase*/ + STAIRS_18973, /*Floor staircase*/ + STAIRS_18974 /*Floor staircase*/ + ); + + static final Set WALL_TRAP_IDS = Sets.newHashSet( + WALL_20590, /*Wall spikes*/ + WALL_20592, /*Wall spikes*/ + WALL_20594, /*Wall spikes*/ + WALL_20596, /*Wall spikes*/ + WALL_20588, /*Wall spikes*/ + WALL_20613, /*Wall pusher*/ + WALL_20615, /*Wall pusher*/ + WALL_20616, /*Wall pusher*/ + WALL_20618, /*Wall pusher*/ + HANGING_LOG_20571, /*Hanging log*/ + HANGING_LOG_20572, /*Hanging log*/ + HANGING_LOG_20573, /*Hanging log*/ + HANGING_LOG_20574 /*Hanging log*/ + ); + + static final Set FLOOR_TRAP_IDS = Sets.newHashSet( + FLOOR_20583, /*Floor spikes*/ + FLOOR_20584, /*Floor spikes*/ + NULL_20575, /*Floor spikes (visible)*/ + FLOOR_20628, /*Trapdoor*/ + FLOOR_20634, /*Floor button*/ + FLOOR_20636 /*Floor button*/ + ); +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/tarnslair/TarnsLairOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/tarnslair/TarnsLairOverlay.java new file mode 100644 index 0000000000..e7ed05731e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/tarnslair/TarnsLairOverlay.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2018, Shaun Dreclin + * 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.tarnslair; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Polygon; +import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.coords.LocalPoint; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; + +@Slf4j +public class TarnsLairOverlay extends Overlay +{ + private static final int MAX_DISTANCE = 2350; + + private final Client client; + private final TarnsLairPlugin plugin; + + @Inject + public TarnsLairOverlay(Client client, TarnsLairPlugin plugin) + { + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + this.client = client; + this.plugin = plugin; + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (!plugin.isInLair()) + { + return null; + } + + LocalPoint playerLocation = client.getLocalPlayer().getLocalLocation(); + + plugin.getStaircases().forEach((obstacle, tile) -> + { + if (tile.getPlane() == client.getPlane() && obstacle.getLocalLocation().distanceTo(playerLocation) < MAX_DISTANCE) + { + Polygon p = tile.getGameObjects()[0].getConvexHull(); + if (p != null) + { + graphics.setColor(Color.GREEN); + graphics.drawPolygon(p); + } + } + }); + + plugin.getWallTraps().forEach((obstacle, tile) -> + { + if (tile.getPlane() == client.getPlane() && obstacle.getLocalLocation().distanceTo(playerLocation) < MAX_DISTANCE) + { + Polygon p = tile.getGameObjects()[0].getConvexHull(); + if (p != null) + { + graphics.setColor(Color.CYAN); + graphics.drawPolygon(p); + } + } + }); + + plugin.getFloorTraps().forEach((obstacle, tile) -> + { + if (tile.getPlane() == client.getPlane() && obstacle.getLocalLocation().distanceTo(playerLocation) < MAX_DISTANCE) + { + Polygon p = obstacle.getCanvasTilePoly(); + if (p != null) + { + graphics.setColor(Color.CYAN); + graphics.drawPolygon(p); + } + } + }); + + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/tarnslair/TarnsLairPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/tarnslair/TarnsLairPlugin.java new file mode 100644 index 0000000000..c262ad813d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/tarnslair/TarnsLairPlugin.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2018-2019, Shaun Dreclin + * 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.tarnslair; + +import java.util.HashMap; +import javax.inject.Inject; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.Tile; +import net.runelite.api.TileObject; +import net.runelite.api.events.GameObjectChanged; +import net.runelite.api.events.GameObjectDespawned; +import net.runelite.api.events.GameObjectSpawned; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.GroundObjectChanged; +import net.runelite.api.events.GroundObjectDespawned; +import net.runelite.api.events.GroundObjectSpawned; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.overlay.OverlayManager; + +@PluginDescriptor( + name = "Tarn's Lair", + description = "Mark tiles and clickboxes to help traverse the maze", + tags = {"agility", "maze", "minigame", "overlay"} +) +@Slf4j +public class TarnsLairPlugin extends Plugin +{ + private static final int TARNS_LAIR_NORTH_REGION = 12616; + private static final int TARNS_LAIR_SOUTH_REGION = 12615; + + @Getter(AccessLevel.PACKAGE) + private final HashMap staircases = new HashMap<>(); + + @Getter(AccessLevel.PACKAGE) + private final HashMap wallTraps = new HashMap<>(); + + @Getter(AccessLevel.PACKAGE) + private final HashMap floorTraps = new HashMap<>(); + + @Getter(AccessLevel.PACKAGE) + private boolean inLair; + + @Inject + private Client client; + + @Inject + private OverlayManager overlayManager; + + @Inject + private TarnsLairOverlay overlay; + + @Override + protected void startUp() throws Exception + { + overlayManager.add(overlay); + } + + @Override + protected void shutDown() throws Exception + { + overlayManager.remove(overlay); + staircases.clear(); + wallTraps.clear(); + floorTraps.clear(); + inLair = false; + } + + @Subscribe + public void onGameTick(GameTick event) + { + int regionID = client.getLocalPlayer().getWorldLocation().getRegionID(); + inLair = (regionID == TARNS_LAIR_NORTH_REGION || regionID == TARNS_LAIR_SOUTH_REGION); + } + + @Subscribe + public void onGameObjectSpawned(GameObjectSpawned event) + { + onTileObject(event.getTile(), null, event.getGameObject()); + } + + @Subscribe + public void onGameObjectChanged(GameObjectChanged event) + { + onTileObject(event.getTile(), event.getPrevious(), event.getGameObject()); + } + + @Subscribe + public void onGameObjectDespawned(GameObjectDespawned event) + { + onTileObject(event.getTile(), event.getGameObject(), null); + } + + @Subscribe + public void onGroundObjectSpawned(GroundObjectSpawned event) + { + onTileObject(event.getTile(), null, event.getGroundObject()); + } + + @Subscribe + public void onGroundObjectChanged(GroundObjectChanged event) + { + onTileObject(event.getTile(), event.getPrevious(), event.getGroundObject()); + } + + @Subscribe + public void onGroundObjectDespawned(GroundObjectDespawned event) + { + onTileObject(event.getTile(), event.getGroundObject(), null); + } + + @Subscribe + public void onGameStateChanged(GameStateChanged event) + { + if (event.getGameState() == GameState.LOADING) + { + staircases.clear(); + wallTraps.clear(); + floorTraps.clear(); + } + } + + private void onTileObject(Tile tile, TileObject oldObject, TileObject newObject) + { + staircases.remove(oldObject); + if (newObject != null && Obstacles.STAIRCASE_IDS.contains(newObject.getId())) + { + staircases.put(newObject, tile); + } + + wallTraps.remove(oldObject); + if (newObject != null && Obstacles.WALL_TRAP_IDS.contains(newObject.getId())) + { + wallTraps.put(newObject, tile); + } + + floorTraps.remove(oldObject); + if (newObject != null && Obstacles.FLOOR_TRAP_IDS.contains(newObject.getId())) + { + floorTraps.put(newObject, tile); + } + } +}