Repository: runelite/runelite
Pull request #17857 (2024-05-18 12:55:05)

Fix bug where hidden prayers were movable when prayer plugin is off.
to replicate:
enter prayer reordering mode
make sure a prayer is hidden.
disable prayer plugin
observe that hidden prayer can be dragged.


Patch:
diff --git a/cache/src/main/java/net/runelite/cache/definitions/AbstractWorldMapDataDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/AbstractWorldMapDataDefinition.java
index d56b2f5d5..c227a5528 100644
--- a/cache/src/main/java/net/runelite/cache/definitions/AbstractWorldMapDataDefinition.java
+++ b/cache/src/main/java/net/runelite/cache/definitions/AbstractWorldMapDataDefinition.java
@@ -31,10 +31,10 @@ public abstract class AbstractWorldMapDataDefinition
 {
 	public int minLevel;
 	public int levels;
-	public int displaySquareX;
-	public int displaySquareZ;
 	public int sourceSquareX;
 	public int sourceSquareZ;
+	public int displaySquareX;
+	public int displaySquareZ;
 	public int groupId;
 	public int fileId;
 }
diff --git a/cache/src/main/java/net/runelite/cache/definitions/MapSquareDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/MapSquareDefinition.java
index 57df51771..c0eaa5ba4 100644
--- a/cache/src/main/java/net/runelite/cache/definitions/MapSquareDefinition.java
+++ b/cache/src/main/java/net/runelite/cache/definitions/MapSquareDefinition.java
@@ -40,12 +40,12 @@ public class MapSquareDefinition extends AbstractWorldMapDataDefinition
 		else
 		{
 			MapSquareDefinition other = (MapSquareDefinition) obj;
-			return other.sourceSquareX == this.sourceSquareX && other.sourceSquareZ == this.sourceSquareZ;
+			return other.displaySquareX == this.displaySquareX && other.displaySquareZ == this.displaySquareZ;
 		}
 	}
 
 	public int hashCode()
 	{
-		return this.sourceSquareX | this.sourceSquareZ << 8;
+		return this.displaySquareX | this.displaySquareZ << 8;
 	}
 }
diff --git a/cache/src/main/java/net/runelite/cache/definitions/WorldMapCompositeDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/WorldMapCompositeDefinition.java
index 9f508db32..c448746da 100644
--- a/cache/src/main/java/net/runelite/cache/definitions/WorldMapCompositeDefinition.java
+++ b/cache/src/main/java/net/runelite/cache/definitions/WorldMapCompositeDefinition.java
@@ -48,20 +48,20 @@ public class WorldMapCompositeDefinition
 
 		for (MapSquareDefinition mapSquare : mapSquareDefinitions)
 		{
-			if (squareX == mapSquare.sourceSquareX && squareZ == mapSquare.sourceSquareZ)
+			if (squareX == mapSquare.displaySquareX && squareZ == mapSquare.displaySquareZ)
 			{
-				int shiftX = ((mapSquare.displaySquareX - mapSquare.sourceSquareX) * 64);
-				int shiftZ = ((mapSquare.displaySquareZ - mapSquare.sourceSquareZ) * 64);
+				int shiftX = ((mapSquare.sourceSquareX - mapSquare.displaySquareX) * 64);
+				int shiftZ = ((mapSquare.sourceSquareZ - mapSquare.displaySquareZ) * 64);
 				offset = new Position(shiftX, shiftZ, mapSquare.getMinLevel());
 			}
 		}
 
 		for (ZoneDefinition zone : zoneDefinitions)
 		{
-			if (squareX == zone.sourceSquareX && squareZ == zone.sourceSquareZ && zoneX == zone.sourceZoneX && zoneZ == zone.sourceZoneZ)
+			if (squareX == zone.displaySquareX && squareZ == zone.displaySquareZ && zoneX == zone.displayZoneX && zoneZ == zone.displayZoneZ)
 			{
-				int shiftX = ((zone.displaySquareX - zone.sourceSquareX) * 64) + ((zone.displayZoneX - zone.sourceZoneX) * 8);
-				int shiftZ = ((zone.displaySquareZ - zone.sourceSquareZ) * 64) + ((zone.displayZoneY - zone.sourceZoneZ) * 8);
+				int shiftX = ((zone.sourceSquareX - zone.displaySquareX) * 64) + ((zone.sourceZoneX - zone.displayZoneX) * 8);
+				int shiftZ = ((zone.sourceSquareZ - zone.displaySquareZ) * 64) + ((zone.sourceZoneZ - zone.displayZoneZ) * 8);
 				offset = new Position(shiftX, shiftZ, zone.getMinLevel());
 			}
 		}
