All scripts

AIO Smelter

by SolemnVanguard ·Smithing

All in one smelter

1

Installed

1.0

Version

23 Aug 2026

Updated

About this script

All in one smelter, smelts all bars from Bronze to Rune.

Builds

VersionKindPublishedSizeWhat changed
1.0 current text script 23 Aug 2026 10 KB First upload.

Source of 1.0

Published by the author. This is the current build, exactly as it will be delivered.

script "AIO Bar Smelter"
author "SolemnVanguard"
version "1.0"
every 600

setting BarType selectedBar = BarType.BRONZE "Which bar to smelt (only one at a time)"

enum BarType { BRONZE, IRON, STEEL, SILVER, GOLD, MITHRIL, ADAMANTITE, RUNE }

# Withdraws the correct ore(s) - and coal, if the selected bar needs it -
# from the Bank Chest (id 942) near tile(333, 553), walks to the Furnace
# (id 118) via tile(324, 553), tile(315, 550), and tile(311, 545),
# smelts them, then walks back and deposits the bars, repeating forever.
# Uses Sleeping Bag (id 1263) at 100% fatigue.
#
# Recipes (ore ids, coal-per-bar, bar id):
#   BRONZE     - copper ore (150) + tin ore (202), no coal      -> 169
#   IRON       - iron ore (151), no coal                        -> 170
#   STEEL      - iron ore (151) + 2 coal (155)                  -> 171
#   SILVER     - silver ore (383), no coal                      -> 384
#   GOLD       - gold ore (152), no coal                        -> 172
#   MITHRIL    - mithril ore (153) + 4 coal (155)                -> 173
#   ADAMANTITE - adamantite ore (154) + 6 coal (155) (estimated) -> 174
#   RUNE       - runite ore (409) + 8 coal (155) (estimated)    -> 408
# The adamantite/rune coal amounts follow the classic 2/4/6/8 coal
# progression by tier but were not separately confirmed - verify in
# game and adjust coalPerBar below if smelting fails partway.
#
# The withdraw amount is computed from how many total inventory slots
# one "unit" of the recipe needs (1 ore, or 1 ore + N coal, or 2 ores
# for bronze), so it always fits in the 30-slot inventory regardless of
# which bar is selected.
#
# Fatigue failsafe: if fatigue jumps more than 25 points in a single
# pass, that is treated as suspicious (e.g. a mod forcing it up to test
# you) and the script halts (shows an orange warning) instead of using
# the Sleeping Bag as if it were normal.

var stuckCount = 0

var prevFatigueVal = -1
var haltedFatigueJump = false
var FatigueJumpLimit = 25

var oreIdA = 150
var oreIdB = -1
var coalPerBar = 0
var barId = 169
var withdrawAmount = 15
var useCoalAsSelector = false

fun halted() -> bool {
    return haltedFatigueJump
}

fun fatigueSuspiciousCheck() {
    let curFatigue = fatigue()
    if prevFatigueVal >= 0 {
        let jump = curFatigue - prevFatigueVal
        if jump > FatigueJumpLimit {
            print("Fatigue spike detected:", prevFatigueVal, "->", curFatigue, "- halting")
            haltedFatigueJump = true
        }
    }
    prevFatigueVal = curFatigue
}

