· 5 years ago · Jun 10, 2020, 03:10 AM
1--
2-- racemidvote_server.lua
3--
4-- Mid-race random map vote and
5-- NextMapVote handled in this file
6--
7--[[
8local lastVoteStarterName = ''
9local lastVoteStarterCount = 0
10
11----------------------------------------------------------------------------
12-- displayHilariarseMessage
13--
14-- Comedy gold
15----------------------------------------------------------------------------
16function displayHilariarseMessage( player )
17 if not player then
18 lastVoteStarterName = ''
19 else
20 local playerName = getPlayerName(player)
21 local msg = ''
22 if playerName == lastVoteStarterName then
23 lastVoteStarterCount = lastVoteStarterCount + 1
24 if lastVoteStarterCount == 5 then
25 msg = playerName .. ' started a vote. Hardly a suprise.'
26 elseif lastVoteStarterCount == 10 then
27 msg = 'Guess what! '..playerName .. ' started ANOTHER vote!'
28 elseif lastVoteStarterCount < 5 then
29 msg = playerName .. ' started another vote.'
30 else
31 msg = playerName .. ' continues to abuse the vote system.'
32 end
33 else
34 lastVoteStarterCount = 0
35 lastVoteStarterName = playerName
36 msg = playerName .. ' started a vote.'
37 end
38 outputRace( msg )
39 end
40end
41
42
43----------------------------------------------------------------------------
44-- displayKillerPunchLine
45--
46-- Sewing kits available in the foyer
47----------------------------------------------------------------------------
48function displayKillerPunchLine( player )
49 if lastVoteStarterName ~= '' then
50 outputRace( 'Offical news: Everybody hates ' .. lastVoteStarterName )
51 end
52end
53
54
55----------------------------------------------------------------------------
56-- startMidMapVoteForRandomMap
57--
58-- Start the vote menu if during a race and more than 30 seconds from the end
59-- No messages if this was not started by a player
60----------------------------------------------------------------------------
61function startMidMapVoteForRandomMap(player)
62
63 -- Check state and race time left
64 if not stateAllowsRandomMapVote() or g_CurrentRaceMode:getTimeRemaining() < 30000 then
65 if player then
66 outputRace( "I'm afraid I can't let you do that, " .. getPlayerName(player) .. ".", player )
67 end
68 return
69 end
70
71 displayHilariarseMessage( player )
72 exports.votemanager:stopPoll()
73
74 -- Actual vote started here
75 local pollDidStart = exports.votemanager:startPoll {
76 title='Do you want to change to a random map?',
77 percentage=51,
78 timeout=15,
79 allowchange=true,
80 visibleTo=getRootElement(),
81 [1]={'Yes', 'midMapVoteResult', getRootElement(), true},
82 [2]={'No', 'midMapVoteResult', getRootElement(), false;default=true},
83 }
84
85 -- Change state if vote did start
86 if pollDidStart then
87 gotoState('MidMapVote')
88 end
89
90end
91addCommandHandler('new',startMidMapVoteForRandomMap)
92
93
94----------------------------------------------------------------------------
95-- event midMapVoteResult
96--
97-- Called from the votemanager when the poll has completed
98----------------------------------------------------------------------------
99addEvent('midMapVoteResult')
100addEventHandler('midMapVoteResult', getRootElement(),
101 function( votedYes )
102 -- Change state back
103 if stateAllowsRandomMapVoteResult() then
104 gotoState('Running')
105 if votedYes then
106 startRandomMap()
107 else
108 displayKillerPunchLine()
109 end
110 end
111 end
112)
113]]
114
115
116----------------------------------------------------------------------------
117-- startRandomMap
118--
119-- Changes the current map to a random race map
120----------------------------------------------------------------------------
121function startRandomMap()
122 --for i,player in ipairs (getElementsByType("player")) do
123 --setElementData(player,"hasVoted","nope")
124 --end
125 if not redo then
126 -- Handle forced nextmap setting
127 if maybeApplyForcedNextMap() then
128 return
129 end
130 -- Get a random map chosen from the 10% of least recently player maps, with enough spawn points for all the players (if required)
131 local map = getRandomMapCompatibleWithGamemode( getThisResource(), 10, g_GameOptions.ghostmode and 0 or getTotalPlayerCount() )
132 if map then
133 g_IgnoreSpawnCountProblems = map -- Uber hack 4000
134 if not exports.mapmanager:changeGamemodeMap ( map, nil, true ) then
135 problemChangingMap()
136 end
137 else
138 outputWarning( 'startRandomMap failed' )
139 end
140 mapRestarted = true
141 else
142 local currentMap = exports.mapmanager:getRunningGamemodeMap()
143 exports.mapmanager:changeGamemodeMap (currentMap, nil, true)
144 mapRestarted = false
145 end
146 redo = false
147 playersVoted = 0
148end
149
150----------------------------------------------------------------------------
151-- outputRace
152--
153-- Race color is defined in the settings
154----------------------------------------------------------------------------
155function outputRace(message, toElement)
156 toElement = toElement or g_Root
157 local r, g, b = getColorFromString(string.upper(get("color")))
158 if getElementType(toElement) == 'console' then
159 outputServerLog(message)
160 else
161 if toElement == rootElement then
162 outputServerLog(message)
163 end
164 if getElementType(toElement) == 'player' then
165 message = '[PM] ' .. message
166 end
167 outputChatBox(message, toElement, r, g, b)
168 end
169end
170
171----------------------------------------------------------------------------
172-- problemChangingMap
173--
174-- Sort it
175----------------------------------------------------------------------------
176function problemChangingMap()
177 outputRace( 'Changing to random map in 5 seconds' )
178 local currentMap = exports.mapmanager:getRunningGamemodeMap()
179 TimerManager.createTimerFor("resource","mapproblem"):setTimer(
180 function()
181 -- Check that something else hasn't already changed the map
182 if currentMap == exports.mapmanager:getRunningGamemodeMap() then
183 startRandomMap()
184 end
185 end,
186 math.random(4500,5500), 1 )
187end
188
189--
190--
191-- NextMapVote
192--
193--
194--
195
196local g_Poll
197
198----------------------------------------------------------------------------
199-- startNextMapVote
200--
201-- Start a votemap for the next map. Should only be called during the
202-- race state 'NextMapSelect'
203----------------------------------------------------------------------------
204function startNextMapVote()
205
206 exports.votemanager:stopPoll()
207
208 -- Handle forced nextmap setting
209 if maybeApplyForcedNextMap() then
210 return
211 end
212
213 -- Get all maps
214 local compatibleMaps = exports.mapmanager:getMapsCompatibleWithGamemode(getThisResource())
215
216 -- limit it to eight random maps
217 if #compatibleMaps > 8 then
218 math.randomseed(getTickCount())
219 repeat
220 table.remove(compatibleMaps, math.random(1, #compatibleMaps))
221 until #compatibleMaps == 8
222 elseif #compatibleMaps < 2 then
223 return false, errorCode.onlyOneCompatibleMap
224 end
225
226 -- mix up the list order
227 for i,map in ipairs(compatibleMaps) do
228 local swapWith = math.random(1, #compatibleMaps)
229 local temp = compatibleMaps[i]
230 compatibleMaps[i] = compatibleMaps[swapWith]
231 compatibleMaps[swapWith] = temp
232 end
233
234 local poll = {
235 title="Choose the next map:",
236 visibleTo=getRootElement(),
237 percentage=51,
238 timeout=15,
239 allowchange=true;
240 }
241
242 for index, map in ipairs(compatibleMaps) do
243 local mapName = getResourceInfo(map, "name") or getResourceName(map)
244 table.insert(poll, {mapName, 'nextMapVoteResult', getRootElement(), map})
245 end
246
247 local currentMap = exports.mapmanager:getRunningGamemodeMap()
248 if currentMap then
249 table.insert(poll, {"Play again", 'nextMapVoteResult', getRootElement(), currentMap})
250 end
251
252 -- Allow addons to modify the poll
253 g_Poll = poll
254 triggerEvent('onPollStarting', g_Root, poll )
255 poll = g_Poll
256 g_Poll = nil
257
258 local pollDidStart = exports.votemanager:startPoll(poll)
259
260 if pollDidStart then
261 gotoState('NextMapVote')
262 addEventHandler("onPollEnd", getRootElement(), chooseRandomMap)
263 end
264
265 return pollDidStart
266end
267
268
269-- Used by addons in response to onPollStarting
270addEvent('onPollModified')
271addEventHandler('onPollModified', getRootElement(),
272 function( poll )
273 g_Poll = poll
274 end
275)
276
277
278function chooseRandomMap (chosen)
279 if not chosen then
280 cancelEvent()
281 math.randomseed(getTickCount())
282 exports.votemanager:finishPoll(1)
283 end
284 removeEventHandler("onPollEnd", getRootElement(), chooseRandomMap)
285end
286
287
288
289----------------------------------------------------------------------------
290-- event nextMapVoteResult
291--
292-- Called from the votemanager when the poll has completed
293----------------------------------------------------------------------------
294addEvent('nextMapVoteResult')
295addEventHandler('nextMapVoteResult', getRootElement(),
296 function( map )
297 if stateAllowsNextMapVoteResult() then
298 if not exports.mapmanager:changeGamemodeMap ( map, nil, true ) then
299 problemChangingMap()
300 end
301 end
302 end
303)
304
305
306
307----------------------------------------------------------------------------
308-- startMidMapVoteForRestartMap
309--
310-- Start the vote menu to restart the current map if during a race
311-- No messages if this was not started by a player
312----------------------------------------------------------------------------
313function startMidMapVoteForRestartMap(player)
314
315 -- Check state and race time left
316 if not stateAllowsRestartMapVote() then
317 if player then
318 outputRace( "I'm afraid I can't let you do that, " .. getPlayerName(player) .. ".", player )
319 end
320 return
321 end
322
323 displayHilariarseMessage( player )
324 exports.votemanager:stopPoll()
325
326 -- Actual vote started here
327 local pollDidStart = exports.votemanager:startPoll {
328 title='Do you want to restart the current map?',
329 percentage=51,
330 timeout=15,
331 allowchange=true,
332 visibleTo=getRootElement(),
333 [1]={'Yes', 'midMapRestartVoteResult', getRootElement(), true},
334 [2]={'No', 'midMapRestartVoteResult', getRootElement(), false;default=true},
335 }
336
337 -- Change state if vote did start
338 if pollDidStart then
339 gotoState('MidMapVote')
340 end
341
342end
343addCommandHandler('voteredo',startMidMapVoteForRestartMap)
344
345
346----------------------------------------------------------------------------
347-- event midMapRestartVoteResult
348--
349-- Called from the votemanager when the poll has completed
350----------------------------------------------------------------------------
351addEvent('midMapRestartVoteResult')
352addEventHandler('midMapRestartVoteResult', getRootElement(),
353 function( votedYes )
354 -- Change state back
355 if stateAllowsRandomMapVoteResult() then
356 gotoState('Running')
357 if votedYes then
358 if not exports.mapmanager:changeGamemodeMap ( exports.mapmanager:getRunningGamemodeMap(), nil, true ) then
359 problemChangingMap()
360 end
361 else
362 displayKillerPunchLine()
363 end
364 end
365 end
366)
367
368redo = false
369addCommandHandler('redo',
370 function( player, command, value )
371 if hasObjectPermissionTo(player,'general.adminpanel') then
372 if not redo then
373 redo = true
374 --outputChatBox('The actual map has set again by ' .. getPlayerName(player), g_Root, 0, 240, 0)
375 outputChatBox ( "#3381FF[SFR] #ffffffEl mapa actual se reiniciará por "..getPlayerNametagText(player), root, 255, 99, 99, true)
376 triggerClientEvent("onSetNextMapRedo",root,redo)
377 else
378 for i,playersVR in ipairs(getElementsByType ("player")) do
379 setElementData( playersVR, "hasVoted", "nope" )
380 end
381 redo = false
382 playersVoted = 0
383 mapRestarted = true
384 --outputChatBox('Restart map aborted by ' .. getPlayerName(player), g_Root, 255, 0, 0)
385 outputChatBox ( "#3381FF[SFR] #ffffffReinicio del mapa cancelado por "..getPlayerNametagText(player), root, 255, 99, 99, true)
386 triggerClientEvent("onSetNextMapRedo",root,redo)
387 end
388 triggerEvent("redoByRace",root,redo)
389 else
390 outputRace("Tú no eres Admin.", player)
391 end
392 end
393)
394
395
396addCommandHandler('random',
397 function( player, command, value )
398 if hasObjectPermissionTo(player,'general.adminpanel') then
399 if not stateAllowsRandomMapVote() or g_CurrentRaceMode:getTimeRemaining() < 1000 then
400 outputRace( "El comando aleatorio solo funciona durante una carrera y cuando no hay encuestas en marcha.", player )
401 else
402 local choice = {'curtailed', 'cut short', 'terminated', 'given the heave ho', 'dropkicked', 'expunged', 'put out of our misery', 'got rid of'}
403 --outputChatBox('Current map ' .. choice[math.random( 1, #choice )] .. ' by ' .. getPlayerName(player), g_Root, 0, 240, 0)
404 outputChatBox ( "#3381FF[SFR] #ffffffMapa actual " ..choice[math.random( 1, #choice )].." por "..getPlayerNametagText(player), root, 255, 99, 99, true)
405 startRandomMap()
406 end
407 end
408 end
409)
410
411
412----------------------------------------------------------------------------
413-- maybeApplyForcedNextMap
414--
415-- Returns true if nextmap did override
416----------------------------------------------------------------------------
417function maybeApplyForcedNextMap()
418 if g_ForcedNextMap then
419 local map = g_ForcedNextMap
420 g_ForcedNextMap = nil
421 g_IgnoreSpawnCountProblems = map -- Uber hack 4000
422 if not exports.mapmanager:changeGamemodeMap ( map, nil, true ) then
423 outputWarning( 'Forced next map failed' )
424 return false
425 end
426 playersVoted = 0
427 mapRestarted = true
428 return true
429 end
430 return false
431end
432
433---------------------------------------------------------------------------
434--
435-- Testing
436--
437--
438--
439---------------------------------------------------------------------------
440addCommandHandler('forcevote',
441 function( player, command, value )
442 if not _TESTING and not isPlayerInACLGroup(player, g_GameOptions.admingroup) then
443 return
444 end
445 startNextMapVote()
446 end
447)
448
449
450---------------------------------------------------------------------------
451--
452-- getRandomMapCompatibleWithGamemode
453--
454-- This should go in mapmanager, but ACL needs doing
455--
456---------------------------------------------------------------------------
457
458addEventHandler('onResourceStart', getRootElement(),
459 function( res )
460 if exports.mapmanager:isMap( res ) then
461 setMapLastTimePlayed( res )
462 end
463 end
464)
465
466function getRandomMapCompatibleWithGamemode( gamemode, oldestPercentage, minSpawnCount )
467
468 -- Get all relevant maps
469 local compatibleMaps = exports.mapmanager:getMapsCompatibleWithGamemode( gamemode )
470
471 if #compatibleMaps == 0 then
472 outputDebugString( 'getRandomMapCompatibleWithGamemode: No maps.', 1 )
473 return false
474 end
475
476 -- Sort maps by time since played
477 local sortList = {}
478 for i,map in ipairs(compatibleMaps) do
479 sortList[i] = {}
480 sortList[i].map = map
481 sortList[i].lastTimePlayed = getMapLastTimePlayed( map )
482 end
483
484 table.sort( sortList, function(a, b) return a.lastTimePlayed > b.lastTimePlayed end )
485
486 -- Use the bottom n% of maps as the initial selection pool
487 local cutoff = #sortList - math.floor( #sortList * oldestPercentage / 100 )
488
489 outputDebug( 'RANDMAP', 'getRandomMapCompatibleWithGamemode' )
490 outputDebug( 'RANDMAP', ''
491 .. ' minSpawns:' .. tostring( minSpawnCount )
492 .. ' nummaps:' .. tostring( #sortList )
493 .. ' cutoff:' .. tostring( cutoff )
494 .. ' poolsize:' .. tostring( #sortList - cutoff + 1 )
495 )
496
497 math.randomseed( getTickCount() % 50000 )
498 local fallbackMap
499 while #sortList > 0 do
500 -- Get random item from range
501 local idx = math.random( cutoff, #sortList )
502 local map = sortList[idx].map
503
504 if not minSpawnCount or minSpawnCount <= getMapSpawnPointCount( map ) then
505 outputDebug( 'RANDMAP', ''
506 .. ' ++ using map:' .. tostring( getResourceName( map ) )
507 .. ' spawns:' .. tostring( getMapSpawnPointCount( map ) )
508 .. ' age:' .. tostring( getRealTimeSeconds() - getMapLastTimePlayed( map ) )
509 )
510 return map
511 end
512
513 -- Remember best match incase we cant find any with enough spawn points
514 if not fallbackMap or getMapSpawnPointCount( fallbackMap ) < getMapSpawnPointCount( map ) then
515 fallbackMap = map
516 end
517
518 outputDebug( 'RANDMAP', ''
519 .. ' skip:' .. tostring( getResourceName( map ) )
520 .. ' spawns:' .. tostring( getMapSpawnPointCount( map ) )
521 .. ' age:' .. tostring( getRealTimeSeconds() - getMapLastTimePlayed( map ) )
522 )
523
524 -- If map not good enough, remove from the list and try another
525 table.remove( sortList, idx )
526 -- Move cutoff up the list if required
527 cutoff = math.min( cutoff, #sortList )
528 end
529
530 -- No maps found - use best match
531 outputDebug( 'RANDMAP', ''
532 .. ' ** fallback map:' .. tostring( getResourceName( fallbackMap ) )
533 .. ' spawns:' .. tostring( getMapSpawnPointCount( fallbackMap ) )
534 .. ' ageLstPlyd:' .. tostring( getRealTimeSeconds() - getMapLastTimePlayed( fallbackMap ) )
535 )
536 return fallbackMap
537end
538
539-- Look for spawnpoints in map file
540-- Not very quick as it loads the map file everytime
541function countSpawnPointsInMap(res)
542 local count = 0
543 local meta = xmlLoadFile(':' .. getResourceName(res) .. '/' .. 'meta.xml')
544 if meta then
545 local mapnode = xmlFindChild(meta, 'map', 0) or xmlFindChild(meta, 'race', 0)
546 local filename = mapnode and xmlNodeGetAttribute(mapnode, 'src')
547 xmlUnloadFile(meta)
548 if filename then
549 local map = xmlLoadFile(':' .. getResourceName(res) .. '/' .. filename)
550 if map then
551 while xmlFindChild(map, 'spawnpoint', count) do
552 count = count + 1
553 end
554 xmlUnloadFile(map)
555 end
556 end
557 end
558 return count
559end
560
561---------------------------------------------------------------------------
562-- g_MapInfoList access
563---------------------------------------------------------------------------
564local g_MapInfoList
565
566function getMapLastTimePlayed( map )
567 local mapInfo = getMapInfo( map )
568 return mapInfo.lastTimePlayed or 0
569end
570
571function setMapLastTimePlayed( map, time )
572 time = time or getRealTimeSeconds()
573 local mapInfo = getMapInfo( map )
574 mapInfo.lastTimePlayed = time
575 mapInfo.playedCount = ( mapInfo.playedCount or 0 ) + 1
576 saveMapInfoItem( map, mapInfo )
577end
578
579function getMapSpawnPointCount( map )
580 local mapInfo = getMapInfo( map )
581 if not mapInfo.spawnPointCount then
582 mapInfo.spawnPointCount = countSpawnPointsInMap( map )
583 saveMapInfoItem( map, mapInfo )
584 end
585 return mapInfo.spawnPointCount
586end
587
588function getMapInfo( map )
589 if not g_MapInfoList then
590 loadMapInfoAll()
591 end
592 if not g_MapInfoList[map] then
593 g_MapInfoList[map] = {}
594 end
595 local mapInfo = g_MapInfoList[map]
596 if mapInfo.loadTime ~= getResourceLoadTime(map) then
597 -- Reset or clear data that may change between loads
598 mapInfo.loadTime = getResourceLoadTime( map )
599 mapInfo.spawnPointCount = false
600 end
601 return mapInfo
602end
603
604function addOneMoreHunter()
605 local currentMap = exports.mapmanager:getRunningGamemodeMap()
606 if currentMap then
607 local mapInfo = getMapInfo( currentMap )
608 if mapInfo then
609 mapInfo.hunterCount = (tonumber(mapInfo.hunterCount) or 0) + 1
610 saveMapInfoItem( currentMap, mapInfo )
611 end
612 end
613end
614
615function addOneMoreToptime()
616 local currentMap = exports.mapmanager:getRunningGamemodeMap()
617 if currentMap then
618 local mapInfo = getMapInfo( currentMap )
619 if mapInfo then
620 mapInfo.toptimesCount = (tonumber(mapInfo.toptimesCount) or 0) + 1
621 saveMapInfoItem( currentMap, mapInfo )
622 end
623 end
624end
625addEvent("onPlayerToptimeImprovement",true)
626addEventHandler("onPlayerToptimeImprovement",getRootElement(), addOneMoreToptime)
627
628---------------------------------------------------------------------------
629-- g_MapInfoList <-> database
630---------------------------------------------------------------------------
631function sqlString(value)
632 value = tostring(value) or ''
633 return "'" .. value:gsub( "(['])", "''" ) .. "'"
634end
635
636function sqlInt(value)
637 return tonumber(value) or 0
638end
639
640function getTableName(value)
641 return sqlString( 'race_mapInfo_database' )
642end
643
644function ensureTableExists()
645 local cmd = ( 'CREATE TABLE IF NOT EXISTS ' .. getTableName() .. ' ('
646 .. 'resName TEXT UNIQUE'
647 .. ', infoName TEXT '
648 .. ', spawnPointCount INTEGER'
649 .. ', playedCount INTEGER'
650 .. ', lastTimePlayedText TEXT'
651 .. ', lastTimePlayed INTEGER'
652 .. ', hunterCount INTEGER'
653 .. ', toptimesCount INTEGER'
654 .. ')' )
655 executeSQLQuery( cmd )
656end
657
658-- Load all rows into g_MapInfoList
659function loadMapInfoAll()
660 ensureTableExists()
661 local rows = executeSQLQuery( 'SELECT * FROM ' .. getTableName() )
662 g_MapInfoList = {}
663 for i,row in ipairs(rows) do
664 local map = getResourceFromName( row.resName )
665 if map then
666 local mapInfo = getMapInfo( map )
667 mapInfo.playedCount = row.playedCount
668 mapInfo.lastTimePlayed = row.lastTimePlayed
669 mapInfo.hunterCount = row.hunterCount
670 mapInfo.toptimesCount = row.toptimesCount
671 end
672 end
673end
674
675-- Save one row
676function saveMapInfoItem( map, info )
677 executeSQLQuery( 'BEGIN TRANSACTION' )
678
679 ensureTableExists()
680
681 local cmd = ( 'INSERT OR IGNORE INTO ' .. getTableName() .. ' VALUES ('
682 .. '' .. sqlString( getResourceName( map ) )
683 .. ',' .. sqlString( "" )
684 .. ',' .. sqlInt( 0 )
685 .. ',' .. sqlInt( 0 )
686 .. ',' .. sqlString( "" )
687 .. ',' .. sqlInt( 0 )
688 .. ',' .. sqlInt( 0 )
689 .. ',' .. sqlInt( 0 )
690 .. ')' )
691 executeSQLQuery( cmd )
692
693 cmd = ( 'UPDATE ' .. getTableName() .. ' SET '
694 .. 'infoName=' .. sqlString( getResourceInfo( map, "name" ) )
695 .. ',spawnPointCount=' .. sqlInt( info.spawnPointCount )
696 .. ',playedCount=' .. sqlInt( info.playedCount )
697 .. ',lastTimePlayedText=' .. sqlString( info.lastTimePlayed and info.lastTimePlayed > 0 and getRealDateTimeString(getRealTime(info.lastTimePlayed)) or "-" )
698 .. ',lastTimePlayed=' .. sqlInt( info.lastTimePlayed )
699 .. ',hunterCount=' .. sqlInt( info.hunterCount )
700 .. ',toptimesCount=' .. sqlInt( info.toptimeCount )
701 .. ' WHERE '
702 .. 'resName=' .. sqlString( getResourceName( map ) )
703 )
704 executeSQLQuery( cmd )
705
706 executeSQLQuery( 'END TRANSACTION' )
707end
708
709
710
711---------------------------------------------------------------------------
712--
713-- More things that should go in mapmanager
714--
715---------------------------------------------------------------------------
716
717addCommandHandler('checkmap',
718 function( player, command, ... )
719 local query = #{...}>0 and table.concat({...},' ') or nil
720 if query then
721 local map, errormsg = findMap( query )
722 outputRace( errormsg, player )
723 end
724 end
725)
726
727addCommandHandler('nextmap',
728 function SetRaceNextMap( player, command, ... )
729 local query = #{...}>0 and table.concat({...},' ') or nil
730 if not query then
731 if g_ForcedNextMap then
732 outputRace( 'Next map is ' .. getMapName( g_ForcedNextMap ), player )
733 else
734 outputRace( 'Next map is not set', player )
735 end
736 return
737 end
738 if not _TESTING and not hasObjectPermissionTo(player,'general.adminpanel') then
739 return
740 end
741 local map, errormsg = findMap( query )
742 if not map then
743 outputRace( errormsg, player )
744 return
745 end
746 if g_ForcedNextMap == map then
747 outputRace( 'Next map is already set to ' .. getMapName( g_ForcedNextMap ), player )
748 return
749 end
750 g_ForcedNextMap = map
751 --outputChatBox('Next map set to ' .. getMapName( g_ForcedNextMap ) .. ' by ' .. getPlayerName( player ), g_Root, 0, 240, 0)
752 outputChatBox ( "#3381FF[SFR] #ffffffEl siguiente mapa es " ..getMapName( g_ForcedNextMap ).." gracias a "..getPlayerNametagText(player), root, 255, 99, 99, true)
753 g_MapInfo.nextmap = getMapName(g_ForcedNextMap)
754 triggerClientEvent("onSetNextMap",root,g_MapInfo.nextmap)
755 triggerEvent("nextMapByRace",root,true)
756 end
757)
758
759
760
761---------------------------------------------------------------------------
762--
763-- UserPanel
764--
765---------------------------------------------------------------------------
766--[[addEvent("onUseranelWantSetMap",true)
767addEventHandler("onUseranelWantSetMap",root,
768function ( ... )
769 local query = #{...}>0 and table.concat({...},' ') or nil
770 if not query then
771 return
772 end
773 local map, errormsg = findMap( query )
774 if not map then
775 return
776 end
777 if g_ForcedNextMap == map then
778 return
779 end
780 g_ForcedNextMap = map
781 triggerClientEvent("onSetNextMap", getRootElement(),getMapName( g_ForcedNextMap ))
782 g_MapInfo.nextmap = getMapName(g_ForcedNextMap)
783 outputChatBox(getMapName(g_ForcedNextMap))
784end
785)]]
786
787--=============================
788
789addEvent('onUserpanelWantNextmap',true)
790addEventHandler('onUserpanelWantNextmap',getRootElement(),
791function(tableValue)
792 if tableValue ~= 'Random map' then
793 local resourceName = tableValue[2]
794 if resourceName then
795 local map = getResourceFromName(resourceName)
796 if map then
797 g_ForcedNextMap = map
798 triggerClientEvent("onSetNextMap", getRootElement(),getMapName( g_ForcedNextMap ))
799 g_MapInfo.nextmap = getMapName(g_ForcedNextMap)
800 end
801 end
802 end
803end
804)
805
806function UpdateNext(...)
807 if g_ForcedNextMap then
808 g_MapInfo.nextmap = getMapName(g_ForcedNextMap)
809 else
810 g_MapInfo.nextmap = false
811 end
812 return
813end
814addEvent("onMapStarting",true)
815addEventHandler("onMapStarting",root,UpdateNext)
816
817---------------------------------------------------------------------------
818--
819-- Vote Redo
820--
821---------------------------------------------------------------------------
822local color = "#FFFFFF"
823VoteRedo = true
824playersVoted = 0
825mapRestarted = true
826
827addCommandHandler("turnVR",
828function( player, cmd)
829 if hasObjectPermissionTo(player,'general.adminpanel') then
830 if VoteRedo then
831 VoteRedo = false
832 outputChatBox("#3381FF[SFR] "..color.."Comando [/vr] Vote Redo #FF4040DESACTIVADO "..color.."por #ffffff"..getPlayerNametagText(player), root, 255, 99, 99, true)
833 else
834 VoteRedo = true
835 outputChatBox("#3381FF[SFR] "..color.."Comando [/vr] Vote Redo #adff2fACTIVADO "..color.."por #ffffff"..getPlayerNametagText(player), root, 255, 99, 99, true)
836 end
837 end
838end
839)
840
841addEventHandler ( "onPlayerJoin",root,
842function ()
843 setElementData( source, "hasVoted", "nope" )
844end
845)
846
847function PlayerVoted(thePlayer)
848 if VoteRedo then
849 if not stateAllowsRandomMapVote() or g_CurrentRaceMode:getTimeRemaining() < 1000 then
850 outputChatBox("#3381FF[SFR] "..color.."Comando solo habilitado con el juego en proceso.", thePlayer, 255, 99, 99, true)
851 return
852 end
853 if mapRestarted then
854 if redo then
855 outputChatBox("#3381FF[SFR] "..color.."El mapa ahora se reiniciará automáticamente.", thePlayer, 255, 99, 99, true)
856 return
857 end
858 if (getElementData( thePlayer, "hasVoted" ) == "nope") then
859 setElementData( thePlayer, "hasVoted", "voted" )
860 playersVoted = playersVoted + 1
861 outputChatBox("#3381FF[SFR] #ffffff("..playersVoted.."/".. math.floor(getPlayerCount() /2 + 0.5) ..")"..color.." El jugador #ffffff"..getPlayerNametagText(thePlayer).." "..color.."ha votado para reiniciar este mapa.", root, 255, 99, 99, true)
862 if playersVoted >= math.floor(getPlayerCount() /2 + 0.5) then
863 redo = true
864 outputChatBox ("#3381FF[SFR] "..color.."Votación finalizada, el mapa se reiniciará.", root, 255, 99, 99, true)
865 triggerClientEvent("onSetNextMapRedo",root,redo)
866 triggerEvent("redoByRace",root,redo)
867 end
868 else
869 outputChatBox("#3381FF[SFR] "..color.."Tú ya has votado.", thePlayer, 255, 99, 99, true)
870 end
871 else
872 outputChatBox("#3381FF[SFR] "..color.."Lo sentimos, este mapa ya ha sido reiniciado.", thePlayer, 255, 99, 99, true)
873 end
874 else
875 outputChatBox("#3381FF[SFR] "..color.."Lo sentimos, el Vote Redo está desactivado.", thePlayer, 255, 99, 99, true)
876 end
877end
878addCommandHandler ( "vr", PlayerVoted )
879
880
881---------------------------------------------------------------------------
882--
883-- DeleteMap
884--
885---------------------------------------------------------------------------
886
887addCommandHandler("deletemap",
888 function(thePlayer,command)
889 local currentMap = exports.mapmanager:getRunningGamemodeMap()
890 if currentMap then
891 if isPlayerInACLGroup(thePlayer, g_GameOptions.admingroup) then
892 local resourceState = getResourceState(currentMap)
893 if resourceState == "running" then
894 if stopResource(currentMap) then
895 setTimer(deleteMapWithDelay,1000,1,currentMap,thePlayer)
896 end
897 end
898 end
899 end
900 end
901)
902
903function deleteMapWithDelay(mapResource,thePlayer)
904 if mapResource then
905 local mapName = getMapName(mapResource)
906 outputChatBox ( "#3381FF[SFR] El mapa " ..mapName.." ha sido eliminado por "..getPlayerNametagText(thePlayer), root, 255, 99, 99, true)
907 deleteResource(mapResource)
908 startRandomMap()
909 end
910end
911
912---------------------------------------------------------------------------
913--
914-- pickup hunter
915--
916---------------------------------------------------------------------------
917
918addEvent('onPlayerPickUpRacePickup')
919addEventHandler('onPlayerPickUpRacePickup', g_Root,
920 function(number, sort, model)
921 if sort == "vehiclechange" then
922 if model == 425 then
923 if getElementData ( source, "state" ) ~= "alive" then
924 return
925 end
926 fixVehicle(getPedOccupiedVehicle(source))
927 setWeather(1)
928 setTime(0,0)
929 resetSkyGradient()
930 setMinuteDuration(2147483647)
931 end
932 end
933 end
934)
935
936--Find a map which matches, or nil and a text message if there is not one match
937function findMap( query )
938 local maps = findMaps( query )
939
940 -- Make status string
941 local status = "Found " .. #maps .. " match" .. ( #maps==1 and "" or "es" )
942 for i=1,math.min(5,#maps) do
943 status = status .. ( i==1 and ": " or ", " ) .. "'" .. getMapName( maps[i] ) .. "'"
944 end
945 if #maps > 5 then
946 status = status .. " (" .. #maps - 5 .. " more)"
947 end
948
949 if #maps == 0 then
950 return nil, status .. " for '" .. query .. "'"
951 end
952 if #maps == 1 then
953 return maps[1], status
954 end
955 if #maps > 1 then
956 return nil, status
957 end
958end
959
960-- Find all maps which match the query string
961function findMaps( query )
962 local results = {}
963 --escape all meta chars
964 query = string.gsub(query, "([%*%+%?%.%(%)%[%]%{%}%\%/%|%^%$%-])","%%%1")
965 -- Loop through and find matching maps
966 for i,resource in ipairs(exports.mapmanager:getMapsCompatibleWithGamemode(getThisResource())) do
967 local resName = getResourceName( resource )
968 local infoName = getMapName( resource )
969
970 -- Look for exact match first
971 if query == resName or query == infoName then
972 return {resource}
973 end
974
975 -- Find match for query within infoName
976 if string.find( infoName:lower(), query:lower() ) then
977 table.insert( results, resource )
978 end
979 end
980 return results
981end
982
983function getMapName( map )
984 return getResourceInfo( map, "name" ) or getResourceName( map ) or "unknown"
985end