Jump to content

New to scripting - Question about Woodcutting


barnold

Recommended Posts

Hi,

I am very new to the scripting scene, I have programming experience with other languages so I am quite familiar with programming logic.

I have gone through 2 tutorials to get a basic understanding of the structure of scripts and how to write a simple script but I am struggling a little bit with something.

The 2 tutorials for anyone thats interested:

Explv's tutorial:

Apaec's tutorial:

I found these both very useful.

 

I have written a very simple woodcutting script:

Spoiler

import org.osbot.rs07.api.map.Area;
import org.osbot.rs07.api.map.constants.Banks;
import org.osbot.rs07.api.ui.RS2Widget;
import org.osbot.rs07.script.MethodProvider;
import org.osbot.rs07.script.Script;
import org.osbot.rs07.api.model.Entity;
import org.osbot.rs07.script.ScriptManifest;
import org.osbot.rs07.utility.ConditionalSleep;
import org.osbot.rs07.api.map.constants.Banks;

@ScriptManifest(author = "Barnold", name = "TestScriptWC", info = "Basic script.", version = 0.1, logo = "")
public final class Woodcutter extends Script{

    private final Area treeArea = new Area(3006, 3415, 3018, 3398);

    @Override
    public void onStart() throws InterruptedException{
        log("test3");
        if(!haveAxe()){
            log("No axe found, stopping script!");
            stop(true);
        }
    }

    public enum State {
        CHOP, BANK, WALK;
    }

    private State getState() {
        Entity tree = objects.closest("Tree");

        if (bankNeeded())
            return State.BANK;

        if (tree != null)
            return State.CHOP;

        else
            return State.WALK;


    }

    @Override
    public final int onLoop() throws InterruptedException {
        switch (getState()){
            case BANK:
                bank();
                break;

            case CHOP:
                getWalking().webWalk(treeArea);

                Entity tree = objects.closest("Oak");

                if(tree.interact("Chop Down")) {
                    new ConditionalSleep(50000){
                        public boolean condition() throws InterruptedException {
                            return !tree.exists() || myPlayer().getAnimation() == -1;
                        }
                    }.sleep();
                    break;
                }

            case WALK:
                walkToArea();
                break;

        }
        return random(150,200);
    }


    private void walkToArea() {
        getWalking().webWalk(treeArea);

    }

    private void bank() throws InterruptedException {
        if(!Banks.FALADOR_WEST.contains(myPosition())){
            getWalking().webWalk(Banks.FALADOR_WEST);
        } else if (!getBank().isOpen()){
            getBank().open();
        } else if (!getInventory().isEmpty()){
            getBank().depositAll();
        } else {
            stop(true);
        }
    }


    private boolean haveAxe() {
        return getInventory().contains("Bronze Axe","Iron Axe") || getEquipment().contains("Bronze Axe","Iron Axe");
        }


    private boolean bankNeeded() {
        return getInventory().isFull();
    }
}

 

I am just a bit unsure how to handle something that happens in the script.

When my script finds a tree, it seems to spam click the tree whilst walking up to it until it actually begins chopping. I would like to prevent this from happening, but I am not sure how I achieve this.

If anyone could give me a clue on how I could achieve this, it would be massively appreciated.

 

Also - if anyone can see areas where I can improve me code please don't hesitate to tell me.

 

Thank you in advance.

Edited by barnold
Link to comment
Share on other sites

31 minutes ago, barnold said:

Hi,

I am very new to the scripting scene, I have programming experience with other languages so I am quite familiar with programming logic.

I have gone through 2 tutorials to get a basic understanding of the structure of scripts and how to write a simple script but I am struggling a little bit with something.

The 2 tutorials for anyone thats interested:

Explv's tutorial:

Apaec's tutorial:

I found these both very useful.

 

I have written a very simple woodcutting script:

  Reveal hidden contents


import org.osbot.rs07.api.map.Area;
import org.osbot.rs07.api.map.constants.Banks;
import org.osbot.rs07.api.ui.RS2Widget;
import org.osbot.rs07.script.MethodProvider;
import org.osbot.rs07.script.Script;
import org.osbot.rs07.api.model.Entity;
import org.osbot.rs07.script.ScriptManifest;
import org.osbot.rs07.utility.ConditionalSleep;
import org.osbot.rs07.api.map.constants.Banks;

