All scripts

AIO Smither

by SolemnVanguard ·Smithing

All in one smither

1

Installed

1.1

Version

23 Aug 2026

Updated

About this script

All in one smither, currently I just added Plate Mail Body, Daggers and Arrows heads for Bronze to Rune.
Tell me if you want something else added, I will update this one at a later time.

Builds

VersionKindPublishedSizeWhat changed
1.1 current text script 23 Aug 2026 11 KB First upload.

Source of 1.1

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

script "AIO Smither"
author "SolemnVanguard"
version "1.1"
every 600

setting SmithItem selectedItem = SmithItem.DAGGER "Which item to smith"
setting BarMaterial selectedMaterial = BarMaterial.BRONZE "Which bar material to use"

enum SmithItem { DAGGER, PLATE_BODY, ARROW_HEADS }
enum BarMaterial { BRONZE, IRON, STEEL, MITHRIL, ADAMANTITE, RUNE }

# Withdraws the correct bar for the selected material (BRONZE=169,
# IRON=170, STEEL=171, MITHRIL=173, ADAMANTITE=174, RUNE=408) from the
# Bank Chest (id 942), walks to the Anvil (id 50) - they stand right
# next to each other, so no path system is used, just direct interact()
# with whichever is needed - selects a bar and uses it on the anvil
# (Hammer id 168 just needs to be present in inventory), and navigates
# the smithing menu for the selected item (same menu labels regardless
# of material):
#   DAGGER       - Make Weapon > Dagger > Make all
#   PLATE_BODY   - Make Armour > Armour > Plate Mail Body > Make All
#   ARROW_HEADS  - Make Missile Heads > Make 10 Arrow Heads > Make All
# The Hammer (id 168) is never deposited and is auto-withdrawn if
# missing, so smithing always has one available. Uses Sleeping Bag
# (id 1263) at 100% fatigue.
#
# After using the bar on the anvil there is a smithing animation of
# variable length before the "Choose an option" dialogue actually
# appears, so the script waits for dialogueOpen() to become true rather
# than guessing a fixed delay. Each answer() call itself waits for the
# dialogue to advance to the next level, so no extra delay is needed
# between steps. Confirmed via testing: this is the dialogueOpen()/
# answer() system, not the right-click menu system.
#
# 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 prevFatigueVal = -1
var haltedFatigueJump = false
var FatigueJumpLimit = 25

var step1 = "Make Weapon"
var step2 = "Dagger"
var step3 = "Make all"
var step4 = ""
var barId = 169
var withdrawAmount = 29
var failedAttempts = 0

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
}

fun setupRecipe() {
    if selectedItem.name == "DAGGER" {
        step1 = "Make Weapon"
        step2 = "Dagger"
        step3 = "Make all"
        step4 = ""
        withdrawAmount = 29
    } else if selectedItem.name == "PLATE_BODY" {
        step1 = "Make Armour"
        step2 = "Armour"
        step3 = "Plate Mail Body"
        step4 = "Make All"
        withdrawAmount = 25   # multiple of 5 bars/item - no leftover remainder
    } else if selectedItem.name == "ARROW_HEADS" {
        step1 = "Make Missile Heads"
        step2 = "Make 10 Arrow Heads"
        step3 = "Make All"
        step4 = ""
        withdrawAmount = 29
    }

    if selectedMaterial.name == "BRONZE" {
        barId = 169
    } else if selectedMaterial.name == "IRON" {
        barId = 170
    } else if selectedMaterial.name == "STEEL" {
        barId = 171
    } else if selectedMaterial.name == "MITHRIL" {
        barId = 173
    } else if selectedMaterial.name == "ADAMANTITE" {
        barId = 174
    } else if selectedMaterial.name == "RUNE" {
        barId = 408
    }
}

# Picks one dialogue option if the smithing dialogue is currently open
# and offers it. answer() itself waits for the dialogue to advance, so
# no manual delay is needed between steps.
fun tryMenuStep(string label) -> bool {
    if label == "" { return false }
    if dialogueOpen() {
        let ok = answer(label)
        print("answer(", label, ") =", ok)
        return ok
    }
    return false
}

on start {
    if not bankAvailable() {
        print("Bank not available")
    }
    setupRecipe()
    print("Selected item:", selectedItem.name, "steps:", step1, step2)
}

