From 8d41f3c360b9d3ea97298d833bcae215c128efb0 Mon Sep 17 00:00:00 2001 From: yace0 <109323993+yace0@users.noreply.github.com> Date: Thu, 15 Aug 2024 23:25:20 +0100 Subject: [PATCH 01/52] maybe fixed plank make, works on my end. + foldered the things scripts I changed. --- .../construction2/Construction2Config.java | 2 +- .../construction2/Construction2Overlay.java | 3 +- .../construction2/Construction2Plugin.java | 4 +-- .../construction2/Construction2Script.java | 4 +-- .../enums/Construction2State.java | 2 +- .../lunarplankmake/LunarPlankMakeConfig.java} | 6 ++-- .../LunarPlankMakeOverlay.java} | 10 +++---- .../lunarplankmake/LunarPlankMakePlugin.java} | 28 +++++++++---------- .../lunarplankmake/LunarPlankMakeScript.java} | 14 +++++----- .../lunarplankmake}/enums/Logs.java | 2 +- 10 files changed, 37 insertions(+), 38 deletions(-) rename runelite-client/src/main/java/net/runelite/client/plugins/microbot/{ => GeoffPlugins}/construction2/Construction2Config.java (97%) rename runelite-client/src/main/java/net/runelite/client/plugins/microbot/{ => GeoffPlugins}/construction2/Construction2Overlay.java (90%) rename runelite-client/src/main/java/net/runelite/client/plugins/microbot/{ => GeoffPlugins}/construction2/Construction2Plugin.java (92%) rename runelite-client/src/main/java/net/runelite/client/plugins/microbot/{ => GeoffPlugins}/construction2/Construction2Script.java (98%) rename runelite-client/src/main/java/net/runelite/client/plugins/microbot/{ => GeoffPlugins}/construction2/enums/Construction2State.java (50%) rename runelite-client/src/main/java/net/runelite/client/plugins/microbot/{plankmake/PlankMakeConfig.java => GeoffPlugins/lunarplankmake/LunarPlankMakeConfig.java} (92%) rename runelite-client/src/main/java/net/runelite/client/plugins/microbot/{plankmake/PlankMakeOverlay.java => GeoffPlugins/lunarplankmake/LunarPlankMakeOverlay.java} (74%) rename runelite-client/src/main/java/net/runelite/client/plugins/microbot/{plankmake/PlankMakePlugin.java => GeoffPlugins/lunarplankmake/LunarPlankMakePlugin.java} (55%) rename runelite-client/src/main/java/net/runelite/client/plugins/microbot/{plankmake/PlankMakeScript.java => GeoffPlugins/lunarplankmake/LunarPlankMakeScript.java} (92%) rename runelite-client/src/main/java/net/runelite/client/plugins/microbot/{plankmake => GeoffPlugins/lunarplankmake}/enums/Logs.java (84%) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/construction2/Construction2Config.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/construction2/Construction2Config.java similarity index 97% rename from runelite-client/src/main/java/net/runelite/client/plugins/microbot/construction2/Construction2Config.java rename to runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/construction2/Construction2Config.java index 0401fbdb4a..52696f9309 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/construction2/Construction2Config.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/construction2/Construction2Config.java @@ -1,4 +1,4 @@ -package net.runelite.client.plugins.microbot.construction2; +package net.runelite.client.plugins.microbot.GeoffPlugins.construction2; import net.runelite.api.ItemID; import net.runelite.client.config.Config; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/construction2/Construction2Overlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/construction2/Construction2Overlay.java similarity index 90% rename from runelite-client/src/main/java/net/runelite/client/plugins/microbot/construction2/Construction2Overlay.java rename to runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/construction2/Construction2Overlay.java index 4f1d7dee29..821e765ae4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/construction2/Construction2Overlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/construction2/Construction2Overlay.java @@ -1,6 +1,5 @@ -package net.runelite.client.plugins.microbot.construction2; +package net.runelite.client.plugins.microbot.GeoffPlugins.construction2; -import net.runelite.client.ui.overlay.Overlay; import net.runelite.client.ui.overlay.OverlayPanel; import net.runelite.client.ui.overlay.OverlayPosition; import net.runelite.client.ui.overlay.components.LineComponent; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/construction2/Construction2Plugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/construction2/Construction2Plugin.java similarity index 92% rename from runelite-client/src/main/java/net/runelite/client/plugins/microbot/construction2/Construction2Plugin.java rename to runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/construction2/Construction2Plugin.java index 7325c546f7..b4913cb763 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/construction2/Construction2Plugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/construction2/Construction2Plugin.java @@ -1,4 +1,4 @@ -package net.runelite.client.plugins.microbot.construction2; +package net.runelite.client.plugins.microbot.GeoffPlugins.construction2; import com.google.inject.Provides; import lombok.extern.slf4j.Slf4j; @@ -9,7 +9,7 @@ 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.construction2.enums.Construction2State; +import net.runelite.client.plugins.microbot.GeoffPlugins.construction2.enums.Construction2State; import net.runelite.client.plugins.microbot.util.mouse.VirtualMouse; import net.runelite.client.ui.overlay.OverlayManager; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/construction2/Construction2Script.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/construction2/Construction2Script.java similarity index 98% rename from runelite-client/src/main/java/net/runelite/client/plugins/microbot/construction2/Construction2Script.java rename to runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/construction2/Construction2Script.java index e025a18e13..2284c0fbb2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/construction2/Construction2Script.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/construction2/Construction2Script.java @@ -1,10 +1,10 @@ -package net.runelite.client.plugins.microbot.construction2; +package net.runelite.client.plugins.microbot.GeoffPlugins.construction2; import net.runelite.api.*; 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.construction2.enums.Construction2State; +import net.runelite.client.plugins.microbot.GeoffPlugins.construction2.enums.Construction2State; import net.runelite.client.plugins.microbot.util.dialogues.Rs2Dialogue; import net.runelite.client.plugins.microbot.util.gameobject.Rs2GameObject; import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/construction2/enums/Construction2State.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/construction2/enums/Construction2State.java similarity index 50% rename from runelite-client/src/main/java/net/runelite/client/plugins/microbot/construction2/enums/Construction2State.java rename to runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/construction2/enums/Construction2State.java index 593d4f94d4..f1adad9ab8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/construction2/enums/Construction2State.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/construction2/enums/Construction2State.java @@ -1,4 +1,4 @@ -package net.runelite.client.plugins.microbot.construction2.enums; +package net.runelite.client.plugins.microbot.GeoffPlugins.construction2.enums; public enum Construction2State { Idle, diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/plankmake/PlankMakeConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/lunarplankmake/LunarPlankMakeConfig.java similarity index 92% rename from runelite-client/src/main/java/net/runelite/client/plugins/microbot/plankmake/PlankMakeConfig.java rename to runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/lunarplankmake/LunarPlankMakeConfig.java index fe8e5b527a..ee74a4600f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/plankmake/PlankMakeConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/lunarplankmake/LunarPlankMakeConfig.java @@ -1,13 +1,13 @@ -package net.runelite.client.plugins.microbot.plankmake; +package net.runelite.client.plugins.microbot.GeoffPlugins.lunarplankmake; 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.plankmake.enums.Logs; +import net.runelite.client.plugins.microbot.GeoffPlugins.lunarplankmake.enums.Logs; @ConfigGroup("plankMake") -public interface PlankMakeConfig extends Config { +public interface LunarPlankMakeConfig extends Config { String GROUP = "Plank Make"; @ConfigItem( diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/plankmake/PlankMakeOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/lunarplankmake/LunarPlankMakeOverlay.java similarity index 74% rename from runelite-client/src/main/java/net/runelite/client/plugins/microbot/plankmake/PlankMakeOverlay.java rename to runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/lunarplankmake/LunarPlankMakeOverlay.java index 1a5663ba73..989476f2d5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/plankmake/PlankMakeOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/lunarplankmake/LunarPlankMakeOverlay.java @@ -1,4 +1,4 @@ -package net.runelite.client.plugins.microbot.plankmake; +package net.runelite.client.plugins.microbot.GeoffPlugins.lunarplankmake; import java.awt.Color; import java.awt.Dimension; @@ -11,10 +11,10 @@ import net.runelite.client.ui.overlay.components.LineComponent; import net.runelite.client.ui.overlay.components.TitleComponent; -public class PlankMakeOverlay extends OverlayPanel { +public class LunarPlankMakeOverlay extends OverlayPanel { @Inject - PlankMakeOverlay(PlankMakePlugin plugin) + LunarPlankMakeOverlay(LunarPlankMakePlugin plugin) { super(plugin); setPosition(OverlayPosition.ABOVE_CHATBOX_RIGHT); @@ -25,13 +25,13 @@ public class PlankMakeOverlay extends OverlayPanel { public Dimension render(Graphics2D graphics) { panelComponent.setPreferredSize(new Dimension(200, 300)); panelComponent.getChildren().add(TitleComponent.builder() - .text("Plank Make " + PlankMakeScript.version) + .text("Plank Make " + LunarPlankMakeScript.version) .color(Color.YELLOW) .build()); // Update to display the combined message panelComponent.getChildren().add(LineComponent.builder() - .left(PlankMakeScript.combinedMessage) + .left(LunarPlankMakeScript.combinedMessage) .build()); return super.render(graphics); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/plankmake/PlankMakePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/lunarplankmake/LunarPlankMakePlugin.java similarity index 55% rename from runelite-client/src/main/java/net/runelite/client/plugins/microbot/plankmake/PlankMakePlugin.java rename to runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/lunarplankmake/LunarPlankMakePlugin.java index fc7ddef498..da6ebbc460 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/plankmake/PlankMakePlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/lunarplankmake/LunarPlankMakePlugin.java @@ -1,4 +1,4 @@ -package net.runelite.client.plugins.microbot.plankmake; +package net.runelite.client.plugins.microbot.GeoffPlugins.lunarplankmake; import java.awt.AWTException; @@ -15,43 +15,43 @@ import net.runelite.client.ui.overlay.OverlayManager; @PluginDescriptor( - name = PluginDescriptor.Geoff + "Plank Make", + name = PluginDescriptor.Geoff + "Lunar Plank Make", description = "Geoff's lunar plank maker", tags = {"magic", "moneymaking"}, enabledByDefault = false ) @Slf4j -public class PlankMakePlugin extends Plugin { +public class LunarPlankMakePlugin extends Plugin { @Inject - private PlankMakeConfig config; + private LunarPlankMakeConfig config; @Provides - PlankMakeConfig provideConfig(ConfigManager configManager) { - return configManager.getConfig(PlankMakeConfig.class); + LunarPlankMakeConfig provideConfig(ConfigManager configManager) { + return configManager.getConfig(LunarPlankMakeConfig.class); } @Inject private OverlayManager overlayManager; @Inject - private PlankMakeOverlay PlankMakeOverlay; + private LunarPlankMakeOverlay LunarPlankMakeOverlay; @Inject - PlankMakeScript PlankMakeScript; + LunarPlankMakeScript LunarPlankMakeScript; @Override protected void startUp() throws AWTException { Microbot.setMouse(new VirtualMouse()); - log.info("Starting up PlankMakePlugin"); + log.info("Starting up LunarPlankMakePlugin"); if (overlayManager != null) { - overlayManager.add(PlankMakeOverlay); + overlayManager.add(LunarPlankMakeOverlay); } - PlankMakeScript.run(config); + LunarPlankMakeScript.run(config); } @Override protected void shutDown() { - log.info("Shutting down PlankMakePlugin"); - PlankMakeScript.shutdown(); - overlayManager.remove(PlankMakeOverlay); + log.info("Shutting down LunarPlankMakePlugin"); + LunarPlankMakeScript.shutdown(); + overlayManager.remove(LunarPlankMakeOverlay); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/plankmake/PlankMakeScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/lunarplankmake/LunarPlankMakeScript.java similarity index 92% rename from runelite-client/src/main/java/net/runelite/client/plugins/microbot/plankmake/PlankMakeScript.java rename to runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/lunarplankmake/LunarPlankMakeScript.java index 17406e92a8..619c24a0d9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/plankmake/PlankMakeScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/lunarplankmake/LunarPlankMakeScript.java @@ -1,4 +1,4 @@ -package net.runelite.client.plugins.microbot.plankmake; +package net.runelite.client.plugins.microbot.GeoffPlugins.lunarplankmake; import java.util.concurrent.TimeUnit; import net.runelite.client.plugins.microbot.Microbot; @@ -10,7 +10,7 @@ import net.runelite.client.util.QuantityFormatter; import net.runelite.client.plugins.microbot.util.math.Random; -public class PlankMakeScript extends Script { +public class LunarPlankMakeScript extends Script { public static String version = "1.0.1"; public static String combinedMessage = ""; @@ -31,7 +31,7 @@ private enum State { private State currentState = State.PLANKING; - public boolean run(PlankMakeConfig config) { + public boolean run(LunarPlankMakeConfig config) { startTime = System.currentTimeMillis(); int unprocessedItemPrice = Microbot.getItemManager().search(config.ITEM().getName()).get(0).getPrice(); int processedItemPrice = Microbot.getItemManager().search(config.ITEM().getFinished()).get(0).getPrice(); @@ -57,13 +57,13 @@ public boolean run(PlankMakeConfig config) { break; } } catch (Exception ex) { - Microbot.log("Exception in PlankMakeScript: " + ex.getMessage()); + Microbot.log("Exception in LunarPlankMakeScript: " + ex.getMessage()); } }, 0, 50, TimeUnit.MILLISECONDS); return true; } - private void plankItems(PlankMakeConfig config) { + private void plankItems(LunarPlankMakeConfig config) { if (Rs2Inventory.hasItem(config.ITEM().getName(), true)) { int initialPlankCount = Rs2Inventory.count(config.ITEM().getFinished()); Rs2Magic.cast(MagicAction.PLANK_MAKE); @@ -95,7 +95,7 @@ private boolean waitForInventoryChange(String itemName, int initialCount) { return true; } - private void bank(PlankMakeConfig config) { + private void bank(LunarPlankMakeConfig config) { if (!Rs2Bank.openBank()) return; Rs2Bank.depositAll(config.ITEM().getFinished()); @@ -120,7 +120,7 @@ private void waitUntilReady() { currentState = State.PLANKING; } - private void calculateProfitAndDisplay(PlankMakeConfig config) { + private void calculateProfitAndDisplay(LunarPlankMakeConfig config) { double elapsedHours = (System.currentTimeMillis() - startTime) / 3600000.0; int plankPerHour = (int) (plankMade / elapsedHours); int totalProfit = profitPerPlank * (int) plankMade; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/plankmake/enums/Logs.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/lunarplankmake/enums/Logs.java similarity index 84% rename from runelite-client/src/main/java/net/runelite/client/plugins/microbot/plankmake/enums/Logs.java rename to runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/lunarplankmake/enums/Logs.java index 124aa2dd8e..285679ff3a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/plankmake/enums/Logs.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/GeoffPlugins/lunarplankmake/enums/Logs.java @@ -1,4 +1,4 @@ -package net.runelite.client.plugins.microbot.plankmake.enums; +package net.runelite.client.plugins.microbot.GeoffPlugins.lunarplankmake.enums; import lombok.Getter; import lombok.RequiredArgsConstructor; From e6f43575864424d45c86db3ef241814e0f519105 Mon Sep 17 00:00:00 2001 From: chsami Date: Sun, 18 Aug 2024 09:14:54 +0200 Subject: [PATCH 02/52] alkharid pay-toll fix --- .../client/plugins/microbot/util/walker/Rs2Walker.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/Rs2Walker.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/Rs2Walker.java index 96bb0e48b5..bee41f37f4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/Rs2Walker.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/Rs2Walker.java @@ -423,8 +423,11 @@ private static boolean handleDoors(List path, int index) { // Match action var action = Arrays.stream(objectComp.getActions()) - .filter(x -> x != null && doorActions.contains(x.toLowerCase())) - .min(Comparator.comparing(x -> doorActions.indexOf(x.toLowerCase()))).orElse(null); + .filter(x -> x != null && doorActions.stream().anyMatch(doorAction -> x.toLowerCase().startsWith(doorAction))) + .min(Comparator.comparing(x -> doorActions.indexOf( + doorActions.stream().filter(doorAction -> x.toLowerCase().startsWith(doorAction)).findFirst().orElse("")))) + .orElse(null); + if (action == null) continue; boolean found = false; @@ -469,6 +472,7 @@ private static boolean handleDoors(List path, int index) { if (found){ + System.out.println(action); Rs2GameObject.interact(object, action); Rs2Player.waitForWalking(); return true; From fcf6e664ecc67c3f385ca349fcd72b115c9a346d Mon Sep 17 00:00:00 2001 From: chsami Date: Sun, 18 Aug 2024 12:04:17 +0200 Subject: [PATCH 03/52] remove dependency 1.0.4 --- runelite-client/pom.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index 88d57cccf4..8b40e4c17d 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -56,12 +56,6 @@ RoaringBitmap 0.9.44 - - - com.google.archivepatcher - archive-patch-applier - 1.0.4 - org.benf From ca8605c163db24fe1c2c6afedaeab8b2210a22ba Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sat, 10 Aug 2024 16:43:47 +0200 Subject: [PATCH 04/52] Antiban stage 1 --- .../plugins/devtools/DevToolsPanel.java | 2 + .../plugins/devtools/DevToolsPlugin.java | 104 ++-- .../devtools/MicrobotClickOverlay.java | 43 ++ .../devtools/MicrobotMouseOverlay.java | 77 +++ .../client/plugins/microbot/Microbot.java | 64 ++- .../plugins/microbot/MicrobotPlugin.java | 17 +- .../breakhandler/BreakHandlerConfig.java | 43 +- .../breakhandler/BreakHandlerOverlay.java | 4 +- .../breakhandler/BreakHandlerPlugin.java | 26 +- .../breakhandler/BreakHandlerScript.java | 63 ++- .../microbot/util/antiban/AntibanOverlay.java | 68 +++ .../microbot/util/antiban/AntibanPlugin.java | 274 +++++++++ .../util/antiban/AntibanPluginPanel.java | 300 ++++++++++ .../util/antiban/AntibanSetupTemplates.java | 529 ++++++++++++++++++ .../microbot/util/antiban/MouseFatigue.java | 20 + .../microbot/util/antiban/Rs2Antiban.java | 434 ++++++++++++++ .../util/antiban/Rs2AntibanSettings.java | 28 + .../microbot/util/antiban/enums/Activity.java | 478 ++++++++++++++++ .../util/antiban/enums/ActivityIntensity.java | 65 +++ .../microbot/util/antiban/enums/Category.java | 249 +++++++++ .../util/antiban/enums/PlaySchedule.java | 47 ++ .../util/antiban/enums/PlayStyle.java | 184 ++++++ .../plugins/microbot/util/bank/Rs2Bank.java | 3 +- .../microbot/util/inventory/Rs2Inventory.java | 52 +- .../microbot/util/misc/Rs2UiHelper.java | 97 ++++ .../plugins/microbot/util/mouse/Mouse.java | 37 +- .../microbot/util/mouse/VirtualMouse.java | 148 +++-- .../util/mouse/naturalmouse/NaturalMouse.java | 180 ++++++ .../naturalmouse/api/DeviationProvider.java | 36 ++ .../naturalmouse/api/MouseInfoAccessor.java | 18 + .../mouse/naturalmouse/api/MouseMotion.java | 203 +++++++ .../naturalmouse/api/MouseMotionFactory.java | 200 +++++++ .../naturalmouse/api/MouseMotionObserver.java | 8 + .../mouse/naturalmouse/api/NoiseProvider.java | 36 ++ .../naturalmouse/api/OvershootManager.java | 49 ++ .../mouse/naturalmouse/api/SpeedManager.java | 20 + .../mouse/naturalmouse/api/SystemCalls.java | 16 + .../support/DefaultMouseInfoAccessor.java | 13 + .../support/DefaultMouseMotionNature.java | 36 ++ .../support/DefaultNoiseProvider.java | 34 ++ .../support/DefaultOvershootManager.java | 89 +++ .../support/DefaultSpeedManager.java | 56 ++ .../support/DefaultSystemCalls.java | 49 ++ .../naturalmouse/support/DoublePoint.java | 20 + .../util/mouse/naturalmouse/support/Flow.java | 121 ++++ .../support/MouseMotionNature.java | 238 ++++++++ .../support/ScreenAdjustedNature.java | 83 +++ .../support/SinusoidalDeviationProvider.java | 23 + .../support/mousemotion/MouseMovement.java | 34 ++ .../support/mousemotion/MovementFactory.java | 105 ++++ .../naturalmouse/tools/SystemDiagnosis.java | 60 ++ .../naturalmouse/util/FactoryTemplates.java | 279 +++++++++ .../naturalmouse/util/FlowTemplates.java | 99 ++++ .../mouse/naturalmouse/util/FlowUtil.java | 113 ++++ .../mouse/naturalmouse/util/MathUtil.java | 18 + .../util/mouse/naturalmouse/util/Pair.java | 11 + .../plugins/microbot/util/npc/Rs2Npc.java | 7 +- .../stretchedmode/StretchedModePlugin.java | 3 +- .../stretchedmode/TranslateMouseListener.java | 15 +- .../microbot/util/antiban/activity.png | Bin 0 -> 21873 bytes .../plugins/microbot/util/antiban/antiban.png | 0 .../microbot/util/antiban/cooldown.png | 0 .../plugins/microbot/util/antiban/general.png | 0 .../microbot/util/antiban/icons/cooldown.png | 0 .../microbot/util/antiban/icons/general.png | 0 .../util/antiban/icons/microbreak.png | 0 .../microbot/util/antiban/icons/mistake.png | 0 .../microbot/util/antiban/icons/mouse.png | 0 .../microbot/util/antiban/icons/profile.png | 0 .../microbot/util/antiban/icons/timeout.png | 0 .../microbot/util/antiban/microbreak.png | 0 .../plugins/microbot/util/antiban/mistake.png | 0 .../plugins/microbot/util/antiban/mouse.png | 0 .../plugins/microbot/util/antiban/profile.png | 0 .../plugins/microbot/util/antiban/timeout.png | 0 75 files changed, 5562 insertions(+), 166 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/devtools/MicrobotClickOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/devtools/MicrobotMouseOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPluginPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanSetupTemplates.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/MouseFatigue.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2Antiban.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2AntibanSettings.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/Activity.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/ActivityIntensity.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/Category.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/PlaySchedule.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/PlayStyle.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/NaturalMouse.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/DeviationProvider.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/MouseInfoAccessor.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/MouseMotion.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/MouseMotionFactory.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/MouseMotionObserver.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/NoiseProvider.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/OvershootManager.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/SpeedManager.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/SystemCalls.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultMouseInfoAccessor.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultMouseMotionNature.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultNoiseProvider.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultOvershootManager.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultSpeedManager.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultSystemCalls.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DoublePoint.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/Flow.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/MouseMotionNature.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/ScreenAdjustedNature.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/SinusoidalDeviationProvider.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/mousemotion/MouseMovement.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/mousemotion/MovementFactory.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/tools/SystemDiagnosis.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/util/FactoryTemplates.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/util/FlowTemplates.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/util/FlowUtil.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/util/MathUtil.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/util/Pair.java create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/activity.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/antiban.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/cooldown.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/general.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/icons/cooldown.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/icons/general.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/icons/microbreak.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/icons/mistake.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/icons/mouse.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/icons/profile.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/icons/timeout.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/microbreak.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/mistake.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/mouse.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/profile.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/timeout.png diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java index 2010ede138..2a8f00adc4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java @@ -125,6 +125,8 @@ private JPanel createOptionsPanel() container.add(plugin.getValidMovement()); container.add(plugin.getMovementFlags()); container.add(plugin.getInteracting()); + container.add(plugin.getMouseClick()); + container.add(plugin.getMouseMovement()); container.add(plugin.getExamine()); container.add(plugin.getDetachedCamera()); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java index ca9ad37d68..fc4161f527 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPlugin.java @@ -127,10 +127,50 @@ public class DevToolsPlugin extends Plugin @Inject private ChatMessageManager chatMessageManager; - @Inject private DevToolsConfig config; + private final HotkeyListener swingInspectorHotkeyListener = new HotkeyListener(() -> config.swingInspectorHotkey()) { + Object inspector; + @Override + public void hotkeyPressed() { + Window window = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow(); + try { + if (inspector == null) { + JRootPane rootPane = ((RootPaneContainer) window).getRootPane(); + FlatInspector fi = new FlatInspector(rootPane); + fi.setEnabled(true); + inspector = fi; + fi.addPropertyChangeListener(ev -> + { + if ("enabled".equals(ev.getPropertyName()) && !fi.isEnabled() && inspector == ev.getSource()) { + inspector = null; + } + }); + } else { + ((FlatInspector) inspector).setEnabled(false); + } + } catch (LinkageError | Exception e) { + log.warn("unable to open swing inspector", e); + JOptionPane.showMessageDialog(window, "The swing inspector is not available."); + } + } + }; + private final AWTEventListener swingInspectorKeyListener = rawEv -> + { + if (rawEv instanceof KeyEvent) { + KeyEvent kev = (KeyEvent) rawEv; + if (kev.getID() == KeyEvent.KEY_PRESSED) { + swingInspectorHotkeyListener.keyPressed(kev); + } else if (kev.getID() == KeyEvent.KEY_RELEASED) { + swingInspectorHotkeyListener.keyReleased(kev); + } + } + }; + @Inject + private MicrobotClickOverlay microbotClickOverlay; + @Inject + private MicrobotMouseOverlay microbotMouseOverlay; private DevToolsButton players; private DevToolsButton npcs; private DevToolsButton inventory; @@ -163,61 +203,10 @@ public class DevToolsPlugin extends Plugin private DevToolsButton shell; private DevToolsButton menus; private DevToolsButton uiDefaultsInspector; + private DevToolsButton mouseClick; + private DevToolsButton mouseMovement; private NavigationButton navButton; - private final HotkeyListener swingInspectorHotkeyListener = new HotkeyListener(() -> config.swingInspectorHotkey()) - { - Object inspector; - - @Override - public void hotkeyPressed() - { - Window window = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow(); - try - { - if (inspector == null) - { - JRootPane rootPane = ((RootPaneContainer) window).getRootPane(); - FlatInspector fi = new FlatInspector(rootPane); - fi.setEnabled(true); - inspector = fi; - fi.addPropertyChangeListener(ev -> - { - if ("enabled".equals(ev.getPropertyName()) && !fi.isEnabled() && inspector == ev.getSource()) - { - inspector = null; - } - }); - } - else - { - ((FlatInspector) inspector).setEnabled(false); - } - } - catch (LinkageError | Exception e) - { - log.warn("unable to open swing inspector", e); - JOptionPane.showMessageDialog(window, "The swing inspector is not available."); - } - } - }; - - private final AWTEventListener swingInspectorKeyListener = rawEv -> - { - if (rawEv instanceof KeyEvent) - { - KeyEvent kev = (KeyEvent) rawEv; - if (kev.getID() == KeyEvent.KEY_PRESSED) - { - swingInspectorHotkeyListener.keyPressed(kev); - } - else if (kev.getID() == KeyEvent.KEY_RELEASED) - { - swingInspectorHotkeyListener.keyReleased(kev); - } - } - }; - @Provides DevToolsConfig provideConfig(ConfigManager configManager) { @@ -267,6 +256,9 @@ protected void startUp() throws Exception uiDefaultsInspector = new DevToolsButton("Swing Defaults"); + mouseClick = new DevToolsButton("Bot Clicks"); + mouseMovement = new DevToolsButton("Bot Mouse"); + overlayManager.add(overlay); overlayManager.add(locationOverlay); overlayManager.add(sceneOverlay); @@ -274,6 +266,8 @@ protected void startUp() throws Exception overlayManager.add(worldMapLocationOverlay); overlayManager.add(mapRegionOverlay); overlayManager.add(soundEffectOverlay); + overlayManager.add(microbotClickOverlay); + overlayManager.add(microbotMouseOverlay); final DevToolsPanel panel = injector.getInstance(DevToolsPanel.class); @@ -304,6 +298,8 @@ protected void shutDown() throws Exception overlayManager.remove(worldMapLocationOverlay); overlayManager.remove(mapRegionOverlay); overlayManager.remove(soundEffectOverlay); + overlayManager.remove(microbotClickOverlay); + overlayManager.remove(microbotMouseOverlay); clientToolbar.removeNavigation(navButton); Toolkit.getDefaultToolkit().removeAWTEventListener(swingInspectorKeyListener); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/MicrobotClickOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/MicrobotClickOverlay.java new file mode 100644 index 0000000000..bf314ce1b3 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/MicrobotClickOverlay.java @@ -0,0 +1,43 @@ +package net.runelite.client.plugins.devtools; + +import net.runelite.api.Client; +import net.runelite.client.plugins.microbot.Microbot; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayUtil; + +import javax.inject.Inject; +import java.awt.*; + +public class MicrobotClickOverlay extends Overlay { + private final Client client; + private final DevToolsPlugin plugin; + + @Inject + MicrobotClickOverlay(Client client, DevToolsPlugin plugin) { + this.client = client; + this.plugin = plugin; + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_WIDGETS); + setPriority(Overlay.PRIORITY_LOW); + } + + @Override + public Dimension render(Graphics2D g) { + if (plugin.getMouseClick().isActive()) { + + g.setFont(new Font("Tahoma", Font.BOLD, 18)); + + OverlayUtil.renderTextLocation(g, new net.runelite.api.Point(Microbot.getMouse().getLastClick().getX() - (g.getFont().getSize() / 3), + Microbot.getMouse().getLastClick().getY() + (g.getFont().getSize() / 3)), "X", Color.WHITE); + OverlayUtil.renderTextLocation(g, new net.runelite.api.Point(Microbot.getMouse().getLastClick2().getX() - (g.getFont().getSize() / 3), + Microbot.getMouse().getLastClick2().getY() + (g.getFont().getSize() / 3)), "X", Color.GREEN); + + + } + + return null; + } +} + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/MicrobotMouseOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/MicrobotMouseOverlay.java new file mode 100644 index 0000000000..b67c283323 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/MicrobotMouseOverlay.java @@ -0,0 +1,77 @@ +package net.runelite.client.plugins.devtools; + +import net.runelite.api.Client; +import net.runelite.client.plugins.microbot.Microbot; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayUtil; + +import javax.inject.Inject; +import java.awt.*; +import java.awt.geom.Path2D; + +public class MicrobotMouseOverlay extends Overlay { + private final Client client; + private final DevToolsPlugin plugin; + + @Inject + MicrobotMouseOverlay(Client client, DevToolsPlugin plugin) { + this.client = client; + this.plugin = plugin; + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_WIDGETS); + setPriority(Overlay.PRIORITY_LOW); + } + + @Override + public Dimension render(Graphics2D g) { + if (plugin.getMouseMovement().isActive()) { + if (Microbot.getClient().getCanvas().getCursor().getType() != Cursor.CROSSHAIR_CURSOR) { + Microbot.getClient().getCanvas().setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR)); + } + if (!Microbot.getMouse().getTimer().isRunning()) { + Microbot.getMouse().getTimer().start(); + } + //g.setFont(new Font("Tahoma", Font.BOLD, 18)); + g.setFont(g.getFont().deriveFont(40.0f)); + // Get the FontMetrics for the current font + FontMetrics metrics = g.getFontMetrics(g.getFont()); + +// Get the width and height of the character + int charWidth = metrics.stringWidth("⊹"); + int charHeight = metrics.getAscent(); // ascent gives the height of the character above the baseline + +// Calculate the new position + int x = Microbot.getMouse().getLastMove().getX() - (charWidth / 2); + int y = Microbot.getMouse().getLastMove().getY() + (charHeight / 2); + + OverlayUtil.renderTextLocation(g, new net.runelite.api.Point(x, y), "⊹", Microbot.getMouse().getRainbowColor()); + + g.setStroke(new BasicStroke(3)); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + var points = Microbot.getMouse().getPoints(); + if (points.size() > 1) { + Path2D path = new Path2D.Double(); + net.runelite.api.Point firstPoint = points.getFirst(); + path.moveTo(firstPoint.getX(), firstPoint.getY()); + + for (int i = 1; i < points.size(); i++) { + net.runelite.api.Point p = points.get(i); + path.lineTo(p.getX(), p.getY()); + g.setColor(Microbot.getMouse().getRainbowColor()); + } + + g.draw(path); + } + // draw trail of mouse movements + + } else { + Microbot.getMouse().getPoints().clear(); + Microbot.getMouse().getTimer().stop(); + } + + return null; + } +} + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java index 01372d75a1..f3e64e2058 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java @@ -21,7 +21,9 @@ import net.runelite.client.plugins.microbot.dashboard.PluginRequestModel; import net.runelite.client.plugins.microbot.util.inventory.Rs2Item; import net.runelite.client.plugins.microbot.util.math.Random; +import net.runelite.client.plugins.microbot.util.misc.Rs2UiHelper; import net.runelite.client.plugins.microbot.util.mouse.Mouse; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.NaturalMouse; import net.runelite.client.plugins.timers.GameTimer; import net.runelite.client.plugins.timers.TimersPlugin; import net.runelite.client.ui.overlay.infobox.InfoBox; @@ -239,9 +241,13 @@ public static List updateItemContainer(int id, ItemContainerChanged e) } public static void startPlugin(Plugin plugin) { - + if (plugin == null) return; + if (!Microbot.getPluginManager().isPluginEnabled(plugin)) { + Microbot.getPluginManager().setPluginEnabled(plugin, true); + } } + @Deprecated(since = "1.3.8 - use Rs2UiHelper", forRemoval = true) public static Point calculateClickingPoint(Rectangle rect) { if (rect.getX() == 1 && rect.getY() == 1) return new Point(1, 1); int x = (int) (rect.getX() + (double) Random.random((int) rect.getWidth() / 6 * -1, (int) rect.getWidth() / 6) + rect.getWidth() / 2.0); @@ -251,38 +257,52 @@ public static Point calculateClickingPoint(Rectangle rect) { public static void doInvoke(MenuEntry entry, Rectangle rectangle) { targetMenu = entry; - int viewportHeight = client.getViewportHeight(); - int viewportWidth = client.getViewportWidth(); - if (!(rectangle.getX() > (double) viewportWidth) && !(rectangle.getY() > (double) viewportHeight) && !(rectangle.getX() < 0.0) && !(rectangle.getY() < 0.0)) { - click(rectangle); - } else { - click(new Rectangle(1, 1)); + try { + if (Rs2UiHelper.isRectangleWithinViewport(rectangle)) { + click(rectangle); + } else { + click(new Rectangle(1, 1)); + } + } catch (ArrayIndexOutOfBoundsException e) { + e.printStackTrace(); + // Handle the error as needed } } + public static void drag(Rectangle start, Rectangle end) { + if (start == null || end == null) return; + if (!Rs2UiHelper.isRectangleWithinViewport(start) || !Rs2UiHelper.isRectangleWithinViewport(end)) return; + Point startPoint = Rs2UiHelper.getClickingPoint(start, true); + Point endPoint = Rs2UiHelper.getClickingPoint(end, true); + mouse.drag(startPoint, endPoint); + } + public static void click(Rectangle rectangle) { - Point point = calculateClickingPoint(rectangle); - if (client.isStretchedEnabled()) { - Dimension stretched = client.getStretchedDimensions(); - Dimension real = client.getRealDimensions(); - double width = (double) stretched.width / real.getWidth(); - double height = (double) stretched.height / real.getHeight(); - point = new Point((int) ((double) point.getX() * width), (int) ((double) point.getY() * height)); - } + Point point = Rs2UiHelper.getClickingPoint(rectangle, true); +// if (client.isStretchedEnabled()) { +// Dimension stretched = client.getStretchedDimensions(); +// Dimension real = client.getRealDimensions(); +// double width = (double) stretched.width / real.getWidth(); +// double height = (double) stretched.height / real.getHeight(); +// point = new Point((int) ((double) point.getX() * width), (int) ((double) point.getY() * height)); +// } +// mouseEvent(504, point); +// mouseEvent(505, point); +// mouseEvent(503, point); +// mouseEvent(501, point); +// mouseEvent(502, point); +// mouseEvent(500, point); + + mouse.click(point); - mouseEvent(504, point); - mouseEvent(505, point); - mouseEvent(503, point); - mouseEvent(501, point); - mouseEvent(502, point); - mouseEvent(500, point); if (!Microbot.getClient().isClientThread()) { - sleep(50, 100); + sleep(50, 80); } } + @Deprecated(since = "1.3.8 - use Mouse class", forRemoval = true) private static void mouseEvent(int id, Point point) { MouseEvent e = new MouseEvent(client.getCanvas(), id, System.currentTimeMillis(), 0, point.getX(), point.getY(), 1, false, 1); client.getCanvas().dispatchEvent(e); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotPlugin.java index 367038b573..8aa4919545 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/MicrobotPlugin.java @@ -22,6 +22,7 @@ import net.runelite.client.plugins.microbot.util.equipment.Rs2Equipment; import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; import net.runelite.client.plugins.microbot.util.mouse.VirtualMouse; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.NaturalMouse; import net.runelite.client.plugins.microbot.util.player.Rs2Player; import net.runelite.client.plugins.microbot.util.reflection.Rs2Reflection; import net.runelite.client.plugins.microbot.util.shop.Rs2Shop; @@ -46,12 +47,6 @@ ) @Slf4j public class MicrobotPlugin extends Plugin { - @Inject - private Client client; - @Inject - private ClientThread clientThread; - @Inject - private ClientToolbar clientToolbar; @Inject Notifier notifier; @Inject @@ -68,7 +63,12 @@ public class MicrobotPlugin extends Plugin { ChatMessageManager chatMessageManager; @Inject PluginManager pluginManager; - + @Inject + private Client client; + @Inject + private ClientThread clientThread; + @Inject + private ClientToolbar clientToolbar; @Inject private MicrobotOverlay microbotOverlay; @Inject @@ -81,6 +81,8 @@ public class MicrobotPlugin extends Plugin { private InfoBoxManager infoBoxManager; @Inject private WorldMapPointManager worldMapPointManager; + @Inject + private NaturalMouse naturalMouse; @Override protected void startUp() throws AWTException { Microbot.pauseAllScripts = false; @@ -92,6 +94,7 @@ protected void startUp() throws AWTException { Microbot.setItemManager(itemManager); Microbot.setNpcManager(npcManager); Microbot.setMouse(new VirtualMouse()); + Microbot.setNaturalMouse(naturalMouse); Microbot.setSpriteManager(spriteManager); Microbot.setPluginManager(pluginManager); Microbot.setWorldMapOverlay(worldMapOverlay); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerConfig.java index 5d58564c7f..5a6196b508 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerConfig.java @@ -3,21 +3,33 @@ 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.util.antiban.enums.PlaySchedule; @ConfigGroup("Breakhandler") public interface BreakHandlerConfig extends Config { + + // Play Schedule section + @ConfigSection( + name = "Play Schedule", + description = "Options related to using a play schedule", + position = 5 + ) + String usePlaySchedule = "usePlaySchedule"; + @ConfigItem( keyName = "TimeUntilBreakStart", - name = "Time break start", + name = "Time until break start", description = "Time until break start in minutes", position = 0 ) default int timeUntilBreakStart() { return 60; } + @ConfigItem( keyName = "TimeUntilBreakEnd", - name = "Time break end", + name = "Time until break end", description = "Time until break ends in minutes", position = 1 ) @@ -34,6 +46,7 @@ default int timeUntilBreakEnd() { default int breakDurationStart() { return 10; } + @ConfigItem( keyName = "BreakDurationEnd", name = "Break duration end", @@ -43,14 +56,36 @@ default int breakDurationStart() { default int breakDurationEnd() { return 15; } + @ConfigItem( keyName = "Logout", name = "Logout", - description = "logout when taking a break", + description = "Logout when taking a break", position = 4 ) default boolean logoutAfterBreak() { return true; } -} + @ConfigItem( + keyName = "UsePlaySchedule", + name = "Use Play Schedule", + description = "Enable or disable the use of a play schedule", + position = 5, + section = "UsePlaySchedule" + ) + default boolean usePlaySchedule() { + return false; + } + + @ConfigItem( + keyName = "PlaySchedule", + name = "Play Schedule", + description = "Select the play schedule", + position = 6, + section = "UsePlaySchedule" + ) + default PlaySchedule playSchedule() { + return PlaySchedule.MEDIUM_DAY; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerOverlay.java index 161d1c6a86..56198390ee 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerOverlay.java @@ -40,12 +40,12 @@ public Dimension render(Graphics2D graphics) { if (BreakHandlerScript.breakIn > 0) { panelComponent.getChildren().add(LineComponent.builder() - .left(String.format("Break in: %02d:%02d:%02d%n", hours, minutes, seconds)) + .left(BreakHandlerScript.formatDuration(BreakHandlerScript.breakInDuration, "Break in:")) .build()); } if (BreakHandlerScript.breakDuration > 0) { panelComponent.getChildren().add(LineComponent.builder() - .left(String.format("Break duration: %02d:%02d:%02d%n", hours, minutes, seconds)) + .left(BreakHandlerScript.formatDuration(BreakHandlerScript.duration, "Break duration:")) .build()); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerPlugin.java index 20b7709c04..66c5414991 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerPlugin.java @@ -3,6 +3,8 @@ import com.google.inject.Provides; import lombok.extern.slf4j.Slf4j; import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ConfigChanged; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.ui.overlay.OverlayManager; @@ -18,21 +20,19 @@ ) @Slf4j public class BreakHandlerPlugin extends Plugin { + @Inject + BreakHandlerScript breakHandlerScript; @Inject private BreakHandlerConfig config; - @Provides - BreakHandlerConfig provideConfig(ConfigManager configManager) { - return configManager.getConfig(BreakHandlerConfig.class); - } - @Inject private OverlayManager overlayManager; @Inject private BreakHandlerOverlay breakHandlerOverlay; - @Inject - BreakHandlerScript breakHandlerScript; - + @Provides + BreakHandlerConfig provideConfig(ConfigManager configManager) { + return configManager.getConfig(BreakHandlerConfig.class); + } @Override protected void startUp() throws AWTException { @@ -46,4 +46,14 @@ protected void shutDown() { breakHandlerScript.shutdown(); overlayManager.remove(breakHandlerOverlay); } + + // on settings change + @Subscribe + public void onConfigChanged(ConfigChanged event) { + if (event.getGroup().equals("Breakhandler")) { + if (event.getKey().equals("UsePlaySchedule")) { + breakHandlerScript.reset(); + } + } + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerScript.java index 6a5d35193b..06fb91085b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerScript.java @@ -4,6 +4,7 @@ import lombok.Setter; 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.math.Random; import net.runelite.client.plugins.microbot.util.player.Rs2Player; import net.runelite.client.plugins.microbot.util.security.Login; @@ -13,7 +14,6 @@ import java.time.LocalDateTime; import java.util.concurrent.TimeUnit; - public class BreakHandlerScript extends Script { public static String version = "1.0.0"; @@ -23,49 +23,86 @@ public class BreakHandlerScript extends Script { public static int totalBreaks = 0; public static Duration duration; - + public static Duration breakInDuration; + @Setter @Getter public static boolean lockState = false; private String title = ""; + + public static String formatDuration(Duration duration, String header) { + long hours = duration.toHours(); + long minutes = duration.toMinutes() % 60; + long seconds = duration.getSeconds() % 60; + return String.format(header + " %02d:%02d:%02d", hours, minutes, seconds); + } public boolean run(BreakHandlerConfig config) { + Microbot.enableAutoRunOn = false; title = ClientUI.getFrame().getTitle(); breakIn = Random.random(config.timeUntilBreakStart() * 60, config.timeUntilBreakEnd() * 60); mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { try { - if (breakIn > 0) { + if (config.playSchedule().isOutsideSchedule() && config.usePlaySchedule() && !isLockState()) { + Duration untilNextSchedule = config.playSchedule().timeUntilNextSchedule(); + breakIn = -1; + breakDuration = (int) untilNextSchedule.toSeconds(); + } + + if (breakIn > 0 && breakDuration <= 0) { breakIn--; - duration = Duration.between(LocalDateTime.now(),LocalDateTime.now().plusSeconds(breakIn)); + duration = Duration.between(LocalDateTime.now(), LocalDateTime.now().plusSeconds(breakIn)); + breakInDuration = duration; } + if (breakDuration > 0) { breakDuration--; - duration = Duration.between(LocalDateTime.now(),LocalDateTime.now().plusSeconds(breakDuration)); + duration = Duration.between(LocalDateTime.now(), LocalDateTime.now().plusSeconds(breakDuration)); long hours = BreakHandlerScript.duration.toHours(); long minutes = BreakHandlerScript.duration.toMinutes() % 60; long seconds = BreakHandlerScript.duration.getSeconds() % 60; - ClientUI.getFrame().setTitle(String.format("Break duration: %02d:%02d:%02d%n", hours, minutes, seconds)); + if (Rs2AntibanSettings.takeMicroBreaks && Rs2AntibanSettings.microBreakActive) { + ClientUI.getFrame().setTitle(String.format("Micro break duration: %02d:%02d:%02d", hours, minutes, seconds)); + } else if (config.playSchedule().isOutsideSchedule() && config.usePlaySchedule()) { + ClientUI.getFrame().setTitle(String.format("Next schedule in: %02d:%02d:%02d", hours, minutes, seconds)); + } else { + ClientUI.getFrame().setTitle(String.format("Break duration: %02d:%02d:%02d", hours, minutes, seconds)); + } } if (breakDuration <= 0 && Microbot.pauseAllScripts) { Microbot.pauseAllScripts = false; - breakIn = Random.random(config.timeUntilBreakStart() * 60, config.timeUntilBreakEnd() * 60); + if (breakIn <= 0) + breakIn = Random.random(config.timeUntilBreakStart() * 60, config.timeUntilBreakEnd() * 60); new Login(); totalBreaks++; ClientUI.getFrame().setTitle(title); + if (Rs2AntibanSettings.takeMicroBreaks) { + Rs2AntibanSettings.microBreakActive = false; + } return; } - if (breakIn <= 0 && !Microbot.pauseAllScripts && !isLockState()) { + if ((breakIn <= 0 && !Microbot.pauseAllScripts && !isLockState()) || (Rs2AntibanSettings.microBreakActive && !Microbot.pauseAllScripts && !isLockState())) { Microbot.pauseAllScripts = true; + + if (Rs2AntibanSettings.microBreakActive) + return; + if (config.playSchedule().isOutsideSchedule() && config.usePlaySchedule()) { + Rs2Player.logout(); + return; + } + + breakDuration = Random.random(config.breakDurationStart() * 60, config.breakDurationEnd() * 60); - if (config.logoutAfterBreak()) + + if (config.logoutAfterBreak()) { Rs2Player.logout(); + } } - } catch (Exception ex) { System.out.println(ex.getMessage()); } @@ -80,4 +117,10 @@ public void shutdown() { ClientUI.getFrame().setTitle(title); super.shutdown(); } + + public void reset() { + breakIn = 0; + breakDuration = 0; + ClientUI.getFrame().setTitle(title); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanOverlay.java new file mode 100644 index 0000000000..51b5121c9a --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanOverlay.java @@ -0,0 +1,68 @@ +package net.runelite.client.plugins.microbot.util.antiban; + +import net.runelite.api.Actor; +import net.runelite.api.Point; +import net.runelite.client.plugins.microbot.Microbot; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.components.ProgressPieComponent; + +import javax.inject.Inject; +import java.awt.*; +import java.awt.image.BufferedImage; + + +public class AntibanOverlay extends Overlay { + + private static final Color PUBLIC_TIMER_COLOR = Color.YELLOW; + private static final Color PRIVATE_TIMER_COLOR = Color.GREEN; + private static final int TIMER_OVERLAY_DIAMETER = 20; + private final ProgressPieComponent progressPieComponent = new ProgressPieComponent(); + + @Inject + public AntibanOverlay() { + setPosition(OverlayPosition.DYNAMIC); + } + + private Point getCanvasTextLocation(Graphics2D graphics, Actor actor) { + int zOffset = Math.min(actor.getLogicalHeight(), 140); + // create blank buffered image + BufferedImage bufferedImage = new BufferedImage(TIMER_OVERLAY_DIAMETER, TIMER_OVERLAY_DIAMETER, BufferedImage.TYPE_INT_ARGB); + + return actor.getCanvasImageLocation(bufferedImage, zOffset); + } + + private void drawTimerPieOverlay(Graphics2D graphics) { + + Color fillColor = Color.YELLOW; + + // Calculate the remaining time as a fraction of the total time + int totalTime = Rs2Antiban.getPlayStyle().getSecondaryTickInterval(); + + int timeLeft = Rs2Antiban.getTIMEOUT(); + float percent = (float) timeLeft / totalTime; + + // Get the player's screen location + Point playerLocation = getCanvasTextLocation(graphics, Microbot.getClient().getLocalPlayer()); + + progressPieComponent.setDiameter(TIMER_OVERLAY_DIAMETER); + // Shift over to not be on top of the text + int x = playerLocation.getX() + (TIMER_OVERLAY_DIAMETER / 2); + int y = playerLocation.getY() - (15 + (Microbot.getClient().getScale() / 30)); + progressPieComponent.setPosition(new Point(x, y)); + progressPieComponent.setFill(fillColor); + progressPieComponent.setBorderColor(fillColor); + progressPieComponent.setProgress(percent); // inverse so pie drains over time + progressPieComponent.render(graphics); + } + + @Override + public Dimension render(Graphics2D graphics) { + if (Rs2AntibanSettings.actionCooldownActive) { + drawTimerPieOverlay(graphics); + } + + + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java new file mode 100644 index 0000000000..00e95920b1 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java @@ -0,0 +1,274 @@ +package net.runelite.client.plugins.microbot.util.antiban; + +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.AnimationID; +import net.runelite.api.GameState; +import net.runelite.api.Skill; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.StatChanged; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ProfileChanged; +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.breakhandler.BreakHandlerPlugin; +import net.runelite.client.plugins.microbot.util.antiban.enums.Activity; +import net.runelite.client.plugins.microbot.util.antiban.enums.ActivityIntensity; +import net.runelite.client.plugins.microbot.util.player.Rs2Player; +import net.runelite.client.ui.ClientToolbar; +import net.runelite.client.ui.NavigationButton; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.util.ImageUtil; + +import javax.inject.Inject; +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.time.Duration; +import java.time.Instant; +import java.util.EnumMap; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; + +@PluginDescriptor( + name = PluginDescriptor.See1Duck + "Antiban", + description = "Antiban for microbot", + tags = {"main", "microbot", "antiban parent"}, + alwaysOn = true, + hidden = true +) +@Slf4j +public class AntibanPlugin extends Plugin { + + private static final int COOK_TIMEOUT = 3; + private static final int MINING_TIMEOUT = 3; + private static final int IDLE_TIMEOUT = 1; + public static int ticksSinceLogin; + private static Instant lastCookingAction = Instant.MIN; + private static Instant lastMiningAction = Instant.MIN; + private static int idleTicks = 0; + private final Map skillExp = new EnumMap<>(Skill.class); + private boolean ready; + private Skill lastSkillChanged; + private NavigationButton navButton; + + @Inject + private OverlayManager overlayManager; + + @Inject + private ClientToolbar clientToolbar; + + public static boolean isCooking() { + return Rs2Player.getAnimation() == AnimationID.COOKING_FIRE + || Rs2Player.getAnimation() == AnimationID.COOKING_RANGE + || Duration.between(lastCookingAction, Instant.now()).getSeconds() < COOK_TIMEOUT; + } + + public static boolean isMining() { + return Rs2Antiban.isMining() + || Duration.between(lastMiningAction, Instant.now()).getSeconds() < MINING_TIMEOUT; + } + + public static boolean isIdle() { + return idleTicks > IDLE_TIMEOUT; + } + + public static void updateIdleTicks() { + idleTicks++; + } + + private static void updateLastCookingAction() { + lastCookingAction = Instant.now(); + } + + private static void updateLastMiningAction() { + lastMiningAction = Instant.now(); + } + + public static void performActionBreak() { + if (Rs2AntibanSettings.actionCooldownActive) { + if (Rs2Antiban.getTIMEOUT() > 0) { + if (!Rs2Antiban.getCategory().isBusy()) { + Rs2Antiban.TIMEOUT--; + } + } else { + Rs2AntibanSettings.actionCooldownActive = false; + //Microbot.pauseAllScripts = false; + } + } + } + + @Override + protected void startUp() throws AWTException { + final AntibanPluginPanel panel = injector.getInstance(AntibanPluginPanel.class); + final BufferedImage icon = ImageUtil.loadImageResource(getClass(), "antiban.png"); + navButton = NavigationButton.builder() + .tooltip("Antiban") + .icon(icon) + .priority(1) + .panel(panel) + .build(); + + Timer timer = new Timer(); + timer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + SwingUtilities.invokeLater(panel::loadSettings); + } + }, 0, 600); + + clientToolbar.addNavigation(navButton); + overlayManager.add(new AntibanOverlay()); + } + + @Override + protected void shutDown() { + overlayManager.removeIf(overlay -> overlay instanceof AntibanOverlay); + clientToolbar.removeNavigation(navButton); + } + + @Subscribe + public void onChatMessage(ChatMessage event) { + if (Rs2Antiban.checkForCookingEvent(event)) { + updateLastCookingAction(); + } + } + + @Subscribe + public void onProfileChanged(ProfileChanged event) { + Rs2Antiban.resetAntiban(); + } + + @Subscribe + public void onGameStateChanged(GameStateChanged event) { + GameState state = event.getGameState(); + + switch (state) { + case LOGGING_IN: + case HOPPING: + ready = true; + break; + case LOGGED_IN: + if (ready) { + ticksSinceLogin = 0; + ready = false; + } + break; + } + } + + @Subscribe + public void onGameTick(GameTick event) { + ticksSinceLogin++; + + if (!Rs2AntibanSettings.antibanEnabled) { + return; + } + + if (!Rs2Player.isAnimating()) { + updateIdleTicks(); + } else { + if (Rs2AntibanSettings.simulateFatigue) { + ticksSinceLogin -= idleTicks; + } + idleTicks = 0; + } + + if (Rs2AntibanSettings.takeMicroBreaks && !Microbot.isPluginEnabled(BreakHandlerPlugin.class)) { + Microbot.log("BreakHandlerPlugin is not enabled, attempting to enable it...."); + Plugin breakHandlerPlugin = Microbot.getPluginManager().getPlugins() + .stream() + .filter(p -> p.getName().contains("BreakHandler")) + .findFirst() + .orElse(null); + Microbot.startPlugin(breakHandlerPlugin); + } + + if (Rs2Antiban.isMining()) { + updateLastMiningAction(); + } + + if (Rs2AntibanSettings.actionCooldownActive) { + performActionBreak(); + } + + if (Rs2AntibanSettings.simulateAttentionSpan && Rs2AntibanSettings.profileSwitching && + Rs2Antiban.getPlayStyle().shouldSwitchProfileBasedOnAttention()) { + Rs2Antiban.setPlayStyle(Rs2Antiban.getPlayStyle().switchProfile()); + Rs2Antiban.getPlayStyle().resetPlayStyle(); + } + } + + @Subscribe + public void onStatChanged(StatChanged statChanged) { + if (!Rs2AntibanSettings.antibanEnabled || + (!Rs2AntibanSettings.dynamicIntensity && !Rs2AntibanSettings.dynamicActivity)) { + return; + } + + final Skill skill = statChanged.getSkill(); + final int exp = statChanged.getXp(); + final Integer previous = skillExp.put(skill, exp); + + if (lastSkillChanged != null && lastSkillChanged.equals(skill)) { + if (Rs2AntibanSettings.contextualVariability && !Rs2AntibanSettings.actionCooldownActive) { + Rs2Antiban.actionCooldown(); + } + return; + } + + lastSkillChanged = skill; + + if (previous == null || previous >= exp) { + return; + } + + updateAntibanSettings(skill); + } + + private void updateAntibanSettings(Skill skill) { + final ActivityIntensity activityIntensity = ActivityIntensity.fromSkill(skill); + final Activity activity = Activity.fromSkill(skill); + + if (activity != null && Rs2AntibanSettings.dynamicActivity) { + Rs2Antiban.setActivity(activity); + Microbot.log("Activity changed, new activity: " + activity); + if (Rs2AntibanSettings.contextualVariability) { + applyContextualVariabilitySetup(); + Rs2Antiban.actionCooldown(); + } + } + + if (activityIntensity != null && Rs2AntibanSettings.dynamicIntensity) { + Rs2Antiban.setActivityIntensity(activityIntensity); + Microbot.log("Activity changed, new activity intensity: " + activityIntensity); + } + } + + private void applyContextualVariabilitySetup() { + Rs2AntibanSettings.antibanEnabled = true; + Rs2AntibanSettings.usePlayStyle = true; + Rs2AntibanSettings.randomIntervals = false; + Rs2AntibanSettings.simulateFatigue = true; + Rs2AntibanSettings.simulateAttentionSpan = true; + Rs2AntibanSettings.behavioralVariability = true; + Rs2AntibanSettings.nonLinearIntervals = true; + Rs2AntibanSettings.profileSwitching = true; + Rs2AntibanSettings.timeOfDayAdjust = false; + Rs2AntibanSettings.simulateMistakes = true; + Rs2AntibanSettings.naturalMouse = true; + Rs2AntibanSettings.contextualVariability = true; + Rs2AntibanSettings.dynamicIntensity = true; + Rs2AntibanSettings.dynamicActivity = true; + Rs2AntibanSettings.devDebug = false; + Rs2AntibanSettings.takeMicroBreaks = true; + Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.microBreakDurationLow = 3; + Rs2AntibanSettings.microBreakDurationHigh = 8; + Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.microBreakChance = 0.05; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPluginPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPluginPanel.java new file mode 100644 index 0000000000..03f44e6ae6 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPluginPanel.java @@ -0,0 +1,300 @@ +package net.runelite.client.plugins.microbot.util.antiban; + +import net.runelite.client.plugins.microbot.Microbot; +import net.runelite.client.ui.PluginPanel; + +import javax.swing.*; +import java.awt.*; +import java.util.Objects; + +public class AntibanPluginPanel extends PluginPanel { + private final JCheckBox isActionCooldownActive = new JCheckBox("Action Cooldown Active"); + private final JCheckBox isMicroBreakActive = new JCheckBox("Micro Break Active"); + private final JCheckBox isEnabled = new JCheckBox("Enabled"); + private final JCheckBox usePlayStyle = new JCheckBox("Use Play Style"); + private final JCheckBox useRandomIntervals = new JCheckBox("Use Random Intervals"); + private final JCheckBox simulateFatigue = new JCheckBox("Simulate Fatigue"); + private final JCheckBox simulateAttentionSpan = new JCheckBox("Simulate Attention Span"); + private final JCheckBox useBehavioralVariability = new JCheckBox("Use Behavioral Variability"); + private final JCheckBox useNonLinearIntervals = new JCheckBox("Use Non-Linear Intervals"); + private final JCheckBox enableProfileSwitching = new JCheckBox("Enable Profile Switching"); + private final JCheckBox adjustForTimeOfDay = new JCheckBox("Adjust For Time Of Day"); + private final JCheckBox simulateMistakes = new JCheckBox("Simulate Mistakes"); + private final JCheckBox useNaturalMouse = new JCheckBox("Use Natural Mouse"); + private final JCheckBox moveMouseOffScreen = new JCheckBox("Move Mouse Off Screen"); + private final JCheckBox useContextualVariability = new JCheckBox("Use Contextual Variability"); + private final JCheckBox dynamicActivityIntensity = new JCheckBox("Dynamic Activity Intensity"); + private final JCheckBox dynamicActivity = new JCheckBox("Dynamic Activity"); + private final JCheckBox devDebug = new JCheckBox("Dev Debug"); + private final JCheckBox takeMicroBreaks = new JCheckBox("Take Micro Breaks"); + private final JCheckBox simulatePlaySchedule = new JCheckBox("Simulate Play Schedule"); + + private final JSlider microBreakDurationLow = new JSlider(1, 10, Rs2AntibanSettings.microBreakDurationLow); + private final JSlider microBreakDurationHigh = new JSlider(1, 30, Rs2AntibanSettings.microBreakDurationHigh); + private final JSlider actionCooldownChance = new JSlider(0, 100, (int) (Rs2AntibanSettings.actionCooldownChance * 100)); + private final JSlider microBreakChance = new JSlider(0, 100, (int) (Rs2AntibanSettings.microBreakChance * 100)); + private final JSlider timeout = new JSlider(0, 60, Rs2Antiban.getTIMEOUT()); + + private final JLabel microBreakDurationLowLabel = new JLabel("Micro Break Duration Low (min): " + Rs2AntibanSettings.microBreakDurationLow); + private final JLabel microBreakDurationHighLabel = new JLabel("Micro Break Duration High (min): " + Rs2AntibanSettings.microBreakDurationHigh); + private final JLabel actionCooldownChanceLabel = new JLabel("Action Cooldown Chance (%): " + (int) (Rs2AntibanSettings.actionCooldownChance * 100)); + private final JLabel microBreakChanceLabel = new JLabel("Micro Break Chance (%): " + (int) (Rs2AntibanSettings.microBreakChance * 100)); + private final JLabel timeoutLabel = new JLabel("Timeout (min): " + Rs2Antiban.getTIMEOUT()); + + // Additional Info Panel + private final JLabel playStyleLabel = new JLabel("Play Style: " + (Rs2Antiban.getPlayStyle() != null ? Rs2Antiban.getPlayStyle().getName() : "null")); + private final JLabel playStyleChangeLabel = new JLabel("Play Style Change: " + (Rs2Antiban.getPlayStyle() != null ? Rs2Antiban.getPlayStyle().getTimeLeftUntilNextSwitch() : "null")); + private final JLabel profileLabel = new JLabel("Category: " + (Rs2Antiban.getCategory() != null ? Rs2Antiban.getCategory().getName() : "null")); + private final JLabel activityLabel = new JLabel("Activity: " + (Rs2Antiban.getActivity() != null ? Rs2Antiban.getActivity().getMethod() : "null")); + private final JLabel activityIntensityLabel = new JLabel("Activity Intensity: " + (Rs2Antiban.getActivityIntensity() != null ? Rs2Antiban.getActivityIntensity().getName() : "null")); + private final JLabel busyLabel = new JLabel("Busy: " + (Rs2Antiban.getCategory() != null ? Rs2Antiban.getCategory().isBusy() : "null")); + + public AntibanPluginPanel() { + setLayout(new BorderLayout()); + + // Create CardLayout and main content panel + CardLayout cardLayout = new CardLayout(); + JPanel cardPanel = new JPanel(cardLayout); + + // Create navigation panel + JPanel navPanel = new JPanel(); + navPanel.setLayout(new BoxLayout(navPanel, BoxLayout.X_AXIS)); + + // Add buttons with icons to navigation panel + addNavButton(navPanel, "General Settings", "general.png", cardPanel, "GeneralSettings"); + addNavButton(navPanel, "Activity Settings", "activity.png", cardPanel, "ActivitySettings"); + addNavButton(navPanel, "Profile Settings", "profile.png", cardPanel, "ProfileSettings"); + addNavButton(navPanel, "Mouse Settings", "mouse.png", cardPanel, "MouseSettings"); + addNavButton(navPanel, "Micro Break Settings", "microbreak.png", cardPanel, "MicroBreakSettings"); + addNavButton(navPanel, "Cooldown Settings", "cooldown.png", cardPanel, "CooldownSettings"); + + // Create and add panels for each card + cardPanel.add(createGeneralSettingsPanel(), "GeneralSettings"); + cardPanel.add(createActivitySettingsPanel(), "ActivitySettings"); + cardPanel.add(createProfileSettingsPanel(), "ProfileSettings"); + cardPanel.add(createMouseSettingsPanel(), "MouseSettings"); + cardPanel.add(createMicroBreakSettingsPanel(), "MicroBreakSettings"); + cardPanel.add(createCooldownSettingsPanel(), "CooldownSettings"); + + // Add navigation and card panels to the main panel + add(navPanel, BorderLayout.NORTH); + add(cardPanel, BorderLayout.CENTER); + + // Add the Info Panel at the bottom + add(createInfoPanel(), BorderLayout.SOUTH); + + setupSliders(); + setupListeners(); + loadSettings(); + } + + private void addNavButton(JPanel navPanel, String tooltip, String iconPath, JPanel cardPanel, String cardName) { + JButton button = new JButton(createIcon(iconPath)); + button.setToolTipText(tooltip); + button.setFocusPainted(false); + button.setBorderPainted(false); + button.setContentAreaFilled(false); + button.addActionListener(e -> { + CardLayout cl = (CardLayout) (cardPanel.getLayout()); + cl.show(cardPanel, cardName); + }); + navPanel.add(button); + } + + private JPanel createGeneralSettingsPanel() { + JPanel panel = createPanel("General Settings"); + addCheckboxesToPanel(panel, isEnabled, devDebug); + return panel; + } + + private JPanel createActivitySettingsPanel() { + JPanel panel = createPanel("Activity Settings"); + addCheckboxesToPanel(panel, usePlayStyle, useRandomIntervals, simulateFatigue, simulateAttentionSpan, useBehavioralVariability, useNonLinearIntervals, dynamicActivityIntensity, dynamicActivity); + return panel; + } + + private JPanel createProfileSettingsPanel() { + JPanel panel = createPanel("Profile Settings"); + addCheckboxesToPanel(panel, enableProfileSwitching, adjustForTimeOfDay, simulatePlaySchedule); + return panel; + } + + private JPanel createMouseSettingsPanel() { + JPanel panel = createPanel("Mouse Settings"); + addCheckboxesToPanel(panel, useNaturalMouse, moveMouseOffScreen, useContextualVariability, simulateMistakes); + return panel; + } + + private JPanel createMicroBreakSettingsPanel() { + JPanel panel = createPanel("Micro Break Settings"); + addCheckboxesToPanel(panel, isMicroBreakActive, takeMicroBreaks); + addSlidersToPanel(panel, microBreakDurationLowLabel, microBreakDurationLow, microBreakDurationHighLabel, microBreakDurationHigh, microBreakChanceLabel, microBreakChance); + return panel; + } + + private JPanel createCooldownSettingsPanel() { + JPanel panel = createPanel("Cooldown and Timeout Settings"); + addCheckboxesToPanel(panel, isActionCooldownActive); + addSlidersToPanel(panel, actionCooldownChanceLabel, actionCooldownChance, timeoutLabel, timeout); + return panel; + } + + private JPanel createInfoPanel() { + JPanel panel = new JPanel(new GridBagLayout()); + panel.setBorder(BorderFactory.createTitledBorder("Additional Info")); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = GridBagConstraints.RELATIVE; + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(5, 5, 5, 5); + + panel.add(playStyleLabel, gbc); + panel.add(playStyleChangeLabel, gbc); + panel.add(profileLabel, gbc); + panel.add(activityLabel, gbc); + panel.add(activityIntensityLabel, gbc); + panel.add(busyLabel, gbc); + + return panel; + } + + private JPanel createPanel(String title) { + JPanel panel = new JPanel(new GridBagLayout()); + panel.setBorder(BorderFactory.createTitledBorder(title)); + return panel; + } + + private void addCheckboxesToPanel(JPanel panel, JCheckBox... checkBoxes) { + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = GridBagConstraints.RELATIVE; + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(5, 5, 5, 5); + for (JCheckBox checkBox : checkBoxes) { + panel.add(checkBox, gbc); + } + } + + private void addSlidersToPanel(JPanel panel, JComponent... labelsAndSliders) { + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = GridBagConstraints.RELATIVE; + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.insets = new Insets(5, 5, 5, 5); + for (JComponent component : labelsAndSliders) { + panel.add(component, gbc); + } + } + + private void setupSliders() { + setupSlider(microBreakDurationLow, 1, 5, 1); + setupSlider(microBreakDurationHigh, 5, 15, 1); + setupSlider(actionCooldownChance, 20, 100, 10); + setupSlider(microBreakChance, 20, 100, 10); + setupSlider(timeout, 10, 60, 5); + } + + private void setupSlider(JSlider slider, int majorTickSpacing, int max, int minorTickSpacing) { + slider.setMajorTickSpacing(majorTickSpacing); + slider.setMinorTickSpacing(minorTickSpacing); + slider.setPaintTicks(true); + slider.setPaintLabels(true); + slider.setMinimum(0); + slider.setMaximum(max); + } + + private void setupListeners() { + isActionCooldownActive.addActionListener(e -> Rs2AntibanSettings.actionCooldownActive = isActionCooldownActive.isSelected()); + isMicroBreakActive.addActionListener(e -> Rs2AntibanSettings.microBreakActive = isMicroBreakActive.isSelected()); + isEnabled.addActionListener(e -> Rs2AntibanSettings.antibanEnabled = isEnabled.isSelected()); + usePlayStyle.addActionListener(e -> Rs2AntibanSettings.usePlayStyle = usePlayStyle.isSelected()); + useRandomIntervals.addActionListener(e -> Rs2AntibanSettings.randomIntervals = useRandomIntervals.isSelected()); + simulateFatigue.addActionListener(e -> Rs2AntibanSettings.simulateFatigue = simulateFatigue.isSelected()); + simulateAttentionSpan.addActionListener(e -> Rs2AntibanSettings.simulateAttentionSpan = simulateAttentionSpan.isSelected()); + useBehavioralVariability.addActionListener(e -> Rs2AntibanSettings.behavioralVariability = useBehavioralVariability.isSelected()); + useNonLinearIntervals.addActionListener(e -> Rs2AntibanSettings.nonLinearIntervals = useNonLinearIntervals.isSelected()); + enableProfileSwitching.addActionListener(e -> Rs2AntibanSettings.profileSwitching = enableProfileSwitching.isSelected()); + adjustForTimeOfDay.addActionListener(e -> Rs2AntibanSettings.timeOfDayAdjust = adjustForTimeOfDay.isSelected()); + simulateMistakes.addActionListener(e -> Rs2AntibanSettings.simulateMistakes = simulateMistakes.isSelected()); + useNaturalMouse.addActionListener(e -> Rs2AntibanSettings.naturalMouse = useNaturalMouse.isSelected()); + moveMouseOffScreen.addActionListener(e -> Rs2AntibanSettings.moveMouseOffScreen = moveMouseOffScreen.isSelected()); + useContextualVariability.addActionListener(e -> Rs2AntibanSettings.contextualVariability = useContextualVariability.isSelected()); + dynamicActivityIntensity.addActionListener(e -> Rs2AntibanSettings.dynamicIntensity = dynamicActivityIntensity.isSelected()); + dynamicActivity.addActionListener(e -> Rs2AntibanSettings.dynamicActivity = dynamicActivity.isSelected()); + devDebug.addActionListener(e -> Rs2AntibanSettings.devDebug = devDebug.isSelected()); + takeMicroBreaks.addActionListener(e -> Rs2AntibanSettings.takeMicroBreaks = takeMicroBreaks.isSelected()); + simulatePlaySchedule.addActionListener(e -> Rs2AntibanSettings.playSchedule = simulatePlaySchedule.isSelected()); + + microBreakDurationLow.addChangeListener(e -> { + Rs2AntibanSettings.microBreakDurationLow = microBreakDurationLow.getValue(); + microBreakDurationLowLabel.setText("Micro Break Duration Low (min): " + microBreakDurationLow.getValue()); + }); + microBreakDurationHigh.addChangeListener(e -> { + Rs2AntibanSettings.microBreakDurationHigh = microBreakDurationHigh.getValue(); + microBreakDurationHighLabel.setText("Micro Break Duration High (min): " + microBreakDurationHigh.getValue()); + }); + actionCooldownChance.addChangeListener(e -> { + Rs2AntibanSettings.actionCooldownChance = actionCooldownChance.getValue() / 100.0; + actionCooldownChanceLabel.setText("Action Cooldown Chance (%): " + actionCooldownChance.getValue()); + }); + microBreakChance.addChangeListener(e -> { + Rs2AntibanSettings.microBreakChance = microBreakChance.getValue() / 100.0; + microBreakChanceLabel.setText("Micro Break Chance (%): " + microBreakChance.getValue()); + }); + timeout.addChangeListener(e -> { + Rs2Antiban.setTIMEOUT(timeout.getValue()); + timeoutLabel.setText("Timeout (min): " + timeout.getValue()); + }); + } + + public void loadSettings() { + isActionCooldownActive.setSelected(Rs2AntibanSettings.actionCooldownActive); + isMicroBreakActive.setSelected(Rs2AntibanSettings.microBreakActive); + isEnabled.setSelected(Rs2AntibanSettings.antibanEnabled); + usePlayStyle.setSelected(Rs2AntibanSettings.usePlayStyle); + useRandomIntervals.setSelected(Rs2AntibanSettings.randomIntervals); + simulateFatigue.setSelected(Rs2AntibanSettings.simulateFatigue); + simulateAttentionSpan.setSelected(Rs2AntibanSettings.simulateAttentionSpan); + useBehavioralVariability.setSelected(Rs2AntibanSettings.behavioralVariability); + useNonLinearIntervals.setSelected(Rs2AntibanSettings.nonLinearIntervals); + enableProfileSwitching.setSelected(Rs2AntibanSettings.profileSwitching); + adjustForTimeOfDay.setSelected(Rs2AntibanSettings.timeOfDayAdjust); + simulateMistakes.setSelected(Rs2AntibanSettings.simulateMistakes); + useNaturalMouse.setSelected(Rs2AntibanSettings.naturalMouse); + moveMouseOffScreen.setSelected(Rs2AntibanSettings.moveMouseOffScreen); + useContextualVariability.setSelected(Rs2AntibanSettings.contextualVariability); + dynamicActivityIntensity.setSelected(Rs2AntibanSettings.dynamicIntensity); + dynamicActivity.setSelected(Rs2AntibanSettings.dynamicActivity); + devDebug.setSelected(Rs2AntibanSettings.devDebug); + takeMicroBreaks.setSelected(Rs2AntibanSettings.takeMicroBreaks); + simulatePlaySchedule.setSelected(Rs2AntibanSettings.playSchedule); + + microBreakDurationLow.setValue(Rs2AntibanSettings.microBreakDurationLow); + microBreakDurationHigh.setValue(Rs2AntibanSettings.microBreakDurationHigh); + actionCooldownChance.setValue((int) (Rs2AntibanSettings.actionCooldownChance * 100)); + microBreakChance.setValue((int) (Rs2AntibanSettings.microBreakChance * 100)); + timeout.setValue(Rs2Antiban.getTIMEOUT()); + + microBreakDurationLowLabel.setText("Micro Break Duration Low (min): " + Rs2AntibanSettings.microBreakDurationLow); + microBreakDurationHighLabel.setText("Micro Break Duration High (min): " + Rs2AntibanSettings.microBreakDurationHigh); + actionCooldownChanceLabel.setText("Action Cooldown Chance (%): " + (int) (Rs2AntibanSettings.actionCooldownChance * 100)); + microBreakChanceLabel.setText("Micro Break Chance (%): " + (int) (Rs2AntibanSettings.microBreakChance * 100)); + timeoutLabel.setText("Timeout (min): " + Rs2Antiban.getTIMEOUT()); + + if (!Microbot.isLoggedIn()) + return; + + playStyleLabel.setText("Play Style: " + (Rs2Antiban.getPlayStyle() != null ? Rs2Antiban.getPlayStyle().getName() : "null")); + playStyleChangeLabel.setText("Play Style Change: " + (Rs2Antiban.getPlayStyle() != null ? Rs2Antiban.getPlayStyle().getTimeLeftUntilNextSwitch() : "null")); + profileLabel.setText("Category: " + (Rs2Antiban.getCategory() != null ? Rs2Antiban.getCategory().getName() : "null")); + activityLabel.setText("Activity: " + (Rs2Antiban.getActivity() != null ? Rs2Antiban.getActivity().getMethod() : "null")); + activityIntensityLabel.setText("Activity Intensity: " + (Rs2Antiban.getActivityIntensity() != null ? Rs2Antiban.getActivityIntensity().getName() : "null")); + busyLabel.setText("Busy: " + (Rs2Antiban.getCategory() != null ? Rs2Antiban.getCategory().isBusy() : "null")); + } + + private Icon createIcon(String path) { + return new ImageIcon(Objects.requireNonNull(getClass().getResource(path))); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanSetupTemplates.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanSetupTemplates.java new file mode 100644 index 0000000000..96df7d0017 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanSetupTemplates.java @@ -0,0 +1,529 @@ +package net.runelite.client.plugins.microbot.util.antiban; + +import net.runelite.client.plugins.microbot.util.antiban.enums.Activity; + +/** + * The {@code AntibanSetupTemplates} class provides a set of methods to apply specific antiban + * configurations tailored to different activities within the game. These configurations adjust various + * settings such as mouse movement, random intervals, and fatigue simulation to mimic human behavior and + * reduce the likelihood of detection by anti-cheat systems. + */ +public class AntibanSetupTemplates { + /** + * Applies the antiban setup tailored for general combat activities. + * This setup enables various human-like behaviors such as fatigue simulation, attention span, + * and mouse movement variability to reduce detection risk. + */ + public void applyCombatSetup() { + Rs2AntibanSettings.antibanEnabled = true; + Rs2AntibanSettings.usePlayStyle = true; + Rs2AntibanSettings.randomIntervals = false; + Rs2AntibanSettings.simulateFatigue = true; + Rs2AntibanSettings.simulateAttentionSpan = true; + Rs2AntibanSettings.behavioralVariability = true; + Rs2AntibanSettings.nonLinearIntervals = true; + Rs2AntibanSettings.profileSwitching = true; + Rs2AntibanSettings.timeOfDayAdjust = false; + Rs2AntibanSettings.simulateMistakes = false; + Rs2AntibanSettings.naturalMouse = true; + Rs2AntibanSettings.contextualVariability = true; + Rs2AntibanSettings.dynamicIntensity = false; + Rs2AntibanSettings.dynamicActivity = false; + Rs2AntibanSettings.devDebug = false; + Rs2AntibanSettings.takeMicroBreaks = true; + Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.microBreakDurationLow = 3; + Rs2AntibanSettings.microBreakDurationHigh = 8; + Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.microBreakChance = 0.05; + Rs2Antiban.setActivity(Activity.GENERAL_COMBAT); + } + + /** + * Applies the antiban setup tailored for runecrafting activities. + * This setup adjusts settings to simulate human-like behaviors during runecrafting tasks. + */ + public void applyRunecraftingSetup() { + // Implementation for Runecrafting setup + Rs2AntibanSettings.antibanEnabled = true; + Rs2AntibanSettings.usePlayStyle = true; + Rs2AntibanSettings.randomIntervals = false; + Rs2AntibanSettings.simulateFatigue = true; + Rs2AntibanSettings.simulateAttentionSpan = true; + Rs2AntibanSettings.behavioralVariability = true; + Rs2AntibanSettings.nonLinearIntervals = true; + Rs2AntibanSettings.profileSwitching = true; + Rs2AntibanSettings.timeOfDayAdjust = false; + Rs2AntibanSettings.simulateMistakes = false; + Rs2AntibanSettings.naturalMouse = true; + Rs2AntibanSettings.contextualVariability = true; + Rs2AntibanSettings.dynamicIntensity = false; + Rs2AntibanSettings.dynamicActivity = false; + Rs2AntibanSettings.devDebug = false; + Rs2AntibanSettings.takeMicroBreaks = true; + Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.microBreakDurationLow = 3; + Rs2AntibanSettings.microBreakDurationHigh = 8; + Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.microBreakChance = 0.05; + Rs2Antiban.setActivity(Activity.GENERAL_RUNECRAFT); + } + + /** + * Applies the antiban setup tailored for construction activities. + * This setup focuses on mimicking human-like behaviors during construction tasks. + */ + public void applyConstructionSetup() { + // Implementation for Construction setup + Rs2AntibanSettings.antibanEnabled = true; + Rs2AntibanSettings.usePlayStyle = true; + Rs2AntibanSettings.randomIntervals = false; + Rs2AntibanSettings.simulateFatigue = true; + Rs2AntibanSettings.simulateAttentionSpan = true; + Rs2AntibanSettings.behavioralVariability = true; + Rs2AntibanSettings.nonLinearIntervals = true; + Rs2AntibanSettings.profileSwitching = true; + Rs2AntibanSettings.timeOfDayAdjust = false; + Rs2AntibanSettings.simulateMistakes = false; + Rs2AntibanSettings.naturalMouse = true; + Rs2AntibanSettings.contextualVariability = true; + Rs2AntibanSettings.dynamicIntensity = false; + Rs2AntibanSettings.dynamicActivity = false; + Rs2AntibanSettings.devDebug = false; + Rs2AntibanSettings.takeMicroBreaks = true; + Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.microBreakDurationLow = 3; + Rs2AntibanSettings.microBreakDurationHigh = 8; + Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.microBreakChance = 0.05; + Rs2Antiban.setActivity(Activity.GENERAL_CONSTRUCTION); + } + + /** + * Applies the antiban setup tailored for agility activities. + * This setup includes adjustments to simulate human-like behaviors during agility tasks. + */ + + public void applyAgilitySetup() { + // Implementation for Agility setup + Rs2AntibanSettings.antibanEnabled = true; + Rs2AntibanSettings.usePlayStyle = true; + Rs2AntibanSettings.randomIntervals = false; + Rs2AntibanSettings.simulateFatigue = true; + Rs2AntibanSettings.simulateAttentionSpan = true; + Rs2AntibanSettings.behavioralVariability = true; + Rs2AntibanSettings.nonLinearIntervals = true; + Rs2AntibanSettings.profileSwitching = true; + Rs2AntibanSettings.timeOfDayAdjust = false; + Rs2AntibanSettings.simulateMistakes = false; + Rs2AntibanSettings.naturalMouse = true; + Rs2AntibanSettings.contextualVariability = true; + Rs2AntibanSettings.dynamicIntensity = false; + Rs2AntibanSettings.dynamicActivity = false; + Rs2AntibanSettings.devDebug = false; + Rs2AntibanSettings.takeMicroBreaks = true; + Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.microBreakDurationLow = 3; + Rs2AntibanSettings.microBreakDurationHigh = 8; + Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.microBreakChance = 0.05; + Rs2Antiban.setActivity(Activity.GENERAL_AGILITY); + } + + /** + * Applies the antiban setup tailored for herblore activities. + * This setup configures settings to mimic human-like behaviors during herblore tasks. + */ + public void applyHerbloreSetup() { + // Implementation for Herblore setup + Rs2AntibanSettings.antibanEnabled = true; + Rs2AntibanSettings.usePlayStyle = true; + Rs2AntibanSettings.randomIntervals = false; + Rs2AntibanSettings.simulateFatigue = true; + Rs2AntibanSettings.simulateAttentionSpan = true; + Rs2AntibanSettings.behavioralVariability = true; + Rs2AntibanSettings.nonLinearIntervals = true; + Rs2AntibanSettings.profileSwitching = true; + Rs2AntibanSettings.timeOfDayAdjust = false; + Rs2AntibanSettings.simulateMistakes = false; + Rs2AntibanSettings.naturalMouse = true; + Rs2AntibanSettings.contextualVariability = true; + Rs2AntibanSettings.dynamicIntensity = false; + Rs2AntibanSettings.dynamicActivity = false; + Rs2AntibanSettings.devDebug = false; + Rs2AntibanSettings.takeMicroBreaks = true; + Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.microBreakDurationLow = 3; + Rs2AntibanSettings.microBreakDurationHigh = 8; + Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.microBreakChance = 0.05; + Rs2Antiban.setActivity(Activity.GENERAL_HERBLORE); + } + + /** + * Applies the antiban setup tailored for thieving activities. + * This setup simulates human-like behaviors during thieving tasks to reduce detection risk. + */ + public void applyThievingSetup() { + // Implementation for Thieving setup + Rs2AntibanSettings.antibanEnabled = true; + Rs2AntibanSettings.usePlayStyle = true; + Rs2AntibanSettings.randomIntervals = false; + Rs2AntibanSettings.simulateFatigue = true; + Rs2AntibanSettings.simulateAttentionSpan = true; + Rs2AntibanSettings.behavioralVariability = true; + Rs2AntibanSettings.nonLinearIntervals = true; + Rs2AntibanSettings.profileSwitching = true; + Rs2AntibanSettings.timeOfDayAdjust = false; + Rs2AntibanSettings.simulateMistakes = false; + Rs2AntibanSettings.naturalMouse = true; + Rs2AntibanSettings.contextualVariability = true; + Rs2AntibanSettings.dynamicIntensity = false; + Rs2AntibanSettings.dynamicActivity = false; + Rs2AntibanSettings.devDebug = false; + Rs2AntibanSettings.takeMicroBreaks = true; + Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.microBreakDurationLow = 3; + Rs2AntibanSettings.microBreakDurationHigh = 8; + Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.microBreakChance = 0.05; + Rs2Antiban.setActivity(Activity.GENERAL_THIEVING); + } + + /** + * Applies the antiban setup tailored for crafting activities. + * This setup focuses on human-like behavior simulation during crafting tasks. + */ + public void applyCraftingSetup() { + // Implementation for Crafting setup + Rs2AntibanSettings.antibanEnabled = true; + Rs2AntibanSettings.usePlayStyle = true; + Rs2AntibanSettings.randomIntervals = false; + Rs2AntibanSettings.simulateFatigue = true; + Rs2AntibanSettings.simulateAttentionSpan = true; + Rs2AntibanSettings.behavioralVariability = true; + Rs2AntibanSettings.nonLinearIntervals = true; + Rs2AntibanSettings.profileSwitching = true; + Rs2AntibanSettings.timeOfDayAdjust = false; + Rs2AntibanSettings.simulateMistakes = false; + Rs2AntibanSettings.naturalMouse = true; + Rs2AntibanSettings.contextualVariability = true; + Rs2AntibanSettings.dynamicIntensity = false; + Rs2AntibanSettings.dynamicActivity = false; + Rs2AntibanSettings.devDebug = false; + Rs2AntibanSettings.takeMicroBreaks = true; + Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.microBreakDurationLow = 3; + Rs2AntibanSettings.microBreakDurationHigh = 8; + Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.microBreakChance = 0.05; + Rs2Antiban.setActivity(Activity.GENERAL_CRAFTING); + } + + /** + * Applies the antiban setup tailored for fletching activities. + * This setup adjusts settings to mimic human behavior during fletching tasks. + */ + public void applyFletchingSetup() { + // Implementation for Fletching setup + Rs2AntibanSettings.antibanEnabled = true; + Rs2AntibanSettings.usePlayStyle = true; + Rs2AntibanSettings.randomIntervals = false; + Rs2AntibanSettings.simulateFatigue = true; + Rs2AntibanSettings.simulateAttentionSpan = true; + Rs2AntibanSettings.behavioralVariability = true; + Rs2AntibanSettings.nonLinearIntervals = true; + Rs2AntibanSettings.profileSwitching = true; + Rs2AntibanSettings.timeOfDayAdjust = false; + Rs2AntibanSettings.simulateMistakes = false; + Rs2AntibanSettings.naturalMouse = true; + Rs2AntibanSettings.contextualVariability = true; + Rs2AntibanSettings.dynamicIntensity = false; + Rs2AntibanSettings.dynamicActivity = false; + Rs2AntibanSettings.devDebug = false; + Rs2AntibanSettings.takeMicroBreaks = true; + Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.microBreakDurationLow = 3; + Rs2AntibanSettings.microBreakDurationHigh = 8; + Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.microBreakChance = 0.05; + Rs2Antiban.setActivity(Activity.GENERAL_FLETCHING); + } + + public void applySlayerSetup() { + // Implementation for Slayer setup + } + + /** + * Applies the antiban setup tailored for hunter activities. + * This setup simulates human-like behaviors during hunting tasks. + */ + public void applyHunterSetup() { + // Implementation for Hunter setup + Rs2AntibanSettings.antibanEnabled = true; + Rs2AntibanSettings.usePlayStyle = true; + Rs2AntibanSettings.randomIntervals = false; + Rs2AntibanSettings.simulateFatigue = true; + Rs2AntibanSettings.simulateAttentionSpan = true; + Rs2AntibanSettings.behavioralVariability = true; + Rs2AntibanSettings.nonLinearIntervals = true; + Rs2AntibanSettings.profileSwitching = true; + Rs2AntibanSettings.timeOfDayAdjust = false; + Rs2AntibanSettings.simulateMistakes = false; + Rs2AntibanSettings.naturalMouse = true; + Rs2AntibanSettings.contextualVariability = true; + Rs2AntibanSettings.dynamicIntensity = false; + Rs2AntibanSettings.dynamicActivity = false; + Rs2AntibanSettings.devDebug = false; + Rs2AntibanSettings.takeMicroBreaks = true; + Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.microBreakDurationLow = 3; + Rs2AntibanSettings.microBreakDurationHigh = 8; + Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.microBreakChance = 0.05; + Rs2Antiban.setActivity(Activity.GENERAL_HUNTER); + } + + /** + * Applies the antiban setup tailored for mining activities. + * This setup includes adjustments to mimic human behaviors during mining tasks. + */ + public void applyMiningSetup() { + // Implementation for Mining setup + Rs2AntibanSettings.antibanEnabled = true; + Rs2AntibanSettings.usePlayStyle = true; + Rs2AntibanSettings.randomIntervals = false; + Rs2AntibanSettings.simulateFatigue = true; + Rs2AntibanSettings.simulateAttentionSpan = true; + Rs2AntibanSettings.behavioralVariability = true; + Rs2AntibanSettings.nonLinearIntervals = true; + Rs2AntibanSettings.profileSwitching = true; + Rs2AntibanSettings.timeOfDayAdjust = false; + Rs2AntibanSettings.simulateMistakes = true; + Rs2AntibanSettings.naturalMouse = true; + Rs2AntibanSettings.contextualVariability = false; + Rs2AntibanSettings.dynamicIntensity = false; + Rs2AntibanSettings.dynamicActivity = false; + Rs2AntibanSettings.devDebug = false; + Rs2AntibanSettings.takeMicroBreaks = true; + Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.microBreakDurationLow = 1; + Rs2AntibanSettings.microBreakDurationHigh = 4; + Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.microBreakChance = 0.05; + Rs2Antiban.setActivity(Activity.GENERAL_MINING); + } + + /** + * Applies the antiban setup tailored for smithing activities. + * This setup configures settings to simulate human-like behaviors during smithing tasks. + */ + public void applySmithingSetup() { + // Implementation for Smithing setup + Rs2AntibanSettings.antibanEnabled = true; + Rs2AntibanSettings.usePlayStyle = true; + Rs2AntibanSettings.randomIntervals = false; + Rs2AntibanSettings.simulateFatigue = true; + Rs2AntibanSettings.simulateAttentionSpan = true; + Rs2AntibanSettings.behavioralVariability = true; + Rs2AntibanSettings.nonLinearIntervals = true; + Rs2AntibanSettings.profileSwitching = true; + Rs2AntibanSettings.timeOfDayAdjust = false; + Rs2AntibanSettings.simulateMistakes = false; + Rs2AntibanSettings.naturalMouse = true; + Rs2AntibanSettings.contextualVariability = true; + Rs2AntibanSettings.dynamicIntensity = false; + Rs2AntibanSettings.dynamicActivity = false; + Rs2AntibanSettings.devDebug = false; + Rs2AntibanSettings.takeMicroBreaks = true; + Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.microBreakDurationLow = 3; + Rs2AntibanSettings.microBreakDurationHigh = 8; + Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.microBreakChance = 0.05; + Rs2Antiban.setActivity(Activity.GENERAL_SMITHING); + } + + /** + * Applies the antiban setup tailored for fishing activities. + * This setup focuses on mimicking human-like behaviors during fishing tasks. + */ + public void applyFishingSetup() { + // Implementation for Fishing setup + Rs2AntibanSettings.antibanEnabled = true; + Rs2AntibanSettings.usePlayStyle = true; + Rs2AntibanSettings.randomIntervals = false; + Rs2AntibanSettings.simulateFatigue = true; + Rs2AntibanSettings.simulateAttentionSpan = true; + Rs2AntibanSettings.behavioralVariability = true; + Rs2AntibanSettings.nonLinearIntervals = true; + Rs2AntibanSettings.profileSwitching = true; + Rs2AntibanSettings.timeOfDayAdjust = false; + Rs2AntibanSettings.simulateMistakes = false; + Rs2AntibanSettings.naturalMouse = true; + Rs2AntibanSettings.contextualVariability = true; + Rs2AntibanSettings.dynamicIntensity = false; + Rs2AntibanSettings.dynamicActivity = false; + Rs2AntibanSettings.devDebug = false; + Rs2AntibanSettings.takeMicroBreaks = true; + Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.microBreakDurationLow = 3; + Rs2AntibanSettings.microBreakDurationHigh = 8; + Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.microBreakChance = 0.05; + Rs2Antiban.setActivity(Activity.GENERAL_FISHING); + } + + /** + * Applies the antiban setup tailored for cooking activities. + * This setup simulates human-like behaviors during cooking tasks to reduce detection risk. + */ + public void applyCookingSetup() { + // Implementation for Cooking setup + Rs2AntibanSettings.antibanEnabled = true; + Rs2AntibanSettings.usePlayStyle = true; + Rs2AntibanSettings.randomIntervals = false; + Rs2AntibanSettings.simulateFatigue = true; + Rs2AntibanSettings.simulateAttentionSpan = true; + Rs2AntibanSettings.behavioralVariability = true; + Rs2AntibanSettings.nonLinearIntervals = true; + Rs2AntibanSettings.profileSwitching = true; + Rs2AntibanSettings.timeOfDayAdjust = false; + Rs2AntibanSettings.simulateMistakes = false; + Rs2AntibanSettings.naturalMouse = true; + Rs2AntibanSettings.contextualVariability = true; + Rs2AntibanSettings.dynamicIntensity = false; + Rs2AntibanSettings.dynamicActivity = false; + Rs2AntibanSettings.devDebug = false; + Rs2AntibanSettings.takeMicroBreaks = true; + Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.microBreakDurationLow = 3; + Rs2AntibanSettings.microBreakDurationHigh = 8; + Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.microBreakChance = 0.05; + Rs2Antiban.setActivity(Activity.GENERAL_COOKING); + } + + /** + * Applies the antiban setup tailored for firemaking activities. + * This setup is designed to simulate human behavior during firemaking tasks. + */ + public void applyFiremakingSetup() { + // Implementation for Firemaking setup + Rs2AntibanSettings.antibanEnabled = true; + Rs2AntibanSettings.usePlayStyle = true; + Rs2AntibanSettings.randomIntervals = false; + Rs2AntibanSettings.simulateFatigue = true; + Rs2AntibanSettings.simulateAttentionSpan = true; + Rs2AntibanSettings.behavioralVariability = true; + Rs2AntibanSettings.nonLinearIntervals = true; + Rs2AntibanSettings.profileSwitching = true; + Rs2AntibanSettings.timeOfDayAdjust = false; + Rs2AntibanSettings.simulateMistakes = false; + Rs2AntibanSettings.naturalMouse = true; + Rs2AntibanSettings.contextualVariability = true; + Rs2AntibanSettings.dynamicIntensity = false; + Rs2AntibanSettings.dynamicActivity = false; + Rs2AntibanSettings.devDebug = false; + Rs2AntibanSettings.takeMicroBreaks = true; + Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.microBreakDurationLow = 3; + Rs2AntibanSettings.microBreakDurationHigh = 8; + Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.microBreakChance = 0.05; + Rs2Antiban.setActivity(Activity.GENERAL_FIREMAKING); + } + + /** + * Applies the antiban setup tailored for woodcutting activities. + * This setup mimics human-like behaviors during woodcutting tasks to reduce detection risk. + */ + public void applyWoodcuttingSetup() { + // Implementation for Woodcutting setup + Rs2AntibanSettings.antibanEnabled = true; + Rs2AntibanSettings.usePlayStyle = true; + Rs2AntibanSettings.randomIntervals = false; + Rs2AntibanSettings.simulateFatigue = true; + Rs2AntibanSettings.simulateAttentionSpan = true; + Rs2AntibanSettings.behavioralVariability = true; + Rs2AntibanSettings.nonLinearIntervals = true; + Rs2AntibanSettings.profileSwitching = true; + Rs2AntibanSettings.timeOfDayAdjust = false; + Rs2AntibanSettings.simulateMistakes = false; + Rs2AntibanSettings.naturalMouse = true; + Rs2AntibanSettings.contextualVariability = true; + Rs2AntibanSettings.dynamicIntensity = false; + Rs2AntibanSettings.dynamicActivity = false; + Rs2AntibanSettings.devDebug = false; + Rs2AntibanSettings.takeMicroBreaks = true; + Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.microBreakDurationLow = 3; + Rs2AntibanSettings.microBreakDurationHigh = 8; + Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.microBreakChance = 0.05; + Rs2Antiban.setActivity(Activity.GENERAL_WOODCUTTING); + } + + /** + * Applies the antiban setup tailored for farming activities. + * This setup configures settings to simulate human-like behaviors during farming tasks. + */ + public void applyFarmingSetup() { + // Implementation for Farming setup + Rs2AntibanSettings.antibanEnabled = true; + Rs2AntibanSettings.usePlayStyle = true; + Rs2AntibanSettings.randomIntervals = false; + Rs2AntibanSettings.simulateFatigue = true; + Rs2AntibanSettings.simulateAttentionSpan = true; + Rs2AntibanSettings.behavioralVariability = true; + Rs2AntibanSettings.nonLinearIntervals = true; + Rs2AntibanSettings.profileSwitching = true; + Rs2AntibanSettings.timeOfDayAdjust = false; + Rs2AntibanSettings.simulateMistakes = false; + Rs2AntibanSettings.naturalMouse = true; + Rs2AntibanSettings.contextualVariability = false; + Rs2AntibanSettings.dynamicIntensity = false; + Rs2AntibanSettings.dynamicActivity = false; + Rs2AntibanSettings.devDebug = false; + Rs2AntibanSettings.takeMicroBreaks = true; + Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.microBreakDurationLow = 3; + Rs2AntibanSettings.microBreakDurationHigh = 8; + Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.microBreakChance = 0.05; + Rs2Antiban.setActivity(Activity.GENERAL_FARMING); + } + + /** + * Applies the basic antiban setup. + * This setup configures settings to simulate human-like mouse movement and reduce detection risk. + * This setup does not include advanced features such as action cooldown, attention span or micro breaks. + */ + public void applyGeneralBasicSetup() { + // Implementation for General Basic setup + Rs2AntibanSettings.antibanEnabled = true; + Rs2AntibanSettings.usePlayStyle = false; + Rs2AntibanSettings.randomIntervals = false; + Rs2AntibanSettings.simulateFatigue = true; + Rs2AntibanSettings.simulateAttentionSpan = false; + Rs2AntibanSettings.behavioralVariability = false; + Rs2AntibanSettings.nonLinearIntervals = false; + Rs2AntibanSettings.profileSwitching = false; + Rs2AntibanSettings.timeOfDayAdjust = false; + Rs2AntibanSettings.simulateMistakes = true; + Rs2AntibanSettings.naturalMouse = true; + Rs2AntibanSettings.contextualVariability = false; + Rs2AntibanSettings.dynamicIntensity = true; + Rs2AntibanSettings.dynamicActivity = true; + Rs2AntibanSettings.devDebug = false; + Rs2AntibanSettings.takeMicroBreaks = false; + Rs2AntibanSettings.playSchedule = false; + Rs2AntibanSettings.microBreakDurationLow = 3; + Rs2AntibanSettings.microBreakDurationHigh = 8; + Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.microBreakChance = 0.05; + } + + +} + diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/MouseFatigue.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/MouseFatigue.java new file mode 100644 index 0000000000..aee1e896d0 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/MouseFatigue.java @@ -0,0 +1,20 @@ +package net.runelite.client.plugins.microbot.util.antiban; + +import java.util.Random; + +public class MouseFatigue { + + + private final Random random = new Random(); + public double increaseRate = 0.005; + public double noiseAmplitude = 5.0; + + // Method to calculate the base time with noise + public int calculateBaseTimeWithNoise(int initialBaseTimeMs, int maxBaseTimeMs) { + double noise = random.nextGaussian() * noiseAmplitude; + int newBaseTimeMs = (int) (initialBaseTimeMs + AntibanPlugin.ticksSinceLogin * increaseRate + noise); + return Math.min(newBaseTimeMs, maxBaseTimeMs); + } + + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2Antiban.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2Antiban.java new file mode 100644 index 0000000000..495e62314d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2Antiban.java @@ -0,0 +1,434 @@ +package net.runelite.client.plugins.microbot.util.antiban; + +import com.google.common.collect.ImmutableSet; +import lombok.Getter; +import lombok.Setter; +import net.runelite.api.ChatMessageType; +import net.runelite.api.events.ChatMessage; +import net.runelite.client.plugins.microbot.Microbot; +import net.runelite.client.plugins.microbot.breakhandler.BreakHandlerScript; +import net.runelite.client.plugins.microbot.util.antiban.enums.Activity; +import net.runelite.client.plugins.microbot.util.antiban.enums.ActivityIntensity; +import net.runelite.client.plugins.microbot.util.antiban.enums.Category; +import net.runelite.client.plugins.microbot.util.antiban.enums.PlayStyle; +import net.runelite.client.plugins.microbot.util.math.Random; +import net.runelite.client.plugins.microbot.util.player.Rs2Player; +import net.runelite.client.ui.overlay.components.*; +import net.runelite.client.util.ColorUtil; + +import java.awt.*; +import java.util.Set; + +import static net.runelite.api.AnimationID.*; + +@Getter +@Setter +public class Rs2Antiban { + public static final ImmutableSet MINING_ANIMATION_IDS = ImmutableSet.of( + MINING_BRONZE_PICKAXE, MINING_MOTHERLODE_BRONZE, MINING_CRASHEDSTAR_BRONZE, + MINING_IRON_PICKAXE, MINING_MOTHERLODE_IRON, MINING_CRASHEDSTAR_IRON, + MINING_STEEL_PICKAXE, MINING_MOTHERLODE_STEEL, MINING_CRASHEDSTAR_STEEL, + MINING_BLACK_PICKAXE, MINING_MOTHERLODE_BLACK, MINING_CRASHEDSTAR_BLACK, + MINING_MITHRIL_PICKAXE, MINING_MOTHERLODE_MITHRIL, MINING_CRASHEDSTAR_MITHRIL, + MINING_ADAMANT_PICKAXE, MINING_MOTHERLODE_ADAMANT, MINING_CRASHEDSTAR_ADAMANT, + MINING_RUNE_PICKAXE, MINING_MOTHERLODE_RUNE, MINING_CRASHEDSTAR_RUNE, + MINING_GILDED_PICKAXE, MINING_MOTHERLODE_GILDED, MINING_CRASHEDSTAR_GILDED, + MINING_DRAGON_PICKAXE, MINING_MOTHERLODE_DRAGON, MINING_CRASHEDSTAR_DRAGON, + MINING_DRAGON_PICKAXE_OR, MINING_MOTHERLODE_DRAGON_OR, MINING_CRASHEDSTAR_DRAGON_OR, + MINING_DRAGON_PICKAXE_OR_TRAILBLAZER, MINING_MOTHERLODE_DRAGON_OR_TRAILBLAZER, MINING_CRASHEDSTAR_DRAGON_OR_TRAILBLAZER, + MINING_DRAGON_PICKAXE_UPGRADED, MINING_MOTHERLODE_DRAGON_UPGRADED, MINING_CRASHEDSTAR_DRAGON_UPGRADED, + MINING_INFERNAL_PICKAXE, MINING_MOTHERLODE_INFERNAL, MINING_CRASHEDSTAR_INFERNAL, + MINING_3A_PICKAXE, MINING_MOTHERLODE_3A, MINING_CRASHEDSTAR_3A, + MINING_CRYSTAL_PICKAXE, MINING_MOTHERLODE_CRYSTAL, MINING_CRASHEDSTAR_CRYSTAL, + MINING_TRAILBLAZER_PICKAXE, MINING_TRAILBLAZER_PICKAXE_2, MINING_TRAILBLAZER_PICKAXE_3, MINING_MOTHERLODE_TRAILBLAZER + ); + private static final Set WOODCUTTING_ANIMS = ImmutableSet.of( + WOODCUTTING_BRONZE, WOODCUTTING_IRON, WOODCUTTING_STEEL, WOODCUTTING_BLACK, WOODCUTTING_MITHRIL, + WOODCUTTING_ADAMANT, WOODCUTTING_RUNE, WOODCUTTING_GILDED, WOODCUTTING_DRAGON, WOODCUTTING_DRAGON_OR, + WOODCUTTING_INFERNAL, WOODCUTTING_3A_AXE, WOODCUTTING_CRYSTAL, WOODCUTTING_TRAILBLAZER, + WOODCUTTING_2H_BRONZE, WOODCUTTING_2H_IRON, WOODCUTTING_2H_STEEL, WOODCUTTING_2H_BLACK, + WOODCUTTING_2H_MITHRIL, WOODCUTTING_2H_ADAMANT, WOODCUTTING_2H_RUNE, WOODCUTTING_2H_DRAGON, + WOODCUTTING_2H_CRYSTAL, WOODCUTTING_2H_CRYSTAL_INACTIVE, WOODCUTTING_2H_3A + ); + + // Mouse fatigue class + public static MouseFatigue mouseFatigue = new MouseFatigue(); + + // Antiban setup class + public static AntibanSetupTemplates antibanSetupTemplates = new AntibanSetupTemplates(); + + @Getter + @Setter + public static int TIMEOUT = 0; + @Getter + private static Activity activity; + @Getter + private static ActivityIntensity activityIntensity; + @Getter + @Setter + private static Category category; + @Getter + @Setter + private static PlayStyle playStyle; + + /** + *

Basic Setup

+ * This method sets up the basic configuration for the antiban system. + * It is used to enable the minimal functionality that works out of the box without any additional configuration. + * The basic setup utilizes natural mouse movements to create natural delays between actions + */ + public static void basicSetup() { + Rs2AntibanSettings.actionCooldownActive = false; + Rs2AntibanSettings.microBreakActive = false; + Rs2AntibanSettings.antibanEnabled = true; + Rs2AntibanSettings.usePlayStyle = false; + Rs2AntibanSettings.randomIntervals = false; + Rs2AntibanSettings.simulateFatigue = false; + Rs2AntibanSettings.simulateAttentionSpan = false; + Rs2AntibanSettings.behavioralVariability = false; + Rs2AntibanSettings.nonLinearIntervals = false; + Rs2AntibanSettings.profileSwitching = false; + Rs2AntibanSettings.timeOfDayAdjust = false; + Rs2AntibanSettings.simulateMistakes = false; + Rs2AntibanSettings.naturalMouse = true; + Rs2AntibanSettings.contextualVariability = false; + Rs2AntibanSettings.dynamicIntensity = true; + Rs2AntibanSettings.dynamicActivity = true; + Rs2AntibanSettings.devDebug = false; + Rs2AntibanSettings.actionCooldownChance = 0.1; + Rs2AntibanSettings.microBreakChance = 0.1; + Rs2AntibanSettings.microBreakDurationLow = 3; + Rs2AntibanSettings.microBreakDurationHigh = 15; + } + + /** + *

Basic Play Style Setup

+ * This method sets up the basic configuration for the antiban system with play style enabled. + * Provides action cooldowns in the most basic and aggressive play style. + * To activate the action cooldown, call the actionCooldown() method after the desired action. + * + * @param activity The activity to be performed + * @see Rs2Antiban#actionCooldown() + */ + public static void basicPlayStyleSetup(Activity activity) { + Rs2AntibanSettings.actionCooldownActive = false; + Rs2AntibanSettings.microBreakActive = false; + Rs2AntibanSettings.antibanEnabled = true; + Rs2AntibanSettings.usePlayStyle = true; + Rs2AntibanSettings.randomIntervals = false; + Rs2AntibanSettings.simulateFatigue = false; + Rs2AntibanSettings.simulateAttentionSpan = false; + Rs2AntibanSettings.behavioralVariability = false; + Rs2AntibanSettings.nonLinearIntervals = false; + Rs2AntibanSettings.profileSwitching = false; + Rs2AntibanSettings.timeOfDayAdjust = false; + Rs2AntibanSettings.simulateMistakes = false; + Rs2AntibanSettings.naturalMouse = false; + Rs2AntibanSettings.contextualVariability = false; + Rs2AntibanSettings.dynamicIntensity = false; + Rs2AntibanSettings.dynamicActivity = false; + Rs2AntibanSettings.devDebug = false; + Rs2AntibanSettings.actionCooldownChance = 0.1; + Rs2AntibanSettings.microBreakChance = 0.1; + Rs2AntibanSettings.microBreakDurationLow = 3; + Rs2AntibanSettings.microBreakDurationHigh = 15; + setActivity(activity); + Rs2Antiban.playStyle = PlayStyle.EXTREME_AGGRESSIVE; + + } + + /** + *

Intermediate Play Style Setup

+ * This method sets up the intermediate configuration for the antiban system with play style enabled. + * Provides action cooldowns based on the activity intensity and play style. + * The action cooldown is randomized within the primary and secondary tick intervals in the current play style. + * To activate the action cooldown, call the actionCooldown() method after the desired action. + * + * @param activity The activity to be performed + * @see Rs2Antiban#actionCooldown() + */ + + // Advanced play style setup: maximal functionality with play style enabled + public static void intermediatePlayStyleSetup(Activity activity) { + Rs2AntibanSettings.actionCooldownActive = false; + Rs2AntibanSettings.microBreakActive = false; + Rs2AntibanSettings.antibanEnabled = true; + Rs2AntibanSettings.usePlayStyle = true; + Rs2AntibanSettings.randomIntervals = false; + Rs2AntibanSettings.simulateFatigue = false; + Rs2AntibanSettings.simulateAttentionSpan = false; + Rs2AntibanSettings.behavioralVariability = true; + Rs2AntibanSettings.nonLinearIntervals = false; + Rs2AntibanSettings.profileSwitching = false; //TODO: Implement this + Rs2AntibanSettings.timeOfDayAdjust = false; //TODO: Implement this + Rs2AntibanSettings.simulateMistakes = false; //Handled by the natural mouse + Rs2AntibanSettings.naturalMouse = true; + Rs2AntibanSettings.contextualVariability = false; //TODO: Implement this + Rs2AntibanSettings.dynamicIntensity = false; + Rs2AntibanSettings.dynamicActivity = false; + Rs2AntibanSettings.devDebug = true; + Rs2AntibanSettings.actionCooldownChance = 0.1; + Rs2AntibanSettings.microBreakChance = 0.1; + Rs2AntibanSettings.microBreakDurationLow = 1; + Rs2AntibanSettings.microBreakDurationHigh = 5; + setActivity(activity); + } + + /** + *

Advanced Play Style Setup

+ * This method sets up the advanced configuration for the antiban system with play style enabled. + * Provides action cooldowns based on the activity intensity and play style. + * The action cooldown is randomized within the primary and secondary tick intervals in the current play style. + * Attention span is simulated to switch play styles to create a more human-like drift in attention. + * Non-linear intervals are used to create Anti-patterns in the action cooldowns to avoid fingerprinting. + * To activate the action cooldown, call the actionCooldown() method after the desired action. + * + * @param activity The activity to be performed + * @see Rs2Antiban#actionCooldown() + */ + public static void advancedPlayStyleSetup(Activity activity) { + Rs2AntibanSettings.actionCooldownActive = false; + Rs2AntibanSettings.microBreakActive = false; + Rs2AntibanSettings.antibanEnabled = true; + Rs2AntibanSettings.usePlayStyle = true; + Rs2AntibanSettings.randomIntervals = false; + Rs2AntibanSettings.simulateFatigue = false; + Rs2AntibanSettings.simulateAttentionSpan = true; + Rs2AntibanSettings.behavioralVariability = true; + Rs2AntibanSettings.nonLinearIntervals = true; + Rs2AntibanSettings.profileSwitching = false; //TODO: Implement this + Rs2AntibanSettings.timeOfDayAdjust = false; //TODO: Implement this + Rs2AntibanSettings.simulateMistakes = false; //Handled by the natural mouse + Rs2AntibanSettings.naturalMouse = true; + Rs2AntibanSettings.contextualVariability = false; //TODO: Implement this + Rs2AntibanSettings.dynamicIntensity = false; + Rs2AntibanSettings.dynamicActivity = false; + Rs2AntibanSettings.devDebug = true; + Rs2AntibanSettings.takeMicroBreaks = true; + Rs2AntibanSettings.actionCooldownChance = 0.1; + Rs2AntibanSettings.microBreakChance = 0.1; + Rs2AntibanSettings.microBreakDurationLow = 1; + Rs2AntibanSettings.microBreakDurationHigh = 5; + setActivity(activity); + } + + + public static void setActivity(Activity activity) { + Rs2AntibanSettings.dynamicIntensity = false; + Rs2AntibanSettings.dynamicActivity = false; + Rs2Antiban.activity = activity; + Rs2Antiban.category = activity.getCategory(); + Rs2Antiban.activityIntensity = activity.getActivityIntensity(); + + if (Rs2AntibanSettings.simulateAttentionSpan) { + Rs2Antiban.playStyle = PlayStyle.EXTREME_AGGRESSIVE; + //Rs2Antiban.playStyle = activityIntensity.getPlayStyle(); + } else + Rs2Antiban.playStyle = activityIntensity.getPlayStyle(); + if (Rs2AntibanSettings.randomIntervals) { + Rs2Antiban.playStyle = PlayStyle.RANDOM; + } + playStyle.frequency = activityIntensity.getFrequency(); + playStyle.amplitude = activityIntensity.getAmplitude(); + Rs2Antiban.playStyle.resetPlayStyle(); + + + } + + public static void setActivityIntensity(ActivityIntensity activityIntensity) { + Rs2AntibanSettings.dynamicIntensity = false; + Rs2Antiban.activityIntensity = activityIntensity; + } + + + public static boolean checkForCookingEvent(ChatMessage event) { + if (event.getType() != ChatMessageType.SPAM) { + return false; + } + final String message = event.getMessage(); + return message.startsWith("You successfully cook") + || message.startsWith("You successfully bake") + || message.startsWith("You successfully fry") + || message.startsWith("You manage to cook") + || message.startsWith("You roast a") + || message.startsWith("You spit-roast") + || message.startsWith("You cook") + || message.startsWith("Eventually the Jubbly") + || message.startsWith("You half-cook") + || message.startsWith("The undead meat is now cooked") + || message.startsWith("The undead chicken is now cooked") + || message.startsWith("You successfully scramble") + || message.startsWith("You dry a piece of meat") + || message.startsWith("You accidentally burn") + || message.equals("You burn the mushroom in the fire.") + || message.startsWith("Unfortunately the Jubbly") + || message.startsWith("You accidentally spoil"); + } + + public static boolean isWoodcutting() { + return WOODCUTTING_ANIMS.contains(Rs2Player.getAnimation()); + } + + public static boolean isMining() { + return MINING_ANIMATION_IDS.contains(Rs2Player.getAnimation()); + } + + public static boolean isIdle() { + return AntibanPlugin.isIdle(); + } + + public static void actionCooldown() { + if (!Rs2AntibanSettings.usePlayStyle) { + Microbot.log("PlayStyle not enabled, cannot perform action cooldown"); + return; + } + + if (Rs2AntibanSettings.contextualVariability) + Microbot.pauseAllScripts = true; + if (Rs2AntibanSettings.nonLinearIntervals) + playStyle.evolvePlayStyle(); + if (Rs2AntibanSettings.behavioralVariability) + TIMEOUT = playStyle.getRandomTickInterval(); + else + TIMEOUT = playStyle.getPrimaryTickInterval(); + Rs2AntibanSettings.actionCooldownActive = true; + if (Rs2AntibanSettings.moveMouseOffScreen) + moveMouseOffScreen(); + } + + // method to activate the action cooldown by chance + public static void actionCooldownByChance() { + if (!Rs2AntibanSettings.usePlayStyle) { + Microbot.log("PlayStyle not enabled, cannot perform action cooldown"); + return; + } + + + if (Math.random() < Rs2AntibanSettings.actionCooldownChance) { + + if (Rs2AntibanSettings.nonLinearIntervals) + playStyle.evolvePlayStyle(); + if (Rs2AntibanSettings.behavioralVariability) + TIMEOUT = playStyle.getRandomTickInterval(); + else + TIMEOUT = playStyle.getPrimaryTickInterval(); + Rs2AntibanSettings.actionCooldownActive = true; + if (Rs2AntibanSettings.moveMouseOffScreen) + moveMouseOffScreen(); + } + + } + + // method to take a micro break by chance + public static void takeMicroBreakByChance() { + if (Math.random() < Rs2AntibanSettings.microBreakChance) { + Rs2AntibanSettings.microBreakActive = true; + BreakHandlerScript.breakDuration = Random.random(Rs2AntibanSettings.microBreakDurationLow * 60, Rs2AntibanSettings.microBreakDurationHigh * 60); + if (Rs2AntibanSettings.moveMouseOffScreen) + moveMouseOffScreen(); + + } + } + + + public static void renderAntibanOverlayComponents(PanelComponent panelComponent) { + final ProgressBarComponent progressBarComponent = new ProgressBarComponent(); + progressBarComponent.setBackgroundColor(Color.DARK_GRAY); + progressBarComponent.setForegroundColor(ColorUtil.fromHex("#cc8400")); + progressBarComponent.setMaximum(0); + progressBarComponent.setMaximum(playStyle.getSecondaryTickInterval()); + progressBarComponent.setValue(TIMEOUT); + progressBarComponent.setLabelDisplayMode(ProgressBarComponent.LabelDisplayMode.TEXT_ONLY); + progressBarComponent.setLeftLabel("0"); + progressBarComponent.setRightLabel(String.valueOf(playStyle.getSecondaryTickInterval())); + progressBarComponent.setCenterLabel(String.valueOf(TIMEOUT)); + + panelComponent.getChildren().add(TitleComponent.builder().text("\uD83E\uDD86 Humanizer \uD83E\uDD86") + .color(Color.ORANGE).build()); + panelComponent.getChildren().add(LineComponent.builder().build()); + panelComponent.getChildren().add(SplitComponent.builder() + .first(LineComponent.builder().left("Activity: " + activity.getMethod()).right(Rs2AntibanSettings.devDebug ? "Dynamic: " + (Rs2AntibanSettings.dynamicActivity ? "✔" : "❌") : "").build()) + .second(LineComponent.builder().left("Category: " + category.getName()).build()).build()); + panelComponent.getChildren().add(LineComponent.builder().build()); + panelComponent.getChildren().add(LineComponent.builder().left("Activity Intensity: " + activityIntensity.getName()).right(Rs2AntibanSettings.devDebug ? "Dynamic: " + (Rs2AntibanSettings.dynamicIntensity ? "✔" : "❌") : "").build()); + if (Rs2AntibanSettings.devDebug) { + panelComponent.getChildren().add(LineComponent.builder().left("isActionCooldownActive: " + (Rs2AntibanSettings.actionCooldownActive ? "✔" : "❌")).build()); + panelComponent.getChildren().add(LineComponent.builder().left("isEnabled: " + (Rs2AntibanSettings.antibanEnabled ? "✔" : "❌")).build()); + panelComponent.getChildren().add(LineComponent.builder().left("useRandomIntervals: " + (Rs2AntibanSettings.randomIntervals ? "✔" : "❌")).build()); + panelComponent.getChildren().add(LineComponent.builder().left("simulateFatigue: " + (Rs2AntibanSettings.simulateFatigue ? "✔" : "❌")).build()); + panelComponent.getChildren().add(LineComponent.builder().left("simulateAttentionSpan: " + (Rs2AntibanSettings.simulateAttentionSpan ? "✔" : "❌")).build()); + panelComponent.getChildren().add(LineComponent.builder().left("useBehavioralVariability: " + (Rs2AntibanSettings.behavioralVariability ? "✔" : "❌")).build()); + panelComponent.getChildren().add(LineComponent.builder().left("useNonLinearIntervals: " + (Rs2AntibanSettings.nonLinearIntervals ? "✔" : "❌")).build()); + panelComponent.getChildren().add(LineComponent.builder().left("enableProfileSwitching: " + (Rs2AntibanSettings.profileSwitching ? "✔" : "❌")).build()); + panelComponent.getChildren().add(LineComponent.builder().left("adjustForTimeOfDay: " + (Rs2AntibanSettings.timeOfDayAdjust ? "✔" : "❌")).build()); + panelComponent.getChildren().add(LineComponent.builder().left("simulateMistakes: " + (Rs2AntibanSettings.simulateMistakes ? "✔" : "❌")).build()); + panelComponent.getChildren().add(LineComponent.builder().left("useNaturalMouse: " + (Rs2AntibanSettings.naturalMouse ? "✔" : "❌")).build()); + panelComponent.getChildren().add(LineComponent.builder().left("useContextualVariability: " + (Rs2AntibanSettings.contextualVariability ? "✔" : "❌")).build()); + } + if (playStyle != null) { + panelComponent.getChildren().add(LineComponent.builder().left("Play Style: " + playStyle.getName()).build()); + if (Rs2AntibanSettings.simulateAttentionSpan) { + panelComponent.getChildren().add(LineComponent.builder().left("Play style change in: " + playStyle.getTimeLeftUntilNextSwitch()).build()); + } + panelComponent.getChildren().add(LineComponent.builder().build()); + if (Rs2Antiban.getCategory().isBusy()) { + panelComponent.getChildren().add(LineComponent.builder().left("We are busy").build()); + } else { + panelComponent.getChildren().add(LineComponent.builder().left("Not busy anymore, breaking").build()); + } + panelComponent.getChildren().add(LineComponent.builder().build()); + panelComponent.getChildren().add(TitleComponent.builder().text("Action cooldown(Tick)").color(Color.WHITE).build()); + panelComponent.getChildren().add(progressBarComponent); + } + + } + + /** + *

Move Mouse Off Screen

+ * This method moves the mouse off the screen with a 1/4 chance to trigger. + * This is used to simulate a user moving the mouse off the screen to take a break. + */ + public static void moveMouseOffScreen() { + Microbot.naturalMouse.moveOffScreen(); + } + + public static void activateAntiban() { + Rs2AntibanSettings.antibanEnabled = true; + } + + public static void deactivateAntiban() { + Rs2AntibanSettings.antibanEnabled = false; + } + + // reset all the variables + public static void resetAntiban() { + Rs2AntibanSettings.antibanEnabled = false; + Rs2AntibanSettings.microBreakActive = false; + Rs2AntibanSettings.actionCooldownActive = false; + Rs2AntibanSettings.usePlayStyle = false; + Rs2AntibanSettings.randomIntervals = false; + Rs2AntibanSettings.simulateFatigue = false; + Rs2AntibanSettings.simulateAttentionSpan = false; + Rs2AntibanSettings.behavioralVariability = false; + Rs2AntibanSettings.nonLinearIntervals = false; + Rs2AntibanSettings.profileSwitching = false; + Rs2AntibanSettings.timeOfDayAdjust = false; + Rs2AntibanSettings.simulateMistakes = false; + Rs2AntibanSettings.naturalMouse = false; + Rs2AntibanSettings.contextualVariability = false; + Rs2AntibanSettings.dynamicIntensity = true; + Rs2AntibanSettings.dynamicActivity = true; + Rs2AntibanSettings.devDebug = true; + TIMEOUT = 0; + activity = null; + activityIntensity = null; + category = null; + playStyle = null; + Rs2AntibanSettings.takeMicroBreaks = false; + Rs2AntibanSettings.actionCooldownChance = 0.1; + Rs2AntibanSettings.microBreakChance = 0.1; + Rs2AntibanSettings.microBreakDurationLow = 3; + Rs2AntibanSettings.microBreakDurationHigh = 15; + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2AntibanSettings.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2AntibanSettings.java new file mode 100644 index 0000000000..6aeb189df5 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2AntibanSettings.java @@ -0,0 +1,28 @@ +package net.runelite.client.plugins.microbot.util.antiban; + +public class Rs2AntibanSettings { + public static boolean actionCooldownActive = false; + public static boolean microBreakActive = false; + public static boolean antibanEnabled = true; + public static boolean usePlayStyle = true; + public static boolean randomIntervals = false; + public static boolean simulateFatigue = false; + public static boolean simulateAttentionSpan = false; + public static boolean behavioralVariability = false; + public static boolean nonLinearIntervals = false; + public static boolean profileSwitching = false; + public static boolean timeOfDayAdjust = false; //TODO: Implement this + public static boolean simulateMistakes = false; //Handled by the natural mouse + public static boolean naturalMouse = false; + public static boolean moveMouseOffScreen = false; + public static boolean contextualVariability = true; //TODO: Implement this + public static boolean dynamicIntensity = true; + public static boolean dynamicActivity = true; + public static boolean devDebug = true; + public static boolean takeMicroBreaks = false; // will take micro breaks lasting 3-15 minutes at random intervals by default. + public static boolean playSchedule = false; //TODO: Implement this + public static int microBreakDurationLow = 3; // 3 minutes + public static int microBreakDurationHigh = 15; // 15 minutes + public static double actionCooldownChance = 0.1; // 10% chance of activating the action cooldown by default + public static double microBreakChance = 0.1; // 10% chance of taking a micro break by default +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/Activity.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/Activity.java new file mode 100644 index 0000000000..e3845fbdf3 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/Activity.java @@ -0,0 +1,478 @@ +package net.runelite.client.plugins.microbot.util.antiban.enums; + +import lombok.Getter; +import net.runelite.api.Skill; + +public enum Activity { + GENERAL_MINING("General Mining", Category.SKILLING_MINING, ActivityIntensity.LOW), + GENERAL_SMITHING("General Smithing", Category.SKILLING_SMITHING, ActivityIntensity.LOW), + GENERAL_FISHING("General Fishing", Category.SKILLING_FISHING, ActivityIntensity.LOW), + GENERAL_COOKING("General Cooking", Category.SKILLING_COOKING, ActivityIntensity.LOW), + GENERAL_FIREMAKING("General Firemaking", Category.SKILLING_FIREMAKING, ActivityIntensity.LOW), + GENERAL_WOODCUTTING("General Woodcutting", Category.SKILLING_WOODCUTTING, ActivityIntensity.LOW), + GENERAL_FLETCHING("General Fletching", Category.SKILLING_FLETCHING, ActivityIntensity.LOW), + GENERAL_CRAFTING("General Crafting", Category.SKILLING_CRAFTING, ActivityIntensity.LOW), + GENERAL_AGILITY("General Agility", Category.SKILLING_AGILITY, ActivityIntensity.MODERATE), + GENERAL_THIEVING("General Thieving", Category.SKILLING_THIEVING, ActivityIntensity.MODERATE), + GENERAL_SLAYER("General Slayer", Category.COMBAT_HIGH, ActivityIntensity.MODERATE), + GENERAL_RUNECRAFT("General Runecraft", Category.SKILLING_RUNECRAFT, ActivityIntensity.MODERATE), + GENERAL_HUNTER("General Hunter", Category.SKILLING_HUNTER, ActivityIntensity.MODERATE), + GENERAL_COMBAT("General Combat", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + GENERAL_HERBLORE("General Herblore", Category.SKILLING_HERBLORE, ActivityIntensity.LOW), + GENERAL_FARMING("General Farming", Category.SKILLING_FARMING, ActivityIntensity.MODERATE), + GENERAL_PRAYER("General Prayer", Category.SKILLING_PRAYER, ActivityIntensity.HIGH), + GENERAL_CONSTRUCTION("General Construction", Category.SKILLING_CONSTRUCTION, ActivityIntensity.MODERATE), + COMPLETING_THE_FORTIS_COLOSSEUM_WAVE_12("Completing the Fortis Colosseum (Wave 12)", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_NEX_DUO("Killing Nex (Duo)", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + TOMBS_OF_AMASCUT_SOLO_500_RAID_LEVEL("Tombs of Amascut (solo 500 raid level)", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_NEX_TEAM("Killing Nex (Team)", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + THEATRE_OF_BLOOD_SCYTHE_OF_VITUR("Theatre of Blood (Scythe of vitur)", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + CHAMBERS_OF_XERIC("Chambers of Xeric", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + THEATRE_OF_BLOOD_ABYSSAL_TENTACLE("Theatre of Blood (Abyssal tentacle)", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_KREEARRA("Killing Kreearra", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_PHOSANIS_NIGHTMARE("Killing Phosanis Nightmare", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_VENENATIS("Killing Venenatis", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_VARDORVIS("Killing Vardorvis", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + TOMBS_OF_AMASCUT_EXPERT("Tombs of Amascut (Expert)", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_COMMANDER_ZILYANA("Killing Commander Zilyana", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_SPINDEL("Killing Spindel", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_VETION("Killing Vetion", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_PHANTOM_MUSPAH_TWISTED_BOW("Killing Phantom Muspah (Twisted bow)", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_THE_ALCHEMICAL_HYDRA("Killing the Alchemical Hydra", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + COMPLETING_THE_CORRUPTED_GAUNTLET("Completing The Corrupted Gauntlet", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_CERBERUS("Killing Cerberus", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_GENERAL_GRAARDOR("Killing General Graardor", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_DEMONIC_GORILLAS("Killing demonic gorillas", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_THE_WHISPERER("Killing The Whisperer", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_DUKE_SUCELLUS("Killing Duke Sucellus", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_THE_NIGHTMARE("Killing The Nightmare", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_CALVARION("Killing Calvarion", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_THE_LEVIATHAN("Killing The Leviathan", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + PICKPOCKETING_VYRES("Pickpocketing vyres", Category.SKILLING_THIEVING, ActivityIntensity.HIGH), + KILLING_CALLISTO("Killing Callisto", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_REVENANTS_CRAWS_BOW("Killing revenants (Craws bow)", Category.COMBAT_MID, ActivityIntensity.HIGH), + KILLING_ARTIO("Killing Artio", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_VORKATH_DRAGON_HUNTER_LANCE("Killing Vorkath (Dragon hunter lance)", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_VORKATH_DRAGON_HUNTER_CROSSBOW("Killing Vorkath (Dragon hunter crossbow)", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_ZULRAH_MAX_EFFICIENCY("Killing Zulrah (max efficiency)", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + COMPLETING_THE_FORTIS_COLOSSEUM_WAVE_1("Completing the Fortis Colosseum (Wave 1)", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + WILDERNESS_AGILITY_COURSE("Wilderness Agility Course", Category.SKILLING_AGILITY, ActivityIntensity.HIGH), + KILLING_KRIL_TSUTSAROTH("Killing Kril Tsutsaroth", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_REVENANTS_MAGIC_SHORTBOW("Killing revenants (Magic shortbow)", Category.COMBAT_MID, ActivityIntensity.HIGH), + PICKPOCKETING_ELVES("Pickpocketing elves", Category.SKILLING_THIEVING, ActivityIntensity.HIGH), + KILLING_DAGANNOTH_KINGS_SOLO_TRIBRID("Killing Dagannoth Kings (Solo tribrid)", Category.COMBAT_HIGH, ActivityIntensity.MODERATE), + KILLING_THE_CORPOREAL_BEAST("Killing the Corporeal Beast", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_ZOMBIE_PIRATES("Killing zombie pirates", Category.COMBAT_LOW, ActivityIntensity.HIGH), + OPENING_ESSENCE_IMPLING_JARS("Opening essence impling jars", Category.PROCESSING, ActivityIntensity.HIGH), + CRAFTING_WRATH_RUNES("Crafting wrath runes", Category.SKILLING_RUNECRAFT, ActivityIntensity.HIGH), + KILLING_THE_GIANT_MOLE_TWISTED_BOW("Killing the Giant Mole (Twisted bow)", Category.COMBAT_MID, ActivityIntensity.LOW), + STEALING_FROM_ROGUES_CASTLE_CHESTS("Stealing from Rogues Castle chests", Category.SKILLING_THIEVING, ActivityIntensity.HIGH), + COMPLETING_THE_GAUNTLET("Completing The Gauntlet", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + DELIVERING_FOOD_IN_GNOME_RESTAURANT("Delivering food in Gnome Restaurant", Category.COLLECTING, ActivityIntensity.HIGH), + OPENING_GRUBBY_CHESTS("Opening grubby chests", Category.PROCESSING, ActivityIntensity.HIGH), + MOONS_OF_PERIL("Moons of Peril", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + CRAFTING_BLOOD_RUNES("Crafting blood runes", Category.SKILLING_RUNECRAFT, ActivityIntensity.HIGH), + KILLING_VORKATH_TOXIC_BLOWPIPE("Killing Vorkath (Toxic blowpipe)", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + HALLOWED_SEPULCHRE("Hallowed Sepulchre", Category.SKILLING_AGILITY, ActivityIntensity.HIGH), + OPENING_SINISTER_CHESTS("Opening sinister chests", Category.PROCESSING, ActivityIntensity.HIGH), + CRAFTING_ASTRAL_RUNES("Crafting astral runes", Category.SKILLING_RUNECRAFT, ActivityIntensity.HIGH), + OPENING_ECLECTIC_IMPLING_JARS("Opening eclectic impling jars", Category.PROCESSING, ActivityIntensity.HIGH), + MAKING_TOY_CATS("Making toy cats", Category.PROCESSING, ActivityIntensity.HIGH), + CRAFTING_SUNFIRE_RUNES("Crafting sunfire runes", Category.SKILLING_RUNECRAFT, ActivityIntensity.HIGH), + SMELTING_RUNITE_BARS_AT_BLAST_FURNACE("Smelting runite bars at Blast Furnace", Category.SKILLING_SMITHING, ActivityIntensity.HIGH), + FILLING_BULLSEYE_LANTERN_EMPTY("Filling bullseye lantern (empty)", Category.PROCESSING, ActivityIntensity.MODERATE), + KILLING_THE_THERMONUCLEAR_SMOKE_DEVIL("Killing the Thermonuclear smoke devil", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + BUYING_MONKEY_NUTS("Buying monkey nuts", Category.COLLECTING, ActivityIntensity.LOW), + KILLING_ZALCANO("Killing Zalcano", Category.SKILLING, ActivityIntensity.HIGH), + KILLING_RUNE_DRAGONS("Killing rune dragons", Category.COMBAT_HIGH, ActivityIntensity.MODERATE), + KILLING_LIZARDMAN_SHAMANS_CANYON("Killing Lizardman Shamans (Canyon)", Category.COMBAT_HIGH, ActivityIntensity.MODERATE), + CRAFTING_DEATH_RUNES_THROUGH_THE_ABYSS("Crafting death runes through the Abyss", Category.SKILLING_RUNECRAFT, ActivityIntensity.MODERATE), + KILLING_ZULRAH("Killing Zulrah", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + CRAFTING_DOUBLE_LAW_RUNES_THROUGH_THE_ABYSS("Crafting double law runes through the Abyss", Category.SKILLING_RUNECRAFT, ActivityIntensity.MODERATE), + KILLING_LAVA_DRAGONS("Killing lava dragons", Category.COMBAT_HIGH, ActivityIntensity.MODERATE), + KILLING_DAGANNOTH_KINGS_REX_ONLY("Killing Dagannoth Kings (Rex only)", Category.COMBAT_MID, ActivityIntensity.LOW), + OPENING_CRYSTAL_CHESTS("Opening crystal chests", Category.PROCESSING, ActivityIntensity.HIGH), + MAKING_REDWOOD_PYRE_LOGS("Making redwood pyre logs", Category.PROCESSING, ActivityIntensity.MODERATE), + MAKING_SACRED_OIL("Making sacred oil", Category.SKILLING_CRAFTING, ActivityIntensity.LOW), + MAKING_DIVINE_SUPER_COMBAT_POTIONS("Making divine super combat potions", Category.SKILLING_HERBLORE, ActivityIntensity.LOW), + KILLING_THE_ABYSSAL_SIRE("Killing the Abyssal Sire", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_THE_GIANT_MOLE_DHAROKS("Killing the Giant Mole (Dharoks)", Category.COMBAT_MID, ActivityIntensity.LOW), + BUYING_KEGS_OF_BEER("Buying kegs of beer", Category.COLLECTING, ActivityIntensity.MODERATE), + MAKING_AVANTOE_POTIONS("Making avantoe potions", Category.PROCESSING, ActivityIntensity.LOW), + PICKPOCKETING_HAM_MEMBERS("Pickpocketing H.A.M. members", Category.SKILLING_THIEVING, ActivityIntensity.HIGH), + HUNTING_BLACK_CHINCHOMPAS("Hunting black chinchompas", Category.SKILLING_HUNTER, ActivityIntensity.MODERATE), + MAKING_SNAPDRAGON_POTIONS("Making snapdragon potions", Category.PROCESSING, ActivityIntensity.LOW), + CASTING_TAN_LEATHER("Casting Tan Leather", Category.SKILLING_MAGIC, ActivityIntensity.MODERATE), + KILLING_THE_GROTESQUE_GUARDIANS("Killing the Grotesque Guardians", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + HUNTING_IMPLINGS("Hunting implings", Category.SKILLING_HUNTER, ActivityIntensity.HIGH), + CRAFTING_BLOOD_RUNES_THROUGH_THE_ABYSS("Crafting blood runes through the Abyss", Category.SKILLING_RUNECRAFT, ActivityIntensity.HIGH), + MAKING_MAGIC_PYRE_LOGS("Making magic pyre logs", Category.PROCESSING, ActivityIntensity.MODERATE), + KILLING_BRUTAL_BLACK_DRAGONS("Killing brutal black dragons", Category.COMBAT_HIGH, ActivityIntensity.LOW), + MAKING_RANARR_POTIONS("Making ranarr potions", Category.PROCESSING, ActivityIntensity.LOW), + KILLING_HYDRAS("Killing hydras", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + ENCHANTING_DRAGONSTONE_JEWELLERY("Enchanting dragonstone jewellery", Category.SKILLING_MAGIC, ActivityIntensity.MODERATE), + MAKING_SANFEW_SERUM4("Making sanfew serum(4)", Category.SKILLING_HERBLORE, ActivityIntensity.MODERATE), + MAKING_DWARF_WEED_POTIONS("Making dwarf weed potions", Category.PROCESSING, ActivityIntensity.LOW), + SMELTING_ADAMANTITE_BARS_AT_BLAST_FURNACE("Smelting adamantite bars at Blast Furnace", Category.SKILLING_SMITHING, ActivityIntensity.HIGH), + MAKING_KWUARM_POTIONS("Making kwuarm potions", Category.PROCESSING, ActivityIntensity.LOW), + MAKING_CADANTINE_POTIONS("Making cadantine potions", Category.PROCESSING, ActivityIntensity.LOW), + KILLING_GREEN_DRAGONS_MYTHS_GUILD("Killing green dragons (Myths Guild)", Category.COMBAT_MID, ActivityIntensity.MODERATE), + MAKING_RAW_WILD_PIES("Making raw wild pies", Category.PROCESSING, ActivityIntensity.HIGH), + KILLING_VYREWATCH_SENTINELS("Killing Vyrewatch Sentinels", Category.COMBAT_HIGH, ActivityIntensity.LOW), + KILLING_THE_KRAKEN("Killing the Kraken", Category.COMBAT_HIGH, ActivityIntensity.LOW), + KILLING_FIYR_SHADES("Killing fiyr shades", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + MAKING_TOADFLAX_POTIONS("Making toadflax potions", Category.PROCESSING, ActivityIntensity.LOW), + KILLING_GREEN_DRAGONS("Killing green dragons", Category.COMBAT_MID, ActivityIntensity.MODERATE), + MAKING_LANTADYME_POTIONS("Making lantadyme potions", Category.PROCESSING, ActivityIntensity.LOW), + BARROWS("Barrows", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + PICKPOCKETING_MASTER_FARMERS("Pickpocketing master farmers", Category.SKILLING_THIEVING, ActivityIntensity.MODERATE), + KILLING_LIZARDMAN_SHAMANS_SETTLEMENT_OR_TEMPLE("Killing Lizardman Shamans (Settlement or Temple)", Category.COMBAT_HIGH, ActivityIntensity.MODERATE), + COLLECTING_MORT_MYRE_FUNGI("Collecting mort myre fungi", Category.COLLECTING, ActivityIntensity.LOW), + HUNTING_MOONLIGHT_ANTELOPES("Hunting moonlight antelopes", Category.SKILLING_HUNTER, ActivityIntensity.MODERATE), + COMPLETING_ELITE_CLUES_URIUM_REMAINS("Completing elite clues (urium remains)", Category.SKILLING_FIREMAKING, ActivityIntensity.HIGH), + MAKING_WEAPON_POISON_PLUS_PLUS("Making weapon poison(++)", Category.COLLECTING, ActivityIntensity.HIGH), + KILLING_MITHRIL_DRAGONS("Killing mithril dragons", Category.COMBAT_HIGH, ActivityIntensity.MODERATE), + KILLING_URIUM_SHADES("Killing urium shades", Category.COMBAT_HIGH, ActivityIntensity.MODERATE), + MAKING_GUTHIX_RESTS("Making Guthix rests", Category.PROCESSING, ActivityIntensity.HIGH), + SMELTING_STEEL_BARS_AT_BLAST_FURNACE("Smelting steel bars at Blast Furnace", Category.SKILLING_SMITHING, ActivityIntensity.HIGH), + KILLING_SKELETAL_WYVERNS("Killing skeletal wyverns", Category.COMBAT_HIGH, ActivityIntensity.LOW), + CRAFTING_DRIFT_NETS("Crafting drift nets", Category.SKILLING_CRAFTING, ActivityIntensity.MODERATE), + SMITHING_RUNE_ITEMS("Smithing rune items", Category.SKILLING_SMITHING, ActivityIntensity.LOW), + MINING_RUNITE_ORE("Mining runite ore", Category.SKILLING_MINING, ActivityIntensity.LOW), + MAKING_IRIT_POTIONS("Making irit potions", Category.PROCESSING, ActivityIntensity.LOW), + LAST_MAN_STANDING("Last Man Standing", Category.COMBAT_LOW, ActivityIntensity.HIGH), + CREATING_TELEPORT_TABLETS_AT_LECTERN_LUNAR("Creating teleport tablets at Lectern (Lunar)", Category.SKILLING_MAGIC, ActivityIntensity.LOW), + MAKING_ULTRACOMPOST("Making ultracompost", Category.PROCESSING, ActivityIntensity.LOW), + KILLING_FEVER_SPIDERS_MAXIMUM_EFFICIENCY("Killing fever spiders (maximum efficiency)", Category.COMBAT_LOW, ActivityIntensity.HIGH), + FLETCHING_MOONLIGHT_ANTLER_BOLTS("Fletching moonlight antler bolts", Category.PROCESSING, ActivityIntensity.LOW), + KILLING_DISCIPLES_OF_IBAN("Killing disciples of Iban", Category.COMBAT_LOW, ActivityIntensity.MODERATE), + KILLING_GARGOYLES("Killing gargoyles", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + FLETCHING_OGRE_ARROW_SHAFTS("Fletching ogre arrow shafts", Category.PROCESSING, ActivityIntensity.LOW), + KILLING_ENTS("Killing ents", Category.COMBAT_MID, ActivityIntensity.MODERATE), + GRINDING_DESERT_GOAT_HORNS("Grinding desert goat horns", Category.PROCESSING, ActivityIntensity.MODERATE), + GRINDING_UNICORN_HORNS("Grinding unicorn horns", Category.PROCESSING, ActivityIntensity.MODERATE), + CRAFTING_DRAGONSTONE_JEWELLERY("Crafting dragonstone jewellery", Category.SKILLING_CRAFTING, ActivityIntensity.LOW), + CRAFTING_BLOOD_RUNES_AT_ARCEUUS_MAX("Crafting blood runes at Arceuus (max)", Category.SKILLING_RUNECRAFT, ActivityIntensity.LOW), + HUNTING_SUNLIGHT_ANTELOPES("Hunting sunlight antelopes", Category.SKILLING_HUNTER, ActivityIntensity.MODERATE), + PICKPOCKETING_PALADINS("Pickpocketing paladins", Category.SKILLING_THIEVING, ActivityIntensity.MODERATE), + STEALING_CAVE_GOBLIN_WIRE("Stealing cave goblin wire", Category.COLLECTING, ActivityIntensity.HIGH), + CRAFTING_COSMIC_RUNES_THROUGH_THE_ABYSS("Crafting cosmic runes through the Abyss", Category.SKILLING_RUNECRAFT, ActivityIntensity.MODERATE), + MINING_BASALT("Mining basalt", Category.SKILLING_MINING, ActivityIntensity.LOW), + TANNING_RED_DRAGONHIDE("Tanning red dragonhide", Category.PROCESSING, ActivityIntensity.LOW), + CRAFTING_BLOOD_RUNES_AT_ARCEUUS_NO_DIARY("Crafting blood runes at Arceuus (no diary)", Category.SKILLING_RUNECRAFT, ActivityIntensity.LOW), + MAKING_PINEAPPLE_PIZZAS("Making pineapple pizzas", Category.SKILLING_COOKING, ActivityIntensity.HIGH), + MAKING_HARRALANDER_POTIONS("Making harralander potions", Category.PROCESSING, ActivityIntensity.LOW), + KILLING_BLUE_DRAGONS("Killing blue dragons", Category.COMBAT_MID, ActivityIntensity.MODERATE), + MINING_GEMSTONES("Mining gemstones", Category.SKILLING_MINING, ActivityIntensity.MODERATE), + CRAFTING_OPAL_BRACELETS("Crafting opal bracelets", Category.SKILLING_CRAFTING, ActivityIntensity.LOW), + ENCHANTING_TOPAZ_JEWELLERY("Enchanting topaz jewellery", Category.SKILLING_MAGIC, ActivityIntensity.MODERATE), + KILLING_SARACHNIS("Killing Sarachnis", Category.COMBAT_MID, ActivityIntensity.MODERATE), + BUYING_BEER_TANKARDS("Buying beer tankards", Category.PROCESSING, ActivityIntensity.MODERATE), + CRAFTING_XERICIAN_ROBES("Crafting Xerician robes", Category.SKILLING_CRAFTING, ActivityIntensity.LOW), + SMELTING_MITHRIL_BARS_AT_BLAST_FURNACE("Smelting mithril bars at Blast Furnace", Category.SKILLING_SMITHING, ActivityIntensity.HIGH), + CHARGING_AND_ALCHEMISING_BRACELETS_OF_ETHEREUM("Charging and alchemising bracelets of ethereum", Category.PROCESSING, ActivityIntensity.MODERATE), + CREATING_TELEPORT_TO_HOUSE_TABLETS("Creating teleport to house tablets", Category.PROCESSING, ActivityIntensity.LOW), + CREATING_BARROWS_TELEPORT_TABLETS("Creating Barrows teleport tablets", Category.SKILLING_MAGIC, ActivityIntensity.LOW), + KILLING_THE_KALPHITE_QUEEN("Killing the Kalphite Queen", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + CRAFTING_LAW_RUNES_THROUGH_THE_ABYSS("Crafting law runes through the Abyss", Category.SKILLING_RUNECRAFT, ActivityIntensity.MODERATE), + CRAFTING_COSMIC_RUNES("Crafting cosmic runes", Category.SKILLING_RUNECRAFT, ActivityIntensity.HIGH), + FILLING_BUCKETS_WITH_WATER("Filling buckets with water", Category.SKILLING_MAGIC, ActivityIntensity.MODERATE), + ENCHANTING_JADE_AMULETS("Enchanting jade amulets", Category.SKILLING_MAGIC, ActivityIntensity.MODERATE), + HUNTING_HERBIBOARS("Hunting herbiboars", Category.SKILLING_HUNTER, ActivityIntensity.LOW), + TEMPLE_TREKKING("Temple Trekking", Category.COMBAT_LOW, ActivityIntensity.LOW), + FLETCHING_RUBY_BOLTS("Fletching ruby bolts", Category.SKILLING_FLETCHING, ActivityIntensity.LOW), + PLANTING_MITHRIL_SEEDS("Planting mithril seeds", Category.COLLECTING, ActivityIntensity.LOW), + MAKING_RAW_SUMMER_PIES("Making raw summer pies", Category.PROCESSING, ActivityIntensity.MODERATE), + CRUSHING_BIRD_NESTS("Crushing bird nests", Category.PROCESSING, ActivityIntensity.MODERATE), + FLETCHING_FLIGHTED_OGRE_ARROWS("Fletching flighted ogre arrows", Category.PROCESSING, ActivityIntensity.LOW), + HIGH_ALCHING_MYSTIC_EARTH_STAVES_AT_THE_FOUNTAIN_OF_RUNE("High alching mystic earth staves at the fountain of rune", Category.SKILLING_MAGIC, ActivityIntensity.LOW), + TANNING_BLACK_DRAGONHIDE("Tanning black dragonhide", Category.PROCESSING, ActivityIntensity.LOW), + HIGH_ALCHING_MYSTIC_WATER_STAVES_AT_THE_FOUNTAIN_OF_RUNE("High alching mystic water staves at the fountain of rune", Category.SKILLING_MAGIC, ActivityIntensity.LOW), + BLAST_MINING("Blast mining", Category.SKILLING_MINING, ActivityIntensity.MODERATE), + HIGH_ALCHING_MYSTIC_AIR_STAVES_AT_THE_FOUNTAIN_OF_RUNE("High alching mystic air staves at the fountain of rune", Category.SKILLING_MAGIC, ActivityIntensity.LOW), + CRAFTING_SOUL_RUNES("Crafting soul runes", Category.SKILLING_RUNECRAFT, ActivityIntensity.HIGH), + MAKING_TARROMIN_POTIONS("Making tarromin potions", Category.PROCESSING, ActivityIntensity.LOW), + KILLING_FEVER_SPIDERS_ALCHING("Killing fever spiders (alching", Category.COMBAT_LOW, ActivityIntensity.HIGH), + MAKING_ADAMANT_BRUTAL_ARROWS("Making adamant brutal arrows", Category.PROCESSING, ActivityIntensity.LOW), + CASTING_SUPERGLASS_MAKE("Casting Superglass Make", Category.SKILLING_MAGIC, ActivityIntensity.HIGH), + KILLING_FEVER_SPIDERS_NO_CANNON("Killing fever spiders (no cannon)", Category.COMBAT_LOW, ActivityIntensity.MODERATE), + MAKING_RAW_ADMIRAL_PIES("Making raw admiral pies", Category.PROCESSING, ActivityIntensity.HIGH), + CRAFTING_TOPAZ_BRACELETS("Crafting topaz bracelets", Category.SKILLING_CRAFTING, ActivityIntensity.LOW), + MINING_RUNITE_ORE_FREE_TO_PLAY("Mining runite ore (free-to-play)", Category.SKILLING_MINING, ActivityIntensity.MODERATE), + CREATING_VARROCK_TELEPORT_TABLETS("Creating Varrock teleport tablets", Category.PROCESSING, ActivityIntensity.LOW), + CLEANING_GRIMY_TORSTOL("Cleaning grimy torstol", Category.SKILLING_HERBLORE, ActivityIntensity.HIGH), + CHARGING_AIR_ORBS("Charging air orbs", Category.SKILLING_MAGIC, ActivityIntensity.MODERATE), + KILLING_BRUTAL_RED_DRAGONS("Killing brutal red dragons", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + HUNTING_CARNIVOROUS_CHINCHOMPAS("Hunting carnivorous chinchompas", Category.SKILLING_HUNTER, ActivityIntensity.MODERATE), + CRAFTING_NATURE_RUNES_THROUGH_THE_ABYSS("Crafting nature runes through the Abyss", Category.SKILLING_RUNECRAFT, ActivityIntensity.HIGH), + HIGH_ALCHING_PROFITABLE_FREE_TO_PLAY_ITEMS("High alching profitable free-to-play items", Category.SKILLING_MAGIC, ActivityIntensity.LOW), + HUMIDIFYING_CLAY("Humidifying clay", Category.SKILLING_MAGIC, ActivityIntensity.MODERATE), + SEARCHING_A_HERBLORE_CAPE("Searching a herblore cape", Category.COLLECTING, ActivityIntensity.HIGH), + KILLING_SKOGRES_AND_ZOGRES("Killing skogres and zogres", Category.COMBAT_MID, ActivityIntensity.MODERATE), + CRAFTING_BLOOD_RUNES_AT_ARCEUUS_MINIMAL_REQUIREMENTS("Crafting blood runes at Arceuus (minimal requirements)", Category.SKILLING_RUNECRAFT, ActivityIntensity.LOW), + CATCHING_DARK_CRABS("Catching dark crabs", Category.SKILLING_FISHING, ActivityIntensity.LOW), + COOKING_RAW_SUNLIGHT_ANTELOPE("Cooking raw sunlight antelope", Category.SKILLING_COOKING, ActivityIntensity.LOW), + KILLING_ICE_TROLL_RUNTS("Killing ice troll runts", Category.COMBAT_MID, ActivityIntensity.MODERATE), + SMITHING_CANNONBALLS("Smithing cannonballs", Category.SKILLING_SMITHING, ActivityIntensity.LOW), + MINING_SALTS("Mining salts", Category.SKILLING_MINING, ActivityIntensity.LOW), + CUTTING_DIAMOND_BOLT_TIPS("Cutting diamond bolt tips", Category.PROCESSING, ActivityIntensity.LOW), + MAKING_MARRENTILL_POTIONS("Making marrentill potions", Category.PROCESSING, ActivityIntensity.LOW), + HIGH_ALCHING_COMBAT_BRACELETS_AT_THE_FOUNTAIN_OF_RUNE("High alching combat bracelets at the fountain of rune", Category.SKILLING_MAGIC, ActivityIntensity.LOW), + CRAFTING_LIMESTONE_BRICKS("Crafting limestone bricks", Category.PROCESSING, ActivityIntensity.HIGH), + TANNING_BLUE_DRAGONHIDE("Tanning blue dragonhide", Category.PROCESSING, ActivityIntensity.LOW), + MAKING_MAHOGANY_PLANKS("Making mahogany planks", Category.PROCESSING, ActivityIntensity.HIGH), + CRAFTING_RUBY_BRACELETS("Crafting ruby bracelets", Category.SKILLING_CRAFTING, ActivityIntensity.LOW), + MAKING_GUAM_POTIONS("Making guam potions", Category.PROCESSING, ActivityIntensity.LOW), + CRAFTING_DIAMOND_BRACELETS("Crafting diamond bracelets", Category.SKILLING_CRAFTING, ActivityIntensity.LOW), + CLEANING_GRIMY_KWUARM("Cleaning grimy kwuarm", Category.SKILLING_HERBLORE, ActivityIntensity.HIGH), + CREATING_CAMELOT_TELEPORT_TABLETS("Creating Camelot teleport tablets", Category.PROCESSING, ActivityIntensity.LOW), + EXCHANGING_IMPLING_JARS("Exchanging impling jars", Category.PROCESSING, ActivityIntensity.MODERATE), + HIGH_ALCHING_RUNITE_LIMBS_AT_THE_FOUNTAIN_OF_RUNE("High alching runite limbs at the fountain of rune", Category.SKILLING_MAGIC, ActivityIntensity.LOW), + COLLECTING_CLIMBING_BOOTS("Collecting climbing boots", Category.COLLECTING, ActivityIntensity.HIGH), + BLESSING_UNBLESSED_SYMBOLS("Blessing unblessed symbols", Category.PROCESSING, ActivityIntensity.LOW), + CHARGING_FIRE_ORBS("Charging fire orbs", Category.SKILLING_MAGIC, ActivityIntensity.LOW), + MINING_AMETHYST("Mining amethyst", Category.SKILLING_MINING, ActivityIntensity.LOW), + MINING_IRON_ORE("Mining iron ore", Category.SKILLING_MINING, ActivityIntensity.MODERATE), + MAKING_OAK_PLANKS("Making oak planks", Category.PROCESSING, ActivityIntensity.MODERATE), + TANNING_GREEN_DRAGONHIDE("Tanning green dragonhide", Category.PROCESSING, ActivityIntensity.LOW), + MOTHERLODE_MINE("Motherlode Mine", Category.SKILLING_MINING, ActivityIntensity.LOW), + MAKING_MAHOGANY_PLANKS_AT_THE_WOODCUTTING_GUILD("Making mahogany planks at the Woodcutting Guild", Category.PROCESSING, ActivityIntensity.MODERATE), + COLLECTING_SNAPE_GRASS("Collecting snape grass", Category.COLLECTING, ActivityIntensity.LOW), + CUTTING_AMETHYST_ARROWTIPS("Cutting amethyst arrowtips", Category.PROCESSING, ActivityIntensity.LOW), + KILLING_THE_KING_BLACK_DRAGON("Killing the King Black Dragon", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + RECHARGING_RINGS_OF_WEALTH("Recharging rings of wealth", Category.PROCESSING, ActivityIntensity.MODERATE), + COLLECTING_MONKS_ROBES("Collecting monks robes", Category.COLLECTING, ActivityIntensity.LOW), + LOOTING_OGRE_COFFINS("Looting ogre coffins", Category.COLLECTING, ActivityIntensity.MODERATE), + CASTING_SPIN_FLAX("Casting Spin Flax", Category.SKILLING_MAGIC, ActivityIntensity.MODERATE), + KILLING_JUBSTERS("Killing Jubsters", Category.COMBAT_LOW, ActivityIntensity.LOW), + FLETCHING_HEADLESS_ARROWS("Fletching headless arrows", Category.PROCESSING, ActivityIntensity.LOW), + CASTING_PLANK_MAKE("Casting plank make", Category.SKILLING_MAGIC, ActivityIntensity.LOW), + MAKING_DYNAMITE("Making dynamite", Category.SKILLING_MINING, ActivityIntensity.MODERATE), + CRAFTING_SAPPHIRE_BRACELETS("Crafting sapphire bracelets", Category.SKILLING_CRAFTING, ActivityIntensity.LOW), + BUYING_IRON_ORE("Buying iron ore", Category.COLLECTING, ActivityIntensity.MODERATE), + SMITHING_BRONZE_DART_TIPS("Smithing bronze dart tips", Category.SKILLING_SMITHING, ActivityIntensity.LOW), + CLEANING_GRIMY_CADANTINE("Cleaning grimy cadantine", Category.SKILLING_HERBLORE, ActivityIntensity.HIGH), + CRAFTING_JADE_BRACELETS("Crafting jade bracelets", Category.SKILLING_CRAFTING, ActivityIntensity.LOW), + DEGRIMING_GRIMY_GUAM_LEAF("Degriming grimy guam leaf", Category.SKILLING_HERBLORE, ActivityIntensity.HIGH), + CHARGING_WATER_ORBS("Charging water orbs", Category.SKILLING_MAGIC, ActivityIntensity.LOW), + CATCHING_RAW_KARAMBWAN("Catching raw karambwan", Category.SKILLING_FISHING, ActivityIntensity.LOW), + BUYING_TEAM_CAPES("Buying team capes", Category.COLLECTING, ActivityIntensity.LOW), + MAKING_DYES("Making dyes", Category.PROCESSING, ActivityIntensity.HIGH), + CHARGING_EARTH_ORBS("Charging earth orbs", Category.SKILLING_MAGIC, ActivityIntensity.MODERATE), + CRAFTING_RUBY_JEWELLERY("Crafting ruby jewellery", Category.SKILLING_CRAFTING, ActivityIntensity.LOW), + CRAFTING_CLOCKWORK_TELEPORT_METHOD("Crafting clockwork (teleport method)", Category.SKILLING_CRAFTING, ActivityIntensity.HIGH), + MAKING_PIE_SHELLS("Making pie shells", Category.PROCESSING, ActivityIntensity.LOW), + KILLING_SPIDINES("Killing spidines", Category.COMBAT_LOW, ActivityIntensity.LOW), + CATCHING_ANGLERFISH("Catching anglerfish", Category.SKILLING_FISHING, ActivityIntensity.MODERATE), + HUNTING_CHINCHOMPAS("Hunting chinchompas", Category.SKILLING_HUNTER, ActivityIntensity.MODERATE), + FLETCHING_DIAMOND_BOLTS("Fletching diamond bolts", Category.SKILLING_FLETCHING, ActivityIntensity.LOW), + CRAFTING_MUD_RUNES("Crafting mud runes", Category.SKILLING_RUNECRAFT, ActivityIntensity.MODERATE), + CRAFTING_EMERALD_BRACELETS("Crafting emerald bracelets", Category.SKILLING_CRAFTING, ActivityIntensity.LOW), + COLLECTING_STEEL_PLATEBODIES_HIGH_ALCHEMY("Collecting steel platebodies (High Alchemy)", Category.SKILLING_MAGIC, ActivityIntensity.MODERATE), + CRAFTING_DIAMOND_JEWELLERY("Crafting diamond jewellery", Category.SKILLING_CRAFTING, ActivityIntensity.LOW), + MAKING_TEAK_PLANKS("Making teak planks", Category.PROCESSING, ActivityIntensity.MODERATE), + COOKING_RAW_SHARKS("Cooking raw sharks", Category.SKILLING_COOKING, ActivityIntensity.LOW), + CUTTING_RUBY_BOLT_TIPS("Cutting ruby bolt tips", Category.PROCESSING, ActivityIntensity.LOW), + CRAFTING_RUNES_AT_OURANIA_ALTAR("Crafting runes at Ourania Altar", Category.SKILLING_RUNECRAFT, ActivityIntensity.MODERATE), + COLLECTING_BLACK_SCIMITARS_FROM_ARDOUGNE_CASTLE("Collecting black scimitars from Ardougne Castle", Category.COLLECTING, ActivityIntensity.LOW), + MAKING_UNCOOKED_BERRY_PIES("Making uncooked berry pies", Category.PROCESSING, ActivityIntensity.LOW), + PICKING_BANANAS("Picking bananas", Category.COLLECTING, ActivityIntensity.MODERATE), + ENCHANTING_DIAMOND_NECKLACES("Enchanting diamond necklaces", Category.SKILLING_MAGIC, ActivityIntensity.MODERATE), + KILLING_THE_CRAZY_ARCHAEOLOGIST("Killing the Crazy archaeologist", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + KILLING_FERAL_VAMPYRES("Killing Feral Vampyres", Category.COMBAT_MID, ActivityIntensity.LOW), + COLLECTING_ANTI_DRAGON_SHIELDS_FREE_TO_PLAY("Collecting anti-dragon shields (free-to-play)", Category.COLLECTING, ActivityIntensity.MODERATE), + STRINGING_MAPLE_LONGBOWS("Stringing maple longbows", Category.SKILLING_FLETCHING, ActivityIntensity.MODERATE), + SMITHING_IRON_DART_TIPS("Smithing iron dart tips", Category.SKILLING_SMITHING, ActivityIntensity.LOW), + HIGH_ALCHING_UNFINISHED_RUNITE_CROSSBOWS_AT_THE_FOUNTAIN_OF_RUNE("High alching unfinished runite crossbows at the fountain of rune", Category.SKILLING_MAGIC, ActivityIntensity.LOW), + KILLING_RED_DRAGONS("Killing red dragons", Category.COMBAT_MID, ActivityIntensity.MODERATE), + COLLECTING_JANGERBERRIES("Collecting jangerberries", Category.COLLECTING, ActivityIntensity.LOW), + GRINDING_CHOCOLATE_BARS("Grinding chocolate bars", Category.PROCESSING, ActivityIntensity.MODERATE), + CRAFTING_EMERALD_JEWELLERY("Crafting emerald jewellery", Category.SKILLING_CRAFTING, ActivityIntensity.LOW), + MAKING_GLOVES_OF_SILENCE("Making gloves of silence", Category.PROCESSING_MAGIC, ActivityIntensity.MODERATE), + CATCHING_MINNOWS("Catching minnows", Category.SKILLING_FISHING, ActivityIntensity.MODERATE), + KILLING_BLACK_DRAGONS("Killing black dragons", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + STRINGING_MAGIC_LONGBOWS("Stringing magic longbows", Category.SKILLING_FLETCHING, ActivityIntensity.MODERATE), + HIGH_ALCHING_ATLATL_DARTS_AT_THE_FOUNTAIN_OF_RUNE("High alching atlatl darts at the fountain of rune", Category.SKILLING_MAGIC, ActivityIntensity.LOW), + MAKING_UNCOOKED_APPLE_PIES("Making uncooked apple pies", Category.PROCESSING, ActivityIntensity.LOW), + KILLING_AVIANSIES("Killing aviansies", Category.COMBAT_MID, ActivityIntensity.MODERATE), + KILLING_CAVE_HORRORS("Killing cave horrors", Category.COMBAT_MID, ActivityIntensity.MODERATE), + COOKING_PLAIN_PIZZAS("Cooking plain pizzas", Category.SKILLING_COOKING, ActivityIntensity.LOW), + CLEANING_GRIMY_SNAPDRAGON("Cleaning grimy snapdragon", Category.SKILLING_HERBLORE, ActivityIntensity.HIGH), + HIGH_ALCHING_RUNE_ARROWS_AT_THE_FOUNTAIN_OF_RUNE("High alching rune arrows at the fountain of rune", Category.SKILLING_MAGIC, ActivityIntensity.LOW), + KILLING_SPIRITUAL_MAGES("Killing spiritual mages", Category.COMBAT_HIGH, ActivityIntensity.MODERATE), + CLEANING_GRIMY_TOADFLAX("Cleaning grimy toadflax", Category.SKILLING_HERBLORE, ActivityIntensity.HIGH), + COLLECTING_PLANKS("Collecting planks", Category.COLLECTING, ActivityIntensity.LOW), + COLLECTING_RED_SPIDERS_EGGS("Collecting red spiders eggs", Category.COLLECTING, ActivityIntensity.LOW), + ENCHANTING_SAPPHIRE_RINGS("Enchanting sapphire rings", Category.SKILLING_MAGIC, ActivityIntensity.MODERATE), + CATCHING_SACRED_EELS("Catching sacred eels", Category.SKILLING_FISHING, ActivityIntensity.LOW), + CLIMBING_THE_AGILITY_PYRAMID("Climbing the Agility Pyramid", Category.SKILLING_AGILITY, ActivityIntensity.HIGH), + CRAFTING_STEAM_RUNES("Crafting steam runes", Category.SKILLING_RUNECRAFT, ActivityIntensity.MODERATE), + MAKING_ANCHOVY_PIZZAS("Making anchovy pizzas", Category.SKILLING_COOKING, ActivityIntensity.MODERATE), + CRAFTING_GOLD_BRACELETS("Crafting gold bracelets", Category.SKILLING_CRAFTING, ActivityIntensity.LOW), + KILLING_BRUTAL_GREEN_DRAGONS("Killing brutal green dragons", Category.COMBAT_HIGH, ActivityIntensity.HIGH), + STRINGING_YEW_LONGBOWS("Stringing yew longbows", Category.SKILLING_FLETCHING, ActivityIntensity.MODERATE), + KILLING_BRYOPHYTA_FREE_TO_PLAY("Killing Bryophyta (free-to-play)", Category.COMBAT_MID, ActivityIntensity.MODERATE), + COLLECTING_WHITE_BERRIES("Collecting white berries", Category.COLLECTING, ActivityIntensity.LOW), + COLLECTING_ANTI_DRAGON_SHIELDS("Collecting anti-dragon shields", Category.COLLECTING, ActivityIntensity.HIGH), + CUTTING_MAHOGANY_LOGS("Cutting mahogany logs", Category.SKILLING_WOODCUTTING, ActivityIntensity.LOW), + KILLING_CHAOS_DRUIDS("Killing chaos druids", Category.COMBAT_LOW, ActivityIntensity.LOW), + COLLECTING_FISH_FOOD("Collecting fish food", Category.COLLECTING, ActivityIntensity.MODERATE), + CATCHING_INFERNAL_EELS("Catching infernal eels", Category.SKILLING_FISHING, ActivityIntensity.LOW), + MINING_VOLCANIC_ASH("Mining volcanic ash", Category.SKILLING_MINING, ActivityIntensity.LOW), + CUTTING_YEW_LOGS_FREE_TO_PLAY("Cutting yew logs (free-to-play)", Category.SKILLING_WOODCUTTING, ActivityIntensity.LOW), + FORGING_GIANT_SWORDS("Forging Giant swords", Category.SKILLING_SMITHING, ActivityIntensity.MODERATE), + COOKING_RAW_MONKFISH("Cooking raw monkfish", Category.SKILLING_COOKING, ActivityIntensity.LOW), + MINING_ADAMANTITE_ORE("Mining adamantite ore", Category.SKILLING_MINING, ActivityIntensity.LOW), + SPINNING_FLAX_TO_BOW_STRINGS("Spinning flax to bow strings", Category.PROCESSING, ActivityIntensity.LOW), + BUYING_PIES("Buying pies", Category.COLLECTING, ActivityIntensity.MODERATE), + TANNING_COWHIDE("Tanning cowhide", Category.PROCESSING, ActivityIntensity.LOW), + KILLING_BRINE_RATS("Killing brine rats", Category.COMBAT_MID, ActivityIntensity.MODERATE), + CRAFTING_SAPPHIRE_JEWELLERY("Crafting sapphire jewellery", Category.SKILLING_CRAFTING, ActivityIntensity.LOW), + COLLECTING_BRONZE_PICKAXES("Collecting bronze pickaxes", Category.COLLECTING, ActivityIntensity.HIGH), + KILLING_OGRESS_SHAMANS("Killing ogress shamans", Category.COMBAT_HIGH, ActivityIntensity.MODERATE), + SMITHING_MITHRIL_DART_TIPS("Smithing mithril dart tips", Category.SKILLING_SMITHING, ActivityIntensity.LOW), + COLLECTING_WINE_OF_ZAMORAK("Collecting wine of zamorak", Category.COLLECTING, ActivityIntensity.MODERATE), + MAKING_UNCOOKED_MEAT_PIES("Making uncooked meat pies", Category.PROCESSING, ActivityIntensity.LOW), + CLEANING_GRIMY_IRIT_LEAVES("Cleaning grimy irit leaves", Category.SKILLING_HERBLORE, ActivityIntensity.HIGH), + CLEANING_GRIMY_HARRALANDER("Cleaning grimy harralander", Category.SKILLING_HERBLORE, ActivityIntensity.HIGH), + CREATING_BONES_TO_PEACHES_TABLETS("Creating bones to peaches tablets", Category.PROCESSING, ActivityIntensity.LOW), + COLLECTING_CHAOS_RUNES("Collecting chaos runes", Category.COLLECTING, ActivityIntensity.MODERATE), + CUTTING_MAGIC_LOGS("Cutting magic logs", Category.SKILLING_WOODCUTTING, ActivityIntensity.LOW), + FILLING_WATER_CONTAINERS_HOSIDIUS_KITCHEN("Filling water containers (Hosidius kitchen)", Category.PROCESSING, ActivityIntensity.LOW), + COLLECTING_NATURE_RUNES("Collecting nature runes", Category.SKILLING_MAGIC, ActivityIntensity.MODERATE), + COLLECTING_RED_SPIDERS_EGGS_FREE_TO_PLAY("Collecting red spiders eggs (free-to-play)", Category.COLLECTING, ActivityIntensity.MODERATE), + MAKING_TUNA_POTATOES("Making tuna potatoes", Category.PROCESSING, ActivityIntensity.MODERATE), + CLEANING_GRIMY_TARROMIN("Cleaning grimy tarromin", Category.SKILLING_HERBLORE, ActivityIntensity.HIGH), + COLLECTING_TINDERBOXES("Collecting tinderboxes", Category.COLLECTING, ActivityIntensity.HIGH), + MAKING_PASTRY_DOUGH("Making pastry dough", Category.PROCESSING, ActivityIntensity.MODERATE), + MINING_ADAMANTITE_ORE_FREE_TO_PLAY("Mining adamantite ore (free-to-play)", Category.SKILLING_MINING, ActivityIntensity.LOW), + CLEANING_GRIMY_GUAM_LEAVES("Cleaning grimy guam leaves", Category.SKILLING_HERBLORE, ActivityIntensity.HIGH), + RUNNING_LAPS_OF_THE_CANIFIS_ROOFTOP_COURSE("Running laps of the Canifis rooftop course", Category.SKILLING_AGILITY, ActivityIntensity.LOW), + BAKING_POTATOES("Baking potatoes", Category.SKILLING_COOKING, ActivityIntensity.LOW), + MAKING_PIZZA_BASES("Making pizza bases", Category.PROCESSING, ActivityIntensity.HIGH), + COOKING_RAW_KARAMBWAN("Cooking raw karambwan", Category.SKILLING_COOKING, ActivityIntensity.LOW), + CREATING_ARDOUGNE_TELEPORT_TABLETS("Creating Ardougne teleport tablets", Category.PROCESSING, ActivityIntensity.LOW), + COLLECTING_COSMIC_RUNES("Collecting cosmic runes", Category.COLLECTING, ActivityIntensity.MODERATE), + BUYING_BRONZE_BARS("Buying bronze bars", Category.COLLECTING, ActivityIntensity.LOW), + FLETCHING_UNSTRUNG_MAPLE_LONGBOWS("Fletching unstrung maple longbows", Category.SKILLING_FLETCHING, ActivityIntensity.LOW), + COOKING_RAW_SWORDFISH("Cooking raw swordfish", Category.SKILLING_COOKING, ActivityIntensity.LOW), + SMELTING_RUNITE_BARS("Smelting runite bars", Category.SKILLING_SMITHING, ActivityIntensity.LOW), + COLLECTING_IRON_ORE("Collecting iron ore", Category.COLLECTING, ActivityIntensity.LOW), + CUTTING_REDWOOD_LOGS("Cutting redwood logs", Category.SKILLING_WOODCUTTING, ActivityIntensity.LOW), + COOKING_RAW_ANGLERFISH("Cooking raw anglerfish", Category.SKILLING_COOKING, ActivityIntensity.LOW), + MINING_IRON_ORE_FREE_TO_PLAY("Mining iron ore (free-to-play)", Category.SKILLING_MINING, ActivityIntensity.HIGH), + MINING_CLAY_MEMBERS("Mining clay members", Category.SKILLING_MINING, ActivityIntensity.MODERATE), + KILLING_IMPS("Killing imps", Category.COMBAT_LOW, ActivityIntensity.LOW), + COLLECTING_BIG_BONES_FROM_THE_BONE_YARD("Collecting big bones from the Bone Yard", Category.COLLECTING, ActivityIntensity.MODERATE), + KILLING_MONKEYS("Killing monkeys", Category.COMBAT_LOW, ActivityIntensity.LOW), + CATCHING_SHRIMP_AND_ANCHOVIES("Catching shrimp & anchovies", Category.SKILLING_FISHING, ActivityIntensity.LOW), + COLLECTING_ASHES("Collecting ashes", Category.COLLECTING, ActivityIntensity.LOW), + KILLING_OGRESS_WARRIORS("Killing ogress warriors", Category.COMBAT_MID, ActivityIntensity.LOW), + COLLECTING_AND_TANNING_COWHIDE("Collecting and tanning cowhide", Category.COLLECTING, ActivityIntensity.LOW), + BUYING_ALE_FROM_THE_RISING_SUN_INN("Buying ale from the Rising Sun Inn", Category.COLLECTING, ActivityIntensity.HIGH), + KILLING_HILL_GIANTS("Killing hill giants", Category.COMBAT_LOW, ActivityIntensity.MODERATE), + COLLECTING_STEEL_PLATEBODIES("Collecting steel platebodies", Category.COLLECTING, ActivityIntensity.MODERATE), + COOKING_RAW_LOBSTER("Cooking raw lobster", Category.SKILLING_COOKING, ActivityIntensity.LOW), + KILLING_COWS_AND_TANNING_COWHIDE("Killing cows and tanning cowhide", Category.COMBAT_LOW, ActivityIntensity.LOW), + BUYING_JUG_PACKS("Buying jug packs", Category.COLLECTING, ActivityIntensity.LOW), + KILLING_CHAOS_DWARVES("Killing chaos dwarves", Category.COMBAT_MID, ActivityIntensity.LOW), + FILLING_WATER_CONTAINERS("Filling water containers", Category.PROCESSING, ActivityIntensity.LOW), + COLLECTING_WINE_OF_ZAMORAK_FREE_TO_PLAY("Collecting wine of zamorak (free-to-play)", Category.COLLECTING, ActivityIntensity.LOW), + MAKING_DOUGH_AT_COOKS_GUILD("Making dough at Cooks Guild", Category.PROCESSING, ActivityIntensity.HIGH), + SMELTING_BRONZE_BARS("Smelting bronze bars", Category.SKILLING_SMITHING, ActivityIntensity.LOW), + MINING_CLAY("Mining clay", Category.SKILLING_MINING, ActivityIntensity.LOW), + CRAFTING_GOLD_JEWELLERY("Crafting gold jewellery", Category.SKILLING_CRAFTING, ActivityIntensity.LOW), + BUYING_KEBABS_IN_AL_KHARID("Buying kebabs in Al Kharid", Category.COLLECTING, ActivityIntensity.HIGH), + STEALING_WYDINS_BANANAS("Stealing Wydins bananas", Category.COLLECTING, ActivityIntensity.HIGH), + COLLECTING_GARLIC("Collecting garlic", Category.COLLECTING, ActivityIntensity.MODERATE), + COOKING_RAW_TUNA("Cooking raw tuna", Category.SKILLING_COOKING, ActivityIntensity.LOW), + COLLECTING_SPADES("Collecting spades", Category.COLLECTING, ActivityIntensity.MODERATE), + CUTTING_WILLOW_LOGS("Cutting willow logs", Category.SKILLING_WOODCUTTING, ActivityIntensity.LOW), + CUTTING_OAK_LOGS("Cutting oak logs", Category.SKILLING_WOODCUTTING, ActivityIntensity.LOW), + CATCHING_TROUT_AND_SALMON("Catching trout & salmon", Category.SKILLING_FISHING, ActivityIntensity.LOW), + KILLING_ZAMORAK_MONKS("Killing Zamorak monks", Category.COMBAT_LOW, ActivityIntensity.MODERATE), + MINING_COAL_AND_SUPERHEATING_ADAMANTITE_BARS("Mining coal and superheating Adamantite bars", Category.SKILLING_MINING, ActivityIntensity.MODERATE), + SMELTING_SILVER_BARS("Smelting silver bars", Category.SKILLING_SMITHING, ActivityIntensity.LOW), + KILLING_CHICKENS("Killing chickens", Category.COMBAT_LOW, ActivityIntensity.LOW), + SHEARING_SHEEP("Shearing sheep", Category.COLLECTING, ActivityIntensity.LOW), + PICKING_POTATOES("Picking potatoes", Category.COLLECTING, ActivityIntensity.LOW), + KILLING_HOBGOBLINS("Killing hobgoblins", Category.COMBAT_MID, ActivityIntensity.MODERATE), + MINING_GOLD_ORE_CRAFTING_GUILD("Mining gold ore (Crafting Guild)", Category.SKILLING_MINING, ActivityIntensity.LOW), + KILLING_WIZARDS("Killing wizards", Category.COMBAT_LOW, ActivityIntensity.LOW), + KILLING_BEARS("Killing bears", Category.COMBAT_MID, ActivityIntensity.MODERATE), + MINING_COAL_AND_SUPERHEATING_MITHRIL_BARS("Mining coal and superheating Mithril bars", Category.SKILLING_MINING, ActivityIntensity.MODERATE), + MINING_COAL_FREE_TO_PLAY("Mining coal (free-to-play)", Category.SKILLING_MINING, ActivityIntensity.MODERATE), + SMELTING_IRON_BARS("Smelting iron bars", Category.SKILLING_SMITHING, ActivityIntensity.LOW), + SMELTING_STEEL_BARS("Smelting steel bars", Category.SKILLING_SMITHING, ActivityIntensity.LOW), + KILLING_ANKOU_FREE_TO_PLAY("Killing Ankou (free-to-play)", Category.COMBAT_MID, ActivityIntensity.MODERATE), + BUYING_FROM_WYDINS_FOOD_STORE("Buying from Wydins Food Store", Category.COLLECTING, ActivityIntensity.HIGH), + COLLECTING_BEER_GLASSES("Collecting beer glasses", Category.COLLECTING, ActivityIntensity.LOW), + MAKING_FLOUR("Making flour", Category.PROCESSING, ActivityIntensity.LOW), + COLLECTING_AIR_TALISMANS("Collecting air talismans", Category.COLLECTING, ActivityIntensity.MODERATE), + KILLING_DARK_WIZARDS("Killing dark wizards", Category.COMBAT_LOW, ActivityIntensity.LOW), + MINING_GOLD_ORE_FREE_TO_PLAY("Mining gold ore (free-to-play)", Category.SKILLING_MINING, ActivityIntensity.LOW), + GETTING_INFINITE_MONEY("Getting infinite money", Category.COLLECTING_NONE, ActivityIntensity.HIGH); + + @Getter + private final String method; + @Getter + private final Category category; + private final ActivityIntensity intensity; + + Activity(String method, Category category, ActivityIntensity intensity) { + this.method = method; + this.category = category; + this.intensity = intensity; + } + + public static Activity fromSkill(Skill skill) { + switch (skill) { + case ATTACK: + case DEFENCE: + case STRENGTH: + case RANGED: + case MAGIC: + return GENERAL_COMBAT; + case PRAYER: + return GENERAL_PRAYER; + case CONSTRUCTION: + return GENERAL_CONSTRUCTION; + case COOKING: + return GENERAL_COOKING; + case WOODCUTTING: + return GENERAL_WOODCUTTING; + case FLETCHING: + return GENERAL_FLETCHING; + case FISHING: + return GENERAL_FISHING; + case FIREMAKING: + return GENERAL_FIREMAKING; + case CRAFTING: + return GENERAL_CRAFTING; + case SMITHING: + return GENERAL_SMITHING; + case MINING: + return GENERAL_MINING; + case HERBLORE: + return GENERAL_HERBLORE; + case AGILITY: + return GENERAL_AGILITY; + case THIEVING: + return GENERAL_THIEVING; + case SLAYER: + return GENERAL_SLAYER; + case RUNECRAFT: + return GENERAL_RUNECRAFT; + case HUNTER: + return GENERAL_HUNTER; + case FARMING: + return GENERAL_FARMING; + default: + return null; + } + } + + public ActivityIntensity getActivityIntensity() { + return intensity; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/ActivityIntensity.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/ActivityIntensity.java new file mode 100644 index 0000000000..d92891b4aa --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/ActivityIntensity.java @@ -0,0 +1,65 @@ +package net.runelite.client.plugins.microbot.util.antiban.enums; + +import lombok.Getter; +import net.runelite.api.Skill; + +public enum ActivityIntensity { + VERY_LOW("Very Low", 0.6, 1.8, PlayStyle.PASSIVE), + LOW("Low", 0.4, 1.5, PlayStyle.CAUTIOUS), + MODERATE("Moderate", 0.3, 1.2, PlayStyle.AGGRESSIVE), + HIGH("High", 0.2, 1.1, PlayStyle.EXTREME_AGGRESSIVE), + EXTREME("Extreme", 0.1, 1.0, PlayStyle.EXTREME_AGGRESSIVE); + + @Getter + private final String name; + @Getter + private final double frequency; + @Getter + private final double amplitude; + @Getter + private final PlayStyle playStyle; + + + ActivityIntensity(String name, double frequency, double amplitude, PlayStyle playStyle) { + this.name = name; + this.frequency = frequency; + this.amplitude = amplitude; + this.playStyle = playStyle; + } + + public static ActivityIntensity random() { + return values()[(int) (Math.random() * values().length - 1)]; + } + + public static ActivityIntensity fromSkill(Skill skill) { + switch (skill) { + case ATTACK: + case DEFENCE: + case STRENGTH: + case RANGED: + case PRAYER: + case MAGIC: + case CONSTRUCTION: + return HIGH; + case COOKING: + case WOODCUTTING: + case FLETCHING: + case FISHING: + case FIREMAKING: + case CRAFTING: + case SMITHING: + case MINING: + case HERBLORE: + return LOW; + case AGILITY: + case THIEVING: + case SLAYER: + case RUNECRAFT: + case HUNTER: + case FARMING: + return MODERATE; + default: + return null; + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/Category.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/Category.java new file mode 100644 index 0000000000..f8c9189328 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/Category.java @@ -0,0 +1,249 @@ +package net.runelite.client.plugins.microbot.util.antiban.enums; + +import net.runelite.api.AnimationID; +import net.runelite.client.plugins.microbot.Microbot; +import net.runelite.client.plugins.microbot.util.antiban.AntibanPlugin; +import net.runelite.client.plugins.microbot.util.antiban.Rs2Antiban; +import net.runelite.client.plugins.microbot.util.combat.Rs2Combat; +import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; +import net.runelite.client.plugins.microbot.util.player.Rs2Player; + +public enum Category { + COMBAT_MID("Combat/Mid") { + @Override + public boolean isBusy() { + return Rs2Combat.inCombat(); + } + }, + SKILLING_AGILITY("Skilling/Agility") { + @Override + public boolean isBusy() { + return Rs2Player.isMoving(); + } + }, + PROCESSING("Processing") { + @Override + public boolean isBusy() { + return Rs2Player.isAnimating() || Microbot.isGainingExp; + } + }, + COLLECTING("Collecting") { + @Override + public boolean isBusy() { + return Rs2Player.isMoving() || Rs2Player.isInteracting(); + } + }, + SKILLING_CRAFTING("Skilling/Crafting") { + @Override + public boolean isBusy() { + return !Rs2Antiban.isIdle(); + } + }, + SKILLING_MAGIC("Skilling/Magic") { + @Override + public boolean isBusy() { + return Rs2Player.isAnimating() || Microbot.isGainingExp; + } + }, + COMBAT_LOW("Combat/Low") { + @Override + public boolean isBusy() { + return Rs2Combat.inCombat(); + } + }, + SKILLING_HERBLORE("Skilling/Herblore") { + @Override + public boolean isBusy() { + return !Rs2Antiban.isIdle() || Microbot.isGainingExp; + } + }, + SKILLING_FLETCHING("Skilling/Fletching") { + @Override + public boolean isBusy() { + return !Rs2Antiban.isIdle(); + } + }, + SKILLING_FISHING("Skilling/Fishing") { + @Override + public boolean isBusy() { + return Rs2Player.isInteracting(); + } + }, + PROCESSING_MAGIC("Processing/Magic") { + @Override + public boolean isBusy() { + return Rs2Player.isAnimating() && Microbot.isGainingExp; + } + }, + SKILLING_COOKING("Skilling/Cooking") { + @Override + public boolean isBusy() { + return AntibanPlugin.isCooking(); + } + }, + SKILLING_FIREMAKING("Skilling/Firemaking") { + @Override + public boolean isBusy() { + return Rs2Player.getPoseAnimation() == AnimationID.FIREMAKING; + } + }, + SKILLING_THIEVING("Skilling/Thieving") { + @Override + public boolean isBusy() { + return !Rs2Player.isAnimating() || Rs2Inventory.isFull(); + } + }, + SKILLING("Skilling") { + @Override + public boolean isBusy() { + return !Rs2Player.isAnimating() || !Rs2Inventory.isFull(); + } + }, + COLLECTING_NONE("Collecting/None") { + @Override + public boolean isBusy() { + return Rs2Player.isMoving(); + } + }, + COMBAT_HIGH("Combat/High") { + @Override + public boolean isBusy() { + return Rs2Combat.inCombat(); + } + }, + SKILLING_WOODCUTTING("Skilling/Woodcutting") { + @Override + public boolean isBusy() { + return Rs2Antiban.isWoodcutting(); + } + }, + SKILLING_MINING("Skilling/Mining") { + @Override + public boolean isBusy() { + return AntibanPlugin.isMining(); + } + }, + SKILLING_RUNECRAFT("Skilling/Runecraft") { + @Override + public boolean isBusy() { + return Rs2Inventory.contains("pure essence", "rune essence", "Daeyalt essence", "Dark essence fragment", "blood essence"); + } + }, + SKILLING_SMITHING("Skilling/Smithing") { + /** + * Checks if the player is busy. + *

+ * TODO: This method has not been implemented correctly yet. + * It currently only checks if the player is animating or if the inventory is full. + * Additional conditions may need to be added to accurately determine if the player is busy. + * Consider adding checks for: + *

    + *
  • Whether the player is interacting with specific game objects.
  • + *
  • Other animations that might indicate a busy state.
  • + *
  • Game-specific conditions that represent the player being busy.
  • + *
+ * + * @return {@code true} if the player is animating or the inventory is full; {@code false} otherwise. + */ + @Override + public boolean isBusy() { + return !Rs2Antiban.isIdle(); + } + }, + SKILLING_HUNTER("Skilling/Hunter") { + /** + * Checks if the player is busy. + *

+ * TODO: This method has not been implemented correctly yet. + * It currently only checks if the player is animating or if the inventory is full. + * Additional conditions may need to be added to accurately determine if the player is busy. + * Consider adding checks for: + *

    + *
  • Whether the player is interacting with specific game objects.
  • + *
  • Other animations that might indicate a busy state.
  • + *
  • Game-specific conditions that represent the player being busy.
  • + *
+ * + * @return {@code true} if the player is animating or the inventory is full; {@code false} otherwise. + */ + @Override + public boolean isBusy() { + return !Rs2Antiban.isIdle(); + } + }, + SKILLING_FARMING("Skilling/Farming") { + /** + * Checks if the player is busy. + *

+ * TODO: This method has not been implemented correctly yet. + * It currently only checks if the player is animating or if the inventory is full. + * Additional conditions may need to be added to accurately determine if the player is busy. + * Consider adding checks for: + *

    + *
  • Whether the player is interacting with specific game objects.
  • + *
  • Other animations that might indicate a busy state.
  • + *
  • Game-specific conditions that represent the player being busy.
  • + *
+ * + * @return {@code true} if the player is animating or the inventory is full; {@code false} otherwise. + */ + @Override + public boolean isBusy() { + return Rs2Player.isAnimating() || Rs2Inventory.isFull(); + } + }, + SKILLING_PRAYER("Skilling/Prayer") { + /** + * Checks if the player is busy. + *

+ * TODO: This method has not been implemented correctly yet. + * It currently only checks if the player is animating or if the inventory is full. + * Additional conditions may need to be added to accurately determine if the player is busy. + * Consider adding checks for: + *

    + *
  • Whether the player is interacting with specific game objects.
  • + *
  • Other animations that might indicate a busy state.
  • + *
  • Game-specific conditions that represent the player being busy.
  • + *
+ * + * @return {@code true} if the player is animating or the inventory is full; {@code false} otherwise. + */ + @Override + public boolean isBusy() { + return Rs2Player.isAnimating() || Rs2Inventory.isFull(); + } + }, + SKILLING_CONSTRUCTION("Skilling/Construction") { + /** + * Checks if the player is busy. + *

+ * TODO: This method has not been implemented correctly yet. + * It currently only checks if the player is animating or if the inventory is full. + * Additional conditions may need to be added to accurately determine if the player is busy. + * Consider adding checks for: + *

    + *
  • Whether the player is interacting with specific game objects.
  • + *
  • Other animations that might indicate a busy state.
  • + *
  • Game-specific conditions that represent the player being busy.
  • + *
+ * + * @return {@code true} if the player is animating or the inventory is full; {@code false} otherwise. + */ + @Override + public boolean isBusy() { + return Rs2Player.isAnimating() || Rs2Inventory.isFull(); + } + }; + + private final String name; + + Category(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public abstract boolean isBusy(); +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/PlaySchedule.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/PlaySchedule.java new file mode 100644 index 0000000000..bc5b765661 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/PlaySchedule.java @@ -0,0 +1,47 @@ +package net.runelite.client.plugins.microbot.util.antiban.enums; + +import java.time.Duration; +import java.time.LocalTime; + +public enum PlaySchedule { + SHORT_MORNING(LocalTime.of(8, 0), LocalTime.of(9, 0)), + MEDIUM_MORNING(LocalTime.of(7, 0), LocalTime.of(10, 0)), + LONG_MORNING(LocalTime.of(6, 0), LocalTime.of(12, 0)), + + SHORT_AFTERNOON(LocalTime.of(12, 0), LocalTime.of(13, 0)), + MEDIUM_AFTERNOON(LocalTime.of(12, 0), LocalTime.of(15, 0)), + LONG_AFTERNOON(LocalTime.of(12, 0), LocalTime.of(18, 0)), + + SHORT_EVENING(LocalTime.of(18, 0), LocalTime.of(19, 0)), + MEDIUM_EVENING(LocalTime.of(17, 0), LocalTime.of(20, 0)), + LONG_EVENING(LocalTime.of(17, 0), LocalTime.of(23, 0)), + + SHORT_DAY(LocalTime.of(9, 0), LocalTime.of(17, 0)), + MEDIUM_DAY(LocalTime.of(8, 0), LocalTime.of(18, 0)), + LONG_DAY(LocalTime.of(6, 0), LocalTime.of(22, 0)); + + private final LocalTime startTime; + private final LocalTime endTime; + + PlaySchedule(LocalTime startTime, LocalTime endTime) { + this.startTime = startTime; + this.endTime = endTime; + } + + public boolean isOutsideSchedule() { + LocalTime currentTime = LocalTime.now(); + return currentTime.isBefore(startTime) || currentTime.isAfter(endTime); + } + + public Duration timeUntilNextSchedule() { + LocalTime currentTime = LocalTime.now(); + if (currentTime.isBefore(startTime)) { + return Duration.between(currentTime, startTime); + } else if (currentTime.isAfter(endTime)) { + return Duration.between(currentTime, startTime).plusDays(1); + } else { + return Duration.ZERO; + } + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/PlayStyle.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/PlayStyle.java new file mode 100644 index 0000000000..0fc52c1b9a --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/PlayStyle.java @@ -0,0 +1,184 @@ +package net.runelite.client.plugins.microbot.util.antiban.enums; + +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.plugins.microbot.Microbot; + +import java.time.Duration; +import java.time.Instant; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +@Slf4j +public enum PlayStyle { + EXTREME_AGGRESSIVE("Extreme Aggressive", 1, 3, 2, 0.00), // Almost no break between inputs + AGGRESSIVE("Aggressive", 2, 5, 5, 0.01), // Very short breaks + MODERATE("Moderate", 3, 9, 10, 0.08), // Short breaks + BALANCED("Balanced", 7, 15, 14, 0.12), // Moderate breaks + CAREFUL("Careful", 12, 21, 14, 0.17), // Longer breaks + CAUTIOUS("Cautious", 17, 30, 14, 0.23), // Very long breaks + PASSIVE("Passive", 20, 35, 14, 0.4), // Minimal actions + RANDOM("Random", 1, 35, 25, 0.00); // Random actions + + + @Getter + private final String name; + private final int originalPrimaryTickInterval; + private final int originalSecondaryTickInterval; + private final int baseDuration; // Base duration for the attention span + private final double refocusProbability; // Probability of refocusing + @Getter + @Setter + public double frequency; + @Getter + @Setter + public double amplitude; + @Setter + public Instant startTime; // Start time for the current playstyle + @Getter + private int primaryTickInterval; + @Getter + private int secondaryTickInterval; + private double phase; // Phase angle for the sine function + private int attentionSpan; // Attention span for the current playstyle + + PlayStyle(String name, int primaryTickInterval, int secondaryTickInterval, int baseDuration, double refocusProbability) { + this.name = name; + this.originalPrimaryTickInterval = primaryTickInterval; + this.originalSecondaryTickInterval = secondaryTickInterval; + this.primaryTickInterval = primaryTickInterval; + this.secondaryTickInterval = secondaryTickInterval; + this.refocusProbability = refocusProbability; + this.phase = 0.0; + this.frequency = 0.2; + this.amplitude = 2.0; + this.startTime = Instant.now(); + this.baseDuration = baseDuration; + this.attentionSpan = generateAttentionSpan(baseDuration); + } + + // Return a random PlayStyle + public static PlayStyle random() { + return values()[(int) (Math.random() * values().length)]; + } + + public int getRandomTickInterval() { + return ThreadLocalRandom.current().nextInt(primaryTickInterval, secondaryTickInterval + 1); + } + + // Method to evolve the play style over time using a sine function + public void evolvePlayStyle() { + phase += frequency; + primaryTickInterval = adjustInterval(primaryTickInterval, amplitude); + log.info("Primary tick interval: {}", primaryTickInterval); + secondaryTickInterval = adjustInterval(secondaryTickInterval, amplitude); + log.info("Secondary tick interval: {}", secondaryTickInterval); + Microbot.log("Slightly adjusting playStyle intervals."); + } + + // Helper method to adjust intervals using the sine of the phase + private int adjustInterval(int interval, double amplitude) { + int change = (int) (amplitude * Math.sin(phase)); + interval += change; + // Ensure intervals remain within logical boundaries + return Math.max(1, interval); + } + + // Method to reset the intervals to their original values + public void resetPlayStyle() { + this.primaryTickInterval = originalPrimaryTickInterval; + this.secondaryTickInterval = originalSecondaryTickInterval; + this.phase = 0.0; // Reset the phase as well + this.startTime = Instant.now(); // Reset start time + this.attentionSpan = generateAttentionSpan(this.baseDuration); // Generate new attention span + } + + // Switch profile to a new play style either one step up or down + public PlayStyle switchProfile() { + boolean up = Math.random() < 0.5; + boolean refocus = Math.random() < refocusProbability; + if (refocus) { + PlayStyle newProfile = values()[0]; + Microbot.log("Refocusing, switching to: " + newProfile.getName()); + return newProfile; + } + int index = this.ordinal(); + if (up) { + index++; + } else { + index--; + } + + // Check if index is out of bounds and invert the direction if necessary + if (index < 0 || index >= values().length - 1) { + // Invert the direction + up = !up; + // Reset index to the current ordinal value + index = this.ordinal(); + // Update the index again with the inverted direction + if (up) { + index = Math.min(values().length - 1, index + 1); + } else { + index = Math.max(0, index - 1); + } + } + + PlayStyle newProfile = values()[index]; + Microbot.log("Switched profile to: " + newProfile.getName()); + return newProfile; + } + + // Switch profile based on simulated attention span + public boolean shouldSwitchProfileBasedOnAttention() { + Instant currentTime = Instant.now(); + long elapsedTime = java.time.Duration.between(this.startTime, currentTime).getSeconds(); // elapsed time in seconds + + return elapsedTime >= this.attentionSpan; + } + + // Time left in the current play style + public String getTimeLeftUntilNextSwitch() { + Instant currentTime = Instant.now(); + long elapsedTime = Duration.between(this.startTime, currentTime).getSeconds(); // elapsed time in seconds + long timeLeft = (this.attentionSpan) - elapsedTime; + + if (timeLeft <= 0) { + return "00:00"; // switch should occur immediately + } + + long minutes = timeLeft / 60; + long seconds = timeLeft % 60; + + return String.format("%02d:%02d", minutes, seconds); + } + + // Generate attention span using normal and Poisson distributions + private int generateAttentionSpan(int baseDuration) { + // Define parameters for normal and Poisson distributions + double normalMean = baseDuration * 60; // mean of the normal distribution + double normalStdDev = normalMean * 0.2; // standard deviation of the normal distribution (20% of mean) + double poissonLambda = normalMean * 0.1; // lambda parameter for the Poisson distribution (10% of mean) + + // Generate random values from normal and Poisson distributions + Random random = new Random(); + double normalSample = random.nextGaussian() * normalStdDev + normalMean; + double poissonSample = nextPoisson(poissonLambda); + + // Combine the values to get the attention span + return (int) Math.max(1, normalSample + poissonSample); + } + + // Helper method to generate a Poisson-distributed random value + private int nextPoisson(double lambda) { + Random random = new Random(); + double l = Math.exp(-lambda); + int k = 0; + double p = 1.0; + do { + k++; + p *= random.nextDouble(); + } while (p > l); + return k - 1; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/bank/Rs2Bank.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/bank/Rs2Bank.java index 8d015f1fc7..4ce8162fe2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/bank/Rs2Bank.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/bank/Rs2Bank.java @@ -37,6 +37,7 @@ import static net.runelite.api.widgets.ComponentID.BANK_ITEM_CONTAINER; import static net.runelite.client.plugins.microbot.Microbot.updateItemContainer; import static net.runelite.client.plugins.microbot.util.Global.*; +import static net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory.itemBounds; @SuppressWarnings("unused") @Slf4j @@ -68,7 +69,7 @@ public static void invokeMenu(int entryIndex, Rs2Item rs2Item) { if (container == ComponentID.BANK_INVENTORY_ITEM_CONTAINER) { identifier = identifier + 1; } - Microbot.doInvoke(new NewMenuEntry(rs2Item.slot, container, MenuAction.CC_OP.getId(), identifier, rs2Item.id, rs2Item.name), new Rectangle(1, 1, Microbot.getClient().getCanvasWidth(), Microbot.getClient().getCanvasHeight())); + Microbot.doInvoke(new NewMenuEntry(rs2Item.slot, container, MenuAction.CC_OP.getId(), identifier, rs2Item.id, rs2Item.name), (itemBounds(rs2Item) == null) ? new Rectangle(1, 1) : itemBounds(rs2Item)); // MenuEntryImpl(getOption=Wear, getTarget=Amulet of glory(4), getIdentifier=9, getType=CC_OP_LOW_PRIORITY, getParam0=1, getParam1=983043, getItemId=1712, isForceLeftClick=false, isDeprioritized=false) // Rs2Reflection.invokeMenu(rs2Item.slot, container, MenuAction.CC_OP.getId(), identifier, rs2Item.id, "Withdraw-1", rs2Item.name, -1, -1); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java index 7ec9dd6d95..0bb7c05ea8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java @@ -7,6 +7,7 @@ import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.plugins.microbot.globval.enums.InterfaceTab; +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.equipment.Rs2Equipment; import net.runelite.client.plugins.microbot.util.gameobject.Rs2GameObject; @@ -23,8 +24,8 @@ import java.awt.*; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; -import java.util.Optional; import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -411,7 +412,8 @@ public static boolean dropAll() { items()) { if (item == null) continue; invokeMenu(item, "Drop"); - sleep(300, 600); + if (!Rs2AntibanSettings.naturalMouse) + sleep(150, 300); } return true; } @@ -427,7 +429,8 @@ public static boolean dropAll(int id) { items().stream().filter(x -> x.id == id).collect(Collectors.toList())) { if (item == null) continue; invokeMenu(item, "Drop"); - sleep(300, 600); + if (!Rs2AntibanSettings.naturalMouse) + sleep(150, 300); } return true; } @@ -443,7 +446,8 @@ public static boolean dropAll(Integer... ids) { items().stream().filter(x -> Arrays.stream(ids).anyMatch(id -> id == x.id)).collect(Collectors.toList())) { if (item == null) continue; invokeMenu(item, "Drop"); - sleep(300, 600); + if (!Rs2AntibanSettings.naturalMouse) + sleep(150, 300); } return true; } @@ -459,7 +463,8 @@ public static boolean dropAll(String name) { items().stream().filter(x -> x.name.equalsIgnoreCase(name)).collect(Collectors.toList())) { if (item == null) continue; invokeMenu(item, "Drop"); - sleep(300, 600); + if (!Rs2AntibanSettings.naturalMouse) + sleep(150, 300); } return true; } @@ -475,7 +480,8 @@ public static boolean dropAll(String... names) { items().stream().filter(x -> Arrays.stream(names).anyMatch(name -> name.equalsIgnoreCase(x.name))).collect(Collectors.toList())) { if (item == null) continue; invokeMenu(item, "Drop"); - sleep(300, 600); + if (!Rs2AntibanSettings.naturalMouse) + sleep(150, 300); } return true; } @@ -491,7 +497,8 @@ public static boolean dropAll(Predicate predicate) { items().stream().filter(predicate).collect(Collectors.toList())) { if (item == null) continue; invokeMenu(item, "Drop"); - sleep(150, 300); + if (!Rs2AntibanSettings.naturalMouse) + sleep(150, 300); } return true; } @@ -578,7 +585,8 @@ public static boolean dropAll(Predicate predicate, DropOrder dropOrder) for (Rs2Item item : itemsToDrop) { if (item == null) continue; invokeMenu(item, "Drop"); - sleep(150, 300); + if (!Rs2AntibanSettings.naturalMouse) + sleep(150, 300); } return true; } @@ -635,7 +643,8 @@ public static boolean dropAllExcept(Predicate predicate) { items().stream().filter(predicate).collect(Collectors.toList())) { if (item == null) continue; invokeMenu(item, "Drop"); - sleep(300, 600); + if (!Rs2AntibanSettings.naturalMouse) + sleep(150, 300); } return true; } @@ -667,7 +676,8 @@ public static boolean dropAllExcept(int gpValue, List ignoreItems) { if (totalPrice >= gpValue) continue; invokeMenu(item, "Drop"); - sleep(300, 600); + if (!Rs2AntibanSettings.naturalMouse) + sleep(150, 300); } return true; } @@ -1868,7 +1878,7 @@ public static void useRestoreEnergyItem() { * @return List of Potion Items in Inventory */ public static List getFilteredPotionItemsInInventory(String potionName) { - return getFilteredPotionItemsInInventory(Arrays.asList(potionName)); + return getFilteredPotionItemsInInventory(Collections.singletonList(potionName)); } /** @@ -2014,6 +2024,26 @@ public static boolean waitForInventoryChanges() { return isInventoryChanged; } + /** + * Moves the specified item to the specified slot in the inventory. + * + * @return + */ + public static boolean moveItemToSlot(Rs2Item item, int slot) { + if (item == null) return false; + if (slot < 0 || slot >= CAPACITY) return false; + if (item.slot == slot) return true; + + Widget inventory = getInventory(); + if (inventory == null) return false; + Rectangle itemBounds = itemBounds(item); + Rectangle slotBounds = inventory.getDynamicChildren()[slot].getBounds(); + + Microbot.drag(itemBounds, slotBounds); + + return true; + } + public static boolean dropEmptyVials() { return dropAll("empty vial"); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java new file mode 100644 index 0000000000..6d5fab51d9 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java @@ -0,0 +1,97 @@ +package net.runelite.client.plugins.microbot.util.misc; + +import net.runelite.api.NPC; +import net.runelite.api.Perspective; +import net.runelite.api.Point; +import net.runelite.api.TileObject; +import net.runelite.api.coords.LocalPoint; +import net.runelite.client.plugins.microbot.Microbot; + +import java.awt.*; +import java.util.Objects; +import java.util.Random; + +public class Rs2UiHelper { + public static boolean isRectangleWithinViewport(Rectangle rectangle) { + int viewportHeight = Microbot.getClient().getViewportHeight(); + int viewportWidth = Microbot.getClient().getViewportWidth(); + + return !(rectangle.getX() > (double) viewportWidth) && + !(rectangle.getY() > (double) viewportHeight) && + !(rectangle.getX() < 0.0) && + !(rectangle.getY() < 0.0); + } + + public static Point getClickingPoint(Rectangle rectangle, boolean randomize) { + if (rectangle.getX() == 1 && rectangle.getY() == 1) return new Point(1, 1); + if (rectangle.getX() == 0 && rectangle.getY() == 0) return new Point(1, 1); + //check if mouse is already within the rectangle and return current position + java.awt.Point mousePos = Microbot.getMouse().getMousePosition(); + if (rectangle.contains(mousePos)) return new Point(mousePos.x, mousePos.y); + + if (!randomize) return new Point((int) rectangle.getCenterX(), (int) rectangle.getCenterY()); + Random random = new Random(); + // Calculate mean and standard deviation for the normal distribution + double meanX = rectangle.getX() + rectangle.getWidth() / 2.0; + double meanY = rectangle.getY() + rectangle.getHeight() / 2.0; + double stddevX = rectangle.getWidth() / 6.0; + double stddevY = rectangle.getHeight() / 6.0; + + // Generate normally distributed random values + double normalX = random.nextGaussian() * stddevX + meanX; + double normalY = random.nextGaussian() * stddevY + meanY; + + // Add jitter with a small uniformly distributed random offset + double jitterRangeX = rectangle.getWidth() / 10.0; // Adjust the range as needed + double jitterRangeY = rectangle.getHeight() / 10.0; // Adjust the range as needed + double jitterX = random.nextDouble() * jitterRangeX - jitterRangeX / 2.0; + double jitterY = random.nextDouble() * jitterRangeY - jitterRangeY / 2.0; + + // Apply jitter to the normally distributed values + normalX += jitterX; + normalY += jitterY; + + // Clamp the values to ensure they lie within the rectangle + int x = (int) Math.max(rectangle.getX(), Math.min(normalX, rectangle.getX() + rectangle.getWidth() - 1)); + int y = (int) Math.max(rectangle.getY(), Math.min(normalY, rectangle.getY() + rectangle.getHeight() - 1)); + + return new Point(x, y); + } + + public static Rectangle getNpcClickbox(NPC npc) { + + LocalPoint lp = npc.getLocalLocation(); + if (lp == null) { + Microbot.log("LocalPoint is null"); + return new Rectangle(1, 1); + } + Shape clickbox = Microbot.getClientThread().runOnClientThread(() -> Perspective.getClickbox(Microbot.getClient(), npc.getModel(), npc.getCurrentOrientation(), lp.getX(), lp.getY(), + Perspective.getTileHeight(Microbot.getClient(), lp, npc.getWorldLocation().getPlane()))); + + + //check if any of the values are negative and return a new rectangle with positive values + assert clickbox != null; + if (clickbox.getBounds().getX() < 0 || clickbox.getBounds().getY() < 0 || clickbox.getBounds().getWidth() < 0 || clickbox.getBounds().getHeight() < 0) { + return new Rectangle((int) Math.abs(clickbox.getBounds().getX()), (int) Math.abs(clickbox.getBounds().getY()), (int) Math.abs(clickbox.getBounds().getWidth()), (int) Math.abs(clickbox.getBounds().getHeight())); + } + + return new Rectangle(clickbox.getBounds()); + } + + public static Rectangle getObjectClickbox(TileObject object) { + + if (object == null) return new Rectangle(1, 1); //return a small rectangle if object is null + Shape clickbox = Microbot.getClientThread().runOnClientThread(() -> Objects.requireNonNull(object.getClickbox())); + + + //check if any of the values are negative and return a new rectangle with positive values + assert clickbox != null; + if (clickbox.getBounds().getX() < 0 || clickbox.getBounds().getY() < 0 || clickbox.getBounds().getWidth() < 0 || clickbox.getBounds().getHeight() < 0) { + return new Rectangle((int) Math.abs(clickbox.getBounds().getX()), (int) Math.abs(clickbox.getBounds().getY()), (int) Math.abs(clickbox.getBounds().getWidth()), (int) Math.abs(clickbox.getBounds().getHeight())); + } + + return new Rectangle(clickbox.getBounds()); + } + + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/Mouse.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/Mouse.java index f7e7066a85..dd035f57c3 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/Mouse.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/Mouse.java @@ -1,19 +1,41 @@ package net.runelite.client.plugins.microbot.util.mouse; +import lombok.Getter; import net.runelite.api.Point; import net.runelite.client.plugins.microbot.Microbot; +import javax.swing.*; import java.awt.*; -import java.util.ArrayList; -import java.util.List; +import java.util.LinkedList; import static net.runelite.client.plugins.microbot.util.math.Random.random; +@Getter public abstract class Mouse { - + private static final int POINT_LIFETIME = 12;// Maximum number of points to store + final int MAX_POINTS = 500; + LinkedList points = new LinkedList<>(); + Point lastClick = new Point(-1, -1); // getter for last click + // getter for click before last click + Point lastClick2 = new Point(-1, -1); + Point lastMove = new Point(-1, -1); // getter for last move + float hue = 0.0f; // Initial hue value + Timer timer = new Timer(POINT_LIFETIME, e -> { + if (!points.isEmpty()) { + points.removeFirst(); + } + }); public Mouse() { } + public Color getRainbowColor() { + hue += 0.001f; // Increment hue to cycle through colors + if (hue > 1.0f) { + hue = 0.0f; // Reset hue when it exceeds 1.0 + } + return Color.getHSBColor(hue, 1.0f, 1.0f); + } + public Canvas getCanvas() { return Microbot.getClient().getCanvas(); } @@ -22,6 +44,12 @@ public int randomizeClick() { return random(-10, 10); } + + public abstract void setLastClick(Point point); + + public abstract void setLastMove(Point point); + + public abstract Mouse click(int x, int y); public abstract Mouse click(double x, double y); @@ -39,10 +67,13 @@ public int randomizeClick() { public abstract Mouse move(Rectangle rect); public abstract Mouse move(int x, int y); + public abstract Mouse move(double x, double y); public abstract Mouse move(Polygon polygon); public abstract Mouse scrollDown(Point point); public abstract Mouse scrollUp(Point point); + + public abstract Mouse drag(Point startPoint, Point endPoint); public abstract java.awt.Point getMousePosition(); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/VirtualMouse.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/VirtualMouse.java index f6a38da072..b532331ef0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/VirtualMouse.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/VirtualMouse.java @@ -2,7 +2,8 @@ import net.runelite.api.Point; import net.runelite.client.plugins.microbot.Microbot; -import net.runelite.client.plugins.microbot.util.math.Random; +import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings; +import net.runelite.client.plugins.microbot.util.misc.Rs2UiHelper; import javax.inject.Inject; import java.awt.*; @@ -17,8 +18,8 @@ public class VirtualMouse extends Mouse { - private final ScheduledExecutorService scheduledExecutorService; + private boolean exited = true; @Inject public VirtualMouse() { @@ -27,16 +28,42 @@ public VirtualMouse() { //getCanvas().setFocusable(false); } - public Mouse click(Point point, boolean rightClick) { + public void setLastClick(Point point) { + lastClick2 = lastClick; + lastClick = point; + } + public void setLastMove(Point point) { + lastMove = point; + points.add(point); + if (points.size() > MAX_POINTS) { + points.removeFirst(); + } + } + + private void handleClick(Point point, boolean rightClick) { + entered(point); + exited(point); + moved(point); + pressed(point, rightClick ? MouseEvent.BUTTON3 : MouseEvent.BUTTON1); + released(point, rightClick ? MouseEvent.BUTTON3 : MouseEvent.BUTTON1); + clicked(point, rightClick ? MouseEvent.BUTTON3 : MouseEvent.BUTTON1); + setLastClick(point); + } + + public Mouse click(Point point, boolean rightClick) { if (point == null) return this; - mouseEvent(MouseEvent.MOUSE_ENTERED, point, rightClick); - mouseEvent(MouseEvent.MOUSE_EXITED, point, rightClick); - mouseEvent(MouseEvent.MOUSE_MOVED, point, rightClick); - mouseEvent(MouseEvent.MOUSE_PRESSED, point, rightClick); - mouseEvent(MouseEvent.MOUSE_RELEASED, point, rightClick); - mouseEvent(MouseEvent.MOUSE_FIRST, point, rightClick); + if (Rs2AntibanSettings.naturalMouse && (point.getX() > 1 && point.getY() > 1)) + Microbot.naturalMouse.moveTo(point.getX(), point.getY()); + + if (Microbot.getClient().isClientThread()) { + scheduledExecutorService.schedule(() -> { + handleClick(point, rightClick); + }, 0, TimeUnit.MILLISECONDS); + } else { + handleClick(point, rightClick); + } return this; } @@ -50,7 +77,7 @@ public Mouse click(double x, double y) { } public Mouse click(Rectangle rectangle) { - return click(new Point((int) rectangle.getCenterX() , (int) rectangle.getCenterY()), false); + return click(Rs2UiHelper.getClickingPoint(rectangle, true), false); } @Override @@ -65,38 +92,33 @@ public Mouse click(Point point) { @Override public Mouse click() { - return click(new Point((int) MouseInfo.getPointerInfo().getLocation().getX(), (int) MouseInfo.getPointerInfo().getLocation().getY())); + return click(Microbot.getClient().getMouseCanvasPosition()); } public Mouse move(Point point) { - long time = System.currentTimeMillis(); - - MouseEvent mouseMove = new MouseEvent(getCanvas(), MouseEvent.MOUSE_MOVED, time, 0, point.getX(), point.getY(), 1, false, MouseEvent.BUTTON1); - + setLastMove(point); + MouseEvent mouseMove = new MouseEvent(Microbot.getClient().getCanvas(), MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, point.getX(), point.getY(), 0, false); + mouseMove.setSource("Microbot"); getCanvas().dispatchEvent(mouseMove); return this; } public Mouse move(Rectangle rect) { - long time = System.currentTimeMillis(); - - MouseEvent mouseMove = new MouseEvent(getCanvas(), MouseEvent.MOUSE_MOVED, time, 0, (int) rect.getCenterX(), (int) rect.getCenterY(), 1, false, MouseEvent.BUTTON1); - + MouseEvent mouseMove = new MouseEvent(Microbot.getClient().getCanvas(), MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, (int) rect.getCenterX(), (int) rect.getCenterY(), 0, false); + mouseMove.setSource("Microbot"); getCanvas().dispatchEvent(mouseMove); return this; } public Mouse move(Polygon polygon) { - long time = System.currentTimeMillis(); Point point = new Point((int) polygon.getBounds().getCenterX(), (int) polygon.getBounds().getCenterY()); - MouseEvent mouseMove = new MouseEvent(getCanvas(), MouseEvent.MOUSE_MOVED, time, 0, point.getX(), point.getY(), 1, false, MouseEvent.BUTTON1); - + MouseEvent mouseMove = new MouseEvent(getCanvas(), MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, point.getX(), point.getY(), 0, false); + mouseMove.setSource("Microbot"); getCanvas().dispatchEvent(mouseMove); - return this; } @@ -108,10 +130,8 @@ public Mouse scrollDown(Point point) { scheduledExecutorService.schedule(() -> { MouseEvent mouseScroll = new MouseWheelEvent(getCanvas(), MouseEvent.MOUSE_WHEEL, time, 0, point.getX(), point.getY(), 0, false, 0, 10, 2); - - getCanvas().dispatchEvent(mouseScroll); - - + mouseScroll.setSource("Microbot"); + getCanvas().dispatchEvent(mouseScroll); }, random(40, 100), TimeUnit.MILLISECONDS); return this; } @@ -129,26 +149,86 @@ public Mouse scrollUp(Point point) { @Override public java.awt.Point getMousePosition() { - PointerInfo pointerInfo = MouseInfo.getPointerInfo(); - - return pointerInfo != null ? pointerInfo.getLocation() : null; + Point point = lastMove; + return new java.awt.Point(point.getX(), point.getY()); } @Override public Mouse move(int x, int y) { return move(new Point(x, y)); } + @Override public Mouse move(double x, double y) { return move(new Point((int) x, (int) y)); } - private void mouseEvent(int id, Point point, boolean rightClick) - { + @Deprecated + private void mouseEvent(int id, Point point, boolean rightClick) { int button = rightClick ? MouseEvent.BUTTON3 : MouseEvent.BUTTON1; - MouseEvent e = new MouseEvent(Microbot.getClient().getCanvas(), id, System.currentTimeMillis(), 0, point.getX(), point.getY(), 1, false, button); - getCanvas().dispatchEvent(e); } + + private synchronized void pressed(Point point, int button) { + MouseEvent event = new MouseEvent(Microbot.getClient().getCanvas(), MouseEvent.MOUSE_PRESSED, System.currentTimeMillis(), 0, point.getX(), point.getY(), 1, false, button); + event.setSource("Microbot"); + getCanvas().dispatchEvent(event); + } + + private synchronized void released(Point point, int button) { + MouseEvent event = new MouseEvent(Microbot.getClient().getCanvas(), MouseEvent.MOUSE_RELEASED, System.currentTimeMillis(), 0, point.getX(), point.getY(), 1, false, button); + event.setSource("Microbot"); + getCanvas().dispatchEvent(event); + } + + private synchronized void clicked(Point point, int button) { + MouseEvent event = new MouseEvent(Microbot.getClient().getCanvas(), MouseEvent.MOUSE_CLICKED, System.currentTimeMillis(), 0, point.getX(), point.getY(), 1, false, button); + event.setSource("Microbot"); + getCanvas().dispatchEvent(event); + } + + private synchronized void exited(Point point) { + MouseEvent event = new MouseEvent(Microbot.getClient().getCanvas(), MouseEvent.MOUSE_EXITED, System.currentTimeMillis(), 0, point.getX(), point.getY(), 0, false); + event.setSource("Microbot"); + getCanvas().dispatchEvent(event); + exited = true; + } + + private synchronized void entered(Point point) { + MouseEvent event = new MouseEvent(Microbot.getClient().getCanvas(), MouseEvent.MOUSE_ENTERED, System.currentTimeMillis(), 0, point.getX(), point.getY(), 0, false); + event.setSource("Microbot"); + getCanvas().dispatchEvent(event); + exited = false; + } + + private synchronized void moved(Point point) { + MouseEvent event = new MouseEvent(Microbot.getClient().getCanvas(), MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), 0, point.getX(), point.getY(), 0, false); + event.setSource("Microbot"); + getCanvas().dispatchEvent(event); + } + + // New drag method + public Mouse drag(Point startPoint, Point endPoint) { + if (startPoint == null || endPoint == null) return this; + + if (Rs2AntibanSettings.naturalMouse && (startPoint.getX() > 1 && startPoint.getY() > 1)) + Microbot.naturalMouse.moveTo(startPoint.getX(), startPoint.getY()); + else + move(startPoint); + sleep(50, 80); + // Press the mouse button at the start point + pressed(startPoint, MouseEvent.BUTTON1); + sleep(80, 120); + // Move to the end point while holding the button down + if (Rs2AntibanSettings.naturalMouse && (endPoint.getX() > 1 && endPoint.getY() > 1)) + Microbot.naturalMouse.moveTo(endPoint.getX(), endPoint.getY()); + else + move(endPoint); + sleep(80, 120); + // Release the mouse button at the end point + released(endPoint, MouseEvent.BUTTON1); + + return this; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/NaturalMouse.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/NaturalMouse.java new file mode 100644 index 0000000000..cd01569120 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/NaturalMouse.java @@ -0,0 +1,180 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse; + +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Client; +import net.runelite.client.plugins.microbot.Microbot; +import net.runelite.client.plugins.microbot.util.Global; +import net.runelite.client.plugins.microbot.util.antiban.Rs2Antiban; +import net.runelite.client.plugins.microbot.util.antiban.enums.ActivityIntensity; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api.MouseInfoAccessor; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api.MouseMotionFactory; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api.SystemCalls; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.DefaultMouseMotionNature; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.DefaultSpeedManager; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.Flow; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.MouseMotionNature; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.util.FactoryTemplates; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.util.FlowTemplates; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.util.Pair; + +import javax.inject.Inject; +import java.awt.*; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadLocalRandom; + +@Slf4j +public class NaturalMouse { + public final MouseMotionNature nature; + private final ThreadLocalRandom random = ThreadLocalRandom.current(); + private final ExecutorService executorService = Executors.newSingleThreadExecutor(); + @Inject + private Client client; + @Getter + @Setter + private List flows = List.of( + new Flow(FlowTemplates.variatingFlow()), + new Flow(FlowTemplates.slowStartupFlow()), + new Flow(FlowTemplates.slowStartup2Flow()), + new Flow(FlowTemplates.jaggedFlow()), + new Flow(FlowTemplates.interruptedFlow()), + new Flow(FlowTemplates.interruptedFlow2()), + new Flow(FlowTemplates.stoppingFlow()), + new Flow(FlowTemplates.adjustingFlow()), + new Flow(FlowTemplates.random()) + ); + + @Inject + public NaturalMouse() { + nature = new DefaultMouseMotionNature(); + nature.setSystemCalls(new SystemCallsImpl()); + nature.setMouseInfo(new MouseInfoImpl()); + } + + public synchronized void moveTo(int dx, int dy) { +// if(Rs2UiHelper.isStretchedEnabled()) +// { +// dx = Rs2UiHelper.stretchX(dx); +// dy = Rs2UiHelper.stretchY(dy); +// } + int finalDx = dx; + int finalDy = dy; + + if (!Microbot.getClient().isClientThread()) { + move(finalDx, finalDy); + } else { + + executorService.submit(() -> move(finalDx, finalDy)); + } + } + + private synchronized void move(int dx, int dy) { + var motion = getFactory().build(dx, dy); + try { + motion.move(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public MouseMotionFactory getFactory() { + if (Rs2Antiban.getActivityIntensity() == ActivityIntensity.VERY_LOW) { + log.info("Creating average computer user motion factory"); + return FactoryTemplates.createAverageComputerUserMotionFactory(nature); + } else if (Rs2Antiban.getActivityIntensity() == ActivityIntensity.LOW) { + log.info("Creating normal gamer motion factory"); + return FactoryTemplates.createNormalGamerMotionFactory(nature); + } else if (Rs2Antiban.getActivityIntensity() == ActivityIntensity.MODERATE) { + log.info("Creating fast gamer motion factory"); + return FactoryTemplates.createFastGamerMotionFactory(nature); + } else if (Rs2Antiban.getActivityIntensity() == ActivityIntensity.HIGH) { + log.info("Creating fast gamer motion factory"); + return FactoryTemplates.createFastGamerMotionFactory(nature); + } else if (Rs2Antiban.getActivityIntensity() == ActivityIntensity.EXTREME) { + log.info("Creating super fast gamer motion factory"); + return FactoryTemplates.createSuperFastGamerMotionFactory(nature); + } else { + log.info("Default: Creating super fast gamer motion factory"); + return FactoryTemplates.createSuperFastGamerMotionFactory(nature); + } + +// var factory = new MouseMotionFactory(); +// factory.setNature(nature); +// factory.setRandom(random); +// +// var manager = new SpeedManagerImpl(flows); +// factory.setDeviationProvider(new SinusoidalDeviationProvider(15.0)); +// factory.setNoiseProvider(new DefaultNoiseProvider(2.0)); +// factory.getNature().setReactionTimeVariationMs(120); +// manager.setMouseMovementBaseTimeMs(130); +// +// var overshootManager = (DefaultOvershootManager) factory.getOvershootManager(); +// overshootManager.setOvershoots(2); +// factory.setSpeedManager(manager); +// +// return factory; + } + + public void moveOffScreen() { + // 1 in 4 chance of moving off screen + if (random.nextInt(4) == 0) { + // Edges of the screen + int horizontal = random.nextBoolean() ? -1 : client.getCanvasWidth() + 1; + int vertical = random.nextBoolean() ? -1 : client.getCanvasHeight() + 1; + + boolean exitHorizontally = random.nextBoolean(); + if (exitHorizontally) { + moveTo(horizontal, random.nextInt(0, client.getCanvasHeight() + 1)); + } else { + moveTo(random.nextInt(0, client.getCanvasWidth() + 1), vertical); + } + + } + } + + private static class SpeedManagerImpl extends DefaultSpeedManager { + private SpeedManagerImpl(Collection flows) { + super(flows); + } + + @Override + public Pair getFlowWithTime(double distance) { + var pair = super.getFlowWithTime(distance); + return new Pair<>(pair.x, pair.y); + } + } + + private static class MouseInfoImpl implements MouseInfoAccessor { + @Override + public Point getMousePosition() { + return Microbot.getMouse().getMousePosition(); + } + } + + private class SystemCallsImpl implements SystemCalls { + @Override + public long currentTimeMillis() { + return System.currentTimeMillis(); + } + + @Override + public void sleep(long time) { + Global.sleep((int) time); + } + + @Override + public Dimension getScreenSize() { + return Microbot.getClient().getCanvas().getSize(); + } + + @Override + public void setMousePosition(int x, int y) { + Microbot.getMouse().move(x, y); + + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/DeviationProvider.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/DeviationProvider.java new file mode 100644 index 0000000000..e18333c7e2 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/DeviationProvider.java @@ -0,0 +1,36 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api; + +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.DoublePoint; + +/** + * Creates arcs or deviation into mouse movement. + *

+ * DeviationProvider implementation should be immutable. + */ +public interface DeviationProvider { + + /** + * Gets the deviation for current trajectory. Deviation is an offset from the original straight trajectory. + *

+ * Deviation is different from the Noise because it works like a mathematical function, the resulting + * Point is added to single trajectory point and it will not have any effect in the next + * mouse movement step, making it easy to implement this as a formula based on the input parameters. + * e.g the something like 'deviation = totalDistanceInPixels / completionFraction', resulting in smooth movement. + * (Don't actually use this formula), 'Noise' is generating an offset from the original trajectory and is accumulating. + *

+ * As deviation should be deterministic and return same result for same parameters, it should not include Random + * behaviour, thus Random is not included as a parameter. + *

+ * It is recommended that deviation is decreasing when completionFraction nears 1, but MouseMotion itself + * also makes sure that the effect of deviation is reduced when the mouse is nearing its destination. + * + * @param totalDistanceInPixels the total pixels between target and mouse initial position + * @param completionFraction the completed fraction of mouse movement total distance, value from 0...1 (0;1] + * @return a point which describes how much the mouse is going to deviate from the straight trajectory between + * target and initial position. This is not the final deviation of the mouse as MouseMotion will randomly decide + * to either amplify or decrease it over the whole mouse movement, making the resulting arc stand out more or less, + * or is flipped negatively. + * @see NoiseProvider + */ + DoublePoint getDeviation(double totalDistanceInPixels, double completionFraction); +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/MouseInfoAccessor.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/MouseInfoAccessor.java new file mode 100644 index 0000000000..cde30447ca --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/MouseInfoAccessor.java @@ -0,0 +1,18 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api; + +import java.awt.*; + +/** + * Abstraction for getting mouse position. + */ +public interface MouseInfoAccessor { + /** + * Get the current mouse position. + * NB, for optimization reasons this method might return the same Point instance, but is not quaranteed to. + * It is recommended not to save this Point anywhere as it may or may not change its coordinates. + * + * @return the current mouse position + */ + + Point getMousePosition(); +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/MouseMotion.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/MouseMotion.java new file mode 100644 index 0000000000..d8f7abab8b --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/MouseMotion.java @@ -0,0 +1,203 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api; + +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.DoublePoint; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.Flow; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.MouseMotionNature; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.mousemotion.MouseMovement; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.mousemotion.MovementFactory; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.util.MathUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.*; +import java.util.ArrayDeque; +import java.util.Random; + +/** + * Contains instructions to move cursor smoothly to the destination coordinates from where ever the cursor + * currently is. The class is reusable, meaning user can keep calling it and the cursor returns in a random, + * but reliable way, described in this class, to the destination. + */ +public class MouseMotion { + private static final Logger log = LoggerFactory.getLogger(MouseMotion.class); + private static final int SLEEP_AFTER_ADJUSTMENT_MS = 2; + private final int minSteps; + private final int effectFadeSteps; + private final int reactionTimeBaseMs; + private final int reactionTimeVariationMs; + private final double timeToStepsDivider; + private final Dimension screenSize; + private final SystemCalls systemCalls; + private final DeviationProvider deviationProvider; + private final NoiseProvider noiseProvider; + private final SpeedManager speedManager; + private final OvershootManager overshootManager; + private final int xDest; + private final int yDest; + private final Random random; + private final MouseInfoAccessor mouseInfo; + private Point mousePosition; + + /** + * @param nature the nature that defines how mouse is moved + * @param xDest the x-coordinate of destination + * @param yDest the y-coordinate of destination + * @param random the random used for unpredictability + */ + public MouseMotion(MouseMotionNature nature, Random random, int xDest, int yDest) { + this.deviationProvider = nature.getDeviationProvider(); + this.noiseProvider = nature.getNoiseProvider(); + this.systemCalls = nature.getSystemCalls(); + this.screenSize = systemCalls.getScreenSize(); + this.xDest = limitByScreenWidth(xDest); + this.yDest = limitByScreenHeight(yDest); + this.random = random; + this.mouseInfo = nature.getMouseInfo(); + this.speedManager = nature.getSpeedManager(); + this.timeToStepsDivider = nature.getTimeToStepsDivider(); + this.minSteps = nature.getMinSteps(); + this.effectFadeSteps = nature.getEffectFadeSteps(); + this.reactionTimeBaseMs = nature.getReactionTimeBaseMs(); + this.reactionTimeVariationMs = nature.getReactionTimeVariationMs(); + this.overshootManager = nature.getOvershootManager(); + } + + /** + * Blocking call, starts to move the cursor to the specified location from where it currently is. + * + * @throws InterruptedException when interrupted + */ + public void move() throws InterruptedException { + move((x, y) -> + { + }); + } + + /** + * Blocking call, starts to move the cursor to the specified location from where it currently is. + * + * @param observer Provide observer if you are interested receiving the location of mouse on every step + * @throws InterruptedException when interrupted + */ + public void move(MouseMotionObserver observer) throws InterruptedException { + updateMouseInfo(); + + MovementFactory movementFactory = new MovementFactory(xDest, yDest, speedManager, overshootManager, screenSize); + ArrayDeque movements = movementFactory.createMovements(mousePosition); + int overshoots = movements.size() - 1; + while (mousePosition.x != xDest || mousePosition.y != yDest) { + if (movements.isEmpty()) { + // This shouldn't usually happen, but it's possible that somehow we won't end up on the target, + // Then just re-attempt from mouse new position. (There are known JDK bugs, that can cause sending the cursor + // to wrong pixel) + updateMouseInfo(); + movements = movementFactory.createMovements(mousePosition); + } + + MouseMovement movement = movements.removeFirst(); + double distance = movement.distance; + long mouseMovementMs = movement.time; + Flow flow = movement.flow; + double xDistance = movement.xDistance; + double yDistance = movement.yDistance; + + /* Number of steps is calculated from the movement time and limited by minimal amount of steps + (should have at least MIN_STEPS) and distance (shouldn't have more steps than pixels travelled) */ + int steps = (int) Math.ceil(Math.min(distance, Math.max(mouseMovementMs / timeToStepsDivider, minSteps))); + + long startTime = systemCalls.currentTimeMillis(); + long stepTime = (long) (mouseMovementMs / (double) steps); + + updateMouseInfo(); + double simulatedMouseX = mousePosition.x; + double simulatedMouseY = mousePosition.y; + + double deviationMultiplierX = (random.nextDouble() - 0.5) * 2; + double deviationMultiplierY = (random.nextDouble() - 0.5) * 2; + + double completedXDistance = 0; + double completedYDistance = 0; + double noiseX = 0; + double noiseY = 0; + + for (int i = 0; i < steps; i++) { + // All steps take equal amount of time. This is a value from 0...1 describing how far along the process is. + double timeCompletion = i / (double) steps; + + double effectFadeStep = Math.max(i - (steps - effectFadeSteps) + 1, 0); + // value from 0 to 1, when effectFadeSteps remaining steps, starts to decrease to 0 linearly + // This is here so noise and deviation wouldn't add offset to mouse final position, when we need accuracy. + double effectFadeMultiplier = (effectFadeSteps - effectFadeStep) / effectFadeSteps; + + double xStepSize = flow.getStepSize(xDistance, steps, timeCompletion); + double yStepSize = flow.getStepSize(yDistance, steps, timeCompletion); + + completedXDistance += xStepSize; + completedYDistance += yStepSize; + double completedDistance = Math.hypot(completedXDistance, completedYDistance); + double completion = Math.min(1, completedDistance / distance); + + DoublePoint noise = noiseProvider.getNoise(random, xStepSize, yStepSize); + DoublePoint deviation = deviationProvider.getDeviation(distance, completion); + + noiseX += noise.getX(); + noiseY += noise.getY(); + simulatedMouseX += xStepSize; + simulatedMouseY += yStepSize; + + long endTime = startTime + stepTime * (i + 1); + int mousePosX = MathUtil.roundTowards(simulatedMouseX + deviation.getX() * deviationMultiplierX * effectFadeMultiplier + noiseX * effectFadeMultiplier, movement.destX); + + int mousePosY = MathUtil.roundTowards(simulatedMouseY + deviation.getY() * deviationMultiplierY * effectFadeMultiplier + noiseY * effectFadeMultiplier, movement.destY); + + mousePosX = limitByScreenWidth(mousePosX); + mousePosY = limitByScreenHeight(mousePosY); + + systemCalls.setMousePosition(mousePosX, mousePosY); + + // Allow other action to take place or just observe, we'll later compensate by sleeping less. + observer.observe(mousePosX, mousePosY); + + long timeLeft = endTime - systemCalls.currentTimeMillis(); + sleepAround(Math.max(timeLeft, 0), 0); + } + updateMouseInfo(); + + if (mousePosition.x != movement.destX || mousePosition.y != movement.destY) { + // It's possible that mouse is manually moved or for some other reason. + // Let's start next step from pre-calculated location to prevent errors from accumulating. + // But print warning as this is not expected behavior. + systemCalls.setMousePosition(movement.destX, movement.destY); + // Let's wait a bit before getting mouse info. + sleepAround(SLEEP_AFTER_ADJUSTMENT_MS, 0); + updateMouseInfo(); + } + + if (mousePosition.x != xDest || mousePosition.y != yDest) { + // We are dealing with overshoot, let's sleep a bit to simulate human reaction time. + sleepAround(reactionTimeBaseMs, reactionTimeVariationMs); + } + } + } + + private int limitByScreenWidth(int value) { + return Math.max(0, Math.min(screenSize.width - 1, value)); + } + + private int limitByScreenHeight(int value) { + return Math.max(0, Math.min(screenSize.height - 1, value)); + } + + private void sleepAround(long sleepMin, long randomPart) { + long sleepTime = (long) (sleepMin + random.nextDouble() * randomPart); + if (log.isTraceEnabled() && sleepTime > 0) { + updateMouseInfo(); + } + systemCalls.sleep(sleepTime); + } + + private void updateMouseInfo() { + mousePosition = mouseInfo.getMousePosition(); + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/MouseMotionFactory.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/MouseMotionFactory.java new file mode 100644 index 0000000000..b3f134b21e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/MouseMotionFactory.java @@ -0,0 +1,200 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api; + +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.DefaultMouseMotionNature; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.MouseMotionNature; + +import java.util.Random; + +/** + * This class should be used for creating new MouseMotion-s + * The default instance is available via getDefault(), but can create new instance via constructor. + */ +public class MouseMotionFactory { + private static final MouseMotionFactory defaultFactory = new MouseMotionFactory(); + private MouseMotionNature nature; + private Random random = new Random(); + + public MouseMotionFactory(MouseMotionNature nature) { + this.nature = nature; + } + + public MouseMotionFactory() { + this(new DefaultMouseMotionNature()); + } + + /** + * Get the default factory implementation. + * + * @return the factory + */ + public static MouseMotionFactory getDefault() { + return defaultFactory; + } + + /** + * Builds the MouseMotion, which can be executed instantly or saved for later. + * + * @param xDest the end position x-coordinate for the mouse + * @param yDest the end position y-coordinate for the mouse + * @return the MouseMotion which can be executed instantly or saved for later. (Mouse will be moved from its + * current position, not from the position where mouse was during building.) + */ + public MouseMotion build(int xDest, int yDest) { + return new MouseMotion(nature, random, xDest, yDest); + } + + /** + * Start moving the mouse to specified location. Blocks until done. + * + * @param xDest the end position x-coordinate for the mouse + * @param yDest the end position y-coordinate for the mouse + * @throws InterruptedException if something interrupts the thread. + */ + public void move(int xDest, int yDest) throws InterruptedException { + build(xDest, yDest).move(); + } + + /** + * see {@link MouseMotionNature#getSystemCalls()} + * + * @return the systemcalls + */ + public SystemCalls getSystemCalls() { + return nature.getSystemCalls(); + } + + /** + * see {@link MouseMotionNature#setSystemCalls(SystemCalls)} + * + * @param systemCalls the systemcalls + */ + public void setSystemCalls(SystemCalls systemCalls) { + nature.setSystemCalls(systemCalls); + } + + /** + * see {@link MouseMotionNature#getDeviationProvider()} + * + * @return the deviation provider + */ + public DeviationProvider getDeviationProvider() { + return nature.getDeviationProvider(); + } + + /** + * see {@link MouseMotionNature#setDeviationProvider(DeviationProvider)} + * + * @param deviationProvider the deviation provider + */ + public void setDeviationProvider(DeviationProvider deviationProvider) { + nature.setDeviationProvider(deviationProvider); + } + + /** + * see {@link MouseMotionNature#getNoiseProvider()} + * + * @return the noise provider + */ + public NoiseProvider getNoiseProvider() { + return nature.getNoiseProvider(); + } + + /** + * see {@link MouseMotionNature#setNoiseProvider(NoiseProvider)}} + * + * @param noiseProvider the noise provider + */ + public void setNoiseProvider(NoiseProvider noiseProvider) { + nature.setNoiseProvider(noiseProvider); + } + + /** + * Get the random used whenever randomized behavior is needed in MouseMotion + * + * @return the random + */ + public Random getRandom() { + return random; + } + + /** + * Set the random used whenever randomized behavior is needed in MouseMotion + * + * @param random the random + */ + public void setRandom(Random random) { + this.random = random; + } + + /** + * see {@link MouseMotionNature#getMouseInfo()} + * + * @return the mouseInfo + */ + public MouseInfoAccessor getMouseInfo() { + return nature.getMouseInfo(); + } + + /** + * see {@link MouseMotionNature#setMouseInfo(MouseInfoAccessor)} + * + * @param mouseInfo the mouseInfo + */ + public void setMouseInfo(MouseInfoAccessor mouseInfo) { + nature.setMouseInfo(mouseInfo); + } + + /** + * see {@link MouseMotionNature#getSpeedManager()} + * + * @return the manager + */ + public SpeedManager getSpeedManager() { + return nature.getSpeedManager(); + } + + /** + * see {@link MouseMotionNature#setSpeedManager(SpeedManager)} + * + * @param speedManager the manager + */ + public void setSpeedManager(SpeedManager speedManager) { + nature.setSpeedManager(speedManager); + } + + /** + * The Nature of mousemotion covers all aspects how the mouse is moved. + * + * @return the nature + */ + public MouseMotionNature getNature() { + return nature; + } + + /** + * The Nature of mousemotion covers all aspects how the mouse is moved. + * + * @param nature the new nature + */ + public void setNature(MouseMotionNature nature) { + this.nature = nature; + } + + /** + * see {@link MouseMotionNature#getOvershootManager()} + * + * @return the manager + */ + public OvershootManager getOvershootManager() { + return nature.getOvershootManager(); + } + + /** + * see {@link MouseMotionNature#setOvershootManager(OvershootManager)} + * + * @param manager the manager + */ + public void setOvershootManager(OvershootManager manager) { + nature.setOvershootManager(manager); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/MouseMotionObserver.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/MouseMotionObserver.java new file mode 100644 index 0000000000..004ef8e274 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/MouseMotionObserver.java @@ -0,0 +1,8 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api; + +/** + * Use to observe mouse movement in MouseMotion + */ +public interface MouseMotionObserver { + void observe(int xPos, int yPos); +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/NoiseProvider.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/NoiseProvider.java new file mode 100644 index 0000000000..89ca8c861b --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/NoiseProvider.java @@ -0,0 +1,36 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api; + +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.DoublePoint; + +import java.util.Random; + +/** + * Provides noise or mistakes in the mouse movement + *

+ * NoiseProvider implementation should be immutable. + */ +public interface NoiseProvider { + /** + * Noise is offset from the original trajectory, simulating user and physical errors on mouse movement. + *

+ * Noise is accumulating, so on average it should create an equal chance of either positive or negative movement + * on each axis, otherwise the mouse movement will always be slightly offset to single direction. + *

+ * Deviation from DeviationProvider is different from the Noise + * because it works like a mathematical function and is not accumulating. + *

+ * Not every step needs to add noise, use randomness to only add noise sometimes, otherwise return Point(0, 0). + *

+ * During the final steps of mouse movement, the effect of noise is gradually reduced, so the mouse + * would finish on the intended pixel smoothly, thus the implementation of this class can safely ignore + * and not know the beginning and end of the movement. + * + * @param random use this to generate randomness in the offset + * @param xStepSize the step size that is taken horizontally + * @param yStepSize the step size that is taken vertically + * @return a point which describes how much the mouse offset is increased or decreased this step. + * This value must not include the parameters xStepSize and yStepSize. For no change in noise just return (0,0). + * @see DeviationProvider + */ + DoublePoint getNoise(Random random, double xStepSize, double yStepSize); +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/OvershootManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/OvershootManager.java new file mode 100644 index 0000000000..119d615884 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/OvershootManager.java @@ -0,0 +1,49 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api; + +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.Flow; + +import java.awt.*; + +/** + * Overshoots provide a realistic way to simulate user trying to reach the destination with mouse, but miss. + * Points around the destination are produced which will be hit before the mouse hits the real destination. + * If overshoot happens to match the target, then overshooting is cancelled and real destination will be reached. + */ +public interface OvershootManager { + /** + * Get the maximum amount of overshoots the cursor does before reaching its final destination. + * + * @param flow the flow which is planned to be used to reach the target. + * (If returned overshoots > 0, then a new flow will be calculated for each overshoot.). + * This flow could be analyzed if overshooting is suitable. It is not available + * as a parameter in overshootAmount calculation, because flow itself is calculated + * from the movement distance, which is dependent on the overshoot amount. + * @param mouseMovementMs the planned time for reaching the real target + * @param distance the distance between mouse position and real target + * @return the number of maximum overshoots used or 0 if no overshoots + */ + int getOvershoots(Flow flow, long mouseMovementMs, double distance); + + /** + * Returns the overshoot amount which will be added to real target, thus getting the overshoot target. + * + * @param distanceToRealTargetX distance to real target X-coordinate + * @param distanceToRealTargetY distance to real target Y-coordinate + * @param mouseMovementMs the time planned for reaching the real target + * @param overshootsRemaining the amount of overshoots remaining, current included. + * Values from (n to 1), where n >= 1 + * @return the amount which will be added to real target, thus getting the overshoot target. + */ + Point getOvershootAmount(double distanceToRealTargetX, double distanceToRealTargetY, long mouseMovementMs, int overshootsRemaining); + + /** + * Once the mouse reaches the overshoot target, new trajectory with new speed is calculated for next target + * (can be real or overshoot target, if the next target is real target, the overshootsRemaining value is 0) + * + * @param mouseMovementMs the last mouse movement in ms + * @param overshootsRemaining the amount of overshoots remaining, including this. + * Values from (n to 0), where n >= 0 + * @return the next mouse movement time in ms + */ + long deriveNextMouseMovementTimeMs(long mouseMovementMs, int overshootsRemaining); +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/SpeedManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/SpeedManager.java new file mode 100644 index 0000000000..a3833f2132 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/SpeedManager.java @@ -0,0 +1,20 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api; + +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.Flow; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.util.Pair; + +/** + * SpeedManager controls how long does it take to complete a mouse movement and within that + * time how slow or fast the cursor is moving at a particular moment, the flow. + * Flow controls how jagged or smooth, accelerating or decelerating, the movement is. + */ +public interface SpeedManager { + + /** + * Get the SpeedFlow object, which contains Flow and planned time for mouse movement in ms. + * + * @param distance the distance from where the cursor is now to the destination point * + * @return the SpeedFlow object, which details are a SpeedManager implementation decision. + */ + Pair getFlowWithTime(double distance); +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/SystemCalls.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/SystemCalls.java new file mode 100644 index 0000000000..7031ef3278 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/api/SystemCalls.java @@ -0,0 +1,16 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api; + +import java.awt.*; + +/** + * Abstracts ordinary static System calls away + */ +public interface SystemCalls { + long currentTimeMillis(); + + void sleep(long time); + + Dimension getScreenSize(); + + void setMousePosition(int x, int y); +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultMouseInfoAccessor.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultMouseInfoAccessor.java new file mode 100644 index 0000000000..cac1fb8ef9 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultMouseInfoAccessor.java @@ -0,0 +1,13 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support; + +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api.MouseInfoAccessor; + +import java.awt.*; + +public class DefaultMouseInfoAccessor implements MouseInfoAccessor { + + @Override + public Point getMousePosition() { + return MouseInfo.getPointerInfo().getLocation(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultMouseMotionNature.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultMouseMotionNature.java new file mode 100644 index 0000000000..56fc48cec2 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultMouseMotionNature.java @@ -0,0 +1,36 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support; + +import java.awt.*; +import java.util.Random; + +import static net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.DefaultNoiseProvider.DEFAULT_NOISINESS_DIVIDER; +import static net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.SinusoidalDeviationProvider.DEFAULT_SLOPE_DIVIDER; + + +public class DefaultMouseMotionNature extends MouseMotionNature { + + public static final int TIME_TO_STEPS_DIVIDER = 8; + public static final int MIN_STEPS = 10; + public static final int EFFECT_FADE_STEPS = 15; + public static final int REACTION_TIME_BASE_MS = 20; + public static final int REACTION_TIME_VARIATION_MS = 120; + + public DefaultMouseMotionNature() { + try { + setSystemCalls(new DefaultSystemCalls(new Robot())); + } catch (AWTException e) { + throw new RuntimeException(e); + } + + setDeviationProvider(new SinusoidalDeviationProvider(DEFAULT_SLOPE_DIVIDER)); + setNoiseProvider(new DefaultNoiseProvider(DEFAULT_NOISINESS_DIVIDER)); + setSpeedManager(new DefaultSpeedManager()); + setOvershootManager(new DefaultOvershootManager(new Random())); + setEffectFadeSteps(EFFECT_FADE_STEPS); + setMinSteps(MIN_STEPS); + setMouseInfo(new DefaultMouseInfoAccessor()); + setReactionTimeBaseMs(REACTION_TIME_BASE_MS); + setReactionTimeVariationMs(REACTION_TIME_VARIATION_MS); + setTimeToStepsDivider(TIME_TO_STEPS_DIVIDER); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultNoiseProvider.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultNoiseProvider.java new file mode 100644 index 0000000000..a58d682479 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultNoiseProvider.java @@ -0,0 +1,34 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support; + +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api.NoiseProvider; + +import java.util.Random; + +public class DefaultNoiseProvider implements NoiseProvider { + public static final double DEFAULT_NOISINESS_DIVIDER = 2; + private static final double SMALL_DELTA = 10e-6; + private final double noisinessDivider; + + /** + * @param noisinessDivider bigger value means less noise. + */ + public DefaultNoiseProvider(double noisinessDivider) { + this.noisinessDivider = noisinessDivider; + } + + @Override + public DoublePoint getNoise(Random random, double xStepSize, double yStepSize) { + if (Math.abs(xStepSize - 0) < SMALL_DELTA && Math.abs(yStepSize - 0) < SMALL_DELTA) { + return DoublePoint.ZERO; + } + double noiseX = 0; + double noiseY = 0; + double stepSize = Math.hypot(xStepSize, yStepSize); + double noisiness = Math.max(0, (8 - stepSize)) / 50; + if (random.nextDouble() < noisiness) { + noiseX = (random.nextDouble() - 0.5) * Math.max(0, (8 - stepSize)) / noisinessDivider; + noiseY = (random.nextDouble() - 0.5) * Math.max(0, (8 - stepSize)) / noisinessDivider; + } + return new DoublePoint(noiseX, noiseY); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultOvershootManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultOvershootManager.java new file mode 100644 index 0000000000..5e1eabeb7e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultOvershootManager.java @@ -0,0 +1,89 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support; + + +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api.OvershootManager; + +import java.awt.*; +import java.util.Random; + +public class DefaultOvershootManager implements OvershootManager { + public static final double OVERSHOOT_SPEEDUP_DIVIDER = 1.8; + public static final int MIN_OVERSHOOT_MOVEMENT_MS = 40; + public static final int OVERSHOOT_RANDOM_MODIFIER_DIVIDER = 20; + public static final int MIN_DISTANCE_FOR_OVERSHOOTS = 10; + public static final int DEFAULT_OVERSHOOT_AMOUNT = 3; + private final Random random; + private long minOvershootMovementMs = MIN_OVERSHOOT_MOVEMENT_MS; + private long minDistanceForOvershoots = MIN_DISTANCE_FOR_OVERSHOOTS; + private double overshootRandomModifierDivider = OVERSHOOT_RANDOM_MODIFIER_DIVIDER; + private double overshootSpeedupDivider = OVERSHOOT_SPEEDUP_DIVIDER; + private int overshoots = DEFAULT_OVERSHOOT_AMOUNT; + + public DefaultOvershootManager(Random random) { + this.random = random; + } + + @Override + public int getOvershoots(Flow flow, long mouseMovementMs, double distance) { + if (distance < minDistanceForOvershoots) { + return 0; + } + return overshoots; + } + + @Override + public Point getOvershootAmount(double distanceToRealTargetX, double distanceToRealTargetY, long mouseMovementMs, int overshootsRemaining) { + double distanceToRealTarget = Math.hypot(distanceToRealTargetX, distanceToRealTargetY); + + double randomModifier = distanceToRealTarget / overshootRandomModifierDivider; + //double speedPixelsPerSecond = distanceToRealTarget / mouseMovementMs * 1000; // TODO utilize speed + int x = (int) (random.nextDouble() * randomModifier - randomModifier / 2d) * overshootsRemaining; + int y = (int) (random.nextDouble() * randomModifier - randomModifier / 2d) * overshootsRemaining; + return new Point(x, y); + } + + @Override + public long deriveNextMouseMovementTimeMs(long mouseMovementMs, int overshootsRemaining) { + return Math.max((long) (mouseMovementMs / overshootSpeedupDivider), minOvershootMovementMs); + } + + public long getMinOvershootMovementMs() { + return minOvershootMovementMs; + } + + public void setMinOvershootMovementMs(long minOvershootMovementMs) { + this.minOvershootMovementMs = minOvershootMovementMs; + } + + public double getOvershootRandomModifierDivider() { + return overshootRandomModifierDivider; + } + + public void setOvershootRandomModifierDivider(double overshootRandomModifierDivider) { + this.overshootRandomModifierDivider = overshootRandomModifierDivider; + } + + public double getOvershootSpeedupDivider() { + return overshootSpeedupDivider; + } + + public void setOvershootSpeedupDivider(double overshootSpeedupDivider) { + this.overshootSpeedupDivider = overshootSpeedupDivider; + } + + public int getOvershoots() { + return overshoots; + } + + public void setOvershoots(int overshoots) { + this.overshoots = overshoots; + } + + public long getMinDistanceForOvershoots() { + return minDistanceForOvershoots; + } + + public void setMinDistanceForOvershoots(long minDistanceForOvershoots) { + this.minDistanceForOvershoots = minDistanceForOvershoots; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultSpeedManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultSpeedManager.java new file mode 100644 index 0000000000..bd4e431d59 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultSpeedManager.java @@ -0,0 +1,56 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support; + + +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api.SpeedManager; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.util.FlowTemplates; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.util.Pair; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +public class DefaultSpeedManager implements SpeedManager { + private static final double SMALL_DELTA = 10e-6; + private final List flows = new ArrayList<>(); + private long mouseMovementTimeMs = 500; + + public DefaultSpeedManager(Collection flows) { + this.flows.addAll(flows); + } + + public DefaultSpeedManager() { + this(Arrays.asList( + new Flow(FlowTemplates.constantSpeed()), + new Flow(FlowTemplates.variatingFlow()), + new Flow(FlowTemplates.interruptedFlow()), + new Flow(FlowTemplates.interruptedFlow2()), + new Flow(FlowTemplates.slowStartupFlow()), + new Flow(FlowTemplates.slowStartup2Flow()), + new Flow(FlowTemplates.adjustingFlow()), + new Flow(FlowTemplates.jaggedFlow()), + new Flow(FlowTemplates.stoppingFlow()) + )); + } + + @Override + public Pair getFlowWithTime(double distance) { + double time = mouseMovementTimeMs + (long) (Math.random() * mouseMovementTimeMs); + Flow flow = flows.get((int) (Math.random() * flows.size())); + + // Let's ignore waiting time, e.g 0's in flow, by increasing the total time + // by the amount of 0's there are in the flow multiplied by the time each bucket represents. + double timePerBucket = time / (double) flow.getFlowCharacteristics().length; + for (double bucket : flow.getFlowCharacteristics()) { + if (Math.abs(bucket - 0) < SMALL_DELTA) { + time += timePerBucket; + } + } + + return new Pair<>(flow, (long) time); + } + + public void setMouseMovementBaseTimeMs(long mouseMovementSpeedMs) { + this.mouseMovementTimeMs = mouseMovementSpeedMs; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultSystemCalls.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultSystemCalls.java new file mode 100644 index 0000000000..98ce5a2aa6 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DefaultSystemCalls.java @@ -0,0 +1,49 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support; + + +import net.runelite.client.plugins.microbot.util.Global; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api.SystemCalls; + +import java.awt.*; + +public class DefaultSystemCalls implements SystemCalls { + private final Robot robot; + + public DefaultSystemCalls(Robot robot) { + this.robot = robot; + } + + @Override + public long currentTimeMillis() { + return System.currentTimeMillis(); + } + + @Override + public void sleep(long time) { + Global.sleep((int) time); + } + + @Override + public Dimension getScreenSize() { + return Toolkit.getDefaultToolkit().getScreenSize(); + } + + /** + *

Moves the mouse to specified pixel using the provided Robot.

+ * + *

It seems there is a certain delay, measurable in less than milliseconds, + * before the mouse actually ends up on the requested pixel when using a Robot class. + * this usually isn't a problem, but when we ask the mouse position right after this call, + * there's extremely low but real chance we get wrong information back. I didn't add sleep + * here as it would cause overhead to sleep always, even when we don't instantly use + * the mouse position, but just acknowledged the issue with this warning. + * (Use fast unrestricted loop of Robot movement and checking the position after every move to invoke the issue.)

+ * + * @param x the x-coordinate + * @param y the y-coordinate + */ + @Override + public void setMousePosition(int x, int y) { + robot.mouseMove(x, y); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DoublePoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DoublePoint.java new file mode 100644 index 0000000000..26185336ce --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/DoublePoint.java @@ -0,0 +1,20 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support; + +public class DoublePoint { + public final static DoublePoint ZERO = new DoublePoint(0, 0); + private final double x; + private final double y; + + public DoublePoint(double x, double y) { + this.x = x; + this.y = y; + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/Flow.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/Flow.java new file mode 100644 index 0000000000..ff36094938 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/Flow.java @@ -0,0 +1,121 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support; + +/** + * Flow for the mouse movement + * Flow defines how slow or fast the cursor is moving at a particular moment, defining the characteristics + * of movement itself not the trajectory, but how jagged or smooth, accelerating or decelerating, the movement is. + */ +public class Flow { + private static final int AVERAGE_BUCKET_VALUE = 100; + + private final double[] buckets; + + /** + * @param characteristics the characteristics array, which can be any size, contain non-negative numbers. + * The values in the array are translated to flow and all values are relative. For example an + * array of [1,2,3,4] has the same meaning as [100, 200, 300, 400] or [10, 10, 20, 20, 30, 30, 40, 40] + * Every array element describes a time of the movement, so that in array of n-elements every element is + * describing (100 / n)% of the movement. In an array of [1,2,3,4] every element is responsible for + * 25% of time and the movement is accelerating - in the last 25% of time the mouse cursor is 4 times faster + * than it was in the first 25% of the time. + */ + public Flow(double[] characteristics) { + buckets = normalizeBuckets(characteristics); + } + + /** + * Normalizes the characteristics to have an average of AVERAGE_BUCKET_VALUE + * + * @param flowCharacteristics an array of values which describe how the mouse should move at each moment + * @return the normalized bucket array + */ + private double[] normalizeBuckets(double[] flowCharacteristics) { + double[] buckets = new double[flowCharacteristics.length]; + long sum = 0; + for (int i = 0; i < flowCharacteristics.length; i++) { + if (flowCharacteristics[i] < 0) { + throw new IllegalArgumentException("Invalid FlowCharacteristics at [" + i + "] : " + flowCharacteristics[i]); + } + sum += flowCharacteristics[i]; + } + + if (sum == 0) { + throw new IllegalArgumentException("Invalid FlowCharacteristics. All array elements can't be 0."); + } + /* + * By multiplying AVERAGE_BUCKET_VALUE to buckets.length we get a required fill for the buckets, + * For example if there are 5 buckets then 100 * 5 gives us 500, which is how much the buckets should + * contain on total ideally. Then we divide it by the sum which we got from adding all contents of characteristics + * array together. The resulting value describes the FlowCharacteristics array and how much is missing or + * overfilled in it. for example when we get 0.5, then we know it contains twice as much as our normalized + * buckets array should have and we multiply all characteristics values by 0.5, this preserves the + * characteristics, but reduces the values to levels our algorithm knows how to work with. + */ + double multiplier = (double) AVERAGE_BUCKET_VALUE * buckets.length / sum; + for (int i = 0; i < flowCharacteristics.length; i++) { + buckets[i] = flowCharacteristics[i] * multiplier; + } + return buckets; + } + + public double[] getFlowCharacteristics() { + return buckets; + } + + /** + * This returns step size for a single axis. + * + * @param distance the total distance current movement has on current axis from beginning to target in pixels + * @param steps number of steps the current movement involves + * @param completion value between 0 and 1, the value describes movement completion in time + * @return the step size which should be taken next + */ + public double getStepSize(double distance, int steps, double completion) { + // This is essentially how big is a single completion step, + // so we can expect next 'completion' is current completion + completionStep + double completionStep = 1d / steps; + // Define the first bucket we read from + double bucketFrom = (completion * buckets.length); + // Define the last bucket we read from + double bucketUntil = ((completion + completionStep) * buckets.length); + + double bucketContents = getBucketsContents(bucketFrom, bucketUntil); + // This shows how much distance is assigned to single contents value in the buckets. + // For example if this gets assigned to 0.4, then for every value in the bucket + // the cursor needs to travel 0.4 pixels, so for a bucket containing 50, the mouse + // travelling distance is 0.4 * 50 = 20pixels + double distancePerBucketContent = distance / (buckets.length * AVERAGE_BUCKET_VALUE); + + return bucketContents * distancePerBucketContent; + } + + /** + * Summarizes the bucket contents from bucketFrom to bucketUntil, where + * provided parameters may have decimal places. In that case the value + * from first or last bucket is just a fragment of it's full value, depending how + * large portion the decimal place contains. For example getBucketContents(0.6, 2.4) + * returns 0.4 * bucket[0] + 1 * bucket[1] + 0.4 * bucket[2] + * + * @param bucketFrom bucket from where to start reading + * @param bucketUntil bucket where to read + * @return the sum of the contents in the buckets + */ + private double getBucketsContents(double bucketFrom, double bucketUntil) { + double sum = 0; + for (int i = (int) bucketFrom; i < bucketUntil; i++) { + double value = buckets[i]; + double endMultiplier = 1; + double startMultiplier = 0; + if (bucketUntil < i + 1) { + endMultiplier = bucketUntil - (int) bucketUntil; + } + if ((int) bucketFrom == i) { + startMultiplier = bucketFrom - (int) bucketFrom; + } + value *= endMultiplier - startMultiplier; + sum += value; + } + + return sum; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/MouseMotionNature.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/MouseMotionNature.java new file mode 100644 index 0000000000..a2686812c8 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/MouseMotionNature.java @@ -0,0 +1,238 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support; + +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api.*; + +public class MouseMotionNature { + private double timeToStepsDivider; + private int minSteps; + + private int effectFadeSteps; + private int reactionTimeBaseMs; + private int reactionTimeVariationMs; + private DeviationProvider deviationProvider; + private NoiseProvider noiseProvider; + private OvershootManager overshootManager; + private MouseInfoAccessor mouseInfo; + private SystemCalls systemCalls; + private SpeedManager speedManager; + + /** + * Time to steps is how NaturalMouseMotion calculates how many locations need to be visited between + * start and end point. More steps means more smooth movement. Thus increasing this divider means less + * steps and decreasing means more steps. + * + * @return the divider which is used to get amount of steps from the planned movement time + */ + public double getTimeToStepsDivider() { + return timeToStepsDivider; + } + + /** + * Time to steps is how NaturalMouseMotion calculates how many locations need to be visited between + * start and end point. More steps means more smooth movement. Thus increasing this divider means less + * steps and decreasing means more steps. The default value should be as smooth as needed for any real + * purpose. So unless this really is the issue, you shouldn't touch this value. + * + * @param timeToStepsDivider the divider which is used to get amount of steps from the planned movement time + */ + public void setTimeToStepsDivider(double timeToStepsDivider) { + this.timeToStepsDivider = timeToStepsDivider; + } + + /** + * Minimum amount of steps that is taken to reach the target, this is used when calculation otherwise would + * lead to too few steps for smooth mouse movement, which can happen for very fast movements. + * + * @return the minimal amount of steps used. + */ + public int getMinSteps() { + return minSteps; + } + + /** + * Minimum amount of steps that is taken to reach the target, this is used when calculation otherwise would + * lead to too few steps for smooth mouse movement, which can happen for very fast movements. + * The default value should cover your needs, usually no need to touch this. + * + * @param minSteps the minimal amount of steps used + */ + public void setMinSteps(int minSteps) { + this.minSteps = minSteps; + } + + /** + * Effect fade decreases the noise and deviation effects linearly to 0 at the end of the mouse movement, + * so mouse would end up in the intended target pixel even when noise or deviation would otherwise + * add offset to mouse position. + * + * @return the number of steps before last the effect starts to fade + */ + public int getEffectFadeSteps() { + return effectFadeSteps; + } + + /** + * Effect fade decreases the noise and deviation effects linearly to 0 at the end of the mouse movement, + * so mouse would end up in the intended target pixel even when noise or deviation would otherwise + * add offset to mouse position. + * + * @param effectFadeSteps the number of steps before last the effect starts to fade + */ + public void setEffectFadeSteps(int effectFadeSteps) { + this.effectFadeSteps = effectFadeSteps; + } + + /** + * Get the minimal sleep time when overshoot or some other feature has caused mouse to miss the original target + * to prepare for next attempt to move the mouse to target. + * + * @return the sleep time + */ + public int getReactionTimeBaseMs() { + return reactionTimeBaseMs; + } + + /** + * Set the minimal sleep time when overshoot or some other feature has caused mouse to miss the original target + * to prepare for next attempt to move the mouse to target. + * + * @param reactionTimeBaseMs the sleep time + */ + public void setReactionTimeBaseMs(int reactionTimeBaseMs) { + this.reactionTimeBaseMs = reactionTimeBaseMs; + } + + /** + * Get the random sleep time when overshoot or some other feature has caused mouse to miss the original target + * to prepare for next attempt to move the mouse to target. Random part of this is added to the reactionTimeBaseMs. + * + * @return reactionTimeVariationMs the sleep time + */ + public int getReactionTimeVariationMs() { + return reactionTimeVariationMs; + } + + /** + * Set the random sleep time when overshoot or some other feature has caused mouse to miss the original target + * to prepare for next attempt to move the mouse to target. Random part of this is added to the reactionTimeBaseMs. + * + * @param reactionTimeVariationMs the sleep time + */ + public void setReactionTimeVariationMs(int reactionTimeVariationMs) { + this.reactionTimeVariationMs = reactionTimeVariationMs; + } + + /** + * Get the provider which is used to define how the MouseMotion trajectory is being deviated or arced + * + * @return the provider + */ + public DeviationProvider getDeviationProvider() { + return deviationProvider; + } + + /** + * Set the provider which is used to define how the MouseMotion trajectory is being deviated or arced. + * Alters the underlying nature instance in this factory. + * + * @param deviationProvider the provider + */ + public void setDeviationProvider(DeviationProvider deviationProvider) { + this.deviationProvider = deviationProvider; + } + + /** + * Get the provider which is used to make random mistakes in the trajectory of the moving mouse + * + * @return the provider + */ + public NoiseProvider getNoiseProvider() { + return noiseProvider; + } + + /** + * set the provider which is used to make random mistakes in the trajectory of the moving mouse. + * Alters the underlying nature instance in this factory. + * + * @param noiseProvider the provider + */ + public void setNoiseProvider(NoiseProvider noiseProvider) { + this.noiseProvider = noiseProvider; + } + + /** + * Get the accessor object, which MouseMotion uses to detect the position of mouse on screen. + * + * @return the accessor + */ + public MouseInfoAccessor getMouseInfo() { + return mouseInfo; + } + + /** + * Set the accessor object, which MouseMotion uses to detect the position of mouse on screen. + * + * @param mouseInfo the accessor object + */ + public void setMouseInfo(MouseInfoAccessor mouseInfo) { + this.mouseInfo = mouseInfo; + } + + /** + * Get a system call interface, which MouseMotion uses internally + * + * @return the interface + */ + public SystemCalls getSystemCalls() { + return systemCalls; + } + + /** + * Set a system call interface, which MouseMotion uses internally. + * + * @param systemCalls the interface + */ + public void setSystemCalls(SystemCalls systemCalls) { + this.systemCalls = systemCalls; + } + + /** + * Get the speed manager. SpeedManager controls how long does it take to complete a movement and within that + * time how slow or fast the cursor is moving at a particular moment, the flow of movement. + * + * @return the SpeedManager + */ + public SpeedManager getSpeedManager() { + return speedManager; + } + + /** + * Sets the speed manager. SpeedManager controls how long does it take to complete a movement and within that + * time how slow or fast the cursor is moving at a particular moment, the flow of movement. + * + * @param speedManager the SpeedManager + */ + public void setSpeedManager(SpeedManager speedManager) { + this.speedManager = speedManager; + } + + /** + * Get the manager that deals with overshoot properties. + * Overshoots provide a realistic way to simulate user trying to reach the destination with mouse, but miss. + * + * @return the manager + */ + public OvershootManager getOvershootManager() { + return overshootManager; + } + + /** + * Set the manager that deals with overshoot properties. + * Overshoots provide a realistic way to simulate user trying to reach the destination with mouse, but miss. + * + * @param overshootManager the manager + */ + public void setOvershootManager(OvershootManager overshootManager) { + this.overshootManager = overshootManager; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/ScreenAdjustedNature.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/ScreenAdjustedNature.java new file mode 100644 index 0000000000..e7825a600d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/ScreenAdjustedNature.java @@ -0,0 +1,83 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support; + + +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api.MouseInfoAccessor; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api.SystemCalls; + +import java.awt.*; + +/** + * This nature translates mouse coordinates to specified offset and screen dimension. + * Internally it wraps the SystemCalls and MouseInfoAccessor in proxies which handle the translations. + */ +public class ScreenAdjustedNature extends DefaultMouseMotionNature { + private final Point offset; + private final Dimension screenSize; + + public ScreenAdjustedNature(int x, int y, int x2, int y2) { + this(new Dimension(x2 - x, y2 - y), new Point(x, y)); + if (y2 <= y || x2 <= x) { + throw new IllegalArgumentException("Invalid range " + x + " " + y + " " + x2 + " " + y2); + } + } + + public ScreenAdjustedNature(Dimension screenSize, Point mouseOffset) { + this.screenSize = screenSize; + this.offset = mouseOffset; + } + + @Override + public void setMouseInfo(MouseInfoAccessor mouseInfo) { + super.setMouseInfo(new ProxyMouseInfo(mouseInfo)); + } + + @Override + public void setSystemCalls(SystemCalls systemCalls) { + super.setSystemCalls(new ProxySystemCalls(systemCalls)); + } + + private class ProxyMouseInfo implements MouseInfoAccessor { + private final MouseInfoAccessor underlying; + // This implementation reuses the point. + private final Point p = new Point(); + + public ProxyMouseInfo(MouseInfoAccessor underlying) { + this.underlying = underlying; + } + + @Override + public Point getMousePosition() { + Point realPointer = underlying.getMousePosition(); + p.setLocation(realPointer.x - offset.x, realPointer.y - offset.y); + return p; + } + } + + private class ProxySystemCalls implements SystemCalls { + private final SystemCalls underlying; + + public ProxySystemCalls(SystemCalls underlying) { + this.underlying = underlying; + } + + @Override + public long currentTimeMillis() { + return underlying.currentTimeMillis(); + } + + @Override + public void sleep(long time) { + underlying.sleep(time); + } + + @Override + public Dimension getScreenSize() { + return screenSize; + } + + @Override + public void setMousePosition(int x, int y) { + underlying.setMousePosition(x + offset.x, y + offset.y); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/SinusoidalDeviationProvider.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/SinusoidalDeviationProvider.java new file mode 100644 index 0000000000..ef034cb16f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/SinusoidalDeviationProvider.java @@ -0,0 +1,23 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support; + + +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api.DeviationProvider; + +public class SinusoidalDeviationProvider implements DeviationProvider { + public static final int DEFAULT_SLOPE_DIVIDER = 10; + private final double slopeDivider; + + public SinusoidalDeviationProvider(double slopeDivider) { + this.slopeDivider = slopeDivider; + } + + @Override + public DoublePoint getDeviation(double totalDistanceInPixels, double completionFraction) { + double deviationFunctionResult = (1 - Math.cos(completionFraction * Math.PI * 2)) / 2; + + double deviationX = totalDistanceInPixels / slopeDivider; + double deviationY = totalDistanceInPixels / slopeDivider; + + return new DoublePoint(deviationFunctionResult * deviationX, deviationFunctionResult * deviationY); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/mousemotion/MouseMovement.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/mousemotion/MouseMovement.java new file mode 100644 index 0000000000..898e3ce620 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/mousemotion/MouseMovement.java @@ -0,0 +1,34 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.mousemotion; + +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.Flow; + +public class MouseMovement { + public final int destX; + public final int destY; + public final double distance; + public final int xDistance; + public final int yDistance; + public final long time; + public final Flow flow; + + public MouseMovement(int destX, int destY, double distance, int xDistance, int yDistance, long time, Flow flow) { + this.destX = destX; + this.destY = destY; + this.distance = distance; + this.xDistance = xDistance; + this.yDistance = yDistance; + this.time = time; + this.flow = flow; + } + + @Override + public String toString() { + return "Movement{" + + "destX=" + destX + + ", destY=" + destY + + ", xDistance=" + xDistance + + ", yDistance=" + yDistance + + ", time=" + time + + '}'; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/mousemotion/MovementFactory.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/mousemotion/MovementFactory.java new file mode 100644 index 0000000000..9c2803e429 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/support/mousemotion/MovementFactory.java @@ -0,0 +1,105 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.mousemotion; + +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api.OvershootManager; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api.SpeedManager; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.Flow; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.util.Pair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.*; +import java.util.ArrayDeque; +import java.util.Iterator; + +public class MovementFactory { + private static final Logger log = LoggerFactory.getLogger(MovementFactory.class); + private final int xDest; + private final int yDest; + private final SpeedManager speedManager; + private final OvershootManager overshootManager; + private final Dimension screenSize; + + public MovementFactory(int xDest, int yDest, SpeedManager speedManager, + OvershootManager overshootManager, Dimension screenSize) { + this.xDest = xDest; + this.yDest = yDest; + this.speedManager = speedManager; + this.overshootManager = overshootManager; + this.screenSize = screenSize; + } + + public ArrayDeque createMovements(Point currentMousePosition) { + ArrayDeque movements = new ArrayDeque<>(); + int lastMousePositionX = currentMousePosition.x; + int lastMousePositionY = currentMousePosition.y; + int xDistance = xDest - lastMousePositionX; + int yDistance = yDest - lastMousePositionY; + + double initialDistance = Math.hypot(xDistance, yDistance); + Pair flowTime = speedManager.getFlowWithTime(initialDistance); + Flow flow = flowTime.x; + long mouseMovementMs = flowTime.y; + int overshoots = overshootManager.getOvershoots(flow, mouseMovementMs, initialDistance); + + if (overshoots == 0) { + movements.add(new MouseMovement(xDest, yDest, initialDistance, xDistance, yDistance, mouseMovementMs, flow)); + return movements; + } + + for (int i = overshoots; i > 0; i--) { + Point overshoot = overshootManager.getOvershootAmount( + xDest - lastMousePositionX, yDest - lastMousePositionY, mouseMovementMs, i + ); + int currentDestinationX = limitByScreenWidth(xDest + overshoot.x); + int currentDestinationY = limitByScreenHeight(yDest + overshoot.y); + xDistance = currentDestinationX - lastMousePositionX; + yDistance = currentDestinationY - lastMousePositionY; + double distance = Math.hypot(xDistance, yDistance); + flow = speedManager.getFlowWithTime(distance).x; + movements.add( + new MouseMovement(currentDestinationX, currentDestinationY, distance, xDistance, yDistance, mouseMovementMs, flow) + ); + lastMousePositionX = currentDestinationX; + lastMousePositionY = currentDestinationY; + // Apply for the next overshoot if exists. + mouseMovementMs = overshootManager.deriveNextMouseMovementTimeMs(mouseMovementMs, i - 1); + } + + Iterator it = movements.descendingIterator(); + + boolean remove = true; + // Remove overshoots from the end, which are matching the final destination, but keep those in middle of motion. + while (it.hasNext() && remove) { + MouseMovement movement = it.next(); + if (movement.destX == xDest && movement.destY == yDest) { + lastMousePositionX = movement.destX - movement.xDistance; + lastMousePositionY = movement.destY - movement.yDistance; + it.remove(); + } else { + remove = false; + } + } + + xDistance = xDest - lastMousePositionX; + yDistance = yDest - lastMousePositionY; + double distance = Math.hypot(xDistance, yDistance); + Pair movementToTargetFlowTime = speedManager.getFlowWithTime(distance); + long finalMovementTime = overshootManager.deriveNextMouseMovementTimeMs(movementToTargetFlowTime.y, 0); + MouseMovement finalMove = new MouseMovement( + xDest, yDest, distance, xDistance, yDistance, finalMovementTime, movementToTargetFlowTime.x + ); + movements.add(finalMove); + + return movements; + } + + private int limitByScreenWidth(int value) { + return Math.max(0, Math.min(screenSize.width - 1, value)); + } + + private int limitByScreenHeight(int value) { + return Math.max(0, Math.min(screenSize.height - 1, value)); + } + + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/tools/SystemDiagnosis.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/tools/SystemDiagnosis.java new file mode 100644 index 0000000000..ab8820a244 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/tools/SystemDiagnosis.java @@ -0,0 +1,60 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.tools; + + +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api.MouseInfoAccessor; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api.SystemCalls; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.DefaultMouseInfoAccessor; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.DefaultSystemCalls; + +import java.awt.*; + +public class SystemDiagnosis { + + /** + * Runs a diagnosis with default configuration, by setting mouse all over your screen and expecting to receive + * correct coordinates back. + * If java.awt.Robot cannot be constructed, then new RuntimeException is thrown. + * If no issues are found, then this method completes without throwing an error, otherwise IllegalStateException is + * thrown. + */ + public static void validateMouseMovement() { + try { + Robot robot = new Robot(); + validateMouseMovement(new DefaultSystemCalls(robot), new DefaultMouseInfoAccessor()); + } catch (AWTException e) { + throw new RuntimeException(e); + } + } + + /** + * Runs a diagnosis, by setting mouse all over your screen and expecting to receive correct coordinates back. + * If no issues are found, then this method completes without throwing an error, otherwise IllegalStateException is + * thrown. + * + * @param system a SystemCalls class which is used for setting the mouse position + * @param accessor a MouseInfoAccessor which is used for querying mouse position + */ + public static void validateMouseMovement(SystemCalls system, MouseInfoAccessor accessor) { + Dimension dimension = system.getScreenSize(); + for (int y = 0; y < dimension.height; y += 50) { + for (int x = 0; x < dimension.width; x += 50) { + system.setMousePosition(x, y); + + try { + Thread.sleep(1); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + Point p = accessor.getMousePosition(); + if (x != p.x || y != p.y) { + throw new IllegalStateException( + "Tried to move mouse to (" + x + ", " + y + "). Actually moved to (" + p.x + ", " + p.y + ")" + + "This means NaturalMouseMotion is not able to work optimally on this system as the cursor move " + + "calls may miss the target pixels on the screen." + ); + } + } + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/util/FactoryTemplates.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/util/FactoryTemplates.java new file mode 100644 index 0000000000..84b18a19a7 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/util/FactoryTemplates.java @@ -0,0 +1,279 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.util; + + +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.mouse.naturalmouse.api.MouseMotionFactory; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.api.SpeedManager; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.support.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class FactoryTemplates { + /** + *

Stereotypical granny using a computer with non-optical mouse from the 90s.

+ * Low speed, variating flow, lots of noise in movement. + * + * @return the factory + */ + public static MouseMotionFactory createGrannyMotionFactory() { + return createGrannyMotionFactory(new DefaultMouseMotionNature()); + } + + /** + *

Stereotypical granny using a computer with non-optical mouse from the 90s.

+ * Low speed, variating flow, lots of noise in movement. + * + * @param nature the nature for the template to be configured on + * @return the factory + */ + public static MouseMotionFactory createGrannyMotionFactory(MouseMotionNature nature) { + MouseMotionFactory factory = new MouseMotionFactory(nature); + List flows = new ArrayList<>(Arrays.asList( + new Flow(FlowTemplates.jaggedFlow()), + new Flow(FlowTemplates.random()), + new Flow(FlowTemplates.interruptedFlow()), + new Flow(FlowTemplates.interruptedFlow2()), + new Flow(FlowTemplates.adjustingFlow()), + new Flow(FlowTemplates.stoppingFlow()) + )); + DefaultSpeedManager manager = new DefaultSpeedManager(flows); + factory.setDeviationProvider(new SinusoidalDeviationProvider(9)); + factory.setNoiseProvider(new DefaultNoiseProvider(1.6)); + factory.getNature().setReactionTimeBaseMs(100); + + DefaultOvershootManager overshootManager = (DefaultOvershootManager) factory.getOvershootManager(); + if (Rs2AntibanSettings.simulateMistakes) + overshootManager.setOvershoots(3); + else + overshootManager.setOvershoots(0); + overshootManager.setMinDistanceForOvershoots(3); + overshootManager.setMinOvershootMovementMs(400); + overshootManager.setOvershootRandomModifierDivider(DefaultOvershootManager.OVERSHOOT_RANDOM_MODIFIER_DIVIDER / 2); + overshootManager.setOvershootSpeedupDivider(DefaultOvershootManager.OVERSHOOT_SPEEDUP_DIVIDER * 2); + + factory.getNature().setTimeToStepsDivider(DefaultMouseMotionNature.TIME_TO_STEPS_DIVIDER - 2); + manager.setMouseMovementBaseTimeMs(1000); + factory.setSpeedManager(manager); + return factory; + } + + /** + *

Robotic fluent movement.

+ * Custom speed, constant movement, no mistakes, no overshoots. + * + * @param motionTimeMsPer100Pixels approximate time a movement takes per 100 pixels of travelling + * @return the factory + */ + public static MouseMotionFactory createDemoRobotMotionFactory(long motionTimeMsPer100Pixels) { + return createDemoRobotMotionFactory(new DefaultMouseMotionNature(), motionTimeMsPer100Pixels); + } + + /** + *

Robotic fluent movement.

+ * Custom speed, constant movement, no mistakes, no overshoots. + * + * @param nature the nature for the template to be configured on + * @param motionTimeMsPer100Pixels approximate time a movement takes per 100 pixels of travelling + * @return the factory + */ + public static MouseMotionFactory createDemoRobotMotionFactory( + MouseMotionNature nature, long motionTimeMsPer100Pixels + ) { + MouseMotionFactory factory = new MouseMotionFactory(nature); + final Flow flow = new Flow(FlowTemplates.constantSpeed()); + double timePerPixel = motionTimeMsPer100Pixels / 100d; + SpeedManager manager = distance -> new Pair<>(flow, (long) (timePerPixel * distance)); + factory.setDeviationProvider((totalDistanceInPixels, completionFraction) -> DoublePoint.ZERO); + factory.setNoiseProvider(((random, xStepSize, yStepSize) -> DoublePoint.ZERO)); + + DefaultOvershootManager overshootManager = (DefaultOvershootManager) factory.getOvershootManager(); + overshootManager.setOvershoots(0); + + factory.setSpeedManager(manager); + return factory; + } + + /** + *

Gamer with fast reflexes and quick mouse movements.

+ * Quick movement, low noise, some deviation, lots of overshoots. + * + * @return the factory + */ + public static MouseMotionFactory createNormalGamerMotionFactory() { + return createNormalGamerMotionFactory(new DefaultMouseMotionNature()); + } + + /** + *

Gamer with fast reflexes and quick mouse movements.

+ * Quick movement, low noise, some deviation, lots of overshoots. + * + * @param nature the nature for the template to be configured on + * @return the factory + */ + public static MouseMotionFactory createNormalGamerMotionFactory(MouseMotionNature nature) { + int initialBaseTime = 150; + int maxBaseTime = 200; + int currentBaseTime = initialBaseTime; + if (Rs2AntibanSettings.simulateFatigue) + currentBaseTime = (Rs2Antiban.mouseFatigue.calculateBaseTimeWithNoise(currentBaseTime, maxBaseTime)); + MouseMotionFactory factory = new MouseMotionFactory(nature); + List flows = new ArrayList<>(Arrays.asList( + new Flow(FlowTemplates.variatingFlow()), + new Flow(FlowTemplates.slowStartupFlow()), + new Flow(FlowTemplates.slowStartup2Flow()), + new Flow(FlowTemplates.adjustingFlow()), + new Flow(FlowTemplates.jaggedFlow()) + )); + DefaultSpeedManager manager = new DefaultSpeedManager(flows); + factory.setDeviationProvider(new SinusoidalDeviationProvider(SinusoidalDeviationProvider.DEFAULT_SLOPE_DIVIDER)); + factory.setNoiseProvider(new DefaultNoiseProvider(DefaultNoiseProvider.DEFAULT_NOISINESS_DIVIDER)); + factory.getNature().setReactionTimeVariationMs(100); + manager.setMouseMovementBaseTimeMs(currentBaseTime); + + DefaultOvershootManager overshootManager = (DefaultOvershootManager) factory.getOvershootManager(); + if (Rs2AntibanSettings.simulateMistakes) + overshootManager.setOvershoots(4); + else + overshootManager.setOvershoots(0); + overshootManager.setMinDistanceForOvershoots(3); + overshootManager.setMinOvershootMovementMs(250); + + factory.setSpeedManager(manager); + return factory; + } + + /** + *

Gamer with fast reflexes and quick mouse movements.

+ * Quick movement, low noise, some deviation, lots of overshoots. + * + * @return the factory + */ + public static MouseMotionFactory createFastGamerMotionFactory() { + return createFastGamerMotionFactory(new DefaultMouseMotionNature()); + } + + /** + *

Gamer with fast reflexes and quick mouse movements.

+ * Quick movement, low noise, some deviation, lots of overshoots. + * + * @param nature the nature for the template to be configured on + * @return the factory + */ + public static MouseMotionFactory createFastGamerMotionFactory(MouseMotionNature nature) { + int initialBaseTime = 120; + int maxBaseTime = 170; + int currentBaseTime = initialBaseTime; + if (Rs2AntibanSettings.simulateFatigue) + currentBaseTime = (Rs2Antiban.mouseFatigue.calculateBaseTimeWithNoise(currentBaseTime, maxBaseTime)); + MouseMotionFactory factory = new MouseMotionFactory(nature); + List flows = new ArrayList<>(Arrays.asList( + new Flow(FlowTemplates.variatingFlow()), + new Flow(FlowTemplates.slowStartupFlow()), + new Flow(FlowTemplates.slowStartup2Flow()), + new Flow(FlowTemplates.adjustingFlow()), + new Flow(FlowTemplates.jaggedFlow()) + )); + DefaultSpeedManager manager = new DefaultSpeedManager(flows); + factory.setDeviationProvider(new SinusoidalDeviationProvider(SinusoidalDeviationProvider.DEFAULT_SLOPE_DIVIDER)); + factory.setNoiseProvider(new DefaultNoiseProvider(DefaultNoiseProvider.DEFAULT_NOISINESS_DIVIDER)); + factory.getNature().setReactionTimeVariationMs(100); + manager.setMouseMovementBaseTimeMs(currentBaseTime); + + DefaultOvershootManager overshootManager = (DefaultOvershootManager) factory.getOvershootManager(); + if (Rs2AntibanSettings.simulateMistakes) + overshootManager.setOvershoots(3); + else + overshootManager.setOvershoots(0); + overshootManager.setMinDistanceForOvershoots(3); + overshootManager.setMinOvershootMovementMs(130); + + factory.setSpeedManager(manager); + return factory; + } + + // Super fast gamer + public static MouseMotionFactory createSuperFastGamerMotionFactory() { + return createSuperFastGamerMotionFactory(new DefaultMouseMotionNature()); + } + + public static MouseMotionFactory createSuperFastGamerMotionFactory(MouseMotionNature nature) { + int initialBaseTime = 90; + int maxBaseTime = 120; + int currentBaseTime = initialBaseTime; + if (Rs2AntibanSettings.simulateFatigue) + currentBaseTime = (Rs2Antiban.mouseFatigue.calculateBaseTimeWithNoise(currentBaseTime, maxBaseTime)); + + MouseMotionFactory factory = new MouseMotionFactory(nature); + List flows = new ArrayList<>(Arrays.asList( + new Flow(FlowTemplates.variatingFlow()), + new Flow(FlowTemplates.slowStartupFlow()), + new Flow(FlowTemplates.slowStartup2Flow()), + new Flow(FlowTemplates.adjustingFlow()), + new Flow(FlowTemplates.jaggedFlow()) + )); + DefaultSpeedManager manager = new DefaultSpeedManager(flows); + factory.setDeviationProvider(new SinusoidalDeviationProvider(SinusoidalDeviationProvider.DEFAULT_SLOPE_DIVIDER)); + factory.setNoiseProvider(new DefaultNoiseProvider(DefaultNoiseProvider.DEFAULT_NOISINESS_DIVIDER)); + factory.getNature().setReactionTimeVariationMs(90); + manager.setMouseMovementBaseTimeMs(currentBaseTime); + + DefaultOvershootManager overshootManager = (DefaultOvershootManager) factory.getOvershootManager(); + if (Rs2AntibanSettings.simulateMistakes) + overshootManager.setOvershoots(2); + else + overshootManager.setOvershoots(0); + overshootManager.setMinDistanceForOvershoots(3); + overshootManager.setMinOvershootMovementMs(100); + + factory.setSpeedManager(manager); + return factory; + } + + /** + *

Standard computer user with average speed and movement mistakes

+ * medium noise, medium speed, medium noise and deviation. + * + * @return the factory + */ + public static MouseMotionFactory createAverageComputerUserMotionFactory() { + return createAverageComputerUserMotionFactory(new DefaultMouseMotionNature()); + } + + /** + *

Standard computer user with average speed and movement mistakes

+ * medium noise, medium speed, medium noise and deviation. + * + * @param nature the nature for the template to be configured on + * @return the factory + */ + public static MouseMotionFactory createAverageComputerUserMotionFactory(MouseMotionNature nature) { + MouseMotionFactory factory = new MouseMotionFactory(nature); + List flows = new ArrayList<>(Arrays.asList( + new Flow(FlowTemplates.variatingFlow()), + new Flow(FlowTemplates.interruptedFlow()), + new Flow(FlowTemplates.interruptedFlow2()), + new Flow(FlowTemplates.slowStartupFlow()), + new Flow(FlowTemplates.slowStartup2Flow()), + new Flow(FlowTemplates.adjustingFlow()), + new Flow(FlowTemplates.jaggedFlow()), + new Flow(FlowTemplates.stoppingFlow()) + )); + DefaultSpeedManager manager = new DefaultSpeedManager(flows); + factory.setDeviationProvider(new SinusoidalDeviationProvider(SinusoidalDeviationProvider.DEFAULT_SLOPE_DIVIDER)); + factory.setNoiseProvider(new DefaultNoiseProvider(DefaultNoiseProvider.DEFAULT_NOISINESS_DIVIDER)); + factory.getNature().setReactionTimeVariationMs(110); + manager.setMouseMovementBaseTimeMs(400); + + DefaultOvershootManager overshootManager = (DefaultOvershootManager) factory.getOvershootManager(); + if (Rs2AntibanSettings.simulateMistakes) + overshootManager.setOvershoots(4); + else + overshootManager.setOvershoots(0); + + factory.setSpeedManager(manager); + return factory; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/util/FlowTemplates.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/util/FlowTemplates.java new file mode 100644 index 0000000000..622811527d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/util/FlowTemplates.java @@ -0,0 +1,99 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.util; + +import java.util.Arrays; + +public class FlowTemplates { + public static double[] variatingFlow() { + return new double[]{ + 10, 13, 14, 19, 16, 13, 15, 22, 56, 90, 97, 97, 66, 51, 50, 66, 91, 95, 87, 96, 98, + 88, 70, 62, 57, 63, 79, 93, 98, 97, 100, 104, 83, 49, 37, 53, 68, 73, 61, 51, 64, 107, + 103, 111, 94, 88, 95, 86, 88, 97, 108, 85, 86, 74, 72, 73, 58, 50, 50, 60, 62, 61, 52, + 53, 44, 30, 21, 25, 21, 17, 16, 13, 8, 2, 6, 9, 6, 3, 7, 12, 13, 15, 11, 9, + 9, 7, 6, 4, 1, 2, 3, 2, 2, 11, 15, 7, 1, 0, 0, 1 + }; + } + + public static double[] interruptedFlow() { + return new double[]{ + 12, 11, 10, 20, 24, 19, 26, 15, 9, 9, 10, 24, 26, 30, 24, 49, 72, 60, 81, 113, 82, + 99, 67, 10, 7, 7, 7, 10, 8, 7, 9, 6, 6, 7, 10, 11, 12, 8, 7, 3, 0, 2, + 8, 10, 10, 12, 6, 4, 4, 3, 8, 11, 11, 11, 11, 13, 11, 20, 25, 18, 21, 23, 56, + 40, 36, 58, 69, 60, 63, 51, 87, 71, 86, 66, 115, 97, 80, 65, 50, 66, 57, 24, 11, 11, + 7, 3, 0, 0, 1, 3, 3, 5, 6, 12, 11, 7, 11, 17, 17, 23 + }; + } + + public static double[] interruptedFlow2() { + return new double[]{ + 12, 11, 10, 20, 24, 19, 26, 15, 9, 9, 10, 24, 26, 30, 24, 49, 72, 60, 81, 113, 82, + 99, 67, 10, 12, 8, 11, 15, 16, 17, 17, 12, 16, 37, 10, 25, 12, 11, 41, 10, 12, 11, + 40, 36, 52, 61, 60, 64, 51, 82, 71, 81, 66, 105, 92, 59, 65, 51, 66, 54, 21, 21, 12, + 40, 36, 58, 69, 60, 63, 51, 87, 71, 86, 66, 115, 97, 80, 65, 50, 66, 57, 24, 11, 11, + 7, 3, 0, 0, 1, 3, 3, 5, 6, 12, 11, 7, 11, 17, 17, 23 + }; + } + + + public static double[] slowStartupFlow() { + return new double[]{ + 8, 5, 1, 1, 1, 2, 2, 3, 3, 3, 5, 7, 9, 10, 10, 11, 11, 11, 12, 12, 13, + 15, 14, 13, 15, 15, 17, 17, 18, 18, 20, 19, 20, 20, 19, 20, 19, 20, 21, 22, 20, 17, + 20, 22, 18, 20, 21, 18, 20, 20, 18, 20, 19, 21, 19, 19, 19, 19, 20, 19, 20, 21, 19, + 19, 17, 21, 21, 17, 19, 18, 20, 18, 19, 24, 34, 43, 35, 40, 41, 42, 42, 38, 40, 40, + 37, 36, 42, 40, 63, 85, 98, 92, 103, 102, 95, 86, 70, 52, 31, 19 + }; + } + + + public static double[] slowStartup2Flow() { + return new double[]{ + 7, 2, 1, 2, 2, 3, 5, 9, 10, 10, 11, 13, 13, 10, 4, 1, 1, 2, 3, 4, 6, + 9, 11, 11, 10, 14, 11, 9, 2, 1, 2, 2, 3, 4, 8, 9, 10, 11, 11, 13, 13, 15, + 14, 15, 18, 17, 19, 21, 20, 19, 18, 20, 20, 20, 20, 19, 20, 19, 19, 18, 20, 20, 19, + 20, 18, 20, 21, 19, 21, 18, 19, 25, 37, 37, 35, 41, 43, 41, 41, 40, 48, 81, 108, 91, + 88, 74, 46, 19, 46, 84, 35, 14, 19, 12, 13, 18, 38, 35, 11, 4 + }; + } + + public static double[] jaggedFlow() { + return new double[]{ + 52, 106, 122, 8, 6, 117, 32, 2, 68, 34, 21, 81, 61, 86, 55, 4, 104, 21, 51, 8, 93, + 90, 43, 65, 82, 31, 40, 115, 107, 13, 35, 73, 81, 67, 31, 79, 57, 100, 55, 64, 13, 54, + 18, 68, 82, 61, 11, 84, 37, 20, 68, 33, 36, 55, 68, 75, 56, 20, 41, 120, 63, 72, 102, + 49, 4, 48, 69, 50, 35, 49, 54, 19, 95, 121, 26, 78, 31, 62, 53, 123, 73, 22, 39, 72, + 98, 33, 26, 5, 103, 23, 75, 35, 69, 33, 44, 12, 10, 101, 122, 19 + }; + } + + public static double[] stoppingFlow() { + return new double[]{ + 8, 20, 39, 48, 66, 71, 79, 57, 29, 5, 2, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 6, 10, 12, 15, 19, + 37, 60, 100, 103, 98, 82, 87, 74, 65, 51, 57, 54, 61, 46, 38, 16 + }; + } + + public static double[] adjustingFlow() { + return new double[]{ + 1, 1, 1, 3, 8, 7, 2, 2, 4, 8, 6, 3, 7, 13, 18, 19, 24, 35, 26, 14, 31, + 43, 49, 55, 61, 67, 61, 50, 43, 37, 30, 16, 5, 4, 4, 3, 3, 3, 4, 4, 3, + 2, 2, 3, 10, 14, 10, 7, 5, 5 + }; + } + + public static double[] random() { + double[] result = new double[100]; + for (int i = 0; i < result.length; i++) { + result[i] = (int) (Math.random() * 100); + } + return result; + } + + public static double[] constantSpeed() { + double[] flowBuckets = new double[10]; + Arrays.fill(flowBuckets, 100); + return flowBuckets; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/util/FlowUtil.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/util/FlowUtil.java new file mode 100644 index 0000000000..aa79478cf9 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/util/FlowUtil.java @@ -0,0 +1,113 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.util; + +import java.util.Arrays; +import java.util.function.Function; + +public class FlowUtil { + /** + * Stretch flow to longer length. Tries to fill the caps with averages. + *

+ * This is an unintuitive method, because it turns out that, for example, array size of 3 + * scales better to array size of 5 than it does to array size of 6. [1, 2, 3] can be + * easily scaled to [1, 1.5, 2, 2.5, 3], but it's not possible without recalculating middle number (2) + * with array size of 6, simplistic solutions quickly would run to trouble like this [1, 1.5, 2, 2.5, 3, (3)? ] + * or maybe: [1, 1.5, 2, 2.5, ..., 3 ]. The correct solution would correctly scale the middle numbers + * + * @param flow the original flow + * @param targetLength the resulting flow length + * @return the resulting flow + */ + public static double[] stretchFlow(double[] flow, int targetLength) { + return stretchFlow(flow, targetLength, a -> a); + } + + /** + * Stretch flow to longer length. Tries to fill the caps with averages. + *

+ * This is an unintuitive method, because it turns out that, for example, array size of 3 + * scales better to array size of 5 than it does to array size of 6. [1, 2, 3] can be + * easily scaled to [1, 1.5, 2, 2.5, 3], but it's not possible without recalculating middle number (2) + * with array size of 6, simplistic solutions quickly would run to trouble like this [1, 1.5, 2, 2.5, 3, (3)? ] + * or maybe: [1, 1.5, 2, 2.5, ..., 3 ]. The correct solution would correctly scale the middle numbers + * over several indexes. + * + * @param flow the original flow + * @param targetLength the resulting flow length + * @param modifier modifies the resulting values, you can use this to provide noise or amplify + * the flow characteristics. + * @return the resulting flow + */ + public static double[] stretchFlow(double[] flow, int targetLength, Function modifier) { + if (targetLength < flow.length) { + throw new IllegalArgumentException("Target bucket length smaller than flow. " + targetLength + " vs " + flow.length); + } + double[] result; + int tempLength = targetLength; + + if (flow.length != 1 && (tempLength - flow.length) % (flow.length - 1) != 0) { + tempLength = (flow.length - 1) * (tempLength - flow.length) + 1; + } + + result = new double[tempLength]; + int insider = flow.length - 2; + int stepLength = (int) ((tempLength - 2) / (double) (insider + 1)) + 1; + int countToNextStep = stepLength; + int fillValueIndex = 0; + for (int i = 0; i < tempLength; i++) { + double fillValueBottom = flow[fillValueIndex]; + double fillValueTop = fillValueIndex + 1 < flow.length ? flow[fillValueIndex + 1] : flow[fillValueIndex]; + + double completion = (stepLength - countToNextStep) / (double) stepLength; + + result[i] = fillValueBottom * (1 - completion) + fillValueTop * completion; + + countToNextStep--; + + if (countToNextStep == 0) { + countToNextStep = stepLength; + fillValueIndex++; + } + } + + if (tempLength != targetLength) { + result = reduceFlow(result, targetLength); + } + + return Arrays.stream(result).map(modifier::apply).toArray(); + } + + /** + * Reduction causes loss of information, so the resulting flow is always 'good enough', but is not quaranteed + * to be equivalent, just a shorter version of the original flow + * + * @param flow the original flow + * @param targetLength the resulting array length + * @return the resulting flow + */ + public static double[] reduceFlow(double[] flow, int targetLength) { + if (flow.length <= targetLength) { + throw new IllegalArgumentException("Bad arguments [" + flow.length + ", " + targetLength + "]"); + } + + double multiplier = targetLength / (double) flow.length; + double[] result = new double[targetLength]; + for (int i = 0; i < flow.length; i++) { + double index = (i * multiplier); + double untilIndex = (i + 1) * multiplier; + int indexInt = (int) index; + int untilIndexInt = (int) untilIndex; + if (indexInt != untilIndexInt) { + double resultIndexPortion = 1 - (index - indexInt); + double nextResultIndexPortion = untilIndex - untilIndexInt; + result[indexInt] += flow[i] * resultIndexPortion; + if (untilIndexInt < result.length) { + result[untilIndexInt] += flow[i] * nextResultIndexPortion; + } + } else { + result[indexInt] += flow[i] * (untilIndex - index); + } + } + + return result; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/util/MathUtil.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/util/MathUtil.java new file mode 100644 index 0000000000..d24d06a044 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/util/MathUtil.java @@ -0,0 +1,18 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.util; + +public class MathUtil { + /** + * Rounds value towards target to exact integer value. + * + * @param value the value to be rounded + * @param target the target to be rounded towards + * @return the rounded value + */ + public static int roundTowards(double value, int target) { + if (target > value) { + return (int) Math.ceil(value); + } else { + return (int) Math.floor(value); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/util/Pair.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/util/Pair.java new file mode 100644 index 0000000000..8ee71dc4b3 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/util/Pair.java @@ -0,0 +1,11 @@ +package net.runelite.client.plugins.microbot.util.mouse.naturalmouse.util; + +public class Pair { + public final X x; + public final Y y; + + public Pair(X x, Y y) { + this.x = x; + this.y = y; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/npc/Rs2Npc.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/npc/Rs2Npc.java index de8f1d1990..a89c5c3499 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/npc/Rs2Npc.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/npc/Rs2Npc.java @@ -9,13 +9,12 @@ import net.runelite.client.plugins.microbot.util.camera.Rs2Camera; import net.runelite.client.plugins.microbot.util.combat.Rs2Combat; import net.runelite.client.plugins.microbot.util.menu.NewMenuEntry; +import net.runelite.client.plugins.microbot.util.misc.Rs2UiHelper; import net.runelite.client.plugins.microbot.util.player.Rs2Player; import net.runelite.client.plugins.microbot.util.tile.Rs2Tile; import net.runelite.client.plugins.microbot.util.walker.Rs2Walker; import org.jetbrains.annotations.Nullable; -import java.awt.*; -import java.util.List; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -195,7 +194,7 @@ public static boolean interact(NPC npc, String action) { MenuAction menuAction = getMenuAction(index); if (menuAction != null) { - Microbot.doInvoke(new NewMenuEntry(0, 0, menuAction.getId(), npc.getIndex(), -1, npc.getName()), new Rectangle(npc.getCanvasTilePoly().getBounds())); + Microbot.doInvoke(new NewMenuEntry(0, 0, menuAction.getId(), npc.getIndex(), -1, npc.getName()), Rs2UiHelper.getNpcClickbox(npc)); } } catch (Exception ex) { @@ -255,7 +254,7 @@ public static boolean attack(int npcId) { } public static boolean attack(String npcName) { - return attack(Arrays.asList(npcName)); + return attack(Collections.singletonList(npcName)); } public static boolean attack(List npcNames) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/stretchedmode/StretchedModePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/stretchedmode/StretchedModePlugin.java index 2ae8369b9b..98bd13bcc1 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/stretchedmode/StretchedModePlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/stretchedmode/StretchedModePlugin.java @@ -34,7 +34,6 @@ import net.runelite.client.input.MouseManager; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.plugins.microbot.Microbot; import javax.inject.Inject; @@ -76,7 +75,7 @@ protected void startUp() client.setStretchedEnabled(true); updateConfig(); - Microbot.showMessage("ALERT! Microbot has noticed that your StretchMode plugin is enabled. Please disable this plugin to avoid weird behavior."); + //Microbot.showMessage("ALERT! Microbot has noticed that your StretchMode plugin is enabled. Please disable this plugin to avoid weird behavior."); } @Override diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/stretchedmode/TranslateMouseListener.java b/runelite-client/src/main/java/net/runelite/client/plugins/stretchedmode/TranslateMouseListener.java index 83a68f8e9f..b0fcd709e2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/stretchedmode/TranslateMouseListener.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/stretchedmode/TranslateMouseListener.java @@ -25,13 +25,15 @@ */ package net.runelite.client.plugins.stretchedmode; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.event.MouseEvent; -import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; import net.runelite.client.input.MouseListener; +import javax.inject.Inject; +import java.awt.*; +import java.awt.event.MouseEvent; + +@Slf4j public class TranslateMouseListener implements MouseListener { private final Client client; @@ -86,6 +88,11 @@ public MouseEvent mouseMoved(MouseEvent mouseEvent) private MouseEvent translateEvent(MouseEvent e) { + // Check if the event source is "Microbot" + if ("Microbot".equals(e.getSource().toString())) { + // Ignore the event by returning null or the original event (decide based on your needs) + return e; // or return e; if you want to pass the event unchanged + } Dimension stretchedDimensions = client.getStretchedDimensions(); Dimension realDimensions = client.getRealDimensions(); diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/activity.png b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/activity.png new file mode 100644 index 0000000000000000000000000000000000000000..f620bf1da95598286318e85cc2eea6507eddb886 GIT binary patch literal 21873 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 Date: Sat, 10 Aug 2024 16:46:05 +0200 Subject: [PATCH 05/52] MLM Antiban update 1 --- .../mining/motherloadmine/MotherloadMineOverlay.java | 3 ++- .../mining/motherloadmine/MotherloadMineScript.java | 7 +++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/motherloadmine/MotherloadMineOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/motherloadmine/MotherloadMineOverlay.java index 4dd4c142b5..094a31a6e7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/motherloadmine/MotherloadMineOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/motherloadmine/MotherloadMineOverlay.java @@ -1,5 +1,6 @@ package net.runelite.client.plugins.microbot.mining.motherloadmine; +import net.runelite.client.plugins.microbot.util.antiban.Rs2Antiban; import net.runelite.client.ui.overlay.OverlayPanel; import net.runelite.client.ui.overlay.OverlayPosition; import net.runelite.client.ui.overlay.components.LineComponent; @@ -30,7 +31,7 @@ public Dimension render(Graphics2D graphics) { .build()); - //Rs2Antiban.renderAntibanOverlayComponents(panelComponent); + Rs2Antiban.renderAntibanOverlayComponents(panelComponent); addEmptyLine(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/motherloadmine/MotherloadMineScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/motherloadmine/MotherloadMineScript.java index 9740ad4949..bbecd145fb 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/motherloadmine/MotherloadMineScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/motherloadmine/MotherloadMineScript.java @@ -49,9 +49,7 @@ public boolean run(MotherloadMineConfig config) { } private void initialize() { - //Rs2Antiban.isEnabled = true; - //Rs2Antiban.simulateAttentionSpan = true; - //Rs2Antiban.setActivity(Activity.MOTHERLODE_MINE); + Rs2Antiban.antibanSetupTemplates.applyMiningSetup(); miningSpot = MLMMiningSpot.IDLE; status = MLMStatus.IDLE; emptySack = false; @@ -149,7 +147,8 @@ private void handleMining() { if (walkToMiningSpot()) { if (Rs2Player.isMoving()) return; mineVein(); - //Rs2Antiban.actionCooldown(); + Rs2Antiban.actionCooldown(); + Rs2Antiban.takeMicroBreakByChance(); } } } From 529b1f9a64a495434dc0ba083c284c34eb0c6e7d Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sat, 10 Aug 2024 16:47:06 +0200 Subject: [PATCH 06/52] MLM Antiban 2 --- .../mining/motherloadmine/MotherloadMineScript.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/motherloadmine/MotherloadMineScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/motherloadmine/MotherloadMineScript.java index bbecd145fb..c9ac000f92 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/motherloadmine/MotherloadMineScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/motherloadmine/MotherloadMineScript.java @@ -8,6 +8,9 @@ import net.runelite.client.plugins.microbot.Script; import net.runelite.client.plugins.microbot.mining.motherloadmine.enums.MLMMiningSpot; import net.runelite.client.plugins.microbot.mining.motherloadmine.enums.MLMStatus; +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.antiban.enums.ActivityIntensity; import net.runelite.client.plugins.microbot.util.bank.Rs2Bank; import net.runelite.client.plugins.microbot.util.combat.Rs2Combat; import net.runelite.client.plugins.microbot.util.equipment.Rs2Equipment; @@ -24,7 +27,7 @@ @Slf4j public class MotherloadMineScript extends Script { - public static final String version = "1.5.4"; + public static final String version = "1.6.4"; private static final WorldArea UPSTAIRS = new WorldArea(new WorldPoint(3747, 5676, 0), 7, 8); private static final WorldPoint HOPPER = new WorldPoint(3748, 5674, 0); private static final int UPPER_FLOOR_HEIGHT = -490; @@ -72,7 +75,7 @@ private void executeTask() { return; } - //if(Rs2Antiban.isActionCooldownActive) return; + if (Rs2AntibanSettings.actionCooldownActive) return; if (Rs2Player.isAnimating() || Microbot.getClient().getLocalPlayer().isInteracting()) return; @@ -84,9 +87,11 @@ private void executeTask() { case IDLE: return; case MINING: + Rs2Antiban.setActivityIntensity(Rs2Antiban.getActivity().getActivityIntensity()); handleMining(); break; case EMPTY_SACK: + Rs2Antiban.setActivityIntensity(ActivityIntensity.EXTREME); emptySack(); break; case FIXING_WATERWHEEL: @@ -173,6 +178,7 @@ private void emptySack() { bank(); } emptySack = false; + Rs2Antiban.takeMicroBreakByChance(); status = MLMStatus.IDLE; } From fd9a7e9df515fd1ad8047c83003b872d7e9875d9 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sat, 10 Aug 2024 16:48:10 +0200 Subject: [PATCH 07/52] Amethyst Antiban update --- .../mining/amethyst/AmethystMiningConfig.java | 33 ++++++- .../amethyst/AmethystMiningOverlay.java | 2 + .../mining/amethyst/AmethystMiningScript.java | 97 +++++++++++++++---- .../enums/AmethystCraftingOption.java | 31 ++++++ .../mining/amethyst/enums/MiningSpot.java | 2 +- .../mining/amethyst/enums/Status.java | 3 +- 6 files changed, 144 insertions(+), 24 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/enums/AmethystCraftingOption.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/AmethystMiningConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/AmethystMiningConfig.java index 4892be1649..d710c432a8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/AmethystMiningConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/AmethystMiningConfig.java @@ -3,10 +3,19 @@ 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.mining.amethyst.enums.AmethystCraftingOption; @ConfigGroup("AmethystMining") public interface AmethystMiningConfig extends Config { + @ConfigSection( + name = "Chiseling Options", + description = "Options related to chiseling amethysts", + position = 2 + ) + String chiselingOptionsSection = "chiselingOptionsSection"; + @ConfigItem( keyName = "guide", name = "How to use", @@ -26,4 +35,26 @@ default String GUIDE() { default boolean pickAxeInInventory() { return false; } -} \ No newline at end of file + + @ConfigItem( + keyName = "chiselAmethysts", + name = "Chisel Amethysts", + description = "If enabled, the player will chisel the amethysts after mining.", + position = 0, + section = chiselingOptionsSection + ) + default boolean chiselAmethysts() { + return false; + } + + @ConfigItem( + keyName = "amethystCraftingOption", + name = "Item", + description = "Choose what to craft from amethysts.", + position = 1, + section = chiselingOptionsSection + ) + default AmethystCraftingOption amethystCraftingOption() { + return AmethystCraftingOption.BOLT_TIPS; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/AmethystMiningOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/AmethystMiningOverlay.java index 2e04e86376..e57aff2c1a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/AmethystMiningOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/AmethystMiningOverlay.java @@ -1,5 +1,6 @@ package net.runelite.client.plugins.microbot.mining.amethyst; +import net.runelite.client.plugins.microbot.util.antiban.Rs2Antiban; import net.runelite.client.ui.overlay.OverlayPanel; import net.runelite.client.ui.overlay.OverlayPosition; import net.runelite.client.ui.overlay.components.LineComponent; @@ -29,6 +30,7 @@ public Dimension render(Graphics2D graphics) { addEmptyLine(); + Rs2Antiban.renderAntibanOverlayComponents(panelComponent); addEmptyLine(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/AmethystMiningScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/AmethystMiningScript.java index 1442e80619..53ac4f599f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/AmethystMiningScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/AmethystMiningScript.java @@ -1,30 +1,40 @@ package net.runelite.client.plugins.microbot.mining.amethyst; +import net.runelite.api.ItemID; +import net.runelite.api.Skill; import net.runelite.api.TileObject; import net.runelite.api.WallObject; import net.runelite.api.coords.WorldPoint; +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.mining.amethyst.enums.AmethystCraftingOption; import net.runelite.client.plugins.microbot.mining.amethyst.enums.MiningSpot; import net.runelite.client.plugins.microbot.mining.amethyst.enums.Status; +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.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.combat.Rs2Combat; 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.inventory.Rs2Inventory; +import net.runelite.client.plugins.microbot.util.inventory.Rs2Item; +import net.runelite.client.plugins.microbot.util.keyboard.Rs2Keyboard; 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.Comparator; import java.util.concurrent.TimeUnit; public class AmethystMiningScript extends Script { - public static String version = "1.0.0"; + public static String version = "1.1.0"; public static Status status = Status.IDLE; public static WallObject oreVein; private static AmethystMiningConfig config; - private static MiningSpot miningSpot; + private static MiningSpot miningSpot = MiningSpot.NULL; private String pickAxeInInventory = ""; public boolean run(AmethystMiningConfig config) { @@ -35,19 +45,28 @@ public boolean run(AmethystMiningConfig config) { return true; } + private boolean isClickHereToPlayButtonVisible() { + Widget clickHereToPlayButton = Rs2Widget.getWidget(24772680); + return (clickHereToPlayButton != null && !Microbot.getClientThread().runOnClientThread(clickHereToPlayButton::isHidden)); + } + private void executeTask() { - if (!super.run() || !Microbot.isLoggedIn()) { - oreVein = null; + if (!super.run() || !Microbot.isLoggedIn() || isClickHereToPlayButtonVisible()) { miningSpot = MiningSpot.NULL; + oreVein = null; return; } - + if (config.pickAxeInInventory() && pickAxeInInventory.isEmpty()) { + pickAxeInInventory = Rs2Inventory.get("pickaxe").name; + } if (pickAxeInInventory.isEmpty() && config.pickAxeInInventory()) { Microbot.showMessage("Pickaxe was not found in your inventory"); sleep(5000); return; } + if (Rs2AntibanSettings.actionCooldownActive) return; + if (Rs2Player.isAnimating() || Microbot.getClient().getLocalPlayer().isInteracting()) return; handleDragonPickaxeSpec(); @@ -62,8 +81,10 @@ private void executeTask() { case BANKING: bankItems(); break; + case CHISELING: + chiselAmethysts(); + break; } - } private void handleDragonPickaxeSpec() { @@ -73,13 +94,15 @@ private void handleDragonPickaxeSpec() { } private void handleInventory() { - if (!Rs2Inventory.isFull()) { status = Status.MINING; - } else if (Rs2Inventory.isFull()) { + } else { oreVein = null; miningSpot = MiningSpot.NULL; - status = Status.BANKING; + if (config.chiselAmethysts()) + status = Status.CHISELING; + else + status = Status.BANKING; } } @@ -95,7 +118,6 @@ private void bank() { Rs2Bank.depositAllExcept(pickAxeInInventory); sleep(100, 300); - if (config.pickAxeInInventory() && !Rs2Inventory.hasItem(pickAxeInInventory)) { Rs2Bank.withdrawOne(pickAxeInInventory); } @@ -103,13 +125,39 @@ private void bank() { } } + + private void chiselAmethysts() { + AmethystCraftingOption craftingOption = config.amethystCraftingOption(); + int requiredLevel = craftingOption.getRequiredLevel(); + Rs2Item chisel = Rs2Inventory.get("chisel"); + Rs2Inventory.moveItemToSlot(chisel, 27); + sleepUntil(() -> Rs2Inventory.slotContains(27, "chisel"), 5000); + if (Microbot.getClient().getRealSkillLevel(Skill.CRAFTING) >= requiredLevel) { + Rs2Inventory.combineClosest(ItemID.CHISEL, ItemID.AMETHYST); + sleepUntil(() -> Rs2Widget.getWidget(17694733) != null); + Rs2Keyboard.keyPress(craftingOption.getDialogOption()); + sleepUntil(() -> !Rs2Inventory.hasItem(ItemID.AMETHYST), 40000); + Rs2Antiban.takeMicroBreakByChance(); + + } else { + Microbot.showMessage("You do not have the required crafting level to make " + craftingOption.getDisplayName()); + status = Status.BANKING; + } + } + private void handleMining() { if (oreVein != null) return; if (miningSpot == MiningSpot.NULL) miningSpot = MiningSpot.getRandomMiningSpot(); - if (walkToMiningSpot()) { - mineVein(); + else { + if (walkToMiningSpot()) { + if (Rs2Player.isMoving()) return; + mineVein(); + Rs2Antiban.actionCooldown(); + Rs2Antiban.takeMicroBreakByChance(); + } } + } private boolean mineVein() { @@ -144,16 +192,17 @@ private int distanceToPlayer(WallObject wallObject) { } private void interactWithVein(WallObject vein) { - Rs2GameObject.interact(vein); - oreVein = vein; - sleepUntil(Rs2Player::isAnimating); + if (Rs2GameObject.interact(vein)) + oreVein = vein; + sleepUntil(Rs2Player::isAnimating, 10000); + if (!Rs2Player.isAnimating()) { + oreVein = null; + } } private boolean walkToMiningSpot() { WorldPoint miningWorldPoint = miningSpot.getWorldPoint(); - Rs2Walker.walkTo(miningWorldPoint); - moveToMiningSpot(); - return true; + return Rs2Walker.walkTo(miningWorldPoint, 5); } private void moveToMiningSpot() { @@ -161,12 +210,18 @@ private void moveToMiningSpot() { } private void initialize() { + Rs2Antiban.antibanSetupTemplates.applyMiningSetup(); + Rs2Antiban.setActivityIntensity(ActivityIntensity.EXTREME); status = Status.IDLE; miningSpot = MiningSpot.NULL; oreVein = null; - if (config.pickAxeInInventory()) { - pickAxeInInventory = Rs2Inventory.get("pickaxe").name; - } } + @Override + public void shutdown() { + super.shutdown(); + status = Status.IDLE; + miningSpot = MiningSpot.NULL; + oreVein = null; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/enums/AmethystCraftingOption.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/enums/AmethystCraftingOption.java new file mode 100644 index 0000000000..4cd3ede525 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/enums/AmethystCraftingOption.java @@ -0,0 +1,31 @@ +package net.runelite.client.plugins.microbot.mining.amethyst.enums; + +import lombok.Getter; + +public enum AmethystCraftingOption { + BOLT_TIPS(83, "Bolt tips", '1'), + ARROW_TIPS(85, "Arrow tips", '2'), + JAVELIN_HEADS(87, "Javelin heads", '3'), + DART_TIPS(89, "Dart tips", '4'); + + @Getter + private final int requiredLevel; + @Getter + private final String displayName; + private final char dialogOption; + + AmethystCraftingOption(int requiredLevel, String displayName, char dialogOption) { + this.requiredLevel = requiredLevel; + this.displayName = displayName; + this.dialogOption = dialogOption; + } + + public char getDialogOption() { + return dialogOption; + } + + @Override + public String toString() { + return displayName + " (Level " + requiredLevel + ")"; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/enums/MiningSpot.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/enums/MiningSpot.java index 42f131abcf..9f4671bc90 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/enums/MiningSpot.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/enums/MiningSpot.java @@ -14,7 +14,7 @@ public enum MiningSpot { RANDOM_POINT_3(new WorldPoint(3018, 9703, 0)), RANDOM_POINT_4(new WorldPoint(3019, 9703, 0)), RANDOM_POINT_5(new WorldPoint(3020, 9700, 0)), - NULL(new WorldPoint(0, 0, 0)); + NULL(null); private static final Random RANDOM = new Random(); private final WorldPoint worldPoint; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/enums/Status.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/enums/Status.java index e1960d7c6e..2e25b35386 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/enums/Status.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/enums/Status.java @@ -3,5 +3,6 @@ public enum Status { IDLE, MINING, - BANKING + BANKING, + CHISELING } From 505725b3ca18b2ade9a55254b29fd5332fd57898 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sat, 10 Aug 2024 16:49:29 +0200 Subject: [PATCH 08/52] Fletcher Antiban update 1 --- .../client/plugins/microbot/fletching/FletchingScript.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 12ff755508..b5b4f8576e 100644 --- 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 @@ -148,7 +148,7 @@ private void bankItems(FletchingConfig config) { } private void fletch(FletchingConfig config) { - Rs2Inventory.combine(primaryItemToFletch, secondaryItemToFletch); + Rs2Inventory.combineClosest(primaryItemToFletch, secondaryItemToFletch); sleepUntil(() -> Rs2Widget.getWidget(17694736) != null); if (fletchingMode == FletchingMode.PROGRESSIVE) { keyPress(model.getFletchingItem().getOption(model.getFletchingMaterial(), fletchingMode)); From 9d0e889aa096682feba637ac9d2c0d4d100a8027 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sat, 10 Aug 2024 16:51:21 +0200 Subject: [PATCH 09/52] Fletcher update 2 --- .../microbot/fletching/FletchingScript.java | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) 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 index b5b4f8576e..0649910b06 100644 --- 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 @@ -9,9 +9,11 @@ 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.antiban.enums.Activity; 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.math.Random; import net.runelite.client.plugins.microbot.util.widget.Rs2Widget; import java.util.concurrent.TimeUnit; @@ -36,6 +38,8 @@ public class FletchingScript extends Script { public void run(FletchingConfig config) { fletchingMode = config.fletchingMode(); + Rs2Antiban.resetAntiban(); + Rs2Antiban.advancedPlayStyleSetup(Activity.GENERAL_FLETCHING); mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { try { if (!Microbot.isLoggedIn()) @@ -48,8 +52,11 @@ public void run(FletchingConfig config) { if (!configChecks(config)) return; - if (config.Afk() && Random.random(1, 100) == 2) - sleep(1000, 60000); + if (Rs2AntibanSettings.actionCooldownActive) + return; + +// if (config.Afk() && Random.random(1, 100) == 2) +// sleep(1000, 60000); boolean hasRequirementsToFletch; boolean hasRequirementsToBank; @@ -152,15 +159,18 @@ private void fletch(FletchingConfig config) { sleepUntil(() -> Rs2Widget.getWidget(17694736) != null); if (fletchingMode == FletchingMode.PROGRESSIVE) { keyPress(model.getFletchingItem().getOption(model.getFletchingMaterial(), fletchingMode)); + } else { keyPress(config.fletchingItem().getOption(config.fletchingMaterial(), fletchingMode)); } sleepUntil(() -> Rs2Widget.getWidget(17694736) == null); - if (fletchingMode == FletchingMode.PROGRESSIVE) { - sleepUntil(() -> !Rs2Inventory.hasItemAmount(secondaryItemToFletch, model.getFletchingItem().getAmountRequired()) || hasLeveledUp, 60000); - } else { - sleepUntil(() -> !Rs2Inventory.hasItemAmount(secondaryItemToFletch, config.fletchingItem().getAmountRequired()) || hasLeveledUp, 60000); - } + Rs2Antiban.actionCooldown(); + Rs2Antiban.takeMicroBreakByChance(); +// if (fletchingMode == FletchingMode.PROGRESSIVE) { +// sleepUntil(() -> !Rs2Inventory.hasItemAmount(secondaryItemToFletch, model.getFletchingItem().getAmountRequired()) || hasLeveledUp, 60000); +// } else { +// sleepUntil(() -> !Rs2Inventory.hasItemAmount(secondaryItemToFletch, config.fletchingItem().getAmountRequired()) || hasLeveledUp, 60000); +// } } private boolean configChecks(FletchingConfig config) { From 0eb2115f4f98fc90d95d00c2220f67d86b601d5a Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sat, 10 Aug 2024 16:52:33 +0200 Subject: [PATCH 10/52] Eel fisher Antiban update 1 --- .../fishing/eel/EelFishingConfig.java | 12 +++++++ .../fishing/eel/EelFishingScript.java | 36 ++++++++++++------- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/eel/EelFishingConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/eel/EelFishingConfig.java index c5ca436ec8..731da1094e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/eel/EelFishingConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/eel/EelFishingConfig.java @@ -27,4 +27,16 @@ public interface EelFishingConfig extends Config { default EelFishingSpot fishingSpot() { return EelFishingSpot.INFERNAL_EEL; } + + // check box if the user wants to use fast combination + @ConfigItem( + keyName = "useFastCombination", + name = "Use fast combination", + description = "Use fast combination", + position = 1, + section = generalSection + ) + default boolean useFastCombination() { + return false; + } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/eel/EelFishingScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/eel/EelFishingScript.java index aa1aceada9..337a6b9164 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/eel/EelFishingScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/eel/EelFishingScript.java @@ -6,11 +6,14 @@ import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.plugins.microbot.Script; import net.runelite.client.plugins.microbot.fishing.eel.enums.EelFishingSpot; +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.antiban.enums.Activity; +import net.runelite.client.plugins.microbot.util.antiban.enums.ActivityIntensity; import net.runelite.client.plugins.microbot.util.camera.Rs2Camera; import net.runelite.client.plugins.microbot.util.equipment.Rs2Equipment; import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; import net.runelite.client.plugins.microbot.util.npc.Rs2Npc; -import net.runelite.client.plugins.microbot.util.player.Rs2Player; import java.util.concurrent.TimeUnit; @@ -18,7 +21,7 @@ public class EelFishingScript extends Script { - public static String version = "1.0.0"; + public static String version = "1.1.0"; private EelFishingConfig config; public static boolean hasRequiredGloves() { @@ -27,19 +30,19 @@ public static boolean hasRequiredGloves() { public boolean run(EelFishingConfig config) { this.config = config; - //Rs2Antiban.resetAntiban(); - //Rs2Antiban.advancedPlayStyleSetup(Activity.GENERAL_FISHING); + Rs2Antiban.resetAntiban(); + Rs2Antiban.advancedPlayStyleSetup(Activity.GENERAL_FISHING); mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { if (!super.run() || !Microbot.isLoggedIn() || !Rs2Inventory.hasItem("bait") || !Rs2Inventory.hasItem("rod")) { return; } - //if(Rs2Antiban.isActionCooldownActive) - // return; - - if (Rs2Player.isInteracting()) + if (Rs2AntibanSettings.actionCooldownActive) return; +// if (Rs2Player.isInteracting()) +// return; + if (config.fishingSpot().equals(EelFishingSpot.INFERNAL_EEL) && !hasRequiredGloves()) { Microbot.log("You need ice gloves to fish infernal eels."); return; @@ -60,7 +63,8 @@ public boolean run(EelFishingConfig config) { } if (Rs2Npc.interact(fishingspot)) { - //Rs2Antiban.actionCooldown(); + Rs2Antiban.actionCooldown(); + Rs2Antiban.takeMicroBreakByChance(); } @@ -96,19 +100,27 @@ private int[] getFishingSpotIds(EelFishingSpot spot) { private void processEels(EelFishingConfig config) { if (config.fishingSpot() == EelFishingSpot.INFERNAL_EEL) { if (Rs2Inventory.hasItem(ItemID.HAMMER)) { + if (config.useFastCombination()) { + Rs2Antiban.setActivityIntensity(ActivityIntensity.EXTREME); + while (Rs2Inventory.hasItem("Infernal eel")) { + Rs2Inventory.combineClosest("Infernal eel", "Hammer"); + } + return; + } Rs2Inventory.combineClosest("Infernal eel", "Hammer"); - sleepUntil(() -> !Rs2Inventory.hasItem("Infernal eel"), 40000); // Wait until all eels are processed + sleepUntil(() -> !Rs2Inventory.hasItem("Infernal eel"), 50000); // Wait until all eels are processed } } else if (config.fishingSpot() == EelFishingSpot.SACRED_EEL) { if (Rs2Inventory.hasItem(ItemID.KNIFE)) { Rs2Inventory.combineClosest("Sacred eel", "Knife"); - sleepUntil(() -> !Rs2Inventory.hasItem("Sacred eel"), 40000); // Wait until all eels are processed + sleepUntil(() -> !Rs2Inventory.hasItem("Sacred eel"), 50000); // Wait until all eels are processed } } + Rs2Antiban.takeMicroBreakByChance(); } public void shutdown() { - //Rs2Antiban.resetAntiban(); + Rs2Antiban.resetAntiban(); super.shutdown(); } } From 422d7fb06535d1fd5e6af61a94562787fe71d956 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sat, 10 Aug 2024 16:52:54 +0200 Subject: [PATCH 11/52] Minnows Antiban update 1 --- .../fishing/minnows/MinnowsScript.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/minnows/MinnowsScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/minnows/MinnowsScript.java index 49126bf548..a3bc74088e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/minnows/MinnowsScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/minnows/MinnowsScript.java @@ -8,6 +8,9 @@ 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.Rs2Antiban; +import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings; +import net.runelite.client.plugins.microbot.util.antiban.enums.Activity; import net.runelite.client.plugins.microbot.util.npc.Rs2Npc; import net.runelite.client.plugins.microbot.util.player.Rs2Player; @@ -28,6 +31,8 @@ public class MinnowsScript extends Script { private int timeout; public boolean run() { + Rs2Antiban.resetAntiban(); + Rs2Antiban.advancedPlayStyleSetup(Activity.CATCHING_MINNOWS); mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { try { if (!Microbot.isLoggedIn()) return; @@ -42,7 +47,7 @@ public boolean run() { Microbot.status = "MOVING"; return; } - if (Rs2Player.isInteracting()) { + if (Rs2AntibanSettings.actionCooldownActive) { if (Microbot.getClient().getLocalPlayer().getInteracting().hasSpotAnim(FLYING_FISH_GRAPHIC_ID)) { if (TARGET_SPOT_ID == FISHING_SPOT_1_ID) { TARGET_SPOT_ID = FISHING_SPOT_2_ID; @@ -52,15 +57,19 @@ public boolean run() { Microbot.status = "DODGING FLYING FISH"; fishingspot = Rs2Npc.getNpc(TARGET_SPOT_ID); Rs2Npc.interact(fishingspot, "Small Net"); + Rs2Antiban.actionCooldown(); return; } Microbot.status = "FISHING"; return; } - if (timeout != 0) return; + Microbot.status = "INTERACTING"; fishingspot = Rs2Npc.getNpc(TARGET_SPOT_ID); Rs2Npc.interact(fishingspot, "Small Net"); + Rs2Antiban.actionCooldown(); + Rs2Antiban.takeMicroBreakByChance(); + } catch (Exception ex) { System.out.println(ex.getMessage()); @@ -70,15 +79,6 @@ public boolean run() { } public void onGameTick() { - log.info("Timeout : " + timeout); - if (timeout > 0) { - timeout--; - } - if (Rs2Player.isInteracting()) { - //set timeout to random number between 3 and 4 - timeout = 3 + (int) (Math.random() * 2); - - } } @Override From a9c743a6975d0131bbb20d2d3a02e30ff324426c Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sat, 10 Aug 2024 17:00:03 +0200 Subject: [PATCH 12/52] Wintertodt improvements --- .../wintertodt/MWintertodtScript.java | 111 +++++++++--------- 1 file changed, 56 insertions(+), 55 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtScript.java index 6df6a344c7..bb27314209 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtScript.java @@ -34,19 +34,55 @@ public class MWintertodtScript extends Script { public static State state = State.BANKING; public static boolean resetActions = false; - - final WorldPoint BOSS_ROOM = new WorldPoint(1630, 3982, 0); - static MWintertodtConfig config; - - String axe = ""; + private static boolean lockState = false; + final WorldPoint BOSS_ROOM = new WorldPoint(1630, 3982, 0); final String SUPPLY_CRATE = "supply crate"; + String axe = ""; int wintertodtHp = -1; - private static boolean lockState = false; + private static void changeState(State scriptState) { + changeState(scriptState, false); + } + + private static void changeState(State scriptState, boolean lock) { + if (state == scriptState || lockState) return; + System.out.println("Changing current script state from: " + state + " to " + scriptState); + state = scriptState; + resetActions = true; + setLockState(scriptState, lock); + lockState = lock; + } + + private static void setLockState(State state, boolean lock) { + if (lockState == lock) return; + lockState = lock; + System.out.println("State " + state.toString() + " has set lockState to " + lockState); + } + + private static boolean shouldFletchRoots() { + if (!config.fletchRoots()) return false; + if (!Rs2Inventory.hasItem(ItemID.BRUMA_ROOT)) { + setLockState(State.FLETCH_LOGS, false); + return false; + } + changeState(State.FLETCH_LOGS, true); + return true; + } + + public static void onHitsplatApplied(HitsplatApplied hitsplatApplied) { + Actor actor = hitsplatApplied.getActor(); + + if (actor != Microbot.getClient().getLocalPlayer()) { + return; + } + + resetActions = true; + + } public boolean run(MWintertodtConfig config) { - this.config = config; + MWintertodtScript.config = config; state = State.BANKING; mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { try { @@ -139,19 +175,21 @@ public boolean run(MWintertodtConfig config) { break; case CHOP_ROOTS: Rs2Combat.setSpecState(true, 1000); - if (!Rs2Player.isAnimating() || resetActions) { + if (!Rs2Player.isAnimating()) { Rs2GameObject.interact(ObjectID.BRUMA_ROOTS, "Chop"); sleepUntil(Rs2Player::isAnimating, 2000); resetActions = false; } break; case FLETCH_LOGS: - if (!Microbot.isGainingExp || resetActions) + if (Rs2Player.getAnimation() != AnimationID.FLETCHING_BOW_CUTTING || resetActions) { walkToBrazier(); Rs2Inventory.combine(ItemID.KNIFE, ItemID.BRUMA_ROOT); - Rs2Player.waitForAnimation(); resetActions = false; + sleep(Constants.GAME_TICK_LENGTH); + sleepUntil(() -> Rs2Player.getAnimation() != AnimationID.FLETCHING_BOW_CUTTING, 2000); + } break; case BURN_LOGS: @@ -159,10 +197,11 @@ public boolean run(MWintertodtConfig config) { TileObject burningBrazier = Rs2GameObject.findObjectById(BURNING_BRAZIER_29314); if (burningBrazier.getWorldLocation().distanceTo(Rs2Player.getWorldLocation()) < 10) { Rs2GameObject.interact(BURNING_BRAZIER_29314, "feed"); - Rs2Player.waitForAnimation(); - sleep(2000); + resetActions = false; + sleep(Constants.GAME_TICK_LENGTH * 3); + } - resetActions = false; + } break; // case FIX_BRAZIER: // TODO: fix this state @@ -189,41 +228,14 @@ public boolean run(MWintertodtConfig config) { private void handleMainLoop() { if (isWintertodtAlmostDead()) { setLockState(State.BURN_LOGS, false); - if (shouldBurnLogs()) return; + if (shouldBurnLogs()) { + } } else { if (shouldChopRoots()) return; if (shouldFletchRoots()) return; - if (shouldBurnLogs()) return; - } - } - - private static void changeState(State scriptState) { - changeState(scriptState, false); - } - - private static void changeState(State scriptState, boolean lock) { - if (state == scriptState || lockState) return; - System.out.println("Changing current script state from: " + state + " to " + scriptState); - state = scriptState; - resetActions = true; - setLockState(scriptState, lock); - lockState = lock; - } - - private static void setLockState(State state, boolean lock) { - if (lockState == lock) return; - lockState = lock; - System.out.println("State " + state.toString() + " has set lockState to " + lockState); - } - - private static boolean shouldFletchRoots() { - if (!config.fletchRoots()) return false; - if (!Rs2Inventory.hasItem(ItemID.BRUMA_ROOT)) { - setLockState(State.FLETCH_LOGS, false); - return false; + if (shouldBurnLogs()) { + } } - changeState(State.FLETCH_LOGS, true); - return true; } private boolean shouldBurnLogs() { @@ -302,17 +314,6 @@ public void shutdown() { super.shutdown(); } - public static void onHitsplatApplied(HitsplatApplied hitsplatApplied) { - Actor actor = hitsplatApplied.getActor(); - - if (actor != Microbot.getClient().getLocalPlayer()) { - return; - } - - resetActions = true; - - } - private void walkToBrazier() { if (Rs2Player.getWorldLocation().distanceTo(config.brazierLocation().getBRAZIER_LOCATION()) > 6) { Rs2Walker.walkTo(config.brazierLocation().getBRAZIER_LOCATION(), 2); From 771512796dc091972a0d819e6fa03119246d0bb1 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sat, 10 Aug 2024 17:39:27 +0200 Subject: [PATCH 13/52] icons --- .../plugins/microbot/util/antiban/antiban.png | Bin 0 -> 679 bytes .../plugins/microbot/util/antiban/cooldown.png | Bin 0 -> 2941 bytes .../plugins/microbot/util/antiban/general.png | Bin 0 -> 2936 bytes .../microbot/util/antiban/icons/cooldown.png | Bin 0 -> 25533 bytes .../microbot/util/antiban/icons/general.png | Bin 0 -> 27848 bytes .../microbot/util/antiban/icons/microbreak.png | Bin 0 -> 16476 bytes .../microbot/util/antiban/icons/mouse.png | Bin 0 -> 22684 bytes .../microbot/util/antiban/icons/profile.png | Bin 0 -> 24050 bytes .../microbot/util/antiban/microbreak.png | Bin 0 -> 2748 bytes .../plugins/microbot/util/antiban/mouse.png | Bin 0 -> 2899 bytes .../plugins/microbot/util/antiban/profile.png | Bin 0 -> 2835 bytes 11 files changed, 0 insertions(+), 0 deletions(-) diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/antiban.png b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/antiban.png index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f88c7a6d14d6ee45c7b27b13a7a43a3a5ebc3e23 100644 GIT binary patch literal 679 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 E6ZnGNasU7T literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/general.png b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/general.png index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5228bf9dcf2a5926128fb9314a23de01a0311a5c 100644 GIT binary patch literal 2936 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 literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/icons/cooldown.png b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/icons/cooldown.png index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..cda804027361c79c2cfddeeaaac49730bc81dbc6 100644 GIT binary patch literal 25533 zcmb5VbyQqG^e;M4oC3w&b#Uw8v^aw^Sn=ZSQlNNocXuBginnNSmx1E$?hbu?fA_6* z-}~dfwcbv0@=5kSd+(fNot5O|{G0!`4)`D^4VDJL!2tkpZvpUc6K)@`-7svnd{{y}0y`B3{I{+}l_J5H1 z|EfkcHFGg}6F7fMbk1+a-<-vJ!*~||3p4$PjsF)G{tth1b##4`QTY!$t3xE-u<09S zviN^sK+oqI#@$l{JkEhV)8~Z5bo3G?i3NrAGrW>gKN2=Ji9pgC z!AUvlnHL=VA!q=Od-J9OViLU9`vQH;4=K8tC!`}{o*-(cNfUWepYUrMd1 zkK?GAGkIQZZ{QN1fMRgna>;V2UlATMAE(fo1NFXs2`KQlx|4jbnM>Bm47b%zRToz5 zQrPX6S-r+g7maUhGuA-gI!Q-9t{8}by3{e2gafH-mtSdnVVnhL36BrlymJH=Ylam& zsL|6o(e>q?d|ziwZ;+%yWg@90?&^yE_om9d7R_`G;+LDo7-q7+=;#V$+-jAw%xw(! zxTDz@dPg0>Qy(W7WD8*ncF=IJy1;X1R?IJ3u_K!s3R-~l*AuK+l0r0VZYXa;;m8C5 zANNe>5z{LXmxnN{{WGlUqpz=T2|G)bFfP*f$~)$)A;sZg&^+$fJXYT?*ffv%S>QTA z%6?GQ)hW2#s>2?-GrWXY9)uhzzhs70p1&^@1Jcnd`B-H*X1g=m7B-=wZ)cyMj)OD7 zt-@Y`Z!1eW@9u%dEN-V`4BcUY2x2W0_$ZmdVvFlHiPe-~h_XGa` z)MJL99)-vM0bUww)4ycH7kV}^LEfoDPGA}ev8-Zcl_&36fMur&$8j*W4ou2h7VBFX6# zgV-9L@LSFgqC>Tw38P44`40x+967T{4t&Kk-7T@Uv}++b4C`l z?-U(6|m>CXm`viV@7Z7bE+nV>MGro++@2>7f_=ELcjA7(IK5VZY}(a_j=?)=0%X9NMp&obwKgw_6GYKJwFVrFyB@Vjqp zhUR%%HH2hlr*G#G&vNIOxlmL!KQdAJ#UPE3uY8PFsnojaogZ?Yc`*8{7TzLXuDGo7 z@-;Btk3NfhNSXWFyPfdh{9k!aQKhZVA>_YmdaBNHR0^bCHl0En>BW>g4poAdq4FC3 z1bx_jqE%Y15Dd&lW&iY~P<=I=8b3D+R~+A|0C~ZS#B82E0v{SuuJzuOgvAl?-E<<% za3fG)qaCtrPfE8g>6&wF}6| z9pg&cmSZ&qa+Bz^;_L%k+aDiZ(h}x=iMyCqN!bS{a*vXXn#p6+f~wtwfO_4c`xK@p z%>!^IKQhbbpdK!nKwDk^nCtS_7XQP!!0i>=k`hsi;-;&UqT-AnewjW2j}@d#lPA&D zj$^Sqw%J;hlBGgg$!IJIB>qG7YY;JNTG)p1p$q{@Vuf-!=?$ONd5)@B@ie5QyR{kk zIDC&A&7Q}nec@!5brd!G4f1=Ya8aUIC-B*mIQw`CjhT!&wDz+T@J^)^4v_iaw-O=! zD|068@PJ}?1(bne$F+%$Q1Vp1f>sroD&L|ows6bnqho2ds53JfPfzHnk0RW>R2svO z{wsh-Zsc-z`U;o9)*Np?=5AU)G!7v$^4`;grM$@?KT(P@{;a-+otT$JCTn<-2c#D= zXPyDc@obb>YZ=L*jXMq$CF~ZY0|Ehn_-#QWz5|9<^D4ZtPs#Pqc_fervzcsq1@LE~ zVsNM790AoDh6LEnIkr*&6Vsxw1v2v2UeW>&ZL=f(tf9gqrf~Fww<8rSSBOGZslXPW zc#9-z?(4(q^fV+Jz8?o8cUKJuBYi+7#a@Z21yts%^DCu==DM7}?BV|P~Fr<8JC>g|34SaM# zVfb^R$r(G)QJKGLVNfI-C>kCp{SQF@wM52vCY<51)$irUKfu!#?R-Fhz<<~qeeM;m$e>hHKXdcfcvGqU-zqX z;q-qeSgiQv-O)6e^7=t<)YKLGxkuJP6 zo2yuNeEkfy5R&{zKO4-D{n-xif@S8q>{oQZg}X=zjShHa8HuEZI=I9K4CI34iiGa;~~`>t66!HN4cIHNE0JjaF1ixsWx-}4Ee{%$3oB>O}`5cdA} zU-vx$+g>;Dm!=`UZ^d_u^CZQ?JZ2*Fz4VyEo2xo1SWL{Sbm$3o9e??o?@Kq@9Nv@U zK4WF-ns+qLEas%!a_a!t?h*mYW4P?Yvv9(JAtsTy}HMefE+itZ9XbDD*MlWkRmM&(3$wR+e=r#KX<`}he(Q0 zm7^0-xkAaHlYLy=0%>qCMGz_JqmhI9z1FC}_m4^f&W-Y;*B&+;O~*2=Pq1RG9ToWA z6BD~Sq_MWf+?xa=^L(w`FC3N5YFlh#@h-I}NUNC-#-oE?;o1E5g};=m&HAI{B1QgS zfeqV+E5QW&(pxn#?6;oZl#Bws#kL)PE;tv4YZY3BSmqHGWUHqn|0Lp!(ZxNj_-pi? zqNVXk&^44@taxHV%}l!7JYlnNAbJ$9{JUnIHU+xD(>+#rA79Ef+OJAaqqkSF&HEv} zeNPI$t<|sbvkbzquvMry4B{#RDs+6kQwZc<@l^O=xI=cnj?}BBZ z?QOZjXunyUI7dnJY!fqz=~EV|@eU)0=NY?;a{;1IAzcSnLbf)7NhdUP+sI(LWitDP zKPs{e5ej@nFcTd&t8AK?wiF{BmE#hBYdSaFhrda-@#o~qdHjgHjH*A~EjK{{YVnXn zNEvLGU^}l$ihOZeyfP;MsCX4=ZIb;_kWx|dL(VkhZ zNU=6pk{xl)(CdKwpkd{u9#?}Q!Z~vCkjJSS4TlhAO)oiVoK`w&r?^qr%v$U1agW*U zxU`jI>>2HlE<*_kR4-_2!Wg}EyrfF-s&EG{oOjr4z9;Knx5_bQ$RDDjIYypL0)zn+ zr8sljbae2(9whe(o5R`h1)ZOVFGB7tQF2o+<#5?wca*@}Y8b<)RK!vRI{ zFU)H(V=DH|`X#3Ou`UG}3a#Cgl7ji>(>~(hPvb;*D0E~NG`100duH;UL)4coh6=ff!O{Q_HJ4~(`9`Ufa% zuV>V+lwx;X(wu$t7wPqCTY7YX;4k7Qqi|G=@rB1PxS1|d-L<5z#Nu$s!kv2PL z`eS(UH+U}&aV_N-PW2F7#`IpAwW2((J)?1^8w2U_Lqs#bVGqjvt^LhU>TtW7-zt{x zQ`KJd-Wy2^{yHv9rM@)?{i`0hK&?hw6w~ZRd%K0LTZ;2J8*iy*FR^(oRs?0!ME%1L z+bazrSDi_}!YJ}hc{NikKLJ>>!(Z8F+?aFh=RU1ejwvtF=NiCuCg}^apRZ^{FZ$(^ ztIfkV=*#_8i$@47(BYXAs&~^i?N=E4$M6Q*WOeL@BO(4pPx(jeE=#M64`G4f@VU(M z<)=sW*JtmKY42Wr{zsMYN%VIZPR;Wf6#KB~A3%$UL%$KDe^?{)@VhFqZ{!qzMVEGMHd!v!p`Wh+!v63hx6UWTD9>r7W{cY%*>QY{e*itK<9^G(KH_rK zJi%HHSAXtIe&dS#buND7kQ$1cJ{jKB@T2uM8%^9q9mp>62#2M_n`9oMUG6e{ni}Z4 zV1z1`0g2ljX&)$O2RuODBdyMN#rsKR?~MykK&$gtUHp}^pcMRPt}>>WtmTk3tZ$@c z!q!I`BFTKDD$Jvx0<<4Wm)G~MT}rs$1(ezT0i@h+Yum^++hunsGe^#wFIv?~Z{H(H z@h2RD%FV03j+Sy-ko;;a@BG<}C+7x4JB>q;Q)VJO{B{(!()5QZGe4k076D`SqC2FZ zIl)bJSa0Bm3eP*M2(&O%99-wwp{urOz@a9m?% z;YLmPU|Xa@bSe*TgY;P4B&ppVYtqDLQYMP*bpk|D5t%RyD&Ix4%VjOpzUBLY!>UK5 zbThpQkmRr;4xIouf#z0p13Q>+X{08Exr4h5=g7;De7|ljtxOH)aKVUP4yf7tw!*aQ zyd`YaPi#4e!Ms~-KZy>abCTQN#vvcnkU_adOEH+bmC~V&D^f@~4&uB-(h}0a{}tgbn}72D6|A~=}sKk*Smr=bwGR_U3YkV@gq5z!}2?&H4X)F zH-4yBat5rTETf@)=pR71($={;6X~`i#`FHB6{>7h`+z6aNyGzvreMo) zZ`(5=>F2r>aZ4^g6URo0Qqt!_1?b!Rf?NcJeSCwW+cAd3^ ziEOw6R>25V-l#|6Ad|`RXpvfDs1o$unp|iM=EVA_psDFVD1O=05`ny85g<#32g$%n z{Xi)QZg?^L6YGb5;Ma?tQ#_D-wK^fS==L{%Oi)2GmYzeyjFNR*P42$7bVyGwbas~y zzZS6vPV{o(uvmDQafp#*aVEmO3fO0_ei*YxMkx}zs87*Zp8iWn-iV!%L(HD;BOBq4 zFIbB^;GDWCNfgt_^lYa#F7roYl7pG(L_7R2o=4nF$#q*{-DDipP05q}cOI*HK_j;F zw_1S1q!HxeMP>NC6SdyR14#gQ-5vsU+&llr+R>y&*`$-3MgOKX(%xeJHPZNk(G3ck@L zIQ>B3PuO%wfuQ z2M|{X5WmMq=KhHOG_+XN3(_BY*l1oZnEO7s&$Y%1I)g-CooMWsME zb!r;&thbjKhrJ(y>wzsQ>@spToSab`8JaU)lzsTHG9_qTYPK6VnlJl)zIkE z-AF{8$)ZbqH&}5Jna-vuw{ACbef0W)952UFy+ak_|LooiEIAhc-2QkLLfzA^S)y{i z_YqxT!o{o?Nf$pzfMMtn&yn;Wr3RSmHa)+-6qD+ zN)y5%ck--IWQbk-B|+43YAh) zGv(O#qc$)IMXvZlwTY3C$=)Y^JrVVOC1I-o-wb0{$JsYLp*|gzLVx#;@`G|U`=KFm zD?z~k@03b$>t<-$-V+*!e?*0Rsu)y6xlG2HwO^Sfnx9oIhJeUfZkWW%feAfs5iNG* zvhU%{enr)M<9^1oyi?Lfvt?nJ^Fz@L^oAtyk88rBuGmAO)2(qlY#eyyo8Rw+@G+`W z&e6H5EJEaPAR+7MRu^A^tPzs6j(xzhos0LA*m~`_e}-!t?Ny_o2dZI(W5rc0tHv4* z-qsCbOv_Pgp=b#mVhPy{to-sN9$Pne*6l_F1XCE7d2LAYTVg@zM1et*S<_YQ=`2iJ z!hE~`^TFRxDp}J+`=X{#?&$A- zhh8z?iceT3l7b(BcA~lLX=nH2De9AaQ!Q=FIe%E3T8-)B^QKO_P|mO!ugO2*s7qMOB7kXC&$MXmBy* zf6t-lJrhE$<_5>}PSauy23%^pzn7CNeV+Lur>`Wd{>S%}$CMDy`;`cjU#%>H0g4)4 z$JXs5B^tojxe`WEuhRF70v+neiABQ z7W`4cDov|ym#%uP6G!TTPkK}URKHYJj(5}l#SU$mP4bH_nGXI!O&Q5X@q~q`I*kf( z+=4#C`?9J7CLQ|$-KM-;OC zDz!@~DmHYE`S8wa_jDTL7T~cpnMG6 zUmUO!UOK5zQQ^=Gq2PQ>dO77$c&>TB6Wkk>@vyonijioI*>YB$KK*%Zl4N@7{%938 zNR}8N7zu*>HoN)q;~;Wjyf0-;L@%RkKYRG|Zeh&fKfr-Qi0uf}2k3+VY{WE;pZqaH z_M?PgD4FfpN~s=4U~Y9x-*Bod24>uCAuyHEmdYGH+N`Ubu|S*V$Cvh12SV^C!VU&) zES92YCZLK{#>x@-IPdvYck{>3Gv$^rb14WZ+YZ^L7_j2ADbku&sPBBO;bn00p`)dX za*31^)xNm&?(qE9g*bSrw3=Ptdjl8J;#yWkoDsGKE9j7B;XN-c5Ed3W7x}aQi%d=E zhXd&nh8QNn$`tS7Geli@AVstoq}XP1t|I3!LqL#m{sh(OJ!4lM`j{~zaUgHc98WHS z5m{c((V0Wn!mFE#U0?n1ZFOz{p=wel6!^?(4trwz=p34GD3H*|!#+*h6|%kXUN%Q= zQ5<``0ty5o0KDyvw)|V=*!g^pWwv5WqY|ezP}>8qm3yLgU@hj;3+yRTuc_b@wE#<{{3w^aa}}NLDUJbpc%*Y zXJSgXL5bHz2p`8fJ}n*FaC_-ao$sm}G4UfLGiM|GL$0iT_13o)h7~SEn9?ZgY!dHm z#tZZB*>9Y+6W>(|K~u5>hs1_|X<_K=W<#?X{c`2SV%)SqmL0cdI0CAL&#}#93nXV? zIkSOK>e3X;;Gdh>{YBO%Qgdi>1VKJnqI$9|{5H9w8qom|U<`-TcK*zhhjG)aSA*B7 zg?fKDy%nMv-F)U(|Kka^U%ikFPmGpOpz~+(L|=ao%G9Mm7j%?3%t*=jRYul~1%pp1 zs|Um4V(nG6eZ>9m4wDo1F+X6@-2h~Qmp=V^|JqorZNkIbX7u(#u^P~68>dl0Vhr?axis#_tsYhm$@^Q={#~m=`H>A{GVl47@}Bo@fNweh>AD)H z5k(DgJ`@I6n4^Pa>B>CgWO?k)+61IeRdkszE$BCwfo+&A7W*t>wp@DYgNd-B-vx=c zADk8j+qN?6Db&AlOj?f=b$fx6E}{hVoTR8h%ttm@?>Xw~3<_xoO~r{;ud~&ytm5kl zl4DYl(X?V)I!1bTjqBmxMaj4r!BYCE8`|y0lMaB%q&(`GUOh**!7LdA{d?HiauXk; zWk>x|Z0g&a3bZBIMWrHo+`*zPP?|6lqBN}jRBaGY`Ch6RZMRPC;FQH;t7Ad6V|gs; zby;iSG?|fcPp6o{#SF*la&B?5?rXJ%-6hSMJzbXY;SUlpH0kiUgph%TeWt4L0HMM- z|B%c5fnLlyHdnX@H zZ;Fs}x-6-TbIIC;haNQKe~1GgS1Y@-mVbeO{TJ=5fl`-S>x0)6MSeUj^*-->30=mt z?)G!R$(ieYQV}}5*YdiLgPd6(B+8#%w5kd8?6hDXVnkcodEUhHlf3R1goO$JeL7LE zYthXW1wZH)Z6lqT0gbh%{@sX^iwK*gRe`%+3YJIgteqH$XhH1BUT z9g86&c2+=@hd`&@ziQ5OE1WRlp(JZmV7gS92x<=;;F8t@B0p>(@TXGwD*9*C z&I4M%;;6K03_)dAg5zj*z|B;&)5b40IcHh-UlL}#)`e!0HSK29Opa?V<7+f$yUOw1 z6mU%+1`x=UMS5Oq8bN9?ER1tKG@8VUvx9JV6z|3ma^pqQJOv#1r>~?ytc%|LMg2*O z`et(}c&Q)q9gA{5NE7ae@2{BTt2Tz$#2WGB4nHl2gv)mVJR@0<4M2 zwy!}L=Rq|&21=ltYS>I@HA$I(_3T0ET!Ck>4z)wJ$R1($g4z^qI}F}<)BJ{@A}lH- zo{sxa>zE3&7y98V8@C}#vz8O6)z?Zy{?F~9aX0?}JAn;DEM=L@)!&?hPiHaQB1@h{ z!9#VXi=A2OX;yY<^%-Hwh{8Yj^WxTO1TK4Grm}f5E4^xxx%KcWI;xbdX_AtboBn=W zaam94nLStH6PB{cb_vsmlrDGtz&FfV;)BD&yRRT^1lL4l#|8?DPDqLbhysj2 zw-pxq+P}i%!4W}MJHu+61|CdI{F_4_v2l**ooKoKFtpyo#zwQb;P_t2-N=O>En@Qr+P9BSPM?6L&w5`|6wR_ifILB;I+ZA15 zy$3nMn4pd}!2%!2Hu`;~^DOJe-)oShGuY5`Li1jI9I)-hbJ1CheK;G9{+jPCCJpRd zR-5i6U1b#vi6<2(b}ShSNe(7h|C}ynO#Qz0cjC0~Z%UbZR=SdgoORrYZH!xGq+aw% zL|ydPyi^c_$pX@l#A%6{%w?5XN{*brXYHn3sNRR{v}5G^G}(PMa6+9QWdWDO;ci|l zR3KaJsTL-U>_S}2>WLhF!yh$i)2R28ccc(jJY5V#Y!tz`;jaHN%cmF}9m0CDB=2eD zt)mQShYwwp4;Fcn@+%-CeBubsUrby42MEy?_}08@xcaJ>hKXra)pqAqzN6OOK9aMz zp(^@4%f1my^GdU{tMJe0&nZ>wgkV1Go=N$9hQGTikA{-Qqh+VRify}71k!<20khIB z#3n=Bh)~`6*(W7wCt=GDkY68KHsHcwVO=esL>5Xd zmEd!K$YG37_o^N!fmG zX6p&Ngt3<-!$5oSm!hBCfv#%5y_>e1CHfCl6~~gY&d`6`)sYyhoz>=X{4KlcFMjS| z5G=}vlgu@Xa+)|O0|XNibdmN^BuUOx zUYA;x!(&LM#*N+C8Gg|nei8L31BFJWFbJZokdP-Xt2dPBPel!JPtdh;u>U@oFM67g zYJXfkrSoI;pfifJI1ZJ79cO9|x5m74W2V;#mPsVb8f^1yFK_L0%4j=+%Di@efRH)rvRUZc+eJv}s75gh*+8ay$QFq~be(WCqg7CGGxmECD$Ucpp=6>+#-peRtsD;Vp; zx4vfC8nvVNKefet)FQITP8SqJSA|7h581BFjSn(Rb7WylLBt;@p0~AWul4M2Kaq#6 z_8P^U&knF_%y~LlX&}%t?cqlfmFpMEX|&bXd%Z%}hHpEVp80dUf8k|Vm~Oxy&aJhX zlFeN<3A!=ENaWGn)l1UEr6#yL&6!dw5-ADYy^EK9C@oc=Y65V>?FF6ZQbZWSQzoN^ z(x;WVFt4Pe>YDfPMB`snciQWu&)50lmslMyWOm3or6XTVHtnq4qxr&n5WA< zOKM-l#!Xga+S9)JMqp1GKdoo_XASg`Yv4zeHkz%7MAe<(8?R)gh!L7|YW6O!*X)&!c6tt;(WLdT+-TgUtJ)9X&Lw=@v3vpOIX4!yqd|jjqU6jJ1_TS>#);+USeAI~jh7rjc8+KD#iV^~V7N<)ZvYy~1)OLOOMP)-QUi za}@l?FbmlVrFeFeQ~6ZtS>fx@6!m5@)wZMpuLN%;c3(W#zNm|wsD-gm;B_qp1P`Pe z^i;U>_xKOf7Iw3&q)U(jJBwdjgP_OAiDFt$y5~9pDX>nqIc&gLTGE*`i0zlRH2Ti8 z4zO0-&IW+2tf1u;ARDe6bv+o$wv2n|=*DhuE(J3Uz z#->U*PL;6wgnrZ2T(0NRSE{K0iSDy~;h=4K(fHS)Pvrq`>jT-Yw!C`2QLDfy=jO>_ zysbzSJ0YkX=-sB1#9<9_D-RVR)wKRAu0|JQYheCz-UHiejt{$gmwuL8qTr!xF`stj zXx7p|GT!}P337XY-6#1n(&dxLcMANXd6aI&N%WT6k+EFTammtn*RV; zco?&BXA4$5ex#3?+D>xD`A8vS$0^3#col{=P|4+I(NQ;J>(%DN6EqDB0k`ENxs1Q= zN9?LYOJxv!S+a5jJ?1CuADs#<>NARbQGcfPoenj4A?oX$-j?G{|iuLvLhRPPe7KF-=*@4(bp-h>?D9 zqMiA@q&6rJ!)B0h#u_q~vhc{BcO%!1b=u9dh+7(8ID4?r#tqMadz>f}F*Dmp8M3t( zz!0?o@h462^(b5_HQWE1+^T{cKTBM5VI&qSC2`X0sW$R8MID)0!|v{WU2c~300JAG zpn-n;n*3DNydD$_7e&|SU6J@pwd1)-#OY^&DM_|Q?;`i~f`~dN6`l;9is!7pAo>&v zJ4jsPTEi}Og6u(ySL0I2BtSo3-4tSdu8$>?j&Ftus@p_)7M4cM&`buM00;49PlluR zmk}21a(Q##MR2I`*8AotN<^y0EBZ+C(9H3@;A6duY|RKxk~XcEu|$PBx-IIax*3qY zuYzI?jHLGhrkRyKEHelGM%9we5|vqD%qM(@v|VPmV!OK~wMZ%O4JoZW-6 zOV(UVhi!LS|3$)kbU#8{H~7y0mKlsJJJlv5vZ%T$!cL@=QN6x082`?WaaCutiBd!0~=nxSwCsKr6z9`W`(d{L*bRg>NC=gk` zC#PMJ5}_FLA%tQ%<$1gC0Vhv3kyPIK6{5%dzAUIWznO=+PE^`LM*UDMByMDzEoVDO z(0yNTK9;H`f?A!@`MWILFQQ9 zN=3fQTy!CowI(uAWJH3Q3Rgh}!z3aUA{?M2Y(?XO1{!$*?E*=2R6qc|#5X))6_GVj zyMCe@?o`Z3cwr{&F9AqAMolI%c5Ds9fLb}o+d*Z)_wJujL4-@Tz>XPTwx2gb+AKP{ zsYe_;$E^yHWOAIEx?a|+R_rXI zg<4l_2_@NOAc;C)Xnt#ZmZNW23vx|ZPgIRLbB=ALe8rrFY`I1Wzd<~=TJQk5HXk!H z|MXq4JY9lZ^blh;AIw^-F?x`h{PWS}AC!3#CYrkZ%xZqT(&82Yh=2^l$;lE_T?0Ox zpiyvqVOJ$Ta~HXe-~8lGtF90#S_WCx%+hY5Vz}R~Hf_r>ROQ)d8TLj?tm}_h&{V|1 zz+~FZvp!WDb;M;L*UrSdB3Ra9p1DAVzrz(gT(ypS)hQ4dQxp$vl#`JOdxTz7`ZAkd zNfU}O(Yb+}Xp?#G#syk}?@~G(T)jd3 zMhkzq#0MAbH6SI2CfyM^#v9k?pOyv)!oC4N;SbYqk$1MmbFVudS3QTXQ8KG-P32cB z3`n|T&)OXLapna-zKt$U%de5&m7W)FyV$9IHwm__ zwps1|wifiYa?Vv26rTD1&K9fDb|&2o^NZa;4s=i_l!fW8`yAlY((F6{zcBN$D^}Dd zzRb$eur=PfR`ea7O=L6!W+Wwf8-s|-(}N$+p`BIOrkNTqY*{1IB;9_V@&o3dtw35u z4;JX`qP>!Rd4`%bSw8{|!9Kx-Y8%8Cu%0@i&B3w_V^U#q1R5FlIccLZlYbMDUEZ=% z8B01O(-0E;jxF$w{3zg43^7WEU~z&IDjkVafAXJFFM$Nt*@jo?{Mzu(4j|>dT#Zg` zeNoMWs78k{FMxCB(m~3; zoX}*-hIP06oywca#>Q6p0B$J#O|X!_(Ixr~v-UjZulMQ)gEP>`#PCOYO z$y+fr_CoUEzn8^h(O`Eh1q#GM`+VZFVWHLv!^8goDf*tw=n~;9Y->YWcPRJkzozZc znbOTr%FO#1+BFD7y8i(Z8+_m%Wni1nifH5@B70s<;{jVgf<<@;;NCb$U`-18aNf15 z&RUx)UDlZUC1QDWOXU1|8vnRll}W>&@5nATRKe=HB;nR{+;v}8nSXE3(cxi4|=7T|M4>tXb~U*~|RshJ9Wp zv4SJ~hUBf9t5r5o`P=Gnj%()Pl8Hwjaf+HLcM@E)f&;ugS5S=fqRw1VJ}Mz3$rffCl=POE)mVA`c zP{B9o9xdx#5;gpwxoQ7C8EgNQAIg*zz7SZ0maG_dBVNq4T}MJ@n%s63K}8RgJp}U? zZs%VsUT`(|nzU6nd>J41!Qft8ak54g|Bz73l;aNvTaK<>`Oe4BT4gyBYc1_&Idg?5 z4HtEvYS^r*k zuGV4i;i`Oqb0%ORN0GA={t(JNYocmdOL}Iin6@rqNr?-~@y8gV!Z=US9u3Kya`^s) zxH#&@-=Np7UZ&)Ooi7z}boP6rURPX2y%={LfJ|W)m~gr5s`o8Yr|(6>n>5S*Hd^E3 zO=~L;Eh?U(C}V=eDJED$1?%tHvgHlRp;yHqLNu1wEvoDj(nphl5yXMEln8VVjElzM zkCLi9`Zz_qgTVsf%!^Ody=AXN$$wkE!+tlHUhwp((azuE90fNn zx^;t0Cj^pD^ajtDD}R?&{nRO{&9UgR5{AZ2OCP~e(Su1N;W^sN?cWJW(p&ZznUYKT z96XR!@*x$8IUOp#LdiA9CdAr=*+rK5B0d@;`ljMQ<I5L*IIeh{93Ph0;At-$DN)N5 zTQnt<7SHI&+KU)M8UC1dc;ipW9N0SqV}P=mV@1`8>sCTREahhR6M6alDT!wEV4*-1 zn)x-ns>A~RQUp84{M<6_YEoEw{Je4X!0%utxK+Bx`z(WnQhEpvKD&Z-kS0NJQH&b^ z-p8?)Ng&V-DX+H-M=z4T(S>?0*%*_ZuyKHYm)W6yv`V-HFD@8qf`hO3e#@XLA-y*1 z;3H=h$R&>^Je?WuHdiu4+ck)Pv6^k2%Wy7Tjh&S=SMLknu*oct8L&dHCH#lvL~^8} zRNCtGD4trcD8S~~$pGr_Xvdq}xuK?|H*Kd;GVEl-yBIiwsrwy(Q~0$ep06*rpAdUM zr98LmxalyrgnJ(cUFO?zd#g+uVOREOk+!TBP5*()IloEPN7EIYu|*q7UbV8iLUW?% zLw{^Ol+9eHmHEEWj0cT;j}P}8n6!}2!>p)K zUgWQvDH6Pb9p3`M$)B~zzr4Su{JDnf`J`G0m@VREOtfwtzj-L~sF%C0Z99TuS}=dooz<7+`}M&=jrrKALh8T|L3E6%Mz)Z* zZ?7lmH$DSJGwEDbQ4Fighq7|X>$vvqT=bCy`K31xc;yN5kaRwxs}8M zwURrudqpV(J=U*Yo66dFAz8z&p;fDN-(5q-Z$(Iv{|+4YmU3B0sqQD3hvEp3&NH1V z2M2WJYGvJ+mp?b7vWsp&&R|*cV+gEzJ~yqd1=8_?YOFFw4nuCe4I>zg>GoBU2t{zP zgwI4SS@7o{-+ktOw%y4z{>TGiet)I!dtTI#_lbKgRDe>^WR}+E3Tq|#nYl5|TFHjO z?8j=i|3P%BiEyslK*+dUa~Swe^|4!~%P9LVi|ccpWbT&=d3y}dJM=_mwc&aaI{;LU zaS-4bO9R;|tv9`!>$Y^W(5Rma-xkcVA4^782Lb8YiGgV~GlCZ&q=oO&Fc*`k^L91UJ(NE`l7pR<)p=&$?Bufa9$^+$@|lrVMd zMKF`UIDh*2DSe}UJuTmckW@^I)*kzN89W@HEVwc74zF2HEm9}T$rP%@rUeOKPVfc` zZN_spEQp&ZyCvY)UR`xtb@P!YuM!_AOjcAR>dmApz4`*Qcxv$qe@&hGT04BL8XxdR z9V=M3CqLc2vqW9o$mdMOMMAO3&}=wzpJk3mF?BULPgDD>R)nJH*iAbJ0tKcWhR zBYOWN?tvLwnFRF;j$3c06zQ?7wq;NoWjpJdEY|`X8L}vQk0iA9c7z-^{GgQCm4^*wFQ4pa?l}0!P4{xce3_iIi zI*jUvH{*AG^kdd*6P7U=U0p0-vr0*pofNZpUs3`_7;RS+n0s_x?-bneWQZ(dZHTI9 zdRRNVT!G34v33YKs{109(3|W-48ROGl=5wi)iClJNN5=kB^53QcM!`^#W%$$MxxYC zMTMPsUr|?IAp3sh*oyqYcm6zXH9SKFglk@w*@dyy|Hc|1Sw%ledhSaohE||nWMi~N zy0&!AE1t{Xp}P{^o0yuI?KtmwyS#==nS1|K9l9BE&8R>#HZ@Cn>wiib5lm1ky6EWO zOs*QU(>(VQt3ntaBsG(!*W1U@{etn&HMv%C`s5!c9x`=`gN<6xPX(16%r7*)8Wlq& z>V?(3^G8g&HUv};v0O(nj?-$(o?D|au?8D@4~D@DBx%!o+j7T*R&pwP<~E^=#)&t0 z$c7SIk-v7~DZvh|87<*Ib$!o}i=`C(X)L`I*)e z62_$L}NM0WhQXg2{*sH*FZAhUynkM!Tm>Jqd#a__q29XWTSc3#Dh=#u)V*U zfRPd{_+2C|S`?H+z4uaiK#34~v#_e3(&COCmBr79rAWgFScU_54n#0!VU6yJcIq%U z89j{RU;fRBcDF~(y6fCCKGx$pg4;Fgjq>xo`49d2pPlMu#Nqa(#_wl#g}2gN#(c3TUOF@!Bt0NA%KR?Iy7;*m`W>xwJkbT@zEEaV6q}fQy zzJq3oWM}T;1o?$Sil+7ZXR~CX z;r?o%x1)S`F4}$EvrR5}gxzZ~BAEF(e(8A${6J4j#ASlzlQYOhx>#wC=dcxPIXOkTjC=r%ulM6Uj`qf-YhMWWcKKfoL$Xha2Xii5s%af+QF3J&vrwZc!*XJk z4)verEkQ%k!ZryQRp+KG&KGq_%^KntBIcyLS&I^;+f!5p?-kSCK0ZC#*Kb>6H0;*G z6&x);eMZPMtx?F8%&A*pGAWgKi-___1f(czp?-=~l@u}O<2N}hduhRP`$d~#n7EgcQR4Y z4pP*o{wfp_KBqPdUzV z9U=qsJrE#>=GrLv@uLSiIe8*T{@{M}oZ9y=>_(@b?iCL(|S3@j4E#lj4 zHsD&U1<5wp4e)!^VpXF!u>3dJvHB}XP6qBR;z^N3puV<>P>BmcS@0!2KW-S$hRCBY z2G^S5{Azs6mcsUF3X0Z866UXGMJCq#u`8!f<5hysvuX7$8M-Q1xKqqHr_a^0Rb-Ez z_z`u5Dq~l3yJ{9GtxN=e*DnTFW{Z)Spyw$syxR#9*-Dn`!ay8{a!wb=)F)Zyu>Syr zb4QMsTU$#&T2EeaaDF&dTz1kwBJmMZXeG3B#%XCoTO}Zkw(!Fue~1k+q}08JUVWgk zC0lYCs5bE2gJJW++vCeQ(X*csH7QgYgj%!~A+XQ2!*wn+rE=WaNhBLy*t?IF&9tY5 zHU1;a-pg0Q10n5fr3;Q=)9NGO+5Px5^ZrSt=UPIk^+>6Gg>+HbXg+B^2LWdW5)hqq zf|ndhIibi2=R04NVDG9uy}qf$}bs~*v>kQmsG#-nY8$m(vm)_D_d&&-%dRKVDfF)h3j4EOlMmry-v~G?~zX~iUuA9VElu{fJGz#y}#F0`YwD_XT5*`!aWM9K8GY)qQFpPUIh+%}t&x1GE& z!#Hy6QjYB!Zwijjw=xEb;aU}`_ z8xTRH z9vID{8R4D``z}+|Q-iI5St)R*XP>Av_Wcqid{nXOYM<_yNQ48Ki~mV`#h22-E~ z6sY(l4qsDqfr++@jo1!@7cULGL|3?a%nrF7ACzblR6Vws;;ac92M~M}ZM=-m|vkSHFXq8fMy(RVYscJX6 zi6;L5ek%hW&~=+jpc3dI{S*s`N|>qVCf)HYi;#E4quIQt&lEJK*k3DZr^{2`Wg#Q( zy|GN|5N&yZwJ_>l9oXhlP4~sU+3laQo|@~Fy4G&0N2Eg(hgfDro}J>Bjfamwe#~h- zXf1gyEyf|D_D#a8m8sc=nDY}OHsP@eUA^S|4_quxII4cY?Jk6sqz2|V>Vij55&-;u z{2y?*veFs6%|_3Zl`E&d=P0@JCkw-mx{PUg_KvtuGb$v8-H_Yto25x5HnIDD+-PQ@ z9D&QrU<=KRxxI>{IEX}a>%M*VM^OA_R;4WtG@w$TTqj~W^~4@EVsto8!(w!Z@m*M3 zjG@AmEP-HD0kOBPIr7F@t!Fy)T166@Cbd?SyEhs;WolOYljH}9I}2}Y9U3#)!3G3& zDtenxT**rJzamo}C}|!Rao(yvHcf}Az=L*Prfi6Dm;{@k45+xJAvbYW-;{G)e7+d` zs%ohF{px6p`%D4#N-2`A@`jb9<lCEdl>vENzy?P%n3>rG8z!scmW*vVn2QqC?i=@T7k(93`Hll1llWZ^DV9_km zp}C0CvlKEAvP@~rf~F*=%_{ss++vjAZG|w4BO5$uaNp{U%%iAOT|=d&p+=M*Z|k(J zoNW0iJw`i~7r|wI7xTjU$~o4ll4?ksNtU>;SOd%SRy>ID!4}57wLd2+sU;YNhu?t9tR8N0!s_i zM%M^{cUoSa#^~#Dk3KwYXyck6#J+T(cSWjfxoWCYEG&Z=@?1(&=gLZv@kzrK;j6V5 zOWe{-y-(Dv#Qh&QK#Z7^6sS+WnH-+#UFSj+E~J1@t?@&4jd25@982WdeNQQ7)w`c) zO67``(q%M>(U!5^GF($&0+LG8&9=%NRg6#vvGhj8QLYzDZP{}JF>2CdC16DOY>c62dPqp#={K5FX}}Wyy=WyRX(7T%BXWkbmjdovH2W%u%=WV zrcGv4ch??ME+xjFVSK7>K-?)LbR!3DlXjTJYb+v{s`LqU+JrQvxYf!}AbsS4Ydn<@ zIlowS@fY0cb~G~^cE{ah%M-*y;2MW;oLYVG2;6ytjD{7o9@NZ{Pxm0To7iM@O3=~% zo;C>WYRgFgp;>~~9_x`BK0nIFbHeM>AVO|Tr(+RJ=NOZ|7DxBnCYPY-b0SViW^{{YOUd^%(8@UVZrx0fAJyqv_5;maAJ{VaTQ zwO=WLbvy*V-}9<)1L6R|{{X4osM;$<$(7aRY{e?Kn3D>iDYv01OaOw3{Wsj3YqGv{;NMVB2)|!2B>k&^nZsZZ8yOn%hYIG67Z6 zcIoea%y*uuOrFeUWXf^&qa>k}twe^>O~@DMFTJqlvlkAUdz#^`nx;{syc8@{n217v z^iTkU@W7|7-JH-wSE@R7P%&hr6@E&Z;*{F`61~Vj8~m}wm(Zz)ioq*}w$WD?3fwSi%WVYtm9y~ld1DP#FpHuKvN z{SwsLmkYV!_|^I==#4p)H!43M4kvr1V3T8Ot84h;r>be-T5-d^GrDGNxqlLGjV^x3 z6fIE7voiK#f{C>0DI?xU=V@LGZC_PGz>ITce8o)7)L4~jM5IfBB^~2#Am8E-kq2Ti zxlY)P!LXG1m~3o9O&Z;#RUHr1nx0jtIp4O?4?~x02@%zDg^ynY>JA|eOMkda9pbx` zo^DWlaKt-DwC$SCol*09EDvp>$`RdlBXb?ytbPmS`%j)1Q@2RmAC4j2ZF@{PjzleS z>bE%;B~`T}IMK4T1%yegRwW`FMqG6*B~B>V0;LcJ3GvxW#1p2*hem7<(yX=nFN&9g zP?tEc(nP62Blt@U+?#ZA_WQBuCT3!#Q`}Q>YB3mCJEg~xl0HP?Y|oLW%h46mX*-mc z;r2&sw9TGxEW;n9^-!z**3Kj_yBl?KLuvM$$&_LN)jrc9XYOYW(Rtr5wrr}*5&r;Q zpWlu@?tzy&3d}`(9R~vE&DedFIl%Wyg*}WqnxJ{JzEe&7y{m4&=Dry1G1$GIE(7|5 zmr|a#Ll3Ka`lNVas(%kC2c=PhKb4GEp6EauRG2@`#Z2=K-(?)%s#W8$i#-I8to@gj zatX9~1LNV0m)OUh`G+og%b*Vq=0NfOP8whE`isqBJ(KV_{BzwKueUSy;9Tkd0PtPR zzUf*cv6nps+~=ITYaP3(9e?cOBz7=oUW(6|e%2irf33IrxM@Q_Q14?}J)5j7fpZ@>_9N;^Itku}Xo%v5Ld&}4IEWdB}eD97m_AqDF6U&}5<*%_Vzk7e! z!%6<=*?b#&a6ipB2doKYNEO_q=a(e80Q|1ct~7FcBlW?TEZg~NLU!F16(s%W!)|w0a^n{U zQ0Av%yaof3&e<=ZLW{y`h4^wM78!^2O zMP8U0FF66#ILQt)hf`%KDJR`n2X42-EtlMFwIL(~sPBRov>v?9Y2xcW)S`l8Jg9xz zlq_tRjHmHQ!1K$?e@tw1T+N@=GGl|<91Vh{T5G5EJyOoJtg%T?G}-P1^V;F0ePy3O zE^yy;i%e)5A(J9y+5}{vHUgx}Ht%FPZPvATpHBgS7qfFtn+;|>?M*l7My1vD2VG8Os4Y*aqU5LDUi`~nhsyY54XE-1h`mcx z*zJMApzgbYVM-P^zlJIPBe;^H=w4EIH2YI?WV9&|DacS#lA z)pH=}E6?TUJ({C2$O0 zOHI-?9w#^nUFk*l1v+a?%o)x6?Wt8Luv$xn=;*1slo#Gr%A7piK>TrX@za*{*HZFJ zHF_Rx%VRgv!3?1jTIQ6|8?}4d;BGJDe%t zvMpq<;Hw0a;jz7j7=m6)h)al2S`-43q?>{;qw}6p&DwYr)pC~fWEET~N#{bg>&>|P z@I>%+#N_Gug{+U5rPL}CCz6_b?z`Thu0&tEAdX~$5_bfP*jvX0BDPtxaruDH8QZ!d z_^^blB3x|y4sRqtSj+kwr zpQN`I6Ki}feppnD(;JC2i^kX($>Q&W{Q?Z&2)^zNOde#DN<1~ zE2>CPONczlHa?>SLOVotgS#PBXiF;UR~G*Ow-(Nwc1&g*`IBjt$1~(u3!2Ja40{Tc z6Lj1TZf*WJkLE7THuB|hG+8>+lbEZiT&aN}-bcyuUes zufJP##ORa@E?jajj^E)>E90M4Z*)ng17bhq>;AqzqmI2@00h)2=>T;0Qhya<_oVhw z*Q?a_BvbVlEKN&^=1GpFub3YZ$`07&Uu1rNYGPVOWumy)8@Qyp1^)o-hr78B>S^xxHFn8;J z)5jXP>DA@7P-?XCat_dk^|5I?*;T7-E0uF;2q&Q~l0SYih1qGS(j<2;W*Q__AagBF z_k<*P^u>g-0b%GxoHwSBt0eK!t4Rv~02))2NxA2Mm3}82ZgIz}D!KflNJ%~-lF|He zZ$Rv}&2y5s27I9|FjA5qWrr4$L9(r5y^hBniG7yUI8oO8-Aty%WdIW4rzj`We)MCE z^Pi0iPYVZTH~2yBe9yI=OwAJW6@B)fs!B={>x?j#QbH0EnP)RcdW{qW?e0<(LH_`3FdS5AF3@Wg8gt4EavYaqDPUMAp=CF>t+5Vc{ZCABb1|}R zI30RZJm8u%2pl=(zhrd|IO?BvNkIgMORBg&L=IlQn76YXUNt72B5apZ%ytl5amA{^ zDFWn^;epSBD*PYrB?g-fND^bok?m-}c&%1znuS+xRbFe= zNvk0!4k)W@>=K`e!6Bx64YM6ddpT89@=`}6T6(M$FV#vuV%;%d<_cbsXdKw9pAnjL zi3#tcbF`;c;7J#;R$4l7PGUTKB^F)y^|3S**E8$p(Ak_O5=E2eRhLspqXu;?12l$sJ6ZmmR|(p^DO_f|TB zJ#o^@x-*jUuOX)8=uE9W7N&xg_M4mryf<+*CrT73b)e+zuW8FLPoz63(TPTQ?LcPzlnwCAAIBDW?psVPz%NF)CMo+8?DpgA{9vkR4) zJuxlRloSym)fK6ruAw)-F57Lrw#9>R0X+cNoDcf<#ceAg!IeYG@|vmUml9B3Y=yB0 zue`I+uVZA3`uQ2U8>>Xgr#z^Mqt#Y?d1|67RXTP^g8E!$oTDpZT9w2<2NO{W1TmVjYL-IjNS29^#nrmlQWSf#Z+)x{&Bh1= z=m_W~&^?cbPea^)(zf{d)(upjsyQP>a;xsp>a|Gmkty19%4gc&aYe9{xv>ok6XDl> zn1R=H=P~wfYF&d$xROnWj^nAtUBfPwOVx_n_1}p+&Y5(2GLX$O z1$p6T)RH+?rq02nsNa^}@~%$6-eok<;5%>nt3%5%>8-x&_P%j{tGc^Tpk!S%$(4+v zGjchg%aV@jboNw8=50jTq*z?69-G__y;bQ2bF-UHX6j8>IWZ$r>b#I(&{M(Hs7au`xu> zD>X`;IgIJ3`5#Ym^q6kPlO89tNp;kzwIq*soQvMX_lYCG925fgH?UbH1C|Ir=sV%> zLMaulA2nlGvpsfw4mMYh>{Jm7M{;DA%YVoL!s3;r?;~El%+d>E>x7A?r5ve zsdg*Oxgiayi3HrZr6pfPSt&M6wz%xe)ZQg^UqLf&x={57O(vZ4kFxGoidNWWBRrh% z#Z}2kO0RnmND8?<2=zkY3p-aeDZGS?)e-!PnI6*qVq$n*HMrw)@kcd-yte@c$D3>N z``Bo@N1k+ciI5}ZdNyyO#L85*S4xi-RQ4P15CHQ4x(D>t+dSRyJk|CZaI=r2_-&O*qm8h8bZ;-#(sX!Flw}Xqo)~U zC{R;as5Zh~bz`|g(ci0?K&3Ca*6HR&?TVTQQS6$INfyD=f8}?3Na^!53nFm$molAB zT`ihCN`eynXO!EHx}Rp|ab-5SQk0^9lyO6Ae;(PdTy$=qk!R6iQyy*h-k(&6;)5!n`_koAW$F+M1a z#X@Vf7;0?Y3uV=n4pZn!1b7jHinGL?k!kb@8Ga6Ng(@vFD^VIW^eyxhp(#kQLP$FS zfxC)*aMSHz%JA5uIVMeVpCmZMmm(>!TTmfr=2eZl8+nX2$%Z~ptIWKo0+TKdAo0mO zw~{WpdnmUDx~kru>X+oogn-p9zLdr*QQ5b}`=We-xlhGI%M2aq1z_)l`txN#C>~Ot zgmni74-R}>wB$J1V?4_4m-uD F|JiAlX;T0I literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/icons/general.png b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/icons/general.png index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..29c0bd90244aa20a17c744ff7624ae4e17eab40f 100644 GIT binary patch literal 27848 zcmb5VbyOT*@Gm$7cPD``I01%0@L+=m8Jxl0T?cn}f-|`5;K4Nz0>Oj31a}GUNtW;L zo!z(pygg^D`*wf&R&`a~d-~k!Q(gUU@!tl3Ku#JW4M0Ky0FYibz`re|MTn%NkqT5< z8loWezZgS+7ee6x0300MT%a-%G}=14H0T@u>yH0;CT6bA|DFF2^rH86{y*&izzo~} zLFWIf8`Ipv)$B#!{G~CtycmCRmgEJKSp6@|{2w;`Us(7*?CIw0_9CP5A9hiPO1@z8 z7tCz+|H7vKFKp)Q@}KXqoumegAt-oK0Lz{zo0k%kh<^ zB>-?)4glcl006|3005TW|L|T8{|Db_URabbemT8tRsef|1%L(s0XPE80PHV_3%~*3 z1n~V^0Z0OnU%mQoe<75Y9Tfu=6$J$q3mqK|0|yHS2OA3;8yAm=02hxC4;!0+jDV1s zgp`yN2mkdOGLkn$B%~z&DM3Phxd#Om6BQMc1Q#2ZQZjm|X<lFelXZYloCNNOenWNkyb2>`n)wfepuXf(5CCH0H_5 zCUGcw$ER!v2Y-q8S?sIoN18KLySB|L@=3+R{{W4Q`~sH~uv(z31HX>RcAJL)C4UIM z!coO9#hyy_L(3`$2nYKc2&;+BJ#Y{jp1|Qwy_pc_-@WC5WjbR;7v~msB_xrxf$w&v z2Yxj;Q#Y#BU)BySD4e_&yh1}<0lcMhyM3jkwvOhqIoojcvYS6C2i-#@{M_+=3i?=p zo??$jWPu$vwz7^j)^odk&LgGj#r$o*Ti|4?62ae-VGlMxFCen9&2#3C_FUSZEE4BVNa6BqOX=*Oiq0~UBdTdAsco)Tod-RNOP>o-`5i-4-5!iDn%iO97<1bdJx zT{cpmfwF&zdZij@Os}QpeTh=_PkPFuldxCP8pjLB^a~VBM}NnBbsO!XTRb*p18h^$ z7C2je#cy>5vu%`68@62w*4;)rg7jsa-RzI$%m{1ceY#GTcy6lcPk+{s+rxcsRFr&t zGp=W|BY{1uXC#%s^|h)Ivv$DQj4M;QoMi8DVP0DDgX$nIJfB0&&KD-cP>oH6NtwzbK=}PXBz8-yyFa$q?UMn5jj~U$m=fxQiR`Fy7!h9@EZA^rFFwm!mpCZ7ptf!kW~-k&*~}=8sbOjHfhciZgN@e9<`X{m@8Ii!m~p%s?Jkb< zHLiM(67ig4$9X2clBZJ|8kd6Y3gHT%(9W^LEWCtcBd@U3xSc92%n2R+EqWi-Pfd28 zRq}LPNE)_t3N){&Rvm6mOUstZ)Z@ogfO2RiQ4XmbRq%qXB|lDd%rJvgpV4X^>AgzZ z`Qy7ODm7Q+)!!%uopPmrRFgB%erYN4T=>n zjX5rK1HOJF>Q`8FQP2n@;)}9A|1$scJfZ3Q3_E74oZDw^v-Dx2Mtm68s%~jADVYcl zY0!nsuYTXjL1cR?`KC>n2TqyW6 zdNzVmM9pZNDhar9cBj_JzW^m6r1`btX0_Pmw`l@i%$n2ddhaN*nv`H zH(#4!l0U@G&ErqWoHpXerwWxVJPU{nbKfb_1tnE+J2L$Qq%`WHE3j=rgPu44hGlJI z+wf+Uh19)SRRiq)`t`ODDN^gYRmMFIDc)EEyqXbkqnp;4T$`qhCo�aIR|5`H{$P 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 CTgbZr literal 0 HcmV?d00001 From 6bdeca3d92ac5ac322dc0b8774c336877baf8d31 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sat, 10 Aug 2024 17:41:25 +0200 Subject: [PATCH 14/52] icon --- .../microbot/util/antiban/icons/activity.png | Bin 0 -> 21873 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/icons/activity.png diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/icons/activity.png b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/icons/activity.png new file mode 100644 index 0000000000000000000000000000000000000000..f620bf1da95598286318e85cc2eea6507eddb886 GIT binary patch literal 21873 zcmZsiWmH>B)UboQ7k4QRL4&&%_d?Mi!QI`9ySuemaf%g+TX88)ao6BZKkj{ft?$Q4 z&Y9WsY@L-!R(6z{iW~+i2`T^p!1y39t?~9w_0NS2`}X=wbXo!cz#v&mNvVC1lA>~Q zafDdgSpWcxlV&C+!aV?ZRz!64Ht+W`5@#x?I7#ZLCs;&8!Pw}yDKTh7PH=^ljtf*a zz+D z^YU`04Ak{0Xwl+FzCm_5tGP4gVeQ0T8ds8`!8}OVqa5$J;W=Bm`GcRa)RVg+gL}4E z;NY&{!*pkq5I>(*qod=tV4E_d5fR<%6A?A%$wgB4exa&o#9{4+?N_q4Mn^A5=jMRQ za&aIVo0x1we@1}EY+(xevp1256?TX&fLRFVK4jhr#&z>8{H;0LxRf>+ek$bpI~!@%82|BS{P|uyh@3`T44* ztCRBe(Cm!kq4K#8>Bap5`h11;SU}nT3Z#4&7)W`Ff@Gbhm%C zAGh!P_nB~EJ>l-j?ls_Lo6p58ZLN3zE0HJa;oe>9s{!MKu}X^A)5Fh;ABoJy5x?1A z87rO%Zm-xc0}T@bEqiJMAA7o3y~YAN3tlbqj@AQTUtW$W1F=WCI;da#v+ff--|epx z#{YGbJ5@P+9s2vy*Bh|D(seZ^JTR7~T@^@iRLdY>Y}f$>gJ-<;cRy}-^)8$~lRO{Y zbr7~ctQ`m3H}xVX<=snSI%aO733U(}SJkx+k$IX^n#Qt}Uj3ZtjBQQ!O?koZT%3^y zwiC<>3T10P{~1X$9Ci+x7~QX#h|#mFQUsY?kRei zqh)jAK?rCj*L!EFKB~9T74UNW_nvAjg^krRO9+eqeE$wd73HtB{-K@QmV3BZpu@Xm zU-Z7X5yA!bRV8`59z!YSqR;IEi;^bPLZm!x?tZkQC*q9nJ6Fxen6z(8<$&Y*%lH$v zW>H<|p>ikEJ)tDN+%x8lF6@h{qvWL7_nPcqf6piYOUANoYQD~YYi6z+Pygibb#DHX zW65~F&)L_7HJ|g6NgO%YyOHveIMKn(6k8Qh@5{ZV+Fzde_g@&qb}LkUAG!0}I117} z>bW<~E$CjwIr;>DZs|NdJ@?wjB-LE@&M@?B_gFWZ#L|D7$>_SAtMz{&U4Qmncs)4^ ze7NaaY=T-YY%MVvFTAetcCIW_MhqITi}j0`4Vhw|c$uNWB9i}UY>&t-5!%oHL%W6l z-X&$=r+28(Am$!wpYu>k~HNbdocPzAM${Nin_;6s)2gCoJVDPE`&hSqe zrrCL-zM_Ty&C<8nm#QYq!2F9_mI=oOT2`aX66pBP%SK9_vz7LQnVFW`KDdruy{w1| ztZ#`!=Gv6$SL`po>w+~MAMagIj`PuIM}kN19@E<69!~tkHu3!K$O)!|mOgbp`Q16N z3<1B?E_@ofGXPH#JVfTSZ?K)$I9ylpp*3$^IdmW2U=UxOyLNKd5`SdbRoS*~Vs{Ag z?ONtj+#$L1v-R;AajY_%o4y*2u^FBeLRx%=a;Wa#Ud>K^ZhWRCG}->nFJ2 z*S7E|>kw3MokT+by^OyM+2kg{T#k6Czu52v}T~c|1;<+Fo#{a2L6!={BJ*jQMvFy>qgRh!vB_J|F5V~4D(cx_z z)BZEle$+AN*-mVC4JY8b2De6ZG{DuOhdF2FOfRsj&#>3v1rGCZcF|gA?{J$b^kYPs zi++!%e z=e)DMlgmP|zuB+NEVIpy%mZcfz1_Cj`|-7|(x*C~mpIcYZrf86aoSuz`xfVm@f-`Z zO!4uGQ$CY>v$BDJ&5l#O58eI1ixbavpguwYGAWst!uf{`1v5kG#$8EM%5~~*RrZWP z6X-k8duZ2BISXTMG&MNBn?IAQz{#Zirj?JEt50pMmk|ZE_mZ-*<*d^}b&e-sy!*PO zMd#I@x))lDja(!b4IPtrRtZ>&-6Wm|^`5X?WjZ#dv=6^nS$T%;`muo_c6uYjz~K^tYdW}(&E(j>qx!}3)Q8UIL6e*uZLcao-u3~ zMpj7Iz1TWcq6yOK-%#ailD6gFRYoU&+tGk)Cp|u}>;5x8J=DmkqFmsN-c+ zg>AWX{?P^~fXgO2f2Ohk7ht*sZ6r*t3lbm@N0Jv}r`a-kB3)(7fD08}ylMG<7GYs~ zvU+7&S|9KctCe=5xvTRNWQAKD8vtYAADDxJ&N$fu7(hBkmrhbQAtxuneu9NYO?MB+ z!okCunDvBE+R;kwh&9Vun(28dg{%cNhAxn%CwSlWw4-F5AY*sXqWRWq^NFIY#f+!y zBlA;=8jWmRcdJ;eTa zA?8ibXPhBF)QCKZ@{{v2v z*E?AEqXifoa(HR|j&h{oL9ZjpFXMWB-;6?Ag)?1-DHAi1nKj`p$f3JzS%LK@bkcT_ zguS-SxK3K>`)5_urgpB!7PL`PVeUKz9`fFUKYhW0$UTX;RDlL85NIVXYh*LItwKN5 zyg=yT2@0~}!4RAo{m4c*G=kT)Ang7RikTX1{`+e*Nf~sxp}DBv1?w1NX8F2oYOK!h z4w#cZZ$u)w$wLl|>G`+D?6q~N6t%lgO3P4W_&gWAm){u)YklUeqrOGsnu{DI60V9@ zL*K!*f@ArxL$Nyw%q1$zBZi;@8j}6^RZE5}4btTF`q?Q+U;*I>->8ud#Ny-jMEGvL z2VC(OBiK07EKl^(p?+6ESco9jX^yDI`M%6&>c z9puA9Oog|OkP-Z?wq_mI6;V9aP%0t_*B*h=L#7k;DFFe_uk)CE71`vv(`g8qnxn=a z$f*ZgbYW|KGAUQ6S6v?nFXn~muuwpZhXc1YRYP|K76?n$uPovW1YFkBid(_Ol6T^A zJR-Y=H7#D$%X+1Vz7Vx_)fzu#L8r0G2A27ZuA$lTx)m>J(P@D7?ubh-U9dFlvuwW7 z1_>MX3Jp$R?R7+*nQoR)f;SoN%5Ghv5t+GlT=%jNbzBhaD&b&=3$+uqskpW&z1t%* z9`3dISm1{!W{OzbP%7NuvGESut+fr2?|dc)2B?2TY_KooMG_n4L5txw1MkeuJlAsg0oF z9+im89;Qhezr+yjDD)FlYH~=4;-Gu|H!ld)r@{&+ghaeJ1WK;8`2->~Qu#&Txqfyx zq0OPM=p?MnHv{6WtJ4@Z|0#+XhQaFZiS3~>!%QI>(z%}k|et|(DxvpUCJg6t+vZ=gjCT72y%?Pv7YzzY>H zLxF?Z^>bz1g3QTZfXtiX${32YW;8lc0Fw)`hI0 zgY!k)mCj-kPGZYNo|l;P;_8K>7tKW!$)mWxfJR^B@2hDih$)ylvW}$&C25v%OPZ@L zH0%*gNJ@(gz8gi*DpF0FqDvA>>5SJgkuIK*yBU6Q(2HQO7s*CSKh$ATW()y2d$gA; z)P|J_7Mn$80=74Z{%qpKqbf)>44mU4kpUsH;VeOF{&NAFY4n_bBWu(F*EXM=6-HW$ zCAS*q#h`Q>JDhM4+P*R4gnYd(D=H2t6L+;nfzR^9+bhziLM9z;kL1=AePrhF=83YB z%A+2ofQ7dq@5!m;bT3x!j1hN0#Km-+MIPC2Bi{C@_@2qScY4KUW39dsD_fXzIaR)C?-pmI|y zm>^dpMD|u5)Q!DIXq9#A1jM3<5o$|=ixq!W);4FXi;5-;^61A(3Q-ejmGDGMipS98 zldIFsAJ(bwi*PhUJ2{3YqTfMYFbjPLM1HlE4~;0cTNg*#>h4g;6Sj+0kd@|b&uzt+ zJIR}&*Fv>|kqlbQQt+qiPY)V7qL9ZS)f{Kon0YTr5S8mi%0Bd0>{oO#voJA9Om_vq zn7|gd;rNp;Hpm2(R4e>!{hr9{Jx}BAZaYec(3*U7|55mg(>J@}duJsRq zpfbD(jxwlM-~tmhLMsRkwm`@LjP+U$uaY$t)LV$5C=L-TZ-QXJSjof#0L0|#Ny6(G z_+7hB(1;))^%+p>a);hYjsU#nP`x_&kVWl>=iN#dgZ5Pm!BhtA4E94pB)mCFjC@eD z2Hx3_!Bgn`kZ$;`gxjlVb9P+o1Ihtrviv<2Ab`Uc=4_pcV`5yt)stZ52VAyeNGW;m z@RJfr(LVah-OdAb4mKt-@w^2^hWzRF&nkrq4owHzlNdg{cud#ut+Et>yiwsJA}mmR zLoJ6pl&_J4!{L^cj1YZ!8?l(%AOns4+QcSA)(%OUHiWV$3VW9=(>$+`4jbJF)?5HL zJc_Y;Qyj)FJl2HeiAyoOuK~u6exj?e%0S;=YI8_(i*XxwdhX z5;h*O9U_Ts1urWPd_6F;E$m!o^-e2bm#YNo={K=#JxbuHl^)Tya_KQ{1YVCH-&%c&VVfxcujQ|1cG zym$?K&xBAJ$OKCDT|5Uu`IKoHr%5dOB^ppTvOIQHb_tu+1mVl6>lYTRP|!&)iCQ%G zehfly2FgohEG+67z*!XrW^%c%<8CBOKzcuhc?e@E>C%MEAnkzglVG>j*|BVQ?m=2} z)v%IqLPW(nG&1bmp^&Imp6?y@J*QXdcv;&HaffdjzgO4cJki|`^|&eL;>A05@^)s& zLZ|bCesj%Dzvqa7;jbuau4|k?kjG}@P^DW3c5>5VFu!%Yr2ZOjm?Hm|q&hDZ;XTr<;NrZp>FAN$dhi9*qDS!*C!B;|HQl zQo8wfDn5=Cq_my@f*RFpboy`zWRfP4$3Goqz&hEK)u6J`?KMG&?-vH~fD-ag;O*Uf zip4eVo`@cA>=*C5$gIU!*yjjP#F``*?KNBWfOJg}4mFCy7CQUPH zDQmcCJY`;;9U|+c%PQ7PU-e26%pgMHtrVSPz6rzW^jVuEM|QEB1*E{8C6fAfSjFYN z4h0Rs5yQs?n@P9hn;jq09=5vWjl?g$0~B@9JO5FxqNw^^n!J90E}NvB>9!WBYMr!-kdz2U1tF38z*`@q{I1d5(&St7p_O?$i}80;dIFO`czd& zb&&-}3|RL2*U${^wCS(7i6INDpeI~bH}Yz#R4wzFcA!~z!(*%~PK+}}Io_I#SI{S& zw2`Y4#=cce5a|qaYKtQW%q!9`G}{a3zV&^I@&yXc%7CP8Q)i|*nbl1RFvzYQDJVC* zpqOIH0s-vY$~04GJbjJS!mon4a|ZNkw{6r7I}uyYfvP@3BtJmg%boeeVJebgTOwyd zn2c#`_&90QcL_NWE4pYUvhDkxKvi6y3XmS@>#UP|;k9SN#Vyf3*&WM7Mx37Fd(kex zOP?Q`2n>Sz6pZ!W<=t0nt}vLB`+kZP-Z&iXo8IA^PM=Nqaw*9|zoC&nln0LWNz+~= zE|V{;NBKQUN!k8E6%X{0sOPhn#F!pgHlNvO*^{0U;{v!Qkn_t*ej6Qsbn>(Hm9HL% zYxDski%r#)0!>m{mE~e|H|cUuU2qB1_U~OSLAVyrcmuj11tQnj$QtT-f2=|*UV)^9 z#aVWueYMb{*H|P1k}2h|#iw^sZK6125_v6JMVGNcOef$I#%ID!;==tE8Ahw)= zMJV=!2|Ome^tMA^Ras%^EF0dmbPz1B{k$GUT1pim<3vVasCSE|D1y-vWjpK4|I(?&{5V2K$deel0qt8WA!v5U(wYl>7^pjUiTs0jXFr!eSY=$v!D1^J>2h+&bHx{ zI&=H;q8}lRiEhF)N_Ef$M))PA(6vYUHH0g6K68}Osb3?mf5QB{h||roBR3>CK8E=@ z`haEn)mJEgc>B|c4gNB<*p5CdTw$IQkLcQ_L6rc7FfSj8h9G@2=06^a>B5MCL)7&B z^gUU)8c)o}v{_4)yu!cU`LwN(UmbPrR4s})J%viTbBR3?hOwG`i{ z{2iBLHU!o(%Fo=~vR2d( z<5d#z8LTRqbrq}4i!xY@UKl0pO3p+kAJd3L=M&WtF?^3ILXMYlXM40+2nKciq7}yj zXV9nXx^fuvO-2kJYXvzT46ErP$wR^;uOIW?>RqC0@fJ@TyIF~R`5zMH^*rz{nU-8K z$d})Jn`uL9#WA4mcsmKPjCoR>7<2v#EQa zHsh?pQOM{qVqsAapW{V2h(~mO_Pcr^0=LZ6uu+&w12&p3Xbo!74>-fJ=2m7^sED}y zdWHEy*!q7Rt9PPtN=!+oN>E=|0)|i-AqdLqo>Vo8?G!8(1!SFl9cM^;fSxA>*~w40 z81+(F{Rv~+is49-6hV*sE^UzEss!%ZmBCD zBl6LJc}db$&^8+fbp&2HCZM-vU0VB+N<6%L#IrupEz@Hita|9E@2Fsx{&sdhV+|W`bDE4CtTqr!?4U74(iU! z)RZZ+YWwZcdhofyJ4$b6PP451Scc9rZ*nSg?y8CWWB~}W6+#%to*wNWhRl5NdVHsC z4?u)+4Ny;1K}xqzas}(bF=;2$^_zCC&sK*m%FXo7C}oteT(uu+=X`2QWmQNsUi^|0 zCB$nSY1MAT+s3laT7a?e=y!9=ZJccY-1~Di4{dS#=tBP?^PUE{de5{DBLIxh7F=S z6MWO!V*YIQ7vpRZ{$0v29-&l|qeUDt!u!po$m>#JQ?A+{Qwdd;6bCp4$IUW0K~=a~ zq-EE{VKD0NK*XtQ^VVs`AI9Y6t5=?JcREZau!VO1e6Q>l1Y*1f7l?1ulU+py(C8=H z`>#%8v<;+i00>^<=f}GQTMt0$Gm`wB{mdAqaAR9qZ9Pt_oPlkGAi~26;%(S-kXB6*h%(|#UxwRu(3}1+KmJC9TkzKaSQL!SHoGH&FFgwCq_aIp zgmo~!Ywmb+4q(g~&>`yqrAeESo~V=lwh+|1OVDgD zYR5Kx3N!s=lZM^De)ojifC3@dZ`OwKrIFAwVN&#PCNhDN-(*qk-i;3hz#FXFur$Zh z+AFdAVZC*o5il99jexcJ2G8-})nZ4CX@#&W-fx1{KOgW;mcTy@oU%`lS<5p5N6~{2 zB`0jKIxo;>YnO|pR8}t28Zr6n$YgWYNbZ1jr-h0$d*J$a?ZcXj4J#~xeHk_>($+ls z$L6f&vsvofOaS$;F#Z}sXHoM;>odXecdQ2E^cj;y;^>;M=rN{sLA`8jIm~5q&M`fu zUP*ad63K+cG;JiTctF494rELc$-jrWGi!X)W8+D)thhjRsgONTM&I+{mMPR{!*eUy)<`vgkmzZ%(mYu0ICL{~L8jXNFzqR%K@8d^}mORTW zLa!gHruAQqx3tet%-iQWYAwQ0x#4H9Yfqcc5XEWHoX;Rvc+;Z;3doJaEj{J&Fvi%@ z^3)vpPe#&Yr0Vh1Cu@}V#~94ZRH9L)S^OcS&fp_ zq}_^6J^H2BU?IaI(V|LWjWlY7g?6MP2iFc@?FF_mq(#NX54gX%oqs&goN-Dq1eo_) zz{FLKBhZH~daMWvf;*Y6rsdlV!ale@ReZUd((@od=DL`Nssu#h>QFblFu2MtcA)4M zKE?KH_ag*3NBAtEmUK6??fg5J#zls+upG$ z4Q8vgFjN7VC(7Phc}zik2+vqwi{XpYr_zbOdo<%mwN% zKIFn>(BLfI0HR8EJ{&l2+0Z;mXdTm3!7KQ|=S0+Z)#9X|emr70XeikGgJ2=b2UiBW>$OT5_?7vYa=GL~js;oCOaG`V+W7`5!~E1# z_hEB{r=NELmFO^Ef&TDJs7IjK%M1J3M35-T!yRx|J;KVJwLzN{z3ee}zGC7tzL1hn?c#28`-5of@ zTJUL8v~C5?d6Y$65OStD$cr^QS%>nCP;+ta<xTIs4r zu`!ZUi1<fP=XNpkyA8eWhs&>${Q)rdq5%I6Zm(xRn-1AzlWLqmXKoOA|cPR=Q6^;pDmzC)D5bLEDy8%=nK5I-A6*RFCtVbYFHwUY zRSm+s*~Mz`R*R63KC|R07^#}xI30bP?t=yg%{{K~O1i-V*%Hmo%v;N{F5x*|Ph{}V z#d&}DRAC5u=Z$yL)MU^fs-AhHKrQGZ4fdpJLy@GW8ZN$@r|FYefz;(`2TD5Vb+B&0Z;a=o%B?)k6ET`brJCuE~)r?_f+qPzZupmO9Oeo#B)sYoO72YtcL z+^ysl=*gaAnDD9mB-MUKKY4bntdNnEF(B-lb?jaM$#A$v@X7J!H?+w@i)-&) zD)OFWWrX*FX3b}Uu>Mj+rBO~}>JA^&X~DN{Z&}suYsFKr$XQv_>D@k=ye|lscI2qI zl_oAiMY9@uWcB!N8%JZ*%QGMG-GdAP={{-(t378ZU_^K0`81tQh&|HWI!%&syFMuM zvG}PEL-zvXY201DIRQ8$Y$%8p8;*tBjwTJ{ubR!{tJj?rbrIW$J8<6|$L4c`_0?Co zxz^ue3W^l}dw<;cee|$+Ir9>8CA(|kfw@Fr8#I#VD)eHNiq;*$RPY3FjqZ?;(-#i71u$lNT1ccA3{J=@h_h3*SF z^?@3A-%NK2?VZI1UOU>pbW@{ZQ*Dfc{(W#qwWax^89hDfl(>&eW$R*%-ybIc zGBLW1UK$qbd!Iy%W*FQ}d+yaOeFA`&Y^su;IOFAbs{8$vRHBT0fAVw@W}8JD3}3e< zwXGRDv+oh{i4SVy@RXjv9>6em#6 z!GN%Jgyi*UUqG)!sC*2>gB*dh;gIWNF6X1tLZD8ewJ8X|nXQmJ-LbqVkQ#)AjKTJUT_mrD=bdd3)^Ka0YMYD`xCL{yt2`(hiGR({cYVrDYnEPq zUCKgaW-z2A^`@{b0sdgl`LzXRmG#q^oQSkyWB92-Ue@c>R&7r52|>Blc8M0p!4!C* zR*rS5#rFXHGaVvhS^k40AmgaOwC#|r${yUMLQb=pxEJ4!?@BjWA#uwc{Uw06Dc+{B zBM10|YXg5ta7DcK4O|@}HGid4AnLU(Sl&IM&kVy}_fttA_-)4qyO1AUJWNl4Ne=0` z7Fip;NmORtA`FlMv2Ni*XPx$T-&qK3`vAD$|G>A_6A$2_pmk1ec9B=h*bxDm>w$g1^_;yY5R{kgO*Wn3Xy9?Ak@2>n`LCUf2>`xH_$XPRvfsR{@_QZ6f- zoeg~X7PQo&+Jt1?eu6r$=R=mQuDK+#dvcU zv8B4RrO;;bPA#zUH$*WD@i2_-ar+1JC?fmzW)zdZ{s~k=vw)Y0OxRc_WS5Wrjs*kKKr) z35{G3>@DozLbc)(XOJAqYPEf+N!)Ji7Y9$|QvOMWXNVjiZBJ_qs9bUq=~`>IM24d7%MTpRD}JT*2>BW{$3an#CYYNB$(24fj%8(#f-aszaPM((%3LcG*I`j za9R0l%-+NK7ETl|k^E2ziPWp=X>~%Q2cYf@Drl!eXnAJ!?Lw{70XG4iOcq4e-80Mh z>h_hcl=gkp;*;I+DfhCB_YKLS8+uT6Y&8wH_FzB*v3=mD8$0$BK~rL3JmNQT6&dl4Kz;(yNDP0_QOeq=_>8Xr#SJ_*#}O;9Qblr z)Xa&PZiZ-%s{On?HDn9|N(TlQsmrOXlM`6t>~cTKa^O)U<|0$*(WM-Zo5PhyRHT0< zy5hv?u~K{Oh>taw;_ z9vvNi$U;T6OPGSy+GW;oFl3u6j=!D;U3y1eQYXjdRgiQ888pY36%Eb?)iXHDY!RXa zbNyGgVd9uw>julpfJNjJNm#3mcF03m6BI|S%{)9;XeWJePR2d5hi;!Ms&%uanp(uC z`;QRX&wg+sQ`eJ?VqK`0e_%%TIEwe!OR9~)mn+~~vP9Z&mTD!ub5zvtAb+ffS^ver^$gYD^%?vH zG?k7kA9DxwY956ndnI>XZvGJ8^W&`4J>Qn_3~KaY7$?B!zix#}@SDRfOiv-m)yA~i zYQyaj=5__iqXXV|Z6ns5oIF8e@7k4vga?5D4p(>9jJ-#RGNZkUnN=!l3y{ z@2-gl@T*nE$BuA)J$<|J=%($a(dR+;)O3XmyWq`WDPpytY9WDMV;csp zhOj0PmG|8&ivbVnOfJeDh=#F3LJr|`I1Mc?2KP@N1Co*Ms^+P?YqL&1EcbIsU#tLE z=89BWC)GdjXWO&^3^30uBlL=9sf%j8}~fnp)T1$tq9fg5qttuD&k`U=Pz?>s%&sA2eEbq znGp7IRW9e7-JT%=r!0~5zo$qd=~V`2Ez8(Z&$cZO9KU#U(h>-4fs|Ty!^1~mOEm(Fdv%q``hJ|YKZP@Nxv%+6U zKzkAhXr@ZDw`nRp&0iov}to?w=erNEl!K?~`7m`lqXoI;NBkR@U zQbIB8*rzggxa@b8We{xEGtGe=%*LfIQmA{|$ec z<&JN4WC=k0qh!b=$K(kYOD+z^OY9^CSb*$LbjH4GH)6@I)OWZk478?OkiBsS`61O& zvj@;J`!Z?&{%0U`{ZM5{;i;3R)E<>4utmhNRPo%^3F(@g_B$Q`R|*@BWYedxm|#7P zWKQc?Skn$#GKp84Z1}KBXF{5rNV=iiC`+P3d?|$}f_lSO;uYdKW}4gFW}a_gwi~*& z+(X^K6e`>pSC){MHn)#VHXFkxT)&j~g=)K$lSxLVK_B@e@q)|9Cd&BOvan?ubm8!U z&oUl=O;RiP^Tr+j?KM1V-z)=ST>?D6SwX_SG2?QL_hYyw z7ezJ()TMVdliUI7RX)DmO31OpWC^JvOBI8GQz`&Wh6y4sl|yahot!Qx;CFobv`;i9 zHg*orzq`J8Ct}^IGo4F7Mtu$mVeRnr<56R(DHb4QN&6N%{mt7}qj-&K>3JMbHiiuQ zcCmNKe`;aSuQ!BqswRRBEV+U*K00CJgnj&(emgixvmxISp`bSsh7%ZOA8>csla*#~ z-+d*<&dX%#c6k^{?ExU|FJ(aX<`T*n%7kBPqu*|KxrIMd5NjqJByAKf)SIy8(@ zt)-}GZPBRwi&;=fz(Q^lsu=qyM9+?Rr3j%+()d7P!u!-vjTXRX7d-E_*p&FlRJ>(z zXGU9YunHwin!nc;>66D(CQ2+a6E1HKK0DuZXvSHy8;@4^(}=)DMI+r!vZAIb*jZOw z>1Bsw2QhWzlWgi)*q!?K7%$?n;J{ms0%y}lF{ivCsQi!FF;MjP+?VvPM*$*vXIW(; z&KX~N{t5uf$cl%KCz#;!xxlzf!J@Vm%~3eoPUi&VFCwsFD-je*c3!3$4>j&SOxSS5 z$l@Or>I3{PoBJd_<4@4>P_7N)@KR)K?;h<4dNmUty7Y7xhRI9FIZt+MH6Z@_xdP7_ zn&+sRb|(ayzpsQkS*r2rPPg1~_tCQR_Zrw{2g4ZHePu5xVB+k}#eXsP`Lna@NJ1*I za8c?#(s8?0In`LdK7GsP+NK)j&f3r9ma_piqL(6}kT~#pbd3K~5$8hL(LC^R37O2G z_A8Gl(NrbqhfqPZ=~(Fywt&=XI2H}nIya@ZQ@_bdGai3_RH>*j*Z{^(u)1FuJ(DUP zDM+PXqc;Bzr26Qzj4AhSvfKCKi$FV8KhhVr`gJ_Q){Bkuva$oM)G&q*^qJwNk-#~> zhwIVf#Jt2>2_N*{?ZmGYSntf83+Em+hhR)5f-p2Vx>{wVal}sp$s_uW`K864N1MGn zm#C^2zoDMAQ{_)cnI&RFKS`Ysyey_$DGxo+OgbF4oZ3DcO<)AenAuYiedo_D`OUIc z$En&;NwAxn+g}Z7nT9#PjV<$sNc!-XC9%3s_&z4u=_{l!jAL|W3?1=yc*S-cSn5^%l5%j!to;;M|c1 zT#rubBJta&X6(ZYfbf_|S@~`)9HCS}?_lv0e z$lK?`1EQ(qhd$IoY{Vx}DpPUh(vj$r6_1&^Bu7`JS+hin?bndM897+jP>||xz$qoi z3IfXtZmL-gBvo$cjJCmP4KM4rvf!p~B3mk!%Y=ZADyt~l%^{g2a;p&m28;PzBllXv zgf$;yyQ+npAHSYQ`wIg$s^BC;rp1-rp=)JA8CH>^l zYJg1*nF=9#D%>R+N&8Qr+jzyyNzvE4IRh;D$bBn;kJ6d4h$v-APTMDC4AIy*cm68m z&Vt{wb@_)_F%vA><<1`-TN&-PDozX2V9aw`#3Gn(*q51<3g`2ceUji>ZkE3S-ArK7t^$$e1hN$@;j+7qT9apuZL8j_b11@9+s{rkE%mCNOW1K{0!`2 zGS5$}+j<7R_Kz{^S*lo;^FtIyP7IxgE_UPABoUEODD62-Xa@tWPhf9seamEWnq~Eg z72)*Rt5YNB2waBOyLWUd+dV2R?m}3~PE-5tm40vkf!vxF++sf}HZw`v9+q$F=!>uk zRBc*XoQ_}z<>0zrX7(+%LxgQ%*xe9&wM7A$L?j^_ z^-=W-*y4|n_lb{|L(0?_VVHX)6W9F0_Sd*2ST<*RrQ?!q^?zki(FsvuW`-DG?58Br6En9cjChE^zFY}P> zy?4wsT9Aqfi@%%22|K$s9QjX3e2kB8p%NLqt(3Xm(er8kJbxw-6OA&;GNH;7Nu7Ke zGc8PKI@nkqQzj+2bkd{yunZyWx{vR3PmWv8*Kx_gUCCcKQg2Bp6I__g-$7NyT!(*7 z%FV>yXq#rf;2Q5gZu&&^!Xm3V{66XaA~)D7mxQ9NxpmnSIh(cSb-4EHchk$)&bQ;N z>A(atJ=QMWXDrpO_y(q!c6jS{PqM8kIETo;$tsIP3*-@yoaMAJw7Nk7ud%)6)^?HD z1(ap57#GbrjPYcL>!P73KQJ@f&oawCD#XNNkm90#n=ANh6qCTR@1q{4Lq16_ND`p>6M-vSA5q_M>;PX< zjqR(V1n_FVM@MlhO>g}m417nW&WigQ0oRc~iXcyZVY~=9w zPZ+i^+b>t+9-MMXby2S$=AX%(i)PIDEiVyPp`j1iU*swmk5u`8cBW_qlXUMSBfZBi zOu{H|cI(51u)pvzWp8%0<~O=%JN1!f$X(@5=BW*zPKQ}31nV8Lom%AJ|KG7cYCDLG5}{q z=8v7T&k&MZ{ZC1WyWk|7*A95EA!PYuvF`)z5;1H@SVMNfqCU zb(N{TrYJTzqdmR^$j-Ajv5gewUN`PP>u(}_UHO2`oL<&PauKO185qb&>(YSFT!FqL!ye?q4-bt3wd153Nx8G&GRlh@F zs4m?99=rC~ZB4PuA@I8T6nS8ypKZw#oDXeRQuQJ!L}Dw|6@jd{3Xmz1RSt-lG^;FM zb8Fr5TZbw*x!LvT3%OZh?fhyYcU#`~nad|o9RrN2877vxZsd<>V{+0_5a3N=IG*Lx zV5=6b&XoP_6~9`|m5>*fdY&Ihls~c>O5T(;=^wthB&1v}}uF+!VF>l)!M^TG_;O zyM41N$VkHj+@w5mi$T30!zmm3c`{(1;U|!%Z6oG^`b{w-HibOeaKa3%9hwkV?mT!Q z0=uNXV^^Kd>*iJ9)Rw9=v5k~q>B0Z(qvX2r^ZbFqVa@qP?6FQBe{1UX`K?8p$oj3t zSXWs|(9F@E9c=DsYQgSh@ATG?{MLRf=H&!7v$b%eGPSU@b`YUCZ||a^vNji?`N*rx zq3k4OVP!4v<6@!dqoQTzV{0a0P9r9YD(ofr2C%nq15|quCf}Hl%tCU6%RWPI|rMLm$e5MjVLOWu!}iFP(xbwKM`+FA~aTRZcc(g zpr@xNyC*lhql+bwQ$Rof$iW5V;$nM~U~~0$a07d>Ik?jPgZK}Iw1umgi?x%RwW9;o zKTNQxqr0034b9s;)qmM%@1(5!-*^Yt|BS+0Jb+$cCm<&~2hiRg_`g@Uy2*IFLH^UA z|L+P{t+!5XpoWF3qq~clg^Y)VgB$JtM#z0oR{L+lKQ38X+dKX1)LZcWk1}(!|CV!d zcd`3d#@q~OVP|3g26cV2kMsY?yIDj2Cs_Z-wtr^+b?1K@@}};;`Ts}yzkL5!_)SV# zSy0;1%>5swAEZTS{;^lk+|kV1T=3spUUP0PK2A;xHcoz!1sjjK1s|I!2M;G353hhJ z$ifWF$HBq#zfd0>T;0G9W)}aT-r(%kZyW(mUJH-~go6#l#V^3dW5&zLCcp>cVFU5- zfx#d?5Qq;V@V^kMF4k{V3AX#+R{euAe?x)HI8FI@IKgZjATa378h$VvnA@C>jT;2w z3{x54N-bIyqSW zyX2q52)?=V7FzH>mHI~hcl^y8K`9ptu$!ZcmZPJc2+cp%QvHMcx4@}{|06B()~;_7 z-v8A6e^$Mwh4X(r{l_M-v;KFAit69O6$G38hZ0w?hlTmSir&`!$CQ~B*um1`ZHND7 zN&VNj_5UYb5H1Ta7e5CF8-(+pbaC?Vun9mcxY)P___=v4{$D%i8P$Z6#c@hBk0MPv zQpAAtgkA##LJ6WYfq-C;-lQ84P}-v#dhZ~;BM1UQ=q*Yw3L-UB5s-&~w8i(_x4UOQ zdCu-P=i{6^<^P*I_spF+XT;4#B`p71uD^SCceeKMeBfpUv$;@uq2|Jc{#3)k^Mf+) zf6m6!*6KS#h=`aVL`o1Mc~@9MMp#M)BEkv&Ar}07PybV`9QeO@$o*;XZ$aRq-CuPV z!Q~=Wf&UIxKX`pd_5@1(SZU!H(qge#J{17GE0FP7am@~mj-$?1i}36~)gU+|$6R@wha^uoQH2_2 z2WDm=4O0h`;W$4DHH*f(Cns++)Dpm!<)n5uKhvr-?XR61H0C-LFfi+8R^9dI%t z*rym`r1f=GJFFw(Fs*)aP;jOJdGDTe1}+ekVY)?4{IwsOg^e=B?hRlZhDse)fL>bl zqQ_@N%08{f4+au=6Y|UM@ld_-Kv$=jLC5-axD)A?@qS18%Z!k)94c7H6P<<5z~%zJ z30o_?Xp3vp-pXUE4BWF*Ye9th+ShH_9vYJFncJfdxPQ_rhX8=ahNbQm&-1K2KrjNrwpvH2)_>Gp5KBSB^+qN%jDyV&#VY{>jv z!p+UC$I>@&on!csptz8p!mpEPE$3)Gh4Xrk_neFFs3$Ap%^pU50DjTpxE}e9O(yakR6Sbx zkLN(V5^HXA{ONHaCHVgM>g*7=Ws9@Fm3?AqS=o$tp7<@A#zqyRWakO29TkbnwYW=9 zcoO*NDpp9P_zfz;!jQ|0B3Fg8nJ(wC>k;jw=@}TbxSmo(a4O*B)6*GUy~cxaC1|~y zG(o0A0U;@o+5!|Wp=Z@d6Q`UaAlfQOnLLa_okF)uuZ=n5Q`D^MjHgq6JE7Erl#guT zH8mvNxzjVvk$c^vu+)HsSKX08@FrtGm|ad8{iJy*hwdnNFO4Nvw~Y7p#ym}H4T1(m z6}gFsDL-`0Do7NL?j??_VL`usy4&MaI-B`gAU2Br4Pp2Tu^?M<0MOG5W36~%>2#+? zqkts!fyu0O??cmOtC=3*8avLmDJIdv^)Qdl&OGPSr;=^5O2QnNbM6-nwa=>+$qB9I z-ax$%lb1-aXS=J=x4v+4^dfnid|q*NOp2$og5rni>5~ZKnye_uf~~HqH$A8bK^XmZ zm*wU(vbX!QFl77+MMjwcA8ugdvV643`Q9k#UYWGe9&6FGfBl?Tz2#~~w)jjIpdcua zAiUIOXmd`&Y$2X!1IP35;ls3CvM=59C#a=11_{fBljH7!ct^pGpw|u#_doRe39uS; z`L=Y7`P6Iosfa=E7|sk%5_xAgHcH1ZYxe*_?t7|2g!+Z^%U$Kft`PBwM_Ug~Q;3H7 z#g5X9QjCm@6C!XjP2RQc+YR*xEKE!pEiQ|;9h9Ip(w6xAZy|Kqpc^1qYFeuK{BQh8=1u@)OmolJP%>DJdnO-ZC`%SIJ=h8a;?py zYZP=feUjYV8p5S(8=t#JZpqqL6IbRGsiFCJZ*x#N&DQr1tFyzKI9!g757^sfx+u5U zkvprIo0aaL9u#>Q8@(-1x3N)s!|ce~uXgu(`^Zw=tC5k&H{6=D2Nrn^(Z{jBZ-)mA z8Y7MLkCS?*+}zViCjHH;3I13-43>nqRliy0ML#dq35Ofc9ZH(hRkJRzwL>g?uknxc zU$p@jSUqiaiY>{UeYM*&OVfQet3V&&j-ZIva(k((3(nHe2v!%KPgBmXz%jVwo(xCl zlsufd(% zGRKhtj7xPMsi_Sya=5s7eK=|ls7_R>^E=gw+CYuHm%wjdLu({wMV zl!}R}v^9h8m$ZPlqZrftc3U_R+3;dP4g9nc1 zFU1f%=Y}9{&>>on9?Qh9nK_ydpVV}Yk9%#mZmq0b79fs^qr;0km@g7YSLY)z!TrID zjvJa^sDZt9ZQiEP*%lv3px}18t0z|*>^_@JeM+R8*1R!-x!Gr-Wx<|#C^Rrl+_I2mYS+u`Sw^)J~l;83Ib>%=z@$?E3U^>``e9e z5g^K4h@b+GpB+fX$lzWzz7-K0Cd6{=4@B^>Wiv1_WhHrw%h+%3TG%*p8+jkSGDv`O ziHcOR%It`y^g!wO$z=!vXKhy;k6GR`44$B`*~tLO1Fw^jvCqc{%f5>lJ{pG1U#91y zeeN%z)mB?uOHF!sF&i<-hRw}QURBk;rY5ZG%Uub7GT?HmwSxGKy*FF0H{CyE_Gm_9 zLP97jvSq|ZRjN@TA(sLl0ZbFkOieOXL-47FwVm`pRBMtO$Q4^tf>`g=NR}SVj~^3w z2BgK9^`PEM{q}yGJetYZjMP=C&mjOrJ<);1kIJGDc)@KSnw$nh^ zV7@cIzQl$Vq~CB6g6=IImG`4G< Date: Sun, 11 Aug 2024 00:02:03 +0200 Subject: [PATCH 15/52] Moved Menu entry target to allow mouse movement we force interaction --- .../plugins/microbot/util/mouse/Mouse.java | 8 +++--- .../microbot/util/mouse/VirtualMouse.java | 27 ++++++++++++++++++- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/Mouse.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/Mouse.java index dd035f57c3..147d4ee1ea 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/Mouse.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/Mouse.java @@ -1,15 +1,15 @@ package net.runelite.client.plugins.microbot.util.mouse; import lombok.Getter; +import net.runelite.api.MenuEntry; import net.runelite.api.Point; import net.runelite.client.plugins.microbot.Microbot; +import net.runelite.client.plugins.microbot.util.math.Rs2Random; import javax.swing.*; import java.awt.*; import java.util.LinkedList; -import static net.runelite.client.plugins.microbot.util.math.Random.random; - @Getter public abstract class Mouse { private static final int POINT_LIFETIME = 12;// Maximum number of points to store @@ -41,7 +41,7 @@ public Canvas getCanvas() { } public int randomizeClick() { - return random(-10, 10); + return (int) Rs2Random.normalRange(-10, 10, 4); } @@ -50,6 +50,7 @@ public int randomizeClick() { public abstract void setLastMove(Point point); + public abstract Mouse click(int x, int y); public abstract Mouse click(double x, double y); @@ -61,6 +62,7 @@ public int randomizeClick() { public abstract Mouse click(Point point); public abstract Mouse click(Point point, boolean rightClick); + public abstract Mouse click(Point point, MenuEntry entry); public abstract Mouse click(); public abstract Mouse move(Point point); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/VirtualMouse.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/VirtualMouse.java index b532331ef0..34137e0e8b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/VirtualMouse.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/VirtualMouse.java @@ -1,5 +1,6 @@ package net.runelite.client.plugins.microbot.util.mouse; +import net.runelite.api.MenuEntry; import net.runelite.api.Point; import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings; @@ -50,7 +51,6 @@ private void handleClick(Point point, boolean rightClick) { clicked(point, rightClick ? MouseEvent.BUTTON3 : MouseEvent.BUTTON1); setLastClick(point); } - public Mouse click(Point point, boolean rightClick) { if (point == null) return this; @@ -68,6 +68,26 @@ public Mouse click(Point point, boolean rightClick) { return this; } + public Mouse click(Point point, boolean rightClick, MenuEntry entry) { + if (point == null) return this; + if (Rs2AntibanSettings.naturalMouse && (point.getX() > 1 && point.getY() > 1)) + Microbot.naturalMouse.moveTo(point.getX(), point.getY()); + + + // Target menu was set before mouse movement causing some unintended behavior + // This will set the target menu after the mouse movement is finished + Microbot.targetMenu = entry; + if (Microbot.getClient().isClientThread()) { + scheduledExecutorService.schedule(() -> { + handleClick(point, rightClick); + }, 0, TimeUnit.MILLISECONDS); + } else { + handleClick(point, rightClick); + } + + return this; + } + public Mouse click(int x, int y) { return click(new Point(x, y), false); } @@ -90,6 +110,11 @@ public Mouse click(Point point) { return click(point, false); } + @Override + public Mouse click(Point point, MenuEntry entry) { + return click(point, false, entry); + } + @Override public Mouse click() { return click(Microbot.getClient().getMouseCanvasPosition()); From af38d8a0f48a57f9499e1b8d97db4e9bc01e6d68 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sun, 11 Aug 2024 00:04:54 +0200 Subject: [PATCH 16/52] Cleanup and moved targetMenu --- .../client/plugins/microbot/Microbot.java | 55 +++++++------------ 1 file changed, 19 insertions(+), 36 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java index f3e64e2058..5bc6b4b441 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java @@ -23,7 +23,6 @@ import net.runelite.client.plugins.microbot.util.math.Random; import net.runelite.client.plugins.microbot.util.misc.Rs2UiHelper; import net.runelite.client.plugins.microbot.util.mouse.Mouse; -import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.NaturalMouse; import net.runelite.client.plugins.timers.GameTimer; import net.runelite.client.plugins.timers.TimersPlugin; import net.runelite.client.ui.overlay.infobox.InfoBox; @@ -50,7 +49,18 @@ import static net.runelite.client.plugins.microbot.util.Global.sleep; public class Microbot { + private static final ScheduledExecutorService xpSchedulor = Executors.newSingleThreadScheduledExecutor(); + @Getter + private static final SpecialAttackConfigs specialAttackConfigs = new SpecialAttackConfigs(); public static MenuEntry targetMenu; + @Inject + @Named("microbot.storage") + public static String storageUrl; + public static boolean debug = false; + public static boolean isGainingExp = false; + public static boolean pauseAllScripts = false; + public static String status = "IDLE"; + public static boolean enableAutoRunOn = true; @Getter @Setter private static Mouse mouse; @@ -99,24 +109,8 @@ public class Microbot { @Getter @Setter private static ChatMessageManager chatMessageManager; - - @Inject - @Named("microbot.storage") public static String storageUrl; - - - public static boolean debug = false; - - public static boolean isGainingExp = false; - public static boolean pauseAllScripts = false; - public static String status = "IDLE"; - - public static boolean enableAutoRunOn = true; - - private static final ScheduledExecutorService xpSchedulor = Executors.newSingleThreadScheduledExecutor(); private static ScheduledFuture xpSchedulorFuture; private static net.runelite.api.World quickHopTargetWorld; - @Getter - private static final SpecialAttackConfigs specialAttackConfigs = new SpecialAttackConfigs(); @Deprecated(since = "Use isMoving", forRemoval = true) public static boolean isWalking() { @@ -256,12 +250,12 @@ public static Point calculateClickingPoint(Rectangle rect) { } public static void doInvoke(MenuEntry entry, Rectangle rectangle) { - targetMenu = entry; + try { if (Rs2UiHelper.isRectangleWithinViewport(rectangle)) { - click(rectangle); + click(rectangle, entry); } else { - click(new Rectangle(1, 1)); + click(new Rectangle(1, 1), entry); } } catch (ArrayIndexOutOfBoundsException e) { e.printStackTrace(); @@ -275,26 +269,15 @@ public static void drag(Rectangle start, Rectangle end) { Point startPoint = Rs2UiHelper.getClickingPoint(start, true); Point endPoint = Rs2UiHelper.getClickingPoint(end, true); mouse.drag(startPoint, endPoint); + if (!Microbot.getClient().isClientThread()) { + sleep(50, 80); + } } - public static void click(Rectangle rectangle) { + public static void click(Rectangle rectangle, MenuEntry entry) { Point point = Rs2UiHelper.getClickingPoint(rectangle, true); -// if (client.isStretchedEnabled()) { -// Dimension stretched = client.getStretchedDimensions(); -// Dimension real = client.getRealDimensions(); -// double width = (double) stretched.width / real.getWidth(); -// double height = (double) stretched.height / real.getHeight(); -// point = new Point((int) ((double) point.getX() * width), (int) ((double) point.getY() * height)); -// } -// mouseEvent(504, point); -// mouseEvent(505, point); -// mouseEvent(503, point); -// mouseEvent(501, point); -// mouseEvent(502, point); -// mouseEvent(500, point); - - mouse.click(point); + mouse.click(point, entry); if (!Microbot.getClient().isClientThread()) { From 3ae4f84dd5e79dee75acd524aa3bb905c831022e Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sun, 11 Aug 2024 00:06:38 +0200 Subject: [PATCH 17/52] Smoother & more human-like method when deciding click point --- .../plugins/microbot/util/math/Rs2Random.java | 174 ++++++++++++++++++ .../microbot/util/misc/Rs2UiHelper.java | 39 +--- 2 files changed, 182 insertions(+), 31 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/math/Rs2Random.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/math/Rs2Random.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/math/Rs2Random.java new file mode 100644 index 0000000000..de43265506 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/math/Rs2Random.java @@ -0,0 +1,174 @@ +package net.runelite.client.plugins.microbot.util.math; + +import net.runelite.api.Point; +import net.runelite.client.plugins.microbot.util.Global; + +import java.awt.*; +import java.util.Random; + +public class Rs2Random { + + private static final double GAUSS_CUTOFF = 4.0; + private static final Random RANDOM = new Random(); + + // Non-zero random + public static double nzRandom() { + return Math.max(RANDOM.nextDouble(), 1.0e-320); + } + + // Generates a random gaussian/normal number + public static double gaussRand(double mean, double dev) { + double len = dev * Math.sqrt(-2 * Math.log(nzRandom())); + return mean + len * Math.cos(2 * Math.PI * RANDOM.nextDouble()); + } + + // Generates a truncated gaussian/normal number within the given range + public static double truncatedGauss(double left, double right, double cutoff) { + if (cutoff <= 0) { + cutoff = GAUSS_CUTOFF; + } + + double result; + do { + result = Math.abs(Math.sqrt(-2 * Math.log(nzRandom())) * Math.cos(2 * Math.PI * RANDOM.nextDouble())); + } while (result >= cutoff); + + return result / cutoff * (right - left) + left; + } + + public static long truncatedGauss(long left, long right, double cutoff) { + return Math.round(truncatedGauss((double) left, (double) right, cutoff)); + } + + // Random skewed distribution generation + public static double skewedRand(double mode, double lo, double hi, double cutoff) { + if (cutoff <= 0) { + cutoff = GAUSS_CUTOFF; + } + + double top = lo; + if (RANDOM.nextDouble() * (hi - lo) > mode - lo) { + top = hi; + } + + double result; + do { + result = Math.abs(Math.sqrt(-2 * Math.log(nzRandom())) * Math.cos(2 * Math.PI * RANDOM.nextDouble())); + } while (result >= cutoff); + + return result / cutoff * (top - mode) + mode; + } + + public static long skewedRand(long mode, long lo, long hi, double cutoff) { + return Math.round(skewedRand((double) mode, (double) lo, (double) hi, cutoff)); + } + + // Generates a random float in the given range, weighted towards the mean + public static double normalRange(double min, double max, double cutoff) { + if (cutoff <= 0) { + cutoff = GAUSS_CUTOFF; + } + + switch (RANDOM.nextInt(2)) { + case 0: + return (max + min) / 2.0 + truncatedGauss(0, (max - min) / 2, cutoff); + case 1: + return (max + min) / 2.0 - truncatedGauss(0, (max - min) / 2, cutoff); + default: + throw new IllegalStateException("Unexpected value: " + RANDOM.nextInt(2)); + } + } + + public static long normalRange(long min, long max, double cutoff) { + if (cutoff <= 0) { + cutoff = GAUSS_CUTOFF; + } + + switch (RANDOM.nextInt(2)) { + case 0: + return Math.round((max + min) / 2.0 + truncatedGauss(0, (max - min) / 2, cutoff)); + case 1: + return Math.round((max + min) / 2.0 - truncatedGauss(0, (max - min) / 2, cutoff)); + default: + throw new IllegalStateException("Unexpected value: " + RANDOM.nextInt(2)); + } + } + + // Generates a random point weighted around Mean with a max distance from Mean defined by MaxRad + public static Point randomPoint(Point mean, int maxRad, double cutoff) { + int x = (int) normalRange(mean.getX() - maxRad, mean.getX() + maxRad, cutoff); + int y = (int) normalRange(mean.getY() - maxRad, mean.getY() + maxRad, cutoff); + return new Point(x, y); + } + + // Generates a random point within the bounds of the given rectangle, weighted towards the middle + public static Point randomPoint(Rectangle rect, double cutoff) { + double x1 = rect.getX(); + double y1 = rect.getY(); + double x2 = rect.getX() + rect.getWidth(); + double y2 = rect.getY() + rect.getHeight(); + double a = Math.atan2(rect.getHeight(), rect.getWidth()); + + int x = (int) normalRange(x1 + 1, x2 - 1, cutoff); + int y = (int) normalRange(y1 + 1, y2 - 1, cutoff); + return rotatePoint(new Point(x, y), a, (x2 + x1) / 2 + RANDOM.nextDouble() - 0.5, (y2 + y1) / 2 + RANDOM.nextDouble() - 0.5); + } + + // Generates a random point in the bounds of the given box, the point generated is skewed towards From-point + public static Point randomPointEx(Point from, Rectangle rect, double force) { + Point p = from; + p = new Point(Math.min(Math.max(p.getX(), (int) rect.getX()), (int) (rect.getX() + rect.getWidth())), Math.min(Math.max(p.getY(), (int) rect.getY()), (int) (rect.getY() + rect.getHeight()))); + + Point c = new Point((int) (rect.getX() + rect.getWidth() / 2), (int) (rect.getY() + rect.getHeight() / 2)); + double r = Math.hypot(p.getX() - c.getX(), p.getY() - c.getY()) * force; + double x = Math.atan2(c.getY() - p.getY(), c.getX() - p.getX()); + p = new Point((int) (p.getX() + Math.round(Math.cos(x) * r)), (int) (p.getY() + Math.round(Math.sin(x) * r))); + + int resultX = (int) skewedRand(p.getX(), (int) rect.getX(), (int) (rect.getX() + rect.getWidth()), GAUSS_CUTOFF); + int resultY = (int) skewedRand(p.getY(), (int) rect.getY(), (int) (rect.getY() + rect.getHeight()), GAUSS_CUTOFF); + return new Point(resultX, resultY); + } + + // Dice function: Generates a random number and returns true if within the chance percentage + public static boolean dice(double chancePercent) { + return RANDOM.nextDouble() < chancePercent / 100; + } + + // Wait function weighted towards the mean of Min and Max + public static void wait(double min, double max, EWaitDir weight) { + switch (weight) { + case wdLeft: + systemWait(Math.round(truncatedGauss(min, max, 0))); + break; + case wdMean: + systemWait(Math.round(normalRange(min, max, 0))); + break; + case wdRight: + systemWait(Math.round(truncatedGauss(max, min, 0))); + break; + } + } + + // WaitEx function: waits with regular Gaussian randomness + public static void waitEx(double mean, double dev) { + wait(Math.abs(Math.round(gaussRand(mean, dev))), 0, EWaitDir.wdMean); + } + + private static void systemWait(long time) { + Global.sleep((int) time); + } + + private static Point rotatePoint(Point point, double angle, double originX, double originY) { + double sin = Math.sin(angle); + double cos = Math.cos(angle); + double dx = point.getX() - originX; + double dy = point.getY() - originY; + int newX = (int) (cos * dx - sin * dy + originX); + int newY = (int) (sin * dx + cos * dy + originY); + return new Point(newX, newY); + } + + enum EWaitDir { + wdLeft, wdMean, wdRight + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java index 6d5fab51d9..78e6a06f7a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java @@ -6,10 +6,11 @@ import net.runelite.api.TileObject; import net.runelite.api.coords.LocalPoint; import net.runelite.client.plugins.microbot.Microbot; +import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings; +import net.runelite.client.plugins.microbot.util.math.Rs2Random; import java.awt.*; import java.util.Objects; -import java.util.Random; public class Rs2UiHelper { public static boolean isRectangleWithinViewport(Rectangle rectangle) { @@ -26,36 +27,12 @@ public static Point getClickingPoint(Rectangle rectangle, boolean randomize) { if (rectangle.getX() == 1 && rectangle.getY() == 1) return new Point(1, 1); if (rectangle.getX() == 0 && rectangle.getY() == 0) return new Point(1, 1); //check if mouse is already within the rectangle and return current position - java.awt.Point mousePos = Microbot.getMouse().getMousePosition(); - if (rectangle.contains(mousePos)) return new Point(mousePos.x, mousePos.y); - - if (!randomize) return new Point((int) rectangle.getCenterX(), (int) rectangle.getCenterY()); - Random random = new Random(); - // Calculate mean and standard deviation for the normal distribution - double meanX = rectangle.getX() + rectangle.getWidth() / 2.0; - double meanY = rectangle.getY() + rectangle.getHeight() / 2.0; - double stddevX = rectangle.getWidth() / 6.0; - double stddevY = rectangle.getHeight() / 6.0; - - // Generate normally distributed random values - double normalX = random.nextGaussian() * stddevX + meanX; - double normalY = random.nextGaussian() * stddevY + meanY; - - // Add jitter with a small uniformly distributed random offset - double jitterRangeX = rectangle.getWidth() / 10.0; // Adjust the range as needed - double jitterRangeY = rectangle.getHeight() / 10.0; // Adjust the range as needed - double jitterX = random.nextDouble() * jitterRangeX - jitterRangeX / 2.0; - double jitterY = random.nextDouble() * jitterRangeY - jitterRangeY / 2.0; - - // Apply jitter to the normally distributed values - normalX += jitterX; - normalY += jitterY; - - // Clamp the values to ensure they lie within the rectangle - int x = (int) Math.max(rectangle.getX(), Math.min(normalX, rectangle.getX() + rectangle.getWidth() - 1)); - int y = (int) Math.max(rectangle.getY(), Math.min(normalY, rectangle.getY() + rectangle.getHeight() - 1)); - - return new Point(x, y); + if (Rs2AntibanSettings.naturalMouse) { + java.awt.Point mousePos = Microbot.getMouse().getMousePosition(); + if (rectangle.contains(mousePos)) return new Point(mousePos.x, mousePos.y); + else return Rs2Random.randomPointEx(new Point(mousePos.x, mousePos.y), rectangle, 0.3); + } else + return Rs2Random.randomPointEx(Microbot.getMouse().getLastClick(), rectangle, 0.3); } public static Rectangle getNpcClickbox(NPC npc) { From 01a34caedfcd3120a347972354375dc56356c5f7 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sun, 11 Aug 2024 00:12:11 +0200 Subject: [PATCH 18/52] fine tuning --- .../client/plugins/microbot/util/misc/Rs2UiHelper.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java index 78e6a06f7a..53fb1ebdc1 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java @@ -30,9 +30,9 @@ public static Point getClickingPoint(Rectangle rectangle, boolean randomize) { if (Rs2AntibanSettings.naturalMouse) { java.awt.Point mousePos = Microbot.getMouse().getMousePosition(); if (rectangle.contains(mousePos)) return new Point(mousePos.x, mousePos.y); - else return Rs2Random.randomPointEx(new Point(mousePos.x, mousePos.y), rectangle, 0.3); + else return Rs2Random.randomPointEx(new Point(mousePos.x, mousePos.y), rectangle, 0.5); } else - return Rs2Random.randomPointEx(Microbot.getMouse().getLastClick(), rectangle, 0.3); + return Rs2Random.randomPointEx(Microbot.getMouse().getLastClick(), rectangle, 0.5); } public static Rectangle getNpcClickbox(NPC npc) { From 1ccc977d2bec8f5829a2ad616fed5332fc7df6a3 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sun, 11 Aug 2024 00:15:56 +0200 Subject: [PATCH 19/52] Added Natural mouse to main class --- .../java/net/runelite/client/plugins/microbot/Microbot.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java index 5bc6b4b441..b8f5b712e8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java @@ -23,6 +23,7 @@ import net.runelite.client.plugins.microbot.util.math.Random; import net.runelite.client.plugins.microbot.util.misc.Rs2UiHelper; import net.runelite.client.plugins.microbot.util.mouse.Mouse; +import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.NaturalMouse; import net.runelite.client.plugins.timers.GameTimer; import net.runelite.client.plugins.timers.TimersPlugin; import net.runelite.client.ui.overlay.infobox.InfoBox; @@ -63,6 +64,9 @@ public class Microbot { public static boolean enableAutoRunOn = true; @Getter @Setter + public static NaturalMouse naturalMouse; + @Getter + @Setter private static Mouse mouse; @Getter @Setter From 5bf73701de3d602d102769e0c11f20b593bf587a Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Thu, 15 Aug 2024 22:26:01 +0200 Subject: [PATCH 20/52] devtools update --- .../devtools/MicrobotClickOverlay.java | 21 +++++++++++++++---- .../devtools/MicrobotMouseOverlay.java | 3 --- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/MicrobotClickOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/MicrobotClickOverlay.java index bf314ce1b3..0847b65880 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/MicrobotClickOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/MicrobotClickOverlay.java @@ -29,10 +29,23 @@ public Dimension render(Graphics2D g) { g.setFont(new Font("Tahoma", Font.BOLD, 18)); - OverlayUtil.renderTextLocation(g, new net.runelite.api.Point(Microbot.getMouse().getLastClick().getX() - (g.getFont().getSize() / 3), - Microbot.getMouse().getLastClick().getY() + (g.getFont().getSize() / 3)), "X", Color.WHITE); - OverlayUtil.renderTextLocation(g, new net.runelite.api.Point(Microbot.getMouse().getLastClick2().getX() - (g.getFont().getSize() / 3), - Microbot.getMouse().getLastClick2().getY() + (g.getFont().getSize() / 3)), "X", Color.GREEN); + // Get the FontMetrics for the current font + FontMetrics metrics = g.getFontMetrics(g.getFont()); + + // Get the width and height of the character + int charWidth = metrics.stringWidth("X"); + int charHeight = metrics.getAscent(); // ascent gives the height of the character above the baseline + + int x = Microbot.getMouse().getLastClick().getX() - (charWidth / 2); + int y = Microbot.getMouse().getLastClick().getY() + (charHeight / 2); + + OverlayUtil.renderTextLocation(g, new net.runelite.api.Point(x, y), "X", Color.WHITE); + + x = Microbot.getMouse().getLastClick2().getX() - (charWidth / 2); + y = Microbot.getMouse().getLastClick2().getY() + (charHeight / 2); + + OverlayUtil.renderTextLocation(g, new net.runelite.api.Point(x, y), "X", Color.GREEN); + } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/MicrobotMouseOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/MicrobotMouseOverlay.java index b67c283323..119f777ebc 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/MicrobotMouseOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/MicrobotMouseOverlay.java @@ -27,9 +27,6 @@ public class MicrobotMouseOverlay extends Overlay { @Override public Dimension render(Graphics2D g) { if (plugin.getMouseMovement().isActive()) { - if (Microbot.getClient().getCanvas().getCursor().getType() != Cursor.CROSSHAIR_CURSOR) { - Microbot.getClient().getCanvas().setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR)); - } if (!Microbot.getMouse().getTimer().isRunning()) { Microbot.getMouse().getTimer().start(); } From ef0b3955eced56f7e9c738cd4b01aa1c274196a7 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Thu, 15 Aug 2024 22:28:50 +0200 Subject: [PATCH 21/52] MenuEntry can include source object/actor --- .../microbot/util/menu/NewMenuEntry.java | 55 ++++++++++++++----- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/menu/NewMenuEntry.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/menu/NewMenuEntry.java index 71c785ef99..0157399bbf 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/menu/NewMenuEntry.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/menu/NewMenuEntry.java @@ -16,6 +16,8 @@ public class NewMenuEntry implements MenuEntry { private int param1; private boolean forceLeftClick; private int itemId; + private Actor actor; + private TileObject gameObject; public NewMenuEntry(int param0, int param1, int opcode, int identifier, int itemId, String target) { this.option = "Use"; @@ -28,6 +30,30 @@ public NewMenuEntry(int param0, int param1, int opcode, int identifier, int item this.itemId = itemId; } + public NewMenuEntry(int param0, int param1, int opcode, int identifier, int itemId, String target, Actor actor) { + this.option = "Use"; + this.target = target; + this.identifier = identifier; + this.type = MenuAction.of(opcode); + this.param0 = param0; + this.param1 = param1; + this.forceLeftClick = true; + this.itemId = itemId; + this.actor = actor; + } + + public NewMenuEntry(int param0, int param1, int opcode, int identifier, int itemId, String target, TileObject gameObject) { + this.option = "Use"; + this.target = target; + this.identifier = identifier; + this.type = MenuAction.of(opcode); + this.param0 = param0; + this.param1 = param1; + this.forceLeftClick = true; + this.itemId = itemId; + this.gameObject = gameObject; + } + public NewMenuEntry(String option, String target, int identifier, MenuAction type, int param0, int param1, boolean forceLeftClick) { this.option = option; this.target = target; @@ -49,6 +75,9 @@ public NewMenuEntry(String option, int param0, int param1, int opcode, int ident this.itemId = itemId; } + public NewMenuEntry() { + } + public String getOption() { return this.option; } @@ -138,6 +167,7 @@ public MenuEntry onClick(Consumer callback) { public Consumer onClick() { return null; } + public MenuEntry getParent() { return this; } @@ -167,17 +197,23 @@ public Widget getWidget() { @Nullable public NPC getNpc() { - return null; + return actor instanceof NPC ? (NPC) actor : null; + } @Nullable public Player getPlayer() { - return null; + return actor instanceof Player ? (Player) actor : null; } @Nullable public Actor getActor() { - return null; + return actor; + } + + @Nullable + public TileObject getGameObject() { + return gameObject; } @org.jetbrains.annotations.Nullable @@ -238,14 +274,8 @@ public boolean equals(Object o) { Object this$type = this.getType(); Object other$type = other.getType(); if (this$type == null) { - if (other$type != null) { - return false; - } - } else if (!this$type.equals(other$type)) { - return false; - } - - return true; + return other$type == null; + } else return this$type.equals(other$type); } } } @@ -274,7 +304,4 @@ public String toString() { String var10000 = this.getOption(); return "NewMenuEntry(option=" + var10000 + ", target=" + this.getTarget() + ", identifier=" + this.getIdentifier() + ", type=" + this.getType() + ", param0=" + this.getParam0() + ", param1=" + this.getParam1() + ", forceLeftClick=" + this.isForceLeftClick() + ")"; } - - public NewMenuEntry() { - } } From 0fda4daf89535603820283aad7a41a6b5177c8bb Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Thu, 15 Aug 2024 22:29:47 +0200 Subject: [PATCH 22/52] updated ui helper --- .../microbot/util/misc/Rs2UiHelper.java | 29 +++++++++++++++---- .../plugins/microbot/util/misc/TimeUtils.java | 21 ++++++++++++++ 2 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/TimeUtils.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java index 53fb1ebdc1..9aa1c93ccb 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java @@ -1,6 +1,6 @@ package net.runelite.client.plugins.microbot.util.misc; -import net.runelite.api.NPC; +import net.runelite.api.Actor; import net.runelite.api.Perspective; import net.runelite.api.Point; import net.runelite.api.TileObject; @@ -8,6 +8,7 @@ import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings; import net.runelite.client.plugins.microbot.util.math.Rs2Random; +import net.runelite.client.plugins.microbot.util.menu.NewMenuEntry; import java.awt.*; import java.util.Objects; @@ -29,21 +30,28 @@ public static Point getClickingPoint(Rectangle rectangle, boolean randomize) { //check if mouse is already within the rectangle and return current position if (Rs2AntibanSettings.naturalMouse) { java.awt.Point mousePos = Microbot.getMouse().getMousePosition(); - if (rectangle.contains(mousePos)) return new Point(mousePos.x, mousePos.y); + if (isMouseWithinRectangle(rectangle)) return new Point(mousePos.x, mousePos.y); else return Rs2Random.randomPointEx(new Point(mousePos.x, mousePos.y), rectangle, 0.5); } else return Rs2Random.randomPointEx(Microbot.getMouse().getLastClick(), rectangle, 0.5); } - public static Rectangle getNpcClickbox(NPC npc) { + //check if mouse is already within the rectangle + public static boolean isMouseWithinRectangle(Rectangle rectangle) { + java.awt.Point mousePos = Microbot.getMouse().getMousePosition(); + return rectangle.contains(mousePos); + } - LocalPoint lp = npc.getLocalLocation(); + public static Rectangle getActorClickbox(Actor actor) { + LocalPoint lp = actor.getLocalLocation(); if (lp == null) { Microbot.log("LocalPoint is null"); return new Rectangle(1, 1); } - Shape clickbox = Microbot.getClientThread().runOnClientThread(() -> Perspective.getClickbox(Microbot.getClient(), npc.getModel(), npc.getCurrentOrientation(), lp.getX(), lp.getY(), - Perspective.getTileHeight(Microbot.getClient(), lp, npc.getWorldLocation().getPlane()))); + + + Shape clickbox = Microbot.getClientThread().runOnClientThread(() -> Perspective.getClickbox(Microbot.getClient(), actor.getModel(), actor.getCurrentOrientation(), lp.getX(), lp.getY(), + Perspective.getTileHeight(Microbot.getClient(), lp, actor.getWorldLocation().getPlane()))); //check if any of the values are negative and return a new rectangle with positive values @@ -70,5 +78,14 @@ public static Rectangle getObjectClickbox(TileObject object) { return new Rectangle(clickbox.getBounds()); } + // check if a menu entry is a actor + public static boolean hasActor(NewMenuEntry entry) { + return entry.getActor() != null; + } + + // check if a menu entry is a game object + public static boolean isGameObject(NewMenuEntry entry) { + return entry.getGameObject() != null; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/TimeUtils.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/TimeUtils.java new file mode 100644 index 0000000000..8cd5032b67 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/TimeUtils.java @@ -0,0 +1,21 @@ +package net.runelite.client.plugins.microbot.util.misc; + +import java.time.Duration; +import java.time.Instant; + +public class TimeUtils { + /** + * Get formatted duration between two instants + * + * @param start + * @param finish + * @return duration as string formatted to d:mm:ss + */ + public static String getFormattedDurationBetween(Instant start, Instant finish) { + Duration duration = Duration.between(start, finish); + return String.format("%d:%02d:%02d", + duration.toHours(), + duration.toMinutesPart(), + duration.toSecondsPart()); + } +} \ No newline at end of file From 303181983a89336bcf7d3f38b82ca454ffdc62e5 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Thu, 15 Aug 2024 22:30:55 +0200 Subject: [PATCH 23/52] updated Rs2npc for antiban recs --- .../net/runelite/client/plugins/microbot/util/npc/Rs2Npc.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/npc/Rs2Npc.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/npc/Rs2Npc.java index a89c5c3499..7b0e157fd2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/npc/Rs2Npc.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/npc/Rs2Npc.java @@ -194,7 +194,7 @@ public static boolean interact(NPC npc, String action) { MenuAction menuAction = getMenuAction(index); if (menuAction != null) { - Microbot.doInvoke(new NewMenuEntry(0, 0, menuAction.getId(), npc.getIndex(), -1, npc.getName()), Rs2UiHelper.getNpcClickbox(npc)); + Microbot.doInvoke(new NewMenuEntry(0, 0, menuAction.getId(), npc.getIndex(), -1, npc.getName(), npc), Rs2UiHelper.getActorClickbox(npc)); } } catch (Exception ex) { From b22690846467bd4c093edd4a316dedf77b15f3ba Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Thu, 15 Aug 2024 22:32:22 +0200 Subject: [PATCH 24/52] Minor change to moveItemToSlot method --- .../client/plugins/microbot/util/inventory/Rs2Inventory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java index 0bb7c05ea8..5bcab4c516 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java @@ -2032,7 +2032,7 @@ public static boolean waitForInventoryChanges() { public static boolean moveItemToSlot(Rs2Item item, int slot) { if (item == null) return false; if (slot < 0 || slot >= CAPACITY) return false; - if (item.slot == slot) return true; + if (item.slot == slot) return false; Widget inventory = getInventory(); if (inventory == null) return false; From d2b72f22a420e5baaec37394e613823644acdfc6 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Thu, 15 Aug 2024 22:33:03 +0200 Subject: [PATCH 25/52] updated Rs2GameObject for antiban recs --- .../plugins/microbot/util/gameobject/Rs2GameObject.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java index 583807de2c..f58bcf4713 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java @@ -9,16 +9,15 @@ import net.runelite.client.plugins.microbot.util.camera.Rs2Camera; import net.runelite.client.plugins.microbot.util.keyboard.Rs2Keyboard; import net.runelite.client.plugins.microbot.util.menu.NewMenuEntry; +import net.runelite.client.plugins.microbot.util.misc.Rs2UiHelper; import net.runelite.client.plugins.microbot.util.player.Rs2Player; import net.runelite.client.plugins.microbot.util.reflection.Rs2Reflection; import net.runelite.client.plugins.microbot.util.tile.Rs2Tile; import net.runelite.client.plugins.microbot.util.walker.Rs2Walker; import javax.annotation.Nullable; -import java.awt.*; import java.awt.event.KeyEvent; import java.lang.reflect.Field; -import java.util.List; import java.util.*; import java.util.stream.Collectors; @@ -1087,7 +1086,7 @@ private static boolean clickObject(TileObject object, String action) { } - Microbot.doInvoke(new NewMenuEntry(param0, param1, menuAction.getId(), object.getId(), -1, objComp.getName()), new Rectangle(object.getCanvasTilePoly().getBounds())); + Microbot.doInvoke(new NewMenuEntry(param0, param1, menuAction.getId(), object.getId(), -1, objComp.getName(), object), Rs2UiHelper.getObjectClickbox(object)); //Rs2Reflection.invokeMenu(param0, param1, menuAction.getId(), object.getId(),-1, "", "", -1, -1); From ce45fe63581ef1dfd3b21469d2fafa988c8645f6 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Thu, 15 Aug 2024 22:35:05 +0200 Subject: [PATCH 26/52] Additional antiban settings and tweaks. --- .../microbot/util/antiban/AntibanPlugin.java | 10 +- .../util/antiban/AntibanPluginPanel.java | 16 +- .../util/antiban/AntibanSetupTemplates.java | 51 ++++-- .../microbot/util/antiban/Rs2Antiban.java | 160 ++---------------- .../util/antiban/Rs2AntibanSettings.java | 13 +- .../plugins/microbot/util/math/Rs2Random.java | 2 +- 6 files changed, 79 insertions(+), 173 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java index 00e95920b1..7d158d13e6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java @@ -96,7 +96,7 @@ public static void performActionBreak() { } } else { Rs2AntibanSettings.actionCooldownActive = false; - //Microbot.pauseAllScripts = false; + Microbot.pauseAllScripts = false; } } } @@ -204,8 +204,7 @@ public void onGameTick(GameTick event) { @Subscribe public void onStatChanged(StatChanged statChanged) { - if (!Rs2AntibanSettings.antibanEnabled || - (!Rs2AntibanSettings.dynamicIntensity && !Rs2AntibanSettings.dynamicActivity)) { + if (!Rs2AntibanSettings.antibanEnabled) { return; } @@ -214,7 +213,7 @@ public void onStatChanged(StatChanged statChanged) { final Integer previous = skillExp.put(skill, exp); if (lastSkillChanged != null && lastSkillChanged.equals(skill)) { - if (Rs2AntibanSettings.contextualVariability && !Rs2AntibanSettings.actionCooldownActive) { + if (Rs2AntibanSettings.universalAntiban && !Rs2AntibanSettings.actionCooldownActive) { Rs2Antiban.actionCooldown(); } return; @@ -236,8 +235,7 @@ private void updateAntibanSettings(Skill skill) { if (activity != null && Rs2AntibanSettings.dynamicActivity) { Rs2Antiban.setActivity(activity); Microbot.log("Activity changed, new activity: " + activity); - if (Rs2AntibanSettings.contextualVariability) { - applyContextualVariabilitySetup(); + if (Rs2AntibanSettings.universalAntiban) { Rs2Antiban.actionCooldown(); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPluginPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPluginPanel.java index 03f44e6ae6..06cfc4db22 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPluginPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPluginPanel.java @@ -11,6 +11,7 @@ public class AntibanPluginPanel extends PluginPanel { private final JCheckBox isActionCooldownActive = new JCheckBox("Action Cooldown Active"); private final JCheckBox isMicroBreakActive = new JCheckBox("Micro Break Active"); private final JCheckBox isEnabled = new JCheckBox("Enabled"); + private final JCheckBox universalAntiban = new JCheckBox("Universal Antiban"); private final JCheckBox usePlayStyle = new JCheckBox("Use Play Style"); private final JCheckBox useRandomIntervals = new JCheckBox("Use Random Intervals"); private final JCheckBox simulateFatigue = new JCheckBox("Simulate Fatigue"); @@ -22,6 +23,7 @@ public class AntibanPluginPanel extends PluginPanel { private final JCheckBox simulateMistakes = new JCheckBox("Simulate Mistakes"); private final JCheckBox useNaturalMouse = new JCheckBox("Use Natural Mouse"); private final JCheckBox moveMouseOffScreen = new JCheckBox("Move Mouse Off Screen"); + private final JCheckBox moveMouseRandomly = new JCheckBox("Move Mouse Randomly"); private final JCheckBox useContextualVariability = new JCheckBox("Use Contextual Variability"); private final JCheckBox dynamicActivityIntensity = new JCheckBox("Dynamic Activity Intensity"); private final JCheckBox dynamicActivity = new JCheckBox("Dynamic Activity"); @@ -34,12 +36,14 @@ public class AntibanPluginPanel extends PluginPanel { private final JSlider actionCooldownChance = new JSlider(0, 100, (int) (Rs2AntibanSettings.actionCooldownChance * 100)); private final JSlider microBreakChance = new JSlider(0, 100, (int) (Rs2AntibanSettings.microBreakChance * 100)); private final JSlider timeout = new JSlider(0, 60, Rs2Antiban.getTIMEOUT()); + private final JSlider moveMouseRandomlyChance = new JSlider(0, 100, (int) (Rs2AntibanSettings.moveMouseRandomlyChance * 100)); private final JLabel microBreakDurationLowLabel = new JLabel("Micro Break Duration Low (min): " + Rs2AntibanSettings.microBreakDurationLow); private final JLabel microBreakDurationHighLabel = new JLabel("Micro Break Duration High (min): " + Rs2AntibanSettings.microBreakDurationHigh); private final JLabel actionCooldownChanceLabel = new JLabel("Action Cooldown Chance (%): " + (int) (Rs2AntibanSettings.actionCooldownChance * 100)); private final JLabel microBreakChanceLabel = new JLabel("Micro Break Chance (%): " + (int) (Rs2AntibanSettings.microBreakChance * 100)); private final JLabel timeoutLabel = new JLabel("Timeout (min): " + Rs2Antiban.getTIMEOUT()); + private final JLabel moveMouseRandomlyChanceLabel = new JLabel("Random Mouse Movement (%): " + (int) (Rs2AntibanSettings.moveMouseRandomlyChance * 100)); // Additional Info Panel private final JLabel playStyleLabel = new JLabel("Play Style: " + (Rs2Antiban.getPlayStyle() != null ? Rs2Antiban.getPlayStyle().getName() : "null")); @@ -103,7 +107,7 @@ private void addNavButton(JPanel navPanel, String tooltip, String iconPath, JPan private JPanel createGeneralSettingsPanel() { JPanel panel = createPanel("General Settings"); - addCheckboxesToPanel(panel, isEnabled, devDebug); + addCheckboxesToPanel(panel, isEnabled, universalAntiban, useContextualVariability, devDebug); return panel; } @@ -121,7 +125,8 @@ private JPanel createProfileSettingsPanel() { private JPanel createMouseSettingsPanel() { JPanel panel = createPanel("Mouse Settings"); - addCheckboxesToPanel(panel, useNaturalMouse, moveMouseOffScreen, useContextualVariability, simulateMistakes); + addCheckboxesToPanel(panel, useNaturalMouse, simulateMistakes, moveMouseOffScreen, moveMouseRandomly); + addSlidersToPanel(panel, moveMouseRandomlyChanceLabel, moveMouseRandomlyChance); return panel; } @@ -194,6 +199,7 @@ private void setupSliders() { setupSlider(actionCooldownChance, 20, 100, 10); setupSlider(microBreakChance, 20, 100, 10); setupSlider(timeout, 10, 60, 5); + setupSlider(moveMouseRandomlyChance, 20, 100, 10); } private void setupSlider(JSlider slider, int majorTickSpacing, int max, int minorTickSpacing) { @@ -209,6 +215,7 @@ private void setupListeners() { isActionCooldownActive.addActionListener(e -> Rs2AntibanSettings.actionCooldownActive = isActionCooldownActive.isSelected()); isMicroBreakActive.addActionListener(e -> Rs2AntibanSettings.microBreakActive = isMicroBreakActive.isSelected()); isEnabled.addActionListener(e -> Rs2AntibanSettings.antibanEnabled = isEnabled.isSelected()); + universalAntiban.addActionListener(e -> Rs2AntibanSettings.universalAntiban = universalAntiban.isSelected()); usePlayStyle.addActionListener(e -> Rs2AntibanSettings.usePlayStyle = usePlayStyle.isSelected()); useRandomIntervals.addActionListener(e -> Rs2AntibanSettings.randomIntervals = useRandomIntervals.isSelected()); simulateFatigue.addActionListener(e -> Rs2AntibanSettings.simulateFatigue = simulateFatigue.isSelected()); @@ -220,6 +227,7 @@ private void setupListeners() { simulateMistakes.addActionListener(e -> Rs2AntibanSettings.simulateMistakes = simulateMistakes.isSelected()); useNaturalMouse.addActionListener(e -> Rs2AntibanSettings.naturalMouse = useNaturalMouse.isSelected()); moveMouseOffScreen.addActionListener(e -> Rs2AntibanSettings.moveMouseOffScreen = moveMouseOffScreen.isSelected()); + moveMouseRandomly.addActionListener(e -> Rs2AntibanSettings.moveMouseRandomly = moveMouseRandomly.isSelected()); useContextualVariability.addActionListener(e -> Rs2AntibanSettings.contextualVariability = useContextualVariability.isSelected()); dynamicActivityIntensity.addActionListener(e -> Rs2AntibanSettings.dynamicIntensity = dynamicActivityIntensity.isSelected()); dynamicActivity.addActionListener(e -> Rs2AntibanSettings.dynamicActivity = dynamicActivity.isSelected()); @@ -247,6 +255,10 @@ private void setupListeners() { Rs2Antiban.setTIMEOUT(timeout.getValue()); timeoutLabel.setText("Timeout (min): " + timeout.getValue()); }); + moveMouseRandomlyChance.addChangeListener(e -> { + Rs2AntibanSettings.moveMouseRandomlyChance = moveMouseRandomlyChance.getValue() / 100.0; + moveMouseRandomlyChanceLabel.setText("Random Mouse Movement (%): " + moveMouseRandomlyChance.getValue()); + }); } public void loadSettings() { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanSetupTemplates.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanSetupTemplates.java index 96df7d0017..24f10e90f6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanSetupTemplates.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanSetupTemplates.java @@ -32,9 +32,10 @@ public void applyCombatSetup() { Rs2AntibanSettings.devDebug = false; Rs2AntibanSettings.takeMicroBreaks = true; Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.universalAntiban = false; Rs2AntibanSettings.microBreakDurationLow = 3; Rs2AntibanSettings.microBreakDurationHigh = 8; - Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.actionCooldownChance = 1.00; Rs2AntibanSettings.microBreakChance = 0.05; Rs2Antiban.setActivity(Activity.GENERAL_COMBAT); } @@ -62,9 +63,10 @@ public void applyRunecraftingSetup() { Rs2AntibanSettings.devDebug = false; Rs2AntibanSettings.takeMicroBreaks = true; Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.universalAntiban = false; Rs2AntibanSettings.microBreakDurationLow = 3; Rs2AntibanSettings.microBreakDurationHigh = 8; - Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.actionCooldownChance = 1.00; Rs2AntibanSettings.microBreakChance = 0.05; Rs2Antiban.setActivity(Activity.GENERAL_RUNECRAFT); } @@ -92,9 +94,10 @@ public void applyConstructionSetup() { Rs2AntibanSettings.devDebug = false; Rs2AntibanSettings.takeMicroBreaks = true; Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.universalAntiban = false; Rs2AntibanSettings.microBreakDurationLow = 3; Rs2AntibanSettings.microBreakDurationHigh = 8; - Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.actionCooldownChance = 1.00; Rs2AntibanSettings.microBreakChance = 0.05; Rs2Antiban.setActivity(Activity.GENERAL_CONSTRUCTION); } @@ -123,9 +126,10 @@ public void applyAgilitySetup() { Rs2AntibanSettings.devDebug = false; Rs2AntibanSettings.takeMicroBreaks = true; Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.universalAntiban = false; Rs2AntibanSettings.microBreakDurationLow = 3; Rs2AntibanSettings.microBreakDurationHigh = 8; - Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.actionCooldownChance = 1.00; Rs2AntibanSettings.microBreakChance = 0.05; Rs2Antiban.setActivity(Activity.GENERAL_AGILITY); } @@ -153,9 +157,10 @@ public void applyHerbloreSetup() { Rs2AntibanSettings.devDebug = false; Rs2AntibanSettings.takeMicroBreaks = true; Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.universalAntiban = false; Rs2AntibanSettings.microBreakDurationLow = 3; Rs2AntibanSettings.microBreakDurationHigh = 8; - Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.actionCooldownChance = 1.00; Rs2AntibanSettings.microBreakChance = 0.05; Rs2Antiban.setActivity(Activity.GENERAL_HERBLORE); } @@ -183,9 +188,10 @@ public void applyThievingSetup() { Rs2AntibanSettings.devDebug = false; Rs2AntibanSettings.takeMicroBreaks = true; Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.universalAntiban = false; Rs2AntibanSettings.microBreakDurationLow = 3; Rs2AntibanSettings.microBreakDurationHigh = 8; - Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.actionCooldownChance = 1.00; Rs2AntibanSettings.microBreakChance = 0.05; Rs2Antiban.setActivity(Activity.GENERAL_THIEVING); } @@ -213,9 +219,10 @@ public void applyCraftingSetup() { Rs2AntibanSettings.devDebug = false; Rs2AntibanSettings.takeMicroBreaks = true; Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.universalAntiban = false; Rs2AntibanSettings.microBreakDurationLow = 3; Rs2AntibanSettings.microBreakDurationHigh = 8; - Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.actionCooldownChance = 1.00; Rs2AntibanSettings.microBreakChance = 0.05; Rs2Antiban.setActivity(Activity.GENERAL_CRAFTING); } @@ -243,9 +250,10 @@ public void applyFletchingSetup() { Rs2AntibanSettings.devDebug = false; Rs2AntibanSettings.takeMicroBreaks = true; Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.universalAntiban = false; Rs2AntibanSettings.microBreakDurationLow = 3; Rs2AntibanSettings.microBreakDurationHigh = 8; - Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.actionCooldownChance = 1.00; Rs2AntibanSettings.microBreakChance = 0.05; Rs2Antiban.setActivity(Activity.GENERAL_FLETCHING); } @@ -277,9 +285,10 @@ public void applyHunterSetup() { Rs2AntibanSettings.devDebug = false; Rs2AntibanSettings.takeMicroBreaks = true; Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.universalAntiban = false; Rs2AntibanSettings.microBreakDurationLow = 3; Rs2AntibanSettings.microBreakDurationHigh = 8; - Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.actionCooldownChance = 1.00; Rs2AntibanSettings.microBreakChance = 0.05; Rs2Antiban.setActivity(Activity.GENERAL_HUNTER); } @@ -307,9 +316,10 @@ public void applyMiningSetup() { Rs2AntibanSettings.devDebug = false; Rs2AntibanSettings.takeMicroBreaks = true; Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.universalAntiban = false; Rs2AntibanSettings.microBreakDurationLow = 1; Rs2AntibanSettings.microBreakDurationHigh = 4; - Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.actionCooldownChance = 1.00; Rs2AntibanSettings.microBreakChance = 0.05; Rs2Antiban.setActivity(Activity.GENERAL_MINING); } @@ -337,9 +347,10 @@ public void applySmithingSetup() { Rs2AntibanSettings.devDebug = false; Rs2AntibanSettings.takeMicroBreaks = true; Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.universalAntiban = false; Rs2AntibanSettings.microBreakDurationLow = 3; Rs2AntibanSettings.microBreakDurationHigh = 8; - Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.actionCooldownChance = 1.00; Rs2AntibanSettings.microBreakChance = 0.05; Rs2Antiban.setActivity(Activity.GENERAL_SMITHING); } @@ -367,9 +378,10 @@ public void applyFishingSetup() { Rs2AntibanSettings.devDebug = false; Rs2AntibanSettings.takeMicroBreaks = true; Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.universalAntiban = false; Rs2AntibanSettings.microBreakDurationLow = 3; Rs2AntibanSettings.microBreakDurationHigh = 8; - Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.actionCooldownChance = 1.00; Rs2AntibanSettings.microBreakChance = 0.05; Rs2Antiban.setActivity(Activity.GENERAL_FISHING); } @@ -397,9 +409,10 @@ public void applyCookingSetup() { Rs2AntibanSettings.devDebug = false; Rs2AntibanSettings.takeMicroBreaks = true; Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.universalAntiban = false; Rs2AntibanSettings.microBreakDurationLow = 3; Rs2AntibanSettings.microBreakDurationHigh = 8; - Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.actionCooldownChance = 1.00; Rs2AntibanSettings.microBreakChance = 0.05; Rs2Antiban.setActivity(Activity.GENERAL_COOKING); } @@ -427,9 +440,10 @@ public void applyFiremakingSetup() { Rs2AntibanSettings.devDebug = false; Rs2AntibanSettings.takeMicroBreaks = true; Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.universalAntiban = false; Rs2AntibanSettings.microBreakDurationLow = 3; Rs2AntibanSettings.microBreakDurationHigh = 8; - Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.actionCooldownChance = 1.00; Rs2AntibanSettings.microBreakChance = 0.05; Rs2Antiban.setActivity(Activity.GENERAL_FIREMAKING); } @@ -457,9 +471,10 @@ public void applyWoodcuttingSetup() { Rs2AntibanSettings.devDebug = false; Rs2AntibanSettings.takeMicroBreaks = true; Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.universalAntiban = false; Rs2AntibanSettings.microBreakDurationLow = 3; Rs2AntibanSettings.microBreakDurationHigh = 8; - Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.actionCooldownChance = 1.00; Rs2AntibanSettings.microBreakChance = 0.05; Rs2Antiban.setActivity(Activity.GENERAL_WOODCUTTING); } @@ -487,9 +502,10 @@ public void applyFarmingSetup() { Rs2AntibanSettings.devDebug = false; Rs2AntibanSettings.takeMicroBreaks = true; Rs2AntibanSettings.playSchedule = true; + Rs2AntibanSettings.universalAntiban = false; Rs2AntibanSettings.microBreakDurationLow = 3; Rs2AntibanSettings.microBreakDurationHigh = 8; - Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.actionCooldownChance = 1.00; Rs2AntibanSettings.microBreakChance = 0.05; Rs2Antiban.setActivity(Activity.GENERAL_FARMING); } @@ -518,9 +534,10 @@ public void applyGeneralBasicSetup() { Rs2AntibanSettings.devDebug = false; Rs2AntibanSettings.takeMicroBreaks = false; Rs2AntibanSettings.playSchedule = false; + Rs2AntibanSettings.universalAntiban = false; Rs2AntibanSettings.microBreakDurationLow = 3; Rs2AntibanSettings.microBreakDurationHigh = 8; - Rs2AntibanSettings.actionCooldownChance = 0.05; + Rs2AntibanSettings.actionCooldownChance = 1.00; Rs2AntibanSettings.microBreakChance = 0.05; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2Antiban.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2Antiban.java index 495e62314d..7f0546e1cb 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2Antiban.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2Antiban.java @@ -12,6 +12,7 @@ import net.runelite.client.plugins.microbot.util.antiban.enums.Category; import net.runelite.client.plugins.microbot.util.antiban.enums.PlayStyle; import net.runelite.client.plugins.microbot.util.math.Random; +import net.runelite.client.plugins.microbot.util.math.Rs2Random; import net.runelite.client.plugins.microbot.util.player.Rs2Player; import net.runelite.client.ui.overlay.components.*; import net.runelite.client.util.ColorUtil; @@ -71,147 +72,6 @@ public class Rs2Antiban { @Setter private static PlayStyle playStyle; - /** - *

Basic Setup

- * This method sets up the basic configuration for the antiban system. - * It is used to enable the minimal functionality that works out of the box without any additional configuration. - * The basic setup utilizes natural mouse movements to create natural delays between actions - */ - public static void basicSetup() { - Rs2AntibanSettings.actionCooldownActive = false; - Rs2AntibanSettings.microBreakActive = false; - Rs2AntibanSettings.antibanEnabled = true; - Rs2AntibanSettings.usePlayStyle = false; - Rs2AntibanSettings.randomIntervals = false; - Rs2AntibanSettings.simulateFatigue = false; - Rs2AntibanSettings.simulateAttentionSpan = false; - Rs2AntibanSettings.behavioralVariability = false; - Rs2AntibanSettings.nonLinearIntervals = false; - Rs2AntibanSettings.profileSwitching = false; - Rs2AntibanSettings.timeOfDayAdjust = false; - Rs2AntibanSettings.simulateMistakes = false; - Rs2AntibanSettings.naturalMouse = true; - Rs2AntibanSettings.contextualVariability = false; - Rs2AntibanSettings.dynamicIntensity = true; - Rs2AntibanSettings.dynamicActivity = true; - Rs2AntibanSettings.devDebug = false; - Rs2AntibanSettings.actionCooldownChance = 0.1; - Rs2AntibanSettings.microBreakChance = 0.1; - Rs2AntibanSettings.microBreakDurationLow = 3; - Rs2AntibanSettings.microBreakDurationHigh = 15; - } - - /** - *

Basic Play Style Setup

- * This method sets up the basic configuration for the antiban system with play style enabled. - * Provides action cooldowns in the most basic and aggressive play style. - * To activate the action cooldown, call the actionCooldown() method after the desired action. - * - * @param activity The activity to be performed - * @see Rs2Antiban#actionCooldown() - */ - public static void basicPlayStyleSetup(Activity activity) { - Rs2AntibanSettings.actionCooldownActive = false; - Rs2AntibanSettings.microBreakActive = false; - Rs2AntibanSettings.antibanEnabled = true; - Rs2AntibanSettings.usePlayStyle = true; - Rs2AntibanSettings.randomIntervals = false; - Rs2AntibanSettings.simulateFatigue = false; - Rs2AntibanSettings.simulateAttentionSpan = false; - Rs2AntibanSettings.behavioralVariability = false; - Rs2AntibanSettings.nonLinearIntervals = false; - Rs2AntibanSettings.profileSwitching = false; - Rs2AntibanSettings.timeOfDayAdjust = false; - Rs2AntibanSettings.simulateMistakes = false; - Rs2AntibanSettings.naturalMouse = false; - Rs2AntibanSettings.contextualVariability = false; - Rs2AntibanSettings.dynamicIntensity = false; - Rs2AntibanSettings.dynamicActivity = false; - Rs2AntibanSettings.devDebug = false; - Rs2AntibanSettings.actionCooldownChance = 0.1; - Rs2AntibanSettings.microBreakChance = 0.1; - Rs2AntibanSettings.microBreakDurationLow = 3; - Rs2AntibanSettings.microBreakDurationHigh = 15; - setActivity(activity); - Rs2Antiban.playStyle = PlayStyle.EXTREME_AGGRESSIVE; - - } - - /** - *

Intermediate Play Style Setup

- * This method sets up the intermediate configuration for the antiban system with play style enabled. - * Provides action cooldowns based on the activity intensity and play style. - * The action cooldown is randomized within the primary and secondary tick intervals in the current play style. - * To activate the action cooldown, call the actionCooldown() method after the desired action. - * - * @param activity The activity to be performed - * @see Rs2Antiban#actionCooldown() - */ - - // Advanced play style setup: maximal functionality with play style enabled - public static void intermediatePlayStyleSetup(Activity activity) { - Rs2AntibanSettings.actionCooldownActive = false; - Rs2AntibanSettings.microBreakActive = false; - Rs2AntibanSettings.antibanEnabled = true; - Rs2AntibanSettings.usePlayStyle = true; - Rs2AntibanSettings.randomIntervals = false; - Rs2AntibanSettings.simulateFatigue = false; - Rs2AntibanSettings.simulateAttentionSpan = false; - Rs2AntibanSettings.behavioralVariability = true; - Rs2AntibanSettings.nonLinearIntervals = false; - Rs2AntibanSettings.profileSwitching = false; //TODO: Implement this - Rs2AntibanSettings.timeOfDayAdjust = false; //TODO: Implement this - Rs2AntibanSettings.simulateMistakes = false; //Handled by the natural mouse - Rs2AntibanSettings.naturalMouse = true; - Rs2AntibanSettings.contextualVariability = false; //TODO: Implement this - Rs2AntibanSettings.dynamicIntensity = false; - Rs2AntibanSettings.dynamicActivity = false; - Rs2AntibanSettings.devDebug = true; - Rs2AntibanSettings.actionCooldownChance = 0.1; - Rs2AntibanSettings.microBreakChance = 0.1; - Rs2AntibanSettings.microBreakDurationLow = 1; - Rs2AntibanSettings.microBreakDurationHigh = 5; - setActivity(activity); - } - - /** - *

Advanced Play Style Setup

- * This method sets up the advanced configuration for the antiban system with play style enabled. - * Provides action cooldowns based on the activity intensity and play style. - * The action cooldown is randomized within the primary and secondary tick intervals in the current play style. - * Attention span is simulated to switch play styles to create a more human-like drift in attention. - * Non-linear intervals are used to create Anti-patterns in the action cooldowns to avoid fingerprinting. - * To activate the action cooldown, call the actionCooldown() method after the desired action. - * - * @param activity The activity to be performed - * @see Rs2Antiban#actionCooldown() - */ - public static void advancedPlayStyleSetup(Activity activity) { - Rs2AntibanSettings.actionCooldownActive = false; - Rs2AntibanSettings.microBreakActive = false; - Rs2AntibanSettings.antibanEnabled = true; - Rs2AntibanSettings.usePlayStyle = true; - Rs2AntibanSettings.randomIntervals = false; - Rs2AntibanSettings.simulateFatigue = false; - Rs2AntibanSettings.simulateAttentionSpan = true; - Rs2AntibanSettings.behavioralVariability = true; - Rs2AntibanSettings.nonLinearIntervals = true; - Rs2AntibanSettings.profileSwitching = false; //TODO: Implement this - Rs2AntibanSettings.timeOfDayAdjust = false; //TODO: Implement this - Rs2AntibanSettings.simulateMistakes = false; //Handled by the natural mouse - Rs2AntibanSettings.naturalMouse = true; - Rs2AntibanSettings.contextualVariability = false; //TODO: Implement this - Rs2AntibanSettings.dynamicIntensity = false; - Rs2AntibanSettings.dynamicActivity = false; - Rs2AntibanSettings.devDebug = true; - Rs2AntibanSettings.takeMicroBreaks = true; - Rs2AntibanSettings.actionCooldownChance = 0.1; - Rs2AntibanSettings.microBreakChance = 0.1; - Rs2AntibanSettings.microBreakDurationLow = 1; - Rs2AntibanSettings.microBreakDurationHigh = 5; - setActivity(activity); - } - public static void setActivity(Activity activity) { Rs2AntibanSettings.dynamicIntensity = false; @@ -278,12 +138,17 @@ public static boolean isIdle() { } public static void actionCooldown() { + if (Rs2AntibanSettings.actionCooldownChance < 1.00) { + actionCooldownByChance(); + return; + } + if (!Rs2AntibanSettings.usePlayStyle) { Microbot.log("PlayStyle not enabled, cannot perform action cooldown"); return; } - if (Rs2AntibanSettings.contextualVariability) + if (Rs2AntibanSettings.universalAntiban) Microbot.pauseAllScripts = true; if (Rs2AntibanSettings.nonLinearIntervals) playStyle.evolvePlayStyle(); @@ -292,6 +157,9 @@ public static void actionCooldown() { else TIMEOUT = playStyle.getPrimaryTickInterval(); Rs2AntibanSettings.actionCooldownActive = true; + if (Rs2AntibanSettings.moveMouseRandomly) + if (Rs2Random.dice(Rs2AntibanSettings.moveMouseRandomlyChance)) + moveMouseRandomly(); if (Rs2AntibanSettings.moveMouseOffScreen) moveMouseOffScreen(); } @@ -313,8 +181,12 @@ public static void actionCooldownByChance() { else TIMEOUT = playStyle.getPrimaryTickInterval(); Rs2AntibanSettings.actionCooldownActive = true; + if (Rs2AntibanSettings.moveMouseRandomly) + if (Rs2Random.dice(Rs2AntibanSettings.moveMouseRandomlyChance)) + moveMouseRandomly(); if (Rs2AntibanSettings.moveMouseOffScreen) moveMouseOffScreen(); + } } @@ -392,6 +264,10 @@ public static void moveMouseOffScreen() { Microbot.naturalMouse.moveOffScreen(); } + public static void moveMouseRandomly() { + Microbot.naturalMouse.moveRandom(); + } + public static void activateAntiban() { Rs2AntibanSettings.antibanEnabled = true; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2AntibanSettings.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2AntibanSettings.java index 6aeb189df5..0d4a89fca9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2AntibanSettings.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2AntibanSettings.java @@ -4,7 +4,7 @@ public class Rs2AntibanSettings { public static boolean actionCooldownActive = false; public static boolean microBreakActive = false; public static boolean antibanEnabled = true; - public static boolean usePlayStyle = true; + public static boolean usePlayStyle = false; public static boolean randomIntervals = false; public static boolean simulateFatigue = false; public static boolean simulateAttentionSpan = false; @@ -15,14 +15,17 @@ public class Rs2AntibanSettings { public static boolean simulateMistakes = false; //Handled by the natural mouse public static boolean naturalMouse = false; public static boolean moveMouseOffScreen = false; - public static boolean contextualVariability = true; //TODO: Implement this - public static boolean dynamicIntensity = true; - public static boolean dynamicActivity = true; - public static boolean devDebug = true; + public static boolean moveMouseRandomly = false; + public static boolean contextualVariability = false; + public static boolean dynamicIntensity = false; + public static boolean dynamicActivity = false; + public static boolean devDebug = false; public static boolean takeMicroBreaks = false; // will take micro breaks lasting 3-15 minutes at random intervals by default. public static boolean playSchedule = false; //TODO: Implement this + public static boolean universalAntiban = false; // Will attempt to use the same antiban settings for all plugins that has not yet implemented their own antiban settings. public static int microBreakDurationLow = 3; // 3 minutes public static int microBreakDurationHigh = 15; // 15 minutes public static double actionCooldownChance = 0.1; // 10% chance of activating the action cooldown by default public static double microBreakChance = 0.1; // 10% chance of taking a micro break by default + public static double moveMouseRandomlyChance = 0.1; // 10% chance of moving the mouse randomly by default } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/math/Rs2Random.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/math/Rs2Random.java index de43265506..ccf15d71c7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/math/Rs2Random.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/math/Rs2Random.java @@ -131,7 +131,7 @@ public static Point randomPointEx(Point from, Rectangle rect, double force) { // Dice function: Generates a random number and returns true if within the chance percentage public static boolean dice(double chancePercent) { - return RANDOM.nextDouble() < chancePercent / 100; + return RANDOM.nextDouble() < (chancePercent <= 0.99 ? chancePercent * 100 : chancePercent) / 100; } // Wait function weighted towards the mean of Min and Max From c2e76de516e82fc360552314f05df79b363c0204 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Thu, 15 Aug 2024 22:36:03 +0200 Subject: [PATCH 27/52] moveRandom added to natural mouse --- .../microbot/util/mouse/naturalmouse/NaturalMouse.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/NaturalMouse.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/NaturalMouse.java index cd01569120..17d9a78aba 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/NaturalMouse.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/naturalmouse/NaturalMouse.java @@ -136,6 +136,11 @@ public void moveOffScreen() { } } + // Move to a random point on the screen + public void moveRandom() { + moveTo(random.nextInt(0, client.getCanvasWidth() + 1), random.nextInt(0, client.getCanvasHeight() + 1)); + } + private static class SpeedManagerImpl extends DefaultSpeedManager { private SpeedManagerImpl(Collection flows) { super(flows); From 6cbdab57b531074ca8b718e0568f92f6747168ce Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Thu, 15 Aug 2024 22:37:29 +0200 Subject: [PATCH 28/52] Double check so that mouse is inside clickbox after moving --- .../plugins/microbot/util/mouse/Mouse.java | 4 +-- .../microbot/util/mouse/VirtualMouse.java | 34 ++++++++++++++++--- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/Mouse.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/Mouse.java index 147d4ee1ea..a386b01989 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/Mouse.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/Mouse.java @@ -1,10 +1,10 @@ package net.runelite.client.plugins.microbot.util.mouse; import lombok.Getter; -import net.runelite.api.MenuEntry; import net.runelite.api.Point; import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.plugins.microbot.util.math.Rs2Random; +import net.runelite.client.plugins.microbot.util.menu.NewMenuEntry; import javax.swing.*; import java.awt.*; @@ -62,7 +62,7 @@ public int randomizeClick() { public abstract Mouse click(Point point); public abstract Mouse click(Point point, boolean rightClick); - public abstract Mouse click(Point point, MenuEntry entry); + public abstract Mouse click(Point point, NewMenuEntry entry); public abstract Mouse click(); public abstract Mouse move(Point point); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/VirtualMouse.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/VirtualMouse.java index 34137e0e8b..ee26eb8e4b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/VirtualMouse.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/mouse/VirtualMouse.java @@ -1,9 +1,10 @@ package net.runelite.client.plugins.microbot.util.mouse; -import net.runelite.api.MenuEntry; +import lombok.extern.slf4j.Slf4j; import net.runelite.api.Point; import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings; +import net.runelite.client.plugins.microbot.util.menu.NewMenuEntry; import net.runelite.client.plugins.microbot.util.misc.Rs2UiHelper; import javax.inject.Inject; @@ -17,6 +18,7 @@ import static net.runelite.client.plugins.microbot.util.Global.sleep; import static net.runelite.client.plugins.microbot.util.math.Random.random; +@Slf4j public class VirtualMouse extends Mouse { private final ScheduledExecutorService scheduledExecutorService; @@ -68,18 +70,40 @@ public Mouse click(Point point, boolean rightClick) { return this; } - public Mouse click(Point point, boolean rightClick, MenuEntry entry) { + public Mouse click(Point point, boolean rightClick, NewMenuEntry entry) { if (point == null) return this; - if (Rs2AntibanSettings.naturalMouse && (point.getX() > 1 && point.getY() > 1)) + if (Rs2AntibanSettings.naturalMouse && (point.getX() > 1 && point.getY() > 1)) { Microbot.naturalMouse.moveTo(point.getX(), point.getY()); + if (Rs2UiHelper.hasActor(entry)) { + log.info("Actor found: " + entry.getActor().getName()); + Rectangle rectangle = Rs2UiHelper.getActorClickbox(entry.getActor()); + if (!Rs2UiHelper.isMouseWithinRectangle(rectangle)) { + point = Rs2UiHelper.getClickingPoint(rectangle, true); + Microbot.naturalMouse.moveTo(point.getX(), point.getY()); + } + + } + if (Rs2UiHelper.isGameObject(entry)) { + log.info("Game Object found: " + entry.getGameObject().toString()); + Rectangle rectangle = Rs2UiHelper.getObjectClickbox(entry.getGameObject()); + if (!Rs2UiHelper.isMouseWithinRectangle(rectangle)) { + point = Rs2UiHelper.getClickingPoint(rectangle, true); + Microbot.naturalMouse.moveTo(point.getX(), point.getY()); + } + } + + + } + // Target menu was set before mouse movement causing some unintended behavior // This will set the target menu after the mouse movement is finished Microbot.targetMenu = entry; if (Microbot.getClient().isClientThread()) { + Point finalPoint = point; scheduledExecutorService.schedule(() -> { - handleClick(point, rightClick); + handleClick(finalPoint, rightClick); }, 0, TimeUnit.MILLISECONDS); } else { handleClick(point, rightClick); @@ -111,7 +135,7 @@ public Mouse click(Point point) { } @Override - public Mouse click(Point point, MenuEntry entry) { + public Mouse click(Point point, NewMenuEntry entry) { return click(point, false, entry); } From 1518851f5a2ed8e97ac25c92837c41ba376ef533 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Thu, 15 Aug 2024 23:45:21 +0200 Subject: [PATCH 29/52] Plugin start fix --- .../runelite/client/plugins/microbot/Microbot.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java index b8f5b712e8..65b4748c25 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java @@ -21,6 +21,7 @@ import net.runelite.client.plugins.microbot.dashboard.PluginRequestModel; import net.runelite.client.plugins.microbot.util.inventory.Rs2Item; import net.runelite.client.plugins.microbot.util.math.Random; +import net.runelite.client.plugins.microbot.util.menu.NewMenuEntry; import net.runelite.client.plugins.microbot.util.misc.Rs2UiHelper; import net.runelite.client.plugins.microbot.util.mouse.Mouse; import net.runelite.client.plugins.microbot.util.mouse.naturalmouse.NaturalMouse; @@ -240,9 +241,10 @@ public static List updateItemContainer(int id, ItemContainerChanged e) public static void startPlugin(Plugin plugin) { if (plugin == null) return; - if (!Microbot.getPluginManager().isPluginEnabled(plugin)) { - Microbot.getPluginManager().setPluginEnabled(plugin, true); - } + Microbot.getPluginManager().setPluginEnabled(plugin, true); + Microbot.getPluginManager().startPlugins(); + + } @Deprecated(since = "1.3.8 - use Rs2UiHelper", forRemoval = true) @@ -253,7 +255,7 @@ public static Point calculateClickingPoint(Rectangle rect) { return new Point(x, y); } - public static void doInvoke(MenuEntry entry, Rectangle rectangle) { + public static void doInvoke(NewMenuEntry entry, Rectangle rectangle) { try { if (Rs2UiHelper.isRectangleWithinViewport(rectangle)) { @@ -278,7 +280,7 @@ public static void drag(Rectangle start, Rectangle end) { } } - public static void click(Rectangle rectangle, MenuEntry entry) { + public static void click(Rectangle rectangle, NewMenuEntry entry) { Point point = Rs2UiHelper.getClickingPoint(rectangle, true); mouse.click(point, entry); From 7825fe4fc9952bbffd4326a20b3390ee318f84f8 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Thu, 15 Aug 2024 23:46:06 +0200 Subject: [PATCH 30/52] Start breakHandler --- .../client/plugins/microbot/util/antiban/AntibanPlugin.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java index 7d158d13e6..35582f9168 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java @@ -179,9 +179,9 @@ public void onGameTick(GameTick event) { if (Rs2AntibanSettings.takeMicroBreaks && !Microbot.isPluginEnabled(BreakHandlerPlugin.class)) { Microbot.log("BreakHandlerPlugin is not enabled, attempting to enable it...."); - Plugin breakHandlerPlugin = Microbot.getPluginManager().getPlugins() - .stream() - .filter(p -> p.getName().contains("BreakHandler")) + String name = BreakHandlerPlugin.class.getName(); + Plugin breakHandlerPlugin = Microbot.getPluginManager().getPlugins().stream() + .filter(x -> x.getClass().getName().equals(name)) .findFirst() .orElse(null); Microbot.startPlugin(breakHandlerPlugin); From 2115ef3e833957b811ed4451a40a38b6e69e75a1 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Thu, 15 Aug 2024 23:46:37 +0200 Subject: [PATCH 31/52] Rs2UiHelper additional checks --- .../runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java index 9aa1c93ccb..d8413bd1cc 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java @@ -39,6 +39,8 @@ public static Point getClickingPoint(Rectangle rectangle, boolean randomize) { //check if mouse is already within the rectangle public static boolean isMouseWithinRectangle(Rectangle rectangle) { java.awt.Point mousePos = Microbot.getMouse().getMousePosition(); + if (rectangle.getX() == 1 && rectangle.getY() == 1) return true; + if (rectangle.getX() == 0 && rectangle.getY() == 0) return true; return rectangle.contains(mousePos); } From d73c9c1e7c5c04b1aa61bc47f4311c09847c5c49 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Thu, 15 Aug 2024 23:47:08 +0200 Subject: [PATCH 32/52] BreakHandler antiban update --- .../plugins/microbot/breakhandler/BreakHandlerScript.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerScript.java index 06fb91085b..b512078b1f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerScript.java @@ -73,6 +73,8 @@ public boolean run(BreakHandlerConfig config) { } if (breakDuration <= 0 && Microbot.pauseAllScripts) { + if (Rs2AntibanSettings.universalAntiban && Rs2AntibanSettings.actionCooldownActive) + return; Microbot.pauseAllScripts = false; if (breakIn <= 0) breakIn = Random.random(config.timeUntilBreakStart() * 60, config.timeUntilBreakEnd() * 60); From 97d951ad1d9b651411810bf88cc90ec2cb33338c Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sat, 17 Aug 2024 21:44:03 +0200 Subject: [PATCH 33/52] Documentation for Antiban system --- .../microbot/util/antiban/AntibanOverlay.java | 93 ++++- .../microbot/util/antiban/AntibanPlugin.java | 70 ++-- .../util/antiban/AntibanPluginPanel.java | 29 ++ .../util/antiban/AntibanSetupTemplates.java | 62 +++- .../microbot/util/antiban/Rs2Antiban.java | 323 ++++++++++++++---- .../util/antiban/Rs2AntibanSettings.java | 104 ++++++ .../microbot/util/antiban/enums/Activity.java | 52 +++ .../util/antiban/enums/ActivityIntensity.java | 50 +++ .../microbot/util/antiban/enums/Category.java | 55 ++- .../util/antiban/enums/PlaySchedule.java | 34 ++ .../util/antiban/enums/PlayStyle.java | 46 +++ .../plugins/microbot/util/math/Rs2Random.java | 240 ++++++++++++- 12 files changed, 1049 insertions(+), 109 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanOverlay.java index 51b5121c9a..b466477e3c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanOverlay.java @@ -11,12 +11,94 @@ import java.awt.*; import java.awt.image.BufferedImage; +/** + * The {@code AntibanOverlay} class provides a visual representation of the anti-ban system through + * an overlay component on the game screen. This overlay uses a progress pie to display the remaining + * time for the current action cooldown, simulating a human-like delay or pause in gameplay. The overlay + * dynamically follows the player's location and updates in real-time as the cooldown progresses. + * + *

+ * This class extends the {@code Overlay} class from RuneLite's API and leverages {@code ProgressPieComponent} + * to render a circular timer. The timer visually decreases over time, providing an indication of when the + * next action will be performed. It is designed to be unobtrusive and provides important feedback for + * antiban behaviors. + *

+ * + *

Features:

+ *
    + *
  • Dynamic positioning: The overlay automatically adjusts its position to follow the player's location on the screen.
  • + *
  • Visual feedback: A progress pie that gradually depletes, showing the remaining time before the next action is triggered.
  • + *
  • Integration with Anti-ban system: The overlay is displayed only when the anti-ban cooldown is active, + * providing clear feedback during specific bot behaviors.
  • + *
+ * + *

Usage:

+ *

+ * The {@code AntibanOverlay} class does not need to be explicitly initialized or called. + * It is automatically integrated into the overlay system and activated when the anti-ban action cooldown + * is active, as determined by the {@code Rs2AntibanSettings}. + *

+ * + *

+ * This class works in conjunction with {@code Rs2Antiban} to monitor the player's activity and to display the + * appropriate overlay based on the current status of the anti-ban system. It draws a circular timer around the + * player character, providing a visual cue for the current cooldown. + *

+ * + *

Example Flow:

+ *
+ * // This class is automatically invoked by the overlay system when the action cooldown is active.
+ * // The following example describes its integration in the larger system:
+ *
+ * // 1. The anti-ban system detects an action cooldown.
+ * Rs2Antiban.actionCooldown();
+ *
+ * // 2. If the cooldown is active, the overlay renders the progress pie around the player.
+ * AntibanOverlay.render(graphics);
+ * 
+ * + *

Primary Methods:

+ *
    + *
  • getCanvasTextLocation(Graphics2D graphics, Actor actor): Calculates the screen location of the player, + * used for positioning the overlay relative to the player's character.
  • + *
  • drawTimerPieOverlay(Graphics2D graphics): Renders the progress pie overlay, representing the remaining + * cooldown time as a gradually decreasing pie chart.
  • + *
  • render(Graphics2D graphics): The main render loop that checks if an action cooldown is active and draws + * the overlay if necessary.
  • + *
+ * + *

Rendering Logic:

+ *

+ * The rendering process begins with checking if the anti-ban cooldown is active. If active, the overlay retrieves + * the player's screen location and calculates the remaining cooldown time. The progress pie is then drawn at the + * calculated location, visually indicating the time left until the next action. + *

+ * + *

Customization:

+ *

+ * The color of the overlay and the size of the timer can be customized by adjusting the constants + * {@code PUBLIC_TIMER_COLOR}, and {@code TIMER_OVERLAY_DIAMETER}. + *

+ * + *

Dependencies:

+ *
    + *
  • Requires integration with the {@code Rs2Antiban} system for cooldown tracking and updates.
  • + *
  • Relies on {@code ProgressPieComponent} for rendering the progress pie as the visual indicator of time remaining.
  • + *
  • Uses the RuneLite API's {@code Overlay} class for managing the visual overlay within the game.
  • + *
+ * + *

Limitations:

+ *
    + *
  • The overlay will only render if {@code Rs2AntibanSettings.actionCooldownActive} is set to true, meaning it is dependent on the + * anti-ban system being active and configured properly.
  • + *
  • The overlay is tied to the player's screen position, so if the player is off-screen, the overlay may not be visible.
  • + *
+ */ public class AntibanOverlay extends Overlay { - private static final Color PUBLIC_TIMER_COLOR = Color.YELLOW; - private static final Color PRIVATE_TIMER_COLOR = Color.GREEN; - private static final int TIMER_OVERLAY_DIAMETER = 20; + public static Color PUBLIC_TIMER_COLOR = Color.YELLOW; + public static int TIMER_OVERLAY_DIAMETER = 20; private final ProgressPieComponent progressPieComponent = new ProgressPieComponent(); @Inject @@ -34,7 +116,6 @@ private Point getCanvasTextLocation(Graphics2D graphics, Actor actor) { private void drawTimerPieOverlay(Graphics2D graphics) { - Color fillColor = Color.YELLOW; // Calculate the remaining time as a fraction of the total time int totalTime = Rs2Antiban.getPlayStyle().getSecondaryTickInterval(); @@ -50,8 +131,8 @@ private void drawTimerPieOverlay(Graphics2D graphics) { int x = playerLocation.getX() + (TIMER_OVERLAY_DIAMETER / 2); int y = playerLocation.getY() - (15 + (Microbot.getClient().getScale() / 30)); progressPieComponent.setPosition(new Point(x, y)); - progressPieComponent.setFill(fillColor); - progressPieComponent.setBorderColor(fillColor); + progressPieComponent.setFill(PUBLIC_TIMER_COLOR); + progressPieComponent.setBorderColor(PUBLIC_TIMER_COLOR); progressPieComponent.setProgress(percent); // inverse so pie drains over time progressPieComponent.render(graphics); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java index 35582f9168..e4d34bbd3a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java @@ -1,6 +1,5 @@ package net.runelite.client.plugins.microbot.util.antiban; -import lombok.extern.slf4j.Slf4j; import net.runelite.api.AnimationID; import net.runelite.api.GameState; import net.runelite.api.Skill; @@ -33,6 +32,44 @@ import java.util.Timer; import java.util.TimerTask; +/** + * The AntibanPlugin is responsible for managing anti-ban behaviors during bot operation. + * + *

+ * This plugin ensures that the bot behaves in a more human-like manner to avoid detection by using various + * anti-ban strategies. These strategies include simulating breaks, adjusting activity levels, and mimicking + * attention span variations. The plugin tracks user activity and game state to dynamically adjust bot behavior. + *

+ * + *

Main Features:

+ *
    + *
  • Simulates action cooldowns and micro-breaks based on the bot's current activities.
  • + *
  • Dynamically adjusts activity intensity and behavior depending on the bot's in-game actions, such as mining or cooking.
  • + *
  • Tracks user skill changes and updates the anti-ban settings accordingly.
  • + *
  • Supports attention span simulation, profile switching, and periodic breaks to ensure realistic play styles.
  • + *
  • Automatically enables the BreakHandlerPlugin when needed for managing breaks.
  • + *
+ * + *

Usage:

+ *

+ * The AntibanPlugin works silently in the background to adjust the bot's behavior during runtime. + * Users do not need to manually interact with this plugin, as it is automatically integrated into the bot framework. + *

+ * + *

+ * The plugin monitors in-game actions, such as cooking, mining, and skill changes, to adjust its anti-ban strategy + * accordingly. It also manages the simulation of breaks, cooldowns, and play style variations to mimic human behavior + * and avoid detection. + *

+ * + *

Additional Details:

+ *
    + *
  • Automatic tracking of idle time to determine if the bot should take a break.
  • + *
  • Real-time updates to anti-ban settings based on player activities and game state changes.
  • + *
  • Hidden from the user interface to avoid unnecessary distractions, while always being active in the background.
  • + *
+ */ + @PluginDescriptor( name = PluginDescriptor.See1Duck + "Antiban", description = "Antiban for microbot", @@ -40,7 +77,8 @@ alwaysOn = true, hidden = true ) -@Slf4j + + public class AntibanPlugin extends Plugin { private static final int COOK_TIMEOUT = 3; @@ -76,7 +114,7 @@ public static boolean isIdle() { return idleTicks > IDLE_TIMEOUT; } - public static void updateIdleTicks() { + private static void updateIdleTicks() { idleTicks++; } @@ -139,7 +177,7 @@ public void onChatMessage(ChatMessage event) { @Subscribe public void onProfileChanged(ProfileChanged event) { - Rs2Antiban.resetAntiban(); + Rs2Antiban.resetAntibanSettings(); } @Subscribe @@ -245,28 +283,4 @@ private void updateAntibanSettings(Skill skill) { Microbot.log("Activity changed, new activity intensity: " + activityIntensity); } } - - private void applyContextualVariabilitySetup() { - Rs2AntibanSettings.antibanEnabled = true; - Rs2AntibanSettings.usePlayStyle = true; - Rs2AntibanSettings.randomIntervals = false; - Rs2AntibanSettings.simulateFatigue = true; - Rs2AntibanSettings.simulateAttentionSpan = true; - Rs2AntibanSettings.behavioralVariability = true; - Rs2AntibanSettings.nonLinearIntervals = true; - Rs2AntibanSettings.profileSwitching = true; - Rs2AntibanSettings.timeOfDayAdjust = false; - Rs2AntibanSettings.simulateMistakes = true; - Rs2AntibanSettings.naturalMouse = true; - Rs2AntibanSettings.contextualVariability = true; - Rs2AntibanSettings.dynamicIntensity = true; - Rs2AntibanSettings.dynamicActivity = true; - Rs2AntibanSettings.devDebug = false; - Rs2AntibanSettings.takeMicroBreaks = true; - Rs2AntibanSettings.playSchedule = true; - Rs2AntibanSettings.microBreakDurationLow = 3; - Rs2AntibanSettings.microBreakDurationHigh = 8; - Rs2AntibanSettings.actionCooldownChance = 0.05; - Rs2AntibanSettings.microBreakChance = 0.05; - } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPluginPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPluginPanel.java index 06cfc4db22..40265fe462 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPluginPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPluginPanel.java @@ -7,6 +7,35 @@ import java.awt.*; import java.util.Objects; +/** + * The AntibanPluginPanel is a user interface panel for configuring anti-ban settings. + * + *

+ * This panel allows users to adjust settings related to the anti-ban system, such as enabling micro-breaks, + * adjusting action cooldown probabilities, and configuring behavioral simulations like fatigue or attention span. + * The panel is divided into different categories, each focusing on specific aspects of anti-ban behavior, + * including activity settings, mouse behavior, and cooldown management. + *

+ * + *

+ * Users can interact with various checkboxes and sliders to tailor the bot's anti-ban features to their preferences, + * making it behave more like a human player during automated tasks. + *

+ * + *

Main Features:

+ *
    + *
  • Enable or disable anti-ban features like action cooldowns and micro-breaks.
  • + *
  • Customize the bot's behavior with random intervals, dynamic activity, and simulated fatigue.
  • + *
  • Adjust the duration and probability of micro-breaks and action cooldowns.
  • + *
  • Fine-tune mouse behavior, including natural movements and random actions.
  • + *
  • View real-time information about the current play style, activity, and bot status.
  • + *
+ * + *

+ * This panel is automatically integrated into the bot's user interface and does not require manual initialization by the user. + *

+ */ + public class AntibanPluginPanel extends PluginPanel { private final JCheckBox isActionCooldownActive = new JCheckBox("Action Cooldown Active"); private final JCheckBox isMicroBreakActive = new JCheckBox("Micro Break Active"); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanSetupTemplates.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanSetupTemplates.java index 24f10e90f6..93b2c7f288 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanSetupTemplates.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanSetupTemplates.java @@ -3,11 +3,65 @@ import net.runelite.client.plugins.microbot.util.antiban.enums.Activity; /** - * The {@code AntibanSetupTemplates} class provides a set of methods to apply specific antiban - * configurations tailored to different activities within the game. These configurations adjust various - * settings such as mouse movement, random intervals, and fatigue simulation to mimic human behavior and - * reduce the likelihood of detection by anti-cheat systems. + * The {@code AntibanSetupTemplates} class provides predefined antiban setup configurations tailored to specific + * in-game activities. These configurations are designed to mimic human-like behaviors such as fatigue simulation, + * attention span, and behavioral variability, with the goal of reducing detection risks by anti-cheat systems. + * + *

+ * Each method in this class corresponds to a specific activity (e.g., combat, runecrafting, construction) and + * adjusts various settings to create a more realistic and undetectable experience for the bot. + * These setups adjust mouse movement patterns, simulate breaks, introduce variability, and more. + *

+ * + *

Main Features:

+ *
    + *
  • Activity-Specific Setups: Methods are provided to apply antiban configurations for a wide range of activities, + * including skilling, combat, and more specialized tasks like runecrafting and herblore.
  • + *
  • Human-Like Behavior Simulation: The setups simulate human-like behaviors to avoid detection, including fatigue, + * attention span, micro breaks, mouse movement patterns, and random intervals.
  • + *
  • Flexible Configurations: Each setup method customizes settings such as action cooldowns, behavioral variability, + * and micro breaks to tailor the antiban behavior to the specific activity.
  • + *
  • Basic Setup: A general setup method is included for cases where simpler antiban measures are needed without + * advanced features like attention span or micro breaks.
  • + *
+ * + *

Usage:

+ *

+ * These methods are intended to be called before executing specific game activities to ensure that the antiban measures + * are properly configured for the task. The configurations can be adjusted to suit different activities or playstyles + * by enabling or disabling certain features like profile switching or dynamic intensity. + *

+ * + *

Example:

+ *

Inside your plugin script class, execute the initialization outside the main loop.

+ *
+ * private void initialize() {
+ *         Rs2Antiban.antibanSetupTemplates.applyMiningSetup();
+ *     }
+ * 
+ * + *

Available Setup Methods:

+ *
    + *
  • applyCombatSetup(): Configures antiban settings for combat activities.
  • + *
  • applyRunecraftingSetup(): Configures antiban settings for runecrafting activities.
  • + *
  • applyConstructionSetup(): Configures antiban settings for construction activities.
  • + *
  • applyAgilitySetup(): Configures antiban settings for agility tasks.
  • + *
  • applyHerbloreSetup(): Configures antiban settings for herblore tasks.
  • + *
  • applyThievingSetup(): Configures antiban settings for thieving tasks.
  • + *
  • applyCraftingSetup(): Configures antiban settings for crafting tasks.
  • + *
  • applyFletchingSetup(): Configures antiban settings for fletching tasks.
  • + *
  • applyHunterSetup(): Configures antiban settings for hunter tasks.
  • + *
  • applyMiningSetup(): Configures antiban settings for mining tasks.
  • + *
  • applySmithingSetup(): Configures antiban settings for smithing tasks.
  • + *
  • applyFishingSetup(): Configures antiban settings for fishing tasks.
  • + *
  • applyCookingSetup(): Configures antiban settings for cooking tasks.
  • + *
  • applyFiremakingSetup(): Configures antiban settings for firemaking tasks.
  • + *
  • applyWoodcuttingSetup(): Configures antiban settings for woodcutting tasks.
  • + *
  • applyFarmingSetup(): Configures antiban settings for farming tasks.
  • + *
  • applyGeneralBasicSetup(): Applies a basic antiban configuration without advanced features.
  • + *
*/ + public class AntibanSetupTemplates { /** * Applies the antiban setup tailored for general combat activities. diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2Antiban.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2Antiban.java index 7f0546e1cb..b2516c818e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2Antiban.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2Antiban.java @@ -22,6 +22,72 @@ import static net.runelite.api.AnimationID.*; +/** + * The {@code Rs2Antiban} class provides a comprehensive anti-ban system that simulates human-like behavior + * during various in-game activities. This system includes features such as mouse fatigue, random intervals, + * micro-breaks, action cooldowns, and contextually aware mouse movements, all aimed at reducing the risk + * of detection by anti-cheat systems. + * + *

+ * The class uses configurations set in {@code Rs2AntibanSettings} to determine the behavior of the bot + * during activities like woodcutting, mining, cooking, and more. It leverages various methods to simulate + * natural player behaviors, such as random mouse movements and taking breaks. It also integrates with + * specific activity configurations like play style, activity intensity, and categories, which are adjusted + * based on the activity being performed. + *

+ * + *

Main Features:

+ *
    + *
  • Human-Like Behavior Simulation: Simulates actions such as moving the mouse randomly, taking micro-breaks, + * and varying the intervals between actions to mimic natural gameplay.
  • + *
  • Activity-Based Configurations: Allows setting activity-specific antiban configurations through + * the {@code setActivity()} and {@code setActivityIntensity()} methods, ensuring that the antiban + * behavior is appropriate for the current task.
  • + *
  • Mouse Fatigue Simulation: Integrates with a mouse fatigue system to simulate the effects of fatigue + * on mouse movement over time.
  • + *
  • Overlay Rendering: Provides methods to render an overlay that displays current antiban status, + * including activity, play style, and action cooldown progress.
  • + *
  • Micro-Breaks and Cooldowns: Supports taking breaks based on a random chance or specific intervals + * to simulate a player taking short pauses during gameplay.
  • + *
+ * + *

Usage:

+ *

+ * The methods provided in this class are designed to be called during in-game activities to ensure the antiban + * system is engaged and functioning according to the activity being performed. The configurations can be customized + * through {@code Rs2AntibanSettings} to adjust behaviors such as action cooldowns, break chances, and mouse movements. + *

+ * + *

Example:

+ *
+ * // Setting the antiban activity to woodcutting
+ * Rs2Antiban.setActivity(Activity.GENERAL_WOODCUTTING);
+ *
+ * // Triggering an action cooldown based on current settings
+ * Rs2Antiban.actionCooldown();
+ *
+ * // Rendering the antiban overlay in a panel
+ * Rs2Antiban.renderAntibanOverlayComponents(panelComponent);
+ * 
+ * + *

Available Methods:

+ *
    + *
  • setActivity(Activity activity): Sets the current activity and adjusts antiban settings based on the activity type.
  • + *
  • setActivityIntensity(ActivityIntensity intensity): Sets the intensity level of the current activity.
  • + *
  • actionCooldown(): Triggers an action cooldown, potentially adjusting play style and performing random mouse movements.
  • + *
  • takeMicroBreakByChance(): Attempts to trigger a micro-break based on a random chance.
  • + *
  • isWoodcutting(): Checks if the player is currently performing a woodcutting animation.
  • + *
  • isMining(): Checks if the player is currently performing a mining animation.
  • + *
  • isIdle(): Checks if the player is currently idle (not performing any animation).
  • + *
  • renderAntibanOverlayComponents(PanelComponent panelComponent): Renders an overlay showing the current antiban status and action cooldown progress.
  • + *
  • moveMouseOffScreen(): Moves the mouse off-screen to simulate taking a break.
  • + *
  • moveMouseRandomly(): Moves the mouse randomly to simulate natural behavior during gameplay.
  • + *
  • activateAntiban(): Activates the antiban system.
  • + *
  • deactivateAntiban(): Deactivates the antiban system.
  • + *
  • resetAntibanSettings(): Resets all antiban settings to their default values.
  • + *
+ */ + @Getter @Setter public class Rs2Antiban { @@ -125,21 +191,80 @@ public static boolean checkForCookingEvent(ChatMessage event) { || message.startsWith("You accidentally spoil"); } + /** + * Checks if the player is currently performing a woodcutting animation. + * + * @return true if the player is performing a woodcutting animation, false otherwise. + */ public static boolean isWoodcutting() { return WOODCUTTING_ANIMS.contains(Rs2Player.getAnimation()); } + /** + * Checks if the player is currently performing a mining animation. + * + * @return true if the player is performing a mining animation, false otherwise. + */ public static boolean isMining() { return MINING_ANIMATION_IDS.contains(Rs2Player.getAnimation()); } + /** + * Checks if the player is currently idle. + * + * @return true if the player is idle, false otherwise. + */ public static boolean isIdle() { return AntibanPlugin.isIdle(); } + /** + *

Handles the Execution of an Action Cooldown Based on Anti-Ban Behaviors

+ *

+ * This method controls the flow for activating the cooldown either with certainty or based on a chance. + * It includes logic to adjust behaviors such as non-linear intervals, behavioral variability, and random mouse movements + * to simulate more human-like actions. + *

+ *

+ * Execute this method at any point in your script where you want to trigger an action cooldown. + *

+ *

+ * The cooldown can be triggered directly if actionCooldownChance is 1.00 (100%), + * or by chance if actionCooldownChance is less than 1.00 (100%). Several features like universal antiban, + * non-linear intervals, and play style evolution are configurable through Rs2AntibanSettings. + *

+ * + *

Primary Actions Handled:

+ *
    + *
  • Pausing all scripts if the universal antiban is enabled.
  • + *
  • Evolving play style if non-linear intervals are enabled.
  • + *
  • Setting a timeout based on behavioral variability settings.
  • + *
  • Optionally moving the mouse randomly or off-screen based on respective settings.
  • + *
+ * + *

Preconditions:

+ *
    + *
  • If Rs2AntibanSettings.usePlayStyle is disabled, the cooldown will not be performed.
  • + *
+ * + *

Main Flow:

+ *
    + *
  • If actionCooldownChance < 1.00 (100%), the cooldown is triggered based on the result of a random dice roll.
  • + *
  • If actionCooldownChance is 1.00 (100%) or greater, the cooldown is triggered unconditionally.
  • + *
+ * + *

Helper Methods:

+ *

+ * performActionCooldown() encapsulates the shared logic for performing the cooldown, + * adjusting the play style, and invoking other anti-ban actions like moving the mouse randomly or off-screen. + *

+ */ + public static void actionCooldown() { - if (Rs2AntibanSettings.actionCooldownChance < 1.00) { - actionCooldownByChance(); + if (Rs2AntibanSettings.actionCooldownChance <= 0.99) { + if (Rs2Random.dice(Rs2AntibanSettings.actionCooldownChance)) { + performActionCooldown(); + } return; } @@ -148,50 +273,62 @@ public static void actionCooldown() { return; } + performActionCooldown(); + } + + private static void performActionCooldown() { if (Rs2AntibanSettings.universalAntiban) Microbot.pauseAllScripts = true; + if (Rs2AntibanSettings.nonLinearIntervals) playStyle.evolvePlayStyle(); + if (Rs2AntibanSettings.behavioralVariability) TIMEOUT = playStyle.getRandomTickInterval(); else TIMEOUT = playStyle.getPrimaryTickInterval(); + Rs2AntibanSettings.actionCooldownActive = true; - if (Rs2AntibanSettings.moveMouseRandomly) - if (Rs2Random.dice(Rs2AntibanSettings.moveMouseRandomlyChance)) - moveMouseRandomly(); - if (Rs2AntibanSettings.moveMouseOffScreen) - moveMouseOffScreen(); - } - // method to activate the action cooldown by chance - public static void actionCooldownByChance() { - if (!Rs2AntibanSettings.usePlayStyle) { - Microbot.log("PlayStyle not enabled, cannot perform action cooldown"); - return; + if (Rs2AntibanSettings.moveMouseRandomly && Rs2Random.dice(Rs2AntibanSettings.moveMouseRandomlyChance)) { + Rs2Random.wait(100, 200); + moveMouseRandomly(); } + if (Rs2AntibanSettings.moveMouseOffScreen) + moveMouseOffScreen(); + } - if (Math.random() < Rs2AntibanSettings.actionCooldownChance) { - - if (Rs2AntibanSettings.nonLinearIntervals) - playStyle.evolvePlayStyle(); - if (Rs2AntibanSettings.behavioralVariability) - TIMEOUT = playStyle.getRandomTickInterval(); - else - TIMEOUT = playStyle.getPrimaryTickInterval(); - Rs2AntibanSettings.actionCooldownActive = true; - if (Rs2AntibanSettings.moveMouseRandomly) - if (Rs2Random.dice(Rs2AntibanSettings.moveMouseRandomlyChance)) - moveMouseRandomly(); - if (Rs2AntibanSettings.moveMouseOffScreen) - moveMouseOffScreen(); - - } - } + /** + * Attempts to trigger a micro-break based on a random chance, as configured in Rs2AntibanSettings. + * + *

+ * This method simulates human-like pauses in the bot's behavior by invoking a micro-break if a randomly generated + * value is less than the configured microBreakChance. When triggered, the break duration is determined + * randomly within a specified range and the mouse may be optionally moved off-screen. + *

+ * + *

Behavior:

+ *
    + *
  • If a random value is less than Rs2AntibanSettings.microBreakChance, the micro-break is activated.
  • + *
  • The break duration is randomly set between Rs2AntibanSettings.microBreakDurationLow and + * Rs2AntibanSettings.microBreakDurationHigh, in seconds.
  • + *
  • If Rs2AntibanSettings.moveMouseOffScreen is enabled, the mouse is moved off-screen during the break.
  • + *
+ * + *

Preconditions:

+ *
    + *
  • The configuration in Rs2AntibanSettings must define valid break chance and duration values.
  • + *
+ * + *

Postconditions:

+ *
    + *
  • Rs2AntibanSettings.microBreakActive is set to true if the break is triggered.
  • + *
  • BreakHandlerScript.breakDuration is set to a randomly determined value in seconds.
  • + *
+ */ - // method to take a micro break by chance public static void takeMicroBreakByChance() { if (Math.random() < Rs2AntibanSettings.microBreakChance) { Rs2AntibanSettings.microBreakActive = true; @@ -203,6 +340,95 @@ public static void takeMicroBreakByChance() { } + /** + * Renders an overlay component that displays various anti-ban settings and information within a panel. + * + *

+ * This method populates a PanelComponent with details regarding the current anti-ban system's state, + * activity levels, play styles, and other related information. It is intended for use in providing a visual representation + * of the anti-ban system's status during runtime, with debug information shown when enabled. + *

+ * + *

Overlay Components:

+ *
    + *
  • A title component labeled "🦆 Humanizer 🦆" with orange coloring.
  • + *
  • Details about the current activity, including method name, category, and intensity.
  • + *
  • If Rs2AntibanSettings.devDebug is enabled, several debug lines will show key anti-ban settings, + * such as action cooldown, random intervals, and behavioral variability.
  • + *
  • If a play style is active, the panel displays the current play style name and the time remaining until the next switch + * if attention span simulation is enabled.
  • + *
  • A progress bar representing the current action cooldown based on a tick interval, providing a visual cue for + * the remaining time.
  • + *
  • Status updates on whether the bot is busy or idle, indicating potential upcoming breaks.
  • + *
+ * + *

Behavior:

+ *
    + *
  • The method dynamically updates the panel with current information based on settings in Rs2AntibanSettings + * and playStyle.
  • + *
  • If debug mode is enabled, additional lines provide detailed state information, such as whether action cooldown, + * fatigue simulation, and natural mouse movements are active.
  • + *
  • The progress bar visually indicates the current state of the action cooldown timer.
  • + *
+ * + *

Preconditions:

+ *
    + *
  • playStyle and Rs2AntibanSettings must be properly initialized.
  • + *
  • The panelComponent must be passed as a valid and non-null component to receive overlay data.
  • + *
+ * + *

Where to Use:

+ *

+ * This method should be used within overlay rendering methods, typically in custom overlay classes that extend + * OverlayPanel. For example, in the MotherloadMineOverlay class, this method is used to + * display anti-ban information in the mining overlay. It is invoked within the render(Graphics2D graphics) + * method to ensure that the anti-ban status is updated every time the overlay is drawn. + *

+ * + *

+ * To integrate this method into a custom overlay: + *

+ *
    + *
  1. Ensure that your overlay class extends OverlayPanel or a similar class that supports adding components.
  2. + *
  3. Invoke Rs2Antiban.renderAntibanOverlayComponents(panelComponent); within the overlay's + * render method, before or after other components are added, depending on the desired layout.
  4. + *
  5. Ensure that the appropriate Rs2AntibanSettings are configured before invoking the method.
  6. + *
+ * + *

Example Usage:

+ *
+     * {@code
+     * @Override
+     * public Dimension render(Graphics2D graphics) {
+     *     try {
+     *         panelComponent.setPreferredSize(new Dimension(275, 900));
+     *         panelComponent.getChildren().add(TitleComponent.builder()
+     *                 .text("\uD83E\uDD86 Motherlode Mine \uD83E\uDD86")
+     *                 .color(Color.ORANGE)
+     *                 .build());
+     *
+     *         Rs2Antiban.renderAntibanOverlayComponents(panelComponent);
+     *         addEmptyLine();
+     *
+     *         panelComponent.getChildren().add(LineComponent.builder()
+     *                 .left("Mining Location: " + MotherloadMineScript.miningSpot.name())
+     *                 .build());
+     *
+     *         addEmptyLine();
+     *
+     *         panelComponent.getChildren().add(LineComponent.builder()
+     *                 .left(status.toString())
+     *                 .right("Version: " + MotherloadMineScript.version)
+     *                 .build());
+     *     } catch (Exception ex) {
+     *         System.out.println(ex.getMessage());
+     *     }
+     *     return super.render(graphics);
+     * }
+     * }
+     * 
+ */ + public static void renderAntibanOverlayComponents(PanelComponent panelComponent) { final ProgressBarComponent progressBarComponent = new ProgressBarComponent(); progressBarComponent.setBackgroundColor(Color.DARK_GRAY); @@ -264,6 +490,11 @@ public static void moveMouseOffScreen() { Microbot.naturalMouse.moveOffScreen(); } + /** + *

Move Mouse Randomly

+ * This method moves the mouse randomly based on the given chance in settings. + * This is used to simulate a user moving the mouse randomly to take a break. + */ public static void moveMouseRandomly() { Microbot.naturalMouse.moveRandom(); } @@ -277,34 +508,8 @@ public static void deactivateAntiban() { } // reset all the variables - public static void resetAntiban() { - Rs2AntibanSettings.antibanEnabled = false; - Rs2AntibanSettings.microBreakActive = false; - Rs2AntibanSettings.actionCooldownActive = false; - Rs2AntibanSettings.usePlayStyle = false; - Rs2AntibanSettings.randomIntervals = false; - Rs2AntibanSettings.simulateFatigue = false; - Rs2AntibanSettings.simulateAttentionSpan = false; - Rs2AntibanSettings.behavioralVariability = false; - Rs2AntibanSettings.nonLinearIntervals = false; - Rs2AntibanSettings.profileSwitching = false; - Rs2AntibanSettings.timeOfDayAdjust = false; - Rs2AntibanSettings.simulateMistakes = false; - Rs2AntibanSettings.naturalMouse = false; - Rs2AntibanSettings.contextualVariability = false; - Rs2AntibanSettings.dynamicIntensity = true; - Rs2AntibanSettings.dynamicActivity = true; - Rs2AntibanSettings.devDebug = true; - TIMEOUT = 0; - activity = null; - activityIntensity = null; - category = null; - playStyle = null; - Rs2AntibanSettings.takeMicroBreaks = false; - Rs2AntibanSettings.actionCooldownChance = 0.1; - Rs2AntibanSettings.microBreakChance = 0.1; - Rs2AntibanSettings.microBreakDurationLow = 3; - Rs2AntibanSettings.microBreakDurationHigh = 15; + public static void resetAntibanSettings() { + Rs2AntibanSettings.reset(); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2AntibanSettings.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2AntibanSettings.java index 0d4a89fca9..e6009cc913 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2AntibanSettings.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2AntibanSettings.java @@ -1,5 +1,78 @@ package net.runelite.client.plugins.microbot.util.antiban; +/** + * Provides configuration settings for the anti-ban system used by various plugins within the bot framework. + * + *

+ * The Rs2AntibanSettings class contains a collection of static fields that define behaviors + * and settings related to anti-ban mechanisms. These settings control how the bot simulates human-like + * behavior to avoid detection during automated tasks. Each setting adjusts a specific aspect of the + * anti-ban system, including break patterns, mouse movements, play style variability, and other behaviors + * designed to mimic natural human interaction with the game. + *

+ * + *

Main Features:

+ *
    + *
  • Action Cooldowns: Controls the cooldown behavior of actions, including random intervals + * and non-linear patterns.
  • + *
  • Micro Breaks: Defines settings for taking small breaks at random intervals to simulate human pauses.
  • + *
  • Play Style Simulation: Includes variables to simulate different play styles, attention span, + * and behavioral variability to create a more realistic user profile.
  • + *
  • Mouse Movements: Settings to control mouse behavior, such as moving off-screen or randomly, + * mimicking natural user actions.
  • + *
  • Dynamic Behaviors: Provides options to dynamically adjust activity intensity and behavior + * based on context and time of day.
  • + *
+ * + *

Fields:

+ *
    + *
  • actionCooldownActive: Tracks whether action cooldowns are currently active.
  • + *
  • microBreakActive: Indicates if a micro break is currently active.
  • + *
  • antibanEnabled: Globally enables or disables the anti-ban system.
  • + *
  • usePlayStyle: Determines whether play style simulation is active.
  • + *
  • randomIntervals: Enables random intervals between actions to avoid detection.
  • + *
  • simulateFatigue: Simulates user fatigue by introducing delays or slower actions.
  • + *
  • simulateAttentionSpan: Simulates varying levels of user attention over time.
  • + *
  • behavioralVariability: Adds variability to actions to simulate a human's inconsistency.
  • + *
  • nonLinearIntervals: Activates non-linear time intervals between actions.
  • + *
  • profileSwitching: Simulates user behavior switching profiles at intervals.
  • + *
  • timeOfDayAdjust: (TODO) Adjusts behaviors based on the time of day.
  • + *
  • simulateMistakes: Simulates user mistakes, often controlled by natural mouse movements.
  • + *
  • naturalMouse: Enables natural-looking mouse movements.
  • + *
  • moveMouseOffScreen: Moves the mouse off-screen during breaks to simulate user behavior.
  • + *
  • moveMouseRandomly: Moves the mouse randomly to simulate human inconsistency.
  • + *
  • contextualVariability: Adjusts behaviors based on the context of the user's actions.
  • + *
  • dynamicIntensity: Dynamically adjusts the intensity of user actions based on context.
  • + *
  • dynamicActivity: Adjusts activities dynamically based on the user's behavior profile.
  • + *
  • devDebug: Enables debug mode for developers to inspect the anti-ban system's state.
  • + *
  • takeMicroBreaks: Controls whether the bot takes micro breaks at random intervals.
  • + *
  • playSchedule: (TODO) Allows scheduling of playtime based on specific conditions.
  • + *
  • universalAntiban: Applies the same anti-ban settings across all plugins.
  • + *
  • microBreakDurationLow: Minimum duration for micro breaks, in minutes.
  • + *
  • microBreakDurationHigh: Maximum duration for micro breaks, in minutes.
  • + *
  • actionCooldownChance: Probability of triggering an action cooldown.
  • + *
  • microBreakChance: Probability of taking a micro break.
  • + *
  • moveMouseRandomlyChance: Probability of moving the mouse randomly.
  • + *
+ * + *

Usage:

+ *

+ * These settings are typically used by anti-ban mechanisms within various plugins to adjust their behavior + * dynamically based on the user's preferences or to simulate human-like play styles. Developers can adjust + * these fields based on the needs of their specific automation scripts. + *

+ * + *

Example:

+ *
+ * // Enable fatigue simulation and random intervals
+ * Rs2AntibanSettings.simulateFatigue = true;
+ * Rs2AntibanSettings.randomIntervals = true;
+ *
+ * // Set the micro break chance to 20%
+ * Rs2AntibanSettings.microBreakChance = 0.2;
+ * 
+ */ + public class Rs2AntibanSettings { public static boolean actionCooldownActive = false; public static boolean microBreakActive = false; @@ -28,4 +101,35 @@ public class Rs2AntibanSettings { public static double actionCooldownChance = 0.1; // 10% chance of activating the action cooldown by default public static double microBreakChance = 0.1; // 10% chance of taking a micro break by default public static double moveMouseRandomlyChance = 0.1; // 10% chance of moving the mouse randomly by default + + // reset method to reset all settings to default values + public static void reset() { + actionCooldownActive = false; + microBreakActive = false; + antibanEnabled = true; + usePlayStyle = false; + randomIntervals = false; + simulateFatigue = false; + simulateAttentionSpan = false; + behavioralVariability = false; + nonLinearIntervals = false; + profileSwitching = false; + timeOfDayAdjust = false; + simulateMistakes = false; + naturalMouse = false; + moveMouseOffScreen = false; + moveMouseRandomly = false; + contextualVariability = false; + dynamicIntensity = false; + dynamicActivity = false; + devDebug = false; + takeMicroBreaks = false; + playSchedule = false; + universalAntiban = false; + microBreakDurationLow = 3; + microBreakDurationHigh = 15; + actionCooldownChance = 0.1; + microBreakChance = 0.1; + moveMouseRandomlyChance = 0.1; + } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/Activity.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/Activity.java index e3845fbdf3..688390f28f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/Activity.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/Activity.java @@ -3,6 +3,58 @@ import lombok.Getter; import net.runelite.api.Skill; +/** + * The Activity enum represents various activities that the player can perform in the game, each associated with a specific + * category and intensity level. + * + *

+ * Activities range from general skilling and combat tasks to specific high-intensity boss fights or complex tasks. Each activity + * is linked to a Category that defines the type of activity (e.g., skilling, combat), and an ActivityIntensity + * that controls how aggressive or passive the bot's behavior should be during that activity. + *

+ * + *

Main Features:

+ *
    + *
  • Wide Range of Activities: Covers general skilling activities such as mining, cooking, and woodcutting, + * as well as high-intensity tasks like boss fights and combat encounters.
  • + *
  • Category Association: Each activity is linked to a Category that helps the bot identify the + * type of activity and how to handle it.
  • + *
  • Intensity Levels: Activities have different intensity levels based on their complexity and demands, which are + * represented by ActivityIntensity. For example, combat activities tend to have higher intensity, while + * skilling tasks may be more moderate or low intensity.
  • + *
  • Skill Mapping: The enum provides a method to map game skills to the appropriate general activity, + * ensuring that the bot behaves correctly when training a particular skill.
  • + *
+ * + *

Usage:

+ *

+ * The Activity enum is used to control bot behavior based on the type of activity the player is performing. + * Each activity informs the bot of the type of task being executed, its intensity, and the corresponding category to ensure + * appropriate behavior, such as taking breaks or adjusting actions dynamically. + *

+ * + *

Example:

+ *
+ * Rs2Antiban.setActivity(Activity activity);
+ * Rs2Antiban.getCategory();
+ * Rs2Antiban.setActivityIntensity(ActivityIntensity activityIntensity);
+ * 
+ * + *

Skill-Based Activity Mapping:

+ *

+ * The fromSkill(Skill skill) method maps in-game skills (e.g., Mining, Fishing, Combat) to general activities, + * allowing the bot to adjust its behavior based on the skill currently being trained. This ensures that the bot + * behaves consistently for all general activities related to a specific skill. + *

+ * + *

Activity Categories and Intensities:

+ *

+ * Each activity belongs to a Category that defines the overall type of the activity (e.g., skilling, combat, + * collecting), and each is assigned an ActivityIntensity level, which determines the speed and aggression of + * the bot's actions during that activity. + *

+ */ + public enum Activity { GENERAL_MINING("General Mining", Category.SKILLING_MINING, ActivityIntensity.LOW), GENERAL_SMITHING("General Smithing", Category.SKILLING_SMITHING, ActivityIntensity.LOW), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/ActivityIntensity.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/ActivityIntensity.java index d92891b4aa..1edefbbd6a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/ActivityIntensity.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/ActivityIntensity.java @@ -3,6 +3,56 @@ import lombok.Getter; import net.runelite.api.Skill; +/** + * The ActivityIntensity enum represents different levels of intensity for player activities, + * each associated with a specific play style and behavior pattern. + * + *

+ * Activity intensities are used to simulate varying levels of focus and action frequency during bot operation. + * Each intensity level controls the frequency and amplitude of actions, which corresponds to how aggressive or passive + * the bot's behavior is during different tasks. The intensity also determines the corresponding play style, which + * adjusts the bot's overall behavior to better mimic human variability. + *

+ * + *

Main Features:

+ *
    + *
  • Intensity Levels: Ranges from "Very Low" to "Extreme," with varying levels of action frequency and amplitude.
  • + *
  • Play Style Association: Each intensity is linked to a PlayStyle, which defines the overall behavior + * of the bot during specific activities.
  • + *
  • Skill Mapping: Different skills are mapped to specific intensity levels, with combat-related skills generally + * assigned higher intensities, while skilling activities are mapped to lower intensities.
  • + *
  • Randomization: Provides a method to generate a random activity intensity, adding unpredictability to bot behavior.
  • + *
+ * + *

Usage:

+ *

+ * The ActivityIntensity enum is used to dynamically adjust the bot's behavior based on the intensity of the + * player's activity. Higher intensity levels correspond to more aggressive actions, while lower intensity levels simulate + * slower, more deliberate behavior. This allows the bot to adapt to different types of activities, from combat to skilling. + *

+ * + *

Example:

+ *
+ * ActivityIntensity currentIntensity = ActivityIntensity.HIGH;
+ * PlayStyle associatedStyle = currentIntensity.getPlayStyle();
+ * double actionFrequency = currentIntensity.getFrequency();
+ * 
+ * + *

Skill-Based Intensity:

+ *

+ * The fromSkill(Skill skill) method maps in-game skills to specific activity intensity levels. For example, + * combat-related skills such as Attack, Defence, and Ranged are mapped to higher intensities, while skills like Cooking, + * Fishing, and Crafting are mapped to lower intensities. This allows the bot to adjust its behavior based on the current + * skill being trained. + *

+ * + *

Random Intensity:

+ *

+ * The random() method provides a way to select a random activity intensity, introducing unpredictability + * and making the bot's actions appear more human-like by varying the intensity over time. + *

+ */ + public enum ActivityIntensity { VERY_LOW("Very Low", 0.6, 1.8, PlayStyle.PASSIVE), LOW("Low", 0.4, 1.5, PlayStyle.CAUTIOUS), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/Category.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/Category.java index f8c9189328..0f0ee5b456 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/Category.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/Category.java @@ -8,6 +8,59 @@ import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; import net.runelite.client.plugins.microbot.util.player.Rs2Player; +/** + * The Category enum represents different categories of player activities, such as combat, skilling, and processing. + * + *

+ * Each category is associated with a specific type of activity and contains logic to determine whether the player is "busy" + * based on the current game state. This is used to control bot behavior and ensure it adapts to the player's activity, + * pausing or adjusting actions when the player is engaged in a task. + *

+ * + *

Main Features:

+ *
    + *
  • Categories for Various Activities: Includes categories such as combat, skilling (e.g., fishing, cooking, magic), + * processing, and collecting, each with its own logic to determine if the player is busy.
  • + *
  • Custom Busy Logic: Each category overrides the isBusy() method, providing custom logic + * for determining if the player is engaged in the respective activity.
  • + *
  • Bot Activity Control: The bot uses these categories to manage when it should take action or pause, based + * on whether the player is currently busy performing an activity.
  • + *
+ * + *

Usage:

+ *

+ * The Category enum is used within the bot's logic to monitor the player's activities and determine + * if the bot should continue executing actions or wait for the player to finish their current task. Each category + * provides specific logic to handle different types of activities such as combat, skilling, and crafting. + * This class is used by the anti-ban system to make sure the action cooldown is not counting down while the player is busy. + *

+ * + *

Example:

+ *
+ * Category currentCategory = Category.SKILLING_COOKING;
+ * if (currentCategory.isBusy()) {
+ *     // The player is busy cooking, so the bot may pause actions.
+ * } else {
+ *     // The player is idle, and the bot can continue with the next task.
+ * }
+ * 
+ * + *

Customization:

+ *

+ * Each category overrides the isBusy() method to implement custom logic for checking if the player is engaged + * in a specific task. For example, the COMBAT_MID category checks if the player is in combat, while the + * SKILLING_COOKING category checks if the player is currently cooking. Some categories are not fully implemented + * and include TODO notes for further customization based on game-specific conditions. + *

+ * + *

Development Notes:

+ *

+ * Several categories contain TODO notes indicating that additional logic may be required to accurately determine if the player is busy. + * These categories currently rely on simple checks, such as whether the player is animating or if the inventory is full, + * but may need further refinement based on specific interactions or game mechanics. + *

+ */ + public enum Category { COMBAT_MID("Combat/Mid") { @Override @@ -90,7 +143,7 @@ public boolean isBusy() { SKILLING_THIEVING("Skilling/Thieving") { @Override public boolean isBusy() { - return !Rs2Player.isAnimating() || Rs2Inventory.isFull(); + return !Rs2Player.isAnimating(); } }, SKILLING("Skilling") { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/PlaySchedule.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/PlaySchedule.java index bc5b765661..5c0a2f3e6b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/PlaySchedule.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/PlaySchedule.java @@ -3,6 +3,40 @@ import java.time.Duration; import java.time.LocalTime; +/** + * The PlaySchedule enum represents predefined play time schedules for the bot, simulating real-life play patterns. + * + *

+ * Each schedule defines a specific time range during which the bot is active, allowing for more human-like + * behavior by simulating different play habits throughout the day. These schedules are managed and enforced + * by the Break Handler plugin, which controls when the bot should be active or inactive based on the selected schedule. + *

+ * + *

Main Features:

+ *
    + *
  • Predefined Time Ranges: Each schedule has a start and end time that corresponds to specific periods of the day + * (e.g., morning, afternoon, evening).
  • + *
  • Varied Durations: Schedules vary in length, offering short, medium, and long play periods for each time of day.
  • + *
  • Realistic Break Simulation: The bot automatically checks whether it is within or outside the defined play schedule, + * allowing for breaks and simulating a more realistic user pattern. This is managed by the Break Handler plugin.
  • + *
+ * + *

Usage:

+ *

+ * The PlaySchedule enum is used to control when the bot is active during specific periods of the day. + * By adhering to these schedules, the bot avoids continuous operation and mimics a more realistic user behavior. + * The Break Handler plugin ensures that the bot respects the defined schedules and manages any necessary pauses. + *

+ * + *

Schedule Checks:

+ *

+ * The isOutsideSchedule() method determines whether the current time is outside of the defined play schedule. + * This is useful for ensuring that the bot only runs during the specified time periods, as controlled by the Break Handler plugin. + * The timeUntilNextSchedule() method calculates how long the bot must wait until the next play period starts, + * helping to schedule breaks and restarts efficiently. + *

+ */ + public enum PlaySchedule { SHORT_MORNING(LocalTime.of(8, 0), LocalTime.of(9, 0)), MEDIUM_MORNING(LocalTime.of(7, 0), LocalTime.of(10, 0)), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/PlayStyle.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/PlayStyle.java index 0fc52c1b9a..1a00e876dd 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/PlayStyle.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/enums/PlayStyle.java @@ -10,6 +10,52 @@ import java.util.Random; import java.util.concurrent.ThreadLocalRandom; +/** + * The PlayStyle enum represents different behavioral profiles for bot activity, + * each simulating varying levels of human-like behavior by adjusting the frequency + * and intensity of actions over time. + * + *

+ * Each play style has its own characteristics, such as how aggressive or passive the bot's actions are, + * the length of breaks between actions, and the overall attention span. The enum offers profiles ranging + * from highly aggressive behavior with minimal breaks to passive styles with long pauses between actions. + * It is used to simulate different user behavior patterns and make the bot's actions less predictable. + *

+ * + *

Main Features:

+ *
    + *
  • Profiles: Ranges from "Extreme Aggressive" to "Passive," with varying tick intervals and breaks.
  • + *
  • Dynamic Evolution: Play styles can evolve over time, adjusting action intervals using sine wave patterns + * to simulate natural human variability.
  • + *
  • Attention Span: Each play style includes a simulated attention span, after which the profile may switch + * to another one to mimic shifts in focus or behavior.
  • + *
  • Profile Switching: Play styles can switch up or down based on probability, simulating refocusing and + * different user habits.
  • + *
  • Randomization: Random play style generation for unpredictable behavior and more realistic automation.
  • + *
+ * + *

Usage:

+ *

+ * The PlayStyle enum is primarily used to control how the bot behaves in different situations, + * allowing it to alternate between aggressive, moderate, or cautious behaviors dynamically. This helps the bot + * to avoid detection by simulating varied human actions over time. + *

+ * + *

Example:

+ *
+ * PlayStyle currentStyle = PlayStyle.AGGRESSIVE;
+ * Rs2Antiban.playStyle = currentStyle; // Set the current play style
+ * Rs2Antiban.playStyle.evolvePlayStyle(); // Dynamically evolve the play style (evolve is called each time Rs2Antiban.actionCooldown() is triggered)
+ * 
+ * + *

Attention Span Simulation:

+ *

+ * Each play style tracks an attention span, representing the time the bot will continue with the current profile + * before switching to a new one. This ensures that the bot does not remain in a single behavior pattern for too long, + * helping it to mimic real human behavior. + *

+ */ + @Slf4j public enum PlayStyle { EXTREME_AGGRESSIVE("Extreme Aggressive", 1, 3, 2, 0.00), // Almost no break between inputs diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/math/Rs2Random.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/math/Rs2Random.java index ccf15d71c7..dbe800d8e1 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/math/Rs2Random.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/math/Rs2Random.java @@ -6,23 +6,131 @@ import java.awt.*; import java.util.Random; +/** + * The {@code Rs2Random} class provides a variety of random number generation methods + * aimed at simulating human-like randomness and behavior in bots. This includes methods + * for generating random numbers using Gaussian/Normal distributions, truncated ranges, + * skewed distributions, and methods that generate random points on the game screen. + * + *

+ * The randomness generated by this class is intended to mimic the variability in human + * actions, reducing the predictability of the bot's behavior and avoiding detection + * by anti-cheat systems. + *

+ * + *

Key Features:

+ *
    + *
  • Gaussian/Normal random number generation with custom mean and deviation.
  • + *
  • Truncated Gaussian distribution for generating random numbers within specific bounds.
  • + *
  • Skewed random number generation towards a certain mode within a specified range.
  • + *
  • Methods for generating random points on the game screen, weighted around a center or skewed towards a direction.
  • + *
  • Random wait times and dice roll simulations based on probability percentages.
  • + *
+ * + *

Main Methods:

+ *
    + *
  • {@code nzRandom()}: Returns a non-zero random double value, ensuring that the result + * is greater than a very small number.
  • + *
  • {@code gaussRand(double mean, double dev)}: Generates a random value based on a + * Gaussian (normal) distribution with the specified mean and standard deviation.
  • + *
  • {@code truncatedGauss(double left, double right, double cutoff)}: Generates a random + * number within the given range using a truncated Gaussian distribution.
  • + *
  • {@code skewedRand(double mode, double lo, double hi, double cutoff)}: Generates a random + * number skewed towards the specified mode within a specified range.
  • + *
  • {@code normalRange(double min, double max, double cutoff)}: Generates a random number + * within a range, with a bias towards the mean.
  • + *
  • {@code randomPoint(Point mean, int maxRad, double cutoff)}: Generates a random point + * on the screen, weighted around a central point within a maximum radius.
  • + *
  • {@code randomPoint(Rectangle rect, double cutoff)}: Generates a random point within the + * bounds of the given rectangle, biased towards the center.
  • + *
  • {@code dice(double chancePercent)}: Simulates a dice roll, returning true if the randomly + * generated number is within the chance percentage.
  • + *
  • {@code wait(double min, double max, EWaitDir weight)}: Simulates a wait with a random + * duration, biased towards the mean, left, or right of the given range.
  • + *
  • {@code waitEx(double mean, double dev)}: Waits for a random duration based on a Gaussian + * distribution.
  • + *
+ * + *

Use Cases:

+ *
    + *
  • Generate human-like randomness in bot scripts for actions such as clicking, waiting, + * or moving the mouse.
  • + *
  • Simulate random wait times to avoid predictable patterns in bot behavior.
  • + *
  • Generate random points within a specified area for simulating mouse movements.
  • + *
  • Apply randomness in decision-making processes, such as determining whether to perform + * an action based on a random chance.
  • + *
+ * + *

Dependencies:

+ *
    + *
  • {@code Global.sleep(int)}: Used in the {@code systemWait(long)} method to simulate delays.
  • + *
  • {@code Point} and {@code Rectangle}: Utilized for geometric calculations related to screen coordinates.
  • + *
+ * + *

Example Usage:

+ *
+ * // Example: Generate a random point on the screen within a rectangle, biased towards the center
+ * Point randomPoint = Rs2Random.randomPoint(new Rectangle(100, 100, 300, 300), 2.0);
+ *
+ * // Example: Simulate a wait with random duration biased towards the middle of the range
+ * Rs2Random.wait(1000, 2000, Rs2Random.EWaitDir.wdMean);
+ * 
+ * + *

Limitations:

+ *
    + *
  • The class heavily relies on randomness, which may produce unexpected results if not handled carefully.
  • + *
  • Randomness might not always be sufficient to simulate real human behavior and may require tuning for specific use cases.
  • + *
+ * + *

Enum:

+ *
    + *
  • {@code EWaitDir}: Defines the direction of bias for random waits, including left, mean, and right skew.
  • + *
+ * + *

Private Helpers:

+ *
    + *
  • {@code systemWait(long time)}: Invokes a system-level wait for the specified time in milliseconds.
  • + *
  • {@code rotatePoint(Point point, double angle, double originX, double originY)}: Rotates a point around an origin by a specified angle.
  • + *
+ */ + public class Rs2Random { private static final double GAUSS_CUTOFF = 4.0; private static final Random RANDOM = new Random(); - // Non-zero random + /** + * Returns a non-zero random double value. + * Ensures that the result is always greater than a very small number (1.0e-320), + * preventing the generation of an exact zero. + * + * @return A non-zero random double value. + */ public static double nzRandom() { return Math.max(RANDOM.nextDouble(), 1.0e-320); } - // Generates a random gaussian/normal number + /** + * Generates a random value based on a Gaussian (normal) distribution with a specified mean and standard deviation. + * + * @param mean The mean (center) value of the distribution. + * @param dev The standard deviation of the distribution. + * @return A random double value from the Gaussian distribution. + */ public static double gaussRand(double mean, double dev) { double len = dev * Math.sqrt(-2 * Math.log(nzRandom())); return mean + len * Math.cos(2 * Math.PI * RANDOM.nextDouble()); } - // Generates a truncated gaussian/normal number within the given range + /** + * Generates a random number within the given range using a truncated Gaussian distribution. + * This ensures that the value is within the bounds of the left and right range. + * + * @param left The minimum bound of the range. + * @param right The maximum bound of the range. + * @param cutoff The cutoff value to restrict extreme values. Defaults to GAUSS_CUTOFF(4) if less than or equal to 0. + * @return A random double value within the specified range. + */ public static double truncatedGauss(double left, double right, double cutoff) { if (cutoff <= 0) { cutoff = GAUSS_CUTOFF; @@ -36,11 +144,28 @@ public static double truncatedGauss(double left, double right, double cutoff) { return result / cutoff * (right - left) + left; } + /** + * Generates a random long value within the given range using a truncated Gaussian distribution. + * + * @param left The minimum bound of the range. + * @param right The maximum bound of the range. + * @param cutoff The cutoff value to restrict extreme values. + * @return A random long value within the specified range. + */ public static long truncatedGauss(long left, long right, double cutoff) { return Math.round(truncatedGauss((double) left, (double) right, cutoff)); } - // Random skewed distribution generation + /** + * Generates a random number skewed towards the specified mode within a specified range. + * This allows for a biased distribution where the values tend to cluster around the mode. + * + * @param mode The central value around which the distribution is skewed. + * @param lo The lower bound of the range. + * @param hi The upper bound of the range. + * @param cutoff The cutoff value to restrict extreme values. Defaults to GAUSS_CUTOFF(4) if less than or equal to 0. + * @return A random double value skewed towards the mode. + */ public static double skewedRand(double mode, double lo, double hi, double cutoff) { if (cutoff <= 0) { cutoff = GAUSS_CUTOFF; @@ -59,11 +184,28 @@ public static double skewedRand(double mode, double lo, double hi, double cutoff return result / cutoff * (top - mode) + mode; } + /** + * Generates a random long value skewed towards the specified mode within a specified range. + * + * @param mode The central value around which the distribution is skewed. + * @param lo The lower bound of the range. + * @param hi The upper bound of the range. + * @param cutoff The cutoff value to restrict extreme values. + * @return A random long value skewed towards the mode. + */ public static long skewedRand(long mode, long lo, long hi, double cutoff) { return Math.round(skewedRand((double) mode, (double) lo, (double) hi, cutoff)); } - // Generates a random float in the given range, weighted towards the mean + /** + * Generates a random number within the specified range, biased towards the mean. + * The distribution has a higher likelihood of generating numbers closer to the midpoint of the range. + * + * @param min The minimum bound of the range. + * @param max The maximum bound of the range. + * @param cutoff The cutoff value to restrict extreme values. Defaults to GAUSS_CUTOFF(4) if less than or equal to 0. + * @return A random double value within the specified range, biased towards the middle. + */ public static double normalRange(double min, double max, double cutoff) { if (cutoff <= 0) { cutoff = GAUSS_CUTOFF; @@ -79,6 +221,14 @@ public static double normalRange(double min, double max, double cutoff) { } } + /** + * Generates a random long value within the specified range, biased towards the mean. + * + * @param min The minimum bound of the range. + * @param max The maximum bound of the range. + * @param cutoff The cutoff value to restrict extreme values. Defaults to GAUSS_CUTOFF(4) if less than or equal to 0. + * @return A random long value within the specified range, biased towards the middle. + */ public static long normalRange(long min, long max, double cutoff) { if (cutoff <= 0) { cutoff = GAUSS_CUTOFF; @@ -94,14 +244,29 @@ public static long normalRange(long min, long max, double cutoff) { } } - // Generates a random point weighted around Mean with a max distance from Mean defined by MaxRad + /** + * Generates a random point on the screen, weighted around a central point (mean) within a maximum radius. + * The point is selected to simulate human-like randomness in mouse movement or other actions. + * + * @param mean The central point to weight the randomness around. + * @param maxRad The maximum radius away from the central point. + * @param cutoff The cutoff value for restricting extreme values. Defaults to GAUSS_CUTOFF(4) if less than or equal to 0. + * @return A random point near the central point, within the specified radius. + */ public static Point randomPoint(Point mean, int maxRad, double cutoff) { int x = (int) normalRange(mean.getX() - maxRad, mean.getX() + maxRad, cutoff); int y = (int) normalRange(mean.getY() - maxRad, mean.getY() + maxRad, cutoff); return new Point(x, y); } - // Generates a random point within the bounds of the given rectangle, weighted towards the middle + /** + * Generates a random point within the bounds of the given rectangle, biased towards the center. + * This method is useful for simulating human-like randomness in screen interactions. + * + * @param rect The rectangular area within which to generate the random point. + * @param cutoff The cutoff value for restricting extreme values. Defaults to GAUSS_CUTOFF(4) if less than or equal to 0. + * @return A random point within the rectangle, biased towards the middle. + */ public static Point randomPoint(Rectangle rect, double cutoff) { double x1 = rect.getX(); double y1 = rect.getY(); @@ -114,7 +279,15 @@ public static Point randomPoint(Rectangle rect, double cutoff) { return rotatePoint(new Point(x, y), a, (x2 + x1) / 2 + RANDOM.nextDouble() - 0.5, (y2 + y1) / 2 + RANDOM.nextDouble() - 0.5); } - // Generates a random point in the bounds of the given box, the point generated is skewed towards From-point + /** + * Generates a random point within the bounds of a rectangle, skewed towards a specified 'from' point. + * Useful for simulating more human-like randomness in actions such as dragging or moving the mouse. + * + * @param from The point to bias the random point generation towards. + * @param rect The rectangular area within which to generate the random point. + * @param force A multiplier that defines how strongly the point should be skewed towards the 'from' point. + * @return A random point within the rectangle, skewed towards the 'from' point. + */ public static Point randomPointEx(Point from, Rectangle rect, double force) { Point p = from; p = new Point(Math.min(Math.max(p.getX(), (int) rect.getX()), (int) (rect.getX() + rect.getWidth())), Math.min(Math.max(p.getY(), (int) rect.getY()), (int) (rect.getY() + rect.getHeight()))); @@ -129,12 +302,24 @@ public static Point randomPointEx(Point from, Rectangle rect, double force) { return new Point(resultX, resultY); } - // Dice function: Generates a random number and returns true if within the chance percentage + /** + * Simulates a dice roll, returning true if the random number generated falls within the given chance percentage. + * + * @param chancePercent The percentage chance of returning true. Must be between 0 and 100. + * @return True if the random number falls within the chance, false otherwise. + */ public static boolean dice(double chancePercent) { return RANDOM.nextDouble() < (chancePercent <= 0.99 ? chancePercent * 100 : chancePercent) / 100; } - // Wait function weighted towards the mean of Min and Max + /** + * Simulates a wait with a random duration, biased towards the mean, left, or right of the given range. + * This method is useful for introducing randomness in bot actions to reduce predictability. + * + * @param min The minimum wait time in milliseconds. + * @param max The maximum wait time in milliseconds. + * @param weight The direction of bias for the wait time (left, mean, or right skew). + */ public static void wait(double min, double max, EWaitDir weight) { switch (weight) { case wdLeft: @@ -149,15 +334,47 @@ public static void wait(double min, double max, EWaitDir weight) { } } - // WaitEx function: waits with regular Gaussian randomness + /** + * Simulates a wait with a random duration, biased towards the left side of the given range. + * + * @param min The minimum wait time in milliseconds. + * @param max The maximum wait time in milliseconds. + */ + public static void wait(int min, int max) { + wait(min, max, EWaitDir.wdLeft); + } + + /** + * Waits for a random duration based on a Gaussian distribution. + * The wait time is calculated using a normal distribution with the specified mean and standard deviation. + * + * @param mean The mean wait time in milliseconds. + * @param dev The standard deviation of the wait time. + */ public static void waitEx(double mean, double dev) { wait(Math.abs(Math.round(gaussRand(mean, dev))), 0, EWaitDir.wdMean); } + /** + * Pauses the execution for a specified amount of time in milliseconds. + * This method is a system-level wait used for simulating delays in bot actions. + * + * @param time The duration to wait in milliseconds. + */ private static void systemWait(long time) { Global.sleep((int) time); } + /** + * Rotates a given point around a specified origin by a certain angle. + * This method is used for calculating rotated positions in 2D space. + * + * @param point The point to be rotated. + * @param angle The angle to rotate the point, in radians. + * @param originX The x-coordinate of the origin point. + * @param originY The y-coordinate of the origin point. + * @return A new point representing the rotated coordinates. + */ private static Point rotatePoint(Point point, double angle, double originX, double originY) { double sin = Math.sin(angle); double cos = Math.cos(angle); @@ -168,6 +385,7 @@ private static Point rotatePoint(Point point, double angle, double originX, doub return new Point(newX, newY); } + enum EWaitDir { wdLeft, wdMean, wdRight } From 93a800a947cadb48aad91a359162765b5f825565 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sat, 17 Aug 2024 21:45:28 +0200 Subject: [PATCH 34/52] Rs2UiHelper fix --- .../plugins/microbot/util/misc/Rs2UiHelper.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java index d8413bd1cc..a9c5aef8d7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/misc/Rs2UiHelper.java @@ -27,6 +27,9 @@ public static boolean isRectangleWithinViewport(Rectangle rectangle) { public static Point getClickingPoint(Rectangle rectangle, boolean randomize) { if (rectangle.getX() == 1 && rectangle.getY() == 1) return new Point(1, 1); if (rectangle.getX() == 0 && rectangle.getY() == 0) return new Point(1, 1); + + if (!randomize) return new Point((int) rectangle.getCenterX(), (int) rectangle.getCenterY()); + //check if mouse is already within the rectangle and return current position if (Rs2AntibanSettings.naturalMouse) { java.awt.Point mousePos = Microbot.getMouse().getMousePosition(); @@ -58,9 +61,9 @@ public static Rectangle getActorClickbox(Actor actor) { //check if any of the values are negative and return a new rectangle with positive values assert clickbox != null; - if (clickbox.getBounds().getX() < 0 || clickbox.getBounds().getY() < 0 || clickbox.getBounds().getWidth() < 0 || clickbox.getBounds().getHeight() < 0) { - return new Rectangle((int) Math.abs(clickbox.getBounds().getX()), (int) Math.abs(clickbox.getBounds().getY()), (int) Math.abs(clickbox.getBounds().getWidth()), (int) Math.abs(clickbox.getBounds().getHeight())); - } +// if (clickbox.getBounds().getX() < 0 || clickbox.getBounds().getY() < 0 || clickbox.getBounds().getWidth() < 0 || clickbox.getBounds().getHeight() < 0) { +// return new Rectangle((int) Math.abs(clickbox.getBounds().getX()), (int) Math.abs(clickbox.getBounds().getY()), (int) Math.abs(clickbox.getBounds().getWidth()), (int) Math.abs(clickbox.getBounds().getHeight())); +// } return new Rectangle(clickbox.getBounds()); } @@ -73,9 +76,9 @@ public static Rectangle getObjectClickbox(TileObject object) { //check if any of the values are negative and return a new rectangle with positive values assert clickbox != null; - if (clickbox.getBounds().getX() < 0 || clickbox.getBounds().getY() < 0 || clickbox.getBounds().getWidth() < 0 || clickbox.getBounds().getHeight() < 0) { - return new Rectangle((int) Math.abs(clickbox.getBounds().getX()), (int) Math.abs(clickbox.getBounds().getY()), (int) Math.abs(clickbox.getBounds().getWidth()), (int) Math.abs(clickbox.getBounds().getHeight())); - } +// if (clickbox.getBounds().getX() < 0 || clickbox.getBounds().getY() < 0 || clickbox.getBounds().getWidth() < 0 || clickbox.getBounds().getHeight() < 0) { +// return new Rectangle((int) Math.abs(clickbox.getBounds().getX()), (int) Math.abs(clickbox.getBounds().getY()), (int) Math.abs(clickbox.getBounds().getWidth()), (int) Math.abs(clickbox.getBounds().getHeight())); +// } return new Rectangle(clickbox.getBounds()); } From 988d4bb5cd5a327f85501251898f07bcc66a7c6d Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sat, 17 Aug 2024 22:02:33 +0200 Subject: [PATCH 35/52] Antiban bugfix --- .../client/plugins/microbot/util/antiban/AntibanPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java index e4d34bbd3a..305051f343 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java @@ -242,7 +242,7 @@ public void onGameTick(GameTick event) { @Subscribe public void onStatChanged(StatChanged statChanged) { - if (!Rs2AntibanSettings.antibanEnabled) { + if (!Rs2AntibanSettings.antibanEnabled && !Rs2AntibanSettings.universalAntiban) { return; } From 4df312fb4c0dea51a84e6b1134d5697f9bdb7fca Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sun, 18 Aug 2024 13:34:17 +0200 Subject: [PATCH 36/52] duck resource --- .../microbot/util/antiban/walkingduck.gif | Bin 0 -> 9156 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/walkingduck.gif diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/walkingduck.gif b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/walkingduck.gif new file mode 100644 index 0000000000000000000000000000000000000000..2a6526d7d0810bcceb12513b9657206b9f1c1ba2 GIT binary patch literal 9156 zcmZ?wbhEHbRA5kG_|Cxa|347@U)P}c|HaNfM}rs`7*=~o{BQC6e{k`oc=g-M%z+}K zU^E1VYX~U*=k{|A33hf2a5d61U}gmRQ}HJYrzDWp0r?q}O&BEJ2<6^$a+e{`bSao%|z37gRfGrv!t&)rt zSG*h=BPUA8SN+*hxaQ{O_=8*)onZ%}cVs=^<)isP_1=nF`**Sr!W+-GW>+5iQXP>O zf7I-;oDWZB_T!dh`P(@^w#+4zaEG+$ zmJo+28dqAxT)8fs3TkZX7nRL>(qXtda4f)6N#y7S&isF1)?F+?!oY_gBG{u2B6p z8Hoj1O&~yBMy839(xA#6nB&bS zLo4@0;Y~R+g);ot)^XO9WPVD$(`w1QU*^H54Tp}HDqCO64lj7JX|n#U*6K=Nj?Z^) zI$~Y?`R?xdg56?s%`@HB*jX}h6qq`O-P~+yHk#3KME0og$PEFGTi7$Ygki(cZbl6u z9)$&q-5DiKi$GO7!vqmX)qZkywa{b%1 z!sks{`SsnQ#j=r;-u(Xdz;~Z9`<5@iL)9M5)sa*_@=IdV?ad`8ou*}O-|+B^yzREB zJm$rBcP;tf#`jiI->uull#j>3)FJ$4;z~7!Zfl+QZH$bg_4>$7*P}ki8Uh?QhC_A_ z?MZVD@?9`(#jyW`-p&tk+z9 wh-Y^6_nh86@@y;&)+0A&M}0mT0;3@?8UmvsFd71*Aut*OqaiRF0;3@S09~!L+W-In literal 0 HcmV?d00001 From 7581731c3b7440f2248fc732316ba71dcbec0dd5 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sun, 18 Aug 2024 13:35:06 +0200 Subject: [PATCH 37/52] Antiban plugin panel update --- .../microbot/util/antiban/AntibanPlugin.java | 5 +- .../util/antiban/AntibanPluginPanel.java | 341 ------------------ .../util/antiban/ui/ActivityPanel.java | 64 ++++ .../microbot/util/antiban/ui/CardPanel.java | 25 ++ .../util/antiban/ui/CooldownPanel.java | 73 ++++ .../util/antiban/ui/GeneralPanel.java | 57 +++ .../microbot/util/antiban/ui/MasterPanel.java | 228 ++++++++++++ .../util/antiban/ui/MicroBreakPanel.java | 95 +++++ .../microbot/util/antiban/ui/MousePanel.java | 79 ++++ .../util/antiban/ui/NavigationPanel.java | 52 +++ .../util/antiban/ui/ProfilePanel.java | 50 +++ .../microbot/util/antiban/ui/UiHelper.java | 14 + 12 files changed, 740 insertions(+), 343 deletions(-) delete mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPluginPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/ActivityPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/CardPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/CooldownPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/GeneralPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/MasterPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/MicroBreakPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/MousePanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/NavigationPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/ProfilePanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/UiHelper.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java index 305051f343..9ebb31de7b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java @@ -15,6 +15,7 @@ import net.runelite.client.plugins.microbot.breakhandler.BreakHandlerPlugin; import net.runelite.client.plugins.microbot.util.antiban.enums.Activity; import net.runelite.client.plugins.microbot.util.antiban.enums.ActivityIntensity; +import net.runelite.client.plugins.microbot.util.antiban.ui.MasterPanel; import net.runelite.client.plugins.microbot.util.player.Rs2Player; import net.runelite.client.ui.ClientToolbar; import net.runelite.client.ui.NavigationButton; @@ -141,7 +142,7 @@ public static void performActionBreak() { @Override protected void startUp() throws AWTException { - final AntibanPluginPanel panel = injector.getInstance(AntibanPluginPanel.class); + final MasterPanel panel = injector.getInstance(MasterPanel.class); final BufferedImage icon = ImageUtil.loadImageResource(getClass(), "antiban.png"); navButton = NavigationButton.builder() .tooltip("Antiban") @@ -251,7 +252,7 @@ public void onStatChanged(StatChanged statChanged) { final Integer previous = skillExp.put(skill, exp); if (lastSkillChanged != null && lastSkillChanged.equals(skill)) { - if (Rs2AntibanSettings.universalAntiban && !Rs2AntibanSettings.actionCooldownActive) { + if (Rs2AntibanSettings.universalAntiban && !Rs2AntibanSettings.actionCooldownActive && Rs2Antiban.getActivity() != null) { Rs2Antiban.actionCooldown(); } return; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPluginPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPluginPanel.java deleted file mode 100644 index 40265fe462..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPluginPanel.java +++ /dev/null @@ -1,341 +0,0 @@ -package net.runelite.client.plugins.microbot.util.antiban; - -import net.runelite.client.plugins.microbot.Microbot; -import net.runelite.client.ui.PluginPanel; - -import javax.swing.*; -import java.awt.*; -import java.util.Objects; - -/** - * The AntibanPluginPanel is a user interface panel for configuring anti-ban settings. - * - *

- * This panel allows users to adjust settings related to the anti-ban system, such as enabling micro-breaks, - * adjusting action cooldown probabilities, and configuring behavioral simulations like fatigue or attention span. - * The panel is divided into different categories, each focusing on specific aspects of anti-ban behavior, - * including activity settings, mouse behavior, and cooldown management. - *

- * - *

- * Users can interact with various checkboxes and sliders to tailor the bot's anti-ban features to their preferences, - * making it behave more like a human player during automated tasks. - *

- * - *

Main Features:

- *
    - *
  • Enable or disable anti-ban features like action cooldowns and micro-breaks.
  • - *
  • Customize the bot's behavior with random intervals, dynamic activity, and simulated fatigue.
  • - *
  • Adjust the duration and probability of micro-breaks and action cooldowns.
  • - *
  • Fine-tune mouse behavior, including natural movements and random actions.
  • - *
  • View real-time information about the current play style, activity, and bot status.
  • - *
- * - *

- * This panel is automatically integrated into the bot's user interface and does not require manual initialization by the user. - *

- */ - -public class AntibanPluginPanel extends PluginPanel { - private final JCheckBox isActionCooldownActive = new JCheckBox("Action Cooldown Active"); - private final JCheckBox isMicroBreakActive = new JCheckBox("Micro Break Active"); - private final JCheckBox isEnabled = new JCheckBox("Enabled"); - private final JCheckBox universalAntiban = new JCheckBox("Universal Antiban"); - private final JCheckBox usePlayStyle = new JCheckBox("Use Play Style"); - private final JCheckBox useRandomIntervals = new JCheckBox("Use Random Intervals"); - private final JCheckBox simulateFatigue = new JCheckBox("Simulate Fatigue"); - private final JCheckBox simulateAttentionSpan = new JCheckBox("Simulate Attention Span"); - private final JCheckBox useBehavioralVariability = new JCheckBox("Use Behavioral Variability"); - private final JCheckBox useNonLinearIntervals = new JCheckBox("Use Non-Linear Intervals"); - private final JCheckBox enableProfileSwitching = new JCheckBox("Enable Profile Switching"); - private final JCheckBox adjustForTimeOfDay = new JCheckBox("Adjust For Time Of Day"); - private final JCheckBox simulateMistakes = new JCheckBox("Simulate Mistakes"); - private final JCheckBox useNaturalMouse = new JCheckBox("Use Natural Mouse"); - private final JCheckBox moveMouseOffScreen = new JCheckBox("Move Mouse Off Screen"); - private final JCheckBox moveMouseRandomly = new JCheckBox("Move Mouse Randomly"); - private final JCheckBox useContextualVariability = new JCheckBox("Use Contextual Variability"); - private final JCheckBox dynamicActivityIntensity = new JCheckBox("Dynamic Activity Intensity"); - private final JCheckBox dynamicActivity = new JCheckBox("Dynamic Activity"); - private final JCheckBox devDebug = new JCheckBox("Dev Debug"); - private final JCheckBox takeMicroBreaks = new JCheckBox("Take Micro Breaks"); - private final JCheckBox simulatePlaySchedule = new JCheckBox("Simulate Play Schedule"); - - private final JSlider microBreakDurationLow = new JSlider(1, 10, Rs2AntibanSettings.microBreakDurationLow); - private final JSlider microBreakDurationHigh = new JSlider(1, 30, Rs2AntibanSettings.microBreakDurationHigh); - private final JSlider actionCooldownChance = new JSlider(0, 100, (int) (Rs2AntibanSettings.actionCooldownChance * 100)); - private final JSlider microBreakChance = new JSlider(0, 100, (int) (Rs2AntibanSettings.microBreakChance * 100)); - private final JSlider timeout = new JSlider(0, 60, Rs2Antiban.getTIMEOUT()); - private final JSlider moveMouseRandomlyChance = new JSlider(0, 100, (int) (Rs2AntibanSettings.moveMouseRandomlyChance * 100)); - - private final JLabel microBreakDurationLowLabel = new JLabel("Micro Break Duration Low (min): " + Rs2AntibanSettings.microBreakDurationLow); - private final JLabel microBreakDurationHighLabel = new JLabel("Micro Break Duration High (min): " + Rs2AntibanSettings.microBreakDurationHigh); - private final JLabel actionCooldownChanceLabel = new JLabel("Action Cooldown Chance (%): " + (int) (Rs2AntibanSettings.actionCooldownChance * 100)); - private final JLabel microBreakChanceLabel = new JLabel("Micro Break Chance (%): " + (int) (Rs2AntibanSettings.microBreakChance * 100)); - private final JLabel timeoutLabel = new JLabel("Timeout (min): " + Rs2Antiban.getTIMEOUT()); - private final JLabel moveMouseRandomlyChanceLabel = new JLabel("Random Mouse Movement (%): " + (int) (Rs2AntibanSettings.moveMouseRandomlyChance * 100)); - - // Additional Info Panel - private final JLabel playStyleLabel = new JLabel("Play Style: " + (Rs2Antiban.getPlayStyle() != null ? Rs2Antiban.getPlayStyle().getName() : "null")); - private final JLabel playStyleChangeLabel = new JLabel("Play Style Change: " + (Rs2Antiban.getPlayStyle() != null ? Rs2Antiban.getPlayStyle().getTimeLeftUntilNextSwitch() : "null")); - private final JLabel profileLabel = new JLabel("Category: " + (Rs2Antiban.getCategory() != null ? Rs2Antiban.getCategory().getName() : "null")); - private final JLabel activityLabel = new JLabel("Activity: " + (Rs2Antiban.getActivity() != null ? Rs2Antiban.getActivity().getMethod() : "null")); - private final JLabel activityIntensityLabel = new JLabel("Activity Intensity: " + (Rs2Antiban.getActivityIntensity() != null ? Rs2Antiban.getActivityIntensity().getName() : "null")); - private final JLabel busyLabel = new JLabel("Busy: " + (Rs2Antiban.getCategory() != null ? Rs2Antiban.getCategory().isBusy() : "null")); - - public AntibanPluginPanel() { - setLayout(new BorderLayout()); - - // Create CardLayout and main content panel - CardLayout cardLayout = new CardLayout(); - JPanel cardPanel = new JPanel(cardLayout); - - // Create navigation panel - JPanel navPanel = new JPanel(); - navPanel.setLayout(new BoxLayout(navPanel, BoxLayout.X_AXIS)); - - // Add buttons with icons to navigation panel - addNavButton(navPanel, "General Settings", "general.png", cardPanel, "GeneralSettings"); - addNavButton(navPanel, "Activity Settings", "activity.png", cardPanel, "ActivitySettings"); - addNavButton(navPanel, "Profile Settings", "profile.png", cardPanel, "ProfileSettings"); - addNavButton(navPanel, "Mouse Settings", "mouse.png", cardPanel, "MouseSettings"); - addNavButton(navPanel, "Micro Break Settings", "microbreak.png", cardPanel, "MicroBreakSettings"); - addNavButton(navPanel, "Cooldown Settings", "cooldown.png", cardPanel, "CooldownSettings"); - - // Create and add panels for each card - cardPanel.add(createGeneralSettingsPanel(), "GeneralSettings"); - cardPanel.add(createActivitySettingsPanel(), "ActivitySettings"); - cardPanel.add(createProfileSettingsPanel(), "ProfileSettings"); - cardPanel.add(createMouseSettingsPanel(), "MouseSettings"); - cardPanel.add(createMicroBreakSettingsPanel(), "MicroBreakSettings"); - cardPanel.add(createCooldownSettingsPanel(), "CooldownSettings"); - - // Add navigation and card panels to the main panel - add(navPanel, BorderLayout.NORTH); - add(cardPanel, BorderLayout.CENTER); - - // Add the Info Panel at the bottom - add(createInfoPanel(), BorderLayout.SOUTH); - - setupSliders(); - setupListeners(); - loadSettings(); - } - - private void addNavButton(JPanel navPanel, String tooltip, String iconPath, JPanel cardPanel, String cardName) { - JButton button = new JButton(createIcon(iconPath)); - button.setToolTipText(tooltip); - button.setFocusPainted(false); - button.setBorderPainted(false); - button.setContentAreaFilled(false); - button.addActionListener(e -> { - CardLayout cl = (CardLayout) (cardPanel.getLayout()); - cl.show(cardPanel, cardName); - }); - navPanel.add(button); - } - - private JPanel createGeneralSettingsPanel() { - JPanel panel = createPanel("General Settings"); - addCheckboxesToPanel(panel, isEnabled, universalAntiban, useContextualVariability, devDebug); - return panel; - } - - private JPanel createActivitySettingsPanel() { - JPanel panel = createPanel("Activity Settings"); - addCheckboxesToPanel(panel, usePlayStyle, useRandomIntervals, simulateFatigue, simulateAttentionSpan, useBehavioralVariability, useNonLinearIntervals, dynamicActivityIntensity, dynamicActivity); - return panel; - } - - private JPanel createProfileSettingsPanel() { - JPanel panel = createPanel("Profile Settings"); - addCheckboxesToPanel(panel, enableProfileSwitching, adjustForTimeOfDay, simulatePlaySchedule); - return panel; - } - - private JPanel createMouseSettingsPanel() { - JPanel panel = createPanel("Mouse Settings"); - addCheckboxesToPanel(panel, useNaturalMouse, simulateMistakes, moveMouseOffScreen, moveMouseRandomly); - addSlidersToPanel(panel, moveMouseRandomlyChanceLabel, moveMouseRandomlyChance); - return panel; - } - - private JPanel createMicroBreakSettingsPanel() { - JPanel panel = createPanel("Micro Break Settings"); - addCheckboxesToPanel(panel, isMicroBreakActive, takeMicroBreaks); - addSlidersToPanel(panel, microBreakDurationLowLabel, microBreakDurationLow, microBreakDurationHighLabel, microBreakDurationHigh, microBreakChanceLabel, microBreakChance); - return panel; - } - - private JPanel createCooldownSettingsPanel() { - JPanel panel = createPanel("Cooldown and Timeout Settings"); - addCheckboxesToPanel(panel, isActionCooldownActive); - addSlidersToPanel(panel, actionCooldownChanceLabel, actionCooldownChance, timeoutLabel, timeout); - return panel; - } - - private JPanel createInfoPanel() { - JPanel panel = new JPanel(new GridBagLayout()); - panel.setBorder(BorderFactory.createTitledBorder("Additional Info")); - - GridBagConstraints gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = GridBagConstraints.RELATIVE; - gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(5, 5, 5, 5); - - panel.add(playStyleLabel, gbc); - panel.add(playStyleChangeLabel, gbc); - panel.add(profileLabel, gbc); - panel.add(activityLabel, gbc); - panel.add(activityIntensityLabel, gbc); - panel.add(busyLabel, gbc); - - return panel; - } - - private JPanel createPanel(String title) { - JPanel panel = new JPanel(new GridBagLayout()); - panel.setBorder(BorderFactory.createTitledBorder(title)); - return panel; - } - - private void addCheckboxesToPanel(JPanel panel, JCheckBox... checkBoxes) { - GridBagConstraints gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = GridBagConstraints.RELATIVE; - gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(5, 5, 5, 5); - for (JCheckBox checkBox : checkBoxes) { - panel.add(checkBox, gbc); - } - } - - private void addSlidersToPanel(JPanel panel, JComponent... labelsAndSliders) { - GridBagConstraints gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = GridBagConstraints.RELATIVE; - gbc.anchor = GridBagConstraints.WEST; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(5, 5, 5, 5); - for (JComponent component : labelsAndSliders) { - panel.add(component, gbc); - } - } - - private void setupSliders() { - setupSlider(microBreakDurationLow, 1, 5, 1); - setupSlider(microBreakDurationHigh, 5, 15, 1); - setupSlider(actionCooldownChance, 20, 100, 10); - setupSlider(microBreakChance, 20, 100, 10); - setupSlider(timeout, 10, 60, 5); - setupSlider(moveMouseRandomlyChance, 20, 100, 10); - } - - private void setupSlider(JSlider slider, int majorTickSpacing, int max, int minorTickSpacing) { - slider.setMajorTickSpacing(majorTickSpacing); - slider.setMinorTickSpacing(minorTickSpacing); - slider.setPaintTicks(true); - slider.setPaintLabels(true); - slider.setMinimum(0); - slider.setMaximum(max); - } - - private void setupListeners() { - isActionCooldownActive.addActionListener(e -> Rs2AntibanSettings.actionCooldownActive = isActionCooldownActive.isSelected()); - isMicroBreakActive.addActionListener(e -> Rs2AntibanSettings.microBreakActive = isMicroBreakActive.isSelected()); - isEnabled.addActionListener(e -> Rs2AntibanSettings.antibanEnabled = isEnabled.isSelected()); - universalAntiban.addActionListener(e -> Rs2AntibanSettings.universalAntiban = universalAntiban.isSelected()); - usePlayStyle.addActionListener(e -> Rs2AntibanSettings.usePlayStyle = usePlayStyle.isSelected()); - useRandomIntervals.addActionListener(e -> Rs2AntibanSettings.randomIntervals = useRandomIntervals.isSelected()); - simulateFatigue.addActionListener(e -> Rs2AntibanSettings.simulateFatigue = simulateFatigue.isSelected()); - simulateAttentionSpan.addActionListener(e -> Rs2AntibanSettings.simulateAttentionSpan = simulateAttentionSpan.isSelected()); - useBehavioralVariability.addActionListener(e -> Rs2AntibanSettings.behavioralVariability = useBehavioralVariability.isSelected()); - useNonLinearIntervals.addActionListener(e -> Rs2AntibanSettings.nonLinearIntervals = useNonLinearIntervals.isSelected()); - enableProfileSwitching.addActionListener(e -> Rs2AntibanSettings.profileSwitching = enableProfileSwitching.isSelected()); - adjustForTimeOfDay.addActionListener(e -> Rs2AntibanSettings.timeOfDayAdjust = adjustForTimeOfDay.isSelected()); - simulateMistakes.addActionListener(e -> Rs2AntibanSettings.simulateMistakes = simulateMistakes.isSelected()); - useNaturalMouse.addActionListener(e -> Rs2AntibanSettings.naturalMouse = useNaturalMouse.isSelected()); - moveMouseOffScreen.addActionListener(e -> Rs2AntibanSettings.moveMouseOffScreen = moveMouseOffScreen.isSelected()); - moveMouseRandomly.addActionListener(e -> Rs2AntibanSettings.moveMouseRandomly = moveMouseRandomly.isSelected()); - useContextualVariability.addActionListener(e -> Rs2AntibanSettings.contextualVariability = useContextualVariability.isSelected()); - dynamicActivityIntensity.addActionListener(e -> Rs2AntibanSettings.dynamicIntensity = dynamicActivityIntensity.isSelected()); - dynamicActivity.addActionListener(e -> Rs2AntibanSettings.dynamicActivity = dynamicActivity.isSelected()); - devDebug.addActionListener(e -> Rs2AntibanSettings.devDebug = devDebug.isSelected()); - takeMicroBreaks.addActionListener(e -> Rs2AntibanSettings.takeMicroBreaks = takeMicroBreaks.isSelected()); - simulatePlaySchedule.addActionListener(e -> Rs2AntibanSettings.playSchedule = simulatePlaySchedule.isSelected()); - - microBreakDurationLow.addChangeListener(e -> { - Rs2AntibanSettings.microBreakDurationLow = microBreakDurationLow.getValue(); - microBreakDurationLowLabel.setText("Micro Break Duration Low (min): " + microBreakDurationLow.getValue()); - }); - microBreakDurationHigh.addChangeListener(e -> { - Rs2AntibanSettings.microBreakDurationHigh = microBreakDurationHigh.getValue(); - microBreakDurationHighLabel.setText("Micro Break Duration High (min): " + microBreakDurationHigh.getValue()); - }); - actionCooldownChance.addChangeListener(e -> { - Rs2AntibanSettings.actionCooldownChance = actionCooldownChance.getValue() / 100.0; - actionCooldownChanceLabel.setText("Action Cooldown Chance (%): " + actionCooldownChance.getValue()); - }); - microBreakChance.addChangeListener(e -> { - Rs2AntibanSettings.microBreakChance = microBreakChance.getValue() / 100.0; - microBreakChanceLabel.setText("Micro Break Chance (%): " + microBreakChance.getValue()); - }); - timeout.addChangeListener(e -> { - Rs2Antiban.setTIMEOUT(timeout.getValue()); - timeoutLabel.setText("Timeout (min): " + timeout.getValue()); - }); - moveMouseRandomlyChance.addChangeListener(e -> { - Rs2AntibanSettings.moveMouseRandomlyChance = moveMouseRandomlyChance.getValue() / 100.0; - moveMouseRandomlyChanceLabel.setText("Random Mouse Movement (%): " + moveMouseRandomlyChance.getValue()); - }); - } - - public void loadSettings() { - isActionCooldownActive.setSelected(Rs2AntibanSettings.actionCooldownActive); - isMicroBreakActive.setSelected(Rs2AntibanSettings.microBreakActive); - isEnabled.setSelected(Rs2AntibanSettings.antibanEnabled); - usePlayStyle.setSelected(Rs2AntibanSettings.usePlayStyle); - useRandomIntervals.setSelected(Rs2AntibanSettings.randomIntervals); - simulateFatigue.setSelected(Rs2AntibanSettings.simulateFatigue); - simulateAttentionSpan.setSelected(Rs2AntibanSettings.simulateAttentionSpan); - useBehavioralVariability.setSelected(Rs2AntibanSettings.behavioralVariability); - useNonLinearIntervals.setSelected(Rs2AntibanSettings.nonLinearIntervals); - enableProfileSwitching.setSelected(Rs2AntibanSettings.profileSwitching); - adjustForTimeOfDay.setSelected(Rs2AntibanSettings.timeOfDayAdjust); - simulateMistakes.setSelected(Rs2AntibanSettings.simulateMistakes); - useNaturalMouse.setSelected(Rs2AntibanSettings.naturalMouse); - moveMouseOffScreen.setSelected(Rs2AntibanSettings.moveMouseOffScreen); - useContextualVariability.setSelected(Rs2AntibanSettings.contextualVariability); - dynamicActivityIntensity.setSelected(Rs2AntibanSettings.dynamicIntensity); - dynamicActivity.setSelected(Rs2AntibanSettings.dynamicActivity); - devDebug.setSelected(Rs2AntibanSettings.devDebug); - takeMicroBreaks.setSelected(Rs2AntibanSettings.takeMicroBreaks); - simulatePlaySchedule.setSelected(Rs2AntibanSettings.playSchedule); - - microBreakDurationLow.setValue(Rs2AntibanSettings.microBreakDurationLow); - microBreakDurationHigh.setValue(Rs2AntibanSettings.microBreakDurationHigh); - actionCooldownChance.setValue((int) (Rs2AntibanSettings.actionCooldownChance * 100)); - microBreakChance.setValue((int) (Rs2AntibanSettings.microBreakChance * 100)); - timeout.setValue(Rs2Antiban.getTIMEOUT()); - - microBreakDurationLowLabel.setText("Micro Break Duration Low (min): " + Rs2AntibanSettings.microBreakDurationLow); - microBreakDurationHighLabel.setText("Micro Break Duration High (min): " + Rs2AntibanSettings.microBreakDurationHigh); - actionCooldownChanceLabel.setText("Action Cooldown Chance (%): " + (int) (Rs2AntibanSettings.actionCooldownChance * 100)); - microBreakChanceLabel.setText("Micro Break Chance (%): " + (int) (Rs2AntibanSettings.microBreakChance * 100)); - timeoutLabel.setText("Timeout (min): " + Rs2Antiban.getTIMEOUT()); - - if (!Microbot.isLoggedIn()) - return; - - playStyleLabel.setText("Play Style: " + (Rs2Antiban.getPlayStyle() != null ? Rs2Antiban.getPlayStyle().getName() : "null")); - playStyleChangeLabel.setText("Play Style Change: " + (Rs2Antiban.getPlayStyle() != null ? Rs2Antiban.getPlayStyle().getTimeLeftUntilNextSwitch() : "null")); - profileLabel.setText("Category: " + (Rs2Antiban.getCategory() != null ? Rs2Antiban.getCategory().getName() : "null")); - activityLabel.setText("Activity: " + (Rs2Antiban.getActivity() != null ? Rs2Antiban.getActivity().getMethod() : "null")); - activityIntensityLabel.setText("Activity Intensity: " + (Rs2Antiban.getActivityIntensity() != null ? Rs2Antiban.getActivityIntensity().getName() : "null")); - busyLabel.setText("Busy: " + (Rs2Antiban.getCategory() != null ? Rs2Antiban.getCategory().isBusy() : "null")); - } - - private Icon createIcon(String path) { - return new ImageIcon(Objects.requireNonNull(getClass().getResource(path))); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/ActivityPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/ActivityPanel.java new file mode 100644 index 0000000000..d319b29052 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/ActivityPanel.java @@ -0,0 +1,64 @@ +package net.runelite.client.plugins.microbot.util.antiban.ui; + +import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings; +import net.runelite.client.ui.ColorScheme; + +import javax.swing.*; +import java.awt.*; + +public class ActivityPanel extends JPanel { + private final JCheckBox usePlayStyle = new JCheckBox("Use Play Style"); + private final JCheckBox useRandomIntervals = new JCheckBox("Use Random Intervals"); + private final JCheckBox simulateFatigue = new JCheckBox("Simulate Fatigue"); + private final JCheckBox simulateAttentionSpan = new JCheckBox("Simulate Attention Span"); + private final JCheckBox useBehavioralVariability = new JCheckBox("Use Behavioral Variability"); + private final JCheckBox useNonLinearIntervals = new JCheckBox("Use Non-Linear Intervals"); + private final JCheckBox dynamicActivityIntensity = new JCheckBox("Dynamic Activity Intensity"); + private final JCheckBox dynamicActivity = new JCheckBox("Dynamic Activity"); + + public ActivityPanel() { + setLayout(new GridBagLayout()); + setBackground(ColorScheme.DARK_GRAY_HOVER_COLOR); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = GridBagConstraints.RELATIVE; + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(5, 5, 5, 5); + + add(usePlayStyle, gbc); + add(useRandomIntervals, gbc); + add(simulateFatigue, gbc); + add(simulateAttentionSpan, gbc); + add(useBehavioralVariability, gbc); + add(useNonLinearIntervals, gbc); + add(dynamicActivityIntensity, gbc); + add(dynamicActivity, gbc); + + setupActionListeners(); + + } + + private void setupActionListeners() { + usePlayStyle.addActionListener(e -> Rs2AntibanSettings.usePlayStyle = usePlayStyle.isSelected()); + useRandomIntervals.addActionListener(e -> Rs2AntibanSettings.randomIntervals = useRandomIntervals.isSelected()); + simulateFatigue.addActionListener(e -> Rs2AntibanSettings.simulateFatigue = simulateFatigue.isSelected()); + simulateAttentionSpan.addActionListener(e -> Rs2AntibanSettings.simulateAttentionSpan = simulateAttentionSpan.isSelected()); + useBehavioralVariability.addActionListener(e -> Rs2AntibanSettings.behavioralVariability = useBehavioralVariability.isSelected()); + useNonLinearIntervals.addActionListener(e -> Rs2AntibanSettings.nonLinearIntervals = useNonLinearIntervals.isSelected()); + dynamicActivityIntensity.addActionListener(e -> Rs2AntibanSettings.dynamicIntensity = dynamicActivityIntensity.isSelected()); + dynamicActivity.addActionListener(e -> Rs2AntibanSettings.dynamicActivity = dynamicActivity.isSelected()); + + } + + public void updateValues() { + usePlayStyle.setSelected(Rs2AntibanSettings.usePlayStyle); + useRandomIntervals.setSelected(Rs2AntibanSettings.randomIntervals); + simulateFatigue.setSelected(Rs2AntibanSettings.simulateFatigue); + simulateAttentionSpan.setSelected(Rs2AntibanSettings.simulateAttentionSpan); + useBehavioralVariability.setSelected(Rs2AntibanSettings.behavioralVariability); + useNonLinearIntervals.setSelected(Rs2AntibanSettings.nonLinearIntervals); + dynamicActivityIntensity.setSelected(Rs2AntibanSettings.dynamicIntensity); + dynamicActivity.setSelected(Rs2AntibanSettings.dynamicActivity); + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/CardPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/CardPanel.java new file mode 100644 index 0000000000..5f2fc90ab5 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/CardPanel.java @@ -0,0 +1,25 @@ +package net.runelite.client.plugins.microbot.util.antiban.ui; + +import net.runelite.client.ui.ColorScheme; + +import javax.swing.*; +import java.awt.*; + +public class CardPanel extends JPanel { + public CardPanel() { + setLayout(new CardLayout()); + setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createMatteBorder(2, 2, 2, 2, ColorScheme.DARKER_GRAY_COLOR.darker()), + BorderFactory.createEmptyBorder(0, 0, 5, 0))); + setBackground(ColorScheme.DARK_GRAY_HOVER_COLOR); + } + + public void addPanel(JPanel panel, String name) { + add(panel, name); + } + + public void showPanel(String name) { + CardLayout cardLayout = (CardLayout) getLayout(); + cardLayout.show(this, name); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/CooldownPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/CooldownPanel.java new file mode 100644 index 0000000000..155fdb1bba --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/CooldownPanel.java @@ -0,0 +1,73 @@ +package net.runelite.client.plugins.microbot.util.antiban.ui; + +import net.runelite.client.plugins.microbot.util.antiban.Rs2Antiban; +import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings; +import net.runelite.client.ui.ColorScheme; + +import javax.swing.*; +import java.awt.*; + +import static net.runelite.client.plugins.microbot.util.antiban.ui.UiHelper.setupSlider; + +public class CooldownPanel extends JPanel { + private final JCheckBox isActionCooldownActive = new JCheckBox("Action Cooldown Active"); + private final JSlider actionCooldownChance = new JSlider(0, 100, (int) (Rs2AntibanSettings.actionCooldownChance * 100)); + private final JSlider timeout = new JSlider(0, 60, Rs2Antiban.getTIMEOUT()); + private final JLabel actionCooldownChanceLabel = new JLabel("Action Cooldown Chance (%): " + (int) (Rs2AntibanSettings.actionCooldownChance * 100)); + private final JLabel timeoutLabel = new JLabel("Timeout (min): " + Rs2Antiban.getTIMEOUT()); + + public CooldownPanel() { + // Set the layout manager for the panel to GridBagLayout + setLayout(new GridBagLayout()); + setBackground(ColorScheme.DARK_GRAY_HOVER_COLOR); + setupSlider(actionCooldownChance, 20, 100, 10); + setupSlider(timeout, 10, 60, 5); + + // Create a GridBagConstraints object to define the layout settings for each component + GridBagConstraints gbc = new GridBagConstraints(); + gbc.insets = new Insets(5, 5, 5, 5); // Padding around components + gbc.anchor = GridBagConstraints.WEST; // Align components to the left + gbc.gridx = 0; // All components will be in column 0 + gbc.gridy = GridBagConstraints.RELATIVE; // Components will be placed in consecutive rows + + // Add the "Action Cooldown Active" checkbox + add(isActionCooldownActive, gbc); + + // Add the "Action Cooldown Chance" label + add(actionCooldownChanceLabel, gbc); + + gbc.fill = GridBagConstraints.HORIZONTAL; + // Add the "Action Cooldown Chance" slider + add(actionCooldownChance, gbc); + + gbc.fill = GridBagConstraints.NONE; + // Add the "Timeout" label + add(timeoutLabel, gbc); + + gbc.fill = GridBagConstraints.HORIZONTAL; + // Add the "Timeout" slider + add(timeout, gbc); + + setupActionListeners(); + } + + private void setupActionListeners() { + isActionCooldownActive.addActionListener(e -> Rs2AntibanSettings.actionCooldownActive = isActionCooldownActive.isSelected()); + actionCooldownChance.addChangeListener(e -> { + Rs2AntibanSettings.actionCooldownChance = actionCooldownChance.getValue() / 100.0; + actionCooldownChanceLabel.setText("Action Cooldown Chance (%): " + actionCooldownChance.getValue()); + }); + timeout.addChangeListener(e -> { + Rs2Antiban.setTIMEOUT(timeout.getValue()); + timeoutLabel.setText("Timeout (min): " + timeout.getValue()); + }); + } + + public void updateValues() { + actionCooldownChance.setValue((int) (Rs2AntibanSettings.actionCooldownChance * 100)); + actionCooldownChanceLabel.setText("Action Cooldown Chance (%): " + actionCooldownChance.getValue()); + timeout.setValue(Rs2Antiban.getTIMEOUT()); + timeoutLabel.setText("Timeout (min): " + timeout.getValue()); + + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/GeneralPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/GeneralPanel.java new file mode 100644 index 0000000000..be20a36d60 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/GeneralPanel.java @@ -0,0 +1,57 @@ +package net.runelite.client.plugins.microbot.util.antiban.ui; + +import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings; +import net.runelite.client.ui.ColorScheme; + +import javax.swing.*; +import java.awt.*; + +public class GeneralPanel extends JPanel { + + private final JCheckBox isEnabled = new JCheckBox("Enabled"); + private final JCheckBox universalAntiban = new JCheckBox("Universal Antiban"); + private final JCheckBox useContextualVariability = new JCheckBox("Use Contextual Variability"); + private final JCheckBox devDebug = new JCheckBox("Dev Debug"); + + public GeneralPanel() { + // Set the layout manager for the panel to GridBagLayout + setLayout(new GridBagLayout()); + setBackground(ColorScheme.DARK_GRAY_HOVER_COLOR); + // Create a GridBagConstraints object to define the layout settings for each component + GridBagConstraints gbc = new GridBagConstraints(); + gbc.insets = new Insets(5, 5, 5, 5); // Padding around components + gbc.anchor = GridBagConstraints.WEST; // Align components to the left + gbc.gridx = 0; // All components will be in column 0 + gbc.gridy = GridBagConstraints.RELATIVE; // Components will be placed in consecutive rows + + // Add the "Enabled" checkbox + add(isEnabled, gbc); + + // Add the "Universal Antiban" checkbox + add(universalAntiban, gbc); + + // Add the "Use Contextual Variability" checkbox + add(useContextualVariability, gbc); + + // Add the "Dev Debug" checkbox + add(devDebug, gbc); + + setupActionListeners(); + } + + private void setupActionListeners() { + isEnabled.addActionListener(e -> Rs2AntibanSettings.antibanEnabled = isEnabled.isSelected()); + universalAntiban.addActionListener(e -> Rs2AntibanSettings.universalAntiban = universalAntiban.isSelected()); + useContextualVariability.addActionListener(e -> Rs2AntibanSettings.contextualVariability = useContextualVariability.isSelected()); + devDebug.addActionListener(e -> Rs2AntibanSettings.devDebug = devDebug.isSelected()); + + } + + public void updateValues() { + isEnabled.setSelected(Rs2AntibanSettings.antibanEnabled); + universalAntiban.setSelected(Rs2AntibanSettings.universalAntiban); + useContextualVariability.setSelected(Rs2AntibanSettings.contextualVariability); + devDebug.setSelected(Rs2AntibanSettings.devDebug); + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/MasterPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/MasterPanel.java new file mode 100644 index 0000000000..9fdac8314e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/MasterPanel.java @@ -0,0 +1,228 @@ +package net.runelite.client.plugins.microbot.util.antiban.ui; + +import net.runelite.client.plugins.microbot.Microbot; +import net.runelite.client.plugins.microbot.util.antiban.Rs2Antiban; +import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.PluginPanel; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.geom.AffineTransform; + +/** + * The MasterPanel is a user interface panel for configuring anti-ban settings. + * + *

+ * This panel allows users to adjust settings related to the anti-ban system, such as enabling micro-breaks, + * adjusting action cooldown probabilities, and configuring behavioral simulations like fatigue or attention span. + * The panel is divided into different categories, each focusing on specific aspects of anti-ban behavior, + * including activity settings, mouse behavior, and cooldown management. + *

+ * + *

+ * Users can interact with various checkboxes and sliders to tailor the bot's anti-ban features to their preferences, + * making it behave more like a human player during automated tasks. + *

+ * + *

Main Features:

+ *
    + *
  • Enable or disable anti-ban features like action cooldowns and micro-breaks.
  • + *
  • Customize the bot's behavior with random intervals, dynamic activity, and simulated fatigue.
  • + *
  • Adjust the duration and probability of micro-breaks and action cooldowns.
  • + *
  • Fine-tune mouse behavior, including natural movements and random actions.
  • + *
  • View real-time information about the current play style, activity, and bot status.
  • + *
+ * + *

+ * This panel is automatically integrated into the bot's user interface and does not require manual initialization by the user. + *

+ */ +public class MasterPanel extends PluginPanel { + private static final int BOUNDARY_RIGHT = 150; // Adjust for how far you want the GIF to move + private static final int BOUNDARY_LEFT = 0; + // Additional Info Panel + private final JLabel playStyleLabel = new JLabel("Play Style: " + (Rs2Antiban.getPlayStyle() != null ? Rs2Antiban.getPlayStyle().getName() : "null")); + private final JLabel playStyleChangeLabel = new JLabel("Play Style Change: " + (Rs2Antiban.getPlayStyle() != null ? Rs2Antiban.getPlayStyle().getTimeLeftUntilNextSwitch() : "null")); + private final JLabel profileLabel = new JLabel("Category: " + (Rs2Antiban.getCategory() != null ? Rs2Antiban.getCategory().getName() : "null")); + private final JLabel activityLabel = new JLabel("Activity: " + (Rs2Antiban.getActivity() != null ? Rs2Antiban.getActivity().getMethod() : "null")); + private final JLabel activityIntensityLabel = new JLabel("Activity Intensity: " + (Rs2Antiban.getActivityIntensity() != null ? Rs2Antiban.getActivityIntensity().getName() : "null")); + private final JLabel busyLabel = new JLabel("Busy: " + (Rs2Antiban.getCategory() != null ? Rs2Antiban.getCategory().isBusy() : "null")); + private final boolean isFlipped = false; // Track if the image is flipped + private final FlippableLabel label; + private final JLayeredPane layeredPane; // Use a layered pane for positioning the GIF + GeneralPanel generalPanel = new GeneralPanel(); + ActivityPanel activityPanel = new ActivityPanel(); + ProfilePanel profilePanel = new ProfilePanel(); + MousePanel mousePanel = new MousePanel(); + MicroBreakPanel microBreakPanel = new MicroBreakPanel(); + CooldownPanel cooldownPanel = new CooldownPanel(); + JButton resetButton = new JButton("Reset"); + private int xPosition = 0; + private int xVelocity = 1; // Change this value to control the speed of movement + + public MasterPanel() { + setLayout(new BorderLayout()); + + // Create the CardPanel (which contains the CardLayout) + CardPanel cardPanel = new CardPanel(); + + // Add panels to the CardPanel with unique names + cardPanel.addPanel(generalPanel, "General"); + cardPanel.addPanel(activityPanel, "Activity"); + cardPanel.addPanel(profilePanel, "Profile"); + cardPanel.addPanel(mousePanel, "Mouse"); + cardPanel.addPanel(microBreakPanel, "MicroBreak"); + cardPanel.addPanel(cooldownPanel, "Cooldown"); + + // Create the NavigationPanel and pass the CardPanel to it + NavigationPanel navigationPanel = new NavigationPanel(cardPanel); + + JPanel headerPanel = createHeaderPanel(navigationPanel); + JPanel mainDisplayPanel = new JPanel(); + + mainDisplayPanel.add(cardPanel); + mainDisplayPanel.setLayout(new BoxLayout(mainDisplayPanel, BoxLayout.Y_AXIS)); + mainDisplayPanel.add(createInfoPanel()); + mainDisplayPanel.add(Box.createVerticalStrut(100)); + mainDisplayPanel.add(new Box(BoxLayout.Y_AXIS)); + layeredPane = new JLayeredPane(); + layeredPane.setPreferredSize(new Dimension(250, 32)); + + // Create and position the GIF JLabel + ImageIcon icon = new ImageIcon(Rs2Antiban.class.getResource("walkingduck.gif")); + label = new FlippableLabel(icon); + label.setBounds(xPosition, 0, icon.getIconWidth(), icon.getIconHeight()); // Initial position + layeredPane.add(label, JLayeredPane.DEFAULT_LAYER); // Add label to the default layer + mainDisplayPanel.add(layeredPane); + + // Timer to move the GIF back and forth + Timer timer = new Timer(80, new ActionListener() { // Update every 20ms (50fps) + @Override + public void actionPerformed(ActionEvent e) { + // Update the position + xPosition += xVelocity; + + // Check boundaries and reverse direction if needed + if (xPosition >= 250 || xPosition <= BOUNDARY_LEFT) { + xVelocity = -xVelocity; // Reverse direction + label.flip(); // Flip the image when direction changes + } + + // Update the label's position + label.updatePosition(xPosition); + } + }); + timer.start(); // Start the movement timer + + add(headerPanel, BorderLayout.NORTH); + add(mainDisplayPanel, BorderLayout.CENTER); + add(resetButton, BorderLayout.SOUTH); + + cardPanel.showPanel("General"); + setupResetButton(); + + } + + private JPanel createHeaderPanel(NavigationPanel navigationPanel) { + JPanel headerPanel = new JPanel(); + headerPanel.setLayout(new BorderLayout()); + headerPanel.setBackground(new Color(27, 27, 27)); + +// JLabel headerLabel = new JLabel("\uD83E\uDD86 ANTIBAN \uD83E\uDD86"); + JLabel headerLabel = new JLabel("ANTIBAN"); + headerLabel.setFont(FontManager.getRunescapeBoldFont().deriveFont(24.0F)); + headerLabel.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0)); + headerLabel.setHorizontalAlignment(SwingConstants.CENTER); + + headerPanel.add(headerLabel, BorderLayout.NORTH); + headerPanel.add(navigationPanel, BorderLayout.CENTER); + + return headerPanel; + } + + //set up the reset button to reset all settings + public void setupResetButton() { + resetButton.addActionListener(e -> { + Rs2AntibanSettings.reset(); + loadSettings(); + }); + } + + public void loadSettings() { + // Load settings from the settings object and set the checkboxes accordingly + generalPanel.updateValues(); + activityPanel.updateValues(); + profilePanel.updateValues(); + mousePanel.updateValues(); + microBreakPanel.updateValues(); + cooldownPanel.updateValues(); + + if (!Microbot.isLoggedIn()) + return; + + playStyleLabel.setText("Play Style: " + (Rs2Antiban.getPlayStyle() != null ? Rs2Antiban.getPlayStyle().getName() : "null")); + playStyleChangeLabel.setText("Play Style Change: " + (Rs2Antiban.getPlayStyle() != null ? Rs2Antiban.getPlayStyle().getTimeLeftUntilNextSwitch() : "null")); + profileLabel.setText("Category: " + (Rs2Antiban.getCategory() != null ? Rs2Antiban.getCategory().getName() : "null")); + activityLabel.setText("Activity: " + (Rs2Antiban.getActivity() != null ? Rs2Antiban.getActivity().getMethod() : "null")); + activityIntensityLabel.setText("Activity Intensity: " + (Rs2Antiban.getActivityIntensity() != null ? Rs2Antiban.getActivityIntensity().getName() : "null")); + busyLabel.setText("Busy: " + (Rs2Antiban.getCategory() != null ? Rs2Antiban.getCategory().isBusy() : "null")); + } + + private JPanel createInfoPanel() { + JPanel panel = new JPanel(new GridBagLayout()); + panel.setBorder(BorderFactory.createTitledBorder("Additional Info")); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = GridBagConstraints.RELATIVE; + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(5, 5, 5, 5); + + panel.add(playStyleLabel, gbc); + panel.add(playStyleChangeLabel, gbc); + panel.add(profileLabel, gbc); + panel.add(activityLabel, gbc); + panel.add(activityIntensityLabel, gbc); + panel.add(busyLabel, gbc); + + return panel; + } + + // Custom JLabel class that supports flipping the image + private class FlippableLabel extends JLabel { + private boolean isFlipped = false; + + public FlippableLabel(ImageIcon icon) { + super(icon); + setDoubleBuffered(true); // Enable double buffering to prevent flickering + } + + public void flip() { + isFlipped = !isFlipped; + repaint(); // Request a repaint to apply the flip + } + + public void updatePosition(int x) { + // Safely update the label position without interfering with other UI events + SwingUtilities.invokeLater(() -> setBounds(x, getY(), getWidth(), getHeight())); + } + + @Override + protected void paintComponent(Graphics g) { + Graphics2D g2d = (Graphics2D) g.create(); // Create a copy of Graphics2D to avoid modifying the original + + if (isFlipped) { + // Apply horizontal flip by flipping the x-axis + AffineTransform transform = AffineTransform.getScaleInstance(-1, 1); + transform.translate(-getWidth(), 0); + g2d.setTransform(transform); + } + + super.paintComponent(g2d); // Let JLabel handle the image rendering + g2d.dispose(); // Dispose of the copy to release resources + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/MicroBreakPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/MicroBreakPanel.java new file mode 100644 index 0000000000..408aa32aa4 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/MicroBreakPanel.java @@ -0,0 +1,95 @@ +package net.runelite.client.plugins.microbot.util.antiban.ui; + +import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings; +import net.runelite.client.ui.ColorScheme; + +import javax.swing.*; +import java.awt.*; + +import static net.runelite.client.plugins.microbot.util.antiban.ui.UiHelper.setupSlider; + +public class MicroBreakPanel extends JPanel { + private final JCheckBox isMicroBreakActive = new JCheckBox("Micro Break Active"); + private final JCheckBox takeMicroBreaks = new JCheckBox("Take Micro Breaks"); + private final JSlider microBreakDurationLow = new JSlider(1, 10, Rs2AntibanSettings.microBreakDurationLow); + private final JSlider microBreakDurationHigh = new JSlider(1, 30, Rs2AntibanSettings.microBreakDurationHigh); + private final JSlider microBreakChance = new JSlider(0, 100, (int) (Rs2AntibanSettings.microBreakChance * 100)); + private final JLabel microBreakDurationLowLabel = new JLabel("Micro Break Duration Low (min): " + Rs2AntibanSettings.microBreakDurationLow); + private final JLabel microBreakDurationHighLabel = new JLabel("Micro Break Duration High (min): " + Rs2AntibanSettings.microBreakDurationHigh); + private final JLabel microBreakChanceLabel = new JLabel("Micro Break Chance (%): " + (int) (Rs2AntibanSettings.microBreakChance * 100)); + + public MicroBreakPanel() { + // Set the layout manager for the panel to GridBagLayout + setLayout(new GridBagLayout()); + setBackground(ColorScheme.DARK_GRAY_HOVER_COLOR); + setupSlider(microBreakDurationLow, 1, 5, 1); + setupSlider(microBreakDurationHigh, 5, 15, 1); + setupSlider(microBreakChance, 20, 100, 10); + + // Create a GridBagConstraints object to define the layout settings for each component + GridBagConstraints gbc = new GridBagConstraints(); + gbc.insets = new Insets(5, 5, 5, 5); // Padding around components + gbc.anchor = GridBagConstraints.WEST; // Align components to the left + gbc.gridx = 0; // All components will be in column 0 + gbc.gridy = GridBagConstraints.RELATIVE; // Components will be placed in consecutive rows + + // Add the "Micro Break Active" checkbox + add(isMicroBreakActive, gbc); + + // Add the "Take Micro Breaks" checkbox + add(takeMicroBreaks, gbc); + + // Add the "Micro Break Duration Low" label + add(microBreakDurationLowLabel, gbc); + + gbc.fill = GridBagConstraints.HORIZONTAL; + // Add the "Micro Break Duration Low" slider + add(microBreakDurationLow, gbc); + + gbc.fill = GridBagConstraints.NONE; + // Add the "Micro Break Duration High" label + add(microBreakDurationHighLabel, gbc); + + gbc.fill = GridBagConstraints.HORIZONTAL; + // Add the "Micro Break Duration High" slider + add(microBreakDurationHigh, gbc); + + gbc.fill = GridBagConstraints.NONE; + // Add the "Micro Break Chance" label + add(microBreakChanceLabel, gbc); + + gbc.fill = GridBagConstraints.HORIZONTAL; + // Add the "Micro Break Chance" slider + add(microBreakChance, gbc); + + setupActionListeners(); + } + + private void setupActionListeners() { + isMicroBreakActive.addActionListener(e -> Rs2AntibanSettings.microBreakActive = isMicroBreakActive.isSelected()); + takeMicroBreaks.addActionListener(e -> Rs2AntibanSettings.takeMicroBreaks = takeMicroBreaks.isSelected()); + microBreakDurationLow.addChangeListener(e -> { + Rs2AntibanSettings.microBreakDurationLow = microBreakDurationLow.getValue(); + microBreakDurationLowLabel.setText("Micro Break Duration Low (min): " + microBreakDurationLow.getValue()); + }); + microBreakDurationHigh.addChangeListener(e -> { + Rs2AntibanSettings.microBreakDurationHigh = microBreakDurationHigh.getValue(); + microBreakDurationHighLabel.setText("Micro Break Duration High (min): " + microBreakDurationHigh.getValue()); + }); + microBreakChance.addChangeListener(e -> { + Rs2AntibanSettings.microBreakChance = microBreakChance.getValue() / 100.0; + microBreakChanceLabel.setText("Micro Break Chance (%): " + microBreakChance.getValue()); + }); + } + + public void updateValues() { + isMicroBreakActive.setSelected(Rs2AntibanSettings.microBreakActive); + takeMicroBreaks.setSelected(Rs2AntibanSettings.takeMicroBreaks); + microBreakDurationLow.setValue(Rs2AntibanSettings.microBreakDurationLow); + microBreakDurationLowLabel.setText("Micro Break Duration Low (min): " + microBreakDurationLow.getValue()); + microBreakDurationHigh.setValue(Rs2AntibanSettings.microBreakDurationHigh); + microBreakDurationHighLabel.setText("Micro Break Duration High (min): " + microBreakDurationHigh.getValue()); + microBreakChance.setValue((int) (Rs2AntibanSettings.microBreakChance * 100)); + microBreakChanceLabel.setText("Micro Break Chance (%): " + microBreakChance.getValue()); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/MousePanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/MousePanel.java new file mode 100644 index 0000000000..dfb8c1c56f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/MousePanel.java @@ -0,0 +1,79 @@ +package net.runelite.client.plugins.microbot.util.antiban.ui; + +import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings; +import net.runelite.client.ui.ColorScheme; + +import javax.swing.*; +import java.awt.*; + +import static net.runelite.client.plugins.microbot.util.antiban.ui.UiHelper.setupSlider; + +public class MousePanel extends JPanel { + private final JCheckBox useNaturalMouse = new JCheckBox("Use Natural Mouse"); + private final JCheckBox simulateMistakes = new JCheckBox("Simulate Mistakes"); + private final JCheckBox moveMouseOffScreen = new JCheckBox("Move Mouse Off Screen"); + private final JCheckBox moveMouseRandomly = new JCheckBox("Move Mouse Randomly"); + private final JSlider moveMouseRandomlyChance = new JSlider(0, 100, (int) (Rs2AntibanSettings.moveMouseRandomlyChance * 100)); + private final JLabel moveMouseRandomlyChanceLabel = new JLabel("Random Mouse Movement (%): " + (int) (Rs2AntibanSettings.moveMouseRandomlyChance * 100)); + + public MousePanel() { + // Set the layout manager for the panel to GridBagLayout + setLayout(new GridBagLayout()); + setBackground(ColorScheme.DARK_GRAY_HOVER_COLOR); + setupSlider(moveMouseRandomlyChance, 20, 100, 10); + + // Create a GridBagConstraints object to define the layout settings for each component + GridBagConstraints gbc = new GridBagConstraints(); + gbc.insets = new Insets(5, 5, 5, 5); // Padding around components + gbc.anchor = GridBagConstraints.WEST; // Align components to the left + gbc.gridx = 0; // All components will be in column 0 + gbc.gridy = GridBagConstraints.RELATIVE; // Components will be placed in consecutive rows + + // Add the "Use Natural Mouse" checkbox + add(useNaturalMouse, gbc); + + // Add a gap between "Use Natural Mouse" and the rest of the settings + gbc.insets = new Insets(20, 5, 5, 5); // Increase the top padding to create a larger gap + add(Box.createVerticalStrut(15), gbc); // Add a vertical gap of 15 pixels + + // Add the "Simulate Mistakes" checkbox + gbc.insets = new Insets(5, 5, 5, 5); // Reset padding for normal spacing + add(simulateMistakes, gbc); + + // Add the "Move Mouse Off Screen" checkbox + add(moveMouseOffScreen, gbc); + + // Add the "Move Mouse Randomly" checkbox + add(moveMouseRandomly, gbc); + + // Add the "Random Mouse Movement" label + add(moveMouseRandomlyChanceLabel, gbc); + + gbc.fill = GridBagConstraints.HORIZONTAL; + // Add the "Random Mouse Movement" slider + add(moveMouseRandomlyChance, gbc); + + setupActionListeners(); + } + + private void setupActionListeners() { + useNaturalMouse.addActionListener(e -> Rs2AntibanSettings.naturalMouse = useNaturalMouse.isSelected()); + simulateMistakes.addActionListener(e -> Rs2AntibanSettings.simulateMistakes = simulateMistakes.isSelected()); + moveMouseOffScreen.addActionListener(e -> Rs2AntibanSettings.moveMouseOffScreen = moveMouseOffScreen.isSelected()); + moveMouseRandomly.addActionListener(e -> Rs2AntibanSettings.moveMouseRandomly = moveMouseRandomly.isSelected()); + moveMouseRandomlyChance.addChangeListener(e -> { + Rs2AntibanSettings.moveMouseRandomlyChance = moveMouseRandomlyChance.getValue() / 100.0; + moveMouseRandomlyChanceLabel.setText("Random Mouse Movement (%): " + moveMouseRandomlyChance.getValue()); + }); + } + + public void updateValues() { + + useNaturalMouse.setSelected(Rs2AntibanSettings.naturalMouse); + simulateMistakes.setSelected(Rs2AntibanSettings.simulateMistakes); + moveMouseOffScreen.setSelected(Rs2AntibanSettings.moveMouseOffScreen); + moveMouseRandomly.setSelected(Rs2AntibanSettings.moveMouseRandomly); + moveMouseRandomlyChance.setValue((int) (Rs2AntibanSettings.moveMouseRandomlyChance * 100)); + moveMouseRandomlyChanceLabel.setText("Random Mouse Movement (%): " + moveMouseRandomlyChance.getValue()); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/NavigationPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/NavigationPanel.java new file mode 100644 index 0000000000..e95e4c7536 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/NavigationPanel.java @@ -0,0 +1,52 @@ +package net.runelite.client.plugins.microbot.util.antiban.ui; + +import net.runelite.client.plugins.microbot.util.antiban.Rs2Antiban; + +import javax.swing.*; +import java.awt.*; +import java.util.Objects; + +public class NavigationPanel extends JPanel { + + public NavigationPanel(CardPanel cardPanel) { + setLayout(new BorderLayout()); + + // Create a navigation panel for the buttons + JPanel navPanel = new JPanel(); + navPanel.setLayout(new GridLayout(1, 6)); // 1 rows, 6 column + + // Create buttons with icons + JButton generalButton = createIconButton("general.png", "General"); + JButton activityButton = createIconButton("activity.png", "Activity"); + JButton profileButton = createIconButton("profile.png", "Profile"); + JButton mouseButton = createIconButton("mouse.png", "Mouse"); + JButton microBreakButton = createIconButton("microbreak.png", "MicroBreak"); + JButton cooldownButton = createIconButton("cooldown.png", "Cooldown"); + + // Add buttons to the navigation panel + navPanel.add(generalButton); + navPanel.add(activityButton); + navPanel.add(profileButton); + navPanel.add(mouseButton); + navPanel.add(microBreakButton); + navPanel.add(cooldownButton); + + // Add navigation panel to the NavigationPanel + add(navPanel, BorderLayout.CENTER); + + // Add action listeners to switch panels on button click + generalButton.addActionListener(e -> cardPanel.showPanel("General")); + activityButton.addActionListener(e -> cardPanel.showPanel("Activity")); + profileButton.addActionListener(e -> cardPanel.showPanel("Profile")); + mouseButton.addActionListener(e -> cardPanel.showPanel("Mouse")); + microBreakButton.addActionListener(e -> cardPanel.showPanel("MicroBreak")); + cooldownButton.addActionListener(e -> cardPanel.showPanel("Cooldown")); + } + + private JButton createIconButton(String iconPath, String altText) { + ImageIcon icon = new ImageIcon(Objects.requireNonNull(Rs2Antiban.class.getResource(iconPath))); + JButton button = new JButton(icon); + button.setToolTipText(altText); // Set alt text for accessibility + return button; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/ProfilePanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/ProfilePanel.java new file mode 100644 index 0000000000..734cf815a7 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/ProfilePanel.java @@ -0,0 +1,50 @@ +package net.runelite.client.plugins.microbot.util.antiban.ui; + +import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings; +import net.runelite.client.ui.ColorScheme; + +import javax.swing.*; +import java.awt.*; + +public class ProfilePanel extends JPanel { + + private final JCheckBox enableProfileSwitching = new JCheckBox("Enable Profile Switching"); + private final JCheckBox adjustForTimeOfDay = new JCheckBox("Adjust For Time Of Day"); + private final JCheckBox simulatePlaySchedule = new JCheckBox("Simulate Play Schedule"); + + public ProfilePanel() { + // Set the layout manager for the panel to GridBagLayout + setLayout(new GridBagLayout()); + setBackground(ColorScheme.DARK_GRAY_HOVER_COLOR); + // Create a GridBagConstraints object to define the layout settings for each component + GridBagConstraints gbc = new GridBagConstraints(); + gbc.insets = new Insets(5, 5, 5, 5); // Padding around components + gbc.anchor = GridBagConstraints.WEST; // Align components to the left + gbc.gridx = 0; // All components will be in column 0 + gbc.gridy = GridBagConstraints.RELATIVE; // Components will be placed in consecutive rows + + // Add the "Enable Profile Switching" checkbox + add(enableProfileSwitching, gbc); + + // Add the "Adjust For Time Of Day" checkbox + add(adjustForTimeOfDay, gbc); + + // Add the "Simulate Play Schedule" checkbox + add(simulatePlaySchedule, gbc); + + setupActionListeners(); + } + + private void setupActionListeners() { + enableProfileSwitching.addActionListener(e -> Rs2AntibanSettings.profileSwitching = enableProfileSwitching.isSelected()); + adjustForTimeOfDay.addActionListener(e -> Rs2AntibanSettings.timeOfDayAdjust = adjustForTimeOfDay.isSelected()); + simulatePlaySchedule.addActionListener(e -> Rs2AntibanSettings.playSchedule = simulatePlaySchedule.isSelected()); + + } + + public void updateValues() { + enableProfileSwitching.setSelected(Rs2AntibanSettings.profileSwitching); + adjustForTimeOfDay.setSelected(Rs2AntibanSettings.timeOfDayAdjust); + simulatePlaySchedule.setSelected(Rs2AntibanSettings.playSchedule); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/UiHelper.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/UiHelper.java new file mode 100644 index 0000000000..bf897c7e7e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/UiHelper.java @@ -0,0 +1,14 @@ +package net.runelite.client.plugins.microbot.util.antiban.ui; + +import javax.swing.*; + +public class UiHelper { + public static void setupSlider(JSlider slider, int majorTickSpacing, int max, int minorTickSpacing) { + slider.setMajorTickSpacing(majorTickSpacing); + slider.setMinorTickSpacing(minorTickSpacing); + slider.setPaintTicks(true); + slider.setPaintLabels(true); + slider.setMinimum(0); + slider.setMaximum(max); + } +} From e2772bad64be0b19542742278be1eacd4af1bd4e Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sun, 18 Aug 2024 13:36:38 +0200 Subject: [PATCH 38/52] old resources removed --- .../net/runelite/client/plugins/microbot/util/antiban/mistake.png | 0 .../net/runelite/client/plugins/microbot/util/antiban/timeout.png | 0 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/mistake.png delete mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/timeout.png diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/mistake.png b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/mistake.png deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/timeout.png b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/timeout.png deleted file mode 100644 index e69de29bb2..0000000000 From 3c2b6ab2b8ce7428ac98bc624a6deafd45dc74b0 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sun, 18 Aug 2024 13:39:30 +0200 Subject: [PATCH 39/52] Antiban update --- .../natehumidifier/HumidifierScript.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/nateplugins/moneymaking/natehumidifier/HumidifierScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/nateplugins/moneymaking/natehumidifier/HumidifierScript.java index 166aeb4d3c..184c9a09eb 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/nateplugins/moneymaking/natehumidifier/HumidifierScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/nateplugins/moneymaking/natehumidifier/HumidifierScript.java @@ -3,10 +3,12 @@ import net.runelite.api.ItemID; import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.plugins.microbot.Script; +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.antiban.enums.Activity; 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.magic.Rs2Magic; -import net.runelite.client.plugins.skillcalculator.skills.MagicAction; import net.runelite.client.util.QuantityFormatter; import java.util.concurrent.TimeUnit; @@ -14,15 +16,17 @@ public class HumidifierScript extends Script { public static String version = "1.6.1"; - private static long itemsProcessed = 0; public static String itemsProcessedMessage = ""; public static String profitMessage = "Calculating..."; - + private static long itemsProcessed = 0; private int profit = 0; private long timeBegan; public boolean run(HumidifierConfig config) { + Rs2Antiban.antibanSetupTemplates.applyCombatSetup(); + Rs2Antiban.setActivity(Activity.HUMIDIFYING_CLAY); + timeBegan = System.currentTimeMillis(); int unprocessedItemPrice = Microbot.getItemManager().search(config.ITEM().getName()).get(0).getPrice(); int processedItemPrice = Microbot.getItemManager().search(config.ITEM().getFinished()).get(0).getPrice(); @@ -31,13 +35,17 @@ public boolean run(HumidifierConfig config) { mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { if (!super.run()) return; if (!Microbot.isLoggedIn()) return; + if (Rs2AntibanSettings.actionCooldownActive) return; try { boolean hasAstralRunesInInventory = Rs2Inventory.hasItem(ItemID.ASTRAL_RUNE); if (Microbot.pauseAllScripts) return; if (Rs2Inventory.hasItem(config.ITEM().getName(), true) && hasAstralRunesInInventory) { - Rs2Magic.cast(MagicAction.HUMIDIFY); + if (!Rs2Bank.isOpen()) + Rs2Magic.humidify(); sleepUntilOnClientThread(() -> Rs2Inventory.hasItem(config.ITEM().getFinished())); + Rs2Antiban.actionCooldown(); + Rs2Antiban.takeMicroBreakByChance(); } else { bank(config, hasAstralRunesInInventory); calculateItemsProcessedPerHour(config); @@ -79,7 +87,7 @@ private void bank(HumidifierConfig config, boolean hasAstralRunes){ Rs2Bank.withdrawAll(true, config.ITEM().getName(), true); sleepUntilOnClientThread(() -> Rs2Inventory.hasItem(config.ITEM().getName())); Rs2Bank.closeBank(); - sleepUntilOnClientThread(() -> !Rs2Bank.isOpen()); + //sleepUntilOnClientThread(() -> !Rs2Bank.isOpen()); } else { Rs2Bank.openBank(); From 00e3f7e9ad3f590c2b2368342ea795496a0e8c39 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sun, 18 Aug 2024 13:40:12 +0200 Subject: [PATCH 40/52] Added click overload method --- .../runelite/client/plugins/microbot/Microbot.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java index 65b4748c25..327b193b1f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java @@ -291,6 +291,17 @@ public static void click(Rectangle rectangle, NewMenuEntry entry) { } } + public static void click(Rectangle rectangle) { + + Point point = Rs2UiHelper.getClickingPoint(rectangle, true); + mouse.click(point); + + + if (!Microbot.getClient().isClientThread()) { + sleep(50, 80); + } + } + @Deprecated(since = "1.3.8 - use Mouse class", forRemoval = true) private static void mouseEvent(int id, Point point) { MouseEvent e = new MouseEvent(client.getCanvas(), id, System.currentTimeMillis(), 0, point.getX(), point.getY(), 1, false, 1); From 1b7da8e0fd349d6a694d18fdc0a888677471b24a Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sun, 18 Aug 2024 21:59:14 +0200 Subject: [PATCH 41/52] added humidify --- .../plugins/microbot/util/magic/Rs2Magic.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/magic/Rs2Magic.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/magic/Rs2Magic.java index 57c57e730b..5f7967701b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/magic/Rs2Magic.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/magic/Rs2Magic.java @@ -3,7 +3,6 @@ import net.runelite.api.Point; import net.runelite.api.*; import net.runelite.api.widgets.Widget; -import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.plugins.microbot.globval.enums.InterfaceTab; import net.runelite.client.plugins.microbot.util.camera.Rs2Camera; @@ -14,7 +13,6 @@ import net.runelite.client.plugins.microbot.util.tabs.Rs2Tab; import net.runelite.client.plugins.microbot.util.widget.Rs2Widget; import net.runelite.client.plugins.skillcalculator.skills.MagicAction; -import net.runelite.client.plugins.timers.GameTimer; import org.apache.commons.lang3.NotImplementedException; import java.awt.*; @@ -70,7 +68,7 @@ public static void cast(MagicAction magicSpell) { if (magicSpell.getWidgetId() == -1) throw new NotImplementedException("This spell has not been configured yet in the MagicAction.java class"); - Microbot.doInvoke(new NewMenuEntry(-1, magicSpell.getWidgetId(), menuAction.getId(), identifier, -1, "" + magicSpell.getName() + ""), new Rectangle(0, 0, Microbot.getClient().getCanvasWidth(), Microbot.getClient().getCanvasHeight())); + Microbot.doInvoke(new NewMenuEntry("cast", -1, magicSpell.getWidgetId(), menuAction.getId(), identifier, -1, magicSpell.getName()), new Rectangle(1, 1)); //Rs2Reflection.invokeMenu(-1, magicSpell.getWidgetId(), menuAction.getId(), 1, -1, "Cast", "" + magicSpell.getName() + "", -1, -1); } @@ -233,6 +231,19 @@ private static void superHeat(Widget superheat, Rs2Item item, int sleepMin, int } } + // humidify + public static void humidify() { + sleepUntil(() -> { + Rs2Tab.switchToMagicTab(); + sleep(50, 150); + return Rs2Tab.getCurrentTab() == InterfaceTab.MAGIC; + }); + Widget humidify = Rs2Widget.findWidget(MagicAction.HUMIDIFY.getName()); + if (humidify.getSpriteId() == 1972) { + Microbot.click(humidify.getBounds()); + } + } + private static void alch(Widget alch) { alch(alch, null, 300, 600); } From e48c37112feba7f62b7513b88019e8a54853e03f Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sun, 18 Aug 2024 22:00:17 +0200 Subject: [PATCH 42/52] various tweaks & bugfixes --- .../breakhandler/BreakHandlerScript.java | 6 ++++-- .../microbot/util/antiban/AntibanPlugin.java | 2 +- .../util/antiban/AntibanSetupTemplates.java | 4 ++-- .../microbot/util/antiban/Rs2Antiban.java | 18 +++++++++++++----- .../microbot/util/antiban/ui/MasterPanel.java | 2 +- .../microbot/util/antiban/walkingduckdark.gif | Bin 0 -> 9156 bytes 6 files changed, 21 insertions(+), 11 deletions(-) create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/walkingduckdark.gif diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerScript.java index b512078b1f..cea474988a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/BreakHandlerScript.java @@ -24,13 +24,15 @@ public class BreakHandlerScript extends Script { public static Duration duration; public static Duration breakInDuration; - @Setter @Getter public static boolean lockState = false; - private String title = ""; + public static boolean isBreakActive() { + return breakDuration > 0; + } + public static String formatDuration(Duration duration, String header) { long hours = duration.toHours(); long minutes = duration.toMinutes() % 60; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java index 9ebb31de7b..bbfd21f371 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanPlugin.java @@ -243,7 +243,7 @@ public void onGameTick(GameTick event) { @Subscribe public void onStatChanged(StatChanged statChanged) { - if (!Rs2AntibanSettings.antibanEnabled && !Rs2AntibanSettings.universalAntiban) { + if (!Rs2AntibanSettings.antibanEnabled) { return; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanSetupTemplates.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanSetupTemplates.java index 93b2c7f288..c0b20adf57 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanSetupTemplates.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/AntibanSetupTemplates.java @@ -583,8 +583,8 @@ public void applyGeneralBasicSetup() { Rs2AntibanSettings.simulateMistakes = true; Rs2AntibanSettings.naturalMouse = true; Rs2AntibanSettings.contextualVariability = false; - Rs2AntibanSettings.dynamicIntensity = true; - Rs2AntibanSettings.dynamicActivity = true; + Rs2AntibanSettings.dynamicIntensity = false; + Rs2AntibanSettings.dynamicActivity = false; Rs2AntibanSettings.devDebug = false; Rs2AntibanSettings.takeMicroBreaks = false; Rs2AntibanSettings.playSchedule = false; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2Antiban.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2Antiban.java index b2516c818e..17ac5988cf 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2Antiban.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/Rs2Antiban.java @@ -140,8 +140,6 @@ public class Rs2Antiban { public static void setActivity(Activity activity) { - Rs2AntibanSettings.dynamicIntensity = false; - Rs2AntibanSettings.dynamicActivity = false; Rs2Antiban.activity = activity; Rs2Antiban.category = activity.getCategory(); Rs2Antiban.activityIntensity = activity.getActivityIntensity(); @@ -149,8 +147,11 @@ public static void setActivity(Activity activity) { if (Rs2AntibanSettings.simulateAttentionSpan) { Rs2Antiban.playStyle = PlayStyle.EXTREME_AGGRESSIVE; //Rs2Antiban.playStyle = activityIntensity.getPlayStyle(); - } else - Rs2Antiban.playStyle = activityIntensity.getPlayStyle(); + } else { + if (Rs2Antiban.playStyle == null) + Rs2Antiban.playStyle = activityIntensity.getPlayStyle(); + } + if (Rs2AntibanSettings.randomIntervals) { Rs2Antiban.playStyle = PlayStyle.RANDOM; } @@ -327,16 +328,19 @@ private static void performActionCooldown() { *
  • Rs2AntibanSettings.microBreakActive is set to true if the break is triggered.
  • *
  • BreakHandlerScript.breakDuration is set to a randomly determined value in seconds.
  • * + * @return true if a micro-break is triggered, false otherwise. */ - public static void takeMicroBreakByChance() { + public static boolean takeMicroBreakByChance() { if (Math.random() < Rs2AntibanSettings.microBreakChance) { Rs2AntibanSettings.microBreakActive = true; BreakHandlerScript.breakDuration = Random.random(Rs2AntibanSettings.microBreakDurationLow * 60, Rs2AntibanSettings.microBreakDurationHigh * 60); if (Rs2AntibanSettings.moveMouseOffScreen) moveMouseOffScreen(); + return true; } + return false; } @@ -510,6 +514,10 @@ public static void deactivateAntiban() { // reset all the variables public static void resetAntibanSettings() { Rs2AntibanSettings.reset(); + Rs2Antiban.playStyle = null; + Rs2Antiban.activity = null; + Rs2Antiban.activityIntensity = null; + Rs2Antiban.category = null; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/MasterPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/MasterPanel.java index 9fdac8314e..3ca94d9b2d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/MasterPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/antiban/ui/MasterPanel.java @@ -92,7 +92,7 @@ public MasterPanel() { layeredPane.setPreferredSize(new Dimension(250, 32)); // Create and position the GIF JLabel - ImageIcon icon = new ImageIcon(Rs2Antiban.class.getResource("walkingduck.gif")); + ImageIcon icon = new ImageIcon(Rs2Antiban.class.getResource("walkingduckdark.gif")); label = new FlippableLabel(icon); label.setBounds(xPosition, 0, icon.getIconWidth(), icon.getIconHeight()); // Initial position layeredPane.add(label, JLayeredPane.DEFAULT_LAYER); // Add label to the default layer diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/walkingduckdark.gif b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/util/antiban/walkingduckdark.gif new file mode 100644 index 0000000000000000000000000000000000000000..61274bd01d954a3d75f618e4f19f80ea1d2c0ebe GIT binary patch literal 9156 zcmZ?wbhEHbRA5kG_|Cxa|38CfwT_D!=P-!z;TU%kweB~!-9j& z9Ku>LCpIiR+%BN(HOFJ4(jf*3W3~+%1`Q4U3amM6UKBQ*oXqSJq~p0@sW+3RyW5u^ zo0fTX%5h7qxcTXs4_GI6tmnIYG#{wmTTyHOPWC~0$JRJLl*2#Ag%a-^aP-d|&tSBG2_EHciQmp}ms5kG8COkioFt z;;`72UIPV8ueOht2cTFV1tT>CIPPN2>M9Zo79C{}=4KN(uyHX%k0fJ`jKiiU4gHet zLJ=n?9q^nWXufSuXV9WEOl)aFD2b=muW%HhN7_N@oY_ph4qqFSIZD5{1ms)x0`L!L~6N{c$ zKghq=Z1ne>&KaQ%_vTuaTDi>#ElgpmxO!{r9f{(!vjw(AHP(>}Z|^SmW*5`_RdA&% zRKHC|V!^7WEt@l=b!J5`zJ5=nP@9KiG{=wBggxqS93jAQ1#6BczH&F=6fsB;R`z4m z7yjaOF(%2&k%6gLJg4$}X1=U34|riqf$pvoPX zPlda z&v$M*VqN_C?(X@5-C}diGu_tMSu$}HggS=Z+-zz#n$dAY_Neg44FQf@*fY9>VZ+gG zMhzhzg$0Y<86{1NKvg@#1QAHpesY2+>lR?uuH??d%5z2~z*y}ZlMX+JDA(tQXQ#0X zX!uTe;Bc|cy;*C9*HulIKJF+z39B!ehq@UyWW_RETIAd(Yn&8&VA`7SR%Op=q8Fy+ z`nPF?&zrLH>$^jXWg{oO`TgyI?>=MpEnj|zsy&*kBdL7km&B&qn@dhQP0QTA;o%v1 z+ig>M%!}{tTJpb*@2#Z1TepiTACH5nL-@_am0Aql);jOo7#T4FQfjSTp-j>$Z1`b+?Jk3`<~G zuetgV&+O>$IlX)2*;p8?M{dlH`g}A5MnhmU1V%$(Gz3ONU^E0qLtr!nMneDqoh7|= literal 0 HcmV?d00001 From fe4ba4540e624266c5985a01efabede9710e0a3f Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sun, 18 Aug 2024 22:02:22 +0200 Subject: [PATCH 43/52] minor improvements & fixed spam clicking --- .../microbot/vorkath/VorkathScript.java | 92 ++++++++++++------- 1 file changed, 60 insertions(+), 32 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/vorkath/VorkathScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/vorkath/VorkathScript.java index 3a9582835b..3f387b81a3 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/vorkath/VorkathScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/vorkath/VorkathScript.java @@ -17,10 +17,12 @@ 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.grandexchange.Rs2GrandExchange; +import net.runelite.client.plugins.microbot.util.grounditem.LootingParameters; 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.magic.Rs2Magic; import net.runelite.client.plugins.microbot.util.math.Random; +import net.runelite.client.plugins.microbot.util.math.Rs2Random; import net.runelite.client.plugins.microbot.util.misc.Rs2Potion; import net.runelite.client.plugins.microbot.util.npc.Rs2Npc; import net.runelite.client.plugins.microbot.util.player.Rs2Player; @@ -52,31 +54,39 @@ enum State { } public class VorkathScript extends Script { - public static String version = "1.3.6"; - - State state = State.ZOMBIE_SPAWN; - - private final int whiteProjectileId = 395; - private final int redProjectileId = 1481; + public static String version = "1.3.7"; + public static VorkathConfig config; @Getter public final int acidProjectileId = 1483; + final String ZOMBIFIED_SPAWN = "Zombified Spawn"; + private final int whiteProjectileId = 395; + private final int redProjectileId = 1481; private final int acidRedProjectileId = 1482; + @Getter + private final HashSet acidPools = new HashSet<>(); + public int vorkathSessionKills = 0; + public int tempVorkathKills = 0; + State state = State.ZOMBIE_SPAWN; NPC vorkath; boolean hasEquipment = false; boolean hasInventory = false; + boolean clickedSafeTile = false; + boolean clickedVorkath = false; boolean init = true; - public static VorkathConfig config; - @Getter - private HashSet acidPools = new HashSet<>(); - String primaryBolts = ""; + Rs2InventorySetup rs2InventorySetup; - final String ZOMBIFIED_SPAWN = "Zombified Spawn"; - - public int vorkathSessionKills = 0; - public int tempVorkathKills = 0; + private static void walkToCenter() { + Rs2Walker.walkFastLocal( + LocalPoint.fromScene(48, 58, Microbot.getClient().getTopLevelWorldView().getScene()) + ); + } - Rs2InventorySetup rs2InventorySetup; + private static void drinkPrayer() { + if ((Microbot.getClient().getBoostedSkillLevel(Skill.PRAYER) * 100) / Microbot.getClient().getRealSkillLevel(Skill.PRAYER) < Random.random(25, 30)) { + Rs2Inventory.interact(Rs2Potion.getPrayerPotionsVariants(), "drink"); + } + } private void calculateState() { if (Rs2Npc.getNpc(NpcID.VORKATH_8061) != null) { @@ -103,7 +113,7 @@ public boolean run(VorkathConfig config) { state = State.BANKING; hasEquipment = false; hasInventory = false; - this.config = config; + VorkathScript.config = config; tempVorkathKills = config.SellItemsAtXKills(); Microbot.getSpecialAttackConfigs().setSpecialAttack(true); @@ -299,8 +309,20 @@ public boolean run(VorkathConfig config) { } } togglePrayer(false); + LootingParameters valueParams = new LootingParameters( + config.priceOfItemsToLoot(), + Integer.MAX_VALUE, + 20, + 1, + 0, + false, + false + ); + Rs2GroundItem.loot("Vorkath's head", 20); - Rs2GroundItem.lootAllItemBasedOnValue(config.priceOfItemsToLoot(), 20); + if (Rs2GroundItem.lootItemBasedOnValue(valueParams)) { + Microbot.pauseAllScripts = false; + } int foodInventorySize = Rs2Inventory.getInventoryFood().size(); boolean hasVenom = Rs2Inventory.hasItem("venom"); boolean hasSuperAntifire = Rs2Inventory.hasItem("super antifire"); @@ -401,12 +423,6 @@ private boolean checkSellingItems(VorkathConfig config) { return false; } - private static void walkToCenter() { - Rs2Walker.walkFastLocal( - LocalPoint.fromScene(48, 58, Microbot.getClient().getTopLevelWorldView().getScene()) - ); - } - /** * will heal and drink pray pots */ @@ -478,7 +494,6 @@ public void togglePrayer(boolean onOff) { private void handleRedBall() { if (doesProjectileExistById(redProjectileId)) { redBallWalk(); - sleep(600); Rs2Npc.interact("Vorkath", "attack"); } } @@ -488,12 +503,6 @@ private void handlePrayer() { togglePrayer(true); } - private static void drinkPrayer() { - if ((Microbot.getClient().getBoostedSkillLevel(Skill.PRAYER) * 100) / Microbot.getClient().getRealSkillLevel(Skill.PRAYER) < Random.random(25, 30)) { - Rs2Inventory.interact(Rs2Potion.getPrayerPotionsVariants(), "drink"); - } - } - private boolean doesProjectileExistById(int id) { for (Projectile projectile : Microbot.getClient().getProjectiles()) { if (projectile.getId() == id) { @@ -543,6 +552,8 @@ boolean isTileSafe(WorldPoint tile) { private void handleAcidWalk() { if (!doesProjectileExistById(acidProjectileId) && !doesProjectileExistById(acidRedProjectileId) && Rs2GameObject.getGameObjects(ObjectID.ACID_POOL_32000).isEmpty()) { Rs2Npc.interact("Vorkath", "attack"); + clickedVorkath = false; + clickedSafeTile = false; state = State.FIGHT_VORKATH; acidPools.clear(); return; @@ -559,10 +570,27 @@ private void handleAcidWalk() { if (safeTile != null) { if (playerLocation.equals(safeTile)) { - Rs2Npc.interact(vorkath, "attack"); + clickedSafeTile = false; + if (clickedVorkath) { + return; + } + WorldPoint wooxTile = new WorldPoint(safeTile.getX(), safeTile.getY() + 1, safeTile.getPlane()); + //Rs2Player.eatAt(75); + //Rs2Walker.walkFastLocal(LocalPoint.fromWorld(Microbot.getClient(), wooxTile)); + Rs2Npc.interact("Vorkath", "attack"); + clickedVorkath = true; + Microbot.log("Walking to woox tile"); + Rs2Random.wait(100, 150); } else { - Rs2Player.eatAt(75); + if (clickedSafeTile) { + clickedVorkath = false; + return; + } + Rs2Player.eatAt(60); Rs2Walker.walkFastLocal(LocalPoint.fromWorld(Microbot.getClient(), safeTile)); + clickedSafeTile = true; + Microbot.log("Walking to safe tile"); + Rs2Random.wait(100, 150); } } } From 93c33cb762c2b1b22b4fcab4e611f368c93106ec Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sun, 18 Aug 2024 22:03:01 +0200 Subject: [PATCH 44/52] Various improvements & Antiban implementation --- .../wintertodt/MWintertodtConfig.java | 15 +- .../wintertodt/MWintertodtOverlay.java | 55 +++++++- .../wintertodt/MWintertodtPlugin.java | 132 ++++++++++++++++-- .../wintertodt/MWintertodtScript.java | 83 +++++++++-- 4 files changed, 258 insertions(+), 27 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtConfig.java index 5fd62b7f52..bfb51b2b24 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtConfig.java @@ -117,11 +117,22 @@ default int foodAmount() { return 6; } + @ConfigItem( + keyName = "MinFood", + name = "Min Food", + description = "Minimum food to start a new game", + position = 3, + section = foodSection + ) + default int minFood() { + return 2; + } + @ConfigItem( keyName = "Eat at %", name = "Eat at %", description = "Eat at specific percentage health.", - position = 3, + position = 4, section = foodSection ) default int eatAt() { @@ -132,7 +143,7 @@ default int eatAt() { keyName = "Hitpoints Tresshold", name = "HP % to run away", description = "Runs to the bank if a specific health treshhold is reached and the player does not have any food in their inventory.", - position = 4, + position = 5, section = foodSection ) default int hpTreshhold() { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtOverlay.java index dac1c0a025..0b50808a81 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtOverlay.java @@ -10,10 +10,15 @@ import java.awt.*; public class MWintertodtOverlay extends OverlayPanel { + private final MWintertodtPlugin plugin; + MWintertodtConfig config; + @Inject - MWintertodtOverlay(MWintertodtPlugin plugin) + MWintertodtOverlay(MWintertodtPlugin plugin, MWintertodtConfig config) { super(plugin); + this.plugin = plugin; + this.config = config; setPosition(OverlayPosition.TOP_CENTER); setNaughty(); } @@ -28,6 +33,54 @@ public Dimension render(Graphics2D graphics) { panelComponent.getChildren().add(LineComponent.builder().build()); + panelComponent.getChildren().add(LineComponent.builder() + .left("Running: " + plugin.getTimeRunning()) + .leftColor(Color.GREEN) + .build()); + panelComponent.getChildren().add(LineComponent.builder().build()); + + panelComponent.getChildren().add(LineComponent.builder() + .left("Won: " + plugin.getWon()) + .leftColor(Color.GREEN) + .build()); + + panelComponent.getChildren().add(LineComponent.builder() + .left("Lost: " + plugin.getLost()) + .leftColor(Color.RED) + .build()); + + panelComponent.getChildren().add(LineComponent.builder() + .left("Cut: " + plugin.getLogsCut()) + .leftColor(Color.GREEN) + .build()); + + + panelComponent.getChildren().add(LineComponent.builder() + .left("Fletched: " + plugin.getLogsFletched()) + .leftColor(Color.GREEN) + .build()); + + + panelComponent.getChildren().add(LineComponent.builder() + .left("Braziers fixed: " + plugin.getBraziersFixed()) + .leftColor(Color.GREEN) + .build()); + + panelComponent.getChildren().add(LineComponent.builder() + .left("Braziers lit: " + plugin.getBraziersLit()) + .leftColor(Color.GREEN) + .build()); + + panelComponent.getChildren().add(LineComponent.builder() + .left("Food consumed: " + plugin.getFoodConsumed()) + .leftColor(Color.GREEN) + .build()); + + panelComponent.getChildren().add(LineComponent.builder() + .left("Times banked: " + plugin.getTimesBanked()) + .leftColor(Color.GREEN) + .build()); + panelComponent.getChildren().add(LineComponent.builder() .left(MWintertodtScript.state.toString()) .build()); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtPlugin.java index e9eeae88e5..7ebf85cf35 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtPlugin.java @@ -1,16 +1,29 @@ package net.runelite.client.plugins.microbot.wintertodt; import com.google.inject.Provides; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; +import net.runelite.api.ChatMessageType; +import net.runelite.api.ItemID; +import net.runelite.api.MessageNode; +import net.runelite.api.Skill; +import net.runelite.api.events.ChatMessage; import net.runelite.api.events.HitsplatApplied; +import net.runelite.api.events.StatChanged; 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.inventory.Rs2Inventory; +import net.runelite.client.plugins.microbot.util.misc.TimeUtils; import net.runelite.client.ui.overlay.OverlayManager; import javax.inject.Inject; import java.awt.*; +import java.time.Instant; @PluginDescriptor( name = PluginDescriptor.Mocrosoft + "Wintertodt", @@ -20,28 +33,77 @@ ) @Slf4j public class MWintertodtPlugin extends Plugin { + @Inject + MWintertodtScript wintertodtScript; @Inject private MWintertodtConfig config; - @Provides - MWintertodtConfig provideConfig(ConfigManager configManager) { - return configManager.getConfig(MWintertodtConfig.class); - } - @Inject private OverlayManager overlayManager; @Inject private MWintertodtOverlay wintertodtOverlay; - @Inject - MWintertodtScript wintertodtScript; + @Getter(AccessLevel.PACKAGE) + private int won; + + @Getter(AccessLevel.PACKAGE) + private int lost; + + @Getter(AccessLevel.PACKAGE) + private int logsCut; + + @Getter(AccessLevel.PACKAGE) + private int logsFletched; + + @Getter(AccessLevel.PACKAGE) + private int braziersFixed; + + @Getter(AccessLevel.PACKAGE) + private int braziersLit; + @Getter + @Setter + private int foodConsumed; + + @Getter + @Setter + private int timesBanked; + + @Getter(AccessLevel.PACKAGE) + private boolean scriptStarted; + + private Instant scriptStartTime; + + @Provides + MWintertodtConfig provideConfig(ConfigManager configManager) { + return configManager.getConfig(MWintertodtConfig.class); + } + + protected String getTimeRunning() { + return scriptStartTime != null ? TimeUtils.getFormattedDurationBetween(scriptStartTime, Instant.now()) : ""; + } + + private void reset() { + this.won = 0; + this.lost = 0; + this.logsCut = 0; + this.logsFletched = 0; + this.braziersFixed = 0; + this.braziersLit = 0; + this.foodConsumed = 0; + this.timesBanked = 0; + this.scriptStartTime = null; + this.scriptStarted = false; + } @Override protected void startUp() throws AWTException { + reset(); + this.scriptStartTime = Instant.now(); + this.scriptStarted = true; if (overlayManager != null) { overlayManager.add(wintertodtOverlay); } - wintertodtScript.run(config); + wintertodtScript.run(config, this); } protected void shutDown() { @@ -49,9 +111,61 @@ protected void shutDown() { overlayManager.remove(wintertodtOverlay); } + @Subscribe + public void onChatMessage(ChatMessage chatMessage) { + ChatMessageType chatMessageType = chatMessage.getType(); + MessageNode messageNode = chatMessage.getMessageNode(); + + if (!scriptStarted + || !isInWintertodtRegion() + || chatMessageType != ChatMessageType.GAMEMESSAGE + && chatMessageType != ChatMessageType.SPAM) { + return; + } + + + if (messageNode.getValue().startsWith("You fix the brazier")) { + braziersFixed++; + } + + if (messageNode.getValue().startsWith("You light the brazier")) { + braziersLit++; + } + + if (messageNode.getValue().startsWith("You have gained a supply crate")) { + won++; + } + + if (messageNode.getValue().startsWith("You did not earn enough points")) { + lost++; + } + + } + + private boolean isInWintertodtRegion() { + return Microbot.getClient().getLocalPlayer().getWorldLocation().getRegionID() == 6462; + } + + private int getResourcesInInventory() { + return Rs2Inventory.count(ItemID.BRUMA_ROOT) + Rs2Inventory.count(ItemID.BRUMA_KINDLING); + } + @Subscribe public void onHitsplatApplied(HitsplatApplied hitsplatApplied) { - wintertodtScript.onHitsplatApplied(hitsplatApplied); + MWintertodtScript.onHitsplatApplied(hitsplatApplied); + } + + @Subscribe + public void onStatChanged(StatChanged event) { + + + if (event.getSkill() == Skill.WOODCUTTING) { + logsCut++; + } + + if (event.getSkill() == Skill.FLETCHING) { + logsFletched++; + } } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtScript.java index bb27314209..2c87f8f05b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtScript.java @@ -6,11 +6,17 @@ 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.breakhandler.BreakHandlerScript; +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.antiban.enums.Activity; +import net.runelite.client.plugins.microbot.util.antiban.enums.PlayStyle; import net.runelite.client.plugins.microbot.util.bank.Rs2Bank; import net.runelite.client.plugins.microbot.util.combat.Rs2Combat; 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.inventory.Rs2Inventory; +import net.runelite.client.plugins.microbot.util.inventory.Rs2Item; import net.runelite.client.plugins.microbot.util.keyboard.Rs2Keyboard; import net.runelite.client.plugins.microbot.util.player.Rs2Player; import net.runelite.client.plugins.microbot.util.walker.Rs2Walker; @@ -19,6 +25,7 @@ import java.util.concurrent.TimeUnit; +import static net.runelite.api.Constants.GAME_TICK_LENGTH; import static net.runelite.api.ObjectID.BRAZIER_29312; import static net.runelite.api.ObjectID.BURNING_BRAZIER_29314; import static net.runelite.client.plugins.microbot.util.Global.sleepUntilTrue; @@ -30,11 +37,12 @@ */ public class MWintertodtScript extends Script { - public static String version = "1.4.1"; + public static String version = "1.4.2"; public static State state = State.BANKING; public static boolean resetActions = false; static MWintertodtConfig config; + static MWintertodtPlugin plugin; private static boolean lockState = false; final WorldPoint BOSS_ROOM = new WorldPoint(1630, 3982, 0); final String SUPPLY_CRATE = "supply crate"; @@ -81,13 +89,35 @@ public static void onHitsplatApplied(HitsplatApplied hitsplatApplied) { } - public boolean run(MWintertodtConfig config) { + public boolean run(MWintertodtConfig config, MWintertodtPlugin plugin) { + MWintertodtScript.config = config; + MWintertodtScript.plugin = plugin; + Rs2Antiban.resetAntibanSettings(); + Rs2Antiban.antibanSetupTemplates.applyGeneralBasicSetup(); + Rs2Antiban.setActivity(Activity.GENERAL_WOODCUTTING); + Rs2AntibanSettings.usePlayStyle = true; + Rs2AntibanSettings.universalAntiban = false; + Rs2AntibanSettings.contextualVariability = true; + Rs2AntibanSettings.dynamicActivity = true; + Rs2AntibanSettings.behavioralVariability = true; + Rs2AntibanSettings.simulateAttentionSpan = false; + Rs2AntibanSettings.simulateFatigue = true; + Rs2AntibanSettings.simulateMistakes = true; + Rs2AntibanSettings.moveMouseRandomly = true; + Rs2AntibanSettings.moveMouseOffScreen = true; + Rs2AntibanSettings.naturalMouse = true; + Rs2AntibanSettings.takeMicroBreaks = true; + Rs2AntibanSettings.profileSwitching = false; + Rs2AntibanSettings.actionCooldownChance = 0.15; + Rs2AntibanSettings.microBreakChance = 0.05; + Rs2Antiban.setPlayStyle(PlayStyle.EXTREME_AGGRESSIVE); state = State.BANKING; mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { try { if (!Microbot.isLoggedIn()) return; if (!super.run()) return; + if (Rs2AntibanSettings.actionCooldownActive) return; long startTime = System.currentTimeMillis(); @@ -105,7 +135,7 @@ public boolean run(MWintertodtConfig config) { boolean isWintertodtAlive = Rs2Widget.hasWidget("Wintertodt's Energy"); GameObject brazier = Rs2GameObject.findObject(BRAZIER_29312, config.brazierLocation().getOBJECT_BRAZIER_LOCATION()); GameObject fireBrazier = Rs2GameObject.findObject(ObjectID.BURNING_BRAZIER_29314, config.brazierLocation().getOBJECT_BRAZIER_LOCATION()); - boolean needBanking = !Rs2Inventory.hasItemAmount(config.food().getName(), config.foodAmount(), false, false) + boolean needBanking = !Rs2Inventory.hasItemAmount(config.food().getName(), config.minFood(), false, false) && !isWintertodtAlive; Widget wintertodtHealthbar = Rs2Widget.getWidget(25952276); @@ -132,7 +162,7 @@ public boolean run(MWintertodtConfig config) { if (!isWintertodtAlive) { if (state != State.ENTER_ROOM && state != State.WAITING && state != State.BANKING) { setLockState(State.GLOBAL, false); - changeState(State.BANKING); + changeState(State.WAITING); } } else { handleMainLoop(); @@ -149,11 +179,19 @@ public boolean run(MWintertodtConfig config) { switch (state) { case BANKING: if (!handleBankLogic(config)) return; + if (BreakHandlerScript.isLockState()) + BreakHandlerScript.setLockState(false); + if (Rs2Player.isFullHealth() && Rs2Inventory.hasItemAmount(config.food().getName(), config.foodAmount(), false, true)) { + MWintertodtScript.plugin.setTimesBanked(plugin.getTimesBanked() + 1); + if (Rs2Antiban.takeMicroBreakByChance() || BreakHandlerScript.isBreakActive()) + break; changeState(State.ENTER_ROOM); } break; case ENTER_ROOM: + if (!BreakHandlerScript.isLockState() && !BreakHandlerScript.isBreakActive()) + BreakHandlerScript.setLockState(true); if (!wintertodtRespawning && !isWintertodtAlive) { Rs2Walker.walkTo(BOSS_ROOM, 12); } else { @@ -179,26 +217,37 @@ public boolean run(MWintertodtConfig config) { Rs2GameObject.interact(ObjectID.BRUMA_ROOTS, "Chop"); sleepUntil(Rs2Player::isAnimating, 2000); resetActions = false; + Rs2Antiban.actionCooldown(); } break; case FLETCH_LOGS: if (Rs2Player.getAnimation() != AnimationID.FLETCHING_BOW_CUTTING || resetActions) { walkToBrazier(); - Rs2Inventory.combine(ItemID.KNIFE, ItemID.BRUMA_ROOT); + + Rs2Item knife = Rs2Inventory.get("knife"); + if (Rs2Inventory.moveItemToSlot(knife, 27)) + sleepUntil(() -> Rs2Inventory.slotContains(27, "knife"), 5000); + Rs2Inventory.combineClosest(ItemID.KNIFE, ItemID.BRUMA_ROOT); resetActions = false; - sleep(Constants.GAME_TICK_LENGTH); + sleep(GAME_TICK_LENGTH); sleepUntil(() -> Rs2Player.getAnimation() != AnimationID.FLETCHING_BOW_CUTTING, 2000); + Rs2Antiban.actionCooldown(); } break; case BURN_LOGS: if (!Microbot.isGainingExp || resetActions) { - TileObject burningBrazier = Rs2GameObject.findObjectById(BURNING_BRAZIER_29314); - if (burningBrazier.getWorldLocation().distanceTo(Rs2Player.getWorldLocation()) < 10) { + if (fireBrazier == null && brazier != null && config.relightBrazier()) { + Rs2GameObject.interact(brazier, "light"); + sleep(1000); + } + if (fireBrazier.getWorldLocation().distanceTo(Rs2Player.getWorldLocation()) < 10 && hasItemsToBurn()) { + Rs2GameObject.interact(BURNING_BRAZIER_29314, "feed"); resetActions = false; - sleep(Constants.GAME_TICK_LENGTH * 3); + sleep(GAME_TICK_LENGTH * 3); + Rs2Antiban.actionCooldown(); } @@ -250,6 +299,7 @@ private boolean shouldBurnLogs() { private boolean shouldEat() { if (eatAt(config.eatAt())) { sleep(600, 800); + plugin.setFoodConsumed(plugin.getFoodConsumed() + 1); Rs2Inventory.dropAll("jug"); resetActions = true; return true; @@ -319,11 +369,12 @@ private void walkToBrazier() { Rs2Walker.walkTo(config.brazierLocation().getBRAZIER_LOCATION(), 2); } else if (!Rs2Player.getWorldLocation().equals(config.brazierLocation().getBRAZIER_LOCATION())) { Rs2Walker.walkFastCanvas(config.brazierLocation().getBRAZIER_LOCATION()); - if (Rs2Player.getWorldLocation().distanceTo(config.brazierLocation().getBRAZIER_LOCATION()) > 4) { - Rs2Player.waitForWalking(); - } else { - sleep(3000); - } + sleep(GAME_TICK_LENGTH); +// if (Rs2Player.getWorldLocation().distanceTo(config.brazierLocation().getBRAZIER_LOCATION()) > 4) { +// Rs2Player.waitForWalking(); +// } else { +// //sleep(3000); +// } } } @@ -336,8 +387,10 @@ private void dodgeOrbDamage() { System.out.println(WorldPoint.fromLocalInstance(Microbot.getClient(), graphicsObject.getLocation()).distanceTo(Rs2Player.getWorldLocation())); //walk south + Rs2Walker.walkFastCanvas(new WorldPoint(Rs2Player.getWorldLocation().getX(), Rs2Player.getWorldLocation().getY() - 1, Rs2Player.getWorldLocation().getPlane())); - Rs2Player.waitForWalking(4000); + Rs2Player.waitForWalking(1000); + sleep(GAME_TICK_LENGTH * 2); resetActions = true; } } From 4b5c66eeec7bcc28ff2f7c29afdc6219cca8d9a4 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sun, 18 Aug 2024 22:03:35 +0200 Subject: [PATCH 45/52] minor change --- .../plugins/microbot/mining/amethyst/AmethystMiningScript.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/AmethystMiningScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/AmethystMiningScript.java index 53ac4f599f..5f17717297 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/AmethystMiningScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/AmethystMiningScript.java @@ -13,7 +13,6 @@ import net.runelite.client.plugins.microbot.mining.amethyst.enums.Status; 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.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.combat.Rs2Combat; @@ -211,7 +210,6 @@ private void moveToMiningSpot() { private void initialize() { Rs2Antiban.antibanSetupTemplates.applyMiningSetup(); - Rs2Antiban.setActivityIntensity(ActivityIntensity.EXTREME); status = Status.IDLE; miningSpot = MiningSpot.NULL; oreVein = null; From 0eab394ea66862ad2f9986c373e7a78fa34e3419 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sun, 18 Aug 2024 22:04:35 +0200 Subject: [PATCH 46/52] minor antiban api update --- .../client/plugins/microbot/fletching/FletchingScript.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 index 0649910b06..52ec619b6f 100644 --- 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 @@ -11,7 +11,6 @@ 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.antiban.enums.Activity; 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.widget.Rs2Widget; @@ -38,8 +37,8 @@ public class FletchingScript extends Script { public void run(FletchingConfig config) { fletchingMode = config.fletchingMode(); - Rs2Antiban.resetAntiban(); - Rs2Antiban.advancedPlayStyleSetup(Activity.GENERAL_FLETCHING); + Rs2Antiban.resetAntibanSettings(); + Rs2Antiban.antibanSetupTemplates.applyFletchingSetup(); mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { try { if (!Microbot.isLoggedIn()) From 206baa310c155f11430dfb5e0ef13978537122b9 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sun, 18 Aug 2024 22:05:27 +0200 Subject: [PATCH 47/52] Antiban api update --- .../plugins/microbot/fishing/eel/EelFishingScript.java | 7 +++---- .../plugins/microbot/fishing/minnows/MinnowsScript.java | 5 ++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/eel/EelFishingScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/eel/EelFishingScript.java index 337a6b9164..0715ad7cdc 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/eel/EelFishingScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/eel/EelFishingScript.java @@ -8,7 +8,6 @@ import net.runelite.client.plugins.microbot.fishing.eel.enums.EelFishingSpot; 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.antiban.enums.Activity; import net.runelite.client.plugins.microbot.util.antiban.enums.ActivityIntensity; import net.runelite.client.plugins.microbot.util.camera.Rs2Camera; import net.runelite.client.plugins.microbot.util.equipment.Rs2Equipment; @@ -30,8 +29,8 @@ public static boolean hasRequiredGloves() { public boolean run(EelFishingConfig config) { this.config = config; - Rs2Antiban.resetAntiban(); - Rs2Antiban.advancedPlayStyleSetup(Activity.GENERAL_FISHING); + Rs2Antiban.resetAntibanSettings(); + Rs2Antiban.antibanSetupTemplates.applyFishingSetup(); mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { if (!super.run() || !Microbot.isLoggedIn() || !Rs2Inventory.hasItem("bait") || !Rs2Inventory.hasItem("rod")) { return; @@ -120,7 +119,7 @@ private void processEels(EelFishingConfig config) { } public void shutdown() { - Rs2Antiban.resetAntiban(); + Rs2Antiban.resetAntibanSettings(); super.shutdown(); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/minnows/MinnowsScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/minnows/MinnowsScript.java index a3bc74088e..8e70cfca81 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/minnows/MinnowsScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/minnows/MinnowsScript.java @@ -10,7 +10,6 @@ import net.runelite.client.plugins.microbot.Script; 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.antiban.enums.Activity; import net.runelite.client.plugins.microbot.util.npc.Rs2Npc; import net.runelite.client.plugins.microbot.util.player.Rs2Player; @@ -31,8 +30,8 @@ public class MinnowsScript extends Script { private int timeout; public boolean run() { - Rs2Antiban.resetAntiban(); - Rs2Antiban.advancedPlayStyleSetup(Activity.CATCHING_MINNOWS); + Rs2Antiban.resetAntibanSettings(); + Rs2Antiban.antibanSetupTemplates.applyFishingSetup(); mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { try { if (!Microbot.isLoggedIn()) return; From 3b0778faf798a5db9ce15c0f77db31cbe24301a8 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Sun, 18 Aug 2024 22:22:39 +0200 Subject: [PATCH 48/52] Version bump & antiban shutdown added --- .../fishing/barbarian/BarbarianFishingScript.java | 14 ++++++++------ .../microbot/fishing/minnows/MinnowsScript.java | 4 +++- .../microbot/fletching/FletchingOverlay.java | 4 ++-- .../microbot/fletching/FletchingScript.java | 4 +++- .../plugins/microbot/mining/AutoMiningScript.java | 11 +++++++++-- .../mining/amethyst/AmethystMiningScript.java | 3 ++- .../motherloadmine/MotherloadMineScript.java | 3 ++- .../microbot/wintertodt/MWintertodtScript.java | 3 ++- .../natehumidifier/HumidifierScript.java | 3 ++- 9 files changed, 33 insertions(+), 16 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/barbarian/BarbarianFishingScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/barbarian/BarbarianFishingScript.java index e21f5c073e..e6fd125a11 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/barbarian/BarbarianFishingScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/barbarian/BarbarianFishingScript.java @@ -4,6 +4,8 @@ import net.runelite.client.game.FishingSpot; import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.plugins.microbot.Script; +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.camera.Rs2Camera; import net.runelite.client.plugins.microbot.util.inventory.DropOrder; import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; @@ -16,21 +18,21 @@ public class BarbarianFishingScript extends Script { - public static String version = "1.1.1"; + public static String version = "1.1.2"; public static int timeout = 0; private BarbarianFishingConfig config; public boolean run(BarbarianFishingConfig config) { this.config = config; - //Rs2Antiban.resetAntiban(); - //Rs2Antiban.advancedPlayStyleSetup(Activity.GENERAL_FISHING); + Rs2Antiban.resetAntibanSettings(); + Rs2Antiban.antibanSetupTemplates.applyFishingSetup(); mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { if (!super.run() || !Microbot.isLoggedIn() || !Rs2Inventory.hasItem("feather") || !Rs2Inventory.hasItem("rod")) { return; } - // if(Rs2Antiban.isActionCooldownActive) - // return; + if (Rs2AntibanSettings.actionCooldownActive) return; + if (Rs2Player.isInteracting()) return; @@ -77,7 +79,7 @@ private void dropInventoryItems(BarbarianFishingConfig config) { } public void shutdown() { - //Rs2Antiban.resetAntiban(); + Rs2Antiban.resetAntibanSettings(); super.shutdown(); } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/minnows/MinnowsScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/minnows/MinnowsScript.java index 8e70cfca81..5a854fe6e2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/minnows/MinnowsScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/fishing/minnows/MinnowsScript.java @@ -18,7 +18,7 @@ @Slf4j public class MinnowsScript extends Script { - public static final String version = "1.0.3"; + public static final String version = "1.0.4"; public static final WorldArea MINNOWS_PLATFORM = new WorldArea(new WorldPoint(2607, 3440, 0), 2622 - 2607, 3446 - 3440); private static final int FLYING_FISH_GRAPHIC_ID = GraphicID.FLYING_FISH; @@ -82,6 +82,8 @@ public void onGameTick() { @Override public void shutdown() { + + Rs2Antiban.resetAntibanSettings(); super.shutdown(); } } 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 index e950860925..098479bed7 100644 --- 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 @@ -1,6 +1,5 @@ package net.runelite.client.plugins.microbot.fletching; -import net.runelite.api.Skill; import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.ui.overlay.OverlayPanel; import net.runelite.client.ui.overlay.OverlayPosition; @@ -24,12 +23,13 @@ public Dimension render(Graphics2D graphics) { try { panelComponent.setPreferredSize(new Dimension(200, 300)); panelComponent.getChildren().add(TitleComponent.builder() - .text("Micro Fletcher V" + FletchingScript.version) + .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()); 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 index 52ec619b6f..a1d7bb8de4 100644 --- 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 @@ -27,7 +27,7 @@ class ProgressiveFletchingModel { public class FletchingScript extends Script { - public static double version = 1.6; + public static String version = "1.6.1"; ProgressiveFletchingModel model = new ProgressiveFletchingModel(); String primaryItemToFletch = ""; @@ -227,6 +227,8 @@ public void calculateItemToFletch() { @Override public void shutdown() { + + Rs2Antiban.resetAntibanSettings(); super.shutdown(); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/AutoMiningScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/AutoMiningScript.java index 175dde6a54..01e5ef6b0c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/AutoMiningScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/AutoMiningScript.java @@ -4,6 +4,8 @@ import net.runelite.api.Skill; import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.plugins.microbot.Script; +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.combat.Rs2Combat; import net.runelite.client.plugins.microbot.util.equipment.Rs2Equipment; @@ -23,16 +25,19 @@ enum State { public class AutoMiningScript extends Script { - public static String version = "1.4.1"; + public static String version = "1.4.2"; State state = State.MINING; public boolean run(AutoMiningConfig config) { initialPlayerLocation = null; + Rs2Antiban.resetAntibanSettings(); + Rs2Antiban.antibanSetupTemplates.applyMiningSetup(); + Rs2AntibanSettings.actionCooldownChance = 0.1; mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { try { if (!super.run()) return; if (!Microbot.isLoggedIn()) return; - + if (Rs2AntibanSettings.actionCooldownActive) return; if (initialPlayerLocation == null) { initialPlayerLocation = Rs2Player.getWorldLocation(); } @@ -60,6 +65,8 @@ public boolean run(AutoMiningConfig config) { if (rock != null) { if (Rs2GameObject.interact(rock)) { Rs2Player.waitForXpDrop(Skill.MINING, true); + Rs2Antiban.actionCooldown(); + Rs2Antiban.takeMicroBreakByChance(); } } break; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/AmethystMiningScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/AmethystMiningScript.java index 5f17717297..418ac59d13 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/AmethystMiningScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/amethyst/AmethystMiningScript.java @@ -29,7 +29,7 @@ import java.util.concurrent.TimeUnit; public class AmethystMiningScript extends Script { - public static String version = "1.1.0"; + public static String version = "1.1.1"; public static Status status = Status.IDLE; public static WallObject oreVein; private static AmethystMiningConfig config; @@ -217,6 +217,7 @@ private void initialize() { @Override public void shutdown() { + Rs2Antiban.resetAntibanSettings(); super.shutdown(); status = Status.IDLE; miningSpot = MiningSpot.NULL; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/motherloadmine/MotherloadMineScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/motherloadmine/MotherloadMineScript.java index c9ac000f92..573745032b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/motherloadmine/MotherloadMineScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/mining/motherloadmine/MotherloadMineScript.java @@ -27,7 +27,7 @@ @Slf4j public class MotherloadMineScript extends Script { - public static final String version = "1.6.4"; + public static final String version = "1.6.5"; private static final WorldArea UPSTAIRS = new WorldArea(new WorldPoint(3747, 5676, 0), 7, 8); private static final WorldPoint HOPPER = new WorldPoint(3748, 5674, 0); private static final int UPPER_FLOOR_HEIGHT = -490; @@ -303,6 +303,7 @@ private boolean isUpperFloor() { } public void shutdown() { + Rs2Antiban.resetAntibanSettings(); oreVein = null; miningSpot = MLMMiningSpot.IDLE; Rs2Walker.setTarget(null); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtScript.java index 2c87f8f05b..b2be31f4a4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/wintertodt/MWintertodtScript.java @@ -37,7 +37,7 @@ */ public class MWintertodtScript extends Script { - public static String version = "1.4.2"; + public static String version = "1.4.3"; public static State state = State.BANKING; public static boolean resetActions = false; @@ -361,6 +361,7 @@ private void dropUnnecessaryItems() { @Override public void shutdown() { + Rs2Antiban.resetAntibanSettings(); super.shutdown(); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/nateplugins/moneymaking/natehumidifier/HumidifierScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/nateplugins/moneymaking/natehumidifier/HumidifierScript.java index 184c9a09eb..d19b8057ed 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/nateplugins/moneymaking/natehumidifier/HumidifierScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/nateplugins/moneymaking/natehumidifier/HumidifierScript.java @@ -15,7 +15,7 @@ public class HumidifierScript extends Script { - public static String version = "1.6.1"; + public static String version = "1.6.2"; public static String itemsProcessedMessage = ""; public static String profitMessage = "Calculating..."; private static long itemsProcessed = 0; @@ -96,6 +96,7 @@ private void bank(HumidifierConfig config, boolean hasAstralRunes){ @Override public void shutdown() { + Rs2Antiban.resetAntibanSettings(); super.shutdown(); itemsProcessed = 0; } From 46f29a7bdb35cb8edec8ccdd288ede75fe986818 Mon Sep 17 00:00:00 2001 From: chsami Date: Mon, 19 Aug 2024 22:06:25 +0200 Subject: [PATCH 49/52] add lineofsight config for woodcutting --- .../client/plugins/camera/CameraPlugin.java | 26 ++++++------------- .../microbot/util/inventory/Rs2Inventory.java | 2 +- .../woodcutting/AutoWoodcuttingConfig.java | 12 ++++++++- .../woodcutting/AutoWoodcuttingScript.java | 2 +- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraPlugin.java index 9689414f7f..cb4d143b24 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/camera/CameraPlugin.java @@ -29,23 +29,8 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.primitives.Ints; import com.google.inject.Provides; -import java.awt.event.KeyEvent; -import java.awt.event.MouseEvent; -import javax.inject.Inject; -import javax.swing.SwingUtilities; -import net.runelite.api.Client; -import net.runelite.api.MenuAction; -import net.runelite.api.MenuEntry; -import net.runelite.api.ScriptID; -import net.runelite.api.SettingID; -import net.runelite.api.VarClientInt; -import net.runelite.api.VarPlayer; -import net.runelite.api.events.BeforeRender; -import net.runelite.api.events.ClientTick; -import net.runelite.api.events.FocusChanged; -import net.runelite.api.events.ScriptCallbackEvent; -import net.runelite.api.events.ScriptPreFired; -import net.runelite.api.events.WidgetLoaded; +import net.runelite.api.*; +import net.runelite.api.events.*; import net.runelite.api.widgets.ComponentID; import net.runelite.api.widgets.InterfaceID; import net.runelite.api.widgets.JavaScriptCallback; @@ -63,6 +48,11 @@ import net.runelite.client.ui.overlay.tooltip.Tooltip; import net.runelite.client.ui.overlay.tooltip.TooltipManager; +import javax.inject.Inject; +import javax.swing.*; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; + @PluginDescriptor( name = "Camera", description = "Expands zoom limit, provides vertical camera, and remaps mouse input keys", @@ -274,7 +264,7 @@ public void keyTyped(KeyEvent e) @Override public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_CONTROL) + if (e.getKeyCode() == KeyEvent.VK_F1) { controlDown = true; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java index 7ec9dd6d95..53cd7faf18 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2Inventory.java @@ -8,6 +8,7 @@ import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.plugins.microbot.globval.enums.InterfaceTab; import net.runelite.client.plugins.microbot.util.bank.Rs2Bank; +import net.runelite.client.plugins.microbot.util.combat.Rs2Combat; 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.grandexchange.Rs2GrandExchange; @@ -24,7 +25,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Optional; import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingConfig.java index efe5d9bdf8..30d0cbed49 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingConfig.java @@ -40,6 +40,17 @@ default int distanceToStray() { return 20; } + @ConfigItem( + keyName = "CheckLineOfSight", + name = "Check LineOfSight", + description = "Will only cut tree's when has line of sight", + position = 2, + section = generalSection + ) + default boolean useHasLineOfSight() + { + return false; + } @ConfigItem( keyName = "Hop", @@ -93,5 +104,4 @@ default WoodcuttingWalkBack walkBack() { return WoodcuttingWalkBack.LAST_LOCATION; } - } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingScript.java index dd00b6fc0d..13f18570b0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingScript.java @@ -72,7 +72,7 @@ public boolean run(AutoWoodcuttingConfig config) { return; } - GameObject tree = Rs2GameObject.findObject(config.TREE().getName(), true, config.distanceToStray(), true, getInitialPlayerLocation()); + GameObject tree = Rs2GameObject.findObject(config.TREE().getName(), true, config.distanceToStray(), config.useHasLineOfSight(), getInitialPlayerLocation()); if (tree != null) { if(Rs2GameObject.interact(tree, config.TREE().getAction())) { From 889b14687763e10fd834854a6005c40bd47370e2 Mon Sep 17 00:00:00 2001 From: chsami Date: Tue, 20 Aug 2024 06:49:41 +0200 Subject: [PATCH 50/52] areSurroundingTilesWalkable for woodcutting --- .../playerassist/PlayerAssistOverlay.java | 4 --- .../util/gameobject/Rs2GameObject.java | 5 +++- .../plugins/microbot/util/tile/Rs2Tile.java | 25 ++++++++++++++++--- .../woodcutting/AutoWoodcuttingConfig.java | 11 -------- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/playerassist/PlayerAssistOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/playerassist/PlayerAssistOverlay.java index d014f50b0d..31ec5aec6b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/playerassist/PlayerAssistOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/playerassist/PlayerAssistOverlay.java @@ -6,7 +6,6 @@ import net.runelite.api.coords.LocalPoint; import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.plugins.microbot.playerassist.model.Monster; -import net.runelite.client.plugins.microbot.util.player.Rs2Player; import net.runelite.client.ui.overlay.OverlayLayer; import net.runelite.client.ui.overlay.OverlayPanel; import net.runelite.client.ui.overlay.OverlayPosition; @@ -53,9 +52,6 @@ public static void renderMinimapRect(Client client, Graphics2D graphics, Point c public Dimension render(Graphics2D graphics) { if (attackableNpcs == null) return null; - - net.runelite.api.Point loc = Perspective.localToCanvas(Microbot.getClient(), Rs2Player.getLocalLocation(), Rs2Player.getWorldLocation().getPlane(), 150); - LocalPoint lp = LocalPoint.fromWorld(Microbot.getClient(), config.centerLocation()); if (lp != null) { Polygon poly = Perspective.getCanvasTileAreaPoly(Microbot.getClient(), lp, config.attackRadius() * 2); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java index 583807de2c..ce396b7cc7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/gameobject/Rs2GameObject.java @@ -432,11 +432,14 @@ public static GameObject findObject(String objectName, boolean exact, int distan } for (GameObject gameObject : gameObjects) { - ObjectComposition objComp = convertGameObjectToObjectComposition(gameObject); + if (!Rs2Tile.areSurroundingTilesWalkable(gameObject.getWorldLocation(), gameObject.sizeX(), gameObject.sizeY())) + continue; if (hasLineOfSight && !hasLineOfSight(gameObject)) continue; + ObjectComposition objComp = convertGameObjectToObjectComposition(gameObject); + if (objComp == null) { continue; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/tile/Rs2Tile.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/tile/Rs2Tile.java index 9661b967f3..8a335a42dc 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/tile/Rs2Tile.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/tile/Rs2Tile.java @@ -1,10 +1,7 @@ package net.runelite.client.plugins.microbot.util.tile; import lombok.Getter; -import net.runelite.api.Client; -import net.runelite.api.CollisionDataFlag; -import net.runelite.api.GraphicsObject; -import net.runelite.api.Tile; +import net.runelite.api.*; import net.runelite.api.coords.LocalPoint; import net.runelite.api.coords.WorldPoint; import net.runelite.client.plugins.devtools.MovementFlag; @@ -242,6 +239,26 @@ public static boolean isTileReachable(WorldPoint targetPoint) { return isVisited(targetPoint, visited); } + public static boolean areSurroundingTilesWalkable(WorldPoint worldPoint, int sizeX, int sizeY) { + for (int dx = -1; dx <= sizeX; dx++) { + for (int dy = -1; dy <= sizeY; dy++) { + // Skip the inside tiles, only check the border + if (dx >= 0 && dx < sizeX && dy >= 0 && dy < sizeY) { + continue; + } + + int checkX = worldPoint.getX() + dx; + int checkY = worldPoint.getY() + dy; + + if (isTileReachable(new WorldPoint(checkX, checkY, worldPoint.getPlane()))) { + return true; + } + } + } + + return false; + } + private static boolean isWithinBounds(int x, int y) { return x >= 0 && y >= 0 && x < 104 && y < 104; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingConfig.java index 30d0cbed49..1b59b00bd9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingConfig.java @@ -40,17 +40,6 @@ default int distanceToStray() { return 20; } - @ConfigItem( - keyName = "CheckLineOfSight", - name = "Check LineOfSight", - description = "Will only cut tree's when has line of sight", - position = 2, - section = generalSection - ) - default boolean useHasLineOfSight() - { - return false; - } @ConfigItem( keyName = "Hop", From a9a8a12d4892ba8726ac836397d52dd8c573d73d Mon Sep 17 00:00:00 2001 From: chsami Date: Tue, 20 Aug 2024 06:49:56 +0200 Subject: [PATCH 51/52] added woodcutting overlay --- .../woodcutting/AutoWoodcuttingOverlay.java | 27 +++++++++++++++++-- .../woodcutting/AutoWoodcuttingScript.java | 25 ++++++++++++++--- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingOverlay.java index 871722a61c..ace6e77482 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingOverlay.java @@ -1,20 +1,32 @@ package net.runelite.client.plugins.microbot.woodcutting; +import net.runelite.api.Perspective; +import net.runelite.api.coords.LocalPoint; import net.runelite.client.plugins.microbot.Microbot; +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.OverlayPriority; import net.runelite.client.ui.overlay.components.LineComponent; import net.runelite.client.ui.overlay.components.TitleComponent; import javax.inject.Inject; import java.awt.*; +import static net.runelite.client.ui.overlay.OverlayUtil.renderPolygon; + public class AutoWoodcuttingOverlay extends OverlayPanel { + private static final Color WHITE_TRANSLUCENT = new Color(255, 255, 255, 127); + private final AutoWoodcuttingConfig config; + @Inject - AutoWoodcuttingOverlay(AutoWoodcuttingPlugin plugin) + AutoWoodcuttingOverlay(AutoWoodcuttingPlugin plugin, AutoWoodcuttingConfig config) { super(plugin); - setPosition(OverlayPosition.TOP_LEFT); + this.config = config; + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + setPriority(OverlayPriority.HIGH); setNaughty(); } @Override @@ -31,6 +43,17 @@ public Dimension render(Graphics2D graphics) { .left(Microbot.status) .build()); + if (config.distanceToStray() < 21) { + LocalPoint lp = LocalPoint.fromWorld(Microbot.getClient(), AutoWoodcuttingScript.initPlayerLoc(config)); + if (lp != null) { + Polygon poly = Perspective.getCanvasTileAreaPoly(Microbot.getClient(), lp, config.distanceToStray() * 2); + + if (poly != null) + { + renderPolygon(graphics, poly, WHITE_TRANSLUCENT); + } + } + } } catch(Exception ex) { System.out.println(ex.getMessage()); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingScript.java index 13f18570b0..368b7691a4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/woodcutting/AutoWoodcuttingScript.java @@ -28,11 +28,19 @@ enum State { public class AutoWoodcuttingScript extends Script { - public static String version = "1.6.0"; + public static String version = "1.6.1"; public boolean cannotLightFire = false; State state = State.WOODCUTTING; - private WorldPoint returnPoint; + private static WorldPoint returnPoint; + + public static WorldPoint initPlayerLoc(AutoWoodcuttingConfig config) { + if (config.walkBack() == WoodcuttingWalkBack.INITIAL_LOCATION) { + return getInitialPlayerLocation(); + } else { + return returnPoint; + } + } public boolean run(AutoWoodcuttingConfig config) { if (config.hopWhenPlayerDetected()) { @@ -49,6 +57,10 @@ public boolean run(AutoWoodcuttingConfig config) { initialPlayerLocation = Rs2Player.getWorldLocation(); } + if (returnPoint == null) { + returnPoint = Rs2Player.getWorldLocation(); + } + if (!config.TREE().hasRequiredLevel()) { Microbot.showMessage("You do not have the required woodcutting level to cut this tree."); shutdown(); @@ -72,7 +84,7 @@ public boolean run(AutoWoodcuttingConfig config) { return; } - GameObject tree = Rs2GameObject.findObject(config.TREE().getName(), true, config.distanceToStray(), config.useHasLineOfSight(), getInitialPlayerLocation()); + GameObject tree = Rs2GameObject.findObject(config.TREE().getName(), true, config.distanceToStray(), false, getInitialPlayerLocation()); if (tree != null) { if(Rs2GameObject.interact(tree, config.TREE().getAction())) { @@ -166,4 +178,11 @@ private void walkBack(AutoWoodcuttingConfig config) { Rs2Walker.walkTo(new WorldPoint(calculateReturnPoint(config).getX() - Random.random(-1, 1), calculateReturnPoint(config).getY() - Random.random(-1, 1), calculateReturnPoint(config).getPlane())); sleepUntil(() -> Rs2Player.getWorldLocation().distanceTo(calculateReturnPoint(config)) <= 4); } + + @Override + public void shutdown() { + super.shutdown(); + returnPoint = null; + initialPlayerLocation = null; + } } \ No newline at end of file From df2242664e1ab59c58559576af139148d8163182 Mon Sep 17 00:00:00 2001 From: SexyPinata <61428716+SexyPinata@users.noreply.github.com> Date: Tue, 20 Aug 2024 17:02:03 +0200 Subject: [PATCH 52/52] Rs2Magic can use natural mouse & removed commented code in fletching --- .../plugins/microbot/fletching/FletchingScript.java | 10 +++++----- .../client/plugins/microbot/util/magic/Rs2Magic.java | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) 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 index a1d7bb8de4..9bdcb9c600 100644 --- 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 @@ -165,11 +165,11 @@ private void fletch(FletchingConfig config) { sleepUntil(() -> Rs2Widget.getWidget(17694736) == null); Rs2Antiban.actionCooldown(); Rs2Antiban.takeMicroBreakByChance(); -// if (fletchingMode == FletchingMode.PROGRESSIVE) { -// sleepUntil(() -> !Rs2Inventory.hasItemAmount(secondaryItemToFletch, model.getFletchingItem().getAmountRequired()) || hasLeveledUp, 60000); -// } else { -// sleepUntil(() -> !Rs2Inventory.hasItemAmount(secondaryItemToFletch, config.fletchingItem().getAmountRequired()) || hasLeveledUp, 60000); -// } + if (fletchingMode == FletchingMode.PROGRESSIVE) { + sleepUntil(() -> !Rs2Inventory.hasItemAmount(secondaryItemToFletch, model.getFletchingItem().getAmountRequired()) || hasLeveledUp, 60000); + } else { + sleepUntil(() -> !Rs2Inventory.hasItemAmount(secondaryItemToFletch, config.fletchingItem().getAmountRequired()) || hasLeveledUp, 60000); + } } private boolean configChecks(FletchingConfig config) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/magic/Rs2Magic.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/magic/Rs2Magic.java index 5f7967701b..821c41b96c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/magic/Rs2Magic.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/magic/Rs2Magic.java @@ -68,7 +68,7 @@ public static void cast(MagicAction magicSpell) { if (magicSpell.getWidgetId() == -1) throw new NotImplementedException("This spell has not been configured yet in the MagicAction.java class"); - Microbot.doInvoke(new NewMenuEntry("cast", -1, magicSpell.getWidgetId(), menuAction.getId(), identifier, -1, magicSpell.getName()), new Rectangle(1, 1)); + Microbot.doInvoke(new NewMenuEntry("cast", -1, magicSpell.getWidgetId(), menuAction.getId(), identifier, -1, magicSpell.getName()), new Rectangle(Rs2Widget.getWidget(magicSpell.getWidgetId()).getBounds())); //Rs2Reflection.invokeMenu(-1, magicSpell.getWidgetId(), menuAction.getId(), 1, -1, "Cast", "" + magicSpell.getName() + "", -1, -1); }