Trading, ground loot and wayfarers
Gold enters a world only through merchants’ purchases, story effects and loot. There is no auction, market board or player-set price. Merchants and player exchanges are server-authoritative: the client shows offers and sends intentions.
Merchants
An NPC tagged shop:<id> keeps a shop whose goods and prices live in
ishtaria-server/etc/shops.json. Every generated town has a trader at the
plaza (etc/trader.yaml, tag shop:general); coastal towns also get a
shipwright. A datadisk may tag any standing character the same way.
GET /shops/{npc_id}lists what the shop sells and buys (character within talking reach). Selling pays less than buying costs.POST /shops/{npc_id}/buy|sellexchanges items for gold in one transaction. One character may sell at mostdaily_sell_limit_gold(400) a day to a shop (shop_sales), which bounds the gold the world creates.Better tiers of the same kind of gear (migration
0050_item_quality.sql) cost more in the shop list and carry highermax_durability(and, for weapons, more swing strength). A blacksmith shop withrepairs: truerestores worn weapons, armour and shields for gold.
Player exchanges
Between players there is one open exchange at a time (migration
0037_trade_and_shops.sql, src/trade.rs):
POST /trades {"with": name}(both within 10 m),GET|DELETE /trades/current,PUT /trades/current/offer,POST /trades/current/accept.Changing an offer withdraws both acceptances. When both have accepted, the server re-checks ownership, distance and capacity and swaps everything or nothing.
Used pieces (with remaining durability) cannot be traded, so trading cannot make a worn tool new. Exchanges expire after five minutes; the partner is told by a
tradeevent.
Dropping and ground piles
Unequipped inventory can be thrown at the character’s feet (migration
0051_ground_loot.sql, src/loot.rs):
POST /players/me/dropwith{"item_id", "quantity"}. An item in hand or worn as armour cannot be dropped until it is unequipped (DELETE /players/me/equip). Worn gear leaves the pack one piece at a time so its remaining durability stays on the ground.POST /ground/{id}/pickuptakes a pile within a few metres into the pack.GET /world/objects(and the region reply) lists nearbyground_items; the client draws them as sprites.
Piles stay until a player picks them up or an NPC comes within view and pockets
them into npc_inventory. Standing NPCs scoop within about four metres;
wayfarers see about fourteen.
Buying from a finder
While an NPC still holds a find, dialogue answers carry can_trade: true and
the client offers a Trade button. GET /npcs/{id}/found and
POST /npcs/{id}/found/buy sell those stacks at the finder’s price (the
general shop’s sell price when the item is on a list, otherwise a small floor).
That is cheaper than waiting for the same item to reach a merchant.
Wayfarers
Independent of any datadisk, the settlement generator places wayfarers on
the footpaths between towns that have a market (MovementKind::Travel,
etc/traveler.yaml, migration 0052_travelers.sql). Travel is a long
patrol along trail waypoints (preferring the stones the generator already laid);
it is not offered to datadisk authors (see 0015 – Characters of a datadisk may walk).
When a wayfarer pockets a pile it records an errand toward the nearest general
merchant and its route is diverted along those road waypoints. On arrival it
hands everything into merchant_stock and clears the errand. The finder’s
pack is empty, so Trade with them no longer offers the item.
While that limited stock sits on the merchant’s shelf, GET /shops/{npc_id}
lists it with a quantity at twice the finder’s asking price, replacing the
infinite catalogue price for that item until the stock is sold. Buying depletes
merchant_stock first; afterwards the ordinary catalogue price returns.
Gameplay consequence: on the road between towns it pays to talk to every wayfarer — a find bought from them is cheaper than the same piece after it reaches the market.
Client
E at a merchant opens the shop panel. Dialogue with a character that holds
finds offers Trade and reuses the same panel (trading.gd /
trade_panel.gd, kind found). Inventory marks held and worn pieces,
allows unequip, and offers Drop only for unequipped stacks. Ground piles are
drawn from the region reply; G is the usual pick-up binding where the
client wires it to /ground/{id}/pickup.