Repository: runelite/runelite
Pull request #18953 (2025-04-28 23:09:19)

maven plugin: add javadoc for deprecated InterfaceID and ComponentID
points devs to where the replacement is


Patch:
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/LayoutManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/LayoutManager.java
index 6bbd4ead8..8abe0bc33 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/LayoutManager.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/LayoutManager.java
@@ -697,13 +697,13 @@ public class LayoutManager
 					return;
 				}
 
-				idx = potionStorage.find(w.getItemId());
+				idx = potionStorage.getIdx(w.getItemId());
 				if (idx > -1)
 				{
 					potionStorage.prepareWidgets();
 					menu.setIdentifier(mungeBankToPotionStore(menu.getIdentifier()));
 					menu.setParam1(InterfaceID.Bankmain.POTIONSTORE_ITEMS);
-					menu.setParam0(idx * PotionStorage.COMPONENTS_PER_POTION);
+					menu.setParam0(idx);
 				}
 			}
 		}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/PotionStorage.java b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/PotionStorage.java
index 388868fe6..a8c410d15 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/PotionStorage.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/tabs/PotionStorage.java
@@ -39,6 +39,8 @@ import net.runelite.api.events.ClientTick;
 import net.runelite.api.events.VarbitChanged;
 import net.runelite.api.events.WidgetClosed;
 import net.runelite.api.gameval.InterfaceID;
+import net.runelite.api.gameval.ItemID;
+import net.runelite.api.gameval.VarPlayerID;
 import net.runelite.api.widgets.Widget;
 import net.runelite.api.widgets.WidgetType;
 import net.runelite.client.eventbus.Subscribe;
@@ -164,6 +166,16 @@ class PotionStorage
 
 	int matches(Set<Integer> bank, int itemId)
 	{
+		if (itemId == ItemID.VIAL_EMPTY)
+		{
+			if (hasVialsInPotionStorage())
+			{
+				return ItemID.VIAL_EMPTY;
+			}
+
+			return -1;
+		}
+
 		if (potions == null)
 		{
 			return -1;
@@ -200,6 +212,11 @@ class PotionStorage
 
 	int count(int itemId)
 	{
+		if (itemId == ItemID.VIAL_EMPTY)
+		{
+			return getVialsInPotionStorage();
+		}
+
 		if (potions == null)
 		{
 			return 0;
@@ -215,25 +232,45 @@ class PotionStorage
 		return 0;
 	}
 
-	int find(int itemId)
+	int getIdx(int itemId)
 	{
 		if (potions == null)
 		{
 			return -1;
 		}
 
+		if (itemId == ItemID.VIAL_EMPTY)
+		{
+			if (hasVialsInPotionStorage())
+			{
+				return potions.length * COMPONENTS_PER_POTION + 4;
+			}
+
+			return -1;
+		}
+
 		int potionIdx = 0;
 		for (Potion potion : potions)
 		{
 			++potionIdx;
 			if (potion != null && potion.itemId == itemId)
 			{
-				return potionIdx - 1;
+				return (potionIdx - 1) * COMPONENTS_PER_POTION;
 			}
 		}
 		return -1;
 	}
 
+	boolean hasVialsInPotionStorage()
+	{
+		return getVialsInPotionStorage() > 0;
+	}
+
+	int getVialsInPotionStorage()
+	{
+		return client.getVarpValue(VarPlayerID.POTIONSTORE_VIALS);
+	}
+
 	void prepareWidgets()
 	{
 		// if the potion store hasn't been opened yet, the client components won't have been made yet.
@@ -249,6 +286,13 @@ class PotionStorage
 					potStoreContent.createChild(childIdx++, WidgetType.GRAPHIC);
 				}
 			}
+
+			// Add widgets so that we can also take out vials
+			potStoreContent.createChild(childIdx++, WidgetType.GRAPHIC);
+			potStoreContent.createChild(childIdx++, WidgetType.RECTANGLE);
+			potStoreContent.createChild(childIdx++, WidgetType.TEXT);
+			potStoreContent.createChild(childIdx++, WidgetType.RECTANGLE);
+			potStoreContent.createChild(childIdx++, WidgetType.TEXT);
 		}
 	}
 }
