Jump to content
View in the app

A better way to browse. Learn more.

OSBot :: 2007 OSRS Botting

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

[Open Source] ~ Fanny Shafter ~ Fletches arrow shafts

Featured Replies

Never coded in Java before and first script!

It's a really simple one, but hopefully somebody gets some use out of it

Would really appreciate any feedback regards the code & methodology (but not planning to add features)

*Note: membership ran out before I got widgets working, so its kind of a guess whether it works or not... Would appreciate widget help tips... It can be modified to just wait a period of time and then press 1 or something to bypass widgets (thats how I used it since they seem difficult to use)

 

import java.util.List;

import org.osbot.rs07.api.ui.Skill;
import org.osbot.rs07.api.model.Item;
import org.osbot.rs07.api.ui.RS2Widget;
import org.osbot.rs07.script.Script;
import org.osbot.rs07.script.ScriptManifest;
import org.osbot.rs07.utility.ConditionalSleep;

@ScriptManifest(author = "Fanny", info = "Fletches arrow shafts and makes headless arrows", name = "Fanny Shafter", version = 1.0, logo = "")
public class FannyShafter extends Script {
    private long startTime;
    private int startLevel;
    private long startXp;

    private final String[] logTypes = {"Logs", "Oak logs", "Willow logs", "Maple logs", "Yew logs", "Magic logs"};
    private final int[] levelRequirements = {1, 15, 30, 45, 60, 75};
    private String logType;
    private long lastAnimationTime = 0;



    @Override
    public void onStart() {
        // onStart logic remains unchanged
    }

    @Override
    public int onLoop() throws InterruptedException {
        if (shouldFletch()) {
            fletchArrowShafts();
        }
        else if (shouldBank()) {
            handleBanking();
        }
        else {
            stop();
        }
        return random(200, 300); // Wait time before the loop starts over
    }

    private void handleBanking() throws InterruptedException {
        // Enhanced banking logic considering both logs and knife
        if (!getBank().isOpen() && !getBank().open()) return;
        new ConditionalSleep(5000, 1000) {
            @Override
            public boolean condition() throws InterruptedException {
                return getBank().isOpen();
            }
        }.sleep();

        if (!getInventory().contains("Knife") && getBank().contains("Knife")) {
            getBank().withdraw("Knife", 1);
        }
        else if(!getBank().contains("Knife") && !getInventory().contains("Knife")){
            stop();
        }

        for (String log : logTypes) {
            if (getBank().contains(log) && getSkills().getStatic(Skill.FLETCHING) >= getLevelRequirement(log)) {
                getBank().withdrawAll(log);
                break;
            }
        }

        getBank().close();
    }

    private boolean shouldBank() {
        // Check if player needs to bank
        return !hasRequiredItemsForFletching();
    }

    private boolean shouldFletch() {
        // Check if player can fletch
        return hasRequiredItemsForFletching();
    }

    private boolean hasRequiredItemsForFletching() {
        // Check if the inventory contains a knife and any of the log types
        if (!getInventory().contains("Knife")) {
            return false;
        }
        for (String log : logTypes) {
            if (getInventory().contains(log) && getSkills().getStatic(Skill.FLETCHING) >= getLevelRequirement(log)) {
                return true;
            }
        }
        return false;
    }

    private int getLevelRequirement(String logType) {
        // Return the level requirement for the given log type
        for (int i = 0; i < logTypes.length; i++) {
            if (logTypes[i].equals(logType)) {
                return levelRequirements[i];
            }
        }
        return 0; // Default to 0 if log type not found
    }

    private void fletchArrowShafts() throws InterruptedException {
        // Check bank is closed, then proceed to check logs & fletch
        if (getBank().isOpen()) closeBank();

        for (String log : logTypes) {
            if (getInventory().contains(log) && getSkills().getStatic(Skill.FLETCHING) >= getLevelRequirement(log)) {
                Item logToUse = getInventory().getItem(log);
                handleFletching(logToUse);
                break;
            }
        }
    }

