NPC Sales
Server-side NPC sales simulation for passive vending revenue in Oxide Vending.
Overview
The NPC sales system evaluates active vending machines on a timer and simulates purchases without spawning real sale entities. Revenue is based on machine type, time of day, location, pricing, stock variety, and nearby competition.
Simulation Flow
Every tick:
- Skip disabled simulation.
- Read the current hour.
- Iterate active machines.
- Skip machines with no stock.
- Calculate sale chance.
- Roll a random purchase.
- Process the sale if the roll succeeds.
Default tick settings:
TickInterval = 60BaseChancePerTick = 0.05
Sale Chance Formula
saleChance = baseChance * time * location * type * price * stock * competition
| Factor | Source |
|---|---|
baseChance | Config.NPCSales.BaseChancePerTick |
time | GetNPCTimeMultiplier(hour) |
location | Configured hotzone or default location multiplier |
type | Config.NPCSales.MachineTypeModifiers[machineType] |
price | Machine average markup competitiveness |
stock | Stocked slots divided by total machine slots |
competition | Nearby same-type competition score |
Time Source
The current hour is read from:
exports["oxide-weather"]:GetTime()
when available. If oxide-weather is not running or does not return an hour, the system falls back to real server time via os.date("%H").
Time Multipliers
Configured GTA-hour multipliers range from quiet overnight periods to lunch and commute spikes.
Examples from the shipped config:
00:00->0.208:00->1.312:00->1.517:00->1.423:00->0.3
Hotzones
Hotzones live in Config.NPCSales.Hotzones and are simple radius-based location multipliers.
Current high-traffic examples include:
- Legion Square
- Del Perro Pier
- Airport terminals
- Vespucci Beach
Current low-traffic examples include:
- Sandy Shores
- Paleto Bay
- Grapeseed
- Harmony
- Chumash
Machines outside all configured hotzones use:
Config.NPCSales.DefaultLocationMultiplier
Adding a Hotzone
{
name = "custom_zone",
coords = vector3(x, y, z),
radius = 150.0,
multiplier = 1.5,
}
Machine Type Weighting
Default machine multipliers:
| Type | Multiplier |
|---|---|
drinks | 1.5 |
snacks | 1.3 |
general | 0.8 |
electronics | 0.4 |
Price Competitiveness
Price weighting is based on average machine markup:
multiplier = 1.5 - ((avgMarkup - 1.0) * 0.5)
This value is clamped between 0.5 and 1.5.
Practical examples:
- Base-price or cheaper machines get a strong bonus
- Default
1.5xmarkup is still favorable - Very expensive machines are penalized
Stock Variety
Variety uses:
stockedSlots / totalSlots
This means a machine with more filled slots is more attractive to the NPC simulation than a machine with only one or two stocked products.
Competition
Competition compares nearby machines of the same type.
Default competition config:
Config.Competition = {
Enabled = true,
Radius = 500.0,
SameOwnerCompetes = false,
MinMultiplier = 0.3,
MaxMultiplier = 1.8,
CheapestBonus = 1.5,
SuggestedPriceMargin = 0.95,
SuggestedPriceMinMarkup = 0.85,
AnalysisCacheTime = 300,
ShowCompetitorNames = true,
ShowExactPrices = true,
}
Key behavior:
- Only same-type machines compete
- Same-business machines do not compete by default
- The cheapest machine can receive an extra
CheapestBonus - Dashboard suggestions use competitor pricing data and the configured margin floor
- Dashboard competition analysis is cached for
300seconds by default
Item Selection and Quantity
When a sale occurs, the system selects an item by weighted random.
Default weighting favors:
- Cheaper items
- Items with more quantity on hand
Current purchase behavior:
MinQuantity = 1MaxQuantity = 2AvoidLastItem = truePreferCheaperItems = true
If AvoidLastItem is enabled, the system avoids consuming the final item in a stack.
Revenue and Progression
Processed NPC sales:
- Reduce stock
- Add machine revenue
- Log an
npc_saletransaction withplayer_citizenid = "NPC" - Degrade machine durability using NPC wear settings
- Grant progression XP and lifetime revenue
- Check sales milestones
The progression XP formula is:
xp = Config.Progression.XPSources.npcSale + (saleAmount * Config.Progression.XPSources.salePerDollar)
Level rewards can also add an NPC revenue boost on top of the base sale value.
Visual Feedback
The actual sale logic is entity-free, but the resource can optionally animate nearby ambient peds when players are in range.
Current visual feedback settings:
VisualFeedback = {
Enabled = true,
PlayerRange = 50.0,
PedSearchRadius = 30.0,
MachineCooldown = 30000,
}
This is cosmetic only. Disabling it does not disable NPC revenue generation.