diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/moaaudit/MoaAuditPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/moaaudit/MoaAuditPlugin.java new file mode 100644 index 0000000000..03345e71e8 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/moaaudit/MoaAuditPlugin.java @@ -0,0 +1,32 @@ +package net.runelite.client.plugins.microbot.moaaudit; + +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.microbot.util.walker.Rs2Walker; + +// TEMP debug plugin: on enable, iterates every Map of Alacrity seasonal transport, +// attempts to teleport, and logs actual landing vs expected coord for each. Used to +// catch bad destination coords in seasonal_transports.tsv. Delete when done. +@PluginDescriptor( + name = PluginDescriptor.Default + "MoA Audit", + description = "[TEMP] Record Map of Alacrity teleport landing tiles", + tags = {"temp", "debug", "league", "microbot"}, + enabledByDefault = false +) +@Slf4j +public class MoaAuditPlugin extends Plugin { + private Thread worker; + + @Override + protected void startUp() { + worker = new Thread(Rs2Walker::runMoaAudit, "moa-audit"); + worker.setDaemon(true); + worker.start(); + } + + @Override + protected void shutDown() { + if (worker != null) worker.interrupt(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/ShortestPathConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/ShortestPathConfig.java index 1422b51ff4..05bea3fb3f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/ShortestPathConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/ShortestPathConfig.java @@ -376,7 +376,7 @@ default boolean useMagicMushtrees() { @ConfigItem( keyName = "useSeasonalTransports", name = "Use Seasonal Transports", - description = "Whether to include seasonal transports (e.g. clue compass) in the path.", + description = "Whether to include seasonal League transports (e.g. Map of Alacrity) in the path. League worlds only.", position = 19, section = sectionSettings ) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/TransportType.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/TransportType.java index 49ef959872..60b1cac35e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/TransportType.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/TransportType.java @@ -40,6 +40,7 @@ public static boolean isTeleport(TransportType transportType) { case TELEPORTATION_ITEM: case TELEPORTATION_MINIGAME: case TELEPORTATION_SPELL: + case SEASONAL_TRANSPORT: return true; default: return false; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/CollisionMap.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/CollisionMap.java index 87c32133c4..af2da68622 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/CollisionMap.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/CollisionMap.java @@ -153,21 +153,45 @@ public List getNeighbors(Node node, VisitedTiles visited, PathfinderConfig Set transports = config.getTransportsPacked().getOrDefault(node.packedPosition, Collections.emptySet()); + int moaSeenHere = 0; + int moaAddedHere = 0; + int moaVisited = 0; + int moaIgnored = 0; + // Transports are pre-filtered by PathfinderConfig.refreshTransports // Thus any transports in the list are guaranteed to be valid per the user's settings for (Transport transport : transports) { + boolean isMoa = transport.getType() == TransportType.SEASONAL_TRANSPORT + && transport.getDisplayInfo() != null + && transport.getDisplayInfo().toLowerCase().contains("map of alacrity"); + if (isMoa) moaSeenHere++; + //START microbot variables - if (visited.get(transport.getDestination())) continue; + if (visited.get(transport.getDestination())) { + if (isMoa) moaVisited++; + continue; + } if (TransportType.isTeleport(transport.getType())) { - if (config.isIgnoreTeleportAndItems()) continue; + if (config.isIgnoreTeleportAndItems()) { + if (isMoa) moaIgnored++; + continue; + } neighbors.add(new TransportNode(transport.getDestination(), node, config.getDistanceBeforeUsingTeleport() + transport.getDuration())); + if (isMoa) moaAddedHere++; } 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={})", + x, y, z, moaSeenHere, moaAddedHere, moaVisited, moaIgnored, + config.getDistanceBeforeUsingTeleport(), + config.getDistanceBeforeUsingTeleport() + 4); + } + if (isBlocked(x, y, z)) { boolean westBlocked = isBlocked(x - 1, y, z); boolean eastBlocked = isBlocked(x + 1, y, z); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/PathfinderConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/PathfinderConfig.java index e526e555a4..60ec296983 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/PathfinderConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/shortestpath/pathfinder/PathfinderConfig.java @@ -332,6 +332,9 @@ private void refreshTransports(WorldPoint target) { long useTransportTimeNanos = 0; Map typeStats = new java.util.EnumMap<>(TransportType.class); + int moaSeen = 0; + int moaKept = 0; + for (Map.Entry> entry : mergedList.entrySet()) { WorldPoint point = entry.getKey(); Set usableTransports = new HashSet<>(entry.getValue().size()); @@ -339,6 +342,11 @@ private void refreshTransports(WorldPoint target) { totalTransports++; updateActionBasedOnQuestState(transport); + boolean isMoa = transport.getType() == TransportType.SEASONAL_TRANSPORT + && transport.getDisplayInfo() != null + && transport.getDisplayInfo().toLowerCase().contains("map of alacrity"); + if (isMoa) moaSeen++; + long t0 = System.nanoTime(); boolean usable = useTransport(transport); long elapsed = System.nanoTime() - t0; @@ -357,6 +365,7 @@ private void refreshTransports(WorldPoint target) { } else { usableTransports.add(transport); } + if (isMoa) moaKept++; } if (point != null && !usableTransports.isEmpty()) { @@ -385,6 +394,10 @@ private void refreshTransports(WorldPoint target) { .limit(5) .forEach(e -> log.info("[refreshTransports] {} : count={}, usable={}, time={}ms", e.getKey(), e.getValue()[0], e.getValue()[1], e.getValue()[2] / 1000)); + + log.debug("[MoA] refreshTransports: seen={} kept={} (useSeasonalTransports={}, VarbitID.LEAGUE_TYPE={})", + moaSeen, moaKept, useSeasonalTransports, + Microbot.getVarbitValue(10032)); } @@ -553,9 +566,22 @@ private boolean varplayerChecks(Transport transport) { } private boolean useTransport(Transport transport) { + boolean traceMoa = transport.getType() == TransportType.SEASONAL_TRANSPORT + && transport.getDisplayInfo() != null + && transport.getDisplayInfo().toLowerCase().contains("map of alacrity"); + + // Session blacklist: once an MoA destination fails at runtime (locked region or + // unrecognised name), don't let the pathfinder keep routing through it. + if (traceMoa && Rs2Walker.blacklistedMoaDestinations.contains( + WorldPointUtil.packWorldPoint(transport.getDestination()))) { + return false; + } + // Check if the feature flag is disabled if (!isFeatureEnabled(transport)) { log.debug("Transport Type {} is disabled by feature flag", transport.getType()); + if (traceMoa) log.debug("[MoA] rejected '{}' — feature flag disabled (useSeasonalTransports={})", + transport.getDisplayInfo(), useSeasonalTransports); return false; } // If the transport requires you to be in a members world (used for more granular member requirements) @@ -581,6 +607,8 @@ private boolean useTransport(Transport transport) { // If the transport has varbit requirements & the varbits do not match if (!varbitChecks(transport)) { log.debug("Transport ( O: {} D: {} ) requires varbits {}", transport.getOrigin(), transport.getDestination(), transport.getVarbits()); + if (traceMoa) log.debug("[MoA] rejected '{}' — varbit check failed (varbits={}, LEAGUE_TYPE={})", + transport.getDisplayInfo(), transport.getVarbits(), Microbot.getVarbitValue(10032)); return false; } @@ -637,6 +665,8 @@ private boolean useTransport(Transport transport) { boolean hasRequiredItems = hasRequiredItems(transport); if (!hasRequiredItems) { log.debug("Transport ( O: {} D: {} ) requires items {}", transport.getOrigin(), transport.getDestination(), transport.getItemIdRequirements().stream().flatMap(Set::stream).collect(Collectors.toSet())); + if (traceMoa) log.debug("[MoA] rejected '{}' — missing required items {}", + transport.getDisplayInfo(), transport.getItemIdRequirements()); } return hasRequiredItems; } 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 0ab83f107f..11bc2231fa 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 @@ -58,6 +58,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReentrantLock; @@ -1949,6 +1951,14 @@ private static boolean handleTransports(List path, int indexOfStartP } } + if (transport.getType() == TransportType.SEASONAL_TRANSPORT) { + if (handleSeasonalTransport(transport)) { + sleepUntil(() -> !Rs2Player.isAnimating()); + sleepUntilTrue(() -> Rs2Player.getWorldLocation().distanceTo(transport.getDestination()) < OFFSET); + break; + } + } + if (transport.getObjectId() <= 0) break; final int transportObjectId = transport.getObjectId(); @@ -2558,6 +2568,367 @@ public static int getDistanceBetween(WorldPoint startpoint, WorldPoint endpoint) return pathfinder.getPath().size(); } + // Map of Alacrity (League 6 / Demonic Pacts tier 3 relic — teleports to agility shortcuts). + // Item not in ItemID enum yet; widget group 187 is a two-step picker: + // Step 1: click a region (LJ_LAYER1 children 0-9). Locked regions are visible but not + // clickable. After clicking, the same widget repopulates with destinations. + // Step 2: click the destination in the same LJ_LAYER1. + private static final int MAP_OF_ALACRITY_ITEM_ID = 33233; + private static final int MAP_OF_ALACRITY_WIDGET_GROUP = 187; + private static final int MAP_OF_ALACRITY_LIST_CHILD = 3; + // Strikethrough markup the client wraps around locked (unselectable) menu rows. + private static final String MOA_LOCKED_MARKUP = ""; + + // Session blacklist of MoA destinations whose region or row is locked for this player, + // or whose display info doesn't resolve to any widget child. Prevents the pathfinder from + // re-picking the same doomed edge every tick. + public static final java.util.Set blacklistedMoaDestinations = + java.util.concurrent.ConcurrentHashMap.newKeySet(); + + // Session cache of MoA regions detected as locked. Short-circuits every destination in + // that region without re-opening the widget each attempt. Key is lowercased region name. + public static final java.util.Set lockedMoaRegions = + java.util.concurrent.ConcurrentHashMap.newKeySet(); + + private static boolean handleSeasonalTransport(Transport transport) { + String displayInfo = transport.getDisplayInfo(); + log.info("[MoA] entry: displayInfo='{}'", displayInfo); + if (displayInfo == null) return false; + + if (!displayInfo.toLowerCase().contains("map of alacrity")) { + log.debug("[MoA] not Map of Alacrity, skipping"); + return false; + } + + int packedDest = WorldPointUtil.packWorldPoint(transport.getDestination()); + if (blacklistedMoaDestinations.contains(packedDest)) { + log.debug("[MoA] destination {} previously blacklisted this session — skipping", + transport.getDestination()); + return false; + } + + Rs2ItemModel relic = Rs2Inventory.get(MAP_OF_ALACRITY_ITEM_ID); + if (relic == null) { + log.debug("[MoA] item {} not in inventory — abort", MAP_OF_ALACRITY_ITEM_ID); + return false; + } + + // Display info format: "Map of Alacrity: - " + String rest = displayInfo.contains(":") ? displayInfo.split(":", 2)[1].trim() : displayInfo.trim(); + int dashIdx = rest.indexOf(" - "); + if (dashIdx < 0) { + log.warn("[MoA] cannot split region/shortcut from '{}'", rest); + return false; + } + String region = rest.substring(0, dashIdx).trim(); + String shortName = rest.substring(dashIdx + 3).trim(); + log.debug("[MoA] region='{}' shortName='{}'", region, shortName); + + if (lockedMoaRegions.contains(region.toLowerCase())) { + log.debug("[MoA] region '{}' already known-locked — skipping '{}'", region, shortName); + blacklistedMoaDestinations.add(packedDest); + return false; + } + + String action = relic.getAction("Read"); + if (action == null) action = relic.getActionFromList(Arrays.asList("Read", "Open", "Teleport", "Invoke")); + if (action == null) { + log.warn("[MoA] no usable action; available={}", Arrays.toString(relic.getInventoryActions())); + return false; + } + if (!Rs2Inventory.interact(relic, action)) { + log.warn("[MoA] Rs2Inventory.interact returned false for action '{}'", action); + return false; + } + + // Step 1: wait for the region picker to render, then click the matching region. + if (!sleepUntil(() -> Rs2Widget.isWidgetVisible(MAP_OF_ALACRITY_WIDGET_GROUP, MAP_OF_ALACRITY_LIST_CHILD), 3000)) { + log.warn("[MoA] region widget {}.{} did not open", MAP_OF_ALACRITY_WIDGET_GROUP, MAP_OF_ALACRITY_LIST_CHILD); + return false; + } + + Widget regionRoot = Rs2Widget.getWidget(MAP_OF_ALACRITY_WIDGET_GROUP, MAP_OF_ALACRITY_LIST_CHILD); + if (regionRoot == null) { + log.warn("[MoA] region widget lookup returned null"); + return false; + } + dumpMapOfAlacrityWidget(regionRoot); + + Widget regionMatch = findMoaWidget(regionRoot, region); + if (regionMatch == null) { + log.warn("[MoA] region '{}' not found in picker — check dump", region); + return false; + } + // Locked regions render with ... strikethrough markup. Don't waste a press. + String regionText = Microbot.getClientThread().runOnClientThreadOptional(regionMatch::getText).orElse(""); + if (regionText != null && regionText.contains(MOA_LOCKED_MARKUP)) { + log.warn("[MoA] region '{}' is locked (text='{}') — caching + blacklisting destination {}", + region, regionText, transport.getDestination()); + lockedMoaRegions.add(region.toLowerCase()); + blacklistedMoaDestinations.add(packedDest); + return false; + } + log.debug("[MoA] selecting region '{}'", region); + Character regionHotkey = extractMoaHotkey(regionText); + if (regionHotkey == null) regionHotkey = computeMoaHotkeyByIndex(regionRoot, regionMatch); + if (regionHotkey != null) { + Rs2Keyboard.keyPress(regionHotkey); + } else { + log.warn("[MoA] no hotkey resolved for region '{}' — falling back to clickWidget", region); + if (!Rs2Widget.clickWidget(regionMatch)) { + log.warn("[MoA] region click returned false"); + return false; + } + } + + // Step 2: wait for the destination to appear in the (same) widget. If the region was + // locked or otherwise non-clickable, this poll will time out with shortName never + // showing, and we return false. + Widget destMatch = sleepUntilNotNull(() -> { + Widget root = Rs2Widget.getWidget(MAP_OF_ALACRITY_WIDGET_GROUP, MAP_OF_ALACRITY_LIST_CHILD); + if (root == null) return null; + return findMoaWidget(root, shortName); + }, 3000); + + if (destMatch == null) { + log.warn("[MoA] destination '{}' never appeared after clicking region '{}' — name mismatch or locked; blacklisting", + 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; + } + + // Individual destinations can also be locked inside an unlocked region. + String destText = Microbot.getClientThread().runOnClientThreadOptional(destMatch::getText).orElse(""); + if (destText != null && destText.contains(MOA_LOCKED_MARKUP)) { + log.warn("[MoA] destination '{}' is locked (text='{}') — blacklisting", shortName, destText); + blacklistedMoaDestinations.add(packedDest); + return false; + } + + // 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); + Character hotkey = extractMoaHotkey(destText); + if (hotkey == null) { + Widget destRoot = Rs2Widget.getWidget(MAP_OF_ALACRITY_WIDGET_GROUP, MAP_OF_ALACRITY_LIST_CHILD); + hotkey = computeMoaHotkeyByIndex(destRoot, destMatch); + } + if (hotkey != null) { + Rs2Keyboard.keyPress(hotkey); + log.debug("[MoA] pressed hotkey '{}' for '{}'", hotkey, shortName); + return true; + } + + log.warn("[MoA] no hotkey resolved for '{}' — falling back to clickWidget", shortName); + return Rs2Widget.clickWidget(destMatch); + } + + // Matches the OSRS menu-row hotkey prefix, e.g. "[1] ..." or "1: ..." or "A. ...". + private static final Pattern MOA_HOTKEY_PATTERN = + Pattern.compile("^\\s*(?:\\[([0-9A-Za-z])\\]|([0-9A-Za-z])\\s*[:.])"); + private static final Pattern MOA_MARKUP_PATTERN = Pattern.compile("<[^>]+>"); + private static final Pattern MOA_PUNCT_PATTERN = Pattern.compile("[^a-zA-Z0-9 ]"); + private static final Pattern MOA_WHITESPACE_PATTERN = Pattern.compile("\\s+"); + + // Token-contains match tolerant of punctuation, / markup, and case. Used for + // both the region picker and the destination picker. Fixes the colon mismatch between TSV + // short names (e.g. "Chaos Temple Stepping Stone") and in-game labels ("Chaos Temple: + // Stepping Stone") without per-row data curation. + private static Widget findMoaWidget(Widget root, String shortName) { + String normalised = normaliseMoaText(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; } + } + if (all) return w; + } + return null; + }).orElse(null); + } + + private static java.util.List collectMoaChildren(Widget root) { + java.util.List 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(" "); + s = MOA_PUNCT_PATTERN.matcher(s).replaceAll(" "); + return MOA_WHITESPACE_PATTERN.matcher(s.toLowerCase()).replaceAll(" ").trim(); + } + + private static Character extractMoaHotkey(String rawText) { + if (rawText == null) return null; + String stripped = rawText.replaceAll("<[^>]+>", "").trim(); + Matcher m = MOA_HOTKEY_PATTERN.matcher(stripped); + if (!m.find()) return null; + String g = m.group(1) != null ? m.group(1) : m.group(2); + if (g == null || g.isEmpty()) return null; + char c = g.charAt(0); + return Character.isLetter(c) ? Character.toUpperCase(c) : c; + } + + // Fallback when the row text has no bracketed/colon-prefixed key we can parse. + // OSRS numbers unlocked rows 1-9 then A-Z; locked () rows are skipped. + 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++; + } + return null; + }).orElse(null); + } + + private static Character indexToHotkey(int i) { + if (i < 9) return (char) ('1' + i); + int letter = i - 9; + if (letter >= 26) return null; + return (char) ('A' + letter); + } + + // Verbose one-shot dump of MoA destination widget children to the log. Helps us figure out + // the real in-game label format on the first invocation; can be trimmed once execution is + // known to work end-to-end. Widget accessors must run on the client thread. + private static void dumpMapOfAlacrityWidget(Widget listRoot) { + Microbot.getClientThread().runOnClientThreadOptional(() -> { + try { + Widget[] dyn = listRoot.getDynamicChildren(); + Widget[] stc = listRoot.getStaticChildren(); + Widget[] nst = listRoot.getNestedChildren(); + log.debug("[MoA] widget dump: listRoot id={} text='{}' name='{}' dyn={} static={} nested={}", + listRoot.getId(), + listRoot.getText(), + listRoot.getName(), + dyn == null ? "null" : dyn.length, + stc == null ? "null" : stc.length, + nst == null ? "null" : nst.length); + Widget[] toDump = dyn != null ? dyn : (stc != null ? stc : nst); + if (toDump == null) return true; + for (int i = 0; i < toDump.length; i++) { + Widget c = toDump[i]; + if (c == null) continue; + log.debug("[MoA] child[{}] id={} hidden={} text='{}' name='{}' actions={}", + i, c.getId(), c.isHidden(), c.getText(), c.getName(), + Arrays.toString(c.getActions())); + } + } catch (Exception e) { + log.warn("[MoA] widget dump threw", e); + } + return true; + }); + } + + // 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> all = Transport.loadAllFromResources(); + List moa = new ArrayList<>(); + for (Set 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(); diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/agility_shortcuts.tsv b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/agility_shortcuts.tsv index 022671f672..023b0053d0 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/agility_shortcuts.tsv +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/agility_shortcuts.tsv @@ -274,8 +274,8 @@ 1369 3294 0 1369 3296 0 Climb-over;Broken wall;56996 24 Agility 3 1387 3303 0 1387 3301 0 Climb-over;Broken wall;56996 24 Agility 3 1387 3301 0 1387 3303 0 Climb-over;Broken wall;56996 24 Agility 3 -1389 3309 0 1391 3309 0 Climb-over;Broken wall;56996 24 Agility 3 -1391 3309 0 1389 3309 0 Climb-over;Broken wall;56996 24 Agility 3 +1389 3309 0 1391 3309 0 Climb-over;Broken wall;56997 24 Agility 3 +1391 3309 0 1389 3309 0 Climb-over;Broken wall;56997 24 Agility 3 1393 3309 0 1399 3309 0 Cross;Stepping stone;56988 36 Agility 3 1399 3309 0 1393 3309 0 Cross;Stepping stone;56988 36 Agility 3 1391 3323 0 1396 3323 0 Climb;Rocks;56994 41 Agility 3 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/seasonal_transports.tsv b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/seasonal_transports.tsv index 221ba2a2a9..7549e9e234 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/seasonal_transports.tsv +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/seasonal_transports.tsv @@ -1,49 +1,172 @@ -# Destination menuOption menuTarget objectID Skills Items Quests Duration Display info Consumable Wilderness level Varbits Varplayers -# Clue compass -1641 3809 0 30363=1 4 Clue compass: 4. Arceuus Library F 20 10032>0 -3110 3420 0 30363=1 4 Clue compass: B. Barbarian Village F 20 10032>0 -1663 10045 0 30363=1 4 Clue compass: H. Catacombs of Kourend F 20 10032>0 -3375 3419 0 30363=1 4 Clue compass: Q. Digsite F 20 10032>0 -3355 3348 0 30363=1 4 Clue compass: R: Digsite Exam centre F 20 10032>0 -3089 3333 0 30363=1 4 Clue compass: S: Draynor Manor F 20 10032>0 -3109 3288 0 30363=1 4 Clue compass: T: Draynor Village (Crossroads) F 20 10032>0 -3129 3250 0 30363=1 4 Clue compass: U: Draynor Village (Jail) F 20 10032>0 -3082 3252 0 30363=1 4 Clue compass: V: Draynor Village (Market) F 20 10032>0 -3096 3252 0 30363=1 4 Clue compass: W: Draynor Village (Miss Schism) F 20 10032>0 -3079 3504 0 30363=1 4 Clue compass: X: Edgeville F 20 10032>0 -3056 3484 0 30363=1 4 Clue compass: Y: Edgeville Monestary F 20 10032>0 -3024 4518 0 30363=1 4 Clue compass: Enchanted Valley F 20 10032>0 -2942 3338 0 30363=1 4 Clue compass: Falador (Gem shop) F 20 10032>0 -3045 3371 0 30363=1 4 Clue compass: Falador (Party room) F 20 10032>0 -2957 3502 0 30363=1 4 Clue compass: Goblin Village F 20 10032>0 -1712 3469 0 30363=1 4 Clue compass: Hosidius (Charcoal Burners) F 20 10032>0 -1645 3632 0 30363=1 4 Clue compass: Hosidius Mess F 20 10032>0 -3372 3499 0 30363=1 4 Clue compass: Limestone Mine F 20 10032>0 -3299 3490 0 30363=1 4 Clue compass: Lumber Yard Sawmill F 20 10032>0 -3234 3201 0 30363=1 4 Clue compass: Lumbridge (Bob's axes) F 20 10032>0 -3167 9571 0 30363=1 4 Clue compass: Lumbridge (Swamp caves) F 20 10032>0 -3200 3169 0 30363=1 4 Clue compass: Lumbridge (Swamp) F 20 10032>0 -3161 3298 0 30363=1 4 Clue compass: Lumbridge (Windmill) F 20 10032>0 -1317 3839 0 30363=1 4 Clue compass: Mount Karuulm F 20 10032>0 -2990 3110 0 30363=1 4 Clue compass: Mudskipper point F 20 10032>0 -2911 3169 0 30363=1 4 Clue compass: Musa Point F 20 10032>0 -3049 3237 0 30363=1 4 Clue compass: Port Sarim F 20 10032>0 -2981 3276 0 30363=1 4 Clue compass: Rimmington (Crossroads) F 20 10032>0 -2975 3238 0 30363=1 4 Clue compass: Rimmington (Mine) F 20 10032>0 -1542 3630 0 30363=1 4 Clue compass: Shayzien Combat Ring F 20 10032>0 -1487 3637 0 30363=1 4 Clue compass: Shayzien War Tent F 20 10032>0 -2852 2953 0 30363=1 4 Clue compass: Shilo Village F 20 10032>0 -1812 3854 0 30363=1 4 Clue compass: Soul Altar F 20 10032>0 -2801 3080 0 30363=1 4 Clue compass: Tai Bwo Wannai Village F 20 10032>0 -2924 3479 0 30363=1 4 Clue compass: Taverly (Stone Circle) F 20 10032>0 -2887 3677 0 30363=1 4 Clue compass: Trollheim F 20 10032>0 -2465 5148 0 30363=1 4 Clue compass: Tzhaar Gem stall F 20 10032>0 -2479 5145 0 30363=1 4 Clue compass: Tzhaar Weapon Shop F 20 10032>0 -3208 3423 0 30363=1 4 Clue compass: Varrock (Aris' Tent) F 20 10032>0 -3210 3415 0 30363=1 4 Clue compass: Varrock (Clothes shop) F 20 10032>0 -3160 3465 0 30363=1 4 Clue compass: Varrock (Grand Exchange) F 20 10032>0 -3231 3494 0 30363=1 4 Clue compass: Varrock (Palace Gardens) F 20 10032>0 -3212 3490 0 30363=1 4 Clue compass: Varrock (Palace library) F 20 10032>0 -3253 3404 0 30363=1 4 Clue compass: Varrock (Rune shop) F 20 10032>0 -2851 3496 0 30363=1 4 Clue compass: White Wolf Mountain F 20 10032>0 -3113 3195 0 30363=1 4 Clue compass: Wizards' Tower F 20 10032>0 +# Destination menuOption menuTarget objectID Skills Item IDs Quests Duration Display info Consumable Wilderness level Varbits Varplayers +# Clue compass +1641 3809 0 30363 4 Clue compass: 4. Arceuus Library F 20 10032>0 +3110 3420 0 30363 4 Clue compass: B. Barbarian Village F 20 10032>0 +1663 10045 0 30363 4 Clue compass: H. Catacombs of Kourend F 20 10032>0 +3375 3419 0 30363 4 Clue compass: Q. Digsite F 20 10032>0 +3355 3348 0 30363 4 Clue compass: R: Digsite Exam centre F 20 10032>0 +3089 3333 0 30363 4 Clue compass: S: Draynor Manor F 20 10032>0 +3109 3288 0 30363 4 Clue compass: T: Draynor Village (Crossroads) F 20 10032>0 +3129 3250 0 30363 4 Clue compass: U: Draynor Village (Jail) F 20 10032>0 +3082 3252 0 30363 4 Clue compass: V: Draynor Village (Market) F 20 10032>0 +3096 3252 0 30363 4 Clue compass: W: Draynor Village (Miss Schism) F 20 10032>0 +3079 3504 0 30363 4 Clue compass: X: Edgeville F 20 10032>0 +3056 3484 0 30363 4 Clue compass: Y: Edgeville Monestary F 20 10032>0 +3024 4518 0 30363 4 Clue compass: Enchanted Valley F 20 10032>0 +2942 3338 0 30363 4 Clue compass: Falador (Gem shop) F 20 10032>0 +3045 3371 0 30363 4 Clue compass: Falador (Party room) F 20 10032>0 +2957 3502 0 30363 4 Clue compass: Goblin Village F 20 10032>0 +1712 3469 0 30363 4 Clue compass: Hosidius (Charcoal Burners) F 20 10032>0 +1645 3632 0 30363 4 Clue compass: Hosidius Mess F 20 10032>0 +3372 3499 0 30363 4 Clue compass: Limestone Mine F 20 10032>0 +3299 3490 0 30363 4 Clue compass: Lumber Yard Sawmill F 20 10032>0 +3234 3201 0 30363 4 Clue compass: Lumbridge (Bob's axes) F 20 10032>0 +3167 9571 0 30363 4 Clue compass: Lumbridge (Swamp caves) F 20 10032>0 +3200 3169 0 30363 4 Clue compass: Lumbridge (Swamp) F 20 10032>0 +3161 3298 0 30363 4 Clue compass: Lumbridge (Windmill) F 20 10032>0 +1317 3839 0 30363 4 Clue compass: Mount Karuulm F 20 10032>0 +2990 3110 0 30363 4 Clue compass: Mudskipper point F 20 10032>0 +2911 3169 0 30363 4 Clue compass: Musa Point F 20 10032>0 +3049 3237 0 30363 4 Clue compass: Port Sarim F 20 10032>0 +2981 3276 0 30363 4 Clue compass: Rimmington (Crossroads) F 20 10032>0 +2975 3238 0 30363 4 Clue compass: Rimmington (Mine) F 20 10032>0 +1542 3630 0 30363 4 Clue compass: Shayzien Combat Ring F 20 10032>0 +1487 3637 0 30363 4 Clue compass: Shayzien War Tent F 20 10032>0 +2852 2953 0 30363 4 Clue compass: Shilo Village F 20 10032>0 +1812 3854 0 30363 4 Clue compass: Soul Altar F 20 10032>0 +2801 3080 0 30363 4 Clue compass: Tai Bwo Wannai Village F 20 10032>0 +2924 3479 0 30363 4 Clue compass: Taverly (Stone Circle) F 20 10032>0 +2887 3677 0 30363 4 Clue compass: Trollheim F 20 10032>0 +2465 5148 0 30363 4 Clue compass: Tzhaar Gem stall F 20 10032>0 +2479 5145 0 30363 4 Clue compass: Tzhaar Weapon Shop F 20 10032>0 +3208 3423 0 30363 4 Clue compass: Varrock (Aris' Tent) F 20 10032>0 +3210 3415 0 30363 4 Clue compass: Varrock (Clothes shop) F 20 10032>0 +3160 3465 0 30363 4 Clue compass: Varrock (Grand Exchange) F 20 10032>0 +3231 3494 0 30363 4 Clue compass: Varrock (Palace Gardens) F 20 10032>0 +3212 3490 0 30363 4 Clue compass: Varrock (Palace library) F 20 10032>0 +3253 3404 0 30363 4 Clue compass: Varrock (Rune shop) F 20 10032>0 +2851 3496 0 30363 4 Clue compass: White Wolf Mountain F 20 10032>0 +3113 3195 0 30363 4 Clue compass: Wizards' Tower F 20 10032>0 +# Map of Alacrity +3035 9806 0 33233 4 Map of Alacrity: Asgarnia - Dwarf Mine Crevice F 60 10032>0 +2936 3355 0 33233 4 Map of Alacrity: Asgarnia - Falador Crumbling Wall F 60 10032>0 +3031 3391 0 33233 4 Map of Alacrity: Asgarnia - Falador Wall Grapple F 60 10032>0 +2947 3313 0 33233 4 Map of Alacrity: Asgarnia - Falador Wall Tunnel F 60 10032>0 +2900 3712 0 33233 4 Map of Alacrity: Asgarnia - God Wars Dungeon Crack F 60 10032>0 +2927 3761 0 33233 4 Map of Alacrity: Asgarnia - God Wars Dungeon Rocky Handholds F 60 10032>0 +2912 5300 0 33233 4 Map of Alacrity: Asgarnia - God Wars Dungeon Saradomin Rope F 60 10032>0 +2898 9901 0 33233 4 Map of Alacrity: Asgarnia - Heroes' Guild Crevice F 60 10032>0 +2989 9547 0 33233 4 Map of Alacrity: Asgarnia - Ice Dungeon Giants Tunnel F 60 10032>0 +3025 9570 0 33233 4 Map of Alacrity: Asgarnia - Ice Dungeon Wyvern/Hobgoblin Tunnel F 60 10032>0 +3012 9549 0 33233 4 Map of Alacrity: Asgarnia - Ice Dungeon Wyvern Entry F 60 10032>0 +2998 3484 0 33233 4 Map of Alacrity: Asgarnia - Ice Mountain Rocks F 60 10032>0 +3124 9703 0 33233 4 Map of Alacrity: Asgarnia - Motherlode Mine North Tunnel F 60 10032>0 +3118 9702 0 33233 4 Map of Alacrity: Asgarnia - Motherlode Mine South Tunnel F 60 10032>0 +2946 3439 0 33233 4 Map of Alacrity: Asgarnia - Taverley Wall Climb F 60 10032>0 +2936 9810 0 33233 4 Map of Alacrity: Asgarnia - Taverley Dungeon Loose Railing F 60 10032>0 +2886 9799 0 33233 4 Map of Alacrity: Asgarnia - Taverley Dungeon Blue Dragon Pipe F 60 10032>0 +2880 9813 0 33233 4 Map of Alacrity: Asgarnia - Taverley Dungeon Floor Trap F 60 10032>0 +2841 3694 0 33233 4 Map of Alacrity: Asgarnia - Troll Stronghold Rooftop Rocks F 60 10032>0 +2869 3670 0 33233 4 Map of Alacrity: Asgarnia - Trollheim Easy Climb F 60 10032>0 +2876 3666 0 33233 4 Map of Alacrity: Asgarnia - Trollheim Medium Climb F 60 10032>0 +2907 3686 0 33233 4 Map of Alacrity: Asgarnia - Trollheim Advanced Climb F 60 10032>0 +2902 3680 0 33233 4 Map of Alacrity: Asgarnia - Trollheim Hard Climb F 60 10032>0 +2945 3678 0 33233 4 Map of Alacrity: Asgarnia - Trollheim Wilderness Climb F 60 10032>0 +3293 3158 0 33233 4 Map of Alacrity: Desert - Al Kharid Palace Window F 60 10032>0 +3305 3315 0 33233 4 Map of Alacrity: Desert - Al Kharid Mine Rocks F 60 10032>0 +3245 3179 0 33233 4 Map of Alacrity: Desert - Al Kharid River Grapple F 60 10032>0 +3214 9508 0 33233 4 Map of Alacrity: Desert - Kalphite Lair Crevice F 60 10032>0 +3210 3135 0 33233 4 Map of Alacrity: Desert - Kalphite Lair River Stepping Stone F 60 10032>0 +3293 2706 0 33233 4 Map of Alacrity: Desert - Necropolis Stepping Stone F 60 10032>0 +3371 2958 0 33233 4 Map of Alacrity: Desert - Pollnivneach Stepping Stone F 60 10032>0 +2775 10003 0 33233 4 Map of Alacrity: Fremennik - Slayer Dungeon Traps F 60 10032>0 +2729 10008 0 33233 4 Map of Alacrity: Fremennik - Slayer Dungeon Crevice F 60 10032>0 +2572 3862 0 33233 4 Map of Alacrity: Fremennik - Miscellania Stepping Stone F 60 10032>0 +2688 3697 0 33233 4 Map of Alacrity: Fremennik - Rellekka Broken Fence F 60 10032>0 +2546 3750 0 33233 4 Map of Alacrity: Fremennik - Waterbirth Island Rocks F 60 10032>0 +2604 10070 0 33233 4 Map of Alacrity: Fremennik - Waterbirth Island Dungeon Crevice F 60 10032>0 +2605 3335 0 33233 4 Map of Alacrity: Kandarin - Ardougne: Log balance F 60 10032>0 +2521 3595 0 33233 4 Map of Alacrity: Kandarin - Barbarian outpost: Basalt causeway F 60 10032>0 +2868 3429 0 33233 4 Map of Alacrity: Kandarin - Catherby: Rock grapple F 60 10032>0 +2840 3427 0 33233 4 Map of Alacrity: Kandarin - Catherby: Water obelisk grapple F 60 10032>0 +2545 2871 0 33233 4 Map of Alacrity: Kandarin - Corsair cove: Rocks F 60 10032>0 +2491 2898 0 33233 4 Map of Alacrity: Kandarin - Corsair cove: Resource area rocks F 60 10032>0 +1981 8993 1 33233 4 Map of Alacrity: Kandarin - Corsair cove: Cave pillar jump F 60 10032>0 +2485 3515 0 33233 4 Map of Alacrity: Kandarin - Gnome Stronghold: Rock climb F 60 10032>0 +2315 3505 0 33233 4 Map of Alacrity: Kandarin - Eagles peak: Rocks F 60 10032>0 +2604 3473 0 33233 4 Map of Alacrity: Kandarin - Coal trucks: Log balance F 60 10032>0 +2451 3154 0 33233 4 Map of Alacrity: Kandarin - Observatory: Grapple F 60 10032>0 +2721 3591 0 33233 4 Map of Alacrity: Kandarin - Sinclair mansion: Log balance F 60 10032>0 +2428 9806 0 33233 4 Map of Alacrity: Kandarin - Stronghold slayer cave: Tunnel F 60 10032>0 +2560 3107 0 33233 4 Map of Alacrity: Kandarin - Yanille: Wall climb F 60 10032>0 +2575 3114 0 33233 4 Map of Alacrity: Kandarin - Yanille: Wall tunnel F 60 10032>0 +2556 3075 0 33233 4 Map of Alacrity: Kandarin - Yanille Wall grapple F 60 10032>0 +2580 9521 0 33233 4 Map of Alacrity: Kandarin - Yanille dungeon: Ledge F 60 10032>0 +2579 9505 0 33233 4 Map of Alacrity: Kandarin - Yanille dungeon: Pipe F 60 10032>0 +2599 9495 0 33233 4 Map of Alacrity: Kandarin - Yanille dungeon: Monkeybars F 60 10032>0 +2618 9571 0 33233 4 Map of Alacrity: Kandarin - Yanille dungeon: Rubble F 60 10032>0 +2698 9491 0 33233 4 Map of Alacrity: Karamja - Brimhaven Dungeon Demon Pipe F 60 10032>0 +2685 9547 0 33233 4 Map of Alacrity: Karamja - Brimhaven Dungeon Red Dragon Stepping Stones F 60 10032>0 +2672 9582 0 33233 4 Map of Alacrity: Karamja - Brimhaven Dungeon Vine F 60 10032>0 +2831 3252 0 33233 4 Map of Alacrity: Karamja - Crandor Rock Climb F 60 10032>0 +2898 2943 0 33233 4 Map of Alacrity: Karamja - Kharazi Jungle Vine F 60 10032>0 +2873 3143 0 33233 4 Map of Alacrity: Karamja - Musa Point Grapple F 60 10032>0 +2864 2970 0 33233 4 Map of Alacrity: Karamja - Shilo Village Stepping Stone F 60 10032>0 +2870 3003 0 33233 4 Map of Alacrity: Karamja - Shilo Village Rocks F 60 10032>0 +1316 10214 0 33233 4 Map of Alacrity: Kourend - Karuulm Slayer Dungeon Hydra Pipe F 60 10032>0 +1324 3782 0 33233 4 Map of Alacrity: Kourend - Karuulm Climb F 60 10032>0 +1774 3888 0 33233 4 Map of Alacrity: Kourend - Dense Essence Mine Boulder Jump F 60 10032>0 +1770 3851 0 33233 4 Map of Alacrity: Kourend - Dense Essence Mine Rocks (East) F 60 10032>0 +1759 3873 0 33233 4 Map of Alacrity: Kourend - Dense Essence Mine Rocks (North) F 60 10032>0 +1742 3853 0 33233 4 Map of Alacrity: Kourend - Dense Essence Mine Rocks (West) F 60 10032>0 +1715 10057 0 33233 4 Map of Alacrity: Kourend - Catacombs Crack (North) F 60 10032>0 +1647 10008 0 33233 4 Map of Alacrity: Kourend - Catacombs Crack (South) F 60 10032>0 +1613 10071 0 33233 4 Map of Alacrity: Kourend - Catacombs Dragons Stepping Stones F 60 10032>0 +1309 10099 0 33233 4 Map of Alacrity: Kourend - Chasm of Fire Gap F 60 10032>0 +1307 10076 0 33233 4 Map of Alacrity: Kourend - Chasm of Fire Chain F 60 10032>0 +1612 3570 0 33233 4 Map of Alacrity: Kourend - Hosidius Lake Stepping Stones F 60 10032>0 +1720 3551 0 33233 4 Map of Alacrity: Kourend - Hosidius River Stepping Stone F 60 10032>0 +1819 9946 0 33233 4 Map of Alacrity: Kourend - Forthos Dungeon Trap F 60 10032>0 +1629 4023 0 33233 4 Map of Alacrity: Kourend - Wintertodt Gap F 60 10032>0 +3544 3282 0 33233 4 Map of Alacrity: Morytania - Barrows Wall F 60 10032>0 +3470 3219 0 33233 4 Map of Alacrity: Morytania - Burgh de Rott Fence F 60 10032>0 +3669 3375 0 33233 4 Map of Alacrity: Morytania - Darkmeyer Wall Climb F 60 10032>0 +3563 3380 0 33233 4 Map of Alacrity: Morytania - Darkmeyer Wall Rocks F 60 10032>0 +3504 3560 0 33233 4 Map of Alacrity: Morytania - Mausoleum Bridge Jump F 60 10032>0 +3623 9747 0 33233 4 Map of Alacrity: Morytania - Meyerditch Lab Cave F 60 10032>0 +3499 9738 0 33233 4 Map of Alacrity: Morytania - Meyerditch Lab Cave (Advanced) F 60 10032>0 +3418 3326 0 33233 4 Map of Alacrity: Morytania - Mort Myre Stepping Stone F 60 10032>0 +3710 2970 0 33233 4 Map of Alacrity: Morytania - Mos le'Harmless Stepping Stone F 60 10032>0 +3442 3532 0 33233 4 Map of Alacrity: Morytania - Slayer Tower Broken Window F 60 10032>0 +3421 3550 0 33233 4 Map of Alacrity: Morytania - Slayer Tower Chain (Medium) F 60 10032>0 +3447 3578 0 33233 4 Map of Alacrity: Morytania - Slayer Tower Chain (Hard) F 60 10032>0 +3417 3533 0 33233 4 Map of Alacrity: Morytania - Slayer Tower Ivy Climb F 60 10032>0 +2345 3300 0 33233 4 Map of Alacrity: Tirannwn - Arandar Rocks (North) F 60 10032>0 +2337 3288 0 33233 4 Map of Alacrity: Tirannwn - Arandar Rocks (Mid) F 60 10032>0 +2337 3253 0 33233 4 Map of Alacrity: Tirannwn - Arandar Rocks (South) F 60 10032>0 +3221 12441 0 33233 4 Map of Alacrity: Tirannwn - Iorwerth Dungeon Gap (North) F 60 10032>0 +3241 12420 0 33233 4 Map of Alacrity: Tirannwn - Iorwerth Dungeon Gap (South) F 60 10032>0 +2156 3073 0 33233 4 Map of Alacrity: Tirannwn - Zul-Andra Stepping Stone F 60 10032>0 +1343 2917 0 33233 4 Map of Alacrity: Varlamore - Aldarin: Rocks F 60 10032>0 +1367 3328 0 33233 4 Map of Alacrity: Varlamore - Nemus retreat (north) F 60 10032>0 +1370 3293 0 33233 4 Map of Alacrity: Varlamore - Nemus retreat (south) F 60 10032>0 +1391 3309 0 33233 4 Map of Alacrity: Varlamore - Nemus retreat (east) F 60 10032>0 +1452 3328 0 33233 4 Map of Alacrity: Varlamore - Auburn valley: Log balance (north) F 60 10032>0 +1402 3291 0 33233 4 Map of Alacrity: Varlamore - Auburn valley: Log balance (south) F 60 10032>0 +1477 3310 0 33233 4 Map of Alacrity: Varlamore - Darkfrost: Rocks F 60 10032>0 +1586 3263 0 33233 4 Map of Alacrity: Varlamore - Proudspire: Lower rocks F 60 10032>0 +1582 3247 0 33233 4 Map of Alacrity: Varlamore - Proudspire: Upper rocks F 60 10032>0 +1466 3127 0 33233 4 Map of Alacrity: Varlamore - Ralos rise: Rocks F 60 10032>0 +1283 3148 0 33233 4 Map of Alacrity: Varlamore - Tlati rainforest: Log balance F 60 10032>0 +1274 3003 0 33233 4 Map of Alacrity: Varlamore - Tlati rainforest: Rock climb F 60 10032>0 +2403 4406 0 33233 4 Map of Alacrity: Varlamore - Zanaris: Cosmic altar path F 60 10032>0 +3268 3625 0 33233 4 Map of Alacrity: Wilderness - Chaos Temple Stepping Stone F 60 10032>0 +3046 10325 0 33233 4 Map of Alacrity: Wilderness - Deep Wilderness Dungeon Crevice F 60 10032>0 +3200 3807 0 33233 4 Map of Alacrity: Wilderness - Lava Dragon Isle Stepping Stone F 60 10032>0 +3090 3883 0 33233 4 Map of Alacrity: Wilderness - Lava Maze Stepping Stone F 60 10032>0 +3180 10212 0 33233 4 Map of Alacrity: Wilderness - Revenant Cave Medium Pillar (North 1) F 60 10032>0 +3205 10195 0 33233 4 Map of Alacrity: Wilderness - Revenant Cave Medium Pillar (North 2) F 60 10032>0 +3203 10135 0 33233 4 Map of Alacrity: Wilderness - Revenant Cave Medium Pillar (South) F 60 10032>0 +3243 10145 0 33233 4 Map of Alacrity: Wilderness - Revenant Cave Hard Pillar F 60 10032>0 +3435 10103 0 33233 4 Map of Alacrity: Wilderness - Wilderness Slayer Cave Jellies F 60 10032>0 +3337 10132 0 33233 4 Map of Alacrity: Wilderness - Wilderness Slayer Cave Lesser Demons F 60 10032>0