Включение/Отключение ботов игроками / Bot toggle 1.0.1

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

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

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

Включение/Отключение ботов игроками / Bot toggle 1.0.1

Сообщение Longipongeoos » 11 ноя 2021, 16:23

Включение/Отключение ботов игроками / Bot toggle 1.0.1
Информация
Плагин предоставляет самим игрокам решать играть ли им с ботами на сервере или нет.
Функции:
Игроки могут включать и отключать ботов самостоятельно
Настройки ботов по умолчанию
Настроить команды для управления ботами
Оповещение в чате о том что игроки могут настроить ботов
Отключение ботов после определенного количества раундов
Автоматическое отключение ботов когда достаточно игроков
Логирование действий игроков по отключение и включению ботов
Плагин подчиняется настройкам bot_quota и bot_quota_mode
Команды:
!bots или /bots - включает и отключает ботов
Настройки:

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

sm_bot_toggle - давать ли игрокам доступ к управлению ботами?
sm_bot_toggle_default - должны ли боты появлятся автоматический на каждой карте?
sm_bot_toggle_command - команда с помощью которой игроки могут управлять ботами (по стандарту: bots)
sm_bot_toggle_chat - можно ли использовать команду по управлению ботами без слеша и !
sm_bot_toggle_announce - оповещать ли игроков о том что есть функция управления ботами?
sm_bot_toggle_rounds - разрешить игрокам управлять ботами только определенное количество раундов ( 0 = разрешать всего; все что более 0 = количество раундов)
sm_bot_toggle_cooldown - через сколько секунд после прошлого включения/отключения ботов разрешить следующее использование команды?
sm_bot_toggle_flag - админы с каким флагом имеют преимущество над действиями игроков?

Установка:
Скомпилировать при необходимости
Раскидать файлы по папкам (.smx в plugins, .cfg в configs и так далее)]
Исходный код

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

#include <sourcemod>
#include <cstrike>

#pragma semicolon 1
#pragma newdecls required 

public Plugin myinfo =
{
   name = "Bot Toggle",
   author = "decowboy",
   description = "Allow players to enable/disable bots with a simple chat command",
   version = "1.0.1",
   url = "https://forums.alliedmods.net/showthread.php?p=2353510"
}


Handle g_Cvar_BotQuota = INVALID_HANDLE; // handle for cvar bot_quota
Handle g_Cvar_BotQuotaMode = INVALID_HANDLE; // handle for cvar bot command

int botQuota = 0; // desired bot quota according to cvar
int botQuotaMode = -1; // bot quota mode according to cvar
int botEnabled = true; // whether bots are currently toggled on
int humanCount = 0; // current number of human players

int botMemory = -1; // value of the bot_quota cvar before overriding it
int currentGeneration = 0; // current generation of timers

int toggleStamp = 0; // timestamp of last toggle

char toggleCommand[33]; // current bot command

ConVar sm_bot_toggle = null;
ConVar sm_bot_toggle_default = null;
ConVar sm_bot_toggle_command = null;
ConVar sm_bot_toggle_chat = null;
ConVar sm_bot_toggle_announce = null;
ConVar sm_bot_toggle_rounds = null;
ConVar sm_bot_toggle_cooldown = null;
ConVar sm_bot_toggle_flag = null;

#define TOGGLE_ACTIVE 1
#define TOGGLE_DISABLED 0
#define TOGGLE_INACTIVE -1
#define TOGGLE_EXPIRED -2

#define MODE_FILL 1
#define MODE_NORMAL 0
#define MODE_DISABLED -1

#define MAX_INT 2147483647