# Sets oreIdA/oreIdB/coalPerBar/barId/withdrawAmount from the chosen
# BarType. withdrawAmount = how many units of the recipe fit in 30
# inventory slots (1 slot spare), where a "unit" is 1 ore (+ coal, or
# + a second ore for bronze).
fun setupRecipe() {
    useCoalAsSelector = false
    if selectedBar.name == "BRONZE" {
        oreIdA = 150
        oreIdB = 202
        coalPerBar = 0
        barId = 169
        withdrawAmount = 15
    } else if selectedBar.name == "IRON" {
        oreIdA = 151
        oreIdB = -1
        coalPerBar = 0
        barId = 170
        withdrawAmount = 30
    } else if selectedBar.name == "STEEL" {
        oreIdA = 151
        oreIdB = -1
        coalPerBar = 2
        barId = 171
        withdrawAmount = 10
        useCoalAsSelector = true
    } else if selectedBar.name == "SILVER" {
        oreIdA = 383
        oreIdB = -1
        coalPerBar = 0
        barId = 384
        withdrawAmount = 30
    } else if selectedBar.name == "GOLD" {
        oreIdA = 152
        oreIdB = -1
        coalPerBar = 0
        barId = 172
        withdrawAmount = 30
    } else if selectedBar.name == "MITHRIL" {
        oreIdA = 153
        oreIdB = -1
        coalPerBar = 4
        barId = 173
        withdrawAmount = 6
    } else if selectedBar.name == "ADAMANTITE" {
        oreIdA = 154
        oreIdB = -1
        coalPerBar = 6
        barId = 174
        withdrawAmount = 4
    } else if selectedBar.name == "RUNE" {
        oreIdA = 409
        oreIdB = -1
        coalPerBar = 8
        barId = 408
        withdrawAmount = 3
    }
}

on start {
    if not bankAvailable() {
        print("Bank not available")
    }
    if not minimapAvailable() {
        print("Warning: minimap hooks not ready yet - walkPath may misbehave")
    }

    setupRecipe()
    print("Selected bar:", selectedBar.name, "ore A:", oreIdA, "ore B:", oreIdB)
    print("coal/bar:", coalPerBar, "withdraw:", withdrawAmount)

    pathStart("route", "bank", tile(333, 553))
    pathTile("route", tile(324, 553))
    pathTile("route", tile(315, 550))
    pathFinish("route", "furnace", tile(311, 545))
}

state banking {
    fatigueSuspiciousCheck()
    if halted() { return 999999 }

    if isSleeping() {
        print("Sleeping, fatigue left to sleep off:", sleepFatigue())
        return 2000
    }
    if fatigue() >= 100 {
        if bankIsOpen() {
            bankClose()
            return 600
        }
        print("Fatigue is 100 - using Sleeping Bag")
        let bag = invItem(1263)
        if bag != none {
            interact(bag, "Sleep")
        }
        return random(600, 900)
    }

    setupRecipe()

    let chest = nearestObject(id: 942, within: 5)
    if chest == none {
        print("No Bank Chest (942) found - waiting")
        return 1000
    }

    if not bankIsOpen() {
        if reach(chest) == "DRAWN" {
            interact(chest, "open")
            wait until bankIsOpen() timeout 2000 else { return 300 }
        }
        return 1000
    }

    if invContains(barId) {
        bankDepositAll(barId)
        wait random(200, 500)
        return 600
    }

    if not bankWithdraw(oreIdA, withdrawAmount) {
        print("Failed to withdraw ore A (", oreIdA, ") - bank may be out of stock")
        bankClose()
        return 1000
    }
    wait random(200, 400)
    if oreIdB != -1 {
        if not bankWithdraw(oreIdB, withdrawAmount) {
            print("Failed to withdraw ore B (", oreIdB, ") - bank may be out of stock")
            bankClose()
            return 1000
        }
        wait random(200, 400)
    }
    if coalPerBar > 0 {
        if not bankWithdraw(155, withdrawAmount * coalPerBar) {
            print("Failed to withdraw coal - bank may be out of stock")
            bankClose()
            return 1000
        }
        wait random(200, 400)
    }
    bankClose()
    become goingToFurnace
}