state banking {
    fatigueSuspiciousCheck()
    if halted() { return 999999 }
    print("=== banking state entered")

    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: 10)
    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
    }

    # Deposit absolutely everything first (bars, smithed items, anything
    # left over) EXCEPT the Hammer (id 168) - it stays in inventory
    # permanently so smithing never runs without one.
    let hasSomethingToBank = false
    for item in inv() {
        if item.id != 168 {
            hasSomethingToBank = true
        }
    }

    if hasSomethingToBank {
        for item in inv() {
            if item.id != 168 {
                bankDepositAll(item.id)
                wait random(150, 300)
            }
        }
        return 600
    }

    if invItem(168) == none {
        bankWithdraw(168, 1)
        wait random(200, 400)
    }

    bankWithdraw(barId, withdrawAmount)
    wait random(200, 400)
    bankClose()
    become goingToAnvil
}

state goingToAnvil {
    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 anvil = nearestObject(id: 50, within: 10)
    if anvil == none {
        print("No Anvil (50) found nearby - waiting")
        return 1000
    }
    if reach(anvil) != "DRAWN" {
        return 900
    }
    become smithing
}

state smithing {
    fatigueSuspiciousCheck()
    if halted() { return 999999 }
    print("=== smithing state entered, invContains(barId)=", invContains(barId))
    if menuIsOpen() {
        closeMenu()
    }

    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(barId) {
        become goingToBank
    }

    if batchActive() { return 600 }

    let anvil = nearestObject(id: 50, within: 10)
    if anvil == none {
        print("No Anvil (50) found nearby")
        return 1000
    }

    let bar = invItem(barId)
    if bar == none {
        become goingToBank
    }
    let hammer = invItem(168)
    if hammer == none {
        print("No Hammer (168) in inventory - stopping")
        stop
    }

    if reach(anvil) == "DRAWN" {
        let selectedBar = interact(bar, "Use")
        if not selectedBar {
            if menuIsOpen() {
                if menuContains("Use") {
                    clickMenu("Use")
                } else {
                    closeMenu()
                    failedAttempts = failedAttempts + 1
                    if failedAttempts >= 4 {
                        print("Bar selection keeps failing (", failedAttempts, ") - likely not enough bars left for this item, banking instead")
                        failedAttempts = 0
                        become goingToBank
                    }
                    return 600
                }
            } else {
                failedAttempts = failedAttempts + 1
                if failedAttempts >= 4 {
                    print("Bar selection keeps failing (", failedAttempts, ") - likely not enough bars left for this item, banking instead")
                    failedAttempts = 0
                    become goingToBank
                }
                return 600
            }
        }
        failedAttempts = 0
        let usedAnvil = interact(anvil, "Use")
        print("interact(anvil, Use) =", usedAnvil)

        wait until dialogueOpen() timeout 8000 else { return 600 }
        wait random(1200, 1800)
        tryMenuStep(step1)
        wait until dialogueOpen() timeout 3000 else { }
        wait random(900, 1400)
        tryMenuStep(step2)
        wait until dialogueOpen() timeout 3000 else { }
        wait random(900, 1400)
        tryMenuStep(step3)
        wait until dialogueOpen() timeout 3000 else { }
        wait random(900, 1400)
        tryMenuStep(step4)

        print("Waiting for batch to finish...")
        wait until not batchActive() timeout 90000 else { return 600 }

        # batchActive() can dip false briefly BETWEEN individual items in
        # the same "Make All" job (same issue we found with cooking
        # batches earlier) - confirm it stays false for a moment before
        # treating the whole job as actually done, or we cut the batch
        # short after just the first couple of items.
        let confirmStart = nowMillis()
        while not batchActive() and nowMillis() - confirmStart < 1500 {
            wait 200
        }
        if batchActive() {
            print("Batch resumed after a brief gap - still going")
            return 600
        }

        print("Batch finished - becoming goingToBank")

        # Only one "Make All" batch per anvil visit - go bank leftover
        # bars (and whatever got made) rather than immediately trying
        # to smith again with what's left.
        become goingToBank
    }
    return random(1000, 1500)
}

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 chest = nearestObject(id: 942, within: 10)
    if chest == none {
        print("No Bank Chest (942) found nearby - waiting")
        return 1000
    }
    if reach(chest) != "DRAWN" {
        return 900
    }
    become banking
}

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)
    }
}