diff --git a/cache/src/main/java/net/runelite/cache/definitions/WorldMapDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/WorldMapDefinition.java
new file mode 100644
index 000000000..9f0f80c75
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/definitions/WorldMapDefinition.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2017, Adam <Adam@sigterm.info>
+ * 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.cache.definitions;
+
+import java.util.List;
+import lombok.Data;
+import net.runelite.cache.region.Position;
+
+@Data
+public class WorldMapDefinition
+{
+	public String safeName;
+	public String name;
+	public int emptyTileColor;
+	public int backgroundColor;
+	public int defaultZoom;
+	public int fileId;
+	public boolean isSurface;
+	public List<WorldMapTypeBase> regionList;
+	public Position position;
+}
diff --git a/cache/src/main/java/net/runelite/cache/definitions/ZoneDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/ZoneDefinition.java
index b487d546b..1626bf35c 100644
--- a/cache/src/main/java/net/runelite/cache/definitions/ZoneDefinition.java
+++ b/cache/src/main/java/net/runelite/cache/definitions/ZoneDefinition.java
@@ -31,10 +31,10 @@ import lombok.ToString;
 @ToString(callSuper = true)
 public class ZoneDefinition extends AbstractWorldMapDataDefinition
 {
-	public int displayZoneX;
-	public int displayZoneY;
 	public int sourceZoneX;
 	public int sourceZoneZ;
+	public int displayZoneX;
+	public int displayZoneZ;
 
 	public boolean equals(Object obj)
 	{
@@ -45,13 +45,13 @@ public class ZoneDefinition extends AbstractWorldMapDataDefinition
 		else
 		{
 			ZoneDefinition other = (ZoneDefinition) obj;
-			return other.sourceSquareX == this.sourceSquareX && other.sourceSquareZ == this.sourceSquareZ
-					&& other.sourceZoneX == this.sourceZoneX && other.sourceZoneZ == this.sourceZoneZ;
+			return other.displaySquareX == this.displaySquareX && other.displaySquareZ == this.displaySquareZ
+					&& other.displayZoneX == this.displayZoneX && other.displayZoneZ == this.displayZoneZ;
 		}
 	}
 
 	public int hashCode()
 	{
-		return this.sourceSquareX | this.sourceSquareZ << 8 | this.sourceZoneX << 16 | this.sourceZoneZ << 24;
+		return this.displaySquareX | this.displaySquareZ << 8 | this.displayZoneX << 16 | this.displayZoneZ << 24;
 	}
 }
diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapDataLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapDataLoader.java
index 54691e9a1..9277fe4ec 100644
--- a/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapDataLoader.java
+++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapDataLoader.java
@@ -42,10 +42,10 @@ public class WorldMapDataLoader
 		MapSquareDefinition mapSquareDefinition = new MapSquareDefinition();
 		mapSquareDefinition.minLevel = in.readUnsignedByte();
 		mapSquareDefinition.levels = in.readUnsignedByte();
-		mapSquareDefinition.displaySquareX = in.readUnsignedShort();
-		mapSquareDefinition.displaySquareZ = in.readUnsignedShort();
 		mapSquareDefinition.sourceSquareX = in.readUnsignedShort();
 		mapSquareDefinition.sourceSquareZ = in.readUnsignedShort();
+		mapSquareDefinition.displaySquareX = in.readUnsignedShort();
+		mapSquareDefinition.displaySquareZ = in.readUnsignedShort();
 		mapSquareDefinition.groupId = in.readBigSmart2();
 		mapSquareDefinition.fileId = in.readBigSmart2();
 
@@ -64,14 +64,14 @@ public class WorldMapDataLoader
 		ZoneDefinition zoneDefinition = new ZoneDefinition();
 		zoneDefinition.minLevel = in.readUnsignedByte();
 		zoneDefinition.levels = in.readUnsignedByte();
