Repository: runelite/runelite
Pull request #18391 (2024-10-05 14:29:30)

Item Charges : Add support for newer potions
Add support for charges on :
 - Menaphite Remedy
 - Extended anti-venom+
 - Goading potion
 - Prayer regeneration potion


Patch:
diff --git a/cache/pom.xml b/cache/pom.xml
index 846c39172..34f2e8dc4 100644
--- a/cache/pom.xml
+++ b/cache/pom.xml
@@ -29,7 +29,7 @@
 	<parent>
 		<groupId>net.runelite</groupId>
 		<artifactId>runelite-parent</artifactId>
-		<version>1.10.40-SNAPSHOT</version>
+		<version>1.10.41-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>cache</artifactId>
diff --git a/pom.xml b/pom.xml
index b67a4a149..362a39015 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,7 +28,7 @@
 
 	<groupId>net.runelite</groupId>
 	<artifactId>runelite-parent</artifactId>
-	<version>1.10.40-SNAPSHOT</version>
+	<version>1.10.41-SNAPSHOT</version>
 	<packaging>pom</packaging>
 
 	<name>RuneLite</name>
diff --git a/runelite-api/pom.xml b/runelite-api/pom.xml
index beab947ac..ef55a7ec1 100644
--- a/runelite-api/pom.xml
+++ b/runelite-api/pom.xml
@@ -29,7 +29,7 @@
 	<parent>
 		<groupId>net.runelite</groupId>
 		<artifactId>runelite-parent</artifactId>
-		<version>1.10.40-SNAPSHOT</version>
+		<version>1.10.41-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>runelite-api</artifactId>
diff --git a/runelite-api/src/main/interfaces/interfaces.toml b/runelite-api/src/main/interfaces/interfaces.toml
index f54f78c88..2dfa7c09d 100644
--- a/runelite-api/src/main/interfaces/interfaces.toml
+++ b/runelite-api/src/main/interfaces/interfaces.toml
@@ -70,12 +70,12 @@ potion_store=48
 incinerator=49
 incinerator_confirm=50
 potionstore_content=52
-equipment_parent=74
-equipment_set_bonus=115
-settings_button=119
-equipment_button=120
-popup=122
-equipment_stat_bonus=127
+equipment_parent=77
+equipment_set_bonus=118
+settings_button=122
+equipment_button=123
+popup=125
+equipment_stat_bonus=131
 
 [bank_inventory]
 id=15
@@ -627,6 +627,7 @@ container=0
 
 [prayer]
 id=541
+parent=0
 
 [private_chat]
 id=163
diff --git a/runelite-api/src/main/java/net/runelite/api/NpcID.java b/runelite-api/src/main/java/net/runelite/api/NpcID.java
index a8c869287..ed2a35e21 100644
--- a/runelite-api/src/main/java/net/runelite/api/NpcID.java
+++ b/runelite-api/src/main/java/net/runelite/api/NpcID.java
@@ -11601,8 +11601,7 @@ public final class NpcID
 	public static final int ICE_BLOCK_13790 = 13790;
 	public static final int FROST_CRAB_13791 = 13791;
 	public static final int ICE_BLOCK_13792 = 13792;
-	public static final int BLUE_DRAGON_13793 = 13793;
-	public static final int BABY_BLUE_DRAGON_13794 = 13794;
+	public static final int BREWMA = 13793;
 	public static final int BRUTAL_BLUE_DRAGON_13795 = 13795;
 	public static final int ICE_GIANT_13796 = 13796;
 	public static final int ICE_GIANT_13797 = 13797;
@@ -11836,5 +11835,9 @@ public final class NpcID
 	public static final int HUBERTE_14045 = 14045;
 	public static final int MOXI_14046 = 14046;
 	public static final int GULL_14047 = 14047;
+	public static final int BLUE_DRAGON_14103 = 14103;
+	public static final int BLUE_DRAGON_14104 = 14104;
+	public static final int BABY_BLUE_DRAGON_14105 = 14105;
+	public static final int BABY_BLUE_DRAGON_14106 = 14106;
 	/* This file is automatically generated. Do not edit. */
 }
diff --git a/runelite-api/src/main/java/net/runelite/api/NullObjectID.java b/runelite-api/src/main/java/net/runelite/api/NullObjectID.java
index 33a6ad308..8df4af53e 100644
--- a/runelite-api/src/main/java/net/runelite/api/NullObjectID.java
+++ b/runelite-api/src/main/java/net/runelite/api/NullObjectID.java
@@ -28811,5 +28811,9 @@ public final class NullObjectID
 	public static final int NULL_55403 = 55403;
 	public static final int NULL_55404 = 55404;
 	public static final int NULL_55405 = 55405;
+	public static final int NULL_55420 = 55420;
+	public static final int NULL_55421 = 55421;
+	public static final int NULL_55422 = 55422;
+	public static final int NULL_55423 = 55423;
 	/* This file is automatically generated. Do not edit. */
 }
diff --git a/runelite-api/src/main/java/net/runelite/api/ObjectID.java b/runelite-api/src/main/java/net/runelite/api/ObjectID.java
index dd7905b6d..c00198faf 100644
--- a/runelite-api/src/main/java/net/runelite/api/ObjectID.java
+++ b/runelite-api/src/main/java/net/runelite/api/ObjectID.java
@@ -26582,5 +26582,18 @@ public final class ObjectID
 	public static final int SHELVES_55407 = 55407;
 	public static final int SHELVES_55408 = 55408;
 	public static final int SHELVES_55409 = 55409;
+	public static final int CAULDRON_55410 = 55410;
+	public static final int REWARD_CART = 55411;
+	public static final int REWARD_CART_55412 = 55412;
+	public static final int REWARD_CART_55413 = 55413;
+	public static final int REWARD_CART_55414 = 55414;
+	public static final int REWARD_CART_55415 = 55415;
+	public static final int REWARD_CART_55416 = 55416;
+	public static final int ROCKSLIDE_55417 = 55417;
+	public static final int ROCKSLIDE_55418 = 55418;
+	public static final int ROCKSLIDE_55419 = 55419;
+	public static final int GRAPE_STORAGE_55424 = 55424;
+	public static final int GRAPE_STORAGE_55425 = 55425;
+	public static final int GRAPE_STORAGE_55426 = 55426;
 	/* This file is automatically generated. Do not edit. */
 }
diff --git a/runelite-api/src/main/java/net/runelite/api/hooks/DrawCallbacks.java b/runelite-api/src/main/java/net/runelite/api/hooks/DrawCallbacks.java
index 4b5281912..26bfc57be 100644
--- a/runelite-api/src/main/java/net/runelite/api/hooks/DrawCallbacks.java
+++ b/runelite-api/src/main/java/net/runelite/api/hooks/DrawCallbacks.java
@@ -127,19 +127,19 @@ public interface DrawCallbacks
 	{
 	}
 
-	default void drawPass(Projection projection, Scene scene, int pass)
+	default void drawPass(Projection entityProjection, Scene scene, int pass)
 	{
 	}
 
-	default void drawZone(Projection projection, Scene scene, int pass, int zx, int zz)
+	default void drawZone(Projection entityProjection, Scene scene, int pass, int zx, int zz)
 	{
 	}
 
-	default void drawDynamic(Scene scene, TileObject tileObject, Renderable r, Model m, int orient, int x, int y, int z)
+	default void drawDynamic(Projection worldProjection, Scene scene, TileObject tileObject, Renderable r, Model m, int orient, int x, int y, int z)
 	{
 	}
 
-	default void drawTemp(Scene scene, GameObject gameObject, Model m)
+	default void drawTemp(Projection worldProjection, Scene scene, GameObject gameObject, Model m)
 	{
 	}
 
diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml
index ebb95b16c..98020cc38 100644
--- a/runelite-client/pom.xml
+++ b/runelite-client/pom.xml
@@ -29,7 +29,7 @@
 	<parent>
 		<groupId>net.runelite</groupId>
 		<artifactId>runelite-parent</artifactId>
-		<version>1.10.40-SNAPSHOT</version>
+		<version>1.10.41-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>client</artifactId>
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/attackstyles/AttackStylesPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/attackstyles/AttackStylesPlugin.java
index 7f6e43959..5ddb2204c 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/attackstyles/AttackStylesPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/attackstyles/AttackStylesPlugin.java
@@ -58,6 +58,7 @@ import net.runelite.client.eventbus.Subscribe;
 import net.runelite.client.events.ConfigChanged;
 import net.runelite.client.plugins.Plugin;
 import net.runelite.client.plugins.PluginDescriptor;
+import static net.runelite.client.plugins.attackstyles.AttackStyle.CASTING;
 import static net.runelite.client.plugins.attackstyles.AttackStyle.DEFENSIVE;
 import static net.runelite.client.plugins.attackstyles.AttackStyle.DEFENSIVE_CASTING;
 import static net.runelite.client.plugins.attackstyles.AttackStyle.OTHER;
