Skip to content

Barter Shops

Overview

Barter shops define the inventory of NPC merchants: what they sell, what they accept as payment, how much stock is available, and when it refreshes. Each shop file contains a list of TradeSlots that are either Fixed (always the same trade) or Pool (randomly selected from a weighted list of possible trades at each refresh). The shop stock resets on a configurable daily schedule.

How NPC Trading Works

flowchart TD;
A["Player Interacts<br>with Merchant NPC"] --> B[Load Barter Shop];
B --> C[Display Trade Slots];
C --> D["Fixed Slots<br>Always same trades"];
C --> E["Pool Slots<br>Randomly selected"];
E --> F["Shop Refresh<br>Time Reached?"];
F -->|"Yes"| G["Roll New Trades<br>from Weighted Pool"];
F -->|"No"| H[Keep Current Trades];
D --> I[Player Selects Trade];
G --> I;
H --> I;
I --> J["Has Payment<br>Items?"];
J -->|"No"| K[Trade Unavailable];
J -->|"Yes"| L{Stock > 0?};
L -->|"No"| M[Out of Stock];
L -->|"Yes"| N[Execute Trade];
N --> O["Remove Payment<br>from Player"];
O --> P["Give Output<br>to Player"];
P --> Q[Decrease Stock];
style A fill:darkgreen,color:white;
style K fill:darkred,color:white;
style M fill:darkgoldenrod,color:white;
style P fill:rebeccapurple,color:white;

File Location

Assets/Server/BarterShops/
Klops_Merchant.json
Kweebec_Merchant.json

Schema

Top-level

FieldTypeRequiredDefaultDescription
DisplayNameKeystringYesLocalisation key for the shop’s display name shown in the UI.
RefreshIntervalRefreshIntervalYesHow often the shop’s stock resets.
RestockHournumberYesIn-game hour (0–23) at which the stock refreshes each cycle.
TradeSlotsTradeSlot[]YesOrdered list of trade slots displayed in the shop UI.

RefreshInterval

FieldTypeRequiredDefaultDescription
DaysnumberNoNumber of in-game days between restocks.

TradeSlot

FieldTypeRequiredDefaultDescription
Type"Fixed" | "Pool"YesFixed always shows the same trade. Pool randomly picks trades from a weighted list.
TradeTradeNoThe single trade for Fixed slots.
SlotCountnumberNoPool only. Number of trades randomly selected from Trades to display.
TradesPoolTrade[]NoPool only. Weighted list of possible trades to sample from.

Trade (Fixed)

FieldTypeRequiredDefaultDescription
OutputTradeItemYesThe item the player receives.
InputTradeItem[]YesItems the player must provide as payment (one or more).
StocknumberYesNumber of times this trade can be completed before the slot runs out of stock.

PoolTrade

FieldTypeRequiredDefaultDescription
WeightnumberYesRelative probability this trade is selected when the pool is sampled.
OutputTradeItemYesThe item the player receives.
InputTradeItem[]YesItems the player must provide as payment.
Stocknumber | [number, number]YesFixed stock count, or [min, max] range for randomised stock on each refresh.

TradeItem

FieldTypeRequiredDefaultDescription
ItemIdstringYesID of the item.
QuantitynumberYesStack size of the item.

Examples

Simple fixed shop (Assets/Server/BarterShops/Klops_Merchant.json):

{
"DisplayNameKey": "server.barter.klops_merchant.title",
"RefreshInterval": {
"Days": 1
},
"RestockHour": 6,
"TradeSlots": [
{
"Type": "Fixed",
"Trade": {
"Output": { "ItemId": "Furniture_Construction_Sign", "Quantity": 1 },
"Input": [{ "ItemId": "Furniture_Construction_Sign", "Quantity": 1 }],
"Stock": 1
}
}
]
}

Mixed fixed and pool shop (Assets/Server/BarterShops/Kweebec_Merchant.json, condensed):

{
"DisplayNameKey": "server.barter.kweebec_merchant.title",
"RefreshInterval": {
"Days": 3
},
"RestockHour": 6,
"TradeSlots": [
{
"Type": "Fixed",
"Trade": {
"Output": { "ItemId": "Ingredient_Spices", "Quantity": 3 },
"Input": [{ "ItemId": "Ingredient_Life_Essence", "Quantity": 20 }],
"Stock": 10
}
},
{
"Type": "Pool",
"SlotCount": 3,
"Trades": [
{
"Weight": 50,
"Output": { "ItemId": "Plant_Crop_Berry_Block", "Quantity": 1 },
"Input": [{ "ItemId": "Ingredient_Life_Essence", "Quantity": 30 }],
"Stock": [10, 20]
},
{
"Weight": 30,
"Output": { "ItemId": "Plant_Crop_Berry_Winter_Block", "Quantity": 1 },
"Input": [{ "ItemId": "Ingredient_Life_Essence", "Quantity": 50 }],
"Stock": [10, 20]
},
{
"Weight": 20,
"Output": { "ItemId": "Food_Salad_Berry", "Quantity": 1 },
"Input": [{ "ItemId": "Ingredient_Life_Essence", "Quantity": 15 }],
"Stock": [4, 8]
}
]
}
]
}

In the pool slot above, 3 trades are randomly chosen from the weighted list each time the shop refreshes every 3 days at hour 6. Stock is randomised between the min and max values.