· 7 years ago · Feb 23, 2019, 06:28 PM
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
10local mapIsAlreadySet = false
11local g_ForcedNextMap
12local mapBlock
13local hunterMsg = false
14
15----------------------------------------------------------------------------
16-- displayHilariarseMessage
17--
18-- Comedy gold
19----------------------------------------------------------------------------
20function displayHilariarseMessage( player )
21 if not player then
22 lastVoteStarterName = ''
23 else
24 local playerName = getPlayerName(player)
25 local msg = ''
26 if playerName == lastVoteStarterName then
27 lastVoteStarterCount = lastVoteStarterCount + 1
28 if lastVoteStarterCount == 5 then
29 msg = playerName .. ' started a vote. Hardly a suprise.'
30 elseif lastVoteStarterCount == 10 then
31 msg = 'Guess what! '..playerName .. ' started ANOTHER vote!'
32 elseif lastVoteStarterCount < 5 then
33 msg = playerName .. ' started another vote.'
34 else
35 msg = playerName .. ' continues to abuse the vote system.'
36 end
37 else
38 lastVoteStarterCount = 0
39 lastVoteStarterName = playerName
40 msg = playerName .. ' started a vote.'
41 end
42 outputRace( msg )
43 end
44end
45
46
47----------------------------------------------------------------------------
48-- displayKillerPunchLine
49--
50-- Sewing kits available in the foyer
51----------------------------------------------------------------------------
52function displayKillerPunchLine( player )
53 if lastVoteStarterName ~= '' then
54 outputRace( 'Offical news: Everybody hates ' .. lastVoteStarterName )
55 end
56end
57
58
59----------------------------------------------------------------------------
60-- startMidMapVoteForRandomMap
61--
62-- Start the vote menu if during a race and more than 30 seconds from the end
63-- No messages if this was not started by a player
64----------------------------------------------------------------------------
65function startMidMapVoteForRandomMap(player)
66
67 -- Check state and race time left
68 if not stateAllowsRandomMapVote() or g_CurrentRaceMode:getTimeRemaining() < 30000 then
69 if player then
70 outputRace( "I'm afraid I can't let you do that, " .. getPlayerName(player) .. ".", player )
71 end
72 return
73 end
74
75 displayHilariarseMessage( player )
76 exports.votemanager:stopPoll()
77
78 -- Actual vote started here
79 local pollDidStart = exports.votemanager:startPoll {
80 title='Do you want to change to a random map?',
81 percentage=51,
82 timeout=15,
83 allowchange=true,
84 visibleTo=getRootElement(),
85 [1]={'Yes', 'midMapVoteResult', getRootElement(), true},
86 [2]={'No', 'midMapVoteResult', getRootElement(), false;default=true},
87 }
88
89 -- Change state if vote did start
90 if pollDidStart then
91 gotoState('MidMapVote')
92 end
93
94end
95--addCommandHandler('new',startMidMapVoteForRandomMap)
96
97
98----------------------------------------------------------------------------
99-- event midMapVoteResult
100--
101-- Called from the votemanager when the poll has completed
102----------------------------------------------------------------------------
103addEvent('midMapVoteResult')
104addEventHandler('midMapVoteResult', getRootElement(),
105 function( votedYes )
106 -- Change state back
107 if stateAllowsRandomMapVoteResult() then
108 gotoState('Running')
109 if votedYes then
110 startRandomMap()
111 else
112 displayKillerPunchLine()
113 end
114 end
115 end
116)
117
118
119
120----------------------------------------------------------------------------
121-- startRandomMap
122--
123-- Changes the current map to a random race map
124----------------------------------------------------------------------------
125function startRandomMap()
126
127 -- Handle forced nextmap setting
128 if maybeApplyForcedNextMap() then
129 return
130 end
131
132 -- Get a random map chosen from the 10% of least recently player maps, with enough spawn points for all the players (if required)
133 local map = getRandomMapCompatibleWithGamemode( getThisResource(), 10, g_GameOptions.ghostmode and 0 or getTotalPlayerCount() )
134 if map then
135 g_IgnoreSpawnCountProblems = map -- Uber hack 4000
136 if not exports.mapmanager:changeGamemodeMap ( map, nil, true ) then
137 problemChangingMap()
138 end
139 else
140 outputWarning( 'startRandomMap failed' )
141 end
142end
143
144
145----------------------------------------------------------------------------
146-- outputRace
147--
148-- Race color is defined in the settings
149----------------------------------------------------------------------------
150function outputRace(message, toElement)
151 toElement = toElement or g_Root
152 local r, g, b = getColorFromString(string.upper(get("color")))
153 if getElementType(toElement) == 'console' then
154 outputServerLog(message)
155 else
156 if toElement == rootElement then
157 outputServerLog(message)
158 end
159 if getElementType(toElement) == 'player' then
160 message = '[PM] ' .. message
161 end
162 outputChatBox(message, toElement, r, g, b)
163 end
164end
165
166
167----------------------------------------------------------------------------
168-- problemChangingMap
169--
170-- Sort it
171----------------------------------------------------------------------------
172function problemChangingMap()
173 outputRace( 'Changing to random map in 5 seconds' )
174 local currentMap = exports.mapmanager:getRunningGamemodeMap()
175 TimerManager.createTimerFor("resource","mapproblem"):setTimer(
176 function()
177 -- Check that something else hasn't already changed the map
178 if currentMap == exports.mapmanager:getRunningGamemodeMap() then
179 startRandomMap()
180 end
181 end,
182 math.random(4500,5500), 1 )
183end
184
185
186
187--
188--
189-- NextMapVote
190--
191--
192--
193
194local g_Poll
195
196----------------------------------------------------------------------------
197-- startNextMapVote
198--
199-- Start a votemap for the next map. Should only be called during the
200-- race state 'NextMapSelect'
201----------------------------------------------------------------------------
202function startNextMapVote()
203
204 exports.votemanager:stopPoll()
205
206 -- Handle forced nextmap setting
207 if maybeApplyForcedNextMap() then
208 return
209 end
210
211 -- Get all maps
212 local compatibleMaps = exports.mapmanager:getMapsCompatibleWithGamemode(getThisResource())
213
214 -- limit it to eight random maps
215 if #compatibleMaps > 8 then
216 math.randomseed(getTickCount())
217 repeat
218 table.remove(compatibleMaps, math.random(1, #compatibleMaps))
219 until #compatibleMaps == 8
220 elseif #compatibleMaps < 2 then
221 return false, errorCode.onlyOneCompatibleMap
222 end
223
224 -- mix up the list order
225 for i,map in ipairs(compatibleMaps) do
226 local swapWith = math.random(1, #compatibleMaps)
227 local temp = compatibleMaps[i]
228 compatibleMaps[i] = compatibleMaps[swapWith]
229 compatibleMaps[swapWith] = temp
230 end
231
232 local poll = {
233 title="Choose the next map:",
234 visibleTo=getRootElement(),
235 percentage=51,
236 timeout=15,
237 allowchange=true;
238 }
239
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 then
247 table.insert(poll, {"Play again", 'nextMapVoteResult', getRootElement(), currentMap})
248 end
249
250 -- Allow addons to modify the poll
251 g_Poll = poll
252 triggerEvent('onPollStarting', g_Root, poll )
253 poll = g_Poll
254 g_Poll = nil
255
256 local pollDidStart = exports.votemanager:startPoll(poll)
257
258 if pollDidStart then
259 gotoState('NextMapVote')
260 addEventHandler("onPollEnd", getRootElement(), chooseRandomMap)
261 end
262
263 return pollDidStart
264end
265
266
267-- Used by addons in response to onPollStarting
268addEvent('onPollModified')
269addEventHandler('onPollModified', getRootElement(),
270 function( poll )
271 g_Poll = poll
272 end
273)
274
275
276function chooseRandomMap (chosen)
277 if not chosen then
278 cancelEvent()
279 math.randomseed(getTickCount())
280 exports.votemanager:finishPoll(1)
281 end
282 removeEventHandler("onPollEnd", getRootElement(), chooseRandomMap)
283end
284
285
286
287----------------------------------------------------------------------------
288-- event nextMapVoteResult
289--
290-- Called from the votemanager when the poll has completed
291----------------------------------------------------------------------------
292addEvent('nextMapVoteResult')
293addEventHandler('nextMapVoteResult', getRootElement(),
294 function( map )
295 if stateAllowsNextMapVoteResult() then
296 if not exports.mapmanager:changeGamemodeMap ( map, nil, true ) then
297 problemChangingMap()
298 end
299 end
300 end
301)
302
303
304
305----------------------------------------------------------------------------
306-- startMidMapVoteForRestartMap
307--
308-- Start the vote menu to restart the current map if during a race
309-- No messages if this was not started by a player
310----------------------------------------------------------------------------
311function startMidMapVoteForRestartMap(player)
312
313 -- Check state and race time left
314 if not stateAllowsRestartMapVote() then
315 if player then
316 outputRace( "I'm afraid I can't let you do that, " .. getPlayerName(player) .. ".", player )
317 end
318 return
319 end
320
321 displayHilariarseMessage( player )
322 exports.votemanager:stopPoll()
323
324 -- Actual vote started here
325 local pollDidStart = exports.votemanager:startPoll {
326 title='Do you want to restart the current map?',
327 percentage=51,
328 timeout=15,
329 allowchange=true,
330 visibleTo=getRootElement(),
331 [1]={'Yes', 'midMapRestartVoteResult', getRootElement(), true},
332 [2]={'No', 'midMapRestartVoteResult', getRootElement(), false;default=true},
333 }
334
335 -- Change state if vote did start
336 if pollDidStart then
337 gotoState('MidMapVote')
338 end
339
340end
341--addCommandHandler('voteredo',startMidMapVoteForRestartMap)
342
343
344----------------------------------------------------------------------------
345-- event midMapRestartVoteResult
346--
347-- Called from the votemanager when the poll has completed
348----------------------------------------------------------------------------
349addEvent('midMapRestartVoteResult')
350addEventHandler('midMapRestartVoteResult', getRootElement(),
351 function( votedYes )
352 -- Change state back
353 if stateAllowsRandomMapVoteResult() then
354 gotoState('Running')
355 if votedYes then
356 if not exports.mapmanager:changeGamemodeMap ( exports.mapmanager:getRunningGamemodeMap(), nil, true ) then
357 problemChangingMap()
358 end
359 else
360 displayKillerPunchLine()
361 end
362 end
363 end
364)
365
366addCommandHandler('redo',
367 function( player, command, value )
368 if isPlayerInACLGroup(player, g_GameOptions.admingroup) then
369 local currentMap = exports.mapmanager:getRunningGamemodeMap()
370 --if currentMap then
371 if currentMap then
372 --if not g_ForcedNextMap then
373 outputChatBox('#FF6400M#ffffffap #FF6400r#ffffffestarted #FF6400b#ffffffy ' .. getPlayerNametagText(player), g_Root, 0, 240, 0,true)
374 if not exports.mapmanager:changeGamemodeMap (currentMap, nil, true) then
375 problemChangingMap()
376 --end
377 end
378 else
379 outputRace("You can't restart the map because no map is running", player)
380 end
381 else
382 outputRace("You are not an Admin", player)
383 end
384 end
385)
386
387
388addCommandHandler('random',
389 function( player, command, value )
390 if isPlayerInACLGroup(player, g_GameOptions.admingroup) or isPlayerInACLGroup(player, "Moderator") or isPlayerInACLGroup(player, "SuperModerator") then
391 if not stateAllowsRandomMapVote() or g_CurrentRaceMode:getTimeRemaining() < 1000 then
392 outputRace( "Random command only works during a race and when no polls are running.", player )
393 else
394 local choice = {'#FF6400S#fffffftarted'}
395 outputChatBox("#FF6400R#ffffffandom~> #FF6400N#ffffffext #FF6400M#ffffffap " .. choice[math.random( 1, #choice )] .. " #FF6400b#ffffffy " .. getPlayerNametagText(player), g_Root, 0, 240, 0, true)
396 startRandomMap()
397 end
398 end
399 end
400)
401
402function RGBToHex(red, green, blue, alpha)
403 if((red < 0 or red > 255 or green < 0 or green > 255 or blue < 0 or blue > 255) or (alpha and (alpha < 0 or alpha > 255))) then
404 return nil
405 end
406 if(alpha) then
407 return string.format("#%.2X%.2X%.2X%.2X", red,green,blue,alpha)
408 else
409 return string.format("#%.2X%.2X%.2X", red,green,blue)
410 end
411end
412
413
414----------------------------------------------------------------------------
415-- maybeApplyForcedNextMap
416--
417-- Returns true if nextmap did override
418----------------------------------------------------------------------------
419function maybeApplyForcedNextMap()
420 if g_ForcedNextMap then
421 local map = g_ForcedNextMap
422 g_ForcedNextMap = nil
423 g_IgnoreSpawnCountProblems = map -- Uber hack 4000
424 if not exports.mapmanager:changeGamemodeMap ( map, nil, true ) then
425 outputWarning( 'Forced next map failed' )
426 return false
427 end
428 return true
429 end
430 return false
431end
432
433---------------------------------------------------------------------------
434--
435-- Testing
436--
437--
438--
439---------------------------------------------------------------------------
440--addCommandHandler('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
466addEvent('onPlayerPickUpRacePickup')
467addEventHandler("onPlayerPickUpRacePickup", getRootElement(),
468function (number, sort, model)
469 if sort == "vehiclechange" then
470 if model == 425 then
471 local mapname = call(getResourceFromName("mapmanager"),"getRunningGamemodeMap")
472 setHunterOnMap(mapname)
473 if not hunterMsg then
474 outputChatBox( "#FF6400H#ffffffunter#FF6400A#fffffflert~> #FF6400H#ffffffunter #FF6400r#ffffffeached! Sky and water will be reseted to default color!", getRootElement(),255,255,255,true )
475 triggerClientEvent("onServerSendHunterMessage",getRootElement(),hunterHundleInfo)
476 hunterMsg = true
477 end
478 if getActivePlayerCount() <= 1 then
479 setElementHealth(source, 0)
480 end
481 end
482 end
483end)
484
485
486function toptimeMapAdd()
487 local mapname = call(getResourceFromName("mapmanager"),"getRunningGamemodeMap")
488 local mapInfo = getMapInfo( mapname )
489 mapInfo.toptimes = ( mapInfo.toptimes or 0 ) + 1
490 saveMapInfoItem( mapname, mapInfo )
491end
492addEvent("onPlayerToptimeImprovement",true)
493addEventHandler("onPlayerToptimeImprovement", getRootElement(),toptimeMapAdd)
494
495
496function getRandomMapCompatibleWithGamemode( gamemode, oldestPercentage, minSpawnCount )
497
498 -- Get all relevant maps
499 local compatibleMaps = exports.mapmanager:getMapsCompatibleWithGamemode( gamemode )
500
501 if #compatibleMaps == 0 then
502 outputDebugString( 'getRandomMapCompatibleWithGamemode: No maps.', 1 )
503 return false
504 end
505
506 -- Sort maps by time since played
507 local sortList = {}
508 for i,map in ipairs(compatibleMaps) do
509 sortList[i] = {}
510 sortList[i].map = map
511 sortList[i].lastTimePlayed = getMapLastTimePlayed( map )
512 end
513
514 table.sort( sortList, function(a, b) return a.lastTimePlayed > b.lastTimePlayed end )
515
516 -- Use the bottom n% of maps as the initial selection pool
517 local cutoff = #sortList - math.floor( #sortList * oldestPercentage / 100 )
518
519 outputDebug( 'RANDMAP', 'getRandomMapCompatibleWithGamemode' )
520 outputDebug( 'RANDMAP', ''
521 .. ' minSpawns:' .. tostring( minSpawnCount )
522 .. ' nummaps:' .. tostring( #sortList )
523 .. ' cutoff:' .. tostring( cutoff )
524 .. ' poolsize:' .. tostring( #sortList - cutoff + 1 )
525 )
526
527 math.randomseed( getTickCount() % 50000 )
528 local fallbackMap
529 while #sortList > 0 do
530 -- Get random item from range
531 local idx = math.random( cutoff, #sortList )
532 local map = sortList[idx].map
533
534 if not minSpawnCount or minSpawnCount <= getMapSpawnPointCount( map ) then
535 outputDebug( 'RANDMAP', ''
536 .. ' ++ using map:' .. tostring( getResourceName( map ) )
537 .. ' spawns:' .. tostring( getMapSpawnPointCount( map ) )
538 .. ' age:' .. tostring( getRealTimeSeconds() - getMapLastTimePlayed( map ) )
539 )
540 return map
541 end
542
543 -- Remember best match incase we cant find any with enough spawn points
544 if not fallbackMap or getMapSpawnPointCount( fallbackMap ) < getMapSpawnPointCount( map ) then
545 fallbackMap = map
546 end
547
548 outputDebug( 'RANDMAP', ''
549 .. ' skip:' .. tostring( getResourceName( map ) )
550 .. ' spawns:' .. tostring( getMapSpawnPointCount( map ) )
551 .. ' age:' .. tostring( getRealTimeSeconds() - getMapLastTimePlayed( map ) )
552 )
553
554 -- If map not good enough, remove from the list and try another
555 table.remove( sortList, idx )
556 -- Move cutoff up the list if required
557 cutoff = math.min( cutoff, #sortList )
558 end
559
560 -- No maps found - use best match
561 outputDebug( 'RANDMAP', ''
562 .. ' ** fallback map:' .. tostring( getResourceName( fallbackMap ) )
563 .. ' spawns:' .. tostring( getMapSpawnPointCount( fallbackMap ) )
564 .. ' ageLstPlyd:' .. tostring( getRealTimeSeconds() - getMapLastTimePlayed( fallbackMap ) )
565 )
566 return fallbackMap
567end
568
569-- Look for spawnpoints in map file
570-- Not very quick as it loads the map file everytime
571function countSpawnPointsInMap(res)
572 local count = 0
573 local meta = xmlLoadFile(':' .. getResourceName(res) .. '/' .. 'meta.xml')
574 if meta then
575 local mapnode = xmlFindChild(meta, 'map', 0) or xmlFindChild(meta, 'race', 0)
576 local filename = mapnode and xmlNodeGetAttribute(mapnode, 'src')
577 xmlUnloadFile(meta)
578 if filename then
579 local map = xmlLoadFile(':' .. getResourceName(res) .. '/' .. filename)
580 if map then
581 while xmlFindChild(map, 'spawnpoint', count) do
582 count = count + 1
583 end
584 xmlUnloadFile(map)
585 end
586 end
587 end
588 return count
589end
590
591---------------------------------------------------------------------------
592-- g_MapInfoList access
593---------------------------------------------------------------------------
594local g_MapInfoList
595
596function getMapLastTimePlayed( map )
597 local mapInfo = getMapInfo( map )
598 return mapInfo.lastTimePlayed or 0
599end
600
601function setMapLastTimePlayed( map, time )
602 time = time or getRealTimeSeconds()
603 local mapInfo = getMapInfo( map )
604 local lastTimePlayed = mapInfo.lastTimePlayed
605 mapInfo.lastTimePlayedText = getRealDateTimeString(getRealTime(lastTimePlayed))
606 mapInfo.lastTimePlayed = time
607 mapInfo.playedCount = ( mapInfo.playedCount or 0 ) + 1
608 saveMapInfoItem( map, mapInfo )
609end
610
611function setHunterOnMap(map)
612 local mapInfo = getMapInfo( map )
613 mapInfo.hunter = ( mapInfo.hunter or 0 ) + 1
614 saveMapInfoItem( map, mapInfo )
615end
616
617function getMapSpawnPointCount( map )
618 local mapInfo = getMapInfo( map )
619 if not mapInfo.spawnPointCount then
620 mapInfo.spawnPointCount = countSpawnPointsInMap( map )
621 saveMapInfoItem( map, mapInfo )
622 end
623 return mapInfo.spawnPointCount
624end
625
626function getMapInfo( map )
627 if not g_MapInfoList then
628 loadMapInfoAll()
629 end
630 if not g_MapInfoList[map] then
631 g_MapInfoList[map] = {}
632 end
633 local mapInfo = g_MapInfoList[map]
634 if mapInfo.loadTime ~= getResourceLoadTime(map) then
635 -- Reset or clear data that may change between loads
636 mapInfo.loadTime = getResourceLoadTime( map )
637 mapInfo.spawnPointCount = false
638 end
639 return mapInfo
640end
641
642
643---------------------------------------------------------------------------
644-- g_MapInfoList <-> database
645---------------------------------------------------------------------------
646function sqlString(value)
647 value = tostring(value) or ''
648 return "'" .. value:gsub( "(['])", "''" ) .. "'"
649end
650
651function sqlInt(value)
652 return tonumber(value) or 0
653end
654
655function getTableName(value)
656 return sqlString( 'race_mapInfo' )
657end
658
659function ensureTableExists()
660 local cmd = ( 'CREATE TABLE IF NOT EXISTS ' .. getTableName() .. ' ('
661 .. 'resName TEXT UNIQUE'
662 .. ', infoName TEXT '
663 .. ', spawnPointCount INTEGER'
664 .. ', playedCount INTEGER'
665 .. ', lastTimePlayedText TEXT'
666 .. ', lastTimePlayed INTEGER'
667 .. ', hunter INTEGER'
668 .. ', toptimes INTEGER'
669 .. ', likes INTEGER'
670 .. ')' )
671 executeSQLQuery( cmd )
672end
673
674-- Load all rows into g_MapInfoList
675function loadMapInfoAll()
676 ensureTableExists()
677 local rows = executeSQLQuery( 'SELECT * FROM ' .. getTableName() )
678 g_MapInfoList = {}
679 for i,row in ipairs(rows) do
680 local map = getResourceFromName( row.resName )
681 if map then
682 local mapInfo = getMapInfo( map )
683 mapInfo.playedCount = row.playedCount
684 mapInfo.lastTimePlayed = row.lastTimePlayed
685 mapInfo.lastTimePlayedText = row.lastTimePlayedText
686 mapInfo.hunter = row.hunter
687 mapInfo.toptimes = row.toptimes
688 mapInfo.likes = row.likes
689 end
690 end
691end
692
693-- Save one row
694function saveMapInfoItem( map, info )
695 executeSQLQuery( 'BEGIN TRANSACTION' )
696
697 ensureTableExists()
698
699 local cmd = ( 'INSERT OR IGNORE INTO ' .. getTableName() .. ' VALUES ('
700 .. '' .. sqlString( getResourceName( map ) )
701 .. ',' .. sqlString( "" )
702 .. ',' .. sqlInt( 0 )
703 .. ',' .. sqlInt( 0 )
704 .. ',' .. sqlString( "" )
705 .. ',' .. sqlInt( 0 )
706 .. ',' .. sqlInt( 0 )
707 .. ',' .. sqlInt( 0 )
708 .. ',' .. sqlInt( 0 )
709 .. ')' )
710 executeSQLQuery( cmd )
711
712 cmd = ( 'UPDATE ' .. getTableName() .. ' SET '
713 .. 'infoName=' .. sqlString( getResourceInfo( map, "name" ) )
714 .. ',spawnPointCount=' .. sqlInt( info.spawnPointCount )
715 .. ',playedCount=' .. sqlInt( info.playedCount )
716 .. ',lastTimePlayedText=' .. sqlString( info.lastTimePlayedText or "-" )
717 .. ',lastTimePlayed=' .. sqlInt( info.lastTimePlayed )
718 .. ',hunter=' .. sqlInt( info.hunter)
719 .. ',toptimes=' .. sqlInt( info.toptimes)
720 .. ',likes=' .. sqlInt( info.likes)
721 .. ' WHERE '
722 .. 'resName=' .. sqlString( getResourceName( map ) )
723 )
724 executeSQLQuery( cmd )
725
726 executeSQLQuery( 'END TRANSACTION' )
727end
728
729
730---------------------------------------------------------------------------
731--
732-- More things that should go in mapmanager
733--
734---------------------------------------------------------------------------
735addCommandHandler('hurpderp',
736 function( player, command, ... )
737 local query = #{...}>0 and table.concat({...},' ') or nil
738 if not query then
739 if g_ForcedNextMap then
740 outputRace( 'Next map is ' .. getMapName( g_ForcedNextMap ), player )
741 else
742 outputRace( 'Next map is not set', player )
743 end
744 return
745 end
746 local map, errormsg = findMap( query )
747 if not map then
748 outputRace( errormsg, player )
749 return
750 end
751 if g_ForcedNextMap == map then
752 outputRace( 'Next map is already set to ' .. getMapName( g_ForcedNextMap ), player )
753 return
754 end
755 g_ForcedNextMap = map
756 end
757)
758
759
760addCommandHandler('checkmap',
761 function( player, command, ... )
762 local query = #{...}>0 and table.concat({...},' ') or nil
763 if query then
764 local map, errormsg = findMap( query )
765 if(map)then
766 errormsgS = "Resource name: '"..getResourceName(map).."'"
767 else
768 errormsgS = "Resource not Found"
769 end
770 outputRace( errormsg, player )
771 outputRace( errormsgS, player )
772 end
773 end
774)
775
776function buyNextmap( ... )
777 local query = #{...}>0 and table.concat({...},' ') or nil
778 if not query then
779 return
780 end
781 local admin = true
782 if not _TESTING and admin == false then
783 return
784 end
785 local map, errormsg = findMap( query )
786 if not map then
787 return
788 end
789 if g_ForcedNextMap == map then
790 return
791 end
792 g_ForcedNextMap = map
793 mapIsAlreadySet = true
794 local confirm = true
795 mapBlock = getMapName( g_ForcedNextMap )
796 triggerClientEvent("onNextMap", getRootElement(), confirm,getMapName( g_ForcedNextMap ))
797 end
798addEvent("onBoughtMap",true)
799addEventHandler("onBoughtMap",getRootElement(),buyNextmap)
800
801addCommandHandler('pawnsucksxd',
802 function( player, command, ... )
803 local query = #{...}>0 and table.concat({...},' ') or nil
804 if not query then
805 if g_ForcedNextMap then
806 outputRace( 'Next map is ' .. getMapName( g_ForcedNextMap ), player )
807 else
808 outputRace( 'Next map is not set', player )
809 end
810 return
811 end
812 local map, errormsg = findMap( query )
813 if not map then
814 outputRace( errormsg, player )
815 return
816 end
817 if g_ForcedNextMap == map then
818 outputRace( 'Next map is already set to ' .. getMapName( g_ForcedNextMap ), player )
819 return
820 end
821 g_ForcedNextMap = map
822 triggerEvent("onRaceSetNextMap", getRootElement())
823 triggerClientEvent(getRootElement(), "nextmapchange", getRootElement(), getMapName(g_ForcedNextMap))
824 end
825)
826function setNextmapByPanel(...)
827 local query = #{...}>0 and table.concat({...},' ') or nil
828 local map, errormsg = findMap( query )
829 if not map then
830 return
831 end
832 g_ForcedNextMap = map
833 triggerClientEvent(getRootElement(), "nextmapchange", getRootElement(), getMapName(g_ForcedNextMap))
834end
835addEvent("onUseranelWantSetMap",true)
836addEventHandler("onUseranelWantSetMap",getRootElement(),setNextmapByPanel)
837
838addCommandHandler('vaskagag',
839 function( player, command, ... )
840 local query = #{...}>0 and table.concat({...},' ') or nil
841 if not query then
842 if g_ForcedNextMap then
843 outputRace( 'Next map is ' .. getMapName( g_ForcedNextMap ), player )
844 else
845 outputRace( 'Next map is not set', player )
846 end
847 return
848 end
849 if not _TESTING and not isPlayerInACLGroup(player, g_GameOptions.admingroup) then
850 return
851 end
852 local map, errormsg = findMap( query )
853 if not map then
854 outputRace( errormsg, player )
855 return
856 end
857 if g_ForcedNextMap == map then
858 outputRace( 'Next map is already set to ' .. getMapName( g_ForcedNextMap ), player )
859 return
860 end
861
862 if not stateAllowsRandomMapVote() or g_CurrentRaceMode:getTimeRemaining() < 30000 then
863 if player then
864 outputRace( "I'm afraid I can't let you do that, " .. getPlayerName(player) .. ".", player )
865 end
866 return
867 end
868
869 displayHilariarseMessage( player )
870 exports.votemanager:stopPoll()
871 nextmapvoteselecteD = map
872 -- Actual vote started here
873 local pollDidStart = exports.votemanager:startPoll {
874 title='Do you want to set next map to '..getMapName( map )..'?',
875 percentage=51,
876 timeout=10,
877 allowchange=true,
878 visibleTo=getRootElement(),
879 [1]={'Yes', 'midNextMapVoteResult', getRootElement(), true},
880 [2]={'No', 'midNextMapVoteResult', getRootElement(), false;default=true},
881 }
882
883 -- Change state if vote did start
884 if pollDidStart then
885 gotoState('MidMapVote')
886 end
887 --g_ForcedNextMap = map
888 --outputChatBox('Next map set to ' .. getMapName( g_ForcedNextMap ) .. ' by ' .. getPlayerName( player ), g_Root, 0, 240, 0)
889 --triggerEvent("onRaceSetNextMap", getRootElement())
890 --triggerClientEvent(getRootElement(), "nextmapchange", getRootElement(), getMapName(g_ForcedNextMap))
891 end
892)
893
894--Find a map which matches, or nil and a text message if there is not one match
895function findMap( query )
896 local maps = findMaps( query )
897
898 -- Make status string
899 local status = "Found " .. #maps .. " match" .. ( #maps==1 and "" or "es" )
900 for i=1,math.min(5,#maps) do
901 status = status .. ( i==1 and ": " or ", " ) .. "'" .. getMapName( maps[i] ) .. "'"
902 end
903 if #maps > 5 then
904 status = status .. " (" .. #maps - 5 .. " more)"
905 end
906
907 if #maps == 0 then
908 return nil, status .. " for '" .. query .. "'"
909 end
910 if #maps == 1 then
911 return maps[1], status
912 end
913 if #maps > 1 then
914 return nil, status
915 end
916end
917
918-- Find all maps which match the query string
919function findMaps( query )
920 local results = {}
921 --escape all meta chars
922 query = string.gsub(query, "([%*%+%?%.%(%)%[%]%{%}%\%/%|%^%$%-])","%%%1")
923 -- Loop through and find matching maps
924 for i,resource in ipairs(exports.mapmanager:getMapsCompatibleWithGamemode(getThisResource())) do
925 local resName = getResourceName( resource )
926 local infoName = getMapName( resource )
927
928 -- Look for exact match first
929 if query == resName or query == infoName then
930 return {resource}
931 end
932
933 -- Find match for query within infoName
934 if string.find( infoName:lower(), query:lower() ) then
935 table.insert( results, resource )
936 break
937 end
938 end
939 return results
940end
941
942
943function findMapSpecial( query )
944 local maps = findMapsSpecial( query )
945
946 -- Make status string
947 local status = "Found " .. #maps .. " match" .. ( #maps==1 and "" or "es" )
948 for i=1,math.min(5,#maps) do
949 status = status .. ( i==1 and ": " or ", " ) .. "'" .. getMapName( maps[i] ) .. "'"
950 end
951 if #maps > 5 then
952 status = status .. " (" .. #maps - 5 .. " more)"
953 end
954
955 if #maps == 0 then
956 return nil, status .. " for '" .. query .. "'"
957 end
958 if #maps == 1 then
959 return maps[1], status
960 end
961 if #maps > 1 then
962 return nil, status
963 end
964end
965
966function findMapsSpecial( query )
967 local results = {}
968 --escape all meta chars
969 query = string.gsub(query, "([%*%+%?%.%(%)%[%]%{%}%\%/%|%^%$%-])","%%%1")
970 -- Loop through and find matching maps
971 for i,resource in ipairs(exports.mapmanager:getMapsCompatibleWithGamemode(getThisResource())) do
972 local resName = getResourceName( resource )
973 local infoName = getMapName( resource )
974
975 -- Look for exact match first
976 if query == resName or query == infoName then
977 return {resource}
978 end
979
980 -- Find match for query within infoName
981 local querryEdited = tostring(query)
982 local infoNameEdited = tostring(infoName)
983 querryEdited = querryEdited.."end"
984 infoNameEdited = infoNameEdited.."end"
985 infoNameEdited:lower()
986 querryEdited:lower()
987
988 if string.find( infoNameEdited, querryEdited) then
989 table.insert( results, resource )
990 break
991 end
992
993 end
994 return results
995end
996
997function getMapName( map )
998 return getResourceInfo( map, "name" ) or getResourceName( map ) or "unknown"
999end
1000
1001addCommandHandler("deletemap",
1002 function(thePlayer,command)
1003 local currentMap = exports.mapmanager:getRunningGamemodeMap()
1004 if currentMap then
1005 if isPlayerInACLGroup(thePlayer, g_GameOptions.admingroup) then
1006 local resourceState = getResourceState(currentMap)
1007 if resourceState == "running" then
1008 if stopResource(currentMap) then
1009 setTimer(deleteMapWithDelay,1000,1,currentMap,thePlayer)
1010 end
1011 end
1012 end
1013 end
1014 end
1015)
1016
1017function deleteMapWithDelay(mapResource,thePlayer)
1018 if mapResource then
1019 local mapName = getMapName(mapResource)
1020 deleteResource(mapResource)
1021 outputChatBox(mapName.." #FF6400d#ffffffeleted #FF6400b#ffffffy "..getPlayerNametagText(thePlayer).."#ffffff." , g_Root, 255, 255, 255,true)
1022 startRandomMap()
1023 end
1024end
1025
1026-- Find all maps which match the query string
1027function findMaps( query )
1028 local results = {}
1029 --escape all meta chars
1030 query = string.gsub(query, "([%*%+%?%.%(%)%[%]%{%}%\%/%|%^%$%-])","%%%1")
1031 -- Loop through and find matching maps
1032 for i,resource in ipairs(exports.mapmanager:getMapsCompatibleWithGamemode(getThisResource())) do
1033 local resName = getResourceName( resource )
1034 local infoName = getMapName( resource )
1035
1036 -- Look for exact match first
1037 if query == resName or query == infoName then
1038 return {resource}
1039 end
1040
1041 -- Find match for query within infoName
1042 if string.find( infoName:lower(), query:lower() ) then
1043 table.insert( results, resource )
1044 end
1045 end
1046 return results
1047end
1048
1049function getMapName( map )
1050 return getResourceInfo( map, "name" ) or getResourceName( map ) or "unknown"
1051end
1052
1053-------------------------VOTE REDO-----------------------------
1054local isEventRunning = false
1055local votesRedo = 0
1056local allowRedo = false
1057local lastMap = ""
1058local requiredVotes = 100
1059local reqFlag = false
1060
1061function onEventBlock(state)
1062 isEventRunning = state
1063end
1064addEvent("onEventStateChange", true)
1065addEventHandler("onEventStateChange", getRootElement(), onEventBlock)
1066
1067
1068local votedPlayers = {}
1069
1070function addVotedPlayers(player)
1071 votedPlayers[#votedPlayers+1] = getPlayerSerial(player)
1072end
1073
1074function checkVotedPlayer(player)
1075 for i=1,#votedPlayers do
1076 if (getPlayerSerial(player) == votedPlayers[i])then
1077 return true
1078 end
1079 end
1080 return false
1081end
1082
1083function voteRedoManagment(player)
1084 if not(tostring(isEventRunning) == "true") then
1085 if not(checkVotedPlayer(player)) then
1086 local currentMap = getMapName(exports.mapmanager:getRunningGamemodeMap())
1087
1088 if not(reqFlag) then
1089 requiredVotes = tonumber(math.ceil((getPlayerCount()/2)))
1090 reqFlag = true
1091 end
1092
1093 if not(currentMap == lastMap) then
1094 if(votesRedo >= requiredVotes) then
1095 outputChatBox("#FF6600* T#ffffffhe #ff6600m#ffffffap #ff6600w#ffffffill #ff6600be r#ffffffrestarted", player, 255,255,255,true)
1096 allowRedo = true
1097 end
1098
1099 if(votesRedo < requiredVotes) then
1100 votesRedo = votesRedo + 1
1101 addVotedPlayers(player)
1102 local r, g, b = getPlayerNametagColor(player)
1103 local hex = RGBToHex(r, g, b)
1104 outputChatBox("#FF6600* V#ffffffoteredo #ff6600| #FF6600"..votesRedo.."#FFFFFF/#FF6600"..requiredVotes.." #ff6600| V#ffffffoted #ff6600B#ffffffy ".. hex ..''.. _getPlayerName(player)..'#FFFFFF ', root, 255, 100, 0, true)
1105 end
1106 else
1107 outputChatBox("#FF6600* Y#ffffffou #ff6600c#ffffffan't #ff6600V#ffffffote #ff6600a s#ffffffecond #ff6600T#fffffime #ff6600o#ffffffn #ff6600t#ffffffhe #ff6600s#ffffffame #ff6600m#ffffffap!", player, 255,255,255,true)
1108 end
1109 else
1110 outputChatBox("#FF6600* Y#FFFFFFou #FF6600h#FFFFFFave #ff6600a#fffffflready #Ff6600v#ffffffoted #ff6600o#ffffffn #ff6600t#ffffffhis #ff6600m#ffffffap", player, 255,255,255,true)
1111 end
1112 else
1113 outputChatBox("#FF6600* #FFFFFFYou can't voteredo maps during the #ff6600DGE#FFFFFF.",player,255,255,255,true)
1114 end
1115end
1116addCommandHandler("voteredo", voteRedoManagment)
1117addCommandHandler("vr", voteRedoManagment)
1118
1119function onGridCountDown (stateName)
1120 if stateName == "PostFinish" then
1121 lastMap = getMapName(exports.mapmanager:getRunningGamemodeMap())
1122 if(allowRedo)then
1123 setTimer(function()
1124 voteRedoMap()
1125 outputChatBox('#ff6600* R#FFFFFFestarted #FF6600B#FFFFFFy #FF6600V#ffffffoteredo.', root, 255, 100, 0, true)
1126 votesRedo = 0
1127 allowRedo = false
1128 votedPlayers = {}
1129 reqFlag = false
1130 end,7000, 1)
1131 end
1132 end
1133end
1134addEvent("onRaceStateChanging",true)
1135addEventHandler("onRaceStateChanging", getRootElement(), onGridCountDown)
1136
1137--voteRedo
1138function voteRedoMap()
1139 local currentMap = exports.mapmanager:getRunningGamemodeMap()
1140 if currentMap then
1141 if not exports.mapmanager:changeGamemodeMap (currentMap, nil, true) then
1142 problemChangingMap()
1143 end
1144 else
1145 outputRace("You can't restart the map because no map is running", getRootElement())
1146 end
1147end
1148
1149--Played maps array.
1150local mapTimeLimit = 1500 --25 min.
1151local currentDate = 0
1152local objects = {}
1153function addMapToArray(map)
1154local mapName = tostring(map)
1155local mapTime = tonumber(getCurrentTimeHMS())
1156local found = false
1157
1158local map = {}
1159map[1] = mapName
1160map[2] = mapTime+mapTimeLimit
1161
1162 for i,mapA in ipairs(objects)do
1163 if (mapA[1] == mapName)then
1164 mapA[1] = map[1]
1165 mapA[2] = map[2]
1166 found = true
1167 break
1168 end
1169 end
1170
1171if not(found)then
1172 for i,mapA in ipairs(objects)do
1173 if (mapA[2] == false)then
1174 mapA[1] = map[1]
1175 mapA[2] = map[2]
1176 found = true
1177 break
1178 end
1179 end
1180end
1181
1182 if not(found)then
1183 objects[#objects+1] = map
1184 end
1185end
1186
1187
1188function checkMap(mapName)
1189if(getPlayerCount() == 1)then
1190 return true
1191end
1192local todayDate = getDate ()
1193local currentTime = tonumber(getCurrentTimeHMS())
1194
1195 if not(tonumber(currentDate) == tonumber(todayDate))then
1196 ressetAllMapsTime()
1197 currentDate = tonumber(todayDate)
1198 end
1199
1200 for i,mapA in ipairs(objects)do
1201 if (mapA[2])and(currentTime > mapA[2])then
1202 mapA[2] = false
1203 end
1204 end
1205
1206 for i,mapA in ipairs(objects)do
1207 if(mapA[1] == mapName)then
1208 if (mapA[2])and(currentTime > mapA[2] or mapA[2] == false)then
1209 return true
1210 else
1211 if(mapA[2])then
1212 return mapA[2]-currentTime
1213 else
1214 return false
1215 end
1216 end
1217 end
1218 end
1219 return true
1220end
1221
1222function ressetAllMapsTime()
1223 objects = {}
1224end
1225
1226function getCurrentTimeHMS ()
1227 local time = getRealTime()
1228 local hours = time.hour
1229 local minutes = time.minute
1230 local seconds = time.second
1231 local secondsLength = #tostring(minutes)
1232 local minutesLength = #tostring(minutes)
1233 local hoursLength = #tostring(hours)
1234 if (secondsLength == 1) then seconds = "0"..seconds end
1235 if (minutesLength == 1) then minutes = "0"..minutes end
1236 if (hoursLength == 1) then hours = "0"..hours end
1237 local minutesSeconds = tonumber((hours*60*60)+(minutes*60)+seconds)
1238
1239 return minutesSeconds
1240end
1241
1242
1243function getDate ()
1244 local time = getRealTime()
1245 local hours = time.hour
1246 local minutes = time.minute
1247 local date = time.monthday
1248
1249 return date
1250end