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