diff --git a/runelite-api/src/main/java/net/runelite/api/Client.java b/runelite-api/src/main/java/net/runelite/api/Client.java index 4da908f480..a90a8738b1 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -39,6 +39,8 @@ public interface Client extends GameEngine List getNpcs(); + NPC[] getCachedNPCs(); + int getBoostedSkillLevel(Skill skill); int getRealSkillLevel(Skill skill); diff --git a/runelite-api/src/main/java/net/runelite/api/NPC.java b/runelite-api/src/main/java/net/runelite/api/NPC.java index 9fcf4ff919..10497c7a4e 100644 --- a/runelite-api/src/main/java/net/runelite/api/NPC.java +++ b/runelite-api/src/main/java/net/runelite/api/NPC.java @@ -34,4 +34,5 @@ public interface NPC extends Actor @Override int getCombatLevel(); + int getIndex(); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/opponentinfo/OpponentInfoOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/opponentinfo/OpponentInfoOverlay.java index 51b2c5f163..0156805cb2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/opponentinfo/OpponentInfoOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/opponentinfo/OpponentInfoOverlay.java @@ -37,6 +37,7 @@ import java.util.Map; import javax.inject.Inject; import net.runelite.api.Actor; import net.runelite.api.Client; +import net.runelite.api.NPC; import net.runelite.api.Player; import net.runelite.api.Varbits; import net.runelite.client.ui.overlay.Overlay; @@ -60,6 +61,8 @@ class OpponentInfoOverlay extends Overlay private static final Duration WAIT = Duration.ofSeconds(3); private final Client client; + private final NPC[] clientNpcs; + private Integer lastMaxHealth; private DecimalFormat df = new DecimalFormat("0.0"); private float lastRatio = 0; @@ -67,6 +70,7 @@ class OpponentInfoOverlay extends Overlay private String opponentName; private String opponentsOpponentName; private Map oppInfoHealth = OpponentInfoPlugin.loadNpcHealth(); + private NPC lastOpponent; @Inject OpponentInfoOverlay(Client client) @@ -74,6 +78,7 @@ class OpponentInfoOverlay extends Overlay setPosition(OverlayPosition.TOP_LEFT); setPriority(OverlayPriority.HIGH); this.client = client; + this.clientNpcs = client.getCachedNPCs(); } private Actor getOpponent() @@ -92,6 +97,25 @@ class OpponentInfoOverlay extends Overlay { Actor opponent = getOpponent(); + // If opponent is null, try to use last opponent + if (opponent == null) + { + if (lastOpponent != null && clientNpcs[lastOpponent.getIndex()] != lastOpponent) + { + // lastOpponent is no longer valid + lastOpponent = null; + } + else + { + opponent = lastOpponent; + } + } + else + { + // Update last opponent + lastOpponent = opponent instanceof NPC ? (NPC) opponent : null; + } + if (opponent != null && opponent.getHealth() > 0) { lastTime = Instant.now(); 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 94cd998160..70eadca682 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java @@ -65,6 +65,7 @@ import net.runelite.rs.api.RSDeque; import net.runelite.rs.api.RSHashTable; import net.runelite.rs.api.RSIndexedSprite; import net.runelite.rs.api.RSItemContainer; +import net.runelite.rs.api.RSNPC; import net.runelite.rs.api.RSName; import net.runelite.rs.api.RSWidget; @@ -494,6 +495,24 @@ public abstract class RSClientMixin implements RSClient eventBus.post(gameStateChange); } + + @FieldHook("cachedNPCs") + @Inject + public static void cachedNPCsChanged(int idx) + { + RSNPC[] cachedNPCs = client.getCachedNPCs(); + if (idx < 0 || idx >= cachedNPCs.length) + { + return; + } + + RSNPC npc = cachedNPCs[idx]; + if (npc != null) + { + npc.setIndex(idx); + } + } + @Inject @FieldHook("grandExchangeOffers") public static void onGrandExchangeOffersChanged(int idx) diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSNPCMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSNPCMixin.java index 0b08a1cc7d..d052b8c6e2 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSNPCMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSNPCMixin.java @@ -32,6 +32,9 @@ import net.runelite.rs.api.RSNPCComposition; @Mixin(RSNPC.class) public abstract class RSNPCMixin implements RSNPC { + @Inject + private int npcIndex; + @Inject @Override public int getId() @@ -55,4 +58,18 @@ public abstract class RSNPCMixin implements RSNPC RSNPCComposition composition = getComposition(); return composition == null ? -1 : composition.getCombatLevel(); } + + @Inject + @Override + public int getIndex() + { + return npcIndex; + } + + @Inject + @Override + public void setIndex(int id) + { + npcIndex = id; + } } diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java b/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java index 5a099109ea..2fa2e4940f 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java @@ -129,6 +129,7 @@ public interface RSClient extends RSGameEngine, Client int[] getNpcIndices(); @Import("cachedNPCs") + @Override RSNPC[] getCachedNPCs(); @Import("collisionMaps") diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSNPC.java b/runescape-api/src/main/java/net/runelite/rs/api/RSNPC.java index 9419850d27..a401cf3cf5 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSNPC.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSNPC.java @@ -31,4 +31,9 @@ public interface RSNPC extends RSActor, NPC { @Import("composition") RSNPCComposition getComposition(); + + @Override + int getIndex(); + + void setIndex(int id); }