Playersvotes-redux  [На сервере]

Плагины и дополнения / Plugins and add-ons

Модераторы: Dame-danners, Longipongeoos

Аватар пользователя
Longipongeoos
Сообщений: 189
Зарегистрирован: 12 апр 2017, 16:50

Playersvotes-redux  [На сервере]

Сообщение Longipongeoos » 08 апр 2023, 09:43

Описание
Данный плагин представляет функции voteban, votemute, votekick, votemap, все те функции которые требуют голосования между игроками на сервере. Гибкие настройки, есть возможность присвоить иммунитет администраторам и т.п.

Код: Выбрать все

#pragma semicolon 1

// ====[ INCLUDES ]============================================================
#include <sourcemod>
#include <sdktools>
#include <nextmap>
#include <customvotes>
#include <playersvotes>
#tryinclude <basecomm>

// ====[ DEFINES ]=============================================================
#define PLUGIN_NAME "Players Votes Redux"
#define PLUGIN_VERSION "1.3"

// ====[ HANDLES | CVARS ]=====================================================

//  __  __      _
// |  \/  |_  _| |_ ___
// | |\/| | || |  _/ -_)
// |_|  |_|\_,_|\__`___|
//

new Handle:g_hArrayVoteMuteClientIdentity;

//  ___
// | _ ) __ _ _ _
// | _ \/ _` | ' `
// |___/\__,_|_||_|
//

new Handle:g_hCvarVoteBanSB;
new Handle:g_hArrayVoteBanClientUserIds;
new Handle:g_hArrayVoteBanClientCurrentUserId;
new Handle:g_hArrayVoteBanClientIdentity;
new Handle:g_hArrayVoteBanClientNames;
new Handle:g_hArrayVoteBanClientTeam;
new Handle:g_hArrayVoteBanReasons;
new Handle:g_hArrayVoteBanFor[MAXPLAYERS + 1];
new Handle:g_hArrayVoteBanForReason[MAXPLAYERS + 1];

//  __  __
// |  \/  |__ _ _ __
// | |\/| / _` | '_ `
// |_|  |_`__,_| .__/
//

new Handle:g_hCvarVoteMapTimeLimit;
new Handle:g_hArrayVoteMapLastMaps;
new Handle:g_hArrayVoteMapList;
new Handle:g_hArrayVotedForMap[MAXPLAYERS + 1];

// ====[ VARIABLES ]===========================================================
new g_iStartTime;
new bool:g_bImmune[MAXPLAYERS + 1];
new String:g_strConfigFile[255];
new bool:g_bChatTriggers;
new g_iVoteImmunity;

//  _  ___    _
// | |/ (_)__| |__
// | ' <| / _| / /
// |_|\_`_`__|_`_`
//

new bool:g_bVoteKickEnabled;
new Float:g_flVoteKickRatio;
new g_iVoteKickMinimum;
new g_iVoteKickDelay;
new g_iVoteKickLimit;
new g_iVoteKickLast[MAXPLAYERS + 1];
new g_iVoteKickInterval;
new bool:g_bVoteKickTeam;
new g_iVoteKickCount[MAXPLAYERS + 1];
new bool:g_bVoteKickFor[MAXPLAYERS + 1][MAXPLAYERS + 1];

//  ___
// | _ ) __ _ _ _
// | _ \/ _` | ' `
// |___/\__,_|_||_|
//

new bool:g_bVoteBanEnabled;
new Float:g_flVoteBanRatio;
new g_iVoteBanMinimum;
new g_iVoteBanDelay;
new g_iVoteBanLimit;
new g_iVoteBanInterval;
new g_iVoteBanLast[MAXPLAYERS + 1];
new bool:g_bVoteBanTeam;
new g_iVoteBanTime;
new String:g_strVoteBanReasons[256];
new g_iVoteBanCount[MAXPLAYERS + 1];
new g_iVoteBanClients[MAXPLAYERS + 1] = {-1, ...};

//  __  __
// |  \/  |__ _ _ __
// | |\/| / _` | '_ `
// |_|  |_`__,_| .__/
//

new bool:g_bVoteMapEnabled;
new Float:g_flVoteMapRatio;
new g_iVoteMapMinimum;
new g_iVoteMapDelay;
new g_iVoteMapLimit;
new g_iVoteMapInterval;
new g_iVoteMapLast[MAXPLAYERS + 1];
new g_iVoteMapLastMaps;
new g_iVoteMapExtendTime;
new g_iVoteMapMaxExtends;
new bool:g_bVoteMapMode;
new g_iVoteMapCount[MAXPLAYERS + 1];
new g_iVoteMapListSerial = -1;
new g_iVoteMapCurrent;

//  __  __      _
// |  \/  |_  _| |_ ___
// | |\/| | || |  _/ -_)
// |_|  |_|\_,_|\__`___|
//

new bool:g_bVoteMuteEnabled;
new Float:g_flVoteMuteRatio;
new g_iVoteMuteMinimum;
new g_iVoteMuteDelay;
new g_iVoteMuteLimit;
new g_iVoteMuteInterval;
new g_iVoteMuteLast[MAXPLAYERS + 1];
new bool:g_bVoteMuteTeam;
new g_iVoteMuteCount[MAXPLAYERS + 1];
new bool:g_bVoteMuteFor[MAXPLAYERS + 1][MAXPLAYERS + 1];
new bool:g_bVoteMuteMuted[MAXPLAYERS + 1];

// ====[ PLUGIN ]==============================================================
public APLRes:AskPluginLoad2(Handle:hPlugin, bool:bLateLoad, String:strError[], iErrorSize)
{
   CreateNative("PlayersVotes_OpenVote", Native_OpenVote);
   RegPluginLibrary("playersvotes");
   return APLRes_Success;
}

public Plugin:myinfo =
{
   name = PLUGIN_NAME,
   author = "ReFlexPoison",
   description = PLUGIN_NAME,
   version = PLUGIN_VERSION,
   url = "http://www.sourcemod.net/"
}

// ====[ NATIVES ]=============================================================
public Native_OpenVote(Handle:hPlugin, iParams)
{
   new iClient = GetNativeCell(1);

   decl String:strType[56];
   GetNativeString(2, strType, sizeof(strType));

   if(StrEqual(strType, "Kick"))
      Menu_DisplayKickVote(iClient);
   else if(StrEqual(strType, "Ban"))
      Menu_DisplayBanVote(iClient);
   if(StrEqual(strType, "Map"))
      Menu_DisplayMapVote(iClient);
   if(StrEqual(strType, "Mute"))
      Menu_DisplayMuteVote(iClient);
}

// ====[ EVENTS ]==============================================================
public OnPluginStart()
{
   LoadTranslations("playersvotes.phrases");

   CreateConVar("sm_playersvotes_redux_version", PLUGIN_VERSION, PLUGIN_NAME, FCVAR_PLUGIN | FCVAR_SPONLY | FCVAR_DONTRECORD | FCVAR_NOTIFY);

   BuildPath(Path_SM, g_strConfigFile, sizeof(g_strConfigFile), "configs/playersvotes.cfg");

   RegAdminCmd("sm_playersvotes_cancel", Command_Cancel, ADMFLAG_ROOT, "Cancels all votes");
   RegAdminCmd("sm_playersvotes_reload", Command_Reload, ADMFLAG_ROOT, "Reload playersvotes config");

   if(g_hArrayVoteMapList == INVALID_HANDLE)
      g_hArrayVoteMapList = CreateArray(33);

   if(g_hArrayVoteMapLastMaps == INVALID_HANDLE)
      g_hArrayVoteMapLastMaps = CreateArray(33);

   if(g_hArrayVoteBanClientUserIds == INVALID_HANDLE)
      g_hArrayVoteBanClientUserIds = CreateArray();

   if(g_hArrayVoteBanClientCurrentUserId == INVALID_HANDLE)
      g_hArrayVoteBanClientCurrentUserId = CreateArray();

   if(g_hArrayVoteBanClientTeam == INVALID_HANDLE)
      g_hArrayVoteBanClientTeam = CreateArray();

   if(g_hArrayVoteBanClientIdentity == INVALID_HANDLE)
      g_hArrayVoteBanClientIdentity = CreateArray(33);

   if(g_hArrayVoteBanClientNames == INVALID_HANDLE)
      g_hArrayVoteBanClientNames = CreateArray(33);

   if(g_hArrayVoteBanReasons == INVALID_HANDLE)
      g_hArrayVoteBanReasons = CreateArray(33);

   if(g_hArrayVoteMuteClientIdentity == INVALID_HANDLE)
      g_hArrayVoteMuteClientIdentity = CreateArray(33);

   for(new i = 0; i <= MAXPLAYERS; ++i)
   {
      if(g_hArrayVotedForMap[i] == INVALID_HANDLE)
         g_hArrayVotedForMap[i] = CreateArray();

      if(g_hArrayVoteBanFor[i] == INVALID_HANDLE)
         g_hArrayVoteBanFor[i] = CreateArray();

      if(g_hArrayVoteBanForReason[i] == INVALID_HANDLE)
         g_hArrayVoteBanForReason[i] = CreateArray();
   }
}

