Repository: runelite/runelite
Pull request #18592 (2024-12-07 09:39:29)

entityhider: selectively hide party members
I placed party members above friends, since it is a logically more restrictive group than your entire friends list. It is likely to only contain the people you are actively playing with, and so you might want to hide everyone else, including friends.

PartyService#getMemberByDisplayName performs jagex name sanitization, so it may be desirable to use a cached form of this list if performance is a concern. Let me know and I can add it. I didn't notice any performance impact even at busy places like the GE.


Patch:
diff --git a/runelite-api/src/main/java/net/runelite/api/DynamicObject.java b/runelite-api/src/main/java/net/runelite/api/DynamicObject.java
index 04607cace..bc0e583f5 100644
--- a/runelite-api/src/main/java/net/runelite/api/DynamicObject.java
+++ b/runelite-api/src/main/java/net/runelite/api/DynamicObject.java
@@ -46,4 +46,11 @@ public interface DynamicObject extends Renderable
 	 * @return
 	 */
 	int getAnimCycle();
+
+	/**
+	 * Like {@link #getModel()} but is threadsafe and doesn't support animations.
+	 *
+	 * @return
+	 */
+	Model getModelZbuf();
 }
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/entityhider/EntityHiderConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/entityhider/EntityHiderConfig.java
index f31d3589d..0d4bfe9de 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/entityhider/EntityHiderConfig.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/entityhider/EntityHiderConfig.java
@@ -58,6 +58,17 @@ public interface EntityHiderConfig extends Config
 
 	@ConfigItem(
 		position = 3,
+		keyName = "hidePartyMembers",
+		name = "Hide party members",
+		description = "Configures whether or not party members are hidden."
+	)
+	default boolean hidePartyMembers()
+	{
+		return false;
+	}
+
+	@ConfigItem(
+		position = 4,
 		keyName = "hideFriends",
 		name = "Hide friends",
 		description = "Configures whether or not friends are hidden."
@@ -68,7 +79,7 @@ public interface EntityHiderConfig extends Config
 	}
 
 	@ConfigItem(
-		position = 4,
+		position = 5,
 		keyName = "hideClanMates", // is actually friends chat
 		name = "Hide friends chat members",
 		description = "Configures whether or not friends chat members are hidden."
@@ -79,7 +90,7 @@ public interface EntityHiderConfig extends Config
 	}
 
 	@ConfigItem(
-		position = 5,
+		position = 6,
 		keyName = "hideClanChatMembers",
 		name = "Hide clan chat members",
 		description = "Configures whether or not clan chat members are hidden."
@@ -90,7 +101,7 @@ public interface EntityHiderConfig extends Config
 	}
 
 	@ConfigItem(
-		position = 6,
+		position = 7,
 		keyName = "hideIgnores",
 		name = "Hide ignores",
 		description = "Configures whether or not ignored players are hidden."
@@ -101,7 +112,7 @@ public interface EntityHiderConfig extends Config
 	}
 
 	@ConfigItem(
-		position = 7,
+		position = 8,
 		keyName = "hideLocalPlayer",
 		name = "Hide local player",
 		description = "Configures whether or not the local player is hidden."
@@ -112,7 +123,7 @@ public interface EntityHiderConfig extends Config
 	}
 
 	@ConfigItem(
-		position = 8,
+		position = 9,
 		keyName = "hideLocalPlayer2D",
 		name = "Hide local player 2D",
 		description = "Configures whether or not the local player's 2D elements are hidden."
@@ -123,7 +134,7 @@ public interface EntityHiderConfig extends Config
 	}
 
 	@ConfigItem(
-		position = 9,
+		position = 10,
 		keyName = "hideNPCs",
 		name = "Hide NPCs",
 		description = "Configures whether or not NPCs are hidden."
@@ -134,7 +145,7 @@ public interface EntityHiderConfig extends Config
 	}
 
 	@ConfigItem(
-		position = 10,
+		position = 11,
 		keyName = "hideNPCs2D",
 		name = "Hide NPCs 2D",
 		description = "Configures whether or not NPCs 2D elements are hidden."
@@ -145,7 +156,7 @@ public interface EntityHiderConfig extends Config
 	}
 
 	@ConfigItem(
-		position = 11,
+		position = 12,
 		keyName = "hidePets",
 		name = "Hide other players' pets",
 		description = "Configures whether or not other player pets are hidden."
@@ -156,7 +167,7 @@ public interface EntityHiderConfig extends Config
 	}
 
 	@ConfigItem(
-		position = 12,
+		position = 13,
 		keyName = "hideAttackers",
 		name = "Hide attackers",
 		description = "Configures whether or not NPCs/players attacking you are hidden."
@@ -167,7 +178,7 @@ public interface EntityHiderConfig extends Config
 	}
 
 	@ConfigItem(
-		position = 13,
+		position = 14,
 		keyName = "hideProjectiles",
 		name = "Hide projectiles",
 		description = "Configures whether or not projectiles are hidden."
@@ -178,7 +189,7 @@ public interface EntityHiderConfig extends Config
 	}
 
 	@ConfigItem(
-		position = 14,
+		position = 15,
 		keyName = "hideDeadNpcs",
 		name = "Hide dead NPCs",
 		description = "Hides NPCs when their health reaches 0."
@@ -189,7 +200,7 @@ public interface EntityHiderConfig extends Config
 	}
 
 	@ConfigItem(
-		position = 15,
+		position = 16,
 		keyName = "hideThralls",
 		name = "Hide thralls",
 		description = "Configures whether or not thralls are hidden."
@@ -200,7 +211,7 @@ public interface EntityHiderConfig extends Config
 	}
 
 	@ConfigItem(
-		position = 16,
+		position = 17,
 		keyName = "hideRandomEvents",
 		name = "Hide random events",
 		description = "Configures whether or not random events are hidden."
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/entityhider/EntityHiderPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/entityhider/EntityHiderPlugin.java
index c43f798d2..0556cd0cf 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/entityhider/EntityHiderPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/entityhider/EntityHiderPlugin.java
@@ -44,6 +44,7 @@ import net.runelite.client.config.ConfigManager;
 import net.runelite.client.eventbus.Subscribe;
 import net.runelite.client.events.ConfigChanged;
 import net.runelite.client.game.NpcUtil;
+import net.runelite.client.party.PartyService;
 import net.runelite.client.plugins.Plugin;
 import net.runelite.client.plugins.PluginDescriptor;
 
@@ -98,8 +99,12 @@ public class EntityHiderPlugin extends Plugin
 	@Inject
 	private NpcUtil npcUtil;
 
+	@Inject
+	private PartyService partyService;
+
 	private boolean hideOthers;
 	private boolean hideOthers2D;
+	private boolean hidePartyMembers;
 	private boolean hideFriends;
 	private boolean hideFriendsChatMembers;
 	private boolean hideClanMembers;
@@ -151,6 +156,7 @@ public class EntityHiderPlugin extends Plugin
 		hideOthers = config.hideOthers();
 		hideOthers2D = config.hideOthers2D();
 
+		hidePartyMembers = config.hidePartyMembers();
 		hideFriends = config.hideFriends();
 		hideFriendsChatMembers = config.hideFriendsChatMembers();
 		hideClanMembers = config.hideClanChatMembers();
@@ -199,6 +205,10 @@ public class EntityHiderPlugin extends Plugin
 				return false; // hide
 			}
 
