Jump to content
Frequently Asked Questions
  • Are you not able to open the client? Try following our getting started guide
  • Still not working? Try downloading and running JarFix
  • Help! My bot doesn't do anything! Enable fresh start in client settings and restart the client
  • How to purchase with PayPal/OSRS/Crypto gold? You can purchase vouchers from other users
  • GraveDigger - Loot/Bury/Bank Bones [Open Source]


    Nucleus

    Recommended Posts

    Hey everyone, I'm new to scripting and I've been exploring the Dreambot API. I decided to make a basic script that loots and buries or banks bones in Lumbridge, specifically the cow pen beside the Al-Kharid gates.

    The script is pending approval on the SDN, here is the topic:

    I've decided to share the source (Attached to post) since it's a basic script and it could be used as a learning resource for beginners interested in learning how to script. I would appreciate any feedback from scripters, whether there is a more efficient way to do something, if I'm using the API methods incorrectly, or whatever really.

    I'll explain my thought process for certain functions, if anyone wants clarification for a certain part of the script feel free to ask and ill add the clarification/explanation to this post.

    API:

    Drone.java

    findBones:

    public List<GroundItem> findBones(LootArea area, LootItems li){
            List<GroundItem> g = ctx.getGroundItems().all(item -> item.getName().contains(li.getName()));
    
            g.removeIf(gi -> !area.getArea().contains(gi));
            g.sort(new SortByDistance());
    
            return g;
    }

    The findBones function takes a LootArea variable and a LootItems variable as arguments and returns a List of GroundItems.

    findBones finds items in a given area and returns a List of items sorted in ascending order by distance to the player.

    1. The List<GroundItem> is generated by calling the getGroundItems.all() method and uses a lambda expression to get the name of the item we're searching for, stored in the LootItems variable li. This method call gets a List of all the ground items with the name "Bones"
    2. The Java Collections removeIf method is used to remove all items in the list that is not within the search area. This call uses a lambda expression which checks if the ground item gi is in the area given by the LootArea variable area
    3. The Java Collections sort method is used to sort the list in ascending order by distance to the player.

    SortByDistance:

    private class SortByDistance implements Comparator<GroundItem>{
            public int compare(GroundItem a, GroundItem b){
    
                Tile groundA = new Tile(a.getX(), a.getY(), a.getZ());
                Tile groundB = new Tile(b.getX(), b.getY(), b.getZ());
    
                double distA = ctx.getLocalPlayer().distance(groundA);
                double distB = ctx.getLocalPlayer().distance(groundB);
    
                long dA = Math.round(distA);
                long dB = Math.round(distB);
                int daInt = 0;
                int dbInt = 0;
    
                try{
                    daInt = Math.toIntExact(dA);
                    dbInt = Math.toIntExact(dB);
                }
                catch(ArithmeticException e){
                    e.printStackTrace();
                }
    
                return daInt-dbInt;
            }
    }

    SortByDistance implements the Java Comparator class.

    Compares two GroundItems a, and b, and gets their distance to the player. Used to sort a list of GroundItems in ascending order by distance.

    1. Gets the tiles of both GroundItems
    2. Stores the distance to the player in double variables distA, distB. NOTE: Since the function compare requires a variable of type int to be returned, we can't return the distance as a double. So we have to cast the value to an int. So how do we cast a double to an int? How do you get a whole number when you're given a number like 2.55? Round it.
    3. We see the Java method Math.round() returns a number with the long primitive, this makes sense since the int primitive can only store a number within this range [-2147483648 , 2147483647]. However a double can store a number much greater than this range. So we store the value of the rounded double in a long variable and cast it to an integer using the method toIntExact(). This method can throw an arithmetic exception (very unlikely but better to be safe) so it must be placed in a try catch block.

    Data:

    LootArea: a simple enumerator for accessing Areas to be used by the script

    LootItems: a simple enumerator for accessing items that the script should loot.

    Nodes:

    PickUp:

        @Override
        public boolean accept(){
            return !getInventory().isFull()
                    && c.getDrone().isInLootArea();
        }

    The accept parameters for the PickUp node. Why do I use the method isInLootArea instead of simply checking if the player is in the area? During testing I noticed whenever the script was picking up bones the client would lag heavily for a few seconds. I realized that checking if the players current tile is one of the tiles in a fairly large area (the entire pen probably consists of 150+ tiles) is fairly intensive on the CPU. I realized I could just have the script sleep for longer so it wouldn't loop through the nodes as quickly but I decided to use a boolean check instead. The node checks a boolean value which is toggled when the player enters the loot area.

    Pick Up Code:

    List<GroundItem> b = c.getDrone().findBones(LootArea.FULL_PEN, LootItems.BONES);
    
    GroundItem bones = b.get(0);
    
    if (bones != null) {
        if(bones.interact("Take")) {
            sleepWhile(() -> bones.exists(), 2000);
            sleepWhile(() -> c.getLocalPlayer().isMoving(), 20000);
            if(guardian.hasChanged()){
                c.getDrone().addBonesLooted();
            }
        }
    }
    1. Finds all the bones available in the FULL_PEN area and sorts the list by distance to the player in ascending order.
    2. Store the closest bones in the GroundItem variable bones
    3. Null check the bones GroundItem
    4. If the bones GroundItem is clicked then sleep while the bones still exist or until 2 seconds passed. Then sleep while the player is moving or for a maximum 20 seconds (So the script doesn't spam click the bones)
    5. Uses an InventoryMonitor to check if the player has picked up the bones. (Used for paint purposes)

     

    GDSource.zip

    Link to comment
    Share on other sites

    For your Comparator, you could just the built in method Double#compare, instead of rounding and casting the double to an int. You could also use a lambda here instead of creating a new class.

    g.sort((GroundItem a, GroundItem b)-> Double.compare(ctx.getLocalPlayer().distance(a), ctx.getLocalPlayer().distance(b)));

    Also, for your list of ground items, why not just add the area check to the filter? 

    List<GroundItem> g = ctx.getGroundItems().all(item -> item.getName().contains(li.getName()) && area.getArea().contains(item));

    That way, it doesn't get added to the list in the first place so you won't have to remove it.

    Also, instead of even comparing the distances, you could just use the closest method instead of all, since it does it for you.

    List<GroundItem> g = ctx.getGroundItems().closest(item -> item.getName().contains(li.getName()) && area.getArea().contains(item));

    Nice release though :P 

    Link to comment
    Share on other sites

    12 hours ago, Milasoft said:

    For your Comparator, you could just the built in method Double#compare, instead of rounding and casting the double to an int. You could also use a lambda here instead of creating a new class.

    
    g.sort((GroundItem a, GroundItem b)-> Double.compare(ctx.getLocalPlayer().distance(a), ctx.getLocalPlayer().distance(b)));

    Also, for your list of ground items, why not just add the area check to the filter? 

    
    List<GroundItem> g = ctx.getGroundItems().all(item -> item.getName().contains(li.getName()) && area.getArea().contains(item));

    That way, it doesn't get added to the list in the first place so you won't have to remove it.

    Also, instead of even comparing the distances, you could just use the closest method instead of all, since it does it for you.

    
    List<GroundItem> g = ctx.getGroundItems().closest(item -> item.getName().contains(li.getName()) && area.getArea().contains(item));

    Nice release though :P 

    Thanks, this kind of feedback was just what I was looking for. I didn't realize the Double type even had a compare method. This will replace like 10 lines of code with just one. I like it. 👌 Annnnd I'm a little embarrassed that I didn't use getGroundItems().closest() in the first place haha, oh well.

    Link to comment
    Share on other sites

    Archived

    This topic is now archived and is closed to further replies.

    ×
    ×
    • Create New...

    Important Information

    We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.