// Main function
public void OnPluginStart()
{
   
   // Get and store the bot_quota and bot_quota_mode cvars
   g_Cvar_BotQuota = FindConVar("bot_quota");
   botQuota = GetConVarInt(g_Cvar_BotQuota);
   
   g_Cvar_BotQuotaMode = FindConVar("bot_quota_mode");
   char mode[33];
   GetConVarString(g_Cvar_BotQuotaMode, mode, sizeof(mode));
   setQuotaMode(mode);
   
   // Keep track of future cvar bot_quota and bot_quota_mode changes
   HookConVarChange(g_Cvar_BotQuota, BotQuotaChanged);
   HookConVarChange(g_Cvar_BotQuotaMode, BotQuotaModeChanged);
   
   // Hook up to round start events
   HookEvent("round_start", Event_RoundStart);
   
   // Hook up to (post) player team switch
   HookEvent("player_team", Event_PlayerTeam, EventHookMode_Post);
   
   // Hook up to bot add commands
   RegServerCmd("bot_add", BotAdd);
   RegServerCmd("bot_add_t", BotAdd);
   RegServerCmd("bot_add_ct", BotAdd);
   
   // Register all plugin cvars
   sm_bot_toggle = CreateConVar("sm_bot_toggle", "1", "Should players be allowed to toggle bots? (0/1)");
   sm_bot_toggle_default = CreateConVar("sm_bot_toggle_default", "1", "Should bots automatically spawn when a new map is loaded? (0/1)");
   sm_bot_toggle_command = CreateConVar("sm_bot_toggle_command", "bots", "Command for players to toggle bots with (do *not* prepend with exclamation mark or slash; max. 32 characters)");
   sm_bot_toggle_chat = CreateConVar("sm_bot_toggle_chat", "0", "Should the use of the toggle command in chat without an exclamation mark or slash in front of it be allowed? (0/1)");
   sm_bot_toggle_announce = CreateConVar("sm_bot_toggle_announce", "1", "Announce the toggle command to players in chat? (0/1)");
   sm_bot_toggle_rounds = CreateConVar("sm_bot_toggle_rounds", "2", "Only allow players to toggle bots during the first x rounds of the match (-1 or 0 = always allow)");
   sm_bot_toggle_cooldown = CreateConVar("sm_bot_toggle_cooldown", "6", "Do not allow players to toggle bots for x seconds after the previous toggle (-1 or 0 = always allow)");
   sm_bot_toggle_flag = CreateConVar("sm_bot_toggle_flag", "generic", "Allow admins with this flag to toggle even if other players can't (e.g. \"kick\" for admin kick flag; max. 32 characters; leave empty to disable)");
   
   // Keep track of future cvar bot command changes
   HookConVarChange(sm_bot_toggle_command, BotCommandChanged);
   
   // Execute the config-file
   AutoExecConfig(true, "plugin_bot_toggle");
   
   // Get the command set by cvar
   GetConVarString(sm_bot_toggle_command, toggleCommand, sizeof(toggleCommand));
   
   // And register that command
   RegConsoleCmd(toggleCommand, Command_Toggle);
   
   // Check whether the admin flag set by cvar exists
   IsAdmin(-1);
   
}


// This function is called when a bot is added
public Action BotAdd(int args)
{
   // If the current bot quota is zero, then this is the first bot
   
   // If the bot_quota_mode cvar is also set to fill,
   // then we need to first normalize the value of the bot quota cvar
   // to maintain: bot quota = bot count + player count
   
   // If the bot quota mode cvar is set to normal,
   // then the server will itself maintain: bot quota = bot count
   
   if (botQuota == 0 && botQuotaMode == MODE_FILL) {
      // Set the bot_quota cvar to the amount of human players
      // This shouldn't spawn any bots
      SetConVarInt(g_Cvar_BotQuota, humanCount);
   }
   
   // A bot was added, so bots are enabled
   botEnabled = true;
   botMemory = -1;
   
   return Plugin_Continue;
}


// This function is called when the value of cvar bot_quota_mode changes
public void BotQuotaModeChanged(Handle cvar, const char[] oldValue, const char[] newValue) {
   
   setQuotaMode(newValue);
}


