GTA's traffic and parked cars are switched off on an EndCore server, so every vehicle in the world comes from en-garages. It leaves abandoned vehicles at roadsides, out of players' sight. They are damaged, low on fuel and often locked. You break in or hotwire one (see en-vehiclekeys), drive it to a garage lot and pay cash to claim it. You then get a key item and the vehicle becomes yours for good.
Owned vehicles stay exactly where you parked them, even across restarts. They only exist in the world while a player is nearby. You can store and retrieve them at garages, recall one from wherever it is, or recover it after a wreck. Dealers sell a few working vehicles for cash. The resource also handles fuel (burned while driving, refilled from jerry cans, cans filled at the rare working pump) and repair kits.
At a glance
| Depends on | /onesync, oxmysql, en-core, en-ui, en-inventory, en-vehiclekeys |
| Start after | All of the above |
| Database tables | encore_vehicles (created automatically) |
| Config files | config/shared.lua (Config, sent to clients), config/server.lua (ServerConfig, server only) |
| Key binds | en_garages_use (default E): store, claim, open a garage or browse a dealer, depending on where you are |
| In-game builder | Content kinds garage and dealer in en-admin |
How it works
Abandoned vehicles
On each spawn pass the server asks a nearby player's client for roadside points. It checks the distance, requires 6 m of clearance, and spawns the vehicle server-side with a random plate. It then rolls random fuel, engine and body health and dirt, plus a chance of flat tyres and broken windows. The vehicle is locked with probability lockedChance. An unclaimed vehicle despawns after despawnAfter seconds with nobody within despawnDistance.
Claiming
To claim a vehicle you must:
- sit in the driver seat, inside the radius of a garage with
claim = true - use a vehicle nobody owns yet
- have hotwired it, or have key access to it
- own fewer than
maxPerCharactervehicles - pay
claimCostcash
The plate is kept if it's free, otherwise a new one is issued (ABC 123 format). You receive a vehicle_key item, and the vehicle's trunk and glovebox inventories (trunk:<plate> and glovebox:<plate>) become persistent. With groupClaims on, group leaders and ranks with the invite permission can register vehicles to their group instead.
Persistence
Owned vehicles that are out spawn when a player comes within 350 m. They despawn, with the position saved, after 45 s with nobody within 450 m and nobody inside. Positions, fuel and damage are batch-saved every 30 s. Appearance is saved when the driver gets out.
A vehicle whose engine health drops to -3900 or lower is marked wrecked.
Retrieving
| Where the vehicle is | Cost |
|---|---|
| Stored at this garage | Free |
| Stored at another garage | recallCost (free if that garage no longer exists) |
| Out in the world | recallCost |
| Wrecked | recoverCost, returned with engine 250, body 400 and at least 10 fuel |
Fuel
The driving client burns fuel based on revs, vehicle class and Config.fuel.usage. To refuel, target any vehicle while carrying a jerrycan (8 s). A can's fill level is stored in its metadata.durability (0–100). Fuel pump props can fill cans for pricePerCan cash (6 s), but whether a pump works is rolled per pump on every restart (25% by default).
Repair
Target a vehicle's engine or bonnet with a repair_kit. The repair skill bonus cuts the duration by up to 50%, and the engine and body health restored are multiplied by 1 + repair bonus. Each repair awards mechanics XP. See en-skills.
Configuration
config/shared.lua
| Key | Default | What it does |
|---|---|---|
Config.garages | 4 garages: motel, sapcounsel, sandy, paleto | Each has id, label, coords (vec3), radius, claim (can unowned vehicles be registered here) and spawns (vec4 list, the first clear one is used) |
Config.dealers | larrys (Larry's Salvage) | Each has id, label, coords, radius, spawn (vec4 delivery spot) and stock of { model, label, price, type } |
Config.ownership.maxPerCharacter | 5 | Most vehicles one character can own |
Config.ownership.claimCost | 250 | Cash to register a scavenged vehicle (0 = free) |
Config.ownership.groupClaims | true | Let group leaders and ranks with invite permission register vehicles to their group |
Config.ownership.recoverCost | 750 | Fee to recover a wreck |
Config.ownership.recallCost | 250 | Fee to recall a vehicle that is out, or move it between garages |
Config.fuel.enabled | true | Turns fuel burn on or off |
Config.fuel.usage | 1.6 | Percent of the tank burned per minute at full revs |
Config.fuel.classMultiplier | table by class 0–21 | Per-class burn rate; cycles (13) use 0.0, motorcycles (8) 0.6, helicopters and planes (15, 16) 2.0 |
Config.fuel.jerrycanAmount | 35 | Fuel a full can adds |
Config.fuel.jerrycanItem | 'jerrycan' | Jerry can item name |
Config.fuel.pumps | enabled = true, workingChance = 0.25, pricePerCan = 120, models = {...} | Pump props that can fill cans |
Config.repair | item = 'repair_kit', duration = 12000, engine = 450, body = 300, xp = { mechanics = 20 } | Repair kit behaviour |
Config.keys.use | 'E' | Interact key |
config/server.lua
| Key | Default | What it does |
|---|---|---|
ServerConfig.persistence | interval = 5000, streamDistance = 350.0, despawnDistance = 450.0, despawnAfter = 45, saveInterval = 30000 | How owned vehicles stream in and out, and how often they save |
ServerConfig.world.enabled | true | Spawn abandoned vehicles |
ServerConfig.world.maxTotal | 45 | Most abandoned vehicles in the world |
ServerConfig.world.perPlayer | 4 | Most abandoned vehicles within 300 m of a player |
ServerConfig.world.spawnDistance | { min = 110, max = 240 } | How far from players they appear |
ServerConfig.world.despawnDistance / despawnAfter | 400 / 150 | Remove after this many seconds with nobody within this range |
ServerConfig.world.interval | 15000 | Milliseconds between spawn passes |
ServerConfig.world.lockedChance | 0.6 | Chance a spawned vehicle is locked |
ServerConfig.world.fuel, engine, body, dirt | {2,35}, {150,850}, {350,950}, {8,15} | Random ranges for condition |
ServerConfig.world.flatTyreChance / brokenWindowChance | 0.15 / 0.2 | Chance of flat tyres and broken windows |
ServerConfig.world.pools | list | Models that can spawn, as { model, type, weight } |
Valid vehicle types are automobile, bike, boat, heli and plane.
Adding garages, dealers and scavenged models
-- config/shared.lua
Config.garages[#Config.garages + 1] = {
id = 'grapeseed', label = 'Grapeseed Barn',
coords = vec3(2447.1, 4971.2, 46.8), radius = 20.0, claim = true,
spawns = { vec4(2440.3, 4975.0, 46.2, 45.0), vec4(2436.8, 4970.9, 46.2, 45.0) },
}
-- inside Config.dealers[1].stock
{ model = 'sandking', label = 'Vapid Sandking', price = 6800, type = 'automobile' },
-- config/server.lua, inside ServerConfig.world.pools
{ model = 'dune', type = 'automobile', weight = 2 },You can also place garages and dealers in game with en-admin. A placed entry with the same id as a config entry replaces it, and deleting the placement brings the config version back.
Exports
All exports are server-side. There are no client exports.
| Export | Arguments | Returns |
|---|---|---|
GetVehicleByPlate | plate | { id, plate, citizenid, group_id, model, label, state, garage, fuel, engine, body } or nil. The plate is normalised through en-vehiclekeys |
IsOwnedVehicle | entity | boolean |
GetOwnedVehicles | source | Vehicles the player manages as owner or group member: { id, plate, label, state, where, fuel, engine, body, group, cost, action }[] |
GiveVehicle | source, model, label?, garageId?, vehicleType? | New record id or nil. Stored in garageId, else the first config garage that still exists, else any garage. Also gives a key |
GetFuel | entity | Number 0–100 or nil |
SetFuel | entity, fuel | Sets the fuel state bag, clamped to 0–100 |
BuilderList | kind | For 'garage' or 'dealer': { id, label, subtitle, coords, placed, inConfig }[], sorted by label |
BuilderGet | kind, id | Editable data for the entry |
BuilderValidate | kind, id, data | ok, err |
BuilderSchema | kind | Form fields for en-admin |
BuilderTemplate | kind | Defaults for a new garage or dealer |
The Builder* exports are used by en-admin's in-game builder; see Content registry.
Commands
All commands are restricted to group.admin.
| Command | What it does |
|---|---|
/givevehicle <target> <model> [label...] | Creates an owned vehicle stored in a garage |
/deletevehicle <plate...> | Permanently deletes an owned vehicle |
/clearworldvehicles | Removes every unclaimed world vehicle |
Events
Server-local events, fired with TriggerEvent:
| Event | Payload |
|---|---|
en-garages:server:claimed | source, vehicleId, isGroup |
en-garages:server:stored | source, vehicleId, garageId |
en-garages:server:retrieved | source, vehicleId, garageId, action (retrieve, transfer, recall or recover) |
en-garages:server:bought | source, vehicleId, dealerId |
en-garages:server:wrecked | vehicleId |
The client event en-garages:client:repaired (netId, engine, body) is sent to the entity owner after a repair.
State bags
Entity state bags on vehicles:
| Key | Meaning |
|---|---|
owned | true on owned vehicles |
vehicleId | Database id of an owned vehicle |
groupId | Owning group id, for group vehicles |
model | Model name of an owned vehicle |
world | true on an unclaimed abandoned vehicle; cleared when claimed |
fuel | 0–100. The driving client publishes it when it changes by 0.5 or more, or every 15 s |
props | Appearance and damage to apply; the owning client applies it, then clears the bag |
hotwired | Set by en-vehiclekeys; claiming needs this or key access |
Database
encore_vehicles: id (primary key), plate (unique, VARCHAR 12), citizenid, group_id, model, type, label, props (JSON), fuel, engine, body, state (stored, out or wrecked), garage, position (JSON {x,y,z,heading}), created_at, updated_at.
Examples
Give a vehicle as a quest or event reward:
-- server
local id = exports['en-garages']:GiveVehicle(source, 'bodhi2', 'Canis Bodhi', 'sandy', 'automobile')
if not id then
print(('Could not give a vehicle to %s'):format(source))
endTop up the fuel of whatever a player is driving:
-- server
local vehicle = GetVehiclePedIsIn(GetPlayerPed(source), false)
if vehicle ~= 0 and exports['en-garages']:IsOwnedVehicle(vehicle) then
local fuel = exports['en-garages']:GetFuel(vehicle) or 0
exports['en-garages']:SetFuel(vehicle, fuel + 25)
endLog every claim:
AddEventHandler('en-garages:server:claimed', function(source, vehicleId, isGroup)
print(('%s claimed vehicle %d%s'):format(GetPlayerName(source), vehicleId, isGroup and ' for their group' or ''))
end)