Repository: runelite/runelite
Pull request #18170 (2024-08-17 06:54:22)

slayer: fix checking slayer task from inventory with eternal slayer ring
This PR fixes a bug with the slayer plugin where checking the current task from the inventory using an eternal slayer ring will not add the slayer task infobox, whereas doing the same action using a regular slayer ring or slayer helmet will. Conversely, using the "Check" option (on the eternal slayer ring) from the worn equipment interface will add the slayer task infobox as expected.

The issue behind the discrepancy is that the "Check" option is a different menuAction (`CC_OP_LOW_PRIORITY`) in the inventory, thus failing the first check. This change addresses that by including the menuAction in the conditional statement.


Patch:
diff --git a/runelite-api/src/main/java/net/runelite/api/Client.java b/runelite-api/src/main/java/net/runelite/api/Client.java
index e7c943968..8c70ec7ab 100644
--- a/runelite-api/src/main/java/net/runelite/api/Client.java
+++ b/runelite-api/src/main/java/net/runelite/api/Client.java
@@ -607,6 +607,8 @@ public interface Client extends OAuthApi, GameEngine
 	 * Create a new menu entry
 	 * @param idx the index to create the menu entry at. Accepts negative indexes eg. -1 inserts at the end.
 	 * @return the newly created menu entry
+	 * @see #getMenu()
+	 * @see Menu#createMenuEntry(int)
 	 */
 	@Deprecated
 	MenuEntry createMenuEntry(int idx);
@@ -616,6 +618,8 @@ public interface Client extends OAuthApi, GameEngine
 	 * clicked and activated.
 	 *
 	 * @return array of open menu entries
+	 * @see #getMenu()
+	 * @see Menu#getMenuEntries()
 	 */
 	@Deprecated
 	MenuEntry[] getMenuEntries();
@@ -627,6 +631,8 @@ public interface Client extends OAuthApi, GameEngine
 	 * event, since setting the menu entries will be overwritten the next frame
 	 *
 	 * @param entries new array of open menu entries
+	 * @see #getMenu()
+	 * @see Menu#setMenuEntries(MenuEntry[])
 	 */
 	@Deprecated
 	void setMenuEntries(MenuEntry[] entries);
@@ -660,6 +666,7 @@ public interface Client extends OAuthApi, GameEngine
 	 * Get the menu x location. Only valid if the menu is open.
 	 *
 	 * @return the menu x location
+	 * @see Menu#getMenuX()
 	 */
 	@Deprecated
 	int getMenuX();
@@ -668,6 +675,7 @@ public interface Client extends OAuthApi, GameEngine
 	 * Get the menu y location. Only valid if the menu is open.
 	 *
 	 * @return the menu y location
+	 * @see Menu#getMenuY()
 	 */
 	@Deprecated
 	int getMenuY();
@@ -676,6 +684,7 @@ public interface Client extends OAuthApi, GameEngine
 	 * Get the menu height. Only valid if the menu is open.
 	 *
 	 * @return the menu height
+	 * @see Menu#getMenuHeight()
 	 */
 	@Deprecated
 	int getMenuHeight();
@@ -684,6 +693,7 @@ public interface Client extends OAuthApi, GameEngine
 	 * Get the menu width. Only valid if the menu is open.
 	 *
 	 * @return the menu width
+	 * @see Menu#getMenuWidth()
 	 */
 	@Deprecated
 	int getMenuWidth();
@@ -2060,6 +2070,7 @@ public interface Client extends OAuthApi, GameEngine
 	 * @return the array of instance template chunks
 	 * @see Constants#CHUNK_SIZE
 	 * @see InstanceTemplates
+	 * @see WorldView#getInstanceTemplateChunks()
 	 */
 	@Deprecated
 	int[][][] getInstanceTemplateChunks();
@@ -2081,6 +2092,7 @@ public interface Client extends OAuthApi, GameEngine
 
 	/**
 	 * Checks whether the scene is in an instanced region.
+	 * @see WorldView#isInstance()
 	 */
 	@Deprecated
 	boolean isInInstancedRegion();
@@ -2095,6 +2107,7 @@ public interface Client extends OAuthApi, GameEngine
 
 	/**
 	 * Gets the current scene
+	 * @see WorldView#getScene()
 	 */
 	@Deprecated
 	default Scene getScene()
@@ -2107,6 +2120,7 @@ public interface Client extends OAuthApi, GameEngine
 	 * Gets a list of all valid players from the player cache.
 	 *
 	 * @return a list of all players
+	 * @see WorldView#players()
 	 */
 	@Deprecated
 	default List<Player> getPlayers()
@@ -2121,6 +2135,7 @@ public interface Client extends OAuthApi, GameEngine
 	 * Gets a list of all valid NPCs from the NPC cache.
 	 *
 	 * @return a list of all NPCs
+	 * @see WorldView#npcs()
 	 */
 	@Deprecated
 	default List<NPC> getNpcs()
@@ -2135,6 +2150,7 @@ public interface Client extends OAuthApi, GameEngine
 	 * Gets an array of all cached NPCs.
 	 *
 	 * @return cached NPCs
+	 * @see WorldView#npcs()
 	 */
 	@Deprecated
 	default NPC[] getCachedNPCs()
@@ -2147,6 +2163,7 @@ public interface Client extends OAuthApi, GameEngine
 	 * Gets an array of all cached players.
 	 *
 	 * @return cached players
+	 * @see WorldView#players()
 	 */
 	@Deprecated
 	default Player[] getCachedPlayers()
@@ -2161,6 +2178,7 @@ public interface Client extends OAuthApi, GameEngine
 	 * The index into the array is the plane/z-axis coordinate.
 	 *
 	 * @return the collision data
+	 * @see WorldView#getCollisionMaps()
 	 */
 	@Nullable
 	@Deprecated
@@ -2180,6 +2198,7 @@ public interface Client extends OAuthApi, GameEngine
 	 * level use a tile offset and are still considered plane 0 by the game.
 	 *
 	 * @return the plane
+	 * @see WorldView#getPlane()
 	 */
 	@Deprecated
 	default int getPlane()
@@ -2192,6 +2211,7 @@ public interface Client extends OAuthApi, GameEngine
 	 * current scene.
 	 *
 	 * @return the tile heights
+	 * @see WorldView#getTileHeights()
 	 */
 	@Deprecated
 	default int[][][] getTileHeights()
@@ -2204,6 +2224,7 @@ public interface Client extends OAuthApi, GameEngine
 	 * current scene.
 	 *
 	 * @return the tile settings
+	 * @see WorldView#getTileSettings()
 	 */
 	@Deprecated
 	default byte[][][] getTileSettings()
@@ -2218,6 +2239,7 @@ public interface Client extends OAuthApi, GameEngine
 	 * the current scene (ie. the bottom-left most coordinates in the scene).
 	 *
 	 * @return the base x-axis coordinate
+	 * @see WorldView#getBaseX()
 	 */
 	@Deprecated
 	default int getBaseX()
@@ -2233,6 +2255,7 @@ public interface Client extends OAuthApi, GameEngine
 	 * the current scene (ie. the bottom-left most coordinates in the scene).
 	 *
 	 * @return the base y-axis coordinate
+	 * @see WorldView#getBaseY()
 	 */
 	@Deprecated
 	default int getBaseY()
@@ -2257,6 +2280,7 @@ public interface Client extends OAuthApi, GameEngine
 	 * @param targetX target x - if an actor target is supplied should be the target x
 	 * @param targetY target y - if an actor target is supplied should be the target y
 	 * @return the new projectile
+	 * @see WorldView#createProjectile(int, int, int, int, int, int, int, int, int, int, Actor, int, int)
 	 */
 	@Deprecated
 	default Projectile createProjectile(int id, int plane, int startX, int startY, int startZ, int startCycle, int endCycle,
@@ -2269,6 +2293,7 @@ public interface Client extends OAuthApi, GameEngine
 	 * Gets a list of all projectiles currently spawned.
 	 *
 	 * @return all projectiles
+	 * @see WorldView#getProjectiles()
 	 */
 	@Deprecated
 	default Deque<Projectile> getProjectiles()
@@ -2280,6 +2305,7 @@ public interface Client extends OAuthApi, GameEngine
 	 * Gets a list of all graphics objects currently drawn.
 	 *
 	 * @return all graphics objects
+	 * @see WorldView#getGraphicsObjects()
 	 */
 	@Deprecated
 	default Deque<GraphicsObject> getGraphicsObjects()
@@ -2291,6 +2317,7 @@ public interface Client extends OAuthApi, GameEngine
 	 * Gets the currently selected tile. (ie. last right clicked tile)
 	 *
 	 * @return the selected tile
+	 * @see WorldView#getSelectedSceneTile()
 	 */
 	@Deprecated
 	@Nullable
diff --git a/runelite-api/src/main/java/net/runelite/api/SpriteID.java b/runelite-api/src/main/java/net/runelite/api/SpriteID.java
index 3fb2f0eeb..1e1b4cfc4 100644
--- a/runelite-api/src/main/java/net/runelite/api/SpriteID.java
+++ b/runelite-api/src/main/java/net/runelite/api/SpriteID.java
@@ -1683,6 +1683,9 @@ public final class SpriteID
 	public static final int HEALTHBAR_PURPLE_BACK_140PX = 4725;
 	public static final int HEALTHBAR_PURPLE_FRONT_160PX = 4726;
 	public static final int HEALTHBAR_PURPLE_BACK_160PX = 4727;
+	/* Unmapped: 4728~4765 */
+	public static final int COLOSSEUM_DOOM = 4766;
+	/* Unmapped: 4767~4842 */
 	public static final int PRAYER_RP_ANCIENT_STRENGTH = 4843;
 	public static final int PRAYER_RP_ANCIENT_SIGHT = 4844;
 	public static final int PRAYER_RP_ANCIENT_WILL = 4845;
diff --git a/runelite-api/src/main/java/net/runelite/api/Varbits.java b/runelite-api/src/main/java/net/runelite/api/Varbits.java
index b72cc8b39..6ad752084 100644
--- a/runelite-api/src/main/java/net/runelite/api/Varbits.java
+++ b/runelite-api/src/main/java/net/runelite/api/Varbits.java
@@ -89,6 +89,15 @@ public final class Varbits
 	public static final int DIVINE_BASTION = 13664;
 	public static final int DIVINE_BATTLEMAGE = 13665;
 
+	/**
+	 * Moonlight potion timer.
+	 * When at least 70 herblore, the moonlight potion's defense effect will be removed when this timer runs out.
+	 * If the player drinks a dose of moonlight potion while already under its effects, desync between
+	 * Varbits.MOONLIGHT_POTION and Varbits.DIVINE_SUPER_DEFENCE can occur, with the latter being 1 tick greater.
+	 * In case of desync, the moonlight defence effect will be removed once Varbits.DIVINE_SUPER_DEFENCE becomes 0.
+	 */
+	public static final int MOONLIGHT_POTION = 10029;
+
 	/**
 	 * Ring of endurance effect timer, stamina duration extended from using the ring of endurance
 	 * Number of game ticks remaining on ring of endurance effect in intervals of 10; for a value X there are 10 * X game ticks remaining.
@@ -892,4 +901,19 @@ public final class Varbits
 
 	public static final int SPELLBOOK = 4070;
 	public static final int SPELLBOOK_SUBMENU = 9730;
+
+	/**
+	 * The amount of Curse of the Moons stacks received when fighting the Blue Moon or Eclipse Moon.
+	 * The varbit value remains 0 when fighting the Blood Moon.
+	 * When fighting the Blue Moon, the player's joints will lock up at 18 stacks, which causes their next attack to be
+	 * canceled and 18 stacks to be removed.
+	 * When fighting the Eclipse Moon, the stacks increase the chance of a player's attack glancing off the shield of
+	 * the Eclipse Moon. Glancing attacks reduce the player's max hit by two times the flat armour of the Eclipse Moon.
+	 */
+	public static final int CURSE_OF_THE_MOONS = 9853;
+
+	/**
+	 * The amount of Doom stacks received in the Fortis Colosseum.
+	 */
+	public static final int COLOSSEUM_DOOM = 9801;
 }
diff --git a/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java b/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java
index c534c8994..f31a1d59d 100644
--- a/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java
+++ b/runelite-client/src/main/java/net/runelite/client/game/ItemMapping.java
@@ -126,7 +126,7 @@ public enum ItemMapping
 	ITEM_RUNE_CROSSBOW(RUNE_CROSSBOW, RUNE_CROSSBOW_OR),
 
 	// Godsword ornament kits