+			if (partyService.isInParty() && partyService.getMemberByDisplayName(player.getName()) != null)
+			{
+				return !hidePartyMembers;
+			}
 			if (player.isFriend())
 			{
 				return !hideFriends;
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/StatBoost.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/StatBoost.java
index 2be7dfccd..0ad1682e5 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/StatBoost.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/StatBoost.java
@@ -26,6 +26,8 @@ package net.runelite.client.plugins.itemstats;
 
 import lombok.Getter;
 import lombok.Setter;
+import net.runelite.api.InventoryID;
+import net.runelite.api.ItemID;
 import net.runelite.api.Skill;
 import net.runelite.api.Varbits;
 import net.runelite.client.plugins.itemstats.stats.Stat;
@@ -64,10 +66,21 @@ public abstract class StatBoost extends SingleEffect
 			final int rangedMastery = client.getVarbitValue(Varbits.LEAGUES_RANGED_COMBAT_MASTERY_LEVEL);
 			final int magicMastery = client.getVarbitValue(Varbits.LEAGUES_MAGIC_COMBAT_MASTERY_LEVEL);
 
-			if ((meleeMastery >= 2 || rangedMastery >= 2 || magicMastery >= 2)
-				&& statName.equals(Skill.HITPOINTS.getName()))
+			if (statName.equals(Skill.HITPOINTS.getName()))
 			{
-				calcedDelta *= 1.2;
+				float multiplier = 1;
+
+				if (meleeMastery >= 2 || rangedMastery >= 2 || magicMastery >= 2)
+				{
+					multiplier += 0.2;
+				}
+
+				if (client.getItemContainer(InventoryID.EQUIPMENT).contains(ItemID.SUNLIT_BRACERS))
+				{
+					multiplier += 1;
+				}
+
+				calcedDelta *= multiplier;
 			}
 			else if ((meleeMastery >= 5 || rangedMastery >= 5 || magicMastery >= 5)
 				&& statName.equals(Skill.PRAYER.getName()))


Test patch:
diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/entityhider/EntityHiderPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/entityhider/EntityHiderPluginTest.java
index 834a29649..98af33524 100644
--- a/runelite-client/src/test/java/net/runelite/client/plugins/entityhider/EntityHiderPluginTest.java
+++ b/runelite-client/src/test/java/net/runelite/client/plugins/entityhider/EntityHiderPluginTest.java
@@ -36,11 +36,14 @@ import net.runelite.api.NameableContainer;
 import net.runelite.api.Player;
 import net.runelite.client.callback.Hooks;
 import net.runelite.client.events.ConfigChanged;
+import net.runelite.client.party.PartyMember;
+import net.runelite.client.party.PartyService;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import static org.mockito.ArgumentMatchers.eq;
 import org.mockito.Mock;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -64,6 +67,10 @@ public class EntityHiderPluginTest
 	@Bind
 	Hooks hooks;
 
+	@Mock
+	@Bind
+	PartyService partyService;
+
 	@Mock
 	NameableContainer<Ignore> ignoreNameableContainer;
 
@@ -239,4 +246,43 @@ public class EntityHiderPluginTest
 		assertFalse(plugin.shouldDraw(npc, true));
 		assertFalse(plugin.shouldDraw(npc, false));
 	}
