Files
jim_bridge/shared/vehicles.lua

298 lines
12 KiB
Lua

--[[
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.
]]
-- 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.
---
--- @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.
---
--- @usage
--- ```lua
--- local info = searchCar(vehicleEntity)
--- print(info.name, info.price, info.class.name, info.class.index)
--- ```
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
-------------------------------------------------------------
-- Vehicle Properties Functions
-------------------------------------------------------------
--- Retrieves the properties of a given vehicle using the active framework.
---
--- @param vehicle number The entity ID of the vehicle.
--- @return table|nil table A table containing the vehicle's properties or nil if invalid.
---
--- @usage
--- ```lua
--- local props = getVehicleProperties(vehicleEntity)
--- if props then
--- -- Use vehicle properties
--- end
--- ```
function getVehicleProperties(vehicle)
if not vehicle then return nil end
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
--- 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.
---
--- @param vehicle number The entity ID of the vehicle.
--- @param props table The new properties to apply.
---
--- @usage
--- ```lua
--- setVehicleProperties(vehicleEntity, newProperties)
--- ```
function setVehicleProperties(vehicle, props)
if checkDifferences(vehicle, props) then
if not DoesEntityExist(vehicle) then
print("Unable to set vehicle properties for '"..vehicle.."' (entity does not exist)")
end
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.
--- Compares properties using JSON encoding for deep comparison and logs differences.
---
--- @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.
---
--- @usage
--- ```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")
local differencesFound = false
for k in pairs(oldProps) do
if json.encode(oldProps[k]) ~= json.encode(newProps[k]) then
differencesFound = true
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
return differencesFound
end
-------------------------------------------------------------
-- Vehicle Properties Synchronization
-------------------------------------------------------------
--- Event handler for setting vehicle properties received from the server.
--- Listens for the `ox:setVehicleProperties` event and applies the properties.
---
--- @event `getScript()..ox:setVehicleProperties`
--- @param netId number The network ID of the vehicle.
--- @param props table The new vehicle properties.
RegisterNetEvent(getScript()..":ox:setVehicleProperties", function(netId, props)
local vehicle = NetworkGetEntityFromNetworkId(netId)
local value = props
Entity(vehicle).state[getScript()..':setVehicleProperties'] = value
end)
--- Handles state bag changes for updating vehicle properties.
--- When the state bag changes, the new properties are applied to the vehicle.
---
--- @param bagName string The state bag's name.
--- @param key string The key that changed.
--- @param value table The new state value.
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)
-------------------------------------------------------------
-- Vehicle Control Functions
-------------------------------------------------------------
--- 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
-- Request network control if not already controlled.
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
debugPrint("^6Bridge^7: ^3pushVehicle^7: ^2Network now has control of the entity^7.")
end
end
-- Set as mission entity if not already set.
if not IsEntityAMissionEntity(entity) then
debugPrint("^6Bridge^7: ^3pushVehicle^7: ^2Setting vehicle as a ^7'^2mission^7' ^2entity^7.")
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
--- 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)
--- ```
function getClosestVehicle(coords, src)
local ped, vehicles, closestDistance, closestVehicle
if src then
ped = GetPlayerPed(src)
vehicles = GetAllVehicles()
else
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)
end
else
coords = GetEntityCoords(ped)
end
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]
end
end
return closestVehicle, closestDistance
end