Firemaking
by SolemnVanguard ·Firemaking
RSCFalador
Chops nearest tree and light up
5
Installed
3.0
Version
25 Aug 2026
Updated
About this script
Chops nearest tree and light it up, will not light up logs if there is a fire there already. Has built in sleeper.
Builds
| Version | Kind | Published | Size | What changed |
|---|---|---|---|---|
| 3.0 current | text script | 24 Aug 2026 | 8 KB | Fixed everything that didn't work lol. |
| 1.1 | text script | 20 Aug 2026 | 6 KB | Added ignore logs on fern, flower Added mod protection Improve logic |
| 1.0 | text script | 20 Aug 2026 | 3 KB | First upload. |
Source of 3.0
Published by the author. This is the current build, exactly as it will be delivered.
script "Woodcutter Firemaker" author "you" version "3.0" description "Chops trees (skipping ones with fire within 1 tile) and lights the logs where they drop, using explicit states instead of one big loop" category "Woodcutting" every 600 setting int treeSearchRadius = 30 "How far to search for a tree that has no fire within 1 tile of it (raise this if it stops with trees still visible - it means the nearby ones are all next to a fire)" # Rewritten from scratch as two explicit states instead of one big on-loop # juggling several interacting flags - much easier to reason about, and # each state only ever does one job. # # Meant to be run with the inventory already FULL of junk items # ("power-chopping") - with no room to receive the log, the game drops it # straight onto the ground at your feet instead, which is what the # tinderbox step finds and lights. Runs forever - no banking. # # state chopping: picks the nearest Tree (id 1) with no Fire within 1 tile # of it, clicks Chop, and waits for the server's own "You get some wood" # message (up to 20s, then moves on anyway as a failsafe) before doing # anything else - this is what stops it from wandering off to a new tree # while a log it just dropped is still sitting there unlit. Then becomes # handlingLog. # # state handlingLog: drops any log that somehow ended up in the inventory # (safety net - shouldn't happen with a full inventory), then finds the # dropped log and lights it once the tile is clear of Fire/Fern/Flower and # a Tinderbox is available. If a specific log is still blocked after 15s # (a lingering fire, a fern that won't move, no tinderbox), prints exactly # why once and gives up on it, going back to chopping another tree rather # than waiting forever or spamming the same line. var lastTile = tile(0, 0) var haveLastTile = false var teleported = false var lastFatigueMillis = -1 var fatigueSuspicious = false var fatigueSuspiciousWindowMs = 120000 # 2 minutes var choppingConfirmed = false var choppingSince = 0 var blockedLogTile = tile(0, 0) var blockedSince = 0 on message(line) { if contains(line, "You get some wood") { choppingConfirmed = true } } on start { if not batchAvailable() { print("batch hooks missing - clicks may double up") } if not invIsFull() { print("Inventory is not full - fill it with junk first so chopped logs drop to the ground instead of sitting in your bag unused") } } fun teleportCheck() { let now = myTile() if haveLastTile { let jumped = distance(lastTile, now) if jumped > 20 { print("Jumped", jumped, "tiles - stopping") teleported = true } } lastTile = now haveLastTile = true } fun fatigueSuspiciousCheck() { if fatigue() >= 100 { let now = nowMillis() if lastFatigueMillis >= 0 and (now - lastFatigueMillis) < fatigueSuspiciousWindowMs { print("Fatigue hit 100 again within", fatigueSuspiciousWindowMs, "ms - stopping") fatigueSuspicious = true } lastFatigueMillis = now } } fun hasFireNear(tile t, int radius) -> bool { return objectCount(name: "Fire", area: areaAround(t, radius)) > 0 } # The nearest Tree (id 1) that does NOT have a Fire within 1 tile of it - # chopping right next to a fire is skipped entirely. Searches out to # treeSearchRadius since in a dense patch a couple of fires can put # several close trees on cooldown at once - it reaches past them for one # further out rather than giving up. fun pickTree() -> object? { for candidate in objectsByDistance(id: 1, within: treeSearchRadius) { if not hasFireNear(candidate.tile, 1) { return candidate } } return none } state chopping { teleportCheck() if teleported { return 999999 } fatigueSuspiciousCheck() if fatigueSuspicious { return 999999 } if isSleeping() { print("Sleeping, fatigue left:", sleepFatigue()) return 2000 } if fatigue() >= 100 { let bag = invItem(1263) if bag != none { interact(bag, "Sleep") } return random(600, 900) } if batchActive() { return 600 } if choppingConfirmed { choppingConfirmed = false choppingSince = 0 become handlingLog } if choppingSince > 0 { let waited = nowMillis() - choppingSince if waited > 20000 { print("No 'You get some wood' after 20s - moving on anyway") choppingSince = 0 become handlingLog } return 300 } let tree = pickTree() if tree == none { print("no tree in sight without a fire within 1 tile") return 1200 } if not interact(tree, "Chop") { return 600 } # interact narrates its own reason choppingSince = nowMillis() wait until batchActive() timeout 3000 else { } return random(1500, 2000) } state handlingLog { teleportCheck() if teleported { return 999999 } fatigueSuspiciousCheck() if fatigueSuspicious { return 999999 } if isSleeping() { print("Sleeping, fatigue left:", sleepFatigue()) return 2000 } if fatigue() >= 100 { let bag = invItem(1263) if bag != none { interact(bag, "Sleep") } return random(600, 900) } if batchActive() { return 600 } if invContains(14) { # Shouldn't normally happen with a full inventory (see header) - a # log landed in the bag anyway, so drop it rather than let it sit # there unused and unnoticed. dropAll(14) return random(400, 700) } let logs = nearestDrop(id: 14, within: 10) if logs == none { # Nothing to light (shouldn't normally happen right after a # confirmed chop) - don't get stuck here, go chop another tree. print("No log found to light - back to chopping") blockedLogTile = tile(0, 0) become chopping } let onTile = areaAround(logs.tile, 0) let fireHere = hasFireNear(logs.tile, 0) let fernHere = objectCount(name: "Fern", area: onTile) > 0 let flowerHere = objectCount(name: "Flower", area: onTile) > 0 let tinderbox = invItem(166) if not fireHere and not fernHere and not flowerHere and tinderbox != none { interact(tinderbox, "Use") if not interact(logs, "Use") { return 600 } # interact narrates its own reason wait until batchActive() timeout 3000 else { } blockedLogTile = tile(0, 0) become chopping } # Blocked - wait it out briefly, but give up and move to another tree # if it's STILL blocked 15s later, rather than waiting forever (e.g. a # fire that lingers a long time, or no tinderbox at all). if logs.tile != blockedLogTile { blockedLogTile = logs.tile blockedSince = nowMillis() print("Log at", str(logs.tile), "is blocked:") if fireHere { print(" - fire on this tile") } if fernHere { print(" - fern nearby") } if flowerHere { print(" - flower nearby") } if tinderbox == none { print(" - no tinderbox in inventory") } return 1000 } if nowMillis() - blockedSince < 15000 { return 1000 } print("Log at", str(logs.tile), "still blocked after 15s - giving up on it, moving to another tree") blockedLogTile = tile(0, 0) become chopping } on render { if teleported { paintRoundFill(4, 300, 220, 60, 12, "#cc0000ee") paintTextCenteredSized(114, 322, "TELEPORTED", "white", 16) paintTextCenteredSized(114, 344, "SCRIPT STOPPED", "white", 12) } if fatigueSuspicious { paintRoundFill(4, 300, 220, 60, 12, "#cc6600ee") paintTextCenteredSized(114, 322, "FATIGUE LOOP", "white", 16) paintTextCenteredSized(114, 344, "SCRIPT STOPPED", "white", 12) } }