3473 lines
107 KiB
SourcePawn
Executable File
3473 lines
107 KiB
SourcePawn
Executable File
/**
|
|
* vim: set ts=4 :
|
|
* =============================================================================
|
|
* MapChooser Extended
|
|
* Creates a map vote at appropriate times, setting sm_nextmap to the winning
|
|
* vote. Includes extra options not present in the SourceMod MapChooser
|
|
*
|
|
* MapChooser Extended (C)2011-2013 Powerlord (Ross Bemrose)
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
* 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 <http://www.sourcemod.net/license.php>.
|
|
*
|
|
* Version: $Id$
|
|
*/
|
|
|
|
//#define DEBUG
|
|
|
|
#if defined DEBUG
|
|
#define assert(%1) if(!(%1)) ThrowError("Debug Assertion Failed");
|
|
#define assert_msg(%1,%2) if(!(%1)) ThrowError(%2);
|
|
#else
|
|
#define assert(%1)
|
|
#define assert_msg(%1,%2)
|
|
#endif
|
|
|
|
#include <sourcemod>
|
|
#include <mapchooser>
|
|
#include <mapchooser_extended>
|
|
#include <unloze_playtime>
|
|
#include <nextmap>
|
|
#include <sdktools>
|
|
#include <cstrike>
|
|
#include <multicolors>
|
|
#include <PlayerManager>
|
|
#include <nominations_extended>
|
|
#include <rockthevote_extended>
|
|
#include <AFKManager>
|
|
#include <leader>
|
|
|
|
#pragma semicolon 1
|
|
#pragma newdecls required
|
|
|
|
#define MCE_VERSION "1.3.1"
|
|
|
|
enum RoundCounting
|
|
{
|
|
RoundCounting_Standard = 0,
|
|
RoundCounting_MvM,
|
|
RoundCounting_ArmsRace,
|
|
}
|
|
|
|
// CSGO requires two cvars to get the game type
|
|
enum
|
|
{
|
|
GameType_Classic = 0,
|
|
GameType_GunGame = 1,
|
|
GameType_Training = 2,
|
|
GameType_Custom = 3,
|
|
}
|
|
|
|
enum
|
|
{
|
|
GunGameMode_ArmsRace = 0,
|
|
GunGameMode_Demolition = 1,
|
|
GunGameMode_DeathMatch = 2,
|
|
}
|
|
|
|
public Plugin myinfo =
|
|
{
|
|
name = "MapChooser Extended",
|
|
author = "Powerlord, Zuko, BotoX and AlliedModders LLC",
|
|
description = "Automated Map Voting with Extensions",
|
|
version = MCE_VERSION,
|
|
url = ""
|
|
};
|
|
|
|
/* Valve ConVars */
|
|
ConVar g_Cvar_Winlimit;
|
|
ConVar g_Cvar_Maxrounds;
|
|
ConVar g_Cvar_Fraglimit;
|
|
ConVar g_Cvar_Bonusroundtime;
|
|
ConVar g_Cvar_GameType;
|
|
ConVar g_Cvar_GameMode;
|
|
|
|
/* Plugin ConVars */
|
|
ConVar g_Cvar_StartTime;
|
|
ConVar g_Cvar_StartRounds;
|
|
ConVar g_Cvar_StartFrags;
|
|
ConVar g_Cvar_ExtendTimeStep;
|
|
ConVar g_Cvar_ExtendRoundStep;
|
|
ConVar g_Cvar_ExtendFragStep;
|
|
ConVar g_Cvar_ExcludeMaps;
|
|
ConVar g_Cvar_ExcludeMapsTime;
|
|
ConVar g_Cvar_IncludeMaps;
|
|
ConVar g_Cvar_IncludeMapsReserved;
|
|
ConVar g_Cvar_NoVoteMode;
|
|
ConVar g_Cvar_Extend;
|
|
ConVar g_Cvar_DontChange;
|
|
ConVar g_Cvar_EndOfMapVote;
|
|
ConVar g_Cvar_VoteDuration;
|
|
|
|
Handle g_VoteTimer = INVALID_HANDLE;
|
|
Handle g_RetryTimer = INVALID_HANDLE;
|
|
Handle g_WarningTimer = INVALID_HANDLE;
|
|
|
|
/* Data Handles */
|
|
Handle g_MapList = INVALID_HANDLE;
|
|
Handle g_NominateList[MAXPLAYERS + 1];
|
|
Handle g_NominateOwners = INVALID_HANDLE;
|
|
StringMap g_OldMapList;
|
|
StringMap g_TimeMapList;
|
|
Handle g_NextMapList = INVALID_HANDLE;
|
|
Handle g_VoteMenu = INVALID_HANDLE;
|
|
KeyValues g_Config;
|
|
|
|
int g_Extends;
|
|
int g_TotalRounds;
|
|
bool g_HasVoteStarted;
|
|
bool g_WaitingForVote;
|
|
bool g_MapVoteCompleted;
|
|
bool g_ChangeMapInProgress;
|
|
bool g_HasIntermissionStarted = false;
|
|
bool g_SaveCDOnMapEnd = true;
|
|
int g_iNextmapState = 0; //0 = nothing. 1 = rtv succesfull. 2 = admin used setnextmap. 3 = did not extend or used all extends. 4 = vote at end of map is going on.
|
|
int g_mapFileSerial = -1;
|
|
int g_iPlayerCount_excludeSpec;
|
|
int g_iMapsFromCasualPool;
|
|
int g_iPlayerAFKTime;
|
|
|
|
|
|
int g_NominateCount = 0;
|
|
int g_NominateReservedCount = 0;
|
|
int g_iInterval;
|
|
float player_mapvote_worth[MAXPLAYERS + 1];
|
|
char player_mapvote[MAXPLAYERS + 1][PLATFORM_MAX_PATH];
|
|
MapChange g_ChangeTime;
|
|
|
|
//check if autismbot
|
|
bool is_bot_player[MAXPLAYERS + 1];
|
|
|
|
Handle g_NominationsResetForward = INVALID_HANDLE;
|
|
Handle g_MapVoteStartedForward = INVALID_HANDLE;
|
|
|
|
/* Mapchooser Extended Plugin ConVars */
|
|
|
|
ConVar g_Cvar_RunOff;
|
|
ConVar g_Cvar_RunOffPercent;
|
|
ConVar g_Cvar_BlockSlots;
|
|
ConVar g_Cvar_MaxRunOffs;
|
|
ConVar g_Cvar_StartTimePercent;
|
|
ConVar g_Cvar_StartTimePercentEnable;
|
|
ConVar g_Cvar_WarningTime;
|
|
ConVar g_Cvar_RunOffWarningTime;
|
|
ConVar g_Cvar_TimerLocation;
|
|
ConVar g_Cvar_ExtendPosition;
|
|
ConVar g_Cvar_MarkCustomMaps;
|
|
ConVar g_Cvar_RandomizeNominations;
|
|
ConVar g_Cvar_HideTimer;
|
|
ConVar g_Cvar_NoVoteOption;
|
|
ConVar g_Cvar_ShufflePerClient;
|
|
ConVar g_Cvar_NoRestrictionTimeframeEnable;
|
|
ConVar g_Cvar_NoRestrictionTimeframeMinTime;
|
|
ConVar g_Cvar_NoRestrictionTimeframeMaxTime;
|
|
|
|
/* Mapchooser Extended Data Handles */
|
|
Handle g_OfficialList = INVALID_HANDLE;
|
|
|
|
/* Mapchooser Extended Forwards */
|
|
Handle g_MapVoteWarningStartForward = INVALID_HANDLE;
|
|
Handle g_MapVoteWarningTickForward = INVALID_HANDLE;
|
|
Handle g_MapVoteStartForward = INVALID_HANDLE;
|
|
Handle g_MapVoteEndForward = INVALID_HANDLE;
|
|
Handle g_MapVoteRunoffStartForward = INVALID_HANDLE;
|
|
|
|
/* Mapchooser Extended Globals */
|
|
int g_RunoffCount = 0;
|
|
int g_mapOfficialFileSerial = -1;
|
|
char g_GameModName[64];
|
|
bool g_WarningInProgress = false;
|
|
bool g_AddNoVote = false;
|
|
|
|
RoundCounting g_RoundCounting = RoundCounting_Standard;
|
|
|
|
/* Upper bound of how many team there could be */
|
|
#define MAXTEAMS 10
|
|
int g_winCount[MAXTEAMS];
|
|
|
|
bool g_BlockedSlots = false;
|
|
int g_ObjectiveEnt = -1;
|
|
|
|
enum TimerLocation
|
|
{
|
|
TimerLocation_Hint = 0,
|
|
TimerLocation_Center = 1,
|
|
TimerLocation_Chat = 2,
|
|
}
|
|
|
|
enum WarningType
|
|
{
|
|
WarningType_Vote,
|
|
WarningType_Revote,
|
|
}
|
|
|
|
#define VOTE_EXTEND "##extend##"
|
|
#define VOTE_DONTCHANGE "##dontchange##"
|
|
|
|
/* Mapchooser Extended Defines */
|
|
#define LINE_ONE "##lineone##"
|
|
#define LINE_TWO "##linetwo##"
|
|
#define LINE_SPACER "##linespacer##"
|
|
#define FAILURE_TIMER_LENGTH 5
|
|
|
|
//call forward to reset all nominations
|
|
public void OnPluginEnd()
|
|
{
|
|
for (int i = 0; i < MaxClients; i++)
|
|
{
|
|
int index = FindValueInArray(g_NominateOwners, i);
|
|
if (index == -1) continue;
|
|
for (int j = 0; j < GetArraySize(g_NominateList[i]); j++)
|
|
{
|
|
char oldmap[PLATFORM_MAX_PATH];
|
|
Call_StartForward(g_NominationsResetForward);
|
|
GetArrayString(g_NominateList[i], j, oldmap, PLATFORM_MAX_PATH);
|
|
Call_PushString(oldmap);
|
|
Call_PushCell(i);
|
|
Call_Finish();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnPluginStart()
|
|
{
|
|
LoadTranslations("mapchooser_extended.phrases");
|
|
LoadTranslations("basevotes.phrases");
|
|
LoadTranslations("common.phrases");
|
|
|
|
int arraySize = ByteCountToCells(PLATFORM_MAX_PATH);
|
|
g_MapList = CreateArray(arraySize);
|
|
g_NominateOwners = CreateArray(1);
|
|
g_OldMapList = new StringMap();
|
|
g_TimeMapList = new StringMap();
|
|
g_NextMapList = CreateArray(arraySize);
|
|
g_OfficialList = CreateArray(arraySize);
|
|
|
|
for (int i = 0; i < MaxClients; i++)
|
|
{
|
|
g_NominateList[i] = CreateArray(arraySize);
|
|
if (IsValidClient(i))
|
|
{
|
|
OnClientPostAdminCheck(i);
|
|
}
|
|
}
|
|
|
|
GetGameFolderName(g_GameModName, sizeof(g_GameModName));
|
|
|
|
g_Cvar_EndOfMapVote = CreateConVar("mce_endvote", "1", "Specifies if MapChooser should run an end of map vote", _, true, 0.0, true, 1.0);
|
|
|
|
g_Cvar_StartTime = CreateConVar("mce_starttime", "10.0", "Specifies when to start the vote based on time remaining.", _, true, 1.0);
|
|
g_Cvar_StartRounds = CreateConVar("mce_startround", "2.0", "Specifies when to start the vote based on rounds remaining. Use 0 on DoD:S, CS:S, and TF2 to start vote during bonus round time", _, true, 0.0);
|
|
g_Cvar_StartFrags = CreateConVar("mce_startfrags", "5.0", "Specifies when to start the vote base on frags remaining.", _, true, 1.0);
|
|
g_Cvar_ExtendTimeStep = CreateConVar("mce_extend_timestep", "15", "Specifies how much many more minutes each extension makes", _, true, 5.0);
|
|
g_Cvar_ExtendRoundStep = CreateConVar("mce_extend_roundstep", "5", "Specifies how many more rounds each extension makes", _, true, 1.0);
|
|
g_Cvar_ExtendFragStep = CreateConVar("mce_extend_fragstep", "10", "Specifies how many more frags are allowed when map is extended.", _, true, 5.0);
|
|
g_Cvar_ExcludeMaps = CreateConVar("mce_exclude", "5", "Specifies how many past maps to exclude from the vote.", _, true, 0.0);
|
|
g_Cvar_ExcludeMapsTime = CreateConVar("mce_exclude_time", "5h", "Specifies how long in minutes an old map is excluded from the vote.");
|
|
g_Cvar_IncludeMaps = CreateConVar("mce_include", "5", "Specifies how many maps to include in the vote.", _, true, 2.0, true, 9.0);
|
|
g_Cvar_IncludeMapsReserved = CreateConVar("mce_include_reserved", "2", "Specifies how many private/random maps to include in the vote.", _, true, 0.0, true, 5.0);
|
|
g_Cvar_NoVoteMode = CreateConVar("mce_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("mce_extend", "0", "Number of extensions allowed each map.", _, true, 0.0);
|
|
g_Cvar_DontChange = CreateConVar("mce_dontchange", "1", "Specifies if a 'Don't Change option should be added to early votes", _, true, 0.0);
|
|
g_Cvar_VoteDuration = CreateConVar("mce_voteduration", "20", "Specifies how long the mapvote should be available for.", _, true, 5.0);
|
|
|
|
|
|
ConVar cvar2;
|
|
HookConVarChange((cvar2 = CreateConVar("mce_randomized_maps_from_casual_pool", "2", "f we have space left over for randomized maps at the mapvote. how many should be prioritized from the casual pool?")), Cvar_mapsFromCasualPool);
|
|
g_iMapsFromCasualPool = cvar2.IntValue;
|
|
delete cvar2;
|
|
|
|
ConVar cvar1;
|
|
HookConVarChange((cvar1 = CreateConVar("sm_active_players_required", "15", "Amount of players required to be considered active before any restrictions are enabled at all.")), Cvar_playerExcludeSpec);
|
|
g_iPlayerCount_excludeSpec = cvar1.IntValue;
|
|
delete cvar1;
|
|
|
|
ConVar cvar;
|
|
HookConVarChange((cvar = CreateConVar("sm_mapchooser_afk_time", "120", "Time in seconds until a player is considered as AFK and therefore excluded from player average")), Cvar_playerAFKTime);
|
|
g_iPlayerAFKTime = cvar.IntValue;
|
|
delete cvar;
|
|
|
|
// MapChooser Extended cvars
|
|
CreateConVar("mce_version", MCE_VERSION, "MapChooser Extended Version", FCVAR_SPONLY|FCVAR_NOTIFY|FCVAR_DONTRECORD);
|
|
|
|
g_Cvar_RunOff = CreateConVar("mce_runoff", "1", "Hold run off votes if winning choice has less than a certain percentage of votes", _, true, 0.0, true, 1.0);
|
|
g_Cvar_RunOffPercent = CreateConVar("mce_runoffpercent", "50", "If winning choice has less than this percent of votes, hold a runoff", _, true, 0.0, true, 100.0);
|
|
g_Cvar_BlockSlots = CreateConVar("mce_blockslots", "0", "Block slots to prevent accidental votes. Only applies when Voice Command style menus are in use.", _, true, 0.0, true, 1.0);
|
|
//g_Cvar_BlockSlotsCount = CreateConVar("mce_blockslots_count", "2", "Number of slots to block.", _, true, 1.0, true, 3.0);
|
|
g_Cvar_MaxRunOffs = CreateConVar("mce_maxrunoffs", "1", "Number of run off votes allowed each map.", _, true, 0.0);
|
|
g_Cvar_StartTimePercent = CreateConVar("mce_start_percent", "35.0", "Specifies when to start the vote based on percents.", _, true, 0.0, true, 100.0);
|
|
g_Cvar_StartTimePercentEnable = CreateConVar("mce_start_percent_enable", "0", "Enable or Disable percentage calculations when to start vote.", _, true, 0.0, true, 1.0);
|
|
g_Cvar_WarningTime = CreateConVar("mce_warningtime", "15.0", "Warning time in seconds.", _, true, 0.0, true, 60.0);
|
|
g_Cvar_RunOffWarningTime = CreateConVar("mce_runoffvotewarningtime", "5.0", "Warning time for runoff vote in seconds.", _, true, 0.0, true, 30.0);
|
|
g_Cvar_TimerLocation = CreateConVar("mce_warningtimerlocation", "0", "Location for the warning timer text. 0 is HintBox, 1 is Center text, 2 is Chat. Defaults to HintBox.", _, true, 0.0, true, 2.0);
|
|
g_Cvar_MarkCustomMaps = CreateConVar("mce_markcustommaps", "1", "Mark custom maps in the vote list. 0 = Disabled, 1 = Mark with *, 2 = Mark with phrase.", _, true, 0.0, true, 2.0);
|
|
g_Cvar_ExtendPosition = CreateConVar("mce_extendposition", "0", "Position of Extend/Dont Change options. 0 = at end, 1 = at start.", _, true, 0.0, true, 1.0);
|
|
g_Cvar_RandomizeNominations = CreateConVar("mce_randomizeorder", "0", "Randomize map order?", _, true, 0.0, true, 1.0);
|
|
g_Cvar_HideTimer = CreateConVar("mce_hidetimer", "0", "Hide the MapChooser Extended warning timer", _, true, 0.0, true, 1.0);
|
|
g_Cvar_NoVoteOption = CreateConVar("mce_addnovote", "1", "Add \"No Vote\" to vote menu?", _, true, 0.0, true, 1.0);
|
|
g_Cvar_ShufflePerClient = CreateConVar("mce_shuffle_per_client", "1", "Random shuffle map vote menu per client?", _, true, 0.0, true, 1.0);
|
|
g_Cvar_NoRestrictionTimeframeEnable = CreateConVar("mce_no_restriction_timeframe_enable", "1", "Enable timeframe where all nomination restrictions and cooldowns are disabled?", _, true, 0.0, true, 1.0);
|
|
g_Cvar_NoRestrictionTimeframeMinTime = CreateConVar("mce_no_restriction_timeframe_mintime", "0100", "Start of the timeframe where all nomination restrictions and cooldowns are disabled (Format: HHMM)", _, true, 0000.0, true, 2359.0);
|
|
g_Cvar_NoRestrictionTimeframeMaxTime = CreateConVar("mce_no_restriction_timeframe_maxtime", "0700", "End of the timeframe where all nomination restrictions and cooldowns are disabled (Format: HHMM)", _, true, 0000.0, true, 2359.0);
|
|
|
|
RegAdminCmd("sm_mapvote", Command_Mapvote, ADMFLAG_CHANGEMAP, "sm_mapvote - Forces MapChooser to attempt to run a map vote now.");
|
|
RegAdminCmd("sm_setnextmap", Command_SetNextmap, ADMFLAG_CHANGEMAP, "sm_setnextmap <map>");
|
|
|
|
// Mapchooser Extended Commands
|
|
RegAdminCmd("mce_reload_maplist", Command_ReloadMaps, ADMFLAG_CHANGEMAP, "mce_reload_maplist - Reload the Official Maplist file.");
|
|
|
|
RegConsoleCmd("sm_extends", Command_ExtendsLeft, "sm_extends - Shows how many extends are left on the current map.");
|
|
RegConsoleCmd("sm_extendsleft", Command_ExtendsLeft, "sm_extendsleft - Shows how many extends are left on the current map.");
|
|
RegConsoleCmd("sm_houravg", Command_hours_average, "Prints in the chat what the current hour average of each player accumulated is.");
|
|
RegConsoleCmd("sm_avghour", Command_hours_average, "Prints in the chat what the current hour average of each player accumulated is.");
|
|
|
|
g_Cvar_Winlimit = FindConVar("mp_winlimit");
|
|
g_Cvar_Maxrounds = FindConVar("mp_maxrounds");
|
|
g_Cvar_Fraglimit = FindConVar("mp_fraglimit");
|
|
|
|
EngineVersion version = GetEngineVersion();
|
|
|
|
static char mapListPath[PLATFORM_MAX_PATH];
|
|
|
|
BuildPath(Path_SM, mapListPath, PLATFORM_MAX_PATH, "configs/mapchooser_extended/maps/%s.txt", g_GameModName);
|
|
SetMapListCompatBind("official", mapListPath);
|
|
|
|
switch(version)
|
|
{
|
|
case Engine_TF2:
|
|
{
|
|
g_Cvar_Bonusroundtime = FindConVar("mp_bonusroundtime");
|
|
}
|
|
|
|
case Engine_CSGO:
|
|
{
|
|
g_Cvar_GameType = FindConVar("game_type");
|
|
g_Cvar_GameMode = FindConVar("game_mode");
|
|
g_Cvar_Bonusroundtime = FindConVar("mp_round_restart_delay");
|
|
}
|
|
|
|
case Engine_DODS:
|
|
{
|
|
g_Cvar_Bonusroundtime = FindConVar("dod_bonusroundtime");
|
|
}
|
|
|
|
case Engine_CSS:
|
|
{
|
|
g_Cvar_Bonusroundtime = FindConVar("mp_round_restart_delay");
|
|
}
|
|
|
|
default:
|
|
{
|
|
g_Cvar_Bonusroundtime = FindConVar("mp_bonusroundtime");
|
|
}
|
|
}
|
|
|
|
switch(version)
|
|
{
|
|
case Engine_TF2:
|
|
{
|
|
HookEvent("teamplay_restart_round", Event_TFRestartRound);
|
|
HookEvent("pve_win_panel", Event_MvMWinPanel);
|
|
}
|
|
|
|
case Engine_NuclearDawn:
|
|
{
|
|
HookEvent("round_win", Event_RoundEnd);
|
|
}
|
|
|
|
case Engine_CSGO:
|
|
{
|
|
HookEvent("round_end", Event_RoundEnd);
|
|
HookEvent("cs_intermission", Event_Intermission);
|
|
HookEvent("announce_phase_end", Event_PhaseEnd);
|
|
}
|
|
|
|
case Engine_DODS:
|
|
{
|
|
HookEvent("dod_round_win", Event_RoundEnd);
|
|
}
|
|
|
|
default:
|
|
{
|
|
HookEvent("round_end", Event_RoundEnd);
|
|
}
|
|
}
|
|
|
|
HookEvent("player_death", Event_PlayerDeath);
|
|
|
|
AutoExecConfig(true, "mapchooser_extended");
|
|
|
|
//Change the mp_bonusroundtime max so that we have time to display the vote
|
|
//If you display a vote during bonus time good defaults are 17 vote duration and 19 mp_bonustime
|
|
SetConVarBounds(g_Cvar_Bonusroundtime, ConVarBound_Upper, true, 30.0);
|
|
|
|
g_NominationsResetForward = CreateGlobalForward("OnNominationRemoved", ET_Ignore, Param_String, Param_Cell);
|
|
g_MapVoteStartedForward = CreateGlobalForward("OnMapVoteStarted", ET_Ignore);
|
|
|
|
//MapChooser Extended Forwards
|
|
g_MapVoteStartForward = CreateGlobalForward("OnMapVoteStart", ET_Ignore); // Deprecated
|
|
g_MapVoteEndForward = CreateGlobalForward("OnMapVoteEnd", ET_Ignore, Param_String);
|
|
g_MapVoteWarningStartForward = CreateGlobalForward("OnMapVoteWarningStart", ET_Ignore);
|
|
g_MapVoteWarningTickForward = CreateGlobalForward("OnMapVoteWarningTick", ET_Ignore, Param_Cell);
|
|
g_MapVoteRunoffStartForward = CreateGlobalForward("OnMapVoteRunnoffWarningStart", ET_Ignore);
|
|
|
|
|
|
InternalRestoreMapCooldowns();
|
|
|
|
|
|
int total_time = (GetConVarInt(g_Cvar_VoteDuration) * 2) + GetConVarInt(g_Cvar_RunOffWarningTime) + 10;
|
|
ServerCommand("sm_cvar mp_chattime %i", total_time); //prevents map switching supposedly.
|
|
}
|
|
|
|
public void Cvar_mapsFromCasualPool(ConVar convar, const char[] oldValue, const char[] newValue)
|
|
{
|
|
g_iMapsFromCasualPool = convar.IntValue;
|
|
}
|
|
|
|
public void Cvar_playerExcludeSpec(ConVar convar, const char[] oldValue, const char[] newValue)
|
|
{
|
|
g_iPlayerCount_excludeSpec = convar.IntValue;
|
|
}
|
|
|
|
public void Cvar_playerAFKTime(ConVar convar, const char[] oldValue, const char[] newValue)
|
|
{
|
|
g_iPlayerAFKTime = convar.IntValue;
|
|
}
|
|
|
|
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
|
|
{
|
|
if(LibraryExists("mapchooser"))
|
|
{
|
|
strcopy(error, err_max, "MapChooser already loaded, aborting.");
|
|
return APLRes_Failure;
|
|
}
|
|
|
|
RegPluginLibrary("mapchooser");
|
|
|
|
MarkNativeAsOptional("GetEngineVersion");
|
|
|
|
CreateNative("NominateMap", Native_NominateMap);
|
|
CreateNative("RemoveNominationByMap", Native_RemoveNominationByMap);
|
|
CreateNative("RemoveNominationByOwner", Native_RemoveNominationByOwner);
|
|
CreateNative("InitiateMapChooserVote", Native_InitiateVote);
|
|
CreateNative("CanMapChooserStartVote", Native_CanVoteStart);
|
|
CreateNative("HasEndOfMapVoteFinished", Native_CheckVoteDone);
|
|
CreateNative("GetExcludeMapList", Native_GetExcludeMapList);
|
|
CreateNative("GetNominatedMapList", Native_GetNominatedMapList);
|
|
CreateNative("EndOfMapVoteEnabled", Native_EndOfMapVoteEnabled);
|
|
|
|
// MapChooser Extended natives
|
|
CreateNative("IsMapOfficial", Native_IsMapOfficial);
|
|
CreateNative("CanNominate", Native_CanNominate);
|
|
CreateNative("ExcludeMap", Native_ExcludeMap);
|
|
CreateNative("ExcludeMapTime", Native_ExcludeMapTime);
|
|
CreateNative("GetMapCooldown", Native_GetMapCooldown);
|
|
CreateNative("GetMapCooldownTime", Native_GetMapCooldownTime);
|
|
CreateNative("GetMapMinTime", Native_GetMapMinTime);
|
|
CreateNative("GetMapMaxTime", Native_GetMapMaxTime);
|
|
CreateNative("GetMapMinPlayers", Native_GetMapMinPlayers);
|
|
CreateNative("GetMapMaxPlayers", Native_GetMapMaxPlayers);
|
|
CreateNative("GetMapTimeRestriction", Native_GetMapTimeRestriction);
|
|
CreateNative("GetMapPlayerRestriction", Native_GetMapPlayerRestriction);
|
|
CreateNative("GetMapGroups", Native_GetMapGroups);
|
|
CreateNative("GetMapGroupRestriction", Native_GetMapGroupRestriction);
|
|
CreateNative("GetMapVIPRestriction", Native_GetMapVIPRestriction);
|
|
CreateNative("GetExtendsLeft", Native_GetExtendsLeft);
|
|
CreateNative("AreRestrictionsActive", Native_AreRestrictionsActive);
|
|
CreateNative("SimulateMapEnd", Native_SimulateMapEnd);
|
|
CreateNative("GetAveragePlayerTimeOnServerMapRestriction", Native_GetAveragePlayerTimeOnServerMapRestriction);
|
|
|
|
return APLRes_Success;
|
|
}
|
|
|
|
public Action GetInternalGetCvars(Handle timer)
|
|
{
|
|
InternalGetCvars(); //reading some cvars from the mapchooser_extended.cfg instead of server.cfg so people can view the values on
|
|
// https://unloze.com/mapchooser_css_ze/mapchooser_extended.cfg
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
public void OnMapStart()
|
|
{
|
|
g_iNextmapState = 0;
|
|
CreateTimer(1.0, GetInternalGetCvars);
|
|
|
|
int total_time = (GetConVarInt(g_Cvar_VoteDuration) * 2) + GetConVarInt(g_Cvar_RunOffWarningTime) + 10;
|
|
ServerCommand("sm_cvar mp_chattime %i", total_time); //prevents map switching supposedly.
|
|
ServerCommand("sm_cvar sm_vote_progress_hintbox 1"); //yeah its cheesy but does the job.
|
|
g_iInterval = 0;
|
|
static char folder[64];
|
|
GetGameFolderName(folder, sizeof(folder));
|
|
|
|
g_RoundCounting = RoundCounting_Standard;
|
|
g_ObjectiveEnt = -1;
|
|
|
|
if(strcmp(folder, "tf") == 0 && GameRules_GetProp("m_bPlayingMannVsMachine"))
|
|
{
|
|
g_RoundCounting = RoundCounting_MvM;
|
|
g_ObjectiveEnt = EntIndexToEntRef(FindEntityByClassname(-1, "tf_objective_resource"));
|
|
}
|
|
else if(strcmp(folder, "csgo") == 0 && GetConVarInt(g_Cvar_GameType) == GameType_GunGame &&
|
|
GetConVarInt(g_Cvar_GameMode) == GunGameMode_ArmsRace)
|
|
{
|
|
g_RoundCounting = RoundCounting_ArmsRace;
|
|
}
|
|
|
|
if(g_Config)
|
|
delete g_Config;
|
|
|
|
char sConfigFile[PLATFORM_MAX_PATH];
|
|
BuildPath(Path_SM, sConfigFile, sizeof(sConfigFile), "configs/mapchooser_extended.cfg");
|
|
if(!FileExists(sConfigFile))
|
|
{
|
|
LogMessage("Could not find config: \"%s\"", sConfigFile);
|
|
return;
|
|
}
|
|
LogMessage("Found config: \"%s\"", sConfigFile);
|
|
|
|
g_Config = new KeyValues("mapchooser_extended");
|
|
if(!g_Config.ImportFromFile(sConfigFile))
|
|
{
|
|
delete g_Config;
|
|
LogMessage("ImportFromFile() failed!");
|
|
return;
|
|
}
|
|
g_Config.Rewind();
|
|
//this does not detect obvioulsy when there is more or less than 15 players active because its the mapstart
|
|
// nobody has connected yet. OnMapEnd also does not work for checking this. so no point setting it to false here.
|
|
if(InternalAreRestrictionsActive(true))
|
|
g_SaveCDOnMapEnd = true;
|
|
else
|
|
g_SaveCDOnMapEnd = false;
|
|
}
|
|
|
|
public void OnConfigsExecuted()
|
|
{
|
|
if(ReadMapList(g_MapList,
|
|
g_mapFileSerial,
|
|
"mapchooser",
|
|
MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_MAPSFOLDER)
|
|
!= INVALID_HANDLE)
|
|
|
|
{
|
|
if(g_mapFileSerial == -1)
|
|
LogError("Unable to create a valid map list.");
|
|
}
|
|
|
|
|
|
SetupTimeleftTimer();
|
|
|
|
g_TotalRounds = 0;
|
|
|
|
g_Extends = 0;
|
|
|
|
g_MapVoteCompleted = false;
|
|
|
|
g_NominateCount = 0;
|
|
g_NominateReservedCount = 0;
|
|
for (int i = 0; i < MaxClients; i++)
|
|
{
|
|
if (g_NominateList[i] != INVALID_HANDLE)
|
|
{
|
|
ClearArray(g_NominateList[i]);
|
|
}
|
|
}
|
|
|
|
ClearArray(g_NominateOwners);
|
|
|
|
for(int i = 0; i < MAXTEAMS; i++)
|
|
g_winCount[i] = 0;
|
|
|
|
/* Check if mapchooser will attempt to start mapvote during bonus round time */
|
|
if (!GetConVarInt(g_Cvar_StartRounds))
|
|
{
|
|
if(!GetConVarInt(g_Cvar_StartTime) && GetConVarFloat(g_Cvar_Bonusroundtime) <= GetConVarFloat(g_Cvar_VoteDuration))
|
|
LogError("Warning - Bonus Round Time shorter than Vote Time. Votes during bonus round may not have time to complete");
|
|
}
|
|
|
|
InitializeOfficialMapList();
|
|
}
|
|
|
|
public void OnMapEnd()
|
|
{
|
|
g_iNextmapState = 0;
|
|
int total_time = (GetConVarInt(g_Cvar_VoteDuration) * 2) + GetConVarInt(g_Cvar_RunOffWarningTime) + 10;
|
|
ServerCommand("sm_cvar mp_chattime %i", total_time);
|
|
|
|
ServerCommand("sm_cvar sm_vote_progress_hintbox 1"); //yeah its cheesy but does the job.
|
|
g_iInterval = 0;
|
|
g_HasVoteStarted = false;
|
|
g_WaitingForVote = false;
|
|
g_ChangeMapInProgress = false;
|
|
g_HasIntermissionStarted = false;
|
|
|
|
g_VoteTimer = INVALID_HANDLE;
|
|
g_RetryTimer = INVALID_HANDLE;
|
|
g_WarningTimer = INVALID_HANDLE;
|
|
g_RunoffCount = 0;
|
|
|
|
|
|
static char map[PLATFORM_MAX_PATH];
|
|
int Cooldown;
|
|
|
|
if(g_SaveCDOnMapEnd)
|
|
{
|
|
GetCurrentMap(map, PLATFORM_MAX_PATH);
|
|
Cooldown = InternalGetMapCooldown(map);
|
|
g_OldMapList.SetValue(map, Cooldown, true);
|
|
|
|
Cooldown = GetTime() + InternalGetMapCooldownTime(map) - RoundToFloor(GetGameTime());
|
|
g_TimeMapList.SetValue(map, Cooldown, true);
|
|
}
|
|
|
|
StringMapSnapshot OldMapListSnapshot = g_OldMapList.Snapshot();
|
|
for(int i = 0; i < OldMapListSnapshot.Length; i++)
|
|
{
|
|
OldMapListSnapshot.GetKey(i, map, sizeof(map));
|
|
g_OldMapList.GetValue(map, Cooldown);
|
|
|
|
Cooldown--;
|
|
if(Cooldown > 0)
|
|
g_OldMapList.SetValue(map, Cooldown, true);
|
|
else
|
|
g_OldMapList.Remove(map);
|
|
}
|
|
delete OldMapListSnapshot;
|
|
|
|
StringMapSnapshot TimeMapListSnapshot = g_TimeMapList.Snapshot();
|
|
for(int i = 0; i < TimeMapListSnapshot.Length; i++)
|
|
{
|
|
TimeMapListSnapshot.GetKey(i, map, sizeof(map));
|
|
g_TimeMapList.GetValue(map, Cooldown);
|
|
|
|
if(Cooldown < GetTime())
|
|
g_TimeMapList.Remove(map);
|
|
}
|
|
delete OldMapListSnapshot;
|
|
|
|
InternalStoreMapCooldowns();
|
|
}
|
|
|
|
public void OnClientPutInServer(int client)
|
|
{
|
|
if (g_NominateList[client] != INVALID_HANDLE)
|
|
{
|
|
ClearArray(g_NominateList[client]);
|
|
}
|
|
}
|
|
|
|
public void OnClientDisconnect(int client)
|
|
{
|
|
is_bot_player[client] = false;
|
|
player_mapvote_worth[client] = 0.0;
|
|
Format(player_mapvote[client], 128, "");
|
|
int index = FindValueInArray(g_NominateOwners, client);
|
|
|
|
if(index == -1)
|
|
return;
|
|
|
|
//2023 edit for handling multiple nominations -jenz
|
|
for (int i = 0; i < GetArraySize(g_NominateList[client]); i++)
|
|
{
|
|
Call_StartForward(g_NominationsResetForward);
|
|
char oldmap[PLATFORM_MAX_PATH];
|
|
GetArrayString(g_NominateList[client], i, oldmap, PLATFORM_MAX_PATH);
|
|
Call_PushString(oldmap);
|
|
Call_PushCell(client);
|
|
Call_Finish();
|
|
}
|
|
|
|
RemoveFromArray(g_NominateOwners, index);
|
|
for (int i = 0; i < GetArraySize(g_NominateList[client]); i++)
|
|
{
|
|
RemoveFromArray(g_NominateList[client], i);
|
|
}
|
|
|
|
ClearArray(g_NominateList[client]);
|
|
g_NominateCount--;
|
|
}
|
|
|
|
public Action Command_SetNextmap(int client, int args)
|
|
{
|
|
if(args < 1)
|
|
{
|
|
CReplyToCommand(client, "[MCE] Usage: sm_setnextmap <map>");
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
static char map[PLATFORM_MAX_PATH];
|
|
GetCmdArg(1, map, PLATFORM_MAX_PATH);
|
|
|
|
if(!IsMapValid(map))
|
|
{
|
|
CReplyToCommand(client, "[MCE] %t", "Map was not found", map);
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
ShowActivity(client, "%t", "Changed Next Map", map);
|
|
LogAction(client, -1, "\"%L\" changed nextmap to \"%s\"", client, map);
|
|
|
|
SetNextMap(map);
|
|
g_iNextmapState = 2; //admin set the next map
|
|
g_MapVoteCompleted = true;
|
|
//checking on MapStart and MapEnd is not good enough. Players are not considered alive and on teams at those points in time.
|
|
//therefore instead applying the bool here after the mapvote completed.
|
|
if(InternalAreRestrictionsActive(false))
|
|
g_SaveCDOnMapEnd = true;
|
|
else
|
|
g_SaveCDOnMapEnd = false;
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
public Action Command_ReloadMaps(int client, int args)
|
|
{
|
|
InitializeOfficialMapList();
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
public Action Command_hours_average(int client, int args)
|
|
{
|
|
float total_votes = 0.0;
|
|
for (int i = 0; i < MaxClients; i++)
|
|
{
|
|
if (IsValidClient(i) && !is_bot_player[i] && PM_IsPlayerSteam(i))
|
|
{
|
|
GetPlayerWorthRTV_(i);
|
|
float nominate_worth = GetPlayerWorthRTV_boost_(i);
|
|
if (nominate_worth < 1.0)
|
|
{
|
|
nominate_worth = 1.0;
|
|
}
|
|
total_votes += nominate_worth;
|
|
}
|
|
}
|
|
float nominate_worth = GetPlayerWorthRTV_boost_(client);
|
|
if (nominate_worth < 1.0)
|
|
{
|
|
nominate_worth = 1.0;
|
|
}
|
|
|
|
CReplyToCommand(client, "Average hour count for nominations is: %i \nAverage hour count for mapvote and rtv boost is: %i \nYour mapvote and rtv boost is %0.1f \n(%i%% mapvote) (%i%% rtv)",
|
|
GetAveragePlayerTimeOnServer(),
|
|
GetAveragePlayerTimeOnServerRTV(),
|
|
nominate_worth,
|
|
RoundToFloor((nominate_worth/total_votes) * 100),
|
|
GetRtvPercentage(client));
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
public Action Command_ExtendsLeft(int client, int args)
|
|
{
|
|
CReplyToCommand(client, "[MCE] Available Extends: %d", GetConVarInt(g_Cvar_Extend) - g_Extends);
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
public void OnMapTimeLeftChanged()
|
|
{
|
|
if(GetArraySize(g_MapList))
|
|
{
|
|
SetupTimeleftTimer();
|
|
}
|
|
}
|
|
|
|
void SetupTimeleftTimer()
|
|
{
|
|
if (g_iNextmapState != 0)
|
|
{
|
|
if (g_VoteTimer != INVALID_HANDLE)
|
|
{
|
|
KillTimer(g_VoteTimer);
|
|
g_VoteTimer = INVALID_HANDLE;
|
|
}
|
|
return; //all extends were used or map was not extended or rtv was succesfull or admin set nextmap
|
|
}
|
|
|
|
int time;
|
|
if(GetMapTimeLeft(time) && time > 0)
|
|
{
|
|
int startTime;
|
|
if(GetConVarBool(g_Cvar_StartTimePercentEnable))
|
|
{
|
|
int timeLimit;
|
|
if(GetMapTimeLimit(timeLimit) && timeLimit > 0)
|
|
{
|
|
startTime = GetConVarInt(g_Cvar_StartTimePercent) * (timeLimit * 60) / 100;
|
|
}
|
|
}
|
|
else
|
|
startTime = GetConVarInt(g_Cvar_StartTime) * 60;
|
|
|
|
if(time - startTime < 0 && GetConVarBool(g_Cvar_EndOfMapVote) && !g_MapVoteCompleted && !g_HasVoteStarted && !g_WaitingForVote)
|
|
{
|
|
SetupWarningTimer(WarningType_Vote);
|
|
//LogMessage("SetupWarningTimer 1");
|
|
//PrintToChatAll("SetupWarningTimer 1");
|
|
}
|
|
else
|
|
{
|
|
if(g_WarningTimer == INVALID_HANDLE)
|
|
{
|
|
if(g_VoteTimer != INVALID_HANDLE)
|
|
{
|
|
KillTimer(g_VoteTimer);
|
|
g_VoteTimer = INVALID_HANDLE;
|
|
}
|
|
|
|
g_VoteTimer = CreateTimer(float(time - startTime), Timer_StartWarningTimer, _, TIMER_FLAG_NO_MAPCHANGE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public Action Timer_StartWarningTimer(Handle timer)
|
|
{
|
|
g_VoteTimer = INVALID_HANDLE;
|
|
|
|
if (!g_WarningInProgress || g_WarningTimer == INVALID_HANDLE)
|
|
{
|
|
SetupWarningTimer(WarningType_Vote);
|
|
//LogMessage("SetupWarningTimer 2");
|
|
//PrintToChatAll("SetupWarningTimer 2");
|
|
}
|
|
return Plugin_Stop;
|
|
}
|
|
|
|
public Action Timer_StartMapVote(Handle timer, Handle data)
|
|
{
|
|
static int timePassed;
|
|
|
|
// This is still necessary because InitiateVote still calls this directly via the retry timer
|
|
if(!GetArraySize(g_MapList) || !GetConVarBool(g_Cvar_EndOfMapVote) || g_MapVoteCompleted || g_HasVoteStarted)
|
|
{
|
|
g_WarningTimer = INVALID_HANDLE;
|
|
//LogMessage("Mapchooser_extended_avg Timer_Start MapVote Plugin_Stop");
|
|
return Plugin_Stop;
|
|
}
|
|
|
|
ResetPack(data);
|
|
int warningMaxTime = ReadPackCell(data);
|
|
int warningTimeRemaining = warningMaxTime - timePassed;
|
|
|
|
char warningPhrase[32];
|
|
ReadPackString(data, warningPhrase, sizeof(warningPhrase));
|
|
|
|
// Tick timer for external plugins
|
|
Call_StartForward(g_MapVoteWarningTickForward);
|
|
Call_PushCell(warningTimeRemaining);
|
|
Call_Finish();
|
|
|
|
if(timePassed == 0 || !GetConVarBool(g_Cvar_HideTimer))
|
|
{
|
|
TimerLocation timerLocation = view_as<TimerLocation>(GetConVarInt(g_Cvar_TimerLocation));
|
|
|
|
switch(timerLocation)
|
|
{
|
|
case TimerLocation_Center:
|
|
{
|
|
PrintCenterTextAll("%t", warningPhrase, warningTimeRemaining);
|
|
}
|
|
|
|
case TimerLocation_Chat:
|
|
{
|
|
PrintToChatAll("%t", warningPhrase, warningTimeRemaining);
|
|
}
|
|
|
|
default:
|
|
{
|
|
PrintHintTextToAll("%t", warningPhrase, warningTimeRemaining);
|
|
}
|
|
}
|
|
}
|
|
|
|
//LogMessage("timePassed: %i \n warningMaxTime: %i", timePassed, warningMaxTime);
|
|
if(timePassed++ >= warningMaxTime)
|
|
{
|
|
if(timer == g_RetryTimer)
|
|
{
|
|
g_WaitingForVote = false;
|
|
g_RetryTimer = INVALID_HANDLE;
|
|
}
|
|
else
|
|
g_WarningTimer = INVALID_HANDLE;
|
|
|
|
timePassed = 0;
|
|
MapChange mapChange = view_as<MapChange>(ReadPackCell(data));
|
|
Handle hndl = view_as<Handle>(ReadPackCell(data));
|
|
//LogMessage("Mapchooser_extended_avg calling InitiateVote()");
|
|
if (mapChange == MapChange_MapEnd && GetConVarInt(g_Cvar_Extend) - g_Extends < 1)
|
|
{
|
|
return Plugin_Stop; //feature where normal map changes does not happen if we ran out of extends, instead end of map does the actual real vote.
|
|
}
|
|
InitiateVote(mapChange, hndl);
|
|
return Plugin_Stop;
|
|
}
|
|
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
public void Event_TFRestartRound(Handle event, const char[] name, bool dontBroadcast)
|
|
{
|
|
/* Game got restarted - reset our round count tracking */
|
|
g_TotalRounds = 0;
|
|
}
|
|
|
|
public void Event_MvMWinPanel(Handle event, const char[] name, bool dontBroadcast)
|
|
{
|
|
if(GetEventInt(event, "winning_team") == 2)
|
|
{
|
|
int objectiveEnt = EntRefToEntIndex(g_ObjectiveEnt);
|
|
if(objectiveEnt != INVALID_ENT_REFERENCE)
|
|
{
|
|
g_TotalRounds = GetEntProp(g_ObjectiveEnt, Prop_Send, "m_nMannVsMachineWaveCount");
|
|
CheckMaxRounds(g_TotalRounds);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Event_Intermission(Handle event, const char[] name, bool dontBroadcast)
|
|
{
|
|
g_HasIntermissionStarted = true;
|
|
}
|
|
|
|
public void Event_PhaseEnd(Handle event, const char[] name, bool dontBroadcast)
|
|
{
|
|
/* announce_phase_end fires for both half time and the end of the map, but intermission fires first for end of the map. */
|
|
if(g_HasIntermissionStarted)
|
|
return;
|
|
|
|
/* No intermission yet, so this must be half time. Swap the score counters. */
|
|
int t_score = g_winCount[2];
|
|
g_winCount[2] = g_winCount[3];
|
|
g_winCount[3] = t_score;
|
|
}
|
|
|
|
public void Event_WeaponRank(Handle event, const char[] name, bool dontBroadcast)
|
|
{
|
|
int rank = GetEventInt(event, "weaponrank");
|
|
if(rank > g_TotalRounds)
|
|
{
|
|
g_TotalRounds = rank;
|
|
CheckMaxRounds(g_TotalRounds);
|
|
}
|
|
}
|
|
|
|
public Action CS_OnTerminateRound(float &delay, CSRoundEndReason &reason)
|
|
{
|
|
int timeleft;
|
|
GetMapTimeLeft(timeleft);
|
|
//cases that can happen:
|
|
//0 timeleft and admin did setnextmap.
|
|
//0 timeleft and rtv already picked a map. (round_end event forces the nextmap)
|
|
//0 timeleft and all extends used or map was not extended. (we do the map vote at map end)
|
|
if (timeleft <= 0 && g_iNextmapState >= 3)
|
|
{
|
|
int total_time = (GetConVarInt(g_Cvar_VoteDuration) * 2) + GetConVarInt(g_Cvar_RunOffWarningTime) + 10;
|
|
if (g_iNextmapState == 3)
|
|
{
|
|
if (IsVoteInProgress()) //fucking admins being brain dead doing extend votes right when the map ends.
|
|
{
|
|
PrintToChatAll("The admin slept on the job again. Should had made that extend vote 2-3 minutes before the timeleft ran out instead of panic extend voting the last second possible.");
|
|
CancelVote();
|
|
}
|
|
g_iNextmapState = 4; //started vote, so lets not repeat that.
|
|
CreateTimer(float(total_time + 10), Timer_fall_back_map_switch, INVALID_HANDLE, TIMER_FLAG_NO_MAPCHANGE);
|
|
//sometimes it will simply happen that theres a mapvote where nobody votes anything.
|
|
//have to prevent server from being stuck in those situations with this timer.
|
|
|
|
InitiateVote(MapChange_Instant); //feature so mapvote happens at actual mapend
|
|
|
|
CreateTimer(1.0, unfreeze_players, INVALID_HANDLE, TIMER_FLAG_NO_MAPCHANGE);
|
|
//extending the map allows players to run around instead of being frozen. should just be careful about it running into edicts limits
|
|
}
|
|
delay = float(total_time);
|
|
return Plugin_Changed;
|
|
}
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
public Action unfreeze_players(Handle hTimer, Handle dp)
|
|
{
|
|
ServerCommand("sm_testround");
|
|
CreateTimer(1.0, unfreeze_players2, INVALID_HANDLE, TIMER_FLAG_NO_MAPCHANGE);
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
public Action unfreeze_players2(Handle hTimer, Handle dp)
|
|
{
|
|
ServerCommand("sm_extend 20"); //this is not actually meant for extending the map. Its meant to unfreeze players during the mapvote.
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
public Action Timer_fall_back_map_switch(Handle hTimer, Handle dp)
|
|
{
|
|
ForceChangeLevel("ze_random_v9", "nobody voted at mapvote");
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
/* You ask, why don't you just use team_score event? And I answer... Because CSS doesn't. */
|
|
public void Event_RoundEnd(Handle event, const char[] name, bool dontBroadcast)
|
|
{
|
|
int timeleft;
|
|
GetMapTimeLeft(timeleft);
|
|
if (g_iNextmapState == 1 || (g_iNextmapState == 2 && timeleft <= 0)) //rtv went through or admin used setnextmap
|
|
{
|
|
char map[PLATFORM_MAX_PATH];
|
|
GetNextMap(map, sizeof(map));
|
|
PrintToChatAll("[MCE] Next Map: %s", map);
|
|
PrintToChatAll("[MCE] Next Map: %s", map);
|
|
PrintToChatAll("[MCE] Next Map: %s", map);
|
|
CreateTimer(2.0, Timer_ChangeMap, INVALID_HANDLE, TIMER_FLAG_NO_MAPCHANGE);
|
|
g_ChangeMapInProgress = true;
|
|
}
|
|
|
|
int winner;
|
|
if(strcmp(name, "round_win") == 0 || strcmp(name, "dod_round_win") == 0)
|
|
winner = GetEventInt(event, "team"); // Nuclear Dawn & DoD:S
|
|
else
|
|
winner = GetEventInt(event, "winner");
|
|
|
|
if(winner == 0 || winner == 1 || !GetConVarBool(g_Cvar_EndOfMapVote))
|
|
return;
|
|
|
|
if(winner >= MAXTEAMS)
|
|
SetFailState("Mod exceed maximum team count - Please file a bug report.");
|
|
|
|
g_TotalRounds++;
|
|
|
|
g_winCount[winner]++;
|
|
|
|
if(!GetArraySize(g_MapList) || g_HasVoteStarted || g_MapVoteCompleted)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CheckWinLimit(g_winCount[winner]);
|
|
CheckMaxRounds(g_TotalRounds);
|
|
}
|
|
|
|
public void CheckWinLimit(int winner_score)
|
|
{
|
|
int winlimit = GetConVarInt(g_Cvar_Winlimit);
|
|
if(winlimit)
|
|
{
|
|
if(winner_score >= (winlimit - GetConVarInt(g_Cvar_StartRounds)))
|
|
{
|
|
if(!g_WarningInProgress || g_WarningTimer == INVALID_HANDLE)
|
|
{
|
|
SetupWarningTimer(WarningType_Vote, MapChange_MapEnd);
|
|
//PrintToChatAll("SetupWarningTimer 3");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void CheckMaxRounds(int roundcount)
|
|
{
|
|
int maxrounds = 0;
|
|
|
|
if(g_RoundCounting == RoundCounting_ArmsRace)
|
|
maxrounds = GameRules_GetProp("m_iNumGunGameProgressiveWeaponsCT");
|
|
else if(g_RoundCounting == RoundCounting_MvM)
|
|
maxrounds = GetEntProp(g_ObjectiveEnt, Prop_Send, "m_nMannVsMachineMaxWaveCount");
|
|
else
|
|
return;
|
|
|
|
if(maxrounds)
|
|
{
|
|
if(roundcount >= (maxrounds - GetConVarInt(g_Cvar_StartRounds)))
|
|
{
|
|
if(!g_WarningInProgress || g_WarningTimer == INVALID_HANDLE)
|
|
{
|
|
SetupWarningTimer(WarningType_Vote, MapChange_MapEnd);
|
|
//PrintToChatAll("SetupWarningTimer 4");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public Action Event_PlayerDeath(Handle event, const char[] name, bool dontBroadcast)
|
|
{
|
|
if(!GetArraySize(g_MapList) || g_HasVoteStarted)
|
|
return Plugin_Continue;
|
|
|
|
if(!GetConVarInt(g_Cvar_Fraglimit) || !GetConVarBool(g_Cvar_EndOfMapVote))
|
|
return Plugin_Continue;
|
|
|
|
if(g_MapVoteCompleted)
|
|
return Plugin_Continue;
|
|
|
|
int fragger = GetClientOfUserId(GetEventInt(event, "attacker"));
|
|
|
|
if(!fragger)
|
|
return Plugin_Continue;
|
|
|
|
if(GetClientFrags(fragger) >= (GetConVarInt(g_Cvar_Fraglimit) - GetConVarInt(g_Cvar_StartFrags)))
|
|
{
|
|
if(!g_WarningInProgress || g_WarningTimer == INVALID_HANDLE)
|
|
{
|
|
SetupWarningTimer(WarningType_Vote, MapChange_MapEnd);
|
|
//LogMessage("SetupWarningTimer 5");
|
|
//PrintToChatAll("SetupWarningTimer 5");
|
|
}
|
|
}
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
public Action Command_Mapvote(int client, int args)
|
|
{
|
|
ShowActivity2(client, "[MCE] ", "%t", "Initiated Vote Map");
|
|
|
|
SetupWarningTimer(WarningType_Vote, MapChange_MapEnd, INVALID_HANDLE, true);
|
|
//LogMessage("SetupWarningTimer 6");
|
|
//PrintToChatAll("SetupWarningTimer 6");
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
public Handle get_most_nominated_maps(bool create_next_vote)
|
|
{
|
|
int arraySize = ByteCountToCells(PLATFORM_MAX_PATH);
|
|
Handle most_nominated_maps = CreateArray(arraySize);
|
|
StringMap sm = new StringMap();
|
|
|
|
for (int i = 0; i < MaxClients; i++)
|
|
{
|
|
for (int j = 0; j < GetArraySize(g_NominateList[i]); j++)
|
|
{
|
|
char map_iteration[PLATFORM_MAX_PATH];
|
|
GetArrayString(g_NominateList[i], j, map_iteration, PLATFORM_MAX_PATH);
|
|
int nominate_count_for_particular_map = 0;
|
|
sm.GetValue(map_iteration, nominate_count_for_particular_map);
|
|
|
|
//if i is 0 its admin nominated map that must come into the vote.
|
|
if (!i)
|
|
{
|
|
nominate_count_for_particular_map = 1999;
|
|
}
|
|
else
|
|
{
|
|
int nominate_worth = RoundToFloor(GetPlayerWorthRTV_boost_(i));
|
|
if (nominate_worth < 1)
|
|
{
|
|
nominate_worth = 1;
|
|
}
|
|
nominate_count_for_particular_map += nominate_worth;
|
|
}
|
|
sm.SetValue(map_iteration, nominate_count_for_particular_map, true);
|
|
|
|
if (!create_next_vote)
|
|
{
|
|
/* Notify Nominations that this map is now free */
|
|
Call_StartForward(g_NominationsResetForward);
|
|
Call_PushString(map_iteration);
|
|
Call_PushCell(i + 100); //differentiate between all other calls and the call invoked by get_most_nominated_maps()
|
|
Call_Finish();
|
|
}
|
|
}
|
|
}
|
|
static char map_[PLATFORM_MAX_PATH];
|
|
|
|
StringMapSnapshot sm_snapshot = sm.Snapshot();
|
|
ArrayList SortedList = CreateArray(arraySize);
|
|
|
|
for(int i = 0; i < sm_snapshot.Length; i++)
|
|
{
|
|
sm_snapshot.GetKey(i, map_, sizeof(map_));
|
|
SortedList.PushString(map_);
|
|
}
|
|
|
|
SortedList.SortCustom(NominateListSortCmp, sm);
|
|
|
|
for(int i = 0; i < GetArraySize(SortedList); i++)
|
|
{
|
|
char picked_map[MAX_NAME_LENGTH];
|
|
GetArrayString(SortedList, i, picked_map, sizeof(picked_map));
|
|
|
|
//2023 edit: respecting that only right amount of maps per group is allowed in vote
|
|
int groups_[32];
|
|
int groups[32];
|
|
int groupsfound = InternalGetMapGroups(picked_map, groups, sizeof(groups));
|
|
bool skip_nomination = false;
|
|
for (int group = 0; group < groupsfound; group ++)
|
|
{
|
|
int groupcur = 0;
|
|
int groupmax = InternalGetGroupMax(groups[group]);
|
|
if (groupmax >= 0)
|
|
{
|
|
for (int j = 0; j < GetArraySize(most_nominated_maps); j++)
|
|
{
|
|
//Native_GetMapGroupRestriction
|
|
GetArrayString(most_nominated_maps, j, map_, PLATFORM_MAX_PATH);
|
|
int tmp = InternalGetMapGroups(map_, groups_, sizeof(groups_));
|
|
if (FindIntInArray(groups_, tmp, groups[group]) != -1)
|
|
{
|
|
groupcur++;
|
|
}
|
|
if (groupcur >= groupmax)
|
|
{
|
|
skip_nomination = true;
|
|
break;
|
|
}
|
|
}
|
|
if (skip_nomination)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (skip_nomination)
|
|
{
|
|
continue;
|
|
}
|
|
PushArrayString(most_nominated_maps, picked_map);
|
|
}
|
|
|
|
delete sm;
|
|
return most_nominated_maps;
|
|
}
|
|
|
|
int NominateListSortCmp(int index1, int index2, Handle array, Handle hndl)
|
|
{
|
|
char map1[PLATFORM_MAX_PATH];
|
|
char map2[PLATFORM_MAX_PATH];
|
|
GetArrayString(array, index1, map1, sizeof(map1));
|
|
GetArrayString(array, index2, map2, sizeof(map2));
|
|
|
|
int count1, count2;
|
|
|
|
StringMap sm = view_as<StringMap>(hndl);
|
|
|
|
sm.GetValue(map1, count1);
|
|
sm.GetValue(map2, count2);
|
|
|
|
if (count1 == count2)
|
|
return 0;
|
|
|
|
return count1 > count2 ? -1 : 1;
|
|
}
|
|
|
|
/**
|
|
* Starts a new map vote
|
|
*
|
|
* @param when When the resulting map change should occur.
|
|
* @param inputlist Optional list of maps to use for the vote, otherwise an internal list of nominations + random maps will be used.
|
|
*/
|
|
void InitiateVote(MapChange when, Handle inputlist=INVALID_HANDLE)
|
|
{
|
|
g_WaitingForVote = true;
|
|
g_WarningInProgress = false;
|
|
int MenuRandomShuffleStart = 0;
|
|
int MenuRandomShuffleStop = 0;
|
|
|
|
// Check if a vote is in progress first
|
|
if(IsVoteInProgress())
|
|
{
|
|
CPrintToChatAll("[MCE] %t", "Cannot Start Vote", FAILURE_TIMER_LENGTH);
|
|
Handle data;
|
|
g_RetryTimer = CreateDataTimer(1.0, Timer_StartMapVote, data, TIMER_FLAG_NO_MAPCHANGE | TIMER_REPEAT);
|
|
//LogMessage("Mapchooser_extended_avg: called Timer_StartMapVote");
|
|
|
|
/* Mapchooser Extended */
|
|
WritePackCell(data, FAILURE_TIMER_LENGTH);
|
|
|
|
if(GetConVarBool(g_Cvar_RunOff) && g_RunoffCount > 0)
|
|
WritePackString(data, "Revote Warning");
|
|
else
|
|
{
|
|
if (when == MapChange_MapEnd)
|
|
{
|
|
WritePackString(data, "Vote Warning Extend");
|
|
}
|
|
else
|
|
{
|
|
WritePackString(data, "Vote Warning");
|
|
}
|
|
}
|
|
/* End Mapchooser Extended */
|
|
|
|
WritePackCell(data, view_as<int>(when));
|
|
WritePackCell(data, view_as<int>(inputlist));
|
|
ResetPack(data);
|
|
return;
|
|
}
|
|
|
|
/* If the main map vote has completed (and chosen result) and its currently changing (not a delayed change) we block further attempts */
|
|
if(g_MapVoteCompleted && g_ChangeMapInProgress)
|
|
{
|
|
//LogMessage("Mapchooser_extended_avg ended here: g_MapVoteCompleted: %i \n g_ChangeMapInProgress: %i", g_MapVoteCompleted, g_ChangeMapInProgress);
|
|
return;
|
|
}
|
|
|
|
CreateNextVote();
|
|
|
|
g_ChangeTime = when;
|
|
|
|
ServerCommand("sm_cvar sm_vote_progress_hintbox 0"); //yeah its cheesy but does the job.
|
|
g_iInterval = GetConVarInt(g_Cvar_VoteDuration);
|
|
CreateTimer(1.0, Timer_Countdown, _, TIMER_REPEAT);
|
|
|
|
g_HasVoteStarted = true;
|
|
|
|
g_VoteMenu = CreateMenu(Handler_MapVoteMenu, MenuAction_End | MenuAction_Display | MenuAction_DisplayItem | MenuAction_VoteCancel);
|
|
|
|
g_AddNoVote = GetConVarBool(g_Cvar_NoVoteOption);
|
|
|
|
// Block Vote Slots
|
|
if(GetConVarBool(g_Cvar_BlockSlots))
|
|
{
|
|
Handle radioStyle = GetMenuStyleHandle(MenuStyle_Radio);
|
|
|
|
if(GetMenuStyle(g_VoteMenu) == radioStyle)
|
|
{
|
|
g_BlockedSlots = true;
|
|
AddMenuItem(g_VoteMenu, LINE_ONE, "Choose something...", ITEMDRAW_DISABLED);
|
|
AddMenuItem(g_VoteMenu, LINE_TWO, "...will ya?", ITEMDRAW_DISABLED);
|
|
MenuRandomShuffleStart += 2;
|
|
|
|
if(!g_AddNoVote) {
|
|
AddMenuItem(g_VoteMenu, LINE_SPACER, "", ITEMDRAW_SPACER);
|
|
MenuRandomShuffleStart++;
|
|
}
|
|
}
|
|
else
|
|
g_BlockedSlots = false;
|
|
}
|
|
|
|
if(g_AddNoVote)
|
|
SetMenuOptionFlags(g_VoteMenu, MENUFLAG_BUTTON_NOVOTE);
|
|
|
|
SetMenuTitle(g_VoteMenu, "Vote Nextmap");
|
|
SetVoteResultCallback(g_VoteMenu, Handler_MapVoteFinished);
|
|
|
|
/* Call OnMapVoteStarted() Forward */
|
|
// Call_StartForward(g_MapVoteStartedForward);
|
|
// Call_Finish();
|
|
|
|
/**
|
|
* TODO: Make a proper decision on when to clear the nominations list.
|
|
* Currently it clears when used, and stays if an external list is provided.
|
|
* Is this the right thing to do? External lists will probably come from places
|
|
* like sm_mapvote from the adminmenu in the future.
|
|
*/
|
|
|
|
static char map[PLATFORM_MAX_PATH];
|
|
if (when == MapChange_MapEnd) //18th september feature idea where a normal mapvote at 3 minutes is between extend or not extend.
|
|
{
|
|
SetMenuTitle(g_VoteMenu, "Vote Extend");
|
|
AddMenuItem(g_VoteMenu, LINE_ONE, "Choose something...", ITEMDRAW_DISABLED);
|
|
AddMenuItem(g_VoteMenu, LINE_TWO, "...will ya?", ITEMDRAW_DISABLED);
|
|
AddExtendToMenu(g_VoteMenu, when);
|
|
//AddMenuItem(g_VoteMenu, LINE_ONE, "Choose something...", ITEMDRAW_DISABLED);
|
|
AddMapItem("Dont extend");
|
|
SetMenuOptionFlags(g_VoteMenu, MENUFLAG_BUTTON_NOVOTE); //should add no vote option for the extend vote?
|
|
}
|
|
/* No input given - User our internal nominations and maplist */
|
|
else if(inputlist == INVALID_HANDLE)
|
|
{
|
|
Handle randomizeList = INVALID_HANDLE;
|
|
//2023 edit to allow multiple nominations per player
|
|
Handle most_nominated_maps = get_most_nominated_maps(false);
|
|
|
|
int voteSize = GetVoteSize(0); //voteSize wrong size probably for my for loop
|
|
|
|
if(GetConVarBool(g_Cvar_RandomizeNominations))
|
|
randomizeList = CloneArray(most_nominated_maps);
|
|
|
|
int nominateCount = GetArraySize(most_nominated_maps);
|
|
|
|
|
|
/* Smaller of the two - It should be impossible for nominations to exceed the size though (cvar changed mid-map?) */
|
|
int nominationsToAdd = nominateCount >= voteSize ? voteSize : nominateCount;
|
|
|
|
bool extendFirst = GetConVarBool(g_Cvar_ExtendPosition);
|
|
|
|
if(extendFirst) {
|
|
AddExtendToMenu(g_VoteMenu, when);
|
|
MenuRandomShuffleStart++;
|
|
}
|
|
|
|
for(int i = 0; i < nominationsToAdd; i++)
|
|
{
|
|
GetArrayString(most_nominated_maps, i, map, PLATFORM_MAX_PATH);
|
|
|
|
if(randomizeList == INVALID_HANDLE)
|
|
AddMapItem(map);
|
|
RemoveStringFromArray(g_NextMapList, map);
|
|
}
|
|
|
|
|
|
/* There should currently be 'nominationsToAdd' unique maps in the vote */
|
|
|
|
int i = nominationsToAdd;
|
|
int count = 0;
|
|
int availableMaps = GetArraySize(g_NextMapList);
|
|
|
|
if(i < voteSize && availableMaps == 0)
|
|
{
|
|
if(i == 0)
|
|
{
|
|
LogError("No maps available for vote.");
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
LogMessage("Not enough maps to fill map list, reducing map count. Adjust mce_include and mce_exclude to avoid this warning.");
|
|
voteSize = i;
|
|
}
|
|
}
|
|
|
|
while(i < voteSize)
|
|
{
|
|
GetArrayString(g_NextMapList, count, map, PLATFORM_MAX_PATH);
|
|
count++;
|
|
|
|
if(randomizeList == INVALID_HANDLE)
|
|
{
|
|
/* Insert the map and increment our count */
|
|
AddMapItem(map);
|
|
}
|
|
else
|
|
PushArrayString(randomizeList, map);
|
|
i++;
|
|
|
|
//Run out of maps, this will have to do.
|
|
if(count >= availableMaps)
|
|
break;
|
|
}
|
|
|
|
if(randomizeList != INVALID_HANDLE)
|
|
{
|
|
// Fisher-Yates Shuffle
|
|
for(int j = GetArraySize(randomizeList) - 1; j >= 1; j--)
|
|
{
|
|
int k = GetRandomInt(0, j);
|
|
SwapArrayItems(randomizeList, j, k);
|
|
}
|
|
|
|
for(int j = 0; j < GetArraySize(randomizeList); j++)
|
|
{
|
|
GetArrayString(randomizeList, j, map, PLATFORM_MAX_PATH);
|
|
AddMapItem(map);
|
|
}
|
|
|
|
delete randomizeList;
|
|
randomizeList = INVALID_HANDLE;
|
|
delete most_nominated_maps;
|
|
most_nominated_maps = INVALID_HANDLE;
|
|
}
|
|
|
|
/* Wipe out our nominations list - Nominations have already been informed of this */
|
|
g_NominateCount = 0;
|
|
g_NominateReservedCount = 0;
|
|
ClearArray(g_NominateOwners);
|
|
for (int j = 0; j < MaxClients; j++)
|
|
{
|
|
ClearArray(g_NominateList[j]);
|
|
}
|
|
|
|
if(!extendFirst) {
|
|
AddExtendToMenu(g_VoteMenu, when);
|
|
MenuRandomShuffleStop++;
|
|
}
|
|
}
|
|
else //We were given a list of maps to start the vote with
|
|
{
|
|
int size = GetArraySize(inputlist);
|
|
|
|
for(int i = 0; i < size; i++)
|
|
{
|
|
GetArrayString(inputlist, i, map, PLATFORM_MAX_PATH);
|
|
|
|
if(IsMapValid(map))
|
|
{
|
|
AddMapItem(map);
|
|
}
|
|
// New in Mapchooser Extended
|
|
else if(StrEqual(map, VOTE_DONTCHANGE))
|
|
{
|
|
AddMenuItem(g_VoteMenu, VOTE_DONTCHANGE, "Dont Change");
|
|
}
|
|
else if(StrEqual(map, VOTE_EXTEND))
|
|
{
|
|
AddMenuItem(g_VoteMenu, VOTE_EXTEND, "Extend Map");
|
|
}
|
|
}
|
|
delete inputlist;
|
|
}
|
|
|
|
int voteDuration = GetConVarInt(g_Cvar_VoteDuration);
|
|
|
|
//SetMenuExitButton(g_VoteMenu, false);
|
|
|
|
if(GetVoteSize(0) <= GetMaxPageItems(GetMenuStyle(g_VoteMenu)))
|
|
{
|
|
//This is necessary to get items 9 and 0 as usable voting items
|
|
SetMenuPagination(g_VoteMenu, MENU_NO_PAGINATION);
|
|
}
|
|
|
|
if(GetConVarInt(g_Cvar_ShufflePerClient))
|
|
MenuShufflePerClient(g_VoteMenu, MenuRandomShuffleStart, GetMenuItemCount(g_VoteMenu) - MenuRandomShuffleStop);
|
|
|
|
//VoteMenuToAll(g_VoteMenu, voteDuration);
|
|
//2023 excluding nosteamers and autismbots.
|
|
|
|
int clients[MAXPLAYERS + 1];
|
|
int count = 0;
|
|
//here we pick clients to show the vote to, we dont show it to autismbots and nosteamers.
|
|
for (int i = 0; i < MaxClients; i++)
|
|
{
|
|
player_mapvote_worth[i] = 0.0;
|
|
Format(player_mapvote[i], 128, "");
|
|
if (IsValidClient(i) && PM_IsPlayerSteam(i) && !is_bot_player[i])
|
|
{
|
|
GetPlayerWorthRTV_(i);
|
|
float nominate_worth = GetPlayerWorthRTV_boost_(i);
|
|
if (nominate_worth < 1.0)
|
|
{
|
|
nominate_worth = 1.0;
|
|
}
|
|
player_mapvote_worth[i] = nominate_worth;
|
|
clients[count] = i;
|
|
count++;
|
|
}
|
|
}
|
|
VoteMenu(g_VoteMenu, clients, sizeof(clients), voteDuration);
|
|
|
|
/* Call OnMapVoteStarted() Forward */
|
|
Call_StartForward(g_MapVoteStartForward); // Deprecated
|
|
Call_Finish();
|
|
|
|
Call_StartForward(g_MapVoteStartedForward);
|
|
Call_Finish();
|
|
|
|
LogAction(-1, -1, "Voting for next map has started.");
|
|
CPrintToChatAll("[MCE] %t", "Nextmap Voting Started");
|
|
}
|
|
|
|
public Action Timer_Countdown(Handle timer)
|
|
{
|
|
if (g_iInterval <= 0)
|
|
return Plugin_Stop;
|
|
|
|
float most_voted[3];
|
|
char most_voted_map[3][PLATFORM_MAX_PATH];
|
|
|
|
for (int j = 0; j < 3; j++)
|
|
{
|
|
float max_count = 0.0;
|
|
StringMap sm = new StringMap();
|
|
char picked_map[PLATFORM_MAX_PATH];
|
|
for (int i = 0; i < MaxClients; i++)
|
|
{
|
|
if (IsValidClient(i) && !StrEqual(player_mapvote[i], "") && !is_bot_player[i] && PM_IsPlayerSteam(i))
|
|
{
|
|
if (StrEqual(player_mapvote[i], most_voted_map[0]) || StrEqual(player_mapvote[i], most_voted_map[1]) ||
|
|
StrEqual(player_mapvote[i], most_voted_map[2]))
|
|
{
|
|
continue;
|
|
}
|
|
float value = 0.0;
|
|
sm.GetValue(player_mapvote[i], value);
|
|
value += player_mapvote_worth[i];
|
|
sm.SetValue(player_mapvote[i], value, true);
|
|
if (value >= max_count)
|
|
{
|
|
max_count = value;
|
|
strcopy(picked_map, sizeof(picked_map), player_mapvote[i]);
|
|
}
|
|
}
|
|
}
|
|
most_voted[j] = max_count;
|
|
Format(most_voted_map[j], PLATFORM_MAX_PATH, picked_map);
|
|
delete sm;
|
|
}
|
|
|
|
float total_votes = 0.0;
|
|
float voted_so_far = 0.0;
|
|
for (int i = 0; i < MaxClients; i++)
|
|
{
|
|
if (IsValidClient(i) && !is_bot_player[i] && PM_IsPlayerSteam(i))
|
|
{
|
|
total_votes += player_mapvote_worth[i];
|
|
//LogMessage("client: %N player_mapvote_worth i: %f", i, player_mapvote_worth[i]);
|
|
if (!StrEqual(player_mapvote[i], ""))
|
|
{
|
|
voted_so_far += player_mapvote_worth[i];
|
|
}
|
|
}
|
|
}
|
|
//why on earth is %%%s needed for formatting it to show a % lmao. just %s, "%" or %% on their own dont work.
|
|
if (strlen(most_voted_map[2]) > 0)
|
|
{
|
|
//displaying 3 most voted maps
|
|
PrintHintTextToAll("Votes: %i/100%%%s, %ds left\n1. %s: (%i%%%s)\n2. %s: (%i%%%s)\n3. %s: (%i%%%s)", RoundToFloor((voted_so_far/total_votes) * 100),
|
|
"%", g_iInterval, most_voted_map[0], RoundToFloor((most_voted[0]/total_votes) * 100), "%", most_voted_map[1], RoundToFloor((most_voted[1]/total_votes) * 100),
|
|
"%", most_voted_map[2], RoundToFloor((most_voted[2]/total_votes) * 100), "%");
|
|
}
|
|
else if (strlen(most_voted_map[1]) > 0)
|
|
{
|
|
//displaying 2 most voted maps
|
|
PrintHintTextToAll("Votes: %i/100%%%s, %ds left\n1. %s: (%i%%%s)\n2. %s: (%i%%%s)", RoundToFloor((voted_so_far/total_votes) * 100), "%", g_iInterval,
|
|
most_voted_map[0], RoundToFloor((most_voted[0]/total_votes) * 100), "%", most_voted_map[1], RoundToFloor((most_voted[1]/total_votes) * 100), "%");
|
|
}
|
|
else if (strlen(most_voted_map[0]) > 0)
|
|
{
|
|
//displaying the most voted map
|
|
PrintHintTextToAll("Votes: %i/100%%%s, %ds left\n1. %s: (%i%%%s)", RoundToFloor((voted_so_far/total_votes) * 100),
|
|
"%", g_iInterval, most_voted_map[0], RoundToFloor((most_voted[0]/total_votes) * 100), "%");
|
|
}
|
|
else
|
|
{
|
|
//displaying just the votecount and vote duration remaining
|
|
PrintHintTextToAll("Votes: %i/100%%%s, %ds left", RoundToFloor((voted_so_far/total_votes) * 100), "%", g_iInterval);
|
|
}
|
|
g_iInterval--;
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
public void Handler_VoteFinishedGeneric(char[] map,
|
|
float num_votes,
|
|
float map_votes)
|
|
{
|
|
Call_StartForward(g_MapVoteEndForward);
|
|
Call_PushString(map);
|
|
Call_Finish();
|
|
|
|
if(strcmp(map, VOTE_EXTEND, false) == 0)
|
|
{
|
|
g_Extends++;
|
|
int time;
|
|
if(GetMapTimeLimit(time))
|
|
{
|
|
if(time > 0)
|
|
ExtendMapTimeLimit(GetConVarInt(g_Cvar_ExtendTimeStep)*60);
|
|
}
|
|
|
|
int winlimit = GetConVarInt(g_Cvar_Winlimit);
|
|
if(winlimit)
|
|
SetConVarInt(g_Cvar_Winlimit, winlimit + GetConVarInt(g_Cvar_ExtendRoundStep));
|
|
|
|
int maxrounds = GetConVarInt(g_Cvar_Maxrounds);
|
|
if(maxrounds)
|
|
SetConVarInt(g_Cvar_Maxrounds, maxrounds + GetConVarInt(g_Cvar_ExtendRoundStep));
|
|
|
|
int fraglimit = GetConVarInt(g_Cvar_Fraglimit);
|
|
if(fraglimit)
|
|
SetConVarInt(g_Cvar_Fraglimit, fraglimit + GetConVarInt(g_Cvar_ExtendFragStep));
|
|
|
|
PrintToChatAll("The current map has been extended. (Received %i%s of votes)", RoundToFloor((map_votes /num_votes)*100.0), "%");
|
|
LogAction(-1, -1, "Voting for next map has finished. The current map has been extended.");
|
|
CPrintToChatAll("[MCE] Available Extends: %d", GetConVarInt(g_Cvar_Extend) - g_Extends);
|
|
|
|
// We extended, so well have to vote again.
|
|
g_RunoffCount = 0;
|
|
g_HasVoteStarted = false;
|
|
|
|
if (GetConVarInt(g_Cvar_Extend) - g_Extends < 1)
|
|
{
|
|
g_iNextmapState = 3; //last extend was used
|
|
}
|
|
|
|
SetupTimeleftTimer();
|
|
}
|
|
else if(strcmp(map, VOTE_DONTCHANGE, false) == 0)
|
|
{
|
|
PrintToChatAll("[MCE] Current map continues! The Vote has spoken! (Received %i%s of votes)", RoundToFloor((map_votes /num_votes)*100.0), "%");
|
|
LogAction(-1, -1, "Voting for next map has finished. 'No Change' was the winner");
|
|
|
|
g_RunoffCount = 0;
|
|
g_HasVoteStarted = false;
|
|
SetupTimeleftTimer();
|
|
}
|
|
else
|
|
{
|
|
if(g_ChangeTime == MapChange_MapEnd)
|
|
{
|
|
g_iNextmapState = 3; //map was not extended
|
|
}
|
|
else if(g_ChangeTime == MapChange_Instant) //end of map voting ends up here because we change instantly.
|
|
{
|
|
PrintToChatAll("[MCE] Next Map: %s", map);
|
|
PrintToChatAll("[MCE] Next Map: %s", map);
|
|
PrintToChatAll("[MCE] Next Map: %s", map);
|
|
Handle data;
|
|
CreateDataTimer(4.0, Timer_ChangeMap, data);
|
|
WritePackString(data, map);
|
|
g_ChangeMapInProgress = false;
|
|
}
|
|
else // MapChange_RoundEnd
|
|
{
|
|
g_iNextmapState = 1; //rtv was performed.
|
|
SetNextMap(map);
|
|
}
|
|
|
|
g_HasVoteStarted = false;
|
|
g_MapVoteCompleted = true;
|
|
//checking on MapStart and MapEnd is not good enough. Players are not considered alive and on teams at those points in time.
|
|
//therefore instead applying the bool here after the mapvote completed.
|
|
if(InternalAreRestrictionsActive(false))
|
|
g_SaveCDOnMapEnd = true;
|
|
else
|
|
g_SaveCDOnMapEnd = false;
|
|
|
|
//PrintToChatAll("map: %s", map);
|
|
if (g_ChangeTime == MapChange_MapEnd)
|
|
{
|
|
CPrintToChatAll("[MCE] %t", "Extend Voting Failed", RoundToFloor((map_votes /num_votes)*100.0));
|
|
g_MapVoteCompleted = false; //this was only the extend or dont extend vote, still need actual mapvote.
|
|
}
|
|
else
|
|
{
|
|
CPrintToChatAll("[MCE] %t", "Nextmap Voting Finished", map, RoundToFloor((map_votes /num_votes)*100.0));
|
|
LogAction(-1, -1, "Voting for next map has finished. Nextmap: %s.", map);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Handler_MapVoteFinished(Handle menu,
|
|
int num_votes,
|
|
int num_clients,
|
|
const int[][] client_info,
|
|
int num_items,
|
|
const int[][] item_info)
|
|
{
|
|
g_iInterval = 1; //we display it shortly after finishing
|
|
ServerCommand("sm_cvar sm_vote_progress_hintbox 1"); //yeah its cheesy but does the job.
|
|
//reweighting votes
|
|
StringMap sm = new StringMap();
|
|
for (int i = 0; i < num_clients; i++)
|
|
{
|
|
int client = client_info[i][0];
|
|
if (IsValidClient(client) && !StrEqual(player_mapvote[client], ""))
|
|
{
|
|
//default intention is 1-5 votes, just like rtv.
|
|
int item_index = client_info[i][VOTEINFO_CLIENT_ITEM];
|
|
static char map[PLATFORM_MAX_PATH];
|
|
for(int j = 0; j < num_items; j++)
|
|
{
|
|
if (item_info[j][VOTEINFO_ITEM_INDEX] == item_index)
|
|
{
|
|
GetMapItem(menu, item_info[j][VOTEINFO_ITEM_INDEX], map, PLATFORM_MAX_PATH);
|
|
break;
|
|
}
|
|
}
|
|
float value = 0.0;
|
|
sm.GetValue(map, value);
|
|
|
|
float nominate_worth = GetPlayerWorthRTV_boost_(client);
|
|
if (nominate_worth < 1.0)
|
|
{
|
|
nominate_worth = 1.0;
|
|
}
|
|
value += nominate_worth;
|
|
sm.SetValue(map, value, true);
|
|
}
|
|
}
|
|
|
|
//ordering stringmap by voteweight
|
|
float[] weighted_votes = new float[num_items];
|
|
char[][] weighted_maps = new char[num_items][PLATFORM_MAX_PATH];
|
|
for (int i = 0; i < num_items; i++)
|
|
{
|
|
StringMapSnapshot keys = sm.Snapshot();
|
|
float max_count = 0.0;
|
|
char picked_map[PLATFORM_MAX_PATH];
|
|
for (int j = 0; j < keys.Length; j++)
|
|
{
|
|
int size = keys.KeyBufferSize(j);
|
|
char[] buffer = new char[size];
|
|
keys.GetKey(j, buffer, size);
|
|
float value = 0.0;
|
|
sm.GetValue(buffer, value);
|
|
|
|
//first selection has most votes, second selection second most etc etc
|
|
if (value >= max_count)
|
|
{
|
|
max_count = value;
|
|
strcopy(picked_map, sizeof(picked_map), buffer);
|
|
}
|
|
}
|
|
sm.Remove(picked_map);
|
|
weighted_votes[i] = max_count;
|
|
Format(weighted_maps[i], 128, picked_map);
|
|
delete keys;
|
|
}
|
|
delete sm;
|
|
|
|
float total_votes = 0.0;
|
|
for (int i = 0; i < MaxClients; i++)
|
|
{
|
|
if (IsValidClient(i) && !is_bot_player[i] && PM_IsPlayerSteam(i))
|
|
{
|
|
total_votes += player_mapvote_worth[i];
|
|
}
|
|
}
|
|
// Implement revote logic - Only run this` block if revotes are enabled and this isn't the last revote'
|
|
//LogMessage("Mapchooser_extended_avg Handler_MapVoteFinished.");
|
|
int required_percent = GetConVarInt(g_Cvar_RunOffPercent);
|
|
int most_voted_map_percentage = RoundToFloor((weighted_votes[0] / total_votes) * 100);
|
|
if(GetConVarBool(g_Cvar_RunOff) && g_RunoffCount < GetConVarInt(g_Cvar_MaxRunOffs) && num_items > 1 && g_ChangeTime != MapChange_MapEnd &&
|
|
(weighted_votes[0] == weighted_votes[1] || most_voted_map_percentage < required_percent))
|
|
{
|
|
//LogMessage("Mapchooser_extended_avg Handler_MapVoteFinished passed check1.");
|
|
g_RunoffCount++;
|
|
if(weighted_votes[0] == weighted_votes[1])
|
|
{
|
|
g_HasVoteStarted = false;
|
|
|
|
//Revote is needed
|
|
int arraySize = ByteCountToCells(PLATFORM_MAX_PATH);
|
|
Handle mapList = CreateArray(arraySize);
|
|
|
|
//adding all maps with same amount of votes
|
|
for(int i = 0; i < num_items; i++)
|
|
{
|
|
if(weighted_votes[i] == weighted_votes[0])
|
|
{
|
|
PushArrayString(mapList, weighted_maps[i]);
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
CPrintToChatAll("[MCE] %t", "Tie Vote", GetArraySize(mapList));
|
|
//LogMessage("Mapchooser_extended_avg reached WarningType_Revote 1");
|
|
SetupWarningTimer(WarningType_Revote, view_as<MapChange>(g_ChangeTime), mapList);
|
|
//PrintToChatAll("SetupWarningTimer 7");
|
|
return;
|
|
}
|
|
else if(most_voted_map_percentage < required_percent)
|
|
{
|
|
g_HasVoteStarted = false;
|
|
|
|
//Revote is needed
|
|
int arraySize = ByteCountToCells(PLATFORM_MAX_PATH);
|
|
Handle mapList = CreateArray(arraySize);
|
|
|
|
PushArrayString(mapList, weighted_maps[0]);
|
|
// We allow more than two maps for a revote if they are tied
|
|
for(int i = 1; i < num_items; i++)
|
|
{
|
|
if(GetArraySize(mapList) < 2 || weighted_votes[i] == weighted_votes[i - 1])
|
|
{
|
|
PushArrayString(mapList, weighted_maps[i]);
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
CPrintToChatAll("[MCE] %t", "Revote Is Needed", required_percent);
|
|
//LogMessage("Mapchooser_extended_avg reached WarningType_Revote 2");
|
|
SetupWarningTimer(WarningType_Revote, view_as<MapChange>(g_ChangeTime), mapList);
|
|
//PrintToChatAll("SetupWarningTimer 8");
|
|
return;
|
|
}
|
|
}
|
|
|
|
g_WaitingForVote = false;
|
|
//LogMessage("Mapchooser_extended_avg no revote needed, continue as normal.");
|
|
// No revote needed, continue as normal.
|
|
Handler_VoteFinishedGeneric(weighted_maps[0], total_votes, weighted_votes[0]);
|
|
}
|
|
|
|
public int Handler_MapVoteMenu(Handle menu, MenuAction action, int param1, int param2)
|
|
{
|
|
switch(action)
|
|
{
|
|
case MenuAction_End:
|
|
{
|
|
g_VoteMenu = INVALID_HANDLE;
|
|
delete menu;
|
|
}
|
|
case MenuAction_Select:
|
|
{
|
|
char map[PLATFORM_MAX_PATH];
|
|
GetMenuItem(menu, param2, map, PLATFORM_MAX_PATH, _, _, _, param1);
|
|
if (StrEqual(map, "##dontchange##"))
|
|
{
|
|
Format(map, sizeof(map), "Dont change");
|
|
}
|
|
else if (StrEqual(map, "##extend##"))
|
|
{
|
|
Format(map, sizeof(map), "Extend");
|
|
}
|
|
Format(player_mapvote[param1], 128, map);
|
|
}
|
|
case MenuAction_Display:
|
|
{
|
|
Format(player_mapvote[param1], 128, "");
|
|
static char buffer[255];
|
|
|
|
//displaying to the client how much percent his vote is worth.
|
|
float total_votes = 0.0;
|
|
for (int i = 0; i < MaxClients; i++)
|
|
{
|
|
if (IsValidClient(i) && !is_bot_player[i] && PM_IsPlayerSteam(i))
|
|
{
|
|
total_votes += player_mapvote_worth[i];
|
|
}
|
|
}
|
|
//second parameter shows for example 5.0, last parameter is how much percentage of the entire vote the client decides.
|
|
if (g_ChangeTime == MapChange_MapEnd)
|
|
{
|
|
Format(buffer, sizeof(buffer), "%T", "Vote Extend", param1, player_mapvote_worth[param1], RoundToFloor((player_mapvote_worth[param1]/total_votes) * 100));
|
|
}
|
|
else
|
|
{
|
|
Format(buffer, sizeof(buffer), "%T", "Vote Nextmap", param1, player_mapvote_worth[param1], RoundToFloor((player_mapvote_worth[param1]/total_votes) * 100));
|
|
}
|
|
Handle panel = view_as<Handle>(param2);
|
|
SetPanelTitle(panel, buffer);
|
|
//char PannelText[256] = "Warning: The Position of the Maps are different for each Player.";
|
|
//DrawPanelText(panel, PannelText);
|
|
}
|
|
|
|
case MenuAction_DisplayItem:
|
|
{
|
|
char map[PLATFORM_MAX_PATH];
|
|
char buffer[255];
|
|
int mark = GetConVarInt(g_Cvar_MarkCustomMaps);
|
|
|
|
GetMenuItem(menu, param2, map, PLATFORM_MAX_PATH, _, _, _, param1);
|
|
if(StrEqual(map, VOTE_EXTEND, false))
|
|
{
|
|
Format(buffer, sizeof(buffer), "%T", "Extend Map", param1);
|
|
}
|
|
else if(StrEqual(map, VOTE_DONTCHANGE, false))
|
|
{
|
|
Format(buffer, sizeof(buffer), "%T", "Dont Change", param1);
|
|
}
|
|
// Mapchooser Extended
|
|
else if(StrEqual(map, LINE_ONE, false))
|
|
{
|
|
Format(buffer, sizeof(buffer),"%T", "Line One", param1);
|
|
}
|
|
else if(StrEqual(map, LINE_TWO, false))
|
|
{
|
|
Format(buffer, sizeof(buffer),"%T", "Line Two", param1);
|
|
}
|
|
// Note that the first part is to discard the spacer line
|
|
else if(!StrEqual(map, LINE_SPACER, false))
|
|
{
|
|
if(mark == 1 && !InternalIsMapOfficial(map))
|
|
{
|
|
Format(buffer, sizeof(buffer), "%T", "Custom Marked", param1, map);
|
|
}
|
|
else if(mark == 2 && !InternalIsMapOfficial(map))
|
|
{
|
|
Format(buffer, sizeof(buffer), "%T", "Custom", param1, map);
|
|
}
|
|
else if(InternalGetMapVIPRestriction(map))
|
|
{
|
|
Format(buffer, sizeof(buffer), "%s (%T)", map, "VIP Nomination", param1);
|
|
}
|
|
}
|
|
|
|
if(buffer[0] != '\0')
|
|
{
|
|
return RedrawMenuItem(buffer);
|
|
}
|
|
// End Mapchooser Extended
|
|
}
|
|
|
|
case MenuAction_VoteCancel:
|
|
{
|
|
// If we receive 0 votes, pick at random.
|
|
if(param1 == VoteCancel_NoVotes && GetConVarBool(g_Cvar_NoVoteMode))
|
|
{
|
|
int count = GetMenuItemCount(menu);
|
|
|
|
int item;
|
|
static char map[PLATFORM_MAX_PATH];
|
|
|
|
do
|
|
{
|
|
int startInt = 0;
|
|
if(g_BlockedSlots)
|
|
{
|
|
if(g_AddNoVote)
|
|
{
|
|
startInt = 2;
|
|
}
|
|
else
|
|
{
|
|
startInt = 3;
|
|
}
|
|
}
|
|
item = GetRandomInt(startInt, count - 1);
|
|
|
|
GetMenuItem(menu, item, map, PLATFORM_MAX_PATH, _, _, _, param1);
|
|
}
|
|
while(strcmp(map, VOTE_EXTEND, false) == 0);
|
|
|
|
SetNextMap(map);
|
|
g_MapVoteCompleted = true;
|
|
}
|
|
g_WaitingForVote = false;
|
|
g_HasVoteStarted = false;
|
|
g_RunoffCount = 0;
|
|
g_iInterval = 1; //we display it shortly after finishing
|
|
ServerCommand("sm_cvar sm_vote_progress_hintbox 1"); //yeah its cheesy but does the job.
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public Action Timer_ChangeMap(Handle hTimer, Handle dp)
|
|
{
|
|
g_ChangeMapInProgress = false;
|
|
|
|
char map[PLATFORM_MAX_PATH];
|
|
|
|
if(dp == INVALID_HANDLE)
|
|
{
|
|
if(!GetNextMap(map, PLATFORM_MAX_PATH))
|
|
{
|
|
//No passed map and no set nextmap. fail!
|
|
return Plugin_Stop;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ResetPack(dp);
|
|
ReadPackString(dp, map, PLATFORM_MAX_PATH);
|
|
}
|
|
PrintToChatAll("[MCE] Next Map: %s", map);
|
|
PrintToChatAll("[MCE] Next Map: %s", map);
|
|
PrintToChatAll("[MCE] Next Map: %s", map);
|
|
ForceChangeLevel(map, "Map Vote");
|
|
|
|
return Plugin_Stop;
|
|
}
|
|
|
|
bool RemoveStringFromArray(Handle array, char[] str)
|
|
{
|
|
int index = FindStringInArray(array, str);
|
|
if(index != -1)
|
|
{
|
|
RemoveFromArray(array, index);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CreateNextVote()
|
|
{
|
|
assert(g_NextMapList)
|
|
ClearArray(g_NextMapList);
|
|
|
|
static char map[PLATFORM_MAX_PATH];
|
|
Handle tempMaps = CloneArray(g_MapList);
|
|
|
|
GetCurrentMap(map, PLATFORM_MAX_PATH);
|
|
RemoveStringFromArray(tempMaps, map);
|
|
|
|
if(GetArraySize(tempMaps) > GetConVarInt(g_Cvar_ExcludeMaps) && InternalAreRestrictionsActive(false))
|
|
{
|
|
StringMapSnapshot OldMapListSnapshot = g_OldMapList.Snapshot();
|
|
for(int i = 0; i < OldMapListSnapshot.Length; i++)
|
|
{
|
|
OldMapListSnapshot.GetKey(i, map, sizeof(map));
|
|
RemoveStringFromArray(tempMaps, map);
|
|
}
|
|
delete OldMapListSnapshot;
|
|
}
|
|
|
|
if(InternalAreRestrictionsActive(false))
|
|
{
|
|
StringMapSnapshot TimeMapListSnapshot = g_TimeMapList.Snapshot();
|
|
for(int i = 0; i < TimeMapListSnapshot.Length; i++)
|
|
{
|
|
TimeMapListSnapshot.GetKey(i, map, sizeof(map));
|
|
int Cooldown;
|
|
g_TimeMapList.GetValue(map, Cooldown);
|
|
|
|
if(Cooldown > GetTime())
|
|
RemoveStringFromArray(tempMaps, map);
|
|
}
|
|
delete TimeMapListSnapshot;
|
|
}
|
|
|
|
int voteSize = GetVoteSize(0);
|
|
int limit = (voteSize < GetArraySize(tempMaps) ? voteSize : GetArraySize(tempMaps));
|
|
|
|
// group -> number of maps nominated from group
|
|
StringMap groupmap = new StringMap();
|
|
char groupstr[8];
|
|
|
|
// populate groupmap with maps from nomination list
|
|
static char map_[PLATFORM_MAX_PATH];
|
|
int groups_[32];
|
|
|
|
//2023 edit
|
|
Handle most_nominated_maps = get_most_nominated_maps(true);
|
|
for(int i = 0; i < GetArraySize(most_nominated_maps); i++)
|
|
{
|
|
GetArrayString(most_nominated_maps, i, map_, PLATFORM_MAX_PATH);
|
|
int groupsfound = InternalGetMapGroups(map_, groups_, sizeof(groups_));
|
|
for(int group = 0; group < groupsfound; group++)
|
|
{
|
|
IntToString(group, groupstr, sizeof(groupstr));
|
|
int groupcur = 0;
|
|
groupmap.GetValue(groupstr, groupcur);
|
|
groupcur++;
|
|
groupmap.SetValue(groupstr, groupcur, true);
|
|
}
|
|
}
|
|
|
|
// find random maps which honor all restrictions
|
|
int pickedFromCasualPool = 0;
|
|
for(int i = 0; i < limit; i++)
|
|
{
|
|
int b;
|
|
for(int j = 0; j < 1000; j++)
|
|
{
|
|
b = GetRandomInt(0, GetArraySize(tempMaps) - 1);
|
|
GetArrayString(tempMaps, b, map, PLATFORM_MAX_PATH);
|
|
|
|
//if we abandon the feature again its important to just set mce_randomized_maps_from_casual_pool to 0.
|
|
if (pickedFromCasualPool < g_iMapsFromCasualPool)
|
|
{
|
|
if (!InternalGetCasualPool(map))
|
|
{
|
|
continue;
|
|
}
|
|
//requested by keen in september 2024 that we have a pool that is prioritized when taking random maps.
|
|
//it might be considered micro management which would indeed be bad. i guess this is mostly about pleasing a regular players request
|
|
//which does not seem too outlandish to make possible.
|
|
pickedFromCasualPool++;
|
|
break;
|
|
}
|
|
|
|
if(!InternalAreRestrictionsActive(false))
|
|
break;
|
|
|
|
if(InternalGetMapVIPRestriction(map))
|
|
continue;
|
|
|
|
if(InternalGetMapCooldownTime(map) > GetTime())
|
|
continue;
|
|
|
|
if(InternalGetMapTimeRestriction(map) != 0)
|
|
continue;
|
|
|
|
if(InternalGetMapPlayerRestriction(map) != 0)
|
|
continue;
|
|
|
|
if (InternalGetAveragePlayerHourRestriction(map) != 0)
|
|
continue;
|
|
|
|
bool okay = true;
|
|
|
|
int groups[32];
|
|
int groupsfound = InternalGetMapGroups(map, groups, sizeof(groups));
|
|
for(int group = 0; group < groupsfound; group++)
|
|
{
|
|
IntToString(group, groupstr, sizeof(groupstr));
|
|
|
|
int groupmax = InternalGetGroupMax(groups[group]);
|
|
if(groupmax >= 0)
|
|
{
|
|
int groupcur = 0;
|
|
groupmap.GetValue(groupstr, groupcur);
|
|
|
|
if(groupcur >= groupmax)
|
|
{
|
|
okay = false;
|
|
break;
|
|
}
|
|
|
|
groupcur++;
|
|
groupmap.SetValue(groupstr, groupcur, true);
|
|
}
|
|
}
|
|
|
|
if(okay)
|
|
break;
|
|
}
|
|
PushArrayString(g_NextMapList, map);
|
|
RemoveFromArray(tempMaps, b);
|
|
}
|
|
|
|
delete groupmap;
|
|
delete tempMaps;
|
|
}
|
|
|
|
bool CanVoteStart()
|
|
{
|
|
if(g_WaitingForVote || g_HasVoteStarted)
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
NominateResult InternalNominateMap(char[] map, int owner)
|
|
{
|
|
if(!IsMapValid(map))
|
|
{
|
|
return Nominate_InvalidMap;
|
|
}
|
|
|
|
|
|
/* Look to replace an existing nomination by this client - Nominations made with owner = 0 arent replaced */
|
|
//2023 edit: change clients first nomination out of the clients multiple nominations, make a check if client filled all his nomination slots
|
|
//currently hard coded to 3 maps, just add a cvar to replace it with in the future
|
|
if(owner && ((FindValueInArray(g_NominateOwners, owner)) != -1) && GetArraySize(g_NominateList[owner]) > 2)
|
|
{
|
|
char oldmap[PLATFORM_MAX_PATH];
|
|
GetArrayString(g_NominateList[owner], 0, oldmap, PLATFORM_MAX_PATH);
|
|
Call_StartForward(g_NominationsResetForward);
|
|
Call_PushString(oldmap);
|
|
Call_PushCell(owner);
|
|
Call_Finish();
|
|
|
|
RemoveFromArray(g_NominateList[owner], 0);
|
|
PushArrayString(g_NominateList[owner], map);
|
|
return Nominate_Replaced;
|
|
}
|
|
|
|
|
|
/* Too many nominated maps. */
|
|
//2023 edit: we dont want this check
|
|
/*
|
|
if(g_NominateCount >= GetVoteSize(0) && !force)
|
|
{
|
|
return Nominate_VoteFull;
|
|
}
|
|
*/
|
|
|
|
if (owner != 0 && g_NominateList[owner] != INVALID_HANDLE)
|
|
{
|
|
for (int j = 0; j < GetArraySize(g_NominateList[owner]); j++)
|
|
{
|
|
char map_iteration[PLATFORM_MAX_PATH];
|
|
GetArrayString(g_NominateList[owner], j, map_iteration, PLATFORM_MAX_PATH);
|
|
if (StrEqual(map, map_iteration, false))
|
|
{
|
|
return Nominate_InvalidMap;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (g_NominateList[owner] == INVALID_HANDLE)
|
|
{
|
|
if (IsClientConnected(owner) && IsClientInGame(owner))
|
|
{
|
|
ReplyToCommand(owner, "This should be invalid.");
|
|
}
|
|
return Nominate_InvalidMap;
|
|
}
|
|
|
|
PushArrayString(g_NominateList[owner], map);
|
|
PushArrayCell(g_NominateOwners, owner); //maybe i only want to do this for the first nomination of each client
|
|
if(owner == 0 && g_NominateReservedCount < GetVoteSize(0))
|
|
g_NominateReservedCount++;
|
|
else
|
|
g_NominateCount++;
|
|
|
|
while(GetArraySize(g_NominateList[owner]) > GetVoteSize(0))
|
|
{
|
|
char oldmap[PLATFORM_MAX_PATH];
|
|
GetArrayString(g_NominateList[owner], 0, oldmap, PLATFORM_MAX_PATH);
|
|
Call_StartForward(g_NominationsResetForward);
|
|
Call_PushString(oldmap);
|
|
Call_PushCell(owner);
|
|
int owner_ = GetArrayCell(g_NominateOwners, 0);
|
|
Call_Finish();
|
|
|
|
RemoveFromArray(g_NominateList[owner], 0);
|
|
RemoveFromArray(g_NominateOwners, 0);
|
|
if(owner_ == 0)
|
|
g_NominateReservedCount--;
|
|
else
|
|
g_NominateCount--;
|
|
}
|
|
|
|
return Nominate_Added;
|
|
}
|
|
|
|
/* Add natives to allow nominate and initiate vote to be call */
|
|
|
|
/* native bool NominateMap(const char[] map, bool force, &NominateError:error); */
|
|
public int Native_NominateMap(Handle plugin, int numParams)
|
|
{
|
|
int len;
|
|
GetNativeStringLength(1, len);
|
|
|
|
if(len <= 0)
|
|
return false;
|
|
|
|
char[] map = new char[len+1];
|
|
GetNativeString(1, map, len+1);
|
|
|
|
return view_as<int>(InternalNominateMap(map, GetNativeCell(3)));
|
|
}
|
|
|
|
bool InternalRemoveNominationByMap(char[] map)
|
|
{
|
|
for (int client = 0; client < MaxClients; client++)
|
|
{
|
|
for(int i = 0; i < GetArraySize(g_NominateList[client]); i++)
|
|
{
|
|
char oldmap[PLATFORM_MAX_PATH];
|
|
GetArrayString(g_NominateList[client], i, oldmap, PLATFORM_MAX_PATH);
|
|
|
|
if(strcmp(map, oldmap, false) == 0)
|
|
{
|
|
Call_StartForward(g_NominationsResetForward);
|
|
Call_PushString(oldmap);
|
|
Call_PushCell(client);
|
|
Call_Finish();
|
|
|
|
int owner = GetArrayCell(g_NominateOwners, i);
|
|
if(owner)
|
|
g_NominateCount--;
|
|
else
|
|
g_NominateReservedCount--;
|
|
|
|
RemoveFromArray(g_NominateList[client], i);
|
|
RemoveFromArray(g_NominateOwners, i);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* native bool RemoveNominationByMap(const char[] map); */
|
|
public int Native_RemoveNominationByMap(Handle plugin, int numParams)
|
|
{
|
|
int len;
|
|
GetNativeStringLength(1, len);
|
|
|
|
if(len <= 0)
|
|
return false;
|
|
|
|
char[] map = new char[len+1];
|
|
GetNativeString(1, map, len+1);
|
|
|
|
return view_as<int>(InternalRemoveNominationByMap(map));
|
|
}
|
|
|
|
bool InternalRemoveNominationByOwner(int owner)
|
|
{
|
|
int index;
|
|
|
|
if(owner && ((index = FindValueInArray(g_NominateOwners, owner)) != -1))
|
|
{
|
|
char oldmap[PLATFORM_MAX_PATH];
|
|
GetArrayString(g_NominateList[owner], index, oldmap, PLATFORM_MAX_PATH);
|
|
|
|
Call_StartForward(g_NominationsResetForward);
|
|
Call_PushString(oldmap);
|
|
Call_PushCell(owner);
|
|
Call_Finish();
|
|
|
|
RemoveFromArray(g_NominateList[owner], index);
|
|
//maybe only do once or change g_NominateOwners
|
|
RemoveFromArray(g_NominateOwners, index);
|
|
g_NominateCount--;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* native bool RemoveNominationByOwner(owner); */
|
|
public int Native_RemoveNominationByOwner(Handle plugin, int numParams)
|
|
{
|
|
return view_as<int>(InternalRemoveNominationByOwner(GetNativeCell(1)));
|
|
}
|
|
|
|
/* native InitiateMapChooserVote(); */
|
|
public int Native_InitiateVote(Handle plugin, int numParams)
|
|
{
|
|
MapChange when = view_as<MapChange>(GetNativeCell(1));
|
|
Handle inputarray = view_as<Handle>(GetNativeCell(2));
|
|
|
|
int timeleft;
|
|
GetMapTimeLeft(timeleft);
|
|
int total_time = (GetConVarInt(g_Cvar_VoteDuration) * 2) + GetConVarInt(g_Cvar_RunOffWarningTime) + 10;
|
|
if (timeleft <= total_time)
|
|
{
|
|
PrintToChatAll("[RTVE] Rock the vote was cancelled due to not enough time left over.");
|
|
return 0;
|
|
}
|
|
SetupWarningTimer(WarningType_Vote, when, inputarray);
|
|
//LogMessage("SetupWarningTimer 9");
|
|
//PrintToChatAll("SetupWarningTimer 9");
|
|
return 0;
|
|
}
|
|
|
|
//CanMapChooserStartVote
|
|
public int Native_CanVoteStart(Handle plugin, int numParams)
|
|
{
|
|
return CanVoteStart();
|
|
}
|
|
|
|
public int Native_CheckVoteDone(Handle plugin, int numParams)
|
|
{
|
|
return g_MapVoteCompleted;
|
|
}
|
|
|
|
public int Native_EndOfMapVoteEnabled(Handle plugin, int numParams)
|
|
{
|
|
return GetConVarBool(g_Cvar_EndOfMapVote);
|
|
}
|
|
|
|
public int Native_GetExcludeMapList(Handle plugin, int numParams)
|
|
{
|
|
Handle array = view_as<Handle>(GetNativeCell(1));
|
|
if(array == INVALID_HANDLE)
|
|
return 0;
|
|
|
|
static char map[PLATFORM_MAX_PATH];
|
|
StringMapSnapshot OldMapListSnapshot = g_OldMapList.Snapshot();
|
|
for(int i = 0; i < OldMapListSnapshot.Length; i++)
|
|
{
|
|
OldMapListSnapshot.GetKey(i, map, sizeof(map));
|
|
PushArrayString(array, map);
|
|
}
|
|
delete OldMapListSnapshot;
|
|
return 0;
|
|
}
|
|
|
|
//GetNominatedMapList
|
|
public int Native_GetNominatedMapList(Handle plugin, int numParams)
|
|
{
|
|
Handle maparray = view_as<Handle>(GetNativeCell(1));
|
|
Handle ownerarray = view_as<Handle>(GetNativeCell(2));
|
|
|
|
if(maparray == INVALID_HANDLE)
|
|
return 0;
|
|
|
|
static char map[PLATFORM_MAX_PATH];
|
|
for (int client = 0; client < MaxClients; client++)
|
|
{
|
|
for(int i = 0; i < GetArraySize(g_NominateList[client]); i++)
|
|
{
|
|
GetArrayString(g_NominateList[client], i, map, PLATFORM_MAX_PATH);
|
|
PushArrayString(maparray, map);
|
|
|
|
// If the optional parameter for an owner list was passed, then we need to fill that out as well
|
|
if(ownerarray != INVALID_HANDLE)
|
|
{
|
|
//int index = GetArrayCell(g_NominateOwners, i);
|
|
PushArrayCell(ownerarray, client);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Functions new to Mapchooser Extended
|
|
stock void SetupWarningTimer(WarningType type, MapChange when=MapChange_MapEnd, Handle mapList=INVALID_HANDLE, bool force=false)
|
|
{
|
|
if(!GetArraySize(g_MapList) || g_ChangeMapInProgress || g_HasVoteStarted || (!force && ((when == MapChange_MapEnd && !GetConVarBool(g_Cvar_EndOfMapVote)) || g_MapVoteCompleted)))
|
|
return;
|
|
|
|
bool interrupted = false;
|
|
if(g_WarningInProgress && g_WarningTimer != INVALID_HANDLE)
|
|
{
|
|
interrupted = true;
|
|
KillTimer(g_WarningTimer);
|
|
}
|
|
|
|
g_WarningInProgress = true;
|
|
|
|
Handle forwardVote;
|
|
Handle cvarTime;
|
|
static char translationKey[64];
|
|
|
|
switch(type)
|
|
{
|
|
case WarningType_Vote:
|
|
{
|
|
forwardVote = g_MapVoteWarningStartForward;
|
|
cvarTime = g_Cvar_WarningTime;
|
|
if (when == MapChange_MapEnd)
|
|
{
|
|
strcopy(translationKey, sizeof(translationKey), "Vote Warning Extend");
|
|
}
|
|
else
|
|
{
|
|
strcopy(translationKey, sizeof(translationKey), "Vote Warning");
|
|
}
|
|
//LogMessage("Mapchooser_extended_avg WarningType_Vote");
|
|
}
|
|
|
|
case WarningType_Revote:
|
|
{
|
|
forwardVote = g_MapVoteRunoffStartForward;
|
|
cvarTime = g_Cvar_RunOffWarningTime;
|
|
strcopy(translationKey, sizeof(translationKey), "Revote Warning");
|
|
//LogMessage("Mapchooser_extended_avg WarningType_Revote. cvarTime: %i", GetConVarInt(cvarTime));
|
|
}
|
|
}
|
|
|
|
if(!interrupted)
|
|
{
|
|
Call_StartForward(forwardVote);
|
|
Call_Finish();
|
|
}
|
|
|
|
Handle data;
|
|
g_WarningTimer = CreateDataTimer(1.0, Timer_StartMapVote, data, TIMER_FLAG_NO_MAPCHANGE | TIMER_REPEAT);
|
|
WritePackCell(data, GetConVarInt(cvarTime));
|
|
WritePackString(data, translationKey);
|
|
WritePackCell(data, view_as<int>(when));
|
|
WritePackCell(data, view_as<int>(mapList));
|
|
ResetPack(data);
|
|
}
|
|
|
|
stock void InitializeOfficialMapList()
|
|
{
|
|
// If this fails, we want it to have an empty adt_array
|
|
if(ReadMapList(g_OfficialList,
|
|
g_mapOfficialFileSerial,
|
|
"official",
|
|
MAPLIST_FLAG_CLEARARRAY|MAPLIST_FLAG_NO_DEFAULT)
|
|
!= INVALID_HANDLE)
|
|
{
|
|
LogMessage("Loaded map list for %s.", g_GameModName);
|
|
}
|
|
// Check if the map list was ever loaded
|
|
else if(g_mapOfficialFileSerial == -1)
|
|
{
|
|
LogMessage("No official map list found for %s. Consider submitting one!", g_GameModName);
|
|
}
|
|
}
|
|
|
|
stock bool IsMapEndVoteAllowed()
|
|
{
|
|
if(!GetConVarBool(g_Cvar_EndOfMapVote) || g_MapVoteCompleted || g_HasVoteStarted)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
public int Native_IsMapOfficial(Handle plugin, int numParams)
|
|
{
|
|
int len;
|
|
GetNativeStringLength(1, len);
|
|
|
|
if(len <= 0)
|
|
return false;
|
|
|
|
char[] map = new char[len+1];
|
|
GetNativeString(1, map, len+1);
|
|
|
|
return InternalIsMapOfficial(map);
|
|
}
|
|
|
|
bool InternalIsMapOfficial(const char[] mapname)
|
|
{
|
|
int officialMapIndex = FindStringInArray(g_OfficialList, mapname);
|
|
return (officialMapIndex > -1);
|
|
}
|
|
|
|
public int Native_IsWarningTimer(Handle plugin, int numParams)
|
|
{
|
|
return g_WarningInProgress;
|
|
}
|
|
|
|
public int Native_CanNominate(Handle plugin, int numParams)
|
|
{
|
|
if(g_HasVoteStarted)
|
|
{
|
|
return view_as<int>(CanNominate_No_VoteInProgress);
|
|
}
|
|
|
|
if(g_MapVoteCompleted)
|
|
{
|
|
return view_as<int>(CanNominate_No_VoteComplete);
|
|
}
|
|
|
|
if(g_NominateCount >= GetVoteSize())
|
|
{
|
|
return view_as<int>(CanNominate_No_VoteFull);
|
|
}
|
|
|
|
return view_as<int>(CanNominate_Yes);
|
|
}
|
|
|
|
public int Native_ExcludeMap(Handle plugin, int numParams)
|
|
{
|
|
if(!InternalAreRestrictionsActive(false))
|
|
return true;
|
|
|
|
int len;
|
|
GetNativeStringLength(1, len);
|
|
|
|
if(len <= 0)
|
|
return false;
|
|
|
|
char[] map = new char[len+1];
|
|
GetNativeString(1, map, len+1);
|
|
|
|
int Cooldown;
|
|
int Mode = GetNativeCell(3);
|
|
|
|
if(Mode == 0)
|
|
{
|
|
Cooldown = InternalGetMapCooldown(map);
|
|
}
|
|
else if(Mode == 1)
|
|
{
|
|
Cooldown = GetNativeCell(2);
|
|
}
|
|
else if(Mode == 2)
|
|
{
|
|
g_OldMapList.GetValue(map, Cooldown);
|
|
int NewCooldown = GetNativeCell(2);
|
|
if(NewCooldown > Cooldown)
|
|
Cooldown = NewCooldown;
|
|
}
|
|
|
|
g_OldMapList.SetValue(map, Cooldown, true);
|
|
InternalStoreMapCooldowns();
|
|
|
|
return true;
|
|
}
|
|
|
|
public int Native_ExcludeMapTime(Handle plugin, int numParams)
|
|
{
|
|
if(!InternalAreRestrictionsActive(false))
|
|
return true;
|
|
|
|
int len;
|
|
GetNativeStringLength(1, len);
|
|
|
|
if(len <= 0)
|
|
return false;
|
|
|
|
char[] map = new char[len+1];
|
|
GetNativeString(1, map, len+1);
|
|
|
|
int Cooldown;
|
|
int Mode = GetNativeCell(3);
|
|
|
|
if(Mode == 0)
|
|
{
|
|
Cooldown = InternalGetMapCooldownTime(map);
|
|
}
|
|
else if(Mode == 1)
|
|
{
|
|
Cooldown = GetNativeCell(2);
|
|
}
|
|
else if(Mode == 2)
|
|
{
|
|
g_TimeMapList.GetValue(map, Cooldown);
|
|
int NewCooldown = GetTime() + GetNativeCell(2);
|
|
if(NewCooldown > Cooldown)
|
|
Cooldown = GetNativeCell(2);
|
|
else
|
|
return true;
|
|
}
|
|
|
|
Cooldown += GetTime();
|
|
g_TimeMapList.SetValue(map, Cooldown, true);
|
|
InternalStoreMapCooldowns();
|
|
|
|
return true;
|
|
}
|
|
|
|
public int Native_GetMapCooldown(Handle plugin, int numParams)
|
|
{
|
|
if(!InternalAreRestrictionsActive(false))
|
|
return 0;
|
|
|
|
int len;
|
|
GetNativeStringLength(1, len);
|
|
|
|
if(len <= 0)
|
|
return false;
|
|
|
|
char[] map = new char[len+1];
|
|
GetNativeString(1, map, len+1);
|
|
|
|
int Cooldown = 0;
|
|
g_OldMapList.GetValue(map, Cooldown);
|
|
|
|
return Cooldown;
|
|
}
|
|
|
|
public int Native_GetMapCooldownTime(Handle plugin, int numParams)
|
|
{
|
|
if(!InternalAreRestrictionsActive(false))
|
|
return 0;
|
|
|
|
int len;
|
|
GetNativeStringLength(1, len);
|
|
|
|
if(len <= 0)
|
|
return false;
|
|
|
|
char[] map = new char[len+1];
|
|
GetNativeString(1, map, len+1);
|
|
|
|
int Cooldown = 0;
|
|
g_TimeMapList.GetValue(map, Cooldown);
|
|
|
|
return Cooldown;
|
|
}
|
|
|
|
public int Native_GetMapMinTime(Handle plugin, int numParams)
|
|
{
|
|
int len;
|
|
GetNativeStringLength(1, len);
|
|
|
|
if(len <= 0)
|
|
return false;
|
|
|
|
char[] map = new char[len+1];
|
|
GetNativeString(1, map, len+1);
|
|
|
|
return InternalGetMapMinTime(map);
|
|
}
|
|
|
|
public int Native_GetMapMaxTime(Handle plugin, int numParams)
|
|
{
|
|
int len;
|
|
GetNativeStringLength(1, len);
|
|
|
|
if(len <= 0)
|
|
return false;
|
|
|
|
char[] map = new char[len+1];
|
|
GetNativeString(1, map, len+1);
|
|
|
|
return InternalGetMapMaxTime(map);
|
|
}
|
|
|
|
public int Native_GetMapMinPlayers(Handle plugin, int numParams)
|
|
{
|
|
int len;
|
|
GetNativeStringLength(1, len);
|
|
|
|
if(len <= 0)
|
|
return false;
|
|
|
|
char[] map = new char[len+1];
|
|
GetNativeString(1, map, len+1);
|
|
|
|
return InternalGetMapMinPlayers(map);
|
|
}
|
|
|
|
public int Native_GetMapMaxPlayers(Handle plugin, int numParams)
|
|
{
|
|
int len;
|
|
GetNativeStringLength(1, len);
|
|
|
|
if(len <= 0)
|
|
return false;
|
|
|
|
char[] map = new char[len+1];
|
|
GetNativeString(1, map, len+1);
|
|
|
|
return InternalGetMapMaxPlayers(map);
|
|
}
|
|
|
|
public int Native_GetMapTimeRestriction(Handle plugin, int numParams)
|
|
{
|
|
int len;
|
|
GetNativeStringLength(1, len);
|
|
|
|
if(len <= 0)
|
|
return false;
|
|
|
|
char[] map = new char[len+1];
|
|
GetNativeString(1, map, len+1);
|
|
|
|
return InternalGetMapTimeRestriction(map);
|
|
}
|
|
|
|
//GetAveragePlayerTimeOnServerMapRestriction
|
|
public int Native_GetAveragePlayerTimeOnServerMapRestriction(Handle plugin, int numParams)
|
|
{
|
|
int len;
|
|
GetNativeStringLength(1, len);
|
|
|
|
if(len <= 0)
|
|
return false;
|
|
|
|
char[] map = new char[len+1];
|
|
GetNativeString(1, map, len+1);
|
|
return InternalGetAveragePlayerHourRestriction(map);
|
|
}
|
|
|
|
stock int InternalGetAveragePlayerHourRestriction(const char[] map)
|
|
{
|
|
int players_average_hours = GetAveragePlayerTimeOnServer();
|
|
int MinAverageHours = 0;
|
|
if(g_Config && g_Config.JumpToKey(map))
|
|
{
|
|
MinAverageHours = g_Config.GetNum("MinHoursAvg", MinAverageHours);
|
|
g_Config.Rewind();
|
|
}
|
|
//0 means map can be nominated, anything above 0 means more hours are required
|
|
if (players_average_hours >= MinAverageHours)
|
|
{
|
|
return 0;
|
|
}
|
|
return MinAverageHours - players_average_hours;
|
|
}
|
|
//GetMapPlayerRestriction
|
|
public int Native_GetMapPlayerRestriction(Handle plugin, int numParams)
|
|
{
|
|
int len;
|
|
GetNativeStringLength(1, len);
|
|
|
|
if(len <= 0)
|
|
return false;
|
|
|
|
char[] map = new char[len+1];
|
|
GetNativeString(1, map, len+1);
|
|
|
|
return InternalGetMapPlayerRestriction(map);
|
|
}
|
|
|
|
public int Native_GetMapGroups(Handle plugin, int numParams)
|
|
{
|
|
int len;
|
|
GetNativeStringLength(1, len);
|
|
int size = GetNativeCell(3);
|
|
|
|
if(len <= 0)
|
|
return -999;
|
|
|
|
char[] map = new char[len+1];
|
|
GetNativeString(1, map, len+1);
|
|
|
|
int[] groups = new int[size];
|
|
int found = InternalGetMapGroups(map, groups, size);
|
|
if(found >= 0)
|
|
SetNativeArray(2, groups, size);
|
|
return found;
|
|
}
|
|
|
|
//GetMapGroupRestriction
|
|
public int Native_GetMapGroupRestriction(Handle plugin, int numParams)
|
|
{
|
|
GetNativeCell(2);
|
|
int len;
|
|
GetNativeStringLength(1, len);
|
|
|
|
if(len <= 0)
|
|
return -999;
|
|
|
|
char[] map = new char[len+1];
|
|
GetNativeString(1, map, len+1);
|
|
|
|
int groups[32];
|
|
int groupsfound = InternalGetMapGroups(map, groups, sizeof(groups));
|
|
|
|
for(int group = 0; group < groupsfound; group ++)
|
|
{
|
|
int groupcur = 0;
|
|
int groupmax = InternalGetGroupMax(groups[group]);
|
|
|
|
if(groupmax >= 0)
|
|
{
|
|
static char map_[PLATFORM_MAX_PATH];
|
|
char kv_map[PLATFORM_MAX_PATH];
|
|
int groups_[32];
|
|
KeyValues kv = CreateKeyValues("cur_groups");
|
|
for (int clienti = 0; clienti < MaxClients; clienti++)
|
|
{
|
|
for(int i = 0; i < GetArraySize(g_NominateList[clienti]); i++)
|
|
{
|
|
GetArrayString(g_NominateList[clienti], i, map_, PLATFORM_MAX_PATH);
|
|
int tmp = InternalGetMapGroups(map_, groups_, sizeof(groups_));
|
|
kv.GetString(map_, kv_map, sizeof(kv_map), "not_found");
|
|
if(FindIntInArray(groups_, tmp, groups[group]) != -1 && StrEqual(kv_map, "not_found"))
|
|
{
|
|
groupcur++;
|
|
kv.SetString(map_, map_);
|
|
}
|
|
}
|
|
}
|
|
delete kv;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
public int Native_GetMapVIPRestriction(Handle plugin, int numParams)
|
|
{
|
|
int client = GetNativeCell(2);
|
|
int len;
|
|
GetNativeStringLength(1, len);
|
|
|
|
if(len <= 0)
|
|
return false;
|
|
|
|
char[] map = new char[len+1];
|
|
GetNativeString(1, map, len+1);
|
|
|
|
// Check if client should bypass vip restrictions
|
|
if(client >= 1 && client <= MaxClients)
|
|
{
|
|
// Client has bypass flag, dont return vip restrictions
|
|
if(CheckCommandAccess(client, "sm_nominate_ignore", ADMFLAG_CHEATS))
|
|
return false;
|
|
|
|
// Client has vip flag, dont return vip restrictions
|
|
if(CheckCommandAccess(client, "sm_nominate_vip", ADMFLAG_CUSTOM1))
|
|
return false;
|
|
}
|
|
|
|
return InternalGetMapVIPRestriction(map);
|
|
}
|
|
|
|
public int Native_GetExtendsLeft(Handle plugin, int numParams)
|
|
{
|
|
return GetConVarInt(g_Cvar_Extend) - g_Extends;
|
|
}
|
|
|
|
public int Native_AreRestrictionsActive(Handle plugin, int numParams)
|
|
{
|
|
return InternalAreRestrictionsActive(false);
|
|
}
|
|
|
|
public int Native_SimulateMapEnd(Handle plugin, int numParams)
|
|
{
|
|
OnMapEnd();
|
|
return 0;
|
|
}
|
|
|
|
stock void AddMapItem(const char[] map)
|
|
{
|
|
AddMenuItem(g_VoteMenu, map, map);
|
|
}
|
|
|
|
stock void GetMapItem(Handle menu, int position, char[] map, int mapLen)
|
|
{
|
|
GetMenuItem(menu, position, map, mapLen, _, _, _, -1);
|
|
}
|
|
|
|
stock void AddExtendToMenu(Handle menu, MapChange when)
|
|
{
|
|
/* Do we add any special items? */
|
|
// Moved for Mapchooser Extended
|
|
if (g_iNextmapState != 0) //we return if admin set next map. if all extends were used or map was not extended.
|
|
{
|
|
return;
|
|
}
|
|
|
|
if((when == MapChange_Instant || when == MapChange_RoundEnd) && GetConVarBool(g_Cvar_DontChange))
|
|
{
|
|
// Built-in votes doesnt have "Dont Change", send Extend instead
|
|
AddMenuItem(menu, VOTE_DONTCHANGE, "Dont Change");
|
|
}
|
|
else if(GetConVarBool(g_Cvar_Extend) && g_Extends < GetConVarInt(g_Cvar_Extend))
|
|
{
|
|
AddMenuItem(menu, VOTE_EXTEND, "Extend Map");
|
|
}
|
|
}
|
|
|
|
// 0 = IncludeMaps, 1 = Reserved, 2 = IncludeMaps+Reserved
|
|
stock int GetVoteSize(int what=0)
|
|
{
|
|
int includeMaps = GetConVarInt(g_Cvar_IncludeMaps);
|
|
int includeMapsReserved = GetConVarInt(g_Cvar_IncludeMapsReserved);
|
|
|
|
if(what == 0)
|
|
return includeMaps;
|
|
else if(what == 1)
|
|
return includeMapsReserved;
|
|
else if(what == 2)
|
|
return includeMaps + includeMapsReserved;
|
|
|
|
return 0;
|
|
}
|
|
|
|
stock int InternalGetMapCooldown(const char[] map)
|
|
{
|
|
int Cooldown = g_Cvar_ExcludeMaps.IntValue;
|
|
|
|
if(g_Config && g_Config.JumpToKey(map))
|
|
{
|
|
Cooldown = g_Config.GetNum("Cooldown", Cooldown);
|
|
g_Config.Rewind();
|
|
}
|
|
|
|
return Cooldown;
|
|
}
|
|
|
|
stock int InternalGetMapCooldownTime(const char[] map)
|
|
{
|
|
char time[16];
|
|
g_Cvar_ExcludeMapsTime.GetString(time, sizeof(time));
|
|
int Cooldown = TimeStrToSeconds(time);
|
|
|
|
if(g_Config && g_Config.JumpToKey(map))
|
|
{
|
|
g_Config.GetString("CooldownTime", time, sizeof(time), "");
|
|
if(time[0])
|
|
Cooldown = TimeStrToSeconds(time);
|
|
|
|
g_Config.Rewind();
|
|
}
|
|
|
|
return Cooldown;
|
|
}
|
|
|
|
stock int InternalGetMapMinTime(const char[] map)
|
|
{
|
|
int MinTime = 0;
|
|
|
|
if(g_Config && g_Config.JumpToKey(map))
|
|
{
|
|
MinTime = g_Config.GetNum("MinTime", MinTime);
|
|
g_Config.Rewind();
|
|
}
|
|
|
|
return MinTime;
|
|
}
|
|
|
|
stock int InternalGetMapMaxTime(const char[] map)
|
|
{
|
|
int MaxTime = 0;
|
|
|
|
if(g_Config && g_Config.JumpToKey(map))
|
|
{
|
|
MaxTime = g_Config.GetNum("MaxTime", MaxTime);
|
|
g_Config.Rewind();
|
|
}
|
|
|
|
return MaxTime;
|
|
}
|
|
|
|
stock int InternalGetMapMinPlayers(const char[] map)
|
|
{
|
|
int MinPlayers = 0;
|
|
|
|
if(g_Config && g_Config.JumpToKey(map))
|
|
{
|
|
MinPlayers = g_Config.GetNum("MinPlayers", MinPlayers);
|
|
g_Config.Rewind();
|
|
}
|
|
|
|
return MinPlayers;
|
|
}
|
|
|
|
stock int InternalGetMapMaxPlayers(const char[] map)
|
|
{
|
|
int MaxPlayers = 0;
|
|
|
|
if(g_Config && g_Config.JumpToKey(map))
|
|
{
|
|
MaxPlayers = g_Config.GetNum("MaxPlayers", MaxPlayers);
|
|
g_Config.Rewind();
|
|
}
|
|
|
|
return MaxPlayers;
|
|
}
|
|
|
|
stock int InternalGetMapGroups(const char[] map, int[] groups, int size)
|
|
{
|
|
int found = 0;
|
|
if(g_Config && g_Config.JumpToKey("_groups"))
|
|
{
|
|
if(!g_Config.GotoFirstSubKey(false))
|
|
{
|
|
g_Config.Rewind();
|
|
return -999;
|
|
}
|
|
|
|
do
|
|
{
|
|
char groupstr[8];
|
|
g_Config.GetSectionName(groupstr, sizeof(groupstr));
|
|
int group = StringToInt(groupstr);
|
|
if(g_Config.JumpToKey(map, false))
|
|
{
|
|
groups[found++] = group;
|
|
if(found >= size)
|
|
{
|
|
g_Config.Rewind();
|
|
return found;
|
|
}
|
|
g_Config.GoBack();
|
|
}
|
|
} while(g_Config.GotoNextKey());
|
|
|
|
g_Config.Rewind();
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
stock void InternalGetCvars()
|
|
{
|
|
if (g_Config && g_Config.JumpToKey("_cvars"))
|
|
{
|
|
g_Config.GotoFirstSubKey(false);
|
|
char cvar_name[256];
|
|
char cvar_value[256];
|
|
do
|
|
{
|
|
g_Config.GetSectionName(cvar_name, sizeof(cvar_name));
|
|
g_Config.GetString(NULL_STRING, cvar_value, sizeof(cvar_value));
|
|
ServerCommand("sm_cvar %s %s", cvar_name, cvar_value);
|
|
} while (g_Config.GotoNextKey(false));
|
|
g_Config.Rewind();
|
|
}
|
|
}
|
|
|
|
stock int InternalGetGroupMax(int group)
|
|
{
|
|
char groupstr[8];
|
|
IntToString(group, groupstr, sizeof(groupstr));
|
|
if(g_Config && g_Config.JumpToKey("_groups"))
|
|
{
|
|
if(g_Config.JumpToKey(groupstr, false))
|
|
{
|
|
int max = g_Config.GetNum("_max", -1);
|
|
g_Config.Rewind();
|
|
return max;
|
|
}
|
|
|
|
g_Config.Rewind();
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// 0 = Okay
|
|
// >0 = Minutes till Okay
|
|
stock int InternalGetMapTimeRestriction(const char[] map)
|
|
{
|
|
char sTime[8];
|
|
FormatTime(sTime, sizeof(sTime), "%H%M");
|
|
|
|
int CurTime = StringToInt(sTime);
|
|
int MinTime = InternalGetMapMinTime(map);
|
|
int MaxTime = InternalGetMapMaxTime(map);
|
|
|
|
//Wrap around.
|
|
CurTime = (CurTime <= MinTime) ? CurTime + 2400 : CurTime;
|
|
MaxTime = (MaxTime <= MinTime) ? MaxTime + 2400 : MaxTime;
|
|
|
|
if (!(MinTime <= CurTime <= MaxTime))
|
|
{
|
|
//Wrap around.
|
|
MinTime = (MinTime <= CurTime) ? MinTime + 2400 : MinTime;
|
|
MinTime = (MinTime <= MaxTime) ? MinTime + 2400 : MinTime;
|
|
|
|
// Convert our 'time' to minutes.
|
|
CurTime = (RoundToFloor(float(CurTime / 100)) * 60) + (CurTime % 100);
|
|
MinTime = (RoundToFloor(float(MinTime / 100)) * 60) + (MinTime % 100);
|
|
MaxTime = (RoundToFloor(float(MaxTime / 100)) * 60) + (MaxTime % 100);
|
|
|
|
return MinTime - CurTime;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
public void OnClientPostAdminCheck(int client)
|
|
{
|
|
is_bot_player[client] = false;
|
|
player_mapvote_worth[client] = 0.0;
|
|
Format(player_mapvote[client], 128, "");
|
|
char auth[50];
|
|
GetClientAuthId(client, AuthId_Engine, auth, sizeof(auth));
|
|
if (StrEqual("[U:1:1221121532]", auth, false) || StrEqual("STEAM_0:0:610560766", auth, false))
|
|
{
|
|
is_bot_player[client] = true;
|
|
}
|
|
if (StrEqual("[U:1:408797742]", auth, false) || StrEqual("STEAM_0:0:204398871", auth, false))
|
|
{
|
|
is_bot_player[client] = true;
|
|
}
|
|
if (StrEqual("[U:1:1036189204]", auth, false) || StrEqual("STEAM_0:0:518094602", auth, false))
|
|
{
|
|
is_bot_player[client] = true;
|
|
}
|
|
if (StrEqual("[U:1:120378081]", auth, false) || StrEqual("STEAM_0:1:60189040", auth, false))
|
|
{
|
|
is_bot_player[client] = true;
|
|
}
|
|
}
|
|
|
|
// <0 = Less than MinPlayers
|
|
// 0 = Okay
|
|
// >0 = More than MaxPlayers
|
|
stock int InternalGetMapPlayerRestriction(const char[] map)
|
|
{
|
|
//int NumPlayers = GetClientCount(false);
|
|
int NumPlayers = 0;
|
|
//excluding non connected players, fakeclients, sourceTV, autism bots and nosteamers from player count restriction
|
|
for (int client = 1; client <= MaxClients; client++)
|
|
{
|
|
if (IsClientConnected(client) && IsClientInGame(client) && !IsFakeClient(client) && !IsClientSourceTV(client) && !is_bot_player[client]
|
|
&& PM_IsPlayerSteam(client))
|
|
{
|
|
NumPlayers++;
|
|
}
|
|
}
|
|
|
|
int MinPlayers = InternalGetMapMinPlayers(map);
|
|
int MaxPlayers = InternalGetMapMaxPlayers(map);
|
|
|
|
if(MinPlayers && NumPlayers < MinPlayers)
|
|
return NumPlayers - MinPlayers;
|
|
|
|
if(MaxPlayers && NumPlayers > MaxPlayers)
|
|
return NumPlayers - MaxPlayers;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
false means we never put the map on cooldown if time range is inside the no map restriction time or
|
|
if less than the minimum active players for enabling map restrictions are on the server.
|
|
|
|
true means we put maps on Cooldown even if there was no map restrictions because of less than 15 active players (the number is a cvar, its not static)
|
|
true still respects the time range with no map restrictions (23:30 to 10:30 right now) and wont apply cooldown there.
|
|
*/
|
|
stock bool InternalAreRestrictionsActive(bool skip_player_check)
|
|
{
|
|
if (!GetConVarBool(g_Cvar_NoRestrictionTimeframeEnable))
|
|
return true;
|
|
|
|
char sTime[8];
|
|
FormatTime(sTime, sizeof(sTime), "%H%M");
|
|
|
|
int CurTime = StringToInt(sTime);
|
|
int MinTime = GetConVarInt(g_Cvar_NoRestrictionTimeframeMinTime);
|
|
int MaxTime = GetConVarInt(g_Cvar_NoRestrictionTimeframeMaxTime);
|
|
|
|
//Wrap around.
|
|
CurTime = (CurTime <= MinTime) ? CurTime + 2400 : CurTime;
|
|
MaxTime = (MaxTime <= MinTime) ? MaxTime + 2400 : MaxTime;
|
|
|
|
if ((MinTime <= CurTime <= MaxTime))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int ActivePlayerCount = 0;
|
|
for (int i = 0; i < MaxClients; i++)
|
|
{
|
|
if (IsValidClient(i) && !IsFakeClient(i) && !IsClientSourceTV(i) && !is_bot_player[i] && GetClientIdleTime(i) < g_iPlayerAFKTime
|
|
&& (GetClientTeam(i) == CS_TEAM_T || GetClientTeam(i) == CS_TEAM_CT))
|
|
{
|
|
ActivePlayerCount++;
|
|
}
|
|
}
|
|
|
|
if (ActivePlayerCount <= g_iPlayerCount_excludeSpec && !skip_player_check)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
stock int FindIntInArray(int[] array, int size, int value)
|
|
{
|
|
for(int i = 0; i < size; i++)
|
|
{
|
|
if(array[i] == value)
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
stock bool InternalGetCasualPool(const char[] map)
|
|
{
|
|
int CasualPool = 0;
|
|
if(g_Config && g_Config.JumpToKey(map))
|
|
{
|
|
CasualPool = g_Config.GetNum("CasualPool", CasualPool);
|
|
g_Config.Rewind();
|
|
}
|
|
|
|
return view_as<bool>(CasualPool);
|
|
}
|
|
|
|
stock bool InternalGetMapVIPRestriction(const char[] map)
|
|
{
|
|
int VIP = 0;
|
|
|
|
if(g_Config && g_Config.JumpToKey(map))
|
|
{
|
|
VIP = g_Config.GetNum("VIP", VIP);
|
|
g_Config.Rewind();
|
|
}
|
|
|
|
return view_as<bool>(VIP);
|
|
}
|
|
|
|
stock void InternalRestoreMapCooldowns()
|
|
{
|
|
char sCooldownFile[PLATFORM_MAX_PATH];
|
|
BuildPath(Path_SM, sCooldownFile, sizeof(sCooldownFile), "configs/mapchooser_extended/cooldowns.cfg");
|
|
|
|
if(!FileExists(sCooldownFile))
|
|
{
|
|
LogMessage("Could not find cooldown file: \"%s\"", sCooldownFile);
|
|
return;
|
|
}
|
|
|
|
KeyValues Cooldowns = new KeyValues("mapchooser_extended");
|
|
|
|
if(!Cooldowns.ImportFromFile(sCooldownFile))
|
|
{
|
|
LogMessage("Unable to load cooldown file: \"%s\"", sCooldownFile);
|
|
delete Cooldowns;
|
|
return;
|
|
}
|
|
|
|
if(!Cooldowns.GotoFirstSubKey(true))
|
|
{
|
|
LogMessage("Unable to goto first sub key: \"%s\"", sCooldownFile);
|
|
delete Cooldowns;
|
|
return;
|
|
}
|
|
|
|
int Cooldown;
|
|
char map[PLATFORM_MAX_PATH];
|
|
|
|
do
|
|
{
|
|
if(!Cooldowns.GetSectionName(map, sizeof(map)))
|
|
{
|
|
LogMessage("Unable to get section name: \"%s\"", sCooldownFile);
|
|
delete Cooldowns;
|
|
return;
|
|
}
|
|
|
|
if((Cooldown = Cooldowns.GetNum("Cooldown", -1)) > 0)
|
|
{
|
|
LogMessage("Restored cooldown: %s -> %d", map, Cooldown);
|
|
g_OldMapList.SetValue(map, Cooldown, true);
|
|
}
|
|
|
|
if((Cooldown = Cooldowns.GetNum("CooldownTime", -1)) > 0)
|
|
{
|
|
LogMessage("Restored time cooldown: %s -> %d", map, Cooldown);
|
|
g_TimeMapList.SetValue(map, Cooldown, true);
|
|
}
|
|
} while(Cooldowns.GotoNextKey(true));
|
|
|
|
delete Cooldowns;
|
|
}
|
|
|
|
stock void InternalStoreMapCooldowns()
|
|
{
|
|
char sCooldownFile[PLATFORM_MAX_PATH];
|
|
BuildPath(Path_SM, sCooldownFile, sizeof(sCooldownFile), "configs/mapchooser_extended/cooldowns.cfg");
|
|
|
|
if(!FileExists(sCooldownFile))
|
|
{
|
|
LogMessage("Could not find cooldown file: \"%s\"", sCooldownFile);
|
|
return;
|
|
}
|
|
|
|
KeyValues Cooldowns = new KeyValues("mapchooser_extended");
|
|
|
|
int Cooldown;
|
|
char map[PLATFORM_MAX_PATH];
|
|
|
|
StringMapSnapshot OldMapListSnapshot = g_OldMapList.Snapshot();
|
|
for(int i = 0; i < OldMapListSnapshot.Length; i++)
|
|
{
|
|
OldMapListSnapshot.GetKey(i, map, sizeof(map));
|
|
g_OldMapList.GetValue(map, Cooldown);
|
|
|
|
if (!Cooldowns.JumpToKey(map, true))
|
|
{
|
|
LogMessage("Unable to create/find key: %s", map);
|
|
delete OldMapListSnapshot;
|
|
delete Cooldowns;
|
|
return;
|
|
}
|
|
|
|
Cooldowns.SetNum("Cooldown", Cooldown);
|
|
Cooldowns.Rewind();
|
|
}
|
|
delete OldMapListSnapshot;
|
|
|
|
StringMapSnapshot TimeMapListSnapshot = g_TimeMapList.Snapshot();
|
|
for(int i = 0; i < TimeMapListSnapshot.Length; i++)
|
|
{
|
|
TimeMapListSnapshot.GetKey(i, map, sizeof(map));
|
|
g_TimeMapList.GetValue(map, Cooldown);
|
|
|
|
if (!Cooldowns.JumpToKey(map, true))
|
|
{
|
|
LogMessage("Unable to create/find key: %s", map);
|
|
delete TimeMapListSnapshot;
|
|
delete Cooldowns;
|
|
return;
|
|
}
|
|
|
|
Cooldowns.SetNum("CooldownTime", Cooldown);
|
|
Cooldowns.Rewind();
|
|
}
|
|
delete TimeMapListSnapshot;
|
|
|
|
if(!Cooldowns.ExportToFile(sCooldownFile))
|
|
{
|
|
LogMessage("Unable to export cooldown file: \"%s\"", sCooldownFile);
|
|
delete Cooldowns;
|
|
return;
|
|
}
|
|
|
|
delete Cooldowns;
|
|
}
|
|
|
|
stock int TimeStrToSeconds(const char[] str)
|
|
{
|
|
int seconds = 0;
|
|
int maxlen = strlen(str);
|
|
for(int i = 0; i < maxlen;)
|
|
{
|
|
int val = 0;
|
|
i += StringToIntEx(str[i], val);
|
|
if(str[i] == 'h')
|
|
{
|
|
val *= 60;
|
|
i++;
|
|
}
|
|
seconds += val * 60;
|
|
}
|
|
return seconds;
|
|
}
|
|
|
|
stock bool IsValidClient(int client)
|
|
{
|
|
if (client > 0 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client) && !IsFakeClient(client) && !IsClientSourceTV(client))
|
|
return true;
|
|
return false;
|
|
}
|