From 5628dea869e50af2bdfbe3ccb95dc91bee66696a Mon Sep 17 00:00:00 2001 From: Levente Kurusa Date: Mon, 28 Apr 2025 11:02:42 +0200 Subject: [PATCH] Implement Lightbearer Support fix typo remove unneeded changes fix bugs --- build.gradle | 2 +- .../java/com/infernostats/GodbookConfig.java | 25 ++++ .../java/com/infernostats/GodbookOverlay.java | 14 +- .../java/com/infernostats/GodbookPlugin.java | 128 +++++++++++++++++- src/main/java/com/infernostats/Preach.java | 25 ++++ .../com/infernostats/RingChangeMessage.java | 15 ++ .../com/infernostats/GodbookPluginTest.java | 6 +- 7 files changed, 202 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/infernostats/Preach.java create mode 100644 src/main/java/com/infernostats/RingChangeMessage.java diff --git a/build.gradle b/build.gradle index 6afc850..7b9bf98 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ repositories { mavenCentral() } -def runeLiteVersion = '1.8.30' +def runeLiteVersion = 'latest.release' dependencies { compileOnly group: 'net.runelite', name:'client', version: runeLiteVersion diff --git a/src/main/java/com/infernostats/GodbookConfig.java b/src/main/java/com/infernostats/GodbookConfig.java index 3565304..d762983 100644 --- a/src/main/java/com/infernostats/GodbookConfig.java +++ b/src/main/java/com/infernostats/GodbookConfig.java @@ -20,6 +20,14 @@ default boolean theatreOnly() @ConfigItem( position = 1, + keyName = "lightbearerSupport", + name = "Lightbearer Support", + description = "Additional info displayed based on ring usage" + ) + default boolean lightbearerSupport() { return false; } + + @ConfigItem( + position = 2, keyName = "ticks", name = "Ticks", description = "How many ticks the counter remains active for" @@ -28,4 +36,21 @@ default int maxTicks() { return 157; } + + @ConfigItem( + position = 4, + hidden = true, + keyName = "hornAnimId", + name = "[WIP] Extra Animation Id", + description = "Extra animation ID to treat as a godbook" + ) + default int hornAnimId() { return 0; } + + @ConfigItem( + position = 3, + keyName = "backwardsCount", + name = "Count Backwards", + description = "Display ticks until full regen, instead of ticks since preach." + ) + default boolean backwardsCount() { return false; } } diff --git a/src/main/java/com/infernostats/GodbookOverlay.java b/src/main/java/com/infernostats/GodbookOverlay.java index 6d9f05d..30f1367 100644 --- a/src/main/java/com/infernostats/GodbookOverlay.java +++ b/src/main/java/com/infernostats/GodbookOverlay.java @@ -29,7 +29,7 @@ private GodbookOverlay(GodbookPlugin plugin) public Dimension render(Graphics2D graphics) { String title = "Tick:"; - HashMap players = plugin.getPlayers(); + HashMap players = plugin.getPlayers(); panelComponent.getChildren().clear(); @@ -44,15 +44,19 @@ public Dimension render(Graphics2D graphics) return panelComponent.render(graphics); } - private int getMaxWidth(Graphics2D graphics, HashMap players, String title) + private int getMaxWidth(Graphics2D graphics, HashMap players, String title) { String longestKey = Collections.max(players.keySet(), Comparator.comparingInt(String::length)); - return graphics.getFontMetrics().stringWidth(longestKey) + graphics.getFontMetrics().stringWidth(title); + return graphics.getFontMetrics().stringWidth(longestKey) + graphics.getFontMetrics().stringWidth(title) + graphics.getFontMetrics().stringWidth("(157)"); } - private void addPlayerToOverlay(String playerName, Integer ticks) + private void addPlayerToOverlay(String playerName, Preach preach) { - panelComponent.getChildren().add(LineComponent.builder().left(playerName).right(Integer.toString(ticks)).build()); + panelComponent.getChildren().add(LineComponent.builder() + .leftColor(preach.preachedWithRing ? Color.YELLOW : Color.WHITE) + .left(playerName) + .right(Integer.toString(preach.tick) + (!preach.preachedWithRing && preach.ringTick != 0 ? " (" + Integer.toString(preach.ringTick) + ")" : "")) + .build()); } } diff --git a/src/main/java/com/infernostats/GodbookPlugin.java b/src/main/java/com/infernostats/GodbookPlugin.java index ac5f511..c6555c9 100644 --- a/src/main/java/com/infernostats/GodbookPlugin.java +++ b/src/main/java/com/infernostats/GodbookPlugin.java @@ -9,13 +9,18 @@ import net.runelite.api.*; import net.runelite.api.events.AnimationChanged; import net.runelite.api.events.GameTick; +import net.runelite.api.events.VarbitChanged; +import net.runelite.api.gameval.VarbitID; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.party.PartyService; +import net.runelite.client.party.WSClient; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.ui.overlay.OverlayManager; -import java.util.LinkedHashMap; +import java.util.*; +import java.util.stream.Collectors; @Slf4j @PluginDescriptor( @@ -24,7 +29,7 @@ public class GodbookPlugin extends Plugin { @Getter(AccessLevel.MODULE) - private LinkedHashMap players; + private LinkedHashMap players; @Inject private Client client; @@ -35,6 +40,12 @@ public class GodbookPlugin extends Plugin @Inject private OverlayManager overlayManager; + @Inject + private PartyService partyService; + + @Inject + private WSClient wsClient; + @Inject private GodbookConfig config; @@ -48,6 +59,7 @@ GodbookConfig provideConfig(ConfigManager configManager) protected void startUp() throws Exception { players = new LinkedHashMap<>(); + wsClient.registerMessage(RingChangeMessage.class); overlayManager.add(overlay); } @@ -55,17 +67,36 @@ protected void startUp() throws Exception protected void shutDown() throws Exception { overlayManager.remove(overlay); + wsClient.unregisterMessage(RingChangeMessage.class); players.clear(); } @Subscribe public void onAnimationChanged(final AnimationChanged event) { + int animId = event.getActor().getAnimation(); if (config.theatreOnly() && !isInTheatreOfBlood()) return; - if (GodbookAnimationID.isGodbookAnimation(event.getActor().getAnimation())) - players.put(event.getActor().getName(), 0); + if (GodbookAnimationID.isGodbookAnimation(animId) || isHornAnimation(animId)) { + boolean isLightbearerPreach = false; + String name = event.getActor().getName(); + + if (event.getActor().getName().equalsIgnoreCase(client.getLocalPlayer().getName())) { + if (config.lightbearerSupport()) { + // This is preaching, check our equipment. + try { + Item item = client.getItemContainer(InventoryID.EQUIPMENT) + .getItem(EquipmentInventorySlot.RING.getSlotIdx()); + isLightbearerPreach = item.getId() == ItemID.LIGHTBEARER; + } catch (NullPointerException _e) { + isLightbearerPreach = false; + } + } + } + + doNewPreach(name, isLightbearerPreach, 0); + } } @Subscribe @@ -74,11 +105,96 @@ public void onGameTick(GameTick event) if (players.isEmpty()) return; + // Check if the localPlayer equipped a Lightbearer + if (config.lightbearerSupport() && players.containsKey(client.getLocalPlayer().getName())) { + Preach localPreach = players.get(client.getLocalPlayer().getName()); + + Item item; + try { + item = client.getItemContainer(InventoryID.EQUIPMENT).getItem(EquipmentInventorySlot.RING.getSlotIdx()); + } catch (NullPointerException _e) { + item = null; + } + + if (item != null && item.getId() == ItemID.LIGHTBEARER) { + + if (localPreach.ringTick == 0) { + localPreach.ringTick = findLowestPreach(); + + if (partyService.isInParty()) { + partyService.send(new RingChangeMessage(client.getLocalPlayer().getName(), 1, localPreach.ringTick)); + } + } + } else { + if (partyService.isInParty() && localPreach.ringTick != 0) { + partyService.send(new RingChangeMessage(client.getLocalPlayer().getName(), 0, 0)); + } + + localPreach.ringTick = 0; + } + } + players.entrySet() - .forEach(i -> i.setValue(i.getValue() + 1)); + .forEach(i -> i.setValue(i.getValue().advance(config.backwardsCount()))); players.entrySet() - .removeIf(i -> i.getValue() >= config.maxTicks()); + .removeIf(this::shouldRemove); + } + + public void doNewPreach(String name, boolean isLightbearerPreach, int offset) { + players.put(name, + new Preach(0, isLightbearerPreach, name, startTick() + offset)); + + // Re-order in descending order so that the lowest preach tick always appears + // at the bottom of the list. + players = players.entrySet() + .stream() + .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) + .collect(Collectors.toMap( + Map.Entry::getKey, + Map.Entry::getValue, + (oldValue, newValue) -> newValue, LinkedHashMap::new)); + } + + @Subscribe + public void onRingChangeMessage(RingChangeMessage e) { + if (!e.n.equalsIgnoreCase(client.getLocalPlayer().getName())) { + players.entrySet().forEach(p -> { + if (p.getKey().equalsIgnoreCase(e.n)) { + Preach remotePreach = p.getValue(); + remotePreach.ringTick = e.r == 1 ? e.t : 0; + p.setValue(remotePreach); + } + }); + } + } + + private int findLowestPreach() { + // Find the lowest preach + final int[] lowestPreach = {Integer.MAX_VALUE}; + players.entrySet().forEach(i -> { + if (lowestPreach[0] > i.getValue().tick) { + lowestPreach[0] = i.getValue().tick; + } + }); + + return lowestPreach[0]; + } + + private boolean shouldRemove(Map.Entry p) { + if (config.backwardsCount()) { + return p.getValue().tick < 0; + } else { + return p.getValue().tick >= config.maxTicks(); + } + } + + private boolean isHornAnimation(int animId) { + return config.hornAnimId() != 0 && animId == config.hornAnimId(); + } + + private int startTick() { + return config.backwardsCount() ? 150 : 0; } private boolean isInTheatreOfBlood() diff --git a/src/main/java/com/infernostats/Preach.java b/src/main/java/com/infernostats/Preach.java new file mode 100644 index 0000000..cfaa432 --- /dev/null +++ b/src/main/java/com/infernostats/Preach.java @@ -0,0 +1,25 @@ +package com.infernostats; + +public class Preach implements Comparable { + public int tick; + public String name; + public boolean preachedWithRing; + public int ringTick; + + public Preach(int ringTick, boolean preachedWithRing, String name, int tick) { + this.ringTick = ringTick; + this.preachedWithRing = preachedWithRing; + this.name = name; + this.tick = tick; + } + + @Override + public int compareTo(Preach p) { + return Integer.compare(p.tick, this.tick); + } + + public Preach advance(boolean isBackwards) { + this.tick += isBackwards ? -1 : 1; + return this; + } +} diff --git a/src/main/java/com/infernostats/RingChangeMessage.java b/src/main/java/com/infernostats/RingChangeMessage.java new file mode 100644 index 0000000..7d85cf7 --- /dev/null +++ b/src/main/java/com/infernostats/RingChangeMessage.java @@ -0,0 +1,15 @@ +package com.infernostats; + +import net.runelite.client.party.messages.PartyMessage; + +public class RingChangeMessage extends PartyMessage { + String n; + int r; + int t; + + public RingChangeMessage(String n, int r, int t) { + this.n = n; + this.r = r; + this.t = t; + } +} diff --git a/src/test/java/com/infernostats/GodbookPluginTest.java b/src/test/java/com/infernostats/GodbookPluginTest.java index 877cf08..4081f9b 100644 --- a/src/test/java/com/infernostats/GodbookPluginTest.java +++ b/src/test/java/com/infernostats/GodbookPluginTest.java @@ -3,11 +3,15 @@ import net.runelite.client.RuneLite; import net.runelite.client.externalplugins.ExternalPluginManager; +import java.util.Arrays; + public class GodbookPluginTest { public static void main(String[] args) throws Exception { ExternalPluginManager.loadBuiltin(GodbookPlugin.class); - RuneLite.main(args); + String[] debugArgs = Arrays.copyOf(args, args.length + 1); + debugArgs[args.length] = "--developer-mode"; + RuneLite.main(debugArgs); } } \ No newline at end of file