-	ITEM_ARMADYL_GODSWORD(ARMADYL_GODSWORD, ARMADYL_GODSWORD_OR),
+	ITEM_ARMADYL_GODSWORD(ARMADYL_GODSWORD, ARMADYL_GODSWORD_OR, ARMADYL_GODSWORD_DEADMAN),
 	ITEM_ARMADYL_GODSWORD_ORNAMENT_KIT(ARMADYL_GODSWORD_ORNAMENT_KIT, ARMADYL_GODSWORD_OR),
 	ITEM_BANDOS_GODSWORD(BANDOS_GODSWORD, BANDOS_GODSWORD_OR),
 	ITEM_BANDOS_GODSWORD_ORNAMENT_KIT(BANDOS_GODSWORD_ORNAMENT_KIT, BANDOS_GODSWORD_OR),
@@ -286,7 +286,7 @@ public enum ItemMapping
 	ITEM_INFINITY_HAT_DARK_COLOUR_KIT(DARK_INFINITY_COLOUR_KIT, DARK_INFINITY_HAT),
 
 	// Miscellaneous ornament kits
-	ITEM_DARK_BOW(DARK_BOW, DARK_BOW_12765, DARK_BOW_12766, DARK_BOW_12767, DARK_BOW_12768, DARK_BOW_20408, DARK_BOW_BH),
+	ITEM_DARK_BOW(DARK_BOW, DARK_BOW_12765, DARK_BOW_12766, DARK_BOW_12767, DARK_BOW_12768, DARK_BOW_20408, DARK_BOW_BH, DARK_BOW_DEADMAN),
 	ITEM_ODIUM_WARD(ODIUM_WARD, ODIUM_WARD_12807),
 	ITEM_MALEDICTION_WARD(MALEDICTION_WARD, MALEDICTION_WARD_12806),
 	ITEM_STEAM_BATTLESTAFF(STEAM_BATTLESTAFF, STEAM_BATTLESTAFF_12795),
@@ -294,6 +294,7 @@ public enum ItemMapping
 	ITEM_TZHAARKETOM(TZHAARKETOM, TZHAARKETOM_T),
 	ITEM_TZHAARKETOM_ORNAMENT_KIT(TZHAARKETOM_ORNAMENT_KIT, TZHAARKETOM_T),
 	ITEM_DRAGON_HUNTER_CROSSBOW(DRAGON_HUNTER_CROSSBOW, DRAGON_HUNTER_CROSSBOW_B, DRAGON_HUNTER_CROSSBOW_T),
+	ITEM_VOIDWAKER(VOIDWAKER, VOIDWAKER_DEADMAN),
 
 	// Slayer helm/black mask
 	ITEM_BLACK_MASK(BLACK_MASK, true, 1L, BLACK_MASK, SLAYER_HELMET),
@@ -308,8 +309,8 @@ public enum ItemMapping
 	ITEM_TWISTED_HORNS(TWISTED_HORNS, TWISTED_SLAYER_HELMET, TWISTED_SLAYER_HELMET_I, TWISTED_SLAYER_HELMET_I_25191, TWISTED_SLAYER_HELMET_I_26681),
 	ITEM_ELDRITCH_ORB(ELDRITCH_ORB, ELDRITCH_NIGHTMARE_STAFF),
 	ITEM_HARMONISED_ORB(HARMONISED_ORB, HARMONISED_NIGHTMARE_STAFF),
