--[[ 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 "Open Wheel", --23 } if Vehicles then for k, v in pairs(Vehicles) do if tonumber(v.hash) == model or joaat(v.hash) == model or joaat(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 propertyFunc = { { framework = OXLibExport, func = function(vehicle) return lib.getVehicleProperties(vehicle) end, }, { framework = QBExport, func = function(vehicle) return Core.Functions.GetVehicleProperties(vehicle) end, }, } for i = 1, #propertyFunc do local prop = propertyFunc[i] if isStarted(prop.framework) then local properties = prop.func(vehicle) debugPrint("^6Bridge^7: ^2Getting Vehicle Properties ^7[^6"..prop.framework.."^7] - [^3"..vehicle.."^7] - [^3"..GetEntityModel(vehicle).."^7/^3"..properties.model.."^7] - [^3"..properties.plate.."^7]") return properties end end return nil 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.."' (^1entity does not exist^7)") 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]") elseif isStarted(OXLibExport) then lib.setVehicleProperties(vehicle, props, false) debugPrint("^6Bridge^7: ^2Setting Vehicle Properties ^7[^6"..OXLibExport.."^7] - [^3"..vehicle.."^7] - [^3"..GetEntityModel(vehicle).."^7/^3"..props.model.."^7] - [^3"..props.plate.."^7]") 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 = true 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 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: ^3pushEnt^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: ^3pushEnt^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: ^3pushEnt^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: ^3pushEnt^7: ^2Vehicle is a ^7'^2mission^7'^2 entity^7.") end end end end -- add entitty named version function pushEnt(...) pushVehicle(...) 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 closestVehicle The closest vehicle entity and its distance. --- @return number closestDistance The distance of the closest vehicle. --- --- @usage --- ```lua --- local closestVeh, distance = getClosestVehicle({ x = 100, y = 200, z = 30 }, src) --- ``` function getClosestVehicle(coords, src) local ped, vehicles, closestDistance, closestVehicle if src then -- if checking server side cache src's ped and use server native ped = GetPlayerPed(src) vehicles = GetAllVehicles() else -- if checking client side cache local ped and use client native 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.xyz) if closestDistance == -1 or distance < closestDistance then closestDistance = distance closestVehicle = vehicles[i] end end return closestVehicle, closestDistance end --- Checks whether a vehicle is owned or not using the plate as reference --- --- @param plate string The plate of the vehicle to check --- @return boolean whether vehicle is owned --- --- @usage --- ```lua --- local plate = "ABCD1234" --- local isVehicleOwned = isVehicleOwned(plate) --- ``` local vehiclesOwned = {} function isVehicleOwned(plate) -- If already checked, cache it to reduce database calls if vehiclesOwned[plate] == true then return true else -- Find frameworks vehicle table and search sql for if vehicle plate is owned local sqlTable = "player_vehicles" local vehDatabase = { { framework = ESXExport, sqlTable = "owned_vehicles" }, { framework = QBExport, sqlTable = "player_vehicles" }, { framework = QBXExport, sqlTable = "player_vehicles" }, { framework = OXCoreExport, sqlTable = "owned_vehicles" }, } for i = 1, #vehDatabase do local framework = vehDatabase[i] if isStarted(framework.framework) then sqlTable = framework.sqlTable break end end local result = MySQL.query.await("SELECT 1 from "..sqlTable.." WHERE plate = ?", { plate }) if json.encode(result) ~= "[]" then vehiclesOwned[plate] = true -- Cache ownership for later checks return true else return false end end end