    private void handleFletching(Item log) throws InterruptedException {
        // Check if log is not null
        if (log == null) {
            return;
        }

        boolean knifeUsedFirst = random(0, 2) == 0; // Randomly decide whether to use the knife or the log first

        if (knifeUsedFirst ? getInventory().interact("Use", "Knife") : log.interact("Use")) {
            new ConditionalSleep(2000, 500) {
                @Override
                public boolean condition() throws InterruptedException {
                    return getInventory().isItemSelected();
                }
            }.sleep();

            sleep(random(50, 300));
        }

        if (knifeUsedFirst ? log.interact("Use") : getInventory().interact("Use", "Knife")) {
            sleep(random(100,300));
        }
        
        new ConditionalSleep(5000, 1000) {
            @Override
            public boolean condition() throws InterruptedException {
                return isFletchingWidgetOpen();
            }
        }.sleep();

        sleep(random(100, 800));
        
        RS2Widget fletchingWidget = getFletchingWidget();

        if (fletchingWidget != null) {
            if (random(0, 100) < 25) {
                fletchingWidget.interact("15 arrow shafts");
            } else {
                getKeyboard().typeKey((char) 49);
            }
        }

        sleep(random(800,2500));

        getMouse().moveOutsideScreen();

        final long[] lastAnimated = {System.currentTimeMillis()};

        new ConditionalSleep(25000) { // Wait up to 25 seconds
            @Override
            public boolean condition() throws InterruptedException {
                // Check if the player is animating
                if(!getInventory().contains(logType)) {
                    return true;
                }
                else if (myPlayer().isAnimating()) {
                    // Update lastAnimated if the player is still animating
                    lastAnimated[0] = System.currentTimeMillis();
                }
                // Return true if more than 5 seconds have elapsed since lastAnimated
                return (System.currentTimeMillis() - lastAnimated[0]) > 5000;
            }
        }.sleep();

        // AFK returning to screen
        exponentialBackoffSleep();
    }

    private void exponentialBackoffSleep() throws InterruptedException{
        final int duration = random(1,3) * random(1,3) * random(1,3) * random(1, 3) * random(100, 300);
        sleep(duration);
    }
    private boolean isFletchingWidgetOpen(){
        if(getFletchingWidget() != null){
            return true;
        }
        return false;
    }
    private RS2Widget getFletchingWidget() {
        List<RS2Widget> allWidgets = getWidgets().getAll();
        RS2Widget storedWidget = allWidgets.stream().filter(w -> w.getWidth() == 52 && w.getHeight() == 52 && w.getItemId() == 52).findFirst().orElse(null);
        return storedWidget;
    }

    private void closeBank() throws InterruptedException {
        // Logic to close the bank
        getBank().close();
        new ConditionalSleep(3000, 1000) {
            @Override
            public boolean condition() throws InterruptedException {
                return !getBank().isOpen();
            }
        }.sleep();
    }
}

 

Edited by Fanny

Neat! What about adding an additional step to have headless arrowheads in inventory and use those as well?
Regarding the make widget I think the root id is 270. Also you can use "1" hotkey instead of clicking the widget since arrowheads are always the 1st option from the left.

Here is a example of how I get the widget then interact with it with spacebar. Same concept as "1" hotkey. Except spacebar repeats the last widget interaction used. 

https://github.com/YifanLi5/Bankstanding-Skiller/blob/cc8c13d063fd141125494fe3bf7643330d14a49d/src/Task/CombineItems.java#L86

 

Edited by yfoo

  • Author
3 hours ago, yfoo said:

Neat! What about adding an additional step to have headless arrowheads in inventory and use those as well?
Regarding the make widget I think the root id is 270. Also you can use "1" hotkey instead of clicking the widget since arrowheads are always the 1st option from the left.

Here is a example of how I get the widget then interact with it with spacebar. Same concept as "1" hotkey. Except spacebar repeats the last widget interaction used. 

https://github.com/YifanLi5/Bankstanding-Skiller/blob/cc8c13d063fd141125494fe3bf7643330d14a49d/src/Task/CombineItems.java#L86

 

Thanks a lot for the tips, when I get enough gold for membership again I'l ltry that!

Really appreciate the link; that randomGaussian method is a good idea for sleeps! Although I feel like I would add a base 50ms just because reaction time?

I was also not aware of a conditionalSleep2 method - what is the difference?

15 hours ago, Fanny said:

Thanks a lot for the tips, when I get enough gold for membership again I'l ltry that!

Really appreciate the link; that randomGaussian method is a good idea for sleeps! Although I feel like I would add a base 50ms just because reaction time?

I was also not aware of a conditionalSleep2 method - what is the difference?

>I was also not aware of a conditionalSleep2 method - what is the difference?

regular CS requires you to make an object to call sleep. CS2.sleep() is a static method. 

 

>randomGaussian method is a good idea for sleeps! Although I feel like I would add a base 50ms just because reaction time?

That's the point actually, I was using it to delay menu interactions by a small amount, like a human would. 

ex:)

sleep(randomGaussian(300, 100));
keyboard.pressKey(KeyEvent.VK_SPACE);
ScriptPaint.setStatus("Spacebar-ing make widget");
result = ConditionalSleep2.sleep(2000, () -> {
  List<RS2Widget> widgets = new ArrayList<>(getWidgets().containingActions(270, CREATE_VERBS));
  return widgets.isEmpty();
});
sleep(randomGaussian(300, 100));
keyboard.releaseKey(KeyEvent.VK_SPACE);

In this block, I wanted to wait a moment after the make widget appears before interacting with it. Then wait again after spacebar-ing it before releasing it. 

Edited by yfoo

Create an account or sign in to comment

Recently Browsing 0

  • No registered users viewing this page.

Account

Navigation

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.