calculate2DBounds(Client client, WorldView wv, Model m, int jauOrient, int x, int y, int z)
{
int[] x2d = new int[m.getVerticesCount()];
int[] y2d = new int[m.getVerticesCount()];
final int[] faceColors3 = m.getFaceColors3();
- Perspective.modelToCanvasCpu(client,
+ Perspective.modelToCanvas(client, wv,
m.getVerticesCount(),
x, y, z,
jauOrient,
diff --git a/runelite-api/src/main/java/net/runelite/api/WorldEntity.java b/runelite-api/src/main/java/net/runelite/api/WorldEntity.java
index 2e747e9023b..c8c93ca1972 100644
--- a/runelite-api/src/main/java/net/runelite/api/WorldEntity.java
+++ b/runelite-api/src/main/java/net/runelite/api/WorldEntity.java
@@ -24,7 +24,28 @@
*/
package net.runelite.api;
-public interface WorldEntity
+import net.runelite.api.coords.LocalPoint;
+
+public interface WorldEntity extends CameraFocusableEntity
{
WorldView getWorldView();
+
+ /**
+ * Get the location of this world entity in the top level world.
+ * @return
+ */
+ LocalPoint getLocalLocation();
+
+ /**
+ * Transform a point within the world entity to the overworld
+ * @param point
+ * @return
+ */
+ LocalPoint transformToMainWorld(LocalPoint point);
+
+ /**
+ * Return true if this worldentity is overlapped
+ * @return
+ */
+ boolean isHiddenForOverlap();
}
diff --git a/runelite-api/src/main/java/net/runelite/api/WorldView.java b/runelite-api/src/main/java/net/runelite/api/WorldView.java
index 6261e9a5708..8924d45134e 100644
--- a/runelite-api/src/main/java/net/runelite/api/WorldView.java
+++ b/runelite-api/src/main/java/net/runelite/api/WorldView.java
@@ -25,6 +25,8 @@
package net.runelite.api;
import javax.annotation.Nullable;
+import net.runelite.api.coords.LocalPoint;
+import net.runelite.api.coords.WorldPoint;
public interface WorldView
{
@@ -62,6 +64,12 @@ public interface WorldView
*/
IndexedObjectSet extends WorldEntity> worldEntities();
+ /**
+ * Get the worldviews of each worldentity in this worldview.
+ * @return
+ */
+ IndexedObjectSet extends WorldView> worldViews();
+
/**
* Gets an array of tile collision data.
*
@@ -205,4 +213,30 @@ Projectile createProjectile(int id, int plane, int startX, int startY, int start
* @return the map regions
*/
int[] getMapRegions();
+
+ /**
+ * Test if this worldview contains the given point
+ * @param point
+ * @return
+ */
+ boolean contains(WorldPoint point);
+
+ /**
+ * Test if this worldview contains the given point
+ * @param point
+ * @return
+ */
+ boolean contains(LocalPoint point);
+
+ /**
+ * Returns a {@link Projection} to translate from this world view to the main world
+ * @return
+ */
+ Projection getMainWorldProjection();
+
+ /**
+ * Returns a {@link Projection} to translate from this world view to the canvas
+ * @return
+ */
+ Projection getCanvasProjection();
}
diff --git a/runelite-api/src/main/java/net/runelite/api/coords/LocalPoint.java b/runelite-api/src/main/java/net/runelite/api/coords/LocalPoint.java
index 84453faabaf..b0d1ff7c9f4 100644
--- a/runelite-api/src/main/java/net/runelite/api/coords/LocalPoint.java
+++ b/runelite-api/src/main/java/net/runelite/api/coords/LocalPoint.java
@@ -65,10 +65,10 @@ public LocalPoint(int x, int y)
}
@Nullable
- @Deprecated
- public static LocalPoint fromWorld(Client client, WorldPoint world)
+ public static LocalPoint fromWorld(Client client, WorldPoint point)
{
- return fromWorld(client.getTopLevelWorldView(), world);
+ WorldView wv = client.findWorldViewFromWorldPoint(point);
+ return fromWorld(wv, point);
}
/**
@@ -147,7 +147,6 @@ public int distanceTo(LocalPoint other)
{
if (worldView != other.worldView)
{
- assert false;
return Integer.MAX_VALUE;
}
@@ -158,6 +157,7 @@ public int distanceTo(LocalPoint other)
* Test if this point is in the basic 104x104 tile scene.
* @return
*/
+ @Deprecated
public boolean isInScene()
{
return x >= 0 && x < Perspective.SCENE_SIZE << Perspective.LOCAL_COORD_BITS
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 b7578e5b499..55f67680204 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
@@ -383,7 +383,7 @@ public boolean canTravelInDirection(WorldView wv, int dx, int dy,
for (int x = startX; x <= endX; x++)
{
if ((collisionDataFlags[x][checkY] & yFlags) != 0 ||
- !extraCondition.test(WorldPoint.fromScene(wv, x, checkY, wv.getPlane())))
+ !extraCondition.test(WorldPoint.fromScene(wv, x, checkY, plane)))
{
// Collision while attempting to travel along the y axis
return false;
@@ -411,7 +411,7 @@ public boolean canTravelInDirection(WorldView wv, int dx, int dy,
if (dx != 0 && dy != 0)
{
if ((collisionDataFlags[checkX][checkY] & xyFlags) != 0 ||
- !extraCondition.test(WorldPoint.fromScene(wv, checkX, checkY, wv.getPlane())))
+ !extraCondition.test(WorldPoint.fromScene(wv, checkX, checkY, plane)))
{
// Collision while attempting to travel diagonally
return false;
@@ -423,7 +423,7 @@ public boolean canTravelInDirection(WorldView wv, int dx, int dy,
if (width == 1)
{
if ((collisionDataFlags[checkX][checkY - dy] & xFlags) != 0 &&
- extraCondition.test(WorldPoint.fromScene(wv, checkX, startY, wv.getPlane())))
+ extraCondition.test(WorldPoint.fromScene(wv, checkX, startY, plane)))
{
return false;
}
@@ -431,7 +431,7 @@ public boolean canTravelInDirection(WorldView wv, int dx, int dy,
if (height == 1)
{
if ((collisionDataFlags[checkX - dx][checkY] & yFlags) != 0 &&
- extraCondition.test(WorldPoint.fromScene(wv, startX, checkY, wv.getPlane())))
+ extraCondition.test(WorldPoint.fromScene(wv, startX, checkY, plane)))
{
return false;
}
diff --git a/runelite-api/src/main/java/net/runelite/api/coords/WorldPoint.java b/runelite-api/src/main/java/net/runelite/api/coords/WorldPoint.java
index d984597833a..cefa4b1be08 100644
--- a/runelite-api/src/main/java/net/runelite/api/coords/WorldPoint.java
+++ b/runelite-api/src/main/java/net/runelite/api/coords/WorldPoint.java
@@ -334,26 +334,35 @@ public static Collection toLocalInstance(WorldView wv, WorldPoint wo
{
return toLocalInstance(wv.getInstanceTemplateChunks(), wv.getBaseX(), wv.getBaseY(), worldPoint);
}
- else
+ else if (wv.contains(worldPoint))
{
return Collections.singleton(worldPoint);
}
+ else
+ {
+ return Collections.emptyList();
+ }
}
/**
* Get occurrences of a tile on the scene, accounting for instances. There may be
* more than one if the same template chunk occurs more than once on the scene.
*/
+ @Deprecated
public static Collection toLocalInstance(Scene scene, WorldPoint worldPoint)
{
if (scene.isInstance())
{
return toLocalInstance(scene.getInstanceTemplateChunks(), scene.getBaseX(), scene.getBaseY(), worldPoint);
}
- else
+ else if (isInScene(scene, worldPoint.getX(), worldPoint.getY()))
{
return Collections.singleton(worldPoint);
}
+ else
+ {
+ return Collections.emptyList();
+ }
}
private static Collection toLocalInstance(int[][][] instanceTemplateChunks, int baseX, int baseY, WorldPoint worldPoint)
diff --git a/runelite-api/src/main/java/net/runelite/api/events/WorldEntityDespawned.java b/runelite-api/src/main/java/net/runelite/api/events/WorldEntityDespawned.java
new file mode 100644
index 00000000000..288f995d204
--- /dev/null
+++ b/runelite-api/src/main/java/net/runelite/api/events/WorldEntityDespawned.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2025, 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.api.events;
+
+import lombok.Value;
+import net.runelite.api.WorldEntity;
+
+/**
+ * Called when a world entity despawns
+ */
+@Value
+public class WorldEntityDespawned
+{
+ WorldEntity worldEntity;
+}
diff --git a/runelite-api/src/main/java/net/runelite/api/events/WorldEntitySpawned.java b/runelite-api/src/main/java/net/runelite/api/events/WorldEntitySpawned.java
new file mode 100644
index 00000000000..6cb7d55decc
--- /dev/null
+++ b/runelite-api/src/main/java/net/runelite/api/events/WorldEntitySpawned.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2025, 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.api.events;
+
+import lombok.Value;
+import net.runelite.api.WorldEntity;
+
+/**
+ * Called when a world entity spawns
+ *
+ * This is called when the world entity spawns, but before the WorldView has been loaded.
+ * @see WorldViewLoaded
+ */
+@Value
+public class WorldEntitySpawned
+{
+ WorldEntity worldEntity;
+}
diff --git a/runelite-api/src/main/java/net/runelite/api/events/WorldViewLoaded.java b/runelite-api/src/main/java/net/runelite/api/events/WorldViewLoaded.java
new file mode 100644
index 00000000000..017ea118f51
--- /dev/null
+++ b/runelite-api/src/main/java/net/runelite/api/events/WorldViewLoaded.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2025, 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.api.events;
+
+import lombok.Value;
+import net.runelite.api.WorldView;
+
+/**
+ * Called when a worldview has been loaded
+ */
+@Value
+public class WorldViewLoaded
+{
+ WorldView worldView;
+}
diff --git a/runelite-api/src/main/java/net/runelite/api/events/WorldViewUnloaded.java b/runelite-api/src/main/java/net/runelite/api/events/WorldViewUnloaded.java
new file mode 100644
index 00000000000..b6c286c69ca
--- /dev/null
+++ b/runelite-api/src/main/java/net/runelite/api/events/WorldViewUnloaded.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2025, 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.api.events;
+
+import lombok.Value;
+import net.runelite.api.WorldView;
+
+/**
+ * Called when a worldview has been unloaded
+ */
+@Value
+public class WorldViewUnloaded
+{
+ WorldView worldView;
+}
diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml
index 9541576a854..59e31d3af2f 100644
--- a/runelite-client/pom.xml
+++ b/runelite-client/pom.xml
@@ -29,7 +29,7 @@
net.runelite
runelite-parent
- 1.11.20-SNAPSHOT
+ 1.11.21-SNAPSHOT
client
@@ -41,7 +41,7 @@
nogit
false
false
- 2.0.22
+ 2.0.24
nogit
diff --git a/runelite-client/src/main/java/net/runelite/client/game/AgilityShortcut.java b/runelite-client/src/main/java/net/runelite/client/game/AgilityShortcut.java
index b1962d22a05..2cab92eeb80 100644
--- a/runelite-client/src/main/java/net/runelite/client/game/AgilityShortcut.java
+++ b/runelite-client/src/main/java/net/runelite/client/game/AgilityShortcut.java
@@ -32,6 +32,7 @@
import net.runelite.api.TileObject;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.gameval.ObjectID;
+import net.runelite.api.gameval.VarbitID;
@Getter
public enum AgilityShortcut
@@ -117,6 +118,7 @@ public boolean matches(Client client, TileObject object)
CRABCLAW_CAVES_CREVICE(18, "Crevice", new WorldPoint(1710, 9822, 0), ObjectID.HOSIDIUSQUEST_CRACKIN, ObjectID.HOSIDIUSQUEST_CRACKOUT),
CRABCLAW_CAVES_ROCKS(18, "Rocks", new WorldPoint(1687, 9802, 0), ObjectID.HOSIDIUSQUEST_ROCK),
CRABCLAW_CAVES_STEPPING_STONES(18, "Stepping Stones", new WorldPoint(1704, 9800, 0), ObjectID.HOSIDIUSQUEST_STONE),
+ SLAYER_TOWER_GROUND_WINDOW(18, "Window", new WorldPoint(3442, 3532, 0), ObjectID.SLAYERTOWER_WINDOW_SHORTCUT_THROUGH),
YANILLE_WATCHTOWER_TRELLIS(18, "Trellis", null, ObjectID.QIP_WATCHTOWER_TRELLIS_BASE),
COAL_TRUCKS_LOG_BALANCE(20, "Log Balance", new WorldPoint(2598, 3475, 0), ObjectID.MINE_LOG_BALANCE1),
GRAND_EXCHANGE_UNDERWALL_TUNNEL(21, "Underwall Tunnel", new WorldPoint(3139, 3515, 0), ObjectID.VARROCK_SC_TUNNEL_WEST, ObjectID.VARROCK_SC_TUNNEL_EAST),
@@ -242,6 +244,14 @@ public boolean matches(Client client, TileObject object)
WEISS_OBSTACLES(68, "Shortcut", null, ObjectID.MY2ARM_CLIFF_SHORTCUT_1, ObjectID.MY2ARM_CLIFF_SHORTCUT_2, ObjectID.MY2ARM_CLIFF_SHORTCUT_3, ObjectID.MY2ARM_CLIFF_SHORTCUT_3_ROPETRAIL_MULTI, ObjectID.MY2ARM_CLIFF_SHORTCUT_4, ObjectID.MY2ARM_CLIFF_SHORTCUT_5, ObjectID.MY2ARM_CLIFF_SHORTCUT_6),
WEISS_FARMING_PATCH_BOULDER(0, "Shortcut", null, ObjectID.MY2ARM_HERBPATCH_ACCESS),
ARCEUUS_ESSENSE_NORTH(69, "Rock Climb", new WorldPoint(1759, 3873, 0), ObjectID.ARCHEUUS_RUNESTONE_SHORTCUT_GREY_SHORTCUT_NORTH),
+ FENKENSTRAIN_MAUSOLEUM_BRIDGE(69, "Bridge Jump", new WorldPoint(3504, 3560, 0), ObjectID.FENK_BRIDGE_MULTI_NORTH, ObjectID.FENK_BRIDGE_MULTI_NORTH_MIRROR, ObjectID.FENK_BRIDGE_MULTI_SOUTH, ObjectID.FENK_BRIDGE_MULTI_SOUTH_MIRROR)
+ {
+ @Override
+ public boolean matches(Client client, TileObject object)
+ {
+ return client.getVarbitValue(VarbitID.FENK_BUILT_BRIDGE_NORTH) == 2 && client.getVarbitValue(VarbitID.FENK_BUILT_BRIDGE_SOUTH) == 2;
+ }
+ },
TAVERLEY_DUNGEON_PIPE_BLUE_DRAGON(70, "Pipe Squeeze", new WorldPoint(2886, 9798, 0), ObjectID.TAVERLY_DUNGEON_PIPE_SC),
TAVERLEY_DUNGEON_ROCKS_NORTH(70, "Rocks", new WorldPoint(2887, 9823, 0), ObjectID.TAVERLEY_DRAGON_JUMPUP, ObjectID.TAVERLEY_DRAGON_JUMPDOWN),
TAVERLEY_DUNGEON_ROCKS_SOUTH(70, "Rocks", new WorldPoint(2887, 9631, 0), ObjectID.TAVERLEY_DRAGON_JUMPUP, ObjectID.TAVERLEY_DRAGON_JUMPDOWN),
@@ -275,6 +285,7 @@ public boolean matches(Client client, TileObject object)
LAVA_DRAGON_ISLE_JUMP(74, "Stepping Stone", new WorldPoint(3200, 3807, 0), ObjectID.WILDERNESS_LAVA_DRAGONS_SHORTCUT),
MEIYERDITCH_LAB_TUNNELS_NORTH(74, "Cave", new WorldPoint(3623, 9747, 0), ObjectID.MYQ5_WALL_CAVE_SHORTCUT_1, ObjectID.MYQ5_WALL_CAVE_SHORTCUT_2),
MEIYERDITCH_LAB_TUNNELS_SOUTH(74, "Cave", new WorldPoint(3618, 9722, 0), ObjectID.MYQ5_WALL_CAVE_SHORTCUT_3, ObjectID.MYQ5_WALL_CAVE_SHORTCUT_4),
+ FOSSIL_ISLAND_ZIPLINE(74, "Zipline", new WorldPoint(3764, 3883, 0), ObjectID.FOSSIL_ZIPWIRE_START),
MOKHAIOTL_PIT_JUMP(75, "Jump", null, ObjectID.MOKI_AGIL_SHORTCUT_OP),
REVENANT_CAVES_DEMONS_JUMP(75, "Jump", new WorldPoint(3199, 10135, 0), ObjectID.WILD_CAVE_AGILITY_JUMP),
REVENANT_CAVES_ANKOU_EAST(75, "Jump", new WorldPoint(3201, 10195, 0), ObjectID.WILD_CAVE_AGILITY_JUMP),
@@ -290,6 +301,8 @@ public boolean matches(Client client, TileObject object)
SHILO_VILLAGE_ROCKS(79, "Rocks", new WorldPoint(2870, 3003, 0), ObjectID.SHORTCUT_SHILO_ROCKS_TOP, ObjectID.SHORTCUT_SHILO_ROCKS_BOTTOM),
KHARAZI_JUNGLE_VINE_CLIMB(79, "Vine", new WorldPoint(2897, 2939, 0), ObjectID.KHARAZI_SHORTCUT_VINE_END, ObjectID.KHARAZI_SHORTCUT_VINE_DIAG1),
TAVERLEY_DUNGEON_SPIKED_BLADES(80, "Strange Floor", new WorldPoint(2877, 9813, 0), ObjectID.TAVERLY_DUNGEON_FLOOR_SPIKES_SC),
+ SLAYER_TOWER_IVY(81, "Ivy", new WorldPoint(3417, 3533, 0), ObjectID.SLAYERTOWER_SHORTCUT_UP),
+ SLAYER_TOWER_TOP_WINDOW(81, "Window", new WorldPoint(3419, 3534, 0), ObjectID.SLAYERTOWER_WINDOW_SHORTCUT_DOWN),
WATERBIRTH_DUNGEON_CREVICE(81, "Crevice", new WorldPoint(2604, 10070, 0), ObjectID.DAGANNOTH_CREVICE),
LAVA_MAZE_NORTH_JUMP(82, "Stepping Stone", new WorldPoint(3092, 3880, 0), ObjectID.WILDERNESS_LAVA_MAZE_NORTHERN_SHORTCUT),
ASGARNIA_ICE_DUNGEON_ADEPT_WEST(82, "Tunnel", new WorldPoint(3012, 9549, 0), ObjectID.CAVEWALL_SHORTCUT_WYVERN_WEST),
@@ -301,6 +314,14 @@ public boolean matches(Client client, TileObject object)
ELVEN_ADVANCED_CLIFF_SCRAMBLE(85, "Rocks", new WorldPoint(2337, 3253, 0), ObjectID.ELVES_OVERPASS_SC_ROCKS_TOP, ObjectID.ELVES_OVERPASS_SC_ROCKS_BOTTOM),
ELVEN_ADVANCED_CLIFF_SCRAMBLE_PRIFDDINAS(85, "Rocks", new WorldPoint(3361, 6005, 0), ObjectID.ELVES_OVERPASS_SC_ROCKS_TOP, ObjectID.ELVES_OVERPASS_SC_ROCKS_BOTTOM),
WATERBIRTH_ISLAND_ROCKS(85, "Rocks", new WorldPoint(2546, 3750, 0), ObjectID.DAGANNOTH_WATERBIRTH_ROCK_CLIMB_AGILITY_SHORTCUT_BOTTOM, ObjectID.DAGANNOTH_WATERBIRTH_ROCK_CLIMB_AGILITY_SHORTCUT_TOP),
+ DARKMEYER_WALL_ROCKS(86, "Rocks", new WorldPoint(3563, 3380, 0), ObjectID.HH_MASTER005, ObjectID.DARKM_WALL_ROCK_SHORTCUT)
+ {
+ @Override
+ public boolean matches(Client client, TileObject object)
+ {
+ return Quest.SINS_OF_THE_FATHER.getState(client) == QuestState.FINISHED;
+ }
+ },
KALPHITE_WALL(86, "Crevice", new WorldPoint(3214, 9508, 0), ObjectID.KALPHITE_WALL_SHORTCUT),
BRIMHAVEN_DUNGEON_VINE_EAST(87, "Vine", new WorldPoint(2672, 9582, 0), ObjectID.KARAM_CAVEWALL_VINE_CLIMBABLE_BOTTOM, ObjectID.KARAM_CAVEWALL_VINE_CLIMBABLE_TOP),
BRIMHAVEN_DUNGEON_VINE_WEST(87, "Vine", new WorldPoint(2606, 9584, 0), ObjectID.KARAM_CAVEWALL_VINE_CLIMBABLE_BOTTOM, ObjectID.KARAM_CAVEWALL_VINE_CLIMBABLE_TOP),
diff --git a/runelite-client/src/main/java/net/runelite/client/game/npcoverlay/NpcOverlayService.java b/runelite-client/src/main/java/net/runelite/client/game/npcoverlay/NpcOverlayService.java
index db96bc91a52..f7942ddf0ac 100644
--- a/runelite-client/src/main/java/net/runelite/client/game/npcoverlay/NpcOverlayService.java
+++ b/runelite-client/src/main/java/net/runelite/client/game/npcoverlay/NpcOverlayService.java
@@ -35,6 +35,7 @@
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.NPC;
+import net.runelite.api.WorldView;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.NpcChanged;
import net.runelite.api.events.NpcDespawned;
@@ -127,21 +128,30 @@ public void rebuild()
clientThread.invoke(() ->
{
highlightedNpcs.clear();
+ rebuild(client.getTopLevelWorldView());
+ });
+ }
- outer:
- for (NPC npc : client.getNpcs())
+ private void rebuild(WorldView wv)
+ {
+ outer:
+ for (NPC npc : wv.npcs())
+ {
+ for (Function f : highlightFunctions)
{
- for (Function f : highlightFunctions)
+ HighlightedNpc highlightedNpc = f.apply(npc);
+ if (highlightedNpc != null)
{
- HighlightedNpc highlightedNpc = f.apply(npc);
- if (highlightedNpc != null)
- {
- highlightedNpcs.put(npc, highlightedNpc);
- continue outer;
- }
+ highlightedNpcs.put(npc, highlightedNpc);
+ continue outer;
}
}
- });
+ }
+
+ for (WorldView sub : wv.worldViews())
+ {
+ rebuild(sub);
+ }
}
public void registerHighlighter(Function p)
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItem.java
index 783e148bb9d..38e3a9f3712 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItem.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItem.java
@@ -30,6 +30,7 @@
import javax.annotation.Nullable;
import lombok.Builder;
import lombok.Data;
+import net.runelite.api.ItemLayer;
import static net.runelite.api.TileItem.OWNERSHIP_GROUP;
import static net.runelite.api.TileItem.OWNERSHIP_NONE;
import static net.runelite.api.TileItem.OWNERSHIP_OTHER;
@@ -47,6 +48,7 @@ public class GroundItem
private int itemId;
private String name;
private int quantity;
+ private ItemLayer itemLayer;
@Getter
private WorldPoint location;
private int height;
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java
index a4b3486bb2c..ba0bdc8bf67 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java
@@ -42,6 +42,7 @@
import net.runelite.api.Perspective;
import net.runelite.api.Player;
import net.runelite.api.Point;
+import net.runelite.api.WorldView;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.gameval.ItemID;
@@ -113,7 +114,6 @@ public Dimension render(Graphics2D graphics)
}
offsetMap.clear();
- final LocalPoint localLocation = player.getLocalLocation();
final Point mousePos = client.getMouseCanvasPosition();
Collection groundItemList = plugin.getCollectedGroundItems().values();
GroundItem topGroundItem = null;
@@ -128,7 +128,7 @@ public Dimension render(Graphics2D graphics)
for (GroundItem item : groundItemList)
{
- item.setOffset(offsetMap.compute(item.getLocation(), (k, v) -> v != null ? v + 1 : 0));
+ item.setOffset(offsetMap.compute(item.getItemLayer().getWorldLocation(), (k, v) -> v != null ? v + 1 : 0));
if (groundItem != null)
{
@@ -171,6 +171,7 @@ public Dimension render(Graphics2D graphics)
plugin.setHiddenBoxBounds(null);
plugin.setHighlightBoxBounds(null);
+ final LocalPoint localLocation = player.getLocalLocation();
final DespawnTimerMode groundItemTimers = config.groundItemTimers();
final boolean outline = config.textOutline();
final OwnershipFilterMode ownershipFilterMode = config.ownershipFilterMode();
@@ -178,9 +179,11 @@ public Dimension render(Graphics2D graphics)
for (GroundItem item : groundItemList)
{
- final LocalPoint groundPoint = LocalPoint.fromWorld(client, item.getLocation());
+ final WorldView wv = item.getItemLayer().getWorldView();
+ final LocalPoint groundPoint = LocalPoint.fromWorld(wv, item.getItemLayer().getWorldLocation());
- if (groundPoint == null || localLocation.distanceTo(groundPoint) > MAX_DISTANCE
+ if (groundPoint == null
+ || (groundPoint.getWorldView() == WorldView.TOPLEVEL && localLocation.distanceTo(groundPoint) > MAX_DISTANCE)
|| !plugin.shouldDisplayItem(ownershipFilterMode, item.getOwnership(), accountType))
{
continue;
@@ -207,7 +210,7 @@ public Dimension render(Graphics2D graphics)
if (config.highlightTiles())
{
- final Polygon poly = Perspective.getCanvasTilePoly(client, groundPoint, item.getHeight());
+ final Polygon poly = Perspective.getCanvasTilePoly(client, groundPoint, item.getItemLayer().getHeight());
if (poly != null)
{
@@ -271,7 +274,7 @@ else if (displayMode != PriceDisplayMode.OFF)
graphics,
groundPoint,
itemString,
- item.getHeight() + OFFSET_Z);
+ item.getItemLayer().getHeight() + OFFSET_Z);
if (textPoint == null)
{
@@ -280,7 +283,7 @@ else if (displayMode != PriceDisplayMode.OFF)
final int offset = plugin.isHotKeyPressed()
? item.getOffset()
- : offsetMap.compute(item.getLocation(), (k, v) -> v != null ? v + 1 : 0);
+ : offsetMap.compute(item.getItemLayer().getWorldLocation(), (k, v) -> v != null ? v + 1 : 0);
final int textX = textPoint.getX();
final int textY = textPoint.getY() - (STRING_GAP * offset);
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java
index c88e7c736fa..f284cb691ca 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java
@@ -59,8 +59,8 @@
import lombok.Setter;
import lombok.Value;
import net.runelite.api.Client;
-import net.runelite.api.GameState;
import net.runelite.api.ItemComposition;
+import net.runelite.api.ItemLayer;
import net.runelite.api.KeyCode;
import net.runelite.api.Menu;
import net.runelite.api.MenuAction;
@@ -70,14 +70,15 @@
import static net.runelite.api.TileItem.OWNERSHIP_GROUP;
import static net.runelite.api.TileItem.OWNERSHIP_OTHER;
import static net.runelite.api.TileItem.OWNERSHIP_SELF;
+import net.runelite.api.WorldView;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.ClientTick;
import net.runelite.api.events.FocusChanged;
-import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.ItemDespawned;
import net.runelite.api.events.ItemQuantityChanged;
import net.runelite.api.events.ItemSpawned;
import net.runelite.api.events.MenuEntryAdded;
+import net.runelite.api.events.WorldViewUnloaded;
import net.runelite.api.gameval.ItemID;
import net.runelite.api.gameval.VarbitID;
import net.runelite.client.Notifier;
@@ -263,13 +264,11 @@ private void migrate()
}
@Subscribe
- public void onGameStateChanged(final GameStateChanged event)
+ public void onWorldViewUnloaded(WorldViewUnloaded event)
{
- if (event.getGameState() == GameState.LOADING)
- {
- collectedGroundItems.clear();
- lootbeams.clear();
- }
+ var wv = event.getWorldView();
+ collectedGroundItems.values().removeIf(g -> g.getItemLayer().getWorldView() == wv);
+ lootbeams.values().removeIf(l -> l.getWorldView() == wv.getId());
}
@Subscribe
@@ -277,8 +276,9 @@ public void onItemSpawned(ItemSpawned itemSpawned)
{
TileItem item = itemSpawned.getItem();
Tile tile = itemSpawned.getTile();
+ ItemLayer layer = tile.getItemLayer();
- GroundItem groundItem = buildGroundItem(tile, item);
+ GroundItem groundItem = buildGroundItem(layer, item);
GroundItem existing = collectedGroundItems.get(tile.getWorldLocation(), item.getId());
if (existing != null)
{
@@ -411,7 +411,7 @@ public void onClientTick(ClientTick event)
}).toArray(MenuEntry[]::new));
}
- private GroundItem buildGroundItem(final Tile tile, final TileItem item)
+ private GroundItem buildGroundItem(final ItemLayer layer, final TileItem item)
{
// Collect the data for the item
final int itemId = item.getId();
@@ -423,12 +423,11 @@ private GroundItem buildGroundItem(final Tile tile, final TileItem item)
final GroundItem groundItem = GroundItem.builder()
.id(itemId)
- .location(tile.getWorldLocation())
.itemId(realItemId)
.quantity(item.getQuantity())
+ .itemLayer(layer)
.name(itemComposition.getName())
.haPrice(alchPrice)
- .height(tile.getItemLayer().getHeight())
.tradeable(itemComposition.isTradeable())
.ownership(item.getOwnership())
.isPrivate(item.isPrivate())
@@ -512,10 +511,10 @@ public void onMenuEntryAdded(MenuEntryAdded event)
final int sceneX = event.getActionParam0();
final int sceneY = event.getActionParam1();
- MenuEntry[] menuEntries = client.getMenuEntries();
- MenuEntry lastEntry = menuEntries[menuEntries.length - 1];
+ MenuEntry lastEntry = event.getMenuEntry();
+ WorldView wv = client.getWorldView(lastEntry.getWorldViewId());
- final WorldPoint worldPoint = WorldPoint.fromScene(client, sceneX, sceneY, client.getPlane());
+ final WorldPoint worldPoint = WorldPoint.fromScene(wv, sceneX, sceneY, wv.getPlane());
GroundItem groundItem = collectedGroundItems.get(worldPoint, itemId);
if (groundItem == null) return;
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/Lootbeam.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/Lootbeam.java
index 0fe13514971..4f49ca843b5 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/Lootbeam.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/Lootbeam.java
@@ -102,6 +102,11 @@ public Lootbeam(Client client, ClientThread clientThread, WorldPoint worldPoint,
runeLiteObject.setActive(true);
}
+ int getWorldView()
+ {
+ return runeLiteObject.getWorldView();
+ }
+
public void setColor(Color color)
{
if (this.color != null && this.color.equals(color))
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerMinimapOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerMinimapOverlay.java
index 0a808fdd36a..0c05513e7d1 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerMinimapOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerMinimapOverlay.java
@@ -24,17 +24,18 @@
*/
package net.runelite.client.plugins.groundmarkers;
+import com.google.common.collect.Multimap;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Stroke;
-import java.util.Collection;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.Perspective;
import net.runelite.api.Point;
+import net.runelite.api.WorldView;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.ui.overlay.Overlay;
@@ -61,50 +62,51 @@ private GroundMarkerMinimapOverlay(Client client, GroundMarkerConfig config, Gro
@Override
public Dimension render(Graphics2D graphics)
{
- if (!config.drawTileOnMinimmap())
+ final Multimap points = plugin.getPoints();
+ if (points.isEmpty() || !config.drawTileOnMinimmap())
{
return null;
}
- final Collection points = plugin.getPoints();
- for (final ColorTileMarker point : points)
+ for (WorldView wv : points.keySet())
{
- WorldPoint worldPoint = point.getWorldPoint();
- if (worldPoint.getPlane() != client.getPlane())
+ for (final ColorTileMarker point : points.get(wv))
{
- continue;
- }
+ WorldPoint worldPoint = point.getWorldPoint();
+ if (worldPoint.getPlane() != wv.getPlane())
+ {
+ continue;
+ }
- Color tileColor = point.getColor();
- if (tileColor == null)
- {
- // If this is an old tile which has no color, use marker color
- tileColor = config.markerColor();
- }
+ Color tileColor = point.getColor();
+ if (tileColor == null)
+ {
+ // If this is an old tile which has no color, use marker color
+ tileColor = config.markerColor();
+ }
- drawOnMinimap(graphics, worldPoint, tileColor);
+ drawOnMinimap(graphics, wv, worldPoint, tileColor);
+ }
}
return null;
}
- private void drawOnMinimap(Graphics2D graphics, WorldPoint point, Color color)
+ private void drawOnMinimap(Graphics2D graphics, WorldView wv, WorldPoint point, Color color)
{
- if (!point.isInScene(client))
+ LocalPoint lp = LocalPoint.fromWorld(wv, point);
+ if (lp == null)
{
return;
}
- int x = point.getX() - client.getBaseX();
- int y = point.getY() - client.getBaseY();
-
- x <<= Perspective.LOCAL_COORD_BITS;
- y <<= Perspective.LOCAL_COORD_BITS;
+ int x = lp.getX() & -Perspective.LOCAL_TILE_SIZE;
+ int y = lp.getY() & -Perspective.LOCAL_TILE_SIZE;
- Point mp1 = Perspective.localToMinimap(client, new LocalPoint(x, y));
- Point mp2 = Perspective.localToMinimap(client, new LocalPoint(x, y + Perspective.LOCAL_TILE_SIZE));
- Point mp3 = Perspective.localToMinimap(client, new LocalPoint(x + Perspective.LOCAL_TILE_SIZE, y + Perspective.LOCAL_TILE_SIZE));
- Point mp4 = Perspective.localToMinimap(client, new LocalPoint(x + Perspective.LOCAL_TILE_SIZE, y));
+ Point mp1 = Perspective.localToMinimap(client, new LocalPoint(x, y, wv.getId()));
+ Point mp2 = Perspective.localToMinimap(client, new LocalPoint(x, y + Perspective.LOCAL_TILE_SIZE, wv.getId()));
+ Point mp3 = Perspective.localToMinimap(client, new LocalPoint(x + Perspective.LOCAL_TILE_SIZE, y + Perspective.LOCAL_TILE_SIZE, wv.getId()));
+ Point mp4 = Perspective.localToMinimap(client, new LocalPoint(x + Perspective.LOCAL_TILE_SIZE, y, wv.getId()));
if (mp1 == null || mp2 == null || mp3 == null || mp4 == null)
{
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerOverlay.java
index b0243cf67f6..2ab8a888eb9 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerOverlay.java
@@ -26,18 +26,19 @@
package net.runelite.client.plugins.groundmarkers;
import com.google.common.base.Strings;
+import com.google.common.collect.Multimap;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Stroke;
-import java.util.Collection;
import javax.annotation.Nullable;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.Perspective;
import net.runelite.api.Point;
+import net.runelite.api.WorldView;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.ui.overlay.Overlay;
@@ -67,44 +68,50 @@ private GroundMarkerOverlay(Client client, GroundMarkerConfig config, GroundMark
@Override
public Dimension render(Graphics2D graphics)
{
- final Collection points = plugin.getPoints();
+ final Multimap points = plugin.getPoints();
if (points.isEmpty())
{
return null;
}
Stroke stroke = new BasicStroke((float) config.borderWidth());
- for (final ColorTileMarker point : points)
+ for (WorldView wv : points.keySet())
{
- WorldPoint worldPoint = point.getWorldPoint();
- if (worldPoint.getPlane() != client.getPlane())
+ for (final ColorTileMarker point : points.get(wv))
{
- continue;
- }
+ WorldPoint worldPoint = point.getWorldPoint();
+ if (worldPoint.getPlane() != wv.getPlane())
+ {
+ continue;
+ }
- Color tileColor = point.getColor();
- if (tileColor == null)
- {
- // If this is an old tile which has no color, use marker color
- tileColor = config.markerColor();
- }
+ Color tileColor = point.getColor();
+ if (tileColor == null)
+ {
+ // If this is an old tile which has no color, use marker color
+ tileColor = config.markerColor();
+ }
- drawTile(graphics, worldPoint, tileColor, point.getLabel(), stroke);
+ drawTile(graphics, wv, worldPoint, tileColor, point.getLabel(), stroke);
+ }
}
return null;
}
- private void drawTile(Graphics2D graphics, WorldPoint point, Color color, @Nullable String label, Stroke borderStroke)
+ private void drawTile(Graphics2D graphics, WorldView wv, WorldPoint point, Color color, @Nullable String label, Stroke borderStroke)
{
- WorldPoint playerLocation = client.getLocalPlayer().getWorldLocation();
-
- if (point.distanceTo(playerLocation) >= MAX_DRAW_DISTANCE)
+ if (wv.isTopLevel())
{
- return;
+ WorldPoint playerLocation = client.getLocalPlayer().getWorldLocation();
+
+ if (point.distanceTo(playerLocation) >= MAX_DRAW_DISTANCE)
+ {
+ return;
+ }
}
- LocalPoint lp = LocalPoint.fromWorld(client, point);
+ LocalPoint lp = LocalPoint.fromWorld(wv, point);
if (lp == null)
{
return;
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPlugin.java
index 8c093e193ae..1057059d431 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerPlugin.java
@@ -26,6 +26,8 @@
package net.runelite.client.plugins.groundmarkers;
import com.google.common.base.Strings;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
import com.google.common.util.concurrent.Runnables;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
@@ -43,16 +45,17 @@
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
-import net.runelite.api.GameState;
import net.runelite.api.KeyCode;
import net.runelite.api.Menu;
import net.runelite.api.MenuAction;
import net.runelite.api.MenuEntry;
import net.runelite.api.Tile;
-import net.runelite.api.coords.LocalPoint;
+import net.runelite.api.WorldEntity;
+import net.runelite.api.WorldView;
import net.runelite.api.coords.WorldPoint;
-import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.MenuEntryAdded;
+import net.runelite.api.events.WorldViewLoaded;
+import net.runelite.api.events.WorldViewUnloaded;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.eventbus.Subscribe;
@@ -79,7 +82,7 @@ public class GroundMarkerPlugin extends Plugin
private static final String REGION_PREFIX = "region_";
@Getter(AccessLevel.PACKAGE)
- private final List points = new ArrayList<>();
+ private final ListMultimap points = ArrayListMultimap.create();
@Inject
private Client client;
@@ -149,8 +152,20 @@ void loadPoints()
{
points.clear();
- int[] regions = client.getMapRegions();
+ WorldView wv = client.getTopLevelWorldView();
+ loadPoints(wv);
+ for (WorldEntity we : wv.worldEntities())
+ {
+ loadPoints(we.getWorldView());
+ }
+ }
+
+ void loadPoints(WorldView wv)
+ {
+ points.removeAll(wv);
+
+ int[] regions = wv.getMapRegions();
if (regions == null)
{
return;
@@ -161,8 +176,8 @@ void loadPoints()
// load points for region
log.debug("Loading points for region {}", regionId);
Collection regionPoints = getPoints(regionId);
- Collection colorTileMarkers = translateToColorTileMarker(regionPoints);
- points.addAll(colorTileMarkers);
+ Collection colorTileMarkers = translateToColorTileMarker(wv, regionPoints);
+ points.putAll(wv, colorTileMarkers);
}
}
@@ -173,7 +188,7 @@ void loadPoints()
* @return A collection of color tile markers, converted from the passed ground marker points, accounting for local
* instance points. See {@link WorldPoint#toLocalInstance(Client, WorldPoint)}
*/
- private Collection translateToColorTileMarker(Collection points)
+ private Collection translateToColorTileMarker(WorldView wv, Collection points)
{
if (points.isEmpty())
{
@@ -186,7 +201,7 @@ private Collection translateToColorTileMarker(Collection
{
- final Collection localWorldPoints = WorldPoint.toLocalInstance(client, colorTile.getWorldPoint());
+ final Collection localWorldPoints = WorldPoint.toLocalInstance(wv, colorTile.getWorldPoint());
return localWorldPoints.stream().map(wp -> new ColorTileMarker(wp, colorTile.getColor(), colorTile.getLabel()));
})
.collect(Collectors.toList());
@@ -223,15 +238,15 @@ public void onProfileChanged(ProfileChanged profileChanged)
}
@Subscribe
- public void onGameStateChanged(GameStateChanged gameStateChanged)
+ public void onWorldViewLoaded(WorldViewLoaded event)
{
- if (gameStateChanged.getGameState() != GameState.LOGGED_IN)
- {
- return;
- }
+ loadPoints(event.getWorldView());
+ }
- // map region has just been updated
- loadPoints();
+ @Subscribe
+ public void onWorldViewUnloaded(WorldViewUnloaded event)
+ {
+ points.removeAll(event.getWorldView());
}
@Subscribe
@@ -240,8 +255,14 @@ public void onMenuEntryAdded(MenuEntryAdded event)
final boolean hotKeyPressed = client.isKeyPressed(KeyCode.KC_SHIFT);
if (hotKeyPressed && event.getOption().equals(WALK_HERE))
{
- final Tile selectedSceneTile = client.getSelectedSceneTile();
+ int worldId = event.getMenuEntry().getWorldViewId();
+ WorldView wv = client.getWorldView(worldId);
+ if (wv == null)
+ {
+ return;
+ }
+ final Tile selectedSceneTile = wv.getSelectedSceneTile();
if (selectedSceneTile == null)
{
return;
@@ -259,13 +280,7 @@ public void onMenuEntryAdded(MenuEntryAdded event)
.setTarget("Tile")
.setType(MenuAction.RUNELITE)
.onClick(e ->
- {
- Tile target = client.getSelectedSceneTile();
- if (target != null)
- {
- markTile(target.getLocalLocation());
- }
- });
+ markTile(worldPoint));
if (existingOpt.isPresent())
{
@@ -317,7 +332,7 @@ public void onMenuEntryAdded(MenuEntryAdded event)
});
});
- var existingColors = points.stream()
+ var existingColors = points.values().stream()
.map(ColorTileMarker::getColor)
.distinct()
.collect(Collectors.toList());
@@ -351,15 +366,8 @@ public void onConfigChanged(ConfigChanged event)
}
}
- private void markTile(LocalPoint localPoint)
+ private void markTile(WorldPoint worldPoint)
{
- if (localPoint == null)
- {
- return;
- }
-
- WorldPoint worldPoint = WorldPoint.fromLocalInstance(client, localPoint);
-
int regionId = worldPoint.getRegionID();
GroundMarkerPoint point = new GroundMarkerPoint(regionId, worldPoint.getRegionX(), worldPoint.getRegionY(), worldPoint.getPlane(), config.markerColor(), null);
log.debug("Updating point: {} - {}", point, worldPoint);
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/interacthighlight/InteractHighlightOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/interacthighlight/InteractHighlightOverlay.java
index 7e4b2f3ba7d..d4e03546a20 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/interacthighlight/InteractHighlightOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/interacthighlight/InteractHighlightOverlay.java
@@ -93,10 +93,11 @@ private void renderMouseover()
case GAME_OBJECT_FIFTH_OPTION:
case EXAMINE_OBJECT:
{
+ int worldId = entry.getWorldViewId();
int x = entry.getParam0();
int y = entry.getParam1();
int id = entry.getIdentifier();
- TileObject tileObject = plugin.findTileObject(x, y, id);
+ TileObject tileObject = plugin.findTileObject(worldId, x, y, id);
if (tileObject != null && config.objectShowHover() && (tileObject != plugin.getInteractedObject() || !config.objectShowInteract()))
{
modelOutlineRenderer.drawOutline(tileObject, config.borderWidth(), config.objectHoverHighlightColor(), config.outlineFeather());
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/interacthighlight/InteractHighlightPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/interacthighlight/InteractHighlightPlugin.java
index d27eb527902..02083f38584 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/interacthighlight/InteractHighlightPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/interacthighlight/InteractHighlightPlugin.java
@@ -42,6 +42,7 @@
import net.runelite.api.Tile;
import net.runelite.api.TileObject;
import net.runelite.api.WallObject;
+import net.runelite.api.WorldView;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.InteractingChanged;
@@ -150,10 +151,11 @@ public void onMenuOptionClicked(MenuOptionClicked menuOptionClicked)
case GAME_OBJECT_FOURTH_OPTION:
case GAME_OBJECT_FIFTH_OPTION:
{
+ int worldId = menuOptionClicked.getMenuEntry().getWorldViewId();
int x = menuOptionClicked.getParam0();
int y = menuOptionClicked.getParam1();
int id = menuOptionClicked.getId();
- interactedObject = findTileObject(x, y, id);
+ interactedObject = findTileObject(worldId, x, y, id);
interactedNpc = null;
clickTick = client.getTickCount();
gameCycle = client.getGameCycle();
@@ -198,13 +200,15 @@ public void onMenuOptionClicked(MenuOptionClicked menuOptionClicked)
}
}
- TileObject findTileObject(int x, int y, int id)
+ TileObject findTileObject(int worldId, int x, int y, int id)
{
- x += (Constants.EXTENDED_SCENE_SIZE - Constants.SCENE_SIZE) / 2;
- y += (Constants.EXTENDED_SCENE_SIZE - Constants.SCENE_SIZE) / 2;
- Scene scene = client.getScene();
+ WorldView wv = client.getWorldView(worldId);
+ int offset = worldId == WorldView.TOPLEVEL ? (Constants.EXTENDED_SCENE_SIZE - Constants.SCENE_SIZE) / 2 : 0;
+ x += offset;
+ y += offset;
+ Scene scene = wv.getScene();
Tile[][][] tiles = scene.getExtendedTiles();
- Tile tile = tiles[client.getPlane()][x][y];
+ Tile tile = tiles[wv.getPlane()][x][y];
if (tile != null)
{
for (GameObject gameObject : tile.getGameObjects())
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/interfacestyles/InterfaceStylesPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/interfacestyles/InterfaceStylesPlugin.java
index 9b0a814aa37..2490bc94fad 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/interfacestyles/InterfaceStylesPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/interfacestyles/InterfaceStylesPlugin.java
@@ -185,6 +185,7 @@ private void condensePlayerOptions()
.setType(type)
.setParam0(menuEntry.getParam0())
.setParam1(menuEntry.getParam1())
+ .setWorldViewId(menuEntry.getWorldViewId())
.setDeprioritized(deprioritized);
changed = true;
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentification.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentification.java
index 4e9d3f4160a..fee3fe1bcb6 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentification.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentification.java
@@ -325,6 +325,7 @@ enum ItemIdentification
ANCIENT_BREW(Type.POTION, "AncBr", "A.Br", ItemID._4DOSEANCIENTBREW, ItemID._3DOSEANCIENTBREW, ItemID._2DOSEANCIENTBREW, ItemID._1DOSEANCIENTBREW),
ANCIENT_MIX(Type.POTION, "AncBr", "Anc", ItemID.BRUTAL_1DOSEANCIENTBREW, ItemID.BRUTAL_2DOSEANCIENTBREW),
FORGOTTEN_BREW(Type.POTION, "ForgBr", "F.Br", ItemID._4DOSEFORGOTTENBREW, ItemID._3DOSEFORGOTTENBREW, ItemID._2DOSEFORGOTTENBREW, ItemID._1DOSEFORGOTTENBREW),
+ SURGE_POTION(Type.POTION, "Surge", "Sur", ItemID._4DOSESURGE, ItemID._3DOSESURGE, ItemID._2DOSESURGE, ItemID._1DOSESURGE),
ANTIPOISON(Type.POTION, "AntiP", "AP", ItemID._4DOSEANTIPOISON, ItemID._3DOSEANTIPOISON, ItemID._2DOSEANTIPOISON, ItemID._1DOSEANTIPOISON),
ANTIPOISON_MIX(Type.POTION, "AntiP", "AP", ItemID.BRUTAL_1DOSEANTIPOISON, ItemID.BRUTAL_2DOSEANTIPOISON),
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/runeliteobjects/extendedruneliteobjects/ExtendedRuneliteObject.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/runeliteobjects/extendedruneliteobjects/ExtendedRuneliteObject.java
index f233ef79559..6275094d6ad 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/runeliteobjects/extendedruneliteobjects/ExtendedRuneliteObject.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/runeliteobjects/extendedruneliteobjects/ExtendedRuneliteObject.java
@@ -191,6 +191,7 @@ public Shape getClickbox()
if (QuestPerspective.getInstanceLocalPointFromReal(client, worldPoint).isEmpty()) return null;
return Perspective.getClickbox(client,
+ client.getTopLevelWorldView(),
getRuneliteObject().getModel(),
getRuneliteObject().getOrientation(),
getRuneliteObject().getLocation().getX(),
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/runeliteobjects/extendedruneliteobjects/ReplacedNpc.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/runeliteobjects/extendedruneliteobjects/ReplacedNpc.java
index 904e99baf2f..f966b4b6d9f 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/runeliteobjects/extendedruneliteobjects/ReplacedNpc.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/runeliteobjects/extendedruneliteobjects/ReplacedNpc.java
@@ -96,7 +96,7 @@ public void addMenuEntry(MenuEntryWrapper menuEntry)
public Shape getClickbox()
{
if (npc == null) return null;
- return Perspective.getClickbox(client, npc.getModel(), npc.getOrientation(), npc.getLocalLocation().getX(), npc.getLocalLocation().getY(),
+ return Perspective.getClickbox(client, client.getTopLevelWorldView(), npc.getModel(), npc.getOrientation(), npc.getLocalLocation().getX(), npc.getLocalLocation().getY(),
Perspective.getTileHeight(client, npc.getLocalLocation(), getWorldPoint().getPlane()));
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/ui/MicrobotConfigPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/ui/MicrobotConfigPanel.java
index 365c7f4fe84..83ed9de9439 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/ui/MicrobotConfigPanel.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/ui/MicrobotConfigPanel.java
@@ -107,6 +107,11 @@ class MicrobotConfigPanel extends MicrobotPluginPanel {
private final MicrobotPluginToggleButton pluginToggle;
private MicrobotPluginConfigurationDescriptor pluginConfig = null;
+ // add near other fields
+ private final JTextField searchField = new JTextField();
+ private final Map itemIndex = new HashMap<>(); // item panel -> lowercased name
+ private final Map sectionContentByKey = new HashMap<>(); // section key -> contents panel
+ private final Map sectionPanelByDesc = new HashMap<>(); // whole section panel
@Inject
private MicrobotConfigPanel(
@@ -129,11 +134,7 @@ private MicrobotConfigPanel(
setLayout(new BorderLayout());
setBackground(ColorScheme.DARK_GRAY_COLOR);
- JPanel topPanel = new JPanel();
- topPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
- topPanel.setLayout(new BorderLayout(0, BORDER_OFFSET));
- add(topPanel, BorderLayout.NORTH);
-
+ // --- initialize mainPanel first ---
mainPanel = new MicrobotFixedWidthPanel();
mainPanel.setBorder(new EmptyBorder(8, 10, 10, 10));
mainPanel.setLayout(new DynamicGridLayout(0, 1, 0, 5));
@@ -147,21 +148,83 @@ private MicrobotConfigPanel(
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
add(scrollPane, BorderLayout.CENTER);
- JButton topPanelBackButton = new JButton(BACK_ICON);
- SwingUtil.removeButtonDecorations(topPanelBackButton);
- topPanelBackButton.setPreferredSize(new Dimension(22, 0));
- topPanelBackButton.setBorder(new EmptyBorder(0, 0, 0, 5));
- topPanelBackButton.addActionListener(e -> pluginList.getMuxer().popState());
- topPanelBackButton.setToolTipText("Back");
- topPanel.add(topPanelBackButton, BorderLayout.WEST);
+ // --- topPanel construction ---
+ JPanel topPanel = new JPanel(new BorderLayout(0, BORDER_OFFSET));
+ topPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
+ add(topPanel, BorderLayout.NORTH);
+
+ JPanel header = new JPanel(new BorderLayout());
+ JButton backBtn = new JButton(BACK_ICON);
+ SwingUtil.removeButtonDecorations(backBtn);
+ backBtn.setPreferredSize(new Dimension(22, 0));
+ backBtn.setBorder(new EmptyBorder(0, 0, 0, 5));
+ backBtn.addActionListener(e -> pluginList.getMuxer().popState());
+ backBtn.setToolTipText("Back");
pluginToggle = new MicrobotPluginToggleButton();
- topPanel.add(pluginToggle, BorderLayout.EAST);
title = new JLabel();
title.setForeground(Color.WHITE);
- topPanel.add(title);
+ header.add(backBtn, BorderLayout.WEST);
+ header.add(title, BorderLayout.CENTER);
+ header.add(pluginToggle, BorderLayout.EAST);
+ topPanel.add(header, BorderLayout.NORTH);
+
+ // --- search field ---
+ searchField.setToolTipText("Filter settings");
+ searchField.putClientProperty("JTextField.placeholderText", "Search settings...");
+ searchField.getDocument().addDocumentListener(new javax.swing.event.DocumentListener() {
+ public void insertUpdate(javax.swing.event.DocumentEvent e) {
+ applyFilter();
+ }
+
+ public void removeUpdate(javax.swing.event.DocumentEvent e) {
+ applyFilter();
+ }
+
+ public void changedUpdate(javax.swing.event.DocumentEvent e) {
+ applyFilter();
+ }
+ });
+ searchField.setPreferredSize(new Dimension(10, 26));
+ topPanel.add(searchField, BorderLayout.CENTER);
+ }
+
+
+ private void applyFilter() {
+ String q = searchField.getText();
+ String needle = q == null ? "" : q.trim().toLowerCase(Locale.ROOT);
+ // show all if empty
+ boolean noFilter = needle.isEmpty();
+
+ // toggle each item
+ for (Map.Entry e : itemIndex.entrySet()) {
+ boolean match = noFilter || e.getValue().contains(needle);
+ e.getKey().setVisible(match);
+ }
+
+ // hide section contents with no visible children
+ for (Map.Entry e : sectionContentByKey.entrySet()) {
+ JPanel contents = e.getValue();
+ boolean anyVisible = false;
+ for (Component c : contents.getComponents()) {
+ if (c.isVisible()) {
+ anyVisible = true;
+ break;
+ }
+ }
+ contents.getParent().setVisible(anyVisible); // whole section panel
+ contents.setVisible(anyVisible); // keep contents open when filtering
+
+ // auto-expand sections with matches
+ if (!anyVisible) continue;
+ // ensure expanded when filtering
+ if (!noFilter && !contents.isVisible()) contents.setVisible(true);
+ }
+
+ mainPanel.revalidate();
+ mainPanel.repaint();
}
void init(MicrobotPluginConfigurationDescriptor pluginConfig) {
@@ -211,16 +274,15 @@ private void toggleSection(ConfigSectionDescriptor csd, JButton button, JPanel c
private void rebuild() {
mainPanel.removeAll();
-
+ itemIndex.clear();
+ sectionContentByKey.clear();
+ sectionPanelByDesc.clear();
ConfigDescriptor cd = pluginConfig.getConfigDescriptor();
final Map sectionWidgets = new HashMap<>();
final Map topLevelPanels = new TreeMap<>((a, b) ->
- ComparisonChain.start()
- .compare(a.position(), b.position())
- .compare(a.name(), b.name())
- .result());
+ ComparisonChain.start().compare(a.position(), b.position()).compare(a.name(), b.name()).result());
if (cd.getInformation() != null) {
buildInformationPanel(cd.getInformation());
@@ -228,23 +290,20 @@ private void rebuild() {
for (ConfigSectionDescriptor csd : cd.getSections()) {
ConfigSection cs = csd.getSection();
- final boolean isOpen = sectionExpandStates.getOrDefault(csd, !cs.closedByDefault());
+ boolean isOpen = sectionExpandStates.getOrDefault(csd, !cs.closedByDefault());
- final JPanel section = new JPanel();
+ JPanel section = new JPanel();
section.setLayout(new BoxLayout(section, BoxLayout.Y_AXIS));
section.setMinimumSize(new Dimension(PANEL_WIDTH, 0));
- final JPanel sectionHeader = new JPanel();
- sectionHeader.setLayout(new BorderLayout());
+ JPanel sectionHeader = new JPanel(new BorderLayout());
sectionHeader.setMinimumSize(new Dimension(PANEL_WIDTH, 0));
- // For whatever reason, the header extends out by a single pixel when closed. Adding a single pixel of
- // border on the right only affects the width when closed, fixing the issue.
sectionHeader.setBorder(new CompoundBorder(
new MatteBorder(0, 0, 1, 0, ColorScheme.MEDIUM_GRAY_COLOR),
new EmptyBorder(0, 0, 3, 1)));
section.add(sectionHeader, BorderLayout.NORTH);
- final JButton sectionToggle = new JButton(isOpen ? SECTION_RETRACT_ICON : SECTION_EXPAND_ICON);
+ JButton sectionToggle = new JButton(isOpen ? SECTION_RETRACT_ICON : SECTION_EXPAND_ICON);
sectionToggle.setPreferredSize(new Dimension(18, 0));
sectionToggle.setBorder(new EmptyBorder(0, 0, 0, 5));
sectionToggle.setToolTipText(isOpen ? "Retract" : "Expand");
@@ -252,14 +311,13 @@ private void rebuild() {
sectionHeader.add(sectionToggle, BorderLayout.WEST);
String name = cs.name();
- final JLabel sectionName = new JLabel(name);
+ JLabel sectionName = new JLabel(name);
sectionName.setForeground(ColorScheme.BRAND_ORANGE);
sectionName.setFont(FontManager.getRunescapeBoldFont());
sectionName.setToolTipText("" + name + ":
" + cs.description() + "");
sectionHeader.add(sectionName, BorderLayout.CENTER);
- final JPanel sectionContents = new JPanel();
- sectionContents.setLayout(new DynamicGridLayout(0, 1, 0, 5));
+ JPanel sectionContents = new JPanel(new DynamicGridLayout(0, 1, 0, 5));
sectionContents.setMinimumSize(new Dimension(PANEL_WIDTH, 0));
sectionContents.setBorder(new CompoundBorder(
new MatteBorder(0, 0, 1, 0, ColorScheme.MEDIUM_GRAY_COLOR),
@@ -267,22 +325,23 @@ private void rebuild() {
sectionContents.setVisible(isOpen);
section.add(sectionContents, BorderLayout.SOUTH);
- // Add listeners to each part of the header so that it's easier to toggle them
- final MouseAdapter adapter = new MouseAdapter() {
+ MouseAdapter adapter = new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
toggleSection(csd, sectionToggle, sectionContents);
}
};
- sectionToggle.addActionListener(actionEvent -> toggleSection(csd, sectionToggle, sectionContents));
+
+ sectionToggle.addActionListener(ev -> toggleSection(csd, sectionToggle, sectionContents));
sectionName.addMouseListener(adapter);
sectionHeader.addMouseListener(adapter);
sectionWidgets.put(csd.getKey(), sectionContents);
+ sectionPanelByDesc.put(csd, section);
+ sectionContentByKey.put(csd.getKey(), sectionContents);
topLevelPanels.put(csd, section);
}
-
for (ConfigItemDescriptor cid : cd.getItems()) {
if (cid.getItem().hidden()) {
continue;
@@ -334,6 +393,7 @@ public void mouseClicked(MouseEvent e) {
} else {
section.add(item);
}
+ itemIndex.put(item, name.toLowerCase(Locale.ROOT));
}
topLevelPanels.values().forEach(mainPanel::add);
@@ -364,6 +424,7 @@ public void mouseClicked(MouseEvent e) {
mainPanel.add(backButton);
revalidate();
+ applyFilter();
}
private void buildInformationPanel(ConfigInformation ci) {
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/ActorModel.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/ActorModel.java
index 146e50243f6..ddfc8d29cdc 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/ActorModel.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/ActorModel.java
@@ -26,6 +26,11 @@ public WorldView getWorldView()
return actor.getWorldView();
}
+ @Override
+ public LocalPoint getCameraFocus() {
+ return Microbot.getClientThread().runOnClientThreadOptional(actor::getCameraFocus).orElse(null);
+ }
+
@Override
public int getCombatLevel()
{
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/dialogues/Rs2Dialogue.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/dialogues/Rs2Dialogue.java
index d7227e34880..1f5242b5d8d 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/dialogues/Rs2Dialogue.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/dialogues/Rs2Dialogue.java
@@ -9,6 +9,7 @@
import java.awt.event.KeyEvent;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@@ -84,7 +85,7 @@ private static boolean hasDeathContinue() {
*
* @return true if the "Continue" option is visible in either sprite-based dialogue, false otherwise.
*/
-
+
private static boolean hasSpriteContinue() {
return Rs2Widget.isWidgetVisible(InterfaceID.DIALOG_SPRITE, 0) || Rs2Widget.isWidgetVisible(InterfaceID.DIALOG_SPRITE, 3) || Rs2Widget.isWidgetVisible(InterfaceID.DIALOG_DOUBLE_SPRITE, 4);
}
@@ -138,10 +139,10 @@ private static boolean hasSpellFilterContinue() {
public static boolean hasSelectAnOption() {
boolean isWidgetVisible = Rs2Widget.isWidgetVisible(InterfaceID.DIALOG_OPTION, 1);
if (!isWidgetVisible) return false;
-
+
Widget widget = Rs2Widget.getWidget(InterfaceID.DIALOG_OPTION, 1);
if (widget == null) return false;
-
+
return widget.getDynamicChildren() != null;
}
@@ -337,6 +338,36 @@ public static boolean keyPressForDialogueOption(int index) {
return true;
}
+ /**
+ * Attempts to click on a dialogue option based on the specified text(s). The method
+ * performs a partial matching depending on the provided parameter and will return
+ * whether the operation was successful.
+ *
+ * @param texts varargs parameter representing the*/
+ public static boolean clickOption(String... texts){
+ return clickOption(false, texts);
+ }
+
+ /**
+ * Attempts to click on a dialogue option based on the specified text(s). The method can
+ * perform an exact or partial matching depending on the provided parameter and will return
+ * whether the operation was successful.
+ *
+ * @param exact specifies whether the matching should be exact (true) or partial (false).
+ * @param texts varargs parameter representing the*/
+ public static boolean clickOption(boolean exact, String... texts){
+ if (!hasSelectAnOption()) return false;
+ List options = getDialogueOptions();
+ if(options.isEmpty()) return false;
+
+ Widget dialogueOption = options.stream()
+ .filter(dialop -> exact ? Arrays.stream(texts).anyMatch(t -> dialop.getText().equalsIgnoreCase(t)) : Arrays.stream(texts).anyMatch(t -> dialop.getText().toLowerCase().contains(t.toLowerCase())))
+ .findFirst()
+ .orElse(null);
+ if (dialogueOption == null) return false;
+ return Rs2Widget.clickWidget(dialogueOption);
+ }
+
/**
* Attempts to click on a dialogue option widget with the specified text.
*
@@ -449,7 +480,7 @@ public static boolean sleepUntilHasQuestion(String text, boolean exact) {
public static boolean sleepUntilHasQuestion(String text) {
return sleepUntilHasQuestion(text, false);
}
-
+
/**
* Checks if the combination dialogue widget is currently visible.
*
@@ -597,11 +628,11 @@ public static boolean clickCombinationOption(String text, boolean exact) {
if (!hasCombinationDialogue()) return false;
Widget option = getCombinationOption(text, exact);
-
+
if (option == null) return false;
-
+
return Rs2Widget.clickWidget(option);
-
+
}
/**
@@ -627,7 +658,7 @@ public static boolean sleepUntilHasCombinationDialogue() {
* Pauses the current thread until a specific combination dialogue option becomes available.
*
* This method continuously checks for a combination dialogue option that matches the specified
- * text. If an exact match is required, it will search for an option that exactly matches the text;
+ * text. If an exact match is required, it will search for an option that exactly matches the text;
* otherwise, it will look for an option containing the text.
*
* @param text the text to search for within the combination dialogue options.
@@ -649,7 +680,7 @@ public static boolean sleepUntilHasCombinationOption(String text, boolean exact)
public static boolean sleepUntilHasCombinationOption(String text) {
return sleepUntilHasCombinationOption(text, false);
}
-
+
/**
* Determines whether the game is currently in a cutscene.
*
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/equipment/Rs2Equipment.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/equipment/Rs2Equipment.java
index 580362819fe..f917104088c 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/equipment/Rs2Equipment.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/equipment/Rs2Equipment.java
@@ -6,21 +6,19 @@
import net.runelite.api.ItemContainer;
import net.runelite.api.MenuAction;
import net.runelite.api.events.ItemContainerChanged;
+import net.runelite.api.gameval.InterfaceID;
import net.runelite.api.gameval.InventoryID;
+import net.runelite.api.widgets.Widget;
import net.runelite.client.plugins.microbot.Microbot;
import net.runelite.client.plugins.microbot.globval.enums.InterfaceTab;
import net.runelite.client.plugins.microbot.util.inventory.Rs2ItemModel;
import net.runelite.client.plugins.microbot.util.menu.NewMenuEntry;
import net.runelite.client.plugins.microbot.util.tabs.Rs2Tab;
+import net.runelite.client.plugins.microbot.util.widget.Rs2Widget;
import org.slf4j.event.Level;
import java.awt.Rectangle;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
+import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Stream;
@@ -313,6 +311,7 @@ public static void invokeMenu(Rs2ItemModel rs2Item, String action) {
int param0 = -1;
int param1 = -1;
int identifier;
+ String target = rs2Item.getName();
MenuAction menuAction = MenuAction.CC_OP;
if (action.equalsIgnoreCase("remove")) {
identifier = 1;
@@ -325,37 +324,69 @@ public static void invokeMenu(Rs2ItemModel rs2Item, String action) {
break;
}
}
+ // We could not find the action in the equipment actions, so we try to find it in the sub-menu actions
if (identifier == -1) {
- Microbot.log("Item=" + rs2Item.getName() + " does not have action=" + action + ". Actions=" + Arrays.toString(actions.stream().filter(Objects::nonNull).toArray()), Level.ERROR);
- return;
+ Map.Entry subMenuEntry = rs2Item.getIndexOfSubAction(action);
+ if (subMenuEntry == null) {
+ Microbot.log("Item=" + rs2Item.getName() + " does not have a subaction=" + action, Level.ERROR);
+ return;
+ }
+ int mainMenuIndex = actions.indexOf(subMenuEntry.getKey());
+ if (mainMenuIndex < 0) {
+ Microbot.log("Cannot find action=%s, in main actions=%s, mainMenuIndex=%s", subMenuEntry.getKey(), String.join(", ", actions), mainMenuIndex, Level.ERROR);
+ return;
+ }
+ target = "";
+ identifier = NewMenuEntry.findIdentifier(subMenuEntry.getValue() + 1, mainMenuIndex + 2);
}
}
-
+ Rectangle rectangle = new Rectangle(1, 1, Microbot.getClient().getCanvasWidth(), Microbot.getClient().getCanvasHeight());
if (rs2Item.getSlot() == EquipmentInventorySlot.CAPE.getSlotIdx()) {
param1 = 25362448;
+ rectangle = getSafeBounds(InterfaceID.WORNITEMS,16);
} else if (rs2Item.getSlot() == EquipmentInventorySlot.HEAD.getSlotIdx()) {
param1 = 25362447;
+ rectangle = getSafeBounds(InterfaceID.WORNITEMS,15);
} else if (rs2Item.getSlot() == EquipmentInventorySlot.AMMO.getSlotIdx()) {
param1 = 25362457;
+ rectangle = getSafeBounds(InterfaceID.WORNITEMS,25);
} else if (rs2Item.getSlot() == EquipmentInventorySlot.AMULET.getSlotIdx()) {
param1 = 25362449;
+ rectangle = getSafeBounds(InterfaceID.WORNITEMS,17);
} else if (rs2Item.getSlot() == EquipmentInventorySlot.WEAPON.getSlotIdx()) {
param1 = 25362450;
+ rectangle = getSafeBounds(InterfaceID.WORNITEMS,18);
} else if (rs2Item.getSlot() == EquipmentInventorySlot.BODY.getSlotIdx()) {
param1 = 25362451;
+ rectangle = getSafeBounds(InterfaceID.WORNITEMS,19);
} else if (rs2Item.getSlot() == EquipmentInventorySlot.SHIELD.getSlotIdx()) {
param1 = 25362452;
+ rectangle = getSafeBounds(InterfaceID.WORNITEMS,20);
} else if (rs2Item.getSlot() == EquipmentInventorySlot.LEGS.getSlotIdx()) {
param1 = 25362453;
+ rectangle = getSafeBounds(InterfaceID.WORNITEMS,21);
} else if (rs2Item.getSlot() == EquipmentInventorySlot.GLOVES.getSlotIdx()) {
param1 = 25362454;
+ rectangle = getSafeBounds(InterfaceID.WORNITEMS,22);
} else if (rs2Item.getSlot() == EquipmentInventorySlot.BOOTS.getSlotIdx()) {
param1 = 25362455;
+ rectangle = getSafeBounds(InterfaceID.WORNITEMS,23);
} else if (rs2Item.getSlot() == EquipmentInventorySlot.RING.getSlotIdx()) {
param1 = 25362456;
+ rectangle = getSafeBounds(InterfaceID.WORNITEMS,24);
}
- Microbot.doInvoke(new NewMenuEntry(param0, param1, menuAction.getId(), identifier, -1, rs2Item.getName()), new Rectangle(1, 1, Microbot.getClient().getCanvasWidth(), Microbot.getClient().getCanvasHeight()));
+ Microbot.doInvoke(new NewMenuEntry(param0, param1, menuAction.getId(), identifier, -1, rs2Item.getName()), rectangle);
//Rs2Reflection.invokeMenu(param0, param1, menuAction.getId(), identifier, rs2Item.id, action, target, -1, -1);
}
+
+ private static Rectangle getSafeBounds(int interfaceId, int childId) {
+ Widget widget = Rs2Widget.getWidget(interfaceId, childId);
+ if (widget != null && widget.getBounds() != null) {
+ return widget.getBounds();
+ }
+ return new Rectangle(1, 1,
+ Microbot.getClient().getCanvasWidth(),
+ Microbot.getClient().getCanvasHeight());
+ }
}
\ No newline at end of file
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java
index e06669b634e..cfe623a2f72 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java
@@ -1206,20 +1206,6 @@ public static boolean interact(int id) {
public static boolean interact(int id, String action) {
return interact(get(id), action);
}
- /**
- * Interacts with an item with the specified ID in the inventory using the specified action.
- *
- * @param id The ID of the item to interact with.
- * @param action The action to perform on the item.
- *
- * @return True if the interaction was successful, false otherwise.
- */
- public static boolean interact(int id, String action, int identifier) {
- final Rs2ItemModel rs2Item = get(id);
- if (rs2Item == null) return false;
- invokeMenu(rs2Item, action, identifier);
- return true;
- }
/**
* Interacts with an item with the specified name in the inventory using the first available action.
@@ -1888,17 +1874,16 @@ public static boolean hasRunePouch() {
*
* @param rs2Item The current item to interact with.
* @param action The action to be used on the item.
- * @param providedIdentifier The identifier to use; if -1, compute using the old logic.
*/
- private static void invokeMenu(Rs2ItemModel rs2Item, String action, int providedIdentifier) {
+ private static void invokeMenu(Rs2ItemModel rs2Item, String action) {
if (rs2Item == null) return;
-
Rs2Tab.switchToInventoryTab();
Microbot.status = action + " " + rs2Item.getName();
int param0;
int param1;
int identifier = -1;
+ String target = rs2Item.getName();
MenuAction menuAction = MenuAction.CC_OP;
Widget[] inventoryWidgets;
param0 = rs2Item.getSlot();
@@ -1939,7 +1924,21 @@ private static void invokeMenu(Rs2ItemModel rs2Item, String action, int provided
itemWidget.getActions() :
rs2Item.getInventoryActions();
- identifier = providedIdentifier == -1 ? indexOfIgnoreCase(stripColTags(actions), action) + 1 : providedIdentifier;
+ int simpleIndex = indexOfIgnoreCase(stripColTags(actions), action);
+ if (simpleIndex != -1) {
+ identifier = simpleIndex + 1;
+ } else {
+ // We could not find the action in the item widget's actions, so we try to find it in the sub-menu actions
+ Map.Entry subActionMap = rs2Item.getIndexOfSubAction(action);
+ if (subActionMap == null) {
+ Microbot.log("Item=" + rs2Item.getName() + " does not have action=" + action, Level.ERROR);
+ return;
+ }
+ // The main menu index depends on the inventory interface from which this item is interacted with
+ int mainMenuIndex = java.util.Arrays.asList(actions).indexOf(subActionMap.getKey());
+ identifier = NewMenuEntry.findIdentifier(subActionMap.getValue() + 1, mainMenuIndex + 1);
+ target = "";
+ }
}
@@ -1951,7 +1950,7 @@ private static void invokeMenu(Rs2ItemModel rs2Item, String action, int provided
menuAction = MenuAction.WIDGET_TARGET_ON_WIDGET;
}
- Microbot.doInvoke(new NewMenuEntry(action, param0, param1, menuAction.getId(), identifier, rs2Item.getId(), rs2Item.getName()), (itemBounds(rs2Item) == null) ? new Rectangle(1, 1) : itemBounds(rs2Item));
+ Microbot.doInvoke(new NewMenuEntry(action, param0, param1, menuAction.getId(), identifier, rs2Item.getId(), target), (itemBounds(rs2Item) == null) ? new Rectangle(1, 1) : itemBounds(rs2Item));
if (action.equalsIgnoreCase("destroy")) {
sleepUntil(() -> Rs2Widget.isWidgetVisible(584, 0));
@@ -1960,16 +1959,6 @@ private static void invokeMenu(Rs2ItemModel rs2Item, String action, int provided
}
- /**
- * Method executes menu actions
- *
- * @param rs2Item Current item to interact with
- * @param action Action used on the item
- */
- private static void invokeMenu(Rs2ItemModel rs2Item, String action) {
- invokeMenu(rs2Item, action, -1);
- }
-
public static Widget getInventory() {
final int BANK_PIN_INVENTORY_ITEM_CONTAINER = 17563648;
final int SHOP_INVENTORY_ITEM_CONTAINER = 19726336;
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2ItemModel.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2ItemModel.java
index 2dba3e2e466..5a39cdca884 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2ItemModel.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2ItemModel.java
@@ -6,14 +6,11 @@
import net.runelite.api.EquipmentInventorySlot;
import net.runelite.api.Item;
import net.runelite.api.ItemComposition;
-import net.runelite.api.gameval.ItemID;
import net.runelite.api.ParamID;
+import net.runelite.api.gameval.ItemID;
import net.runelite.client.plugins.microbot.Microbot;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
+import java.util.*;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
@@ -23,18 +20,19 @@ public class Rs2ItemModel {
private int id;
@Getter
@Setter
- private int quantity;
+ private int quantity;
@Getter
- private int slot = -1;
- private String name;
- private String[] inventoryActions;
+ private int slot = -1;
+ private String name;
+ private String[] inventoryActions;
+ private String[][] subops;
@Getter
- private List equipmentActions = new ArrayList<>();
- private boolean isStackable;
- private boolean isNoted;
- private boolean isTradeable;
- private ItemComposition itemComposition;
- private int[] wearableActionIndexes = new int[]{
+ private List equipmentActions = new ArrayList<>();
+ private boolean isStackable;
+ private boolean isNoted;
+ private boolean isTradeable;
+ private ItemComposition itemComposition;
+ private int[] wearableActionIndexes = new int[]{
ParamID.OC_ITEM_OP1,
ParamID.OC_ITEM_OP2,
ParamID.OC_ITEM_OP3,
@@ -43,7 +41,7 @@ public class Rs2ItemModel {
ParamID.OC_ITEM_OP6,
ParamID.OC_ITEM_OP7,
ParamID.OC_ITEM_OP8
- };
+ };
public Rs2ItemModel(Item item, ItemComposition itemComposition, int slot) {
@@ -57,10 +55,10 @@ public Rs2ItemModel(Item item, ItemComposition itemComposition, int slot) {
* Creates an Rs2ItemModel from cached data (ID, quantity, slot).
* This is used when loading bank data from config where we don't have the full ItemComposition.
* ItemComposition data will be loaded lazily when needed.
- *
- * @param id Item ID
+ *
+ * @param id Item ID
* @param quantity Item quantity
- * @param slot Item slot position
+ * @param slot Item slot position
* @return Rs2ItemModel with basic data, ItemComposition loaded lazily
*/
public static Rs2ItemModel createFromCache(int id, int quantity, int slot) {
@@ -69,12 +67,12 @@ public static Rs2ItemModel createFromCache(int id, int quantity, int slot) {
/**
- * Constructor for creating Rs2ItemModel with explicit ItemComposition.
+ * Constructor for creating Rs2ItemModel with explicit ItemComposition.
*/
public Rs2ItemModel(int id, int quantity, int slot, ItemComposition itemComposition) {
this.id = id;
this.quantity = quantity;
- this.slot = slot;
+ this.slot = slot;
if (itemComposition == null) {
//lazy loading will handle this
initializeDefaults();
@@ -82,6 +80,7 @@ public Rs2ItemModel(int id, int quantity, int slot, ItemComposition itemComposit
initializeFromComposition(itemComposition);
}
}
+
/**
* Private constructor for creating Rs2ItemModel from cached data.
* ItemComposition will be loaded lazily when needed.
@@ -91,12 +90,12 @@ public Rs2ItemModel(int id, int quantity, int slot) {
this.quantity = quantity;
this.slot = slot;
ItemComposition itemComposition = Microbot.getClientThread().runOnClientThreadOptional(() ->
- Microbot.getClient().getItemDefinition(id)).orElse(null);
- if (itemComposition == null) {
+ Microbot.getClient().getItemDefinition(id)).orElse(null);
+ if (itemComposition == null) {
initializeDefaults();
- }else{
+ } else {
initializeFromComposition(itemComposition);
- }
+ }
}
/**
@@ -104,29 +103,8 @@ public Rs2ItemModel(int id, int quantity, int slot) {
* This ensures we can work with cached items while minimizing performance impact.
*/
private void ensureCompositionLoaded() {
-
if (itemComposition == null && id > 0) {
- this.itemComposition = Microbot.getClientThread().runOnClientThreadOptional(()-> Microbot.getClient().getItemDefinition(id)).orElse(null);
- if (itemComposition != null) {
- this.name = itemComposition.getName();
- this.isStackable = itemComposition.isStackable();
- this.isNoted = itemComposition.getNote() == 799;
- if (this.isNoted) {
- Microbot.getClientThread().runOnClientThreadOptional(() ->
- Microbot.getClient().getItemDefinition(itemComposition.getLinkedNoteId())).ifPresent(itemDefinition -> this.isTradeable = itemDefinition.isTradeable());
- } else {
- this.isTradeable = itemComposition.isTradeable();
- }
- this.inventoryActions = itemComposition.getInventoryActions();
- Microbot.getClientThread().runOnClientThreadOptional(() -> {
- addEquipmentActions(itemComposition);
- return true;
- });
- }
- else {
- // If we can't load the ItemComposition, set defaults
- log.warn("Failed to load ItemComposition for id: {}, setting defaults", id);
- }
+ Microbot.getClientThread().runOnClientThreadOptional(() -> Microbot.getClient().getItemDefinition(id)).ifPresent(this::initializeFromComposition);
}
}
@@ -179,6 +157,55 @@ public String[] getInventoryActions() {
}
return inventoryActions;
}
+
+ /**
+ * Gets the sub-actions, loading composition if needed.
+ * This returns a list of sub-menu actions that can be performed on the item.
+ * The first index corresponds to the main menu's index, the second index is the sub-menu index.
+ *
+ * @return A list of sub-actions, or null if none are available
+ */
+ public String[][] getSubops() {
+ if (itemComposition == null) {
+ ensureCompositionLoaded();
+ }
+ return subops;
+ }
+
+ /**
+ * Retrieves the index of a sub-action from the sub-actions list, matching the given action.
+ *
+ * This method searches through the sub-actions of the item, attempting to find a match
+ * for the specified action. If a match is found, it returns the corresponding main action
+ * and index of the sub-action. If no match is found or if the sub-actions are unavailable,
+ * it returns a default result with null and -1.
+ *
+ * @param action The action to search for in the sub-actions list. Case-insensitive comparison is used.
+ * @return A Map.Entry containing the inventory action (String) and the index of the sub-action (Integer)
+ * if a match is found; otherwise, returns a Map.Entry with null and -1.
+ */
+ public Map.Entry getIndexOfSubAction(String action) {
+ if (action == null) return null;
+ String alc = action.toLowerCase();
+
+ String[][] subOps = getSubops();
+ if (subOps == null) {
+ return null;
+ }
+ for (int i = 0; i < subOps.length; i++) {
+ String[] subOpsActions = subOps[i];
+ if (subOpsActions == null) continue;
+ for (int j = 0; j < subOpsActions.length; j++) {
+ String subObsAction = subOpsActions[j];
+ if (subObsAction != null && subObsAction.toLowerCase().contains(alc)) {
+ return Map.entry(inventoryActions[i], j);
+ }
+ }
+ }
+
+ return null;
+ }
+
/**
* Gets the equipment actions, loading composition if needed.
* This returns a list of actions that can be performed on the item when equipped.
@@ -200,15 +227,15 @@ public ItemComposition getItemComposition() {
return itemComposition;
}
- public boolean isFood() {
- if (isNoted()) return false;
+ public boolean isFood() {
+ if (isNoted()) return false;
- String lowerName = getName().toLowerCase();
+ String lowerName = getName().toLowerCase();
- boolean isEdible = Arrays.stream(getInventoryActions()).anyMatch(action -> action != null && action.equalsIgnoreCase("eat"));
+ boolean isEdible = Arrays.stream(getInventoryActions()).anyMatch(action -> action != null && action.equalsIgnoreCase("eat"));
- return (isEdible || lowerName.contains("jug of wine")) && !lowerName.contains("rock cake");
- }
+ return (isEdible || lowerName.contains("jug of wine")) && !lowerName.contains("rock cake");
+ }
private void addEquipmentActions(ItemComposition itemComposition) {
for (int i = 0; i < wearableActionIndexes.length; i++) {
@@ -235,7 +262,7 @@ public int getHaPrice() {
public boolean isHaProfitable() {
int natureRunePrice = Microbot.getClientThread().runOnClientThreadOptional(() ->
Microbot.getItemManager().getItemPrice(ItemID.NATURERUNE)).orElse(0);
- return (getHaPrice() - natureRunePrice) > (getPrice()/quantity) && isTradeable;
+ return (getHaPrice() - natureRunePrice) > (getPrice() / quantity) && isTradeable;
}
@@ -271,21 +298,21 @@ public String toString() {
sb.append("\tisNoted: ").append(isNoted()).append("\n");
sb.append("\tisTradeable: ").append(isTradeable()).append("\n");
sb.append("\tisFood: ").append(isFood()).append("\n");
-
+
// Price information
int price = getPrice();
sb.append("\tprice: ").append(price).append(" gp (total)\n");
if (quantity > 0) {
sb.append("\tunitPrice: ").append(price / quantity).append(" gp (each)\n");
}
-
+
// High Alchemy information
if (itemComposition != null) {
int haPrice = getHaPrice();
sb.append("\thaPrice: ").append(haPrice).append(" gp\n");
sb.append("\tisHaProfitable: ").append(isHaProfitable()).append("\n");
}
-
+
// Actions
String[] invActions = getInventoryActions();
if (invActions != null && invActions.length > 0) {
@@ -298,7 +325,7 @@ public String toString() {
}
sb.append("]\n");
}
-
+
// Equipment actions
if (!equipmentActions.isEmpty()) {
sb.append("\tequipmentActions: [");
@@ -312,10 +339,10 @@ public String toString() {
}
sb.append("]\n");
}
-
+
// Composition status
sb.append("\tcompositionLoaded: ").append(itemComposition != null).append("\n");
-
+
sb.append("}");
return sb.toString();
}
@@ -336,7 +363,7 @@ public static Predicate matches(int... ids) {
public static Predicate matches(EquipmentInventorySlot... slots) {
return matches(slots, (item, slot) -> item.getSlot() == slot.getSlotIdx());
}
-
+
/**
* Initialize default values when ItemComposition is not available.
*/
@@ -348,18 +375,18 @@ private void initializeDefaults() {
this.inventoryActions = new String[0];
this.itemComposition = null;
}
-
+
/**
* Gets the noted variant of this item if it exists and is stackable.
* Returns this item's ID if the item is already noted or has no noted variant.
- *
+ *
* @return The noted item ID if available, otherwise the original item ID
*/
public int getNotedId() {
if (itemComposition == null) {
ensureCompositionLoaded();
}
-
+
if (itemComposition == null) {
return id; // fallback to original ID
}
@@ -368,18 +395,18 @@ public int getNotedId() {
}
return getNotedItemId(itemComposition);
}
-
+
/**
* Gets the unnoted variant of this item if it exists.
* Returns this item's ID if the item is already unnoted or has no unnoted variant.
- *
+ *
* @return The unnoted item ID if available, otherwise the original item ID
*/
public int getUnNotedId() {
if (itemComposition == null) {
ensureCompositionLoaded();
}
-
+
if (itemComposition == null) {
return id; // fallback to original ID
}
@@ -389,27 +416,27 @@ public int getUnNotedId() {
}
return getUnNotedId(itemComposition);
}
-
+
/**
* Gets the linked item ID (noted/unnoted counterpart) of this item.
- *
+ *
* @return The linked item ID
*/
public int getLinkedId() {
if (itemComposition == null) {
ensureCompositionLoaded();
}
-
+
if (itemComposition == null) {
return id; // fallback to original ID
}
-
+
return getLinkedItemId(itemComposition);
}
-
+
/**
* Static method to get the noted variant of an item ID.
- *
+ *
* @param itemId The original item ID
* @return The noted item ID if available, otherwise the original item ID
*/
@@ -417,13 +444,13 @@ public static int getNotedId(int itemId) {
ItemComposition composition = Microbot.getClientThread().runOnClientThreadOptional(() ->
Microbot.getClient().getItemDefinition(itemId)
).orElse(null);
-
+
return getNotedItemId(composition);
}
-
+
/**
* Static method to get the unnoted variant of an item ID.
- *
+ *
* @param itemId The original item ID
* @return The unnoted item ID if available, otherwise the original item ID
*/
@@ -431,14 +458,14 @@ public static int getUnNotedId(int itemId) {
ItemComposition composition = Microbot.getClientThread().runOnClientThreadOptional(() ->
Microbot.getClient().getItemDefinition(itemId)
).orElse(null);
-
+
return getUnNotedId(composition);
}
-
+
/**
* Helper method to get the noted variant from ItemComposition.
* Returns the noted ID if the item has a stackable noted variant.
- *
+ *
* @param composition The ItemComposition to check
* @return The noted item ID if available, otherwise the original item ID
*/
@@ -447,26 +474,26 @@ private static int getNotedItemId(ItemComposition composition) {
if (composition == null) {
return -1;
}
-
+
int itemId = composition.getId();
boolean isNoted = composition.getNote() == 799;
int linkedNoteId = composition.getLinkedNoteId();
// if already stackable, return original ID
- if ( (isNoted && composition.isStackable()) || linkedNoteId == - 1) {
+ if ((isNoted && composition.isStackable()) || linkedNoteId == -1) {
return itemId;
- }
-
+ }
+
return linkedNoteId;
-
+
} catch (Exception e) {
return -1; // fall back to original on error
}
}
-
+
/**
* Helper method to get the unnoted variant from ItemComposition.
* Returns the unnoted ID if the item has an unnoted variant.
- *
+ *
* @param composition The ItemComposition to check
* @return The unnoted item ID if available, otherwise the original item ID
*/
@@ -475,25 +502,25 @@ private static int getUnNotedId(ItemComposition composition) {
if (composition == null) {
log.warn("Could not get item composition for item ID, returning original ID");
return -1;
- }
+ }
int itemId = composition.getId();
boolean isNoted = composition.getNote() == 799;
int linkedNoteId = composition.getLinkedNoteId();
// if already stackable, return original ID
- if ( (!isNoted && !composition.isStackable()) || linkedNoteId == - 1) {
+ if ((!isNoted && !composition.isStackable()) || linkedNoteId == -1) {
return itemId;
- }
+ }
return linkedNoteId;
-
+
} catch (Exception e) {
log.error("Error getting unnoted item ID: {}", e.getMessage());
return -1; // fall back to original on error
}
}
-
+
/**
* Helper method to get the linked item ID from ItemComposition.
- *
+ *
* @param composition The ItemComposition to check
* @return The linked item ID
*/
@@ -503,10 +530,10 @@ private static int getLinkedItemId(ItemComposition composition) {
log.warn("no item composition for item ID, returning -1");
return -1;
}
-
+
// check if this item has a noted variant
return composition.getLinkedNoteId();
-
+
} catch (Exception e) {
log.error("Error getting linked item ID: {}", e.getMessage());
return -1; // fall back on error
@@ -520,7 +547,7 @@ private void initializeFromComposition(ItemComposition itemComposition) {
this.name = itemComposition.getName();
this.isStackable = itemComposition.isStackable();
this.isNoted = itemComposition.getNote() == 799;
-
+
// Handle noted item tradeable status
if (this.isNoted) {
Microbot.getClientThread().runOnClientThreadOptional(() ->
@@ -529,14 +556,50 @@ private void initializeFromComposition(ItemComposition itemComposition) {
} else {
this.isTradeable = itemComposition.isTradeable();
}
-
+
this.inventoryActions = itemComposition.getInventoryActions();
+ // This has to be run in microbot's client thread
+ this.subops = Microbot.getClientThread().runOnClientThreadOptional(itemComposition::getSubops).orElse(null);
this.itemComposition = itemComposition;
-
+
// Add equipment actions asynchronously
Microbot.getClientThread().runOnClientThreadOptional(() -> {
addEquipmentActions(itemComposition);
return true;
});
}
+
+ /**
+ * Retrieves an inventory action that contains the specified substring, ignoring case sensitivity.
+ * Searches through all non-null inventory actions and returns the first match.
+ *
+ * @param partOfAction The substring to search for within the inventory actions. Case-insensitive comparison is used.
+ * @return The first matching inventory action that contains the specified substring, or null if no match is found.
+ */
+ public String getAction(String partOfAction) {
+ return Arrays.stream(getInventoryActions())
+ .filter(Objects::nonNull)
+ .filter(x -> x.toLowerCase().contains(partOfAction))
+ .findFirst().orElse(null);
+ }
+
+ /**
+ * Retrieves the most relevant action from a list of possible actions by matching them against
+ * the inventory actions of the item. The relevance is determined by the order of the given actions
+ * and their occurrence within the inventory actions.
+ *
+ * @param actions The list of actions to search for, provided as varargs. Null values will be ignored.
+ * @return The most relevant matching action from the inventory actions, or null if no match is found.
+ */
+ public String getActionFromList(List actions) {
+ return Arrays.stream(getInventoryActions())
+ .filter(action -> action != null && actions.stream().anyMatch(keyword -> action.toLowerCase().contains(keyword.toLowerCase())))
+ .min(Comparator.comparingInt(action ->
+ actions.stream()
+ .filter(keyword -> action.toLowerCase().contains(keyword.toLowerCase()))
+ .mapToInt(actions::indexOf)
+ .findFirst()
+ .orElse(Integer.MAX_VALUE)
+ )).orElse(null);
+ }
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java
index 72c4789283c..0d77a6b9029 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java
@@ -76,7 +76,7 @@ public static Rectangle getActorClickbox(Actor actor) {
}
- Shape clickbox = Microbot.getClientThread().runOnClientThreadOptional(() -> Perspective.getClickbox(Microbot.getClient(), actor.getModel(), actor.getCurrentOrientation(), lp.getX(), lp.getY(),
+ Shape clickbox = Microbot.getClientThread().runOnClientThreadOptional(() -> Perspective.getClickbox(Microbot.getClient(), Microbot.getClient().getTopLevelWorldView(), actor.getModel(), actor.getCurrentOrientation(), lp.getX(), lp.getY(),
Perspective.getTileHeight(Microbot.getClient(), lp, actor.getWorldLocation().getPlane())))
.orElse(null);
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/Rs2Walker.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/Rs2Walker.java
index f4f4e6f3841..cdd8c7ebb88 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/Rs2Walker.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/Rs2Walker.java
@@ -3,26 +3,24 @@
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
-import net.runelite.api.Point;
import net.runelite.api.*;
+import net.runelite.api.Point;
import net.runelite.api.annotations.Component;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldArea;
import net.runelite.api.coords.WorldPoint;
+import net.runelite.api.gameval.*;
import net.runelite.api.gameval.ItemID;
import net.runelite.api.gameval.NpcID;
import net.runelite.api.gameval.ObjectID;
-import net.runelite.api.gameval.*;
import net.runelite.api.widgets.ComponentID;
import net.runelite.api.widgets.Widget;
import net.runelite.client.plugins.devtools.MovementFlag;
import net.runelite.client.plugins.microbot.Microbot;
import net.runelite.client.plugins.microbot.globval.enums.InterfaceTab;
-import net.runelite.client.plugins.microbot.shortestpath.ShortestPathConfig;
-import net.runelite.client.plugins.microbot.shortestpath.ShortestPathPlugin;
-import net.runelite.client.plugins.microbot.shortestpath.Transport;
-import net.runelite.client.plugins.microbot.shortestpath.TransportType;
+import net.runelite.client.plugins.microbot.shortestpath.*;
import net.runelite.client.plugins.microbot.shortestpath.pathfinder.Pathfinder;
+import net.runelite.client.plugins.microbot.shortestpath.pathfinder.PathfinderConfig;
import net.runelite.client.plugins.microbot.util.bank.Rs2Bank;
import net.runelite.client.plugins.microbot.util.bank.enums.BankLocation;
import net.runelite.client.plugins.microbot.util.camera.Rs2Camera;
@@ -55,12 +53,10 @@
import javax.inject.Named;
import java.awt.*;
-import java.util.List;
import java.util.*;
+import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@@ -1836,7 +1832,8 @@ private static boolean handleTeleportSpell(Transport transport) {
}
private static boolean handleTeleportItem(Transport transport) {
- if (Rs2Pvp.isInWilderness() && (Rs2Pvp.getWildernessLevelFrom(Rs2Player.getWorldLocation()) > (transport.getMaxWildernessLevel() + 1))) return false;
+ if (Rs2Pvp.isInWilderness() && (Rs2Pvp.getWildernessLevelFrom(Rs2Player.getWorldLocation()) > (transport.getMaxWildernessLevel() + 1)))
+ return false;
boolean succesfullAction = false;
for (Set itemIds : transport.getItemIdRequirements()) {
if (succesfullAction)
@@ -1848,127 +1845,95 @@ private static boolean handleTeleportItem(Transport transport) {
if (succesfullAction) break;
//If an action is succesfully we break out of the loop
- succesfullAction = handleInventoryTeleports(transport, itemId) || handleWearableTeleports(transport, itemId);
-
+ succesfullAction = handleWearableTeleports(transport, itemId) || handleInventoryTeleports(transport, itemId);
}
}
return succesfullAction;
}
- private static boolean handleInventoryTeleports(Transport transport, int itemId) {
+ private static boolean handleInventoryTeleports(Transport transport, int itemId) {
Rs2ItemModel rs2Item = Rs2Inventory.get(itemId);
if (rs2Item == null) return false;
- List locationKeyWords = Arrays.asList("farm", "monastery", "lletya", "prifddinas", "rellekka", "waterbirth island", "neitiznot", "jatiszo",
- "ver sinhaza", "darkmeyer", "slepe", "troll stronghold", "weiss", "ecto", "burgh", "duradel", "gem mine", "nardah", "kalphite cave", "Tele to POH", "inside", "outside",
- "kourend woodland", "mount karuulm", "outside", "fishing guild", "otto's grotto", "stronghold slayer cave", "slayer tower", "fremennik", "tarn's lair", "dark beasts");
List genericKeyWords = Arrays.asList("invoke", "empty", "consume", "open", "teleport", "rub", "break", "reminisce", "signal", "play", "commune", "squash");
+ // Return true when the item can be used to teleport to multiple places
boolean hasMultipleDestination = transport.getDisplayInfo().contains(":");
String destination = hasMultipleDestination
? transport.getDisplayInfo().split(":")[1].trim().toLowerCase()
: transport.getDisplayInfo().trim().toLowerCase();
- // Check location keywords based on multiple destinations
- String itemAction = hasMultipleDestination
- ? Arrays.stream(rs2Item.getInventoryActions())
- .filter(action -> action != null && locationKeyWords.stream().anyMatch(keyword ->
- destination.contains(keyword.toLowerCase()) && action.toLowerCase().contains(keyword.toLowerCase())))
- .findFirst()
- .orElse(null)
- : Arrays.stream(rs2Item.getInventoryActions())
- .filter(action -> action != null && locationKeyWords.stream().anyMatch(keyword -> action.toLowerCase().contains(keyword.toLowerCase())))
- .findFirst()
- .orElse(null);
+ boolean wildernessTransport = PathfinderConfig.isInWilderness(WorldPointUtil.packWorldPoint(transport.getDestination()));
- // If no location-based action found, try generic actions
- if (itemAction == null) {
+ log.debug("Trying to find action for destination={}", destination);
+ // Check if item has destination as direct action
+ String itemAction = rs2Item.getAction(destination);
- itemAction = Arrays.stream(rs2Item.getInventoryActions())
- .filter(action -> action != null && genericKeyWords.stream().anyMatch(keyword -> action.toLowerCase().contains(keyword.toLowerCase())))
- .min(Comparator.comparingInt(action ->
- genericKeyWords.stream()
- .filter(keyword -> action.toLowerCase().contains(keyword.toLowerCase()))
- .mapToInt(genericKeyWords::indexOf)
- .findFirst()
- .orElse(Integer.MAX_VALUE)
- ))
- .orElse(null);
+ // Check if item has destination as sub-menu action
+ Map.Entry sub = rs2Item.getIndexOfSubAction(destination);
+ if (itemAction == null && sub != null && sub.getKey() != null) {
+ itemAction = destination;
}
- if (itemAction == null) return false;
-
- // Check the first character of the item name, if it is a number return true
- boolean hasMenuOption = !transport.getDisplayInfo().isEmpty() && Character.isDigit(transport.getDisplayInfo().charAt(0));
-
- if (!hasMenuOption) {
- if (Rs2Inventory.interact(itemId, itemAction)) {
- if (itemAction.equalsIgnoreCase("rub") && (itemId == ItemID.XERIC_TALISMAN || transport.getDisplayInfo().toLowerCase().contains("skills necklace"))) {
- return interactWithAdventureLog(transport);
- }
-
- if (itemAction.equalsIgnoreCase("rub") && transport.getDisplayInfo().toLowerCase().contains("burning amulet")) {
- Rs2Dialogue.sleepUntilInDialogue();
- Rs2Dialogue.clickOption(destination);
- Rs2Dialogue.sleepUntilHasDialogueOption("Okay, teleport to level");
- Rs2Dialogue.clickOption("Okay, teleport to level");
- }
-
- if (itemAction.equalsIgnoreCase("teleport") && transport.getDisplayInfo().toLowerCase().contains("revenant cave teleport")) {
- Rs2Dialogue.sleepUntilHasDialogueOption("Yes, teleport me now");
- Rs2Dialogue.clickOption("Yes, teleport me now");
- }
+ // If there's only one destination with the item possible, a generic action will also work
+ if (itemAction == null && !hasMultipleDestination) {
+ itemAction = rs2Item.getActionFromList(genericKeyWords);
+ }
- if (itemAction.equalsIgnoreCase("break") && itemId == ItemID.LUNAR_TABLET_ICE_PLATEAU_TELEPORT) {
- Rs2Dialogue.sleepUntilHasQuestion("Teleport into the DEEP wilderness?");
- Rs2Dialogue.clickOption("Yes");
- }
+ if (itemAction != null) {
+ boolean interaction = Rs2Inventory.interact(rs2Item, itemAction);
+ if (!interaction) {
+ return false;
+ } else if (wildernessTransport) {
+ Rs2Dialogue.sleepUntilInDialogue();
+ return Rs2Dialogue.clickOption("Yes", "Okay");
+ }
+ return true;
+ }
- if (itemAction.equalsIgnoreCase("teleport") && transport.getDisplayInfo().toLowerCase().contains("slayer ring")) {
- Rs2Dialogue.sleepUntilSelectAnOption();
- Rs2Dialogue.clickOption(destination);
- }
+ // If no location-based action found, try generic actions
+ itemAction = rs2Item.getActionFromList(genericKeyWords);
- if (itemAction.equalsIgnoreCase("rub") || itemAction.equalsIgnoreCase("reminisce")) {
- Rs2Dialogue.sleepUntilSelectAnOption();
- Rs2Dialogue.clickOption(destination);
- }
+ if (itemAction == null) {
+ log.debug("No generic keyword found for={}, genericKeywords={}", itemAction, String.join(",", genericKeyWords));
+ return false;
+ }
- if (itemAction.equalsIgnoreCase("open") && itemId == ItemID.BOOKOFSCROLLS_CHARGED) {
- handleMasterScrollBook(destination);
- }
+ if (Rs2Inventory.interact(itemId, itemAction)) {
+ log.debug("Traveling with genericAction={}, to {} - ({})", itemAction, transport.getDisplayInfo(), transport.getDestination());
- log.info("Traveling to {} - ({})", transport.getDisplayInfo(), transport.getDestination());
- return sleepUntilTrue(() -> Rs2Player.getWorldLocation().distanceTo2D(transport.getDestination()) < OFFSET, 100, 8000);
+ if (itemAction.equalsIgnoreCase("open") && itemId == ItemID.BOOKOFSCROLLS_CHARGED) {
+ return handleMasterScrollBook(destination);
+ } else if (wildernessTransport) {
+ Rs2Dialogue.sleepUntilInDialogue();
+ return Rs2Dialogue.clickOption("Yes", "Okay");
+ } else {
+ log.info("Unsure how to handle this itemTransport={} action={}", transport, itemAction);
}
}
- else {
- return interactWithNewRuneliteMenu(transport,itemId);
- }
-
return false;
}
private static boolean handleWearableTeleports(Transport transport, int itemId) {
- if (Rs2Equipment.isWearing(itemId)) {
- if (transport.getDisplayInfo().contains(":")) {
- String[] values = transport.getDisplayInfo().split(":");
- String destination = values[1].trim().toLowerCase();
- Rs2ItemModel rs2Item = Rs2Equipment.get(itemId);
- if (transport.getDisplayInfo().toLowerCase().contains("slayer ring")) {
- Rs2Equipment.invokeMenu(rs2Item, "teleport");
- Rs2Dialogue.sleepUntilSelectAnOption();
- Rs2Dialogue.clickOption(destination);
- } else {
- Rs2Equipment.invokeMenu(rs2Item, destination);
- if (transport.getDisplayInfo().toLowerCase().contains("burning amulet")) {
- Rs2Dialogue.sleepUntilInDialogue();
- Rs2Dialogue.clickOption("Okay, teleport to level");
- }
+ Rs2ItemModel rs2Item = Rs2Equipment.get(itemId);
+ if (rs2Item == null) return false;
+ if (transport.getDisplayInfo().contains(":")) {
+ String[] values = transport.getDisplayInfo().split(":");
+ String destination = values[1].trim().toLowerCase();
+
+ if (transport.getDisplayInfo().toLowerCase().contains("slayer ring")) {
+ Rs2Equipment.invokeMenu(rs2Item, "teleport");
+ Rs2Dialogue.sleepUntilSelectAnOption();
+ Rs2Dialogue.clickOption(destination);
+ } else {
+ Rs2Equipment.invokeMenu(rs2Item, destination);
+ if (transport.getDisplayInfo().toLowerCase().contains("burning amulet")) {
+ Rs2Dialogue.sleepUntilInDialogue();
+ Rs2Dialogue.clickOption("Okay, teleport to level");
}
- log.info("Traveling to {} - ({})", transport.getDisplayInfo(), transport.getDestination());
- return sleepUntilTrue(() -> Rs2Player.getWorldLocation().distanceTo2D(transport.getDestination()) < OFFSET, 100, 8000);
}
+ log.info("Traveling to {} - ({})", transport.getDisplayInfo(), transport.getDestination());
+ return true;
}
return false;
}
@@ -2321,27 +2286,27 @@ private static boolean handleQuetzal(Transport transport) {
return false;
}
- private static void handleMasterScrollBook(String destination) {
- boolean isMasterScrollBookOpen = sleepUntilTrue(() -> Rs2Widget.isWidgetVisible(InterfaceID.Bookofscrolls.CONTENTS), 100, 10000);
- if (!isMasterScrollBookOpen) {
- log.error("Master Scroll Book did not open within timeout period");
- return;
- }
+ private static boolean handleMasterScrollBook(String destination) {
+ boolean isMasterScrollBookOpen = sleepUntilTrue(() -> Rs2Widget.isWidgetVisible(InterfaceID.Bookofscrolls.CONTENTS), 100, 10000);
+ if (!isMasterScrollBookOpen) {
+ log.error("Master Scroll Book did not open within timeout period");
+ return false;
+ }
- Widget bookOfScrollsWidget = Rs2Widget.getWidget(InterfaceID.Bookofscrolls.CONTENTS);
- List bookOfScrollsChildren = Arrays.stream(bookOfScrollsWidget.getStaticChildren())
- .filter(Objects::nonNull)
- .collect(Collectors.toList());
-
- Widget destinationWidget = Rs2Widget.findWidget(destination, bookOfScrollsChildren, false);
- if (destinationWidget == null) return;
- Rs2Widget.clickWidget(destinationWidget);
- if (destination.equalsIgnoreCase("Revenant cave")) {
- Rs2Dialogue.sleepUntilInDialogue();
- Rs2Dialogue.clickOption("Yes, teleport me now");
- }
- Rs2Player.waitForAnimation();
- }
+ Widget bookOfScrollsWidget = Rs2Widget.getWidget(InterfaceID.Bookofscrolls.CONTENTS);
+ List bookOfScrollsChildren = Arrays.stream(bookOfScrollsWidget.getStaticChildren())
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+
+ Widget destinationWidget = Rs2Widget.findWidget(destination, bookOfScrollsChildren, false);
+ if (destinationWidget == null) return false;
+ boolean interaction = Rs2Widget.clickWidget(destinationWidget);
+ if (interaction && destination.equalsIgnoreCase("Revenant cave")) {
+ Rs2Dialogue.sleepUntilInDialogue();
+ return Rs2Dialogue.clickOption("Yes, teleport me now");
+ }
+ return interaction;
+ }
private static boolean handleMagicCarpet(Transport transport) {
final int flyingPoseAnimation = 6936;
@@ -2407,56 +2372,6 @@ private static boolean interactWithAdventureLog(Transport transport) {
return sleepUntilTrue(() -> Rs2Player.getWorldLocation().distanceTo2D(transport.getDestination()) < OFFSET, 100, 5000);
}
- private static boolean interactWithNewRuneliteMenu(Transport transport,int itemId) {
- if (transport.getDisplayInfo() == null || transport.getDisplayInfo().isEmpty()) return false;
-
- Pattern pattern = Pattern.compile("^(\\d+)\\.");
- Matcher matcher = pattern.matcher(transport.getDisplayInfo());
- if (matcher.find()) {
- int menuOption = Integer.parseInt(matcher.group(1));
- String[] values = transport.getDisplayInfo().split(":");
- String destination = values[1].trim();
- int identifier = NewMenuEntry.findIdentifier(menuOption, getIdentifierOffset(transport.getDisplayInfo()));
- Rs2Inventory.interact(itemId, destination, identifier);
- if (transport.getDisplayInfo().toLowerCase().contains("burning amulet")) {
- Rs2Dialogue.sleepUntilHasDialogueOption("Okay, teleport to level");
- Rs2Dialogue.clickOption("Okay, teleport to level");
- }
- log.info("Traveling to {} - ({})", transport.getDisplayInfo(), transport.getDestination());
- return sleepUntilTrue(() -> Rs2Player.getWorldLocation().distanceTo2D(transport.getDestination()) < OFFSET, 100, 5000);
- }
- return false;
- }
-
- private static int getIdentifierOffset(String itemName) {
- String lowerCaseItemName = itemName.toLowerCase();
- if (lowerCaseItemName.contains("ring of dueling") ||
- lowerCaseItemName.contains("games necklace") ||
- lowerCaseItemName.contains("skills necklace") ||
- lowerCaseItemName.contains("amulet of glory") ||
- lowerCaseItemName.contains("ring of wealth") ||
- lowerCaseItemName.contains("combat bracelet") ||
- lowerCaseItemName.contains("digsite pendant") ||
- lowerCaseItemName.contains("necklace of passage") ||
- lowerCaseItemName.contains("camulet") ||
- lowerCaseItemName.contains("burning amulet") ||
- lowerCaseItemName.contains("giantsoul amulet")) {
- return 6;
- } else if (lowerCaseItemName.contains("xeric's talisman") ||
- lowerCaseItemName.contains("slayer ring") ||
- lowerCaseItemName.contains("construct. cape") ||
- lowerCaseItemName.contains("pendant of ates")) {
- return 4;
- } else if (lowerCaseItemName.contains("book of the dead")) {
- return 3;
- } else if (lowerCaseItemName.contains("kharedst's memoirs") ||
- lowerCaseItemName.contains("enchanted lyre")) {
- return 2;
- } else {
- return 4; // Default offset if no match is found
- }
- }
-
private static boolean handleGlider(Transport transport) {
int TA_QUIR_PRIW = 9043972;
int SINDARPOS = 9043975;
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsPlugin.java
index 438293ce496..7a1f163417a 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsPlugin.java
@@ -41,6 +41,7 @@
import javax.inject.Inject;
import javax.swing.SwingUtilities;
import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.*;
@@ -185,7 +186,14 @@ public class NpcIndicatorsPlugin extends Plugin
* Tagged NPCs that despawned this tick, which need to be verified that
* they actually spawned and didn't just walk into view range.
*/
- private final List despawnedNpcsThisTick = new ArrayList<>();
+ private final List despawnedNpcsThisTick = new ArrayList<>();
+
+ @AllArgsConstructor
+ private static class DespawnedNpc
+ {
+ WorldPoint coord;
+ int index;
+ }
/**
* World locations of graphics object which indicate that an
@@ -310,6 +318,7 @@ public void onMenuEntryAdded(MenuEntryAdded event)
client.createMenuEntry(idx--)
.setOption(idMatch ? UNTAG : TAG)
.setTarget(event.getTarget())
+ .setWorldViewId(menuEntry.getWorldViewId())
.setIdentifier(event.getIdentifier())
.setType(MenuAction.RUNELITE)
.onClick(this::tag);
@@ -320,6 +329,7 @@ public void onMenuEntryAdded(MenuEntryAdded event)
client.createMenuEntry(idx--)
.setOption(nameMatch ? UNTAG_ALL : TAG_ALL)
.setTarget(event.getTarget())
+ .setWorldViewId(menuEntry.getWorldViewId())
.setIdentifier(event.getIdentifier())
.setType(MenuAction.RUNELITE)
.onClick(this::tag);
@@ -507,10 +517,14 @@ private int createTagStyleMenu(int idx, String target, NPC npc)
private void tag(MenuEntry entry)
{
+ WorldView wv = client.getWorldView(entry.getWorldViewId());
+ if (wv == null)
+ {
+ return;
+ }
+
final int id = entry.getIdentifier();
- WorldView wv = client.getTopLevelWorldView();
final NPC npc = wv.npcs().byIndex(id);
-
if (npc == null || npc.getName() == null)
{
return;
@@ -518,10 +532,11 @@ private void tag(MenuEntry entry)
if (entry.getOption().equals(TAG) || entry.getOption().equals(UNTAG))
{
- final boolean removed = npcTags.remove(id);
+ final boolean exists = highlightedNpcs.containsKey(npc);
- if (removed)
+ if (exists)
{
+ npcTags.remove(id);
if (!highlightMatchesNPCName(npc.getName()))
{
highlightedNpcs.remove(npc);
@@ -592,7 +607,7 @@ public void onNpcDespawned(NpcDespawned npcDespawned)
if (memorizedNpcs.containsKey(npc.getIndex()))
{
- despawnedNpcsThisTick.add(npc);
+ despawnedNpcsThisTick.add(new DespawnedNpc(npc.getWorldLocation(), npc.getIndex()));
}
highlightedNpcs.remove(npc);
@@ -722,7 +737,14 @@ void rebuild()
return;
}
- for (NPC npc : client.getNpcs())
+ rebuildWorldview(client.getTopLevelWorldView());
+
+ npcOverlayService.rebuild();
+ }
+
+ private void rebuildWorldview(WorldView wv)
+ {
+ for (NPC npc : wv.npcs())
{
final String npcName = npc.getName();
@@ -739,7 +761,7 @@ void rebuild()
if (highlightMatchesNPCName(npcName))
{
- if (!client.isInInstancedRegion())
+ if (!wv.isInstance())
{
memorizeNpc(npc);
}
@@ -751,7 +773,10 @@ void rebuild()
memorizedNpcs.remove(npc.getIndex());
}
- npcOverlayService.rebuild();
+ for (WorldView sub : wv.worldViews())
+ {
+ rebuildWorldview(sub);
+ }
}
private boolean highlightMatchesNPCName(String npcName)
@@ -775,20 +800,20 @@ private void validateSpawnedNpcs()
}
else
{
- for (NPC npc : despawnedNpcsThisTick)
+ for (DespawnedNpc npc : despawnedNpcsThisTick)
{
if (!teleportGraphicsObjectSpawnedThisTick.isEmpty())
{
- if (teleportGraphicsObjectSpawnedThisTick.contains(npc.getWorldLocation()))
+ if (teleportGraphicsObjectSpawnedThisTick.contains(npc.coord))
{
// NPC teleported away, so we don't want to add the respawn timer
continue;
}
}
- if (isInViewRange(client.getLocalPlayer().getWorldLocation(), npc.getWorldLocation()))
+ if (isInViewRange(client.getLocalPlayer().getWorldLocation(), npc.coord))
{
- final MemorizedNpc mn = memorizedNpcs.get(npc.getIndex());
+ final MemorizedNpc mn = memorizedNpcs.get(npc.index);
if (mn != null)
{
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsOverlay.java
index a24d85152cb..eae68f75382 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsOverlay.java
@@ -41,6 +41,8 @@
import net.runelite.api.ObjectComposition;
import net.runelite.api.TileObject;
import net.runelite.api.WallObject;
+import net.runelite.api.WorldEntity;
+import net.runelite.api.WorldView;
import static net.runelite.client.plugins.objectindicators.ColorTileObject.HF_CLICKBOX;
import static net.runelite.client.plugins.objectindicators.ColorTileObject.HF_HULL;
import static net.runelite.client.plugins.objectindicators.ColorTileObject.HF_OUTLINE;
@@ -81,6 +83,7 @@ public Dimension render(Graphics2D graphics)
return null;
}
+ WorldView toplevel = client.getTopLevelWorldView();
Stroke stroke = new BasicStroke((float) config.borderWidth());
final var defaultFlags =
(config.highlightHull() ? HF_HULL : 0) |
@@ -90,8 +93,15 @@ public Dimension render(Graphics2D graphics)
for (ColorTileObject obj : objects)
{
TileObject object = obj.getTileObject();
+ WorldView wv = object.getWorldView();
- if (object.getPlane() != client.getPlane())
+ if (wv == null || object.getPlane() != wv.getPlane())
+ {
+ continue;
+ }
+
+ WorldEntity we = toplevel.worldEntities().byIndex(wv.getId());
+ if (we != null && we.isHiddenForOverlap())
{
continue;
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsPlugin.java
index 4cc2fcca02a..4acd0053c5a 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsPlugin.java
@@ -32,6 +32,7 @@
import com.google.inject.Provides;
import java.awt.Color;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -50,7 +51,6 @@
import net.runelite.api.Client;
import net.runelite.api.DecorativeObject;
import net.runelite.api.GameObject;
-import net.runelite.api.GameState;
import net.runelite.api.GroundObject;
import net.runelite.api.KeyCode;
import net.runelite.api.Menu;
@@ -61,17 +61,20 @@
import net.runelite.api.Tile;
import net.runelite.api.TileObject;
import net.runelite.api.WallObject;
+import net.runelite.api.WorldEntity;
+import net.runelite.api.WorldView;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.DecorativeObjectDespawned;
import net.runelite.api.events.DecorativeObjectSpawned;
import net.runelite.api.events.GameObjectDespawned;
import net.runelite.api.events.GameObjectSpawned;
-import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GroundObjectDespawned;
import net.runelite.api.events.GroundObjectSpawned;
import net.runelite.api.events.MenuEntryAdded;
import net.runelite.api.events.WallObjectDespawned;
import net.runelite.api.events.WallObjectSpawned;
+import net.runelite.api.events.WorldViewLoaded;
+import net.runelite.api.events.WorldViewUnloaded;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
@@ -138,7 +141,7 @@ ObjectIndicatorsConfig provideConfig(ConfigManager configManager)
protected void startUp()
{
overlayManager.add(overlay);
- clientThread.invokeLater(this::reloadPoints);
+ clientThread.invokeLater(() -> loadPoints());
}
@Override
@@ -152,7 +155,7 @@ protected void shutDown()
@Subscribe
public void onProfileChanged(ProfileChanged e)
{
- clientThread.invokeLater(this::reloadPoints);
+ clientThread.invokeLater(() -> loadPoints());
}
@Subscribe
@@ -203,32 +206,53 @@ public void onGroundObjectDespawned(GroundObjectDespawned event)
objects.removeIf(o -> o.getTileObject() == event.getGroundObject());
}
- private void reloadPoints()
+ private void loadPoints()
{
points.clear();
- if (client.getMapRegions() != null)
+
+ WorldView wv = client.getTopLevelWorldView();
+ loadPoints(wv);
+
+ for (WorldEntity we : wv.worldEntities())
+ {
+ loadPoints(we.getWorldView());
+ }
+ }
+
+ private void loadPoints(WorldView wv)
+ {
+ int[] regions = wv.getMapRegions();
+ if (regions == null)
+ {
+ return;
+ }
+
+ for (int regionId : regions)
{
- for (int regionId : client.getMapRegions())
+ // load points for region
+ final Set regionPoints = loadPoints(regionId);
+ if (regionPoints != null)
{
- // load points for region
- final Set regionPoints = loadPoints(regionId);
- if (regionPoints != null)
- {
- points.put(regionId, regionPoints);
- }
+ points.put(regionId, regionPoints);
}
}
}
@Subscribe
- public void onGameStateChanged(GameStateChanged gameStateChanged)
+ public void onWorldViewLoaded(WorldViewLoaded event)
+ {
+ loadPoints(event.getWorldView());
+ }
+
+ @Subscribe
+ public void onWorldViewUnloaded(WorldViewUnloaded event)
{
- GameState gameState = gameStateChanged.getGameState();
- if (gameState == GameState.LOADING)
+ var wv = event.getWorldView();
+ objects.removeIf(c -> c.getTileObject().getWorldView() == wv);
+ // TODO remove points when the last boat using it despawns?
+ if (wv.isTopLevel())
{
- // Reload points with new map regions
- objects.clear();
- reloadPoints();
+ Arrays.stream(wv.getMapRegions()).forEach(points::remove);
}
}
@@ -240,7 +264,14 @@ public void onMenuEntryAdded(MenuEntryAdded event)
return;
}
- final TileObject tileObject = findTileObject(client.getPlane(), event.getActionParam0(), event.getActionParam1(), event.getIdentifier());
+ int worldId = event.getMenuEntry().getWorldViewId();
+ WorldView wv = client.getWorldView(worldId);
+ if (wv == null)
+ {
+ return;
+ }
+
+ final TileObject tileObject = findTileObject(wv, event.getActionParam0(), event.getActionParam1(), event.getIdentifier());
if (tileObject == null)
{
return;
@@ -251,6 +282,7 @@ public void onMenuEntryAdded(MenuEntryAdded event)
client.createMenuEntry(idx--)
.setOption(marked.isPresent() ? UNMARK : MARK)
.setTarget(event.getTarget())
+ .setWorldViewId(worldId)
.setParam0(event.getActionParam0())
.setParam1(event.getActionParam1())
.setIdentifier(event.getIdentifier())
@@ -403,7 +435,13 @@ private int createTagStyleMenu(int idx, String target, TileObject object)
private void markObject(MenuEntry entry)
{
- TileObject object = findTileObject(client.getPlane(), entry.getParam0(), entry.getParam1(), entry.getIdentifier());
+ WorldView wv = client.getWorldView(entry.getWorldViewId());
+ if (wv == null)
+ {
+ return;
+ }
+
+ TileObject object = findTileObject(wv, entry.getParam0(), entry.getParam1(), entry.getIdentifier());
if (object == null)
{
return;
@@ -420,7 +458,37 @@ private void markObject(MenuEntry entry)
return;
}
- markObject(objectDefinition, name, object);
+ final WorldPoint worldPoint = WorldPoint.fromLocalInstance(client, object.getLocalLocation());
+ final int regionId = worldPoint.getRegionID();
+ final Color borderColor = config.markerColor();
+ final Color fillColor = config.fillColor();
+ final ObjectPoint point = new ObjectPoint(
+ object.getId(),
+ name,
+ regionId,
+ worldPoint.getRegionX(),
+ worldPoint.getRegionY(),
+ worldPoint.getPlane(),
+ borderColor,
+ fillColor,
+ // use the default config values
+ null, null, null, null);
+
+ Set objectPoints = points.computeIfAbsent(regionId, k -> new HashSet<>());
+
+ if (objectPoints.removeIf(findObjectPredicate(objectDefinition, object, worldPoint)))
+ {
+ unmarkObjects(client.getTopLevelWorldView(), worldPoint, objectDefinition);
+ log.debug("Unmarking object: {}", point);
+ }
+ else
+ {
+ objectPoints.add(point);
+ markObjects(client.getTopLevelWorldView(), worldPoint, objectDefinition);
+ log.debug("Marking object: {}", point);
+ }
+
+ savePoints(regionId, objectPoints);
}
private void updateObjectConfig(TileObject object, Consumer c)
@@ -445,9 +513,13 @@ private void updateObjectConfig(TileObject object, Consumer c)
savePoints(regionId, objectPoints);
// rebuild the ColorTileObject from the new config
- if (objects.removeIf(o -> o.getTileObject() == object))
+ for (ColorTileObject o : new ArrayList<>(objects))
{
- checkObjectPoints(object);
+ if (o.getTileObject().getId() == object.getId())
+ {
+ objects.remove(o);
+ checkObjectPoints(o.getTileObject());
+ }
}
}
@@ -503,11 +575,12 @@ private void checkObjectPoints(TileObject object)
}
}
- private TileObject findTileObject(int z, int x, int y, int id)
+ private TileObject findTileObject(WorldView wv, int x, int y, int id)
{
- Scene scene = client.getScene();
+ int level = wv.getPlane();
+ Scene scene = wv.getScene();
Tile[][][] tiles = scene.getTiles();
- final Tile tile = tiles[z][x][y];
+ final Tile tile = tiles[level][x][y];
if (tile == null)
{
return null;
@@ -574,54 +647,48 @@ private boolean objectIdEquals(TileObject tileObject, int id)
return false;
}
- /** mark or unmark an object
- *
- * @param objectComposition transformed composition of object based on vars
- * @param name name of objectComposition
- * @param object tile object, for multilocs object.getId() is the base id
- */
- private void markObject(ObjectComposition objectComposition, String name, final TileObject object)
+ private void markObjects(WorldView wv, WorldPoint p, ObjectComposition objectConfig)
{
- final WorldPoint worldPoint = WorldPoint.fromLocalInstance(client, object.getLocalLocation());
- final int regionId = worldPoint.getRegionID();
- final Color borderColor = config.markerColor();
- final Color fillColor = config.fillColor();
- final ObjectPoint point = new ObjectPoint(
- object.getId(),
- name,
- regionId,
- worldPoint.getRegionX(),
- worldPoint.getRegionY(),
- worldPoint.getPlane(),
- borderColor,
- fillColor,
- // use the default config values
- null, null, null, null);
-
- Set objectPoints = points.computeIfAbsent(regionId, k -> new HashSet<>());
-
- if (objects.removeIf(o -> o.getTileObject() == object))
+ for (WorldPoint sp : WorldPoint.toLocalInstance(wv, p))
{
- if (!objectPoints.removeIf(findObjectPredicate(objectComposition, object, worldPoint)))
+ int x = sp.getX() - wv.getBaseX(), y = sp.getY() - wv.getBaseY();
+ TileObject object = findTileObject(wv, x, y, objectConfig.getId());
+ if (object != null)
{
- log.warn("unable to find object point for unmarked object {}", object.getId());
+ objects.add(new ColorTileObject(object,
+ client.getObjectDefinition(object.getId()),
+ objectConfig.getName(),
+ config.markerColor(),
+ config.fillColor(),
+ (byte) 0));
}
+ }
- log.debug("Unmarking object: {}", point);
+ for (WorldView sub : wv.worldViews())
+ {
+ markObjects(sub, p, objectConfig);
}
- else
+ }
+
+ private void unmarkObjects(WorldView wv, WorldPoint p, ObjectComposition objectConfig)
+ {
+ for (WorldPoint sp : WorldPoint.toLocalInstance(wv, p))
{
- objectPoints.add(point);
- objects.add(new ColorTileObject(object,
- client.getObjectDefinition(object.getId()),
- name,
- borderColor,
- fillColor,
- (byte) 0));
- log.debug("Marking object: {}", point);
+ int x = sp.getX() - wv.getBaseX(), y = sp.getY() - wv.getBaseY();
+ TileObject object = findTileObject(wv, x, y, objectConfig.getId());
+ if (object != null)
+ {
+ if (!objects.removeIf(o -> o.getTileObject() == object))
+ {
+ log.warn("unable to find object point for unmarked object {}", object.getId());
+ }
+ }
}
- savePoints(regionId, objectPoints);
+ for (WorldView sub : wv.worldViews())
+ {
+ unmarkObjects(sub, p, objectConfig);
+ }
}
private static Predicate findObjectPredicate(ObjectComposition objectComposition, TileObject object, WorldPoint worldPoint)
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/playerindicators/PlayerIndicatorsService.java b/runelite-client/src/main/java/net/runelite/client/plugins/playerindicators/PlayerIndicatorsService.java
index 069100b8be9..b5f6b691887 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/playerindicators/PlayerIndicatorsService.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/playerindicators/PlayerIndicatorsService.java
@@ -36,11 +36,13 @@
import net.runelite.api.FriendsChatRank;
import static net.runelite.api.FriendsChatRank.UNRANKED;
import net.runelite.api.Player;
+import net.runelite.api.WorldView;
import net.runelite.api.clan.ClanChannel;
import net.runelite.api.clan.ClanChannelMember;
import net.runelite.api.clan.ClanRank;
import net.runelite.api.clan.ClanSettings;
import net.runelite.api.clan.ClanTitle;
+import net.runelite.api.coords.LocalPoint;
import net.runelite.api.gameval.VarbitID;
import net.runelite.client.party.PartyService;
import net.runelite.client.util.Text;
@@ -62,9 +64,20 @@ private PlayerIndicatorsService(Client client, PlayerIndicatorsConfig config, Pa
void forEachPlayer(final BiConsumer consumer)
{
- for (Player player : client.getPlayers())
+ WorldView wv = client.getTopLevelWorldView();
+ forEachPlayer(consumer, wv);
+ for (WorldView sub : wv.worldViews())
{
- if (player == null || player.getName() == null)
+ forEachPlayer(consumer, sub);
+ }
+ }
+
+ private void forEachPlayer(BiConsumer consumer, WorldView wv)
+ {
+ for (Player player : wv.players())
+ {
+ LocalPoint lp = player.getLocalLocation();
+ if (!wv.contains(lp))
{
continue;
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/tileindicators/TileIndicatorsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/tileindicators/TileIndicatorsOverlay.java
index 23b45e9c83b..04acf99e3fa 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/tileindicators/TileIndicatorsOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/tileindicators/TileIndicatorsOverlay.java
@@ -32,6 +32,8 @@
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.Perspective;
+import net.runelite.api.Tile;
+import net.runelite.api.WorldView;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.ui.overlay.Overlay;
@@ -59,10 +61,12 @@ public Dimension render(Graphics2D graphics)
{
if (config.highlightHoveredTile())
{
+ WorldView wv = client.getLocalPlayer().getWorldView();
+ Tile tile = wv.getSelectedSceneTile();
// If we have tile "selected" render it
- if (client.getSelectedSceneTile() != null)
+ if (tile != null)
{
- renderTile(graphics, client.getSelectedSceneTile().getLocalLocation(), config.highlightHoveredColor(), config.hoveredTileBorderWidth(), config.hoveredTileFillColor());
+ renderTile(graphics, tile.getLocalLocation(), config.highlightHoveredColor(), config.hoveredTileBorderWidth(), config.hoveredTileFillColor());
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xpupdater/XpUpdaterConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/xpupdater/XpUpdaterConfig.java
index 992d84a211b..40a0113413f 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/xpupdater/XpUpdaterConfig.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/xpupdater/XpUpdaterConfig.java
@@ -42,17 +42,6 @@ default boolean cml()
return false;
}
- @ConfigItem(
- position = 2,
- keyName = "runetracker",
- name = "RuneTracker",
- description = "Automatically updates your stats on runetracker.org when you log out."
- )
- default boolean runetracker()
- {
- return false;
- }
-
@ConfigItem(
position = 3,
keyName = "templeosrs",
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xpupdater/XpUpdaterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/xpupdater/XpUpdaterPlugin.java
index bb7087ca003..d6bc9548651 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/xpupdater/XpUpdaterPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/xpupdater/XpUpdaterPlugin.java
@@ -136,7 +136,6 @@ private void update(long accountHash, String username)
EnumSet worldTypes = client.getWorldType();
username = username.replace(" ", "_");
updateCml(username, worldTypes);
- updateRunetracker(username, worldTypes);
updateTempleosrs(accountHash, username, worldTypes);
updateWom(accountHash, username, worldTypes);
}
@@ -147,7 +146,8 @@ private void updateCml(String username, EnumSet worldTypes)
&& !worldTypes.contains(WorldType.SEASONAL)
&& !worldTypes.contains(WorldType.DEADMAN)
&& !worldTypes.contains(WorldType.NOSAVE_MODE)
- && !worldTypes.contains(WorldType.FRESH_START_WORLD))
+ && !worldTypes.contains(WorldType.FRESH_START_WORLD)
+ && !worldTypes.contains(WorldType.TOURNAMENT_WORLD))
{
HttpUrl url = new HttpUrl.Builder()
.scheme("https")
@@ -167,37 +167,13 @@ private void updateCml(String username, EnumSet worldTypes)
}
}
- private void updateRunetracker(String username, EnumSet worldTypes)
- {
- if (config.runetracker()
- && !worldTypes.contains(WorldType.SEASONAL)
- && !worldTypes.contains(WorldType.DEADMAN)
- && !worldTypes.contains(WorldType.NOSAVE_MODE)
- && !worldTypes.contains(WorldType.FRESH_START_WORLD))
- {
- HttpUrl url = new HttpUrl.Builder()
- .scheme("https")
- .host("rscript.org")
- .addPathSegment("lookup.php")
- .addQueryParameter("type", "stats07")
- .addQueryParameter("user", username)
- .build();
-
- Request request = new Request.Builder()
- .header("User-Agent", "RuneLite")
- .url(url)
- .build();
-
- sendRequest("RuneTracker", request);
- }
- }
-
private void updateTempleosrs(long accountHash, String username, EnumSet worldTypes)
{
if (config.templeosrs()
&& !worldTypes.contains(WorldType.SEASONAL)
&& !worldTypes.contains(WorldType.DEADMAN)
- && !worldTypes.contains(WorldType.NOSAVE_MODE))
+ && !worldTypes.contains(WorldType.NOSAVE_MODE)
+ && !worldTypes.contains(WorldType.TOURNAMENT_WORLD))
{
HttpUrl.Builder url = new HttpUrl.Builder()
.scheme("https")
@@ -225,7 +201,8 @@ private void updateWom(long accountHash, String username, EnumSet wor
{
if (config.wiseoldman()
&& !worldTypes.contains(WorldType.DEADMAN)
- && !worldTypes.contains(WorldType.NOSAVE_MODE))
+ && !worldTypes.contains(WorldType.NOSAVE_MODE)
+ && !worldTypes.contains(WorldType.TOURNAMENT_WORLD))
{
HttpUrl url = new HttpUrl.Builder()
.scheme("https")
diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/outline/ModelOutlineRenderer.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/outline/ModelOutlineRenderer.java
index a7160963502..8af834178c9 100644
--- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/outline/ModelOutlineRenderer.java
+++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/outline/ModelOutlineRenderer.java
@@ -51,6 +51,7 @@
import net.runelite.api.RuneLiteObject;
import net.runelite.api.TileObject;
import net.runelite.api.WallObject;
+import net.runelite.api.WorldView;
import net.runelite.api.coords.LocalPoint;
@Singleton
@@ -521,10 +522,10 @@ private void simulateTriangleRasterizationForOutline(
* @param vertexOrientation The orientation of the vertices.
* @return Returns true if any of them are inside the clip area, otherwise false.
*/
- private boolean projectVertices(Model model, int localX, int localY, int localZ, final int vertexOrientation)
+ private boolean projectVertices(WorldView wv, Model model, int localX, int localY, int localZ, final int vertexOrientation)
{
final int vertexCount = model.getVerticesCount();
- Perspective.modelToCanvas(client,
+ Perspective.modelToCanvas(client, wv,
vertexCount,
localX, localY, localZ,
vertexOrientation,
@@ -894,7 +895,7 @@ private void processOutlinePixelQueue(int outlineWidth, Color color, int feather
* @param outlineWidth The width of the outline
* @param color The color of the outline
*/
- private void drawModelOutline(Model model,
+ private void drawModelOutline(WorldView wv, Model model,
int localX, int localY, int localZ, int orientation,
int outlineWidth, Color color, int feather)
{
@@ -927,7 +928,7 @@ else if (feather > MAX_FEATHER)
clipX2 = client.getViewportWidth() + clipX1;
clipY2 = client.getViewportHeight() + clipY1;
- if (!projectVertices(model, localX, localY, localZ, orientation))
+ if (!projectVertices(wv, model, localX, localY, localZ, orientation))
{
// No vertex of the model is visible on the screen, so we can
// assume there are no parts of the model to outline.
@@ -985,8 +986,9 @@ public void drawOutline(NPC npc, int outlineWidth, Color color, int feather)
LocalPoint lp = npc.getLocalLocation();
if (lp != null)
{
- drawModelOutline(npc.getModel(), lp.getX(), lp.getY(),
- Perspective.getFootprintTileHeight(client, lp, client.getPlane(), npc.getComposition().getFootprintSize()) - npc.getAnimationHeightOffset(),
+ WorldView wv = npc.getWorldView();
+ drawModelOutline(wv, npc.getModel(), lp.getX(), lp.getY(),
+ Perspective.getFootprintTileHeight(client, lp, wv.getPlane(), npc.getComposition().getFootprintSize()) - npc.getAnimationHeightOffset(),
npc.getCurrentOrientation(), outlineWidth, color, feather);
}
}
@@ -996,8 +998,9 @@ public void drawOutline(Player player, int outlineWidth, Color color, int feathe
LocalPoint lp = player.getLocalLocation();
if (lp != null)
{
- drawModelOutline(player.getModel(), lp.getX(), lp.getY(),
- Perspective.getFootprintTileHeight(client, lp, client.getPlane(), player.getFootprintSize()) - player.getAnimationHeightOffset(),
+ WorldView wv = player.getWorldView();
+ drawModelOutline(wv, player.getModel(), lp.getX(), lp.getY(),
+ Perspective.getFootprintTileHeight(client, lp, wv.getPlane(), player.getFootprintSize()) - player.getAnimationHeightOffset(),
player.getCurrentOrientation(), outlineWidth, color, feather);
}
}
@@ -1010,7 +1013,7 @@ private void drawOutline(GameObject gameObject, int outlineWidth, Color color, i
Model model = renderable instanceof Model ? (Model) renderable : renderable.getModel();
if (model != null)
{
- drawModelOutline(model, gameObject.getX(), gameObject.getY(), gameObject.getZ(),
+ drawModelOutline(gameObject.getWorldView(), model, gameObject.getX(), gameObject.getY(), gameObject.getZ(),
gameObject.getModelOrientation(), outlineWidth, color, feather);
}
}
@@ -1024,7 +1027,7 @@ private void drawOutline(GroundObject groundObject, int outlineWidth, Color colo
Model model = renderable instanceof Model ? (Model) renderable : renderable.getModel();
if (model != null)
{
- drawModelOutline(model, groundObject.getX(), groundObject.getY(), groundObject.getZ(),
+ drawModelOutline(groundObject.getWorldView(), model, groundObject.getX(), groundObject.getY(), groundObject.getZ(),
0, outlineWidth, color, feather);
}
}
@@ -1038,7 +1041,7 @@ private void drawOutline(ItemLayer itemLayer, int outlineWidth, Color color, int
Model model = bottomRenderable instanceof Model ? (Model) bottomRenderable : bottomRenderable.getModel();
if (model != null)
{
- drawModelOutline(model, itemLayer.getX(), itemLayer.getY(), itemLayer.getZ() - itemLayer.getHeight(),
+ drawModelOutline(itemLayer.getWorldView(), model, itemLayer.getX(), itemLayer.getY(), itemLayer.getZ() - itemLayer.getHeight(),
0, outlineWidth, color, feather);
}
}
@@ -1049,7 +1052,7 @@ private void drawOutline(ItemLayer itemLayer, int outlineWidth, Color color, int
Model model = middleRenderable instanceof Model ? (Model) middleRenderable : middleRenderable.getModel();
if (model != null)
{
- drawModelOutline(model, itemLayer.getX(), itemLayer.getY(), itemLayer.getZ() - itemLayer.getHeight(),
+ drawModelOutline(itemLayer.getWorldView(), model, itemLayer.getX(), itemLayer.getY(), itemLayer.getZ() - itemLayer.getHeight(),
0, outlineWidth, color, feather);
}
}
@@ -1060,7 +1063,7 @@ private void drawOutline(ItemLayer itemLayer, int outlineWidth, Color color, int
Model model = topRenderable instanceof Model ? (Model) topRenderable : topRenderable.getModel();
if (model != null)
{
- drawModelOutline(model, itemLayer.getX(), itemLayer.getY(), itemLayer.getZ() - itemLayer.getHeight(),
+ drawModelOutline(itemLayer.getWorldView(), model, itemLayer.getX(), itemLayer.getY(), itemLayer.getZ() - itemLayer.getHeight(),
0, outlineWidth, color, feather);
}
}
@@ -1074,7 +1077,7 @@ private void drawOutline(DecorativeObject decorativeObject, int outlineWidth, Co
Model model = renderable1 instanceof Model ? (Model) renderable1 : renderable1.getModel();
if (model != null)
{
- drawModelOutline(model,
+ drawModelOutline(decorativeObject.getWorldView(), model,
decorativeObject.getX() + decorativeObject.getXOffset(),
decorativeObject.getY() + decorativeObject.getYOffset(),
decorativeObject.getZ(),
@@ -1089,7 +1092,7 @@ private void drawOutline(DecorativeObject decorativeObject, int outlineWidth, Co
if (model != null)
{
// Offset is not used for the second model
- drawModelOutline(model, decorativeObject.getX(), decorativeObject.getY(), decorativeObject.getZ(),
+ drawModelOutline(decorativeObject.getWorldView(), model, decorativeObject.getX(), decorativeObject.getY(), decorativeObject.getZ(),
0, outlineWidth, color, feather);
}
}
@@ -1103,7 +1106,7 @@ private void drawOutline(WallObject wallObject, int outlineWidth, Color color, i
Model model = renderable1 instanceof Model ? (Model) renderable1 : renderable1.getModel();
if (model != null)
{
- drawModelOutline(model, wallObject.getX(), wallObject.getY(), wallObject.getZ(),
+ drawModelOutline(wallObject.getWorldView(), model, wallObject.getX(), wallObject.getY(), wallObject.getZ(),
0, outlineWidth, color, feather);
}
}
@@ -1114,7 +1117,7 @@ private void drawOutline(WallObject wallObject, int outlineWidth, Color color, i
Model model = renderable2 instanceof Model ? (Model) renderable2 : renderable2.getModel();
if (model != null)
{
- drawModelOutline(model, wallObject.getX(), wallObject.getY(), wallObject.getZ(),
+ drawModelOutline(wallObject.getWorldView(), model, wallObject.getX(), wallObject.getY(), wallObject.getZ(),
0, outlineWidth, color, feather);
}
}
@@ -1152,7 +1155,7 @@ public void drawOutline(GraphicsObject graphicsObject, int outlineWidth, Color c
Model model = graphicsObject.getModel();
if (model != null)
{
- drawModelOutline(model, lp.getX(), lp.getY(), graphicsObject.getZ(),
+ drawModelOutline(graphicsObject.getWorldView(), model, lp.getX(), lp.getY(), graphicsObject.getZ(),
0, outlineWidth, color, feather);
}
}
@@ -1166,8 +1169,13 @@ public void drawOutline(RuneLiteObject runeLiteObject, int outlineWidth, Color c
Model model = runeLiteObject.getModel();
if (model != null)
{
- drawModelOutline(model, lp.getX(), lp.getY(), runeLiteObject.getZ(),
- runeLiteObject.getOrientation(), outlineWidth, color, feather);
+ int worldView = runeLiteObject.getWorldView();
+ WorldView wv = client.getWorldView(worldView);
+ if (wv != null)
+ {
+ drawModelOutline(wv, model, lp.getX(), lp.getY(), runeLiteObject.getZ(),
+ runeLiteObject.getOrientation(), outlineWidth, color, feather);
+ }
}
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/util/GameEventManager.java b/runelite-client/src/main/java/net/runelite/client/util/GameEventManager.java
index 5246b6db09d..b72e97913ed 100644
--- a/runelite-client/src/main/java/net/runelite/client/util/GameEventManager.java
+++ b/runelite-client/src/main/java/net/runelite/client/util/GameEventManager.java
@@ -42,6 +42,7 @@
import net.runelite.api.Tile;
import net.runelite.api.TileItem;
import net.runelite.api.WallObject;
+import net.runelite.api.WorldView;
import net.runelite.api.events.DecorativeObjectSpawned;
import net.runelite.api.events.GameObjectSpawned;
import net.runelite.api.events.GroundObjectSpawned;
@@ -72,19 +73,18 @@ private GameEventManager(Client client, ClientThread clientThread)
*
* @param consumer consumer accepting tile as parameter
*/
- private void forEachTile(Consumer consumer)
+ private void forEachTile(WorldView wv, Consumer consumer)
{
- final Scene scene = client.getScene();
+ final Scene scene = wv.getScene();
final Tile[][][] tiles = scene.getTiles();
for (int z = 0; z < Constants.MAX_Z; ++z)
{
- for (int x = 0; x < Constants.SCENE_SIZE; ++x)
+ for (int x = 0; x < wv.getSizeX(); ++x)
{
- for (int y = 0; y < Constants.SCENE_SIZE; ++y)
+ for (int y = 0; y < wv.getSizeY(); ++y)
{
Tile tile = tiles[z][x][y];
-
if (tile == null)
{
continue;
@@ -122,84 +122,94 @@ public void simulateGameEvents(Object subscriber)
eventBus.post(new ItemContainerChanged(itemContainer.getId(), itemContainer));
}
- for (NPC npc : client.getTopLevelWorldView().npcs())
+ simulateGameEvents(client.getTopLevelWorldView());
+
+ eventBus.unregister(subscriber);
+ });
+ }
+
+ private void simulateGameEvents(WorldView wv)
+ {
+ for (NPC npc : wv.npcs())
+ {
+ if (npc != null)
{
- if (npc != null)
- {
- final NpcSpawned npcSpawned = new NpcSpawned(npc);
- eventBus.post(npcSpawned);
- }
+ final NpcSpawned npcSpawned = new NpcSpawned(npc);
+ eventBus.post(npcSpawned);
}
+ }
- for (Player player : client.getTopLevelWorldView().players())
+ for (Player player : wv.players())
+ {
+ if (player != null)
{
- if (player != null)
- {
- final PlayerSpawned playerSpawned = new PlayerSpawned(player);
- eventBus.post(playerSpawned);
- }
+ final PlayerSpawned playerSpawned = new PlayerSpawned(player);
+ eventBus.post(playerSpawned);
}
+ }
- forEachTile((tile) ->
+ forEachTile(wv, (tile) ->
+ {
+ WallObject wallObject = tile.getWallObject();
+ if (wallObject != null)
{
- WallObject wallObject = tile.getWallObject();
- if (wallObject != null)
- {
- final WallObjectSpawned objectSpawned = new WallObjectSpawned();
- objectSpawned.setTile(tile);
- objectSpawned.setWallObject(wallObject);
- eventBus.post(objectSpawned);
- }
+ final WallObjectSpawned objectSpawned = new WallObjectSpawned();
+ objectSpawned.setTile(tile);
+ objectSpawned.setWallObject(wallObject);
+ eventBus.post(objectSpawned);
+ }
- DecorativeObject decorativeObject = tile.getDecorativeObject();
- if (decorativeObject != null)
- {
- final DecorativeObjectSpawned objectSpawned = new DecorativeObjectSpawned();
- objectSpawned.setTile(tile);
- objectSpawned.setDecorativeObject(decorativeObject);
- eventBus.post(objectSpawned);
- }
+ DecorativeObject decorativeObject = tile.getDecorativeObject();
+ if (decorativeObject != null)
+ {
+ final DecorativeObjectSpawned objectSpawned = new DecorativeObjectSpawned();
+ objectSpawned.setTile(tile);
+ objectSpawned.setDecorativeObject(decorativeObject);
+ eventBus.post(objectSpawned);
+ }
- GroundObject groundObject = tile.getGroundObject();
- if (groundObject != null)
- {
- final GroundObjectSpawned objectSpawned = new GroundObjectSpawned();
- objectSpawned.setTile(tile);
- objectSpawned.setGroundObject(groundObject);
- eventBus.post(objectSpawned);
- }
+ GroundObject groundObject = tile.getGroundObject();
+ if (groundObject != null)
+ {
+ final GroundObjectSpawned objectSpawned = new GroundObjectSpawned();
+ objectSpawned.setTile(tile);
+ objectSpawned.setGroundObject(groundObject);
+ eventBus.post(objectSpawned);
+ }
- for (GameObject object : tile.getGameObjects())
+ for (GameObject object : tile.getGameObjects())
+ {
+ if (object != null)
{
- if (object != null)
+ if (object.getSceneMinLocation().equals(tile.getSceneLocation()))
{
- if (object.getSceneMinLocation().equals(tile.getSceneLocation()))
- {
- final GameObjectSpawned objectSpawned = new GameObjectSpawned();
- objectSpawned.setTile(tile);
- objectSpawned.setGameObject(object);
- eventBus.post(objectSpawned);
- }
+ final GameObjectSpawned objectSpawned = new GameObjectSpawned();
+ objectSpawned.setTile(tile);
+ objectSpawned.setGameObject(object);
+ eventBus.post(objectSpawned);
}
}
+ }
- ItemLayer itemLayer = tile.getItemLayer();
- if (itemLayer != null)
+ ItemLayer itemLayer = tile.getItemLayer();
+ if (itemLayer != null)
+ {
+ Node current = itemLayer.getTop();
+ while (current instanceof TileItem)
{
- Node current = itemLayer.getTop();
- while (current instanceof TileItem)
- {
- final TileItem item = (TileItem) current;
+ final TileItem item = (TileItem) current;
- current = current.getNext();
+ current = current.getNext();
- final ItemSpawned itemSpawned = new ItemSpawned(tile, item);
- eventBus.post(itemSpawned);
- }
+ final ItemSpawned itemSpawned = new ItemSpawned(tile, item);
+ eventBus.post(itemSpawned);
}
- });
-
- eventBus.unregister(subscriber);
+ }
});
+
+ for (WorldView sub : wv.worldViews())
+ {
+ simulateGameEvents(sub);
+ }
}
}
\ No newline at end of file
diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java
index c51bb9f8c6c..88c948c5188 100644
--- a/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java
+++ b/runelite-client/src/test/java/net/runelite/client/plugins/loottracker/LootTrackerPluginTest.java
@@ -162,8 +162,8 @@ public void setUp()
wv = mock(WorldView.class);
when(client.getTopLevelWorldView()).thenReturn(wv);
-
when(client.getWorldView(anyInt())).thenReturn(wv);
+ when(client.findWorldViewFromWorldPoint(any(WorldPoint.class))).thenReturn(wv);
lootTrackerPlugin = spy(lootTrackerPlugin);
doNothing().when(lootTrackerPlugin).addLoot(any(), anyInt(), any(), any(), any(Collection.class));
diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/timetracking/farming/CompostTrackerTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/timetracking/farming/CompostTrackerTest.java
index 0116d69b54e..cd8fc990935 100644
--- a/runelite-client/src/test/java/net/runelite/client/plugins/timetracking/farming/CompostTrackerTest.java
+++ b/runelite-client/src/test/java/net/runelite/client/plugins/timetracking/farming/CompostTrackerTest.java
@@ -125,6 +125,7 @@ public void before()
when(wv.getSizeX()).thenReturn(104);
when(wv.getSizeY()).thenReturn(104);
when(client.getTopLevelWorldView()).thenReturn(wv);
+ when(client.findWorldViewFromWorldPoint(any(WorldPoint.class))).thenReturn(wv);
when(client.getLocalPlayer()).thenReturn(player);
when(player.getWorldLocation()).thenReturn(worldPoint);
diff --git a/runelite-jshell/pom.xml b/runelite-jshell/pom.xml
index 3797f812a7e..26b27ae731b 100644
--- a/runelite-jshell/pom.xml
+++ b/runelite-jshell/pom.xml
@@ -30,7 +30,7 @@
net.runelite
runelite-parent
- 1.11.20-SNAPSHOT
+ 1.11.21-SNAPSHOT
jshell
diff --git a/runelite-maven-plugin/pom.xml b/runelite-maven-plugin/pom.xml
index db6db0ed655..f0abdd956fc 100644
--- a/runelite-maven-plugin/pom.xml
+++ b/runelite-maven-plugin/pom.xml
@@ -29,7 +29,7 @@
net.runelite
runelite-parent
- 1.11.20-SNAPSHOT
+ 1.11.21-SNAPSHOT
runelite-maven-plugin