public OnConfigsExecuted()
{
   Config_Load();

   ReadMapList(g_hArrayVoteMapList, g_iVoteMapListSerial, "playersvotes", MAPLIST_FLAG_CLEARARRAY | MAPLIST_FLAG_MAPSFOLDER);
   PlayersVotes_ResetMapVotes();

   decl String:strMap[64];
   GetCurrentMap(strMap, sizeof(strMap));

   g_iVoteMapCurrent = -1;

   decl String:strMapListEntry[65];
   for(new i = 0; i < GetArraySize(g_hArrayVoteMapList); i++)
   {
      GetArrayString(g_hArrayVoteMapList, i, strMapListEntry, sizeof(strMapListEntry));
      if(StrEqual(strMapListEntry, strMap, false))
         g_iVoteMapCurrent = i;
   }
}

public OnMapStart()
{
   g_iStartTime = GetTime();

   g_hCvarVoteMapTimeLimit = FindConVar("mp_timelimit");
   g_hCvarVoteBanSB = FindConVar("sb_version");

   decl String:strMap[64];
   GetCurrentMap(strMap, sizeof(strMap));

   PushArrayString(g_hArrayVoteMapLastMaps, strMap);
   if(GetArraySize(g_hArrayVoteMapLastMaps) > 64)
      RemoveFromArray(g_hArrayVoteMapLastMaps, 0);

   PlayersVotes_ResetKickVotes();
   PlayersVotes_ResetBanVotes();
   PlayersVotes_ResetMapVotes();
   PlayersVotes_ResetMuteVotes();

   for(new i = 0; i <= MAXPLAYERS; ++i)
   {
      g_iVoteKickCount[i] = 0;
      g_iVoteBanCount[i] = 0;
      g_iVoteMapCount[i] = 0;
      g_iVoteMuteCount[i] = 0;
      g_iVoteBanClients[i] = -1;
      g_bVoteMuteMuted[i] = false;
   }

   ClearArray(g_hArrayVoteMuteClientIdentity);
}

public OnClientDisconnect(iClient)
{
   g_bImmune[iClient] = false;
   g_iVoteKickLast[iClient] = 0;
   g_iVoteBanLast[iClient] = 0;
   g_iVoteMapLast[iClient] = 0;
   g_iVoteMuteLast[iClient] = 0;
   g_iVoteKickCount[iClient] = 0;
   g_iVoteBanCount[iClient] = 0;
   g_iVoteMapCount[iClient] = 0;
   g_iVoteMuteCount[iClient] = 0;
   g_iVoteBanClients[iClient] = -1;

   for(new i = 0; i <= MAXPLAYERS; i++)
   {
      g_bVoteKickFor[iClient][i] = false;
      g_bVoteKickFor[i][iClient] = false;
      g_bVoteMuteFor[iClient][i] = false;
      g_bVoteMuteFor[i][iClient] = false;
   }

   new iBanVotes = GetArraySize(g_hArrayVoteBanFor[iClient]);
   for(new i = 0; i < iBanVotes; ++i)
   {
      new iTarget = GetArrayCell(g_hArrayVoteBanFor[iClient], i);
      if(PlayersVotes_GetBanVotesForTarget(iTarget) == 1)
      {
         PlayersVotes_RemoveBanVotesFromTarget(iTarget);
         --i;
         --iBanVotes;
      }
   }

   ClearArray(g_hArrayVoteBanFor[iClient]);
   ClearArray(g_hArrayVoteBanForReason[iClient]);

   PlayersVotes_ResetClientMapVotes(iClient);

   if(g_bVoteMuteMuted[iClient] && !(GetClientListeningFlags(iClient) & VOICE_MUTED))
   {
      decl String:strClientAuth[33];
      PlayersVotes_GetIdentity(iClient, strClientAuth, sizeof(strClientAuth));

      new iRemoveMuteIndex = PlayersVotes_MatchIdentity(g_hArrayVoteMuteClientIdentity, strClientAuth);
      if(iRemoveMuteIndex != -1)
         RemoveFromArray(g_hArrayVoteMuteClientIdentity, iRemoveMuteIndex);
   }

   g_bVoteMuteMuted[iClient] = false;
}

public OnClientConnected(iClient)
{
   decl String:strIp[33];
   GetClientIP(iClient, strIp, sizeof(strIp));

   g_iVoteBanClients[iClient] = PlayersVotes_MatchIdentity(g_hArrayVoteBanClientIdentity, strIp);

   if(PlayersVotes_MatchIdentity(g_hArrayVoteMuteClientIdentity, strIp) != -1)
      g_bVoteMuteMuted[iClient] = true;

   new iBanTarget = g_iVoteBanClients[iClient];
   if(iBanTarget != -1)
   {
      decl String:strClientName[33];
      GetClientName(iClient, strClientName, sizeof(strClientName));

      decl String:strStoredName[33];
      GetArrayString(g_hArrayVoteBanClientNames, iBanTarget, strStoredName, sizeof(strStoredName));

      if(!StrEqual(strClientName, strStoredName))
      {
         PrintToChatAll("[SM] Bans: %s changed name to %s!", strStoredName, strClientName);
         SetArrayString(g_hArrayVoteBanClientNames, iBanTarget, strClientName);
      }

      SetArrayCell(g_hArrayVoteBanClientCurrentUserId, iBanTarget, GetClientUserId(iClient));
   }
}

public OnClientAuthorized(iClient, const String:strAuth[])
{
   if(PlayersVotes_MatchIdentity(g_hArrayVoteMuteClientIdentity, strAuth) != -1)
      g_bVoteMuteMuted[iClient] = true;

   new iBanTarget = g_iVoteBanClients[iClient];
   if(iBanTarget != -1)
   {
      if(PlayersVotes_IsValidAuth(strAuth))
         SetArrayString(g_hArrayVoteBanClientIdentity, iBanTarget, strAuth);
   }
   else
   {
      g_iVoteBanClients[iClient] = PlayersVotes_MatchIdentity(g_hArrayVoteBanClientIdentity, strAuth);
      iBanTarget = g_iVoteBanClients[iClient];
   }

   if(iBanTarget != -1)
   {
      decl String:strClientName[33];
      GetClientName(iClient, strClientName, sizeof(strClientName));

      decl String:strStoredName[33];
      GetArrayString(g_hArrayVoteBanClientNames, iBanTarget, strStoredName, sizeof(strStoredName));

      if(!StrEqual(strClientName, strStoredName))
      {
         PrintToChatAll("[SM] Bans: %s changed name to %s!", strStoredName, strClientName);
         SetArrayString(g_hArrayVoteBanClientNames, iBanTarget, strClientName);
      }

      SetArrayCell(g_hArrayVoteBanClientCurrentUserId, iBanTarget, GetClientUserId(iClient));
   }
}

