diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml
index 9a8b53c4276..7d58256e577 100644
--- a/runelite-client/pom.xml
+++ b/runelite-client/pom.xml
@@ -41,7 +41,7 @@
nogit
false
false
- 2.0.16.1
+ 2.0.17
nogit
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2RunePouch.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2RunePouch.java
index acf96baee99..bf69a4c4873 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2RunePouch.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/inventory/Rs2RunePouch.java
@@ -1,7 +1,9 @@
package net.runelite.client.plugins.microbot.util.inventory;
+import java.awt.Rectangle;
import lombok.Getter;
import lombok.Setter;
+import net.runelite.api.MenuAction;
import net.runelite.api.Varbits;
import net.runelite.api.events.VarbitChanged;
import net.runelite.api.events.WidgetLoaded;
@@ -12,6 +14,8 @@
import net.runelite.client.plugins.microbot.util.Global;
import net.runelite.client.plugins.microbot.util.bank.Rs2Bank;
import net.runelite.client.plugins.microbot.util.magic.Runes;
+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.widget.Rs2Widget;
import java.util.*;
@@ -42,10 +46,10 @@ public class Rs2RunePouch
};
private static final int BANK_PARENT_ID = InterfaceID.BANKSIDE;
- private static final int RUNEPOUCH_ROOT_CHILD_ID = 19;
- private static final int RUNEPOUCH_CLOSE_CHILD_ID = 22;
- private static final int RUNEPOUCH_DEPOSIT_ALL_CHILD_ID = 20;
- private static final List RUNEPOUCH_LOADOUT_WIDGETS = Arrays.asList(28, 30, 32, 34);
+ private static final int RUNEPOUCH_ROOT_CHILD_ID = 19; // Validated
+ private static final int RUNEPOUCH_CLOSE_CHILD_ID = 22; // Validated
+ private static final int RUNEPOUCH_DEPOSIT_ALL_CHILD_ID = 20; // Validated
+ private static final List RUNEPOUCH_LOADOUT_WIDGETS = Arrays.asList(34, 38, 41, 44, 46, 48, 50, 52, 54, 56); // New Loadout Child IDs
@Getter
private static final List slots = new ArrayList<>();
@@ -427,8 +431,22 @@ private static boolean coreLoad(Map requiredRunes)
if (loadoutMap.equals(requiredRunes))
{
- int widgetIndex = RUNEPOUCH_LOADOUT_WIDGETS.get(entry.getKey());
- Rs2Widget.clickWidget(BANK_PARENT_ID, (widgetIndex + 1));
+ final int widgetIndex = RUNEPOUCH_LOADOUT_WIDGETS.get(entry.getKey());
+ Widget parentLoadoutWidget = Rs2Widget.getWidget(BANK_PARENT_ID, widgetIndex);
+ if (parentLoadoutWidget == null || parentLoadoutWidget.getStaticChildren() == null)
+ {
+ Microbot.log("Failed to find loadout widget for index: " + widgetIndex, Level.WARNING);
+ break;
+ }
+ Widget loadWidget = Rs2Widget.findWidget("Load", List.of(parentLoadoutWidget.getStaticChildren()));
+ if (loadWidget == null)
+ {
+ Microbot.log("Failed to find 'Load' child widget in loadout index: " + widgetIndex, Level.WARNING);
+ break;
+ }
+ Rectangle loadBounds = loadWidget.getBounds();
+ NewMenuEntry menuEntry = new NewMenuEntry("Load", "", 1, MenuAction.CC_OP, -1, loadWidget.getId(), false);
+ Microbot.doInvoke(menuEntry, loadBounds != null && Rs2UiHelper.isRectangleWithinCanvas(loadBounds) ? loadBounds : Rs2UiHelper.getDefaultRectangle());
Global.sleepUntil(() -> getRunes().entrySet().stream().allMatch(e -> requiredRunes.getOrDefault(e.getKey(), 0) <= e.getValue()));
return closeRunePouch();
}
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/keyboard/Rs2Keyboard.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/keyboard/Rs2Keyboard.java
index 1040b291402..9189db44a17 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/keyboard/Rs2Keyboard.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/keyboard/Rs2Keyboard.java
@@ -3,172 +3,125 @@
import net.runelite.client.plugins.microbot.Microbot;
import net.runelite.client.plugins.microbot.util.Global;
+import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
-import static java.awt.event.KeyEvent.CHAR_UNDEFINED;
-import net.runelite.client.plugins.microbot.util.math.Rs2Random;
-
/**
* Utility class for simulating keyboard input.
*/
-public class Rs2Keyboard
-{
-
+public final class Rs2Keyboard {
/**
- * Gets the current game canvas.
- *
- * @return the game canvas
+ * Private constructor to prevent instantiation.
*/
- private static Canvas getCanvas()
- {
- return Microbot.getClient().getCanvas();
- }
+ private Rs2Keyboard() {}
/**
- * Executes a given action with the canvas temporarily made focusable if it wasn't already.
- * This ensures key events are properly dispatched to the game client.
- *
- * @param action the code to run while the canvas is focusable
+ * Get the game canvas.
+ * @return
*/
- private static void withFocusCanvas(Runnable action)
- {
- Canvas canvas = getCanvas();
- boolean originalFocus = canvas.isFocusable();
- if (!originalFocus) canvas.setFocusable(true);
-
- try
- {
- action.run();
- }
- finally
- {
- if (!originalFocus) canvas.setFocusable(false);
- }
- }
+ private static Canvas canvas() { return Microbot.getClient().getCanvas(); }
/**
- * Dispatches a low-level KeyEvent to the canvas after a specified delay.
- *
- * @param id the KeyEvent type (e.g. KEY_TYPED, KEY_PRESSED, etc.)
- * @param keyCode the key code from {@link KeyEvent}
- * @param keyChar the character to type, if applicable
- * @param delay the delay in milliseconds before the event time is set
+ * Run an action with the canvas focused, restoring previous focus state afterwards.
+ * @param action
*/
- private static void dispatchKeyEvent(int id, int keyCode, char keyChar, int delay)
- {
- KeyEvent event = new KeyEvent(
- getCanvas(),
- id,
- System.currentTimeMillis() + delay,
- 0,
- keyCode,
- keyChar
- );
- getCanvas().dispatchEvent(event);
+ private static void withFocusedCanvas(Runnable action) {
+ Canvas c = canvas();
+ boolean wasFocusable = c.isFocusable();
+ boolean hadFocus = c.isFocusOwner();
+ if (!wasFocusable) c.setFocusable(true);
+ if (!hadFocus) c.requestFocusInWindow();
+ try { action.run(); }
+ finally {
+ if (!wasFocusable) c.setFocusable(false);
+ }
}
/**
- * Types out a string character-by-character using KEY_TYPED events.
- * Each character is sent with a short randomized delay and sleep between characters.
- *
- * @param word the string to type into the game
+ * Post an event to the AWT event queue.
+ * @param e
*/
- public static void typeString(final String word)
- {
- withFocusCanvas(() -> {
- for (char c : word.toCharArray())
- {
- int delay = Rs2Random.between(20, 200);
- dispatchKeyEvent(KeyEvent.KEY_TYPED, KeyEvent.VK_UNDEFINED, c, delay);
- Global.sleep(100, 200);
- }
- });
+ private static void post(KeyEvent e) {
+ // Safe from any thread; posts into AWT event queue
+ Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(e);
}
/**
- * Simulates pressing a single character using a KEY_TYPED event.
- *
- * @param key the character to press
+ * Fire a keyboard event on the canvas.
+ * @param id
+ * @param keyCode
+ * @param keyChar
*/
- public static void keyPress(final char key)
- {
- withFocusCanvas(() -> {
- int delay = Rs2Random.between(20, 200);
- dispatchKeyEvent(KeyEvent.KEY_TYPED, KeyEvent.VK_UNDEFINED, key, delay);
- });
+ private static void fire(int id, int keyCode, char keyChar) {
+ long now = System.currentTimeMillis();
+ KeyEvent e = new KeyEvent(canvas(), id, now, 0, keyCode, keyChar);
+ if (SwingUtilities.isEventDispatchThread()) {
+ // Dispatch directly if on EDT
+ canvas().dispatchEvent(e);
+ } else {
+ post(e);
+ }
}
/**
- * Simulates holding the Shift key using a KEY_PRESSED event.
+ * Type a string into the focused canvas, one character at a time.
+ * @param s
*/
- public static void holdShift()
- {
- withFocusCanvas(() -> {
- int delay = Rs2Random.between(20, 200);
- dispatchKeyEvent(KeyEvent.KEY_PRESSED, KeyEvent.VK_SHIFT, CHAR_UNDEFINED, delay);
+ public static void typeString(String s) {
+ withFocusedCanvas(() -> {
+ for (char ch : s.toCharArray()) {
+ // For printable characters, send TYPED only
+ fire(KeyEvent.KEY_TYPED, KeyEvent.VK_UNDEFINED, ch);
+ Global.sleep(30, 80);
+ }
});
}
/**
- * Simulates releasing the Shift key using a KEY_RELEASED event.
+ * Hold a key down on the focused canvas.
+ * @param keyCode
*/
- public static void releaseShift()
- {
- withFocusCanvas(() -> {
- int delay = Rs2Random.between(20, 200);
- dispatchKeyEvent(KeyEvent.KEY_RELEASED, KeyEvent.VK_SHIFT, CHAR_UNDEFINED, delay);
- });
+ public static void keyHold(int keyCode) {
+ withFocusedCanvas(() -> fire(KeyEvent.KEY_PRESSED, keyCode, KeyEvent.CHAR_UNDEFINED));
}
/**
- * Simulates holding down a key using a KEY_PRESSED event.
- *
- * @param key the key code from {@link KeyEvent}
+ * Release a key on the focused canvas.
+ * @param keyCode
*/
- public static void keyHold(int key)
- {
- withFocusCanvas(() ->
- dispatchKeyEvent(KeyEvent.KEY_PRESSED, key, CHAR_UNDEFINED, 0)
- );
+ public static void keyRelease(int keyCode) {
+ withFocusedCanvas(() -> fire(KeyEvent.KEY_RELEASED, keyCode, KeyEvent.CHAR_UNDEFINED));
}
/**
- * Simulates releasing a key using a KEY_RELEASED event.
- *
- * @param key the key code from {@link KeyEvent}
+ * Press (hold and release) a key on the focused canvas.
+ * @param keyCode
*/
- public static void keyRelease(int key)
- {
- withFocusCanvas(() -> {
- int delay = Rs2Random.between(20, 200);
- dispatchKeyEvent(KeyEvent.KEY_RELEASED, key, CHAR_UNDEFINED, delay);
+ public static void keyPress(int keyCode) {
+ withFocusedCanvas(() -> {
+ fire(KeyEvent.KEY_PRESSED, keyCode, KeyEvent.CHAR_UNDEFINED);
+ Global.sleep(20, 40); // real delay to preserve ordering
+ fire(KeyEvent.KEY_RELEASED, keyCode, KeyEvent.CHAR_UNDEFINED);
});
}
/**
- * Simulates pressing and releasing a key in quick succession.
- *
- * @param key the key code from {@link KeyEvent}
+ * Press (hold and release) a key on the focused canvas, with a character.
*/
- public static void keyPress(int key)
- {
- keyHold(key);
- keyRelease(key);
- }
+ public static void holdShift() { keyHold(KeyEvent.VK_SHIFT); }
+ public static void releaseShift() { keyRelease(KeyEvent.VK_SHIFT); }
/**
- * Simulates pressing the Enter key.
- * If the player is not logged in, this uses KEY_TYPED to avoid auto-login triggers.
+ * Press (hold and release) the Enter key on the focused canvas.
*/
- public static void enter()
- {
- // this is to avoid automatically login with jagex account when you are on the login screen
- if (!Microbot.isLoggedIn()) {
- dispatchKeyEvent(KeyEvent.KEY_TYPED, KeyEvent.VK_UNDEFINED, '\n', 0);
- return;
- }
-
- keyPress(KeyEvent.VK_ENTER);
+ public static void enter() {
+ withFocusedCanvas(() -> {
+ fire(KeyEvent.KEY_PRESSED, KeyEvent.VK_ENTER, KeyEvent.CHAR_UNDEFINED);
+ Global.sleep(20, 40);
+ fire(KeyEvent.KEY_TYPED, KeyEvent.VK_UNDEFINED, '\n');
+ Global.sleep(5, 10);
+ fire(KeyEvent.KEY_RELEASED, KeyEvent.VK_ENTER, KeyEvent.CHAR_UNDEFINED);
+ });
}
-}
\ No newline at end of file
+}
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 f92dece79c8..dc72d46cec4 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
@@ -2008,7 +2008,6 @@ public static boolean isInArea(WorldPoint... worldPoints) {
* @param range an int of range to which the boundaries will be drawn in a square,
* @return true if the player's current location is within the specified area, false otherwise
*/
- @Deprecated(since = "1.5.5", forRemoval = true)
public static boolean isInArea(WorldPoint centerOfArea, int range) {
WorldPoint seCorner = new WorldPoint(centerOfArea.getX() + range, centerOfArea.getY() - range, centerOfArea.getPlane());
WorldPoint nwCorner = new WorldPoint(centerOfArea.getX() - range, centerOfArea.getY() + range, centerOfArea.getPlane());
diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/teleportation_portals.tsv b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/teleportation_portals.tsv
index 0f5c84c6f7a..71c42019479 100644
--- a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/teleportation_portals.tsv
+++ b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/teleportation_portals.tsv
@@ -87,10 +87,10 @@
3097 3508 1 3157 9818 0 Use;Clan Cup portal;27094 1
3157 9818 0 3097 3508 1 Use;Clan Cup portal;27095 1
2040 5240 0 2021 5223 0 Use;Portal;19005 1
-1863 5239 0 1914 5222 0 Use;Portal;20786 1
-2120 5257 0 2146 5287 0 Use;Portal;23707 1
-2119 5258 0 2146 5287 0 Use;Portal;23707 1
-2121 5258 0 2146 5287 0 Use;Portal;23707 1
+1863 5239 0 1914 5222 0 Use;Portal;20786 2309=1 1
+2120 5257 0 2146 5287 0 Use;Portal;23707 2310=1 1
+2119 5258 0 2146 5287 0 Use;Portal;23707 2311=1 1
+2121 5258 0 2146 5287 0 Use;Portal;23707 2312=1 1
2119 5258 0 2146 5287 0 Use;Portal;23707 1
2121 5258 0 2146 5287 0 Use;Portal;23707 1
2364 5212 0 2341 5219 0 Use;Portal;23922 1