Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 0 additions & 19 deletions CVTemplates.bat
Original file line number Diff line number Diff line change
Expand Up @@ -48,25 +48,6 @@ curl -o "%MOUSE_DIR%\red_4.png" https://raw.githubusercontent.com/kelltom/OS-Bot

echo Mouse clicks downloaded.

REM Download compass degree images from RuneDark repo
echo Cloning RuneDark repo to extract compass degrees...

REM Create compass directory if it doesn't exist
if not exist "src\main\resources\images\ui\compass_degrees" (
mkdir "src\main\resources\images\ui\compass_degrees"
)

REM Clone the repo into a temp folder
git clone --depth 1 https://github.com/cemenenkoff/runedark-public.git temp_runedark

REM Copy compass degree images
xcopy /E /Y "temp_runedark\src\img\bot\ui_templates\compass_degrees\*" "src\main\resources\images\ui\compass_degrees\"

REM Clean up
rmdir /S /Q temp_runedark

echo Compass degree images copied to src\main\resources\images\ui\compass_degrees\

echo UI images downloaded to %UI_DIR%

REM Now generate .index files for fonts
Expand Down
26 changes: 11 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,17 @@ Due to the separation of domain, core and actions utilities it provides a greate
## Mouse input
Here's a very simple scripting example from the [DemoMiningScript](https://github.com/StaticSweep/ChromaScape/blob/main/src/main/java/com/chromascape/scripts/DemoMiningScript.java) that you'll see working below.
```java
private void clickOre() {
try {
BufferedImage gameView = controller().zones().getGameView();
Point clickLoc = PointSelector.getRandomPointInColour(gameView, "Cyan", 15);
if (clickLoc == null) {
stop();
return;
}
controller().mouse().moveTo(clickLoc, "medium");
controller().mouse().leftClick();
} catch (Exception e) {
logger.error(e);
logger.error(e.getStackTrace());
}
private void clickOre() {
BufferedImage gameView = controller().zones().getGameView();
Point clickLoc = PointSelector.getRandomPointInColour(gameView, "Cyan", 15);
if (clickLoc == null) {
logger.error("Click location is null");
stop();
return;
}
controller().mouse().moveTo(clickLoc, "medium");
controller().mouse().leftClick();
}
```


Expand Down Expand Up @@ -99,4 +95,4 @@ ChromaScape includes functionality for identifying images or sprites by comparin
ChromaScape utilises template matching for accurate and fast OCR. This solution - as opposed to machine learning - provides for ocr at runtime. This was inspired by SRL and OSBC.