public OnClientPostAdminCheck(iClient)
{
   if(g_bVoteMuteMuted[iClient])
      PlayersVotes_MutePlayer(iClient);

   if(g_iVoteImmunity > -1)
   {
      new AdminId:idTargetAdmin = GetUserAdmin(iClient);
      if(idTargetAdmin != INVALID_ADMIN_ID || CheckCommandAccess(iClient, "playersvotes_immunity", ADMFLAG_GENERIC))
      {
         if(GetAdminImmunityLevel(idTargetAdmin) >= g_iVoteImmunity)
            g_bImmune[iClient] = true;
      }
   }
}

// ====[ COMMANDS ]============================================================
public Action:Command_Cancel(iClient, iArgs)
{
   PlayersVotes_ResetBanVotes();
   PlayersVotes_ResetMapVotes();
   PlayersVotes_ResetMuteVotes();
   PlayersVotes_ResetKickVotes();
   ReplyToCommand(iClient, "[SM] All votes canceled.");
   return Plugin_Handled;
}

public Action:Command_Reload(iClient, iArgs)
{
   Config_Load();
   ReplyToCommand(iClient, "[SM] Config 'playersvotes.cfg' reloaded.");
   return Plugin_Handled;
}

public Action:OnClientSayCommand(iClient, const String:strCommand[], const String:sArgs[])
{
   if(!IsValidClient(iClient) || !g_bChatTriggers)
      return Plugin_Continue;

   decl String:strText[255];
   strcopy(strText, sizeof(strText), sArgs);
   StripQuotes(strText);

   new AdminId:idAdmin = GetUserAdmin(iClient);
   if(idAdmin == INVALID_ADMIN_ID)
   {
      ReplaceString(strText, sizeof(strText), "!", "");
      ReplaceString(strText, sizeof(strText), "/", "");
   }

   if(StrEqual(strText, "votekick", false))
      Menu_DisplayKickVote(iClient);
   else if(StrEqual(strText, "voteban", false))
      Menu_DisplayBanVote(iClient);
   else if(StrEqual(strText, "votemap", false))
      Menu_DisplayMapVote(iClient);
   else if(StrEqual(strText, "votemute", false))
      Menu_DisplayMuteVote(iClient);
   return Plugin_Continue;
}

//  _  ___    _
// | |/ (_)__| |__
// | ' <| / _| / /
// |_|\_`_`__|_`_`
//

public Menu_DisplayKickVote(iClient)
{
   if(!g_bVoteKickEnabled)
      return;

   if(!CheckCommandAccess(iClient, "sm_votemenu", 0) || !CheckCommandAccess(iClient, "playersvotes_kick", 0))
   {
      ReplyToCommand(iClient, "[SM] %t.", "No Access");
      return;
   }

   if(g_iVoteKickLimit != 0 && g_iVoteKickLimit <= g_iVoteKickCount[iClient])
   {
      PrintToChat(iClient, "[SM] %t.", "votes spent", g_iVoteKickLimit, "Votekick");
      return;
   }

   new iTime = GetTime();
   new iFromLast = iTime - g_iVoteKickLast[iClient];
   if(iFromLast < g_iVoteKickInterval)
   {
      PrintToChat(iClient, "[SM] %t.", "voting not allowed again", g_iVoteKickInterval - iFromLast);
      return;
   }

   new iFromStart = iTime - g_iStartTime;
   if(iFromStart < g_iVoteKickDelay)
   {
      PrintToChat(iClient, "[SM] %t.", "voting not allowed", g_iVoteKickDelay - iFromStart);
      return;
   }

   g_iVoteKickLast[iClient] = iTime;

   new Handle:hMenu = CreateMenu(MenuHandler_DisplayKickVote);
   if(g_iVoteKickLimit > 0)
      SetMenuTitle(hMenu, "%t: %t", "Votekick", "votes remaining", g_iVoteKickLimit - g_iVoteKickCount[iClient]);
   else
      SetMenuTitle(hMenu, "%t:", "Votekick");
   SetMenuExitBackButton(hMenu, true);

   decl String:strName[MAX_NAME_LENGTH + 12];
   decl String:strClient[8];

   for(new i = 1; i <= MaxClients; i++) if(IsClientInGame(i))
   {
      if(IsFakeClient(i))
         continue;

      if(g_bVoteKickTeam && GetClientTeam(iClient) != GetClientTeam(i))
         continue;

      if(i == iClient || g_bImmune[i])
         continue;

      new iVotes = PlayersVotes_GetKickVotesForTarget(i);
      new iRequired = PlayersVotes_GetRequiredKickVotes(iClient);

      IntToString(i, strClient, sizeof(strClient));
      Format(strName, sizeof(strName), "%N [%d/%d]", i, iVotes, iRequired);

      if(iVotes > 0)
      {
         if(i == 1)
            AddMenuItem(hMenu, strClient, strName);
         else
            InsertMenuItem(hMenu, 0, strClient, strName);
      }
      else
         AddMenuItem(hMenu, strClient, strName);
   }

   DisplayMenu(hMenu, iClient, 30);
}

public MenuHandler_DisplayKickVote(Handle:hMenu, MenuAction:iAction, iParam1, iParam2)
{
   if(iAction == MenuAction_End)
   {
      CloseHandle(hMenu);
      return;
   }

   if(iAction == MenuAction_Cancel)
   {
      if(iParam2 == MenuCancel_ExitBack)
         CustomVotes_OpenVote(iParam1);
   }
   else if(iAction == MenuAction_Select)
   {
      decl String:strInfo[8];
      GetMenuItem(hMenu, iParam2, strInfo, sizeof(strInfo));

      new iTarget = StringToInt(strInfo);
      if(IsValidClient(iTarget) && !IsFakeClient(iTarget))
      {
         g_bVoteKickFor[iParam1][iTarget] = true;
         g_iVoteKickCount[iParam1] += 1;
         PlayersVotes_CheckKickVotes(iParam1, iTarget);
      }
   }
}

//  ___
// | _ ) __ _ _ _
// | _ \/ _` | ' `
// |___/\__,_|_||_|
//