\ No newline at end of file
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollOverlay.java
index 8242c4fcc..0634810ca 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollOverlay.java
@@ -69,6 +69,7 @@ public class ClueScrollOverlay extends OverlayPanel
 		item(ItemID.SEERS_HEADBAND_HARD),
 		item(ItemID.SEERS_HEADBAND_ELITE),
 		item(ItemID.WINT_TORCH),
+		item(ItemID.WINT_TORCH_OFFHAND),
 		item(ItemID.SKILLCAPE_MAX),
 		item(ItemID.SKILLCAPE_MAX_WORN),
 		item(ItemID.ABYSSAL_LANTERN_NORMAL),
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CoordinateClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CoordinateClue.java
index 4d1558477..c71ef70e7 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CoordinateClue.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CoordinateClue.java
@@ -45,6 +45,7 @@ import static net.runelite.client.plugins.cluescrolls.clues.Enemy.ARMADYLEAN_GUA
 import static net.runelite.client.plugins.cluescrolls.clues.Enemy.ARMADYLEAN_OR_BANDOSIAN_GUARD;
 import static net.runelite.client.plugins.cluescrolls.clues.Enemy.BANDOSIAN_GUARD;
 import static net.runelite.client.plugins.cluescrolls.clues.Enemy.BRASSICAN_MAGE;
+import static net.runelite.client.plugins.cluescrolls.clues.Enemy.BRASSICAN_OR_WIZARDS;
 import static net.runelite.client.plugins.cluescrolls.clues.Enemy.SARADOMIN_WIZARD;
 import static net.runelite.client.plugins.cluescrolls.clues.Enemy.ZAMORAK_WIZARD;
 import net.runelite.client.ui.overlay.OverlayUtil;
@@ -738,7 +739,7 @@ public class CoordinateClue extends ClueScroll implements LocationClueScroll
 		CoordinateClue.builder()
 			.location(new WorldPoint(2303, 3328, 0))
 			.directions("East of Prifddinas.")
-			.enemy(BRASSICAN_MAGE)
+			.enemy(BRASSICAN_OR_WIZARDS)
 			.build(),
 		CoordinateClue.builder()
 			.location(new WorldPoint(3570, 3405, 0))
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/SkillChallengeClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/SkillChallengeClue.java
index 101e1fb8f..8ba1ffcc4 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/SkillChallengeClue.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/SkillChallengeClue.java
@@ -153,7 +153,8 @@ public class SkillChallengeClue extends ClueScroll implements NpcClueScroll, Nam
 
 	private static final AnyRequirementCollection ANY_HAMMER = any("Hammer",
 		item(ItemID.HAMMER),
-		item(ItemID.IMCANDO_HAMMER)
+		item(ItemID.IMCANDO_HAMMER),
+		item(ItemID.IMCANDO_HAMMER_OFFHAND)
 	);
 
 	private static final AllRequirementsCollection A_FULL_GRACEFUL_SET = all("A full Graceful set",
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java
index 246f76ed9..13f4a4987 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginHubPanel.java
@@ -52,7 +52,6 @@ import java.util.concurrent.ScheduledExecutorService;
 import java.util.function.Function;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 import javax.annotation.Nullable;
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -494,19 +493,19 @@ class PluginHubPanel extends PluginPanel
 			@Override
 			public void insertUpdate(DocumentEvent e)
 			{
-				filter();
+				executor.execute(PluginHubPanel.this::filter);
 			}
 
 			@Override
 			public void removeUpdate(DocumentEvent e)
 			{
-				filter();
+				executor.execute(PluginHubPanel.this::filter);
 			}
 
 			@Override
 			public void changedUpdate(DocumentEvent e)
 			{
-				filter();
+				executor.execute(PluginHubPanel.this::filter);
 			}
 		});
 
@@ -653,6 +652,12 @@ class PluginHubPanel extends PluginPanel
 
 		Set<String> installed = new HashSet<>(externalPluginManager.getInstalledExternalPlugins());
 
