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
  • Try asking for help in the chatbox
  • Generic Anti-Ban System


    Zenarchist
     Share

    Recommended Posts

    Long story short, I have decided to retire early from my OSRS scripting career and I feel bad leaving all this work I've done behind. So I figure I'll release the source code to everyone and maybe someone can learn something from it and then it isn't a complete waste of time lol.

    Features:

    Easy to use, robust anti-pattern system that randomizes various activities while botting. The current code will:

    • Examine random entities (objects, npcs, ground and inventory items etc)
    • Check a random stat (you can tell it which stats to check with antiban.setStatsToCheck(Skill.ATTACK, Skill.DEFENCE);)
    • Type something random (gibberish)
    • Move the mouse to a random location (and sometimes click the left or right button)
    • Walk to a random location nearby
    • Chop a random tree nearby
    • Click on a random entity (object, npc, item etc.)
    • Go AFK for a while (turns off autologin and random solvers temporarily)
    • Open your inventory
    • Open your stats menu
    • Open your magic menu
    • Open your combat menu (only if you have included Melee stats in your Stats to Check)
    • Move the mouse off-screen for a while
    • Move the camera randomly
    • Trigger a "DO_RANDOM" flag for custom script-specific anti-pattern measures (eg. drop junk early etc.)

    It also includes ChatBot code (for retrieving CleverBot API responses). I will also include the code to my script-helper class just for anyone who is curious and maybe wants to steal some of my helper methods (such as randomized slot-pattern item dropping and general convenience methods).

    The way it was designed is to be used as a final callback after your usual script logic is performed. When your character is idling or between actions, call antiban.antiBan() to perform any of the given actions at random.

    Enjoy guys :) have fun.

    Script Example:

    package ZenTester;
    
    import org.dreambot.api.script.AbstractScript;
    import org.dreambot.api.script.Category;
    import org.dreambot.api.script.ScriptManifest;
    
    import java.awt.*;
    
    @ScriptManifest(category = Category.MISC, name = "ZenTester", author = "Zenarchist", version = 1.0, description = "For testing")
    public class Main extends AbstractScript {
        // Declare anti-ban instance
        private ZenAntiBan antiban;
    
        @Override
        public void onStart() {
            // Initialize anti-ban instance
            antiban = new ZenAntiBan(this);
        }
    
        @Override
        public int onLoop() {
            // Check for random flag (for adding extra customized anti-ban features)
            if(antiban.doRandom())
                log("Script-specific random flag triggered");
    
            // Call anti-ban (returns a wait time after performing any actions)
            return antiban.antiBan();
        }
    
        @Override
        // Draw anti-ban info to the screen
        public void onPaint(Graphics g) {
            g.drawString("Anti-Ban Status: " + (antiban.getStatus().equals("") ? "Inactive" : antiban.getStatus()), 10, 100);
        }
    }

    ZenAntiBan.java:

     
    package YourScript;
    
    import org.dreambot.api.methods.Calculations;
    import org.dreambot.api.methods.map.Area;
    import org.dreambot.api.methods.map.Tile;
    import org.dreambot.api.methods.skills.Skill;
    import org.dreambot.api.methods.tabs.Tab;
    import org.dreambot.api.randoms.RandomEvent;
    import org.dreambot.api.script.AbstractScript;
    import org.dreambot.api.wrappers.interactive.Entity;
    import org.dreambot.api.wrappers.interactive.GameObject;
    import org.dreambot.api.wrappers.interactive.NPC;
    import org.dreambot.api.wrappers.interactive.Player;
    import org.dreambot.api.wrappers.items.Item;
    import org.dreambot.api.wrappers.widgets.Menu;
    import org.dreambot.api.wrappers.widgets.message.Message;
    
    import java.awt.*;
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.net.URL;
    import java.net.URLConnection;
    
    /**
     * This class holds all of my anti-ban ideas.
     *
     * @author Zenarchist
     */
    public class ZenAntiBan {
        public int ANTIBAN_RATE = 50; // This is the frequency rate for anti-ban actions (in % terms - 100% = frequent, 0% = never
        public int MIN_WAIT_NO_ACTION = 50; // This is the minimum time to wait if no action was taken
        public int MAX_WAIT_NO_ACTION = 100; // This is the maximum time to wait if no action was taken
        private AbstractScript s; // Script
        private String STATUS = "Idling"; // Current anti-ban status
        private Skill[] STATS_TO_CHECK = { Skill.HITPOINTS }; // This is used for determining which stats to randomly check
        public int MIN_WAIT_BETWEEN_EVENTS = 10; // In seconds
        private long LAST_EVENT = 0L; // Last time an antiban event was triggered
        private long LAST_IDLE; // Last time we idled for a while
        private boolean DO_RANDOM = false; // This is a generic flag for randomly doing something early in a script for anti-patterning
        private int MAX_RUNTIME_MINUTES = -1; // This is the maximum amount of time the script should run for (used for calculating progressive lag multiplier + max duration)
        private long START_TIME = 0L; // Time the script was started
        // Stat widget coordinates (in order of DB API listing for Skill array)
        public final Point[] STAT_WIDGET = {
                new Point(550, 210), // Attack
                new Point(550, 270), // Defence
                new Point(550, 240), // Strength
                new Point(612, 210), // Hits
                new Point(550, 304), // Ranged
                new Point(550, 336), // Prayer
                new Point(350, 370), // Magic
                new Point(367, 304), // Cooking
                new Point(676, 368), // Woodcut
                new Point(613, 369), // Fletching
                new Point(677, 273), // Fishing
                new Point(676, 336), // Firemaking
                new Point(614, 337), // Crafting
                new Point(677, 240), // Smithing
                new Point(677, 209), // Mining
                new Point(613, 271), // Herblore
                new Point(614, 240), // Agility
                new Point(614, 304), // Thieving
                new Point(614, 401), // Slayer
                new Point(676, 400), // Farming
                new Point(550, 400), // Runecrafting
                new Point(613, 432), // Hunter
                new Point(550, 432), // Construction
        };
        private final Point STATS_WIDGET = new Point(577, 186); // Stats menu
        private final Point INVENTORY_WIDGET = new Point(643, 185); // Inventory menu
        private final Point COMBAT_WIDGET = new Point(543, 186); // Combat style menu
        private final Point MAGIC_WIDGET = new Point(742, 186); // Magic menu
        // Chatbot variables
        private boolean USE_CHATBOT = false; // Whether or not to use the ChatBot
        private int CHATBOT_RATE_MINS = 5; // Minimum amount of minutes between chatbot replies
        private String CHATBOT_STATE = "x"; // This is used for continuing conversations (not that it matters - chatbot is dumb as hell)
        private long LAST_CHATBOT_REPLY = 0L; // Last time the chatbot replied
        private int CHATBOT_REPLIES = 0; // How many times the chatbot has replied to player messages
        private final String VALID_CHARACTERS = "abcdefghijklmnopqrstuvwxyz"; // Valid characters for chatbot
        private long LAST_PERSONAL_REPLY = 0L; // Last time we replied to a 'bot' flagged message or a message addressed to us
        private String CHATBOT_KEY = "xxx"; // CleverBot API key
    
        // Constructs a new Anti-Ban class with the given script
        public ZenAntiBan(AbstractScript script) {
            this.s = script;
            // Set last idle to now to avoid idling early into the script
            LAST_IDLE = System.currentTimeMillis();
            START_TIME = System.currentTimeMillis();
        }
    
        // Returns the wait time for when the antiban system does nothing
        private int doNothing() {
            return rh(MIN_WAIT_NO_ACTION, MAX_WAIT_NO_ACTION);
        }
    
        // Sets the stats to check during random antiban events
        public void setStatsToCheck(Skill... skills) {
            STATS_TO_CHECK = skills;
        }
    
        // Returns the sleep time after performing an anti-ban check
        public int antiBan() {
            setStatus("");
            if(ANTIBAN_RATE == 0 || System.currentTimeMillis() - LAST_EVENT <= r(MIN_WAIT_BETWEEN_EVENTS * 1000, MIN_WAIT_BETWEEN_EVENTS * 2000))
                return doNothing();
    
            // If we have moved the mouse outside of the screen, wait a moment before performing the ban action
            if(s.getMouse().getX() == -1 && s.getMouse().getY() == -1)
                s.sleep(1000, 2000);
    
            // Calculate overall random anti-ban intervention rate (%)
            int rp = r(0, 100);
            if(rp < ANTIBAN_RATE) {
                // Calculate event-specific activation rate (%)
                rp = r(0, 100);
                // Calculate event ID
                int event = r(0, 14);
                // Handle specified event
                switch(event) {
                    case 0: { // Examine random entity
                        if(rp < 25) { // 25% chance
                            int r = r(1, 3);
                            Entity e = s.getGameObjects().closest(o -> o != null && !o.getName().equals("null") && r(1, 2) != 1);
                            if (e == null || r == 2) {
                                e = s.getNpcs().closest(n -> n != null && !n.getName().equals("null"));
                                if (e == null || r == 3) {
                                    e = s.getGroundItems().closest(i -> i != null && !i.getName().equals("null"));
                                    if (e == null)
                                        return doNothing();
                                }
                            }
    
                            setStatus("Examining entity (" + e.getName() + ")");
                            s.getMouse().move(e);
    
                            if(r(0, 100) < 99) { // 99% chance of clicking examine
                                // Open right-click menu and find Examine option
                                Menu menu = new Menu(s.getClient());
                                rh(1, 100);
                                menu.open();
                                s.sleep(rh(250, 1000));
                                if (menu.contains("Examine"))
                                    menu.clickAction("Examine", e);
                                else
                                if(menu.contains("Cancel"))
                                    menu.clickAction("Cancel");
                            }
    
                            LAST_EVENT = System.currentTimeMillis();
                            return rh(250, 3000);
                        }
                    }
                    case 1: { // Check random stat
                        if(rp < 10) { // 10% chance
                            if (s.getTabs().getOpen() != Tab.STATS)
                                openStats();
                            int x = r(0, 25);
                            int y = r(0, 15);
                            int skill = -1;
                            long t = System.currentTimeMillis();
                            while (skill == -1 && System.currentTimeMillis() - t <= 500) {
                                int r = r(0, Skill.values().length - 1);
                                for (Skill s : STATS_TO_CHECK) {
                                    if (s.getName().equals(Skill.values()[r].getName()))
                                        skill = r;
                                }
                            }
    
                            setStatus("Checking EXP (" + Skill.values()[skill].getName() + ")");
                            Point p = STAT_WIDGET[skill];
                            p.setLocation(p.getX() + x, p.getY() + y);
                            s.getMouse().move(p);
    
                            LAST_EVENT = System.currentTimeMillis();
                            return rh(2000, 5000);
                        }
                    }
                    case 2: { // Type something random
                        if(rp < 1) { // 1% chance
                            String str = "";
                            int r = r(1, 2);
                            for(int i = 0; i < r; i++)
                                str += VALID_CHARACTERS.charAt(r(0, VALID_CHARACTERS.length()));
                            setStatus("Typing random keys (" + str + ")");
                            s.getKeyboard().type(str);
                            LAST_EVENT = System.currentTimeMillis();
                            return rh(500, 3000);
                        }
                    }
                    case 3: { // Move mouse to random location (and sometimes click)
                        if(rp < 10) { // 10% chance
                            int r = r(0, 100);
                            int x = r(0, 760);
                            int y = r(0,500);
                            setStatus("Moving mouse (" + x + "," + y + ")");
                            s.getMouse().move(new Point(x, y));
                            if(r < 5) // 10% chance of right-clicking
                                s.getMouse().click(true);
                            else
                            if(r < 5) // 5% chance of left-clicking
                                s.getMouse().click();
    
                            LAST_EVENT = System.currentTimeMillis();
                            return rh(500, 3000);
                        }
                    }
                    case 4: { // Walk to random location
                        if(rp < 1) { // 1% chance
                            int x = s.getLocalPlayer().getX() - 15;
                            int y = s.getLocalPlayer().getY() - 15;
                            int x2 = r(0, 30);
                            int y2 = r(0, 30);
                            Area a = new Area(x, y, x2, y2);
                            Tile t = a.getRandomTile();
                            setStatus("Walking to random tile (" + t.getX() + "," + t.getY() + ")");
                            s.getWalking().walk(t);
                            LAST_EVENT = System.currentTimeMillis();
                            return rh(500, 3000);
                        }
                    }
                    case 5: { // Chop random tree
                        if(rp < 1) { // 1% chance
                            GameObject obj = s.getGameObjects().closest(o -> o != null && !o.getName().equals("null") && r(1, 2) != 1 && o.hasAction("Chop down") && s.getLocalPlayer().distance(o) < 5);
                            if(obj == null)
                                return doNothing();
    
                            setStatus("Chopping random tree (" + obj.getName() + ")");
                            if(r(1, 2) != 1)
                                obj.interact("Chop down");
                            else
                                obj.interactForceLeft("Chop down");
    
                            LAST_EVENT = System.currentTimeMillis();
                            return rh(500, 3000);
                        }
                    }
                    case 6: { // Click random entity
                        if(rp < 1) { // 1% chance
                            Entity e = s.getGameObjects().closest(o -> o != null && !o.getName().equals("null")  && r(1, 2) != 1 && s.getLocalPlayer().distance(o) < 5);
                            int r = r(1, 3);
                            if(e == null || r == 2) {
                                e = s.getNpcs().closest(n -> n != null && !n.getName().equals("null"));
                                if(e == null || r == 3) {
                                    e = s.getGroundItems().closest(i -> i != null && !i.getName().equals("null"));
                                    if(e == null)
                                        return doNothing();
                                }
                            }
    
                            if(e instanceof NPC && s.getCombat().isInMultiCombat())
                                break;
    
                            setStatus("Clicking random entity (" + e.getName() + ")");
                            s.getMouse().move(e);
                            s.sleep(rh(0, 50));
                            if(r(0, 100) < 25)
                                s.getMouse().click(true);
                            else
                                s.getMouse().click();
    
                            LAST_EVENT = System.currentTimeMillis();
                            return rh(500, 3000);
                        }
                    }
                    case 7: { // Just idle for a while
                        if(rp < 5) { // 5% chance
                            if (System.currentTimeMillis() - LAST_IDLE >= 300000) { // Only allow idling to occur every 5+ minutes
                                int idle = r(60000, 120000);
                                setStatus("Idling for " + (idle / 1000) + " seconds");
                                if (r(0, 100) < 99)
                                    s.getMouse().moveMouseOutsideScreen();
                                // Disable dismiss & autologin solvers temporarily
                                s.getRandomManager().disableSolver(RandomEvent.LOGIN);
                                s.getRandomManager().disableSolver(RandomEvent.DISMISS);
                                // Sleep for the calculated time
                                s.sleep(idle);
                                // Enable dismiss & autologin solvers and resume script as normal
                                s.getRandomManager().enableSolver(RandomEvent.LOGIN);
                                s.getRandomManager().enableSolver(RandomEvent.DISMISS);
                                LAST_IDLE = System.currentTimeMillis();
                                return 1;
                            }
                        }
                    }
                    case 8: { // Open inventory or stats
                        if(rp < 25) { // 25% chance
                            if(s.getTabs().getOpen() != Tab.INVENTORY && s.getInventory().getEmptySlots() > 0)
                                setStatus("Opening inventory");
                            if (openInventory()) {
                                LAST_EVENT = System.currentTimeMillis();
                                s.sleep(50, 100);
                                s.getMouse().moveMouseOutsideScreen();
                                return rh(500, 1000);
                            }
                        } else
                        if(rp > 75) { // 25% chance
                            if(s.getTabs().getOpen() != Tab.STATS)
                                setStatus("Opening stats");
                            if(openStats()) {
                                LAST_EVENT = System.currentTimeMillis();
                                s.sleep(50, 100);
                                s.getMouse().moveMouseOutsideScreen();
                                return rh(500, 1000);
                            }
                        }
                    }
                    case 9: { // Open combat menu (only if we are training melee stats)
                        boolean meleeStats = false;
                        for(Skill s : STATS_TO_CHECK)
                            if(s.equals(Skill.ATTACK) || s.equals(Skill.STRENGTH) || s.equals(Skill.DEFENCE))
                                meleeStats = true;
    
                        if(!meleeStats)
                            break;
    
                        if(rp < 5) { // 5% chance
                            if (s.getTabs().getOpen() != Tab.COMBAT)
                                setStatus("Opening combat menu");
                            openCombat();
                            s.sleep(50, 100);
                            s.getMouse().moveMouseOutsideScreen();
                            LAST_EVENT = System.currentTimeMillis();
                            return rh(500, 1000);
                        }
                    }
                    case 10: { // Moving mouse off-screen for a moment
                        if(rp < 50) { // 50% chance
                            if(s.getMouse().getX() == -1 && s.getMouse().getY() == -1)
                                return doNothing();
    
                            setStatus("Moving mouse off-screen");
                            s.getMouse().moveMouseOutsideScreen();
                            LAST_EVENT = System.currentTimeMillis();
                            return rh(5000, 8000);
                        }
                    }
                    case 11: { // Open magic menu
                        if(rp < 1) { // 1% chance
                            if(s.getTabs().getOpen() != Tab.MAGIC) {
                                setStatus("Opening magic menu");
                                openMagic();
                                s.sleep(50, 100);
                                s.getMouse().moveMouseOutsideScreen();
                            }
                        }
                    }
                    case 12: { // Examine random inventory item
                        if(rp < 1) { // 1% chance
                            if (openInventory())
                                s.sleep(10, 250);
                            for(Item i : s.getInventory().all(it -> it != null)) {
                                if(i != null && r(1, 3) == 2) {
                                    setStatus("Examining item (" + i.getName() + ")");
                                    s.getMouse().move(i.getDestination());
                                    s.sleep(rh(0, 50));
                                    // Open right-click menu and find Examine option
                                    Menu menu = new Menu(s.getClient());
                                    menu.open();
                                    s.sleep(rh(250, 1000));
                                    if (menu.contains("Examine"))
                                        menu.clickAction("Examine");
                                    else
                                    if(menu.contains("Cancel"))
                                        menu.clickAction("Cancel");
    
                                    break;
                                }
                            }
                        }
                    }
                    case 13: { // Move camera randomly
                        if(rp < 30) { // 30% chance
                            print("Moving camera");
                            Area a = new Area(s.getLocalPlayer().getX() - 10, s.getLocalPlayer().getY() - 10, s.getLocalPlayer().getX() + 10, s.getLocalPlayer().getY() + 10);
                            s.getCamera().rotateToTile(a.getRandomTile());
                            return doNothing();
                        }
                    }
                    case 14: { // Do early flag
                        if(rp < 5) { // 5% chance
                            DO_RANDOM = true;
                            return doNothing();
                        }
                    }
                    default:
                        return doNothing();
                }
    
            }
    
            return doNothing();
        }
    
        // Returns whether or not the DO_RANDOM flag has been triggerd
        public boolean doRandom() {
            if(DO_RANDOM) {
                DO_RANDOM = false;
                return true;
            }
    
            return false;
        }
    
        // Get antiban status
        public String getStatus() {
            return STATUS;
        }
    
        // Print to the console if debug is enabled
        private void print(Object o) {
            s.log("[AntiBan] " + o.toString());
        }
    
        // Returns the chatbot reply count
        public int getChatBotReplyCount() {
            return CHATBOT_REPLIES;
        }
    
        // Returns a chat reply from Cleverbot
        public String getChatbotReply(String phrase) {
            // If message contains spam, ignore it
            if(containsSpam(phrase))
                return null;
    
            // If message does not contain any letters, ignore it
            boolean containsLetter = false;
            for(char c : VALID_CHARACTERS.toCharArray()) {
                if(phrase.contains("" + c))
                    containsLetter = true;
            }
    
            if(!containsLetter)
                return null;
    
            String returner = null;
            phrase = phrase.toLowerCase().trim().replaceAll("[^\\w\\s]", "").replaceAll(s.getLocalPlayer().getName().toLowerCase(), "");
            phrase = phrase.replaceAll("/", "");
    
            try {
                // Fetch URL result for chatbot API call
                URL url = new URL("https://www.cleverbot.com/getreply?key=" + CHATBOT_KEY + "&input=" + phrase.toLowerCase().trim() + "&cs=" + CHATBOT_STATE + "&callback=ProcessReply");
                URLConnection urlConnection = url.openConnection();
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
                String line = bufferedReader.readLine();
                String T1 = "\"output\":";
                String T2 = "\",\"";
                // Scrape relevant data from JSON output
                String reply = line.substring(line.indexOf(T1) + T1.length());
                reply = reply.substring(0, reply.indexOf(T2)).replaceAll("\"", "").replaceAll("\\.", "");
                String cleverbotState = line.substring(20, line.indexOf("\",\""));
                // Remove any special characters and inappropriate words from the bot reply
                reply = reply.toLowerCase().replaceAll("cleverbot", "");
                reply = reply.toLowerCase().replaceAll("robot", "");
                reply = reply.toLowerCase().replaceAll("name", "deal");
                reply = reply.toLowerCase().replaceAll("how old", "");
                reply = reply.toLowerCase().replaceAll("age", "deal");
                reply = reply.replaceAll("[^\\w\\s]","");
    
                // Randomly mix up certain punctuation
                if(r(1, 5) == 3)
                    reply = reply.replaceAll("'", "");
                if (reply.equalsIgnoreCase("what?") && r(1, 6) == 2)
                    reply = reply + "!";
    
                // If reply does not contain any letters at all, then ignore it
                boolean letterFound = false;
                for(char c : VALID_CHARACTERS.toCharArray()) {
                    if(reply.contains("" + c))
                        letterFound = true;
                }
    
                if(!letterFound)
                    return null;
    
                returner = reply;
                CHATBOT_STATE = cleverbotState;
                bufferedReader.close();
            } catch (Exception e) {
                e.printStackTrace();
                print("ERROR: Failed to get reply from chatbot!");
            }
    
            return returner;
        }
    
        // Checks to see if the given message should be replied to
        public boolean checkPlayerMessage(Message message) {
            // Ignore spam
            if(!USE_CHATBOT || containsSpam(message.getMessage()))
                return false;
    
            long now = System.currentTimeMillis();
    
            // If script has just started, do not reply to chat
            if(now - START_TIME <= 60000)
                return false;
    
            String text = message.getMessage().toLowerCase().trim();
            String user = message.getUsername();
            Player speaker = null;
    
            // Check local players to find if the Speaker exists as a Player entity
            for(Player p : s.getPlayers().all()) {
                if(p != null && p.getName().equalsIgnoreCase(user))
                    speaker = p;
            }
    
            // If the speaker player is null, or the speaker is us, or the speaker is far away, then do not reply
            if(speaker == null || s.getLocalPlayer().equals(speaker) || s.getLocalPlayer().distance(speaker) > 15)
                return false;
    
            int chatLimitMins = CHATBOT_RATE_MINS;
            // If we have replied within the last X minutes and this chat does not contain our player name or the word bot, don't reply
            if(now - LAST_CHATBOT_REPLY <= r(chatLimitMins, chatLimitMins * 2) && !containsPlayerName(text) && !containsBotWord(text)) {
                //print("We cannot reply for 5-10 more minutes");
                return false;
            }
    
            // If this chat DOES contain the word bot or our player name, and we have not replied to a similar message in the last 20-45 seconds, then reply.
            if(containsPlayerName(text) || containsBotWord(text)) {
                if(now - LAST_PERSONAL_REPLY <= r(20000, 45000)) {
                    return false;
                }
    
                LAST_PERSONAL_REPLY = now;
            }
    
            // Get reply from chatbot and type it
            String chatbot = getChatbotReply(message.getMessage());
            if(chatbot != null && !chatbot.equals("")) {
                LAST_CHATBOT_REPLY = System.currentTimeMillis();
                s.log("[ChatBot] Reply: " + chatbot + ", Phrase: " + message.getMessage() + ", From: " + message.getUsername());
                s.sleep(rh(3000, 5000));
                s.getKeyboard().type(chatbot, true, true);
                CHATBOT_REPLIES++;
                return true;
            }
    
            // No reply was given, return false
            return false;
        }
    
        // Returns true if any of these botting-related words are found in the given sentence
        public boolean containsBotWord(String phrase) {
            return phrase.contains("bot") || phrase.contains("macro") || phrase.contains("script") || phrase.contains("auto");
        }
    
        // Returns true if any of these spam-related wrods are found in the given sentence
        public boolean containsSpam(String phrase) {
            String spam = phrase.replaceAll(" ", "").toLowerCase().trim();
            // If message is selling OSRS gp or advertising a clan, ignore it
            if (spam.contains("sell") || spam.contains("usd") || spam.contains("gp") || spam.contains("fee") || spam.contains("free") || spam.contains("join") || phrase.equals("") || phrase.length() <= 1)
                return true;
    
            return false;
        }
    
        // Returns true if the player's name is found in the given string
        public boolean containsPlayerName(String phrase) {
            // Check if phrase straight-up contains our name
            boolean containsName = phrase.contains(s.getLocalPlayer().getName());
    
            // Check if this chat straight-up contains our username
            if(containsName)
                return true;
    
            // Check for spam
            if(containsSpam(phrase))
                return false;
    
            // Removes all numbers from our player's name
            String name = s.getLocalPlayer().getName().replaceAll("[^A-Za-z]", "");
            String[] nameWords = splitCamelCase(name);// Splits our name up by capital letters (for nicknames/shorthand - eg. ZenArchist becomes Zen,Archist)
            String[] spaceWords = s.getLocalPlayer().getName().toLowerCase().split(" "); // Splits our name up by spaces (eg. Zen Archist becomes Zen,Archist)
    
            // Cycle through camelcase name words
            if(nameWords != null && nameWords.length > 1) {
                for (String s : nameWords) {
                    if (s.equals("") || s.length() <= 2)
                        continue;
    
                    if (phrase.toLowerCase().contains(s.toLowerCase()))
                        containsName = true;
                }
            }
    
            // Cycle through spaced name words
            if(spaceWords != null && spaceWords.length > 1) {
                for(String s : spaceWords) {
                    if(s.equals("") || s.length() <= 2)
                        continue;
    
                    if(phrase.toLowerCase().contains(s.toLowerCase()))
                        containsName = true;
                }
            }
    
            // If we have found a chat flag, let the user know in the debug console
            if(containsName)
                print("[ChatBot] Flag: " + phrase + " = " + containsName);
    
            return containsName;
        }
    
        // Converts the given string into split words (ie. BitcoinMan becomes Bitcoin Man)
        private static String[] splitCamelCase(String s) {
            String split = s.replaceAll(
                    String.format("%s|%s|%s",
                            "(?<=[A-Z])(?=[A-Z][a-z])",
                            "(?<=[^A-Z])(?=[A-Z])",
                            "(?<=[A-Za-z])(?=[^A-Za-z])"
                    ),
                    ","
            );
    
            return split.split(",");
        }
    
        // Allows an external class to set the anti-ban status
        public void setStatus(String status) {
            STATUS = status;
            if(!status.equals(""))
                print(status);
        }
    
        // Returns a random number
        public int r(int x, int y) {
            return Calculations.random(x, y+1);
        }
    
        // Returns a random number with the human lag element added to the minimum wait time
        public int rh(int x, int y) {
            //return r(x + getHumanLag(), y + RAND + getHumanLag());
            return r(x + (int)(x*getLagMultiplier()), y + (int)(y*getLagMultiplier()));
        }
    
        // Returns the lag multiplier (increases as script runs longer)
        public double getLagMultiplier() {
            double minutesRunning = (double) ((System.currentTimeMillis() - START_TIME) / 60000);
            double percent = (minutesRunning / (double)(MAX_RUNTIME_MINUTES == -1 ? 480 : MAX_RUNTIME_MINUTES));
    
            if(percent > 1.0)
                percent = 1.0D;
    
            return percent;
        }
    
        // This method opens the stats menu
        public boolean openStats() {
            if (s.getTabs().getOpen() != Tab.STATS) {
                // Sometimes use hot keys, sometimes use mouse
                if (Calculations.random(1, 3) == 2)
                    s.getSkills().open();
                else {
                    int x = (int) STATS_WIDGET.getX() + r(0, 10);
                    int y = (int) STATS_WIDGET.getY() + r(0, 10);
                    s.getMouse().move(new Point(x, y));
                    s.sleep(0, 50);
                    s.getMouse().click();
                }
    
                s.sleep(50, 250);
            }
    
            return s.getTabs().getOpen() == Tab.STATS;
        }
    
        // Opens the  combat menu then waits for a second
        public boolean openCombat() {
            if (s.getTabs().getOpen() != Tab.COMBAT) {
                // Sometimes use hot keys, sometimes use mouse
                if (Calculations.random(1, 3) == 2)
                    s.getTabs().open(Tab.COMBAT);
                else {
                    int x = (int) COMBAT_WIDGET.getX() + Calculations.random(0, 10);
                    int y = (int) COMBAT_WIDGET.getY() + Calculations.random(0, 10);
                    s.getMouse().move(new Point(x, y));
                    s.sleep(0, 50);
                    s.getMouse().click();
                }
    
                s.sleep(50, 250);
            }
    
            return s.getTabs().getOpen() == Tab.COMBAT;
        }
    
        // This method opens the magic menu
        public boolean openMagic() {
            if (s.getTabs().getOpen() != Tab.MAGIC) {
                // Sometimes use hot keys, sometimes use mouse
                if (Calculations.random(1, 3) == 2)
                    s.getTabs().open(Tab.MAGIC);
                else {
                    int x = (int) MAGIC_WIDGET.getX() + r(0, 10);
                    int y = (int) MAGIC_WIDGET.getY() + r(0, 10);
                    s.getMouse().move(new Point(x, y));
                    s.getMouse().click();
                }
    
                s.sleep(50, 250);
            }
    
            return s.getTabs().getOpen() == Tab.MAGIC;
        }
    
        // This method opens the inventory
        public boolean openInventory() {
            if (s.getTabs().getOpen() != Tab.INVENTORY) {
                // Sometimes use hot keys, sometimes use mouse
                if (Calculations.random(1, 3) == 2)
                    s.getTabs().open(Tab.INVENTORY);
                else {
                    int x = (int) INVENTORY_WIDGET.getX() + r(0, 10);
                    int y = (int) INVENTORY_WIDGET.getY() + r(0, 10);
                    s.getMouse().move(new Point(x, y));
                    s.getMouse().click();
                }
    
                s.sleep(50, 250);
            }
    
            return s.getTabs().getOpen() == Tab.INVENTORY;
        }
    }
    
    Edited by Zenarchist
    Link to comment
    Share on other sites

    And here is my entire heavily-commented Helper class package for anyone who is curious and looking for ideas. Just put all of these into a package named Zen, then import Zen.* and call the constructor Zen(script) in your AbstractScript's start method to gain access to the generic helper methods, GUI and the AntiBan systems in your own scripts.

    This class contains features like generic shift-dropping methods, inventory slot pattern randomization, various widget IDs and coordinates etc. Basically it is just a custom library for simplifying any actions that are performed repeatedly across multiple scripts.

    Zen.java:

     
    package Zen;
    
    import org.dreambot.api.data.GameState;
    import org.dreambot.api.methods.Calculations;
    import org.dreambot.api.methods.container.impl.Inventory;
    import org.dreambot.api.methods.container.impl.equipment.EquipmentSlot;
    import org.dreambot.api.methods.filter.Filter;
    import org.dreambot.api.methods.map.Area;
    import org.dreambot.api.methods.map.Tile;
    import org.dreambot.api.methods.skills.Skill;
    import org.dreambot.api.methods.tabs.Tab;
    import org.dreambot.api.randoms.RandomEvent;
    import org.dreambot.api.script.AbstractScript;
    import org.dreambot.api.wrappers.interactive.GameObject;
    import org.dreambot.api.wrappers.interactive.NPC;
    import org.dreambot.api.wrappers.interactive.Player;
    import org.dreambot.api.wrappers.items.GroundItem;
    import org.dreambot.api.wrappers.items.Item;
    import org.dreambot.api.wrappers.widgets.Menu;
    import org.dreambot.core.Instance;
    
    import javax.management.Attribute;
    import javax.management.AttributeList;
    import javax.management.MBeanServer;
    import javax.management.ObjectName;
    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.*;
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.lang.management.ManagementFactory;
    import java.net.URL;
    import java.text.DecimalFormat;
    import java.text.NumberFormat;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.concurrent.TimeUnit;
    
    // This is a script-helper class designed to make generic actions easier to code across many scripts.
    public class Zen {
        public AbstractScript s; // This holds the reference to the main script
        public ZenAntiBan ban; // This holds the reference to the anti-ban system
        public ZenGUI gui; // This holds the reference to the script settings GUI
        public final int GUI_X = 10; // GUI start X for script paint()
        public final int GUI_Y = 70; // GUI start Y for script paint()
        private final int IMAGE_X = 471; // Image X coordinate for paint()
        private final int IMAGE_Y = 433; // Image Y coordinate for paint()
        public String STATUS = "#Loading"; // Current script status (# before text = text color red)
        public long START_TIME; // Time script was started (used for stopping script after max duration)
        private long LAG_START_TIME; // This is used to calculating progressive lag multiplier
        private HashMap<Skill, Integer> START_LEVELS = new HashMap<>(); // Starting levels (used for calculating individual levels gained)
        private HashMap<Skill, Integer> START_EXP = new HashMap<>(); // Starting exps (used for calculating individual EXP gained)
        private int STARTING_TOTAL_EXP = 0; // Used for calculating total exp gained
        private int STARTING_TOTAL_LEVELS; // Used for calculating total levels gained
        private int HOPS_PAST_5_MINUTES = 0; // This is how many times we've world-hopped in the past 5 minutes
        private long LAST_WORLD_HOP = 0L; // This is used to throttle world hopping to prevent the bot flipping out if all the worlds are full of players
        private int MOUSE_SPEED; // This is the mouse speed (which is set randomly at start of script to help with anti-patterning)
        private int MAX_RUNTIME_MINUTES = -1; // This is the maximum amount of time the script should run for (used for calculating progressive lag multiplier + max duration)
        private boolean CHANGED_MOUSE_LAG = false; // This is set to true when we reach our initial max lag multiplier. Slows the mouse speed slightly
        private boolean CHANGED_MOUSE_LAG2 = false; // This is set to true when we reach our second max lag multiplier. Slows the mouse speed slightly
        private long LAST_CPU_CHECK = 0L; // This is used to throttle CPU load checks to avoid unnecessary memory usage in monitoring the memory usgage which would be fucking dumb
        private double LAST_CPU_LOAD = -1; // % of CPU load - used to store the last CPU load value in between checks for display purposes
        private int WORLD_HOPS = 0; // Total number of times we have world-hopped
        private Image icon = null; // Icon image to draw to game UI
        private String EXP_TIL_LVL = null; // Last EXP til Level string (store for UI display)
        private int RUN_THRESHOLD; // The threshold that our energy must reach in order to turn run on
        public long PAUSE_TIME = 0L; // This is the accumulated time that the script has been paused for
        // Item IDs
        public static int ITEM_COINS = 995; // Coins ID
        public static int ITEM_BONES = 526; // Bones ID
        // Setting IDs
        public int COMBAT_STYLE_SETTING = 43; // The widget ID for the combat style menu
        // Various inventory clicking patterns
        public int CLICK_ORDER1[] = {0, 1, 2, 3, 7, 6, 5, 4, 8, 9, 10, 11, 15, 14, 13, 12, 16, 17, 18, 19, 23, 22, 21, 20, 24, 25, 26, 27};
        public int CLICK_ORDER2[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27};
        public int CLICK_ORDER3[] = {0, 4, 1, 5, 2, 6, 3, 7, 11, 15, 10, 14, 9, 13, 8, 12, 16, 20, 17, 21, 18, 22, 19, 23, 27, 26, 25, 24};
        public int CLICK_ORDER4[] = {0, 4, 8, 12, 16, 20, 24, 25, 21, 17, 13, 9, 5, 1, 2, 6, 10, 14, 18, 22, 26, 3, 7, 11, 15, 19, 23, 27};
        public int CLICK_ORDER5[] = {0, 1, 4, 5, 8, 9, 12, 13, 16, 17, 20, 21, 24, 25, 2, 3, 6, 7, 10, 11, 14, 15, 18, 19, 22, 23, 26, 27};
        // F2P world list
        public int[] F2P_WORLDS = {1, 8, 16, 26, 35, 82, 83, 84, 93, 94, 117, 118, 124};
        private ArrayList<Integer> RECENT_WORLD_HOPS = new ArrayList<>();
        // Items that we should not drop
        public ArrayList<String> DONT_DROP = new ArrayList<>();
        public ArrayList<String> DONT_DROP_NAMES = new ArrayList<>();
        // Grand Exchange variables
        private static final String BASE_GE_URL = "http://services.runescape.com/m=itemdb_oldschool/api/catalogue/detail.json?item="; // Website for GE
        private static final int MILLION_GP = 1000000; // Used for converting 1m to 1000000
        private static final int THOUSAND_GP = 1000; // Used for converting 1k to 1000
        // Widget Coordinates
        public final Point STATS_WIDGET = new Point(577, 186); // Stats menu
        public final Point INVENTORY_WIDGET = new Point(643, 185); // Inventory menu
        public final Point LOGOUT_WIDGET = new Point(635, 475); // Logout menu
        public final Point LOGOUT_BUTTON_WIDGET = new Point(638, 432); // Logout button
        public final Point COMBAT_WIDGET = new Point(543, 186); // Combat style menu
        public final Point MAGIC_WIDGET = new Point(742, 186); // Magic menu
        public final Point ATTACK_STYLE_WIDGET = new Point(602, 273); // Attack combat mode
        public final Point STRENGTH_STYLE_WIDGET = new Point(688, 272); // Strength combat mode
        public final Point STRENGTH_OR_DEFENCE_STYLE_WIDGET = new Point(603, 326); // This can be defence or strength depending on weapon (combat mode)
        public final Point DEFENCE_STYLE_WIDGET = new Point(687, 326); // This is only Defence combat mode
        public final Point DEATH_CLOSE = new Point(262, 315); // This is the 'Never show this again' coordinates for the death screen
        public final Point DIALOGUE_CONTINUE = new Point(290, 445); // Middle of continue dialogue button
        public final Point OPTIONS_WIDGET = new Point(665, 472); // Options widget location
        public final Point CONTROLS_WIDGET = new Point(700, 215); // Controls widget location
        public final Point SHIFT_DROP_WIDGET = new Point(592, 300); // Shift drop control widget location
        public final Point COOKING_WIDGET = new Point(260, 430); // Cooking widget location
        public final int[] COOKING_WIDGET_CHILD = { 270, 5 };
        // EXP Popup variables
        private boolean showExpPopup = false; // If true, the EXP popup draws to the screen
        private long expPopupStart = 0L; // This is used to determine when the last EXP gain happened
        private int xpGained = 0; // This tracks the EXP gain to display
        private int lastExpPopupTotal = 0; // This tracks our last total exp so that we can detect when our new total exp changes
        private double expY = 160; // This is the Y coordinate of the EXP popup (by decrementing, we can make the popup float to the top of the screen)
        // Stat widget coordinates (in order of DB API listing for Skill array, for sake of convenience)
        public final Point[] STAT_WIDGET = {
                new Point(550, 210), // Attack
                new Point(550, 270), // Defence
                new Point(550, 240), // Strength
                new Point(612, 210), // Hits
                new Point(550, 304), // Ranged
                new Point(550, 336), // Prayer
                new Point(350, 370), // Magic
                new Point(367, 304), // Cooking
                new Point(676, 368), // Woodcut
                new Point(613, 369), // Fletching
                new Point(677, 273), // Fishing
                new Point(676, 336), // Firemaking
                new Point(614, 337), // Crafting
                new Point(677, 240), // Smithing
                new Point(677, 209), // Mining
                new Point(613, 271), // Herblore
                new Point(614, 240), // Agility
                new Point(614, 304), // Thieving
                new Point(614, 401), // Slayer
                new Point(676, 400), // Farming
                new Point(550, 400), // Runecrafting
                new Point(613, 432), // Hunter
                new Point(550, 432), // Construction
        };
    
        public Zen(AbstractScript script) {
            this(script, true);
        }
    
        // Creates the script-helper class Zen
        public Zen(AbstractScript script, boolean showGui) {
            LAG_START_TIME = System.currentTimeMillis();
            this.s = script;
            //this.MAX_RUNTIME_MINUTES = this.MAX_RUNTIME_MINUTES + r(0, 30);
            this.RUN_THRESHOLD = r(50, 99);
            this.STARTING_TOTAL_LEVELS = s.getSkills().getTotalLevel();
            Skill[] skills = Skill.values();
            for (Skill skill : skills) {
                START_LEVELS.put(skill, s.getSkills().getRealLevel(skill));
                START_EXP.put(skill, s.getSkills().getExperience(skill));
                STARTING_TOTAL_EXP += s.getSkills().getExperience(skill);
            }
            s.getMouse().getMouseSettings().setWordsPerMinute(rd(50.0, 100.0));
            this.MOUSE_SPEED = r(5, 6);
            s.getMouse().getMouseSettings().setSpeed(MOUSE_SPEED);
            s.log("[" + getScriptName() + "] Started.");
            // Setup anti-ban
            ban = new ZenAntiBan(this);
    
            if(showGui) {
                gui = new ZenGUI(this);
                gui.addPanel(s.getManifest().name(), "Script settings");
                gui.addPanel("Anti-Ban", "Anti-Ban settings");
                gui.addPanel("World Hopper", "World hop settings");
                setupUI();
            }
        }
    
        // Adds generic panel settings to script GUI
        private void setupUI() {
            // Add generic script settings
            gui.addBooleanInput(0, "Show EXP Popups", "exppopup", true);
            gui.addBooleanInput(0, "Keep Starting Items", "items", false);
            gui.addBooleanInput(0, "Shift Drop", "shift", "Use shift-drop?", true);
            gui.addStringInput(0, "Don't Drop List", "dontdrop", "Separate with commas - (eg. bronze sword,rune,hammer)", "");
            gui.addIntegerInput(1, "Max Runtime (mins)", "maxruntime", -1, -1, 9999);
            gui.addBooleanInput(1, "Debug", "debug", false);
            gui.addBooleanInput(1, "Use ChatBot", "chatbot", true);
            gui.addBooleanInput(1, "Only Reply to Username/Flags", "chatbotusername", true);
            gui.addStringInput(1, "Chat Flags (Separate by ,)", "chatflags", "");
            gui.addStringInput(1, "CleverBot API Key", "chatkey", "If you have purchasead a CleverBot API Key, enter it here", "");
            gui.addSliderInput(1, "ChatBot Limit (mins)", "chatbotrate", 20, 5, 100, 25, 5);
            gui.addSliderInput(1, "Anti-Ban Activity", "antibanrate", 50, 0, 100, 25, 5);
            gui.addBooleanInput(2, "World Hop", "worldhop", "Change worlds if another player is detected?", false);
            gui.addBooleanInput(2, "F2P", "f2p", "Hop to Free worlds?", true);
            gui.addBooleanInput(2, "P2P", "p2p", "Hop to Members worlds?", false);
            gui.addIntegerInput(2, "Player Distance", "pdistance", "Hop worlds if a player gets within X tiles of you", 6, 0, 50);
            gui.setTooltip("items", "Do not drop any items that start out in your inventory?");
            gui.setTooltip("maxruntime", "This stops the script after the specified time (-1 to disable)");
            gui.setTooltip("debug", "Print verbose script information to console?");
            gui.setTooltip("chatbot", "Use automatic chatbot replies?");
            gui.setTooltip("chatflags", "The chatbot will prioritize replying to any keywords you put here (optional)");
            gui.setTooltip("antibanrate", "This affects how frequently the anti-ban system performs actions (0 = disabled)");
            gui.setTooltip("chatbotrate", "This affects how frequently the ChatBot can reply (5 = no more than every 5 minutes)");
            gui.setTooltip("chatbotusername", "If selected, the ChatBot will only reply when your username or a given chat flag is mentioned");
        }
    
        // Shows the GUI and waits until input has been received
        public boolean showGUI() {
            boolean run = gui.show();
    
            // Setup main script variables
            if(run) {
                setMaxRuntime(gui.getInt("maxruntime"));
            }
    
            return run;
        }
    
        // Add any items that exist in our inventory to the Don't Drop list
        public void saveStartingItems() {
            if(gui.getBoolean("items")) {
                for(Item i : s.getInventory().all()) {
                    if (i != null)
                        dontDrop(i.getID());
                }
            }
        }
    
        // Sets the start time for the script (called by GUI when it is closed)
        public void setStartTime() {
            this.START_TIME = System.currentTimeMillis();
        }
    
        // Adds the given item to the don't drop list
        public void dontDrop(int id) {
            if(!DONT_DROP.contains("" + id))
                DONT_DROP.add("" + id);
        }
    
        // Adds the given item to the don't drop list
        public void dontDrop(String name) {
            if(!DONT_DROP_NAMES.contains(name))
                DONT_DROP_NAMES.add(name);
        }
    
        // World hops if necessary
        public int checkForWorldHop() {
            if(!gui.getBoolean("worldhop"))
                return -1;
    
            // If there are players in the vicinity, hop worlds
            if(getBoolean("worldhop") && s.getPlayers().all().size() > 1) {
                for(Player p : s.getPlayers().all()) {
                    if (!p.equals(s.getLocalPlayer()) && p.distance(s.getLocalPlayer()) < gui.getInt("pdistance") && !p.isMoving()) {
                        //print("Player detected too close - " + p.distance(getLocalPlayer()));
                        if(hopWorlds()) // TODO: Only do this if player lingers for longer than a few seconds?
                            return rh(3000, 6000);
                        break;
                    }
                }
            }
    
            return -1;
        }
    
        // Returns the anti-ban system
        public ZenAntiBan getAntiBan() {
            return ban;
        }
    
        // Returns getBoolean from the GUI
        public boolean getBoolean(String key) {
            return gui.getBoolean(key);
        }
    
        // Returns getBoolean from the GUI
        public int getInt(String key) {
            return gui.getInt(key);
        }
    
        // Returns getBoolean from the GUI
        public String getString(String key) {
            return gui.getString(key);
        }
    
        // Set the max runtime of the script
        public void setMaxRuntime(int mins) {
            this.MAX_RUNTIME_MINUTES = mins;
        }
    
        // Returns the script this helper class is attached to
        public AbstractScript getScript() {
            return s;
        }
    
        // Continues the dialogue. If mouse is close to button, 75% of the time it will click it. Otherwise it will spacebar
        public boolean continueDialogue() {
            if(s.getDialogues().canContinue()) {
                if(getMouseDistance(DIALOGUE_CONTINUE) <= 50 && r(0, 100) < 75)
                    s.getDialogues().clickContinue();
                else {
                    if (r(1, 5) == 2)
                        s.getDialogues().continueDialogue();
                    else
                        s.getDialogues().clickContinue();
                }
    
                sleep(10, 250);
                return true;
            }
    
            return false;
        }
    
        // Return how far the mouse is from a given point
        public double getMouseDistance(Point p) {
            Point mouse = s.getMouse().getPosition();
            return Math.sqrt(Math.pow(mouse.getX() - p.getX(), 2) + Math.pow(mouse.getY() - p.getY(), 2));
        }
    
        // Returns the Skill array index for the given Skill
        public int getSkillIndex(Skill skill) {
            Skill[] skills = Skill.values();
            for(int i = 0; i < skills.length; i++) {
                Skill s = skills[i];
                if(s.getName().equals(skill.getName()))
                    return i;
            }
    
            return -1;
        }
    
        // Returns how many minutes the script has been running
        public int getMinsRunning() {
            return (int) (((System.currentTimeMillis() - START_TIME - PAUSE_TIME) / 1000) / 60);
        }
    
        // Returns the preset mouse speed
        public int getMouseSpeed() {
            return MOUSE_SPEED;
        }
    
        // Gets a random double
        public static double rd(double min, double max) {
            return (Math.random() * ((max - min) + 1)) + min;
        }
    
        // Get levels gained for given skill
        public int getLevelsGained(Skill skill) {
            return s.getSkills().getRealLevel(skill) - START_LEVELS.get(skill);
        }
    
        // Returns the current 'real' level for the given skill
        public int getLevel(Skill skill) { return s.getSkills().getRealLevel(skill); }
    
        // Get EXP gained for given skill
        public int getExpGained(Skill skill) {
            return s.getSkills().getExperience(skill) - START_EXP.get(skill);
        }
    
        // Returns total EXP gained
        public int getExpGained() {
            return getTotalExp() - STARTING_TOTAL_EXP;
        }
    
        // Returns total EXP gained
        public int getTotalExp() {
            Skill[] skills = Skill.values();
            int exp = 0;
            for (Skill skill : skills)
                exp += s.getSkills().getExperience(skill);
    
            return exp;
        }
    
        // Get total levels gained
        public int getLevelsGained() {
            return s.getSkills().getTotalLevel() - STARTING_TOTAL_LEVELS;
        }
    
        // Sets the current displayed status and prints it to the debug console
        public void setStatus(String status) {
            if (!status.equals(STATUS)) {
                STATUS = status;
                ban.setStatus("");
                debug(status);
            }
        }
    
        // Returns the max runtime for this script
        public int getMaxRuntime() {
            return MAX_RUNTIME_MINUTES;
        }
    
        // Prints only if debug is enabled
        public void debug(Object o) {
            if(gui.finished() && getBoolean("debug"))
                print(o);
        }
    
        // Returns the lag multiplier (increases as script runs longer)
        public double getLagMultiplier() {
            double minutesRunning = (double) ((System.currentTimeMillis() - LAG_START_TIME) / 60000);
            double percent = (minutesRunning / (double)(MAX_RUNTIME_MINUTES == -1 ? 480 : MAX_RUNTIME_MINUTES));
    
            if(percent >= 0.5) {
                // Slow the mouse down a bit
                if(!CHANGED_MOUSE_LAG) {
                    MOUSE_SPEED = r(3, 4);
                    s.getMouse().getMouseSettings().setSpeed(MOUSE_SPEED);
                    CHANGED_MOUSE_LAG = true;
                }
            }
    
            if(percent >= 0.8) {
                // Slow the mouse down a bit more
                if(!CHANGED_MOUSE_LAG2) {
                    MOUSE_SPEED = r(2, 3);
                    s.getMouse().getMouseSettings().setSpeed(MOUSE_SPEED);
                    CHANGED_MOUSE_LAG2 = true;
                }
            }
    
            if(percent > 1.0)
                percent = 1.0D;
    
            return percent;
        }
    
        // Returns true if we should break a loop to free the mouse
        public boolean freeMouse() {
            return s.isPaused() || s.getRandomManager().isSolving() || Instance.getInstance().isMouseInputEnabled();
        }
    
        // Walks to the given area in a human-like manner
        public boolean walk(Area area) {
            return walk(area, true);
        }
    
        // Walks to the given area in a human-like manner
        public boolean walk(Area area, boolean walkOnScreen) {
            Tile t = area.getRandomTile();
            long lastIdle = System.currentTimeMillis();
            // Use a loop to make using this method in scripts easier
            while(!area.contains(s.getLocalPlayer())) {
                if(freeMouse() || !clientLoaded() || s.getLocalPlayer().getZ() != t.getZ() || s.getLocalPlayer().distance(t) >= 10000)
                    return false;
    
                STATUS = "Walking (" + t.getX() + "," + t.getY() + ") - " + (int)(s.getLocalPlayer().distance(t));
                boolean walkedOnScreen = false;
                if(!s.getMap().canReach(t))
                    t = area.getRandomTile();
                if(t == null || s.getWalking() == null)
                    continue;
    
                try {
                    if (s.getLocalPlayer().distance(t) <= 10 && s.getMap().canReach(t) && !s.getLocalPlayer().isMoving() && walkOnScreen) {
                        s.getWalking().walkOnScreen(t);
                        walkedOnScreen = true;
                    } else if (s.getMap().canReach(t) && walkOnScreen)
                        s.getWalking().walkExact(t);
                    else
                        s.getWalking().walk(t);
                } catch(Exception e) {
                    s.getWalking().walk(t);
                }
    
                int r = r(1, 10 - getLagMultiplier() >= 0.25 ? 3 : 0);
                // Randomly idle after clicking the next tile (becomes more likely to happen the longer the script runs)
                if((s.getWalking().isRunEnabled() ? r == 1 : r <= 3) && System.currentTimeMillis() - lastIdle >= 15000 && !walkedOnScreen) {
                    int wait = rh(5000, 15000);
                    setStatus("#Idling for " + (wait / 1000) + " secs");
                    s.getMouse().moveMouseOutsideScreen();
                    s.sleep(wait);
                    lastIdle = System.currentTimeMillis();
                    continue;
                }
    
                sleep(250, 1000);
                s.sleepUntil(() -> s.getWalking().shouldWalk(r(3, 6)), rh(10000, 15000));
            }
    
            return true;
        }
    
        // Walks to the given Area
        public boolean walk(Area area, Filter<GameObject> filter) {
            Tile t = area.getRandomTile();
            // Use a loop to make using this method in scripts easier
            while(!area.contains(s.getLocalPlayer())) {
                if(freeMouse() || !clientLoaded() || s.getLocalPlayer().getZ() != t.getZ() || s.getLocalPlayer().distance(t) >= 10000)
                    return false;
    
                GameObject o = s.getGameObjects().closest(filter);
                if(o != null && s.getLocalPlayer().distance(o) <= 15 && s.getMap().canReach(o))
                    return true;
    
                STATUS = "Walking (" + t.getX() + "," + t.getY() + ") - " + (int)(s.getLocalPlayer().distance(t));
                if(!s.getMap().canReach(t))
                    t = area.getRandomTile();
                if(t == null || s.getWalking() == null)
                    continue;
                if(s.getLocalPlayer().distance(t) <= 10 && s.getMap().canReach(t) && !s.getLocalPlayer().isMoving())
                    s.getWalking().walkOnScreen(t);
                else
                if(s.getMap().canReach(t))
                    s.getWalking().walkExact(t);
                else
                    s.getWalking().walk(t);
    
                // Randomly idle after clicking the next tile
                if(r(1, 10) == 1) {
                    int wait = rh(5000, 15000);
                    setStatus("#Idling for " + (wait / 1000) + " secs");
                    s.getMouse().moveMouseOutsideScreen();
                    s.sleep(wait);
                    continue;
                }
    
                sleep(1000, 2000);
                s.sleepUntil(() -> s.getWalking().shouldWalk(r(3, 6)), rh(10000, 15000));
            }
    
            return true;
        }
    
        // Chooses any of the given answers if they pop up in the dialogue menu
        public boolean answer(String... answers) {
            if(s.getDialogues().getOptions() != null) {
                int reply = getOption(answers);
                if (reply != -1) {
                    if (r(1, 3) == 2)
                        s.getDialogues().clickOption(reply);
                    else
                        s.getDialogues().chooseOption(reply);
    
                    sleep(250, 500);
                    return true;
                } else {
                    // If we have been caught by a continuable dialogue, then continue it.
                    if (continueDialogue()) {
                        sleep(2500, 3000);
                        return true;
                    }
    
                    print("Error - did not know how to continue NPC dialogue!");
                }
            }
    
            return false;
        }
    
        // Returns the current script status
        public String getStatus() {
            return STATUS;
        }
    
        // Returns our player's X coordinate
        public int getX() {
            return s.getLocalPlayer().getX();
        }
    
        // Returns our player's Y coordinate
        public int getY() {
            return s.getLocalPlayer().getY();
        }
    
        // This method opens the inventory
        public boolean openInventory() {
            if (s.getTabs().getOpen() != Tab.INVENTORY) {
                // Sometimes use hot keys, sometimes use mouse
                if (Calculations.random(1, 3) == 2)
                    s.getTabs().open(Tab.INVENTORY);
                else {
                    int x = (int) INVENTORY_WIDGET.getX() + r(0, 10);
                    int y = (int) INVENTORY_WIDGET.getY() + r(0, 10);
                    s.getMouse().move(new Point(x, y));
                    s.getMouse().click();
                }
    
                sleep(50, 250);
            }
    
            return s.getTabs().getOpen() == Tab.INVENTORY;
        }
    
        // This method opens the options menu
        public boolean openOptions() {
            if (s.getTabs().getOpen() != Tab.OPTIONS) {
                // Sometimes use hot keys, sometimes use mouse
                if (Calculations.random(1, 3) == 2)
                    s.getTabs().open(Tab.OPTIONS);
                else {
                    int x = (int) OPTIONS_WIDGET.getX() + r(0, 10);
                    int y = (int) OPTIONS_WIDGET.getY() + r(0, 10);
                    s.getMouse().move(new Point(x, y));
                    s.getMouse().click();
                }
    
                sleep(50, 250);
            }
    
            return s.getTabs().getOpen() == Tab.OPTIONS;
        }
    
        // This method opens the magic menu
        public boolean openMagic() {
            if (s.getTabs().getOpen() != Tab.MAGIC) {
                // Sometimes use hot keys, sometimes use mouse
                if (Calculations.random(1, 3) == 2)
                    s.getTabs().open(Tab.MAGIC);
                else {
                    int x = (int) MAGIC_WIDGET.getX() + r(0, 10);
                    int y = (int) MAGIC_WIDGET.getY() + r(0, 10);
                    s.getMouse().move(new Point(x, y));
                    s.getMouse().click();
                }
    
                sleep(50, 250);
            }
    
            return s.getTabs().getOpen() == Tab.MAGIC;
        }
    
        // Gets the given answer for the given set of replies if the NPC Dialogue is open
        public int getOption(String... replies) {
            String[] options = s.getDialogues().getOptions();
            if (options == null)
                return -1;
    
            int reply = -1;
            for (int i = 0; i < options.length; i++) {
                String s = options[i].toLowerCase();
                for (String replyStr : replies) {
                    if (s.contains(replyStr)) {
                        return i + 1;
                    }
                }
            }
    
            return -1;
        }
    
        // This method opens the stats menu
        public boolean openStats() {
            if (s.getTabs().getOpen() != Tab.STATS) {
                // Sometimes use hot keys, sometimes use mouse
                if (Calculations.random(1, 3) == 2)
                    s.getSkills().open();
                else {
                    int x = (int) STATS_WIDGET.getX() + r(0, 10);
                    int y = (int) STATS_WIDGET.getY() + r(0, 10);
                    s.getMouse().move(new Point(x, y));
                    sleep(0, 50);
                    s.getMouse().click();
                }
    
                sleep(50, 250);
            }
    
            return s.getTabs().getOpen() == Tab.STATS;
        }
    
        // Opens the  combat menu then waits for a second
        public boolean openCombat() {
            if (s.getTabs().getOpen() != Tab.COMBAT) {
                // Sometimes use hot keys, sometimes use mouse
                if (Calculations.random(1, 3) == 2)
                    s.getTabs().open(Tab.COMBAT);
                else {
                    int x = (int) COMBAT_WIDGET.getX() + Calculations.random(0, 10);
                    int y = (int) COMBAT_WIDGET.getY() + Calculations.random(0, 10);
                    s.getMouse().move(new Point(x, y));
                    sleep(0, 50);
                    s.getMouse().click();
                }
    
                sleep(50, 250);
            }
    
            return s.getTabs().getOpen() == Tab.COMBAT;
        }
    
        // Opens the logout menu
        public boolean openLogout() {
            if (s.getTabs().getOpen() != Tab.LOGOUT) {
                if(r(1, 3) == 2) {
                    s.getTabs().open(Tab.LOGOUT);
                } else {
                    int x = (int) LOGOUT_WIDGET.getX() + Calculations.random(0, 10);
                    int y = (int) LOGOUT_WIDGET.getY() + Calculations.random(0, 10);
                    s.getMouse().move(new Point(x, y));
                    sleep(0, 50);
                    s.getMouse().click();
                }
    
                sleep(50, 250);
            } else
                return true;
    
            return s.getTabs().getOpen() == Tab.LOGOUT;
        }
    
        // Logs out of the game
        public void logout() {
            // Disable autologin otherwise this method is pointless
            s.getRandomManager().disableSolver(RandomEvent.LOGIN);
    
            // If we fail to open the logout menu, just afk til logout.
            if(!openLogout())
                return;
    
            int x = (int) LOGOUT_BUTTON_WIDGET.getX() + r(0, 25);
            int y = (int) LOGOUT_BUTTON_WIDGET.getY() + r(0, 5);
            s.getMouse().move(new Point(x, y));
            sleep(0, 50);
            s.getMouse().click();
        }
    
        public boolean interactItems(String action) {
            boolean interacted = false;
            if(action.equals("Drop") && gui.getBoolean("shift") && !quickDropActive())
                turnOnShiftDrop();
    
            if(!s.getTabs().isOpen(Tab.INVENTORY))
                openInventory();
    
            // If quick drop is active, hold shift
            if(quickDropActive() && action.equals("Drop"))
                pressShift();
    
            // Randomize click order for anti-patterning
            int[] CLICK_ORDER = getClickOrder();
            Inventory inventory = s.getInventory();
            for (int i = 0; i < CLICK_ORDER.length; i++) {
                Item item = inventory.getItemInSlot(CLICK_ORDER[i]);
                if (item != null) {
                    if(action.equals("Drop") && itemsMatchID(item.getID(), DONT_DROP))
                        continue;
                    if(action.equals("Drop") && itemsMatch(item.getName(), DONT_DROP_NAMES))
                        continue;
                    if(action.equals("Drop") && nameContains(item.getName(), gui.getString("dontdrop").split(",")))
                        continue;
                    if(!action.equals("Drop") && !item.hasAction(action))
                        continue;
    
                    if(quickDropActive() && action.equals("Drop")) {
                        s.getMouse().click(item.getDestination());
                    } else
                        s.getInventory().slotInteract(CLICK_ORDER[i], r(0, 100) < 1 && action.equals("Drop") ? "Examine" : action);
    
                    interacted = true;
                    final int slot = CLICK_ORDER[i];
                    if (i < CLICK_ORDER.length - 1) {
                        // Declare inner loop, cycle through items and find the next item that has our specified Action
                        INNER_LOOP:
                        for(int j = i + 1; j < CLICK_ORDER.length; j++) {
                            Item nextItem = inventory.getItemInSlot(CLICK_ORDER[j]);
                            // If the item is found with the given Action, then move the mouse over it in anticipation of the next click (like a human would)
                            if (nextItem != null) {
                                if(action.equals("Drop") && itemsMatchID(nextItem.getID(), DONT_DROP))
                                    continue INNER_LOOP;
                                if(action.equals("Drop") && itemsMatch(nextItem.getName(), DONT_DROP_NAMES))
                                    continue INNER_LOOP;
                                if(action.equals("Drop") && nameContains(nextItem.getName(), gui.getString("dontdrop").split(",")))
                                    continue INNER_LOOP;
                                if(!action.equals("Drop") && !nextItem.hasAction(action))
                                    continue INNER_LOOP;
                                // Disable mouse input while doing this action in a loop
                                Instance.getInstance().setMouseInputEnabled(false);
                                s.getMouse().move(nextItem.getDestination());
                                s.sleepUntil(() -> s.getInventory().getItemInSlot(slot) == null, r(2000, 3000));
                                // If we found a valid item to hover over, break this inner loop and resume the main outer loop.
                                break INNER_LOOP;
                            }
                        }
                    }
    
                    s.sleep(r(1, 10));
    
                    // If we are paused or solving a random event, stop this loop
                    if(freeMouse())
                        break;
                }
            }
    
            // If quick drop is active, release shift
            if(quickDropActive() && action.equals("Drop"))
                releaseShift();
    
            return interacted;
        }
    
        // Interacts with items in a randomized pattern (and randomly Examines sometimes)
        public boolean interactItemsOnly(String action, int... only) {
            if(action.equals("Drop") && gui.getBoolean("shift") && !quickDropActive())
                turnOnShiftDrop();
    
            // If quick drop is active, hold shift
            if(quickDropActive() && action.equals("Drop")) {
                pressShift();
            }
    
            if (!s.getTabs().isOpen(Tab.INVENTORY))
                openInventory();
    
            boolean interacted = false;
            // Randomize click order for anti-patterning
            int[] CLICK_ORDER = getClickOrder();
            Inventory inventory = s.getInventory();
            for (int i = 0; i < CLICK_ORDER.length; i++) {
                Item item = inventory.getItemInSlot(CLICK_ORDER[i]);
                if (item != null) {
                    if(!itemsMatch(item.getID(), only))
                        continue;
                    if(action.equals("Drop") && itemsMatchID(item.getID(), DONT_DROP))
                        continue;
                    if(action.equals("Drop") && itemsMatch(item.getName(), DONT_DROP_NAMES))
                        continue;
                    if(!action.equals("Drop") && !item.hasAction(action))
                        continue;
    
                    if(quickDropActive() && action.equals("Drop")) {
                        s.getMouse().click(item.getDestination());
                    } else
                        s.getInventory().slotInteract(CLICK_ORDER[i], r(0, 100) < 1 && action.equals("Drop") ? "Examine" : action);
    
                    interacted = true;
                    final int slot = CLICK_ORDER[i];
                    if (i < CLICK_ORDER.length - 1) {
                        // Declare inner loop, cycle through items and find the next item that has our specified Action
                        INNER_LOOP:
                        for(int j = i + 1; j < CLICK_ORDER.length; j++) {
                            Item nextItem = inventory.getItemInSlot(CLICK_ORDER[j]);
                            // If the item is found with the given Action, then move the mouse over it in anticipation of the next click (like a human would)
                            if (nextItem != null) {
                                if(!itemsMatch(nextItem.getID(), only))
                                    continue INNER_LOOP;
                                if(action.equals("Drop") && itemsMatchID(nextItem.getID(), DONT_DROP))
                                    continue INNER_LOOP;
                                if(action.equals("Drop") && itemsMatch(nextItem.getName(), DONT_DROP_NAMES))
                                    continue INNER_LOOP;
                                if(!action.equals("Drop") && !nextItem.hasAction(action))
                                    continue INNER_LOOP;
                                // Disable mouse input while doing this action in a loop
                                Instance.getInstance().setMouseInputEnabled(false);
                                s.getMouse().move(nextItem.getDestination());
                                s.sleepUntil(() -> s.getInventory().getItemInSlot(slot) == null, r(2000, 3000));
                                // If we found a valid item to hover over, break this inner loop and resume the main outer loop.
                                break INNER_LOOP;
                            }
                        }
                    }
    
                    s.sleep(r(1, 10));
    
                    // If we are paused or solving a random event, stop this loop
                    if(freeMouse())
                        break;
                }
            }
    
            // If quick drop is active, release shift
            if(quickDropActive() && action.equals("Drop")) {
                releaseShift();
            }
    
            return interacted;
        }
    
        // Interacts with items in a randomized pattern (and randomly Examines sometimes)
        public boolean interactItemsExcept(String action, int... except) {
            if(action.equals("Drop") && gui.getBoolean("shift") && !quickDropActive())
                turnOnShiftDrop();
    
            // If quick drop is active, hold shift
            if(quickDropActive() && action.equals("Drop")) {
                pressShift();
            }
    
            if (!s.getTabs().isOpen(Tab.INVENTORY))
                openInventory();
    
            boolean interacted = false;
            // Randomize click order for anti-patterning
            int[] CLICK_ORDER = getClickOrder();
            Inventory inventory = s.getInventory();
            for (int i = 0; i < CLICK_ORDER.length; i++) {
                Item item = inventory.getItemInSlot(CLICK_ORDER[i]);
                if (item != null) {
                    if(itemsMatch(item.getID(), except))
                        continue;
                    if(action.equals("Drop") && itemsMatchID(item.getID(), DONT_DROP))
                        continue;
                    if(action.equals("Drop") && itemsMatch(item.getName(), DONT_DROP_NAMES))
                        continue;
                    if(action.equals("Drop") && nameContains(item.getName(), gui.getString("dontdrop").split(",")))
                        continue;
                    if(!action.equals("Drop") && !item.hasAction(action))
                        continue;
    
                    if(quickDropActive() && action.equals("Drop")) {
                        s.getMouse().click(item.getDestination());
                    } else
                        s.getInventory().slotInteract(CLICK_ORDER[i], r(0, 100) < 1 && action.equals("Drop") ? "Examine" : action);
    
                    interacted = true;
                    final int slot = CLICK_ORDER[i];
                    if (i < CLICK_ORDER.length - 1) {
                        // Declare inner loop, cycle through items and find the next item that has our specified Action
                        INNER_LOOP:
                        for(int j = i + 1; j < CLICK_ORDER.length; j++) {
                            Item nextItem = inventory.getItemInSlot(CLICK_ORDER[j]);
                            // If the item is found with the given Action, then move the mouse over it in anticipation of the next click (like a human would)
                            if (nextItem != null) {
                                if(itemsMatch(item.getID(), except))
                                    continue;
                                if(action.equals("Drop") && itemsMatchID(nextItem.getID(), DONT_DROP))
                                    continue INNER_LOOP;
                                if(action.equals("Drop") && itemsMatch(nextItem.getName(), DONT_DROP_NAMES))
                                    continue INNER_LOOP;
                                if(action.equals("Drop") && nameContains(nextItem.getName(), gui.getString("dontdrop").split(",")))
                                    continue INNER_LOOP;
                                if(!action.equals("Drop") && !nextItem.hasAction(action))
                                    continue INNER_LOOP;
                                // Disable mouse input while doing this action in a loop
                                Instance.getInstance().setMouseInputEnabled(false);
                                s.getMouse().move(nextItem.getDestination());
                                s.sleepUntil(() -> s.getInventory().getItemInSlot(slot) == null, r(2000, 3000));
                                // If we found a valid item to hover over, break this inner loop and resume the main outer loop.
                                break INNER_LOOP;
                            }
                        }
                    }
    
                    s.sleep(r(1, 10));
    
                    // If we are paused or solving a random event, stop this loop
                    if(freeMouse())
                        break;
                }
            }
    
            // If quick drop is active, release shift
            if(quickDropActive() && action.equals("Drop")) {
                releaseShift();
            }
    
            return interacted;
        }
    
        public void pressShift() {
            Instance instance = s.getClient().getInstance();
            Canvas canvas = instance.getCanvas();
            instance.setKeyboardInputEnabled(true);
            canvas.dispatchEvent(new KeyEvent(canvas, KeyEvent.KEY_PRESSED, System.currentTimeMillis(), 0, KeyEvent.VK_SHIFT, KeyEvent.CHAR_UNDEFINED));
        }
    
        public void releaseShift() {
            Instance instance = s.getClient().getInstance();
            Canvas canvas = instance.getCanvas();
            canvas.dispatchEvent(new KeyEvent(canvas, KeyEvent.KEY_RELEASED, System.currentTimeMillis(), 0, KeyEvent.VK_SHIFT, KeyEvent.CHAR_UNDEFINED));
            instance.setKeyboardInputEnabled(false);
        }
    
        // Returns a random inventory slot clicking order
        public int[] getClickOrder() {
            // Randomize click order for anti-patterning
            int random = Calculations.random(1, 5);
            int[] CLICK_ORDER = new int[CLICK_ORDER1.length];
            switch (random) {
                case 1:
                    CLICK_ORDER = CLICK_ORDER1;
                    break;
                case 2:
                    CLICK_ORDER = CLICK_ORDER2;
                    break;
                case 3:
                    CLICK_ORDER = CLICK_ORDER3;
                    break;
                case 4:
                    CLICK_ORDER = CLICK_ORDER4;
                    break;
                case 5:
                    CLICK_ORDER = CLICK_ORDER5;
                    break;
            }
    
            // Randomly flip the array order sometimes to reverse the pattern
            if (Calculations.random(1, 3) == 2) {
                int[] flippedArray = new int[CLICK_ORDER.length];
                int x = 0;
                for (int i = flippedArray.length - 1; i > 0; i--)
                    flippedArray[i] = CLICK_ORDER[x++];
                CLICK_ORDER = flippedArray;
            }
    
            return CLICK_ORDER;
        }
    
        // Returns whether or not our inventory contains one of the given IDs
        public boolean hasOneOfTheseItems(int... ids) {
            for(Item i : s.getInventory().all()) {
                for(int id : ids) {
                    if (i != null && i.getID() == id)
                        return true;
                }
            }
    
            return false;
        }
    
        // Returns whether or not the given item matches any of the given ids
        public boolean itemsMatch(int id, int... ids) {
            if(id == -1)
                return false;
    
            for (int i : ids) {
                if(i == -1)
                    return false;
                if (id == i)
                    return true;
            }
    
            return false;
        }
    
        // Returns whether or not the given item matches any of the given ids
        public boolean itemsMatchID(int id, ArrayList<String> ids) {
            for (String  s : ids) {
                int i = Integer.parseInt(s);
                if (id == i)
                    return true;
            }
    
            return false;
        }
    
        // Returns whether or not the given item matches any of the given ids (ArrayList version)
        public boolean itemsMatch(int id, ArrayList<Integer> ids) {
            for (int i : ids) {
                if (id == i)
                    return true;
            }
    
            return false;
        }
    
        // Returns whether or not the given item matches any of the given ids (ArrayList version)
        public boolean itemsMatch(String name, ArrayList<String> names) {
            for (String s : names) {
                if (!s.equals("") && name.toLowerCase().contains(s.toLowerCase()))
                    return true;
            }
    
            return false;
        }
    
        // Returns whether or not the given string matches any of the given names
        public boolean nameMatches(String name, String... compare) {
            for(String s : compare) {
                if(s.equalsIgnoreCase(name))
                    return true;
            }
    
            return false;
        }
    
        // Returns whether or not the given string contains any of the given names
        public boolean nameContains(String name, String... compare) {
            for(String s : compare) {
                if(!s.equals("") && s.toLowerCase().contains(name.toLowerCase()))
                    return true;
            }
    
            return false;
        }
    
        // Returns a random item slot for the given id
        public int getRandomSlot(int id) {
            int slot = -1;
            if (s.getInventory().count(id) == 0)
                return slot;
    
            long start = System.currentTimeMillis();
            while (true) {
                if (System.currentTimeMillis() - start >= 500) {
                    print("ERROR: getRandomSlot(" + id + ") timed out!");
                    break;
                }
                int randomSlot = r(0, 27);
                Item i = s.getInventory().getItemInSlot(randomSlot);
                if (i != null && i.getID() == id)
                    return randomSlot;
            }
    
            return slot;
        }
    
        // Returns a random item slot for the given ids
        public int getRandomSlot(int... ids) {
            int slot = -1;
            long start = System.currentTimeMillis();
            while (true) {
                if (System.currentTimeMillis() - start >= 500) {
                    String idstr = "";
                    for (int id : ids)
                        idstr = idstr + id + ",";
                    print("ERROR: getRandomSlot(" + idstr + ") timed out!");
                    break;
                }
    
                int randomSlot = r(0, 27);
                Item i = s.getInventory().getItemInSlot(randomSlot);
                for (int id : ids) {
                    if (i != null && i.getID() == id)
                        return randomSlot;
                }
            }
    
            return slot;
        }
        // Returns a random item slot that is not on our don't drop list
        public int getRandomJunkSlot() {
            int slot = -1;
            long start = System.currentTimeMillis();
            while (true) {
                if (System.currentTimeMillis() - start >= 500) {
                    print("ERROR: getRandomJunkSlot() timed out!");
                    break;
                }
    
                int randomSlot = r(0, 27);
                Item i = s.getInventory().getItemInSlot(randomSlot);
                if(itemsMatchID(i.getID(), DONT_DROP))
                    continue;
                if(itemsMatch(i.getName(), DONT_DROP_NAMES))
                    continue;
                if(nameContains(i.getName(), gui.getString("dontdrop").split(",")))
                    continue;
    
                return randomSlot;
            }
    
            return slot;
        }
    
        // Returns a random item slot for the given id
        public int getRandomSlotAction(String action) {
            int slot = -1;
    
            long start = System.currentTimeMillis();
            while (true) {
                if (System.currentTimeMillis() - start >= 500) {
                    //print("ERROR: getRandomSlot(" + id + ") timed out!");
                    break;
                }
                int randomSlot = r(0, 27);
                Item i = s.getInventory().getItemInSlot(randomSlot);
                if (i != null && i.hasAction(action))
                    return randomSlot;
            }
    
            return slot;
        }
    
        // Returns the inventory count of the given item ids
        public int countInventory(int... ids) {
            int count = 0;
            for (Item i : s.getInventory().all()) {
                for (int id : ids) {
                    if (i != null && i.getID() == id)
                        count++;
                }
            }
    
            return count;
        }
    
        // Returns how many items in the player's inventory contain the given word
        public int countInventory(String phrase) {
            int count = 0;
            for (Item i : s.getInventory().all()) {
                if (i != null && i.getName().toLowerCase().contains(phrase.toLowerCase()))
                    count++;
            }
    
            return count;
        }
    
        // Returns true or false if the given item phrase is found
        public boolean hasItem(String phrase) {
            return countInventory(phrase) > 0;
        }
    
        // Returns true if the player has the given item with the given action
        public boolean hasItemWithAction(String action) {
            for(Item i : s.getInventory().all()) {
                if(i != null && i.hasAction(action))
                    return true;
            }
    
            return false;
        }
    
        // Returns true or false if the given item IDs are found
    
        // This method turns Run on if our energy is above or equal to the given threshold
        public boolean turnOnRun() {
            if (!s.getWalking().isRunEnabled() && s.getWalking().getRunEnergy() >= r(RUN_THRESHOLD, 100)) {
                setStatus("#Turning run on");
                s.getWalking().toggleRun();
                RUN_THRESHOLD = r(5, 99);
                return true;
            }
    
            return false;
        }
    
        // Turns on shift drop if it is not already on
        public boolean turnOnShiftDrop() {
            if(openOptions()) {
                s.getMouse().move(new Point((int)CONTROLS_WIDGET.getX() + r(0, 20), (int)CONTROLS_WIDGET.getY() + r(0, 25)));
                sleep(0, 10);
                s.getMouse().click();
                sleep(30, 150);
                s.getMouse().move(new Point((int)SHIFT_DROP_WIDGET.getX() + r(0, 20), (int)SHIFT_DROP_WIDGET.getY() + r(0, 25)));
                sleep(0, 10);
                s.getMouse().click();
                sleep(250, 500);
                return quickDropActive();
            }
    
            return false;
        }
    
        // Returns whether or not shift drop is active
        public boolean quickDropActive() {
            return ((s.getPlayerSettings().getConfig(1055) >> 17) & 0x1) == 1 && gui.getBoolean("shift");
        }
    
        // This method sets the fighting style to whatever mode is desired
        public boolean setCombatStyle(int combatStyle) {
            int selectedStyle = s.getPlayerSettings().getConfig(COMBAT_STYLE_SETTING);
            // Check if we need to change our combat style
            if (selectedStyle != combatStyle) {
                if (s.getTabs().getOpen() != Tab.COMBAT)
                    openCombat();
    
                Point styleButton = null;
                if (combatStyle == 0) { // Attack
                    styleButton = ATTACK_STYLE_WIDGET;
                } else if (combatStyle == 1) { // Strength
                    styleButton = STRENGTH_STYLE_WIDGET;
                } else if (combatStyle == 3) { // Defence
                    if (s.getEquipment().isSlotEmpty(EquipmentSlot.WEAPON.getSlot()))
                        styleButton = STRENGTH_OR_DEFENCE_STYLE_WIDGET;
                    else
                        styleButton = DEFENCE_STYLE_WIDGET;
                }
    
                int x = (int) styleButton.getX() + r(0, 10);
                int y = (int) styleButton.getY() + r(0, 10);
                s.getMouse().move(new Point(x, y));
                s.getMouse().click();
                sleep(250, 550);
            }
    
            return s.getPlayerSettings().getConfig(COMBAT_STYLE_SETTING) == combatStyle;
        }
    
        // Returns a random number
        public int r(int x, int y) {
            return Calculations.random(x, y+1);
        }
    
        // Returns a random number with the human lag element added to the minimum wait time
        public int rh(int x, int y) {
            //return r(x + getHumanLag(), y + RAND + getHumanLag());
            return r(x + (int)(x*getLagMultiplier()), y + (int)(y*getLagMultiplier()));
        }
    
        // Gets the item ID for the given name
        public int getItemID(String name) {
            for(Item item : s.getInventory().all(i -> i != null && i.getName().toLowerCase().contains(name.toLowerCase())))
                return item.getID();
    
            return -1;
        }
    
        // Opens the shop with the default general store names (shop keeper, shop assistant)
        public boolean openShop() {
            return openShop("shop keeper", "shop assistant");
        }
    
        // Opens the shop with any of the given NPC names
        public boolean openShop(String... names) {
            for (NPC n : s.getNpcs().all(n -> n != null && n.hasAction("Trade") && s.getMap().canReach(n))) {
                for (String name : names) {
                    if (n.getName().toLowerCase().contains(name)) {
                        // Open shop
                        n.interact("Trade");
                        sleep(250, 1000);
                        s.sleepUntil(() -> s.getShop().isOpen(), rh(5000, 10000));
                        return true;
                    }
                }
            }
    
            return false;
        }
    
        // Returns a random F2P world
        public int getRandomWorldF2P() {
            int world = s.getWorlds().getRandomWorld(w -> w != null && !w.isMembers() && !w.isDeadmanMode() && !w.isHighRisk() && !w.isPVP() && s.getSkills().getTotalLevel() >= w.getMinimumLevel()).getID();
            while(RECENT_WORLD_HOPS.contains(world))
                world = s.getWorlds().getRandomWorld(w -> w != null && !w.isMembers() && !w.isDeadmanMode() && !w.isHighRisk() && !w.isPVP() && s.getSkills().getTotalLevel() >= w.getMinimumLevel()).getID();
    
            return world;
        }
    
        // Returns a random P2P world
        public int getRandomWorldP2P() {
            int world = s.getWorlds().getRandomWorld(w -> w != null && w.isMembers() && !w.isDeadmanMode() && !w.isHighRisk() && !w.isPVP() && s.getSkills().getTotalLevel() >= w.getMinimumLevel()).getID();
            while(RECENT_WORLD_HOPS.contains(world))
                world = s.getWorlds().getRandomWorld(w -> w != null && w.isMembers() && !w.isDeadmanMode() && !w.isHighRisk() && !w.isPVP() && s.getSkills().getTotalLevel() >= w.getMinimumLevel()).getID();
    
            return world;
        }
    
        // Returns a random world
        public int getRandomWorld() {
            int world = s.getWorlds().getRandomWorld(w -> w != null && !w.isHighRisk() && !w.isDeadmanMode() && s.getSkills().getTotalLevel() >= w.getMinimumLevel() && !w.isPVP()).getID();
            while(RECENT_WORLD_HOPS.contains(world))
                world = s.getWorlds().getRandomWorld(w -> w != null && !w.isHighRisk() && !w.isDeadmanMode() && s.getSkills().getTotalLevel() >= w.getMinimumLevel() && !w.isPVP()).getID();
    
            return world;
        }
    
        // Hops to a random F2P world
        public boolean hopWorlds() {
            long now = System.currentTimeMillis();
            if(now - LAST_WORLD_HOP <= 300000 && HOPS_PAST_5_MINUTES >= 10) {
                print("Hopped too many times - waiting for a few minutes");
                logout();
                s.getRandomManager().disableSolver(RandomEvent.LOGIN);
                sleep(60000, 120000);
                s.getRandomManager().enableSolver(RandomEvent.LOGIN);
                HOPS_PAST_5_MINUTES = 0;
                return false;
            }
    
            WORLD_HOPS++;
            boolean f2p = gui.getBoolean("f2p");
            boolean p2p = gui.getBoolean("p2p");
            int world;
            if(f2p && !p2p)
                world = getRandomWorldF2P();
            else
            if(!f2p && p2p)
                world = getRandomWorldP2P();
            else
                world = getRandomWorld();
    
            int oldWorld = s.getClient().getCurrentWorld();
            if(s.getWorldHopper().hopWorld(world)) {
                sleep(0, 50);
                s.getMouse().moveMouseOutsideScreen();
                sleep(4000, 5000);
                s.sleepUntil(() -> s.getLocalPlayer().exists() && s.getClient().isLoggedIn() && s.getClient().getCurrentWorld() != oldWorld, r(5000, 10000));
                HOPS_PAST_5_MINUTES++;
                RECENT_WORLD_HOPS.add(world);
                if(now - LAST_WORLD_HOP >= 300000) {
                    LAST_WORLD_HOP = System.currentTimeMillis();
                    HOPS_PAST_5_MINUTES = 0;
                    RECENT_WORLD_HOPS.clear();
                }
                return true;
            } else
                return false;
        }
    
        // Takes the given ground item, custom method to eliminate bug where bot will continually open the menu even after taking the item
        public boolean takeGroundItem(GroundItem i) {
            // Move mouse over the item
            s.getMouse().move(i);
            Menu menu = new Menu(s.getClient());
    
            // 75% of the time, just left-click the item
            if(r(0, 100) < 75 && menu.getDefaultAction().equals("Take")) {
                s.getMouse().click();
                s.sleepUntil(() -> !i.exists() || s.getLocalPlayer().distance(i) <= 1, r(1000, 2000));
                return true;
            }
    
            // Otherwise open right-click menu and find Take option for bones
            s.sleep(r(1, 100));
            menu.open();
            if(menu.contains("Take", i)) {
                menu.clickAction("Take", i);
                s.sleepUntil(() -> !i.exists() || s.getLocalPlayer().distance(i) <= 1, r(3000, 6000));
                return true;
            } else
                menu.close();
    
            return false;
        }
    
        // This returns whether or not we have exceeded our max runtime
        public boolean maxRuntime() {
            return MAX_RUNTIME_MINUTES != -1 && getMinsRunning() > MAX_RUNTIME_MINUTES;
        }
    
        // If client isn't loaded, we aren't logged in, we just hopped worlds or the user has not closed the GUI, wait.
        public void waitTilReady() {
            long t = System.currentTimeMillis();
            while(!clientLoaded()) {
                sleep(3000, 6000);
                if(gui.finished() && System.currentTimeMillis() - t >= 30000) {
                    print("Error: waitTilReady() timed out!");
                    break;
                }
            }
        }
    
        // This sleeps for the given time in MS between the given min and max (using the RandomHuman modifier)
        public void sleep(int min, int max) {
            s.sleep(rh(min, max));
        }
    
        // Returns whether or not the client is loaded and logged in
        public boolean clientLoaded() {
            return s.getLocalPlayer() != null && s.getClient().isLoggedIn() && System.currentTimeMillis() - LAST_WORLD_HOP >= 5000 && s.getClient().getGameState() == GameState.LOGGED_IN && gui.finished();
        }
    
        // Prints the given Object to the console
        public void print(Object o) {
            String tag = o.toString().contains("[") ? "" : "[" + getScriptName() + "] ";
            s.log(tag + o.toString());
        }
    
        // Returns the start time for this script
        public long getStartTime() {
            return START_TIME;
        }
    
        // Returns the script title
        public String getScriptName() {
            return s.getManifest().name();
        }
    
        // Displays an error message to the user
        public void error(String msg) {
            print("Error: " + msg);
        }
    
        // Returns how many times we have hopped worlds
        public int getWorldHops() {
            return WORLD_HOPS;
        }
    
        // Returns the script's current CPU load every 5 seconds (to avoid unnecessary extra load)
        public double getCPU() {
            if(System.currentTimeMillis() - LAST_CPU_CHECK <= 5000)
                return LAST_CPU_LOAD;
    
            try {
                LAST_CPU_CHECK = System.currentTimeMillis();
                MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
                ObjectName name = ObjectName.getInstance("java.lang:type=OperatingSystem");
                AttributeList list = mbs.getAttributes(name, new String[]{"ProcessCpuLoad"});
    
                if (list.isEmpty())
                    return -1;
    
                Attribute att = (Attribute) list.get(0);
                Double value = (Double) att.getValue();
    
                // Usually takes a couple of seconds before we get real values
                if (value == -1.0)
                    return -1;
    
                // Returns a percentage value with 1 decimal point precision
                return LAST_CPU_LOAD = ((int) (value * 1000) / 10.0);
            } catch(Exception e) {
                return -1;
            }
        }
    
        // Returns the current runtime in a String format
        public String getRuntime() {
            long runtime = System.currentTimeMillis() - START_TIME - PAUSE_TIME;
            return String.format("%d min, %d sec",
                    TimeUnit.MILLISECONDS.toMinutes(runtime),
                    TimeUnit.MILLISECONDS.toSeconds(runtime) -
                            TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(runtime))
            );
        }
    
        // Draws script title + generic feedback info to the screen about anti-ban & script performance
        public int drawInfo(Graphics g) {
            int x = GUI_X;
            int y = GUI_Y;
            // Paint information to the screen
            drawTitle(g, x, y += 14);
            drawString(g, "Runtime: ", s.isPaused() ? "#Paused" : (START_TIME == 0L ? "#Not started yet" : getRuntime()), x, y += 14);
            drawString(g, "Status: ", ban.getStatus().equals("") ? getStatus() : "#" + ban.getStatus(), x, y += 14);
            drawString(g, "Levels Gained: ", getLevelsGained(), x, y += 14);
            drawString(g, "EXP Gained: ", format(getExpGained()) + "xp", x, y += 14);
            double hrsRunning = (double)(getMinsRunning() <= 0 ? 1 : getMinsRunning()) / 60D;
            if(hrsRunning >= 1)
                drawString(g, "EXP / Hour: ", format((int)((double)getExpGained() / hrsRunning)) + "xp", x, y += 14);
    
            int a = s.getLocalPlayer().getAnimation();
            // TODO: ADD MORE SKILLS
            if(s.getLocalPlayer().isInCombat())
                a = -420;
    
            // Determine which skill to track
            switch(a) {
                case 879: // Woodcut
                    EXP_TIL_LVL = format(s.getSkills().getExperienceToLevel(Skill.WOODCUTTING));
                    break;
                case 733: // Firemaking
                    EXP_TIL_LVL = format(s.getSkills().getExperienceToLevel(Skill.FIREMAKING));
                    break;
                case 897: // Cooking
                case 896:
                    EXP_TIL_LVL = format(s.getSkills().getExperienceToLevel(Skill.COOKING));
                    break;
                case 621: // Fishing
                case 619:
                    EXP_TIL_LVL = format(s.getSkills().getExperienceToLevel(Skill.FISHING));
                    break;
                case -420: // In combat
                    int selectedStyle = s.getPlayerSettings().getConfig(COMBAT_STYLE_SETTING);
                    if(selectedStyle == 0)
                        EXP_TIL_LVL = format(s.getSkills().getExperienceToLevel(Skill.ATTACK));
                    else
                    if(selectedStyle == 1)
                        EXP_TIL_LVL = format(s.getSkills().getExperienceToLevel(Skill.STRENGTH));
                    else
                        EXP_TIL_LVL = format(s.getSkills().getExperienceToLevel(Skill.DEFENCE));
            }
    
            if(EXP_TIL_LVL != null)
                drawString(g, "EXP > Level: ", EXP_TIL_LVL + "xp", x, y += 14);
            int inventoryPercent = (int)(((double)s.getInventory().fullSlotCount() / 28D) * 100D);
            if(s.getInventory().fullSlotCount() == 27)
                inventoryPercent = 99;
            drawString(g, "Inventory: ", s.getInventory().fullSlotCount() + " (" + inventoryPercent + "%)", x, y += 14);
            if(gui.getBoolean("worldhop")) {
                drawString(g, "World Hops: ", WORLD_HOPS, x, y += 14);
                drawString(g, "World: ", s.getClient().getCurrentWorld(), x, y += 14);
                drawString(g, "Nearby Players: ", (s.getPlayers().all().size() > 1 ? "#" : "") + (s.getPlayers().all().size() - 1), x, y += 14);
            }
            if(gui.getBoolean("chatbot"))
                drawString(g, "Chatbot Replies: ", ban.getChatBotReplyCount(), x, y += 14);
    
            // Draw icon
            if(icon == null)
                icon = gui.getScaledImage(gui.getIcon(), 25, 25);
            else
            if(!s.getDialogues().inDialogue())
                g.drawImage(icon, IMAGE_X, IMAGE_Y, null);
    
            // Display EXP popups if they are enabled
            if(gui.getBoolean("exppopup")) {
                if(lastExpPopupTotal == 0)
                    lastExpPopupTotal = getTotalExp();
    
                // Show EXP Popup
                if (showExpPopup) {
                    // If it has been less than 3 seconds, display the popup
                    if (System.currentTimeMillis() - expPopupStart <= 3000 && xpGained != 0) {
                        String gained = "+" + xpGained + " EXP";
                        drawString(g, "", "@" + gained, 270, (int) expY);
                        expY -= 0.5;
                    } else {
                        // Otherwise reset the popup
                        expY = 160;
                        showExpPopup = false;
                    }
                }
    
                // New exp gain detected
                if (getTotalExp() != lastExpPopupTotal) {
                    showExpPopup = true;
                    expPopupStart = System.currentTimeMillis();
                    xpGained = getTotalExp() - lastExpPopupTotal;
                    lastExpPopupTotal = getTotalExp();
                    expY = 160;
                }
            }
    
            return y;
        }
    
        // Draws debug info to the screen (separate method to allow for tacking this on to the end of script info)
        public int drawDebug(Graphics g, int x, int y) {
            if(getBoolean("debug")) {
                drawString(g, "Mouse Speed: ", getMouseSpeed(), x, y += 14);
                drawString(g, "Lag Multiplier: ", formatDecimal(getLagMultiplier()) + "x", x, y += 14);
                drawString(g, "CPU: ", (getCPU() > 80 ? "#" : "") + formatDecimal(getCPU()) + "%", x, y += 14);
            }
    
            return y;
        }
    
        // Draws the given 2 variables to the screen with different colors
        public void drawString(Graphics g, Object title, Object value, int x, int y) {
            g.setColor(Color.black);
            g.drawString(title.toString() + value.toString().replaceAll("#", "").replaceAll("@", ""), x + 1, y + 1);
            g.setColor(Color.white);
            g.drawString(title.toString(), x, y);
            Color c = Color.cyan;
            if(value.toString().startsWith("#"))
                c = Color.red;
            else
            if(value.toString().startsWith("@"))
                c = Color.green;
            g.setColor(c);
            g.drawString(value.toString().replaceAll("#", "").replaceAll("@", ""), x + g.getFontMetrics().stringWidth(title.toString()), y);
            g.setColor(Color.white);
        }
    
        // Draws the script title
        public void drawTitle(Graphics g, int x, int y) {
            g.setColor(Color.black);
            g.setFont(new Font(g.getFont().getName(), Font.BOLD, g.getFont().getSize()));
            drawString(g, getScriptName() + " - v" + s.getManifest().version(), "", x, y);
            g.setFont(new Font(g.getFont().getName(), Font.PLAIN, g.getFont().getSize()));
        }
    
        // Adds the given pause time (ms)
        public void addPauseTime(long ms) {
            PAUSE_TIME += ms;
        }
    
        // Formats the given percentage number input
        public String formatDecimal(Object o) {
            return new DecimalFormat("#.##").format(o);
        }
    
        // FOrmats the given number input
        public String format(Object o) {
            return NumberFormat.getInstance().format(o);
        }
    
        // Returns the current REPORT string
        public String getReport() {
            return "Runtime: " + getRuntime() + ", Lvls Gained: " + getLevelsGained() + ", Exp Gained: " + getExpGained() + "xp, Chatbot Replies: " + ban.getChatBotReplyCount() + ", Lag: " + new DecimalFormat("#.##").format(getLagMultiplier()) + "x, ";
        }
    
        // Gets the current price for the given item ID
        public int getPrice(final int id) {
            try (
                final BufferedReader reader = new BufferedReader(new InputStreamReader(new URL(BASE_GE_URL + id).openStream()))) {
                final String raw = reader.readLine().replace(",", "").replace("\"", "").split("price:")[1].split("}")[0];
                return raw.endsWith("m") || raw.endsWith("k") ? (int) (Double.parseDouble(raw.substring(0, raw.length() - 1)) * (raw.endsWith("m") ? MILLION_GP : THOUSAND_GP)) : Integer.parseInt(raw);
            } catch (final Exception e) {
                e.printStackTrace();
            }
    
            return -1;
        }
    }

    ZenGUI.java:

     
    package Zen;
    
    import org.dreambot.core.Instance;
    
    import javax.imageio.ImageIO;
    import javax.swing.*;
    import javax.swing.border.EmptyBorder;
    import javax.swing.event.ChangeEvent;
    import javax.swing.event.ChangeListener;
    import java.awt.*;
    import java.awt.event.*;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.prefs.Preferences;
    
    /**
     * This class creates a generic re-usable script settings GUI.
     * It will also save and load the input data for each OSRS account between use.
     *
     * TODO: Add ability to tie certain components to each other (ie. checkbox or slider = disabled unless reference checkbox is enabled etc.)
     *
     * @author Zenarchist
     * @version 1.0
     */
    public class ZenGUI implements ActionListener, FocusListener, ChangeListener {
        // Main class variables
        private boolean START_BUTTON_PRESSED = false;
        private boolean CANCEL_BUTTON_PRESSED = false;
        private final int WIDTH = 420;
        private final int HEIGHT = 250;
        private Preferences SAVE;
        // GUI components
        private JFrame frame;
        private JTabbedPane tabbedPane;
        // JPanels (Panel ID = order in which it was added)
        private ArrayList<JPanel> TAB_PANELS = new ArrayList<>();
        // Input components
        private HashMap<String, JTextField> INPUT_TEXTFIELDS = new HashMap<>();
        private HashMap<String, JSpinner> INPUT_SPINNERS = new HashMap<>();
        private HashMap<String, JCheckBox> INPUT_CHECKBOX = new HashMap<>();
        private HashMap<String, JSlider> INPUT_SLIDER = new HashMap<>();
        private HashMap<String, JComboBox> INPUT_COMBO = new HashMap<>();
        // Raw input collections
        private HashMap<String, String> SETTINGS_TEXT = new HashMap<>();
        private HashMap<String, Integer> SETTINGS_INT = new HashMap<>();
        private HashMap<String, Boolean> SETTINGS_BOOL = new HashMap<>();
        // Colors
        private Color COL_FOREGROUND = Color.white;
        private Color COL_HIGHLIGHT = new Color(241, 182, 90);
        private Color COL_BACKGROUND = new Color(80, 100, 116);
        private Color COL_FRAME_BACKGROUND = new Color(70, 88, 102);
        // Default Font
        private Font DEFAULT_FONT = new Font("Courier New", Font.BOLD, 14);
        // Script helper class
        private Zen zen;
        // Location of frame icon image
        private String ICON_URL = "https://dreambot.org/forums/uploads/monthly_2018_08/zenarchist.png.082672c17e9b51500ee1d0f2d1565d73.png";
        // Stores the icon image for rendering to game screen
        private Image icon;
    
        public void changeSetting(String key, Object newValue) {
            if(SETTINGS_TEXT.get(key) != null) {
                SETTINGS_TEXT.put(key, newValue.toString());
                return;
            }
            if(SETTINGS_BOOL.get(key) != null) {
                SETTINGS_BOOL.put(key, (Boolean)newValue);
                return;
            }
            if(SETTINGS_INT.get(key) != null) {
                SETTINGS_INT.put(key, (int)newValue);
                return;
            }
        }
    
        // This creates a ZenGUI with the given Zen helper class (for access to AbstractScript)
        public ZenGUI(Zen zen) {
            this.zen = zen;
            this.SAVE = Preferences.userNodeForPackage(ZenGUI.class);
            // Setup JFrame
            setupThemeColors();
            frame = new JFrame(zen.getScriptName() + " GUI");
            frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
            frame.setSize(WIDTH, HEIGHT);
            frame.setMaximumSize(new Dimension(WIDTH, HEIGHT));
            frame.setMinimumSize(new Dimension(WIDTH, HEIGHT));
            frame.setResizable(false);
            frame.setAlwaysOnTop(true);
            frame.setLocationRelativeTo(null);
            frame.setBackground(COL_FRAME_BACKGROUND);
            tabbedPane = new JTabbedPane();
            tabbedPane.setBackground(COL_FRAME_BACKGROUND);
            tabbedPane.setForeground(COL_FOREGROUND);
            tabbedPane.setFont(DEFAULT_FONT);
        }
    
        // Builds and shows the GUI to the user
        public boolean show() {
            zen.STATUS = "#Getting user input";
            Instance.getInstance().setMouseInputEnabled(true);
            Instance.getInstance().setKeyboardInputEnabled(true);
            loadPreferences();
            // Add START & CANCEL buttons to each tab
            for(JPanel p : TAB_PANELS) {
                JButton startBtn = new JButton("START");
                JButton cancelBtn = new JButton("CANCEL");
                startBtn.setBackground(COL_BACKGROUND);
                startBtn.setForeground(COL_FOREGROUND);
                startBtn.setFont(DEFAULT_FONT);
                startBtn.addActionListener(this);
                cancelBtn.setBackground(COL_BACKGROUND);
                cancelBtn.setForeground(COL_FOREGROUND);
                cancelBtn.setFont(DEFAULT_FONT);
                cancelBtn.addActionListener(this);
                p.add(startBtn);
                p.add(cancelBtn);
            }
    
            frame.setContentPane(tabbedPane);
            frame.pack();
            frame.setVisible(true);
            frame.toFront();
            getFrameIcon();
            // This loop waits for the frame to close before allowing the script to start
            while(!finished()) { zen.getScript().sleep(10); }
            if(CANCEL_BUTTON_PRESSED) {
                zen.getScript().stop();
                return false;
            }
    
            Instance.getInstance().setMouseInputEnabled(false);
            Instance.getInstance().setKeyboardInputEnabled(false);
            zen.setStartTime();
            return true;
        }
    
        @Override
        public void actionPerformed(ActionEvent actionEvent) {
            Object obj = actionEvent.getSource();
            if(obj == null)
                return;
    
            // If object is a UI element, handle it here
            if(obj instanceof JButton) {
                String src = ((JButton)obj).getText();
                if(src.equals("START")) {
                    // Close frame and dispose of UI elements
                    frame.setVisible(false);
                    storeSettings();
                    printSettings();
                    frame.dispose();
                    START_BUTTON_PRESSED = true;
                } else
                if(src.equals("CANCEL")) {
                    // Close frame, dispose of UI elements and set cancel flag to true (stops script)
                    frame.setVisible(false);
                    frame.dispose();
                    CANCEL_BUTTON_PRESSED = true;
                }
            }
        }
    
        // Sets up the custom UI look & feel colors for the Tabbed Pane to match DB's color theme
        private void setupThemeColors() {
            UIManager.put("TabbedPane.borderColor", COL_FRAME_BACKGROUND);
            UIManager.put("TabbedPane.darkShadow", COL_FRAME_BACKGROUND);
            UIManager.put("TabbedPane.light", COL_FRAME_BACKGROUND);
            UIManager.put("TabbedPane.highlight", COL_FRAME_BACKGROUND);
            UIManager.put("TabbedPane.focus", COL_FRAME_BACKGROUND);
            UIManager.put("TabbedPane.selected", COL_FRAME_BACKGROUND);
            UIManager.put("TabbedPane.unselectedBackground", COL_FRAME_BACKGROUND);
            UIManager.put("TabbedPane.selectHighlight", COL_FRAME_BACKGROUND);
            UIManager.put("TabbedPane.tabAreaBackground", COL_FRAME_BACKGROUND);
            UIManager.put("TabbedPane.borderHightlightColor", Color.white);
            UIManager.put("TabbedPane.contentBorderInsets", new Insets(0, 0, 0, 0));
        }
    
        // Adds a panel to the main tabbed pane
        public void addPanel(String title, String tooltip) {
            JPanel newPanel = new JPanel();
            newPanel.setName(title);
            newPanel.setBackground(COL_FRAME_BACKGROUND);
            newPanel.setLayout(new GridLayout(0, 2));
            newPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
            tabbedPane.addTab(title, null, newPanel, tooltip);
            TAB_PANELS.add(newPanel);
        }
    
        // Short-hand for adding main panel inputs
        public void addStringInput(int panelID, String title, String key, String defaultValue) {
            addStringInput(panelID, title, key, "", defaultValue);
        }
    
        // Adds a String input to the GUI
        public void addStringInput(int panelID, String title, String key, String tooltip, String defaultValue) {
            JLabel label = getLabel(title);
            JTextField field = new JTextField();
            // Setup text field
            field.setName(key);
            field.setToolTipText(tooltip);
            field.setText(defaultValue);
            field.setBackground(COL_BACKGROUND);
            field.setForeground(COL_FOREGROUND);
            field.setCaretColor(COL_HIGHLIGHT);
            field.setSelectionColor(COL_HIGHLIGHT);
            field.setFont(DEFAULT_FONT);
            field.setColumns(50);
            field.setHorizontalAlignment(JTextField.CENTER);
            field.addActionListener(this);
            field.addFocusListener(this);
            // Add the new components to the content pane
            JPanel p = TAB_PANELS.get(panelID);
            if(p == null) {
                zen.print("Error: Panel ID is null - " + panelID);
                return;
            }
    
            p.add(label);
            p.add(field);
            INPUT_TEXTFIELDS.put(key, field);
        }
    
        // Short-hand for adding main panel inputs
        public void addIntegerInput(int panelID, String title, String key, int defaultValue, int min, int max) {
            addIntegerInput(panelID, title, key, null, defaultValue, min, max);
        }
    
        // Adds an Integer input to the GUI
        public void addIntegerInput(int panelID, String title, String key, String tooltip, int defaultValue, int min, int max) {
            JLabel label = getLabel(title);
            SpinnerNumberModel snm = new SpinnerNumberModel(defaultValue, min, max, 1);
            JSpinner spinner = new JSpinner(snm);
            // Setup the spinner
            spinner.setName(key);
            spinner.setToolTipText(tooltip);
            spinner.setValue(defaultValue);
            spinner.setFont(DEFAULT_FONT);
    
            for(int i = 0; i < spinner.getEditor().getComponents().length; i++) {
                Component c = spinner.getEditor().getComponent(i);
                if(c instanceof JTextField) {
                    c.setForeground(COL_FOREGROUND);
                    c.setBackground(COL_BACKGROUND);
                    ((JTextField) c).setCaretColor(COL_HIGHLIGHT);
                    ((JTextField) c).setSelectionColor(COL_HIGHLIGHT);
                    ((JTextField) c).setColumns(4);
                    ((JTextField) c).setHorizontalAlignment(JTextField.CENTER);
                    c.addFocusListener(this);
                }
            }
            // Add the new components to the content pane
            JPanel p = TAB_PANELS.get(panelID);
            if(p == null) {
                zen.print("Error: Panel ID is null - " + panelID);
                return;
            }
    
            p.add(label);
            p.add(spinner);
            INPUT_SPINNERS.put(key, spinner);
        }
    
        // Short-hand for adding main panel inputs
        public void addBooleanInput(int panelID, String title, String key, boolean defaultValue) {
            addBooleanInput(panelID, title, key, null, defaultValue);
        }
    
        // Adds an Boolean input to the GUI
        public void addBooleanInput(int panelID, String title, String key, String tooltip, boolean defaultValue) {
            JLabel label = getLabel(title);
            JCheckBox checkbox = new JCheckBox();
            // Setup the checkbox
            checkbox.setName(key);
            checkbox.setToolTipText(tooltip);
            checkbox.setSelected(defaultValue);
            checkbox.setForeground(COL_FOREGROUND);
            checkbox.setBackground(COL_FRAME_BACKGROUND);
            // Add the new components to the content pane
            JPanel p = TAB_PANELS.get(panelID);
            if(p == null) {
                zen.print("Error: Panel ID is null - " + panelID);
                return;
            }
    
            p.add(label);
            p.add(checkbox);
            INPUT_CHECKBOX.put(key, checkbox);
        }
    
        // Short-hand for adding main panel inputs
        public void addSliderInput(int panelID, String title, String key, int defaultValue, int min, int max, int majorTicks, int minorTicks) {
            addSliderInput(panelID, title, key, null, defaultValue, min, max, majorTicks, minorTicks);
        }
    
        // Adds a Slider input to the GUI
        public void addSliderInput(int panelID, String title, String key, String tooltip, int defaultValue, int min, int max, int majorTicks, int minorTicks) {
            JLabel label = getLabel(title);
            JSlider slider = new JSlider(JSlider.HORIZONTAL, min, max, defaultValue);
            // Setup the checkbox
            slider.setName(key);
            slider.setForeground(COL_FOREGROUND);
            slider.setBackground(COL_FRAME_BACKGROUND);
            slider.setMajorTickSpacing(majorTicks);
            slider.setMinorTickSpacing(minorTicks);
            slider.setPaintTicks(true);
            slider.setPaintLabels(true);
            slider.addChangeListener(this);
            slider.setToolTipText("Value: " + slider.getValue() + (slider.getName().equals("antibanrate") ? "%" : ""));
            // Add the new components to the content pane
            JPanel p = TAB_PANELS.get(panelID);
            if(p == null) {
                zen.print("Error: Panel ID is null - " + panelID);
                return;
            }
    
            p.add(label);
            p.add(slider);
            INPUT_SLIDER.put(key, slider);
        }
    
        // Short-hand for adding main panel inputs
        public void addComboInput(int panelID, String title, String key, int defaultValue, String... list) {
            addComboInput(panelID, title, key,  null, defaultValue, list);
        }
    
        // Adds a Slider input to the GUI
        public void addComboInput(int panelID, String title, String key, String tooltip, int defaultValue, String... list) {
            JLabel label = getLabel(title);
            JComboBox combo = new JComboBox(list);
            // Setup the checkbox
            combo.setName(key);
            combo.setToolTipText(tooltip);
            combo.setEditable(false);
            combo.setForeground(COL_FOREGROUND);
            combo.setBackground(COL_BACKGROUND);
            // Add the new components to the content pane
            JPanel p = TAB_PANELS.get(panelID);
            if(p == null) {
                zen.print("Error: Panel ID is null - " + panelID);
                return;
            }
    
            p.add(label);
            p.add(combo);
            INPUT_COMBO.put(key, combo);
        }
    
        // Returns a label with the given text
        private JLabel getLabel(String text) {
            JLabel label = new JLabel();
            JTextField field = new JTextField();
            // Setup label
            label.setText(" " + text + ": ");
            label.setForeground(COL_FOREGROUND);
            label.setFont(DEFAULT_FONT);
            return label;
        }
    
        // This method prints the input settings to console (for debugging purposes)
        private void printSettings() {
            String print = "";
            for(String k : SETTINGS_TEXT.keySet()) {
                String value = SETTINGS_TEXT.get(k);
                if(!value.equals(""))
                    print = print + k + "=" + value + ",";
            }
            for(String k : SETTINGS_INT.keySet()) {
                int value = SETTINGS_INT.get(k);
                print = print + k + "=" + value + ",";
            }
            for(String k : SETTINGS_BOOL.keySet()) {
                boolean value = SETTINGS_BOOL.get(k);
                print = print + k + "=" + value + ",";
            }
    
            zen.print("[GUI] " + print.substring(0, print.length()-1));
        }
    
        // This method returns true if the user has clicked START or CANCEL
        public boolean finished() {
            return START_BUTTON_PRESSED || CANCEL_BUTTON_PRESSED;
        }
    
        // Returns the user input for the given String variable key
        public String getString(String key) {
            return SETTINGS_TEXT == null ? "null" : SETTINGS_TEXT.get(key);
        }
    
        // Returns the user input for the given String variable key
        public String[] getStrings(String key) {
            return SETTINGS_TEXT == null || SETTINGS_TEXT.get(key).split(",").length == 0 ? null : SETTINGS_TEXT.get(key).split(",");
        }
    
        // Returns the user input for the given Integer variable key
        public int getInt(String key) {
            return SETTINGS_INT == null ? -1 : SETTINGS_INT.get(key);
        }
    
        // Returns the user input for the given Boolean variable key
        public boolean getBoolean(String key) {
            return SETTINGS_BOOL == null ? false : SETTINGS_BOOL.get(key);
        }
    
        // This method stores the input values into a raw datatype collection
        private void storeSettings() {
            // Store raw values from UI elements and save application preferences
            for(String k : INPUT_TEXTFIELDS.keySet()) {
                SETTINGS_TEXT.put(k, INPUT_TEXTFIELDS.get(k).getText());
                SAVE.put(getKey() + k, INPUT_TEXTFIELDS.get(k).getText());
            }
            for(String k : INPUT_SPINNERS.keySet()) {
                SETTINGS_INT.put(k, (int) INPUT_SPINNERS.get(k).getValue());
                SAVE.putInt(getKey() + k, (int) INPUT_SPINNERS.get(k).getValue());
            }
            for(String k : INPUT_CHECKBOX.keySet()) { // Store as string because you can't use pref.getBoolean(key, null) == null as booleans don't allow for null values
                SETTINGS_BOOL.put(k, INPUT_CHECKBOX.get(k).isSelected());
                SAVE.put(getKey() + k, INPUT_CHECKBOX.get(k).isSelected() == true ? "true" : "false");
            }
            for(String k : INPUT_SLIDER.keySet()) {
                SETTINGS_INT.put(k, INPUT_SLIDER.get(k).getValue());
                SAVE.putInt(getKey() + k, INPUT_SLIDER.get(k).getValue());
            }
            for(String k : INPUT_COMBO.keySet()) {
                SETTINGS_INT.put(k, INPUT_COMBO.get(k).getSelectedIndex());
                SAVE.putInt(getKey() + k, INPUT_COMBO.get(k).getSelectedIndex());
            }
            // Clean up UI components to save RAM
            INPUT_TEXTFIELDS.clear();
            INPUT_SPINNERS.clear();
            INPUT_CHECKBOX.clear();
            INPUT_SLIDER.clear();
            INPUT_COMBO.clear();
            INPUT_TEXTFIELDS = null;
            INPUT_SPINNERS = null;
            INPUT_CHECKBOX = null;
            INPUT_SLIDER = null;
            INPUT_COMBO = null;
        }
    
        // Loads any saved data from the system to display into the UI
        public void loadPreferences() {
            // First check text fields
            for(String s : INPUT_TEXTFIELDS.keySet()) {
                if (INPUT_TEXTFIELDS.get(s) != null && SAVE.get(getKey() + s, null) != null) {
                    JTextField txt = INPUT_TEXTFIELDS.get(s);
                    txt.setText(SAVE.get(getKey() + s, null));
                }
            }
    
            // Next check spinners
            for(String s : INPUT_SPINNERS.keySet()) {
                if (INPUT_SPINNERS.get(s) != null && SAVE.getInt(getKey() + s, -1) != -1) {
                    JSpinner spinner = INPUT_SPINNERS.get(s);
                    spinner.setValue(SAVE.getInt(getKey() + s, -1));
                }
            }
    
            // Next check checkboxes
            for(String s : INPUT_CHECKBOX.keySet()) {
                if (INPUT_CHECKBOX.get(s) != null && SAVE.get(getKey() + s, null) != null) {
                    JCheckBox box = INPUT_CHECKBOX.get(s);
                    box.setSelected(SAVE.get(getKey() + s, null).equals("true"));
                }
            }
    
            // Next check sliders
            for(String s : INPUT_SLIDER.keySet()) {
                if(INPUT_SLIDER.get(s) != null && SAVE.getInt(getKey() + s, -1) != -1) {
                    JSlider slider = INPUT_SLIDER.get(s);
                    slider.setValue(SAVE.getInt(getKey() + s, -1));
                }
            }
    
            // Next check combo boxes
            for(String s : INPUT_COMBO.keySet()) {
                if (INPUT_COMBO.get(s) != null && SAVE.getInt(getKey() + s, -1) != -1) {
                    JComboBox box = INPUT_COMBO.get(s);
                    box.setSelectedIndex(SAVE.getInt(getKey() + s, -1));
                }
            }
        }
    
        // Finds the given input component and sets its default value
        // This can only be used by scripts before they call show(), as show() stalls script until START or CANCEL is pressed
        public void setValue(String key, Object value) {
            JComponent c = null;
    
            // First check text fields
            for(String s : INPUT_TEXTFIELDS.keySet())
                if(s.equals(key) && INPUT_TEXTFIELDS.get(s) != null)
                    c = INPUT_TEXTFIELDS.get(s);
    
            // Next check spinners
            if(c == null) {
                for(String s : INPUT_SPINNERS.keySet())
                    if(s.equals(key) && INPUT_SPINNERS.get(s) != null)
                        c = INPUT_SPINNERS.get(s);
            }
    
            // Next check checkboxes
            if(c == null) {
                for(String s : INPUT_CHECKBOX.keySet())
                    if(s.equals(key) && INPUT_CHECKBOX.get(s) != null)
                        c = INPUT_CHECKBOX.get(s);
            }
    
            // Next check sliders
            if(c == null) {
                for(String s : INPUT_SLIDER.keySet())
                    if(s.equals(key) && INPUT_SLIDER.get(s) != null)
                        c = INPUT_SLIDER.get(s);
            }
    
            // Next check combo boxes
            if(c == null) {
                for(String s : INPUT_COMBO.keySet())
                    if(s.equals(key) && INPUT_COMBO.get(s) != null)
                        c = INPUT_COMBO.get(s);
            }
    
            // If the component was found, set the tooltip
            if(c != null) {
                if(c instanceof JTextField) {
                    ((JTextField)c).setText(value.toString());
                } else
                if(c instanceof JSpinner) {
                    ((JSpinner)c).setValue(value);
                } else
                if(c instanceof JCheckBox) {
                    ((JCheckBox)c).setSelected((Boolean)value);
                } else
                if(c instanceof JSlider) {
                    ((JSlider)c).setValue((int)value);
                } else
                if(c instanceof JComboBox) {
                    ((JComboBox)c).setSelectedIndex((int)value);
                }
            }
        }
    
        // Finds the given input component and sets whether or not it is enabled
        public void setEnabled(String key, boolean enabled) {
            JComponent c = null;
    
            // First check text fields
            for(String s : INPUT_TEXTFIELDS.keySet())
                if(s.equals(key) && INPUT_TEXTFIELDS.get(s) != null)
                    c = INPUT_TEXTFIELDS.get(s);
    
            // Next check spinners
            if(c == null) {
                for(String s : INPUT_SPINNERS.keySet())
                    if(s.equals(key) && INPUT_SPINNERS.get(s) != null)
                        c = INPUT_SPINNERS.get(s);
            }
    
            // Next check checkboxes
            if(c == null) {
                for(String s : INPUT_CHECKBOX.keySet())
                    if(s.equals(key) && INPUT_CHECKBOX.get(s) != null)
                        c = INPUT_CHECKBOX.get(s);
            }
    
            // Next check sliders
            if(c == null) {
                for(String s : INPUT_SLIDER.keySet())
                    if(s.equals(key) && INPUT_SLIDER.get(s) != null)
                        c = INPUT_SLIDER.get(s);
            }
    
            // Next check combo boxes
            if(c == null) {
                for(String s : INPUT_COMBO.keySet())
                    if(s.equals(key) && INPUT_COMBO.get(s) != null)
                        c = INPUT_COMBO.get(s);
            }
    
            // If the component was found, set the enabled flag
            if(c != null) {
                c.setEnabled(enabled);
                if(!enabled) {
                    if(c instanceof JCheckBox)
                        ((JCheckBox)c).setSelected(false);
                }
            }
        }
    
        // Finds the given input component and sets the tooltip text
        public void setTooltip(String key, String tip) {
            JComponent c = null;
    
            // First check text fields
            for(String s : INPUT_TEXTFIELDS.keySet())
                if(s.equals(key) && INPUT_TEXTFIELDS.get(s) != null)
                    c = INPUT_TEXTFIELDS.get(s);
    
            // Next check spinners
            if(c == null) {
                for(String s : INPUT_SPINNERS.keySet())
                    if(s.equals(key) && INPUT_SPINNERS.get(s) != null)
                        c = INPUT_SPINNERS.get(s);
            }
    
            // Next check checkboxes
            if(c == null) {
                for(String s : INPUT_CHECKBOX.keySet())
                    if(s.equals(key) && INPUT_CHECKBOX.get(s) != null)
                        c = INPUT_CHECKBOX.get(s);
            }
    
            // Next check sliders
            if(c == null) {
                for(String s : INPUT_SLIDER.keySet())
                    if(s.equals(key) && INPUT_SLIDER.get(s) != null)
                        c = INPUT_SLIDER.get(s);
            }
    
            // Next check sliders
            if(c == null) {
                for(String s : INPUT_COMBO.keySet())
                    if(s.equals(key) && INPUT_COMBO.get(s) != null)
                        c = INPUT_COMBO.get(s);
            }
    
            // If the component was found, set the tooltip
            if(c != null)
                c.setToolTipText(tip);
        }
    
        // Finds the given input components and sets the tooltip text
        public void setTooltips(String tip, String... keys) {
            ArrayList<JComponent> clist = new ArrayList<>();
            for(String key : keys) {
                JComponent c = null;
    
                // First check text fields
                for (String s : INPUT_TEXTFIELDS.keySet())
                    if (s.equals(key) && INPUT_TEXTFIELDS.get(s) != null)
                        c = INPUT_TEXTFIELDS.get(s);
    
                // Next check spinners
                if (c == null) {
                    for (String s : INPUT_SPINNERS.keySet())
                        if (s.equals(key) && INPUT_SPINNERS.get(s) != null)
                            c = INPUT_SPINNERS.get(s);
                }
    
                // Next check checkboxes
                if (c == null) {
                    for (String s : INPUT_CHECKBOX.keySet())
                        if (s.equals(key) && INPUT_CHECKBOX.get(s) != null)
                            c = INPUT_CHECKBOX.get(s);
                }
    
                // Next check sliders
                if (c == null) {
                    for (String s : INPUT_SLIDER.keySet())
                        if (s.equals(key) && INPUT_SLIDER.get(s) != null)
                            c = INPUT_SLIDER.get(s);
                }
    
                // Next check sliders
                if (c == null) {
                    for (String s : INPUT_COMBO.keySet())
                        if (s.equals(key) && INPUT_COMBO.get(s) != null)
                            c = INPUT_COMBO.get(s);
                }
    
                // If the component was found, set the tooltip
                if (c != null)
                    clist.add(c);
            }
    
            for(JComponent c : clist)
                c.setToolTipText(tip);
        }
    
        @Override
        public void focusGained(FocusEvent focusEvent) {
            final Component c = focusEvent.getComponent();
            if (c instanceof JFormattedTextField) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        ((JFormattedTextField) c).selectAll();
                    }
                });
            } else if (c instanceof JTextField) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        ((JTextField) c).selectAll();
                    }
                });
            }
        }
    
        @Override
        public void focusLost(FocusEvent focusEvent) {
        }
    
        @Override
        public void stateChanged(ChangeEvent changeEvent) {
            if(changeEvent.getSource() instanceof JSlider) {
                JSlider slider = (JSlider)changeEvent.getSource();
                slider.setToolTipText("Value: " + slider.getValue() + (slider.getName().equals("antibanrate") ? "%" : ""));
            }
        }
    
        // This method sets the JFrame icon to one downloaded from the web
        private void getFrameIcon() {
            icon = null;
            try {
                // Gets the GUI frame icon from a Dreambot forum avatar image
                URL url = new URL(ICON_URL);
                icon = ImageIO.read(url);
            } catch (IOException e) { }
    
            // If the image is successfully grabbed and resized, set it as the frame icon
            if(icon != null) {
                ImageIcon i = new ImageIcon(getScaledImage(icon, 256, 256));
                if(i != null)
                    frame.setIconImage(i.getImage());
            }
        }
    
        // This method returns a resized image (used to turn avatar into better icon resolution)
        public Image getScaledImage(Image srcImg, int w, int h){
            BufferedImage resizedImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2 = resizedImg.createGraphics();
            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g2.drawImage(srcImg, 0, 0, w, h, null);
            g2.dispose();
            return resizedImg;
        }
    
        // This returns the loaded image
        public Image getIcon() {
            if(icon == null)
                getFrameIcon();
    
            return icon;
        }
    
        // Returns the unique key for this script and this username
        public String getKey() {
            return zen.getScriptName() + "-" + zen.s.getLocalPlayer().getName() + "-";
        }
    }

    ZenAntiBan.java:

     
    package Zen;
    
    import org.dreambot.api.methods.map.Area;
    import org.dreambot.api.methods.map.Tile;
    import org.dreambot.api.methods.skills.Skill;
    import org.dreambot.api.methods.tabs.Tab;
    import org.dreambot.api.randoms.RandomEvent;
    import org.dreambot.api.wrappers.interactive.Entity;
    import org.dreambot.api.wrappers.interactive.GameObject;
    import org.dreambot.api.wrappers.interactive.NPC;
    import org.dreambot.api.wrappers.interactive.Player;
    import org.dreambot.api.wrappers.items.Item;
    import org.dreambot.api.wrappers.widgets.Menu;
    import org.dreambot.api.wrappers.widgets.message.Message;
    
    import java.awt.*;
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.net.URL;
    import java.net.URLConnection;
    import java.util.ArrayList;
    
    /**
     * This class holds all of my anti-ban ideas.
     * 
     * @author Zenarchist
     */
    public class ZenAntiBan {
        public int MIN_WAIT_NO_ACTION = 50; // This is the minimum time to wait if no action was taken
        public int MAX_WAIT_NO_ACTION = 100; // This is the maximum time to wait if no action was taken
        private Zen z; // Script helper
        private String STATUS = ""; // Current anti-ban status
        private Skill[] STATS_TO_CHECK = { Skill.HITPOINTS }; // This is used for determining which stats to randomly check
        public int MIN_WAIT_BETWEEN_EVENTS = 10; // In seconds
        private long LAST_EVENT = 0L; // Last time an antiban event was triggered
        private long LAST_IDLE; // Last time we idled for a while
        private boolean DO_RANDOM = false; // This is a generic flag for randomly doing something early in a script for anti-patterning
        // Chatbot variables
        private String CHATBOT_STATE = "x"; // This is used for continuing conversations (not that it matters - chatbot is dumb as hell)
        private long LAST_CHATBOT_REPLY = 0L; // Last time the chatbot replied
        private int CHATBOT_REPLIES = 0; // How many times the chatbot has replied to player messages
        private final String VALID_CHARACTERS = "abcdefghijklmnopqrstuvwxyz"; // Valid characters for chatbot
        private long LAST_PERSONAL_REPLY = 0L; // Last time we replied to a 'bot' flagged message or a message addressed to us
        private String[] CHAT_FLAGS = {};
        private String CHATBOT_KEY = "xxx";
    
        // Constructs a new Anti-Ban class with the given script-helper
        public ZenAntiBan(Zen zen) {
            this.z = zen;
            // Set last idle to now to avoid idling early into the script
            LAST_IDLE = System.currentTimeMillis();
        }
    
        // Sets the custom chat flags to scan for
        public void setChatFlags(String[] chatFlags) {
            this.CHAT_FLAGS = chatFlags;
        }
    
        // Returns the wait time for when the antiban system does nothing
        private int doNothing() {
            return z.rh(MIN_WAIT_NO_ACTION, MAX_WAIT_NO_ACTION);
        }
    
        // Sets the stats to check during random antiban events
        public void setStatsToCheck(Skill... skills) {
            STATS_TO_CHECK = skills;
        }
    
        // Returns the sleep time after performing an anti-ban check
        public int antiBan() {
            setStatus("");
            if(z.gui.getInt("antibanrate") == 0 || System.currentTimeMillis() - LAST_EVENT <= z.r(MIN_WAIT_BETWEEN_EVENTS * 1000, MIN_WAIT_BETWEEN_EVENTS * 2000))
                return doNothing();
    
            // If we have moved the mouse outside of the screen, wait a moment before performing the ban action
            if(z.s.getMouse().getX() == -1 && z.s.getMouse().getY() == -1)
                z.sleep(1000, 2000);
    
            // Calculate overall random anti-ban intervention rate (%)
            int rp = z.r(0, 100);
            if(rp < z.gui.getInt("antibanrate")) {
                // Calculate event-specific activation rate (%)
                rp = z.r(0, 100);
                // Calculate event ID
                int event = z.r(0, 14);
                // Handle specified event
                switch(event) {
                    case 0: { // Examine random entity
                        if(rp < 25) { // 25% chance
                            int r = z.r(1, 3);
                            Entity e = z.s.getGameObjects().closest(o -> o != null && !o.getName().equals("null") && z.r(1, 2) != 1);
                            if (e == null || r == 2) {
                                e = z.s.getNpcs().closest(n -> n != null && !n.getName().equals("null"));
                                if (e == null || r == 3) {
                                    e = z.s.getGroundItems().closest(i -> i != null && !i.getName().equals("null"));
                                    if (e == null)
                                        return doNothing();
                                }
                            }
    
                            setStatus("Examining entity (" + e.getName() + ")");
                            z.s.getMouse().move(e);
    
                            if(z.r(0, 100) < 99) { // 99% chance of clicking examine
                                // Open right-click menu and find Examine option
                                Menu menu = new Menu(z.s.getClient());
                                z.rh(1, 100);
                                menu.open();
                                z.s.sleep(z.rh(250, 1000));
                                if (menu.contains("Examine"))
                                    menu.clickAction("Examine", e);
                                else
                                if(menu.contains("Cancel"))
                                    menu.clickAction("Cancel");
                            }
    
                            LAST_EVENT = System.currentTimeMillis();
                            return z.rh(250, 3000);
                        }
                    }
                    case 1: { // Check random stat
                        if(rp < 10) { // 10% chance
                            if (z.s.getTabs().getOpen() != Tab.STATS)
                                z.openStats();
                            int x = z.r(0, 25);
                            int y = z.r(0, 15);
                            int skill = -1;
                            long t = System.currentTimeMillis();
                            while (skill == -1 && System.currentTimeMillis() - t <= 500) {
                                int r = z.r(0, Skill.values().length - 1);
                                for (Skill s : STATS_TO_CHECK) {
                                    if (s.getName().equals(Skill.values()[r].getName()))
                                        skill = r;
                                }
                            }
    
                            setStatus("Checking EXP (" + Skill.values()[skill].getName() + ")");
                            Point p = z.STAT_WIDGET[skill];
                            p.setLocation(p.getX() + x, p.getY() + y);
                            z.s.getMouse().move(p);
    
                            LAST_EVENT = System.currentTimeMillis();
                            return z.rh(2000, 5000);
                        }
                    }
                    case 2: { // Type something random
                        if(rp < 1) { // 1% chance
                            String s = "";
                            int r = z.r(1, 2);
                            for(int i = 0; i < r; i++)
                                s += VALID_CHARACTERS.charAt(z.r(0, VALID_CHARACTERS.length()));
                            setStatus("Typing random keys (" + s + ")");
                            z.s.getKeyboard().type(s);
    
                            LAST_EVENT = System.currentTimeMillis();
                            return z.rh(500, 3000);
                        }
                    }
                    case 3: { // Move mouse to random location (and sometimes click)
                        if(rp < 10) { // 10% chance
                            int r = z.r(0, 100);
                            int x = z.r(0, 760);
                            int y = z.r(0,500);
                            setStatus("Moving mouse (" + x + "," + y + ")");
                            z.s.getMouse().move(new Point(x, y));
                            if(r < 5) // 10% chance of right-clicking
                                z.s.getMouse().click(true);
                            else
                            if(r < 5) // 5% chance of left-clicking
                                z.s.getMouse().click();
    
                            LAST_EVENT = System.currentTimeMillis();
                            return z.rh(500, 3000);
                        }
                    }
                    case 4: { // Walk to random location
                        if(rp < 1) { // 1% chance
                            int x = z.s.getLocalPlayer().getX() - 15;
                            int y = z.s.getLocalPlayer().getY() - 15;
                            int x2 = z.r(0, 30);
                            int y2 = z.r(0, 30);
                            Area a = new Area(x, y, x2, y2);
                            Tile t = a.getRandomTile();
                            setStatus("Walking to random tile (" + t.getX() + "," + t.getY() + ")");
                            z.s.getWalking().walk(t);
                            LAST_EVENT = System.currentTimeMillis();
                            return z.rh(500, 3000);
                        }
                    }
                    case 5: { // Chop random tree
                        if(rp < 1) { // 1% chance
                            GameObject obj = z.s.getGameObjects().closest(o -> o != null && !o.getName().equals("null") && z.r(1, 2) != 1 && o.hasAction("Chop down") && z.s.getLocalPlayer().distance(o) < 5);
                            if(obj == null)
                                return doNothing();
    
                            setStatus("Chopping random tree (" + obj.getName() + ")");
                            if(z.r(1, 2) != 1)
                                obj.interact("Chop down");
                            else
                                obj.interactForceLeft("Chop down");
    
                            LAST_EVENT = System.currentTimeMillis();
                            return z.rh(500, 3000);
                        }
                    }
                    case 6: { // Click random entity
                        if(rp < 1) { // 1% chance
                            Entity e = z.s.getGameObjects().closest(o -> o != null && !o.getName().equals("null")  && z.r(1, 2) != 1 && z.s.getLocalPlayer().distance(o) < 5);
                            int r = z.r(1, 3);
                            if(e == null || r == 2) {
                                e = z.s.getNpcs().closest(n -> n != null && !n.getName().equals("null"));
                                if(e == null || r == 3) {
                                    e = z.s.getGroundItems().closest(i -> i != null && !i.getName().equals("null"));
                                    if(e == null)
                                        return doNothing();
                                }
                            }
    
                            if(e instanceof NPC && z.s.getCombat().isInMultiCombat())
                                break;
    
                            setStatus("Clicking random entity (" + e.getName() + ")");
                            z.s.getMouse().move(e);
                            z.s.sleep(z.rh(0, 50));
                            if(z.r(0, 100) < 25)
                                z.s.getMouse().click(true);
                            else
                                z.s.getMouse().click();
    
                            LAST_EVENT = System.currentTimeMillis();
                            return z.rh(500, 3000);
                        }
                    }
                    case 7: { // Just idle for a while
                        if(rp < 5) { // 5% chance
                            if (System.currentTimeMillis() - LAST_IDLE >= 300000) { // Only allow idling to occur every 5+ minutes
                                int minsRunning = z.getMinsRunning();
                                int idle = z.r(minsRunning < 60 ? 20000 : 60000, 120000 + (minsRunning * 1200));
                                setStatus("Idling for " + (idle / 1000) + " seconds");
                                if (z.r(0, 100) < 99)
                                    z.s.getMouse().moveMouseOutsideScreen();
                                // Disable dismiss & autologin solvers temporarily
                                z.s.getRandomManager().disableSolver(RandomEvent.LOGIN);
                                z.s.getRandomManager().disableSolver(RandomEvent.DISMISS);
                                // Sleep for the calculated time
                                z.s.sleep(idle);
                                // Enable dismiss & autologin solvers and resume script as normal
                                z.s.getRandomManager().enableSolver(RandomEvent.LOGIN);
                                z.s.getRandomManager().enableSolver(RandomEvent.DISMISS);
                                LAST_IDLE = System.currentTimeMillis();
                                return 1;
                            }
                        }
                    }
                    case 8: { // Open inventory or stats
                        if(rp < 25) { // 25% chance
                            if(z.s.getTabs().getOpen() != Tab.INVENTORY && z.s.getInventory().getEmptySlots() > 0)
                                setStatus("Opening inventory");
                            if (z.openInventory()) {
                                LAST_EVENT = System.currentTimeMillis();
                                z.sleep(50, 100);
                                z.s.getMouse().moveMouseOutsideScreen();
                                return z.rh(500, 1000);
                            }
                        } else
                        if(rp > 75) { // 25% chance
                            if(z.s.getTabs().getOpen() != Tab.STATS)
                                setStatus("Opening stats");
                            if(z.openStats()) {
                                LAST_EVENT = System.currentTimeMillis();
                                z.sleep(50, 100);
                                z.s.getMouse().moveMouseOutsideScreen();
                                return z.rh(500, 1000);
                            }
                        }
                    }
                    case 9: { // Open combat menu (only if we are training melee stats)
                        boolean meleeStats = false;
                        for(Skill s : STATS_TO_CHECK)
                            if(s.equals(Skill.ATTACK) || s.equals(Skill.STRENGTH) || s.equals(Skill.DEFENCE))
                                meleeStats = true;
    
                        if(!meleeStats)
                            break;
    
                        if(rp < 5) { // 5% chance
                            if (z.s.getTabs().getOpen() != Tab.COMBAT)
                                setStatus("Opening combat menu");
                            z.openCombat();
                            z.sleep(50, 100);
                            z.s.getMouse().moveMouseOutsideScreen();
                            LAST_EVENT = System.currentTimeMillis();
                            return z.rh(500, 1000);
                        }
                    }
                    case 10: { // Moving mouse off-screen for a moment
                        if(rp < 50) { // 50% chance
                            if(z.s.getMouse().getX() == -1 && z.s.getMouse().getY() == -1)
                                return doNothing();
    
                            setStatus("Moving mouse off-screen");
                            z.s.getMouse().moveMouseOutsideScreen();
                            LAST_EVENT = System.currentTimeMillis();
                            return z.rh(5000, 8000);
                        }
                    }
                    case 11: { // Open magic menu
                        if(rp < 1) { // 1% chance
                            if(z.s.getTabs().getOpen() != Tab.MAGIC) {
                                setStatus("Opening magic menu");
                                z.openMagic();
                                z.sleep(50, 100);
                                z.s.getMouse().moveMouseOutsideScreen();
                            }
                        }
                    }
                    case 12: { // Examine random inventory item
                        if(rp < 1) { // 1% chance
                            if (z.openInventory())
                                z.sleep(10, 250);
                            for(Item i : z.s.getInventory().all(it -> it != null)) {
                                if(i != null && z.r(1, 3) == 2) {
                                    setStatus("Examining item (" + i.getName() + ")");
                                    z.s.getMouse().move(i.getDestination());
                                    z.s.sleep(z.rh(0, 50));
                                    // Open right-click menu and find Examine option
                                    Menu menu = new Menu(z.s.getClient());
                                    menu.open();
                                    z.s.sleep(z.rh(250, 1000));
                                    if (menu.contains("Examine"))
                                        menu.clickAction("Examine");
                                    else
                                    if(menu.contains("Cancel"))
                                        menu.clickAction("Cancel");
    
                                    break;
                                }
                            }
                        }
                    }
                    case 13: { // Move camera randomly
                        if(rp < 30) { // 30% chance
                            print("Moving camera");
                            Area a = new Area(z.getX() - 10, z.getY() - 10, z.getX() + 10, z.getY() + 10);
                            z.s.getCamera().rotateToTile(a.getRandomTile());
                            return doNothing();
                        }
                    }
                    case 14: { // Do early flag
                        if(rp < 5) { // 5% chance
                            DO_RANDOM = true;
                            return doNothing();
                        }
                    }
                    default:
                        return doNothing();
                }
    
            }
    
            return doNothing();
        }
    
        // Returns whether or not the DO_RANDOM flag has been triggerd
        public boolean doRandom() {
            if(DO_RANDOM) {
                DO_RANDOM = false;
                return true;
            }
    
            return false;
        }
    
        // Get antiban status
        public String getStatus() {
            return STATUS;
        }
    
        // Print to the console if debug is enabled
        private void print(Object o) {
            if(z.gui.getBoolean("debug"))
                z.print("[AntiBan] " + o.toString());
        }
    
        // Returns the chatbot reply count
        public int getChatBotReplyCount() {
            return CHATBOT_REPLIES;
        }
    
        // Returns a chat reply from Cleverbot
        public String getChatbotReply(String phrase) {
            // If message contains spam, ignore it
            if(containsSpam(phrase))
                return null;
    
            // If message does not contain any letters, ignore it
            boolean containsLetter = false;
            for(char c : VALID_CHARACTERS.toCharArray()) {
                if(phrase.contains("" + c))
                    containsLetter = true;
            }
    
            if(!containsLetter)
                return null;
    
            String returner = null;
            phrase = phrase.toLowerCase().trim().replaceAll("[^\\w\\s]", "").replaceAll(z.s.getLocalPlayer().getName().toLowerCase(), "");
            phrase = phrase.replaceAll("/", "");
    
            try {
                // Fetch URL result for chatbot API call
                URL url = new URL("https://www.cleverbot.com/getreply?key=" + (z.gui.getString("chatkey").equals("") ? CHATBOT_KEY : z.gui.getString("chatkey")) + "&input=" + phrase.toLowerCase().trim() + "&cs=" + CHATBOT_STATE + "&callback=ProcessReply");
                URLConnection urlConnection = url.openConnection();
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
                String line = bufferedReader.readLine();
                String T1 = "\"output\":";
                String T2 = "\",\"";
                // Scrape relevant data from JSON output
                String reply = line.substring(line.indexOf(T1) + T1.length());
                reply = reply.substring(0, reply.indexOf(T2)).replaceAll("\"", "").replaceAll("\\.", "");
                String cleverbotState = line.substring(20, line.indexOf("\",\""));
                // Remove any special characters and inappropriate words from the bot reply
                reply = reply.toLowerCase().replaceAll("cleverbot", "");
                reply = reply.toLowerCase().replaceAll("robot", "");
                reply = reply.toLowerCase().replaceAll("name", "deal");
                reply = reply.toLowerCase().replaceAll("how old", "");
                reply = reply.toLowerCase().replaceAll("age", "deal");
                reply = reply.replaceAll("[^\\w\\s]","");
    
                // Randomly mix up certain punctuation
                if(z.r(1, 5) == 3)
                    reply = reply.replaceAll("'", "");
                if (reply.equalsIgnoreCase("what?") && z.r(1, 6) == 2)
                    reply = reply + "!";
    
                // If reply does not contain any letters at all, then ignore it
                boolean letterFound = false;
                for(char c : VALID_CHARACTERS.toCharArray()) {
                    if(reply.contains("" + c))
                        letterFound = true;
                }
    
                if(!letterFound)
                    return null;
    
                returner = reply;
                CHATBOT_STATE = cleverbotState;
                bufferedReader.close();
            } catch (Exception e) {
                e.printStackTrace();
                print("ERROR: Failed to get reply from chatbot!");
            }
    
            return returner;
        }
    
        // Checks to see if the given message should be replied to
        public boolean checkPlayerMessage(Message message) {
            // Ignore spam
            if(!z.gui.getBoolean("chatbot") || containsSpam(message.getMessage()))
                return false;
    
            long now = System.currentTimeMillis();
    
            // If script has just started, do not reply to chat
            if(now - z.getStartTime() <= 60000)
                return false;
    
            String text = message.getMessage().toLowerCase().trim();
            String user = message.getUsername();
            Player speaker = null;
    
            // Check local players to find if the Speaker exists as a Player entity
            for(Player p : z.s.getPlayers().all()) {
                if(p != null && p.getName().equalsIgnoreCase(user))
                    speaker = p;
            }
    
            // If the speaker player is null, or the speaker is us, or the speaker is far away, then do not reply
            if(speaker == null || z.s.getLocalPlayer().equals(speaker) || z.s.getLocalPlayer().distance(speaker) > 15)
                return false;
    
            int chatLimitMins = z.gui.getInt("chatbotrate") * 1000 * 60;
            // If we have replied within the last X minutes and this chat does not contain our player name or the word bot, don't reply
            if(now - LAST_CHATBOT_REPLY <= z.r(chatLimitMins, chatLimitMins * 2) && !containsPlayerName(text) && !containsBotWord(text)) {
                //print("We cannot reply for 5-10 more minutes");
                return false;
            }
    
            // If this chat DOES contain the word bot or our player name, and we have not replied to a similar message in the last 20-45 seconds, then reply.
            if(containsPlayerName(text) || containsBotWord(text)) {
                if(now - LAST_PERSONAL_REPLY <= z.r(20000, 45000)) {
                    return false;
                }
    
                LAST_PERSONAL_REPLY = now;
            }
    
            // If 'only reply to username/chatflags' is selected, then do not handle the message unless it contains our name or a chat flag
            if(z.gui.getBoolean("chatbotusername") && (!containsPlayerName(text) && !containsBotWord(text)))
                return false;
    
            // Get reply from chatbot and type it
            String chatbot = getChatbotReply(message.getMessage());
            if(chatbot != null && !chatbot.equals("")) {
                LAST_CHATBOT_REPLY = System.currentTimeMillis();
                z.s.log("[ChatBot] Reply: " + chatbot + ", Phrase: " + message.getMessage() + ", From: " + message.getUsername());
                z.s.sleep(z.rh(3000, 5000));
                z.s.getKeyboard().type(chatbot, true, true);
                CHATBOT_REPLIES++;
                return true;
            }
    
            // No reply was given, return false
            return false;
        }
    
        // Returns true if any of these botting-related words are found in the given sentence
        public boolean containsBotWord(String phrase) {
            return phrase.contains("bot") || phrase.contains("macro") || phrase.contains("script") || phrase.contains("auto");
        }
    
        // Returns true if any of these spam-related wrods are found in the given sentence
        public boolean containsSpam(String phrase) {
            String spam = phrase.replaceAll(" ", "").toLowerCase().trim();
            // If message is selling OSRS gp or advertising a clan, ignore it
            if (spam.contains("sell") || spam.contains("usd") || spam.contains("gp") || spam.contains("fee") || spam.contains("free") || spam.contains("join") || phrase.equals("") || phrase.length() <= 1)
                return true;
    
            return false;
        }
    
        // Returns true if the player's name is found in the given string
        public boolean containsPlayerName(String phrase) {
            // Check if phrase straight-up contains our name
            boolean containsName = phrase.contains(z.s.getLocalPlayer().getName());
    
            // Check if this chat straight-up contains our username
            if(containsName)
                return true;
    
            // Check for spam
            if(containsSpam(phrase))
                return false;
    
            // Cycle through user-defined chat flags
            if(CHAT_FLAGS.length > 0) {
                for (String s : CHAT_FLAGS) {
                    if (!s.equals("") && phrase.toLowerCase().contains(s.toLowerCase()))
                        return true;
                }
            }
    
            // Removes all numbers from our player's name
            String name = z.s.getLocalPlayer().getName().replaceAll("[^A-Za-z]", "");
            String[] nameWords = splitCamelCase(name);// Splits our name up by capital letters (for nicknames/shorthand - eg. ZenArchist becomes Zen,Archist)
            String[] spaceWords = z.s.getLocalPlayer().getName().toLowerCase().split(" "); // Splits our name up by spaces (eg. Zen Archist becomes Zen,Archist)
    
            // Cycle through camelcase name words
            if(nameWords != null && nameWords.length > 1) {
                for (String s : nameWords) {
                    if (s.equals("") || s.length() <= 2)
                        continue;
    
                    if (phrase.toLowerCase().contains(s.toLowerCase()))
                        containsName = true;
                }
            }
    
            // Cycle through spaced name words
            if(spaceWords != null && spaceWords.length > 1) {
                for(String s : spaceWords) {
                    if(s.equals("") || s.length() <= 2)
                        continue;
    
                    if(phrase.toLowerCase().contains(s.toLowerCase()))
                        containsName = true;
                }
            }
    
            // If we have found a chat flag, let the user know in the debug console
            if(containsName)
                print("[ChatBot] Flag: " + phrase + " = " + containsName);
    
            return containsName;
        }
    
        // Converts the given string into split words (ie. BitcoinMan becomes Bitcoin Man)
        private static String[] splitCamelCase(String s) {
            String split = s.replaceAll(
                String.format("%s|%s|%s",
                        "(?<=[A-Z])(?=[A-Z][a-z])",
                        "(?<=[^A-Z])(?=[A-Z])",
                        "(?<=[A-Za-z])(?=[^A-Za-z])"
                ),
                ","
            );
    
            return split.split(",");
        }
    
        // Allows an external class to set the anti-ban status
        public void setStatus(String status) {
            STATUS = status;
            if(!status.equals(""))
                print(status);
        }
    }
    

    Script Example:

     
    package ZenTester;
    
    import Zen.*;
    
    import org.dreambot.api.script.AbstractScript;
    import org.dreambot.api.script.Category;
    import org.dreambot.api.script.ScriptManifest;
    
    import java.awt.*;
    
    @ScriptManifest(category = Category.MISC, name = "ZenTester", author = "Zenarchist", version = 1.0, description = "For testing")
    public class Main extends AbstractScript {
        // Declare anti-ban instance
        private Zen z;
        private int START_X;
        private int START_Y;
    
        @Override
        public void onStart() {
            // Construct the Zen helper class and attach it to this script
            z = new Zen(this);
            // Wait until the user is logged in before building the GUI as it depends on character data
            while (!getClient().isLoggedIn())
                z.setStatus("#Logged out");
            // Setup GUI
            z.gui.addStringInput(0, "Starting Coords (x,y)", "location", "Leave blank to use current location", "");
            if (!z.showGUI())
                return;
            // Add starting items to don't-drop list if the option is selected
            z.saveStartingItems();
    
            // Set up antiban
            if (!z.gui.getString("chatflags").equals(""))
                z.ban.setChatFlags(z.gui.getString("chatflags").split(","));
    
            for(String s : z.gui.getString("dontdrop").split(","))
                z.dontDrop(s);
    
            if(z.gui.getStrings("location") == null) {
                START_X = getLocalPlayer().getX();
                START_Y = getLocalPlayer().getY();
            } else {
                START_X = Integer.parseInt(z.gui.getStrings("location")[0]);
                START_Y = Integer.parseInt(z.gui.getStrings("location")[1]);
            }
        }
    
        @Override
        public int onLoop() {
            // Check for random flag (for adding extra customized anti-ban features)
            if(z.getAntiBan().doRandom())
                log("Script-specific random flag triggered");
    
            // Call anti-ban (returns a wait time after performing any actions)
            return z.getAntiBan().antiBan();
        }
    
        @Override
        // Draw anti-ban info to the screen
        public void onPaint(Graphics g) {
            g.drawString("Anti-Ban Status: " + (z.getAntiBan().getStatus().equals("") ? "Inactive" : z.getAntiBan().getStatus()), 10, 100);
            g.drawString("Starting Position: (" + START_X + ", " + START_Y + ")", 10, 100);
        }
    }
    Edited by Zenarchist
    Link to comment
    Share on other sites

    • 2 weeks later...

    added this to my mule script so it would have something to do while it afks waiting for bots..Seemed to work great but then somehow made it walk half way across the map and die a bunch of times losing 70m lol. Just a warning to those that want to add it make sure you edit it and fully test how it interacts with your script.. 

    Link to comment
    Share on other sites

    • 2 weeks later...
    • 1 month later...
    • 1 year later...
    • 2 months later...
    • 3 months later...

    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
     Share

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