// Update the bot quota mode
public void setQuotaMode(const char[] value) {

   if (strcmp(value, "fill", false) == 0) {
      botQuotaMode = MODE_FILL;
   } else if (strcmp(value, "normal", false) == 0) {
      botQuotaMode = MODE_NORMAL;
   } else {
      botQuotaMode = MODE_DISABLED;
   }   
}

// This function is called when a round ends
public Action CS_OnTerminateRound(float &delay, CSRoundEndReason &reason) {
   
   // Check if the game is commencing
   if (reason == CSRoundEnd_GameStart) {
      
      // If bots should not spawn at map start, then set a timer to despawn them
      if (GetConVarInt(sm_bot_toggle_default) != 1) {
         
         CreateTimer(5.0, TimerDespawn);
         
      }
   }
   
}


// This timer despawns bots if possible
public Action TimerDespawn(Handle timer) {
   
   // If there are bots in the server
   if (botEnabled && botQuota > 0) {
      
      PrintToServer("[SM] Despawning bots on cvar request");
      
      // Despawn them
      botEnabled = false;
      HandleChanges();   
      
   }
   
}


// This function is called when the value of cvar bot_quota changes
public void BotQuotaChanged(Handle cvar, const char[] oldValue, const char[] newValue) {
   
   int newQuota = StringToInt(newValue);
   
   // Store the new value of the bot_quota cvar
   botQuota = newQuota;
   
   // If the value is higher than zero and the bot_mode cvar is set to 'fill',
   // then bots have been enabled by a map change, another plugin or a server admin
   if (botQuota > 0) {
      
      // So update accordingly
      botEnabled = true;
      botMemory = -1;
      
   }
   
}


// This function is called when the value of the bot toggle command cvar changes
public void BotCommandChanged(Handle cvar, const char[] oldValue, const char[] newValue) {
   
   // Register the new command
   strcopy(toggleCommand, sizeof(toggleCommand), newValue);
   RegConsoleCmd(toggleCommand, Command_Toggle);
   
}


// This function is called when a round starts
public void Event_RoundStart(Handle event, const char[] name, bool dontBroadcast)
{
   
   // Announce to players if instructed to do so by cvar
   if (GetConVarInt(sm_bot_toggle_announce)) {
      AnnounceToAll();
   }
   
}


// This function is called when a player joins
public void OnClientPostAdminCheck(int client)
{
   
   // Update the number of current players
   UpdatePlayerCount();
   
   // Set timers to announce the plugin to this player
   AnnounceToPlayer(client);
   
}


// This function is called when a player leaves
public void OnClientDisconnect(int client) {
   
   // Update the number of current players
   UpdatePlayerCount();
   
}


public Action Event_PlayerTeam(Handle event, const char[] name, bool dontBroadcast)
{
   
   // Update the number of current players
   UpdatePlayerCount();
   
}


// This function checks whether a player is human and non-spectator
public bool IsParticipatingPlayer(int client) {
   if (client < 1 || client > MaxClients) {
      return false;
   }
   
   if (!IsClientInGame(client) || IsFakeClient(client)) {
         return false;
   }
   
   if (GetClientTeam(client) <= CS_TEAM_SPECTATOR) {
      return false;
   }
   
   return true;
}


// This function updates the number of current players
public void UpdatePlayerCount() {
   
   int newCount = 0;
   
   // Loop through players
   for (int i = 1; i <= MaxClients; i++)
   {
      
      // If the client is in-game and not a bot
      if (IsParticipatingPlayer(i)) {
         
         newCount++;
         
      }
      
   }
   
         
   // Store the updated count of human players
   humanCount = newCount;
   
}


