1.1 --- a/plugins/mapchooser.sp Mon Jun 30 23:18:26 2008 +0000
1.2 +++ b/plugins/mapchooser.sp Tue Jul 01 01:18:52 2008 +0000
1.3 @@ -34,6 +34,11 @@
1.4
1.5 #pragma semicolon 1
1.6 #include <sourcemod>
1.7 +#include <mapchooser>
1.8 +#include <nextmap>
1.9 +#undef REQUIRE_EXTENSIONS
1.10 +#include <tf2>
1.11 +#define REQUIRE_EXTENSIONS
1.12
1.13 public Plugin:myinfo =
1.14 {
1.15 @@ -44,12 +49,13 @@
1.16 url = "http://www.sourcemod.net/"
1.17 };
1.18
1.19 +/* Valve ConVars */
1.20 new Handle:g_Cvar_Winlimit = INVALID_HANDLE;
1.21 new Handle:g_Cvar_Maxrounds = INVALID_HANDLE;
1.22 new Handle:g_Cvar_Fraglimit = INVALID_HANDLE;
1.23 +new Handle:g_Cvar_Bonusroundtime = INVALID_HANDLE;
1.24
1.25 -new Handle:g_Cvar_Nextmap = INVALID_HANDLE;
1.26 -
1.27 +/* Plugin ConVars */
1.28 new Handle:g_Cvar_StartTime = INVALID_HANDLE;
1.29 new Handle:g_Cvar_StartRounds = INVALID_HANDLE;
1.30 new Handle:g_Cvar_StartFrags = INVALID_HANDLE;
1.31 @@ -60,22 +66,39 @@
1.32 new Handle:g_Cvar_IncludeMaps = INVALID_HANDLE;
1.33 new Handle:g_Cvar_NoVoteMode = INVALID_HANDLE;
1.34 new Handle:g_Cvar_Extend = INVALID_HANDLE;
1.35 -
1.36 +new Handle:g_Cvar_DontChange = INVALID_HANDLE;
1.37 +new Handle:g_Cvar_EndOfMapVote = INVALID_HANDLE;
1.38 +new Handle:g_Cvar_VoteDuration = INVALID_HANDLE;
1.39 new Handle:g_VoteTimer = INVALID_HANDLE;
1.40 new Handle:g_RetryTimer = INVALID_HANDLE;
1.41
1.42 +/* Data Handles */
1.43 new Handle:g_MapList = INVALID_HANDLE;
1.44 +new Handle:g_NominateList = INVALID_HANDLE;
1.45 +new Handle:g_NominateOwners = INVALID_HANDLE;
1.46 new Handle:g_OldMapList = INVALID_HANDLE;
1.47 new Handle:g_NextMapList = INVALID_HANDLE;
1.48 -new Handle:g_TeamScores = INVALID_HANDLE;
1.49 new Handle:g_VoteMenu = INVALID_HANDLE;
1.50
1.51 new g_Extends;
1.52 new g_TotalRounds;
1.53 new bool:g_HasVoteStarted;
1.54 +new bool:g_WaitingForVote;
1.55 +new bool:g_MapVoteCompleted;
1.56 +new bool:g_ChangeMapAtRoundEnd;
1.57 new g_mapFileSerial = -1;
1.58
1.59 +new g_NominateCount = 0;
1.60 +new MapChange:g_ChangeTime;
1.61 +
1.62 +new Handle:g_NominationsResetForward = INVALID_HANDLE;
1.63 +
1.64 +/* Upper bound of how many team there could be */
1.65 +#define MAXTEAMS 10
1.66 +new g_winCount[MAXTEAMS];
1.67 +
1.68 #define VOTE_EXTEND "##extend##"
1.69 +#define VOTE_DONTCHANGE "##dontchange##"
1.70
1.71 public OnPluginStart()
1.72 {
1.73 @@ -83,30 +106,38 @@
1.74
1.75 new arraySize = ByteCountToCells(33);
1.76 g_MapList = CreateArray(arraySize);
1.77 + g_NominateList = CreateArray(arraySize);
1.78 + g_NominateOwners = CreateArray(1);
1.79 g_OldMapList = CreateArray(arraySize);
1.80 g_NextMapList = CreateArray(arraySize);
1.81 +
1.82 + g_Cvar_EndOfMapVote = CreateConVar("sm_mapvote_endvote", "1", "Specifies if MapChooser should run an end of map vote", _, true, 0.0, true, 1.0);
1.83 + HookConVarChange(g_Cvar_EndOfMapVote, EndVoteChanged);
1.84
1.85 g_Cvar_StartTime = CreateConVar("sm_mapvote_start", "3.0", "Specifies when to start the vote based on time remaining.", _, true, 1.0);
1.86 - g_Cvar_StartRounds = CreateConVar("sm_mapvote_startround", "2.0", "Specifies when to start the vote based on rounds remaining.", _, true, 1.0);
1.87 + g_Cvar_StartRounds = CreateConVar("sm_mapvote_startround", "2.0", "Specifies when to start the vote based on rounds remaining. Use 0 on TF2 to start vote during bonus round time", _, true, 0.0);
1.88 g_Cvar_StartFrags = CreateConVar("sm_mapvote_startfrags", "5.0", "Specifies when to start the vote base on frags remaining.", _, true, 1.0);
1.89 g_Cvar_ExtendTimeStep = CreateConVar("sm_extendmap_timestep", "15", "Specifies how much many more minutes each extension makes", _, true, 5.0);
1.90 - g_Cvar_ExtendRoundStep = CreateConVar("sm_extendmap_roundstep", "5", "Specifies how many more rounds each extension makes", _, true, 5.0);
1.91 + g_Cvar_ExtendRoundStep = CreateConVar("sm_extendmap_roundstep", "5", "Specifies how many more rounds each extension makes", _, true, 1.0);
1.92 g_Cvar_ExtendFragStep = CreateConVar("sm_extendmap_fragstep", "10", "Specifies how many more frags are allowed when map is extended.", _, true, 5.0);
1.93 g_Cvar_ExcludeMaps = CreateConVar("sm_mapvote_exclude", "5", "Specifies how many past maps to exclude from the vote.", _, true, 0.0);
1.94 g_Cvar_IncludeMaps = CreateConVar("sm_mapvote_include", "5", "Specifies how many maps to include in the vote.", _, true, 2.0, true, 6.0);
1.95 g_Cvar_NoVoteMode = CreateConVar("sm_mapvote_novote", "1", "Specifies whether or not MapChooser should pick a map if no votes are received.", _, true, 0.0, true, 1.0);
1.96 g_Cvar_Extend = CreateConVar("sm_mapvote_extend", "0", "Number of extensions allowed each map.", _, true, 0.0);
1.97 -
1.98 + g_Cvar_DontChange = CreateConVar("sm_mapvote_dontchange", "1", "Specifies if a 'Don't Change' option should be added to early votes", _, true, 0.0);
1.99 + g_Cvar_VoteDuration = CreateConVar("sm_mapvote_voteduration", "20", "Specifies how long the mapvote should be available for.", _, true, 5.0, true, 25.0);
1.100 RegAdminCmd("sm_mapvote", Command_Mapvote, ADMFLAG_CHANGEMAP, "sm_mapvote - Forces MapChooser to attempt to run a map vote now.");
1.101
1.102 g_Cvar_Winlimit = FindConVar("mp_winlimit");
1.103 g_Cvar_Maxrounds = FindConVar("mp_maxrounds");
1.104 g_Cvar_Fraglimit = FindConVar("mp_fraglimit");
1.105 + g_Cvar_Bonusroundtime = FindConVar("mp_bonusroundtime");
1.106
1.107 if (g_Cvar_Winlimit != INVALID_HANDLE || g_Cvar_Maxrounds != INVALID_HANDLE)
1.108 {
1.109 HookEvent("round_end", Event_RoundEnd);
1.110 - HookEventEx("teamplay_round_win", Event_TeamPlayRoundWin);
1.111 + HookEventEx("teamplay_win_panel", Event_TeamPlayWinPanel);
1.112 + HookEventEx("teamplay_restart_round", Event_TFRestartRound);
1.113 }
1.114
1.115 if (g_Cvar_Fraglimit != INVALID_HANDLE)
1.116 @@ -115,18 +146,37 @@
1.117 }
1.118
1.119 AutoExecConfig(true, "mapchooser");
1.120 +
1.121 + //Change the mp_bonusroundtime max so that we have time to display the vote
1.122 + //If you display a vote during bonus time good defaults are 17 vote duration and 19 mp_bonustime
1.123 + if (g_Cvar_Bonusroundtime != INVALID_HANDLE)
1.124 + {
1.125 + SetConVarBounds(g_Cvar_Bonusroundtime, ConVarBound_Upper, true, 30.0);
1.126 + }
1.127 +
1.128 + RegPluginLibrary("mapchooser");
1.129 +
1.130 + g_NominationsResetForward = CreateGlobalForward("OnNominationRemoved", ET_Ignore, Param_String, Param_Cell);
1.131 +}
1.132 +
1.133 +public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max)
1.134 +{
1.135 + CreateNative("NominateMap", Native_NominateMap);
1.136 + CreateNative("InitiateMapChooserVote", Native_InitiateVote);
1.137 + CreateNative("CanMapChooserStartVote", Native_CanVoteStart);
1.138 + CreateNative("HasEndOfMapVoteFinished", Native_CheckVoteDone);
1.139 + CreateNative("GetExcludeMapList", Native_GetExcludeMapList);
1.140 +
1.141 + return true;
1.142 +}
1.143 +
1.144 +public OnMapStart()
1.145 +{
1.146 + Call_Cancel();
1.147 }
1.148
1.149 public OnConfigsExecuted()
1.150 {
1.151 - g_Cvar_Nextmap = FindConVar("sm_nextmap");
1.152 -
1.153 - if (g_Cvar_Nextmap == INVALID_HANDLE)
1.154 - {
1.155 - LogError("FATAL: Cannot find sm_nextmap cvar. Mapchooser not loaded.");
1.156 - SetFailState("sm_nextmap not found");
1.157 - }
1.158 -
1.159 if (ReadMapList(g_MapList,
1.160 g_mapFileSerial,
1.161 "mapchooser",
1.162 @@ -142,22 +192,37 @@
1.163
1.164 CreateNextVote();
1.165 SetupTimeleftTimer();
1.166 - SetConVarString(g_Cvar_Nextmap, "Pending Vote");
1.167
1.168 - if (g_TeamScores != INVALID_HANDLE)
1.169 - {
1.170 - CloseHandle(g_TeamScores);
1.171 - }
1.172 -
1.173 - g_TeamScores = CreateArray(2);
1.174 g_TotalRounds = 0;
1.175
1.176 g_Extends = 0;
1.177 +
1.178 + g_MapVoteCompleted = false;
1.179 +
1.180 + g_NominateCount = 0;
1.181 + ClearArray(g_NominateList);
1.182 + ClearArray(g_NominateOwners);
1.183 +
1.184 + for (new i=0; i<MAXTEAMS; i++)
1.185 + {
1.186 + g_winCount[i] = 0;
1.187 + }
1.188 +
1.189 +
1.190 + /* Check if mapchooser will attempt to start mapvote during bonus round time - TF2 Only */
1.191 + if ((g_Cvar_Bonusroundtime != INVALID_HANDLE) && !GetConVarInt(g_Cvar_StartRounds))
1.192 + {
1.193 + if (GetConVarFloat(g_Cvar_Bonusroundtime) <= GetConVarFloat(g_Cvar_VoteDuration))
1.194 + {
1.195 + LogError("Warning - Bonus Round Time shorter than Vote Time. Votes during bonus round may not have time to complete");
1.196 + }
1.197 + }
1.198 }
1.199
1.200 public OnMapEnd()
1.201 {
1.202 g_HasVoteStarted = false;
1.203 + g_WaitingForVote = false;
1.204
1.205 if (g_VoteTimer != INVALID_HANDLE)
1.206 {
1.207 @@ -170,6 +235,48 @@
1.208 KillTimer(g_RetryTimer);
1.209 g_RetryTimer = INVALID_HANDLE;
1.210 }
1.211 +
1.212 + decl String:map[32];
1.213 + GetCurrentMap(map, sizeof(map));
1.214 + PushArrayString(g_OldMapList, map);
1.215 +
1.216 + if (GetArraySize(g_OldMapList) > GetConVarInt(g_Cvar_ExcludeMaps))
1.217 + {
1.218 + RemoveFromArray(g_OldMapList, 0);
1.219 + }
1.220 +}
1.221 +
1.222 +public OnClientDisconnect(client)
1.223 +{
1.224 + new index = FindValueInArray(g_NominateOwners, client);
1.225 +
1.226 + if (index == -1)
1.227 + {
1.228 + return;
1.229 + }
1.230 +
1.231 + new String:oldmap[33];
1.232 + GetArrayString(g_NominateList, index, oldmap, sizeof(oldmap));
1.233 + Call_StartForward(g_NominationsResetForward);
1.234 + Call_PushString(oldmap);
1.235 + Call_PushCell(GetArrayCell(g_NominateOwners, index));
1.236 + Call_Finish();
1.237 +
1.238 + RemoveFromArray(g_NominateOwners, index);
1.239 + RemoveFromArray(g_NominateList, index);
1.240 + g_NominateCount--;
1.241 +}
1.242 +
1.243 +public EndVoteChanged(Handle:convar, const String:oldValue[], const String:newValue[])
1.244 +{
1.245 + if (newValue[0] == '1')
1.246 + {
1.247 + SetNextMap("Pending Vote");
1.248 + }
1.249 + else
1.250 + {
1.251 + SetNextMap("");
1.252 + }
1.253 }
1.254
1.255 public OnMapTimeLeftChanged()
1.256 @@ -186,9 +293,9 @@
1.257 if (GetMapTimeLeft(time) && time > 0)
1.258 {
1.259 new startTime = GetConVarInt(g_Cvar_StartTime) * 60;
1.260 - if (time - startTime < 0)
1.261 + if (time - startTime < 0 && GetConVarBool(g_Cvar_EndOfMapVote) && !g_MapVoteCompleted && !g_HasVoteStarted)
1.262 {
1.263 - InitiateVote();
1.264 + InitiateVote(MapChange_MapEnd, INVALID_HANDLE);
1.265 }
1.266 else
1.267 {
1.268 @@ -205,7 +312,7 @@
1.269
1.270 public Action:Timer_StartMapVote(Handle:timer)
1.271 {
1.272 - if (!GetArraySize(g_MapList))
1.273 + if (!GetArraySize(g_MapList) || !GetConVarBool(g_Cvar_EndOfMapVote) || g_MapVoteCompleted || g_HasVoteStarted)
1.274 {
1.275 return Plugin_Stop;
1.276 }
1.277 @@ -218,87 +325,116 @@
1.278 {
1.279 g_VoteTimer = INVALID_HANDLE;
1.280 }
1.281 -
1.282 - InitiateVote();
1.283 +
1.284 + InitiateVote(MapChange_MapEnd, INVALID_HANDLE);
1.285
1.286 return Plugin_Stop;
1.287 }
1.288
1.289 -public Event_TeamPlayRoundWin(Handle:event, const String:name[], bool:dontBroadcast)
1.290 +public Event_TFRestartRound(Handle:event, const String:name[], bool:dontBroadcast)
1.291 +{
1.292 + /* Game got restarted - reset our round count tracking */
1.293 + g_TotalRounds = 0;
1.294 +}
1.295 +
1.296 +public Event_TeamPlayWinPanel(Handle:event, const String:name[], bool:dontBroadcast)
1.297 {
1.298 - new isFull = GetEventInt(event, "full_round");
1.299 - new winningTeam = GetEventInt(event, "team");
1.300 + if (g_ChangeMapAtRoundEnd)
1.301 + {
1.302 + g_ChangeMapAtRoundEnd = false;
1.303 + CreateTimer(2.0, Timer_ChangeMap, INVALID_HANDLE);
1.304 + }
1.305 +
1.306 + if (!GetArraySize(g_MapList) || g_HasVoteStarted || g_MapVoteCompleted || !GetConVarBool(g_Cvar_EndOfMapVote))
1.307 + {
1.308 + return;
1.309 + }
1.310 +
1.311 + new bluescore = GetEventInt(event, "blue_score");
1.312 + new redscore = GetEventInt(event, "red_score");
1.313
1.314 - if (isFull == 1)
1.315 - {
1.316 - RoundCompleted(winningTeam);
1.317 + if(GetEventInt(event, "round_complete") == 1)
1.318 + {
1.319 + switch(GetEventInt(event, "winning_team"))
1.320 + {
1.321 + case TFTeam_Blue:
1.322 + {
1.323 + CheckWinLimit(bluescore);
1.324 + }
1.325 + case TFTeam_Red:
1.326 + {
1.327 + CheckWinLimit(redscore);
1.328 + }
1.329 + //We need to do nothing on winning_team == 0 this indicates stalemate.
1.330 + default:
1.331 + {
1.332 + return;
1.333 + }
1.334 + }
1.335 +
1.336 + g_TotalRounds++;
1.337 + CheckMaxRounds(g_TotalRounds);
1.338 }
1.339 }
1.340 /* You ask, why don't you just use team_score event? And I answer... Because CSS doesn't. */
1.341 public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast)
1.342 {
1.343 - if (!GetArraySize(g_MapList) || g_HasVoteStarted)
1.344 + if (g_ChangeMapAtRoundEnd)
1.345 + {
1.346 + g_ChangeMapAtRoundEnd = false;
1.347 + CreateTimer(2.0, Timer_ChangeMap, INVALID_HANDLE);
1.348 + }
1.349 +
1.350 + if (!GetArraySize(g_MapList) || g_HasVoteStarted || g_MapVoteCompleted)
1.351 {
1.352 return;
1.353 }
1.354
1.355 new winner = GetEventInt(event, "winner");
1.356
1.357 - RoundCompleted(winner);
1.358 -}
1.359 -
1.360 -public RoundCompleted(winner)
1.361 -{
1.362 - if (winner == 0 || winner == 1)
1.363 + if (winner == 0 || winner == 1 || !GetConVarBool(g_Cvar_EndOfMapVote))
1.364 {
1.365 return;
1.366 }
1.367 +
1.368 + if (winner >= MAXTEAMS)
1.369 + {
1.370 + SetFailState("Mod exceed maximum team count - Please file a bug report.");
1.371 + }
1.372
1.373 g_TotalRounds++;
1.374
1.375 - new team[2], teamPos = -1;
1.376 - for (new i; i < GetArraySize(g_TeamScores); i++)
1.377 - {
1.378 - GetArrayArray(g_TeamScores, i, team);
1.379 - if (team[0] == winner)
1.380 - {
1.381 - teamPos = i;
1.382 - break;
1.383 - }
1.384 - }
1.385 + g_winCount[winner]++;
1.386
1.387 - if (teamPos == -1)
1.388 - {
1.389 - team[0] = winner;
1.390 - team[1] = 1;
1.391 - PushArrayArray(g_TeamScores, team);
1.392 - }
1.393 - else
1.394 - {
1.395 - team[1]++;
1.396 - SetArrayArray(g_TeamScores, teamPos, team);
1.397 - }
1.398 -
1.399 + CheckWinLimit(g_winCount[winner]);
1.400 + CheckMaxRounds(g_TotalRounds);
1.401 +}
1.402 +
1.403 +public CheckWinLimit(winner_score)
1.404 +{
1.405 if (g_Cvar_Winlimit != INVALID_HANDLE)
1.406 {
1.407 new winlimit = GetConVarInt(g_Cvar_Winlimit);
1.408 if (winlimit)
1.409 - {
1.410 - if (team[1] >= (winlimit - GetConVarInt(g_Cvar_StartRounds)))
1.411 + {
1.412 + if (winner_score >= (winlimit - GetConVarInt(g_Cvar_StartRounds)))
1.413 {
1.414 - InitiateVote();
1.415 + InitiateVote(MapChange_MapEnd, INVALID_HANDLE);
1.416 }
1.417 }
1.418 }
1.419 -
1.420 +}
1.421 +
1.422 +public CheckMaxRounds(roundcount)
1.423 +{
1.424 if (g_Cvar_Maxrounds != INVALID_HANDLE)
1.425 {
1.426 new maxrounds = GetConVarInt(g_Cvar_Maxrounds);
1.427 if (maxrounds)
1.428 {
1.429 - if (g_TotalRounds >= (maxrounds - GetConVarInt(g_Cvar_StartRounds)))
1.430 + if (roundcount >= (maxrounds - GetConVarInt(g_Cvar_StartRounds)))
1.431 {
1.432 - InitiateVote();
1.433 + InitiateVote(MapChange_MapEnd, INVALID_HANDLE);
1.434 }
1.435 }
1.436 }
1.437 @@ -311,7 +447,7 @@
1.438 return;
1.439 }
1.440
1.441 - if (!GetConVarInt(g_Cvar_Fraglimit))
1.442 + if (!GetConVarInt(g_Cvar_Fraglimit) || !GetConVarBool(g_Cvar_EndOfMapVote))
1.443 {
1.444 return;
1.445 }
1.446 @@ -319,54 +455,249 @@
1.447 new fragger = GetClientOfUserId(GetEventInt(event, "attacker"));
1.448 if (fragger && GetClientFrags(fragger) >= (GetConVarInt(g_Cvar_Fraglimit) - GetConVarInt(g_Cvar_StartFrags)))
1.449 {
1.450 - InitiateVote();
1.451 + InitiateVote(MapChange_MapEnd, INVALID_HANDLE);
1.452 }
1.453 }
1.454
1.455 public Action:Command_Mapvote(client, args)
1.456 {
1.457 - InitiateVote();
1.458 + InitiateVote(MapChange_MapEnd, INVALID_HANDLE);
1.459
1.460 return Plugin_Handled;
1.461 }
1.462
1.463 -InitiateVote()
1.464 +InitiateVote(MapChange:when, Handle:inputlist)
1.465 {
1.466 - if (g_HasVoteStarted || g_RetryTimer != INVALID_HANDLE)
1.467 + if (!CanVoteStart())
1.468 {
1.469 return;
1.470 }
1.471
1.472 + g_WaitingForVote = true;
1.473 +
1.474 if (IsVoteInProgress())
1.475 {
1.476 // Can't start a vote, try again in 5 seconds.
1.477 g_RetryTimer = CreateTimer(5.0, Timer_StartMapVote);
1.478 return;
1.479 - }
1.480 + }
1.481
1.482 + g_ChangeTime = when;
1.483 +
1.484 + g_WaitingForVote = false;
1.485 +
1.486 g_HasVoteStarted = true;
1.487 g_VoteMenu = CreateMenu(Handler_MapVoteMenu, MenuAction:MENU_ACTIONS_ALL);
1.488 SetMenuTitle(g_VoteMenu, "Vote Nextmap");
1.489 -
1.490 + SetVoteResultCallback(g_VoteMenu, Handler_MapVoteFinished);
1.491 +
1.492 + /**
1.493 + * TODO: Make a proper decision on when to clear the nominations list.
1.494 + * Currently it clears when used, and stays if an external list is provided.
1.495 + * Is this the right thing to do? External lists will probably come from places
1.496 + * like sm_mapvote from the adminmenu in the future.
1.497 + */
1.498 +
1.499 decl String:map[32];
1.500 - for (new i = 0; i < GetArraySize(g_NextMapList); i++)
1.501 +
1.502 + /* No input given - User our internal nominations and maplist */
1.503 + if (inputlist == INVALID_HANDLE)
1.504 {
1.505 - GetArrayString(g_NextMapList, i, map, sizeof(map));
1.506 - AddMenuItem(g_VoteMenu, map, map);
1.507 + new nominateCount = GetArraySize(g_NominateList);
1.508 + new voteSize = GetConVarInt(g_Cvar_IncludeMaps);
1.509 +
1.510 + /* Smaller of the two - It should be impossible for nominations to exceed the size though (cvar changed mid-map?) */
1.511 + new nominationsToAdd = nominateCount >= voteSize ? voteSize : nominateCount;
1.512 +
1.513 +
1.514 + for (new i=0; i<nominationsToAdd; i++)
1.515 + {
1.516 + GetArrayString(g_NominateList, i, map, sizeof(map));
1.517 + AddMenuItem(g_VoteMenu, map, map);
1.518 +
1.519 + /* Notify Nominations that this map is now free */
1.520 + Call_StartForward(g_NominationsResetForward);
1.521 + Call_PushString(map);
1.522 + Call_PushCell(GetArrayCell(g_NominateOwners, i));
1.523 + Call_Finish();
1.524 + }
1.525 +
1.526 + /* Clear out the rest of the nominations array */
1.527 + for (new i=nominationsToAdd; i<nominateCount; i++)
1.528 + {
1.529 + GetArrayString(g_NominateList, i, map, sizeof(map));
1.530 +
1.531 + /* Notify Nominations that this map is now free */
1.532 + Call_StartForward(g_NominationsResetForward);
1.533 + Call_PushString(map);
1.534 + Call_PushCell(GetArrayCell(g_NominateOwners, i));
1.535 + Call_Finish();
1.536 + }
1.537 +
1.538 + /* There should currently be 'nominationsToAdd' unique maps in the vote */
1.539 +
1.540 + new i = nominationsToAdd;
1.541 + new count = 0;
1.542 + new availableMaps = GetArraySize(g_NextMapList);
1.543 +
1.544 + while (i < voteSize)
1.545 + {
1.546 + GetArrayString(g_NextMapList, count, map, sizeof(map));
1.547 + count++;
1.548 +
1.549 + //Check if this map is in the nominate list (and thus already in the vote) */
1.550 + if (FindStringInArray(g_NominateList, map) == -1)
1.551 + {
1.552 + /* Insert the map and increment our count */
1.553 + AddMenuItem(g_VoteMenu, map, map);
1.554 + i++;
1.555 + }
1.556 +
1.557 + if (count >= availableMaps)
1.558 + {
1.559 + //Run out of maps, this will have to do.
1.560 + break;
1.561 + }
1.562 + }
1.563 +
1.564 + /* Wipe out our nominations list - Nominations have already been informed of this */
1.565 + ClearArray(g_NominateOwners);
1.566 + ClearArray(g_NominateList);
1.567 }
1.568 -
1.569 - if (GetConVarBool(g_Cvar_Extend) && g_Extends < GetConVarInt(g_Cvar_Extend))
1.570 + else //We were given a list of maps to start the vote with
1.571 + {
1.572 + new size = GetArraySize(inputlist);
1.573 +
1.574 + for (new i=0; i<size; i++)
1.575 + {
1.576 + GetArrayString(inputlist, i, map, sizeof(map));
1.577 +
1.578 + if (IsMapValid(map))
1.579 + {
1.580 + AddMenuItem(g_VoteMenu, map, map);
1.581 + }
1.582 + }
1.583 + }
1.584 +
1.585 + /* Do we add any special items? */
1.586 + if ((when == MapChange_Instant || when == MapChange_RoundEnd) && GetConVarBool(g_Cvar_DontChange))
1.587 + {
1.588 + AddMenuItem(g_VoteMenu, VOTE_DONTCHANGE, "Don't Change");
1.589 + }
1.590 + else if (GetConVarBool(g_Cvar_Extend) && g_Extends < GetConVarInt(g_Cvar_Extend))
1.591 {
1.592 AddMenuItem(g_VoteMenu, VOTE_EXTEND, "Extend Map");
1.593 }
1.594 +
1.595 + new voteDuration = GetConVarInt(g_Cvar_VoteDuration);
1.596
1.597 SetMenuExitButton(g_VoteMenu, false);
1.598 - VoteMenuToAll(g_VoteMenu, 20);
1.599 + VoteMenuToAll(g_VoteMenu, voteDuration);
1.600
1.601 LogMessage("Voting for next map has started.");
1.602 PrintToChatAll("[SM] %t", "Nextmap Voting Started");
1.603 }
1.604
1.605 +public Handler_MapVoteFinished(Handle:menu,
1.606 + num_votes,
1.607 + num_clients,
1.608 + const client_info[][2],
1.609 + num_items,
1.610 + const item_info[][2])
1.611 +{
1.612 + if (num_votes == 0)
1.613 + {
1.614 + LogError("No Votes recorded yet Advanced callback fired - Tell pRED* to fix this");
1.615 + return;
1.616 + }
1.617 +
1.618 + decl String:map[32];
1.619 + GetMenuItem(menu, item_info[0][VOTEINFO_ITEM_INDEX], map, sizeof(map));
1.620 +
1.621 + if (strcmp(map, VOTE_EXTEND, false) == 0)
1.622 + {
1.623 + g_Extends++;
1.624 +
1.625 + new time;
1.626 + if (GetMapTimeLimit(time))
1.627 + {
1.628 + if (time > 0)
1.629 + {
1.630 + ExtendMapTimeLimit(GetConVarInt(g_Cvar_ExtendTimeStep)*60);
1.631 + }
1.632 + }
1.633 +
1.634 + if (g_Cvar_Winlimit != INVALID_HANDLE)
1.635 + {
1.636 + new winlimit = GetConVarInt(g_Cvar_Winlimit);
1.637 + if (winlimit)
1.638 + {
1.639 + SetConVarInt(g_Cvar_Winlimit, winlimit + GetConVarInt(g_Cvar_ExtendRoundStep));
1.640 + }
1.641 + }
1.642 +
1.643 + if (g_Cvar_Maxrounds != INVALID_HANDLE)
1.644 + {
1.645 + new maxrounds = GetConVarInt(g_Cvar_Maxrounds);
1.646 + if (maxrounds)
1.647 + {
1.648 + SetConVarInt(g_Cvar_Maxrounds, maxrounds + GetConVarInt(g_Cvar_ExtendRoundStep));
1.649 + }
1.650 + }
1.651 +
1.652 + if (g_Cvar_Fraglimit != INVALID_HANDLE)
1.653 + {
1.654 + new fraglimit = GetConVarInt(g_Cvar_Fraglimit);
1.655 + if (fraglimit)
1.656 + {
1.657 + SetConVarInt(g_Cvar_Fraglimit, fraglimit + GetConVarInt(g_Cvar_ExtendFragStep));
1.658 + }
1.659 + }
1.660 +
1.661 + PrintToChatAll("[SM] %t", "Current Map Extended", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes);
1.662 + LogMessage("Voting for next map has finished. The current map has been extended.");
1.663 +
1.664 + // We extended, so we'll have to vote again.
1.665 + g_HasVoteStarted = false;
1.666 + CreateNextVote();
1.667 + SetupTimeleftTimer();
1.668 +
1.669 + }
1.670 + else if (strcmp(map, VOTE_DONTCHANGE, false) == 0)
1.671 + {
1.672 + PrintToChatAll("[SM] %t", "Current Map Stays", RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes);
1.673 + LogMessage("Voting for next map has finished. 'No Change' was the winner");
1.674 +
1.675 + g_HasVoteStarted = false;
1.676 + CreateNextVote();
1.677 + SetupTimeleftTimer();
1.678 + }
1.679 + else
1.680 + {
1.681 + if (g_ChangeTime == MapChange_MapEnd)
1.682 + {
1.683 + SetNextMap(map);
1.684 + }
1.685 + else if (g_ChangeTime == MapChange_Instant)
1.686 + {
1.687 + new Handle:data;
1.688 + CreateDataTimer(2.0, Timer_ChangeMap, data);
1.689 + WritePackString(data, map);
1.690 + }
1.691 + else // MapChange_RoundEnd
1.692 + {
1.693 + SetNextMap(map);
1.694 + g_ChangeMapAtRoundEnd = true;
1.695 + }
1.696 +
1.697 + g_HasVoteStarted = false;
1.698 + g_MapVoteCompleted = true;
1.699 +
1.700 + PrintToChatAll("[SM] %t", "Nextmap Voting Finished", map, RoundToFloor(float(item_info[0][VOTEINFO_ITEM_VOTES])/float(num_votes)*100), num_votes);
1.701 + LogMessage("Voting for next map has finished. Nextmap: %s.", map);
1.702 + }
1.703 +}
1.704 +
1.705 public Handler_MapVoteMenu(Handle:menu, MenuAction:action, param1, param2)
1.706 {
1.707 switch (action)
1.708 @@ -397,20 +728,14 @@
1.709 Format(buffer, sizeof(buffer), "%T", "Extend Map", param1);
1.710 return RedrawMenuItem(buffer);
1.711 }
1.712 + else if (strcmp(map, VOTE_DONTCHANGE, false) == 0)
1.713 + {
1.714 + Format(buffer, sizeof(buffer), "%T", "Dont Change", param1);
1.715 + return RedrawMenuItem(buffer);
1.716 + }
1.717 }
1.718 }
1.719 -
1.720 - // Why am I commented out? Because BAIL hasn't decided yet if
1.721 - // vote notification will be built into the Vote API.
1.722 - /*case MenuAction_Select:
1.723 - {
1.724 - decl String:Name[32], String:Map[32];
1.725 - GetClientName(param1, Name, sizeof(Name));
1.726 - GetMenuItem(menu, param2, Map, sizeof(Map));
1.727 -
1.728 - PrintToChatAll("[SM] %s has voted for map '%s'", Name, Map);
1.729 - }*/
1.730 -
1.731 +
1.732 case MenuAction_VoteCancel:
1.733 {
1.734 // If we receive 0 votes, pick at random.
1.735 @@ -432,84 +757,37 @@
1.736 else
1.737 {
1.738 // We were actually cancelled. What should we do?
1.739 - }
1.740 - }
1.741 -
1.742 - case MenuAction_VoteEnd:
1.743 - {
1.744 - decl String:map[32];
1.745 - GetMenuItem(menu, param1, map, sizeof(map));
1.746 -
1.747 - if (strcmp(map, VOTE_EXTEND, false) == 0)
1.748 - {
1.749 - g_Extends++;
1.750 -
1.751 - new time;
1.752 - if (GetMapTimeLimit(time))
1.753 - {
1.754 - if (time > 0)
1.755 - {
1.756 - ExtendMapTimeLimit(GetConVarInt(g_Cvar_ExtendTimeStep)*60);
1.757 - }
1.758 - }
1.759 -
1.760 - if (g_Cvar_Winlimit != INVALID_HANDLE)
1.761 - {
1.762 - new winlimit = GetConVarInt(g_Cvar_Winlimit);
1.763 - if (winlimit)
1.764 - {
1.765 - SetConVarInt(g_Cvar_Winlimit, winlimit + GetConVarInt(g_Cvar_ExtendRoundStep));
1.766 - }
1.767 - }
1.768 -
1.769 - if (g_Cvar_Maxrounds != INVALID_HANDLE)
1.770 - {
1.771 - new maxrounds = GetConVarInt(g_Cvar_Maxrounds);
1.772 - if (maxrounds)
1.773 - {
1.774 - SetConVarInt(g_Cvar_Maxrounds, maxrounds + GetConVarInt(g_Cvar_ExtendRoundStep));
1.775 - }
1.776 - }
1.777 -
1.778 - if (g_Cvar_Fraglimit != INVALID_HANDLE)
1.779 - {
1.780 - new fraglimit = GetConVarInt(g_Cvar_Fraglimit);
1.781 - if (fraglimit)
1.782 - {
1.783 - SetConVarInt(g_Cvar_Fraglimit, fraglimit + GetConVarInt(g_Cvar_ExtendFragStep));
1.784 - }
1.785 - }
1.786 -
1.787 - PrintToChatAll("[SM] %t", "Current Map Extended");
1.788 - LogMessage("Voting for next map has finished. The current map has been extended.");
1.789 -
1.790 - // We extended, so we'll have to vote again.
1.791 g_HasVoteStarted = false;
1.792 CreateNextVote();
1.793 SetupTimeleftTimer();
1.794 }
1.795 - else
1.796 - {
1.797 - SetNextMap(map);
1.798 - }
1.799 }
1.800 }
1.801
1.802 return 0;
1.803 }
1.804
1.805 -SetNextMap(const String:map[])
1.806 +public Action:Timer_ChangeMap(Handle:hTimer, Handle:dp)
1.807 {
1.808 - SetConVarString(g_Cvar_Nextmap, map);
1.809 - PushArrayString(g_OldMapList, map);
1.810 -
1.811 - if (GetArraySize(g_OldMapList) > GetConVarInt(g_Cvar_ExcludeMaps))
1.812 + new String:map[65];
1.813 +
1.814 + if (dp == INVALID_HANDLE)
1.815 {
1.816 - RemoveFromArray(g_OldMapList, 0);
1.817 + if (!GetNextMap(map, sizeof(map)))
1.818 + {
1.819 + //No passed map and no set nextmap. fail!
1.820 + return Plugin_Stop;
1.821 + }
1.822 }
1.823 -
1.824 - PrintToChatAll("[SM] %t", "Nextmap Voting Finished", map);
1.825 - LogMessage("Voting for next map has finished. Nextmap: %s.", map);
1.826 + else
1.827 + {
1.828 + ResetPack(dp);
1.829 + ReadPackString(dp, map, sizeof(map));
1.830 + }
1.831 +
1.832 + ServerCommand("changelevel \"%s\"", map);
1.833 +
1.834 + return Plugin_Stop;
1.835 }
1.836
1.837 CreateNextVote()
1.838 @@ -552,4 +830,131 @@
1.839 }
1.840
1.841 CloseHandle(tempMaps);
1.842 +}
1.843 +
1.844 +bool:CanVoteStart()
1.845 +{
1.846 + if (g_WaitingForVote || g_HasVoteStarted)
1.847 + {
1.848 + return false;
1.849 + }
1.850 +
1.851 + return true;
1.852 +}
1.853 +
1.854 +NominateResult:InternalNominateMap(String:map[], bool:force, owner)
1.855 +{
1.856 + if (!IsMapValid(map))
1.857 + {
1.858 + return Nominate_InvalidMap;
1.859 + }
1.860 +
1.861 + new index;
1.862 +
1.863 + if ((index = FindValueInArray(g_NominateOwners, owner)) != -1)
1.864 + {
1.865 + new String:oldmap[33];
1.866 + GetArrayString(g_NominateList, index, oldmap, sizeof(oldmap));
1.867 + Call_StartForward(g_NominationsResetForward);
1.868 + Call_PushString(oldmap);
1.869 + Call_PushCell(owner);
1.870 + Call_Finish();
1.871 +
1.872 +
1.873 + SetArrayString(g_NominateList, index, map);
1.874 + return Nominate_Replaced;
1.875 + }
1.876 +
1.877 + /* Too many nominated maps. */
1.878 + if (g_NominateCount >= GetConVarInt(g_Cvar_IncludeMaps) && !force)
1.879 + {
1.880 + return Nominate_VoteFull;
1.881 + }
1.882 +
1.883 + /* Map already in the vote */
1.884 + if (FindStringInArray(g_NominateList, map) != -1)
1.885 + {
1.886 + return Nominate_AlreadyInVote;
1.887 + }
1.888 +
1.889 +
1.890 + PushArrayString(g_NominateList, map);
1.891 + PushArrayCell(g_NominateOwners, owner);
1.892 + g_NominateCount++;
1.893 +
1.894 + while (GetArraySize(g_NominateList) > GetConVarInt(g_Cvar_IncludeMaps))
1.895 + {
1.896 + new String:oldmap[33];
1.897 + GetArrayString(g_NominateList, 0, oldmap, sizeof(oldmap));
1.898 + Call_StartForward(g_NominationsResetForward);
1.899 + Call_PushString(oldmap);
1.900 + Call_PushCell(GetArrayCell(g_NominateOwners, 0));
1.901 + Call_Finish();
1.902 +
1.903 + RemoveFromArray(g_NominateList, 0);
1.904 + RemoveFromArray(g_NominateOwners, 0);
1.905 + }
1.906 +
1.907 + return Nominate_Added;
1.908 +}
1.909 +
1.910 +/* Add natives to allow nominate and initiate vote to be call */
1.911 +
1.912 +/* native bool:NominateMap(const String:map[], bool:force, &NominateError:error); */
1.913 +public Native_NominateMap(Handle:plugin, numParams)
1.914 +{
1.915 + new len;
1.916 + GetNativeStringLength(1, len);
1.917 +
1.918 + if (len <= 0)
1.919 + {
1.920 + return false;
1.921 + }
1.922 +
1.923 + new String:map[len+1];
1.924 + GetNativeString(1, map, len+1);
1.925 +
1.926 + return _:InternalNominateMap(map, GetNativeCell(2), GetNativeCell(3));
1.927 +}
1.928 +
1.929 +/* native InitiateMapChooserVote(); */
1.930 +public Native_InitiateVote(Handle:plugin, numParams)
1.931 +{
1.932 + new MapChange:when = MapChange:GetNativeCell(1);
1.933 + new Handle:inputarray = Handle:GetNativeCell(2);
1.934 +
1.935 + LogMessage("Starting map vote because outside request");
1.936 + InitiateVote(when, inputarray);
1.937 +}
1.938 +
1.939 +public Native_CanVoteStart(Handle:plugin, numParams)
1.940 +{
1.941 + return CanVoteStart();
1.942 +}
1.943 +
1.944 +public Native_CheckVoteDone(Handle:plugin, numParams)
1.945 +{
1.946 + return g_MapVoteCompleted;
1.947 +}
1.948 +
1.949 +public Native_GetExcludeMapList(Handle:plugin, numParams)
1.950 +{
1.951 + new Handle:array = Handle:GetNativeCell(1);
1.952 +
1.953 + if (array == INVALID_HANDLE)
1.954 + {
1.955 + return;
1.956 + }
1.957 +
1.958 +
1.959 + new size = GetArraySize(g_OldMapList);
1.960 + decl String:map[33];
1.961 +
1.962 + for (new i=0; i<size; i++)
1.963 + {
1.964 + GetArrayString(g_OldMapList, i, map, sizeof(map));
1.965 + PushArrayString(array, map);
1.966 + }
1.967 +
1.968 + return;
1.969 }
1.970 \ No newline at end of file