plugins = getInstalledPlugins();
- if (!plugins.contains(manifest.getInternalName()))
- {
- plugins.add(manifest.getInternalName());
- saveInstalledPlugins(plugins);
- }
- loadSideLoadPlugin(manifest.getInternalName());
- }
- }
- catch (IOException e)
- {
- log.error("Error installing plugin: {}", manifest.getInternalName(), e);
- }
- });
+ /**
+ * Creates an OkHttpClient instance that does not use any proxy settings.
+ * @param base
+ * @return
+ */
+ private static OkHttpClient noProxy(OkHttpClient base) {
+ return base.newBuilder()
+ .proxy(Proxy.NO_PROXY)
+ .proxySelector(ProxySelector.of(null))
+ .build();
}
- public void remove(String internalName)
+ /**
+ * Verifies that the SHA-256 hash of a locally installed plugin matches the
+ * authoritative hash from the manifest map.
+ *
+ * This ensures the integrity of the plugin and detects tampering or corruption.
+ *
+ * @param internalName the internal name of the plugin to verify (must not be null or empty)
+ * @return {@code true} if the plugin exists in both the local and authoritative manifests
+ * and the SHA-256 hashes match, {@code false} otherwise
+ * @throws IllegalArgumentException if {@code internalName} is null or empty
+ */
+ private boolean verifyHash(String internalName)
{
- executor.execute(() -> {
- List pluginsToRemove = pluginManager.getPlugins().stream()
- .filter(plugin -> {
- PluginDescriptor descriptor = plugin.getClass().getAnnotation(PluginDescriptor.class);
- if (descriptor == null) {
- return false;
- }
-
- boolean isExternal = descriptor.isExternal();
- String className = plugin.getClass().getSimpleName();
- String descriptorName = descriptor.name();
-
- boolean nameMatches = className.equals(internalName) ||
- descriptorName.equals(internalName) ||
- className.toLowerCase().equals(internalName.toLowerCase()) ||
- descriptorName.toLowerCase().equals(internalName.toLowerCase());
-
- return isExternal && nameMatches;
- })
- .collect(Collectors.toList());
-
- for (Plugin plugin : pluginsToRemove) {
- if (pluginManager.isPluginEnabled(plugin)) {
- try {
- pluginManager.setPluginEnabled(plugin, false);
-
- if (pluginManager.isPluginActive(plugin)) {
- SwingUtilities.invokeLater(() -> {
- try {
- pluginManager.stopPlugin(plugin);
- } catch (PluginInstantiationException e) {
- log.warn("Error stopping plugin {}: {}", plugin.getClass().getSimpleName(), e.getMessage());
- }
- });
- }
- } catch (Exception e) {
- log.warn("Error stopping plugin {}: {}", plugin.getClass().getSimpleName(), e.getMessage());
- }
- }
-
- pluginManager.remove(plugin);
- }
-
- File pluginFile = getPluginJarFile(internalName);
- if (pluginFile.exists()) {
- pluginFile.delete();
- }
+ if (internalName == null || internalName.isEmpty()) {
+ throw new IllegalArgumentException("Internal name is null/empty");
+ }
- List plugins = getInstalledPlugins();
- if (plugins.contains(internalName))
- {
- plugins.remove(internalName);
- saveInstalledPlugins(plugins);
- }
+ MicrobotPluginManifest localManifest = getInstalledPluginManifest(internalName);
+ MicrobotPluginManifest authoritativeManifest = manifestMap.get(internalName);
- eventBus.post(new ExternalPluginsChanged());
- });
- }
+ if (localManifest == null || authoritativeManifest == null) {
+ return false;
+ }
+ String localHash = localManifest.getSha256();
+ String authoritativeHash = authoritativeManifest.getSha256();
- private boolean verifyHash(byte[] jarData, String expectedHash)
- {
- if ((expectedHash == null || expectedHash.isEmpty()) || (jarData == null || jarData.length == 0))
- {
- throw new IllegalArgumentException("Hash or jar data is null/empty");
+ if (localHash == null || localHash.isEmpty() || authoritativeHash == null || authoritativeHash.isEmpty()) {
+ return false;
}
- String computedHash = calculateSHA256Hash(jarData);
- return computedHash.equals(expectedHash);
+ return localHash.equals(authoritativeHash);
}
- /**
- * Calculate SHA-256 hash for byte array data and return as hex string
- */
- private String calculateSHA256Hash(byte[] data)
- {
- try
- {
- MessageDigest digest = MessageDigest.getInstance("SHA-256");
-
- int offset = 0;
- int bufferSize = 8192;
-
- while (offset < data.length)
- {
- int bytesToProcess = Math.min(bufferSize, data.length - offset);
- digest.update(data, offset, bytesToProcess);
- offset += bytesToProcess;
- }
-
- byte[] hash = digest.digest();
+ public static File[] createSideloadingFolder()
+ {
+ try
+ {
+ Files.createParentDirs(PLUGIN_DIR);
- StringBuilder hexString = new StringBuilder();
- for (byte b : hash)
- {
- String hex = Integer.toHexString(0xff & b);
- if (hex.length() == 1)
- {
- hexString.append('0');
- }
- hexString.append(hex);
- }
- return hexString.toString();
- }
- catch (NoSuchAlgorithmException e)
- {
- log.trace("Error computing SHA-256 hash", e);
- throw new RuntimeException("SHA-256 algorithm not found", e);
- }
- }
+ if (!PLUGIN_DIR.exists() && PLUGIN_DIR.mkdir())
+ {
+ log.debug("Directory for sideloading was created successfully.");
+ }
+ }
+ catch (IOException e)
+ {
+ log.trace("Error creating directory for microbot-plugins!", e);
+ }
- public static File[] createSideloadingFolder() {
- final File MICROBOT_PLUGINS = new File(RuneLite.RUNELITE_DIR, "microbot-plugins");
- if (!java.nio.file.Files.exists(MICROBOT_PLUGINS.toPath())) {
- try {
- java.nio.file.Files.createDirectories(MICROBOT_PLUGINS.toPath());
- log.debug("Directory for sideloading was created successfully.");
- return MICROBOT_PLUGINS.listFiles();
- } catch (IOException e) {
- log.trace("Error creating directory for sideloading!", e);
- }
- }
- return MICROBOT_PLUGINS.listFiles();
- }
+ return PLUGIN_DIR.listFiles();
+ }
/**
* Loads a single plugin from the sideload folder if not already loaded.
@@ -405,19 +341,19 @@ private void loadSideLoadPlugin(String internalName)
log.debug("Plugin file {} does not exist", pluginFile);
return;
}
- List installedPlugins = getInstalledPlugins();
- if (!installedPlugins.contains(internalName))
+ List installedPlugins = getInstalledPlugins();
+ if (installedPlugins.stream().noneMatch(x -> x.getInternalName().equals(internalName)))
{
- return; // Not installed
+ return;
}
Set loadedInternalNames = pluginManager.getPlugins().stream()
.filter(p -> p.getClass().isAnnotationPresent(PluginDescriptor.class))
.filter(p -> p.getClass().getAnnotation(PluginDescriptor.class).isExternal())
- .map(p -> p.getClass().getAnnotation(PluginDescriptor.class).name())
+ .map(p -> p.getClass().getSimpleName())
.collect(Collectors.toSet());
if (loadedInternalNames.contains(internalName))
{
- return; // Already loaded
+ return;
}
MicrobotPluginManifest manifest = manifestMap.get(internalName);
if (manifest == null)
@@ -427,35 +363,26 @@ private void loadSideLoadPlugin(String internalName)
}
try
{
- byte[] fileBytes = Files.toByteArray(pluginFile);
- // Validate hash before loading
- if (!verifyHash(fileBytes, manifest.getSha256()))
+ if (!verifyHash(manifest.getInternalName()))
{
- log.error("Hash mismatch for plugin {}. Skipping load.", internalName);
- pluginFile.delete();
- List plugins = getInstalledPlugins();
- plugins.remove(internalName);
- saveInstalledPlugins(plugins);
- eventBus.post(new ExternalPluginsChanged());
- return;
+ log.warn("Plugin hash verification failed for: {}", manifest.getInternalName());
}
List> plugins = new ArrayList<>();
- MicrobotPluginClassLoader classLoader = new MicrobotPluginClassLoader(getClass().getClassLoader(), pluginFile.getName(), fileBytes);
- Set classNamesToLoad = classLoader.getLoadedClassNames();
- for (String className : classNamesToLoad)
+ MicrobotPluginClassLoader classLoader = new MicrobotPluginClassLoader(pluginFile, getClass().getClassLoader());
+
+ for (ClassPath.ClassInfo classInfo : ClassPath.from(classLoader).getAllClasses())
{
try
{
- Class> clazz = classLoader.loadClass(className);
+ Class> clazz = classLoader.loadClass(classInfo.getName());
plugins.add(clazz);
}
catch (ClassNotFoundException e)
{
- log.trace("Class not found during sideloading: {}", className, e);
+ log.trace("Class not found during sideloading: {}", classInfo.getName(), e);
}
}
loadPlugins(plugins, null);
- eventBus.post(new ExternalPluginsChanged());
}
catch (PluginInstantiationException | IOException e)
{
@@ -465,16 +392,20 @@ private void loadSideLoadPlugin(String internalName)
public void loadSideLoadPlugins()
{
+ if (safeMode) {
+ log.warn("Safe mode is enabled, skipping loading of sideloaded plugins.");
+ return;
+ }
File[] files = createSideloadingFolder();
if (files == null)
{
return;
}
- List installedPlugins = getInstalledPlugins();
+ List installedPlugins = getInstalledPlugins();
Set loadedInternalNames = pluginManager.getPlugins().stream()
.filter(p -> p.getClass().isAnnotationPresent(PluginDescriptor.class))
.filter(p -> p.getClass().getAnnotation(PluginDescriptor.class).isExternal())
- .map(p -> p.getClass().getAnnotation(PluginDescriptor.class).name())
+ .map(p -> p.getClass().getSimpleName())
.collect(Collectors.toSet());
for (File f : files)
{
@@ -483,16 +414,17 @@ public void loadSideLoadPlugins()
continue;
}
String internalName = f.getName().replace(".jar", "");
- if (!installedPlugins.contains(internalName))
+ if (installedPlugins.stream().noneMatch(x -> x.getInternalName().equals(internalName)))
{
- continue; // Skip if not in installed list
+ continue;
}
if (loadedInternalNames.contains(internalName))
{
- continue; // Already loaded
+ continue;
}
loadSideLoadPlugin(internalName);
}
+ eventBus.post(new ExternalPluginsChanged());
}
/**
@@ -531,7 +463,7 @@ static List topologicalSort(Graph graph) {
return l;
}
- public List loadPlugins(List> plugins, BiConsumer onPluginLoaded) throws PluginInstantiationException
+ private List loadPlugins(List> plugins, BiConsumer onPluginLoaded) throws PluginInstantiationException
{
MutableGraph> graph = GraphBuilder
.directed()
@@ -565,15 +497,13 @@ public List loadPlugins(List> plugins, BiConsumer loadPlugins(List> plugins, BiConsumer) clazz);
}
- // Build plugin graph
for (Class extends Plugin> pluginClazz : graph.nodes())
{
PluginDependency[] pluginDependencies = pluginClazz.getAnnotationsByType(PluginDependency.class);
@@ -657,7 +586,6 @@ private Plugin instantiate(Collection scannedPlugins, Class claz
if (deps.size() > 1) {
List modules = new ArrayList<>(deps.size());
for (Plugin p : deps) {
- // Create a module for each dependency
com.google.inject.Module module = (Binder binder) ->
{
binder.bind((Class) p.getClass()).toInstance(p);
@@ -666,17 +594,13 @@ private Plugin instantiate(Collection scannedPlugins, Class claz
modules.add(module);
}
- // Create a parent injector containing all of the dependencies
parent = parent.createChildInjector(modules);
} else if (!deps.isEmpty()) {
- // With only one dependency we can simply use its injector
parent = deps.get(0).getInjector();
}
- // Create injector for the module
Module pluginModule = (Binder binder) ->
{
- // Since the plugin itself is a module, it won't bind itself, so we'll bind it here
binder.bind(clazz).toInstance(plugin);
binder.install(plugin);
};
@@ -690,77 +614,604 @@ private Plugin instantiate(Collection scannedPlugins, Class claz
return plugin;
}
- /**
- * Check if the current client version is compatible with the required minimum version
- */
- public boolean isClientVersionCompatible(String minClientVersion) {
- if (minClientVersion == null || minClientVersion.isEmpty()) {
- return true;
- }
+ /**
+ * Determines if a class is a Microbot-related Plugin that should be loaded.
+ * This includes plugins from utility packages, UI components, and specific Microbot systems.
+ *
+ * @param clazz the class to check
+ * @return true if the class should be included in Microbot plugin loading
+ */
+ private static boolean isMicrobotRelatedPlugin(Class> clazz) {
+ if (clazz == null || clazz.getPackage() == null) {
+ return false;
+ }
- String currentVersion = RuneLiteProperties.getMicrobotVersion();
- if (currentVersion == null) {
- log.warn("Unable to determine current Microbot version");
- return false;
- }
+ if (!Plugin.class.isAssignableFrom(clazz) || clazz == Plugin.class) {
+ return false;
+ }
- return compareVersions(currentVersion, minClientVersion) >= 0;
- }
+ PluginDescriptor descriptor = clazz.getAnnotation(PluginDescriptor.class);
+ if (descriptor == null) {
+ return false;
+ }
- /**
- * Compare two version strings using semantic versioning with support for 4-part versions
- * Supports formats like: 1.9.7, 1.9.7.1, 1.9.8, 1.9.8.1
- * @param version1 The first version to compare
- * @param version2 The second version to compare
- * @return -1 if version1 < version2, 0 if equal, 1 if version1 > version2
- */
- @VisibleForTesting
- static int compareVersions(String version1, String version2) {
- if (version1 == null && version2 == null) return 0;
- if (version1 == null) return -1;
- if (version2 == null) return 1;
+ String pkg = clazz.getPackage().getName();
+
+ if (pkg.startsWith(PLUGIN_PACKAGE)) {
+ return pkg.equals(PLUGIN_PACKAGE)
+ || pkg.contains(".ui")
+ || pkg.contains(".util")
+ || pkg.contains(".shortestpath")
+ || pkg.contains(".rs2cachedebugger")
+ || pkg.contains(".questhelper")
+ || pkg.contains("pluginscheduler")
+ || pkg.contains("inventorysetups")
+ || pkg.contains("breakhandler");
+ }
- // Split versions by dots and handle up to 4 parts (major.minor.patch.build)
- String[] v1Parts = version1.split("\\.");
- String[] v2Parts = version2.split("\\.");
+ return false;
+ }
- int maxLength = Math.max(v1Parts.length, v2Parts.length);
+ /**
+ * Scans the classpath for Microbot-related Plugin classes and returns them.
+ * This includes plugin classes from utility packages, UI components, and specific Microbot systems.
+ *
+ * @return list of Microbot-related Plugin classes found on the classpath
+ */
+ private List> scanForMicrobotPlugins() {
+ List> microbotPlugins = new ArrayList<>();
+
+ try {
+ ClassPath classPath = ClassPath.from(getClass().getClassLoader());
+
+ for (ClassPath.ClassInfo classInfo : classPath.getAllClasses()) {
+ if (!classInfo.getPackageName().startsWith(PLUGIN_PACKAGE)) {
+ continue;
+ }
- for (int i = 0; i < maxLength; i++) {
- int v1Part = i < v1Parts.length ? parseVersionPart(v1Parts[i]) : 0;
- int v2Part = i < v2Parts.length ? parseVersionPart(v2Parts[i]) : 0;
+ try {
+ Class> clazz = classInfo.load();
+ if (isMicrobotRelatedPlugin(clazz)) {
+ microbotPlugins.add(clazz);
+ log.debug("Found Microbot plugin class: {}", clazz.getName());
+ }
+ } catch (Throwable e) {
+ log.trace("Could not load class during Microbot scan: {}", classInfo.getName(), e);
+ }
+ }
- if (v1Part < v2Part) return -1;
- if (v1Part > v2Part) return 1;
- }
+ log.info("Found {} additional Microbot plugin classes during classpath scan", microbotPlugins.size());
+ } catch (IOException e) {
+ log.error("Failed to scan classpath for Microbot plugin classes", e);
+ }
- return 0;
- }
+ return microbotPlugins;
+ }
- /**
- * Parse a version part, extracting only the numeric portion
- */
- private static int parseVersionPart(String part) {
- if (part == null || part.isEmpty()) return 0;
+ public void loadCorePlugins(List> plugins) throws PluginInstantiationException
+ {
+ SplashScreen.stage(.59, null, "Loading plugins");
+ List> combinedPlugins = new ArrayList<>(plugins);
- StringBuilder numericPart = new StringBuilder();
- for (char c : part.toCharArray()) {
- if (!Character.isDigit(c)) break;
- numericPart.append(c);
- }
+ List> additionalMicrobotPlugins = scanForMicrobotPlugins();
- try {
- return numericPart.length() > 0 ? Integer.parseInt(numericPart.toString()) : 0;
- } catch (NumberFormatException e) {
- return 0;
- }
- }
+ Set> existingPlugins = new HashSet<>(plugins);
- public void loadCorePlugins(List> plugins) throws IOException, PluginInstantiationException
- {
- SplashScreen.stage(.59, null, "Loading plugins");
+ List> newMicrobotPlugins = additionalMicrobotPlugins.stream()
+ .filter(clazz -> !existingPlugins.contains(clazz))
+ .collect(Collectors.toList());
- loadPlugins(plugins, (loaded, total) ->
- SplashScreen.stage(.60, .70, null, "Loading plugins", loaded, total, false));
- }
+ combinedPlugins.addAll(newMicrobotPlugins);
+
+ log.info("Loading core plugins: {} passed in + {} core Microbot plugins = {} total",
+ plugins.size(), newMicrobotPlugins.size(), combinedPlugins.size());
+
+ if (!combinedPlugins.isEmpty()) {
+ loadPlugins(combinedPlugins, (loaded, total) ->
+ SplashScreen.stage(.60, .70, null, "Loading Microbot plugins", loaded, total, false));
+ }
+ }
+
+ @Subscribe
+ public void onClientShutdown(ClientShutdown shutdown)
+ {
+ log.info("Client shutdown detected, stopping all Microbot plugins");
+ shutdown();
+ }
+
+ /**
+ * Handles profile changes by refreshing plugins for the new profile.
+ */
+ @Subscribe
+ public void onProfileChanged(ProfileChanged profileChanged) {
+ if (profileRefreshInProgress) {
+ log.debug("Profile refresh already in progress, skipping duplicate request");
+ return;
+ }
+
+ log.info("Profile changed, refreshing Microbot plugins for new profile");
+ update();
+ }
+
+ /**
+ * Refreshes plugins when the profile changes or when install/remove operations occur.
+ */
+ private void refresh() {
+ if (safeMode) {
+ log.warn("Safe mode is enabled, skipping loading of sideloaded plugins.");
+ return;
+ }
+
+ if (isShuttingDown.get()) {
+ return;
+ }
+
+ synchronized (this) {
+ if (profileRefreshInProgress) {
+ return;
+ }
+ profileRefreshInProgress = true;
+ }
+
+ try {
+ log.debug("Starting plugin refresh");
+
+ List installedPlugins = getInstalledPlugins();
+
+ List disabledPlugins = installedPlugins.stream()
+ .filter(plugin -> {
+ MicrobotPluginManifest upstreamManifest = manifestMap.get(plugin.getInternalName());
+ return upstreamManifest != null && upstreamManifest.isDisable();
+ })
+ .collect(Collectors.toList());
+
+ if (!disabledPlugins.isEmpty()) {
+ log.warn("Found {} disabled plugin(s) that have been disabled upstream:", disabledPlugins.size());
+ for (MicrobotPluginManifest disabledPlugin : disabledPlugins) {
+ log.warn(" - Plugin '{}' ({}) has been disabled upstream and will be removed from your installed plugins",
+ disabledPlugin.getDisplayName(), disabledPlugin.getInternalName());
+ }
+
+ List enabledPlugins = installedPlugins.stream()
+ .filter(plugin -> {
+ MicrobotPluginManifest upstreamManifest = manifestMap.get(plugin.getInternalName());
+ return upstreamManifest == null || !upstreamManifest.isDisable();
+ })
+ .collect(Collectors.toList());
+
+ saveInstalledPlugins(enabledPlugins);
+ installedPlugins = enabledPlugins;
+
+ log.info("Automatically removed {} disabled plugin(s) from your installed plugins list", disabledPlugins.size());
+ }
+
+ Set installedNames = installedPlugins.stream()
+ .map(MicrobotPluginManifest::getInternalName)
+ .collect(Collectors.toSet());
+
+ List allLoadedPlugins = new ArrayList<>(pluginManager.getPlugins());
+
+ List loadedExternalPlugins = allLoadedPlugins.stream()
+ .filter(plugin -> {
+ PluginDescriptor descriptor = plugin.getClass().getAnnotation(PluginDescriptor.class);
+ if (descriptor == null || !descriptor.isExternal()) {
+ return false;
+ }
+ String packageName = plugin.getClass().getPackage().getName();
+ return packageName.contains("microbot");
+ })
+ .collect(Collectors.toList());
+
+ Set loadedPluginNames = loadedExternalPlugins.stream()
+ .map(plugin -> plugin.getClass().getSimpleName())
+ .collect(Collectors.toSet());
+
+ log.info("Profile refresh - Installed plugins: {}, Currently loaded Microbot plugins: {}",
+ installedNames, loadedPluginNames);
+
+ log.debug("All loaded plugins ({}):", allLoadedPlugins.size());
+ for (Plugin plugin : allLoadedPlugins) {
+ PluginDescriptor descriptor = plugin.getClass().getAnnotation(PluginDescriptor.class);
+ boolean isExternal = descriptor != null && descriptor.isExternal();
+ MicrobotPluginManifest manifest = getPluginManifest(plugin);
+ log.debug(" - {} (external: {}, has manifest: {})",
+ plugin.getClass().getSimpleName(), isExternal, manifest != null);
+ }
+
+ Map validManifests = installedNames.stream()
+ .map(pluginName -> Map.entry(pluginName, manifestMap.get(pluginName)))
+ .filter(entry -> {
+ if (entry.getValue() == null) {
+ log.warn("No manifest found for installed plugin: {}", entry.getKey());
+ return false;
+ }
+ return true;
+ })
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+
+ Set needsDownload = validManifests.keySet().stream()
+ .filter(microbotPluginManifest -> !getPluginJarFile(microbotPluginManifest).exists())
+ .collect(Collectors.toSet());
+
+ Set needsRedownload = validManifests.keySet().stream()
+ .filter(pluginName -> {
+ File pluginFile = getPluginJarFile(pluginName);
+ if (!pluginFile.exists())
+ {
+ return false;
+ }
+ if (!verifyHash(pluginName))
+ {
+ log.info("Hash verification failed for plugin: {}. Marking for redownload.", pluginName);
+ if (pluginFile.delete())
+ {
+ log.info("Deleted outdated plugin file: {}", pluginFile.getName());
+ }
+ else
+ {
+ log.warn("Failed to delete outdated plugin file: {}", pluginFile.getAbsolutePath());
+ }
+ return true;
+ }
+ return false;
+ })
+ .collect(Collectors.toSet());
+
+ needsDownload.addAll(needsRedownload);
+
+ Set keepFiles = validManifests.keySet().stream()
+ .map(this::getPluginJarFile)
+ .filter(File::exists)
+ .collect(Collectors.toSet());
+
+ Set validPluginManifests = new HashSet<>(validManifests.values());
+
+ Instant now = Instant.now();
+ Instant keepAfter = now.minus(3, ChronoUnit.DAYS);
+
+ Optional.ofNullable(PLUGIN_DIR.listFiles((dir, name) -> name.endsWith(".jar"))).stream()
+ .flatMap(Arrays::stream)
+ .filter(file -> !keepFiles.contains(file) && file.lastModified() < keepAfter.toEpochMilli())
+ .forEach(file -> {
+ log.info("Cleaning up old plugin file (>3 days): {}", file.getName());
+ if (!file.delete()) {
+ log.warn("Failed to delete old plugin file: {}", file.getAbsolutePath());
+ }
+ });
+
+ for (String pluginName : needsDownload) {
+ log.info("Downloading missing plugin: {}", pluginName);
+ if (!downloadPlugin(pluginName)) {
+ MicrobotPluginManifest failedManifest = manifestMap.get(pluginName);
+ if (failedManifest != null) {
+ validPluginManifests.remove(failedManifest);
+ }
+ }
+ }
+
+ Set installedPluginNames = validPluginManifests.stream()
+ .map(MicrobotPluginManifest::getInternalName)
+ .collect(Collectors.toSet());
+
+ Set toAdd = validPluginManifests.stream()
+ .filter(manifest -> !loadedPluginNames.contains(manifest.getInternalName()))
+ .collect(Collectors.toSet());
+
+ List toRemove = loadedExternalPlugins.stream()
+ .filter(plugin -> !installedPluginNames.contains(plugin.getClass().getSimpleName()))
+ .collect(Collectors.toList());
+
+ log.info("Plugin refresh - Will add: {} plugins, Will remove: {} plugins",
+ toAdd.stream().map(MicrobotPluginManifest::getInternalName).collect(Collectors.toSet()),
+ toRemove.stream().map(p -> p.getClass().getSimpleName()).collect(Collectors.toSet()));
+
+ toRemove.forEach(plugin -> {
+ log.info("Stopping plugin \"{}\" (no longer installed for this profile)", plugin.getClass().getSimpleName());
+ stopPlugin(plugin);
+ });
+
+ for (MicrobotPluginManifest manifest : toAdd) {
+ String pluginName = manifest.getInternalName();
+ File pluginFile = getPluginJarFile(pluginName);
+ if (!pluginFile.exists()) {
+ log.warn("Plugin file missing for {}, skipping load", pluginName);
+ continue;
+ }
+
+ log.info("Loading plugin \"{}\"", pluginName);
+ List newPlugins = null;
+ MicrobotPluginClassLoader classLoader = null;
+ try {
+ if (!verifyHash(pluginName)) {
+ log.warn("Plugin hash verification failed for: {}. The installed version may be outdated or from a different source.", pluginName);
+ continue;
+ }
+
+ List> pluginClasses = new ArrayList<>();
+ classLoader = new MicrobotPluginClassLoader(pluginFile, getClass().getClassLoader());
+
+ for (ClassPath.ClassInfo classInfo : ClassPath.from(classLoader).getAllClasses()) {
+ try
+ {
+ Class> clazz = classLoader.loadClass(classInfo.getName());
+ pluginClasses.add(clazz);
+ }
+ catch (ClassNotFoundException e)
+ {
+ log.trace("Class not found during plugin loading: {}", classInfo.getName(), e);
+ }
+ }
+
+ newPlugins = loadPlugins(pluginClasses, null);
+
+ boolean startup = SplashScreen.isOpen();
+ if (!startup && !newPlugins.isEmpty()) {
+ pluginManager.loadDefaultPluginConfiguration(newPlugins);
+ final List pluginsToStart = newPlugins;
+ SwingUtilities.invokeAndWait(() -> {
+ try {
+ for (Plugin p : pluginsToStart) {
+ pluginManager.startPlugin(p);
+ }
+ } catch (PluginInstantiationException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+ log.info("Successfully loaded plugin: {}", pluginName);
+ } catch (ThreadDeath e) {
+ throw e;
+ } catch (Throwable e) {
+ log.warn("Unable to load or start plugin \"{}\"", pluginName, e);
+ }
+ }
+
+ if (!toAdd.isEmpty() || !toRemove.isEmpty()) {
+ eventBus.post(new ExternalPluginsChanged());
+ }
+
+ log.info("Completed plugin refresh - Added: {}, Removed: {}", toAdd.size(), toRemove.size());
+ } catch (Exception e) {
+ log.error("Error during plugin refresh", e);
+ } finally {
+ profileRefreshInProgress = false;
+ }
+ }
+
+ /**
+ * Downloads a plugin JAR file from the remote server.
+ *
+ * @param internalName the internal name of the plugin to download
+ * @return true if the plugin was successfully downloaded, false otherwise
+ */
+ private boolean downloadPlugin(String internalName) {
+ MicrobotPluginManifest manifest = manifestMap.get(internalName);
+ if (manifest == null) {
+ log.error("Cannot download plugin {}: manifest not found", internalName);
+ return false;
+ }
+
+ try {
+ File pluginFile = getPluginJarFile(internalName);
+
+ HttpUrl jarUrl = microbotPluginClient.getJarURL(manifest);
+ if (jarUrl == null || !jarUrl.isHttps()) {
+ log.error("Invalid JAR URL for plugin {}", internalName);
+ return false;
+ }
+
+ OkHttpClient clientWithoutProxy = noProxy(okHttpClient);
+ Request request = new Request.Builder()
+ .url(jarUrl)
+ .build();
+
+ try (Response response = clientWithoutProxy.newCall(request).execute()) {
+ if (!response.isSuccessful()) {
+ log.error("Failed to download plugin {}: HTTP {}", internalName, response.code());
+ return false;
+ }
+
+ byte[] jarData = response.body().bytes();
+
+ Files.write(jarData, pluginFile);
+ log.info("Plugin {} downloaded to {}", internalName, pluginFile.getAbsolutePath());
+ return true;
+ }
+
+ } catch (Exception e) {
+ log.error("Failed to download plugin {}", internalName, e);
+
+ File pluginFile = getPluginJarFile(internalName);
+ if (pluginFile.exists() && !pluginFile.delete()) {
+ log.warn("Failed to delete corrupted plugin file: {}", pluginFile.getAbsolutePath());
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Installs a plugin and triggers UI refresh.
+ *
+ * @param manifest the manifest of the plugin to install
+ */
+ public void installPlugin(MicrobotPluginManifest manifest) {
+ executor.submit(() -> install(manifest));
+ }
+
+ /**
+ * Removes a plugin and triggers UI refresh.
+ *
+ * @param manifest the manifest of the plugin to remove
+ */
+ public void removePlugin(MicrobotPluginManifest manifest) {
+ executor.submit(() -> remove(manifest));
+ }
+
+ /**
+ * Installs a plugin by adding it to the installed plugins list in config.
+ *
+ * @param manifest the manifest of the plugin to install
+ */
+ public void install(MicrobotPluginManifest manifest) {
+ if (manifest == null || !manifestMap.containsValue(manifest)) {
+ log.error("Can't install plugin: unable to identify manifest");
+ return;
+ }
+
+ final String internalName = manifest.getInternalName();
+ if (internalName == null || internalName.isEmpty()) {
+ log.error("Cannot install plugin: internal name is null or empty");
+ return;
+ }
+
+ if (manifest.isDisable()) {
+ log.warn("Cannot install plugin '{}' ({}): This plugin has been disabled upstream by the developers. " +
+ "This usually means the plugin is no longer functional, has security issues, or has been deprecated.",
+ manifest.getDisplayName(), internalName);
+ return;
+ }
+
+ List installedPlugins = getInstalledPlugins();
+
+ if (installedPlugins.stream().anyMatch(p -> internalName.equals(p.getInternalName()))) {
+ log.info("Plugin {} is already installed", internalName);
+ return;
+ }
+
+ installedPlugins.add(manifest);
+ saveInstalledPlugins(installedPlugins);
+
+ log.info("Added plugin {} to installed list", manifest.getDisplayName());
+
+ update();
+ }
+
+ /**
+ * Removes a plugin by removing it from the installed plugins list in config.
+ *
+ * @param manifest the manifest of the plugin to remove
+ */
+ public void remove(MicrobotPluginManifest manifest) {
+ if (manifest == null) {
+ log.error("Can't remove plugin: unable to identify manifest");
+ return;
+ }
+
+ final String internalName = manifest.getInternalName();
+
+ if (internalName == null || internalName.isEmpty()) {
+ log.error("Cannot remove plugin: internal name is null or empty");
+ return;
+ }
+
+ List installedPlugins = getInstalledPlugins();
+
+ boolean wasInstalled = installedPlugins.removeIf(p -> internalName.equals(p.getInternalName()));
+
+ if (!wasInstalled) {
+ log.info("Plugin {} was not in installed list", internalName);
+ return;
+ }
+
+ saveInstalledPlugins(installedPlugins);
+
+ log.info("Removed plugin {} from installed list", internalName);
+
+ update();
+ }
+
+ /**
+ * Submits a plugin refresh task to the executor.
+ * This will reload plugins based on the current profile's installed plugins list.
+ */
+ public void update() {
+ executor.submit(this::refresh);
+ }
+
+ /**
+ * Gets the manifest for a given plugin, this pulls from the global manifest map.
+ *
+ * @param plugin the plugin to get the manifest for
+ * @return the manifest for the plugin, or null if not found or not an external plugin
+ */
+ @Nullable
+ private MicrobotPluginManifest getPluginManifest(Plugin plugin) {
+ PluginDescriptor descriptor = plugin.getClass().getAnnotation(PluginDescriptor.class);
+ if (descriptor == null || !descriptor.isExternal()) {
+ return null;
+ }
+
+ String internalName = plugin.getClass().getSimpleName();
+
+ return manifestMap.get(internalName);
+ }
+
+ /**
+ * Gets the manifest for a plugin from the current profile's installed plugins list only.
+ *
+ * @param internalName the internal name of the plugin
+ * @return the manifest for the plugin from the current profile, or null if not found
+ */
+ @Nullable
+ private MicrobotPluginManifest getInstalledPluginManifest(String internalName) {
+ List installedPlugins = getInstalledPlugins();
+ return installedPlugins.stream()
+ .filter(manifest -> internalName.equals(manifest.getInternalName()))
+ .findFirst()
+ .orElse(null);
+ }
+
+ /**
+ * Gracefully stops a plugin
+ */
+ private void stopPlugin(Plugin plugin) {
+ String pluginName = plugin.getClass().getSimpleName();
+
+ try {
+ if (pluginManager.isPluginEnabled(plugin)) {
+ pluginManager.setPluginEnabled(plugin, false);
+ }
+
+ if (pluginManager.isPluginActive(plugin)) {
+ SwingUtilities.invokeAndWait(() -> {
+ try {
+ pluginManager.stopPlugin(plugin);
+ } catch (PluginInstantiationException e) {
+ log.warn("Error stopping plugin {}: {}", pluginName, e.getMessage());
+ }
+ });
+ }
+ pluginManager.remove(plugin);
+ } catch (Exception e) {
+ log.warn("Error during plugin stop for {}: {}", pluginName, e.getMessage());
+ }
+ }
+
+ /**
+ * Gracefully shuts down the plugin manager and performs final cleanup.
+ */
+ private void shutdown() {
+ if (!isShuttingDown.compareAndSet(false, true)) {
+ return;
+ }
+
+ log.info("Shutting down MicrobotPluginManager");
+
+ try {
+ List externalPlugins = pluginManager.getPlugins().stream()
+ .filter(plugin -> {
+ PluginDescriptor descriptor = plugin.getClass().getAnnotation(PluginDescriptor.class);
+ return descriptor != null && descriptor.isExternal();
+ })
+ .collect(Collectors.toList());
+
+ for (Plugin plugin : externalPlugins) {
+ stopPlugin(plugin);
+ }
+
+ log.info("MicrobotPluginManager shutdown complete");
+ } catch (Exception e) {
+ log.error("Error during MicrobotPluginManager shutdown", e);
+ }
+ }
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/farmTreeRun/FarmTreeRunConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/farmTreeRun/FarmTreeRunConfig.java
index 5599dba18f9..939ddd2bf1d 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/farmTreeRun/FarmTreeRunConfig.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/farmTreeRun/FarmTreeRunConfig.java
@@ -33,290 +33,282 @@
" Filled Bottomless compost bucket\n" +
"" +
"
Extra information:\n" +
- "
If you want to stop the script during your farm run (maybe it gets stuck or whatever reason), make sure to disable 'Banking' and disable patches you previously ran.
Happy botting\n"
+ "
If you want to stop the script during your farm run (maybe it gets stuck or whatever reason), make sure to disable 'Banking' and disable patches you previously ran." +
+ "Happy botting\n" +
+ "
UI and new trees added thanks to Diogenes and T\n" +
+ "
The tree order is as follows: GS Fruit → GS Tree → TGV Fruit → Farming Guild Tree → Farming Guild Fruit → Taverley → Falador → Lumbridge → Varrock → Brimhaven Fruit → Catherby Fruit → Fossil A/B/C → Lletya Fruit → Auburnvale Tree → Kastori Fruit → Avium Savannah Hardwood.
Patches are listed in the order they will be attended filtered by type\n"
)
public interface FarmTreeRunConfig extends Config {
-
public static final boolean DEBUG_MODE = System.getProperty("java.vm.info", "").contains("sharing");
+ /* =========================
+ * Sections (as requested)
+ * ========================= */
@ConfigSection(
- name = "General",
- description = "General",
+ name = "Sapling selection",
+ description = "Choose which saplings to plant",
position = 1
)
- String generalSection = "general";
+ String saplingSection = "saplingSection";
- // Tree patches section
@ConfigSection(
- name = "Tree patches",
- description = "Select which tree patches to use",
+ name = "Protection",
+ description = "Configure payment (protection) per tree type",
position = 2
)
+ String protectionSection = "protectionSection";
+
+ @ConfigSection(
+ name = "Gear",
+ description = "General gear and run settings",
+ position = 3
+ )
+ String gearSection = "gearSection";
+
+ @ConfigSection(
+ name = "Tree patches",
+ description = "Select which regular tree patches to use",
+ position = 4
+ )
String treePatchesSection = "treePatchesSection";
- // Fruit tree patches section
@ConfigSection(
name = "Fruit tree patches",
description = "Select which fruit tree patches to use",
- position = 3
+ position = 5
)
String fruitTreePatchesSection = "fruitTreePatchesSection";
-// TODO: Not implemented yet
-// @ConfigItem(
-// keyName = "trackRuneLite",
-// name = "Use RuneLite time tracking plugin",
-// description = "When enabled it tracks RuneLite farm patch times. Only when select farm patches below are fully grown, it will start the farm run.",
-// position = 1,
-// section = generalSection
-// )
-// default boolean trackRuneLiteTimeTracking()
-// {
-// return false;
-// }
-
- @ConfigItem(
- keyName = "banking",
- name = "Banking",
- description = "Enabling this will run to bank and reset inventory with required items.",
- position = 1,
- section = generalSection
+ @ConfigSection(
+ name = "Hardwood patches",
+ description = "Select which hardwood patches to use",
+ position = 6
)
- default boolean banking() {
- return true;
- }
+ String hardTreePatchesSection = "hardTreePatchesSection";
+ /* =========================
+ * Sapling selection
+ * ========================= */
@ConfigItem(
keyName = "treeSapling",
name = "Tree sapling",
description = "Select tree sapling to use",
+ position = 0,
+ section = saplingSection
+ )
+ default TreeEnums selectedTree() { return TreeEnums.MAPLE; }
+
+ @ConfigItem(
+ keyName = "fruitTreeSapling",
+ name = "Fruit tree sapling",
+ description = "Select fruit tree sapling to use",
+ position = 1,
+ section = saplingSection
+ )
+ default FruitTreeEnum selectedFruitTree() { return FruitTreeEnum.PAPAYA; }
+
+ @ConfigItem(
+ keyName = "Fossil Island Tree",
+ name = "Hard sapling",
+ description = "Select Hard tree sapling to use",
position = 2,
- section = generalSection
+ section = saplingSection
)
- default TreeEnums selectedTree() {
- return TreeEnums.MAPLE;
- }
+ default HardTreeEnums selectedHardTree() { return HardTreeEnums.MAHOGANY; }
+ /* =========================
+ * Protection
+ * ========================= */
@ConfigItem(
keyName = "protectTree",
name = "Protect trees",
description = "Do you want to protect your trees?",
- position = 3,
- section = generalSection
- )
- default boolean protectTrees() {
- return true;
- }
-
- @ConfigItem(
- keyName = "fruitTreeSapling",
- name = "Fruit tree sapling",
- description = "Select fruit tree sapling to use",
- position = 4,
- section = generalSection
+ position = 0,
+ section = protectionSection
)
- default FruitTreeEnum selectedFruitTree() {
- return FruitTreeEnum.PAPAYA;
- }
+ default boolean protectTrees() { return true; }
@ConfigItem(
keyName = "protectFruitTree",
name = "Protect fruit trees",
description = "Do you want to protect your fruit trees?",
- position = 5,
- section = generalSection
+ position = 1,
+ section = protectionSection
)
- default boolean protectFruitTrees() {
- return false;
- }
+ default boolean protectFruitTrees() { return false; }
@ConfigItem(
- keyName = "Fossil Island Tree",
- name = "Hard sapling",
- description = "Select Hard tree sapling to use",
- position = 6,
- section = generalSection
+ keyName = "protectHardTree",
+ name = "Protect Hard trees",
+ description = "Do you want to protect your hard wood ;)?",
+ position = 2,
+ section = protectionSection
)
- default HardTreeEnums selectedHardTree() {
- return HardTreeEnums.MAHOGANY;
- }
+ default boolean protectHardTrees() { return false; }
+ /* =========================
+ * Gear
+ * ========================= */
@ConfigItem(
- keyName = "protectHardTree",
- name = "Protect Hard trees",
- description = "Do you want to protect your hard wood ;) ?",
- position = 7,
- section = generalSection
+ keyName = "banking",
+ name = "Banking",
+ description = "Enabling this will run to bank and reset inventory with required items.",
+ position = 0,
+ section = gearSection
)
- default boolean protectHardTrees() {
- return false;
- }
+ default boolean banking() { return true; }
@ConfigItem(
keyName = "useCompost",
name = "Use compost",
description = "Only bottomless compost bucket is supported",
- position = 8,
- section = generalSection
+ position = 1,
+ section = gearSection
)
- default boolean useCompost() {
- return true;
- }
+ default boolean useCompost() { return true; }
@ConfigItem(
keyName = "useGraceful",
name = "Use graceful",
description = "Enable if you want to wear graceful outfit",
- position = 9,
- section = generalSection
+ position = 2,
+ section = gearSection
)
- default boolean useGraceful() {
- return true;
- }
+ default boolean useGraceful() { return true; }
@ConfigItem(
keyName = "useSkillsNecklace",
name = "Use Skills Necklace",
description = "Useful if you don't have Spirit tree or Farming cape",
- position = 10,
- section = generalSection
+ position = 3,
+ section = gearSection
)
- default boolean useSkillsNecklace() {
- return true;
- }
+ default boolean useSkillsNecklace() { return true; }
@ConfigItem(
keyName = "useEnergyPotion",
name = "Use Energy Potion",
description = "Useful if you want to have a faster run",
- position = 11,
- section = generalSection
+ position = 4,
+ section = gearSection
)
- default boolean useEnergyPotion() {
- return true;
- }
+ default boolean useEnergyPotion() { return true; }
+ /* =========================
+ * Tree patches (regular) — ordered to match run:
+ * GS Tree → Farming Guild Tree → Taverley → Falador → Lumbridge → Varrock → Auburnvale
+ * ========================= */
@ConfigItem(
- keyName = "falador",
- name = "Falador",
- description = "Falador tree patch",
+ keyName = "gnomeStrongholdTree",
+ name = "Gnome Stronghold",
+ description = "Gnome Stronghold tree patch",
position = 0,
section = treePatchesSection
)
- default boolean faladorTreePatch() {
- return true;
- }
+ default boolean gnomeStrongholdTreePatch() { return true; }
@ConfigItem(
- keyName = "gnomeStrongholdTree",
- name = "Gnome Stronghold",
- description = "Gnome Stronghold tree patch",
+ keyName = "farmingGuildTree",
+ name = "Farming Guild",
+ description = "FarmingGuild tree patch",
position = 1,
section = treePatchesSection
)
- default boolean gnomeStrongholdTreePatch() {
- return true;
- }
+ default boolean farmingGuildTreePatch() { return true; }
@ConfigItem(
- keyName = "lumbridge",
- name = "Lumbridge",
- description = "Lumbridge tree patch",
+ keyName = "taverley",
+ name = "Taverley",
+ description = "Taverley tree patch",
position = 2,
section = treePatchesSection
)
- default boolean lumbridgeTreePatch() {
- return true;
- }
+ default boolean taverleyTreePatch() { return true; }
@ConfigItem(
- keyName = "taverley",
- name = "Taverley",
- description = "Taverley tree patch",
+ keyName = "falador",
+ name = "Falador",
+ description = "Falador tree patch",
position = 3,
section = treePatchesSection
)
- default boolean taverleyTreePatch() {
- return true;
- }
+ default boolean faladorTreePatch() { return true; }
@ConfigItem(
- keyName = "varrock",
- name = "Varrock",
- description = "Varrock tree patch",
+ keyName = "lumbridge",
+ name = "Lumbridge",
+ description = "Lumbridge tree patch",
position = 4,
section = treePatchesSection
)
- default boolean varrockTreePatch() {
- return true;
- }
+ default boolean lumbridgeTreePatch() { return true; }
@ConfigItem(
- keyName = "fossil",
- name = "Fossil Island",
- description = "Fossil Island tree patch x3",
- position = 4,
+ keyName = "varrock",
+ name = "Varrock",
+ description = "Varrock tree patch",
+ position = 5,
section = treePatchesSection
)
- default boolean fossilTreePatch() {
- return true;
- }
+ default boolean varrockTreePatch() { return true; }
@ConfigItem(
- keyName = "farmingGuildTree",
- name = "Farming Guild",
- description = "FarmingGuild tree patch",
- position = 5,
+ keyName = "AuburnvaleTree",
+ name = "Auburnvale",
+ description = "Auburnvale tree patch",
+ position = 6,
section = treePatchesSection
)
- default boolean farmingGuildTreePatch() {
- return true;
- }
+ default boolean auburnTreePatch() { return true; }
+ /* =========================
+ * Fruit tree patches — ordered to match run:
+ * GS Fruit → TGV Fruit → Farming Guild Fruit → Brimhaven → Catherby → Lletya → Kastori
+ * ========================= */
+ @ConfigItem(
+ keyName = "gnomeStrongholdFruitTree",
+ name = "Gnome Stronghold",
+ description = "Gnome Stronghold fruit tree patch",
+ position = 0,
+ section = fruitTreePatchesSection
+ )
+ default boolean gnomeStrongholdFruitTreePatch() { return true; }
@ConfigItem(
- keyName = "brimhaven",
- name = "Brimhaven",
- description = "Brimhaven fruit tree patch",
+ keyName = "treeGnomeVillage",
+ name = "Tree gnome village",
+ description = "Tree gnome village fruit tree patch",
position = 1,
section = fruitTreePatchesSection
)
- default boolean brimhavenFruitTreePatch() {
- return true;
- }
+ default boolean treeGnomeVillageFruitTreePatch() { return true; }
@ConfigItem(
- keyName = "catherby",
- name = "Catherby",
- description = "Catherby fruit tree patch",
+ keyName = "farmingGuildFruitTree",
+ name = "Farming Guild",
+ description = "Farming guild fruit tree patch",
position = 2,
section = fruitTreePatchesSection
)
- default boolean catherbyFruitTreePatch() {
- return true;
- }
+ default boolean farmingGuildFruitTreePatch() { return false; }
@ConfigItem(
- keyName = "gnomeStrongholdFruitTree",
- name = "Gnome Stronghold",
- description = "Gnome Stronghold fruit tree patch",
+ keyName = "brimhaven",
+ name = "Brimhaven",
+ description = "Brimhaven fruit tree patch",
position = 3,
section = fruitTreePatchesSection
)
- default boolean gnomeStrongholdFruitTreePatch() {
- return true;
- }
+ default boolean brimhavenFruitTreePatch() { return true; }
@ConfigItem(
- keyName = "treeGnomeVillage",
- name = "Tree gnome village",
- description = "Tree gnome village tree patch",
+ keyName = "catherby",
+ name = "Catherby",
+ description = "Catherby fruit tree patch",
position = 4,
section = fruitTreePatchesSection
)
- default boolean treeGnomeVillageFruitTreePatch() {
- return true;
- }
+ default boolean catherbyFruitTreePatch() { return true; }
@ConfigItem(
keyName = "lletya",
@@ -325,18 +317,35 @@ default boolean treeGnomeVillageFruitTreePatch() {
position = 5,
section = fruitTreePatchesSection
)
- default boolean lletyaFruitTreePatch() {
- return false;
- }
+ default boolean lletyaFruitTreePatch() { return false; }
@ConfigItem(
- keyName = "farmingGuildFruitTree",
- name = "Farming Guild",
- description = "Farming guild fruit tree patch",
+ keyName = "kastoriFruitTreePatch",
+ name = "Kastori",
+ description = "Enable Kastori fruit tree patch",
position = 6,
section = fruitTreePatchesSection
)
- default boolean farmingGuildFruitTreePatch() {
- return false;
- }
+ default boolean kastoriFruitTreePatch() { return true; }
+
+ /* =========================
+ * Hardwood patches — run uses Fossil Island (x3) then Avium Savannah
+ * ========================= */
+ @ConfigItem(
+ keyName = "fossil",
+ name = "Fossil Island",
+ description = "Fossil Island tree patch x3",
+ position = 0,
+ section = hardTreePatchesSection
+ )
+ default boolean fossilTreePatch() { return true; }
+
+ @ConfigItem(
+ keyName = "aviumSavannahHardwood",
+ name = "Avium Savannah",
+ description = "Enable this hardwood tree patch",
+ position = 1,
+ section = hardTreePatchesSection
+ )
+ default boolean aviumSavannahHardwoodPatch() { return false; }
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/farmTreeRun/FarmTreeRunOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/farmTreeRun/FarmTreeRunOverlay.java
index 1162ce386a0..8d1433ce59c 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/farmTreeRun/FarmTreeRunOverlay.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/farmTreeRun/FarmTreeRunOverlay.java
@@ -23,7 +23,7 @@ public Dimension render(Graphics2D graphics) {
try {
panelComponent.setPreferredSize(new Dimension(200, 300));
panelComponent.getChildren().add(TitleComponent.builder()
- .text("Acun's farm tree runner (v1.3.0)")
+ .text("Acun's farm tree runner (v1.3.1)")
.color(Color.GREEN)
.build());
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/farmTreeRun/FarmTreeRunPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/farmTreeRun/FarmTreeRunPlugin.java
index a6f2fa3f04e..c5d7c77171d 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/farmTreeRun/FarmTreeRunPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/farmTreeRun/FarmTreeRunPlugin.java
@@ -24,7 +24,7 @@
*/
@PluginDescriptor(
name = PluginDescriptor.Default + "Farm tree runner",
- description = "Acun's farm tree runner. Supports regular and fruit trees",
+ description = "Acun's farm tree runner. Supports regular, fruit and hardwood trees",
tags = {"Farming", "Tree run"},
enabledByDefault = false
)
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/farmTreeRun/FarmTreeRunScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/farmTreeRun/FarmTreeRunScript.java
index 9d0ba9e26a4..f8e524e8585 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/farmTreeRun/FarmTreeRunScript.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/farmTreeRun/FarmTreeRunScript.java
@@ -4,8 +4,6 @@
import lombok.RequiredArgsConstructor;
import net.runelite.api.*;
import net.runelite.api.coords.WorldPoint;
-import net.runelite.client.Notifier;
-import net.runelite.client.config.Notification;
import net.runelite.client.plugins.microbot.farmTreeRun.enums.FarmTreeRunState;
import net.runelite.client.plugins.microbot.farmTreeRun.enums.FruitTreeEnum;
import net.runelite.client.plugins.microbot.farmTreeRun.enums.HardTreeEnums;
@@ -16,7 +14,6 @@
import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings;
import net.runelite.client.plugins.microbot.util.antiban.enums.ActivityIntensity;
import net.runelite.client.plugins.microbot.util.bank.Rs2Bank;
-import net.runelite.client.plugins.microbot.util.bank.enums.BankLocation;
import net.runelite.client.plugins.microbot.util.dialogues.Rs2Dialogue;
import net.runelite.client.plugins.microbot.util.equipment.Rs2Equipment;
import net.runelite.client.plugins.microbot.util.gameobject.Rs2GameObject;
@@ -87,7 +84,10 @@ public enum Patch {
LLETYA_FRUIT_TREE_PATCH(26579, new WorldPoint(2345, 3163, 0), TreeKind.FRUIT_TREE, 1, 0),
FOSSIL_TREE_PATCH_A(30482, new WorldPoint(3718, 3835, 0), TreeKind.HARD_TREE, 1, 0),
FOSSIL_TREE_PATCH_B(30480, new WorldPoint(3709, 3836, 0), TreeKind.HARD_TREE, 1, 0),
- FOSSIL_TREE_PATCH_C(30481, new WorldPoint(3701, 3840, 0), TreeKind.HARD_TREE, 1, 0);
+ FOSSIL_TREE_PATCH_C(30481, new WorldPoint(3701, 3840, 0), TreeKind.HARD_TREE, 1, 0),
+ AUBURNVALE_TREE_PATCH(56953, new WorldPoint(1365, 3320, 0), TreeKind.TREE, 1, 0),
+ KASTORI_FRUIT_TREE_PATCH(56955, new WorldPoint(1349, 3058, 0), TreeKind.FRUIT_TREE, 1, 12765),
+ AVIUM_SAVANNAH_HARDWOOD_PATCH(50692, new WorldPoint(1684, 2974, 0), TreeKind.HARD_TREE,1,0);
private final int id;
private final WorldPoint location;
@@ -301,9 +301,46 @@ public boolean run(FarmTreeRunConfig config) {
if (!handledPatch)
return;
}
- botStatus = FINISHED;
+ botStatus = HANDLE_AUBURNVALE_TREE_PATCH;
break;
- case FINISHED:
+
+ case HANDLE_AUBURNVALE_TREE_PATCH: {
+ patch = Patch.AUBURNVALE_TREE_PATCH;
+ if (config.auburnTreePatch()) {
+ if (walkToLocation(patch.getLocation())) {
+ handledPatch = handlePatch(config, patch);
+ }
+ if (!handledPatch) return; // stay in this state until done
+ }
+ botStatus = HANDLE_KASTORI_FRUIT_TREE_PATCH;
+ break;
+ }
+ case HANDLE_KASTORI_FRUIT_TREE_PATCH: {
+ patch = Patch.KASTORI_FRUIT_TREE_PATCH;
+ if (config.kastoriFruitTreePatch()) {
+ if (walkToLocation(patch.getLocation())) {
+ handledPatch = handlePatch(config, patch);
+ }
+ if (!handledPatch) return;
+ }
+ botStatus = HANDLE_AVIUM_SAVANNAH_HARDWOOD_PATCH;
+ break;
+ }
+
+ case HANDLE_AVIUM_SAVANNAH_HARDWOOD_PATCH: {
+ patch = Patch.AVIUM_SAVANNAH_HARDWOOD_PATCH;
+ if (config.aviumSavannahHardwoodPatch()) {
+ if (walkToLocation(patch.getLocation())) {
+ handledPatch = handlePatch(config, patch);
+ }
+ if (!handledPatch) return;
+ }
+ botStatus = FINISHED;
+ break;
+ }
+
+
+ case FINISHED:
Microbot.getClientThread().runOnClientThreadOptional(() -> {
Microbot.getClient().addChatMessage(ChatMessageType.ENGINE, "", "Tree run completed.", "Acun", false);
Microbot.getClient().addChatMessage(ChatMessageType.ENGINE, "", "Made with love by Acun.", "Acun", false);
@@ -839,7 +876,8 @@ private List getSelectedTreePatches(FarmTreeRunConfig config) {
config::lumbridgeTreePatch,
config::taverleyTreePatch,
config::varrockTreePatch,
- config::farmingGuildTreePatch
+ config::farmingGuildTreePatch,
+ config::auburnTreePatch
);
// Filter the patches to include only those that return true
@@ -853,7 +891,8 @@ private List getSelectedHardTreePatches(FarmTreeRunConfig confi
List allHardTreePatches = List.of(
config::fossilTreePatch,
config::fossilTreePatch,
- config::fossilTreePatch
+ config::fossilTreePatch,
+ config::aviumSavannahHardwoodPatch
);
// Filter the patches to include only those that return true
@@ -870,7 +909,8 @@ private List getSelectedFruitTreePatches(FarmTreeRunConfig conf
config::farmingGuildFruitTreePatch,
config::lletyaFruitTreePatch,
config::gnomeStrongholdFruitTreePatch,
- config::treeGnomeVillageFruitTreePatch
+ config::treeGnomeVillageFruitTreePatch,
+ config::kastoriFruitTreePatch
);
// Filter the patches to include only those that return true
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/farmTreeRun/enums/FarmTreeRunState.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/farmTreeRun/enums/FarmTreeRunState.java
index 9c024372da6..4febb5d3e92 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/farmTreeRun/enums/FarmTreeRunState.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/farmTreeRun/enums/FarmTreeRunState.java
@@ -45,5 +45,11 @@ public enum FarmTreeRunState {
HANDLE_FOSSIL_TREE_PATCH_C,
+ HANDLE_AUBURNVALE_TREE_PATCH,
+
+ HANDLE_KASTORI_FRUIT_TREE_PATCH,
+
+ HANDLE_AVIUM_SAVANNAH_HARDWOOD_PATCH,
+
FINISHED
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fletching/FletchingConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fletching/FletchingConfig.java
deleted file mode 100644
index c0b5b11c48e..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fletching/FletchingConfig.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package net.runelite.client.plugins.microbot.fletching;
-
-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.plugins.microbot.fletching.enums.FletchingItem;
-import net.runelite.client.plugins.microbot.fletching.enums.FletchingMaterial;
-import net.runelite.client.plugins.microbot.fletching.enums.FletchingMode;
-
-@ConfigGroup(FletchingConfig.GROUP)
-public interface FletchingConfig extends Config {
-
- String GROUP = "Fletching";
-
- @ConfigItem(
- keyName = "guide",
- name = "How to use",
- description = "How to use this plugin",
- position = 1
- )
- default String GUIDE() {
- return "Start the script at any bank (grand exchange preferably)\n" +
- "Make sure to have a bank and all the logs in your bank";
- }
-
- @ConfigSection(
- name = "General",
- description = "General",
- position = 0,
- closedByDefault = false
- )
- String generalSection = "general";
-
- @ConfigItem(
- keyName = "Mode",
- name = "Mode",
- description = "Choose your mode of fletching",
- position = 0,
- section = generalSection
- )
- default FletchingMode fletchingMode()
- {
- return FletchingMode.UNSTRUNG;
- }
- @ConfigItem(
- keyName = "Material",
- name = "Material",
- description = "Choose your material",
- position = 1,
- section = generalSection
- )
- default FletchingMaterial fletchingMaterial()
- {
- return FletchingMaterial.LOG;
- }
- @ConfigItem(
- keyName = "Item",
- name = "Item",
- description = "Choose your item",
- position = 2,
- section = generalSection
- )
- default FletchingItem fletchingItem()
- {
- return FletchingItem.SHORT;
- }
- @ConfigSection(
- name = "Antiban",
- description = "Configure antiban measures",
- position = 1,
- closedByDefault = false
- )
- String antibanSection = "antiban";
- @ConfigItem(
- keyName = "Afk",
- name = "Afk randomly",
- description = "Randomy afks between 3 and 60 seconds",
- position = 0,
- section = antibanSection
- )
- default boolean Afk()
- {
- return false;
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fletching/FletchingOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fletching/FletchingOverlay.java
deleted file mode 100644
index 098479bed7e..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fletching/FletchingOverlay.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package net.runelite.client.plugins.microbot.fletching;
-
-import net.runelite.client.plugins.microbot.Microbot;
-import net.runelite.client.ui.overlay.OverlayPanel;
-import net.runelite.client.ui.overlay.OverlayPosition;
-import net.runelite.client.ui.overlay.components.LineComponent;
-import net.runelite.client.ui.overlay.components.TitleComponent;
-
-import javax.inject.Inject;
-import java.awt.*;
-
-
-public class FletchingOverlay extends OverlayPanel {
- @Inject
- FletchingOverlay(FletchingPlugin plugin)
- {
- super(plugin);
- setPosition(OverlayPosition.TOP_LEFT);
- setNaughty();
- }
- @Override
- public Dimension render(Graphics2D graphics) {
- try {
- panelComponent.setPreferredSize(new Dimension(200, 300));
- panelComponent.getChildren().add(TitleComponent.builder()
- .text("Micro Fletcher")
- .color(Color.GREEN)
- .build());
-
- panelComponent.getChildren().add(LineComponent.builder()
- .left(Microbot.status)
- .right("Version: " + FletchingScript.version)
- .build());
- } catch(Exception ex) {
- System.out.println(ex.getMessage());
- }
- return super.render(graphics);
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fletching/FletchingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fletching/FletchingPlugin.java
deleted file mode 100644
index 23484780bc2..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fletching/FletchingPlugin.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package net.runelite.client.plugins.microbot.fletching;
-
-import com.google.inject.Provides;
-import lombok.extern.slf4j.Slf4j;
-import net.runelite.api.Client;
-import net.runelite.api.events.WidgetLoaded;
-import net.runelite.client.Notifier;
-import net.runelite.client.callback.ClientThread;
-import net.runelite.client.config.ConfigManager;
-import net.runelite.client.eventbus.Subscribe;
-import net.runelite.client.plugins.Plugin;
-import net.runelite.client.plugins.PluginDescriptor;
-import net.runelite.client.plugins.microbot.Microbot;
-import net.runelite.client.plugins.microbot.util.mouse.VirtualMouse;
-import net.runelite.client.ui.overlay.OverlayManager;
-
-import javax.inject.Inject;
-import java.awt.*;
-
-@PluginDescriptor(
- name = PluginDescriptor.Mocrosoft + "Fletcher",
- description = "Microbot fletching plugin",
- tags = {"fletching", "microbot", "skills"},
- enabledByDefault = false
-)
-@Slf4j
-public class FletchingPlugin extends Plugin {
- @Inject
- private FletchingConfig config;
-
- @Provides
- FletchingConfig provideConfig(ConfigManager configManager) {
- return configManager.getConfig(FletchingConfig.class);
- }
- @Inject
- private OverlayManager overlayManager;
- @Inject
- private FletchingOverlay fletchingOverlay;
-
- FletchingScript fletchingScript;
-
-
- @Override
- protected void startUp() throws AWTException {
- Microbot.pauseAllScripts.compareAndSet(true, false);
- if (overlayManager != null) {
- overlayManager.add(fletchingOverlay);
- }
- fletchingScript = new FletchingScript();
- fletchingScript.run(config);
- }
-
- protected void shutDown() {
- fletchingScript.shutdown();
- overlayManager.remove(fletchingOverlay);
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fletching/FletchingScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fletching/FletchingScript.java
deleted file mode 100644
index 97dcd04627c..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fletching/FletchingScript.java
+++ /dev/null
@@ -1,288 +0,0 @@
-package net.runelite.client.plugins.microbot.fletching;
-
-
-import lombok.Getter;
-import lombok.Setter;
-import net.runelite.api.*;
-import net.runelite.api.events.WidgetLoaded;
-import net.runelite.api.widgets.Widget;
-import net.runelite.client.plugins.microbot.Microbot;
-import net.runelite.client.plugins.microbot.Script;
-import net.runelite.client.plugins.microbot.fletching.enums.FletchingItem;
-import net.runelite.client.plugins.microbot.fletching.enums.FletchingMaterial;
-import net.runelite.client.plugins.microbot.fletching.enums.FletchingMode;
-import net.runelite.client.plugins.microbot.util.antiban.Rs2Antiban;
-import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings;
-import net.runelite.client.plugins.microbot.util.bank.Rs2Bank;
-import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory;
-import net.runelite.client.plugins.microbot.util.keyboard.Rs2Keyboard;
-import net.runelite.client.plugins.microbot.util.math.Rs2Random;
-import net.runelite.client.plugins.microbot.util.misc.Rs2UiHelper;
-import net.runelite.client.plugins.microbot.util.widget.Rs2Widget;
-
-import java.util.concurrent.TimeUnit;
-
-@Getter
-class ProgressiveFletchingModel {
- @Setter
- private FletchingItem fletchingItem;
- @Setter
- private FletchingMaterial fletchingMaterial;
-}
-
-public class FletchingScript extends Script {
-
- public static String version = "1.6.2";
-
- // The fletching interface widget group ID
- private static final int FLETCHING_WIDGET_GROUP_ID = 17694736;
-
- ProgressiveFletchingModel model = new ProgressiveFletchingModel();
-
- String primaryItemToFletch = "";
- String secondaryItemToFletch = "";
-
- FletchingMode fletchingMode;
-
- public void run(FletchingConfig config) {
- fletchingMode = config.fletchingMode();
- Rs2Antiban.resetAntibanSettings();
- Rs2Antiban.antibanSetupTemplates.applyFletchingSetup();
- mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> {
- try {
- if (!Microbot.isLoggedIn())
- return;
- if (!super.run()) return;
-
- if ((fletchingMode == FletchingMode.PROGRESSIVE || fletchingMode == FletchingMode.PROGRESSIVE_STRUNG)
- && model.getFletchingItem() == null) {
- calculateItemToFletch();
- }
-
-
- if (!configChecks(config)) return;
-
- if (Rs2AntibanSettings.actionCooldownActive)
- return;
-
-// if (config.Afk() && Random.random(1, 100) == 2)
-// sleep(1000, 60000);
-
- boolean hasRequirementsToFletch;
- boolean hasRequirementsToBank;
- primaryItemToFletch = fletchingMode.getItemName();
-
- if (fletchingMode == FletchingMode.PROGRESSIVE) {
- secondaryItemToFletch = (model.getFletchingMaterial().getName() + " logs").trim();
- hasRequirementsToFletch = Rs2Inventory.hasItem(primaryItemToFletch)
- && Rs2Inventory.hasItemAmount(secondaryItemToFletch, model.getFletchingItem().getAmountRequired());
- hasRequirementsToBank = !Rs2Inventory.hasItem(primaryItemToFletch)
- || !Rs2Inventory.hasItemAmount(secondaryItemToFletch, model.getFletchingItem().getAmountRequired());
- } else if (fletchingMode == FletchingMode.PROGRESSIVE_STRUNG) {
- secondaryItemToFletch = model.getFletchingMaterial().getName() + " "
- + model.getFletchingItem().getContainsInventoryName() + " (u)";
- hasRequirementsToFletch = Rs2Inventory.hasItem(primaryItemToFletch) && Rs2Inventory.hasItem(secondaryItemToFletch);
- hasRequirementsToBank = !Rs2Inventory.hasItem(primaryItemToFletch) || !Rs2Inventory.hasItem(secondaryItemToFletch);
- } else {
- secondaryItemToFletch = fletchingMode == FletchingMode.STRUNG
- ? config.fletchingMaterial().getName() + " " + config.fletchingItem().getContainsInventoryName() + " (u)"
- : (config.fletchingMaterial().getName() + " logs").trim();
- hasRequirementsToFletch = Rs2Inventory.hasItem(primaryItemToFletch)
- && Rs2Inventory.hasItemAmount(secondaryItemToFletch, config.fletchingItem().getAmountRequired());
- hasRequirementsToBank = !Rs2Inventory.hasItem(primaryItemToFletch)
- || !Rs2Inventory.hasItemAmount(secondaryItemToFletch, config.fletchingItem().getAmountRequired());
- }
-
- if (hasRequirementsToFletch) {
- fletch(config);
- }
- if (hasRequirementsToBank) {
- bankItems(config);
- }
-
- } catch (Exception ex) {
- System.out.println(ex.getMessage());
- }
- }, 0, 600, TimeUnit.MILLISECONDS);
- }
-
- private void bankItems(FletchingConfig config) {
- Rs2Bank.openBank();
-
- // Deposit items based on the fletching mode
- switch (fletchingMode) {
- case STRUNG:
- Rs2Bank.depositAll();
- break;
- case PROGRESSIVE:
- Rs2Bank.depositAll(model.getFletchingItem().getContainsInventoryName());
- calculateItemToFletch();
- secondaryItemToFletch = (model.getFletchingMaterial().getName() + " logs").trim();
- break;
- case PROGRESSIVE_STRUNG:
- Rs2Bank.depositAll();
- calculateItemToFletch();
- secondaryItemToFletch = model.getFletchingMaterial().getName() + " "
- + model.getFletchingItem().getContainsInventoryName() + " (u)";
- break;
- default:
- Rs2Bank.depositAll(config.fletchingItem().getContainsInventoryName());
- Rs2Inventory.waitForInventoryChanges(5000);
- break;
- }
-
- // Check if the primary item is available
- if (!Rs2Bank.hasItem(primaryItemToFletch) && !Rs2Inventory.hasItem(primaryItemToFletch)) {
- Rs2Bank.closeBank();
- Microbot.status = "[Shutting down] - Reason: " + primaryItemToFletch + " not found in the bank.";
- Microbot.showMessage(Microbot.status);
- shutdown();
- return;
- }
-
- // Ensure the inventory isn't full without the primary item
- if (!Rs2Inventory.hasItem(primaryItemToFletch)) {
- Rs2Bank.depositAll();
- }
-
- // Withdraw the primary item if not already in the inventory
- if (!Rs2Inventory.hasItem(primaryItemToFletch)) {
- Rs2Bank.withdrawX(primaryItemToFletch, fletchingMode.getAmount(), true);
- }
-
- // Check if the secondary item is available
- if (!Rs2Bank.hasItem(secondaryItemToFletch)) {
- if (fletchingMode == FletchingMode.UNSTRUNG_STRUNG && Rs2Bank.hasBankItem("bow string")) {
- Rs2Bank.depositAll();
- fletchingMode = FletchingMode.STRUNG;
- return;
- }
- Rs2Bank.closeBank();
- Microbot.status = "[Shutting down] - Reason: " + secondaryItemToFletch + " not found in the bank.";
- Microbot.showMessage(Microbot.status);
- shutdown();
- return;
- }
-
- // Withdraw the secondary item if not already in the inventory
- if (!Rs2Inventory.hasItem(secondaryItemToFletch)) {
- if (fletchingMode == FletchingMode.STRUNG) {
- Rs2Bank.withdrawDeficit(secondaryItemToFletch, fletchingMode.getAmount());
- } else {
- Rs2Bank.withdrawAll(secondaryItemToFletch);
- }
- }
- if (Rs2AntibanSettings.naturalMouse) {
- // Testing if completing the mouse movement before the final item check improves the overall flow.
- // This should allow time for the inventory to update while the mouse is moving.
- // Enhances the bot's behavior to appear more natural and less automated.
- Widget closeButton = Rs2Widget.getWidget(786434).getChild(11);
- Point closePoint = Rs2UiHelper.getClickingPoint(closeButton != null ? closeButton.getBounds() : null, true);
- Rs2Random.waitEx(200, 100);
- Microbot.naturalMouse.moveTo(closePoint.getX(), closePoint.getY());
- }
-
- // Final check to ensure both items are in the inventory
- if (!Rs2Inventory.hasItem(primaryItemToFletch) || !Rs2Inventory.hasItem(secondaryItemToFletch)) {
- Microbot.log("waiting for inventory changes.");
- Rs2Inventory.waitForInventoryChanges(5000);
- }
-
- Rs2Random.waitEx(200, 100);
- Rs2Bank.closeBank();
- }
-
-
- private void fletch(FletchingConfig config) {
- Rs2Inventory.combineClosest(primaryItemToFletch, secondaryItemToFletch);
- sleepUntil(() -> Rs2Widget.getWidget(FLETCHING_WIDGET_GROUP_ID) != null, 5000);
- char option;
- if (fletchingMode == FletchingMode.PROGRESSIVE || fletchingMode == FletchingMode.PROGRESSIVE_STRUNG) {
-
- option = model.getFletchingItem().getOption(model.getFletchingMaterial(), fletchingMode);
- Rs2Keyboard.keyPress(option);
- } else {
- option = config.fletchingItem().getOption(config.fletchingMaterial(), fletchingMode);
- Rs2Keyboard.keyPress(option);
- }
-
- sleepUntil(() -> !Rs2Inventory.hasItem(secondaryItemToFletch), 60000);
- Rs2Antiban.actionCooldown();
- Rs2Antiban.takeMicroBreakByChance();
- Rs2Bank.preHover();
- }
-
- private boolean configChecks(FletchingConfig config) {
- if (config.fletchingMaterial() == FletchingMaterial.REDWOOD && config.fletchingItem() != FletchingItem.SHIELD) {
- Microbot.getNotifier().notify("[Wrong Configuration] You can only make shields with redwood logs.");
- shutdown();
- return false;
- }
- return true;
- }
-
- public void calculateItemToFletch() {
- int level = Microbot.getClient().getRealSkillLevel(Skill.FLETCHING);
- FletchingItem item = null;
- FletchingMaterial material = null;
-
-
-
- if (fletchingMode == FletchingMode.PROGRESSIVE_STRUNG && level < 5) {
- Microbot.showMessage("Can't String Bows Below Level 5");
- shutdown();
- return;
- }
- if (level < 5) {
- item = FletchingItem.ARROW_SHAFT;
- material = FletchingMaterial.LOG;
- } else if (level < 10) {
- item = FletchingItem.SHORT;
- material = (fletchingMode == FletchingMode.PROGRESSIVE) ? FletchingMaterial.LOG : FletchingMaterial.WOOD;
- } else if (level < 20) {
- item = FletchingItem.LONG;
- material = (fletchingMode == FletchingMode.PROGRESSIVE) ? FletchingMaterial.LOG : FletchingMaterial.WOOD;
- } else if (level < 25) {
- item = FletchingItem.SHORT;
- material = FletchingMaterial.OAK;
- } else if (level < 35) {
- item = FletchingItem.LONG;
- material = FletchingMaterial.OAK;
- } else if (level < 40) {
- item = FletchingItem.SHORT;
- material = FletchingMaterial.WILLOW;
- } else if (level < 50) {
- item = FletchingItem.LONG;
- material = FletchingMaterial.WILLOW;
- } else if (level < 55) {
- item = FletchingItem.SHORT;
- material = FletchingMaterial.MAPLE;
- } else if (level < 65) {
- item = FletchingItem.LONG;
- material = FletchingMaterial.MAPLE;
- } else if (level < 70) {
- item = FletchingItem.SHORT;
- material = FletchingMaterial.YEW;
- } else if (level < 80) {
- item = FletchingItem.LONG;
- material = FletchingMaterial.YEW;
- } else if (level < 85) {
- item = FletchingItem.SHORT;
- material = FletchingMaterial.MAGIC;
- } else {
- item = FletchingItem.LONG;
- material = FletchingMaterial.MAGIC;
- }
-
- model.setFletchingItem(item);
- model.setFletchingMaterial(material);
- }
-
-
- @Override
- public void shutdown() {
-
- Rs2Antiban.resetAntibanSettings();
- super.shutdown();
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fletching/enums/FletchingItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fletching/enums/FletchingItem.java
deleted file mode 100644
index d8e0aa125ec..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fletching/enums/FletchingItem.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package net.runelite.client.plugins.microbot.fletching.enums;
-
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-
-@Getter
-@RequiredArgsConstructor
-public enum FletchingItem
-{
- ARROW_SHAFT("Arrow shaft", '1', "arrow shaft", 1),
- SHORT("Short bows", '2', "shortbow", 1),
- LONG("Long bows", '3', "longbow", 1),
- STOCK("Crossbow stock", '4', "stock", 1),
- SHIELD("Shield", '5', "shield", 2);
-
- private final String name;
- private final char option;
- private final String containsInventoryName;
- private final int amountRequired;
-
- @Override
- public String toString()
- {
- return name;
- }
-
- public char getOption(FletchingMaterial material, FletchingMode fletchingMode) {
- if (fletchingMode == FletchingMode.STRUNG
- || fletchingMode == FletchingMode.PROGRESSIVE_STRUNG) {
- return '1';
- }
- if (material == FletchingMaterial.LOG && option == '2') return '3';
- if (material == FletchingMaterial.LOG && option == '3') return '4';
- //redwood is an exception
- if (material == FletchingMaterial.REDWOOD)
- return '2';
- return option;
- }
-}
\ No newline at end of file
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fletching/enums/FletchingMaterial.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fletching/enums/FletchingMaterial.java
deleted file mode 100644
index 0bdc7c49a37..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fletching/enums/FletchingMaterial.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package net.runelite.client.plugins.microbot.fletching.enums;
-
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-
-@Getter
-@RequiredArgsConstructor
-public enum FletchingMaterial
-{
- LOG(""),
- WOOD("Wood"),
- OAK("Oak"),
- WILLOW("Willow"),
- MAPLE("Maple"),
- YEW("Yew"),
- MAGIC("Magic"),
- REDWOOD("Redwood");
-
- private final String name;
-
-
- @Override
- public String toString()
- {
- return name;
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fletching/enums/FletchingMode.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fletching/enums/FletchingMode.java
deleted file mode 100644
index 39ba42e828f..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fletching/enums/FletchingMode.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package net.runelite.client.plugins.microbot.fletching.enums;
-
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-
-@Getter
-@RequiredArgsConstructor
-public enum FletchingMode {
- UNSTRUNG("Cutting", "knife", 1),
- STRUNG("Stringing", "bow string", 14),
- PROGRESSIVE_STRUNG("Progressive Bow Stringing", "bow string", 14),
- UNSTRUNG_STRUNG("Cutting & Stringing", "knife", 1),
- PROGRESSIVE("Progressive Logs Cutting", "knife", 1);
-
-
- private final String name;
- private final String itemName;
- private final int amount;
-
- @Override
- public String toString() {
- return name;
- }
-}
\ No newline at end of file
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/frosty/frostyrc/RcPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/frosty/frostyrc/RcPlugin.java
index 8453221403b..fadfa51a1aa 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/frosty/frostyrc/RcPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/frosty/frostyrc/RcPlugin.java
@@ -57,7 +57,7 @@ RcConfig provideConfig(ConfigManager configManager) {
@Getter
private WorldPoint myWorldPoint;
@Getter
- public static String version = "v1.1.0";
+ public static String version = "v1.1.2";
@Subscribe
public void onGameObjectSpawned(GameObjectSpawned event) {
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/frosty/frostyrc/RcScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/frosty/frostyrc/RcScript.java
index f4fe9510d86..4e39cd052bf 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/frosty/frostyrc/RcScript.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/frosty/frostyrc/RcScript.java
@@ -48,6 +48,8 @@ public class RcScript extends Script {
private final WorldPoint outsideWrathRuins = new WorldPoint(2445, 2818, 0);
private final WorldPoint wrathRuinsLoc = new WorldPoint(2445, 2824, 0);
+ private volatile boolean forceDrinkAtFerox = false;
+
public static final int pureEss = 7936;
public static final int feroxPool = 39651;
public static final int monasteryRegion = 10290;
@@ -99,6 +101,7 @@ public boolean run() {
try {
if (!Microbot.isLoggedIn()) return;
if (!super.run()) return;
+ if (shouldPauseForBreak()) return;
long startTime = System.currentTimeMillis();
if (lumbyElite == -1) {
@@ -158,6 +161,23 @@ public void shutdown() {
//Rs2Player.logout();
}
+ private boolean shouldPauseForBreak() {
+ if (!plugin.isBreakHandlerEnabled()) {
+ return false;
+ }
+
+ if (BreakHandlerScript.isBreakActive()) {
+ return true;
+ }
+
+ if (BreakHandlerScript.breakIn <= 0) {
+ BreakHandlerScript.setLockState(false);
+ return true;
+ }
+
+ return false;
+ }
+
private void checkPouches() {
Rs2Inventory.interact(colossalPouch, "Check");
sleepGaussian(900, 200);
@@ -174,18 +194,22 @@ private void handleBanking() {
}
}
+ if (plugin.isBreakHandlerEnabled()) {
+ BreakHandlerScript.setLockState(true);
+ }
+
Rs2Tab.switchToInventoryTab();
+ if (Rs2Inventory.hasDegradedPouch()) {
+ Rs2Magic.repairPouchesWithLunar();
+ sleepGaussian(900, 200);
+ return;
+ }
+
if (Rs2Inventory.anyPouchUnknown()) {
checkPouches();
}
- if (Rs2Inventory.hasDegradedPouch()) {
- Rs2Magic.repairPouchesWithLunar();
- sleepGaussian(900, 200);
- return;
- }
-
if (Rs2Inventory.isFull() && Rs2Inventory.allPouchesFull() && Rs2Inventory.contains(pureEss)) {
Microbot.log("We are full, skipping bank");
state = State.GOING_HOME;
@@ -195,10 +219,6 @@ private void handleBanking() {
handleFeroxRunEnergy();
}
- if (plugin.isBreakHandlerEnabled()) {
- BreakHandlerScript.setLockState(false);
- }
-
while (!Rs2Bank.isOpen() && isRunning() &&
(!Rs2Inventory.allPouchesFull()
|| !Rs2Inventory.contains(colossalPouch)
@@ -330,8 +350,9 @@ private void handleFillPouch() {
}
private void handleFeroxRunEnergy() {
- if (Rs2Player.getRunEnergy() < 45) {
- Microbot.log("We are thirsty...let us Drink");
+ if (forceDrinkAtFerox || Rs2Player.getRunEnergy() <= 15 || Rs2Player.getHealthPercentage() <= 20) {
+ Microbot.log("We are thirsty...let us Drink");
+ forceDrinkAtFerox = true;
if (plugin.getMyWorldPoint().distanceTo(feroxPoolWp) > 5) {
Microbot.log("Walking to Ferox pool");
Rs2Walker.walkTo(feroxPoolWp);
@@ -344,6 +365,7 @@ private void handleFeroxRunEnergy() {
}
sleepUntil(() -> (!Rs2Player.isInteracting()) && !Rs2Player.isAnimating() && Rs2Player.getRunEnergy() > 90);
sleepGaussian(1100, 200);
+ forceDrinkAtFerox = false;
}
}
@@ -452,11 +474,18 @@ private void handleWrathWalking() {
BreakHandlerScript.setLockState(true);
}
+ if (Rs2Bank.isOpen()) { Rs2Bank.closeBank(); }
+
if (Rs2Inventory.contains(mythCape)) {
Microbot.log("Interacting with myth cape");
Rs2Inventory.interact(mythCape, "Teleport");
sleepUntil(() -> plugin.getMyWorldPoint().getRegionID() == mythicStatueRegion);
- sleepGaussian(1100, 200);
+ sleepGaussian(600, 200);
+
+ GameObject statue = Rs2GameObject.get("Mythic Statue");
+ if (statue != null && !Rs2Player.isAnimating()) {
+ Rs2GameObject.interact(statue, "Teleport");
+ }
if (plugin.getMyWorldPoint().getRegionID() == mythicStatueRegion) {
Microbot.log("Walking to Wrath ruins");
@@ -707,6 +736,16 @@ private void handleCrafting() {
Microbot.log("Crafting runes");
handleEmptyPouch();
}
+
+ handleFeroxRunEnergy();
+
+ if (plugin.isBreakHandlerEnabled()) {
+ BreakHandlerScript.setLockState(false);
+ if (BreakHandlerScript.isBreakActive() || BreakHandlerScript.breakIn <= 0) {
+ return;
+ }
+ }
+
state = State.BANKING;
}
@@ -731,7 +770,13 @@ private void handleBankTeleport() {
Rs2Tab.switchToEquipmentTab();
sleepGaussian(1300, 200);
- List bankTeleport = Arrays.asList(
+ boolean needRefill = (forceDrinkAtFerox || Rs2Player.getRunEnergy() <= 15 || Rs2Player.getHealthPercentage() <= 20);
+ List bankTeleport = needRefill
+ ? Arrays.asList(
+ Teleports.FEROX_ENCLAVE,
+ Teleports.CRAFTING_CAPE,
+ Teleports.FARMING_CAPE)
+ : Arrays.asList(
Teleports.CRAFTING_CAPE,
Teleports.FARMING_CAPE,
Teleports.FEROX_ENCLAVE
@@ -745,6 +790,10 @@ private void handleBankTeleport() {
Rs2Equipment.interact(bankTeleportsId, teleport.getInteraction());
sleepUntil(() -> teleport.matchesRegion(plugin.getMyWorldPoint().getRegionID()));
sleepGaussian(1100, 200);
+ if (teleport == Teleports.FEROX_ENCLAVE) {
+ forceDrinkAtFerox = true;
+ handleFeroxRunEnergy();
+ }
teleportUsed = true;
break;
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/gabplugs/sandminer/GabulhasSandMinerScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/gabplugs/sandminer/GabulhasSandMinerScript.java
index 48eb030a10b..2fde9ed3f09 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/gabplugs/sandminer/GabulhasSandMinerScript.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/gabplugs/sandminer/GabulhasSandMinerScript.java
@@ -83,6 +83,11 @@ private void miningLoop(GabulhasSandMinerConfig config) {
sleep(100, 4000);
}
while (!Rs2Inventory.isFull() && super.isRunning()) {
+ // Drop empty waterskins if not using humidify (only when idle)
+ if (!config.useHumidify() && !Rs2Player.isAnimating()) {
+ dropEmptyWaterskins();
+ }
+
while (Rs2Player.hopIfPlayerDetected(1, 3000, 100) && super.isRunning()) {
sleepUntil(() -> Microbot.getClient().getGameState() == GameState.HOPPING);
sleepUntil(() -> Microbot.getClient().getGameState() == GameState.LOGGED_IN);
@@ -131,6 +136,12 @@ private void humidifyIfNeeded() {
}
}
+ private void dropEmptyWaterskins() {
+ while (Rs2Inventory.hasItem(ItemID.WATER_SKIN0)) {
+ Rs2Inventory.drop(ItemID.WATER_SKIN0);
+ }
+ }
+
private void deposit(GabulhasSandMinerConfig config) {
if (!config.turboMode()) Rs2Walker.walkTo(grinder);
GameObject sandstoneRock = Rs2GameObject.findObject(26199, grinder);
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/github/GithubConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/github/GithubConfig.java
deleted file mode 100644
index 07f51a1616d..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/github/GithubConfig.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package net.runelite.client.plugins.microbot.github;
-
-import net.runelite.client.config.Config;
-import net.runelite.client.config.ConfigGroup;
-import net.runelite.client.config.ConfigInformation;
-import net.runelite.client.config.ConfigItem;
-
-@ConfigGroup(GithubConfig.GROUP)
-@ConfigInformation(
- "• Loads jar files from github repository."
-)
-public interface GithubConfig extends Config {
- String GROUP = "GithubPlugin";
-
-
- @ConfigItem(
- keyName = "repoUrls",
- name = "My repoUrls",
- description = "Comma-separated list of options"
- )
- default String repoUrls()
- {
- return "https://github.com/chsami/microbot";
- }
-
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/github/GithubDownloader.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/github/GithubDownloader.java
deleted file mode 100644
index 475f5bac96f..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/github/GithubDownloader.java
+++ /dev/null
@@ -1,227 +0,0 @@
-package net.runelite.client.plugins.microbot.github;
-
-import lombok.extern.slf4j.Slf4j;
-import net.runelite.client.RuneLite;
-import net.runelite.client.plugins.microbot.github.models.FileInfo;
-import net.runelite.client.plugins.microbot.github.models.GithubRepoInfo;
-import org.jetbrains.annotations.Nullable;
-import org.json.JSONArray;
-import org.json.JSONObject;
-
-import javax.swing.*;
-import java.io.*;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.net.URLEncoder;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Downloads files from a GitHub repository using the GitHub API.
- */
-@Slf4j
-public class GithubDownloader {
- // GitHub API URL format: replace {owner}, {repo}, and {path}
- private static final String GITHUB_API_URL = "https://api.github.com/repos/%s/%s/contents/%s";
-
-
- /**
- * Makes an HTTP GET request and returns the response as a String.
- */
- private static String get(String urlStr, String token) throws Exception {
- URL url = new URL(urlStr);
- HttpURLConnection connection = (HttpURLConnection) url.openConnection();
- // Set headers to ensure GitHub returns JSON in the v3 format
- connection.setRequestProperty("Accept", "application/vnd.github.v3+json");
- connection.setRequestProperty("User-Agent", "Java-GitHubDownloader");
- if (!token.isEmpty()) {
- connection.setRequestProperty("Authorization", "Bearer " + token);
- }
-
- BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
- StringBuilder response = new StringBuilder();
- String line;
- while ((line = reader.readLine()) != null) {
- response.append(line);
- }
- reader.close();
- return response.toString();
- }
-
- /**
- * Downloads a file from the given URL and saves it to the specified destination.
- */
- public static void downloadFile(String downloadUrl) throws Exception {
- URL url = new URL(downloadUrl);
- HttpURLConnection connection = (HttpURLConnection) url.openConnection();
- connection.setRequestProperty("User-Agent", "Java-GitHubDownloader");
- String filename = url.toString().substring(url.toString().lastIndexOf('/') + 1);
- InputStream in = connection.getInputStream();
- OutputStream out = new FileOutputStream(Paths.get(RuneLite.RUNELITE_DIR + "/microbot-plugins/" + filename).toString());
-
- byte[] buffer = new byte[4096];
- int bytesRead;
- while ((bytesRead = in.read(buffer)) != -1) {
- out.write(buffer, 0, bytesRead);
- }
-
- in.close();
- out.close();
- }
-
- /**
- * Fetches the files in a GitHub repository folder.
- *
- * @param url
- * @param folder
- * @return
- */
- public static String fetchFiles(String url, String folder, String token) {
- try {
- GithubRepoInfo repoInfo = new GithubRepoInfo(url);
- String apiUrl = String.format(GITHUB_API_URL, repoInfo.getOwner(), repoInfo.getRepo(), folder);
-
- HttpURLConnection conn = (HttpURLConnection) new URL(apiUrl).openConnection();
- conn.setRequestProperty("Accept", "application/vnd.github.v3+json");
- if (!token.isEmpty()) {
- conn.setRequestProperty("Authorization", "Bearer " + token);
- }
-
- checkRateLimit(conn);
- String json = new String(conn.getInputStream().readAllBytes());
-
- return json;
- } catch (Exception ex) {
- log.error(ex.getMessage());
- }
- return "";
- }
-
- @Nullable
- private static String checkRateLimit(HttpURLConnection conn) throws IOException {
- int responseCode = conn.getResponseCode();
- if (responseCode == 403) {
- // read the error stream and show it
- String errorMsg = readStream(conn.getErrorStream());
- JOptionPane.showConfirmDialog(null, "GitHub API error (403):\n" + errorMsg, "Error",
- JOptionPane.DEFAULT_OPTION);
- }
- return "";
- }
-
- private static String readStream(InputStream stream) {
- if (stream == null) return "No error message received.";
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) {
- StringBuilder sb = new StringBuilder();
- String line;
- while ((line = reader.readLine()) != null) {
- sb.append(line).append("\n");
- }
- return sb.toString().trim();
- } catch (IOException e) {
- return "Failed to read error message: " + e.getMessage();
- }
- }
-
- /**
- * Checks if the repository is too large to download.
- *
- * @param url
- * @return
- */
- public static boolean isLargeRepo(String url, String token) {
- try {
- var githubRepoInfo = new GithubRepoInfo(url);
-
- String apiUrl = String.format("https://api.github.com/repos/%s/%s",
- URLEncoder.encode(githubRepoInfo.getOwner(), "UTF-8"), URLEncoder.encode(githubRepoInfo.getRepo(), "UTF-8"));
-
- HttpURLConnection conn = (HttpURLConnection) new URL(apiUrl).openConnection();
- conn.setRequestProperty("Accept", "application/vnd.github.v3+json");
- if (!token.isEmpty()) {
- conn.setRequestProperty("Authorization", "Bearer " + token);
- }
-
- if (conn.getResponseCode() != 200) {
- return false; // can't determine size, assume it's fine
- }
-
- checkRateLimit(conn);
- String json = new String(conn.getInputStream().readAllBytes());
- JSONObject obj = new JSONObject(json);
- int sizeKB = obj.getInt("size"); // GitHub gives size in kilobytes
- double sizeMB = sizeKB / 1024.0;
-
- if (sizeMB > 50) {
- return true;
- }
- } catch (Exception e) {
- log.error("Failed to check repo size: " + e.getMessage());
- }
- return false;
- }
-
- /**
- * Fetches all files in a GitHub repository folder recursively.
- *
- * @param url
- * @param path
- * @return
- */
- public static List getAllFilesRecursively(String url, String path, String token) {
- List files = new ArrayList<>();
- fetchRecursive(url, path, token, files);
- return files;
- }
-
- /**
- * Fetches all files in a GitHub repository folder recursively.
- *
- * @param url
- * @param path
- * @param files
- */
- public static void fetchRecursive(String url, String path, String token, List files) {
- try {
- GithubRepoInfo repoInfo = new GithubRepoInfo(url);
- String apiUrl = String.format("https://api.github.com/repos/%s/%s/contents/%s",
- URLEncoder.encode(repoInfo.getOwner(), "UTF-8"),
- URLEncoder.encode(repoInfo.getRepo(), "UTF-8"),
- URLEncoder.encode(path, "UTF-8"));
-
- HttpURLConnection conn = (HttpURLConnection) new URL(apiUrl).openConnection();
- conn.setRequestProperty("Accept", "application/vnd.github.v3+json");
- if (!token.isEmpty()) {
- conn.setRequestProperty("Authorization", "Bearer " + token);
- }
-
- if (conn.getResponseCode() != 200) {
- System.err.println("Failed to fetch: " + apiUrl + " (HTTP " + conn.getResponseCode() + ")");
- return;
- }
-
- checkRateLimit(conn);
- String json = new String(conn.getInputStream().readAllBytes());
- JSONArray items = new JSONArray(json);
-
- for (int i = 0; i < items.length(); i++) {
- JSONObject obj = items.getJSONObject(i);
- String type = obj.getString("type");
-
- if (type.equals("file")) {
- String name = obj.getString("name");
- String downloadUrl = obj.getString("download_url");
-
- files.add(new FileInfo(name, downloadUrl));
- } else if (type.equals("dir")) {
- String subPath = obj.getString("path"); // full subfolder path
- fetchRecursive(url, subPath, token, files); // recurse
- }
- }
- } catch (Exception e) {
- log.error("Error in fetchRecursive: " + e.getMessage());
- }
- }
-
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/github/GithubPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/github/GithubPanel.java
deleted file mode 100644
index 9f4ae943782..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/github/GithubPanel.java
+++ /dev/null
@@ -1,378 +0,0 @@
-package net.runelite.client.plugins.microbot.github;
-
-import lombok.SneakyThrows;
-import net.runelite.client.RuneLite;
-import net.runelite.client.config.ConfigManager;
-import net.runelite.client.plugins.microbot.externalplugins.MicrobotPluginManager;
-import net.runelite.client.plugins.microbot.github.models.FileInfo;
-import net.runelite.client.ui.ColorScheme;
-import net.runelite.client.ui.PluginPanel;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import javax.inject.Inject;
-import javax.swing.*;
-import java.awt.*;
-import java.io.File;
-import java.io.IOException;
-import java.util.*;
-import java.util.List;
-import java.util.stream.Collectors;
-
-import static javax.swing.JOptionPane.showMessageDialog;
-
-public class GithubPanel extends PluginPanel {
-
- private final JComboBox repoDropdown = new JComboBox();
- private final JTextField folderField = new JTextField("", 10);
- private final JTextField tokenField = new JTextField("", 10);
-
- private final DefaultListModel listModel = new DefaultListModel();
- private final JList fileList = new JList<>(listModel);
-
- @Inject
- MicrobotPluginManager microbotPluginManager;
-
- @Inject
- ConfigManager configManager;
-
- GithubPlugin plugin;
-
- @Inject
- public GithubPanel(GithubPlugin plugin) {
- this.plugin = plugin;
-
- // Top panel for inputs
- // Keep BoxLayout
- JPanel inputPanel = new JPanel(new GridBagLayout());
-
- GridBagConstraints gbc = new GridBagConstraints();
-
-
- gbc.insets = new Insets(2, 2, 2, 2); // Add some padding
- gbc.fill = GridBagConstraints.HORIZONTAL;
- gbc.weightx = 1.0;
- gbc.gridwidth = GridBagConstraints.REMAINDER;
- gbc.anchor = GridBagConstraints.WEST;
-
- GridBagConstraints gbci = new GridBagConstraints();
-
-
- gbci.insets = new Insets(10, 2, 10, 2); // Add some padding
- gbci.fill = GridBagConstraints.HORIZONTAL;
- gbci.weightx = 1.0;
- gbci.gridwidth = GridBagConstraints.REMAINDER;
- gbci.anchor = GridBagConstraints.WEST;
-
- inputPanel.add(new JLabel("Repo Url:*"), gbc);
- repoDropdown.setBorder(BorderFactory.createLineBorder(ColorScheme.BRAND_ORANGE));
- inputPanel.add(repoDropdown, gbci);
- for (String option : getOptionsList()) {
- repoDropdown.addItem(option);
- }
-
-
- inputPanel.add(new JLabel("Folder: (empty = root folder)"), gbc);
- folderField.setBorder(BorderFactory.createLineBorder(ColorScheme.BRAND_ORANGE));
-
- inputPanel.add(folderField, gbci);
-
- inputPanel.add(new JLabel("Token:"), gbc);
- tokenField.setBorder(BorderFactory.createLineBorder(ColorScheme.BRAND_ORANGE));
- inputPanel.add(tokenField, gbci);
- inputPanel.add(new JLabel(""), gbci);
-
-
- // Button panel
- JPanel buttonPanel = new JPanel(new GridLayout(5, 1, 10, 10));
- JButton addRepoButton = new JButton("Add Repo Url");
- addRepoButton.setBorder(BorderFactory.createLineBorder(ColorScheme.BRAND_ORANGE));
- JButton deleteRepoButton = new JButton("Delete Repo Url");
- deleteRepoButton.setBorder(BorderFactory.createLineBorder(ColorScheme.PROGRESS_ERROR_COLOR));
- JButton fetchButton = new JButton("Fetch from GitHub");
- fetchButton.setBorder(BorderFactory.createLineBorder(ColorScheme.BRAND_ORANGE));
- JButton downloadButton = new JButton("Download Selected");
- downloadButton.setBorder(BorderFactory.createLineBorder(ColorScheme.BRAND_ORANGE));
- JButton downloadAllButton = new JButton("Download All");
- downloadAllButton.setBorder(BorderFactory.createLineBorder(ColorScheme.BRAND_ORANGE));
- JButton openMicrobotSideLoadPluginFolder = new JButton("Open folder");
- openMicrobotSideLoadPluginFolder.setBorder(BorderFactory.createLineBorder(ColorScheme.BRAND_ORANGE));
- buttonPanel.add(addRepoButton);
- buttonPanel.add(deleteRepoButton);
- buttonPanel.add(fetchButton);
- buttonPanel.add(downloadButton);
- buttonPanel.add(downloadAllButton);
- buttonPanel.add(openMicrobotSideLoadPluginFolder);
- buttonPanel.add(new JLabel(""));
-
- // Main layout
- setLayout(new BorderLayout());
- add(inputPanel, BorderLayout.NORTH);
- add(buttonPanel, BorderLayout.CENTER);
- add(new JScrollPane(fileList), BorderLayout.SOUTH);
-
-
- fileList.setCellRenderer(new DefaultListCellRenderer() {
- @Override
- public Component getListCellRendererComponent(JList> list, Object value, int index,
- boolean isSelected, boolean cellHasFocus) {
- JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
- if (value instanceof FileInfo) {
- FileInfo fileInfo = (FileInfo) value;
- File localFile = new File(RuneLite.RUNELITE_DIR, "microbot-plugins/" + fileInfo.getName());
- boolean exists = localFile.exists();
-
- if (exists) {
- label.setText("✔ " + fileInfo.getName());
- label.setForeground(Color.GREEN.darker());
- } else {
- label.setText(fileInfo.getName());
- label.setForeground(isSelected ? list.getSelectionForeground() : list.getForeground());
- }
- }
- return label;
- }
- });
-
- // Button actions
- addRepoButton.addActionListener(e -> addRepoUrl());
- deleteRepoButton.addActionListener(e -> deleteRepoUrl());
- fetchButton.addActionListener(e -> fetchFiles());
- downloadButton.addActionListener(e -> downloadSelected());
- downloadAllButton.addActionListener(e -> downloadAll());
- openMicrobotSideLoadPluginFolder.addActionListener(e -> openMicrobotSideLoadingFolder());
-
- }
-
- /**
- * Deletes a repository URL from the dropdown and saves the updated list to the configuration.
- */
- private void deleteRepoUrl() {
- String selected = (String) repoDropdown.getSelectedItem();
- if (selected != null) {
- int confirm = JOptionPane.showConfirmDialog(this,
- "Are you sure you want to delete the selected repository URL?",
- "Confirm Deletion",
- JOptionPane.YES_NO_OPTION);
-
- if (confirm == JOptionPane.YES_OPTION) {
- List currentItems = getOptionsList();
- currentItems.remove(selected);
- String updatedConfig = String.join(",", currentItems);
-
- configManager.setConfiguration("GithubPlugin", "repoUrls", updatedConfig);
- repoDropdown.removeItem(selected);
- }
- }
- }
-
- /**
- * Adds a repository URL to the dropdown and saves it to the configuration.
- */
- private void addRepoUrl() {
- String url = JOptionPane.showInputDialog(this, "Enter the repository URL:");
- if (url != null && !url.isEmpty()) {
- repoDropdown.addItem(url);
- repoDropdown.setSelectedItem(url);
- List currentItems = getOptionsList();
- currentItems.add(url);
- String updatedConfig = String.join(",", currentItems);
-
- configManager.setConfiguration("GithubPlugin", "repoUrls", updatedConfig);
- }
- }
-
- /**
- * Deletes all files in the downloads directory.
- */
- private void openMicrobotSideLoadingFolder() {
- String userHome = System.getProperty("user.home");
- File folder = new File(userHome, ".runelite/microbot-plugins");
-
- if (folder.exists()) {
- try {
- Desktop.getDesktop().open(folder);
- } catch (IOException e) {
- System.err.println("Failed to open folder: " + e.getMessage());
- }
- } else {
- System.err.println("Folder does not exist: " + folder.getAbsolutePath());
- }
- }
-
- /**
- * Downloads all files in the specified GitHub repository folder.
- */
- @SneakyThrows
- private void downloadAll() {
- if (!isRepoSelected()) return;
-
- if (listModel.isEmpty()) {
- showMessageDialog(this, "No files to download.", "Error", JOptionPane.ERROR_MESSAGE);
- return;
- }
-
- if (folderField.getText().isEmpty() && GithubDownloader.isLargeRepo(Objects.requireNonNull(repoDropdown.getSelectedItem()).toString(), tokenField.getText())) {
- int choice = JOptionPane.showConfirmDialog(this,
- "⚠ The repository is over 50MB.\nAre you sure you want to continue?",
- "Large Repository",
- JOptionPane.YES_NO_OPTION);
- if (choice != JOptionPane.YES_OPTION) {
- return;
- }
- }
- Window parentWindow = SwingUtilities.getWindowAncestor(this);
- JDialog dialog = createLoadingDialog(parentWindow);
-
- List allFiles = GithubDownloader.getAllFilesRecursively(Objects.requireNonNull(repoDropdown.getSelectedItem()).toString(), folderField.getText(), tokenField.getText());
-
- dialog.setVisible(false);
- parentWindow.remove(dialog);
- // Create progress dialog
- JDialog progressDialog = new JDialog(parentWindow, "loader message...", Dialog.ModalityType.APPLICATION_MODAL);
- JProgressBar progressBar = new JProgressBar(0, allFiles.size());
- progressBar.setStringPainted(true);
- progressDialog.add(progressBar);
- progressDialog.setSize(300, 75);
- progressDialog.setLocationRelativeTo(this);
-
- List downloadedPlugins = new ArrayList<>();
-
- // Background task
- SwingWorker worker = new SwingWorker<>() {
- @SneakyThrows
- @Override
- protected Void doInBackground() {
- for (int i = 0; i < allFiles.size(); i++) {
- FileInfo fileInfo = allFiles.get(i);
- String downloadUrl = fileInfo.getUrl();
- String fileName = fileInfo.getName();
- System.out.println("Downloading file: " + fileName);
- GithubDownloader.downloadFile(downloadUrl);
- publish(i + 1);
- downloadedPlugins.add(fileName);
- }
- return null;
- }
-
- @Override
- protected void process(List chunks) {
- int latest = chunks.get(chunks.size() - 1);
- progressBar.setValue(latest);
- }
-
- @SneakyThrows
- @Override
- protected void done() {
- progressDialog.dispose();
- fileList.repaint(); // update any downloaded indicators
- JOptionPane.showMessageDialog(parentWindow, "All files downloaded.", "Download Successful!",
- JOptionPane.INFORMATION_MESSAGE);
- }
- };
-
- worker.execute();
- progressDialog.setVisible(true); // blocks until worker finishes
- microbotPluginManager.saveInstalledPlugins(downloadedPlugins);
- microbotPluginManager.loadSideLoadPlugins();
-
- }
-
- /**
- * Checks if a repository URL is selected.
- *
- * @return
- */
- private boolean isRepoSelected() {
- if (repoDropdown.getSelectedItem() == null) {
- showMessageDialog(this, "Please select a repository URL.", "Error", JOptionPane.ERROR_MESSAGE);
- return false;
- }
- return true;
- }
-
- /**
- * Downloads the selected files in the list.
- */
- @SneakyThrows
- private void downloadSelected() {
- if (!isRepoSelected()) return;
-
- if (listModel.isEmpty()) {
- showMessageDialog(this, "No files to download.", "Error", JOptionPane.ERROR_MESSAGE);
- return;
- }
-
- List selectedFileInfoList = fileList.getSelectedValuesList();
- for (FileInfo fileInfo : selectedFileInfoList) {
- GithubDownloader.downloadFile(fileInfo.getUrl());
- }
- showMessageDialog(this, "Restart the client so the plugin(s) get shown", "Information", JOptionPane.INFORMATION_MESSAGE);
- fileList.repaint();
- }
-
- /**
- * Fetches the files in the specified GitHub repository folder and adds them to the list.
- */
- private void fetchFiles() {
- if (!isRepoSelected()) return;
- try {
- String json = GithubDownloader.fetchFiles(Objects.requireNonNull(repoDropdown.getSelectedItem()).toString(), folderField.getText(), tokenField.getText());
- JSONArray arr = new JSONArray(json);
-
- listModel.clear();
- for (int i = 0; i < arr.length(); i++) {
- JSONObject obj = arr.getJSONObject(i);
- if (obj.getString("type").equals("file") && obj.getString("name").endsWith(".jar")) {
- String fileName = obj.getString("name");
- String downloadUrl = obj.getString("download_url");
- listModel.addElement(new FileInfo(fileName, downloadUrl));
- }
- }
- if (listModel.isEmpty()) {
- showMessageDialog(this, "No jar files found in repository.", "Error", JOptionPane.ERROR_MESSAGE);
- }
- } catch (JSONException ex) {
- // show dialog box with message failed
- showMessageDialog(this, "Failed to fetch files from repository.", "Error", JOptionPane.ERROR_MESSAGE);
- }
- }
-
- /**
- * Creates a loading dialog with a progress bar.
- *
- * @param parent
- * @return
- */
- private JDialog createLoadingDialog(Window parent) {
- JDialog dialog = new JDialog(parent, "Please wait...", Dialog.ModalityType.APPLICATION_MODAL);
- dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
- dialog.setSize(300, 100);
- dialog.setLayout(new BorderLayout());
-
- JLabel label = new JLabel("Scanning Repo...", SwingConstants.CENTER);
- JProgressBar progressBar = new JProgressBar();
- progressBar.setIndeterminate(true);
-
- dialog.add(label, BorderLayout.NORTH);
- dialog.add(progressBar, BorderLayout.CENTER);
- dialog.setLocationRelativeTo(parent);
- return dialog;
- }
-
- /**
- * Gets the list of options from the configuration.
- *
- * @return
- */
- private List getOptionsList() {
- String raw = plugin.config.repoUrls();
- if (raw == null || raw.isEmpty()) {
- return Collections.emptyList();
- }
- return Arrays.stream(raw.split(","))
- .map(String::trim)
- .collect(Collectors.toList());
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/github/GithubPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/github/GithubPlugin.java
deleted file mode 100644
index f2999b28211..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/github/GithubPlugin.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package net.runelite.client.plugins.microbot.github;
-
-import com.google.inject.Provides;
-import lombok.extern.slf4j.Slf4j;
-import net.runelite.client.config.ConfigManager;
-import net.runelite.client.plugins.Plugin;
-import net.runelite.client.plugins.PluginDescriptor;
-import net.runelite.client.ui.ClientToolbar;
-import net.runelite.client.ui.NavigationButton;
-import net.runelite.client.util.ImageUtil;
-
-import javax.inject.Inject;
-import java.awt.image.BufferedImage;
-
-@PluginDescriptor(
- name = "Github plugin",
- description = "Allows to download plugins from a github and sideload them",
- tags = {"github", "microbot"},
- enabledByDefault = false,
- hidden = true
-)
-@Slf4j
-public class GithubPlugin extends Plugin {
-
- GithubPanel panel;
- NavigationButton navButton;
- @Inject
- ClientToolbar clientToolbar;
-
- @Inject
- public GithubConfig config;
-
- @Provides
- GithubConfig provideConfig(ConfigManager configManager) {
- return configManager.getConfig(GithubConfig.class);
- }
-
- @Override
- protected void startUp() {
- panel = injector.getInstance(GithubPanel.class);
- final BufferedImage icon = ImageUtil.loadImageResource(GithubPlugin.class, "github_icon.png");
- navButton = NavigationButton.builder()
- .tooltip("Github Repository")
- .icon(icon)
- .priority(8)
- .panel(panel)
- .build();
- clientToolbar.addNavigation(navButton);
- }
-
- @Override
- protected void shutDown() {
- System.out.println("Github plugin stopped");
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/github/models/FileInfo.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/github/models/FileInfo.java
deleted file mode 100644
index 9f63da55e12..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/github/models/FileInfo.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package net.runelite.client.plugins.microbot.github.models;
-
-public class FileInfo {
- private final String name;
- private final String url;
-
- public FileInfo(String name, String url) {
- this.name = name;
- this.url = url;
- }
-
- public String getName() {
- return name;
- }
-
- public String getUrl() {
- return url;
- }
-
- @Override
- public String toString() {
- return name; // this is what gets shown in the JList
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/github/models/GithubRepoInfo.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/github/models/GithubRepoInfo.java
deleted file mode 100644
index c8c148e1b8d..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/github/models/GithubRepoInfo.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package net.runelite.client.plugins.microbot.github.models;
-
-import lombok.Getter;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class GithubRepoInfo {
- @Getter
- private final String owner;
- @Getter
- private final String repo;
-
- public GithubRepoInfo(String url) {
- Pattern pattern = Pattern.compile("github\\.com/([^/]+)/([^/]+)");
- Matcher matcher = pattern.matcher(url);
- if (matcher.find()) {
- String owner = matcher.group(1);
- String repo = matcher.group(2);
- this.owner = owner;
- this.repo = repo;
- } else {
- this.owner = "";
- this.repo = "";
- }
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaConfig.java
deleted file mode 100644
index 6605ec2ea6b..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaConfig.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package net.runelite.client.plugins.microbot.magetrainingarena;
-
-import net.runelite.client.config.*;
-import net.runelite.client.plugins.microbot.magetrainingarena.enums.*;
-
-@ConfigGroup("mta")
-@ConfigInformation("- Enable the official RuneLite plugin 'Mage Training Arena'
" +
- "
" +
- "- Configure staves and tomes, make sure you can equip them.
" +
- "- Staves, Tomes, Laws, Cosmic and Nature runes in inventory only, No rune pouch!
" +
- "
" +
- "- T6 enchant requires Lava staff OR Tome of Fire and any Earth staff.
" +
- "- T5 enchant requires Tome of Water and any Earth staff, OR either water/earth runes.
"+
- "
" +
- "- When set to buy rewards, the rooms are cycled until the points are met, the reward will be stored in your bank.
" +
- "- If not set to buy rewards, the rooms are cycled as if it would buy the rewards then continues cycling the rooms afterwards.
" +
- "
" +
- "- 'All items' will get enough points for you to finish Collection Log'" +
- "
" +
- " For repeat rooms functionality to work the requirements must be met and you must be in the room you wish to repeat.")
-public interface MageTrainingArenaConfig extends Config {
- @ConfigSection(
- name = "Rooms",
- description = "Magic Training Arena Rooms",
- position = 1
- )
- String roomSection = "rooms";
-
- @ConfigItem(
- keyName = "repeatRoom",
- name = "Repeat Room",
- description = "Determines whether the bot should repeat the current room.",
- position = 0,
- section = roomSection
- )
- default boolean repeatRoom() { return false; }
-
- @ConfigSection(
- name = "Rewards",
- description = "Rewards",
- position = 2
- )
- String rewardsSection = "rewards";
-
- @ConfigSection(
- name = "Graveyard",
- description = "Graveyard",
- position = 3,
- closedByDefault = true
- )
- String graveyardSection = "graveyard";
-
- @ConfigItem(
- keyName = "Buy rewards",
- name = "Buy rewards",
- description = "Determines whether the bot should buy the selected reward.",
- position = 1,
- section = rewardsSection
- )
- default boolean buyRewards() {
- return true;
- }
-
- @ConfigItem(
- keyName = "Reward",
- name = "Reward",
- description = "The reward to aim for.",
- position = 2,
- section = rewardsSection
- )
- default Rewards reward() {
- return Rewards.BONES_TO_PEACHES;
- }
-
- @ConfigItem(
- keyName = "Healing threshold (min)",
- name = "Healing threshold (min)",
- description = "Each time the bot eats it chooses a random threshold (between min and max value) to eat at next time.",
- position = 3,
- section = graveyardSection
- )
- default int healingThresholdMin() {
- return 40;
- }
-
- @ConfigItem(
- keyName = "Healing threshold (max)",
- name = "Healing threshold (max)",
- description = "Each time the bot eats it chooses a random threshold (between min and max value) to eat at next time.",
- position = 4,
- section = graveyardSection
- )
- default int healingThresholdMax() {
- return 70;
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaOverlay.java
deleted file mode 100644
index 68ab44ac6e9..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaOverlay.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package net.runelite.client.plugins.microbot.magetrainingarena;
-
-import net.runelite.client.plugins.microbot.Microbot;
-import net.runelite.client.ui.overlay.OverlayPanel;
-import net.runelite.client.ui.overlay.OverlayPosition;
-import net.runelite.client.ui.overlay.components.*;
-
-import javax.inject.Inject;
-import java.awt.*;
-
-public class MageTrainingArenaOverlay extends OverlayPanel {
- MageTrainingArenaConfig config;
-
- @Inject
- MageTrainingArenaOverlay(MageTrainingArenaPlugin plugin, MageTrainingArenaConfig config)
- {
- super(plugin);
- setPosition(OverlayPosition.TOP_LEFT);
- setPriority(PRIORITY_HIGHEST);
- setNaughty();
-
- this.config = config;
- }
- @Override
- public Dimension render(Graphics2D graphics) {
- try {
- panelComponent.setPreferredSize(new Dimension(200, 300));
- panelComponent.getChildren().add(TitleComponent.builder()
- .text("Basche's Mage Training Arena " + MageTrainingArenaScript.version)
- .color(Color.GREEN)
- .build());
-
- panelComponent.getChildren().add(LineComponent.builder().build());
-
- if (!Microbot.getPluginManager().isActive(MageTrainingArenaScript.getMtaPlugin())){
- panelComponent.getChildren().add(LineComponent.builder()
- .left("Make sure to enable the 'Mage Training Arena' plugin!")
- .leftColor(Color.RED)
- .build());
- } else {
- panelComponent.getChildren().add(LineComponent.builder()
- .left("Room: " + (MageTrainingArenaScript.getCurrentRoom() != null ? MageTrainingArenaScript.getCurrentRoom() : "-"))
- .build());
- panelComponent.getChildren().add(LineComponent.builder()
- .left("Reward: " + config.reward())
- .build());
-
- panelComponent.getChildren().add(LineComponent.builder().build());
- for (var points : MageTrainingArenaScript.getCurrentPoints().entrySet()){
- var rewardPoints = MageTrainingArenaScript.getRequiredPoints(config).get(points.getKey());
- panelComponent.getChildren().add(LineComponent.builder()
- .left(String.format("%s: %d / %d", points.getKey(), points.getValue(), rewardPoints))
- .build());
- }
-
- panelComponent.getChildren().add(LineComponent.builder().build());
-
- double progress = 0;
- for (var points : MageTrainingArenaScript.getCurrentPoints().entrySet()){
- var rewardPoints = MageTrainingArenaScript.getRequiredPoints(config).get(points.getKey());
- progress += Math.min((double) (points.getValue() - (config.buyRewards() ? 0 : MageTrainingArenaScript.getBuyable()) * rewardPoints) / rewardPoints, 1) * 25;
- }
-
-
- if (config.buyRewards() && MageTrainingArenaScript.getBought() > 0)
- panelComponent.getChildren().add(LineComponent.builder().left("Bought: " + MageTrainingArenaScript.getBought()).build());
- else if (!config.buyRewards() && MageTrainingArenaScript.getBuyable() > 0)
- panelComponent.getChildren().add(LineComponent.builder().left("Buyable: " + MageTrainingArenaScript.getBuyable()).build());
-
- var progressBar = new ProgressBarComponent();
- progressBar.setValue(progress);
- panelComponent.getChildren().add(progressBar);
- }
- } catch(Exception ex) {
- System.out.println(ex.getMessage());
- }
- return super.render(graphics);
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaPlugin.java
deleted file mode 100644
index 091c6aeb017..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaPlugin.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package net.runelite.client.plugins.microbot.magetrainingarena;
-
-import com.google.inject.Provides;
-import lombok.extern.slf4j.Slf4j;
-import net.runelite.client.config.ConfigManager;
-import net.runelite.client.plugins.Plugin;
-import net.runelite.client.plugins.PluginDescriptor;
-import net.runelite.client.ui.overlay.OverlayManager;
-
-import javax.inject.Inject;
-import java.awt.*;
-
-@PluginDescriptor(
- name = PluginDescriptor.Basche + "Mage Training Arena",
- description = "Basche's Mage Training Arena plugin",
- tags = {"basche", "mta", "moneymaking"},
- enabledByDefault = false
-)
-@Slf4j
-public class MageTrainingArenaPlugin extends Plugin {
- @Inject
- private MageTrainingArenaConfig config;
- @Provides
- MageTrainingArenaConfig provideConfig(ConfigManager configManager) {
- return configManager.getConfig(MageTrainingArenaConfig.class);
- }
-
- @Inject
- private OverlayManager overlayManager;
- @Inject
- private MageTrainingArenaOverlay overlay;
-
- @Inject
- MageTrainingArenaScript script;
-
-
- @Override
- protected void startUp() throws AWTException {
- if (overlayManager != null) {
- overlayManager.add(overlay);
- }
-
- script.run(config);
- }
-
- protected void shutDown() {
- script.shutdown();
- overlayManager.remove(overlay);
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaScript.java
deleted file mode 100644
index d84432d0917..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/MageTrainingArenaScript.java
+++ /dev/null
@@ -1,741 +0,0 @@
-package net.runelite.client.plugins.microbot.magetrainingarena;
-
-import lombok.Getter;
-import net.runelite.api.EquipmentInventorySlot;
-import net.runelite.api.Skill;
-import net.runelite.api.gameval.*;
-import net.runelite.api.coords.LocalPoint;
-import net.runelite.api.coords.WorldPoint;
-import net.runelite.client.plugins.microbot.Microbot;
-import net.runelite.client.plugins.microbot.Script;
-import net.runelite.client.plugins.microbot.globval.enums.InterfaceTab;
-import net.runelite.client.plugins.microbot.magetrainingarena.enums.*;
-import net.runelite.client.plugins.microbot.util.bank.Rs2Bank;
-import net.runelite.client.plugins.microbot.util.camera.Rs2Camera;
-import net.runelite.client.plugins.microbot.util.dialogues.Rs2Dialogue;
-import net.runelite.client.plugins.microbot.util.equipment.Rs2Equipment;
-import net.runelite.client.plugins.microbot.util.gameobject.Rs2GameObject;
-import net.runelite.client.plugins.microbot.util.grounditem.Rs2GroundItem;
-import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory;
-import net.runelite.client.plugins.microbot.util.inventory.Rs2ItemModel;
-import net.runelite.client.plugins.microbot.util.inventory.Rs2RunePouch;
-import net.runelite.client.plugins.microbot.util.keyboard.Rs2Keyboard;
-import net.runelite.client.plugins.microbot.util.magic.*;
-import net.runelite.client.plugins.microbot.util.math.Rs2Random;
-import net.runelite.client.plugins.microbot.util.npc.Rs2Npc;
-import net.runelite.client.plugins.microbot.util.npc.Rs2NpcModel;
-import net.runelite.client.plugins.microbot.util.player.Rs2Player;
-import net.runelite.client.plugins.microbot.util.tabs.Rs2Tab;
-import net.runelite.client.plugins.microbot.util.walker.Rs2Walker;
-import net.runelite.client.plugins.microbot.util.widget.Rs2Widget;
-import net.runelite.client.plugins.mta.MTAPlugin;
-import net.runelite.client.plugins.mta.alchemy.AlchemyRoomTimer;
-import net.runelite.client.plugins.mta.telekinetic.TelekineticRoom;
-import net.runelite.client.plugins.skillcalculator.skills.MagicAction;
-
-import java.awt.event.KeyEvent;
-import java.util.*;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-import java.util.stream.StreamSupport;
-
-import static net.runelite.client.plugins.microbot.util.magic.Rs2Magic.getRs2Staff;
-import static net.runelite.client.plugins.microbot.util.magic.Rs2Magic.getRs2Tome;
-
-public class MageTrainingArenaScript extends Script {
- public static String version = "1.1.4";
-
- private static boolean firstTime = false;
-
- private static final WorldPoint portalPoint = new WorldPoint(3363, 3318, 0);
- private static final WorldPoint bankPoint = new WorldPoint(3365, 3318, 1);
-
- private MageTrainingArenaConfig config;
- private Rooms currentRoom;
- private int nextHpThreshold = 50;
- private Boolean btp = null;
- private int shapesToPick = 3;
-
- @Getter
- private static MTAPlugin mtaPlugin;
- @Getter
- private static final Map currentPoints = Arrays.stream(Points.values()).collect(Collectors.toMap(x -> x, x -> -1));
- @Getter
- private static int bought;
- @Getter
- private static int buyable;
-
- public boolean run(MageTrainingArenaConfig config) {
- this.config = config;
- Microbot.log(String.format("repeatRoom: %s", config.repeatRoom()));
- Microbot.enableAutoRunOn = true;
- bought = 0;
- buyable = 0;
- Rs2Walker.disableTeleports = true;
- mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> {
- try {
- if (!Microbot.isLoggedIn()) return;
- if (!super.run()) return;
- if (mtaPlugin != null && !Microbot.getPluginManager().isActive(mtaPlugin)) return;
-
- if (!Rs2Magic.isSpellbook(Rs2Spellbook.MODERN)) {
- Microbot.log("Wrong spellbook found...please use the modern spellbook for this script.");
- sleep(5000);
- return;
- }
-
- if (mtaPlugin == null) {
- if (Microbot.getPluginManager() == null) return;
-
- mtaPlugin = (MTAPlugin) Microbot.getPluginManager().getPlugins()
- .stream().filter(x -> x instanceof net.runelite.client.plugins.mta.MTAPlugin)
- .findFirst().orElse(null);
-
- return;
- }
-
- if (handleFirstTime())
- return;
-
- currentRoom = getCurrentRoom();
- updatePoints();
- if (initPoints())
- return;
-
- if (currentRoom == null) {
- if (ensureInventory())
- return;
-
- if (currentPoints.entrySet().stream().allMatch(x -> getRequiredPoints().get(x.getKey()) * (config.buyRewards() ? 1 : (buyable + 1)) <= x.getValue())) {
- if (config.buyRewards()) {
- var rewardToBuy = config.reward();
- while (rewardToBuy.getPreviousReward() != null && !Rs2Inventory.contains(rewardToBuy.getPreviousReward().getItemId()))
- rewardToBuy = rewardToBuy.getPreviousReward();
-
- buyReward(rewardToBuy);
- } else {
- buyable = getRequiredPoints().entrySet().stream()
- .mapToInt(x -> currentPoints.get(x.getKey()) / x.getValue())
- .min().orElseThrow();
- }
- return;
- }
-
- var missingPoints = currentPoints.entrySet().stream()
- .filter(entry -> {
- var required = getRequiredPoints().get(entry.getKey()) * (config.buyRewards() ? 1 : (buyable + 1));
- return required > entry.getValue()
- && Arrays.stream(Rooms.values())
- .anyMatch(room -> room.getPoints() == entry.getKey()
- && room.getRequirements().getAsBoolean());
- })
- .map(Map.Entry::getKey)
- .collect(Collectors.toList());
-
- if (!missingPoints.isEmpty()) {
- var index = Rs2Random.between(0, missingPoints.size());
- var nextRooms = Arrays.stream(Rooms.values())
- .filter(room -> room.getPoints() == missingPoints.get(index))
- .collect(Collectors.toList());
-
- enterRoom(nextRooms.get(0));
- } else {
- Microbot.showMessage("MTA: Out of runes! Please restart the plugin after you restocked on runes.");
- sleep(500);
- shutdown();
- }
- } else if (config.repeatRoom()) {
- if (currentRoom != null) {
- Microbot.log("Repeating room: " + currentRoom.name());
- switch (currentRoom) {
- case ALCHEMIST:
- handleAlchemistRoom();
- break;
- case GRAVEYARD:
- handleGraveyardRoom();
- break;
- case ENCHANTMENT:
- handleEnchantmentRoom();
- break;
- case TELEKINETIC:
- handleTelekineticRoom();
- break;
- }
- }
- } else if (!currentRoom.getRequirements().getAsBoolean()
- || currentPoints.get(currentRoom.getPoints()) >= getRequiredPoints().get(currentRoom.getPoints()) * (config.buyRewards() ? 1 : (buyable + 1))) {
- leaveRoom();
- } else {
- switch (currentRoom) {
- case ALCHEMIST:
- handleAlchemistRoom();
- break;
- case GRAVEYARD:
- handleGraveyardRoom();
- break;
- case ENCHANTMENT:
- handleEnchantmentRoom();
- break;
- case TELEKINETIC:
- handleTelekineticRoom();
- break;
- }
- }
-
- sleepGaussian(600, 150);
- } catch (Exception ex) {
- if (ex instanceof InterruptedException)
- return;
-
- System.out.println(ex.getMessage());
- ex.printStackTrace(System.out);
- Microbot.log("MTA Exception: " + ex.getMessage());
- }
- }, 0, 10, TimeUnit.MILLISECONDS);
- return true;
- }
-
- private boolean ensureInventory() {
- var reward = config.reward();
- var previousRewards = new ArrayList();
- while (reward.getPreviousReward() != null) {
- reward = reward.getPreviousReward();
- previousRewards.add(reward.getItemId());
- }
-
- Predicate additionalItemPredicate = x -> !x.getName().toLowerCase().contains("rune")
- && !x.getName().toLowerCase().contains("staff")
- && !x.getName().toLowerCase().contains("tome")
- && !previousRewards.contains(x.getId())
- && !x.getName().toLowerCase().contains("bass");
-
- if (Rs2Inventory.contains(additionalItemPredicate)) {
- if (!Rs2Bank.walkToBankAndUseBank())
- return true;
-
- Rs2Bank.depositAll(additionalItemPredicate);
- return true;
- }
-
- return false;
- }
-
- private boolean initPoints() {
- if (currentPoints.values().stream().anyMatch(x -> x == -1)) {
- if (currentRoom != null)
- leaveRoom();
- else
- Rs2Walker.walkTo(portalPoint);
-
- return true;
- }
-
- return false;
- }
-
- private void updatePoints() {
- for (var points : currentPoints.entrySet()) {
- int gain = 0;
- if (points.getKey() == Points.ALCHEMIST && currentRoom == Rooms.ALCHEMIST && Rs2Inventory.hasItem("Coins"))
- gain = Rs2Inventory.get("Coins").getQuantity() / 100;
-
- var widget = Rs2Widget.getWidget(points.getKey().getWidgetId(), points.getKey().getChildId());
- if (widget != null && !Microbot.getClientThread().runOnClientThreadOptional(widget::isHidden).orElse(false))
- currentPoints.put(points.getKey(), Integer.parseInt(widget.getText().replace(",", "")));
- else {
- var roomWidget = Rs2Widget.getWidget(points.getKey().getRoomWidgetId(), points.getKey().getRoomChildId());
- if (roomWidget != null)
- currentPoints.put(points.getKey(), Integer.parseInt(roomWidget.getText().replace(",", "")) + gain);
- }
- }
- }
-
- private Map getRequiredPoints() {
- return getRequiredPoints(config);
- }
-
- public static Map getRequiredPoints(MageTrainingArenaConfig config) {
- var currentReward = config.reward();
- var requiredPoints = currentReward.getPoints().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
-
- while (currentReward.getPreviousReward() != null) {
- currentReward = currentReward.getPreviousReward();
- if (Rs2Inventory.contains(currentReward.getItemId()))
- break;
-
- for (var points : requiredPoints.entrySet())
- points.setValue(points.getValue() + currentReward.getPoints().get(points.getKey()));
- }
-
- return requiredPoints;
- }
-
- private void handleEnchantmentRoom() {
- MagicAction enchant;
- var magicLevel = Microbot.getClient().getBoostedSkillLevel(Skill.MAGIC);
-
- if (magicLevel >= 87 && currentRoom.getRequirements().getAsBoolean()) {
- enchant = MagicAction.ENCHANT_ONYX_JEWELLERY;
- } else if (magicLevel >= 68 && currentRoom.getRequirements().getAsBoolean()) {
- enchant = MagicAction.ENCHANT_DRAGONSTONE_JEWELLERY;
- } else if (magicLevel >= 57 && currentRoom.getRequirements().getAsBoolean()) {
- enchant = MagicAction.ENCHANT_DIAMOND_JEWELLERY;
- } else if (magicLevel >= 49 && currentRoom.getRequirements().getAsBoolean()) {
- enchant = MagicAction.ENCHANT_RUBY_JEWELLERY;
- } else if (magicLevel >= 27 && currentRoom.getRequirements().getAsBoolean()) {
- enchant = MagicAction.ENCHANT_EMERALD_JEWELLERY;
- } else {
- enchant = MagicAction.ENCHANT_SAPPHIRE_JEWELLERY;
- }
-
- if (areRoomRequirementsInvalid()) {
- if (!config.repeatRoom()) {
- leaveRoom();
- }
- return;
- }
-
- if (Rs2Inventory.isFull()) {
- if (!Rs2Walker.walkTo(new WorldPoint(3363, 9640, 0)))
- return;
-
- Rs2GameObject.interact(ObjectID.MAGICTRAINING_ENCHA_HOLE, "Deposit");
- Rs2Player.waitForWalking();
- return;
- }
-
- boolean successFullLoot = Rs2GroundItem.loot(ItemID.MAGICTRAINING_DRAGONSTONE, 12) && Rs2Inventory.waitForInventoryChanges(5000);
-
- if (successFullLoot && Rs2Inventory.emptySlotCount() > 0)
- return;
-
- var bonusShape = getBonusShape();
- if (bonusShape == null) return;
- var object = Rs2GameObject.getGameObject(obj -> (obj.getId() == bonusShape.getObjectId()) && Rs2Camera.isTileOnScreen(obj));
- if (object == null) {
- var index = Rs2Random.between(0, 4);
- Rs2Walker.walkTo(new WorldPoint[]{
- new WorldPoint(3347, 9655, 0),
- new WorldPoint(3378, 9655, 0),
- new WorldPoint(3379, 9624, 0),
- new WorldPoint(3346, 9624, 0)
- }[index]);
- Rs2Player.waitForWalking();
- return;
- }
- int itemId;
- if (Rs2Inventory.contains(ItemID.MAGICTRAINING_DRAGONSTONE))
- itemId = ItemID.MAGICTRAINING_DRAGONSTONE;
- else
- itemId = bonusShape.getItemId();
- if (Rs2Inventory.contains(ItemID.MAGICTRAINING_DRAGONSTONE) || Rs2Inventory.count(itemId) >= shapesToPick) {
- shapesToPick = Rs2Random.between(2, 4);
-
- Rs2Magic.cast(enchant);
- sleepUntil(() -> Rs2Tab.getCurrentTab() == InterfaceTab.INVENTORY);
- sleepGaussian(600, 150);
- Rs2Inventory.interact(itemId);
-
- sleepUntil(() -> !Rs2Inventory.contains(itemId) || itemId != ItemID.MAGICTRAINING_DRAGONSTONE && bonusShape != getBonusShape(), 20000);
- } else if (Rs2GameObject.interact(object, "Take-from")) {
- Rs2Inventory.waitForInventoryChanges(1000);
- } else if (Rs2Player.getWorldLocation().distanceTo(object.getWorldLocation()) > 10){
- Rs2Walker.walkFastCanvas(object.getWorldLocation());
- }
- }
-
- private EnchantmentShapes getBonusShape() {
- for (var shape : EnchantmentShapes.values())
- if (Rs2Widget.isWidgetVisible(shape.getWidgetId(), shape.getWidgetChildId()))
- return shape;
-
- return null;
- }
-
- private void handleTelekineticRoom() {
- if (areRoomRequirementsInvalid()) {
- if (!config.repeatRoom()) {
- leaveRoom();
- }
- return;
- }
-
- var room = mtaPlugin.getTelekineticRoom();
- var teleRoom = Arrays.stream(TelekineticRooms.values())
- .filter(x -> Rs2Player.getWorldLocation().distanceTo(x.getArea()) == 0)
- .findFirst().orElseThrow();
-
- // Walk to maze if guardian is not visible
- WorldPoint target;
- if (room.getTarget() != null)
- target = room.getTarget();
- else {
- Rs2Walker.walkTo(teleRoom.getMaze(), 4);
- sleepUntil(() -> room.getTarget() != null, 10_000);
- // MageTrainingArenaScript is dependent on the official mage arena plugin of runelite
- // In some cases it glitches out and target is not defined by an arrow, in this case we will reset them room
- if (room.getTarget() == null) {
- Microbot.log("Something seems wrong, room target was still not found...leaving room to reset.");
- leaveRoom();
- return;
- }
- target = room.getTarget();
- sleep(400, 600);
- }
-
- var localTarget = LocalPoint.fromWorld(Microbot.getClient().getTopLevelWorldView(), target);
- var targetConverted = WorldPoint.fromLocalInstance(Microbot.getClient(), Objects.requireNonNull(localTarget));
-
- if (Rs2Camera.getZoom() < 40 || Rs2Camera.getZoom() > 60) {
- Rs2Camera.setZoom(Rs2Random.betweenInclusive(40,60));
- }
-
- if (room.getGuardian().getWorldLocation().equals(room.getFinishLocation())) {
- sleepUntil(() -> room.getGuardian().getId() == NpcID.MAGICTRAINING_GUARD_MAZE_COMPLETE);
- sleep(200, 400);
- Rs2Npc.interact(new Rs2NpcModel(room.getGuardian()), "New-maze");
- sleepUntil(() -> Rs2Player.getWorldLocation().distanceTo(teleRoom.getArea()) != 0);
- } else {
- while (!Rs2Player.getWorldLocation().equals(targetConverted)
- && (Microbot.getClient().getLocalDestinationLocation() == null
- || !Microbot.getClient().getLocalDestinationLocation().equals(localTarget))) {
- if (Rs2Camera.isTileOnScreen(localTarget) && Rs2Player.getWorldLocation().distanceTo(targetConverted) < 10) {
- Rs2Walker.walkFastCanvas(targetConverted);
- sleepGaussian(600, 150);
- } else {
- Rs2Walker.walkTo(targetConverted);
- }
- sleepUntil(() -> !Rs2Player.isMoving());
- }
-
- if (!Rs2Player.isAnimating()
- && !Rs2Player.isMoving()
- && StreamSupport.stream(Microbot.getClient().getProjectiles().spliterator(), false).noneMatch(x -> x.getId() == SpotanimID.TELEGRAB_TRAVEL)
- && !TelekineticRoom.getMoves().isEmpty()
- && TelekineticRoom.getMoves().peek() == room.getPosition()
- && room.getGuardian().getId() != NpcID.MAGICTRAINING_GUARD_MAZE_MOVING
- && !room.getGuardian().getLocalLocation().equals(room.getDestination())) {
- Rs2Magic.cast(MagicAction.TELEKINETIC_GRAB);
- sleepGaussian(600, 150);
- if (Rs2Random.dicePercentage(50)) {
- Rs2Camera.turnTo(room.getGuardian());
- }
- Rs2Npc.interact(new Rs2NpcModel(room.getGuardian()));
- sleepUntil(()->room.getGuardian().getId() != NpcID.MAGICTRAINING_GUARD_MAZE_MOVING);
- }
- }
- }
-
- private void handleGraveyardRoom() {
- if (areRoomRequirementsInvalid()) {
- if (!config.repeatRoom()) {
- leaveRoom();
- }
- return;
- }
-
- if (btp == null)
- btp = Rs2Magic.canCast(MagicAction.BONES_TO_PEACHES);
-
- var boneGoal = 28 - Rs2Inventory.items().filter(x -> x.getName().equalsIgnoreCase("Animals' bones")).count();
- if (mtaPlugin.getGraveyardRoom().getCounter() != null && mtaPlugin.getGraveyardRoom().getCounter().getCount() >= boneGoal) {
- Rs2Magic.cast(btp ? MagicAction.BONES_TO_PEACHES : MagicAction.BONES_TO_BANANAS);
- Rs2Player.waitForAnimation();
- if(!Rs2Tab.isCurrentTab(InterfaceTab.INVENTORY)){
- sleep(500,1000);
- Rs2Tab.switchTo(InterfaceTab.INVENTORY);
- sleep(500,1000);
- }
- return;
- }
-
- if (Rs2Inventory.contains(ItemID.BANANA, ItemID.PEACH)) {
- var currentHp = Microbot.getClient().getBoostedSkillLevel(Skill.HITPOINTS);
- var maxHp = Microbot.getClient().getRealSkillLevel(Skill.HITPOINTS);
- if ((currentHp * 100) / maxHp < nextHpThreshold) {
- var maxAmountToEat = (maxHp - currentHp) / (btp ? 8 : 2);
- var amountToEat = Rs2Random.between(Math.min(2, maxAmountToEat), Math.min(6, maxAmountToEat));
- nextHpThreshold = Rs2Random.between(config.healingThresholdMin(), config.healingThresholdMax());
- for (int i = 0; i < amountToEat; i++) {
- Rs2Inventory.interact(btp ? ItemID.PEACH : ItemID.BANANA, "eat");
-
- if (i < amountToEat - 1)
- sleepGaussian(1400, 350);
- }
- }
- if (Rs2Inventory.contains(ItemID.BANANA, ItemID.PEACH)) {
- Rs2GameObject.interact(new WorldPoint(3354, 9639, 1), "Deposit");
- Rs2Inventory.waitForInventoryChanges(5000);
- }
- return;
- }
- if (mtaPlugin.getGraveyardRoom().getCounter() == null){
- Rs2GameObject.interact(new WorldPoint(3352, 9637, 1), "Grab");
- sleepUntil(()->!Rs2Player.isMoving() && Rs2Inventory.waitForInventoryChanges(2000));
- }
- while (mtaPlugin.getGraveyardRoom().getCounter() != null && mtaPlugin.getGraveyardRoom().getCounter().getCount() < boneGoal && isRunning()){
- System.out.println("Plugin Counter: " + mtaPlugin.getGraveyardRoom().getCounter().getCount() + " boneGoal: " + boneGoal);
- Rs2GameObject.interact(new WorldPoint(3352, 9637, 1), "Grab");
- Rs2Inventory.waitForInventoryChanges(5000);
- boneGoal = (Rs2Inventory.items().filter(x -> x.getName().equalsIgnoreCase("Animals' bones")).count()+Rs2Inventory.emptySlotCount());
- sleepGaussian(400, 150);
- }
- }
-
- private void handleAlchemistRoom() {
- if (areRoomRequirementsInvalid()) {
- if (!config.repeatRoom()) {
- leaveRoom();
- }
- return;
- }
-
- var room = mtaPlugin.getAlchemyRoom();
- var best = room.getBest();
- var item = Rs2Inventory.getLast(best.getId());
- if (item != null) {
- sleep(50,150);
- Rs2Magic.alch(item);
- return;
- }else {
- Rs2Inventory.dropAll(6897,6896,6895,6894,6893);
- }
-
- var timer = (AlchemyRoomTimer) Microbot.getInfoBoxManager().getInfoBoxes().stream()
- .filter(x -> x instanceof AlchemyRoomTimer)
- .findFirst().orElse(null);
- if (timer == null || Integer.parseInt(timer.getText().split(":")[1]) < 2) {
- Rs2Walker.walkTo(3364, 9636, 2, 2);
- return;
- }
-
- if (room.getSuggestion() == null) {
- Rs2GameObject.interact("Cupboard", "Search");
- sleep(300,600);
-
- if (sleepUntilTrue(Rs2Player::isMoving, 100, 1000))
- sleepUntil(() -> !Rs2Player.isMoving());
- } else {
- Rs2GameObject.interact(room.getSuggestion().getGameObject(), "Take-5");
- Rs2Inventory.waitForInventoryChanges(3000);
- sleep(300,600);
- }
- }
-
- private boolean areRoomRequirementsInvalid() {
- if (!currentRoom.getRequirements().getAsBoolean()) {
- Microbot.log("You're missing room requirements. Please restock or fix your staves settings.");
- if (!config.repeatRoom()) {
- sleep(5000);
- }
- return true;
- }
- return false;
- }
-
- private void buyReward(Rewards reward) {
- if (!Rs2Walker.walkTo(bankPoint))
- return;
-
- if (!Rs2Widget.isWidgetVisible(197, 0)) {
- Rs2Npc.interact(NpcID.MAGICTRAINING_GUARD_REWARDS, "Trade-with");
- sleepUntil(() -> Rs2Widget.isWidgetVisible(197, 0));
- sleepGaussian(600, 150);
- return;
- }
-
- Rs2Inventory.waitForInventoryChanges(() -> {
- var rewardWidgets = Rs2Widget.getWidget(197, 11).getDynamicChildren();
- if (rewardWidgets == null) return;
- var widget = Arrays.stream(rewardWidgets).filter(x -> x.getItemId() == reward.getItemId()).findFirst().orElse(null);
- Rs2Widget.clickWidgetFast(widget, Arrays.asList(rewardWidgets).indexOf(widget));
- sleepGaussian(600, 150);
- Rs2Widget.clickWidget(197, 9);
- sleepGaussian(600, 150);
- Rs2Keyboard.keyPress(KeyEvent.VK_ESCAPE);
- });
-
- if (reward == config.reward())
- bought++;
- }
-
- public static Rooms getCurrentRoom() {
- for (var room : Rooms.values()) {
- if (room == Rooms.TELEKINETIC && Arrays.stream(TelekineticRooms.values()).anyMatch(x -> Rs2Player.getWorldLocation().distanceTo(x.getArea()) == 0)
- || room.getArea() != null && Rs2Player.getWorldLocation().distanceTo(room.getArea()) == 0)
- return room;
- }
-
- return null;
- }
-
- public static void enterRoom(Rooms room) {
- if (!Rs2Walker.walkTo(portalPoint))
- return;
-
- Rs2GameObject.interact(room.getTeleporter(), "Enter");
- Rs2Player.waitForAnimation();
- if (Rs2Widget.hasWidget("You must talk to the Entrance Guardian"))
- firstTime = true;
- }
-
- public static void leaveRoom() {
- var room = getCurrentRoom();
-
- if (room == null)
- return;
-
- WorldPoint exit = null;
- if (room != Rooms.TELEKINETIC)
- exit = room.getExit();
- else {
- for (var teleRoom : TelekineticRooms.values()) {
- if (Rs2Player.getWorldLocation().distanceTo(teleRoom.getArea()) == 0) {
- exit = teleRoom.getExit();
- break;
- }
- }
- }
-
- if (!Rs2Walker.walkTo(exit))
- return;
-
- Rs2GameObject.interact(ObjectID.MAGICTRAINING_RETURNDOOR, "Enter");
- Rs2Player.waitForWalking();
- }
-
- private boolean handleFirstTime() {
- if (firstTime) {
- if (!Rs2Walker.walkTo(new WorldPoint(3363, 3304, 0)))
- return true;
-
- if (!Rs2Dialogue.isInDialogue())
- Rs2Npc.interact(NpcID.MAGICTRAINING_GUARD_ENTRANCE, "Talk-to");
- else if (Rs2Dialogue.hasSelectAnOption() && Rs2Widget.hasWidget("I'm new to this place"))
- Rs2Widget.clickWidget("I'm new to this place");
- else if (Rs2Dialogue.hasSelectAnOption() && Rs2Widget.hasWidget("Thanks, bye!")) {
- Rs2Widget.clickWidget("Thanks, bye!");
- firstTime = false;
- } else
- Rs2Dialogue.clickContinue();
-
- return true;
- }
-
- return false;
- }
-
- @Override
- public void shutdown() {
- super.shutdown();
- }
-
- /**
- * Attempts to equip the best available {@code staff} from inventory that reduces rune cost for the specified {@link Rs2Spells} spell.
- * This method also accounts for any equipped or equippable {@code tome} in the shield slot which may provide passive rune substitution.
- *
- * The method evaluates all available staff+tome combinations, selects the one with the highest effective rune savings,
- * equips the staff if not already equipped, and verifies that the spell is now castable with the resulting equipment and runes.
- *
- *
Logic Summary:
- *
- * - Scans equipped weapon/shield and inventory for candidate staff and tome combinations
- * - Simulates rune savings provided by each staff+tome combo
- * - Verifies castability after accounting for free runes and available inventory/pouch supply
- * - Equips the best staff (if not already equipped) and confirms the spell is ready for casting
- *
- *
- * This method does not return {@code true} unless a valid staff was equipped (or already equipped)
- * and the spell is verifiably castable after staff+tome rune substitution.
- *
- * @param spell The {@link Rs2Spells} spell to evaluate for castability.
- * @param hasRunePouch {@code true} if the player's rune pouch is available and should be included in rune calculations.
- * @return {@code true} if the spell is castable after equipping the best available staff (and considering any tome); {@code false} otherwise.
- *
- *
Side effects: May trigger staff equipping from inventory. No tome swapping is performed — only passively recognized if equipped.
- */
- public static boolean tryEquipBestStaffAndCast(Rs2Spells spell, boolean hasRunePouch) {
- Map requiredRunes = spell.getRequiredRunes();
-
- List candidates = new ArrayList<>();
- Rs2ItemModel equipped = Rs2Equipment.get(EquipmentInventorySlot.WEAPON);
- if (equipped != null) candidates.add(equipped);
- candidates.addAll(Rs2Inventory.items().collect(Collectors.toList()));
-
- Rs2Tome equippedTome = Rs2Tome.NONE;
- Rs2ItemModel shield = Rs2Equipment.get(EquipmentInventorySlot.SHIELD);
- if (shield != null) equippedTome = getRs2Tome(shield.getId());
-
- Map inventoryRunes = new EnumMap<>(Runes.class);
- Rs2Inventory.items().forEach(item -> {
- Runes rune = Runes.byItemId(item.getId());
- if (rune != null) {
- inventoryRunes.merge(rune, item.getQuantity(), Integer::sum);
- }
- });
-
- if (hasRunePouch) {
- Rs2RunePouch.getRunes().forEach((id, qty) -> {
- Runes rune = Runes.byItemId(id.getItemId());
- if (rune != null) {
- inventoryRunes.merge(rune, qty, Integer::sum);
- }
- });
- }
-
- int maxSavings = -1;
- Integer bestStaffId = null;
- Set bestProvidedRunes = null;
-
- for (Rs2ItemModel staffItem : candidates) {
- Rs2Staff staff = getRs2Staff(staffItem.getId());
- if (staff == Rs2Staff.NONE) continue;
-
- Set providedRunes = new HashSet<>(staff.getRunes());
- if (equippedTome != Rs2Tome.NONE) providedRunes.addAll(equippedTome.getRunes());
-
- boolean castable = true;
- int savings = 0;
-
- for (Map.Entry entry : requiredRunes.entrySet()) {
- if (providedRunes.contains(entry.getKey())) {
- savings += entry.getValue();
- continue;
- }
- if (inventoryRunes.getOrDefault(entry.getKey(), 0) < entry.getValue()) {
- castable = false;
- break;
- }
- }
-
- if (castable && savings > maxSavings) {
- maxSavings = savings;
- bestStaffId = staff.getItemID();
- bestProvidedRunes = providedRunes;
- }
- }
-
- if (bestStaffId != null) {
- if (!Rs2Equipment.isWearing(bestStaffId)) {
- Rs2Inventory.wear(bestStaffId);
- }
-
- Set activeRunes = new HashSet<>(bestProvidedRunes);
- Rs2ItemModel activeShield = Rs2Equipment.get(EquipmentInventorySlot.SHIELD);
- if (activeShield != null) {
- Rs2Tome newTome = getRs2Tome(activeShield.getId());
- if (newTome != Rs2Tome.NONE) activeRunes.addAll(newTome.getRunes());
- }
-
- for (Map.Entry entry : requiredRunes.entrySet()) {
- if (activeRunes.contains(entry.getKey())) continue;
- if (inventoryRunes.getOrDefault(entry.getKey(), 0) < entry.getValue()) return false;
- }
- return true;
- }
-
- return false;
- }
-
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/enums/EnchantmentShapes.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/enums/EnchantmentShapes.java
deleted file mode 100644
index 8ccfbb2d45e..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/enums/EnchantmentShapes.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package net.runelite.client.plugins.microbot.magetrainingarena.enums;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import net.runelite.api.gameval.ItemID;
-import net.runelite.api.gameval.ObjectID;
-
-@Getter
-@AllArgsConstructor
-public enum EnchantmentShapes {
- PENTAMID(ObjectID.MAGICTRAINING_ENCHAN_SHAPEPILE4, ItemID.MAGICTRAINING_ENCHAN_PENTAMID, 195, 14),
- ICOSAHEDRON(ObjectID.MAGICTRAINING_ENCHAN_SHAPEPILE3, ItemID.MAGICTRAINING_ENCHAN_ICOSAHENDRON, 195, 16),
- CUBE(ObjectID.MAGICTRAINING_ENCHAN_SHAPEPILE1, ItemID.MAGICTRAINING_ENCHAN_CUBE, 195, 10),
- CYLINDER(ObjectID.MAGICTRAINING_ENCHAN_SHAPEPILE2, ItemID.MAGICTRAINING_ENCHAN_CYLINDER, 195, 12);
-
- private final int objectId;
- private final int itemId;
- private final int widgetId;
- private final int widgetChildId;
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/enums/Points.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/enums/Points.java
deleted file mode 100644
index 3530e170103..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/enums/Points.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package net.runelite.client.plugins.microbot.magetrainingarena.enums;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-@Getter
-@AllArgsConstructor
-public enum Points {
- TELEKINETIC(553, 10, 198, 6),
- ALCHEMIST(553, 11, 194, 6),
- ENCHANTMENT(553, 12, 195, 6),
- GRAVEYARD(553, 13, 196, 6);
-
- private int widgetId;
- private int childId;
- private int roomWidgetId;
- private int roomChildId;
-
- @Override
- public String toString() {
- String name = name();
- return name.charAt(0) + name.substring(1).toLowerCase();
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/enums/Rewards.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/enums/Rewards.java
deleted file mode 100644
index bfccbacb287..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/enums/Rewards.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package net.runelite.client.plugins.microbot.magetrainingarena.enums;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import net.runelite.api.gameval.ItemID;
-
-import java.util.Map;
-
-@Getter
-@AllArgsConstructor
-public enum Rewards {
- ALL_ITEMS(ItemID.SKILLCAPE_MAX, Map.of(
- Points.TELEKINETIC, 2675,
- Points.GRAVEYARD, 2675,
- Points.ENCHANTMENT, 27500,
- Points.ALCHEMIST, 3075), null),
-
- INFINITY_HAT(ItemID.MAGICTRAINING_INFINITYHAT, Map.of(
- Points.TELEKINETIC, 350,
- Points.GRAVEYARD, 350,
- Points.ENCHANTMENT, 3000,
- Points.ALCHEMIST, 400), null),
-
- INFINITY_TOP(ItemID.MAGICTRAINING_INFINITYTOP, Map.of(
- Points.TELEKINETIC, 400,
- Points.GRAVEYARD, 400,
- Points.ENCHANTMENT, 4000,
- Points.ALCHEMIST, 450), null),
-
- INFINITY_BOTTOMS(ItemID.MAGICTRAINING_INFINITYBOTTOM, Map.of(
- Points.TELEKINETIC, 450,
- Points.GRAVEYARD, 450,
- Points.ENCHANTMENT, 5000,
- Points.ALCHEMIST, 500), null),
-
- INFINITY_GLOVES(ItemID.MAGICTRAINING_INFINITYGLOVES, Map.of(
- Points.TELEKINETIC, 175,
- Points.GRAVEYARD, 175,
- Points.ENCHANTMENT, 1500,
- Points.ALCHEMIST, 225), null),
-
- INFINITY_BOOTS(ItemID.MAGICTRAINING_INFINITYBOOTS, Map.of(
- Points.TELEKINETIC, 120,
- Points.GRAVEYARD, 120,
- Points.ENCHANTMENT, 1200,
- Points.ALCHEMIST, 120), null),
-
- BEGINNER_WAND(ItemID.MAGICTRAINING_WAND_BEG, Map.of(
- Points.TELEKINETIC, 30,
- Points.GRAVEYARD, 30,
- Points.ENCHANTMENT, 300,
- Points.ALCHEMIST, 30), null),
-
- APPRENTICE_WAND(ItemID.MAGICTRAINING_WAND_APPR, Map.of(
- Points.TELEKINETIC, 60,
- Points.GRAVEYARD, 60,
- Points.ENCHANTMENT, 600,
- Points.ALCHEMIST, 60), BEGINNER_WAND),
-
- TEACHER_WAND(ItemID.MAGICTRAINING_WAND_TEACH, Map.of(
- Points.TELEKINETIC, 150,
- Points.GRAVEYARD, 150,
- Points.ENCHANTMENT, 1500,
- Points.ALCHEMIST, 200), APPRENTICE_WAND),
-
- MASTER_WAND(ItemID.MAGICTRAINING_WAND_MASTER, Map.of(
- Points.TELEKINETIC, 240,
- Points.GRAVEYARD, 240,
- Points.ENCHANTMENT, 2400,
- Points.ALCHEMIST, 240), TEACHER_WAND),
-
- MAGES_BOOK(ItemID.MAGICTRAINING_BOOKOFMAGIC, Map.of(
- Points.TELEKINETIC, 500,
- Points.GRAVEYARD, 500,
- Points.ENCHANTMENT, 6000,
- Points.ALCHEMIST, 550), null),
-
- BONES_TO_PEACHES(ItemID.MAGICTRAINING_PEACHSPELL, Map.of(
- Points.TELEKINETIC, 200,
- Points.GRAVEYARD, 200,
- Points.ENCHANTMENT, 2000,
- Points.ALCHEMIST, 300), null);
-
- private final int itemId;
- private final Map points;
- private final Rewards previousReward;
-
- @Override
- public String toString() {
- String name = name();
- return name.charAt(0) + name.substring(1).toLowerCase().replace("_", " ");
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/enums/Rooms.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/enums/Rooms.java
deleted file mode 100644
index d787d2187f1..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/enums/Rooms.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package net.runelite.client.plugins.microbot.magetrainingarena.enums;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import net.runelite.api.Skill;
-import net.runelite.api.coords.WorldArea;
-import net.runelite.api.coords.WorldPoint;
-import net.runelite.client.plugins.microbot.Microbot;
-import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory;
-import net.runelite.client.plugins.microbot.util.magic.Rs2Spells;
-import org.slf4j.event.Level;
-
-import java.util.function.BooleanSupplier;
-
-import static net.runelite.client.plugins.microbot.magetrainingarena.MageTrainingArenaScript.tryEquipBestStaffAndCast;
-
-@Getter
-@AllArgsConstructor
-public enum Rooms {
- TELEKINETIC("Telekinetic", 23673, null, null, Points.TELEKINETIC,
- () -> {
- boolean result = tryEquipBestStaffAndCast(Rs2Spells.TELEKINETIC_GRAB, Rs2Inventory.hasRunePouch());
- Microbot.log("TELEKINETIC req met: " + result, Level.DEBUG);
- return result;
- }),
-
- ALCHEMIST("Alchemist", 23675,
- new WorldArea(3345, 9616, 38, 38, 2),
- new WorldPoint(3364, 9623, 2),
- Points.ALCHEMIST,
- () -> {
- boolean high = tryEquipBestStaffAndCast(Rs2Spells.HIGH_LEVEL_ALCHEMY, Rs2Inventory.hasRunePouch());
- boolean low = tryEquipBestStaffAndCast(Rs2Spells.LOW_LEVEL_ALCHEMY, Rs2Inventory.hasRunePouch());
- Microbot.log("ALCHEMIST req met: HIGH=" + high + " LOW=" + low, Level.DEBUG);
- return high || low;
- }),
-
- ENCHANTMENT("Enchantment", 23674,
- new WorldArea(3339, 9617, 50, 46, 0),
- new WorldPoint(3363, 9640, 0),
- Points.ENCHANTMENT,
- () -> {
- int level = Microbot.getClient().getBoostedSkillLevel(Skill.MAGIC);
- boolean result;
-
- if (level >= 87) {
- result = tryEquipBestStaffAndCast(Rs2Spells.ENCHANT_ONYX_JEWELLERY, Rs2Inventory.hasRunePouch());
- } else if (level >= 68) {
- result = tryEquipBestStaffAndCast(Rs2Spells.ENCHANT_DRAGONSTONE_JEWELLERY, Rs2Inventory.hasRunePouch());
- } else if (level >= 57) {
- result = tryEquipBestStaffAndCast(Rs2Spells.ENCHANT_DIAMOND_JEWELLERY, Rs2Inventory.hasRunePouch());
- } else if (level >= 49) {
- result = tryEquipBestStaffAndCast(Rs2Spells.ENCHANT_RUBY_JEWELLERY, Rs2Inventory.hasRunePouch());
- } else if (level >= 27) {
- result = tryEquipBestStaffAndCast(Rs2Spells.ENCHANT_EMERALD_JEWELLERY, Rs2Inventory.hasRunePouch());
- } else {
- result = tryEquipBestStaffAndCast(Rs2Spells.ENCHANT_SAPPHIRE_JEWELLERY, Rs2Inventory.hasRunePouch());
- }
-
- Microbot.log("ENCHANTMENT req met (level=" + level + "): " + result, Level.DEBUG);
- return result;
- }),
-
- GRAVEYARD("Graveyard", 23676,
- new WorldArea(3336, 9614, 54, 51, 1),
- new WorldPoint(3363, 9640, 1),
- Points.GRAVEYARD,
- () -> {
- boolean bananas = tryEquipBestStaffAndCast(Rs2Spells.BONES_TO_BANANAS, Rs2Inventory.hasRunePouch());
- boolean peaches = tryEquipBestStaffAndCast(Rs2Spells.BONES_TO_PEACHES, Rs2Inventory.hasRunePouch());
-
- Microbot.log("GRAVEYARD req met: BANANAS=" + bananas + " PEACHES=" + peaches, Level.DEBUG);
- if (!bananas && !peaches) {
- Microbot.log("Missing requirement to cast Bones to Bananas or Peaches.", Level.DEBUG);
- return false;
- }
- return true;
- });
-
- private final String name;
- private final int teleporter;
- private final WorldArea area;
- private final WorldPoint exit;
- private final Points points;
- private final BooleanSupplier requirements;
-
- @Override
- public String toString() {
- String n = name();
- return n.charAt(0) + n.substring(1).toLowerCase();
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/enums/TelekineticRooms.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/enums/TelekineticRooms.java
deleted file mode 100644
index 2f2c359039b..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magetrainingarena/enums/TelekineticRooms.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package net.runelite.client.plugins.microbot.magetrainingarena.enums;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import net.runelite.api.coords.WorldArea;
-import net.runelite.api.coords.WorldPoint;
-
-@Getter
-@AllArgsConstructor
-public enum TelekineticRooms {
- A(new WorldArea(3324, 9697, 34, 30, 0), new WorldPoint(3334, 9718, 0), new WorldPoint(3339, 9713, 0)),
- B(new WorldArea(3358, 9704, 34, 23, 0), new WorldPoint(3379, 9716, 0), new WorldPoint(3373, 9716, 0)),
- C(new WorldArea(3327, 9669, 31, 27, 0), new WorldPoint(3352, 9690, 0), new WorldPoint(3346, 9683, 0)),
- D(new WorldArea(3361, 9664, 28, 39, 0), new WorldPoint(3373, 9696, 0), new WorldPoint(3373, 9681, 0)),
- E(new WorldArea(3331, 9702, 37, 24, 1), new WorldPoint(3362, 9713, 1), new WorldPoint(3349, 9713, 1)),
- F(new WorldArea(3368, 9704, 22, 22, 1), new WorldPoint(3377, 9706, 1), new WorldPoint(3382, 9714, 1)),
- G(new WorldArea(3332, 9671, 35, 29, 1), new WorldPoint(3355, 9693, 1), new WorldPoint(3354, 9688, 1)),
- H(new WorldArea(3367, 9669, 25, 34, 1), new WorldPoint(3382, 9698, 1), new WorldPoint(3381, 9685, 1)),
- I(new WorldArea(3332, 9696, 42, 31, 2), new WorldPoint(3359, 9701, 2), new WorldPoint(3351, 9710, 2)),
- J(new WorldArea(3331, 9667, 45, 27, 2), new WorldPoint(3368, 9680, 2), new WorldPoint(3347, 9679, 2));
-
- private final WorldArea area;
- private final WorldPoint exit;
- private final WorldPoint maze;
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mahoganyhomez/ContractLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mahoganyhomez/ContractLocation.java
index 5b61781f159..2ca2d34a70a 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mahoganyhomez/ContractLocation.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mahoganyhomez/ContractLocation.java
@@ -7,7 +7,7 @@
@Getter
public enum ContractLocation {
MAHOGANY_HOMES_ARDOUGNE("Mahogany Homes", new WorldPoint(2635, 3294, 0)),
- MAHOGANY_HOMES_FALADOR("Mahogany Homes", new WorldPoint(2989, 3363, 0)),
+ MAHOGANY_HOMES_FALADOR("Mahogany Homes", new WorldPoint(2990, 3365, 0)),
MAHOGANY_HOMES_HOSIDIUS("Mahogany Homes", new WorldPoint(1781, 3626, 0)),
MAHOGANY_HOMES_VARROCK("Mahogany Homes", new WorldPoint(3240, 3471, 0));
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mahoganyhomez/MahoganyHomesScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mahoganyhomez/MahoganyHomesScript.java
index 262ebd12ea3..a5df49d1cd3 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mahoganyhomez/MahoganyHomesScript.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mahoganyhomez/MahoganyHomesScript.java
@@ -262,7 +262,7 @@ private void finish() {
if (plugin.getCurrentHome() != null
&& plugin.getCurrentHome().isInside(Rs2Player.getWorldLocation())
&& Hotspot.isEverythingFixed()) {
- if(plugin.getConfig().usePlankSack() && planksInPlankSack() > 0){
+ if(plugin.getConfig().usePlankSack() && planksInPlankSack() > 0 && !Rs2Inventory.isFull()){
if (Rs2Inventory.contains(ItemID.PLANK_SACK) && Rs2Inventory.contains(ItemID.STEEL_BAR)) {
Rs2ItemModel plankSack = Rs2Inventory.get(ItemID.PLANK_SACK);
if (plankSack != null) {
@@ -322,11 +322,22 @@ private void getNewContract() {
log("Getting new contract");
- var npc = Rs2Npc.getNpcWithAction("Contract");
+ // Search for Mahogany Homes contract NPCs directly by name
+ var npc = Rs2Npc.getNpcs()
+ .filter(n -> n.getName() != null &&
+ (n.getName().equals("Amy") ||
+ n.getName().equals("Marlo") ||
+ n.getName().equals("Ellie") ||
+ n.getName().equals("Angelo")))
+ .findFirst()
+ .orElse(null);
+
if (npc == null) {
+ log("No contract NPC found, waiting before retry");
+ sleep(2000, 3000); // Wait 2-3 seconds to prevent spam
return;
}
- log("NPC found: " + npc.getComposition().transform().getName());
+ log("NPC found: " + npc.getName());
if (Rs2Npc.interact(npc, "Contract")) {
handleContractDialogue();
}
@@ -338,9 +349,13 @@ private void getNewContract() {
}
public void handleContractDialogue() {
- sleepUntil(Rs2Dialogue::hasSelectAnOption, Rs2Dialogue::clickContinue, 10000, 300);
+ // Reduced timeout and early return if dialogue not available
+ if (!sleepUntil(Rs2Dialogue::hasSelectAnOption, Rs2Dialogue::clickContinue, 5000, 300)) {
+ log("No dialogue options available, returning early");
+ return;
+ }
Rs2Dialogue.keyPressForDialogueOption(plugin.getConfig().currentTier().getPlankSelection().getChatOption());
- sleepUntil(Rs2Dialogue::hasContinue, 10000);
+ sleepUntil(Rs2Dialogue::hasContinue, 5000);
sleep(400, 800);
sleepUntil(() -> !Rs2Dialogue.isInDialogue(), Rs2Dialogue::clickContinue, 6000, 300);
sleep(1200, 2200);
@@ -394,12 +409,30 @@ && isMissingItems()) {
Rs2Bank.withdrawAll(plugin.getConfig().currentTier().getPlankSelection().getPlankId());
Rs2Bank.closeBank();
} else {
- if (Rs2Inventory.getEmptySlots() - steelBarsNeeded() > 0)
- Rs2Bank.withdrawX(plugin.getConfig().currentTier().getPlankSelection().getPlankId(), Rs2Inventory.getEmptySlots() - steelBarsNeeded());
- sleep(600, 1200);
+ // Withdraw steel bars first if needed
if (steelBarsNeeded() > steelBarsInInventory()) {
- Rs2Bank.withdrawX(ItemID.STEEL_BAR, steelBarsNeeded());
- sleep(600, 1200);
+ Rs2Bank.withdrawX(ItemID.STEEL_BAR, steelBarsNeeded() - steelBarsInInventory());
+ Rs2Inventory.waitForInventoryChanges(5000);
+ }
+
+ // Calculate if we'll have enough space for planks after steel bars
+ int freeSlots = Rs2Inventory.getEmptySlots();
+ int currentPlanks = planksInInventory() + planksInPlankSack();
+ int additionalPlanksNeeded = planksNeeded() - currentPlanks;
+
+ if (additionalPlanksNeeded <= 0) {
+ // We already have enough planks
+ log("Already have sufficient planks: %d/%d", currentPlanks, planksNeeded());
+ } else if (freeSlots >= additionalPlanksNeeded) {
+ // Withdraw all planks to fill inventory
+ Rs2Bank.withdrawAll(plugin.getConfig().currentTier().getPlankSelection().getPlankId());
+ Rs2Inventory.waitForInventoryChanges(5000);
+ } else {
+ // This should never happen - inventory can't fit required materials
+ log("CRITICAL ERROR: Need %d more planks but only %d slots available!", additionalPlanksNeeded, freeSlots);
+ Microbot.showMessage("Please free up inventory space! Need " + additionalPlanksNeeded + " more planks but only " + freeSlots + " slots available. Stopping script.");
+ shutdown();
+ return;
}
}
Rs2Bank.closeBank();
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/AgaHerbs.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/AgaHerbs.java
deleted file mode 100644
index 58b55c8cfff..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/AgaHerbs.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package net.runelite.client.plugins.microbot.mixology;
-
-import lombok.RequiredArgsConstructor;
-@RequiredArgsConstructor
-public enum AgaHerbs {
- Irit("irit leaf"),
- Cadantine("cadantine"),
- Lantadyme("Lantadyme"),
- Dwarf_Weed("dwarf weed"),
- Torstol("torstol");
-
- private final String itemName;
-
- @Override
- public String toString() {
- return itemName;
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/AlchemyObject.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/AlchemyObject.java
deleted file mode 100644
index 39d469a9a20..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/AlchemyObject.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package net.runelite.client.plugins.microbot.mixology;
-
-import net.runelite.api.coords.WorldPoint;
-
-public enum AlchemyObject {
- MOX_LEVER(54868, new WorldPoint(1395, 9324, 0)),
- AGA_LEVER(54867, new WorldPoint(1394, 9324, 0)),
- LYE_LEVER(54869, new WorldPoint(1393, 9324, 0)),
- MIXING_VESSEL(55395, new WorldPoint(1394, 9326, 0)),
- ALEMBIC(55391, new WorldPoint(1391, 9325, 0)),
- AGITATOR(55390, new WorldPoint(1393, 9329, 0)),
- RETORT(55389, new WorldPoint(1397, 9325, 0)),
- CONVEYOR_BELT(54917, new WorldPoint(1394, 9331, 0)),
- HOPPER(54903, new WorldPoint(1394, 9322, 0)),
- DIGWEED_NORTH_EAST(55396, new WorldPoint(1399, 9331, 0)),
- DIGWEED_SOUTH_EAST(55397, new WorldPoint(1399, 9322, 0)),
- DIGWEED_SOUTH_WEST(55398, new WorldPoint(1389, 9322, 0)),
- DIGWEED_NORTH_WEST(55399, new WorldPoint(1389, 9331, 0));
-
- private final int objectId;
- private final WorldPoint coordinate;
-
- AlchemyObject(int objectId, WorldPoint coordinate) {
- this.objectId = objectId;
- this.coordinate = coordinate;
- }
-
- public int objectId() {
- return this.objectId;
- }
-
- public WorldPoint coordinate() {
- return this.coordinate;
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/InventoryPotionOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/InventoryPotionOverlay.java
deleted file mode 100644
index 2fa33ef5720..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/InventoryPotionOverlay.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package net.runelite.client.plugins.microbot.mixology;
-
-import com.google.inject.Inject;
-import net.runelite.api.widgets.WidgetItem;
-import net.runelite.client.ui.FontManager;
-import net.runelite.client.ui.overlay.WidgetItemOverlay;
-
-import java.awt.*;
-
-public class InventoryPotionOverlay extends WidgetItemOverlay {
-
- @Inject
- InventoryPotionOverlay() {
- this.showOnInventory();
- }
-
- public void renderItemOverlay(Graphics2D graphics2D, int itemId, WidgetItem widgetItem) {
- PotionType potion = PotionType.fromItemId(itemId);
- if (potion != null) {
- Rectangle bounds = widgetItem.getCanvasBounds();
- graphics2D.setFont(FontManager.getRunescapeSmallFont());
- graphics2D.setColor(Color.WHITE);
- graphics2D.drawString(potion.abbreviation(), bounds.x - 1, bounds.y + 15);
- }
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/LyeHerbs.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/LyeHerbs.java
deleted file mode 100644
index 32674c1751c..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/LyeHerbs.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package net.runelite.client.plugins.microbot.mixology;
-
-import lombok.RequiredArgsConstructor;
-@RequiredArgsConstructor
-public enum LyeHerbs {
- Ranarr("ranarr weed"),
- Toadflax("toadflax"),
- Avantoe("avantoe"),
- Kwuarm("kwuarm"),
- Snapdragon("snapdragon");
-
- private final String itemName;
-
- @Override
- public String toString() {
- return itemName;
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/MixologyConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/MixologyConfig.java
deleted file mode 100644
index 48b3d64f94e..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/MixologyConfig.java
+++ /dev/null
@@ -1,160 +0,0 @@
-package net.runelite.client.plugins.microbot.mixology;
-
-import net.runelite.client.config.*;
-import org.jetbrains.annotations.Range;
-
-@ConfigGroup("mixology")
-@ConfigInformation("Start the script at the bankchest of the mixology minigame room with an empty inventory.
" +
- "
Requirements: " +
- " " +
- "- 60 Herblore
" +
- "- Herbs for making mox/aga/lye paste
" +
- "
")
-public interface MixologyConfig extends Config {
- @ConfigSection(
- name = "Refiner",
- description = "Refiner configuration",
- position = 0
- )
- String refiner = "Refiner";
-
- @ConfigSection(
- name = "Minigame",
- description = "General minigame configuration",
- position = 1
- )
- String minigame = "Minigame";
-
- @ConfigItem(
- keyName = "RefinerHerbMox",
- name = "Refining Mox Herb",
- description = "Refine herbs into mox paste",
- position = 0,
- section = refiner
- )
- default MoxHerbs moxHerb() {
- return MoxHerbs.GUAM;
- }
- @Range(from = 100, to = 3000)
- @ConfigItem(
- keyName = "RefinerHerbMoxAmt",
- name = "Refining Mox Herb Amount",
- description = "Amount of herbs to refine into mox paste",
- position = 1,
- section = refiner
- )
- default int amtMoxHerb() {
- return 1000;
- }
-
- @ConfigItem(
- keyName = "RefinerHerbLye",
- name = "Refining Lye Herb",
- description = "Refine herbs into lye paste",
- position = 2,
- section = refiner
- )
- default LyeHerbs lyeHerb() {
- return LyeHerbs.Ranarr;
- }
- @Range(from = 100, to = 3000)
- @ConfigItem(
- keyName = "RefinerHerbLyeAmt",
- name = "Refining lye Herb Amount",
- description = "Amount of herbs to refine into lye paste",
- position = 3,
- section = refiner
- )
- default int amtLyeHerb() {
- return 1000;
- }
-
- @ConfigItem(
- keyName = "RefinerHerbAga",
- name = "Refining Aga Herb",
- description = "Refine herbs into aga paste",
- position = 4,
- section = refiner
- )
- default AgaHerbs agaHerb() {
- return AgaHerbs.Irit;
- }
- @Range(from = 100, to = 3000)
- @ConfigItem(
- keyName = "RefinerHerbAgaAmt",
- name = "Refining Aga Herb Amount",
- description = "Amount of herbs to refine into aga paste",
- position = 5,
- section = refiner
- )
- default int amtAgaHerb() {
- return 1000;
- }
-
- @ConfigItem(
- keyName = "useQuickActionRefiner",
- name = "Use Quick Action on Refiner",
- description = "Will click while paste to allow for faster completion of the task",
- position = 5,
- section = refiner
- )
- default boolean useQuickActionRefiner() {
- return true;
- }
-
- // -- MINIGAME SECTION -- //
-
- @ConfigItem(
- keyName = "useQuickActionAlembic",
- name = "Use Quick Action on Alembic",
- description = "Will click once there is a quick action available on the alembic",
- position = 0,
- section = minigame
- )
- default boolean useQuickActionOnAlembic() {
- return true;
- }
- @ConfigItem(
- keyName = "useQuickActionAgitator",
- name = "Use Quick Action on Agitator",
- description = "Will click once there is a quick action available on the agitator",
- position = 1,
- section = minigame
- )
- default boolean useQuickActionOnAgitator() {
- return true;
- }
-
- @ConfigItem(
- keyName = "useQuickActionRetort",
- name = "Use Quick Action on Retort",
- description = "Will click once there is a quick action available on the retort",
- position = 2,
- section = minigame
- )
- default boolean useQuickActionOnRetort() {
- return true;
- }
-
- @ConfigItem(
- keyName = "pickDigWeed",
- name = "Pick DigWeed",
- description = "Will pick digweed if available to increase points",
- position = 3,
- section = minigame
- )
- default boolean pickDigWeed() {
- return true;
- }
-
- @ConfigItem(
- keyName = "useQuickActionLever",
- name = "Use Quick Action on Lever",
- description = "Will click fast when interacting with the lever for mixing potions",
- position = 4,
- section = minigame
- )
- default boolean useQuickActionLever() {
- return true;
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/MixologyOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/MixologyOverlay.java
deleted file mode 100644
index d9ecaeb05f2..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/MixologyOverlay.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package net.runelite.client.plugins.microbot.mixology;
-
-import net.runelite.client.ui.overlay.OverlayLayer;
-import net.runelite.client.ui.overlay.OverlayPanel;
-import net.runelite.client.ui.overlay.OverlayPosition;
-import net.runelite.client.ui.overlay.components.LineComponent;
-import net.runelite.client.ui.overlay.components.TitleComponent;
-import net.runelite.client.ui.overlay.outline.ModelOutlineRenderer;
-
-import javax.inject.Inject;
-import java.awt.*;
-import java.time.Duration;
-
-public class MixologyOverlay extends OverlayPanel {
- private final MixologyPlugin plugin;
- private final ModelOutlineRenderer modelOutlineRenderer;
-
- @Inject
- MixologyOverlay(MixologyPlugin plugin, ModelOutlineRenderer modelOutlineRenderer) {
- this.plugin = plugin;
- this.modelOutlineRenderer = modelOutlineRenderer;
- this.setPosition(OverlayPosition.DYNAMIC);
- this.setLayer(OverlayLayer.ABOVE_SCENE);
- }
-
- public Dimension render(Graphics2D graphics) {
-
- panelComponent.setPreferredLocation(new Point(200, 20));
- panelComponent.setPreferredSize(new Dimension(300, 300));
- panelComponent.getChildren().add(TitleComponent.builder()
- .text("Micro Mixology V" + MixologyScript.version)
- .color(Color.GREEN)
- .build());
-
- panelComponent.getChildren().add(LineComponent.builder().build());
-
- panelComponent.getChildren().add(LineComponent.builder()
- .left("Mixology state")
- .right(MixologyScript.mixologyState.toString())
- .build());
-
- panelComponent.getChildren().add(LineComponent.builder()
- .left("Mox/Aga/Lye paste")
- .right(String.valueOf(MixologyScript.moxPasteAmount) + "/" + String.valueOf(MixologyScript.agaPasteAmount) + "/" + String.valueOf(MixologyScript.lyePasteAmount))
- .build());
-
- Duration runtime = plugin.mixologyScript.getRunTime();
-
- panelComponent.getChildren().add(LineComponent.builder()
- .left("Mox/Aga/Lye points per hour")
- .rightColor(Color.YELLOW)
- .right(calculatePointsPerHour(runtime.getSeconds()))
- .build());
-
- panelComponent.getChildren().add(LineComponent.builder()
- .left("Runtime")
- .rightColor(Color.GREEN)
- .right(String.format("%02d:%02d:%02d", runtime.toHours(), runtime.toMinutesPart(), runtime.toSecondsPart()))
- .build());
-
- for (MixologyPlugin.HighlightedObject highlightedObject : this.plugin.highlightedObjects().values()) {
- this.modelOutlineRenderer.drawOutline(highlightedObject.object(), highlightedObject.outlineWidth(), highlightedObject.color(), highlightedObject.feather());
- }
-
- return super.render(graphics);
- }
-
- private String calculatePointsPerHour(long seconds) {
- // int elapsedTimeInSeconds = 3600; // Time elapsed (e.g., 1 hour = 3600 seconds)
-
- // Convert time to hours
- double elapsedTimeInHours = seconds / 3600.0;
-
- int gainedMoxPoints = MixologyScript.currentMoxPoints - MixologyScript.startMoxPoints;
- int gainedLyePoints = MixologyScript.currentLyePoints - MixologyScript.startLyePoints;
- int gainedAgaPoints = MixologyScript.currentAgaPoints - MixologyScript.startAgaPoints;
-
- // Calculate experience per hour
- int moxPointsPerHour = (int) (gainedMoxPoints / elapsedTimeInHours);
- int lyePointsPerHour = (int) (gainedLyePoints / elapsedTimeInHours);
- int agaPointsPerHour = (int) (gainedAgaPoints / elapsedTimeInHours);
-
- if (moxPointsPerHour < 0)
- moxPointsPerHour = 0;
- if (lyePointsPerHour < 0)
- lyePointsPerHour = 0;
- if (agaPointsPerHour < 0)
- agaPointsPerHour = 0;
-
- return "" + moxPointsPerHour + "/" + agaPointsPerHour + "/" + lyePointsPerHour;
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/MixologyPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/MixologyPlugin.java
deleted file mode 100644
index c76f77912cc..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/MixologyPlugin.java
+++ /dev/null
@@ -1,344 +0,0 @@
-package net.runelite.client.plugins.microbot.mixology;
-
-import com.google.inject.Provides;
-import lombok.extern.slf4j.Slf4j;
-import net.runelite.api.Client;
-import net.runelite.api.GameState;
-import net.runelite.api.TileObject;
-import net.runelite.api.VarbitComposition;
-import net.runelite.api.events.*;
-import net.runelite.api.widgets.Widget;
-import net.runelite.client.callback.ClientThread;
-import net.runelite.client.config.ConfigManager;
-import net.runelite.client.eventbus.Subscribe;
-import net.runelite.client.plugins.Plugin;
-import net.runelite.client.plugins.PluginDescriptor;
-import net.runelite.client.ui.overlay.OverlayManager;
-import net.runelite.client.util.ColorUtil;
-
-import javax.inject.Inject;
-import java.awt.*;
-import java.util.List;
-import java.util.*;
-
-@PluginDescriptor(
- name = PluginDescriptor.Mocrosoft + "AutoMixology",
- description = "Mixology plugin",
- tags = {"herblore", "microbot", "mixology"},
- enabledByDefault = false
-)
-@Slf4j
-public class MixologyPlugin extends Plugin {
- @Inject
- private Client client;
- @Inject
- private MixologyConfig config;
- @Inject
- private OverlayManager overlayManager;
- @Inject
- private ClientThread clientThread;
- @Inject
- private MixologyOverlay overlay;
- @Inject
- private InventoryPotionOverlay potionOverlay;
- private final Map highlightedObjects = new LinkedHashMap();
- private boolean inLab = false;
- private PotionType alembicPotionType;
- private PotionType agitatorPotionType;
- private PotionType retortPotionType;
-
- @Inject
- MixologyScript mixologyScript;
-
- public MixologyPlugin() {
- }
-
- public Map highlightedObjects() {
- return this.highlightedObjects;
- }
-
- @Provides
- MixologyConfig provideConfig(ConfigManager configManager) {
- return (MixologyConfig) configManager.getConfig(MixologyConfig.class);
- }
-
- protected void startUp() {
- mixologyScript.run(config);
- this.overlayManager.add(this.overlay);
- this.overlayManager.add(this.potionOverlay);
- if (this.client.getGameState() == GameState.LOGGED_IN) {
- this.clientThread.invokeLater(this::initialize);
- }
-
- }
-
- protected void shutDown() {
- mixologyScript.shutdown();
- this.overlayManager.remove(this.overlay);
- this.overlayManager.remove(this.potionOverlay);
- this.inLab = false;
- }
-
- @Subscribe
- public void onGameStateChanged(GameStateChanged event) {
- if (event.getGameState() == GameState.LOGIN_SCREEN || event.getGameState() == GameState.HOPPING) {
- this.highlightedObjects.clear();
- }
-
- }
-
- @Subscribe
- public void onWidgetLoaded(WidgetLoaded event) {
- if (event.getGroupId() == 882) {
- this.initialize();
- }
- }
-
- @Subscribe
- public void onWidgetClosed(WidgetClosed event) {
- if (event.getGroupId() == 882) {
- this.highlightedObjects.clear();
- this.inLab = false;
- }
- }
-
- @Subscribe
- public void onVarbitChanged(VarbitChanged event) {
- int varbitId = event.getVarbitId();
- int value = event.getValue();
- if (varbitId == 11315) {
- if (value == 0) {
- // this.unHighlightAllStations();
- } else {
- this.clientThread.invokeAtTickEnd(this::updatePotionOrders);
- }
- } else if (varbitId == 11342) {
- if (value == 0) {
- this.tryFulfillOrder(this.alembicPotionType, PotionModifier.CRYSTALISED);
- this.alembicPotionType = null;
- } else {
- this.alembicPotionType = PotionType.fromIdx(value - 1);
- }
- } else if (varbitId == 11340) {
- if (value == 0) {
- this.tryFulfillOrder(this.agitatorPotionType, PotionModifier.HOMOGENOUS);
- this.agitatorPotionType = null;
- } else {
- this.agitatorPotionType = PotionType.fromIdx(value - 1);
- }
- } else if (varbitId == 11341) {
- if (value == 0) {
- this.tryFulfillOrder(this.retortPotionType, PotionModifier.CONCENTRATED);
- this.retortPotionType = null;
- } else {
- this.retortPotionType = PotionType.fromIdx(value - 1);
- }
- } else if (varbitId == 11330) {
- if (value == 1) {
- mixologyScript.digweed = AlchemyObject.DIGWEED_NORTH_EAST;
- } else {
- mixologyScript.digweed = null;
- }
- } else if (varbitId == 11331) {
- if (value == 1) {
- mixologyScript.digweed = AlchemyObject.DIGWEED_SOUTH_EAST;
- } else {
- mixologyScript.digweed = null;
- }
- } else if (varbitId == 11332) {
- if (value == 1) {
- mixologyScript.digweed = AlchemyObject.DIGWEED_SOUTH_WEST;
- } else {
- mixologyScript.digweed = null;
- }
- } else if (varbitId == 11333) {
- if (value == 1) {
- mixologyScript.digweed = AlchemyObject.DIGWEED_NORTH_WEST;
- } else {
- mixologyScript.digweed = null;
- }
- } else if (varbitId == 11329) {
- if (mixologyScript.agitatorQuickActionTicks == 2) {
- mixologyScript.agitatorQuickActionTicks = 0;
- }
-
- if (mixologyScript.agitatorQuickActionTicks == 1) {
- mixologyScript.agitatorQuickActionTicks = 2;
- }
- } else if (varbitId == 11328) {
- if (mixologyScript.alembicQuickActionTicks == 1) {
- mixologyScript.alembicQuickActionTicks = 0;
- }
- }
- }
-
- @Subscribe
- public void onGraphicsObjectCreated(GraphicsObjectCreated event) {
- int spotAnimId = event.getGraphicsObject().getId();
- if (spotAnimId == 2955 && this.alembicPotionType != null) {
- mixologyScript.alembicQuickActionTicks = 1;
- }
-
- if (spotAnimId == 2954 && this.agitatorPotionType != null) {
- mixologyScript.agitatorQuickActionTicks = 1;
- }
- }
-
- @Subscribe
- public void onScriptPostFired(ScriptPostFired event) {
- int scriptId = event.getScriptId();
- if (scriptId == 7063 || scriptId == 7064) {
- Widget baseWidget = this.client.getWidget(57802754);
- if (baseWidget != null) {
- if (scriptId == 7063) {
- this.updatePotionOrdersComponent(baseWidget);
- } else {
- this.appendResins(baseWidget);
- }
-
- }
- }
- }
-
- private void updatePotionOrdersComponent(Widget baseWidget) {
- Widget[] children = baseWidget.getChildren();
- if (children != null) {
- for (int i = 0; i < mixologyScript.potionOrders.size(); ++i) {
- PotionOrder order = mixologyScript.potionOrders.get(i);
- Widget orderGraphic = children[order.idx() * 2 + 1];
- Widget orderText = children[order.idx() * 2 + 2];
- if (orderGraphic.getType() == 5 && orderText.getType() == 4) {
- StringBuilder builder = new StringBuilder(orderText.getText());
- if (order.fulfilled()) {
- builder.append(" (done!)");
- } else {
- builder.append(" (").append(order.potionType().recipe()).append(")");
- }
-
- orderText.setText(builder.toString());
- if (i != order.idx()) {
- int y = 20 + i * 26 + 3;
- orderGraphic.setOriginalY(y);
- orderText.setOriginalY(y);
- orderGraphic.revalidate();
- orderText.revalidate();
- }
- }
- }
-
- }
- }
-
- private void appendResins(Widget baseWidget) {
- int parentWidth = baseWidget.getWidth();
- int dx = parentWidth / 3;
- int x = dx / 2;
- this.addResinText(baseWidget.createChild(-1, 4), x, 4416, PotionComponent.MOX);
- this.addResinText(baseWidget.createChild(-1, 4), x + dx, 4415, PotionComponent.AGA);
- this.addResinText(baseWidget.createChild(-1, 4), x + dx * 2, 4414, PotionComponent.LYE);
- }
-
- private void initialize() {
- Widget ordersLayer = this.client.getWidget(882, 0);
- if (ordersLayer != null && !ordersLayer.isSelfHidden()) {
- this.inLab = true;
- this.updatePotionOrders();
- }
- }
-
- private void updatePotionOrders() {
- System.out.println("Updating potion orders");
- mixologyScript.potionOrders = this.getPotionOrders();
- // Desired order: CRYSTALISED > CONCENTRATED > HOMOGENOUS
-
- mixologyScript.potionOrders.sort(Comparator.comparingInt(mixologyScript.customOrder::indexOf));
-
- VarbitComposition varbitType = this.client.getVarbit(11315);
- if (varbitType != null) {
- this.client.queueChangedVarp(varbitType.getIndex());
- }
-
- }
-
- private void addResinText(Widget widget, int x, int varp, PotionComponent component) {
- int amount = this.client.getVarpValue(varp);
- int color = ColorUtil.fromHex(component.color()).getRGB();
- widget.setText("" + amount).setTextShadowed(true).setTextColor(color).setOriginalWidth(20).setOriginalHeight(15).setFontId(497).setOriginalY(0).setOriginalX(x).setYPositionMode(2).setXTextAlignment(1).setYTextAlignment(1);
- widget.revalidate();
- }
-
- private void tryFulfillOrder(PotionType potionType, PotionModifier modifier) {
- for (PotionOrder order : mixologyScript.potionOrders) {
- if (order.potionType() == potionType && order.potionModifier() == modifier && !order.fulfilled()) {
- order.setFulfilled(true);
- break;
- }
- }
-
- }
-
- private List getPotionOrders() {
- ArrayList potionOrders = new ArrayList(3);
-
- for (int orderIdx = 0; orderIdx < 3; ++orderIdx) {
- PotionType potionType = this.getPotionType(orderIdx);
- PotionModifier potionModifier = this.getPotionModifier(orderIdx);
- if (potionType != null && potionModifier != null) {
- potionOrders.add(new PotionOrder(orderIdx, potionType, potionModifier));
- }
- }
-
- return potionOrders;
- }
-
- private PotionType getPotionType(int orderIdx) {
- if (orderIdx == 0) {
- return PotionType.fromIdx(this.client.getVarbitValue(11315) - 1);
- } else if (orderIdx == 1) {
- return PotionType.fromIdx(this.client.getVarbitValue(11317) - 1);
- } else {
- return orderIdx == 2 ? PotionType.fromIdx(this.client.getVarbitValue(11319) - 1) : null;
- }
- }
-
- private PotionModifier getPotionModifier(int orderIdx) {
- if (orderIdx == 0) {
- return PotionModifier.from(this.client.getVarbitValue(11316) - 1);
- } else if (orderIdx == 1) {
- return PotionModifier.from(this.client.getVarbitValue(11318) - 1);
- } else {
- return orderIdx == 2 ? PotionModifier.from(this.client.getVarbitValue(11320) - 1) : null;
- }
- }
-
- public static class HighlightedObject {
- private final TileObject object;
- private final Color color;
- private final int outlineWidth;
- private final int feather;
-
- private HighlightedObject(TileObject object, Color color, int outlineWidth, int feather) {
- this.object = object;
- this.color = color;
- this.outlineWidth = outlineWidth;
- this.feather = feather;
- }
-
- public TileObject object() {
- return this.object;
- }
-
- public Color color() {
- return this.color;
- }
-
- public int outlineWidth() {
- return this.outlineWidth;
- }
-
- public int feather() {
- return this.feather;
- }
- }
-
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/MixologyScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/MixologyScript.java
deleted file mode 100644
index 54a32b532ec..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/MixologyScript.java
+++ /dev/null
@@ -1,466 +0,0 @@
-package net.runelite.client.plugins.microbot.mixology;
-
-import net.runelite.api.DynamicObject;
-import net.runelite.api.GameObject;
-import net.runelite.api.gameval.ItemID;
-import net.runelite.api.gameval.ObjectID;
-
-
-import net.runelite.api.coords.WorldPoint;
-import net.runelite.client.plugins.microbot.Microbot;
-import net.runelite.client.plugins.microbot.Script;
-import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings;
-import net.runelite.client.plugins.microbot.util.bank.Rs2Bank;
-import net.runelite.client.plugins.microbot.util.gameobject.Rs2GameObject;
-import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory;
-import net.runelite.client.plugins.microbot.util.math.Rs2Random;
-import net.runelite.client.plugins.microbot.util.player.Rs2Player;
-import net.runelite.client.plugins.microbot.util.walker.Rs2Walker;
-import net.runelite.client.plugins.microbot.util.widget.Rs2Widget;
-
-import java.util.*;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
-import static net.runelite.client.plugins.microbot.mixology.AlchemyObject.MIXING_VESSEL;
-
-
-public class MixologyScript extends Script {
-
- public final static String version = "1.0.2-beta";
- private static final Integer DIGWEED = ItemID.MM_LAB_SPECIAL_HERB;
-
- public java.util.List potionOrders = Collections.emptyList();
-
- public static MixologyState mixologyState = MixologyState.IDLE;
- public static int lyePasteAmount, agaPasteAmount, moxPasteAmount = 0;
- public static int startLyePoints, startAgaPoints, startMoxPoints = 0;
- public static int currentLyePoints, currentAgaPoints, currentMoxPoints = 0;
- public int agitatorQuickActionTicks = 0;
- public int alembicQuickActionTicks = 0;
- public AlchemyObject digweed;
- public int leverRetries = 0;
- public List customOrder = Arrays.asList(
- PotionModifier.CRYSTALISED,
- PotionModifier.CONCENTRATED,
- PotionModifier.HOMOGENOUS
- );
-
- public boolean run(MixologyConfig config) {
- Microbot.enableAutoRunOn = false;
- currentMoxPoints = 0;
- currentAgaPoints = 0;
- currentLyePoints = 0;
- leverRetries = 0;
- if (!Rs2AntibanSettings.naturalMouse) {
- Microbot.log("Hey! Did you know this script works really well with natural mouse? Feel free to enable it in the antiban settings.");
- }
- mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> {
- try {
- if (!Microbot.isLoggedIn()) return;
- if (!super.run()) return;
- long startTime = System.currentTimeMillis();
-
- if (leverRetries >= 20) {
- Microbot.log("Failed to create a potion. Please do this step manually and restart the script.");
- return;
- }
-
-
- boolean isInMinigame = Rs2Widget.getWidget(882, 2) != null;
-
-
- if (!isInMinigame && mixologyState != MixologyState.REFINER) {
- Rs2Walker.walkTo(1395, 9322, 0, 2);
- return;
- }
-
- if (isInMinigame) {
-
- if (startLyePoints == 0 && startAgaPoints == 0 && startMoxPoints == 0) {
- startMoxPoints = getMoxPoints();
- startAgaPoints = getAgaPoints();
- startLyePoints = getLyePoints();
- }
-
- if (digweed != null && !Rs2Player.isAnimating() && !Rs2Inventory.hasItem(DIGWEED)
- && config.pickDigWeed()) {
- Rs2GameObject.interact(digweed.coordinate());
- Rs2Player.waitForWalking();
- Rs2Player.waitForAnimation();
- return;
- }
-
- if (Rs2Inventory.hasItem(DIGWEED) && !Rs2Player.isAnimating()) {
- Optional potionItemId = potionOrders
- .stream()
- .filter(x -> !x.fulfilled() && Rs2Inventory.hasItem(x.potionType().itemId()))
- .map(x -> x.potionType().itemId())
- .findFirst();
- if (potionItemId.isPresent()) {
- Rs2Inventory.interact(DIGWEED, "use");
- Rs2Inventory.interact(potionItemId.get(), "use");
- Rs2Player.waitForAnimation();
- return;
- }
- }
-
- moxPasteAmount = Integer.parseInt(Rs2Widget.getWidget(882, 2).getDynamicChildren()[8].getText()) + Rs2Inventory.itemQuantity(ItemID.MM_MOX_PASTE);
- agaPasteAmount = Integer.parseInt(Rs2Widget.getWidget(882, 2).getDynamicChildren()[11].getText()) + Rs2Inventory.itemQuantity(ItemID.MM_AGA_PASTE);
- lyePasteAmount = Integer.parseInt(Rs2Widget.getWidget(882, 2).getDynamicChildren()[14].getText()) + Rs2Inventory.itemQuantity(ItemID.MM_LYE_PASTE);
-
- if (mixologyState != MixologyState.REFINER && (moxPasteAmount < 100 || agaPasteAmount < 100 || lyePasteAmount < 100)) {
- mixologyState = MixologyState.REFINER;
- } else if (Rs2Inventory.hasItem(ItemID.MM_MOX_PASTE) || Rs2Inventory.hasItem(ItemID.MM_LYE_PASTE) || Rs2Inventory.hasItem(ItemID.MM_AGA_PASTE)) {
- if (Integer.parseInt(Rs2Widget.getWidget(882, 2).getDynamicChildren()[8].getText()) >= 3000 && Rs2Inventory.hasItem(ItemID.MM_MOX_PASTE)) {
- mixologyState = MixologyState.BANK;
- } else if (Integer.parseInt(Rs2Widget.getWidget(882, 2).getDynamicChildren()[11].getText()) >= 3000 && Rs2Inventory.hasItem(ItemID.MM_AGA_PASTE)) {
- mixologyState = MixologyState.BANK;
-
- } else if (Integer.parseInt(Rs2Widget.getWidget(882, 2).getDynamicChildren()[14].getText()) >= 3000 && Rs2Inventory.hasItem(ItemID.MM_LYE_PASTE)) {
- mixologyState = MixologyState.BANK;
- } else {
- mixologyState = MixologyState.DEPOSIT_HOPPER;
- }
- }
- }
-
- if (mixologyState == MixologyState.IDLE) {
- mixologyState = MixologyState.MIX_POTION_STAGE_1;
- }
-
- if (hasAllFulFilledItems()) {
- mixologyState = MixologyState.CONVEYER_BELT;
- }
-
- switch (mixologyState) {
- case BANK:
- if (Rs2Inventory.hasItem("paste")) {
- if (Rs2Bank.openBank()) {
- Rs2Bank.depositAll();
- }
- return;
- }
- mixologyState = MixologyState.MIX_POTION_STAGE_1;
- break;
- case REFINER:
- String herb = "";
- WorldPoint bankLocation = new WorldPoint(1398, 9313, 0);
- if (Rs2Player.getWorldLocation().distanceTo(bankLocation) > 10) {
- Rs2Walker.walkTo(bankLocation);
- return;
- }
-
- if (Rs2Inventory.hasItem(config.agaHerb().toString()) || Rs2Inventory.hasItem(config.lyeHerb().toString()) || Rs2Inventory.hasItem(config.moxHerb().toString())) {
- Rs2GameObject.interact(ObjectID.MM_LAB_MILL);
- Rs2Player.waitForAnimation();
- sleepGaussian(450, 150);
- if (!config.useQuickActionRefiner()) {
- sleepUntil(() -> !Microbot.isGainingExp, 30000);
- }
- return;
- }
- if (Rs2Bank.openBank()) {
- sleepUntil(Rs2Bank::isOpen);
- moxPasteAmount = Rs2Bank.count(ItemID.MM_MOX_PASTE);
- lyePasteAmount = Rs2Bank.count(ItemID.MM_LYE_PASTE);
- agaPasteAmount = Rs2Bank.count(ItemID.MM_AGA_PASTE);
- if (moxPasteAmount < config.amtMoxHerb()) {
- herb = config.moxHerb().toString();
- } else if (lyePasteAmount < config.amtLyeHerb()) {
- herb = config.lyeHerb().toString();
- } else if (agaPasteAmount < config.amtAgaHerb()) {
- herb = config.agaHerb().toString();
- } else {
- if (Rs2Bank.openBank()) {
- Rs2Bank.depositAll();
- Rs2Bank.withdrawAll(ItemID.MM_MOX_PASTE);
- Rs2Bank.withdrawAll(ItemID.MM_LYE_PASTE);
- Rs2Bank.withdrawAll(ItemID.MM_AGA_PASTE);
- mixologyState = MixologyState.DEPOSIT_HOPPER;
- return;
- }
- }
- Rs2Bank.depositAll();
- if (!Rs2Bank.hasItem(herb, true)) {
- Microbot.showMessage("Failed to find " + herb + " in your bank. Shutting down script...");
- shutdown();
- return;
- }
- Rs2Bank.withdrawAll(herb, true);
- Rs2Bank.closeBank();
- sleepGaussian(600, 150);
- }
- break;
- case DEPOSIT_HOPPER:
- if (Rs2GameObject.interact(ObjectID.MM_LAB_HOPPER)) {
- Rs2Player.waitForWalking();
- Rs2Inventory.waitForInventoryChanges(10000);
- mixologyState = MixologyState.MIX_POTION_STAGE_1;
- }
- break;
- case MIX_POTION_STAGE_1:
-
- Map itemsToCheck = new HashMap<>();
- PotionOrder potionToMake = null;
-
- for (PotionOrder _potionOrder : potionOrders) {
- int key = _potionOrder.potionType().itemId();
- int value = itemsToCheck.getOrDefault(key, 0);
- itemsToCheck.put(key, value + 1);
- }
-
- for (int itemId : itemsToCheck.keySet()) {
- PotionOrder _potionOrder = potionOrders
- .stream()
- .filter(x -> x.potionType().itemId() == itemId)
- .findFirst()
- .orElse(null);
-
- if (_potionOrder == null) continue;
-
- int itemAmount = itemsToCheck.getOrDefault(itemId, 1);
-
- if (!Rs2Inventory.hasItemAmount(itemId, itemAmount)) {
- potionToMake = _potionOrder;
- }
- }
-
- if (potionToMake == null) {
- mixologyState = MixologyState.MIX_POTION_STAGE_2;
- return;
- }
-
- if (canCreatePotion(potionToMake)) {
- mixologyState = MixologyState.TAKE_FROM_MIXIN_VESSEL;
- leverRetries = 0;
- } else {
- createPotion(potionToMake, config);
- }
- break;
- case TAKE_FROM_MIXIN_VESSEL:
- Rs2GameObject.interact(MIXING_VESSEL.objectId());
- boolean result = Rs2Inventory.waitForInventoryChanges(5000);
- if (result) {
- mixologyState = MixologyState.MIX_POTION_STAGE_1;
- }
- break;
- case MIX_POTION_STAGE_2:
-
- // Sort using a custom comparator
- List nonFulfilledPotions = potionOrders
- .stream()
- .filter(x -> !x.fulfilled())
- .sorted(Comparator.comparingInt(customOrder::indexOf))
- .collect(Collectors.toList());
-
- if (nonFulfilledPotions.isEmpty()) {
- mixologyState = MixologyState.CONVEYER_BELT;
- return;
- }
-
- PotionOrder nonFulfilledPotion = nonFulfilledPotions.get(0);
-
- if (Rs2Player.isAnimating()) {
- if (agitatorQuickActionTicks > 0 && config.useQuickActionOnAgitator()) {
- int clicks = Rs2AntibanSettings.naturalMouse ? Rs2Random.between(4, 6) : Rs2Random.between(6, 10);
- for (int i = 0; i < clicks; i++) {
- quickActionProcessPotion(nonFulfilledPotion);
- }
- agitatorQuickActionTicks = 0;
- } else if (alembicQuickActionTicks > 0 && config.useQuickActionOnAlembic()) {
- quickActionProcessPotion(nonFulfilledPotion);
- alembicQuickActionTicks = 0;
- }
- if (nonFulfilledPotion.potionModifier().alchemyObject() == AlchemyObject.RETORT && config.useQuickActionOnRetort()&&Microbot.getVarbitValue(11327)<15&&Microbot.getVarbitValue(11327)!=0) {
- quickActionProcessPotion(nonFulfilledPotion);
- sleep(350, 400);
- }
- return;
- }
-
- if (nonFulfilledPotion == null || !Rs2Inventory.hasItem(nonFulfilledPotion.potionType().itemId())) {
- mixologyState = MixologyState.MIX_POTION_STAGE_1;
- return;
- }
-
- processPotion(nonFulfilledPotion);
- sleepUntil(Rs2Player::isAnimating);
- break;
- case CONVEYER_BELT:
- if (potionOrders.stream().noneMatch(x -> Rs2Inventory.hasItem(x.potionType().getFulfilledItemId()))) {
- mixologyState = MixologyState.MIX_POTION_STAGE_1;
- return;
- }
- if (Rs2GameObject.interact(AlchemyObject.CONVEYOR_BELT.objectId())) {
- Rs2Inventory.waitForInventoryChanges(5000);
- currentAgaPoints = getAgaPoints();
- currentLyePoints = getLyePoints();
- currentMoxPoints = getMoxPoints();
- }
- break;
- }
-
-
- long endTime = System.currentTimeMillis();
- long totalTime = endTime - startTime;
- System.out.println("Total time for loop " + totalTime);
-
- } catch (Exception ex) {
- System.out.println(ex.getMessage());
- }
- }, 0, 100, TimeUnit.MILLISECONDS);
- return true;
- }
-
- private boolean hasAllFulFilledItems() {
- Map itemsToCheck = new HashMap<>();
- boolean hasAllFulFilledItems = true;
-
- for (PotionOrder _potionOrder : potionOrders) {
- int key = _potionOrder.potionType().getFulfilledItemId();
- int value = itemsToCheck.getOrDefault(key, 0);
- itemsToCheck.put(key, value + 1);
- }
-
- for (int itemId : itemsToCheck.keySet()) {
- PotionOrder _potionOrder = potionOrders
- .stream()
- .filter(x -> x.potionType().getFulfilledItemId() == itemId)
- .findFirst()
- .orElse(null);
-
- if (_potionOrder == null) continue;
-
- int itemAmount = itemsToCheck.getOrDefault(itemId, 1);
-
- if (!Rs2Inventory.hasItemAmount(itemId, itemAmount)) {
- hasAllFulFilledItems = false;
- }
- }
- return hasAllFulFilledItems;
- }
-
- private static void processPotion(PotionOrder nonFulfilledPotion) {
- switch (nonFulfilledPotion.potionModifier()) {
- case HOMOGENOUS:
- GameObject agitator = (GameObject) Rs2GameObject.findObjectById(AlchemyObject.AGITATOR.objectId());
- if (agitator != null && (((DynamicObject) agitator.getRenderable()).getAnimation().getId() == 11633 || ((DynamicObject) agitator.getRenderable()).getAnimation().getId() == 11632)) {
- Rs2GameObject.interact(AlchemyObject.AGITATOR.objectId());
- } else {
- Rs2Inventory.useItemOnObject(nonFulfilledPotion.potionType().itemId(), AlchemyObject.AGITATOR.objectId());
- }
- break;
- case CONCENTRATED:
- GameObject retort = (GameObject) Rs2GameObject.findObjectById(AlchemyObject.RETORT.objectId());
- if (retort != null && (((DynamicObject) retort.getRenderable()).getAnimation().getId() == 11643 || ((DynamicObject) retort.getRenderable()).getAnimation().getId() == 11642)) {
- Rs2GameObject.interact(AlchemyObject.RETORT.objectId());
- } else {
- Rs2Inventory.useItemOnObject(nonFulfilledPotion.potionType().itemId(), AlchemyObject.RETORT.objectId());
- }
- break;
- case CRYSTALISED:
- GameObject alembic = (GameObject) Rs2GameObject.findObjectById(AlchemyObject.ALEMBIC.objectId());
- if (alembic != null && (((DynamicObject) alembic.getRenderable()).getAnimation().getId() == 11638 || ((DynamicObject) alembic.getRenderable()).getAnimation().getId() == 11637)) {
- Rs2GameObject.interact(AlchemyObject.ALEMBIC.objectId());
- } else {
- Rs2Inventory.useItemOnObject(nonFulfilledPotion.potionType().itemId(), AlchemyObject.ALEMBIC.objectId());
- }
- break;
- }
- }
-
- private static void quickActionProcessPotion(PotionOrder nonFulfilledPotion) {
- switch (nonFulfilledPotion.potionModifier()) {
- case HOMOGENOUS:
- Rs2GameObject.interact(AlchemyObject.AGITATOR.objectId());
- break;
- case CONCENTRATED:
- Rs2GameObject.interact(AlchemyObject.RETORT.objectId());
- break;
- case CRYSTALISED:
- Rs2GameObject.interact(AlchemyObject.ALEMBIC.objectId());
- break;
- }
- }
-
- private void createPotion(PotionOrder potionOrder, MixologyConfig config) {
- for (PotionComponent component : potionOrder.potionType().components()) {
- if (canCreatePotion(potionOrder)) break;
- if (component.character() == 'A') {
- Rs2GameObject.interact(AlchemyObject.AGA_LEVER.objectId());
- } else if (component.character() == 'L') {
- Rs2GameObject.interact(AlchemyObject.LYE_LEVER.objectId());
- } else if (component.character() == 'M') {
- Rs2GameObject.interact(AlchemyObject.MOX_LEVER.objectId());
- }
- if (config.useQuickActionLever()) {
- Rs2Player.waitForAnimation();
- } else {
- sleepUntil(Rs2Player::isAnimating);
- final int sleep = Rs2Random.between(300, 600);
- sleepGaussian(sleep, sleep / 4);
- }
- leverRetries++;
- }
- }
-
- private boolean canCreatePotion(PotionOrder potionOrder) {
- // Get the mixer game objects
- GameObject[] mixers = {
- (GameObject) Rs2GameObject.findObjectById(ObjectID.MM_LAB_MIXER_03), // mixer3
- (GameObject) Rs2GameObject.findObjectById(ObjectID.MM_LAB_MIXER_02), // mixer2
- (GameObject) Rs2GameObject.findObjectById(ObjectID.MM_LAB_MIXER_01) // mixer1
- };
-
- // Check if any mixers are missing
- if (Arrays.stream(mixers).anyMatch(Objects::isNull)) {
- return false;
- }
-
- // Get animations in correct order
- int[] currentAnimations = Arrays.stream(mixers)
- .map(mixer -> ((DynamicObject) mixer.getRenderable()).getAnimation().getId())
- .mapToInt(Integer::intValue)
- .toArray();
-
- // Map components to their valid animations
- Map componentAnimations = Map.of(
- 'A', new int[]{11615, 11609, 11612}, // AGA animations
- 'M', new int[]{11617, 11614, 11607}, // MOX animations
- 'L', new int[]{11608, 11611, 11618} // LYE animations
- );
-
- // Check each position
- for (int i = 0; i < potionOrder.potionType().components().length; i++) {
- char expectedComponent = potionOrder.potionType().components()[i].character();
- int currentAnimation = currentAnimations[i];
-
- boolean isValid = Arrays.stream(componentAnimations.get(expectedComponent))
- .anyMatch(validAnim -> validAnim == currentAnimation);
-
- if (!isValid) {
- return false;
- }
- }
-
- return true;
- }
-
- private int getMoxPoints() {
- return Integer.parseInt(Rs2Widget.getWidget(882, 2).getDynamicChildren()[16].getText());
- }
-
- private int getAgaPoints() {
- return Integer.parseInt(Rs2Widget.getWidget(882, 2).getDynamicChildren()[17].getText());
- }
-
- private int getLyePoints() {
- return Integer.parseInt(Rs2Widget.getWidget(882, 2).getDynamicChildren()[18].getText());
- }
-
- @Override
- public void shutdown() {
- super.shutdown();
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/MixologyState.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/MixologyState.java
deleted file mode 100644
index 44c7dcd7c74..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/MixologyState.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package net.runelite.client.plugins.microbot.mixology;
-
-public enum MixologyState {
- IDLE,
- BANK,
- REFINER,
- DEPOSIT_HOPPER,
- MIX_POTION_STAGE_1,
- TAKE_FROM_MIXIN_VESSEL,
- MIX_POTION_STAGE_2,
- CONVEYER_BELT
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/MoxHerbs.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/MoxHerbs.java
deleted file mode 100644
index cad04822f89..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/MoxHerbs.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package net.runelite.client.plugins.microbot.mixology;
-
-import lombok.RequiredArgsConstructor;
-
-@RequiredArgsConstructor
-public enum MoxHerbs {
- GUAM("guam leaf"),
- Marrentill("marrentill"),
- Tarromin("tarromin"),
- Harralander("harralander");
-
- private final String itemName;
-
- @Override
- public String toString() {
- return itemName;
- }
-}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/PotionComponent.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/PotionComponent.java
deleted file mode 100644
index 22fcc672fa5..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/PotionComponent.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package net.runelite.client.plugins.microbot.mixology;
-
-
-public enum PotionComponent {
- AGA('A', "00e676"),
- LYE('L', "e91e63"),
- MOX('M', "03a9f4");
-
- private final char character;
- private final String color;
-
- private PotionComponent(char character, String color) {
- this.character = character;
- this.color = color;
- }
-
- public char character() {
- return this.character;
- }
-
- public String color() {
- return this.color;
- }
-}
-
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/PotionModifier.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/PotionModifier.java
deleted file mode 100644
index 4b8c93d149a..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/PotionModifier.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package net.runelite.client.plugins.microbot.mixology;
-
-public enum PotionModifier {
- HOMOGENOUS(AlchemyObject.AGITATOR, 21),
- CONCENTRATED(AlchemyObject.RETORT, 20),
- CRYSTALISED(AlchemyObject.ALEMBIC, 14);
-
- private static final PotionModifier[] TYPES = values();
- private final AlchemyObject alchemyObject;
- private final int quickActionExperience;
-
- private PotionModifier(AlchemyObject alchemyObject, int quickActionExperience) {
- this.alchemyObject = alchemyObject;
- this.quickActionExperience = quickActionExperience;
- }
-
- public static PotionModifier from(int potionModifierId) {
- return potionModifierId >= 0 && potionModifierId < TYPES.length ? TYPES[potionModifierId] : null;
- }
-
- public AlchemyObject alchemyObject() {
- return this.alchemyObject;
- }
-
- public int quickActionExperience() {
- return this.quickActionExperience;
- }
-}
\ No newline at end of file
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/PotionOrder.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/PotionOrder.java
deleted file mode 100644
index 4f0fa581c64..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/PotionOrder.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package net.runelite.client.plugins.microbot.mixology;
-
-public class PotionOrder {
- private final int idx;
- private final PotionType potionType;
- private final PotionModifier potionModifier;
- private boolean fulfilled;
-
- public PotionOrder(int idx, PotionType potionType, PotionModifier potionModifier) {
- this.idx = idx;
- this.potionType = potionType;
- this.potionModifier = potionModifier;
- }
-
- public int idx() {
- return this.idx;
- }
-
- public PotionType potionType() {
- return this.potionType;
- }
-
- public PotionModifier potionModifier() {
- return this.potionModifier;
- }
-
- public void setFulfilled(boolean fulfilled) {
- this.fulfilled = fulfilled;
- }
-
- public boolean fulfilled() {
- return this.fulfilled;
- }
-
- public String toString() {
- return "PotionOrder{idx=" + this.idx + ", potionType=" + this.potionType + ", potionModifier=" + this.potionModifier + "}";
- }
-}
\ No newline at end of file
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/PotionType.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/PotionType.java
deleted file mode 100644
index e4234fb2ddc..00000000000
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mixology/PotionType.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package net.runelite.client.plugins.microbot.mixology;
-
-import com.google.common.collect.ImmutableMap;
-import lombok.Getter;
-import net.runelite.api.gameval.ItemID;
-
-import java.util.Arrays;
-import java.util.Map;
-
-public enum PotionType {
- MAMMOTH_MIGHT_MIX(ItemID.MM_POTION_MMM_UNFINISHED, ItemID.MM_POTION_MMM_FINISHED, 1900, new PotionComponent[]{PotionComponent.MOX, PotionComponent.MOX, PotionComponent.MOX}),
- MYSTIC_MANA_AMALGAM(ItemID.MM_POTION_MMA_UNFINISHED, ItemID.MM_POTION_MMA_FINISHED, 2150, new PotionComponent[]{PotionComponent.MOX, PotionComponent.MOX, PotionComponent.AGA}),
- MARLEYS_MOONLIGHT(ItemID.MM_POTION_MML_UNFINISHED, ItemID.MM_POTION_MML_FINISHED, 2400, new PotionComponent[]{PotionComponent.MOX, PotionComponent.MOX, PotionComponent.LYE}),
- ALCO_AUGMENTATOR(ItemID.MM_POTION_AAA_UNFINISHED, ItemID.MM_POTION_AAA_FINISHED, 1900, new PotionComponent[]{PotionComponent.AGA, PotionComponent.AGA, PotionComponent.AGA}),
- AZURE_AURA_MIX(ItemID.MM_POTION_AAM_UNFINISHED, ItemID.MM_POTION_AAM_FINISHED, 2650, new PotionComponent[]{PotionComponent.AGA, PotionComponent.AGA, PotionComponent.MOX}),
- AQUALUX_AMALGAM(ItemID.MM_POTION_AAL_UNFINISHED, ItemID.MM_POTION_AAL_FINISHED, 2900, new PotionComponent[]{PotionComponent.AGA, PotionComponent.LYE, PotionComponent.AGA}),
- LIPLACK_LIQUOR(ItemID.MM_POTION_LLL_UNFINISHED, ItemID.MM_POTION_LLL_FINISHED, 1900, new PotionComponent[]{PotionComponent.LYE, PotionComponent.LYE, PotionComponent.LYE}),
- MEGALITE_LIQUID(ItemID.MM_POTION_LLM_UNFINISHED, ItemID.MM_POTION_LLM_FINISHED, 3150, new PotionComponent[]{PotionComponent.MOX, PotionComponent.LYE, PotionComponent.LYE}),
- ANTI_LEECH_LOTION(ItemID.MM_POTION_LLA_UNFINISHED, ItemID.MM_POTION_LLA_FINISHED, 3400, new PotionComponent[]{PotionComponent.AGA, PotionComponent.LYE, PotionComponent.LYE}),
- MIXALOT(ItemID.MM_POTION_MAL_UNFINISHED, ItemID.MM_POTION_MAL_FINISHED, 3650, new PotionComponent[]{PotionComponent.MOX, PotionComponent.AGA, PotionComponent.LYE});
-
- public static final PotionType[] TYPES = values();
- private static final Map ITEM_MAP;
- private final int itemId;
- @Getter
- private final int fulfilledItemId;
- private final String recipe;
- private final String abbreviation;
- private final int experience;
- private final PotionComponent[] components;
-
- PotionType(int itemId, int fulfilledItemId, int experience, PotionComponent... components) {
- this.itemId = itemId;
- this.fulfilledItemId = fulfilledItemId;
- this.recipe = colorizeRecipe(components);
- this.experience = experience;
- this.components = components;
- this.abbreviation = "" + components[0].character() + components[1].character() + components[2].character();
- }
-
- public static PotionType fromItemId(int itemId) {
- return (PotionType)ITEM_MAP.get(itemId);
- }
-
- public static PotionType fromIdx(int potionTypeId) {
- return potionTypeId >= 0 && potionTypeId < TYPES.length ? TYPES[potionTypeId] : null;
- }
-
- private static String colorizeRecipe(PotionComponent[] components) {
- if (components.length != 3) {
- throw new IllegalArgumentException("Invalid potion components: " + Arrays.toString(components));
- } else {
- String var10000 = colorizeRecipeComponent(components[0]);
- return var10000 + colorizeRecipeComponent(components[1]) + colorizeRecipeComponent(components[2]);
- }
- }
-
- private static String colorizeRecipeComponent(PotionComponent component) {
- return "" + component.character() + "";
- }
-
- public int itemId() {
- return this.itemId;
- }
-
- public String recipe() {
- return this.recipe;
- }
- public int experience() {
- return this.experience;
- }
-
- public PotionComponent[] components() {
- return this.components;
- }
-
- public String abbreviation() {
- return this.abbreviation;
- }
-
- static {
- ImmutableMap.Builder builder = new ImmutableMap.Builder();
- for (PotionType potionType: values()) {
- builder.put(potionType.itemId(), potionType);
- }
-
- ITEM_MAP = builder.build();
- }
-}
-
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/moonsOfPeril/handlers/BossHandler.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/moonsOfPeril/handlers/BossHandler.java
index fb4dddd3816..4d14275f5a1 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/moonsOfPeril/handlers/BossHandler.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/moonsOfPeril/handlers/BossHandler.java
@@ -113,6 +113,9 @@ public void eatIfNeeded() {
public void drinkIfNeeded() {
int maxPrayer = Rs2Player.getRealSkillLevel(Skill.PRAYER);
int minimumPrayerPoint = (maxPrayer * prayerPercentage) / 100;
+ if (!Rs2Player.hasMoonlightActive()) {
+ Rs2Player.drinkPrayerPotion();
+ }
Rs2Player.drinkPrayerPotionAt(minimumPrayerPoint);
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/moonsOfPeril/handlers/ResupplyHandler.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/moonsOfPeril/handlers/ResupplyHandler.java
index cb28b54498f..d6892ad3b89 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/moonsOfPeril/handlers/ResupplyHandler.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/moonsOfPeril/handlers/ResupplyHandler.java
@@ -177,7 +177,8 @@ private int checkPotionQuantum(int target)
int desired = target - countMoonlightPotions();
if (desired <= 0) return 0;
- int requiredSlots = desired * 2 + 1; // 2 per potion + 1 mortar
+ final int overheadSlots = 2; // 1 mortar + 1 extra slot for double pulls from crate
+ int requiredSlots = desired * 2 + overheadSlots; // 2 per potion + 1 mortar + 1 space for double potion pulls from crate
if (debugLogging) {Microbot.log("Required free inventory slots: " + requiredSlots);}
int freeSlots = Rs2Inventory.emptySlotCount();
if (debugLogging) {Microbot.log("Current free inventory slots: " + freeSlots);}
@@ -196,7 +197,7 @@ private int checkPotionQuantum(int target)
freeSlots = Rs2Inventory.emptySlotCount();
}
if (freeSlots < requiredSlots) {
- desired = Math.max((freeSlots - 1) / 2, 0);
+ desired = Math.max((freeSlots - overheadSlots) / 2, 0);
}
return desired;
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/qualityoflife/QoLConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/qualityoflife/QoLConfig.java
index 878c102ec90..7a3d0c5eb33 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/qualityoflife/QoLConfig.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/qualityoflife/QoLConfig.java
@@ -1,9 +1,9 @@
package net.runelite.client.plugins.microbot.qualityoflife;
import net.runelite.client.config.*;
-import net.runelite.client.plugins.microbot.fletching.enums.FletchingItem;
import net.runelite.client.plugins.microbot.inventorysetups.InventorySetup;
import net.runelite.client.plugins.microbot.qualityoflife.enums.CraftingItem;
+import net.runelite.client.plugins.microbot.qualityoflife.enums.FletchingItem;
import net.runelite.client.plugins.microbot.qualityoflife.enums.WintertodtActions;
import net.runelite.client.plugins.microbot.util.misc.SpecialAttackWeaponEnum;
@@ -133,6 +133,13 @@ public interface QoLConfig extends Config {
)
String autoPrayerSection = "autoPrayerSection";
+ @ConfigSection(
+ name = "Grand Exchange",
+ description = "Grand Exchange settings",
+ position = 91
+ )
+ String grandExchangeSection = "grandExchangeSection";
+
// boolean to render Max Hit Overlay
@ConfigItem(
keyName = "renderMaxHitOverlay",
@@ -934,4 +941,14 @@ default boolean aggressiveAntiPkMode() {
return false;
}
+ @ConfigItem(
+ keyName = "grandExchangeHotkey",
+ name = "Paste and Search GE Hotkey",
+ description = "Pastes clipboard text into the GE search box.",
+ position = 0,
+ section = grandExchangeSection
+ )
+ default Keybind grandExchangeHotkey() {
+ return Keybind.NOT_SET;
+ }
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/qualityoflife/QoLPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/qualityoflife/QoLPlugin.java
index 3b3f0109f48..8b636f3990c 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/qualityoflife/QoLPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/qualityoflife/QoLPlugin.java
@@ -10,8 +10,16 @@
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.events.ProfileChanged;
+import net.runelite.client.input.KeyManager;
+import net.runelite.api.gameval.InterfaceID;
+import net.runelite.api.gameval.VarClientID;
+import net.runelite.client.plugins.microbot.util.widget.Rs2Widget;
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.DataFlavor;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
+import net.runelite.api.widgets.Widget;
import net.runelite.client.plugins.microbot.Microbot;
import net.runelite.client.plugins.microbot.MicrobotPlugin;
import net.runelite.client.plugins.microbot.inventorysetups.InventorySetup;
@@ -47,6 +55,13 @@
import net.runelite.client.ui.SplashScreen;
import net.runelite.client.ui.overlay.OverlayManager;
import net.runelite.client.util.ImageUtil;
+import net.runelite.client.input.KeyListener;
+import net.runelite.api.gameval.InterfaceID;
+import net.runelite.api.gameval.VarClientID;
+import net.runelite.client.plugins.microbot.util.widget.Rs2Widget;
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.DataFlavor;
import org.apache.commons.lang3.reflect.FieldUtils;
@@ -73,7 +88,7 @@
enabledByDefault = false
)
@Slf4j
-public class QoLPlugin extends Plugin {
+public class QoLPlugin extends Plugin implements KeyListener {
public static final List bankMenuEntries = new LinkedList<>();
public static final List furnaceMenuEntries = new LinkedList<>();
public static final List anvilMenuEntries = new LinkedList<>();
@@ -142,6 +157,9 @@ public class QoLPlugin extends Plugin {
@Inject
private AutoPrayer autoPrayer;
+ @Inject
+ private KeyManager keyManager;
+
@Provides
QoLConfig provideConfig(ConfigManager configManager) {
return configManager.getConfig(QoLConfig.class);
@@ -203,6 +221,7 @@ protected void startUp() throws AWTException {
bankpinScript.run(config);
potionManagerScript.run(config);
autoPrayer.run(config);
+ keyManager.registerKeyListener(this);
// pvpScript.run(config);
awaitExecutionUntil(() ->Microbot.getClientThread().invokeLater(this::updateUiElements), () -> !SplashScreen.isOpen(), 600);
}
@@ -222,6 +241,7 @@ protected void shutDown() {
eventBus.unregister(craftingManager);
potionManagerScript.shutdown();
autoPrayer.shutdown();
+ keyManager.unregisterKeyListener(this);
}
@Subscribe(
@@ -824,4 +844,36 @@ public void onPlayerChanged(PlayerChanged event) {
autoPrayer.handleAggressivePrayerOnGearChange(event.getPlayer(), config);
}
}
+
+ @Override
+ public void keyTyped(KeyEvent e) {
+ if (config.grandExchangeHotkey().matches(e)) {
+ e.consume();
+ }
+ }
+
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if (config.grandExchangeHotkey().matches(e)) {
+ e.consume();
+ try {
+ String clipboardText = (String) Toolkit.getDefaultToolkit().getSystemClipboard().getData(DataFlavor.stringFlavor);
+ if (clipboardText != null && !clipboardText.isEmpty()) {
+ Microbot.getClientThread().invoke(() -> {
+ Microbot.getClient().setVarcStrValue(VarClientID.MESLAYERINPUT, clipboardText);
+ Microbot.getClient().runScript(ScriptID.GE_ITEM_SEARCH);
+ });
+ }
+ } catch (Exception ex) {
+ Microbot.log("Failed to paste from clipboard: " + ex.getMessage());
+ }
+ }
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ if (config.grandExchangeHotkey().matches(e)) {
+ e.consume();
+ }
+ }
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/qualityoflife/enums/FletchingItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/qualityoflife/enums/FletchingItem.java
new file mode 100644
index 00000000000..f8329ccac13
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/qualityoflife/enums/FletchingItem.java
@@ -0,0 +1,39 @@
+package net.runelite.client.plugins.microbot.qualityoflife.enums;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor
+public enum FletchingItem
+{
+ ARROW_SHAFT("Arrow shaft", '1', "arrow shaft", 1),
+ SHORT("Short bows", '2', "shortbow", 1),
+ LONG("Long bows", '3', "longbow", 1),
+ STOCK("Crossbow stock", '4', "stock", 1),
+ SHIELD("Shield", '5', "shield", 2);
+
+ private final String name;
+ private final char option;
+ private final String containsInventoryName;
+ private final int amountRequired;
+
+ @Override
+ public String toString()
+ {
+ return name;
+ }
+
+ public char getOption(FletchingMaterial material, FletchingMode fletchingMode) {
+ if (fletchingMode == FletchingMode.STRUNG
+ || fletchingMode == FletchingMode.PROGRESSIVE_STRUNG) {
+ return '1';
+ }
+ if (material == FletchingMaterial.LOG && option == '2') return '3';
+ if (material == FletchingMaterial.LOG && option == '3') return '4';
+ //redwood is an exception
+ if (material == FletchingMaterial.REDWOOD)
+ return '2';
+ return option;
+ }
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/qualityoflife/enums/FletchingLogs.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/qualityoflife/enums/FletchingLogs.java
index 99ef95ec253..dc8f3f4708e 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/qualityoflife/enums/FletchingLogs.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/qualityoflife/enums/FletchingLogs.java
@@ -2,7 +2,6 @@
import net.runelite.api.gameval.ItemID;
import net.runelite.api.Skill;
-import net.runelite.client.plugins.microbot.fletching.enums.FletchingItem;
import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory;
import net.runelite.client.plugins.microbot.util.inventory.Rs2ItemModel;
import net.runelite.client.plugins.microbot.util.player.Rs2Player;
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/qualityoflife/enums/FletchingMaterial.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/qualityoflife/enums/FletchingMaterial.java
new file mode 100644
index 00000000000..8bfc756af22
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/qualityoflife/enums/FletchingMaterial.java
@@ -0,0 +1,27 @@
+package net.runelite.client.plugins.microbot.qualityoflife.enums;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor
+public enum FletchingMaterial
+{
+ LOG(""),
+ WOOD("Wood"),
+ OAK("Oak"),
+ WILLOW("Willow"),
+ MAPLE("Maple"),
+ YEW("Yew"),
+ MAGIC("Magic"),
+ REDWOOD("Redwood");
+
+ private final String name;
+
+
+ @Override
+ public String toString()
+ {
+ return name;
+ }
+}
\ No newline at end of file
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/qualityoflife/enums/FletchingMode.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/qualityoflife/enums/FletchingMode.java
new file mode 100644
index 00000000000..85f8223f98f
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/qualityoflife/enums/FletchingMode.java
@@ -0,0 +1,24 @@
+package net.runelite.client.plugins.microbot.qualityoflife.enums;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor
+public enum FletchingMode {
+ UNSTRUNG("Cutting", "knife", 1),
+ STRUNG("Stringing", "bow string", 14),
+ PROGRESSIVE_STRUNG("Progressive Bow Stringing", "bow string", 14),
+ UNSTRUNG_STRUNG("Cutting & Stringing", "knife", 1),
+ PROGRESSIVE("Progressive Logs Cutting", "knife", 1);
+
+
+ private final String name;
+ private final String itemName;
+ private final int amount;
+
+ @Override
+ public String toString() {
+ return name;
+ }
+}
\ No newline at end of file
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/.quest-helper-sync b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/.quest-helper-sync
new file mode 100644
index 00000000000..98091843fe1
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/.quest-helper-sync
@@ -0,0 +1,51 @@
+# Quest Helper Sync Tracking File
+# This file tracks the last synchronized commit from the external quest-helper repository
+#
+# External Repository: https://github.com/Zoinkwiz/quest-helper
+# Local Integration: runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/
+#
+# IMPORTANT: Do not manually edit this file unless you know what you're doing.
+# Use the quest-helper update scripts to manage synchronization.
+
+# Current sync position - this is the last external commit that has been integrated
+5539626281a502ba8c44fe59b8b57bce32fe4f16
+
+# Sync metadata (for script reference)
+# External repo HEAD at last check: 5539626281a502ba8c44fe59b8b57bce32fe4f16
+# Last sync date: 2025-08-29
+# Sync method: automated script with manual package transformation
+# Integration branch: quest-helper-update-20250829_063754
+# Local commit: cb416ef02a (quest-helper: polish Enakhras Lament)
+
+# Sync history log:
+# 2025-08-29 07:14:46 - Applied commit 55396262 (Polish enahkras lament #2294)
+# - Enhanced EnakhrasLament.java with better code organization
+# - Fixed Shadow Room brazier puzzle sequence and locations
+# - Added missing chisel icon for cutOffLimb step
+# - Files changed: 1 Java file, +359/-204 lines
+# - Package transformation: com.questhelper -> net.runelite.client.plugins.microbot.questhelper
+# - Status: SUCCESS
+#
+# 2025-08-28 16:35:20 - Complete sync to version 4.10.0
+# - Applied 76 patches successfully from external repository
+# - Skipped 4 patches (build files, CI workflows not applicable to Microbot)
+# - Base sync point: 6329d81ee15a07a4b4fb53217bd4305e978c7a0e
+# - Status: SUCCESS
+#
+# 2025-08-28 16:28:12 - Partial sync (first 4 patches)
+# - Initial automated sync setup and testing
+# - Applied commits d2df892a..4c204e89
+# - Status: SUCCESS
+#
+# 2025-08-28 16:13:45 - Initial sync tracking setup
+# - Created sync file and established tracking
+# - Method: manual setup
+# - Status: INITIALIZED
+
+# Integration status
+# Current external version: 4.10.0+
+# Integration health: HEALTHY
+# Last validation: 2025-08-29
+# Compilation status: SUCCESS
+# Package transformation: COMPLETE
+# Sync file location: CORRECT (moved from project root)
\ No newline at end of file
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/QuestHelperConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/QuestHelperConfig.java
index 2277d5fad83..9047d7866fa 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/QuestHelperConfig.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/QuestHelperConfig.java
@@ -534,6 +534,17 @@ default boolean showWidgetHints()
)
default boolean solvePuzzles() { return true; }
+ @ConfigItem(
+ keyName = "showWorldMapPoint",
+ name = "Display world map point",
+ description = "Choose whether the arrow & icon of your current step should be visible on the world map.
Changing this will take effect next time your quest step updates.",
+ section = hintsSection
+ )
+ default boolean showWorldMapPoint()
+ {
+ return true;
+ }
+
@ConfigItem(
keyName = "useShortestPath",
name = "Use 'Shortest Path' plugin",
@@ -681,4 +692,24 @@ default boolean showCompletedQuests()
{
return false;
}
+
+ @ConfigSection(
+ position = 5,
+ name = "Development",
+ description = "Options that configure the quest helper development experience",
+ closedByDefault = true
+ )
+ String developmentSection = "developmentSection";
+
+ @ConfigItem(
+ keyName = "devShowOverlayOnLaunch",
+ name = "Show overlay on launch",
+ description = "Show the dev overlay (::questhelperdebug) on launch",
+ position = 4,
+ section = developmentSection
+ )
+ default boolean devShowOverlayOnLaunch()
+ {
+ return false;
+ }
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/QuestHelperPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/QuestHelperPlugin.java
index 349bc76f559..fa2c724236d 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/QuestHelperPlugin.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/QuestHelperPlugin.java
@@ -29,6 +29,7 @@
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.Provides;
+import net.runelite.client.plugins.microbot.Microbot;
import net.runelite.client.plugins.microbot.questhelper.bank.banktab.BankTabItems;
import net.runelite.client.plugins.microbot.questhelper.bank.banktab.PotionStorage;
import net.runelite.client.plugins.microbot.questhelper.managers.*;
@@ -71,6 +72,7 @@
import net.runelite.client.util.Text;
import org.apache.commons.lang3.ArrayUtils;
+import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;
import javax.swing.*;
@@ -198,6 +200,14 @@ protected void startUp() throws IOException
questOverlayManager.startUp();
+ if (developerMode)
+ {
+ if (config.devShowOverlayOnLaunch())
+ {
+ questOverlayManager.addDebugOverlay();
+ }
+ }
+
final BufferedImage icon = Icon.QUEST_ICON.getImage();
panel = new QuestHelperPanel(this, questManager, configManager);
@@ -229,6 +239,11 @@ protected void shutDown()
eventBus.unregister(playerStateManager);
eventBus.unregister(runeliteObjectManager);
eventBus.unregister(worldMapAreaManager);
+ if (developerMode)
+ {
+ // We don't check if it was added, since removing an unadded overlay is a no-op
+ questOverlayManager.removeDebugOverlay();
+ }
questOverlayManager.shutDown();
playerStateManager.shutDown();
@@ -409,6 +424,18 @@ public void onConfigChanged(ConfigChanged event)
questManager.getSelectedQuest().setSidebarOrder(loadSidebarOrder(questManager.getSelectedQuest()));
}
}
+
+ if (developerMode && "devShowOverlayOnLaunch".equals(event.getKey()))
+ {
+ if (config.devShowOverlayOnLaunch())
+ {
+ questOverlayManager.addDebugOverlay();
+ }
+ else
+ {
+ questOverlayManager.removeDebugOverlay();
+ }
+ }
}
@Subscribe
@@ -462,7 +489,7 @@ public List getPluginBankTagItemsForSections()
return questBankManager.getBankTagService().getPluginBankTagItemsForSections(false);
}
- public QuestHelper getSelectedQuest()
+ public @Nullable QuestHelper getSelectedQuest()
{
return questManager.getSelectedQuest();
}
@@ -553,7 +580,7 @@ private void instantiate(QuestHelperQuest quest)
binder.bind(QuestHelper.class).toInstance(questHelper);
binder.install(questHelper);
};
- Injector questInjector = RuneLite.getInjector().createChildInjector(questModule);
+ Injector questInjector = Microbot.getInjector().createChildInjector(questModule);
injector.injectMembers(questHelper);
questHelper.setInjector(questInjector);
questHelper.setQuest(quest);
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/collections/ItemCollections.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/collections/ItemCollections.java
index 300131a53c1..07007445b54 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/collections/ItemCollections.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/collections/ItemCollections.java
@@ -170,6 +170,7 @@ public enum ItemCollections
SAW("Saw", ImmutableList.of(
ItemID.POH_SAW,
ItemID.WEARABLE_SAW,
+ ItemID.WEARABLE_SAW_OFFHAND,
ItemID.EYEGLO_CRYSTAL_SAW
)),
@@ -485,7 +486,14 @@ public enum ItemCollections
ItemID._4DOSEPRAYERRESTORE,
ItemID._3DOSEPRAYERRESTORE,
ItemID._2DOSEPRAYERRESTORE,
- ItemID._1DOSEPRAYERRESTORE
+ ItemID._1DOSEPRAYERRESTORE,
+ ItemID._4DOSE2RESTORE,
+ ItemID._3DOSE2RESTORE,
+ ItemID._2DOSE2RESTORE,
+ ItemID._1DOSE2RESTORE,
+ ItemID.HUNTER_MIX_MOONMOTH_2DOSE,
+ ItemID.HUNTER_MIX_MOONMOTH_1DOSE,
+ ItemID.BUTTERFLY_JAR_MOONMOTH
)),
RESTORE_POTIONS(ImmutableList.of(
@@ -1218,17 +1226,16 @@ public enum ItemCollections
ItemID.DRAMEN_STAFF
)),
- ESSENCE_LOW(ImmutableList.of(
- ItemID.BLANKRUNE_DAEYALT,
- ItemID.BLANKRUNE_HIGH,
- ItemID.BLANKRUNE
- )),
-
ESSENCE_HIGH(ImmutableList.of(
ItemID.BLANKRUNE_DAEYALT,
ItemID.BLANKRUNE_HIGH
)),
+ ESSENCE_LOW(new ImmutableList.Builder()
+ .addAll(ESSENCE_HIGH.items).add(
+ ItemID.BLANKRUNE).build()
+ ),
+
COINS(ImmutableList.of(
ItemID.COINS,
ItemID.MAGICTRAINING_COINS,
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/achievementdiaries/ardougne/ArdougneHard.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/achievementdiaries/ardougne/ArdougneHard.java
index 25ef4b36aaa..1c2cc093fd6 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/achievementdiaries/ardougne/ArdougneHard.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/achievementdiaries/ardougne/ArdougneHard.java
@@ -184,16 +184,20 @@ protected void setupRequirements()
crystalTrink = new ItemRequirement("Crystal Trinket", ItemID.MOURNING_CRYSTAL_TRINKET).showConditioned(notDeathRune).isNotConsumed();
highEss = new ItemRequirement("Pure or Daeyalt essence", ItemCollections.ESSENCE_HIGH)
.showConditioned(notDeathRune);
- newKey = new KeyringRequirement("New key", configManager, KeyringCollection.NEW_KEY).showConditioned(notDeathRune).isNotConsumed();
+
+ var hasCompletedSOTE = new QuestRequirement(QuestHelperQuest.SONG_OF_THE_ELVES, QuestState.FINISHED);
+
+ newKey = new KeyringRequirement("New key", configManager, KeyringCollection.NEW_KEY).showConditioned(notDeathRune).isNotConsumed().hideConditioned(hasCompletedSOTE);
newKey.setTooltip("Another can be found on the desk in the south-east room of the Mourner HQ basement.");
- mournerBoots = new ItemRequirement("Mourner boots", ItemID.MOURNING_MOURNER_BOOTS).isNotConsumed();
- gasMask = new ItemRequirement("Gas mask", ItemID.GASMASK).isNotConsumed();
- mournerGloves = new ItemRequirement("Mourner gloves", ItemID.MOURNING_MOURNER_GLOVES).isNotConsumed();
- mournerCloak = new ItemRequirement("Mourner cloak", ItemID.MOURNING_MOURNER_CLOAK).isNotConsumed();
- mournerTop = new ItemRequirement("Mourner top", ItemID.MOURNING_MOURNER_TOP).isNotConsumed();
- mournerTrousers = new ItemRequirement("Mourner trousers", ItemID.MOURNING_MOURNER_LEGS).isNotConsumed();
+
+ mournerBoots = new ItemRequirement("Mourner boots", ItemID.MOURNING_MOURNER_BOOTS).isNotConsumed().hideConditioned(hasCompletedSOTE);
+ gasMask = new ItemRequirement("Gas mask", ItemID.GASMASK).isNotConsumed().hideConditioned(hasCompletedSOTE);
+ mournerGloves = new ItemRequirement("Mourner gloves", ItemID.MOURNING_MOURNER_GLOVES).isNotConsumed().hideConditioned(hasCompletedSOTE);
+ mournerCloak = new ItemRequirement("Mourner cloak", ItemID.MOURNING_MOURNER_CLOAK).isNotConsumed().hideConditioned(hasCompletedSOTE);
+ mournerTop = new ItemRequirement("Mourner top", ItemID.MOURNING_MOURNER_TOP).isNotConsumed().hideConditioned(hasCompletedSOTE);
+ mournerTrousers = new ItemRequirement("Mourner trousers", ItemID.MOURNING_MOURNER_LEGS).isNotConsumed().hideConditioned(hasCompletedSOTE);
mournersOutfit = new ItemRequirements("Full mourners' outfit", gasMask, mournerTop, mournerTrousers,
- mournerCloak, mournerBoots, mournerGloves).showConditioned(notDeathRune).isNotConsumed();
+ mournerCloak, mournerBoots, mournerGloves).showConditioned(notDeathRune).isNotConsumed().hideConditioned(hasCompletedSOTE);
mournersOutfit.setTooltip("Another set can be obtained at the north entrance to Arandar.");
rake = new ItemRequirement("Rake", ItemID.RAKE)
.showConditioned(new Conditions(LogicType.OR, notPalmTree, notPoisonIvy)).isNotConsumed();
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/miniquests/barbariantraining/BarbarianTraining.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/miniquests/barbariantraining/BarbarianTraining.java
index 55ab7657130..7041dcc9171 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/miniquests/barbariantraining/BarbarianTraining.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/miniquests/barbariantraining/BarbarianTraining.java
@@ -667,7 +667,8 @@ public List getItemRequirements()
bow, oakLogs, tinderbox, axe,
feathers, knife,
hammer, bronzeBar.quantity(2), logs.quantity(3),
- attackPotion, roe);
+ attackPotion, roe,
+ antifireShield, combatGear);
}
@Override
@@ -676,6 +677,12 @@ public List getItemRecommended()
return Arrays.asList(gamesNecklace.quantity(5), catherbyTeleport);
}
+ @Override
+ public List getCombatRequirements()
+ {
+ return Collections.singletonList("Mithril Dragon (level 304)");
+ }
+
@Override
public List getNotes()
{
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/miniquests/valetotems/ValeTotems.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/miniquests/valetotems/ValeTotems.java
new file mode 100644
index 00000000000..411e998c0b1
--- /dev/null
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/miniquests/valetotems/ValeTotems.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2025, pajlada
+ * 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.microbot.questhelper.helpers.miniquests.valetotems;
+
+import net.runelite.client.plugins.microbot.questhelper.panel.PanelDetails;
+import net.runelite.client.plugins.microbot.questhelper.questhelpers.BasicQuestHelper;
+import net.runelite.client.plugins.microbot.questhelper.questinfo.QuestHelperQuest;
+import net.runelite.client.plugins.microbot.questhelper.requirements.Requirement;
+import net.runelite.client.plugins.microbot.questhelper.requirements.conditional.Conditions;
+import net.runelite.client.plugins.microbot.questhelper.requirements.item.ItemRequirement;
+import net.runelite.client.plugins.microbot.questhelper.requirements.player.SkillRequirement;
+import net.runelite.client.plugins.microbot.questhelper.requirements.quest.QuestRequirement;
+import static net.runelite.client.plugins.microbot.questhelper.requirements.util.LogicHelper.and;
+import static net.runelite.client.plugins.microbot.questhelper.requirements.util.LogicHelper.or;
+import net.runelite.client.plugins.microbot.questhelper.requirements.util.Operation;
+import net.runelite.client.plugins.microbot.questhelper.requirements.var.VarbitRequirement;
+import net.runelite.client.plugins.microbot.questhelper.requirements.zone.Zone;
+import net.runelite.client.plugins.microbot.questhelper.rewards.UnlockReward;
+import net.runelite.client.plugins.microbot.questhelper.steps.ConditionalStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.NpcStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.ObjectStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.QuestStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.widget.WidgetHighlight;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import net.runelite.api.QuestState;
+import net.runelite.api.Skill;
+import net.runelite.api.coords.WorldPoint;
+import net.runelite.api.gameval.ItemID;
+import net.runelite.api.gameval.NpcID;
+import net.runelite.api.gameval.ObjectID;
+import net.runelite.api.gameval.VarbitID;
+
+/**
+ * The quest guide for the "Vale Totems" OSRS quest
+ */
+public class ValeTotems extends BasicQuestHelper
+{
+ // Item requirements
+ ItemRequirement knife;
+ ItemRequirement oneOakLog;
+ ItemRequirement fourDecorativeItems;
+ ItemRequirement threeDecorativeItems;
+ ItemRequirement twoDecorativeItems;
+ ItemRequirement oneDecorativeItem;
+
+ // Miscellaneous requirements
+ VarbitRequirement needToBuildTotem;
+ VarbitRequirement isTotemBaseBuilt;
+
+ VarbitRequirement needToCarveAnimals;
+
+ Conditions isBuffaloNearby;
+ Conditions isJaguarNearby;
+ Conditions isEagleNearby;
+ Conditions isSnakeNearby;
+ Conditions isScorpionNearby;
+
+ Conditions missingBuffaloCarve;
+ Conditions missingJaguarCarve;
+ Conditions missingEagleCarve;
+ Conditions missingSnakeCarve;
+ Conditions missingScorpionCarve;
+
+ VarbitRequirement isDoneCarving;
+
+ VarbitRequirement needToDecorate;
+
+ VarbitRequirement oneShieldAdded;
+ VarbitRequirement twoShieldsAdded;
+ VarbitRequirement threeShieldsAdded;
+ VarbitRequirement isDoneDecorating;
+
+ // Steps
+ NpcStep startQuest;
+
+ ObjectStep buildTotemBase;
+
+ ObjectStep carveAnimalsYouSee;
+ ConditionalStep carveTotem;
+ NpcStep talkToIsadoraAfterCarvingTotem;
+ ConditionalStep decorateTotem;
+ ObjectStep decorateTotemFourShields;
+ NpcStep talkToIsadoraAfterDecoratingTotem;
+ ConditionalStep carveAndDecorateTotem;
+
+ QuestStep claimOffering;
+
+ QuestStep finishQuest;
+ NpcStep talkToIsadoraToLearnAboutCarving;
+
+ @Override
+ public Map loadSteps()
+ {
+ initializeRequirements();
+ setupSteps();
+
+ var steps = new HashMap();
+
+ steps.put(0, startQuest);
+ steps.put(10, startQuest);
+ steps.put(20, startQuest);
+ steps.put(30, carveAndDecorateTotem);
+ steps.put(40, talkToIsadoraAfterDecoratingTotem);
+ steps.put(50, claimOffering);
+ steps.put(60, finishQuest);
+
+ return steps;
+ }
+
+ @Override
+ protected void setupRequirements()
+ {
+ knife = new ItemRequirement("Knife", ItemID.KNIFE);
+ knife.setTooltip("There's a knife upstairs in the General Store south of the miniquest start point");
+ oneOakLog = new ItemRequirement("Oak log", ItemID.OAK_LOGS, 1);
+ oneOakLog.setTooltip("You can also use Willow, Maple, Yew, Magic, or Redwood logs, but it needs to match the decorative items you're bringing.");
+
+ var possibleDecorativeItems = List.of(ItemID.OAK_SHIELD, ItemID.UNSTRUNG_OAK_LONGBOW, ItemID.OAK_LONGBOW, ItemID.UNSTRUNG_OAK_SHORTBOW, ItemID.OAK_SHORTBOW);
+
+ fourDecorativeItems = new ItemRequirement("Oak shield/longbow/shortbow", possibleDecorativeItems, 4);
+ fourDecorativeItems.setTooltip("You can also use Willow, Maple, Yew, Magic, or Redwood decorative items, but it needs to match the logs you used to build the totem.");
+ threeDecorativeItems = new ItemRequirement("Oak shield/longbow/shortbow", possibleDecorativeItems, 3);
+ threeDecorativeItems.setTooltip("You can also use Willow, Maple, Yew, Magic, or Redwood decorative items, but it needs to match the logs you used to build the totem.");
+ twoDecorativeItems = new ItemRequirement("Oak shield/longbow/shortbow", possibleDecorativeItems, 2);
+ twoDecorativeItems.setTooltip("You can also use Willow, Maple, Yew, Magic, or Redwood decorative items, but it needs to match the logs you used to build the totem.");
+ oneDecorativeItem = new ItemRequirement("Oak shield/longbow/shortbow", possibleDecorativeItems, 1);
+ oneDecorativeItem.setTooltip("You can also use Willow, Maple, Yew, Magic, or Redwood decorative items, but it needs to match the logs you used to build the totem.");
+
+ needToBuildTotem = new VarbitRequirement(VarbitID.ENT_TOTEMS_BROKEN_CHAT, 1);
+ isTotemBaseBuilt = new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_BASE, 1);
+
+ isBuffaloNearby = or(
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_ANIMAL_1, 1),
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_ANIMAL_2, 1),
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_ANIMAL_3, 1)
+ );
+ isJaguarNearby = or(
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_ANIMAL_1, 2),
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_ANIMAL_2, 2),
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_ANIMAL_3, 2)
+ );
+ isEagleNearby = or(
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_ANIMAL_1, 3),
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_ANIMAL_2, 3),
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_ANIMAL_3, 3)
+ );
+ isSnakeNearby = or(
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_ANIMAL_1, 4),
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_ANIMAL_2, 4),
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_ANIMAL_3, 4)
+ );
+ isScorpionNearby = or(
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_ANIMAL_1, 5),
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_ANIMAL_2, 5),
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_ANIMAL_3, 5)
+ );
+
+ missingBuffaloCarve = and(
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_LOW, 10, Operation.NOT_EQUAL),
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_MID, 10, Operation.NOT_EQUAL),
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_TOP, 10, Operation.NOT_EQUAL)
+ );
+
+ missingJaguarCarve = and(
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_LOW, 11, Operation.NOT_EQUAL),
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_MID, 11, Operation.NOT_EQUAL),
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_TOP, 11, Operation.NOT_EQUAL)
+ );
+
+ missingEagleCarve = and(
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_LOW, 12, Operation.NOT_EQUAL),
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_MID, 12, Operation.NOT_EQUAL),
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_TOP, 12, Operation.NOT_EQUAL)
+ );
+
+ missingSnakeCarve = and(
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_LOW, 13, Operation.NOT_EQUAL),
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_MID, 13, Operation.NOT_EQUAL),
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_TOP, 13, Operation.NOT_EQUAL)
+ );
+
+ missingScorpionCarve = and(
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_LOW, 14, Operation.NOT_EQUAL),
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_MID, 14, Operation.NOT_EQUAL),
+ new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_TOP, 14, Operation.NOT_EQUAL)
+ );
+
+ isDoneCarving = new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_BASE_CARVED, 1);
+
+ needToCarveAnimals = new VarbitRequirement(VarbitID.ENT_TOTEMS_CARVE_CHAT, 1);
+ needToDecorate = new VarbitRequirement(VarbitID.ENT_TOTEMS_DECORATE_CHAT, 1);
+
+ oneShieldAdded = new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_DECORATIONS, 1);
+ twoShieldsAdded = new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_DECORATIONS, 2);
+ threeShieldsAdded = new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_DECORATIONS, 3);
+ isDoneDecorating = new VarbitRequirement(VarbitID.ENT_TOTEMS_SITE_1_DECORATIONS, 4);
+ }
+
+ public void setupSteps()
+ {
+ startQuest = new NpcStep(this, new int[]{NpcID.ENT_TOTEMS_INTRO_RANULPH, NpcID.ENT_TOTEMS_INTRO_RANULPH_1OP, NpcID.ENT_TOTEMS_INTRO_RANULPH_2OP, NpcID.ENT_TOTEMS_INTRO_RANULPH_CS,}, new WorldPoint(1365, 3366, 0), "Talk to Ranulph in the west part of Auburnvale to start the quest.");
+ startQuest.addDialogStep("Yes.");
+
+ buildTotemBase = new ObjectStep(this, ObjectID.ENT_TOTEMS_BASE_NONE, new WorldPoint(1370, 3375, 0), "Build the totem site.", knife, oneOakLog);
+
+ talkToIsadoraToLearnAboutCarving = new NpcStep(this, NpcID.ENT_TOTEMS_INTRO_CHILD_VIS, new WorldPoint(1366, 3369, 0), "Talk to Isadora to learn about carving.");
+ talkToIsadoraAfterCarvingTotem = new NpcStep(this, NpcID.ENT_TOTEMS_INTRO_CHILD_VIS, new WorldPoint(1366, 3369, 0), "Return to Isadora after carving the spirit animals into the totem.");
+
+ var carveBuffalo = new ObjectStep(this, ObjectID.ENT_TOTEMS_SITE_1_BASE, new WorldPoint(1370, 3375, 0), "Carve a Buffalo into the totem base.");
+ carveBuffalo.addWidgetHighlight(WidgetHighlight.createMultiskillByName("Buffalo"));
+
+ var carveJaguar = new ObjectStep(this, ObjectID.ENT_TOTEMS_SITE_1_BASE, new WorldPoint(1370, 3375, 0), "Carve a Jaguar into the totem base.");
+ carveJaguar.addWidgetHighlight(WidgetHighlight.createMultiskillByName("Jaguar"));
+
+ var carveEagle = new ObjectStep(this, ObjectID.ENT_TOTEMS_SITE_1_BASE, new WorldPoint(1370, 3375, 0), "Carve a Eagle into the totem base.");
+ carveEagle.addWidgetHighlight(WidgetHighlight.createMultiskillByName("Eagle"));
+
+ var carveSnake = new ObjectStep(this, ObjectID.ENT_TOTEMS_SITE_1_BASE, new WorldPoint(1370, 3375, 0), "Carve a Snake into the totem base.");
+ carveSnake.addWidgetHighlight(WidgetHighlight.createMultiskillByName("Snake"));
+
+ var carveScorpion = new ObjectStep(this, ObjectID.ENT_TOTEMS_SITE_1_BASE, new WorldPoint(1370, 3375, 0), "Carve a Scorpion into the totem base.");
+ carveScorpion.addWidgetHighlight(WidgetHighlight.createMultiskillByName("Scorpion"));
+
+ // fallback step in case our detection fails
+ carveAnimalsYouSee = new ObjectStep(this, ObjectID.ENT_TOTEMS_SITE_1_BASE, new WorldPoint(1370, 3375, 0), "Carve spirit animals you see into the totem.");
+ carveAnimalsYouSee.addSubSteps(carveBuffalo, carveJaguar, carveEagle, carveSnake, carveScorpion);
+
+ carveTotem = new ConditionalStep(this, carveAnimalsYouSee);
+ carveTotem.addStep(and(isBuffaloNearby, missingBuffaloCarve), carveBuffalo);
+ carveTotem.addStep(and(isJaguarNearby, missingJaguarCarve), carveJaguar);
+ carveTotem.addStep(and(isEagleNearby, missingEagleCarve), carveEagle);
+ carveTotem.addStep(and(isSnakeNearby, missingSnakeCarve), carveSnake);
+ carveTotem.addStep(and(isScorpionNearby, missingScorpionCarve), carveScorpion);
+
+ var decorateTotemOneShield = new ObjectStep(this, ObjectID.ENT_TOTEMS_SITE_1_BASE, new WorldPoint(1370, 3375, 0), "Decorate the totem with one decorative item (shield, longbow, or shortbow) of the same wood type you used to build/carve the totem.", oneDecorativeItem);
+ var decorateTotemTwoShields = new ObjectStep(this, ObjectID.ENT_TOTEMS_SITE_1_BASE, new WorldPoint(1370, 3375, 0), "Decorate the totem with two decorative items (shield, longbow, or shortbow) of the same wood type you used to build/carve the totem.", twoDecorativeItems);
+ var decorateTotemThreeShields = new ObjectStep(this, ObjectID.ENT_TOTEMS_SITE_1_BASE, new WorldPoint(1370, 3375, 0), "Decorate the totem with three decorative items (shield, longbow, or shortbow) of the same wood type you used to build/carve the totem.", threeDecorativeItems);
+ decorateTotemFourShields = new ObjectStep(this, ObjectID.ENT_TOTEMS_SITE_1_BASE, new WorldPoint(1370, 3375, 0), "Decorate the totem with four decorative items (shield, longbow, or shortbow) of the same wood type you used to build/carve the totem.", fourDecorativeItems);
+ decorateTotemFourShields.addSubSteps(decorateTotemOneShield, decorateTotemTwoShields, decorateTotemThreeShields);
+ talkToIsadoraAfterDecoratingTotem = new NpcStep(this, NpcID.ENT_TOTEMS_INTRO_CHILD_VIS, new WorldPoint(1366, 3369, 0), "Return to Isadora to talk after decorating the totem.");
+ decorateTotem = new ConditionalStep(this, decorateTotemFourShields);
+ decorateTotem.addStep(oneShieldAdded, decorateTotemThreeShields);
+ decorateTotem.addStep(twoShieldsAdded, decorateTotemTwoShields);
+ decorateTotem.addStep(threeShieldsAdded, decorateTotemOneShield);
+
+ carveAndDecorateTotem = new ConditionalStep(this, startQuest);
+ carveAndDecorateTotem.addStep(isDoneDecorating, talkToIsadoraAfterDecoratingTotem);
+ carveAndDecorateTotem.addStep(needToDecorate, decorateTotem);
+ carveAndDecorateTotem.addStep(isDoneCarving, talkToIsadoraAfterCarvingTotem);
+ carveAndDecorateTotem.addStep(needToCarveAnimals, carveTotem);
+ carveAndDecorateTotem.addStep(isTotemBaseBuilt, talkToIsadoraToLearnAboutCarving);
+ carveAndDecorateTotem.addStep(needToBuildTotem, buildTotemBase);
+
+ claimOffering = new ObjectStep(this, ObjectID.ENT_TOTEMS_OFFERINGS_B, new WorldPoint(1370, 3374, 0), "Claim the offering the Ent left next to your totem.");
+ finishQuest = new NpcStep(this, NpcID.ENT_TOTEMS_INTRO_CHILD_VIS, new WorldPoint(1366, 3369, 0), "Return to Isadora to finish the quest.");
+ }
+
+ @Override
+ public List getItemRequirements()
+ {
+ return List.of(
+ knife,
+ oneOakLog,
+ fourDecorativeItems
+ );
+ }
+
+ @Override
+ public List getGeneralRequirements()
+ {
+ return List.of(
+ new QuestRequirement(QuestHelperQuest.CHILDREN_OF_THE_SUN, QuestState.FINISHED),
+ new SkillRequirement(Skill.FLETCHING, 20)
+ );
+ }
+
+ @Override
+ public List getUnlockRewards()
+ {
+ return List.of(
+ new UnlockReward("Unlocks the Auburnvale fletching minigame")
+ );
+ }
+
+ @Override
+ public List getPanels()
+ {
+ var panels = new ArrayList();
+
+ panels.add(new PanelDetails("Repair the totem", List.of(
+ startQuest,
+ buildTotemBase,
+ talkToIsadoraToLearnAboutCarving,
+ carveAnimalsYouSee,
+ talkToIsadoraAfterCarvingTotem,
+ decorateTotemFourShields,
+ talkToIsadoraAfterDecoratingTotem,
+ claimOffering,
+ finishQuest
+ ), List.of(
+ knife,
+ oneOakLog,
+ fourDecorativeItems
+ )));
+
+ return panels;
+ }
+}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/aporcineofinterest/APorcineOfInterest.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/aporcineofinterest/APorcineOfInterest.java
index c8734b17ddc..9deed8869ec 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/aporcineofinterest/APorcineOfInterest.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/aporcineofinterest/APorcineOfInterest.java
@@ -101,7 +101,7 @@ protected void setupRequirements()
rope.setHighlightInInventory(true);
slashItem = new ItemRequirement("A knife or slash weapon", ItemID.KNIFE).isNotConsumed();
- slashItem.setTooltip("Except abyssal whip, abyssal tentacle, or dragon claws.");
+ slashItem.setTooltip("Except abyssal whip, abyssal tentacle, noxious halberd, or dragon claws.");
reinforcedGoggles = new ItemRequirement("Reinforced goggles", ItemID.SLAYER_REINFORCED_GOGGLES, 1, true).isNotConsumed();
reinforcedGoggles.setTooltip("You can get another pair from Spria");
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/clocktower/ClockTower.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/clocktower/ClockTower.java
index b0afaea4ad9..795c01f1d08 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/clocktower/ClockTower.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/clocktower/ClockTower.java
@@ -180,7 +180,7 @@ public void setupConditions()
startedQuestDuringSession = new Conditions(true, new VarplayerRequirement(QuestVarPlayer.QUEST_CLOCK_TOWER.getId(), 0));
synced = new Conditions(true, LogicType.OR,
- new WidgetTextRequirement(InterfaceID.Questjournal.TITLE, "Clock Tower"),
+ new WidgetTextRequirement(InterfaceID.QuestjournalOverview.TITLE, "Clock Tower"),
startedQuestDuringSession
);
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/cooksassistant/CooksAssistant.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/cooksassistant/CooksAssistant.java
index 005813cdf04..30c33d4aabf 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/cooksassistant/CooksAssistant.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/cooksassistant/CooksAssistant.java
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2019, Trevor
+ * Copyright (c) 2025, pajlada
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -27,112 +28,128 @@
import net.runelite.client.plugins.microbot.questhelper.collections.ItemCollections;
import net.runelite.client.plugins.microbot.questhelper.panel.PanelDetails;
import net.runelite.client.plugins.microbot.questhelper.questhelpers.BasicQuestHelper;
-import net.runelite.client.plugins.microbot.questhelper.requirements.Requirement;
-import net.runelite.client.plugins.microbot.questhelper.requirements.conditional.Conditions;
import net.runelite.client.plugins.microbot.questhelper.requirements.item.ItemRequirement;
+import static net.runelite.client.plugins.microbot.questhelper.requirements.util.LogicHelper.and;
+import static net.runelite.client.plugins.microbot.questhelper.requirements.util.LogicHelper.nor;
+import static net.runelite.client.plugins.microbot.questhelper.requirements.util.LogicHelper.or;
+import net.runelite.client.plugins.microbot.questhelper.requirements.npc.DialogRequirement;
import net.runelite.client.plugins.microbot.questhelper.requirements.var.VarbitRequirement;
import net.runelite.client.plugins.microbot.questhelper.requirements.zone.Zone;
import net.runelite.client.plugins.microbot.questhelper.requirements.zone.ZoneRequirement;
import net.runelite.client.plugins.microbot.questhelper.rewards.ExperienceReward;
import net.runelite.client.plugins.microbot.questhelper.rewards.QuestPointReward;
import net.runelite.client.plugins.microbot.questhelper.rewards.UnlockReward;
-import net.runelite.client.plugins.microbot.questhelper.steps.*;
+import net.runelite.client.plugins.microbot.questhelper.steps.ConditionalStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.ItemStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.NpcStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.ObjectStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.QuestStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.widget.WidgetHighlight;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import net.runelite.api.Skill;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.gameval.ItemID;
import net.runelite.api.gameval.NpcID;
import net.runelite.api.gameval.ObjectID;
-
-import java.util.*;
+import net.runelite.api.gameval.VarbitID;
public class CooksAssistant extends BasicQuestHelper
{
- //Items Required
- ItemRequirement egg, milk, flour, bucket, pot, coins, grain;
+ // Required items
+ ItemRequirement egg;
+ ItemRequirement milk;
+ ItemRequirement flour;
+ ItemRequirement bucket;
+ ItemRequirement pot;
+ ItemRequirement coins;
+ ItemRequirement grain;
- Requirement controlsUsed;
+ // Zones
+ Zone millSecond;
+ Zone millThird;
- QuestStep getEgg, getWheat, milkCow, climbLadderOne, climbLadderTwoUp, climbLadderTwoDown, climbLadderThree, fillHopper,
- operateControls, collectFlour, finishQuest;
+ // Miscellaneous requirements
+ DialogRequirement hasTurnedInMilk;
+ DialogRequirement hasTurnedInFlour;
+ DialogRequirement hasTurnedInEgg;
+ DialogRequirement hasTurnedInEverything;
- NpcStep getPot, getBucket;
+ VarbitRequirement controlsUsed;
- Zone millSecond, millThird;
+ ZoneRequirement inMillSecond;
+ ZoneRequirement inMillThird;
- Requirement inMillSecond, inMillThird;
+ // Steps
+ NpcStep getBucket;
+ NpcStep getPot;
+ ObjectStep milkCow;
+ ItemStep getEgg;
+ ObjectStep getWheat;
+ ObjectStep climbLadderOne;
+ ObjectStep fillHopper;
+ ObjectStep operateControls;
+ ObjectStep climbLadderThree;
+ ObjectStep collectFlour;
+ ObjectStep climbLadderTwoUp;
+ ObjectStep climbLadderTwoDown;
+ NpcStep finishQuest;
@Override
- public Map loadSteps()
+ protected void setupZones()
{
- initializeRequirements();
- setupConditions();
- setupSteps();
-
- Map steps = new HashMap<>();
- ConditionalStep doQuest = new ConditionalStep(this, getBucket);
- doQuest.addStep(new Conditions(milk, flour, egg), finishQuest);
- doQuest.addStep(new Conditions(milk, pot, egg, controlsUsed, inMillThird), climbLadderThree);
- doQuest.addStep(new Conditions(milk, pot, egg, controlsUsed, inMillSecond), climbLadderTwoDown);
- doQuest.addStep(new Conditions(milk, pot, egg, controlsUsed), collectFlour);
- doQuest.addStep(new Conditions(milk, pot, egg, grain, inMillThird), fillHopper);
- doQuest.addStep(new Conditions(milk, pot, egg, inMillThird), operateControls);
- doQuest.addStep(new Conditions(milk, pot, egg, grain, inMillSecond), climbLadderTwoUp);
- doQuest.addStep(new Conditions(milk, pot, egg, grain), climbLadderOne);
- doQuest.addStep(new Conditions(milk, pot, egg), getWheat);
- doQuest.addStep(new Conditions(milk, pot), getEgg);
- doQuest.addStep(new Conditions(bucket, pot), milkCow);
- doQuest.addStep(bucket, getPot);
-
- steps.put(0, doQuest);
- steps.put(1, doQuest);
-
- return steps;
+ millSecond = new Zone(new WorldPoint(3162, 3311, 1), new WorldPoint(3171, 3302, 1));
+ millThird = new Zone(new WorldPoint(3162, 3311, 2), new WorldPoint(3171, 3302, 2));
}
@Override
protected void setupRequirements()
{
+ hasTurnedInMilk = new DialogRequirement("Here's a bucket of milk.");
+ hasTurnedInFlour = new DialogRequirement("Here's a pot of flour.");
+ hasTurnedInEgg = new DialogRequirement("Here's a fresh egg.");
+ hasTurnedInEverything = new DialogRequirement("You've brought me everything I need! I am saved!");
+
egg = new ItemRequirement("Egg", ItemID.EGG);
+ egg.setConditionToHide(or(hasTurnedInEgg, hasTurnedInEverything));
egg.canBeObtainedDuringQuest();
milk = new ItemRequirement("Bucket of milk", ItemID.BUCKET_MILK);
+ milk.setConditionToHide(or(hasTurnedInMilk, hasTurnedInEverything));
milk.canBeObtainedDuringQuest();
flour = new ItemRequirement("Pot of flour", ItemID.POT_FLOUR);
+ flour.setConditionToHide(or(hasTurnedInFlour, hasTurnedInEverything));
flour.canBeObtainedDuringQuest();
bucket = new ItemRequirement("Bucket", ItemID.BUCKET_EMPTY);
pot = new ItemRequirement("Pot", ItemID.POT_EMPTY);
- coins = new ItemRequirement("Coins", ItemCollections.COINS);
+ coins = new ItemRequirement("Coins", ItemCollections.COINS, 3);
coins.setTooltip("Necessary if you do not have a pot / bucket");
grain = new ItemRequirement("Grain", ItemID.GRAIN);
- controlsUsed = new VarbitRequirement(4920, 1);
- }
+ controlsUsed = new VarbitRequirement(VarbitID.MILL_FLOUR, 1);
- @Override
- protected void setupZones()
- {
- millSecond = new Zone(new WorldPoint(3162, 3311, 1), new WorldPoint(3171, 3302, 1));
- millThird = new Zone(new WorldPoint(3162, 3311, 2), new WorldPoint(3171, 3302, 2));
- }
-
- public void setupConditions()
- {
inMillSecond = new ZoneRequirement(millSecond);
inMillThird = new ZoneRequirement(millThird);
}
public void setupSteps()
{
+ var lumbridgeShopkeepers = new int[]{
+ NpcID.GENERALSHOPKEEPER1,
+ NpcID.GENERALASSISTANT1,
+ };
+
+ getBucket = new NpcStep(this, lumbridgeShopkeepers, new WorldPoint(3212, 3246, 0),
+ "Purchase a bucket from the Lumbridge General Store.", coins.quantity(2));
+ getBucket.addWidgetHighlight(WidgetHighlight.createShopItemHighlight(ItemID.BUCKET_EMPTY));
+
+ getPot = new NpcStep(this, lumbridgeShopkeepers, new WorldPoint(3212, 3246, 0),
+ "Purchase a pot from the Lumbridge General Store.", coins.quantity(1));
+ getPot.addWidgetHighlight(WidgetHighlight.createShopItemHighlight(ItemID.POT_EMPTY));
+
getEgg = new ItemStep(this, new WorldPoint(3177, 3296, 0),
"Grab an egg from the farm north of Lumbridge.", egg);
- getBucket = new NpcStep(this, NpcID.GENERALSHOPKEEPER1, new WorldPoint(3212, 3246, 0),
- "Purchase a bucket from the Lumbridge General Store.", coins.quantity(3));
- getBucket.addWidgetHighlightWithItemIdRequirement(300, 16, ItemID.BUCKET_EMPTY, true);
- getBucket.addAlternateNpcs(NpcID.GENERALASSISTANT1);
- getPot = new NpcStep(this, NpcID.GENERALSHOPKEEPER1, new WorldPoint(3212, 3246, 0),
- "Purchase a pot from the Lumbridge General Store.", coins.quantity(3));
- getPot.addAlternateNpcs(NpcID.GENERALASSISTANT1);
- milkCow = new ObjectStep(this, ObjectID.FAT_COW, new WorldPoint(3254, 3272, 0),
- "Milk the cow north-east of Lumbridge.", bucket);
getWheat = new ObjectStep(this, ObjectID.FAI_VARROCK_WHEAT_CORNER, new WorldPoint(3161, 3292, 0),
"Pick some wheat north of Lumbridge.");
climbLadderOne = new ObjectStep(this, ObjectID.QIP_COOK_LADDER, new WorldPoint(3164, 3307, 0),
@@ -155,26 +172,67 @@ public void setupSteps()
collectFlour = new ObjectStep(this, ObjectID.MILLBASE_FLOUR, new WorldPoint(3166, 3306, 0),
"Collect the flour in the bin.", pot.highlighted());
collectFlour.addIcon(ItemID.POT_EMPTY);
- finishQuest = new NpcStep(this, NpcID.POH_SERVANT_COOK_WOMAN, new WorldPoint(3206, 3214, 0),
+
+ milkCow = new ObjectStep(this, ObjectID.FAT_COW, new WorldPoint(3172, 3317, 0),
+ "Milk the dairy cow north of Lumbridge.", bucket);
+
+ finishQuest = new NpcStep(this, NpcID.COOK, new WorldPoint(3206, 3214, 0),
"Give the Cook in Lumbridge Castle's kitchen the required items to finish the quest.",
egg, milk, flour);
+ finishQuest.addAlternateNpcs(NpcID.POH_SERVANT_COOK_WOMAN);
finishQuest.addDialogSteps("What's wrong?", "Can I help?", "Yes.");
}
+ @Override
+ public Map loadSteps()
+ {
+ initializeRequirements();
+ setupSteps();
+
+ var steps = new HashMap();
+
+
+ var getFlour = new ConditionalStep(this, getPot);
+ getFlour.addStep(and(pot, controlsUsed, inMillThird), climbLadderThree);
+ getFlour.addStep(and(pot, controlsUsed, inMillSecond), climbLadderTwoDown);
+ getFlour.addStep(and(pot, controlsUsed), collectFlour);
+ getFlour.addStep(and(pot, grain, inMillThird), fillHopper);
+ getFlour.addStep(and(pot, inMillThird), operateControls);
+ getFlour.addStep(and(pot, grain, inMillSecond), climbLadderTwoUp);
+ getFlour.addStep(and(pot, grain), climbLadderOne);
+ getFlour.addStep(and(pot), getWheat);
+
+ var doQuest = new ConditionalStep(this, finishQuest);
+ doQuest.addStep(hasTurnedInEverything, finishQuest);
+ doQuest.addStep(nor(milk, bucket, hasTurnedInMilk), getBucket);
+ doQuest.addStep(nor(flour, pot, hasTurnedInFlour), getPot);
+ doQuest.addStep(nor(egg, hasTurnedInEgg), getEgg);
+ doQuest.addStep(nor(flour, hasTurnedInFlour), getFlour);
+ doQuest.addStep(nor(milk, hasTurnedInMilk), milkCow);
+
+ steps.put(0, doQuest);
+ steps.put(1, doQuest);
+
+ return steps;
+ }
+
+
@Override
public List getItemRequirements()
{
- ArrayList reqs = new ArrayList<>();
- reqs.add(egg);
- reqs.add(flour);
- reqs.add(milk);
- return reqs;
+ return List.of(
+ egg,
+ flour,
+ milk
+ );
}
@Override
public List getItemRecommended()
{
- return Collections.singletonList(coins);
+ return List.of(
+ coins
+ );
}
@Override
@@ -186,26 +244,60 @@ public QuestPointReward getQuestPointReward()
@Override
public List getExperienceRewards()
{
- return Collections.singletonList(new ExperienceReward(Skill.COOKING, 300));
+ return List.of(
+ new ExperienceReward(Skill.COOKING, 300)
+ );
}
@Override
public List getUnlockRewards()
{
- return Collections.singletonList(new UnlockReward("Permission to use The Cook's range."));
+ return List.of(
+ new UnlockReward("Permission to use The Cook's range.")
+ );
}
@Override
public List getPanels()
{
- List allSteps = new ArrayList<>();
- allSteps.add(new PanelDetails("Starting off", Arrays.asList(getBucket, getPot), coins.quantity(3)));
- allSteps.add(new PanelDetails("Getting the Milk", Collections.singletonList(milkCow), bucket));
- allSteps.add(new PanelDetails("Getting the Egg", Collections.singletonList(getEgg)));
- allSteps.add(new PanelDetails("Getting the Flour", Arrays.asList(getWheat, climbLadderOne, fillHopper,
- operateControls, climbLadderThree, collectFlour), pot));
- allSteps.add(new PanelDetails("Finishing up", Collections.singletonList(finishQuest), egg, flour, milk));
-
- return allSteps;
+ var steps = new ArrayList();
+
+ steps.add(new PanelDetails("Starting off", List.of(
+ getBucket,
+ getPot
+ ), List.of(
+ coins
+ )));
+
+ steps.add(new PanelDetails("Getting the Egg", List.of(
+ getEgg
+ )));
+
+ steps.add(new PanelDetails("Getting the Flour", List.of(
+ getWheat,
+ climbLadderOne,
+ fillHopper,
+ operateControls,
+ climbLadderThree,
+ collectFlour
+ ), List.of(
+ pot
+ )));
+
+ steps.add(new PanelDetails("Getting the Milk", List.of(
+ milkCow
+ ), List.of(
+ bucket
+ )));
+
+ steps.add(new PanelDetails("Finishing up", List.of(
+ finishQuest
+ ), List.of(
+ egg,
+ flour,
+ milk
+ )));
+
+ return steps;
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/creatureoffenkenstrain/CreatureOfFenkenstrain.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/creatureoffenkenstrain/CreatureOfFenkenstrain.java
index 7bdd85c071d..73cd4840736 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/creatureoffenkenstrain/CreatureOfFenkenstrain.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/creatureoffenkenstrain/CreatureOfFenkenstrain.java
@@ -271,7 +271,7 @@ public void setupSteps()
"Go back to the ground floor.");
talkToGardenerForHead = new NpcStep(this, NpcID.FENK_GARDENER, new WorldPoint(3548, 3562, 0),
- "Talk to the Gardener Ghost.", ghostSpeakAmulet.equipped());
+ "Talk to the Gardener Ghost while wearing your Ghostspeak amulet.", ghostSpeakAmulet.equipped());
talkToGardenerForHead.addDialogStep("What happened to your head?");
goToHeadGrave = new DigStep(this, new WorldPoint(3608, 3490, 0),
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/deserttreasureii/VardorvisSteps.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/deserttreasureii/VardorvisSteps.java
index 3a217ec3e51..6247bdafbac 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/deserttreasureii/VardorvisSteps.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/deserttreasureii/VardorvisSteps.java
@@ -67,7 +67,7 @@ public class VardorvisSteps extends ConditionalStep
inAnyStranglewood, inVardorvisArea, unlockedShortcut, defeatedVardorvis, templeKeyNearby, kasondeAggressive, givenKasondeKey, defeatedKasonde,
kasondeRevealedMedallion, gotVardorvisMedallion, inVault;
ItemRequirement potionNote, strangePotion, freezes, berry, herb, unfinishedSerum, serumWithHerb, stranglerSerum, templeKey,
- vardorvisMedallion;
+ vardorvisMedallion, food;
Zone stranglewood, towerDefenseRoom, stranglewoodPyramidRoom, vardorvisArea, vault;
QuestBank questBank;
@@ -139,6 +139,7 @@ protected void setupItemRequirements()
potionNote = new ItemRequirement("Potion note", ItemID.DT2_KASONDE_NOTE);
strangePotion = new ItemRequirement("Strange potion", ItemID.DT2_KASONDE_POTION);
+ food = new ItemRequirement("Bring high healing food to tank the infected.", -1);
freezes = new ItemRequirement("Freezing spells STRONGLY recommended + reasonable mage accuracy", -1);
berry = new ItemRequirement("Argian berries", ItemID.DT2_STRANGLEWOOD_BERRIES);
berry.setTooltip("You can get another from the south-west corner of The Stranglewood");
@@ -249,6 +250,7 @@ protected void setupSteps()
defendKasonde = new DetailedQuestStep(getQuestHelper(), "Defend Kasonde! Read the sidebar for more details.");
defendKasonde.addRecommended(freezes);
+ defendKasonde.addRecommended(food);
defendKasondeSidebar.addSubSteps(defendKasonde);
// TODO: Get actual coordinate and ladder ID!
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/deviousminds/DeviousMinds.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/deviousminds/DeviousMinds.java
index 7100b81635a..6e675842bfa 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/deviousminds/DeviousMinds.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/deviousminds/DeviousMinds.java
@@ -112,9 +112,9 @@ public Map loadSteps()
protected void setupRequirements()
{
//Recommended
- fallyTele = new ItemRequirement("Falador Teleports", ItemID.POH_TABLET_FALADORTELEPORT);
- lumberTele = new ItemRequirement("Lumberyard Teleports", ItemID.TELEPORTSCROLL_LUMBERYARD);
- glory = new ItemRequirement("Amulet of Glory", ItemCollections.AMULET_OF_GLORIES);
+ fallyTele = new ItemRequirement("Falador Teleports", ItemID.POH_TABLET_FALADORTELEPORT, 2);
+ lumberTele = new ItemRequirement("Lumberyard Teleports", ItemID.TELEPORTSCROLL_LUMBERYARD, 3);
+ glory = new ItemRequirement("Amulet of Glory (Teleports to Port Sarim and Edgeville)", ItemCollections.AMULET_OF_GLORIES);
//Required
mith2h = new ItemRequirement("Mithril 2h Sword", ItemID.MITHRIL_2H_SWORD);
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/elementalworkshopi/ElementalWorkshopI.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/elementalworkshopi/ElementalWorkshopI.java
index 2c4a0142ddb..755bc53bafe 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/elementalworkshopi/ElementalWorkshopI.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/elementalworkshopi/ElementalWorkshopI.java
@@ -137,8 +137,9 @@ protected void setupRequirements()
knife.setHighlightInInventory(true);
pickaxe = new ItemRequirement("Any pickaxe", ItemCollections.PICKAXES).isNotConsumed();
needle = new ItemRequirement("Needle", ItemID.NEEDLE).isNotConsumed();
- needle.setTooltip("You can obtain this during the quest");
+ needle.setTooltip("Costume needle cannot be used as a substitute. You can obtain this during the quest");
thread = new ItemRequirement("Thread", ItemID.THREAD);
+ thread.setTooltip("Costume needle cannot be used as a substitute.");
leather = new ItemRequirement("Leather", ItemID.LEATHER);
leather.setTooltip("You can obtain this during the quest");
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/enakhraslament/EnakhrasLament.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/enakhraslament/EnakhrasLament.java
index 2ad7f0d5977..e637dc28686 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/enakhraslament/EnakhrasLament.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/enakhraslament/EnakhrasLament.java
@@ -55,123 +55,163 @@
public class EnakhrasLament extends BasicQuestHelper
{
- //Items Required
- ItemRequirement pickaxe, chiselHighlighted, sandstone32, sandstone20, base, body, head, granite2, granite, leftArm, rightArm, leftLeg,
- rightLeg, kSigil, rSigil, mSigil, zSigil, softClay, camelMould, camelHead, breadOrCake, fireSpellRunes, airSpellRunes,
- mapleLog, log, oakLog, willowLog, coal, candle, air2, chaos, earth2, sandstone5, tinderbox, crumbleUndeadRunes, sandstone52,
- airStaff, airRuneOrStaff, earthRuneOrStaff, earthStaff;
-
+ // Required items
+ ItemRequirement pickaxe;
+ ItemRequirement chiselHighlighted;
+ ItemRequirement sandstone32;
+ ItemRequirement sandstone20;
+ ItemRequirement granite2;
+ ItemRequirement granite;
+ ItemRequirement softClay;
+ ItemRequirement breadOrCake;
+ ItemRequirement fireSpellRunes;
+ ItemRequirement airSpellRunes;
+ ItemRequirement mapleLog;
+ ItemRequirement log;
+ ItemRequirement oakLog;
+ ItemRequirement willowLog;
+ ItemRequirement coal;
+ ItemRequirement candle;
+ ItemRequirement air2;
+ ItemRequirement chaos;
+ ItemRequirement earth2;
+ ItemRequirement sandstone5;
+ ItemRequirement tinderbox;
+ ItemRequirement crumbleUndeadRunes;
+ ItemRequirement sandstone52;
+ ItemRequirement airStaff;
+ ItemRequirement airRuneOrStaff;
+ ItemRequirement earthRuneOrStaff;
+ ItemRequirement earthStaff;
+
+ // Mid-quest requirements
+ ItemRequirement base;
+ ItemRequirement body;
+ ItemRequirement head;
+ ItemRequirement leftArm;
+ ItemRequirement rightArm;
+ ItemRequirement leftLeg;
+ ItemRequirement rightLeg;
+ ItemRequirement kSigil;
+ ItemRequirement rSigil;
+ ItemRequirement mSigil;
+ ItemRequirement zSigil;
+ ItemRequirement camelMould;
+ ItemRequirement camelHead;
+
+ // Miscellaneous requirements
SpellbookRequirement onNormals;
-
- Requirement hasPlacedBase, hasTalkedToLazimAfterBase, hasPlacedBody, chiseledStatue, canChooseHead, inTempleEntranceRoom,
- inTempleGroundFloor, startedTemple, gottenLimbs, openedDoor1, openedDoor2, openedDoor3, openedDoor4, mPlaced, kPlaced,
- rPlaced, zPlaced, goneUpstairs, hasGottenRightArm, hasGottenRightLeg, inCentreRoom, inPuzzleFloor,
- fedBread, meltedFountain, cleanedFurnace, litBraziers, litLog, litOak, litWillow, litMaple, litCandle, litCoal, inNorthPuzzleRoom,
- inTopRoom, inLastRoom, wallNeedsChisel, finishedWall, protectFromMelee;
-
- DetailedQuestStep talkToLazim, bringLazim32Sandstone, useChiselOn32Sandstone, placeBase, bringLazim20Sandstone,
- useChiselOn20Sandstone, placeBody, talkToLazimToChooseHead, getGranite, craftHead, talkToLazimAboutBody,
- chiselStatue, giveLazimHead, talkToLazimInTemple, enterTemple, enterTempleDownLadder, cutOffLimb, takeM,
- talkToLazimForHead, enterDoor1, enterDoor2, enterDoor3, enterDoor4, enterKDoor, enterRDoor, enterMDoor, enterZDoor,
- takeZ, takeK, takeR, useStoneHeadOnPedestal, useSoftClayOnPedestal, useChiselOnGranite, goUpToPuzzles, useBread, castAirSpell,
- castFireSpell, useMapleLog, useOakLog, useLog, useWillowLog, useCoal, useCandle, passBarrier, goUpFromPuzzleRoom, castCrumbleUndead,
- goDownToFinalRoom, protectThenTalk, repairWall, useChiselOnWall, talkToAkthankos;
-
- //Zones
- Zone templeEntranceRoom, templeGroundFloor, centreRoom, puzzleFloor, northPuzzleRoom, topRoom, lastRoom;
+ VarbitRequirement hasPlacedBase;
+ VarbitRequirement hasTalkedToLazimAfterBase;
+ VarbitRequirement hasPlacedBody;
+ VarbitRequirement chiseledStatue;
+ VarbitRequirement canChooseHead;
+ ZoneRequirement inTempleEntranceRoom;
+ ZoneRequirement inTempleGroundFloor;
+ VarbitRequirement startedTemple;
+ VarbitRequirement gottenLimbs;
+ VarbitRequirement openedDoor1;
+ VarbitRequirement openedDoor2;
+ VarbitRequirement openedDoor3;
+ VarbitRequirement openedDoor4;
+ VarbitRequirement mPlaced;
+ VarbitRequirement kPlaced;
+ VarbitRequirement rPlaced;
+ VarbitRequirement zPlaced;
+ VarbitRequirement goneUpstairs;
+ VarbitRequirement hasGottenRightArm;
+ VarbitRequirement hasGottenRightLeg;
+ ZoneRequirement inCentreRoom;
+ ZoneRequirement inPuzzleFloor;
+ VarbitRequirement fedBread;
+ VarbitRequirement meltedFountain;
+ VarbitRequirement cleanedFurnace;
+ VarbitRequirement litBraziers;
+ VarbitRequirement litLog;
+ VarbitRequirement litOak;
+ VarbitRequirement litWillow;
+ VarbitRequirement litMaple;
+ VarbitRequirement litCandle;
+ VarbitRequirement litCoal;
+ ZoneRequirement inNorthPuzzleRoom;
+ ZoneRequirement inTopRoom;
+ ZoneRequirement inLastRoom;
+ VarbitRequirement wallNeedsChisel;
+ VarbitRequirement finishedWall;
+ PrayerRequirement protectFromMelee;
+
+ // Steps
+ NpcStep talkToLazim;
+ NpcStep bringLazim32Sandstone;
+ DetailedQuestStep useChiselOn32Sandstone;
+ ObjectStep placeBase;
+ NpcStep bringLazim20Sandstone;
+ DetailedQuestStep useChiselOn20Sandstone;
+ ObjectStep placeBody;
+ NpcStep talkToLazimToChooseHead;
+ NpcStep getGranite;
+ DetailedQuestStep craftHead;
+ NpcStep talkToLazimAboutBody;
+ DetailedQuestStep chiselStatue;
+ NpcStep giveLazimHead;
+ NpcStep talkToLazimInTemple;
+ ObjectStep enterTemple;
+ ObjectStep enterTempleDownLadder;
+ ObjectStep cutOffLimb;
+ ObjectStep takeM;
+ NpcStep talkToLazimForHead;
+ ObjectStep enterDoor1;
+ ObjectStep enterDoor2;
+ ObjectStep enterDoor3;
+ ObjectStep enterDoor4;
+ ObjectStep enterKDoor;
+ ObjectStep enterRDoor;
+ ObjectStep enterMDoor;
+ ObjectStep enterZDoor;
+ ObjectStep takeZ;
+ ObjectStep takeK;
+ ObjectStep takeR;
+ ObjectStep useStoneHeadOnPedestal;
+ ObjectStep useSoftClayOnPedestal;
+ DetailedQuestStep useChiselOnGranite;
+ ObjectStep goUpToPuzzles;
+ NpcStep useBread;
+ NpcStep castAirSpell;
+ NpcStep castFireSpell;
+ ObjectStep useMapleLog;
+ ObjectStep useOakLog;
+ ObjectStep useLog;
+ ObjectStep useWillowLog;
+ ObjectStep useCoal;
+ ObjectStep useCandle;
+ ObjectStep passBarrier;
+ ObjectStep goUpFromPuzzleRoom;
+ NpcStep castCrumbleUndead;
+ ObjectStep goDownToFinalRoom;
+ NpcStep protectThenTalk;
+ ObjectStep repairWall;
+ ObjectStep useChiselOnWall;
+ NpcStep talkToAkthankos;
+
+ // Zones
+ Zone templeEntranceRoom;
+ Zone templeGroundFloor;
+ Zone centreRoom;
+ Zone puzzleFloor;
+ Zone northPuzzleRoom;
+ Zone topRoom;
+ Zone lastRoom;
@Override
- public Map loadSteps()
+ protected void setupZones()
{
- initializeRequirements();
- setupConditions();
- setupSteps();
- Map steps = new HashMap<>();
-
- steps.put(0, talkToLazim);
-
- ConditionalStep makeAndPlaceBase = new ConditionalStep(this, bringLazim32Sandstone);
- makeAndPlaceBase.addStep(new Conditions(head, granite), giveLazimHead);
- makeAndPlaceBase.addStep(new Conditions(granite2, canChooseHead), craftHead);
- makeAndPlaceBase.addStep(canChooseHead, getGranite);
- makeAndPlaceBase.addStep(chiseledStatue, talkToLazimToChooseHead);
- makeAndPlaceBase.addStep(hasPlacedBody, chiselStatue);
- makeAndPlaceBase.addStep(body, placeBody);
- makeAndPlaceBase.addStep(sandstone20, useChiselOn20Sandstone);
- makeAndPlaceBase.addStep(hasTalkedToLazimAfterBase, bringLazim20Sandstone);
- makeAndPlaceBase.addStep(hasPlacedBase, talkToLazimAboutBody);
- makeAndPlaceBase.addStep(base, placeBase);
- makeAndPlaceBase.addStep(sandstone32, useChiselOn32Sandstone);
-
- steps.put(10, makeAndPlaceBase);
-
- ConditionalStep exploreBottomLayer = new ConditionalStep(this, enterTemple);
- exploreBottomLayer.addStep(new Conditions(camelHead, inPuzzleFloor), useStoneHeadOnPedestal);
- exploreBottomLayer.addStep(camelMould, useChiselOnGranite);
- exploreBottomLayer.addStep(inPuzzleFloor, useSoftClayOnPedestal);
- exploreBottomLayer.addStep(new Conditions(gottenLimbs, inTempleGroundFloor, openedDoor1, openedDoor2, openedDoor3, openedDoor4), goUpToPuzzles);
- exploreBottomLayer.addStep(new Conditions(gottenLimbs, inTempleGroundFloor, openedDoor1, openedDoor2, openedDoor3, rSigil), enterDoor4);
- exploreBottomLayer.addStep(new Conditions(gottenLimbs, inTempleGroundFloor, openedDoor1, openedDoor2, openedDoor3), takeR);
- exploreBottomLayer.addStep(new Conditions(gottenLimbs, inTempleGroundFloor, openedDoor1, openedDoor2, kSigil), enterDoor3);
- exploreBottomLayer.addStep(new Conditions(gottenLimbs, inTempleGroundFloor, openedDoor1, openedDoor2), takeK);
- // It's possible to skip the rest of this, but it skips some of the quest story and leaves doors locked after you finish, so this encourages players to explore
- exploreBottomLayer.addStep(new Conditions(gottenLimbs, inTempleGroundFloor, openedDoor1, zSigil), enterDoor2);
- exploreBottomLayer.addStep(new Conditions(gottenLimbs, inTempleGroundFloor, openedDoor1), takeZ);
- exploreBottomLayer.addStep(new Conditions(gottenLimbs, inTempleGroundFloor, mSigil), enterDoor1);
- exploreBottomLayer.addStep(new Conditions(gottenLimbs, inTempleGroundFloor), takeM);
- exploreBottomLayer.addStep(new Conditions(startedTemple, inTempleGroundFloor), cutOffLimb);
- exploreBottomLayer.addStep(inTempleGroundFloor, talkToLazimInTemple);
- exploreBottomLayer.addStep(inTempleEntranceRoom, enterTempleDownLadder);
-
- steps.put(20, exploreBottomLayer);
-
- ConditionalStep puzzles = new ConditionalStep(this, enterTemple);
- puzzles.addStep(new Conditions(fedBread, inPuzzleFloor, meltedFountain, cleanedFurnace, litLog, litOak, litWillow, litMaple, litCandle), useCoal);
- puzzles.addStep(new Conditions(fedBread, inPuzzleFloor, meltedFountain, cleanedFurnace, litLog, litOak, litWillow, litMaple), useCandle);
- puzzles.addStep(new Conditions(fedBread, inPuzzleFloor, meltedFountain, cleanedFurnace, litLog, litOak, litWillow), useMapleLog);
- puzzles.addStep(new Conditions(fedBread, inPuzzleFloor, meltedFountain, cleanedFurnace, litLog, litOak), useWillowLog);
- puzzles.addStep(new Conditions(fedBread, inPuzzleFloor, meltedFountain, cleanedFurnace, litLog), useOakLog);
- puzzles.addStep(new Conditions(fedBread, inPuzzleFloor, meltedFountain, cleanedFurnace), useLog);
- puzzles.addStep(new Conditions(fedBread, inPuzzleFloor, meltedFountain), castAirSpell);
- puzzles.addStep(new Conditions(fedBread, inPuzzleFloor), castFireSpell);
- puzzles.addStep(inPuzzleFloor, useBread);
- puzzles.addStep(inTempleGroundFloor, goUpToPuzzles);
- puzzles.addStep(inTempleEntranceRoom, enterTempleDownLadder);
-
- steps.put(30, puzzles);
-
- ConditionalStep topFloorPuzzle = new ConditionalStep(this, enterTemple);
- topFloorPuzzle.addStep(inTopRoom, castCrumbleUndead);
- topFloorPuzzle.addStep(inNorthPuzzleRoom, goUpFromPuzzleRoom);
- topFloorPuzzle.addStep(inPuzzleFloor, passBarrier);
- topFloorPuzzle.addStep(inTempleGroundFloor, goUpToPuzzles);
- topFloorPuzzle.addStep(inTempleEntranceRoom, enterTempleDownLadder);
-
- steps.put(40, topFloorPuzzle);
-
- ConditionalStep protectMeleePuzzle = new ConditionalStep(this, enterTemple);
- protectMeleePuzzle.addStep(inLastRoom, protectThenTalk);
- protectMeleePuzzle.addStep(inTopRoom, goDownToFinalRoom);
- protectMeleePuzzle.addStep(inNorthPuzzleRoom, goUpFromPuzzleRoom);
- protectMeleePuzzle.addStep(inPuzzleFloor, passBarrier);
- protectMeleePuzzle.addStep(inTempleGroundFloor, goUpToPuzzles);
- protectMeleePuzzle.addStep(inTempleEntranceRoom, enterTempleDownLadder);
-
- steps.put(50, protectMeleePuzzle);
-
- ConditionalStep repairWallForAkthankos = new ConditionalStep(this, enterTemple);
- repairWallForAkthankos.addStep(new Conditions(inLastRoom, wallNeedsChisel), useChiselOnWall);
- repairWallForAkthankos.addStep(new Conditions(inLastRoom, finishedWall), talkToAkthankos);
- repairWallForAkthankos.addStep(inLastRoom, repairWall);
- repairWallForAkthankos.addStep(inTopRoom, goDownToFinalRoom);
- repairWallForAkthankos.addStep(inNorthPuzzleRoom, goUpFromPuzzleRoom);
- repairWallForAkthankos.addStep(inPuzzleFloor, passBarrier);
- repairWallForAkthankos.addStep(inTempleGroundFloor, goUpToPuzzles);
- repairWallForAkthankos.addStep(inTempleEntranceRoom, enterTempleDownLadder);
-
- steps.put(60, repairWallForAkthankos);
-
- return steps;
+ templeEntranceRoom = new Zone(new WorldPoint(3124, 9328, 1), new WorldPoint(3128, 9330, 1));
+ templeGroundFloor = new Zone(new WorldPoint(3074, 9282, 0), new WorldPoint(3133, 9341, 0));
+ centreRoom = new Zone(new WorldPoint(3098, 9306, 0), new WorldPoint(3110, 9318, 0));
+ puzzleFloor = new Zone(new WorldPoint(3086, 9305, 1), new WorldPoint(3121, 9326, 1));
+ northPuzzleRoom = new Zone(new WorldPoint(2095, 9319, 1), new WorldPoint(3112, 9335, 1));
+ topRoom = new Zone(new WorldPoint(3097, 9299, 2), new WorldPoint(3113, 9334, 2));
+ lastRoom = new Zone(new WorldPoint(3096, 9291, 1), new WorldPoint(3112, 9302, 1));
}
@Override
@@ -260,22 +300,7 @@ protected void setupRequirements()
tinderbox = new ItemRequirement("Tinderbox", ItemID.TINDERBOX).isNotConsumed();
onNormals = new SpellbookRequirement(Spellbook.NORMAL);
- }
- @Override
- protected void setupZones()
- {
- templeEntranceRoom = new Zone(new WorldPoint(3124, 9328, 1), new WorldPoint(3128, 9330, 1));
- templeGroundFloor = new Zone(new WorldPoint(3074, 9282, 0), new WorldPoint(3133, 9341, 0));
- centreRoom = new Zone(new WorldPoint(3098, 9306, 0), new WorldPoint(3110, 9318, 0));
- puzzleFloor = new Zone(new WorldPoint(3086, 9305, 1), new WorldPoint(3121, 9326, 1));
- northPuzzleRoom = new Zone(new WorldPoint(2095, 9319, 1), new WorldPoint(3112, 9335, 1));
- topRoom = new Zone(new WorldPoint(3097, 9299, 2), new WorldPoint(3113, 9334, 2));
- lastRoom = new Zone(new WorldPoint(3096, 9291, 1), new WorldPoint(3112, 9302, 1));
- }
-
- public void setupConditions()
- {
hasPlacedBase = new VarbitRequirement(VarbitID.ENAKH_STATUE_MULTIVAR, 1);
hasPlacedBody = new VarbitRequirement(VarbitID.ENAKH_STATUE_MULTIVAR, 2);
chiseledStatue = new VarbitRequirement(VarbitID.ENAKH_STATUE_MULTIVAR, 3);
@@ -327,11 +352,15 @@ public void setupConditions()
protectFromMelee = new PrayerRequirement("Protect from Melee", Prayer.PROTECT_FROM_MELEE);
}
+
public void setupSteps()
{
- talkToLazim = new NpcStep(this, NpcID.ENAKH_LAZIM, new WorldPoint(3190, 2925, 0), "Before you begin, ensure that you have enough prayer points to use Protect from Melee for around five seconds (you will need this later in the temple). Talk to Lazim in the quarry south of the Bandit Camp.", pickaxe, onNormals);
+ talkToLazim = new NpcStep(this, NpcID.ENAKH_LAZIM, new WorldPoint(3190, 2925, 0), "Before you begin, ensure that you have enough prayer points to use" +
+ " Protect from Melee for around five seconds (you will need this later in the temple). Talk to Lazim in the quarry south of the Bandit Camp.",
+ pickaxe, onNormals);
talkToLazim.addDialogSteps("Yes.", "Of course!");
- bringLazim32Sandstone = new NpcStep(this, NpcID.ENAKH_LAZIM, new WorldPoint(3190, 2925, 0), "Get 32kg of sandstone and give it to Lazim. This can be done in batches, and you can mine some nearby.");
+ bringLazim32Sandstone = new NpcStep(this, NpcID.ENAKH_LAZIM, new WorldPoint(3190, 2925, 0), "Get 32kg of sandstone and give it to Lazim. This can be " +
+ "done in batches, and you can mine some nearby.");
bringLazim32Sandstone.addDialogStep("Okay, I'll get on with it.");
bringLazim32Sandstone.addDialogStep("Yes, I have more stone.");
bringLazim32Sandstone.addDialogStep("Here's a large 10 kg block.");
@@ -343,7 +372,8 @@ public void setupSteps()
talkToLazimAboutBody = new NpcStep(this, NpcID.ENAKH_LAZIM, new WorldPoint(3190, 2925, 0), "Talk to Lazim again.");
talkToLazimAboutBody.addDialogStep("I'll do it right away!");
- bringLazim20Sandstone = new NpcStep(this, NpcID.ENAKH_LAZIM, new WorldPoint(3190, 2925, 0), "Get 20kg of sandstone and give it to Lazim. This can be done in batches, and you can mine some nearby.");
+ bringLazim20Sandstone = new NpcStep(this, NpcID.ENAKH_LAZIM, new WorldPoint(3190, 2925, 0), "Get 20kg of sandstone and give it to Lazim. This can be " +
+ "done in batches, and you can mine some nearby.");
bringLazim20Sandstone.addDialogStep("I'll do it right away!");
bringLazim20Sandstone.addDialogStep("Yes, I have more stone.");
bringLazim20Sandstone.addDialogStep("Here's a large 10 kg block.");
@@ -353,23 +383,31 @@ public void setupSteps()
useChiselOn20Sandstone = new DetailedQuestStep(this, "Use a chisel on the sandstone 20kg.", chiselHighlighted, sandstone20);
placeBody = new ObjectStep(this, ObjectID.ENAKH_STATUE_EAST_MULTILOC, new WorldPoint(3190, 2926, 0), "Place the body on the sandstone base.", body);
- talkToLazimToChooseHead = new NpcStep(this, NpcID.ENAKH_LAZIM, new WorldPoint(3190, 2925, 0), "Talk to Lazim and choose the head you'd like the statue to have.");
+ talkToLazimToChooseHead = new NpcStep(this, NpcID.ENAKH_LAZIM, new WorldPoint(3190, 2925, 0), "Talk to Lazim and choose the head you'd like the " +
+ "statue to have.");
getGranite = new NpcStep(this, NpcID.ENAKH_LAZIM, new WorldPoint(3190, 2925, 0), "Get 2 x granite (5kg). You can mine some nearby.", granite2);
// TODO: Change head highlight text based on choice
- craftHead = new DetailedQuestStep(this, "Use a chisel on a piece of granite 5kg, and choose the head you decided on to craft.", chiselHighlighted, granite);
+ craftHead = new DetailedQuestStep(this, "Use a chisel on a piece of granite 5kg, and choose the head you decided on to craft.", chiselHighlighted,
+ granite);
- chiselStatue = new ObjectStep(this, ObjectID.ENAKH_STATUE_EAST_MULTILOC, new WorldPoint(3190, 2926, 0), "Use a chisel on the headless statue.", chiselHighlighted);
+ chiselStatue = new ObjectStep(this, ObjectID.ENAKH_STATUE_EAST_MULTILOC, new WorldPoint(3190, 2926, 0), "Use a chisel on the headless statue.",
+ chiselHighlighted);
chiselStatue.addIcon(ItemID.CHISEL);
giveLazimHead = new NpcStep(this, NpcID.ENAKH_LAZIM, new WorldPoint(3190, 2925, 0), "Give Lazim the head.", head);
- enterTemple = new ObjectStep(this, ObjectID.ENAKH_SECRET_BOULDER_MULTILOC_E, new WorldPoint(3194, 2925, 0), "Enter the temple south of the Bandit's Camp.");
- enterTempleDownLadder = new ObjectStep(this, ObjectID.ENAKH_TEMPLE_LADDERDOWN, new WorldPoint(3127, 9329, 1), "Enter the temple south of the Bandit's Camp.");
+ enterTemple = new ObjectStep(this, ObjectID.ENAKH_SECRET_BOULDER_MULTILOC_E, new WorldPoint(3194, 2925, 0), "Enter the temple south of the Bandit's " +
+ "Camp.");
+ enterTempleDownLadder = new ObjectStep(this, ObjectID.ENAKH_TEMPLE_LADDERDOWN, new WorldPoint(3127, 9329, 1), "Enter the temple south of the Bandit's" +
+ " Camp.");
talkToLazimInTemple = new NpcStep(this, NpcID.ENAKH_LAZIM, new WorldPoint(3127, 9324, 0), "Talk to Lazim in the temple.");
- cutOffLimb = new ObjectStep(this, ObjectID.ENAKH_FALLEN_STATUE_EAST_MULTILOC, new WorldPoint(3130, 9326, 0), "Use a chisel on the fallen statue to get all its limbs.", chiselHighlighted);
- cutOffLimb.addDialogSteps("Remove the statue's left arm", "Remove the statue's right arm", "Remove the statue's left leg", "Remove the statue's right leg");
+ cutOffLimb = new ObjectStep(this, ObjectID.ENAKH_FALLEN_STATUE_EAST_MULTILOC, new WorldPoint(3130, 9326, 0), "Use a chisel on the fallen statue to " +
+ "get all its limbs.", chiselHighlighted);
+ cutOffLimb.addDialogSteps("Remove the statue's left arm", "Remove the statue's right arm", "Remove the statue's left leg", "Remove the statue's right" +
+ " leg");
+ cutOffLimb.addIcon(ItemID.CHISEL);
takeM = new ObjectStep(this, ObjectID.ENAKH_PEDESTAL_SIGIL_M, new WorldPoint(3128, 9319, 0), "Take the M sigil from the pedestal in the room.");
takeZ = new ObjectStep(this, ObjectID.ENAKH_PEDESTAL_SIGIL_Z, new WorldPoint(3097, 9336, 0), "Take the Z sigil from the pedestal in the north room.");
@@ -392,101 +430,199 @@ public void setupSteps()
enterMDoor = new ObjectStep(this, ObjectID.ENAKH_DOOR_M_SIGIL, new WorldPoint(3097, 9312, 0), "Enter the door with an M.", mSigil);
enterZDoor = new ObjectStep(this, ObjectID.ENAKH_DOOR_Z_SIGIL, new WorldPoint(3104, 9305, 0), "Enter the door with a Z.", zSigil);
- goUpToPuzzles = new ObjectStep(this, ObjectID.ENAKH_TEMPLE_LADDERUP, new WorldPoint(3104, 9309, 0), "Open the central room's doors using the metal letters. Go up the ladder in the central room.");
+ goUpToPuzzles = new ObjectStep(this, ObjectID.ENAKH_TEMPLE_LADDERUP, new WorldPoint(3104, 9309, 0), "Open the central room's doors using the metal " +
+ "letters. Go up the ladder in the central room.");
useSoftClayOnPedestal = new ObjectStep(this, ObjectID.ENAKH_PEDESTAL_MULTILOC, new WorldPoint(3104, 9312, 1),
"Use soft clay on the pedestal.", softClay.highlighted());
useChiselOnGranite = new DetailedQuestStep(this, "Use a chisel on granite (5kg).", granite, chiselHighlighted);
- useStoneHeadOnPedestal = new ObjectStep(this, ObjectID.ENAKH_PEDESTAL_MULTILOC, new WorldPoint(3104, 9312, 1), "Use the camel stone head on the pedestal.", camelHead);
+ useStoneHeadOnPedestal = new ObjectStep(this, ObjectID.ENAKH_PEDESTAL_MULTILOC, new WorldPoint(3104, 9312, 1), "Use the camel stone head on the " +
+ "pedestal.", camelHead);
useStoneHeadOnPedestal.addIcon(ItemID.ENAKH_STONE_HEAD_AKTHANAKOS);
- useBread = new NpcStep(this, NpcID.ENAKH_PENTYN, new WorldPoint(3091, 9324, 1), "Right-click use bread or cake on Pentyn.", breadOrCake.highlighted());
- castFireSpell = new NpcStep(this, NpcID.ENAKH_DUMMY_FOUNTAIN, new WorldPoint(3092, 9308, 1), "Cast a fire spell on the frozen fountain.", fireSpellRunes, onNormals);
- castAirSpell = new NpcStep(this, NpcID.ENAKH_DUMMY_FURNACE, new WorldPoint(3116, 9323, 1), "Cast an air spell on the furnace.", airSpellRunes, onNormals);
- useMapleLog = new ObjectStep(this, ObjectID.ENAKH_BRAZIER_4_MULTILOC, new WorldPoint(3114, 9309, 1), "Use a maple log on the north west brazier.", mapleLog);
- useMapleLog.addIcon(ItemID.MAPLE_LOGS);
+ useBread = new NpcStep(this, NpcID.ENAKH_PENTYN, new WorldPoint(3091, 9324, 1), "Right-click use bread or cake on Pentyn.", breadOrCake.highlighted());
+ castFireSpell = new NpcStep(this, NpcID.ENAKH_DUMMY_FOUNTAIN, new WorldPoint(3092, 9308, 1), "Cast a fire spell on the frozen fountain.",
+ fireSpellRunes, onNormals);
+ castAirSpell = new NpcStep(this, NpcID.ENAKH_DUMMY_FURNACE, new WorldPoint(3116, 9323, 1), "Cast an air spell on the furnace.", airSpellRunes,
+ onNormals);
+
+ // Shadow Room Puzzle
+ useLog = new ObjectStep(this, ObjectID.ENAKH_BRAZIER_1_MULTILOC, new WorldPoint(3114, 9306, 1), "Use a normal log on the south west brazier.", log);
+ useLog.addIcon(ItemID.LOGS);
useOakLog = new ObjectStep(this, ObjectID.ENAKH_BRAZIER_2_MULTILOC, new WorldPoint(3116, 9306, 1), "Use an oak log on the south brazier.", oakLog);
useOakLog.addIcon(ItemID.OAK_LOGS);
- useWillowLog = new ObjectStep(this, ObjectID.ENAKH_BRAZIER_1_MULTILOC, new WorldPoint(3114, 9306, 1), "Use a willow log on the south east brazier.", willowLog);
+ useWillowLog = new ObjectStep(this, ObjectID.ENAKH_BRAZIER_3_MULTILOC, new WorldPoint(3118, 9306, 1), "Use a willow log on the south east brazier.",
+ willowLog);
useWillowLog.addIcon(ItemID.WILLOW_LOGS);
- useLog = new ObjectStep(this, ObjectID.ENAKH_BRAZIER_3_MULTILOC, new WorldPoint(3118, 9306, 1), "Use a normal log on the south west brazier.", log);
- useLog.addIcon(ItemID.LOGS);
- useCoal = new ObjectStep(this, ObjectID.ENAKH_BRAZIER_6_MULTILOC, new WorldPoint(3118, 9309, 1), "Use coal on the north east brazier.", coal);
- useCoal.addIcon(ItemID.COAL);
+ useMapleLog = new ObjectStep(this, ObjectID.ENAKH_BRAZIER_4_MULTILOC, new WorldPoint(3114, 9309, 1), "Use a maple log on the north west brazier.",
+ mapleLog);
+ useMapleLog.addIcon(ItemID.MAPLE_LOGS);
useCandle = new ObjectStep(this, ObjectID.ENAKH_BRAZIER_5_MULTILOC, new WorldPoint(3116, 9309, 1), "Use a candle on the north brazier.", candle);
useCandle.addIcon(ItemID.UNLIT_CANDLE);
+ useCoal = new ObjectStep(this, ObjectID.ENAKH_BRAZIER_6_MULTILOC, new WorldPoint(3118, 9309, 1), "Use coal on the north east brazier.", coal);
+ useCoal.addIcon(ItemID.COAL);
passBarrier = new ObjectStep(this, ObjectID.ENAKH_MAGIC_WALL, new WorldPoint(3104, 9319, 1), "Pass through the magic barrier and go up the ladder.");
goUpFromPuzzleRoom = new ObjectStep(this, ObjectID.ENAKH_TEMPLE_LADDERUP, new WorldPoint(3104, 9332, 1), "Go up the ladder.");
passBarrier.addSubSteps(goUpFromPuzzleRoom);
- castCrumbleUndead = new NpcStep(this, NpcID.ENAKH_BONEGUARD, new WorldPoint(3104, 9307, 2), "Cast crumble undead on the Boneguard.", earth2, airRuneOrStaff, chaos, onNormals);
+ castCrumbleUndead = new NpcStep(this, NpcID.ENAKH_BONEGUARD, new WorldPoint(3104, 9307, 2), "Cast crumble undead on the Boneguard.", earth2,
+ airRuneOrStaff, chaos, onNormals);
- goDownToFinalRoom = new ObjectStep(this, ObjectID.ENAKH_TEMPLE_PILLAR_LADDER_TOP, new WorldPoint(3105, 9300, 2), "Climb down the stone ladder past the Boneguard.");
+ goDownToFinalRoom = new ObjectStep(this, ObjectID.ENAKH_TEMPLE_PILLAR_LADDER_TOP, new WorldPoint(3105, 9300, 2), "Climb down the stone ladder past " +
+ "the Boneguard.");
protectThenTalk = new NpcStep(this, NpcID.ENAKH_AKTHANAKOS_BONEGUARD, new WorldPoint(3105, 9297, 1),
"Put on Protect from Melee, then talk to the Boneguard.", protectFromMelee);
- repairWall = new ObjectStep(this, ObjectID.ENAKH_LARGEWALL_L_MULTILOC, new WorldPoint(3107, 9291, 1), "Take sandstone from the nearby rubble, and use it to repair the south wall. For each piece added, use a chisel on the wall.", sandstone5);
+ repairWall = new ObjectStep(this, ObjectID.ENAKH_LARGEWALL_L_MULTILOC, new WorldPoint(3107, 9291, 1), "Take sandstone from the nearby rubble, and use" +
+ " it to repair the south wall. For each piece added, use a chisel on the wall.", sandstone5);
repairWall.addDialogSteps("Of course, I'll help you out.", "Okay, I'll start building.");
repairWall.addIcon(ItemID.ENAKH_SANDSTONE_MEDIUM);
- useChiselOnWall = new ObjectStep(this, ObjectID.ENAKH_LARGEWALL_L_MULTILOC, new WorldPoint(3107, 9291, 1), "Use a chisel on the wall.", chiselHighlighted);
+ useChiselOnWall = new ObjectStep(this, ObjectID.ENAKH_LARGEWALL_L_MULTILOC, new WorldPoint(3107, 9291, 1), "Use a chisel on the wall.",
+ chiselHighlighted);
useChiselOnWall.addDialogSteps("Of course, I'll help you out.", "Okay, I'll start building.");
useChiselOnWall.addIcon(ItemID.CHISEL);
repairWall.addSubSteps(useChiselOnWall);
talkToAkthankos = new NpcStep(this, NpcID.ENAKH_AKTHANAKOS_BONEGUARD, new WorldPoint(3105, 9297, 1), "Talk to the Boneguard to finish the quest.");
- ((NpcStep) talkToAkthankos).addAlternateNpcs(NpcID.ENAKH_AKTHANAKOS_FREED);
+ talkToAkthankos.addAlternateNpcs(NpcID.ENAKH_AKTHANAKOS_FREED);
+
+ }
+
+ @Override
+ public Map loadSteps()
+ {
+ initializeRequirements();
+ setupSteps();
+
+ var steps = new HashMap();
+
+ steps.put(0, talkToLazim);
+
+ ConditionalStep makeAndPlaceBase = new ConditionalStep(this, bringLazim32Sandstone);
+ makeAndPlaceBase.addStep(new Conditions(head, granite), giveLazimHead);
+ makeAndPlaceBase.addStep(new Conditions(granite2, canChooseHead), craftHead);
+ makeAndPlaceBase.addStep(canChooseHead, getGranite);
+ makeAndPlaceBase.addStep(chiseledStatue, talkToLazimToChooseHead);
+ makeAndPlaceBase.addStep(hasPlacedBody, chiselStatue);
+ makeAndPlaceBase.addStep(body, placeBody);
+ makeAndPlaceBase.addStep(sandstone20, useChiselOn20Sandstone);
+ makeAndPlaceBase.addStep(hasTalkedToLazimAfterBase, bringLazim20Sandstone);
+ makeAndPlaceBase.addStep(hasPlacedBase, talkToLazimAboutBody);
+ makeAndPlaceBase.addStep(base, placeBase);
+ makeAndPlaceBase.addStep(sandstone32, useChiselOn32Sandstone);
+
+ steps.put(10, makeAndPlaceBase);
+ ConditionalStep exploreBottomLayer = new ConditionalStep(this, enterTemple);
+ exploreBottomLayer.addStep(new Conditions(camelHead, inPuzzleFloor), useStoneHeadOnPedestal);
+ exploreBottomLayer.addStep(camelMould, useChiselOnGranite);
+ exploreBottomLayer.addStep(inPuzzleFloor, useSoftClayOnPedestal);
+ exploreBottomLayer.addStep(new Conditions(gottenLimbs, inTempleGroundFloor, openedDoor1, openedDoor2, openedDoor3, openedDoor4), goUpToPuzzles);
+ exploreBottomLayer.addStep(new Conditions(gottenLimbs, inTempleGroundFloor, openedDoor1, openedDoor2, openedDoor3, rSigil), enterDoor4);
+ exploreBottomLayer.addStep(new Conditions(gottenLimbs, inTempleGroundFloor, openedDoor1, openedDoor2, openedDoor3), takeR);
+ exploreBottomLayer.addStep(new Conditions(gottenLimbs, inTempleGroundFloor, openedDoor1, openedDoor2, kSigil), enterDoor3);
+ exploreBottomLayer.addStep(new Conditions(gottenLimbs, inTempleGroundFloor, openedDoor1, openedDoor2), takeK);
+ // It's possible to skip the rest of this, but it skips some of the quest story and leaves doors locked after you finish, so this encourages players
+ // to explore
+ exploreBottomLayer.addStep(new Conditions(gottenLimbs, inTempleGroundFloor, openedDoor1, zSigil), enterDoor2);
+ exploreBottomLayer.addStep(new Conditions(gottenLimbs, inTempleGroundFloor, openedDoor1), takeZ);
+ exploreBottomLayer.addStep(new Conditions(gottenLimbs, inTempleGroundFloor, mSigil), enterDoor1);
+ exploreBottomLayer.addStep(new Conditions(gottenLimbs, inTempleGroundFloor), takeM);
+ exploreBottomLayer.addStep(new Conditions(startedTemple, inTempleGroundFloor), cutOffLimb);
+ exploreBottomLayer.addStep(inTempleGroundFloor, talkToLazimInTemple);
+ exploreBottomLayer.addStep(inTempleEntranceRoom, enterTempleDownLadder);
+
+ steps.put(20, exploreBottomLayer);
+
+ ConditionalStep puzzles = new ConditionalStep(this, enterTemple);
+ puzzles.addStep(new Conditions(fedBread, inPuzzleFloor, meltedFountain, cleanedFurnace, litLog, litOak, litWillow, litMaple, litCandle), useCoal);
+ puzzles.addStep(new Conditions(fedBread, inPuzzleFloor, meltedFountain, cleanedFurnace, litLog, litOak, litWillow, litMaple), useCandle);
+ puzzles.addStep(new Conditions(fedBread, inPuzzleFloor, meltedFountain, cleanedFurnace, litLog, litOak, litWillow), useMapleLog);
+ puzzles.addStep(new Conditions(fedBread, inPuzzleFloor, meltedFountain, cleanedFurnace, litLog, litOak), useWillowLog);
+ puzzles.addStep(new Conditions(fedBread, inPuzzleFloor, meltedFountain, cleanedFurnace, litLog), useOakLog);
+ puzzles.addStep(new Conditions(fedBread, inPuzzleFloor, meltedFountain, cleanedFurnace), useLog);
+ puzzles.addStep(new Conditions(fedBread, inPuzzleFloor, meltedFountain), castAirSpell);
+ puzzles.addStep(new Conditions(fedBread, inPuzzleFloor), castFireSpell);
+ puzzles.addStep(inPuzzleFloor, useBread);
+ puzzles.addStep(inTempleGroundFloor, goUpToPuzzles);
+ puzzles.addStep(inTempleEntranceRoom, enterTempleDownLadder);
+
+ steps.put(30, puzzles);
+
+ ConditionalStep topFloorPuzzle = new ConditionalStep(this, enterTemple);
+ topFloorPuzzle.addStep(inTopRoom, castCrumbleUndead);
+ topFloorPuzzle.addStep(inNorthPuzzleRoom, goUpFromPuzzleRoom);
+ topFloorPuzzle.addStep(inPuzzleFloor, passBarrier);
+ topFloorPuzzle.addStep(inTempleGroundFloor, goUpToPuzzles);
+ topFloorPuzzle.addStep(inTempleEntranceRoom, enterTempleDownLadder);
+
+ steps.put(40, topFloorPuzzle);
+
+ ConditionalStep protectMeleePuzzle = new ConditionalStep(this, enterTemple);
+ protectMeleePuzzle.addStep(inLastRoom, protectThenTalk);
+ protectMeleePuzzle.addStep(inTopRoom, goDownToFinalRoom);
+ protectMeleePuzzle.addStep(inNorthPuzzleRoom, goUpFromPuzzleRoom);
+ protectMeleePuzzle.addStep(inPuzzleFloor, passBarrier);
+ protectMeleePuzzle.addStep(inTempleGroundFloor, goUpToPuzzles);
+ protectMeleePuzzle.addStep(inTempleEntranceRoom, enterTempleDownLadder);
+
+ steps.put(50, protectMeleePuzzle);
+
+ ConditionalStep repairWallForAkthankos = new ConditionalStep(this, enterTemple);
+ repairWallForAkthankos.addStep(new Conditions(inLastRoom, wallNeedsChisel), useChiselOnWall);
+ repairWallForAkthankos.addStep(new Conditions(inLastRoom, finishedWall), talkToAkthankos);
+ repairWallForAkthankos.addStep(inLastRoom, repairWall);
+ repairWallForAkthankos.addStep(inTopRoom, goDownToFinalRoom);
+ repairWallForAkthankos.addStep(inNorthPuzzleRoom, goUpFromPuzzleRoom);
+ repairWallForAkthankos.addStep(inPuzzleFloor, passBarrier);
+ repairWallForAkthankos.addStep(inTempleGroundFloor, goUpToPuzzles);
+ repairWallForAkthankos.addStep(inTempleEntranceRoom, enterTempleDownLadder);
+
+ steps.put(60, repairWallForAkthankos);
+
+ return steps;
}
@Override
public List getItemRequirements()
{
- ArrayList reqs = new ArrayList<>();
- reqs.add(pickaxe);
- reqs.add(chiselHighlighted);
- reqs.add(softClay);
- reqs.add(breadOrCake);
- reqs.add(tinderbox);
- reqs.add(log);
- reqs.add(oakLog);
- reqs.add(willowLog);
- reqs.add(mapleLog);
- reqs.add(candle);
- reqs.add(coal);
- reqs.add(fireSpellRunes);
- reqs.add(airSpellRunes);
- reqs.add(crumbleUndeadRunes);
- int miningLevel = client.getRealSkillLevel(Skill.MINING);
- if (miningLevel < 45)
- {
- reqs.add(granite2);
- }
- if (miningLevel < 35)
- {
- reqs.add(sandstone52);
- }
- return reqs;
+ return List.of(
+ pickaxe,
+ chiselHighlighted,
+ softClay,
+ breadOrCake,
+ tinderbox,
+ log,
+ oakLog,
+ willowLog,
+ mapleLog,
+ candle,
+ coal,
+ fireSpellRunes,
+ airSpellRunes,
+ crumbleUndeadRunes,
+ granite2.hideConditioned(new SkillRequirement(Skill.MINING, 45)),
+ sandstone52.hideConditioned(new SkillRequirement(Skill.MINING, 35))
+ );
}
@Override
public List getGeneralRecommended()
{
- ArrayList req = new ArrayList<>();
- req.add(onNormals);
- return req;
+ return List.of(onNormals);
}
@Override
public List getGeneralRequirements()
{
- ArrayList req = new ArrayList<>();
- req.add(new SkillRequirement(Skill.CRAFTING, 50));
- req.add(new SkillRequirement(Skill.FIREMAKING, 45, true));
- req.add(new SkillRequirement(Skill.PRAYER, 43));
- req.add(new SkillRequirement(Skill.MAGIC, 39));
- return req;
+ return List.of(
+ new SkillRequirement(Skill.CRAFTING, 50),
+ new SkillRequirement(Skill.FIREMAKING, 45, true),
+ new SkillRequirement(Skill.PRAYER, 43),
+ new SkillRequirement(Skill.MAGIC, 39)
+ );
}
@Override
@@ -498,31 +634,50 @@ public QuestPointReward getQuestPointReward()
@Override
public List getExperienceRewards()
{
- return Arrays.asList(
- new ExperienceReward(Skill.CRAFTING, 7000),
- new ExperienceReward(Skill.MINING, 7000),
- new ExperienceReward(Skill.FIREMAKING, 7000),
- new ExperienceReward(Skill.MAGIC, 7000));
+ return List.of(
+ new ExperienceReward(Skill.CRAFTING, 7000),
+ new ExperienceReward(Skill.MINING, 7000),
+ new ExperienceReward(Skill.FIREMAKING, 7000),
+ new ExperienceReward(Skill.MAGIC, 7000)
+ );
}
@Override
public List getItemRewards()
{
- return Collections.singletonList(new ItemReward("Akthanakos's Camulet", ItemID.CAMULET, 1));
+ return List.of(
+ new ItemReward("Akthanakos's Camulet", ItemID.CAMULET, 1)
+ );
}
@Override
public List getPanels()
{
- List allSteps = new ArrayList<>();
- allSteps.add(new PanelDetails("Starting off", Collections.singletonList(talkToLazim)));
- allSteps.add(new PanelDetails("Craft a statue", Arrays.asList(bringLazim32Sandstone, useChiselOn32Sandstone, placeBase, talkToLazimAboutBody,
- bringLazim20Sandstone, useChiselOn20Sandstone, placeBody, chiselStatue, talkToLazimToChooseHead, getGranite, craftHead, giveLazimHead),
- pickaxe, chiselHighlighted, softClay, breadOrCake, tinderbox, log, oakLog, willowLog, mapleLog, candle, coal, fireSpellRunes, airSpellRunes, earth2, air2, chaos));
- allSteps.add(new PanelDetails("Explore the ground floor", Arrays.asList(talkToLazimInTemple, cutOffLimb, takeM, enterDoor1, enterDoor2, enterMDoor, goUpToPuzzles)));
- allSteps.add(new PanelDetails("Solve the puzzles", Arrays.asList(useSoftClayOnPedestal, useChiselOnGranite, useStoneHeadOnPedestal, useBread, castFireSpell, castAirSpell,
- useLog, useOakLog, useWillowLog, useMapleLog, useCandle, useCoal)));
- allSteps.add(new PanelDetails("Free Akthankos", Arrays.asList(passBarrier, goUpFromPuzzleRoom, castCrumbleUndead, goDownToFinalRoom, protectThenTalk, repairWall, talkToAkthankos)));
+ var allSteps = new ArrayList();
+
+ allSteps.add(new PanelDetails("Starting off", List.of(
+ talkToLazim
+ )));
+
+ allSteps.add(new PanelDetails("Craft a statue", List.of(
+ bringLazim32Sandstone, useChiselOn32Sandstone, placeBase, talkToLazimAboutBody, bringLazim20Sandstone,
+ useChiselOn20Sandstone, placeBody, chiselStatue, talkToLazimToChooseHead, getGranite, craftHead, giveLazimHead
+ ), List.of(pickaxe, chiselHighlighted, softClay, breadOrCake, tinderbox, log, oakLog, willowLog, mapleLog, candle, coal, fireSpellRunes,
+ airSpellRunes, earth2, air2, chaos
+ )));
+
+ allSteps.add(new PanelDetails("Explore the ground floor", List.of(
+ talkToLazimInTemple, cutOffLimb, takeM, enterDoor1, enterDoor2, enterMDoor, goUpToPuzzles
+ )));
+
+ allSteps.add(new PanelDetails("Solve the puzzles", List.of(
+ useSoftClayOnPedestal, useChiselOnGranite, useStoneHeadOnPedestal, useBread, castFireSpell, castAirSpell,
+ useLog, useOakLog, useWillowLog, useMapleLog, useCandle, useCoal
+ )));
+
+ allSteps.add(new PanelDetails("Free Akthankos", List.of(
+ passBarrier, goUpFromPuzzleRoom, castCrumbleUndead, goDownToFinalRoom, protectThenTalk, repairWall, talkToAkthankos
+ )));
return allSteps;
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/misthalinmystery/MisthalinMystery.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/misthalinmystery/MisthalinMystery.java
index dfc4edfe626..7735e135ae5 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/misthalinmystery/MisthalinMystery.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/misthalinmystery/MisthalinMystery.java
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2020, Zoinkwiz
+ * Copyright (c) 2025, pajlada
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,6 +29,7 @@
import net.runelite.client.plugins.microbot.questhelper.questhelpers.BasicQuestHelper;
import net.runelite.client.plugins.microbot.questhelper.requirements.conditional.Conditions;
import net.runelite.client.plugins.microbot.questhelper.requirements.item.ItemRequirement;
+import static net.runelite.client.plugins.microbot.questhelper.requirements.util.LogicHelper.and;
import net.runelite.client.plugins.microbot.questhelper.requirements.util.Operation;
import net.runelite.client.plugins.microbot.questhelper.requirements.var.VarbitRequirement;
import net.runelite.client.plugins.microbot.questhelper.requirements.widget.WidgetTextRequirement;
@@ -36,21 +38,35 @@
import net.runelite.client.plugins.microbot.questhelper.rewards.ExperienceReward;
import net.runelite.client.plugins.microbot.questhelper.rewards.ItemReward;
import net.runelite.client.plugins.microbot.questhelper.rewards.QuestPointReward;
-import net.runelite.client.plugins.microbot.questhelper.steps.*;
+import net.runelite.client.plugins.microbot.questhelper.steps.ConditionalStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.DetailedQuestStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.NpcStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.ObjectStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.PuzzleWrapperStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.QuestStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.WidgetStep;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import net.runelite.api.Skill;
import net.runelite.api.coords.WorldPoint;
+import net.runelite.api.gameval.InterfaceID;
import net.runelite.api.gameval.ItemID;
import net.runelite.api.gameval.NpcID;
import net.runelite.api.gameval.ObjectID;
import net.runelite.api.gameval.VarbitID;
-import java.util.*;
-
-import static net.runelite.client.plugins.microbot.questhelper.requirements.util.LogicHelper.and;
-
public class MisthalinMystery extends BasicQuestHelper
{
- // Requirements
+ // Zones
+ Zone island;
+ Zone outside1;
+ Zone outside2;
+ Zone outside3;
+ Zone bossRoom;
+
+ // Miscellaneous requirements
ItemRequirement bucket;
ItemRequirement manorKey;
ItemRequirement knife;
@@ -108,18 +124,25 @@ public class MisthalinMystery extends BasicQuestHelper
ObjectStep observeThroughTree;
ObjectStep takeNote2;
DetailedQuestStep readNotes2;
+
+ PuzzleWrapperStep pwPlayPiano;
+ ConditionalStep cPlayPiano;
ObjectStep playPiano;
WidgetStep playD;
WidgetStep playE;
WidgetStep playA;
WidgetStep playDAgain;
DetailedQuestStep restartPiano;
- ObjectStep searchThePiano;
+ PuzzleWrapperStep searchThePiano;
+
ObjectStep returnOverBrokenWall;
ObjectStep openEmeraldDoor;
ObjectStep enterBandosGodswordRoomStep;
ObjectStep takeNote3;
DetailedQuestStep readNotes3;
+
+ PuzzleWrapperStep pwSolveFireplacePuzzle;
+ ConditionalStep solveFireplacePuzzle;
ObjectStep useKnifeOnFireplace;
ObjectStep searchFireplace;
WidgetStep clickSapphire;
@@ -129,7 +152,8 @@ public class MisthalinMystery extends BasicQuestHelper
WidgetStep clickOnyx;
WidgetStep clickRuby;
DetailedQuestStep restartGems;
- ObjectStep searchFireplaceForSapphireKey;
+ PuzzleWrapperStep searchFireplaceForSapphireKey;
+
ObjectStep goThroughSapphireDoor;
DetailedQuestStep reflectKnives;
ObjectStep continueThroughSapphireDoor;
@@ -139,162 +163,6 @@ public class MisthalinMystery extends BasicQuestHelper
ObjectStep leaveSapphireRoom;
NpcStep talkToMandy;
- // Zones
- Zone island;
- Zone outside1;
- Zone outside2;
- Zone outside3;
- Zone bossRoom;
-
- @Override
- public Map loadSteps()
- {
- initializeRequirements();
- setupConditions();
- setupSteps();
-
- var steps = new HashMap();
-
- steps.put(0, talkToAbigale);
-
- steps.put(5, talkToAbigale);
-
- var investigatingTheBarrel = new ConditionalStep(this, takeTheBoat);
- investigatingTheBarrel.addStep(new Conditions(onIsland, bucket), searchTheBarrel);
- investigatingTheBarrel.addStep(onIsland, takeTheBucket);
- steps.put(10, investigatingTheBarrel);
- steps.put(15, investigatingTheBarrel);
-
- var emptyTheBarrel = new ConditionalStep(this, takeTheBoat);
- emptyTheBarrel.addStep(new Conditions(onIsland, bucket), useBucketOnBarrel);
- emptyTheBarrel.addStep(onIsland, takeTheBucket);
- steps.put(20, emptyTheBarrel);
-
- var enterTheHouse = new ConditionalStep(this, takeTheBoat);
- enterTheHouse.addStep(new Conditions(onIsland, manorKey), openManorDoor);
- enterTheHouse.addStep(onIsland, searchTheBarrelForKey);
- steps.put(25, enterTheHouse);
-
- var pinkDoor = new ConditionalStep(this, takeTheBoat);
- pinkDoor.addStep(knife, tryToOpenPinkKnobDoor);
- pinkDoor.addStep(onIsland, takeKnife);
- steps.put(30, pinkDoor);
-
- var pickUpAndReadNotes1 = new ConditionalStep(this, takeTheBoat);
- pickUpAndReadNotes1.addStep(new Conditions(onIsland, notes1), readNotes1);
- pickUpAndReadNotes1.addStep(onIsland, takeNote1);
- steps.put(35, pickUpAndReadNotes1);
-
- var cutPainting = new ConditionalStep(this, takeTheBoat);
- cutPainting.addStep(new Conditions(onIsland, knife), useKnifeOnPainting);
- cutPainting.addStep(onIsland, takeKnife);
- steps.put(40, cutPainting);
-
- var enterRubyRoom = new ConditionalStep(this, takeTheBoat);
- enterRubyRoom.addStep(new Conditions(onIsland, rubyKey), goThroughRubyDoor);
- enterRubyRoom.addStep(onIsland, searchPainting);
- steps.put(45, enterRubyRoom);
-
- var lightCandles = new ConditionalStep(this, takeTheBoat);
- lightCandles.addStep(new Conditions(onIsland, tinderbox, litCandle1, litCandle2, litCandle3), lightCandle4);
- lightCandles.addStep(new Conditions(onIsland, tinderbox, litCandle1, litCandle2), lightCandle3);
- lightCandles.addStep(new Conditions(onIsland, tinderbox, litCandle1), lightCandle2);
- lightCandles.addStep(new Conditions(onIsland, tinderbox), lightCandle1);
- lightCandles.addStep(onIsland, takeTinderbox);
- steps.put(50, lightCandles);
-
- var lightFuseOnBarrel = new ConditionalStep(this, takeTheBoat);
- lightFuseOnBarrel.addStep(new Conditions(onIsland, tinderbox), lightBarrel);
- lightFuseOnBarrel.addStep(onIsland, takeTinderbox);
- steps.put(55, lightFuseOnBarrel);
- steps.put(60, leaveExplosionRoom);
-
- var goToLacey = new ConditionalStep(this, takeTheBoat);
- goToLacey.addStep(inOutsideArea, observeThroughTree);
- goToLacey.addStep(onIsland, climbWall);
- steps.put(65, goToLacey);
-
- var pickUpAndReadNotes2 = new ConditionalStep(this, takeTheBoat);
- pickUpAndReadNotes2.addStep(notes2, readNotes2);
- pickUpAndReadNotes2.addStep(inOutsideArea, takeNote2);
- pickUpAndReadNotes2.addStep(onIsland, climbWall);
- steps.put(70, pickUpAndReadNotes2);
-
- var playMusic = new ConditionalStep(this, takeTheBoat);
- playMusic.addStep(playedA, playDAgain);
- playMusic.addStep(playedE, playA);
- playMusic.addStep(playedD, playE);
- playMusic.addStep(new Conditions(playedAnyKey, inPianoWidget), restartPiano);
- playMusic.addStep(inPianoWidget, playD);
- playMusic.addStep(inOutsideArea, playPiano);
- playMusic.addStep(onIsland, climbWall);
- steps.put(75, playMusic);
-
- var openingTheEmeraldDoor = new ConditionalStep(this, takeTheBoat);
- openingTheEmeraldDoor.addStep(new Conditions(inOutsideArea, emeraldKey), returnOverBrokenWall);
- openingTheEmeraldDoor.addStep(inOutsideArea, searchThePiano);
- openingTheEmeraldDoor.addStep(new Conditions(onIsland, emeraldKey), openEmeraldDoor);
- openingTheEmeraldDoor.addStep(onIsland, climbWall);
- steps.put(80, openingTheEmeraldDoor);
-
- var enterBandosGodswordRoom = new ConditionalStep(this, takeTheBoat);
- enterBandosGodswordRoom.addStep(onIsland, enterBandosGodswordRoomStep);
- steps.put(85, enterBandosGodswordRoom);
-
- var startPuzzle3 = new ConditionalStep(this, takeTheBoat);
- startPuzzle3.addStep(new Conditions(onIsland, notes3), readNotes3);
- startPuzzle3.addStep(onIsland, takeNote3);
- steps.put(90, startPuzzle3);
-
- var openFireplace = new ConditionalStep(this, takeTheBoat);
- openFireplace.addStep(new Conditions(onIsland, knife), useKnifeOnFireplace);
- openFireplace.addStep(onIsland, takeKnife);
- steps.put(95, openFireplace);
-
- var solveFireplacePuzzle = new ConditionalStep(this, takeTheBoat);
- solveFireplacePuzzle.addStep(selectedOnyx, clickRuby);
- solveFireplacePuzzle.addStep(selectedEmerald, clickOnyx);
- solveFireplacePuzzle.addStep(selectedZenyte, clickEmerald);
- solveFireplacePuzzle.addStep(selectedDiamond, clickZenyte);
- solveFireplacePuzzle.addStep(selectedSaphire, clickDiamond);
- solveFireplacePuzzle.addStep(selectAnyGem, restartGems);
- solveFireplacePuzzle.addStep(inGemWidget, clickSapphire);
- solveFireplacePuzzle.addStep(onIsland, searchFireplace);
- steps.put(100, solveFireplacePuzzle);
-
- var openSapphireDoor = new ConditionalStep(this, takeTheBoat);
- openSapphireDoor.addStep(new Conditions(onIsland, sapphireKey), goThroughSapphireDoor);
- openSapphireDoor.addStep(onIsland, searchFireplaceForSapphireKey);
- steps.put(105, openSapphireDoor);
-
- var goDoBoss = new ConditionalStep(this, takeTheBoat);
- goDoBoss.addStep(inBossRoom, reflectKnives);
- goDoBoss.addStep(onIsland, goThroughSapphireDoor);
- steps.put(110, goDoBoss);
- steps.put(111, goDoBoss);
-
- var watchRevealCutscene = new ConditionalStep(this, takeTheBoat);
- watchRevealCutscene.addStep(inBossRoom, watchTheKillersReveal);
- watchRevealCutscene.addStep(onIsland, continueThroughSapphireDoor);
- steps.put(115, watchRevealCutscene);
-
- var goFightAbigale = new ConditionalStep(this, takeTheBoat);
- goFightAbigale.addStep(new Conditions(inBossRoom, killersKnife), fightAbigale);
- goFightAbigale.addStep(inBossRoom, pickUpKillersKnife);
- goFightAbigale.addStep(onIsland, continueThroughSapphireDoor);
- steps.put(120, goFightAbigale);
-
- var attemptToLeaveSapphireRoom = new ConditionalStep(this, takeTheBoat);
- attemptToLeaveSapphireRoom.addStep(onIsland, leaveSapphireRoom);
- steps.put(125, attemptToLeaveSapphireRoom);
-
- var finishTheQuest = new ConditionalStep(this, takeTheBoat);
- finishTheQuest.addStep(onIsland, talkToMandy);
- steps.put(130, finishTheQuest);
-
- return steps;
- }
-
@Override
protected void setupZones()
{
@@ -305,19 +173,20 @@ protected void setupZones()
bossRoom = new Zone(new WorldPoint(1619, 4825, 0), new WorldPoint(1627, 4834, 0));
}
- public void setupConditions()
+ @Override
+ protected void setupRequirements()
{
onIsland = new ZoneRequirement(island);
inOutsideArea = new ZoneRequirement(outside1, outside2, outside3);
inBossRoom = new ZoneRequirement(bossRoom);
- litCandle1 = new VarbitRequirement(4042, 1);
- litCandle2 = new VarbitRequirement(4041, 1);
- litCandle3 = new VarbitRequirement(4039, 1);
+ litCandle1 = new VarbitRequirement(VarbitID.MISTMYST_CANDLE4, 1);
+ litCandle2 = new VarbitRequirement(VarbitID.MISTMYST_CANDLE3, 1);
+ litCandle3 = new VarbitRequirement(VarbitID.MISTMYST_CANDLE1, 1);
- playedD = and(new VarbitRequirement(4044, 1), new VarbitRequirement(4049, 1));
- playedE = and(new VarbitRequirement(4045, 1), new VarbitRequirement(4049, 2));
- playedA = and(new VarbitRequirement(4046, 1), new VarbitRequirement(4049, 3));
+ playedD = and(new VarbitRequirement(VarbitID.MISTMYST_PIANO_D1, 1), new VarbitRequirement(VarbitID.MISTMYST_PIANO_ATTEMPTS, 1));
+ playedE = and(new VarbitRequirement(VarbitID.MISTMYST_PIANO_E, 1), new VarbitRequirement(VarbitID.MISTMYST_PIANO_ATTEMPTS, 2));
+ playedA = and(new VarbitRequirement(VarbitID.MISTMYST_PIANO_A, 1), new VarbitRequirement(VarbitID.MISTMYST_PIANO_ATTEMPTS, 3));
playedAnyKey = new VarbitRequirement(VarbitID.MISTMYST_PIANO_ATTEMPTS, 1, Operation.GREATER_EQUAL);
inPianoWidget = new WidgetTextRequirement(554, 20, "C");
inGemWidget = new WidgetTextRequirement(555, 1, 1, "Gemstone switch panel");
@@ -327,11 +196,7 @@ public void setupConditions()
selectedEmerald = and(new VarbitRequirement(4054, 1), new VarbitRequirement(4050, 4));
selectedOnyx = and(new VarbitRequirement(4055, 1), new VarbitRequirement(4050, 5));
selectAnyGem = new VarbitRequirement(VarbitID.MISTMYST_SWITCH_ATTEMPTS, 1, Operation.GREATER_EQUAL);
- }
- @Override
- protected void setupRequirements()
- {
bucket = new ItemRequirement("Bucket", ItemID.BUCKET_EMPTY);
manorKey = new ItemRequirement("Manor key", ItemID.MISTMYST_FRONTDOOR_KEY);
knife = new ItemRequirement("Knife", ItemID.KNIFE);
@@ -363,7 +228,7 @@ public void setupSteps()
tryToOpenPinkKnobDoor = new ObjectStep(this, ObjectID.MISTMYST_DOOR_REDTOPAZ, new WorldPoint(1635, 4838, 0), "Try to open the door with the pink handle.");
takeNote1 = new ObjectStep(this, ObjectID.MISTMYST_CLUE_LIBRARY, new WorldPoint(1635, 4839, 0), "Pick up the note that appeared.");
readNotes1 = new DetailedQuestStep(this, "Read the notes.", notes1.highlighted());
- useKnifeOnPainting = new ObjectStep(this, ObjectID.MISTMYST_PAINTING, new WorldPoint(1632, 4833, 0), "Use a knife on the marked painting.", knife);
+ useKnifeOnPainting = new ObjectStep(this, ObjectID.MISTMYST_PAINTING, new WorldPoint(1632, 4833, 0), "Use a knife on the marked painting.", knife.highlighted());
useKnifeOnPainting.addIcon(ItemID.KNIFE);
searchPainting = new ObjectStep(this, ObjectID.MISTMYST_PAINTING, new WorldPoint(1632, 4833, 0), "Search the painting for a ruby key.");
goThroughRubyDoor = new ObjectStep(this, ObjectID.MISTMYST_DOOR_RUBY, new WorldPoint(1640, 4828, 0), "Go through the door with the ruby handle.", rubyKey);
@@ -389,15 +254,24 @@ public void setupSteps()
takeNote2 = new ObjectStep(this, ObjectID.MISTMYST_CLUE_OUTSIDE, new WorldPoint(1632, 4850, 0), "Pick up the note that appeared by the fence.");
readNotes2 = new DetailedQuestStep(this, "Read the notes.", notes2.highlighted());
- playPiano = new ObjectStep(this, ObjectID.MISTMYST_PIANO, new WorldPoint(1647, 4842, 0), "Play the piano in the room to the south.");
- playD = new WidgetStep(this, "Play the D key.", 554, 21);
- playE = new WidgetStep(this, "Play the E key.", 554, 22);
- playA = new WidgetStep(this, "Play the A key.", 554, 25);
- playDAgain = new WidgetStep(this, "Play the D key again.", 554, 21);
+ playPiano = new ObjectStep(this, ObjectID.MISTMYST_PIANO, new WorldPoint(1647, 4841, 0), "");
+ playD = new WidgetStep(this, "Play the D key.", InterfaceID.MistmystPiano.LABEL_D1);
+ playE = new WidgetStep(this, "Play the E key.", InterfaceID.MistmystPiano.LABEL_E1);
+ playA = new WidgetStep(this, "Play the A key.", InterfaceID.MistmystPiano.LABEL_A2);
+ playDAgain = new WidgetStep(this, "Play the D key again.", InterfaceID.MistmystPiano.LABEL_D1);
restartPiano = new DetailedQuestStep(this, "Unfortunately you've played an incorrect key. Restart.");
playPiano.addSubSteps(restartPiano);
- searchThePiano = new ObjectStep(this, ObjectID.MISTMYST_PIANO, new WorldPoint(1647, 4842, 0), "Right-click search the piano for the emerald key.");
+ cPlayPiano = new ConditionalStep(this, playPiano, "Play the piano in the room to the south for the emerald key.");
+ cPlayPiano.addStep(playedA, playDAgain);
+ cPlayPiano.addStep(playedE, playA);
+ cPlayPiano.addStep(playedD, playE);
+ cPlayPiano.addStep(and(playedAnyKey, inPianoWidget), restartPiano);
+ cPlayPiano.addStep(inPianoWidget, playD);
+
+ pwPlayPiano = cPlayPiano.puzzleWrapStepWithDefaultText("Find the emerald key in the room to the south.");
+
+ searchThePiano = new ObjectStep(this, ObjectID.MISTMYST_PIANO, new WorldPoint(1647, 4842, 0), "Right-click search the piano for the emerald key.").puzzleWrapStep(true);
returnOverBrokenWall = new ObjectStep(this, ObjectID.MISTMYST_DESTRUCTABLE_WALL_CLIMBABLE, new WorldPoint(1648, 4829, 0),
"Climb back over the damaged wall into the manor.", emeraldKey);
@@ -408,13 +282,12 @@ public void setupSteps()
takeNote3 = new ObjectStep(this, ObjectID.MISTMYST_CLUE_KITCHEN, new WorldPoint(1630, 4842, 0), "Pick up the note that appeared by the door.");
readNotes3 = new DetailedQuestStep(this, "Read the notes.", notes3.highlighted());
- useKnifeOnFireplace = new ObjectStep(this, ObjectID.MISTMYST_FIREPLACE, new WorldPoint(1647, 4836, 0), "Use a knife on the unlit fireplace in the eastern room.", knife);
+ useKnifeOnFireplace = new ObjectStep(this, ObjectID.MISTMYST_FIREPLACE, new WorldPoint(1647, 4836, 0), "Use a knife on the unlit fireplace in the eastern room.", knife.highlighted());
useKnifeOnFireplace.addIcon(ItemID.KNIFE);
- searchFireplace = new ObjectStep(this, ObjectID.MISTMYST_FIREPLACE, new WorldPoint(1647, 4836, 0), "Search the fireplace.");
+ searchFireplace = new ObjectStep(this, ObjectID.MISTMYST_FIREPLACE, new WorldPoint(1647, 4836, 0), "");
restartGems = new DetailedQuestStep(this, "You've clicked a gem in the wrong order. Try restarting.");
- searchFireplace.addSubSteps(restartGems);
clickSapphire = new WidgetStep(this, "Click the sapphire.", 555, 19);
clickDiamond = new WidgetStep(this, "Click the diamond.", 555, 4);
@@ -423,7 +296,21 @@ public void setupSteps()
clickOnyx = new WidgetStep(this, "Click the onyx.", 555, 7);
clickRuby = new WidgetStep(this, "Click the ruby.", 555, 15);
- searchFireplaceForSapphireKey = new ObjectStep(this, ObjectID.MISTMYST_FIREPLACE, new WorldPoint(1647, 4836, 0), "Search the fireplace again for the sapphire key.");
+ searchFireplaceForSapphireKey = new ObjectStep(this, ObjectID.MISTMYST_FIREPLACE, new WorldPoint(1647, 4836, 0), "Search the fireplace again for the sapphire key.").puzzleWrapStep(true);
+
+ solveFireplacePuzzle = new ConditionalStep(this, takeTheBoat, "Search the fireplace and solve the puzzle for the sapphire key.");
+ solveFireplacePuzzle.addStep(selectedOnyx, clickRuby);
+ solveFireplacePuzzle.addStep(selectedEmerald, clickOnyx);
+ solveFireplacePuzzle.addStep(selectedZenyte, clickEmerald);
+ solveFireplacePuzzle.addStep(selectedDiamond, clickZenyte);
+ solveFireplacePuzzle.addStep(selectedSaphire, clickDiamond);
+ solveFireplacePuzzle.addStep(selectAnyGem, restartGems);
+ solveFireplacePuzzle.addStep(inGemWidget, clickSapphire);
+ solveFireplacePuzzle.addStep(onIsland, searchFireplace);
+
+ pwSolveFireplacePuzzle = solveFireplacePuzzle.puzzleWrapStepWithDefaultText("Find the sapphire key in the room to the east.");
+ pwSolveFireplacePuzzle.addSubSteps(searchFireplaceForSapphireKey);
+
goThroughSapphireDoor = new ObjectStep(this, ObjectID.MISTMYST_DOOR_SAPPHIRE, new WorldPoint(1628, 4829, 0),
"Go through the sapphire door.");
reflectKnives = new DetailedQuestStep(this, "This puzzle requires you to move the mirror to reflect the knives the murderer throws. You can tell which wardrobe the murderer will throw from by a black swirl that'll surround it.");
@@ -446,6 +333,140 @@ public void setupSteps()
"Talk to Mandy just outside the manor to complete the quest.");
}
+ @Override
+ public Map loadSteps()
+ {
+ initializeRequirements();
+ setupSteps();
+
+ var steps = new HashMap();
+
+ steps.put(0, talkToAbigale);
+
+ steps.put(5, talkToAbigale);
+
+ var investigatingTheBarrel = new ConditionalStep(this, takeTheBoat);
+ investigatingTheBarrel.addStep(and(onIsland, bucket), searchTheBarrel);
+ investigatingTheBarrel.addStep(onIsland, takeTheBucket);
+ steps.put(10, investigatingTheBarrel);
+ steps.put(15, investigatingTheBarrel);
+
+ var emptyTheBarrel = new ConditionalStep(this, takeTheBoat);
+ emptyTheBarrel.addStep(and(onIsland, bucket), useBucketOnBarrel);
+ emptyTheBarrel.addStep(onIsland, takeTheBucket);
+ steps.put(20, emptyTheBarrel);
+
+ var enterTheHouse = new ConditionalStep(this, takeTheBoat);
+ enterTheHouse.addStep(and(onIsland, manorKey), openManorDoor);
+ enterTheHouse.addStep(onIsland, searchTheBarrelForKey);
+ steps.put(25, enterTheHouse);
+
+ var pinkDoor = new ConditionalStep(this, takeTheBoat);
+ pinkDoor.addStep(knife, tryToOpenPinkKnobDoor);
+ pinkDoor.addStep(onIsland, takeKnife);
+ steps.put(30, pinkDoor);
+
+ var pickUpAndReadNotes1 = new ConditionalStep(this, takeTheBoat);
+ pickUpAndReadNotes1.addStep(and(onIsland, notes1), readNotes1);
+ pickUpAndReadNotes1.addStep(onIsland, takeNote1);
+ steps.put(35, pickUpAndReadNotes1);
+
+ var cutPainting = new ConditionalStep(this, takeTheBoat);
+ cutPainting.addStep(and(onIsland, knife), useKnifeOnPainting);
+ cutPainting.addStep(onIsland, takeKnife);
+ steps.put(40, cutPainting);
+
+ var enterRubyRoom = new ConditionalStep(this, takeTheBoat);
+ enterRubyRoom.addStep(and(onIsland, rubyKey), goThroughRubyDoor);
+ enterRubyRoom.addStep(onIsland, searchPainting);
+ steps.put(45, enterRubyRoom);
+
+ var lightCandles = new ConditionalStep(this, takeTheBoat);
+ lightCandles.addStep(and(onIsland, tinderbox, litCandle1, litCandle2, litCandle3), lightCandle4);
+ lightCandles.addStep(and(onIsland, tinderbox, litCandle1, litCandle2), lightCandle3);
+ lightCandles.addStep(and(onIsland, tinderbox, litCandle1), lightCandle2);
+ lightCandles.addStep(and(onIsland, tinderbox), lightCandle1);
+ lightCandles.addStep(onIsland, takeTinderbox);
+ steps.put(50, lightCandles);
+
+ var lightFuseOnBarrel = new ConditionalStep(this, takeTheBoat);
+ lightFuseOnBarrel.addStep(and(onIsland, tinderbox), lightBarrel);
+ lightFuseOnBarrel.addStep(onIsland, takeTinderbox);
+ steps.put(55, lightFuseOnBarrel);
+ steps.put(60, leaveExplosionRoom);
+
+ var goToLacey = new ConditionalStep(this, takeTheBoat);
+ goToLacey.addStep(inOutsideArea, observeThroughTree);
+ goToLacey.addStep(onIsland, climbWall);
+ steps.put(65, goToLacey);
+
+ var pickUpAndReadNotes2 = new ConditionalStep(this, takeTheBoat);
+ pickUpAndReadNotes2.addStep(notes2, readNotes2);
+ pickUpAndReadNotes2.addStep(inOutsideArea, takeNote2);
+ pickUpAndReadNotes2.addStep(onIsland, climbWall);
+ steps.put(70, pickUpAndReadNotes2);
+
+ var playMusic = new ConditionalStep(this, takeTheBoat);
+ playMusic.addStep(inOutsideArea, pwPlayPiano);
+ playMusic.addStep(onIsland, climbWall);
+ steps.put(75, playMusic);
+
+ var openingTheEmeraldDoor = new ConditionalStep(this, takeTheBoat);
+ openingTheEmeraldDoor.addStep(and(inOutsideArea, emeraldKey), returnOverBrokenWall);
+ openingTheEmeraldDoor.addStep(inOutsideArea, searchThePiano);
+ openingTheEmeraldDoor.addStep(and(onIsland, emeraldKey), openEmeraldDoor);
+ openingTheEmeraldDoor.addStep(onIsland, climbWall);
+ steps.put(80, openingTheEmeraldDoor);
+
+ var enterBandosGodswordRoom = new ConditionalStep(this, takeTheBoat);
+ enterBandosGodswordRoom.addStep(onIsland, enterBandosGodswordRoomStep);
+ steps.put(85, enterBandosGodswordRoom);
+
+ var startPuzzle3 = new ConditionalStep(this, takeTheBoat);
+ startPuzzle3.addStep(and(onIsland, notes3), readNotes3);
+ startPuzzle3.addStep(onIsland, takeNote3);
+ steps.put(90, startPuzzle3);
+
+ var openFireplace = new ConditionalStep(this, takeTheBoat);
+ openFireplace.addStep(and(onIsland, knife), useKnifeOnFireplace);
+ openFireplace.addStep(onIsland, takeKnife);
+ steps.put(95, openFireplace);
+
+ steps.put(100, pwSolveFireplacePuzzle);
+
+ var openSapphireDoor = new ConditionalStep(this, takeTheBoat);
+ openSapphireDoor.addStep(and(onIsland, sapphireKey), goThroughSapphireDoor);
+ openSapphireDoor.addStep(onIsland, searchFireplaceForSapphireKey);
+ steps.put(105, openSapphireDoor);
+
+ var goDoBoss = new ConditionalStep(this, takeTheBoat);
+ goDoBoss.addStep(inBossRoom, reflectKnives);
+ goDoBoss.addStep(onIsland, goThroughSapphireDoor);
+ steps.put(110, goDoBoss);
+ steps.put(111, goDoBoss);
+
+ var watchRevealCutscene = new ConditionalStep(this, takeTheBoat);
+ watchRevealCutscene.addStep(inBossRoom, watchTheKillersReveal);
+ watchRevealCutscene.addStep(onIsland, continueThroughSapphireDoor);
+ steps.put(115, watchRevealCutscene);
+
+ var goFightAbigale = new ConditionalStep(this, takeTheBoat);
+ goFightAbigale.addStep(and(inBossRoom, killersKnife), fightAbigale);
+ goFightAbigale.addStep(inBossRoom, pickUpKillersKnife);
+ goFightAbigale.addStep(onIsland, continueThroughSapphireDoor);
+ steps.put(120, goFightAbigale);
+
+ var attemptToLeaveSapphireRoom = new ConditionalStep(this, takeTheBoat);
+ attemptToLeaveSapphireRoom.addStep(onIsland, leaveSapphireRoom);
+ steps.put(125, attemptToLeaveSapphireRoom);
+
+ var finishTheQuest = new ConditionalStep(this, takeTheBoat);
+ finishTheQuest.addStep(onIsland, talkToMandy);
+ steps.put(130, finishTheQuest);
+
+ return steps;
+ }
+
@Override
public QuestPointReward getQuestPointReward()
{
@@ -473,17 +494,71 @@ public List getItemRewards()
@Override
public List getPanels()
{
- var allSteps = new ArrayList();
-
- allSteps.add(new PanelDetails("Talk to Abigale", Collections.singletonList(talkToAbigale)));
- allSteps.add(new PanelDetails("Enter the manor", Arrays.asList(takeTheBoat, takeTheBucket, searchTheBarrel, useBucketOnBarrel, searchTheBarrelForKey, openManorDoor)));
- allSteps.add(new PanelDetails("Solve the first puzzle", Arrays.asList(takeKnife, tryToOpenPinkKnobDoor, takeNote1, readNotes1, useKnifeOnPainting, searchPainting, goThroughRubyDoor)));
- allSteps.add(new PanelDetails("Solve the second puzzle", Arrays.asList(takeTinderbox, lightCandle1, lightBarrel, leaveExplosionRoom, climbWall)));
- allSteps.add(new PanelDetails("Solve the third puzzle", Arrays.asList(observeThroughTree, takeNote2, readNotes2, playPiano, playD, playE, playA, playDAgain, searchThePiano)));
- allSteps.add(new PanelDetails("Witness another murder", Arrays.asList(returnOverBrokenWall, openEmeraldDoor, enterBandosGodswordRoomStep)));
- allSteps.add(new PanelDetails("Solve the fourth puzzle", Arrays.asList(takeNote3, readNotes3, useKnifeOnFireplace, searchFireplace, clickSapphire, clickDiamond, clickZenyte, clickEmerald, clickOnyx, clickRuby, searchFireplaceForSapphireKey)));
- allSteps.add(new PanelDetails("Confront the killer", Arrays.asList(goThroughSapphireDoor, reflectKnives, watchTheKillersReveal, pickUpKillersKnife, fightAbigale, leaveSapphireRoom, talkToMandy)));
-
- return allSteps;
+ var steps = new ArrayList();
+
+ steps.add(new PanelDetails("Talk to Abigale", List.of(
+ talkToAbigale
+ )));
+
+ steps.add(new PanelDetails("Enter the manor", List.of(
+ takeTheBoat,
+ takeTheBucket,
+ searchTheBarrel,
+ useBucketOnBarrel,
+ searchTheBarrelForKey,
+ openManorDoor
+ )));
+
+ steps.add(new PanelDetails("Solve the first puzzle", List.of(
+ takeKnife,
+ tryToOpenPinkKnobDoor,
+ takeNote1,
+ readNotes1,
+ useKnifeOnPainting,
+ searchPainting,
+ goThroughRubyDoor
+ )));
+
+ steps.add(new PanelDetails("Solve the second puzzle", List.of(
+ takeTinderbox,
+ lightCandle1,
+ lightBarrel,
+ leaveExplosionRoom,
+ climbWall
+ )));
+
+ steps.add(new PanelDetails("Solve the third puzzle", List.of(
+ observeThroughTree,
+ takeNote2,
+ readNotes2,
+ pwPlayPiano,
+ searchThePiano
+ )));
+
+ steps.add(new PanelDetails("Witness another murder", List.of(
+ returnOverBrokenWall,
+ openEmeraldDoor,
+ enterBandosGodswordRoomStep
+ )));
+
+ steps.add(new PanelDetails("Solve the fourth puzzle", List.of(
+ takeNote3,
+ readNotes3,
+ useKnifeOnFireplace,
+ pwSolveFireplacePuzzle,
+ searchFireplaceForSapphireKey
+ )));
+
+ steps.add(new PanelDetails("Confront the killer", List.of(
+ goThroughSapphireDoor,
+ reflectKnives,
+ watchTheKillersReveal,
+ pickUpKillersKnife,
+ fightAbigale,
+ leaveSapphireRoom,
+ talkToMandy
+ )));
+
+ return steps;
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/piratestreasure/PiratesTreasure.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/piratestreasure/PiratesTreasure.java
index 50f2018ec9e..c3c4b80bb61 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/piratestreasure/PiratesTreasure.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/piratestreasure/PiratesTreasure.java
@@ -27,115 +27,159 @@
import net.runelite.client.plugins.microbot.questhelper.collections.ItemCollections;
import net.runelite.client.plugins.microbot.questhelper.panel.PanelDetails;
import net.runelite.client.plugins.microbot.questhelper.questhelpers.BasicQuestHelper;
-import net.runelite.client.plugins.microbot.questhelper.requirements.conditional.Conditions;
import net.runelite.client.plugins.microbot.questhelper.requirements.item.ItemRequirement;
+import net.runelite.client.plugins.microbot.questhelper.requirements.npc.NpcHintArrowRequirement;
import net.runelite.client.plugins.microbot.questhelper.requirements.zone.Zone;
import net.runelite.client.plugins.microbot.questhelper.requirements.zone.ZoneRequirement;
import net.runelite.client.plugins.microbot.questhelper.rewards.ItemReward;
import net.runelite.client.plugins.microbot.questhelper.rewards.QuestPointReward;
-import net.runelite.client.plugins.microbot.questhelper.steps.*;
+import net.runelite.client.plugins.microbot.questhelper.steps.ConditionalStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.DetailedQuestStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.DigStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.NpcStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.ObjectStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.QuestStep;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.gameval.ItemID;
import net.runelite.api.gameval.NpcID;
import net.runelite.api.gameval.ObjectID;
-import java.util.*;
-
public class PiratesTreasure extends BasicQuestHelper
{
- //ItemRequirements
- ItemRequirement sixtyCoins, spade, pirateMessage, chestKey;
+ // Required items
+ ItemRequirement sixtyCoins;
+ ItemRequirement spade;
+ ItemRequirement tenBananas;
- private NpcStep speakToRedbeard;
+ // Recommended items
+ ItemRequirement teleportVarrock;
+ ItemRequirement teleportFalador;
- private RumSmugglingStep smuggleRum;
+ // Mid-quest requirements
+ ItemRequirement pirateMessage;
+ ItemRequirement chestKey;
- private QuestStep readPirateMessage;
+ // Zones
+ Zone blueMoonFirst;
- private ObjectStep openChest, climbStairs;
+ // Miscellaneous requirements
+ ZoneRequirement inBlueMoonFirst;
- private QuestStep digUpTreasure;
+ // Steps
+ NpcStep speakToRedbeard;
- Zone blueMoonFirst;
+ QuestStep readPirateMessage;
- ZoneRequirement inBlueMoonFirst;
+ RumSmugglingStep smuggleRum;
+
+ ObjectStep openChest;
+ ObjectStep climbStairs;
+
+ QuestStep digUpTreasure;
+ NpcStep killGardener;
@Override
- public Map loadSteps()
+ protected void setupZones()
{
- initializeRequirements();
+ blueMoonFirst = new Zone(new WorldPoint(3213, 3405, 1), new WorldPoint(3234, 3391, 1));
+ }
+
+ @Override
+ protected void setupRequirements()
+ {
+ sixtyCoins = new ItemRequirement("Coins", ItemCollections.COINS, 60);
+ spade = new ItemRequirement("Spade", ItemID.SPADE).isNotConsumed();
- Map steps = new HashMap<>();
+ teleportVarrock = new ItemRequirement("A teleport to Varrock", ItemID.POH_TABLET_VARROCKTELEPORT);
+ teleportFalador = new ItemRequirement("A teleport to Falador", ItemID.POH_TABLET_FALADORTELEPORT);
+ tenBananas = new ItemRequirement("Bananas", ItemID.BANANA, 10).canBeObtainedDuringQuest();
+
+ pirateMessage = new ItemRequirement("Pirate message", ItemID.PIRATEMESSAGE);
+ chestKey = new ItemRequirement("Chest key", ItemID.CHEST_KEY);
+ chestKey.setTooltip("You can get another one from Redbeard Frank");
+ inBlueMoonFirst = new ZoneRequirement(blueMoonFirst);
+ }
+
+ private void setupSteps()
+ {
speakToRedbeard = new NpcStep(this, NpcID.REDBEARD_FRANK, new WorldPoint(3053, 3251, 0),
"Talk to Redbeard Frank in Port Sarim.");
speakToRedbeard.addDialogSteps("I'm in search of treasure.", "Yes.");
- steps.put(0, speakToRedbeard);
- smuggleRum = new RumSmugglingStep(this);
+ readPirateMessage = new DetailedQuestStep(this, "Read the Pirate message.", pirateMessage.highlighted());
- steps.put(1, smuggleRum);
- readPirateMessage = new DetailedQuestStep(this, "Read the Pirate message.", pirateMessage.highlighted());
- climbStairs = new ObjectStep(this, ObjectID.FAI_VARROCK_STAIRS, new WorldPoint(3228, 3393, 0),
- "Climb up the stairs in The Blue Moon Inn in Varrock.");
- openChest = new ObjectStep(this, ObjectID.PIRATECHEST, new WorldPoint(3219, 3396, 1),
- "Open the chest by using the key on it.", chestKey.highlighted());
+ climbStairs = new ObjectStep(this, ObjectID.FAI_VARROCK_STAIRS, new WorldPoint(3228, 3393, 0), "Climb up the stairs in The Blue Moon Inn in Varrock.", chestKey);
+ climbStairs.addTeleport(teleportVarrock);
+ openChest = new ObjectStep(this, ObjectID.PIRATECHEST, new WorldPoint(3219, 3396, 1), "Open the chest by using the key on it.", chestKey.highlighted());
openChest.addDialogStep("Ok thanks, I'll go and get it.");
openChest.addIcon(ItemID.CHEST_KEY);
- blueMoonFirst = new Zone(new WorldPoint(3213, 3405, 1), new WorldPoint(3234, 3391, 1));
- inBlueMoonFirst = new ZoneRequirement(blueMoonFirst);
+ smuggleRum = new RumSmugglingStep(this);
+
+ digUpTreasure = new DigStep(this, new WorldPoint(2999, 3383, 0), "Dig in the middle of the cross in Falador Park, and kill the Gardener (level 4) who appears. Once killed, dig again.");
+ // TODO: Add a teleport to DigStep
+
+ killGardener = new NpcStep(this, NpcID.PIRATE_IRATE_GARDENER, new WorldPoint(2999, 3383, 0), "Kill the Gardener (level 4).");
+ digUpTreasure.addSubSteps(killGardener);
+ }
+
+ @Override
+ public Map loadSteps()
+ {
+ initializeRequirements();
+ setupSteps();
+
+ var steps = new HashMap();
+
+ steps.put(0, speakToRedbeard);
+
+ steps.put(1, smuggleRum);
- ConditionalStep getTreasureMap = new ConditionalStep(this, climbStairs);
- getTreasureMap.addStep(new Conditions(chestKey, inBlueMoonFirst), openChest);
+ var getTreasureMap = new ConditionalStep(this, climbStairs);
getTreasureMap.addStep(pirateMessage, readPirateMessage);
+ getTreasureMap.addStep(inBlueMoonFirst, openChest);
steps.put(2, getTreasureMap);
- digUpTreasure = new DigStep(this, new WorldPoint(2999, 3383, 0),
- "Dig in the middle of the cross in Falador Park, and kill the Gardener (level 4) who appears. Once killed, dig again.");
+ var cDigUpTreasure = new ConditionalStep(this, digUpTreasure);
+ cDigUpTreasure.addStep(new NpcHintArrowRequirement(NpcID.PIRATE_IRATE_GARDENER), killGardener);
+ steps.put(3, cDigUpTreasure);
- steps.put(3, digUpTreasure);
return steps;
}
- @Override
- protected void setupRequirements()
- {
- sixtyCoins = new ItemRequirement("Coins", ItemCollections.COINS, 60);
- spade = new ItemRequirement("Spade", ItemID.SPADE).isNotConsumed();
- pirateMessage = new ItemRequirement("Pirate message", ItemID.PIRATEMESSAGE);
- chestKey = new ItemRequirement("Chest key", ItemID.CHEST_KEY);
- chestKey.setTooltip("You can get another one from Redbeard Frank");
- }
-
@Override
public List getItemRequirements()
{
- ArrayList reqs = new ArrayList<>();
- reqs.add(sixtyCoins);
- reqs.add(spade);
-
- return reqs;
+ return List.of(
+ sixtyCoins,
+ spade,
+ tenBananas
+ );
}
@Override
public List getItemRecommended()
{
- ArrayList reqs = new ArrayList<>();
- reqs.add(new ItemRequirement("A teleport to Varrock", ItemID.POH_TABLET_VARROCKTELEPORT));
- reqs.add(new ItemRequirement("A teleport to Falador", ItemID.POH_TABLET_FALADORTELEPORT));
- reqs.add(new ItemRequirement("Bananas (obtainable in quest)", ItemID.BANANA, 10));
-
- return reqs;
+ return List.of(
+ teleportVarrock,
+ teleportFalador
+ );
}
@Override
public List getCombatRequirements()
{
- return Collections.singletonList("Gardener (level 4)");
+ return List.of(
+ "Gardener (level 4)"
+ );
}
@Override
@@ -147,22 +191,35 @@ public QuestPointReward getQuestPointReward()
@Override
public List getItemRewards()
{
- return Arrays.asList(
- new ItemReward("A Gold Ring", ItemID.GOLD_RING, 1),
- new ItemReward("An Emerald", ItemID.EMERALD, 1),
- new ItemReward("Coins", ItemID.COINS, 450));
+ return List.of(
+ new ItemReward("A Gold Ring", ItemID.GOLD_RING, 1),
+ new ItemReward("An Emerald", ItemID.EMERALD, 1),
+ new ItemReward("Coins", ItemID.COINS, 450)
+ );
}
@Override
public List getPanels()
{
- List allSteps = new ArrayList<>();
-
- allSteps.add(new PanelDetails("Talk to Redbeard Frank", Collections.singletonList(speakToRedbeard), sixtyCoins));
- allSteps.addAll(smuggleRum.panelDetails());
- allSteps.add(new PanelDetails("Discover the treasure", Arrays.asList(climbStairs, openChest, readPirateMessage,
- digUpTreasure), spade));
-
- return allSteps;
+ var sections = new ArrayList();
+
+ sections.add(new PanelDetails("Talk to Redbeard Frank", List.of(
+ speakToRedbeard
+ ), List.of(
+ sixtyCoins
+ )));
+
+ sections.addAll(smuggleRum.panelDetails());
+
+ sections.add(new PanelDetails("Discover the treasure", List.of(
+ climbStairs,
+ openChest,
+ readPirateMessage,
+ digUpTreasure
+ ), List.of(
+ spade
+ )));
+
+ return sections;
}
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/piratestreasure/RumSmugglingStep.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/piratestreasure/RumSmugglingStep.java
index faafa6e5a28..b4952501757 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/piratestreasure/RumSmugglingStep.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/helpers/quests/piratestreasure/RumSmugglingStep.java
@@ -26,35 +26,46 @@
import net.runelite.client.plugins.microbot.questhelper.collections.ItemCollections;
import net.runelite.client.plugins.microbot.questhelper.panel.PanelDetails;
-import net.runelite.client.plugins.microbot.questhelper.questhelpers.QuestHelper;
import net.runelite.client.plugins.microbot.questhelper.requirements.ChatMessageRequirement;
import net.runelite.client.plugins.microbot.questhelper.requirements.MesBoxRequirement;
import net.runelite.client.plugins.microbot.questhelper.requirements.Requirement;
import net.runelite.client.plugins.microbot.questhelper.requirements.conditional.Conditions;
import net.runelite.client.plugins.microbot.questhelper.requirements.item.ItemRequirement;
import net.runelite.client.plugins.microbot.questhelper.requirements.npc.DialogRequirement;
+import static net.runelite.client.plugins.microbot.questhelper.requirements.util.LogicHelper.and;
import net.runelite.client.plugins.microbot.questhelper.requirements.util.LogicType;
import net.runelite.client.plugins.microbot.questhelper.requirements.widget.WidgetTextRequirement;
import net.runelite.client.plugins.microbot.questhelper.requirements.zone.Zone;
import net.runelite.client.plugins.microbot.questhelper.requirements.zone.ZoneRequirement;
-import net.runelite.client.plugins.microbot.questhelper.steps.*;
+import net.runelite.client.plugins.microbot.questhelper.steps.ConditionalStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.DetailedQuestStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.NpcStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.ObjectStep;
+import net.runelite.client.plugins.microbot.questhelper.steps.QuestStep;
+import java.util.ArrayList;
+import java.util.List;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.gameval.InterfaceID;
import net.runelite.api.gameval.ItemID;
import net.runelite.api.gameval.NpcID;
import net.runelite.api.gameval.ObjectID;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
public class RumSmugglingStep extends ConditionalStep
{
- private Zone karamjaZone1, karamjaZone2, karamjaBoat;
+ private final PiratesTreasure pt;
+
+ // Zones
+ private Zone karamjaZone1;
+ private Zone karamjaZone2;
+ private Zone karamjaBoat;
- private ItemRequirement karamjanRum, tenBananas, whiteApron, whiteApronEquipped, whiteApronHanging;
+ // Miscellaneous requirements
+ private ZoneRequirement onKaramja;
- private Requirement onKaramja;
+ private ItemRequirement karamjanRum;
+ private ItemRequirement whiteApron;
+ private ItemRequirement whiteApronEquipped;
+ private ItemRequirement whiteApronHanging;
private Conditions atStart;
private Conditions employed;
private Conditions stashedRum;
@@ -66,57 +77,34 @@ public class RumSmugglingStep extends ConditionalStep
private Conditions filledCrateWithBananasAndRum;
private ChatMessageRequirement crateSent;
private ChatMessageRequirement fillCrateWithBananasChat;
-
- private QuestStep talkToCustomsOfficer, getRumFromCrate, getWhiteApron, addBananasToCrate, addRumToCrate, talkToZambo, talkToLuthas, talkToLuthasAgain, goToKaramja, bringRumToRedbeard;
-
- public RumSmugglingStep(QuestHelper questHelper)
+ private Requirement haveYouCompletedyourTaskYet;
+
+ // Steps
+ private QuestStep syncStep;
+ private QuestStep talkToCustomsOfficer;
+ private QuestStep getRumFromCrate;
+ private QuestStep getWhiteApron;
+ private QuestStep addBananasToCrate;
+ private QuestStep addRumToCrate;
+ private QuestStep talkToZambo;
+ private QuestStep talkToLuthas;
+ private QuestStep talkToLuthasAgain;
+ private QuestStep goToKaramja;
+ private QuestStep bringRumToRedbeard;
+ private MesBoxRequirement fillCrateBananas;
+
+ public RumSmugglingStep(PiratesTreasure questHelper)
{
super(questHelper, new DetailedQuestStep(questHelper, "Please open Pirate Treasure's Quest Journal to sync the current quest state."));
- setupItemRequirements();
- setupZones();
- setupConditions();
- setupSteps();
- addSteps();
- }
+ pt = questHelper;
- private void addSteps()
- {
- this.addStep(new Conditions(hasRumOffKaramja), bringRumToRedbeard);
- this.addStep(new Conditions(verifiedAState, haveShippedRum, onKaramja), talkToCustomsOfficer);
- this.addStep(new Conditions(verifiedAState, haveShippedRum, whiteApron), getRumFromCrate);
- this.addStep(new Conditions(verifiedAState, haveShippedRum), getWhiteApron);
- this.addStep(new Conditions(verifiedAState, filledCrateWithBananasAndRum, onKaramja), talkToLuthasAgain);
- this.addStep(new Conditions(verifiedAState, stashedRum, onKaramja), addBananasToCrate);
- this.addStep(new Conditions(verifiedAState, employed, karamjanRum, onKaramja), addRumToCrate);
- this.addStep(new Conditions(verifiedAState, employed, onKaramja), talkToZambo);
- this.addStep(new Conditions(verifiedAState, atStart, karamjanRum, onKaramja), talkToLuthas);
- this.addStep(new Conditions(verifiedAState, atStart, onKaramja), talkToZambo);
- this.addStep(verifiedAState, goToKaramja);
- }
+ syncStep = this.steps.get(null);
- @Override
- protected void updateSteps()
- {
- if ((hadRumOffKaramja.check(client) && !hasRumOffKaramja.check(client))
- || lostRum.check(client))
- {
- haveShippedRum.setHasPassed(false);
- stashedRum.setHasPassed(false);
- atStart.setHasPassed(true);
- hadRumOffKaramja.setHasPassed(false);
- lostRum.setHasPassed(false);
- }
-
- if (crateSent.check(client))
- {
- haveShippedRum.check(client);
- employed.setHasPassed(false);
- fillCrateWithBananasChat.setHasReceivedChatMessage(false);
- filledCrateWithBananasAndRum.setHasPassed(false);
- crateSent.setHasReceivedChatMessage(false);
- }
+ setupZones();
+ setupRequirements();
- super.updateSteps();
+ setupSteps();
+ addSteps();
}
private void setupZones()
@@ -126,18 +114,14 @@ private void setupZones()
karamjaBoat = new Zone(new WorldPoint(2964, 3138, 0), new WorldPoint(2951, 3144, 1));
}
- private void setupItemRequirements()
+ private void setupRequirements()
{
karamjanRum = new ItemRequirement("Karamjan rum", ItemID.KARAMJA_RUM);
- tenBananas = new ItemRequirement("Banana", ItemID.BANANA, 10);
whiteApron = new ItemRequirement("White apron", ItemID.WHITE_APRON);
- whiteApronEquipped = new ItemRequirement("White apron", ItemID.WHITE_APRON, 1, true);
+ whiteApronEquipped = whiteApron.equipped();
whiteApronHanging = new ItemRequirement("White apron", ItemID.PIRATETREASURE_APRON);
whiteApronHanging.addAlternates(ItemID.WHITE_APRON);
- }
- private void setupConditions()
- {
onKaramja = new ZoneRequirement(karamjaZone1, karamjaZone2, karamjaBoat);
Requirement offKaramja = new ZoneRequirement(false, karamjaZone1, karamjaZone2, karamjaBoat);
Requirement inPirateTreasureMenu = new WidgetTextRequirement(InterfaceID.Questjournal.TITLE, getQuestHelper().getQuest().getName());
@@ -162,16 +146,20 @@ private void setupConditions()
Requirement employedFromDialog = new Conditions(new DialogRequirement("If you could fill it up with bananas, I'll pay you 30 gold.", "Have you completed your task yet?", "you should see the old crate"));
employed = new Conditions(true, LogicType.OR, employedFromDialog, employedFromWidget, employedByWydinFromWidget);
+ // This can't be a dialog requirement because the check function doesn't do the actual checking
+ haveYouCompletedyourTaskYet = new WidgetTextRequirement(InterfaceID.ChatLeft.TEXT, "Have you completed your task yet?");
+
Requirement stashedRumFromWidget = new Conditions(inPirateTreasureMenu, new WidgetTextRequirement(InterfaceID.Questjournal.TEXTLAYER, true, "I have hidden my"));
Requirement stashedRumFromDialog = new MesBoxRequirement("You stash the rum in the crate.");
Requirement stashedRumFromChat = new Conditions(new ChatMessageRequirement("There is also some rum stashed in here too.", "There's already some rum in here...",
"There is some rum in here, although with no bananas to cover it. It is a little obvious."));
stashedRum = new Conditions(true, LogicType.OR, stashedRumFromDialog, stashedRumFromWidget, stashedRumFromChat, employedByWydinFromWidget);
- MesBoxRequirement fillCrateBananas = new MesBoxRequirement("You fill the crate with bananas.", "You pack all your bananas into the crate.");
- fillCrateBananas.setInvalidateRequirement(new ChatMessageRequirement("Have you completed your task yet?"));
+ var filledCrateWidget = and(inPirateTreasureMenu, new WidgetTextRequirement(InterfaceID.Questjournal.TEXTLAYER, true, "in the crate and filled it with"));
+
+ fillCrateBananas = new MesBoxRequirement("You fill the crate with bananas.", "You pack all your bananas into the crate.");
fillCrateWithBananasChat = new ChatMessageRequirement("The crate is full of bananas.", "The crate is already full.");
- Requirement filledCrateWithBananas = new Conditions(false, LogicType.OR, fillCrateWithBananasChat, fillCrateBananas);
+ Requirement filledCrateWithBananas = new Conditions(false, LogicType.OR, fillCrateWithBananasChat, fillCrateBananas, filledCrateWidget);
filledCrateWithBananasAndRum = new Conditions(true, LogicType.AND, filledCrateWithBananas, stashedRum);
Requirement shippedRumFromWidget = new Conditions(inPirateTreasureMenu, new WidgetTextRequirement(InterfaceID.Questjournal.TEXTLAYER, true, "the crate has been shipped"));
@@ -200,11 +188,11 @@ private void setupSteps()
talkToLuthas.addDialogStep("Will you pay me for another crate full?");
addRumToCrate = new ObjectStep(getQuestHelper(), ObjectID.BANANACRATE, new WorldPoint(2943, 3151, 0),
- "Put the Karamjan rum into the crate.", karamjanRum.highlighted(), tenBananas);
+ "Put the Karamjan rum into the crate.", karamjanRum.highlighted(), pt.tenBananas);
addRumToCrate.addIcon(ItemID.KARAMJA_RUM);
addBananasToCrate = new ObjectStep(getQuestHelper(), ObjectID.BANANACRATE, new WorldPoint(2943, 3151, 0),
- "Right-click fill the rest of the crate with bananas, then talk to Luthas.", tenBananas);
+ "Right-click fill the rest of the crate with bananas, then talk to Luthas.", pt.tenBananas);
talkToLuthasAgain = new NpcStep(getQuestHelper(), NpcID.LUTHAS, new WorldPoint(2938, 3154, 0),
"Talk to Luthas and tell him you finished filling the crate.");
@@ -228,12 +216,78 @@ private void setupSteps()
karamjanRum);
}
- public List