// This function handles the toggling of bots
public void HandleChanges() {
   
   int processedChanges = false;
   
   // If bots are toggled on
   if (botEnabled) {
   
      // If the bot_quota cvar hasn't been restored yet
      if (botMemory > -1) {
         
         // Calculate the amount of bots to add based on the bot quota cvar
         int addBots = botMemory;
         
         // If the bot quota mode cvar is set to "fill", then this value
         // also includes the human players, so we should subtract that
         if (botQuotaMode == MODE_FILL) {
            addBots = addBots - humanCount;
         }
         
         // Add enough bots
         // This should raise the bot_quota cvar back to its original value
         for (int i = 1; i <= addBots; i++) {
            ServerCommand("bot_add");
         }
         
         // Remember that changes have been processed
         processedChanges = true;
         
      }
   
   } else { // If bots are toggled off
   
      // If the bot_quota hasn't been set to zero yet
      if (botQuota > 0) {
         
         // Remember the current bot_quota for later
         botMemory = botQuota;
         
         // Set the bot_quota to zero
         SetConVarInt(g_Cvar_BotQuota, botMemory);
         
         // Kick all bots
         ServerCommand("bot_kick");
         
         // Remember that changes have been processed
         processedChanges = true;
         
      }
   
   }
   
   // If we just processed any changes
   if (processedChanges) {
      
      // Update the timestamp of the last toggle to right now
      toggleStamp = GetTime();
      
      // Create timers to once more announce the plugin to all human players
      AnnounceToAll();
      
   }
   
}


// This function is called when a player gives the bot toggle command
public Action Command_Toggle(int client, int args)
{
   
   // Get the bot toggle command used
   char buffer[33];
   GetCmdArg(0, buffer, 33);
   
   // Discard if an outdated bot toggle command was used
   if (strcmp(buffer, toggleCommand) < 0) {
      return Plugin_Handled;
   }
   
   
   // Ignore commands from spectators, bots and the likes
   if (!IsParticipatingPlayer(client)) {
      
      return Plugin_Handled;
      
   }
   
   // Process this toggle
   processToggle(client);
   
   return Plugin_Handled;
   
}


// This function is called when a player sends a chat message
public Action OnClientSayCommand(int client, const char[] command, const char[] sArgs)
{
   
   // Check if command without a ! or / is permitted by cvar
   // If not, we don't have to get involved with chat messages
   if (GetConVarInt(sm_bot_toggle_chat) != 1) {
      return Plugin_Continue;
   }
   
   // Ignore chats from spectators, bots and the likes
   if (!IsParticipatingPlayer(client)) {
      
      return Plugin_Continue;
      
   }
   
   // If the player is asking to toggle the bots
   if (strcmp(sArgs, toggleCommand, false) == 0)
   {
      
      // Process this toggle
      processToggle(client);
      
      // Block the message from broadcasting
      return Plugin_Handled;
      
   }
 
   // Otherwise, let the chat message continue
   return Plugin_Continue;
}


// This function is called when a player requests to toggle bots
public Action processToggle(int client) {

      // Calculate the remaining cooldown if one is set by cvar
      int cooldownRemaining = GetConVarInt(sm_bot_toggle_cooldown);
      if (cooldownRemaining > 0) {
         cooldownRemaining += toggleStamp - GetTime();
      
         // Discard when we are still in cooldown from the last toggle
         // But make an exception for admins if instructed so by cvar
         if (cooldownRemaining > 0 && !IsAdmin(client)) {
            
            // Sneaky little hack.
            // If we raise the remaining time by one second, the resulting number will always be larger than 1.
            // That way our language string can be phrased in plural without it leading to "1 seconds remaining"
            // Plus, it's one second. Who will know the difference?
            
            cooldownRemaining++;
            
            
            if (botEnabled) {
                  
               PrintToChat(client,"You need to wait %d seconds before disabling bots.", cooldownRemaining);
                  
            } else {
                  
               PrintToChat(client,"You need to wait %d seconds before enabling bots.", cooldownRemaining);
                  
            }
            
            return;
            
         }
      }
      
      // If it is too late in the match to toggle bots, do not allow to toggle
      // But make an exception for admins if instructed to do so by cvar
      if (PluginActive() == TOGGLE_EXPIRED && !IsAdmin(client)) {
            
         // Notify the player that his command was discarded
         if (botEnabled) {
            
            PrintToChat(client,"You can't disable bots this late in the match.");
            
         } else {
            
            PrintToChat(client,"You can't enable bots this late in the match.");
            
         }
            
      } else { // Or if this plugin is currently active
      
         // If bots are currently enabled
         if (botEnabled) {
            
            // Log to the server
            PrintToServer("[SM] %N just disabled the bots", client);
            
            // Notify all players that bots will be disabled
            PrintCenterTextAll("%N just disabled the bots", client);
            
            // Disable the bots
            botEnabled = false;
            
            
            
         } else { // Or if they are currently disabled
            
            // Log to the server
            PrintToServer("[SM] %N just enabled the bots", client);
         
            // Notify all players that bots will be enabled
            PrintCenterTextAll("%N just enabled the bots", client);
            
            // Enable the bots   
            botEnabled = true;
            
         }
         
         // Perform additional operations to handle the change
         HandleChanges();
      
      }
      
}