@@ -296,6 +297,14 @@ public class AttackStylesPlugin extends Plugin
 		int weaponStyleEnum = client.getEnum(EnumID.WEAPON_STYLES).getIntValue(weaponType);
 		if (weaponStyleEnum == -1)
 		{
+			// Blue moon spear
+			if (weaponType == 22)
+			{
+				return new AttackStyle[]{
+					AttackStyle.ACCURATE, AttackStyle.AGGRESSIVE, null, DEFENSIVE, CASTING, DEFENSIVE_CASTING
+				};
+			}
+
 			if (weaponType == 30)
 			{
 				// Partisan
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/EmoteClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/EmoteClue.java
index 666e74484..3d5a1a269 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/EmoteClue.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/EmoteClue.java
@@ -181,7 +181,7 @@ public class EmoteClue extends ClueScroll implements LocationClueScroll
 		new EmoteClue("Jump for joy in the Ancient Cavern. Equip a granite shield, splitbark body and any rune heraldic helm.", "Ancient cavern", ENTRANCE_OF_THE_CAVERN_UNDER_THE_WHIRLPOOL, new WorldPoint(1768, 5366, 1), JUMP_FOR_JOY, item(GRANITE_SHIELD), item(SPLITBARK_BODY), range("Any rune heraldic helm", RUNE_HELM_H1, RUNE_HELM_H5)),
 		new EmoteClue("Jump for joy at the Neitiznot rune rock. Equip Rune boots, a proselyte hauberk and a dragonstone ring.", "Fremennik Isles", NEAR_A_RUNITE_ROCK_IN_THE_FREMENNIK_ISLES, new WorldPoint(2375, 3850, 0), JUMP_FOR_JOY, item(RUNE_BOOTS), item(PROSELYTE_HAUBERK), item(DRAGONSTONE_RING)),
 		new EmoteClue("Jump for joy in the centre of Zul-Andra. Beware of double agents! Equip a dragon 2h sword, bandos boots and an obsidian cape.", "Zul-Andra", NEAR_THE_PIER_IN_ZULANDRA, new WorldPoint(2199, 3056, 0), DOUBLE_AGENT_141, JUMP_FOR_JOY, item(DRAGON_2H_SWORD), any("Bandos boots", item(BANDOS_BOOTS), item(GUARDIAN_BOOTS), item(ECHO_BOOTS)), item(OBSIDIAN_CAPE)),
-		new EmoteClue("Jump for Joy in the mine near the Twilight Temple. Bow before you talk to me. Equip a maple longbow, a ruby amulet and some steel platelegs.", "Twilight Temple", TWILIGHT_TEMPLE_MINE, new WorldPoint (1672, 3284, 0), JUMP_FOR_JOY, item(MAPLE_LONGBOW), item(RUBY_AMULET), item(STEEL_PLATELEGS)),
+		new EmoteClue("Jump for Joy in the mine near the Twilight Temple. Bow before you talk to me. Equip a maple longbow, a ruby amulet and some steel platelegs.", "Twilight Temple", TWILIGHT_TEMPLE_MINE, new WorldPoint (1672, 3284, 0), JUMP_FOR_JOY, BOW, item(MAPLE_LONGBOW), item(RUBY_AMULET), item(STEEL_PLATELEGS)),
 		new EmoteClue("Laugh by the fountain of heroes. Equip splitbark legs, dragon boots and a Rune longsword.", "Fountain of heroes", FOUNTAIN_OF_HEROES, new WorldPoint(2920, 9893, 0), LAUGH, item(SPLITBARK_LEGS), any("Dragon boots", item(DRAGON_BOOTS), item(DRAGON_BOOTS_G), item(PRIMORDIAL_BOOTS)), item(RUNE_LONGSWORD)),
 		new EmoteClue("Laugh in Jokul's tent in the Mountain Camp. Beware of double agents! Equip a rune full helmet, blue dragonhide chaps and a fire battlestaff.", "Mountain Camp", MOUNTAIN_CAMP_GOAT_ENCLOSURE, new WorldPoint(2812, 3681, 0), DOUBLE_AGENT_108, LAUGH, item(RUNE_FULL_HELM), item(BLUE_DHIDE_CHAPS), item(FIRE_BATTLESTAFF)),
 		new EmoteClue("Laugh at the crossroads south of the Sinclair Mansion. Equip a cowl, a blue wizard robe top and an iron scimitar.", "Sinclair Mansion", ROAD_JUNCTION_SOUTH_OF_SINCLAIR_MANSION, new WorldPoint(2741, 3536, 0), LAUGH, item(LEATHER_COWL), item(BLUE_WIZARD_ROBE), item(IRON_SCIMITAR)),
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdLocation.java
index 22632b4df..01926fe63 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdLocation.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdLocation.java
@@ -151,6 +151,7 @@ public enum HotColdLocation
 	VARLAMORE_BAZAAR(MASTER, new WorldPoint(1680, 3107, 0), VARLAMORE, "In the centre of the Bazaar in Civitas illa Fortis.", BRASSICAN_MAGE),
 	VARLAMORE_LOCUS_OASIS(MASTER, new WorldPoint(1695, 2990, 0), VARLAMORE, "Amongst the trees at the Locus Oasis.", BRASSICAN_MAGE),
 	VARLAMORE_SUNSET_COAST(MASTER, new WorldPoint(1534, 2997, 0), VARLAMORE, "North-east corner of Sunset Coast.", ANCIENT_WIZARDS),
+	VARLAMORE_VILLA_LUCENS(MASTER, new WorldPoint(1469, 2933, 0), VARLAMORE, "On the Villa Lucens theatre stage in Aldarin. Completion of Death on the Isle is required.", BRASSICAN_MAGE),
 	WESTERN_PROVINCE_EAGLES_PEAK(MASTER, new WorldPoint(2297, 3529, 0), WESTERN_PROVINCE, "North-west of Eagles' Peak.", BRASSICAN_MAGE),
 	WESTERN_PROVINCE_PISCATORIS(MASTER, new WorldPoint(2334, 3685, 0), WESTERN_PROVINCE, "Piscatoris Fishing Colony", ANCIENT_WIZARDS),
 	WESTERN_PROVINCE_PISCATORIS_HUNTER_AREA(MASTER, new WorldPoint(2359, 3564, 0), WESTERN_PROVINCE, "Eastern part of Piscatoris Hunter area, south-west of the Falconry.", BRASSICAN_MAGE),
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemWithCharge.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemWithCharge.java
index ed815bb78..774295aad 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemWithCharge.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemWithCharge.java
@@ -546,6 +546,22 @@ enum ItemWithCharge
 	FORGOTTEN_BR2(POTION, FORGOTTEN_BREW2, 2),
 	FORGOTTEN_BR3(POTION, FORGOTTEN_BREW3, 3),
 	FORGOTTEN_BR4(POTION, FORGOTTEN_BREW4, 4),
+	MENAPHITE_REM1(POTION, MENAPHITE_REMEDY1, 1),
+	MENAPHITE_REM2(POTION, MENAPHITE_REMEDY2, 2),
+	MENAPHITE_REM3(POTION, MENAPHITE_REMEDY3, 3),
+	MENAPHITE_REM4(POTION, MENAPHITE_REMEDY4, 4),
+	EXTENDED_ANTI_VENOM1(POTION, EXTENDED_ANTIVENOM1, 1),
+	EXTENDED_ANTI_VENOM2(POTION, EXTENDED_ANTIVENOM2, 2),
+	EXTENDED_ANTI_VENOM3(POTION, EXTENDED_ANTIVENOM3, 3),
+	EXTENDED_ANTI_VENOM4(POTION, EXTENDED_ANTIVENOM4, 4),
+	GOADING1(POTION, GOADING_POTION1, 1),
+	GOADING2(POTION, GOADING_POTION2, 2),
+	GOADING3(POTION, GOADING_POTION3, 3),
+	GOADING4(POTION, GOADING_POTION4, 4),
+	PRAYER_REGENERATION1(POTION, PRAYER_REGENERATION_POTION1, 1),
+	PRAYER_REGENERATION2(POTION, PRAYER_REGENERATION_POTION2, 2),
+	PRAYER_REGENERATION3(POTION, PRAYER_REGENERATION_POTION3, 3),
+	PRAYER_REGENERATION4(POTION, PRAYER_REGENERATION_POTION4, 4),
 	;
 
 	private final ItemChargeType type;
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatChanges.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatChanges.java
index e526a7be6..eab1a0a39 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatChanges.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatChanges.java
@@ -34,9 +34,13 @@ import static net.runelite.api.ItemID.*;
 import static net.runelite.client.plugins.itemstats.Builders.*;
 import net.runelite.client.plugins.itemstats.delta.DeltaPercentage;
 import net.runelite.client.plugins.itemstats.food.Anglerfish;
+import net.runelite.client.plugins.itemstats.food.CookedBream;
+import net.runelite.client.plugins.itemstats.food.CookedMossLizard;
+import net.runelite.client.plugins.itemstats.special.SunlightMoth;
 import net.runelite.client.plugins.itemstats.potions.Ambrosia;
 import net.runelite.client.plugins.itemstats.potions.AncientBrew;
 import net.runelite.client.plugins.itemstats.potions.MixedPotion;
+import net.runelite.client.plugins.itemstats.potions.MoonlightPotion;
 import net.runelite.client.plugins.itemstats.potions.PrayerPotion;
 import net.runelite.client.plugins.itemstats.potions.SaradominBrew;
 import net.runelite.client.plugins.itemstats.potions.StaminaPotion;
@@ -56,6 +60,13 @@ public class ItemStatChanges
 		init();
 	}
 
+	public static final SingleEffect ATTACK_POT = boost(ATTACK, perc(.10, 3));
+	public static final SingleEffect STRENGTH_POT = boost(STRENGTH, perc(.10, 3));
+	public static final SingleEffect DEFENCE_POT = boost(DEFENCE, perc(.10, 3));
+	public static final SingleEffect SUPER_ATTACK_POT = boost(ATTACK, perc(.15, 5));
+	public static final SingleEffect SUPER_STRENGTH_POT = boost(STRENGTH, perc(.15, 5));
+	public static final SingleEffect SUPER_DEFENCE_POT = boost(DEFENCE, perc(.15, 5));
+
 	private void init()
 	{
 		add(food(-5), POISON_KARAMBWAN);
@@ -105,6 +116,8 @@ public class ItemStatChanges
 		add(food(maxHP -> (int) Math.ceil(maxHP * .06)), STRAWBERRY);
 		add(food(maxHP -> (int) Math.ceil(maxHP * .05)), WATERMELON_SLICE);
 		add(food(perc(.1, 1)), COOKED_SWEETCORN, SWEETCORN_7088 /* Bowl of cooked sweetcorn */);
+		add(new CookedBream(), COOKED_BREAM);
+		add(new CookedMossLizard(), COOKED_MOSS_LIZARD);
 		add(combo(food(1), boost(DEFENCE, perc(.02, 1))), CABBAGE_1967 /* Draynor Manor */);
 		add(combo(food(8), heal(RUN_ENERGY, 5)), PAPAYA_FRUIT);
 		add(combo(food(3), boost(ATTACK, perc(.02, 2))), CUP_OF_TEA_1978 /* Standard tea */);
@@ -168,6 +181,12 @@ public class ItemStatChanges
 		add(combo(food(2), heal(PRAYER, perc(.04, -2))), BLOODY_BRACER);
 		add(combo(food(1), boost(AGILITY, 1), heal(STRENGTH, -1)), ELVEN_DAWN);
 		add(combo(boost(RANGED, 4), new BoostedStatBoost(STRENGTH, false, perc(.04, -2)), new BoostedStatBoost(MAGIC, false, perc(.04, -2))), LIZARDKICKER);
+		add(combo(food(1), boost(HUNTER, 2), dec(STRENGTH, 1), dec(ATTACK, 2)), TRAPPERS_TIPPLE);
+		add(combo(food(1), boost(AGILITY, 1), boost(STRENGTH, 1), new BoostedStatBoost(ATTACK, false, perc(.05, -1))), SUNBEAM_ALE);
+		add(combo(food(5), boost(STRENGTH, perc(.05, 1)), new BoostedStatBoost(ATTACK, false, perc(0.02, -3))), MOONLITE);
+		add(combo(food(5), boost(STRENGTH, perc(.05, 1)), new BoostedStatBoost(ATTACK, false, perc(0.02, -3))), SUNSHINE);
+		add(combo(food(5), boost(STRENGTH, 5), new BoostedStatBoost(ATTACK, false, perc(.02, -3))), RUM_28896);
+		add(combo(food(16), boost(WOODCUTTING, 1), heal(ATTACK, -5), heal(FLETCHING, -1)), ECLIPSE_RED);
 
 		// Sq'irk Juice
 		add(heal(RUN_ENERGY, 5), WINTER_SQIRKJUICE);
@@ -176,73 +195,68 @@ public class ItemStatChanges
 		add(combo(heal(RUN_ENERGY, 20), boost(THIEVING, 3)), SUMMER_SQIRKJUICE);
 
 		// Combat potions
-		final SingleEffect attackPot = boost(ATTACK, perc(.10, 3));
-		final SingleEffect strengthPot = boost(STRENGTH, perc(.10, 3));
-		final SingleEffect defencePot = boost(DEFENCE, perc(.10, 3));
-		final Effect combatPot = combo(attackPot, strengthPot);
+		final Effect combatPot = combo(ATTACK_POT, STRENGTH_POT);
 		final Effect magicEssence = boost(MAGIC, 3);
 		final SingleEffect magicPot = boost(MAGIC, 4);
 		final SingleEffect imbuedHeart = boost(MAGIC, perc(.10, 1));
 		final SingleEffect rangingPot = boost(RANGED, perc(.10, 4));
-		final SingleEffect superAttackPot = boost(ATTACK, perc(.15, 5));
-		final SingleEffect superStrengthPot = boost(STRENGTH, perc(.15, 5));
-		final SingleEffect superDefencePot = boost(DEFENCE, perc(.15, 5));
 		final SingleEffect superMagicPot = boost(MAGIC, perc(.15, 5));
 		final SingleEffect superRangingPot = boost(RANGED, perc(.15, 5));
 		final SingleEffect divinePot = heal(HITPOINTS, -10);
 		final Effect zamorakBrew = combo(boost(ATTACK, perc(.20, 2)), boost(STRENGTH, perc(.12, 2)), heal(PRAYER, perc(.10, 0)), new BoostedStatBoost(DEFENCE, false, perc(.10, -2)), new BoostedStatBoost(HITPOINTS, false, perc(-.12, 0)));
 		final Effect ancientBrew = new AncientBrew(.05, 2);
-		add(attackPot, ATTACK_POTION1, ATTACK_POTION2, ATTACK_POTION3, ATTACK_POTION4);
-		add(strengthPot, STRENGTH_POTION1, STRENGTH_POTION2, STRENGTH_POTION3, STRENGTH_POTION4);
-		add(defencePot, DEFENCE_POTION1, DEFENCE_POTION2, DEFENCE_POTION3, DEFENCE_POTION4);
+		add(ATTACK_POT, ATTACK_POTION1, ATTACK_POTION2, ATTACK_POTION3, ATTACK_POTION4);
+		add(STRENGTH_POT, STRENGTH_POTION1, STRENGTH_POTION2, STRENGTH_POTION3, STRENGTH_POTION4);
+		add(DEFENCE_POT, DEFENCE_POTION1, DEFENCE_POTION2, DEFENCE_POTION3, DEFENCE_POTION4);
 		add(magicPot, MAGIC_POTION1, MAGIC_POTION2, MAGIC_POTION3, MAGIC_POTION4);
 		add(rangingPot, RANGING_POTION1, RANGING_POTION2, RANGING_POTION3, RANGING_POTION4,
 			RANGING_POTION4_23551, RANGING_POTION3_23553, RANGING_POTION2_23555, RANGING_POTION1_23557 /* LMS */);
 		add(combatPot, COMBAT_POTION1, COMBAT_POTION2, COMBAT_POTION3, COMBAT_POTION4,
 			COMBAT_POTION4_26150, COMBAT_POTION3_26151, COMBAT_POTION2_26152, COMBAT_POTION1_26153 /* Deadman starter pack */);
-		add(superAttackPot, SUPER_ATTACK1, SUPER_ATTACK2, SUPER_ATTACK3, SUPER_ATTACK4);
-		add(superStrengthPot, SUPER_STRENGTH1, SUPER_STRENGTH2, SUPER_STRENGTH3, SUPER_STRENGTH4);
-		add(superDefencePot, SUPER_DEFENCE1, SUPER_DEFENCE2, SUPER_DEFENCE3, SUPER_DEFENCE4);
+		add(SUPER_ATTACK_POT, SUPER_ATTACK1, SUPER_ATTACK2, SUPER_ATTACK3, SUPER_ATTACK4);
+		add(SUPER_STRENGTH_POT, SUPER_STRENGTH1, SUPER_STRENGTH2, SUPER_STRENGTH3, SUPER_STRENGTH4);
+		add(SUPER_DEFENCE_POT, SUPER_DEFENCE1, SUPER_DEFENCE2, SUPER_DEFENCE3, SUPER_DEFENCE4);
 		add(magicEssence, MAGIC_ESSENCE1, MAGIC_ESSENCE2, MAGIC_ESSENCE3, MAGIC_ESSENCE4);
-		add(combo(superAttackPot, superStrengthPot, superDefencePot), SUPER_COMBAT_POTION1, SUPER_COMBAT_POTION2, SUPER_COMBAT_POTION3, SUPER_COMBAT_POTION4);
+		add(combo(SUPER_ATTACK_POT, SUPER_STRENGTH_POT, SUPER_DEFENCE_POT), SUPER_COMBAT_POTION1, SUPER_COMBAT_POTION2, SUPER_COMBAT_POTION3, SUPER_COMBAT_POTION4);
 		add(zamorakBrew, ZAMORAK_BREW1, ZAMORAK_BREW2, ZAMORAK_BREW3, ZAMORAK_BREW4);
 		add(new SaradominBrew(0.15, 0.2, 0.1, 2, 2), SARADOMIN_BREW1, SARADOMIN_BREW2, SARADOMIN_BREW3,
 			SARADOMIN_BREW4, SARADOMIN_BREW4_23575, SARADOMIN_BREW3_23577, SARADOMIN_BREW2_23579, SARADOMIN_BREW1_23581 /* LMS */);
 		add(superRangingPot, SUPER_RANGING_1, SUPER_RANGING_2, SUPER_RANGING_3, SUPER_RANGING_4);
 		add(superMagicPot, SUPER_MAGIC_POTION_1, SUPER_MAGIC_POTION_2, SUPER_MAGIC_POTION_3, SUPER_MAGIC_POTION_4);
-		add(combo(rangingPot, superDefencePot), BASTION_POTION1, BASTION_POTION2, BASTION_POTION3, BASTION_POTION4);
-		add(combo(magicPot, superDefencePot), BATTLEMAGE_POTION1, BATTLEMAGE_POTION2, BATTLEMAGE_POTION3, BATTLEMAGE_POTION4);
+		add(combo(rangingPot, SUPER_DEFENCE_POT), BASTION_POTION1, BASTION_POTION2, BASTION_POTION3, BASTION_POTION4);
+		add(combo(magicPot, SUPER_DEFENCE_POT), BATTLEMAGE_POTION1, BATTLEMAGE_POTION2, BATTLEMAGE_POTION3, BATTLEMAGE_POTION4);
 		add(combo(magicPot, divinePot), DIVINE_MAGIC_POTION1, DIVINE_MAGIC_POTION2, DIVINE_MAGIC_POTION3, DIVINE_MAGIC_POTION4);
 		add(combo(rangingPot, divinePot), DIVINE_RANGING_POTION1, DIVINE_RANGING_POTION2, DIVINE_RANGING_POTION3, DIVINE_RANGING_POTION4);
-		add(combo(superAttackPot, divinePot), DIVINE_SUPER_ATTACK_POTION1, DIVINE_SUPER_ATTACK_POTION2, DIVINE_SUPER_ATTACK_POTION3, DIVINE_SUPER_ATTACK_POTION4);
-		add(combo(superStrengthPot, divinePot), DIVINE_SUPER_STRENGTH_POTION1, DIVINE_SUPER_STRENGTH_POTION2, DIVINE_SUPER_STRENGTH_POTION3, DIVINE_SUPER_STRENGTH_POTION4);
-		add(combo(superDefencePot, divinePot), DIVINE_SUPER_DEFENCE_POTION1, DIVINE_SUPER_DEFENCE_POTION2, DIVINE_SUPER_DEFENCE_POTION3, DIVINE_SUPER_DEFENCE_POTION4);
-		add(combo(superAttackPot, superStrengthPot, superDefencePot, divinePot), DIVINE_SUPER_COMBAT_POTION1, DIVINE_SUPER_COMBAT_POTION2, DIVINE_SUPER_COMBAT_POTION3, DIVINE_SUPER_COMBAT_POTION4);
-		add(combo(rangingPot, superDefencePot, divinePot), DIVINE_BASTION_POTION1, DIVINE_BASTION_POTION2, DIVINE_BASTION_POTION3, DIVINE_BASTION_POTION4);
-		add(combo(magicPot, superDefencePot, divinePot), DIVINE_BATTLEMAGE_POTION1, DIVINE_BATTLEMAGE_POTION2, DIVINE_BATTLEMAGE_POTION3, DIVINE_BATTLEMAGE_POTION4);
-		add(combo(superAttackPot, superStrengthPot, superDefencePot, rangingPot, imbuedHeart),
+		add(combo(SUPER_ATTACK_POT, divinePot), DIVINE_SUPER_ATTACK_POTION1, DIVINE_SUPER_ATTACK_POTION2, DIVINE_SUPER_ATTACK_POTION3, DIVINE_SUPER_ATTACK_POTION4);
+		add(combo(SUPER_STRENGTH_POT, divinePot), DIVINE_SUPER_STRENGTH_POTION1, DIVINE_SUPER_STRENGTH_POTION2, DIVINE_SUPER_STRENGTH_POTION3, DIVINE_SUPER_STRENGTH_POTION4);
+		add(combo(SUPER_DEFENCE_POT, divinePot), DIVINE_SUPER_DEFENCE_POTION1, DIVINE_SUPER_DEFENCE_POTION2, DIVINE_SUPER_DEFENCE_POTION3, DIVINE_SUPER_DEFENCE_POTION4);
+		add(combo(SUPER_ATTACK_POT, SUPER_STRENGTH_POT, SUPER_DEFENCE_POT, divinePot), DIVINE_SUPER_COMBAT_POTION1, DIVINE_SUPER_COMBAT_POTION2, DIVINE_SUPER_COMBAT_POTION3, DIVINE_SUPER_COMBAT_POTION4);
+		add(combo(rangingPot, SUPER_DEFENCE_POT, divinePot), DIVINE_BASTION_POTION1, DIVINE_BASTION_POTION2, DIVINE_BASTION_POTION3, DIVINE_BASTION_POTION4);
+		add(combo(magicPot, SUPER_DEFENCE_POT, divinePot), DIVINE_BATTLEMAGE_POTION1, DIVINE_BATTLEMAGE_POTION2, DIVINE_BATTLEMAGE_POTION3, DIVINE_BATTLEMAGE_POTION4);
+		add(combo(SUPER_ATTACK_POT, SUPER_STRENGTH_POT, SUPER_DEFENCE_POT, rangingPot, imbuedHeart),
 			CASTLEWARS_BREW4, CASTLEWARS_BREW3, CASTLEWARS_BREW2, CASTLEWARS_BREW1);
-		add(combo(superAttackPot, superStrengthPot),
+		add(combo(SUPER_ATTACK_POT, SUPER_STRENGTH_POT),
 			SUPER_COMBAT_POTION4_23543, SUPER_COMBAT_POTION3_23545, SUPER_COMBAT_POTION2_23547, SUPER_COMBAT_POTION1_23549 /* LMS */);
 		add(ancientBrew, ANCIENT_BREW1, ANCIENT_BREW2, ANCIENT_BREW3, ANCIENT_BREW4);
 		add(new AncientBrew(.08, 3), FORGOTTEN_BREW1, FORGOTTEN_BREW2, FORGOTTEN_BREW3, FORGOTTEN_BREW4);
+		add(new MoonlightPotion(), MOONLIGHT_POTION1, MOONLIGHT_POTION2, MOONLIGHT_POTION3, MOONLIGHT_POTION4);
 
 		// Mixed combat potions
-		add(new MixedPotion(3, attackPot), ATTACK_MIX1, ATTACK_MIX2);
-		add(new MixedPotion(3, strengthPot), STRENGTH_MIX1, STRENGTH_MIX2);
+		add(new MixedPotion(3, ATTACK_POT), ATTACK_MIX1, ATTACK_MIX2);
+		add(new MixedPotion(3, STRENGTH_POT), STRENGTH_MIX1, STRENGTH_MIX2);
 		add(new MixedPotion(3, combatPot), COMBAT_MIX1, COMBAT_MIX2);
-		add(new MixedPotion(6, defencePot), DEFENCE_MIX1, DEFENCE_MIX2);
+		add(new MixedPotion(6, DEFENCE_POT), DEFENCE_MIX1, DEFENCE_MIX2);
 		add(new MixedPotion(6, magicPot), MAGIC_MIX1, MAGIC_MIX2);
 		add(new MixedPotion(6, rangingPot), RANGING_MIX1, RANGING_MIX2);
-		add(new MixedPotion(6, superAttackPot), SUPERATTACK_MIX1, SUPERATTACK_MIX2);
-		add(new MixedPotion(6, superStrengthPot), SUPER_STR_MIX1, SUPER_STR_MIX2);
-		add(new MixedPotion(6, superDefencePot), SUPER_DEF_MIX1, SUPER_DEF_MIX2);
+		add(new MixedPotion(6, SUPER_ATTACK_POT), SUPERATTACK_MIX1, SUPERATTACK_MIX2);
+		add(new MixedPotion(6, SUPER_STRENGTH_POT), SUPER_STR_MIX1, SUPER_STR_MIX2);
+		add(new MixedPotion(6, SUPER_DEFENCE_POT), SUPER_DEF_MIX1, SUPER_DEF_MIX2);
 		add(new MixedPotion(6, magicEssence), MAGIC_ESSENCE_MIX1, MAGIC_ESSENCE_MIX2);
 		add(new MixedPotion(6, zamorakBrew), ZAMORAK_MIX1, ZAMORAK_MIX2);
 		add(new MixedPotion(6, ancientBrew), ANCIENT_MIX1, ANCIENT_MIX2);
 
 		// Regular overload (NMZ)
-		add(combo(superAttackPot, superStrengthPot, superDefencePot, superRangingPot, superMagicPot, heal(HITPOINTS, -50)), OVERLOAD_1, OVERLOAD_2, OVERLOAD_3, OVERLOAD_4);
+		add(combo(SUPER_ATTACK_POT, SUPER_STRENGTH_POT, SUPER_DEFENCE_POT, superRangingPot, superMagicPot, heal(HITPOINTS, -50)), OVERLOAD_1, OVERLOAD_2, OVERLOAD_3, OVERLOAD_4);
 
 		// Bandages (Castle Wars)
 		add(new CastleWarsBandage(), BANDAGES);
@@ -358,6 +372,15 @@ public class ItemStatChanges
 		add(combo(heal(HITPOINTS, perc(.15, 1)), heal(RUN_ENERGY, 100)), BANDAGES_25202);
 		add(combo(boost(ATTACK, perc(.15, 5)), boost(STRENGTH, perc(.15, 5)), boost(DEFENCE, perc(.15, 5)), boost(RANGED, perc(.15, 5)), boost(MAGIC, perc(.15, 5)), heal(PRAYER, perc(.25, 8))), POTION_OF_POWER1, POTION_OF_POWER2, POTION_OF_POWER3, POTION_OF_POWER4);
 
+		// Moths & Butterflies
+		add(new SunlightMoth(.2, 6), SUNLIGHT_MOTH_28890, SUNLIGHT_MOTH_MIX_1, SUNLIGHT_MOTH_MIX_2);
+		add(heal(PRAYER, 22), MOONLIGHT_MOTH_28893, MOONLIGHT_MOTH_MIX_1, MOONLIGHT_MOTH_MIX_2);
+		add(heal(HITPOINTS, 15), SNOWY_KNIGHT);
+		add(heal(HITPOINTS, 8), SNOWY_KNIGHT_MIX_1, SNOWY_KNIGHT_MIX_2);
+		add(boost(ATTACK, perc(.15, 4)), RUBY_HARVEST, RUBY_HARVEST_MIX_1, RUBY_HARVEST_MIX_2);
+		add(boost(STRENGTH, perc(.15, 4)), BLACK_WARLOCK, BLACK_WARLOCK_MIX_1, BLACK_WARLOCK_MIX_2);
+		add(boost(DEFENCE, perc(.15, 4)), SAPPHIRE_GLACIALIS, SAPPHIRE_GLACIALIS_MIX_1, SAPPHIRE_GLACIALIS_MIX_2);
+
 		log.debug("{} items; {} behaviours loaded", effects.size(), new HashSet<>(effects.values()).size());
 	}
 
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/food/CookedBream.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/food/CookedBream.java
new file mode 100644
index 000000000..bd725dccc
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/food/CookedBream.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2024 Macweese
+ * 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.itemstats.food;
+
+import net.runelite.api.Client;
+import net.runelite.client.plugins.itemstats.FoodBase;
+import static net.runelite.client.plugins.itemstats.stats.Stats.COOKING;
+import static net.runelite.client.plugins.itemstats.stats.Stats.FISHING;
+
+public class CookedBream extends FoodBase
+{
+	@Override
+	public int heals(Client client)
+	{
+		int cooking = COOKING.getValue(client) / 3;
+		int fishing = FISHING.getValue(client) / 3;
+		return Math.max(7, Math.min(cooking, fishing));
+	}
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/food/CookedMossLizard.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/food/CookedMossLizard.java
new file mode 100644
index 000000000..6103028de
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/food/CookedMossLizard.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2024 Macweese
+ * 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.itemstats.food;
+
+import net.runelite.api.Client;
+import net.runelite.client.plugins.itemstats.FoodBase;
+import static net.runelite.client.plugins.itemstats.stats.Stats.COOKING;
+import static net.runelite.client.plugins.itemstats.stats.Stats.HUNTER;
+
+public class CookedMossLizard extends FoodBase
+{
+	@Override
+	public int heals(Client client)
+	{
+		int cooking = COOKING.getValue(client) / 3;
+		int hunter = HUNTER.getValue(client) / 2;
+		return Math.min(cooking, hunter);
+	}
+}
\ No newline at end of file
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/potions/MoonlightPotion.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/potions/MoonlightPotion.java
new file mode 100644
index 000000000..b3230328d
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/potions/MoonlightPotion.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2024 Macweese
+ * 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.itemstats.potions;
+
+import java.util.ArrayList;
+import java.util.Optional;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import net.runelite.api.Client;
+import net.runelite.api.Skill;
+import net.runelite.client.plugins.itemstats.Effect;
+import net.runelite.client.plugins.itemstats.ItemStatChanges;
+import net.runelite.client.plugins.itemstats.SimpleStatBoost;
+import net.runelite.client.plugins.itemstats.SingleEffect;
+import net.runelite.client.plugins.itemstats.StatBoost;
+import net.runelite.client.plugins.itemstats.StatChange;
+import net.runelite.client.plugins.itemstats.StatsChanges;
+import net.runelite.client.plugins.itemstats.delta.DeltaPercentage;
+import net.runelite.client.plugins.itemstats.stats.Stat;
+import java.util.Arrays;
+import java.util.Comparator;
+import static net.runelite.client.plugins.itemstats.stats.Stats.ATTACK;
+import static net.runelite.client.plugins.itemstats.stats.Stats.DEFENCE;
+import static net.runelite.client.plugins.itemstats.stats.Stats.HERBLORE;
+import static net.runelite.client.plugins.itemstats.stats.Stats.PRAYER;
+import static net.runelite.client.plugins.itemstats.stats.Stats.STRENGTH;
+
+@RequiredArgsConstructor
+public class MoonlightPotion implements Effect
+{
+	@Override
+	public StatsChanges calculate(Client client)
+	{
+		StatsChanges changes = new StatsChanges(4);
+		ArrayList<StatChange> statChanges = new ArrayList<>();
+
+		getStatChange(client, ATTACK).ifPresent(e -> statChanges.add(e.getEffect().effect(client)));
+		getStatChange(client, DEFENCE).ifPresent(e -> statChanges.add(e.getEffect().effect(client)));
+		getStatChange(client, STRENGTH).ifPresent(e -> statChanges.add(e.getEffect().effect(client)));
+		getStatChange(client, PRAYER).ifPresent(e -> statChanges.add(e.getEffect().effect(client)));
+
+		changes.setStatChanges(statChanges.toArray(StatChange[]::new));
+		return changes;
+	}
+
+	private Optional<StatEffect> getStatChange(Client client, Stat stat)
+	{
+		return Arrays.stream(StatEffect.values())
+			.filter(t -> t.getStat() == stat && client.getBoostedSkillLevel(Skill.HERBLORE) >= t.getLevelRequirement())
+			.max(Comparator.comparingInt(StatEffect::getLevelRequirement));
+	}
+}
+
+@Getter
+enum StatEffect
+{
+	ATTACK_POTION(ATTACK, 3, ItemStatChanges.ATTACK_POT),
+	DEFENCE_POTION(DEFENCE, 30, ItemStatChanges.DEFENCE_POT),
+	// Misnomer: Moonlight potion gives a stronger effect than Super Defence on defence but requires the level for,
+	// and activates the var/effect timer for—albeit without the drain prevention from—Divine Super Defence potion
+	DIVINE_SUPER_DEFENCE_POTION(DEFENCE, 70, new SimpleStatBoost(DEFENCE, true, new DeltaPercentage(.2, 7))),
+	STRENGTH_POTION(STRENGTH, 12, ItemStatChanges.STRENGTH_POT),
+	SUPER_ATTACK_POTION(ATTACK, 45, ItemStatChanges.SUPER_ATTACK_POT),
+	SUPER_DEFENCE_POTION(DEFENCE, 66, ItemStatChanges.SUPER_DEFENCE_POT),
+	SUPER_STRENGTH_POTION(STRENGTH, 55, ItemStatChanges.SUPER_STRENGTH_POT),
+	PRAYER_POTION(PRAYER, 38, new StatBoost(PRAYER, false)
+	{
+		@Override
+		public int heals(Client client)
+		{
+			int pray = PRAYER.getMaximum(client);
+			int herb = HERBLORE.getValue(client);
+			return herb < 38 ? 0 : (int) Math.max(pray * 0.25, herb * 0.3) + 7;
+		}
+	}),
+	;
+
+	private final Stat stat;
+	private final int levelRequirement;
+	private final SingleEffect effect;
+
+	StatEffect(Stat stat, int levelRequirement, SingleEffect effect)
+	{
+		this.stat = stat;
+		this.levelRequirement = levelRequirement;
+		this.effect = effect;
+	}
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/special/SunlightMoth.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/special/SunlightMoth.java
new file mode 100644
index 000000000..d99e05c53
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/special/SunlightMoth.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2024, Macweese
+ * 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.itemstats.special;
+
+import net.runelite.api.Client;
+import net.runelite.client.plugins.itemstats.Effect;
+import net.runelite.client.plugins.itemstats.SimpleStatBoost;
+import net.runelite.client.plugins.itemstats.StatChange;
+import net.runelite.client.plugins.itemstats.StatsChanges;
+import net.runelite.client.plugins.itemstats.stats.Stat;
+import java.util.Comparator;
+import java.util.stream.Stream;
+import static net.runelite.client.plugins.itemstats.Builders.perc;
+import static net.runelite.client.plugins.itemstats.stats.Stats.*;
+
+public class SunlightMoth implements Effect
+{
+	private static final Stat[] RESTORED_STATS = {
+		ATTACK, DEFENCE, STRENGTH, RANGED, MAGIC, COOKING,
+		WOODCUTTING, FLETCHING, FISHING, FIREMAKING, CRAFTING, SMITHING, MINING,
+		HERBLORE, AGILITY, THIEVING, SLAYER, FARMING, RUNECRAFT, HUNTER,
+		CONSTRUCTION
+	};
+
+	public final double percentRestored;
+	private final int delta;
+
+	public SunlightMoth(double percentRestored, int delta)
+	{
+		this.percentRestored = percentRestored;
+		this.delta = delta;
+	}
+
+	@Override
+	public StatsChanges calculate(Client client)
+	{
+		StatsChanges changes = new StatsChanges(0);
+
+		SimpleStatBoost calcRestore = new SimpleStatBoost(null, false, perc(percentRestored, delta));
+		SimpleStatBoost calcHeal = new SimpleStatBoost(HITPOINTS, false, perc(0, 8));
+
+		changes.setStatChanges(Stream.concat(
+			Stream.of(new Stat[]{HITPOINTS})
+				.map(stat -> calcHeal.effect(client)),
+			Stream.of(RESTORED_STATS)
+				.filter(stat -> stat.getValue(client) < stat.getMaximum(client))
+				.map(stat ->
+				{
+					calcRestore.setStat(stat);
+					return calcRestore.effect(client);
+				})
+		).toArray(StatChange[]::new));
+		changes.setPositivity(Stream.of(changes.getStatChanges())
+				.map(StatChange::getPositivity)
+				.max(Comparator.naturalOrder()).orElse(null));
+		return changes;
+	}
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/prayer/PrayerReorder.java b/runelite-client/src/main/java/net/runelite/client/plugins/prayer/PrayerReorder.java
index 698250a67..1a170ee5b 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/prayer/PrayerReorder.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/prayer/PrayerReorder.java
@@ -50,6 +50,8 @@ import net.runelite.api.widgets.InterfaceID;
 import net.runelite.api.widgets.Widget;
 import static net.runelite.api.widgets.WidgetConfig.DRAG;
 import static net.runelite.api.widgets.WidgetConfig.DRAG_ON;
+import net.runelite.api.widgets.WidgetSizeMode;
+import net.runelite.api.widgets.WidgetType;
 import net.runelite.api.widgets.WidgetUtil;
 import net.runelite.client.callback.ClientThread;
 import net.runelite.client.chat.ChatMessageManager;
@@ -407,6 +409,8 @@ class PrayerReorder
 
 				++index;
 			}
+
+			createWarning(unlocked);
 		}
 
 		if (isInterfaceOpen(InterfaceID.QUICK_PRAYER))
@@ -455,6 +459,23 @@ class PrayerReorder
 		}
 	}
 
+	private void createWarning(boolean unlocked)
+	{
+		Widget w = client.getWidget(ComponentID.PRAYER_PARENT);
+		w.deleteAllChildren();
+
+		if (unlocked)
+		{
+			Widget c = w.createChild(WidgetType.RECTANGLE);
+			c.setHeightMode(WidgetSizeMode.MINUS);
+			c.setWidthMode(WidgetSizeMode.MINUS);
+			c.setTextColor(0xff0000);
+			c.setFilled(true);
+			c.setOpacity(220);
+			c.revalidate();
+		}
+	}
+
 	private boolean isInterfaceOpen(@Interface int interfaceId)
 	{
 		return client.getWidget(interfaceId, 0) != null;
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/AgilityAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/AgilityAction.java
index 383958503..ed6d2dcd0 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/AgilityAction.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/AgilityAction.java
@@ -42,7 +42,7 @@ public enum AgilityAction implements NamedSkillAction
 {
 	GNOME_STRONGHOLD_COURSE("Gnome Stronghold", 1, 110.5f, ItemID.SWAMP_TOAD),
 	DRAYNOR_VILLAGE_ROOFTOP("Draynor Village Rooftop", 1, 120, ItemID.MARK_OF_GRACE),
-	SHAYZIEN_BASIC_COURSE("Shayzien Basic Course", 1, 153.5f, ItemID.SHAYZIEN_HELM_1),
+	SHAYZIEN_BASIC_COURSE("Shayzien Basic Course", 1, 153.5f, ItemID.SHAYZIEN_BANNER),
 	LEAPING_TROUT("Leaping trout", 15, 5, ItemID.LEAPING_TROUT),
 	AL_KHARID_ROOFTOP("Al Kharid Rooftop", 20, 216, ItemID.MARK_OF_GRACE),
 	LEAPING_SALMON("Leaping salmon", 30, 6, ItemID.LEAPING_SALMON),
@@ -51,16 +51,16 @@ public enum AgilityAction implements NamedSkillAction
 	BARBARIAN_OUTPOST("Barbarian Outpost", 35, 152.5f, ItemID.STEEL_BATTLEAXE),
 	CANIFIS_ROOFTOP("Canifis Rooftop", 40, 240, ItemID.MARK_OF_GRACE),
 	LEAPING_STURGEON("Leaping sturgeon", 45, 7, ItemID.LEAPING_STURGEON),
-	SHAYZIEN_ADVANCED_COURSE("Shayzien Advanced Course", 45, 508, ItemID.SHAYZIEN_HELM_5),
+	SHAYZIEN_ADVANCED_COURSE("Shayzien Advanced Course", 45, 508, ItemID.SHAYZIEN_BANNER),
 	APE_ATOLL_COURSE("Ape Atoll", 48, 580, ItemID.GORILLA_GREEGREE),
-	COLOSSAL_WYRM_BASIC_COURSE("Colossal Wyrm Basic", 50, 504, ItemID.TERMITES),
+	COLOSSAL_WYRM_BASIC_COURSE("Colossal Wyrm Basic", 50, 504.1f, ItemID.TERMITES),
 	FALADOR_ROOFTOP("Falador Rooftop", 50, 586, ItemID.MARK_OF_GRACE),
 	WILDERNESS_AGILITY_COURSE_TICKET("Wilderness Agility Ticket", 52, 200, ItemID.WILDERNESS_AGILITY_TICKET),
 	WILDERNESS_AGILITY_COURSE("Wilderness Agility Course", 52, 571.4f, ItemID.SKULL),
 	HALLOWED_SEPULCHRE_FLOOR_1("Hallowed Sepulchre Floor 1", 52, 575, ItemID.RING_OF_ENDURANCE),
 	SEERS_VILLAGE_ROOFTOP("Seers' Village Rooftop", 60, 570, ItemID.MARK_OF_GRACE),
 	WEREWOLF_AGILITY_COURSE("Werewolf Agility Course", 60, 730, ItemID.STICK),
-	COLOSSAL_WYRM_ADVANCED_COURSE("Colossal Wyrm Advanced", 62, 685, ItemID.BONE_SQUIRREL),
+	COLOSSAL_WYRM_ADVANCED_COURSE("Colossal Wyrm Advanced", 62, 749.6f, ItemID.TERMITES),
 	HALLOWED_SEPULCHRE_FLOOR_2("Hallowed Sepulchre Floor 2", 62, 925, ItemID.RING_OF_ENDURANCE),
 	POLLNIVNEACH_ROOFTOP("Pollnivneach Rooftop", 70, 890, ItemID.MARK_OF_GRACE),
 	DORGESH_KAAN_AGILITY_COURSE("Dorgesh-Kaan Agility Course", 70, 2750, ItemID.POWERBOX),
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/HerbloreAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/HerbloreAction.java
index 985867b35..f56a58e47 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/HerbloreAction.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/HerbloreAction.java
@@ -71,47 +71,47 @@ public enum HerbloreAction implements ItemSkillAction
 	HUASCA(ItemID.HUASCA, 58, 11.8f),
 	PRAYER_REGENERATION_POTION(ItemID.PRAYER_REGENERATION_POTION3, 58, 132),
 	SNAPDRAGON(ItemID.SNAPDRAGON, 59, 11.8f),
-	MAMMOTHMIGHT_MIX(ItemID.MAMMOTHMIGHT_MIX, 60, 135),
 	WEAPON_POISON(ItemID.WEAPON_POISON, 60, 137.5f),
-	MYSTIC_MANA_AMALGAM(ItemID.MYSTIC_MANA_AMALGAM, 60, 175),
-	MARLEYS_MOONLIGHT(ItemID.MARLEYS_MOONLIGHT, 60, 215),
+	ALCOAUGMENTATOR(ItemID.ALCOAUGMENTATOR, 60, 190),
+	LIPLACK_LIQUOR(ItemID.LIPLACK_LIQUOR, 60, 190),
+	MAMMOTHMIGHT_MIX(ItemID.MAMMOTHMIGHT_MIX, 60, 190),
 	SUPER_RESTORE_3(ItemID.SUPER_RESTORE3, 63, 142.5f),
-	MIXALOT(ItemID.MIXALOT, 64, 255),
+	MYSTIC_MANA_AMALGAM(ItemID.MYSTIC_MANA_AMALGAM, 63, 215),
 	CADANTINE(ItemID.CADANTINE, 65, 12.5f),
 	SANFEW_SERUM_3(ItemID.SANFEW_SERUM3, 65, 160),
 	SUPER_DEFENCE_3(ItemID.SUPER_DEFENCE3, 66, 150),
+	MARLEYS_MOONLIGHT(ItemID.MARLEYS_MOONLIGHT, 66, 240),
 	LANTADYME(ItemID.LANTADYME, 67, 13.1f),
 	ANTIDOTE_PLUS_4(ItemID.ANTIDOTE4, 68, 155),
-	AZURE_AURA_MIX(ItemID.AZURE_AURA_MIX, 68, 215),
 	ANTIFIRE_POTION_3(ItemID.ANTIFIRE_POTION3, 69, 157.5f),
+	AZURE_AURA_MIX(ItemID.AZURE_AURA_MIX, 69, 265),
 	DIVINE_SUPER_ATTACK_POTION_4(ItemID.DIVINE_SUPER_ATTACK_POTION4, 70, 2),
 	DIVINE_SUPER_DEFENCE_POTION_4(ItemID.DIVINE_SUPER_DEFENCE_POTION4, 70, 2),
 	DIVINE_SUPER_STRENGTH_POTION_4(ItemID.DIVINE_SUPER_STRENGTH_POTION4, 70, 2),
 	DWARF_WEED(ItemID.DWARF_WEED, 70, 13.8f),
 	RANGING_POTION_3(ItemID.RANGING_POTION3, 72, 162.5f),
-	AQUALUX_AMALGAM(ItemID.AQUALUX_AMALGAM, 72, 295),
+	AQUALUX_AMALGAM(ItemID.AQUALUX_AMALGAM, 72, 290),
 	WEAPON_POISON_PLUS(ItemID.WEAPON_POISON_5937, 73, 165),
 	DIVINE_RANGING_POTION_4(ItemID.DIVINE_RANGING_POTION4, 74, 2),
 	TORSTOL(ItemID.TORSTOL, 75, 15),
+	MEGALITE_LIQUID(ItemID.MEGALITE_LIQUID, 75, 315),
 	MAGIC_POTION_3(ItemID.MAGIC_POTION3, 76, 172.5f),
-	ALCOAUGMENTATOR(ItemID.ALCOAUGMENTATOR, 76, 255),
 	STAMINA_POTION_3(ItemID.STAMINA_POTION3, 77, 76.5f),
 	STAMINA_POTION_4(ItemID.STAMINA_POTION4, 77, 102),
 	DIVINE_MAGIC_POTION_4(ItemID.DIVINE_MAGIC_POTION4, 78, 2),
 	ZAMORAK_BREW_3(ItemID.ZAMORAK_BREW3, 78, 175),
+	ANTILEECH_LOTION(ItemID.ANTILEECH_LOTION, 78, 340),
 	ANTIDOTE_PLUS_PLUS_4(ItemID.ANTIDOTE4_5952, 79, 177.5f),
 	BASTION_POTION_3(ItemID.BASTION_POTION3, 80, 155),
 	BATTLEMAGE_POTION_3(ItemID.BATTLEMAGE_POTION3, 80, 155),
-	MEGALITE_LIQUID(ItemID.MEGALITE_LIQUID, 80, 295),
 	SARADOMIN_BREW_3(ItemID.SARADOMIN_BREW3, 81, 180),
+	MIXALOT(ItemID.MIXALOT, 81, 365),
 	WEAPON_POISON_PLUS_PLUS(ItemID.WEAPON_POISON_5940, 82, 190),
 	EXTENDED_ANTIFIRE_3(ItemID.EXTENDED_ANTIFIRE3, 84, 82.5f),
 	EXTENDED_ANTIFIRE_4(ItemID.EXTENDED_ANTIFIRE4, 84, 110),
-	ANTILEECH_LOTION(ItemID.ANTILEECH_LOTION, 84, 335),
 	ANCIENT_BREW_4(ItemID.ANCIENT_BREW4, 85, 190),
 	DIVINE_BASTION_POTION_4(ItemID.DIVINE_BASTION_POTION4, 86, 2),
 	DIVINE_BATTLEMAGE_POTION_4(ItemID.DIVINE_BATTLEMAGE_POTION4, 86, 2),
-	LIPLACK_LIQUOR(ItemID.LIPLACK_LIQUOR, 86, 375),
 	ANTIVENOM_3(ItemID.ANTIVENOM3, 87, 90),
 	ANTIVENOM_4(ItemID.ANTIVENOM4, 87, 120),
 	MENAPHITE_REMEDY_3(ItemID.MENAPHITE_REMEDY3, 88, 200),
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/spellbook/SpellbookPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/spellbook/SpellbookPlugin.java
index 813643977..fb735f009 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/spellbook/SpellbookPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/spellbook/SpellbookPlugin.java
@@ -47,6 +47,8 @@ import net.runelite.api.widgets.JavaScriptCallback;
 import net.runelite.api.widgets.Widget;
 import static net.runelite.api.widgets.WidgetConfig.DRAG;
 import static net.runelite.api.widgets.WidgetConfig.DRAG_ON;
+import net.runelite.api.widgets.WidgetSizeMode;
+import net.runelite.api.widgets.WidgetType;
 import net.runelite.api.widgets.WidgetUtil;
 import net.runelite.client.callback.ClientThread;
 import net.runelite.client.chat.ChatMessageManager;
@@ -289,6 +291,8 @@ public class SpellbookPlugin extends Plugin
 			return;
 		}
 
+		createWarning(reordering);
+
 		// this is called after ~magic_spellbook_redraw has built and sorted the array of visible spells
 		// based on the vanilla filtering
 
@@ -361,6 +365,23 @@ public class SpellbookPlugin extends Plugin
 		stack[size - 1] = numSpells = numNewSpells;
 	}
 
+	private void createWarning(boolean unlocked)
+	{
+		Widget w = client.getWidget(ComponentID.SPELLBOOK_PARENT);
+		w.deleteAllChildren();
+
+		if (unlocked)
+		{
+			Widget c = w.createChild(WidgetType.RECTANGLE);
+			c.setHeightMode(WidgetSizeMode.MINUS);
+			c.setWidthMode(WidgetSizeMode.MINUS);
+			c.setTextColor(0xff0000);
+			c.setFilled(true);
+			c.setOpacity(220);
+			c.revalidate();
+		}
+	}
+
 	private void initializeSpells(int spellbookEnum)
 	{
 		EnumComposition spellbook = client.getEnum(spellbookEnum);
diff --git a/runelite-client/src/main/scripts/BankMainBuild.hash b/runelite-client/src/main/scripts/BankMainBuild.hash
index 006d22d9c..4d1c9b45d 100644
--- a/runelite-client/src/main/scripts/BankMainBuild.hash
+++ b/runelite-client/src/main/scripts/BankMainBuild.hash
@@ -1,1 +1,1 @@
-C2F0851465815DB751D28B1D6459FA00DB85273452BA6DE14BB2B5A10FB423AA
\ No newline at end of file
+AA424C74E9017ACF4DAB947F6F25501D2C65E53974355CB7CE860AE203C9969F
\ No newline at end of file
diff --git a/runelite-client/src/main/scripts/BankMainBuild.rs2asm b/runelite-client/src/main/scripts/BankMainBuild.rs2asm
index f05efe39d..677acd83d 100644
--- a/runelite-client/src/main/scripts/BankMainBuild.rs2asm
+++ b/runelite-client/src/main/scripts/BankMainBuild.rs2asm
@@ -204,7 +204,7 @@ LABEL174:
    iload                  18
    iconst                 1
    if_icmpeq              LABEL180
-   jump                   LABEL204
+   jump                   LABEL210
 LABEL180:
    iconst                 1
    iload                  8
@@ -223,14 +223,20 @@ LABEL180:
    iconst                 1
    iload                  3
    if_setsize            
+   iconst                 53
+   iconst                 39
+   iconst                 1
+   iconst                 1
+   iconst                 786447
+   if_setsize            
    iconst                 28
    iconst                 42
    iconst                 2
    iconst                 0
    iload                  1
    if_setposition        
-   jump                   LABEL225
-LABEL204:
+   jump                   LABEL237
+LABEL210:
    iconst                 0
    iload                  8
    if_sethide            
@@ -246,28 +252,34 @@ LABEL204:
    iconst                 1
    iload                  3
    if_setsize            
+   iconst                 53
+   iconst                 81
+   iconst                 1
+   iconst                 1
+   iconst                 786447
+   if_setsize            
    iconst                 12
    iconst                 42
    iconst                 2
    iconst                 0
    iload                  1
    if_setposition        
-LABEL225:
+LABEL237:
    get_varbit             4150
    iconst                 15
-   if_icmpeq              LABEL229
-   jump                   LABEL234
-LABEL229:
+   if_icmpeq              LABEL241
+   jump                   LABEL246
+LABEL241:
    iconst                 0
    iconst                 786447
    if_sethide            
    invoke                 6080
-   jump                   LABEL237
-LABEL234:
+   jump                   LABEL249
+LABEL246:
    iconst                 1
    iconst                 786447
    if_sethide            
-LABEL237:
+LABEL249:
    iload                  3
    iload                  2
    invoke                 231
@@ -279,28 +291,28 @@ LABEL237:
    multiply              
    add                   
    istore                 21
-LABEL248:
+LABEL260:
    iload                  20
    iload                  21
-   if_icmple              LABEL252
-   jump                   LABEL265
-LABEL252:
+   if_icmple              LABEL264
+   jump                   LABEL277
+LABEL264:
    iload                  2
    iload                  20
    cc_find               
    iconst                 1
-   if_icmpeq              LABEL258
-   jump                   LABEL260
-LABEL258:
+   if_icmpeq              LABEL270
+   jump                   LABEL272
+LABEL270:
    iconst                 1
    cc_sethide            
-LABEL260:
+LABEL272:
    iload                  20
    iconst                 1
    add                   
    istore                 20
-   jump                   LABEL248
-LABEL265:
+   jump                   LABEL260
+LABEL277:
    iconst                 0
    istore                 20
    iconst                 8
@@ -347,52 +359,52 @@ LABEL265:
    if_icmpeq              singletabbuildmode ;
    get_varbit             4150
    iconst                 0
-   if_icmple              LABEL309
+   if_icmple              LABEL321
    get_varbit             4150
    iconst                 9
-   if_icmpgt              LABEL309
-   jump                   LABEL766
-LABEL309:
+   if_icmpgt              LABEL321
+   jump                   LABEL778
+LABEL321:
    get_varbit             4150
    iconst                 15
-   if_icmpne              LABEL313
-   jump                   LABEL766
-LABEL313:
+   if_icmpne              LABEL325
+   jump                   LABEL778
+LABEL325:
    iload                  20
    iconst                 1220
-   if_icmplt              LABEL317
-   jump                   LABEL342
-LABEL317:
+   if_icmplt              LABEL329
+   jump                   LABEL354
+LABEL329:
    iload                  2
    iload                  20
    cc_find               
    iconst                 1
-   if_icmpeq              LABEL323
-   jump                   LABEL325
-LABEL323:
+   if_icmpeq              LABEL335
+   jump                   LABEL337
+LABEL335:
    iconst                 1
    cc_sethide            
-LABEL325:
+LABEL337:
    iconst                 95
    iload                  20
    inv_getobj            
    iconst                 -1
-   if_icmpne              LABEL331
-   jump                   LABEL337
-LABEL331:
+   if_icmpne              LABEL343
+   jump                   LABEL349
+LABEL343:
    iload                  29
    iconst                 1
    add                   
    iload                  20
    istore                 30
    istore                 29
-LABEL337:
+LABEL349:
    iload                  20
    iconst                 1
    add                   
    istore                 20
-   jump                   LABEL313
-LABEL342:
+   jump                   LABEL325
+LABEL354:
    get_varbit             4171
    get_varbit             4172
    add                   
@@ -413,14 +425,14 @@ LABEL342:
    istore                 31
    iload                  31
    iconst                 0
-   if_icmple              LABEL364
-   jump                   LABEL368
-LABEL364:
+   if_icmple              LABEL376
+   jump                   LABEL380
+LABEL376:
    iconst                 1220
    iconst                 1
    sub                   
    istore                 30
-LABEL368:
+LABEL380:
    iload                  31
    iload                  30
    iconst                 1
@@ -446,9 +458,9 @@ LABEL368:
    istore                 20
    get_varbit             4171
    iconst                 0
-   if_icmpgt              LABEL395
-   jump                   LABEL425
-LABEL395:
+   if_icmpgt              LABEL407
+   jump                   LABEL437
+LABEL407:
    iconst                 1
    iload                  2
    iload                  28
@@ -479,12 +491,12 @@ LABEL395:
    get_varbit             4171
    add                   
    istore                 20
-LABEL425:
+LABEL437:
    get_varbit             4172
    iconst                 0
-   if_icmpgt              LABEL429
-   jump                   LABEL459
-LABEL429:
+   if_icmpgt              LABEL441
+   jump                   LABEL471
+LABEL441:
    iconst                 2
    iload                  2
    iload                  28
@@ -515,12 +527,12 @@ LABEL429:
    get_varbit             4172
    add                   
    istore                 20
-LABEL459:
+LABEL471:
    get_varbit             4173
    iconst                 0
-   if_icmpgt              LABEL463
-   jump                   LABEL493
-LABEL463:
+   if_icmpgt              LABEL475
+   jump                   LABEL505
+LABEL475:
    iconst                 3
    iload                  2
    iload                  28
@@ -551,12 +563,12 @@ LABEL463:
    get_varbit             4173
    add                   
    istore                 20
-LABEL493:
+LABEL505:
    get_varbit             4174
    iconst                 0
-   if_icmpgt              LABEL497
-   jump                   LABEL527
-LABEL497:
+   if_icmpgt              LABEL509
+   jump                   LABEL539
+LABEL509:
    iconst                 4
    iload                  2
    iload                  28
@@ -587,12 +599,12 @@ LABEL497:
    get_varbit             4174
    add                   
    istore                 20
-LABEL527:
+LABEL539:
    get_varbit             4175
    iconst                 0
-   if_icmpgt              LABEL531
-   jump                   LABEL561
-LABEL531:
+   if_icmpgt              LABEL543
+   jump                   LABEL573
+LABEL543:
    iconst                 5
    iload                  2
    iload                  28
@@ -623,12 +635,12 @@ LABEL531:
    get_varbit             4175
    add                   
    istore                 20
-LABEL561:
+LABEL573:
    get_varbit             4176
    iconst                 0
-   if_icmpgt              LABEL565
-   jump                   LABEL595
-LABEL565:
+   if_icmpgt              LABEL577
+   jump                   LABEL607
+LABEL577:
    iconst                 6
    iload                  2
    iload                  28
@@ -659,12 +671,12 @@ LABEL565:
    get_varbit             4176
    add                   
    istore                 20
-LABEL595:
+LABEL607:
    get_varbit             4177
    iconst                 0
-   if_icmpgt              LABEL599
-   jump                   LABEL629
-LABEL599:
+   if_icmpgt              LABEL611
+   jump                   LABEL641
+LABEL611:
    iconst                 7
    iload                  2
    iload                  28
@@ -695,12 +707,12 @@ LABEL599:
    get_varbit             4177
    add                   
    istore                 20
-LABEL629:
+LABEL641:
    get_varbit             4178
    iconst                 0
-   if_icmpgt              LABEL633
-   jump                   LABEL663
-LABEL633:
+   if_icmpgt              LABEL645
+   jump                   LABEL675
+LABEL645:
    iconst                 8
    iload                  2
    iload                  28
@@ -731,12 +743,12 @@ LABEL633:
    get_varbit             4178
    add                   
    istore                 20
-LABEL663:
+LABEL675:
    get_varbit             4179
    iconst                 0
-   if_icmpgt              LABEL667
-   jump                   LABEL697
-LABEL667:
+   if_icmpgt              LABEL679
+   jump                   LABEL709
+LABEL679:
    iconst                 9
    iload                  2
    iload                  28
@@ -767,12 +779,12 @@ LABEL667:
    get_varbit             4179
    add                   
    istore                 20
-LABEL697:
+LABEL709:
    invoke                 514
    iconst                 1
-   if_icmpeq              LABEL701
-   jump                   LABEL742
-LABEL701:
+   if_icmpeq              LABEL713
+   jump                   LABEL754
+LABEL713:
    jump                   GET_INPUT_TEXT ;  Skip truncating of varcstr 22 by not calling 280
    invoke                 280
 GET_INPUT_TEXT:                          ;
@@ -782,9 +794,9 @@ GET_INPUT_TEXT:                          ;
    sload                  0
    string_length         
    iconst                 0
-   if_icmpgt              LABEL708
-   jump                   LABEL727
-LABEL708:
+   if_icmpgt              LABEL720
+   jump                   LABEL739
+LABEL720:
    sconst                 "Showing items: "
    sconst                 "<col=ff0000>"
    sload                  0
@@ -794,9 +806,9 @@ LABEL708:
    if_settext            
    get_varc_int           5
    iconst                 11
-   if_icmpeq              LABEL719
-   jump                   LABEL726
-LABEL719:
+   if_icmpeq              LABEL731
+   jump                   LABEL738
+LABEL731:
    sconst                 "Show items whose names contain the following text: ("
    iload                  27
    tostring              
@@ -808,9 +820,9 @@ LABEL719:
    pop_int               ; pop number of matches
    iconst                 10616873
    if_settext            
-LABEL726:
-   jump                   LABEL741
-LABEL727:
+LABEL738:
+   jump                   LABEL753
+LABEL739:
    sconst                 "Showing items: "
    sconst                 "<col=ff0000>"
    sconst                 "*"
@@ -820,21 +832,21 @@ LABEL727:
    if_settext            
    get_varc_int           5
    iconst                 11
-   if_icmpeq              LABEL738
-   jump                   LABEL741
-LABEL738:
+   if_icmpeq              LABEL750
+   jump                   LABEL753
+LABEL750:
    sconst                 "Show items whose names contain the following text:"
    sconst                 "setSearchBankInputText"  ; load event name
    runelite_callback     ; invoke callback
    iconst                 10616873
    if_settext            
-LABEL741:
-   jump                   LABEL745
-LABEL742:
+LABEL753:
+   jump                   LABEL757
+LABEL754:
    sconst                 "The Bank of Gielinor"
    iload                  5
    if_settext            
-LABEL745:
+LABEL757:
    iload                  0
    iload                  1
    iload                  2
@@ -878,18 +890,18 @@ singletabbuildmode:
    get_varbit             4179  ;
    add                          ;
    istore                 31    ; store start to the first item in the "All items" tab
-LABEL766:
+LABEL778:
   ; if (~bankmain_searching = 1) {
    invoke                 514
    iconst                 1
-   if_icmpeq              LABEL770
-   jump                   LABEL774
-LABEL770:
+   if_icmpeq              LABEL782
+   jump                   LABEL786
+LABEL782:
    iconst                 1
    iconst                 1
    iconst                 1
    invoke                 299
-LABEL774:
+LABEL786:
   ; after ~meslayer close
    iconst                 -1
    istore                 32
@@ -903,12 +915,12 @@ LABEL774:
    istore                 34
    iconst                 0
    istore                 35
-LABEL786:
+LABEL798:
    iload                  20
    iconst                 1220
-   if_icmplt              LABEL790
-   jump                   LABEL870
-LABEL790:
+   if_icmplt              LABEL802
+   jump                   LABEL882
+LABEL802:
    ; item index = (loop index + offset) % bank size
    iload                  20   ; loop index
    iload                  31   ; offset
@@ -923,9 +935,9 @@ LOAD_ITEM_INDEX:
    iload                  30   ; use item index instead of loop index
    cc_find               
    iconst                 1
-   if_icmpeq              LABEL796
-   jump                   LABEL865
-LABEL796:
+   if_icmpeq              LABEL808
+   jump                   LABEL877
+LABEL808:
    iconst                 95
    jump                   LOAD_ITEM_INDEX2
    iload                  20
@@ -935,32 +947,32 @@ LOAD_ITEM_INDEX2:
    istore                 25
    iload                  25
    iconst                 -1
-   if_icmpne              LABEL804
-   jump                   LABEL808
-LABEL804:
+   if_icmpne              LABEL816
+   jump                   LABEL820
+LABEL816:
    iload                  29
    iconst                 1
    add                   
    istore                 29
-LABEL808:
+LABEL820:
    iload                  36         ; overriding single tab building mode?
    iconst                 1          ;
    if_icmpeq              filtertest ;
    iload                  20
    iload                  32
-   if_icmpge              LABEL812
-   jump                   LABEL863
-LABEL812:
+   if_icmpge              LABEL824
+   jump                   LABEL875
+LABEL824:
    iload                  20
    iload                  33
-   if_icmplt              LABEL816
-   jump                   LABEL863
+   if_icmplt              LABEL828
+   jump                   LABEL875
 filtertest:
    iload                  25        ; obj
    invoke                 279       ; ~bankmain_filtertest
    iconst                 1         ;
-   if_icmpne              LABEL863  ;
-LABEL816:
+   if_icmpne              LABEL875  ;
+LABEL828:
    iconst                 0
    cc_sethide            
    iload                  25
@@ -997,49 +1009,49 @@ LOAD_ITEM_INDEX3:
    istore                 28
    iload                  34
    iload                  22
-   if_icmplt              LABEL851
-   jump                   LABEL856
-LABEL851:
+   if_icmplt              LABEL863
+   jump                   LABEL868
+LABEL863:
    iload                  34
    iconst                 1
    add                   
    istore                 34
-   jump                   LABEL862
-LABEL856:
+   jump                   LABEL874
+LABEL868:
    iconst                 0
    iload                  35
    iconst                 1
    add                   
    istore                 35
    istore                 34
-LABEL862:
-   jump                   LABEL865
-LABEL863:
+LABEL874:
+   jump                   LABEL877
+LABEL875:
    iconst                 1
    cc_sethide            
-LABEL865:
+LABEL877:
    iload                  20
    iconst                 1
    add                   
    istore                 20
-   jump                   LABEL786
-LABEL870:
+   jump                   LABEL798
+LABEL882:
    iconst                 1
    iconst                 786447
    if_sethide            
    get_varbit             4150
    iconst                 15
-   if_icmpeq              LABEL877
-   jump                   LABEL884
-LABEL877:
+   if_icmpeq              LABEL889
+   jump                   LABEL896
+LABEL889:
    sconst                 "Potion store"
    iload                  5
    if_settext            
    iconst                 0
    iconst                 786447
    if_sethide            
-   jump                   LABEL904
-LABEL884:
+   jump                   LABEL916
+LABEL896:
    iload                  36                     ; overriding single tab building mode?
    iconst                 1                      ;
    if_icmpne              tabtitle               ; set normal "Tab" title
@@ -1050,9 +1062,9 @@ LABEL884:
 tabtitle:                                        ;
    get_varbit             4170
    iconst                 2
-   if_icmpeq              LABEL888
-   jump                   LABEL898
-LABEL888:
+   if_icmpeq              LABEL900
+   jump                   LABEL910
+LABEL900:
    sconst                 "Tab "
    iconst                 105
    iconst                 115
@@ -1062,8 +1074,8 @@ LABEL888:
    join_string            2
    iload                  5
    if_settext            
-   jump                   LABEL904
-LABEL898:
+   jump                   LABEL916
+LABEL910:
    sconst                 "Tab "
    get_varbit             4150
    tostring              
@@ -1071,7 +1083,7 @@ LABEL898:
    iload                  5
    if_settext            
 FinishBuilding:
-LABEL904:
+LABEL916:
    iload                  0
    iload                  1
    iload                  2
diff --git a/runelite-jshell/pom.xml b/runelite-jshell/pom.xml
index ffa02bf71..61f866178 100644
--- a/runelite-jshell/pom.xml
+++ b/runelite-jshell/pom.xml
@@ -30,7 +30,7 @@
     <parent>
         <groupId>net.runelite</groupId>
         <artifactId>runelite-parent</artifactId>
-        <version>1.10.40-SNAPSHOT</version>
+        <version>1.10.41-SNAPSHOT</version>
     </parent>
 
     <artifactId>jshell</artifactId>
diff --git a/runelite-maven-plugin/pom.xml b/runelite-maven-plugin/pom.xml
index dc9e5b838..a3cb2f0d7 100644
--- a/runelite-maven-plugin/pom.xml
+++ b/runelite-maven-plugin/pom.xml
@@ -29,7 +29,7 @@
 	<parent>
 		<groupId>net.runelite</groupId>
 		<artifactId>runelite-parent</artifactId>
-		<version>1.10.40-SNAPSHOT</version>
+		<version>1.10.41-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>runelite-maven-plugin</artifactId>


Test patch:
diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/itemstats/ItemStatEffectTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/itemstats/ItemStatEffectTest.java
index b9ed3a5c2..075dfc098 100644
--- a/runelite-client/src/test/java/net/runelite/client/plugins/itemstats/ItemStatEffectTest.java
+++ b/runelite-client/src/test/java/net/runelite/client/plugins/itemstats/ItemStatEffectTest.java
@@ -247,6 +247,176 @@ public class ItemStatEffectTest
 		assertEquals(0, skillChange(Skill.PRAYER, 99, 123, ambrosia));
 	}
 
+	@Test
+	public void testMoonlightPotion()
+	{
+		final Effect moonlightPotion = new ItemStatChanges().get(ItemID.MOONLIGHT_POTION1);
+
+		when(client.getBoostedSkillLevel(Skill.HERBLORE)).thenReturn(11);
+		assertEquals(12, skillChange(Skill.ATTACK, 99, 1, moonlightPotion));
+		assertEquals(0, skillChange(Skill.STRENGTH, 99, 99, moonlightPotion));
+		assertEquals(0, skillChange(Skill.DEFENCE, 40, 33, moonlightPotion));
+		assertEquals(0, skillChange(Skill.PRAYER, 99, 54, moonlightPotion));
+
+		when(client.getBoostedSkillLevel(Skill.HERBLORE)).thenReturn(38);
+		assertEquals(12, skillChange(Skill.ATTACK, 99, 99, moonlightPotion));
+		assertEquals(12, skillChange(Skill.STRENGTH, 99, 99, moonlightPotion));
+		assertEquals(4, skillChange(Skill.DEFENCE, 40, 43, moonlightPotion));
+		assertEquals(31, skillChange(Skill.PRAYER, 99, 0, moonlightPotion));
+		assertEquals(9, skillChange(Skill.PRAYER, 99, 90, moonlightPotion));
+
+		when(client.getBoostedSkillLevel(Skill.HERBLORE)).thenReturn(54);
+		assertEquals(3, skillChange(Skill.ATTACK, 60, 71, moonlightPotion));
+		assertEquals(2, skillChange(Skill.STRENGTH, 60, 67, moonlightPotion));
+		assertEquals(2, skillChange(Skill.DEFENCE, 60, 67, moonlightPotion));
+		assertEquals(31, skillChange(Skill.PRAYER, 99, 10, moonlightPotion));
+
+		when(client.getBoostedSkillLevel(Skill.HERBLORE)).thenReturn(65);
+		assertEquals(12, skillChange(Skill.ATTACK, 50, 19, moonlightPotion));
+		assertEquals(13, skillChange(Skill.STRENGTH, 58, 22, moonlightPotion));
+		assertEquals(0, skillChange(Skill.DEFENCE, 34, 40, moonlightPotion));
+		assertEquals(26, skillChange(Skill.PRAYER, 42, 0, moonlightPotion));
+		assertEquals(4, skillChange(Skill.PRAYER, 42, 38, moonlightPotion));
+
+		when(client.getBoostedSkillLevel(Skill.HERBLORE)).thenReturn(77);
+		assertEquals(30, skillChange(Skill.PRAYER, 77, 18, moonlightPotion));
+
+		when(client.getBoostedSkillLevel(Skill.HERBLORE)).thenReturn(80);
+		assertEquals(6, skillChange(Skill.ATTACK, 80, 91, moonlightPotion));
+		assertEquals(7, skillChange(Skill.STRENGTH, 92, 103, moonlightPotion));
+		assertEquals(8, skillChange(Skill.DEFENCE, 75, 89, moonlightPotion));
+		assertEquals(31, skillChange(Skill.PRAYER, 77, 18, moonlightPotion));
+
+		when(client.getBoostedSkillLevel(Skill.HERBLORE)).thenReturn(99);
+		assertEquals(19, skillChange(Skill.ATTACK, 99, 99, moonlightPotion));
+		assertEquals(19, skillChange(Skill.STRENGTH, 99, 99, moonlightPotion));
+		assertEquals(26, skillChange(Skill.DEFENCE, 99, 99, moonlightPotion));
+		assertEquals(36, skillChange(Skill.PRAYER, 99, 0, moonlightPotion));
+
+		when(client.getBoostedSkillLevel(Skill.HERBLORE)).thenReturn(102);
+		assertEquals(37, skillChange(Skill.PRAYER, 99, 0, moonlightPotion));
+
+		when(client.getBoostedSkillLevel(Skill.HERBLORE)).thenReturn(104);
+		assertEquals(33, skillChange(Skill.PRAYER, 34, 1, moonlightPotion));
+		assertEquals(38, skillChange(Skill.PRAYER, 54, 0, moonlightPotion));
+	}
+
+	@Test
+	public void testMoonlightMoth()
+	{
+		final Effect moonlightMoth = new ItemStatChanges().get(ItemID.MOONLIGHT_MOTH_28893);
+
+		assertEquals(0, skillChange(Skill.PRAYER, 1, 1, moonlightMoth));
+		assertEquals(5, skillChange(Skill.PRAYER, 7, 2, moonlightMoth));
+		assertEquals(22, skillChange(Skill.PRAYER, 30, 0, moonlightMoth));
+		assertEquals(19, skillChange(Skill.PRAYER, 68, 49, moonlightMoth));
+		assertEquals(22, skillChange(Skill.PRAYER, 99, 70, moonlightMoth));
+
+		final Effect moonlightMothMix = new ItemStatChanges().get(ItemID.MOONLIGHT_MOTH_MIX_1);
+
+		assertEquals(0, skillChange(Skill.PRAYER, 1, 1, moonlightMothMix));
+		assertEquals(5, skillChange(Skill.PRAYER, 7, 2, moonlightMothMix));
+		assertEquals(22, skillChange(Skill.PRAYER, 30, 0, moonlightMothMix));
+		assertEquals(19, skillChange(Skill.PRAYER, 68, 49, moonlightMothMix));
+		assertEquals(22, skillChange(Skill.PRAYER, 99, 70, moonlightMothMix));
+	}
+
+	@Test
+	public void testSunlightMoth()
+	{
+		final Effect sunlightMoth = new ItemStatChanges().get(ItemID.SUNLIGHT_MOTH_28890);
+
+		assertEquals(16, skillChange(Skill.AGILITY, 50, 1, sunlightMoth));
+		assertEquals(0, skillChange(Skill.ATTACK, 70, 70, sunlightMoth));
+		assertEquals(17, skillChange(Skill.CONSTRUCTION, 66, 49, sunlightMoth));
+		assertEquals(0, skillChange(Skill.COOKING, 70, 70, sunlightMoth));
+		assertEquals(12, skillChange(Skill.CRAFTING, 70, 58, sunlightMoth));
+		assertEquals(21, skillChange(Skill.DEFENCE, 75, 2, sunlightMoth));
+		assertEquals(13, skillChange(Skill.FARMING, 83, 70, sunlightMoth));
+		assertEquals(1, skillChange(Skill.FIREMAKING, 71, 70, sunlightMoth));
+		assertEquals(19, skillChange(Skill.FISHING, 89, 70, sunlightMoth));
+		assertEquals(7, skillChange(Skill.FLETCHING, 77, 70, sunlightMoth));
+		assertEquals(0, skillChange(Skill.HERBLORE, 1, 1, sunlightMoth));
+		assertEquals(1, skillChange(Skill.HITPOINTS, 84, 83, sunlightMoth));
+		assertEquals(0, skillChange(Skill.HUNTER, 75, 77, sunlightMoth));
+		assertEquals(12, skillChange(Skill.MAGIC, 30, 0, sunlightMoth));
+		assertEquals(19, skillChange(Skill.MINING, 68, 49, sunlightMoth));
+		assertEquals(0, skillChange(Skill.PRAYER, 99, 70, sunlightMoth));
+		assertEquals(25, skillChange(Skill.RANGED, 99, 70, sunlightMoth));
+		assertEquals(25, skillChange(Skill.RUNECRAFT, 99, 70, sunlightMoth));
+		assertEquals(12, skillChange(Skill.SLAYER, 99, 87, sunlightMoth));
+		assertEquals(0, skillChange(Skill.SMITHING, 31, 31, sunlightMoth));
+		assertEquals(0, skillChange(Skill.STRENGTH, 68, 81, sunlightMoth));
+		assertEquals(11, skillChange(Skill.THIEVING, 28, 10, sunlightMoth));
+		assertEquals(3, skillChange(Skill.WOODCUTTING, 54, 51, sunlightMoth));
+
+		final Effect sunlightMothMix = new ItemStatChanges().get(ItemID.SUNLIGHT_MOTH_MIX_1);
+
+		assertEquals(16, skillChange(Skill.AGILITY, 50, 1, sunlightMothMix));
+		assertEquals(0, skillChange(Skill.ATTACK, 70, 70, sunlightMothMix));
+		assertEquals(17, skillChange(Skill.CONSTRUCTION, 66, 49, sunlightMothMix));
+		assertEquals(0, skillChange(Skill.COOKING, 70, 70, sunlightMothMix));
+		assertEquals(12, skillChange(Skill.CRAFTING, 70, 58, sunlightMothMix));
+		assertEquals(21, skillChange(Skill.DEFENCE, 75, 2, sunlightMothMix));
+		assertEquals(13, skillChange(Skill.FARMING, 83, 70, sunlightMothMix));
+		assertEquals(1, skillChange(Skill.FIREMAKING, 71, 70, sunlightMothMix));
+		assertEquals(19, skillChange(Skill.FISHING, 89, 70, sunlightMothMix));
+		assertEquals(7, skillChange(Skill.FLETCHING, 77, 70, sunlightMothMix));
+		assertEquals(0, skillChange(Skill.HERBLORE, 1, 1, sunlightMothMix));
+		assertEquals(1, skillChange(Skill.HITPOINTS, 84, 83, sunlightMothMix));
+		assertEquals(0, skillChange(Skill.HUNTER, 75, 77, sunlightMothMix));
+		assertEquals(12, skillChange(Skill.MAGIC, 30, 0, sunlightMothMix));
+		assertEquals(19, skillChange(Skill.MINING, 68, 49, sunlightMothMix));
+		assertEquals(0, skillChange(Skill.PRAYER, 99, 70, sunlightMothMix));
+		assertEquals(25, skillChange(Skill.RANGED, 99, 70, sunlightMothMix));
+		assertEquals(25, skillChange(Skill.RUNECRAFT, 99, 70, sunlightMothMix));
+		assertEquals(12, skillChange(Skill.SLAYER, 99, 87, sunlightMothMix));
+		assertEquals(0, skillChange(Skill.SMITHING, 31, 31, sunlightMothMix));
+		assertEquals(0, skillChange(Skill.STRENGTH, 68, 81, sunlightMothMix));
+		assertEquals(11, skillChange(Skill.THIEVING, 28, 10, sunlightMothMix));
+		assertEquals(3, skillChange(Skill.WOODCUTTING, 54, 51, sunlightMothMix));
+	}
+
+	@Test
+	public void testRubyHarvest()
+	{
+		final Effect rubyHarvest = new ItemStatChanges().get(ItemID.RUBY_HARVEST);
+
+		assertEquals(16, skillChange(Skill.ATTACK, 82, 82, rubyHarvest));
+	}
+
+	@Test
+	public void testSapphireGlacial()
+	{
+		final Effect sapphireGlacial = new ItemStatChanges().get(ItemID.SAPPHIRE_GLACIALIS);
+
+		assertEquals(13, skillChange(Skill.DEFENCE, 64, 64, sapphireGlacial));
+	}
+
+	@Test
+	public void testBlackWarlock()
+	{
+		final Effect blackWarlock = new ItemStatChanges().get(ItemID.BLACK_WARLOCK);
+
+		assertEquals(13, skillChange(Skill.STRENGTH, 64, 64, blackWarlock));
+	}
+
+	@Test
+	public void testSnowyKnight()
+	{
+		final Effect snowyKnight = new ItemStatChanges().get(ItemID.SNOWY_KNIGHT);
+
+		assertEquals(5, skillChange(Skill.HITPOINTS, 49, 44, snowyKnight));
+		assertEquals(0, skillChange(Skill.HITPOINTS, 64, 64, snowyKnight));
+		assertEquals(15, skillChange(Skill.HITPOINTS, 99, 77, snowyKnight));
+
+		final Effect snowyKnightMix = new ItemStatChanges().get(ItemID.SNOWY_KNIGHT_MIX_1);
+
+		assertEquals(5, skillChange(Skill.HITPOINTS, 50, 45, snowyKnightMix));
+		assertEquals(0, skillChange(Skill.HITPOINTS, 64, 64, snowyKnightMix));
+		assertEquals(8, skillChange(Skill.HITPOINTS, 99, 10, snowyKnightMix));
+	}
+
 	@Test
 	public void prayerRestoreVariants()
 	{