local spawnedVehicles = {} function OpenVehicleSpawnerMenu(type, station, part, partNum) local playerCoords = GetEntityCoords(PlayerPedId()) ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'vehicle', { title = _U('garage_title'), align = 'top-left', elements = { {label = _U('garage_storeditem'), action = 'garage'}, {label = _U('garage_storeitem'), action = 'store_garage'}, {label = _U('garage_buyitem'), action = 'buy_vehicle'} }}, function(data, menu) if data.current.action == 'buy_vehicle' then local shopElements = {} local shopCoords = Config.PoliceStations[station][part][partNum].InsideShop local authorizedVehicles = Config.AuthorizedVehicles[type][ESX.PlayerData.job.grade_name] if authorizedVehicles then if #authorizedVehicles > 0 then for k,vehicle in ipairs(authorizedVehicles) do if IsModelInCdimage(vehicle.model) then local vehicleLabel = GetLabelText(GetDisplayNameFromVehicleModel(vehicle.model)) table.insert(shopElements, { label = ('%s - %s'):format(vehicleLabel, _U('shop_item', ESX.Math.GroupDigits(vehicle.price))), name = vehicleLabel, model = vehicle.model, price = vehicle.price, props = vehicle.props, type = type }) end end if #shopElements > 0 then OpenShopMenu(shopElements, playerCoords, shopCoords) else ESX.ShowNotification(_U('garage_notauthorized')) end else ESX.ShowNotification(_U('garage_notauthorized')) end else ESX.ShowNotification(_U('garage_notauthorized')) end elseif data.current.action == 'garage' then local garage = {} ESX.TriggerServerCallback('esx_vehicleshop:retrieveJobVehicles', function(jobVehicles) if #jobVehicles > 0 then local allVehicleProps = {} for k,v in ipairs(jobVehicles) do local props = json.decode(v.vehicle) if IsModelInCdimage(props.model) then local vehicleName = GetLabelText(GetDisplayNameFromVehicleModel(props.model)) local label = ('%s - %s: '):format(vehicleName, props.plate) if v.stored then label = label .. ('%s'):format(_U('garage_stored')) else label = label .. ('%s'):format(_U('garage_notstored')) end table.insert(garage, { label = label, stored = v.stored, model = props.model, plate = props.plate }) allVehicleProps[props.plate] = props end end if #garage > 0 then ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'vehicle_garage', { title = _U('garage_title'), align = 'top-left', elements = garage }, function(data2, menu2) if data2.current.stored then local foundSpawn, spawnPoint = GetAvailableVehicleSpawnPoint(station, part, partNum) if foundSpawn then menu2.close() ESX.Game.SpawnVehicle(data2.current.model, spawnPoint.coords, spawnPoint.heading, function(vehicle) local vehicleProps = allVehicleProps[data2.current.plate] ESX.Game.SetVehicleProperties(vehicle, vehicleProps) TriggerServerEvent('esx_vehicleshop:setJobVehicleState', data2.current.plate, false) ESX.ShowNotification(_U('garage_released')) end) end else ESX.ShowNotification(_U('garage_notavailable')) end end, function(data2, menu2) menu2.close() end) else ESX.ShowNotification(_U('garage_empty')) end else ESX.ShowNotification(_U('garage_empty')) end end, type) elseif data.current.action == 'store_garage' then StoreNearbyVehicle(playerCoords) end end, function(data, menu) menu.close() end) end function StoreNearbyVehicle(playerCoords) local vehicles, vehiclePlates = ESX.Game.GetVehiclesInArea(playerCoords, 30.0), {} if #vehicles > 0 then for k,v in ipairs(vehicles) do -- Make sure the vehicle we're saving is empty, or else it wont be deleted if GetVehicleNumberOfPassengers(v) == 0 and IsVehicleSeatFree(v, -1) then table.insert(vehiclePlates, { vehicle = v, plate = ESX.Math.Trim(GetVehicleNumberPlateText(v)) }) end end else ESX.ShowNotification(_U('garage_store_nearby')) return end ESX.TriggerServerCallback('esx_policejob:storeNearbyVehicle', function(storeSuccess, foundNum) if storeSuccess then local vehicleId = vehiclePlates[foundNum] local attempts = 0 ESX.Game.DeleteVehicle(vehicleId.vehicle) IsBusy = true Citizen.CreateThread(function() BeginTextCommandBusyspinnerOn('STRING') AddTextComponentSubstringPlayerName(_U('garage_storing')) EndTextCommandBusyspinnerOn(4) while IsBusy do Citizen.Wait(100) end BusyspinnerOff() end) -- Workaround for vehicle not deleting when other players are near it. while DoesEntityExist(vehicleId.vehicle) do Citizen.Wait(500) attempts = attempts + 1 -- Give up if attempts > 30 then break end vehicles = ESX.Game.GetVehiclesInArea(playerCoords, 30.0) if #vehicles > 0 then for k,v in ipairs(vehicles) do if ESX.Math.Trim(GetVehicleNumberPlateText(v)) == vehicleId.plate then ESX.Game.DeleteVehicle(v) break end end end end IsBusy = false ESX.ShowNotification(_U('garage_has_stored')) else ESX.ShowNotification(_U('garage_has_notstored')) end end, vehiclePlates) end function GetAvailableVehicleSpawnPoint(station, part, partNum) local spawnPoints = Config.PoliceStations[station][part][partNum].SpawnPoints local found, foundSpawnPoint = false, nil for i=1, #spawnPoints, 1 do if ESX.Game.IsSpawnPointClear(spawnPoints[i].coords, spawnPoints[i].radius) then found, foundSpawnPoint = true, spawnPoints[i] break end end if found then return true, foundSpawnPoint else ESX.ShowNotification(_U('vehicle_blocked')) return false end end function OpenShopMenu(elements, restoreCoords, shopCoords) local playerPed = PlayerPedId() isInShopMenu = true ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'vehicle_shop', { title = _U('vehicleshop_title'), align = 'top-left', elements = elements }, function(data, menu) ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'vehicle_shop_confirm', { title = _U('vehicleshop_confirm', data.current.name, data.current.price), align = 'top-left', elements = { {label = _U('confirm_no'), value = 'no'}, {label = _U('confirm_yes'), value = 'yes'} }}, function(data2, menu2) if data2.current.value == 'yes' then local newPlate = exports['esx_vehicleshop']:GeneratePlate() local vehicle = GetVehiclePedIsIn(playerPed, false) local props = ESX.Game.GetVehicleProperties(vehicle) props.plate = newPlate ESX.TriggerServerCallback('esx_policejob:buyJobVehicle', function (bought) if bought then ESX.ShowNotification(_U('vehicleshop_bought', data.current.name, ESX.Math.GroupDigits(data.current.price))) isInShopMenu = false ESX.UI.Menu.CloseAll() DeleteSpawnedVehicles() FreezeEntityPosition(playerPed, false) SetEntityVisible(playerPed, true) ESX.Game.Teleport(playerPed, restoreCoords) else ESX.ShowNotification(_U('vehicleshop_money')) menu2.close() end end, props, data.current.type) else menu2.close() end end, function(data2, menu2) menu2.close() end) end, function(data, menu) isInShopMenu = false ESX.UI.Menu.CloseAll() DeleteSpawnedVehicles() FreezeEntityPosition(playerPed, false) SetEntityVisible(playerPed, true) ESX.Game.Teleport(playerPed, restoreCoords) end, function(data, menu) DeleteSpawnedVehicles() WaitForVehicleToLoad(data.current.model) ESX.Game.SpawnLocalVehicle(data.current.model, shopCoords, 0.0, function(vehicle) table.insert(spawnedVehicles, vehicle) TaskWarpPedIntoVehicle(playerPed, vehicle, -1) FreezeEntityPosition(vehicle, true) SetModelAsNoLongerNeeded(data.current.model) if data.current.props then ESX.Game.SetVehicleProperties(vehicle, data.current.props) end end) end) WaitForVehicleToLoad(elements[1].model) ESX.Game.SpawnLocalVehicle(elements[1].model, shopCoords, 0.0, function(vehicle) table.insert(spawnedVehicles, vehicle) TaskWarpPedIntoVehicle(playerPed, vehicle, -1) FreezeEntityPosition(vehicle, true) SetModelAsNoLongerNeeded(elements[1].model) if elements[1].props then ESX.Game.SetVehicleProperties(vehicle, elements[1].props) end end) end Citizen.CreateThread(function() while true do Citizen.Wait(0) if isInShopMenu then DisableControlAction(0, 75, true) -- Disable exit vehicle DisableControlAction(27, 75, true) -- Disable exit vehicle else Citizen.Wait(500) end end end) function DeleteSpawnedVehicles() while #spawnedVehicles > 0 do local vehicle = spawnedVehicles[1] ESX.Game.DeleteVehicle(vehicle) table.remove(spawnedVehicles, 1) end end function WaitForVehicleToLoad(modelHash) modelHash = (type(modelHash) == 'number' and modelHash or GetHashKey(modelHash)) if not HasModelLoaded(modelHash) then RequestModel(modelHash) BeginTextCommandBusyspinnerOn('STRING') AddTextComponentSubstringPlayerName(_U('vehicleshop_awaiting_model')) EndTextCommandBusyspinnerOn(4) while not HasModelLoaded(modelHash) do Citizen.Wait(0) DisableAllControlActions(0) end BusyspinnerOff() end end