From d53ca5c2b87fa9f1ee18ea43db2177274deaa510 Mon Sep 17 00:00:00 2001 From: illumineawake Date: Sat, 14 Nov 2020 02:03:01 +1100 Subject: [PATCH] iherbcleaner: cleans herbs and deposits/withdraws from the bank. --- iherbcleaner/iherbcleaner.gradle.kts | 54 ++++ .../client/plugins/iherbcleaner/Task.java | 74 +++++ .../client/plugins/iherbcleaner/TaskSet.java | 42 +++ .../iherbcleaner/iHerbCleanerConfig.java | 282 ++++++++++++++++++ .../iherbcleaner/iHerbCleanerOverlay.java | 93 ++++++ .../iherbcleaner/iHerbCleanerPlugin.java | 238 +++++++++++++++ .../iherbcleaner/tasks/BankItemsTask.java | 69 +++++ .../iherbcleaner/tasks/CleanHerbTask.java | 60 ++++ .../iherbcleaner/tasks/MovingTask.java | 37 +++ .../iherbcleaner/tasks/OpenBankTask.java | 65 ++++ .../iherbcleaner/tasks/TimeoutTask.java | 23 ++ .../client/plugins/itasktemplate/Task.java | 10 +- .../itasktemplate/tasks/MovingTask.java | 6 +- .../itasktemplate/tasks/TimeoutTask.java | 8 +- plugins.json | 2 +- release/iherbcleaner-1.0.0.jar | Bin 0 -> 18017 bytes release/itasktemplate-1.0.1.jar | Bin 12760 -> 12758 bytes settings.gradle.kts | 1 + 18 files changed, 1049 insertions(+), 15 deletions(-) create mode 100644 iherbcleaner/iherbcleaner.gradle.kts create mode 100644 iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/Task.java create mode 100644 iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/TaskSet.java create mode 100644 iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/iHerbCleanerConfig.java create mode 100644 iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/iHerbCleanerOverlay.java create mode 100644 iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/iHerbCleanerPlugin.java create mode 100644 iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/tasks/BankItemsTask.java create mode 100644 iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/tasks/CleanHerbTask.java create mode 100644 iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/tasks/MovingTask.java create mode 100644 iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/tasks/OpenBankTask.java create mode 100644 iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/tasks/TimeoutTask.java create mode 100644 release/iherbcleaner-1.0.0.jar diff --git a/iherbcleaner/iherbcleaner.gradle.kts b/iherbcleaner/iherbcleaner.gradle.kts new file mode 100644 index 00000000..687ff789 --- /dev/null +++ b/iherbcleaner/iherbcleaner.gradle.kts @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019 Owain van Brakel + * 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. + */ + +version = "1.0.0" + +project.extra["PluginName"] = "iHerbCleaner" +project.extra["PluginDescription"] = "Illumine - Herb Cleaner" + +dependencies { + //compileOnly(group = "com.openosrs.externals", name = "iutils", version = "1.0.0+") uncomment this is you want to use this in a project that doesn't also hold iUtils + compileOnly(project(":iutils")) + compileOnly(group = "com.owain.externals", name = "chinbreakhandler", version = "0.0.13+") +} + +tasks { + jar { + manifest { + attributes(mapOf( + "Plugin-Version" to project.version, + "Plugin-Id" to nameToId(project.extra["PluginName"] as String), + "Plugin-Provider" to project.extra["PluginProvider"], + "Plugin-Dependencies" to + arrayOf( + nameToId("iUtils"), + "chinbreakhandler-plugin" + ).joinToString(), + "Plugin-Description" to project.extra["PluginDescription"], + "Plugin-License" to project.extra["PluginLicense"] + )) + } + } +} \ No newline at end of file diff --git a/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/Task.java b/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/Task.java new file mode 100644 index 00000000..316b98dc --- /dev/null +++ b/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/Task.java @@ -0,0 +1,74 @@ +package net.runelite.client.plugins.iherbcleaner; + +import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.MenuEntry; +import net.runelite.api.events.GameTick; +import static net.runelite.client.plugins.iherbcleaner.iHerbCleanerPlugin.sleepLength; +import static net.runelite.client.plugins.iherbcleaner.iHerbCleanerPlugin.tickLength; +import net.runelite.client.plugins.iutils.CalculationUtils; +import net.runelite.client.plugins.iutils.MenuUtils; +import net.runelite.client.plugins.iutils.MouseUtils; +import net.runelite.client.plugins.iutils.ObjectUtils; +import net.runelite.client.plugins.iutils.PlayerUtils; +import net.runelite.client.plugins.iutils.iUtils; + +@Slf4j +public abstract class Task +{ + public Task() + { + } + + @Inject + public Client client; + + @Inject + public iHerbCleanerConfig config; + + @Inject + public iUtils utils; + + @Inject + public MenuUtils menu; + + @Inject + public MouseUtils mouse; + + @Inject + public CalculationUtils calc; + + @Inject + public PlayerUtils playerUtils; + + @Inject + public ObjectUtils object; + + public MenuEntry entry; + + public abstract boolean validate(); + + public long sleepDelay() + { + sleepLength = calc.randomDelay(config.sleepWeightedDistribution(), config.sleepMin(), config.sleepMax(), config.sleepDeviation(), config.sleepTarget()); + return sleepLength; + } + + public int tickDelay() + { + tickLength = (int) calc.randomDelay(config.tickDelayWeightedDistribution(), config.tickDelayMin(), config.tickDelayMax(), config.tickDelayDeviation(), config.tickDelayTarget()); + return tickLength; + } + + public String getTaskDescription() + { + return this.getClass().getSimpleName(); + } + + public void onGameTick(GameTick event) + { + return; + } + +} diff --git a/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/TaskSet.java b/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/TaskSet.java new file mode 100644 index 00000000..e2ab0572 --- /dev/null +++ b/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/TaskSet.java @@ -0,0 +1,42 @@ +package net.runelite.client.plugins.iherbcleaner; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class TaskSet +{ + public List taskList = new ArrayList<>(); + + public TaskSet(Task... tasks) + { + taskList.addAll(Arrays.asList(tasks)); + } + + public void addAll(Task... tasks) + { + taskList.addAll(Arrays.asList(tasks)); + } + + public void clear() + { + taskList.clear(); + } + + /** + * Iterates through all the tasks in the set and returns + * the highest priority valid task. + * @return The first valid task from the task list or null if no valid task. + */ + public Task getValidTask() + { + for (Task task : this.taskList) + { + if (task.validate()) + { + return task; + } + } + return null; + } +} diff --git a/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/iHerbCleanerConfig.java b/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/iHerbCleanerConfig.java new file mode 100644 index 00000000..e9f3ac11 --- /dev/null +++ b/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/iHerbCleanerConfig.java @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2018, SomeoneWithAnInternetConnection + * Copyright (c) 2018, oplosthee + * 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.iherbcleaner; + +import net.runelite.client.config.Button; +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.ConfigSection; +import net.runelite.client.config.ConfigTitleSection; +import net.runelite.client.config.Range; +import net.runelite.client.config.Title; + +@ConfigGroup("iHerbCleaner") +public interface iHerbCleanerConfig extends Config +{ + + @ConfigSection( + keyName = "delayConfig", + name = "Sleep Delay Configuration", + description = "Configure how the bot handles sleep delays", + position = 0 + ) + default boolean delayConfig() + { + return false; + } + + @Range( + min = 0, + max = 550 + ) + @ConfigItem( + keyName = "sleepMin", + name = "Sleep Min", + description = "", + position = 1, + section = "delayConfig" + ) + default int sleepMin() + { + return 60; + } + + @Range( + min = 0, + max = 550 + ) + @ConfigItem( + keyName = "sleepMax", + name = "Sleep Max", + description = "", + position = 2, + section = "delayConfig" + ) + default int sleepMax() + { + return 350; + } + + @Range( + min = 0, + max = 550 + ) + @ConfigItem( + keyName = "sleepTarget", + name = "Sleep Target", + description = "", + position = 3, + section = "delayConfig" + ) + default int sleepTarget() + { + return 100; + } + + @Range( + min = 0, + max = 550 + ) + @ConfigItem( + keyName = "sleepDeviation", + name = "Sleep Deviation", + description = "", + position = 4, + section = "delayConfig" + ) + default int sleepDeviation() + { + return 10; + } + + @ConfigItem( + keyName = "sleepWeightedDistribution", + name = "Sleep Weighted Distribution", + description = "Shifts the random distribution towards the lower end at the target, otherwise it will be an even distribution", + position = 5, + section = "delayConfig" + ) + default boolean sleepWeightedDistribution() + { + return false; + } + + @ConfigSection( + keyName = "delayTickConfig", + name = "Game Tick Configuration", + description = "Configure how the bot handles game tick delays, 1 game tick equates to roughly 600ms", + position = 10 + ) + default boolean delayTickConfig() + { + return false; + } + + @Range( + min = 0, + max = 10 + ) + @ConfigItem( + keyName = "tickDelayMin", + name = "Game Tick Min", + description = "", + position = 11, + section = "delayTickConfig" + ) + default int tickDelayMin() + { + return 1; + } + + @Range( + min = 0, + max = 10 + ) + @ConfigItem( + keyName = "tickDelayMax", + name = "Game Tick Max", + description = "", + position = 12, + section = "delayTickConfig" + ) + default int tickDelayMax() + { + return 3; + } + + @Range( + min = 0, + max = 10 + ) + @ConfigItem( + keyName = "tickDelayTarget", + name = "Game Tick Target", + description = "", + position = 13, + section = "delayTickConfig" + ) + default int tickDelayTarget() + { + return 2; + } + + @Range( + min = 0, + max = 10 + ) + @ConfigItem( + keyName = "tickDelayDeviation", + name = "Game Tick Deviation", + description = "", + position = 14, + section = "delayTickConfig" + ) + default int tickDelayDeviation() + { + return 1; + } + + @ConfigItem( + keyName = "tickDelayWeightedDistribution", + name = "Game Tick Weighted Distribution", + description = "Shifts the random distribution towards the lower end at the target, otherwise it will be an even distribution", + position = 15, + section = "delayTickConfig" + ) + default boolean tickDelayWeightedDistribution() + { + return false; + } + + @ConfigTitleSection( + keyName = "instructionsTitle", + name = "Instructions", + description = "", + position = 16 + ) + default Title instructionsTitle() + { + return new Title(); + } + + @ConfigItem( + keyName = "instructions", + name = "", + description = "Instructions. Don't enter anything into this field", + position = 17, + titleSection = "instructionsTitle" + ) + default String instructions() + { + return "Have herbs in bank, setup IDs in config and press Start. Sleep Delays determine length of time between cleaning."; + } + + @ConfigItem( + keyName = "herbID", + name = "Herb ID", + description = "Item ID of the Herb you want to clean", + position = 20 + ) + default int herbID() + { + return 199; + } + + @ConfigItem( + keyName = "bankID", + name = "Bank Booth ID", + description = "Game Object ID of the Bank Booth you want to use", + position = 25 + ) + default int bankID() + { + return 10583; + } + + + @ConfigItem( + keyName = "enableUI", + name = "Enable UI", + description = "Enable to turn on in game UI", + position = 95 + ) + default boolean enableUI() + { + return true; + } + + @ConfigItem( + keyName = "startButton", + name = "Start/Stop", + description = "Test button that changes variable value", + position = 100 + ) + default Button startButton() + { + return new Button(); + } +} diff --git a/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/iHerbCleanerOverlay.java b/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/iHerbCleanerOverlay.java new file mode 100644 index 00000000..24b31879 --- /dev/null +++ b/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/iHerbCleanerOverlay.java @@ -0,0 +1,93 @@ +package net.runelite.client.plugins.iherbcleaner; + +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.time.Duration; +import java.time.Instant; +import javax.inject.Inject; +import javax.inject.Singleton; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import static net.runelite.api.MenuOpcode.RUNELITE_OVERLAY_CONFIG; +import static net.runelite.client.ui.overlay.OverlayManager.OPTION_CONFIGURE; +import net.runelite.client.ui.overlay.OverlayMenuEntry; +import net.runelite.client.ui.overlay.OverlayPanel; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.components.TitleComponent; +import net.runelite.client.ui.overlay.components.table.TableAlignment; +import net.runelite.client.ui.overlay.components.table.TableComponent; +import net.runelite.client.util.ColorUtil; +import static org.apache.commons.lang3.time.DurationFormatUtils.formatDuration; + +@Slf4j +@Singleton +class iHerbCleanerOverlay extends OverlayPanel +{ + private final Client client; + private final iHerbCleanerPlugin plugin; + private final iHerbCleanerConfig config; + + String timeFormat; + private String infoStatus = "Starting..."; + + @Inject + private iHerbCleanerOverlay(final Client client, final iHerbCleanerPlugin plugin, final iHerbCleanerConfig config) + { + super(plugin); + setPosition(OverlayPosition.BOTTOM_LEFT); + this.client = client; + this.plugin = plugin; + this.config = config; + getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY_CONFIG, OPTION_CONFIGURE, "Herb Cleaner overlay")); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (plugin.botTimer == null || !config.enableUI()) + { + return null; + } + TableComponent tableComponent = new TableComponent(); + tableComponent.setColumnAlignments(TableAlignment.LEFT, TableAlignment.RIGHT); + + Duration duration = Duration.between(plugin.botTimer, Instant.now()); + timeFormat = (duration.toHours() < 1) ? "mm:ss" : "HH:mm:ss"; + tableComponent.addRow("Time running:", formatDuration(duration.toMillis(),timeFormat)); + + tableComponent.addRow("Status:", plugin.status); + + TableComponent tableStatsComponent = new TableComponent(); + tableStatsComponent.setColumnAlignments(TableAlignment.LEFT, TableAlignment.RIGHT); + + TableComponent tableDelayComponent = new TableComponent(); + tableDelayComponent.setColumnAlignments(TableAlignment.LEFT, TableAlignment.RIGHT); + + tableDelayComponent.addRow("Sleep delay:", iHerbCleanerPlugin.sleepLength + "ms"); + tableDelayComponent.addRow("Tick delay:", String.valueOf(plugin.timeout)); + + if (!tableComponent.isEmpty()) + { + panelComponent.setBackgroundColor(ColorUtil.fromHex("#121212")); //Material Dark default + panelComponent.setPreferredSize(new Dimension(270,200)); + panelComponent.setBorder(new Rectangle(5,5,5,5)); + panelComponent.getChildren().add(TitleComponent.builder() + .text("Illumine Herb Cleaner") + .color(ColorUtil.fromHex("#40C4FF")) + .build()); + panelComponent.getChildren().add(tableComponent); + panelComponent.getChildren().add(TitleComponent.builder() + .text("Stats") + .color(ColorUtil.fromHex("#FFA000")) + .build()); + panelComponent.getChildren().add(tableStatsComponent); + panelComponent.getChildren().add(TitleComponent.builder() + .text("Delays") + .color(ColorUtil.fromHex("#F8BBD0")) + .build()); + panelComponent.getChildren().add(tableDelayComponent); + } + return super.render(graphics); + } +} diff --git a/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/iHerbCleanerPlugin.java b/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/iHerbCleanerPlugin.java new file mode 100644 index 00000000..087e1585 --- /dev/null +++ b/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/iHerbCleanerPlugin.java @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2018, SomeoneWithAnInternetConnection + * Copyright (c) 2018, oplosthee + * 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.iherbcleaner; + +import com.google.inject.Injector; +import com.google.inject.Provides; +import com.owain.chinbreakhandler.ChinBreakHandler; +import java.time.Duration; +import java.time.Instant; +import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.MenuEntry; +import net.runelite.api.Player; +import net.runelite.api.coords.LocalPoint; +import net.runelite.api.events.ConfigButtonClicked; +import net.runelite.api.events.GameTick; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDependency; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.PluginType; +import net.runelite.client.plugins.iherbcleaner.tasks.BankItemsTask; +import net.runelite.client.plugins.iherbcleaner.tasks.CleanHerbTask; +import net.runelite.client.plugins.iherbcleaner.tasks.MovingTask; +import net.runelite.client.plugins.iherbcleaner.tasks.OpenBankTask; +import net.runelite.client.plugins.iherbcleaner.tasks.TimeoutTask; +import net.runelite.client.plugins.iutils.iUtils; +import net.runelite.client.ui.overlay.OverlayManager; +import org.pf4j.Extension; + + +@Extension +@PluginDependency(iUtils.class) +@PluginDescriptor( + name = "iHerbCleaner", + enabledByDefault = false, + description = "Illumine - Herb Cleaner plugin", + tags = {"illumine", "task", "herblore", "clean", "bot"}, + type = PluginType.MISCELLANEOUS +) +@Slf4j +public class iHerbCleanerPlugin extends Plugin +{ + @Inject + private Injector injector; + + @Inject + private Client client; + + @Inject + private iHerbCleanerConfig config; + + @Inject + private OverlayManager overlayManager; + + @Inject + private iHerbCleanerOverlay overlay; + + @Inject + private iUtils utils; + + @Inject + public ChinBreakHandler chinBreakHandler; + + @Inject + private ConfigManager configManager; + + private TaskSet tasks = new TaskSet(); + public static LocalPoint beforeLoc = new LocalPoint(0, 0); + + MenuEntry targetMenu; + Instant botTimer; + Player player; + + public static boolean startBot; + public static long sleepLength; + public static int tickLength; + public static int timeout; + public static String status = "starting..."; + + @Provides + iHerbCleanerConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(iHerbCleanerConfig.class); + } + + @Override + protected void startUp() + { + chinBreakHandler.registerPlugin(this); + } + + @Override + protected void shutDown() + { + resetVals(); + chinBreakHandler.unregisterPlugin(this); + } + + + private void loadTasks() + { + tasks.clear(); + tasks.addAll( + injector.getInstance(TimeoutTask.class), + injector.getInstance(MovingTask.class), + injector.getInstance(CleanHerbTask.class), + injector.getInstance(BankItemsTask.class), + injector.getInstance(OpenBankTask.class) + ); + } + + public void resetVals() + { + log.debug("stopping iHerb Cleaner plugin"); + overlayManager.remove(overlay); + chinBreakHandler.stopPlugin(this); + startBot = false; + botTimer = null; + tasks.clear(); + } + + @Subscribe + private void onConfigButtonPressed(ConfigButtonClicked configButtonClicked) + { + if (!configButtonClicked.getGroup().equalsIgnoreCase("iHerbCleaner")) + { + return; + } + log.debug("button {} pressed!", configButtonClicked.getKey()); + if (configButtonClicked.getKey().equals("startButton")) + { + if (!startBot) + { + Player player = client.getLocalPlayer(); + if (client != null && player != null && client.getGameState() == GameState.LOGGED_IN) + { + log.info("starting Herb Cleaner plugin"); + loadTasks(); + startBot = true; + chinBreakHandler.startPlugin(this); + timeout = 0; + targetMenu = null; + botTimer = Instant.now(); + overlayManager.add(overlay); + beforeLoc = client.getLocalPlayer().getLocalLocation(); + } + else + { + log.info("Start logged in"); + } + } + else + { + resetVals(); + } + } + } + + public void updateStats() + { + //templatePH = (int) getPerHour(totalBraceletCount); + //coinsPH = (int) getPerHour(totalCoins - ((totalCoins / BRACELET_HA_VAL) * (unchargedBraceletCost + revEtherCost + natureRuneCost))); + } + + public long getPerHour(int quantity) + { + Duration timeSinceStart = Duration.between(botTimer, Instant.now()); + if (!timeSinceStart.isZero()) + { + return (int) ((double) quantity * (double) Duration.ofHours(1).toMillis() / (double) timeSinceStart.toMillis()); + } + return 0; + } + + @Subscribe + private void onGameTick(GameTick event) + { + if (!startBot || chinBreakHandler.isBreakActive(this)) + { + return; + } + player = client.getLocalPlayer(); + if (client != null && player != null && client.getGameState() == GameState.LOGGED_IN) + { + if (chinBreakHandler.shouldBreak(this)) + { + status = "Taking a break"; + chinBreakHandler.startBreak(this); + timeout = 5; + } + if (timeout > 0) + { + timeout--; + return; + } + Task task = tasks.getValidTask(); + + if (task != null) + { + status = task.getTaskDescription(); + task.onGameTick(event); + } + else + { + status = "Task not found"; + log.debug(status); + } + beforeLoc = player.getLocalLocation(); + } + } +} \ No newline at end of file diff --git a/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/tasks/BankItemsTask.java b/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/tasks/BankItemsTask.java new file mode 100644 index 00000000..a9bcb8f0 --- /dev/null +++ b/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/tasks/BankItemsTask.java @@ -0,0 +1,69 @@ +package net.runelite.client.plugins.iherbcleaner.tasks; + +import java.awt.Rectangle; +import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.GameObject; +import net.runelite.api.MenuEntry; +import net.runelite.api.MenuOpcode; +import net.runelite.api.events.GameTick; +import net.runelite.client.plugins.iherbcleaner.Task; +import net.runelite.client.plugins.iherbcleaner.iHerbCleanerPlugin; +import static net.runelite.client.plugins.iherbcleaner.iHerbCleanerPlugin.startBot; +import static net.runelite.client.plugins.iherbcleaner.iHerbCleanerPlugin.status; +import net.runelite.client.plugins.iutils.ActionQueue; +import net.runelite.client.plugins.iutils.BankUtils; +import net.runelite.client.plugins.iutils.InventoryUtils; + +@Slf4j +public class BankItemsTask extends Task +{ + + @Inject + ActionQueue action; + + @Inject + InventoryUtils inventory; + + @Inject + BankUtils bank; + + @Override + public boolean validate() + { + return action.delayedActions.isEmpty() && !inventory.containsItem(config.herbID()) && + bank.isOpen(); + } + + @Override + public String getTaskDescription() + { + return iHerbCleanerPlugin.status; + } + + @Override + public void onGameTick(GameTick event) + { + int sleep = 0; + if (!inventory.isEmpty()) + { + status = "Depositing items"; + bank.depositAll(); + } + else + { + status = "Withdrawing items"; + if (bank.contains(config.herbID(), 1)) + { + bank.withdrawAllItem(config.herbID()); + } + else + { + status = "Out of herbs to clean, stopping"; + utils.sendGameMessage(status); + startBot = false; + } + } + log.info(status); + } +} \ No newline at end of file diff --git a/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/tasks/CleanHerbTask.java b/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/tasks/CleanHerbTask.java new file mode 100644 index 00000000..07b4e91d --- /dev/null +++ b/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/tasks/CleanHerbTask.java @@ -0,0 +1,60 @@ +package net.runelite.client.plugins.iherbcleaner.tasks; + +import java.awt.Rectangle; +import java.util.List; +import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.MenuEntry; +import net.runelite.api.MenuOpcode; +import net.runelite.api.Player; +import net.runelite.api.events.GameTick; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.api.widgets.WidgetItem; +import net.runelite.client.plugins.iherbcleaner.Task; +import net.runelite.client.plugins.iherbcleaner.iHerbCleanerPlugin; +import static net.runelite.client.plugins.iherbcleaner.iHerbCleanerPlugin.timeout; +import net.runelite.client.plugins.iutils.ActionQueue; +import net.runelite.client.plugins.iutils.InventoryUtils; +import static net.runelite.client.plugins.iherbcleaner.iHerbCleanerPlugin.status; + +@Slf4j +public class CleanHerbTask extends Task +{ + + @Inject + ActionQueue action; + + @Inject + InventoryUtils inventory; + + @Override + public boolean validate() + { + return action.delayedActions.isEmpty() && inventory.containsItem(config.herbID()); + } + + @Override + public String getTaskDescription() + { + return iHerbCleanerPlugin.status; + } + + @Override + public void onGameTick(GameTick event) + { + status = "Starting herb cleaning"; + List herbs = inventory.getItems(List.of(config.herbID())); + int sleep = 0; + for (WidgetItem herb : herbs) + { + log.info("Adding herb: {}, delay time: {}", herb.getIndex(), sleep); + entry = new MenuEntry("", "", herb.getId(), MenuOpcode.ITEM_FIRST_OPTION.getId(), + herb.getIndex(), WidgetInfo.INVENTORY.getId(), true); + sleep += (int) sleepDelay(); + herb.getCanvasBounds().getBounds(); + Rectangle rectangle = herb.getCanvasBounds().getBounds(); + utils.doActionMsTime(entry, rectangle, sleep); + } + log.info(status); + } +} \ No newline at end of file diff --git a/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/tasks/MovingTask.java b/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/tasks/MovingTask.java new file mode 100644 index 00000000..bc09b3a6 --- /dev/null +++ b/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/tasks/MovingTask.java @@ -0,0 +1,37 @@ +package net.runelite.client.plugins.iherbcleaner.tasks; + +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Player; +import net.runelite.api.events.GameTick; +import net.runelite.client.plugins.iherbcleaner.Task; +import net.runelite.client.plugins.iherbcleaner.iHerbCleanerPlugin; +import static net.runelite.client.plugins.iherbcleaner.iHerbCleanerPlugin.beforeLoc; +import static net.runelite.client.plugins.iherbcleaner.iHerbCleanerPlugin.timeout; + +@Slf4j +public class MovingTask extends Task +{ + + @Override + public boolean validate() + { + return playerUtils.isMoving(beforeLoc); + } + + @Override + public String getTaskDescription() + { + return iHerbCleanerPlugin.status; + } + + @Override + public void onGameTick(GameTick event) + { + Player player = client.getLocalPlayer(); + if (player != null) + { + playerUtils.handleRun(20, 30); + timeout = tickDelay(); + } + } +} \ No newline at end of file diff --git a/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/tasks/OpenBankTask.java b/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/tasks/OpenBankTask.java new file mode 100644 index 00000000..c0451ba1 --- /dev/null +++ b/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/tasks/OpenBankTask.java @@ -0,0 +1,65 @@ +package net.runelite.client.plugins.iherbcleaner.tasks; + +import java.awt.Rectangle; +import java.util.List; +import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.GameObject; +import net.runelite.api.MenuEntry; +import net.runelite.api.MenuOpcode; +import net.runelite.api.events.GameTick; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.api.widgets.WidgetItem; +import net.runelite.client.plugins.iherbcleaner.Task; +import net.runelite.client.plugins.iherbcleaner.iHerbCleanerPlugin; +import static net.runelite.client.plugins.iherbcleaner.iHerbCleanerPlugin.status; +import net.runelite.client.plugins.iutils.ActionQueue; +import net.runelite.client.plugins.iutils.BankUtils; +import net.runelite.client.plugins.iutils.InventoryUtils; + +@Slf4j +public class OpenBankTask extends Task +{ + + @Inject + ActionQueue action; + + @Inject + InventoryUtils inventory; + + @Inject + BankUtils bank; + + @Override + public boolean validate() + { + return action.delayedActions.isEmpty() && !inventory.containsItem(config.herbID()) && + !bank.isOpen(); + } + + @Override + public String getTaskDescription() + { + return iHerbCleanerPlugin.status; + } + + @Override + public void onGameTick(GameTick event) + { + GameObject bank = object.findNearestGameObject(config.bankID()); + if (bank != null) + { + status = "Opening bank"; + entry = new MenuEntry("", "", bank.getId(), MenuOpcode.GAME_OBJECT_SECOND_OPTION.getId(), + bank.getSceneMinLocation().getX(), bank.getSceneMinLocation().getY(), false); + Rectangle rectangle = (bank.getConvexHull() != null) ? bank.getConvexHull().getBounds() : + new Rectangle(client.getCenterX() - 50, client.getCenterY() - 50, 100, 100);; + utils.doActionMsTime(entry, rectangle, (int) sleepDelay()); + } + else + { + status = "Bank not found"; + } + log.info(status); + } +} \ No newline at end of file diff --git a/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/tasks/TimeoutTask.java b/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/tasks/TimeoutTask.java new file mode 100644 index 00000000..71890764 --- /dev/null +++ b/iherbcleaner/src/main/java/net/runelite/client/plugins/iherbcleaner/tasks/TimeoutTask.java @@ -0,0 +1,23 @@ +package net.runelite.client.plugins.iherbcleaner.tasks; + +import net.runelite.api.events.GameTick; +import net.runelite.client.plugins.iherbcleaner.Task; +import static net.runelite.client.plugins.iherbcleaner.iHerbCleanerPlugin.timeout; + +public class TimeoutTask extends Task +{ + @Override + public boolean validate() { return timeout > 0; } + + @Override + public String getTaskDescription() + { + return "Timeout: " + timeout; + } + + @Override + public void onGameTick(GameTick event) + { + timeout--; + } +} \ No newline at end of file diff --git a/itasktemplate/src/main/java/net/runelite/client/plugins/itasktemplate/Task.java b/itasktemplate/src/main/java/net/runelite/client/plugins/itasktemplate/Task.java index e89ca242..4dfe40ce 100644 --- a/itasktemplate/src/main/java/net/runelite/client/plugins/itasktemplate/Task.java +++ b/itasktemplate/src/main/java/net/runelite/client/plugins/itasktemplate/Task.java @@ -5,8 +5,6 @@ import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; import net.runelite.api.MenuEntry; import net.runelite.api.events.GameTick; -import static net.runelite.client.plugins.itasktemplate.iTaskTemplatePlugin.sleepLength; -import static net.runelite.client.plugins.itasktemplate.iTaskTemplatePlugin.tickLength; import net.runelite.client.plugins.iutils.CalculationUtils; import net.runelite.client.plugins.iutils.MenuUtils; import net.runelite.client.plugins.iutils.MouseUtils; @@ -51,14 +49,14 @@ public abstract class Task public long sleepDelay() { - sleepLength = calc.randomDelay(config.sleepWeightedDistribution(), config.sleepMin(), config.sleepMax(), config.sleepDeviation(), config.sleepTarget()); - return sleepLength; + iTaskTemplatePlugin.sleepLength = calc.randomDelay(config.sleepWeightedDistribution(), config.sleepMin(), config.sleepMax(), config.sleepDeviation(), config.sleepTarget()); + return iTaskTemplatePlugin.sleepLength; } public int tickDelay() { - tickLength = (int) calc.randomDelay(config.tickDelayWeightedDistribution(), config.tickDelayMin(), config.tickDelayMax(), config.tickDelayDeviation(), config.tickDelayTarget()); - return tickLength; + iTaskTemplatePlugin.tickLength = (int) calc.randomDelay(config.tickDelayWeightedDistribution(), config.tickDelayMin(), config.tickDelayMax(), config.tickDelayDeviation(), config.tickDelayTarget()); + return iTaskTemplatePlugin.tickLength; } public String getTaskDescription() diff --git a/itasktemplate/src/main/java/net/runelite/client/plugins/itasktemplate/tasks/MovingTask.java b/itasktemplate/src/main/java/net/runelite/client/plugins/itasktemplate/tasks/MovingTask.java index 974cfc5f..acf642b6 100644 --- a/itasktemplate/src/main/java/net/runelite/client/plugins/itasktemplate/tasks/MovingTask.java +++ b/itasktemplate/src/main/java/net/runelite/client/plugins/itasktemplate/tasks/MovingTask.java @@ -5,8 +5,6 @@ import net.runelite.api.Player; import net.runelite.api.events.GameTick; import net.runelite.client.plugins.itasktemplate.Task; import net.runelite.client.plugins.itasktemplate.iTaskTemplatePlugin; -import static net.runelite.client.plugins.itasktemplate.iTaskTemplatePlugin.beforeLoc; -import static net.runelite.client.plugins.itasktemplate.iTaskTemplatePlugin.timeout; @Slf4j public class MovingTask extends Task @@ -15,7 +13,7 @@ public class MovingTask extends Task @Override public boolean validate() { - return playerUtils.isMoving(beforeLoc); + return playerUtils.isMoving(iTaskTemplatePlugin.beforeLoc); } @Override @@ -31,7 +29,7 @@ public class MovingTask extends Task if (player != null) { playerUtils.handleRun(20, 30); - timeout = tickDelay(); + iTaskTemplatePlugin.timeout = tickDelay(); } } } \ No newline at end of file diff --git a/itasktemplate/src/main/java/net/runelite/client/plugins/itasktemplate/tasks/TimeoutTask.java b/itasktemplate/src/main/java/net/runelite/client/plugins/itasktemplate/tasks/TimeoutTask.java index 74484bb8..b8bf61df 100644 --- a/itasktemplate/src/main/java/net/runelite/client/plugins/itasktemplate/tasks/TimeoutTask.java +++ b/itasktemplate/src/main/java/net/runelite/client/plugins/itasktemplate/tasks/TimeoutTask.java @@ -2,22 +2,22 @@ package net.runelite.client.plugins.itasktemplate.tasks; import net.runelite.api.events.GameTick; import net.runelite.client.plugins.itasktemplate.Task; -import static net.runelite.client.plugins.itasktemplate.iTaskTemplatePlugin.timeout; +import net.runelite.client.plugins.itasktemplate.iTaskTemplatePlugin; public class TimeoutTask extends Task { @Override - public boolean validate() { return timeout > 0; } + public boolean validate() { return iTaskTemplatePlugin.timeout > 0; } @Override public String getTaskDescription() { - return "Timeout: " + timeout; + return "Timeout: " + iTaskTemplatePlugin.timeout; } @Override public void onGameTick(GameTick event) { - timeout--; + iTaskTemplatePlugin.timeout--; } } \ No newline at end of file diff --git a/plugins.json b/plugins.json index f5e22a92..4535eb31 100644 --- a/plugins.json +++ b/plugins.json @@ -1 +1 @@ -[{"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"BotUtils","description":"Illumine - Utils required for plugins to function with added automation","id":"botutils-plugin","releases":[{"date":"2020-11-13","sha512sum":"E6E86ECC9B7E7F45A97E7BF97A57F733247D5EDF280E14C83B685EEB517F64C421E069210E76E3672547B18F80EE148D973240627EB41D8FBA11C2A8EB359355","version":"4.9.1","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/botutils-4.9.1.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iBlackjack","description":"Illumine - Blackjack plugin","id":"iblackjack-plugin","releases":[{"date":"2020-11-13","sha512sum":"4720D7B7B28CAE15386B1B1640C4C2EA46F54FE22128698CC23DDE69CBABD954A37A09451D9B9E8E740FF62C65F89C1EE3E834156CA500D3F4CF7C227E0CE83D","version":"1.2.0","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/iblackjack-1.2.0.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iCombination Runecrafter Plugin","description":"Illumine - Combination Runecrafting plugin","id":"icombinationrunecrafterplugin-plugin","releases":[{"date":"2020-11-13","sha512sum":"5CF8190D3AB08D63ED8E5BA87D183A580270C31353F07BC80FF4576F3F742F66CFC2E97066370ADA2D7B5B61C67F00CB7C123C0B362E6581F7B4F831EDA147DE","version":"2.0.1","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/icombinationrunecrafter-2.0.1.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iMagic Caster","description":"Illumine automated magic caster","id":"imagiccaster-plugin","releases":[{"date":"2020-11-13","sha512sum":"1B65C27E79CAC1BFFA33782477DC1FA5265BF482254FDA8F85DD82423C7F9429ABB3B1BC16380F778D7D67BC874F13D148EDA17EF9EB1E0407D5EB37E5E31E7F","version":"3.2.0","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/imagiccaster-3.2.0.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iMenu Debugger Plugin","description":"Illumine - Menu Debugger plugin","id":"imenudebuggerplugin-plugin","releases":[{"date":"2020-11-13","sha512sum":"0491580D6FD8D5DA35F2F6F8723D84365D46F5446F533EE3B0053AEF9318F4E842F78886E6567F0F1E0CB0034C9D7D3CD438943615DBFA1E5495A6D58E04789E","version":"1.0.0","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/imenudebugger-1.0.0.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iPowerfighter Plugin","description":"Illumine - Powerfighter plugin","id":"ipowerfighterplugin-plugin","releases":[{"date":"2020-11-13","sha512sum":"007012DC00FA99B176F16BD744A138C2698095A382962BB7C35B13516E3F809323C85A45A3B87EDD26C482A415ADC39D268F83F912161408BA1EE4B2CD5FCB2A","version":"3.4.0","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/ipowerfighter-3.4.0.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iPower Skiller","description":"Illumine auto power skiller plugin","id":"ipowerskiller-plugin","releases":[{"date":"2020-11-13","sha512sum":"65B6112296D33786A454651A97768F7F157595D302C71E5A290866EC72CE5B8C181EDF90F59348F430C11EDE3825A0A883C91D2A1BAE4CBE247F11C49DA430B0","version":"5.0.1","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/ipowerskiller-5.0.1.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iQuick Eater","description":"Illumine - auto eat food, consume potions and equip items","id":"iquickeater-plugin","releases":[{"date":"2020-11-13","sha512sum":"373B237576D56EAF30CA9430628A20AE71A86C6A744445D35FE167FF518D9865CE6CD4B9A23555CDAEE00A5B7427E1E78A1273874306E54A0053E2AB593E5841","version":"4.3.0","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/iquickeater-4.3.0.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iRandom Handler","description":"illumine - Dismiss random events and handle genie","id":"irandomhandler-plugin","releases":[{"date":"2020-11-13","sha512sum":"12B35C7A9DF568EE8B8C3437264B90770EAEFC351BDE29DBC16653E71280E30663863D7027216F0118563267AA24DF4D7ED2FE54375491B1DED0A70F2FC557E9","version":"2.0.0","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/irandomhandler-2.0.0.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iRooftop Agility","description":"Illumine automated rooftop agility plugin","id":"irooftopagility-plugin","releases":[{"date":"2020-11-13","sha512sum":"0377CC9C8D2472971C538728A49B2529C751B82C2A8A73E2C3BA086A1EA7FB876E1EC63CC621B55BF8800678C3BEDD7FD42120A4EDB7D711BA21F32D3DD1CB2D","version":"5.0.4","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/irooftopagility-5.0.4.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iTaskTemplate","description":"Illumine - Task Template plugin","id":"itasktemplate-plugin","releases":[{"date":"2020-11-13","sha512sum":"29C88613DAC55A2DB28E9FF4856BDCAD41074053285ACE55C9BB5F75AF45773EC90EB27B4879A52AA4F82E2D4CB2D116E37A9816E31AE12BE35620DB5B959CE6","version":"1.0.1","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/itasktemplate-1.0.1.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iUtils","description":"Illumine - Utils required for plugins to function with added automation","id":"iutils-plugin","releases":[{"date":"2020-11-13","sha512sum":"D380B70C762A3F11983A3898C3950026017C711C5687D4596D46486C00F642868573817EE86097002530C2E5D2C8C2428114FB755F81CBFD5073C3229C39AD26","version":"2.0.0","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/iutils-2.0.0.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iWorld Walker Plugin","description":"Illumine - World Walker plugin","id":"iworldwalkerplugin-plugin","releases":[{"date":"2020-11-13","sha512sum":"3AA5155AD5C2F54340D3ECE2ADA43C4D8C9585E1DDBEF31A8682A4469E28EF4E69F8F78D6AEA4FF8FFC24D04212BC532232C9EEA8636BDCEEA004B1AC3E31A2E","version":"2.3.0","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/iworldwalker-2.3.0.jar?raw=true","requires":"0.0.1"}]}] +[{"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"BotUtils","description":"Illumine - Utils required for plugins to function with added automation","id":"botutils-plugin","releases":[{"date":"2020-11-14","sha512sum":"E6E86ECC9B7E7F45A97E7BF97A57F733247D5EDF280E14C83B685EEB517F64C421E069210E76E3672547B18F80EE148D973240627EB41D8FBA11C2A8EB359355","version":"4.9.1","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/botutils-4.9.1.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iBlackjack","description":"Illumine - Blackjack plugin","id":"iblackjack-plugin","releases":[{"date":"2020-11-14","sha512sum":"4720D7B7B28CAE15386B1B1640C4C2EA46F54FE22128698CC23DDE69CBABD954A37A09451D9B9E8E740FF62C65F89C1EE3E834156CA500D3F4CF7C227E0CE83D","version":"1.2.0","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/iblackjack-1.2.0.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iCombination Runecrafter Plugin","description":"Illumine - Combination Runecrafting plugin","id":"icombinationrunecrafterplugin-plugin","releases":[{"date":"2020-11-14","sha512sum":"5CF8190D3AB08D63ED8E5BA87D183A580270C31353F07BC80FF4576F3F742F66CFC2E97066370ADA2D7B5B61C67F00CB7C123C0B362E6581F7B4F831EDA147DE","version":"2.0.1","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/icombinationrunecrafter-2.0.1.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iHerbCleaner","description":"Illumine - Herb Cleaner","id":"iherbcleaner-plugin","releases":[{"date":"2020-11-14","sha512sum":"D35F9971933A6F54AEB6F968C384655452037DA52FE93170AD317C620EEC94F76EBA33AF02A1DA0AC5BBE71B5B73841255C783CBFDBECF5CB7B89FDE4C0AC625","version":"1.0.0","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/iherbcleaner-1.0.0.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iMagic Caster","description":"Illumine automated magic caster","id":"imagiccaster-plugin","releases":[{"date":"2020-11-14","sha512sum":"1B65C27E79CAC1BFFA33782477DC1FA5265BF482254FDA8F85DD82423C7F9429ABB3B1BC16380F778D7D67BC874F13D148EDA17EF9EB1E0407D5EB37E5E31E7F","version":"3.2.0","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/imagiccaster-3.2.0.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iMenu Debugger Plugin","description":"Illumine - Menu Debugger plugin","id":"imenudebuggerplugin-plugin","releases":[{"date":"2020-11-14","sha512sum":"0491580D6FD8D5DA35F2F6F8723D84365D46F5446F533EE3B0053AEF9318F4E842F78886E6567F0F1E0CB0034C9D7D3CD438943615DBFA1E5495A6D58E04789E","version":"1.0.0","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/imenudebugger-1.0.0.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iPowerfighter Plugin","description":"Illumine - Powerfighter plugin","id":"ipowerfighterplugin-plugin","releases":[{"date":"2020-11-14","sha512sum":"007012DC00FA99B176F16BD744A138C2698095A382962BB7C35B13516E3F809323C85A45A3B87EDD26C482A415ADC39D268F83F912161408BA1EE4B2CD5FCB2A","version":"3.4.0","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/ipowerfighter-3.4.0.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iPower Skiller","description":"Illumine auto power skiller plugin","id":"ipowerskiller-plugin","releases":[{"date":"2020-11-14","sha512sum":"65B6112296D33786A454651A97768F7F157595D302C71E5A290866EC72CE5B8C181EDF90F59348F430C11EDE3825A0A883C91D2A1BAE4CBE247F11C49DA430B0","version":"5.0.1","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/ipowerskiller-5.0.1.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iQuick Eater","description":"Illumine - auto eat food, consume potions and equip items","id":"iquickeater-plugin","releases":[{"date":"2020-11-14","sha512sum":"373B237576D56EAF30CA9430628A20AE71A86C6A744445D35FE167FF518D9865CE6CD4B9A23555CDAEE00A5B7427E1E78A1273874306E54A0053E2AB593E5841","version":"4.3.0","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/iquickeater-4.3.0.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iRandom Handler","description":"illumine - Dismiss random events and handle genie","id":"irandomhandler-plugin","releases":[{"date":"2020-11-14","sha512sum":"12B35C7A9DF568EE8B8C3437264B90770EAEFC351BDE29DBC16653E71280E30663863D7027216F0118563267AA24DF4D7ED2FE54375491B1DED0A70F2FC557E9","version":"2.0.0","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/irandomhandler-2.0.0.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iRooftop Agility","description":"Illumine automated rooftop agility plugin","id":"irooftopagility-plugin","releases":[{"date":"2020-11-14","sha512sum":"0377CC9C8D2472971C538728A49B2529C751B82C2A8A73E2C3BA086A1EA7FB876E1EC63CC621B55BF8800678C3BEDD7FD42120A4EDB7D711BA21F32D3DD1CB2D","version":"5.0.4","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/irooftopagility-5.0.4.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iTaskTemplate","description":"Illumine - Task Template plugin","id":"itasktemplate-plugin","releases":[{"date":"2020-11-14","sha512sum":"24DB8CBE0552AC1FFA50A667FBB75D518AA696D97D9D745F164C4614A0FFAEEFE49C806BCEBC5F21B3FA9171703EBB683B9EF474112A7C0FAAD70169A3DA9FA2","version":"1.0.1","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/itasktemplate-1.0.1.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iUtils","description":"Illumine - Utils required for plugins to function with added automation","id":"iutils-plugin","releases":[{"date":"2020-11-14","sha512sum":"D380B70C762A3F11983A3898C3950026017C711C5687D4596D46486C00F642868573817EE86097002530C2E5D2C8C2428114FB755F81CBFD5073C3229C39AD26","version":"2.0.0","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/iutils-2.0.0.jar?raw=true","requires":"0.0.1"}]}, {"projectUrl":"https://discord.gg/9fGzEDR","provider":"illumine","name":"iWorld Walker Plugin","description":"Illumine - World Walker plugin","id":"iworldwalkerplugin-plugin","releases":[{"date":"2020-11-14","sha512sum":"3AA5155AD5C2F54340D3ECE2ADA43C4D8C9585E1DDBEF31A8682A4469E28EF4E69F8F78D6AEA4FF8FFC24D04212BC532232C9EEA8636BDCEEA004B1AC3E31A2E","version":"2.3.0","url":"https://github.com/illumineawake/illu-plugins/blob/master/release/iworldwalker-2.3.0.jar?raw=true","requires":"0.0.1"}]}] diff --git a/release/iherbcleaner-1.0.0.jar b/release/iherbcleaner-1.0.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..3f3bf9a8e585f0abf9e9b4c5221a5859145479a0 GIT binary patch literal 18017 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 W?0rE10z!HJD|o-l{w*~J^#1^uOWdab literal 0 HcmV?d00001 diff --git a/release/itasktemplate-1.0.1.jar b/release/itasktemplate-1.0.1.jar index c30281882c73abe05e388446c963f0d0aaa0a035..ef8f0ad62f8a825e3479fc9a8bbe70ce6a12a9a4 100644 GIT binary patch delta 1967 zcmV;g2T=IfW7cD^kpc~u9wZ1C1ONcN2><{zlOYN%lgk1Ve-e@`+k!x;)mBTj3RKzJ zf=WqS+W?gsUMztszHX8s3~YAezC`*D{4sitEj{@1!5`p{@_6rTHVMRIvF4oIx%14u z&z*bcP9}f;_4^+HKES?#QRK52#ypFKek|fXi;r0>v3S7Z69WY-XR(4+p02U@l*L09 z>nw`Ay1{&#e+C}mvot<8u!S#}_scZC5;)~}Re_;`=U1)3*`D6D3SPA;{W*cYvZJIM z3Y;&vGPL~2m5vIfW!IIJ*Gpt{{$b>XswOv8ph}Ls>$+ZOhstvUfwP5OyJ7EJ%H5Ua z&|1=iCidKIMPct8C641+btkGSH?UM_2YaEc)g7A_e^rrY#pW!p35Y5ch038&SO1+! zTUly@2lDEzNjDOhI*HRVnI?XznimC9z&ep5+qNQ>ZKo_Sa}viqnMRH-V&dLUSwEKk zMx5-mld!C{J-FmviHmq{8n&Y<1n9t90!H9SS)Z4b zzlI(NWJ6WnYa>gviK+~lU|t4gU)8zR0wd!Sx??TJcB|HU=qs0d%<$a%c1;$EL*UAI zcNJuVb{SZS(Hy6r(xAZQ=bKXwIm$FHp0Vyle|}jmDy~YFw>ZUHGVv~^OkBZRCNAQ& zG`=?R4Q5Pyi&+ykixP`6X49yckl0S6YGMb<#4a1|@v_6B#=>Rcp>EO}X8NIfw+N!-T ze{iOG{tacvX8jr{+CH7Rz>OEUfyCk4ZpEuLdnhx$wL~9otryL2O`9Z}D@eB*?g$8C zyx3~if6m%$t?SG?rkwpyYuZtPffkrl!C<5nl`cev;Jjl8l zSCo!ip;i_+cl-@mw9BE#zZ_Yzj3Ir2e~yz+biY2mWAs9e(sO9w749w4s24~Vc$M@% z8ZT&!E<+dgIxcl#W4PRfy@5BoFuGw~*xMNI!X_};g4UA1k6o7Ll1pV{l<2&f09a+ zzC=kUN=D!i*#j6)Vf=I~{4|lzbcCOa!;}6YW}9Jmn_+Y9u>A?QnJA|d@iVCh^a8#Lczh-XuJZd%an=%;X_nI>gY=Esl6UA9eU=TK+lQYWmM;dvw-3 zc3jYoqw!+Xk+s{nLo!3e$;@(u4S~b! zA|3+(0Cxre06>!=3N4f0Fb0!SCK7*1QxicH{u)AtnT!FE#a%$rfXcXlTW}4cXh0O? z;H?R5Xc%TvGaaft_>VjYTDTlMdhj<{majV_AOWGQsp@&l_tt)|fBgFT4ZwB0aFE40 z8yVzm^kcxmd0f!cMJ*f5z{4diyX+8qMJuja7;-R-YZ`Ff!VQK-KP)q}6~lk1?8W}; zp+&D4mdhf_Go(jkAk`hh0<@+y9{EQ8pSipNVzELVI^O3lRHM7Sb@@(j_GLh}mu%w&6(z%fp*?@sUtHuX-1B)*_NG-t4#?OEgGYQ>%*fIbLtpOiOn60l zVug~4y+5G5uCmDp8G84IQ->(=HzHyvPh31j z(ZvKN8E*Z5=Pf*QF@@(YTF}ZcxPMRDnxSi#ZZKqO9eb&yPt$Sf-_udmsKnG*P&aSy zKNqQ!nMt@Q17Wb|#Op8;G_nk5>Tj(ShS7ZNnTehXWuWqVm0y=6U9XI5y<|AI!`Riw z(372x6<5_*uhR~*oUVU`-EqG?S;fDOKVe$m)yfKTAAHP)?VPjyf0o5OE&;nU)TG0$U}b0qu0AjnF{{oo;^(fKGH7M~87FF&-wg z)@24hqG1h<>qx!Z=Aw0ri_C_Lqd1nh7);`)*3k5Rn{DS7+th~bahynO-=dK|pohYz zzrbRcxbPhvI+@n?)|2aK9$1I{0gkS#(Kzm;IzTQQa+1X#xEjFEfroOYYfGPY01eYEp z2(y4Lbpi!}!|Ng*llm}&0)Q!#5iv58&?ylXP)h{{000005C9MWWd#5LTPy$o000jA By_^65 delta 1949 zcmV;O2V(fvW7uP`kpc~HTfo#81ONcN2><{zlOYN%lgk1Ve*#ICZJ|)8)mBTj3RKzJ z7AumrwxJ+3yjTKNeBFd03~YAez99Vv{un*SmL7ch&>!HB@_6rTb`uhh#hP<+=gysb zpJ(o!JDL3X*B{RT+{C_t5fpM5!YqsXeVD^Mi%(cAuz0}YQv*dT=CFiip02R?jKxD1 zt1L>qy2g0xe+C}m^9;T)uz@cb_p1!P7C7N}Re`~x=U1)3*}Ab~6}@Uz`ZEH(6-P-o z6gXFOWoY@4D;*U|%dRV{pqI$$+{4HXRZXs|K$RVN*LA(n4wdHy0>i}}d(Ymtl)ED< zp|zj~4eYsFifHc~1&;Zwx)W8E8(1o|gWXWp>W)o|f2u^YQe#%o00h+&g~}nSEB}t9 zy)3oH14VV$q#FrL9w%s#T$8w-nimC9z&chS`=*Q)+o=dl9Vf6ru92gQn1uIH){mvX z7AJf4I4CR42rju-=HeeSc3C^8S(AtVSm1nT8*p*%^D0y1m8-(r)KX*P>y&Z9+oV$U z7nLiQf1+Ah`X!qhME4pMW8L-@&l|?>a9h1Nvm*_`{Xn$YJ=;;6WYEML0!H9SS)Y}Z zzcxJ($c3u1+q5juCaN-IfLR$-d{yUK3!E7n*Bxs)wp+DULtnYvV}|F>+cjAt41vpI zomG%~w9CLsoMt%v9&Hr3^kR3)Ay1jc#WPmDf5@-MImK1U@fIg}OD5jKq>0OT+r$OD zp20UJzQvS@?=Wq`W>IEQ!E^?jCM32psG8V@GO@#syS(hMsIhQac&MBB9)1Rai4e4t z$^TaW3?dVI_`$?kydp5rtln~?-){Y{8&1D3`ZOO&RoxEd=Bx_nRLW6&URk}isG6$1 ze=l&VasGSCj@|mSp=A4X<^tDW;sz3iZ@Zgbt9# z6Td<{A*Bmfby{5{htW+*zE7GNJK-7ypj+SurZnUS^nV}*Qkn>unRtdS`kwfmfBj;m zCsBG6C6g#wfkWgDU_6EKa~nN_-)y0uiRnrI5Yr9Xod#{bN!yoTn~8EVQBEaFzoz~1 z00U1k@KYOap73wA@a9wAk7y4u@3ltVvJ;0meTczdQjB;%_gdn!X?&h=;@(u4Iq8b z03ZVZ0Cxre06>!=3N4dNCK!K9QxicD{suypBr70-yc9t}11gIOq6Y90MA3jK$idqX zhA^<~rgkS(dGH^35U}uZ@aVzcWLfr1RzLzmS*ev^!%*$|1%`jdoF5dN&|Mx{ zb#i{8Ac71-d{lZ;jT0u_x4;m~`b&bLJ|{gfQ(9jX!8~7dg>lYvcYy~|>ya_0R%FO9 zn)8Hmf|4g(sf3evrSOzfbV~*4g^pA_TvKAb=yJN$O6W}c8`3MxlT0PlZg5vF(WNQk z4dp5brSB$0m=9!8N#B2CXiE3xR`~{ZT<#T|ITcUqG!z&flkdSo?n~hMZ=7RyVTYw5mDwgIp>#x zyqJ`lwEDf~2XsR=PNUJrJ!CA5+8D#Qh5I%h;GvC2$l92|V+((iHlE7%I#G+XC13@Fp&{O%<^1dG|g^ro%Szmf8b6oosMbhV$VVO&YD~F6-Wez>r z@lbJ93iUc2GRuGQa@s>DEo-hahtZymyy|Fe%K4)2&_OG6^vEVwxVPkrS0y5>COtK! z7bBr)<|hO}%q~tbSW2!7zeM2~X)Om|kV&3ben9AiNv=(14fndsx;zXseU)zyy6JtY zqeqURU5_A1H6+tA(0PV5hyIzi_J0D~B%umTbjFR*Mzeo84FG4+Vgj|IEpi?vw7yIA ze?-+5s&^23e;`ETz7VP15bfxQLJUOtV_T^CaKN{9pKol}_Z-egzVA>?KcJh^$G^a0 zn7;NM&AOO|riRWP)b{Tnv5lnetJ(xUOML*HNa6y~(oLOQL>GEYqONkVD3O*r2}KVJ z>0P8%5f&n-1^bCE3ztah*`=}D6bU36Q=egPtd`~(DWTfo$_ jelB$a1t5LT03eh2FoObuDU-1