ScriptsM Vending
SM Vending is a full vending machine business system for FiveM RP servers.
Players can interact with vending machines directly, press real machine buttons, watch products physically dispense, pick them up in-world, and even deal with stuck items by pushing the machine.
Server owners can also enable player-owned vending machines, allowing players to buy machines, place them in the world, manage stock, set prices, track revenue, repair machines, and build their own small vending business.
Installation
Download the resource and place sm-vending inside your server resources folder
Import the SQL file
Import:
sm-vending/sql/sm-vending.sqlCheck dependencies
Required:
oxmysqlSupported frameworks:
es_extended
qb-core
qbx_coreSupported inventories:
ox_inventory
qb-inventorySupported target systems:
ox_target
qb-targetResource start order
Make sure your resources start in a logical order:
start oxmysql
start [your framework]
start [your inventory]
start [your target]
start sm-vendingAdd the resource to server.cfg
ensure sm-vendingConfig
All main settings are inside:
sm-vending/config.luaThe config already includes comments for every parameter.
Main areas:
Config.Framework
Config.Inventory
Config.Target
Config.ShopPed
Config.EnableOwnership
Config.EnablePortableMachines
Config.MachineProfiles
Config.MachineModels
Config.Catalog
Config.Buttons
Config.DispenseOffset
Config.TrayOffset
Config.ItemSpawnAdjust
Config.ItemProp
Config.CameraProfiles
Config.OwnerNotifications
Config.OwnerSettings
Config.OwnerBlips
Config.VendingBlipsSupported Machine Behaviors
glass
Classic glass vending machine behavior.
- Product spawns inside the machine
- Product slides forward
- Product falls down
- Product can jam
tray
Used for tray-output machines.
- Product goes to one tray output
- Product can jam
- Tray fall timing is configurable
basic
Used for water / coffee style machines.
- Cup or container appears
- Product slides to fill position
- Pour effect plays
- Pickup happens after pour sequence
Framework / Inventory / Target
Auto detection is supported:
Config.Framework = 'auto'
Config.Inventory = 'auto'
Config.Target = 'auto'You can also force them manually:
Config.Framework = 'esx' -- esx | qb | qbox
Config.Inventory = 'ox' -- framework | ox | qb
Config.Target = 'ox' -- none | ox | qbPortable Machines
Portable machines are tied to these default item names:
vending_snack
vending_soda
vending_coffee
vending_waterPortable flow:
- Buy the vending machine from the shop NPC
- Receive the machine item in inventory
- Use the item or run
/placevending <itemname> - Preview hologram appears
- Rotate and place the machine
- Machine is saved to SQL
To disable portable vending completely:
Config.EnablePortableMachines = falseThat disables:
- portable machine buying
- shop NPC usage
- machine placement from items
- respawn of portable placed machines
SQL Persistence
Owned machine state is saved in SQL.
Main tables:
Config.SqlTableName = 'vending_owned_machines'
Config.SqlSetupTableName = 'vending_machine_setup'This stores:
- ownership
- stock
- prices
- durability
- placement position
- revenue
- daily analytics
- journal
- notify preferences
- setup offsets
Adding Item Images
Item images are loaded automatically by item name.
Folder:
sm-vending/html/item-imagesRule:
- Put a
.pngfile with the exact same name as the item
Examples:
sm-vending/html/item-images/ecola.png
sm-vending/html/item-images/sprunk.png
sm-vending/html/item-images/chips.png
sm-vending/html/item-images/proteinbar.pngIf the file does not exist:
- no fallback SVG is shown
- the item image area simply stays empty
Adding Machine Images
Machine purchase previews are also loaded automatically from PNG files.
Folder:
sm-vending/html/machine-imagesThe machine profile controls which image is used:
Config.MachineProfiles = {
snack = {
image = 'snack',
},
}That means the script will load:
sm-vending/html/machine-images/snack.pngExamples:
sm-vending/html/machine-images/snack.png
sm-vending/html/machine-images/soda.png
sm-vending/html/machine-images/water.png
sm-vending/html/machine-images/coffee.pngAdding Sounds
MP3 sounds are loaded from:
sm-vending/html/soundsCurrent sound files:
slide.mp3
pour.mp3
jam.mp3
drop.mp3Volumes are controlled in config:
Config.EnableUiAudio = true
Config.AudioVolumeMaster = 0.85
Config.AudioVolume = {
slide = 1.0,
pour = 0.85,
jam = 0.85,
drop = 0.40,
}If you replace a sound:
- keep the same file name
- keep
.mp3format - restart the resource after replacing files
Adding a New Product
To add a product, you usually need 4 things:
- Add it to your framework or inventory items
- Add it to
Config.Catalog - Add button and dispense offsets if needed
- Add prop and optional PNG image
Example:
Config.Catalog.snack[#Config.Catalog.snack + 1] = {
item = 'mychips',
label = 'My Chips',
price = 9,
}
Config.ItemProp.mychips = `prop_ld_snack_01`Optional image:
sm-vending/html/item-images/mychips.pngIf the machine is glass, tray, or basic, also configure the relevant offsets:
Config.Buttons
Config.DispenseOffset
Config.ItemSpawnAdjustAdding a New Machine Type
To add a new machine type:
- Add a machine profile
- Add machine world model mapping
- Add catalog entries
- Add button offsets
- Add dispense / tray offsets
- Add camera profile if needed
- Add machine preview image
Basic example:
Config.MachineProfiles.juice = {
behavior = 'tray',
label = 'Juice',
item = 'vending_juice',
buyPrice = 2200,
maxStock = 20,
image = 'juice',
jamEnabled = true,
}Then map a world model:
Config.MachineModels[#Config.MachineModels + 1] = {
model = `prop_your_machine_model`,
type = 'juice',
label = 'Juice',
}Then create:
sm-vending/html/machine-images/juice.pngPlacement Setup
Portable machines support:
- hologram preview
- rotation
- ground snap
- blocked zones
- anti-road placement
- anti-traffic placement
- optional placement rent
Useful settings:
Config.PlaceFromItemCommand
Config.PlacementBlockedZones
Config.PlacementSafety
Config.PlacementRentExample blocked zone:
Config.PlacementBlockedZones = {
{ coords = vector3(-42.15, -1752.64, 29.42), radius = 8.0, label = '24/7 Entrance' },
}Setup Offsets
Button offsets and dispense offsets can be adjusted in-game and are saved to SQL.
Relevant config sections:
Config.Buttons
Config.DispenseOffset
Config.TrayOffset
Config.ItemSpawnAdjustIf you use setup mode / placement helpers, saved SQL offsets override raw config defaults.
Blips
Owner-only blips:
Config.OwnerBlips = {
enabled = true,
sprite = 52,
color = 2,
scale = 0.75,
shortRange = true,
labelPrefix = 'Owned Vending',
}Public vending blips:
Config.VendingBlips = {
enabled = true,
static = true,
PlayerOwnedOnly = false,
sprite = 52,
color = 1,
scale = 0.50,
shortRange = true,
playerOwnedShortRange = false,
labelPrefix = 'Vending',
}playerOwnedShortRange = false means player-owned public blips are visible across the whole map.
Owner Features
Owner machines support:
- revenue balance
- total revenue
- units sold
- restock
- repair
- clean
- sell machine
- pickup machine
- transfer ownership
- journal
- weekly analytics
- notification settings
Main owner config:
Config.OwnerNotifications
Config.OwnerSettings
Config.OwnerJournal
Config.OwnerLimits
Config.DiscordLogsOptional Notifications
Owner notifications can be configured globally and per machine.
Default kinds:
purchase
jam
low_stock
sold_out
low_health
ambient_sale
balance_readyPer-machine settings let the owner:
- toggle supported notification kinds
- set stock threshold
- set health threshold
- set balance threshold
- set custom machine name
Troubleshooting
Machines, NPC, or blips do not appear on first join
Check:
oxmysql started
framework started
sm-vending started after dependenciesAlso make sure:
Config.EnablePortableMachines = trueif you expect the NPC shop- the resource name is exactly
sm-vending
Sounds do not play
Check:
Config.EnableUiAudio = true- MP3 files exist in
sm-vending/html/sounds - file names are exactly:
slide.mp3
pour.mp3
jam.mp3
drop.mp3Portable machine does not place
Check:
- item exists in inventory
- item name matches machine profile
item - placement spot is not blocked by road / zone / collision checks
Machine image or item image does not show
Check:
- file is
.png - file name matches the configured item or machine image name exactly
- file is inside the correct folder
Config Example
There you can find the full resource config with descriptions for all params:
Config = {}
-- Framework detection mode.
-- auto = detect automatically, esx/qb/qbox = force a specific framework.
Config.Framework = 'auto' -- auto | esx | qb | qbox
-- Inventory bridge mode.
-- auto = detect automatically, framework = use framework inventory helpers, ox/qb = force integration.
Config.Inventory = 'auto' -- auto | framework | ox | qb
-- Interaction mode.
-- auto = detect target resource, none = built-in interaction only, ox/qb = force target integration.
Config.Target = 'auto' -- auto | none | ox | qb
-- Interaction
-- Max distance from player to machine for initial interaction.
Config.InteractDistance = 1.25
-- Extra search padding when scanning nearest machine to make interaction less strict.
Config.InteractSearchPadding = 0.4
-- Max distance to pick up a dispensed item after it lands.
Config.PostDispenseInteractDistance = 2.5
-- Control used for normal interaction.
Config.InteractKey = 38 -- E
-- Control used for manage / purchase owner menu.
Config.ManageKey = 47 -- G
-- Optional pickup key used in some flows.
Config.PickupKey = 74 -- H
-- Portable vending shop NPC config.
Config.ShopPed = {
enabled = true, -- Enables the vending shop NPC completely.
model = `s_m_m_dockwork_01`, -- Ped model used for the shop NPC.
coords = vector4(29.05, -1770.22, 29.61, 52.85), -- Spawn position and heading of the NPC.
scenario = 'WORLD_HUMAN_CLIPBOARD', -- Scenario played by the NPC while idle.
blip = {
enabled = true, -- Creates a map blip for the NPC shop.
sprite = 605, -- GTA blip sprite ID.
color = 47, -- GTA blip color ID.
scale = 0.75, -- Blip scale on the map.
shortRange = true, -- true = visible only nearby, false = visible at long range.
label = 'Vending Shop', -- Blip name shown on the map.
}
}
-- Command that starts placement from a vending machine item.
Config.PlaceFromItemCommand = 'placevending'
-- Purchase / inventory
-- Account used when players buy products from a machine.
Config.PayAccount = 'money' -- money | bank
-- Ownership / management
-- Enables owner systems such as stocking, revenue, repairs, and management UI.
Config.EnableOwnership = true
-- Enables portable / placeable vending machines.
Config.EnablePortableMachines = true
-- Enables transfer ownership action in owner UI.
Config.EnableTransferOwnership = true
-- SQL table used to store owned machine state.
Config.SqlTableName = 'vending_owned_machines'
-- SQL table used to store setup offsets configured in-game.
Config.SqlSetupTableName = 'vending_machine_setup'
-- Fallback purchase price used only if a machine profile does not define buyPrice.
Config.MachineBuyPrice = 2500
-- Percent of machine buy price returned when selling the machine.
Config.MachineSellRefundPercent = 0.65
-- Account where owner withdrawals and owner refunds are paid.
Config.OwnerWithdrawAccount = 'money' -- money | bank
-- Starting durability for a newly purchased owner machine.
Config.OwnerStartDurability = 100
-- Durability loss applied on every successful customer sale.
Config.OwnerDurabilityLossPerSale = 1
-- Extra durability loss applied when a sale jams.
Config.OwnerDurabilityLossOnJam = 3
-- If durability goes to or below this value, the machine is considered out of service.
Config.OwnerOutOfServiceThreshold = 5
-- Default stock threshold used for low-stock notifications.
Config.OwnerStockWarningThreshold = 2
-- Base jam chance for owner machines before durability penalties.
Config.OwnerBaseJamChance = 0.05
-- Additional jam chance added when durability is effectively at zero.
Config.OwnerBonusJamChanceAtZeroDurability = 0.20
-- Repair payment mode.
-- money = cash/bank style payment, item = consume a repair item instead.
Config.OwnerRepairMode = 'money' -- money | item
-- Base repair cost when using money mode.
Config.OwnerRepairCostMoney = 350
-- Item name consumed for repair when using item mode.
Config.OwnerRepairItem = 'repairkit'
-- Amount of repair items consumed per repair action.
Config.OwnerRepairItemCount = 1
-- Durability restored by one repair action.
Config.OwnerRepairRestore = 25
-- Money cost of a clean action.
Config.OwnerCleanCostMoney = 80
-- Durability restored by cleaning.
Config.OwnerCleanRestore = 10
-- Broken / damaged visual effects for low-durability machines.
Config.DamagedMachineVisual = {
enabled = true, -- Enables smoke / spark damaged visuals.
threshold = 25, -- Durability threshold at or below which visuals start.
default = {
smoke = {
dict = 'core', -- PTFX asset dictionary.
name = 'ent_dst_elec_fire_sp', -- PTFX effect name.
scale = 1.0, -- PTFX scale.
offset = vector3(0.0, 0.4, 0.6), -- Local offset from the machine.
rotation = vector3(-90.0, 0.0, 0.0), -- PTFX rotation.
intervalMs = 1200, -- Delay between non-looped smoke bursts.
},
sparks = {
dict = 'core', -- PTFX asset dictionary.
name = 'ent_dst_elec_crackle', -- PTFX effect name.
scale = 0.7, -- PTFX scale.
offset = vector3(0.35, -0.4, 0.4), -- Local offset from the machine.
rotation = vector3(90.0, 0.0, 0.0), -- PTFX rotation.
intervalMs = 1500, -- Delay between non-looped spark bursts.
},
},
perType = {
soda = {
smoke = {
dict = 'core',
name = 'ent_dst_elec_fire_sp',
scale = 1.0,
offset = vector3(0.0, 0.4, 0.6),
rotation = vector3(-90.0, 0.0, 0.0),
intervalMs = 1200,
},
sparks = {
dict = 'core',
name = 'ent_dst_elec_crackle',
scale = 0.7,
offset = vector3(0.35, -0.4, 0.4),
rotation = vector3(90.0, 0.0, 0.0),
intervalMs = 1500,
},
},
snack = {
smoke = {
dict = 'core',
name = 'ent_dst_elec_fire_sp',
scale = 1.0,
offset = vector3(0.0, 0.4, 0.6),
rotation = vector3(-90.0, 0.0, 0.0),
intervalMs = 1200,
},
sparks = {
dict = 'core',
name = 'ent_dst_elec_crackle',
scale = 0.7,
offset = vector3(0.35, -0.4, 0.4),
rotation = vector3(90.0, 0.0, 0.0),
intervalMs = 1500,
},
},
water = {
smoke = {
dict = 'core',
name = 'ent_dst_elec_fire_sp',
scale = 0.2,
offset = vector3(0.0, -0.04, 1.05),
rotation = vector3(0.0, 0.0, 0.0),
intervalMs = 1500,
},
sparks = {
dict = 'core',
name = 'ent_dst_elec_crackle',
scale = 0.16,
offset = vector3(0.0, -0.04, 0.96),
rotation = vector3(0.0, 0.0, 0.0),
intervalMs = 1900,
},
},
coffee = {
smoke = {
dict = 'core',
name = 'ent_dst_elec_fire_sp',
scale = 1.0,
offset = vector3(0.0, 0.4, 0.6),
rotation = vector3(-90.0, 0.0, 0.0),
intervalMs = 1200,
},
sparks = {
dict = 'core',
name = 'ent_dst_elec_crackle',
scale = 0.7,
offset = vector3(0.35, -0.4, 0.4),
rotation = vector3(90.0, 0.0, 0.0),
intervalMs = 1500,
},
},
}
}
-- Default stock amount added for each product when buying a machine.
Config.OwnerStarterStock = 5
-- Max stock allowed per item line unless a profile overrides it.
Config.OwnerMaxStockPerItem = 25
-- Global owner notification defaults.
Config.OwnerNotifications = {
enabled = true, -- Master toggle for owner notifications.
kinds = {
purchase = true, -- Notify owner when a player buys an item.
jam = true, -- Notify owner when a jam occurs.
low_stock = true, -- Notify owner when stock gets low.
sold_out = true, -- Notify owner when an item sells out.
low_health = true, -- Notify owner when durability gets low.
ambient_sale = false, -- Notify owner about ambient NPC purchases.
balance_ready = true, -- Notify owner when balance passes threshold.
},
stockThreshold = 2, -- Default low stock threshold.
lowHealthThreshold = 25, -- Default low health threshold.
balanceThreshold = 500, -- Default balance-ready threshold.
}
-- What the owner is allowed to customize in settings UI.
Config.OwnerSettings = {
enabled = true, -- Enables the owner settings tab.
allowCustomName = true, -- Allows per-machine custom names.
customNameMaxLength = 32, -- Max length for custom machine names.
notifications = {
enabled = true, -- Enables notification settings section.
allowKinds = true, -- Allows toggling individual notification kinds.
allowThresholds = {
stock = true, -- Allows changing stock threshold.
lowHealth = true, -- Allows changing health threshold.
balance = true, -- Allows changing balance threshold.
}
}
}
-- Blips visible only to the owner for their machines.
Config.OwnerBlips = {
enabled = true, -- Enables owner-specific blips.
sprite = 52, -- GTA blip sprite ID.
color = 2, -- GTA blip color ID.
scale = 0.75, -- Blip scale.
shortRange = true, -- true = only nearby, false = long range.
labelPrefix = 'Owned Vending', -- Prefix used in blip name.
}
-- Public vending blip settings.
Config.VendingBlips = {
enabled = true, -- Enables public vending blips.
static = true, -- Show blips for unowned / static world machines.
PlayerOwnedOnly = false, -- If true, hide static public blips and show only player-owned public blips.
sprite = 52, -- GTA blip sprite ID.
color = 1, -- GTA blip color ID.
scale = 0.50, -- Blip scale.
shortRange = true, -- Range setting for static public vending blips.
playerOwnedShortRange = false, -- false = player-owned public blips are visible across the whole map.
labelPrefix = 'Vending', -- Prefix used in public blip name.
}
-- Ambient NPC sales simulation.
Config.AmbientCustomerUsage = {
enabled = true, -- Enables simulated customer purchases.
tickMs = 180000, -- Time between ambient sale ticks.
chancePerTick = 0.28, -- Chance for a machine to get an ambient sale per tick.
requirePlayersOnline = true, -- Only run ambient sales if at least one player is online.
maxPriceMultiplier = 1.8, -- Ambient customers refuse prices above default price * this multiplier.
maxAbsolutePrice = 100, -- Ambient customers refuse prices above this hard cap.
}
-- Hard ownership limits.
Config.OwnerLimits = {
enabled = true, -- Enables per-player / per-type limits.
maxPerPlayer = 8, -- Total amount of machines one player may own.
maxPerType = {
default = 3, -- Default limit for types not listed below.
soda = 2, -- Max owned soda machines per player.
snack = 2, -- Max owned snack machines per player.
water = 2, -- Max owned water machines per player.
coffee = 2, -- Max owned coffee machines per player.
}
}
-- Optional rent charged when placing a portable machine into the world.
Config.PlacementRent = {
enabled = false, -- Enables placement rent.
amount = 75, -- Rent amount charged on placement.
account = 'money', -- Which account pays rent. money | bank
}
-- Owner journal settings.
Config.OwnerJournal = {
enabled = true, -- Enables owner action journal.
maxEntries = 60, -- Max journal entries stored per machine.
visibleActions = {
buy = true, -- Log machine purchases.
placement = true, -- Log machine placements.
pickup = true, -- Log pickups back into inventory.
sell = true, -- Log selling a machine.
withdraw = true, -- Log withdrawing funds.
repair = true, -- Log repairs.
clean = true, -- Log cleaning.
restock = true, -- Log restocks.
set_price = true, -- Log price changes.
purchase_sale = true, -- Log customer purchases.
transfer_out = true, -- Log outgoing ownership transfers.
transfer_in = true, -- Log incoming ownership transfers.
}
}
-- Discord webhook logging settings.
Config.DiscordLogs = {
enabled = false, -- Enables webhook logging.
webhook = '', -- Discord webhook URL.
username = 'Vending Machine', -- Username displayed by webhook.
avatarUrl = '', -- Avatar URL displayed by webhook.
color = 16777215, -- Embed color as decimal integer.
actions = {
buy = true, -- Log machine purchases.
placement = true, -- Log placements.
pickup = true, -- Log pickups.
sell = true, -- Log machine sales.
withdraw = true, -- Log owner withdrawals.
repair = true, -- Log repairs.
clean = true, -- Log cleaning.
restock = true, -- Log restocks.
set_price = true, -- Log price changes.
purchase_sale = true, -- Log customer sales.
transfer_out = true, -- Log outgoing transfers.
transfer_in = true, -- Log incoming transfers.
}
}
-- Placement
-- Default distance in front of the player used for placement preview.
Config.PlacementPreviewDistance = 1.15
-- Rotation step applied when rotating the placement preview.
Config.PlacementRotateStep = 5.0
-- Height used when probing ground for placement.
Config.PlacementGroundProbeHeight = 1.0
-- Manual blacklist zones where machines cannot be placed.
Config.PlacementBlockedZones = {
-- Example:
-- { coords = vector3(-42.15, -1752.64, 29.42), radius = 8.0, label = '24/7 Entrance' },
}
-- Automatic placement safety checks.
Config.PlacementSafety = {
blockRoads = true, -- Prevent placement on roads.
roadSampleRadius = 0.85, -- Radius used for road probing.
roadSamplePoints = 8, -- Number of sample points used for road checks.
blockNearVehicleNodes = true, -- Prevent placement near traffic nodes.
vehicleNodeRadius = 3.2, -- Radius for vehicle node exclusion.
blockOpenTransitAreas = false, -- Prevent placement in exposed transit spaces.
transitProbeDistance = 1.0, -- Distance used for open-area probes.
transitOpenDirections = 3, -- Number of open directions required to block a spot.
}
-- World cursor / buttons
-- Click detection sphere radius on machine buttons.
Config.ButtonHitRadius = 0.02
-- Visual radius of the ring marker shown on buttons.
Config.ButtonVisualRadius = 0.011
-- Segment count used to draw the ring.
Config.ButtonRingSegments = 20
-- Draw all button markers while in machine camera.
Config.DrawAllButtons = true
-- Draw 3D item labels above buttons.
Config.DrawButtonLabels = true
-- Mouse cursor sprite index while interacting.
Config.CursorSprite = 1
-- Vertical offset of button labels above the button point.
Config.ButtonLabelOffset = 0.025
-- Scale of 3D button labels.
Config.ButtonLabelScale = 0.22
-- Enables NUI-based MP3 sounds.
Config.EnableUiAudio = true
-- Master multiplier applied to all UI sound volumes.
Config.AudioVolumeMaster = 0.85
-- Per-sound volume multipliers.
Config.AudioVolume = {
slide = 1.0, -- Slide-out sound volume multiplier.
pour = 0.85, -- Pour sound volume multiplier.
jam = 0.85, -- Jam sound volume multiplier.
drop = 0.40, -- Drop sound volume multiplier.
}
-- Draw tray debug marker.
Config.DebugTraySpawn = false
-- ACE permission required for admin delete action.
Config.AdminDeleteAce = 'vending.admin.delete'
-- Camera (relative to machine entity)
-- Default camera field of view.
Config.CameraFov = 70.0
-- Default camera position relative to machine. x = right, y = forward, z = up.
Config.CameraOffset = vector3(0.0, -1.55, 0.25)
-- Default look-at point relative to machine.
Config.CameraLookAtOffset = vector3(0.0, 0.25, 0.40)
-- Blend time when entering / leaving vending camera.
Config.CameraTransitionMs = 600
-- Where the player's ped is moved locally while in vending camera.
Config.PedCameraStagingOffset = vector3(0.00, -1.6, 0.45)
-- Per-machine-type camera overrides.
Config.CameraProfiles = {
soda = {
fov = 70.0, -- FOV override for soda machines.
offset = vector3(0.0, -1.55, 0.25), -- Camera offset for soda machines.
lookAtOffset = vector3(0.0, 0.25, 0.10), -- Look-at offset for soda machines.
pedStagingOffset = vector3(0.00, -1.6, 0.0), -- Local ped staging offset for soda machines.
},
snack = {
fov = 70.0,
offset = vector3(0.0, -1.55, 0.25),
lookAtOffset = vector3(0.0, 0.25, 0.10),
pedStagingOffset = vector3(0.00, -1.6, 0.0),
},
water = {
fov = 62.0,
offset = vector3(0.0, -1.35, 1.25),
lookAtOffset = vector3(0.0, 0.25, 0.80),
pedStagingOffset = vector3(0.00, -1.4, 1.0),
},
coffee = {
fov = 62.0,
offset = vector3(0.0, -1.35, 1.25),
lookAtOffset = vector3(0.0, 0.25, 0.80),
pedStagingOffset = vector3(0.00, -1.4, 1.0),
},
}
-- Timing
-- Seconds before an approved purchase expires and is refunded.
Config.PendingTimeoutSec = 45
-- Delay after pressing a machine button before dispense starts.
Config.DispenseStartDelayMs = 1000
-- Machine type definitions.
Config.MachineProfiles = {
soda = {
behavior = 'tray', -- tray = output tray machine behavior.
label = 'Soda', -- Display label in UI.
item = 'vending_soda', -- Inventory item that represents this portable machine.
buyPrice = 2350, -- Price to buy this machine from shop.
maxStock = 25, -- Max stock capacity for this machine.
image = 'soda', -- Image name in html/machine-images.
jamEnabled = true, -- Whether jams are allowed for this machine type.
},
snack = {
behavior = 'glass',
label = 'Snacks',
item = 'vending_snack',
buyPrice = 2500,
maxStock = 25,
image = 'snack',
jamEnabled = true,
},
water = {
behavior = 'basic',
label = 'Water',
item = 'vending_water',
buyPrice = 2100,
maxStock = 30,
image = 'water',
jamEnabled = true,
},
coffee = {
behavior = 'basic',
label = 'Coffee',
item = 'vending_coffee',
buyPrice = 1850,
maxStock = 20,
image = 'coffee',
jamEnabled = false,
},
}
-- World models treated as vending machines.
Config.MachineModels = {
{ model = `prop_vend_soda_02`, type = 'soda', label = 'Sprunk' }, -- Sprunk soda machine.
{ model = `prop_vend_soda_01`, type = 'soda', label = 'eCola' }, -- eCola soda machine.
{ model = `prop_vend_snak_01`, type = 'snack', label = 'Snacks' }, -- Snack machine.
{ model = `prop_vend_water_01`, type = 'water', label = 'Water' }, -- Water machine.
{ model = `prop_vend_coffe_01`, type = 'coffee', label = 'Coffee' }, -- Coffee machine.
}
-- What each machine sells.
-- Item names must exist in your framework / inventory.
Config.Catalog = {
soda = {
{ item = 'sprunk', label = 'Sprunk', price = 8 }, -- Default sprunk sale line.
{ item = 'ecola', label = 'eCola', price = 8 }, -- Default eCola sale line.
},
snack = {
{ item = 'ecola', label = 'ECola', price = 5 }, -- Default snack machine drink option.
{ item = 'proteinbar', label = 'Protein Bar', price = 7 }, -- Default protein bar option.
{ item = 'chips', label = 'Chips', price = 10 }, -- Default chips option.
},
coffee = {
{ item = 'coffee', label = 'Coffee', price = 12 }, -- Default coffee option.
},
water = {
{ item = 'water', label = 'Water', price = 6 }, -- Default water option.
}
}
-- Default button layout per machine type.
-- Offsets are local to the machine entity.
-- Setup values can be overridden in-game and persisted to SQL.
Config.Buttons = {
soda = {
{ item = 'sprunk', offset = vector3(0.445, -0.4, 0.143) }, -- Sprunk button position.
{ item = 'ecola', offset = vector3(0.445, -0.4, -0.011) }, -- eCola button position.
},
snack = {
{ item = 'ecola', offset = vector3(0.326, -0.5, 0.43) }, -- Snack machine eCola button.
{ item = 'proteinbar', offset = vector3(0.326, -0.5, 0.35) }, -- Protein bar button.
{ item = 'chips', offset = vector3(0.326, -0.5, 0.271) }, -- Chips button.
},
coffee = {
{ item = 'coffee', offset = vector3(0.266, -0.28, 1.132) }, -- Coffee button position.
},
water = {
{ item = 'water', offset = vector3(0.295, -0.37, 1.0) }, -- Water button position.
}
}
-- Where each product initially spawns inside the machine before slide / drop.
Config.DispenseOffset = {
snack = {
ecola = vector3(0.0, -0.15, 0.48), -- Snack machine eCola slot.
proteinbar = vector3(-0.16, -0.18, 0.04), -- Protein bar slot.
chips = vector3(-0.16, -0.13, 0.72), -- Chips slot.
},
soda = {
ecola = vector3(0.0, -1.5, 0.14), -- eCola spawn point inside soda machine.
sprunk = vector3(-0.16, 1.18, 0.04), -- Sprunk spawn point inside soda machine.
},
water = {
water = vector3(-0.07, 0.0, 0.68), -- Water cup / bottle spawn point.
},
coffee = {
coffee = vector3(-0.08, -0.14, 0.72), -- Coffee cup spawn point.
},
}
-- Single tray output point per tray/basic machine type.
Config.TrayOffset = {
soda = vector3(0.0, -0.35, -0.6), -- Tray output for soda machines.
coffee = vector3(-0.08, -0.24, 0.72), -- Cup tray/output for coffee machines.
water = vector3(-0.07, -0.24, 0.68), -- Cup tray/output for water machines.
}
-- Per-item visual spawn adjustment.
-- offset uses only X/Z for side and height adjustment.
-- rotation is the prop rotation at spawn / slide time.
Config.ItemSpawnAdjust = {
snack = {
ecola = {
offset = vector3(0.0, 0.0, 0.0), -- Extra local offset for snack eCola prop.
rotation = vector3(0.0, 0.0, 0.0), -- Spawn rotation for snack eCola prop.
},
proteinbar = {
offset = vector3(0.0, 0.0, 0.0),
rotation = vector3(0.0, 90.0, 90.0),
},
chips = {
offset = vector3(0.0, 0.0, 0.0),
rotation = vector3(80.0, 0.0, 0.0),
},
},
soda = {
sprunk = {
offset = vector3(0.0, 0.0, 0.0),
rotation = vector3(90.0, 90.0, 0.0),
},
ecola = {
offset = vector3(0.0, 0.0, 0.0),
rotation = vector3(90.0, 90.0, 0.0),
},
},
water = {
water = {
offset = vector3(0.0, 0.0, 0.0),
rotation = vector3(0.0, 0.0, 0.0),
},
},
coffee = {
coffee = {
offset = vector3(0.0, 0.0, 0.0),
rotation = vector3(0.0, 0.0, 0.0),
},
},
}
-- How far items slide out before dropping, per machine type.
Config.DispenseSlideDistance = {
snack = 0.20, -- Slide distance for glass snack machine items.
}
-- Tiny forward push so props are visible instead of clipping into the machine.
Config.DispenseVisiblePush = 0.02
-- Duration of the slide-out movement.
Config.DispenseSlideDurationMs = 2000
-- Delay between end of slide and start of fall.
Config.DispenseFallDelayMs = 10
-- Angular velocity applied when the product falls.
Config.DispenseFallAngularVelocity = vector3(2.0, 6.0, 7.5)
-- Base jam chance for non-owned / raw dispense simulation.
Config.DispenseJamChance = 1.0
-- Optional per-machine jam profiles. Empty table means use default jam settings below.
Config.DispenseJamProfiles = {}
-- How far along the slide the item gets before stopping in a jam.
Config.DispenseJamProgress = 0.35
-- Item tilt while jammed.
Config.DispenseJamTilt = vector3(-18.0, 0.0, 0.0)
-- How long the jam shake animation runs.
Config.DispenseJamShakeMs = 320
-- Position shake amplitude for jammed items.
Config.DispenseJamShakeAmplitude = 0.006
-- Delay before camera exits after a jam.
Config.DispenseJamExitDelayMs = 1200
-- Delay after push animation starts before machine shake begins.
Config.MachinePushShakeDelayMs = 200
-- Duration of the machine shake while pushing a jammed item.
Config.MachinePushShakeDurationMs = 220
-- Positional shake amplitude of the machine.
Config.MachinePushShakePosAmplitude = 0.008
-- Rotational shake amplitude of the machine.
Config.MachinePushShakeRotAmplitude = vector3(0.8, 1.6, 0.4)
-- Distance the jammed item advances per successful push step.
Config.DispensePushStep = 0.02
-- Remaining distance threshold at which the jammed item is released.
Config.DispensePushReleaseDistance = 0.09
-- Duration of each push interaction.
Config.DispensePushDurationMs = 800
-- Height above tray where tray items begin their fall.
Config.TrayDropHeight = 0.28
-- Delay between tray sound start and actual prop fall.
Config.TrayFallStartDelayMs = 400
-- Duration of tray drop interpolation.
Config.TrayDropDurationMs = 420
-- Delay before leaving camera after tray dispense.
Config.TrayExitDelayMs = 600
-- Rotation applied while dropping into tray.
Config.TrayDropRotation = vector3(0.0, 0.0, 18.0)
-- How long the basic machine pour sequence runs.
Config.BasicPourDurationMs = 3000
-- Slide duration for basic machine cup/container emergence.
Config.BasicSlideDurationMs = 1500
-- Delay before pickup becomes available in basic machines.
Config.BasicPickupReadyDelayMs = 350
-- Delay before camera exits after basic machine sequence.
Config.BasicExitDelayMs = 450
-- Chance that a bonus duplicate item is dispensed.
Config.BonusDropChance = 0.0
-- Number of total items dropped if bonus triggers.
Config.BonusDropQuantity = 2
-- Extra delay before spawning the bonus extra item.
Config.BonusExtraDelayMs = 300
-- Pour particle effects for basic machines.
Config.BasicPourEffects = {
water = {
dict = 'core', -- PTFX asset dictionary.
name = 'ent_sht_water_tower', -- PTFX effect name.
scale = 0.15, -- PTFX scale.
sourceHeight = 0.37, -- Vertical source point if sourceOffset is omitted.
-- sourceOffset = vector3(0.0, 0.0, 0.0), -- Optional exact local source point relative to machine.
},
coffee = {
dict = 'core',
name = 'ent_sht_petrol',
scale = 0.2,
sourceHeight = 0.77,
-- sourceOffset = vector3(0.0, 0.0, 0.0),
},
}
-- Specific world prop model used for each dispensed item.
Config.ItemProp = {
ecola = `prop_ecola_can`, -- eCola can prop.
sprunk = `prop_ecola_can`, -- Sprunk fallback can prop.
chips = `prop_ld_snack_01`, -- Chips bag prop.
proteinbar = `prop_choc_pq`, -- Protein bar prop.
water = `prop_ld_flow_bottle`, -- Water bottle prop.
coffee = `v_ret_gc_cup`, -- Coffee cup prop.
}
-- Fallback prop if an item does not have a specific prop entry.
Config.DefaultProp = `prop_ld_can_01`
-- Animations
-- Press button animation dictionary.
Config.AnimPressDict = 'mini@sprunk'
-- Press button animation name.
Config.AnimPressName = 'plyr_buy_drink_pt1'
-- Pickup animation dictionary.
Config.AnimPickupDict = 'pickup_object'
-- Pickup animation clip name.
Config.AnimPickupName = 'pickup_low'
-- Push jam animation dictionary.
Config.AnimPushJamDict = 'missheistfbi3b_ig7'
-- Push jam animation clip name.
Config.AnimPushJamName = 'lift_fibagent_loop'
-- If press animation is missing, fallback to using a scenario.
Config.UseScenarioFallback = true
-- Scenario used if animation fallback is needed.
Config.ScenarioName = 'PROP_HUMAN_VENDING_MACHINE'