plugins/mapchooser.sp
author Ruben Gonzalez <drifter01620@gmail.com>
Thu Feb 13 13:19:59 2014 -0500 (2014-02-13)
changeset 4252 e665928a4dba
parent 4218 0634bd808f0f
child 4359 444f983b8356
permissions -rw-r--r--
Fix not returning a valid weaponID for cz75a (irc, r=KyleS)
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod Mapchooser Plugin
     5  * Creates a map vote at appropriate times, setting sm_nextmap to the winning
     6  * vote
     7  *
     8  * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
     9  * =============================================================================
    10  *
    11  * This program is free software; you can redistribute it and/or modify it under
    12  * the terms of the GNU General Public License, version 3.0, as published by the
    13  * Free Software Foundation.
    14  * 
    15  * This program is distributed in the hope that it will be useful, but WITHOUT
    16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    17  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    18  * details.
    19  *
    20  * You should have received a copy of the GNU General Public License along with
    21  * this program.  If not, see <http://www.gnu.org/licenses/>.
    22  *
    23  * As a special exception, AlliedModders LLC gives you permission to link the
    24  * code of this program (as well as its derivative works) to "Half-Life 2," the
    25  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    26  * by the Valve Corporation.  You must obey the GNU General Public License in
    27  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    28  * this exception to all derivative works.  AlliedModders LLC defines further
    29  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    30  * or <http://www.sourcemod.net/license.php>.
    31  *
    32  * Version: $Id$
    33  */
    34  
    35 //#define DEBUG
    36 
    37 #if defined DEBUG
    38 	#define assert(%1) if (!(%1)) ThrowError("Debug Assertion Failed");
    39 	#define assert_msg(%1,%2) if (!(%1)) ThrowError(%2);
    40 #else
    41 	#define assert(%1)
    42 	#define assert_msg(%1,%2)
    43 #endif
    44 
    45 #pragma semicolon 1
    46 #include <sourcemod>
    47 #include <mapchooser>
    48 #include <nextmap>
    49 
    50 public Plugin:myinfo =
    51 {
    52 	name = "MapChooser",
    53 	author = "AlliedModders LLC",
    54 	description = "Automated Map Voting",
    55 	version = SOURCEMOD_VERSION,
    56 	url = "http://www.sourcemod.net/"
    57 };
    58 
    59 /* Valve ConVars */
    60 new Handle:g_Cvar_Winlimit = INVALID_HANDLE;
    61 new Handle:g_Cvar_Maxrounds = INVALID_HANDLE;
    62 new Handle:g_Cvar_Fraglimit = INVALID_HANDLE;
    63 new Handle:g_Cvar_Bonusroundtime = INVALID_HANDLE;
    64 
    65 /* Plugin ConVars */
    66 new Handle:g_Cvar_StartTime = INVALID_HANDLE;
    67 new Handle:g_Cvar_StartRounds = INVALID_HANDLE;
    68 new Handle:g_Cvar_StartFrags = INVALID_HANDLE;
    69 new Handle:g_Cvar_ExtendTimeStep = INVALID_HANDLE;
    70 new Handle:g_Cvar_ExtendRoundStep = INVALID_HANDLE;
    71 new Handle:g_Cvar_ExtendFragStep = INVALID_HANDLE;
    72 new Handle:g_Cvar_ExcludeMaps = INVALID_HANDLE;
    73 new Handle:g_Cvar_IncludeMaps = INVALID_HANDLE;
    74 new Handle:g_Cvar_NoVoteMode = INVALID_HANDLE;
    75 new Handle:g_Cvar_Extend = INVALID_HANDLE;
    76 new Handle:g_Cvar_DontChange = INVALID_HANDLE;
    77 new Handle:g_Cvar_EndOfMapVote = INVALID_HANDLE;
    78 new Handle:g_Cvar_VoteDuration = INVALID_HANDLE;
    79 new Handle:g_Cvar_RunOff = INVALID_HANDLE;
    80 new Handle:g_Cvar_RunOffPercent = INVALID_HANDLE;
    81 
    82 new Handle:g_VoteTimer = INVALID_HANDLE;
    83 new Handle:g_RetryTimer = INVALID_HANDLE;
    84 
    85 /* Data Handles */
    86 new Handle:g_MapList = INVALID_HANDLE;
    87 new Handle:g_NominateList = INVALID_HANDLE;
    88 new Handle:g_NominateOwners = INVALID_HANDLE;
    89 new Handle:g_OldMapList = INVALID_HANDLE;
    90 new Handle:g_NextMapList = INVALID_HANDLE;
    91 new Handle:g_VoteMenu = INVALID_HANDLE;
    92 
    93 new g_Extends;
    94 new g_TotalRounds;
    95 new bool:g_HasVoteStarted;
    96 new bool:g_WaitingForVote;
    97 new bool:g_MapVoteCompleted;
    98 new bool:g_ChangeMapAtRoundEnd;
    99 new bool:g_ChangeMapInProgress;
   100 new g_mapFileSerial = -1;
   101 
   102 new MapChange:g_ChangeTime;
   103 
   104 new Handle:g_NominationsResetForward = INVALID_HANDLE;
   105 new Handle:g_MapVoteStartedForward = INVALID_HANDLE;
   106 
   107 /* Upper bound of how many team there could be */
   108 #define MAXTEAMS 10
   109 new g_winCount[MAXTEAMS];
   110 
   111 #define VOTE_EXTEND "##extend##"
   112 #define VOTE_DONTCHANGE "##dontchange##"
   113 
   114 public OnPluginStart()
   115 {
   116 	LoadTranslations("mapchooser.phrases");
   117 	LoadTranslations("common.phrases");
   118 	
   119 	new arraySize = ByteCountToCells(PLATFORM_MAX_PATH);
   120 	g_MapList = CreateArray(arraySize);
   121 	g_NominateList = CreateArray(arraySize);
   122 	g_NominateOwners = CreateArray(1);
   123 	g_OldMapList = CreateArray(arraySize);
   124 	g_NextMapList = CreateArray(arraySize);
   125 	
   126 	g_Cvar_EndOfMapVote = CreateConVar("sm_mapvote_endvote", "1", "Specifies if MapChooser should run an end of map vote", _, true, 0.0, true, 1.0);
   127 
   128 	g_Cvar_StartTime = CreateConVar("sm_mapvote_start", "3.0", "Specifies when to start the vote based on time remaining.", _, true, 1.0);
   129 	g_Cvar_StartRounds = CreateConVar("sm_mapvote_startround", "2.0", "Specifies when to start the vote based on rounds remaining. Use 0 on TF2 to start vote during bonus round time", _, true, 0.0);
   130 	g_Cvar_StartFrags = CreateConVar("sm_mapvote_startfrags", "5.0", "Specifies when to start the vote base on frags remaining.", _, true, 1.0);
   131 	g_Cvar_ExtendTimeStep = CreateConVar("sm_extendmap_timestep", "15", "Specifies how much many more minutes each extension makes", _, true, 5.0);
   132 	g_Cvar_ExtendRoundStep = CreateConVar("sm_extendmap_roundstep", "5", "Specifies how many more rounds each extension makes", _, true, 1.0);
   133 	g_Cvar_ExtendFragStep = CreateConVar("sm_extendmap_fragstep", "10", "Specifies how many more frags are allowed when map is extended.", _, true, 5.0);	
   134 	g_Cvar_ExcludeMaps = CreateConVar("sm_mapvote_exclude", "5", "Specifies how many past maps to exclude from the vote.", _, true, 0.0);
   135 	g_Cvar_IncludeMaps = CreateConVar("sm_mapvote_include", "5", "Specifies how many maps to include in the vote.", _, true, 2.0, true, 6.0);
   136 	g_Cvar_NoVoteMode = CreateConVar("sm_mapvote_novote", "1", "Specifies whether or not MapChooser should pick a map if no votes are received.", _, true, 0.0, true, 1.0);
   137 	g_Cvar_Extend = CreateConVar("sm_mapvote_extend", "0", "Number of extensions allowed each map.", _, true, 0.0);
   138 	g_Cvar_DontChange = CreateConVar("sm_mapvote_dontchange", "1", "Specifies if a 'Don't Change' option should be added to early votes", _, true, 0.0);
   139 	g_Cvar_VoteDuration = CreateConVar("sm_mapvote_voteduration", "20", "Specifies how long the mapvote should be available for.", _, true, 5.0);
   140 	g_Cvar_RunOff = CreateConVar("sm_mapvote_runoff", "0", "Hold run of votes if winning choice is less than a certain margin", _, true, 0.0, true, 1.0);
   141 	g_Cvar_RunOffPercent = CreateConVar("sm_mapvote_runoffpercent", "50", "If winning choice has less than this percent of votes, hold a runoff", _, true, 0.0, true, 100.0);
   142 	
   143 	RegAdminCmd("sm_mapvote", Command_Mapvote, ADMFLAG_CHANGEMAP, "sm_mapvote - Forces MapChooser to attempt to run a map vote now.");
   144 	RegAdminCmd("sm_setnextmap", Command_SetNextmap, ADMFLAG_CHANGEMAP, "sm_setnextmap <map>");
   145 
   146 	g_Cvar_Winlimit = FindConVar("mp_winlimit");
   147 	g_Cvar_Maxrounds = FindConVar("mp_maxrounds");
   148 	g_Cvar_Fraglimit = FindConVar("mp_fraglimit");
   149 	g_Cvar_Bonusroundtime = FindConVar("mp_bonusroundtime");
   150 	
   151 	if (g_Cvar_Winlimit != INVALID_HANDLE || g_Cvar_Maxrounds != INVALID_HANDLE)
   152 	{
   153 		decl String:folder[64];
   154 		GetGameFolderName(folder, sizeof(folder));
   155 
   156 		if (strcmp(folder, "tf") == 0)
   157 		{
   158 			HookEvent("teamplay_win_panel", Event_TeamPlayWinPanel);
   159 			HookEvent("teamplay_restart_round", Event_TFRestartRound);
   160 			HookEvent("arena_win_panel", Event_TeamPlayWinPanel);
   161 		}
   162 		else if (strcmp(folder, "nucleardawn") == 0)
   163 		{
   164 			HookEvent("round_win", Event_RoundEnd);
   165 		}
   166 		else
   167 		{
   168 			HookEvent("round_end", Event_RoundEnd);
   169 		}
   170 	}
   171 	
   172 	if (g_Cvar_Fraglimit != INVALID_HANDLE)
   173 	{
   174 		HookEvent("player_death", Event_PlayerDeath);		
   175 	}
   176 	
   177 	AutoExecConfig(true, "mapchooser");
   178 	
   179 	//Change the mp_bonusroundtime max so that we have time to display the vote
   180 	//If you display a vote during bonus time good defaults are 17 vote duration and 19 mp_bonustime
   181 	if (g_Cvar_Bonusroundtime != INVALID_HANDLE)
   182 	{
   183 		SetConVarBounds(g_Cvar_Bonusroundtime, ConVarBound_Upper, true, 30.0);		
   184 	}
   185 	
   186 	g_NominationsResetForward = CreateGlobalForward("OnNominationRemoved", ET_Ignore, Param_String, Param_Cell);
   187 	g_MapVoteStartedForward = CreateGlobalForward("OnMapVoteStarted", ET_Ignore);
   188 }
   189 
   190 public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
   191 {
   192 	RegPluginLibrary("mapchooser");	
   193 	
   194 	CreateNative("NominateMap", Native_NominateMap);
   195 	CreateNative("RemoveNominationByMap", Native_RemoveNominationByMap);
   196 	CreateNative("RemoveNominationByOwner", Native_RemoveNominationByOwner);
   197 	CreateNative("InitiateMapChooserVote", Native_InitiateVote);
   198 	CreateNative("CanMapChooserStartVote", Native_CanVoteStart);
   199 	CreateNative("HasEndOfMapVoteFinished", Native_CheckVoteDone);
   200 	CreateNative("GetExcludeMapList", Native_GetExcludeMapList);
   201 	CreateNative("GetNominatedMapList", Native_GetNominatedMapList);
   202 	CreateNative("EndOfMapVoteEnabled", Native_EndOfMapVoteEnabled);
   203 
   204 	return APLRes_Success;
   205 }
   206 
   207 public OnConfigsExecuted()
   208 {
   209 	if (ReadMapList(g_MapList,
   210 					 g_mapFileSerial, 
   211 					 "mapchooser",
   212 					 MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER)
   213 		!= INVALID_HANDLE)
   214 		
   215 	{
   216 		if (g_mapFileSerial == -1)
   217 		{
   218 			LogError("Unable to create a valid map list.");
   219 		}
   220 	}
   221 	
   222 	CreateNextVote();
   223 	SetupTimeleftTimer();
   224 	
   225 	g_TotalRounds = 0;
   226 	
   227 	g_Extends = 0;
   228 	
   229 	g_MapVoteCompleted = false;
   230 	
   231 	ClearArray(g_NominateList);
   232 	ClearArray(g_NominateOwners);
   233 	
   234 	for (new i=0; i<MAXTEAMS; i++)
   235 	{
   236 		g_winCount[i] = 0;	
   237 	}
   238 	
   239 
   240 	/* Check if mapchooser will attempt to start mapvote during bonus round time - TF2 Only */
   241 	if ((g_Cvar_Bonusroundtime != INVALID_HANDLE) && !GetConVarInt(g_Cvar_StartRounds))
   242 	{
   243 		if (GetConVarFloat(g_Cvar_Bonusroundtime) <= GetConVarFloat(g_Cvar_VoteDuration))
   244 		{
   245 			LogError("Warning - Bonus Round Time shorter than Vote Time. Votes during bonus round may not have time to complete");
   246 		}
   247 	}
   248 }
   249 
   250 public OnMapEnd()
   251 {
   252 	g_HasVoteStarted = false;
   253 	g_WaitingForVote = false;
   254 	g_ChangeMapAtRoundEnd = false;
   255 	g_ChangeMapInProgress = false;
   256 	
   257 	g_VoteTimer = INVALID_HANDLE;
   258 	g_RetryTimer = INVALID_HANDLE;
   259 	
   260 	decl String:map[PLATFORM_MAX_PATH];
   261 	GetCurrentMap(map, sizeof(map));
   262 	PushArrayString(g_OldMapList, map);
   263 				
   264 	if (GetArraySize(g_OldMapList) > GetConVarInt(g_Cvar_ExcludeMaps))
   265 	{
   266 		RemoveFromArray(g_OldMapList, 0);
   267 	}	
   268 }
   269 
   270 public OnClientDisconnect(client)
   271 {
   272 	new index = FindValueInArray(g_NominateOwners, client);
   273 	
   274 	if (index == -1)
   275 	{
   276 		return;
   277 	}
   278 	
   279 	new String:oldmap[PLATFORM_MAX_PATH];
   280 	GetArrayString(g_NominateList, index, oldmap, sizeof(oldmap));
   281 	Call_StartForward(g_NominationsResetForward);
   282 	Call_PushString(oldmap);
   283 	Call_PushCell(GetArrayCell(g_NominateOwners, index));
   284 	Call_Finish();
   285 	
   286 	RemoveFromArray(g_NominateOwners, index);
   287 	RemoveFromArray(g_NominateList, index);
   288 }
   289 
   290 public Action:Command_SetNextmap(client, args)
   291 {
   292 	if (args < 1)
   293 	{
   294 		ReplyToCommand(client, "[SM] Usage: sm_setnextmap <map>");
   295 		return Plugin_Handled;
   296 	}
   297 
   298 	decl String:map[PLATFORM_MAX_PATH];
   299 	GetCmdArg(1, map, sizeof(map));
   300 
   301 	if (!IsMapValid(map))
   302 	{
   303 		ReplyToCommand(client, "[SM] %t", "Map was not found", map);
   304 		return Plugin_Handled;
   305 	}
   306 
   307 	ShowActivity(client, "%t", "Changed Next Map", map);
   308 	LogAction(client, -1, "\"%L\" changed nextmap to \"%s\"", client, map);
   309 
   310 	SetNextMap(map);
   311 	g_MapVoteCompleted = true;
   312 
   313 	return Plugin_Handled;
   314 }
   315 
   316 public OnMapTimeLeftChanged()
   317 {
   318 	if (GetArraySize(g_MapList))
   319 	{
   320 		SetupTimeleftTimer();
   321 	}
   322 }
   323 
   324 SetupTimeleftTimer()
   325 {
   326 	new time;
   327 	if (GetMapTimeLeft(time) && time > 0)
   328 	{
   329 		new startTime = GetConVarInt(g_Cvar_StartTime) * 60;
   330 		if (time - startTime < 0 && GetConVarBool(g_Cvar_EndOfMapVote) && !g_MapVoteCompleted && !g_HasVoteStarted)
   331 		{
   332 			InitiateVote(MapChange_MapEnd, INVALID_HANDLE);		
   333 		}
   334 		else
   335 		{
   336 			if (g_VoteTimer != INVALID_HANDLE)
   337 			{
   338 				KillTimer(g_VoteTimer);
   339 				g_VoteTimer = INVALID_HANDLE;
   340 			}	
   341 			
   342 			//g_VoteTimer = CreateTimer(float(time - startTime), Timer_StartMapVote, _, TIMER_FLAG_NO_MAPCHANGE);
   343 			new Handle:data;
   344 			g_VoteTimer = CreateDataTimer(float(time - startTime), Timer_StartMapVote, data, TIMER_FLAG_NO_MAPCHANGE);
   345 			WritePackCell(data, _:MapChange_MapEnd);
   346 			WritePackCell(data, _:INVALID_HANDLE);
   347 			ResetPack(data);
   348 		}		
   349 	}
   350 }
   351 
   352 public Action:Timer_StartMapVote(Handle:timer, Handle:data)
   353 {
   354 	if (timer == g_RetryTimer)
   355 	{
   356 		g_WaitingForVote = false;
   357 		g_RetryTimer = INVALID_HANDLE;
   358 	}
   359 	else
   360 	{
   361 		g_VoteTimer = INVALID_HANDLE;
   362 	}
   363 	
   364 	if (!GetArraySize(g_MapList) || !GetConVarBool(g_Cvar_EndOfMapVote) || g_MapVoteCompleted || g_HasVoteStarted)
   365 	{
   366 		return Plugin_Stop;
   367 	}
   368 	
   369 	new MapChange:mapChange = MapChange:ReadPackCell(data);
   370 	new Handle:hndl = Handle:ReadPackCell(data);
   371 
   372 	InitiateVote(mapChange, hndl);
   373 
   374 	return Plugin_Stop;
   375 }
   376 
   377 public Event_TFRestartRound(Handle:event, const String:name[], bool:dontBroadcast)
   378 {
   379 	/* Game got restarted - reset our round count tracking */
   380 	g_TotalRounds = 0;	
   381 }
   382 
   383 public Event_TeamPlayWinPanel(Handle:event, const String:name[], bool:dontBroadcast)
   384 {
   385 	if (g_ChangeMapAtRoundEnd)
   386 	{
   387 		g_ChangeMapAtRoundEnd = false;
   388 		CreateTimer(2.0, Timer_ChangeMap, INVALID_HANDLE, TIMER_FLAG_NO_MAPCHANGE);
   389 		g_ChangeMapInProgress = true;
   390 	}
   391 	
   392 	new bluescore = GetEventInt(event, "blue_score");
   393 	new redscore = GetEventInt(event, "red_score");
   394 		
   395 	if(GetEventInt(event, "round_complete") == 1 || StrEqual(name, "arena_win_panel"))
   396 	{
   397 		g_TotalRounds++;
   398 		
   399 		if (!GetArraySize(g_MapList) || g_HasVoteStarted || g_MapVoteCompleted || !GetConVarBool(g_Cvar_EndOfMapVote))
   400 		{
   401 			return;
   402 		}
   403 		
   404 		CheckMaxRounds(g_TotalRounds);
   405 		
   406 		switch(GetEventInt(event, "winning_team"))
   407 		{
   408 			case 3:
   409 			{
   410 				CheckWinLimit(bluescore);
   411 			}
   412 			case 2:
   413 			{
   414 				CheckWinLimit(redscore);				
   415 			}			
   416 			//We need to do nothing on winning_team == 0 this indicates stalemate.
   417 			default:
   418 			{
   419 				return;
   420 			}			
   421 		}
   422 	}
   423 }
   424 /* You ask, why don't you just use team_score event? And I answer... Because CSS doesn't. */
   425 public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast)
   426 {
   427 	if (g_ChangeMapAtRoundEnd)
   428 	{
   429 		g_ChangeMapAtRoundEnd = false;
   430 		CreateTimer(2.0, Timer_ChangeMap, INVALID_HANDLE, TIMER_FLAG_NO_MAPCHANGE);
   431 		g_ChangeMapInProgress = true;
   432 	}
   433 	
   434 	new winner;
   435 	if (strcmp(name, "round_win") == 0)
   436 	{
   437 		// Nuclear Dawn
   438 		winner = GetEventInt(event, "team");
   439 	}
   440 	else
   441 	{
   442 		winner = GetEventInt(event, "winner");
   443 	}
   444 	
   445 	if (winner == 0 || winner == 1 || !GetConVarBool(g_Cvar_EndOfMapVote))
   446 	{
   447 		return;
   448 	}
   449 	
   450 	if (winner >= MAXTEAMS)
   451 	{
   452 		SetFailState("Mod exceed maximum team count - Please file a bug report.");	
   453 	}
   454 
   455 	g_TotalRounds++;
   456 	
   457 	g_winCount[winner]++;
   458 	
   459 	if (!GetArraySize(g_MapList) || g_HasVoteStarted || g_MapVoteCompleted)
   460 	{
   461 		return;
   462 	}
   463 	
   464 	CheckWinLimit(g_winCount[winner]);
   465 	CheckMaxRounds(g_TotalRounds);
   466 }
   467 
   468 public CheckWinLimit(winner_score)
   469 {	
   470 	if (g_Cvar_Winlimit != INVALID_HANDLE)
   471 	{
   472 		new winlimit = GetConVarInt(g_Cvar_Winlimit);
   473 		if (winlimit)
   474 		{			
   475 			if (winner_score >= (winlimit - GetConVarInt(g_Cvar_StartRounds)))
   476 			{
   477 				InitiateVote(MapChange_MapEnd, INVALID_HANDLE);
   478 			}
   479 		}
   480 	}
   481 }
   482 
   483 public CheckMaxRounds(roundcount)
   484 {		
   485 	if (g_Cvar_Maxrounds != INVALID_HANDLE)
   486 	{
   487 		new maxrounds = GetConVarInt(g_Cvar_Maxrounds);
   488 		if (maxrounds)
   489 		{
   490 			if (roundcount >= (maxrounds - GetConVarInt(g_Cvar_StartRounds)))
   491 			{
   492 				InitiateVote(MapChange_MapEnd, INVALID_HANDLE);
   493 			}			
   494 		}
   495 	}
   496 }
   497 
   498 public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast)
   499 {
   500 	if (!GetArraySize(g_MapList) || g_Cvar_Fraglimit == INVALID_HANDLE || g_HasVoteStarted)
   501 	{
   502 		return;
   503 	}
   504 	
   505 	if (!GetConVarInt(g_Cvar_Fraglimit) || !GetConVarBool(g_Cvar_EndOfMapVote))
   506 	{
   507 		return;
   508 	}
   509 
   510 	if (g_MapVoteCompleted)
   511 	{
   512 		return;
   513 	}
   514 
   515 	new fragger = GetClientOfUserId(GetEventInt(event, "attacker"));
   516 
   517 	if (!fragger)
   518 	{
   519 		return;
   520 	}
   521 
   522 	if (GetClientFrags(fragger) >= (GetConVarInt(g_Cvar_Fraglimit) - GetConVarInt(g_Cvar_StartFrags)))
   523 	{
   524 		InitiateVote(MapChange_MapEnd, INVALID_HANDLE);
   525 	}
   526 }
   527 
   528 public Action:Command_Mapvote(client, args)
   529 {
   530 	InitiateVote(MapChange_MapEnd, INVALID_HANDLE);
   531 
   532 	return Plugin_Handled;	
   533 }
   534 
   535 /**
   536  * Starts a new map vote
   537  *
   538  * @param when			When the resulting map change should occur.
   539  * @param inputlist		Optional list of maps to use for the vote, otherwise an internal list of nominations + random maps will be used.
   540  * @param noSpecials	Block special vote options like extend/nochange (upgrade this to bitflags instead?)
   541  */
   542 InitiateVote(MapChange:when, Handle:inputlist=INVALID_HANDLE)
   543 {
   544 	g_WaitingForVote = true;
   545 	
   546 	if (IsVoteInProgress())
   547 	{
   548 		// Can't start a vote, try again in 5 seconds.
   549 		//g_RetryTimer = CreateTimer(5.0, Timer_StartMapVote, _, TIMER_FLAG_NO_MAPCHANGE);
   550 		
   551 		new Handle:data;
   552 		g_RetryTimer = CreateDataTimer(5.0, Timer_StartMapVote, data, TIMER_FLAG_NO_MAPCHANGE);
   553 		WritePackCell(data, _:when);
   554 		WritePackCell(data, _:inputlist);
   555 		ResetPack(data);
   556 		return;
   557 	}
   558 	
   559 	/* If the main map vote has completed (and chosen result) and its currently changing (not a delayed change) we block further attempts */
   560 	if (g_MapVoteCompleted && g_ChangeMapInProgress)
   561 	{
   562 		return;
   563 	}
   564 	
   565 	g_ChangeTime = when;
   566 	
   567 	g_WaitingForVote = false;
   568 		
   569 	g_HasVoteStarted = true;
   570 	g_VoteMenu = CreateMenu(Handler_MapVoteMenu, MenuAction:MENU_ACTIONS_ALL);
   571 	SetMenuTitle(g_VoteMenu, "Vote Nextmap");
   572 	SetVoteResultCallback(g_VoteMenu, Handler_MapVoteFinished);
   573 
   574 	/* Call OnMapVoteStarted() Forward */
   575 	Call_StartForward(g_MapVoteStartedForward);
   576 	Call_Finish();
   577 	
   578 	/**
   579 	 * TODO: Make a proper decision on when to clear the nominations list.
   580 	 * Currently it clears when used, and stays if an external list is provided.
   581 	 * Is this the right thing to do? External lists will probably come from places
   582 	 * like sm_mapvote from the adminmenu in the future.
   583 	 */
   584 	 
   585 	decl String:map[PLATFORM_MAX_PATH];
   586 	
   587 	/* No input given - User our internal nominations and maplist */
   588 	if (inputlist == INVALID_HANDLE)
   589 	{
   590 		new nominateCount = GetArraySize(g_NominateList);
   591 		new voteSize = GetConVarInt(g_Cvar_IncludeMaps);
   592 		
   593 		/* Smaller of the two - It should be impossible for nominations to exceed the size though (cvar changed mid-map?) */
   594 		new nominationsToAdd = nominateCount >= voteSize ? voteSize : nominateCount;
   595 		
   596 		
   597 		for (new i=0; i<nominationsToAdd; i++)
   598 		{
   599 			GetArrayString(g_NominateList, i, map, sizeof(map));
   600 			AddMenuItem(g_VoteMenu, map, map);
   601 			RemoveStringFromArray(g_NextMapList, map);
   602 			
   603 			/* Notify Nominations that this map is now free */
   604 			Call_StartForward(g_NominationsResetForward);
   605 			Call_PushString(map);
   606 			Call_PushCell(GetArrayCell(g_NominateOwners, i));
   607 			Call_Finish();
   608 		}
   609 		
   610 		/* Clear out the rest of the nominations array */
   611 		for (new i=nominationsToAdd; i<nominateCount; i++)
   612 		{
   613 			GetArrayString(g_NominateList, i, map, sizeof(map));
   614 			/* These maps shouldn't be excluded from the vote as they weren't really nominated at all */
   615 			
   616 			/* Notify Nominations that this map is now free */
   617 			Call_StartForward(g_NominationsResetForward);
   618 			Call_PushString(map);
   619 			Call_PushCell(GetArrayCell(g_NominateOwners, i));
   620 			Call_Finish();			
   621 		}
   622 		
   623 		/* There should currently be 'nominationsToAdd' unique maps in the vote */
   624 		
   625 		new i = nominationsToAdd;
   626 		new count = 0;
   627 		new availableMaps = GetArraySize(g_NextMapList);
   628 		
   629 		while (i < voteSize)
   630 		{
   631 			GetArrayString(g_NextMapList, count, map, sizeof(map));
   632 			count++;
   633 			
   634 			/* Insert the map and increment our count */
   635 			AddMenuItem(g_VoteMenu, map, map);
   636 			i++;
   637 			
   638 			if (count >= availableMaps)
   639 			{
   640 				//Run out of maps, this will have to do.
   641 				break;	
   642 			}
   643 		}
   644 		
   645 		/* Wipe out our nominations list - Nominations have already been informed of this */
   646 		ClearArray(g_NominateOwners);
   647 		ClearArray(g_NominateList);
   648 	}
   649 	else //We were given a list of maps to start the vote with
   650 	{
   651 		new size = GetArraySize(inputlist);
   652 		
   653 		for (new i=0; i<size; i++)
   654 		{
   655 			GetArrayString(inputlist, i, map, sizeof(map));
   656 			
   657 			if (IsMapValid(map))
   658 			{
   659 				AddMenuItem(g_VoteMenu, map, map);
   660 			}	
   661 		}
   662 	}
   663 	
   664 	/* Do we add any special items? */
   665 	if ((when == MapChange_Instant || when == MapChange_RoundEnd) && GetConVarBool(g_Cvar_DontChange))
   666 	{
   667 		AddMenuItem(g_VoteMenu, VOTE_DONTCHANGE, "Don't Change");
   668 	}
   669 	else if (GetConVarBool(g_Cvar_Extend) && g_Extends < GetConVarInt(g_Cvar_Extend))
   670 	{
   671 		AddMenuItem(g_VoteMenu, VOTE_EXTEND, "Extend Map");
   672 	}
   673 	
   674 	new voteDuration = GetConVarInt(g_Cvar_VoteDuration);
   675 
   676 	SetMenuExitButton(g_VoteMenu, false);
   677 	VoteMenuToAll(g_VoteMenu, voteDuration);
   678 
   679 	LogAction(-1, -1, "Voting for next map has started.");
   680 	PrintToChatAll("[SM] %t", "Nextmap Voting Started");
   681 }
   682 
   683 public Handler_VoteFinishedGeneric(Handle:menu,
   684 						   num_votes, 
   685 						   num_clients,
   686 						   const client_info[][2], 
   687 						   num_items,
   688 						   const item_info[][2])
   689 {
   690 	decl String:map[PLATFORM_MAX_PATH];
   691 	GetMenuItem(menu, item_info[0][VOTEINFO_ITEM_INDEX], map, sizeof(map));
   692 
   693 	if (strcmp(map, VOTE_EXTEND, false) == 0)
   694 	{
   695 		g_Extends++;
   696 		
   697 		new time;
   698 		if (GetMapTimeLimit(time))
   699 		{
   700 			if (time > 0)
   701 			{
   702 				ExtendMapTimeLimit(GetConVarInt(g_Cvar_ExtendTimeStep)*60);						
   703 			}
   704 		}
   705 		
   706 		if (g_Cvar_Winlimit != INVALID_HANDLE)
   707 		{
   708 			new winlimit = GetConVarInt(g_Cvar_Winlimit);
   709 			if (winlimit)
   710 			{
   711 				SetConVarInt(g_Cvar_Winlimit, winlimit + GetConVarInt(g_Cvar_ExtendRoundStep));
   712 			}					
   713 		}
   714 		
   715 		if (g_Cvar_Maxrounds != INVALID_HANDLE)
   716 		{
   717 			new maxrounds = GetConVarInt(g_Cvar_Maxrounds);
   718 			if (maxrounds)
   719 			{
   720 				SetConVarInt(g_Cvar_Maxrounds, maxrounds + GetConVarInt(g_Cvar_ExtendRoundStep));
   721 			}
   722 		}
   723 		
   724 		if (g_Cvar_Fraglimit != INVALID_HANDLE)
   725 		{
   726 			new fraglimit = GetConVarInt(g_Cvar_Fraglimit);
   727 			if (fraglimit)
   728 			{
   729 				SetConVarInt(g_Cvar_Fraglimit, fraglimit + GetConVarInt(g_Cvar_ExtendFragStep));						
   730 			}
   731 		}
   732 
   733 		PrintToChatAll("[SM] %t", "Current Map Extended", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes);
   734 		LogAction(-1, -1, "Voting for next map has finished. The current map has been extended.");
   735 		
   736 		// We extended, so we'll have to vote again.
   737 		g_HasVoteStarted = false;
   738 		CreateNextVote();
   739 		SetupTimeleftTimer();
   740 		
   741 	}
   742 	else if (strcmp(map, VOTE_DONTCHANGE, false) == 0)
   743 	{
   744 		PrintToChatAll("[SM] %t", "Current Map Stays", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes);
   745 		LogAction(-1, -1, "Voting for next map has finished. 'No Change' was the winner");
   746 		
   747 		g_HasVoteStarted = false;
   748 		CreateNextVote();
   749 		SetupTimeleftTimer();
   750 	}
   751 	else
   752 	{
   753 		if (g_ChangeTime == MapChange_MapEnd)
   754 		{
   755 			SetNextMap(map);
   756 		}
   757 		else if (g_ChangeTime == MapChange_Instant)
   758 		{
   759 			new Handle:data;
   760 			CreateDataTimer(2.0, Timer_ChangeMap, data);
   761 			WritePackString(data, map);
   762 			g_ChangeMapInProgress = false;
   763 		}
   764 		else // MapChange_RoundEnd
   765 		{
   766 			SetNextMap(map);
   767 			g_ChangeMapAtRoundEnd = true;
   768 		}
   769 		
   770 		g_HasVoteStarted = false;
   771 		g_MapVoteCompleted = true;
   772 		
   773 		PrintToChatAll("[SM] %t", "Nextmap Voting Finished", map, RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes);
   774 		LogAction(-1, -1, "Voting for next map has finished. Nextmap: %s.", map);
   775 	}	
   776 }
   777 
   778 public Handler_MapVoteFinished(Handle:menu,
   779 						   num_votes, 
   780 						   num_clients,
   781 						   const client_info[][2], 
   782 						   num_items,
   783 						   const item_info[][2])
   784 {
   785 	if (GetConVarBool(g_Cvar_RunOff) && num_items > 1)
   786 	{
   787 		new Float:winningvotes = float(item_info[0][VOTEINFO_ITEM_VOTES]);
   788 		new Float:required = num_votes * (GetConVarFloat(g_Cvar_RunOffPercent) / 100.0);
   789 		
   790 		if (winningvotes < required)
   791 		{
   792 			/* Insufficient Winning margin - Lets do a runoff */
   793 			g_VoteMenu = CreateMenu(Handler_MapVoteMenu, MenuAction:MENU_ACTIONS_ALL);
   794 			SetMenuTitle(g_VoteMenu, "Runoff Vote Nextmap");
   795 			SetVoteResultCallback(g_VoteMenu, Handler_VoteFinishedGeneric);
   796 
   797 			decl String:map[PLATFORM_MAX_PATH];
   798 			decl String:info1[PLATFORM_MAX_PATH];
   799 			decl String:info2[PLATFORM_MAX_PATH];
   800 			
   801 			GetMenuItem(menu, item_info[0][VOTEINFO_ITEM_INDEX], map, sizeof(map), _, info1, sizeof(info1));
   802 			AddMenuItem(g_VoteMenu, map, info1);
   803 			GetMenuItem(menu, item_info[1][VOTEINFO_ITEM_INDEX], map, sizeof(map), _, info2, sizeof(info2));
   804 			AddMenuItem(g_VoteMenu, map, info2);
   805 			
   806 			new voteDuration = GetConVarInt(g_Cvar_VoteDuration);
   807 			SetMenuExitButton(g_VoteMenu, false);
   808 			VoteMenuToAll(g_VoteMenu, voteDuration);
   809 			
   810 			/* Notify */
   811 			new Float:map1percent = float(item_info[0][VOTEINFO_ITEM_VOTES])/ float(num_votes) * 100;
   812 			new Float:map2percent = float(item_info[1][VOTEINFO_ITEM_VOTES])/ float(num_votes) * 100;
   813 			
   814 			
   815 			PrintToChatAll("[SM] %t", "Starting Runoff", GetConVarFloat(g_Cvar_RunOffPercent), info1, map1percent, info2, map2percent);
   816 			LogMessage("Voting for next map was indecisive, beginning runoff vote");
   817 					
   818 			return;
   819 		}
   820 	}
   821 	
   822 	Handler_VoteFinishedGeneric(menu, num_votes, num_clients, client_info, num_items, item_info);
   823 }
   824 
   825 public Handler_MapVoteMenu(Handle:menu, MenuAction:action, param1, param2)
   826 {
   827 	switch (action)
   828 	{
   829 		case MenuAction_End:
   830 		{
   831 			g_VoteMenu = INVALID_HANDLE;
   832 			CloseHandle(menu);
   833 		}
   834 		
   835 		case MenuAction_Display:
   836 		{
   837 	 		decl String:buffer[255];
   838 			Format(buffer, sizeof(buffer), "%T", "Vote Nextmap", param1);
   839 
   840 			new Handle:panel = Handle:param2;
   841 			SetPanelTitle(panel, buffer);
   842 		}		
   843 		
   844 		case MenuAction_DisplayItem:
   845 		{
   846 			if (GetMenuItemCount(menu) - 1 == param2)
   847 			{
   848 				decl String:map[PLATFORM_MAX_PATH], String:buffer[255];
   849 				GetMenuItem(menu, param2, map, sizeof(map));
   850 				if (strcmp(map, VOTE_EXTEND, false) == 0)
   851 				{
   852 					Format(buffer, sizeof(buffer), "%T", "Extend Map", param1);
   853 					return RedrawMenuItem(buffer);
   854 				}
   855 				else if (strcmp(map, VOTE_DONTCHANGE, false) == 0)
   856 				{
   857 					Format(buffer, sizeof(buffer), "%T", "Dont Change", param1);
   858 					return RedrawMenuItem(buffer);					
   859 				}
   860 			}
   861 		}		
   862 	
   863 		case MenuAction_VoteCancel:
   864 		{
   865 			// If we receive 0 votes, pick at random.
   866 			if (param1 == VoteCancel_NoVotes && GetConVarBool(g_Cvar_NoVoteMode))
   867 			{
   868 				new count = GetMenuItemCount(menu);
   869 				new item = GetRandomInt(0, count - 1);
   870 				decl String:map[PLATFORM_MAX_PATH];
   871 				GetMenuItem(menu, item, map, sizeof(map));
   872 				
   873 				while (strcmp(map, VOTE_EXTEND, false) == 0)
   874 				{
   875 					item = GetRandomInt(0, count - 1);
   876 					GetMenuItem(menu, item, map, sizeof(map));
   877 				}
   878 				
   879 				SetNextMap(map);
   880 				g_MapVoteCompleted = true;
   881 			}
   882 			else
   883 			{
   884 				// We were actually cancelled. I guess we do nothing.
   885 			}
   886 			
   887 			g_HasVoteStarted = false;
   888 		}
   889 	}
   890 	
   891 	return 0;
   892 }
   893 
   894 public Action:Timer_ChangeMap(Handle:hTimer, Handle:dp)
   895 {
   896 	g_ChangeMapInProgress = false;
   897 	
   898 	new String:map[PLATFORM_MAX_PATH];
   899 	
   900 	if (dp == INVALID_HANDLE)
   901 	{
   902 		if (!GetNextMap(map, sizeof(map)))
   903 		{
   904 			//No passed map and no set nextmap. fail!
   905 			return Plugin_Stop;	
   906 		}
   907 	}
   908 	else
   909 	{
   910 		ResetPack(dp);
   911 		ReadPackString(dp, map, sizeof(map));		
   912 	}
   913 	
   914 	ForceChangeLevel(map, "Map Vote");
   915 	
   916 	return Plugin_Stop;
   917 }
   918 
   919 bool:RemoveStringFromArray(Handle:array, String:str[])
   920 {
   921 	new index = FindStringInArray(array, str);
   922 	if (index != -1)
   923 	{
   924 		RemoveFromArray(array, index);
   925 		return true;
   926 	}
   927 	
   928 	return false;
   929 }
   930 
   931 CreateNextVote()
   932 {
   933 	assert(g_NextMapList)
   934 	ClearArray(g_NextMapList);
   935 	
   936 	decl String:map[PLATFORM_MAX_PATH];
   937 	new Handle:tempMaps  = CloneArray(g_MapList);
   938 	
   939 	GetCurrentMap(map, sizeof(map));
   940 	RemoveStringFromArray(tempMaps, map);
   941 	
   942 	if (GetConVarInt(g_Cvar_ExcludeMaps) && GetArraySize(tempMaps) > GetConVarInt(g_Cvar_ExcludeMaps))
   943 	{
   944 		for (new i = 0; i < GetArraySize(g_OldMapList); i++)
   945 		{
   946 			GetArrayString(g_OldMapList, i, map, sizeof(map));
   947 			RemoveStringFromArray(tempMaps, map);
   948 		}	
   949 	}
   950 
   951 	new limit = (GetConVarInt(g_Cvar_IncludeMaps) < GetArraySize(tempMaps) ? GetConVarInt(g_Cvar_IncludeMaps) : GetArraySize(tempMaps));
   952 	for (new i = 0; i < limit; i++)
   953 	{
   954 		new b = GetRandomInt(0, GetArraySize(tempMaps) - 1);
   955 		GetArrayString(tempMaps, b, map, sizeof(map));		
   956 		PushArrayString(g_NextMapList, map);
   957 		RemoveFromArray(tempMaps, b);
   958 	}
   959 	
   960 	CloseHandle(tempMaps);
   961 }
   962 
   963 bool:CanVoteStart()
   964 {
   965 	if (g_WaitingForVote || g_HasVoteStarted)
   966 	{
   967 		return false;	
   968 	}
   969 	
   970 	return true;
   971 }
   972 
   973 NominateResult:InternalNominateMap(String:map[], bool:force, owner)
   974 {
   975 	if (!IsMapValid(map))
   976 	{
   977 		return Nominate_InvalidMap;
   978 	}
   979 	
   980 	/* Map already in the vote */
   981 	if (FindStringInArray(g_NominateList, map) != -1)
   982 	{
   983 		return Nominate_AlreadyInVote;	
   984 	}
   985 	
   986 	new index;
   987 
   988 	/* Look to replace an existing nomination by this client - Nominations made with owner = 0 aren't replaced */
   989 	if (owner && ((index = FindValueInArray(g_NominateOwners, owner)) != -1))
   990 	{
   991 		new String:oldmap[PLATFORM_MAX_PATH];
   992 		GetArrayString(g_NominateList, index, oldmap, sizeof(oldmap));
   993 		Call_StartForward(g_NominationsResetForward);
   994 		Call_PushString(oldmap);
   995 		Call_PushCell(owner);
   996 		Call_Finish();
   997 		
   998 		SetArrayString(g_NominateList, index, map);
   999 		return Nominate_Replaced;
  1000 	}
  1001 	
  1002 	/* Too many nominated maps. */
  1003 	if (GetArraySize(g_NominateList) >= GetConVarInt(g_Cvar_IncludeMaps) && !force)
  1004 	{
  1005 		return Nominate_VoteFull;
  1006 	}
  1007 	
  1008 	PushArrayString(g_NominateList, map);
  1009 	PushArrayCell(g_NominateOwners, owner);
  1010 	
  1011 	while (GetArraySize(g_NominateList) > GetConVarInt(g_Cvar_IncludeMaps))
  1012 	{
  1013 		new String:oldmap[PLATFORM_MAX_PATH];
  1014 		GetArrayString(g_NominateList, 0, oldmap, sizeof(oldmap));
  1015 		Call_StartForward(g_NominationsResetForward);
  1016 		Call_PushString(oldmap);
  1017 		Call_PushCell(GetArrayCell(g_NominateOwners, 0));
  1018 		Call_Finish();
  1019 		
  1020 		RemoveFromArray(g_NominateList, 0);
  1021 		RemoveFromArray(g_NominateOwners, 0);
  1022 	}
  1023 	
  1024 	return Nominate_Added;
  1025 }
  1026 
  1027 /* Add natives to allow nominate and initiate vote to be call */
  1028 
  1029 /* native  bool:NominateMap(const String:map[], bool:force, &NominateError:error); */
  1030 public Native_NominateMap(Handle:plugin, numParams)
  1031 {
  1032 	new len;
  1033 	GetNativeStringLength(1, len);
  1034 	
  1035 	if (len <= 0)
  1036 	{
  1037 	  return false;
  1038 	}
  1039 	
  1040 	new String:map[len+1];
  1041 	GetNativeString(1, map, len+1);
  1042 	
  1043 	return _:InternalNominateMap(map, GetNativeCell(2), GetNativeCell(3));
  1044 }
  1045 
  1046 bool:InternalRemoveNominationByMap(String:map[])
  1047 {	
  1048 	for (new i = 0; i < GetArraySize(g_NominateList); i++)
  1049 	{
  1050 		new String:oldmap[PLATFORM_MAX_PATH];
  1051 		GetArrayString(g_NominateList, i, oldmap, sizeof(oldmap));
  1052 
  1053 		if(strcmp(map, oldmap, false) == 0)
  1054 		{
  1055 			Call_StartForward(g_NominationsResetForward);
  1056 			Call_PushString(oldmap);
  1057 			Call_PushCell(GetArrayCell(g_NominateOwners, i));
  1058 			Call_Finish();
  1059 
  1060 			RemoveFromArray(g_NominateList, i);
  1061 			RemoveFromArray(g_NominateOwners, i);
  1062 
  1063 			return true;
  1064 		}
  1065 	}
  1066 	
  1067 	return false;
  1068 }
  1069 
  1070 /* native  bool:RemoveNominationByMap(const String:map[]); */
  1071 public Native_RemoveNominationByMap(Handle:plugin, numParams)
  1072 {
  1073 	new len;
  1074 	GetNativeStringLength(1, len);
  1075 	
  1076 	if (len <= 0)
  1077 	{
  1078 	  return false;
  1079 	}
  1080 	
  1081 	new String:map[len+1];
  1082 	GetNativeString(1, map, len+1);
  1083 	
  1084 	return _:InternalRemoveNominationByMap(map);
  1085 }
  1086 
  1087 bool:InternalRemoveNominationByOwner(owner)
  1088 {	
  1089 	new index;
  1090 
  1091 	if (owner && ((index = FindValueInArray(g_NominateOwners, owner)) != -1))
  1092 	{
  1093 		new String:oldmap[PLATFORM_MAX_PATH];
  1094 		GetArrayString(g_NominateList, index, oldmap, sizeof(oldmap));
  1095 
  1096 		Call_StartForward(g_NominationsResetForward);
  1097 		Call_PushString(oldmap);
  1098 		Call_PushCell(owner);
  1099 		Call_Finish();
  1100 
  1101 		RemoveFromArray(g_NominateList, index);
  1102 		RemoveFromArray(g_NominateOwners, index);
  1103 
  1104 		return true;
  1105 	}
  1106 	
  1107 	return false;
  1108 }
  1109 
  1110 /* native  bool:RemoveNominationByOwner(owner); */
  1111 public Native_RemoveNominationByOwner(Handle:plugin, numParams)
  1112 {	
  1113 	return _:InternalRemoveNominationByOwner(GetNativeCell(1));
  1114 }
  1115 
  1116 /* native InitiateMapChooserVote(); */
  1117 public Native_InitiateVote(Handle:plugin, numParams)
  1118 {
  1119 	new MapChange:when = MapChange:GetNativeCell(1);
  1120 	new Handle:inputarray = Handle:GetNativeCell(2);
  1121 	
  1122 	LogAction(-1, -1, "Starting map vote because outside request");
  1123 	InitiateVote(when, inputarray);
  1124 }
  1125 
  1126 public Native_CanVoteStart(Handle:plugin, numParams)
  1127 {
  1128 	return CanVoteStart();	
  1129 }
  1130 
  1131 public Native_CheckVoteDone(Handle:plugin, numParams)
  1132 {
  1133 	return g_MapVoteCompleted;
  1134 }
  1135 
  1136 public Native_EndOfMapVoteEnabled(Handle:plugin, numParams)
  1137 {
  1138 	return GetConVarBool(g_Cvar_EndOfMapVote);
  1139 }
  1140 
  1141 public Native_GetExcludeMapList(Handle:plugin, numParams)
  1142 {
  1143 	new Handle:array = Handle:GetNativeCell(1);
  1144 	
  1145 	if (array == INVALID_HANDLE)
  1146 	{
  1147 		return;	
  1148 	}
  1149 	new size = GetArraySize(g_OldMapList);
  1150 	decl String:map[PLATFORM_MAX_PATH];
  1151 	
  1152 	for (new i=0; i<size; i++)
  1153 	{
  1154 		GetArrayString(g_OldMapList, i, map, sizeof(map));
  1155 		PushArrayString(array, map);	
  1156 	}
  1157 	
  1158 	return;
  1159 }
  1160 
  1161 public Native_GetNominatedMapList(Handle:plugin, numParams)
  1162 {
  1163 	new Handle:maparray = Handle:GetNativeCell(1);
  1164 	new Handle:ownerarray = Handle:GetNativeCell(2);
  1165 	
  1166 	if (maparray == INVALID_HANDLE)
  1167 		return;
  1168 
  1169 	decl String:map[PLATFORM_MAX_PATH];
  1170 
  1171 	for (new i = 0; i < GetArraySize(g_NominateList); i++)
  1172 	{
  1173 		GetArrayString(g_NominateList, i, map, sizeof(map));
  1174 		PushArrayString(maparray, map);
  1175 
  1176 		// If the optional parameter for an owner list was passed, then we need to fill that out as well
  1177 		if(ownerarray != INVALID_HANDLE)
  1178 		{
  1179 			new index = GetArrayCell(g_NominateOwners, i);
  1180 			PushArrayCell(ownerarray, index);
  1181 		}
  1182 	}
  1183 
  1184 	return;
  1185 }