forked from Simnation/Main
859 lines
38 KiB
Lua
859 lines
38 KiB
Lua
local QBCore = exports['qb-core']:GetCoreObject()
|
|
local Countdown = 10
|
|
local ToFarCountdown = 10
|
|
local FinishedUITimeout = false
|
|
|
|
local RaceData = {
|
|
InCreator = false,
|
|
InRace = false,
|
|
ClosestCheckpoint = 0,
|
|
}
|
|
|
|
local CreatorData = {
|
|
RaceName = nil,
|
|
Checkpoints = {},
|
|
TireDistance = 3.0,
|
|
ConfirmDelete = false,
|
|
}
|
|
|
|
local CurrentRaceData = {
|
|
RaceId = nil,
|
|
RaceName = nil,
|
|
Checkpoints = {},
|
|
Started = false,
|
|
CurrentCheckpoint = nil,
|
|
TotalLaps = 0,
|
|
Lap = 0,
|
|
}
|
|
|
|
-- Handlers
|
|
|
|
AddEventHandler('onResourceStop', function(resource)
|
|
if resource == GetCurrentResourceName() then
|
|
for k, _ in pairs(CreatorData.Checkpoints) do
|
|
if CreatorData.Checkpoints[k].pileleft ~= nil then
|
|
local coords = CreatorData.Checkpoints[k].offset.right
|
|
local Obj = GetClosestObjectOfType(coords.x, coords.y, coords.z, 5.0, `prop_offroad_tyres02`, 0, 0, 0)
|
|
DeleteObject(Obj)
|
|
ClearAreaOfObjects(coords.x, coords.y, coords.z, 50.0, 0)
|
|
CreatorData.Checkpoints[k].pileright = nil
|
|
end
|
|
if CreatorData.Checkpoints[k].pileright ~= nil then
|
|
local coords = CreatorData.Checkpoints[k].offset.right
|
|
local Obj = GetClosestObjectOfType(coords.x, coords.y, coords.z, 5.0, `prop_offroad_tyres02`, 0, 0, 0)
|
|
DeleteObject(Obj)
|
|
ClearAreaOfObjects(coords.x, coords.y, coords.z, 50.0, 0)
|
|
CreatorData.Checkpoints[k].pileright = nil
|
|
end
|
|
end
|
|
|
|
for k, _ in pairs(CurrentRaceData.Checkpoints) do
|
|
if CurrentRaceData.Checkpoints[k] ~= nil then
|
|
if CurrentRaceData.Checkpoints[k].pileleft ~= nil then
|
|
local coords = CurrentRaceData.Checkpoints[k].offset.right
|
|
local Obj = GetClosestObjectOfType(coords.x, coords.y, coords.z, 5.0, `prop_offroad_tyres02`, 0, 0, 0)
|
|
DeleteObject(Obj)
|
|
ClearAreaOfObjects(coords.x, coords.y, coords.z, 50.0, 0)
|
|
CurrentRaceData.Checkpoints[k].pileright = nil
|
|
end
|
|
if CurrentRaceData.Checkpoints[k].pileright ~= nil then
|
|
local coords = CurrentRaceData.Checkpoints[k].offset.right
|
|
local Obj = GetClosestObjectOfType(coords.x, coords.y, coords.z, 5.0, `prop_offroad_tyres02`, 0, 0, 0)
|
|
DeleteObject(Obj)
|
|
ClearAreaOfObjects(coords.x, coords.y, coords.z, 50.0, 0)
|
|
CurrentRaceData.Checkpoints[k].pileright = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
-- Functions
|
|
|
|
local function DrawText3Ds(x, y, z, text)
|
|
SetTextScale(0.35, 0.35)
|
|
SetTextFont(4)
|
|
SetTextProportional(1)
|
|
SetTextColour(255, 255, 255, 215)
|
|
BeginTextCommandDisplayText("STRING")
|
|
SetTextCentre(true)
|
|
AddTextComponentSubstringPlayerName(text)
|
|
SetDrawOrigin(x,y,z, 0)
|
|
EndTextCommandDisplayText(0.0, 0.0)
|
|
local factor = (string.len(text)) / 370
|
|
DrawRect(0.0, 0.0+0.0125, 0.017+ factor, 0.03, 0, 0, 0, 75)
|
|
ClearDrawOrigin()
|
|
end
|
|
|
|
local function GetClosestCheckpoint()
|
|
local pos = GetEntityCoords(PlayerPedId(), true)
|
|
local current = nil
|
|
local dist = nil
|
|
for id, _ in pairs(CreatorData.Checkpoints) do
|
|
if current ~= nil then
|
|
if #(pos - vector3(CreatorData.Checkpoints[id].coords.x, CreatorData.Checkpoints[id].coords.y, CreatorData.Checkpoints[id].coords.z)) < dist then
|
|
current = id
|
|
dist = #(pos - vector3(CreatorData.Checkpoints[id].coords.x, CreatorData.Checkpoints[id].coords.y, CreatorData.Checkpoints[id].coords.z))
|
|
end
|
|
else
|
|
dist = #(pos - vector3(CreatorData.Checkpoints[id].coords.x, CreatorData.Checkpoints[id].coords.y, CreatorData.Checkpoints[id].coords.z))
|
|
current = id
|
|
end
|
|
end
|
|
RaceData.ClosestCheckpoint = current
|
|
end
|
|
|
|
local function CreatorUI()
|
|
CreateThread(function()
|
|
while true do
|
|
if RaceData.InCreator then
|
|
SendNUIMessage({
|
|
action = "Update",
|
|
type = "creator",
|
|
data = CreatorData,
|
|
racedata = RaceData,
|
|
active = true,
|
|
})
|
|
else
|
|
SendNUIMessage({
|
|
action = "Update",
|
|
type = "creator",
|
|
data = CreatorData,
|
|
racedata = RaceData,
|
|
active = false,
|
|
})
|
|
break
|
|
end
|
|
Wait(200)
|
|
end
|
|
end)
|
|
end
|
|
|
|
local function DeleteCheckpoint()
|
|
local NewCheckpoints = {}
|
|
if RaceData.ClosestCheckpoint ~= 0 then
|
|
if CreatorData.Checkpoints[RaceData.ClosestCheckpoint] ~= nil then
|
|
if CreatorData.Checkpoints[RaceData.ClosestCheckpoint].blip ~= nil then
|
|
RemoveBlip(CreatorData.Checkpoints[RaceData.ClosestCheckpoint].blip)
|
|
CreatorData.Checkpoints[RaceData.ClosestCheckpoint].blip = nil
|
|
end
|
|
if CreatorData.Checkpoints[RaceData.ClosestCheckpoint].pileleft ~= nil then
|
|
local coords = CreatorData.Checkpoints[RaceData.ClosestCheckpoint].offset.left
|
|
local Obj = GetClosestObjectOfType(coords.x, coords.y, coords.z, 5.0, `prop_offroad_tyres02`, 0, 0, 0)
|
|
DeleteObject(Obj)
|
|
ClearAreaOfObjects(coords.x, coords.y, coords.z, 50.0, 0)
|
|
CreatorData.Checkpoints[RaceData.ClosestCheckpoint].pileleft = nil
|
|
end
|
|
if CreatorData.Checkpoints[RaceData.ClosestCheckpoint].pileright ~= nil then
|
|
local coords = CreatorData.Checkpoints[RaceData.ClosestCheckpoint].offset.right
|
|
local Obj = GetClosestObjectOfType(coords.x, coords.y, coords.z, 5.0, `prop_offroad_tyres02`, 0, 0, 0)
|
|
DeleteObject(Obj)
|
|
ClearAreaOfObjects(coords.x, coords.y, coords.z, 50.0, 0)
|
|
CreatorData.Checkpoints[RaceData.ClosestCheckpoint].pileright = nil
|
|
end
|
|
|
|
for id, data in pairs(CreatorData.Checkpoints) do
|
|
if id ~= RaceData.ClosestCheckpoint then
|
|
NewCheckpoints[#NewCheckpoints+1] = data
|
|
end
|
|
end
|
|
CreatorData.Checkpoints = NewCheckpoints
|
|
else
|
|
QBCore.Functions.Notify('You cant go to fast', 'error')
|
|
end
|
|
else
|
|
QBCore.Functions.Notify('You cant go too fast', 'error')
|
|
end
|
|
end
|
|
|
|
local function SaveRace()
|
|
local RaceDistance = 0
|
|
|
|
for k, v in pairs(CreatorData.Checkpoints) do
|
|
if k + 1 <= #CreatorData.Checkpoints then
|
|
local checkpointdistance = #(vector3(v.coords.x, v.coords.y, v.coords.z) - vector3(CreatorData.Checkpoints[k + 1].coords.x, CreatorData.Checkpoints[k + 1].coords.y, CreatorData.Checkpoints[k + 1].coords.z))
|
|
RaceDistance = RaceDistance + checkpointdistance
|
|
end
|
|
end
|
|
|
|
CreatorData.RaceDistance = RaceDistance
|
|
|
|
TriggerServerEvent('qb-lapraces:server:SaveRace', CreatorData)
|
|
|
|
QBCore.Functions.Notify('Race: '..CreatorData.RaceName..' is saved!', 'success')
|
|
|
|
for id,_ in pairs(CreatorData.Checkpoints) do
|
|
if CreatorData.Checkpoints[id].blip ~= nil then
|
|
RemoveBlip(CreatorData.Checkpoints[id].blip)
|
|
CreatorData.Checkpoints[id].blip = nil
|
|
end
|
|
if CreatorData.Checkpoints[id] ~= nil then
|
|
if CreatorData.Checkpoints[id].pileleft ~= nil then
|
|
local coords = CreatorData.Checkpoints[id].offset.left
|
|
local Obj = GetClosestObjectOfType(coords.x, coords.y, coords.z, 5.0, `prop_offroad_tyres02`, 0, 0, 0)
|
|
DeleteObject(Obj)
|
|
ClearAreaOfObjects(coords.x, coords.y, coords.z, 50.0, 0)
|
|
CreatorData.Checkpoints[id].pileleft = nil
|
|
end
|
|
if CreatorData.Checkpoints[id].pileright ~= nil then
|
|
local coords = CreatorData.Checkpoints[id].offset.right
|
|
local Obj = GetClosestObjectOfType(coords.x, coords.y, coords.z, 5.0, `prop_offroad_tyres02`, 0, 0, 0)
|
|
DeleteObject(Obj)
|
|
ClearAreaOfObjects(coords.x, coords.y, coords.z, 50.0, 0)
|
|
CreatorData.Checkpoints[id].pileright = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
RaceData.InCreator = false
|
|
CreatorData.RaceName = nil
|
|
CreatorData.Checkpoints = {}
|
|
end
|
|
|
|
local function AddCheckpoint()
|
|
local PlayerPed = PlayerPedId()
|
|
local PlayerPos = GetEntityCoords(PlayerPed)
|
|
local PlayerVeh = GetVehiclePedIsIn(PlayerPed)
|
|
local Offset = {
|
|
left = {
|
|
x = (GetOffsetFromEntityInWorldCoords(PlayerVeh, -CreatorData.TireDistance, 0.0, 0.0)).x,
|
|
y = (GetOffsetFromEntityInWorldCoords(PlayerVeh, -CreatorData.TireDistance, 0.0, 0.0)).y,
|
|
z = (GetOffsetFromEntityInWorldCoords(PlayerVeh, -CreatorData.TireDistance, 0.0, 0.0)).z,
|
|
},
|
|
right = {
|
|
x = (GetOffsetFromEntityInWorldCoords(PlayerVeh, CreatorData.TireDistance, 0.0, 0.0)).x,
|
|
y = (GetOffsetFromEntityInWorldCoords(PlayerVeh, CreatorData.TireDistance, 0.0, 0.0)).y,
|
|
z = (GetOffsetFromEntityInWorldCoords(PlayerVeh, CreatorData.TireDistance, 0.0, 0.0)).z,
|
|
}
|
|
}
|
|
|
|
CreatorData.Checkpoints[#CreatorData.Checkpoints+1] = {
|
|
coords = {
|
|
x = PlayerPos.x,
|
|
y = PlayerPos.y,
|
|
z = PlayerPos.z,
|
|
},
|
|
offset = Offset,
|
|
}
|
|
|
|
|
|
for id, CheckpointData in pairs(CreatorData.Checkpoints) do
|
|
if CheckpointData.blip ~= nil then
|
|
RemoveBlip(CheckpointData.blip)
|
|
end
|
|
|
|
CheckpointData.blip = AddBlipForCoord(CheckpointData.coords.x, CheckpointData.coords.y, CheckpointData.coords.z)
|
|
|
|
SetBlipSprite(CheckpointData.blip, 1)
|
|
SetBlipDisplay(CheckpointData.blip, 4)
|
|
SetBlipScale(CheckpointData.blip, 0.8)
|
|
SetBlipAsShortRange(CheckpointData.blip, true)
|
|
SetBlipColour(CheckpointData.blip, 26)
|
|
ShowNumberOnBlip(CheckpointData.blip, id)
|
|
SetBlipShowCone(CheckpointData.blip, false)
|
|
BeginTextCommandSetBlipName("STRING")
|
|
AddTextComponentSubstringPlayerName("Checkpoint: "..id)
|
|
EndTextCommandSetBlipName(CheckpointData.blip)
|
|
end
|
|
end
|
|
|
|
local function CreatorLoop()
|
|
CreateThread(function()
|
|
while RaceData.InCreator do
|
|
local PlayerPed = PlayerPedId()
|
|
local PlayerVeh = GetVehiclePedIsIn(PlayerPed)
|
|
|
|
if PlayerVeh ~= 0 then
|
|
if IsControlJustPressed(0, 161) or IsDisabledControlJustPressed(0, 161) then
|
|
AddCheckpoint()
|
|
end
|
|
|
|
if IsControlJustPressed(0, 162) or IsDisabledControlJustPressed(0, 162) then
|
|
if CreatorData.Checkpoints ~= nil and next(CreatorData.Checkpoints) ~= nil then
|
|
DeleteCheckpoint()
|
|
else
|
|
QBCore.Functions.Notify('You have not placed any checkpoints yet..', 'error')
|
|
end
|
|
end
|
|
|
|
if IsControlJustPressed(0, 311) or IsDisabledControlJustPressed(0, 311) then
|
|
if CreatorData.Checkpoints ~= nil and #CreatorData.Checkpoints >= 2 then
|
|
SaveRace()
|
|
else
|
|
QBCore.Functions.Notify('You must have at least 10 checkpoints', 'error')
|
|
end
|
|
end
|
|
|
|
if IsControlJustPressed(0, 40) or IsDisabledControlJustPressed(0, 40) then
|
|
if CreatorData.TireDistance + 1.0 ~= 16.0 then
|
|
CreatorData.TireDistance = CreatorData.TireDistance + 1.0
|
|
else
|
|
QBCore.Functions.Notify('You can not go higher than 15')
|
|
end
|
|
end
|
|
|
|
if IsControlJustPressed(0, 39) or IsDisabledControlJustPressed(0, 39) then
|
|
if CreatorData.TireDistance - 1.0 ~= 1.0 then
|
|
CreatorData.TireDistance = CreatorData.TireDistance - 1.0
|
|
else
|
|
QBCore.Functions.Notify('You cannot go lower than 2')
|
|
end
|
|
end
|
|
else
|
|
local coords = GetEntityCoords(PlayerPedId())
|
|
DrawText3Ds(coords.x, coords.y, coords.z, 'You must be in a vehicle')
|
|
end
|
|
|
|
if IsControlJustPressed(0, 163) or IsDisabledControlJustPressed(0, 163) then
|
|
if not CreatorData.ConfirmDelete then
|
|
CreatorData.ConfirmDelete = true
|
|
QBCore.Functions.Notify('Press [9] again to confirm', 'error', 5000)
|
|
else
|
|
for _, CheckpointData in pairs(CreatorData.Checkpoints) do
|
|
if CheckpointData.blip ~= nil then
|
|
RemoveBlip(CheckpointData.blip)
|
|
end
|
|
end
|
|
|
|
for id,_ in pairs(CreatorData.Checkpoints) do
|
|
if CreatorData.Checkpoints[id].pileleft ~= nil then
|
|
local coords = CreatorData.Checkpoints[id].offset.left
|
|
local Obj = GetClosestObjectOfType(coords.x, coords.y, coords.z, 8.0, `prop_offroad_tyres02`, 0, 0, 0)
|
|
DeleteObject(Obj)
|
|
ClearAreaOfObjects(coords.x, coords.y, coords.z, 50.0, 0)
|
|
CreatorData.Checkpoints[id].pileleft = nil
|
|
end
|
|
|
|
if CreatorData.Checkpoints[id].pileright ~= nil then
|
|
local coords = CreatorData.Checkpoints[id].offset.right
|
|
local Obj = GetClosestObjectOfType(coords.x, coords.y, coords.z, 8.0, `prop_offroad_tyres02`, 0, 0, 0)
|
|
DeleteObject(Obj)
|
|
ClearAreaOfObjects(coords.x, coords.y, coords.z, 50.0, 0)
|
|
CreatorData.Checkpoints[id].pileright = nil
|
|
end
|
|
end
|
|
|
|
RaceData.InCreator = false
|
|
CreatorData.RaceName = nil
|
|
CreatorData.Checkpoints = {}
|
|
QBCore.Functions.Notify('Race-editor canceled!', 'error')
|
|
CreatorData.ConfirmDelete = false
|
|
end
|
|
end
|
|
Wait(3)
|
|
end
|
|
end)
|
|
end
|
|
|
|
local function RaceUI()
|
|
CreateThread(function()
|
|
while true do
|
|
if CurrentRaceData.Checkpoints ~= nil and next(CurrentRaceData.Checkpoints) ~= nil then
|
|
if CurrentRaceData.Started then
|
|
CurrentRaceData.RaceTime = CurrentRaceData.RaceTime + 1
|
|
CurrentRaceData.TotalTime = CurrentRaceData.TotalTime + 1
|
|
end
|
|
SendNUIMessage({
|
|
action = "Update",
|
|
type = "race",
|
|
data = {
|
|
CurrentCheckpoint = CurrentRaceData.CurrentCheckpoint,
|
|
TotalCheckpoints = #CurrentRaceData.Checkpoints,
|
|
TotalLaps = CurrentRaceData.TotalLaps,
|
|
CurrentLap = CurrentRaceData.Lap,
|
|
RaceStarted = CurrentRaceData.Started,
|
|
RaceName = CurrentRaceData.RaceName,
|
|
Time = CurrentRaceData.RaceTime,
|
|
TotalTime = CurrentRaceData.TotalTime,
|
|
BestLap = CurrentRaceData.BestLap,
|
|
},
|
|
racedata = RaceData,
|
|
active = true,
|
|
})
|
|
else
|
|
if not FinishedUITimeout then
|
|
FinishedUITimeout = true
|
|
SetTimeout(10000, function()
|
|
FinishedUITimeout = false
|
|
SendNUIMessage({
|
|
action = "Update",
|
|
type = "race",
|
|
data = {},
|
|
racedata = RaceData,
|
|
active = false,
|
|
})
|
|
end)
|
|
end
|
|
break
|
|
end
|
|
Wait(12)
|
|
end
|
|
end)
|
|
end
|
|
|
|
local function SetupRace(sRaceData, Laps)
|
|
RaceData.RaceId = sRaceData.RaceId
|
|
CurrentRaceData = {
|
|
RaceId = sRaceData.RaceId,
|
|
Creator = sRaceData.Creator,
|
|
RaceName = sRaceData.RaceName,
|
|
Checkpoints = sRaceData.Checkpoints,
|
|
Started = false,
|
|
CurrentCheckpoint = 1,
|
|
TotalLaps = Laps,
|
|
Lap = 1,
|
|
RaceTime = 0,
|
|
TotalTime = 0,
|
|
BestLap = 0,
|
|
Racers = {}
|
|
}
|
|
|
|
for k, v in pairs(CurrentRaceData.Checkpoints) do
|
|
ClearAreaOfObjects(v.offset.left.x, v.offset.left.y, v.offset.left.z, 50.0, 0)
|
|
CurrentRaceData.Checkpoints[k].pileleft = CreateObject(`prop_offroad_tyres02`, v.offset.left.x, v.offset.left.y, v.offset.left.z, 0, 0, 0)
|
|
PlaceObjectOnGroundProperly(CurrentRaceData.Checkpoints[k].pileleft)
|
|
FreezeEntityPosition(CurrentRaceData.Checkpoints[k].pileleft, 1)
|
|
SetEntityAsMissionEntity(CurrentRaceData.Checkpoints[k].pileleft, 1, 1)
|
|
|
|
ClearAreaOfObjects(v.offset.right.x, v.offset.right.y, v.offset.right.z, 50.0, 0)
|
|
CurrentRaceData.Checkpoints[k].pileright = CreateObject(`prop_offroad_tyres02`, v.offset.right.x, v.offset.right.y, v.offset.right.z, 0, 0, 0)
|
|
PlaceObjectOnGroundProperly(CurrentRaceData.Checkpoints[k].pileright)
|
|
FreezeEntityPosition(CurrentRaceData.Checkpoints[k].pileright, 1)
|
|
SetEntityAsMissionEntity(CurrentRaceData.Checkpoints[k].pileright, 1, 1)
|
|
|
|
CurrentRaceData.Checkpoints[k].blip = AddBlipForCoord(v.coords.x, v.coords.y, v.coords.z)
|
|
SetBlipSprite(CurrentRaceData.Checkpoints[k].blip, 1)
|
|
SetBlipDisplay(CurrentRaceData.Checkpoints[k].blip, 4)
|
|
SetBlipScale(CurrentRaceData.Checkpoints[k].blip, 0.6)
|
|
SetBlipAsShortRange(CurrentRaceData.Checkpoints[k].blip, true)
|
|
SetBlipColour(CurrentRaceData.Checkpoints[k].blip, 26)
|
|
ShowNumberOnBlip(CurrentRaceData.Checkpoints[k].blip, k)
|
|
BeginTextCommandSetBlipName("STRING")
|
|
AddTextComponentSubstringPlayerName("Checkpoint: "..k)
|
|
EndTextCommandSetBlipName(CurrentRaceData.Checkpoints[k].blip)
|
|
end
|
|
|
|
RaceUI()
|
|
end
|
|
|
|
local function showNonLoopParticle(dict, particleName, coords, scale)
|
|
RequestNamedPtfxAsset(dict)
|
|
while not HasNamedPtfxAssetLoaded(dict) do
|
|
Wait(0)
|
|
end
|
|
UseParticleFxAssetNextCall(dict)
|
|
local particleHandle = StartParticleFxLoopedAtCoord(particleName, coords.x, coords.y, coords.z, 0.0, 0.0, 0.0, scale, false, false, false)
|
|
SetParticleFxLoopedColour(particleHandle, 0, 255, 0 ,0)
|
|
return particleHandle
|
|
end
|
|
|
|
local function DoPilePfx()
|
|
if CurrentRaceData.Checkpoints[CurrentRaceData.CurrentCheckpoint] ~= nil then
|
|
local Timeout = 500
|
|
local Size = 2.0
|
|
local left = showNonLoopParticle('core', 'ent_sht_flame', CurrentRaceData.Checkpoints[CurrentRaceData.CurrentCheckpoint].offset.left, Size)
|
|
local right = showNonLoopParticle('core', 'ent_sht_flame', CurrentRaceData.Checkpoints[CurrentRaceData.CurrentCheckpoint].offset.right, Size)
|
|
|
|
SetTimeout(Timeout, function()
|
|
StopParticleFxLooped(left, false)
|
|
StopParticleFxLooped(right, false)
|
|
end)
|
|
end
|
|
end
|
|
|
|
local function SetupPiles()
|
|
for k, v in pairs(CreatorData.Checkpoints) do
|
|
if CreatorData.Checkpoints[k].pileleft == nil then
|
|
ClearAreaOfObjects(v.offset.left.x, v.offset.left.y, v.offset.left.z, 50.0, 0)
|
|
CreatorData.Checkpoints[k].pileleft = CreateObject(`prop_offroad_tyres02`, v.offset.left.x, v.offset.left.y, v.offset.left.z, 0, 0, 0)
|
|
PlaceObjectOnGroundProperly(CreatorData.Checkpoints[k].pileleft)
|
|
FreezeEntityPosition(CreatorData.Checkpoints[k].pileleft, 1)
|
|
SetEntityAsMissionEntity(CreatorData.Checkpoints[k].pileleft, 1, 1)
|
|
end
|
|
|
|
if CreatorData.Checkpoints[k].pileright == nil then
|
|
ClearAreaOfObjects(v.offset.right.x, v.offset.right.y, v.offset.right.z, 50.0, 0)
|
|
CreatorData.Checkpoints[k].pileright = CreateObject(`prop_offroad_tyres02`, v.offset.right.x, v.offset.right.y, v.offset.right.z, 0, 0, 0)
|
|
PlaceObjectOnGroundProperly(CreatorData.Checkpoints[k].pileright)
|
|
FreezeEntityPosition(CreatorData.Checkpoints[k].pileleft, 1)
|
|
SetEntityAsMissionEntity(CreatorData.Checkpoints[k].pileleft, 1, 1)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function GetMaxDistance(OffsetCoords)
|
|
local Distance = #(vector3(OffsetCoords.left.x, OffsetCoords.left.y, OffsetCoords.left.z) - vector3(OffsetCoords.right.x, OffsetCoords.right.y, OffsetCoords.right.z))
|
|
local Retval = 7.5
|
|
if Distance > 20.0 then
|
|
Retval = 12.5
|
|
end
|
|
return Retval
|
|
end
|
|
|
|
local function SecondsToClock(seconds)
|
|
seconds = tonumber(seconds)
|
|
local retval
|
|
if seconds <= 0 then
|
|
retval = "00:00:00";
|
|
else
|
|
local hours = string.format("%02.f", math.floor(seconds/3600));
|
|
local mins = string.format("%02.f", math.floor(seconds/60 - (hours*60)));
|
|
local secs = string.format("%02.f", math.floor(seconds - hours*3600 - mins *60));
|
|
retval = hours..":"..mins..":"..secs
|
|
end
|
|
return retval
|
|
end
|
|
|
|
local function FinishRace()
|
|
TriggerServerEvent('qb-lapraces:server:FinishPlayer', CurrentRaceData, CurrentRaceData.TotalTime, CurrentRaceData.TotalLaps, CurrentRaceData.BestLap)
|
|
if CurrentRaceData.BestLap ~= 0 then
|
|
QBCore.Functions.Notify('Race finished in '..SecondsToClock(CurrentRaceData.TotalTime)..', with the best lap: '..SecondsToClock(CurrentRaceData.BestLap))
|
|
else
|
|
QBCore.Functions.Notify('Race finished in '..SecondsToClock(CurrentRaceData.TotalTime))
|
|
end
|
|
for k, _ in pairs(CurrentRaceData.Checkpoints) do
|
|
if CurrentRaceData.Checkpoints[k].blip ~= nil then
|
|
RemoveBlip(CurrentRaceData.Checkpoints[k].blip)
|
|
CurrentRaceData.Checkpoints[k].blip = nil
|
|
end
|
|
if CurrentRaceData.Checkpoints[k].pileleft ~= nil then
|
|
local coords = CurrentRaceData.Checkpoints[k].offset.left
|
|
local Obj = GetClosestObjectOfType(coords.x, coords.y, coords.z, 5.0, `prop_offroad_tyres02`, 0, 0, 0)
|
|
DeleteObject(Obj)
|
|
ClearAreaOfObjects(coords.x, coords.y, coords.z, 50.0, 0)
|
|
CurrentRaceData.Checkpoints[k].pileleft = nil
|
|
end
|
|
if CurrentRaceData.Checkpoints[k].pileright ~= nil then
|
|
local coords = CurrentRaceData.Checkpoints[k].offset.right
|
|
local Obj = GetClosestObjectOfType(coords.x, coords.y, coords.z, 5.0, `prop_offroad_tyres02`, 0, 0, 0)
|
|
DeleteObject(Obj)
|
|
ClearAreaOfObjects(coords.x, coords.y, coords.z, 50.0, 0)
|
|
CurrentRaceData.Checkpoints[k].pileright = nil
|
|
end
|
|
end
|
|
CurrentRaceData.RaceName = nil
|
|
CurrentRaceData.Checkpoints = {}
|
|
CurrentRaceData.Started = false
|
|
CurrentRaceData.CurrentCheckpoint = 0
|
|
CurrentRaceData.TotalLaps = 0
|
|
CurrentRaceData.Lap = 0
|
|
CurrentRaceData.RaceTime = 0
|
|
CurrentRaceData.TotalTime = 0
|
|
CurrentRaceData.BestLap = 0
|
|
CurrentRaceData.RaceId = nil
|
|
RaceData.InRace = false
|
|
end
|
|
|
|
local function Info()
|
|
local PlayerPed = PlayerPedId()
|
|
local plyVeh = GetVehiclePedIsIn(PlayerPed, false)
|
|
local IsDriver = GetPedInVehicleSeat(plyVeh, -1) == PlayerPed
|
|
local returnValue = plyVeh ~= 0 and plyVeh ~= nil and IsDriver
|
|
return returnValue, plyVeh
|
|
end
|
|
|
|
local function IsInRace()
|
|
local retval = false
|
|
if RaceData.InRace then
|
|
retval = true
|
|
end
|
|
return retval
|
|
end
|
|
|
|
local function IsInEditor()
|
|
local retval = false
|
|
if RaceData.InCreator then
|
|
retval = true
|
|
end
|
|
return retval
|
|
end
|
|
|
|
exports('IsInEditor', IsInEditor)
|
|
exports('IsInRace', IsInRace)
|
|
|
|
-- Events
|
|
|
|
RegisterNetEvent('qb-lapraces:client:StartRaceEditor', function(RaceName)
|
|
if not RaceData.InCreator then
|
|
CreatorData.RaceName = RaceName
|
|
RaceData.InCreator = true
|
|
CreatorUI()
|
|
CreatorLoop()
|
|
else
|
|
QBCore.Functions.Notify('You are already making a race.', 'error')
|
|
end
|
|
end)
|
|
|
|
RegisterNetEvent('qb-lapraces:client:UpdateRaceRacerData', function(RaceId, aRaceData)
|
|
if (CurrentRaceData.RaceId ~= nil) and CurrentRaceData.RaceId == RaceId then
|
|
CurrentRaceData.Racers = aRaceData.Racers
|
|
end
|
|
end)
|
|
|
|
RegisterNetEvent('qb-lapraces:client:JoinRace', function(Data, Laps)
|
|
if not RaceData.InRace then
|
|
RaceData.InRace = true
|
|
SetupRace(Data, Laps)
|
|
TriggerServerEvent('qb-lapraces:server:UpdateRaceState', CurrentRaceData.RaceId, false, true)
|
|
else
|
|
QBCore.Functions.Notify('Youre already in a race..', 'error')
|
|
end
|
|
end)
|
|
|
|
RegisterNetEvent('qb-lapraces:client:LeaveRace', function(_)
|
|
QBCore.Functions.Notify('You have completed the race!')
|
|
for k, _ in pairs(CurrentRaceData.Checkpoints) do
|
|
if CurrentRaceData.Checkpoints[k].blip ~= nil then
|
|
RemoveBlip(CurrentRaceData.Checkpoints[k].blip)
|
|
CurrentRaceData.Checkpoints[k].blip = nil
|
|
end
|
|
if CurrentRaceData.Checkpoints[k].pileleft ~= nil then
|
|
local coords = CurrentRaceData.Checkpoints[k].offset.left
|
|
local Obj = GetClosestObjectOfType(coords.x, coords.y, coords.z, 5.0, `prop_offroad_tyres02`, 0, 0, 0)
|
|
DeleteObject(Obj)
|
|
ClearAreaOfObjects(coords.x, coords.y, coords.z, 50.0, 0)
|
|
CurrentRaceData.Checkpoints[k].pileleft = nil
|
|
end
|
|
if CurrentRaceData.Checkpoints[k].pileright ~= nil then
|
|
local coords = CurrentRaceData.Checkpoints[k].offset.right
|
|
local Obj = GetClosestObjectOfType(coords.x, coords.y, coords.z, 5.0, `prop_offroad_tyres02`, 0, 0, 0)
|
|
DeleteObject(Obj)
|
|
ClearAreaOfObjects(coords.x, coords.y, coords.z, 50.0, 0)
|
|
CurrentRaceData.Checkpoints[k].pileright = nil
|
|
end
|
|
end
|
|
CurrentRaceData.RaceName = nil
|
|
CurrentRaceData.Checkpoints = {}
|
|
CurrentRaceData.Started = false
|
|
CurrentRaceData.CurrentCheckpoint = 0
|
|
CurrentRaceData.TotalLaps = 0
|
|
CurrentRaceData.Lap = 0
|
|
CurrentRaceData.RaceTime = 0
|
|
CurrentRaceData.TotalTime = 0
|
|
CurrentRaceData.BestLap = 0
|
|
CurrentRaceData.RaceId = nil
|
|
RaceData.InRace = false
|
|
FreezeEntityPosition(GetVehiclePedIsIn(PlayerPedId(), false), false)
|
|
end)
|
|
|
|
RegisterNetEvent('qb-lapraces:client:RaceCountdown', function()
|
|
TriggerServerEvent('qb-lapraces:server:UpdateRaceState', CurrentRaceData.RaceId, true, false)
|
|
if CurrentRaceData.RaceId ~= nil then
|
|
while Countdown ~= 0 do
|
|
if CurrentRaceData.RaceName ~= nil then
|
|
if Countdown == 10 then
|
|
QBCore.Functions.Notify('The race will start in 10 seconds', 'error', 2500)
|
|
PlaySound(-1, "slow", "SHORT_PLAYER_SWITCH_SOUND_SET", 0, 0, 1)
|
|
elseif Countdown <= 5 then
|
|
QBCore.Functions.Notify(Countdown, 'error', 500)
|
|
PlaySound(-1, "slow", "SHORT_PLAYER_SWITCH_SOUND_SET", 0, 0, 1)
|
|
end
|
|
Countdown = Countdown - 1
|
|
FreezeEntityPosition(GetVehiclePedIsIn(PlayerPedId(), true), true)
|
|
else
|
|
break
|
|
end
|
|
Wait(1000)
|
|
end
|
|
if CurrentRaceData.RaceName ~= nil then
|
|
SetNewWaypoint(CurrentRaceData.Checkpoints[CurrentRaceData.CurrentCheckpoint + 1].coords.x, CurrentRaceData.Checkpoints[CurrentRaceData.CurrentCheckpoint + 1].coords.y)
|
|
QBCore.Functions.Notify('GO!', 'success', 1000)
|
|
SetBlipScale(CurrentRaceData.Checkpoints[CurrentRaceData.CurrentCheckpoint + 1].blip, 1.0)
|
|
FreezeEntityPosition(GetVehiclePedIsIn(PlayerPedId(), true), false)
|
|
DoPilePfx()
|
|
CurrentRaceData.Started = true
|
|
Countdown = 10
|
|
else
|
|
FreezeEntityPosition(GetVehiclePedIsIn(PlayerPedId(), true), false)
|
|
Countdown = 10
|
|
end
|
|
else
|
|
QBCore.Functions.Notify('You are not currently in a race..', 'error')
|
|
end
|
|
end)
|
|
|
|
RegisterNetEvent('qb-lapraces:client:PlayerFinishs', function(RaceId, Place, FinisherData)
|
|
if CurrentRaceData.RaceId ~= nil then
|
|
if CurrentRaceData.RaceId == RaceId then
|
|
QBCore.Functions.Notify(FinisherData.PlayerData.charinfo.firstname..' is finished on spot: '..Place, 'error', 3500)
|
|
end
|
|
end
|
|
end)
|
|
|
|
RegisterNetEvent('qb-lapraces:client:WaitingDistanceCheck', function()
|
|
Wait(1000)
|
|
CreateThread(function()
|
|
while true do
|
|
if not CurrentRaceData.Started then
|
|
local ped = PlayerPedId()
|
|
local pos = GetEntityCoords(ped)
|
|
if CurrentRaceData.Checkpoints[1] ~= nil then
|
|
local cpcoords = CurrentRaceData.Checkpoints[1].coords
|
|
local dist = #(pos - vector3(cpcoords.x, cpcoords.y, cpcoords.z))
|
|
if dist > 115.0 then
|
|
if ToFarCountdown ~= 0 then
|
|
ToFarCountdown = ToFarCountdown - 1
|
|
QBCore.Functions.Notify('Go back to the start or you will be kicked from the race: '..ToFarCountdown..'s', 'error', 500)
|
|
else
|
|
TriggerServerEvent('qb-lapraces:server:LeaveRace', CurrentRaceData)
|
|
ToFarCountdown = 10
|
|
break
|
|
end
|
|
Wait(1000)
|
|
else
|
|
if ToFarCountdown ~= 10 then
|
|
ToFarCountdown = 10
|
|
end
|
|
end
|
|
end
|
|
else
|
|
break
|
|
end
|
|
Wait(3)
|
|
end
|
|
end)
|
|
end)
|
|
|
|
-- Threads
|
|
|
|
CreateThread(function()
|
|
while true do
|
|
if RaceData.InCreator then
|
|
local PlayerPed = PlayerPedId()
|
|
local PlayerVeh = GetVehiclePedIsIn(PlayerPed)
|
|
|
|
if PlayerVeh ~= 0 then
|
|
local Offset = {
|
|
left = {
|
|
x = (GetOffsetFromEntityInWorldCoords(PlayerVeh, -CreatorData.TireDistance, 0.0, 0.0)).x,
|
|
y = (GetOffsetFromEntityInWorldCoords(PlayerVeh, -CreatorData.TireDistance, 0.0, 0.0)).y,
|
|
z = (GetOffsetFromEntityInWorldCoords(PlayerVeh, -CreatorData.TireDistance, 0.0, 0.0)).z,
|
|
},
|
|
right = {
|
|
x = (GetOffsetFromEntityInWorldCoords(PlayerVeh, CreatorData.TireDistance, 0.0, 0.0)).x,
|
|
y = (GetOffsetFromEntityInWorldCoords(PlayerVeh, CreatorData.TireDistance, 0.0, 0.0)).y,
|
|
z = (GetOffsetFromEntityInWorldCoords(PlayerVeh, CreatorData.TireDistance, 0.0, 0.0)).z,
|
|
}
|
|
}
|
|
|
|
DrawText3Ds(Offset.left.x, Offset.left.y, Offset.left.z, 'Checkpoint L')
|
|
DrawText3Ds(Offset.right.x, Offset.right.y, Offset.right.z, 'Checkpoint R')
|
|
end
|
|
end
|
|
Wait(3)
|
|
end
|
|
end)
|
|
|
|
CreateThread(function()
|
|
while true do
|
|
|
|
local ped = PlayerPedId()
|
|
local pos = GetEntityCoords(ped)
|
|
|
|
if CurrentRaceData.RaceName ~= nil then
|
|
if CurrentRaceData.Started then
|
|
local cp
|
|
if CurrentRaceData.CurrentCheckpoint + 1 > #CurrentRaceData.Checkpoints then
|
|
cp = 1
|
|
else
|
|
cp = CurrentRaceData.CurrentCheckpoint + 1
|
|
end
|
|
local data = CurrentRaceData.Checkpoints[cp]
|
|
local CheckpointDistance = #(pos - vector3(data.coords.x, data.coords.y, data.coords.z))
|
|
local MaxDistance = GetMaxDistance(CurrentRaceData.Checkpoints[cp].offset)
|
|
|
|
if CheckpointDistance < MaxDistance then
|
|
if CurrentRaceData.TotalLaps == 0 then
|
|
if CurrentRaceData.CurrentCheckpoint + 1 < #CurrentRaceData.Checkpoints then
|
|
CurrentRaceData.CurrentCheckpoint = CurrentRaceData.CurrentCheckpoint + 1
|
|
SetNewWaypoint(CurrentRaceData.Checkpoints[CurrentRaceData.CurrentCheckpoint + 1].coords.x, CurrentRaceData.Checkpoints[CurrentRaceData.CurrentCheckpoint + 1].coords.y)
|
|
TriggerServerEvent('qb-lapraces:server:UpdateRacerData', CurrentRaceData.RaceId, CurrentRaceData.CurrentCheckpoint, CurrentRaceData.Lap, false)
|
|
DoPilePfx()
|
|
PlaySound(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", 0, 0, 1)
|
|
SetBlipScale(CurrentRaceData.Checkpoints[CurrentRaceData.CurrentCheckpoint].blip, 0.6)
|
|
SetBlipScale(CurrentRaceData.Checkpoints[CurrentRaceData.CurrentCheckpoint + 1].blip, 1.0)
|
|
else
|
|
DoPilePfx()
|
|
PlaySound(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", 0, 0, 1)
|
|
CurrentRaceData.CurrentCheckpoint = CurrentRaceData.CurrentCheckpoint + 1
|
|
TriggerServerEvent('qb-lapraces:server:UpdateRacerData', CurrentRaceData.RaceId, CurrentRaceData.CurrentCheckpoint, CurrentRaceData.Lap, true)
|
|
FinishRace()
|
|
end
|
|
else
|
|
if CurrentRaceData.CurrentCheckpoint + 1 > #CurrentRaceData.Checkpoints then
|
|
if CurrentRaceData.Lap + 1 > CurrentRaceData.TotalLaps then
|
|
DoPilePfx()
|
|
PlaySound(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", 0, 0, 1)
|
|
CurrentRaceData.CurrentCheckpoint = CurrentRaceData.CurrentCheckpoint + 1
|
|
TriggerServerEvent('qb-lapraces:server:UpdateRacerData', CurrentRaceData.RaceId, CurrentRaceData.CurrentCheckpoint, CurrentRaceData.Lap, true)
|
|
FinishRace()
|
|
else
|
|
DoPilePfx()
|
|
PlaySound(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", 0, 0, 1)
|
|
if CurrentRaceData.RaceTime < CurrentRaceData.BestLap then
|
|
CurrentRaceData.BestLap = CurrentRaceData.RaceTime
|
|
elseif CurrentRaceData.BestLap == 0 then
|
|
CurrentRaceData.BestLap = CurrentRaceData.RaceTime
|
|
end
|
|
CurrentRaceData.RaceTime = 0
|
|
CurrentRaceData.Lap = CurrentRaceData.Lap + 1
|
|
CurrentRaceData.CurrentCheckpoint = 1
|
|
SetNewWaypoint(CurrentRaceData.Checkpoints[CurrentRaceData.CurrentCheckpoint + 1].coords.x, CurrentRaceData.Checkpoints[CurrentRaceData.CurrentCheckpoint + 1].coords.y)
|
|
TriggerServerEvent('qb-lapraces:server:UpdateRacerData', CurrentRaceData.RaceId, CurrentRaceData.CurrentCheckpoint, CurrentRaceData.Lap, false)
|
|
end
|
|
else
|
|
CurrentRaceData.CurrentCheckpoint = CurrentRaceData.CurrentCheckpoint + 1
|
|
if CurrentRaceData.CurrentCheckpoint ~= #CurrentRaceData.Checkpoints then
|
|
SetNewWaypoint(CurrentRaceData.Checkpoints[CurrentRaceData.CurrentCheckpoint + 1].coords.x, CurrentRaceData.Checkpoints[CurrentRaceData.CurrentCheckpoint + 1].coords.y)
|
|
TriggerServerEvent('qb-lapraces:server:UpdateRacerData', CurrentRaceData.RaceId, CurrentRaceData.CurrentCheckpoint, CurrentRaceData.Lap, false)
|
|
SetBlipScale(CurrentRaceData.Checkpoints[CurrentRaceData.CurrentCheckpoint].blip, 0.6)
|
|
SetBlipScale(CurrentRaceData.Checkpoints[CurrentRaceData.CurrentCheckpoint + 1].blip, 1.0)
|
|
else
|
|
SetNewWaypoint(CurrentRaceData.Checkpoints[1].coords.x, CurrentRaceData.Checkpoints[1].coords.y)
|
|
TriggerServerEvent('qb-lapraces:server:UpdateRacerData', CurrentRaceData.RaceId, CurrentRaceData.CurrentCheckpoint, CurrentRaceData.Lap, false)
|
|
SetBlipScale(CurrentRaceData.Checkpoints[#CurrentRaceData.Checkpoints].blip, 0.6)
|
|
SetBlipScale(CurrentRaceData.Checkpoints[1].blip, 1.0)
|
|
end
|
|
DoPilePfx()
|
|
PlaySound(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", 0, 0, 1)
|
|
end
|
|
end
|
|
end
|
|
else
|
|
local data = CurrentRaceData.Checkpoints[CurrentRaceData.CurrentCheckpoint]
|
|
-- DrawText3Ds(data.coords.x, data.coords.y, data.coords.z, 'Ga hier staan')
|
|
DrawMarker(4, data.coords.x, data.coords.y, data.coords.z + 1.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.9, 1.5, 1.5, 255, 255, 255, 255, 0, 1, 0, 0, 0, 0, 0)
|
|
end
|
|
else
|
|
Wait(1000)
|
|
end
|
|
|
|
Wait(3)
|
|
end
|
|
end)
|
|
|
|
CreateThread(function()
|
|
while true do
|
|
if RaceData.InCreator then
|
|
GetClosestCheckpoint()
|
|
SetupPiles()
|
|
end
|
|
Wait(1000)
|
|
end
|
|
end)
|
|
|
|
CreateThread(function()
|
|
while true do
|
|
local Driver, plyVeh = Info()
|
|
if Driver then
|
|
if GetVehicleCurrentGear(plyVeh) < 3 and GetVehicleCurrentRpm(plyVeh) == 1.0 and math.ceil(GetEntitySpeed(plyVeh) * 2.236936) > 50 then
|
|
while GetVehicleCurrentRpm(plyVeh) > 0.6 do
|
|
SetVehicleCurrentRpm(plyVeh, 0.3)
|
|
Wait(1)
|
|
end
|
|
Wait(800)
|
|
end
|
|
end
|
|
Wait(500)
|
|
end
|
|
end)
|