Why Rental Systems Enhance Roleplay
Not every player on a roleplay server owns a vehicle, especially new characters who are just starting out. A rental car and bike system fills this gap by providing temporary transportation at affordable rates, creating natural roleplay opportunities at rental counters and giving new players freedom to explore the map without relying on public transit or walking. Beyond basic transportation, rental systems support tourism roleplay where visitors rent convertibles for scenic drives, delivery jobs that require specific vehicle types, and event scenarios where players need matching vehicles for organized cruises or races. The system also generates a passive revenue stream for the server economy through rental fees, damage deposits, and late return penalties.
Fleet Configuration and Vehicle Categories
Organize your rental fleet into categories that serve different player needs and budgets. Economy vehicles provide affordable basic transportation, mid-range cars offer more comfort and performance, premium vehicles cater to luxury experiences, and motorcycles give players a two-wheeled alternative. Each category should have multiple vehicle options with clear pricing. Include both hourly and daily rental rates so players can choose the most economical option for their needs. Define the fleet in a configuration table that specifies every available vehicle with its rental price, required deposit, category, and spawn model:
Config.RentalLocations = {
['airport'] = {
label = 'LS Airport Car Rental',
pedModel = 's_f_y_airhostess_01',
pedCoords = vector4(-1037.34, -2736.82, 20.17, 238.52),
spawnPoint = vector4(-1029.12, -2728.91, 20.17, 148.76),
returnZone = vector3(-1035.00, -2730.00, 20.17),
returnRadius = 10.0,
categories = {'economy', 'midrange', 'premium', 'motorcycle'},
},
['downtown'] = {
label = 'Downtown Bike Rental',
pedModel = 'a_m_y_business_02',
pedCoords = vector4(-259.68, -892.41, 31.08, 69.24),
spawnPoint = vector4(-264.30, -888.12, 31.08, 339.87),
returnZone = vector3(-260.00, -890.00, 31.08),
returnRadius = 8.0,
categories = {'motorcycle', 'bicycle'},
},
}
Config.RentalFleet = {
economy = {
{model = 'blista', label = 'Blista', priceHour = 150, priceDay = 800, deposit = 500},
{model = 'asea', label = 'Asea', priceHour = 120, priceDay = 650, deposit = 400},
{model = 'stanier', label = 'Stanier', priceHour = 130, priceDay = 700, deposit = 450},
},
midrange = {
{model = 'schafter2', label = 'Schafter', priceHour = 300, priceDay = 1600, deposit = 1000},
{model = 'oracle', label = 'Oracle', priceHour = 280, priceDay = 1500, deposit = 900},
},
premium = {
{model = 'felon', label = 'Felon GT', priceHour = 600, priceDay = 3200, deposit = 2500},
{model = 'exemplar', label = 'Exemplar', priceHour = 550, priceDay = 3000, deposit = 2000},
},
motorcycle = {
{model = 'pcj', label = 'PCJ-600', priceHour = 100, priceDay = 500, deposit = 300},
{model = 'sanchez', label = 'Sanchez', priceHour = 80, priceDay = 400, deposit = 250},
},
bicycle = {
{model = 'cruiser', label = 'Cruiser Bike', priceHour = 30, priceDay = 150, deposit = 50},
{model = 'tribike', label = 'Tri-Cycles Race', priceHour = 25, priceDay = 120, deposit = 50},
},
}
Rental Process and NUI Interface
The rental interaction starts when a player approaches the rental counter NPC. Using a target system or proximity check, open an NUI menu that displays available vehicle categories with filtering options. Each vehicle listing should show the model name, an image preview if possible, hourly and daily rates, the required deposit, and availability status. When the player selects a vehicle and confirms the rental duration, the server processes the transaction. The total charge is the rental fee for the selected duration plus the damage deposit. The deposit is held by the system and refunded when the vehicle is returned undamaged. Implement the rental confirmation as a server event that validates the player has sufficient funds, checks that the selected vehicle is available, charges the player, and spawns the rental vehicle at the designated spawn point:
RegisterNetEvent('rental:server:rentVehicle', function(locationId, vehicleModel, duration)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return end
local location = Config.RentalLocations[locationId]
if not location then return end
-- Check if player already has an active rental
if ActiveRentals[Player.PlayerData.citizenid] then
TriggerClientEvent('QBCore:Notify', src, 'You already have an active rental', 'error')
return
end
-- Find vehicle config
local vehicleConfig = nil
for _, category in pairs(Config.RentalFleet) do
for _, v in ipairs(category) do
if v.model == vehicleModel then
vehicleConfig = v
break
end
end
if vehicleConfig then break end
end
if not vehicleConfig then return end
-- Calculate cost
local rentalFee = duration == 'day' and vehicleConfig.priceDay or vehicleConfig.priceHour
local totalCost = rentalFee + vehicleConfig.deposit
if Player.Functions.GetMoney('bank') < totalCost then
TriggerClientEvent('QBCore:Notify', src, 'Insufficient funds. Need $' .. totalCost, 'error')
return
end
-- Charge player
Player.Functions.RemoveMoney('bank', totalCost, 'vehicle-rental')
-- Generate rental plate
local plate = 'RNT' .. math.random(10000, 99999)
-- Store active rental
ActiveRentals[Player.PlayerData.citizenid] = {
model = vehicleModel,
plate = plate,
deposit = vehicleConfig.deposit,
rentalFee = rentalFee,
startTime = os.time(),
duration = duration,
expiresAt = os.time() + (duration == 'day' and 86400 or 3600),
locationId = locationId,
initialHealth = 1000,
}
TriggerClientEvent('rental:client:spawnVehicle', src, vehicleModel, plate, location.spawnPoint)
TriggerClientEvent('QBCore:Notify', src, 'Vehicle rented! Return before your time expires.', 'success')
end)
Damage Deposit and Return Mechanics
The damage deposit system creates accountability for how players treat rental vehicles. When the vehicle is returned, compare its current health values against the initial values recorded at rental time. GTA vehicles have separate health values for the engine, body, and individual components, giving you granular control over damage assessment. Calculate the repair cost based on the damage sustained and deduct it from the deposit before refunding the remainder. If the damage exceeds the deposit amount, the player receives no refund and may be charged an additional fee. If the vehicle is returned in perfect condition, the full deposit is refunded. This system encourages careful driving without being punitive for minor scratches that are inevitable in GTA's driving physics:
RegisterNetEvent('rental:server:returnVehicle', function(vehicleNet)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return end
local citizenid = Player.PlayerData.citizenid
local rental = ActiveRentals[citizenid]
if not rental then return end
local vehicle = NetworkGetEntityFromNetworkId(vehicleNet)
if not DoesEntityExist(vehicle) then return end
-- Check plate matches
local plate = GetVehicleNumberPlateText(vehicle)
if plate:trim() ~= rental.plate then
TriggerClientEvent('QBCore:Notify', src, 'This is not your rental vehicle', 'error')
return
end
-- Calculate damage
local bodyHealth = GetVehicleBodyHealth(vehicle)
local engineHealth = GetVehicleEngineHealth(vehicle)
local damagePercent = 1.0 - ((bodyHealth + engineHealth) / 2000.0)
local repairCost = math.floor(rental.deposit * damagePercent)
-- Calculate late fee
local lateFee = 0
if os.time() > rental.expiresAt then
local overHours = math.ceil((os.time() - rental.expiresAt) / 3600)
lateFee = overHours * 200
end
-- Refund deposit minus costs
local refund = math.max(0, rental.deposit - repairCost - lateFee)
if refund > 0 then
Player.Functions.AddMoney('bank', refund, 'rental-deposit-refund')
end
DeleteEntity(vehicle)
ActiveRentals[citizenid] = nil
local message = string.format('Vehicle returned. Deposit: $%d | Repairs: $%d | Late fee: $%d | Refund: $%d',
rental.deposit, repairCost, lateFee, refund)
TriggerClientEvent('QBCore:Notify', src, message, refund > 0 and 'success' or 'warning')
end)
Expiration and Auto-Return System
Rental vehicles should not persist indefinitely. Implement a timer system that tracks when each rental expires and takes action accordingly. Send the player a notification thirty minutes before expiration, then another at ten minutes, and a final warning at five minutes. When the rental expires, begin charging per-minute late fees up to a maximum threshold. If the player fails to return the vehicle within the grace period after expiration, automatically despawn the vehicle and forfeit the entire deposit. This prevents abandoned rental vehicles from cluttering the server. The expiration check should run server-side on a regular interval, iterating through all active rentals and taking appropriate action based on remaining time. Store the vehicle's network ID so you can delete it remotely if needed, and handle the case where the vehicle has been destroyed before the rental expires by automatically closing the rental with a full deposit forfeit.
Multiple Rental Locations
Place rental locations at strategic points around the map where temporary transportation makes the most sense. The airport is an obvious choice, serving players who have just arrived in the city. Downtown locations near government buildings serve players attending court cases or business meetings. Beach locations offer motorcycles and bicycles for leisure activities along the coastline. Each location can offer a different subset of the fleet, with the airport having the full range from economy to premium, while beach locations only stock motorcycles and bicycles. Customize the NPC model at each location to match the setting. Airport rentals use a professional business model, beach rentals use a casual surfer model, and downtown locations use a suited clerk. This attention to detail makes each rental spot feel like a distinct business rather than identical copies of the same system.
GPS Tracking and Anti-Theft
Rental vehicles should have built-in GPS tracking that lets the rental company and the player locate the vehicle at any time. Display the rental vehicle's location on the player's map with a distinct blip icon. If the vehicle moves outside a configurable boundary, send a warning notification. Implement anti-theft measures that prevent other players from storing or garaging a rental vehicle, ensuring it stays in the rental system's control. When a player attempts to put a rental vehicle in their personal garage, the system should block the action with a notification explaining that rental vehicles cannot be stored. For motorcycles and bicycles, add a virtual lock that only the renting player can unlock, preventing casual theft. If a rental vehicle is stolen, the renting player should be able to report it through a phone app, triggering a GPS ping that helps them locate the vehicle. Track total mileage during the rental for potential surcharges on high-mileage rentals, adding another layer of realistic vehicle rental management.
Business Integration and Player-Owned Rentals
For servers with a business ownership system, allow players to purchase and operate their own rental business. The business owner sets the fleet composition by purchasing vehicles to add to the rental pool, determines pricing, hires employees to staff the counter, and collects revenue from rentals. Business owners should have a management dashboard that shows active rentals, daily revenue, fleet utilization rates, and maintenance costs for vehicles that accumulate damage over multiple rentals. Employees can handle front-desk operations like processing rentals and returns, inspecting returned vehicles for damage, and escalating disputes to the owner. This player-driven approach transforms the rental system from a static game mechanic into a dynamic business that reflects the entrepreneurial spirit of roleplay servers and creates employment opportunities for players who prefer customer service roles over high-action gameplay.