Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ project.build.group=net.runelite
project.build.version=1.12.26.2

glslang.path=
microbot.version=2.5.5
microbot.version=2.5.6
microbot.commit.sha=nogit
microbot.repo.url=http://138.201.81.246:8081/repository/microbot-snapshot/
microbot.repo.username=
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,18 @@ private void handleClick(Point point, boolean rightClick) {
clicked(point, rightClick ? MouseEvent.BUTTON3 : MouseEvent.BUTTON1);
setLastClick(point);
}

private boolean shouldMoveNaturally(Point point) {
return point.getX() > 1
&& point.getY() > 1
&& Microbot.naturalMouse != null;
}

public Mouse click(Point point, boolean rightClick) {
if (point == null) return this;

Runnable clickAction = () -> {
if (point.getX() > 1 && point.getY() > 1 && Microbot.naturalMouse != null) {
if (shouldMoveNaturally(point)) {
Microbot.naturalMouse.moveTo(point.getX(), point.getY());
}
handleClick(point, rightClick);
Expand All @@ -137,7 +144,7 @@ public Mouse click(Point point, boolean rightClick, NewMenuEntry entry) {

Runnable clickAction = () -> {
Point newPoint = point;
if (point.getX() > 1 && point.getY() > 1 && Microbot.naturalMouse != null) {
if (shouldMoveNaturally(point)) {
Microbot.naturalMouse.moveTo(point.getX(), point.getY());

if (Rs2UiHelper.hasActor(entry)) {
Expand Down Expand Up @@ -287,14 +294,14 @@ public void shutdown() {
public Mouse drag(Point startPoint, Point endPoint) {
if (startPoint == null || endPoint == null) return this;

if (startPoint.getX() > 1 && startPoint.getY() > 1 && Microbot.naturalMouse != null)
if (shouldMoveNaturally(startPoint))
Microbot.naturalMouse.moveTo(startPoint.getX(), startPoint.getY());
else
move(startPoint);
sleep(Rs2Random.logNormalBounded(50, 80));
pressed(startPoint, MouseEvent.BUTTON1);
sleep(Rs2Random.logNormalBounded(80, 120));
if (endPoint.getX() > 1 && endPoint.getY() > 1 && Microbot.naturalMouse != null)
if (shouldMoveNaturally(endPoint))
Microbot.naturalMouse.moveTo(endPoint.getX(), endPoint.getY());
else
move(endPoint);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1491,62 +1491,162 @@ private static boolean handleDoors(List<WorldPoint> path, int index) {
TileObject object = (wall != null)
? wall
: Rs2GameObject.getGameObject(o -> o.getWorldLocation().equals(probe), probe, 3);
if (object == null) continue;
if (tryHandleDoorObject(object, probe, fromWp, toWp, doorActions, false)) {
return true;
}
}
}

ObjectComposition comp = Rs2GameObject.convertToObjectComposition(object);
// Ignore imposter objects
if (comp == null || comp.getImpostorIds() != null || comp.getName().equals("null")) continue;
TileObject nearbyDoor = findDoorNearSegment(fromWp, toWp, doorActions);
if (nearbyDoor != null && tryHandleDoorObject(nearbyDoor, nearbyDoor.getWorldLocation(), fromWp, toWp, doorActions, true)) {
return true;
}

String action = Arrays.stream(comp.getActions())
.filter(Objects::nonNull)
.filter(act -> doorActions.stream().anyMatch(dact -> act.toLowerCase().startsWith(dact.toLowerCase())))
.min(Comparator.comparing(act -> doorActions.indexOf(doorActions.stream().filter(dact -> act.toLowerCase().startsWith(dact)).findFirst().orElse(""))))
.orElse(null);
return false;
}

if (action == null) continue;
private static TileObject findDoorNearSegment(WorldPoint fromWp, WorldPoint toWp, List<String> doorActions) {
WorldPoint playerLoc = Rs2Player.getWorldLocation();
if (playerLoc == null || fromWp == null || toWp == null || fromWp.getPlane() != toWp.getPlane()) {
return null;
}

boolean found = false;
final int searchDistance = 10;
return Rs2GameObject.getAll(o -> {
if (o == null || o.getWorldLocation() == null) return false;
WorldPoint loc = o.getWorldLocation();
if (loc.getPlane() != playerLoc.getPlane()) return false;
if (loc.distanceTo2D(playerLoc) > searchDistance) return false;
if (sessionBlacklistedDoors.contains(loc)) return false;
if (!(o instanceof WallObject) && !(o instanceof GameObject)) return false;
ObjectComposition comp = Rs2GameObject.convertToObjectComposition(o);
if (!isDoorComposition(comp, doorActions)) return false;
return isDoorOnSegment(o, fromWp, toWp);
}, playerLoc, searchDistance).stream()
.min(Comparator.comparingInt(o -> o.getWorldLocation().distanceTo2D(playerLoc)))
.orElse(null);
}

final String name = comp.getName();
private static boolean tryHandleDoorObject(TileObject object, WorldPoint probe, WorldPoint fromWp, WorldPoint toWp,
List<String> doorActions, boolean allowSegmentProbe) {
if (object == null || probe == null) return false;

if (object instanceof WallObject) {
int orientation = ((WallObject) object).getOrientationA();
ObjectComposition comp = Rs2GameObject.convertToObjectComposition(object);
if (!isDoorComposition(comp, doorActions)) return false;

if (searchNeighborPoint(orientation, probe, fromWp) || searchNeighborPoint(orientation, probe, toWp)) {
log.info("Found WallObject door - name {} with action {} at {} - from {} to {}", name, action, probe, fromWp, toWp);
found = true;
}
} else {
if (name != null && name.toLowerCase().contains("door")) {
log.info("Found GameObject door - name {} with action {} at {} - from {} to {}", name, action, probe, fromWp, toWp);
found = true;
}
}
String action = getDoorAction(comp, doorActions);
if (action == null) return false;

if (found) {
if (!handleDoorException(object, action)) {
WorldPoint posBefore = Rs2Player.getWorldLocation();
Rs2GameObject.interact(object, action);
Rs2Player.waitForWalking();
WorldPoint posAfter = Rs2Player.getWorldLocation();
boolean moved = posBefore != null && posAfter != null && !posBefore.equals(posAfter);
if (!moved && isQuestLockedDoorDialogue()) {
String dialogue = Rs2Dialogue.getDialogueText();
log.warn("[Walker] Door at {} ({} action={}) appears quest/stat-locked — dialogue=\"{}\" — blacklisting tile, refreshing restrictions, recalculating",
probe, name, action, dialogue);
sessionBlacklistedDoors.add(probe);
Rs2Dialogue.clickContinue();
if (ShortestPathPlugin.pathfinderConfig != null) {
ShortestPathPlugin.pathfinderConfig.refresh();
}
recalculatePath();
}
}
return true;
boolean found = false;
final String name = comp.getName();

if (object instanceof WallObject) {
int orientation = ((WallObject) object).getOrientationA();

if (searchNeighborPoint(orientation, probe, fromWp)
|| searchNeighborPoint(orientation, probe, toWp)
|| (allowSegmentProbe && wallDoorTouchesSegment((WallObject) object, fromWp, toWp))) {
log.info("Found WallObject door - name {} with action {} at {} - from {} to {}", name, action, probe, fromWp, toWp);
found = true;
}
} else if (name != null && name.toLowerCase().contains("door")) {
log.info("Found GameObject door - name {} with action {} at {} - from {} to {}", name, action, probe, fromWp, toWp);
found = true;
}

if (!found) return false;

if (!handleDoorException(object, action)) {
WorldPoint posBefore = Rs2Player.getWorldLocation();
Rs2GameObject.interact(object, action);
Rs2Player.waitForWalking();
WorldPoint posAfter = Rs2Player.getWorldLocation();
boolean moved = posBefore != null && posAfter != null && !posBefore.equals(posAfter);
if (!moved && isQuestLockedDoorDialogue()) {
String dialogue = Rs2Dialogue.getDialogueText();
log.warn("[Walker] Door at {} ({} action={}) appears quest/stat-locked — dialogue=\"{}\" — blacklisting tile, refreshing restrictions, recalculating",
probe, name, action, dialogue);
sessionBlacklistedDoors.add(probe);
Rs2Dialogue.clickContinue();
if (ShortestPathPlugin.pathfinderConfig != null) {
ShortestPathPlugin.pathfinderConfig.refresh();
}
recalculatePath();
}
}
return true;
}

private static boolean isDoorComposition(ObjectComposition comp, List<String> doorActions) {
if (comp == null || comp.getImpostorIds() != null || comp.getName().equals("null") || comp.getActions() == null) {
return false;
}
return getDoorAction(comp, doorActions) != null;
}

private static String getDoorAction(ObjectComposition comp, List<String> doorActions) {
if (comp == null || comp.getActions() == null) {
return null;
}
return Arrays.stream(comp.getActions())
.filter(Objects::nonNull)
.filter(act -> doorActions.stream().anyMatch(dact -> act.toLowerCase().startsWith(dact.toLowerCase())))
.min(Comparator.comparing(act -> doorActions.indexOf(doorActions.stream()
.filter(dact -> act.toLowerCase().startsWith(dact))
.findFirst()
.orElse(""))))
.orElse(null);
}

private static boolean isDoorOnSegment(TileObject object, WorldPoint fromWp, WorldPoint toWp) {
if (object == null || object.getWorldLocation() == null) return false;
if (object instanceof WallObject) {
return wallDoorTouchesSegment((WallObject) object, fromWp, toWp)
|| isPointNearSegment(object.getWorldLocation(), fromWp, toWp, 1);
}
return isPointNearSegment(object.getWorldLocation(), fromWp, toWp, 1);
}

private static boolean wallDoorTouchesSegment(WallObject wall, WorldPoint fromWp, WorldPoint toWp) {
if (wall == null || wall.getWorldLocation() == null || fromWp == null || toWp == null) return false;
if (wall.getWorldLocation().getPlane() != fromWp.getPlane() || fromWp.getPlane() != toWp.getPlane()) return false;

int orientation = wall.getOrientationA();
int x = fromWp.getX();
int y = fromWp.getY();
int steps = 0;
while (steps++ <= 64) {
WorldPoint point = new WorldPoint(x, y, fromWp.getPlane());
if (searchNeighborPoint(orientation, wall.getWorldLocation(), point)) {
return true;
}
if (x == toWp.getX() && y == toWp.getY()) {
return false;
}
x += Integer.signum(toWp.getX() - x);
y += Integer.signum(toWp.getY() - y);
}
return false;
}

private static boolean isPointNearSegment(WorldPoint point, WorldPoint fromWp, WorldPoint toWp, int distance) {
if (point == null || fromWp == null || toWp == null || point.getPlane() != fromWp.getPlane() || fromWp.getPlane() != toWp.getPlane()) {
return false;
}

int x = fromWp.getX();
int y = fromWp.getY();
int steps = 0;
while (steps++ <= 64) {
if (point.distanceTo2D(new WorldPoint(x, y, fromWp.getPlane())) <= distance) {
return true;
}
if (x == toWp.getX() && y == toWp.getY()) {
return false;
}
x += Integer.signum(toWp.getX() - x);
y += Integer.signum(toWp.getY() - y);
}
return false;
}

Expand Down Expand Up @@ -2091,20 +2191,29 @@ private static boolean handleTransports(List<WorldPoint> path, int indexOfStartP
List<TileObject> objects = Rs2GameObject.getAll(o -> {
if (o.getId() == transportObjectId) return true;
Integer legacyClosed = OPEN_TO_CLOSED_MAPPINGS.get(transportObjectId);
if (legacyClosed != null && o.getId() == legacyClosed) return true;
if (!allowClosedVariant) return false;
ObjectComposition comp = Rs2GameObject.convertToObjectComposition(o);
if (comp == null || comp.getActions() == null) return false;
String nm = comp.getName() == null ? "" : comp.getName().toLowerCase();
boolean nameMatches = nm.contains("trapdoor") || nm.contains("manhole")
|| nm.contains("grate") || nm.contains("hatch");
if (!nameMatches) return false;
return Arrays.stream(comp.getActions()).filter(Objects::nonNull)
.anyMatch(a -> a.equalsIgnoreCase("Open"));
return legacyClosed != null && o.getId() == legacyClosed;
}, transport.getOrigin(), 10).stream()
.sorted(Comparator.comparingInt(o -> o.getWorldLocation().distanceTo(transport.getOrigin())))
.collect(Collectors.toList());

if (objects.isEmpty() && allowClosedVariant) {
// The closed-variant fallback needs object composition lookups for name/action
// matching. Keep it off the common exact-id path; doing this for every
// climb-down object was the Varrock staircase delay.
objects = Rs2GameObject.getAll(o -> {
ObjectComposition comp = Rs2GameObject.convertToObjectComposition(o);
if (comp == null || comp.getActions() == null) return false;
String nm = comp.getName() == null ? "" : comp.getName().toLowerCase();
boolean nameMatches = nm.contains("trapdoor") || nm.contains("manhole")
|| nm.contains("grate") || nm.contains("hatch");
if (!nameMatches) return false;
return Arrays.stream(comp.getActions()).filter(Objects::nonNull)
.anyMatch(a -> a.equalsIgnoreCase("Open"));
}, transport.getOrigin(), 10).stream()
.sorted(Comparator.comparingInt(o -> o.getWorldLocation().distanceTo(transport.getOrigin())))
.collect(Collectors.toList());
}

TileObject object = objects.stream().findFirst().orElse(null);
if (object instanceof GroundObject) {
object = objects.stream()
Expand Down Expand Up @@ -2150,7 +2259,10 @@ private static boolean handleTransports(List<WorldPoint> path, int indexOfStartP
}
}

handleObject(transport, object);
prepareTransportObjectForInteraction(object);
if (!handleObject(transport, object)) {
return false;
}
Comment on lines +2262 to +2265
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

The new transport “retry” path never retries.

At Line 2326 the code logs retrying, but handleObject() returns false immediately, and Line 2263 then exits handleTransports() on the first miss. That means no second attempt and no fallback candidate scan for a required stair/ladder/trapdoor interaction.

Possible fix
-            if (!started) {
-                log.debug("[Walker] {} transport click on {} produced no movement/animation; retrying",
-                        transport.getAction(), tileObject.getId());
-                return false;
-            }
+            if (!started) {
+                log.debug("[Walker] {} transport click on {} produced no movement/animation; retrying",
+                        transport.getAction(), tileObject.getId());
+                Rs2GameObject.interact(tileObject, transport.getAction());
+                started = sleepUntil(() -> Rs2Player.getWorldLocation().getPlane() != z
+                        || Rs2Player.isMoving()
+                        || Rs2Player.isAnimating(), 1800);
+                if (!started) {
+                    return false;
+                }
+            }

Also applies to: 2325-2328

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/walker/Rs2Walker.java`
around lines 2262 - 2265, The current logic in handleTransports exits
immediately when handleObject(transport, object) returns false, so the logged
"retrying" path never actually retries; change the control flow so that
transient/miss results from handleObject do not cause an immediate return from
handleTransports. Concretely, update handleObject (or its callers) to
distinguish fatal failures vs transient "retry" outcomes (e.g., boolean -> enum
or set a transport.retry flag) and in handleTransports, when a transient/retry
result is detected after prepareTransportObjectForInteraction(object), do not
return false—instead continue to the retry/fallback logic (allow the second
attempt or trigger candidate rescanning for stairs/ladder/trapdoor) so the retry
path logged around the transport handling is actually executed. Ensure the same
change is applied to the analogous branch around the code that logs "retrying"
(the 2325–2328 area) so both retry paths behave consistently.

sleepUntil(() -> !Rs2Player.isAnimating());
return sleepUntilTrue(() -> Rs2Player.getWorldLocation().distanceTo(transport.getDestination()) < OFFSET);
}
Expand All @@ -2174,9 +2286,19 @@ private static boolean handlePohTransport(Transport transport) {
return ((PohTransport)transport).execute();
}

private static void handleObject(Transport transport, TileObject tileObject) {
private static void prepareTransportObjectForInteraction(TileObject tileObject) {
if (tileObject == null || tileObject.getLocalLocation() == null) {
return;
}
if (!Rs2Camera.isTileOnScreen(tileObject)) {
Rs2Camera.turnTo(tileObject);
sleepUntil(() -> Rs2Camera.isTileOnScreen(tileObject), 1200);
}
}

private static boolean handleObject(Transport transport, TileObject tileObject) {
Rs2GameObject.interact(tileObject, transport.getAction());
if (handleObjectExceptions(transport, tileObject)) return;
if (handleObjectExceptions(transport, tileObject)) return true;
if (transport.getDestination().getPlane() == Rs2Player.getWorldLocation().getPlane()) {
if (transport.getType() == TransportType.AGILITY_SHORTCUT) {
Rs2Player.waitForAnimation();
Expand All @@ -2194,10 +2316,23 @@ private static void handleObject(Transport transport, TileObject tileObject) {
Rs2Player.waitForWalking();
Rs2Dialogue.clickOption("Yes please"); //shillo village cart
}
return true;
} else {
int z = Rs2Player.getWorldLocation().getPlane();
sleepUntil(() -> Rs2Player.getWorldLocation().getPlane() != z);
sleep((int) Rs2Random.gaussRand(1000.0, 300.0));
boolean started = sleepUntil(() -> Rs2Player.getWorldLocation().getPlane() != z
|| Rs2Player.isMoving()
|| Rs2Player.isAnimating(), 1800);
if (!started) {
log.debug("[Walker] {} transport click on {} produced no movement/animation; retrying",
transport.getAction(), tileObject.getId());
return false;
}
boolean planeChanged = Rs2Player.getWorldLocation().getPlane() != z
|| sleepUntil(() -> Rs2Player.getWorldLocation().getPlane() != z, 5000);
if (planeChanged) {
sleep((int) Rs2Random.gaussRand(300.0, 120.0));
}
return planeChanged;
}
}

Expand Down Expand Up @@ -2608,7 +2743,7 @@ public static boolean isNearPath() {
final WorldPoint loc = Rs2Player.getWorldLocation();
if (loc == null) return true;

if (config.recalculateDistance() < 0 || lastPosition.equals(lastPosition = loc)) {
if (config.recalculateDistance() < 0) {
return true;
}

Expand Down Expand Up @@ -4463,4 +4598,3 @@ public static boolean closeWorldMap() {
return sleepUntil(() -> !Rs2Widget.isWidgetVisible(InterfaceID.Worldmap.CLOSE), 3000);
}
}

Loading
Loading