diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/construction/ConstructionScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/construction/ConstructionScript.java index 4ba1b15a05..ac46d2826c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/construction/ConstructionScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/construction/ConstructionScript.java @@ -1,6 +1,9 @@ package net.runelite.client.plugins.microbot.construction; -import net.runelite.api.*; +import net.runelite.api.ItemID; +import net.runelite.api.NPC; +import net.runelite.api.SpriteID; +import net.runelite.api.TileObject; import net.runelite.api.widgets.Widget; import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.plugins.microbot.Script; @@ -9,21 +12,18 @@ import net.runelite.client.plugins.microbot.util.gameobject.Rs2GameObject; import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; import net.runelite.client.plugins.microbot.util.keyboard.Rs2Keyboard; -import net.runelite.client.plugins.microbot.util.math.Random; +import net.runelite.client.plugins.microbot.util.math.Rs2Random; import net.runelite.client.plugins.microbot.util.npc.Rs2Npc; import net.runelite.client.plugins.microbot.util.tabs.Rs2Tab; import net.runelite.client.plugins.microbot.util.widget.Rs2Widget; -import java.awt.event.KeyEvent; import java.util.concurrent.TimeUnit; - public class ConstructionScript extends Script { ConstructionState state = ConstructionState.Idle; - - + public TileObject getOakLarderSpace() { return Rs2GameObject.findObjectById(15403); } @@ -36,26 +36,10 @@ public NPC getButler() { return Rs2Npc.getNpc("Demon butler"); } - public boolean hasDialogueOptionToUnnote() { - return Rs2Widget.findWidget("Un-note", null) != null; - } - - public boolean hasPayButlerDialogue() { - return Rs2Widget.findWidget("must render unto me the 10,000 coins that are due", null) != null; - } - - public boolean hasDialogueOptionToPay() { - return Rs2Widget.findWidget("Okay, here's 10,000 coins.", null) != null; - } - public boolean hasFurnitureInterfaceOpen() { return Rs2Widget.findWidget("Furniture", null) != null; } - public boolean hasRemoveLarderInterfaceOpen() { - return Rs2Widget.findWidget("Really remove it?", null) != null; - } - public boolean run(ConstructionConfig config) { mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { try { @@ -87,7 +71,7 @@ private void calculateState() { TileObject oakLarderSpace = getOakLarderSpace(); TileObject oakLarder = getOakLarder(); NPC butler = getButler(); - boolean hasRequiredPlanks = Rs2Inventory.hasItemAmount(ItemID.OAK_PLANK, Random.random(8, 16)); //oak plank + boolean hasRequiredPlanks = Rs2Inventory.hasItemAmount(ItemID.OAK_PLANK, Rs2Random.between(8, 16)); if (oakLarderSpace == null && oakLarder != null) { state = ConstructionState.Remove; } else if (oakLarderSpace != null && oakLarder == null && hasRequiredPlanks) { @@ -115,9 +99,9 @@ private void remove() { TileObject oaklarder = getOakLarder(); if (oaklarder == null) return; if (Rs2GameObject.interact(oaklarder, "Remove")) { - sleepUntilOnClientThread(() -> hasRemoveLarderInterfaceOpen(), 5000); - Rs2Keyboard.keyPress('1'); - sleepUntilOnClientThread(() -> getOakLarderSpace() != null, 5000); + Rs2Dialogue.sleepUntilHasQuestion("Really remove it?"); + Rs2Dialogue.keyPressForDialogueOption(1); + sleepUntil(() -> getOakLarderSpace() != null, 5000); } } @@ -129,7 +113,9 @@ private void butler() { int distance = butler.getWorldLocation().distanceTo(Microbot.getClient().getLocalPlayer().getWorldLocation()); return distance > 3; }); - if (butlerIsToFar) { + if (!butlerIsToFar) { + Rs2Npc.interact(butler, "talk-to"); + } else { Rs2Tab.switchToSettingsTab(); sleep(800, 1800); Widget houseOptionWidget = Rs2Widget.findWidget(SpriteID.OPTIONS_HOUSE_OPTIONS, null); @@ -141,30 +127,36 @@ private void butler() { Microbot.getMouse().click(callServantWidget.getCanvasLocation()); } + Rs2Dialogue.sleepUntilInDialogue(); + + if (Rs2Dialogue.hasQuestion("Repeat last task?")) { + Rs2Dialogue.keyPressForDialogueOption(1); + Rs2Random.waitEx(2400, 300); + Rs2Dialogue.sleepUntilInDialogue(); + return; + } - if (Rs2Dialogue.isInDialogue() || Rs2Npc.interact(butler, "Talk-to")) { - sleep(1200); - Rs2Keyboard.keyPress(KeyEvent.VK_SPACE); - sleep(1200, 2000); - if (Rs2Widget.findWidget("Go to the bank...", null) != null) { - Rs2Inventory.useItemOnNpc(ItemID.OAK_PLANK + 1, butler.getId()); // + 1 for noted item - sleepUntilOnClientThread(() -> Rs2Widget.hasWidget("Dost thou wish me to exchange that certificate")); - Rs2Keyboard.keyPress(KeyEvent.VK_SPACE); - sleepUntilOnClientThread(() -> Rs2Widget.hasWidget("Select an option")); - Rs2Keyboard.typeString("1"); - sleepUntilOnClientThread(() -> Rs2Widget.hasWidget("Enter amount:")); + if (Rs2Dialogue.hasSelectAnOption()) { + if (Rs2Dialogue.hasDialogueOption("Go to the bank...")) { + Rs2Dialogue.sleepUntilHasDialogueText("Dost thou wish me to exchange that certificate"); + Rs2Dialogue.clickContinue(); + Rs2Dialogue.sleepUntilSelectAnOption(); + Rs2Dialogue.keyPressForDialogueOption(1); + Rs2Widget.sleepUntilHasWidget("Enter amount:"); Rs2Keyboard.typeString("28"); Rs2Keyboard.enter(); - } else if (hasDialogueOptionToUnnote()) { - Rs2Keyboard.keyPress('1'); - sleepUntilOnClientThread(() -> !hasDialogueOptionToUnnote()); - } else if (hasPayButlerDialogue() || hasDialogueOptionToPay()) { - Rs2Keyboard.keyPress(KeyEvent.VK_SPACE); - sleep(1200, 2000); - if (hasDialogueOptionToPay()) { - Rs2Keyboard.keyPress('1'); - } + Rs2Dialogue.clickContinue(); + Rs2Random.waitEx(2400, 300); + Rs2Dialogue.sleepUntilInDialogue(); + return; } } + + if (Rs2Dialogue.hasDialogueText("must render unto me the 10,000 coins that are due")) { + Rs2Dialogue.clickContinue(); + Rs2Random.waitEx(1200, 300); + Rs2Dialogue.sleepUntilSelectAnOption(); + Rs2Dialogue.keyPressForDialogueOption(1); + } } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/crafting/jewelry/JewelryScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/crafting/jewelry/JewelryScript.java index 2495795dc2..3fe3c3176b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/crafting/jewelry/JewelryScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/crafting/jewelry/JewelryScript.java @@ -85,16 +85,21 @@ public boolean run() { if (shouldCutGems) { if (!Rs2Inventory.isEmpty()) { Rs2Bank.depositAllExcept(ItemID.CHISEL); - Rs2Inventory.waitForInventoryChanges(1200); + Rs2Inventory.waitForInventoryChanges(1800); } if (!Rs2Inventory.hasItem(ItemID.CHISEL)) { + if (!Rs2Bank.hasItem(ItemID.CHISEL)) { + Microbot.showMessage("Missing Chisel in Bank!"); + shutdown(); + return; + } Rs2Bank.withdrawOne(ItemID.CHISEL); - Rs2Inventory.waitForInventoryChanges(1200); + Rs2Inventory.waitForInventoryChanges(1800); } Rs2Bank.withdrawAll(plugin.getJewelry().getGem().getUncutItemID()); - Rs2Inventory.waitForInventoryChanges(1200); + Rs2Inventory.waitForInventoryChanges(1800); Rs2Bank.closeBank(); sleepUntil(() -> !Rs2Bank.isOpen()); return; @@ -109,7 +114,7 @@ public boolean run() { if (shouldCraftJewelry) { if (!Rs2Inventory.isEmpty()){ Rs2Bank.depositAllExcept(plugin.getJewelry().getToolItemID()); - Rs2Inventory.waitForInventoryChanges(1200); + Rs2Inventory.waitForInventoryChanges(1800); } if (!Rs2Inventory.hasItem(plugin.getJewelry().getToolItemID())) { @@ -119,16 +124,16 @@ public boolean run() { return; } Rs2Bank.withdrawOne(plugin.getJewelry().getToolItemID()); - Rs2Inventory.waitForInventoryChanges(1200); + Rs2Inventory.waitForInventoryChanges(1800); } if (plugin.getJewelry().getGem() != null) { Rs2Bank.withdrawX(plugin.getJewelry().getGem().getCutItemID(), withdrawAmount); - Rs2Inventory.waitForInventoryChanges(1200); + Rs2Inventory.waitForInventoryChanges(1800); } Rs2Bank.withdrawX(plugin.getJewelry().getJewelryType().getItemID(), withdrawAmount); - Rs2Inventory.waitForInventoryChanges(1200); + Rs2Inventory.waitForInventoryChanges(1800); Rs2Bank.closeBank(); sleepUntil(() -> !Rs2Bank.isOpen()); return; @@ -242,18 +247,18 @@ public boolean run() { if (Rs2Inventory.hasItem(plugin.getJewelry().getItemID())) { Rs2Bank.depositAll(plugin.getJewelry().getItemID()); - Rs2Inventory.waitForInventoryChanges(1200); + Rs2Inventory.waitForInventoryChanges(1800); } if (!Rs2Inventory.isEmpty()) { Rs2Bank.depositAllExcept(false,"coins", "rune", plugin.getJewelry().getItemName()); - Rs2Inventory.waitForInventoryChanges(1200); + Rs2Inventory.waitForInventoryChanges(1800); } // Withdraw and equip the staff if needed if (!Rs2Equipment.hasEquipped(staffItemID)) { Rs2Bank.withdrawOne(staffItemID); - Rs2Inventory.waitForInventoryChanges(1200); + Rs2Inventory.waitForInventoryChanges(1800); Rs2Inventory.equip(staffItemID); } @@ -262,7 +267,7 @@ public boolean run() { if (totalAlchJewelry > 0) { Rs2Bank.setWithdrawAsNote(); Rs2Bank.withdrawAll(plugin.getJewelry().getItemID()); - Rs2Inventory.waitForInventoryChanges(1200); + Rs2Inventory.waitForInventoryChanges(1800); Rs2Bank.setWithdrawAsItem(); } @@ -277,16 +282,16 @@ public boolean run() { } Rs2Bank.withdrawRunePouch(); - Rs2Inventory.waitForInventoryChanges(1200); + Rs2Inventory.waitForInventoryChanges(1800); } } else { // Otherwise, withdraw nature runes based on the needed amount if (natureRunesInInventory == 0 && natureRunesToWithdraw > 0) { Rs2Bank.withdrawAll(ItemID.NATURE_RUNE); - Rs2Inventory.waitForInventoryChanges(1200); + Rs2Inventory.waitForInventoryChanges(1800); } else if (natureRunesToWithdraw > 0) { Rs2Bank.withdrawX(ItemID.NATURE_RUNE, natureRunesToWithdraw); - Rs2Inventory.waitForInventoryChanges(1200); + Rs2Inventory.waitForInventoryChanges(1800); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magic/aiomagic/scripts/AlchScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magic/aiomagic/scripts/AlchScript.java index 1b19586f9e..3a0616353b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magic/aiomagic/scripts/AlchScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/magic/aiomagic/scripts/AlchScript.java @@ -14,6 +14,7 @@ import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; import net.runelite.client.plugins.microbot.util.inventory.Rs2Item; import net.runelite.client.plugins.microbot.util.magic.Rs2Magic; +import net.runelite.client.plugins.microbot.util.magic.Rs2Spells; import net.runelite.client.plugins.microbot.util.magic.Rs2Staff; import net.runelite.client.plugins.microbot.util.magic.Runes; import net.runelite.client.plugins.microbot.util.math.Rs2Random; @@ -21,6 +22,7 @@ import javax.inject.Inject; import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; public class AlchScript extends Script { @@ -164,13 +166,31 @@ private boolean hasStateChanged() { } private MagicState updateState() { - if (state == null) return MagicState.BANKING; + if (state == null) { + if (hasRequiredItems()) { + return MagicState.CASTING; + } else { + return MagicState.BANKING; + } + } if (state == MagicState.BANKING && hasRequiredItems()) return MagicState.CASTING; if (state == MagicState.CASTING && !hasRequiredItems()) return MagicState.BANKING; return null; } private boolean hasRequiredItems() { - return Rs2Inventory.hasItem(plugin.getAlchItemNames()); + return Rs2Inventory.hasItem(plugin.getAlchItemNames()) && getRequiredAlchRunes(getAlchCastAmount()).isEmpty(); + } + + private Map getRequiredAlchRunes(int casts) { + Rs2Spells alchSpell = Rs2Player.getRealSkillLevel(Skill.MAGIC) >= 55 ? Rs2Spells.HIGH_LEVEL_ALCHEMY : Rs2Spells.LOW_LEVEL_ALCHEMY; + return Rs2Magic.getRequiredRunes(alchSpell, plugin.getStaff(), casts, false); + } + + private int getAlchCastAmount() { + return Rs2Inventory.items().stream() + .filter(item -> plugin.getAlchItemNames().contains(item.getName().toLowerCase())) + .mapToInt(Rs2Item::getQuantity) + .sum(); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/nmz/NmzScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/nmz/NmzScript.java index 06dfb64f55..8108622c48 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/nmz/NmzScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/nmz/NmzScript.java @@ -8,6 +8,7 @@ import net.runelite.client.plugins.microbot.Microbot; import net.runelite.client.plugins.microbot.Script; import net.runelite.client.plugins.microbot.playerassist.combat.PrayerPotionScript; +import net.runelite.client.plugins.microbot.util.bank.Rs2Bank; import net.runelite.client.plugins.microbot.util.combat.Rs2Combat; import net.runelite.client.plugins.microbot.util.gameobject.Rs2GameObject; import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; @@ -17,6 +18,8 @@ import net.runelite.client.plugins.microbot.util.player.Rs2Player; import net.runelite.client.plugins.microbot.util.prayer.Rs2Prayer; import net.runelite.client.plugins.microbot.util.prayer.Rs2PrayerEnum; +import net.runelite.client.plugins.microbot.util.security.Encryption; +import net.runelite.client.plugins.microbot.util.security.Login; import net.runelite.client.plugins.microbot.util.walker.Rs2Walker; import net.runelite.client.plugins.microbot.util.walker.WalkerState; import net.runelite.client.plugins.microbot.util.widget.Rs2Widget; @@ -177,7 +180,7 @@ public void manageSelfHarm() { } if (currentHP == 1) { - maxHealth = Random.random(2, 8); + maxHealth = Random.random(2, 4); } } @@ -272,8 +275,15 @@ public void handleStore() { } Rs2GameObject.interact(26273); - - sleepUntil(() -> Rs2Widget.getWidget(13500418) != null, 10000); + sleepUntil(() -> Rs2Widget.isWidgetVisible(13500418) || Rs2Bank.isBankPinWidgetVisible(), 10000); + if (Rs2Bank.isBankPinWidgetVisible()) { + try { + Rs2Bank.handleBankPin(Encryption.decrypt(Login.activeProfile.getBankPin())); + } catch (Exception e) { + throw new RuntimeException(e); + } + sleepUntil(() -> Rs2Widget.isWidgetVisible(13500418), 10000); + } Widget benefitsBtn = Rs2Widget.getWidget(13500418); if (benefitsBtn == null) return; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/bank/Rs2Bank.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/bank/Rs2Bank.java index ba730cdea8..0a7fd63459 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/bank/Rs2Bank.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/bank/Rs2Bank.java @@ -1439,11 +1439,8 @@ public static boolean handleBankPin(String pin) { Microbot.log("Unable to enter bankpin with value " + pin); return false; } - Widget bankPinWidget = Rs2Widget.getWidget(ComponentID.BANK_PIN_CONTAINER); - boolean isBankPinVisible = Microbot.getClientThread().runOnClientThread(() -> bankPinWidget != null && !bankPinWidget.isHidden()); - - if (isBankPinVisible) { + if (isBankPinWidgetVisible()) { for (int i = 0; i < pin.length(); i++){ char c = pin.charAt(i); Rs2Widget.clickWidget(String.valueOf(c), Optional.of(213), 0, true); @@ -1453,6 +1450,10 @@ public static boolean handleBankPin(String pin) { } return false; } + + public static boolean isBankPinWidgetVisible() { + return Rs2Widget.isWidgetVisible(ComponentID.BANK_PIN_CONTAINER); + } /** * Banks items if your inventory does not have enough emptyslots (0 emptyslots being full). Will walk back to the initialplayerlocation passed as param diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/dialogues/Rs2Dialogue.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/dialogues/Rs2Dialogue.java index f5b8b28ade..b6163cfe90 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/dialogues/Rs2Dialogue.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/util/dialogues/Rs2Dialogue.java @@ -406,6 +406,69 @@ public static List getCombinationOptions() { return options; } + /** + * Retrieves the text content of the current dialogue, if any. + * + *

This method checks if the player is currently in a dialogue state and retrieves the + * text from a specific widget associated with dialogue content. If no dialogue is active + * or the relevant widget is not visible, the method returns {@code null}. + * + * @return the text content of the dialogue, or {@code null} if no dialogue is active. + */ + public static String getDialogueText() { + if (!isInDialogue()) return null; + + if (Rs2Widget.isWidgetVisible(231, 6)) { + return Rs2UiHelper.stripColTags(Rs2Widget.getWidget(231, 6).getText()); + } + + return null; + } + + /** + * Checks if the current dialogue contains the specified text. + * + * @param text the text to search for in the dialogue. + * @param exact if true, requires an exact match; if false, allows partial matches. + * @return true if the specified text is found in the dialogue, otherwise false. + */ + public static boolean hasDialogueText(String text, boolean exact) { + String dialogueText = getDialogueText(); + if (dialogueText == null) return false; + return exact ? dialogueText.equalsIgnoreCase(text) : dialogueText.toLowerCase().contains(text.toLowerCase()); + } + + /** + * Checks if the current dialogue contains the specified text, allowing partial matches. + * + * @param text the text to search for in the dialogue. + * @return true if the specified text is found in the dialogue, otherwise false. + */ + public static boolean hasDialogueText(String text) { + return hasDialogueText(text, false); + } + + /** + * Pauses the current thread until the dialogue contains the specified text. + * + * @param text the text to wait for in the dialogue. + * @param exact if true, waits for an exact match; if false, waits for a partial match. + * @return true if the dialogue text appears within the timeout period, otherwise false. + */ + public static boolean sleepUntilHasDialogueText(String text, boolean exact) { + return sleepUntilTrue(() -> hasDialogueText(text, exact)); + } + + /** + * Pauses the current thread until the dialogue contains the specified text, allowing partial matches. + * + * @param text the text to wait for in the dialogue. + * @return true if the dialogue text appears within the timeout period, otherwise false. + */ + public static boolean sleepUntilHasDialogueText(String text) { + return sleepUntilHasDialogueText(text, false); + } + /** * Finds a specific combination dialogue option widget that matches the provided text. * diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/teleportation_items.tsv b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/teleportation_items.tsv index 1b222a2085..84e836b58e 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/teleportation_items.tsv +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/teleportation_items.tsv @@ -292,7 +292,7 @@ 1713 3612 0 21760;25818 The Depths of Despair 6035>1 Y T 19 4 Kharedst's memoirs: Lunch by the Lancalliums 1802 3748 0 21760;25818 The Queen of Thieves 6035>1 Y T 19 4 Kharedst's memoirs: The Fisher's Flute 1478 3576 0 21760;25818 Tale of the Righteous 6035>1 Y T 19 4 Kharedst's memoirs: History and Hearsay -1544 3762 0 21760;25818 The Forsaken Tower 6035>1 Y T 19 4 Kharedst's memoirs: Jewelry of Jubilation +1544 3762 0 21760;25818 The Forsaken Tower 6035>1 Y T 19 4 Kharedst's memoirs: Jewellery of Jubilation 1680 3746 0 21760;25818 The Ascent of Arceuus 6035>1 Y T 19 4 Kharedst's memoirs: A Dark Disposition 3649 3230 0 22400 Y F 19 4 Drakan's medallion: Ver Sinhaza 3592 3337 0 22400 Sins of the Father Y F 19 4 Drakan's medallion: Darkmeyer diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/transports.tsv b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/transports.tsv index 9157877aad..5469790c28 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/transports.tsv +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/shortestpath/transports.tsv @@ -1003,14 +1003,14 @@ 3304 3117 0 3304 3115 0 Go-through;Shantay pass;4031 1 Shantay pass 2 3304 3115 0 3304 3117 0 Go-through;Shantay pass;4031 2 3303 3115 0 3304 3117 0 Go-through;Shantay pass;4031 2 -3193 2843 0 3196 2843 0 Go-through;Shantay pass;41863 1 Shantay pass 3 -3193 2842 0 3196 2842 0 Go-through;Shantay pass;41863 1 Shantay pass 3 -3196 2843 0 3193 2843 0 Go-through;Shantay pass;41863 3 -3196 2842 0 3193 2842 0 Go-through;Shantay pass;41863 3 -3167 2819 0 3167 2816 0 Go-through;Shantay pass;41863 1 Shantay pass 3 -3168 2819 0 3168 2816 0 Go-through;Shantay pass;41863 1 Shantay pass 3 -3167 2816 0 3167 2819 0 Go-through;Shantay pass;41863 3 -3168 2816 0 3168 2819 0 Go-through;Shantay pass;41863 3 +3193 2843 0 3196 2843 0 Go-through;Shantay pass;41326 1 Shantay pass 3 +3193 2842 0 3196 2842 0 Go-through;Shantay pass;41326 1 Shantay pass 3 +3196 2843 0 3193 2843 0 Go-through;Shantay pass;41326 3 +3196 2842 0 3193 2842 0 Go-through;Shantay pass;41326 3 +3167 2819 0 3167 2816 0 Go-through;Shantay pass;41326 1 Shantay pass 3 +3168 2819 0 3168 2816 0 Go-through;Shantay pass;41326 1 Shantay pass 3 +3167 2816 0 3167 2819 0 Go-through;Shantay pass;41326 3 +3168 2816 0 3168 2819 0 Go-through;Shantay pass;41326 3 3322 3138 0 3322 3138 1 Climb-up;Ladder;21781 3322 3138 1 3322 3138 0 Climb-down;Ladder;21782 3312 3184 0 3314 3185 1 Climb-up;Staircase;16671