public Menu_DisplayBanVote(iClient)
{
   if(!g_bVoteBanEnabled)
      return;

   if(!CheckCommandAccess(iClient, "sm_votemenu", 0) || !CheckCommandAccess(iClient, "playersvotes_ban", 0))
   {
      ReplyToCommand(iClient, "[SM] %t.", "No Access");
      return;
   }

   if(g_iVoteBanLimit != 0 && g_iVoteBanLimit <= g_iVoteBanCount[iClient])
   {
      PrintToChat(iClient, "[SM] %t.", "votes spent", g_iVoteBanLimit, "Voteban");
      return;
   }

   new iTime = GetTime();
   new iFromLast = iTime - g_iVoteBanLast[iClient];
   if(iFromLast < g_iVoteBanInterval)
   {
      PrintToChat(iClient, "[SM] %t.", "voting not allowed again", g_iVoteBanInterval - iFromLast);
      return;
   }

   new iFromStart = iTime - g_iStartTime;
   if(iFromStart < g_iVoteBanDelay)
   {
      PrintToChat(iClient, "[SM] %t.", "voting not allowed", g_iVoteBanDelay - iFromStart);
      return;
   }

   g_iVoteBanLast[iClient] = GetTime();

   new Handle:hMenu = CreateMenu(MenuHandler_DisplayBanVote);
   if(g_iVoteBanLimit > 0)
      SetMenuTitle(hMenu, "%t: %t", "Voteban", "votes remaining", g_iVoteBanLimit - g_iVoteBanCount[iClient]);
   else
      SetMenuTitle(hMenu, "%t:", "Voteban");
   SetMenuExitBackButton(hMenu, true);

   decl String:strName[72];
   decl String:strUserId[8];

   new iRequired = PlayersVotes_GetRequiredBanVotes(iClient);
   for(new i = 0; i < GetArraySize(g_hArrayVoteBanClientNames); ++i)
   {
      new iTarget = GetClientOfUserId(GetArrayCell(g_hArrayVoteBanClientCurrentUserId, i));
      new bool:bShowTarget;
      if(g_bVoteBanTeam)
      {
         new iTeam = GetClientTeam(iClient);
         if(iTarget != 0)
         {
            if(iTeam == GetClientTeam(iTarget))
               bShowTarget = true;
         }

         if(GetArrayCell(g_hArrayVoteBanClientTeam, i) == iTeam)
            bShowTarget = true;
      }
      else
         bShowTarget = true;

      if(bShowTarget)
      {
         decl String:strBanName[33];

         GetArrayString(g_hArrayVoteBanClientNames, i, strBanName, sizeof(strBanName));

         IntToString(GetArrayCell(g_hArrayVoteBanClientUserIds, i), strUserId, sizeof(strUserId));
         Format(strName, sizeof(strName), "%s [%d/%d]", strBanName, PlayersVotes_GetBanVotesForTarget(i), iRequired);

         AddMenuItem(hMenu, strUserId, strName);
      }
   }

   for(new i = 1; i <= MaxClients; i++) if(IsClientInGame(i))
   {
      if(IsFakeClient(i))
         continue;

      if(g_iVoteBanClients[i] != -1)
         continue;

      if(g_bVoteBanTeam && GetClientTeam(iClient) != GetClientTeam(i))
         continue;

      if(i == iClient || g_bImmune[i])
         continue;

      IntToString(GetClientUserId(i), strUserId, sizeof(strUserId));
      Format(strName, sizeof(strName), "%N [0/%d]", i, iRequired);

      AddMenuItem(hMenu, strUserId, strName);
   }

   DisplayMenu(hMenu, iClient, 30);
}

public MenuHandler_DisplayBanVote(Handle:hMenu, MenuAction:iAction, iParam1, iParam2)
{
   if(iAction == MenuAction_End)
   {
      CloseHandle(hMenu);
      return;
   }

   if(iAction == MenuAction_Cancel)
   {
      if(iParam2 == MenuCancel_ExitBack)
         CustomVotes_OpenVote(iParam1);
   }
   else if(iAction == MenuAction_Select)
   {
      decl String:strInfo[8];
      GetMenuItem(hMenu, iParam2, strInfo, sizeof(strInfo));

      new iTarget = StringToInt(strInfo);
      if(GetArraySize(g_hArrayVoteBanReasons) > 0)
         Menu_BanReason(iParam1, iTarget);
      else
         PlayersVotes_ProcessBanVote(iParam1, iTarget, -1);
   }
}

public Menu_BanReason(iClient, iTarget)
{
   new iNumReasons = GetArraySize(g_hArrayVoteBanReasons);
   if(iNumReasons <= 0)
   {
      PlayersVotes_ProcessBanVote(iClient, iTarget, -1);
      return;
   }

   new Handle:hMenu = CreateMenu(MenuHandler_BanReason);

   decl String:strTitle[32];
   Format(strTitle, sizeof(strTitle), "%t:", "ban reasons");
   SetMenuTitle(hMenu, strTitle);

   decl String:strTarget[8];
   Format(strTarget, sizeof(strTarget), "%d", iTarget);

   decl String:strReason[33];
   for(new i = 0; i < iNumReasons; ++i)
   {
      GetArrayString(g_hArrayVoteBanReasons, i, strReason, sizeof(strReason));
      AddMenuItem(hMenu, strTarget, strReason);
   }

   DisplayMenu(hMenu, iClient, 30);
}

public MenuHandler_BanReason(Handle:hMenu, MenuAction:iAction, iParam1, iParam2)
{
   if(iAction == MenuAction_End)
   {
      CloseHandle(hMenu);
      return;
   }

   if(iAction == MenuAction_Select)
   {
      decl String:strInfo[8];
      GetMenuItem(hMenu, iParam2, strInfo, sizeof(strInfo));

      new iTarget = StringToInt(strInfo);
      PlayersVotes_ProcessBanVote(iParam1, iTarget, iParam2);
   }
}

//  __  __
// |  \/  |__ _ _ __
// | |\/| / _` | '_ `
// |_|  |_`__,_| .__/
//

public Menu_DisplayMapVote(iClient)
{
   if(!g_bVoteMapEnabled)
      return;

   if(!CheckCommandAccess(iClient, "sm_votemenu", 0) || !CheckCommandAccess(iClient, "playersvotes_map", 0))
   {
      ReplyToCommand(iClient, "[SM] %t.", "No Access");
      return;
   }

   new iTime = GetTime();
   if(g_iVoteMapLimit != 0 && g_iVoteMapLimit <= g_iVoteMapCount[iClient])
   {
      PrintToChat(iClient, "[SM] %t.", "votes spent", g_iVoteMapLimit, "Votemap");
      return;
   }

   new iFromLast = iTime - g_iVoteMapLast[iClient];
   if(iFromLast < g_iVoteMapInterval)
   {
      PrintToChat(iClient, "[SM] %t.", "voting not allowed again", g_iVoteMapInterval - iFromLast);
      return;
   }

   new iFromStart = iTime - g_iStartTime;
   if(iFromStart < g_iVoteMapDelay)
   {
      PrintToChat(iClient, "[SM] %t.", "voting not allowed", g_iVoteMapDelay - iFromStart);
      return;
   }

   g_iVoteMapLast[iClient] = iTime;

   new Handle:hMenu = CreateMenu(MenuHandler_DisplayMapVote);
   if(g_iVoteMapLimit > 0)
      SetMenuTitle(hMenu, "%t: %t", "Votemap", "votes remaining", g_iVoteMapLimit - g_iVoteMapCount[iClient]);
   else
      SetMenuTitle(hMenu, "%t:", "Votemap");
   SetMenuExitBackButton(hMenu, true);

   decl String:strMap[65];
   decl String:strClient[8];

   new bool:bExtendAdded;
   for(new i = 0; i < GetArraySize(g_hArrayVoteMapList); i++)
   {
      GetArrayString(g_hArrayVoteMapList, i, strMap, sizeof(strMap));
      if(IsMapValid(strMap))
      {
         if(g_iVoteMapCurrent == i && g_iVoteMapMaxExtends != 0 && g_iVoteMapExtendTime > 0)
         {
            new iVotes = PlayersVotes_GetMapVotesForTarget(i);
            new iRequired = PlayersVotes_GetRequiredMapVotes(iClient);

            IntToString(i, strClient, sizeof(strClient));
            Format(strMap, sizeof(strMap), "%t [%d/%d]", "extend map by", g_iVoteMapExtendTime, iVotes, iRequired);

            if(g_iVoteMapCurrent == 0)
               AddMenuItem(hMenu, strClient, strMap);
            else
               InsertMenuItem(hMenu, 0, strClient, strMap);

            bExtendAdded = true;
         }
         else if(!PlayersVotes_IsLastPlayed(strMap))
         {
            new iVotes = PlayersVotes_GetMapVotesForTarget(i);
            new iRequired = PlayersVotes_GetRequiredMapVotes(iClient);

            IntToString(i, strClient, sizeof(strClient));
            Format(strMap, sizeof(strMap), "%s [%d/%d]", strMap, iVotes, iRequired);

            if(iVotes > 0)
            {
               if(bExtendAdded)
                  InsertMenuItem(hMenu, 1, strClient, strMap);
               else if(i == 0)
                  AddMenuItem(hMenu, strClient, strMap);
               else
                  InsertMenuItem(hMenu, 0, strClient, strMap);
            }
            else
               AddMenuItem(hMenu, strClient, strMap);
         }
      }
   }

   DisplayMenu(hMenu, iClient, 30);
}

