Jump to content
×
×
  • Create New...

[TUTORIAL] Your first script - Giant Slayer Part 1 (UPDATED)


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. 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) Stream API (new API)

A quick word about the new API that PowBot is using. It's been made in the likeness of Java Stream API so if you don't already know about it I'd advice you to read about it first (Java Stream Tutorial).

So how the API works is you use ClientContext to get a "stream" of ground objects, NPCs, inventory items (or other objects that you want to manipulate is some way) and then you use methods on that stream to sort, filter, map or do whatever you want to do, to end up with the desired object(s).

Most of the stream operations are intermediate. That means that they return a (usually) changed stream (e.g. sorted by some predicate) . And that is pretty cool because we can then chain many different operations on the same stream without creating new variables.

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

ctx.npcs.toStream().name("Hill Giant").nearest().first();
  • most of the api calls will start with "ctx.typeOfStream.toStream()", now you have a stream of objects that you can manipulate
  • so ctx.npcs.toStream() returns all npcs around us (that are loaded), as a stream
  • .name("Hill Giants") filters only npcs with a given name
  • .nearest() sorts all npcs by distance to player (low to high)
  • so now we have a stream of npcs we need to actually get the first one in the stream (closest to player), and for that we use .first()

 

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

You might have to import the Npc class.

public void attack() {
  	// First we find the npc
	Npc giant = ctx.npcs.toStream().name("Hill Giant").nearest().first();
  	// Check if it's valid and we're not already attacking him
  	if (giant.valid() && !giant.interacting().equals(ctx.players.local())) {
	 	giant.interact("Attack");
    }
}
  • We use .valid() to check if we actually got an npc from the stream (maybe there are no giants around?)
  • .interacting() is a method of actors (npcs, players) that returns an actor that is interacting with our actor (in this case Hill Giant). So we check if that giant is not already interacting with our character

 

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 (or wait). Conditional wait  is your best friend. You will use it pretty much everywhere.

Condition.wait(Condition) will wait until you have met the condition (limited by number of tries and frequency.

It will return true if we're interacting with an 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.toStream().name("Hill Giant").nearest().first();
  	// 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.toStream().id(FOOD_ID).first();
  	// 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 (hope we don't die)
        if (ctx.inventory.toStream().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.

gs1final.png.576e96f7d6fc62db6376238d892de0fc.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!

Edited by Toma
  • Like 6
  • 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

Awesome tutorial, thanks. For some reason when I edit my configs it says it cannot find the jagexappletviewer class, even though it's right there at the base of the imported powbot.jar file. It's working either way but still lol, wonder why.

Link to post
Share on other sites
  • Toma changed the title to [TUTORIAL] Your first script - Giant Slayer Part 1 (UPDATED)
  • 1 month later...

Have this issue on the first time running it this is the current code i got sofar.

 

 

package giantslayer;


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

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

public class GiantSlayer extends PollingScript<ClientContext> {

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

    @Override
    public void poll() {

    }
}

 

 

[AWT-EventQueue-0] INFO z.Ta - This script will stop after 1 hours as your account is limited.
[AWT-EventQueue-0] INFO z.Ta - To run local scripts for longer apply to be a script writer - https://powbot.org/community/index.php?/topic/43-get-your-script-repo
[DefaultDispatcher-worker-1] WARN z.LPT7 - Unhandled definition opcode with id: 18.
[DefaultDispatcher-worker-1] WARN z.LPT7 - Unhandled definition opcode with id: 64.
[DefaultDispatcher-worker-1] WARN z.LPT7 - Unhandled definition opcode with id: 52.
[DefaultDispatcher-worker-1] WARN z.LPT7 - Unhandled definition opcode with id: 173.
[DefaultDispatcher-worker-1] WARN z.LPT7 - Unhandled definition opcode with id: 212.
[DefaultDispatcher-worker-1] WARN z.LPT7 - Unhandled definition opcode with id: 51.
[DefaultDispatcher-worker-1] WARN z.LPT7 - Unhandled definition opcode with id: 18.
[DefaultDispatcher-worker-1] WARN z.LPT7 - Unhandled definition opcode with id: 64.
[DefaultDispatcher-worker-1] WARN z.LPT7 - Unhandled definition opcode with id: 52.
[DefaultDispatcher-worker-1] WARN z.LPT7 - Unhandled definition opcode with id: 173.
[DefaultDispatcher-worker-1] WARN z.LPT7 - Unhandled definition opcode with id: 212.
[DefaultDispatcher-worker-1] WARN z.LPT7 - Unhandled definition opcode with id: 51.
[DefaultDispatcher-worker-5] WARN z.LPT7 - Unhandled definition opcode with id: 18.
[DefaultDispatcher-worker-5] WARN z.LPT7 - Unhandled definition opcode with id: 3.
[DefaultDispatcher-worker-5] WARN z.LPT7 - Unhandled definition opcode with id: 222.
[DefaultDispatcher-worker-5] WARN z.LPT7 - Unhandled definition opcode with id: 18.
[DefaultDispatcher-worker-5] WARN z.LPT7 - Unhandled definition opcode with id: 8.
[DefaultDispatcher-worker-5] WARN z.LPT7 - Unhandled definition opcode with id: 18.
[DefaultDispatcher-worker-5] WARN z.LPT7 - Unhandled definition opcode with id: 18.
[DefaultDispatcher-worker-5] WARN z.LPT7 - Unhandled definition opcode with id: 3.
[DefaultDispatcher-worker-5] WARN z.LPT7 - Unhandled definition opcode with id: 117.
[DefaultDispatcher-worker-5] WARN z.LPT7 - Unhandled definition opcode with id: 18.
[DefaultDispatcher-worker-5] WARN z.LPT7 - Unhandled definition opcode with id: 24.
[DefaultDispatcher-worker-5] WARN z.LPT7 - Unhandled definition opcode with id: 18.
[DefaultDispatcher-worker-5] WARN z.LPT7 - Unhandled definition opcode with id: 3.
[DefaultDispatcher-worker-5] WARN z.LPT7 - Unhandled definition opcode with id: 154.
 

Link to post
Share on other sites
1 hour ago, aisu said:

Have this issue on the first time running it this is the current code i got sofar.

[AWT-EventQueue-0] INFO z.Ta - This script will stop after 1 hours as your account is limited.
[AWT-EventQueue-0] INFO z.Ta - To run local scripts for longer apply to be a script writer - https://powbot.org/community/index.php?/topic/43-get-your-script-repo
[DefaultDispatcher-worker-1] WARN z.LPT7 - Unhandled definition opcode with id: 18.
[DefaultDispatcher-worker-1] WARN z.LPT7 - Unhandled definition opcode with id: 64.
[DefaultDispatcher-worker-1] WARN z.LPT7 - Unhandled definition opcode with id: 52.
[DefaultDispatcher-worker-1] WARN z.LPT7 - Unhandled definition opcode with id: 173.
[DefaultDispatcher-worker-1] WARN z.LPT7 - Unhandled definition opcode with id: 212.
[DefaultDispatcher-worker-1] WARN z.LPT7 - Unhandled definition opcode with id: 51.
[DefaultDispatcher-worker-1] WARN z.LPT7 - Unhandled definition opcode with id: 18.
[DefaultDispatcher-worker-1] WARN z.LPT7 - Unhandled definition opcode with id: 64.
[DefaultDispatcher-worker-1] WARN z.LPT7 - Unhandled definition opcode with id: 52.
...

...

Hey. Those are just warnings so you can ignore them. Nothing to do with the script.

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.