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
  • Could you give me feedback on my first script?


    schepa

    Recommended Posts

    Hi!

    I'm a mid level web developer and after years of wondering I finally wrote my first script.

    It's a script for the Cook's Assistant quest.

    I thought about starting with Behavior Tree, but it seemed too complex to start with, so I did it using TaskScript.

    I would appreciate any feedback. :)

    org.dreambot.taskscript

    Main

    Spoiler
    @ScriptManifest(name = "Cook's Assistant", author = "schepa", category = Category.QUEST, version = 1.0)
    public class Main extends TaskScript implements ChatListener {
        public List<String> necessaryItems = new LinkedList<>(Arrays.asList("Bucket of milk", "Pot of flour", "Egg"));
        public boolean isHopperOperated = false;
    
        public final Area LUMBRIDGE_CASTLE_KITCHEN = new Area(3206, 3212, 3211, 3216);
        public final Area LUMBRIDGE_CASTLE_CELLAR = new Area(3219, 9620, 3208, 9625);
        public final Area LUMBRIDGE_EAST_FARM = new Area(3252, 3273, 3260, 3280);
        public final Area LUMBRIDGE_WHEAT_FIELD = new Area(3162, 3292, 3160, 3295);
        public final Area MILL_LANE_MILL_SECOND_FLOOR = new Area(3164, 3307, 3169, 3306, 2);
        public final Area MILL_LANE_MILL_GROUND_FLOOR = new Area(3164, 3307, 3169, 3306);
        public final Area FARMER_FRED_HOUSE = new Area(3185, 3276, 3191, 3278);
    
        @Override
        public void onStart() {
            openQuestWidget();
            getNeededItems();
            closeQuestWidget();
    
            addNodes(
                    new StartQuestNode(this),
                    new GetPotNode(this),
                    new GetBucketNode(this),
                    new FillBucketOfMilkNode(this),
                    new FillPotOfFlourNode(this),
                    new GetEggNode(this),
                    new EndQuestNode(this)
            );
        }
    
        @Override
        public void onMessage(Message message) {
            if (message.getMessage().contains("You operate the hopper.")) {
                isHopperOperated = true;
            }
        }
    
        private void openQuestWidget() {
            while (Tabs.getOpen() != Tab.QUEST) {
                Tabs.openWithMouse(Tab.QUEST);
                sleep(1500, 2000);
            }
    
            while (Widgets.getWidget(119) == null) {
                WidgetChild widgetChild = Widgets.getWidget(399).getChild(7).getChild(1);
    
                if (widgetChild.isVisible()) {
                    widgetChild.interact();
                    sleep(1500, 2000);
                }
            }
        }
    
        private void closeQuestWidget() {
            while (Widgets.getWidget(119) != null) {
                Widgets.getWidget(119).getChild(205).interact();
                sleep(2000, 3000);
            }
        }
    
        private void getNeededItems() {
            Widgets.getWidget(119).getChildren().forEach((children) -> {
                String lineText = children.getText();
    
                if (lineText.contains("I have given the cook a bucket of milk.")) {
                    necessaryItems.remove("Bucket of milk");
                } else if (lineText.contains("I have given the cook a pot of flour.")) {
                    necessaryItems.remove("Pot of flour");
                } else if (lineText.contains("I have given the cook an egg.")) {
                    necessaryItems.remove("Egg");
                }
    
    
            });
    
            if (necessaryItems.isEmpty()) {
                return;
            }
        }
    }

    org.dreambot.tasknodes

    StartQuestNode

    Spoiler
    public class StartQuestNode extends TaskNode {
        private final Main main;
    
        public StartQuestNode(Main main) {
            this.main = main;
        }
    
        @Override
        public int priority() {
            return 6;
        }
    
        @Override
        public boolean accept() {
            return !Quests.isStarted(FreeQuest.COOKS_ASSISTANT);
        }
    
        @Override
        public int execute() {
            if (Quests.isFinished(FreeQuest.COOKS_ASSISTANT)) {
                return -1;
            }
    
            if (Utils.walkToArea(main.LUMBRIDGE_CASTLE_KITCHEN)) {
                Utils.interactWithNPC("Talk-to", "Cook");
                Utils.handleDialog("What's wrong?", "Yes.");
            }
    
            return Calculations.random(250, 500);
        }
    }

    GetPotNode

    Spoiler
    public class GetPotNode extends TaskNode {
        private final Main main;
    
        public GetPotNode(Main main) {
            this.main = main;
        }
    
        @Override
        public int priority() {
            return 5;
        }
    
        @Override
        public boolean accept() {
            boolean isNecessary = main.necessaryItems.contains("Pot of flour");
            boolean hasPotOfFlour = Inventory.contains("Pot of flour");
            boolean hasPot = Inventory.contains("Pot");
    
            return isNecessary && !hasPotOfFlour && !hasPot;
        }
    
        @Override
        public int execute() {
    
            if (Utils.walkToArea(main.LUMBRIDGE_CASTLE_KITCHEN)) {
                Utils.takeGroundItem("Pot");
            }
    
            return Calculations.random(250, 500);
        }
    }

    GetBucketNode

    Spoiler
    public class GetBucketNode extends TaskNode {
        private final Main main;
    
        public GetBucketNode(Main main) {
            this.main = main;
        }
    
        @Override
        public int priority() {
            return 4;
        }
    
        @Override
        public boolean accept() {
            boolean isNecessary = main.necessaryItems.contains("Bucket of milk");
            boolean hasBucketOfMilk = Inventory.contains("Bucket of milk");
            boolean hasBucket = Inventory.contains("Bucket");
    
            return isNecessary && !hasBucketOfMilk && !hasBucket;
        }
    
        @Override
        public int execute() {
            if (Utils.walkToArea(main.LUMBRIDGE_CASTLE_CELLAR)) {
                Utils.takeGroundItem("Bucket");
            }
    
            return Calculations.random(250, 500);
        }
    }

    FillBucketOfMilkNode

    Spoiler
    public class FillBucketOfMilkNode extends TaskNode {
        private final Main main;
    
        public FillBucketOfMilkNode(Main main) {
            this.main = main;
        }
    
        @Override
        public int priority() {
            return 3;
        }
    
        @Override
        public boolean accept() {
            boolean isNecessary = main.necessaryItems.contains("Bucket of milk");
            boolean hasBucketOfMilk = Inventory.contains("Bucket of milk");
    
            return isNecessary && !hasBucketOfMilk;
        }
    
        @Override
        public int execute() {
    
            if (Utils.walkToArea(main.LUMBRIDGE_EAST_FARM)) {
                Utils.interactWithGameObject("Milk", "Dairy cow");
            }
    
            return Calculations.random(250, 500);
        }
    }

    FillPotOfFlourNode

    Spoiler
    public class FillPotOfFlourNode extends TaskNode {
        private final Main main;
    
        public FillPotOfFlourNode(Main main) {
            this.main = main;
        }
    
        @Override
        public int priority() {
            return 3;
        }
    
        @Override
        public boolean accept() {
            boolean isNecessary = main.necessaryItems.contains("Pot of flour");
            boolean hasPotOfFlour = Inventory.contains("Pot of flour");
    
            return isNecessary && !hasPotOfFlour;
        }
    
        @Override
        public int execute() {
            if (!Inventory.contains("Grain")) {
                if (main.isHopperOperated) {
                    Utils.walkToArea(main.MILL_LANE_MILL_GROUND_FLOOR);
                    Utils.interactWithGameObject("Empty", "Flour bin", 5000, 6000);
                } else if (Utils.walkToArea(main.LUMBRIDGE_WHEAT_FIELD)) {
                    Utils.interactWithGameObject("Pick", "Wheat");
                }
            } else {
                if (Utils.walkToArea(main.MILL_LANE_MILL_SECOND_FLOOR)) {
                    Utils.interactWithGameObject("Fill", "Hopper", 5000, 6000);
                    Utils.interactWithGameObject("Operate", "Hopper controls", 5000, 6000);
                }
            }
    
            return Calculations.random(250, 500);
        }
    }

    GetEggNode

    Spoiler
    public class GetEggNode extends TaskNode {
        private final Main main;
    
        public GetEggNode(Main main) {
            this.main = main;
        }
    
        @Override
        public int priority() {
            return 1;
        }
    
        @Override
        public boolean accept() {
            boolean isNecessary = main.necessaryItems.contains("Egg");
            boolean hasEgg = Inventory.contains("Egg");
    
            return isNecessary && !hasEgg;
        }
    
        @Override
        public int execute() {
            if (Utils.walkToArea(main.FARMER_FRED_HOUSE)) {
                Utils.takeGroundItem("Egg");
            }
    
            return Calculations.random(250, 500);
        }
    }

    EndQuestNode

    Spoiler
    public class EndQuestNode extends TaskNode {
        private final Main main;
    
        public EndQuestNode(Main main) {
            this.main = main;
        }
    
        @Override
        public int priority() {
            return 0;
        }
    
        @Override
        public boolean accept() {
            return true;
        }
    
        @Override
        public int execute() {
            if (Utils.walkToArea(main.LUMBRIDGE_CASTLE_KITCHEN)) {
                Utils.interactWithNPC("Talk-to", "Cook");
                Utils.handleDialog();
            }
    
            return Calculations.random(250, 500);
        }
    }

     

    CooksAssistant.zip

    Link to comment
    Share on other sites

    Utils.interactWithNPC("Talk-to", "Cook");
    Utils.handleDialog("What's wrong?", "Yes.");

    1) You're often not checking if the interaction was successful. Any misclick, lag, etc could throw it off.

    2) You're also using a lot of while loops that could loop infinitely,  where a simple sleepUntil would be better to allow to recover from unexpected situations (and just for clarity).

    3) Looks like the majority of your member variables are public? This is usually not a good idea, see here: https://stackoverflow.com/questions/48820066/why-do-we-declare-private-variables-in-java#:~:text=Making a variable private "protects,classes which use the class.

    4) I'd also separate the logic a bit more instead of using state variables and a bunch of areas in your Main class.

    5) I'm not sure what Utils#interactWithGameObject does, but every GameObject already has an interact method.

    Didn't look at the details too much but this is what I noticed. Otherwise good job, and good luck in the future :) 

    Link to comment
    Share on other sites

    Your Utils object is doing a LOT of heavy lifting. A danger of having a generic "Utils" class is that it very quickly becomes unwieldy, and you start losing a lot of the organization that OOP brings to the table. I personally like having separate utility classes that wrap around core functions, like a "Walker", a "Talker", a "Banker", etc. That way, everything stays self-contained.

    I'll mimic SubCZ's recommendations of removing a lot of the "While" statements. Most of the time they're not necessary, and the more while loops, the more likely your bot can end up in a very obviously-botlike state (ie, missing random events, or repeatedly clicking on a game object over and over and over again). I'd rather the script fail and crash than get stuck in one of those states.

    Finally, it seems like you're waiting between 5-6 seconds whenever interacting with an object you may need to walk to. You can use a sleepUntil statement with a condition of "player not moving, player not animating, and player distance to object is 1" to make this much less botlike.

    Good start!

    Link to comment
    Share on other sites

    Create an account or sign in to comment

    You need to be a member in order to leave a comment

    Create an account

    Sign up for a new account in our community. It's easy!

    Register a new account

    Sign in

    Already have an account? Sign in here.

    Sign In Now
    ×
    ×
    • 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.