public MenuHandler_DisplayMapVote(Handle:hMenu, MenuAction:iAction, iParam1, iParam2)
{
   if(iAction == MenuAction_End)
   {
      CloseHandle(hMenu);
      return;
   }

   if(iAction == MenuAction_Cancel)
   {
      if(iParam2 == MenuCancel_ExitBack)
         CustomVotes_OpenVote(iParam1);
   }
   else if(iAction == MenuAction_Select)
   {
      decl String:strInfo[8];
      GetMenuItem(hMenu, iParam2, strInfo, sizeof(strInfo));

      new iTarget = StringToInt(strInfo);
      PlayersVotes_ResetClientMapVotes(iParam1);
      SetArrayCell(g_hArrayVotedForMap[iParam1], iTarget, 1);
      g_iVoteMapCount[iParam1] += 1;
      PlayersVotes_CheckMapVotes(iParam1, iTarget);
   }
}

//  __  __      _
// |  \/  |_  _| |_ ___
// | |\/| | || |  _/ -_)
// |_|  |_|\_,_|\__`___|
//

public Menu_DisplayMuteVote(iClient)
{
   if(!g_bVoteMuteEnabled)
      return;

   if(!CheckCommandAccess(iClient, "sm_votemenu", 0) || !CheckCommandAccess(iClient, "playersvotes_mute", 0))
   {
      ReplyToCommand(iClient, "[SM] %t.", "No Access");
      return;
   }

   if(g_iVoteMuteLimit != 0 && g_iVoteMuteLimit <= g_iVoteMuteCount[iClient])
   {
      PrintToChat(iClient, "[SM] %t.", "votes spent", g_iVoteMuteLimit, "Votemute");
      return;
   }

   new iTime = GetTime();
   new iFromLast = iTime - g_iVoteMuteLast[iClient];
   if(iFromLast < g_iVoteMuteInterval)
   {
      PrintToChat(iClient, "[SM] %t.", "voting not allowed again", g_iVoteMuteInterval - iFromLast);
      return;
   }

   new iFromStart = iTime - g_iStartTime;
   if(iFromStart < g_iVoteMuteDelay)
   {
      PrintToChat(iClient, "[SM] %t.", "voting not allowed", g_iVoteMuteDelay - iFromStart);
      return;
   }

   g_iVoteMuteLast[iClient] = iTime;

   new Handle:hMenu = CreateMenu(MenuHandler_DisplayMuteVote);
   if(g_iVoteMuteLimit > 0)
      SetMenuTitle(hMenu, "%t: %t", "Votemute", "votes remaining", g_iVoteMuteLimit - g_iVoteMuteCount[iClient]);
   else
      SetMenuTitle(hMenu, "%t:", "Votemute");
   SetMenuExitBackButton(hMenu, true);

   decl String:strName[72];
   decl String:strClient[8];

   for(new i = 1; i <= MaxClients; i++) if(IsClientInGame(i))
   {
      if(IsFakeClient(i))
         continue;

      if(g_bVoteMuteTeam && GetClientTeam(iClient) != GetClientTeam(i))
         continue;

      if(i == iClient || g_bImmune[i] || g_bVoteMuteMuted[iClient])
         continue;

      new iVotes = PlayersVotes_GetMuteVotesForTarget(i);
      new iRequired = PlayersVotes_GetRequiredMuteVotes(iClient);

      IntToString(i, strClient, sizeof(strClient));
      Format(strName, sizeof(strName), "%N [%d/%d]", i, iVotes, iRequired);

      if(iVotes > 0)
      {
         if(i == 1)
            AddMenuItem(hMenu, strClient, strName);
         else
            InsertMenuItem(hMenu, 0, strClient, strName);
      }
      else
         AddMenuItem(hMenu, strClient, strName);
   }

   DisplayMenu(hMenu, iClient, 30);
}

public MenuHandler_DisplayMuteVote(Handle:hMenu, MenuAction:iAction, iParam1, iParam2)
{
   if(iAction == MenuAction_End)
   {
      CloseHandle(hMenu);
      return;
   }

   if(iAction == MenuAction_Cancel)
   {
      if(iParam2 == MenuCancel_ExitBack)
         CustomVotes_OpenVote(iParam1);
   }
   else if(iAction == MenuAction_Select)
   {
      decl String:strInfo[8];
      GetMenuItem(hMenu, iParam2, strInfo, sizeof(strInfo));

      new iTarget = StringToInt(strInfo);
      if(IsValidClient(iTarget) && !IsFakeClient(iTarget))
      {
         g_bVoteMuteFor[iParam1][iTarget] = true;
         g_iVoteMuteCount[iParam1] += 1;
         PlayersVotes_CheckMuteVotes(iParam1, iTarget);
      }
   }
}

// ====[ FUNCTIONS ]===========================================================
public Config_Load()
{
   if(!FileExists(g_strConfigFile))
   {
      SetFailState("Configuration file %s not found!", g_strConfigFile);
      return;
   }

   new Handle:hKeyValues = CreateKeyValues("playersvotes");
   if(!FileToKeyValues(hKeyValues, g_strConfigFile))
   {
      SetFailState("Configuration file %s not found!", g_strConfigFile);
      return;
   }

   g_bChatTriggers = bool:KvGetNum(hKeyValues, "chattriggers", 1);
   g_iVoteImmunity = KvGetNum(hKeyValues, "immunity", 0);

   if(KvGotoFirstSubKey(hKeyValues))
   {
      decl String:strSection[32];
      do
      {
         KvGetSectionName(hKeyValues, strSection, sizeof(strSection));

         if(StrEqual(strSection, "kick"))
         {
            g_bVoteKickEnabled = bool:KvGetNum(hKeyValues, "enabled", 1);
            PrintToServer("%i", g_bVoteKickEnabled);
            g_flVoteKickRatio = KvGetFloat(hKeyValues, "ratio", 0.6);
            g_iVoteKickMinimum = KvGetNum(hKeyValues, "minimum", 4);
            g_iVoteKickDelay = KvGetNum(hKeyValues, "delay", 1);
            g_iVoteKickLimit = KvGetNum(hKeyValues, "limit", 0);
            g_iVoteKickInterval = KvGetNum(hKeyValues, "interval", 0);
            g_bVoteKickTeam = bool:KvGetNum(hKeyValues, "team", 0);
         }
         else if(StrEqual(strSection, "ban"))
         {
            g_bVoteBanEnabled = bool:KvGetNum(hKeyValues, "enabled", 1);
            g_flVoteBanRatio = KvGetFloat(hKeyValues, "ratio", 0.8);
            g_iVoteBanMinimum = KvGetNum(hKeyValues, "minimum", 4);
            g_iVoteBanDelay = KvGetNum(hKeyValues, "delay", 1);
            g_iVoteBanLimit = KvGetNum(hKeyValues, "limit", 0);
            g_iVoteBanInterval = KvGetNum(hKeyValues, "interval", 0);
            g_bVoteBanTeam = bool:KvGetNum(hKeyValues, "team", 0);
            g_iVoteBanTime = KvGetNum(hKeyValues, "time", 30);
            KvGetString(hKeyValues, "reasons", g_strVoteBanReasons, sizeof(g_strVoteBanReasons));

            ClearArray(g_hArrayVoteBanReasons);

            decl String:strBanReasonList[256];
            strcopy(strBanReasonList, sizeof(strBanReasonList), g_strVoteBanReasons);
            StrCat(strBanReasonList, sizeof(strBanReasonList), ";");

            new iBanReasonOffset;
            decl String:strBanReason[33];
            for(new i = SplitString(strBanReasonList, ";", strBanReason, sizeof(strBanReason)); i != -1; i = SplitString(strBanReasonList[iBanReasonOffset], ";", strBanReason, sizeof(strBanReason)))
            {
               iBanReasonOffset += i;
               TrimString(strBanReason);
               if(!StrEqual(strBanReason, ""))
                  PushArrayString(g_hArrayVoteBanReasons, strBanReason);
            }
         }
         else if(StrEqual(strSection, "map"))
         {
            g_bVoteMapEnabled = bool:KvGetNum(hKeyValues, "enabled", 1);
            g_flVoteMapRatio = KvGetFloat(hKeyValues, "ratio", 0.6);
            g_iVoteMapMinimum = KvGetNum(hKeyValues, "minimum", 4);
            g_iVoteMapDelay = KvGetNum(hKeyValues, "delay", 1);
            g_iVoteMapLimit = KvGetNum(hKeyValues, "limit", 0);
            g_iVoteMapInterval = KvGetNum(hKeyValues, "interval", 0);
            g_iVoteMapLastMaps = KvGetNum(hKeyValues, "lastmaps", 4);
            g_iVoteMapExtendTime = KvGetNum(hKeyValues, "extendtime", 20);
            g_iVoteMapMaxExtends = KvGetNum(hKeyValues, "maxextends", 1);
            g_bVoteMapMode = bool:KvGetNum(hKeyValues, "mode", 1);
         }
         else if(StrEqual(strSection, "mute"))
         {
            g_bVoteMuteEnabled = bool:KvGetNum(hKeyValues, "enabled", 1);
            g_flVoteMuteRatio = KvGetFloat(hKeyValues, "ratio", 0.6);
            g_iVoteMuteMinimum = KvGetNum(hKeyValues, "minimum", 4);
            g_iVoteMuteDelay = KvGetNum(hKeyValues, "delay", 1);
            g_iVoteMuteLimit = KvGetNum(hKeyValues, "limit", 0);
            g_iVoteMuteInterval = KvGetNum(hKeyValues, "interval", 0);
            g_bVoteMuteTeam = bool:KvGetNum(hKeyValues, "team", 0);
         }
      }
      while(KvGotoNextKey(hKeyValues));
   }
   CloseHandle(hKeyValues);
}