+		plugins = Sets.union(display.keySet(), loadedPlugins.keySet())
+			.stream()
+			.map(id -> new PluginItem(display.get(id), jars.get(id), loadedPlugins.get(id),
+				pluginCounts.getOrDefault(id, -1), installed.contains(id)))
+			.collect(Collectors.toList());
+
 		SwingUtilities.invokeLater(() ->
 		{
 			if (!refreshing.isVisible())
@@ -660,14 +665,8 @@ class PluginHubPanel extends PluginPanel
 				return;
 			}
 
-			plugins = Sets.union(display.keySet(), loadedPlugins.keySet())
-				.stream()
-				.map(id -> new PluginItem(display.get(id), jars.get(id), loadedPlugins.get(id),
-					pluginCounts.getOrDefault(id, -1), installed.contains(id)))
-				.collect(Collectors.toList());
-
 			refreshing.setVisible(false);
-			filter();
+			executor.execute(PluginHubPanel.this::filter);
 		});
 	}
 
@@ -678,29 +677,31 @@ class PluginHubPanel extends PluginPanel
 			return;
 		}
 
-		mainPanel.removeAll();
-
-		Stream<PluginItem> stream = plugins.stream();
-
 		String query = searchBar.getText();
 		boolean isSearching = query != null && !query.trim().isEmpty();
+		List<PluginItem> pluginItems;
 		if (isSearching)
 		{
-			PluginSearch.search(plugins, query).forEach(mainPanel::add);
+			pluginItems = PluginSearch.search(plugins, query);
 		}
 		else
 		{
-			stream.filter(p -> p.isInstalled() || p.getJarData() != null)
+			pluginItems = plugins.stream().filter(p -> p.isInstalled() || p.getJarData() != null)
 				.sorted(Comparator.comparing((PluginItem p) -> p.getJarData() == null)
 					.thenComparing(PluginItem::isInstalled)
 					.thenComparingInt(PluginItem::getUserCount)
 					.reversed()
 					.thenComparing(p -> p.manifest.getDisplayName())
 				)
-				.forEach(mainPanel::add);
+				.collect(Collectors.toList());
 		}
 
-		mainPanel.revalidate();
+		SwingUtilities.invokeLater(() ->
+		{
+			mainPanel.removeAll();
+			pluginItems.forEach(mainPanel::add);
+			mainPanel.revalidate();
+		});
 	}
 
 	@Override
@@ -732,17 +733,13 @@ class PluginHubPanel extends PluginPanel
 	@Subscribe
 	private void onExternalPluginsChanged(ExternalPluginsChanged ev)
 	{
-		Map<String, Integer> pluginCounts = Collections.emptyMap();
-		if (plugins != null)
-		{
-			pluginCounts = plugins.stream()
-				.collect(Collectors.toMap(pi -> pi.manifest.getInternalName(), PluginItem::getUserCount));
-		}
-
 		if (!refreshing.isVisible() && lastManifest != null)
 		{
 			refreshing.setVisible(true);
-			reloadPluginList(lastManifest, pluginCounts);
+
+			Map<String, Integer> pluginCounts = plugins == null ? Collections.emptyMap()
+				: plugins.stream().collect(Collectors.toMap(pi -> pi.manifest.getInternalName(), PluginItem::getUserCount));
+			executor.submit(() -> reloadPluginList(lastManifest, pluginCounts));
 		}
 	}
 }
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 d9ffefce6..4e9d3f416 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
@@ -436,6 +436,8 @@ enum ItemIdentification
 	ARDOUGNE_TELEPORT(Type.TABLET, "Ardoug", "ARD", ItemID.POH_TABLET_ARDOUGNETELEPORT),
 	WATCHTOWER_TELEPORT(Type.TABLET, "W.tow", "WT", ItemID.POH_TABLET_WATCHTOWERTELEPORT),
 	TELEPORT_TO_HOUSE(Type.TABLET, "House", "POH", ItemID.POH_TABLET_TELEPORTTOHOUSE),
+	KOUREND_CASTLE_TELEPORT(Type.TABLET, "K.Cast", "KOU", ItemID.POH_TABLET_KOURENDTELEPORT),
+	CIVITAS_ILLA_FORTIS_TELEPORT(Type.TABLET, "Civitas", "CIV", ItemID.POH_TABLET_FORTISTELEPORT),
 
 	ENCHANT_SAPPHIRE_OR_OPAL(Type.TABLET, "E.Saph", "E SO", ItemID.POH_TABLET_ENCHANTSAPPHIRE),
 	ENCHANT_EMERALD_OR_JADE(Type.TABLET, "E.Emer", "E EJ", ItemID.POH_TABLET_ENCHANTEMERALD),
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 95734fdcb..91e058d29 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
@@ -171,7 +171,7 @@ public class LootTrackerPlugin extends Plugin
 	// Used by Stone Chest, Isle of Souls chest, Dark Chest
 	private static final String OTHER_CHEST_LOOTED_MESSAGE = "You steal some loot from the chest.";
 	private static final String DORGESH_KAAN_CHEST_LOOTED_MESSAGE = "You find treasure inside!";
