mirror of
https://github.com/citizenfx/cfx-server-data.git
synced 2025-12-12 06:14:09 +01:00
initial
This commit is contained in:
9
resources/[gamemodes]/race/__resource.lua
Normal file
9
resources/[gamemodes]/race/__resource.lua
Normal file
@@ -0,0 +1,9 @@
|
||||
resource_type 'gametype' { name = 'Race' }
|
||||
|
||||
dependencies {
|
||||
"spawnmanager",
|
||||
"mapmanager"
|
||||
}
|
||||
|
||||
client_script 'race_client.lua'
|
||||
server_script 'race_server.lua'
|
||||
469
resources/[gamemodes]/race/race_client.lua
Normal file
469
resources/[gamemodes]/race/race_client.lua
Normal file
@@ -0,0 +1,469 @@
|
||||
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)
|
||||
209
resources/[gamemodes]/race/race_server.lua
Normal file
209
resources/[gamemodes]/race/race_server.lua
Normal file
@@ -0,0 +1,209 @@
|
||||
local checkpoints = {}
|
||||
local raceId = 0
|
||||
|
||||
RegisterServerEvent('race:updateCheckpoints')
|
||||
|
||||
AddEventHandler('race:updateCheckpoints', function(cps)
|
||||
if #checkpoints > 0 then
|
||||
return
|
||||
end
|
||||
|
||||
checkpoints = cps
|
||||
|
||||
TriggerClientEvent('race:weGotPorn', -1)
|
||||
end)
|
||||
|
||||
local playerData = {}
|
||||
|
||||
local function ensurePlayerData(id)
|
||||
if playerData[id] then
|
||||
return
|
||||
end
|
||||
|
||||
playerData[id] = {
|
||||
curCheckpoint = 0
|
||||
}
|
||||
end
|
||||
|
||||
local raceStarted = false
|
||||
local playerCount = 0
|
||||
|
||||
local function startRace()
|
||||
raceStarted = true
|
||||
|
||||
print("really starting race")
|
||||
|
||||
local function raceCountdown(num)
|
||||
local time = (4000 - (num * 1000))
|
||||
|
||||
print("setting countdown for " .. tostring(time))
|
||||
|
||||
SetTimeout(time, function()
|
||||
print("trig'd countdown for " .. tostring(time))
|
||||
|
||||
if num == 0 then
|
||||
TriggerClientEvent('race:itsGoTime', -1, 0)
|
||||
TriggerClientEvent('race:showGoMessage', -1, 'GO')
|
||||
else
|
||||
TriggerClientEvent('race:showGoMessage', -1, tostring(num))
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
raceCountdown(3) -- 3...
|
||||
raceCountdown(2) -- 2...
|
||||
raceCountdown(1) -- 1...
|
||||
raceCountdown(0) -- GOGOGO
|
||||
end
|
||||
|
||||
local function incrementPlayerCount()
|
||||
playerCount = playerCount + 1
|
||||
|
||||
if playerCount > 4 then
|
||||
startRace()
|
||||
end
|
||||
|
||||
if playerCount == 1 then
|
||||
SetTimeout(3000, function()
|
||||
if raceStarted then
|
||||
return
|
||||
end
|
||||
|
||||
print("starting race")
|
||||
|
||||
startRace()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
local playersFinished
|
||||
local raceEnded
|
||||
|
||||
AddEventHandler('onMapStart', function()
|
||||
playerCount = 0
|
||||
playersFinished = 0
|
||||
raceId = raceId + 1
|
||||
raceStarted = false
|
||||
raceEnded = false
|
||||
|
||||
playerData = {}
|
||||
checkpoints = {}
|
||||
|
||||
print("mmmmmm race")
|
||||
end)
|
||||
|
||||
local function endRace()
|
||||
raceEnded = true
|
||||
|
||||
TriggerClientEvent('race:results', -1, '1234')
|
||||
|
||||
SetTimeout(7500, function()
|
||||
TriggerEvent('mapmanager:roundEnded')
|
||||
end)
|
||||
end
|
||||
|
||||
AddEventHandler('race:onPlayerFinished', function(player)
|
||||
print(GetPlayerName(player) .. ' finished')
|
||||
|
||||
local data = playerData[player]
|
||||
local finishSeconds = os.clock() - data.startTime
|
||||
|
||||
local position = playersFinished + 1
|
||||
data.position = position
|
||||
playersFinished = position
|
||||
|
||||
TriggerClientEvent('race:onPlayerFinished', -1, player, {
|
||||
finishSeconds = finishSeconds,
|
||||
position = position
|
||||
})
|
||||
|
||||
if playersFinished == playerCount then
|
||||
endRace()
|
||||
elseif playersFinished == 1 then
|
||||
local thisRaceId = raceId
|
||||
|
||||
TriggerClientEvent('race:hurryUp', -1, 30000)
|
||||
|
||||
SetTimeout(30000, function()
|
||||
if raceId ~= thisRaceId or raceEnded then
|
||||
return
|
||||
end
|
||||
|
||||
endRace()
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
AddEventHandler('playerActivated', function()
|
||||
if #checkpoints > 0 then
|
||||
TriggerClientEvent('race:weGotPorn', source)
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterServerEvent('race:requestGo')
|
||||
|
||||
AddEventHandler('race:requestGo', function()
|
||||
if #checkpoints > 0 then
|
||||
TriggerClientEvent('race:weGotPorn', source)
|
||||
end
|
||||
end)
|
||||
|
||||
AddEventHandler('playerDropped', function(player)
|
||||
if playerData[player] and playerData[player].curCheckpoint > 0 then
|
||||
TriggerClientEvent('race:updateStatus', -1, player, -1)
|
||||
|
||||
playerCount = playerCount - 1
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterServerEvent('race:gotCP')
|
||||
|
||||
AddEventHandler('race:gotCP', function()
|
||||
ensurePlayerData(source)
|
||||
|
||||
local data = playerData[source]
|
||||
|
||||
local next = data.curCheckpoint + 1
|
||||
|
||||
if next > #checkpoints then
|
||||
print("omg finished")
|
||||
|
||||
TriggerEvent('race:onPlayerFinished', source)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
data.curCheckpoint = next
|
||||
|
||||
TriggerClientEvent('race:confirmCP', source) -- for sound effects
|
||||
TriggerClientEvent('race:setCheckpoint', source, checkpoints[next], checkpoints[next + 1], checkpoints[next + 2])
|
||||
TriggerClientEvent('race:updateStatus', -1, source, next - 1)
|
||||
end)
|
||||
|
||||
RegisterServerEvent('race:requestCheckpoint')
|
||||
|
||||
AddEventHandler('race:requestCheckpoint', function()
|
||||
print('is it even in here')
|
||||
|
||||
ensurePlayerData(source)
|
||||
|
||||
print(source, 'requesting cp')
|
||||
|
||||
if playerData[source].curCheckpoint == 0 then
|
||||
incrementPlayerCount()
|
||||
|
||||
print(source, 'requesting cp 0')
|
||||
|
||||
local curCP = 1
|
||||
playerData[source].curCheckpoint = curCP
|
||||
playerData[source].startTime = os.clock()
|
||||
|
||||
TriggerClientEvent('race:setCheckpoint', source, checkpoints[curCP], checkpoints[curCP + 1], checkpoints[curCP + 2])
|
||||
TriggerClientEvent('race:updateStatus', -1, source, 0)
|
||||
|
||||
-- should have raceReallyStarted since 4-second countdown
|
||||
if raceStarted then
|
||||
TriggerClientEvent('race:itsGoTime', -1, 0)
|
||||
end
|
||||
end
|
||||
end)
|
||||
Reference in New Issue
Block a user