//  _  ___    _
// | |/ (_)__| |__
// | ' <| / _| / /
// |_|\_`_`__|_`_`
//

public PlayersVotes_ResetKickVotes()
{
   for(new iClient = 0; iClient <= MAXPLAYERS; ++iClient)
   {
      for(new iTarget = 0; iTarget <= MAXPLAYERS; ++iTarget)
         g_bVoteKickFor[iClient][iTarget] = false;
   }
}

public PlayersVotes_CheckKickVotes(iVoter, iTarget)
{
   new iVotesRequired = PlayersVotes_GetRequiredKickVotes(iVoter);
   new iVotes = PlayersVotes_GetKickVotesForTarget(iTarget);

   decl String:strVoterName[65];
   GetClientName(iVoter, strVoterName, sizeof(strVoterName));

   decl String:strTargetName[65];
   GetClientName(iTarget, strTargetName, sizeof(strTargetName));

   PrintToChatAll("[SM] %t.", "voted to kick", strVoterName, strTargetName);

   if(iVotes < iVotesRequired)
   {
      PrintToChatAll("[SM] %t.", "votes required", iVotes, iVotesRequired);
      return;
   }

   PrintToChatAll("[SM] %t.", "kicked by vote", strTargetName);
   LogAction(-1, iTarget, "Vote kick successful, kicked \"%L\" (iReason \"voted by players\")", iTarget);
   ServerCommand("kickid %d %t", GetClientUserId(iTarget), "kicked by users");
}

public PlayersVotes_GetRequiredKickVotes(iVoter)
{
   new iCount;
   for(new i = 1; i <= MaxClients; i++) if(IsClientInGame(i))
   {
      if(IsFakeClient(i))
         continue;

      if(g_bVoteKickTeam && GetClientTeam(i) != GetClientTeam(iVoter))
         continue;

      iCount++;
   }

   new iRequired = RoundToCeil(float(iCount) * g_flVoteKickRatio);
   if(iRequired < g_iVoteKickMinimum)
      iRequired = g_iVoteKickMinimum;

   return iRequired;
}

public PlayersVotes_GetKickVotesForTarget(iTarget)
{
   new iVotes;
   for(new i = 1; i <= MAXPLAYERS; i++)
   {
      if(g_bVoteKickFor[i][iTarget])
         iVotes++;
   }
   return iVotes;
}

//  ___
// | _ ) __ _ _ _
// | _ \/ _` | ' `
// |___/\__,_|_||_|
//

public PlayersVotes_ResetBanVotes()
{
   ClearArray(g_hArrayVoteBanClientUserIds);
   ClearArray(g_hArrayVoteBanClientCurrentUserId);
   ClearArray(g_hArrayVoteBanClientTeam);
   ClearArray(g_hArrayVoteBanClientIdentity);
   ClearArray(g_hArrayVoteBanClientNames);
   for(new iClient = 0; iClient <= MAXPLAYERS; ++iClient)
   {
      ClearArray(g_hArrayVoteBanFor[iClient]);
      ClearArray(g_hArrayVoteBanForReason[iClient]);
      g_iVoteBanClients[iClient] = -1;
   }
}

public PlayersVotes_RemoveBanVotesFromTarget(iTarget)
{
   RemoveFromArray(g_hArrayVoteBanClientUserIds, iTarget);
   RemoveFromArray(g_hArrayVoteBanClientCurrentUserId, iTarget);
   RemoveFromArray(g_hArrayVoteBanClientTeam, iTarget);
   RemoveFromArray(g_hArrayVoteBanClientIdentity, iTarget);
   RemoveFromArray(g_hArrayVoteBanClientNames, iTarget);
   for(new i = 1; i <= MAXPLAYERS; ++i)
   {
      new iVoteToRemove = -1;
      for(new j = 0; j < GetArraySize(g_hArrayVoteBanFor[i]); ++j)
      {
         new iVote = GetArrayCell(g_hArrayVoteBanFor[i], j);
         if(iVote == iTarget)
            iVoteToRemove = j;
         else if(iVote > iTarget)
            SetArrayCell(g_hArrayVoteBanFor[i], j, iVote - 1);
      }
      if(iVoteToRemove != -1)
      {
         RemoveFromArray(g_hArrayVoteBanFor[i], iVoteToRemove);
         RemoveFromArray(g_hArrayVoteBanForReason[i], iVoteToRemove);
      }
      if(g_iVoteBanClients[i] == iTarget)
         g_iVoteBanClients[i] = -1;
      else if(g_iVoteBanClients[i] > iTarget)
         --g_iVoteBanClients[i];
   }
}