state goingToFurnace {
    fatigueSuspiciousCheck()
    if halted() { return 999999 }

    if isSleeping() {
        print("Sleeping, fatigue left to sleep off:", sleepFatigue())
        return 2000
    }
    if fatigue() >= 100 {
        print("Fatigue is 100 - using Sleeping Bag")
        let bag = invItem(1263)
        if bag != none {
            interact(bag, "Sleep")
        }
        return random(600, 900)
    }

    let how = walkPath("route", "furnace")
    print("walkPath to furnace =", how)

    if how == "ARRIVED" {
        stuckCount = 0
        become smelting
    } else if how == "WALKING" {
        stuckCount = 0
        return 900
    } else if how == "CROSSING" {
        stuckCount = 0
        return 1500
    } else if how == "RECOVERING" or how == "BLOCKED" {
        return 900
    } else if how == "STUCK" or how == "LOST" {
        stuckCount = stuckCount + 1
        print("Stuck/lost (", stuckCount, ") going to furnace")
        if stuckCount >= 10 {
            print("Been stuck too long - resetting path")
            pathReset("route")
            stuckCount = 0
        }
        return 1200
    } else {
        print("goingToFurnace status:", how)
        return 1000
    }
}

state smelting {
    fatigueSuspiciousCheck()
    if halted() { return 999999 }

    if isSleeping() {
        print("Sleeping, fatigue left to sleep off:", sleepFatigue())
        return 2000
    }
    if fatigue() >= 100 {
        print("Fatigue is 100 - using Sleeping Bag")
        let bag = invItem(1263)
        if bag != none {
            interact(bag, "Sleep")
        }
        return random(600, 900)
    }

    if not invContains(oreIdA) {
        become goingToBank
    }
    if oreIdB != -1 and not invContains(oreIdB) {
        become goingToBank
    }

    if batchActive() { return 600 }

    let furnace = nearestObject(id: 118, within: 10)
    if furnace == none {
        print("No Furnace (118) found nearby")
        return 1000
    }

    # Only STEEL selects coal and uses it on the furnace (with iron ore
    # just sitting in inventory). MITHRIL/ADAMANTITE/RUNE work the other
    # way around: you select the ORE itself and use it on the furnace,
    # with coal just sitting in inventory.
    let selectItemId = oreIdA
    if useCoalAsSelector {
        selectItemId = 155
    }
    let ore = invItem(selectItemId)
    if ore == none {
        become goingToBank
    }

    if reach(furnace) == "DRAWN" {
        let selected = interact(ore, "Use")
        if not selected {
            return 600
        }
        let used = interact(furnace, "Use")
        if not used {
            walkTo(myTile())
        } else {
            wait until not batchActive() timeout 90000 else { return 600 }
        }
    }
    return random(1500, 2000)
}

state goingToBank {
    fatigueSuspiciousCheck()
    if halted() { return 999999 }

    if isSleeping() {
        print("Sleeping, fatigue left to sleep off:", sleepFatigue())
        return 2000
    }
    if fatigue() >= 100 {
        print("Fatigue is 100 - using Sleeping Bag")
        let bag = invItem(1263)
        if bag != none {
            interact(bag, "Sleep")
        }
        return random(600, 900)
    }

    let how = walkPath("route", "bank")
    print("walkPath to bank =", how)

    if how == "ARRIVED" {
        stuckCount = 0
        become banking
    } else if how == "WALKING" {
        stuckCount = 0
        return 900
    } else if how == "CROSSING" {
        stuckCount = 0
        return 1500
    } else if how == "RECOVERING" or how == "BLOCKED" {
        return 900
    } else if how == "STUCK" or how == "LOST" {
        stuckCount = stuckCount + 1
        print("Stuck/lost (", stuckCount, ") going to bank")
        if stuckCount >= 10 {
            print("Been stuck too long - resetting path")
            pathReset("route")
            stuckCount = 0
        }
        return 1200
    } else {
        print("goingToBank status:", how)
        return 1000
    }
}

on render {
    if halted() {
        let flashOn = wrap(nowMillis(), 0, 999) < 500
        let bg = "#cc6600ee"
        if not flashOn { bg = "#663300ee" }
        paintRoundFill(4, 300, 220, 60, 12, bg)
        paintTextCenteredSized(114, 322, "FATIGUE SPIKE", "white", 16)
        paintTextCenteredSized(114, 344, "SCRIPT STOPPED", "white", 12)
    }
}