dubai Posted August 25 Share Posted August 25 (edited) Dubai's OakBurner [BETA] Chop and Burn any Oak Tree! Start this script next to an Oak Tree with your axe equipped or in your inventory with a tinderbox. The script will chop a full inventory of oak logs, and burn them before returning back to the starting tree. (It might wander off while it's firemaking(It handles "You can't light a fire here" and moves to a new spot to keep lighting fires) But the script will return to the starting tree after burning. Nice and simple Onscreen Paint tracking Runtime and XP/LVLs gained. Need some testers on this, I've ran for over an hour without issues, just moving on to other things. If you encounter bugs/crashes please try and screenshot the issue and provide as much info as you can and I'll return to this project for fixes. I've been running it south/west falador and it works really well over 20k FM xp/hr depending on your axe. The bot seems really smooth, especially firemaking. DOWNLOAD: (EXTRACT TO YOUR "OSBOT/SCRIPTS/" PATH) If anyone could upload some screenshots of nice runtimes, that would be awesome! Source: Spoiler import org.osbot.rs07.api.map.Position; import org.osbot.rs07.api.model.Entity; import org.osbot.rs07.api.ui.Skill; import org.osbot.rs07.canvas.paint.Painter; import org.osbot.rs07.listener.MessageListener; import org.osbot.rs07.script.Script; import org.osbot.rs07.script.ScriptManifest; import org.osbot.rs07.utility.ConditionalSleep; import org.osbot.rs07.api.ui.Message; import java.util.HashSet; import java.util.Set; import java.awt.*; @ScriptManifest(author = "Dubai", info = "Burn any oak tree!", name = "OakBurner", version = 1, logo = "") public class OakBurner extends Script implements MessageListener, Painter { private static final int OAK_TREE_ID = 10820; private boolean fireLit = false; private int moveRetryCount = 0; private static final int MAX_RETRY_COUNT = 3; // Paint-related variables private long startTime; private int startingWoodcuttingLevel; private int startingFiremakingLevel; private int startingWoodcuttingXP; private int startingFiremakingXP; private boolean shouldMoveToSafeSpot = false; private Position startingPosition; // Save the starting position private enum State { CHOPPING, LIGHTING_FIRES, MOVING_TO_SAFE_SPOT, RETURNING_TO_START } private State currentState = State.CHOPPING; @Override public void onStart() { getBot().addMessageListener(this); // Save the starting position startingPosition = myPosition(); // Initialize paint-related variables startTime = System.currentTimeMillis(); startingWoodcuttingLevel = skills.getStatic(Skill.WOODCUTTING); startingFiremakingLevel = skills.getStatic(Skill.FIREMAKING); startingWoodcuttingXP = skills.getExperience(Skill.WOODCUTTING); startingFiremakingXP = skills.getExperience(Skill.FIREMAKING); } @Override public void onExit() { getBot().removeMessageListener(this); } @Override public int onLoop() throws InterruptedException { log("Current State: " + currentState); switch (currentState) { case CHOPPING: chopOakTree(); break; case LIGHTING_FIRES: lightFires(); break; case MOVING_TO_SAFE_SPOT: moveToSafeSpot(); break; case RETURNING_TO_START: returnToStart(); break; } return random(200, 300); } private void chopOakTree() { log("State: CHOPPING"); if (inventory.isFull()) { log("Inventory full, switching to LIGHTING_FIRES state."); currentState = State.LIGHTING_FIRES; return; } Entity oakTree = getObjects().closest(OAK_TREE_ID); if (oakTree != null && oakTree.interact("Chop down")) { log("Chopping Oak tree at: " + oakTree.getPosition()); waitUntilNotAnimating(); if (!inventory.isFull()) { log("Finished chopping. Inventory not full. Retrying CHOPPING state."); chopOakTree(); // Retry chopping if inventory isn't full } else { log("Inventory full, switching to LIGHTING_FIRES state."); currentState = State.LIGHTING_FIRES; } } else { log("No Oak tree found or failed to interact."); } } private void lightFires() throws InterruptedException { log("State: LIGHTING_FIRES"); // Check if the bot should move based on the chat message if (shouldMoveToSafeSpot) { log("Should move to a safe spot due to chat message."); currentState = State.MOVING_TO_SAFE_SPOT; shouldMoveToSafeSpot = false; // Reset the flag after setting the state return; } if (!inventory.contains("Oak logs")) { log("No Oak logs left. Switching to RETURNING_TO_START state."); currentState = State.RETURNING_TO_START; return; } if (inventory.contains("Tinderbox") && inventory.contains("Oak logs")) { log("Using Tinderbox on Oak logs."); if (inventory.getItem("Tinderbox").interact("Use")) { new ConditionalSleep(1000) { @Override public boolean condition() { return inventory.isItemSelected(); } }.sleep(); if (inventory.isItemSelected()) { if (inventory.getItem("Oak logs").interact("Use")) { log("Using Oak logs."); new ConditionalSleep(3000) { @Override public boolean condition() { return myPlayer().isAnimating(); } }.sleep(); if (myPlayer().isAnimating()) { log("Player is animating, waiting for fire to be lit."); new ConditionalSleep(5000) { @Override public boolean condition() { return !myPlayer().isAnimating(); } }.sleep(); if (fireLit) { log("Fire lit successfully. Continuing to light more fires."); fireLit = false; lightFires(); } else if (shouldMoveToSafeSpot) { // Double-check before retrying log("Detected 'You can't light a fire here.' while animating. Switching to MOVING_TO_SAFE_SPOT state."); currentState = State.MOVING_TO_SAFE_SPOT; shouldMoveToSafeSpot = false; } else { log("Retrying lighting fires."); lightFires(); } } else { log("Failed to start lighting logs, retrying."); lightFires(); } } else { log("Failed to interact with Oak logs."); } } else { log("Failed to select Tinderbox."); } } else { log("Failed to use Tinderbox."); } } else { log("Tinderbox or Oak logs not found in inventory."); } } private Set<Position> attemptedSafeSpots = new HashSet<>(); private void moveToSafeSpot() throws InterruptedException { log("State: MOVING_TO_SAFE_SPOT"); Position safeSpot = findSafeSpot(3, 6); if (safeSpot == null || safeSpot.equals(myPosition())) { log("No suitable safe spot found. Expanding search area."); safeSpot = findSafeSpot(6, 10); } if (safeSpot != null && !safeSpot.equals(myPosition())) { log("Moving to safe spot at: " + safeSpot); attemptedSafeSpots.add(safeSpot); // Add the safe spot to the attempted list if (getWalking().webWalk(safeSpot)) { // Use webWalk instead of walk Position finalSafeSpot = safeSpot; new ConditionalSleep(7000) { @Override public boolean condition() { return myPlayer().getPosition().equals(finalSafeSpot); } }.sleep(); if (myPlayer().getPosition().equals(finalSafeSpot)) { log("Arrived at the safe spot. Switching to LIGHTING_FIRES state."); currentState = State.LIGHTING_FIRES; moveRetryCount = 0; lightFires(); // Call the lightFires method to start lighting fires again } else { log("Failed to reach the safe spot. Retrying."); retryMovement(); } } else { log("Failed to initiate webWalk. Retrying."); retryMovement(); } } else { log("Failed to find a safe spot. Retrying."); retryMovement(); } } private void retryMovement() throws InterruptedException { log("Failed to move to the safe spot. Retrying."); moveRetryCount++; if (moveRetryCount >= MAX_RETRY_COUNT) { log("Max retry count reached, returning to starting location."); currentState = State.RETURNING_TO_START; moveRetryCount = 0; } else { moveToSafeSpot(); } } private void waitUntilNotAnimating() { new ConditionalSleep(5000) { @Override public boolean condition() { return !myPlayer().isAnimating(); } }.sleep(); } private boolean useItem(String itemName) { return inventory.getItem(itemName).interact("Use"); } private Position getSafeSpot() { return findSafeSpot(3, 6); } private Position getSafeSpotFarther() { return findSafeSpot(6, 10); } private Position findSafeSpot(int minRadius, int maxRadius) { for (int radius = minRadius; radius <= maxRadius; radius++) { for (int x = -radius; x <= radius; x++) { for (int y = -radius; y <= radius; y++) { Position pos = myPosition().translate(x, y); if (!pos.equals(myPosition()) && getMap().canReach(pos) && !isFireNearby(pos) && !attemptedSafeSpots.contains(pos)) { log("Found safe spot: " + pos); return pos; } } } } log("No safe spot found within radius."); return null; } private boolean isFireNearby(Position pos) { return getObjects().closest(obj -> obj != null && obj.getName().equals("Fire") && obj.getPosition().equals(pos)) != null; } private void returnToStart() { log("State: RETURNING_TO_START"); if (getWalking().webWalk(startingPosition)) { new ConditionalSleep(7000) { @Override public boolean condition() { return myPlayer().getPosition().equals(startingPosition); } }.sleep(); if (myPlayer().getPosition().equals(startingPosition)) { log("Returned to starting position. Switching to CHOPPING state."); currentState = State.CHOPPING; } else { log("Failed to return to starting position. Retrying."); retryReturnToStart(); } } else { log("Failed to initiate webWalk to starting position. Retrying."); retryReturnToStart(); } } private void retryReturnToStart() { log("Retrying return to starting position."); moveRetryCount++; if (moveRetryCount >= MAX_RETRY_COUNT) { log("Max retry count reached. Switching to CHOPPING state."); currentState = State.CHOPPING; moveRetryCount = 0; } else { returnToStart(); } } @Override public void onMessage(Message message) throws InterruptedException { log("Received message: " + message.getMessage()); if (message.getType() == Message.MessageType.GAME) { if (message.getMessage().contains("The fire catches and the logs begin to burn.") || message.getMessage().matches(".*logs begin to burn.*")) { log("Detected fire lit message."); fireLit = true; } else if (message.getMessage().contains("You can't light a fire here.")) { log("Detected 'You can't light a fire here.' message."); shouldMoveToSafeSpot = true; // Set the flag to indicate movement is needed } } } @Override public void onPaint(Graphics2D g) { long runTime = System.currentTimeMillis() - startTime; int currentWoodcuttingLevel = skills.getStatic(Skill.WOODCUTTING); int currentFiremakingLevel = skills.getStatic(Skill.FIREMAKING); int woodcuttingXP = skills.getExperience(Skill.WOODCUTTING) - startingWoodcuttingXP; int firemakingXP = skills.getExperience(Skill.FIREMAKING) - startingFiremakingXP; int xpPerHourWoodcutting = (int) (woodcuttingXP * 3600000D / runTime); int xpPerHourFiremaking = (int) (firemakingXP * 3600000D / runTime); g.setColor(Color.WHITE); g.drawString("Runtime: " + formatTime(runTime), 10, 30); g.drawString("Woodcutting Level: " + currentWoodcuttingLevel + " (+" + (currentWoodcuttingLevel - startingWoodcuttingLevel) + ")", 10, 45); g.drawString("Firemaking Level: " + currentFiremakingLevel + " (+" + (currentFiremakingLevel - startingFiremakingLevel) + ")", 10, 60); g.drawString("Woodcutting XP/Hour: " + xpPerHourWoodcutting, 10, 75); g.drawString("Firemaking XP/Hour: " + xpPerHourFiremaking, 10, 90); } private String formatTime(long ms) { long totalSeconds = ms / 1000; long hours = totalSeconds / 3600; long minutes = (totalSeconds % 3600) / 60; long seconds = totalSeconds % 60; return String.format("%02d:%02d:%02d", hours, minutes, seconds); } } Edited October 5 by dubai Quote Link to comment Share on other sites More sharing options...
Fanny Posted August 25 Share Posted August 25 Wow, you're on fire with these releases... Literally with this one! Had a quick glance over it, looks good! 1 Quote Link to comment Share on other sites More sharing options...
dubai Posted August 25 Author Share Posted August 25 (edited) 43 minutes ago, Fanny said: Wow, you're on fire with these releases... Literally with this one! Had a quick glance over it, looks good! Very punny response, thank you These 2 open source projects were some of my early projects, still fun and they do work nicely. Edited August 25 by dubai Quote Link to comment Share on other sites More sharing options...