diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounter.java b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounter.java index 3c7b0f2135..425c781b45 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounter.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounter.java @@ -25,15 +25,18 @@ package net.runelite.client.plugins.specialcounter; import java.awt.image.BufferedImage; +import java.util.Map; import net.runelite.client.ui.overlay.infobox.Counter; class SpecialCounter extends Counter { + private final SpecialCounterPlugin plugin; private SpecialWeapon weapon; SpecialCounter(BufferedImage image, SpecialCounterPlugin plugin, int hitValue, SpecialWeapon weapon) { super(image, plugin, hitValue); + this.plugin = plugin; this.weapon = weapon; } @@ -46,7 +49,29 @@ class SpecialCounter extends Counter @Override public String getTooltip() { + Map partySpecs = plugin.getPartySpecs(); int hitValue = getCount(); + + if (partySpecs.isEmpty()) + { + return buildTooltip(hitValue); + } + + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(buildTooltip(hitValue)); + + for (Map.Entry entry : partySpecs.entrySet()) + { + stringBuilder.append("
") + .append(entry.getKey() == null ? "You" : entry.getKey()).append(": ") + .append(buildTooltip(entry.getValue())); + } + + return stringBuilder.toString(); + } + + private String buildTooltip(int hitValue) + { if (!weapon.isDamage()) { if (hitValue == 1) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java index 0605791771..4ff5b8ae6f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java @@ -24,9 +24,13 @@ */ package net.runelite.client.plugins.specialcounter; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; import javax.inject.Inject; +import lombok.AccessLevel; +import lombok.Getter; import net.runelite.api.Actor; import net.runelite.api.Client; import net.runelite.api.EquipmentInventorySlot; @@ -42,11 +46,14 @@ import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.GameTick; import net.runelite.api.events.NpcDespawned; import net.runelite.api.events.VarbitChanged; +import net.runelite.client.callback.ClientThread; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.game.ItemManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; +import net.runelite.client.ws.PartyService; +import net.runelite.client.ws.WSClient; @PluginDescriptor( name = "Special Attack Counter", @@ -65,20 +72,38 @@ public class SpecialCounterPlugin extends Plugin private SpecialWeapon specialWeapon; private final Set interactedNpcIds = new HashSet<>(); private final SpecialCounter[] specialCounter = new SpecialCounter[SpecialWeapon.values().length]; + @Getter(AccessLevel.PACKAGE) + private final Map partySpecs = new HashMap<>(); @Inject private Client client; + @Inject + private ClientThread clientThread; + + @Inject + private WSClient wsClient; + + @Inject + private PartyService party; + @Inject private InfoBoxManager infoBoxManager; @Inject private ItemManager itemManager; + @Override + protected void startUp() + { + wsClient.registerMessage(SpecialCounterUpdate.class); + } + @Override protected void shutDown() { removeCounters(); + wsClient.unregisterMessage(SpecialCounterUpdate.class); } @Subscribe @@ -139,7 +164,16 @@ public class SpecialCounterPlugin extends Plugin { if (specialWeapon != null) { - updateCounter(specialWeapon, deltaExperience); + int hit = getHit(specialWeapon, deltaExperience); + + updateCounter(specialWeapon, null, hit); + + if (!party.getMembers().isEmpty()) + { + final SpecialCounterUpdate specialCounterUpdate = new SpecialCounterUpdate(interactingId, specialWeapon, hit); + specialCounterUpdate.setMemberId(party.getLocalMember().getMemberId()); + wsClient.send(specialCounterUpdate); + } } } } @@ -157,16 +191,7 @@ public class SpecialCounterPlugin extends Plugin if (!interactedNpcIds.contains(interactingId)) { removeCounters(); - modifier = 1d; - interactedNpcIds.add(interactingId); - - final Boss boss = Boss.getBoss(interactingId); - if (boss != null) - { - modifier = boss.getModifier(); - interactedNpcIds.addAll(boss.getIds()); - } - + addInteracting(interactingId); } return interactingId; @@ -175,6 +200,20 @@ public class SpecialCounterPlugin extends Plugin return -1; } + private void addInteracting(int npcId) + { + modifier = 1d; + interactedNpcIds.add(npcId); + + // Add alternate forms of bosses + final Boss boss = Boss.getBoss(npcId); + if (boss != null) + { + modifier = boss.getModifier(); + interactedNpcIds.addAll(boss.getIds()); + } + } + @Subscribe public void onNpcDespawned(NpcDespawned npcDespawned) { @@ -186,6 +225,36 @@ public class SpecialCounterPlugin extends Plugin } } + @Subscribe + public void onSpecialCounterUpdate(SpecialCounterUpdate event) + { + if (party.getLocalMember().getMemberId().equals(event.getMemberId())) + { + return; + } + + String name = party.getMemberById(event.getMemberId()).getName(); + if (name == null) + { + return; + } + + clientThread.invoke(() -> + { + // If not interacting with any npcs currently, add to interacting list + if (interactedNpcIds.isEmpty()) + { + addInteracting(event.getNpcId()); + } + + // Otherwise we only add the count if it is against a npc we are already tracking + if (interactedNpcIds.contains(event.getNpcId())) + { + updateCounter(event.getWeapon(), name, event.getHit()); + } + }); + } + private SpecialWeapon usedSpecialWeapon() { ItemContainer equipment = client.getItemContainer(InventoryID.EQUIPMENT); @@ -214,10 +283,9 @@ public class SpecialCounterPlugin extends Plugin return null; } - private void updateCounter(SpecialWeapon specialWeapon, int deltaExperience) + private void updateCounter(SpecialWeapon specialWeapon, String name, int hit) { SpecialCounter counter = specialCounter[specialWeapon.ordinal()]; - int hit = getHit(specialWeapon, deltaExperience); if (counter == null) { @@ -230,11 +298,25 @@ public class SpecialCounterPlugin extends Plugin { counter.addHits(hit); } + + // If in a party, add hit to partySpecs for the infobox tooltip + if (!party.getMembers().isEmpty()) + { + if (partySpecs.containsKey(name)) + { + partySpecs.put(name, hit + partySpecs.get(name)); + } + else + { + partySpecs.put(name, hit); + } + } } private void removeCounters() { interactedNpcIds.clear(); + partySpecs.clear(); for (int i = 0; i < specialCounter.length; ++i) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterUpdate.java b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterUpdate.java new file mode 100644 index 0000000000..16eff1af25 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterUpdate.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019, Trevor + * 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.plugins.specialcounter; + +import lombok.EqualsAndHashCode; +import lombok.Value; +import net.runelite.http.api.ws.messages.party.PartyMemberMessage; + +@Value +@EqualsAndHashCode(callSuper = true) +public class SpecialCounterUpdate extends PartyMemberMessage +{ + private final int npcId; + private final SpecialWeapon weapon; + private final int hit; +}