public PlayersVotes_CheckBanVotes(iVoter, iTarget)
{
   new iVotesRequired = PlayersVotes_GetRequiredBanVotes(iVoter);
   new iVotes = PlayersVotes_GetBanVotesForTarget(iTarget);

   decl String:strVoterName[65];
   GetClientName(iVoter, strVoterName, sizeof(strVoterName));

   decl String:strTargetName[65];
   GetArrayString(g_hArrayVoteBanClientNames, iTarget, strTargetName, sizeof(strTargetName));

   PrintToChatAll("[SM] %t.", "voted to ban", strVoterName, strTargetName);

   if(iVotes < iVotesRequired)
   {
      PrintToChatAll("[SM] %t.", "votes required", iVotes, iVotesRequired);
      return;
   }

   new iUserId = GetArrayCell(g_hArrayVoteBanClientCurrentUserId, iTarget);
   new iClientId = GetClientOfUserId(iUserId);

   new iBanFlags = BANFLAG_AUTHID;
   decl String:strIdentity[33];
   GetArrayString(g_hArrayVoteBanClientIdentity, iTarget, strIdentity, sizeof(strIdentity));
   if(strncmp(strIdentity, "STEAM", 5) != 0)
      iBanFlags = BANFLAG_IP;

   new iReason = PlayersVotes_GetBanReason(iTarget);
   decl String:strVoteReason[33];
   decl String:strReason[100];
   if(iReason > -1)
   {
      GetArrayString(g_hArrayVoteBanReasons, iReason, strVoteReason, sizeof(strVoteReason));
      PrintToChatAll("[SM] %t (\x05%s\x01).", "banned by vote", strTargetName, strVoteReason);
      if(iClientId > 0)
         Format(strReason, sizeof(strReason), "%t (%s)", "banned by users", strVoteReason);
      else
         Format(strReason, sizeof(strReason), "(%s) %t (%s)", strTargetName, "banned by users", strVoteReason);
   }
   else
   {
      strcopy(strVoteReason, sizeof(strVoteReason), "unspecified");
      PrintToChatAll("[SM] %t.", "banned by vote", strTargetName);
      if(iClientId > 0)
         Format(strReason, sizeof(strReason), "%t", "banned by users");
      else
         Format(strReason, sizeof(strReason), "(%s) %t", strTargetName, "banned by users");
   }

   LogAction(-1, -1, "Vote ban successful, banned \"%s\" (iReason \"%s\")", strTargetName, strVoteReason);

   if(g_hCvarVoteBanSB == INVALID_HANDLE)
      BanIdentity(strIdentity, g_iVoteBanTime, iBanFlags, strReason, "players vote");
   else
   {
      if(iClientId > 0)
         ServerCommand("sm_ban #%d %d \"%s\"", iUserId, g_iVoteBanTime, strReason);
      else if(iBanFlags == BANFLAG_AUTHID)
         ServerCommand("sm_addban %d %s \"%s\"", g_iVoteBanTime, strIdentity, strReason);
      else
         ServerCommand("sm_banip %s %d \"%s\"", strIdentity, g_iVoteBanTime, strReason);
   }

   if(iBanFlags == BANFLAG_AUTHID)
      ServerCommand("kickid %d %s", iUserId, strReason);

   PlayersVotes_RemoveBanVotesFromTarget(iTarget);
}

public PlayersVotes_GetRequiredBanVotes(iVoter)
{
   new iCount;
   for(new i = 1; i <= MaxClients; i++) if(IsClientInGame(i))
   {
      if(IsFakeClient(i))
         continue;

      if(g_bVoteBanTeam && GetClientTeam(i) != GetClientTeam(iVoter))
         continue;

      iCount++;
   }

   new iRequired = RoundToCeil(float(iCount) * g_flVoteBanRatio);
   if(iRequired < g_iVoteBanMinimum)
      iRequired = g_iVoteBanMinimum;

   return iRequired;
}

public PlayersVotes_GetBanVotesForTarget(iTarget)
{
   new iVotes;
   for(new i = 1; i <= MAXPLAYERS; i++)
   {
      new iBanVotes = GetArraySize(g_hArrayVoteBanFor[i]);
      for(new j = 0; j < iBanVotes; ++j)
      {
         if(GetArrayCell(g_hArrayVoteBanFor[i], j) == iTarget)
            iVotes++;
      }
   }
   return iVotes;
}

public PlayersVotes_ProcessBanVote(iVoter, iTarget, iReason)
{
   new iTargetIndex = FindValueInArray(g_hArrayVoteBanClientUserIds, iTarget);
   if(iTargetIndex == -1)
   {
      new iClient = GetClientOfUserId(iTarget);
      if(IsValidClient(iClient) && !IsFakeClient(iClient))
      {
         decl String:strClientName[MAX_NAME_LENGTH];
         GetClientName(iClient, strClientName, sizeof(strClientName));

         decl String:strClientAuth[24];
         PlayersVotes_GetIdentity(iClient, strClientAuth, sizeof(strClientAuth));

         PushArrayCell(g_hArrayVoteBanClientUserIds, iTarget);
         PushArrayString(g_hArrayVoteBanClientNames, strClientName);
         PushArrayString(g_hArrayVoteBanClientIdentity, strClientAuth);
         PushArrayCell(g_hArrayVoteBanClientCurrentUserId, iTarget);
         PushArrayCell(g_hArrayVoteBanClientTeam, GetClientTeam(iClient));

         g_iVoteBanClients[iClient] = GetArraySize(g_hArrayVoteBanClientNames) - 1;
         iTargetIndex = g_iVoteBanClients[iClient];
      }
   }

   if(iTargetIndex != -1)
   {
      new bool:bDuplicateVote;
      for(new i = 0; i < GetArraySize(g_hArrayVoteBanFor[iVoter]); ++i)
      {
         if(GetArrayCell(g_hArrayVoteBanFor[iVoter], i) == iTargetIndex)
            bDuplicateVote = true;
      }

      if(!bDuplicateVote)
      {
         PushArrayCell(g_hArrayVoteBanFor[iVoter], iTargetIndex);
         PushArrayCell(g_hArrayVoteBanForReason[iVoter], iReason);
      }

      g_iVoteBanCount[iVoter] += 1;
      PlayersVotes_CheckBanVotes(iVoter, iTargetIndex);
   }
}

public PlayersVotes_GetBanReason(iTarget)
{
   if(GetArraySize(g_hArrayVoteBanReasons) <= 0)
      return -1;

   new Handle:hReasonTally = CreateArray(1, GetArraySize(g_hArrayVoteBanReasons));

   for(new i = 0; i < GetArraySize(hReasonTally); ++i)
      SetArrayCell(hReasonTally, i, 0);

   new iTargetIndex;
   for(new i = 1; i <= MAXPLAYERS; ++i)
   {
      iTargetIndex = FindValueInArray(g_hArrayVoteBanFor[i], iTarget);
      if(iTargetIndex >= 0)
      {
         new iReason = GetArrayCell(g_hArrayVoteBanForReason[i], iTargetIndex);
         new iCount = GetArrayCell(hReasonTally, iReason);
         SetArrayCell(hReasonTally, iReason, iCount + 1);
      }
   }

   new iFinalReason = -1;
   new iFinalReasonCount;
   for(new i = 0; i < GetArraySize(hReasonTally); ++i)
   {
      if(iFinalReasonCount < GetArrayCell(hReasonTally, i))
      {
         iFinalReasonCount = GetArrayCell(hReasonTally, i);
         iFinalReason = i;
      }
   }

   CloseHandle(hReasonTally);
   return iFinalReason;
}

//  __  __
// |  \/  |__ _ _ __
// | |\/| / _` | '_ `
// |_|  |_`__,_| .__/
//             |_|

public PlayersVotes_ResetMapVotes()
{
   new iMapCount = GetArraySize(g_hArrayVoteMapList);
   for(new iClient = 0; iClient <= MAXPLAYERS; ++iClient)
   {
      ResizeArray(g_hArrayVotedForMap[iClient], iMapCount);
      PlayersVotes_ResetClientMapVotes(iClient);
   }
}

public PlayersVotes_CheckMapVotes(iVoter, iTarget)
{
   new iVotesRequired = PlayersVotes_GetRequiredMapVotes(iVoter);
   new iVotes = PlayersVotes_GetMapVotesForTarget(iTarget);

   decl String:strVoterName[65];
   GetClientName(iVoter, strVoterName, sizeof(strVoterName));

   decl String:strMap[32];
   GetArrayString(g_hArrayVoteMapList, iTarget, strMap, sizeof(strMap));
   if(!IsMapValid(strMap))
      return;

   if(g_iVoteMapCurrent == iTarget && g_iVoteMapMaxExtends != 0 && g_iVoteMapExtendTime > 0)
   {
      PrintToChatAll("[SM] %t.", "voted for extend", strVoterName, g_iVoteMapExtendTime);

      if(iVotes < iVotesRequired)
      {
         PrintToChatAll("[SM] %t.", "votes required", iVotes, iVotesRequired);
         return;
      }

      PrintToChatAll("[SM] %t.", "map extend by vote", g_iVoteMapExtendTime);
      LogAction(-1, -1, "Extending map to due to players vote.");
      SetConVarFloat(g_hCvarVoteMapTimeLimit, GetConVarFloat(g_hCvarVoteMapTimeLimit) + g_iVoteMapExtendTime);

      if(g_iVoteMapMaxExtends > 0)
         g_iVoteMapMaxExtends =- 1;

      PlayersVotes_ResetMapVotes();
   }
   else
   {
      if(g_bVoteMapMode)
         PrintToChatAll("[SM] %t.", "voted for map", strVoterName, strMap);
      else
         PrintToChatAll("[SM] %t.", "voted for nextmap", strVoterName, strMap);

      if(iVotes < iVotesRequired)
      {
         PrintToChatAll("[SM] %t.", "votes required", iVotes, iVotesRequired);
         return;
      }

      if(g_bVoteMapMode)
      {
         PrintToChatAll("[SM] %t.", "map change by vote", strMap);
         LogAction(-1, -1, "Changing map to %s due to players vote.", strMap);
         ServerCommand("sm_map \"%s\"", strMap);
      }
      else
      {
         PrintToChatAll("[SM] %t.", "nextmap change by vote", strMap);
         if(SetNextMap(strMap))
            LogAction(-1, -1, "Setting nextmap to %s due to players vote.", strMap);
         else
            LogAction(-1, -1, "ERROR: Failed to set nextmap to %s", strMap);

         PlayersVotes_ResetMapVotes();
      }
   }
}