-	private static final String GRUBBY_CHEST_LOOTED_MESSAGE = "You have opened the Grubby Chest";
+	private static final Pattern GRUBBY_CHEST_LOOTED_MESSAGE = Pattern.compile("You find treasure(?: and supplies|, supplies, and a weirdly coloured egg sac) within the chest.");
 	private static final String ANCIENT_CHEST_LOOTED_MESSAGE = "You open the chest and find";
 	private static final Pattern HAM_CHEST_LOOTED_PATTERN = Pattern.compile("Your (?<key>[a-z]+) key breaks in the lock.*");
 	private static final int HAM_STOREROOM_REGION = 10321;
@@ -1013,7 +1013,7 @@ public class LootTrackerPlugin extends Plugin
 		final String message = event.getMessage();
 
 		if (message.equals(CHEST_LOOTED_MESSAGE) || message.equals(OTHER_CHEST_LOOTED_MESSAGE)
-			|| message.equals(DORGESH_KAAN_CHEST_LOOTED_MESSAGE) || message.startsWith(GRUBBY_CHEST_LOOTED_MESSAGE)
+			|| message.equals(DORGESH_KAAN_CHEST_LOOTED_MESSAGE) || GRUBBY_CHEST_LOOTED_MESSAGE.matcher(message).matches()
 			|| message.startsWith(ANCIENT_CHEST_LOOTED_MESSAGE)
 			|| LARRAN_LOOTED_PATTERN.matcher(message).matches() || ROGUES_CHEST_PATTERN.matcher(message).matches())
 		{
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java
index 8c5cf7ecb..569ff4d43 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java
@@ -143,7 +143,8 @@ public class ScreenshotPlugin extends Plugin
 		TOB_HM,
 		TOA_ENTRY_MODE,
 		TOA,
-		TOA_EXPERT_MODE
+		TOA_EXPERT_MODE,
+		FORTIS_COLOSSEUM
 	}
 
 	private KillType killType;
@@ -429,6 +430,12 @@ public class ScreenshotPlugin extends Plugin
 			}
 		}
 
+		if (chatMessage.contains("Search the chest nearby to retrieve your earned rewards!"))
+		{
+			killType = KillType.FORTIS_COLOSSEUM;
+			return;
+		}
+
 		if (chatMessage.equals("Your request to kick/ban this user was successful.") && config.screenshotKick())
 		{
 			if (kickPlayerName == null)
@@ -551,6 +558,8 @@ public class ScreenshotPlugin extends Plugin
 			case InterfaceID.TOA_CHESTS:
 			case InterfaceID.BARROWS_REWARD:
 			case InterfaceID.PMOON_REWARD:
+			case InterfaceID.COLOSSEUM_REWARD_CHEST_2:
+
 				if (!config.screenshotRewards())
 				{
 					return;
@@ -717,6 +726,18 @@ public class ScreenshotPlugin extends Plugin
 				screenshotSubDir = SD_WILDERNESS_LOOT_CHEST;
 				break;
 			}
+			case InterfaceID.COLOSSEUM_REWARD_CHEST_2:
+			{
+				if (killType != KillType.FORTIS_COLOSSEUM)
+				{
+					return;
+				}
+
+				fileName = "Fortis Colosseum Chest";
+				screenshotSubDir = SD_CHEST_LOOT;
+				killType = null;
+				break;
+			}
 			default:
 				return;
 		}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MinigameLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MinigameLocation.java
index 470ff4221..dc8933972 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MinigameLocation.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MinigameLocation.java
@@ -41,6 +41,7 @@ enum MinigameLocation
 	CATAPULT_ROOM("Catapult Room", new WorldPoint(2842, 3545, 0)),
 	CLAN_WARS("Clan Wars", new WorldPoint(3133, 3621, 0)),
 	DUMMY_ROOM("Dummy Room", new WorldPoint(2857, 3551, 0)),
