/** * 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", "Max Nominations"); } 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)); if (result == Nominate_Added) { PrintToChatAll("[SM] %t", "Map Nominated", name, displayName); } else { ReplyToCommand(client, "[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; }