2025-03-08 13:44:05 +00:00
|
|
|
--[[
|
|
|
|
|
Vehicle Info & Properties Module
|
|
|
|
|
----------------------------------
|
|
|
|
|
This module provides utilities for:
|
|
|
|
|
- Retrieving vehicle information from a Vehicles table.
|
|
|
|
|
- Getting and setting vehicle properties using the active framework.
|
|
|
|
|
- Comparing vehicle property differences.
|
|
|
|
|
- Synchronizing vehicle properties across clients.
|
|
|
|
|
- Managing network control of vehicles.
|
|
|
|
|
- Finding the closest vehicle to a given position.
|
|
|
|
|
]]
|
2025-03-01 12:58:43 +00:00
|
|
|
|
2025-03-08 13:44:05 +00:00
|
|
|
-- Cached vehicle info to avoid unnecessary re-searches.
|
|
|
|
|
local lastCar, carInfo = nil, {}
|
|
|
|
|
|
|
|
|
|
--- Searches the 'Vehicles' table for a specific vehicle's details.
|
|
|
|
|
--- If the vehicle differs from the last searched, it retrieves its model and updates the carInfo table.
|
|
|
|
|
--- The table includes the vehicle's name, price, and class information.
|
2025-03-01 12:58:43 +00:00
|
|
|
---
|
2025-03-08 13:44:05 +00:00
|
|
|
--- @param vehicle number The entity ID of the vehicle to search for.
|
|
|
|
|
--- @return table|nil table A table containing the vehicle's details or nil if the vehicle is invalid.
|
2025-03-01 12:58:43 +00:00
|
|
|
---
|
2025-03-08 13:44:05 +00:00
|
|
|
--- @usage
|
2025-03-01 12:58:43 +00:00
|
|
|
--- ```lua
|
|
|
|
|
--- local info = searchCar(vehicleEntity)
|
2025-03-08 13:44:05 +00:00
|
|
|
--- print(info.name, info.price, info.class.name, info.class.index)
|
2025-03-01 12:58:43 +00:00
|
|
|
--- ```
|
|
|
|
|
function searchCar(vehicle)
|
|
|
|
|
if lastCar ~= vehicle then -- If same car, use previous info
|
|
|
|
|
lastCar = vehicle
|
|
|
|
|
carInfo = {}
|
|
|
|
|
local model = GetEntityModel(vehicle)
|
|
|
|
|
local classlist = {
|
|
|
|
|
"Compacts", --1
|
|
|
|
|
"Sedans", --2
|
|
|
|
|
"SUVs", --3
|
|
|
|
|
"Coupes", --4
|
|
|
|
|
"Muscle", --5
|
|
|
|
|
"Sports Classics", --6
|
|
|
|
|
"Sports", --7
|
|
|
|
|
"Super", --8
|
|
|
|
|
"Motorcycles", --9
|
|
|
|
|
"Off-road", --10
|
|
|
|
|
"Industrial", --11
|
|
|
|
|
"Utility", --12
|
|
|
|
|
"Vans", --13
|
|
|
|
|
"Cycles", --14
|
|
|
|
|
"Boats", --15
|
|
|
|
|
"Helicopters", --16
|
|
|
|
|
"Planes", --17
|
|
|
|
|
"Service", --18
|
|
|
|
|
"Emergency", --19
|
|
|
|
|
"Military", --20
|
|
|
|
|
"Commercial", --21
|
|
|
|
|
"Trains", --22
|
|
|
|
|
}
|
|
|
|
|
if Vehicles then
|
|
|
|
|
for k, v in pairs(Vehicles) do
|
|
|
|
|
if tonumber(v.hash) == model or GetHashKey(v.hash) == model or GetHashKey(v.model) == model then
|
|
|
|
|
debugPrint("^6Bridge^7: ^2Vehicle info found in^7 ^4Vehicles^7 ^2table^7: ^6"..(v.hash and v.hash or v.model).. " ^7(^6"..Vehicles[k].name.."^7)")
|
|
|
|
|
carInfo.name = Vehicles[k].name.." "..Vehicles[k].brand
|
|
|
|
|
carInfo.price = Vehicles[k].price
|
|
|
|
|
carInfo.class = classlist[GetVehicleClass(vehicle) + 1], GetVehicleClass(vehicle)
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if not carInfo.name then
|
|
|
|
|
debugPrint("^6Bridge^7: ^2Vehicle ^1not ^2found in ^4vehicles^7 ^2table^7: ^6"..model.." ^7(^6"..GetDisplayNameFromVehicleModel(model):lower().."^7)")
|
|
|
|
|
carInfo.name = string.upper(GetMakeNameFromVehicleModel(model).." "..GetDisplayNameFromVehicleModel(model))
|
|
|
|
|
carInfo.price = 0
|
|
|
|
|
carInfo.class = classlist[GetVehicleClass(vehicle) + 1], GetVehicleClass(vehicle)
|
|
|
|
|
end
|
|
|
|
|
return carInfo
|
|
|
|
|
else
|
|
|
|
|
if not carInfo.name then
|
|
|
|
|
debugPrint("^6Bridge^7: ^2Vehicle ^1not ^2found in ^4vehicles^7 ^2table^7: ^6"..model.." ^7(^6"..GetDisplayNameFromVehicleModel(model):lower().."^7)")
|
|
|
|
|
carInfo.name = string.upper(GetMakeNameFromVehicleModel(model).." "..GetDisplayNameFromVehicleModel(model))
|
|
|
|
|
carInfo.price = 0
|
|
|
|
|
carInfo.class = classlist[GetVehicleClass(vehicle) + 1], GetVehicleClass(vehicle)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
return carInfo
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2025-03-08 13:44:05 +00:00
|
|
|
-------------------------------------------------------------
|
|
|
|
|
-- Vehicle Properties Functions
|
|
|
|
|
-------------------------------------------------------------
|
2025-03-01 12:58:43 +00:00
|
|
|
|
2025-03-08 13:44:05 +00:00
|
|
|
--- Retrieves the properties of a given vehicle using the active framework.
|
2025-03-01 12:58:43 +00:00
|
|
|
---
|
|
|
|
|
--- @param vehicle number The entity ID of the vehicle.
|
2025-03-08 13:44:05 +00:00
|
|
|
--- @return table|nil table A table containing the vehicle's properties or nil if invalid.
|
2025-03-01 12:58:43 +00:00
|
|
|
---
|
|
|
|
|
--- @usage
|
|
|
|
|
--- ```lua
|
|
|
|
|
--- local props = getVehicleProperties(vehicleEntity)
|
|
|
|
|
--- if props then
|
2025-03-08 13:44:05 +00:00
|
|
|
--- -- Use vehicle properties
|
2025-03-01 12:58:43 +00:00
|
|
|
--- end
|
|
|
|
|
--- ```
|
|
|
|
|
function getVehicleProperties(vehicle)
|
2025-03-08 13:44:05 +00:00
|
|
|
if not vehicle then return nil end
|
|
|
|
|
|
2025-03-01 12:58:43 +00:00
|
|
|
local properties = {}
|
|
|
|
|
if isStarted(QBExport) and not isStarted(QBXExport) then
|
|
|
|
|
properties = Core.Functions.GetVehicleProperties(vehicle)
|
|
|
|
|
debugPrint("^6Bridge^7: ^2Getting Vehicle Properties ^7[^6"..QBExport.."^7] - [^3"..vehicle.."^7] - [^3"..GetEntityModel(vehicle).."^7/^3"..properties.model.."^7] - [^3"..properties.plate.."^7]")
|
|
|
|
|
elseif isStarted(OXLibExport) then
|
|
|
|
|
properties = lib.getVehicleProperties(vehicle)
|
|
|
|
|
debugPrint("^6Bridge^7: ^2Getting Vehicle Properties ^7[^6"..OXLibExport.."^7] - [^3"..vehicle.."^7] - [^3"..GetEntityModel(vehicle).."^7/^3"..properties.model.."^7] - [^3"..properties.plate.."^7]")
|
|
|
|
|
end
|
|
|
|
|
return properties
|
|
|
|
|
end
|
|
|
|
|
|
2025-03-08 13:44:05 +00:00
|
|
|
--- Sets the properties of a given vehicle if changes are detected.
|
|
|
|
|
--- It compares the current properties with the new ones and applies the update using the active framework.
|
2025-03-01 12:58:43 +00:00
|
|
|
---
|
2025-03-08 13:44:05 +00:00
|
|
|
--- @param vehicle number The entity ID of the vehicle.
|
|
|
|
|
--- @param props table The new properties to apply.
|
2025-03-01 12:58:43 +00:00
|
|
|
---
|
2025-03-08 13:44:05 +00:00
|
|
|
--- @usage
|
2025-03-01 12:58:43 +00:00
|
|
|
--- ```lua
|
|
|
|
|
--- setVehicleProperties(vehicleEntity, newProperties)
|
|
|
|
|
--- ```
|
|
|
|
|
function setVehicleProperties(vehicle, props)
|
|
|
|
|
if checkDifferences(vehicle, props) then
|
|
|
|
|
if not DoesEntityExist(vehicle) then
|
2025-03-08 13:44:05 +00:00
|
|
|
print("Unable to set vehicle properties for '"..vehicle.."' (entity does not exist)")
|
2025-03-01 12:58:43 +00:00
|
|
|
end
|
2025-03-08 13:44:05 +00:00
|
|
|
|
2025-03-01 12:58:43 +00:00
|
|
|
if isStarted(QBExport) and not isStarted(QBXExport) then
|
|
|
|
|
Core.Functions.SetVehicleProperties(vehicle, props)
|
|
|
|
|
debugPrint("^6Bridge^7: ^2Setting Vehicle Properties ^7[^6"..QBExport.."^7] - [^3"..vehicle.."^7] - [^3"..GetEntityModel(vehicle).."^7/^3"..props.model.."^7] - [^3"..props.plate.."^7]")
|
|
|
|
|
else
|
|
|
|
|
TriggerServerEvent(getScript()..":ox:setVehicleProperties", VehToNet(vehicle), props)
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
debugPrint("^6Bridge^7: ^2No Changes Found ^7 [^3"..vehicle.."^7] - [^3"..GetEntityModel(vehicle).."^7/^3"..props.model.."^7] - [^3"..props.plate.."^7]")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--- Checks for differences between the current and new vehicle properties.
|
2025-03-08 13:44:05 +00:00
|
|
|
--- Compares properties using JSON encoding for deep comparison and logs differences.
|
2025-03-01 12:58:43 +00:00
|
|
|
---
|
2025-03-08 13:44:05 +00:00
|
|
|
--- @param vehicle number The entity ID of the vehicle.
|
|
|
|
|
--- @param newProps table The new properties to compare.
|
|
|
|
|
--- @return boolean `true` if differences are found; `false` otherwise.
|
2025-03-01 12:58:43 +00:00
|
|
|
---
|
2025-03-08 13:44:05 +00:00
|
|
|
--- @usage
|
2025-03-01 12:58:43 +00:00
|
|
|
--- ```lua
|
|
|
|
|
--- if checkDifferences(vehicleEntity, newProperties) then
|
|
|
|
|
--- setVehicleProperties(vehicleEntity, newProperties)
|
|
|
|
|
--- end
|
|
|
|
|
--- ```
|
|
|
|
|
function checkDifferences(vehicle, newProps)
|
|
|
|
|
local oldProps = getVehicleProperties(vehicle)
|
|
|
|
|
debugPrint("^6Bridge^7: ^2Finding differences in ^3Vehicle Properties^7")
|
2025-03-08 13:44:05 +00:00
|
|
|
local differencesFound = false
|
|
|
|
|
|
2025-03-01 12:58:43 +00:00
|
|
|
for k in pairs(oldProps) do
|
|
|
|
|
if json.encode(oldProps[k]) ~= json.encode(newProps[k]) then
|
2025-03-08 13:44:05 +00:00
|
|
|
differencesFound = true
|
2025-03-01 12:58:43 +00:00
|
|
|
debugPrint("^6Bridge^7: ^5Old ^7[^3"..k.."^7] - "..json.encode(oldProps[k], { indent = true }))
|
|
|
|
|
debugPrint("^6Bridge^7: ^5New ^7[^3"..k.."^7] - "..json.encode(newProps[k], { indent = true }))
|
|
|
|
|
end
|
|
|
|
|
end
|
2025-03-08 13:44:05 +00:00
|
|
|
|
|
|
|
|
return differencesFound
|
2025-03-01 12:58:43 +00:00
|
|
|
end
|
|
|
|
|
|
2025-03-08 13:44:05 +00:00
|
|
|
-------------------------------------------------------------
|
|
|
|
|
-- Vehicle Properties Synchronization
|
|
|
|
|
-------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
--- Event handler for setting vehicle properties received from the server.
|
|
|
|
|
--- Listens for the `ox:setVehicleProperties` event and applies the properties.
|
2025-03-01 12:58:43 +00:00
|
|
|
---
|
2025-03-08 13:44:05 +00:00
|
|
|
--- @event `getScript()..ox:setVehicleProperties`
|
|
|
|
|
--- @param netId number The network ID of the vehicle.
|
|
|
|
|
--- @param props table The new vehicle properties.
|
2025-03-01 12:58:43 +00:00
|
|
|
RegisterNetEvent(getScript()..":ox:setVehicleProperties", function(netId, props)
|
|
|
|
|
local vehicle = NetworkGetEntityFromNetworkId(netId)
|
|
|
|
|
local value = props
|
|
|
|
|
Entity(vehicle).state[getScript()..':setVehicleProperties'] = value
|
|
|
|
|
end)
|
|
|
|
|
|
2025-03-08 13:44:05 +00:00
|
|
|
--- Handles state bag changes for updating vehicle properties.
|
|
|
|
|
--- When the state bag changes, the new properties are applied to the vehicle.
|
2025-03-01 12:58:43 +00:00
|
|
|
---
|
2025-03-08 13:44:05 +00:00
|
|
|
--- @param bagName string The state bag's name.
|
|
|
|
|
--- @param key string The key that changed.
|
|
|
|
|
--- @param value table The new state value.
|
2025-03-01 12:58:43 +00:00
|
|
|
AddStateBagChangeHandler(getScript()..':setVehicleProperties', '', function(bagName, _, value)
|
|
|
|
|
if not value or not GetEntityFromStateBagName then return end
|
|
|
|
|
local entity = GetEntityFromStateBagName(bagName)
|
|
|
|
|
local networked = not bagName:find('localEntity')
|
|
|
|
|
debugPrint("^6Bridge^7: ^2Setting Vehicle Properties ^7[^6"..OXLibExport.."^7] - [^3"..entity.."^7] - [^3"..GetEntityModel(entity).."^7] - [^3"..value.plate.."^7]")
|
|
|
|
|
|
|
|
|
|
if networked and NetworkGetEntityOwner(entity) ~= cache.playerId then return end
|
|
|
|
|
|
|
|
|
|
if lib.setVehicleProperties(entity, value) then
|
|
|
|
|
Entity(entity).state:set('setVehicleProperties', nil, true)
|
|
|
|
|
end
|
|
|
|
|
end)
|
|
|
|
|
|
2025-03-08 13:44:05 +00:00
|
|
|
-------------------------------------------------------------
|
|
|
|
|
-- Vehicle Control Functions
|
|
|
|
|
-------------------------------------------------------------
|
|
|
|
|
|
2025-03-01 12:58:43 +00:00
|
|
|
--- This function ensures that the vehicle is controlled by the current player and is set as a mission entity.
|
|
|
|
|
--- It requests network control and sets the vehicle accordingly to synchronize changes across clients.
|
|
|
|
|
---
|
|
|
|
|
---@param entity number The entity ID of the vehicle to push.
|
|
|
|
|
---
|
|
|
|
|
---@usage
|
|
|
|
|
--- ```lua
|
|
|
|
|
--- pushVehicle(vehicleEntity)
|
|
|
|
|
--- ```
|
|
|
|
|
function pushVehicle(entity)
|
|
|
|
|
SetVehicleModKit(entity, 0)
|
|
|
|
|
if entity ~= 0 and DoesEntityExist(entity) then
|
2025-03-08 13:44:05 +00:00
|
|
|
-- Request network control if not already controlled.
|
2025-03-01 12:58:43 +00:00
|
|
|
if not NetworkHasControlOfEntity(entity) then
|
|
|
|
|
debugPrint("^6Bridge^7: ^3pushVehicle^7: ^2Requesting network control of vehicle^7.")
|
|
|
|
|
NetworkRequestControlOfEntity(entity)
|
|
|
|
|
local timeout = 2000
|
|
|
|
|
while timeout > 0 and not NetworkHasControlOfEntity(entity) do
|
|
|
|
|
Wait(100)
|
|
|
|
|
timeout = timeout - 100
|
|
|
|
|
end
|
|
|
|
|
if NetworkHasControlOfEntity(entity) then
|
2025-03-08 13:44:05 +00:00
|
|
|
debugPrint("^6Bridge^7: ^3pushVehicle^7: ^2Network now has control of the entity^7.")
|
2025-03-01 12:58:43 +00:00
|
|
|
end
|
|
|
|
|
end
|
2025-03-08 13:44:05 +00:00
|
|
|
|
|
|
|
|
-- Set as mission entity if not already set.
|
2025-03-01 12:58:43 +00:00
|
|
|
if not IsEntityAMissionEntity(entity) then
|
2025-03-08 13:44:05 +00:00
|
|
|
debugPrint("^6Bridge^7: ^3pushVehicle^7: ^2Setting vehicle as a ^7'^2mission^7' ^2entity^7.")
|
2025-03-01 12:58:43 +00:00
|
|
|
SetEntityAsMissionEntity(entity, true, true)
|
|
|
|
|
local timeout = 2000
|
|
|
|
|
while timeout > 0 and not IsEntityAMissionEntity(entity) do
|
|
|
|
|
Wait(100)
|
|
|
|
|
timeout = timeout - 100
|
|
|
|
|
end
|
|
|
|
|
if IsEntityAMissionEntity(entity) then
|
|
|
|
|
debugPrint("^6Bridge^7: ^3pushVehicle^7: ^2Vehicle is a ^7'^2mission^7'^2 entity^7.")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2025-03-06 23:49:07 +00:00
|
|
|
|
2025-03-08 13:44:05 +00:00
|
|
|
--- Finds the closest vehicle to the specified coordinates.
|
|
|
|
|
--- The function uses different APIs based on whether a source is provided.
|
|
|
|
|
---
|
|
|
|
|
--- @param coords table|vector3 (Optional) The reference coordinates. If nil, uses the player's position.
|
|
|
|
|
--- @param src boolean (Optional) If true, uses GetPlayerPed(source) and GetAllVehicles.
|
|
|
|
|
--- @return number|number closestVehicle|closestDistance The closest vehicle entity and its distance.
|
|
|
|
|
---
|
|
|
|
|
--- @usage
|
|
|
|
|
--- ```lua
|
|
|
|
|
--- local closestVeh, distance = getClosestVehicle({ x = 100, y = 200, z = 30 }, true)
|
|
|
|
|
--- ```
|
2025-03-06 23:49:07 +00:00
|
|
|
function getClosestVehicle(coords, src)
|
2025-03-08 13:44:05 +00:00
|
|
|
local ped, vehicles, closestDistance, closestVehicle
|
|
|
|
|
|
2025-03-06 23:49:07 +00:00
|
|
|
if src then
|
2025-03-08 13:44:05 +00:00
|
|
|
ped = GetPlayerPed(src)
|
|
|
|
|
vehicles = GetAllVehicles()
|
2025-03-06 23:49:07 +00:00
|
|
|
else
|
2025-03-08 13:44:05 +00:00
|
|
|
ped = PlayerPedId()
|
|
|
|
|
vehicles = GetGamePool('CVehicle')
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local closestDistance, closestVehicle = -1, -1
|
|
|
|
|
|
|
|
|
|
if coords then
|
|
|
|
|
if type(coords) == 'table' then
|
|
|
|
|
coords = vec3(coords.x, coords.y, coords.z)
|
2025-03-06 23:49:07 +00:00
|
|
|
end
|
2025-03-08 13:44:05 +00:00
|
|
|
else
|
|
|
|
|
coords = GetEntityCoords(ped)
|
|
|
|
|
end
|
2025-03-06 23:49:07 +00:00
|
|
|
|
2025-03-08 13:44:05 +00:00
|
|
|
for i = 1, #vehicles, 1 do
|
|
|
|
|
local vehicleCoords = GetEntityCoords(vehicles[i])
|
|
|
|
|
local distance = #(vehicleCoords - coords)
|
|
|
|
|
|
|
|
|
|
if closestDistance == -1 or distance < closestDistance then
|
|
|
|
|
closestDistance = distance
|
|
|
|
|
closestVehicle = vehicles[i]
|
2025-03-06 23:49:07 +00:00
|
|
|
end
|
|
|
|
|
end
|
2025-03-08 13:44:05 +00:00
|
|
|
|
|
|
|
|
return closestVehicle, closestDistance
|
2025-03-06 23:49:07 +00:00
|
|
|
end
|