diff --git a/src/main/java/io/blert/core/TrackedNpc.java b/src/main/java/io/blert/core/TrackedNpc.java index 75d4db7..7b16193 100644 --- a/src/main/java/io/blert/core/TrackedNpc.java +++ b/src/main/java/io/blert/core/TrackedNpc.java @@ -109,7 +109,6 @@ public PrayerSet getActivePrayers() { PrayerSet prayerSet = new PrayerSet(Prayer.PRAYER_BOOK_NORMAL); // Only the first ID is used to store overhead prayers. // Ignore anything that isn't a HeadIcon. - // TODO(frolv): Support other overhead sprites. if (ids != null && ids.length > 0 && ids[0] >= 0 && ids[0] < HeadIcon.values().length) { HeadIcon icon = HeadIcon.values()[ids[0]]; switch (icon) { diff --git a/src/main/java/io/blert/events/NpcEvent.java b/src/main/java/io/blert/events/NpcEvent.java index fbb0d0b..d1ea3e1 100644 --- a/src/main/java/io/blert/events/NpcEvent.java +++ b/src/main/java/io/blert/events/NpcEvent.java @@ -45,7 +45,7 @@ public class NpcEvent extends Event { private final Hitpoints hitpoints; @Getter - private final long prayers; + private final @Nullable Long prayers; @Getter private final TrackedNpc.Properties properties; @@ -64,6 +64,12 @@ public class NpcEvent extends Event { @Getter private final @Nullable List spotAnims; + /** + * The NPC's overhead icons, one per slot in order, packed as {@code archive << 16 | sprite}. + */ + @Getter + private final @Nullable List overheads; + @Getter public static class Target { private final @Nullable String player; @@ -93,24 +99,7 @@ public static NpcEvent update(Stage stage, int tick, WorldPoint point, TrackedNp public static NpcEvent genericUpdate( Stage stage, int tick, WorldPoint point, GenericTrackedNpc trackedNpc, @Nullable Target target) { - NPC npc = trackedNpc.getNpc(); - int animation = npc.getAnimation(); - - List spotAnims = new ArrayList<>(); - for (var spotAnim : npc.getSpotAnims()) { - spotAnims.add(spotAnim.getId()); - } - - return new NpcEvent( - EventType.NPC_UPDATE, - stage, - tick, - point, - trackedNpc, - npc.getComposition().getSize(), - animation != -1 ? animation : null, - target, - spotAnims.isEmpty() ? null : spotAnims); + return new NpcEvent(stage, tick, point, trackedNpc, target); } public static NpcEvent death(Stage stage, int tick, WorldPoint point, TrackedNpc trackedNpc) { @@ -118,19 +107,6 @@ public static NpcEvent death(Stage stage, int tick, WorldPoint point, TrackedNpc } protected NpcEvent(EventType type, Stage stage, int tick, WorldPoint point, TrackedNpc trackedNpc) { - this(type, stage, tick, point, trackedNpc, null, null, null, null); - } - - private NpcEvent( - EventType type, - Stage stage, - int tick, - WorldPoint point, - TrackedNpc trackedNpc, - @Nullable Integer size, - @Nullable Integer animation, - @Nullable Target target, - @Nullable List spotAnims) { super(type, stage, tick, point); this.roomId = trackedNpc.getRoomId(); // Omit an unknown NPC ID. The server should carry forward the last known one for this room ID. @@ -139,10 +115,46 @@ private NpcEvent( this.prayers = trackedNpc.getActivePrayers().getValue(); this.properties = trackedNpc.getProperties(); this.propertiesChanged = trackedNpc.hasUpdatedProperties(); - this.size = size; - this.animation = animation; + this.size = null; + this.animation = null; + this.target = null; + this.spotAnims = null; + this.overheads = null; + } + + private NpcEvent(Stage stage, int tick, WorldPoint point, GenericTrackedNpc trackedNpc, @Nullable Target target) { + super(EventType.NPC_UPDATE, stage, tick, point); + this.roomId = trackedNpc.getRoomId(); + // Omit an unknown NPC ID. The server should carry forward the last known one for this room ID. + this.npcId = Math.max(trackedNpc.getNpcId(), 0); + this.hitpoints = trackedNpc.getHitpoints(); + this.prayers = null; + this.properties = trackedNpc.getProperties(); + this.propertiesChanged = trackedNpc.hasUpdatedProperties(); this.target = target; - this.spotAnims = spotAnims; + + NPC npc = trackedNpc.getNpc(); + this.size = npc.getComposition().getSize(); + int animation = npc.getAnimation(); + this.animation = animation != -1 ? animation : null; + + List spotAnims = new ArrayList<>(); + for (var spotAnim : npc.getSpotAnims()) { + spotAnims.add(spotAnim.getId()); + } + this.spotAnims = spotAnims.isEmpty() ? null : spotAnims; + + List overheads = new ArrayList<>(); + int[] archiveIds = npc.getOverheadArchiveIds(); + short[] spriteIds = npc.getOverheadSpriteIds(); + if (archiveIds != null && spriteIds != null) { + for (int i = 0; i < Math.min(archiveIds.length, spriteIds.length); i++) { + if (archiveIds[i] != -1 && spriteIds[i] != -1) { + overheads.add(((long) archiveIds[i] << 16) | (spriteIds[i] & 0xFFFF)); + } + } + } + this.overheads = overheads.isEmpty() ? null : overheads; } public boolean propertiesChanged() { diff --git a/src/main/java/io/blert/json/Event.java b/src/main/java/io/blert/json/Event.java index 2c7eb81..77fd69a 100644 --- a/src/main/java/io/blert/json/Event.java +++ b/src/main/java/io/blert/json/Event.java @@ -113,6 +113,7 @@ public static class Npc { public Integer animation; public Target target; public List spotAnims; + public List overheads; public MaidenCrab maidenCrab; public Nylo nylo; diff --git a/src/main/java/io/blert/json/JsonEventTranslator.java b/src/main/java/io/blert/json/JsonEventTranslator.java index 2be8160..87d5551 100644 --- a/src/main/java/io/blert/json/JsonEventTranslator.java +++ b/src/main/java/io/blert/json/JsonEventTranslator.java @@ -167,6 +167,7 @@ public static Event toJson(io.blert.events.Event event, @Nullable String challen npc.size = npcEvent.getSize(); npc.animation = npcEvent.getAnimation(); npc.spotAnims = npcEvent.getSpotAnims(); + npc.overheads = npcEvent.getOverheads(); NpcEvent.Target target = npcEvent.getTarget(); if (target != null) {