From 3e0f4f3fe6f49f554540e6f79917426f4546266e Mon Sep 17 00:00:00 2001 From: raqes Date: Sun, 15 Apr 2018 20:52:42 +0200 Subject: [PATCH] Add special attack counter --- .../client/plugins/specialcounter/Boss.java | 64 +++++ .../specialcounter/SpecialCounter.java | 72 ++++++ .../specialcounter/SpecialCounterPlugin.java | 242 ++++++++++++++++++ .../plugins/specialcounter/SpecialWeapon.java | 44 ++++ 4 files changed, 422 insertions(+) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/Boss.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounter.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialWeapon.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/Boss.java b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/Boss.java new file mode 100644 index 0000000000..e4912649f5 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/Boss.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018, Raqes + * 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.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; + +@AllArgsConstructor +@Getter +@ToString +enum Boss +{ + ABYSSAL_SIRE("Abyssal sire", 1.25d), + CALLISTO("Callisto", 1.225d), + CERBERUS("Cerberus", 1.15d), + CHAOS_ELEMENTAL("Chaos elemental", 1.075d), + CORPOREAL_BEAST("Corporeal Beast", 1.55d), + GENERAL_GRAARDOR("General Graardor", 1.325d), + GIANT_MOLE("Giant Mole", 1.075d), + KALPHITE_QUEEM("Kalphite Queem", 1.05d), + KING_BLACK_DRAGON("King Black Dragon", 1.075d), + KRIL_TSUROTH("K'ril Tsutsaroth", 1.375d), + VENETENATIS("Venenatis", 1.4d), + VETION("Vet'ion", 1.225d); + + private final String name; + private final double modifier; // Some NPCs have a modifier to the experience a player receives. + + public static Boss getBoss(String name) + { + for (Boss boss : values()) + { + if (boss.getName().equals(name)) + { + return boss; + } + } + return null; + } + +} \ No newline at end of file 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 new file mode 100644 index 0000000000..8d853c368b --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounter.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018, Raqes + * 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 java.awt.image.BufferedImage; +import net.runelite.client.ui.overlay.infobox.Counter; + +public class SpecialCounter extends Counter +{ + private int hitValue; + private SpecialWeapon weapon; + + public SpecialCounter(BufferedImage image, SpecialCounterPlugin plugin, int hitValue, SpecialWeapon weapon) + { + super(image, plugin, null); + this.weapon = weapon; + this.hitValue = hitValue; + } + + public void addHits(double hit) + { + this.hitValue += hit; + } + + @Override + public String getText() + { + return Integer.toString(hitValue); + } + + @Override + public String getTooltip() + { + if (!weapon.isDamage()) + { + if (hitValue == 1) + { + return weapon.getName() + " special has hit " + hitValue + " time."; + } + else + { + return weapon.getName() + " special has hit " + hitValue + " times."; + } + } + else + { + return weapon.getName() + " special has hit " + hitValue + " total."; + } + } +} \ No newline at end of file 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 new file mode 100644 index 0000000000..7aa397c09a --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2018, Raqes + * 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 com.google.common.eventbus.Subscribe; +import javax.inject.Inject; +import net.runelite.api.Actor; +import net.runelite.api.Client; +import net.runelite.api.EquipmentInventorySlot; +import net.runelite.api.GameState; +import net.runelite.api.InventoryID; +import net.runelite.api.Item; +import net.runelite.api.ItemContainer; +import net.runelite.api.NPC; +import net.runelite.api.Player; +import net.runelite.api.Skill; +import net.runelite.api.VarPlayer; +import net.runelite.api.events.ActorDeath; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.VarbitChanged; +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; + +@PluginDescriptor( + name = "Special Attack Counter", + enabledByDefault = false +) +public class SpecialCounterPlugin extends Plugin +{ + private int currentWorld = -1; + private int specialPercentage = -1; + private int specialHitpointsExperience = -1; + private boolean specialUsed; + private double modifier = 1d; + + private SpecialWeapon specialWeapon; + private int interactedNpcId; + private final SpecialCounter[] specialCounter = new SpecialCounter[SpecialWeapon.values().length]; + + @Inject + private Client client; + + @Inject + private InfoBoxManager infoBoxManager; + + @Inject + private ItemManager itemManager; + + @Override + protected void shutDown() + { + removeCounters(); + } + + @Subscribe + public void onGameStateChanged(GameStateChanged event) + { + if (event.getGameState() == GameState.LOGGED_IN) + { + if (currentWorld == -1) + { + currentWorld = client.getWorld(); + } + else if (currentWorld != client.getWorld()) + { + currentWorld = client.getWorld(); + removeCounters(); + } + } + } + + @Subscribe + public void onSpecialChanged(VarbitChanged event) + { + int specialPercentage = client.getVar(VarPlayer.SPECIAL_ATTACK_PERCENT); + + if (this.specialPercentage == -1 || specialPercentage >= this.specialPercentage) + { + this.specialPercentage = specialPercentage; + return; + } + + this.specialPercentage = specialPercentage; + this.specialWeapon = usedSpecialWeapon(); + + checkInteracting(); + + specialUsed = true; + specialHitpointsExperience = client.getSkillExperience(Skill.HITPOINTS); + } + + @Subscribe + private void onGameTick(GameTick tick) + { + if (client.getGameState() != GameState.LOGGED_IN) + { + return; + } + + checkInteracting(); + + if (specialHitpointsExperience != -1 && specialUsed) + { + specialUsed = false; + int hpXp = client.getSkillExperience(Skill.HITPOINTS); + int deltaExperience = hpXp - specialHitpointsExperience; + specialHitpointsExperience = -1; + + if (deltaExperience > 0) + { + if (specialWeapon != null) + { + updateCounter(specialWeapon, deltaExperience); + } + } + } + } + + private void checkInteracting() + { + Player localPlayer = client.getLocalPlayer(); + Actor interacting = localPlayer.getInteracting(); + + if (interacting instanceof NPC) + { + int interactingId = ((NPC) interacting).getId(); + + if (interactedNpcId != interactingId) + { + interactedNpcId = interactingId; + removeCounters(); + + Boss boss = Boss.getBoss(interacting.getName()); + modifier = boss != null ? boss.getModifier() : 1d; + } + } + } + + @Subscribe + public void onActorDeath(ActorDeath death) + { + Actor actor = death.getActor(); + + if (actor instanceof NPC && ((NPC) actor).getId() == interactedNpcId) + { + removeCounters(); + } + } + + private SpecialWeapon usedSpecialWeapon() + { + ItemContainer equipment = client.getItemContainer(InventoryID.EQUIPMENT); + if (equipment == null) + { + return null; + } + + Item[] items = equipment.getItems(); + Item weapon = items[EquipmentInventorySlot.WEAPON.getSlotIdx()]; + + for (SpecialWeapon specialWeapon : SpecialWeapon.values()) + { + if (specialWeapon.getItemID() == weapon.getId()) + { + return specialWeapon; + } + } + return null; + } + + private void updateCounter(SpecialWeapon specialWeapon, int deltaExperience) + { + SpecialCounter counter = specialCounter[specialWeapon.ordinal()]; + int hit = getHit(specialWeapon, deltaExperience); + + if (counter == null) + { + counter = new SpecialCounter(itemManager.getImage(specialWeapon.getItemID()), this, + hit, specialWeapon); + infoBoxManager.addInfoBox(counter); + specialCounter[specialWeapon.ordinal()] = counter; + } + else + { + counter.addHits(hit); + } + } + + private void removeCounters() + { + for (int i = 0; i < specialCounter.length; ++i) + { + SpecialCounter counter = specialCounter[i]; + + if (counter != null) + { + infoBoxManager.removeInfoBox(counter); + specialCounter[i] = null; + } + } + } + + private int getHit(SpecialWeapon specialWeapon, int deltaExperience) + { + double modifierBase = 1d / modifier; + double damageOutput = (deltaExperience * modifierBase) / 1.3333d; + + if (!specialWeapon.isDamage()) + { + return 1; + } + else + { + return (int) Math.round(damageOutput); + } + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialWeapon.java b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialWeapon.java new file mode 100644 index 0000000000..69d8d68193 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialWeapon.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018, Raqes + * 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.AllArgsConstructor; +import lombok.Getter; +import net.runelite.api.ItemID; + +@AllArgsConstructor +@Getter +enum SpecialWeapon +{ + DRAGON_WARHAMMER("Dragon Warhammer", ItemID.DRAGON_WARHAMMER, false), + ARCLIGHT("Arclight", ItemID.ARCLIGHT, false), + DARKLIGHT("Darklight", ItemID.DARKLIGHT, false), + BANDOS_GODSWORD("Bandos Godsword", ItemID.BANDOS_GODSWORD, true), + BANDOS_GODSWORD_OR("Bandos Godsword", ItemID.BANDOS_GODSWORD_OR, true); + + private final String name; + private final int itemID; + private final boolean damage; +} \ No newline at end of file