diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java index 6ade381617..28658ebf16 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java @@ -99,6 +99,17 @@ public interface SlayerConfig extends Config return Color.RED; } + @ConfigItem( + position = 7, + keyName = "weaknessPrompt", + name = "Show Monster Weakness", + description = "Show an overlay on a monster when it is weak enough to finish off (Only Lizards, Gargoyles & Rockslugs)" + ) + default boolean weaknessPrompt() + { + return true; + } + // Stored data @ConfigItem( keyName = "taskName", diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java index f14bf4b034..538a6e5302 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java @@ -138,6 +138,9 @@ public class SlayerPlugin extends Plugin @Inject private TargetClickboxOverlay targetClickboxOverlay; + @Inject + private TargetWeaknessOverlay targetWeaknessOverlay; + @Inject private TargetMinimapOverlay targetMinimapOverlay; @@ -181,6 +184,7 @@ public class SlayerPlugin extends Plugin { overlayManager.add(overlay); overlayManager.add(targetClickboxOverlay); + overlayManager.add(targetWeaknessOverlay); overlayManager.add(targetMinimapOverlay); if (client.getGameState() == GameState.LOGGED_IN @@ -200,6 +204,7 @@ public class SlayerPlugin extends Plugin { overlayManager.remove(overlay); overlayManager.remove(targetClickboxOverlay); + overlayManager.remove(targetWeaknessOverlay); overlayManager.remove(targetMinimapOverlay); removeCounter(); highlightedTargets.clear(); @@ -557,7 +562,8 @@ public class SlayerPlugin extends Plugin if (name.contains(target)) { NPCComposition composition = npc.getTransformedComposition(); - if (composition != null && Arrays.asList(composition.getActions()).contains("Attack")) + List actions = Arrays.asList(composition.getActions()); + if (composition != null && (actions.contains("Attack") || actions.contains("Pick"))) //Pick action is for zygomite-fungi { return true; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetWeaknessOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetWeaknessOverlay.java new file mode 100644 index 0000000000..af61ddfd13 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetWeaknessOverlay.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2018, Sam "Berry" Beresford + * 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.slayer; + +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.util.List; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.NPC; +import net.runelite.api.Perspective; +import net.runelite.api.Point; +import net.runelite.api.coords.LocalPoint; +import net.runelite.client.game.ItemManager; +import net.runelite.client.game.NPCManager; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayUtil; +import net.runelite.client.util.Text; + +class TargetWeaknessOverlay extends Overlay +{ + private final Client client; + private final SlayerConfig config; + private final SlayerPlugin plugin; + private final ItemManager itemManager; + private final NPCManager npcManager; + + @Inject + private TargetWeaknessOverlay(Client client, SlayerConfig config, SlayerPlugin plugin, ItemManager itemManager, NPCManager npcManager) + { + this.client = client; + this.config = config; + this.plugin = plugin; + this.itemManager = itemManager; + this.npcManager = npcManager; + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (!config.weaknessPrompt()) + { + return null; + } + + final Task curTask = Task.getTask(plugin.getTaskName()); + if (curTask == null || curTask.getWeaknessThreshold() < 0 || curTask.getWeaknessItem() < 0) + { + return null; + } + + final int threshold = curTask.getWeaknessThreshold(); + final BufferedImage image = itemManager.getImage(curTask.getWeaknessItem()); + + if (image == null) + { + return null; + } + + final List targets = plugin.getHighlightedTargets(); + for (NPC target : targets) + { + final int currentHealth = calculateHealth(target); + + if (currentHealth >= 0 && currentHealth <= threshold) + { + renderTargetItem(graphics, target, image); + } + } + + return null; + } + + private int calculateHealth(NPC target) + { + // Based on OpponentInfoOverlay HP calculation + if (target == null || target.getName() == null) + { + return -1; + } + + final int healthScale = target.getHealth(); + final int healthRatio = target.getHealthRatio(); + final String targetName = Text.removeTags(target.getName()); + final Integer maxHealth = npcManager.getHealth(targetName, target.getCombatLevel()); + + if (healthRatio < 0 || healthScale <= 0 || maxHealth == null) + { + return -1; + } + + return (int)((maxHealth * healthRatio / healthScale) + 0.5f); + } + + private void renderTargetItem(Graphics2D graphics, NPC actor, BufferedImage image) + { + final LocalPoint actorPosition = actor.getLocalLocation(); + final int offset = actor.getLogicalHeight() + 40; + + if (actorPosition == null || image == null) + { + return; + } + + final Point imageLoc = Perspective.getCanvasImageLocation(client, actorPosition, image, offset); + + if (imageLoc != null) + { + OverlayUtil.renderImageLocation(graphics, imageLoc, image); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java index 39239517e6..91e7bdd9e1 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java @@ -75,7 +75,7 @@ enum Task DARK_BEASTS("Dark beasts", ItemID.DARK_BEAST, "Night beast"), DARK_WARRIORS("Dark warriors", ItemID.BLACK_MED_HELM, "Dark warrior"), DERANGED_ARCHAEOLOGIST("Deranged Archaeologist", ItemID.ARCHAEOLOGISTS_DIARY), - DESERT_LIZARDS("Desert lizards", ItemID.DESERT_LIZARD, "Small lizard", "Lizard"), + DESERT_LIZARDS("Desert lizards", ItemID.DESERT_LIZARD, 4, ItemID.ICE_COOLER, "Small lizard", "Lizard"), DOGS("Dogs", ItemID.GUARD_DOG, "Jackal"), DUST_DEVILS("Dust devils", ItemID.DUST_DEVIL, "Choke devil"), DWARVES("Dwarves", ItemID.DWARVEN_HELMET, "Dwarf"), @@ -86,7 +86,7 @@ enum Task REVENANTS("Revenants", ItemID.REVENANT_ETHER, "Revenant imp", "Revenant goblin", "Revenant pyrefiend", "Revenant hobgoblin", "Revenant cyclops", "Revenant hellhound", "Revenant demon", "Revenant ork", "Revenant dark beast", "Revenant knight", "Revenant dragon"), FLESH_CRAWLERS("Flesh crawlers", ItemID.ENSOULED_SCORPION_HEAD), FOSSIL_ISLAND_WYVERNS("Fossil island wyverns", ItemID.FOSSIL_ISLAND_WYVERN, "Ancient wyvern", "Long-tailed wyvern", "Spitting wyvern", "Taloned wyvern"), - GARGOYLES("Gargoyles", ItemID.GARGOYLE), + GARGOYLES("Gargoyles", ItemID.GARGOYLE, 9, ItemID.ROCK_HAMMER), GENERAL_GRAARDOR("General Graardor", ItemID.PET_GENERAL_GRAARDOR), GHOSTS("Ghosts", ItemID.GHOSTSPEAK_AMULET, "Tortured soul"), GIANT_MOLE("Giant Mole", ItemID.BABY_MOLE), @@ -94,7 +94,7 @@ enum Task GOBLINS("Goblins", ItemID.ENSOULED_GOBLIN_HEAD), GREATER_DEMONS("Greater demons", ItemID.GREATER_DEMON_MASK), GREEN_DRAGONS("Green dragons", ItemID.GREEN_DRAGON_MASK), - GROTESQUE_GUARDIANS("Grotesque Guardians", ItemID.MIDNIGHT), + GROTESQUE_GUARDIANS("Grotesque Guardians", ItemID.MIDNIGHT, 0, ItemID.ROCK_HAMMER, "Dusk", "Dawn"), HARPIE_BUG_SWARMS("Harpie bug swarms", ItemID.SWARM), HELLHOUNDS("Hellhounds", ItemID.HELLHOUND), HILL_GIANTS("Hill giants", ItemID.ENSOULED_GIANT_HEAD), @@ -126,14 +126,14 @@ enum Task MOLANISKS("Molanisks", ItemID.MOLANISK), MONKEYS("Monkeys", ItemID.ENSOULED_MONKEY_HEAD), MOSS_GIANTS("Moss giants", ItemID.HILL_GIANT_CLUB), - MUTATED_ZYGOMITES("Mutated zygomites", ItemID.MUTATED_ZYGOMITE), + MUTATED_ZYGOMITES("Mutated zygomites", ItemID.MUTATED_ZYGOMITE, 0, ItemID.FUNGICIDE_SPRAY_0, "Zygomite", "Fungi"), NECHRYAEL("Nechryael", ItemID.NECHRYAEL, "Nechryarch"), OGRES("Ogres", ItemID.ENSOULED_OGRE_HEAD), OTHERWORLDLY_BEING("Otherworldly beings", ItemID.GHOSTLY_HOOD), PYREFIENDS("Pyrefiends", ItemID.PYREFIEND, "Flaming pyrelord"), RATS("Rats", ItemID.RATS_TAIL), RED_DRAGONS("Red dragons", ItemID.BABY_RED_DRAGON), - ROCKSLUGS("Rockslugs", ItemID.ROCKSLUG), + ROCKSLUGS("Rockslugs", ItemID.ROCKSLUG, 4, ItemID.BAG_OF_SALT), RUNE_DRAGONS("Rune dragons", ItemID.RUNITE_BAR), SCORPIA("Scorpia", ItemID.SCORPIAS_OFFSPRING), CHAOS_DRUIDS("Chaos druids", ItemID.ELDER_CHAOS_HOOD, "Elder Chaos druid", "Chaos druid"), @@ -174,6 +174,8 @@ enum Task private final String name; private final int itemSpriteId; private final String[] targetNames; + private final int weaknessThreshold; + private final int weaknessItem; static { @@ -188,6 +190,18 @@ enum Task Preconditions.checkArgument(itemSpriteId >= 0); this.name = name; this.itemSpriteId = itemSpriteId; + this.weaknessThreshold = -1; + this.weaknessItem = -1; + this.targetNames = targetNames; + } + + Task(String name, int itemSpriteId, int weaknessThreshold, int weaknessItem, String... targetNames) + { + Preconditions.checkArgument(itemSpriteId >= 0); + this.name = name; + this.itemSpriteId = itemSpriteId; + this.weaknessThreshold = weaknessThreshold; + this.weaknessItem = weaknessItem; this.targetNames = targetNames; }