@ScriptManifest(author = "Barnold", name = "TestScriptWC", info = "Basic script.", version = 0.1, logo = "")
public final class Woodcutter extends Script{

    private final Area treeArea = new Area(3006, 3415, 3018, 3398);

    @Override
    public void onStart() throws InterruptedException{
        log("test3");
        if(!haveAxe()){
            log("No axe found, stopping script!");
            stop(true);
        }
    }

    public enum State {
        CHOP, BANK, WALK;
    }

    private State getState() {
        Entity tree = objects.closest("Tree");

        if (bankNeeded())
            return State.BANK;

        if (tree != null)
            return State.CHOP;

        else
            return State.WALK;


    }

    @Override
    public final int onLoop() throws InterruptedException {
        switch (getState()){
            case BANK:
                bank();
                break;

            case CHOP:
                getWalking().webWalk(treeArea);

                Entity tree = objects.closest("Oak");

                if(tree.interact("Chop Down")) {
                    new ConditionalSleep(50000){
                        public boolean condition() throws InterruptedException {
                            return !tree.exists() || myPlayer().getAnimation() == -1;
                        }
                    }.sleep();
                    break;
                }

            case WALK:
                walkToArea();
                break;

        }
        return random(150,200);
    }


    private void walkToArea() {
        getWalking().webWalk(treeArea);

    }

    private void bank() throws InterruptedException {
        if(!Banks.FALADOR_WEST.contains(myPosition())){
            getWalking().webWalk(Banks.FALADOR_WEST);
        } else if (!getBank().isOpen()){
            getBank().open();
        } else if (!getInventory().isEmpty()){
            getBank().depositAll();
        } else {
            stop(true);
        }
    }


    private boolean haveAxe() {
        return getInventory().contains("Bronze Axe","Iron Axe") || getEquipment().contains("Bronze Axe","Iron Axe");
        }


    private boolean bankNeeded() {
        return getInventory().isFull();
    }
}

 

I am just a bit unsure how to handle something that happens in the script.

When my script finds a tree, it seems to spam click the tree whilst walking up to it until it actually begins chopping. I would like to prevent this from happening, but I am not sure how I achieve this.

If anyone could give me a clue on how I could achieve this, it would be massively appreciated.

 

Also - if anyone can see areas where I can improve me code please don't hesitate to tell me.

 

Thank you in advance.

Check if you are walking/moving before interacting with tree, i dont know what method is in osbot for that.... something like if !playersgetlocal.ismoving cut tree... so it will interact only if u stand

Link to comment
Share on other sites

3 minutes ago, mudrlantik said:

Check if you are walking/moving before interacting with tree, i dont know what method is in osbot for that.... something like if !playersgetlocal.ismoving cut tree... so it will interact only if u stand

 

Ah, thank you. I will try that.

 

I think it's myPlayer().isMoving()

