slayer
This commit is contained in:
@@ -67,7 +67,7 @@ public interface NPC extends Actor
|
||||
* @return the transformed NPC
|
||||
*/
|
||||
@Nullable
|
||||
NPCComposition getTransformedDefinition();
|
||||
NPCComposition getTransformedComposition();
|
||||
|
||||
void onDefinitionChanged(NPCComposition composition);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Seth <Sethtroll3@gmail.com>
|
||||
* Copyright (c) 2018, Shaun Dreclin <shaundreclin@gmail.com>
|
||||
* 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.Color;
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
import net.runelite.client.config.Units;
|
||||
|
||||
@ConfigGroup("slayer")
|
||||
public interface SlayerConfig extends Config
|
||||
{
|
||||
@ConfigItem(
|
||||
position = 1,
|
||||
keyName = "infobox",
|
||||
name = "Task InfoBox",
|
||||
description = "Display task information in an InfoBox"
|
||||
)
|
||||
default boolean showInfobox()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 2,
|
||||
keyName = "itemoverlay",
|
||||
name = "Count on Items",
|
||||
description = "Display task count remaining on slayer items"
|
||||
)
|
||||
default boolean showItemOverlay()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 3,
|
||||
keyName = "superiornotification",
|
||||
name = "Superior foe notification",
|
||||
description = "Toggles notifications on superior foe encounters"
|
||||
)
|
||||
default boolean showSuperiorNotification()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 4,
|
||||
keyName = "statTimeout",
|
||||
name = "InfoBox Expiry",
|
||||
description = "Set the time until the InfoBox expires"
|
||||
)
|
||||
@Units(Units.MINUTES)
|
||||
default int statTimeout()
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 5,
|
||||
keyName = "highlightTargets",
|
||||
name = "Highlight Targets",
|
||||
description = "Highlight monsters you can kill for your current slayer assignment"
|
||||
)
|
||||
default boolean highlightTargets()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 6,
|
||||
keyName = "targetColor",
|
||||
name = "Target Color",
|
||||
description = "Color of the highlighted targets"
|
||||
)
|
||||
default Color getTargetColor()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 8,
|
||||
keyName = "taskCommand",
|
||||
name = "Task Command",
|
||||
description = "Configures whether the slayer task command is enabled<br> !task"
|
||||
)
|
||||
default boolean taskCommand()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Stored data
|
||||
@ConfigItem(
|
||||
keyName = "taskName",
|
||||
name = "",
|
||||
description = "",
|
||||
hidden = true
|
||||
)
|
||||
default String taskName()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "taskName",
|
||||
name = "",
|
||||
description = ""
|
||||
)
|
||||
void taskName(String key);
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "amount",
|
||||
name = "",
|
||||
description = "",
|
||||
hidden = true
|
||||
)
|
||||
default int amount()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "amount",
|
||||
name = "",
|
||||
description = ""
|
||||
)
|
||||
void amount(int amt);
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "initialAmount",
|
||||
name = "",
|
||||
description = "",
|
||||
hidden = true
|
||||
)
|
||||
default int initialAmount()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
@ConfigItem(
|
||||
keyName = "initialAmount",
|
||||
name = "",
|
||||
description = ""
|
||||
)
|
||||
void initialAmount(int initialAmount);
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "taskLocation",
|
||||
name = "",
|
||||
description = "",
|
||||
hidden = true
|
||||
)
|
||||
default String taskLocation()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "taskLocation",
|
||||
name = "",
|
||||
description = ""
|
||||
)
|
||||
void taskLocation(String key);
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "streak",
|
||||
name = "",
|
||||
description = "",
|
||||
hidden = true
|
||||
)
|
||||
default int streak()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "streak",
|
||||
name = "",
|
||||
description = ""
|
||||
)
|
||||
void streak(int streak);
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "points",
|
||||
name = "",
|
||||
description = "",
|
||||
hidden = true
|
||||
)
|
||||
default int points()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "points",
|
||||
name = "",
|
||||
description = ""
|
||||
)
|
||||
void points(int points);
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "expeditious",
|
||||
name = "",
|
||||
description = "",
|
||||
hidden = true
|
||||
)
|
||||
default int expeditious()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "expeditious",
|
||||
name = "",
|
||||
description = ""
|
||||
)
|
||||
void expeditious(int expeditious);
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "slaughter",
|
||||
name = "",
|
||||
description = "",
|
||||
hidden = true
|
||||
)
|
||||
default int slaughter()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "slaughter",
|
||||
name = "",
|
||||
description = ""
|
||||
)
|
||||
void slaughter(int slaughter);
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Seth <Sethtroll3@gmail.com>
|
||||
* 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 com.google.common.collect.ImmutableSet;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.util.Set;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.ItemID;
|
||||
import net.runelite.api.widgets.WidgetItem;
|
||||
import net.runelite.client.ui.FontManager;
|
||||
import net.runelite.client.ui.overlay.WidgetItemOverlay;
|
||||
import net.runelite.client.ui.overlay.components.TextComponent;
|
||||
|
||||
class SlayerOverlay extends WidgetItemOverlay
|
||||
{
|
||||
private final static Set<Integer> SLAYER_JEWELRY = ImmutableSet.of(
|
||||
ItemID.SLAYER_RING_1,
|
||||
ItemID.SLAYER_RING_2,
|
||||
ItemID.SLAYER_RING_3,
|
||||
ItemID.SLAYER_RING_4,
|
||||
ItemID.SLAYER_RING_5,
|
||||
ItemID.SLAYER_RING_6,
|
||||
ItemID.SLAYER_RING_7,
|
||||
ItemID.SLAYER_RING_8
|
||||
);
|
||||
|
||||
private final static Set<Integer> ALL_SLAYER_ITEMS = ImmutableSet.of(
|
||||
ItemID.SLAYER_HELMET,
|
||||
ItemID.SLAYER_HELMET_I,
|
||||
ItemID.BLACK_SLAYER_HELMET,
|
||||
ItemID.BLACK_SLAYER_HELMET_I,
|
||||
ItemID.GREEN_SLAYER_HELMET,
|
||||
ItemID.GREEN_SLAYER_HELMET_I,
|
||||
ItemID.PURPLE_SLAYER_HELMET,
|
||||
ItemID.PURPLE_SLAYER_HELMET_I,
|
||||
ItemID.RED_SLAYER_HELMET,
|
||||
ItemID.RED_SLAYER_HELMET_I,
|
||||
ItemID.TURQUOISE_SLAYER_HELMET,
|
||||
ItemID.TURQUOISE_SLAYER_HELMET_I,
|
||||
ItemID.TWISTED_SLAYER_HELMET,
|
||||
ItemID.TWISTED_SLAYER_HELMET_I,
|
||||
ItemID.HYDRA_SLAYER_HELMET,
|
||||
ItemID.HYDRA_SLAYER_HELMET_I,
|
||||
ItemID.SLAYER_RING_ETERNAL,
|
||||
ItemID.ENCHANTED_GEM,
|
||||
ItemID.ETERNAL_GEM,
|
||||
ItemID.BRACELET_OF_SLAUGHTER,
|
||||
ItemID.EXPEDITIOUS_BRACELET,
|
||||
ItemID.SLAYER_RING_1,
|
||||
ItemID.SLAYER_RING_2,
|
||||
ItemID.SLAYER_RING_3,
|
||||
ItemID.SLAYER_RING_4,
|
||||
ItemID.SLAYER_RING_5,
|
||||
ItemID.SLAYER_RING_6,
|
||||
ItemID.SLAYER_RING_7,
|
||||
ItemID.SLAYER_RING_8
|
||||
);
|
||||
|
||||
private final SlayerConfig config;
|
||||
private final SlayerPlugin plugin;
|
||||
|
||||
@Inject
|
||||
private SlayerOverlay(SlayerPlugin plugin, SlayerConfig config)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
showOnInventory();
|
||||
showOnEquipment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderItemOverlay(Graphics2D graphics, int itemId, WidgetItem itemWidget)
|
||||
{
|
||||
if (!ALL_SLAYER_ITEMS.contains(itemId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!config.showItemOverlay())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int amount = plugin.getAmount();
|
||||
if (amount <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int slaughterCount = plugin.getSlaughterChargeCount();
|
||||
int expeditiousCount = plugin.getExpeditiousChargeCount();
|
||||
|
||||
graphics.setFont(FontManager.getRunescapeSmallFont());
|
||||
|
||||
final Rectangle bounds = itemWidget.getCanvasBounds();
|
||||
final TextComponent textComponent = new TextComponent();
|
||||
|
||||
switch (itemId)
|
||||
{
|
||||
case ItemID.EXPEDITIOUS_BRACELET:
|
||||
textComponent.setText(String.valueOf(expeditiousCount));
|
||||
break;
|
||||
case ItemID.BRACELET_OF_SLAUGHTER:
|
||||
textComponent.setText(String.valueOf(slaughterCount));
|
||||
break;
|
||||
default:
|
||||
textComponent.setText(String.valueOf(amount));
|
||||
break;
|
||||
}
|
||||
|
||||
// Draw the counter in the bottom left for equipment, and top left for jewelry
|
||||
textComponent.setPosition(new Point(bounds.x - 1, bounds.y - 1 + (SLAYER_JEWELRY.contains(itemId)
|
||||
? bounds.height
|
||||
: graphics.getFontMetrics().getHeight())));
|
||||
textComponent.render(graphics);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,903 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Tyler <https://github.com/tylerthardy>
|
||||
* Copyright (c) 2018, Shaun Dreclin <shaundreclin@gmail.com>
|
||||
* 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 com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.inject.Provides;
|
||||
import java.awt.Color;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import static java.lang.Integer.max;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.inject.Inject;
|
||||
import joptsimple.internal.Strings;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.Actor;
|
||||
import net.runelite.api.ChatMessageType;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.Hitsplat;
|
||||
import net.runelite.api.ItemID;
|
||||
import net.runelite.api.MessageNode;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.api.NPCComposition;
|
||||
import static net.runelite.api.Skill.SLAYER;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import net.runelite.api.events.ActorDeath;
|
||||
import net.runelite.api.events.ChatMessage;
|
||||
import net.runelite.api.events.GameStateChanged;
|
||||
import net.runelite.api.events.GameTick;
|
||||
import net.runelite.api.events.HitsplatApplied;
|
||||
import net.runelite.api.events.NpcDespawned;
|
||||
import net.runelite.api.events.NpcSpawned;
|
||||
import net.runelite.api.events.StatChanged;
|
||||
import net.runelite.api.vars.SlayerUnlock;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import net.runelite.client.Notifier;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
import net.runelite.client.chat.ChatColorType;
|
||||
import net.runelite.client.chat.ChatCommandManager;
|
||||
import net.runelite.client.chat.ChatMessageBuilder;
|
||||
import net.runelite.client.chat.ChatMessageManager;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.events.ChatInput;
|
||||
import net.runelite.client.events.ConfigChanged;
|
||||
import net.runelite.client.game.ItemManager;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.ui.overlay.OverlayManager;
|
||||
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
|
||||
import net.runelite.client.util.ColorUtil;
|
||||
import net.runelite.client.util.Text;
|
||||
import net.runelite.http.api.chat.ChatClient;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Slayer",
|
||||
description = "Show additional slayer task related information",
|
||||
tags = {"combat", "notifications", "overlay", "tasks"}
|
||||
)
|
||||
@Slf4j
|
||||
public class SlayerPlugin extends Plugin
|
||||
{
|
||||
//Chat messages
|
||||
private static final Pattern CHAT_GEM_PROGRESS_MESSAGE = Pattern.compile("^(?:You're assigned to kill|You have received a new Slayer assignment from .*:) (?:[Tt]he )?(?<name>.+?)(?: (?:in|on|south of) (?:the )?(?<location>[^;]+))?(?:; only | \\()(?<amount>\\d+)(?: more to go\\.|\\))$");
|
||||
private static final String CHAT_GEM_COMPLETE_MESSAGE = "You need something new to hunt.";
|
||||
private static final Pattern CHAT_COMPLETE_MESSAGE = Pattern.compile("(?:\\d+,)*\\d+");
|
||||
private static final String CHAT_CANCEL_MESSAGE = "Your task has been cancelled.";
|
||||
private static final String CHAT_CANCEL_MESSAGE_JAD = "You no longer have a slayer task as you left the fight cave.";
|
||||
private static final String CHAT_CANCEL_MESSAGE_ZUK = "You no longer have a slayer task as you left the Inferno.";
|
||||
private static final String CHAT_SUPERIOR_MESSAGE = "A superior foe has appeared...";
|
||||
private static final String CHAT_BRACELET_SLAUGHTER = "Your bracelet of slaughter prevents your slayer";
|
||||
private static final Pattern CHAT_BRACELET_SLAUGHTER_REGEX = Pattern.compile("Your bracelet of slaughter prevents your slayer count from decreasing. It has (\\d{1,2}) charges? left\\.");
|
||||
private static final String CHAT_BRACELET_EXPEDITIOUS = "Your expeditious bracelet helps you progress your";
|
||||
private static final Pattern CHAT_BRACELET_EXPEDITIOUS_REGEX = Pattern.compile("Your expeditious bracelet helps you progress your slayer (?:task )?faster. It has (\\d{1,2}) charges? left\\.");
|
||||
private static final String CHAT_BRACELET_SLAUGHTER_CHARGE = "Your bracelet of slaughter has ";
|
||||
private static final Pattern CHAT_BRACELET_SLAUGHTER_CHARGE_REGEX = Pattern.compile("Your bracelet of slaughter has (\\d{1,2}) charges? left\\.");
|
||||
private static final String CHAT_BRACELET_EXPEDITIOUS_CHARGE = "Your expeditious bracelet has ";
|
||||
private static final Pattern CHAT_BRACELET_EXPEDITIOUS_CHARGE_REGEX = Pattern.compile("Your expeditious bracelet has (\\d{1,2}) charges? left\\.");
|
||||
private static final Pattern COMBAT_BRACELET_TASK_UPDATE_MESSAGE = Pattern.compile("^You still need to kill (\\d+) monsters to complete your current Slayer assignment");
|
||||
|
||||
//NPC messages
|
||||
private static final Pattern NPC_ASSIGN_MESSAGE = Pattern.compile(".*(?:Your new task is to kill|You are to bring balance to)\\s*(?<amount>\\d+) (?<name>.+?)(?: (?:in|on|south of) (?:the )?(?<location>.+))?\\.");
|
||||
private static final Pattern NPC_ASSIGN_BOSS_MESSAGE = Pattern.compile("^(?:Excellent\\. )?You're now assigned to (?:kill|bring balance to) (?:the )?(.*) (\\d+) times.*Your reward point tally is (.*)\\.$");
|
||||
private static final Pattern NPC_ASSIGN_FIRST_MESSAGE = Pattern.compile("^We'll start you off (?:hunting|bringing balance to) (.*), you'll need to kill (\\d*) of them\\.$");
|
||||
private static final Pattern NPC_CURRENT_MESSAGE = Pattern.compile("^You're (?:still(?: meant to be)?|currently assigned to) (?:hunting|bringing balance to|kill|bring balance to|slaying) (?<name>.+?)(?: (?:in|on|south of) (?:the )?(?<location>.+))?(?:, with|; (?:you have|only)) (?<amount>\\d+)(?: more)? to go\\..*");
|
||||
|
||||
//Reward UI
|
||||
private static final Pattern REWARD_POINTS = Pattern.compile("Reward points: ((?:\\d+,)*\\d+)");
|
||||
|
||||
private static final int GROTESQUE_GUARDIANS_REGION = 6727;
|
||||
|
||||
private static final int EXPEDITIOUS_CHARGE = 30;
|
||||
private static final int SLAUGHTER_CHARGE = 30;
|
||||
|
||||
// Chat Command
|
||||
private static final String TASK_COMMAND_STRING = "!task";
|
||||
private static final Pattern TASK_STRING_VALIDATION = Pattern.compile("[^a-zA-Z0-9' -]");
|
||||
private static final int TASK_STRING_MAX_LENGTH = 50;
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private SlayerConfig config;
|
||||
|
||||
@Inject
|
||||
private OverlayManager overlayManager;
|
||||
|
||||
@Inject
|
||||
private SlayerOverlay overlay;
|
||||
|
||||
@Inject
|
||||
private InfoBoxManager infoBoxManager;
|
||||
|
||||
@Inject
|
||||
private ItemManager itemManager;
|
||||
|
||||
@Inject
|
||||
private Notifier notifier;
|
||||
|
||||
@Inject
|
||||
private ClientThread clientThread;
|
||||
|
||||
@Inject
|
||||
private TargetClickboxOverlay targetClickboxOverlay;
|
||||
|
||||
@Inject
|
||||
private TargetWeaknessOverlay targetWeaknessOverlay;
|
||||
|
||||
@Inject
|
||||
private TargetMinimapOverlay targetMinimapOverlay;
|
||||
|
||||
@Inject
|
||||
private ChatMessageManager chatMessageManager;
|
||||
|
||||
@Inject
|
||||
private ChatCommandManager chatCommandManager;
|
||||
|
||||
@Inject
|
||||
private ScheduledExecutorService executor;
|
||||
|
||||
@Inject
|
||||
private ChatClient chatClient;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private List<NPC> highlightedTargets = new ArrayList<>();
|
||||
|
||||
private final Set<NPC> taggedNpcs = new HashSet<>();
|
||||
private int taggedNpcsDiedPrevTick;
|
||||
private int taggedNpcsDiedThisTick;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
@Setter(AccessLevel.PACKAGE)
|
||||
private int amount;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
@Setter(AccessLevel.PACKAGE)
|
||||
private int initialAmount;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
@Setter(AccessLevel.PACKAGE)
|
||||
private String taskLocation;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
@Setter(AccessLevel.PACKAGE)
|
||||
private int expeditiousChargeCount;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
@Setter(AccessLevel.PACKAGE)
|
||||
private int slaughterChargeCount;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
@Setter(AccessLevel.PACKAGE)
|
||||
private String taskName;
|
||||
|
||||
private TaskCounter counter;
|
||||
private int cachedXp = -1;
|
||||
private Instant infoTimer;
|
||||
private boolean loginFlag;
|
||||
private final List<String> targetNames = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
{
|
||||
overlayManager.add(overlay);
|
||||
overlayManager.add(targetClickboxOverlay);
|
||||
overlayManager.add(targetWeaknessOverlay);
|
||||
overlayManager.add(targetMinimapOverlay);
|
||||
|
||||
if (client.getGameState() == GameState.LOGGED_IN)
|
||||
{
|
||||
cachedXp = client.getSkillExperience(SLAYER);
|
||||
|
||||
if (config.amount() != -1
|
||||
&& !config.taskName().isEmpty())
|
||||
{
|
||||
setExpeditiousChargeCount(config.expeditious());
|
||||
setSlaughterChargeCount(config.slaughter());
|
||||
clientThread.invoke(() -> setTask(config.taskName(), config.amount(), config.initialAmount(), config.taskLocation(), false));
|
||||
}
|
||||
}
|
||||
|
||||
chatCommandManager.registerCommandAsync(TASK_COMMAND_STRING, this::taskLookup, this::taskSubmit);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown() throws Exception
|
||||
{
|
||||
overlayManager.remove(overlay);
|
||||
overlayManager.remove(targetClickboxOverlay);
|
||||
overlayManager.remove(targetWeaknessOverlay);
|
||||
overlayManager.remove(targetMinimapOverlay);
|
||||
removeCounter();
|
||||
highlightedTargets.clear();
|
||||
taggedNpcs.clear();
|
||||
cachedXp = -1;
|
||||
|
||||
chatCommandManager.unregisterCommand(TASK_COMMAND_STRING);
|
||||
}
|
||||
|
||||
@Provides
|
||||
SlayerConfig provideSlayerConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(SlayerConfig.class);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameStateChanged(GameStateChanged event)
|
||||
{
|
||||
switch (event.getGameState())
|
||||
{
|
||||
case HOPPING:
|
||||
case LOGGING_IN:
|
||||
cachedXp = -1;
|
||||
taskName = "";
|
||||
amount = 0;
|
||||
loginFlag = true;
|
||||
highlightedTargets.clear();
|
||||
taggedNpcs.clear();
|
||||
break;
|
||||
case LOGGED_IN:
|
||||
if (config.amount() != -1
|
||||
&& !config.taskName().isEmpty()
|
||||
&& loginFlag)
|
||||
{
|
||||
setExpeditiousChargeCount(config.expeditious());
|
||||
setSlaughterChargeCount(config.slaughter());
|
||||
setTask(config.taskName(), config.amount(), config.initialAmount(), config.taskLocation(), false);
|
||||
loginFlag = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void save()
|
||||
{
|
||||
config.amount(amount);
|
||||
config.initialAmount(initialAmount);
|
||||
config.taskName(taskName);
|
||||
config.taskLocation(taskLocation);
|
||||
config.expeditious(expeditiousChargeCount);
|
||||
config.slaughter(slaughterChargeCount);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onNpcSpawned(NpcSpawned npcSpawned)
|
||||
{
|
||||
NPC npc = npcSpawned.getNpc();
|
||||
if (isTarget(npc))
|
||||
{
|
||||
highlightedTargets.add(npc);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onNpcDespawned(NpcDespawned npcDespawned)
|
||||
{
|
||||
NPC npc = npcDespawned.getNpc();
|
||||
taggedNpcs.remove(npc);
|
||||
highlightedTargets.remove(npc);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameTick(GameTick tick)
|
||||
{
|
||||
Widget npcDialog = client.getWidget(WidgetInfo.DIALOG_NPC_TEXT);
|
||||
if (npcDialog != null)
|
||||
{
|
||||
String npcText = Text.sanitizeMultilineText(npcDialog.getText()); //remove color and linebreaks
|
||||
final Matcher mAssign = NPC_ASSIGN_MESSAGE.matcher(npcText); // amount, name, (location)
|
||||
final Matcher mAssignFirst = NPC_ASSIGN_FIRST_MESSAGE.matcher(npcText); // name, number
|
||||
final Matcher mAssignBoss = NPC_ASSIGN_BOSS_MESSAGE.matcher(npcText); // name, number, points
|
||||
final Matcher mCurrent = NPC_CURRENT_MESSAGE.matcher(npcText); // name, (location), amount
|
||||
|
||||
if (mAssign.find())
|
||||
{
|
||||
String name = mAssign.group("name");
|
||||
int amount = Integer.parseInt(mAssign.group("amount"));
|
||||
String location = mAssign.group("location");
|
||||
setTask(name, amount, amount, location);
|
||||
}
|
||||
else if (mAssignFirst.find())
|
||||
{
|
||||
int amount = Integer.parseInt(mAssignFirst.group(2));
|
||||
setTask(mAssignFirst.group(1), amount, amount);
|
||||
}
|
||||
else if (mAssignBoss.find())
|
||||
{
|
||||
int amount = Integer.parseInt(mAssignBoss.group(2));
|
||||
setTask(mAssignBoss.group(1), amount, amount);
|
||||
int points = Integer.parseInt(mAssignBoss.group(3).replaceAll(",", ""));
|
||||
config.points(points);
|
||||
}
|
||||
else if (mCurrent.find())
|
||||
{
|
||||
String name = mCurrent.group("name");
|
||||
int amount = Integer.parseInt(mCurrent.group("amount"));
|
||||
String location = mCurrent.group("location");
|
||||
setTask(name, amount, initialAmount, location);
|
||||
}
|
||||
}
|
||||
|
||||
Widget braceletBreakWidget = client.getWidget(WidgetInfo.DIALOG_SPRITE_TEXT);
|
||||
if (braceletBreakWidget != null)
|
||||
{
|
||||
String braceletText = Text.removeTags(braceletBreakWidget.getText()); //remove color and linebreaks
|
||||
if (braceletText.contains("bracelet of slaughter"))
|
||||
{
|
||||
slaughterChargeCount = SLAUGHTER_CHARGE;
|
||||
config.slaughter(slaughterChargeCount);
|
||||
}
|
||||
else if (braceletText.contains("expeditious bracelet"))
|
||||
{
|
||||
expeditiousChargeCount = EXPEDITIOUS_CHARGE;
|
||||
config.expeditious(expeditiousChargeCount);
|
||||
}
|
||||
}
|
||||
|
||||
Widget rewardsBarWidget = client.getWidget(WidgetInfo.SLAYER_REWARDS_TOPBAR);
|
||||
if (rewardsBarWidget != null)
|
||||
{
|
||||
for (Widget w : rewardsBarWidget.getDynamicChildren())
|
||||
{
|
||||
Matcher mPoints = REWARD_POINTS.matcher(w.getText());
|
||||
if (mPoints.find())
|
||||
{
|
||||
final int prevPoints = config.points();
|
||||
int points = Integer.parseInt(mPoints.group(1).replaceAll(",", ""));
|
||||
|
||||
if (prevPoints != points)
|
||||
{
|
||||
config.points(points);
|
||||
removeCounter();
|
||||
addCounter();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (infoTimer != null && config.statTimeout() != 0)
|
||||
{
|
||||
Duration timeSinceInfobox = Duration.between(infoTimer, Instant.now());
|
||||
Duration statTimeout = Duration.ofMinutes(config.statTimeout());
|
||||
|
||||
if (timeSinceInfobox.compareTo(statTimeout) >= 0)
|
||||
{
|
||||
removeCounter();
|
||||
}
|
||||
}
|
||||
|
||||
taggedNpcsDiedPrevTick = taggedNpcsDiedThisTick;
|
||||
taggedNpcsDiedThisTick = 0;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onChatMessage(ChatMessage event)
|
||||
{
|
||||
if (event.getType() != ChatMessageType.GAMEMESSAGE && event.getType() != ChatMessageType.SPAM)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
String chatMsg = Text.removeTags(event.getMessage()); //remove color and linebreaks
|
||||
|
||||
if (chatMsg.startsWith(CHAT_BRACELET_SLAUGHTER))
|
||||
{
|
||||
Matcher mSlaughter = CHAT_BRACELET_SLAUGHTER_REGEX.matcher(chatMsg);
|
||||
|
||||
amount++;
|
||||
slaughterChargeCount = mSlaughter.find() ? Integer.parseInt(mSlaughter.group(1)) : SLAUGHTER_CHARGE;
|
||||
config.slaughter(slaughterChargeCount);
|
||||
}
|
||||
|
||||
if (chatMsg.startsWith(CHAT_BRACELET_EXPEDITIOUS))
|
||||
{
|
||||
Matcher mExpeditious = CHAT_BRACELET_EXPEDITIOUS_REGEX.matcher(chatMsg);
|
||||
|
||||
amount--;
|
||||
expeditiousChargeCount = mExpeditious.find() ? Integer.parseInt(mExpeditious.group(1)) : EXPEDITIOUS_CHARGE;
|
||||
config.expeditious(expeditiousChargeCount);
|
||||
}
|
||||
|
||||
if (chatMsg.startsWith(CHAT_BRACELET_EXPEDITIOUS_CHARGE))
|
||||
{
|
||||
Matcher mExpeditious = CHAT_BRACELET_EXPEDITIOUS_CHARGE_REGEX.matcher(chatMsg);
|
||||
|
||||
if (!mExpeditious.find())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
expeditiousChargeCount = Integer.parseInt(mExpeditious.group(1));
|
||||
config.expeditious(expeditiousChargeCount);
|
||||
}
|
||||
if (chatMsg.startsWith(CHAT_BRACELET_SLAUGHTER_CHARGE))
|
||||
{
|
||||
Matcher mSlaughter = CHAT_BRACELET_SLAUGHTER_CHARGE_REGEX.matcher(chatMsg);
|
||||
if (!mSlaughter.find())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
slaughterChargeCount = Integer.parseInt(mSlaughter.group(1));
|
||||
config.slaughter(slaughterChargeCount);
|
||||
}
|
||||
|
||||
if (chatMsg.startsWith("You've completed") && (chatMsg.contains("Slayer master") || chatMsg.contains("Slayer Master")))
|
||||
{
|
||||
Matcher mComplete = CHAT_COMPLETE_MESSAGE.matcher(chatMsg);
|
||||
|
||||
List<String> matches = new ArrayList<>();
|
||||
while (mComplete.find())
|
||||
{
|
||||
matches.add(mComplete.group(0).replaceAll(",", ""));
|
||||
}
|
||||
|
||||
int streak = -1, points = -1;
|
||||
switch (matches.size())
|
||||
{
|
||||
case 0:
|
||||
streak = 1;
|
||||
break;
|
||||
case 1:
|
||||
streak = Integer.parseInt(matches.get(0));
|
||||
break;
|
||||
case 3:
|
||||
streak = Integer.parseInt(matches.get(0));
|
||||
points = Integer.parseInt(matches.get(2));
|
||||
break;
|
||||
default:
|
||||
log.warn("Unreachable default case for message ending in '; return to Slayer master'");
|
||||
}
|
||||
if (streak != -1)
|
||||
{
|
||||
config.streak(streak);
|
||||
}
|
||||
if (points != -1)
|
||||
{
|
||||
config.points(points);
|
||||
}
|
||||
|
||||
setTask("", 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (chatMsg.equals(CHAT_GEM_COMPLETE_MESSAGE) || chatMsg.equals(CHAT_CANCEL_MESSAGE) || chatMsg.equals(CHAT_CANCEL_MESSAGE_JAD) || chatMsg.equals(CHAT_CANCEL_MESSAGE_ZUK))
|
||||
{
|
||||
setTask("", 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.showSuperiorNotification() && chatMsg.equals(CHAT_SUPERIOR_MESSAGE))
|
||||
{
|
||||
notifier.notify(CHAT_SUPERIOR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
Matcher mProgress = CHAT_GEM_PROGRESS_MESSAGE.matcher(chatMsg);
|
||||
|
||||
if (mProgress.find())
|
||||
{
|
||||
String name = mProgress.group("name");
|
||||
int gemAmount = Integer.parseInt(mProgress.group("amount"));
|
||||
String location = mProgress.group("location");
|
||||
setTask(name, gemAmount, initialAmount, location);
|
||||
return;
|
||||
}
|
||||
|
||||
final Matcher bracerProgress = COMBAT_BRACELET_TASK_UPDATE_MESSAGE.matcher(chatMsg);
|
||||
|
||||
if (bracerProgress.find())
|
||||
{
|
||||
final int taskAmount = Integer.parseInt(bracerProgress.group(1));
|
||||
setTask(taskName, taskAmount, initialAmount);
|
||||
|
||||
// Avoid race condition (combat brace message goes through first before XP drop)
|
||||
amount++;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onStatChanged(StatChanged statChanged)
|
||||
{
|
||||
if (statChanged.getSkill() != SLAYER)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int slayerExp = statChanged.getXp();
|
||||
|
||||
if (slayerExp <= cachedXp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (cachedXp == -1)
|
||||
{
|
||||
// this is the initial xp sent on login
|
||||
cachedXp = slayerExp;
|
||||
return;
|
||||
}
|
||||
|
||||
final int delta = slayerExp - cachedXp;
|
||||
cachedXp = slayerExp;
|
||||
|
||||
log.debug("Slayer xp change delta: {}, killed npcs: {}", delta, taggedNpcsDiedPrevTick);
|
||||
|
||||
final Task task = Task.getTask(taskName);
|
||||
if (task != null && task.getExpectedKillExp() > 0)
|
||||
{
|
||||
// Only decrement a kill if the xp drop matches the expected drop. This is just for Tzhaar tasks.
|
||||
if (task.getExpectedKillExp() == delta)
|
||||
{
|
||||
killed(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is at least one kill, but if we observe multiple tagged NPCs dieing on the previous tick, count them
|
||||
// instead.
|
||||
killed(max(taggedNpcsDiedPrevTick, 1));
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onHitsplatApplied(HitsplatApplied hitsplatApplied)
|
||||
{
|
||||
Actor actor = hitsplatApplied.getActor();
|
||||
Hitsplat hitsplat = hitsplatApplied.getHitsplat();
|
||||
if (hitsplat.getHitsplatType() == Hitsplat.HitsplatType.DAMAGE_ME && highlightedTargets.contains(actor))
|
||||
{
|
||||
// If the actor is in highlightedTargets it must be an NPC and also a task assignment
|
||||
taggedNpcs.add((NPC) actor);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onActorDeath(ActorDeath actorDeath)
|
||||
{
|
||||
Actor actor = actorDeath.getActor();
|
||||
if (taggedNpcs.contains(actor))
|
||||
{
|
||||
log.debug("Tagged NPC {} has died", actor.getName());
|
||||
++taggedNpcsDiedThisTick;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
private void onConfigChanged(ConfigChanged event)
|
||||
{
|
||||
if (!event.getGroup().equals("slayer") || !event.getKey().equals("infobox"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.showInfobox())
|
||||
{
|
||||
clientThread.invoke(this::addCounter);
|
||||
}
|
||||
else
|
||||
{
|
||||
removeCounter();
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void killed(int amt)
|
||||
{
|
||||
if (amount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
amount -= amt;
|
||||
if (doubleTroubleExtraKill())
|
||||
{
|
||||
assert amt == 1;
|
||||
amount--;
|
||||
}
|
||||
|
||||
config.amount(amount); // save changed value
|
||||
|
||||
if (!config.showInfobox())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// add and update counter, set timer
|
||||
addCounter();
|
||||
counter.setCount(amount);
|
||||
infoTimer = Instant.now();
|
||||
}
|
||||
|
||||
private boolean doubleTroubleExtraKill()
|
||||
{
|
||||
return WorldPoint.fromLocalInstance(client, client.getLocalPlayer().getLocalLocation()).getRegionID() == GROTESQUE_GUARDIANS_REGION &&
|
||||
SlayerUnlock.GROTESQUE_GUARDIAN_DOUBLE_COUNT.isEnabled(client);
|
||||
}
|
||||
|
||||
private boolean isTarget(NPC npc)
|
||||
{
|
||||
if (targetNames.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
String name = npc.getName();
|
||||
if (name == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
name = name.toLowerCase();
|
||||
|
||||
for (String target : targetNames)
|
||||
{
|
||||
if (name.contains(target))
|
||||
{
|
||||
NPCComposition composition = npc.getTransformedComposition();
|
||||
|
||||
if (composition != null)
|
||||
{
|
||||
List<String> actions = Arrays.asList(composition.getActions());
|
||||
if (actions.contains("Attack") || actions.contains("Pick")) //Pick action is for zygomite-fungi
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void rebuildTargetNames(Task task)
|
||||
{
|
||||
targetNames.clear();
|
||||
|
||||
if (task != null)
|
||||
{
|
||||
Arrays.stream(task.getTargetNames())
|
||||
.map(String::toLowerCase)
|
||||
.forEach(targetNames::add);
|
||||
|
||||
targetNames.add(taskName.toLowerCase().replaceAll("s$", ""));
|
||||
}
|
||||
}
|
||||
|
||||
private void rebuildTargetList()
|
||||
{
|
||||
highlightedTargets.clear();
|
||||
|
||||
for (NPC npc : client.getNpcs())
|
||||
{
|
||||
if (isTarget(npc))
|
||||
{
|
||||
highlightedTargets.add(npc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setTask(String name, int amt, int initAmt)
|
||||
{
|
||||
setTask(name, amt, initAmt, null);
|
||||
}
|
||||
|
||||
private void setTask(String name, int amt, int initAmt, String location)
|
||||
{
|
||||
setTask(name, amt, initAmt, location, true);
|
||||
}
|
||||
|
||||
private void setTask(String name, int amt, int initAmt, String location, boolean addCounter)
|
||||
{
|
||||
taskName = name;
|
||||
amount = amt;
|
||||
initialAmount = Math.max(amt, initAmt);
|
||||
taskLocation = location;
|
||||
save();
|
||||
removeCounter();
|
||||
|
||||
if (addCounter)
|
||||
{
|
||||
infoTimer = Instant.now();
|
||||
addCounter();
|
||||
}
|
||||
|
||||
Task task = Task.getTask(name);
|
||||
rebuildTargetNames(task);
|
||||
rebuildTargetList();
|
||||
}
|
||||
|
||||
private void addCounter()
|
||||
{
|
||||
if (!config.showInfobox() || counter != null || Strings.isNullOrEmpty(taskName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Task task = Task.getTask(taskName);
|
||||
int itemSpriteId = ItemID.ENCHANTED_GEM;
|
||||
if (task != null)
|
||||
{
|
||||
itemSpriteId = task.getItemSpriteId();
|
||||
}
|
||||
|
||||
BufferedImage taskImg = itemManager.getImage(itemSpriteId);
|
||||
String taskTooltip = ColorUtil.wrapWithColorTag("%s", new Color(255, 119, 0)) + "</br>";
|
||||
|
||||
if (taskLocation != null && !taskLocation.isEmpty())
|
||||
{
|
||||
taskTooltip += taskLocation + "</br>";
|
||||
}
|
||||
|
||||
taskTooltip += ColorUtil.wrapWithColorTag("Pts:", Color.YELLOW)
|
||||
+ " %s</br>"
|
||||
+ ColorUtil.wrapWithColorTag("Streak:", Color.YELLOW)
|
||||
+ " %s";
|
||||
|
||||
if (initialAmount > 0)
|
||||
{
|
||||
taskTooltip += "</br>"
|
||||
+ ColorUtil.wrapWithColorTag("Start:", Color.YELLOW)
|
||||
+ " " + initialAmount;
|
||||
}
|
||||
|
||||
counter = new TaskCounter(taskImg, this, amount);
|
||||
counter.setTooltip(String.format(taskTooltip, capsString(taskName), config.points(), config.streak()));
|
||||
|
||||
infoBoxManager.addInfoBox(counter);
|
||||
}
|
||||
|
||||
private void removeCounter()
|
||||
{
|
||||
if (counter == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
infoBoxManager.removeInfoBox(counter);
|
||||
counter = null;
|
||||
}
|
||||
|
||||
void taskLookup(ChatMessage chatMessage, String message)
|
||||
{
|
||||
if (!config.taskCommand())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ChatMessageType type = chatMessage.getType();
|
||||
|
||||
final String player;
|
||||
if (type.equals(ChatMessageType.PRIVATECHATOUT))
|
||||
{
|
||||
player = client.getLocalPlayer().getName();
|
||||
}
|
||||
else
|
||||
{
|
||||
player = Text.removeTags(chatMessage.getName())
|
||||
.replace('\u00A0', ' ');
|
||||
}
|
||||
|
||||
net.runelite.http.api.chat.Task task;
|
||||
try
|
||||
{
|
||||
task = chatClient.getTask(player);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
log.debug("unable to lookup slayer task", ex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TASK_STRING_VALIDATION.matcher(task.getTask()).find() || task.getTask().length() > TASK_STRING_MAX_LENGTH ||
|
||||
TASK_STRING_VALIDATION.matcher(task.getLocation()).find() || task.getLocation().length() > TASK_STRING_MAX_LENGTH ||
|
||||
Task.getTask(task.getTask()) == null || !Task.LOCATIONS.contains(task.getLocation()))
|
||||
{
|
||||
log.debug("Validation failed for task name or location: {}", task);
|
||||
return;
|
||||
}
|
||||
|
||||
int killed = task.getInitialAmount() - task.getAmount();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(task.getTask());
|
||||
if (!Strings.isNullOrEmpty(task.getLocation()))
|
||||
{
|
||||
sb.append(" (").append(task.getLocation()).append(")");
|
||||
}
|
||||
sb.append(": ");
|
||||
if (killed < 0)
|
||||
{
|
||||
sb.append(task.getAmount()).append(" left");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append(killed).append('/').append(task.getInitialAmount()).append(" killed");
|
||||
}
|
||||
|
||||
String response = new ChatMessageBuilder()
|
||||
.append(ChatColorType.NORMAL)
|
||||
.append("Slayer Task: ")
|
||||
.append(ChatColorType.HIGHLIGHT)
|
||||
.append(sb.toString())
|
||||
.build();
|
||||
|
||||
final MessageNode messageNode = chatMessage.getMessageNode();
|
||||
messageNode.setRuneLiteFormatMessage(response);
|
||||
chatMessageManager.update(messageNode);
|
||||
client.refreshChat();
|
||||
}
|
||||
|
||||
private boolean taskSubmit(ChatInput chatInput, String value)
|
||||
{
|
||||
if (Strings.isNullOrEmpty(taskName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final String playerName = client.getLocalPlayer().getName();
|
||||
|
||||
executor.execute(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
chatClient.submitTask(playerName, capsString(taskName), amount, initialAmount, taskLocation);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.warn("unable to submit slayer task", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
chatInput.resume();
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//Utils
|
||||
private String capsString(String str)
|
||||
{
|
||||
return str.substring(0, 1).toUpperCase() + str.substring(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2018, James Swindle <wilingua@gmail.com>
|
||||
* Copyright (c) 2018, Adam <Adam@sigterm.info>
|
||||
* Copyright (c) 2018, Shaun Dreclin <shaundreclin@gmail.com>
|
||||
* 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.BasicStroke;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Shape;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.client.ui.overlay.Overlay;
|
||||
import net.runelite.client.ui.overlay.OverlayLayer;
|
||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||
|
||||
public class TargetClickboxOverlay extends Overlay
|
||||
{
|
||||
private final SlayerConfig config;
|
||||
private final SlayerPlugin plugin;
|
||||
|
||||
@Inject
|
||||
TargetClickboxOverlay(SlayerConfig config, SlayerPlugin plugin)
|
||||
{
|
||||
this.config = config;
|
||||
this.plugin = plugin;
|
||||
setPosition(OverlayPosition.DYNAMIC);
|
||||
setLayer(OverlayLayer.ABOVE_SCENE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
if (!config.highlightTargets())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
List<NPC> targets = plugin.getHighlightedTargets();
|
||||
for (NPC target : targets)
|
||||
{
|
||||
renderTargetOverlay(graphics, target, config.getTargetColor());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void renderTargetOverlay(Graphics2D graphics, NPC actor, Color color)
|
||||
{
|
||||
Shape objectClickbox = actor.getConvexHull();
|
||||
if (objectClickbox != null)
|
||||
{
|
||||
graphics.setColor(color);
|
||||
graphics.setStroke(new BasicStroke(2));
|
||||
graphics.draw(objectClickbox);
|
||||
graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 20));
|
||||
graphics.fill(objectClickbox);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2018, James Swindle <wilingua@gmail.com>
|
||||
* Copyright (c) 2018, Adam <Adam@sigterm.info>
|
||||
* Copyright (c) 2018, Shaun Dreclin <shaundreclin@gmail.com>
|
||||
* 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.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.api.Point;
|
||||
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;
|
||||
|
||||
public class TargetMinimapOverlay extends Overlay
|
||||
{
|
||||
private final SlayerConfig config;
|
||||
private final SlayerPlugin plugin;
|
||||
|
||||
@Inject
|
||||
TargetMinimapOverlay(SlayerConfig config, SlayerPlugin plugin)
|
||||
{
|
||||
this.config = config;
|
||||
this.plugin = plugin;
|
||||
setPosition(OverlayPosition.DYNAMIC);
|
||||
setLayer(OverlayLayer.ABOVE_WIDGETS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
if (!config.highlightTargets())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
List<NPC> targets = plugin.getHighlightedTargets();
|
||||
for (NPC target : targets)
|
||||
{
|
||||
renderTargetOverlay(graphics, target, config.getTargetColor());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void renderTargetOverlay(Graphics2D graphics, NPC actor, Color color)
|
||||
{
|
||||
Point minimapLocation = actor.getMinimapLocation();
|
||||
if (minimapLocation != null)
|
||||
{
|
||||
OverlayUtil.renderMinimapLocation(graphics, minimapLocation, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Sam "Berry" Beresford <seb1g13@soton.ac.uk>
|
||||
* 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;
|
||||
|
||||
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.UNDER_WIDGETS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
final List<NPC> targets = plugin.getHighlightedTargets();
|
||||
|
||||
if (targets.isEmpty() || !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;
|
||||
}
|
||||
|
||||
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.getHealthScale();
|
||||
final int healthRatio = target.getHealthRatio();
|
||||
final Integer maxHealth = npcManager.getHealth(target.getId());
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Tyler <https://github.com/tylerthardy>
|
||||
* Copyright (c) 2018, Shaun Dreclin <shaundreclin@gmail.com>
|
||||
* 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 com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.ItemID;
|
||||
|
||||
@Getter
|
||||
enum Task
|
||||
{
|
||||
//<editor-fold desc="Enums">
|
||||
ABERRANT_SPECTRES("Aberrant spectres", ItemID.ABERRANT_SPECTRE, "Spectre"),
|
||||
ABYSSAL_DEMONS("Abyssal demons", ItemID.ABYSSAL_DEMON),
|
||||
ABYSSAL_SIRE("Abyssal Sire", ItemID.ABYSSAL_ORPHAN),
|
||||
ADAMANT_DRAGONS("Adamant dragons", ItemID.ADAMANT_DRAGON_MASK),
|
||||
ALCHEMICAL_HYDRA("Alchemical Hydra", ItemID.IKKLE_HYDRA),
|
||||
ANKOU("Ankou", ItemID.ANKOU_MASK),
|
||||
AVIANSIES("Aviansies", ItemID.ENSOULED_AVIANSIE_HEAD),
|
||||
BANDITS("Bandits", ItemID.BANDIT, "Bandit", "Black Heather", "Donny the Lad", "Speedy Keith"),
|
||||
BANSHEES("Banshees", ItemID.BANSHEE),
|
||||
BARROWS_BROTHERS("Barrows Brothers", ItemID.KARILS_COIF),
|
||||
BASILISKS("Basilisks", ItemID.BASILISK),
|
||||
BATS("Bats", ItemID.GIRAL_BAT_2, "Death wing"),
|
||||
BEARS("Bears", ItemID.ENSOULED_BEAR_HEAD),
|
||||
BIRDS("Birds", ItemID.FEATHER, "Chicken", "Rooster", "Terrorbird", "Seagull", "Vulture"),
|
||||
BLACK_DEMONS("Black demons", ItemID.BLACK_DEMON_MASK),
|
||||
BLACK_DRAGONS("Black dragons", ItemID.BLACK_DRAGON_MASK, "Baby black dragon"),
|
||||
BLACK_KNIGHTS("Black Knights", ItemID.BLACK_FULL_HELM, "Black Knight"),
|
||||
BLOODVELD("Bloodveld", ItemID.BLOODVELD),
|
||||
BLUE_DRAGONS("Blue dragons", ItemID.BLUE_DRAGON_MASK, "Baby blue dragon"),
|
||||
BRINE_RATS("Brine rats", ItemID.BRINE_RAT),
|
||||
BRONZE_DRAGONS("Bronze dragons", ItemID.BRONZE_DRAGON_MASK),
|
||||
CALLISTO("Callisto", ItemID.CALLISTO_CUB),
|
||||
CATABLEPON("Catablepon", ItemID.LEFT_SKULL_HALF),
|
||||
CAVE_BUGS("Cave bugs", ItemID.SWAMP_CAVE_BUG),
|
||||
CAVE_CRAWLERS("Cave crawlers", ItemID.CAVE_CRAWLER, "Chasm crawler"),
|
||||
CAVE_HORRORS("Cave horrors", ItemID.CAVE_HORROR, "Cave abomination"),
|
||||
CAVE_KRAKEN("Cave kraken", ItemID.CAVE_KRAKEN),
|
||||
CAVE_SLIMES("Cave slimes", ItemID.SWAMP_CAVE_SLIME),
|
||||
CERBERUS("Cerberus", ItemID.HELLPUPPY),
|
||||
CHAOS_DRUIDS("Chaos druids", ItemID.ELDER_CHAOS_HOOD, "Elder Chaos druid", "Chaos druid"),
|
||||
CHAOS_ELEMENTAL("Chaos Elemental", ItemID.PET_CHAOS_ELEMENTAL),
|
||||
CHAOS_FANATIC("Chaos Fanatic", ItemID.ANCIENT_STAFF),
|
||||
COCKATRICE("Cockatrice", ItemID.COCKATRICE, "Cockathrice"),
|
||||
COWS("Cows", ItemID.COW_MASK),
|
||||
CRAWLING_HANDS("Crawling hands", ItemID.CRAWLING_HAND, "Crushing hand"),
|
||||
CRAZY_ARCHAEOLOGIST("Crazy Archaeologists", ItemID.FEDORA, "Crazy Archaeologist"),
|
||||
CROCODILES("Crocodiles", ItemID.SWAMP_LIZARD),
|
||||
DAGANNOTH("Dagannoth", ItemID.DAGANNOTH),
|
||||
DAGANNOTH_KINGS("Dagannoth Kings", ItemID.PET_DAGANNOTH_PRIME),
|
||||
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),
|
||||
DOGS("Dogs", ItemID.GUARD_DOG, "Jackal"),
|
||||
DRAKES("Drakes", ItemID.DRAKE),
|
||||
DUST_DEVILS("Dust devils", ItemID.DUST_DEVIL, "Choke devil"),
|
||||
DWARVES("Dwarves", ItemID.DWARVEN_HELMET, "Dwarf", "Black Guard"),
|
||||
EARTH_WARRIORS("Earth warriors", ItemID.BRONZE_FULL_HELM_T),
|
||||
ELVES("Elves", ItemID.ELF, "Elf", "Iorwerth Warrior", "Iorwerth Archer"),
|
||||
ENTS("Ents", ItemID.NICE_TREE, "Ent"),
|
||||
FEVER_SPIDERS("Fever spiders", ItemID.FEVER_SPIDER),
|
||||
FIRE_GIANTS("Fire giants", ItemID.FIRE_BATTLESTAFF),
|
||||
FLESH_CRAWLERS("Fleshcrawlers", ItemID.ENSOULED_SCORPION_HEAD, "Flesh crawler"),
|
||||
FOSSIL_ISLAND_WYVERNS("Fossil island wyverns", ItemID.FOSSIL_ISLAND_WYVERN, "Ancient wyvern", "Long-tailed wyvern", "Spitting wyvern", "Taloned wyvern"),
|
||||
GARGOYLES("Gargoyles", ItemID.GARGOYLE, 9, ItemID.ROCK_HAMMER),
|
||||
GENERAL_GRAARDOR("General Graardor", ItemID.PET_GENERAL_GRAARDOR),
|
||||
GHOSTS("Ghosts", ItemID.GHOSTSPEAK_AMULET, "Death wing", "Tortured soul"),
|
||||
GHOULS("Ghouls", ItemID.ZOMBIE_HEAD),
|
||||
GIANT_MOLE("Giant Mole", ItemID.BABY_MOLE),
|
||||
GOBLINS("Goblins", ItemID.ENSOULED_GOBLIN_HEAD),
|
||||
GREATER_DEMONS("Greater demons", ItemID.GREATER_DEMON_MASK),
|
||||
GREEN_DRAGONS("Green dragons", ItemID.GREEN_DRAGON_MASK, "Baby green dragon", "Elvarg"),
|
||||
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, "Cyclops"),
|
||||
HOBGOBLINS("Hobgoblins", ItemID.HOBGOBLIN_GUARD),
|
||||
HYDRAS("Hydras", ItemID.HYDRA),
|
||||
ICEFIENDS("Icefiends", ItemID.ICE_DIAMOND),
|
||||
ICE_GIANTS("Ice giants", ItemID.ICE_DIAMOND),
|
||||
ICE_WARRIORS("Ice warriors", ItemID.MITHRIL_FULL_HELM_T, "Icelord"),
|
||||
INFERNAL_MAGES("Infernal mages", ItemID.INFERNAL_MAGE, "Malevolent mage"),
|
||||
IRON_DRAGONS("Iron dragons", ItemID.IRON_DRAGON_MASK),
|
||||
JAD("TzTok-Jad", ItemID.TZREKJAD, 25250),
|
||||
JELLIES("Jellies", ItemID.JELLY, "Jelly"),
|
||||
JUNGLE_HORROR("Jungle horrors", ItemID.ENSOULED_HORROR_HEAD),
|
||||
KALPHITE("Kalphite", ItemID.KALPHITE_SOLDIER),
|
||||
KALPHITE_QUEEN("Kalphite Queen", ItemID.KALPHITE_PRINCESS),
|
||||
KILLERWATTS("Killerwatts", ItemID.KILLERWATT),
|
||||
KING_BLACK_DRAGON("King Black Dragon", ItemID.PRINCE_BLACK_DRAGON),
|
||||
KRAKEN("Cave Kraken Boss", ItemID.PET_KRAKEN, "Kraken"),
|
||||
KREEARRA("Kree'arra", ItemID.PET_KREEARRA),
|
||||
KRIL_TSUTSAROTH("K'ril Tsutsaroth", ItemID.PET_KRIL_TSUTSAROTH),
|
||||
KURASK("Kurask", ItemID.KURASK),
|
||||
LAVA_DRAGONS("Lava Dragons", ItemID.LAVA_SCALE, "Lava dragon"),
|
||||
LESSER_DEMONS("Lesser demons", ItemID.LESSER_DEMON_MASK),
|
||||
LIZARDMEN("Lizardmen", ItemID.LIZARDMAN_FANG, "Lizardman"),
|
||||
LIZARDS("Lizards", ItemID.DESERT_LIZARD, "Desert lizard", "Sulphur lizard", "Small lizard", "Lizard"),
|
||||
MAGIC_AXES("Magic axes", ItemID.IRON_BATTLEAXE, "Magic axe"),
|
||||
MAMMOTHS("Mammoths", ItemID.ATTACKER_HORN, "Mammoth"),
|
||||
MINIONS_OF_SCABARAS("Minions of scabaras", ItemID.GOLDEN_SCARAB, "Scarab swarm", "Locust rider", "Scarab mage"),
|
||||
MINOTAURS("Minotaurs", ItemID.ENSOULED_MINOTAUR_HEAD),
|
||||
MITHRIL_DRAGONS("Mithril dragons", ItemID.MITHRIL_DRAGON_MASK),
|
||||
MOGRES("Mogres", ItemID.MOGRE),
|
||||
MOLANISKS("Molanisks", ItemID.MOLANISK),
|
||||
MONKEYS("Monkeys", ItemID.ENSOULED_MONKEY_HEAD, "Tortured gorilla"),
|
||||
MOSS_GIANTS("Moss giants", ItemID.HILL_GIANT_CLUB),
|
||||
MUTATED_ZYGOMITES("Mutated zygomites", ItemID.MUTATED_ZYGOMITE, 7, ItemID.FUNGICIDE_SPRAY_0, "Zygomite", "Fungi"),
|
||||
NECHRYAEL("Nechryael", ItemID.NECHRYAEL, "Nechryarch"),
|
||||
OGRES("Ogres", ItemID.ENSOULED_OGRE_HEAD),
|
||||
OTHERWORLDLY_BEING("Otherworldly beings", ItemID.GHOSTLY_HOOD),
|
||||
PIRATES("Pirates", ItemID.PIRATE_HAT, "Pirate"),
|
||||
PYREFIENDS("Pyrefiends", ItemID.PYREFIEND, "Flaming pyrelord"),
|
||||
RATS("Rats", ItemID.RATS_TAIL),
|
||||
RED_DRAGONS("Red dragons", ItemID.BABY_RED_DRAGON, "Baby red dragon"),
|
||||
REVENANTS("Revenants", ItemID.BRACELET_OF_ETHEREUM, "Revenant imp", "Revenant goblin", "Revenant pyrefiend", "Revenant hobgoblin", "Revenant cyclops", "Revenant hellhound", "Revenant demon", "Revenant ork", "Revenant dark beast", "Revenant knight", "Revenant dragon"),
|
||||
ROCKSLUGS("Rockslugs", ItemID.ROCKSLUG, 4, ItemID.BAG_OF_SALT),
|
||||
ROGUES("Rogues", ItemID.ROGUE_MASK, "Rogue"),
|
||||
RUNE_DRAGONS("Rune dragons", ItemID.RUNE_DRAGON_MASK),
|
||||
SARACHNIS("Sarachnis", ItemID.SRARACHA),
|
||||
SCORPIA("Scorpia", ItemID.SCORPIAS_OFFSPRING),
|
||||
SCORPIONS("Scorpions", ItemID.ENSOULED_SCORPION_HEAD),
|
||||
SEA_SNAKES("Sea snakes", ItemID.SNAKE_CORPSE),
|
||||
SHADES("Shades", ItemID.SHADE_ROBE_TOP, "Loar Shadow", "Loar Shade", "Phrin Shadow", "Phrin Shade", "Riyl Shadow", "Riyl Shade", "Asyn Shadow", "Asyn Shade", "Fiyr Shadow", "Fiyr Shade"),
|
||||
SHADOW_WARRIORS("Shadow warriors", ItemID.BLACK_FULL_HELM),
|
||||
SKELETAL_WYVERNS("Skeletal wyverns", ItemID.SKELETAL_WYVERN),
|
||||
SKELETONS("Skeletons", ItemID.SKELETON_GUARD),
|
||||
SMOKE_DEVILS("Smoke devils", ItemID.SMOKE_DEVIL),
|
||||
SOURHOGS("Sourhogs", ItemID.SOURHOG_FOOT),
|
||||
SPIDERS("Spiders", ItemID.HUGE_SPIDER),
|
||||
SPIRITUAL_CREATURES("Spiritual creatures", ItemID.DRAGON_BOOTS, "Spiritual ranger", "Spiritual mage", "Spiritual warrior"),
|
||||
STEEL_DRAGONS("Steel dragons", ItemID.STEEL_DRAGON),
|
||||
SULPHUR_LIZARDS("Sulphur Lizards", ItemID.SULPHUR_LIZARD),
|
||||
SUQAHS("Suqahs", ItemID.SUQAH_TOOTH),
|
||||
TEMPLE_SPIDERS("Temple Spiders", ItemID.RED_SPIDERS_EGGS),
|
||||
TERROR_DOGS("Terror dogs", ItemID.TERROR_DOG),
|
||||
THERMONUCLEAR_SMOKE_DEVIL("Thermonuclear Smoke Devil", ItemID.PET_SMOKE_DEVIL),
|
||||
TROLLS("Trolls", ItemID.TROLL_GUARD, "Dad", "Arrg"),
|
||||
TUROTH("Turoth", ItemID.TUROTH),
|
||||
TZHAAR("Tzhaar", ItemID.ENSOULED_TZHAAR_HEAD),
|
||||
UNDEAD_DRUIDS("Undead Druids", ItemID.MASK_OF_RANUL),
|
||||
VAMPYRES("Vampyres", ItemID.STAKE, "Vyrewatch", "Vampire"),
|
||||
VENENATIS("Venenatis", ItemID.VENENATIS_SPIDERLING),
|
||||
VETION("Vet'ion", ItemID.VETION_JR),
|
||||
VORKATH("Vorkath", ItemID.VORKI),
|
||||
WALL_BEASTS("Wall beasts", ItemID.SWAMP_WALLBEAST),
|
||||
WATERFIENDS("Waterfiends", ItemID.WATER_ORB),
|
||||
WEREWOLVES("Werewolves", ItemID.WOLFBANE, "Werewolf"),
|
||||
WOLVES("Wolves", ItemID.GREY_WOLF_FUR, "Wolf"),
|
||||
WYRMS("Wyrms", ItemID.WYRM),
|
||||
ZILYANA("Commander Zilyana", ItemID.PET_ZILYANA),
|
||||
ZOMBIES("Zombies", ItemID.ZOMBIE_HEAD, "Undead"),
|
||||
ZUK("TzKal-Zuk", ItemID.TZREKZUK, 101890),
|
||||
ZULRAH("Zulrah", ItemID.PET_SNAKELING);
|
||||
//</editor-fold>
|
||||
|
||||
private static final Map<String, Task> tasks;
|
||||
static final List<String> LOCATIONS = ImmutableList.of(
|
||||
"", // no location is a valid location
|
||||
"Abyss",
|
||||
"Ancient Cavern",
|
||||
"Asgarnian Ice Dungeon",
|
||||
"Battlefront",
|
||||
"Brimhaven Dungeon",
|
||||
"Brine Rat Cavern",
|
||||
"Catacombs of Kourend",
|
||||
"Chasm of Fire",
|
||||
"Clan Wars",
|
||||
"Death Plateau",
|
||||
"Evil Chicken's Lair",
|
||||
"Fossil Island",
|
||||
"Forthos Dungeon",
|
||||
"Fremennik Slayer Dungeon",
|
||||
"God Wars Dungeon",
|
||||
"Iorwerth Dungeon",
|
||||
"Jormungand's Prison",
|
||||
"Kalphite Lair",
|
||||
"Karuulm Slayer Dungeon",
|
||||
"Keldagrim",
|
||||
"Kraken Cove",
|
||||
"Lighthouse",
|
||||
"Lithkren Vault",
|
||||
"Lizardman Canyon",
|
||||
"Lizardman Settlement",
|
||||
"Meiyerditch Laboratories",
|
||||
"Molch",
|
||||
"Mount Quidamortem",
|
||||
"Mourner Tunnels",
|
||||
"Myths' Guild Dungeon",
|
||||
"Ogre Enclave",
|
||||
"Slayer Tower",
|
||||
"Smoke Devil Dungeon",
|
||||
"Smoke Dungeon",
|
||||
"Stronghold of Security",
|
||||
"Stronghold Slayer Dungeon",
|
||||
"task-only Kalphite Cave",
|
||||
"Taverley Dungeon",
|
||||
"Troll Stronghold",
|
||||
"Waterbirth Island",
|
||||
"Waterfall Dungeon",
|
||||
"Wilderness",
|
||||
"Witchaven Dungeon",
|
||||
"Zanaris"
|
||||
);
|
||||
|
||||
private final String name;
|
||||
private final int itemSpriteId;
|
||||
private final String[] targetNames;
|
||||
private final int weaknessThreshold;
|
||||
private final int weaknessItem;
|
||||
private final int expectedKillExp;
|
||||
|
||||
static
|
||||
{
|
||||
ImmutableMap.Builder<String, Task> builder = new ImmutableMap.Builder<>();
|
||||
|
||||
for (Task task : values())
|
||||
{
|
||||
builder.put(task.getName().toLowerCase(), task);
|
||||
}
|
||||
|
||||
tasks = builder.build();
|
||||
}
|
||||
|
||||
Task(String name, int itemSpriteId, String... targetNames)
|
||||
{
|
||||
Preconditions.checkArgument(itemSpriteId >= 0);
|
||||
this.name = name;
|
||||
this.itemSpriteId = itemSpriteId;
|
||||
this.weaknessThreshold = -1;
|
||||
this.weaknessItem = -1;
|
||||
this.targetNames = targetNames;
|
||||
this.expectedKillExp = 0;
|
||||
}
|
||||
|
||||
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;
|
||||
this.expectedKillExp = 0;
|
||||
}
|
||||
|
||||
Task(String name, int itemSpriteId, int expectedKillExp)
|
||||
{
|
||||
Preconditions.checkArgument(itemSpriteId >= 0);
|
||||
this.name = name;
|
||||
this.itemSpriteId = itemSpriteId;
|
||||
this.weaknessThreshold = -1;
|
||||
this.weaknessItem = -1;
|
||||
this.targetNames = new String[0];
|
||||
this.expectedKillExp = expectedKillExp;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static Task getTask(String taskName)
|
||||
{
|
||||
return tasks.get(taskName.toLowerCase());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Tyler <https://github.com/tylerthardy>
|
||||
* 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 net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.ui.overlay.infobox.Counter;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
class TaskCounter extends Counter
|
||||
{
|
||||
TaskCounter(BufferedImage img, Plugin plugin, int amount)
|
||||
{
|
||||
super(img, plugin, amount);
|
||||
}
|
||||
}
|
||||
@@ -148,7 +148,7 @@ public abstract class RSNPCMixin implements RSNPC
|
||||
|
||||
@Inject
|
||||
@Override
|
||||
public NPCComposition getTransformedDefinition()
|
||||
public NPCComposition getTransformedComposition()
|
||||
{
|
||||
RSNPCComposition composition = getComposition();
|
||||
if (composition != null && composition.getConfigs() != null)
|
||||
|
||||
Reference in New Issue
Block a user