+	EMIRS_ARENA("Emir's Arena", new WorldPoint(3313, 3238, 0)),
 	FISHING_TRAWLER("Fishing Trawler", new WorldPoint(2667, 3163, 0)),
 	FORTIS_COLOSSEUM("Fortis Colosseum", new WorldPoint(1807, 9507, 0)),
 	FORTIS_COLOSSEUM_SURFACE("Fortis Colosseum", new WorldPoint(1798, 3107, 0)),
@@ -63,7 +64,6 @@ enum MinigameLocation
 	PEST_CONTROL_INTERMEDIATE("Pest Control Intermediate", new WorldPoint(2638, 2641, 0)),
 	PEST_CONTROL_NOVICE("Pest Control Novice", new WorldPoint(2660, 2637, 0)),
 	PEST_CONTROL_VETERAN("Pest Control Veteran", new WorldPoint(2632, 2648, 0)),
-	PVP_ARENA("PvP Arena", new WorldPoint(3313, 3238, 0)),
 	PYRAMID_PLUNDER("Pyramid Plunder", new WorldPoint(3288, 2787, 0)),
 	RANGING_GUILD("Ranging Guild", new WorldPoint(2671, 3419, 0)),
 	RAT_PITS_ARDOUGNE("Rat Pits", new WorldPoint(2561, 3318, 0)),
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/TeleportLocationData.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/TeleportLocationData.java
index a4bc90795..1620d69a4 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/TeleportLocationData.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/TeleportLocationData.java
@@ -78,7 +78,7 @@ enum TeleportLocationData
 	TEARS_OF_GUTHIX(TeleportType.JEWELLERY, "Games Necklace" , "Tears of Guthix", new WorldPoint(3245, 9500, 0), "games_necklace_teleport_icon.png"),
 	CORPOREAL_BEAST(TeleportType.JEWELLERY, "Games Necklace" , "Corporeal Beast", new WorldPoint(2967, 4384, 0), "games_necklace_teleport_icon.png"),
 	WINTERTODT_CAMP(TeleportType.JEWELLERY, "Games Necklace" , "Wintertodt Camp", new WorldPoint(1624, 3938, 0), "games_necklace_teleport_icon.png"),
-	PVP_ARENA(TeleportType.JEWELLERY, "Ring of Dueling" , "Al Kharid PvP Arena", new WorldPoint(3315, 3235, 0), "ring_of_dueling_teleport_icon.png"),
+	EMIRS_ARENA(TeleportType.JEWELLERY, "Ring of Dueling" , "Emir's Arena", new WorldPoint(3315, 3235, 0), "ring_of_dueling_teleport_icon.png"),
 	FEROX_ENCLAVE(TeleportType.JEWELLERY, "Ring of Dueling" , "Ferox Enclave", new WorldPoint(3151, 3636, 0), "ring_of_dueling_teleport_icon.png"),
 	CASTLE_WARS(TeleportType.JEWELLERY, "Ring of Dueling" , "Castle Wars", new WorldPoint(2441, 3091, 0), "ring_of_dueling_teleport_icon.png"),
 	FORTIS_COLOSSEUM(TeleportType.JEWELLERY, "Ring of Dueling" , "Fortis Colosseum", new WorldPoint(1793, 3107, 0), "ring_of_dueling_teleport_icon.png"),
