-
-
Notifications
You must be signed in to change notification settings - Fork 11
Making your first script
- Preamble
- Part 1: Template
- Part 2: Running the script
- Part 3: Clicking an image
- Part 4: Clicking a colour
- Part 5: Keypresses
- Part 6: Clicking a Rectangle
- The finished product
For this we'll use the example demo script DemoWineScript.
Every script in ChromaScape extends BaseScript and implements a looping method called cycle(). The web UI automatically detects and lists any classes you create in the scripts package.
Feel free to look at the complete DemoWineScript in the scripts folder if you get stuck.
Tip
If you hover over a function/class in IntelliJ, it shows you its documentation and usage guidelines (JavaDocs). Take advantage of this if you feel stuck. Our developers have gone to great lengths to ensure that code is well documented. Make sure you've completed the Requirements section before running your scripts.
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.
Create a public class and have it extend the BaseScript. This script should be named the activity you want to bot.
public class DemoWineScript extends BaseScript {
//Everything goes inside this from now on
}Create a logger as shown below to type something out to the UI/terminal:
private final Logger logger = LogManager.getLogger(DemoWineScript.class);Override the cycle() routine.
private final Logger logger = LogManager.getLogger(DemoWineScript.class);
@Override
protected void cycle() {
logger.info("Hello World!");
waitRandomMillis(80, 100);
}All bot logic should go inside cycle(). This method runs repeatedly until stop()` is called.
Now that you have a script file ready, we should learn how to access it and run it.
Run:
src/main/java/com/chromascape/web/ChromaScapeApplication.java
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 then click start.
Let's walk though the idea of clicking an image somewhere on screen. We can also make it modular as this is something often repeated.
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.
You can download sprite/item images from the official OSRS wiki: https://oldschool.runescape.wiki/
Tip
For stacked/banked items (that have numbers over it) you need to crop out the top 10 pixels.
Let's 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) {
BufferedImage gameView = controller().zones().getGameView();
Point clickLocation = PointSelector.getRandomPointInImage(imagePath, gameView, threshold);
if (clickLocation == null) {
logger.error("clickImage click location is null");
stop();
}
controller().mouse().moveTo(clickLocation, speed);
controller().mouse().leftClick();
logger.info("Clicked on image at {}", clickLocation);
}- This example shows you how to get zones from the ZoneManager (
controller().zones()). - The gameView specifically, is stored as a
BufferedImagebecause of its complex shape. Other zones are automatically rectangles. - If a zone is a
Rectangleyou will need to callBufferedImage img = ScreenManager.captureZone(Rectangle zone);to save it as aBufferedImage. - The Threshold is used when the program looks for an image on the screen, a lower threshold means that the match needs to be more accurate. A threshold of
0.05is often preferred, with the maximum being0.15 - You can see how the stateful utility
mouse()must be accessed through the controller:controller().mouse().leftClick();
In this example we will assume there is a purple object on screen that we must click.
EXAMPLES: Highlighting a bank object or NPC purple, using RuneLite's highlight feature (shift + right click)
Tip
Learn more about how to use the Colour Picker utility, to be able to save colours for the framework to see.
/**
* Attempts to locate and click the purple bank object within the game view. It searches for
* purple contours, then clicks a randomly distributed point inside the contour bounding box,
* retrying up to a maximum number of attempts. Logs failures and stops the script if unable to
* click successfully.
*/
private void clickBank() {
// Use the PointSelector to get a random point within the nearest object of a specific colour within the gameView
Point clickLocation =
PointSelector.getRandomPointInColour(
controller().zones().getGameView(), "Cyan", MAX_ATTEMPTS);
// Check if the Point was found correctly
if (clickLocation == null) {
logger.error("clickBank click location is null");
stop();
}
// Click the point
controller().mouse().moveTo(clickLocation, "medium");
controller().mouse().leftClick();
// Use the logger to tell the user
logger.info("Clicked on purple bank object at {}", clickLocation);
}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");
waitRandomMillis(80, 100);
controller().keyboard().sendModifierKey(402, "esc");
}- Most zones are saved as rectangles.
- Rectangle zones are generated dynamically created by the project. e.g. inventory slots, chatbox, control panel, minimap orbs.
- You can access the zones/rectangles through the controller as shown below.
- Learn more about accessing zones in the ZoneManager & SubZoneMapper wiki page.
Let's 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 clickInvSlot(int slot, String speed) {
Rectangle boundingBox = controller().zones().getInventorySlots().get(slot);
if (boundingBox == null || boundingBox.isEmpty()) {
logger.info("Inventory slot {} not found.", slot);
stop();
return;
}
Point clickLocation = ClickDistribution.generateRandomPoint(boundingBox);
if (clickLocation == null) {
logger.error("clickInventSlot click location is null");
stop();
}
controller().mouse().moveTo(clickLocation, speed);
controller().mouse().leftClick();
logger.info("Clicked inventory slot {} at {}", slot, clickLocation);
}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.
-
Pre-requisite Installations
A ground-up tutorial on what to install and how! -
RuneLite Requirements
General requirements for configuring RuneLite to work with this project.
-
Making Your First Script
A beginner-friendly walkthrough of creating and running your first automation script. -
Intermediate Scripting: From Planning to Execution
Look into the theory and execution of a more difficult script, if you're looking to step it up.
-
Colour Picker
How to take a screenshot, define HSV ranges, and create reusable colour profiles. -
ZoneManager & SubZoneMapper
Explanation of how screen regions are mapped and the quirks of Zones. -
Discord Notifications
With less than 5 minutes of setup, send yourself notifications from within your scripts.
-
Contributing Guidelines
Code style, structure, licensing, and how to get involved in improving ChromaScape.