-		zoneDefinition.displaySquareX = in.readUnsignedShort();
-		zoneDefinition.displaySquareZ = in.readUnsignedShort();
-		zoneDefinition.displayZoneX = in.readUnsignedByte();
-		zoneDefinition.displayZoneY = in.readUnsignedByte();
 		zoneDefinition.sourceSquareX = in.readUnsignedShort();
 		zoneDefinition.sourceSquareZ = in.readUnsignedShort();
 		zoneDefinition.sourceZoneX = in.readUnsignedByte();
 		zoneDefinition.sourceZoneZ = in.readUnsignedByte();
+		zoneDefinition.displaySquareX = in.readUnsignedShort();
+		zoneDefinition.displaySquareZ = in.readUnsignedShort();
+		zoneDefinition.displayZoneX = in.readUnsignedByte();
+		zoneDefinition.displayZoneZ = in.readUnsignedByte();
 		zoneDefinition.groupId = in.readBigSmart2();
 		zoneDefinition.fileId = in.readBigSmart2();
 
diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapLoader.java
new file mode 100644
index 000000000..118476a4c
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapLoader.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2017, Adam <Adam@sigterm.info>
+ * 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.cache.definitions.loaders;
+
+import java.util.LinkedList;
+import net.runelite.cache.definitions.WorldMapDefinition;
+import net.runelite.cache.definitions.WorldMapType0;
+import net.runelite.cache.definitions.WorldMapType1;
+import net.runelite.cache.definitions.WorldMapType2;
+import net.runelite.cache.definitions.WorldMapType3;
+import net.runelite.cache.definitions.WorldMapTypeBase;
+import net.runelite.cache.io.InputStream;
+import net.runelite.cache.region.Position;
+
+public class WorldMapLoader
+{
+	public WorldMapDefinition load(byte[] b, int fileId)
+	{
+		WorldMapDefinition def = new WorldMapDefinition();
+		InputStream in = new InputStream(b);
+
+		def.fileId = fileId;
+		def.safeName = in.readString();
+		def.name = in.readString();
+
+		int packedPos = in.readInt();
+		def.position = Position.fromPacked(packedPos);
+
+		def.emptyTileColor = in.readInt();
+		def.backgroundColor = in.readInt();
+		in.readUnsignedByte();
+		def.isSurface = in.readUnsignedByte() == 1;
+		def.defaultZoom = in.readUnsignedByte();
+		int var3 = in.readUnsignedByte();
+		def.regionList = new LinkedList<>();
+
+		for (int var4 = 0; var4 < var3; ++var4)
+		{
+			def.regionList.add(this.loadType(in));
+		}
+
+		return def;
+	}
+
+	private WorldMapTypeBase loadType(InputStream var1)
+	{
+		int var2 = var1.readUnsignedByte();
+		WorldMapTypeBase base;
+		switch (var2)
+		{
+			case 0:
+				// type 1
+				base = load1(var1);
+				break;
+			case 1:
+				// type 2
+				base = load2(var1);
+				break;
+			case 2:
+				// type 3
+				base = load3(var1);
+				break;
+			case 3:
+				// type 0
+				base = load0(var1);
+				break;
+			default:
+				throw new IllegalStateException();
+		}
+		return base;
+	}
+
+	private WorldMapTypeBase load0(InputStream in)
+	{
+		WorldMapType0 wm = new WorldMapType0();
+
+		wm.plane = in.readUnsignedByte();
+		wm.numberOfPlanes = in.readUnsignedByte();
+		wm.xLow = in.readUnsignedShort();
+		wm.chunk_xLow = in.readUnsignedByte();
+		wm.yLow = in.readUnsignedShort();
+		wm.chunk_yLow = in.readUnsignedByte();
+		wm.xHigh = in.readUnsignedShort();
+		wm.chunk_xHigh = in.readUnsignedByte();
+		wm.yHigh = in.readUnsignedShort();
+		wm.chunk_yHigh = in.readUnsignedByte();
+
+		return wm;
+	}
+
+	private WorldMapTypeBase load1(InputStream in)
+	{
+		WorldMapType1 wm = new WorldMapType1();
+
+		wm.plane = in.readUnsignedByte();
+		wm.numberOfPlanes = in.readUnsignedByte();
+		wm.xLowerLeft = in.readUnsignedShort();
+		wm.yLowerLeft = in.readUnsignedShort();
+		wm.xLowerRight = in.readUnsignedShort();
+		wm.yUpperLeft = in.readUnsignedShort();
+		wm.xUpperLeft = in.readUnsignedShort();
+		wm.yLowerRight = in.readUnsignedShort();
+		wm.xUpperRight = in.readUnsignedShort();
+		wm.yUpperRight = in.readUnsignedShort();
+
+		return wm;
+	}
+
+	private WorldMapTypeBase load2(InputStream in)
+	{
+		WorldMapType2 wm = new WorldMapType2();
+
+		wm.plane = in.readUnsignedByte();
+		wm.numberOfPlanes = in.readUnsignedByte();
+		wm.xLow = in.readUnsignedShort();
+		wm.yLow = in.readUnsignedShort();
+		wm.xHigh = in.readUnsignedShort();
+		wm.yHigh = in.readUnsignedShort();
+
+		return wm;
+	}
+
+	private WorldMapTypeBase load3(InputStream in)
+	{
+		WorldMapType3 wm = new WorldMapType3();
+
+		wm.oldPlane = in.readUnsignedByte();
+		wm.numberOfPlanes = in.readUnsignedByte();
+		wm.oldX = in.readUnsignedShort();
+		wm.chunk_oldXLow = in.readUnsignedByte();
+		wm.chunk_oldXHigh = in.readUnsignedByte();
+		wm.oldY = in.readUnsignedShort();
+		wm.chunk_oldYLow = in.readUnsignedByte();
+		wm.chunk_oldYHigh = in.readUnsignedByte();
+		wm.newX = in.readUnsignedShort();
+		wm.chunk_newXLow = in.readUnsignedByte();
+		wm.chunk_newXHigh = in.readUnsignedByte();
+		wm.newY = in.readUnsignedShort();
+		wm.chunk_newYLow = in.readUnsignedByte();
+		wm.chunk_newYHigh = in.readUnsignedByte();
+
+		return wm;
+	}
+}
diff --git a/runelite-api/src/main/java/net/runelite/api/WorldType.java b/runelite-api/src/main/java/net/runelite/api/WorldType.java
index 5cd26c4b6..b4ca3b3dc 100644
--- a/runelite-api/src/main/java/net/runelite/api/WorldType.java
+++ b/runelite-api/src/main/java/net/runelite/api/WorldType.java
@@ -70,6 +70,8 @@ public enum WorldType
 	 * Beta world.
 	 */
 	BETA_WORLD(1 << 16),
