myOSBaccount Posted November 14, 2020 Share Posted November 14, 2020 (edited) Decided that I don't want to use webwalking for fairy rings in a script of mine, so I made my own handler. This will efficiently use fairy rings. I gave it a few tests and didn't find any problems. If you do encounter any issue do tell me. Criticism welcome What it does do: Retrieve nearby fairy ring Check for staff equipped/diary completion Use last destination if possible Open fairy rings for configuration if needed Use the travel log to configure code, if the needed code is among the top (like favorited codes) Efficiently set the code using both the clockwise and the counter-clockwise rotators Confirm selection Return if teleported successfully (based on animation, not location. Can be easily changed) What it doesn't do: Check for specific fairy ring requirements Attempt to equip a staff, even if it's in the inventory Find you love Example: FairyRing fairyTravel = new FairyRing(getBot().getMethods()); /*some code ... */ fairyTravel.teleTo("DIS"); //If a fairy ring is nearby it will use it and teleport to the ring near Wizards' Tower //Alternatively: fairyTravel.teleTo(WIZARDSTOWER); // Will do the same as using the code "DIS" FairyRing preview: Spoiler import org.osbot.rs07.api.Diaries; import org.osbot.rs07.api.model.Entity; import org.osbot.rs07.api.model.Item; import org.osbot.rs07.api.ui.EquipmentSlot; import org.osbot.rs07.api.ui.RS2Widget; import org.osbot.rs07.script.MethodProvider; import java.util.Comparator; import java.util.HashMap; import java.util.List; import static org.osbot.rs07.script.MethodProvider.random; public class FairyRing { private final MethodProvider api; private final HashMap<String, Integer> indexByLetter; private final HashMap<Integer, String> letterByIndex; private final Comparator<RS2Widget> widgetXComparator = Comparator.comparingInt(RS2Widget::getAbsX); private final WidgetActionFilter leftFilter = new WidgetActionFilter("Rotate counter-clockwise"); private final WidgetActionFilter rightFilter = new WidgetActionFilter("Rotate clockwise"); private final WidgetActionFilter confirmFilter = new WidgetActionFilter("Confirm"); public FairyRing (MethodProvider api) { this.api = api; indexByLetter = new HashMap<>(); letterByIndex = new HashMap<>(); String[] letters = new String[]{"A", "D", "C", "B", "I", "L", "K", "J", "P", "S", "R", "Q"}; for (int i = 0; i < letters.length; i++) { indexByLetter.put(letters[i], i); letterByIndex.put(i, letters[i]); } } public boolean isOpen() { return api.getWidgets().singleFilter(leftFilter) != null; } public boolean open() { return getFairyRing().interact("Configure"); } private String getCurrentCode() { if (isOpen()) { return convertCodeIDToString(); } else { return null; } } public Entity getFairyRing() { Entity ring = api.getObjects().closest("Fairy ring"); if (ring != null && ring.isVisible()) { return ring; } else { return null; } } public boolean teleTo(Destination dest) { return teleTo(dest.code); } public boolean teleTo(String code) { Entity ring = getFairyRing(); if (ring != null && code != null && canUseRing()) { if (!isOpen()) { if (code.equals(Destination.ZANARIS.code)) { return teleToZanaris(); } else if (isLastDest(code)){ return teleToLastDest(); } else if (open()) { Sleep.sleepUntil(this::isOpen, random(7000, 10000)); } } if (isOpen()) { if (getCurrentCode() != null && !getCurrentCode().equals(code)) { configureCode(code); } if (getCurrentCode() != null && getCurrentCode().equals(code) && confirm()) { return waitForTele(); } } } return false; } private boolean canUseRing() { return isStaffEquipped() || isDiaryComplete(); } private boolean isDiaryComplete() { return api.getDiaries().isComplete(Diaries.Diary.LUMBRIDGE_ELITE); } private boolean isStaffEquipped() { Item weapon = api.getEquipment().getItemInSlot(EquipmentSlot.WEAPON.slot); if (weapon != null) { String weaponName = weapon.getName(); return weaponName.equals("Dramen staff") || weaponName.equals("Lunar staff"); } else { return false; } } private boolean confirm() { RS2Widget confirmButton = api.getWidgets().singleFilter(confirmFilter); return confirmButton != null && confirmButton.interact("Confirm"); } private void configureCode(String code) { RS2Widget reuseWidg = getCodeFromLog(addSpaces(code)); if (reuseWidg != null) { if (reuseWidg.interact("Use code")) { Sleep.sleepUntil(() -> isCodeSet(code), random(1000, 2000)); } } else { for (int i = 0; i < 3; i++) { String currentLetter = getLetterAtIndex(i); String wantedLetter = ""+code.charAt(i); if (!currentLetter.equals(wantedLetter)) { int rotations = 2 + (getLetterMapIndex(currentLetter) - getLetterMapIndex(wantedLetter)); if (rotations == 0) { rotations = (random(2) == 1? 2 : -2); } rotateBy(i, rotations, code); } } Sleep.sleepUntil(() -> isCodeSet(code), random(500, 1000)); } } private RS2Widget getCodeFromLog(String code) { RS2Widget reuseWidg = api.getWidgets().getWidgetContainingText(code); if (reuseWidg != null && canClickCode(reuseWidg)) { return reuseWidg; } else { return null; } } private void rotateBy(int letterIndex, int amount, String code) { WidgetActionFilter sideFilter = (amount > 0 ? rightFilter : leftFilter); amount = Math.abs(amount); RS2Widget rotator = getRotator(letterIndex, sideFilter); if (rotator != null) { for (int i=0; i<amount; i++) { if (rotator.interact(sideFilter.getAction())) { Sleep.sleepUntil(() -> isCodeSet(code), random(100, 200)); } } } } private RS2Widget getRotator(int letterIndex, WidgetActionFilter sideFilter) { List<RS2Widget> rotators = api.getWidgets().filter(sideFilter); if (rotators != null && letterIndex < rotators.size()) { rotators.sort(widgetXComparator); return rotators.get(letterIndex); } else { return null; } } private String addSpaces(String code) { return code.replace("", " ").trim(); } private boolean canClickCode(RS2Widget reuseWidg) { RS2Widget logWidg = api.getWidgets().getWidgetContainingText("Travel log"); int minY = logWidg.getAbsY() + logWidg.getHeight(); int maxY = minY + 200; int minX = logWidg.getAbsX(); int codeY = reuseWidg.getAbsY(); return codeY >= minY && codeY <= maxY && reuseWidg.getAbsX() >= minX; } private boolean isCodeSet(String code) { return getCurrentCode() != null && getCurrentCode().equals(code); } private boolean isLastDest(String code) { return getLastDest() != null && getLastDest().contains(code); } private boolean teleToLastDest() { Entity ring = getFairyRing(); if (ring.interact(getLastDest())) { return waitForTele(); } else { return false; } } private String getLastDest() { Entity ring = getFairyRing(); for (String action : ring.getActions()) { if (action != null && action.length() > 0 && action.contains("Last-destination (")) { return action; } } return null; } private boolean waitForTele() { boolean startedTele = false; Sleep.sleepUntil(() -> api.myPlayer().getAnimation() == 3265, random(3000, 5000)); if (api.myPlayer().getAnimation() == 3265) { startedTele = true; Sleep.sleepUntil(() -> api.myPlayer().getAnimation() == -1, random(7000, 10000)); } return startedTele; } private boolean teleToZanaris() { Entity ring = api.getObjects().closest("Fairy ring"); if (ring.hasAction("Zanaris")) { if (ring.interact("Zanaris")) { Sleep.sleepUntil(this::isNearFixit, random(7000, 10000)); } } return isNearFixit(); } public boolean isNearFixit() { return api.getNpcs().closest("Fairy Fixit") != null; } private String convertCodeIDToString() { return convertCodeIDToString(api.getConfigs().get(816)); } private String convertCodeIDToString(int codeID) { StringBuilder code = new StringBuilder(); for (int i=0; i<3; i++) { code.append(getLetterAtIndex(i, codeID)); } return code.toString(); } private String getLetterAtIndex(int index) { return getLetterAtIndex(index, api.getConfigs().get(816)); } private String getLetterAtIndex(int index, int codeID) { return letterByIndex.get(4*index + ((codeID / (int) Math.pow(4, index)) % 4)); } private int getLetterMapIndex(String letter) { return indexByLetter.get(letter); } } enum Destination { MUDSKIPPER("AIQ"), ARDOUGNEISLAND("AIR"), DORGESHKAAN("AJQ"), RELLEKACAVE("AJR"), PENGUINS("AJS"), PISCATORIS("AKQ"), FELDIPHUNTER("AKS"), LIGHTHOUSE("ALP"), HAUNTEDWOODS("ALQ"), ABYSSALAREA("ALR"), MCGRUBOR("ALS"), MORTMYREISLAND("BIP"), KALPHITELAIR("BIQ"), ARDOUGNEZOO("BIS"), FISHERKING("BJR"), ZULANDRA("BJS"), CASTLEWARSSOUTH("BKP"), ENCHANTEDVALLEY("BKQ"), MORTMYRESWAMP("BKR"), ZANARIS("BKS"), TZHAAR("BLP"), LEGENDSGUILD("BLR"), MISCELLANIA("CIP"), YANILLE("CIQ"), MOUNTKARUULM("CIR"), ARCEUSSLIBRARY("CIS"), SINCLAIRMANSION("CJR"), COSMICENTITY("CKP"), TAIBWOWANNAISOUTH("CKR"), CANIFIS("CKS"), DRAYNORISLAND("CLP"), APEATOLL("CLR"), HAZELMEREHOME("CLS"), ABYSSALNEXUS("DIP"), PLAYERGARDEN("DIQ"), GORAKSPLANE("DIR"), WIZARDSTOWER("DIS"), TOWEROFLIFE("DJP"), CHASMOFFIRE("DJR"), MUSAPOINTSOUTH("DKP"), EDGEVILLE("DKR"), POLARAREA("DKS"), NARDAHNORTH("DLQ"), POISONWASTE("DLR"), MYREQUEHIDEOUT("DLS"); public String code; Destination(String code) { this.code = code; } } Sleep (made not by me) preview: Spoiler import org.osbot.rs07.utility.ConditionalSleep; import java.util.function.BooleanSupplier; class Sleep extends ConditionalSleep { private final BooleanSupplier condition; public Sleep(final BooleanSupplier condition, final int timeout) { super(timeout); this.condition = condition; } @Override public final boolean condition() { return condition.getAsBoolean(); } public static boolean sleepUntil(final BooleanSupplier condition, final int timeout) { return new Sleep(condition, timeout).sleep(); } } WidgetActionFilter preview: Spoiler import org.osbot.rs07.api.filter.Filter; import org.osbot.rs07.api.ui.RS2Widget; public class WidgetActionFilter implements Filter<RS2Widget> { private final String action; public WidgetActionFilter(String filter) { this.action = filter; } @Override public boolean match(RS2Widget widg) { if (widg == null) { return false; } else if (widg.getInteractActions() == null) { return false; } else { for (String action : widg.getInteractActions()) { if (action == null || action.length() < 1 || this.action == null) { return false; } else if (action.toLowerCase().contains(this.action.toLowerCase())) { return true; } } return false; } } public String getAction() { return action; } } Edited November 24, 2020 by myOSBaccount 5 Quote Link to comment Share on other sites More sharing options...
Lol_marcus Posted November 14, 2020 Share Posted November 14, 2020 That's awesome. Thanks for the snippet. 1 Quote Link to comment Share on other sites More sharing options...
Deshanti Posted August 11, 2022 Share Posted August 11, 2022 Thanks for posting this, very nice addition to my scripts Quote Link to comment Share on other sites More sharing options...