so maybe I need something like if(!myPlayer().isMoving()){ //action }

 

Link to comment
Share on other sites

 Sleeping

In the previous section I showed you how to interact with the closest Tree to the player. However, if you tried that you would notice that the bot spam clicks on the Tree. This is where sleeping comes in.

There are three main ways to sleep in a script:

  1. Returning an integer value from the onLoop() method, this will result in the ScriptExecutor sleeping for the specified number of ms
  2. Calling the sleep(long ms) method in the MethodProvider class, note this method is static and so you can write: MethodProvider.sleep(long ms)
  3. Using a ConditionalSleep (sleep until a condition is matched, or until a specified timeout is reached)

Wherever possible I would recommend that you use ConditionalSleeps. You can use a ConditionalSleep like so:

Entity tree = getObjects().closest("Tree");
if (tree != null && tree.interact("Chop down")) {
      new ConditionalSleep(5000) {
        @Override
        public boolean condition() {
            return myPlayer().isAnimating() || !tree.exists();
        }
    }.sleep();
}

What this snippet does is:

  1. Get the closest Tree to the player
  2. Check the tree value is not null (because if it is null, no tree exists and so we don't wan't to do anything)
  3. Perform the "Chop down" interaction on the tree
  4. Check that the interact() method returned true, if the return value is false, the interaction failed and we do not want to sleep
  5. Create a new ConditionalSleep with timeout set to 5000ms (5 seconds)
  6. Override the ConditionalSleep's condition so that we stop sleeping before the timeout if either the player is animating (chopping the tree), or the tree no longer exists (someone else has chopped it down)
  7. Call the ConditionalSleep's sleep() method to start sleeping.

 

 

 

 

 

This is from @Explv tutorial

 

Edited by Shmeekz
Link to comment
Share on other sites

I wouldn't check any of this moving business, that sounds like over-complicating things, plus you cannot always guarantee that the player will move.

As it seems you've already tried, after the interaction method you should call a conditional sleep. It doesn't have to be anonymous, but for no good reason this one is:

tree.interact("Chop");
if (new ConditionalSleep(5000) {
    @Override
    public boolean condition() throws InterruptedException {
    	return myPlayer().isAnimating();
    }
  }.sleep()) {
// successfully chopping tree
} else {
// Timeout expired before player started animating.
}

While you don't need to put it in an if statement, it's just illustrating the idea. 

For the record, myPlayer().isAnimating() is presumably equivalent to comparing the animation id to -1, just more readable!

 

Edit: You should also take that break out of the interaction if statement! otherwise if you call chop and entity#interact returns false for whatever reason, you will slide straight into your walking state!

Edit 2: The code that i've given you will sleep after the interaction until the player starts chopping. This is fine, but i've noticed in your code you've smacked a massive 50,000ms (50sec) sleep in there after interacting with the tree. Long sleeps like this should be avoided as you're hindering the continuously looping nature of onLoop (also, what if the tree takes longer than 50s to cut? You'll click it again- doh!). Instead, I would recommend adding a new state. e.g IDLE, which is called while you are chopping the tree. (i.e when you don't need any of the other states). 

 

Hope that helps, let me know if you need any more help! The script is looking pretty nice and tidy!

-Apa

Edited by Apaec
Link to comment
Share on other sites

33 minutes ago, Apaec said:

I wouldn't check any of this moving business, that sounds like over-complicating things, plus you cannot always guarantee that the player will move.

As it seems you've already tried, after the interaction method you should call a conditional sleep. It doesn't have to be anonymous, but for no good reason this one is:


tree.interact("Chop");
if (new ConditionalSleep(5000) {
    @Override
    public boolean condition() throws InterruptedException {
    	return myPlayer().isAnimating();
    }
  }.sleep()) {
// successfully chopping tree
} else {
// Timeout expired before player started animating.
}

While you don't need to put it in an if statement, it's just illustrating the idea. 

For the record, myPlayer().isAnimating() is presumably equivalent to comparing the animation id to -1, just more readable!

 

Edit: You should also take that break out of the interaction if statement! otherwise if you call chop and entity#interact returns false for whatever reason, you will slide straight into your walking state!

Edit 2: The code that i've given you will sleep after the interaction until the player starts chopping. This is fine, but i've noticed in your code you've smacked a massive 50,000ms (50sec) sleep in there after interacting with the tree. Long sleeps like this should be avoided as you're hindering the continuously looping nature of onLoop (also, what if the tree takes longer than 50s to cut? You'll click it again- doh!). Instead, I would recommend adding a new state. e.g IDLE, which is called while you are chopping the tree. (i.e when you don't need any of the other states). 

 

Hope that helps, let me know if you need any more help! The script is looking pretty nice and tidy!

-Apa

Hey man, thank you for taking the time to type your reply.

I am unsure what you mean where you have said my conditional sleep doesn't have to be anonymous. What makes it anonymous? Sorry if these are newbie questions - still learning Java.

The 50,000 ms sleep was put there as I was having a problem chopping Oak trees. It was originally set to 10 seconds, but for some reason after the 10 seconds if the tree still existed it just clicked it again, rather than waiting until the tree was chopped down.

 

I will give your suggestions a go and see how I get on - again thank you for taking the time to give such a detailed response (and thank you for writing the original tutorial!)

Much appriciated.

Link to comment
Share on other sites

18 minutes ago, barnold said:

Hey man, thank you for taking the time to type your reply.

I am unsure what you mean where you have said my conditional sleep doesn't have to be anonymous. What makes it anonymous? Sorry if these are newbie questions - still learning Java.

The 50,000 ms sleep was put there as I was having a problem chopping Oak trees. It was originally set to 10 seconds, but for some reason after the 10 seconds if the tree still existed it just clicked it again, rather than waiting until the tree was chopped down.

 

I will give your suggestions a go and see how I get on - again thank you for taking the time to give such a detailed response (and thank you for writing the original tutorial!)

Much appriciated.

Hey (:

The reason it clicks again is the conditional sleep comes after the interaction method. A conditional sleep is exactly what it says - it will sleep until either the timeout elapses or the conditions are met, and it returns accordingly.

If you don't want this to happen, then take full advantage of the state based structure of the script and add in some more states! ...and make sure you maintain the looping nature of the onLoop.

When I said anonymous, I meant the conditional sleep was defined without a reference, i.e instead of defining something like 'ConditonalSleep cs = new ConditionalSleep ... ;' we just created the object anonymously and worked with that anonymous creation. If you're still confused, google should be able to help you on this one!

Apa

 

Link to comment
Share on other sites

9 minutes ago, Apaec said:

Hey (:

The reason it clicks again is the conditional sleep comes after the interaction method. A conditional sleep is exactly what it says - it will sleep until either the timeout elapses or the conditions are met, and it returns accordingly.

If you don't want this to happen, then take full advantage of the state based structure of the script and add in some more states! ...and make sure you maintain the looping nature of the onLoop.

When I said anonymous, I meant the conditional sleep was defined without a reference, i.e instead of defining something like 'ConditonalSleep cs = new ConditionalSleep ... ;' we just created the object anonymously and worked with that anonymous creation. If you're still confused, google should be able to help you on this one!

Apa

 

That clears things up. Just so you know, I implemented your suggestion and the script works perfectly now - no spam clicking or anything.

Ah ok that makes sense about the conditionalsleep being anonymous. Just a final quick one so I can fully understand what is happening with your orignial code (please correct me if I am wrong)

tree.interact("Chop"); //Clicks Chop to attempt to chop the tree
if (new ConditionalSleep(5000) { //Sleeps until my player begins chopping animation 
    @Override
    public boolean condition() throws InterruptedException {
    	return myPlayer().isAnimating();
    }
  }.sleep()) {
// successfully chopping tree // I then added another ConditionalSleep here - so that if my player has succesfully begun chopping it will wait until the tree has been cut.
} else {
// Timeout expired before player started animating. // I wasn't too sure what to add here - I didn't know what conditions would cause this code to execute. Would this get executed if my player fails to interact with the tree within the 5000 ms of the ConditionalSleep? 
}

 

Again thank you for your help - it has really cleared things up for me.

Link to comment
Share on other sites

1 hour ago, barnold said:

That clears things up. Just so you know, I implemented your suggestion and the script works perfectly now - no spam clicking or anything.

Ah ok that makes sense about the conditionalsleep being anonymous. Just a final quick one so I can fully understand what is happening with your orignial code (please correct me if I am wrong)


tree.interact("Chop"); //Clicks Chop to attempt to chop the tree
if (new ConditionalSleep(5000) { //Sleeps until my player begins chopping animation 
    @Override
    public boolean condition() throws InterruptedException {
    	return myPlayer().isAnimating();
    }
  }.sleep()) {
// successfully chopping tree // I then added another ConditionalSleep here - so that if my player has succesfully begun chopping it will wait until the tree has been cut.
} else {
// Timeout expired before player started animating. // I wasn't too sure what to add here - I didn't know what conditions would cause this code to execute. Would this get executed if my player fails to interact with the tree within the 5000 ms of the ConditionalSleep? 
}

 

Again thank you for your help - it has really cleared things up for me.

Hey

It seems you're misunderstanding a couple of things about what the code does here.

The ConditionalSleep#sleep() will sleep (the script will sit idle doing nothing) for as long as EITHER the condition provided evaluates to true, OR The timeout expires (set by the value provided in the constructor). It will then return a boolean value of whether or not the condition evaluated to true. (i.e sleep() returns condition()).

Since we're putting this sleep() method as the evaluation of an if statement, the if statement will be entered if the timeout did NOT expire (i.e the condition in the conditional sleep evaluated to true). Thus, the else statement will be entered if the timeout expired (i.e 5000ms passed and the player is still not animating). Your question of what to add in the else block is a good one - this is up to you. Perhaps you don't need the else block (after all I put it there just to demonstrate)?

The one thing I would like to re-iterate is that you don't want to break the looping nature of onLoop. This means long sleeps are a big no-no (while the script is sleeping, it is not looping, hence it is not running state checks!). Your plan to add one long sleep to wait until the tree is cut is probably not a good plan. As I suggested before, have a new state, perhaps named IDLE, which has a short sleep (or no sleep at all, relying on onLoop's return value) and is called while your player is cutting the tree (i.e animating). That way, you still sleep while the player is cutting the tree, and this sleeping is in essence conditional, however the script is still looping through the code and hence more aware of what is going on as custom checks can be run for other things (e.g bird nests, ...)

Perhaps what might remove some of the mystery for you is to create a wrapper method for this which takes it down to one line in your code, something like this:

private boolean sleepUntilAnimating(long timeout) {
	return new ConditionalSleep(timeout) {
    @Override
    public boolean condition() throws InterruptedException {
    	return myPlayer().isAnimating();
    }
  }.sleep());
}

and then you can just do

tree.interact("Chop");
if (sleepUntilAnimating(5000L)) {
	log ("We're animating!");
} else {
	log ("Hmm... 5 seconds elapsed but we're still not animating.");
}

Let me know if you're still unsure, it's not a simple topic by any means as there is quite alot going on due to the ConditionalSleep class being abstract (although this is probably necessary for custom implementations of ConditionalSleep).

  • Like 1
Link to comment
Share on other sites

2 hours ago, Apaec said:

Hey

It seems you're misunderstanding a couple of things about what the code does here.

The ConditionalSleep#sleep() will sleep (the script will sit idle doing nothing) for as long as EITHER the condition provided evaluates to true, OR The timeout expires (set by the value provided in the constructor). It will then return a boolean value of whether or not the condition evaluated to true. (i.e sleep() returns condition()).

Since we're putting this sleep() method as the evaluation of an if statement, the if statement will be entered if the timeout did NOT expire (i.e the condition in the conditional sleep evaluated to true). Thus, the else statement will be entered if the timeout expired (i.e 5000ms passed and the player is still not animating). Your question of what to add in the else block is a good one - this is up to you. Perhaps you don't need the else block (after all I put it there just to demonstrate)?

The one thing I would like to re-iterate is that you don't want to break the looping nature of onLoop. This means long sleeps are a big no-no (while the script is sleeping, it is not looping, hence it is not running state checks!). Your plan to add one long sleep to wait until the tree is cut is probably not a good plan. As I suggested before, have a new state, perhaps named IDLE, which has a short sleep (or no sleep at all, relying on onLoop's return value) and is called while your player is cutting the tree (i.e animating). That way, you still sleep while the player is cutting the tree, and this sleeping is in essence conditional, however the script is still looping through the code and hence more aware of what is going on as custom checks can be run for other things (e.g bird nests, ...)

Perhaps what might remove some of the mystery for you is to create a wrapper method for this which takes it down to one line in your code, something like this:


private boolean sleepUntilAnimating(long timeout) {
	return new ConditionalSleep(timeout) {
    @Override
    public boolean condition() throws InterruptedException {
    	return myPlayer().isAnimating();
    }
  }.sleep());
}

and then you can just do


tree.interact("Chop");
if (sleepUntilAnimating(5000L)) {
	log ("We're animating!");
} else {
	log ("Hmm... 5 seconds elapsed but we're still not animating.");
}

Let me know if you're still unsure, it's not a simple topic by any means as there is quite alot going on due to the ConditionalSleep class being abstract (although this is probably necessary for custom implementations of ConditionalSleep).

Right ok I think I understand it a bit better now. I will do some reading online and have a little mess about with some code to see if I properly understand it.

Once again thank you for taking the time to respond with such a detailed explanation, really appreciated.

  • Like 1
Link to comment
Share on other sites

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...