// This function checks whether a player is an admin
// May also be called to verify if the admin flag exists
public bool IsAdmin(int client) {
   
   // Get the admin flag set by cvar
   char toggleFlag[33];
   GetConVarString(sm_bot_toggle_flag, toggleFlag, sizeof(toggleFlag));
   
   // Cancel if no admin flag was set
   if (strcmp(toggleFlag, "", false) == 0) {
      
      return false;
      
   }
   
   AdminFlag flag;
   bool result = FindFlagByName(toggleFlag, flag);
   
   // If the admin flag does not exist
   if (result == false) {
      
      PrintToServer("[SM] Admin flag '%s' does not exist", toggleFlag);
      return false;
      
   }
   
   // Check whether the player is in-game and not a bot
   if (!IsParticipatingPlayer(client)) {
      
      return false;
      
   }
   
   // Check whether the player has the admin flag
    AdminId admin = GetUserAdmin(client);
   
   if((admin != INVALID_ADMIN_ID) && (GetAdminFlag(admin, flag, Access_Real) == true))
   {
        return true;
   }
   
   return false;
   
}


// This function starts timers to announce the plugin to all players
public void AnnounceToAll() {
   
   // Prevent the timer generation counter from overflowing
   if (currentGeneration == MAX_INT) {
      currentGeneration = -1;
   }
   
   // Raise the timer generation counter
   currentGeneration++;

   // If the plugin is active, create new timers
   if (PluginActive() == TOGGLE_ACTIVE) {
      CreateTimer(getTimerIntervals(0),TimerAnnounceAll, currentGeneration);
      CreateTimer(getTimerIntervals(1),TimerAnnounceAll, currentGeneration);
   }
   
}


// This function starts timer to announce the plugin to a certain player
public void AnnounceToPlayer(int client) {
   
   // If the plugin is active, create new timers
   if (PluginActive() == TOGGLE_ACTIVE) {
      DataPack pack1;
      CreateDataTimer(getTimerIntervals(0),TimerAnnouncePlayer, pack1);
      pack1.WriteCell(client);
      pack1.WriteCell(currentGeneration);
      
      DataPack pack2;
      CreateDataTimer(getTimerIntervals(1),TimerAnnouncePlayer, pack2);
      pack2.WriteCell(client);
      pack2.WriteCell(currentGeneration);
   }
   
}


// Calculate the timer intervals
public float getTimerIntervals(int index) {
   float interval = 0.0;
   
   // Set the interval based on the index
   if (index == 0) {
      interval = 5.0;
   }
   if (index == 1) {
      interval = 30.0;
   }
   
   // The timer interval must be greater than the cooldown
   // to avoid annoucing this plugin when players can't use it
   int cooldown = GetConVarInt(sm_bot_toggle_cooldown) + 1;
   if (interval < cooldown) {
      interval = float(cooldown);
   }
   
   return interval;
}


