Skip to content

Making your first script

Debs Gayen edited this page Aug 1, 2025 · 18 revisions

For this I'll use the example demo script DemoWineScript. Every script in ChromaScape extends BaseScript and implements a looping method called cycle(). The web UI will automatically detect and list any class you create in the scripts package.

Feel free to look at the complete DemoWineScript in the scripts folder if you get stuck.

Part 1: Template

1. File placement

Navigate to:

src/main/java/com/chromascape/scripts.

This is where all of your scripts should live. The web UI scans this directory to populate the script selection side bar.

2. Creating the class

Create a public class and have it extend the BaseScript.

public class DemoWineScript extends BaseScript { 
  //Everything goes inside this from now on
}

3. The constructor

Create a matching Constructor and save the logger to a instance variable, it should look something like this:

private final LogService logger;

public DemoWineScript(boolean isFixed, int duration, LogService logger) {
    super(isFixed, duration, logger);
    this.logger = logger;
  }

NOTE: We must save the logger outside of the constructor to be able to call its instance.

4. Bot logic

Override the cycle() routine.

@Override
  protected void cycle() {
    logger.addLog("Hello World!");
    Sleeper.waitRandomMillis(80, 100);
  }

We will put all of our bot logic inside the cycle and when it runs, it will repeat until stop() is called, or if the time duration is met.

Part 2 Running the script

Now that you have a script file ready, we should learn how to access it and run it, it'll also help you understand the constructor parameters.

1. The entry point

Run:

src/main/java/com/chromascape/web/ChromaScapeApplication.java

2. Accessing the UI

Open a browser and go to:

http://localhost:8080/

Once the application loads (may take a minute), you should be greeted with the web UI. This is where you'll click a script on the left hand side, enter the duration, whether your application (osrs in this example) is fixed or resizable, and then hit start.

If you've restarted the program, you may also need to refresh the page for it to work.

Part 3 Clicking an image

Lets walk though the idea of clicking an image somewhere on screen. We can also make it modular as this is something often repeated.

1. Accessing a saved image

You should only ever need to store the path of a saved image, not load the file itself.

To store the image path as a class variable use the following structure:

private static final String imageName = "/images/user/your_image.png";

Your image should be stored in:

src/main/resources/images/user

Because it's loaded as a resource.

2. Creating the function

Lets create a function that combines a few utilities to click at a random point within an image.

/**
   * Searches for the provided image template within the current game view, then clicks a random
   * point within the detected bounding box if the match exceeds the defined threshold.
   *
   * @param imagePath the BufferedImage template to locate and click within the game view
   * @param speed the speed that the mouse moves to click the image
   * @param threshold the openCV threshold to decide if a match exists
   */
  private void clickImage(String imagePath, String speed, double threshold) {
    try {
      BufferedImage gameView = controller().zones().getGameView();
      Rectangle boundingBox = TemplateMatching.match(imagePath, gameView, threshold, false);

      if (boundingBox == null || boundingBox.isEmpty()) {
        logger.addLog("Template match failed: No valid bounding box.");
        stop();
        return;
      }

      Point clickLocation = ClickDistribution.generateRandomPoint(boundingBox);

      controller().mouse().moveTo(clickLocation, speed);
      controller().mouse().leftClick();
      logger.addLog("Clicked on image at " + clickLocation);

    } catch (Exception e) {
      logger.addLog("clickImage failed: " + e.getMessage());
      stop();
    }
  }

This example shows you how to get zones from the ZoneManager. Specifically the gameView is stored as a buffered image because of its complex shape. Other zones are automatically rectangles.

You can see how the stateful utility mouse must be accessed through the controller controller().mouse().leftClick();

Exceptions aren't added to the BaseScript because they're caught locally, when there is an exception you must catch it to avoid changing the BaseScript.

Due to its modular nature there are a range of possibilities you have depending on the state of certain Point instances and Rectangles.

Part 5 Keypresses

More information on specific event ID's can be found in the VirtualKeyboardUtils class.

/**
   * Simulates pressing the Escape key by sending the key press and release events to the client
   * keyboard controller.
   */
  private void pressEscape() {
    controller().keyboard().sendModifierKey(401, "esc");
    Sleeper.waitRandomMillis(80, 100);
    controller().keyboard().sendModifierKey(402, "esc");
  }

Part 6 Clicking a Rectangle

Most zones are saved as rectangles (except the gameView). Therefore if you know you want to click a rectangle it's much simpler than having to find out if it exists first.

Lets use inventory slots as an example.

/**
   * Clicks a random point within the bounding box of a given inventory slot.
   *
   * @param slot the index of the inventory slot to click (0-27)
   * @param speed the speed that the mouse moves to click the image
   */
  private void clickInventSlot(int slot, String speed) {
    try {
      Rectangle boundingBox = controller().zones().getInventorySlots().get(slot);
      if (boundingBox == null || boundingBox.isEmpty()) {
        logger.addLog("Inventory slot " + slot + " not found.");
        stop();
        return;
      }

      Point clickLocation = ClickDistribution.generateRandomPoint(boundingBox);

      controller().mouse().moveTo(clickLocation, speed);
      controller().mouse().leftClick();
      logger.addLog("Clicked inventory slot " + slot + " at " + clickLocation);

    } catch (Exception e) {
      logger.addLog("clickInventSlot failed: " + e.getMessage());
      stop();
    }
  }

This function clicks any inventory slot you want, given the number of the slot and the speed of the mouse. Creating small and specific bits of code like this will allow you to modularise your bot and increase code reuse.

Clone this wiki locally