· 8 years ago · Jul 01, 2018, 07:46 AM
1local timesMapPlayed = 0
2local lastMapPlayed = 0
3--
4-- racemidvote_server.lua
5--
6-- Mid-race random map vote and
7-- NextMapVote handled in this file
8--
9
10local lastVoteStarterName = ''
11local lastVoteStarterCount = 0
12
13----------------------------------------------------------------------------
14-- displayHilariarseMessage
15--
16-- Comedy gold
17----------------------------------------------------------------------------
18function displayHilariarseMessage( player )
19 if not player then
20 lastVoteStarterName = ''
21 else
22 local playerName = getPlayerName(player)
23 local msg = ''
24 if playerName == lastVoteStarterName then
25 lastVoteStarterCount = lastVoteStarterCount + 1
26 if lastVoteStarterCount == 5 then
27 msg = playerName .. ' started a vote. Hardly a suprise.'
28 elseif lastVoteStarterCount == 10 then
29 msg = 'Guess what! '..playerName .. ' started ANOTHER vote!'
30 elseif lastVoteStarterCount < 5 then
31 msg = playerName .. ' started another vote.'
32 else
33 msg = playerName .. ' continues to abuse the vote system.'
34 end
35 else
36 lastVoteStarterCount = 0
37 lastVoteStarterName = playerName
38 msg = playerName .. ' started a vote.'
39 end
40 outputRace( msg )
41 end
42end
43
44
45----------------------------------------------------------------------------
46-- displayKillerPunchLine
47--
48-- Sewing kits available in the foyer
49----------------------------------------------------------------------------
50function displayKillerPunchLine( player )
51 if lastVoteStarterName ~= '' then
52 outputRace( 'Offical news: Everybody hates ' .. lastVoteStarterName )
53 end
54end
55
56
57----------------------------------------------------------------------------
58-- startMidMapVoteForRandomMap
59--
60-- Start the vote menu if during a race and more than 30 seconds from the end
61-- No messages if this was not started by a player
62----------------------------------------------------------------------------
63function startMidMapVoteForRandomMap(player)
64
65 -- Check state and race time left
66 if not stateAllowsRandomMapVote() or g_CurrentRaceMode:getTimeRemaining() < 30000 then
67 if player then
68 outputRace( "I'm afraid I can't let you do that, " .. getPlayerName(player) .. ".", player )
69 end
70 return
71 end
72
73 displayHilariarseMessage( player )
74 exports.votemanager:stopPoll()
75
76 -- Actual vote started here
77 local pollDidStart = exports.votemanager:startPoll {
78 title='Do you want to change to a random map?',
79 percentage=51,
80 timeout=10,
81 allowchange=true,
82 visibleTo=getRootElement(),
83 [1]={'Yes', 'midMapVoteResult', getRootElement(), true},
84 [2]={'No', 'midMapVoteResult', getRootElement(), false;default=true},
85 }
86
87 -- Change state if vote did start
88 if pollDidStart then
89 gotoState('MidMapVote')
90 end
91
92end
93addCommandHandler('newzaaaaaaaaaaaaaaaaaaaaaaaaa',startMidMapVoteForRandomMap)
94
95
96----------------------------------------------------------------------------
97-- event midMapVoteResult
98--
99-- Called from the votemanager when the poll has completed
100----------------------------------------------------------------------------
101addEvent('midMapVoteResult')
102addEventHandler('midMapVoteResult', getRootElement(),
103 function( votedYes )
104 -- Change state back
105 if stateAllowsRandomMapVoteResult() then
106 gotoState('Running')
107 if votedYes then
108 startRandomMap()
109 else
110 displayKillerPunchLine()
111 end
112 end
113 end
114)
115
116
117
118----------------------------------------------------------------------------
119-- startRandomMap
120--
121-- Changes the current map to a random race map
122----------------------------------------------------------------------------
123function startRandomMap()
124
125 -- Handle forced nextmap setting
126 if maybeApplyForcedNextMap() then
127 return
128 end
129
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
140end
141addEvent("chooseRandomMap",true)
142addEventHandler("chooseRandomMap",root,startRandomMap)
143
144----------------------------------------------------------------------------
145-- outputRace
146--
147-- Race color is defined in the settings
148----------------------------------------------------------------------------
149function outputRace(message, toElement)
150 toElement = toElement or g_Root
151 local r, g, b = getColorFromString(string.upper(get("color")))
152 if getElementType(toElement) == 'console' then
153 outputServerLog(message)
154 else
155 if toElement == rootElement then
156 outputServerLog(message)
157 end
158 if getElementType(toElement) == 'player' then
159 message = '[PM] ' .. message
160 end
161 outputChatBox(message, toElement, r, g, b)
162 end
163end
164
165
166----------------------------------------------------------------------------
167-- problemChangingMap
168--
169-- Sort it
170----------------------------------------------------------------------------
171function problemChangingMap()
172 outputRace( 'Changing to random map in 5 seconds' )
173 local currentMap = exports.mapmanager:getRunningGamemodeMap()
174 TimerManager.createTimerFor("resource","mapproblem"):setTimer(
175 function()
176 -- Check that something else hasn't already changed the map
177 if currentMap == exports.mapmanager:getRunningGamemodeMap() then
178 startRandomMap()
179 end
180 end,
181 math.random(4500,5500), 1 )
182end
183
184
185
186--
187--
188-- NextMapVote
189--
190--
191--
192
193local g_Poll
194
195----------------------------------------------------------------------------
196-- startNextMapVote
197--
198-- Start a votemap for the next map. Should only be called during the
199-- race state 'NextMapSelect'
200----------------------------------------------------------------------------
201function startNextMapVote()
202
203 exports.votemanager:stopPoll()
204
205 -- Handle forced nextmap setting
206 if maybeApplyForcedNextMap() then
207 return
208 end
209
210 -- Get all maps
211 local compatibleMaps = exports.mapmanager:getMapsCompatibleWithGamemode(getThisResource())
212
213 -- limit it to eight random maps
214 if #compatibleMaps > 8 then
215 math.randomseed(getTickCount())
216 repeat
217 table.remove(compatibleMaps, math.random(1, #compatibleMaps))
218 until #compatibleMaps == 8
219 elseif #compatibleMaps < 2 then
220 return false, errorCode.onlyOneCompatibleMap
221 end
222
223 -- mix up the list order
224 for i,map in ipairs(compatibleMaps) do
225 local swapWith = math.random(1, #compatibleMaps)
226 local temp = compatibleMaps[i]
227 compatibleMaps[i] = compatibleMaps[swapWith]
228 compatibleMaps[swapWith] = temp
229 end
230
231 local poll = {
232 title="Choose the next map:",
233 visibleTo=getRootElement(),
234 percentage=51,
235 timeout=10,
236 allowchange=true;
237 }
238
239 Â Â table.insert(poll, {"Random", 'chooseRandomMap', getRootElement()})
240 for index, map in ipairs(compatibleMaps) do
241 local mapName = getResourceInfo(map, "name") or getResourceName(map)
242 table.insert(poll, {mapName, 'nextMapVoteResult', getRootElement(), map})
243 end
244
245 local currentMap = exports.mapmanager:getRunningGamemodeMap()
246 if currentMap ~= lastMapPlayed then
247 timesMapPlayed = 1
248 lastMapPlayed = currentMap
249 end
250 if currentMap and timesMapPlayed <= 3 then -- map could be played max. 4 times in row
251 timesMapPlayed = timesMapPlayed + 1
252 table.insert(poll, {"Play again", 'nextMapVoteResult', getRootElement(), currentMap})
253 end
254
255 -- Allow addons to modify the poll
256 g_Poll = poll
257 triggerEvent('onPollStarting', g_Root, poll )
258 poll = g_Poll
259 g_Poll = nil
260
261 local pollDidStart = exports.votemanager:startPoll(poll)
262
263 if pollDidStart then
264 gotoState('NextMapVote')
265 addEventHandler("onPollEnd", getRootElement(), chooseRandomMap)
266 end
267
268 return pollDidStart
269end
270
271
272-- Used by addons in response to onPollStarting
273addEvent('onPollModified')
274addEventHandler('onPollModified', getRootElement(),
275 function( poll )
276 g_Poll = poll
277 end
278)
279
280
281function chooseRandomMap (chosen)
282 if not chosen then
283 cancelEvent()
284 math.randomseed(getTickCount())
285 exports.votemanager:finishPoll(1)
286 end
287 removeEventHandler("onPollEnd", getRootElement(), chooseRandomMap)
288end
289
290
291
292----------------------------------------------------------------------------
293-- event nextMapVoteResult
294--
295-- Called from the votemanager when the poll has completed
296----------------------------------------------------------------------------
297addEvent('nextMapVoteResult')
298addEventHandler('nextMapVoteResult', getRootElement(),
299 function( map )
300 if stateAllowsNextMapVoteResult() then
301 if not exports.mapmanager:changeGamemodeMap ( map, nil, true ) then
302 problemChangingMap()
303 end
304 end
305 end
306)
307
308
309
310----------------------------------------------------------------------------
311-- startMidMapVoteForRestartMap
312--
313-- Start the vote menu to restart the current map if during a race
314-- No messages if this was not started by a player
315----------------------------------------------------------------------------
316function startMidMapVoteForRestartMap(player)
317
318 -- Check state and race time left
319 if not stateAllowsRestartMapVote() then
320 if player then
321 outputRace( "I'm afraid I can't let you do that, " .. getPlayerName(player) .. ".", player )
322 end
323 return
324 end
325
326 displayHilariarseMessage( player )
327 exports.votemanager:stopPoll()
328
329 -- Actual vote started here
330 local pollDidStart = exports.votemanager:startPoll {
331 title='Do you want to restart the current map?',
332 percentage=51,
333 timeout=10,
334 allowchange=true,
335 visibleTo=getRootElement(),
336 [1]={'Yes', 'midMapRestartVoteResult', getRootElement(), true},
337 [2]={'No', 'midMapRestartVoteResult', getRootElement(), false;default=true},
338 }
339
340 -- Change state if vote did start
341 if pollDidStart then
342 gotoState('MidMapVote')
343 end
344
345end
346addCommandHandler('voteredofadssssssmadsfnadsfonodag',startMidMapVoteForRestartMap)
347
348
349----------------------------------------------------------------------------
350-- event midMapRestartVoteResult
351--
352-- Called from the votemanager when the poll has completed
353----------------------------------------------------------------------------
354addEvent('midMapRestartVoteResult')
355addEventHandler('midMapRestartVoteResult', getRootElement(),
356 function( votedYes )
357 -- Change state back
358 if stateAllowsRandomMapVoteResult() then
359 gotoState('Running')
360 if votedYes then
361 if not exports.mapmanager:changeGamemodeMap ( exports.mapmanager:getRunningGamemodeMap(), nil, true ) then
362 problemChangingMap()
363 end
364 else
365 displayKillerPunchLine()
366 end
367 end
368 end
369)
370
371addCommandHandler('redo',
372 function( player, command, value )
373 if isPlayerInACLGroup(player, g_GameOptions.admingroup) then
374 local currentMap = exports.mapmanager:getRunningGamemodeMap()
375 if currentMap then
376 outputChatBox('#c0c0c0Map restarted by #abcdef' .. getPlayerName(player), g_Root, 0, 240, 0, true)
377 if not exports.mapmanager:changeGamemodeMap (currentMap, nil, true) then
378 problemChangingMap()
379 end
380 else
381 outputRace("You can't restart the map because no map is running", player)
382 end
383 else
384 outputRace("IDIOT! You are not an Admin", player)
385 end
386 end
387)
388
389
390addCommandHandler('random',
391 function( player, command, value )
392 if isPlayerInACLGroup(player, g_GameOptions.admingroup) then
393 if not stateAllowsRandomMapVote() or g_CurrentRaceMode:getTimeRemaining() < 1000 then
394 outputRace( "Random command only works during a race and when no polls are running.", player )
395 else
396 local choice = {'curtailed', 'cut short', 'terminated', 'given the heave ho', 'dropkicked', 'expunged', 'put out of our misery', 'got rid of'}
397 outputChatBox('#c0c0c0Current map ' .. choice[math.random( 1, #choice )] .. ' by #abcdef' .. getPlayerName(player), g_Root, 0, 240, 0, true)
398 startRandomMap()
399 end
400 end
401 end
402)
403
404
405----------------------------------------------------------------------------
406-- maybeApplyForcedNextMap
407--
408-- Returns true if nextmap did override
409----------------------------------------------------------------------------
410function maybeApplyForcedNextMap()
411 if g_ForcedNextMap then
412 local map = g_ForcedNextMap
413 g_ForcedNextMap = nil
414 g_IgnoreSpawnCountProblems = map -- Uber hack 4000
415 if not exports.mapmanager:changeGamemodeMap ( map, nil, true ) then
416 outputWarning( 'Forced next map failed' )
417 return false
418 end
419 return true
420 end
421 return false
422end
423
424---------------------------------------------------------------------------
425--
426-- Testing
427--
428--
429--
430---------------------------------------------------------------------------
431addCommandHandler('forcevotedsadddddddddadsa',
432 function( player, command, value )
433 if not _TESTING and not isPlayerInACLGroup(player, g_GameOptions.admingroup) then
434 return
435 end
436 startNextMapVote()
437 end
438)
439
440
441---------------------------------------------------------------------------
442--
443-- getRandomMapCompatibleWithGamemode
444--
445-- This should go in mapmanager, but ACL needs doing
446--
447---------------------------------------------------------------------------
448
449addEventHandler('onResourceStart', getRootElement(),
450 function( res )
451 if exports.mapmanager:isMap( res ) then
452 setMapLastTimePlayed( res )
453 end
454 end
455)
456
457function getRandomMapCompatibleWithGamemode( gamemode, oldestPercentage, minSpawnCount )
458
459 -- Get all relevant maps
460 local compatibleMaps = exports.mapmanager:getMapsCompatibleWithGamemode( gamemode )
461
462 if #compatibleMaps == 0 then
463 outputDebugString( 'getRandomMapCompatibleWithGamemode: No maps.', 1 )
464 return false
465 end
466
467 -- Sort maps by time since played
468 local sortList = {}
469 for i,map in ipairs(compatibleMaps) do
470 sortList[i] = {}
471 sortList[i].map = map
472 sortList[i].lastTimePlayed = getMapLastTimePlayed( map )
473 end
474
475 table.sort( sortList, function(a, b) return a.lastTimePlayed > b.lastTimePlayed end )
476
477 -- Use the bottom n% of maps as the initial selection pool
478 local cutoff = #sortList - math.floor( #sortList * oldestPercentage / 100 )
479
480 outputDebug( 'RANDMAP', 'getRandomMapCompatibleWithGamemode' )
481 outputDebug( 'RANDMAP', ''
482 .. ' minSpawns:' .. tostring( minSpawnCount )
483 .. ' nummaps:' .. tostring( #sortList )
484 .. ' cutoff:' .. tostring( cutoff )
485 .. ' poolsize:' .. tostring( #sortList - cutoff + 1 )
486 )
487
488 math.randomseed( getTickCount() % 50000 )
489 local fallbackMap
490 while #sortList > 0 do
491 -- Get random item from range
492 local idx = math.random( cutoff, #sortList )
493 local map = sortList[idx].map
494
495 if not minSpawnCount or minSpawnCount <= getMapSpawnPointCount( map ) then
496 outputDebug( 'RANDMAP', ''
497 .. ' ++ using map:' .. tostring( getResourceName( map ) )
498 .. ' spawns:' .. tostring( getMapSpawnPointCount( map ) )
499 .. ' age:' .. tostring( getRealTimeSeconds() - getMapLastTimePlayed( map ) )
500 )
501 return map
502 end
503
504 -- Remember best match incase we cant find any with enough spawn points
505 if not fallbackMap or getMapSpawnPointCount( fallbackMap ) < getMapSpawnPointCount( map ) then
506 fallbackMap = map
507 end
508
509 outputDebug( 'RANDMAP', ''
510 .. ' skip:' .. tostring( getResourceName( map ) )
511 .. ' spawns:' .. tostring( getMapSpawnPointCount( map ) )
512 .. ' age:' .. tostring( getRealTimeSeconds() - getMapLastTimePlayed( map ) )
513 )
514
515 -- If map not good enough, remove from the list and try another
516 table.remove( sortList, idx )
517 -- Move cutoff up the list if required
518 cutoff = math.min( cutoff, #sortList )
519 end
520
521 -- No maps found - use best match
522 outputDebug( 'RANDMAP', ''
523 .. ' ** fallback map:' .. tostring( getResourceName( fallbackMap ) )
524 .. ' spawns:' .. tostring( getMapSpawnPointCount( fallbackMap ) )
525 .. ' ageLstPlyd:' .. tostring( getRealTimeSeconds() - getMapLastTimePlayed( fallbackMap ) )
526 )
527 return fallbackMap
528end
529
530-- Look for spawnpoints in map file
531-- Not very quick as it loads the map file everytime
532function countSpawnPointsInMap(res)
533 local count = 0
534 local meta = xmlLoadFile(':' .. getResourceName(res) .. '/' .. 'meta.xml')
535 if meta then
536 local mapnode = xmlFindChild(meta, 'map', 0) or xmlFindChild(meta, 'race', 0)
537 local filename = mapnode and xmlNodeGetAttribute(mapnode, 'src')
538 xmlUnloadFile(meta)
539 if filename then
540 local map = xmlLoadFile(':' .. getResourceName(res) .. '/' .. filename)
541 if map then
542 while xmlFindChild(map, 'spawnpoint', count) do
543 count = count + 1
544 end
545 xmlUnloadFile(map)
546 end
547 end
548 end
549 return count
550end
551
552---------------------------------------------------------------------------
553-- g_MapInfoList access
554---------------------------------------------------------------------------
555local g_MapInfoList
556
557function getMapLastTimePlayed( map )
558 local mapInfo = getMapInfo( map )
559 return mapInfo.lastTimePlayed or 0
560end
561
562function setMapLastTimePlayed( map, time )
563 time = time or getRealTimeSeconds()
564 local mapInfo = getMapInfo( map )
565 mapInfo.lastTimePlayed = time
566 mapInfo.playedCount = ( mapInfo.playedCount or 0 ) + 1
567 saveMapInfoItem( map, mapInfo )
568end
569
570function getMapSpawnPointCount( map )
571 local mapInfo = getMapInfo( map )
572 if not mapInfo.spawnPointCount then
573 mapInfo.spawnPointCount = countSpawnPointsInMap( map )
574 saveMapInfoItem( map, mapInfo )
575 end
576 return mapInfo.spawnPointCount
577end
578
579function getMapInfo( map )
580 if not g_MapInfoList then
581 loadMapInfoAll()
582 end
583 if not g_MapInfoList[map] then
584 g_MapInfoList[map] = {}
585 end
586 local mapInfo = g_MapInfoList[map]
587 if mapInfo.loadTime ~= getResourceLoadTime(map) then
588 -- Reset or clear data that may change between loads
589 mapInfo.loadTime = getResourceLoadTime( map )
590 mapInfo.spawnPointCount = false
591 end
592 return mapInfo
593end
594
595
596---------------------------------------------------------------------------
597-- g_MapInfoList <-> database
598---------------------------------------------------------------------------
599function sqlString(value)
600 value = tostring(value) or ''
601 return "'" .. value:gsub( "(['])", "''" ) .. "'"
602end
603
604function sqlInt(value)
605 return tonumber(value) or 0
606end
607
608function getTableName(value)
609 return sqlString( 'race_mapmanager_maps' )
610end
611
612function ensureTableExists()
613 local cmd = ( 'CREATE TABLE IF NOT EXISTS ' .. getTableName() .. ' ('
614 .. 'resName TEXT UNIQUE'
615 .. ', infoName TEXT '
616 .. ', spawnPointCount INTEGER'
617 .. ', playedCount INTEGER'
618 .. ', lastTimePlayedText TEXT'
619 .. ', lastTimePlayed INTEGER'
620 .. ')' )
621 executeSQLQuery( cmd )
622end
623
624-- Load all rows into g_MapInfoList
625function loadMapInfoAll()
626 ensureTableExists()
627 local rows = executeSQLQuery( 'SELECT * FROM ' .. getTableName() )
628 g_MapInfoList = {}
629 for i,row in ipairs(rows) do
630 local map = getResourceFromName( row.resName )
631 if map then
632 local mapInfo = getMapInfo( map )
633 mapInfo.playedCount = row.playedCount
634 mapInfo.lastTimePlayed = row.lastTimePlayed
635 end
636 end
637end
638
639-- Save one row
640function saveMapInfoItem( map, info )
641 executeSQLQuery( 'BEGIN TRANSACTION' )
642
643 ensureTableExists()
644
645 local cmd = ( 'INSERT OR IGNORE INTO ' .. getTableName() .. ' VALUES ('
646 .. '' .. sqlString( getResourceName( map ) )
647 .. ',' .. sqlString( "" )
648 .. ',' .. sqlInt( 0 )
649 .. ',' .. sqlInt( 0 )
650 .. ',' .. sqlString( "" )
651 .. ',' .. sqlInt( 0 )
652 .. ')' )
653 executeSQLQuery( cmd )
654
655 cmd = ( 'UPDATE ' .. getTableName() .. ' SET '
656 .. 'infoName=' .. sqlString( getResourceInfo( map, "name" ) )
657 .. ',spawnPointCount=' .. sqlInt( info.spawnPointCount )
658 .. ',playedCount=' .. sqlInt( info.playedCount )
659 .. ',lastTimePlayedText=' .. sqlString( info.lastTimePlayed and info.lastTimePlayed > 0 and getRealDateTimeString(getRealTime(info.lastTimePlayed)) or "-" )
660 .. ',lastTimePlayed=' .. sqlInt( info.lastTimePlayed )
661 .. ' WHERE '
662 .. 'resName=' .. sqlString( getResourceName( map ) )
663 )
664 executeSQLQuery( cmd )
665
666 executeSQLQuery( 'END TRANSACTION' )
667end
668
669
670
671---------------------------------------------------------------------------
672--
673-- More things that should go in mapmanager
674--
675---------------------------------------------------------------------------
676
677addCommandHandler('checkmapyasakkardesimfdssdfsd',
678 function( player, command, ... )
679 local query = #{...}>0 and table.concat({...},' ') or nil
680 if query then
681 local map, errormsg = findMap( query )
682 outputRace( errormsg, player )
683 end
684 end
685)
686
687
688 addCommandHandler('nextmap',
689 function( player, command, ... )
690 local query = #{...}>0 and table.concat({...},' ') or nil
691 if not query then
692 if g_ForcedNextMap then
693 outputRace( 'Next map is ' .. getMapName( g_ForcedNextMap ), player )
694 else
695 outputRace( 'Next map is not set', player )
696 end
697 return
698 end
699 if not _TESTING and not isPlayerInACLGroup(player, g_GameOptions.admingroup) then
700 return
701 end
702 local map, errormsg = findMap( query )
703 if not map then
704 outputRace( errormsg, player )
705 return
706 end
707 if g_ForcedNextMap == map then
708 outputRace( 'Next map is already set to ' .. getMapName( g_ForcedNextMap ), player )
709 return
710 end
711 g_ForcedNextMap = map
712 outputChatBox('#c0c0c0Next map set to #abcdef' .. getMapName( g_ForcedNextMap ) .. '#c0c0c0 by #abcdef' .. getPlayerName( player ), g_Root, 0, 240, 0, true)
713 triggerClientEvent("setNextMap", getRootElement(), getMapName(g_ForcedNextMap))
714 end
715 )
716
717--Find a map which matches, or nil and a text message if there is not one match
718function findMap( query )
719 local maps = findMaps( query )
720
721 -- Make status string
722 local status = "Found " .. #maps .. " match" .. ( #maps==1 and "" or "es" )
723 for i=1,math.min(5,#maps) do
724 status = status .. ( i==1 and ": " or ", " ) .. "'" .. getMapName( maps[i] ) .. "'"
725 end
726 if #maps > 5 then
727 status = status .. " (" .. #maps - 5 .. " more)"
728 end
729
730 if #maps == 0 then
731 return nil, status .. " for '" .. query .. "'"
732 end
733 if #maps == 1 then
734 return maps[1], status
735 end
736 if #maps > 1 then
737 return nil, status
738 end
739end
740
741-- Find all maps which match the query string
742function findMaps( query )
743 local results = {}
744 --escape all meta chars
745 query = string.gsub(query, "([%*%+%?%.%(%)%[%]%{%}%\%/%|%^%$%-])","%%%1")
746 -- Loop through and find matching maps
747 for i,resource in ipairs(exports.mapmanager:getMapsCompatibleWithGamemode(getThisResource())) do
748 local resName = getResourceName( resource )
749 local infoName = getMapName( resource )
750
751 -- Look for exact match first
752 if query == resName or query == infoName then
753 return {resource}
754 end
755
756 -- Find match for query within infoName
757 if string.find( infoName:lower(), query:lower() ) then
758 table.insert( results, resource )
759 end
760 end
761 return results
762end
763
764function getMapName( map )
765 return getResourceInfo( map, "name" ) or getResourceName( map ) or "unknown"
766end
767
768
769
770 addEvent("onMapStarting",true)
771 addEventHandler("onMapStarting",getRootElement(),
772 function()
773 triggerClientEvent("setNextNil", getRootElement())
774 end)
775
776
777function setNextMap( ... )
778 local query = #{...}>0 and table.concat({...},' ') or nil
779 if not query then
780 if mapIsAlreadySet then
781 return false, "Next map is already set."
782 end
783 end
784 local map, errormsg = findMap( query )
785 if (not map) then
786 outputRace( errormsg, player )
787 return false, "Map not found."
788 end
789 if (mapIsAlreadySet == map) then
790 return false, 'Next map is already set to ' .. getMapName( mapIsAlreadySet )
791 end
792 mapIsAlreadySet = map
793 return true
794end