Untitled diff

Created Diff never expires
76 removals
Lines
Total
Removed
Words
Total
Removed
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
767 lines
103 additions
Lines
Total
Added
Words
Total
Added
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
786 lines
#include <sourcemod>
#include <sourcemod>
#include <cstrike>
#include <cstrike>


public Plugin:myinfo =
#pragma semicolon 1
#pragma newdecls required

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




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


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


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


new toggleStamp = 0; // timestamp of last toggle
int toggleStamp = 0; // timestamp of last toggle


String:toggleCommand[33]; // current bot command
char toggleCommand[33]; // current bot command


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


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


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


#define MAX_INT 2147483647
#define MAX_INT 2147483647






// Main function
// Main function
public OnPluginStart()
public void OnPluginStart()
{
{
// Get and store the bot_quota and bot_quota_mode cvars
// Get and store the bot_quota and bot_quota_mode cvars
g_Cvar_BotQuota = FindConVar("bot_quota");
g_Cvar_BotQuota = FindConVar("bot_quota");
botQuota = GetConVarInt(g_Cvar_BotQuota);
botQuota = GetConVarInt(g_Cvar_BotQuota);
g_Cvar_BotQuotaMode = FindConVar("bot_quota_mode");
g_Cvar_BotQuotaMode = FindConVar("bot_quota_mode");
decl String:mode[33];
char mode[33];
GetConVarString(g_Cvar_BotQuotaMode, mode, sizeof(mode));
GetConVarString(g_Cvar_BotQuotaMode, mode, sizeof(mode));
setQuotaMode(mode);
setQuotaMode(mode);
// Keep track of future cvar bot_quota and bot_quota_mode changes
// Keep track of future cvar bot_quota and bot_quota_mode changes
HookConVarChange(g_Cvar_BotQuota, BotQuotaChanged);
HookConVarChange(g_Cvar_BotQuota, BotQuotaChanged);
HookConVarChange(g_Cvar_BotQuotaMode, BotQuotaModeChanged);
HookConVarChange(g_Cvar_BotQuotaMode, BotQuotaModeChanged);
// Hook up to round start events
// Hook up to round start events
HookEvent("round_start", Event_RoundStart);
HookEvent("round_start", Event_RoundStart);
// Hook up to (post) player team switch
// Hook up to (post) player team switch
HookEvent("player_team", Event_PlayerTeam, EventHookMode_Post);
HookEvent("player_team", Event_PlayerTeam, EventHookMode_Post);
// Hook up to bot add commands
// Hook up to bot add commands
RegServerCmd("bot_add", BotAdd);
RegServerCmd("bot_add", BotAdd);
RegServerCmd("bot_add_t", BotAdd);
RegServerCmd("bot_add_t", BotAdd);
RegServerCmd("bot_add_ct", BotAdd);
RegServerCmd("bot_add_ct", BotAdd);
// Register all plugin cvars
// Register all plugin cvars
sm_bot_toggle = CreateConVar("sm_bot_toggle", "1", "Should players be allowed to toggle bots? (0/1)");
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 spawn when a new map is loaded? (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_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 without an exclamation mark or slash in front of it be allowed? (0/1)");
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_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 (-1 or 0 = always allow)");
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 a toggle (-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; max. 32 characters; leave empty to disable)");
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
// Keep track of future cvar bot command changes
g_Cvar_BotCommand = FindConVar("sm_bot_toggle_command");
HookConVarChange(sm_bot_toggle_command, BotCommandChanged);
HookConVarChange(g_Cvar_BotCommand, BotCommandChanged);
// Execute the config-file
// Execute the config-file
AutoExecConfig(true, "plugin_bot_toggle");
AutoExecConfig(true, "plugin_bot_toggle");
// Get the command set by cvar
// Get the command set by cvar
GetConVarString(sm_bot_toggle_command, toggleCommand, sizeof(toggleCommand));
GetConVarString(sm_bot_toggle_command, toggleCommand, sizeof(toggleCommand));
// And register that command
// And register that command
RegConsoleCmd(toggleCommand, Command_Toggle);
RegConsoleCmd(toggleCommand, Command_Toggle);
// Check whether the admin flag set by cvar exists
IsAdmin(-1);
}
}




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




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




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


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


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




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




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




// This function is called when the value of the bot toggle command cvar changes
// This function is called when the value of the bot toggle command cvar changes
public BotCommandChanged(Handle:cvar, const String:oldValue[], const String:newValue[]) {
public void BotCommandChanged(Handle cvar, const char[] oldValue, const char[] newValue) {
// As the command is registered on plugin start, changing the bot command cvar won't
// have any affect until the plugin is restarted.
PrintToServer("[SM] Changes to the bot command will be applied when this plugin restarts");
// Register the new command
strcopy(toggleCommand, sizeof(toggleCommand), newValue);
RegConsoleCmd(toggleCommand, Command_Toggle);
}
}




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




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




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




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




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




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




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




// This function is called when a player gives the bot toggle command
// This function is called when a player gives the bot toggle command
public Action Command_Toggle(int client, int args)
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
// Ignore commands from spectators, bots and the likes
if (!IsParticipatingPlayer(client)) {
if (!IsParticipatingPlayer(client)) {
return Plugin_Continue;
return Plugin_Handled;
}
}
// Process this toggle
// Process this toggle
return processToggle(client);
processToggle(client);
return Plugin_Handled;
}
}




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




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


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




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




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


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




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




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




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




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




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




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




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


// If the client is in-game and not a bot
// If the client is in-game and not a bot
if(IsParticipatingPlayer(client))
if(IsParticipatingPlayer(client))
{
{
// If this plugin is currently active
// If this plugin is currently active
if (PluginActive() == 1) {
if (PluginActive() == TOGGLE_ACTIVE) {
// Get the bot toggle command and check whether a ! or / is required
// Get the bot toggle command and check whether a ! or / is required
decl String:togglePrepend[1] = "";
char togglePrepend[2] = "";
GetConVarString(sm_bot_toggle_command, toggleCommand, sizeof(toggleCommand));
if (GetConVarInt(sm_bot_toggle_chat) != 1) {
if (GetConVarInt(sm_bot_toggle_chat) != 1) {
togglePrepend[0] = '!';
togglePrepend[0] = '!';
}
}
// If bots are currently toggled on
// If bots are currently toggled on
if (botEnabled) {
if (botEnabled) {
// Announce how to disable bots
// Announce how to disable bots
PrintToChat(client,">> Want to play without bots? Say: %s%s", togglePrepend, toggleCommand);
PrintToChat(client,">> Want to play without bots? Say: %s%s", togglePrepend, toggleCommand);
} else {
} else {
// Announce how to enable bots
// Announce how to enable bots
PrintToChat(client,">> Want to play with bots? Say: %s%s", togglePrepend, toggleCommand);
PrintToChat(client,">> Want to play with bots? Say: %s%s", togglePrepend, toggleCommand);
}
}
}
}
}
}
}
}