### Note on dependencies:
This project downloads specific fonts and UI elements from the [OSBC](https://github.com/kelltom/OS-Bot-COLOR/tree/main), [RuneDark](https://github.com/cemenenkoff/runedark-public) and [SRL-dev](https://github.com/Villavu/SRL-Development/tree/master) projects to enable accurate template matching, OCR, and UI consistency. These resources are used solely for educational and research purposes and they are not packaged directly within this repository.
This project downloads specific fonts and UI elements from the [OSBC](https://github.com/kelltom/OS-Bot-COLOR/tree/main) and [SRL-dev](https://github.com/Villavu/SRL-Development/tree/master) projects to enable accurate template matching, OCR, and UI consistency. These resources are used solely for educational and research purposes and they are not packaged directly within this repository.
23 changes: 17 additions & 6 deletions src/main/java/com/chromascape/api/Dax.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.chromascape.api;

import com.chromascape.utils.core.runtime.exception.DaxAuthException;
import com.chromascape.utils.core.runtime.exception.DaxException;
import com.chromascape.utils.core.runtime.exception.DaxRateLimitException;
import java.awt.Point;
import java.io.IOException;
import java.net.URI;
Expand All @@ -15,6 +18,9 @@ public class Dax {

private static final String WALKER_ENDPOINT = "https://walker.dax.cloud/walker/generatePath";

private final HttpClient client =
HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).build();

/**
* Sends a pathfinding request to the DAX Walker API.
*
Expand All @@ -23,12 +29,13 @@ public class Dax {
* @param members True if the player is a member; false otherwise.
* @return Raw JSON string representing the generated path.
* @throws IOException If an IO error occurs during the request.
* @throws InterruptedException If the thread is interrupted or the API returns
* RATE_LIMIT_EXCEEDED (HTTP 429) or INVALID_CREDENTIALS (HTTP 404).
* @throws InterruptedException If the thread is interrupted.
* @throws DaxRateLimitException If HTTP 429 is returned.
* @throws DaxAuthException If credentials or endpoint are invalid (400, 401, 404).
*/
public String generatePath(Point start, Point end, boolean members)
throws IOException, InterruptedException {
HttpClient client = HttpClient.newHttpClient();

String payload =
String.format(
"""
Expand All @@ -39,6 +46,7 @@ public String generatePath(Point start, Point end, boolean members)
}
""",
start.x, start.y, end.x, end.y, members);

HttpRequest request =
HttpRequest.newBuilder()
.uri(URI.create(WALKER_ENDPOINT))
Expand All @@ -48,11 +56,14 @@ public String generatePath(Point start, Point end, boolean members)
.header("secret", "PUBLIC-KEY")
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build();

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

return switch (response.statusCode()) {
case 429 -> "RATE_LIMIT_EXCEEDED";
case 400, 401, 404 -> throw new InterruptedException("INVALID_CREDENTIALS");
default -> response.body();
case 200 -> response.body();
case 429 -> throw new DaxRateLimitException();
case 400, 401, 404 -> throw new DaxAuthException();
default -> throw new DaxException("Unexpected API error: " + response.statusCode());
};
}
}
2 changes: 1 addition & 1 deletion src/main/java/com/chromascape/base/BaseScript.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.chromascape.base;

import com.chromascape.controller.Controller;
import com.chromascape.utils.core.runtime.ScriptStoppedException;
import com.chromascape.utils.core.runtime.exception.ScriptStoppedException;
import com.chromascape.utils.core.state.BotState;
import com.chromascape.utils.core.state.StateManager;
import com.chromascape.utils.core.statistics.StatisticsManager;
Expand Down
86 changes: 38 additions & 48 deletions src/main/java/com/chromascape/scripts/DemoAgilityScript.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,20 +93,12 @@ public class DemoAgilityScript extends BaseScript {
protected void cycle() {
// Log the current XP before clicking obstacle for comparison later
// The idea is to click the obstacle then wait for XP change then loop
int previousXp = -1;
try {
// Read XP
previousXp = Minimap.getXp(this);
// Make sure it's read properly
if (previousXp == -1) {
stop();
DiscordNotification.send("Xp could not be read.");
}
} catch (IOException e) {
logger.error(e);
int previousXp = Minimap.getXp(this);

// Make sure it's read properly
if (previousXp == -1) {
stop();
DiscordNotification.send(
"Bot couldn't read XP bar because of OCR font library load, stopping and logging :(");
DiscordNotification.send("Xp could not be read.");
}

// Check the state of the course
Expand All @@ -121,14 +113,7 @@ protected void cycle() {

// Interact with the detected obstacle
// Clicking continuously until the Red X animation is detected
try {
MovingObject.clickMovingObjectByColourObjUntilRedClick(OBSTACLE_COLOUR, this);
} catch (Exception e) {
logger.error("Mouse movement interrupted while clicking moving object: {}", e.getMessage());
stop();
DiscordNotification.send(
"Mouse movement interrupted while clicking moving object: " + e.getMessage());
}
MovingObject.clickMovingObjectByColourObjUntilRedClick(OBSTACLE_COLOUR, this);

// Wait for the action to complete via XP update
waitUntilXpChange(previousXp);
Expand All @@ -145,22 +130,38 @@ protected void cycle() {
}

/**
* Manages the scenario when nothing is visible.
* Firstly, confirms that it's really lost, if so -> uses the walker to path back to the reset tile.
* Finally, waits for the player's animation to settle after reaching the true tile.
* Manages the scenario when nothing is visible. Firstly, confirms that it's really lost, if so ->
* uses the walker to path back to the reset tile. Finally, waits for the player's animation to
* settle after reaching the true tile.
*/
private void recoverToResetTile() {
// Double check we are actually lost to protect against lag or rendering delays
waitRandomMillis(600, 800);

if (!isObstacleVisible()) {
try {
logger.info("We are lost. Walking to reset tile.");
controller().walker().pathTo(RESET_TILE, true);
// wait for camera to stabilise and walking animation to finish at true tile.
waitRandomMillis(4000, 6000);
} catch (Exception e) {
logger.error("Walker error {}", e.getMessage());
stop();

int attempts = 0;
int allowedAttempts = 5;

while (attempts < allowedAttempts) {
try {
logger.info("We are lost. Walking to reset tile.");
controller().walker().pathTo(RESET_TILE, true);
// wait for camera to stabilise and walking animation to finish at true tile.
waitRandomMillis(4000, 6000);
break;

} catch (IOException e) {
// This exception refers to Timeout or transport error
logger.error("Walker error {}", e.getMessage());
attempts++;

} catch (InterruptedException e) {
// This error means that the thread was interrupted while calling Dax
DiscordNotification.send("Walker thread interrupted, catastrophic failure.");
logger.error("Walker thread interrupted, catastrophic failure.");
stop();
}
}
}
}
Expand All @@ -179,14 +180,9 @@ private boolean clickMarkOfGraceIfPresent() {
Point clickLocation = PointSelector.getRandomPointByColourObj(gameView, MARK_COLOUR, 15, 15.0);

if (clickLocation != null) {
try {
controller().mouse().moveTo(clickLocation, "medium");
controller().mouse().leftClick();
return true;
} catch (Exception e) {
logger.error("Mouse failed while moving to mark of grace {}", e.getMessage());
stop();
}
controller().mouse().moveTo(clickLocation, "medium");
controller().mouse().leftClick();
return true;
}
return false;
}
Expand All @@ -199,14 +195,8 @@ private boolean clickMarkOfGraceIfPresent() {
private void waitUntilXpChange(int previousXp) {
LocalDateTime endTime = LocalDateTime.now().plusSeconds(TIMEOUT_XP_CHANGE);
// Ensure we do not hang if the initial OCR read failed and returned an empty string
try {
while (previousXp == Minimap.getXp(this) && LocalDateTime.now().isBefore(endTime)) {
waitMillis(300);
}
} catch (Exception e) {
logger.error(e);
stop();
DiscordNotification.send("Bot couldn't read XP bar, stopping");
while (previousXp == Minimap.getXp(this) && LocalDateTime.now().isBefore(endTime)) {
waitMillis(300);
}
}

Expand Down
Loading