// This timer announces the plugin to a player
public Action TimerAnnouncePlayer(Handle timer, Handle pack)
{
   
   // Parse the data pack
   ResetPack(pack);
   
   int client = ReadPackCell(pack);
   int generation = ReadPackCell(pack);
   
   // Cancel if there is a newer generation of timers
   if (!CheckGeneration(generation)) {
      return;
   }
   
   // Cancel if announcing is disabled by cvar
   if (GetConVarInt(sm_bot_toggle_announce) != 1) {
      return;
   }
   
   // Announce the plugin to the given player
   SendAnnouncement(client);
   
}


// This timer announces the plugin to all players
public Action TimerAnnounceAll(Handle timer, any generation)
{
   
   // Cancel if there is a newer generation of timers
   if (!CheckGeneration(generation)) {
      return;
   }
   
   // Cancel if announcing is disabled by cvar
   if (GetConVarInt(sm_bot_toggle_announce) != 1) {
      return;
   }
   
   // Loop through players
   for (int i = 1; i <= MaxClients; i++)
   {
      
      // If the client is in-game and not a bot
      if (IsParticipatingPlayer(i)) {
         
         // Announce the plugin to that player
         SendAnnouncement(i);
         
      }
      
   }
   
}


// Check whether a generation is still current
public bool CheckGeneration(int generation) {
   if (generation == currentGeneration) {
      return true;
   } else {
      return false;
   }
}


// This function return whether this plugin should be active
//    1 = active
//  <=0 = inactive
public int PluginActive() {
   
   // Check whether this plugin is disabled by cvar
   if (GetConVarInt(sm_bot_toggle) != 1) {
      return TOGGLE_DISABLED;
   }
   
   
   // In case the bot_quota_mode cvar is set to "fill"
   if (botQuotaMode == MODE_FILL) {
      
      // Only if the bot quota is higher than the current number of human players,
      // it is useful for the plugin to be active
      // Otherwise there wouldn't be any bots anyway, regardless of whether bots are toggled on
      
      // As consequence, this will effectively disable this plugin if the bot_quota_mode cvar is set to "fill"
      // and the bot_quota cvar is set to zero by something other than this plugin itself
      
      if (botQuota <= humanCount && botMemory <= humanCount) {
         return TOGGLE_INACTIVE;
      }
      
   }
   
   
   // If a cvar was set to only allow toggling of bots during the first x rounds, then verify that
   // This is to prevent players griefing
   
   // As an alternative, players could vote to restart the match or change the map,
   // which will trigger a new warmup round and thus allow players to toggle the bots again
   
   
   int maxRounds = GetConVarInt(sm_bot_toggle_rounds);
   if (maxRounds > 0) {
      
      // Count the number of rounds won so far
      int currentRounds = CS_GetTeamScore(CS_TEAM_CT) + CS_GetTeamScore(CS_TEAM_T);
      
      // If more rounds passed than allowed by cvar, this plugin should be inactive
      if (currentRounds >= maxRounds) {
         return TOGGLE_EXPIRED;
      }
      
   }
   
   // If none of the above, this plugin should be active
   return TOGGLE_ACTIVE;
   
}


// This function announces the plugin to a player
public void SendAnnouncement(int client)
{

   // If the client is in-game and not a bot
   if(IsParticipatingPlayer(client))
   {
      
      // If this plugin is currently active
      if (PluginActive() == TOGGLE_ACTIVE) {
            
         // Get the bot toggle command and check whether a ! or / is required
         char togglePrepend[2] = "";
         if (GetConVarInt(sm_bot_toggle_chat) != 1) {
            togglePrepend[0] = '!';
         }
            
         // If bots are currently toggled on
         if (botEnabled) {
            
            // Announce how to disable bots
            PrintToChat(client,">> Want to play without bots? Say: %s%s", togglePrepend, toggleCommand);
            
         } else {
            
            // Announce how to enable bots
            PrintToChat(client,">> Want to play with bots? Say: %s%s", togglePrepend, toggleCommand);
            
         }
         
      }
      
   }
   
   
}
Вложения
bottoggle.zip
(5.59 KiB) Загружено 97 раз

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

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

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