diff --git a/runelite-maven-plugin/src/main/java/net/runelite/mvn/ComponentMojo.java b/runelite-maven-plugin/src/main/java/net/runelite/mvn/ComponentMojo.java
index 7377974be..619fa273b 100644
--- a/runelite-maven-plugin/src/main/java/net/runelite/mvn/ComponentMojo.java
+++ b/runelite-maven-plugin/src/main/java/net/runelite/mvn/ComponentMojo.java
@@ -70,11 +70,13 @@ public class ComponentMojo extends AbstractMojo
 	{
 		TypeSpec.Builder interfaceType = TypeSpec.classBuilder("InterfaceID")
 			.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
-			.addAnnotation(Deprecated.class);
+			.addAnnotation(Deprecated.class)
+			.addJavadoc("@deprecated Use {@link net.runelite.api.gameval.InterfaceID} instead");
 
 		TypeSpec.Builder componentType = TypeSpec.classBuilder("ComponentID")
 			.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
-			.addAnnotation(Deprecated.class);
+			.addAnnotation(Deprecated.class)
+			.addJavadoc("@deprecated Use nested classes of {@link net.runelite.api.gameval.InterfaceID} instead");
 
 		for (File file : inputDirectory.listFiles((dir, name) -> name.endsWith(".toml")))
 		{


Test patch:
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 c01e7bdaf..7f5e846db 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
@@ -355,6 +355,11 @@ public class LootTrackerPluginTest
 	@Test
 	public void testGrubbyChest()
 	{
+		// The order of events is as follows:
+		// 1. The key is removed from the players inventory
+		// 2. The MESBOX chat message prompt is delivered
+		// 3. The loot is added to the inventory/ground
+
 		Player player = mock(Player.class);
 		when(player.getWorldLocation()).thenReturn(new WorldPoint(7323 >> 2, (7323 & 0xff) << 6, 0));
 		when(client.getLocalPlayer()).thenReturn(player);
@@ -365,29 +370,44 @@ public class LootTrackerPluginTest
 		ItemContainer itemContainer = mock(ItemContainer.class);
 		when(itemContainer.getItems()).thenReturn(new Item[]{
 			new Item(ItemID.TWISTED_BOW, 1),
-			new Item(ItemID.HOSDUN_GRUBBY_KEY, 1)
+			new Item(ItemID.HOSDUN_GRUBBY_KEY, 2)
 		});
 		when(client.getItemContainer(InventoryID.INV)).thenReturn(itemContainer);
 
-		ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.SPAM, "", "You unlock the chest with your key.", "", 0);
+		lootTrackerPluginSpy.onItemContainerChanged(new ItemContainerChanged(InventoryID.INV, itemContainer));
+
+		// Default loot message
+		ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.MESBOX, "", "You find treasure and supplies within the chest.", "", 0);
 		lootTrackerPluginSpy.onChatMessage(chatMessage);
 
 		when(itemContainer.getItems()).thenReturn(new Item[]{
-			new Item(ItemID.TWISTED_BOW, 1)
+			new Item(ItemID.TWISTED_BOW, 1),
+			new Item(ItemID.HOSDUN_GRUBBY_KEY, 1),
+			new Item(ItemID.SHARK, 42)
 		});
 		lootTrackerPluginSpy.onItemContainerChanged(new ItemContainerChanged(InventoryID.INV, itemContainer));
 
-		chatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", "You have opened the Grubby Chest 2 times.", "", 0);
+		verify(lootTrackerPluginSpy).addLoot("Grubby Chest", -1, LootRecordType.EVENT, null, Arrays.asList(
+			new ItemStack(ItemID.SHARK, 42)
+		));
+
+		// Unique loot message
+		chatMessage = new ChatMessage(null, ChatMessageType.MESBOX, "", "You find treasure, supplies, and a weirdly coloured egg sac within the chest.", "", 0);
 		lootTrackerPluginSpy.onChatMessage(chatMessage);
 
 		when(itemContainer.getItems()).thenReturn(new Item[]{
 			new Item(ItemID.TWISTED_BOW, 1),
-			new Item(ItemID.SHARK, 42)
+			new Item(ItemID.SHARK, 42),
+			new Item(ItemID._2DOSEPOTIONOFSARADOMIN, 3),
+			new Item(ItemID.BR_2DOSE2RESTORE, 1),
+			new Item(ItemID.HOSDUN_ORANGE_EGG_SAC, 1),
 		});
 		lootTrackerPluginSpy.onItemContainerChanged(new ItemContainerChanged(InventoryID.INV, itemContainer));
 
 		verify(lootTrackerPluginSpy).addLoot("Grubby Chest", -1, LootRecordType.EVENT, null, Arrays.asList(
-			new ItemStack(ItemID.SHARK, 42)
+			new ItemStack(ItemID._2DOSEPOTIONOFSARADOMIN, 3),
+			new ItemStack(ItemID.BR_2DOSE2RESTORE, 1),
+			new ItemStack(ItemID.HOSDUN_ORANGE_EGG_SAC, 1)
 		));
 	}