plugins/mapmanager.sp
author Matt Woodrow <pred@alliedmods.net>
Mon Mar 31 22:44:18 2008 +0000 (2008-03-31)
changeset 1953 e0ca4605d828
parent 1952 b7aa2b626807
child 2027 887d7271f947
permissions -rw-r--r--
Big changes to dynamic menu. Removed constant kv tracking. Tested everything.
Import of config files.
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod Map Management Plugin
     5  * Provides all map related functionality, including map changing, map voting,
     6  * and nextmap.
     7  *
     8  * SourceMod (C)2004-2008 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 #pragma semicolon 1
    36 #include <sourcemod>
    37 
    38 public Plugin:myinfo =
    39 {
    40 	name = "Map Manager",
    41 	author = "AlliedModders LLC",
    42 	description = "Map Management",
    43 	version = SOURCEMOD_VERSION,
    44 	url = "http://www.sourcemod.net/"
    45 };
    46 
    47 #include "mapmanagement/globals.sp"
    48 #include "mapmanagement/commands.sp"
    49 #include "mapmanagement/events.sp"
    50 #include "mapmanagement/functions.sp"
    51 #include "mapmanagement/menus.sp"
    52 #include "mapmanagement/timers.sp"
    53 #include "mapmanagement/votes.sp"
    54 
    55 public OnPluginStart()
    56 {	
    57 	LoadTranslations("mapmanager.phrases");
    58 
    59 	// Prepare nextmap functionality.	
    60 	g_VGUIMenu = GetUserMessageId("VGUIMenu");
    61 	if (g_VGUIMenu == INVALID_MESSAGE_ID)
    62 	{
    63 		LogError("FATAL: Cannot find VGUIMenu user message id. MapManager crippled.");
    64 		g_NextMapEnabled = false;
    65 	}
    66 	HookUserMessage(g_VGUIMenu, UserMsg_VGUIMenu);
    67 
    68 	// Create all of the arrays, sized for a 64 character string.
    69 	new arraySize = ByteCountToCells(64);
    70 	g_MapCycle = CreateArray(arraySize);
    71 	g_MapList = CreateArray(arraySize);
    72 	g_MapHistory = CreateArray(arraySize);
    73 	g_NextVoteMaps = CreateArray(arraySize);
    74 	g_SelectedMaps = CreateArray(arraySize);
    75 	g_NominatedMaps = CreateArray(arraySize);
    76 	
    77 	g_TeamScores = CreateArray(2);
    78 
    79 	// Hook say
    80 	RegConsoleCmd("say", Command_Say);
    81 	RegConsoleCmd("say_team", Command_Say);	
    82 
    83 	// Register all commands.
    84 	RegAdminCmd("sm_map", Command_Map, ADMFLAG_CHANGEMAP, "sm_map <map> [r/e]");
    85 	RegAdminCmd("sm_setnextmap", Command_SetNextmap, ADMFLAG_CHANGEMAP, "sm_setnextmap <map>");
    86 	RegAdminCmd("sm_votemap", Command_Votemap, ADMFLAG_VOTE|ADMFLAG_CHANGEMAP, "sm_votemap <mapname> [mapname2] ... [mapname5] ");
    87 	RegAdminCmd("sm_maplist", Command_List, ADMFLAG_GENERIC, "sm_maplist");
    88 	RegAdminCmd("sm_nominate", Command_Addmap, ADMFLAG_CHANGEMAP, "sm_nominate <mapname> - Nominates a map for RockTheVote and MapChooser. Overrides existing nominations.");
    89 	RegAdminCmd("sm_mapvote", Command_Mapvote, ADMFLAG_CHANGEMAP, "sm_mapvote - Forces the MapChooser vote to occur.");
    90 	
    91 	if (GetCommandFlags("nextmap") == INVALID_FCVAR_FLAGS)
    92 	{
    93 		RegServerCmd("nextmap", Command_Nextmap);
    94 	}	
    95 	
    96 	// Register all convars
    97 	g_Cvar_Nextmap = CreateConVar("sm_nextmap", "", "Sets the Next Map", FCVAR_NOTIFY);
    98 	
    99 	g_Cvar_MapCount = CreateConVar("sm_mm_maps", "4", "Number of maps to be voted on at end of map or RTV. 2 to 6. (Def 4)", 0, true, 2.0, true, 6.0);	
   100 	g_Cvar_Excludes = CreateConVar("sm_mm_exclude", "5", "Specifies how many past maps to exclude from end of map vote and RTV.", _, true, 0.0);
   101 	
   102 	g_Cvar_MapChooser = CreateConVar("sm_mm_mapchooser", "0", "Enables MapChooser (End of Map voting)", 0, true, 0.0, true, 1.0);
   103 	g_Cvar_RockTheVote = CreateConVar("sm_mm_rockthevote", "0", "Enables RockTheVote (Player initiated map votes)", 0, true, 0.0, true, 1.0);
   104 	g_Cvar_Randomize = CreateConVar("sm_mm_randomize", "0", "Enabled Randomizer (Randomly picks the next map)", 0, true, 0.0, true, 1.0);
   105 	g_Cvar_Nominate = CreateConVar("sm_mm_nominate", "1", "Enables nomination system.", 0, true, 0.0, true, 1.0);
   106 	
   107 	g_Cvar_VoteMap = CreateConVar("sm_mm_votemap", "0.60", "Percentage required for successful sm_votemap.", 0, true, 0.05, true, 1.0);
   108 	g_Cvar_RTVLimit = CreateConVar("sm_mm_rtvlimit", "0.60", "Required percentage of players needed to rockthevote", 0, true, 0.05, true, 1.0);
   109 	g_Cvar_MinPlayers = CreateConVar("sm_mm_minplayers", "0", "Number of players required before RTV will be enabled.", 0, true, 0.0, true, float(MAXPLAYERS));
   110 	
   111 	g_Cvar_StartTime = CreateConVar("sm_mapvote_start", "3.0", "Specifies when to start the vote based on time remaining.", _, true, 1.0);
   112 	g_Cvar_StartRounds = CreateConVar("sm_mapvote_startround", "2.0", "Specifies when to start the vote based on rounds remaining.", _, true, 1.0);
   113 	g_Cvar_StartFrags = CreateConVar("sm_mapvote_startfrags", "5.0", "Specifies when to start the vote base on frags remaining.", _, true, 1.0);
   114 	g_Cvar_ExtendTimeMax = CreateConVar("sm_extendmap_maxtime", "90", "Specifies the maximum amount of time a map can be extended", _, true, 0.0);
   115 	g_Cvar_ExtendTimeStep = CreateConVar("sm_extendmap_timestep", "15", "Specifies how much many more minutes each extension makes", _, true, 5.0);
   116 	g_Cvar_ExtendRoundMax = CreateConVar("sm_extendmap_maxrounds", "30", "Specfies the maximum amount of rounds a map can be extended", _, true, 0.0);
   117 	g_Cvar_ExtendRoundStep = CreateConVar("sm_extendmap_roundstep", "5", "Specifies how many more rounds each extension makes", _, true, 5.0);
   118 	g_Cvar_ExtendFragMax = CreateConVar("sm_extendmap_maxfrags", "150", "Specfies the maximum frags allowed that a map can be extended.", _, true, 0.0);
   119 	g_Cvar_ExtendFragStep = CreateConVar("sm_extendmap_fragstep", "10", "Specifies how many more frags are allowed when map is extended.", _, true, 5.0);	
   120 	g_Cvar_Mapfile = CreateConVar("sm_mapvote_file", "configs/maps.ini", "Map file to use. (Def sourcemod/configs/maps.ini)");
   121 	g_Cvar_ExcludeMaps = CreateConVar("sm_mapvote_exclude", "5", "Specifies how many past maps to exclude from the vote.", _, true, 0.0);
   122 	g_Cvar_IncludeMaps = CreateConVar("sm_mapvote_include", "5", "Specifies how many maps to include in the vote.", _, true, 2.0, true, 6.0);
   123 	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);
   124 	g_Cvar_Extend = CreateConVar("sm_mapvote_extend", "1", "Specifies whether or not MapChooser will allow the map to be extended.", _, true, 0.0, true, 1.0);
   125 	
   126 	// Find game convars
   127 	g_Cvar_Chattime = FindConVar("mp_chattime");
   128 	g_Cvar_Winlimit = FindConVar("mp_winlimit");
   129 	g_Cvar_Maxrounds = FindConVar("mp_maxrounds");
   130 	g_Cvar_Fraglimit = FindConVar("mp_fraglimit");	
   131 
   132 	if (GetCommandFlags("nextmap") == INVALID_FCVAR_FLAGS)
   133 	{
   134 		RegServerCmd("nextmap", Command_Nextmap);
   135 	}
   136 	
   137 	// Hook events
   138 	HookEvent("round_end", Event_RoundEnd);	// We always require round_end
   139 	if (g_Cvar_Fraglimit != INVALID_HANDLE)
   140 	{
   141 		HookEvent("player_death", Event_PlayerDeath);		
   142 	}	
   143 	
   144 	// Set to the current map so OnMapStart() will know what to do
   145 	decl String:currentMap[64];
   146 	GetCurrentMap(currentMap, 64);
   147 	SetNextmap(currentMap);	
   148 
   149 	// Create necessary menus for TopMenu
   150 	g_Menu_Map = CreateMenu(MenuHandler_Map);
   151 	SetMenuTitle(g_Menu_Map, "Please select a map");
   152 	SetMenuExitBackButton(g_Menu_Map, true);
   153 	
   154 	g_Menu_Votemap = CreateMenu(MenuHandler_VoteMap, MenuAction_DrawItem);
   155 	SetMenuTitle(g_Menu_Votemap, "Please select a map");
   156 	SetMenuExitBackButton(g_Menu_Votemap, true);	
   157 	
   158 	// Bind TopMenu commands to adminmenu_maplist.ini, in cases it doesn't exist in maplists.cfg
   159 	decl String:mapListPath[PLATFORM_MAX_PATH];
   160 	BuildPath(Path_SM, mapListPath, sizeof(mapListPath), "configs/adminmenu_maplist.ini");
   161 	SetMapListCompatBind("sm_map menu", mapListPath);
   162 	SetMapListCompatBind("sm_votemap menu", mapListPath);
   163 
   164 	// Account for late loading
   165 	new Handle:topmenu;
   166 	if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE))
   167 	{
   168 		OnAdminMenuReady(topmenu);
   169 	}	
   170 	
   171 	AutoExecConfig(true, "mapmanager");
   172 }
   173 
   174 public OnAdminMenuReady(Handle:topmenu)
   175 {
   176 	/* Block us from being called twice */
   177 	if (topmenu == hTopMenu)
   178 	{
   179 		return;
   180 	}
   181 	
   182 	/* Save the Handle */
   183 	hTopMenu = topmenu;
   184 	
   185 	new TopMenuObject:server_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_SERVERCOMMANDS);
   186 
   187 	if (server_commands != INVALID_TOPMENUOBJECT)
   188 	{
   189 		AddToTopMenu(hTopMenu,
   190 			"sm_map",
   191 			TopMenuObject_Item,
   192 			AdminMenu_Map,
   193 			server_commands,
   194 			"sm_map",
   195 			ADMFLAG_CHANGEMAP);
   196 	}
   197 
   198 	new TopMenuObject:voting_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_VOTINGCOMMANDS);
   199 
   200 	if (voting_commands != INVALID_TOPMENUOBJECT)
   201 	{
   202 		AddToTopMenu(hTopMenu,
   203 			"sm_votemap",
   204 			TopMenuObject_Item,
   205 			AdminMenu_VoteMap,
   206 			voting_commands,
   207 			"sm_votemap",
   208 			ADMFLAG_VOTE|ADMFLAG_CHANGEMAP);
   209 	}
   210 }
   211 
   212 public OnLibraryRemoved(const String:name[])
   213 {
   214 	if (strcmp(name, "adminmenu") == 0)
   215 	{
   216 		hTopMenu = INVALID_HANDLE;
   217 	}
   218 }
   219 
   220 public OnConfigsExecuted()
   221 {
   222 	// Add map logic here
   223 	
   224 	// Get the current and last maps.
   225 	decl String:lastMap[64], String:currentMap[64];
   226 	GetConVarString(g_Cvar_Nextmap, lastMap, 64);
   227 	GetCurrentMap(currentMap, 64);
   228 	
   229 	// Why am I doing this? If we switched to a new map, but it wasn't what we expected (Due to sm_map, sm_votemap, or
   230 	// some other plugin/command), we don't want to scramble the map cycle. Or for example, admin switches to a custom map
   231 	// not in mapcyclefile. So we keep it set to the last expected nextmap. - ferret
   232 	if (strcmp(lastMap, currentMap) == 0)
   233 	{
   234 		FindAndSetNextMap();
   235 	}
   236 	
   237 	// Build map menus for sm_map, sm_votemap, and RTV.
   238 	BuildMapMenu(g_Menu_Map, list);
   239 	BuildMapMenu(g_Menu_VoteMap, list);
   240 	
   241 	// If the Randomize option is on, randomize!
   242 	if (GetConVarBool(g_Cvar_Randomize))
   243 	{
   244 		CreateTimer(5.0, Timer_RandomizeNextmap);
   245 	}
   246 	
   247 	// If MapChooser is active, start it up!
   248 	if (GetConVarBool(g_Cvar_MapChooser))
   249 	{
   250 		SetupTimeleftTimer();
   251 		SetConVarString(g_Cvar_Nextmap, "Pending Vote");
   252 	}
   253 	
   254 	// If RockTheVote is active, start it up!
   255 	if (GetConVarBool(g_Cvar_RockTheVote))
   256 	{
   257 		BuildMapMenu(g_Menu_RTV, list);
   258 		CreateTimer(30.0, Timer_DelayRTV);
   259 	}	
   260 }
   261 
   262 // Reinitialize all our various globals
   263 public OnMapStart()
   264 {
   265 	if (g_Nominate != INVALID_HANDLE)
   266 	{
   267 		ClearArray(g_Nominate);
   268 	}
   269 	
   270 	if (g_TeamScores != INVALID_HANDLE)
   271 	{
   272 		ClearArray(g_TeamScores);
   273 	}
   274 
   275 	g_TotalRounds = 0;	
   276 	
   277 	g_RTV_Voters = 0;
   278 	g_RTV_Votes = 0;
   279 	g_RTV_VotesNeeded = 0;
   280 	g_RTV_Started = false;
   281 	g_RTV_Ended = false;
   282 }
   283 
   284 // Reset globals as necessary and kill timers
   285 public OnMapEnd()
   286 {
   287 	g_IntermissionCalled = false;
   288 	g_HasVoteStarted = false;
   289 		
   290 	g_RTV_Allowed = false;	
   291 	
   292 	if (g_VoteTimer != INVALID_HANDLE)
   293 	{
   294 		KillTimer(g_VoteTimer);
   295 		g_VoteTimer = INVALID_HANDLE;
   296 	}
   297 	
   298 	if (g_RetryTimer != INVALID_HANDLE)
   299 	{
   300 		KillTimer(g_RetryTimer);
   301 		g_RetryTimer = INVALID_HANDLE;
   302 	}
   303 }
   304 
   305 public bool:OnClientConnect(client, String:rejectmsg[], maxlen)
   306 {
   307 	if (IsFakeClient(client))
   308 	{
   309 		return true;
   310 	}
   311 	
   312 	// If RTV is active, deal with vote counting.
   313 	if (GetConVarBool(g_Cvar_RockTheVote))
   314 	{
   315 		g_RTV_Voted[client] = false;
   316 		g_RTV_Voters++;
   317 		g_RTV_VotesNeeded = RoundToFloor(float(g_Voters) * GetConVarFloat(g_Cvar_Needed));
   318 	}
   319 
   320 	// If Nominate is active, let the new client nominate
   321 	if (GetConVarBool(g_Cvar_Nominate))
   322 	{
   323 		g_Nominated[client] = false;
   324 	}
   325 	
   326 	return true;
   327 }
   328 
   329 public OnClientDisconnect(client)
   330 {
   331 	if (IsFakeClient(client))
   332 	{
   333 		return;
   334 	}
   335 	
   336 	// If RTV is active, deal with vote counting.
   337 	if (GetConVarBool(g_Cvar_RockTheVote))
   338 	{
   339 		if(g_RTV_Voted[client])
   340 		{
   341 			g_RTV_Votes--;
   342 		}
   343 
   344 		g_RTV_Voters--;
   345 		g_RTV_VotesNeeded = RoundToFloor(float(g_RTV_Voters) * GetConVarFloat(g_Cvar_RTVLimit));
   346 		
   347 		// If this client caused us to fall below the RTV threshold and its allowed be started, start it.	
   348 		if (g_RTV_Votes && g_RTV_Voters && g_RTV_Votes >= g_RTV_VotesNeeded && g_RTV_Allowed && !g_RTV_Started) 
   349 		{
   350 			g_RTV_Started = true;
   351 			CreateTimer(2.0, Timer_StartRTV, TIMER_FLAG_NO_MAPCHANGE);
   352 		}	
   353 	}
   354 }
   355 
   356 public OnMapTimeLeftChanged()
   357 {
   358 	if (GetConVarBool(g_Cvar_MapChooser))
   359 	{
   360 		SetupTimeleftTimer();
   361 	}
   362 }