344 lines
12 KiB
Lua
344 lines
12 KiB
Lua
|
local QBCore = exports['qb-core']:GetCoreObject()
|
||
|
local PlayerData = QBCore.Functions.GetPlayerData()
|
||
|
local isLoggedIn = LocalPlayer.state.isLoggedIn
|
||
|
local playerPed = PlayerPedId()
|
||
|
local playerCoords = GetEntityCoords(playerPed)
|
||
|
local closestCityhall = nil
|
||
|
local closestDrivingSchool = nil
|
||
|
local inCityhallPage = false
|
||
|
local inRangeCityhall = false
|
||
|
local inRangeDrivingSchool = false
|
||
|
local pedsSpawned = false
|
||
|
local blips = {}
|
||
|
|
||
|
-- Functions
|
||
|
|
||
|
local function getClosestHall()
|
||
|
local distance = #(playerCoords - Config.Cityhalls[1].coords)
|
||
|
local closest = 1
|
||
|
for i = 1, #Config.Cityhalls do
|
||
|
local hall = Config.Cityhalls[i]
|
||
|
local dist = #(playerCoords - hall.coords)
|
||
|
if dist < distance then
|
||
|
distance = dist
|
||
|
closest = i
|
||
|
end
|
||
|
end
|
||
|
return closest
|
||
|
end
|
||
|
|
||
|
local function getClosestSchool()
|
||
|
local distance = #(playerCoords - Config.DrivingSchools[1].coords)
|
||
|
local closest = 1
|
||
|
for i = 1, #Config.DrivingSchools do
|
||
|
local school = Config.DrivingSchools[i]
|
||
|
local dist = #(playerCoords - school.coords)
|
||
|
if dist < distance then
|
||
|
distance = dist
|
||
|
closest = i
|
||
|
end
|
||
|
end
|
||
|
return closest
|
||
|
end
|
||
|
|
||
|
local function getJobs()
|
||
|
QBCore.Functions.TriggerCallback('qb-cityhall:server:receiveJobs', function(result)
|
||
|
SendNUIMessage({
|
||
|
action = 'setJobs',
|
||
|
jobs = result
|
||
|
})
|
||
|
end)
|
||
|
end
|
||
|
|
||
|
local function setCityhallPageState(bool, message)
|
||
|
getJobs()
|
||
|
if message then
|
||
|
local action = bool and "open" or "close"
|
||
|
SendNUIMessage({
|
||
|
action = action
|
||
|
})
|
||
|
end
|
||
|
SetNuiFocus(bool, bool)
|
||
|
inCityhallPage = bool
|
||
|
if not Config.UseTarget or bool then return end
|
||
|
inRangeCityhall = false
|
||
|
end
|
||
|
|
||
|
local function createBlip(options)
|
||
|
if not options.coords or type(options.coords) ~= 'table' and type(options.coords) ~= 'vector3' then return error(('createBlip() expected coords in a vector3 or table but received %s'):format(options.coords)) end
|
||
|
local blip = AddBlipForCoord(options.coords.x, options.coords.y, options.coords.z)
|
||
|
SetBlipSprite(blip, options.sprite or 1)
|
||
|
SetBlipDisplay(blip, options.display or 4)
|
||
|
SetBlipScale(blip, options.scale or 1.0)
|
||
|
SetBlipColour(blip, options.colour or 1)
|
||
|
SetBlipAsShortRange(blip, options.shortRange or false)
|
||
|
BeginTextCommandSetBlipName("STRING")
|
||
|
AddTextComponentSubstringPlayerName(options.title or 'No Title Given')
|
||
|
EndTextCommandSetBlipName(blip)
|
||
|
return blip
|
||
|
end
|
||
|
|
||
|
local function deleteBlips()
|
||
|
if not next(blips) then return end
|
||
|
for i = 1, #blips do
|
||
|
local blip = blips[i]
|
||
|
if DoesBlipExist(blip) then
|
||
|
RemoveBlip(blip)
|
||
|
end
|
||
|
end
|
||
|
blips = {}
|
||
|
end
|
||
|
|
||
|
local function initBlips()
|
||
|
for i = 1, #Config.Cityhalls do
|
||
|
local hall = Config.Cityhalls[i]
|
||
|
if hall.showBlip then
|
||
|
blips[#blips + 1] = createBlip({
|
||
|
coords = hall.coords,
|
||
|
sprite = hall.blipData.sprite,
|
||
|
display = hall.blipData.display,
|
||
|
scale = hall.blipData.scale,
|
||
|
colour = hall.blipData.colour,
|
||
|
shortRange = true,
|
||
|
title = hall.blipData.title
|
||
|
})
|
||
|
end
|
||
|
end
|
||
|
for i = 1, #Config.DrivingSchools do
|
||
|
local school = Config.DrivingSchools[i]
|
||
|
if school.showBlip then
|
||
|
blips[#blips + 1] = createBlip({
|
||
|
coords = school.coords,
|
||
|
sprite = school.blipData.sprite,
|
||
|
display = school.blipData.display,
|
||
|
scale = school.blipData.scale,
|
||
|
colour = school.blipData.colour,
|
||
|
shortRange = true,
|
||
|
title = school.blipData.title
|
||
|
})
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local function spawnPeds()
|
||
|
if not Config.Peds or not next(Config.Peds) or pedsSpawned then return end
|
||
|
for i = 1, #Config.Peds do
|
||
|
local current = Config.Peds[i]
|
||
|
current.model = type(current.model) == 'string' and joaat(current.model) or current.model
|
||
|
RequestModel(current.model)
|
||
|
while not HasModelLoaded(current.model) do
|
||
|
Wait(0)
|
||
|
end
|
||
|
local ped = CreatePed(0, current.model, current.coords.x, current.coords.y, current.coords.z, current.coords.w, false, false)
|
||
|
FreezeEntityPosition(ped, true)
|
||
|
SetEntityInvincible(ped, true)
|
||
|
SetBlockingOfNonTemporaryEvents(ped, true)
|
||
|
TaskStartScenarioInPlace(ped, current.scenario, true, true)
|
||
|
current.pedHandle = ped
|
||
|
if Config.UseTarget then
|
||
|
local opts = nil
|
||
|
if current.drivingschool then
|
||
|
opts = {
|
||
|
label = 'Tag køretimer',
|
||
|
icon = 'fa-solid fa-car-side',
|
||
|
action = function()
|
||
|
TriggerServerEvent('qb-cityhall:server:sendDriverTest', Config.DrivingSchools[closestDrivingSchool].instructors)
|
||
|
end
|
||
|
}
|
||
|
elseif current.cityhall then
|
||
|
opts = {
|
||
|
label = 'Åben borgerservice',
|
||
|
icon = 'fa-solid fa-city',
|
||
|
action = function()
|
||
|
inRangeCityhall = true
|
||
|
setCityhallPageState(true, true)
|
||
|
end
|
||
|
}
|
||
|
end
|
||
|
if opts then
|
||
|
exports['qb-target']:AddTargetEntity(ped, {
|
||
|
options = { opts },
|
||
|
distance = 2.0
|
||
|
})
|
||
|
end
|
||
|
else
|
||
|
local options = current.zoneOptions
|
||
|
if options then
|
||
|
local zone = BoxZone:Create(current.coords.xyz, options.length, options.width, {
|
||
|
name = "zone_cityhall_" .. ped,
|
||
|
heading = current.coords.w,
|
||
|
debugPoly = false,
|
||
|
minZ = current.coords.z - 3.0,
|
||
|
maxZ = current.coords.z + 2.0
|
||
|
})
|
||
|
zone:onPlayerInOut(function(inside)
|
||
|
if isLoggedIn and closestCityhall and closestDrivingSchool then
|
||
|
if inside then
|
||
|
if current.drivingschool then
|
||
|
inRangeDrivingSchool = true
|
||
|
exports['qb-core']:DrawText('[E] Tag køretimer', 'top')
|
||
|
elseif current.cityhall then
|
||
|
inRangeCityhall = true
|
||
|
exports['qb-core']:DrawText('[E] Åben borgerservice', 'top')
|
||
|
end
|
||
|
else
|
||
|
exports['qb-core']:HideText()
|
||
|
if current.drivingschool then
|
||
|
inRangeDrivingSchool = false
|
||
|
elseif current.cityhall then
|
||
|
inRangeCityhall = false
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
pedsSpawned = true
|
||
|
end
|
||
|
|
||
|
local function deletePeds()
|
||
|
if not Config.Peds or not next(Config.Peds) or not pedsSpawned then return end
|
||
|
for i = 1, #Config.Peds do
|
||
|
local current = Config.Peds[i]
|
||
|
if current.pedHandle then
|
||
|
DeletePed(current.pedHandle)
|
||
|
end
|
||
|
end
|
||
|
pedsSpawned = false
|
||
|
end
|
||
|
|
||
|
|
||
|
-- Events
|
||
|
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
|
||
|
PlayerData = QBCore.Functions.GetPlayerData()
|
||
|
isLoggedIn = true
|
||
|
spawnPeds()
|
||
|
end)
|
||
|
|
||
|
RegisterNetEvent('QBCore:Client:OnPlayerUnload', function()
|
||
|
PlayerData = {}
|
||
|
isLoggedIn = false
|
||
|
deletePeds()
|
||
|
end)
|
||
|
|
||
|
RegisterNetEvent('QBCore:Player:SetPlayerData', function(val)
|
||
|
PlayerData = val
|
||
|
end)
|
||
|
|
||
|
RegisterNetEvent('qb-cityhall:Client:AddCityJob', function()
|
||
|
getJobs()
|
||
|
end)
|
||
|
|
||
|
RegisterNetEvent('qb-cityhall:client:getIds', function()
|
||
|
TriggerServerEvent('qb-cityhall:server:getIDs')
|
||
|
end)
|
||
|
|
||
|
RegisterNetEvent('qb-cityhall:client:sendDriverEmail', function(charinfo)
|
||
|
SetTimeout(math.random(2500, 4000), function()
|
||
|
local gender = Lang:t('email.mr')
|
||
|
if PlayerData.charinfo.gender == 1 then
|
||
|
gender = Lang:t('email.mrs')
|
||
|
end
|
||
|
TriggerServerEvent('qb-phone:server:sendNewMail', {
|
||
|
sender = Lang:t('email.sender'),
|
||
|
subject = Lang:t('email.subject'),
|
||
|
message = Lang:t('email.message', { gender = gender, lastname = charinfo.lastname, firstname = charinfo.firstname, phone = charinfo.phone }),
|
||
|
button = {}
|
||
|
})
|
||
|
end)
|
||
|
end)
|
||
|
|
||
|
AddEventHandler('onResourceStop', function(resource)
|
||
|
if resource ~= GetCurrentResourceName() then return end
|
||
|
deleteBlips()
|
||
|
deletePeds()
|
||
|
end)
|
||
|
|
||
|
-- NUI Callbacks
|
||
|
|
||
|
RegisterNUICallback('close', function(_, cb)
|
||
|
setCityhallPageState(false, false)
|
||
|
if not Config.UseTarget and inRangeCityhall then exports['qb-core']:DrawText('[E] Åben borgerservice', 'top') end
|
||
|
cb('ok')
|
||
|
end)
|
||
|
|
||
|
RegisterNUICallback('requestId', function(id, cb)
|
||
|
local license = Config.Cityhalls[closestCityhall].licenses[id.type]
|
||
|
if inRangeCityhall and license and id.cost == license.cost then
|
||
|
TriggerServerEvent('qb-cityhall:server:requestId', id.type, closestCityhall)
|
||
|
QBCore.Functions.Notify(('Du har modtaget %s for $%s'):format(license.label, id.cost), 'success', 3500)
|
||
|
else
|
||
|
QBCore.Functions.Notify(Lang:t('error.not_in_range'), 'error')
|
||
|
end
|
||
|
cb('ok')
|
||
|
end)
|
||
|
|
||
|
RegisterNUICallback('requestLicenses', function(_, cb)
|
||
|
local licensesMeta = PlayerData.metadata["licences"]
|
||
|
local availableLicenses = Config.Cityhalls[closestCityhall].licenses
|
||
|
for license, data in pairs(availableLicenses) do
|
||
|
if data.metadata and not licensesMeta[data.metadata] then
|
||
|
availableLicenses[license] = nil
|
||
|
end
|
||
|
end
|
||
|
cb(availableLicenses)
|
||
|
end)
|
||
|
|
||
|
RegisterNUICallback('applyJob', function(job, cb)
|
||
|
if inRangeCityhall then
|
||
|
TriggerServerEvent('qb-cityhall:server:ApplyJob', job, Config.Cityhalls[closestCityhall].coords)
|
||
|
else
|
||
|
QBCore.Functions.Notify(Lang:t('error.not_in_range'), 'error')
|
||
|
end
|
||
|
cb('ok')
|
||
|
end)
|
||
|
|
||
|
-- Threads
|
||
|
|
||
|
CreateThread(function()
|
||
|
while true do
|
||
|
if isLoggedIn then
|
||
|
playerPed = PlayerPedId()
|
||
|
playerCoords = GetEntityCoords(playerPed)
|
||
|
closestCityhall = getClosestHall()
|
||
|
closestDrivingSchool = getClosestSchool()
|
||
|
end
|
||
|
Wait(1000)
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
CreateThread(function()
|
||
|
initBlips()
|
||
|
spawnPeds()
|
||
|
if not Config.UseTarget then
|
||
|
while true do
|
||
|
local sleep = 1000
|
||
|
if isLoggedIn and closestCityhall and closestDrivingSchool then
|
||
|
if inRangeCityhall then
|
||
|
if not inCityhallPage then
|
||
|
sleep = 0
|
||
|
if IsControlJustPressed(0, 38) then
|
||
|
setCityhallPageState(true, true)
|
||
|
exports['qb-core']:KeyPressed()
|
||
|
Wait(500)
|
||
|
exports['qb-core']:HideText()
|
||
|
sleep = 1000
|
||
|
end
|
||
|
end
|
||
|
elseif inRangeDrivingSchool then
|
||
|
sleep = 0
|
||
|
if IsControlJustPressed(0, 38) then
|
||
|
TriggerServerEvent('qb-cityhall:server:sendDriverTest', Config.DrivingSchools[closestDrivingSchool].instructors)
|
||
|
sleep = 5000
|
||
|
exports['qb-core']:KeyPressed()
|
||
|
Wait(500)
|
||
|
exports['qb-core']:HideText()
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
Wait(sleep)
|
||
|
end
|
||
|
end
|
||
|
end)
|