+
+	@Test
+	public void testHidePartyMembersPositive()
+	{
+		when(config.hidePartyMembers()).thenReturn(true);
+
+		ConfigChanged configChanged = new ConfigChanged();
+		configChanged.setGroup(EntityHiderConfig.GROUP);
+		plugin.onConfigChanged(configChanged);
+
+		Player player = mock(Player.class);
+		when(player.getName()).thenReturn("test player");
+
+		PartyMember partyMember = mock(PartyMember.class);
+		when(partyService.isInParty()).thenReturn(true);
+		when(partyService.getMemberByDisplayName(eq("test player"))).thenReturn(partyMember);
+
+		assertFalse(plugin.shouldDraw(player, true));
+		assertFalse(plugin.shouldDraw(player, false));
+	}
+
+	@Test
+	public void testHidePartyMembersNegative()
+	{
+		when(config.hidePartyMembers()).thenReturn(true);
+
+		ConfigChanged configChanged = new ConfigChanged();
+		configChanged.setGroup(EntityHiderConfig.GROUP);
+		plugin.onConfigChanged(configChanged);
+
+		Player player = mock(Player.class);
+		when(player.getName()).thenReturn("test player");
+
+		when(partyService.isInParty()).thenReturn(true);
+		when(partyService.getMemberByDisplayName("test player")).thenReturn(null);
+
+		assertTrue(plugin.shouldDraw(player, true));
+		assertTrue(plugin.shouldDraw(player, false));
+	}
 }
\ No newline at end of file
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 6d894b598..9126f9019 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
@@ -145,6 +145,7 @@ public class ItemStatEffectTest
 	{
 		when(client.getBoostedSkillLevel(any(Skill.class)))
 			.thenReturn(0);
+		when(client.getItemContainer(InventoryID.EQUIPMENT)).thenReturn(mock(ItemContainer.class));
 	}
 
 	@Test
@@ -467,6 +468,29 @@ public class ItemStatEffectTest
 		assertEquals(-5, skillChange(Skill.PRAYER, 99, 99, bloodPint));
 	}
 
+	@Test
+	public void sunlitBracerHealingBoost()
+	{
+		final Effect karambwan = new ItemStatChanges().get(ItemID.COOKED_KARAMBWAN);
+		final Effect shark = new ItemStatChanges().get(ItemID.SHARK);
+		final Effect saradominBrew = new ItemStatChanges().get(ItemID.SARADOMIN_BREW4);
+
+		final ItemContainer equipment = mock(ItemContainer.class);
+		when(equipment.contains(ItemID.SUNLIT_BRACERS)).thenReturn(true);
+		when(client.getItemContainer(InventoryID.EQUIPMENT)).thenReturn(equipment);
+
+		assertEquals(36, skillChange(Skill.HITPOINTS, 99, 1, karambwan));
+		assertEquals(40, skillChange(Skill.HITPOINTS, 99, 1, shark));
+		assertEquals(32, skillChange(Skill.HITPOINTS, 99, 99, saradominBrew));
+
+		// Test combined with tier 2 combat relic passive
+		when(client.getVarbitValue(Varbits.LEAGUES_MELEE_COMBAT_MASTERY_LEVEL)).thenReturn(2);
+
+		assertEquals(39, skillChange(Skill.HITPOINTS, 99, 1, karambwan));
+		assertEquals(44, skillChange(Skill.HITPOINTS, 99, 1, shark));
+		assertEquals(35, skillChange(Skill.HITPOINTS, 99, 99, saradominBrew));
+	}
+
 	private int skillChange(Skill skill, int maxValue, int currentValue, Effect effect)
 	{
 		if (effect == null)