| name | uo-buy-items |
| description | Buy items from an NPC vendor with the headless UO client — find the vendor, open their buy list with the "<Name> buy" speech command, list what's for sale, and purchase. Use whenever the user asks to buy/purchase something from a shop, vendor, or shopkeeper. |
Buying from an NPC vendor
Requires a logged-in character with the map loaded (see uo-login), and usually some
walking to reach the vendor (see uo-navigation).
1. Identify the vendor
mobiles only shows generic "Human (Male/Female)" — NPCs' real names only show up via
a single-click, which triggers an overhead name bark:
echo "mobiles 8" >> /tmp/cuocmd
sleep 1
tail -15 /tmp/cuolog
Click each candidate to get its name (order of clicks matches order of bark replies):
printf 'click <serial1>\nclick <serial2>\n' >> /tmp/cuocmd
sleep 2
tail -15 /tmp/cuolog
Look for a name+profession like "Deval the bowyer" — that's your vendor, and the shop
you want them to open usually matches their trade (bowyer → bows/arrows, tinker →
tools, etc.).
2. Get adjacent and open their buy list
Double-clicking a vendor does not open their shop here — it opens their paperdoll.
The real trigger is a speech command, and it must be their actual name, not the word
"vendor":
<Name> buy
e.g. Deval buy — NOT vendor buy. This is documented shard behavior (UO Renaissance's
NPC Commands page: (name) buy — "Will show you any goods the vendor has for sale").
Walk within about 1 tile first (uo-navigation), then:
echo "say Deval buy" >> /tmp/cuocmd
sleep 2
A successful open shows the vendor's greeting in the log (e.g. Deval: Greetings. Have a look around.) and, shortly after, an [INFO] [SHOP] Vendor ... buy list ready: N items line.
Why plain "say" text alone isn't enough
This was the hard-won part of getting opendoor-style speech commands working here.
UO has a legacy keyword speech system (speech.mul): the real client checks
outgoing speech against a table of command phrases (bank, guards, *buy*, etc.),
and if it matches, sends a completely different encoded packet — bit-packed keyword
IDs plus the text — instead of plain UTF-16 text, with the message type OR'd with
0xC0. Many servers' NPC command scripts (this one included) only recognize the
encoded form; plain text with the identical words is visibly broadcast and echoed
back to you, but silently ignored by the command parser. Send_Speech in
OutgoingPackets.cs now replicates this:
it loads speech.mul (via MapManager.Load, i.e. automatically on loadmap), matches
the outgoing text against it (see
Map/SpeechKeywords.cs), and encodes
accordingly. If a future speech-triggered command mysteriously does nothing even though
the words are right, this encoding is the first thing to double check — not the
phrasing.
Also worth knowing: while chasing this bug, a second unrelated liveness-check mechanic
was found and fixed — the server periodically sends a blank "SYSTEM" speech packet as a
ping, and a real client answers with a fixed magic-byte ACK (Send_ACKTalk, see
IncomingPackets.cs Handle_AsciiSpeech). This wasn't confirmed as the actual cause of
the vendor issue (the keyword encoding was), but it's implemented now regardless since
some servers silently penalize a connection that never answers it.
3. List what's for sale
echo "shop" >> /tmp/cuocmd
sleep 1
tail -15 /tmp/cuolog
Prints every item currently in the vendor's buy list with its serial, name, graphic,
price, and stock amount, e.g.:
Vendor [000012F3] buy list (8 items):
[400E3689] arrow fletching Graphic:0x1022 Price:2 Amt:20
[400E3693] arrow Graphic:0x0F3F Price:8 Amt:80
...
If shop says no buy list has been received yet, the speech command either didn't
reach the vendor (check adjacency) or hasn't been processed by the server yet (wait a
moment and retry shop).
4. Buy
echo "buy <item_serial> <amount>" >> /tmp/cuocmd
sleep 2
tail -8 /tmp/cuolog
e.g. buy 400E3693 20 to buy 20 arrows. A successful purchase gets a confirmation bark
from the vendor stating the total cost. buy always targets whichever vendor's buy
list was most recently opened — no need to pass the vendor's serial.
Confirm the purchase:
printf 'status\ninv\n' >> /tmp/cuocmd
sleep 2
tail -12 /tmp/cuolog
status shows updated gold; inv shows the new item in the backpack. Note this
relies on the backpack already having been opened at least once this session (see
uo-equip-items step 1) — if inv still shows an empty/stale backpack despite the
gold having gone down, use <backpack_serial> first, then re-check inv before
concluding the purchase didn't land.
Gotchas
- Vendors wander. These aren't fixed behind a counter — they walk around a small
area. Re-check
mobiles for current position immediately before walking to them;
a position from more than a few seconds ago may already be stale.
- The buy-list serial isn't the vendor's serial.
shop output serials are the
individual stock items; buy takes one of those, not the vendor's own mobile serial.
- Selling isn't implemented yet. The shard documents a matching
<Name> sell
speech command and a corresponding incoming packet (0x9E SellList), but only the
buy side (0x74 BuyList, 0x3B BuyReply) has been wired up in the headless client so
far.