Skip to content
Open
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

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ public List<Node> getNeighbors(Node node, VisitedTiles visited, PathfinderConfig
int moaAddedHere = 0;
int moaVisited = 0;
int moaIgnored = 0;
List<Integer> moaCosts = null;

// Transports are pre-filtered by PathfinderConfig.refreshTransports
// Thus any transports in the list are guaranteed to be valid per the user's settings
Expand All @@ -177,19 +178,24 @@ public List<Node> getNeighbors(Node node, VisitedTiles visited, PathfinderConfig
if (isMoa) moaIgnored++;
continue;
}
neighbors.add(new TransportNode(transport.getDestination(), node, config.getDistanceBeforeUsingTeleport() + transport.getDuration()));
if (isMoa) moaAddedHere++;
int cost = config.getDistanceBeforeUsingTeleport() + transport.getDuration();
neighbors.add(new TransportNode(transport.getDestination(), node, cost));
if (isMoa) {
moaAddedHere++;
if (moaCosts == null) moaCosts = new ArrayList<>();
moaCosts.add(cost);
}
} else {
neighbors.add(new TransportNode(transport.getDestination(), node, transport.getDuration()));
}
//END microbot variables
}

if (moaSeenHere > 0) {
log.debug("[MoA] getNeighbors @ ({},{},{}): seen={} added={} visited={} ignored={} (distanceBeforeUsingTeleport={}, cost={})",
log.debug("[MoA] getNeighbors @ ({},{},{}): seen={} added={} visited={} ignored={} (distanceBeforeUsingTeleport={}, costs={})",
x, y, z, moaSeenHere, moaAddedHere, moaVisited, moaIgnored,
config.getDistanceBeforeUsingTeleport(),
config.getDistanceBeforeUsingTeleport() + 4);
moaCosts == null ? "[]" : moaCosts);
}

