One of the design pillars of Starbud Station's economy is that the world should feel like it's doing something whether or not the players are actively interacting with it. Traders are a core part of that. Vendors show up at your station dock, carry goods you might want to buy, and are interested in buying things you might want to sell. They're a pressure valve on the economy and a supply line for production materials — but only if your station is worth visiting.

The trader system is built around three interconnected pieces: a scheduling system that determines when and which traders visit, an inventory generation system that determines what each trader carries, and a pricing system that values goods dynamically at the time of the visit. All of it runs server-side, all of it is replicated to clients, and none of it involves pre-scripted encounters.

Trader Archetypes

Every trader belongs to an archetype defined in a UTraderArchetypeData DataAsset. The archetype defines what category of goods the trader specialises in, their base stock size, their price sensitivity range, their reputation threshold for visiting, and their rarity weight in the visit scheduler. A bulk supplier carries large quantities of common inputs at mid-tier prices. An exotic vendor carries rare materials in small quantities at premium prices. A recycler buys surplus inventory at below-market rates but has no minimum reputation requirement.

The archetypes are data-driven intentionally. Adding a new trader type is a DataAsset creation, not a code change. The C++ system is agnostic to what the archetypes contain — it just reads them and generates the correct trader instance.

Visit Scheduling

The UTraderSchedulerSubsystem runs on the server as a UGameInstanceSubsystem. Each in-game day, it runs a visit evaluation pass that determines which traders arrive at the player's station. The pass samples from the archetype pool using weighted random selection, filtered by the player's current reputation tier. High-reputation stations unlock access to rarer trader archetypes — exotic vendors simply won't appear below a Quality reputation threshold.

TArray<UTraderArchetypeData*> UTraderSchedulerSubsystem::GetEligibleArchetypes(
    const FReputationState& Rep) const
{
    TArray<UTraderArchetypeData*> Eligible;

    for (UTraderArchetypeData* Archetype : AllArchetypes)
    {
        if (Rep.MeetsThreshold(Archetype->MinReputationTier))
            Eligible.Add(Archetype);
    }

    return Eligible;
}

From the eligible pool, the scheduler picks one to three traders per day using weighted sampling. The weights are influenced by how long it's been since a given archetype last visited — preventing any single type from dominating the visit schedule. The result is a visit list for the coming day that gets replicated to clients so they can display an incoming trader notification before the trader actually docks.

Inventory Generation

When a trader is instantiated for a visit, its inventory is generated fresh using the archetype's stock definition and current market conditions. The market state — maintained by the UMarketSubsystem — tracks supply and demand pressure on item categories. If players have been buying large quantities of a particular material, that category's demand pressure increases, which raises the probability that incoming traders carry it.

void UTraderInstance::GenerateInventory(
    const UTraderArchetypeData* Archetype,
    const FMarketSnapshot& Market)
{
    int32 SlotCount = FMath::RandRange(
        Archetype->MinStockSlots,
        Archetype->MaxStockSlots);

    for (int32 i = 0; i < SlotCount; i++)
    {
        FName ItemId = SampleWeightedItem(Archetype->StockTable, Market);
        int32 Qty    = FMath::RandRange(
            Archetype->MinQuantityPerSlot,
            Archetype->MaxQuantityPerSlot);

        TraderInventory.Add({ ItemId, Qty, CalculateAskPrice(ItemId, Market) });
    }
}

The SampleWeightedItem function picks from the archetype's stock table, with item weights boosted by the market's current demand pressure for that item's category. This creates a loose feedback loop — high demand attracts supply — without ever hard-scripting "if demand high, send this trader."

Dynamic Pricing

Traders price their goods at visit time, not at definition time. The base price for any item comes from the UItemDefinition DataAsset. The trader then applies a modifier based on their archetype's price sensitivity range, the market's current supply level for that item, and a small random variance factor to prevent all visits of the same archetype from being identical.

Players can negotiate. The negotiation mechanic checks the player's relationship score with the trader archetype (accumulated across previous trades) and their current shop reputation tier. Higher relationship scores unlock better negotiation outcomes. This is kept deliberately simple — a single multiplier clamped between the archetype's minimum and maximum price bounds — because the complexity here should be in building the relationship over time, not in the negotiation mechanic itself.

Traders remember. Each archetype tracks a relationship value per player that persists across sessions through the save system. A player who consistently trades fairly with exotic vendors builds a reputation with that archetype — eventually unlocking vendor-specific stock that isn't available to stations they've never traded with.

Replication and Session Joining

Active trader visits are replicated as part of the game state. Each UTraderInstance is a UObject held in a replicated array on the Game State. When a new client joins mid-session, they receive the full trader state including inventory and pricing. A player joining mid-trade sees exactly what the other players see, with no resync required.

Trader departure is also server-authoritative. When a trader's visit window expires — measured in in-game hours — the server removes them from the active list and the dock becomes available for the next visitor. Items not purchased during the window are gone. This creates a genuine pressure to engage with traders when they're present rather than treating them as a permanent shop.

What's Next

The core system is implemented. Remaining work covers the trader relationship persistence (currently stubbed in the save system), the player-facing UI for the trader terminal, and the exotic vendor tier with its archetype-exclusive stock lines. The foundation handles all of it — no architectural changes needed, just data and UI work from here.