diff --git a/plugins/mapmanager.sp b/plugins/mapmanager.sp new file mode 100644 index 00000000..dbf3ec39 --- /dev/null +++ b/plugins/mapmanager.sp @@ -0,0 +1,362 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Map Management Plugin + * Provides all map related functionality, including map changing, map voting, + * and nextmap. + * + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#pragma semicolon 1 +#include + +public Plugin:myinfo = +{ + name = "Map Manager", + author = "AlliedModders LLC", + description = "Map Management", + version = SOURCEMOD_VERSION, + url = "http://www.sourcemod.net/" +}; + +#include "mapmanagement/globals.sp" +#include "mapmanagement/commands.sp" +#include "mapmanagement/events.sp" +#include "mapmanagement/functions.sp" +#include "mapmanagement/menus.sp" +#include "mapmanagement/timers.sp" +#include "mapmanagement/votes.sp" + +public OnPluginStart() +{ + LoadTranslations("mapmanager.phrases"); + + // Prepare nextmap functionality. + g_VGUIMenu = GetUserMessageId("VGUIMenu"); + if (g_VGUIMenu == INVALID_MESSAGE_ID) + { + LogError("FATAL: Cannot find VGUIMenu user message id. MapManager crippled."); + g_NextMapEnabled = false; + } + HookUserMessage(g_VGUIMenu, UserMsg_VGUIMenu); + + // Create all of the arrays, sized for a 64 character string. + new arraySize = ByteCountToCells(64); + g_MapCycle = CreateArray(arraySize); + g_MapList = CreateArray(arraySize); + g_MapHistory = CreateArray(arraySize); + g_NextVoteMaps = CreateArray(arraySize); + g_SelectedMaps = CreateArray(arraySize); + g_NominatedMaps = CreateArray(arraySize); + + g_TeamScores = CreateArray(2); + + // Hook say + RegConsoleCmd("say", Command_Say); + RegConsoleCmd("say_team", Command_Say); + + // Register all commands. + RegAdminCmd("sm_map", Command_Map, ADMFLAG_CHANGEMAP, "sm_map [r/e]"); + RegAdminCmd("sm_setnextmap", Command_SetNextmap, ADMFLAG_CHANGEMAP, "sm_setnextmap "); + RegAdminCmd("sm_votemap", Command_Votemap, ADMFLAG_VOTE|ADMFLAG_CHANGEMAP, "sm_votemap [mapname2] ... [mapname5] "); + RegAdminCmd("sm_maplist", Command_List, ADMFLAG_GENERIC, "sm_maplist"); + RegAdminCmd("sm_nominate", Command_Addmap, ADMFLAG_CHANGEMAP, "sm_nominate - Nominates a map for RockTheVote and MapChooser. Overrides existing nominations."); + RegAdminCmd("sm_mapvote", Command_Mapvote, ADMFLAG_CHANGEMAP, "sm_mapvote - Forces the MapChooser vote to occur."); + + if (GetCommandFlags("nextmap") == INVALID_FCVAR_FLAGS) + { + RegServerCmd("nextmap", Command_Nextmap); + } + + // Register all convars + g_Cvar_Nextmap = CreateConVar("sm_nextmap", "", "Sets the Next Map", FCVAR_NOTIFY); + + 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); + 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); + + g_Cvar_MapChooser = CreateConVar("sm_mm_mapchooser", "0", "Enables MapChooser (End of Map voting)", 0, true, 0.0, true, 1.0); + g_Cvar_RockTheVote = CreateConVar("sm_mm_rockthevote", "0", "Enables RockTheVote (Player initiated map votes)", 0, true, 0.0, true, 1.0); + g_Cvar_Randomize = CreateConVar("sm_mm_randomize", "0", "Enabled Randomizer (Randomly picks the next map)", 0, true, 0.0, true, 1.0); + g_Cvar_Nominate = CreateConVar("sm_mm_nominate", "1", "Enables nomination system.", 0, true, 0.0, true, 1.0); + + g_Cvar_VoteMap = CreateConVar("sm_mm_votemap", "0.60", "Percentage required for successful sm_votemap.", 0, true, 0.05, true, 1.0); + g_Cvar_RTVLimit = CreateConVar("sm_mm_rtvlimit", "0.60", "Required percentage of players needed to rockthevote", 0, true, 0.05, true, 1.0); + g_Cvar_MinPlayers = CreateConVar("sm_mm_minplayers", "0", "Number of players required before RTV will be enabled.", 0, true, 0.0, true, float(MAXPLAYERS)); + + g_Cvar_StartTime = CreateConVar("sm_mapvote_start", "3.0", "Specifies when to start the vote based on time remaining.", _, true, 1.0); + g_Cvar_StartRounds = CreateConVar("sm_mapvote_startround", "2.0", "Specifies when to start the vote based on rounds remaining.", _, true, 1.0); + g_Cvar_StartFrags = CreateConVar("sm_mapvote_startfrags", "5.0", "Specifies when to start the vote base on frags remaining.", _, true, 1.0); + g_Cvar_ExtendTimeMax = CreateConVar("sm_extendmap_maxtime", "90", "Specifies the maximum amount of time a map can be extended", _, true, 0.0); + g_Cvar_ExtendTimeStep = CreateConVar("sm_extendmap_timestep", "15", "Specifies how much many more minutes each extension makes", _, true, 5.0); + g_Cvar_ExtendRoundMax = CreateConVar("sm_extendmap_maxrounds", "30", "Specfies the maximum amount of rounds a map can be extended", _, true, 0.0); + g_Cvar_ExtendRoundStep = CreateConVar("sm_extendmap_roundstep", "5", "Specifies how many more rounds each extension makes", _, true, 5.0); + g_Cvar_ExtendFragMax = CreateConVar("sm_extendmap_maxfrags", "150", "Specfies the maximum frags allowed that a map can be extended.", _, true, 0.0); + g_Cvar_ExtendFragStep = CreateConVar("sm_extendmap_fragstep", "10", "Specifies how many more frags are allowed when map is extended.", _, true, 5.0); + g_Cvar_Mapfile = CreateConVar("sm_mapvote_file", "configs/maps.ini", "Map file to use. (Def sourcemod/configs/maps.ini)"); + g_Cvar_ExcludeMaps = CreateConVar("sm_mapvote_exclude", "5", "Specifies how many past maps to exclude from the vote.", _, true, 0.0); + g_Cvar_IncludeMaps = CreateConVar("sm_mapvote_include", "5", "Specifies how many maps to include in the vote.", _, true, 2.0, true, 6.0); + 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); + 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); + + // Find game convars + g_Cvar_Chattime = FindConVar("mp_chattime"); + g_Cvar_Winlimit = FindConVar("mp_winlimit"); + g_Cvar_Maxrounds = FindConVar("mp_maxrounds"); + g_Cvar_Fraglimit = FindConVar("mp_fraglimit"); + + if (GetCommandFlags("nextmap") == INVALID_FCVAR_FLAGS) + { + RegServerCmd("nextmap", Command_Nextmap); + } + + // Hook events + HookEvent("round_end", Event_RoundEnd); // We always require round_end + if (g_Cvar_Fraglimit != INVALID_HANDLE) + { + HookEvent("player_death", Event_PlayerDeath); + } + + // Set to the current map so OnMapStart() will know what to do + decl String:currentMap[64]; + GetCurrentMap(currentMap, 64); + SetNextmap(currentMap); + + // Create necessary menus for TopMenu + g_Menu_Map = CreateMenu(MenuHandler_Map); + SetMenuTitle(g_Menu_Map, "Please select a map"); + SetMenuExitBackButton(g_Menu_Map, true); + + g_Menu_Votemap = CreateMenu(MenuHandler_VoteMap, MenuAction_DrawItem); + SetMenuTitle(g_Menu_Votemap, "Please select a map"); + SetMenuExitBackButton(g_Menu_Votemap, true); + + // Bind TopMenu commands to adminmenu_maplist.ini, in cases it doesn't exist in maplists.cfg + decl String:mapListPath[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, mapListPath, sizeof(mapListPath), "configs/adminmenu_maplist.ini"); + SetMapListCompatBind("sm_map menu", mapListPath); + SetMapListCompatBind("sm_votemap menu", mapListPath); + + // Account for late loading + new Handle:topmenu; + if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE)) + { + OnAdminMenuReady(topmenu); + } + + AutoExecConfig(true, "mapmanager"); +} + +public OnAdminMenuReady(Handle:topmenu) +{ + /* Block us from being called twice */ + if (topmenu == hTopMenu) + { + return; + } + + /* Save the Handle */ + hTopMenu = topmenu; + + new TopMenuObject:server_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_SERVERCOMMANDS); + + if (server_commands != INVALID_TOPMENUOBJECT) + { + AddToTopMenu(hTopMenu, + "sm_map", + TopMenuObject_Item, + AdminMenu_Map, + server_commands, + "sm_map", + ADMFLAG_CHANGEMAP); + } + + new TopMenuObject:voting_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_VOTINGCOMMANDS); + + if (voting_commands != INVALID_TOPMENUOBJECT) + { + AddToTopMenu(hTopMenu, + "sm_votemap", + TopMenuObject_Item, + AdminMenu_VoteMap, + voting_commands, + "sm_votemap", + ADMFLAG_VOTE|ADMFLAG_CHANGEMAP); + } +} + +public OnLibraryRemoved(const String:name[]) +{ + if (strcmp(name, "adminmenu") == 0) + { + hTopMenu = INVALID_HANDLE; + } +} + +public OnConfigsExecuted() +{ + // Add map logic here + + // Get the current and last maps. + decl String:lastMap[64], String:currentMap[64]; + GetConVarString(g_Cvar_Nextmap, lastMap, 64); + GetCurrentMap(currentMap, 64); + + // 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 + // some other plugin/command), we don't want to scramble the map cycle. Or for example, admin switches to a custom map + // not in mapcyclefile. So we keep it set to the last expected nextmap. - ferret + if (strcmp(lastMap, currentMap) == 0) + { + FindAndSetNextMap(); + } + + // Build map menus for sm_map, sm_votemap, and RTV. + BuildMapMenu(g_Menu_Map, list); + BuildMapMenu(g_Menu_VoteMap, list); + + // If the Randomize option is on, randomize! + if (GetConVarBool(g_Cvar_Randomize)) + { + CreateTimer(5.0, Timer_RandomizeNextmap); + } + + // If MapChooser is active, start it up! + if (GetConVarBool(g_Cvar_MapChooser)) + { + SetupTimeleftTimer(); + SetConVarString(g_Cvar_Nextmap, "Pending Vote"); + } + + // If RockTheVote is active, start it up! + if (GetConVarBool(g_Cvar_RockTheVote)) + { + BuildMapMenu(g_Menu_RTV, list); + CreateTimer(30.0, Timer_DelayRTV); + } +} + +// Reinitialize all our various globals +public OnMapStart() +{ + if (g_Nominate != INVALID_HANDLE) + { + ClearArray(g_Nominate); + } + + if (g_TeamScores != INVALID_HANDLE) + { + ClearArray(g_TeamScores); + } + + g_TotalRounds = 0; + + g_RTV_Voters = 0; + g_RTV_Votes = 0; + g_RTV_VotesNeeded = 0; + g_RTV_Started = false; + g_RTV_Ended = false; +} + +// Reset globals as necessary and kill timers +public OnMapEnd() +{ + g_IntermissionCalled = false; + g_HasVoteStarted = false; + + g_RTV_Allowed = false; + + if (g_VoteTimer != INVALID_HANDLE) + { + KillTimer(g_VoteTimer); + g_VoteTimer = INVALID_HANDLE; + } + + if (g_RetryTimer != INVALID_HANDLE) + { + KillTimer(g_RetryTimer); + g_RetryTimer = INVALID_HANDLE; + } +} + +public bool:OnClientConnect(client, String:rejectmsg[], maxlen) +{ + if (IsFakeClient(client)) + { + return true; + } + + // If RTV is active, deal with vote counting. + if (GetConVarBool(g_Cvar_RockTheVote)) + { + g_RTV_Voted[client] = false; + g_RTV_Voters++; + g_RTV_VotesNeeded = RoundToFloor(float(g_Voters) * GetConVarFloat(g_Cvar_Needed)); + } + + // If Nominate is active, let the new client nominate + if (GetConVarBool(g_Cvar_Nominate)) + { + g_Nominated[client] = false; + } + + return true; +} + +public OnClientDisconnect(client) +{ + if (IsFakeClient(client)) + { + return; + } + + // If RTV is active, deal with vote counting. + if (GetConVarBool(g_Cvar_RockTheVote)) + { + if(g_RTV_Voted[client]) + { + g_RTV_Votes--; + } + + g_RTV_Voters--; + g_RTV_VotesNeeded = RoundToFloor(float(g_RTV_Voters) * GetConVarFloat(g_Cvar_RTVLimit)); + + // If this client caused us to fall below the RTV threshold and its allowed be started, start it. + if (g_RTV_Votes && g_RTV_Voters && g_RTV_Votes >= g_RTV_VotesNeeded && g_RTV_Allowed && !g_RTV_Started) + { + g_RTV_Started = true; + CreateTimer(2.0, Timer_StartRTV, TIMER_FLAG_NO_MAPCHANGE); + } + } +} + +public OnMapTimeLeftChanged() +{ + if (GetConVarBool(g_Cvar_MapChooser)) + { + SetupTimeleftTimer(); + } +} \ No newline at end of file diff --git a/plugins/mapmanager/commands.sp b/plugins/mapmanager/commands.sp new file mode 100644 index 00000000..bba25765 --- /dev/null +++ b/plugins/mapmanager/commands.sp @@ -0,0 +1,398 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Map Manager Plugin + * Contains callbacks for commands + * + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + + public Action:Command_Say(client, args) + { + decl String:text[192]; + if (GetCmdArgString(text, sizeof(text)) < 1) + { + return Plugin_Continue; + } + + new startidx; + if (text[strlen(text)-1] == '"') + { + text[strlen(text)-1] = '\0'; + startidx = 1; + } + + decl String:message[8]; + BreakString(text[startidx], message, sizeof(message)); + + if (strcmp(message, "nextmap", false) == 0) + { + decl String:map[32]; + GetConVarString(g_Cvar_Nextmap, map, sizeof(map)); + + PrintToChat(client, "%t", "Next Map", map); + } + else + { + if (GetConVarBool(g_Cvar_RockTheVote) && (strcmp(text[startidx], "rtv", false) == 0 || strcmp(text[startidx], "rockthevote", false) == 0)) + { + if (g_MapChanged) + { + ReplyToCommand(client, "[SM] %t", "Map change in progress"); + return Plugin_Continue; + } + + if (!g_RTV_Allowed) + { + PrintToChat(client, "[SM] %t", "RTV Not Allowed"); + return Plugin_Continue; + } + + if (g_RTV_Ended) + { + PrintToChat(client, "[SM] %t", "RTV Ended"); + return Plugin_Continue; + } + + if (g_RTV_Started) + { + PrintToChat(client, "[SM] %t", "RTV Started"); + return Plugin_Continue; + } + + if (GetClientCount(true) < GetConVarInt(g_Cvar_MinPlayers) && g_RTV_Votes == 0) // Should we keep checking g_Votes here? + { + PrintToChat(client, "[SM] %t", "Minimal Players Not Met"); + return Plugin_Continue; + } + + if (g_RTV_Voted[client]) + { + PrintToChat(client, "[SM] %t", "Already Voted"); + return Plugin_Continue; + } + + new String:name[64]; + GetClientName(client, name, sizeof(name)); + + g_RTV_Votes++; + g_RTV_Voted[client] = true; + + PrintToChatAll("[SM] %t", "RTV Requested", name, g_RTV_Votes, g_RTV_VotesNeeded); + + if (g_RTV_Votes >= g_RTV_VotesNeeded) + { + g_RTV_Started = true; + CreateTimer(2.0, Timer_StartRTV, TIMER_FLAG_NO_MAPCHANGE); + } + } + else if (GetConVarBool(g_Cvar_Nominate) && strcmp(text[startidx], "nominate", false) == 0) + { + if (g_MapChanged) + { + ReplyToCommand(client, "[SM] %t", "Map change in progress"); + return Plugin_Continue; + } + + if (g_RTV_Started || g_HasVoteStarted) + { + ReplyToCommand(client, "[SM] %t", "Map vote in progress"); + return Plugin_Continue; + } + + if (g_Nominated[client]) + { + PrintToChat(client, "[SM] %t", "Already Nominated"); + return Plugin_Continue; + } + + if (GetArraySize(g_Nominate) >= GetConVarInt(g_Cvar_Maps)) + { + PrintToChat(client, "[SM] %t", "Max Nominations"); + return Plugin_Continue; + } + + DisplayMenu(g_Menu_Nominate, client, MENU_TIME_FOREVER); + } + } + + return Plugin_Continue; +} + +public Action:Command_Mapvote(client, args) +{ + InitiateVote(); + + return Plugin_Handled; +} + +public Action:Command_Nextmap(args) +{ + decl String:map[64]; + + GetConVarString(g_Cvar_Nextmap, map, sizeof(map)); + + ReplyToCommand(0, "%t", "Next Map", map); + + return Plugin_Handled; +} + +public Action:Command_List(client, args) +{ + PrintToConsole(client, "Map Cycle:"); + + decl String:currentMap[64]; + GetCurrentMap(currentMap, 64); + + decl String:mapName[64]; + for (new i = 0; i < GetArraySize(g_MapCycle); i++) + { + GetArrayString(g_MapCycle, i, mapName, sizeof(mapName)); + if (strcmp(mapName, currentMap) == 0) + { + PrintToConsole(client, "%s <========= Current map", mapName); + } + else if (strcmp(mapName, g_NextMap) == 0) + { + PrintToConsole(client, "%s <========= Next map", mapName); + } + else + { + PrintToConsole(client, "%s", mapName); + } + } + + return Plugin_Handled; +} + +public Action:Command_SetNextmap(client, args) +{ + if (args < 1) + { + ReplyToCommand(client, "[SM] Usage: sm_setnextmap "); + return Plugin_Handled; + } + + if (g_MapChanged) + { + ReplyToCommand(client, "[SM] %t", "Map change in progress"); + return Plugin_Handled; + } + + decl String:map[64]; + GetCmdArg(1, map, sizeof(map)); + + if (!IsMapValid(map)) + { + ReplyToCommand(client, "[SM] %t", "Map was not found", map); + return Plugin_Handled; + } + + ShowActivity(client, "%t", "Cvar changed", "sm_nextmap", map); + LogMessage("\"%L\" changed sm_nextmap to \"%s\"", client, map); + + SetNextMap(map); + + return Plugin_Handled; +} + + public Action:Command_Map(client, args) + { + if (args < 1) + { + ReplyToCommand(client, "[SM] Usage: sm_map [r/e]"); + return Plugin_Handled; + } + + if (g_MapChanged) + { + ReplyToCommand(client, "[SM] %t", "Map change in progress"); + return Plugin_Handled; + } + + decl String:map[64]; + GetCmdArg(1, map, sizeof(map)); + + if (!IsMapValid(map)) + { + ReplyToCommand(client, "[SM] %t", "Map was not found", map); + return Plugin_Handled; + } + + decl String:when[2]; + if (args > 1) + { + GetCmdArg(2, when, sizeof(when)); + + when[0] = CharToLower(when[0]); + if (when[0] != 'r' && when[0] != 'e') + { + when[0] = 'i'; + } + } + + SetMapChange(client, map, when); + + return Plugin_Handled; +} + +public Action:Command_Votemap(client, args) +{ + if (args < 1) + { + ReplyToCommand(client, "[SM] Usage: sm_votemap [r/e] [mapname2] ... [mapname5]"); + return Plugin_Handled; + } + + if (g_MapChanged) + { + ReplyToCommand(client, "[SM] %t", "Map change in progress"); + return Plugin_Handled; + } + + if (IsVoteInProgress()) + { + ReplyToCommand(client, "[SM] %t", "Vote in Progress"); + return Plugin_Handled; + } + + if (!TestVoteDelay(client)) + { + return Plugin_Handled; + } + + decl String:text[256]; + GetCmdArgString(text, sizeof(text)); + + decl String:maps[5][64]; + new mapCount; + new len, pos; + + // Find out if the user specified "when" + decl String:when[64]; + pos = BreakString(text[len], when, sizeof(when)); + if (!IsMapValid(when)) + { + when[0] = CharToLower(when[0]); + if (when[0] != 'r' && when[0] != 'e') + { + ReplyToCommand(client, "[SM] %t", "Map was not found", maps[mapCount]); + return Plugin_Handled; + } + } + else + { + strcpy(maps[mapCount], sizeof(maps[]), when); + mapCount++; + when[0] = 'i'; + } + + len += pos; + + while (pos != -1 && mapCount < 5) + { + pos = BreakString(text[len], maps[mapCount], sizeof(maps[])); + + if (!IsMapValid(maps[mapCount])) + { + ReplyToCommand(client, "[SM] %t", "Map was not found", maps[mapCount]); + return Plugin_Handled; + } + + mapCount++; + + len += pos; + } + + g_VoteMapInUse = client; + g_Client_Data[client][0] = when[0]; + + DisplayVoteMapMenu(client, mapCount, maps); + + return Plugin_Handled; +} + +public Action:Command_Nominate(client, args) +{ + if (args < 1) + { + ReplyToCommand(client, "[SM] Usage: sm_nominate "); + return Plugin_Handled; + } + + if (!GetConVarBool(g_Cvar_Nominate)) + { + ReplyToCommand(client, "[SM] Nominations are currently disabled."); + return Plugin_Handled; + } + + decl String:mapname[64]; + GetCmdArg(1, mapname, sizeof(mapname)); + + if (FindStringInArray(g_MapList, mapname) == -1) + { + ReplyToCommand(client, "%t", "Map was not found", mapname); + return Plugin_Handled; + } + + if (GetArraySize(g_Nominated) > 0) + { + if (FindStringInArray(g_Nominated, mapname) != -1) + { + ReplyToCommand(client, "%t", "Map Already In Vote", mapname); + return Plugin_Handled; + } + + ShiftArrayUp(g_Nominated, 0); + SetArrayString(g_Nominated, 0, mapname); + + while (GetArraySize(g_Nominated) > GetConVarInt(g_Cvar_Maps)) + { + RemoveFromArray(g_Nominated, GetConVarInt(g_Cvar_Maps)); + } + } + else + { + PushArrayString(g_Nominated, mapname); + } + + decl String:item[64]; + for (new i = 0; i < GetMenuItemCount(g_Menu_Nominate); i++) + { + GetMenuItem(g_Menu_Nominate, i, item, sizeof(item)); + if (strcmp(item, mapname) == 0) + { + RemoveMenuItem(g_Menu_Nominate, i); + break; + } + } + + ReplyToCommand(client, "%t", "Map Inserted", mapname); + LogAction(client, -1, "\"%L\" inserted map \"%s\".", client, mapname); + + return Plugin_Handled; +} \ No newline at end of file diff --git a/plugins/mapmanager/events.sp b/plugins/mapmanager/events.sp new file mode 100644 index 00000000..be57776b --- /dev/null +++ b/plugins/mapmanager/events.sp @@ -0,0 +1,166 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Map Manager Plugin + * Contains event callbacks + * + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +public Action:UserMsg_VGUIMenu(UserMsg:msg_id, Handle:bf, const players[], playersNum, bool:reliable, bool:init) +{ + if (g_IntermissionCalled) + { + return Plugin_Handled; + } + + decl String:type[15]; + + /* If we don't get a valid string, bail out. */ + if (BfReadString(bf, type, sizeof(type)) < 0) + { + return Plugin_Handled; + } + + if (BfReadByte(bf) == 1 && BfReadByte(bf) == 0 && (strcmp(type, "scores", false) == 0)) + { + g_IntermissionCalled = true; + + decl String:map[32]; + new Float:fChatTime = GetConVarFloat(g_Cvar_Chattime); + + if (fChatTime < 2.0) + { + SetConVarFloat(g_Cvar_Chattime, 2.0); + } + + LogMessage("[MapManager] Changing map to '%s' due to map ending.", g_NextMap); + + g_MapChanged = true; + CreateTimer(fChatTime - 1.0, Timer_ChangeMap); + } + + return Plugin_Handled; +} + +public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast) +{ + if (g_MapChanged) + { + return; + } + + if (g_MapChangeSet && g_MapChangeWhen == 'r') + { + LogMessage("[MapManager] Changing map to '%s' due to round ending.", g_NextMap); + + g_MapChanged = true; + CreateTimer(1.0, Timer_ChangeMap); + + return; + } + + if (g_HasVoteStarted) + { + return; + } + + new winner = GetEventInt(event, "winner"); + + if (winner == 0 || winner == 1) + { + return; + } + + g_TotalRounds++; + + new team[2], teamPos = -1; + for (new i; i < GetArraySize(g_TeamScores); i++) + { + GetArrayArray(g_TeamScores, i, team); + if (team[0] == winner) + { + teamPos = i; + break; + } + } + + if (teamPos == -1) + { + team[0] = winner; + team[1] = 1; + PushArrayArray(g_TeamScores, team); + } + else + { + team[1]++; + SetArrayArray(g_TeamScores, teamPos, team); + } + + if (g_Cvar_Winlimit != INVALID_HANDLE) + { + new winlimit = GetConVarInt(g_Cvar_Winlimit); + if (winlimit) + { + if (team[1] >= (winlimit - GetConVarInt(g_Cvar_StartRounds))) + { + InitiateVote(); + } + } + } + + if (g_Cvar_Maxrounds != INVALID_HANDLE) + { + new maxrounds = GetConVarInt(g_Cvar_Maxrounds); + if (maxrounds) + { + if (g_TotalRounds >= (maxrounds - GetConVarInt(g_Cvar_StartRounds))) + { + InitiateVote(); + } + } + } +} + +public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast) +{ + if (g_MapChanged || g_HasVoteStarted || g_Cvar_Fraglimit == INVALID_HANDLE) + { + return; + } + + if (!GetConVarInt(g_Cvar_Fraglimit)) + { + return; + } + + new fragger = GetClientOfUserId(GetEventInt(event, "attacker")); + if (fragger && GetClientFrags(fragger) >= (GetConVarInt(g_Cvar_Fraglimit) - GetConVarInt(g_Cvar_StartFrags))) + { + InitiateVote(); + } +} \ No newline at end of file diff --git a/plugins/mapmanager/functions.sp b/plugins/mapmanager/functions.sp new file mode 100644 index 00000000..963dcafc --- /dev/null +++ b/plugins/mapmanager/functions.sp @@ -0,0 +1,335 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Map Management Plugin + * Contains misc functions. + * + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +SetNextMap(String:map[]) +{ + strcopy(g_NextMap, sizeof(g_NextMap), map); + SetConVarString(g_Cvar_Nextmap, map); +} + +SetMapChange(client, map, when, Float:time = 3.0) +{ + g_MapChangeSet = true; + g_MapChangeWhen = when[0]; + SetNextMap(map); + + if (when[0] == 'r') + { + ShowActivity2(client, "[SM] ", "%t", "Changing map end of round", map); + LogAction(client, -1, "\"%L\" set end of round map change to \"%s\"", client, map); + } + else if (when[0] == 'e') + { + ShowActivity2(client, "[SM] ", "%t", "Set nextmap", map); + LogAction(client, -1, "\"%L\" set the next map to \"%s\"", client, map); + } + else + { + ShowActivity2(client, "[SM] ", "%t", "Change map", map); + LogAction(client, -1, "\"%L\" changed map to \"%s\"", client, map); + + g_MapChanged = true; + CreateTimer(3.0, Timer_ChangeMap); + } +} + +FindAndSetNextMap() +{ + if (ReadMapList(g_MapList, + g_MapListSerial, + "mapcyclefile", + MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_NO_DEFAULT) + == INVALID_HANDLE) + { + if (g_MapListSerial == -1) + { + LogError("FATAL: Cannot load map cycle. Nextmap not loaded."); + SetFailState("Mapcycle Not Found"); + } + } + + new mapCount = GetArraySize(g_MapList); + decl String:mapName[32]; + + if (g_MapPos == -1) + { + decl String:current[64]; + GetCurrentMap(current, 64); + + for (new i = 0; i < mapCount; i++) + { + GetArrayString(g_MapList, i, mapName, sizeof(mapName)); + if (strcmp(current, mapName, false) == 0) + { + g_MapPos = i; + break; + } + } + + if (g_MapPos == -1) + g_MapPos = 0; + } + + g_MapPos++; + if (g_MapPos >= mapCount) + g_MapPos = 0; + + GetArrayString(g_MapList, g_MapPos, mapName, sizeof(mapName)); + SetConVarString(g_Cvar_Nextmap, mapName); +} + +Float:GetVotePercent(votes, totalVotes) +{ + return FloatDiv(float(votes),float(totalVotes)); +} + +bool:TestVoteDelay(client) +{ + new delay = CheckVoteDelay(); + + if (delay > 0) + { + if (delay > 60) + { + ReplyToCommand(client, "[SM] %t", "Vote Delay Minutes", delay % 60); + } + else + { + ReplyToCommand(client, "[SM] %t", "Vote Delay Seconds", delay); + } + + return false; + } + + return true; +} + +BuildMapMenu() +{ + if (g_MapMenu != INVALID_HANDLE) + { + CloseHandle(g_MapMenu); + g_MapMenu = INVALID_HANDLE; + } + + g_MapMenu = CreateMenu(Handler_MapSelectMenu); + SetMenuTitle(g_MapMenu, "%t", "Nominate Title"); + + decl String:map[64]; + for (new i = 0; i < GetArraySize(g_MapList); i++) + { + GetArrayString(g_MapList, i, map, sizeof(map)); + AddMenuItem(g_MapMenu, map, map); + } + + SetMenuExitButton(g_MapMenu, false); +} + +SetupTimeleftTimer() +{ + new time; + if (GetMapTimeLeft(time) && time > 0) + { + new startTime = GetConVarInt(g_Cvar_StartTime) * 60; + if (time - startTime < 0) + { + InitiateVote(); + } + else + { + if (g_VoteTimer != INVALID_HANDLE) + { + KillTimer(g_VoteTimer); + g_VoteTimer = INVALID_HANDLE; + } + + g_VoteTimer = CreateTimer(float(time - startTime), Timer_StartMapVote); + } + } +} + +InitiateVote() +{ + if (g_HasVoteStarted || g_RetryTimer != INVALID_HANDLE) + { + return; + } + + if (IsVoteInProgress()) + { + // Can't start a vote, try again in 5 seconds. + g_RetryTimer = CreateTimer(5.0, Timer_StartMapVote); + return; + } + + g_HasVoteStarted = true; + g_VoteMenu = CreateMenu(Handler_MapVoteMenu, MenuAction:MENU_ACTIONS_ALL); + SetMenuTitle(g_VoteMenu, "Vote Nextmap"); + + decl String:map[32]; + for (new i = 0; i < GetArraySize(g_NextMapList); i++) + { + GetArrayString(g_NextMapList, i, map, sizeof(map)); + AddMenuItem(g_VoteMenu, map, map); + } + + if (GetConVarBool(g_Cvar_Extend)) + { + new bool:allowExtend, time; + if (GetMapTimeLimit(time) && time > 0 && time < GetConVarInt(g_Cvar_ExtendTimeMax)) + { + allowExtend = true; + } + + if (g_Cvar_Winlimit != INVALID_HANDLE && GetConVarInt(g_Cvar_Winlimit) < GetConVarInt(g_Cvar_ExtendRoundMax)) + { + allowExtend = true; + } + + if (g_Cvar_Maxrounds != INVALID_HANDLE && GetConVarInt(g_Cvar_Maxrounds) < GetConVarInt(g_Cvar_ExtendRoundMax)) + { + allowExtend = true; + } + + if (g_Cvar_Fraglimit != INVALID_HANDLE && GetConVarInt(g_Cvar_Fraglimit) < GetConVarInt(g_Cvar_ExtendFragMax)) + { + allowExtend = true; + } + + if (allowExtend) + { + AddMenuItem(g_VoteMenu, VOTE_EXTEND, "Extend Map"); + } + } + + SetMenuExitButton(g_VoteMenu, false); + VoteMenuToAll(g_VoteMenu, 20); + + LogMessage("Voting for next map has started."); + PrintToChatAll("[SM] %t", "Nextmap Voting Started"); +} + +SetNextMap(const String:map[]) +{ + SetConVarString(g_Cvar_Nextmap, map); + PushArrayString(g_OldMapList, map); + + if (GetArraySize(g_OldMapList) > GetConVarInt(g_Cvar_ExcludeMaps)) + { + RemoveFromArray(g_OldMapList, 0); + } + + PrintToChatAll("[SM] %t", "Nextmap Voting Finished", map); + LogMessage("Voting for next map has finished. Nextmap: %s.", map); +} + +CreateNextVote() +{ + if(g_NextMapList != INVALID_HANDLE) + { + ClearArray(g_NextMapList); + } + + decl String:map[32]; + new index, Handle:tempMaps = CloneArray(g_MapList); + + GetCurrentMap(map, sizeof(map)); + index = FindStringInArray(tempMaps, map); + if (index != -1) + { + RemoveFromArray(tempMaps, index); + } + + if (GetConVarInt(g_Cvar_ExcludeMaps) && GetArraySize(tempMaps) > GetConVarInt(g_Cvar_ExcludeMaps)) + { + for (new i = 0; i < GetArraySize(g_OldMapList); i++) + { + GetArrayString(g_OldMapList, i, map, sizeof(map)); + index = FindStringInArray(tempMaps, map); + if (index != -1) + { + RemoveFromArray(tempMaps, index); + } + } + } + + new limit = (GetConVarInt(g_Cvar_IncludeMaps) < GetArraySize(tempMaps) ? GetConVarInt(g_Cvar_IncludeMaps) : GetArraySize(tempMaps)); + for (new i = 0; i < limit; i++) + { + new b = GetRandomInt(0, GetArraySize(tempMaps) - 1); + GetArrayString(tempMaps, b, map, sizeof(map)); + PushArrayString(g_NextMapList, map); + RemoveFromArray(tempMaps, b); + } + + CloseHandle(tempMaps); +} + +/* + +new Handle:g_map_array = INVALID_HANDLE; +new g_map_serial = -1; + +LoadMapList(Handle:menu) +{ + new Handle:map_array; + + if ((map_array = ReadMapList(g_map_array, + g_map_serial, + "sm_map menu", + MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_NO_DEFAULT|MAPLIST_FLAG_MAPSFOLDER)) + != INVALID_HANDLE) + { + g_map_array = map_array; + } + + if (g_map_array == INVALID_HANDLE) + { + return 0; + } + + RemoveAllMenuItems(menu); + + decl String:map_name[64]; + new map_count = GetArraySize(g_map_array); + + for (new i = 0; i < map_count; i++) + { + GetArrayString(g_map_array, i, map_name, sizeof(map_name)); + AddMenuItem(menu, map_name, map_name); + } + + return map_count; +} + +*/ \ No newline at end of file diff --git a/plugins/mapmanager/functions_menu.sp b/plugins/mapmanager/functions_menu.sp new file mode 100644 index 00000000..51f78f57 --- /dev/null +++ b/plugins/mapmanager/functions_menu.sp @@ -0,0 +1,115 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Map Management Plugin + * Contains misc functions. + * + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +DisplayWhenMenu(client, bool:vote = false) +{ + new Handle: + + if (!vote) + { + menu = CreateMenu(MenuHandler_ChangeMap); + } + else + { + menu = CreateMenu(MenuHandler_VoteWhen); + } + + decl String:title[100]; + Format(title, sizeof(title), "%T:", "Map Change When", client); + SetMenuTitle(menu, title); + SetMenuExitBackButton(menu, true); + + AddMenuItem(menu, "i", "Immediately"); + AddMenuItem(menu, "r", "Round End"); + AddMenuItem(menu, "e", "Map End"); + + DisplayMenu(menu, client, MENU_TIME_FOREVER); +} + +DisplayConfirmVoteMenu(client) +{ + new Handle:menu = CreateMenu(MenuHandler_Confirm); + + decl String:title[100]; + Format(title, sizeof(title), "%T:", "Confirm Vote", client); + SetMenuTitle(menu, title); + SetMenuExitBackButton(menu, true); + + AddMenuItem(menu, "Confirm", "Start the Vote"); + + DisplayMenu(menu, client, MENU_TIME_FOREVER); +} + +DisplayAcceptVoteMenu(String:map[]) +{ + new Handle:menu = CreateMenu(MenuHandler_Accept); + + decl String:title[100]; + Format(title, sizeof(title), "%T:", "Accept Vote", client); + SetMenuTitle(menu, title); + SetMenuExitBackButton(menu, true); + + AddMenuItem(menu, map, "Accept Vote"); + + DisplayMenu(menu, g_VoteMapInUse, MENU_TIME_FOREVER); +} + +DisplayVoteMapMenu(client, mapCount, String:maps[5][]) +{ + LogAction(client, -1, "\"%L\" initiated a map vote.", client); + ShowActivity2(client, "[SM] ", "%t", "Initiated Vote Map"); + + new Handle:menu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL); + + if (mapCount == 1) + { + //strcopy(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]), maps[0]); + + SetMenuTitle(menu, "Change Map To"); + AddMenuItem(menu, maps[0], "Yes"); + AddMenuItem(menu, VOTE_NO, "No"); + } + else + { + //g_voteInfo[VOTE_NAME][0] = '\0'; + + SetMenuTitle(menu, "Map Vote"); + for (new i = 0; i < mapCount; i++) + { + AddMenuItem(menu, maps[i], maps[i]); + } + } + + SetMenuExitButton(menu, false); + VoteMenuToAll(menu, 20); +} diff --git a/plugins/mapmanager/globals.sp b/plugins/mapmanager/globals.sp new file mode 100644 index 00000000..171c3a16 --- /dev/null +++ b/plugins/mapmanager/globals.sp @@ -0,0 +1,104 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Map Manager Plugin + * Contains globals and defines + * + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#define VOTE_EXTEND "##extend##" +#define VOTE_YES "###yes###" +#define VOTE_NO "###no###" + +new Handle:hTopMenu = INVALID_HANDLE; // TopMenu + +new Handle:g_MapCycle = INVALID_HANDLE; // mapcyclefile maps +new Handle:g_MapList = INVALID_HANDLE; // maplist.txt maps +new Handle:g_MapHistory = INVALID_HANDLE; // History of maps played +new Handle:g_NextVoteMaps = INVALID_HANDLE; // Array of maps for next RTV or MC vote +new Handle:g_NominatedMaps = INVALID_HANDLE; // Array of maps that have been nominated + +new Handle:g_Menu_Map = INVALID_HANDLE; // Menu of maps used by sm_map in admin menu +new Handle:g_Menu_Votemap = INVALID_HANDLE; // Menu of maps used by sm_votemap in admin menu +new Handle:g_Menu_Nominate = INVALID_HANDLE; // Menu of maps used by Nomination system + +new Handle:g_SelectedMaps; // List of maps chosen so far by a user in sm_votemap admin menu +new g_VoteMapInUse; // Client index of admin using sm_votemap +new String:g_Client_Data[MAXCLIENTS+1][64]; // Used to hold bits of client data during sm_votemap + +new bool:g_MapChangeSet; // True if a command or vote has set the map +new bool:g_MapChanged; // True if a map change has been issued +new g_MapChangeWhen; // Either 'i' for immediate, 'r' for round end, or 'e' for end of map. + +new UserMsg:g_VGUIMenu; // VGUI usermsg id for nextmap +new bool:g_NextMapEnabled = true // If set to false, all features requiring nextmap are disabled. +new bool:g_IntermissionCalled; // Has the end of map intermission begun? +new String:g_NextMap[64]; // All important! This is the next map! +new g_MapPos = -1; // Position in mapcycle + +new bool:g_RTV_Voted[MAXPLAYERS+1] = {false, ...}; // Whether or not a player has voted for RTV +new bool:g_RTV_Allowed = false; // True if RTV is available to players. Used to delay rtv votes. +new bool:g_RTV_Started = false; // Indicates that the actual map vote has started +new bool:g_RTV_Ended = false; // Indicates that the actual map vote has concluded +new g_RTV_Voters = 0; // Total voters connected. Doesn't include fake clients. +new g_RTV_Votes = 0; // Total number of "say rtv" votes +new g_RTV_VotesNeeded = 0; // Necessary votes before map vote begins. (voters * percent_needed) + +new bool:g_Nominated[MAXPLAYERS+1] = {false, ...}; // Whether or not a player has nominated a map + +new Handle:g_TeamScores = INVALID_HANDLE; // Array of team scores +new g_TotalRounds; // Total rounds played this map +new bool:g_HasVoteStarted; // Whether or not MapChooser has begun + +// ConVar handles +new Handle:g_Cvar_NextMap = INVALID_HANDLE; +new Handle:g_Cvar_VoteMap = INVALID_HANDLE; +new Handle:g_Cvar_Excludes = INVALID_HANDLE; +new Handle:g_Cvar_StartTime = INVALID_HANDLE; +new Handle:g_Cvar_StartRounds = INVALID_HANDLE; +new Handle:g_Cvar_StartFrags = INVALID_HANDLE; +new Handle:g_Cvar_ExtendTimeMax = INVALID_HANDLE; +new Handle:g_Cvar_ExtendTimeStep = INVALID_HANDLE; +new Handle:g_Cvar_ExtendRoundMax = INVALID_HANDLE; +new Handle:g_Cvar_ExtendRoundStep = INVALID_HANDLE; +new Handle:g_Cvar_ExtendFragMax = INVALID_HANDLE; +new Handle:g_Cvar_ExtendFragStep = INVALID_HANDLE; +new Handle:g_Cvar_Mapfile = INVALID_HANDLE; +new Handle:g_Cvar_ExcludeMaps = INVALID_HANDLE; +new Handle:g_Cvar_IncludeMaps = INVALID_HANDLE; +new Handle:g_Cvar_NoVoteMode = INVALID_HANDLE; +new Handle:g_Cvar_Extend = INVALID_HANDLE; + +new Handle:g_VoteTimer = INVALID_HANDLE; +new Handle:g_RetryTimer = INVALID_HANDLE; + +// VALVe ConVars +new Handle:g_Cvar_Chattime = INVALID_HANDLE; +new Handle:g_Cvar_Winlimit = INVALID_HANDLE; +new Handle:g_Cvar_Maxrounds = INVALID_HANDLE; +new Handle:g_Cvar_Fraglimit = INVALID_HANDLE; diff --git a/plugins/mapmanager/menus.sp b/plugins/mapmanager/menus.sp new file mode 100644 index 00000000..c47e76be --- /dev/null +++ b/plugins/mapmanager/menus.sp @@ -0,0 +1,263 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Map Manager Plugin + * Contains menu callbacks + * + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +// Following callbacks are for sm_map +public AdminMenu_Map(Handle:topmenu, + TopMenuAction:action, + TopMenuObject:object_id, + param, + String:buffer[], + maxlength) +{ + if (action == TopMenuAction_DisplayOption) + { + Format(buffer, maxlength, "%T", "Choose Map", param); + } + else if (action == TopMenuAction_SelectOption) + { + DisplayMenu(g_Menu_Map, param, MENU_TIME_FOREVER); + } +} + +public MenuHandler_Map(Handle:menu, MenuAction:action, param1, param2) +{ + if (action == MenuAction_Cancel) + { + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + { + DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + } + } + else if (action == MenuAction_Select) + { + GetMenuItem(menu, param2, g_Client_Data[param1], sizeof(g_Client_Data[])); + DisplayWhenMenu(param1); + } +} + +public MenuHandler_ChangeMap(Handle:menu, MenuAction:action, param1, param2) +{ + if (action == MenuAction_Cancel) + { + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + { + DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + } + } + else if (action == MenuAction_Select) + { + decl String:when[2]; + GetMenuItem(menu, param2, when, sizeof(when)); + SetMapChange(client, g_Client_Data[param1], when[0]) + } +} + +// Following callbacks are for sm_votemap +public AdminMenu_VoteMap(Handle:topmenu, + TopMenuAction:action, + TopMenuObject:object_id, + param, + String:buffer[], + maxlength) +{ + if (action == TopMenuAction_DisplayOption) + { + Format(buffer, maxlength, "%T", "Map vote", param); + } + else if (action == TopMenuAction_SelectOption) + { + g_VoteMapInUse = param; + ClearArray(g_SelectedMaps); + DisplayMenu(g_Menu_Votemap, param, MENU_TIME_FOREVER); + } + else if (action == TopMenuAction_DrawOption) + { + /* disable this option if a vote is already running, theres no maps listed or someone else has already accessed this menu */ + buffer[0] = (!IsNewVoteAllowed() || g_VoteMapInUse) ? ITEMDRAW_IGNORE : ITEMDRAW_DEFAULT; + } +} + +public MenuHandler_VoteMap(Handle:menu, MenuAction:action, param1, param2) +{ + if (action == MenuAction_Cancel) + { + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + { + DisplayWhenMenu(param1, true); + } + else // no action was selected. + { + /* Re-enable the menu option */ + g_VoteMapInUse = 0; + } + } + else if (action == MenuAction_DrawItem) + { + decl String:info[32]; + + GetMenuItem(menu, param2, info, sizeof(info)); + + if (FindStringInArray(g_SelectedMaps, info) != -1) + { + return ITEMDRAW_IGNORE; + } + else + { + return ITEMDRAW_DEFAULT; + } + } + else if (action == MenuAction_Select) + { + decl String:info[32]; + + GetMenuItem(menu, param2, info, sizeof(info)); + + PushArrayString(g_SelectedMaps, info); + + /* Redisplay the list */ + if (GetArraySize(g_SelectedMaps) < 5) + { + DisplayMenu(g_MapList, param1, MENU_TIME_FOREVER); + } + else + { + DisplayWhenMenu(param1, true); + } + } + + return 0; +} + +public MenuHandler_VoteWhen(Handle:menu, MenuAction:action, param1, param2) +{ + if (action == MenuAction_End) + { + CloseHandle(menu); + } + else if (action == MenuAction_Cancel) + { + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + { + g_Menu_Info[param1][0] = 'i'; + DisplayConfirmVoteMenu(param1); + } + else + { + g_VoteMapInUse = 0; + } + } + else if (action == MenuAction_Select) + { + GetMenuItem(menu, param2, g_Menu_Info[param1], sizeof(g_Menu_Info[])); + + DisplayConfirmVoteMenu(param1); + } +} + +public MenuHandler_Confirm(Handle:menu, MenuAction:action, param1, param2) +{ + if (action == MenuAction_End) + { + CloseHandle(menu); + } + else if (action == MenuAction_Cancel) + { + g_VoteMapInUse = 0; + + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + { + DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + } + } + else if (action == MenuAction_Select) + { + decl String:maps[5][64]; + new selectedmaps = GetArraySize(g_SelectedMaps); + + for (new i = 0; i < selectedmaps; i++) + { + GetArrayString(g_SelectedMaps, i, maps[i], sizeof(maps[])); + } + + DisplayVoteMapMenu(param1, selectedmaps, maps); + } +} + +public MenuHandler_Accept(Handle:menu, MenuAction:action, param1, param2) +{ + if (action == MenuAction_End) + { + CloseHandle(menu); + g_VoteMapInUse = 0; + } + else if (action == MenuAction_Select) + { + decl String:map[64] + GetMenuItem(menu, 1, map, sizeof(map)); + + SetMapChange(param1, map, g_Client_Data[param1][0]) + } +} + +public Handler_MapSelectMenu(Handle:menu, MenuAction:action, param1, param2) +{ + switch (action) + { + case MenuAction_Select: + { + if (GetArraySize(g_RTVMapList) >= GetConVarInt(g_Cvar_Maps)) + { + PrintToChat(param1, "[SM] %t", "Max Nominations"); + return; + } + + decl String:map[64], String:name[64]; + GetMenuItem(menu, param2, map, sizeof(map)); + + if (FindStringInArray(g_RTVMapList, map) != -1) + { + PrintToChat(param1, "[SM] %t", "Map Already Nominated"); + return; + } + + GetClientName(param1, name, 64); + + PushArrayString(g_RTVMapList, map); + RemoveMenuItem(menu, param2); + + g_Nominated[param1] = true; + + PrintToChatAll("[SM] %t", "Map Nominated", name, map); + } + } +} \ No newline at end of file diff --git a/plugins/mapmanager/timers.sp b/plugins/mapmanager/timers.sp new file mode 100644 index 00000000..c0d5a3e3 --- /dev/null +++ b/plugins/mapmanager/timers.sp @@ -0,0 +1,166 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Map Manager Plugin + * Contains timer callbacks + * + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +public Action:Timer_ChangeMap(Handle:timer) +{ + ServerCommand("changelevel \"%s\"", g_NextMap); + PushArrayString(g_MapHistory, g_NextMap); + + return Plugin_Stop; +} + +public Action:Timer_RandomizeNextmap(Handle:timer) +{ + decl String:map[64]; + + new exclusion = GetConVarInt(g_Cvar_ExcludeMaps); + if (exclusion > GetArraySize(g_MapCycle)) + { + exclusion = GetArraySize(g_MapCycle) - 1; + } + + new b = GetRandomInt(0, GetArraySize(g_MapCycle) - 1); + GetArrayString(g_MapCycle, b, map, sizeof(map)); + + while (FindStringInArray(g_MapHistory, map) != -1) + { + b = GetRandomInt(0, GetArraySize(g_MapCycle) - 1); + GetArrayString(g_MapCycle, b, map, sizeof(map)); + } + + SetNextmap(map); + + LogMessage("[MapManager] Randomizer has chosen '%s' as the next map.", map); + + return Plugin_Stop; +} + +public Action:Timer_DelayRTV(Handle:timer) +{ + g_RTVAllowed = true; + g_RTVStarted = false; + g_RTVEnded = false; +} + +public Action:Timer_StartRTV(Handle:timer) +{ + if (timer == g_RetryTimer) + { + g_RetryTimer = INVALID_HANDLE; + } + + if (g_RetryTimer != INVALID_HANDLE) + { + return; + } + + if (IsVoteInProgress()) + { + // Can't start a vote, try again in 5 seconds. + g_RetryTimer = CreateTimer(5.0, Timer_StartRTV, TIMER_FLAG_NO_MAPCHANGE); + return; + } + + PrintToChatAll("[SM] %t", "RTV Vote Ready"); + + new Handle:MapVoteMenu = CreateMenu(Handler_MapMapVoteMenu, MenuAction:MENU_ACTIONS_ALL); + SetMenuTitle(MapVoteMenu, "Rock The Vote"); + + new Handle:tempMaps = CloneArray(g_MapList); + decl String:map[32]; + + GetCurrentMap(map, sizeof(map)); + new index = FindStringInArray(tempMaps, map); + if (index != -1) + { + RemoveFromArray(tempMaps, index); + } + + // We assume that g_RTVMapList is within the correct limits, based on the logic for nominations + for (new i = 0; i < GetArraySize(g_RTVMapList); i++) + { + GetArrayString(g_RTVMapList, i, map, sizeof(map)); + AddMenuItem(MapVoteMenu, map, map); + + index = FindStringInArray(tempMaps, map); + if (index != -1) + { + RemoveFromArray(tempMaps, index); + } + } + + new limit = GetConVarInt(g_Cvar_Maps) - GetArraySize(g_RTVMapList); + if (limit > GetArraySize(tempMaps)) + { + limit = GetArraySize(tempMaps); + } + + for (new i = 0; i < limit; i++) + { + new b = GetRandomInt(0, GetArraySize(tempMaps) - 1); + GetArrayString(tempMaps, b, map, sizeof(map)); + PushArrayString(g_RTVMapList, map); + AddMenuItem(MapVoteMenu, map, map); + RemoveFromArray(tempMaps, b); + } + + CloseHandle(tempMaps); + + AddMenuItem(MapVoteMenu, "Don't Change", "Don't Change"); + + SetMenuExitButton(MapVoteMenu, false); + VoteMenuToAll(MapVoteMenu, 20); + + LogMessage("[SM] Rockthevote was successfully started."); +} + +public Action:Timer_StartMapVote(Handle:timer) +{ + if (!GetArraySize(g_MapList)) + { + return Plugin_Stop; + } + + if (timer == g_RetryTimer) + { + g_RetryTimer = INVALID_HANDLE; + } + else + { + g_VoteTimer = INVALID_HANDLE; + } + + InitiateVote(); + + return Plugin_Stop; +} \ No newline at end of file diff --git a/plugins/mapmanager/votes.sp b/plugins/mapmanager/votes.sp new file mode 100644 index 00000000..c8a1d6f7 --- /dev/null +++ b/plugins/mapmanager/votes.sp @@ -0,0 +1,318 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Map Manager Plugin + * Contains vote callbacks + * + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +// sm_votemap +public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2) +{ + if (action == MenuAction_End) + { + CloseHandle(menu); + } + else if (action == MenuAction_Display) + { + decl String:title[64]; + GetMenuTitle(menu, title, sizeof(title)); + + decl String:map[64]; + GetMenuItem(menu, 1, map, sizeof(map)); + + decl String:buffer[255]; + Format(buffer, sizeof(buffer), "%T", title, param1, map); + + new Handle:panel = Handle:param2; + SetPanelTitle(panel, buffer); + } + else if (action == MenuAction_DisplayItem) + { + decl String:display[64]; + GetMenuItem(menu, param2, "", 0, _, display, sizeof(display)); + + if (strcmp(display, "No") == 0 || strcmp(display, "Yes") == 0) + { + decl String:buffer[255]; + Format(buffer, sizeof(buffer), "%T", display, param1); + + return RedrawMenuItem(buffer); + } + } + else if (action == MenuAction_VoteCancel && param1 == VoteCancel_NoVotes) + { + PrintToChatAll("[SM] %t", "No Votes Cast"); + } + else if (action == MenuAction_VoteEnd) + { + decl String:item[64], String:display[64]; + new Float:percent, Float:limit, votes, totalVotes; + + GetMenuVoteInfo(param2, votes, totalVotes); + GetMenuItem(menu, param1, item, sizeof(item), _, display, sizeof(display)); + + if (strcmp(item, VOTE_NO) == 0 && param1 == 1) + { + votes = totalVotes - votes; // Reverse the votes to be in relation to the Yes option. + } + + percent = GetVotePercent(votes, totalVotes); + + limit = GetConVarFloat(g_Cvar_VoteMap); + + // A multi-argument vote is "always successful", but have to check if its a Yes/No vote. + if ((strcmp(item, VOTE_YES) == 0 && FloatCompare(percent,limit) < 0 && param1 == 0) || (strcmp(item, VOTE_NO) == 0 && param1 == 1)) + { + LogAction(-1, -1, "Vote failed."); + PrintToChatAll("[SM] %t", "Votemap Failed", RoundToNearest(100.0*limit), RoundToNearest(100.0*percent), totalVotes); + } + else + { + PrintToChatAll("[SM] %t", "Votemap Successful", RoundToNearest(100.0*percent), totalVotes); + + if (g_VoteMapInUse && IsClientInGame(g_VoteMapInUse)) + { + DisplayAcceptVoteMenu(item); + } + else + { + LogAction(-1, -1, "Changing map to %s due to vote.", item); + PrintToChatAll("[SM] %t", "Changing map", item); + SetMapChange(0, item, g_Client_Data[g_VoteMapInUse][0]) + } + } + } + + return 0; +} + +public Handler_MapMapVoteMenu(Handle:menu, MenuAction:action, param1, param2) +{ + switch (action) + { + case MenuAction_End: + { + CloseHandle(menu); + } + + case MenuAction_Display: + { + decl String:oldTitle[255], String:buffer[255]; + GetMenuTitle(menu, oldTitle, sizeof(oldTitle)); + Format(buffer, sizeof(buffer), "%T", oldTitle, param1); + + new Handle:panel = Handle:param2; + SetPanelTitle(panel, buffer); + } + + case MenuAction_DisplayItem: + { + if (GetMenuItemCount(menu) - 1 == param2) + { + decl String:buffer[255]; + Format(buffer, sizeof(buffer), "%T", "Don't Change", param1); + return RedrawMenuItem(buffer); + } + } + + // Why am I commented out? Because BAIL hasn't decided yet if + // vote notification will be built into the Vote API. + /*case MenuAction_Select: + { + decl String:Name[32], String:Map[32]; + GetClientName(param1, Name, sizeof(Name)); + GetMenuItem(menu, param2, Map, sizeof(Map)); + + PrintToChatAll("[SM] %s has voted for map '%s'", Name, Map); + }*/ + + case MenuAction_VoteCancel: + { + if (param1 == VoteCancel_NoVotes) + { + PrintToChatAll("[SM] %t", "No Votes"); + g_RTVEnded = true; + } + } + + case MenuAction_VoteEnd: + { + new String:map[64]; + + GetMenuItem(menu, param1, map, sizeof(map)); + + if (GetMenuItemCount(menu) - 1 == param1) // This should always match the "Keep Current" option + { + PrintToChatAll("[SM] %t", "Current Map Stays"); + LogMessage("[SM] Rockthevote has ended, current map kept."); + } + else + { + PrintToChatAll("[SM] %t", "Changing Maps", map); + LogMessage("[SM] Rockthevote has ended, changing to map %s.", map); + new Handle:dp; + CreateDataTimer(5.0, Timer_ChangeMap, dp); + WritePackString(dp, map); + } + + g_RTVEnded = true; + } + } + + return 0; +} + +public Handler_MapVoteMenu(Handle:menu, MenuAction:action, param1, param2) +{ + switch (action) + { + case MenuAction_End: + { + g_VoteMenu = INVALID_HANDLE; + CloseHandle(menu); + } + + case MenuAction_Display: + { + decl String:buffer[255]; + Format(buffer, sizeof(buffer), "%T", "Vote Nextmap", param1); + + new Handle:panel = Handle:param2; + SetPanelTitle(panel, buffer); + } + + case MenuAction_DisplayItem: + { + if (GetMenuItemCount(menu) - 1 == param2) + { + decl String:map[64], String:buffer[255]; + GetMenuItem(menu, param2, map, sizeof(map)); + if (strcmp(map, VOTE_EXTEND, false) == 0) + { + Format(buffer, sizeof(buffer), "%T", "Extend Map", param1); + return RedrawMenuItem(buffer); + } + } + } + + // Why am I commented out? Because BAIL hasn't decided yet if + // vote notification will be built into the Vote API. + /*case MenuAction_Select: + { + decl String:Name[32], String:Map[32]; + GetClientName(param1, Name, sizeof(Name)); + GetMenuItem(menu, param2, Map, sizeof(Map)); + + PrintToChatAll("[SM] %s has voted for map '%s'", Name, Map); + }*/ + + case MenuAction_VoteCancel: + { + // If we receive 0 votes, pick at random. + if (param1 == VoteCancel_NoVotes && GetConVarBool(g_Cvar_NoVoteMode)) + { + new count = GetMenuItemCount(menu); + new item = GetRandomInt(0, count - 1); + decl String:map[32]; + GetMenuItem(menu, item, map, sizeof(map)); + + while (strcmp(map, VOTE_EXTEND, false) == 0) + { + item = GetRandomInt(0, count - 1); + GetMenuItem(menu, item, map, sizeof(map)); + } + + SetNextMap(map); + } + else + { + // We were actually cancelled. What should we do? + } + } + + case MenuAction_VoteEnd: + { + decl String:map[32]; + GetMenuItem(menu, param1, map, sizeof(map)); + + if (strcmp(map, VOTE_EXTEND, false) == 0) + { + new time; + if (GetMapTimeLimit(time)) + { + if (time > 0 && time < GetConVarInt(g_Cvar_ExtendTimeMax)) + { + ExtendMapTimeLimit(GetConVarInt(g_Cvar_ExtendTimeStep)*60); + } + } + + if (g_Cvar_Winlimit != INVALID_HANDLE) + { + new winlimit = GetConVarInt(g_Cvar_Winlimit); + if (winlimit && winlimit < GetConVarInt(g_Cvar_ExtendRoundMax)) + { + SetConVarInt(g_Cvar_Winlimit, winlimit + GetConVarInt(g_Cvar_ExtendRoundStep)); + } + } + + if (g_Cvar_Maxrounds != INVALID_HANDLE) + { + new maxrounds = GetConVarInt(g_Cvar_Maxrounds); + if (maxrounds && maxrounds < GetConVarInt(g_Cvar_ExtendRoundMax)) + { + SetConVarInt(g_Cvar_Maxrounds, maxrounds + GetConVarInt(g_Cvar_ExtendRoundStep)); + } + } + + if (g_Cvar_Fraglimit != INVALID_HANDLE) + { + new fraglimit = GetConVarInt(g_Cvar_Fraglimit); + if (fraglimit && fraglimit < GetConVarInt(g_Cvar_ExtendFragMax)) + { + SetConVarInt(g_Cvar_Fraglimit, fraglimit + GetConVarInt(g_Cvar_ExtendFragStep)); + } + } + + PrintToChatAll("[SM] %t", "Current Map Extended"); + LogMessage("Voting for next map has finished. The current map has been extended."); + + // We extended, so we'll have to vote again. + g_HasVoteStarted = false; + CreateNextVote(); + SetupTimeleftTimer(); + } + else + { + SetNextMap(map); + } + } + } + + return 0; +} \ No newline at end of file