1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/plugins/mapmanager.sp Sun Mar 30 07:00:22 2008 +0000
1.3 @@ -0,0 +1,362 @@
1.4 +/**
1.5 + * vim: set ts=4 :
1.6 + * =============================================================================
1.7 + * SourceMod Map Management Plugin
1.8 + * Provides all map related functionality, including map changing, map voting,
1.9 + * and nextmap.
1.10 + *
1.11 + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved.
1.12 + * =============================================================================
1.13 + *
1.14 + * This program is free software; you can redistribute it and/or modify it under
1.15 + * the terms of the GNU General Public License, version 3.0, as published by the
1.16 + * Free Software Foundation.
1.17 + *
1.18 + * This program is distributed in the hope that it will be useful, but WITHOUT
1.19 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.20 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.21 + * details.
1.22 + *
1.23 + * You should have received a copy of the GNU General Public License along with
1.24 + * this program. If not, see <http://www.gnu.org/licenses/>.
1.25 + *
1.26 + * As a special exception, AlliedModders LLC gives you permission to link the
1.27 + * code of this program (as well as its derivative works) to "Half-Life 2," the
1.28 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.29 + * by the Valve Corporation. You must obey the GNU General Public License in
1.30 + * all respects for all other code used. Additionally, AlliedModders LLC grants
1.31 + * this exception to all derivative works. AlliedModders LLC defines further
1.32 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.33 + * or <http://www.sourcemod.net/license.php>.
1.34 + *
1.35 + * Version: $Id$
1.36 + */
1.37 +
1.38 +#pragma semicolon 1
1.39 +#include <sourcemod>
1.40 +
1.41 +public Plugin:myinfo =
1.42 +{
1.43 + name = "Map Manager",
1.44 + author = "AlliedModders LLC",
1.45 + description = "Map Management",
1.46 + version = SOURCEMOD_VERSION,
1.47 + url = "http://www.sourcemod.net/"
1.48 +};
1.49 +
1.50 +#include "mapmanagement/globals.sp"
1.51 +#include "mapmanagement/commands.sp"
1.52 +#include "mapmanagement/events.sp"
1.53 +#include "mapmanagement/functions.sp"
1.54 +#include "mapmanagement/menus.sp"
1.55 +#include "mapmanagement/timers.sp"
1.56 +#include "mapmanagement/votes.sp"
1.57 +
1.58 +public OnPluginStart()
1.59 +{
1.60 + LoadTranslations("mapmanager.phrases");
1.61 +
1.62 + // Prepare nextmap functionality.
1.63 + g_VGUIMenu = GetUserMessageId("VGUIMenu");
1.64 + if (g_VGUIMenu == INVALID_MESSAGE_ID)
1.65 + {
1.66 + LogError("FATAL: Cannot find VGUIMenu user message id. MapManager crippled.");
1.67 + g_NextMapEnabled = false;
1.68 + }
1.69 + HookUserMessage(g_VGUIMenu, UserMsg_VGUIMenu);
1.70 +
1.71 + // Create all of the arrays, sized for a 64 character string.
1.72 + new arraySize = ByteCountToCells(64);
1.73 + g_MapCycle = CreateArray(arraySize);
1.74 + g_MapList = CreateArray(arraySize);
1.75 + g_MapHistory = CreateArray(arraySize);
1.76 + g_NextVoteMaps = CreateArray(arraySize);
1.77 + g_SelectedMaps = CreateArray(arraySize);
1.78 + g_NominatedMaps = CreateArray(arraySize);
1.79 +
1.80 + g_TeamScores = CreateArray(2);
1.81 +
1.82 + // Hook say
1.83 + RegConsoleCmd("say", Command_Say);
1.84 + RegConsoleCmd("say_team", Command_Say);
1.85 +
1.86 + // Register all commands.
1.87 + RegAdminCmd("sm_map", Command_Map, ADMFLAG_CHANGEMAP, "sm_map <map> [r/e]");
1.88 + RegAdminCmd("sm_setnextmap", Command_SetNextmap, ADMFLAG_CHANGEMAP, "sm_setnextmap <map>");
1.89 + RegAdminCmd("sm_votemap", Command_Votemap, ADMFLAG_VOTE|ADMFLAG_CHANGEMAP, "sm_votemap <mapname> [mapname2] ... [mapname5] ");
1.90 + RegAdminCmd("sm_maplist", Command_List, ADMFLAG_GENERIC, "sm_maplist");
1.91 + RegAdminCmd("sm_nominate", Command_Addmap, ADMFLAG_CHANGEMAP, "sm_nominate <mapname> - Nominates a map for RockTheVote and MapChooser. Overrides existing nominations.");
1.92 + RegAdminCmd("sm_mapvote", Command_Mapvote, ADMFLAG_CHANGEMAP, "sm_mapvote - Forces the MapChooser vote to occur.");
1.93 +
1.94 + if (GetCommandFlags("nextmap") == INVALID_FCVAR_FLAGS)
1.95 + {
1.96 + RegServerCmd("nextmap", Command_Nextmap);
1.97 + }
1.98 +
1.99 + // Register all convars
1.100 + g_Cvar_Nextmap = CreateConVar("sm_nextmap", "", "Sets the Next Map", FCVAR_NOTIFY);
1.101 +
1.102 + 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);
1.103 + 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);
1.104 +
1.105 + g_Cvar_MapChooser = CreateConVar("sm_mm_mapchooser", "0", "Enables MapChooser (End of Map voting)", 0, true, 0.0, true, 1.0);
1.106 + g_Cvar_RockTheVote = CreateConVar("sm_mm_rockthevote", "0", "Enables RockTheVote (Player initiated map votes)", 0, true, 0.0, true, 1.0);
1.107 + g_Cvar_Randomize = CreateConVar("sm_mm_randomize", "0", "Enabled Randomizer (Randomly picks the next map)", 0, true, 0.0, true, 1.0);
1.108 + g_Cvar_Nominate = CreateConVar("sm_mm_nominate", "1", "Enables nomination system.", 0, true, 0.0, true, 1.0);
1.109 +
1.110 + g_Cvar_VoteMap = CreateConVar("sm_mm_votemap", "0.60", "Percentage required for successful sm_votemap.", 0, true, 0.05, true, 1.0);
1.111 + g_Cvar_RTVLimit = CreateConVar("sm_mm_rtvlimit", "0.60", "Required percentage of players needed to rockthevote", 0, true, 0.05, true, 1.0);
1.112 + g_Cvar_MinPlayers = CreateConVar("sm_mm_minplayers", "0", "Number of players required before RTV will be enabled.", 0, true, 0.0, true, float(MAXPLAYERS));
1.113 +
1.114 + g_Cvar_StartTime = CreateConVar("sm_mapvote_start", "3.0", "Specifies when to start the vote based on time remaining.", _, true, 1.0);
1.115 + g_Cvar_StartRounds = CreateConVar("sm_mapvote_startround", "2.0", "Specifies when to start the vote based on rounds remaining.", _, true, 1.0);
1.116 + g_Cvar_StartFrags = CreateConVar("sm_mapvote_startfrags", "5.0", "Specifies when to start the vote base on frags remaining.", _, true, 1.0);
1.117 + g_Cvar_ExtendTimeMax = CreateConVar("sm_extendmap_maxtime", "90", "Specifies the maximum amount of time a map can be extended", _, true, 0.0);
1.118 + g_Cvar_ExtendTimeStep = CreateConVar("sm_extendmap_timestep", "15", "Specifies how much many more minutes each extension makes", _, true, 5.0);
1.119 + g_Cvar_ExtendRoundMax = CreateConVar("sm_extendmap_maxrounds", "30", "Specfies the maximum amount of rounds a map can be extended", _, true, 0.0);
1.120 + g_Cvar_ExtendRoundStep = CreateConVar("sm_extendmap_roundstep", "5", "Specifies how many more rounds each extension makes", _, true, 5.0);
1.121 + g_Cvar_ExtendFragMax = CreateConVar("sm_extendmap_maxfrags", "150", "Specfies the maximum frags allowed that a map can be extended.", _, true, 0.0);
1.122 + g_Cvar_ExtendFragStep = CreateConVar("sm_extendmap_fragstep", "10", "Specifies how many more frags are allowed when map is extended.", _, true, 5.0);
1.123 + g_Cvar_Mapfile = CreateConVar("sm_mapvote_file", "configs/maps.ini", "Map file to use. (Def sourcemod/configs/maps.ini)");
1.124 + g_Cvar_ExcludeMaps = CreateConVar("sm_mapvote_exclude", "5", "Specifies how many past maps to exclude from the vote.", _, true, 0.0);
1.125 + g_Cvar_IncludeMaps = CreateConVar("sm_mapvote_include", "5", "Specifies how many maps to include in the vote.", _, true, 2.0, true, 6.0);
1.126 + 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);
1.127 + 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);
1.128 +
1.129 + // Find game convars
1.130 + g_Cvar_Chattime = FindConVar("mp_chattime");
1.131 + g_Cvar_Winlimit = FindConVar("mp_winlimit");
1.132 + g_Cvar_Maxrounds = FindConVar("mp_maxrounds");
1.133 + g_Cvar_Fraglimit = FindConVar("mp_fraglimit");
1.134 +
1.135 + if (GetCommandFlags("nextmap") == INVALID_FCVAR_FLAGS)
1.136 + {
1.137 + RegServerCmd("nextmap", Command_Nextmap);
1.138 + }
1.139 +
1.140 + // Hook events
1.141 + HookEvent("round_end", Event_RoundEnd); // We always require round_end
1.142 + if (g_Cvar_Fraglimit != INVALID_HANDLE)
1.143 + {
1.144 + HookEvent("player_death", Event_PlayerDeath);
1.145 + }
1.146 +
1.147 + // Set to the current map so OnMapStart() will know what to do
1.148 + decl String:currentMap[64];
1.149 + GetCurrentMap(currentMap, 64);
1.150 + SetNextmap(currentMap);
1.151 +
1.152 + // Create necessary menus for TopMenu
1.153 + g_Menu_Map = CreateMenu(MenuHandler_Map);
1.154 + SetMenuTitle(g_Menu_Map, "Please select a map");
1.155 + SetMenuExitBackButton(g_Menu_Map, true);
1.156 +
1.157 + g_Menu_Votemap = CreateMenu(MenuHandler_VoteMap, MenuAction_DrawItem);
1.158 + SetMenuTitle(g_Menu_Votemap, "Please select a map");
1.159 + SetMenuExitBackButton(g_Menu_Votemap, true);
1.160 +
1.161 + // Bind TopMenu commands to adminmenu_maplist.ini, in cases it doesn't exist in maplists.cfg
1.162 + decl String:mapListPath[PLATFORM_MAX_PATH];
1.163 + BuildPath(Path_SM, mapListPath, sizeof(mapListPath), "configs/adminmenu_maplist.ini");
1.164 + SetMapListCompatBind("sm_map menu", mapListPath);
1.165 + SetMapListCompatBind("sm_votemap menu", mapListPath);
1.166 +
1.167 + // Account for late loading
1.168 + new Handle:topmenu;
1.169 + if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE))
1.170 + {
1.171 + OnAdminMenuReady(topmenu);
1.172 + }
1.173 +
1.174 + AutoExecConfig(true, "mapmanager");
1.175 +}
1.176 +
1.177 +public OnAdminMenuReady(Handle:topmenu)
1.178 +{
1.179 + /* Block us from being called twice */
1.180 + if (topmenu == hTopMenu)
1.181 + {
1.182 + return;
1.183 + }
1.184 +
1.185 + /* Save the Handle */
1.186 + hTopMenu = topmenu;
1.187 +
1.188 + new TopMenuObject:server_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_SERVERCOMMANDS);
1.189 +
1.190 + if (server_commands != INVALID_TOPMENUOBJECT)
1.191 + {
1.192 + AddToTopMenu(hTopMenu,
1.193 + "sm_map",
1.194 + TopMenuObject_Item,
1.195 + AdminMenu_Map,
1.196 + server_commands,
1.197 + "sm_map",
1.198 + ADMFLAG_CHANGEMAP);
1.199 + }
1.200 +
1.201 + new TopMenuObject:voting_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_VOTINGCOMMANDS);
1.202 +
1.203 + if (voting_commands != INVALID_TOPMENUOBJECT)
1.204 + {
1.205 + AddToTopMenu(hTopMenu,
1.206 + "sm_votemap",
1.207 + TopMenuObject_Item,
1.208 + AdminMenu_VoteMap,
1.209 + voting_commands,
1.210 + "sm_votemap",
1.211 + ADMFLAG_VOTE|ADMFLAG_CHANGEMAP);
1.212 + }
1.213 +}
1.214 +
1.215 +public OnLibraryRemoved(const String:name[])
1.216 +{
1.217 + if (strcmp(name, "adminmenu") == 0)
1.218 + {
1.219 + hTopMenu = INVALID_HANDLE;
1.220 + }
1.221 +}
1.222 +
1.223 +public OnConfigsExecuted()
1.224 +{
1.225 + // Add map logic here
1.226 +
1.227 + // Get the current and last maps.
1.228 + decl String:lastMap[64], String:currentMap[64];
1.229 + GetConVarString(g_Cvar_Nextmap, lastMap, 64);
1.230 + GetCurrentMap(currentMap, 64);
1.231 +
1.232 + // 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
1.233 + // some other plugin/command), we don't want to scramble the map cycle. Or for example, admin switches to a custom map
1.234 + // not in mapcyclefile. So we keep it set to the last expected nextmap. - ferret
1.235 + if (strcmp(lastMap, currentMap) == 0)
1.236 + {
1.237 + FindAndSetNextMap();
1.238 + }
1.239 +
1.240 + // Build map menus for sm_map, sm_votemap, and RTV.
1.241 + BuildMapMenu(g_Menu_Map, list);
1.242 + BuildMapMenu(g_Menu_VoteMap, list);
1.243 +
1.244 + // If the Randomize option is on, randomize!
1.245 + if (GetConVarBool(g_Cvar_Randomize))
1.246 + {
1.247 + CreateTimer(5.0, Timer_RandomizeNextmap);
1.248 + }
1.249 +
1.250 + // If MapChooser is active, start it up!
1.251 + if (GetConVarBool(g_Cvar_MapChooser))
1.252 + {
1.253 + SetupTimeleftTimer();
1.254 + SetConVarString(g_Cvar_Nextmap, "Pending Vote");
1.255 + }
1.256 +
1.257 + // If RockTheVote is active, start it up!
1.258 + if (GetConVarBool(g_Cvar_RockTheVote))
1.259 + {
1.260 + BuildMapMenu(g_Menu_RTV, list);
1.261 + CreateTimer(30.0, Timer_DelayRTV);
1.262 + }
1.263 +}
1.264 +
1.265 +// Reinitialize all our various globals
1.266 +public OnMapStart()
1.267 +{
1.268 + if (g_Nominate != INVALID_HANDLE)
1.269 + {
1.270 + ClearArray(g_Nominate);
1.271 + }
1.272 +
1.273 + if (g_TeamScores != INVALID_HANDLE)
1.274 + {
1.275 + ClearArray(g_TeamScores);
1.276 + }
1.277 +
1.278 + g_TotalRounds = 0;
1.279 +
1.280 + g_RTV_Voters = 0;
1.281 + g_RTV_Votes = 0;
1.282 + g_RTV_VotesNeeded = 0;
1.283 + g_RTV_Started = false;
1.284 + g_RTV_Ended = false;
1.285 +}
1.286 +
1.287 +// Reset globals as necessary and kill timers
1.288 +public OnMapEnd()
1.289 +{
1.290 + g_IntermissionCalled = false;
1.291 + g_HasVoteStarted = false;
1.292 +
1.293 + g_RTV_Allowed = false;
1.294 +
1.295 + if (g_VoteTimer != INVALID_HANDLE)
1.296 + {
1.297 + KillTimer(g_VoteTimer);
1.298 + g_VoteTimer = INVALID_HANDLE;
1.299 + }
1.300 +
1.301 + if (g_RetryTimer != INVALID_HANDLE)
1.302 + {
1.303 + KillTimer(g_RetryTimer);
1.304 + g_RetryTimer = INVALID_HANDLE;
1.305 + }
1.306 +}
1.307 +
1.308 +public bool:OnClientConnect(client, String:rejectmsg[], maxlen)
1.309 +{
1.310 + if (IsFakeClient(client))
1.311 + {
1.312 + return true;
1.313 + }
1.314 +
1.315 + // If RTV is active, deal with vote counting.
1.316 + if (GetConVarBool(g_Cvar_RockTheVote))
1.317 + {
1.318 + g_RTV_Voted[client] = false;
1.319 + g_RTV_Voters++;
1.320 + g_RTV_VotesNeeded = RoundToFloor(float(g_Voters) * GetConVarFloat(g_Cvar_Needed));
1.321 + }
1.322 +
1.323 + // If Nominate is active, let the new client nominate
1.324 + if (GetConVarBool(g_Cvar_Nominate))
1.325 + {
1.326 + g_Nominated[client] = false;
1.327 + }
1.328 +
1.329 + return true;
1.330 +}
1.331 +
1.332 +public OnClientDisconnect(client)
1.333 +{
1.334 + if (IsFakeClient(client))
1.335 + {
1.336 + return;
1.337 + }
1.338 +
1.339 + // If RTV is active, deal with vote counting.
1.340 + if (GetConVarBool(g_Cvar_RockTheVote))
1.341 + {
1.342 + if(g_RTV_Voted[client])
1.343 + {
1.344 + g_RTV_Votes--;
1.345 + }
1.346 +
1.347 + g_RTV_Voters--;
1.348 + g_RTV_VotesNeeded = RoundToFloor(float(g_RTV_Voters) * GetConVarFloat(g_Cvar_RTVLimit));
1.349 +
1.350 + // If this client caused us to fall below the RTV threshold and its allowed be started, start it.
1.351 + if (g_RTV_Votes && g_RTV_Voters && g_RTV_Votes >= g_RTV_VotesNeeded && g_RTV_Allowed && !g_RTV_Started)
1.352 + {
1.353 + g_RTV_Started = true;
1.354 + CreateTimer(2.0, Timer_StartRTV, TIMER_FLAG_NO_MAPCHANGE);
1.355 + }
1.356 + }
1.357 +}
1.358 +
1.359 +public OnMapTimeLeftChanged()
1.360 +{
1.361 + if (GetConVarBool(g_Cvar_MapChooser))
1.362 + {
1.363 + SetupTimeleftTimer();
1.364 + }
1.365 +}
1.366 \ No newline at end of file