if (isBlocked(x, y, z)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,23 @@ private boolean useTransport(Transport transport) {
return false;
}

// Region-level lock: once handleSeasonalTransport sees a region render with
// <str>…</str> (locked), reject every destination in that region. Without this,
// the pathfinder keeps picking a different Asgarnia/Desert/etc. destination on
// each re-path — walker fails, blacklists one, re-path picks the next, infinite
// "running around" loop. Display info format: "Map of Alacrity: <Region> - <Name>".
if (traceMoa && !Rs2Walker.lockedMoaRegions.isEmpty()) {
String disp = transport.getDisplayInfo();
int colon = disp.indexOf(':');
int dash = colon >= 0 ? disp.indexOf(" - ", colon) : -1;
if (colon >= 0 && dash > colon) {
String region = disp.substring(colon + 1, dash).trim().toLowerCase();
if (Rs2Walker.lockedMoaRegions.contains(region)) {
return false;
}
}
}

// Check if the feature flag is disabled
if (!isFeatureEnabled(transport)) {
log.debug("Transport Type {} is disabled by feature flag", transport.getType());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@

public class TransportNode extends Node implements Comparable<TransportNode> {
public TransportNode(WorldPoint point, Node previous, int travelTime) {
super(point, previous, cost(previous, travelTime));
}

private static int cost(Node previous, int travelTime) {
return (previous != null ? previous.cost : 0) + travelTime;
// Use Node(int, Node, int cost) which assigns cost directly. The WorldPoint
// Node constructor re-adds previous.cost via its cost(previous, wait) method,
// which caused (a) double-counting when we passed prev.cost + travelTime as
// wait and (b) integer overflow for plane-crossing transports with travelTime=0
// because its distance fallback returns Integer.MAX_VALUE across planes.
super(net.runelite.client.plugins.microbot.shortestpath.WorldPointUtil.packWorldPoint(point),
previous,
(previous != null ? previous.cost : 0) + travelTime);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2709,7 +2709,7 @@ public static int getDistanceBetween(WorldPoint startpoint, WorldPoint endpoint)

private static boolean handleSeasonalTransport(Transport transport) {
String displayInfo = transport.getDisplayInfo();
log.info("[MoA] entry: displayInfo='{}'", displayInfo);
log.debug("[MoA] entry: displayInfo='{}'", displayInfo);
if (displayInfo == null) return false;

if (!displayInfo.toLowerCase().contains("map of alacrity")) {
Expand Down Expand Up @@ -2808,11 +2808,17 @@ private static boolean handleSeasonalTransport(Transport transport) {
}, 3000);

if (destMatch == null) {
log.warn("[MoA] destination '{}' never appeared after clicking region '{}' — name mismatch or locked; blacklisting",
// Don't blacklist here: a missing destination widget is ambiguous. Combat,
// lag, or the widget being closed by another handler can all manifest as
// "never appeared". Blacklisting on ambiguity permanently poisons legitimate
// destinations mid-session (e.g. player gets attacked during teleport, widget
// closes, we'd blacklist Nemus forever). Just return false and let the
// pathfinder/walker retry. Positive-evidence blacklisting (<str> markup on
// region or destination) below still applies.
log.warn("[MoA] destination '{}' never appeared after clicking region '{}' — retrying later",
shortName, region);
Widget root = Rs2Widget.getWidget(MAP_OF_ALACRITY_WIDGET_GROUP, MAP_OF_ALACRITY_LIST_CHILD);
if (root != null) dumpMapOfAlacrityWidget(root);
blacklistedMoaDestinations.add(packedDest);
return false;
}

Expand All @@ -2826,7 +2832,7 @@ private static boolean handleSeasonalTransport(Transport transport) {

// Select via the row's in-game hotkey (1-9 then A-Z). Keybinds work even when the row
// is scrolled off-screen, which clickWidget cannot handle.
log.info("[MoA] selecting destination '{}' (text='{}')", shortName, destText);
log.debug("[MoA] selecting destination '{}' (text='{}')", shortName, destText);
Character hotkey = extractMoaHotkey(destText);
if (hotkey == null) {
Widget destRoot = Rs2Widget.getWidget(MAP_OF_ALACRITY_WIDGET_GROUP, MAP_OF_ALACRITY_LIST_CHILD);
Expand Down Expand Up @@ -2858,30 +2864,27 @@ private static Widget findMoaWidget(Widget root, String shortName) {
if (normalised.isEmpty()) return null;
String[] tokens = normalised.split(" ");
return Microbot.getClientThread().runOnClientThreadOptional(() -> {
for (Widget w : collectMoaChildren(root)) {
String hay = normaliseMoaText(w.getText());
if (hay.isEmpty()) continue;
boolean all = true;
for (String t : tokens) {
if (t.isEmpty()) continue;
if (!hay.contains(t)) { all = false; break; }
Widget[][] groups = { root.getDynamicChildren(), root.getNestedChildren(), root.getStaticChildren() };
for (Widget[] g : groups) {
if (g == null) continue;
for (Widget w : g) {
if (w == null) continue;
String hay = normaliseMoaText(w.getText());
if (hay.isEmpty()) continue;
// Token-set membership avoids substring false positives (e.g. "log" matching "logstrum").
java.util.Set<String> haySet = new java.util.HashSet<>(java.util.Arrays.asList(hay.split(" ")));
boolean all = true;
for (String t : tokens) {
if (t.isEmpty()) continue;
if (!haySet.contains(t)) { all = false; break; }
}
if (all) return w;
}
if (all) return w;
}
return null;
}).orElse(null);
}

private static java.util.List<Widget> collectMoaChildren(Widget root) {
java.util.List<Widget> out = new java.util.ArrayList<>();
Widget[][] groups = { root.getDynamicChildren(), root.getNestedChildren(), root.getStaticChildren() };
for (Widget[] g : groups) {
if (g == null) continue;
for (Widget w : g) if (w != null) out.add(w);
}
return out;
}

private static String normaliseMoaText(String s) {
if (s == null) return "";
s = MOA_MARKUP_PATTERN.matcher(s).replaceAll(" ");
Expand All @@ -2906,12 +2909,17 @@ private static Character computeMoaHotkeyByIndex(Widget root, Widget destMatch)
if (root == null) return null;
return Microbot.getClientThread().runOnClientThreadOptional(() -> {
int idx = 0;
for (Widget sibling : collectMoaChildren(root)) {
String t = sibling.getText();
if (t == null || t.isEmpty()) continue;
if (t.contains(MOA_LOCKED_MARKUP)) continue;
if (sibling == destMatch) return indexToHotkey(idx);
idx++;
Widget[][] groups = { root.getDynamicChildren(), root.getNestedChildren(), root.getStaticChildren() };
for (Widget[] g : groups) {
if (g == null) continue;
for (Widget sibling : g) {
if (sibling == null) continue;
String t = sibling.getText();
if (t == null || t.isEmpty()) continue;
if (t.contains(MOA_LOCKED_MARKUP)) continue;
if (sibling == destMatch) return indexToHotkey(idx);
idx++;
}
}
return null;
}).orElse(null);
Expand Down Expand Up @@ -2956,96 +2964,6 @@ private static void dumpMapOfAlacrityWidget(Widget listRoot) {
});
}

// TEMP: iterate every MoA seasonal transport, attempt it, log landing vs expected.
// Run from a dedicated worker thread (blocks). Requires Map of Alacrity in inventory;
// locked regions/destinations are reported and skipped via the existing handler's guards.
public static void runMoaAudit() {
try {
while (!Microbot.isLoggedIn()) {
if (Thread.currentThread().isInterrupted()) return;
sleep(1000);
}
if (Rs2Inventory.get(MAP_OF_ALACRITY_ITEM_ID) == null) {
log.warn("[MoA-AUDIT] Map of Alacrity not in inventory — aborting");
return;
}

HashMap<WorldPoint, Set<Transport>> all = Transport.loadAllFromResources();
List<Transport> moa = new ArrayList<>();
for (Set<Transport> set : all.values()) {
for (Transport t : set) {
if (t.getType() == TransportType.SEASONAL_TRANSPORT
&& t.getDisplayInfo() != null
&& t.getDisplayInfo().toLowerCase().contains("map of alacrity")) {
moa.add(t);
}
}
}
moa.sort(Comparator.comparing(Transport::getDisplayInfo));
log.info("[MoA-AUDIT] {} MoA transports queued", moa.size());
blacklistedMoaDestinations.clear();
lockedMoaRegions.clear();

int landed = 0, skipped = 0;
for (int i = 0; i < moa.size(); i++) {
if (Thread.currentThread().isInterrupted()) break;
if (!Microbot.isLoggedIn()) { log.warn("[MoA-AUDIT] logged out — stopping"); break; }

Transport t = moa.get(i);
String disp = t.getDisplayInfo();
WorldPoint expected = t.getDestination();
WorldPoint before = Rs2Player.getWorldLocation();
if (before == null) { sleep(500); continue; }

log.info("[MoA-AUDIT] {}/{}: {} (expected {},{},{})",
i + 1, moa.size(), disp,
expected.getX(), expected.getY(), expected.getPlane());

if (!handleSeasonalTransport(t)) {
log.info("[MoA-AUDIT] handler returned false");
closeMoaWidgetIfOpen();
skipped++;
sleep(600);
continue;
}

boolean moved = sleepUntil(() -> {
WorldPoint now = Rs2Player.getWorldLocation();
return now != null && (now.distanceTo(before) > 5 || now.getPlane() != before.getPlane());
}, 8000);

if (!moved) {
log.info("[MoA-AUDIT] no teleport detected");
closeMoaWidgetIfOpen();
skipped++;
continue;
}

sleep(1500); // settle
WorldPoint after = Rs2Player.getWorldLocation();
int dist = after.getPlane() == expected.getPlane() ? after.distanceTo(expected) : -1;
String marker = dist == 0 ? "EXACT" : (dist > 0 && dist <= 2 ? "close" : (dist > 0 && dist <= 10 ? "NEAR" : "FAR"));
log.info("[MoA-AUDIT] LAND {} | actual={},{},{} expected={},{},{} dist={} | {}",
marker,
after.getX(), after.getY(), after.getPlane(),
expected.getX(), expected.getY(), expected.getPlane(),
dist, disp);
landed++;
sleep(1500);
}
log.info("[MoA-AUDIT] complete: landed={}/{} skipped={}", landed, moa.size(), skipped);
} catch (Exception e) {
log.error("[MoA-AUDIT] crashed", e);
}
}

private static void closeMoaWidgetIfOpen() {
if (Rs2Widget.isWidgetVisible(MAP_OF_ALACRITY_WIDGET_GROUP, MAP_OF_ALACRITY_LIST_CHILD)) {
Rs2Keyboard.keyPress(27); // ESC
sleep(400);
}
}

private static boolean handleSpiritTree(Transport transport) {
// Get Transport Information
String displayInfo = transport.getDisplayInfo();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -689,9 +689,6 @@ net.runelite.client.plugins.microbot.util.walker.Rs2MiniMap#getMinimapDrawWidget
net.runelite.client.plugins.microbot.util.walker.Rs2MiniMap#worldToMinimap(WorldPoint): Point -> net.runelite.api.Client#getTopLevelWorldView(): WorldView
net.runelite.client.plugins.microbot.util.walker.Rs2MiniMap#worldToMinimap(WorldPoint): Point -> net.runelite.api.coords.LocalPoint#fromWorld(WorldView, WorldPoint): LocalPoint
net.runelite.client.plugins.microbot.util.walker.Rs2Walker#closeWorldMap(): boolean -> net.runelite.api.widgets.Widget#getBounds(): Rectangle
net.runelite.client.plugins.microbot.util.walker.Rs2Walker#collectMoaChildren(Widget): List -> net.runelite.api.widgets.Widget#getDynamicChildren(): Widget[]
net.runelite.client.plugins.microbot.util.walker.Rs2Walker#collectMoaChildren(Widget): List -> net.runelite.api.widgets.Widget#getNestedChildren(): Widget[]
net.runelite.client.plugins.microbot.util.walker.Rs2Walker#collectMoaChildren(Widget): List -> net.runelite.api.widgets.Widget#getStaticChildren(): Widget[]
net.runelite.client.plugins.microbot.util.walker.Rs2Walker#distanceToRegion(int, int): int -> net.runelite.api.Client#getTopLevelWorldView(): WorldView
net.runelite.client.plugins.microbot.util.walker.Rs2Walker#distanceToRegion(int, int): int -> net.runelite.api.WorldView#getPlane(): int
net.runelite.client.plugins.microbot.util.walker.Rs2Walker#getPointWithWallDistance(WorldPoint): WorldPoint -> net.runelite.api.Client#getTopLevelWorldView(): WorldView
Expand Down Expand Up @@ -734,11 +731,11 @@ net.runelite.client.plugins.microbot.util.walker.Rs2Walker#handleTransports(List
net.runelite.client.plugins.microbot.util.walker.Rs2Walker#handleTransports(List, int): boolean -> net.runelite.api.WorldView#getScene(): Scene
net.runelite.client.plugins.microbot.util.walker.Rs2Walker#isCloseToRegion(int, int, int): boolean -> net.runelite.api.Client#getTopLevelWorldView(): WorldView
net.runelite.client.plugins.microbot.util.walker.Rs2Walker#isCloseToRegion(int, int, int): boolean -> net.runelite.api.WorldView#getPlane(): int
net.runelite.client.plugins.microbot.util.walker.Rs2Walker#lambda$handleCharterShip$115(Widget): boolean -> net.runelite.api.widgets.Widget#getActions(): String[]
net.runelite.client.plugins.microbot.util.walker.Rs2Walker#lambda$handleCharterShip$114(Widget): boolean -> net.runelite.api.widgets.Widget#getActions(): String[]
net.runelite.client.plugins.microbot.util.walker.Rs2Walker#lambda$handleDoors$14(WorldPoint, GameObject): boolean -> net.runelite.api.GameObject#getWorldLocation(): WorldPoint
net.runelite.client.plugins.microbot.util.walker.Rs2Walker#lambda$handleFairyRing$121(Transport, TileObject): boolean -> net.runelite.api.TileObject#getWorldLocation(): WorldPoint
net.runelite.client.plugins.microbot.util.walker.Rs2Walker#lambda$handleMinigameTeleport$94(Widget, Object[]): boolean -> net.runelite.api.widgets.Widget#getOnOpListener(): Object[]
net.runelite.client.plugins.microbot.util.walker.Rs2Walker#lambda$handleMinigameTeleport$96(String): boolean -> net.runelite.api.widgets.Widget#getText(): String
net.runelite.client.plugins.microbot.util.walker.Rs2Walker#lambda$handleFairyRing$120(Transport, TileObject): boolean -> net.runelite.api.TileObject#getWorldLocation(): WorldPoint
net.runelite.client.plugins.microbot.util.walker.Rs2Walker#lambda$handleMinigameTeleport$93(Widget, Object[]): boolean -> net.runelite.api.widgets.Widget#getOnOpListener(): Object[]
net.runelite.client.plugins.microbot.util.walker.Rs2Walker#lambda$handleMinigameTeleport$95(String): boolean -> net.runelite.api.widgets.Widget#getText(): String
net.runelite.client.plugins.microbot.util.walker.Rs2Walker#lambda$handleObjectExceptions$66(int, TileObject): boolean -> net.runelite.api.TileObject#getId(): int
net.runelite.client.plugins.microbot.util.walker.Rs2Walker#lambda$handleObjectExceptions$72(WorldPoint, TileObject): boolean -> net.runelite.api.TileObject#getId(): int
net.runelite.client.plugins.microbot.util.walker.Rs2Walker#lambda$handleObjectExceptions$72(WorldPoint, TileObject): boolean -> net.runelite.api.TileObject#getWorldLocation(): WorldPoint
Expand Down