← Developer · changelog

launcher

v2026.09.04.2215

4 Sept 2026
Shops


Scripts can now read a shop and trade with it. Everything the shop window knows is readable, and buying and selling go out as the client's own packets rather than by driving the dialog.




Reading a shop


Open a shop and a script can see the whole of it: every slot, what is in it, how many the shop has, and what each one is worth in both directions.

on loop {
    if not shopIsOpen() { return 1000 }

    for row in shop() {
        print(row.name, "x" + str(row.stock), "buy", row.buyPrice, "sell", row.sellPrice)
    }
    return 600
}


  • Every slot, as a handle.
    shop()
    hands back the filled slots in grid order, each with
    .slot .id .stock .buyPrice .sellPrice .priceBasis .name
    . Find one directly with
    shopItem(151)
    ,
    shopItem("Coal")
    or
    shopSlot(2)
    .
  • The shop's own numbers as well:
    shopIsGeneralStore()
    ,
    shopCount()
    ,
    shopBuyPriceMod()
    ,
    shopSellPriceMod()
    ,
    shopPriceMultiplier()
    , and which slot the window has highlighted.
  • A stock of 0 is a real slot the shop has run out of — still sellable to, not buyable until it restocks. In a general store your own bag is listed alongside the stock, which is how selling there works at all, and those slots sit at 0.





Prices, properly


Classic shops do not have a price list. The price is worked out fresh from the item's value, the shop's two percentages, and how far that slot's stock has drifted from where the shop thinks it should be — so every one you buy makes the next one dearer, and every one you sell makes the next one cheaper.

Scripts get that same curve rather than an approximation of it:

# what one costs, and what ten actually cost - not ten times the first
print("each:", shopBuyPrice("Coal"))
print("ten :", shopBuyPrice("Coal", 10))


So a script can decide whether a stack is worth buying before it buys any of it, and a shop-clearing bot can stop at the point the price stops being worth it. A price of
-1
means the trade would not be offered at all — out of stock, or something that shop will not take.




Buying and selling


shopBuy("Coal", 10)
shopSell("Bones", 5)
shopSellAll("Bones")
shopClose()


  • They wait for the result. A
    false
    means the trade really did not happen, not "the packet left" — the command sends and then waits for the bag to actually change, and prints why if it does not. No hand-tuned sleeps.
  • The window's own rules are kept. The amount is clamped to the shop's stock when buying and to what you are carrying when selling; a shop that does not list an item will not be asked to take it; and bank notes are refused anywhere but a general store, exactly as the game refuses them.
  • By id or by name, everywhere —
    shopBuy(151, 10)
    and
    shopBuy("Iron ore", 10)
    both work, and so do the price readers.





For Java scripts


The same thing lives at
ctx.shop()
:
isOpen
,
all
,
byId
/
byName
/
bySlot
,
stockOf
,
buyPrice
/
sellPrice
,
buy
,
sell
,
close
, plus
Shop.price(...)
if you want to ask what a trade would cost without making it.




Notes


  • RSCFalador for now.
    shopAvailable()
    says whether this client publishes the shop, and
    shopMissing()
    names anything unbound — so a script can say what is wrong instead of quietly doing nothing.
  • Prices need the item values the client keeps in its own cache. Where those are not published every price reads
    -1
    and
    shopPricesKnown()
    is false; the stock and the trading still work.
  • Every command has its own card in the shop group of the command reference, and the Claude scripting skill has been updated with worked examples: reading a shop, buying a stack at a price limit, and emptying the bag into a general store.