· 4 years ago · Feb 12, 2021, 03:52 PM
1local marathonPlayers = {}
2local topTimes = {} -- stores winners first times to get top 5 later
3local previousLocation = {}
4local previousSkin = {}
5local eventVehicles = {}
6local marathonCreated
7local marathonStarted
8local payPerParticipant = 25000 -- prize will depend on amount of players participating
9local marathonPrize
10local eventDim = 299
11local marathonSkins = {138, 139, 140, 145, 97, 45, 18, 51, 52}
12local startX, startY, startZ = 1295.03992, -2690.84277, 2.58662
13local startTick
14local TIME_UNTIL_START = 1000 * 60 * 5
15local startTimer
16local isMarathonWon
17local MAX_PLAYERS -- will be taken into consideration if stated by marathon host
18local MIN_PLAYERS = 4
19
20-- Setting up the marathon
21
22function marathonCommands(plr, cmd, arg)
23 if (not arg) then
24 exports.CIThelp:dm("Syntax: /marathon <warp> <leave> <time> <count>", plr, 255, 25, 25)
25 return false
26 end
27 if (arg == "warp") then
28 warpPlayerToMarathon(plr)
29 elseif (arg == "leave") then
30 warpPlayerOutOfMarathon(plr)
31 elseif (arg == "panel") then
32 local adminLvl = exports.CITadmin:getPlayerAdminLevel(plr)
33 if (not adminLvl or adminLvl < 2) then
34 return false
35 end
36 triggerClientEvent(plr, "CITmarathon:openGUI", plr, marathonPlayers)
37 elseif (arg == "time") then
38 if (not marathonCreated or marathonStarted) then
39 exports.CIThelp:dm("There is no marathon going on or it has already started", plr, 255, 25, 25)
40 return false
41 end
42 local remainingInSeconds = math.floor(getTimerDetails(startTimer)/1000)
43 exports.CIThelp:dm("Marathon starts in "..remainingInSeconds.." seconds ("..math.floor(remainingInSeconds / 60).." minutes)", plr, 0, 255, 0)
44 elseif (arg == "count") then
45 if (not marathonCreated) then
46 exports.CIThelp:dm("There is no marathon going on", plr, 255, 25, 25)
47 return false
48 end
49 exports.CIThelp:dm("There are currently "..#marathonPlayers.." player(s) participating in the marathon", plr, 0, 255, 0)
50 end
51end
52addCommandHandler("marathon", marathonCommands)
53
54function createMarathon(maxPlayers)
55 if (not client) then
56 return false
57 end
58 local adminLvl = exports.CITadmin:getPlayerAdminLevel(client)
59 if (not adminLvl or adminLvl < 2) then
60 return false
61 end
62 if (marathonStarted or marathonCreated) then
63 exports.CIThelp:dm("There is already a marathon going on", client, 255, 25, 25)
64 return false
65 end
66 for i, v in ipairs(getElementsByType("player")) do
67 exports.CIThelp:dm(getPlayerName(client).." started a marathon, use '/marathon warp' to join!", v, 128, 0, 128)
68 end
69 setElementDimension(client, eventDim)
70 setElementPosition(client, startX, startY, startZ)
71 local x, y, z = getElementPosition(client)
72 previousLocation[client] = {x, y, z}
73 exports.CIThelp:dm("You created a marathon, you have been warped to the start location", client, 0, 255, 0)
74 addEventHandler("onPlayerLogin", root, informOnLogin) -- will be removed once event starts
75 marathonCreated = true
76 startTimer = setTimer(startMarathon, TIME_UNTIL_START, 1)
77end
78addEvent("CITmarathon:createMarathon", true)
79addEventHandler("CITmarathon:createMarathon", root, createMarathon)
80
81function startMarathon()
82 removeEventHandler("onPlayerLogin", root, informOnLogin)
83 if (#marathonPlayers < MIN_PLAYERS) then
84 for i, v in ipairs(getElementsByType("player")) do
85 exports.CIThelp:dm("Not enough players gathered to start the marathon so it has been cancelled", v, 128, 0, 128)
86 end
87 endMarathon()
88 return false
89 end
90 marathonStarted = true
91 marathonPrize = #marathonPlayers * payPerParticipant
92 for i, v in ipairs(marathonPlayers) do
93 setElementFrozen(v, false)
94 triggerClientEvent(v, "CITmarathon:clientStartMarathon", v)
95 end
96 for i, v in ipairs(getElementsByType("player")) do
97 exports.CIThelp:dm("Marathon started, there are "..#marathonPlayers.." players competing for the prize of $"..marathonPrize, v, 128, 0, 128)
98 end
99 startTick = getTickCount()
100 addEventHandler("onPlayerWeaponSwitch", root, onWpnSwitch) -- will be removed once event ends
101end
102
103function informOnLogin()
104 exports.CIThelp:dm("There is a marathon going on, use '/marathon warp' to join!", source, 128, 0, 128)
105end
106
107function warpPlayerToMarathon(plr)
108 if (not plr) then
109 return false
110 end
111 if (isPlayerInMarathon(plr)) then
112 exports.CIThelp:dm("You are already in the marathon", plr, 255, 25, 25)
113 return false
114 end
115 if (marathonStarted or not marathonCreated) then
116 exports.CIThelp:dm("There is no marathon or it has already started", plr, 255, 25, 25)
117 return false
118 end
119 if (not exports.CITchecking:playerActionCheck(plr, {{"w", 0}, {"i", 0}, {"d", 0}, {"ld", 5}, {"s", "noveh", "noarrest", "nojailed", "nodead", "nopris", "nobc", "noguest", "nojetpack", "noglued", "nohit"}})) then
120 return false
121 end
122 if (MAX_PLAYERS and #marathonPlayers >= MAX_PLAYERS) then
123 exports.CIThelp:dm("Marathon already reached max players limit", plr, 255, 25, 25)
124 return false
125 end
126 local x, y, z = getElementPosition(plr)
127 previousLocation[plr] = {x, y, z}
128 previousSkin[plr] = getElementModel(plr)
129 setElementDimension(plr, eventDim)
130 setElementPosition(plr, startX, startY, startZ)
131 setElementModel(plr, marathonSkins[math.random(#marathonSkins)])
132 setElementFrozen(plr, true)
133 setPedWeaponSlot(plr, 0)
134 exports.CIThelp:dm("You have been warped to the marathon, use '/marathon leave' to leave", plr, 0, 255, 0)
135 marathonPlayers[#marathonPlayers + 1] = plr
136 triggerClientEvent(plr, "CITmarathon:showHighScores", plr, topTimes)
137
138 addEventHandler("onPlayerWasted", plr, onPlrWasted)
139end
140
141function warpPlayerOutOfMarathon(plr)
142 if (not plr) then
143 return false
144 end
145 if (not isPlayerInMarathon(plr)) then
146 return false
147 end
148 setElementDimension(plr, 0)
149 if (previousLocation[plr]) then
150 local x, y, z = unpack(previousLocation[plr])
151 setElementPosition(plr, x, y, z)
152 exports.CIThelp:dm("You have been returned to your previous position", plr, 0, 255, 0)
153 else
154 killPed(plr)
155 exports.CIThelp:dm("Couldn't find your previous position so you were killed instead", plr, 0, 255, 0)
156 end
157 if (previousSkin[plr]) then
158 setElementModel(plr, previousSkin[plr])
159 end
160 if (isElementFrozen(plr)) then
161 setElementFrozen(plr, false)
162 end
163 if (eventVehicles[plr]) then
164 destroyElement(eventVehicles[plr])
165 end
166 eventVehicles[plr] = nil
167 previousSkin[plr] = nil
168 previousLocation[plr] = nil
169 table.removeValue(marathonPlayers, plr)
170 triggerClientEvent(plr, "CITmarathon:clientOnPlrWasted", plr)
171 removeEventHandler("onPlayerWasted", plr, onPlrWasted)
172end
173
174function kickPlayerFromMarathon(plrName)
175 if (not client or not plrName) then
176 return false
177 end
178 local adminLvl = exports.CITadmin:getPlayerAdminLevel(client)
179 if (not adminLvl or adminLvl < 2) then
180 return false
181 end
182 local plr = getPlayerFromName(plrName)
183 if (not plr) then
184 exports.CIThelp:dm("Couldn't find player", client, 255, 25, 25)
185 return false
186 end
187 if (not isPlayerInMarathon(plr)) then
188 exports.CIThelp:dm("Player isn't in marathon", client, 255, 25, 25)
189 return false
190 end
191 warpPlayerOutOfMarathon(plr)
192 exports.CIThelp:dm("You kicked "..plrName.." from the marathon", client, 255, 25, 25)
193 exports.CIThelp:dm(getPlayerName(client).." kicked you from the marathon", plr, 255, 25, 25)
194
195 exports.CITlogs:logSomething("CITmarathon - "..getPlayerName(client).." kicked "..plrName, "events", client, plr)
196end
197addEvent("CITmarathon:kickPlayer", true)
198addEventHandler("CITmarathon:kickPlayer", root, kickPlayerFromMarathon)
199
200-- Main marathon functions
201
202function serverGiveVehicle(ID)
203 if (not client) then
204 return false
205 end
206 if (ID == 46) then
207 giveWeapon(client, ID, 1, true)
208 return true
209 end
210 local veh = getPedOccupiedVehicle(client)
211 if (veh) then
212 setElementModel(veh, ID)
213 else
214 if (eventVehicles[client]) then
215 destroyElement(eventVehicles[client])
216 eventVehicles[client] = nil
217 end
218 local x, y, z = getElementPosition(client)
219 local rx, ry, rz = getElementRotation(client)
220 eventVehicles[client] = createVehicle(ID, x, y, z, rx, ry, rz)
221 warpPedIntoVehicle(client, eventVehicles[client])
222 end
223end
224addEvent("CITmarathon:givePlayerVehicle", true)
225addEventHandler("CITmarathon:givePlayerVehicle", root, serverGiveVehicle)
226
227function serverDestroyVehicle(veh)
228 if (not client) then
229 return false
230 end
231 if (not eventVehicles[client]) then
232 return false
233 end
234 destroyElement(veh)
235 eventVehicles[client] = nil
236end
237addEvent("CITmarathon:destroyVehicle", true)
238addEventHandler("CITmarathon:destroyVehicle", root, serverDestroyVehicle)
239
240function restrictVehEnter(plr, seat, jacked)
241 if (jacked or seat ~= 0) then
242 cancelEvent()
243 end
244end
245addEventHandler("onVehicleStartEnter", resourceRoot, restrictVehEnter)
246
247function serverFinishMarathon()
248 if (not client) then
249 return false
250 end
251 local finishTime = ((getTickCount() - startTick) / 1000) / 60 -- this will be in minutes
252 finishTime = math.round(finishTime, 2)
253 if (isMarathonWon) then
254exports.CIThelp:dm("Congratulations, you finished the marathon in "..finishTime.."m", client, 0, 255, 0)
255 return true
256 end
257 isMarathonWon = true
258 local winnerName = getPlayerName(client)
259 exports.CIThelp:dm("Congratulations, you finished 1st place and won the marathon with a prize of $"..marathonPrize, client, 0, 255, 0)
260 exports.CIThelp:dm("You finished the marathon in "..finishTime.."m", client, 0, 255, 0)
261 exports.CITmoney:GPM(client, marathonPrize, "CITmarathon: won marathon", 1)
262 dbExec(db, "INSERT INTO `marathonTopTimes` (plrName, plrTime) VALUES (?, ?)", winnerName, finishTime)
263 topTimes[#topTimes + 1] = {name = winnerName, time = finishTime}
264 if (#topTimes > 1) then
265 table.sort(topTimes, function(a, b) return b.time > a.time end)
266 end
267 for i, v in ipairs(getElementsByType("player")) do
268 exports.CIThelp:dm(winnerName.." has won the marathon, congratulations!", v, 128, 0, 128)
269 end
270 exports.CIThelp:dm(winnerName.." has won the marathon, everyone will be automatically warped out in 60 seconds", marathonPlayers, 0, 255, 0)
271 setTimer(endMarathon, 60000, 1)
272end
273addEvent("CITmarathon:finishMarathon", true)
274addEventHandler("CITmarathon:finishMarathon", root, serverFinishMarathon)
275
276function endMarathon()
277 for i, v in ipairs(marathonPlayers) do
278 warpPlayerOutOfMarathon(v)
279 end
280 isMarathonWon = nil
281 marathonCreated = nil
282 marathonStarted = nil
283 marathonPrize = nil
284 startTick = nil
285 MAX_PLAYERS = nil
286
287 removeEventHandler("onPlayerWeaponSwitch", root, onWpnSwitch)
288end
289
290function onPlrWasted()
291 if (not isPlayerInMarathon(source)) then
292 return false
293 end
294 if (previousSkin[source]) then
295 setElementModel(source, previousSkin[source])
296 end
297 if (eventVehicles[source]) then
298 destroyElement(eventVehicles[source])
299 end
300 eventVehicles[source] = nil
301 previousSkin[source] = nil
302 previousLocation[source] = nil
303
304 table.removeValue(marathonPlayers, source)
305 triggerClientEvent(source, "CITmarathon:clientOnPlrWasted", source)
306
307 removeEventHandler("onPlayerWasted", source, onPlrWasted)
308 removeEventHandler("onPlayerWeaponSwitch", source, onWpnSwitch)
309end
310
311function onWpnSwitch()
312 if (not isPlayerInMarathon(source)) then
313 return false
314 end
315 setPedWeaponSlot(source, 0)
316end
317
318function loadTopTimes(query)
319 local results = dbPoll(query, 0)
320 if (not results or #results < 0) then
321 return false
322 end
323 for _, v in ipairs(results) do
324 topTimes[#topTimes + 1] = {name = v.plrName, time = v.plrTime}
325 end
326 if (#topTimes > 1) then
327 table.sort(topTimes, function(a, b) return b.time > a.time end)
328 end
329end
330
331function onResourceStart()
332 db = dbConnect("sqlite", ":/registry.db")
333 if (not db) then
334 return false
335 end
336 dbExec(db, "CREATE TABLE IF NOT EXISTS `marathonTopTimes` (`plrName` TEXT, `plrTime` REAL)")
337 dbQuery(loadTopTimes, db, "SELECT * FROM `marathonTopTimes`")
338end
339addEventHandler("onResourceStart", resourceRoot, onResourceStart)
340
341-- Utility functions
342
343function isPlayerInMarathon(plr)
344 if (not plr) then
345 return false
346 end
347 local exists = nil
348 for i, v in ipairs(marathonPlayers) do
349 if (v == plr) then
350 exists = true
351 break
352 end
353 end
354 if (exists) then
355 return true
356 end
357 return false
358end
359
360function table.removeValue(tab, val)
361 if (not tab or not val) then
362 return false
363 end
364 if (type(tab) ~= "table") then
365 return false
366 end
367 for i, v in ipairs(tab) do
368 if (v == val) then
369 table.remove(tab, i)
370 return i
371 end
372 end
373 return false
374end
375
376function math.round(number, decimals, method)
377 decimals = decimals or 0
378 local factor = 10 ^ decimals
379 if (method == "ceil" or method == "floor") then return math[method](number * factor) / factor
380 else return tonumber(("%."..decimals.."f"):format(number)) end
381end