QB-MechanicJob - Vehicle Repair & Tuning System
The qb-mechanicjob resource provides a comprehensive vehicle repair and tuning system for QBCore servers, featuring mechanic shops, vehicle customization, repair services, and towing operations.
Overview
QB-MechanicJob creates a realistic automotive service experience with vehicle repairs, custom tuning, paint jobs, performance modifications, and complete mechanic shop management. Players can operate as mechanics or customers seeking vehicle services.
If you want a marketplace option instead of extending the stock job, review a FiveM mechanic system and test it on staging before replacing qb-mechanicjob.
Key Features
- Mechanic Shops: Multiple workshop locations with full service capabilities
- Vehicle Repairs: Engine, body, and component repair systems
- Custom Tuning: Performance modifications and upgrades
- Paint & Liveries: Vehicle customization and visual modifications
- Towing Service: Flatbed and tow truck operations
- Parts Inventory: Vehicle parts and supplies management
- Service Billing: Customer invoicing and payment system
- Shop Management: Business operations and employee management
- Vehicle Diagnostics: Damage assessment and repair estimates
Installation
Prerequisites
- QBCore Framework
- qb-target (for interaction system)
- qb-menu (for mechanic menus)
- qb-input (for service forms)
- qb-inventory (for vehicle parts)
- qb-vehiclekeys (for key management)
Installation Steps
- Download the Resource
cd resources/[qb]
git clone https://github.com/qbcore-framework/qb-mechanicjob.git- Database Setup
-- Mechanic shop tables
CREATE TABLE IF NOT EXISTS `mechanic_orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`citizenid` varchar(50) DEFAULT NULL,
`mechanic` varchar(50) DEFAULT NULL,
`vehicle_plate` varchar(50) DEFAULT NULL,
`service_type` varchar(100) DEFAULT NULL,
`parts_used` longtext DEFAULT NULL,
`total_cost` int(11) DEFAULT 0,
`status` varchar(50) DEFAULT 'pending',
`created_at` timestamp DEFAULT current_timestamp(),
PRIMARY KEY (`id`)
);
CREATE TABLE IF NOT EXISTS `mechanic_parts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`shop` varchar(50) DEFAULT NULL,
`part_name` varchar(100) DEFAULT NULL,
`quantity` int(11) DEFAULT 0,
`cost` int(11) DEFAULT 0,
`updated_at` timestamp DEFAULT current_timestamp() ON UPDATE current_timestamp(),
PRIMARY KEY (`id`)
);
CREATE TABLE IF NOT EXISTS `vehicle_modifications` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`plate` varchar(50) DEFAULT NULL,
`modifications` longtext DEFAULT NULL,
`performance_mods` longtext DEFAULT NULL,
`last_modified` timestamp DEFAULT current_timestamp() ON UPDATE current_timestamp(),
PRIMARY KEY (`id`),
UNIQUE KEY `plate` (`plate`)
);
CREATE TABLE IF NOT EXISTS `mechanic_invoices` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`citizenid` varchar(50) DEFAULT NULL,
`shop` varchar(50) DEFAULT NULL,
`services` longtext DEFAULT NULL,
`amount` int(11) DEFAULT 0,
`paid` tinyint(1) DEFAULT 0,
`date` timestamp DEFAULT current_timestamp(),
PRIMARY KEY (`id`)
);- Add Items to qb-core/shared/items.lua
-- Vehicle Parts
['engine_part'] = {
['name'] = 'engine_part',
['label'] = 'Engine Part',
['weight'] = 1000,
['type'] = 'item',
['image'] = 'engine_part.png',
['unique'] = false,
['useable'] = true,
['shouldClose'] = true,
['combinable'] = nil,
['description'] = 'Engine component for vehicle repairs'
},
['transmission_part'] = {
['name'] = 'transmission_part',
['label'] = 'Transmission Part',
['weight'] = 800,
['type'] = 'item',
['image'] = 'transmission_part.png',
['unique'] = false,
['useable'] = true,
['shouldClose'] = true,
['combinable'] = nil,
['description'] = 'Transmission component'
},
['brake_pads'] = {
['name'] = 'brake_pads',
['label'] = 'Brake Pads',
['weight'] = 300,
['type'] = 'item',
['image'] = 'brake_pads.png',
['unique'] = false,
['useable'] = true,
['shouldClose'] = true,
['combinable'] = nil,
['description'] = 'Brake pads for vehicle repair'
},
['tire'] = {
['name'] = 'tire',
['label'] = 'Vehicle Tire',
['weight'] = 2000,
['type'] = 'item',
['image'] = 'tire.png',
['unique'] = false,
['useable'] = true,
['shouldClose'] = true,
['combinable'] = nil,
['description'] = 'Replacement vehicle tire'
},
['turbo_kit'] = {
['name'] = 'turbo_kit',
['label'] = 'Turbo Kit',
['weight'] = 5000,
['type'] = 'item',
['image'] = 'turbo_kit.png',
['unique'] = false,
['useable'] = true,
['shouldClose'] = true,
['combinable'] = nil,
['description'] = 'Performance turbo kit upgrade'
}- Add Job Configuration to qb-core/shared/jobs.lua
['mechanic'] = {
label = 'Mechanic',
defaultDuty = true,
offDutyPay = false,
grades = {
['0'] = {
name = 'Apprentice',
payment = 50
},
['1'] = {
name = 'Mechanic',
payment = 75
},
['2'] = {
name = 'Advanced Mechanic',
payment = 100
},
['3'] = {
name = 'Shop Supervisor',
payment = 125,
isboss = true
},
['4'] = {
name = 'Shop Manager',
payment = 150,
isboss = true
}
}
}- Add to server.cfg
ensure qb-mechanicjobEnsure qb-mechanicjob loads after qb-core, qb-target, and qb-inventory in your server.cfg
Configuration
Basic Configuration
The main configuration file is located at config.lua:
Config = {}
-- General Settings
Config.UseTarget = true
Config.RepairCost = {
body = 1000,
engine = 1500,
transmission = 1200,
brakes = 800,
suspension = 900
}
-- Mechanic Shop Locations
Config.Locations = {
["bennys"] = {
label = "Benny's Original Motor Works",
coords = vector3(-212.55, -1324.2, 30.89),
blip = {
sprite = 72,
color = 17,
scale = 0.8
},
zones = {
duty = vector3(-212.55, -1324.2, 30.89),
stash = vector3(-222.47, -1329.73, 30.89),
vehicle = vector4(-229.47, -1315.39, 30.89, 270.0),
lift1 = vector4(-212.0, -1308.0, 31.3, 270.0),
lift2 = vector4(-212.0, -1318.0, 31.3, 270.0)
}
},
["hayes"] = {
label = "Hayes Autos",
coords = vector3(-1419.24, -446.58, 35.91),
blip = {
sprite = 72,
color = 17,
scale = 0.8
},
zones = {
duty = vector3(-1419.24, -446.58, 35.91),
stash = vector3(-1413.35, -448.26, 35.91),
vehicle = vector4(-1428.46, -455.12, 35.91, 120.0),
lift1 = vector4(-1419.0, -450.0, 36.0, 120.0)
}
}
}
-- Tow Truck Spawn Points
Config.TowSpawns = {
[1] = vector4(-229.47, -1315.39, 30.89, 270.0),
[2] = vector4(-228.47, -1320.39, 30.89, 270.0)
}
-- Vehicle Modification Costs
Config.ModificationCosts = {
engine = {
[0] = 0,
[1] = 5000,
[2] = 10000,
[3] = 15000,
[4] = 25000
},
brakes = {
[0] = 0,
[1] = 2500,
[2] = 5000,
[3] = 7500
},
transmission = {
[0] = 0,
[1] = 4000,
[2] = 8000,
[3] = 12000
},
suspension = {
[0] = 0,
[1] = 3000,
[2] = 6000,
[3] = 9000,
[4] = 12000
},
turbo = 15000,
armor = {
[0] = 0,
[1] = 5000,
[2] = 10000,
[3] = 15000,
[4] = 20000,
[5] = 25000
}
}Vehicle Damage System
-- Damage Thresholds
Config.DamageSystem = {
engine = {
threshold = 650.0,
repairCost = 1500,
parts = {"engine_part"}
},
body = {
threshold = 800.0,
repairCost = 1000,
parts = {"body_panel"}
},
transmission = {
threshold = 700.0,
repairCost = 1200,
parts = {"transmission_part"}
},
brakes = {
threshold = 750.0,
repairCost = 800,
parts = {"brake_pads"}
},
tires = {
threshold = 900.0,
repairCost = 400,
parts = {"tire"}
}
}
-- Repair Requirements
Config.RepairRequirements = {
mechanic_level = {
basic = 0, -- Apprentice can do basic repairs
advanced = 2, -- Advanced Mechanic for complex repairs
custom = 3 -- Supervisor+ for custom modifications
},
tools_required = {
basic = {"wrench", "screwdriver"},
advanced = {"diagnostic_tool", "hydraulic_jack"},
custom = {"tuning_laptop", "dyno_access"}
}
}Parts and Pricing
-- Vehicle Parts Configuration
Config.Parts = {
["engine_part"] = {
label = "Engine Component",
cost = 500,
category = "engine",
required_skill = 1
},
["transmission_part"] = {
label = "Transmission Component",
cost = 400,
category = "transmission",
required_skill = 1
},
["brake_pads"] = {
label = "Brake Pads",
cost = 200,
category = "brakes",
required_skill = 0
},
["tire"] = {
label = "Performance Tire",
cost = 300,
category = "tires",
required_skill = 0
},
["turbo_kit"] = {
label = "Turbo Kit",
cost = 2500,
category = "performance",
required_skill = 3
}
}
-- Service Pricing
Config.ServicePrices = {
oil_change = 150,
tire_repair = 100,
brake_service = 300,
engine_tune = 500,
full_service = 800,
custom_paint = 1200,
performance_tune = 2000
}API Reference
Client Exports
RepairVehicle
Repair a vehicle with specified components.
-- Repair specific component
exports['qb-mechanicjob']:RepairVehicle(vehicle, "engine")
-- Repair multiple components
exports['qb-mechanicjob']:RepairVehicle(vehicle, {"engine", "body", "brakes"})
-- Full repair
exports['qb-mechanicjob']:RepairVehicle(vehicle, "all")
-- Parameters:
-- vehicle: Vehicle entity
-- components: String or table of components to repairGetVehicleDamage
Get detailed damage information for a vehicle.
local damageInfo = exports['qb-mechanicjob']:GetVehicleDamage(vehicle)
-- Returns:
-- {
-- engine = number (0-1000),
-- body = number (0-1000),
-- transmission = number (0-1000),
-- brakes = number (0-1000),
-- tires = table of tire conditions
-- }ModifyVehicle
Apply performance modifications to a vehicle.
-- Apply single modification
exports['qb-mechanicjob']:ModifyVehicle(vehicle, "engine", 2)
-- Apply multiple modifications
exports['qb-mechanicjob']:ModifyVehicle(vehicle, {
engine = 2,
brakes = 1,
suspension = 1,
turbo = true
})
-- Parameters:
-- vehicle: Vehicle entity
-- modifications: Modification type and levelServer Exports
CreateServiceOrder
Create a new service order for a customer.
local orderId = exports['qb-mechanicjob']:CreateServiceOrder(data)
-- Parameters:
-- data: {
-- citizenid = string,
-- mechanic = string,
-- vehicle_plate = string,
-- services = table,
-- total_cost = number
-- }ProcessPayment
Process payment for mechanic services.
local success = exports['qb-mechanicjob']:ProcessPayment(source, amount, description)
-- Parameters:
-- source: Player server ID
-- amount: Payment amount
-- description: Service descriptionUpdatePartInventory
Update shop part inventory.
exports['qb-mechanicjob']:UpdatePartInventory(shop, partName, quantity)
-- Parameters:
-- shop: Shop identifier
-- partName: Part name
-- quantity: Quantity change (+/-)GetShopInventory
Get current shop inventory.
local inventory = exports['qb-mechanicjob']:GetShopInventory(shop)
-- Returns table of parts and quantitiesEvents
Client Events
-- Vehicle repair started
RegisterNetEvent('qb-mechanicjob:client:repairStarted', function(repairType)
-- Handle repair start
end)
-- Vehicle modification applied
RegisterNetEvent('qb-mechanicjob:client:modificationApplied', function(modType, level)
-- Handle modification
end)
-- Service order created
RegisterNetEvent('qb-mechanicjob:client:orderCreated', function(orderId)
-- Handle new order
end)
-- Payment processed
RegisterNetEvent('qb-mechanicjob:client:paymentProcessed', function(amount)
-- Handle payment completion
end)Server Events
-- Mechanic on/off duty
RegisterNetEvent('qb-mechanicjob:server:dutyToggle', function(onDuty)
-- Handle duty status change
end)
-- Vehicle towed
RegisterNetEvent('qb-mechanicjob:server:vehicleTowed', function(plate, location)
-- Handle vehicle towing
end)
-- Parts ordered
RegisterNetEvent('qb-mechanicjob:server:orderParts', function(parts, shop)
-- Handle parts ordering
end)
-- Service completed
RegisterNetEvent('qb-mechanicjob:server:serviceCompleted', function(orderId, services)
-- Handle service completion
end)Usage Examples
Vehicle Repair System
-- Client-side repair process
RegisterNetEvent('qb-mechanicjob:client:startRepair', function(repairType)
local player = QBCore.Functions.GetPlayerData()
if player.job.name == "mechanic" and player.job.onduty then
local vehicle = QBCore.Functions.GetClosestVehicle()
local requiredParts = Config.DamageSystem[repairType].parts
-- Check if mechanic has required parts
for i = 1, #requiredParts do
local hasPart = QBCore.Functions.HasItem(requiredParts[i])
if not hasPart then
QBCore.Functions.Notify("Missing required part: " .. requiredParts[i], "error")
return
end
end
-- Start repair animation and progress
QBCore.Functions.Progressbar("vehicle_repair", "Repairing " .. repairType .. "...", 10000, false, true, {
disableMovement = true,
disableCarMovement = true,
disableMouse = false,
disableCombat = true,
}, {
animDict = "mini@repair",
anim = "fixing_a_ped",
}, {
model = "prop_tool_box_04",
bone = 57005,
coords = vector3(0.4, 0.0, 0.0),
rotation = vector3(0.0, 0.0, 0.0),
}, {}, function()
-- Repair completed
TriggerServerEvent('qb-mechanicjob:server:repairVehicle', VehToNet(vehicle), repairType)
end)
end
end)
-- Server-side repair handler
RegisterNetEvent('qb-mechanicjob:server:repairVehicle', function(netId, repairType)
local src = source
local player = QBCore.Functions.GetPlayer(src)
local vehicle = NetworkGetEntityFromNetworkId(netId)
if player.PlayerData.job.name == "mechanic" then
local repairCost = Config.RepairCost[repairType] or 500
local requiredParts = Config.DamageSystem[repairType].parts
-- Remove required parts from inventory
for i = 1, #requiredParts do
player.Functions.RemoveItem(requiredParts[i], 1)
end
-- Apply repair to vehicle
if repairType == "engine" then
SetVehicleEngineHealth(vehicle, 1000.0)
elseif repairType == "body" then
SetVehicleBodyHealth(vehicle, 1000.0)
elseif repairType == "brakes" then
SetVehicleBrake(vehicle, false)
end
TriggerClientEvent('QBCore:Notify', src, repairType .. ' repaired successfully!', 'success')
end
end)Vehicle Modification System
-- Performance tuning interface
RegisterNetEvent('qb-mechanicjob:client:openTuningMenu', function()
local vehicle = QBCore.Functions.GetClosestVehicle()
if not vehicle then return end
local tuningMenu = {
{
header = "Vehicle Tuning",
isMenuHeader = true
},
{
header = "Engine Upgrade",
txt = "Improve engine performance",
params = {
event = "qb-mechanicjob:client:upgradeEngine",
args = {vehicle = vehicle}
}
},
{
header = "Brake System",
txt = "Upgrade brake system",
params = {
event = "qb-mechanicjob:client:upgradeBrakes",
args = {vehicle = vehicle}
}
},
{
header = "Suspension",
txt = "Modify suspension",
params = {
event = "qb-mechanicjob:client:upgradeSuspension",
args = {vehicle = vehicle}
}
},
{
header = "Turbo Kit",
txt = "Install turbo kit - $" .. Config.ModificationCosts.turbo,
params = {
event = "qb-mechanicjob:client:installTurbo",
args = {vehicle = vehicle}
}
}
}
exports['qb-menu']:openMenu(tuningMenu)
end)
-- Engine upgrade implementation
RegisterNetEvent('qb-mechanicjob:client:upgradeEngine', function(data)
local vehicle = data.vehicle
local currentLevel = GetVehicleMod(vehicle, 11) -- Engine mod
local nextLevel = currentLevel + 1
if nextLevel <= 4 then
local cost = Config.ModificationCosts.engine[nextLevel]
QBCore.Functions.TriggerCallback('qb-mechanicjob:server:canAfford', function(canAfford)
if canAfford then
SetVehicleMod(vehicle, 11, nextLevel, false)
TriggerServerEvent('qb-mechanicjob:server:payForMod', cost, 'Engine Upgrade Level ' .. nextLevel)
else
QBCore.Functions.Notify("Insufficient funds for upgrade", "error")
end
end, cost)
else
QBCore.Functions.Notify("Engine already at maximum level", "error")
end
end)Towing Service
-- Tow truck operations
RegisterNetEvent('qb-mechanicjob:client:towVehicle', function()
local player = QBCore.Functions.GetPlayerData()
if player.job.name == "mechanic" and player.job.onduty then
local towTruck = GetVehiclePedIsIn(PlayerPedId(), false)
local towModel = GetEntityModel(towTruck)
-- Check if player is in a tow truck
if towModel == GetHashKey("flatbed") or towModel == GetHashKey("towtruck") then
local targetVehicle = QBCore.Functions.GetClosestVehicle()
if targetVehicle then
QBCore.Functions.Progressbar("towing_vehicle", "Attaching vehicle to tow truck...", 8000, false, true, {
disableMovement = true,
disableCarMovement = true,
disableMouse = false,
disableCombat = true,
}, {
animDict = "mini@repair",
anim = "fixing_a_ped",
}, {}, {}, function()
AttachEntityToEntity(targetVehicle, towTruck, 0, 0.0, -2.5, 1.0, 0.0, 0.0, 0.0, false, false, false, false, 2, true)
QBCore.Functions.Notify("Vehicle attached to tow truck", "success")
end)
else
QBCore.Functions.Notify("No vehicle nearby to tow", "error")
end
else
QBCore.Functions.Notify("You need to be in a tow truck", "error")
end
end
end)Troubleshooting
Common Issues
Vehicle Repairs Not Working
-- Check vehicle entity validity
if not DoesEntityExist(vehicle) then
print("Vehicle entity does not exist")
return
end
-- Verify repair permissions
local player = QBCore.Functions.GetPlayerData()
if player.job.name ~= "mechanic" or not player.job.onduty then
print("Player not authorized for repairs")
return
endModification Costs Not Updating
- Verify modification cost configuration in
config.lua - Check if vehicle model supports specific modifications
- Ensure proper database table structure for tracking modifications
Parts Inventory Issues
-- Debug parts inventory
RegisterCommand('checkparts', function()
local inventory = exports['qb-mechanicjob']:GetShopInventory('bennys')
for part, quantity in pairs(inventory) do
print(part .. ": " .. quantity)
end
end)Debug Commands
-- Repair vehicle instantly (admin only)
/fixveh
-- Add mechanic parts to inventory
/givepart [part_name] [quantity]
-- Check vehicle modification levels
/checkmods
-- Toggle mechanic duty
/mechdutyPerformance Optimization
- Damage Calculations: Optimize vehicle damage checking frequency
- Part Inventory: Cache inventory data to reduce database queries
- Vehicle Spawning: Implement proper vehicle cleanup and pooling
Always test vehicle modifications and repairs thoroughly to ensure they work correctly with your server’s vehicle system.