public PlayersVotes_GetRequiredMapVotes(iVoter)
{
   new iCount;
   for(new i = 1; i <= MaxClients; i++) if(IsClientInGame(i))
   {
      if(IsFakeClient(i))
         continue;

      iCount++;
   }

   new iRequired = RoundToCeil(float(iCount) * g_flVoteMapRatio);
   if(iRequired < g_iVoteMapMinimum)
      iRequired = g_iVoteMapMinimum;

   return iRequired;
}

public PlayersVotes_GetMapVotesForTarget(iTarget)
{
   new iVotes;
   for(new i = 1; i <= MAXPLAYERS; ++i)
      iVotes += GetArrayCell(g_hArrayVotedForMap[i], iTarget);
   return iVotes;
}

public PlayersVotes_ResetClientMapVotes(iClient)
{
   for(new iTarget = 0; iTarget < GetArraySize(g_hArrayVotedForMap[iClient]); ++iTarget)
      SetArrayCell(g_hArrayVotedForMap[iClient], iTarget, 0);
}

public PlayersVotes_IsLastPlayed(const String:strMap[])
{
   new iEndOfLastMapList = GetArraySize(g_hArrayVoteMapLastMaps);
   new iOldestMapToCheck = iEndOfLastMapList - g_iVoteMapLastMaps;
   if(iOldestMapToCheck < 0)
      iOldestMapToCheck = 0;

   decl String:strMap2[64];
   for(new i = iOldestMapToCheck; i < iEndOfLastMapList; ++i)
   {
      GetArrayString(g_hArrayVoteMapLastMaps, i, strMap2, sizeof(strMap2));
      if(StrEqual(strMap2, strMap, false))
         return true;
   }

   return false;
}

//  __  __      _
// |  \/  |_  _| |_ ___
// | |\/| | || |  _/ -_)
// |_|  |_|\_,_|\__`___|
//

public PlayersVotes_ResetMuteVotes()
{
   for(new iClient = 0; iClient <= MAXPLAYERS; ++iClient)
   {
      for(new iTarget = 0; iTarget <= MAXPLAYERS; ++iTarget)
         g_bVoteMuteFor[iClient][iTarget] = false;
   }
}

public PlayersVotes_CheckMuteVotes(iVoter, iTarget)
{
   new iVotesRequired = PlayersVotes_GetRequiredMuteVotes(iVoter);
   new iVotes = PlayersVotes_GetMuteVotesForTarget(iTarget);

   decl String:strVoterName[65];
   GetClientName(iVoter, strVoterName, sizeof(strVoterName));

   decl String:strTargetName[65];
   GetClientName(iTarget, strTargetName, sizeof(strTargetName));

   PrintToChatAll("[SM] %t.", "voted to mute", strVoterName, strTargetName);

   if(iVotes < iVotesRequired)
   {
      PrintToChatAll("[SM] %t.", "votes required", iVotes, iVotesRequired);
      return;
   }

   PrintToChatAll("[SM] %t.", "muted by vote", strTargetName);
   LogAction(-1, iTarget, "Vote mute successful, muted \"%L\" (iReason \"voted by players\")", iTarget);
   g_bVoteMuteMuted[iTarget] = true;
   decl String:strClientAuth[33];
   PlayersVotes_GetIdentity(iTarget, strClientAuth, sizeof(strClientAuth));
   PushArrayString(g_hArrayVoteMuteClientIdentity, strClientAuth);
   PlayersVotes_MutePlayer(iTarget);
}

public PlayersVotes_GetRequiredMuteVotes(iVoter)
{
   new iCount;
   for(new i = 1; i <= MaxClients; i++) if(IsClientInGame(i))
   {
      if(IsFakeClient(i))
         continue;

      if(g_bVoteMuteTeam && GetClientTeam(i) != GetClientTeam(iVoter))
         continue;

      iCount++;
   }

   new iRequired = RoundToCeil(float(iCount) * g_flVoteMuteRatio);
   if(iRequired < g_iVoteMuteMinimum)
      iRequired = g_iVoteMuteMinimum;

   return iRequired;
}

public PlayersVotes_GetMuteVotesForTarget(iTarget)
{
   new iVotes;
   for(new i = 1; i <= MAXPLAYERS; i++)
   {
      if(g_bVoteMuteFor[i][iTarget])
         iVotes++;
   }
   return iVotes;
}

public PlayersVotes_MutePlayer(iClient)
{
   #if defined _basecomm_included
      BaseComm_SetClientMute(iClient, true);
   #else
      SetClientListeningFlags(iClient, VOICE_MUTED);
   #endif
}

// ====[ STOCKS ]==============================================================
stock bool:IsValidClient(iClient)
{
   if(iClient <= 0 || iClient > MaxClients || !IsClientInGame(iClient))
      return false;
   return true;
}

stock bool:PlayersVotes_IsValidAuth(const String:strClientAuth[])
{
   return (strcmp(strClientAuth, "STEAM_ID_LAN", false) != 0) && (strcmp(strClientAuth, "STEAM_ID_PENDING", false) != 0);
}

stock PlayersVotes_MatchIdentity(const Handle:hIdentityArray, const String:strIdentity[])
{
   decl String:strStoredIdentity[33];
   for(new i = 0; i < GetArraySize(hIdentityArray); ++i)
   {
      GetArrayString(hIdentityArray, i, strStoredIdentity, sizeof(strStoredIdentity));
      if(strcmp(strIdentity, strStoredIdentity, false) == 0)
         return i;
   }
   return -1;
}

stock bool:PlayersVotes_GetIdentity(iClient, String:strClientIdentity[], iClientIdentitySize)
{
   GetClientAuthString(iClient, strClientIdentity, iClientIdentitySize);
   if(!IsClientAuthorized(iClient) || !PlayersVotes_IsValidAuth(strClientIdentity))
   {
      GetClientIP(iClient, strClientIdentity, iClientIdentitySize);
      return false;
   }
   return true;
}

Команды:

Код: Выбрать все

sm_votemenu

Открыть меню голосования

Код: Выбрать все

sm_playersvotes_reload

Перезагрузка конфигурационного файла

Команды в чате:

Код: Выбрать все

votekick
voteban
votemap
votemute


Установка:
При необходимости скомпилировать sp файл
Поместите playersvotes-redux.smx в addons/plugins
Поместите playersvotes.cfg в addons/configs
Поместите playersvotes.phrases.txt в addons/translations
Вложения
playersvotes-redux.zip
(32.97 KiB) Загружено 31 раз

Вернуться в «SourceMod»

Кто сейчас на форуме

Количество пользователей, которые сейчас просматривают этот форум: нет зарегистрированных пользователей и 1 гость