-	ITEM_VOLATILE_ORB(VOLATILE_ORB, VOLATILE_NIGHTMARE_STAFF),
-	ITEM_NIGHTMARE_STAFF(NIGHTMARE_STAFF, ELDRITCH_NIGHTMARE_STAFF, HARMONISED_NIGHTMARE_STAFF, VOLATILE_NIGHTMARE_STAFF),
+	ITEM_VOLATILE_ORB(VOLATILE_ORB, VOLATILE_NIGHTMARE_STAFF, VOLATILE_NIGHTMARE_STAFF_DEADMAN),
+	ITEM_NIGHTMARE_STAFF(NIGHTMARE_STAFF, ELDRITCH_NIGHTMARE_STAFF, HARMONISED_NIGHTMARE_STAFF, VOLATILE_NIGHTMARE_STAFF, VOLATILE_NIGHTMARE_STAFF_DEADMAN),
 	ITEM_GHARZI_RAPIER(GHRAZI_RAPIER, HOLY_GHRAZI_RAPIER),
 	ITEM_MASTER_SCROLL_BOOK(MASTER_SCROLL_BOOK_EMPTY, MASTER_SCROLL_BOOK),
 	ITEM_ARCANE_SIGIL(ARCANE_SIGIL, ELIDINIS_WARD_F, ELIDINIS_WARD_OR),
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 3cf59bdc1..8cb8c8cf3 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
@@ -493,7 +493,8 @@ public class SlayerPlugin extends Plugin
 	@Subscribe
 	public void onMenuOptionClicked(MenuOptionClicked menuOptionClicked)
 	{
-		if (menuOptionClicked.getMenuAction() == MenuAction.CC_OP && menuOptionClicked.getMenuOption().equals("Check"))
+		if ((menuOptionClicked.getMenuAction() == MenuAction.CC_OP || menuOptionClicked.getMenuAction() == MenuAction.CC_OP_LOW_PRIORITY)
+			&& menuOptionClicked.getMenuOption().equals("Check"))
 		{
 			Widget w = client.getWidget(menuOptionClicked.getParam1());
 			if (w == null)
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java
index 2193e1e7b..bdba82747 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java
@@ -38,24 +38,24 @@ enum Task
 {
 	//<editor-fold desc="Enums">
 	ABERRANT_SPECTRES("Aberrant spectres", ItemID.ABERRANT_SPECTRE, "Spectre"),
-	ABYSSAL_DEMONS("Abyssal demons", ItemID.ABYSSAL_DEMON),
+	ABYSSAL_DEMONS("Abyssal demons", ItemID.ABYSSAL_DEMON, "Abyssal Sire"),
 	ABYSSAL_SIRE("The Abyssal Sire", ItemID.ABYSSAL_ORPHAN),
 	ADAMANT_DRAGONS("Adamant dragons", ItemID.ADAMANT_DRAGON_MASK),
 	ALCHEMICAL_HYDRA("The Alchemical Hydra", ItemID.IKKLE_HYDRA),
 	ANKOU("Ankou", ItemID.ANKOU_MASK),
-	AVIANSIES("Aviansies", ItemID.ENSOULED_AVIANSIE_HEAD),
+	AVIANSIES("Aviansies", ItemID.ENSOULED_AVIANSIE_HEAD, "Kree'arra", "Flight Kilisa", "Flockleader Geerin", "Wingman Skree"),
 	BANDITS("Bandits", ItemID.BANDIT, "Bandit", "Black Heather", "Donny the Lad", "Speedy Keith"),
 	BANSHEES("Banshees", ItemID.BANSHEE),
 	BARROWS_BROTHERS("Barrows Brothers", ItemID.KARILS_COIF),
 	BASILISKS("Basilisks", ItemID.BASILISK),
 	BATS("Bats", ItemID.GIRAL_BAT_2, "Death wing"),
-	BEARS("Bears", ItemID.ENSOULED_BEAR_HEAD),
-	BIRDS("Birds", ItemID.FEATHER, "Chicken", "Rooster", "Terrorbird", "Seagull", "Vulture", "Duck", "Penguin"),
-	BLACK_DEMONS("Black demons", ItemID.BLACK_DEMON_MASK),
-	BLACK_DRAGONS("Black dragons", ItemID.BLACK_DRAGON_MASK, "Baby black dragon"),
+	BEARS("Bears", ItemID.ENSOULED_BEAR_HEAD, "Callisto", "Artio"),
+	BIRDS("Birds", ItemID.FEATHER, "Chicken", "Rooster", "Terrorbird", "Seagull", "Vulture", "Duck", "Penguin", "Baby Roc"),
+	BLACK_DEMONS("Black demons", ItemID.BLACK_DEMON_MASK, "Demonic gorilla", "Balfrug Kreeyath", "Skotizo", "Porazdir"),
+	BLACK_DRAGONS("Black dragons", ItemID.BLACK_DRAGON_MASK),
 	BLACK_KNIGHTS("Black Knights", ItemID.BLACK_FULL_HELM, "Black Knight"),
 	BLOODVELD("Bloodveld", ItemID.BLOODVELD),
-	BLUE_DRAGONS("Blue dragons", ItemID.BLUE_DRAGON_MASK, "Baby blue dragon"),
+	BLUE_DRAGONS("Blue dragons", ItemID.BLUE_DRAGON_MASK, "Vorkath"),
 	BRINE_RATS("Brine rats", ItemID.BRINE_RAT),
 	BRONZE_DRAGONS("Bronze dragons", ItemID.BRONZE_DRAGON_MASK),
 	CALLISTO("Callisto", ItemID.CALLISTO_CUB),
@@ -63,46 +63,46 @@ enum Task
 	CAVE_BUGS("Cave bugs", ItemID.SWAMP_CAVE_BUG),
 	CAVE_CRAWLERS("Cave crawlers", ItemID.CAVE_CRAWLER, "Chasm crawler"),
 	CAVE_HORRORS("Cave horrors", ItemID.CAVE_HORROR, "Cave abomination"),
-	CAVE_KRAKEN("Cave kraken", ItemID.CAVE_KRAKEN),
+	CAVE_KRAKEN("Cave kraken", ItemID.CAVE_KRAKEN, "Kraken"),
 	CAVE_SLIMES("Cave slimes", ItemID.SWAMP_CAVE_SLIME),
 	CERBERUS("Cerberus", ItemID.HELLPUPPY),
-	CHAOS_DRUIDS("Chaos druids", ItemID.ELDER_CHAOS_HOOD, "Elder Chaos druid", "Chaos druid"),
+	CHAOS_DRUIDS("Chaos druids", ItemID.ELDER_CHAOS_HOOD),
 	CHAOS_ELEMENTAL("The Chaos Elemental", ItemID.PET_CHAOS_ELEMENTAL),
 	CHAOS_FANATIC("The Chaos Fanatic", ItemID.ANCIENT_STAFF),
 	COCKATRICE("Cockatrice", ItemID.COCKATRICE, "Cockathrice"),
 	COWS("Cows", ItemID.COW_MASK),
 	CRAWLING_HANDS("Crawling hands", ItemID.CRAWLING_HAND, "Crushing hand"),
-	CRAZY_ARCHAEOLOGIST("Crazy Archaeologists", ItemID.FEDORA, "Crazy Archaeologist"),
+	CRAZY_ARCHAEOLOGIST("Crazy Archaeologists", ItemID.FEDORA),
 	CROCODILES("Crocodiles", ItemID.SWAMP_LIZARD),
 	DAGANNOTH("Dagannoth", ItemID.DAGANNOTH),
 	DAGANNOTH_KINGS("Dagannoth Kings", ItemID.PET_DAGANNOTH_PRIME),
 	DARK_BEASTS("Dark beasts", ItemID.DARK_BEAST, "Night beast"),
 	DARK_WARRIORS("Dark warriors", ItemID.BLACK_MED_HELM, "Dark warrior"),
 	DERANGED_ARCHAEOLOGIST("Deranged Archaeologist", ItemID.ARCHAEOLOGISTS_DIARY),
-	DOGS("Dogs", ItemID.GUARD_DOG, "Jackal"),
+	DOGS("Dogs", ItemID.GUARD_DOG, "Jackal", "Temple Guardian"),
 	DRAKES("Drakes", ItemID.DRAKE),
 	DUKE_SUCELLUS("Duke Sucellus", ItemID.BARON),
 	DUST_DEVILS("Dust devils", ItemID.DUST_DEVIL, "Choke devil"),
 	DWARVES("Dwarves", ItemID.DWARVEN_HELMET, "Dwarf", "Black Guard"),
 	EARTH_WARRIORS("Earth warriors", ItemID.BRONZE_FULL_HELM_T),
 	ELVES("Elves", ItemID.ELF, "Elf", "Iorwerth Warrior", "Iorwerth Archer"),
-	ENTS("Ents", ItemID.NICE_TREE, "Ent"),
+	ENTS("Ents", ItemID.NICE_TREE),
 	FEVER_SPIDERS("Fever spiders", ItemID.FEVER_SPIDER),
 	FIRE_GIANTS("Fire giants", ItemID.FIRE_BATTLESTAFF),
 	FLESH_CRAWLERS("Fleshcrawlers", ItemID.ENSOULED_SCORPION_HEAD, "Flesh crawler"),
 	FOSSIL_ISLAND_WYVERNS("Fossil island wyverns", ItemID.FOSSIL_ISLAND_WYVERN, "Ancient wyvern", "Long-tailed wyvern", "Spitting wyvern", "Taloned wyvern"),
-	GARGOYLES("Gargoyles", ItemID.GARGOYLE, 9, ItemID.ROCK_HAMMER),
+	GARGOYLES("Gargoyles", ItemID.GARGOYLE, 9, ItemID.ROCK_HAMMER, "Dusk", "Dawn"),
 	GENERAL_GRAARDOR("General Graardor", ItemID.PET_GENERAL_GRAARDOR),
 	GHOSTS("Ghosts", ItemID.GHOSTSPEAK_AMULET, "Death wing", "Tortured soul", "Forgotten Soul", "Revenant"),
 	GHOULS("Ghouls", ItemID.ZOMBIE_HEAD),
 	GIANT_MOLE("The Giant Mole", ItemID.BABY_MOLE),
-	GOBLINS("Goblins", ItemID.ENSOULED_GOBLIN_HEAD),
-	GREATER_DEMONS("Greater demons", ItemID.GREATER_DEMON_MASK),
-	GREEN_DRAGONS("Green dragons", ItemID.GREEN_DRAGON_MASK, "Baby green dragon", "Elvarg"),
+	GOBLINS("Goblins", ItemID.ENSOULED_GOBLIN_HEAD, "Sergeant Strongstack", "Sergeant Grimspike", "Sergeant Steelwill"),
+	GREATER_DEMONS("Greater demons", ItemID.GREATER_DEMON_MASK, "K'ril Tsutsaroth", "Tstanon Karlak", "Skotizo", "Tormented Demon"),
+	GREEN_DRAGONS("Green dragons", ItemID.GREEN_DRAGON_MASK, "Elvarg"),
 	GROTESQUE_GUARDIANS("The Grotesque Guardians", ItemID.MIDNIGHT, 0, ItemID.ROCK_HAMMER, "Dusk", "Dawn"),
 	HARPIE_BUG_SWARMS("Harpie bug swarms", ItemID.SWARM),
-	HELLHOUNDS("Hellhounds", ItemID.HELLHOUND),
-	HILL_GIANTS("Hill giants", ItemID.ENSOULED_GIANT_HEAD, "Cyclops"),
+	HELLHOUNDS("Hellhounds", ItemID.HELLHOUND, "Cerberus"),
+	HILL_GIANTS("Hill giants", ItemID.ENSOULED_GIANT_HEAD, "Cyclops", "Reanimated giant", "Obor"),
 	HOBGOBLINS("Hobgoblins", ItemID.HOBGOBLIN_GUARD),
 	HYDRAS("Hydras", ItemID.HYDRA),
 	ICEFIENDS("Icefiends", ItemID.ICE_DIAMOND),
@@ -122,43 +122,43 @@ enum Task
 	KRIL_TSUTSAROTH("K'ril Tsutsaroth", ItemID.PET_KRIL_TSUTSAROTH),
 	KURASK("Kurask", ItemID.KURASK),
 	LAVA_DRAGONS("Lava Dragons", ItemID.LAVA_SCALE, "Lava dragon"),
-	LESSER_DEMONS("Lesser demons", ItemID.LESSER_DEMON_MASK),
+	LESSER_DEMONS("Lesser demons", ItemID.LESSER_DEMON_MASK, "Zakl'n Gritch"),
 	LESSER_NAGUA("Lesser Nagua", ItemID.LESSER_NAGUA, "Sulphur Nagua"),
 	LIZARDMEN("Lizardmen", ItemID.LIZARDMAN_FANG, "Lizardman"),
-	LIZARDS("Lizards", ItemID.DESERT_LIZARD, "Desert lizard", "Sulphur lizard", "Small lizard", "Lizard"),
-	MAGIC_AXES("Magic axes", ItemID.IRON_BATTLEAXE, "Magic axe"),
-	MAMMOTHS("Mammoths", ItemID.ATTACKER_HORN, "Mammoth"),
+	LIZARDS("Lizards", ItemID.DESERT_LIZARD),
+	MAGIC_AXES("Magic axes", ItemID.IRON_BATTLEAXE),
+	MAMMOTHS("Mammoths", ItemID.ATTACKER_HORN),
 	MINIONS_OF_SCABARAS("Minions of scabaras", ItemID.GOLDEN_SCARAB, "Scarab swarm", "Locust rider", "Scarab mage"),
 	MINOTAURS("Minotaurs", ItemID.ENSOULED_MINOTAUR_HEAD),
 	MITHRIL_DRAGONS("Mithril dragons", ItemID.MITHRIL_DRAGON_MASK),
 	MOGRES("Mogres", ItemID.MOGRE),
 	MOLANISKS("Molanisks", ItemID.MOLANISK),
-	MONKEYS("Monkeys", ItemID.ENSOULED_MONKEY_HEAD, "Tortured gorilla"),
-	MOSS_GIANTS("Moss giants", ItemID.MOSSY_KEY),
+	MONKEYS("Monkeys", ItemID.ENSOULED_MONKEY_HEAD, "Tortured gorilla", "Demonic gorilla", "Padulah"),
+	MOSS_GIANTS("Moss giants", ItemID.MOSSY_KEY, "Bryophyta"),
 	MUTATED_ZYGOMITES("Mutated zygomites", ItemID.MUTATED_ZYGOMITE, 7, ItemID.FUNGICIDE_SPRAY_0, "Zygomite", "Fungi"),
 	NECHRYAEL("Nechryael", ItemID.NECHRYAEL, "Nechryarch"),
-	OGRES("Ogres", ItemID.ENSOULED_OGRE_HEAD, "Mogre", "Ogress", "Skogre", "Zogre"),
+	OGRES("Ogres", ItemID.ENSOULED_OGRE_HEAD, "Enclave guard", "Mogre", "Ogress", "Skogre", "Zogre"),
 	OTHERWORLDLY_BEING("Otherworldly beings", ItemID.GHOSTLY_HOOD),
 	PHANTOM_MUSPAH("The Phantom Muspah", ItemID.MUPHIN),
 	PIRATES("Pirates", ItemID.PIRATE_HAT, "Pirate"),
 	PYREFIENDS("Pyrefiends", ItemID.PYREFIEND, "Flaming pyrelord"),
 	RATS("Rats", ItemID.RATS_TAIL),
-	RED_DRAGONS("Red dragons", ItemID.BABY_RED_DRAGON, "Baby red dragon"),
-	REVENANTS("Revenants", ItemID.BRACELET_OF_ETHEREUM, "Revenant imp", "Revenant goblin", "Revenant pyrefiend", "Revenant hobgoblin", "Revenant cyclops", "Revenant hellhound", "Revenant demon", "Revenant ork", "Revenant dark beast", "Revenant knight", "Revenant dragon"),
+	RED_DRAGONS("Red dragons", ItemID.BABY_RED_DRAGON),
+	REVENANTS("Revenants", ItemID.BRACELET_OF_ETHEREUM),
 	ROCKSLUGS("Rockslugs", ItemID.ROCKSLUG, 4, ItemID.BAG_OF_SALT),
-	ROGUES("Rogues", ItemID.ROGUE_MASK, "Rogue"),
+	ROGUES("Rogues", ItemID.ROGUE_MASK),
 	RUNE_DRAGONS("Rune dragons", ItemID.RUNE_DRAGON_MASK),
 	SARACHNIS("Sarachnis", ItemID.SRARACHA),
 	SCORPIA("Scorpia", ItemID.SCORPIAS_OFFSPRING),
-	SCORPIONS("Scorpions", ItemID.ENSOULED_SCORPION_HEAD),
+	SCORPIONS("Scorpions", ItemID.ENSOULED_SCORPION_HEAD, "Scorpia", "Lobstrosity"),
 	SEA_SNAKES("Sea snakes", ItemID.SNAKE_CORPSE),
-	SHADES("Shades", ItemID.SHADE_ROBE_TOP, "Loar Shadow", "Loar Shade", "Phrin Shadow", "Phrin Shade", "Riyl Shadow", "Riyl Shade", "Asyn Shadow", "Asyn Shade", "Fiyr Shadow", "Fiyr Shade"),
+	SHADES("Shades", ItemID.SHADE_ROBE_TOP, "Loar", "Phrin", "Riyl", "Asyn", "Fiyr", "Urium"),
 	SHADOW_WARRIORS("Shadow warriors", ItemID.BLACK_FULL_HELM),
 	SKELETAL_WYVERNS("Skeletal wyverns", ItemID.SKELETAL_WYVERN),
-	SKELETONS("Skeletons", ItemID.SKELETON_GUARD),
+	SKELETONS("Skeletons", ItemID.SKELETON_GUARD, "Vet'ion", "Calvar'ion"),
 	SMOKE_DEVILS("Smoke devils", ItemID.SMOKE_DEVIL),
 	SOURHOGS("Sourhogs", ItemID.SOURHOG_FOOT),
-	SPIDERS("Spiders", ItemID.HUGE_SPIDER),
+	SPIDERS("Spiders", ItemID.HUGE_SPIDER, "Kalrag", "Sarachnis", "Venenatis", "Spindel"),
 	SPIRITUAL_CREATURES("Spiritual creatures", ItemID.DRAGON_BOOTS, "Spiritual ranger", "Spiritual mage", "Spiritual warrior"),
 	STEEL_DRAGONS("Steel dragons", ItemID.STEEL_DRAGON),
 	SUQAHS("Suqahs", ItemID.SUQAH_TOOTH),
@@ -166,10 +166,10 @@ enum Task
 	THE_LEVIATHAN("The Leviathan", ItemID.LILVIATHAN),
 	THE_WHISPERER("The Whisperer", ItemID.WISP),
 	THERMONUCLEAR_SMOKE_DEVIL("The Thermonuclear Smoke Devil", ItemID.PET_SMOKE_DEVIL),
-	TROLLS("Trolls", ItemID.TROLL_GUARD, "Dad", "Arrg"),
+	TROLLS("Trolls", ItemID.TROLL_GUARD, "Dad", "Arrg", "Stick", "Kraka", "Pee Hat", "Rock", "Twig", "Berry"),
 	TUROTH("Turoth", ItemID.TUROTH),
-	TZHAAR("Tzhaar", ItemID.ENSOULED_TZHAAR_HEAD),
-	VAMPYRES("Vampyres", ItemID.STAKE, "Vyrewatch", "Vampire"),
+	TZHAAR("Tzhaar", ItemID.ENSOULED_TZHAAR_HEAD, "TzTok-Jad", "TzKal-Zuk"),
+	VAMPYRES("Vampyres", ItemID.STAKE, "Vyrewatch"),
 	VARDORVIS("Vardorvis", ItemID.BUTCH),
 	VENENATIS("Venenatis", ItemID.VENENATIS_SPIDERLING),
 	VETION("Vet'ion", ItemID.VETION_JR),
@@ -181,7 +181,7 @@ enum Task
 	WOLVES("Wolves", ItemID.GREY_WOLF_FUR, "Wolf"),
 	WYRMS("Wyrms", ItemID.WYRM),
 	ZILYANA("Commander Zilyana", ItemID.PET_ZILYANA),
-	ZOMBIES("Zombies", ItemID.ZOMBIE_HEAD, "Undead"),
+	ZOMBIES("Zombies", ItemID.ZOMBIE_HEAD, "Undead", "Vorkath", "Zogre"),
 	ZUK("TzKal-Zuk", ItemID.TZREKZUK),
 	ZULRAH("Zulrah", ItemID.PET_SNAKELING);
 	//</editor-fold>
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/GameIndicator.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/GameIndicator.java
deleted file mode 100644
index 55151e340..000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/timers/GameIndicator.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (c) 2018, Tyler <https://github.com/tylerthardy>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- *    list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-package net.runelite.client.plugins.timers;
-
-import java.awt.Color;
-import lombok.AccessLevel;
-import lombok.Getter;
-import net.runelite.api.SpriteID;
-
-@Getter(AccessLevel.PACKAGE)
-enum GameIndicator
-{
-	VENGEANCE_ACTIVE(SpriteID.SPELL_VENGEANCE_OTHER, GameTimerImageType.SPRITE, "Vengeance active");
-
-	private final String description;
-	private String text;
-	private Color textColor;
-	private final int imageId;
-	private final GameTimerImageType imageType;
-
-	GameIndicator(int imageId, GameTimerImageType idType, String description, String text, Color textColor)
-	{
-		this.imageId = imageId;
-		this.imageType = idType;
-		this.description = description;
-		this.text = text;
-		this.textColor = textColor;
-	}
-
-	GameIndicator(int imageId, GameTimerImageType idType, String description)
-	{
-		this(imageId, idType, description, "", null);
-	}
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/IndicatorIndicator.java b/runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/BuffCounter.java
similarity index 66%
rename from runelite-client/src/main/java/net/runelite/client/plugins/timers/IndicatorIndicator.java
rename to runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/BuffCounter.java
index 03990ce5e..9204f4d0a 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/timers/IndicatorIndicator.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/BuffCounter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Tyler <https://github.com/tylerthardy>
+ * Copyright (c) 2024, YvesW <https://github.com/YvesW>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -22,35 +22,38 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-package net.runelite.client.plugins.timers;
+package net.runelite.client.plugins.timersandbuffs;
 
 import java.awt.Color;
+import lombok.AccessLevel;
 import lombok.Getter;
-import net.runelite.client.plugins.Plugin;
-import net.runelite.client.ui.overlay.infobox.InfoBox;
-import net.runelite.client.ui.overlay.infobox.InfoBoxPriority;
+import net.runelite.client.ui.overlay.infobox.Counter;
 
-public class IndicatorIndicator extends InfoBox
+@Getter(AccessLevel.PACKAGE)
+class BuffCounter extends Counter
 {
-	@Getter
-	private final GameIndicator indicator;
+	private final TimersAndBuffsPlugin plugin;
+	private final GameCounter gameCounter;
 
-	IndicatorIndicator(GameIndicator indicator, Plugin plugin)
+	BuffCounter(
+		TimersAndBuffsPlugin plugin,
+		GameCounter gameCounter,
+		int count)
 	{
-		super(null, plugin);
-		this.indicator = indicator;
-		setPriority(InfoBoxPriority.MED);
+		super(null, plugin, count);
+		this.plugin = plugin;
+		this.gameCounter = gameCounter;
 	}
 
 	@Override
 	public String getText()
 	{
-		return indicator.getText();
+		return gameCounter.isShouldDisplayCount() ? Integer.toString(getCount()) : "";
 	}
 
 	@Override
 	public Color getTextColor()
 	{
-		return indicator.getTextColor();
+		return gameCounter.getColorBoundaryType().shouldRecolor(getCount(), gameCounter.getBoundary()) ? gameCounter.getColor() : Color.WHITE;
 	}
-}
+}
\ No newline at end of file
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/ElapsedTimer.java b/runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/ElapsedTimer.java
similarity index 94%
rename from runelite-client/src/main/java/net/runelite/client/plugins/timers/ElapsedTimer.java
rename to runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/ElapsedTimer.java
index b9571ae11..0482e9d61 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/timers/ElapsedTimer.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/ElapsedTimer.java
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-package net.runelite.client.plugins.timers;
+package net.runelite.client.plugins.timersandbuffs;
 
 import lombok.Getter;
 import net.runelite.client.ui.overlay.infobox.InfoBox;
@@ -39,7 +39,7 @@ class ElapsedTimer extends InfoBox
 	private final Instant lastTime;
 
 	// Creates a timer that counts up if lastTime is null, or a paused timer if lastTime is defined
-	ElapsedTimer(BufferedImage image, TimersPlugin plugin, Instant startTime, Instant lastTime)
+	ElapsedTimer(BufferedImage image, TimersAndBuffsPlugin plugin, Instant startTime, Instant lastTime)
 	{
 		super(image, plugin);
 		this.startTime = startTime;
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/GameCounter.java b/runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/GameCounter.java
new file mode 100644
index 000000000..f49302607
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/GameCounter.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2024, YvesW <https://github.com/YvesW>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.client.plugins.timersandbuffs;
+
+import java.awt.Color;
+import java.util.function.BiPredicate;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import net.runelite.api.ItemID;
+import net.runelite.api.SpriteID;
+
+@Getter(AccessLevel.PACKAGE)
+@AllArgsConstructor
+enum GameCounter
+{
+	COLOSSEUM_DOOM(SpriteID.COLOSSEUM_DOOM, GameTimerImageType.SPRITE, "Doom"),
+	CURSE_OF_THE_MOONS_BLUE(ItemID.BLUE_MOON_HELM, GameTimerImageType.ITEM, "Curse of the Moons (Blue Moon)", ColorBoundaryType.GREATER_THAN_EQUAL_TO, 18, Color.RED),
+	CURSE_OF_THE_MOONS_ECLIPSE(ItemID.ECLIPSE_MOON_HELM, GameTimerImageType.ITEM, "Curse of the Moons (Eclipse Moon)"),
+	VENGEANCE_ACTIVE(SpriteID.SPELL_VENGEANCE_OTHER, GameTimerImageType.SPRITE, "Vengeance active", false),
+	;
+
+	private final int imageId;
+	private final GameTimerImageType imageType;
+	private final String description;
+	private final ColorBoundaryType colorBoundaryType;
+	private final int boundary;
+	private final Color color;
+	private final boolean shouldDisplayCount;
+
+	GameCounter(int imageId, GameTimerImageType idType, String description, ColorBoundaryType colorBoundaryType, int boundary, Color color)
+	{
+		this(imageId, idType, description, colorBoundaryType, boundary, color, true);
+	}
+
+	GameCounter(int imageId, GameTimerImageType idType, String description, boolean shouldDisplayCount)
+	{
+		this(imageId, idType, description, ColorBoundaryType.NO_BOUNDARY, 0, Color.WHITE, shouldDisplayCount);
+	}
+
+	GameCounter(int imageId, GameTimerImageType idType, String description)
+	{
+		this(imageId, idType, description, ColorBoundaryType.NO_BOUNDARY, 0, Color.WHITE, true);
+	}
+
+	@AllArgsConstructor
+	enum ColorBoundaryType
+	{
+		GREATER_THAN_EQUAL_TO((count, boundary) -> count >= boundary),
+		LESS_THAN_EQUAL_TO((count, boundary) -> count <= boundary),
+		NO_BOUNDARY((count, boundary) -> false);
+
+		final BiPredicate<Integer, Integer> shouldRecolorPredicate;
+
+		boolean shouldRecolor(int count, int boundary)
+		{
+			return shouldRecolorPredicate.test(count, boundary);
+		}
+	}
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/GameTimer.java b/runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/GameTimer.java
similarity index 98%
rename from runelite-client/src/main/java/net/runelite/client/plugins/timers/GameTimer.java
rename to runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/GameTimer.java
index 02e9c5a47..377e02ec8 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/timers/GameTimer.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/GameTimer.java
@@ -24,7 +24,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-package net.runelite.client.plugins.timers;
+package net.runelite.client.plugins.timersandbuffs;
 
 import java.time.Duration;
 import java.time.temporal.ChronoUnit;
@@ -74,6 +74,7 @@ enum GameTimer
 	DIVINE_MAGIC(ItemID.DIVINE_MAGIC_POTION4, GameTimerImageType.ITEM, "Divine Magic", false),
 	DIVINE_BASTION(ItemID.DIVINE_BASTION_POTION4, GameTimerImageType.ITEM, "Divine Bastion", false),
 	DIVINE_BATTLEMAGE(ItemID.DIVINE_BATTLEMAGE_POTION4, GameTimerImageType.ITEM, "Divine Battlemage", false),
+	MOONLIGHT_POTION(ItemID.MOONLIGHT_POTION4, GameTimerImageType.ITEM, "Moonlight potion", false),
 	ANTIPOISON(ItemID.ANTIPOISON4, GameTimerImageType.ITEM, "Antipoison", false),
 	ANTIVENOM(ItemID.ANTIVENOM4, GameTimerImageType.ITEM, "Anti-venom", false),
 	TELEBLOCK(SpriteID.SPELL_TELE_BLOCK, GameTimerImageType.SPRITE, "Teleblock", false),
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/GameTimerImageType.java b/runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/GameTimerImageType.java
similarity index 96%
rename from runelite-client/src/main/java/net/runelite/client/plugins/timers/GameTimerImageType.java
rename to runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/GameTimerImageType.java
index 1d51d05e3..9c5c3766c 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/timers/GameTimerImageType.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/GameTimerImageType.java
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-package net.runelite.client.plugins.timers;
+package net.runelite.client.plugins.timersandbuffs;
 
 enum GameTimerImageType
 {
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimerTimer.java b/runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/TimerTimer.java
similarity index 97%
rename from runelite-client/src/main/java/net/runelite/client/plugins/timers/TimerTimer.java
rename to runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/TimerTimer.java
index 8dfb1ce36..bc41a6d0d 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimerTimer.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/TimerTimer.java
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-package net.runelite.client.plugins.timers;
+package net.runelite.client.plugins.timersandbuffs;
 
 import java.time.Duration;
 import java.time.temporal.ChronoUnit;
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/TimersAndBuffsConfig.java
similarity index 72%
rename from runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersConfig.java
rename to runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/TimersAndBuffsConfig.java
index daf383090..2502e5891 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersConfig.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/TimersAndBuffsConfig.java
@@ -22,22 +22,52 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-package net.runelite.client.plugins.timers;
+package net.runelite.client.plugins.timersandbuffs;
 
 import net.runelite.client.config.Config;
 import net.runelite.client.config.ConfigGroup;
 import net.runelite.client.config.ConfigItem;
+import net.runelite.client.config.ConfigSection;
 import java.time.Instant;
 
-@ConfigGroup(TimersConfig.GROUP)
-public interface TimersConfig extends Config
+@ConfigGroup(TimersAndBuffsConfig.GROUP)
+public interface TimersAndBuffsConfig extends Config
 {
 	String GROUP = "timers";
 
+	@ConfigSection(
+		name = "Bosses",
+		description = "Timers and buffs related to bosses",
+		position = 0
+	)
+	String bossesSection = "bossesSection";
+
+	@ConfigSection(
+		name = "Potions & Consumables",
+		description = "Timers and buffs related to potions/consumables/boosts",
+		position = 1
+	)
+	String consumablesSection = "consumablesSection";
+
+	@ConfigSection(
+		name = "Spells",
+		description = "Timers and buffs related to spells you cast",
+		position = 2
+	)
+	String spellsSection = "spellsSection";
+
+	@ConfigSection(
+		name = "Miscellaneous",
+		description = "Timers and buffs related to miscellaneous items or activities",
+		position = 3
+	)
+	String miscellaneousSection = "miscellaneousSection";
+
 	@ConfigItem(
 		keyName = "showHomeMinigameTeleports",
 		name = "Teleport cooldown timers",
-		description = "Configures whether timers for home and minigame teleport cooldowns are displayed"
+		description = "Configures whether timers for home and minigame teleport cooldowns are displayed",
+		section = miscellaneousSection
 	)
 	default boolean showHomeMinigameTeleports()
 	{
@@ -47,7 +77,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showAntipoison",
 		name = "Antipoison/Venom timers",
-		description = "Configures whether timers for poison and venom protection are displayed"
+		description = "Configures whether timers for poison and venom protection are displayed",
+		section = consumablesSection
 	)
 	default boolean showAntiPoison()
 	{
@@ -57,7 +88,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showAntiFire",
 		name = "Antifire timer",
-		description = "Configures whether antifire timer is displayed"
+		description = "Configures whether antifire timer is displayed",
+		section = consumablesSection
 	)
 	default boolean showAntiFire()
 	{
@@ -67,7 +99,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showStamina",
 		name = "Stamina timer",
-		description = "Configures whether stamina timer is displayed"
+		description = "Configures whether stamina timer is displayed",
+		section = consumablesSection
 	)
 	default boolean showStamina()
 	{
@@ -77,7 +110,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showOverload",
 		name = "Overload timer",
-		description = "Configures whether overload timer is displayed"
+		description = "Configures whether overload timer is displayed",
+		section = consumablesSection
 	)
 	default boolean showOverload()
 	{
@@ -87,7 +121,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showLiquidAdrenaline",
 		name = "Liquid adrenaline timer",
-		description = "Configures whether liquid adrenaline timer is displayed"
+		description = "Configures whether liquid adrenaline timer is displayed",
+		section = consumablesSection
 	)
 	default boolean showLiquidAdrenaline()
 	{
@@ -97,7 +132,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showMenaphiteRemedy",
 		name = "Menaphite remedy timer",
-		description = "Configures whether Menaphite remedy timer is displayed"
+		description = "Configures whether Menaphite remedy timer is displayed",
+		section = consumablesSection
 	)
 	default boolean showMenaphiteRemedy()
 	{
@@ -107,7 +143,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showSilkDressing",
 		name = "Silk dressing timer",
-		description = "Configures whether silk dressing timer is displayed"
+		description = "Configures whether silk dressing timer is displayed",
+		section = consumablesSection
 	)
 	default boolean showSilkDressing()
 	{
@@ -117,7 +154,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showBlessedCrystalScarab",
 		name = "Blessed crystal scarab timer",
-		description = "Configures whether blessed crystal scarab timer is displayed"
+		description = "Configures whether blessed crystal scarab timer is displayed",
+		section = consumablesSection
 	)
 	default boolean showBlessedCrystalScarab()
 	{
@@ -127,7 +165,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showPrayerEnhance",
 		name = "Prayer enhance timer",
-		description = "Configures whether prayer enhance timer is displayed"
+		description = "Configures whether prayer enhance timer is displayed",
+		section = consumablesSection
 	)
 	default boolean showPrayerEnhance()
 	{
@@ -137,7 +176,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showDivine",
 		name = "Divine potion timer",
-		description = "Configures whether divine potion timer is displayed"
+		description = "Configures whether divine potion timer is displayed",
+		section = consumablesSection
 	)
 	default boolean showDivine()
 	{
@@ -147,7 +187,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showCannon",
 		name = "Cannon timer",
-		description = "Configures whether cannon timer is displayed"
+		description = "Configures whether cannon timer is displayed",
+		section = miscellaneousSection
 	)
 	default boolean showCannon()
 	{
@@ -157,7 +198,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showMagicImbue",
 		name = "Magic imbue timer",
-		description = "Configures whether magic imbue timer is displayed"
+		description = "Configures whether magic imbue timer is displayed",
+		section = spellsSection
 	)
 	default boolean showMagicImbue()
 	{
@@ -167,7 +209,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showCharge",
 		name = "Charge timer",
-		description = "Configures whether to show a timer for the Charge spell"
+		description = "Configures whether to show a timer for the Charge spell",
+		section = spellsSection
 	)
 	default boolean showCharge()
 	{
@@ -177,7 +220,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showImbuedHeart",
 		name = "Imbued heart timer",
-		description = "Configures whether imbued heart timer is displayed"
+		description = "Configures whether imbued heart timer is displayed",
+		section = consumablesSection
 	)
 	default boolean showImbuedHeart()
 	{
@@ -187,7 +231,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showVengeance",
 		name = "Vengeance timer",
-		description = "Configures whether vengeance and vengeance other timer is displayed"
+		description = "Configures whether vengeance and vengeance other timer is displayed",
+		section = spellsSection
 	)
 	default boolean showVengeance()
 	{
@@ -197,7 +242,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showVengeanceActive",
 		name = "Vengeance active",
-		description = "Configures whether an indicator for vengeance being active is displayed"
+		description = "Configures whether an indicator for vengeance being active is displayed",
+		section = spellsSection
 	)
 	default boolean showVengeanceActive()
 	{
@@ -207,7 +253,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showHealGroup",
 		name = "Heal Group timer",
-		description = "Configures whether heal group timer is displayed"
+		description = "Configures whether heal group timer is displayed",
+		section = spellsSection
 	)
 	default boolean showHealGroup()
 	{
@@ -217,7 +264,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showTeleblock",
 		name = "Teleblock timer",
-		description = "Configures whether teleblock timer is displayed"
+		description = "Configures whether teleblock timer is displayed",
+		section = miscellaneousSection
 	)
 	default boolean showTeleblock()
 	{
@@ -227,7 +275,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showFreezes",
 		name = "Freeze timer",
-		description = "Configures whether freeze timer is displayed"
+		description = "Configures whether freeze timer is displayed",
+		section = miscellaneousSection
 	)
 	default boolean showFreezes()
 	{
@@ -237,7 +286,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showGodWarsAltar",
 		name = "God wars altar timer",
-		description = "Configures whether god wars altar timer is displayed"
+		description = "Configures whether god wars altar timer is displayed",
+		section = bossesSection
 	)
 	default boolean showGodWarsAltar()
 	{
@@ -247,7 +297,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showTzhaarTimers",
 		name = "Fight Caves and Inferno timers",
-		description = "Display elapsed time in the Fight Caves and Inferno"
+		description = "Display elapsed time in the Fight Caves and Inferno",
+		section = bossesSection
 	)
 	default boolean showTzhaarTimers()
 	{
@@ -287,7 +338,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showStaffOfTheDead",
 		name = "Staff of the Dead timer",
-		description = "Configures whether staff of the dead timer is displayed"
+		description = "Configures whether staff of the dead timer is displayed",
+		section = miscellaneousSection
 	)
 	default boolean showStaffOfTheDead()
 	{
@@ -297,7 +349,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showAbyssalSireStun",
 		name = "Abyssal Sire stun timer",
-		description = "Configures whether Abyssal Sire stun timer is displayed"
+		description = "Configures whether Abyssal Sire stun timer is displayed",
+		section = bossesSection
 	)
 	default boolean showAbyssalSireStun()
 	{
@@ -307,7 +360,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showDfsSpecial",
 		name = "Dragonfire Shield special timer",
-		description = "Configures whether the special attack cooldown timer for the Dragonfire Shield is displayed"
+		description = "Configures whether the special attack cooldown timer for the Dragonfire Shield is displayed",
+		section = miscellaneousSection
 	)
 	default boolean showDFSSpecial()
 	{
@@ -317,7 +371,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showArceuus",
 		name = "Arceuus spells duration",
-		description = "Whether to show Arceuus spellbook spell timers"
+		description = "Whether to show Arceuus spellbook spell timers",
+		section = spellsSection
 	)
 	default boolean showArceuus()
 	{
@@ -327,7 +382,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showArceuusCooldown",
 		name = "Arceuus spells cooldown",
-		description = "Whether to show cooldown timers for Arceuus spellbook spells"
+		description = "Whether to show cooldown timers for Arceuus spellbook spells",
+		section = spellsSection
 	)
 	default boolean showArceuusCooldown()
 	{
@@ -337,7 +393,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showPickpocketStun",
 		name = "Pickpocket stun timer",
-		description = "Configures whether pickpocket stun timer is displayed"
+		description = "Configures whether pickpocket stun timer is displayed",
+		section = miscellaneousSection
 	)
 	default boolean showPickpocketStun()
 	{
@@ -347,7 +404,8 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showFarmersAffinity",
 		name = "Farmer's Affinity",
-		description = "Configures whether Farmer's Affinity (Puro-Puro) timer is displayed"
+		description = "Configures whether Farmer's Affinity (Puro-Puro) timer is displayed",
+		section = miscellaneousSection
 	)
 	default boolean showFarmersAffinity()
 	{
@@ -357,10 +415,44 @@ public interface TimersConfig extends Config
 	@ConfigItem(
 		keyName = "showSpellbookSwap",
 		name = "Spellbook Swap timer",
-		description = "Configures whether Spellbook Swap timer is displayed"
+		description = "Configures whether Spellbook Swap timer is displayed",
+		section = spellsSection
 	)
 	default boolean showSpellbookSwap()
 	{
 		return true;
 	}
+
+	@ConfigItem(
+		keyName = "showCurseOfTheMoons",
+		name = "Curse of the Moons buff",
+		description = "Configures whether Curse of the Moons buff is displayed",
+		section = bossesSection
+	)
+	default boolean showCurseOfTheMoons()
+	{
+		return true;
+	}
+
+	@ConfigItem(
+		keyName = "showColosseumDoom",
+		name = "Colosseum Doom buff",
+		description = "Configures whether Fortis Colosseum Doom buff is displayed",
+		section = bossesSection
+	)
+	default boolean showColosseumDoom()
+	{
+		return true;
+	}
+
+	@ConfigItem(
+		keyName = "showMoonlightPotion",
+		name = "Moonlight potion timer",
+		description = "Configures whether Moonlight potion timer is displayed",
+		section = consumablesSection
+	)
+	default boolean showMoonlightPotion()
+	{
+		return true;
+	}
 }
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/TimersAndBuffsPlugin.java
similarity index 89%
rename from runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java
rename to runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/TimersAndBuffsPlugin.java
index 35d6b3fe8..4ce09737d 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/timersandbuffs/TimersAndBuffsPlugin.java
@@ -24,7 +24,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-package net.runelite.client.plugins.timers;
+package net.runelite.client.plugins.timersandbuffs;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableSet;
@@ -76,19 +76,20 @@ import net.runelite.client.game.ItemVariationMapping;
 import net.runelite.client.game.SpriteManager;
 import net.runelite.client.plugins.Plugin;
 import net.runelite.client.plugins.PluginDescriptor;
-import static net.runelite.client.plugins.timers.GameIndicator.VENGEANCE_ACTIVE;
-import static net.runelite.client.plugins.timers.GameTimer.*;
+import static net.runelite.client.plugins.timersandbuffs.GameCounter.*;
+import static net.runelite.client.plugins.timersandbuffs.GameTimer.*;
 import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
 import net.runelite.client.util.RSTimeUnit;
 import org.apache.commons.lang3.ArrayUtils;
 
 @PluginDescriptor(
-	name = "Timers",
-	description = "Show various timers in an infobox",
-	tags = {"combat", "items", "magic", "potions", "prayer", "overlay", "abyssal", "sire", "inferno", "fight", "caves", "cape", "timer", "tzhaar", "thieving", "pickpocket", "hunter", "impling", "puro"}
+	name = "Timers & Buffs",
+	configName = "TimersPlugin",
+	description = "Show various timers and buffs in an infobox",
+	tags = {"combat", "items", "magic", "potions", "prayer", "overlay", "abyssal", "sire", "inferno", "fight", "caves", "cape", "timer", "tzhaar", "thieving", "pickpocket", "hunter", "impling", "puro", "buff"}
 )
 @Slf4j
-public class TimersPlugin extends Plugin
+public class TimersAndBuffsPlugin extends Plugin
 {
 	private static final String ABYSSAL_SIRE_STUN_MESSAGE = "The Sire has been disorientated temporarily.";
 	private static final String CANNON_BASE_MESSAGE = "You place the cannon base on the ground.";
@@ -144,6 +145,9 @@ public class TimersPlugin extends Plugin
 	private WorldPoint lastPoint;
 	private ElapsedTimer tzhaarTimer;
 
+	private final Map<GameCounter, BuffCounter> varCounters = new EnumMap<>(GameCounter.class);
+	private static final int ECLIPSE_MOON_REGION_ID = 6038;
+
 	@Inject
 	private ItemManager itemManager;
 
@@ -154,15 +158,15 @@ public class TimersPlugin extends Plugin
 	private Client client;
 
 	@Inject
-	private TimersConfig config;
+	private TimersAndBuffsConfig config;
 
 	@Inject
 	private InfoBoxManager infoBoxManager;
 
 	@Provides
-	TimersConfig getConfig(ConfigManager configManager)
+	TimersAndBuffsConfig getConfig(ConfigManager configManager)
 	{
-		return configManager.getConfig(TimersConfig.class);
+		return configManager.getConfig(TimersAndBuffsConfig.class);
 	}
 
 	@Override
@@ -186,6 +190,8 @@ public class TimersPlugin extends Plugin
 		nextSuperAntifireTick = 0;
 		removeTzhaarTimer();
 		varTimers.clear();
+		infoBoxManager.removeIf(buffCounter -> buffCounter instanceof BuffCounter);
+		varCounters.clear();
 	}
 
 	@Subscribe
@@ -295,14 +301,7 @@ public class TimersPlugin extends Plugin
 
 		if (event.getVarbitId() == Varbits.VENGEANCE_ACTIVE && config.showVengeanceActive())
 		{
-			if (event.getValue() == 1)
-			{
-				createGameIndicator(VENGEANCE_ACTIVE);
-			}
-			else
-			{
-				removeGameIndicator(VENGEANCE_ACTIVE);
-			}
+			updateVarCounter(VENGEANCE_ACTIVE, event.getValue());
 		}
 
 		if (event.getVarbitId() == Varbits.DEATH_CHARGE && config.showArceuus())
@@ -477,11 +476,20 @@ public class TimersPlugin extends Plugin
 		{
 			if (client.getVarbitValue(Varbits.DIVINE_SUPER_COMBAT) > event.getValue()
 				|| client.getVarbitValue(Varbits.DIVINE_BASTION) > event.getValue()
-				|| client.getVarbitValue(Varbits.DIVINE_BATTLEMAGE) > event.getValue())
+				|| client.getVarbitValue(Varbits.DIVINE_BATTLEMAGE) > event.getValue()
+				// When drinking a dose of moonlight potion while already under its effects, desync between
+				// Varbits.MOONLIGHT_POTION and Varbits.DIVINE_SUPER_DEFENCE can occur, with the latter being 1 tick
+				// greater
+				|| client.getVarbitValue(Varbits.MOONLIGHT_POTION) >= event.getValue())
 			{
 				return;
 			}
 
+			if (client.getVarbitValue(Varbits.MOONLIGHT_POTION) < event.getValue())
+			{
+				removeVarTimer(MOONLIGHT_POTION);
+			}
+
 			updateVarTimer(DIVINE_SUPER_DEFENCE, event.getValue(), IntUnaryOperator.identity());
 		}
 
@@ -575,12 +583,43 @@ public class TimersPlugin extends Plugin
 		{
 			updateVarTimer(GOD_WARS_ALTAR, event.getValue(), i -> i * 100);
 		}
+
+		if (event.getVarbitId() == Varbits.CURSE_OF_THE_MOONS && config.showCurseOfTheMoons())
+		{
+			final int regionID = WorldPoint.fromLocal(client, client.getLocalPlayer().getLocalLocation()).getRegionID();
+			if (regionID == ECLIPSE_MOON_REGION_ID)
+			{
+				updateVarCounter(CURSE_OF_THE_MOONS_ECLIPSE, event.getValue());
+			}
+			else
+			{
+				updateVarCounter(CURSE_OF_THE_MOONS_BLUE, event.getValue());
+			}
+		}
+
+		if (event.getVarbitId() == Varbits.COLOSSEUM_DOOM && config.showColosseumDoom())
+		{
+			updateVarCounter(COLOSSEUM_DOOM, event.getValue());
+		}
+
+		if (event.getVarbitId() == Varbits.MOONLIGHT_POTION && config.showMoonlightPotion())
+		{
+			int moonlightValue = event.getValue();
+			// Increase the timer by 1 tick in case of desync due to drinking a dose of moonlight potion while already
+			// under its effects. Otherwise, the timer would be 1 tick shorter than it is meant to be.
+			if (client.getVarbitValue(Varbits.DIVINE_SUPER_DEFENCE) == moonlightValue + 1)
+			{
+				moonlightValue++;
+			}
+
+			updateVarTimer(MOONLIGHT_POTION, moonlightValue, IntUnaryOperator.identity());
+		}
 	}
 
 	@Subscribe
 	public void onConfigChanged(ConfigChanged event)
 	{
-		if (!event.getGroup().equals(TimersConfig.GROUP))
+		if (!event.getGroup().equals(TimersAndBuffsConfig.GROUP))
 		{
 			return;
 		}
@@ -674,7 +713,7 @@ public class TimersPlugin extends Plugin
 
 		if (!config.showVengeanceActive())
 		{
-			removeGameIndicator(VENGEANCE_ACTIVE);
+			removeVarCounter(VENGEANCE_ACTIVE);
 		}
 
 		if (!config.showTeleblock())
@@ -771,6 +810,22 @@ public class TimersPlugin extends Plugin
 		{
 			removeGameTimer(SPELLBOOK_SWAP);
 		}
+
+		if (!config.showCurseOfTheMoons())
+		{
+			removeVarCounter(CURSE_OF_THE_MOONS_BLUE);
+			removeVarCounter(CURSE_OF_THE_MOONS_ECLIPSE);
+		}
+
+		if (!config.showColosseumDoom())
+		{
+			removeVarCounter(COLOSSEUM_DOOM);
+		}
+
+		if (!config.showMoonlightPotion())
+		{
+			removeVarTimer(MOONLIGHT_POTION);
+		}
 	}
 
 	@Subscribe
@@ -1059,6 +1114,8 @@ public class TimersPlugin extends Plugin
 					config.tzhaarStartTime(null);
 					config.tzhaarLastTime(null);
 				}
+				// Varbits.COLOSSEUM_DOOM is not set to 0 when teleporting out of the Colosseum.
+				removeVarCounter(COLOSSEUM_DOOM);
 				break;
 			case LOGIN_SCREEN:
 				// fall through
@@ -1076,7 +1133,6 @@ public class TimersPlugin extends Plugin
 		}
 	}
 
-
 	@Subscribe
 	public void onGraphicChanged(GraphicChanged event)
 	{
@@ -1211,31 +1267,6 @@ public class TimersPlugin extends Plugin
 		infoBoxManager.removeIf(t -> t instanceof TimerTimer && ((TimerTimer) t).getTimer() == timer);
 	}
 
-	private IndicatorIndicator createGameIndicator(GameIndicator gameIndicator)
-	{
-		removeGameIndicator(gameIndicator);
-
-		IndicatorIndicator indicator = new IndicatorIndicator(gameIndicator, this);
-		switch (gameIndicator.getImageType())
-		{
-			case SPRITE:
-				spriteManager.getSpriteAsync(gameIndicator.getImageId(), 0, indicator);
-				break;
-			case ITEM:
-				indicator.setImage(itemManager.getImage(gameIndicator.getImageId()));
-				break;
-		}
-		indicator.setTooltip(gameIndicator.getDescription());
-		infoBoxManager.addInfoBox(indicator);
-
-		return indicator;
-	}
-
-	private void removeGameIndicator(GameIndicator indicator)
-	{
-		infoBoxManager.removeIf(t -> t instanceof IndicatorIndicator && ((IndicatorIndicator) t).getIndicator() == indicator);
-	}
-
 	private void updateVarTimer(final GameTimer gameTimer, final int varValue, final IntUnaryOperator tickDuration)
 	{
 		updateVarTimer(gameTimer, varValue, i -> i == 0, tickDuration);
@@ -1270,4 +1301,53 @@ public class TimersPlugin extends Plugin
 		removeGameTimer(gameTimer);
 		varTimers.remove(gameTimer);
 	}
+
+	private void updateVarCounter(final GameCounter gameCounter, final int varValue)
+	{
+		BuffCounter buffCounter = varCounters.get(gameCounter);
+
+		if (varValue == 0)
+		{
+			removeVarCounter(gameCounter);
+		}
+		else if (buffCounter == null)
+		{
+			buffCounter = createBuffCounter(gameCounter, varValue);
+			varCounters.put(gameCounter, buffCounter);
+		}
+		else
+		{
+			buffCounter.setCount(varValue);
+		}
+	}
+
+	private BuffCounter createBuffCounter(GameCounter gameCounter, int count)
+	{
+		removeBuffCounter(gameCounter);
+
+		BuffCounter buffCounter = new BuffCounter(this, gameCounter, count);
+		switch (gameCounter.getImageType())
+		{
+			case SPRITE:
+				spriteManager.getSpriteAsync(gameCounter.getImageId(), 0, buffCounter);
+				break;
+			case ITEM:
+				buffCounter.setImage(itemManager.getImage(gameCounter.getImageId()));
+				break;
+		}
+		buffCounter.setTooltip(gameCounter.getDescription());
+		infoBoxManager.addInfoBox(buffCounter);
+		return buffCounter;
+	}
+
+	private void removeVarCounter(final GameCounter gameCounter)
+	{
+		removeBuffCounter(gameCounter);
+		varCounters.remove(gameCounter);
+	}
+
+	private void removeBuffCounter(GameCounter gameCounter)
+	{
+		infoBoxManager.removeIf(b -> b instanceof BuffCounter && ((BuffCounter) b).getGameCounter() == gameCounter);
+	}
 }


Test patch:
diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/slayer/SlayerPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/slayer/SlayerPluginTest.java
index 7f36e52e4..90c8f5ee8 100644
--- a/runelite-client/src/test/java/net/runelite/client/plugins/slayer/SlayerPluginTest.java
+++ b/runelite-client/src/test/java/net/runelite/client/plugins/slayer/SlayerPluginTest.java
@@ -272,11 +272,19 @@ public class SlayerPluginTest
 	@Test
 	public void npcMatching()
 	{
+		assertTrue(matches("Abyssal Sire", Task.ABYSSAL_DEMONS));
 		assertTrue(matches("Abyssal demon", Task.ABYSSAL_DEMONS));
 		assertTrue(matches("Baby blue dragon", Task.BLUE_DRAGONS));
+		assertTrue(matches("Vorkath", Task.BLUE_DRAGONS));
+		assertTrue(matches("Brutal red dragon", Task.RED_DRAGONS));
 		assertTrue(matches("Duck", Task.BIRDS));
 		assertTrue(matches("Donny the Lad", Task.BANDITS));
 		assertTrue(matches("Revenant Imp", Task.GHOSTS));
+		assertTrue(matches("Sulphur lizard", Task.LIZARDS));
+		assertTrue(matches("Spiked Turoth", Task.TUROTH));
+		assertTrue(matches("Loar shade", Task.SHADES));
+		assertTrue(matches("Loar shadow", Task.SHADES));
+		assertTrue(matches("Urium shadow", Task.SHADES));
 
 		assertFalse(matches("Rat", Task.PIRATES));
 		assertFalse(matches("Wolf", Task.WEREWOLVES));
diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/timers/ElapsedTimerTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/timersandbuffs/ElapsedTimerTest.java
similarity index 98%
rename from runelite-client/src/test/java/net/runelite/client/plugins/timers/ElapsedTimerTest.java
rename to runelite-client/src/test/java/net/runelite/client/plugins/timersandbuffs/ElapsedTimerTest.java
index 2b3c1e461..8851811e3 100644
--- a/runelite-client/src/test/java/net/runelite/client/plugins/timers/ElapsedTimerTest.java
+++ b/runelite-client/src/test/java/net/runelite/client/plugins/timersandbuffs/ElapsedTimerTest.java
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-package net.runelite.client.plugins.timers;
+package net.runelite.client.plugins.timersandbuffs;
 
 import java.time.Instant;
 import static org.junit.Assert.assertEquals;
diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/timers/TimersPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/timersandbuffs/TimersAndBuffsPluginTest.java
similarity index 79%
rename from runelite-client/src/test/java/net/runelite/client/plugins/timers/TimersPluginTest.java
rename to runelite-client/src/test/java/net/runelite/client/plugins/timersandbuffs/TimersAndBuffsPluginTest.java
index 5499078c2..0b336adff 100644
--- a/runelite-client/src/test/java/net/runelite/client/plugins/timers/TimersPluginTest.java
+++ b/runelite-client/src/test/java/net/runelite/client/plugins/timersandbuffs/TimersAndBuffsPluginTest.java
@@ -22,7 +22,7 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-package net.runelite.client.plugins.timers;
+package net.runelite.client.plugins.timersandbuffs;
 
 import com.google.inject.Guice;
 import com.google.inject.Inject;
@@ -66,14 +66,14 @@ import org.mockito.junit.MockitoJUnitRunner;
 import org.mockito.stubbing.Answer;
 
 @RunWith(MockitoJUnitRunner.class)
-public class TimersPluginTest
+public class TimersAndBuffsPluginTest
 {
 	@Inject
-	private TimersPlugin timersPlugin;
+	private TimersAndBuffsPlugin timersAndBuffsPlugin;
 
 	@Mock
 	@Bind
-	private TimersConfig timersConfig;
+	private TimersAndBuffsConfig timersAndBuffsConfig;
 
 	@Mock
 	@Bind
@@ -100,27 +100,27 @@ public class TimersPluginTest
 	@Test
 	public void removeTimersOnConfigChanged()
 	{
-		timersPlugin = spy(timersPlugin);
+		timersAndBuffsPlugin = spy(timersAndBuffsPlugin);
 		final ConfigChanged configChanged = new ConfigChanged();
-		configChanged.setGroup(TimersConfig.GROUP);
+		configChanged.setGroup(TimersAndBuffsConfig.GROUP);
 
-		timersPlugin.onConfigChanged(configChanged);
+		timersAndBuffsPlugin.onConfigChanged(configChanged);
 
 		for (GameTimer timer : GameTimer.values())
 		{
-			verify(timersPlugin).removeGameTimer(timer);
+			verify(timersAndBuffsPlugin).removeGameTimer(timer);
 		}
 	}
 
 	@Test
 	public void testDivineRanging()
 	{
-		when(timersConfig.showDivine()).thenReturn(true);
+		when(timersAndBuffsConfig.showDivine()).thenReturn(true);
 
 		VarbitChanged varbitChanged = new VarbitChanged();
 		varbitChanged.setVarbitId(Varbits.DIVINE_RANGING);
 		varbitChanged.setValue(500);
-		timersPlugin.onVarbitChanged(varbitChanged);
+		timersAndBuffsPlugin.onVarbitChanged(varbitChanged);
 
 		ArgumentCaptor<InfoBox> captor = ArgumentCaptor.forClass(InfoBox.class);
 		verify(infoBoxManager).addInfoBox(captor.capture());
@@ -131,31 +131,31 @@ public class TimersPluginTest
 	@Test
 	public void testDivineBastion()
 	{
-		when(timersConfig.showDivine()).thenReturn(true);
+		when(timersAndBuffsConfig.showDivine()).thenReturn(true);
 
 		VarbitChanged rangingVarbitChanged = new VarbitChanged();
 		rangingVarbitChanged.setVarbitId(Varbits.DIVINE_RANGING);
 		rangingVarbitChanged.setValue(500);
-		timersPlugin.onVarbitChanged(rangingVarbitChanged);
+		timersAndBuffsPlugin.onVarbitChanged(rangingVarbitChanged);
 		when(client.getVarbitValue(Varbits.DIVINE_RANGING)).thenReturn(500);
 
 		VarbitChanged superDefenceVarbitChanged = new VarbitChanged();
 		superDefenceVarbitChanged.setVarbitId(Varbits.DIVINE_SUPER_DEFENCE);
 		superDefenceVarbitChanged.setValue(500);
-		timersPlugin.onVarbitChanged(superDefenceVarbitChanged);
+		timersAndBuffsPlugin.onVarbitChanged(superDefenceVarbitChanged);
 		when(client.getVarbitValue(Varbits.DIVINE_SUPER_DEFENCE)).thenReturn(500);
 
 		VarbitChanged bastionVarbitChanged = new VarbitChanged();
 		bastionVarbitChanged.setVarbitId(Varbits.DIVINE_BASTION);
 		bastionVarbitChanged.setValue(500);
-		timersPlugin.onVarbitChanged(bastionVarbitChanged);
+		timersAndBuffsPlugin.onVarbitChanged(bastionVarbitChanged);
 
 		ArgumentCaptor<InfoBox> captor = ArgumentCaptor.forClass(InfoBox.class);
 		verify(infoBoxManager, times(3)).addInfoBox(captor.capture());
 		List<InfoBox> infoBoxes = captor.getAllValues();
 
 		ArgumentCaptor<Predicate<InfoBox>> prcaptor = ArgumentCaptor.forClass(Predicate.class);
-		verify(infoBoxManager, times(5)).removeIf(prcaptor.capture());
+		verify(infoBoxManager, times(6)).removeIf(prcaptor.capture());
 		List<Predicate<InfoBox>> filters = prcaptor.getAllValues();
 
 		// test defence, ranging, and bastion infoboxes added
@@ -165,22 +165,24 @@ public class TimersPluginTest
 
 		// test ranging and defence infoboxes removed
 		assertTrue(filters.get(0).test(infoBoxes.get(0)));  // divine ranging infobox added
-		assertTrue(filters.get(1).test(infoBoxes.get(1)));  // divine super defence infobox added
-		assertTrue(filters.get(2).test(infoBoxes.get(0)));  // divine ranging infobox removed
-		assertTrue(filters.get(3).test(infoBoxes.get(1)));  // divine super defence infobox removed
-		assertTrue(filters.get(4).test(infoBoxes.get(2)));  // divine bastion infobox added
+		// filters.get(1) tries to remove a non-existent moonlight potion timer since Varbits.DIVINE_SUPER_DEFENCE
+		// was changed
+		assertTrue(filters.get(2).test(infoBoxes.get(1)));  // divine super defence infobox added
+		assertTrue(filters.get(3).test(infoBoxes.get(0)));  // divine ranging infobox removed
+		assertTrue(filters.get(4).test(infoBoxes.get(1)));  // divine super defence infobox removed
+		assertTrue(filters.get(5).test(infoBoxes.get(2)));  // divine bastion infobox added
 	}
 
 	@Test
 	public void testDivineRangingAfterBastion()
 	{
-		when(timersConfig.showDivine()).thenReturn(true);
+		when(timersAndBuffsConfig.showDivine()).thenReturn(true);
 		when(client.getVarbitValue(Varbits.DIVINE_BASTION)).thenReturn(400);
 
 		VarbitChanged varbitChanged = new VarbitChanged();
 		varbitChanged.setVarbitId(Varbits.DIVINE_RANGING);
 		varbitChanged.setValue(500);
-		timersPlugin.onVarbitChanged(varbitChanged);
+		timersAndBuffsPlugin.onVarbitChanged(varbitChanged);
 
 		ArgumentCaptor<InfoBox> captor = ArgumentCaptor.forClass(InfoBox.class);
 		verify(infoBoxManager).addInfoBox(captor.capture());
@@ -191,14 +193,14 @@ public class TimersPluginTest
 	@Test
 	public void testStamina()
 	{
-		when(timersConfig.showStamina()).thenReturn(true);
+		when(timersAndBuffsConfig.showStamina()).thenReturn(true);
 		when(client.getVarbitValue(Varbits.RUN_SLOWED_DEPLETION_ACTIVE)).thenReturn(1);
 		when(client.getVarbitValue(Varbits.STAMINA_EFFECT)).thenReturn(20);
 		when(client.getVarbitValue(Varbits.RING_OF_ENDURANCE_EFFECT)).thenReturn(0);
 
 		VarbitChanged varbitChanged = new VarbitChanged();
 		varbitChanged.setVarbitId(Varbits.RUN_SLOWED_DEPLETION_ACTIVE); // just has to be one of the vars
-		timersPlugin.onVarbitChanged(varbitChanged);
+		timersAndBuffsPlugin.onVarbitChanged(varbitChanged);
 
 		ArgumentCaptor<InfoBox> captor = ArgumentCaptor.forClass(InfoBox.class);
 		verify(infoBoxManager).addInfoBox(captor.capture());
@@ -210,9 +212,9 @@ public class TimersPluginTest
 	@Test
 	public void testSireStunTimer()
 	{
-		when(timersConfig.showAbyssalSireStun()).thenReturn(true);
+		when(timersAndBuffsConfig.showAbyssalSireStun()).thenReturn(true);
 		ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", "The Sire has been disorientated temporarily.", "", 0);
-		timersPlugin.onChatMessage(chatMessage);
+		timersAndBuffsPlugin.onChatMessage(chatMessage);
 
 		ArgumentCaptor<InfoBox> captor = ArgumentCaptor.forClass(InfoBox.class);
 		verify(infoBoxManager).addInfoBox(captor.capture());
@@ -224,14 +226,14 @@ public class TimersPluginTest
 	@Test
 	public void testEndurance()
 	{
-		when(timersConfig.showStamina()).thenReturn(true);
+		when(timersAndBuffsConfig.showStamina()).thenReturn(true);
 		when(client.getVarbitValue(Varbits.RUN_SLOWED_DEPLETION_ACTIVE)).thenReturn(1);
 		when(client.getVarbitValue(Varbits.STAMINA_EFFECT)).thenReturn(20);
 		when(client.getVarbitValue(Varbits.RING_OF_ENDURANCE_EFFECT)).thenReturn(20);
 
 		VarbitChanged varbitChanged = new VarbitChanged();
 		varbitChanged.setVarbitId(Varbits.RUN_SLOWED_DEPLETION_ACTIVE); // just has to be one of the vars
-		timersPlugin.onVarbitChanged(varbitChanged);
+		timersAndBuffsPlugin.onVarbitChanged(varbitChanged);
 
 		ArgumentCaptor<InfoBox> captor = ArgumentCaptor.forClass(InfoBox.class);
 		verify(infoBoxManager).addInfoBox(captor.capture());
@@ -241,7 +243,7 @@ public class TimersPluginTest
 
 		// unwield ring
 		when(client.getVarbitValue(Varbits.RING_OF_ENDURANCE_EFFECT)).thenReturn(0);
-		timersPlugin.onVarbitChanged(varbitChanged);
+		timersAndBuffsPlugin.onVarbitChanged(varbitChanged);
 		int mins = (int) infoBox.getDuration().toMinutes();
 		assertEquals(2, mins);
 	}
@@ -249,8 +251,8 @@ public class TimersPluginTest
 	@Test
 	public void testTzhaarTimer()
 	{
-		when(timersConfig.showTzhaarTimers()).thenReturn(true);
-		when(client.getMapRegions()).thenReturn(new int[]{TimersPlugin.FIGHT_CAVES_REGION_ID});
+		when(timersAndBuffsConfig.showTzhaarTimers()).thenReturn(true);
+		when(client.getMapRegions()).thenReturn(new int[]{TimersAndBuffsPlugin.FIGHT_CAVES_REGION_ID});
 
 		class InstantRef
 		{
@@ -258,33 +260,33 @@ public class TimersPluginTest
 		}
 
 		InstantRef startTime = new InstantRef();
-		when(timersConfig.tzhaarStartTime()).then(a -> startTime.i);
+		when(timersAndBuffsConfig.tzhaarStartTime()).then(a -> startTime.i);
 		doAnswer((Answer<Void>) invocationOnMock ->
 		{
 			Object argument = invocationOnMock.getArguments()[0];
 			startTime.i = (Instant) argument;
 			return null;
-		}).when(timersConfig).tzhaarStartTime(nullable(Instant.class));
+		}).when(timersAndBuffsConfig).tzhaarStartTime(nullable(Instant.class));
 
 		InstantRef lastTime = new InstantRef();
-		when(timersConfig.tzhaarLastTime()).then(a -> lastTime.i);
+		when(timersAndBuffsConfig.tzhaarLastTime()).then(a -> lastTime.i);
 		doAnswer((Answer<Void>) invocationOnMock ->
 		{
 			Object argument = invocationOnMock.getArguments()[0];
 			lastTime.i = (Instant) argument;
 			return null;
-		}).when(timersConfig).tzhaarLastTime(nullable(Instant.class));
+		}).when(timersAndBuffsConfig).tzhaarLastTime(nullable(Instant.class));
 
 		// test timer creation: verify the infobox was added and that it is an ElapsedTimer
 		ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", "<col=ef1020>Wave: 1</col>", "", 0);
-		timersPlugin.onChatMessage(chatMessage);
+		timersAndBuffsPlugin.onChatMessage(chatMessage);
 		ArgumentCaptor<InfoBox> captor = ArgumentCaptor.forClass(InfoBox.class);
 		verify(infoBoxManager, times(1)).addInfoBox(captor.capture());
 		assertTrue(captor.getValue() instanceof ElapsedTimer);
 
 		// test timer pause: verify the added ElapsedTimer has a non-null lastTime
 		chatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", "<col=ef1020>The Inferno has been paused. You may now log out.", "", 0);
-		timersPlugin.onChatMessage(chatMessage);
+		timersAndBuffsPlugin.onChatMessage(chatMessage);
 		verify(infoBoxManager, times(1)).removeInfoBox(captor.capture());
 		verify(infoBoxManager, times(2)).addInfoBox(captor.capture());
 		assertTrue(captor.getValue() instanceof ElapsedTimer);
@@ -294,7 +296,7 @@ public class TimersPluginTest
 
 		// test timer unpause: verify the last time is null after being unpaused
 		chatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", "<col=ef1020>Wave: 2</col>", "", 0);
-		timersPlugin.onChatMessage(chatMessage);
+		timersAndBuffsPlugin.onChatMessage(chatMessage);
 		verify(infoBoxManager, times(2)).removeInfoBox(captor.capture());
 		verify(infoBoxManager, times(3)).addInfoBox(captor.capture());
 		assertTrue(captor.getValue() instanceof ElapsedTimer);
@@ -306,8 +308,8 @@ public class TimersPluginTest
 		final GameStateChanged gameStateChanged = new GameStateChanged();
 		gameStateChanged.setGameState(GameState.LOADING);
 		when(client.getMapRegions()).thenReturn(new int[0]);
-		timersPlugin.onChatMessage(chatMessage);
-		timersPlugin.onGameStateChanged(gameStateChanged);
+		timersAndBuffsPlugin.onChatMessage(chatMessage);
+		timersAndBuffsPlugin.onGameStateChanged(gameStateChanged);
 		verify(infoBoxManager, times(3)).removeInfoBox(captor.capture());
 		verify(infoBoxManager, times(3)).addInfoBox(captor.capture());
 	}
@@ -315,8 +317,8 @@ public class TimersPluginTest
 	@Test
 	public void testInfernoTimerStartOffset()
 	{
-		when(timersConfig.showTzhaarTimers()).thenReturn(true);
-		when(client.getMapRegions()).thenReturn(new int[]{TimersPlugin.INFERNO_REGION_ID});
+		when(timersAndBuffsConfig.showTzhaarTimers()).thenReturn(true);
+		when(client.getMapRegions()).thenReturn(new int[]{TimersAndBuffsPlugin.INFERNO_REGION_ID});
 
 		class InstantRef
 		{
@@ -324,16 +326,16 @@ public class TimersPluginTest
 		}
 
 		InstantRef startTime = new InstantRef();
-		when(timersConfig.tzhaarStartTime()).then(a -> startTime.i);
+		when(timersAndBuffsConfig.tzhaarStartTime()).then(a -> startTime.i);
 		doAnswer((Answer<Void>) invocationOnMock ->
 		{
 			Object argument = invocationOnMock.getArguments()[0];
 			startTime.i = (Instant) argument;
 			return null;
-		}).when(timersConfig).tzhaarStartTime(nullable(Instant.class));
+		}).when(timersAndBuffsConfig).tzhaarStartTime(nullable(Instant.class));
 
 		ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", "<col=ef1020>Wave: 1</col>", "", 0);
-		timersPlugin.onChatMessage(chatMessage);
+		timersAndBuffsPlugin.onChatMessage(chatMessage);
 
 		ArgumentCaptor<InfoBox> captor = ArgumentCaptor.forClass(InfoBox.class);
 		verify(infoBoxManager, times(1)).addInfoBox(captor.capture());
@@ -346,12 +348,12 @@ public class TimersPluginTest
 	@Test
 	public void testDeathChargeCast()
 	{
-		when(timersConfig.showArceuus()).thenReturn(true);
+		when(timersAndBuffsConfig.showArceuus()).thenReturn(true);
 		when(client.getRealSkillLevel(Skill.MAGIC)).thenReturn(50);
 		VarbitChanged varbitChanged = new VarbitChanged();
 		varbitChanged.setVarbitId(Varbits.DEATH_CHARGE);
 		varbitChanged.setValue(1);
-		timersPlugin.onVarbitChanged(varbitChanged);
+		timersAndBuffsPlugin.onVarbitChanged(varbitChanged);
 
 		ArgumentCaptor<InfoBox> ibcaptor = ArgumentCaptor.forClass(InfoBox.class);
 		verify(infoBoxManager).addInfoBox(ibcaptor.capture());
@@ -363,12 +365,12 @@ public class TimersPluginTest
 	@Test
 	public void testDeathChargeCooldown()
 	{
-		when(timersConfig.showArceuusCooldown()).thenReturn(true);
+		when(timersAndBuffsConfig.showArceuusCooldown()).thenReturn(true);
 
 		VarbitChanged varbitChanged = new VarbitChanged();
 		varbitChanged.setVarbitId(Varbits.DEATH_CHARGE_COOLDOWN);
 		varbitChanged.setValue(1);
-		timersPlugin.onVarbitChanged(varbitChanged);
+		timersAndBuffsPlugin.onVarbitChanged(varbitChanged);
 
 		ArgumentCaptor<InfoBox> ibcaptor = ArgumentCaptor.forClass(InfoBox.class);
 		verify(infoBoxManager).addInfoBox(ibcaptor.capture());
@@ -379,10 +381,10 @@ public class TimersPluginTest
 	@Test
 	public void testArceuusWard()
 	{
-		when(timersConfig.showArceuus()).thenReturn(true);
+		when(timersAndBuffsConfig.showArceuus()).thenReturn(true);
 		when(client.getRealSkillLevel(Skill.MAGIC)).thenReturn(57);
 		ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", "<col=0000b2>Your defence against Arceuus magic has been strengthened.</col>", "", 0);
-		timersPlugin.onChatMessage(chatMessage);
+		timersAndBuffsPlugin.onChatMessage(chatMessage);
 
 		ArgumentCaptor<InfoBox> captor = ArgumentCaptor.forClass(InfoBox.class);
 		verify(infoBoxManager).addInfoBox(captor.capture());
@@ -394,12 +396,12 @@ public class TimersPluginTest
 	@Test
 	public void testArceuusWardCooldown()
 	{
-		when(timersConfig.showArceuusCooldown()).thenReturn(true);
+		when(timersAndBuffsConfig.showArceuusCooldown()).thenReturn(true);
 
 		VarbitChanged varbitChanged = new VarbitChanged();
 		varbitChanged.setVarbitId(Varbits.WARD_OF_ARCEUUS_COOLDOWN);
 		varbitChanged.setValue(1);
-		timersPlugin.onVarbitChanged(varbitChanged);
+		timersAndBuffsPlugin.onVarbitChanged(varbitChanged);
 
 		ArgumentCaptor<InfoBox> captor = ArgumentCaptor.forClass(InfoBox.class);
 		verify(infoBoxManager).addInfoBox(captor.capture());
@@ -410,12 +412,12 @@ public class TimersPluginTest
 	@Test
 	public void testCorruptionCooldown()
 	{
-		when(timersConfig.showArceuusCooldown()).thenReturn(true);
+		when(timersAndBuffsConfig.showArceuusCooldown()).thenReturn(true);
 
 		VarbitChanged varbitChanged = new VarbitChanged();
 		varbitChanged.setVarbitId(Varbits.CORRUPTION_COOLDOWN);
 		varbitChanged.setValue(1);
-		timersPlugin.onVarbitChanged(varbitChanged);
+		timersAndBuffsPlugin.onVarbitChanged(varbitChanged);
 
 		ArgumentCaptor<InfoBox> captor = ArgumentCaptor.forClass(InfoBox.class);
 		verify(infoBoxManager).addInfoBox(captor.capture());
@@ -426,11 +428,11 @@ public class TimersPluginTest
 	@Test
 	public void testShadowVeil()
 	{
-		when(timersConfig.showArceuus()).thenReturn(true);
+		when(timersAndBuffsConfig.showArceuus()).thenReturn(true);
 		when(client.getRealSkillLevel(Skill.MAGIC)).thenReturn(57);
 
 		ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", "<col=6800bf>Your thieving abilities have been enhanced.</col>", "", 0);
-		timersPlugin.onChatMessage(chatMessage);
+		timersAndBuffsPlugin.onChatMessage(chatMessage);
 
 		ArgumentCaptor<InfoBox> captor = ArgumentCaptor.forClass(InfoBox.class);
 		verify(infoBoxManager).addInfoBox(captor.capture());
@@ -441,12 +443,12 @@ public class TimersPluginTest
 	@Test
 	public void testShadowVeilCooldown()
 	{
-		when(timersConfig.showArceuusCooldown()).thenReturn(true);
+		when(timersAndBuffsConfig.showArceuusCooldown()).thenReturn(true);
 
 		VarbitChanged varbitChanged = new VarbitChanged();
 		varbitChanged.setVarbitId(Varbits.SHADOW_VEIL_COOLDOWN);
 		varbitChanged.setValue(1);
-		timersPlugin.onVarbitChanged(varbitChanged);
+		timersAndBuffsPlugin.onVarbitChanged(varbitChanged);
 
 		ArgumentCaptor<InfoBox> captor = ArgumentCaptor.forClass(InfoBox.class);
 		verify(infoBoxManager).addInfoBox(captor.capture());
@@ -457,11 +459,11 @@ public class TimersPluginTest
 	@Test
 	public void testThrall()
 	{
-		when(timersConfig.showArceuus()).thenReturn(true);
+		when(timersAndBuffsConfig.showArceuus()).thenReturn(true);
 		when(client.getBoostedSkillLevel(Skill.MAGIC)).thenReturn(60);
 
 		ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", "<col=ef0083>You resurrect a greater zombified thrall.</col>", "", 0);
-		timersPlugin.onChatMessage(chatMessage);
+		timersAndBuffsPlugin.onChatMessage(chatMessage);
 
 		ArgumentCaptor<InfoBox> ibcaptor = ArgumentCaptor.forClass(InfoBox.class);
 		verify(infoBoxManager).addInfoBox(ibcaptor.capture());
@@ -473,12 +475,12 @@ public class TimersPluginTest
 	@Test
 	public void testThrallCooldown()
 	{
-		when(timersConfig.showArceuusCooldown()).thenReturn(true);
+		when(timersAndBuffsConfig.showArceuusCooldown()).thenReturn(true);
 
 		VarbitChanged varbitChanged = new VarbitChanged();
 		varbitChanged.setVarbitId(Varbits.RESURRECT_THRALL_COOLDOWN);
 		varbitChanged.setValue(1);
-		timersPlugin.onVarbitChanged(varbitChanged);
+		timersAndBuffsPlugin.onVarbitChanged(varbitChanged);
 
 		ArgumentCaptor<InfoBox> ibcaptor = ArgumentCaptor.forClass(InfoBox.class);
 		verify(infoBoxManager).addInfoBox(ibcaptor.capture());
@@ -490,12 +492,12 @@ public class TimersPluginTest
 	@Test
 	public void testImbuedHeartStart()
 	{
-		when(timersConfig.showImbuedHeart()).thenReturn(true);
+		when(timersAndBuffsConfig.showImbuedHeart()).thenReturn(true);
 
 		VarbitChanged varbitChanged = new VarbitChanged();
 		varbitChanged.setVarbitId(Varbits.IMBUED_HEART_COOLDOWN);
 		varbitChanged.setValue(70);
-		timersPlugin.onVarbitChanged(varbitChanged);
+		timersAndBuffsPlugin.onVarbitChanged(varbitChanged);
 
 		ArgumentCaptor<InfoBox> captor = ArgumentCaptor.forClass(InfoBox.class);
 		verify(infoBoxManager).addInfoBox(captor.capture());
@@ -507,15 +509,15 @@ public class TimersPluginTest
 	@Test
 	public void testImbuedHeartEnd()
 	{
-		when(timersConfig.showImbuedHeart()).thenReturn(true);
+		when(timersAndBuffsConfig.showImbuedHeart()).thenReturn(true);
 
 		VarbitChanged varbitChanged = new VarbitChanged();
 		varbitChanged.setVarbitId(Varbits.IMBUED_HEART_COOLDOWN);
 		varbitChanged.setValue(70);
-		timersPlugin.onVarbitChanged(varbitChanged); // Calls removeIf once (on createGameTimer)
+		timersAndBuffsPlugin.onVarbitChanged(varbitChanged); // Calls removeIf once (on createGameTimer)
 
 		ArgumentCaptor<Predicate<InfoBox>> prcaptor = ArgumentCaptor.forClass(Predicate.class);
-		TimerTimer imbuedHeartInfoBox = new TimerTimer(GameTimer.IMBUEDHEART, Duration.ofSeconds(420), timersPlugin);
+		TimerTimer imbuedHeartInfoBox = new TimerTimer(GameTimer.IMBUEDHEART, Duration.ofSeconds(420), timersAndBuffsPlugin);
 		verify(infoBoxManager, times (1)).addInfoBox(any());
 		verify(infoBoxManager, times(1)).removeIf(prcaptor.capture());
 		Predicate<InfoBox> pred = prcaptor.getValue();
@@ -524,7 +526,7 @@ public class TimersPluginTest
 		varbitChanged = new VarbitChanged();
 		varbitChanged.setVarbitId(Varbits.IMBUED_HEART_COOLDOWN);
 		varbitChanged.setValue(0);
-		timersPlugin.onVarbitChanged(varbitChanged); // Calls removeIf once
+		timersAndBuffsPlugin.onVarbitChanged(varbitChanged); // Calls removeIf once
 
 		verify(infoBoxManager, times(1)).addInfoBox(any());
 		verify(infoBoxManager, times(2)).removeIf(prcaptor.capture());
@@ -535,9 +537,9 @@ public class TimersPluginTest
 	@Test
 	public void testMartinPickpocket()
 	{
-		when(timersConfig.showPickpocketStun()).thenReturn(true);
+		when(timersAndBuffsConfig.showPickpocketStun()).thenReturn(true);
 		ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.SPAM, "", "You fail to pick Martin's pocket.", "", 0);
-		timersPlugin.onChatMessage(chatMessage);
+		timersAndBuffsPlugin.onChatMessage(chatMessage);
 
 		ArgumentCaptor<InfoBox> captor = ArgumentCaptor.forClass(InfoBox.class);
 		verify(infoBoxManager).addInfoBox(captor.capture());
@@ -549,16 +551,16 @@ public class TimersPluginTest
 	@Test
 	public void testCoXOverload()
 	{
-		when(timersConfig.showOverload()).thenReturn(true);
+		when(timersAndBuffsConfig.showOverload()).thenReturn(true);
 		ArgumentCaptor<Predicate<InfoBox>> prcaptor = ArgumentCaptor.forClass(Predicate.class);
 
 		when(client.getVarbitValue(Varbits.IN_RAID)).thenReturn(1);
 		VarbitChanged varbitChanged = new VarbitChanged();
 		varbitChanged.setVarbitId(Varbits.COX_OVERLOAD_REFRESHES_REMAINING);
 		varbitChanged.setValue(15);
-		timersPlugin.onVarbitChanged(varbitChanged);
+		timersAndBuffsPlugin.onVarbitChanged(varbitChanged);
 
-		TimerTimer overloadInfobox = new TimerTimer(GameTimer.OVERLOAD_RAID, Duration.ofSeconds(225), timersPlugin);
+		TimerTimer overloadInfobox = new TimerTimer(GameTimer.OVERLOAD_RAID, Duration.ofSeconds(225), timersAndBuffsPlugin);
 		verify(infoBoxManager).addInfoBox(any());
 		verify(infoBoxManager).removeIf(prcaptor.capture());
 		Predicate<InfoBox> pred = prcaptor.getValue();
@@ -566,7 +568,7 @@ public class TimersPluginTest
 
 		// Remove on running out
 		varbitChanged.setValue(0);
-		timersPlugin.onVarbitChanged(varbitChanged);
+		timersAndBuffsPlugin.onVarbitChanged(varbitChanged);
 
 		verify(infoBoxManager).addInfoBox(any());
 		verify(infoBoxManager, times(2)).removeIf(any());
@@ -575,15 +577,15 @@ public class TimersPluginTest
 	@Test
 	public void testNMZOverload()
 	{
-		when(timersConfig.showOverload()).thenReturn(true);
+		when(timersAndBuffsConfig.showOverload()).thenReturn(true);
 		ArgumentCaptor<Predicate<InfoBox>> prcaptor = ArgumentCaptor.forClass(Predicate.class);
 
 		VarbitChanged varbitChanged = new VarbitChanged();
 		varbitChanged.setVarbitId(Varbits.NMZ_OVERLOAD_REFRESHES_REMAINING);
 		varbitChanged.setValue(9);
-		timersPlugin.onVarbitChanged(varbitChanged);
+		timersAndBuffsPlugin.onVarbitChanged(varbitChanged);
 
-		TimerTimer overloadInfobox = new TimerTimer(GameTimer.OVERLOAD, Duration.ofSeconds(135), timersPlugin);
+		TimerTimer overloadInfobox = new TimerTimer(GameTimer.OVERLOAD, Duration.ofSeconds(135), timersAndBuffsPlugin);
 		verify(infoBoxManager).addInfoBox(any());
 		verify(infoBoxManager).removeIf(prcaptor.capture());
 		Predicate<InfoBox> pred = prcaptor.getValue();
@@ -591,7 +593,7 @@ public class TimersPluginTest
 
 		// Remove on running out
 		varbitChanged.setValue(0);
-		timersPlugin.onVarbitChanged(varbitChanged);
+		timersAndBuffsPlugin.onVarbitChanged(varbitChanged);
 
 		verify(infoBoxManager).addInfoBox(any());
 		verify(infoBoxManager, times(2)).removeIf(any());