diff --git a/mapchooser_extended/scripting/nominations.sp b/mapchooser_extended/scripting/nominations.sp new file mode 100644 index 00000000..4f8fde1c --- /dev/null +++ b/mapchooser_extended/scripting/nominations.sp @@ -0,0 +1,502 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod Rock The Vote Plugin + * Creates a map vote when the required number of players have requested one. + * + * SourceMod (C)2004-2014 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$ + */ + +#include +#include + +#pragma semicolon 1 +#pragma newdecls required + +public Plugin myinfo = +{ + name = "Map Nominations", + author = "AlliedModders LLC", + description = "Provides Map Nominations", + version = SOURCEMOD_VERSION, + url = "http://www.sourcemod.net/" +}; + +ConVar g_Cvar_ExcludeOld; +ConVar g_Cvar_ExcludeCurrent; +ConVar g_Cvar_MaxMatches; + +Menu g_MapMenu = null; +ArrayList g_MapList = null; +int g_mapFileSerial = -1; + +#define MAPSTATUS_ENABLED (1<<0) +#define MAPSTATUS_DISABLED (1<<1) +#define MAPSTATUS_EXCLUDE_CURRENT (1<<2) +#define MAPSTATUS_EXCLUDE_PREVIOUS (1<<3) +#define MAPSTATUS_EXCLUDE_NOMINATED (1<<4) + +StringMap g_mapTrie = null; + +public void OnPluginStart() +{ + LoadTranslations("common.phrases"); + LoadTranslations("nominations.phrases"); + + int arraySize = ByteCountToCells(PLATFORM_MAX_PATH); + g_MapList = new ArrayList(arraySize); + + g_Cvar_ExcludeOld = CreateConVar("sm_nominate_excludeold", "1", "Specifies if the MapChooser excluded maps should also be excluded from Nominations", 0, true, 0.00, true, 1.0); + g_Cvar_ExcludeCurrent = CreateConVar("sm_nominate_excludecurrent", "1", "Specifies if the current map should be excluded from the Nominations list", 0, true, 0.00, true, 1.0); + g_Cvar_MaxMatches = CreateConVar("sm_nominate_maxfound", "0", "Maximum number of nomination matches to add to the menu. 0 = infinite.", _, true, 0.0); + + RegConsoleCmd("sm_nominate", Command_Nominate); + + RegAdminCmd("sm_nominate_addmap", Command_Addmap, ADMFLAG_CHANGEMAP, "sm_nominate_addmap - Forces a map to be on the next mapvote."); + + g_mapTrie = new StringMap(); +} + +public void OnConfigsExecuted() +{ + if (ReadMapList(g_MapList, + g_mapFileSerial, + "nominations", + MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER) + == null) + { + if (g_mapFileSerial == -1) + { + SetFailState("Unable to create a valid map list."); + } + } + + BuildMapMenu(); +} + +public void OnNominationRemoved(const char[] map, int owner) +{ + int status; + + char resolvedMap[PLATFORM_MAX_PATH]; + FindMap(map, resolvedMap, sizeof(resolvedMap)); + + /* Is the map in our list? */ + if (!g_mapTrie.GetValue(resolvedMap, status)) + { + return; + } + + /* Was the map disabled due to being nominated */ + if ((status & MAPSTATUS_EXCLUDE_NOMINATED) != MAPSTATUS_EXCLUDE_NOMINATED) + { + return; + } + + g_mapTrie.SetValue(resolvedMap, MAPSTATUS_ENABLED); +} + +public Action Command_Addmap(int client, int args) +{ + if (args < 1) + { + ReplyToCommand(client, "[SM] Usage: sm_nominate_addmap "); + return Plugin_Handled; + } + + char mapname[PLATFORM_MAX_PATH]; + char resolvedMap[PLATFORM_MAX_PATH]; + GetCmdArg(1, mapname, sizeof(mapname)); + + if (FindMap(mapname, resolvedMap, sizeof(resolvedMap)) == FindMap_NotFound) + { + // We couldn't resolve the map entry to a filename, so... + ReplyToCommand(client, "%t", "Map was not found", mapname); + return Plugin_Handled; + } + + char displayName[PLATFORM_MAX_PATH]; + GetMapDisplayName(resolvedMap, displayName, sizeof(displayName)); + + int status; + if (!g_mapTrie.GetValue(resolvedMap, status)) + { + ReplyToCommand(client, "%t", "Map was not found", displayName); + return Plugin_Handled; + } + + NominateResult result = NominateMap(resolvedMap, true, 0); + + if (result > Nominate_Replaced) + { + /* We assume already in vote is the casue because the maplist does a Map Validity check and we forced, so it can't be full */ + ReplyToCommand(client, "%t", "Map Already In Vote", displayName); + + return Plugin_Handled; + } + + + g_mapTrie.SetValue(resolvedMap, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); + + + ReplyToCommand(client, "%t", "Map Inserted", displayName); + LogAction(client, -1, "\"%L\" inserted map \"%s\".", client, mapname); + + return Plugin_Handled; +} + +public void OnClientSayCommand_Post(int client, const char[] command, const char[] sArgs) +{ + if (!client || IsChatTrigger()) + { + return; + } + + if (strcmp(sArgs, "nominate", false) == 0) + { + ReplySource old = SetCmdReplySource(SM_REPLY_TO_CHAT); + + OpenNominationMenu(client); + + SetCmdReplySource(old); + } +} + +public Action Command_Nominate(int client, int args) +{ + if (!client) + { + return Plugin_Handled; + } + + ReplySource source = GetCmdReplySource(); + + if (args == 0) + { + if (source == SM_REPLY_TO_CHAT) + { + OpenNominationMenu(client); + } + else + { + ReplyToCommand(client, "[SM] Usage: sm_nominate "); + } + + return Plugin_Handled; + } + + char mapname[PLATFORM_MAX_PATH]; + GetCmdArg(1, mapname, sizeof(mapname)); + + ArrayList results = new ArrayList(); + int matches = FindMatchingMaps(g_MapList, results, mapname); + + char mapResult[PLATFORM_MAX_PATH]; + + if (matches <= 0) + { + ReplyToCommand(client, "%t", "Map was not found", mapname); + } + // One result + else if (matches == 1) + { + // Get the result and nominate it + g_MapList.GetString(results.Get(0), mapResult, sizeof(mapResult)); + AttemptNominate(client, mapResult, sizeof(mapResult)); + } + else if (matches > 1) + { + if (source == SM_REPLY_TO_CONSOLE) + { + // if source is console, attempt instead of displaying menu. + AttemptNominate(client, mapname, sizeof(mapname)); + delete results; + return Plugin_Handled; + } + + // Display results to the client and end + Menu menu = new Menu(MenuHandler_MapSelect, MENU_ACTIONS_DEFAULT|MenuAction_DrawItem|MenuAction_DisplayItem); + menu.SetTitle("Select map"); + + for (int i = 0; i < results.Length; i++) + { + g_MapList.GetString(results.Get(i), mapResult, sizeof(mapResult)); + menu.AddItem(mapResult, mapResult); + } + + menu.Display(client, 30); + } + + delete results; + + return Plugin_Handled; +} + +int FindMatchingMaps(ArrayList mapList, ArrayList results, const char[] input) +{ + int map_count = mapList.Length; + + if (!map_count) + { + return -1; + } + + int matches = 0; + char map[PLATFORM_MAX_PATH]; + + int maxmatches = g_Cvar_MaxMatches.IntValue; + + for (int i = 0; i < map_count; i++) + { + mapList.GetString(i, map, sizeof(map)); + if (StrContains(map, input) != -1) + { + results.Push(i); + matches++; + + if (maxmatches > 0 && matches >= maxmatches) + { + break; + } + } + } + + return matches; +} + +void AttemptNominate(int client, const char[] map, int size) +{ + char mapname[PLATFORM_MAX_PATH]; + if (FindMap(map, mapname, size) == FindMap_NotFound) + { + // We couldn't resolve the map entry to a filename, so... + ReplyToCommand(client, "%t", "Map was not found", mapname); + return; + } + + char displayName[PLATFORM_MAX_PATH]; + GetMapDisplayName(mapname, displayName, sizeof(displayName)); + + int status; + if (!g_mapTrie.GetValue(mapname, status)) + { + ReplyToCommand(client, "%t", "Map was not found", displayName); + return; + } + + if ((status & MAPSTATUS_DISABLED) == MAPSTATUS_DISABLED) + { + if ((status & MAPSTATUS_EXCLUDE_CURRENT) == MAPSTATUS_EXCLUDE_CURRENT) + { + ReplyToCommand(client, "[SM] %t", "Can't Nominate Current Map"); + } + + if ((status & MAPSTATUS_EXCLUDE_PREVIOUS) == MAPSTATUS_EXCLUDE_PREVIOUS) + { + ReplyToCommand(client, "[SM] %t", "Map in Exclude List"); + } + + if ((status & MAPSTATUS_EXCLUDE_NOMINATED) == MAPSTATUS_EXCLUDE_NOMINATED) + { + ReplyToCommand(client, "[SM] %t", "Map Already Nominated"); + } + + return; + } + + NominateResult result = NominateMap(mapname, false, client); + + if (result > Nominate_Replaced) + { + if (result == Nominate_AlreadyInVote) + { + ReplyToCommand(client, "%t", "Map Already In Vote", displayName); + } + else + { + ReplyToCommand(client, "[SM] %t", "Map Already Nominated"); + } + + return; + } + + /* Map was nominated! - Disable the menu item and update the trie */ + + g_mapTrie.SetValue(mapname, MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_NOMINATED); + + char name[MAX_NAME_LENGTH]; + GetClientName(client, name, sizeof(name)); + + PrintToChatAll("[SM] %t", "Map Nominated", name, displayName); + + return; +} + +void OpenNominationMenu(int client) +{ + g_MapMenu.SetTitle("%T", "Nominate Title", client); + g_MapMenu.Display(client, MENU_TIME_FOREVER); +} + +void BuildMapMenu() +{ + delete g_MapMenu; + + g_mapTrie.Clear(); + + g_MapMenu = new Menu(MenuHandler_MapSelect, MENU_ACTIONS_DEFAULT|MenuAction_DrawItem|MenuAction_DisplayItem); + + char map[PLATFORM_MAX_PATH]; + + ArrayList excludeMaps; + char currentMap[PLATFORM_MAX_PATH]; + + if (g_Cvar_ExcludeOld.BoolValue) + { + excludeMaps = new ArrayList(ByteCountToCells(PLATFORM_MAX_PATH)); + GetExcludeMapList(excludeMaps); + } + + if (g_Cvar_ExcludeCurrent.BoolValue) + { + GetCurrentMap(currentMap, sizeof(currentMap)); + } + + for (int i = 0; i < g_MapList.Length; i++) + { + int status = MAPSTATUS_ENABLED; + + g_MapList.GetString(i, map, sizeof(map)); + + FindMap(map, map, sizeof(map)); + + char displayName[PLATFORM_MAX_PATH]; + GetMapDisplayName(map, displayName, sizeof(displayName)); + + if (g_Cvar_ExcludeCurrent.BoolValue) + { + if (StrEqual(map, currentMap)) + { + status = MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_CURRENT; + } + } + + /* Dont bother with this check if the current map check passed */ + if (g_Cvar_ExcludeOld.BoolValue && status == MAPSTATUS_ENABLED) + { + if (excludeMaps.FindString(map) != -1) + { + status = MAPSTATUS_DISABLED|MAPSTATUS_EXCLUDE_PREVIOUS; + } + } + + g_MapMenu.AddItem(map, displayName); + g_mapTrie.SetValue(map, status); + } + + g_MapMenu.ExitButton = true; + + delete excludeMaps; +} + +public int MenuHandler_MapSelect(Menu menu, MenuAction action, int param1, int param2) +{ + switch (action) + { + case MenuAction_Select: + { + char mapname[PLATFORM_MAX_PATH]; + // Get the map name and attempt to nominate it + menu.GetItem(param2, mapname, sizeof(mapname)); + AttemptNominate(param1, mapname, sizeof(mapname)); + } + case MenuAction_DrawItem: + { + char map[PLATFORM_MAX_PATH]; + menu.GetItem(param2, map, sizeof(map)); + + int status; + if (!g_mapTrie.GetValue(map, status)) + { + LogError("Menu selection of item not in trie. Major logic problem somewhere."); + return ITEMDRAW_DEFAULT; + } + + if ((status & MAPSTATUS_DISABLED) == MAPSTATUS_DISABLED) + { + return ITEMDRAW_DISABLED; + } + + return ITEMDRAW_DEFAULT; + } + case MenuAction_DisplayItem: + { + char mapname[PLATFORM_MAX_PATH]; + menu.GetItem(param2, mapname, sizeof(mapname)); + + int status; + + if (!g_mapTrie.GetValue(mapname, status)) + { + LogError("Menu selection of item not in trie. Major logic problem somewhere."); + return 0; + } + + if ((status & MAPSTATUS_DISABLED) == MAPSTATUS_DISABLED) + { + if ((status & MAPSTATUS_EXCLUDE_CURRENT) == MAPSTATUS_EXCLUDE_CURRENT) + { + Format(mapname, sizeof(mapname), "%s (%T)", mapname, "Current Map", param1); + return RedrawMenuItem(mapname); + } + + if ((status & MAPSTATUS_EXCLUDE_PREVIOUS) == MAPSTATUS_EXCLUDE_PREVIOUS) + { + Format(mapname, sizeof(mapname), "%s (%T)", mapname, "Recently Played", param1); + return RedrawMenuItem(mapname); + } + + if ((status & MAPSTATUS_EXCLUDE_NOMINATED) == MAPSTATUS_EXCLUDE_NOMINATED) + { + Format(mapname, sizeof(mapname), "%s (%T)", mapname, "Nominated", param1); + return RedrawMenuItem(mapname); + } + } + } + case MenuAction_End: + { + // This check allows the plugin to use the same callback + // for the main menu and the match menu. + if (menu != g_MapMenu) + { + delete menu; + } + + } + } + return 0; +}