diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt
index efb253edcf..3ea4637c86 100644
--- a/buildSrc/src/main/kotlin/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/Dependencies.kt
@@ -25,9 +25,9 @@
object ProjectVersions {
const val launcherVersion = "2.2.0"
- const val rlVersion = "1.7.13"
+ const val rlVersion = "1.7.13.1"
- const val openosrsVersion = "4.9.0"
+ const val openosrsVersion = "4.9.2"
const val rsversion = 197
const val cacheversion = 165
diff --git a/runelite-api/src/main/java/net/runelite/api/Actor.java b/runelite-api/src/main/java/net/runelite/api/Actor.java
index 2f7c7d937b..b960461f64 100644
--- a/runelite-api/src/main/java/net/runelite/api/Actor.java
+++ b/runelite-api/src/main/java/net/runelite/api/Actor.java
@@ -117,13 +117,19 @@ public interface Actor extends Renderable, Locatable
int getPoseFrameCycle();
/**
- * Gets the orientation of the actor.
+ * Gets the target orientation of the actor.
*
* @return the orientation
* @see net.runelite.api.coords.Angle
*/
int getOrientation();
+ /**
+ * Gets the current orientation of the actor.
+ *
+ * @return the orientation
+ * @see net.runelite.api.coords.Angle
+ */
int getCurrentOrientation();
/**
diff --git a/runelite-api/src/main/java/net/runelite/api/DecorativeObject.java b/runelite-api/src/main/java/net/runelite/api/DecorativeObject.java
index 246ed2bfd5..ba78add470 100644
--- a/runelite-api/src/main/java/net/runelite/api/DecorativeObject.java
+++ b/runelite-api/src/main/java/net/runelite/api/DecorativeObject.java
@@ -35,7 +35,7 @@ public interface DecorativeObject extends TileObject
* Gets the convex hull of the objects model.
*
* @return the convex hull
- * @see api.model.Jarvis
+ * @see net.runelite.api.model.Jarvis
*/
Shape getConvexHull();
Shape getConvexHull2();
@@ -43,13 +43,15 @@ public interface DecorativeObject extends TileObject
Renderable getRenderable();
Renderable getRenderable2();
- Model getModel1();
-
- Model getModel2();
-
- int getYOffset();
-
+ /**
+ * Decorative object x offset. This is added to the x position of the object, and is used to
+ * account for walls of varying widths.
+ */
int getXOffset();
- int getOrientation();
+ /**
+ * Decorative object y offset. This is added to the z position of the object, and is used to
+ * account for walls of varying widths.
+ */
+ int getYOffset();
}
diff --git a/runelite-api/src/main/java/net/runelite/api/GameObject.java b/runelite-api/src/main/java/net/runelite/api/GameObject.java
index 4183e643d3..9cc903f621 100644
--- a/runelite-api/src/main/java/net/runelite/api/GameObject.java
+++ b/runelite-api/src/main/java/net/runelite/api/GameObject.java
@@ -83,15 +83,14 @@ public interface GameObject extends TileObject
Renderable getRenderable();
- int getRsOrientation();
-
- Model getModel();
-
/**
- * A bitfield containing various flags:
- * object type id = bits & 0x20
- * orientation (0-3) = bits >>> 6 & 3
- * the bitfield may contain other flags in addition to those mentioned above
+ * Gets the orientation of the model in JAU.
+ * This is typically 0 for non-actors, since
+ * most object's models are oriented prior to
+ * lighting during scene loading. See {@link #getOrientation()}
+ * instead for object orientation.
+ *
+ * @see net.runelite.api.coords.Angle
*/
- int getFlags();
+ int getModelOrientation();
}
diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java
index b3e4ce4daf..639475c4ec 100644
--- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java
+++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java
@@ -89,7 +89,7 @@ public class WidgetID
public static final int CLUE_SCROLL_REWARD_GROUP_ID = 73;
public static final int BARROWS_REWARD_GROUP_ID = 155;
public static final int RAIDS_GROUP_ID = 513;
- public static final int TOB_PARTY_GROUP_ID = 28;
+ public static final int TOB_GROUP_ID = 28;
public static final int MOTHERLODE_MINE_GROUP_ID = 382;
public static final int EXPERIENCE_DROP_GROUP_ID = 122;
public static final int PUZZLE_BOX_GROUP_ID = 306;
@@ -225,6 +225,9 @@ public class WidgetID
static class PestControl
{
+ static final int KNIGHT_INFO_CONTAINER = 2;
+
+ static final int ACTIVITY_SHIELD_CONTAINER = 9;
static final int ACTIVITY_BAR = 10;
static final int ACTIVITY_PROGRESS = 12;
@@ -658,6 +661,7 @@ public class WidgetID
static class VolcanicMine
{
+ static final int STABILITY_INFOBOX_CONTAINER = 2;
static final int TIME_LEFT = 6;
static final int POINTS = 8;
static final int STABILITY = 10;
@@ -668,6 +672,7 @@ public class WidgetID
static final int VENT_A_STATUS = 20;
static final int VENT_B_STATUS = 21;
static final int VENT_C_STATUS = 22;
+ static final int VENTS_INFOBOX_CONTAINER = 26;
}
static class BarbarianAssault
@@ -711,7 +716,9 @@ public class WidgetID
static class Tob
{
+ static final int PARTY_INTERFACE = 5;
static final int PARTY_STATS = 7;
+ static final int HEALTHBAR_CONTAINER = 9;
}
static class PuzzleBox
@@ -845,7 +852,7 @@ public class WidgetID
static final int KILLDEATH_RATIO = 26;
static final int SKULL_CONTAINER = 45;
static final int SAFE_ZONE = 47;
- static final int WILDERNESS_LEVEL = 49; // this can also be the Deadman Mode "Protection" text
+ static final int WILDERNESS_LEVEL = 50; // this can also be the Deadman Mode "Protection" text
}
static class KourendFavour
@@ -940,7 +947,7 @@ public class WidgetID
static class LmsKDA
{
- static final int INFO = 7;
+ static final int INFO = 4;
}
static class AdventureLog
@@ -994,7 +1001,7 @@ public class WidgetID
static class TemporossStatus
{
- static final int STATUS_INDICATOR = 4;
+ static final int STATUS_INDICATOR = 2;
}
static class DialogNotification
diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java
index 7c87560a5f..cb9391032e 100644
--- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java
+++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java
@@ -74,6 +74,9 @@ public enum WidgetInfo
DIARY_QUEST_WIDGET_TITLE(WidgetID.DIARY_QUEST_GROUP_ID, WidgetID.Diary.DIARY_TITLE),
DIARY_QUEST_WIDGET_TEXT(WidgetID.DIARY_QUEST_GROUP_ID, WidgetID.Diary.DIARY_TEXT),
+ PEST_CONTROL_BOAT_INFO(WidgetID.PEST_CONTROL_BOAT_GROUP_ID, 2),
+ PEST_CONTROL_KNIGHT_INFO_CONTAINER(WidgetID.PEST_CONTROL_GROUP_ID, WidgetID.PestControl.KNIGHT_INFO_CONTAINER),
+ PEST_CONTROL_ACTIVITY_SHIELD_INFO_CONTAINER(WidgetID.PEST_CONTROL_GROUP_ID, WidgetID.PestControl.ACTIVITY_SHIELD_CONTAINER),
PEST_CONTROL_PURPLE_SHIELD(WidgetID.PEST_CONTROL_GROUP_ID, WidgetID.PestControl.PURPLE_SHIELD),
PEST_CONTROL_BLUE_SHIELD(WidgetID.PEST_CONTROL_GROUP_ID, WidgetID.PestControl.BLUE_SHIELD),
PEST_CONTROL_YELLOW_SHIELD(WidgetID.PEST_CONTROL_GROUP_ID, WidgetID.PestControl.YELLOW_SHIELD),
@@ -93,6 +96,8 @@ public enum WidgetInfo
VOLCANIC_MINE_POINTS(WidgetID.VOLCANIC_MINE_GROUP_ID, WidgetID.VolcanicMine.POINTS),
VOLCANIC_MINE_STABILITY(WidgetID.VOLCANIC_MINE_GROUP_ID, WidgetID.VolcanicMine.STABILITY),
VOLCANIC_MINE_PLAYER_COUNT(WidgetID.VOLCANIC_MINE_GROUP_ID, WidgetID.VolcanicMine.PLAYER_COUNT),
+ VOLCANIC_MINE_VENTS_INFOBOX_GROUP(WidgetID.VOLCANIC_MINE_GROUP_ID, WidgetID.VolcanicMine.VENTS_INFOBOX_CONTAINER),
+ VOLCANIC_MINE_STABILITY_INFOBOX_GROUP(WidgetID.VOLCANIC_MINE_GROUP_ID, WidgetID.VolcanicMine.STABILITY_INFOBOX_CONTAINER),
VOLCANIC_MINE_VENT_A_PERCENTAGE(WidgetID.VOLCANIC_MINE_GROUP_ID, WidgetID.VolcanicMine.VENT_A_PERCENTAGE),
VOLCANIC_MINE_VENT_B_PERCENTAGE(WidgetID.VOLCANIC_MINE_GROUP_ID, WidgetID.VolcanicMine.VENT_B_PERCENTAGE),
VOLCANIC_MINE_VENT_C_PERCENTAGE(WidgetID.VOLCANIC_MINE_GROUP_ID, WidgetID.VolcanicMine.VENT_C_PERCENTAGE),
@@ -396,6 +401,8 @@ public enum WidgetInfo
MOTHERLODE_MINE(WidgetID.MOTHERLODE_MINE_GROUP_ID, 0),
+ GWD_KC(WidgetID.GWD_KC_GROUP_ID, 4),
+
PUZZLE_BOX(WidgetID.PUZZLE_BOX_GROUP_ID, WidgetID.PuzzleBox.VISIBLE_BOX),
LIGHT_BOX(WidgetID.LIGHT_BOX_GROUP_ID, WidgetID.LightBox.LIGHT_BOX),
@@ -417,7 +424,9 @@ public enum WidgetInfo
RAIDS_POINTS_INFOBOX(WidgetID.RAIDS_GROUP_ID, WidgetID.Raids.POINTS_INFOBOX),
- TOB_PARTY_STATS(WidgetID.TOB_PARTY_GROUP_ID, WidgetID.Tob.PARTY_STATS),
+ TOB_PARTY_INTERFACE(WidgetID.TOB_GROUP_ID, WidgetID.Tob.PARTY_INTERFACE),
+ TOB_PARTY_STATS(WidgetID.TOB_GROUP_ID, WidgetID.Tob.PARTY_STATS),
+ TOB_HEALTH_BAR(WidgetID.TOB_GROUP_ID, WidgetID.Tob.HEALTHBAR_CONTAINER),
BLAST_FURNACE_COFFER(WidgetID.BLAST_FURNACE_GROUP_ID, 2),
@@ -474,6 +483,8 @@ public enum WidgetInfo
WORLD_SWITCHER_LIST(WidgetID.WORLD_SWITCHER_GROUP_ID, WidgetID.WorldSwitcher.WORLD_LIST),
+ FOSSIL_ISLAND_OXYGENBAR(WidgetID.FOSSIL_ISLAND_OXYGENBAR_ID, 2),
+
MINIGAME_TELEPORT_BUTTON(WidgetID.MINIGAME_TAB_ID, WidgetID.Minigames.TELEPORT_BUTTON),
SPELL_LUMBRIDGE_HOME_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.StandardSpellBook.LUMBRIDGE_HOME_TELEPORT),
@@ -521,6 +532,8 @@ public enum WidgetInfo
GAUNTLET_TIMER_CONTAINER(WidgetID.GAUNTLET_TIMER_GROUP_ID, WidgetID.GauntletTimer.CONTAINER),
HALLOWED_SEPULCHRE_TIMER_CONTAINER(WidgetID.HALLOWED_SEPULCHRE_TIMER_GROUP_ID, WidgetID.HallowedSepulchreTimer.CONTAINER),
+ HEALTH_OVERLAY_BAR(WidgetID.HEALTH_OVERLAY_BAR_GROUP_ID, 4),
+
TRAILBLAZER_AREA_TELEPORT(WidgetID.TRAILBLAZER_AREAS_GROUP_ID, WidgetID.TrailblazerAreas.TELEPORT),
MULTICOMBAT_FIXED(WidgetID.FIXED_VIEWPORT_GROUP_ID, WidgetID.FixedViewport.MULTICOMBAT_INDICATOR),
diff --git a/runelite-client/src/main/java/com/openosrs/client/graphics/ModelOutlineRenderer.java b/runelite-client/src/main/java/com/openosrs/client/graphics/ModelOutlineRenderer.java
index 87b59dc036..6e366ef7ad 100644
--- a/runelite-client/src/main/java/com/openosrs/client/graphics/ModelOutlineRenderer.java
+++ b/runelite-client/src/main/java/com/openosrs/client/graphics/ModelOutlineRenderer.java
@@ -33,26 +33,27 @@ import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
-
import lombok.RequiredArgsConstructor;
import lombok.Value;
import net.runelite.api.Client;
import net.runelite.api.DecorativeObject;
import net.runelite.api.GameObject;
import net.runelite.api.GroundObject;
+import net.runelite.api.ItemLayer;
import net.runelite.api.MainBufferProvider;
import net.runelite.api.Model;
import net.runelite.api.NPC;
import net.runelite.api.NPCComposition;
import net.runelite.api.Perspective;
import net.runelite.api.Player;
-import net.runelite.api.ItemLayer;
+import net.runelite.api.Renderable;
import net.runelite.api.TileObject;
import net.runelite.api.WallObject;
import net.runelite.api.coords.LocalPoint;
import net.runelite.client.task.Schedule;
import net.runelite.client.task.Scheduler;
+@Deprecated(forRemoval = true, since = "4.9.2")
@Singleton
public class ModelOutlineRenderer
{
@@ -929,11 +930,13 @@ public class ModelOutlineRenderer
renderOutline(image, outlineWidth, innerColor, outerColor);
}
+ @Deprecated(forRemoval = true, since = "4.9.2")
public void drawOutline(NPC npc, int outlineWidth, Color color)
{
drawOutline(npc, outlineWidth, color, color);
}
+ @Deprecated(forRemoval = true, since = "4.9.2")
public void drawOutline(NPC npc, int outlineWidth, Color innerColor, Color outerColor)
{
int size = 1;
@@ -957,11 +960,13 @@ public class ModelOutlineRenderer
}
}
+ @Deprecated(forRemoval = true, since = "4.9.2")
public void drawOutline(Player player, int outlineWidth, Color color)
{
drawOutline(player, outlineWidth, color, color);
}
+ @Deprecated(forRemoval = true, since = "4.9.2")
public void drawOutline(Player player, int outlineWidth, Color innerColor, Color outerColor)
{
LocalPoint lp = player.getLocalLocation();
@@ -973,17 +978,25 @@ public class ModelOutlineRenderer
}
}
+ @Deprecated(forRemoval = true, since = "4.9.2")
public void drawOutline(GameObject gameObject, int outlineWidth, Color innerColor, Color outerColor)
{
LocalPoint lp = gameObject.getLocalLocation();
if (lp != null)
{
- drawModelOutline(gameObject.getModel(), lp.getX(), lp.getY(),
+ Renderable renderable = gameObject.getRenderable();
+ if (renderable == null)
+ {
+ return;
+ }
+
+ drawModelOutline(renderable instanceof Model ? (Model) renderable : renderable.getModel(), lp.getX(), lp.getY(),
Perspective.getTileHeight(client, lp, gameObject.getPlane()),
- gameObject.getRsOrientation(), outlineWidth, innerColor, outerColor);
+ gameObject.getModelOrientation(), outlineWidth, innerColor, outerColor);
}
}
+ @Deprecated(forRemoval = true, since = "4.9.2")
public void drawOutline(GroundObject groundObject, int outlineWidth, Color innerColor, Color outerColor)
{
LocalPoint lp = groundObject.getLocalLocation();
@@ -1031,23 +1044,31 @@ public class ModelOutlineRenderer
LocalPoint lp = decorativeObject.getLocalLocation();
if (lp != null)
{
- Model model = decorativeObject.getModel1();
- if (model != null)
+ Renderable renderable1 = decorativeObject.getRenderable();
+ if (renderable1 != null)
{
- drawModelOutline(model,
- lp.getX() + decorativeObject.getXOffset(),
- lp.getY() + decorativeObject.getYOffset(),
- Perspective.getTileHeight(client, lp, decorativeObject.getPlane()),
- decorativeObject.getOrientation(), outlineWidth, innerColor, outerColor);
+ Model model = renderable1 instanceof Model ? (Model) renderable1 : renderable1.getModel();
+ if (model != null)
+ {
+ drawModelOutline(model,
+ lp.getX() + decorativeObject.getXOffset(),
+ lp.getY() + decorativeObject.getYOffset(),
+ Perspective.getTileHeight(client, lp, decorativeObject.getPlane()),
+ 0, outlineWidth, innerColor, outerColor);
+ }
}
- model = decorativeObject.getModel2();
- if (model != null)
+ Renderable renderable2 = decorativeObject.getRenderable2();
+ if (renderable2 != null)
{
- // Offset is not used for the second model
- drawModelOutline(model, lp.getX(), lp.getY(),
- Perspective.getTileHeight(client, lp, decorativeObject.getPlane()),
- decorativeObject.getOrientation(), outlineWidth, innerColor, outerColor);
+ Model model = renderable2 instanceof Model ? (Model) renderable2 : renderable2.getModel();
+ if (model != null)
+ {
+ // Offset is not used for the second model
+ drawModelOutline(model, lp.getX(), lp.getY(),
+ Perspective.getTileHeight(client, lp, decorativeObject.getPlane()),
+ 0, outlineWidth, innerColor, outerColor);
+ }
}
}
}
@@ -1075,11 +1096,13 @@ public class ModelOutlineRenderer
}
}
+ @Deprecated(forRemoval = true, since = "4.9.2")
public void drawOutline(TileObject tileObject, int outlineWidth, Color color)
{
drawOutline(tileObject, outlineWidth, color, color);
}
+ @Deprecated(forRemoval = true, since = "4.9.2")
public void drawOutline(TileObject tileObject,
int outlineWidth, Color innerColor, Color outerColor)
{
@@ -1107,9 +1130,9 @@ public class ModelOutlineRenderer
@Value
@RequiredArgsConstructor
- class PixelDistanceAlpha
+ static class PixelDistanceAlpha
{
- private final int outerAlpha;
- private final int distArrayPos;
+ int outerAlpha;
+ int distArrayPos;
}
}
\ No newline at end of file
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 902ae3c6e9..cd0c8036da 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
@@ -171,11 +171,11 @@ public enum HotColdLocation
ZEAH_LOVAKITE_FURNACE(MASTER, new WorldPoint(1507, 3819, 0), ZEAH, "Next to the lovakite furnace in Lovakengj.", ANCIENT_WIZARDS),
ZEAH_LOVAKENGJ_MINE(MASTER, new WorldPoint(1477, 3778, 0), ZEAH, "Next to mithril rock in the Lovakengj mine.", ANCIENT_WIZARDS),
ZEAH_SULPHR_MINE(MASTER, new WorldPoint(1428, 3869, 0), ZEAH, "Western entrance in the Lovakengj sulphur mine. Facemask or Slayer Helmet recommended.", BRASSICAN_MAGE),
- ZEAH_SHAYZIEN_BANK(MASTER, new WorldPoint(1498, 3627, 0), ZEAH, "South-east of the bank in Shayzien Encampment.", ANCIENT_WIZARDS),
+ ZEAH_SHAYZIEN_BANK(MASTER, new WorldPoint(1498, 3627, 0), ZEAH, "South-east of the bank in Shayzien Encampment.", BRASSICAN_MAGE),
ZEAH_OVERPASS(MASTER, new WorldPoint(1467, 3714, 0), ZEAH, "Overpass between Lovakengj and Shayzien.", BRASSICAN_MAGE),
ZEAH_LIZARDMAN(MASTER, new WorldPoint(1490, 3698, 0), ZEAH, "Within Lizardman Canyon, east of the ladder. Requires 5% favour with Shayzien.", ANCIENT_WIZARDS),
ZEAH_COMBAT_RING(MASTER, new WorldPoint(1557, 3624, 0), ZEAH, "Shayzien Encampment, south-east of the Combat Ring.", BRASSICAN_MAGE),
- ZEAH_SHAYZIEN_BANK_2(MASTER, new WorldPoint(1490, 3602, 0), ZEAH, "North of the bank in Shayzien.", ANCIENT_WIZARDS),
+ ZEAH_SHAYZIEN_BANK_2(MASTER, new WorldPoint(1490, 3602, 0), ZEAH, "North of the bank in Shayzien.", BRASSICAN_MAGE),
ZEAH_LIBRARY(MASTER, new WorldPoint(1603, 3843, 0), ZEAH, "North-west of the Arceuus Library.", BRASSICAN_MAGE),
ZEAH_HOUSECHURCH(MASTER, new WorldPoint(1682, 3792, 0), ZEAH, "By the entrance to the Arceuus church.", ANCIENT_WIZARDS),
ZEAH_DARK_ALTAR(MASTER, new WorldPoint(1698, 3881, 0), ZEAH, "West of the Dark Altar.", BRASSICAN_MAGE),
@@ -184,7 +184,7 @@ public enum HotColdLocation
ZEAH_ESSENCE_MINE_NE(MASTER, new WorldPoint(1773, 3867, 0), ZEAH, "North-east of the Arceuus essence mine.", BRASSICAN_MAGE),
ZEAH_PISCARILUS_MINE(MASTER, new WorldPoint(1768, 3705, 0), ZEAH, "South of the Piscarilius mine.", ANCIENT_WIZARDS),
ZEAH_GOLDEN_FIELD_TAVERN(MASTER, new WorldPoint(1718, 3643, 0), ZEAH, "South of the gravestone in Kingstown.", BRASSICAN_MAGE),
- ZEAH_MESS_HALL(MASTER, new WorldPoint(1656, 3621, 0), ZEAH, "East of the Mess hall.", ANCIENT_WIZARDS),
+ ZEAH_MESS_HALL(MASTER, new WorldPoint(1656, 3621, 0), ZEAH, "East of the Mess hall.", BRASSICAN_MAGE),
ZEAH_WATSONS_HOUSE(MASTER, new WorldPoint(1653, 3573, 0), ZEAH, "East of Watson's house.", BRASSICAN_MAGE),
ZEAH_VANNAHS_FARM_STORE(MASTER, new WorldPoint(1807, 3523, 0), ZEAH, "North of Tithe Farm, next to the pond.", BRASSICAN_MAGE),
ZEAH_FARMING_GUILD_W(MASTER, new WorldPoint(1208, 3736, 0), ZEAH, "West of the Farming Guild.", BRASSICAN_MAGE),
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/combatlevel/CombatLevelPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/combatlevel/CombatLevelPlugin.java
index 0d401d8e13..13b60c0bbe 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/combatlevel/CombatLevelPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/combatlevel/CombatLevelPlugin.java
@@ -40,7 +40,6 @@ import net.runelite.api.events.GameTick;
import net.runelite.api.events.ScriptPostFired;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo;
-import net.runelite.client.callback.ClientThread;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ConfigChanged;
@@ -59,19 +58,11 @@ public class CombatLevelPlugin extends Plugin
private static final String CONFIG_GROUP = "combatlevel";
private static final String ATTACK_RANGE_CONFIG_KEY = "wildernessAttackLevelRange";
private static final Pattern WILDERNESS_LEVEL_PATTERN = Pattern.compile("^Level: (\\d+)$");
- private static final int SKULL_CONTAINER_ADJUSTED_ORIGINAL_Y = 6;
- private static final int WILDERNESS_LEVEL_TEXT_ADJUSTED_ORIGINAL_Y = 3;
private static final int MIN_COMBAT_LEVEL = 3;
- private int originalWildernessLevelTextPosition = -1;
- private int originalSkullContainerPosition = -1;
-
@Inject
private Client client;
- @Inject
- private ClientThread clientThread;
-
@Inject
private CombatLevelConfig config;
@@ -187,25 +178,10 @@ public class CombatLevelPlugin extends Plugin
return;
}
- final Widget skullContainer = client.getWidget(WidgetInfo.PVP_SKULL_CONTAINER);
- if (originalWildernessLevelTextPosition == -1)
- {
- originalWildernessLevelTextPosition = wildernessLevelWidget.getOriginalY();
- }
- if (originalSkullContainerPosition == -1)
- {
- originalSkullContainerPosition = skullContainer.getRelativeY();
- }
-
final int wildernessLevel = Integer.parseInt(m.group(1));
final int combatLevel = client.getLocalPlayer().getCombatLevel();
wildernessLevelWidget.setText(wildernessLevelText + "
" + combatAttackRange(combatLevel, wildernessLevel));
- wildernessLevelWidget.setOriginalY(WILDERNESS_LEVEL_TEXT_ADJUSTED_ORIGINAL_Y);
- skullContainer.setOriginalY(SKULL_CONTAINER_ADJUSTED_ORIGINAL_Y);
-
- clientThread.invoke(wildernessLevelWidget::revalidate);
- clientThread.invoke(skullContainer::revalidate);
}
private void shutDownAttackLevelRange()
@@ -223,18 +199,7 @@ public class CombatLevelPlugin extends Plugin
{
wildernessLevelWidget.setText(wildernessLevelText.substring(0, wildernessLevelText.indexOf("
")));
}
- wildernessLevelWidget.setOriginalY(originalWildernessLevelTextPosition);
- clientThread.invoke(wildernessLevelWidget::revalidate);
}
- originalWildernessLevelTextPosition = -1;
-
- final Widget skullContainer = client.getWidget(WidgetInfo.PVP_SKULL_CONTAINER);
- if (skullContainer != null)
- {
- skullContainer.setOriginalY(originalSkullContainerPosition);
- clientThread.invoke(skullContainer::revalidate);
- }
- originalSkullContainerPosition = -1;
}
private static String combatAttackRange(final int combatLevel, final int wildernessLevel)
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/mta/alchemy/AlchemyRoom.java b/runelite-client/src/main/java/net/runelite/client/plugins/mta/alchemy/AlchemyRoom.java
index 485a7f6d91..ef03367b0a 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/mta/alchemy/AlchemyRoom.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/mta/alchemy/AlchemyRoom.java
@@ -79,8 +79,8 @@ public class AlchemyRoom extends MTARoom
private static final int IMAGE_Z_OFFSET = 150;
private static final int NUM_CUPBOARDS = 8;
- private static final int INFO_ITEM_START = 8;
- private static final int INFO_POINT_START = 13;
+ private static final int INFO_ITEM_START = 7;
+ private static final int INFO_POINT_START = 12;
private static final int INFO_LENGTH = 5;
private static final int BEST_POINTS = 30;
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsConfig.java
index 6c86a373d0..ba45c310ef 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsConfig.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/npchighlight/NpcIndicatorsConfig.java
@@ -30,6 +30,7 @@ import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
import net.runelite.client.config.ConfigSection;
+import net.runelite.client.config.Range;
@ConfigGroup("npcindicators")
public interface NpcIndicatorsConfig extends Config
@@ -79,6 +80,59 @@ public interface NpcIndicatorsConfig extends Config
@ConfigItem(
position = 3,
+ keyName = "highlightOutline",
+ name = "Highlight outline",
+ description = "Configures whether or not the model of the NPC should be highlighted by outline",
+ section = renderStyleSection
+ )
+ default boolean highlightOutline()
+ {
+ return false;
+ }
+
+ @Alpha
+ @ConfigItem(
+ position = 4,
+ keyName = "npcColor",
+ name = "Highlight Color",
+ description = "Color of the NPC highlight",
+ section = renderStyleSection
+ )
+ default Color getHighlightColor()
+ {
+ return Color.CYAN;
+ }
+
+ @ConfigItem(
+ position = 5,
+ keyName = "borderWidth",
+ name = "Border Width",
+ description = "Width of the highlighted NPC border",
+ section = renderStyleSection
+ )
+ default double borderWidth()
+ {
+ return 2;
+ }
+
+ @ConfigItem(
+ position = 6,
+ keyName = "outlineFeather",
+ name = "Outline feather",
+ description = "Specify between 0-4 how much of the model outline should be faded",
+ section = renderStyleSection
+ )
+ @Range(
+ min = 0,
+ max = 4
+ )
+ default int outlineFeather()
+ {
+ return 0;
+ }
+
+ @ConfigItem(
+ position = 7,
keyName = "npcToHighlight",
name = "NPCs to Highlight",
description = "List of NPC names to highlight"
@@ -95,31 +149,8 @@ public interface NpcIndicatorsConfig extends Config
)
void setNpcToHighlight(String npcsToHighlight);
- @Alpha
@ConfigItem(
- position = 4,
- keyName = "npcColor",
- name = "Highlight Color",
- description = "Color of the NPC highlight"
- )
- default Color getHighlightColor()
- {
- return Color.CYAN;
- }
-
- @ConfigItem(
- position = 5,
- keyName = "borderWidth",
- name = "Border Width",
- description = "Width of the highlighted NPC border"
- )
- default double borderWidth()
- {
- return 2;
- }
-
- @ConfigItem(
- position = 6,
+ position = 8,
keyName = "drawNames",
name = "Draw names above NPC",
description = "Configures whether or not NPC names should be drawn above the NPC"
@@ -130,7 +161,7 @@ public interface NpcIndicatorsConfig extends Config
}
@ConfigItem(
- position = 7,
+ position = 9,
keyName = "drawMinimapNames",
name = "Draw names on minimap",
description = "Configures whether or not NPC names should be drawn on the minimap"
@@ -141,7 +172,7 @@ public interface NpcIndicatorsConfig extends Config
}
@ConfigItem(
- position = 8,
+ position = 10,
keyName = "highlightMenuNames",
name = "Highlight menu names",
description = "Highlight NPC names in right click menu"
@@ -152,7 +183,7 @@ public interface NpcIndicatorsConfig extends Config
}
@ConfigItem(
- position = 9,
+ position = 11,
keyName = "ignoreDeadNpcs",
name = "Ignore dead NPCs",
description = "Prevents highlighting NPCs after they are dead"
@@ -163,7 +194,7 @@ public interface NpcIndicatorsConfig extends Config
}
@ConfigItem(
- position = 10,
+ position = 12,
keyName = "deadNpcMenuColor",
name = "Dead NPC menu color",
description = "Color of the NPC menus for dead NPCs"
@@ -171,7 +202,7 @@ public interface NpcIndicatorsConfig extends Config
Color deadNpcMenuColor();
@ConfigItem(
- position = 11,
+ position = 13,
keyName = "showRespawnTimer",
name = "Show respawn timer",
description = "Show respawn timer of tagged NPCs")
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/npchighlight/NpcSceneOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/npchighlight/NpcSceneOverlay.java
index d07f53c2bf..7b7bf9b461 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/npchighlight/NpcSceneOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/npchighlight/NpcSceneOverlay.java
@@ -48,6 +48,7 @@ import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.OverlayUtil;
+import net.runelite.client.ui.overlay.outline.ModelOutlineRenderer;
import net.runelite.client.util.ColorUtil;
import net.runelite.client.util.Text;
@@ -67,13 +68,16 @@ public class NpcSceneOverlay extends Overlay
private final Client client;
private final NpcIndicatorsConfig config;
private final NpcIndicatorsPlugin plugin;
+ private final ModelOutlineRenderer modelOutlineRenderer;
@Inject
- NpcSceneOverlay(Client client, NpcIndicatorsConfig config, NpcIndicatorsPlugin plugin)
+ NpcSceneOverlay(Client client, NpcIndicatorsConfig config, NpcIndicatorsPlugin plugin,
+ ModelOutlineRenderer modelOutlineRenderer)
{
this.client = client;
this.config = config;
this.plugin = plugin;
+ this.modelOutlineRenderer = modelOutlineRenderer;
setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.ABOVE_SCENE);
}
@@ -181,6 +185,11 @@ public class NpcSceneOverlay extends Overlay
renderPoly(graphics, color, southWestTilePoly);
}
+ if (config.highlightOutline())
+ {
+ modelOutlineRenderer.drawOutline(actor, (int)config.borderWidth(), color, config.outlineFeather());
+ }
+
if (config.drawNames() && actor.getName() != null)
{
String npcName = Text.removeTags(actor.getName());
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsConfig.java
index eacde6f23f..bc5d0e7751 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsConfig.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsConfig.java
@@ -30,15 +30,50 @@ import net.runelite.client.config.Alpha;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
+import net.runelite.client.config.ConfigSection;
+import net.runelite.client.config.Range;
@ConfigGroup("objectindicators")
public interface ObjectIndicatorsConfig extends Config
{
+ @ConfigSection(
+ name = "Render style",
+ description = "The render style of object highlighting",
+ position = 0
+ )
+ String renderStyleSection = "renderStyleSection";
+
+ @ConfigItem(
+ position = 0,
+ keyName = "highlightHull",
+ name = "Highlight hull",
+ description = "Configures whether or not object should be highlighted by hull",
+ section = renderStyleSection
+ )
+ default boolean highlightHull()
+ {
+ return true;
+ }
+
+ @ConfigItem(
+ position = 1,
+ keyName = "highlightOutline",
+ name = "Highlight outline",
+ description = "Configures whether or not the model of the object should be highlighted by outline",
+ section = renderStyleSection
+ )
+ default boolean highlightOutline()
+ {
+ return false;
+ }
+
@Alpha
@ConfigItem(
+ position = 2,
keyName = "markerColor",
name = "Marker color",
- description = "Configures the color of object marker"
+ description = "Configures the color of object marker",
+ section = renderStyleSection
)
default Color markerColor()
{
@@ -46,6 +81,35 @@ public interface ObjectIndicatorsConfig extends Config
}
@ConfigItem(
+ position = 3,
+ keyName = "borderWidth",
+ name = "Border Width",
+ description = "Width of the marked object border",
+ section = renderStyleSection
+ )
+ default double borderWidth()
+ {
+ return 2;
+ }
+
+ @ConfigItem(
+ position = 4,
+ keyName = "outlineFeather",
+ name = "Outline feather",
+ description = "Specify between 0-4 how much of the model outline should be faded",
+ section = renderStyleSection
+ )
+ @Range(
+ min = 0,
+ max = 4
+ )
+ default int outlineFeather()
+ {
+ return 0;
+ }
+
+ @ConfigItem(
+ position = 5,
keyName = "rememberObjectColors",
name = "Remember color per object",
description = "Color objects using the color from time of marking"
@@ -54,14 +118,4 @@ public interface ObjectIndicatorsConfig extends Config
{
return false;
}
-
- @ConfigItem(
- keyName = "borderWidth",
- name = "Border Width",
- description = "Width of the marked object border"
- )
- default double borderWidth()
- {
- return 2;
- }
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsOverlay.java
index a174e92335..087eff54f6 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/objectindicators/ObjectIndicatorsOverlay.java
@@ -43,19 +43,23 @@ import net.runelite.client.ui.overlay.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.OverlayPriority;
import net.runelite.client.ui.overlay.OverlayUtil;
+import net.runelite.client.ui.overlay.outline.ModelOutlineRenderer;
class ObjectIndicatorsOverlay extends Overlay
{
private final Client client;
private final ObjectIndicatorsConfig config;
private final ObjectIndicatorsPlugin plugin;
+ private final ModelOutlineRenderer modelOutlineRenderer;
@Inject
- private ObjectIndicatorsOverlay(Client client, ObjectIndicatorsConfig config, ObjectIndicatorsPlugin plugin)
+ private ObjectIndicatorsOverlay(Client client, ObjectIndicatorsConfig config, ObjectIndicatorsPlugin plugin,
+ ModelOutlineRenderer modelOutlineRenderer)
{
this.client = client;
this.config = config;
this.plugin = plugin;
+ this.modelOutlineRenderer = modelOutlineRenderer;
setPosition(OverlayPosition.DYNAMIC);
setPriority(OverlayPriority.LOW);
setLayer(OverlayLayer.ABOVE_SCENE);
@@ -93,43 +97,56 @@ class ObjectIndicatorsOverlay extends Overlay
color = config.markerColor();
}
- final Shape polygon;
- Shape polygon2 = null;
-
- if (object instanceof GameObject)
+ if (config.highlightHull())
{
- polygon = ((GameObject) object).getConvexHull();
- }
- else if (object instanceof WallObject)
- {
- polygon = ((WallObject) object).getConvexHull();
- polygon2 = ((WallObject) object).getConvexHull2();
- }
- else if (object instanceof DecorativeObject)
- {
- polygon = ((DecorativeObject) object).getConvexHull();
- polygon2 = ((DecorativeObject) object).getConvexHull2();
- }
- else if (object instanceof GroundObject)
- {
- polygon = ((GroundObject) object).getConvexHull();
- }
- else
- {
- polygon = object.getCanvasTilePoly();
+ renderConvexHull(graphics, object, color, stroke);
}
- if (polygon != null)
+ if (config.highlightOutline())
{
- OverlayUtil.renderPolygon(graphics, polygon, color, stroke);
- }
-
- if (polygon2 != null)
- {
- OverlayUtil.renderPolygon(graphics, polygon2, color, stroke);
+ modelOutlineRenderer.drawOutline(object, (int)config.borderWidth(), color, config.outlineFeather());
}
}
return null;
}
+
+ private void renderConvexHull(Graphics2D graphics, TileObject object, Color color, Stroke stroke)
+ {
+ final Shape polygon;
+ Shape polygon2 = null;
+
+ if (object instanceof GameObject)
+ {
+ polygon = ((GameObject) object).getConvexHull();
+ }
+ else if (object instanceof WallObject)
+ {
+ polygon = ((WallObject) object).getConvexHull();
+ polygon2 = ((WallObject) object).getConvexHull2();
+ }
+ else if (object instanceof DecorativeObject)
+ {
+ polygon = ((DecorativeObject) object).getConvexHull();
+ polygon2 = ((DecorativeObject) object).getConvexHull2();
+ }
+ else if (object instanceof GroundObject)
+ {
+ polygon = ((GroundObject) object).getConvexHull();
+ }
+ else
+ {
+ polygon = object.getCanvasTilePoly();
+ }
+
+ if (polygon != null)
+ {
+ OverlayUtil.renderPolygon(graphics, polygon, color, stroke);
+ }
+
+ if (polygon2 != null)
+ {
+ OverlayUtil.renderPolygon(graphics, polygon2, color, stroke);
+ }
+ }
}
\ No newline at end of file
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java
index 5ff2078eba..5f71fb4e40 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java
@@ -711,7 +711,7 @@ public class TimersPlugin extends Plugin
}
else if (message.contains(RESURRECT_THRALL_MESSAGE_START) && message.endsWith(RESURRECT_THRALL_MESSAGE_END))
{
- createGameTimer(RESURRECT_THRALL, duration);
+ createGameTimer(RESURRECT_THRALL, Duration.of(client.getBoostedSkillLevel(Skill.MAGIC), RSTimeUnit.GAME_TICKS));
}
else if (message.contains(RESURRECT_THRALL_DISAPPEAR_MESSAGE_START) && message.endsWith(RESURRECT_THRALL_DISAPPEAR_MESSAGE_END))
{
diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetOverlay.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetOverlay.java
index 6119b0a5df..f14865a527 100644
--- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/WidgetOverlay.java
@@ -42,10 +42,16 @@ public class WidgetOverlay extends Overlay
return Arrays.asList(
new WidgetOverlay(client, WidgetInfo.RESIZABLE_MINIMAP_WIDGET, OverlayPosition.CANVAS_TOP_RIGHT),
new WidgetOverlay(client, WidgetInfo.RESIZABLE_MINIMAP_STONES_WIDGET, OverlayPosition.CANVAS_TOP_RIGHT),
+ new WidgetOverlay(client, WidgetInfo.FOSSIL_ISLAND_OXYGENBAR, OverlayPosition.TOP_CENTER),
new XpTrackerWidgetOverlay(client, WidgetInfo.EXPERIENCE_TRACKER_WIDGET, OverlayPosition.TOP_RIGHT),
new WidgetOverlay(client, WidgetInfo.RAIDS_POINTS_INFOBOX, OverlayPosition.TOP_RIGHT),
+ new WidgetOverlay(client, WidgetInfo.TOB_PARTY_INTERFACE, OverlayPosition.TOP_LEFT),
new WidgetOverlay(client, WidgetInfo.TOB_PARTY_STATS, OverlayPosition.TOP_LEFT),
+ new WidgetOverlay(client, WidgetInfo.GWD_KC, OverlayPosition.TOP_RIGHT),
new WidgetOverlay(client, WidgetInfo.TITHE_FARM, OverlayPosition.TOP_RIGHT),
+ new WidgetOverlay(client, WidgetInfo.PEST_CONTROL_BOAT_INFO, OverlayPosition.TOP_LEFT),
+ new WidgetOverlay(client, WidgetInfo.PEST_CONTROL_KNIGHT_INFO_CONTAINER, OverlayPosition.TOP_LEFT),
+ new WidgetOverlay(client, WidgetInfo.PEST_CONTROL_ACTIVITY_SHIELD_INFO_CONTAINER, OverlayPosition.TOP_RIGHT),
new WidgetOverlay(client, WidgetInfo.ZEAH_MESS_HALL_COOKING_DISPLAY, OverlayPosition.TOP_LEFT),
new WidgetOverlay(client, WidgetInfo.PVP_KILLDEATH_COUNTER, OverlayPosition.TOP_LEFT),
new WidgetOverlay(client, WidgetInfo.SKOTIZO_CONTAINER, OverlayPosition.TOP_LEFT),
@@ -55,7 +61,11 @@ public class WidgetOverlay extends Overlay
new WidgetOverlay(client, WidgetInfo.LMS_KDA, OverlayPosition.TOP_RIGHT),
new WidgetOverlay(client, WidgetInfo.GAUNTLET_TIMER_CONTAINER, OverlayPosition.TOP_LEFT),
new WidgetOverlay(client, WidgetInfo.HALLOWED_SEPULCHRE_TIMER_CONTAINER, OverlayPosition.TOP_LEFT),
+ new WidgetOverlay(client, WidgetInfo.HEALTH_OVERLAY_BAR, OverlayPosition.TOP_CENTER),
+ new WidgetOverlay(client, WidgetInfo.TOB_HEALTH_BAR, OverlayPosition.TOP_CENTER),
new WidgetOverlay(client, WidgetInfo.NIGHTMARE_PILLAR_HEALTH, OverlayPosition.TOP_LEFT),
+ new WidgetOverlay(client, WidgetInfo.VOLCANIC_MINE_VENTS_INFOBOX_GROUP, OverlayPosition.BOTTOM_RIGHT),
+ new WidgetOverlay(client, WidgetInfo.VOLCANIC_MINE_STABILITY_INFOBOX_GROUP, OverlayPosition.BOTTOM_LEFT),
new WidgetOverlay(client, WidgetInfo.MULTICOMBAT_FIXED, OverlayPosition.BOTTOM_RIGHT),
new WidgetOverlay(client, WidgetInfo.MULTICOMBAT_RESIZEABLE_MODERN, OverlayPosition.CANVAS_TOP_RIGHT),
new WidgetOverlay(client, WidgetInfo.MULTICOMBAT_RESIZEABLE_CLASSIC, OverlayPosition.CANVAS_TOP_RIGHT),
diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/outline/IntBlockBuffer.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/outline/IntBlockBuffer.java
new file mode 100644
index 0000000000..500a297985
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/outline/IntBlockBuffer.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2021, Woox
+ * 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.ui.overlay.outline;
+
+import java.util.Arrays;
+
+/**
+ * A class which manages 1024-sized blocks of memory.
+ */
+class IntBlockBuffer
+{
+ public static final int BLOCK_BITS = 10;
+ public static final int BLOCK_SIZE = 1 << BLOCK_BITS;
+
+ private int[] memory = new int[0];
+ private int[] unusedBlockIndices = new int[0];
+ private int unusedBlockIndicesLength;
+
+ private void increaseBlockCount()
+ {
+ int currBlockCount = memory.length >> BLOCK_BITS;
+ int newBlockCount = Math.max(1, currBlockCount * 2);
+ memory = Arrays.copyOf(memory, newBlockCount * BLOCK_SIZE);
+ unusedBlockIndices = Arrays.copyOf(unusedBlockIndices, newBlockCount);
+ for (int i = currBlockCount; i < newBlockCount; i++)
+ {
+ unusedBlockIndices[unusedBlockIndicesLength++] = i;
+ }
+ }
+
+ /**
+ * Retrieves the whole memory buffer.
+ */
+ public int[] getMemory()
+ {
+ return memory;
+ }
+
+ /**
+ * Marks a new block as used.
+ *
+ * @return The index of the block.
+ */
+ public int useNewBlock()
+ {
+ if (unusedBlockIndicesLength == 0)
+ {
+ increaseBlockCount();
+ }
+
+ return unusedBlockIndices[--unusedBlockIndicesLength];
+ }
+
+ /**
+ * Marks a block as unused.
+ *
+ * @param index The index of the block. The block should be in use before calling this method.
+ */
+ public void freeBlock(int index)
+ {
+ unusedBlockIndices[unusedBlockIndicesLength++] = index;
+ }
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/outline/ModelOutlineRenderer.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/outline/ModelOutlineRenderer.java
new file mode 100644
index 0000000000..8fd9daa4ae
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/outline/ModelOutlineRenderer.java
@@ -0,0 +1,1188 @@
+/*
+ * Copyright (c) 2018-2021, Woox
+ * 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.ui.overlay.outline;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import java.awt.Color;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import net.runelite.api.Client;
+import net.runelite.api.DecorativeObject;
+import net.runelite.api.GameObject;
+import net.runelite.api.GraphicsObject;
+import net.runelite.api.GroundObject;
+import net.runelite.api.ItemLayer;
+import net.runelite.api.MainBufferProvider;
+import net.runelite.api.Model;
+import net.runelite.api.NPC;
+import net.runelite.api.NPCComposition;
+import net.runelite.api.Perspective;
+import net.runelite.api.Player;
+import net.runelite.api.Renderable;
+import net.runelite.api.TileObject;
+import net.runelite.api.WallObject;
+import net.runelite.api.coords.LocalPoint;
+
+@Singleton
+public class ModelOutlineRenderer
+{
+ @AllArgsConstructor
+ private static class PixelDistanceDelta
+ {
+ private final int dx;
+ private final int dy;
+ }
+
+ @AllArgsConstructor
+ private static class PixelDistanceGroupIndex
+ {
+ @Getter(AccessLevel.PRIVATE)
+ private final double distance;
+ private final int distanceGroupIndex;
+ private final double alphaMultiply;
+ }
+
+ private static final int MAX_OUTLINE_WIDTH = 50;
+ private static final int MAX_FEATHER = 4;
+ private static final int DIRECT_WRITE_OUTLINE_WIDTH_THRESHOLD = 10;
+
+ private final Client client;
+
+ // Vertex positions projected on the screen.
+ private final int[] projectedVerticesX = new int[6500];
+ private final int[] projectedVerticesY = new int[6500];
+
+ // Window boundaries for the ingame world
+ private int clipX1;
+ private int clipY1;
+ private int clipX2;
+ private int clipY2;
+
+ // Boundaries for the outline
+ private int croppedX1;
+ private int croppedY1;
+ private int croppedX2;
+ private int croppedY2;
+ private int croppedWidth;
+ private int croppedHeight;
+
+ // Bitset with pixel positions that would be rendered to within the cropped area by the model.
+ private int[] visited = new int[0];
+
+ // Memory used for queueing the pixels for the outline of the model.
+ // Pixels are grouped by x and y distance to the closest pixel drawn on the model.
+ // A block buffer is used so memory can be reused after a group has been processed
+ // without using the JVM garbage collector.
+ private final IntBlockBuffer outlinePixelsBlockBuffer = new IntBlockBuffer();
+ private int[][] outlinePixelsBlockIndices = new int[0][];
+ private int[] outlinePixelsBlockIndicesLengths = new int[0];
+ private int[] outlinePixelsLastBlockLength;
+ private int outlineArrayWidth;
+
+ // An array of pixel group indices ordered by distance for each outline width and feather.
+ // These are calculated once upon first usage and then stored here to skip reevaluation.
+ private PixelDistanceGroupIndex[][][] precomputedGroupIndices = new PixelDistanceGroupIndex[0][][];
+
+ // An array of pixel distance deltas for each outline width and direction (right/up/left/down).
+ // These are calculated once upon first usage and then stored here to skip reevaluation.
+ private PixelDistanceDelta[][][] precomputedDistanceDeltas = new PixelDistanceDelta[0][][];
+
+ @Inject
+ private ModelOutlineRenderer(Client client)
+ {
+ this.client = client;
+ }
+
+ /**
+ * Calculate the next power of two of a value.
+ *
+ * @param value The value to find the next power of two of.
+ * @return Returns the next power of two.
+ */
+ private static int nextPowerOfTwo(int value)
+ {
+ value--;
+ value |= value >> 1;
+ value |= value >> 2;
+ value |= value >> 4;
+ value |= value >> 8;
+ value |= value >> 16;
+ value++;
+ return value;
+ }
+
+ /**
+ * Determine if a triangle goes counter clockwise
+ *
+ * @return Returns true if the triangle goes counter clockwise and should be culled, otherwise false
+ */
+ private static boolean cullFace(int x1, int y1, int x2, int y2, int x3, int y3)
+ {
+ return (y2 - y1) * (x3 - x2) - (x2 - x1) * (y3 - y2) <= 0;
+ }
+
+ /**
+ * Get an array of pixel outline group indices ordered by distance for a specific outline width.
+ *
+ * @param outlineWidth The outline width.
+ * @param feather The feather of the outline.
+ * @return Returns the list of pixel distances.
+ */
+ private PixelDistanceGroupIndex[] getPriorityList(int outlineWidth, int feather)
+ {
+ if (precomputedGroupIndices.length <= outlineWidth)
+ {
+ precomputedGroupIndices = Arrays.copyOf(precomputedGroupIndices, outlineWidth + 1);
+ }
+ if (precomputedGroupIndices[outlineWidth] == null)
+ {
+ precomputedGroupIndices[outlineWidth] = new PixelDistanceGroupIndex[feather + 1][];
+ }
+ else if (precomputedGroupIndices[outlineWidth].length <= feather)
+ {
+ precomputedGroupIndices[outlineWidth] = Arrays.copyOf(precomputedGroupIndices[outlineWidth], feather + 1);
+ }
+
+ if (precomputedGroupIndices[outlineWidth][feather] == null)
+ {
+ double fadedDistance = (double) feather / MAX_FEATHER * (outlineWidth - 0.5);
+ List ps = new ArrayList<>();
+ for (int x = 0; x <= outlineWidth; x++)
+ {
+ for (int y = 0; y <= outlineWidth; y++)
+ {
+ if (x == 0 && y == 0)
+ {
+ continue;
+ }
+
+ double dist = Math.hypot(x, y);
+ if (dist > outlineWidth)
+ {
+ continue;
+ }
+
+ double outerDist = outlineWidth - dist + 0.5;
+ double multipliedAlpha = outerDist < fadedDistance ? outerDist / fadedDistance : 1.0;
+ ps.add(new PixelDistanceGroupIndex(dist, x + y * outlineArrayWidth, multipliedAlpha));
+ }
+ }
+ ps.sort(Comparator.comparingDouble(PixelDistanceGroupIndex::getDistance));
+ precomputedGroupIndices[outlineWidth][feather] = ps.toArray(new PixelDistanceGroupIndex[0]);
+ }
+ return precomputedGroupIndices[outlineWidth][feather];
+ }
+
+ private void ensureDistanceDeltasCreated(int outlineWidth)
+ {
+ if (precomputedDistanceDeltas.length <= outlineWidth)
+ {
+ precomputedDistanceDeltas = Arrays.copyOf(precomputedDistanceDeltas, outlineWidth + 1);
+ }
+ if (precomputedDistanceDeltas[outlineWidth] == null)
+ {
+ precomputedDistanceDeltas[outlineWidth] = new PixelDistanceDelta[4][];
+ }
+ if (precomputedDistanceDeltas[outlineWidth][0] != null)
+ {
+ return;
+ }
+
+ List distances = new ArrayList<>();
+ for (int dy = -outlineWidth; dy <= outlineWidth; dy++)
+ {
+ for (int dx = 1; dx <= outlineWidth; dx++)
+ {
+ if (Math.abs(dy) > dx)
+ {
+ continue;
+ }
+
+ double dist = Math.hypot(dx, dy);
+ if (dist > outlineWidth)
+ {
+ continue;
+ }
+
+ distances.add(new PixelDistanceDelta(dx, dy));
+ }
+ }
+
+ for (int direction = 0; direction < 4; direction++)
+ {
+ precomputedDistanceDeltas[outlineWidth][direction] = distances.toArray(new PixelDistanceDelta[0]);
+
+ // Turn 90 deg ccw
+ for (int i = 0; i < distances.size(); i++)
+ {
+ PixelDistanceDelta pdd = distances.get(i);
+ distances.set(i, new PixelDistanceDelta(pdd.dy, -pdd.dx));
+ }
+ }
+ }
+
+ /**
+ * Enqueues a pixel for outlining.
+ *
+ * @param distanceGroupIndex The group index to enqueue the pixel into.
+ * @param x The x position of the pixel.
+ * @param y The y position of the pixel.
+ */
+ private void enqueueOutlinePixel(int distanceGroupIndex, int x, int y)
+ {
+ if (outlinePixelsLastBlockLength[distanceGroupIndex] == IntBlockBuffer.BLOCK_SIZE)
+ {
+ int minimumBlockIndicesSize = outlinePixelsBlockIndicesLengths[distanceGroupIndex] + 1;
+ if (minimumBlockIndicesSize > outlinePixelsBlockIndices[distanceGroupIndex].length)
+ {
+ outlinePixelsBlockIndices[distanceGroupIndex] = Arrays.copyOf(
+ outlinePixelsBlockIndices[distanceGroupIndex],
+ nextPowerOfTwo(minimumBlockIndicesSize));
+ }
+ outlinePixelsBlockIndices[distanceGroupIndex][outlinePixelsBlockIndicesLengths[distanceGroupIndex]] =
+ outlinePixelsBlockBuffer.useNewBlock();
+ outlinePixelsBlockIndicesLengths[distanceGroupIndex]++;
+ outlinePixelsLastBlockLength[distanceGroupIndex] = 0;
+ }
+
+ int[] memory = outlinePixelsBlockBuffer.getMemory();
+ int block = outlinePixelsBlockIndices[distanceGroupIndex][outlinePixelsBlockIndicesLengths[distanceGroupIndex] - 1];
+ int blockPos = outlinePixelsLastBlockLength[distanceGroupIndex]++;
+ memory[(block << IntBlockBuffer.BLOCK_BITS) + blockPos] = (y << 16) | x;
+ }
+
+ /**
+ * Checks that the visited bitset is big enough to hold a certain amount of pixels and sets them to 0.
+ *
+ * @param pixelAmount The amount of pixels needed.
+ */
+ private void resetVisited(int pixelAmount)
+ {
+ int size = (pixelAmount >>> 5);
+ if (visited.length < size)
+ {
+ visited = new int[nextPowerOfTwo(size)];
+ }
+ Arrays.fill(visited, 0, size, 0);
+ }
+
+ /**
+ * Ensures that the outline buffer arrays are large enough to fit the current outline.
+ */
+ private void initializeOutlineBuffers()
+ {
+ int arraySizes = outlineArrayWidth * outlineArrayWidth;
+ if (outlinePixelsBlockIndicesLengths.length < arraySizes)
+ {
+ outlinePixelsBlockIndices = new int[arraySizes][];
+ outlinePixelsBlockIndicesLengths = new int[arraySizes];
+ outlinePixelsLastBlockLength = new int[arraySizes];
+ for (int i = 0; i < arraySizes; i++)
+ {
+ outlinePixelsBlockIndices[i] = new int[0];
+ }
+ }
+
+ for (int i = 0; i < arraySizes; i++)
+ {
+ outlinePixelsLastBlockLength[i] = IntBlockBuffer.BLOCK_SIZE;
+ }
+ }
+
+ /**
+ * Frees all blocks currently in use by the outline block buffer.
+ */
+ private void freeAllBlockMemory()
+ {
+ for (int i = 0; i < outlineArrayWidth * outlineArrayWidth; i++)
+ {
+ while (outlinePixelsBlockIndicesLengths[i] > 0)
+ {
+ outlinePixelsBlockIndicesLengths[i]--;
+ outlinePixelsBlockBuffer.freeBlock(outlinePixelsBlockIndices[i][outlinePixelsBlockIndicesLengths[i]]);
+ }
+ outlinePixelsLastBlockLength[i] = IntBlockBuffer.BLOCK_SIZE;
+ }
+ }
+
+ /**
+ * Simulates a horizontal line rasterization and marks pixels visited.
+ *
+ * @param pixelY The y position of the line
+ * @param x1 The starting x position
+ * @param x2 The ending x position
+ */
+ private void simulateHorizontalLineRasterizationForOutline(int pixelY, int x1, int x2)
+ {
+ if (x2 > clipX2)
+ {
+ x2 = clipX2;
+ }
+ if (x1 < clipX1)
+ {
+ x1 = clipX1;
+ }
+ if (x1 >= x2)
+ {
+ return;
+ }
+
+ int pixelPos1 = (pixelY - croppedY1) * croppedWidth + (x1 - croppedX1);
+ int pixelPos2 = pixelPos1 + x2 - x1;
+ int pixelPosIndex1 = pixelPos1 >> 5;
+ int pixelPosIndex2 = pixelPos2 >> 5;
+ if (pixelPosIndex1 == pixelPosIndex2)
+ {
+ visited[pixelPosIndex1] |= ((1 << (pixelPos2 & 31)) - 1) ^ ((1 << (pixelPos1 & 31)) - 1);
+ }
+ else
+ {
+ visited[pixelPosIndex1] |= -(1 << (pixelPos1 & 31));
+ visited[pixelPosIndex2] |= (1 << (pixelPos2 & 31)) - 1;
+ for (int i = pixelPosIndex1 + 1; i < pixelPosIndex2; i++)
+ {
+ visited[i] = 0xFFFFFFFF;
+ }
+ }
+ }
+
+ /**
+ * Simulates rasterization of a triangle and marks pixels visited.
+ *
+ * @param x1 The x position of the first vertex in the triangle
+ * @param y1 The y position of the first vertex in the triangle
+ * @param x2 The x position of the second vertex in the triangle
+ * @param y2 The y position of the second vertex in the triangle
+ * @param x3 The x position of the third vertex in the triangle
+ * @param y3 The y position of the third vertex in the triangle
+ */
+ private void simulateTriangleRasterizationForOutline(
+ int x1, int y1, int x2, int y2, int x3, int y3)
+ {
+ // Swap vertices so y1 <= y2 <= y3 using bubble sort
+ if (y1 > y2)
+ {
+ int yp = y1;
+ int xp = x1;
+ y1 = y2;
+ y2 = yp;
+ x1 = x2;
+ x2 = xp;
+ }
+ if (y2 > y3)
+ {
+ int yp = y2;
+ int xp = x2;
+ y2 = y3;
+ y3 = yp;
+ x2 = x3;
+ x3 = xp;
+ }
+ if (y1 > y2)
+ {
+ int yp = y1;
+ int xp = x1;
+ y1 = y2;
+ y2 = yp;
+ x1 = x2;
+ x2 = xp;
+ }
+
+ if (y1 > clipY2)
+ {
+ // All points are outside clip boundaries
+ return;
+ }
+
+ int slope1 = 0;
+ if (y1 != y2)
+ {
+ slope1 = (x2 - x1 << 14) / (y2 - y1);
+ }
+
+ int slope2 = 0;
+ if (y3 != y2)
+ {
+ slope2 = (x3 - x2 << 14) / (y3 - y2);
+ }
+
+ int slope3 = 0;
+ if (y1 != y3)
+ {
+ slope3 = (x1 - x3 << 14) / (y1 - y3);
+ }
+
+ if (y2 > clipY2)
+ {
+ y2 = clipY2;
+ }
+ if (y3 > clipY2)
+ {
+ y3 = clipY2;
+ }
+ if (y1 == y3 || y3 < 0)
+ {
+ return;
+ }
+
+ x1 <<= 14;
+ x2 <<= 14;
+ x3 = x1;
+
+ if (y1 < 0)
+ {
+ x3 -= y1 * slope3;
+ x1 -= y1 * slope1;
+ y1 = 0;
+ }
+ if (y2 < 0)
+ {
+ x2 -= slope2 * y2;
+ y2 = 0;
+ }
+
+ int pixelY = y1;
+ int height1 = y2 - y1;
+ int height2 = y3 - y2;
+ if (y1 != y2 && slope3 < slope1 || y1 == y2 && slope3 > slope2)
+ {
+ while (height1-- > 0)
+ {
+ simulateHorizontalLineRasterizationForOutline(pixelY, x3 >> 14, x1 >> 14);
+ x3 += slope3;
+ x1 += slope1;
+ pixelY++;
+ }
+
+ while (height2-- > 0)
+ {
+ simulateHorizontalLineRasterizationForOutline(pixelY, x3 >> 14, x2 >> 14);
+ x3 += slope3;
+ x2 += slope2;
+ pixelY++;
+ }
+ }
+ else
+ {
+ while (height1-- > 0)
+ {
+ simulateHorizontalLineRasterizationForOutline(pixelY, x1 >> 14, x3 >> 14);
+ x1 += slope1;
+ x3 += slope3;
+ pixelY++;
+ }
+
+ while (height2-- > 0)
+ {
+ simulateHorizontalLineRasterizationForOutline(pixelY, x2 >> 14, x3 >> 14);
+ x3 += slope3;
+ x2 += slope2;
+ pixelY++;
+ }
+ }
+ }
+
+ /**
+ * Translates the vertices 3D points to the screen canvas 2D points.
+ *
+ * @param localX The local x position of the vertices.
+ * @param localY The local y position of the vertices.
+ * @param localZ The local z position of the vertices.
+ * @param vertexOrientation The orientation of the vertices.
+ * @return Returns true if any of them are inside the clip area, otherwise false.
+ */
+ private boolean projectVertices(Model model, int localX, int localY, int localZ, final int vertexOrientation)
+ {
+ final int vertexCount = model.getVerticesCount();
+ Perspective.modelToCanvas(client,
+ vertexCount,
+ localX, localY, localZ,
+ vertexOrientation,
+ model.getVerticesX(), model.getVerticesZ(), model.getVerticesY(),
+ projectedVerticesX, projectedVerticesY);
+
+ boolean anyVisible = false;
+
+ for (int i = 0; i < vertexCount; i++)
+ {
+ int x = projectedVerticesX[i];
+ int y = projectedVerticesY[i];
+
+ if (y != Integer.MIN_VALUE)
+ {
+ boolean visibleX = x >= clipX1 && x < clipX2;
+ boolean visibleY = y >= clipY1 && y < clipY2;
+ anyVisible |= visibleX && visibleY;
+
+ croppedX1 = Math.min(croppedX1, x);
+ croppedX2 = Math.max(croppedX2, x + 1);
+ croppedY1 = Math.min(croppedY1, y);
+ croppedY2 = Math.max(croppedY2, y + 1);
+ }
+ else
+ {
+ // Vertex is too close or behind camera and isn't rendered
+ projectedVerticesY[i] = Integer.MIN_VALUE;
+ }
+ }
+
+ return anyVisible;
+ }
+
+ /**
+ * Simulates rendering of the model and marks every pixel visited.
+ */
+ private void simulateModelRasterizationForOutline(Model model)
+ {
+ final int triangleCount = model.getTrianglesCount();
+ final int[] indices1 = model.getTrianglesX();
+ final int[] indices2 = model.getTrianglesY();
+ final int[] indices3 = model.getTrianglesZ();
+ final byte[] triangleTransparencies = model.getTriangleTransparencies();
+
+ for (int i = 0; i < triangleCount; i++)
+ {
+ if (projectedVerticesY[indices1[i]] != Integer.MIN_VALUE &&
+ projectedVerticesY[indices2[i]] != Integer.MIN_VALUE &&
+ projectedVerticesY[indices3[i]] != Integer.MIN_VALUE &&
+ // 254 and 255 counts as fully transparent
+ (triangleTransparencies == null || (triangleTransparencies[i] & 255) < 254))
+ {
+ final int index1 = indices1[i];
+ final int index2 = indices2[i];
+ final int index3 = indices3[i];
+ final int v1x = projectedVerticesX[index1];
+ final int v1y = projectedVerticesY[index1];
+ final int v2x = projectedVerticesX[index2];
+ final int v2y = projectedVerticesY[index2];
+ final int v3x = projectedVerticesX[index3];
+ final int v3y = projectedVerticesY[index3];
+
+ if (!cullFace(v1x, v1y, v2x, v2y, v3x, v3y))
+ {
+ simulateTriangleRasterizationForOutline(v1x, v1y, v2x, v2y, v3x, v3y);
+ }
+ }
+ }
+ }
+
+ /**
+ * Draws the outline of a pixel according to the distance deltas of an outline.
+ *
+ * @param imageData The image data to draw to.
+ * @param imageWidth The width of the image to draw to.
+ * @param x The x position of the pixel.
+ * @param y The y position of the pixel.
+ * @param distanceDeltas The distance deltas of the outline width.
+ * @param color The color to draw the outline in.
+ */
+ private void rasterDistanceDeltas(int[] imageData, int imageWidth, int x, int y,
+ PixelDistanceDelta[] distanceDeltas, int color)
+ {
+ for (PixelDistanceDelta delta : distanceDeltas)
+ {
+ int cx = x + delta.dx;
+ int cy = y + delta.dy;
+ int visitedPixelPos = (cy - croppedY1) * croppedWidth + (cx - croppedX1);
+ if (cx >= clipX1 && cx < clipX2 && cy >= clipY1 && cy < clipY2 &&
+ (visited[visitedPixelPos >> 5] & (1 << (visitedPixelPos & 31))) == 0)
+ {
+ imageData[cy * imageWidth + cx] = color;
+ }
+ }
+ }
+
+ /**
+ * Enqueues pixels that are adjacent above or below the model
+ * or draws them directly to the clients image buffer.
+ *
+ * @param directWrite If true the pixels are drawn to the image buffer, otherwise they are enqueued for drawing.
+ * @param color The color to draw if directWrite == true
+ * @param outlineWidth The outline width to draw if directWrite == true
+ */
+ private void processInitialOutlinePixels(boolean directWrite, Color color, int outlineWidth)
+ {
+ MainBufferProvider bufferProvider = (MainBufferProvider) client.getBufferProvider();
+ BufferedImage image = (BufferedImage) bufferProvider.getImage();
+ int imageWidth = image.getWidth();
+ int[] imageData = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
+ int colorRGB = color.getRGB();
+
+ // Up and down
+ for (int x = 0; x < croppedWidth; x += 32)
+ {
+ int v1 = visited[x >> 5];
+ for (int y = 1; y < croppedHeight; y++)
+ {
+ int v2 = visited[(y * croppedWidth + x) >> 5];
+ if (v1 != v2)
+ {
+ if (directWrite)
+ {
+ // Special case handling outlineWidth == 1 gives a
+ // small but noticeable performance improvement.
+ if (outlineWidth == 1)
+ {
+ for (int bit = 0; bit < 32; bit++)
+ {
+ int bv1 = (v1 >>> bit) & 1;
+ int bv2 = (v2 >>> bit) & 1;
+ if (bv1 != bv2)
+ {
+ imageData[(croppedY1 + y - bv2) * imageWidth + (croppedX1 + x + bit)] = colorRGB;
+ }
+ }
+ }
+ else
+ {
+ PixelDistanceDelta[] distancesDown = precomputedDistanceDeltas[outlineWidth][3];
+ PixelDistanceDelta[] distancesUp = precomputedDistanceDeltas[outlineWidth][1];
+ for (int bit = 0; bit < 32; bit++)
+ {
+ int bv1 = (v1 >>> bit) & 1;
+ int bv2 = (v2 >>> bit) & 1;
+ if (bv1 == 1 && bv2 == 0)
+ {
+ rasterDistanceDeltas(imageData, imageWidth, croppedX1 + x + bit, croppedY1 + y - 1,
+ distancesDown, colorRGB);
+ }
+ else if (bv1 == 0 && bv2 == 1)
+ {
+ rasterDistanceDeltas(imageData, imageWidth, croppedX1 + x + bit, croppedY1 + y,
+ distancesUp, colorRGB);
+ }
+ }
+ }
+ }
+ else
+ {
+ for (int bit = 0; bit < 32; bit++)
+ {
+ int bv1 = (v1 >>> bit) & 1;
+ int bv2 = (v2 >>> bit) & 1;
+ if (bv1 != bv2)
+ {
+ enqueueOutlinePixel(outlineArrayWidth, croppedX1 + x + bit, croppedY1 + y - bv2);
+ }
+ }
+ }
+ }
+
+ v1 = v2;
+ }
+ }
+
+ // Left and right
+ for (int y = 0; y < croppedHeight; y++)
+ {
+ int rowPosition = y * croppedWidth;
+ int lastV = 0;
+ for (int x = 0; x < croppedWidth; x += 32)
+ {
+ int v = visited[(rowPosition + x) >> 5];
+
+ // Test adjacent pixels in the same 32-bit segment
+ if (v != 0 && v != 0xFFFFFFFF)
+ {
+ int end = Math.min(32, clipX2 - croppedX1 - x);
+ int lastBv = v & 1;
+ if (directWrite)
+ {
+ // Special case handling outlineWidth == 1 gives a
+ // small but noticeable performance improvement.
+ if (outlineWidth == 1)
+ {
+ for (int bit = 1; bit < end; bit++)
+ {
+ int bv = (v >>> bit) & 1;
+ if (bv != lastBv)
+ {
+ imageData[(croppedY1 + y) * imageWidth + (croppedX1 + x + bit - bv)] = colorRGB;
+ }
+ lastBv = bv;
+ }
+ }
+ else
+ {
+ PixelDistanceDelta[] distancesRight = precomputedDistanceDeltas[outlineWidth][0];
+ PixelDistanceDelta[] distancesLeft = precomputedDistanceDeltas[outlineWidth][2];
+ for (int bit = 1; bit < end; bit++)
+ {
+ int bv = (v >>> bit) & 1;
+ if (bv == 1 && lastBv == 0)
+ {
+ rasterDistanceDeltas(imageData, imageWidth, croppedX1 + x + bit, croppedY1 + y,
+ distancesLeft, colorRGB);
+ }
+ else if (bv == 0 && lastBv == 1)
+ {
+ rasterDistanceDeltas(imageData, imageWidth, croppedX1 + x + bit - 1, croppedY1 + y,
+ distancesRight, colorRGB);
+ }
+ lastBv = bv;
+ }
+ }
+ }
+ else
+ {
+ for (int bit = 1; bit < end; bit++)
+ {
+ int bv = (v >>> bit) & 1;
+ if (bv != lastBv)
+ {
+ enqueueOutlinePixel(1, croppedX1 + x + bit - bv, croppedY1 + y);
+ }
+ lastBv = bv;
+ }
+ }
+ }
+
+ // Test adjacent pixels in different 32-bit segments
+ if ((lastV >>> 31) != (v & 1) && x > 0)
+ {
+ if (directWrite)
+ {
+ if (outlineWidth == 1)
+ {
+ imageData[(croppedY1 + y) * imageWidth + (croppedX1 + x - (v & 1))] = colorRGB;
+ }
+ else
+ {
+ if ((v & 1) == 1)
+ {
+ PixelDistanceDelta[] distancesLeft = precomputedDistanceDeltas[outlineWidth][2];
+ rasterDistanceDeltas(imageData, imageWidth, croppedX1 + x, croppedY1 + y,
+ distancesLeft, colorRGB);
+ }
+ else
+ {
+ PixelDistanceDelta[] distancesRight = precomputedDistanceDeltas[outlineWidth][0];
+ rasterDistanceDeltas(imageData, imageWidth, croppedX1 + x - 1, croppedY1 + y,
+ distancesRight, colorRGB);
+ }
+ }
+ }
+ else
+ {
+ enqueueOutlinePixel(1, croppedX1 + x - (v & 1), croppedY1 + y);
+ }
+ }
+
+ lastV = v;
+ }
+ }
+ }
+
+ /**
+ * Process the outline queue and draw an outline of the pixels
+ * in the queue to the client image buffer.
+ *
+ * @param outlineWidth The width of the outline.
+ * @param color The color of the outline.
+ */
+ private void processOutlinePixelQueue(int outlineWidth, Color color, int feather)
+ {
+ MainBufferProvider bufferProvider = (MainBufferProvider) client.getBufferProvider();
+ BufferedImage image = (BufferedImage) bufferProvider.getImage();
+ int imageWidth = image.getWidth();
+ int[] imageData = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
+ PixelDistanceGroupIndex[] ps = getPriorityList(outlineWidth, feather);
+
+ for (PixelDistanceGroupIndex p : ps)
+ {
+ final int[] blockMemory = outlinePixelsBlockBuffer.getMemory();
+
+ final int colorARGB;
+ final int inverseAlpha;
+ {
+ int alpha = (int) Math.round(color.getAlpha() * p.alphaMultiply);
+ inverseAlpha = 256 - alpha;
+ colorARGB = (alpha << 24)
+ | ((color.getRed() * alpha) / 255) << 16
+ | ((color.getGreen() * alpha) / 255) << 8
+ | ((color.getBlue() * alpha) / 255);
+ }
+
+ final int groupIndex = p.distanceGroupIndex;
+ final int nextGroupIndexY = groupIndex + outlineArrayWidth;
+ final int nextGroupIndexX = groupIndex + 1;
+
+ while (outlinePixelsBlockIndicesLengths[groupIndex] > 0)
+ {
+ final int block = outlinePixelsBlockIndices[groupIndex][outlinePixelsBlockIndicesLengths[groupIndex] - 1];
+ final int blockStart = block << IntBlockBuffer.BLOCK_BITS;
+ final int blockEnd = blockStart + outlinePixelsLastBlockLength[groupIndex];
+ for (int i = blockStart; i < blockEnd; i++)
+ {
+ int x = blockMemory[i] & 0xFFFF;
+ int y = blockMemory[i] >>> 16;
+ int visitedPixelPos = (y - croppedY1) * croppedWidth + (x - croppedX1);
+ if ((visited[visitedPixelPos >> 5] & (1 << (visitedPixelPos & 31))) != 0)
+ {
+ continue;
+ }
+ visited[visitedPixelPos >> 5] |= 1 << (visitedPixelPos & 31);
+
+ int pixelPos = y * imageWidth + x;
+ int dst = imageData[pixelPos];
+ imageData[pixelPos]
+ = (colorARGB & 0xFF00FF00) + (((dst & 0xFF00FF00) * inverseAlpha) >>> 8) & 0xFF00FF00
+ | (colorARGB & 0x00FF00FF) + (((dst & 0x00FF00FF) * inverseAlpha) >>> 8) & 0x00FF00FF;
+
+ if (x - 1 >= clipX1)
+ {
+ enqueueOutlinePixel(nextGroupIndexX, x - 1, y);
+ }
+ if (x + 1 < clipX2)
+ {
+ enqueueOutlinePixel(nextGroupIndexX, x + 1, y);
+ }
+ if (y - 1 >= clipY1)
+ {
+ enqueueOutlinePixel(nextGroupIndexY, x, y - 1);
+ }
+ if (y + 1 < clipY2)
+ {
+ enqueueOutlinePixel(nextGroupIndexY, x, y + 1);
+ }
+ }
+
+ outlinePixelsBlockBuffer.freeBlock(block);
+ outlinePixelsBlockIndicesLengths[groupIndex]--;
+ outlinePixelsLastBlockLength[groupIndex] = IntBlockBuffer.BLOCK_SIZE;
+ }
+ }
+ }
+
+ /**
+ * Draws an outline around a model to an image
+ *
+ * @param localX The local x position of the model
+ * @param localY The local y position of the model
+ * @param localZ The local z position of the model
+ * @param orientation The orientation of the model
+ * @param outlineWidth The width of the outline
+ * @param color The color of the outline
+ */
+ private void drawModelOutline(Model model,
+ int localX, int localY, int localZ, int orientation,
+ int outlineWidth, Color color, int feather)
+ {
+ if (outlineWidth <= 0 || color.getAlpha() == 0 || model == null)
+ {
+ return;
+ }
+
+ if (outlineWidth > MAX_OUTLINE_WIDTH)
+ {
+ outlineWidth = MAX_OUTLINE_WIDTH;
+ }
+
+ if (feather < 0)
+ {
+ feather = 0;
+ }
+ else if (feather > MAX_FEATHER)
+ {
+ feather = MAX_FEATHER;
+ }
+
+ croppedX1 = Integer.MAX_VALUE;
+ croppedX2 = Integer.MIN_VALUE;
+ croppedY1 = Integer.MAX_VALUE;
+ croppedY2 = Integer.MIN_VALUE;
+
+ clipX1 = client.getViewportXOffset();
+ clipY1 = client.getViewportYOffset();
+ clipX2 = client.getViewportWidth() + clipX1;
+ clipY2 = client.getViewportHeight() + clipY1;
+
+ if (!projectVertices(model, localX, localY, localZ, orientation))
+ {
+ // No vertex of the model is visible on the screen, so we can
+ // assume there are no parts of the model to outline.
+ return;
+ }
+
+ croppedX1 = Math.max(croppedX1 - outlineWidth, clipX1);
+ croppedX2 = Math.min(croppedX2 + outlineWidth, clipX2);
+ croppedX2 += ~(croppedX2 - croppedX1 - 1) & 31; // Increases width to next multiple of 32 so bitset segments align
+ croppedY1 = Math.max(croppedY1 - outlineWidth, clipY1);
+ croppedY2 = Math.min(croppedY2 + outlineWidth, clipY2);
+ croppedWidth = croppedX2 - croppedX1;
+ croppedHeight = croppedY2 - croppedY1;
+
+ resetVisited(croppedWidth * croppedHeight);
+
+ simulateModelRasterizationForOutline(model);
+
+ // We can improve performance and reduce memory needed when drawing
+ // only a small outline around the model by skipping the pixel queueing
+ // and instead raster pixels directly. This only looks right for opaque
+ // outlines since some pixels of the outline can get drawn more than once.
+ // Performance becomes worse than queueing when using larger outline widths,
+ // usually around 10 px outline width according to some basic testing.
+ boolean directWrite = color.getAlpha() == 255 && outlineWidth <= DIRECT_WRITE_OUTLINE_WIDTH_THRESHOLD &&
+ (feather == 0 || outlineWidth == 1); // Feather has no effect on outlineWidth == 1
+
+ if (directWrite)
+ {
+ ensureDistanceDeltasCreated(outlineWidth);
+ }
+ else
+ {
+ outlineArrayWidth = outlineWidth + 2;
+ initializeOutlineBuffers();
+ }
+
+ try
+ {
+ processInitialOutlinePixels(directWrite, color, outlineWidth);
+
+ if (!directWrite)
+ {
+ processOutlinePixelQueue(outlineWidth, color, feather);
+ }
+ }
+ finally
+ {
+ freeAllBlockMemory();
+ }
+ }
+
+ public void drawOutline(NPC npc, int outlineWidth, Color color, int feather)
+ {
+ int size = 1;
+ NPCComposition composition = npc.getTransformedComposition();
+ if (composition != null)
+ {
+ size = composition.getSize();
+ }
+
+ LocalPoint lp = npc.getLocalLocation();
+ if (lp != null)
+ {
+ // NPCs z position are calculated based on the tile height of the northeastern tile
+ final int northEastX = lp.getX() + Perspective.LOCAL_TILE_SIZE * (size - 1) / 2;
+ final int northEastY = lp.getY() + Perspective.LOCAL_TILE_SIZE * (size - 1) / 2;
+ final LocalPoint northEastLp = new LocalPoint(northEastX, northEastY);
+
+ drawModelOutline(npc.getModel(), lp.getX(), lp.getY(),
+ Perspective.getTileHeight(client, northEastLp, client.getPlane()),
+ npc.getCurrentOrientation(), outlineWidth, color, feather);
+ }
+ }
+
+ public void drawOutline(Player player, int outlineWidth, Color color, int feather)
+ {
+ LocalPoint lp = player.getLocalLocation();
+ if (lp != null)
+ {
+ drawModelOutline(player.getModel(), lp.getX(), lp.getY(),
+ Perspective.getTileHeight(client, lp, client.getPlane()),
+ player.getCurrentOrientation(), outlineWidth, color, feather);
+ }
+ }
+
+ private void drawOutline(GameObject gameObject, int outlineWidth, Color color, int feather)
+ {
+ LocalPoint lp = gameObject.getLocalLocation();
+ Renderable renderable = gameObject.getRenderable();
+ if (renderable != null)
+ {
+ Model model = renderable instanceof Model ? (Model) renderable : renderable.getModel();
+ if (model != null)
+ {
+ drawModelOutline(model, lp.getX(), lp.getY(),
+ Perspective.getTileHeight(client, lp, gameObject.getPlane()),
+ gameObject.getModelOrientation(), outlineWidth, color, feather);
+ }
+ }
+ }
+
+ private void drawOutline(GroundObject groundObject, int outlineWidth, Color color, int feather)
+ {
+ LocalPoint lp = groundObject.getLocalLocation();
+ Renderable renderable = groundObject.getRenderable();
+ if (renderable != null)
+ {
+ Model model = renderable instanceof Model ? (Model) renderable : renderable.getModel();
+ if (model != null)
+ {
+ drawModelOutline(model, lp.getX(), lp.getY(),
+ Perspective.getTileHeight(client, lp, client.getPlane()),
+ 0, outlineWidth, color, feather);
+ }
+ }
+ }
+
+ private void drawOutline(ItemLayer itemLayer, int outlineWidth, Color color, int feather)
+ {
+ LocalPoint lp = itemLayer.getLocalLocation();
+
+ Renderable bottomRenderable = itemLayer.getBottom();
+ if (bottomRenderable != null)
+ {
+ Model model = bottomRenderable instanceof Model ? (Model) bottomRenderable : bottomRenderable.getModel();
+ if (model != null)
+ {
+ drawModelOutline(model, lp.getX(), lp.getY(),
+ Perspective.getTileHeight(client, lp, itemLayer.getPlane()) - itemLayer.getHeight(),
+ 0, outlineWidth, color, feather);
+ }
+ }
+
+ Renderable middleRenderable = itemLayer.getMiddle();
+ if (middleRenderable != null)
+ {
+ Model model = middleRenderable instanceof Model ? (Model) middleRenderable : middleRenderable.getModel();
+ if (model != null)
+ {
+ drawModelOutline(model, lp.getX(), lp.getY(),
+ Perspective.getTileHeight(client, lp, itemLayer.getPlane()) - itemLayer.getHeight(),
+ 0, outlineWidth, color, feather);
+ }
+ }
+
+ Renderable topRenderable = itemLayer.getTop();
+ if (topRenderable != null)
+ {
+ Model model = topRenderable instanceof Model ? (Model) topRenderable : topRenderable.getModel();
+ if (model != null)
+ {
+ drawModelOutline(model, lp.getX(), lp.getY(),
+ Perspective.getTileHeight(client, lp, itemLayer.getPlane()) - itemLayer.getHeight(),
+ 0, outlineWidth, color, feather);
+ }
+ }
+ }
+
+ private void drawOutline(DecorativeObject decorativeObject, int outlineWidth, Color color, int feather)
+ {
+ LocalPoint lp = decorativeObject.getLocalLocation();
+
+ Renderable renderable1 = decorativeObject.getRenderable();
+ if (renderable1 != null)
+ {
+ Model model = renderable1 instanceof Model ? (Model) renderable1 : renderable1.getModel();
+ if (model != null)
+ {
+ drawModelOutline(model,
+ lp.getX() + decorativeObject.getXOffset(),
+ lp.getY() + decorativeObject.getYOffset(),
+ Perspective.getTileHeight(client, lp, decorativeObject.getPlane()),
+ 0, outlineWidth, color, feather);
+ }
+ }
+
+ Renderable renderable2 = decorativeObject.getRenderable2();
+ if (renderable2 != null)
+ {
+ Model model = renderable2 instanceof Model ? (Model) renderable2 : renderable2.getModel();
+ if (model != null)
+ {
+ // Offset is not used for the second model
+ drawModelOutline(model, lp.getX(), lp.getY(),
+ Perspective.getTileHeight(client, lp, decorativeObject.getPlane()),
+ 0, outlineWidth, color, feather);
+ }
+ }
+ }
+
+ private void drawOutline(WallObject wallObject, int outlineWidth, Color color, int feather)
+ {
+ LocalPoint lp = wallObject.getLocalLocation();
+
+ Renderable renderable1 = wallObject.getRenderable1();
+ if (renderable1 != null)
+ {
+ Model model = renderable1 instanceof Model ? (Model) renderable1 : renderable1.getModel();
+ if (model != null)
+ {
+ drawModelOutline(model, lp.getX(), lp.getY(),
+ Perspective.getTileHeight(client, lp, wallObject.getPlane()),
+ wallObject.getOrientationA(), outlineWidth, color, feather);
+ }
+ }
+
+ Renderable renderable2 = wallObject.getRenderable2();
+ if (renderable2 != null)
+ {
+ Model model = renderable2 instanceof Model ? (Model) renderable2 : renderable2.getModel();
+ if (model != null)
+ {
+ drawModelOutline(model, lp.getX(), lp.getY(),
+ Perspective.getTileHeight(client, lp, wallObject.getPlane()),
+ wallObject.getOrientationB(), outlineWidth, color, feather);
+ }
+ }
+ }
+
+ public void drawOutline(TileObject tileObject, int outlineWidth, Color color, int feather)
+ {
+ if (tileObject instanceof GameObject)
+ {
+ drawOutline((GameObject) tileObject, outlineWidth, color, feather);
+ }
+ else if (tileObject instanceof GroundObject)
+ {
+ drawOutline((GroundObject) tileObject, outlineWidth, color, feather);
+ }
+ else if (tileObject instanceof ItemLayer)
+ {
+ drawOutline((ItemLayer) tileObject, outlineWidth, color, feather);
+ }
+ else if (tileObject instanceof DecorativeObject)
+ {
+ drawOutline((DecorativeObject) tileObject, outlineWidth, color, feather);
+ }
+ else if (tileObject instanceof WallObject)
+ {
+ drawOutline((WallObject) tileObject, outlineWidth, color, feather);
+ }
+ }
+
+ public void drawOutline(GraphicsObject graphicsObject, int outlineWidth, Color color, int feather)
+ {
+ LocalPoint lp = graphicsObject.getLocation();
+ if (lp != null)
+ {
+ Model model = graphicsObject.getModel();
+ if (model != null)
+ {
+ drawModelOutline(model, lp.getX(), lp.getY(), graphicsObject.getHeight(),
+ 0, outlineWidth, color, feather);
+ }
+ }
+ }
+}
diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/timers/TimersPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/timers/TimersPluginTest.java
index 88fb40823d..58b17fd2ca 100644
--- a/runelite-client/src/test/java/net/runelite/client/plugins/timers/TimersPluginTest.java
+++ b/runelite-client/src/test/java/net/runelite/client/plugins/timers/TimersPluginTest.java
@@ -503,7 +503,7 @@ public class TimersPluginTest
public void testThrall()
{
when(timersConfig.showArceuus()).thenReturn(true);
- when(client.getRealSkillLevel(Skill.MAGIC)).thenReturn(50);
+ when(client.getBoostedSkillLevel(Skill.MAGIC)).thenReturn(60);
ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", "You resurrect a greater zombified thrall.", "", 0);
timersPlugin.onChatMessage(chatMessage);
@@ -512,6 +512,7 @@ public class TimersPluginTest
verify(infoBoxManager).addInfoBox(ibcaptor.capture());
TimerTimer infoBox = (TimerTimer) ibcaptor.getValue();
assertEquals(GameTimer.RESURRECT_THRALL, infoBox.getTimer());
+ assertEquals(Duration.of(60, RSTimeUnit.GAME_TICKS), infoBox.getDuration());
}
@Test
diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java
index 6f2f7061d3..6a26df384e 100644
--- a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java
+++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java
@@ -1435,6 +1435,7 @@ public abstract class RSClientMixin implements RSClient
menuOptionClicked.setMenuAction(MenuAction.of(opcode));
menuOptionClicked.setId(id);
menuOptionClicked.setWidgetId(param1);
+ menuOptionClicked.setSelectedItemIndex(client.getSelectedItemSlot());
client.getCallbacks().post(menuOptionClicked);
diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSGameObjectMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSGameObjectMixin.java
index 3da149cf72..4e831b6b0b 100644
--- a/runelite-mixins/src/main/java/net/runelite/mixins/RSGameObjectMixin.java
+++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSGameObjectMixin.java
@@ -75,7 +75,6 @@ public abstract class RSGameObjectMixin implements RSGameObject
}
@Inject
- @Override
public RSModel getModel()
{
RSRenderable renderable = getRenderable();
diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSWallDecorationMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSWallDecorationMixin.java
index 47e64a6932..c71d3a8d01 100644
--- a/runelite-mixins/src/main/java/net/runelite/mixins/RSWallDecorationMixin.java
+++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSWallDecorationMixin.java
@@ -37,7 +37,6 @@ public abstract class RSWallDecorationMixin implements RSWallDecoration
}
@Inject
- @Override
public RSModel getModel1()
{
RSRenderable renderable = getRenderable();
@@ -61,7 +60,6 @@ public abstract class RSWallDecorationMixin implements RSWallDecoration
}
@Inject
- @Override
public RSModel getModel2()
{
RSRenderable renderable = getRenderable2();
diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSGameObject.java b/runescape-api/src/main/java/net/runelite/rs/api/RSGameObject.java
index db6849bd99..e326a89938 100644
--- a/runescape-api/src/main/java/net/runelite/rs/api/RSGameObject.java
+++ b/runescape-api/src/main/java/net/runelite/rs/api/RSGameObject.java
@@ -32,7 +32,6 @@ public interface RSGameObject extends GameObject
int getHeight();
@Import("orientation")
- @Override
int getRsOrientation();
@Import("tag")
@@ -40,7 +39,6 @@ public interface RSGameObject extends GameObject
long getHash();
@Import("flags")
- @Override
int getFlags();
int getPlane();