Jump to content
×
×
  • Create New...

[TUTORIAL] Your first script - Giant Slayer Part 1


Recommended Posts

 

1) Intro

This is the first part of my Hill Giants tutorial script. The focus will be on making a working F2P hill giants script first, and then adding p2p functionalities (ex. potions).

I will assume that you have successfully finished setting up your local script repository. If not, please go do that first.

I will also assume that you have SOME experience in Java/programming. I won't be explaining every little detail.

Create new project and set it up. I named it "GiantSlayer".

First of all, further down the line, when you will creating scripts yourself, if you don't know how classes connect or if you wanna see if there's a certain method in the API, you can always

look at the powbot api documentation - https://powbot.org/docs/

Also, there's a powbot discord where there's a lot of nice people that are ready to help you with any problem that you might have - https://discord.com/invite/aBG7YqtXeF

So with that out of the way, lets start.

2) Creating a base class

First, lets create a package. Right click "src" folder and create new package. I will name it "toma". Now right click on the new package/folder and create another package with the name of your script.

I named it "giantslayer".

createPackage.png

Create new java class in that package and name it "GiantSlayer".

createClass.png

You should have a new empty class. Lets extend our class with a custom powbot class PollingScript.

extendPollingEmpty.png

 

PollingScript  gives us a basic game loop through poll() method which is called every frame while the game is running. It also gives us access to ClientContext  through a variable ctx.

ClientContext  is a class that gives us access to all of the API classes, pretty much everything we need to interact with the game; be it bank, inventory, skills, magic, npcs, etc. 

 

TIP: In IntelliJ , if you see that a class name or variable is colored red that probably means that you're missing an import or that you need to implement required methods. You can easily do that by hovering over said name and clicking "alt+enter", a context menu will open from which you can choose to import a missing class or implement methods.

Add missing imports

 

missingImports.png

You can also add additional overridden method "public void start()". Override simply means that we're replacing or overriding the method that was defined in the parent class (PollingScript).

Method start() is called only once when script is started, while poll() gets called every frame.  Everything that needs to be defined when the script starts will be defined in start().

Ok, so now we have this.

finalSkeleton.png

Before we continue lets also add Script.Manifest so we can actually run the script. Add the following code right before the class definition (under imports)

@Script.Manifest(
        name = "GiantSlayer",
        description = "Kills Hill Giants",
        version = "0.0.1"
)

addManifest.png
 

Now lets create a basic attack and heal functionality.

3) Query API

But first lets briefly touch upon the Query system of the powbot api.

There's a few different queries but the general syntax is pretty much the same. The whole point is that every method that is done on the query will return the modified query. That means you can chain lots of methods on the same query, instead of calling them separately. 

So if we want to find the nearest npc with the name "Hill Giant" we would do this:

ctx.npcs.select().name("Hill Giant").nearest().poll()

- select() will refresh or rather repopulate the query (in our case with npcs)

- name("Hill Giant") will remove every npc that doesn't contain that name

- nearest() will sort npcs by distance to player

- poll() gives you a head of the query (list) AND it will remove it (npc) from the query (list), so in our case it will be the closest Hill Giant npc (there's also peek(), which doesn't remove anything)

Its important to note that the contents of query are persistent, they will not change until you call .select() again , not to be confused with select(Filter<k>),  this one will not reset the query.

Also,  poll() and peek() will never return null. If the query is empty they will return empty entity. If you want to check if the returned entity is valid you should use entity.valid(), rather than null-checking.

 

4) Attack and heal functionality

4.1) Attack

Alright, lets finally implement attack and heal.

Create a new method private void attack() in GiantSlayer class.

First, we need to actually find an npc to attack. For that we will of course use our query api.

Find the closest npc, check if it's valid, and then attack, but lets also check if we are not already interacting with this npc.

public void attack() {
  	// First we find the npc
	Npc giant = ctx.npcs.select().name("Hill Giant").nearest().poll();
  	// Check if it's valid and we're not already attacking him
  	if (giant.valid() && !giant.interacting().equals(ctx.players.local())) {
	 	
	 	giant.interact("Attack");
    }
}

You might have to import the Npc class.

You can now call attack() function in poll().

Compile + run the program. You should be able to see the script in File->Run.

runScript.png

Alright, it's kinda working, but it spam clicks a bit too much before it gets into combat. Lets add a conditional sleep.

It will return true if we're interacting with the npc. First number (450) says how long to wait before it calls the condition aka frequency ( in ms), and the second one is the number of tries  ( 3 in our case).

So it will wait for the maximum of 450x3 = 1350 ms = 1.35 seconds before it continues.

public void attack() {
  	// First we find the npc
	Npc giant = ctx.npcs.select().name("Hill Giant").nearest().poll();
  	// Check if it's valid and we're not already attacking him
  	if (giant.valid() && !giant.interacting().equals(ctx.players.local())) {
	 	// Attack the giant
	 	giant.interact("Attack");      
      	// Wait until we have started interacting
        Condition.wait(new Callable<Boolean>() {
        	@Override
        	public Boolean call() throws Exception {
            		return giant.interacting().equals(ctx.players.local());
                }
            },450,3);
    	}
}

Ok, so now it's not spamming as much. Lets implement healing.

4.2) Heal

Create a new fuction private void heal()

