Merge pull request #2022 from Owain94/externals-rework

project: Rework externals
This commit is contained in:
Owain van Brakel
2020-02-09 12:26:56 +01:00
committed by GitHub
1946 changed files with 2078 additions and 237921 deletions
+5 -1
View File
@@ -27,7 +27,7 @@ object ProjectVersions {
const val launcherVersion = "2.0.4"
const val rlVersion = "1.6.6-SNAPSHOT"
const val openosrsVersion = "2.1.26-SNAPSHOT"
const val openosrsVersion = "3.0.0"
const val rsversion = 188
const val cacheversion = 165
@@ -80,6 +80,8 @@ object Libraries {
const val okhttp3 = "4.3.0"
const val orangeExtensions = "1.0"
const val petitparser = "2.3.1"
const val pf4j = "3.2.0"
const val pf4jUpdate = "2.2.0"
const val plexus = "3.3.0"
const val radiance = "2.5.1"
const val rxjava = "2.2.16"
@@ -139,6 +141,8 @@ object Libraries {
const val okhttp3Webserver = "com.squareup.okhttp3:mockwebserver:${Versions.okhttp3}"
const val orangeExtensions = "net.runelite:orange-extensions:${Versions.orangeExtensions}"
const val petitparser = "com.github.petitparser:java-petitparser:${Versions.petitparser}"
const val pf4j = "org.pf4j:pf4j:${Versions.pf4j}"
const val pf4jUpdate = "org.pf4j:pf4j-update:${Versions.pf4jUpdate}"
const val plexus = "org.codehaus.plexus:plexus-utils:${Versions.plexus}"
const val rxjava = "io.reactivex.rxjava2:rxjava:${Versions.rxjava}"
const val rxrelay = "com.jakewharton.rxrelay2:rxrelay:${Versions.rxrelay}"
Vendored Executable → Regular
View File
+16 -4
View File
@@ -25,7 +25,7 @@
import org.apache.tools.ant.filters.ReplaceTokens
import java.text.SimpleDateFormat
import java.util.*
import java.util.Date
plugins {
id(Plugins.shadow.first) version Plugins.shadow.second
@@ -39,6 +39,7 @@ description = "RuneLite Client"
dependencies {
annotationProcessor(Libraries.lombok)
annotationProcessor(Libraries.pf4j)
compileOnly(Libraries.javax)
compileOnly(Libraries.lombok)
@@ -58,7 +59,6 @@ dependencies {
implementation(Libraries.substance)
implementation(Libraries.jopt)
implementation(Libraries.apacheCommonsText)
implementation(Libraries.plexus)
implementation(Libraries.annotations)
implementation(Libraries.jogampGluegen)
implementation(Libraries.jogampJogl)
@@ -67,6 +67,10 @@ dependencies {
implementation(Libraries.jooqMeta)
implementation(Libraries.sentry)
implementation(Libraries.slf4jApi)
implementation(Libraries.pf4j) {
exclude(group = "org.slf4j")
}
implementation(Libraries.pf4jUpdate)
implementation(project(":http-api"))
api(project(":runelite-api"))
implementation(Libraries.naturalMouse)
@@ -108,6 +112,14 @@ fun launcherVersion(): String {
return "-1"
}
fun pluginPath(): String {
if (project.hasProperty("pluginPath")) {
print(project.property("pluginPath").toString())
return project.property("pluginPath").toString()
}
return ""
}
tasks {
build {
finalizedBy("shadowJar")
@@ -123,7 +135,8 @@ tasks {
"rs.version" to ProjectVersions.rsversion.toString(),
"open.osrs.version" to ProjectVersions.openosrsVersion,
"open.osrs.builddate" to formatDate(Date()),
"launcher.version" to launcherVersion()
"launcher.version" to launcherVersion(),
"plugin.path" to pluginPath()
)
inputs.properties(tokens)
@@ -151,7 +164,6 @@ tasks {
group = "openosrs"
}
register<JavaExec>("RuneLite.main()") {
group = "openosrs"
@@ -69,6 +69,7 @@ import net.runelite.client.game.XpDropManager;
import net.runelite.client.game.chatbox.ChatboxPanelManager;
import net.runelite.client.graphics.ModelOutlineRenderer;
import net.runelite.client.menus.MenuManager;
import net.runelite.client.plugins.ExternalPluginManager;
import net.runelite.client.plugins.PluginManager;
import net.runelite.client.rs.ClientLoader;
import net.runelite.client.rs.ClientUpdateCheckMode;
@@ -93,10 +94,13 @@ import org.slf4j.LoggerFactory;
@Slf4j
public class RuneLite
{
public static final String SYSTEM_VERSION = "0.0.1";
public static final File RUNELITE_DIR = new File(System.getProperty("user.home"), ".runelite");
public static final File CACHE_DIR = new File(RUNELITE_DIR, "cache");
public static final File PROFILES_DIR = new File(RUNELITE_DIR, "profiles");
public static final File PLUGIN_DIR = new File(RUNELITE_DIR, "plugins");
public static final File EXTERNALPLUGIN_DIR = new File(RUNELITE_DIR, "externalmanager");
public static final File SCREENSHOT_DIR = new File(RUNELITE_DIR, "screenshots");
public static final File LOGS_DIR = new File(RUNELITE_DIR, "logs");
public static final File PLUGINS_DIR = new File(RUNELITE_DIR, "plugins");
@@ -105,16 +109,24 @@ public class RuneLite
@Getter
private static Injector injector;
@Inject
public DiscordService discordService;
@Inject
private WorldService worldService;
@Inject
private PluginManager pluginManager;
@Inject
private ExternalPluginManager externalPluginManager;
@Inject
private ConfigManager configManager;
@Inject
private SessionManager sessionManager;
@Inject
private ClientSessionManager clientSessionManager;
@@ -351,25 +363,34 @@ public class RuneLite
// Tell the plugin manager if client is outdated or not
pluginManager.setOutdated(isOutdated);
// Load external plugins
pluginManager.loadExternalPlugins();
// Initialize UI
RuneLiteSplashScreen.stage(.60, "Initialize UI");
clientUI.init(this);
// Load the plugins, but does not start them yet.
// This will initialize configuration
pluginManager.loadCorePlugins();
RuneLiteSplashScreen.stage(.70, "Finalizing configuration");
// Load external plugins
externalPluginManager.startExternalPluginManager();
RuneLiteSplashScreen.stage(.75, "Finalizing configuration");
// Plugins have provided their config, so set default config
// to main settings
pluginManager.loadDefaultPluginConfiguration();
// Start client session
RuneLiteSplashScreen.stage(.75, "Starting core interface");
clientSessionManager.start();
externalPluginManager.startExternalUpdateManager();
// Initialize UI
RuneLiteSplashScreen.stage(.80, "Initialize UI");
clientUI.init(this);
RuneLiteSplashScreen.stage(.77, "Updating external plugins");
externalPluginManager.update();
// Load external plugins
pluginManager.loadExternalPlugins();
// Start client session
RuneLiteSplashScreen.stage(.80, "Starting core interface");
clientSessionManager.start();
//Set the world if specified via CLI args - will not work until clientUI.init is called
Optional<Integer> worldArg = Optional.ofNullable(System.getProperty("cli.world")).map(Integer::parseInt);
@@ -409,6 +430,7 @@ public class RuneLite
// Start plugins
pluginManager.startCorePlugins();
externalPluginManager.loadPlugins();
// Register additional schedulers
if (this.client != null)
@@ -42,6 +42,7 @@ public class RuneLiteProperties
private static final String WIKI_LINK = "runelite.wiki.link";
private static final String PATREON_LINK = "runelite.patreon.link";
private static final String LAUNCHER_VERSION_PROPERTY = "launcher.version";
private static final String PLUGIN_PATH = "plugin.path";
private static final String TROUBLESHOOTING_LINK = "runelite.wiki.troubleshooting.link";
private static final String BUILDING_LINK = "runelite.wiki.building.link";
private static final String DNS_CHANGE_LINK = "runelite.dnschange.link";
@@ -139,6 +140,13 @@ public class RuneLiteProperties
return launcherVersion.equals("-1") ? null : launcherVersion;
}
@Nullable
public static String getPluginPath()
{
String pluginPath = properties.getProperty(PLUGIN_PATH);
return pluginPath.equals("") ? null : pluginPath;
}
public static String getImgurClientId()
{
return properties.getProperty(IMGUR_CLIENT_ID);
@@ -45,6 +45,7 @@ import java.nio.channels.FileLock;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
@@ -63,6 +64,8 @@ import net.runelite.client.RuneLite;
import static net.runelite.client.RuneLite.PROFILES_DIR;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.plugins.ExternalPluginManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.util.ColorUtil;
import org.apache.commons.lang3.StringUtils;
@@ -74,13 +77,11 @@ public class ConfigManager
private static final String STANDARD_SETTINGS_FILE_NAME = "settings.properties";
private static final File SETTINGS_FILE = new File(RuneLite.RUNELITE_DIR, SETTINGS_FILE_NAME);
private static final File STANDARD_SETTINGS_FILE = new File(RuneLite.RUNELITE_DIR, STANDARD_SETTINGS_FILE_NAME);
@Inject
EventBus eventBus;
private final ConfigInvocationHandler handler = new ConfigInvocationHandler(this);
private final Properties properties = new Properties();
private final Map<String, String> pendingChanges = new HashMap<>();
@Inject
EventBus eventBus;
@Inject
public ConfigManager(ScheduledExecutorService scheduledExecutorService)
@@ -88,6 +89,261 @@ public class ConfigManager
scheduledExecutorService.scheduleWithFixedDelay(this::sendConfig, 30, 30, TimeUnit.SECONDS);
}
@SuppressWarnings("unchecked")
static Object stringToObject(String str, Class<?> type)
{
if (type == boolean.class || type == Boolean.class)
{
return Boolean.parseBoolean(str);
}
if (type == int.class)
{
return Integer.parseInt(str);
}
if (type == long.class)
{
return Long.parseLong(str);
}
if (type == Color.class)
{
return ColorUtil.fromString(str);
}
if (type == Dimension.class)
{
String[] splitStr = str.split("x");
int width = Integer.parseInt(splitStr[0]);
int height = Integer.parseInt(splitStr[1]);
return new Dimension(width, height);
}
if (type == Point.class)
{
String[] splitStr = str.split(":");
int width = Integer.parseInt(splitStr[0]);
int height = Integer.parseInt(splitStr[1]);
return new Point(width, height);
}
if (type == Rectangle.class)
{
String[] splitStr = str.split(":");
int x = Integer.parseInt(splitStr[0]);
int y = Integer.parseInt(splitStr[1]);
int width = Integer.parseInt(splitStr[2]);
int height = Integer.parseInt(splitStr[3]);
return new Rectangle(x, y, width, height);
}
if (type.isEnum())
{
return Enum.valueOf((Class<? extends Enum>) type, str);
}
if (type == Instant.class)
{
return Instant.parse(str);
}
if (type == Keybind.class || type == ModifierlessKeybind.class)
{
String[] splitStr = str.split(":");
int code = Integer.parseInt(splitStr[0]);
int mods = Integer.parseInt(splitStr[1]);
if (type == ModifierlessKeybind.class)
{
return new ModifierlessKeybind(code, mods);
}
return new Keybind(code, mods);
}
if (type == WorldPoint.class)
{
String[] splitStr = str.split(":");
int x = Integer.parseInt(splitStr[0]);
int y = Integer.parseInt(splitStr[1]);
int plane = Integer.parseInt(splitStr[2]);
return new WorldPoint(x, y, plane);
}
if (type == Duration.class)
{
return Duration.ofMillis(Long.parseLong(str));
}
if (type == int[].class)
{
if (str.contains(","))
{
return Arrays.stream(str.split(",")).mapToInt(Integer::valueOf).toArray();
}
return new int[]{Integer.parseInt(str)};
}
if (type == EnumSet.class)
{
try
{
String substring = str.substring(str.indexOf("{") + 1, str.length() - 1);
String[] splitStr = substring.split(", ");
Class<? extends Enum> enumClass = null;
if (!str.contains("{"))
{
return null;
}
enumClass = findEnumClass(str, ExternalPluginManager.pluginClassLoaders);
EnumSet enumSet = EnumSet.noneOf(enumClass);
for (String s : splitStr)
{
try
{
enumSet.add(Enum.valueOf(enumClass, s.replace("[", "").replace("]", "")));
}
catch (IllegalArgumentException ignore)
{
return EnumSet.noneOf(enumClass);
}
}
return enumSet;
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
if (type == Map.class)
{
Map<String, String> output = new HashMap<>();
str = str.substring(1, str.length() - 1);
String[] splitStr = str.split(", ");
for (String s : splitStr)
{
String[] keyVal = s.split("=");
if (keyVal.length > 1)
{
output.put(keyVal[0], keyVal[1]);
}
}
return output;
}
return str;
}
static String objectToString(Object object)
{
if (object instanceof Color)
{
return String.valueOf(((Color) object).getRGB());
}
if (object instanceof Enum)
{
return ((Enum) object).name();
}
if (object instanceof Dimension)
{
Dimension d = (Dimension) object;
return d.width + "x" + d.height;
}
if (object instanceof Point)
{
Point p = (Point) object;
return p.x + ":" + p.y;
}
if (object instanceof Rectangle)
{
Rectangle r = (Rectangle) object;
return r.x + ":" + r.y + ":" + r.width + ":" + r.height;
}
if (object instanceof Instant)
{
return ((Instant) object).toString();
}
if (object instanceof Keybind)
{
Keybind k = (Keybind) object;
return k.getKeyCode() + ":" + k.getModifiers();
}
if (object instanceof WorldPoint)
{
WorldPoint wp = (WorldPoint) object;
return wp.getX() + ":" + wp.getY() + ":" + wp.getPlane();
}
if (object instanceof Duration)
{
return Long.toString(((Duration) object).toMillis());
}
if (object instanceof int[])
{
if (((int[]) object).length == 0)
{
return String.valueOf(object);
}
return StringUtils.join(object, ",");
}
if (object instanceof EnumSet)
{
if (((EnumSet) object).size() == 0)
{
return getElementType((EnumSet) object).getCanonicalName() + "{}";
}
return ((EnumSet) object).toArray()[0].getClass().getCanonicalName() + "{" + object.toString() + "}";
}
if (object instanceof Number)
{
return String.valueOf(object);
}
return object.toString();
}
public static <T extends Enum<T>> Class<T> getElementType(EnumSet<T> enumSet)
{
if (enumSet.isEmpty())
{
enumSet = EnumSet.complementOf(enumSet);
}
return enumSet.iterator().next().getDeclaringClass();
}
public static Class<? extends Enum> findEnumClass(String clasz, ArrayList<ClassLoader> classLoaders)
{
StringBuilder transformedString = new StringBuilder();
for (ClassLoader cl : classLoaders)
{
try
{
String[] strings = clasz.substring(0, clasz.indexOf("{")).split("\\.");
int i = 0;
while (i != strings.length)
{
if (i == 0)
{
transformedString.append(strings[i]);
}
else if (i == strings.length - 1)
{
transformedString.append("$").append(strings[i]);
}
else
{
transformedString.append(".").append(strings[i]);
}
i++;
}
return (Class<? extends Enum>) cl.loadClass(transformedString.toString());
}
catch (Exception e2)
{
// Will likely fail a lot
}
try
{
return (Class<? extends Enum>) cl.loadClass(clasz.substring(0, clasz.indexOf("{")));
}
catch (Exception e)
{
// Will likely fail a lot
}
transformedString = new StringBuilder();
}
throw new RuntimeException("Failed to find Enum for " + clasz.substring(0, clasz.indexOf("{")));
}
public final void switchSession()
{
// Ensure existing config is saved
@@ -439,213 +695,81 @@ public class ConfigManager
}
}
@SuppressWarnings("unchecked")
static Object stringToObject(String str, Class<?> type)
/**
* Initialize the configuration from the default settings, for an external Plugin
*
* @param proxy
*/
public void setDefaultConfiguration(Object proxy, boolean override, Plugin plugin)
{
if (type == boolean.class || type == Boolean.class)
Class<?> clazz = proxy.getClass().getInterfaces()[0];
ConfigGroup group = clazz.getAnnotation(ConfigGroup.class);
if (group == null)
{
return Boolean.parseBoolean(str);
return;
}
if (type == int.class)
for (Method method : clazz.getDeclaredMethods())
{
return Integer.parseInt(str);
}
if (type == long.class)
{
return Long.parseLong(str);
}
if (type == Color.class)
{
return ColorUtil.fromString(str);
}
if (type == Dimension.class)
{
String[] splitStr = str.split("x");
int width = Integer.parseInt(splitStr[0]);
int height = Integer.parseInt(splitStr[1]);
return new Dimension(width, height);
}
if (type == Point.class)
{
String[] splitStr = str.split(":");
int width = Integer.parseInt(splitStr[0]);
int height = Integer.parseInt(splitStr[1]);
return new Point(width, height);
}
if (type == Rectangle.class)
{
String[] splitStr = str.split(":");
int x = Integer.parseInt(splitStr[0]);
int y = Integer.parseInt(splitStr[1]);
int width = Integer.parseInt(splitStr[2]);
int height = Integer.parseInt(splitStr[3]);
return new Rectangle(x, y, width, height);
}
if (type.isEnum())
{
return Enum.valueOf((Class<? extends Enum>) type, str);
}
if (type == Instant.class)
{
return Instant.parse(str);
}
if (type == Keybind.class || type == ModifierlessKeybind.class)
{
String[] splitStr = str.split(":");
int code = Integer.parseInt(splitStr[0]);
int mods = Integer.parseInt(splitStr[1]);
if (type == ModifierlessKeybind.class)
ConfigItem item = method.getAnnotation(ConfigItem.class);
// only apply default configuration for methods which read configuration (0 args)
if (item == null || method.getParameterCount() != 0)
{
return new ModifierlessKeybind(code, mods);
continue;
}
return new Keybind(code, mods);
}
if (type == WorldPoint.class)
{
String[] splitStr = str.split(":");
int x = Integer.parseInt(splitStr[0]);
int y = Integer.parseInt(splitStr[1]);
int plane = Integer.parseInt(splitStr[2]);
return new WorldPoint(x, y, plane);
}
if (type == Duration.class)
{
return Duration.ofMillis(Long.parseLong(str));
}
if (type == int[].class)
{
if (str.contains(","))
if (!method.isDefault())
{
return Arrays.stream(str.split(",")).mapToInt(Integer::valueOf).toArray();
if (override)
{
String current = getConfiguration(group.value(), item.keyName());
// only unset if already set
if (current != null)
{
unsetConfiguration(group.value(), item.keyName());
}
}
continue;
}
return new int[]{Integer.parseInt(str)};
}
if (type == EnumSet.class)
{
if (!override)
{
// This checks if it is set and is also unmarshallable to the correct type; so
// we will overwrite invalid config values with the default
Object current = getConfiguration(group.value(), item.keyName(), method.getReturnType());
if (current != null)
{
continue; // something else is already set
}
}
Object defaultValue;
try
{
String substring = str.substring(str.indexOf("{") + 1, str.length() - 1);
String[] splitStr = substring.split(", ");
final Class<? extends Enum> enumClass;
if (!str.contains("{"))
{
return null;
}
enumClass = (Class<? extends Enum>) Class.forName(str.substring(0, str.indexOf("{")));
EnumSet enumSet = EnumSet.noneOf(enumClass);
for (String s : splitStr)
{
try
{
enumSet.add(Enum.valueOf(enumClass, s.replace("[", "").replace("]", "")));
}
catch (IllegalArgumentException ignore)
{
return EnumSet.noneOf(enumClass);
}
}
return enumSet;
defaultValue = ConfigInvocationHandler.callDefaultMethod(proxy, method, null);
}
catch (Exception e)
catch (Throwable ex)
{
e.printStackTrace();
return null;
log.warn(null, ex);
continue;
}
}
if (type == Map.class)
{
Map<String, String> output = new HashMap<>();
str = str.substring(1, str.length() - 1);
String[] splitStr = str.split(", ");
for (String s : splitStr)
String current = getConfiguration(group.value(), item.keyName());
String valueString = objectToString(defaultValue);
// null and the empty string are treated identically in sendConfig and treated as an unset
// If a config value defaults to "" and the current value is null, it will cause an extra
// unset to be sent, so treat them as equal
if (Objects.equals(current, valueString) || (Strings.isNullOrEmpty(current) && Strings.isNullOrEmpty(valueString)))
{
String[] keyVal = s.split("=");
if (keyVal.length > 1)
{
output.put(keyVal[0], keyVal[1]);
}
continue; // already set to the default value
}
return output;
}
return str;
}
log.debug("Setting default configuration value for {}.{} to {}", group.value(), item.keyName(), defaultValue);
static String objectToString(Object object)
{
if (object instanceof Color)
{
return String.valueOf(((Color) object).getRGB());
setConfiguration(group.value(), item.keyName(), valueString);
}
if (object instanceof Enum)
{
return ((Enum) object).name();
}
if (object instanceof Dimension)
{
Dimension d = (Dimension) object;
return d.width + "x" + d.height;
}
if (object instanceof Point)
{
Point p = (Point) object;
return p.x + ":" + p.y;
}
if (object instanceof Rectangle)
{
Rectangle r = (Rectangle) object;
return r.x + ":" + r.y + ":" + r.width + ":" + r.height;
}
if (object instanceof Instant)
{
return ((Instant) object).toString();
}
if (object instanceof Keybind)
{
Keybind k = (Keybind) object;
return k.getKeyCode() + ":" + k.getModifiers();
}
if (object instanceof WorldPoint)
{
WorldPoint wp = (WorldPoint) object;
return wp.getX() + ":" + wp.getY() + ":" + wp.getPlane();
}
if (object instanceof Duration)
{
return Long.toString(((Duration) object).toMillis());
}
if (object instanceof int[])
{
if (((int[]) object).length == 0)
{
return String.valueOf(object);
}
return StringUtils.join(object, ",");
}
if (object instanceof EnumSet)
{
if (((EnumSet) object).size() == 0)
{
return getElementType((EnumSet) object).getCanonicalName() + "{}";
}
return ((EnumSet) object).toArray()[0].getClass().getCanonicalName() + "{" + object.toString() + "}";
}
if (object instanceof Number)
{
return String.valueOf(object);
}
return object.toString();
}
public static <T extends Enum<T>> Class<T> getElementType(EnumSet<T> enumSet)
{
if (enumSet.isEmpty())
{
enumSet = EnumSet.complementOf(enumSet);
}
return enumSet.iterator().next().getDeclaringClass();
}
public void sendConfig()
@@ -281,8 +281,8 @@ public interface OpenOSRSConfig extends Config
@ConfigItem(
keyName = "enablePlugins",
name = "Enable loading of external plugins",
description = "Enable loading of external plugins",
name = "Enable loading of legacy external plugins",
description = "Enable loading of legacy external plugins",
position = 16,
titleSection = "externalPluginsTitle"
)
@@ -365,4 +365,23 @@ public interface OpenOSRSConfig extends Config
{
return Keybind.NOT_SET;
}
@ConfigItem(
keyName = "externalRepositories",
name = "",
description = "",
hidden = true
)
default String getExternalRepositories()
{
return "OpenOSRS:https://raw.githubusercontent.com/open-osrs/plugin-hosting/master/";
}
@ConfigItem(
keyName = "externalRepositories",
name = "",
description = "",
hidden = true
)
void setExternalRepositories(String val);
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, TheStonedTurtle <https://github.com/TheStonedTurtle>
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -22,14 +22,16 @@
* (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.loottracker.localstorage.events;
package net.runelite.client.events;
import lombok.Data;
import net.runelite.api.events.Event;
import net.runelite.client.plugins.loottracker.localstorage.LTRecord;
import net.runelite.client.plugins.Plugin;
@Data
public class LTRecordStored implements Event
public class ExternalPluginChanged implements Event
{
private final LTRecord record;
}
private final String pluginId;
private final Plugin plugin;
private final boolean added;
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, TheStonedTurtle <https://github.com/TheStonedTurtle>
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -22,8 +22,11 @@
* (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.loottracker.localstorage.events;
package net.runelite.client.events;
import lombok.Data;
import net.runelite.api.events.Event;
public class LTNameChange implements Event {}
@Data
public class ExternalPluginsLoaded implements Event
{}
@@ -1,11 +1,10 @@
package net.runelite.client.plugins.menuentryswapper.comparables;
package net.runelite.client.menus;
import lombok.EqualsAndHashCode;
import net.runelite.api.MenuEntry;
import net.runelite.api.util.Text;
import net.runelite.api.widgets.WidgetID;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.menus.AbstractComparableEntry;
import org.apache.commons.lang3.StringUtils;
@EqualsAndHashCode(callSuper = true)
@@ -1,7 +1,6 @@
package net.runelite.client.menus;
import net.runelite.api.Client;
import net.runelite.client.plugins.menuentryswapper.comparables.BankComparableEntry;
public interface ComparableEntries
{
@@ -1,11 +1,10 @@
package net.runelite.client.plugins.menuentryswapper.comparables;
package net.runelite.client.menus;
import lombok.EqualsAndHashCode;
import net.runelite.api.MenuEntry;
import net.runelite.api.util.Text;
import net.runelite.api.widgets.WidgetID;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.menus.AbstractComparableEntry;
import org.apache.commons.lang3.StringUtils;
@EqualsAndHashCode(callSuper = true)
@@ -1,11 +1,10 @@
package net.runelite.client.plugins.menuentryswapper.comparables;
package net.runelite.client.menus;
import lombok.EqualsAndHashCode;
import net.runelite.api.MenuEntry;
import net.runelite.api.util.Text;
import net.runelite.api.widgets.WidgetID;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.menus.AbstractComparableEntry;
import org.apache.commons.lang3.StringUtils;
@EqualsAndHashCode(callSuper = true)
@@ -1,9 +1,8 @@
package net.runelite.client.plugins.menuentryswapper.comparables;
package net.runelite.client.menus;
import java.util.List;
import net.runelite.api.MenuEntry;
import net.runelite.api.util.Text;
import net.runelite.client.menus.AbstractComparableEntry;
public class ShopComparableEntry extends AbstractComparableEntry
{
@@ -1,11 +1,10 @@
package net.runelite.client.plugins.menuentryswapper.comparables;
package net.runelite.client.menus;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import net.runelite.api.MenuEntry;
import net.runelite.api.util.Text;
import net.runelite.client.menus.AbstractComparableEntry;
@EqualsAndHashCode(callSuper = true)
public class WithdrawComparableEntry extends AbstractComparableEntry
@@ -0,0 +1,524 @@
package net.runelite.client.plugins;
import com.google.common.collect.ImmutableList;
import com.google.inject.Binder;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.swing.JOptionPane;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.RuneLite;
import static net.runelite.client.RuneLite.EXTERNALPLUGIN_DIR;
import static net.runelite.client.RuneLite.SYSTEM_VERSION;
import net.runelite.client.RuneLiteProperties;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.config.OpenOSRSConfig;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.events.ExternalPluginChanged;
import net.runelite.client.events.ExternalPluginsLoaded;
import net.runelite.client.ui.RuneLiteSplashScreen;
import net.runelite.client.util.SwingUtil;
import org.pf4j.DefaultPluginManager;
import org.pf4j.DependencyResolver;
import org.pf4j.JarPluginLoader;
import org.pf4j.JarPluginRepository;
import org.pf4j.ManifestPluginDescriptorFinder;
import org.pf4j.PluginDependency;
import org.pf4j.PluginDescriptorFinder;
import org.pf4j.PluginLoader;
import org.pf4j.PluginRepository;
import org.pf4j.PluginWrapper;
import org.pf4j.RuntimeMode;
import org.pf4j.update.DefaultUpdateRepository;
import org.pf4j.update.PluginInfo;
import org.pf4j.update.UpdateManager;
import org.pf4j.update.UpdateRepository;
import org.pf4j.update.VerifyException;
@Slf4j
@Singleton
public
class ExternalPluginManager
{
public static ArrayList<ClassLoader> pluginClassLoaders = new ArrayList<>();
private final PluginManager runelitePluginManager;
private final org.pf4j.PluginManager externalPluginManager;
@Getter(AccessLevel.PUBLIC)
private final List<UpdateRepository> repositories = new ArrayList<>();
private final OpenOSRSConfig openOSRSConfig;
private final ConfigManager configManager;
private final EventBus eventBus;
@Getter(AccessLevel.PUBLIC)
private UpdateManager updateManager;
@Inject
public ExternalPluginManager(
PluginManager pluginManager,
OpenOSRSConfig openOSRSConfig,
ConfigManager configManager,
EventBus eventBus)
{
this.runelitePluginManager = pluginManager;
this.openOSRSConfig = openOSRSConfig;
this.configManager = configManager;
this.eventBus = eventBus;
//noinspection ResultOfMethodCallIgnored
EXTERNALPLUGIN_DIR.mkdirs();
boolean debug = RuneLiteProperties.getLauncherVersion() == null && RuneLiteProperties.getPluginPath() != null;
this.externalPluginManager = new DefaultPluginManager(debug ? Paths.get(RuneLiteProperties.getPluginPath() + File.separator + "release") : EXTERNALPLUGIN_DIR.toPath())
{
@Override
protected PluginDescriptorFinder createPluginDescriptorFinder()
{
return new ManifestPluginDescriptorFinder();
}
@Override
protected PluginRepository createPluginRepository()
{
return new JarPluginRepository(getPluginsRoot());
}
@Override
protected PluginLoader createPluginLoader()
{
return new JarPluginLoader(this);
}
@Override
public RuntimeMode getRuntimeMode()
{
return debug ? RuntimeMode.DEVELOPMENT : RuntimeMode.DEPLOYMENT;
}
};
this.externalPluginManager.setSystemVersion(SYSTEM_VERSION);
}
private static URL toRepositoryUrl(String owner, String name) throws MalformedURLException
{
return new URL("https://raw.githubusercontent.com/" + owner + "/" + name + "/master/");
}
public static boolean testRepository(String owner, String name)
{
final List<UpdateRepository> repositories = new ArrayList<>();
try
{
repositories.add(new DefaultUpdateRepository("github", new URL("https://raw.githubusercontent.com/" + owner + "/" + name + "/master/")));
}
catch (MalformedURLException e)
{
return true;
}
DefaultPluginManager testPluginManager = new DefaultPluginManager(EXTERNALPLUGIN_DIR.toPath());
UpdateManager updateManager = new UpdateManager(testPluginManager, repositories);
return updateManager.getPlugins().size() <= 0;
}
public static <T> Predicate<T> not(Predicate<T> t)
{
return t.negate();
}
public void startExternalPluginManager()
{
this.externalPluginManager.loadPlugins();
}
public void startExternalUpdateManager()
{
try
{
for (String keyval : openOSRSConfig.getExternalRepositories().split(";"))
{
String id = keyval.substring(0, keyval.lastIndexOf(":https"));
String url = keyval.substring(keyval.lastIndexOf("https"));
repositories.add(new DefaultUpdateRepository(id, new URL(url)));
}
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
this.updateManager = new UpdateManager(this.externalPluginManager, repositories);
}
public void addRepository(String owner, String name)
{
try
{
DefaultUpdateRepository respository = new DefaultUpdateRepository(owner + toRepositoryUrl(owner, name), toRepositoryUrl(owner, name));
updateManager.addRepository(respository);
saveConfig();
}
catch (MalformedURLException e)
{
log.error("Repostitory could not be added");
}
}
public void removeRepository(String owner)
{
updateManager.removeRepository(owner);
saveConfig();
}
private void saveConfig()
{
StringBuilder config = new StringBuilder();
for (UpdateRepository repository : updateManager.getRepositories())
{
config.append(repository.getId());
config.append(":");
config.append(repository.getUrl().toString());
config.append(";");
}
config.deleteCharAt(config.lastIndexOf(";"));
openOSRSConfig.setExternalRepositories(config.toString());
}
private void instantiatePlugin(String pluginId, Plugin plugin) throws PluginInstantiationException
{
List<Plugin> scannedPlugins = new ArrayList<>(runelitePluginManager.getPlugins());
Class<? extends Plugin> clazz = plugin.getClass();
PluginDescriptor[] pluginDescriptors = clazz.getAnnotationsByType(PluginDescriptor.class);
for (PluginDescriptor pluginDescriptor : pluginDescriptors)
{
if (pluginDescriptor.type() == PluginType.EXTERNAL)
{
log.error("Class {} is using the the new external plugin loader, it should not use PluginType.EXTERNAL", clazz);
return;
}
}
net.runelite.client.plugins.PluginDependency[] pluginDependencies = clazz.getAnnotationsByType(net.runelite.client.plugins.PluginDependency.class);
List<Plugin> deps = new ArrayList<>();
for (net.runelite.client.plugins.PluginDependency pluginDependency : pluginDependencies)
{
Optional<Plugin> dependency = scannedPlugins.stream().filter(p -> p.getClass() == pluginDependency.value()).findFirst();
if (dependency.isEmpty())
{
throw new PluginInstantiationException("Unmet dependency for " + clazz.getSimpleName() + ": " + pluginDependency.value().getSimpleName());
}
deps.add(dependency.get());
}
Module pluginModule = (Binder binder) ->
{
//noinspection unchecked
binder.bind((Class<Plugin>) plugin.getClass()).toInstance(plugin);
binder.install(plugin);
for (Plugin p : deps)
{
Module p2 = (Binder binder2) ->
{
//noinspection unchecked
binder2.bind((Class<Plugin>) p.getClass()).toInstance(p);
binder2.install(p);
};
binder.install(p2);
}
};
Injector pluginInjector = RuneLite.getInjector().createChildInjector(pluginModule);
pluginInjector.injectMembers(plugin);
plugin.injector = pluginInjector;
// Initialize default configuration
Injector injector = plugin.getInjector();
for (Key<?> key : injector.getAllBindings().keySet())
{
Class<?> type = key.getTypeLiteral().getRawType();
if (Config.class.isAssignableFrom(type))
{
Config config = (Config) injector.getInstance(key);
configManager.setDefaultConfiguration(config, false);
}
}
try
{
runelitePluginManager.startPlugin(plugin);
}
catch (PluginInstantiationException ex)
{
log.warn("unable to start plugin", ex);
return;
}
runelitePluginManager.add(plugin);
eventBus.post(ExternalPluginChanged.class, new ExternalPluginChanged(pluginId, plugin, true));
}
public void loadPlugins()
{
this.externalPluginManager.startPlugins();
List<PluginWrapper> startedPlugins = getStartedPlugins();
int index = 1;
for (PluginWrapper plugin : startedPlugins)
{
RuneLiteSplashScreen.stage(.90, 1, "Starting external plugins", index++, startedPlugins.size());
loadPlugin(plugin.getPluginId());
}
eventBus.post(ExternalPluginsLoaded.class, new ExternalPluginsLoaded());
}
private void loadPlugin(String pluginId)
{
try
{
List<Plugin> extensions = externalPluginManager.getExtensions(Plugin.class, pluginId);
for (Plugin plugin : extensions)
{
try
{
pluginClassLoaders.add(plugin.getClass().getClassLoader());
instantiatePlugin(pluginId, plugin);
}
catch (PluginInstantiationException e)
{
log.warn("Error instantiating plugin!", e);
return;
}
}
}
catch (NoClassDefFoundError ex)
{
try
{
SwingUtil.syncExec(() ->
JOptionPane.showMessageDialog(null,
pluginId + " could not be loaded due to the following error: " + ex.getMessage(),
"External plugin error",
JOptionPane.ERROR_MESSAGE));
}
catch (InvocationTargetException | InterruptedException ignored)
{
}
}
}
private void stopPlugins()
{
List<PluginWrapper> startedPlugins = ImmutableList.copyOf(getStartedPlugins());
for (PluginWrapper pluginWrapper : startedPlugins)
{
String pluginId = pluginWrapper.getDescriptor().getPluginId();
List<Plugin> extensions = externalPluginManager.getExtensions(Plugin.class, pluginId);
for (Plugin plugin : runelitePluginManager.getPlugins())
{
if (!extensions.get(0).getClass().getName().equals(plugin.getClass().getName()))
{
continue;
}
try
{
runelitePluginManager.stopPlugin(plugin);
runelitePluginManager.remove(plugin);
eventBus.post(ExternalPluginChanged.class, new ExternalPluginChanged(pluginId, plugin, false));
}
catch (PluginInstantiationException ex)
{
log.warn("unable to stop plugin", ex);
return;
}
}
}
}
private Path stopPlugin(String pluginId)
{
List<PluginWrapper> startedPlugins = ImmutableList.copyOf(getStartedPlugins());
for (PluginWrapper pluginWrapper : startedPlugins)
{
if (!pluginId.equals(pluginWrapper.getDescriptor().getPluginId()))
{
continue;
}
List<Plugin> extensions = externalPluginManager.getExtensions(Plugin.class, pluginId);
for (Plugin plugin : runelitePluginManager.getPlugins())
{
if (!extensions.get(0).getClass().getName().equals(plugin.getClass().getName()))
{
continue;
}
try
{
runelitePluginManager.stopPlugin(plugin);
runelitePluginManager.remove(plugin);
eventBus.post(ExternalPluginChanged.class, new ExternalPluginChanged(pluginId, plugin, false));
return pluginWrapper.getPluginPath();
}
catch (PluginInstantiationException ex)
{
log.warn("unable to stop plugin", ex);
return null;
}
}
}
return null;
}
public void install(String pluginId) throws VerifyException
{
if (getDisabledPlugins().contains(pluginId))
{
this.externalPluginManager.enablePlugin(pluginId);
this.externalPluginManager.startPlugin(pluginId);
loadPlugin(pluginId);
return;
}
if (getStartedPlugins().stream().anyMatch(ev -> ev.getPluginId().equals(pluginId)))
{
return;
}
// Null version returns the last release version of this plugin for given system version
try
{
PluginInfo.PluginRelease latest = updateManager.getLastPluginRelease(pluginId);
if (latest == null)
{
try
{
SwingUtil.syncExec(() ->
JOptionPane.showMessageDialog(null,
pluginId + " is outdated and cannot be installed",
"Installation error",
JOptionPane.ERROR_MESSAGE));
}
catch (InvocationTargetException | InterruptedException ignored)
{
}
return;
}
updateManager.installPlugin(pluginId, null);
loadPlugin(pluginId);
}
catch (DependencyResolver.DependenciesNotFoundException ex)
{
uninstall(pluginId);
for (String dep : ex.getDependencies())
{
install(dep);
}
install(pluginId);
}
}
public void uninstall(String pluginId)
{
Path pluginPath = stopPlugin(pluginId);
if (pluginPath == null)
{
return;
}
externalPluginManager.stopPlugin(pluginId);
externalPluginManager.disablePlugin(pluginId);
}
public void update()
{
if (updateManager.hasUpdates())
{
List<PluginInfo> updates = updateManager.getUpdates();
for (PluginInfo plugin : updates)
{
PluginInfo.PluginRelease lastRelease = updateManager.getLastPluginRelease(plugin.id);
String lastVersion = lastRelease.version;
boolean updated = updateManager.updatePlugin(plugin.id, lastVersion);
if (!updated)
{
log.warn("Cannot update plugin '{}'", plugin.id);
}
}
}
}
public Set<String> getDependencies()
{
Set<String> deps = new HashSet<>();
List<PluginWrapper> startedPlugins = getStartedPlugins();
for (PluginWrapper pluginWrapper : startedPlugins)
{
for (PluginDependency pluginDependency : pluginWrapper.getDescriptor().getDependencies())
{
deps.add(pluginDependency.getPluginId());
}
}
return deps;
}
public List<String> getDisabledPlugins()
{
return this.externalPluginManager.getResolvedPlugins()
.stream()
.filter(not(this.externalPluginManager.getStartedPlugins()::contains))
.map(PluginWrapper::getPluginId)
.collect(Collectors.toList());
}
public List<PluginWrapper> getStartedPlugins()
{
return this.externalPluginManager.getStartedPlugins();
}
}
@@ -24,23 +24,26 @@
*/
package net.runelite.client.plugins;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Binder;
import com.google.inject.Injector;
import com.google.inject.Module;
import io.reactivex.Observable;
import io.reactivex.schedulers.Schedulers;
import java.lang.invoke.MethodHandles;
import java.util.Collection;
import io.reactivex.functions.Consumer;
import java.lang.reflect.Method;
import java.util.Set;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.client.eventbus.AccessorGenerator;
import lombok.Value;
import net.runelite.api.events.Event;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.eventbus.Subscription;
import net.runelite.client.eventbus.EventScheduler;
import net.runelite.client.eventbus.Subscribe;
import org.pf4j.ExtensionPoint;
public abstract class Plugin implements Module
public abstract class Plugin implements Module, ExtensionPoint
{
private Set<Subscription> annotatedSubscriptions = null;
private final Set<Subscription> annotatedSubscriptions = findSubscriptions();
private final Object annotatedSubsLock = new Object();
@Getter(AccessLevel.PROTECTED)
protected Injector injector;
@@ -58,34 +61,54 @@ public abstract class Plugin implements Module
{
}
@SuppressWarnings("unchecked")
final void addAnnotatedSubscriptions(EventBus eventBus)
{
if (annotatedSubscriptions == null)
{
Observable.fromCallable(this::findSubscriptions)
.subscribeOn(Schedulers.computation())
.observeOn(Schedulers.single())
.subscribe(subs -> addSubs(eventBus, (annotatedSubscriptions = subs)));
}
else
{
addSubs(eventBus, annotatedSubscriptions);
}
annotatedSubscriptions.forEach(sub -> eventBus.subscribe(sub.type, annotatedSubsLock, sub.method, sub.takeUntil, sub.subscribe, sub.observe));
}
final void removeAnnotatedSubscriptions(EventBus eventBus)
{
eventBus.unregister(this);
eventBus.unregister(annotatedSubsLock);
}
private Set<Subscription> findSubscriptions()
{
return AccessorGenerator.scanSubscribes(MethodHandles.lookup(), this);
ImmutableSet.Builder<Subscription> builder = ImmutableSet.builder();
for (Method method : this.getClass().getDeclaredMethods())
{
Subscribe annotation = method.getAnnotation(Subscribe.class);
if (annotation == null)
{
continue;
}
assert method.getParameterCount() == 1 : "Methods annotated with @Subscribe should have only one parameter";
Class<?> type = method.getParameterTypes()[0];
assert Event.class.isAssignableFrom(type) : "Parameters of methods annotated with @Subscribe should implement net.runelite.api.events.Event";
assert method.getReturnType() == void.class : "Methods annotated with @Subscribe should have a void return type";
method.setAccessible(true);
Subscription sub = new Subscription(type.asSubclass(Event.class), event -> method.invoke(this, event), annotation.takeUntil(), annotation.subscribe(), annotation.observe());
builder.add(sub);
}
return builder.build();
}
private void addSubs(EventBus eventBus, Collection<Subscription> subs)
@Value
private static class Subscription
{
subs.forEach(s -> s.subscribe(eventBus, this));
private final Class type;
private final Consumer method;
private final int takeUntil;
private final EventScheduler subscribe;
private final EventScheduler observe;
}
public void resetConfiguration()
@@ -48,8 +48,6 @@ public @interface PluginDescriptor
*/
String[] tags() default {};
boolean enabledByDefault() default true;
/**
* Whether or not plugin is hidden from configuration panel
*/
@@ -466,8 +466,7 @@ public class PluginManager
return Boolean.parseBoolean(value);
}
final PluginDescriptor pluginDescriptor = plugin.getClass().getAnnotation(PluginDescriptor.class);
return pluginDescriptor == null || pluginDescriptor.enabledByDefault();
return true;
}
@SuppressWarnings("unchecked")
@@ -13,7 +13,7 @@ public enum PluginType
SKILLING("Skilling"),
UTILITY("Utilities"),
MISCELLANEOUS("Miscellaneous"),
EXTERNAL("External"),
EXTERNAL("Legacy External"),
IMPORTANT("System"),
MINIGAME("Minigame"),
GAMEMODE("Gamemode"),
@@ -26,4 +26,4 @@ public enum PluginType
{
return getName();
}
}
}
@@ -1,64 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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.account;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.account.AccountSession;
import net.runelite.client.account.SessionManager;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.SessionOpen;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
@PluginDescriptor(
name = "Account",
description = "Sync RuneLite config settings with your Google account",
tags = {"external", "google", "integration"},
loadWhenOutdated = true,
hidden = true
)
@Slf4j
@Singleton
public class AccountPlugin extends Plugin
{
@Inject
private SessionManager sessionManager;
@Subscribe
private void onSessionOpen(SessionOpen sessionOpen)
{
AccountSession session = sessionManager.getAccountSession();
if (session.getUsername() == null)
{
return; // No username yet
}
log.debug("Session opened as {}", session.getUsername());
}
}
@@ -1,42 +0,0 @@
/*
* Copyright (c) 2019 Abex
* 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.achievementdiary;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Getter(AccessLevel.PACKAGE)
public class CombatLevelRequirement implements Requirement
{
private final int level;
@Override
public String toString()
{
return level + " " + "Combat";
}
}
@@ -1,44 +0,0 @@
/*
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.achievementdiary;
import com.google.common.collect.ImmutableList;
import java.util.List;
import lombok.AccessLevel;
import lombok.Getter;
@Getter(AccessLevel.PACKAGE)
class DiaryRequirement
{
private final String task;
private final List<Requirement> requirements;
DiaryRequirement(String task, Requirement[] requirements)
{
this.task = task;
this.requirements = ImmutableList.copyOf(requirements);
}
}
@@ -1,336 +0,0 @@
/*
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.achievementdiary;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.FontTypeFace;
import net.runelite.api.QuestState;
import net.runelite.api.ScriptID;
import net.runelite.api.VarPlayer;
import net.runelite.api.events.WidgetLoaded;
import net.runelite.api.util.Text;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetID;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.PluginType;
import net.runelite.client.plugins.achievementdiary.diaries.ArdougneDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.diaries.DesertDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.diaries.FaladorDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.diaries.FremennikDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.diaries.KandarinDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.diaries.KaramjaDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.diaries.KourendDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.diaries.LumbridgeDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.diaries.MorytaniaDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.diaries.VarrockDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.diaries.WesternDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.diaries.WildernessDiaryRequirement;
@Slf4j
@PluginDescriptor(
name = "Diary Requirements",
description = "Display level requirements in Achievement Diary interface",
tags = {"achievements", "tasks"},
type = PluginType.UTILITY
)
@Singleton
public class DiaryRequirementsPlugin extends Plugin
{
private static final String AND_JOINER = ", ";
private static final Pattern AND_JOINER_PATTERN = Pattern.compile("(?<=, )");
@Inject
private Client client;
@Inject
private ClientThread clientThread;
@Subscribe
private void onWidgetLoaded(final WidgetLoaded event)
{
if (event.getGroupId() == WidgetID.DIARY_QUEST_GROUP_ID)
{
String widgetTitle = Text.removeTags(
client.getWidget(
WidgetInfo.DIARY_QUEST_WIDGET_TITLE)
.getText())
.replace(' ', '_')
.toUpperCase();
if (widgetTitle.startsWith("ACHIEVEMENT_DIARY"))
{
showDiaryRequirements();
}
}
}
private void showDiaryRequirements()
{
Widget widget = client.getWidget(WidgetInfo.DIARY_QUEST_WIDGET_TEXT);
Widget[] children = widget.getStaticChildren();
Widget titleWidget = children[0];
if (titleWidget == null)
{
return;
}
FontTypeFace font = titleWidget.getFont();
int maxWidth = titleWidget.getWidth();
List<String> originalAchievements = getOriginalAchievements(children);
// new requirements starts out as a copy of the original
List<String> newRequirements = new ArrayList<>(originalAchievements);
GenericDiaryRequirement requirements = getRequirementsForTitle(titleWidget.getText());
if (requirements == null)
{
log.debug("Unknown achievement diary {}", titleWidget.getText());
return;
}
Map<String, String> skillRequirements = buildRequirements(requirements.getRequirements());
int offset = 0;
String taskBuffer = "";
for (int i = 0; i < originalAchievements.size(); i++)
{
String rowText = Text.removeTags(originalAchievements.get(i));
if (skillRequirements.get(taskBuffer + " " + rowText) != null)
{
taskBuffer = taskBuffer + " " + rowText;
}
else
{
taskBuffer = rowText;
}
if (skillRequirements.get(taskBuffer) != null)
{
String levelRequirement = skillRequirements.get(taskBuffer);
String task = originalAchievements.get(i);
int taskWidth = font.getTextWidth(task);
int ourWidth = font.getTextWidth(levelRequirement);
String strike = task.startsWith("<str>") ? "<str>" : "";
if (ourWidth + taskWidth < maxWidth)
{
// Merge onto 1 line
newRequirements.set(i + offset, task + levelRequirement);
}
else if (ourWidth < maxWidth)
{
// 2 line split
newRequirements.add(i + (++offset), strike + levelRequirement);
}
else
{
// Full text layout
StringBuilder b = new StringBuilder();
b.append(task);
int runningWidth = font.getTextWidth(b.toString());
for (String word : AND_JOINER_PATTERN.split(levelRequirement))
{
int wordWidth = font.getTextWidth(word);
if (runningWidth == 0 || wordWidth + runningWidth < maxWidth)
{
runningWidth += wordWidth;
b.append(word);
}
else
{
newRequirements.add(i + (offset++), b.toString());
b.delete(0, b.length());
runningWidth = wordWidth;
b.append(strike);
b.append(word);
}
}
newRequirements.set(i + offset, b.toString());
}
}
}
int lastLine = 0;
for (int i = 0; i < newRequirements.size() && i < children.length; i++)
{
Widget achievementWidget = children[i];
String text = newRequirements.get(i);
achievementWidget.setText(text);
if (text != null && !text.isEmpty())
{
lastLine = i;
}
}
int numLines = lastLine;
clientThread.invokeLater(() -> client.runScript(ScriptID.DIARY_QUEST_UPDATE_LINECOUNT, 1, numLines));
}
private List<String> getOriginalAchievements(Widget[] children)
{
List<String> preloadedRequirements = new ArrayList<>(children.length);
for (Widget requirementWidget : children)
{
preloadedRequirements.add(requirementWidget.getText());
}
return preloadedRequirements;
}
private GenericDiaryRequirement getRequirementsForTitle(String title)
{
String diaryName = Text.removeTags(title
.replaceAll(" ", "_")
.toUpperCase());
GenericDiaryRequirement diaryRequirementContainer;
switch (diaryName)
{
case "ARDOUGNE_AREA_TASKS":
diaryRequirementContainer = new ArdougneDiaryRequirement();
break;
case "DESERT_TASKS":
diaryRequirementContainer = new DesertDiaryRequirement();
break;
case "FALADOR_AREA_TASKS":
diaryRequirementContainer = new FaladorDiaryRequirement();
break;
case "FREMENNIK_TASKS":
diaryRequirementContainer = new FremennikDiaryRequirement();
break;
case "KANDARIN_TASKS":
diaryRequirementContainer = new KandarinDiaryRequirement();
break;
case "KARAMJA_AREA_TASKS":
diaryRequirementContainer = new KaramjaDiaryRequirement();
break;
case "KOUREND_&_KEBOS_TASKS":
diaryRequirementContainer = new KourendDiaryRequirement();
break;
case "LUMBRIDGE_&_DRAYNOR_TASKS":
diaryRequirementContainer = new LumbridgeDiaryRequirement();
break;
case "MORYTANIA_TASKS":
diaryRequirementContainer = new MorytaniaDiaryRequirement();
break;
case "VARROCK_TASKS":
diaryRequirementContainer = new VarrockDiaryRequirement();
break;
case "WESTERN_AREA_TASKS":
diaryRequirementContainer = new WesternDiaryRequirement();
break;
case "WILDERNESS_AREA_TASKS":
diaryRequirementContainer = new WildernessDiaryRequirement();
break;
default:
return null;
}
return diaryRequirementContainer;
}
// returns a map of task -> level requirements
private Map<String, String> buildRequirements(Collection<DiaryRequirement> requirements)
{
Map<String, String> reqs = new HashMap<>();
for (DiaryRequirement req : requirements)
{
StringBuilder b = new StringBuilder();
b.append("<col=ffffff>(");
assert !req.getRequirements().isEmpty();
for (Requirement ireq : req.getRequirements())
{
boolean satifisfied = satisfiesRequirement(ireq);
b.append(satifisfied ? "<col=000080><str>" : "<col=800000>");
b.append(ireq.toString());
b.append(satifisfied ? "</str>" : "<col=000080>");
b.append(AND_JOINER);
}
b.delete(b.length() - AND_JOINER.length(), b.length());
b.append("<col=ffffff>)");
reqs.put(req.getTask(), b.toString());
}
return reqs;
}
private boolean satisfiesRequirement(Requirement r)
{
if (r instanceof OrRequirement)
{
return ((OrRequirement) r).getRequirements()
.stream()
.anyMatch(this::satisfiesRequirement);
}
if (r instanceof SkillRequirement)
{
SkillRequirement s = (SkillRequirement) r;
return client.getRealSkillLevel(s.getSkill()) >= s.getLevel();
}
if (r instanceof CombatLevelRequirement)
{
return client.getLocalPlayer().getCombatLevel() >= ((CombatLevelRequirement) r).getLevel();
}
if (r instanceof QuestRequirement)
{
QuestRequirement q = (QuestRequirement) r;
QuestState state = q.getQuest().getState(client);
if (q.isStarted())
{
return state != QuestState.NOT_STARTED;
}
return state == QuestState.FINISHED;
}
if (r instanceof QuestPointRequirement)
{
return client.getVar(VarPlayer.QUEST_POINTS) >= ((QuestPointRequirement) r).getQp();
}
if (r instanceof FavourRequirement)
{
FavourRequirement f = (FavourRequirement) r;
int realFavour = client.getVar(f.getHouse().getVarbit());
return (realFavour / 10) >= f.getPercent();
}
log.warn("Unknown requirement {}", r);
return false;
}
}
@@ -1,44 +0,0 @@
/*
* Copyright (c) 2019 William <https://github.com/monsterxsync>
* 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.achievementdiary;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.runelite.api.Favour;
@RequiredArgsConstructor
@Getter(AccessLevel.PACKAGE)
public class FavourRequirement implements Requirement
{
private final Favour house;
private final int percent;
@Override
public String toString()
{
return percent + "% " + house.getName() + " favour";
}
}
@@ -1,43 +0,0 @@
/*
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.achievementdiary;
import java.util.HashSet;
import java.util.Set;
import lombok.AccessLevel;
import lombok.Getter;
public abstract class GenericDiaryRequirement
{
@Getter(AccessLevel.PACKAGE)
private Set<DiaryRequirement> requirements = new HashSet<>();
protected void add(String task, Requirement... requirements)
{
DiaryRequirement diaryRequirement = new DiaryRequirement(task, requirements);
this.requirements.add(diaryRequirement);
}
}
@@ -1,48 +0,0 @@
/*
* Copyright (c) 2019 Abex
* 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.achievementdiary;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import java.util.List;
import lombok.AccessLevel;
import lombok.Getter;
public class OrRequirement implements Requirement
{
@Getter(AccessLevel.PACKAGE)
private final List<Requirement> requirements;
public OrRequirement(Requirement... reqs)
{
this.requirements = ImmutableList.copyOf(reqs);
}
@Override
public String toString()
{
return Joiner.on(" or ").join(requirements);
}
}
@@ -1,42 +0,0 @@
/*
* Copyright (c) 2019 Abex
* 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.achievementdiary;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Getter(AccessLevel.PACKAGE)
public class QuestPointRequirement implements Requirement
{
private final int qp;
@Override
public String toString()
{
return qp + " " + "Quest points";
}
}
@@ -1,54 +0,0 @@
/*
* Copyright (c) 2019 Abex
* 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.achievementdiary;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.runelite.api.Quest;
@Getter(AccessLevel.PACKAGE)
@RequiredArgsConstructor
public class QuestRequirement implements Requirement
{
private final Quest quest;
private final boolean started;
public QuestRequirement(Quest quest)
{
this(quest, false);
}
@Override
public String toString()
{
if (started)
{
return "Started " + quest.getName();
}
return quest.getName();
}
}
@@ -1,27 +0,0 @@
/*
* Copyright (c) 2019 Abex
* 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.achievementdiary;
public interface Requirement {}
@@ -1,44 +0,0 @@
/*
* Copyright (c) 2019 Abex
* 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.achievementdiary;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.runelite.api.Skill;
@RequiredArgsConstructor
@Getter(AccessLevel.PACKAGE)
public class SkillRequirement implements Requirement
{
private final Skill skill;
private final int level;
@Override
public String toString()
{
return level + " " + skill.getName();
}
}
@@ -1,137 +0,0 @@
/*
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.achievementdiary.diaries;
import net.runelite.api.Quest;
import net.runelite.api.Skill;
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
public class ArdougneDiaryRequirement extends GenericDiaryRequirement
{
public ArdougneDiaryRequirement()
{
// EASY
add("Have Wizard Cromperty teleport you to the Rune Essence mine.",
new QuestRequirement(Quest.RUNE_MYSTERIES));
add("Steal a cake from the Ardougne market stalls.",
new SkillRequirement(Skill.THIEVING, 5));
add("Enter the Combat Training Camp north of W. Ardougne.",
new QuestRequirement(Quest.BIOHAZARD));
add("Go out fishing on the Fishing Trawler.",
new SkillRequirement(Skill.FISHING, 15));
// MEDIUM
add("Enter the Unicorn pen in Ardougne zoo using Fairy rings.",
new QuestRequirement(Quest.FAIRYTALE_II__CURE_A_QUEEN, true));
add("Grapple over Yanille's south wall.",
new SkillRequirement(Skill.AGILITY, 39),
new SkillRequirement(Skill.STRENGTH, 38),
new SkillRequirement(Skill.RANGED, 21));
add("Harvest some strawberries from the Ardougne farming patch.",
new SkillRequirement(Skill.FARMING, 31));
add("Cast the Ardougne Teleport spell.",
new SkillRequirement(Skill.MAGIC, 51),
new QuestRequirement(Quest.PLAGUE_CITY));
add("Travel to Castlewars by Hot Air Balloon.",
new SkillRequirement(Skill.FIREMAKING, 50),
new QuestRequirement(Quest.ENLIGHTENED_JOURNEY));
add("Claim buckets of sand from Bert in Yanille.",
new SkillRequirement(Skill.CRAFTING, 49),
new QuestRequirement(Quest.THE_HAND_IN_THE_SAND));
add("Catch any fish on the Fishing Platform.",
new QuestRequirement(Quest.SEA_SLUG, true));
add("Pickpocket the master farmer north of Ardougne.",
new SkillRequirement(Skill.THIEVING, 38));
add("Collect some Nightshade from the Skavid Caves.",
new QuestRequirement(Quest.WATCHTOWER, true));
add("Kill a swordchick in the Tower of Life.",
new QuestRequirement(Quest.TOWER_OF_LIFE));
add("Equip Iban's upgraded staff or upgrade an Iban staff.",
new SkillRequirement(Skill.MAGIC, 50),
new SkillRequirement(Skill.ATTACK, 50),
new QuestRequirement(Quest.UNDERGROUND_PASS));
add("Visit the Island East of the Necromancer's tower.",
new QuestRequirement(Quest.FAIRYTALE_II__CURE_A_QUEEN, true));
// HARD
// When the task is completed "the Totem" changes to "Totem" - so we add
// both variations.
add("Recharge some Jewellery at the Totem in the Legends Guild.",
new QuestRequirement(Quest.LEGENDS_QUEST));
add("Recharge some Jewellery at Totem in the Legends Guild.",
new QuestRequirement(Quest.LEGENDS_QUEST));
add("Enter the Magic Guild.",
new SkillRequirement(Skill.MAGIC, 66));
add("Attempt to steal from King Lathas' chest.",
new SkillRequirement(Skill.THIEVING, 72));
add("Have a zookeeper put you in Ardougne Zoo's monkey cage.",
new QuestRequirement(Quest.MONKEY_MADNESS_I, true));
add("Teleport to the Watchtower.",
new SkillRequirement(Skill.MAGIC, 58),
new QuestRequirement(Quest.WATCHTOWER));
add("Catch a Red Salamander.",
new SkillRequirement(Skill.HUNTER, 59));
add("Check the health of a Palm tree near tree gnome village.",
new SkillRequirement(Skill.FARMING, 68));
add("Pick some Poison Ivy berries from the patch south of Ardougne.",
new SkillRequirement(Skill.FARMING, 70));
add("Smith a Mithril platebody near Ardougne.",
new SkillRequirement(Skill.SMITHING, 68));
add("Enter your POH from Yanille.",
new SkillRequirement(Skill.CONSTRUCTION, 50));
add("Smith a Dragon sq shield in West Ardougne.",
new SkillRequirement(Skill.SMITHING, 60),
new QuestRequirement(Quest.LEGENDS_QUEST));
add("Craft some Death runes.",
new SkillRequirement(Skill.RUNECRAFT, 65),
new QuestRequirement(Quest.MOURNINGS_END_PART_II));
// ELITE
add("Catch a Manta ray in the Fishing Trawler and cook it in Port Khazard.",
new SkillRequirement(Skill.FISHING, 81),
new SkillRequirement(Skill.COOKING, 91)
);
add("Attempt to picklock the door to the basement of Yanille Agility Dungeon.",
new SkillRequirement(Skill.THIEVING, 82));
add("Pickpocket a Hero.",
new SkillRequirement(Skill.THIEVING, 80));
add("Make a rune crossbow yourself from scratch within Witchaven or Yanille.",
new SkillRequirement(Skill.CRAFTING, 10),
new SkillRequirement(Skill.SMITHING, 91),
new SkillRequirement(Skill.FLETCHING, 69));
add("Imbue a salve amulet at Nightmare Zone or equip an imbued salve amulet.",
new QuestRequirement(Quest.HAUNTED_MINE));
add("Pick some Torstol from the patch north of Ardougne.",
new SkillRequirement(Skill.FARMING, 85));
add("Complete a lap of Ardougne's rooftop agility course.",
new SkillRequirement(Skill.AGILITY, 90));
add("Cast Ice Barrage on another player within Castlewars.",
new SkillRequirement(Skill.MAGIC, 94),
new QuestRequirement(Quest.DESERT_TREASURE));
}
}
@@ -1,118 +0,0 @@
/*
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.achievementdiary.diaries;
import net.runelite.api.Quest;
import net.runelite.api.Skill;
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
public class DesertDiaryRequirement extends GenericDiaryRequirement
{
public DesertDiaryRequirement()
{
// EASY
add("Catch a Golden Warbler.",
new SkillRequirement(Skill.HUNTER, 5));
add("Mine 5 clay in the north-eastern desert.",
new SkillRequirement(Skill.MINING, 5));
add("Open the Sarcophagus in the first room of Pyramid Plunder.",
new SkillRequirement(Skill.THIEVING, 21),
new QuestRequirement(Quest.ICTHLARINS_LITTLE_HELPER, true));
// MEDIUM
add("Climb to the summit of the Agility Pyramid.",
new SkillRequirement(Skill.AGILITY, 30));
add("Slay a desert lizard.",
new SkillRequirement(Skill.SLAYER, 22));
add("Catch an Orange Salamander.",
new SkillRequirement(Skill.HUNTER, 47));
add("Steal a feather from the Desert Phoenix.",
new SkillRequirement(Skill.THIEVING, 25));
add("Travel to Uzer via Magic Carpet.",
new QuestRequirement(Quest.THE_GOLEM));
add("Travel to the Desert via Eagle.",
new QuestRequirement(Quest.EAGLES_PEAK));
add("Pray at the Elidinis statuette in Nardah.",
new QuestRequirement(Quest.SPIRITS_OF_THE_ELID));
add("Create a combat potion in the desert.",
new SkillRequirement(Skill.HERBLORE, 36));
add("Teleport to Enakhra's Temple with the Camulet.",
new QuestRequirement(Quest.ENAKHRAS_LAMENT));
add("Visit the Genie.",
new QuestRequirement(Quest.SPIRITS_OF_THE_ELID));
add("Teleport to Pollnivneach with a redirected teleport to house tablet.",
new SkillRequirement(Skill.CONSTRUCTION, 20));
add("Chop some Teak logs near Uzer.",
new SkillRequirement(Skill.WOODCUTTING, 35));
// HARD
add("Knock out and pickpocket a Menaphite Thug.",
new SkillRequirement(Skill.THIEVING, 65),
new QuestRequirement(Quest.THE_FEUD));
add("Mine some Granite.",
new SkillRequirement(Skill.MINING, 45));
add("Refill your waterskins in the Desert using Lunar magic.",
new SkillRequirement(Skill.MAGIC, 68),
new QuestRequirement(Quest.DREAM_MENTOR));
add("Complete a lap of the Pollnivneach agility course.",
new SkillRequirement(Skill.AGILITY, 70));
add("Slay a Dust Devil with a Slayer helmet equipped.",
new SkillRequirement(Skill.SLAYER, 65),
new SkillRequirement(Skill.DEFENCE, 10),
new SkillRequirement(Skill.CRAFTING, 55),
new QuestRequirement(Quest.DESERT_TREASURE, true));
add("Activate Ancient Magicks at the altar in the Jaldraocht Pyramid.",
new QuestRequirement(Quest.DESERT_TREASURE));
add("Defeat a Locust Rider with Keris.",
new SkillRequirement(Skill.ATTACK, 50),
new QuestRequirement(Quest.CONTACT));
add("Burn some yew logs on the Nardah Mayor's balcony.",
new SkillRequirement(Skill.FIREMAKING, 60));
add("Create a Mithril Platebody in Nardah.",
new SkillRequirement(Skill.SMITHING, 68));
// ELITE
add("Bake a wild pie at the Nardah Clay Oven.",
new SkillRequirement(Skill.COOKING, 85));
add("Cast Ice Barrage against a foe in the Desert.",
new SkillRequirement(Skill.MAGIC, 94),
new QuestRequirement(Quest.DESERT_TREASURE));
add("Fletch some Dragon darts at the Bedabin Camp.",
new SkillRequirement(Skill.FLETCHING, 95),
new QuestRequirement(Quest.THE_TOURIST_TRAP));
add("Speak to the KQ head in your POH.",
new SkillRequirement(Skill.CONSTRUCTION, 78),
new QuestRequirement(Quest.PRIEST_IN_PERIL));
add("Steal from the Grand Gold Chest in the final room of Pyramid Plunder.",
new SkillRequirement(Skill.THIEVING, 91),
new QuestRequirement(Quest.ICTHLARINS_LITTLE_HELPER, true));
add("Restore at least 85 Prayer points when praying at the Altar in Sophanem.",
new SkillRequirement(Skill.PRAYER, 85),
new QuestRequirement(Quest.ICTHLARINS_LITTLE_HELPER, true));
}
}
@@ -1,121 +0,0 @@
/*
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.achievementdiary.diaries;
import net.runelite.api.Quest;
import net.runelite.api.Skill;
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
public class FaladorDiaryRequirement extends GenericDiaryRequirement
{
public FaladorDiaryRequirement()
{
// EASY
add("Find out what your family crest is from Sir Renitee.",
new SkillRequirement(Skill.CONSTRUCTION, 16));
add("Climb over the western Falador wall.",
new SkillRequirement(Skill.AGILITY, 5));
add("Make a mind tiara.",
new QuestRequirement(Quest.RUNE_MYSTERIES));
add("Smith some Blurite Limbs on Doric's Anvil.",
new SkillRequirement(Skill.MINING, 10),
new SkillRequirement(Skill.SMITHING, 13),
new QuestRequirement(Quest.THE_KNIGHTS_SWORD),
new QuestRequirement(Quest.DORICS_QUEST));
// MEDIUM
add("Light a Bullseye lantern at the Chemist's in Rimmington.",
new SkillRequirement(Skill.FIREMAKING, 49));
add("Telegrab some Wine of Zamorak at the Chaos Temple by the Wilderness.",
new SkillRequirement(Skill.MAGIC, 33));
add("Place a Scarecrow in the Falador farming patch.",
new SkillRequirement(Skill.FARMING, 23));
add("Kill a Mogre at Mudskipper Point.",
new SkillRequirement(Skill.SLAYER, 32),
new QuestRequirement(Quest.SKIPPY_AND_THE_MOGRES));
add("Visit the Port Sarim Rat Pits.",
new QuestRequirement(Quest.RATCATCHERS, true));
add("Grapple up and then jump off the north Falador wall.",
new SkillRequirement(Skill.AGILITY, 11),
new SkillRequirement(Skill.STRENGTH, 37),
new SkillRequirement(Skill.RANGED, 19));
add("Pickpocket a Falador guard.",
new SkillRequirement(Skill.THIEVING, 40));
add("Pray at the Altar of Guthix in Taverley whilst wearing full Initiate.",
new SkillRequirement(Skill.PRAYER, 10),
new SkillRequirement(Skill.DEFENCE, 20),
new QuestRequirement(Quest.RECRUITMENT_DRIVE));
add("Mine some Gold ore at the Crafting Guild.",
new SkillRequirement(Skill.CRAFTING, 40),
new SkillRequirement(Skill.MINING, 40));
add("Squeeze through the crevice in the Dwarven mines.",
new SkillRequirement(Skill.AGILITY, 42));
add("Chop and burn some Willow logs in Taverley",
new SkillRequirement(Skill.WOODCUTTING, 30),
new SkillRequirement(Skill.FIREMAKING, 30));
add("Craft a fruit basket on the Falador Farm loom.",
new SkillRequirement(Skill.CRAFTING, 36));
add("Teleport to Falador.",
new SkillRequirement(Skill.MAGIC, 37));
// HARD
add("Craft 140 Mind runes simultaneously.",
new SkillRequirement(Skill.RUNECRAFT, 56));
add("Change your family crest to the Saradomin symbol.",
new SkillRequirement(Skill.PRAYER, 70));
add("Kill a Skeletal Wyvern in the Asgarnia Ice Dungeon.",
new SkillRequirement(Skill.SLAYER, 72));
add("Complete a lap of the Falador rooftop agility course.",
new SkillRequirement(Skill.AGILITY, 50));
add("Enter the mining guild wearing full prospector.",
new SkillRequirement(Skill.MINING, 60));
add("Kill the Blue Dragon under the Heroes' Guild.",
new QuestRequirement(Quest.HEROES_QUEST));
add("Crack a wall safe within Rogues Den.",
new SkillRequirement(Skill.THIEVING, 50));
add("Recharge your prayer in the Port Sarim church while wearing full Proselyte.",
new SkillRequirement(Skill.DEFENCE, 30),
new QuestRequirement(Quest.THE_SLUG_MENACE));
add("Equip a dwarven helmet within the dwarven mines.",
new SkillRequirement(Skill.DEFENCE, 50),
new QuestRequirement(Quest.GRIM_TALES));
// ELITE
add("Craft 252 Air Runes simultaneously.",
new SkillRequirement(Skill.RUNECRAFT, 88));
add("Purchase a White 2h Sword from Sir Vyvin.",
new QuestRequirement(Quest.WANTED));
add("Find at least 3 magic roots at once when digging up your magic tree in Falador.",
new SkillRequirement(Skill.FARMING, 91),
new SkillRequirement(Skill.WOODCUTTING, 75));
add("Jump over the strange floor in Taverley dungeon.",
new SkillRequirement(Skill.AGILITY, 80));
add("Mix a Saradomin brew in Falador east bank.",
new SkillRequirement(Skill.HERBLORE, 81));
}
}
@@ -1,132 +0,0 @@
/*
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.achievementdiary.diaries;
import net.runelite.api.Quest;
import net.runelite.api.Skill;
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
public class FremennikDiaryRequirement extends GenericDiaryRequirement
{
public FremennikDiaryRequirement()
{
// EASY
add("Catch a Cerulean twitch.",
new SkillRequirement(Skill.HUNTER, 11));
add("Change your boots at Yrsa's Shoe Store.",
new QuestRequirement(Quest.THE_FREMENNIK_TRIALS));
add("Craft a tiara from scratch in Rellekka.",
new SkillRequirement(Skill.CRAFTING, 23),
new SkillRequirement(Skill.MINING, 20),
new SkillRequirement(Skill.SMITHING, 20),
new QuestRequirement(Quest.THE_FREMENNIK_TRIALS));
add("Browse the Stonemasons shop.",
new QuestRequirement(Quest.THE_GIANT_DWARF, true));
add("Steal from the Keldagrim crafting or baker's stall.",
new SkillRequirement(Skill.THIEVING, 5),
new QuestRequirement(Quest.THE_GIANT_DWARF, true));
add("Enter the Troll Stronghold.",
new QuestRequirement(Quest.DEATH_PLATEAU),
new QuestRequirement(Quest.TROLL_STRONGHOLD, true));
add("Chop and burn some oak logs in the Fremennik Province.",
new SkillRequirement(Skill.WOODCUTTING, 15),
new SkillRequirement(Skill.FIREMAKING, 15));
// MEDIUM
add("Slay a Brine rat.",
new SkillRequirement(Skill.SLAYER, 47),
new QuestRequirement(Quest.OLAFS_QUEST, true));
add("Travel to the Snowy Hunter Area via Eagle.",
new QuestRequirement(Quest.EAGLES_PEAK));
add("Mine some coal in Rellekka.",
new SkillRequirement(Skill.MINING, 30),
new QuestRequirement(Quest.THE_FREMENNIK_TRIALS));
add("Steal from the Rellekka Fish stalls.",
new SkillRequirement(Skill.THIEVING, 42),
new QuestRequirement(Quest.THE_FREMENNIK_TRIALS));
add("Travel to Miscellania by Fairy ring.",
new QuestRequirement(Quest.THE_FREMENNIK_TRIALS),
new QuestRequirement(Quest.FAIRYTALE_II__CURE_A_QUEEN, true));
add("Catch a Snowy knight.",
new SkillRequirement(Skill.HUNTER, 35));
add("Pick up your Pet Rock from your POH Menagerie.",
new SkillRequirement(Skill.CONSTRUCTION, 37),
new QuestRequirement(Quest.THE_FREMENNIK_TRIALS));
add("Visit the Lighthouse from Waterbirth island.",
new QuestRequirement(Quest.HORROR_FROM_THE_DEEP),
new QuestRequirement(Quest.THE_FREMENNIK_TRIALS, true));
add("Mine some gold at the Arzinian mine.",
new SkillRequirement(Skill.MINING, 40),
new QuestRequirement(Quest.BETWEEN_A_ROCK, true));
// HARD
add("Teleport to Trollheim.",
new SkillRequirement(Skill.MAGIC, 61),
new QuestRequirement(Quest.EADGARS_RUSE));
add("Catch a Sabre-toothed Kyatt.",
new SkillRequirement(Skill.HUNTER, 55));
add("Mix a super defence potion in the Fremennik province.",
new SkillRequirement(Skill.HERBLORE, 66));
add("Steal from the Keldagrim Gem Stall.",
new SkillRequirement(Skill.THIEVING, 75),
new QuestRequirement(Quest.THE_GIANT_DWARF, true));
add("Craft a Fremennik shield on Neitiznot.",
new SkillRequirement(Skill.WOODCUTTING, 56),
new QuestRequirement(Quest.THE_FREMENNIK_ISLES));
add("Mine 5 Adamantite ores on Jatizso.",
new SkillRequirement(Skill.MINING, 70),
new QuestRequirement(Quest.THE_FREMENNIK_ISLES));
add("Obtain 100% support from your kingdom subjects.",
new QuestRequirement(Quest.THRONE_OF_MISCELLANIA));
add("Teleport to Waterbirth Island.",
new SkillRequirement(Skill.MAGIC, 72),
new QuestRequirement(Quest.LUNAR_DIPLOMACY));
add("Obtain the Blast Furnace Foreman's permission to use the Blast Furnace for free.",
new SkillRequirement(Skill.SMITHING, 60),
new QuestRequirement(Quest.THE_GIANT_DWARF, true));
// ELITE
add("Craft 56 astral runes at once.",
new SkillRequirement(Skill.RUNECRAFT, 82),
new QuestRequirement(Quest.LUNAR_DIPLOMACY));
add("Create a dragonstone amulet in the Neitiznot furnace.",
new SkillRequirement(Skill.CRAFTING, 80),
new QuestRequirement(Quest.THE_FREMENNIK_ISLES, true));
add("Complete a lap of the Rellekka agility course.",
new SkillRequirement(Skill.AGILITY, 80));
add("Kill each of the Godwars generals.",
new SkillRequirement(Skill.AGILITY, 70),
new SkillRequirement(Skill.STRENGTH, 70),
new SkillRequirement(Skill.HITPOINTS, 70),
new SkillRequirement(Skill.RANGED, 70),
new QuestRequirement(Quest.TROLL_STRONGHOLD));
add("Slay a Spiritual mage within the Godwars Dungeon.",
new SkillRequirement(Skill.SLAYER, 83),
new QuestRequirement(Quest.TROLL_STRONGHOLD));
}
}
@@ -1,130 +0,0 @@
/*
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.achievementdiary.diaries;
import net.runelite.api.Quest;
import net.runelite.api.Skill;
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
public class KandarinDiaryRequirement extends GenericDiaryRequirement
{
public KandarinDiaryRequirement()
{
// EASY
add("Catch a Mackerel at Catherby.",
new SkillRequirement(Skill.FISHING, 16));
add("Plant some Jute seeds in the patch north of McGrubor's Wood.",
new SkillRequirement(Skill.FARMING, 13));
add("Defeat one of each elemental in the workshop.",
new QuestRequirement(Quest.ELEMENTAL_WORKSHOP_I, true));
add("Cross the Coal truck log shortcut.",
new SkillRequirement(Skill.AGILITY, 20));
// MEDIUM
add("Complete a lap of the Barbarian agility course.",
new SkillRequirement(Skill.AGILITY, 35),
new QuestRequirement(Quest.ALFRED_GRIMHANDS_BARCRAWL));
add("Create a Super Antipoison potion from scratch in the Seers/Catherby Area.",
new SkillRequirement(Skill.HERBLORE, 48));
add("Enter the Ranging guild.",
new SkillRequirement(Skill.RANGED, 40));
add("Use the grapple shortcut to get from the water obelisk to Catherby shore.",
new SkillRequirement(Skill.AGILITY, 36),
new SkillRequirement(Skill.STRENGTH, 22),
new SkillRequirement(Skill.RANGED, 39));
add("Catch and cook a Bass in Catherby.",
new SkillRequirement(Skill.FISHING, 46),
new SkillRequirement(Skill.COOKING, 43));
add("Teleport to Camelot.",
new SkillRequirement(Skill.MAGIC, 45));
add("String a Maple shortbow in Seers' Village bank.",
new SkillRequirement(Skill.FLETCHING, 50));
add("Pick some Limpwurt root from the farming patch in Catherby.",
new SkillRequirement(Skill.FARMING, 26));
add("Create a Mind helmet.",
new QuestRequirement(Quest.ELEMENTAL_WORKSHOP_II));
add("Kill a Fire Giant inside Baxtorian Waterfall.",
new QuestRequirement(Quest.WATERFALL_QUEST, true));
add("Steal from the chest in Hemenster.",
new SkillRequirement(Skill.THIEVING, 47));
add("Travel to McGrubor's Wood by Fairy Ring.",
new QuestRequirement(Quest.FAIRYTALE_II__CURE_A_QUEEN, true));
add("Mine some coal near the coal trucks.",
new SkillRequirement(Skill.MINING, 30));
// HARD
add("Catch a Leaping Sturgeon.",
new SkillRequirement(Skill.FISHING, 70),
new SkillRequirement(Skill.AGILITY, 45),
new SkillRequirement(Skill.STRENGTH, 45));
add("Complete a lap of the Seers' Village agility course.",
new SkillRequirement(Skill.AGILITY, 60));
add("Create a Yew Longbow from scratch around Seers' Village.",
new SkillRequirement(Skill.WOODCUTTING, 60),
new SkillRequirement(Skill.FLETCHING, 70),
new SkillRequirement(Skill.CRAFTING, 10));
add("Enter the Seers' Village courthouse with piety turned on.",
new SkillRequirement(Skill.PRAYER, 70),
new SkillRequirement(Skill.DEFENCE, 70),
new QuestRequirement(Quest.KINGS_RANSOM));
add("Charge a Water Orb.",
new SkillRequirement(Skill.MAGIC, 56));
add("Burn some Maple logs with a bow in Seers' Village.",
new SkillRequirement(Skill.FIREMAKING, 65));
add("Kill a Shadow Hound in the Shadow dungeon.",
new SkillRequirement(Skill.THIEVING, 53),
new QuestRequirement(Quest.DESERT_TREASURE, true));
add("Purchase and equip a granite body from Barbarian Assault.",
new SkillRequirement(Skill.STRENGTH, 50),
new SkillRequirement(Skill.DEFENCE, 50));
add("Have the Seers' estate agent decorate your house with Fancy Stone.",
new SkillRequirement(Skill.CONSTRUCTION, 50));
add("Smith an Adamant spear at Otto's Grotto.",
new SkillRequirement(Skill.SMITHING, 75),
new QuestRequirement(Quest.TAI_BWO_WANNAI_TRIO));
// ELITE
add("Pick some Dwarf weed from the herb patch at Catherby.",
new SkillRequirement(Skill.FARMING, 79));
add("Fish and Cook 5 Sharks in Catherby using the Cooking gauntlets.",
new SkillRequirement(Skill.FISHING, 76),
new SkillRequirement(Skill.COOKING, 80),
new QuestRequirement(Quest.FAMILY_CREST));
add("Mix a Stamina Mix on top of the Seers' Village bank.",
new SkillRequirement(Skill.HERBLORE, 86),
new SkillRequirement(Skill.AGILITY, 60));
add("Smith a Rune Hasta at Otto's Grotto.",
new SkillRequirement(Skill.SMITHING, 90));
add("Construct a Pyre ship from Magic Logs.(Requires Chewed Bones.)",
new SkillRequirement(Skill.FIREMAKING, 85),
new SkillRequirement(Skill.CRAFTING, 85));
add("Teleport to Catherby.",
new SkillRequirement(Skill.MAGIC, 87),
new QuestRequirement(Quest.LUNAR_DIPLOMACY));
}
}
@@ -1,134 +0,0 @@
/*
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.achievementdiary.diaries;
import net.runelite.api.Quest;
import net.runelite.api.Skill;
import net.runelite.client.plugins.achievementdiary.CombatLevelRequirement;
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.OrRequirement;
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
public class KaramjaDiaryRequirement extends GenericDiaryRequirement
{
public KaramjaDiaryRequirement()
{
// EASY
add("Use the rope swing to travel to the small island north-west of Karamja, where the " +
"moss giants are.",
new SkillRequirement(Skill.AGILITY, 10));
add("Mine some gold from the rocks on the north-west peninsula of Karamja.",
new SkillRequirement(Skill.MINING, 40));
add("Explore Cairn Island to the west of Karamja.",
new SkillRequirement(Skill.AGILITY, 15));
// MEDIUM
add("Claim a ticket from the Agility Arena in Brimhaven.",
new SkillRequirement(Skill.AGILITY, 30));
add("Discover hidden wall in the dungeon below the volcano.",
new QuestRequirement(Quest.DRAGON_SLAYER, true));
add("Visit the Isle of Crandor via the dungeon below the volcano.",
new QuestRequirement(Quest.DRAGON_SLAYER, true));
add("Use Vigroy and Hajedy's cart service.",
new QuestRequirement(Quest.SHILO_VILLAGE));
add("Earn 100% favour in the village of Tai Bwo Wannai.",
new SkillRequirement(Skill.WOODCUTTING, 10),
new QuestRequirement(Quest.JUNGLE_POTION));
add("Cook a spider on a stick.",
new SkillRequirement(Skill.COOKING, 16));
add("Charter the Lady of the Waves from Cairn Isle to Port Khazard.",
new QuestRequirement(Quest.SHILO_VILLAGE));
add("Cut a log from a teak tree.",
new SkillRequirement(Skill.WOODCUTTING, 35),
new QuestRequirement(Quest.JUNGLE_POTION));
add("Cut a log from a mahogany tree.",
new SkillRequirement(Skill.WOODCUTTING, 50),
new QuestRequirement(Quest.JUNGLE_POTION));
add("Catch a karambwan.",
new SkillRequirement(Skill.FISHING, 65),
new QuestRequirement(Quest.TAI_BWO_WANNAI_TRIO, true));
add("Exchange gems for a machete.",
new QuestRequirement(Quest.JUNGLE_POTION));
add("Use the gnome glider to travel to Karamja.",
new QuestRequirement(Quest.THE_GRAND_TREE));
add("Grow a healthy fruit tree in the patch near Brimhaven.",
new SkillRequirement(Skill.FARMING, 27));
add("Trap a horned graahk.",
new SkillRequirement(Skill.HUNTER, 41));
add("Chop the vines to gain deeper access to Brimhaven Dungeon.",
new SkillRequirement(Skill.WOODCUTTING, 10));
add("Cross the lava using the stepping stones within Brimhaven Dungeon.",
new SkillRequirement(Skill.AGILITY, 12));
add("Climb the stairs within Brimhaven Dungeon.",
new SkillRequirement(Skill.WOODCUTTING, 10));
add("Charter a ship from the shipyard in the far east of Karamja.",
new QuestRequirement(Quest.THE_GRAND_TREE));
add("Mine a red topaz from a gem rock.",
new SkillRequirement(Skill.MINING, 40),
new OrRequirement(
new QuestRequirement(Quest.SHILO_VILLAGE),
new QuestRequirement(Quest.JUNGLE_POTION)
)
);
// HARD
add("Craft some nature runes.",
new SkillRequirement(Skill.RUNECRAFT, 44),
new QuestRequirement(Quest.RUNE_MYSTERIES));
add("Cook a karambwan thoroughly.",
new SkillRequirement(Skill.COOKING, 30),
new QuestRequirement(Quest.TAI_BWO_WANNAI_TRIO));
add("Kill a deathwing in the dungeon under the Kharazi Jungle.",
new SkillRequirement(Skill.WOODCUTTING, 15),
new SkillRequirement(Skill.STRENGTH, 50),
new SkillRequirement(Skill.AGILITY, 50),
new SkillRequirement(Skill.THIEVING, 50),
new SkillRequirement(Skill.MINING, 52),
new QuestRequirement(Quest.LEGENDS_QUEST));
add("Use the crossbow short cut south of the volcano.",
new SkillRequirement(Skill.AGILITY, 53),
new SkillRequirement(Skill.RANGED, 42),
new SkillRequirement(Skill.STRENGTH, 21));
add("Collect 5 palm leaves.",
new SkillRequirement(Skill.WOODCUTTING, 15),
new QuestRequirement(Quest.LEGENDS_QUEST));
add("Be assigned a Slayer task by Duradel north of Shilo Village.",
new CombatLevelRequirement(100),
new SkillRequirement(Skill.SLAYER, 50),
new QuestRequirement(Quest.SHILO_VILLAGE));
// ELITE
add("Craft 56 Nature runes at once.",
new SkillRequirement(Skill.RUNECRAFT, 91));
add("Check the health of a palm tree in Brimhaven.",
new SkillRequirement(Skill.FARMING, 68));
add("Create an antivenom potion whilst standing in the horse shoe mine.",
new SkillRequirement(Skill.HERBLORE, 87));
add("Check the health of your Calquat tree patch.",
new SkillRequirement(Skill.FARMING, 72));
}
}
@@ -1,136 +0,0 @@
/*
* Copyright (c) 2019 William <https://github.com/monsterxsync>
* 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.achievementdiary.diaries;
import net.runelite.api.Favour;
import net.runelite.api.Quest;
import net.runelite.api.Skill;
import net.runelite.client.plugins.achievementdiary.FavourRequirement;
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
public class KourendDiaryRequirement extends GenericDiaryRequirement
{
public KourendDiaryRequirement()
{
//EASY
add("Mine some Iron at the Mount Karuulm mine.",
new SkillRequirement(Skill.MINING, 15));
add("Steal from a Hosidius Food Stall.",
new SkillRequirement(Skill.THIEVING, 25),
new FavourRequirement(Favour.HOSIDIUS, 15));
add("Browse the Warrens General Store.",
new QuestRequirement(Quest.THE_QUEEN_OF_THIEVES, true));
add("Enter your Player Owned House from Hosidius.",
new SkillRequirement(Skill.CONSTRUCTION, 25));
add("Create a Strength potion in the Lovakengj Pub.",
new SkillRequirement(Skill.HERBLORE, 12));
add("Fish a Trout from the River Molch.",
new SkillRequirement(Skill.FISHING, 20));
//MEDIUM
add("Travel to the Fairy Ring south of Mount Karuulm.",
new QuestRequirement(Quest.FAIRYTALE_II__CURE_A_QUEEN, true));
add("Use Kharedst's memoirs to teleport to all five cities in Great Kourend.",
new QuestRequirement(Quest.THE_DEPTHS_OF_DESPAIR),
new QuestRequirement(Quest.THE_QUEEN_OF_THIEVES),
new QuestRequirement(Quest.TALE_OF_THE_RIGHTEOUS),
new QuestRequirement(Quest.THE_FORSAKEN_TOWER),
new QuestRequirement(Quest.THE_ASCENT_OF_ARCEUUS));
add("Mine some Volcanic sulphur.",
new SkillRequirement(Skill.MINING, 42));
add("Enter the Farming Guild.",
new SkillRequirement(Skill.FARMING, 45));
add("Switch to the Necromancy Spellbook at Tyss.",
new FavourRequirement(Favour.ARCEUUS, 60));
add("Repair a Piscarilius crane.",
new SkillRequirement(Skill.CRAFTING, 30));
add("Deliver some intelligence to Captain Ginea.",
new FavourRequirement(Favour.SHAYZIEN, 40));
add("Catch a Bluegill on Molch Island.",
new SkillRequirement(Skill.FISHING, 43),
new SkillRequirement(Skill.HUNTER, 35));
add("Use the boulder leap in the Arceuus essence mine.",
new SkillRequirement(Skill.AGILITY, 49));
add("Subdue the Wintertodt.",
new SkillRequirement(Skill.FIREMAKING, 50));
add("Catch a Chinchompa in the Kourend Woodland.",
new SkillRequirement(Skill.HUNTER, 53),
new QuestRequirement(Quest.EAGLES_PEAK));
add("Chop some Mahogany logs north of the Farming Guild.",
new SkillRequirement(Skill.WOODCUTTING, 50));
//HARD
add("Enter the Woodcutting Guild.",
new SkillRequirement(Skill.WOODCUTTING, 60),
new FavourRequirement(Favour.HOSIDIUS, 75));
add("Smelt an Adamantite bar in The Forsaken Tower.",
new SkillRequirement(Skill.SMITHING, 70),
new QuestRequirement(Quest.THE_FORSAKEN_TOWER, true));
add("Kill a Lizardman Shaman in Molch.",
new FavourRequirement(Favour.SHAYZIEN, 100));
add("Mine some Lovakite.",
new SkillRequirement(Skill.MINING, 65),
new FavourRequirement(Favour.LOVAKENGJ, 30));
add("Plant some Logavano seeds at the Tithe Farm.",
new SkillRequirement(Skill.FARMING, 74),
new FavourRequirement(Favour.HOSIDIUS, 100));
add("Teleport to Xeric's Heart using Xeric's Talisman.",
new QuestRequirement(Quest.ARCHITECTURAL_ALLIANCE));
add("Deliver an artefact to Captain Khaled.",
new SkillRequirement(Skill.THIEVING, 49),
new FavourRequirement(Favour.PISCARILIUS, 75));
add("Kill a Wyrm in the Karuulm Slayer Dungeon.",
new SkillRequirement(Skill.SLAYER, 62));
add("Cast Monster Examine on a Troll south of Mount Quidamortem.",
new SkillRequirement(Skill.MAGIC, 66),
new QuestRequirement(Quest.DREAM_MENTOR));
//ELITE
add("Craft one or more Blood runes.",
new SkillRequirement(Skill.RUNECRAFT, 77),
new SkillRequirement(Skill.MINING, 38),
new SkillRequirement(Skill.CRAFTING, 38),
new FavourRequirement(Favour.ARCEUUS, 100));
add("Chop some Redwood logs.",
new SkillRequirement(Skill.WOODCUTTING, 90),
new FavourRequirement(Favour.HOSIDIUS, 75));
add("Catch an Anglerfish and cook it whilst in Great Kourend.",
new SkillRequirement(Skill.FISHING, 82),
new SkillRequirement(Skill.COOKING, 84),
new FavourRequirement(Favour.PISCARILIUS, 100));
add("Kill a Hydra in the Karuulm Slayer Dungeon.",
new SkillRequirement(Skill.SLAYER, 95));
add("Create an Ape Atoll teleport tablet.",
new SkillRequirement(Skill.MAGIC, 90),
new SkillRequirement(Skill.MINING, 38),
new SkillRequirement(Skill.CRAFTING, 38),
new FavourRequirement(Favour.ARCEUUS, 100));
add("Create your own Battlestaff from scratch within the Farming Guild.",
new SkillRequirement(Skill.FARMING, 85),
new SkillRequirement(Skill.FLETCHING, 40));
}
}
@@ -1,135 +0,0 @@
/*
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.achievementdiary.diaries;
import net.runelite.api.Quest;
import net.runelite.api.Skill;
import net.runelite.client.plugins.achievementdiary.CombatLevelRequirement;
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
public class LumbridgeDiaryRequirement extends GenericDiaryRequirement
{
public LumbridgeDiaryRequirement()
{
// EASY
add("Complete a lap of the Draynor Village agility course.",
new SkillRequirement(Skill.AGILITY, 10));
add("Slay a Cave bug beneath Lumbridge Swamp.",
new SkillRequirement(Skill.SLAYER, 7));
add("Have Sedridor teleport you to the Essence Mine.",
new QuestRequirement(Quest.RUNE_MYSTERIES));
add("Craft some water runes.",
new SkillRequirement(Skill.RUNECRAFT, 5),
new QuestRequirement(Quest.RUNE_MYSTERIES));
add("Chop and burn some oak logs in Lumbridge.",
new SkillRequirement(Skill.WOODCUTTING, 15),
new SkillRequirement(Skill.FIREMAKING, 15));
add("Catch some Anchovies in Al Kharid.",
new SkillRequirement(Skill.FISHING, 15));
add("Bake some Bread on the Lumbridge kitchen range.",
new QuestRequirement(Quest.COOKS_ASSISTANT));
add("Mine some Iron ore at the Al Kharid mine.",
new SkillRequirement(Skill.MINING, 15));
// MEDIUM
add("Complete a lap of the Al Kharid agility course.",
new SkillRequirement(Skill.AGILITY, 20));
add("Grapple across the River Lum.",
new SkillRequirement(Skill.AGILITY, 8),
new SkillRequirement(Skill.STRENGTH, 19),
new SkillRequirement(Skill.RANGED, 37));
add("Purchase an upgraded device from Ava.",
new SkillRequirement(Skill.RANGED, 50),
new QuestRequirement(Quest.ANIMAL_MAGNETISM));
add("Travel to the Wizards' Tower by Fairy ring.",
new QuestRequirement(Quest.FAIRYTALE_II__CURE_A_QUEEN, true));
add("Cast the teleport to Lumbridge spell.",
new SkillRequirement(Skill.MAGIC, 31));
add("Catch some Salmon in Lumbridge.",
new SkillRequirement(Skill.FISHING, 30));
add("Craft a coif in the Lumbridge cow pen.",
new SkillRequirement(Skill.CRAFTING, 38));
add("Chop some willow logs in Draynor Village.",
new SkillRequirement(Skill.WOODCUTTING, 30));
add("Pickpocket Martin the Master Gardener.",
new SkillRequirement(Skill.THIEVING, 38));
add("Get a slayer task from Chaeldar.",
new CombatLevelRequirement(70),
new QuestRequirement(Quest.LOST_CITY));
add("Catch an Essence or Eclectic impling in Puro-Puro.",
new SkillRequirement(Skill.HUNTER, 42),
new QuestRequirement(Quest.LOST_CITY));
add("Craft some Lava runes at the fire altar in Al Kharid.",
new SkillRequirement(Skill.RUNECRAFT, 23),
new QuestRequirement(Quest.RUNE_MYSTERIES));
// HARD
add("Cast Bones to Peaches in Al Kharid palace.",
new SkillRequirement(Skill.MAGIC, 60));
add("Squeeze past the jutting wall on your way to the cosmic altar.",
new SkillRequirement(Skill.AGILITY, 46),
new QuestRequirement(Quest.LOST_CITY));
add("Craft 56 Cosmic runes simultaneously.",
new SkillRequirement(Skill.RUNECRAFT, 59),
new QuestRequirement(Quest.LOST_CITY));
add("Travel from Lumbridge to Edgeville on a Waka Canoe.",
new SkillRequirement(Skill.WOODCUTTING, 57));
add("Collect at least 100 Tears of Guthix in one visit.",
new QuestRequirement(Quest.TEARS_OF_GUTHIX));
add("Take the train from Dorgesh-Kaan to Keldagrim.",
new QuestRequirement(Quest.ANOTHER_SLICE_OF_HAM));
add("Purchase some Barrows gloves from the Lumbridge bank chest.",
new QuestRequirement(Quest.RECIPE_FOR_DISASTER));
add("Pick some Belladonna from the farming patch at Draynor Manor.",
new SkillRequirement(Skill.FARMING, 63));
add("Light your mining helmet in the Lumbridge castle basement.",
new SkillRequirement(Skill.FIREMAKING, 65));
add("Recharge your prayer at Clan Wars with Smite activated.",
new SkillRequirement(Skill.PRAYER, 52));
add("Craft, string and enchant an Amulet of Power in Lumbridge.",
new SkillRequirement(Skill.CRAFTING, 70),
new SkillRequirement(Skill.MAGIC, 57));
// ELITE
add("Steal from a Dorgesh-Kaan rich chest.",
new SkillRequirement(Skill.THIEVING, 78),
new QuestRequirement(Quest.DEATH_TO_THE_DORGESHUUN));
add("Pickpocket Movario on the Dorgesh-Kaan Agility course.",
new SkillRequirement(Skill.AGILITY, 70),
new SkillRequirement(Skill.RANGED, 70),
new SkillRequirement(Skill.STRENGTH, 70),
new QuestRequirement(Quest.DEATH_TO_THE_DORGESHUUN));
add("Chop some magic logs at the Mage Training Arena.",
new SkillRequirement(Skill.WOODCUTTING, 75));
add("Smith an Adamant platebody down Draynor sewer.",
new SkillRequirement(Skill.SMITHING, 88));
add("Craft 140 or more Water runes at once.",
new SkillRequirement(Skill.RUNECRAFT, 76),
new QuestRequirement(Quest.RUNE_MYSTERIES));
}
}
@@ -1,139 +0,0 @@
/*
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.achievementdiary.diaries;
import net.runelite.api.Quest;
import net.runelite.api.Skill;
import net.runelite.client.plugins.achievementdiary.CombatLevelRequirement;
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.OrRequirement;
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
public class MorytaniaDiaryRequirement extends GenericDiaryRequirement
{
public MorytaniaDiaryRequirement()
{
// EASY
add("Craft any Snelm from scratch in Morytania.",
new SkillRequirement(Skill.CRAFTING, 15));
add("Cook a thin Snail on the Port Phasmatys range.",
new SkillRequirement(Skill.COOKING, 12));
add("Get a slayer task from Mazchna.",
new CombatLevelRequirement(20));
add("Kill a Banshee in the Slayer Tower.",
new SkillRequirement(Skill.SLAYER, 15));
add("Place a Scarecrow in the Morytania flower patch.",
new SkillRequirement(Skill.FARMING, 23));
add("Kill a werewolf in its human form using the Wolfbane Dagger.",
new QuestRequirement(Quest.PRIEST_IN_PERIL));
add("Restore your prayer points at the nature altar.",
new QuestRequirement(Quest.NATURE_SPIRIT));
// MEDIUM
add("Catch a swamp lizard.",
new SkillRequirement(Skill.HUNTER, 29));
add("Complete a lap of the Canifis agility course.",
new SkillRequirement(Skill.AGILITY, 40));
add("Obtain some Bark from a Hollow tree.",
new SkillRequirement(Skill.WOODCUTTING, 45));
add("Kill a Terror Dog.",
new SkillRequirement(Skill.SLAYER, 40),
new QuestRequirement(Quest.LAIR_OF_TARN_RAZORLOR));
add("Complete a game of trouble brewing.",
new SkillRequirement(Skill.COOKING, 40),
new QuestRequirement(Quest.CABIN_FEVER));
add("Make a batch of cannonballs at the Port Phasmatys furnace.",
new SkillRequirement(Skill.SMITHING, 35),
new QuestRequirement(Quest.DWARF_CANNON),
new QuestRequirement(Quest.GHOSTS_AHOY, true));
add("Kill a Fever Spider on Braindeath Island.",
new SkillRequirement(Skill.SLAYER, 42),
new QuestRequirement(Quest.RUM_DEAL));
add("Use an ectophial to return to Port Phasmatys.",
new QuestRequirement(Quest.GHOSTS_AHOY));
add("Mix a Guthix Balance potion while in Morytania.",
new SkillRequirement(Skill.HERBLORE, 22),
new QuestRequirement(Quest.IN_AID_OF_THE_MYREQUE, true));
// HARD
add("Enter the Kharyrll portal in your POH.",
new SkillRequirement(Skill.MAGIC, 66),
new SkillRequirement(Skill.CONSTRUCTION, 50),
new QuestRequirement(Quest.DESERT_TREASURE));
add("Climb the advanced spike chain within Slayer Tower.",
new SkillRequirement(Skill.AGILITY, 71));
add("Harvest some Watermelon from the Allotment patch on Harmony Island.",
new SkillRequirement(Skill.FARMING, 47),
new QuestRequirement(Quest.THE_GREAT_BRAIN_ROBBERY, true));
add("Chop and burn some mahogany logs on Mos Le'Harmless.",
new SkillRequirement(Skill.WOODCUTTING, 50),
new SkillRequirement(Skill.FIREMAKING, 50),
new QuestRequirement(Quest.CABIN_FEVER));
add("Complete a temple trek with a hard companion.",
new QuestRequirement(Quest.IN_AID_OF_THE_MYREQUE));
add("Kill a Cave Horror.",
new SkillRequirement(Skill.SLAYER, 58),
new QuestRequirement(Quest.CABIN_FEVER));
add("Harvest some Bittercap Mushrooms from the patch in Canifis.",
new SkillRequirement(Skill.FARMING, 53));
add("Pray at the Altar of Nature with Piety activated.",
new SkillRequirement(Skill.PRAYER, 70),
new SkillRequirement(Skill.DEFENCE, 70),
new QuestRequirement(Quest.NATURE_SPIRIT),
new QuestRequirement(Quest.KINGS_RANSOM));
add("Use the shortcut to get to the bridge over the Salve.",
new SkillRequirement(Skill.AGILITY, 65));
add("Mine some Mithril ore in the Abandoned Mine.",
new SkillRequirement(Skill.MINING, 55),
new QuestRequirement(Quest.HAUNTED_MINE));
// ELITE
add("Catch a shark in Burgh de Rott with your bare hands.",
new SkillRequirement(Skill.FISHING, 96),
new SkillRequirement(Skill.STRENGTH, 76),
new QuestRequirement(Quest.IN_AID_OF_THE_MYREQUE));
add("Cremate any Shade remains on a Magic or Redwood pyre.",
new SkillRequirement(Skill.FIREMAKING, 80),
new QuestRequirement(Quest.SHADES_OF_MORTTON));
add("Fertilize the Morytania herb patch using Lunar Magic.",
new SkillRequirement(Skill.MAGIC, 83),
new QuestRequirement(Quest.LUNAR_DIPLOMACY));
add("Craft a Black dragonhide body in Canifis bank.",
new SkillRequirement(Skill.CRAFTING, 84));
add("Kill an Abyssal demon in the Slayer Tower.",
new SkillRequirement(Skill.SLAYER, 85));
add("Loot the Barrows chest while wearing any complete barrows set.",
new SkillRequirement(Skill.DEFENCE, 70),
new OrRequirement(
new SkillRequirement(Skill.ATTACK, 70),
new SkillRequirement(Skill.STRENGTH, 70),
new SkillRequirement(Skill.RANGED, 70),
new SkillRequirement(Skill.MAGIC, 70)
)
);
}
}
@@ -1,119 +0,0 @@
/*
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.achievementdiary.diaries;
import net.runelite.api.Quest;
import net.runelite.api.Skill;
import net.runelite.client.plugins.achievementdiary.CombatLevelRequirement;
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.QuestPointRequirement;
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
public class VarrockDiaryRequirement extends GenericDiaryRequirement
{
public VarrockDiaryRequirement()
{
// EASY
add("Have Aubury teleport you to the Essence mine.",
new QuestRequirement(Quest.RUNE_MYSTERIES));
add("Mine some Iron in the south east mining patch near Varrock.",
new SkillRequirement(Skill.MINING, 15));
add("Jump over the fence south of Varrock.",
new SkillRequirement(Skill.AGILITY, 13));
add("Spin a bowl on the pottery wheel and fire it in the oven in Barb Village.",
new SkillRequirement(Skill.CRAFTING, 8));
add("Craft some Earth runes.",
new SkillRequirement(Skill.RUNECRAFT, 9));
add("Catch some trout in the River Lum at Barbarian Village.",
new SkillRequirement(Skill.FISHING, 20));
add("Steal from the Tea stall in Varrock.",
new SkillRequirement(Skill.THIEVING, 5));
// MEDIUM
add("Enter the Champions' Guild.",
new QuestPointRequirement(32));
add("Select a colour for your kitten.",
new QuestRequirement(Quest.GARDEN_OF_TRANQUILLITY, true),
new QuestRequirement(Quest.GERTRUDES_CAT));
add("Use the spirit tree north of Varrock.",
new QuestRequirement(Quest.TREE_GNOME_VILLAGE));
add("Enter the Tolna dungeon after completing A Soul's Bane.",
new QuestRequirement(Quest.A_SOULS_BANE));
add("Teleport to the digsite using a Digsite pendant.",
new QuestRequirement(Quest.THE_DIG_SITE));
add("Cast the teleport to Varrock spell.",
new SkillRequirement(Skill.MAGIC, 25));
add("Get a Slayer task from Vannaka.",
new CombatLevelRequirement(40));
add("Pick a White tree fruit.",
new SkillRequirement(Skill.FARMING, 25),
new QuestRequirement(Quest.GARDEN_OF_TRANQUILLITY));
add("Use the balloon to travel from Varrock.",
new SkillRequirement(Skill.FIREMAKING, 40),
new QuestRequirement(Quest.ENLIGHTENED_JOURNEY));
add("Complete a lap of the Varrock Agility course.",
new SkillRequirement(Skill.AGILITY, 30));
// HARD
add("Trade furs with the Fancy Dress Seller for a spottier cape and equip it.",
new SkillRequirement(Skill.HUNTER, 66));
add("Make a Waka Canoe near Edgeville.",
new SkillRequirement(Skill.WOODCUTTING, 57));
add("Teleport to Paddewwa.",
new SkillRequirement(Skill.MAGIC, 54),
new QuestRequirement(Quest.DESERT_TREASURE));
add("Chop some yew logs in Varrock and burn them at the top of the Varrock church.",
new SkillRequirement(Skill.WOODCUTTING, 60),
new SkillRequirement(Skill.FIREMAKING, 60));
add("Have the Varrock estate agent decorate your house with Fancy Stone.",
new SkillRequirement(Skill.CONSTRUCTION, 50));
add("Collect at least 2 yew roots from the Tree patch in Varrock Palace.",
new SkillRequirement(Skill.WOODCUTTING, 60),
new SkillRequirement(Skill.FARMING, 68));
add("Pray at the altar in Varrock palace with Smite active.",
new SkillRequirement(Skill.PRAYER, 52));
add("Squeeze through the obstacle pipe in Edgeville dungeon.",
new SkillRequirement(Skill.AGILITY, 51));
// ELITE
add("Create a super combat potion in Varrock west bank.",
new SkillRequirement(Skill.HERBLORE, 90),
new QuestRequirement(Quest.DRUIDIC_RITUAL));
add("Use Lunar magic to make 20 mahogany planks at the Lumberyard.",
new SkillRequirement(Skill.MAGIC, 86),
new QuestRequirement(Quest.DREAM_MENTOR));
add("Bake a summer pie in the Cooking Guild.",
new SkillRequirement(Skill.COOKING, 95));
add("Smith and fletch ten rune darts within Varrock.",
new SkillRequirement(Skill.SMITHING, 89),
new SkillRequirement(Skill.FLETCHING, 81),
new QuestRequirement(Quest.THE_TOURIST_TRAP));
add("Craft 100 or more earth runes simultaneously.",
new SkillRequirement(Skill.RUNECRAFT, 78),
new QuestRequirement(Quest.RUNE_MYSTERIES));
}
}
@@ -1,145 +0,0 @@
/*
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.achievementdiary.diaries;
import net.runelite.api.Quest;
import net.runelite.api.Skill;
import net.runelite.client.plugins.achievementdiary.CombatLevelRequirement;
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
public class WesternDiaryRequirement extends GenericDiaryRequirement
{
public WesternDiaryRequirement()
{
// EASY
add("Catch a Copper Longtail.",
new SkillRequirement(Skill.HUNTER, 9));
add("Complete a novice game of Pest Control.",
new CombatLevelRequirement(40));
add("Mine some Iron Ore near Piscatoris.",
new SkillRequirement(Skill.MINING, 15));
add("Claim any Chompy bird hat from Rantz.",
new QuestRequirement(Quest.BIG_CHOMPY_BIRD_HUNTING));
add("Have Brimstail teleport you to the Essence mine.",
new QuestRequirement(Quest.RUNE_MYSTERIES));
add("Fletch an Oak shortbow from the Gnome Stronghold.",
new SkillRequirement(Skill.FLETCHING, 20));
// MEDIUM
add("Take the agility shortcut from the Grand Tree to Otto's Grotto.",
new SkillRequirement(Skill.AGILITY, 37),
new QuestRequirement(Quest.TREE_GNOME_VILLAGE),
new QuestRequirement(Quest.THE_GRAND_TREE));
add("Travel to the Gnome Stronghold by Spirit Tree.",
new QuestRequirement(Quest.TREE_GNOME_VILLAGE));
add("Trap a Spined Larupia.",
new SkillRequirement(Skill.HUNTER, 31));
add("Fish some Bass on Ape Atoll.",
new SkillRequirement(Skill.FISHING, 46),
new QuestRequirement(Quest.MONKEY_MADNESS_I, true));
add("Chop and burn some teak logs on Ape Atoll.",
new SkillRequirement(Skill.WOODCUTTING, 35),
new SkillRequirement(Skill.FIREMAKING, 35),
new QuestRequirement(Quest.MONKEY_MADNESS_I));
add("Complete an intermediate game of Pest Control.",
new CombatLevelRequirement(70));
add("Travel to the Feldip Hills by Gnome Glider.",
new QuestRequirement(Quest.ONE_SMALL_FAVOUR),
new QuestRequirement(Quest.THE_GRAND_TREE));
add("Claim a Chompy bird hat from Rantz after registering at least 125 kills.",
new QuestRequirement(Quest.BIG_CHOMPY_BIRD_HUNTING));
add("Travel from Eagles' Peak to the Feldip Hills by Eagle.",
new QuestRequirement(Quest.EAGLES_PEAK));
add("Make a Chocolate Bomb at the Grand Tree.",
new SkillRequirement(Skill.COOKING, 42));
add("Complete a delivery for the Gnome Restaurant.",
new SkillRequirement(Skill.COOKING, 29));
add("Turn your small crystal seed into a Crystal saw.",
new QuestRequirement(Quest.THE_EYES_OF_GLOUPHRIE));
add("Mine some Gold ore underneath the Grand Tree.",
new SkillRequirement(Skill.MINING, 40),
new QuestRequirement(Quest.THE_GRAND_TREE));
// HARD
add("Kill an Elf with a Crystal bow.",
new SkillRequirement(Skill.RANGED, 70),
new SkillRequirement(Skill.AGILITY, 56),
new QuestRequirement(Quest.ROVING_ELVES));
add("Catch and cook a Monkfish in Piscatoris.",
new SkillRequirement(Skill.FISHING, 62),
new SkillRequirement(Skill.COOKING, 62),
new QuestRequirement(Quest.SWAN_SONG));
add("Complete a Veteran game of Pest Control.",
new CombatLevelRequirement(100));
add("Catch a Dashing Kebbit.",
new SkillRequirement(Skill.HUNTER, 69));
add("Complete a lap of the Ape Atoll agility course.",
new SkillRequirement(Skill.AGILITY, 48),
new QuestRequirement(Quest.MONKEY_MADNESS_I));
add("Chop and burn some Mahogany logs on Ape Atoll.",
new SkillRequirement(Skill.WOODCUTTING, 50),
new SkillRequirement(Skill.FIREMAKING, 50),
new QuestRequirement(Quest.MONKEY_MADNESS_I));
add("Mine some Adamantite ore in Tirannwn.",
new SkillRequirement(Skill.MINING, 70),
new QuestRequirement(Quest.REGICIDE));
add("Check the health of your Palm tree in Lletya.",
new SkillRequirement(Skill.FARMING, 68),
new QuestRequirement(Quest.MOURNINGS_END_PART_I, true));
add("Claim a Chompy bird hat from Rantz after registering at least 300 kills.",
new QuestRequirement(Quest.BIG_CHOMPY_BIRD_HUNTING));
add("Build an Isafdar painting in your POH Quest hall.",
new SkillRequirement(Skill.CONSTRUCTION, 65),
new QuestRequirement(Quest.ROVING_ELVES));
add("Kill Zulrah.",
new QuestRequirement(Quest.REGICIDE, true));
add("Teleport to Ape Atoll.",
new SkillRequirement(Skill.MAGIC, 64),
new QuestRequirement(Quest.RECIPE_FOR_DISASTER, true));
add("Pickpocket a Gnome.",
new SkillRequirement(Skill.THIEVING, 75),
new QuestRequirement(Quest.TREE_GNOME_VILLAGE));
// ELITE
add("Fletch a Magic Longbow in Tirannwn.",
new SkillRequirement(Skill.FLETCHING, 85),
new QuestRequirement(Quest.MOURNINGS_END_PART_I));
add("Kill the Thermonuclear Smoke devil (Does not require task).",
new SkillRequirement(Skill.SLAYER, 93));
add("Have Prissy Scilla protect your Magic tree.",
new SkillRequirement(Skill.FARMING, 75));
add("Use the Elven overpass advanced cliffside shortcut.",
new SkillRequirement(Skill.AGILITY, 85),
new QuestRequirement(Quest.UNDERGROUND_PASS));
add("Claim a Chompy bird hat from Rantz after registering at least 1000 kills.",
new QuestRequirement(Quest.BIG_CHOMPY_BIRD_HUNTING));
add("Pickpocket an Elf.",
new SkillRequirement(Skill.THIEVING, 85),
new QuestRequirement(Quest.MOURNINGS_END_PART_I, true));
}
}
@@ -1,111 +0,0 @@
/*
* Copyright (c) 2018, Marshall <https://github.com/marshdevs>
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.achievementdiary.diaries;
import net.runelite.api.Quest;
import net.runelite.api.Skill;
import net.runelite.client.plugins.achievementdiary.GenericDiaryRequirement;
import net.runelite.client.plugins.achievementdiary.OrRequirement;
import net.runelite.client.plugins.achievementdiary.QuestRequirement;
import net.runelite.client.plugins.achievementdiary.SkillRequirement;
public class WildernessDiaryRequirement extends GenericDiaryRequirement
{
public WildernessDiaryRequirement()
{
// EASY
add("Cast Low Alchemy at the Fountain of Rune.",
new SkillRequirement(Skill.MAGIC, 21));
add("Kill an Earth Warrior in the Wilderness beneath Edgeville.",
new SkillRequirement(Skill.AGILITY, 15));
add("Mine some Iron ore in the Wilderness.",
new SkillRequirement(Skill.MINING, 15));
add("Have the Mage of Zamorak teleport you to the Abyss.",
new QuestRequirement(Quest.ENTER_THE_ABYSS));
// MEDIUM
add("Mine some Mithril ore in the wilderness.",
new SkillRequirement(Skill.MINING, 55));
add("Chop some yew logs from a fallen Ent.",
new SkillRequirement(Skill.WOODCUTTING, 61));
add("Enter the Wilderness Godwars Dungeon.",
new OrRequirement(
new SkillRequirement(Skill.AGILITY, 60),
new SkillRequirement(Skill.STRENGTH, 60)
)
);
add("Complete a lap of the Wilderness Agility course.",
new SkillRequirement(Skill.AGILITY, 52));
add("Charge an Earth Orb.",
new SkillRequirement(Skill.MAGIC, 60));
add("Kill a Bloodveld in the Wilderness Godwars Dungeon.",
new SkillRequirement(Skill.SLAYER, 50));
add("Smith a Golden helmet in the Resource Area.",
new SkillRequirement(Skill.SMITHING, 50),
new QuestRequirement(Quest.BETWEEN_A_ROCK, true));
// HARD
add("Cast one of the 3 God spells against another player in the Wilderness.",
new SkillRequirement(Skill.MAGIC, 60),
new QuestRequirement(Quest.THE_MAGE_ARENA));
add("Charge an Air Orb.",
new SkillRequirement(Skill.MAGIC, 66));
add("Catch a Black Salamander in the Wilderness.",
new SkillRequirement(Skill.HUNTER, 67));
add("Smith an Adamant scimitar in the Resource Area.",
new SkillRequirement(Skill.SMITHING, 75));
add("Take the agility shortcut from Trollheim into the Wilderness.",
new SkillRequirement(Skill.AGILITY, 64),
new QuestRequirement(Quest.DEATH_PLATEAU));
add("Kill a Spiritual warrior in the Wilderness Godwars Dungeon.",
new SkillRequirement(Skill.SLAYER, 68));
add("Fish some Raw Lava Eel in the Wilderness.",
new SkillRequirement(Skill.FISHING, 53));
// ELITE
add("Teleport to Ghorrock.",
new SkillRequirement(Skill.MAGIC, 96),
new QuestRequirement(Quest.DESERT_TREASURE));
add("Fish and Cook a Dark Crab in the Resource Area.",
new SkillRequirement(Skill.FISHING, 85),
new SkillRequirement(Skill.COOKING, 90));
add("Smith a rune scimitar from scratch in the Resource Area.",
new SkillRequirement(Skill.MINING, 85),
new SkillRequirement(Skill.SMITHING, 90));
add("Steal from the Rogues' chest.",
new SkillRequirement(Skill.THIEVING, 84));
add("Slay a spiritual mage inside the wilderness Godwars Dungeon.",
new SkillRequirement(Skill.SLAYER, 83),
new OrRequirement(
new SkillRequirement(Skill.AGILITY, 60),
new SkillRequirement(Skill.STRENGTH, 60)
)
);
add("Cut and burn some magic logs in the Resource Area.",
new SkillRequirement(Skill.WOODCUTTING, 75),
new SkillRequirement(Skill.FIREMAKING, 75));
}
}
@@ -1,39 +0,0 @@
/*
* Copyright (c) 2018, Alex Kolpa <https://github.com/AlexKolpa>
* 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.agility;
import java.awt.image.BufferedImage;
import java.time.temporal.ChronoUnit;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.ui.overlay.infobox.Timer;
class AgilityArenaTimer extends Timer
{
AgilityArenaTimer(Plugin plugin, BufferedImage image)
{
super(1, ChronoUnit.MINUTES, image, plugin);
setTooltip("Time left until location changes");
}
}
@@ -1,251 +0,0 @@
/*
* Copyright (c) 2018, Cas <https://github.com/casvandongen>
* 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.agility;
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.ConfigTitleSection;
import net.runelite.client.config.Title;
import net.runelite.client.config.Units;
@ConfigGroup("agility")
public interface AgilityConfig extends Config
{
@ConfigTitleSection(
keyName = "mainConfig",
position = 0,
name = "Main Config",
description = ""
)
default Title mainConfig()
{
return new Title();
}
@ConfigItem(
keyName = "removeDistanceCap",
name = "Remove Distance Cap",
description = "This will remove the distance cap on rendering overlays for agility.",
warning = "<html><center>Enabling this setting on a low end machine may severely affect your fps." +
"<br>Click yes to enable this setting, knowing it might affect performance.</center></html>",
position = 0,
titleSection = "mainConfig"
)
default boolean removeDistanceCap()
{
return false;
}
@ConfigItem(
keyName = "showCourseClickboxes",
name = "Show course Clickboxes",
description = "Show agility course obstacle clickboxes",
position = 1
)
default boolean showCourseClickboxes()
{
return true;
}
@ConfigItem(
keyName = "showLapCount",
name = "Show Lap Count",
description = "Enable/disable the lap counter",
position = 2,
titleSection = "mainConfig"
)
default boolean showLapCount()
{
return true;
}
@ConfigItem(
keyName = "lapTimeout",
name = "Hide Lap Count",
description = "Time in minutes until the lap counter hides/resets",
position = 3,
titleSection = "mainConfig",
hidden = true,
unhide = "showLapCount"
)
@Units(Units.MINUTES)
default int lapTimeout()
{
return 5;
}
@ConfigItem(
keyName = "lapsToLevel",
name = "Show Laps Until Level",
description = "Show number of laps remaining until next level is reached.",
position = 4,
titleSection = "mainConfig",
hidden = true,
unhide = "showLapCount"
)
default boolean lapsToLevel()
{
return false;
}
@ConfigItem(
keyName = "lapsToGoal",
name = "Show Laps Until Goal",
description = "Show number of laps remaining until experience tracker goal is reached",
position = 5,
titleSection = "mainConfig",
hidden = true,
unhide = "showLapCount"
)
default boolean lapsToGoal()
{
return false;
}
@ConfigItem(
keyName = "agilityArenaTimer",
name = "Agility Arena timer",
description = "Configures whether Agility Arena timer is displayed",
position = 6,
titleSection = "mainConfig"
)
default boolean showAgilityArenaTimer()
{
return true;
}
@ConfigItem(
keyName = "addLevelsToShortcutOptions",
name = "Show Shortcut Agility Reqs",
description = "Enable/disable showing shortcut agility level requirements in right-click options",
position = 7,
titleSection = "mainConfig"
)
default boolean showShortcutLevel()
{
return false;
}
@ConfigTitleSection(
keyName = "miscConfig",
position = 8,
name = "Misc Config",
description = ""
)
default Title miscConfig()
{
return new Title();
}
@ConfigItem(
keyName = "highlightMarks",
name = "Highlight Marks of Grace",
description = "Enable/disable the highlighting of retrievable Marks of Grace",
position = 9,
titleSection = "miscConfig"
)
default boolean highlightMarks()
{
return true;
}
@ConfigItem(
keyName = "highlightShortcuts",
name = "Highlight Agility Shortcuts",
description = "Enable/disable the highlighting of Agility shortcuts",
position = 10,
titleSection = "miscConfig"
)
default boolean highlightShortcuts()
{
return true;
}
@ConfigItem(
keyName = "showTrapOverlay",
name = "Highlight Traps",
description = "Enable/disable the highlighting of traps on Agility courses",
position = 11,
titleSection = "miscConfig"
)
default boolean showTrapOverlay()
{
return false;
}
@ConfigItem(
keyName = "agilityArenaNotifier",
name = "Agility Arena notifier",
description = "Notify on ticket location change in Agility Arena",
position = 12,
titleSection = "miscConfig"
)
default boolean notifyAgilityArena()
{
return false;
}
@ConfigItem(
keyName = "overlayColor",
name = "Global Overlay Color",
description = "Color of Agility overlay",
position = 13,
titleSection = "miscConfig"
)
default Color getOverlayColor()
{
return Color.GREEN;
}
@ConfigItem(
keyName = "trapHighlight",
name = "Trap Overlay Color",
description = "Color of Agility trap overlay",
position = 14,
titleSection = "miscConfig",
hidden = true,
unhide = "showTrapOverlay"
)
default Color getTrapColor()
{
return Color.RED;
}
@ConfigItem(
keyName = "markHighlight",
name = "Mark Highlight Color",
description = "Color of highlighted Marks of Grace",
position = 15,
titleSection = "miscConfig",
hidden = true,
unhide = "highlightMarks"
)
default Color getMarkColor()
{
return Color.RED;
}
}
@@ -1,140 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* Copyright (c) 2018, Cas <https://github.com/casvandongen>
* 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.agility;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Shape;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.api.Client;
import net.runelite.api.Point;
import net.runelite.api.Tile;
import net.runelite.api.coords.LocalPoint;
import net.runelite.client.game.AgilityShortcut;
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;
@Singleton
class AgilityOverlay extends Overlay
{
private static final int MAX_DISTANCE = 2350;
private static final Color SHORTCUT_HIGH_LEVEL_COLOR = Color.ORANGE;
private final Client client;
private final AgilityPlugin plugin;
@Inject
private AgilityOverlay(final Client client, final AgilityPlugin plugin)
{
super(plugin);
setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.ABOVE_SCENE);
this.client = client;
this.plugin = plugin;
}
@Override
public Dimension render(Graphics2D graphics)
{
LocalPoint playerLocation = client.getLocalPlayer().getLocalLocation();
Point mousePosition = client.getMouseCanvasPosition();
final List<Tile> marksOfGrace = plugin.getMarksOfGrace();
plugin.getObstacles().forEach((object, obstacle) ->
{
if (Obstacles.SHORTCUT_OBSTACLE_IDS.containsKey(object.getId()) && !plugin.isHighlightShortcuts() ||
Obstacles.TRAP_OBSTACLE_IDS.contains(object.getId()) && !plugin.isShowTrapOverlay() ||
Obstacles.COURSE_OBSTACLE_IDS.contains(object.getId()) && !plugin.isShowCourseClickboxes())
{
return;
}
Tile tile = obstacle.getTile();
if (tile.getPlane() == client.getPlane() && checkDistance(object.getLocalLocation(), playerLocation))
{
// This assumes that the obstacle is not clickable.
if (Obstacles.TRAP_OBSTACLE_IDS.contains(object.getId()))
{
Polygon polygon = object.getCanvasTilePoly();
if (polygon != null)
{
OverlayUtil.renderPolygon(graphics, polygon, plugin.getTrapColor());
}
return;
}
Shape objectClickbox = object.getClickbox();
if (objectClickbox != null)
{
AgilityShortcut agilityShortcut = obstacle.getShortcut();
Color configColor = agilityShortcut == null || agilityShortcut.getLevel() <= plugin.getAgilityLevel() ? plugin.getOverlayColor() : SHORTCUT_HIGH_LEVEL_COLOR;
if (plugin.isHighlightMarks() && !marksOfGrace.isEmpty())
{
configColor = plugin.getMarkColor();
}
OverlayUtil.renderClickBox(graphics, mousePosition, objectClickbox, configColor);
}
}
});
if (plugin.isHighlightMarks() && !marksOfGrace.isEmpty())
{
for (Tile markOfGraceTile : marksOfGrace)
{
if (markOfGraceTile.getPlane() == client.getPlane() && markOfGraceTile.getItemLayer() != null
&& checkDistance(markOfGraceTile.getLocalLocation(), playerLocation))
{
final Polygon poly = markOfGraceTile.getItemLayer().getCanvasTilePoly();
if (poly == null)
{
continue;
}
OverlayUtil.renderPolygon(graphics, poly, plugin.getMarkColor());
}
}
}
return null;
}
private boolean checkDistance(LocalPoint localPoint, LocalPoint playerPoint)
{
if (plugin.isRemoveDistanceCap())
{
return true;
}
return localPoint.distanceTo(playerPoint) < MAX_DISTANCE;
}
}
@@ -1,569 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.agility;
import com.google.common.primitives.Ints;
import com.google.inject.Provides;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.ItemID;
import static net.runelite.api.ItemID.AGILITY_ARENA_TICKET;
import net.runelite.api.MenuEntry;
import net.runelite.api.MenuOpcode;
import net.runelite.api.Player;
import net.runelite.api.Skill;
import static net.runelite.api.Skill.AGILITY;
import net.runelite.api.Tile;
import net.runelite.api.TileItem;
import net.runelite.api.TileObject;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.BeforeRender;
import net.runelite.api.events.DecorativeObjectChanged;
import net.runelite.api.events.DecorativeObjectDespawned;
import net.runelite.api.events.DecorativeObjectSpawned;
import net.runelite.api.events.GameObjectChanged;
import net.runelite.api.events.GameObjectDespawned;
import net.runelite.api.events.GameObjectSpawned;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.GroundObjectChanged;
import net.runelite.api.events.GroundObjectDespawned;
import net.runelite.api.events.GroundObjectSpawned;
import net.runelite.api.events.ItemDespawned;
import net.runelite.api.events.ItemSpawned;
import net.runelite.api.events.MenuOpened;
import net.runelite.api.events.StatChanged;
import net.runelite.api.events.WallObjectChanged;
import net.runelite.api.events.WallObjectDespawned;
import net.runelite.api.events.WallObjectSpawned;
import net.runelite.client.Notifier;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.game.AgilityShortcut;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.PluginType;
import net.runelite.client.ui.overlay.OverlayManager;
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
import net.runelite.client.util.ColorUtil;
@PluginDescriptor(
name = "Agility",
description = "Show helpful information about agility courses and obstacles",
tags = {"grace", "marks", "overlay", "shortcuts", "skilling", "traps"},
type = PluginType.SKILLING
)
@Slf4j
@Singleton
public class AgilityPlugin extends Plugin
{
private static final int AGILITY_ARENA_REGION_ID = 11157;
private static final Object MENU_SUBS = new Object();
@Getter(AccessLevel.PACKAGE)
private final Map<TileObject, Obstacle> obstacles = new HashMap<>();
@Getter(AccessLevel.PACKAGE)
private final List<Tile> marksOfGrace = new ArrayList<>();
@Inject
private OverlayManager overlayManager;
@Inject
private AgilityOverlay agilityOverlay;
@Inject
private LapCounterOverlay lapCounterOverlay;
@Inject
private Notifier notifier;
@Inject
private Client client;
@Inject
private InfoBoxManager infoBoxManager;
@Inject
private AgilityConfig config;
@Inject
private ItemManager itemManager;
@Inject
private EventBus eventBus;
@Getter(AccessLevel.PACKAGE)
private AgilitySession session;
private int lastAgilityXp;
private WorldPoint lastArenaTicketPosition;
@Getter(AccessLevel.PACKAGE)
private int agilityLevel;
// Config values
@Getter(AccessLevel.PACKAGE)
private boolean removeDistanceCap;
@Getter(AccessLevel.PACKAGE)
private boolean showCourseClickboxes;
@Getter(AccessLevel.PACKAGE)
private boolean showLapCount;
@Getter(AccessLevel.PACKAGE)
private int lapTimeout;
@Getter(AccessLevel.PACKAGE)
private boolean lapsToLevel;
@Getter(AccessLevel.PACKAGE)
private boolean lapsToGoal;
@Getter(AccessLevel.PACKAGE)
private Color overlayColor;
@Getter(AccessLevel.PACKAGE)
private boolean highlightMarks;
@Getter(AccessLevel.PACKAGE)
private Color markColor;
@Getter(AccessLevel.PACKAGE)
private boolean highlightShortcuts;
@Getter(AccessLevel.PACKAGE)
private boolean showTrapOverlay;
@Getter(AccessLevel.PACKAGE)
private Color trapColor;
private boolean notifyAgilityArena;
private boolean showAgilityArenaTimer;
@Provides
AgilityConfig getConfig(ConfigManager configManager)
{
return configManager.getConfig(AgilityConfig.class);
}
@Override
protected void startUp()
{
updateConfig();
if (config.showShortcutLevel())
{
addMenuSubscriptions();
}
overlayManager.add(agilityOverlay);
overlayManager.add(lapCounterOverlay);
agilityLevel = client.getBoostedSkillLevel(Skill.AGILITY);
}
@Override
protected void shutDown()
{
eventBus.unregister(MENU_SUBS);
overlayManager.remove(agilityOverlay);
overlayManager.remove(lapCounterOverlay);
marksOfGrace.clear();
obstacles.clear();
session = null;
agilityLevel = 0;
}
private void addMenuSubscriptions()
{
eventBus.subscribe(BeforeRender.class, MENU_SUBS, this::onBeforeRender);
eventBus.subscribe(MenuOpened.class, MENU_SUBS, this::onMenuOpened);
}
@Subscribe
private void onGameStateChanged(GameStateChanged event)
{
switch (event.getGameState())
{
case HOPPING:
case LOGIN_SCREEN:
session = null;
lastArenaTicketPosition = null;
removeAgilityArenaTimer();
break;
case LOADING:
marksOfGrace.clear();
obstacles.clear();
break;
case LOGGED_IN:
if (!isInAgilityArena())
{
lastArenaTicketPosition = null;
removeAgilityArenaTimer();
}
break;
}
}
@Subscribe
private void onConfigChanged(ConfigChanged event)
{
if (!event.getGroup().equals("agility"))
{
return;
}
if ("addLevelsToShortcutOptions".equals(event.getKey()))
{
if (config.showShortcutLevel())
{
addMenuSubscriptions();
}
else
{
eventBus.unregister(MENU_SUBS);
}
return;
}
updateConfig();
if (!this.showAgilityArenaTimer)
{
removeAgilityArenaTimer();
}
}
private void updateConfig()
{
this.removeDistanceCap = config.removeDistanceCap();
this.showCourseClickboxes = config.showCourseClickboxes();
this.showLapCount = config.showLapCount();
this.lapTimeout = config.lapTimeout();
this.lapsToLevel = config.lapsToLevel();
this.lapsToGoal = config.lapsToGoal();
this.overlayColor = config.getOverlayColor();
this.highlightMarks = config.highlightMarks();
this.markColor = config.getMarkColor();
this.highlightShortcuts = config.highlightShortcuts();
this.showTrapOverlay = config.showTrapOverlay();
this.trapColor = config.getTrapColor();
this.notifyAgilityArena = config.notifyAgilityArena();
this.showAgilityArenaTimer = config.showAgilityArenaTimer();
}
@Subscribe
public void onStatChanged(StatChanged statChanged)
{
if (statChanged.getSkill() != AGILITY)
{
return;
}
agilityLevel = statChanged.getBoostedLevel();
if (!this.showLapCount)
{
return;
}
// Determine how much EXP was actually gained
int agilityXp = client.getSkillExperience(AGILITY);
int skillGained = agilityXp - lastAgilityXp;
lastAgilityXp = agilityXp;
// Get course
Courses course = Courses.getCourse(client.getLocalPlayer().getWorldLocation().getRegionID());
if (course == null
|| (course.getCourseEndWorldPoints().length == 0
? Math.abs(course.getLastObstacleXp() - skillGained) > 1
: Arrays.stream(course.getCourseEndWorldPoints()).noneMatch(wp -> wp.equals(client.getLocalPlayer().getWorldLocation()))))
{
return;
}
if (session != null && session.getCourse() == course)
{
session.incrementLapCount(client);
}
else
{
session = new AgilitySession(course);
// New course found, reset lap count and set new course
session.resetLapCount();
session.incrementLapCount(client);
}
}
@Subscribe
private void onItemSpawned(ItemSpawned itemSpawned)
{
if (obstacles.isEmpty())
{
return;
}
final TileItem item = itemSpawned.getItem();
final Tile tile = itemSpawned.getTile();
if (item.getId() == ItemID.MARK_OF_GRACE)
{
marksOfGrace.add(tile);
}
}
@Subscribe
private void onItemDespawned(ItemDespawned itemDespawned)
{
final Tile tile = itemDespawned.getTile();
marksOfGrace.remove(tile);
}
@Subscribe
private void onGameTick(GameTick tick)
{
if (isInAgilityArena())
{
// Hint arrow has no plane, and always returns the current plane
WorldPoint newTicketPosition = client.getHintArrowPoint();
WorldPoint oldTickPosition = lastArenaTicketPosition;
lastArenaTicketPosition = newTicketPosition;
if (oldTickPosition != null && newTicketPosition != null
&& (oldTickPosition.getX() != newTicketPosition.getX() || oldTickPosition.getY() != newTicketPosition.getY()))
{
log.debug("Ticked position moved from {} to {}", oldTickPosition, newTicketPosition);
if (this.notifyAgilityArena)
{
notifier.notify("Ticket location changed");
}
if (this.showAgilityArenaTimer)
{
showNewAgilityArenaTimer();
}
}
}
}
private boolean isInAgilityArena()
{
Player local = client.getLocalPlayer();
if (local == null)
{
return false;
}
WorldPoint location = local.getWorldLocation();
return location.getRegionID() == AGILITY_ARENA_REGION_ID;
}
private void removeAgilityArenaTimer()
{
infoBoxManager.removeIf(infoBox -> infoBox instanceof AgilityArenaTimer);
}
private void showNewAgilityArenaTimer()
{
removeAgilityArenaTimer();
infoBoxManager.addInfoBox(new AgilityArenaTimer(this, itemManager.getImage(AGILITY_ARENA_TICKET)));
}
@Subscribe
private void onGameObjectSpawned(GameObjectSpawned event)
{
onTileObject(event.getTile(), null, event.getGameObject());
}
@Subscribe
private void onGameObjectChanged(GameObjectChanged event)
{
onTileObject(event.getTile(), event.getPrevious(), event.getGameObject());
}
@Subscribe
private void onGameObjectDespawned(GameObjectDespawned event)
{
onTileObject(event.getTile(), event.getGameObject(), null);
}
@Subscribe
private void onGroundObjectSpawned(GroundObjectSpawned event)
{
onTileObject(event.getTile(), null, event.getGroundObject());
}
@Subscribe
private void onGroundObjectChanged(GroundObjectChanged event)
{
onTileObject(event.getTile(), event.getPrevious(), event.getGroundObject());
}
@Subscribe
private void onGroundObjectDespawned(GroundObjectDespawned event)
{
onTileObject(event.getTile(), event.getGroundObject(), null);
}
@Subscribe
private void onWallObjectSpawned(WallObjectSpawned event)
{
onTileObject(event.getTile(), null, event.getWallObject());
}
@Subscribe
private void onWallObjectChanged(WallObjectChanged event)
{
onTileObject(event.getTile(), event.getPrevious(), event.getWallObject());
}
@Subscribe
private void onWallObjectDespawned(WallObjectDespawned event)
{
onTileObject(event.getTile(), event.getWallObject(), null);
}
@Subscribe
private void onDecorativeObjectSpawned(DecorativeObjectSpawned event)
{
onTileObject(event.getTile(), null, event.getDecorativeObject());
}
@Subscribe
private void onDecorativeObjectChanged(DecorativeObjectChanged event)
{
onTileObject(event.getTile(), event.getPrevious(), event.getDecorativeObject());
}
@Subscribe
private void onDecorativeObjectDespawned(DecorativeObjectDespawned event)
{
onTileObject(event.getTile(), event.getDecorativeObject(), null);
}
private void onTileObject(Tile tile, TileObject oldObject, TileObject newObject)
{
obstacles.remove(oldObject);
if (newObject == null)
{
return;
}
if (Obstacles.COURSE_OBSTACLE_IDS.contains(newObject.getId()) ||
(Obstacles.TRAP_OBSTACLE_IDS.contains(newObject.getId())
&& Obstacles.TRAP_OBSTACLE_REGIONS.contains(newObject.getWorldLocation().getRegionID())))
{
obstacles.put(newObject, new Obstacle(tile, null));
}
if (Obstacles.SHORTCUT_OBSTACLE_IDS.containsKey(newObject.getId()))
{
AgilityShortcut closestShortcut = null;
int distance = -1;
// Find the closest shortcut to this object
for (AgilityShortcut shortcut : Obstacles.SHORTCUT_OBSTACLE_IDS.get(newObject.getId()))
{
if (shortcut.getWorldLocation() == null)
{
closestShortcut = shortcut;
break;
}
else
{
int newDistance = shortcut.getWorldLocation().distanceTo2D(newObject.getWorldLocation());
if (closestShortcut == null || newDistance < distance)
{
closestShortcut = shortcut;
distance = newDistance;
}
}
}
if (closestShortcut != null)
{
obstacles.put(newObject, new Obstacle(tile, closestShortcut));
}
}
}
private void onBeforeRender(BeforeRender event)
{
if (client.isMenuOpen() || client.getMenuOptionCount() <= 0)
{
return;
}
final MenuEntry entry = client.getLeftClickMenuEntry();
if (checkAndModify(entry))
{
client.setLeftClickMenuEntry(entry);
}
}
private void onMenuOpened(MenuOpened event)
{
boolean changed = false;
for (MenuEntry entry : event.getMenuEntries())
{
changed |= checkAndModify(entry);
}
if (changed)
{
event.setModified();
}
}
private boolean checkAndModify(MenuEntry old)
{
//Guarding against non-first option because agility shortcuts are always that type of event.
if (old.getOpcode() != MenuOpcode.GAME_OBJECT_FIRST_OPTION.getId())
{
return false;
}
for (Obstacle nearbyObstacle : getObstacles().values())
{
AgilityShortcut shortcut = nearbyObstacle.getShortcut();
if (shortcut != null && Ints.contains(shortcut.getObstacleIds(), old.getIdentifier()))
{
final int reqLevel = shortcut.getLevel();
final String requirementText = ColorUtil.getLevelColorString(reqLevel, getAgilityLevel()) + " (level-" + reqLevel + ")";
old.setTarget(old.getTarget() + requirementText);
return true;
}
}
return false;
}
}
@@ -1,77 +0,0 @@
/*
* Copyright (c) 2018, Seth <http://github.com/sethtroll>
* 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.agility;
import java.time.Instant;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import net.runelite.api.Client;
import net.runelite.api.Experience;
import net.runelite.api.Skill;
import net.runelite.api.VarPlayer;
@Getter(AccessLevel.PACKAGE)
@Setter(AccessLevel.PACKAGE)
class AgilitySession
{
private final Courses course;
private Instant lastLapCompleted;
private int totalLaps;
private int lapsTillLevel;
private int lapsTillGoal;
AgilitySession(final Courses course)
{
this.course = course;
}
void incrementLapCount(Client client)
{
lastLapCompleted = Instant.now();
++totalLaps;
int currentExp = client.getSkillExperience(Skill.AGILITY);
int nextLevel = client.getRealSkillLevel(Skill.AGILITY) + 1;
int remainingXp;
do
{
remainingXp = nextLevel <= Experience.MAX_VIRT_LEVEL ? Experience.getXpForLevel(nextLevel) - currentExp : 0;
nextLevel++;
} while (remainingXp < 0);
lapsTillLevel = remainingXp > 0 ? (int) Math.ceil(remainingXp / course.getTotalXp()) : 0;
int goalRemainingXp = client.getVar(VarPlayer.AGILITY_GOAL_END) - currentExp;
lapsTillGoal = goalRemainingXp > 0 ? (int) Math.ceil(goalRemainingXp / course.getTotalXp()) : 0;
}
void resetLapCount()
{
totalLaps = 0;
lapsTillLevel = 0;
lapsTillGoal = 0;
}
}
@@ -1,91 +0,0 @@
/*
* Copyright (c) 2018, Seth <http://github.com/sethtroll>
* 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.agility;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.api.coords.WorldPoint;
enum Courses
{
GNOME(86.5, 46, 9781),
DRAYNOR(120.0, 79, 12338),
AL_KHARID(180.0, 30, 13105, new WorldPoint(3299, 3194, 0)),
PYRAMID(722.0, 300, 13356, new WorldPoint(3364, 2830, 0)),
VARROCK(238.0, 125, 12853),
PENGUIN(540.0, 65, 10559),
BARBARIAN(139.5, 60, 10039),
CANIFIS(240.0, 175, 13878),
APE_ATOLL(580.0, 300, 11050),
FALADOR(440, 180, 12084),
WILDERNESS(571.0, 499, 11837),
WEREWOLF(730.0, 380, 14234),
SEERS(570.0, 435, 10806),
POLLNIVNEACH(890.0, 540, 13358),
RELLEKA(780.0, 475, 10553),
PRIFDDINAS(1337.0, 1037, 12895),
ARDOUGNE(793.0, 529, 10547);
private final static Map<Integer, Courses> coursesByRegion;
@Getter(AccessLevel.PACKAGE)
private final double totalXp;
@Getter(AccessLevel.PACKAGE)
private final int lastObstacleXp;
@Getter(AccessLevel.PACKAGE)
private final int regionId;
@Getter(AccessLevel.PACKAGE)
private final WorldPoint[] courseEndWorldPoints;
static
{
ImmutableMap.Builder<Integer, Courses> builder = new ImmutableMap.Builder<>();
for (Courses course : values())
{
builder.put(course.regionId, course);
}
coursesByRegion = builder.build();
}
Courses(double totalXp, int lastObstacleXp, int regionId, WorldPoint... courseEndWorldPoints)
{
this.totalXp = totalXp;
this.lastObstacleXp = lastObstacleXp;
this.regionId = regionId;
this.courseEndWorldPoints = courseEndWorldPoints;
}
static Courses getCourse(int regionId)
{
return coursesByRegion.get(regionId);
}
}
@@ -1,102 +0,0 @@
/*
* Copyright (c) 2018, Seth <http://github.com/sethtroll>
* 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.agility;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.time.Duration;
import java.time.Instant;
import javax.inject.Inject;
import javax.inject.Singleton;
import static net.runelite.api.MenuOpcode.RUNELITE_OVERLAY_CONFIG;
import net.runelite.client.ui.overlay.Overlay;
import static net.runelite.client.ui.overlay.OverlayManager.OPTION_CONFIGURE;
import net.runelite.client.ui.overlay.OverlayMenuEntry;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.OverlayPriority;
import net.runelite.client.ui.overlay.components.PanelComponent;
import net.runelite.client.ui.overlay.components.table.TableAlignment;
import net.runelite.client.ui.overlay.components.table.TableComponent;
@Singleton
class LapCounterOverlay extends Overlay
{
private final AgilityPlugin plugin;
private final PanelComponent panelComponent = new PanelComponent();
@Inject
private LapCounterOverlay(final AgilityPlugin plugin)
{
super(plugin);
setPosition(OverlayPosition.TOP_LEFT);
setPriority(OverlayPriority.LOW);
this.plugin = plugin;
getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY_CONFIG, OPTION_CONFIGURE, "Agility overlay"));
}
@Override
public Dimension render(Graphics2D graphics)
{
AgilitySession session = plugin.getSession();
if (!plugin.isShowLapCount() ||
session == null ||
session.getLastLapCompleted() == null ||
session.getCourse() == null)
{
return null;
}
Duration lapTimeout = Duration.ofMinutes(plugin.getLapTimeout());
Duration sinceLap = Duration.between(session.getLastLapCompleted(), Instant.now());
if (sinceLap.compareTo(lapTimeout) >= 0)
{
// timeout session
session.setLastLapCompleted(null);
return null;
}
panelComponent.getChildren().clear();
TableComponent tableComponent = new TableComponent();
tableComponent.setColumnAlignments(TableAlignment.LEFT, TableAlignment.RIGHT);
tableComponent.addRow("Total Laps:", Integer.toString(session.getTotalLaps()));
if (plugin.isLapsToLevel() && session.getLapsTillLevel() > 0)
{
tableComponent.addRow("Laps until level:", Integer.toString(session.getLapsTillLevel()));
}
if (plugin.isLapsToGoal() && session.getLapsTillGoal() > 0)
{
tableComponent.addRow("Laps until goal:", Integer.toString(session.getLapsTillGoal()));
}
panelComponent.getChildren().add(tableComponent);
return panelComponent.render(graphics);
}
}
@@ -1,41 +0,0 @@
/*
* Copyright (c) 2019, MrGroggle
* 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 HOLDER 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.agility;
import javax.annotation.Nullable;
import lombok.AllArgsConstructor;
import lombok.Value;
import net.runelite.api.Tile;
import net.runelite.client.game.AgilityShortcut;
@Value
@AllArgsConstructor
class Obstacle
{
private final Tile tile;
@Nullable
private final AgilityShortcut shortcut;
}
@@ -1,123 +0,0 @@
/*
* Copyright (c) 2018, SomeoneWithAnInternetConnection
* 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.agility;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import java.util.List;
import java.util.Set;
import static net.runelite.api.NullObjectID.*;
import static net.runelite.api.ObjectID.*;
import net.runelite.client.game.AgilityShortcut;
class Obstacles
{
static final Set<Integer> COURSE_OBSTACLE_IDS = ImmutableSet.of(
// Gnome
OBSTACLE_NET_23134, TREE_BRANCH_23559, TREE_BRANCH_23560, OBSTACLE_NET_23135, OBSTACLE_PIPE_23138,
OBSTACLE_PIPE_23139, LOG_BALANCE_23145, BALANCING_ROPE_23557,
// Brimhaven
PLANK_3572, PLANK_3571, PLANK_3570, ROPE_SWING, PILLAR_3578, LOW_WALL, LOG_BALANCE, LOG_BALANCE_3557,
BALANCING_LEDGE_3561, BALANCING_LEDGE, MONKEY_BARS_3564, BALANCING_ROPE, HAND_HOLDS_3583,
// Draynor
ROUGH_WALL, TIGHTROPE, TIGHTROPE_11406, NARROW_WALL, WALL_11630, GAP_11631, CRATE_11632, STILE_7527,
// Al-Kharid
ROUGH_WALL_11633, TIGHTROPE_14398, CABLE, ZIP_LINE_14403, TROPICAL_TREE_14404, ROOF_TOP_BEAMS,
TIGHTROPE_14409, GAP_14399,
// Pyramid
STAIRS_10857, LOW_WALL_10865, LEDGE_10860, PLANK_10868, GAP_10882, LEDGE_10886, STAIRS_10857, GAP_10884,
GAP_10859, GAP_10861, LOW_WALL_10865, GAP_10859, LEDGE_10888, PLANK_10868, CLIMBING_ROCKS_10851, DOORWAY_10855,
// Varrock
ROUGH_WALL_14412, CLOTHES_LINE, GAP_14414, WALL_14832, GAP_14833, GAP_14834, GAP_14835, LEDGE_14836, EDGE,
// Penguin
STEPPING_STONE_21120, STEPPING_STONE_21126, STEPPING_STONE_21128, STEPPING_STONE_21129,
STEPPING_STONE_21130, STEPPING_STONE_21131, STEPPING_STONE_21132, STEPPING_STONE_21133,
ICICLES, ICE, ICE_21149, ICE_21150, ICE_21151, ICE_21152, ICE_21153, ICE_21154, ICE_21155, ICE_21156,
// Barbarian
ROPESWING_23131, LOG_BALANCE_23144, OBSTACLE_NET_20211, BALANCING_LEDGE_23547, LADDER_16682, CRUMBLING_WALL_1948,
// Canifis
TALL_TREE_14843, GAP_14844, GAP_14845, GAP_14848, GAP_14846, POLEVAULT, GAP_14847, GAP_14897,
// Ape atoll
STEPPING_STONE_15412, TROPICAL_TREE_15414, MONKEYBARS_15417, SKULL_SLOPE_15483, ROPE_15487, TROPICAL_TREE_16062,
// Falador
ROUGH_WALL_14898, TIGHTROPE_14899, HAND_HOLDS_14901, GAP_14903, GAP_14904, TIGHTROPE_14905,
TIGHTROPE_14911, GAP_14919, LEDGE_14920, LEDGE_14921, LEDGE_14922, LEDGE_14923, LEDGE_14924, EDGE_14925,
// Wilderness
OBSTACLE_PIPE_23137, ROPESWING_23132, STEPPING_STONE_23556, LOG_BALANCE_23542, ROCKS_23640,
// Seers
WALL_14927, GAP_14928, TIGHTROPE_14932, GAP_14929, GAP_14930, EDGE_14931,
// Dorgesh-Kaan
CABLE_22569, CABLE_22572, LADDER_22564, JUTTING_WALL_22552, TUNNEL_22557, PYLON_22664,
CONSOLE, BOILER_22635, STAIRS_22650, STAIRS_22651, STAIRS_22609, STAIRS_22608,
// Pollniveach
BASKET_14935, MARKET_STALL_14936, BANNER_14937, GAP_14938, TREE_14939, ROUGH_WALL_14940,
MONKEYBARS, TREE_14944, DRYING_LINE,
// Rellaka
ROUGH_WALL_14946, GAP_14947, TIGHTROPE_14987, GAP_14990, GAP_14991, TIGHTROPE_14992, PILE_OF_FISH,
// Ardougne
WOODEN_BEAMS, GAP_15609, PLANK_26635, GAP_15610, GAP_15611, STEEP_ROOF, GAP_15612,
// Meiyerditch
NULL_12945, ROCK_17958, ROCK_17959, ROCK_17960, BOAT_17961, NULL_18122, NULL_18124, WALL_RUBBLE,
WALL_RUBBLE_18038, FLOORBOARDS, FLOORBOARDS_18071, FLOORBOARDS_18072, FLOORBOARDS_18073, NULL_18129, NULL_18130,
WALL_18078, NULL_18132, NULL_18133, NULL_18083, TUNNEL_18085, SHELF_18086, SHELF_18087, WALL_18088,
FLOORBOARDS_18089, FLOORBOARDS_18090, DOOR_18091, FLOORBOARDS_18093, FLOORBOARDS_18094, SHELF_18095,
SHELF_18096, FLOORBOARDS_18097, FLOORBOARDS_18098, WASHING_LINE_18099, WASHING_LINE_18100,
NULL_18135, NULL_18136, SHELF_18105, SHELF_18106, SHELF_18107, SHELF_18108, FLOORBOARDS_18109,
FLOORBOARDS_18110, FLOORBOARDS_18112, FLOORBOARDS_18111, FLOORBOARDS_18114, FLOORBOARDS_18113,
NULL_18116, FLOORBOARDS_18117, FLOORBOARDS_18118, STAIRS_DOWN, WALL_17980,
// Werewolf
STEPPING_STONE_11643, HURDLE, HURDLE_11639, HURDLE_11640, PIPE_11657, SKULL_SLOPE, ZIP_LINE,
ZIP_LINE_11645, ZIP_LINE_11646,
// Prifddinas
LADDER_36221, TIGHTROPE_36225, CHIMNEY_36227, ROOF_EDGE, DARK_HOLE_36229, LADDER_36231, LADDER_36232,
ROPE_BRIDGE_36233, TIGHTROPE_36234, ROPE_BRIDGE_36235, TIGHTROPE_36236, TIGHTROPE_36237, DARK_HOLE_36238,
// Prifddinas portals
NULL_36241, NULL_36242, NULL_36243, NULL_36244, NULL_36245, NULL_36246
);
static final Multimap<Integer, AgilityShortcut> SHORTCUT_OBSTACLE_IDS;
static final Set<Integer> TRAP_OBSTACLE_IDS = ImmutableSet.of(
// Agility pyramid
NULL_3550, NULL_10872, NULL_10873
);
static final List<Integer> TRAP_OBSTACLE_REGIONS = ImmutableList.of(12105, 13356);
static
{
final ImmutableMultimap.Builder<Integer, AgilityShortcut> builder = ImmutableMultimap.builder();
for (final AgilityShortcut item : AgilityShortcut.values())
{
for (int obstacle : item.getObstacleIds())
{
builder.put(obstacle, item);
}
}
SHORTCUT_OBSTACLE_IDS = builder.build();
}
}
@@ -1,142 +0,0 @@
/*
* Copyright (c) 2019, Lucas <https://github.com/lucwousin>
* 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.alchemicalhydra;
import java.awt.image.BufferedImage;
import javax.inject.Singleton;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import net.runelite.api.NPC;
import net.runelite.api.Prayer;
import net.runelite.api.ProjectileID;
import net.runelite.api.SpriteID;
import net.runelite.client.game.SpriteManager;
import net.runelite.client.util.ImageUtil;
@Getter(AccessLevel.PACKAGE)
@RequiredArgsConstructor
@Singleton
class AlchemicalHydra
{
@Getter(AccessLevel.PACKAGE)
@RequiredArgsConstructor
enum AttackStyle
{
MAGIC(ProjectileID.HYDRA_MAGIC, Prayer.PROTECT_FROM_MAGIC, SpriteID.PRAYER_PROTECT_FROM_MAGIC),
RANGED(ProjectileID.HYDRA_RANGED, Prayer.PROTECT_FROM_MISSILES, SpriteID.PRAYER_PROTECT_FROM_MISSILES);
private final int projectileID;
private final Prayer prayer;
private final int spriteID;
@Getter(AccessLevel.NONE)
private BufferedImage image;
BufferedImage getImage(SpriteManager spriteManager)
{
if (image == null)
{
BufferedImage tmp = spriteManager.getSprite(spriteID, 0);
image = tmp == null ? null : ImageUtil.resizeImage(tmp, AlchemicalHydraOverlay.IMGSIZE, AlchemicalHydraOverlay.IMGSIZE);
}
return image;
}
}
private final NPC npc;
private AlchemicalHydraPhase phase = AlchemicalHydraPhase.ONE;
private int attackCount = 0;
private int nextSwitch = phase.getAttacksPerSwitch();
@Setter(AccessLevel.PACKAGE)
private int nextSpecial = 3;
private AttackStyle nextAttack = AttackStyle.MAGIC;
private AttackStyle lastAttack = AttackStyle.MAGIC;
@Setter(AccessLevel.PACKAGE)
private boolean weakened = false;
void changePhase(AlchemicalHydraPhase newPhase)
{
phase = newPhase;
nextSpecial = 3;
attackCount = 0;
weakened = false;
if (newPhase == AlchemicalHydraPhase.FOUR)
{
weakened = true;
switchStyles();
nextSwitch = phase.getAttacksPerSwitch();
}
}
private void switchStyles()
{
nextAttack = lastAttack == AlchemicalHydra.AttackStyle.MAGIC
? AlchemicalHydra.AttackStyle.RANGED
: AlchemicalHydra.AttackStyle.MAGIC;
}
void handleAttack(int id)
{
if (id != nextAttack.getProjectileID())
{
if (id == lastAttack.getProjectileID())
{
// If the current attack isn't what was expected and we accidentally counted 1 too much
return;
}
// If the current attack isn't what was expected and we should have switched prayers
switchStyles();
nextSwitch = phase.getAttacksPerSwitch() - 1;
}
else
{
nextSwitch--;
}
lastAttack = nextAttack;
attackCount++;
if (nextSwitch <= 0)
{
switchStyles();
nextSwitch = phase.getAttacksPerSwitch();
}
}
int getNextSpecialRelative()
{
return nextSpecial - attackCount;
}
}
@@ -1,185 +0,0 @@
/*
* Copyright (c) 2019, Lucas <https://github.com/lucwousin>
* 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.alchemicalhydra;
import java.awt.Color;
import net.runelite.client.config.Alpha;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
import net.runelite.client.config.ConfigSection;
@ConfigGroup("betterHydra")
public interface AlchemicalHydraConfig extends Config
{
@ConfigSection(
keyName = "features",
name = "Features",
description = "Feathers. Jk, features",
position = 0
)
default boolean features()
{
return true;
}
@ConfigItem(
keyName = "counting",
name = "Prayer helper",
description = "Basically everything this plugin is known for. Also has attacks between specs and poison overlay. Shouldn't NOT use this tbh",
position = 1,
section = "features"
)
default boolean counting()
{
return true;
}
@ConfigItem(
keyName = "fountain",
name = "Fountain helper",
description = "Indicates if hydra is on a fountain",
position = 2,
section = "features"
)
default boolean fountain()
{
return true;
}
@ConfigItem(
keyName = "stun",
name = "Stun timer",
description = "Shows when you can walk in fire phase",
position = 3,
section = "features"
)
default boolean stun()
{
return false;
}
@ConfigSection(
keyName = "colours",
name = "Colours",
description = "colours...",
position = 2
)
default boolean colours()
{
return false;
}
@Alpha
@ConfigItem(
keyName = "safeCol",
name = "Safe colour",
description = "Colour overlay will be when there's >2 attacks left",
position = 1,
section = "colours"
)
default Color safeCol()
{
return new Color(0, 156, 0, 156);
}
@Alpha
@ConfigItem(
keyName = "medCol",
name = "Medium colour",
description = "Colour overlay will be when a input is coming up",
position = 2,
section = "colours"
)
default Color medCol()
{
return new Color(200, 156, 0, 156);
}
@Alpha
@ConfigItem(
keyName = "badCol",
name = "Bad colour",
description = "Colour overlay will be when you have to do something NOW",
position = 3,
section = "colours"
)
default Color badCol()
{
return new Color(156, 0, 0, 156);
}
@Alpha
@ConfigItem(
keyName = "poisonBorderCol",
name = "Poison border colour",
description = "Colour the edges of the area highlighted by poison special will be",
position = 4,
section = "colours"
)
default Color poisonBorderCol()
{
return new Color(255, 0, 0, 100);
}
@Alpha
@ConfigItem(
keyName = "poisonCol",
name = "Poison colour",
description = "Colour the fill of the area highlighted by poison special will be",
position = 5,
section = "colours"
)
default Color poisonCol()
{
return new Color(255, 0, 0, 50);
}
@Alpha
@ConfigItem(
keyName = "fountainColA",
name = "Fountain colour (not on top)",
description = "Fountain colour (not on top)",
position = 6,
section = "colours"
)
default Color fountainColA()
{
return new Color(255, 0, 0, 100);
}
@Alpha
@ConfigItem(
keyName = "fountainColB",
name = "Fountain colour (on top)",
description = "Fountain colour (on top)",
position = 7,
section = "colours"
)
default Color fountainColB()
{
return new Color(0, 255, 0, 100);
}
}
@@ -1,225 +0,0 @@
/*
* Copyright (c) 2019, Lucas <https://github.com/lucwousin>
* 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.alchemicalhydra;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.AccessLevel;
import lombok.Setter;
import net.runelite.api.Client;
import net.runelite.api.IndexDataBase;
import net.runelite.api.Prayer;
import net.runelite.api.Sprite;
import net.runelite.api.SpriteID;
import net.runelite.client.game.SpriteManager;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.components.ComponentOrientation;
import net.runelite.client.ui.overlay.components.InfoBoxComponent;
import net.runelite.client.ui.overlay.components.PanelComponent;
import net.runelite.client.util.ImageUtil;
@Singleton
class AlchemicalHydraOverlay extends Overlay
{
static final int IMGSIZE = 36;
private final AlchemicalHydraPlugin plugin;
private final Client client;
private final SpriteManager spriteManager;
private final PanelComponent panelComponent = new PanelComponent();
private BufferedImage stunImg;
@Setter(AccessLevel.PACKAGE)
private Color safeCol;
@Setter(AccessLevel.PACKAGE)
private Color medCol;
@Setter(AccessLevel.PACKAGE)
private Color badCol;
@Setter(AccessLevel.PACKAGE)
private int stunTicks;
@Inject
AlchemicalHydraOverlay(final AlchemicalHydraPlugin plugin, final Client client, final SpriteManager spriteManager)
{
this.plugin = plugin;
this.client = client;
this.spriteManager = spriteManager;
this.setPosition(OverlayPosition.BOTTOM_RIGHT);
panelComponent.setOrientation(ComponentOrientation.VERTICAL);
}
@Override
public Dimension render(Graphics2D graphics2D)
{
final AlchemicalHydra hydra = plugin.getHydra();
panelComponent.getChildren().clear();
if (hydra == null)
{
return null;
}
// First add stunned thing if needed
if (stunTicks > 0)
{
addStunOverlay();
}
if (plugin.isCounting())
{
// Add spec box second, to keep it above pray
addSpecOverlay(hydra);
// Finally add prayer box
addPrayOverlay(hydra);
}
panelComponent.setPreferredSize(new Dimension(40, 0));
panelComponent.setBorder(new Rectangle(0, 0, 0, 0));
return panelComponent.render(graphics2D);
}
private void addStunOverlay()
{
final InfoBoxComponent stunComponent = new InfoBoxComponent();
stunComponent.setBackgroundColor(badCol);
stunComponent.setImage(getStunImg());
stunComponent.setText(" " + stunTicks);
stunComponent.setPreferredSize(new Dimension(40, 40));
panelComponent.getChildren().add(stunComponent);
}
private void addSpecOverlay(final AlchemicalHydra hydra)
{
final AlchemicalHydraPhase phase = hydra.getPhase();
final int nextSpec = hydra.getNextSpecialRelative();
if (nextSpec > 3)
{
return;
}
final InfoBoxComponent specComponent = new InfoBoxComponent();
if (nextSpec == 0)
{
specComponent.setBackgroundColor(badCol);
}
else if (nextSpec == 1)
{
specComponent.setBackgroundColor(medCol);
}
specComponent.setImage(phase.getSpecImage(spriteManager));
specComponent.setText(" " + nextSpec); // hacky way to not have to figure out how to move text
specComponent.setPreferredSize(new Dimension(40, 40));
panelComponent.getChildren().add(specComponent);
}
private void addPrayOverlay(final AlchemicalHydra hydra)
{
final Prayer nextPrayer = hydra.getNextAttack().getPrayer();
final int nextSwitch = hydra.getNextSwitch();
InfoBoxComponent prayComponent = new InfoBoxComponent();
if (nextSwitch == 1)
{
prayComponent.setBackgroundColor(client.isPrayerActive(nextPrayer) ? medCol : badCol);
}
else
{
prayComponent.setBackgroundColor(client.isPrayerActive(nextPrayer) ? safeCol : badCol);
}
prayComponent.setImage(hydra.getNextAttack().getImage(spriteManager));
prayComponent.setText(" " + nextSwitch);
prayComponent.setColor(Color.white);
prayComponent.setPreferredSize(new Dimension(40, 40));
panelComponent.getChildren().add(prayComponent);
}
boolean onGameTick()
{
return --stunTicks <= 0;
}
private BufferedImage getStunImg()
{
if (stunImg == null)
{
stunImg = createStunImage(client);
}
return stunImg;
}
private static BufferedImage createStunImage(Client client)
{
final Sprite root = getSprite(client, SpriteID.BIG_ASS_GREY_ENTANGLE);
final Sprite mark = getSprite(client, SpriteID.TRADE_EXCLAMATION_MARK_ITEM_REMOVAL_WARNING);
if (mark == null || root == null)
{
return null;
}
final Sprite sprite = ImageUtil.mergeSprites(client, ImageUtil.resizeSprite(client, root, IMGSIZE, IMGSIZE), mark);
return sprite.toBufferedImage();
}
private static Sprite getSprite(Client client, int id)
{
final IndexDataBase spriteDb = client.getIndexSprites();
if (spriteDb == null)
{
return null;
}
final Sprite[] sprites = client.getSprites(spriteDb, id, 0);
if (sprites == null)
{
return null;
}
return sprites[0];
}
}
@@ -1,69 +0,0 @@
/*
* Copyright (c) 2019, Lucas <https://github.com/lucwousin>
* 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.alchemicalhydra;
import java.awt.image.BufferedImage;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.runelite.api.AnimationID;
import net.runelite.api.ProjectileID;
import net.runelite.api.SpriteID;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.game.SpriteManager;
import net.runelite.client.util.ImageUtil;
@Getter(AccessLevel.PACKAGE)
@RequiredArgsConstructor
enum AlchemicalHydraPhase
{
ONE(3, AnimationID.HYDRA_1_1, AnimationID.HYDRA_1_2, ProjectileID.HYDRA_POISON, 0, SpriteID.BIG_ASS_GUTHIX_SPELL, new WorldPoint(1371, 10263, 0)),
TWO(3, AnimationID.HYDRA_2_1, AnimationID.HYDRA_2_2, 0, AnimationID.HYDRA_LIGHTNING, SpriteID.BIG_SPEC_TRANSFER, new WorldPoint(1371, 10272, 0)),
THREE(3, AnimationID.HYDRA_3_1, AnimationID.HYDRA_3_2, 0, AnimationID.HYDRA_FIRE, SpriteID.BIG_SUPERHEAT, new WorldPoint(1362, 10272, 0)),
FOUR(1, AnimationID.HYDRA_4_1, AnimationID.HYDRA_4_2, ProjectileID.HYDRA_POISON, 0, SpriteID.BIG_ASS_GUTHIX_SPELL, null);
private final int attacksPerSwitch;
private final int deathAnim1;
private final int deathAnim2;
private final int specProjectileId;
private final int specAnimationId;
@Getter(AccessLevel.NONE)
private final int specImageID;
private final WorldPoint fountain;
private BufferedImage specImage;
BufferedImage getSpecImage(SpriteManager spriteManager)
{
if (specImage == null)
{
BufferedImage tmp = spriteManager.getSprite(specImageID, 0);
specImage = tmp == null ? null : ImageUtil.resizeImage(tmp, AlchemicalHydraOverlay.IMGSIZE, AlchemicalHydraOverlay.IMGSIZE);
}
return specImage;
}
}
@@ -1,401 +0,0 @@
/*
* Copyright (c) 2019, Lucas <https://github.com/lucwousin>
* 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.alchemicalhydra;
import com.google.inject.Provides;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Actor;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.NPC;
import net.runelite.api.NpcID;
import net.runelite.api.Projectile;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.events.AnimationChanged;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.NpcSpawned;
import net.runelite.api.events.ProjectileMoved;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.PluginType;
import net.runelite.client.plugins.alchemicalhydra.AlchemicalHydra.AttackStyle;
import net.runelite.client.ui.overlay.OverlayManager;
@PluginDescriptor(
name = "Alchemical Hydra",
description = "Show what to pray against hydra",
tags = {"Hydra", "Lazy", "4 headed asshole"},
type = PluginType.PVM,
enabledByDefault = false
)
@Slf4j
@Singleton
public class AlchemicalHydraPlugin extends Plugin
{
private static final int[] HYDRA_REGIONS = {
5279, 5280,
5535, 5536
};
private static final int STUN_LENGTH = 7;
@Getter(AccessLevel.PACKAGE)
private Map<LocalPoint, Projectile> poisonProjectiles = new HashMap<>();
@Getter(AccessLevel.PACKAGE)
private AlchemicalHydra hydra;
@Getter(AccessLevel.PACKAGE)
private boolean counting;
@Getter(AccessLevel.PACKAGE)
private boolean fountain;
@Getter(AccessLevel.PACKAGE)
private boolean stun;
private boolean inHydraInstance;
private int lastAttackTick;
@Inject
private Client client;
@Inject
private EventBus eventBus;
@Inject
private AlchemicalHydraConfig config;
@Inject
private AlchemicalHydraOverlay overlay;
@Inject
private AlchemicalHydraSceneOverlay sceneOverlay;
@Inject
private OverlayManager overlayManager;
@Provides
AlchemicalHydraConfig provideConfig(ConfigManager configManager)
{
return configManager.getConfig(AlchemicalHydraConfig.class);
}
@Override
protected void startUp()
{
initConfig();
inHydraInstance = checkArea();
lastAttackTick = -1;
poisonProjectiles.clear();
}
@Override
protected void shutDown()
{
eventBus.unregister("fight");
eventBus.unregister("npcSpawned");
inHydraInstance = false;
hydra = null;
poisonProjectiles.clear();
removeOverlays();
lastAttackTick = -1;
}
private void initConfig()
{
this.counting = config.counting();
this.fountain = config.fountain();
this.stun = config.stun();
this.overlay.setSafeCol(config.safeCol());
this.overlay.setMedCol(config.medCol());
this.overlay.setBadCol(config.badCol());
this.sceneOverlay.setPoisonBorder(config.poisonBorderCol());
this.sceneOverlay.setPoisonFill(config.poisonCol());
this.sceneOverlay.setBadFountain(config.fountainColA());
this.sceneOverlay.setGoodFountain(config.fountainColB());
}
private void addFightSubscriptions()
{
eventBus.subscribe(AnimationChanged.class, "fight", this::onAnimationChanged);
eventBus.subscribe(ProjectileMoved.class, "fight", this::onProjectileMoved);
eventBus.subscribe(ChatMessage.class, "fight", this::onChatMessage);
}
@Subscribe
private void onConfigChanged(ConfigChanged event)
{
if (!event.getGroup().equals("betterHydra"))
{
return;
}
switch (event.getKey())
{
case "counting":
this.counting = config.counting();
break;
case "fountain":
this.fountain = config.fountain();
break;
case "stun":
this.stun = config.stun();
break;
case "safeCol":
overlay.setSafeCol(config.safeCol());
return;
case "medCol":
overlay.setMedCol(config.medCol());
return;
case "badCol":
overlay.setBadCol(config.badCol());
return;
case "poisonBorderCol":
sceneOverlay.setPoisonBorder(config.poisonBorderCol());
break;
case "poisonCol":
sceneOverlay.setPoisonFill(config.poisonCol());
break;
case "fountainColA":
sceneOverlay.setBadFountain(config.fountainColA());
break;
case "fountainColB":
sceneOverlay.setGoodFountain(config.fountainColB());
break;
}
}
@Subscribe
private void onGameStateChanged(GameStateChanged state)
{
if (state.getGameState() != GameState.LOGGED_IN)
{
return;
}
inHydraInstance = checkArea();
if (!inHydraInstance)
{
if (hydra != null)
{
removeOverlays();
hydra = null;
}
eventBus.unregister("npcSpawned");
eventBus.unregister("fight");
return;
}
eventBus.subscribe(NpcSpawned.class, "npcSpawned", this::onNpcSpawned);
for (NPC npc : client.getNpcs())
{
if (npc.getId() == NpcID.ALCHEMICAL_HYDRA)
{
hydra = new AlchemicalHydra(npc);
addFightSubscriptions();
break;
}
}
addOverlays();
}
private void onNpcSpawned(NpcSpawned event)
{
if (event.getNpc().getId() != NpcID.ALCHEMICAL_HYDRA)
{
return;
}
eventBus.unregister("npcSpawned");
hydra = new AlchemicalHydra(event.getNpc());
addFightSubscriptions();
addOverlays();
}
private void onAnimationChanged(AnimationChanged animationChanged)
{
Actor actor = animationChanged.getActor();
if (!inHydraInstance || hydra == null || actor == client.getLocalPlayer())
{
return;
}
AlchemicalHydraPhase phase = hydra.getPhase();
if (actor.getAnimation() == phase.getDeathAnim2() &&
phase != AlchemicalHydraPhase.THREE // Else log's gonna say "Tried some weird shit"
|| actor.getAnimation() == phase.getDeathAnim1() &&
phase == AlchemicalHydraPhase.THREE) // We want the pray to switch ye ok ty
{
switch (phase)
{
case ONE:
hydra.changePhase(AlchemicalHydraPhase.TWO);
return;
case TWO:
hydra.changePhase(AlchemicalHydraPhase.THREE);
return;
case THREE:
hydra.changePhase(AlchemicalHydraPhase.FOUR);
return;
case FOUR:
hydra = null;
poisonProjectiles.clear();
eventBus.unregister("fight");
eventBus.subscribe(NpcSpawned.class, "npcSpawned", this::onNpcSpawned);
removeOverlays();
return;
default:
log.debug("Tried some weird shit");
break;
}
}
else if (actor.getAnimation() == phase.getSpecAnimationId() && phase.getSpecAnimationId() != 0)
{
hydra.setNextSpecial(hydra.getNextSpecial() + 9);
}
if (poisonProjectiles.isEmpty())
{
return;
}
Set<LocalPoint> exPoisonProjectiles = new HashSet<>();
for (Entry<LocalPoint, Projectile> entry : poisonProjectiles.entrySet())
{
if (entry.getValue().getEndCycle() < client.getGameCycle())
{
exPoisonProjectiles.add(entry.getKey());
}
}
for (LocalPoint toRemove : exPoisonProjectiles)
{
poisonProjectiles.remove(toRemove);
}
}
private void onProjectileMoved(ProjectileMoved event)
{
if (!inHydraInstance || hydra == null
|| client.getGameCycle() >= event.getProjectile().getStartMovementCycle())
{
return;
}
Projectile projectile = event.getProjectile();
int id = projectile.getId();
if (hydra.getPhase().getSpecProjectileId() != 0 && hydra.getPhase().getSpecProjectileId() == id)
{
if (hydra.getAttackCount() == hydra.getNextSpecial())
{
// Only add 9 to next special on the first poison projectile (whoops)
hydra.setNextSpecial(hydra.getNextSpecial() + 9);
}
poisonProjectiles.put(event.getPosition(), projectile);
}
else if (client.getTickCount() != lastAttackTick
&& (id == AttackStyle.MAGIC.getProjectileID() || id == AttackStyle.RANGED.getProjectileID()))
{
hydra.handleAttack(id);
lastAttackTick = client.getTickCount();
}
}
private void onChatMessage(ChatMessage event)
{
if (event.getMessage().equals("The chemicals neutralise the Alchemical Hydra's defences!"))
{
hydra.setWeakened(true);
}
else if (event.getMessage().equals("The Alchemical Hydra temporarily stuns you."))
{
if (isStun())
{
overlay.setStunTicks(STUN_LENGTH);
eventBus.subscribe(GameTick.class, "hydraStun", this::onGameTick);
}
}
}
private void onGameTick(GameTick tick)
{
if (overlay.onGameTick())
{
// unregister self when 7 ticks have passed
eventBus.unregister("hydraStun");
}
}
private boolean checkArea()
{
return Arrays.equals(client.getMapRegions(), HYDRA_REGIONS) && client.isInInstancedRegion();
}
private void addOverlays()
{
if (counting || stun)
{
overlayManager.add(overlay);
}
if (counting || fountain)
{
overlayManager.add(sceneOverlay);
}
}
private void removeOverlays()
{
overlayManager.remove(overlay);
overlayManager.remove(sceneOverlay);
}
}
@@ -1,165 +0,0 @@
/*
* Copyright (c) 2019, Lucas <https://github.com/lucwousin>
* 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.alchemicalhydra;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.geom.Area;
import java.util.Collection;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.AccessLevel;
import lombok.Setter;
import net.runelite.api.Client;
import static net.runelite.api.Perspective.getCanvasTileAreaPoly;
import net.runelite.api.Projectile;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldArea;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPosition;
@Singleton
class AlchemicalHydraSceneOverlay extends Overlay
{
@Setter(AccessLevel.PACKAGE)
private Color poisonBorder;
@Setter(AccessLevel.PACKAGE)
private Color poisonFill;
@Setter(AccessLevel.PACKAGE)
private Color goodFountain;
@Setter(AccessLevel.PACKAGE)
private Color badFountain;
private final AlchemicalHydraPlugin plugin;
private final Client client;
@Inject
public AlchemicalHydraSceneOverlay(final Client client, final AlchemicalHydraPlugin plugin)
{
setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.UNDER_WIDGETS);
this.plugin = plugin;
this.client = client;
}
@Override
public Dimension render(Graphics2D graphics)
{
AlchemicalHydra hydra = plugin.getHydra();
final Map<LocalPoint, Projectile> poisonProjectiles = plugin.getPoisonProjectiles();
if (plugin.isCounting() && !poisonProjectiles.isEmpty())
{
drawPoisonArea(graphics, poisonProjectiles);
}
if (plugin.isFountain() && hydra.getPhase().getFountain() != null)
{
drawFountain(graphics, hydra);
}
return null;
}
private void drawPoisonArea(Graphics2D graphics, Map<LocalPoint, Projectile> poisonProjectiles)
{
Area poisonTiles = new Area();
for (Map.Entry<LocalPoint, Projectile> entry : poisonProjectiles.entrySet())
{
if (entry.getValue().getEndCycle() < client.getGameCycle())
{
continue;
}
LocalPoint point = entry.getKey();
Polygon poly = getCanvasTileAreaPoly(client, point, 3);
if (poly != null)
{
poisonTiles.add(new Area(poly));
}
}
graphics.setPaintMode();
graphics.setColor(poisonBorder);
graphics.draw(poisonTiles);
graphics.setColor(poisonFill);
graphics.fill(poisonTiles);
}
private void drawFountain(Graphics2D graphics, AlchemicalHydra hydra)
{
Collection<WorldPoint> fountainWorldPoint = WorldPoint.toLocalInstance(client, hydra.getPhase().getFountain()); // thanks
if (fountainWorldPoint.size() > 1) // for
{
return;
}
WorldPoint wp = null;
for (WorldPoint p : fountainWorldPoint) // this
{
wp = p;
}
LocalPoint fountainPoint = wp == null ? null : LocalPoint.fromWorld(client, wp); // trash
if (fountainPoint == null || hydra.isWeakened()) // I
{
return;
}
final Polygon poly = getCanvasTileAreaPoly(client, fountainPoint, 3); // don't
if (poly == null)
{
return;
}
Color color; // like
if (hydra.getNpc().getWorldArea().intersectsWith(new WorldArea(wp, 1, 1))) // coords
{ // WHICH FUCKING RETARD DID X, Y, dX, dY, Z???? IT'S XYZdXdY REEEEEEEEEE
color = goodFountain;
}
else
{
color = badFountain;
}
graphics.setColor(color);
graphics.setStroke(new BasicStroke(3));
graphics.draw(poly);
}
}
@@ -1,94 +0,0 @@
/*
* Copyright (c) 2019 Hydrox6 <ikada@protonmail.ch>
* 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.ammo;
import java.awt.image.BufferedImage;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.Instant;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.ui.overlay.infobox.Counter;
import net.runelite.client.util.QuantityFormatter;
class AmmoCounter extends Counter
{
@Getter(AccessLevel.PACKAGE)
private int itemID;
private final String name;
private final int total;
private final Instant time;
private BigDecimal ammoPerHour;
AmmoCounter(Plugin plugin, int itemID, int count, String name, BufferedImage image)
{
super(image, plugin, count);
this.total = count;
this.itemID = itemID;
this.name = name;
this.time = Instant.now();
}
@Override
public String getText()
{
return QuantityFormatter.quantityToRSDecimalStack(getCount());
}
@Override
public String getTooltip()
{
if (lossRate() >= 1)
{
return String.format("%s</br>Loss Rate: %s/h", name, lossRate());
}
else
{
return "Need more data";
}
}
private int lossRate()
{
BigDecimal diff = BigDecimal.valueOf(total).subtract(BigDecimal.valueOf(getCount()));
BigDecimal timeSinceStart = BigDecimal.valueOf(Duration.between(time, Instant.now()).getSeconds())
.setScale(6, RoundingMode.UP);
if (timeSinceStart.compareTo(BigDecimal.ZERO) != 0)
{
BigDecimal timeSinceStartInHours = timeSinceStart.divide(BigDecimal.valueOf(3600), RoundingMode.UP);
ammoPerHour = diff.divide(timeSinceStartInHours, RoundingMode.HALF_UP);
}
if (ammoPerHour != null)
{
return ammoPerHour.intValue();
}
else
{
return 0;
}
}
}
@@ -1,153 +0,0 @@
/*
* Copyright (c) 2019 Hydrox6 <ikada@protonmail.ch>
* 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.ammo;
import java.awt.image.BufferedImage;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.api.Client;
import net.runelite.api.EquipmentInventorySlot;
import net.runelite.api.InventoryID;
import net.runelite.api.Item;
import net.runelite.api.ItemContainer;
import net.runelite.api.ItemDefinition;
import net.runelite.api.events.ItemContainerChanged;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.PluginType;
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
@PluginDescriptor(
name = "Ammo",
description = "Shows the current ammo the player has equipped",
tags = {"bolts", "darts", "chinchompa", "equipment"},
type = PluginType.UTILITY
)
@Singleton
public class AmmoPlugin extends Plugin
{
@Inject
private Client client;
@Inject
private ClientThread clientThread;
@Inject
private InfoBoxManager infoBoxManager;
@Inject
private ItemManager itemManager;
private AmmoCounter counterBox;
@Override
protected void startUp()
{
clientThread.invokeLater(() ->
{
final ItemContainer container = client.getItemContainer(InventoryID.EQUIPMENT);
if (container != null)
{
checkInventory(container.getItems());
}
});
}
@Override
protected void shutDown()
{
infoBoxManager.removeInfoBox(counterBox);
counterBox = null;
}
@Subscribe
private void onItemContainerChanged(ItemContainerChanged event)
{
if (event.getItemContainer() != client.getItemContainer(InventoryID.EQUIPMENT))
{
return;
}
checkInventory(event.getItemContainer().getItems());
}
private void checkInventory(final Item[] items)
{
// Check for weapon slot items. This overrides the ammo slot,
// as the player will use the thrown weapon (eg. chinchompas, knives, darts)
if (items.length > EquipmentInventorySlot.WEAPON.getSlotIdx())
{
final Item weapon = items[EquipmentInventorySlot.WEAPON.getSlotIdx()];
final ItemDefinition weaponComp = itemManager.getItemDefinition(weapon.getId());
if (weaponComp.isStackable())
{
updateInfobox(weapon, weaponComp);
return;
}
}
if (items.length <= EquipmentInventorySlot.AMMO.getSlotIdx())
{
removeInfobox();
return;
}
final Item ammo = items[EquipmentInventorySlot.AMMO.getSlotIdx()];
final ItemDefinition comp = itemManager.getItemDefinition(ammo.getId());
if (!comp.isStackable())
{
removeInfobox();
return;
}
updateInfobox(ammo, comp);
}
private void updateInfobox(final Item item, final ItemDefinition comp)
{
if (counterBox != null && counterBox.getItemID() == item.getId())
{
counterBox.setCount(item.getQuantity());
return;
}
removeInfobox();
final BufferedImage image = itemManager.getImage(item.getId(), 5, false);
counterBox = new AmmoCounter(this, item.getId(), item.getQuantity(), comp.getName(), image);
infoBoxManager.addInfoBox(counterBox);
}
private void removeInfobox()
{
infoBoxManager.removeInfoBox(counterBox);
counterBox = null;
}
}
@@ -1,78 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.animsmoothing;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
@ConfigGroup(AnimationSmoothingPlugin.CONFIG_GROUP)
public interface AnimationSmoothingConfig extends Config
{
@ConfigItem(
keyName = "smoothPlayerAnimations",
name = "Smooth Player Animations",
description = "Configures whether the player animations are smooth or not",
position = 1
)
default boolean smoothPlayerAnimations()
{
return true;
}
@ConfigItem(
keyName = "smoothNpcAnimations",
name = "Smooth NPC Animations",
description = "Configures whether the npc animations are smooth or not",
position = 2
)
default boolean smoothNpcAnimations()
{
return true;
}
@ConfigItem(
keyName = "smoothObjectAnimations",
name = "Smooth Object Animations",
description = "Configures whether the object animations are smooth or not",
position = 3
)
default boolean smoothObjectAnimations()
{
return true;
}
@ConfigItem(
keyName = "smoothWidgetAnimations",
name = "Smooth Widget Animations",
description = "Configures whether the widget animations are smooth or not",
position = 4
)
default boolean smoothWidgetAnimations()
{
return true;
}
}
@@ -1,93 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* 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.animsmoothing;
import com.google.inject.Provides;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.api.Client;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.PluginType;
@PluginDescriptor(
name = "Animation Smoothing",
description = "Show smoother player, NPC, and object animations",
tags = {"npcs", "objects", "players"},
enabledByDefault = false,
type = PluginType.MISCELLANEOUS
)
@Singleton
public class AnimationSmoothingPlugin extends Plugin
{
static final String CONFIG_GROUP = "animationSmoothing";
@Inject
private Client client;
@Inject
private AnimationSmoothingConfig config;
@Provides
AnimationSmoothingConfig getConfig(ConfigManager configManager)
{
return configManager.getConfig(AnimationSmoothingConfig.class);
}
@Override
protected void startUp()
{
update();
}
@Override
protected void shutDown()
{
client.setInterpolatePlayerAnimations(false);
client.setInterpolateNpcAnimations(false);
client.setInterpolateObjectAnimations(false);
client.setInterpolateWidgetAnimations(false);
}
@Subscribe
private void onConfigChanged(ConfigChanged event)
{
if (event.getGroup().equals(CONFIG_GROUP))
{
update();
}
}
private void update()
{
client.setInterpolatePlayerAnimations(config.smoothPlayerAnimations());
client.setInterpolateNpcAnimations(config.smoothNpcAnimations());
client.setInterpolateObjectAnimations(config.smoothObjectAnimations());
client.setInterpolateWidgetAnimations(config.smoothWidgetAnimations());
}
}
@@ -1,168 +0,0 @@
/*
* Copyright (c) 2018, DennisDeV <https://github.com/DevDennis>
* 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.antidrag;
import java.awt.Color;
import java.awt.event.KeyEvent;
import net.runelite.api.Constants;
import net.runelite.client.config.Alpha;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
import net.runelite.client.config.Keybind;
import net.runelite.client.config.ModifierlessKeybind;
@ConfigGroup("antiDrag")
public interface AntiDragConfig extends Config
{
@ConfigItem(
position = 0,
keyName = "alwaysOn",
name = "Always On",
description = "Makes the anti-drag always active and disables the hotkey toggle",
disabledBy = "toggleKeyBind || holdKeyBind",
hide = "toggleKeyBind || holdKeyBind"
)
default boolean alwaysOn()
{
return false;
}
@ConfigItem(
position = 1,
keyName = "toggleKeyBind",
name = "Toggle with Keybind",
description = "Toggle anti drag on and off, rather than always on.",
disabledBy = "alwaysOn || holdKeyBind",
hide = "alwaysOn || holdKeyBind"
)
default boolean toggleKeyBind()
{
return false;
}
@ConfigItem(
position = 2,
keyName = "holdKeyBind",
name = "Hold with Keybind",
description = "Hold anti drag key to turn it on, rather than toggle it on or off.",
disabledBy = "alwaysOn || toggleKeyBind",
hide = "alwaysOn || toggleKeyBind"
)
default boolean holdKeyBind()
{
return false;
}
@ConfigItem(
keyName = "key",
name = "Keybind",
description = "The keybind you want to use for antidrag",
position = 3,
hidden = true,
unhide = "toggleKeyBind || holdKeyBind"
)
default Keybind key()
{
return new ModifierlessKeybind(KeyEvent.VK_SHIFT, 0);
}
@ConfigItem(
keyName = "dragDelay",
name = "Drag Delay",
description = "Configures the inventory drag delay in client ticks (20ms)",
position = 4
)
default int dragDelay()
{
return Constants.GAME_TICK_LENGTH / Constants.CLIENT_TICK_LENGTH; // one game tick
}
@ConfigItem(
keyName = "reqFocus",
name = "Reset on focus loss",
description = "Disable antidrag when losing focus (like alt tabbing)",
position = 5,
hidden = true,
unhide = "toggleKeyBind || holdKeyBind"
)
default boolean reqFocus()
{
return false;
}
@ConfigItem(
keyName = "overlay",
name = "Enable overlay",
description = "Do you really need a description?",
position = 6,
hidden = true,
unhide = "toggleKeyBind || holdKeyBind"
)
default boolean overlay()
{
return false;
}
@Alpha
@ConfigItem(
keyName = "color",
name = "Overlay color",
description = "Change the overlay color, duh",
hidden = true,
unhide = "toggleKeyBind || holdKeyBind",
position = 7
)
default Color color()
{
return new Color(255, 0, 0, 30);
}
@ConfigItem(
keyName = "changeCursor",
name = "Change Cursor",
description = "Change cursor when you have anti-drag enabled.",
position = 8,
hidden = true,
unhide = "toggleKeyBind || holdKeyBind"
)
default boolean changeCursor()
{
return false;
}
@ConfigItem(
keyName = "cursorStyle",
name = "Cursor",
description = "Select which cursor you wish to use",
hidden = true,
unhide = "changeCursor",
position = 9
)
default CustomCursor selectedCursor()
{
return CustomCursor.RS3_GOLD;
}
}
@@ -1,73 +0,0 @@
/*
* Copyright (c) 2018, https://openosrs.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.antidrag;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import lombok.AccessLevel;
import lombok.Setter;
import net.runelite.api.Client;
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.OverlayPriority;
@Singleton
public class AntiDragOverlay extends Overlay
{
private static final int RADIUS = 20;
private final Client client;
@Setter(AccessLevel.PACKAGE)
private Color color;
@Inject
private AntiDragOverlay(final Client client)
{
this.client = client;
setPosition(OverlayPosition.TOOLTIP);
setPriority(OverlayPriority.HIGHEST);
setLayer(OverlayLayer.ALWAYS_ON_TOP);
}
@Override
public Dimension render(Graphics2D g)
{
g.setColor(color);
final net.runelite.api.Point mouseCanvasPosition = client.getMouseCanvasPosition();
final Point mousePosition = new Point(mouseCanvasPosition.getX() - RADIUS, mouseCanvasPosition.getY() - RADIUS);
final Rectangle bounds = new Rectangle(mousePosition.x, mousePosition.y, 2 * RADIUS, 2 * RADIUS);
g.fillOval(bounds.x, bounds.y, bounds.width, bounds.height);
return bounds.getSize();
}
}
@@ -1,271 +0,0 @@
/*
* Copyright (c) 2018, DennisDeV <https://github.com/DevDennis>
* Copyright (c) 2019, ganom <https://github.com/ganom>
* 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.antidrag;
import com.google.inject.Provides;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.events.FocusChanged;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.config.Keybind;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.input.KeyManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.PluginType;
import net.runelite.client.ui.ClientUI;
import net.runelite.client.ui.overlay.OverlayManager;
import net.runelite.client.util.HotkeyListener;
@PluginDescriptor(
name = "Anti Drag",
description = "Prevent dragging an item for a specified delay",
tags = {"antidrag", "delay", "inventory", "items"},
type = PluginType.UTILITY,
enabledByDefault = false
)
@Singleton
public class AntiDragPlugin extends Plugin
{
private static final int DEFAULT_DELAY = 5;
@Inject
private Client client;
@Inject
private ClientUI clientUI;
@Inject
private AntiDragConfig config;
@Inject
private AntiDragOverlay overlay;
@Inject
private OverlayManager overlayManager;
@Inject
private KeyManager keyManager;
private boolean toggleDrag;
private boolean configOverlay;
private boolean changeCursor;
private CustomCursor selectedCursor;
private Keybind key;
private final HotkeyListener toggleListener = new HotkeyListener(() -> this.key)
{
@Override
public void hotkeyPressed()
{
toggleDrag = !toggleDrag;
if (toggleDrag)
{
if (configOverlay)
{
overlayManager.add(overlay);
}
if (changeCursor)
{
clientUI.setCursor(selectedCursor.getCursorImage(), selectedCursor.toString());
}
client.setInventoryDragDelay(config.dragDelay());
}
else
{
overlayManager.remove(overlay);
client.setInventoryDragDelay(DEFAULT_DELAY);
clientUI.resetCursor();
}
}
};
private final HotkeyListener holdListener = new HotkeyListener(() -> this.key)
{
@Override
public void hotkeyPressed()
{
if (configOverlay)
{
overlayManager.add(overlay);
}
if (changeCursor)
{
clientUI.setCursor(selectedCursor.getCursorImage(), selectedCursor.toString());
}
final int delay = config.dragDelay();
client.setInventoryDragDelay(delay);
setBankDragDelay(delay);
}
@Override
public void hotkeyReleased()
{
overlayManager.remove(overlay);
client.setInventoryDragDelay(DEFAULT_DELAY);
// In this case, 0 is the default for bank item widgets.
setBankDragDelay(0);
clientUI.resetCursor();
}
};
@Provides
AntiDragConfig getConfig(ConfigManager configManager)
{
return configManager.getConfig(AntiDragConfig.class);
}
@Override
protected void startUp()
{
overlay.setColor(config.color());
updateConfig();
updateKeyListeners();
if (config.alwaysOn())
{
client.setInventoryDragDelay(config.dragDelay());
}
}
@Override
protected void shutDown()
{
client.setInventoryDragDelay(DEFAULT_DELAY);
keyManager.unregisterKeyListener(holdListener);
keyManager.unregisterKeyListener(toggleListener);
toggleDrag = false;
overlayManager.remove(overlay);
clientUI.resetCursor();
}
@Subscribe
private void onConfigChanged(ConfigChanged event)
{
if (event.getGroup().equals("antiDrag"))
{
updateConfig();
switch (event.getKey())
{
case "toggleKeyBind":
case "holdKeyBind":
updateKeyListeners();
break;
case "alwaysOn":
client.setInventoryDragDelay(config.alwaysOn() ? config.dragDelay() : DEFAULT_DELAY);
break;
case "dragDelay":
if (config.alwaysOn())
{
client.setInventoryDragDelay(config.dragDelay());
}
break;
case ("changeCursor"):
clientUI.resetCursor();
break;
case ("color"):
overlay.setColor(config.color());
break;
}
}
}
@Subscribe
private void onGameStateChanged(GameStateChanged event)
{
if (event.getGameState() == GameState.LOGIN_SCREEN)
{
keyManager.unregisterKeyListener(toggleListener);
keyManager.unregisterKeyListener(holdListener);
}
else if (event.getGameState() == GameState.LOGGING_IN)
{
updateKeyListeners();
}
}
private void updateConfig()
{
this.key = config.key();
this.configOverlay = config.overlay();
this.changeCursor = config.changeCursor();
this.selectedCursor = config.selectedCursor();
}
@Subscribe
private void onFocusChanged(FocusChanged focusChanged)
{
if (!focusChanged.isFocused() && config.reqFocus() && !config.alwaysOn())
{
client.setInventoryDragDelay(DEFAULT_DELAY);
setBankDragDelay(0);
overlayManager.remove(overlay);
}
}
private void updateKeyListeners()
{
if (config.holdKeyBind())
{
keyManager.registerKeyListener(holdListener);
}
else
{
keyManager.unregisterKeyListener(holdListener);
}
if (config.toggleKeyBind())
{
keyManager.registerKeyListener(toggleListener);
}
else
{
keyManager.unregisterKeyListener(toggleListener);
}
}
private void setBankDragDelay(int delay)
{
final Widget bankItemContainer = client.getWidget(WidgetInfo.BANK_ITEM_CONTAINER);
if (bankItemContainer != null)
{
Widget[] items = bankItemContainer.getDynamicChildren();
for (Widget item : items)
{
item.setDragDeadTime(delay);
}
}
}
}
@@ -1,64 +0,0 @@
/*
* Copyright (c) 2018, Kruithne <kruithne@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.antidrag;
import java.awt.image.BufferedImage;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.client.plugins.customcursor.CustomCursorPlugin;
import net.runelite.client.util.ImageUtil;
public enum CustomCursor
{
RS3_GOLD("RS3 Gold", "cursor-rs3-gold.png"),
RS3_SILVER("RS3 Silver", "cursor-rs3-silver.png"),
DRAGON_DAGGER("Dragon Dagger", "cursor-dragon-dagger.png"),
DRAGON_DAGGER_POISON("Dragon Dagger (p)", "cursor-dragon-dagger-p.png"),
TROUT("Trout", "cursor-trout.png"),
DRAGON_SCIMITAR("Dragon Scimitar", "cursor-dragon-scimitar.png"),
ARMADYL_GODSWORD("Armadyl Godsword", "cursor-armadyl-godsword.png"),
BANDOS_GODSWORD("Bandos Godsword", "cursor-bandos-godsword.png"),
MOUSE("Mouse", "cursor-mouse.png"),
SARADOMIN_GODSWORD("Saradomin Godsword", "cursor-saradomin-godsword.png"),
ZAMORAK_GODSWORD("Zamorak Godsword", "cursor-zamorak-godsword.png"),
SKILL_SPECS("Skill Specs", "cursor-skill-specs.png");
private final String name;
@Getter(AccessLevel.PACKAGE)
private final BufferedImage cursorImage;
CustomCursor(String name, String icon)
{
this.name = name;
this.cursorImage = ImageUtil.getResourceStreamFromClass(CustomCursorPlugin.class, icon);
}
@Override
public String toString()
{
return name;
}
}
@@ -1,174 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* All rights reserved.
*
*
* Modified by farhan1666
*
* 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.aoewarnings;
import java.util.HashMap;
import java.util.Map;
import net.runelite.api.ProjectileID;
public enum AoeProjectileInfo
{
LIZARDMAN_SHAMAN_AOE(ProjectileID.LIZARDMAN_SHAMAN_AOE, 5),
CRAZY_ARCHAEOLOGIST_AOE(ProjectileID.CRAZY_ARCHAEOLOGIST_AOE, 3),
ICE_DEMON_RANGED_AOE(ProjectileID.ICE_DEMON_RANGED_AOE, 3),
/**
* When you don't have pray range on ice demon does an ice barrage
*/
ICE_DEMON_ICE_BARRAGE_AOE(ProjectileID.ICE_DEMON_ICE_BARRAGE_AOE, 3),
/**
* The AOE when vasa first starts
*/
VASA_AWAKEN_AOE(ProjectileID.VASA_AWAKEN_AOE, 3),
VASA_RANGED_AOE(ProjectileID.VASA_RANGED_AOE, 3),
TEKTON_METEOR_AOE(ProjectileID.TEKTON_METEOR_AOE, 3),
/**
* The AOEs of Vorkath
*/
VORKATH_BOMB(ProjectileID.VORKATH_BOMB_AOE, 3),
VORKATH_POISON_POOL(ProjectileID.VORKATH_POISON_POOL_AOE, 1),
VORKATH_SPAWN(ProjectileID.VORKATH_SPAWN_AOE, 1), //extra tick because hard to see otherwise
VORKATH_TICK_FIRE(ProjectileID.VORKATH_TICK_FIRE_AOE, 1),
/**
* the AOEs of Galvek
*/
GALVEK_MINE(ProjectileID.GALVEK_MINE, 3),
GALVEK_BOMB(ProjectileID.GALVEK_BOMB, 3),
/**
* the AOEs of Grotesque Guardians
*/
DAWN_FREEZE(ProjectileID.DAWN_FREEZE, 3),
DUSK_CEILING(ProjectileID.DUSK_CEILING, 3),
/**
* the AOE of Vet'ion
*/
VETION_LIGHTNING(ProjectileID.VETION_LIGHTNING, 1),
/**
* the AOE of Chaos Fanatic
*/
CHAOS_FANATIC(ProjectileID.CHAOS_FANATIC_AOE, 1),
/**
* the AOE of the Corporeal Beast
*/
CORPOREAL_BEAST(ProjectileID.CORPOREAL_BEAST_AOE, 1),
CORPOREAL_BEAST_DARK_CORE(ProjectileID.CORPOREAL_BEAST_DARK_CORE_AOE, 3),
/**
* the AOEs of The Great Olm
*/
OLM_FALLING_CRYSTAL(ProjectileID.OLM_FALLING_CRYSTAL, 3),
OLM_BURNING(ProjectileID.OLM_BURNING, 1),
OLM_FALLING_CRYSTAL_TRAIL(ProjectileID.OLM_FALLING_CRYSTAL_TRAIL, 1),
OLM_ACID_TRAIL(ProjectileID.OLM_ACID_TRAIL, 1),
OLM_FIRE_LINE(ProjectileID.OLM_FIRE_LINE, 1),
/**
* the AOE of the Wintertodt snow that falls
*/
WINTERTODT_SNOW_FALL(ProjectileID.WINTERTODT_SNOW_FALL_AOE, 3),
/**
* AOE of Xarpus throwing poison
*/
XARPUS_POISON_AOE(ProjectileID.XARPUS_ACID, 1),
/**
* Aoe of Addy Drags
*/
ADDY_DRAG_POISON(ProjectileID.ADDY_DRAG_POISON, 1),
/**
* the Breath of the Drake
*/
DRAKE_BREATH(ProjectileID.DRAKE_BREATH, 1),
/**
* Cerbs fire
*/
CERB_FIRE(ProjectileID.CERB_FIRE, 2),
/**
* Demonic gorilla
*/
DEMONIC_GORILLA_BOULDER(ProjectileID.DEMONIC_GORILLA_BOULDER, 1),
/**
* Marble gargoyle (Superior Gargoyle)
*/
MARBLE_GARGOYLE_AOE(ProjectileID.MARBLE_GARGOYLE_AOE, 1);
private static final Map<Integer, AoeProjectileInfo> map = new HashMap<>();
static
{
for (AoeProjectileInfo aoe : values())
{
map.put(aoe.id, aoe);
}
}
/**
* The id of the projectile to trigger this AoE warning
*/
private final int id;
/**
* How long the indicator should last for this AoE warning This might
* need to be a bit longer than the projectile actually takes to land as
* there is a fade effect on the warning
*/
private final int aoeSize;
AoeProjectileInfo(int id, int aoeSize)
{
this.id = id;
this.aoeSize = aoeSize;
}
public static AoeProjectileInfo getById(int id)
{
return map.get(id);
}
public int getId()
{
return id;
}
public int getAoeSize()
{
return aoeSize;
}
}
@@ -1,967 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Modified by farhan1666
*
* 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.aoewarnings;
import java.awt.Color;
import java.awt.Font;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
import net.runelite.client.config.ConfigTitleSection;
import net.runelite.client.config.Range;
import net.runelite.client.config.Title;
import net.runelite.client.config.Units;
@ConfigGroup("aoe")
public interface AoeWarningConfig extends Config
{
@Getter(AccessLevel.PACKAGE)
@AllArgsConstructor
enum FontStyle
{
BOLD("Bold", Font.BOLD),
ITALIC("Italic", Font.ITALIC),
PLAIN("Plain", Font.PLAIN);
private String name;
private int font;
@Override
public String toString()
{
return getName();
}
}
@ConfigTitleSection(
keyName = "notifyTitle",
name = "Notify",
description = "",
position = -1
)
default Title notifyTitle()
{
return new Title();
}
@ConfigItem(
keyName = "aoeNotifyAll",
name = "Notify for all AoE warnings",
description = "Configures whether or not AoE Projectile Warnings should trigger a notification",
position = 0,
titleSection = "notifyTitle"
)
default boolean aoeNotifyAll()
{
return false;
}
@ConfigTitleSection(
keyName = "overlayTitle",
name = "Overlay",
description = "",
position = 1
)
default Title overlayTitle()
{
return new Title();
}
@ConfigItem(
position = 2,
keyName = "overlayColor",
name = "Overlay Color",
description = "Configures the color of the AoE Projectile Warnings overlay",
titleSection = "overlayTitle"
)
default Color overlayColor()
{
return new Color(0, 150, 200);
}
@ConfigItem(
keyName = "outline",
name = "Display Outline",
description = "Configures whether or not AoE Projectile Warnings have an outline",
titleSection = "overlayTitle",
position = 3
)
default boolean isOutlineEnabled()
{
return true;
}
@ConfigItem(
keyName = "delay",
name = "Fade Delay",
description = "Configures the amount of time in milliseconds that the warning lingers for after the projectile has touched the ground",
titleSection = "overlayTitle",
position = 4
)
@Units(Units.MILLISECONDS)
default int delay()
{
return 300;
}
@ConfigItem(
keyName = "fade",
name = "Fade Warnings",
description = "Configures whether or not AoE Projectile Warnings fade over time",
titleSection = "overlayTitle",
position = 5
)
default boolean isFadeEnabled()
{
return true;
}
@ConfigItem(
keyName = "tickTimers",
name = "Tick Timers",
description = "Configures whether or not AoE Projectile Warnings has tick timers overlaid as well.",
titleSection = "overlayTitle",
position = 6
)
default boolean tickTimers()
{
return true;
}
@ConfigTitleSection(
keyName = "textTitle",
position = 7,
name = "Text",
description = "",
hidden = true,
unhide = "tickTimers"
)
default Title textTitle()
{
return new Title();
}
@ConfigItem(
position = 8,
keyName = "fontStyle",
name = "Font Style",
description = "Bold/Italics/Plain",
titleSection = "textTitle",
hidden = true,
unhide = "tickTimers"
)
default FontStyle fontStyle()
{
return FontStyle.BOLD;
}
@Range(
min = 20,
max = 40
)
@ConfigItem(
position = 9,
keyName = "textSize",
name = "Text Size",
description = "Text Size for Timers.",
titleSection = "textTitle",
hidden = true,
unhide = "tickTimers"
)
default int textSize()
{
return 32;
}
@ConfigItem(
position = 10,
keyName = "shadows",
name = "Shadows",
description = "Adds Shadows to text.",
titleSection = "textTitle",
hidden = true,
unhide = "tickTimers"
)
default boolean shadows()
{
return true;
}
@ConfigTitleSection(
keyName = "npcTitle",
name = "NPC's",
description = "",
position = 11
)
default Title npcTitle()
{
return new Title();
}
@ConfigTitleSection(
keyName = "lizardmanaoeTitle",
name = "Lizardman Shamans",
description = "",
position = 12,
titleSection = "npcTitle"
)
default Title lizardmanaoeTitle()
{
return new Title();
}
@ConfigItem(
keyName = "lizardmanaoe",
name = "Lizardman Shamans",
description = "Configures whether or not AoE Projectile Warnings for Lizardman Shamans is displayed",
titleSection = "lizardmanaoeTitle",
position = 13
)
default boolean isShamansEnabled()
{
return true;
}
@ConfigItem(
keyName = "lizardmanaoenotify",
name = "Lizardman Shamans Notify",
description = "Configures whether or not AoE Projectile Warnings for Lizardman Shamans should trigger a notification",
titleSection = "lizardmanaoeTitle",
position = 14,
hide = "aoeNotifyAll"
)
default boolean isShamansNotifyEnabled()
{
return false;
}
@ConfigTitleSection(
keyName = "archaeologistaoeTitle",
name = "Crazy Archaeologist",
description = "",
position = 15,
titleSection = "npcTitle"
)
default Title archaeologistaoeTitle()
{
return new Title();
}
@ConfigItem(
keyName = "archaeologistaoe",
name = "Crazy Archaeologist",
description = "Configures whether or not AoE Projectile Warnings for Archaeologist is displayed",
titleSection = "archaeologistaoeTitle",
position = 16
)
default boolean isArchaeologistEnabled()
{
return true;
}
@ConfigItem(
keyName = "archaeologistaoenotify",
name = "Crazy Archaeologist Notify",
description = "Configures whether or not AoE Projectile Warnings for Crazy Archaeologist should trigger a notification",
titleSection = "archaeologistaoeTitle",
position = 17,
hide = "aoeNotifyAll"
)
default boolean isArchaeologistNotifyEnabled()
{
return false;
}
@ConfigTitleSection(
keyName = "icedemonTitle",
name = "Ice Demon",
description = "",
position = 18,
titleSection = "npcTitle"
)
default Title icedemonTitle()
{
return new Title();
}
@ConfigItem(
keyName = "icedemon",
name = "Ice Demon",
description = "Configures whether or not AoE Projectile Warnings for Ice Demon is displayed",
titleSection = "icedemonTitle",
position = 19
)
default boolean isIceDemonEnabled()
{
return true;
}
@ConfigItem(
keyName = "icedemonnotify",
name = "Ice Demon Notify",
description = "Configures whether or not AoE Projectile Warnings for Ice Demon should trigger a notification",
titleSection = "icedemonTitle",
position = 20,
hide = "aoeNotifyAll"
)
default boolean isIceDemonNotifyEnabled()
{
return false;
}
@ConfigTitleSection(
keyName = "vasaTitle",
name = "Vasa",
description = "",
position = 21,
titleSection = "npcTitle"
)
default Title vasaTitle()
{
return new Title();
}
@ConfigItem(
keyName = "vasa",
name = "Vasa",
description = "Configures whether or not AoE Projectile Warnings for Vasa is displayed",
titleSection = "vasaTitle",
position = 22
)
default boolean isVasaEnabled()
{
return true;
}
@ConfigItem(
keyName = "vasanotify",
name = "Vasa Notify",
description = "Configures whether or not AoE Projectile Warnings for Vasa should trigger a notification",
titleSection = "vasaTitle",
position = 23,
hide = "aoeNotifyAll"
)
default boolean isVasaNotifyEnabled()
{
return false;
}
@ConfigTitleSection(
keyName = "tektonTitle",
name = "Tekton",
description = "",
position = 24,
titleSection = "npcTitle"
)
default Title tektonTitle()
{
return new Title();
}
@ConfigItem(
keyName = "tekton",
name = "Tekton",
description = "Configures whether or not AoE Projectile Warnings for Tekton is displayed",
titleSection = "tektonTitle",
position = 25
)
default boolean isTektonEnabled()
{
return true;
}
@ConfigItem(
keyName = "tektonnotify",
name = "Tekton Notify",
description = "Configures whether or not AoE Projectile Warnings for Tekton should trigger a notification",
titleSection = "tektonTitle",
position = 26,
hide = "aoeNotifyAll"
)
default boolean isTektonNotifyEnabled()
{
return false;
}
@ConfigTitleSection(
keyName = "vorkathTitle",
name = "Vorkath",
description = "",
position = 27,
titleSection = "npcTitle"
)
default Title vorkathTitle()
{
return new Title();
}
@ConfigItem(
keyName = "vorkath",
name = "Vorkath",
description = "Configures whether or not AoE Projectile Warnings for Vorkath are displayed",
titleSection = "vorkathTitle",
position = 28
)
default boolean isVorkathEnabled()
{
return true;
}
@ConfigItem(
keyName = "vorkathotify",
name = "Vorkath Notify",
description = "Configures whether or not AoE Projectile Warnings for Vorkath should trigger a notification",
titleSection = "vorkathTitle",
position = 29,
hide = "aoeNotifyAll"
)
default boolean isVorkathNotifyEnabled()
{
return false;
}
@ConfigTitleSection(
keyName = "galvekTitle",
name = "Galvek",
description = "",
position = 30,
titleSection = "npcTitle"
)
default Title galvekTitle()
{
return new Title();
}
@ConfigItem(
keyName = "galvek",
name = "Galvek",
description = "Configures whether or not AoE Projectile Warnings for Galvek are displayed",
titleSection = "galvekTitle",
position = 31
)
default boolean isGalvekEnabled()
{
return true;
}
@ConfigItem(
keyName = "galveknotify",
name = "Galvek Notify",
description = "Configures whether or not AoE Projectile Warnings for Galvek should trigger a notification",
titleSection = "galvekTitle",
position = 32,
hide = "aoeNotifyAll"
)
default boolean isGalvekNotifyEnabled()
{
return false;
}
@ConfigTitleSection(
keyName = "gargbossTitle",
name = "Gargoyle Boss",
description = "",
position = 33,
titleSection = "npcTitle"
)
default Title gargbossTitle()
{
return new Title();
}
@ConfigItem(
keyName = "gargboss",
name = "Gargoyle Boss",
description = "Configs whether or not AoE Projectile Warnings for Dawn/Dusk are displayed",
titleSection = "gargbossTitle",
position = 34
)
default boolean isGargBossEnabled()
{
return true;
}
@ConfigItem(
keyName = "gargbossnotify",
name = "Gargoyle Boss Notify",
description = "Configures whether or not AoE Projectile Warnings for Gargoyle Bosses should trigger a notification",
titleSection = "gargbossTitle",
position = 35,
hide = "aoeNotifyAll"
)
default boolean isGargBossNotifyEnabled()
{
return false;
}
@ConfigTitleSection(
keyName = "vetionTitle",
name = "Vet'ion",
description = "",
position = 36,
titleSection = "npcTitle"
)
default Title vetionTitle()
{
return new Title();
}
@ConfigItem(
keyName = "vetion",
name = "Vet'ion",
description = "Configures whether or not AoE Projectile Warnings for Vet'ion are displayed",
titleSection = "vetionTitle",
position = 37
)
default boolean isVetionEnabled()
{
return true;
}
@ConfigItem(
keyName = "vetionnotify",
name = "Vet'ion Notify",
description = "Configures whether or not AoE Projectile Warnings for Vet'ion should trigger a notification",
titleSection = "vetionTitle",
position = 38,
hide = "aoeNotifyAll"
)
default boolean isVetionNotifyEnabled()
{
return false;
}
@ConfigTitleSection(
keyName = "chaosfanaticTitle",
name = "Chaos Fanatic",
description = "",
position = 39,
titleSection = "npcTitle"
)
default Title chaosfanaticTitle()
{
return new Title();
}
@ConfigItem(
keyName = "chaosfanatic",
name = "Chaos Fanatic",
description = "Configures whether or not AoE Projectile Warnings for Chaos Fanatic are displayed",
titleSection = "chaosfanaticTitle",
position = 40
)
default boolean isChaosFanaticEnabled()
{
return true;
}
@ConfigItem(
keyName = "chaosfanaticnotify",
name = "Chaos Fanatic Notify",
description = "Configures whether or not AoE Projectile Warnings for Chaos Fanatic should trigger a notification",
titleSection = "chaosfanaticTitle",
position = 41,
hide = "aoeNotifyAll"
)
default boolean isChaosFanaticNotifyEnabled()
{
return false;
}
@ConfigTitleSection(
keyName = "olmTitle",
name = "Olm",
description = "",
position = 42,
titleSection = "npcTitle"
)
default Title olmTitle()
{
return new Title();
}
@ConfigItem(
keyName = "olm",
name = "Olm",
description = "Configures whether or not AoE Projectile Warnings for The Great Olm are displayed",
titleSection = "olmTitle",
position = 43
)
default boolean isOlmEnabled()
{
return true;
}
@ConfigItem(
keyName = "olmnotify",
name = "Olm Notify",
description = "Configures whether or not AoE Projectile Warnings for Olm should trigger a notification",
titleSection = "olmTitle",
position = 44,
hide = "aoeNotifyAll"
)
default boolean isOlmNotifyEnabled()
{
return false;
}
@ConfigTitleSection(
keyName = "olmBombsTitle",
name = "Bombs",
description = "",
position = 45,
titleSection = "olmTitle"
)
default Title olmBombsTitle()
{
return new Title();
}
@ConfigItem(
keyName = "bombDisplay",
name = "Olm Bombs",
description = "Display a timer and colour-coded AoE for Olm's crystal-phase bombs.",
titleSection = "olmBombsTitle",
position = 46
)
default boolean bombDisplay()
{
return true;
}
@ConfigItem(
keyName = "bombDisplaynotify",
name = "Olm Bombs Notify",
description = "Configures whether or not AoE Projectile Warnings for Olm Bombs should trigger a notification",
titleSection = "olmBombsTitle",
position = 47,
hide = "aoeNotifyAll"
)
default boolean bombDisplayNotifyEnabled()
{
return false;
}
@ConfigTitleSection(
keyName = "olmlightningTitle",
name = "Lightning Trails",
description = "",
position = 48,
titleSection = "olmTitle"
)
default Title olmlightningTitle()
{
return new Title();
}
@ConfigItem(
keyName = "lightning",
name = "Olm Lightning Trails",
description = "Show Lightning Trails",
titleSection = "olmlightningTitle",
position = 49
)
default boolean LightningTrail()
{
return true;
}
@ConfigItem(
keyName = "lightningnotify",
name = "Olm Lightning Trails Notify",
description = "Configures whether or not AoE Projectile Warnings for Olm Lightning Trails should trigger a notification",
titleSection = "olmlightningTitle",
position = 50,
hide = "aoeNotifyAll"
)
default boolean LightningTrailNotifyEnabled()
{
return false;
}
@ConfigTitleSection(
keyName = "corpTitle",
name = "Corporeal Beast",
description = "",
position = 51,
titleSection = "npcTitle"
)
default Title corpTitle()
{
return new Title();
}
@ConfigItem(
keyName = "corp",
name = "Corporeal Beast",
description = "Configures whether or not AoE Projectile Warnings for the Corporeal Beast are displayed",
titleSection = "corpTitle",
position = 52
)
default boolean isCorpEnabled()
{
return true;
}
@ConfigItem(
keyName = "corpnotify",
name = "Corporeal Beast Notify",
description = "Configures whether or not AoE Projectile Warnings for Corporeal Beast should trigger a notification",
titleSection = "corpTitle",
position = 53,
hide = "aoeNotifyAll"
)
default boolean isCorpNotifyEnabled()
{
return false;
}
@ConfigTitleSection(
keyName = "wintertodtTitle",
name = "Wintertodt",
description = "",
position = 54,
titleSection = "npcTitle"
)
default Title wintertodtTitle()
{
return new Title();
}
@ConfigItem(
keyName = "wintertodt",
name = "Wintertodt Snow Fall",
description = "Configures whether or not AOE Projectile Warnings for the Wintertodt snow fall are displayed",
titleSection = "wintertodtTitle",
position = 55
)
default boolean isWintertodtEnabled()
{
return true;
}
@ConfigItem(
keyName = "wintertodtnotify",
name = "Wintertodt Snow Fall Notify",
description = "Configures whether or not AoE Projectile Warnings for Wintertodt Snow Fall Notify should trigger a notification",
titleSection = "wintertodtTitle",
position = 56,
hide = "aoeNotifyAll"
)
default boolean isWintertodtNotifyEnabled()
{
return false;
}
@ConfigTitleSection(
keyName = "xarpusTitle",
name = "Xarpus",
description = "",
position = 57,
titleSection = "npcTitle"
)
default Title xarpusTitle()
{
return new Title();
}
@ConfigItem(
keyName = "isXarpusEnabled",
name = "Xarpus",
description = "Configures whether or not AOE Projectile Warnings for Xarpus are displayed",
titleSection = "xarpusTitle",
position = 58
)
default boolean isXarpusEnabled()
{
return true;
}
@ConfigItem(
keyName = "isXarpusEnablednotify",
name = "Xarpus Notify",
description = "Configures whether or not AoE Projectile Warnings for Xarpus should trigger a notification",
titleSection = "xarpusTitle",
position = 59,
hide = "aoeNotifyAll"
)
default boolean isXarpusNotifyEnabled()
{
return false;
}
@ConfigTitleSection(
keyName = "addyDragsTitle",
name = "Addy Drags",
description = "",
position = 60,
titleSection = "npcTitle"
)
default Title addyDragsTitle()
{
return new Title();
}
@ConfigItem(
keyName = "addyDrags",
name = "Addy Drags",
description = "Show Bad Areas",
titleSection = "addyDragsTitle",
position = 61
)
default boolean addyDrags()
{
return true;
}
@ConfigItem(
keyName = "addyDragsnotify",
name = "Addy Drags Notify",
description = "Configures whether or not AoE Projectile Warnings for Addy Dragons should trigger a notification",
titleSection = "addyDragsTitle",
position = 62,
hide = "aoeNotifyAll"
)
default boolean addyDragsNotifyEnabled()
{
return false;
}
@ConfigTitleSection(
keyName = "drakeTitle",
name = "Drakes",
description = "",
position = 63,
titleSection = "npcTitle"
)
default Title drakeTitle()
{
return new Title();
}
@ConfigItem(
keyName = "drake",
name = "Drakes Breath",
description = "Configures if Drakes Breath tile markers are displayed",
titleSection = "drakeTitle",
position = 64
)
default boolean isDrakeEnabled()
{
return true;
}
@ConfigItem(
keyName = "drakenotify",
name = "Drakes Breath Notify",
description = "Configures whether or not AoE Projectile Warnings for Drakes Breath should trigger a notification",
titleSection = "drakeTitle",
position = 65,
hide = "aoeNotifyAll"
)
default boolean isDrakeNotifyEnabled()
{
return false;
}
@ConfigTitleSection(
keyName = "cerberusTitle",
name = "Cerberus",
description = "",
position = 66,
titleSection = "npcTitle"
)
default Title cerberusTitle()
{
return new Title();
}
@ConfigItem(
keyName = "cerbFire",
name = "Cerberus Fire",
description = "Configures if Cerberus fire tile markers are displayed",
titleSection = "cerberusTitle",
position = 67
)
default boolean isCerbFireEnabled()
{
return true;
}
@ConfigItem(
keyName = "cerbFirenotify",
name = "Cerberus Fire Notify",
description = "Configures whether or not AoE Projectile Warnings for Cerberus his fire should trigger a notification",
titleSection = "cerberusTitle",
position = 68,
hide = "aoeNotifyAll"
)
default boolean isCerbFireNotifyEnabled()
{
return false;
}
@ConfigTitleSection(
keyName = "demonicGorillaTitle",
name = "Demonic Gorilla",
description = "",
position = 69,
titleSection = "npcTitle"
)
default Title demonicGorillaTitle()
{
return new Title();
}
@ConfigItem(
keyName = "demonicGorilla",
name = "Demonic Gorilla",
description = "Configures if Demonic Gorilla boulder tile markers are displayed",
titleSection = "demonicGorillaTitle",
position = 70
)
default boolean isDemonicGorillaEnabled()
{
return true;
}
@ConfigItem(
keyName = "demonicGorillaNotify",
name = "Demonic Gorilla Notify",
description = "Configures whether or not AoE Projectile Warnings for Demonic Gorilla boulders should trigger a notification",
titleSection = "demonicGorillaTitle",
position = 71,
hide = "aoeNotifyAll"
)
default boolean isDemonicGorillaNotifyEnabled()
{
return false;
}
}
@@ -1,184 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
*
* Modified by farhan1666
*
* 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.aoewarnings;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.time.Duration;
import java.time.Instant;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.api.Client;
import net.runelite.api.Perspective;
import net.runelite.api.Point;
import net.runelite.api.ProjectileID;
import net.runelite.api.Varbits;
import net.runelite.api.coords.WorldPoint;
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 static net.runelite.client.util.ColorUtil.setAlphaComponent;
@Singleton
public class AoeWarningOverlay extends Overlay
{
private static final int FILL_START_ALPHA = 25;
private static final int OUTLINE_START_ALPHA = 255;
private final Client client;
private final AoeWarningPlugin plugin;
@Inject
public AoeWarningOverlay(final Client client, final AoeWarningPlugin plugin)
{
setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.UNDER_WIDGETS);
this.client = client;
this.plugin = plugin;
}
@Override
public Dimension render(Graphics2D graphics)
{
WorldPoint lp = client.getLocalPlayer().getWorldLocation();
plugin.getLightningTrail().forEach(o ->
OverlayUtil.drawTiles(graphics, client, o, lp, new Color(0, 150, 200), 2, 150, 50));
plugin.getAcidTrail().forEach(o ->
OverlayUtil.drawTiles(graphics, client, o.getWorldLocation(), lp, new Color(69, 241, 44), 2, 150, 50));
plugin.getCrystalSpike().forEach(o ->
OverlayUtil.drawTiles(graphics, client, o.getWorldLocation(), lp, new Color(255, 0, 84), 2, 150, 50));
plugin.getWintertodtSnowFall().forEach(o ->
OverlayUtil.drawTiles(graphics, client, o.getWorldLocation(), lp, new Color(255, 0, 84), 2, 150, 50));
Instant now = Instant.now();
Set<ProjectileContainer> projectiles = plugin.getProjectiles();
projectiles.forEach(proj ->
{
if (proj.getTargetPoint() == null)
{
return;
}
Color color;
if (now.isAfter(proj.getStartTime().plus(Duration.ofMillis(proj.getLifetime()))))
{
return;
}
if (proj.getProjectile().getId() == ProjectileID.ICE_DEMON_ICE_BARRAGE_AOE || proj.getProjectile().getId() == ProjectileID.TEKTON_METEOR_AOE)
{
if (client.getVar(Varbits.IN_RAID) == 0)
{
return;
}
}
final Polygon tilePoly = Perspective.getCanvasTileAreaPoly(client, proj.getTargetPoint(), proj.getAoeProjectileInfo().getAoeSize());
if (tilePoly == null)
{
return;
}
final double progress = (System.currentTimeMillis() - proj.getStartTime().toEpochMilli()) / (double) proj.getLifetime();
final int tickProgress = proj.getFinalTick() - client.getTickCount();
int fillAlpha, outlineAlpha;
if (plugin.isConfigFadeEnabled())
{
fillAlpha = (int) ((1 - progress) * FILL_START_ALPHA);
outlineAlpha = (int) ((1 - progress) * OUTLINE_START_ALPHA);
}
else
{
fillAlpha = FILL_START_ALPHA;
outlineAlpha = OUTLINE_START_ALPHA;
}
if (tickProgress == 0)
{
color = Color.RED;
}
else
{
color = Color.WHITE;
}
if (fillAlpha < 0)
{
fillAlpha = 0;
}
if (outlineAlpha < 0)
{
outlineAlpha = 0;
}
if (fillAlpha > 255)
{
fillAlpha = 255;
}
if (outlineAlpha > 255)
{
outlineAlpha = 255;
}
if (plugin.isConfigOutlineEnabled())
{
graphics.setColor(new Color(setAlphaComponent(plugin.getOverlayColor().getRGB(), outlineAlpha), true));
graphics.drawPolygon(tilePoly);
}
if (plugin.isTickTimers() && tickProgress >= 0)
{
OverlayUtil.renderTextLocation(graphics, Integer.toString(tickProgress), plugin.getTextSize(),
plugin.getFontStyle(), color, centerPoint(tilePoly.getBounds()), plugin.isShadows(), 0);
}
graphics.setColor(new Color(setAlphaComponent(plugin.getOverlayColor().getRGB(), fillAlpha), true));
graphics.fillPolygon(tilePoly);
});
projectiles.removeIf(proj -> now.isAfter(proj.getStartTime().plus(Duration.ofMillis(proj.getLifetime()))));
return null;
}
private Point centerPoint(Rectangle rect)
{
int x = (int) (rect.getX() + rect.getWidth() / 2);
int y = (int) (rect.getY() + rect.getHeight() / 2);
return new Point(x, y);
}
}
@@ -1,496 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Modified by farhan1666
*
* 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.aoewarnings;
import com.google.inject.Provides;
import java.awt.Color;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.GameObject;
import net.runelite.api.GameState;
import net.runelite.api.GraphicID;
import net.runelite.api.NullObjectID;
import net.runelite.api.ObjectID;
import net.runelite.api.Projectile;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.GameObjectDespawned;
import net.runelite.api.events.GameObjectSpawned;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.ProjectileMoved;
import net.runelite.api.events.ProjectileSpawned;
import net.runelite.client.Notifier;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.PluginType;
import net.runelite.client.ui.overlay.OverlayManager;
@PluginDescriptor(
name = "AoE Warnings",
description = "Shows the final destination for AoE Attack projectiles",
tags = {"bosses", "combat", "pve", "overlay"},
type = PluginType.PVM,
enabledByDefault = false
)
@Singleton
@Slf4j
public class AoeWarningPlugin extends Plugin
{
@Getter(AccessLevel.PACKAGE)
private final Set<CrystalBomb> bombs = new HashSet<>();
@Getter(AccessLevel.PACKAGE)
private final Set<ProjectileContainer> projectiles = new HashSet<>();
@Inject
public AoeWarningConfig config;
@Inject
private Notifier notifier;
@Inject
private OverlayManager overlayManager;
@Inject
private AoeWarningOverlay coreOverlay;
@Inject
private BombOverlay bombOverlay;
@Inject
private Client client;
@Getter(AccessLevel.PACKAGE)
private List<WorldPoint> lightningTrail = new ArrayList<>();
@Getter(AccessLevel.PACKAGE)
private List<GameObject> acidTrail = new ArrayList<>();
@Getter(AccessLevel.PACKAGE)
private List<GameObject> crystalSpike = new ArrayList<>();
@Getter(AccessLevel.PACKAGE)
private List<GameObject> wintertodtSnowFall = new ArrayList<>();
// Config values
private boolean aoeNotifyAll;
@Getter(AccessLevel.PACKAGE)
private Color overlayColor;
@Getter(AccessLevel.PACKAGE)
private boolean configOutlineEnabled;
private int delay;
@Getter(AccessLevel.PACKAGE)
private boolean configFadeEnabled;
@Getter(AccessLevel.PACKAGE)
private boolean tickTimers;
@Getter(AccessLevel.PACKAGE)
private int fontStyle;
@Getter(AccessLevel.PACKAGE)
private int textSize;
@Getter(AccessLevel.PACKAGE)
private boolean shadows;
private boolean configShamansEnabled;
private boolean configShamansNotifyEnabled;
private boolean configArchaeologistEnabled;
private boolean configArchaeologistNotifyEnabled;
private boolean configIceDemonEnabled;
private boolean configIceDemonNotifyEnabled;
private boolean configVasaEnabled;
private boolean configVasaNotifyEnabled;
private boolean configTektonEnabled;
private boolean configTektonNotifyEnabled;
private boolean configVorkathEnabled;
private boolean configVorkathNotifyEnabled;
private boolean configGalvekEnabled;
private boolean configGalvekNotifyEnabled;
private boolean configGargBossEnabled;
private boolean configGargBossNotifyEnabled;
private boolean configVetionEnabled;
private boolean configVetionNotifyEnabled;
private boolean configChaosFanaticEnabled;
private boolean configChaosFanaticNotifyEnabled;
private boolean configOlmEnabled;
private boolean configOlmNotifyEnabled;
@Getter(AccessLevel.PACKAGE)
private boolean configbombDisplay;
private boolean configbombDisplayNotifyEnabled;
private boolean configLightningTrail;
private boolean configLightningTrailNotifyEnabled;
private boolean configCorpEnabled;
private boolean configCorpNotifyEnabled;
private boolean configWintertodtEnabled;
private boolean configWintertodtNotifyEnabled;
private boolean configXarpusEnabled;
private boolean configXarpusNotifyEnabled;
private boolean configaddyDrags;
private boolean configaddyDragsNotifyEnabled;
private boolean configDrakeEnabled;
private boolean configDrakeNotifyEnabled;
private boolean configCerbFireEnabled;
private boolean configCerbFireNotifyEnabled;
private boolean configDemonicGorillaEnabled;
private boolean configDemonicGorillaNotifyEnabled;
@Provides
AoeWarningConfig getConfig(ConfigManager configManager)
{
return configManager.getConfig(AoeWarningConfig.class);
}
@Override
protected void startUp()
{
updateConfig();
overlayManager.add(coreOverlay);
overlayManager.add(bombOverlay);
reset();
}
@Override
protected void shutDown()
{
overlayManager.remove(coreOverlay);
overlayManager.remove(bombOverlay);
reset();
}
@Subscribe
private void onConfigChanged(ConfigChanged event)
{
if (!event.getGroup().equals("aoe"))
{
return;
}
updateConfig();
}
@Subscribe
private void onProjectileSpawned(ProjectileSpawned event)
{
final Projectile projectile = event.getProjectile();
if (AoeProjectileInfo.getById(projectile.getId()) == null)
{
return;
}
final int id = projectile.getId();
final int lifetime = this.delay + (projectile.getRemainingCycles() * 20);
int ticksRemaining = projectile.getRemainingCycles() / 30;
if (!isTickTimersEnabledForProjectileID(id))
{
ticksRemaining = 0;
}
final int tickCycle = client.getTickCount() + ticksRemaining;
if (isConfigEnabledForProjectileId(id, false))
{
projectiles.add(new ProjectileContainer(projectile, Instant.now(), lifetime, tickCycle));
if (this.aoeNotifyAll || isConfigEnabledForProjectileId(id, true))
{
notifier.notify("AoE attack detected!");
}
}
}
@Subscribe
private void onProjectileMoved(ProjectileMoved event)
{
if (projectiles.isEmpty())
{
return;
}
final Projectile projectile = event.getProjectile();
projectiles.forEach(proj ->
{
if (proj.getProjectile() == projectile)
{
proj.setTargetPoint(event.getPosition());
}
});
}
@Subscribe
private void onGameObjectSpawned(GameObjectSpawned event)
{
final GameObject gameObject = event.getGameObject();
switch (gameObject.getId())
{
case ObjectID.CRYSTAL_BOMB:
bombs.add(new CrystalBomb(gameObject, client.getTickCount()));
if (this.aoeNotifyAll || this.configbombDisplayNotifyEnabled)
{
notifier.notify("Bomb!");
}
break;
case ObjectID.ACID_POOL:
acidTrail.add(gameObject);
break;
case ObjectID.SMALL_CRYSTALS:
crystalSpike.add(gameObject);
break;
case NullObjectID.NULL_26690:
if (this.configWintertodtEnabled)
{
wintertodtSnowFall.add(gameObject);
if (this.aoeNotifyAll || this.configWintertodtNotifyEnabled)
{
notifier.notify("Snow Fall!");
}
}
break;
}
}
@Subscribe
private void onGameObjectDespawned(GameObjectDespawned event)
{
final GameObject gameObject = event.getGameObject();
switch (gameObject.getId())
{
case ObjectID.CRYSTAL_BOMB:
bombs.removeIf(o -> o.getGameObject() == gameObject);
break;
case ObjectID.ACID_POOL:
acidTrail.remove(gameObject);
break;
case ObjectID.SMALL_CRYSTALS:
crystalSpike.remove(gameObject);
break;
case NullObjectID.NULL_26690:
wintertodtSnowFall.remove(gameObject);
break;
}
}
@Subscribe
private void onGameStateChanged(GameStateChanged event)
{
if (event.getGameState() == GameState.LOGGED_IN)
{
return;
}
reset();
}
@Subscribe
private void onGameTick(GameTick event)
{
lightningTrail.clear();
if (this.configLightningTrail)
{
client.getGraphicsObjects().forEach(o ->
{
if (o.getId() == GraphicID.OLM_LIGHTNING)
{
lightningTrail.add(WorldPoint.fromLocal(client, o.getLocation()));
if (this.aoeNotifyAll || this.configLightningTrailNotifyEnabled)
{
notifier.notify("Lightning!");
}
}
});
}
bombs.forEach(CrystalBomb::bombClockUpdate);
}
private boolean isTickTimersEnabledForProjectileID(int projectileId)
{
AoeProjectileInfo projectileInfo = AoeProjectileInfo.getById(projectileId);
if (projectileInfo == null)
{
return false;
}
switch (projectileInfo)
{
case VASA_RANGED_AOE:
case VORKATH_POISON_POOL:
case VORKATH_SPAWN:
case VORKATH_TICK_FIRE:
case OLM_BURNING:
case OLM_FALLING_CRYSTAL_TRAIL:
case OLM_ACID_TRAIL:
case OLM_FIRE_LINE:
return false;
}
return true;
}
private boolean isConfigEnabledForProjectileId(int projectileId, boolean notify)
{
AoeProjectileInfo projectileInfo = AoeProjectileInfo.getById(projectileId);
if (projectileInfo == null)
{
return false;
}
if (notify && this.aoeNotifyAll)
{
return true;
}
switch (projectileInfo)
{
case LIZARDMAN_SHAMAN_AOE:
return notify ? this.configShamansNotifyEnabled : this.configShamansEnabled;
case CRAZY_ARCHAEOLOGIST_AOE:
return notify ? this.configArchaeologistNotifyEnabled : this.configArchaeologistEnabled;
case ICE_DEMON_RANGED_AOE:
case ICE_DEMON_ICE_BARRAGE_AOE:
return notify ? this.configIceDemonNotifyEnabled : this.configIceDemonEnabled;
case VASA_AWAKEN_AOE:
case VASA_RANGED_AOE:
return notify ? this.configVasaNotifyEnabled : this.configVasaEnabled;
case TEKTON_METEOR_AOE:
return notify ? this.configTektonNotifyEnabled : this.configTektonEnabled;
case VORKATH_BOMB:
case VORKATH_POISON_POOL:
case VORKATH_SPAWN:
case VORKATH_TICK_FIRE:
return notify ? this.configVorkathNotifyEnabled : this.configVorkathEnabled;
case VETION_LIGHTNING:
return notify ? this.configVetionNotifyEnabled : this.configVetionEnabled;
case CHAOS_FANATIC:
return notify ? this.configChaosFanaticNotifyEnabled : this.configChaosFanaticEnabled;
case GALVEK_BOMB:
case GALVEK_MINE:
return notify ? this.configGalvekNotifyEnabled : this.configGalvekEnabled;
case DAWN_FREEZE:
case DUSK_CEILING:
return notify ? this.configGargBossNotifyEnabled : this.configGargBossEnabled;
case OLM_FALLING_CRYSTAL:
case OLM_BURNING:
case OLM_FALLING_CRYSTAL_TRAIL:
case OLM_ACID_TRAIL:
case OLM_FIRE_LINE:
return notify ? this.configOlmNotifyEnabled : this.configOlmEnabled;
case CORPOREAL_BEAST:
case CORPOREAL_BEAST_DARK_CORE:
return notify ? this.configCorpNotifyEnabled : this.configCorpEnabled;
case XARPUS_POISON_AOE:
return notify ? this.configXarpusNotifyEnabled : this.configXarpusEnabled;
case ADDY_DRAG_POISON:
return notify ? this.configaddyDragsNotifyEnabled : this.configaddyDrags;
case DRAKE_BREATH:
return notify ? this.configDrakeNotifyEnabled : this.configDrakeEnabled;
case CERB_FIRE:
return notify ? this.configCerbFireNotifyEnabled : this.configCerbFireEnabled;
case DEMONIC_GORILLA_BOULDER:
return notify ? this.configDemonicGorillaNotifyEnabled : this.configDemonicGorillaEnabled;
}
return false;
}
private void updateConfig()
{
this.aoeNotifyAll = config.aoeNotifyAll();
this.overlayColor = config.overlayColor();
this.configOutlineEnabled = config.isOutlineEnabled();
this.delay = config.delay();
this.configFadeEnabled = config.isFadeEnabled();
this.tickTimers = config.tickTimers();
this.fontStyle = config.fontStyle().getFont();
this.textSize = config.textSize();
this.shadows = config.shadows();
this.configShamansEnabled = config.isShamansEnabled();
this.configShamansNotifyEnabled = config.isShamansNotifyEnabled();
this.configArchaeologistEnabled = config.isArchaeologistEnabled();
this.configArchaeologistNotifyEnabled = config.isArchaeologistNotifyEnabled();
this.configIceDemonEnabled = config.isIceDemonEnabled();
this.configIceDemonNotifyEnabled = config.isIceDemonNotifyEnabled();
this.configVasaEnabled = config.isVasaEnabled();
this.configVasaNotifyEnabled = config.isVasaNotifyEnabled();
this.configTektonEnabled = config.isTektonEnabled();
this.configTektonNotifyEnabled = config.isTektonNotifyEnabled();
this.configVorkathEnabled = config.isVorkathEnabled();
this.configVorkathNotifyEnabled = config.isVorkathNotifyEnabled();
this.configGalvekEnabled = config.isGalvekEnabled();
this.configGalvekNotifyEnabled = config.isGalvekNotifyEnabled();
this.configGargBossEnabled = config.isGargBossEnabled();
this.configGargBossNotifyEnabled = config.isGargBossNotifyEnabled();
this.configVetionEnabled = config.isVetionEnabled();
this.configVetionNotifyEnabled = config.isVetionNotifyEnabled();
this.configChaosFanaticEnabled = config.isChaosFanaticEnabled();
this.configChaosFanaticNotifyEnabled = config.isChaosFanaticNotifyEnabled();
this.configOlmEnabled = config.isOlmEnabled();
this.configOlmNotifyEnabled = config.isOlmNotifyEnabled();
this.configbombDisplay = config.bombDisplay();
this.configbombDisplayNotifyEnabled = config.bombDisplayNotifyEnabled();
this.configLightningTrail = config.LightningTrail();
this.configLightningTrailNotifyEnabled = config.LightningTrailNotifyEnabled();
this.configCorpEnabled = config.isCorpEnabled();
this.configCorpNotifyEnabled = config.isCorpNotifyEnabled();
this.configWintertodtEnabled = config.isWintertodtEnabled();
this.configWintertodtNotifyEnabled = config.isWintertodtNotifyEnabled();
this.configXarpusEnabled = config.isXarpusEnabled();
this.configXarpusNotifyEnabled = config.isXarpusNotifyEnabled();
this.configaddyDrags = config.addyDrags();
this.configaddyDragsNotifyEnabled = config.addyDragsNotifyEnabled();
this.configDrakeEnabled = config.isDrakeEnabled();
this.configDrakeNotifyEnabled = config.isDrakeNotifyEnabled();
this.configCerbFireEnabled = config.isCerbFireEnabled();
this.configCerbFireNotifyEnabled = config.isCerbFireNotifyEnabled();
this.configDemonicGorillaEnabled = config.isDemonicGorillaEnabled();
this.configDemonicGorillaNotifyEnabled = config.isDemonicGorillaNotifyEnabled();
}
private void reset()
{
lightningTrail.clear();
acidTrail.clear();
crystalSpike.clear();
wintertodtSnowFall.clear();
bombs.clear();
projectiles.clear();
}
}
@@ -1,158 +0,0 @@
/*
* Copyright (c) 2018, PallasDieKatze (Pallas Cat)
* 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.aoewarnings;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.time.Instant;
import java.util.Locale;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.Perspective;
import net.runelite.api.Point;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
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.OverlayPriority;
import net.runelite.client.ui.overlay.OverlayUtil;
@Slf4j
@Singleton
public class BombOverlay extends Overlay
{
private static final String SAFE = "#00cc00";
private static final String CAUTION = "#ffff00";
private static final String WARNING = "#ff9933";
private static final String DANGER = "#ff6600";
private static final String LETHAL = "#cc0000";
private static final int BOMB_AOE = 7;
private static final int BOMB_DETONATE_TIME = 8;
private static final double ESTIMATED_TICK_LENGTH = .6;
private static final NumberFormat TIME_LEFT_FORMATTER =
DecimalFormat.getInstance(Locale.US);
static
{
((DecimalFormat) TIME_LEFT_FORMATTER).applyPattern("#0.0");
}
private final Client client;
private final AoeWarningPlugin plugin;
@Inject
public BombOverlay(final Client client, final AoeWarningPlugin plugin)
{
this.client = client;
this.plugin = plugin;
setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.ABOVE_SCENE);
setPriority(OverlayPriority.MED);
}
@Override
public Dimension render(Graphics2D graphics)
{
if (plugin.isConfigbombDisplay())
{
drawDangerZone(graphics);
}
return null;
}
private void drawDangerZone(Graphics2D graphics)
{
final WorldPoint loc = client.getLocalPlayer().getWorldLocation();
plugin.getBombs().forEach(bomb ->
{
final LocalPoint localLoc = LocalPoint.fromWorld(client, bomb.getWorldLocation());
final WorldPoint worldLoc = bomb.getWorldLocation();
if (localLoc == null)
{
return;
}
final double distance_x = Math.abs(worldLoc.getX() - loc.getX());
final double distance_y = Math.abs(worldLoc.getY() - loc.getY());
Color color_code = Color.decode(SAFE);
if (distance_x < 1 && distance_y < 1)
{
color_code = Color.decode(LETHAL);
}
else if (distance_x < 2 && distance_y < 2)
{
color_code = Color.decode(DANGER);
}
else if (distance_x < 3 && distance_y < 3)
{
color_code = Color.decode(WARNING);
}
else if (distance_x < 4 && distance_y < 4)
{
color_code = Color.decode(CAUTION);
}
final LocalPoint CenterPoint = new LocalPoint(localLoc.getX(), localLoc.getY());
final Polygon poly = Perspective.getCanvasTileAreaPoly(client, CenterPoint, BOMB_AOE);
if (poly != null)
{
graphics.setColor(color_code);
graphics.setStroke(new BasicStroke(1));
graphics.drawPolygon(poly);
graphics.setColor(new Color(0, 0, 0, 10));
graphics.fillPolygon(poly);
}
final Instant now = Instant.now();
double timeLeft = ((BOMB_DETONATE_TIME - (client.getTickCount() - bomb.getTickStarted())) * ESTIMATED_TICK_LENGTH) -
(now.toEpochMilli() - bomb.getLastClockUpdate().toEpochMilli()) / 1000.0;
timeLeft = Math.max(0.0, timeLeft);
final String bombTimerString = TIME_LEFT_FORMATTER.format(timeLeft);
final int textWidth = graphics.getFontMetrics().stringWidth(bombTimerString);
final int textHeight = graphics.getFontMetrics().getAscent();
final Point canvasPoint = Perspective.localToCanvas(client, localLoc.getX(), localLoc.getY(), worldLoc.getPlane());
if (canvasPoint != null)
{
Point canvasCenterPoint = new Point(canvasPoint.getX() - textWidth / 2, canvasPoint.getY() + textHeight / 2);
OverlayUtil.renderTextLocation(graphics, canvasCenterPoint, bombTimerString, color_code);
}
});
}
}
@@ -1,58 +0,0 @@
/*
* Copyright (c) 2018, PallasDieKatze (Pallas Cat)
* 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.aoewarnings;
import java.time.Instant;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.GameObject;
import net.runelite.api.coords.WorldPoint;
@Slf4j
@Getter(AccessLevel.PACKAGE)
class CrystalBomb
{
private GameObject gameObject;
private Instant plantedOn;
private Instant lastClockUpdate;
private int objectId;
private int tickStarted;
private WorldPoint worldLocation;
CrystalBomb(GameObject gameObject, int startTick)
{
this.gameObject = gameObject;
this.objectId = gameObject.getId();
this.plantedOn = Instant.now();
this.worldLocation = gameObject.getWorldLocation();
this.tickStarted = startTick;
}
void bombClockUpdate()
{
lastClockUpdate = Instant.now();
}
}
@@ -1,30 +0,0 @@
package net.runelite.client.plugins.aoewarnings;
import java.time.Instant;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import net.runelite.api.Projectile;
import net.runelite.api.coords.LocalPoint;
@Getter(AccessLevel.PACKAGE)
class ProjectileContainer
{
private Projectile projectile;
private Instant startTime;
private AoeProjectileInfo aoeProjectileInfo;
private int lifetime;
private int finalTick;
@Setter(AccessLevel.PACKAGE)
private LocalPoint targetPoint;
ProjectileContainer(Projectile projectile, Instant startTime, int lifetime, int finalTick)
{
this.projectile = projectile;
this.startTime = startTime;
this.targetPoint = null;
this.aoeProjectileInfo = AoeProjectileInfo.getById(projectile.getId());
this.lifetime = lifetime;
this.finalTick = finalTick;
}
}
@@ -1,59 +0,0 @@
/*
* Copyright (c) 2017, honeyhoney <https://github.com/honeyhoney>
* 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.attackstyles;
import net.runelite.api.Skill;
public enum AttackStyle
{
ACCURATE("Accurate", Skill.ATTACK),
AGGRESSIVE("Aggressive", Skill.STRENGTH),
DEFENSIVE("Defensive", Skill.DEFENCE),
CONTROLLED("Controlled", Skill.ATTACK, Skill.STRENGTH, Skill.DEFENCE),
RANGING("Ranging", Skill.RANGED),
LONGRANGE("Longrange", Skill.RANGED, Skill.DEFENCE),
CASTING("Casting", Skill.MAGIC),
DEFENSIVE_CASTING("Defensive Casting", Skill.MAGIC, Skill.DEFENCE),
OTHER("Other");
private final String name;
private final Skill[] skills;
AttackStyle(String name, Skill... skills)
{
this.name = name;
this.skills = skills;
}
public String getName()
{
return name;
}
public Skill[] getSkills()
{
return skills;
}
}
@@ -1,121 +0,0 @@
/*
* Copyright (c) 2017, honeyhoney <https://github.com/honeyhoney>
* 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.attackstyles;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
@ConfigGroup("attackIndicator")
public interface AttackStylesConfig extends Config
{
@ConfigItem(
keyName = "alwaysShowStyle",
name = "Always show style",
description = "Show attack style indicator at all times",
position = 1
)
default boolean alwaysShowStyle()
{
return true;
}
@ConfigItem(
keyName = "warnForDefensive",
name = "Warn for defence",
description = "Show warning when a Defence skill combat option is selected",
position = 2
)
default boolean warnForDefence()
{
return false;
}
@ConfigItem(
keyName = "warnForAttack",
name = "Warn for attack",
description = "Show warning when an Attack skill combat option is selected",
position = 3
)
default boolean warnForAttack()
{
return false;
}
@ConfigItem(
keyName = "warnForStrength",
name = "Warn for strength",
description = "Show warning when a Strength skill combat option is selected",
position = 4
)
default boolean warnForStrength()
{
return false;
}
@ConfigItem(
keyName = "warnForRanged",
name = "Warn for ranged",
description = "Show warning when a Ranged skill combat option is selected",
position = 5
)
default boolean warnForRanged()
{
return false;
}
@ConfigItem(
keyName = "warnForMagic",
name = "Warn for magic",
description = "Show warning when a Magic skill combat option is selected",
position = 6
)
default boolean warnForMagic()
{
return false;
}
@ConfigItem(
keyName = "hideAutoRetaliate",
name = "Hide auto retaliate",
description = "Hide auto retaliate from the combat options tab",
position = 7
)
default boolean hideAutoRetaliate()
{
return false;
}
@ConfigItem(
keyName = "removeWarnedStyles",
name = "Remove warned styles",
description = "Remove warned styles from the combat options tab",
position = 8
)
default boolean removeWarnedStyles()
{
return false;
}
}
@@ -1,86 +0,0 @@
/*
* Copyright (c) 2017, honeyhoney <https://github.com/honeyhoney>
* 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.attackstyles;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import javax.inject.Inject;
import javax.inject.Singleton;
import static net.runelite.api.MenuOpcode.RUNELITE_OVERLAY_CONFIG;
import net.runelite.client.ui.overlay.Overlay;
import static net.runelite.client.ui.overlay.OverlayManager.OPTION_CONFIGURE;
import net.runelite.client.ui.overlay.OverlayMenuEntry;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.components.PanelComponent;
import net.runelite.client.ui.overlay.components.TitleComponent;
@Singleton
class AttackStylesOverlay extends Overlay
{
private final AttackStylesPlugin plugin;
private final PanelComponent panelComponent = new PanelComponent();
@Inject
private AttackStylesOverlay(final AttackStylesPlugin plugin)
{
super(plugin);
setPosition(OverlayPosition.ABOVE_CHATBOX_RIGHT);
this.plugin = plugin;
getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY_CONFIG, OPTION_CONFIGURE, "Attack style overlay"));
}
@Override
public Dimension render(Graphics2D graphics)
{
panelComponent.getChildren().clear();
boolean warnedSkillSelected = plugin.isWarnedSkillSelected();
if (warnedSkillSelected || plugin.isAlwaysShowStyle())
{
final AttackStyle attackStyle = plugin.getAttackStyle();
if (attackStyle == null)
{
return null;
}
final String attackStyleString = attackStyle.getName();
panelComponent.getChildren().add(TitleComponent.builder()
.text(attackStyleString)
.color(warnedSkillSelected ? Color.RED : Color.WHITE)
.build());
panelComponent.setPreferredSize(new Dimension(
graphics.getFontMetrics().stringWidth(attackStyleString) + 10,
0));
return panelComponent.render(graphics);
}
return null;
}
}
@@ -1,416 +0,0 @@
/*
* Copyright (c) 2017, honeyhoney <https://github.com/honeyhoney>
* 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.attackstyles;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import com.google.inject.Provides;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.Skill;
import net.runelite.api.VarPlayer;
import net.runelite.api.Varbits;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.VarbitChanged;
import net.runelite.api.events.WidgetHiddenChanged;
import net.runelite.api.events.WidgetLoaded;
import net.runelite.api.widgets.Widget;
import static net.runelite.api.widgets.WidgetID.COMBAT_GROUP_ID;
import net.runelite.api.widgets.WidgetInfo;
import static net.runelite.api.widgets.WidgetInfo.TO_GROUP;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.PluginType;
import static net.runelite.client.plugins.attackstyles.AttackStyle.CASTING;
import static net.runelite.client.plugins.attackstyles.AttackStyle.DEFENSIVE_CASTING;
import static net.runelite.client.plugins.attackstyles.AttackStyle.OTHER;
import net.runelite.client.ui.overlay.OverlayManager;
@PluginDescriptor(
name = "Attack Styles",
description = "Show your current attack style as an overlay",
tags = {"combat", "defence", "magic", "overlay", "ranged", "strength", "warn", "pure"},
type = PluginType.UTILITY
)
@Singleton
public class AttackStylesPlugin extends Plugin
{
private int attackStyleVarbit = -1;
private int equippedWeaponTypeVarbit = -1;
private int castingModeVarbit = -1;
@Getter(AccessLevel.PACKAGE)
@Nullable
private AttackStyle attackStyle;
private final Set<Skill> warnedSkills = new HashSet<>();
@Getter(AccessLevel.PACKAGE)
private boolean warnedSkillSelected = false;
private final Table<WeaponType, WidgetInfo, Boolean> widgetsToHide = HashBasedTable.create();
@Inject
private Client client;
@Inject
private ClientThread clientThread;
@Inject
private AttackStylesConfig config;
@Inject
private OverlayManager overlayManager;
@Inject
private AttackStylesOverlay overlay;
@Provides
AttackStylesConfig provideConfig(ConfigManager configManager)
{
return configManager.getConfig(AttackStylesConfig.class);
}
// config values
@Getter(AccessLevel.PACKAGE)
private boolean alwaysShowStyle;
private boolean warnForDefence;
private boolean warnForAttack;
private boolean warnForStrength;
private boolean warnForRanged;
private boolean warnForMagic;
private boolean hideAutoRetaliate;
@VisibleForTesting
boolean removeWarnedStyles;
@Override
protected void startUp()
{
updateConfig();
overlayManager.add(overlay);
if (client.getGameState() == GameState.LOGGED_IN)
{
clientThread.invoke(this::start);
}
}
private void start()
{
resetWarnings();
attackStyleVarbit = client.getVar(VarPlayer.ATTACK_STYLE);
equippedWeaponTypeVarbit = client.getVar(Varbits.EQUIPPED_WEAPON_TYPE);
castingModeVarbit = client.getVar(Varbits.DEFENSIVE_CASTING_MODE);
updateAttackStyle(
equippedWeaponTypeVarbit,
attackStyleVarbit,
castingModeVarbit);
updateWarning(false);
processWidgets();
}
@Override
protected void shutDown()
{
overlayManager.remove(overlay);
hideWarnedStyles(false);
processWidgets();
hideWidget(client.getWidget(WidgetInfo.COMBAT_AUTO_RETALIATE), false);
}
@Subscribe
@VisibleForTesting
void onWidgetHiddenChanged(WidgetHiddenChanged event)
{
if (event.getWidget().isSelfHidden() || TO_GROUP(event.getWidget().getId()) != COMBAT_GROUP_ID)
{
return;
}
processWidgets();
}
@Subscribe
private void onWidgetLoaded(WidgetLoaded event)
{
if (event.getGroupId() != COMBAT_GROUP_ID)
{
return;
}
processWidgets();
}
/**
* Hide or unhide widgets depending on widgetsToHide
*/
private void processWidgets()
{
WeaponType equippedWeaponType = WeaponType.getWeaponType(equippedWeaponTypeVarbit);
if (widgetsToHide.containsRow(equippedWeaponType))
{
for (WidgetInfo widgetKey : widgetsToHide.row(equippedWeaponType).keySet())
{
hideWidget(client.getWidget(widgetKey), widgetsToHide.get(equippedWeaponType, widgetKey));
}
}
hideWidget(client.getWidget(WidgetInfo.COMBAT_AUTO_RETALIATE), this.hideAutoRetaliate);
}
@Subscribe
private void onGameStateChanged(GameStateChanged event)
{
if (event.getGameState() == GameState.LOGGED_IN)
{
resetWarnings();
}
}
@Subscribe
@VisibleForTesting
void onVarbitChanged(VarbitChanged event)
{
int currentAttackStyleVarbit = client.getVar(VarPlayer.ATTACK_STYLE);
int currentEquippedWeaponTypeVarbit = client.getVar(Varbits.EQUIPPED_WEAPON_TYPE);
int currentCastingModeVarbit = client.getVar(Varbits.DEFENSIVE_CASTING_MODE);
if (attackStyleVarbit != currentAttackStyleVarbit || equippedWeaponTypeVarbit != currentEquippedWeaponTypeVarbit || castingModeVarbit != currentCastingModeVarbit)
{
boolean weaponSwitch = currentEquippedWeaponTypeVarbit != equippedWeaponTypeVarbit;
attackStyleVarbit = currentAttackStyleVarbit;
equippedWeaponTypeVarbit = currentEquippedWeaponTypeVarbit;
castingModeVarbit = currentCastingModeVarbit;
updateAttackStyle(equippedWeaponTypeVarbit, attackStyleVarbit,
castingModeVarbit);
updateWarning(weaponSwitch);
if (weaponSwitch)
{
processWidgets();
}
}
}
@Subscribe
@VisibleForTesting
void onConfigChanged(ConfigChanged event)
{
if (event.getGroup().equals("attackIndicator"))
{
updateConfig();
boolean enabled = event.getNewValue().equals("true");
switch (event.getKey())
{
case "warnForDefensive":
updateWarnedSkills(enabled, Skill.DEFENCE);
break;
case "warnForAttack":
updateWarnedSkills(enabled, Skill.ATTACK);
break;
case "warnForStrength":
updateWarnedSkills(enabled, Skill.STRENGTH);
break;
case "warnForRanged":
updateWarnedSkills(enabled, Skill.RANGED);
break;
case "warnForMagic":
updateWarnedSkills(enabled, Skill.MAGIC);
break;
case "removeWarnedStyles":
hideWarnedStyles(enabled);
break;
}
processWidgets();
}
}
private void updateConfig()
{
this.alwaysShowStyle = config.alwaysShowStyle();
this.warnForDefence = config.warnForDefence();
this.warnForAttack = config.warnForAttack();
this.warnForStrength = config.warnForStrength();
this.warnForRanged = config.warnForRanged();
this.warnForMagic = config.warnForMagic();
this.hideAutoRetaliate = config.hideAutoRetaliate();
this.removeWarnedStyles = config.removeWarnedStyles();
}
private void resetWarnings()
{
updateWarnedSkills(this.warnForAttack, Skill.ATTACK);
updateWarnedSkills(this.warnForStrength, Skill.STRENGTH);
updateWarnedSkills(this.warnForDefence, Skill.DEFENCE);
updateWarnedSkills(this.warnForRanged, Skill.RANGED);
updateWarnedSkills(this.warnForMagic, Skill.MAGIC);
}
private void updateAttackStyle(int equippedWeaponType, int attackStyleIndex, int castingMode)
{
AttackStyle[] attackStyles = WeaponType.getWeaponType(equippedWeaponType).getAttackStyles();
if (attackStyleIndex < attackStyles.length)
{
attackStyle = attackStyles[attackStyleIndex];
if (attackStyle == null)
{
attackStyle = OTHER;
}
else if ((attackStyle == CASTING) && (castingMode == 1))
{
attackStyle = DEFENSIVE_CASTING;
}
}
}
private void updateWarnedSkills(boolean enabled, Skill skill)
{
if (enabled)
{
warnedSkills.add(skill);
}
else
{
warnedSkills.remove(skill);
}
updateWarning(false);
}
private void updateWarning(boolean weaponSwitch)
{
warnedSkillSelected = false;
if (attackStyle != null)
{
for (Skill skill : attackStyle.getSkills())
{
if (warnedSkills.contains(skill))
{
// if (weaponSwitch)
// {
// // TODO : chat message to warn players that their weapon switch also caused an unwanted attack style change
// }
warnedSkillSelected = true;
break;
}
}
}
hideWarnedStyles(this.removeWarnedStyles);
}
private void hideWarnedStyles(boolean enabled)
{
WeaponType equippedWeaponType = WeaponType.getWeaponType(equippedWeaponTypeVarbit);
if (equippedWeaponType == null)
{
return;
}
AttackStyle[] attackStyles = equippedWeaponType.getAttackStyles();
// Iterate over attack styles
for (int i = 0; i < attackStyles.length; i++)
{
AttackStyle attackStyle = attackStyles[i];
if (attackStyle == null)
{
continue;
}
boolean warnedSkill = false;
for (Skill skill : attackStyle.getSkills())
{
if (warnedSkills.contains(skill))
{
warnedSkill = true;
break;
}
}
// Magic staves defensive casting mode
if (attackStyle == AttackStyle.DEFENSIVE_CASTING || !enabled)
{
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_DEFENSIVE_SPELL_BOX, enabled && warnedSkill);
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_DEFENSIVE_SPELL_ICON, enabled && warnedSkill);
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_DEFENSIVE_SPELL_SHIELD, enabled && warnedSkill);
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_DEFENSIVE_SPELL_TEXT, enabled && warnedSkill);
}
// Remove appropriate combat option
switch (i)
{
case 0:
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_STYLE_ONE, enabled && warnedSkill);
break;
case 1:
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_STYLE_TWO, enabled && warnedSkill);
break;
case 2:
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_STYLE_THREE, enabled && warnedSkill);
break;
case 3:
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_STYLE_FOUR, enabled && warnedSkill);
break;
case 4:
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_SPELLS, enabled && warnedSkill);
break;
default:
// 5 can be defensive casting
}
}
}
private void hideWidget(Widget widget, boolean hidden)
{
if (widget != null)
{
widget.setHidden(hidden);
}
}
@VisibleForTesting
Set<Skill> getWarnedSkills()
{
return warnedSkills;
}
@VisibleForTesting
Table<WeaponType, WidgetInfo, Boolean> getHiddenWidgets()
{
return widgetsToHide;
}
}
@@ -1,100 +0,0 @@
/*
* Copyright (c) 2017, honeyhoney <https://github.com/honeyhoney>
* 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.attackstyles;
import com.google.common.collect.ImmutableMap;
import java.util.Map;
import static net.runelite.client.plugins.attackstyles.AttackStyle.ACCURATE;
import static net.runelite.client.plugins.attackstyles.AttackStyle.AGGRESSIVE;
import static net.runelite.client.plugins.attackstyles.AttackStyle.CASTING;
import static net.runelite.client.plugins.attackstyles.AttackStyle.CONTROLLED;
import static net.runelite.client.plugins.attackstyles.AttackStyle.DEFENSIVE;
import static net.runelite.client.plugins.attackstyles.AttackStyle.DEFENSIVE_CASTING;
import static net.runelite.client.plugins.attackstyles.AttackStyle.LONGRANGE;
import static net.runelite.client.plugins.attackstyles.AttackStyle.OTHER;
import static net.runelite.client.plugins.attackstyles.AttackStyle.RANGING;
public enum WeaponType
{
TYPE_0(ACCURATE, AGGRESSIVE, null, DEFENSIVE),
TYPE_1(ACCURATE, AGGRESSIVE, AGGRESSIVE, DEFENSIVE),
TYPE_2(ACCURATE, AGGRESSIVE, null, DEFENSIVE),
TYPE_3(RANGING, RANGING, null, LONGRANGE),
TYPE_4(ACCURATE, AGGRESSIVE, CONTROLLED, DEFENSIVE),
TYPE_5(RANGING, RANGING, null, LONGRANGE),
TYPE_6(AGGRESSIVE, RANGING, DEFENSIVE_CASTING, null),
TYPE_7(RANGING, RANGING, null, LONGRANGE),
TYPE_8(OTHER, AGGRESSIVE, null, null),
TYPE_9(ACCURATE, AGGRESSIVE, CONTROLLED, DEFENSIVE),
TYPE_10(ACCURATE, AGGRESSIVE, AGGRESSIVE, DEFENSIVE),
TYPE_11(ACCURATE, AGGRESSIVE, AGGRESSIVE, DEFENSIVE),
TYPE_12(CONTROLLED, AGGRESSIVE, null, DEFENSIVE),
TYPE_13(ACCURATE, AGGRESSIVE, null, DEFENSIVE),
TYPE_14(ACCURATE, AGGRESSIVE, AGGRESSIVE, DEFENSIVE),
TYPE_15(CONTROLLED, CONTROLLED, CONTROLLED, DEFENSIVE),
TYPE_16(ACCURATE, AGGRESSIVE, CONTROLLED, DEFENSIVE),
TYPE_17(ACCURATE, AGGRESSIVE, AGGRESSIVE, DEFENSIVE),
TYPE_18(ACCURATE, AGGRESSIVE, null, DEFENSIVE, CASTING, DEFENSIVE_CASTING),
TYPE_19(RANGING, RANGING, null, LONGRANGE),
TYPE_20(ACCURATE, CONTROLLED, null, DEFENSIVE),
TYPE_21(ACCURATE, AGGRESSIVE, null, DEFENSIVE, CASTING, DEFENSIVE_CASTING),
TYPE_22(ACCURATE, AGGRESSIVE, AGGRESSIVE, DEFENSIVE),
TYPE_23(CASTING, CASTING, null, DEFENSIVE_CASTING),
TYPE_24(ACCURATE, AGGRESSIVE, CONTROLLED, DEFENSIVE),
TYPE_25(CONTROLLED, AGGRESSIVE, null, DEFENSIVE),
TYPE_26(AGGRESSIVE, AGGRESSIVE, null, AGGRESSIVE),
TYPE_27(ACCURATE, null, null, OTHER);
private final AttackStyle[] attackStyles;
private static final Map<Integer, WeaponType> weaponTypes;
static
{
ImmutableMap.Builder<Integer, WeaponType> builder = new ImmutableMap.Builder<>();
for (WeaponType weaponType : values())
{
builder.put(weaponType.ordinal(), weaponType);
}
weaponTypes = builder.build();
}
WeaponType(AttackStyle... attackStyles)
{
this.attackStyles = attackStyles;
}
public AttackStyle[] getAttackStyles()
{
return attackStyles;
}
public static WeaponType getWeaponType(int id)
{
return weaponTypes.get(id);
}
}
@@ -1,212 +0,0 @@
/*
* Copyright (c) 2018, TheLonelyDev <https://github.com/TheLonelyDev>
* Copyright (c) 2018, Jeremy Plsek <https://github.com/jplsek>
* 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.bank;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
import net.runelite.client.config.ConfigSection;
@ConfigGroup("bank")
public interface BankConfig extends Config
{
@ConfigSection(
name = "Show Values",
description = "",
position = 0,
keyName = "ValueSection"
)
default boolean ValueSection()
{
return false;
}
@ConfigSection(
name = "Disable Left Click",
description = "",
position = 1,
keyName = "DisableLClickSection"
)
default boolean DisableLClickSection()
{
return false;
}
@ConfigSection(
name = "Bank Pin",
description = "",
position = 2,
keyName = "BankPinSection"
)
default boolean BankPinSection()
{
return false;
}
@ConfigItem(
keyName = "showGE",
name = "Show Grand Exchange price",
description = "Show grand exchange price total (GE)",
position = 0,
section = "ValueSection"
)
default boolean showGE()
{
return true;
}
@ConfigItem(
keyName = "showHA",
name = "Show high alchemy price",
description = "Show high alchemy price total (HA)",
position = 2,
section = "ValueSection"
)
default boolean showHA()
{
return false;
}
@ConfigItem(
keyName = "showExact",
name = "Show exact bank value",
description = "Show exact bank value",
position = 3,
section = "ValueSection"
)
default boolean showExact()
{
return false;
}
@ConfigItem(
keyName = "seedVaultValue",
name = "Show seed vault value",
description = "Adds the total value of all seeds inside the seed vault to the title",
position = 4,
section = "ValueSection"
)
default boolean seedVaultValue()
{
return true;
}
@ConfigItem(
keyName = "rightClickBankInventory",
name = "Disable left click bank inventory",
description = "Configures whether the bank inventory button will bank your inventory on left click",
position = 0,
section = "DisableLClickSection"
)
default boolean rightClickBankInventory()
{
return false;
}
@ConfigItem(
keyName = "rightClickBankEquip",
name = "Disable left click bank equipment",
description = "Configures whether the bank equipment button will bank your equipment on left click",
position = 1,
section = "DisableLClickSection"
)
default boolean rightClickBankEquip()
{
return false;
}
@ConfigItem(
keyName = "rightClickBankLoot",
name = "Disable left click bank looting bag",
description = "Configures whether the bank looting bag button will bank your looting bag contents on left click",
position = 2,
section = "DisableLClickSection"
)
default boolean rightClickBankLoot()
{
return false;
}
@ConfigItem(
keyName = "rightClickSetPlaceholders",
name = "Disable left click set placeholders",
description = "Configures whether the set bank placeholder button will be on left click",
position = 3,
section = "DisableLClickSection"
)
default boolean rightClickSetPlaceholders()
{
return false;
}
@ConfigItem(
keyName = "rightClickReleasePlaceholders",
name = "Disable left click release placeholder",
description = "Configures whether the release bank placeholder button will be on left click",
position = 4,
section = "DisableLClickSection"
)
default boolean rightClickReleasePlaceholders()
{
return false;
}
@ConfigItem(
keyName = "rightClickFillBankFiller",
name = "Disable left click fill bank fillers",
description = "Configures whether the fill bank fillers button will be on left click",
position = 5,
section = "DisableLClickSection"
)
default boolean rightClickFillBankFiller()
{
return false;
}
@ConfigItem(
keyName = "rightClickSearch",
name = "Disable left click search",
description = "Configures whether the search button will search on left click",
position = 6,
section = "DisableLClickSection"
)
default boolean rightClickSearch()
{
return false;
}
@ConfigItem(
keyName = "largePinNumbers",
name = "Large bank pin numbers",
description = "Enlarges and centers the numbers inside the bank pin buttons",
position = 0,
section = "BankPinSection"
)
default boolean largePinNumbers()
{
return false;
}
}
@@ -1,597 +0,0 @@
/*
* Copyright (c) 2018, TheLonelyDev <https://github.com/TheLonelyDev>
* Copyright (c) 2018, Jeremy Plsek <https://github.com/jplsek>
* Copyright (c) 2019, Hydrox6 <ikada@protonmail.ch>
* 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.bank;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multiset;
import com.google.inject.Provides;
import java.text.ParseException;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.api.Client;
import static net.runelite.api.Constants.HIGH_ALCHEMY_MULTIPLIER;
import net.runelite.api.FontID;
import net.runelite.api.InventoryID;
import net.runelite.api.Item;
import net.runelite.api.ItemContainer;
import net.runelite.api.ItemDefinition;
import net.runelite.api.ItemID;
import net.runelite.api.MenuEntry;
import net.runelite.api.SpriteID;
import net.runelite.api.VarClientInt;
import net.runelite.api.VarClientStr;
import net.runelite.api.Varbits;
import net.runelite.api.events.ItemContainerChanged;
import net.runelite.api.events.MenuEntryAdded;
import net.runelite.api.events.MenuShouldLeftClick;
import net.runelite.api.events.ScriptCallbackEvent;
import net.runelite.api.events.VarClientStrChanged;
import net.runelite.api.events.WidgetLoaded;
import net.runelite.api.vars.InputType;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetID;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
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.plugins.PluginType;
import net.runelite.client.plugins.banktags.tabs.BankSearch;
import net.runelite.client.util.QuantityFormatter;
@PluginDescriptor(
name = "Bank",
description = "Modifications to the banking interface",
tags = {"grand", "exchange", "high", "alchemy", "prices", "deposit"},
type = PluginType.UTILITY
)
@Singleton
public class BankPlugin extends Plugin
{
private static final List<Varbits> TAB_VARBITS = ImmutableList.of(
Varbits.BANK_TAB_ONE_COUNT,
Varbits.BANK_TAB_TWO_COUNT,
Varbits.BANK_TAB_THREE_COUNT,
Varbits.BANK_TAB_FOUR_COUNT,
Varbits.BANK_TAB_FIVE_COUNT,
Varbits.BANK_TAB_SIX_COUNT,
Varbits.BANK_TAB_SEVEN_COUNT,
Varbits.BANK_TAB_EIGHT_COUNT,
Varbits.BANK_TAB_NINE_COUNT
);
private static final List<WidgetInfo> BANK_PINS = ImmutableList.of(
WidgetInfo.BANK_PIN_1,
WidgetInfo.BANK_PIN_2,
WidgetInfo.BANK_PIN_3,
WidgetInfo.BANK_PIN_4,
WidgetInfo.BANK_PIN_5,
WidgetInfo.BANK_PIN_6,
WidgetInfo.BANK_PIN_7,
WidgetInfo.BANK_PIN_8,
WidgetInfo.BANK_PIN_9,
WidgetInfo.BANK_PIN_10
);
private static final String DEPOSIT_WORN = "Deposit worn items";
private static final String DEPOSIT_INVENTORY = "Deposit inventory";
private static final String DEPOSIT_LOOT = "Deposit loot";
private static final String DISABLE = "Disable";
private static final String ENABLE = "Enable";
private static final String RELEASE_ALL_PLACEHOLDERS = "Release all placeholders";
private static final String SEARCH = "Search";
private static final String FILL = "Fill";
private static final String SEED_VAULT_TITLE = "Seed Vault";
private static final int PIN_FONT_OFFSET = 5;
private static final String NUMBER_REGEX = "[0-9]+(\\.[0-9]+)?[kmb]?";
private static final Pattern VALUE_SEARCH_PATTERN = Pattern.compile("^(?<mode>ge|ha|alch)?" +
" *(((?<op>[<>=]|>=|<=) *(?<num>" + NUMBER_REGEX + "))|" +
"((?<num1>" + NUMBER_REGEX + ") *- *(?<num2>" + NUMBER_REGEX + ")))$", Pattern.CASE_INSENSITIVE);
@Inject
private Client client;
@Inject
private ClientThread clientThread;
@Inject
private ItemManager itemManager;
@Inject
private BankConfig config;
@Inject
private BankSearch bankSearch;
@Inject
private ContainerCalculation bankCalculation;
@Inject
private ContainerCalculation seedVaultCalculation;
private boolean forceRightClickFlag;
private boolean largePinNumbers;
private Multiset<Integer> itemQuantities; // bank item quantities for bank value search
private String searchString;
@Getter(AccessLevel.PACKAGE)
private boolean showGE;
@Getter(AccessLevel.PACKAGE)
private boolean showHA;
private boolean showExact;
private boolean rightClickBankInventory;
private boolean rightClickBankEquip;
private boolean rightClickBankLoot;
private boolean seedVaultValue;
private boolean rightClickSetPlaceholders;
private boolean rightClickReleasePlaceholders;
private boolean rightClickSearch;
private boolean rightClickFillBankFiller;
@Provides
BankConfig getConfig(ConfigManager configManager)
{
return configManager.getConfig(BankConfig.class);
}
@Override
protected void startUp()
{
updateConfig();
searchString = "";
}
@Override
protected void shutDown()
{
clientThread.invokeLater(() -> bankSearch.reset(false));
forceRightClickFlag = false;
itemQuantities = null;
}
@Subscribe
private void onMenuShouldLeftClick(MenuShouldLeftClick event)
{
if (!forceRightClickFlag)
{
return;
}
forceRightClickFlag = false;
MenuEntry[] menuEntries = client.getMenuEntries();
for (MenuEntry entry : menuEntries)
{
if ((entry.getOption().equals(DEPOSIT_WORN) && this.rightClickBankEquip)
|| (entry.getOption().equals(DEPOSIT_INVENTORY) && this.rightClickBankInventory)
|| (entry.getOption().equals(DEPOSIT_LOOT) && this.rightClickBankLoot)
|| (entry.getOption().equals(DISABLE) && this.rightClickSetPlaceholders)
|| (entry.getOption().equals(ENABLE) && this.rightClickSetPlaceholders)
|| (entry.getOption().equals(RELEASE_ALL_PLACEHOLDERS) && this.rightClickReleasePlaceholders)
|| (entry.getOption().equals(SEARCH) && this.rightClickSearch)
|| (entry.getOption().equals(FILL) && this.rightClickFillBankFiller))
{
event.setForceRightClick(true);
return;
}
}
}
@Subscribe
private void onMenuEntryAdded(MenuEntryAdded event)
{
if ((event.getOption().equals(DEPOSIT_WORN) && this.rightClickBankEquip)
|| (event.getOption().equals(DEPOSIT_INVENTORY) && this.rightClickBankInventory)
|| (event.getOption().equals(DEPOSIT_LOOT) && this.rightClickBankLoot)
|| (event.getOption().startsWith(DISABLE) && this.rightClickSetPlaceholders)
|| (event.getOption().startsWith(ENABLE) && this.rightClickSetPlaceholders)
|| (event.getOption().equals(RELEASE_ALL_PLACEHOLDERS) && this.rightClickReleasePlaceholders)
|| (event.getOption().equals(SEARCH) && this.rightClickSearch)
|| (event.getOption().equals(FILL) && this.rightClickFillBankFiller))
{
forceRightClickFlag = true;
}
}
@Subscribe
private void onScriptCallbackEvent(ScriptCallbackEvent event)
{
if (event.getEventName().equals("bankPinButtons") && this.largePinNumbers)
{
updateBankPinSizes();
}
if (!event.getEventName().equals("setBankTitle"))
{
return;
}
int[] intStack = client.getIntStack();
String[] stringStack = client.getStringStack();
int intStackSize = client.getIntStackSize();
int stringStackSize = client.getStringStackSize();
switch (event.getEventName())
{
case "setBankTitle":
final ContainerPrices prices = bankCalculation.calculate(getBankTabItems());
if (prices == null)
{
return;
}
final String strCurrentTab = createValueText(prices);
stringStack[stringStackSize - 1] += strCurrentTab;
break;
case "bankSearchFilter":
int itemId = intStack[intStackSize - 1];
String search = stringStack[stringStackSize - 1];
if (valueSearch(itemId, search))
{
// return true
intStack[intStackSize - 2] = 1;
}
break;
}
}
@Subscribe
private void onWidgetLoaded(WidgetLoaded event)
{
if (event.getGroupId() != WidgetID.SEED_VAULT_GROUP_ID || !this.seedVaultValue)
{
return;
}
updateSeedVaultTotal();
}
@Subscribe
public void onVarClientStrChanged(VarClientStrChanged event)
{
String searchVar = client.getVar(VarClientStr.INPUT_TEXT);
if (!searchVar.equals(searchString))
{
Widget searchButtonBackground = client.getWidget(WidgetInfo.BANK_SEARCH_BUTTON_BACKGROUND);
if (searchButtonBackground != null && searchButtonBackground.hasListener())
{
searchButtonBackground.setOnTimerListener((Object[]) null);
searchButtonBackground.setHasListener(false);
}
clientThread.invokeLater(() -> bankSearch.layoutBank());
searchString = searchVar;
}
if (client.getVar(VarClientInt.INPUT_TYPE) != InputType.SEARCH.getType() && Strings.isNullOrEmpty(client.getVar(VarClientStr.INPUT_TEXT)))
{
Widget searchBackground = client.getWidget(WidgetInfo.BANK_SEARCH_BUTTON_BACKGROUND);
if (searchBackground != null)
{
searchBackground.setSpriteId(SpriteID.EQUIPMENT_SLOT_TILE);
}
}
}
@Subscribe
public void onItemContainerChanged(ItemContainerChanged event)
{
int containerId = event.getContainerId();
if (containerId == InventoryID.BANK.getId())
{
itemQuantities = null;
}
else if (containerId == InventoryID.SEED_VAULT.getId() && this.seedVaultValue)
{
updateSeedVaultTotal();
}
}
private String createValueText(final ContainerPrices prices)
{
final long gePrice = prices.getGePrice();
final long haPrice = prices.getHighAlchPrice();
String strCurrentTab = "";
if (this.showGE && gePrice != 0)
{
strCurrentTab += " (";
if (this.showHA)
{
strCurrentTab += "EX: ";
}
if (this.showExact)
{
strCurrentTab += QuantityFormatter.formatNumber(gePrice) + ")";
}
else
{
strCurrentTab += QuantityFormatter.quantityToStackSize(gePrice) + ")";
}
}
if (this.showHA && haPrice != 0)
{
strCurrentTab += " (";
if (this.showGE)
{
strCurrentTab += "HA: ";
}
if (this.showExact)
{
strCurrentTab += QuantityFormatter.formatNumber(haPrice) + ")";
}
else
{
strCurrentTab += QuantityFormatter.quantityToStackSize(haPrice) + ")";
}
}
return strCurrentTab;
}
private Item[] getBankTabItems()
{
final ItemContainer container = client.getItemContainer(InventoryID.BANK);
if (container == null)
{
return null;
}
final Item[] items = container.getItems();
int currentTab = client.getVar(Varbits.CURRENT_BANK_TAB);
if (currentTab > 0)
{
int startIndex = 0;
for (int i = currentTab - 1; i > 0; i--)
{
startIndex += client.getVar(TAB_VARBITS.get(i - 1));
}
int itemCount = client.getVar(TAB_VARBITS.get(currentTab - 1));
return Arrays.copyOfRange(items, startIndex, startIndex + itemCount);
}
return items;
}
private void updateSeedVaultTotal()
{
final Widget titleContainer = client.getWidget(WidgetInfo.SEED_VAULT_TITLE_CONTAINER);
if (titleContainer == null)
{
return;
}
final Widget[] children = titleContainer.getDynamicChildren();
if (children == null || children.length < 2)
{
return;
}
final ContainerPrices prices = seedVaultCalculation.calculate(getSeedVaultItems());
if (prices == null)
{
return;
}
final String titleText = createValueText(prices);
final Widget title = children[1];
title.setText(SEED_VAULT_TITLE + titleText);
}
private Item[] getSeedVaultItems()
{
final ItemContainer itemContainer = client.getItemContainer(InventoryID.SEED_VAULT);
if (itemContainer == null)
{
return null;
}
return itemContainer.getItems();
}
@Subscribe
private void onConfigChanged(ConfigChanged event)
{
if (!event.getGroup().equals("bank"))
{
return;
}
updateConfig();
}
private void updateBankPinSizes()
{
for (final WidgetInfo widgetInfo : BANK_PINS)
{
final Widget pin = client.getWidget(widgetInfo);
if (pin == null)
{
continue;
}
final Widget[] children = pin.getDynamicChildren();
if (children.length < 2)
{
continue;
}
final Widget button = children[0];
final Widget number = children[1];
// Change to a bigger font size
number.setFontId(FontID.QUILL_CAPS_LARGE);
number.setYTextAlignment(0);
// Change size to match container widths
number.setOriginalWidth(button.getWidth());
// The large font id text isn't centered, we need to offset it slightly
number.setOriginalHeight(button.getHeight() + PIN_FONT_OFFSET);
number.setOriginalY(-PIN_FONT_OFFSET);
number.setOriginalX(0);
number.revalidate();
}
}
private void updateConfig()
{
this.showGE = config.showGE();
this.showHA = config.showHA();
this.largePinNumbers = config.largePinNumbers();
this.showExact = config.showExact();
this.rightClickBankInventory = config.rightClickBankInventory();
this.rightClickBankEquip = config.rightClickBankEquip();
this.rightClickBankLoot = config.rightClickBankLoot();
this.seedVaultValue = config.seedVaultValue();
this.rightClickSetPlaceholders = config.rightClickSetPlaceholders();
this.rightClickReleasePlaceholders = config.rightClickReleasePlaceholders();
this.rightClickSearch = config.rightClickSearch();
this.rightClickFillBankFiller = config.rightClickFillBankFiller();
}
@VisibleForTesting
boolean valueSearch(final int itemId, final String str)
{
final Matcher matcher = VALUE_SEARCH_PATTERN.matcher(str);
if (!matcher.matches())
{
return false;
}
// Count bank items and remember it for determining item quantity
if (itemQuantities == null)
{
itemQuantities = getBankItemSet();
}
final ItemDefinition itemComposition = itemManager.getItemDefinition(itemId);
long gePrice = (long) itemManager.getItemPrice(itemId) * (long) itemQuantities.count(itemId);
long haPrice = (long) (itemComposition.getPrice() * HIGH_ALCHEMY_MULTIPLIER) * (long) itemQuantities.count(itemId);
long value = Math.max(gePrice, haPrice);
final String mode = matcher.group("mode");
if (mode != null)
{
value = mode.toLowerCase().equals("ge") ? gePrice : haPrice;
}
final String op = matcher.group("op");
if (op != null)
{
long compare;
try
{
compare = QuantityFormatter.parseQuantity(matcher.group("num"));
}
catch (ParseException e)
{
return false;
}
switch (op)
{
case ">":
return value > compare;
case "<":
return value < compare;
case "=":
return value == compare;
case ">=":
return value >= compare;
case "<=":
return value <= compare;
}
}
final String num1 = matcher.group("num1");
final String num2 = matcher.group("num2");
if (num1 != null && num2 != null)
{
long compare1, compare2;
try
{
compare1 = QuantityFormatter.parseQuantity(num1);
compare2 = QuantityFormatter.parseQuantity(num2);
}
catch (ParseException e)
{
return false;
}
return compare1 <= value && compare2 >= value;
}
return false;
}
private Multiset<Integer> getBankItemSet()
{
ItemContainer itemContainer = client.getItemContainer(InventoryID.BANK);
if (itemContainer == null)
{
return HashMultiset.create();
}
Multiset<Integer> set = HashMultiset.create();
for (Item item : itemContainer.getItems())
{
if (item.getId() != ItemID.BANK_FILLER)
{
set.add(item.getId(), item.getQuantity());
}
}
return set;
}
}
@@ -1,118 +0,0 @@
/*
* Copyright (c) 2019, TheStonedTurtle <https://github.com/TheStonedTurtle>
* Copyright (c) 2019, Adam <Adam@sigterm.info>
* 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.bank;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;
import javax.inject.Inject;
import net.runelite.api.Constants;
import net.runelite.api.Item;
import net.runelite.api.ItemID;
import net.runelite.client.game.ItemManager;
class ContainerCalculation
{
private final ItemManager itemManager;
private int hash;
private ContainerPrices containerPrices;
@Inject
private ContainerCalculation(ItemManager itemManager)
{
this.itemManager = itemManager;
}
@Nullable
ContainerPrices calculate(Item[] items)
{
// Returns last calculation if inventory hasn't changed
if (items == null)
{
return null;
}
final int newHash = hashItems(items);
if (containerPrices != null && hash == newHash)
{
return containerPrices;
}
hash = newHash;
long ge = 0;
long alch = 0;
for (final Item item : items)
{
final int qty = item.getQuantity();
final int id = item.getId();
if (id <= 0 || qty == 0)
{
continue;
}
switch (id)
{
case ItemID.COINS_995:
ge += qty;
alch += qty;
break;
case ItemID.PLATINUM_TOKEN:
ge += qty * 1000L;
alch += qty * 1000L;
break;
default:
final long storePrice = itemManager.getItemDefinition(id).getPrice();
final long alchPrice = (long) (storePrice * Constants.HIGH_ALCHEMY_MULTIPLIER);
alch += alchPrice * qty;
ge += (long) itemManager.getItemPrice(id) * qty;
break;
}
}
ContainerPrices prices = new ContainerPrices(ge, alch);
containerPrices = prices;
return prices;
}
private int hashItems(final Item[] items)
{
final Map<Integer, Integer> mapCheck = new HashMap<>(items.length);
for (Item item : items)
{
mapCheck.put(item.getId(), item.getQuantity());
}
return mapCheck.hashCode();
}
}
@@ -1,34 +0,0 @@
/*
* Copyright (c) 2019, TheStonedTurtle <https://github.com/TheStonedTurtle>
* 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.bank;
import lombok.Value;
@Value
class ContainerPrices
{
private long gePrice;
private long highAlchPrice;
}
@@ -1,113 +0,0 @@
/*
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
* 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.banktags;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
@ConfigGroup("banktags")
public interface BankTagsConfig extends Config
{
@ConfigItem(
keyName = "useTabs",
name = "Use Tag Tabs",
description = "Enable the ability to add tabs to your bank which allow fast access to tags.",
position = 1
)
default boolean tabs()
{
return true;
}
@ConfigItem(
keyName = "rememberTab",
name = "Remember last Tag Tab",
description = "Enable the ability to remember last Tag Tab when closing/opening the bank.",
position = 2
)
default boolean rememberTab()
{
return true;
}
@ConfigItem(
keyName = "removeSeparators",
name = "Remove tab separators in Tag Tabs",
description = "Removes tab separators and corrects item layouts in Tag Tabs to mimic a regular tab",
position = 3
)
default boolean removeSeparators()
{
return true;
}
@ConfigItem(
keyName = "hidePlaceholders",
name = "Hide placeholders",
description = "Hide placeholders in tag tabs or tag search.",
position = 4
)
default boolean hidePlaceholders()
{
return false;
}
@ConfigItem(
keyName = "position",
name = "",
description = "",
hidden = true
)
default int position()
{
return 0;
}
@ConfigItem(
keyName = "position",
name = "",
description = ""
)
void position(int idx);
@ConfigItem(
keyName = "tab",
name = "",
description = "",
hidden = true
)
default String tab()
{
return "";
}
@ConfigItem(
keyName = "tab",
name = "",
description = ""
)
void tab(String tab);
}
@@ -1,658 +0,0 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* Copyright (c) 2018, Lucas <https://github.com/Lucwousin>
* 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.banktags;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import com.google.common.collect.Lists;
import com.google.inject.Provides;
import java.awt.event.KeyEvent;
import java.awt.event.MouseWheelEvent;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.api.Client;
import static net.runelite.api.Constants.HIGH_ALCHEMY_MULTIPLIER;
import net.runelite.api.InventoryID;
import net.runelite.api.Item;
import net.runelite.api.ItemContainer;
import net.runelite.api.ItemDefinition;
import net.runelite.api.ItemID;
import net.runelite.api.MenuOpcode;
import net.runelite.api.VarClientInt;
import net.runelite.api.VarClientStr;
import net.runelite.api.events.DraggingWidgetChanged;
import net.runelite.api.events.FocusChanged;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.ItemContainerChanged;
import net.runelite.api.events.MenuEntryAdded;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.api.events.ScriptCallbackEvent;
import net.runelite.api.events.WidgetLoaded;
import net.runelite.api.util.Text;
import net.runelite.api.vars.InputType;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetID;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.game.ItemManager;
import net.runelite.client.game.SpriteManager;
import net.runelite.client.game.chatbox.ChatboxPanelManager;
import net.runelite.client.input.KeyListener;
import net.runelite.client.input.KeyManager;
import net.runelite.client.input.MouseManager;
import net.runelite.client.input.MouseWheelListener;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDependency;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.PluginType;
import net.runelite.client.plugins.banktags.tabs.BankSearch;
import net.runelite.client.plugins.banktags.tabs.TabInterface;
import static net.runelite.client.plugins.banktags.tabs.TabInterface.FILTERED_CHARS;
import net.runelite.client.plugins.banktags.tabs.TabSprites;
import net.runelite.client.plugins.cluescrolls.ClueScrollPlugin;
import net.runelite.client.util.QuantityFormatter;
@PluginDescriptor(
name = "Bank Tags",
description = "Enable tagging of bank items and searching of bank tags",
tags = {"searching", "tagging"},
type = PluginType.UTILITY
)
@PluginDependency(ClueScrollPlugin.class)
@Singleton
public class BankTagsPlugin extends Plugin implements MouseWheelListener, KeyListener
{
public static final String CONFIG_GROUP = "banktags";
public static final String TAG_SEARCH = "tag:";
public static final String ICON_SEARCH = "icon_";
public static final String TAG_TABS_CONFIG = "tagtabs";
public static final String VAR_TAG_SUFFIX = "*";
private static final String EDIT_TAGS_MENU_OPTION = "Edit-tags";
private static final String NUMBER_REGEX = "[0-9]+(\\.[0-9]+)?[kmb]?";
private static final String SEARCH_BANK_INPUT_TEXT =
"Show items whose names or tags contain the following text:<br>" +
"(To show only tagged items, start your search with 'tag:')";
private static final String SEARCH_BANK_INPUT_TEXT_FOUND =
"Show items whose names or tags contain the following text: (%d found)<br>" +
"(To show only tagged items, start your search with 'tag:')";
private static final Pattern VALUE_SEARCH_PATTERN = Pattern.compile("^(?<mode>ge|ha|alch)?" +
" *(((?<op>[<>=]|>=|<=) *(?<num>" + NUMBER_REGEX + "))|" +
"((?<num1>" + NUMBER_REGEX + ") *- *(?<num2>" + NUMBER_REGEX + ")))$", Pattern.CASE_INSENSITIVE);
@VisibleForTesting
final Multiset<Integer> itemQuantities = HashMultiset.create();
@Inject
private ItemManager itemManager;
@Inject
private Client client;
@Inject
private ClientThread clientThread;
@Inject
private ChatboxPanelManager chatboxPanelManager;
@Inject
private MouseManager mouseManager;
@Inject
private BankTagsConfig config;
@Inject
private TagManager tagManager;
@Inject
private TabInterface tabInterface;
@Inject
private BankSearch bankSearch;
@Inject
private KeyManager keyManager;
@Inject
private SpriteManager spriteManager;
@Inject
private ConfigManager configManager;
private boolean shiftPressed = false;
private int nextRowIndex = 0;
@Provides
BankTagsConfig getConfig(ConfigManager configManager)
{
return configManager.getConfig(BankTagsConfig.class);
}
@Override
public void resetConfiguration()
{
List<String> extraKeys = Lists.newArrayList(
CONFIG_GROUP + "." + TagManager.ITEM_KEY_PREFIX,
CONFIG_GROUP + "." + ICON_SEARCH,
CONFIG_GROUP + "." + TAG_TABS_CONFIG
);
for (String prefix : extraKeys)
{
List<String> keys = configManager.getConfigurationKeys(prefix);
for (String key : keys)
{
String[] str = key.split("\\.", 2);
if (str.length == 2)
{
configManager.unsetConfiguration(str[0], str[1]);
}
}
}
clientThread.invokeLater(() ->
{
tabInterface.destroy();
tabInterface.init();
});
}
@Override
public void startUp()
{
cleanConfig();
keyManager.registerKeyListener(this);
mouseManager.registerMouseWheelListener(this);
clientThread.invokeLater(tabInterface::init);
spriteManager.addSpriteOverrides(TabSprites.values());
}
@Override
public void shutDown()
{
keyManager.unregisterKeyListener(this);
mouseManager.unregisterMouseWheelListener(this);
clientThread.invokeLater(tabInterface::destroy);
spriteManager.removeSpriteOverrides(TabSprites.values());
shiftPressed = false;
itemQuantities.clear();
}
@Deprecated
private void cleanConfig()
{
removeInvalidTags("tagtabs");
List<String> tags = configManager.getConfigurationKeys(CONFIG_GROUP + ".item_");
tags.forEach(s ->
{
String[] split = s.split("\\.", 2);
removeInvalidTags(split[1]);
});
List<String> icons = configManager.getConfigurationKeys(CONFIG_GROUP + ".icon_");
icons.forEach(s ->
{
String[] split = s.split("\\.", 2);
String replaced = split[1].replaceAll("[<>/]", "");
if (!split[1].equals(replaced))
{
String value = configManager.getConfiguration(CONFIG_GROUP, split[1]);
configManager.unsetConfiguration(CONFIG_GROUP, split[1]);
if (replaced.length() > "icon_".length())
{
configManager.setConfiguration(CONFIG_GROUP, replaced, value);
}
}
});
}
@Deprecated
private void removeInvalidTags(final String key)
{
final String value = configManager.getConfiguration(CONFIG_GROUP, key);
if (value == null)
{
return;
}
String replaced = value.replaceAll("[<>/]", "");
if (!value.equals(replaced))
{
replaced = Text.toCSV(Text.fromCSV(replaced));
if (replaced.isEmpty())
{
configManager.unsetConfiguration(CONFIG_GROUP, key);
}
else
{
configManager.setConfiguration(CONFIG_GROUP, key, replaced);
}
}
}
private boolean isSearching()
{
return client.getVar(VarClientInt.INPUT_TYPE) == InputType.SEARCH.getType()
|| (client.getVar(VarClientInt.INPUT_TYPE) <= 0
&& client.getVar(VarClientStr.INPUT_TEXT) != null && client.getVar(VarClientStr.INPUT_TEXT).length() > 0);
}
@Subscribe
private void onScriptCallbackEvent(ScriptCallbackEvent event)
{
String eventName = event.getEventName();
int[] intStack = client.getIntStack();
String[] stringStack = client.getStringStack();
int intStackSize = client.getIntStackSize();
int stringStackSize = client.getStringStackSize();
switch (eventName)
{
case "setSearchBankInputText":
stringStack[stringStackSize - 1] = SEARCH_BANK_INPUT_TEXT;
break;
case "setSearchBankInputTextFound":
{
int matches = intStack[intStackSize - 1];
stringStack[stringStackSize - 1] = String.format(SEARCH_BANK_INPUT_TEXT_FOUND, matches);
break;
}
case "bankSearchFilter":
int itemId = intStack[intStackSize - 1];
String search = stringStack[stringStackSize - 1];
boolean tagSearch = search.startsWith(TAG_SEARCH);
if (tagSearch)
{
search = search.substring(TAG_SEARCH.length()).trim();
}
if (tagManager.findTag(itemId, search) || valueSearch(itemId, search))
{
if (!config.hidePlaceholders())
{
// return true
intStack[intStackSize - 2] = 1;
}
// not a placeholder
else if (itemManager.getItemDefinition(itemId).getPlaceholderTemplateId() == -1)
{
// return true
intStack[intStackSize - 2] = 1;
}
break;
}
else if (tagSearch)
{
intStack[intStackSize - 2] = 0;
}
break;
case "getSearchingTagTab":
intStack[intStackSize - 1] = tabInterface.isActive() ? 1 : 0;
break;
}
if (!config.removeSeparators() || !isSearching() || !tabInterface.isActive())
{
return;
}
switch (eventName)
{
case "lineSpace":
// prevent Y value being incremented to account for line separators
// fallthrough
case "tabTextSpace":
// prevent Y value being incremented to account for "Tab x" text
intStack[intStackSize - 1] = 0;
break;
case "hideLine":
case "hideTabText":
// hide the widget for the line separator
// hide the widget for the "Tab x" text
intStack[intStackSize - 1] = 1;
break;
case "rowIndex":
// remember the next index in the row so we can start the next tab's items there
nextRowIndex = intStack[intStackSize - 1];
break;
case "rowIndexInit":
// set the index to our remembered value instead of 0
intStack[intStackSize - 1] = nextRowIndex;
break;
case "bankLayoutInit":
// reset the row index if the bank is rebuilt
nextRowIndex = 0;
break;
case "newBankRow":
// if we haven't filled a row when the current tab is finished building,
// adjust the y offset to continue the next tab on the same row
if (nextRowIndex != 0)
{
intStack[intStackSize - 2] = intStack[intStackSize - 2] - 32;
}
// if we have filled the row, adjust the y offset to maintain appropriate row spacing
else
{
intStack[intStackSize - 2] = intStack[intStackSize - 2] + 4;
}
break;
case "addLastRow":
// after all tabs are finished building, add an extra row of space
// this ensures the scrollbar is set to the correct height
intStack[intStackSize - 1] = intStack[intStackSize - 1] + 32;
break;
}
}
@Subscribe
private void onMenuEntryAdded(MenuEntryAdded event)
{
if (event.getParam1() == WidgetInfo.BANK_ITEM_CONTAINER.getId()
&& event.getOption().equals("Examine"))
{
Widget container = client.getWidget(WidgetInfo.BANK_ITEM_CONTAINER);
Widget item = container.getChild(event.getParam0());
int itemID = item.getItemId();
String text = EDIT_TAGS_MENU_OPTION;
int tagCount = tagManager.getTags(itemID, false).size() + tagManager.getTags(itemID, true).size();
if (tagCount > 0)
{
text += " (" + tagCount + ")";
}
client.insertMenuItem(
text,
event.getTarget(),
MenuOpcode.RUNELITE.getId(),
event.getIdentifier(),
event.getParam0(),
event.getParam1(),
false
);
}
tabInterface.handleAdd(event);
}
@Subscribe
private void onMenuOptionClicked(MenuOptionClicked event)
{
if (event.getParam1() == WidgetInfo.BANK_ITEM_CONTAINER.getId()
&& event.getMenuOpcode() == MenuOpcode.RUNELITE
&& event.getOption().startsWith(EDIT_TAGS_MENU_OPTION))
{
event.consume();
int inventoryIndex = event.getParam0();
ItemContainer bankContainer = client.getItemContainer(InventoryID.BANK);
if (bankContainer == null)
{
return;
}
Item[] items = bankContainer.getItems();
if (inventoryIndex < 0 || inventoryIndex >= items.length)
{
return;
}
Item item = bankContainer.getItems()[inventoryIndex];
if (item == null)
{
return;
}
int itemId = item.getId();
ItemDefinition itemDefinition = itemManager.getItemDefinition(itemId);
String name = itemDefinition.getName();
// Get both tags and vartags and append * to end of vartags name
Collection<String> tags = tagManager.getTags(itemId, false);
tagManager.getTags(itemId, true).stream()
.map(i -> i + "*")
.forEach(tags::add);
boolean isSearchOpen = client.getVar(VarClientInt.INPUT_TYPE) == InputType.SEARCH.getType();
String searchText = client.getVar(VarClientStr.INPUT_TEXT);
String initialValue = Text.toCSV(tags);
chatboxPanelManager.openTextInput(name + " tags:<br>(append " + VAR_TAG_SUFFIX + " for variation tag)")
.addCharValidator(FILTERED_CHARS)
.value(initialValue)
.onDone((newValue) ->
clientThread.invoke(() ->
{
// Split inputted tags to vartags (ending with *) and regular tags
final Collection<String> newTags = new ArrayList<>(Text.fromCSV(newValue.toLowerCase()));
final Collection<String> newVarTags = new ArrayList<>(newTags).stream().filter(s -> s.endsWith(VAR_TAG_SUFFIX)).map(s ->
{
newTags.remove(s);
return s.substring(0, s.length() - VAR_TAG_SUFFIX.length());
}).collect(Collectors.toList());
// And save them
tagManager.setTagString(itemId, Text.toCSV(newTags), false);
tagManager.setTagString(itemId, Text.toCSV(newVarTags), true);
// Check both previous and current tags in case the tag got removed in new tags or in case
// the tag got added in new tags
tabInterface.updateTabIfActive(Text.fromCSV(initialValue.toLowerCase().replaceAll(Pattern.quote(VAR_TAG_SUFFIX), "")));
tabInterface.updateTabIfActive(Text.fromCSV(newValue.toLowerCase().replaceAll(Pattern.quote(VAR_TAG_SUFFIX), "")));
}))
.build();
if (isSearchOpen)
{
bankSearch.reset(false);
bankSearch.search(InputType.SEARCH, searchText, false);
}
}
else
{
tabInterface.handleClick(event);
}
}
@Subscribe
private void onItemContainerChanged(ItemContainerChanged event)
{
if (event.getContainerId() == InventoryID.BANK.getId())
{
itemQuantities.clear();
for (Item item : event.getItemContainer().getItems())
{
if (item.getId() != ItemID.BANK_FILLER)
{
itemQuantities.add(item.getId(), item.getQuantity());
}
}
}
}
@Subscribe
private void onConfigChanged(ConfigChanged configChanged)
{
if (configChanged.getGroup().equals(CONFIG_GROUP) && configChanged.getKey().equals("useTabs"))
{
if (config.tabs())
{
clientThread.invokeLater(tabInterface::init);
}
else
{
clientThread.invokeLater(tabInterface::destroy);
}
}
}
@Subscribe
private void onGameTick(GameTick event)
{
tabInterface.update();
}
@Subscribe
private void onDraggingWidgetChanged(DraggingWidgetChanged event)
{
tabInterface.handleDrag(event.isDraggingWidget(), shiftPressed);
}
@Subscribe
private void onWidgetLoaded(WidgetLoaded event)
{
if (event.getGroupId() == WidgetID.BANK_GROUP_ID)
{
tabInterface.init();
}
}
@Subscribe
private void onFocusChanged(FocusChanged event)
{
if (!event.isFocused())
{
shiftPressed = false;
}
}
@Override
public MouseWheelEvent mouseWheelMoved(MouseWheelEvent event)
{
tabInterface.handleWheel(event);
return event;
}
@Override
public void keyTyped(KeyEvent e)
{
}
@Override
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_SHIFT)
{
shiftPressed = true;
}
}
@Override
public void keyReleased(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_SHIFT)
{
shiftPressed = false;
}
}
@VisibleForTesting
boolean valueSearch(final int itemId, final String str)
{
final Matcher matcher = VALUE_SEARCH_PATTERN.matcher(str);
if (!matcher.matches())
{
return false;
}
final ItemDefinition itemComposition = itemManager.getItemDefinition(itemId);
long gePrice = (long) itemManager.getItemPrice(itemId) * (long) itemQuantities.count(itemId);
long haPrice = (long) (itemComposition.getPrice() * HIGH_ALCHEMY_MULTIPLIER) * (long) itemQuantities.count(itemId);
long value = Math.max(gePrice, haPrice);
final String mode = matcher.group("mode");
if (mode != null)
{
value = mode.toLowerCase().equals("ge") ? gePrice : haPrice;
}
final String op = matcher.group("op");
if (op != null)
{
long compare;
try
{
compare = QuantityFormatter.parseQuantity(matcher.group("num"));
}
catch (ParseException e)
{
return false;
}
switch (op)
{
case ">":
return value > compare;
case "<":
return value < compare;
case "=":
return value == compare;
case ">=":
return value >= compare;
case "<=":
return value <= compare;
}
}
final String num1 = matcher.group("num1");
final String num2 = matcher.group("num2");
if (num1 != null && num2 != null)
{
long compare1, compare2;
try
{
compare1 = QuantityFormatter.parseQuantity(num1);
compare2 = QuantityFormatter.parseQuantity(num2);
}
catch (ParseException e)
{
return false;
}
return compare1 <= value && compare2 >= value;
}
return false;
}
}
@@ -1,231 +0,0 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
* 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.banktags;
import com.google.common.base.Strings;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.api.ItemID;
import net.runelite.api.util.Text;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.game.ItemManager;
import net.runelite.client.game.ItemVariationMapping;
import static net.runelite.client.plugins.banktags.BankTagsPlugin.CONFIG_GROUP;
import net.runelite.client.plugins.cluescrolls.ClueScrollService;
import net.runelite.client.plugins.cluescrolls.clues.ClueScroll;
import net.runelite.client.plugins.cluescrolls.clues.CoordinateClue;
import net.runelite.client.plugins.cluescrolls.clues.EmoteClue;
import net.runelite.client.plugins.cluescrolls.clues.FairyRingClue;
import net.runelite.client.plugins.cluescrolls.clues.HotColdClue;
import net.runelite.client.plugins.cluescrolls.clues.MapClue;
import net.runelite.client.plugins.cluescrolls.clues.item.ItemRequirement;
@Singleton
public class TagManager
{
static final String ITEM_KEY_PREFIX = "item_";
private final ConfigManager configManager;
private final ItemManager itemManager;
private final ClueScrollService clueScrollService;
@Inject
private TagManager(
final ItemManager itemManager,
final ConfigManager configManager,
final ClueScrollService clueScrollService)
{
this.itemManager = itemManager;
this.configManager = configManager;
this.clueScrollService = clueScrollService;
}
private String getTagString(int itemId, boolean variation)
{
itemId = getItemId(itemId, variation);
String config = configManager.getConfiguration(CONFIG_GROUP, ITEM_KEY_PREFIX + itemId);
if (config == null)
{
return "";
}
return config;
}
Collection<String> getTags(int itemId, boolean variation)
{
return new LinkedHashSet<>(Text.fromCSV(getTagString(itemId, variation).toLowerCase()));
}
void setTagString(int itemId, String tags, boolean variation)
{
itemId = getItemId(itemId, variation);
if (Strings.isNullOrEmpty(tags))
{
configManager.unsetConfiguration(CONFIG_GROUP, ITEM_KEY_PREFIX + itemId);
}
else
{
configManager.setConfiguration(CONFIG_GROUP, ITEM_KEY_PREFIX + itemId, tags);
}
}
public void addTags(int itemId, final Collection<String> t, boolean variation)
{
final Collection<String> tags = getTags(itemId, variation);
if (tags.addAll(t))
{
setTags(itemId, tags, variation);
}
}
public void addTag(int itemId, String tag, boolean variation)
{
final Collection<String> tags = getTags(itemId, variation);
if (tags.add(Text.standardize(tag)))
{
setTags(itemId, tags, variation);
}
}
private void setTags(int itemId, Collection<String> tags, boolean variation)
{
setTagString(itemId, Text.toCSV(tags), variation);
}
boolean findTag(int itemId, String search)
{
if (search.equals("clue") && testClue(itemId))
{
return true;
}
Collection<String> tags = getTags(itemId, false);
tags.addAll(getTags(itemId, true));
return tags.stream().anyMatch(tag -> tag.startsWith(Text.standardize(search)));
}
public List<Integer> getItemsForTag(String tag)
{
final String prefix = CONFIG_GROUP + "." + ITEM_KEY_PREFIX;
return configManager.getConfigurationKeys(prefix).stream()
.map(item -> Integer.parseInt(item.replace(prefix, "")))
.filter(item -> getTags(item, false).contains(tag) || getTags(item, true).contains(tag))
.collect(Collectors.toList());
}
public void removeTag(String tag)
{
final String prefix = CONFIG_GROUP + "." + ITEM_KEY_PREFIX;
configManager.getConfigurationKeys(prefix).forEach(item ->
{
int id = Integer.parseInt(item.replace(prefix, ""));
removeTag(id, tag);
});
}
public void removeTag(int itemId, String tag)
{
Collection<String> tags = getTags(itemId, false);
if (tags.remove(Text.standardize(tag)))
{
setTags(itemId, tags, false);
}
tags = getTags(itemId, true);
if (tags.remove(Text.standardize(tag)))
{
setTags(itemId, tags, true);
}
}
public void renameTag(String oldTag, String newTag)
{
List<Integer> items = getItemsForTag(Text.standardize(oldTag));
items.forEach(id ->
{
Collection<String> tags = getTags(id, id < 0);
tags.remove(Text.standardize(oldTag));
tags.add(Text.standardize(newTag));
setTags(id, tags, id < 0);
});
}
private int getItemId(int itemId, boolean variation)
{
itemId = Math.abs(itemId);
itemId = itemManager.canonicalize(itemId);
if (variation)
{
itemId = ItemVariationMapping.map(itemId) * -1;
}
return itemId;
}
private boolean testClue(int itemId)
{
ClueScroll c = clueScrollService.getClue();
if (c == null)
{
return false;
}
if (c instanceof EmoteClue)
{
EmoteClue emote = (EmoteClue) c;
for (ItemRequirement ir : emote.getItemRequirements())
{
if (ir.fulfilledBy(itemId))
{
return true;
}
}
}
else if (c instanceof CoordinateClue || c instanceof HotColdClue || c instanceof FairyRingClue)
{
return itemId == ItemID.SPADE;
}
else if (c instanceof MapClue)
{
MapClue mapClue = (MapClue) c;
return mapClue.getObjectId() == -1 && itemId == ItemID.SPADE;
}
return false;
}
}
@@ -1,94 +0,0 @@
/*
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
* 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.banktags.tabs;
import com.google.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.ScriptID;
import net.runelite.api.VarClientInt;
import net.runelite.api.VarClientStr;
import net.runelite.api.vars.InputType;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.callback.ClientThread;
public class BankSearch
{
private final Client client;
private final ClientThread clientThread;
@Inject
private BankSearch(
final Client client,
final ClientThread clientThread
)
{
this.client = client;
this.clientThread = clientThread;
}
public void search(InputType inputType, String search, boolean closeInput)
{
clientThread.invoke(() ->
{
// This ensures that any chatbox input (e.g from search) will not remain visible when
// selecting/changing tab
if (closeInput)
{
client.runScript(ScriptID.MESSAGE_LAYER_CLOSE, 0, 0);
}
client.setVar(VarClientInt.INPUT_TYPE, inputType.getType());
client.setVar(VarClientStr.INPUT_TEXT, search);
layoutBank();
});
}
public void layoutBank()
{
Widget bankContainer = client.getWidget(WidgetInfo.BANK_ITEM_CONTAINER);
if (bankContainer == null || bankContainer.isHidden())
{
return;
}
Object[] scriptArgs = bankContainer.getOnInvTransmit();
if (scriptArgs == null)
{
return;
}
client.runScript(scriptArgs);
}
public void reset(boolean closeChat)
{
search(InputType.NONE, "", closeChat);
}
}
@@ -1,44 +0,0 @@
/*
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
* 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.banktags.tabs;
class MenuIndexes
{
static class NewTab
{
static final int NEW_TAB = 2;
static final int IMPORT_TAB = 3;
}
static class Tab
{
static final int OPEN_TAG = 2;
static final int CHANGE_ICON = 3;
static final int DELETE_TAB = 4;
static final int EXPORT_TAB = 5;
static final int RENAME_TAB = 6;
}
}
@@ -1,157 +0,0 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
* 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.banktags.tabs;
import com.google.common.base.MoreObjects;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.api.ItemID;
import net.runelite.api.util.Text;
import net.runelite.client.config.ConfigManager;
import static net.runelite.client.plugins.banktags.BankTagsPlugin.CONFIG_GROUP;
import static net.runelite.client.plugins.banktags.BankTagsPlugin.ICON_SEARCH;
import static net.runelite.client.plugins.banktags.BankTagsPlugin.TAG_TABS_CONFIG;
import org.apache.commons.lang3.math.NumberUtils;
@Singleton
class TabManager
{
@Getter(AccessLevel.PACKAGE)
private final List<TagTab> tabs = new ArrayList<>();
private final ConfigManager configManager;
@Inject
private TabManager(ConfigManager configManager)
{
this.configManager = configManager;
}
void add(TagTab tagTab)
{
if (!contains(tagTab.getTag()))
{
tabs.add(tagTab);
}
}
void clear()
{
tabs.forEach(t -> t.setHidden(true));
tabs.clear();
}
TagTab find(String tag)
{
Optional<TagTab> first = tabs.stream().filter(t -> t.getTag().equals(Text.standardize(tag))).findAny();
return first.orElse(null);
}
List<String> getAllTabs()
{
return Text.fromCSV(MoreObjects.firstNonNull(configManager.getConfiguration(CONFIG_GROUP, TAG_TABS_CONFIG), ""));
}
TagTab load(String tag)
{
TagTab tagTab = find(tag);
if (tagTab == null)
{
tag = Text.standardize(tag);
String item = configManager.getConfiguration(CONFIG_GROUP, ICON_SEARCH + tag);
int itemid = NumberUtils.toInt(item, ItemID.SPADE);
tagTab = new TagTab(itemid, tag);
}
return tagTab;
}
void move(String tagToMove, String tagDestination)
{
tagToMove = Text.standardize(tagToMove);
tagDestination = Text.standardize(tagDestination);
if (contains(tagToMove) && contains(tagDestination))
{
Collections.swap(tabs, indexOf(tagToMove), indexOf(tagDestination));
}
}
void remove(String tag)
{
TagTab tagTab = find(tag);
if (tagTab != null)
{
tagTab.setHidden(true);
tabs.remove(tagTab);
removeIcon(tag);
}
}
void save()
{
String tags = Text.toCSV(tabs.stream().map(TagTab::getTag).collect(Collectors.toList()));
configManager.setConfiguration(CONFIG_GROUP, TAG_TABS_CONFIG, tags);
}
void removeIcon(final String tag)
{
configManager.unsetConfiguration(CONFIG_GROUP, ICON_SEARCH + Text.standardize(tag));
}
void setIcon(final String tag, final String icon)
{
configManager.setConfiguration(CONFIG_GROUP, ICON_SEARCH + Text.standardize(tag), icon);
}
int size()
{
return tabs.size();
}
private boolean contains(String tag)
{
return tabs.stream().anyMatch(t -> t.getTag().equals(tag));
}
private int indexOf(TagTab tagTab)
{
return tabs.indexOf(tagTab);
}
private int indexOf(String tag)
{
return indexOf(find(tag));
}
}
@@ -1,48 +0,0 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
* 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.banktags.tabs;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.runelite.client.game.SpriteOverride;
@RequiredArgsConstructor
public enum TabSprites implements SpriteOverride
{
INCINERATOR(-200, "incinerator.png"),
TAB_BACKGROUND(-201, "tag-tab.png"),
TAB_BACKGROUND_ACTIVE(-202, "tag-tab-active.png"),
UP_ARROW(-203, "up-arrow.png"),
DOWN_ARROW(-204, "down-arrow.png"),
NEW_TAB(-205, "new-tab.png");
@Getter(AccessLevel.PUBLIC)
private final int spriteId;
@Getter(AccessLevel.PUBLIC)
private final String fileName;
}
@@ -1,59 +0,0 @@
/*
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
* 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.banktags.tabs;
import lombok.Data;
import lombok.EqualsAndHashCode;
import net.runelite.api.widgets.Widget;
@Data
@EqualsAndHashCode(of = "tag")
class TagTab
{
private String tag;
private int iconItemId;
private Widget background;
private Widget icon;
TagTab(int iconItemId, String tag)
{
this.iconItemId = iconItemId;
this.tag = tag;
}
void setHidden(boolean hide)
{
if (background != null)
{
background.setHidden(hide);
}
if (icon != null)
{
icon.setHidden(hide);
}
}
}
@@ -1,113 +0,0 @@
package net.runelite.client.plugins.banlist;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
import net.runelite.client.config.ConfigTitleSection;
import net.runelite.client.config.Title;
@ConfigGroup("banlist")
public interface BanListConfig extends Config
{
@ConfigTitleSection(
keyName = "listsTitle",
name = "Lists",
description = "",
position = 0
)
default Title listsTitle()
{
return new Title();
}
@ConfigItem(
keyName = "bannedPlayers",
name = "Manual Scammer List",
description = "Manually add players seperated by commas that you wish to be warned about while in a clan/cox/tob party",
position = 1,
titleSection = "listsTitle"
)
default String getBannedPlayers()
{
return "";
}
@ConfigItem(
keyName = "bannedPlayers",
name = "",
description = ""
)
void setBannedPlayers(String key);
@ConfigItem(
position = 2,
keyName = "enableWDRScam",
name = "Enable WDR Scammer List",
description = "Incorporate WDR Scammer list",
titleSection = "listsTitle"
)
default boolean enableWDRScam()
{
return true;
}
@ConfigItem(
position = 3,
keyName = "enableWDRToxic",
name = "Enable WDR Toxic List",
description = "Incorporate WDR Toxic list",
titleSection = "listsTitle"
)
default boolean enableWDRToxic()
{
return true;
}
@ConfigItem(
position = 4,
keyName = "enableRuneWatch",
name = "Enable RuneWatch List",
description = "Incorporate RuneWatch potential scammer list",
titleSection = "listsTitle"
)
default boolean enableRuneWatch()
{
return true;
}
@ConfigTitleSection(
keyName = "highlightTitle",
name = "Highlight",
description = "",
position = 5
)
default Title highlightTitle()
{
return new Title();
}
@ConfigItem(
position = 6,
keyName = "highlightInClan",
name = "Highlight red in Clan Chat",
description = "Highlights Scammer\'s name in your current clan chat.",
titleSection = "highlightTitle"
)
default boolean highlightInClan()
{
return true;
}
@ConfigItem(
position = 7,
keyName = "highlightInTrade",
name = "Highlight red in trade screen",
description = "Highlights Scammer\'s name in your trade window",
titleSection = "highlightTitle"
)
default boolean highlightInTrade()
{
return true;
}
}
@@ -1,455 +0,0 @@
/*
* Copyright (c) 2019, xperiaclash <https://github.com/xperiaclash>
* Copyright (c) 2019, ganom <https://github.com/Ganom>
* Copyright (c) 2019, gazivodag <https://github.com/gazivodag>
* 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.banlist;
import com.google.inject.Provides;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.ChatMessageType;
import net.runelite.api.ClanMember;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.events.ClanMemberJoined;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.WidgetHiddenChanged;
import net.runelite.api.events.WidgetLoaded;
import net.runelite.api.util.Text;
import net.runelite.api.widgets.Widget;
import static net.runelite.api.widgets.WidgetID.PLAYER_TRADE_SCREEN_GROUP_ID;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.chat.ChatColorType;
import net.runelite.client.chat.ChatMessageBuilder;
import net.runelite.client.chat.ChatMessageManager;
import net.runelite.client.chat.QueuedMessage;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.PluginType;
import net.runelite.http.api.RuneLiteAPI;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Request;
import okhttp3.Response;
import org.jetbrains.annotations.NotNull;
@PluginDescriptor(
name = "Ban List",
description = "Displays warning in chat when you join a" +
"clan chat/new member join your clan chat and he is in a WDR/RuneWatch/Manual List",
tags = {"PVM", "WDR", "RuneWatch"},
type = PluginType.MISCELLANEOUS,
enabledByDefault = false
)
@Singleton
@Slf4j
public class BanListPlugin extends Plugin
{
private final Set<String> wdrScamSet = new HashSet<>();
private final Set<String> wdrToxicSet = new HashSet<>();
private final Set<String> runeWatchSet = new HashSet<>();
private final Set<String> manualBans = new HashSet<>();
@Inject
private Client client;
@Inject
private ClientThread clientThread;
@Inject
private BanListConfig config;
@Inject
private ChatMessageManager chatMessageManager;
private String tobNames = "";
private boolean enableWDRScam;
private boolean enableWDRToxic;
private boolean enableRuneWatch;
private boolean highlightInClan;
private boolean highlightInTrade;
@Provides
BanListConfig getConfig(ConfigManager configManager)
{
return configManager.getConfig(BanListConfig.class);
}
@Override
protected void startUp()
{
updateConfig();
manualBans.addAll(Text.fromCSV(Text.standardize(config.getBannedPlayers())));
fetchFromWebsites();
}
@Override
protected void shutDown()
{
wdrScamSet.clear();
wdrToxicSet.clear();
runeWatchSet.clear();
manualBans.clear();
}
@Subscribe
private void onConfigChanged(ConfigChanged event)
{
if (event.getGroup().equals("banlist") && event.getKey().equals("bannedPlayers"))
{
manualBans.clear();
String newValue = event.getNewValue();
manualBans.addAll(Text.fromCSV(Text.standardize(newValue)));
}
}
private void updateConfig()
{
this.enableWDRScam = config.enableWDRScam();
this.enableWDRToxic = config.enableWDRToxic();
this.enableRuneWatch = config.enableRuneWatch();
this.highlightInClan = config.highlightInClan();
this.highlightInTrade = config.highlightInTrade();
}
/**
* Event to keep making sure player names are highlighted red in clan chat, since the red name goes away frequently
*/
@Subscribe
private void onWidgetHiddenChanged(WidgetHiddenChanged widgetHiddenChanged)
{
if (client.getGameState() != GameState.LOGGED_IN
|| client.getWidget(WidgetInfo.LOGIN_CLICK_TO_PLAY_SCREEN) != null
|| client.getViewportWidget() == null
|| client.getWidget(WidgetInfo.CLAN_CHAT) == null
|| !this.highlightInClan)
{
return;
}
clientThread.invokeLater(() ->
{
if (!client.getWidget(WidgetInfo.CLAN_CHAT).isHidden())
{
highlightRedInCC();
}
});
}
@Subscribe
private void onClanMemberJoined(ClanMemberJoined event)
{
ClanMember member = event.getMember();
String memberUsername = Text.standardize(member.getUsername().toLowerCase());
ListType scamList = checkScamList(memberUsername);
ListType toxicList = checkToxicList(memberUsername);
if (scamList != null)
{
sendWarning(memberUsername, scamList);
if (this.highlightInClan)
{
highlightRedInCC();
}
}
if (toxicList != null)
{
sendWarning(memberUsername, toxicList);
if (this.highlightInClan)
{
highlightRedInCC();
}
}
}
/**
* If a trade window is opened and the person trading us is on the list, modify "trading with"
*/
@Subscribe
private void onWidgetLoaded(WidgetLoaded widgetLoaded)
{
if (this.highlightInTrade && widgetLoaded.getGroupId() == PLAYER_TRADE_SCREEN_GROUP_ID)
{ //if trading window was loaded
clientThread.invokeLater(() ->
{
Widget tradingWith = client.getWidget(WidgetInfo.TRADING_WITH);
String name = tradingWith.getText().replaceAll("Trading With: ", "").toLowerCase();
if (checkScamList(name) != null)
{
tradingWith.setText(tradingWith.getText().replaceAll(name, "<col=ff0000>" + name + " (Scammer)" + "</col>"));
}
if (checkToxicList(name) != null)
{
tradingWith.setText(tradingWith.getText().replaceAll(name, "<col=ff6400>" + name + " (Toxic)" + "</col>"));
}
});
}
}
@Subscribe
private void onGameTick(GameTick event)
{
final Widget raidingParty = client.getWidget(WidgetInfo.THEATRE_OF_BLOOD_RAIDING_PARTY);
if (raidingParty == null)
{
return;
}
String allNames = raidingParty.getText();
if (allNames.equalsIgnoreCase(tobNames))
{
return;
}
tobNames = allNames;
String[] split = allNames.split("<br>");
for (String name : split)
{
if (!name.equals("-"))
{
String stdName = Text.standardize(name);
ListType scamList = checkScamList(stdName);
if (scamList != null)
{
sendWarning(name, scamList);
}
ListType toxicList = checkToxicList(stdName);
if (toxicList != null)
{
sendWarning(name, toxicList);
}
}
}
}
/**
* Compares player name to everything in the ban lists
*/
private ListType checkScamList(String nameToBeChecked)
{
if (this.enableWDRScam && wdrScamSet.contains(nameToBeChecked))
{
return ListType.WEDORAIDSSCAM_LIST;
}
if (this.enableRuneWatch && runeWatchSet.contains(nameToBeChecked))
{
return ListType.RUNEWATCH_LIST;
}
if (manualBans.contains(nameToBeChecked))
{
return ListType.MANUAL_LIST;
}
return null;
}
private ListType checkToxicList(String nameToBeChecked)
{
if (this.enableWDRToxic && wdrToxicSet.contains(nameToBeChecked))
{
return ListType.WEDORAIDSTOXIC_LIST;
}
return null;
}
/**
* Sends a warning to our player, notifying them that a player is on a ban list
*/
private void sendWarning(String playerName, ListType listType)
{
switch (listType)
{
case WEDORAIDSSCAM_LIST:
final String wdr__scam_message = new ChatMessageBuilder()
.append(ChatColorType.HIGHLIGHT)
.append("Warning! " + playerName + " is on WeDoRaids\' scammer list!")
.build();
chatMessageManager.queue(
QueuedMessage.builder()
.type(ChatMessageType.CONSOLE)
.runeLiteFormattedMessage(wdr__scam_message)
.build());
break;
case WEDORAIDSTOXIC_LIST:
final String wdr__toxic_message = new ChatMessageBuilder()
.append(ChatColorType.HIGHLIGHT)
.append("Warning! " + playerName + " is on WeDoRaids\' toxic list!")
.build();
chatMessageManager.queue(
QueuedMessage.builder()
.type(ChatMessageType.CONSOLE)
.runeLiteFormattedMessage(wdr__toxic_message)
.build());
break;
case RUNEWATCH_LIST:
final String rw_message = new ChatMessageBuilder()
.append(ChatColorType.HIGHLIGHT)
.append("Warning! " + playerName + " is on the Runewatch\'s potential scammer list!")
.build();
chatMessageManager.queue(
QueuedMessage.builder()
.type(ChatMessageType.CONSOLE)
.runeLiteFormattedMessage(rw_message)
.build());
break;
case MANUAL_LIST:
final String manual_message = new ChatMessageBuilder()
.append(ChatColorType.HIGHLIGHT)
.append("Warning! " + playerName + " is on your manual scammer list!")
.build();
chatMessageManager.queue(
QueuedMessage.builder()
.type(ChatMessageType.CONSOLE)
.runeLiteFormattedMessage(manual_message)
.build());
break;
}
}
/**
* Pulls data from wdr and runewatch to build a list of blacklisted usernames
*/
private void fetchFromWebsites()
{
Request request = new Request.Builder()
.url("https://wdrdev.github.io/index")
.build();
fetchAndParseWdr(request, wdrScamSet);
Request secondRequest = new Request.Builder()
.url("https://runewatch.com/incident-index-page/")
.build();
RuneLiteAPI.CLIENT.newCall(secondRequest).enqueue(new Callback()
{
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e)
{
log.debug("error retrieving names from runewatch");
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException
{
String text = response.body().string();
String mytext = text.substring(text.indexOf("lcp_instance_0"), text.indexOf("strong>Evidence Quality Suggestion"));
String[] split = mytext.split("href=");
for (String x : split)
{
if (x.contains("title"))
{
x = x.substring(x.indexOf("title"), x.indexOf('>'));
x = x.substring(x.indexOf('=') + 2, x.length() - 1);
runeWatchSet.add(Text.standardize(x).toLowerCase());
}
}
}
});
Request thirdRequest = new Request.Builder()
.url("https://wdrdev.github.io/toxic")
.build();
fetchAndParseWdr(thirdRequest, wdrToxicSet);
}
private void fetchAndParseWdr(Request req, Set<String> tgtSet)
{
RuneLiteAPI.CLIENT.newCall(req).enqueue(new Callback()
{
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e)
{
log.debug("error retrieving names from wdr");
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException
{
String text = response.body().string();
text = text.substring(text.indexOf("<p>") + 3, text.indexOf("</p>"));
text = text.replace("/", ",");
text = text.replace(", $", "");
Text.fromCSV(text).forEach(str -> tgtSet.add(Text.standardize(str)));
}
});
}
/**
* Iterates through the clan chat list widget and checks if a string (name) is on any of the ban lists to highlight them red.
*/
private void highlightRedInCC()
{
clientThread.invokeLater(() ->
{
Widget widget = client.getWidget(WidgetInfo.CLAN_CHAT_LIST);
for (Widget widgetChild : widget.getDynamicChildren())
{
String text = widgetChild.getText(), lc = text.toLowerCase();
if (checkScamList(lc) != null)
{
widgetChild.setText("<col=ff0000>" + text + "</col>");
}
else if (checkToxicList(lc) != null)
{
widgetChild.setText("<col=ff6400>" + text + "</col>");
}
}
});
}
}
@@ -1,9 +0,0 @@
package net.runelite.client.plugins.banlist;
public enum ListType
{
WEDORAIDSSCAM_LIST,
WEDORAIDSTOXIC_LIST,
RUNEWATCH_LIST,
MANUAL_LIST
}
@@ -1,217 +0,0 @@
/*
* Copyright (c) 2018, Jacob M <https://github.com/jacoblairm>
* Copyright (c) 2019, 7ate9 <https://github.com/se7enAte9>
* Copyright (c) 2019, https://openosrs.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.barbarianassault;
import com.google.common.collect.ImmutableMap;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.api.Client;
import net.runelite.api.Perspective;
import net.runelite.api.Point;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo;
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;
@Singleton
class AboveSceneOverlay extends Overlay
{
private static final int HEALTH_BAR_HEIGHT = 20;
private static final int HEALTH_BAR_WIDTH = 115;
private static final int CENTER_OFFSET = Perspective.LOCAL_HALF_TILE_SIZE / 8;
private static final int EGG_DIAMETER = Perspective.LOCAL_HALF_TILE_SIZE / 4;
private static final Color HEALTH_BAR_COLOR = new Color(225, 35, 0, 125);
private static final ImmutableMap<WidgetInfo, Point> TEAMMATES = ImmutableMap.of(
WidgetInfo.BA_HEAL_TEAMMATE1, new Point(28, 2),
WidgetInfo.BA_HEAL_TEAMMATE2, new Point(26, 2),
WidgetInfo.BA_HEAL_TEAMMATE3, new Point(26, 2),
WidgetInfo.BA_HEAL_TEAMMATE4, new Point(25, 2));
private final Client client;
private final BarbarianAssaultPlugin game;
@Inject
private AboveSceneOverlay(final Client client, final BarbarianAssaultPlugin game)
{
super(game);
setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.ABOVE_SCENE);
this.client = client;
this.game = game;
}
@Override
public Dimension render(Graphics2D graphics)
{
if (!game.isInGame() || game.getRole() == null || game.isUsingGloryHorn())
{
return null;
}
switch (game.getRole())
{
case HEALER:
if (game.isShowTeammateHealthbars())
{
renderHealthBars(graphics);
}
if (game.isHealerCodes())
{
renderHealerCodes(graphics);
}
break;
case COLLECTOR:
if (game.isHighlightCollectorEggs())
{
renderEggs(graphics);
}
break;
}
return null;
}
//TODO add poison color change or low health color change
private void renderHealthBars(Graphics2D graphics)
{
for (Map.Entry<WidgetInfo, Point> teammate : TEAMMATES.entrySet())
{
Widget widget = client.getWidget(teammate.getKey());
if (widget == null)
{
continue;
}
// This will give us two elements, the first will be current health, and the second will be max health
String[] teammateHealth = widget.getText().split(" / ");
graphics.setColor(HEALTH_BAR_COLOR);
graphics.fillRect((widget.getCanvasLocation().getX() - teammate.getValue().getX()),
(widget.getCanvasLocation().getY() - teammate.getValue().getY()),
getBarWidth(Integer.parseInt(teammateHealth[1]), Integer.parseInt(teammateHealth[0])),
HEALTH_BAR_HEIGHT);
}
}
private int getBarWidth(int base, int current)
{
final double ratio = (double) current / base;
if (ratio >= 1)
{
return HEALTH_BAR_WIDTH;
}
return (int) Math.round(ratio * HEALTH_BAR_WIDTH);
}
private void renderHealerCodes(Graphics2D graphics)
{
for (Healer healer : game.getHealers().values())
{
Color color = Color.GREEN;
int timeLeft = 0;
if (game.getWave() != null)
{
timeLeft = healer.getLastFoodTime() - (int) game.getWave().getWaveTimer().getElapsedTime();
}
timeLeft = timeLeft < 1 ? 0 : timeLeft;
if (timeLeft > 0)
{
color = Color.RED;
}
String text = String.format("%d %d", healer.getFoodRemaining(), timeLeft);
OverlayUtil.renderActorOverlay(graphics, healer.getNpc(), text, color);
}
}
private void renderEggs(Graphics2D graphics)
{
final Color color = graphics.getColor();
final Stroke originalStroke = graphics.getStroke();
String listen = game.getLastListenText();
if (listen != null && !listen.equals("- - -"))
{
graphics.setStroke(new BasicStroke(2));
//TODO Render quantity text as well
//TODO add config options for overlay colors
switch (listen)
{
case "Red eggs":
graphics.setColor(new Color(Color.RED.getRed(), Color.RED.getGreen(), Color.RED.getBlue(), 150));
game.getRedEggs().forEach((point, quantity) -> drawCircle(graphics, LocalPoint.fromWorld(client, point)));
break;
case "Green eggs":
graphics.setColor(new Color(Color.GREEN.getRed(), Color.GREEN.getGreen(), Color.GREEN.getBlue(), 150));
game.getGreenEggs().forEach((point, quantity) -> drawCircle(graphics, LocalPoint.fromWorld(client, point)));
break;
case "Blue eggs":
graphics.setColor(new Color(Color.BLUE.getRed(), Color.BLUE.getGreen(), Color.BLUE.getBlue(), 150));
game.getBlueEggs().forEach((point, quantity) -> drawCircle(graphics, LocalPoint.fromWorld(client, point)));
break;
}
}
graphics.setColor(new Color(Color.YELLOW.getRed(), Color.YELLOW.getGreen(), Color.YELLOW.getBlue(), 150));
game.getYellowEggs().forEach((point, quantity) -> drawCircle(graphics, LocalPoint.fromWorld(client, point)));
graphics.setColor(color);
graphics.setStroke(originalStroke);
}
private void drawCircle(Graphics2D graphics, LocalPoint point)
{
if (point == null)
{
return;
}
Point canvasPoint = Perspective.localToCanvas(client, point, 0);
if (canvasPoint == null)
{
return;
}
//TODO rendering a model would be better / more accurate
graphics.fillOval(canvasPoint.getX() - CENTER_OFFSET, canvasPoint.getY() - CENTER_OFFSET, EGG_DIAMETER, EGG_DIAMETER);
}
}
@@ -1,179 +0,0 @@
/*
* Copyright (c) 2019, 7ate9 <https://github.com/se7enAte9>
* Copyright (c) 2019, https://openosrs.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.barbarianassault;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.api.Client;
import net.runelite.api.Point;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.api.widgets.WidgetItem;
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.ImageUtil;
@Singleton
class AboveWidgetsOverlay extends Overlay
{
private static final int OFFSET_X_TEXT_QUANTITY = 0;
private static final int OFFSET_Y_TEXT_QUANTITY = 10;
private final Client client;
private final BarbarianAssaultPlugin game;
@Inject
private AboveWidgetsOverlay(final Client client, final BarbarianAssaultPlugin game)
{
super(game);
setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.ABOVE_WIDGETS);
this.client = client;
this.game = game;
}
@Override
public Dimension render(Graphics2D graphics)
{
if (!game.isInGame() || game.getRole() == null || game.isUsingGloryHorn())
{
return null;
}
Role role = game.getRole();
if (game.isShowTimer())
{
renderTimer(graphics, role);
}
switch (role)
{
case ATTACKER:
if (game.isHighlightArrows())
{
renderInventoryHighlights(graphics, game.getRole().getListenItem(game.getLastListenText()), game.getHighlightArrowColor());
}
break;
case DEFENDER:
if (game.isHighlightBait())
{
renderInventoryHighlights(graphics, game.getRole().getListenItem(game.getLastListenText()), game.getHighlightBaitColor());
}
break;
case HEALER:
if (game.isHighlightPoison())
{
renderInventoryHighlights(graphics, game.getRole().getListenItem(game.getLastListenText()), game.getHighlightPoisonColor());
}
}
return null;
}
private void renderTimer(Graphics2D graphics, Role role)
{
Widget roleText = client.getWidget(role.getRoleText());
Widget roleSprite = client.getWidget(role.getRoleSprite());
if (roleText == null || roleSprite == null)
{
return;
}
if (role == Role.COLLECTOR && game.isShowEggCountOverlay() && game.getWave() != null)
{
roleText.setText("(" + game.getWave().getCollectedEggCount() + ") " + formatClock());
}
else if (role == Role.HEALER && game.isShowHpCountOverlay() && game.getWave() != null)
{
roleText.setText("(" + game.getWave().getHpHealed() + ") " + formatClock());
}
else
{
roleText.setText(formatClock());
}
Rectangle spriteBounds = roleSprite.getBounds();
graphics.drawImage(game.getClockImage(), spriteBounds.x, spriteBounds.y, null);
roleSprite.setHidden(true);
}
private void renderInventoryHighlights(Graphics2D graphics, int itemID, Color color)
{
Widget inventory = client.getWidget(WidgetInfo.INVENTORY);
if (inventory == null || inventory.isHidden() || itemID == -1)
{
return;
}
Color highlight = new Color(color.getRed(), color.getGreen(), color.getBlue(), 150);
BufferedImage image = ImageUtil.fillImage(client.createItemSprite(itemID, 300, 2, 0, 0, true, 710).toBufferedImage(), highlight);
for (WidgetItem item : inventory.getWidgetItems())
{
if (item.getId() == itemID)
{
OverlayUtil.renderImageLocation(graphics, item.getCanvasLocation(), image);
//The item's text quantity is rendered after the sprite's image is rendered so that the text appears on top
if (item.getQuantity() > 1)
{
OverlayUtil.renderTextLocation(graphics,
new Point(item.getCanvasLocation().getX() + OFFSET_X_TEXT_QUANTITY, item.getCanvasLocation().getY() + OFFSET_Y_TEXT_QUANTITY),
String.valueOf(item.getQuantity()), Color.YELLOW);
}
}
}
}
private String formatClock()
{
if (game.getCallTimer() == null)
{
return "- - -";
}
else
{
long timeLeft = game.getTimeToChange();
if (timeLeft < 0)
{
return "00:00";
}
else
{
return String.format("00:%02d", timeLeft);
}
}
}
}
@@ -1,516 +0,0 @@
/*
* Copyright (c) 2019, 7ate9 <https://github.com/se7enAte9>
* Copyright (c) 2019, https://openosrs.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.barbarianassault;
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.ConfigSection;
import net.runelite.client.config.Range;
@ConfigGroup("barbarianAssault")
public interface BarbarianAssaultConfig extends Config
{
@ConfigItem(
keyName = "swapLadder",
name = "Swap quick-start",
description = "Enables swapping of 'Climb-down' and 'Quick-start' in the wave lobby",
position = 0
)
default boolean swapLadder()
{
return true;
}
@ConfigItem(
keyName = "showTimer",
name = "Show call change timer",
description = "Shows time to next call change",
position = 1
)
default boolean showTimer()
{
return true;
}
@ConfigItem(
keyName = "removeIncorrectCalls",
name = "Remove incorrect calls",
description = "Removes incorrect 'Tell' menu options from horn",
position = 2
)
default boolean removeIncorrectCalls()
{
return true;
}
@ConfigItem(
keyName = "removeUnusedMenus",
name = "Remove unused menus",
description = "Removes unnecessary menu options" +
"<br>Example: Attack options are removed when not attacker",
position = 3
)
default boolean removeUnusedMenus()
{
return true;
}
@ConfigItem(
keyName = "prayerMetronome",
name = "Enable prayer metronome",
description = "Turns on a metronome sync'd to the game's tick rate when any prayer is active",
position = 4
)
default boolean prayerMetronome()
{
return false;
}
@Range(
min = 1,
max = 50
)
@ConfigItem(
keyName = "prayerMetronomeVolume",
name = "Metronome volume",
description = "Adjusts the metronome's volume",
position = 5,
hidden = true,
unhide = "prayerMetronome"
)
default int prayerMetronomeVolume()
{
return 10;
}
@ConfigItem(
keyName = "showDeathTimes",
name = "Show death times",
description = "Shows the time all penance monsters of a certain type are killed in the chat box, an info box, or both",
position = 6
)
default boolean showDeathTimes()
{
return true;
}
@ConfigItem(
keyName = "showDeathTimesMode",
name = "Mode",
description = "",
position = 7,
hidden = true,
unhide = "showDeathTimes"
)
default DeathTimesMode showDeathTimesMode()
{
return DeathTimesMode.BOTH;
}
@ConfigItem(
keyName = "waveTimes",
name = "Show wave and game duration",
description = "Displays wave duration after each wave and total game time after wave 10",
position = 8
)
default boolean waveTimes()
{
return true;
}
@ConfigItem(
keyName = "showTotalRewards",
name = "Summarize total reward points",
description = "Gives summary of advanced points breakdown in chat log",
position = 9
)
default boolean showTotalRewards()
{
return true;
}
/*///************///*/
/*/// Attacker ///*/
/*///************///*/
@ConfigSection(
name = "Attacker",
description = "",
position = 10,
keyName = "attackerSection"
)
default boolean attackerSection()
{
return false;
}
@ConfigItem(
keyName = "highlightArrows",
name = "Highlight called arrows",
description = "Highlights arrows called by your teammate",
position = 0,
section = "attackerSection"
)
default boolean highlightArrows()
{
return true;
}
@ConfigItem(
keyName = "highlightArrowColor",
name = "Arrow color",
description = "Configures the color to highlight the called arrows",
position = 1,
section = "attackerSection",
hidden = true,
unhide = "highlightArrows"
)
default Color highlightArrowColor()
{
return Color.GREEN;
}
@ConfigItem(
keyName = "removeIncorrectAttackStyles",
name = "Remove incorrect attack styles",
description = "Hides wrong attack styles for dragon claws and crystal halberd",
position = 2,
section = "attackerSection"
)
default boolean removeIncorrectAttackStyles()
{
return false;
}
@ConfigItem(
keyName = "tagging",
name = "Enable tagging",
description = "Highlights the menu entry of an attacker/ranger that has not been tagged.",
position = 3,
section = "attackerSection"
)
default boolean tagging()
{
return false;
}
/*///************///*/
/*/// Defender ///*/
/*///************///*/
@ConfigSection(
name = "Defender",
description = "",
position = 11,
keyName = "defenderSection"
)
default boolean defenderSection()
{
return false;
}
@ConfigItem(
keyName = "highlightBait",
name = "Highlight called bait",
description = "Highlights bait called by your teammate",
position = 0,
section = "defenderSection"
)
default boolean highlightBait()
{
return true;
}
@ConfigItem(
keyName = "highlightBaitColor",
name = "Bait color",
description = "Configures the color to highlight the called bait",
position = 1,
section = "defenderSection",
hidden = true,
unhide = "highlightBait"
)
default Color highlightBaitColor()
{
return Color.GREEN;
}
@ConfigItem(
keyName = "showDefTimer",
name = "Show defender tick timer",
description = "Shows the current cycle tick of runners",
position = 2,
section = "defenderSection"
)
default boolean showDefTimer()
{
return true;
}
@ConfigItem(
keyName = "deprioritizeBait",
name = "Deprioritize bait",
description = "Moves 'Take' menu option for all bait below 'Walk Here'",
position = 3,
section = "defenderSection"
)
default boolean deprioritizeBait()
{
return true;
}
@ConfigItem(
keyName = "removePenanceCave",
name = "Remove penance cave",
description = "Removes 'Block' menu option from penance cave",
position = 4,
section = "defenderSection"
)
default boolean removePenanceCave()
{
return true;
}
/*///**********///*/
/*/// Healer ///*/
/*///**********///*/
@ConfigSection(
name = "Healer",
description = "",
position = 12,
keyName = "healerSection"
)
default boolean healerSection()
{
return false;
}
@ConfigItem(
keyName = "highlightPoison",
name = "Highlight called poison",
description = "Highlights poison called by your teammate",
position = 0,
section = "healerSection"
)
default boolean highlightPoison()
{
return true;
}
@ConfigItem(
keyName = "highlightPoisonColor",
name = "Poison color",
description = "Configures the color to highlight the called poison",
position = 1,
section = "healerSection",
hidden = true,
unhide = "highlightPoison"
)
default Color highlightPoisonColor()
{
return Color.GREEN;
}
@ConfigItem(
keyName = "highlightNotification",
name = "Highlight incorrect notification",
description = "Highlights incorrect poison chat notification",
position = 2,
section = "healerSection"
)
default boolean highlightNotification()
{
return true;
}
@ConfigItem(
keyName = "highlightNotificationColor",
name = "Notification color",
description = "Configures the color to highlight the notification text",
position = 3,
section = "healerSection",
hidden = true,
unhide = "highlightNotification"
)
default Color highlightNotificationColor()
{
return Color.RED;
}
@ConfigItem(
keyName = "showHpCountOverlay",
name = "Show number of hitpoints healed",
description = "Displays current number of hitpoints healed",
position = 4,
section = "healerSection"
)
default boolean showHpCountOverlay()
{
return true;
}
@ConfigItem(
keyName = "showTeammateHealthbars",
name = "Show health bars",
description = "Displays a health bar where a teammate's remaining health is located",
position = 5,
section = "healerSection"
)
default boolean showTeammateHealthbars()
{
return true;
}
@ConfigItem(
keyName = "healerCodes",
name = "Show healer codes",
description = "Overlay to show healer codes",
position = 6,
section = "healerSection"
)
default boolean healerCodes()
{
return false;
}
@ConfigItem(
keyName = "healerMenuOption",
name = "Show healer menu options",
description = "Shows tick count in healer menu options",
position = 7,
section = "healerSection"
)
default boolean healerMenuOption()
{
return false;
}
@ConfigItem(
keyName = "shiftOverstock",
name = "Enable shift overstock",
description = "Enables overstocking by pressing shift",
position = 8,
section = "healerSection"
)
default boolean shiftOverstock()
{
return true;
}
@ConfigItem(
keyName = "controlHealer",
name = "Control Healer",
description = "Hold ctrl to put last healer clicked on top",
position = 9,
section = "healerSection"
)
default boolean controlHealer()
{
return true;
}
/*///*************///*/
/*/// Collector ///*/
/*///*************///*/
@ConfigSection(
name = "Collector",
description = "",
position = 13,
keyName = "collectorSection"
)
default boolean collectorSection()
{
return false;
}
@ConfigItem(
keyName = "swapCollectorBag",
name = "Swap empty",
description = "Enables swapping of 'Look-in' and 'Empty' on the collector's bag",
position = 0,
section = "collectorSection"
)
default boolean swapCollectorBag()
{
return true;
}
@ConfigItem(
keyName = "swapDestroyEggs",
name = "Swap destroy",
description = "Enables swapping of 'Use' and 'Destroy' on collector eggs; this does not affect yellow/omega eggs",
position = 1,
section = "collectorSection"
)
default boolean swapDestroyEggs()
{
return true;
}
@ConfigItem(
keyName = "highlightCollectorEggs",
name = "Highlight collector eggs",
description = "Highlight called egg colors",
position = 2,
section = "collectorSection"
)
default boolean highlightCollectorEggs()
{
return true;
}
@ConfigItem(
keyName = "deprioritizeIncorrectEggs",
name = "Deprioritize incorrect eggs",
description = "Moves 'Take' menu option for incorrect eggs below 'Walk Here'",
position = 3,
section = "collectorSection"
)
default boolean deprioritizeIncorrectEggs()
{
return true;
}
@ConfigItem(
keyName = "showEggCountOverlay",
name = "Show number of eggs collected",
description = "Displays current number of eggs collected",
position = 4,
section = "collectorSection"
)
default boolean showEggCountOverlay()
{
return true;
}
}
@@ -1,228 +0,0 @@
/*
* Copyright (c) 2019, 7ate9 <https://github.com/se7enAte9>
* Copyright (c) 2019, https://openosrs.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.barbarianassault;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import javax.inject.Inject;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import net.runelite.client.menus.AbstractComparableEntry;
import net.runelite.client.menus.MenuManager;
class BarbarianAssaultMenu
{
private final MenuManager menuManager;
private final BarbarianAssaultPlugin game;
private final List<AbstractComparableEntry> tracker = new ArrayList<>();
@Getter(AccessLevel.PACKAGE)
@Setter(AccessLevel.PACKAGE)
private boolean hornUpdated = false;
@Getter(AccessLevel.PACKAGE)
@Setter(AccessLevel.PACKAGE)
private boolean rebuildForced = false;
@Inject
BarbarianAssaultMenu(final MenuManager menuManager, final BarbarianAssaultPlugin game)
{
this.menuManager = menuManager;
this.game = game;
}
private boolean isHornOptionHidden(String option)
{
if (game.isInGame() && game.getRole() != null && game.getRole().getTell(game.getLastCallText()).equalsIgnoreCase(option))
{
// This will force the menu to be rebuilt after the correct tell is found
// medic will be added to the menu if it wasn't there before
if (!hornUpdated)
{
rebuildForced = true;
}
hornUpdated = true;
return false;
}
return true;
}
void clearHiddenMenus()
{
// Clears menus from MenuManager and tracker
for (Iterator<AbstractComparableEntry> iterator = tracker.iterator(); iterator.hasNext(); )
{
menuManager.removeHiddenEntry(iterator.next());
iterator.remove();
}
}
//TODO add omega egg use on?
void validateHiddenMenus(Role role)
{
clearHiddenMenus();
HashSet<Menus> hiddenMenus = Sets.newHashSet(Menus.getMenus());
HashSet<Menus> conditionalMenus = Sets.newHashSet(Menus.getMenus());
// Any option left in this set will not be hidden
// Checking each option for the correct role prevents MenuManager from
// iterating over off role menu entry options that are not possible
conditionalMenus.removeIf(entry ->
{
switch (entry)
{
// Attacker role options
case TELL_BLUE_ATTACKER_HORN:
case TELL_GREEN_ATTACKER_HORN:
case TELL_RED_ATTACKER_HORN:
return ((role == Role.ATTACKER && isHornOptionHidden(entry.getOption())) || role == null) && game.isRemoveIncorrectCalls();
case ATTACK_PENANCE_FIGHTER:
case ATTACK_PENANCE_RANGER:
case GET_SPIKES_PETRIFIED_MUSHROOM:
case TAKE_ATTACKER_ITEM_MACHINE:
return (role != Role.ATTACKER && role != null) && game.isRemoveUnusedMenus();
// Defender role Options
case TELL_MEAT_DEFENDER_HORN:
case TELL_TOFU_DEFENDER_HORN:
case TELL_WORMS_DEFENDER_HORN:
return ((role == Role.DEFENDER && isHornOptionHidden(entry.getOption())) || role == null) && game.isRemoveIncorrectCalls();
case BLOCK_PENANCE_CAVE:
return ((role != Role.DEFENDER && role != null) && game.isRemoveUnusedMenus())
|| (role == Role.DEFENDER && game.isRemovePenanceCave());
case DUNK_LAVA_CRATER:
case FIX:
case STOCK_UP_DEFENDER_ITEM_MACHINE:
case TAKE_DEFENDER_ITEM_MACHINE:
case TAKE_HAMMER:
case TAKE_LOGS:
return (role != Role.DEFENDER && role != null) && game.isRemoveUnusedMenus();
// Collector role options
case TELL_ACCURATE_COLLECTOR_HORN:
case TELL_AGGRESSIVE_COLLECTOR_HORN:
case TELL_CONTROLLED_COLLECTOR_HORN:
case TELL_DEFENSIVE_COLLECTOR_HORN:
return ((role == Role.COLLECTOR && isHornOptionHidden(entry.getOption())) || role == null) && game.isRemoveIncorrectCalls();
case CONVERT_COLLECTOR_CONVERTER:
case LOAD_EGG_HOPPER:
case TAKE_BLUE_EGG:
case TAKE_GREEN_EGG:
case TAKE_RED_EGG:
case TAKE_YELLOW_EGG:
return (role != Role.COLLECTOR && role != null) && game.isRemoveUnusedMenus();
// Healer role options
case TELL_CRACKERS_HEALER_HORN:
case TELL_TOFU_HEALER_HORN:
case TELL_WORMS_HEALER_HORN:
return ((role == Role.HEALER && isHornOptionHidden(entry.getOption())) || role == null) && game.isRemoveIncorrectCalls();
case DUNK_POISON_CRATER:
case STOCK_UP_HEALER_ITEM_MACHINE:
case TAKE_HEALER_ITEM_MACHINE:
case TAKE_FROM_HEALER_SPRING:
case DRINK_FROM_HEALER_SPRING:
return (role != Role.HEALER && role != null) && game.isRemoveUnusedMenus();
case USE_VIAL_GROUND:
case USE_VIAL_ITEM:
case USE_VIAL_NPC:
case USE_VIAL_WIDGET:
return role == Role.HEALER && game.isRemoveUnusedMenus();
// Any role options
case DROP_HORN:
case EXAMINE_HORN:
case USE_HORN:
return game.isRemoveIncorrectCalls();
case MEDIC_HORN:
return game.isRemoveIncorrectCalls() && !hornUpdated;
default:
return role != null && game.isRemoveUnusedMenus();
}
});
hiddenMenus.removeAll(conditionalMenus);
for (Menus entry : hiddenMenus)
{
menuManager.addHiddenEntry(entry.getEntry());
tracker.add(entry.getEntry());
}
}
void enableSwaps()
{
if (game.isSwapLadder())
{
menuManager.addSwap("climb-down", "ladder", "quick-start", "ladder");
}
if (game.isSwapCollectorBag())
{
menuManager.addSwap("look-in", "collection bag", "empty", "collection bag");
}
if (game.isSwapDestroyEggs())
{
menuManager.addSwap("use", "blue egg", "destroy", "blue egg");
menuManager.addSwap("use", "green egg", "destroy", "green egg");
menuManager.addSwap("use", "red egg", "destroy", "red egg");
}
}
void disableSwaps(boolean force)
{
if (!game.isSwapLadder() || force)
{
menuManager.removeSwap("climb-down", "ladder", "quick-start", "ladder");
}
if (!game.isSwapCollectorBag() || force)
{
menuManager.removeSwap("look-in", "collection bag", "empty", "collection bag");
}
if (!game.isSwapDestroyEggs() || force)
{
menuManager.removeSwap("use", "blue egg", "destroy", "blue egg");
menuManager.removeSwap("use", "green egg", "destroy", "green egg");
menuManager.removeSwap("use", "red egg", "destroy", "red egg");
}
}
}
@@ -1,47 +0,0 @@
/*
* Copyright (c) 2019, 7ate9 <https://github.com/se7enAte9>
* Copyright (c) 2019, https://openosrs.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.barbarianassault;
public enum DeathTimesMode
{
BOTH("Both"),
CHAT_BOX("Chat Box"),
INFO_BOX("Info Box");
private final String name;
DeathTimesMode(String name)
{
this.name = name;
}
@Override
public String toString()
{
return name;
}
}

Some files were not shown because too many files have changed in this diff Show More