# Configuration > Configuration reference for ML Vehicle Crafting Category: VEHICLE CRAFTING · Source: https://miciomods.it/docs/ml-vehiclecraft-configuration · Last updated: 2026-08-07 ## Overview - `shared/config.lua` - everything about how a build works: chassis mode, part list, animations, placement, collaboration, limits, paid layer - `shared/data/vehicles.lua` - the catalogue of craftable vehicles - `server/config_server.lua` - permission tiers and Discord logging ## General **shared/config.lua** ```lua Config.Debug = false Config.Language = 'en' Config.Theme = 'default' ``` - `Debug` - prints diagnostic output to the server console and F8 - `Language` - language file loaded from `locales/` - `Theme` - NUI theme: `default`, `wasteland`, `cyberpunk`, `noir`, `fantasy` ## Chassis **shared/config.lua** ```lua Config.ChassisMode = 'metadata' Config.Items = { chassis = 'vc_chassis', } Config.ChassisExportResources = { 'ml_crafting', } ``` - `ChassisMode` - `'metadata'` uses one generic chassis item carrying the target vehicle in its metadata; `'items'` uses one `vc_chassis_` item per vehicle and works on any inventory - `Items.chassis` - the generic chassis item name, used by metadata mode only - `ChassisExportResources` - resources allowed to call the give exports > **INFO:** Automatic fallback > > On an inventory without metadata support, metadata mode falls back to `'items'` and says so in the console. The per-vehicle chassis items have to exist in the inventory for that fallback to work. ## Parts **shared/config.lua** ```lua Config.Preset = 'advanced' Config.Parts = { modelLock = true, quality = false, } ``` - `Preset` - how many parts a build takes: `'simple'` (2), `'intermediate'` (5), `'advanced'` (9) - `Parts.modelLock` - a part belongs to one vehicle. An Elegy door only fits an Elegy, and a door pulled off a Sultan is useless on it. Off means any door fits any vehicle - `Parts.quality` - a part carries a condition from 0 to 100, and the weighted average of the installed parts decides the health of the finished vehicle. Dismantling a part to reuse it wears it down Both switches need an inventory with metadata support. On an inventory without one they turn themselves off and the script warns on start. > **WARNING:** Before turning quality on > > Every source of parts on the server has to stamp a condition on the item. Parts that arrive without one count as `Config.Quality.defaultValue`. At a value below 100, a loot table or a recipe that forgot the stamp silently produces half-broken vehicles and nothing points at the cause. **shared/config.lua** ```lua Config.Presets = { simple = { partTypes = { 'engine', 'wheel' }, installTimeMul = 0.6, dismantle = { qualityLoss = 0 }, }, intermediate = { partTypes = { 'engine', 'transmission', 'wheel', 'door', 'bonnet' }, installTimeMul = 1.0, dismantle = { qualityLoss = 10 }, }, advanced = { partTypes = { 'engine', 'transmission', 'brakes', 'wheel', 'door', 'seat', 'bonnet', 'boot', 'exhaust' }, installTimeMul = 1.4, dismantle = { qualityLoss = 20 }, }, } ``` - `partTypes` - which part types the build asks for - `installTimeMul` - multiplies the install time of every part - `dismantle.qualityLoss` - condition lost when a part is pulled back off, only used while `Config.Parts.quality` is on ## Part types Each entry in `Config.PartTypes` defines one kind of part. **shared/config.lua** ```lua engine = { item = 'vc_engine', bone = 'engine', count = 1, installTime = 15000, weight = 3, prop = { model = 'prop_car_engine_01', pos = vec3(0.025, 0.0, 0.15), rot = vec3(90.0, 0.0, 180.0) }, anim = { dict = 'creatures@rottweiler@tricks@', clip = 'petting_franklin' }, hoist = true, }, wheel = { item = 'vc_wheel', bone = { 'wheel_lf', 'wheel_rf', 'wheel_lr', 'wheel_rr' }, count = 'auto', installTime = 6000, weight = 1, prop = { model = 'prop_wheel_01' }, requires = { 'brakes' }, }, ``` - `item` - inventory item consumed to install it, overridable per vehicle through `partItems` - `bone` - the vehicle bone the part mounts on. A list means one slot per bone, each walked to and mounted separately - `offset` - marker position relative to the shell, for parts with no bone - `count` - how many slots the build asks for. A number is fixed and `0` removes the part entirely; `'auto'` reads it from the model, so a boat gets no wheels and a bike gets two. This is the main knob for how heavy a build feels - `installTime` - base duration in milliseconds, scaled by the preset - `weight` - how much this part counts in the condition average of the finished vehicle - `prop` - the model held during the carry, with optional `pos`, `rot` and `bone` to tune the attach - `anim`, `fx` - per-type override of the defaults in `Config.Install` - `requires` - other part types that must be installed first, enforced on both ends. The chain also runs backwards: the hood cannot come off while the engine is in - `hoist` - lower the part in on an engine hoist instead of carrying it > **INFO:** The count cannot be shrunk from the client > > The server recomputes every count from the config. For `'auto'` types it clamps the detected number to a per-class minimum, so a car can never be crafted with fewer than four wheels, two doors and two seats. ## Install sequence **shared/config.lua** ```lua Config.Install = { anim = { dict = 'anim@amb@clubhouse@tutorial@bkr_tut_ig3@', clip = 'machinic_loop_mechandplayer' }, carry = { anim = { dict = 'anim@heists@box_carry@', clip = 'idle' }, time = 1200, }, fx = { dict = 'core', name = 'ent_amb_sparking_wires', scale = 0.4 }, propBone = 56604, propPos = vec3(-0.08, 0.30, 0.37), propRot = vec3(0.0, 0.0, 180.0), broadcastRadius = 30.0, sounds = { enabled = true, mountStart = { name = 'Drill_Pin_Break', set = 'DLC_HEIST_FLEECA_SOUNDSET' }, mountDone = { name = 'PICK_UP', set = 'HUD_FRONTEND_DEFAULT_SOUNDSET' }, }, walkToPart = true, walkStopDistance = 1.2, walkTimeout = 6000, hoistProp = 'prop_engine_hoist', hoistRaise = 0.9, } ``` - `anim` - animation for placement, mounting and salvage. A part type's own `anim` wins over it - `carry.anim` - the pose held while carrying the part to the vehicle - `carry.time` - carry duration when there is no bone to walk to - `fx` - welding sparks played at the mount point - `propBone`, `propPos`, `propRot` - default attach of the held part on the ped - `broadcastRadius` - how far away other players see the carry, mount and hoist. `0` keeps it local - `sounds` - mount feedback, vanilla soundset references. Set `enabled = false` to mute - `walkToPart` - walk to the bone before mounting, when the bone exists - `walkStopDistance` - how close to the bone the player stops - `walkTimeout` - milliseconds before giving up and mounting on the spot - `hoistProp` - stand shown next to the car for hoisted parts. Set `false` to skip it. The hoist only plays on models that have an engine bone, so boats and bikes fall back to the carry - `hoistRaise` - height above the bay the hoisted part starts from ## Cinematic **shared/config.lua** ```lua Config.Cinematic = { enabled = true, duration = 5500, distance = 6.0, height = 1.6, fov = 50.0, turnSpeed = 0.22, } ``` Camera showcase played for the builder when a project finishes. `duration` is in milliseconds and the player can skip it. `turnSpeed` is degrees of orbit per frame. ## Quality mapping **shared/config.lua** ```lua Config.Quality = { defaultValue = 100, health = { engineMin = 250.0, engineMax = 1000.0, bodyMin = 350.0, bodyMax = 1000.0 }, } ``` - `defaultValue` - condition assumed for a part with no stamp - `health` - engine and body health of the finished vehicle, mapped linearly from the condition average With `Config.Parts.quality` off, every build finalizes at 100 and vehicles spawn at `engineMax` and `bodyMax`. ## Blueprints **shared/config.lua** ```lua Config.Blueprint = { consume = true, } ``` - `consume` - remove the blueprint item when the project starts. It is handed back if the placement fails Which vehicles need a blueprint is set per entry in `shared/data/vehicles.lua`. ## Placement **shared/config.lua** ```lua Config.Placement = { maxRayDistance = 25.0, duration = 3000, zoneRestricted = false, zones = { -- { coords = vec3(1730.5, 3310.7, 41.2), radius = 150.0 }, }, } ``` - `maxRayDistance` - how far ahead the placement ghost can be pushed - `duration` - progress bar played when the shell is set down - `zoneRestricted` - restrict building to the zones below - `zones` - list of `{ coords, radius }` workshop areas ## Carcass **shared/config.lua** ```lua Config.Carcass = { colors = { { primary = 13, secondary = 13 }, }, streamDistance = 100.0, workDistance = 20.0, } ``` - `colors` - GTA paint index pairs. One is picked when the shell is created, saved with the project and shown identically to every player. Add entries to give each shell a random colour - `streamDistance` - how far away the shell stays spawned - `workDistance` - how far a player can walk from the shell and still mount, dismantle and use the crew panel > **INFO:** The shell is not a networked vehicle > > Each client spawns its own copy from the project data. The server never owns an entity for it, so it costs no bandwidth and no other script can act on it. It does take a slot in the local vehicle pool, which is what `Config.Build.maxNearby` protects. ## Interaction and board **shared/config.lua** ```lua Config.Interaction = nil Config.InteractionDistance = 2.5 Config.InteractionKey = 38 Config.InteractionIcon = 'fas fa-wrench' Config.Dui = { drawDistance = 22.0, heightOffset = 1.55, sizeNear = 2.2, sizeFar = 0.85, style = 'gauge', styles = { gauge = { width = 700, height = 760, scale = 0.125 }, segmented = { width = 760, height = 320, scale = 0.128 }, pill = { width = 820, height = 210, scale = 0.110 }, }, } ``` - `Interaction` - `nil` lets `ml_bridge` decide, `'target'` forces the target system, `'textui'` forces the key prompt - `InteractionDistance` - range of the build and salvage options - `InteractionKey` - control id used in TextUI mode - `InteractionIcon` - icon of the build options in target mode - `Dui.drawDistance` - how close a player must be for the holographic board to appear. The shell itself streams from much further - `Dui.heightOffset` - metres above the shell the board floats - `Dui.sizeNear`, `Dui.sizeFar` - clamps on the on-screen size, so the board never covers the screen up close nor becomes unreadable far away - `Dui.style` - `'gauge'` is a 270 degree dial, `'segmented'` a ten-segment bar, `'pill'` a single line - `Dui.styles` - resolution and world scale of each style ## Collaboration **shared/config.lua** ```lua Config.Collaboration = { install = 'crew', dismantle = 'crew', partsGoTo = 'remover', claimByFinisher = false, } ``` - `install` - who may mount parts: `'owner'`, `'crew'` or `'public'` - `dismantle` - who may pull parts off. Accepts the same three values plus `'nobody'`, which welds the project shut for everyone including the owner - `partsGoTo` - `'remover'` gives a dismantled part to whoever pulled it; `'owner'` returns it to the project owner, or drops it at the shell if they are offline. Set it to `'owner'` and griefers can undo work but gain nothing - `claimByFinisher` - whoever presses **Finish project** owns the vehicle. Off keeps it with the original owner A safe server runs `install = 'crew'`, `dismantle = 'nobody'`. A hardcore one runs both `'public'` with `claimByFinisher = true`. ## Plate **shared/config.lua** ```lua Config.Plate = { format = '11AAA111', } ``` - `format` - `1` is a digit, `A` a letter, `.` alphanumeric, anything else is kept literal. Maximum eight characters The plate is reserved when the shell is placed and kept through to the finished vehicle. It is checked against other projects and against owned vehicles, and re-rolled until free. ## Build limits **shared/config.lua** ```lua Config.Build = { maxPerPlayer = 2, staleDays = 14, autoFinalize = false, maxNearby = 6, crowdRadius = 60.0, } ``` - `maxPerPlayer` - concurrent projects one player can have open - `staleDays` - delete projects untouched for this long. `0` never deletes - `autoFinalize` - finish the moment the last part goes in. Off leaves the **Finish project** button to the player - `maxNearby` - refuse a new shell when this many already sit within `crowdRadius`. `0` removes the cap - `crowdRadius` - radius the density cap counts in ## Output **shared/config.lua** ```lua Config.Output = { mode = 'owned', startEngineOff = true, } ``` - `mode` - `'owned'` registers the finished vehicle through `OpenServer.OnFinalized`; `'temporary'` spawns it without ownership or persistence - `startEngineOff` - the finished vehicle starts with the engine off ## Integrations **shared/config.lua** ```lua Config.Integrations = { vehicleKeys = true, fuel = true, fuelStart = 0.0, mechanic = true, } ``` - `vehicleKeys` - hand the keys to the owner when the vehicle is finished - `fuel` - set the starting fuel level - `fuelStart` - that level. `0.0` means the first drive is to a pump - `mechanic` - seed the engine condition in `ml_mechanic` from the part condition average Each one is skipped when the matching resource is not running. ## Tools **shared/config.lua** ```lua Config.Tools = { enabled = false, item = 'toolkit', label = 'Toolkit', breakChance = 0, } ``` - `enabled` - require a tool in the inventory to mount or dismantle - `item` - the tool item name - `label` - name used in the messages - `breakChance` - percentage chance the tool is consumed per use ## Skill gate **shared/config.lua** ```lua Config.SkillGate = { enabled = false, category = 'mechanic', tiers = { [2] = 5, [3] = 15 }, } ``` - `enabled` - require a minimum skill level for higher tiers - `category` - the skill category read through `OpenServer.GetSkillLevel` - `tiers` - vehicle tier mapped to the minimum level ## Job gate **shared/config.lua** ```lua Config.JobGate = { enabled = false, actions = { -- build = { 'mechanic' }, -- work = { 'mechanic' }, -- salvage = { 'mechanic' }, }, } ``` - `enabled` - restrict actions to jobs - `actions.build` - starting a project - `actions.work` - mounting and dismantling - `actions.salvage` - stripping a wreck Each key takes a list of job names. A key left out is not restricted. ## Salvage **shared/config.lua** ```lua Config.Salvage = { enabled = false, models = {}, duration = 9000, skillCheck = false, toolRequired = true, deleteVehicle = true, cooldown = 5000, resalvageBlock = 900000, yield = { { item = 'vc_wheel', min = 1, max = 3, chance = 0.70 }, { item = 'vc_door', min = 1, max = 2, chance = 0.50 }, { item = 'vc_seat', min = 1, max = 2, chance = 0.50 }, { item = 'vc_exhaust', min = 1, max = 1, chance = 0.40 }, { item = 'vc_bonnet', min = 1, max = 1, chance = 0.35 }, { item = 'vc_engine', min = 1, max = 1, chance = 0.20 }, }, } ``` - `enabled` - allow stripping vehicles for parts - `models` - the spawn names that can be stripped. The option appears on every vehicle in the world with one of these models - `duration` - milliseconds the strip takes - `skillCheck` - `false`, or a list of `ox_lib` difficulties such as `{ 'easy', 'easy', 'medium' }` - `toolRequired` - also obey `Config.Tools` while salvaging - `deleteVehicle` - remove the vehicle once stripped - `cooldown` - milliseconds between salvages, per player - `resalvageBlock` - milliseconds a stripped vehicle stays empty - `yield` - rolled loot, `chance` from 0.0 to 1.0 > **DANGER:** Choose the models carefully > > The list is matched by model, not by condition, so every vehicle of that model becomes strippable wherever it stands. A model players can own or buy turns their parked vehicle into a target. Vehicles registered to a player are refused by the server, but the safe list is still junk models only. Common traffic models also make parts free: GTA respawns traffic constantly, and only `cooldown` stands between a player and an endless supply. ## Admin **shared/config.lua** ```lua Config.Admin = { command = 'vehiclecraft', } Config.Commands = { giveChassis = 'vc_givechassis', giveParts = 'vc_giveparts', grantUnlock = 'vc_grantunlock', revokeUnlock = 'vc_revokeunlock', giveCredit = 'vc_givecredit', } ``` - `Admin.command` - opens the admin panel - `Commands` - console and chat command names. Rename them freely, the rank each one needs is set by the permission tiers ## Permissions **server/config_server.lua** ```lua Config.Permissions = { Admin = { useDefaultAdmins = true, ace = { 'ml_vehiclecraft.admin', 'group.admin', 'group.superadmin' }, }, Dangerous = { useDefaultAdmins = true, ace = { 'ml_vehiclecraft.admin', 'group.superadmin' }, }, GiveItems = { useDefaultAdmins = true, ace = { 'ml_vehiclecraft.admin', 'group.superadmin' }, }, Premium = { useDefaultAdmins = false, ace = { 'ml_vehiclecraft.premiumadmin' }, }, } ``` - `Admin` - open the panel, look at projects, teleport to them - `Dangerous` - delete a project, force it complete, force a single slot on or off, swap a project to another vehicle - `GiveItems` - spawn chassis and parts from the panel - `Premium` - grant and revoke unlocks and paid uses A tier passes if the player is a framework admin, when `useDefaultAdmins` is true, or holds any of the listed aces. Buttons a tier cannot use are hidden from the panel, and the server refuses the action regardless. > **WARNING:** Premium is ace only > > `Premium` ships with `useDefaultAdmins = false` because it hands out things bought with real money. A normal framework admin cannot reach it. Removing the whole `Config.Permissions` block leaves `Admin` open to any framework admin and locks `Premium` for everyone. ## Logging **server/config_server.lua** ```lua Config.Logs = { enabled = false, webhook = '', provider = 'discord', webhooks = { build = '', part = '', finalize = '', salvage = '', premium = '', admin = '', }, } ``` - `enabled` - master switch - `webhook` - fallback used when a category below is empty - `provider` - `'discord'`, `'fivemanage'`, `'ox_lib'`, `'both'` or `'all'` - `webhooks.build` - project started or abandoned - `webhooks.part` - part mounted or dismantled - `webhooks.finalize` - vehicle completed - `webhooks.salvage` - wreck stripped - `webhooks.premium` - unlocks, uses, paid swaps - `webhooks.admin` - admin panel actions Every entry carries the vehicle, project number, plate, person, position and the detail of the event. Set any webhook to `false` to disable that log entirely. ## Vehicle catalogue **shared/data/vehicles.lua** ```lua ['emperor'] = { label = 'Rusty Sedan', description = 'A tired four-door. Runs, mostly.', class = 'automobile', tier = 1, chassis = 'vc_chassis_emperor', blueprint = false, }, ['rebel'] = { label = 'Off-road 4x4', description = 'Farm truck with a taste for mud.', class = 'automobile', tier = 2, chassis = 'vc_chassis_rebel', blueprint = 'vc_bp_rebel', swapCost = 2, }, ``` **The key of each entry is the GTA spawn name.** Adding a vehicle means pasting its spawn name and giving it a label. - `label` - the name players read. The key is never shown to them - `description` - one line of flavour shown in the panel - `class` - `'automobile'`, `'bike'`, `'boat'`, `'heli'`, `'plane'` or `'trailer'` - `tier` - 1 to 3, used for sorting and the skill gate - `chassis` - the per-vehicle chassis item, used by `Config.ChassisMode = 'items'` - `blueprint` - `false`, or a blueprint item also required to start the project - `model` - only needed to give one car two different recipes. Give the entries two different keys and point both at the same spawn name. Left out, the key is the spawn name - `partItems` - per-type item overrides for this vehicle - `donorOnly` - buildable only by players holding that vehicle's unlock, enforced while the paid layer is on - `swapCost`, `instantCost` - uses spent on this vehicle by the paid actions - `counts` - fixed slot counts, for example `counts = { wheel = 4, door = 0 }`. Overrides what the model reports, and is the way to allow a doorless build past the per-class minimums ## Paid layer **shared/config.lua** ```lua Config.Premium = { enabled = false, redeemCommand = 'vcredeem', actions = { swap = { enabled = true, cost = 1 }, instantFinish = { enabled = true, cost = 1 }, }, packages = { ['ml_vehiclecraft_uses_3'] = { uses = 3, eur = 8 }, ['ml_vehiclecraft_uses_5'] = { uses = 5, eur = 12 }, ['ml_vehiclecraft_uses_10'] = { uses = 10, eur = 20 }, ['ml_vehiclecraft_car_imperator'] = { unlock = 'imperator', chassis = 'imperator', eur = 20 }, }, } ``` - `enabled` - master switch for the whole layer. Off, nothing paid renders or runs - `redeemCommand` - command that opens the player screen with their balance, their unlocks and the redeem box. Set `false` to hide it - `actions.swap` - convert a project into another vehicle keeping the parts already installed - `actions.instantFinish` - complete a project with no parts - `actions.*.cost` - how many uses one action spends - `packages` - store package id mapped to what it grants. `uses` adds to the balance, `unlock` plus `chassis` grant a permanent vehicle unlock and its chassis There is one currency: uses. One use pays for any paid action. Uses are a per-player database balance, not items, so nothing is tradeable, dupeable or lootable. The script never takes in-game money. > **INFO:** The donor gate follows the master switch > > While `Config.Premium.enabled` is false, `donorOnly` is not enforced. The vehicles marked as donor-only in the catalogue are buildable by anyone holding their chassis.