Before you do anything else create a new int variable in the GiantSlayer class. We will need it for the food id.

private final static int FOOD_ID = 361;

361 is the ID of "Tuna". To find item IDs you must have them in the inventory and then go to View->Items.

viewItems.png

Ok, now add this code to the function. We will also use Condition.wait(), but this time we will wait for script to click on the item, we can use it like that because food.interact() returns a boolean (true if clicked, false otherwise)

private void heal() {
  		// Find food in inventory
        Item food = ctx.inventory.select().id(FOOD_ID).poll();
		// Wait until we have successfully clicked on the item
        Condition.wait(new Callable<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                return food.interact("Eat");
            }
        }, 350, 4);
    }

 

4.3)  Combining heal and attack

Now when we're done with the heal() function we can integrate everything together in the poll() function.

First we check if we're out of food. If we are, stop the script. 

Then we check if health percentage is lower than some value (60% in this case). If it is, call heal() function, if not call attack() function.

public void poll() {
        // First check if there's any food, if there's not, stop the script
        if (ctx.inventory.select().id(FOOD_ID).isEmpty()) {
            ctx.controller.stop();
        }

        // If our health is under 60% we should heal
        if (ctx.players.local().healthPercent() <= 60) {
            heal();
        }
        // Otherwise we can  attack
        else {
            attack();
        }
    }

This is the final script.

finalScript.png

Now compile and run the script. It should now attack the nearest giant, switch to another one when the current one dies and heal when low on health.

 

Congratulations if you have managed to successfully complete everything till now. This is gonna be it for this part.

In the next part we will restructure our script in a better way, refine existing functionalities and add new ones.

Thanks for reading.  See you in the next part!

  • Like 5
  • Thanks 5
Link to post
Share on other sites
  • Dan changed the title to [TUTORIAL] Your first script - Giant Slayer Part 1
  • 1 month later...
  • 4 weeks later...
  • 1 month later...

Hi Toma. I just got into scripting for PowerBot after viewing your post.

I saw a couple of things didn't go right for initially with following this tutorial.
It took a bit of searching but I seen that the Query API was deprecated after this tutorial was launched, and was superseded by Stream API.

I got this code right now, in which I get the following error that I don't know how to fix:

package Selirium.GoblinSlayer;

import org.powerbot.script.PollingScript;
import org.powerbot.script.Script;
import org.powerbot.script.rt4.ClientContext;
import org.powerbot.script.rt4.Npc;

@Script.Manifest(
        name = "Goblin Slayer",
        description = "Slays Goblins",
        version = "0.0.1"
)

public class GoblinSlayer extends PollingScript<ClientContext> {

    @Override
    public void start() {

    }

    @Override
    public void poll() {

    }

    public void attack() {
        // First we find the npc
        //Npc giant = ctx.npcs.select().name("Hill Giant").nearest().poll(); > Does not work anymore. Deprecated by Stream API.
        Npc goblin = ctx.npcs.toStream().within(4).name("Goblin").nearest().findFirst();

        // Check if it's valid and we're not already attacking him
        if (goblin.valid() && !goblin.interacting().equals(ctx.players.local())) {

            goblin.interact("Attack");
        }
    }
}

Error:
Incompatible types. Found: 'java.util.Optional<org.powerbot.script.rt4.Npc>', required: 'org.powerbot.script.rt4.Npc'

If I try the provided solution, which is to set the goblin variable to a "Optional<Npc>" variable, it cannot resolve the methods in line 34 (e.g. goblin.valid(), interacting() and interact()).

Link to post
Share on other sites
  • Administrators
1 hour ago, Selirium said:

Hi Toma. I just got into scripting for PowerBot after viewing your post.

I saw a couple of things didn't go right for initially with following this tutorial.
It took a bit of searching but I seen that the Query API was deprecated after this tutorial was launched, and was superseded by Stream API.

I got this code right now, in which I get the following error that I don't know how to fix:

package Selirium.GoblinSlayer;

import org.powerbot.script.PollingScript;
import org.powerbot.script.Script;
import org.powerbot.script.rt4.ClientContext;
import org.powerbot.script.rt4.Npc;

@Script.Manifest(
        name = "Goblin Slayer",
        description = "Slays Goblins",
        version = "0.0.1"
)

public class GoblinSlayer extends PollingScript<ClientContext> {

    @Override
    public void start() {

    }

    @Override
    public void poll() {

    }

    public void attack() {
        // First we find the npc
        //Npc giant = ctx.npcs.select().name("Hill Giant").nearest().poll(); > Does not work anymore. Deprecated by Stream API.
        Npc goblin = ctx.npcs.toStream().within(4).name("Goblin").nearest().findFirst();

        // Check if it's valid and we're not already attacking him
        if (goblin.valid() && !goblin.interacting().equals(ctx.players.local())) {

            goblin.interact("Attack");
        }
    }
}

Error:
Incompatible types. Found: 'java.util.Optional<org.powerbot.script.rt4.Npc>', required: 'org.powerbot.script.rt4.Npc'

If I try the provided solution, which is to set the goblin variable to a "Optional<Npc>" variable, it cannot resolve the methods in line 34 (e.g. goblin.valid(), interacting() and interact()).

        Npc goblin = ctx.npcs.toStream().within(4).name("Goblin").nearest().first();

Will give you what you want.

  • Like 1
Link to post
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.

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.