mirror of
https://github.com/citizenfx/cfx-server-data.git
synced 2025-01-11 00:03:18 +08:00
470 lines
11 KiB
Lua
470 lines
11 KiB
Lua
|
local curCheckpoint, nextCheckpoint
|
||
|
local goGoGo
|
||
|
|
||
|
local playerCar
|
||
|
local weFinished
|
||
|
local resultsShown
|
||
|
|
||
|
local checkpoints = {}
|
||
|
local playerScores = {}
|
||
|
|
||
|
local function initializeMap()
|
||
|
echo("[RACE] initializeMap\n")
|
||
|
|
||
|
TriggerServerEvent('race:updateCheckpoints', checkpoints)
|
||
|
end
|
||
|
|
||
|
local function resetGameMode()
|
||
|
curCheckpoint = nil
|
||
|
nextCheckpoint = nil
|
||
|
|
||
|
SetMultiplayerHudTime('')
|
||
|
|
||
|
checkpointCount = 0
|
||
|
playerScores = {}
|
||
|
|
||
|
if IsThisMachineTheServer() then
|
||
|
-- load the initial map
|
||
|
initializeMap()
|
||
|
end
|
||
|
|
||
|
goGoGo = false
|
||
|
weFinished = false
|
||
|
resultsShown = false
|
||
|
end
|
||
|
|
||
|
local function updatePositions()
|
||
|
local players = {}
|
||
|
|
||
|
for id, data in pairs(playerScores) do
|
||
|
data.playerId = id
|
||
|
|
||
|
table.insert(players, data)
|
||
|
end
|
||
|
|
||
|
table.sort(players, function(a, b)
|
||
|
if a.finishPosition or b.finishPosition then
|
||
|
if not b.finishPosition then
|
||
|
return true
|
||
|
end
|
||
|
|
||
|
if not a.finishPosition then
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
return a.finishPosition < b.finishPosition
|
||
|
end
|
||
|
|
||
|
if a.cp == b.cp then
|
||
|
local aPed = a.ped
|
||
|
local bPed = b.ped
|
||
|
|
||
|
local aPos
|
||
|
local bPos
|
||
|
|
||
|
if not DoesCharExist(aPed) or not DoesCharExist(bPed) then
|
||
|
aPos = { 0, 0 }
|
||
|
bPos = { 0, 0 }
|
||
|
else
|
||
|
aPos = a.ped.position
|
||
|
bPos = b.ped.position
|
||
|
end
|
||
|
|
||
|
local nextCp = checkpoints[a.cp + 1]
|
||
|
|
||
|
if not nextCp then
|
||
|
return a.cp > b.cp
|
||
|
end
|
||
|
|
||
|
local aDist = GetDistanceBetweenCoords2d(aPos[1], aPos[2], nextCp.pos[1], nextCp.pos[2])
|
||
|
local bDist = GetDistanceBetweenCoords2d(bPos[1], bPos[2], nextCp.pos[1], nextCp.pos[2])
|
||
|
|
||
|
return aDist < bDist
|
||
|
end
|
||
|
|
||
|
return a.cp > b.cp
|
||
|
end)
|
||
|
|
||
|
if not playerScores[GetPlayerId().serverId] then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local lastPosition = selfLastPosition
|
||
|
|
||
|
local i = 1
|
||
|
|
||
|
for _, v in ipairs(players) do
|
||
|
playerScores[v.playerId].position = i
|
||
|
|
||
|
i = i + 1
|
||
|
end
|
||
|
|
||
|
local selfPosition = playerScores[GetPlayerId().serverId].position
|
||
|
selfLastPosition = selfPosition
|
||
|
|
||
|
if selfPosition ~= lastPosition then
|
||
|
TriggerEvent('chatMessage', '', { 0, 0, 0 }, 'position changed to ' .. tostring(selfPosition) .. ' from ' .. tostring(lastPosition))
|
||
|
end
|
||
|
|
||
|
|
||
|
-- positions updated, we hope
|
||
|
end
|
||
|
|
||
|
AddEventHandler('race:onPlayerFinished', function(player, data)
|
||
|
local selfId = GetPlayerId().serverId
|
||
|
|
||
|
if not playerScores[player] then
|
||
|
local ped = sPlayer.ped
|
||
|
|
||
|
playerScores[player] = {
|
||
|
cp = #checkpoints,
|
||
|
ped = ped,
|
||
|
vehicle = ped.vehicle
|
||
|
}
|
||
|
end
|
||
|
|
||
|
playerScores[player].finishPosition = data.position
|
||
|
|
||
|
if selfId == player then
|
||
|
exports.obituary:printObituary('New world record!')
|
||
|
|
||
|
TriggerEvent('chatMessage', '', { 0, 0, 0 }, 'you finished!')
|
||
|
|
||
|
weFinished = true
|
||
|
|
||
|
tearDownCheckpoint(curCheckpoint)
|
||
|
tearDownCheckpoint(nextCheckpoint)
|
||
|
|
||
|
-- todo: spectate?
|
||
|
|
||
|
CreateThread(function()
|
||
|
Wait(500)
|
||
|
|
||
|
if playerCar then
|
||
|
FreezeCarPosition(playerCar, true)
|
||
|
end
|
||
|
end)
|
||
|
end
|
||
|
|
||
|
local sPlayer = GetPlayerByServerId(player)
|
||
|
|
||
|
if sPlayer then
|
||
|
exports.obituary:printObituary('<b>%s</b> finished in %s seconds', sPlayer.name, tostring(data.finishSeconds))
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
AddEventHandler('onClientGameTypeStart', function()
|
||
|
CreateThread(function()
|
||
|
--[[while true do
|
||
|
Wait(500)
|
||
|
|
||
|
local player = GetPlayerId()
|
||
|
|
||
|
TriggerServerEvent('race:updatePos', player.ped.position)
|
||
|
end]]
|
||
|
end)
|
||
|
|
||
|
CreateThread(function()
|
||
|
while true do
|
||
|
Wait(250)
|
||
|
|
||
|
updatePositions()
|
||
|
end
|
||
|
end)
|
||
|
end)
|
||
|
|
||
|
function GetPlayerInteger(i)
|
||
|
local serverId = i.serverId
|
||
|
local players = GetPlayers()
|
||
|
|
||
|
for k, v in ipairs(players) do
|
||
|
if v.serverId == serverId then
|
||
|
return k
|
||
|
end
|
||
|
end
|
||
|
|
||
|
return 1
|
||
|
end
|
||
|
|
||
|
local function spawnVehicle(spawnPoint)
|
||
|
local carModel
|
||
|
|
||
|
if not spawnPoint.carModel then
|
||
|
carModel = 'admiral'
|
||
|
else
|
||
|
carModel = spawnPoint.carModel
|
||
|
end
|
||
|
|
||
|
if not tonumber(carModel) then
|
||
|
carModel = GetHashKey(carModel, _r)
|
||
|
end
|
||
|
|
||
|
-- is the model actually a model?
|
||
|
if not IsModelInCdimage(carModel) then
|
||
|
error("invalid spawn model")
|
||
|
end
|
||
|
|
||
|
-- is is even a vehicle?
|
||
|
if not IsThisModelAVehicle(carModel) then
|
||
|
error("this model ain't a vehicle!")
|
||
|
end
|
||
|
|
||
|
-- spawn a vehicle for our lovely player
|
||
|
RequestModel(carModel)
|
||
|
LoadAllObjectsNow()
|
||
|
|
||
|
playerCar = CreateCar(carModel, spawnPoint.x, spawnPoint.y, spawnPoint.z, 0, 1)
|
||
|
SetCarHeading(playerCar, spawnPoint.heading)
|
||
|
SetCarOnGroundProperly(playerCar)
|
||
|
|
||
|
WarpCharIntoCar(GetPlayerId().ped, playerCar)
|
||
|
|
||
|
if not goGoGo then
|
||
|
FreezeCarPosition(playerCar, true)
|
||
|
end
|
||
|
|
||
|
LockCarDoors(playerCar, 4)
|
||
|
|
||
|
-- and done, hopefully.
|
||
|
end
|
||
|
|
||
|
AddEventHandler('race:itsGoTime', function()
|
||
|
if playerCar then
|
||
|
-- let go of the brakes
|
||
|
FreezeCarPosition(playerCar, false)
|
||
|
end
|
||
|
|
||
|
-- gogogo
|
||
|
goGoGo = true
|
||
|
end)
|
||
|
|
||
|
string.lpad = function(str, len, char)
|
||
|
if char == nil then char = ' ' end
|
||
|
return string.rep(char, len - #str) .. str
|
||
|
end
|
||
|
|
||
|
AddEventHandler('race:results', function(time)
|
||
|
if playerCar then
|
||
|
FreezeCarPosition(playerCar, true)
|
||
|
end
|
||
|
|
||
|
tearDownCheckpoint(curCheckpoint)
|
||
|
tearDownCheckpoint(nextCheckpoint)
|
||
|
|
||
|
SetMultiplayerHudTime('')
|
||
|
|
||
|
updatePositions()
|
||
|
|
||
|
local players = {}
|
||
|
|
||
|
for id, data in pairs(playerScores) do
|
||
|
table.insert(players, data)
|
||
|
end
|
||
|
|
||
|
table.sort(players, function(a, b) return a.position < b.position end)
|
||
|
|
||
|
TriggerEvent('chatMessage', '', { 0, 0, 0 }, 'RESULTS')
|
||
|
|
||
|
for i, p in ipairs(players) do
|
||
|
local name = '**INVALID**'
|
||
|
local sp = GetPlayerByServerId(p.playerId)
|
||
|
|
||
|
if sp then
|
||
|
name = sp.name
|
||
|
end
|
||
|
|
||
|
TriggerEvent('chatMessage', '', { 0, 0, 0 }, tostring(i) .. '. ' .. name)
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
AddEventHandler('race:hurryUp', function(time)
|
||
|
CreateThread(function()
|
||
|
echo("resultsShown: " .. tostring(resultsShown) .. " , weF: " .. tostring(weFinished) .. "\n")
|
||
|
|
||
|
while not resultsShown and not weFinished do
|
||
|
Wait(1000)
|
||
|
|
||
|
time = time - 1000
|
||
|
|
||
|
SetMultiplayerHudTime('00:' .. tostring(math.floor(time / 1000)):lpad(2, '0'))
|
||
|
echo(tostring(math.floor(time / 1000)):lpad(2, '0') .. ':' .. tostring(math.floor((time % 1000) / 100)):lpad(2, '0') .. "\n")
|
||
|
end
|
||
|
end)
|
||
|
end)
|
||
|
|
||
|
AddEventHandler('race:showGoMessage', function(message)
|
||
|
TriggerEvent('chatMessage', '', { 0, 0, 0 }, message)
|
||
|
end)
|
||
|
|
||
|
AddEventHandler('onClientMapStart', function(res)
|
||
|
resetGameMode()
|
||
|
|
||
|
requestedGo = true
|
||
|
|
||
|
TriggerServerEvent('race:requestGo')
|
||
|
end)
|
||
|
|
||
|
AddEventHandler('onClientMapStop', function(res)
|
||
|
DoScreenFadeOut(50)
|
||
|
end)
|
||
|
|
||
|
AddEventHandler('race:weGotPorn', function()
|
||
|
echo("[RACE] race:weGotPorn\n")
|
||
|
|
||
|
if not requestedGo then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
requestedGo = false
|
||
|
|
||
|
exports.spawnmanager:setAutoSpawn(false)
|
||
|
|
||
|
exports.spawnmanager:spawnPlayer(GetPlayerInteger(GetPlayerId()), function(spawnPoint)
|
||
|
spawnVehicle(spawnPoint)
|
||
|
end)
|
||
|
|
||
|
TriggerServerEvent('race:requestCheckpoint', '1234')
|
||
|
end)
|
||
|
|
||
|
local function setUpCheckpoint(cp, next)
|
||
|
local nextPos, typeNum
|
||
|
|
||
|
if next then
|
||
|
nextPos = next.pos
|
||
|
typeNum = 2
|
||
|
else
|
||
|
nextPos = { 0.0, 0.0, 0.0 }
|
||
|
typeNum = 3
|
||
|
end
|
||
|
|
||
|
-- 2 = regular 'ground', 3 = finish 'ground', others are different 3dmarker types
|
||
|
cp.handle = CreateCheckpoint(typeNum, cp.pos[1], cp.pos[2], cp.pos[3] + 2.5, nextPos[1], nextPos[2], nextPos[3], 1.0001, _r)
|
||
|
cp.blip = AddBlipForCoord(cp.pos[1], cp.pos[2], cp.pos[3], _i)
|
||
|
|
||
|
if cp == nextCheckpoint then
|
||
|
ChangeBlipScale(cp.blip, 0.8)
|
||
|
end
|
||
|
|
||
|
ChangeBlipSprite(cp.blip, 3)
|
||
|
end
|
||
|
|
||
|
function tearDownCheckpoint(cp)
|
||
|
if not cp then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
if cp.blip then
|
||
|
RemoveBlip(cp.blip)
|
||
|
cp.blip = nil
|
||
|
end
|
||
|
|
||
|
if cp.handle then
|
||
|
DeleteCheckpoint(cp.handle)
|
||
|
cp.handle = nil
|
||
|
end
|
||
|
end
|
||
|
|
||
|
AddEventHandler('race:setCheckpoint', function(cur, next, later)
|
||
|
if curCheckpoint then
|
||
|
tearDownCheckpoint(curCheckpoint)
|
||
|
end
|
||
|
|
||
|
if nextCheckpoint then
|
||
|
tearDownCheckpoint(nextCheckpoint)
|
||
|
end
|
||
|
|
||
|
curCheckpoint = cur
|
||
|
nextCheckpoint = next
|
||
|
|
||
|
if cur then
|
||
|
setUpCheckpoint(curCheckpoint, nextCheckpoint)
|
||
|
|
||
|
-- make a background thread waiting for the checkpoint to be reached
|
||
|
CreateThread(function()
|
||
|
local localCur = curCheckpoint
|
||
|
|
||
|
-- so we exit if the checkpoint target is changed
|
||
|
while curCheckpoint == localCur do
|
||
|
Wait(25)
|
||
|
|
||
|
if playerCar then
|
||
|
local px, py, pz = GetCarCoordinates(playerCar)
|
||
|
local distance = GetDistanceBetweenCoords2d(px, py, localCur.pos[1], localCur.pos[2])
|
||
|
|
||
|
if distance < 10 then
|
||
|
-- pass the fact we reached the checkpoint to the server
|
||
|
TriggerServerEvent('race:gotCP', '1234')
|
||
|
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end)
|
||
|
end
|
||
|
|
||
|
if next then
|
||
|
setUpCheckpoint(nextCheckpoint, later)
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
AddEventHandler('race:confirmCP', function()
|
||
|
PlayAudioEvent('FRONTEND_GAME_PICKUP_CHECKPOINT')
|
||
|
end)
|
||
|
|
||
|
AddEventHandler('race:updateStatus', function(player, curCP)
|
||
|
if curCP == -1 then
|
||
|
playerScores[player] = nil
|
||
|
end
|
||
|
|
||
|
local sPlayer = GetPlayerByServerId(player)
|
||
|
|
||
|
if not sPlayer then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local ped = sPlayer.ped
|
||
|
|
||
|
playerScores[player] = {
|
||
|
cp = curCP,
|
||
|
ped = ped,
|
||
|
vehicle = ped.vehicle
|
||
|
}
|
||
|
|
||
|
TriggerEvent('chatMessage', '', { 0, 0, 0 }, sPlayer.name .. ' now has cp ' .. curCP)
|
||
|
|
||
|
updatePositions()
|
||
|
end)
|
||
|
|
||
|
AddEventHandler('onClientMapStop', function()
|
||
|
if playerCar then
|
||
|
MarkCarAsNoLongerNeeded(playerCar)
|
||
|
playerCar = nil
|
||
|
end
|
||
|
|
||
|
if curCheckpoint and curCheckpoint.handle then
|
||
|
DeleteCheckpoint(curCheckpoint.handle)
|
||
|
end
|
||
|
|
||
|
if nextCheckpoint and nextCheckpoint.handle then
|
||
|
DeleteCheckpoint(nextCheckpoint.handle)
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
AddEventHandler('getMapDirectives', function(add)
|
||
|
-- call the remote callback
|
||
|
add('checkpoint', function(state, data)
|
||
|
table.insert(checkpoints, data)
|
||
|
|
||
|
state.add('pos', data.pos)
|
||
|
|
||
|
-- delete callback follows on the next line
|
||
|
end, function(state, arg)
|
||
|
for i, sp in ipairs(checkpoints) do
|
||
|
if sp.pos[1] == state.pos[1] and sp.pos[2] == state.pos[2] and sp.pos[3] == state.pos[3] then
|
||
|
table.remove(checkpoints, i)
|
||
|
return
|
||
|
end
|
||
|
end
|
||
|
end)
|
||
|
end)
|