+	LEGACY_ONLY(1 << 22),
+	EOC_ONLY(1 << 23),
 	/**
 	 * Beta worlds without profiles that are saved.
 	 */
diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml
index c73318eca..12b876a52 100644
--- a/runelite-client/pom.xml
+++ b/runelite-client/pom.xml
@@ -253,7 +253,7 @@
 		<dependency>
 			<groupId>net.runelite.arn</groupId>
 			<artifactId>http-api</artifactId>
-			<version>1.2.12</version>
+			<version>1.2.16</version>
 		</dependency>
 		<dependency>
 			<groupId>net.runelite</groupId>
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/agility/Courses.java b/runelite-client/src/main/java/net/runelite/client/plugins/agility/Courses.java
index 221a40858..248526ad1 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/agility/Courses.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/agility/Courses.java
@@ -42,7 +42,7 @@ enum Courses
 	CANIFIS(240.0, 13878, new WorldPoint(3510, 3485, 0)),
 	APE_ATOLL(580.0, 11050, new WorldPoint(2770, 2747, 0)),
 	SHAYZIEN_ADVANCED(507.5, 5944, new WorldPoint(1522, 3625, 0)),
-	FALADOR(586, 12084, new WorldPoint(3029, 3332, 0)),
+	FALADOR(586, 12084, new WorldPoint(3029, 3332, 0), new WorldPoint(3029, 3333, 0), new WorldPoint(3029, 3334, 0), new WorldPoint(3029, 3335, 0)),
 	WILDERNESS(571.4, 11837, new WorldPoint(2994, 3933, 0)),
 	WEREWOLF(730.0, 14234, new WorldPoint(3528, 9873, 0)),
 	SEERS(570.0, 10806, new WorldPoint(2704, 3464, 0)),
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterPlugin.java
index c93c0dd24..b5da6850a 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatfilter/ChatFilterPlugin.java
@@ -216,6 +216,7 @@ public class ChatFilterPlugin extends Plugin
 				break;
 			case GAMEMESSAGE:
 			case ENGINE:
+			case FRIENDSCHATNOTIFICATION:
 			case ITEM_EXAMINE:
 			case NPC_EXAMINE:
 			case OBJECT_EXAMINE:
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollPlugin.java
index ea4704136..c19d7e5a0 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollPlugin.java
@@ -693,7 +693,7 @@ public class ClueScrollPlugin extends Plugin
 	@Subscribe
 	public void onCommandExecuted(CommandExecuted commandExecuted)
 	{
-		if (developerMode && commandExecuted.getCommand().equals("clue"))
+		if (developerMode && commandExecuted.getCommand().equalsIgnoreCase("clue"))
 		{
 			var text = String.join(" ", commandExecuted.getArguments());
 
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java
index 57b45e17e..dca93b03a 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java
@@ -1692,10 +1692,10 @@ public class CrypticClue extends ClueScroll implements NpcClueScroll, ObjectClue
 			.solution("Dig anywhere in Yu'biusk. Fairy ring BLQ.")
 			.build(),
 		CrypticClue.builder()
-			.text("Search the Bookshelf with a skull on it inside the Burrow.")
-			.location(new WorldPoint(1569, 9460, 0))
-			.objectId(ObjectID.BOOKSHELF_51736)
-			.solution("Search the bookshelf in the basement of the Hunter Guild.")
+			.text("Search the food barrel at the top of the Hunter Guild.")
+			.location(new WorldPoint(1560, 3048, 2))
+			.objectId(ObjectID.FOOD_BARREL)
+			.solution("Search the food barrel at the top of the Hunter Guild.")
 			.build(),
 		CrypticClue.builder()
 			.text("Are you somewhat entertained?")
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java
index 15b972265..62c214c82 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java
@@ -316,7 +316,7 @@ public class DevToolsPlugin extends Plugin
 	{
 		String[] args = commandExecuted.getArguments();
 
-		switch (commandExecuted.getCommand())
+		switch (commandExecuted.getCommand().toLowerCase())
 		{
 			case "logger":
 			{
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java
index 2021b9fe0..6f0ab3708 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java
@@ -95,7 +95,6 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
 	static final int MAX_TRIANGLE = 6144;
 	static final int SMALL_TRIANGLE_COUNT = 512;
 	private static final int FLAG_SCENE_BUFFER = Integer.MIN_VALUE;
-	private static final int DEFAULT_DISTANCE = 25;
 	static final int MAX_DISTANCE = 184;
 	static final int MAX_FOG_DEPTH = 100;
 	static final int SCENE_OFFSET = (Constants.EXTENDED_SCENE_SIZE - Constants.SCENE_SIZE) / 2; // offset for sxy -> msxy
@@ -1845,8 +1844,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks
 
 	private int getDrawDistance()
 	{
-		final int limit = computeMode != ComputeMode.NONE ? MAX_DISTANCE : DEFAULT_DISTANCE;
-		return Ints.constrainToRange(config.drawDistance(), 0, limit);
+		return Ints.constrainToRange(config.drawDistance(), 0, MAX_DISTANCE);
 	}
 
 	private void updateBuffer(@Nonnull GLBuffer glBuffer, int target, @Nonnull IntBuffer data, int usage, long clFlags)
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java
index aa373ab98..5a13eaebf 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java
@@ -635,7 +635,7 @@ public class LootTrackerPlugin extends Plugin
 		final LootTrackerItem[] entries = buildEntries(stack(items));
 		SwingUtilities.invokeLater(() -> panel.add(name, type, combatLevel, entries, amount));
 
-		LootRecord lootRecord = new LootRecord(name, type, metadata, toGameItems(items), Instant.now(), getLootWorldId());
+		LootRecord lootRecord = new LootRecord(name, type, metadata, toGameItems(items), Instant.now(), getLootWorldId(), amount, null);
 		synchronized (queuedLoots)
 		{
 			queuedLoots.add(lootRecord);
@@ -1319,7 +1319,7 @@ public class LootTrackerPlugin extends Plugin
 		{
 			ConfigLoot key = new ConfigLoot(record.getType(), record.getEventId());
 			ConfigLoot loot = map.computeIfAbsent(key, k -> key);
-			loot.kills++;
+			loot.kills += record.getAmount();
 			for (GameItem item : record.getDrops())
 			{
 				loot.add(item.getId(), item.getQty());
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyPlugin.java
index eb4078752..118d76e20 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyPlugin.java
@@ -608,7 +608,7 @@ public class PartyPlugin extends Plugin
 	@Subscribe
 	public void onCommandExecuted(CommandExecuted commandExecuted)
 	{
-		if (!developerMode || !commandExecuted.getCommand().equals("partyinfo"))
+		if (!developerMode || !commandExecuted.getCommand().equalsIgnoreCase("partyinfo"))
 		{
 			return;
 		}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/prayer/PrayerReorder.java b/runelite-client/src/main/java/net/runelite/client/plugins/prayer/PrayerReorder.java
index 5cdd96671..698250a67 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/prayer/PrayerReorder.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/prayer/PrayerReorder.java
@@ -356,15 +356,6 @@ class PrayerReorder
 
 				assert prayerWidget != null;
 
-				boolean hidden = isHidden(prayerbook, prayerId);
-				// in unlocked mode we show the prayers, but they have opacity set
-				if (hidden && !unlocked)
-				{
-					prayerWidget.setHidden(true);
-					++index;
-					continue;
-				}
-
 				int widgetConfig = prayerWidget.getClickMask();
 				if (unlocked)
 				{
@@ -378,6 +369,15 @@ class PrayerReorder
 				}
 				prayerWidget.setClickMask(widgetConfig);
 
+				boolean hidden = isHidden(prayerbook, prayerId);
+				// in unlocked mode we show the prayers, but they have opacity set
+				if (hidden && !unlocked)
+				{
+					prayerWidget.setHidden(true);
+					++index;
+					continue;
+				}
+
 				if (unlocked)
 				{
 					prayerWidget.setHidden(false);
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java
index f519ad516..3cf59bdc1 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java
@@ -275,7 +275,7 @@ public class SlayerPlugin extends Plugin
 	@Subscribe
 	public void onCommandExecuted(CommandExecuted commandExecuted)
 	{
-		if (developerMode && commandExecuted.getCommand().equals("task"))
+		if (developerMode && commandExecuted.getCommand().equalsIgnoreCase("task"))
 		{
 			var task = String.join(" ", commandExecuted.getArguments());
 			setTask(task, 42, 42);
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java
index b0c1793be..bada231f7 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java
@@ -396,7 +396,7 @@ public class SpecialCounterPlugin extends Plugin
 	@Subscribe
 	public void onCommandExecuted(CommandExecuted commandExecuted)
 	{
-		if (developerMode && commandExecuted.getCommand().equals("spec"))
+		if (developerMode && commandExecuted.getCommand().equalsIgnoreCase("spec"))
 		{
 			playerInfoDrops.add(createSpecInfoDrop(SpecialWeapon.BANDOS_GODSWORD, 42, client.getLocalPlayer().getId()));
 		}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPlugin.java
index a64c47a83..9f6f81e6f 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPlugin.java
@@ -203,7 +203,7 @@ public class TimeTrackingPlugin extends Plugin
 	@Subscribe
 	public void onCommandExecuted(CommandExecuted commandExecuted)
 	{
-		if (commandExecuted.getCommand().equals("resetfarmtick"))
+		if (commandExecuted.getCommand().equalsIgnoreCase("resetfarmtick"))
 		{
 			configManager.unsetRSProfileConfiguration(CONFIG_GROUP, TimeTrackingConfig.FARM_TICK_OFFSET_PRECISION);
 			configManager.unsetRSProfileConfiguration(CONFIG_GROUP, TimeTrackingConfig.FARM_TICK_OFFSET);
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperPlugin.java
index c8781d2af..b39543a0b 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldHopperPlugin.java
@@ -294,7 +294,7 @@ public class WorldHopperPlugin extends Plugin
 	@Subscribe
 	public void onCommandExecuted(CommandExecuted commandExecuted)
 	{
-		if ("hop".equals(commandExecuted.getCommand()))
+		if ("hop".equalsIgnoreCase(commandExecuted.getCommand()))
 		{
 			int worldNumber;
 			try
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableRow.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableRow.java
index aa63a28e8..ec4ea6088 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableRow.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldhopper/WorldTableRow.java
@@ -269,7 +269,8 @@ class WorldTableRow extends JPanel
 		{
 			activityField.setForeground(SEASONAL_WORLD);
 		}
-		else if (types.contains(WorldType.NOSAVE_MODE) || types.contains(WorldType.BETA_WORLD))
+		else if (types.contains(WorldType.NOSAVE_MODE) || types.contains(WorldType.BETA_WORLD)
+			|| types.contains(WorldType.LEGACY_ONLY)) // sometimes used for arbitrarily restricted worlds
 		{
 			activityField.setForeground(BETA_WORLD);
 		}
diff --git a/runelite-client/src/main/java/net/runelite/client/util/WorldUtil.java b/runelite-client/src/main/java/net/runelite/client/util/WorldUtil.java
index 0841fe07b..bdb1abdd3 100644
--- a/runelite-client/src/main/java/net/runelite/client/util/WorldUtil.java
+++ b/runelite-client/src/main/java/net/runelite/client/util/WorldUtil.java
@@ -73,6 +73,10 @@ public class WorldUtil
 				return WorldType.LAST_MAN_STANDING;
 			case BETA_WORLD:
 				return WorldType.BETA_WORLD;
+			case LEGACY_ONLY:
+				return WorldType.LEGACY_ONLY;
+			case EOC_ONLY:
+				return WorldType.EOC_ONLY;
 			case NOSAVE_MODE:
 				return WorldType.NOSAVE_MODE;
 			case TOURNAMENT:
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/regions/regions.txt b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/regions/regions.txt
index 68de1a27b..640669501 100644
--- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/regions/regions.txt
+++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/regions/regions.txt
@@ -238,4 +238,8 @@ r 45 71
 
 // pyramid plunder
 n
-r 30 69
\ No newline at end of file
+r 30 69
+
+// waterbirth dungeon
+n
+R 28 68 30 68
\ No newline at end of file


Test patch:
diff --git a/cache/src/test/java/net/runelite/cache/WorldMapDumperTest.java b/cache/src/test/java/net/runelite/cache/WorldMapDumperTest.java
new file mode 100644
index 000000000..a3f25a3e6
--- /dev/null
+++ b/cache/src/test/java/net/runelite/cache/WorldMapDumperTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2017, Adam <Adam@sigterm.info>
+ * 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.cache;
+
+import com.google.common.io.Files;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import net.runelite.cache.definitions.WorldMapDefinition;
+import net.runelite.cache.definitions.loaders.WorldMapLoader;
+import net.runelite.cache.fs.Archive;
+import net.runelite.cache.fs.ArchiveFiles;
+import net.runelite.cache.fs.FSFile;
+import net.runelite.cache.fs.Index;
+import net.runelite.cache.fs.Storage;
+import net.runelite.cache.fs.Store;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class WorldMapDumperTest
+{
+	private static final Logger logger = LoggerFactory.getLogger(WorldMapDumperTest.class);
+
+	@Rule
+	public TemporaryFolder folder = StoreLocation.getTemporaryFolder();
+
+	private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+	@Test
+	@Ignore
+	public void extract() throws IOException
+	{
+		File base = StoreLocation.LOCATION,
+			outDir = folder.newFolder();
+
+		int count = 0;
+
+		try (Store store = new Store(base))
+		{
+			store.load();
+
+			Storage storage = store.getStorage();
+			Index index = store.getIndex(IndexType.WORLDMAP);
+			Archive archive = index.findArchiveByName("details");
+
+			byte[] archiveData = storage.loadArchive(archive);
+			ArchiveFiles files = archive.getFiles(archiveData);
+
+			for (FSFile file : files.getFiles())
+			{
+				WorldMapLoader loader = new WorldMapLoader();
+				WorldMapDefinition def = loader.load(file.getContents(), file.getFileId());
+
+				Files.asCharSink(new File(outDir, file.getFileId() + ".json"), Charset.defaultCharset()).write(gson.toJson(def));
+				++count;
+			}
+		}
+
+		logger.info("Dumped {} world map data to {}", count, outDir);
+	}
+}