3 * Adds sm_nextmap Cvar for changing map, and nextmap chat trigger
4 * This file is part of SourceMod, Copyright (C) 2004-2007 AlliedModders LLC
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 public Plugin:myinfo =
30 author = "AlliedModders LLC",
31 description = "Provides nextmap and sm_nextmap",
32 version = SOURCEMOD_VERSION,
33 url = "http://www.sourcemod.net/"
36 new bool:g_IntermissionCalled;
37 new UserMsg:g_VGUIMenu;
39 new Handle:g_Cvar_Chattime;
40 new Handle:g_Cvar_Nextmap;
41 new Handle:g_Cvar_Mapcycle;
44 new Handle:g_MapList = INVALID_HANDLE;
47 new bool:g_INS = false;
50 public OnPluginStart()
52 LoadTranslations("plugin.nextmap");
54 g_VGUIMenu = GetUserMessageId("VGUIMenu");
55 if (g_VGUIMenu == INVALID_MESSAGE_ID)
57 LogMessage("FATAL: Cannot find VGUIMenu user message id. Nextmap not loaded.");
61 g_Cvar_Chattime = FindConVar("mp_chattime");
62 g_Cvar_Mapcycle = FindConVar("mapcyclefile");
64 g_MapList = CreateArray(32);
66 decl String:mapCycle[64];
67 GetConVarString(g_Cvar_Mapcycle, mapCycle, 64);
68 if (LoadMaps(mapCycle) == 0)
70 LogMessage("FATAL: Cannot load map cycle. Nextmap not loaded.");
74 HookUserMessage(g_VGUIMenu, UserMsg_VGUIMenu);
75 HookConVarChange(g_Cvar_Mapcycle, ConVarChange_Mapcyclefile);
77 g_Cvar_Nextmap = CreateConVar("sm_nextmap", "", "Sets the Next Map", FCVAR_NOTIFY);
79 RegConsoleCmd("say", Command_Say);
80 RegConsoleCmd("say_team", Command_Say);
83 decl String:modname[64];
84 GetGameFolderName(modname, sizeof(modname));
86 if (strcmp(modname, "ins") == 0)
88 RegConsoleCmd("say2", Command_SayChat);
93 RegConsoleCmd("listmaps", Command_List);
95 /* Set to the current map so OnMapStart() will know what to do */
96 decl String:currentMap[64];
97 GetCurrentMap(currentMap, 64);
98 SetConVarString(g_Cvar_Nextmap, currentMap);
103 decl String:lastMap[64], String:currentMap[64];
104 GetConVarString(g_Cvar_Nextmap, lastMap, 64);
105 GetCurrentMap(currentMap, 64);
107 // 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
108 // some other plugin/command), we don't want to scramble the map cycle. Or for example, admin switches to a custom map
109 // not in mapcyclefile. So we keep it set to the last expected nextmap. - ferret
110 if (strcmp(lastMap, currentMap) == 0)
118 g_IntermissionCalled = false;
121 public ConVarChange_Mapcyclefile(Handle:convar, const String:oldValue[], const String:newValue[])
123 if (strcmp(oldValue, newValue, false) != 0)
129 public Action:Command_Say(client, args)
131 decl String:text[192];
132 GetCmdArgString(text, sizeof(text));
135 if (text[strlen(text)-1] == '"')
137 text[strlen(text)-1] = '\0';
148 decl String:message[8];
149 BreakString(text[startidx], message, sizeof(message));
151 if (strcmp(message, "nextmap", false) == 0)
154 GetConVarString(g_Cvar_Nextmap, map, sizeof(map));
156 PrintToChatAll("%t", "Next Map", map);
159 return Plugin_Continue;
162 public Action:UserMsg_VGUIMenu(UserMsg:msg_id, Handle:bf, const players[], playersNum, bool:reliable, bool:init)
164 if (g_IntermissionCalled)
166 return Plugin_Handled;
169 decl String:type[15];
170 BfReadString(bf, type, sizeof(type));
172 if (BfReadByte(bf) == 1 && BfReadByte(bf) == 0 && (strcmp(type, "scores", false) == 0))
174 g_IntermissionCalled = true;
177 new Float:fChatTime = GetConVarFloat(g_Cvar_Chattime);
179 GetConVarString(g_Cvar_Nextmap, map, sizeof(map));
182 SetConVarFloat(g_Cvar_Chattime, 2.0);
185 CreateDataTimer(fChatTime - 1.0, Timer_ChangeMap, dp);
186 WritePackString(dp, map);
189 return Plugin_Handled;
192 public Action:Timer_ChangeMap(Handle:timer, Handle:dp)
197 ReadPackString(dp, map, sizeof(map));
199 InsertServerCommand("changelevel \"%s\"", map);
202 LogMessage("Nextmap changed map to \"%s\"", map);
207 public Action:Command_List(client, args)
209 PrintToConsole(client, "Map Cycle:");
211 new mapCount = GetArraySize(g_MapList);
212 decl String:mapName[32];
213 for (new i = 0; i < mapCount; i++)
215 GetArrayString(g_MapList, i, mapName, sizeof(mapName));
216 PrintToConsole(client, "%s", mapName);
219 return Plugin_Handled;
222 LoadMaps(const String:path[])
224 if (!FileExists(path))
229 new Handle:file = OpenFile(path, "r");
231 if (file == INVALID_HANDLE)
233 LogError("[SM] Could not open file: %s", path);
237 ClearArray(g_MapList);
240 decl String:buffer[255];
241 while (!IsEndOfFile(file) && ReadFileLine(file, buffer, sizeof(buffer)))
244 if (buffer[0] == '\0' || buffer[0] == ';')
249 if (IsMapValid(buffer))
251 PushArrayString(g_MapList, buffer);
257 return GetArraySize(g_MapList);
262 new mapCount = GetArraySize(g_MapList);
263 decl String:mapName[32];
267 decl String:current[64];
268 GetCurrentMap(current, 64);
271 for (new i = 0; i < mapCount; i++)
273 GetArrayString(g_MapList, i, mapName, sizeof(mapName));
274 if (strcmp(current, mapName, false) == 0)
286 if (g_MapPos >= mapCount)
289 GetArrayString(g_MapList, g_MapPos, mapName, sizeof(mapName));
290 SetConVarString(g_Cvar_Nextmap, mapName);