Jump to content

[Snippet] My Fairy Ring Handler


myOSBaccount

Recommended Posts

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 by myOSBaccount
  • Like 5
Link to comment
Share on other sites

  • myOSBaccount changed the title to [Snippet] My Fairy Ring Handler
  • 1 year later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...