sourcemod-plugins/scripting/abm.sp
2023-12-23 12:30:25 -06:00

3774 lines
No EOL
90 KiB
SourcePawn

//# vim: set filetype=cpp :
/*
ABM a SourceMod L4D2 Plugin
Copyright (C) 2016-2017 Victor "NgBUCKWANGS" Gonzalez
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the
Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <sourcemod>
#include <sdktools>
#include <sdkhooks>
#undef REQUIRE_EXTENSIONS
#include <left4dhooks>
#undef REQUIRE_PLUGIN
#include <CreateSurvivorBot>
#pragma semicolon 1
#pragma newdecls required
#define PLUGIN_VERSION "0.1.97q"
#define LOGFILE "addons/sourcemod/logs/abm.log" // TODO change this to DATE/SERVER FORMAT?
Handle g_GameData = null;
ArrayList g_sQueue;
ArrayList g_iQueue;
// menu parameters
#define menuArgs g_menuItems[client] // Global argument tracking for the menu system
#define menuArg0 g_menuItems[client][0] // GetItem(1...)
#define menuArg1 g_menuItems[client][1] // GetItem(2...)
int g_menuItems[MAXPLAYERS + 1][2];
// menu tracking
#define g_callBacks g_menuStack[client]
ArrayStack g_menuStack[MAXPLAYERS + 1];
Function callBack;
char g_QKey[64]; // holds players by STEAM_ID
StringMap g_QDB; // holds player records linked by STEAM_ID
StringMap g_QRecord; // changes to an individual STEAM_ID mapping
StringMap g_QRtmp; // temporary QRecord holder for checking records without changing the main
StringMap g_Cvars; // locked cvars end up here
char g_InfectedNames[6][] = {"Boomer", "Smoker", "Hunter", "Spitter", "Jockey", "Charger"};
char g_SurvivorNames[8][] = {"Nick", "Rochelle", "Coach", "Ellis", "Bill", "Zoey", "Francis", "Louis"};
char g_SurvivorPaths[8][] = {
"models/survivors/survivor_gambler.mdl",
"models/survivors/survivor_producer.mdl",
"models/survivors/survivor_coach.mdl",
"models/survivors/survivor_mechanic.mdl",
"models/survivors/survivor_namvet.mdl",
"models/survivors/survivor_teenangst.mdl",
"models/survivors/survivor_biker.mdl",
"models/survivors/survivor_manager.mdl",
};
int g_OS; // 0: Linux, 1: Windows (Prevent crash on Windows /w Zoey)
char g_sB[512]; // generic catch all string buffer
char g_pN[128]; // a dedicated buffer to storing a temporary name
char g_name[128], g_tmpName[128]; // g_QDB client name
int g_client, g_tmpClient; // g_QDB client id
int g_target, g_tmpTarget; // g_QDB player (human or bot) id
int g_lastid, g_tmpLastid; // g_QDB client's last known bot id
int g_onteam, g_tmpOnteam = 1; // g_QDB client's team
bool g_queued, g_tmpQueued; // g_QDB client's takeover state
bool g_inspec, g_tmpInspec; // g_QDB check client's specator mode
bool g_status, g_tmpStatus; // g_QDB client life state
bool g_update, g_tmpUpdate; // g_QDB should we update this record?
char g_model[32], g_tmpModel[32]; // g_QDB client's model
char g_ghost[32], g_tmpGhost[32]; // g_QDB queued model for SwitchTeam
float g_origin[3], g_tmpOrigin[3]; // g_QDB client's origin vector
float g_rdelay, g_tmpRdelay; // g_QDB delay time for SI respawn
Handle g_rDelays[MAXPLAYERS + 1]; // Individual respawn SI timers
int g_models[8];
Handle g_MkBotsTimer;
Handle g_ADAssistant; // Tries to make sure the AD starts
Handle g_AD; // Assistant Director Timer
int g_survivorSet; // 0 = l4d2 survivors, 4 = l4d1 survivors
bool g_survivorSetScan; // Check to discover the survivor set
bool g_IsVs;
bool g_IsCoop;
bool g_AssistedSpawning = false;
bool g_ADFreeze = true;
bool g_AutoWave;
int g_ADInterval;
ConVar g_cvVersion; // abm_version
ConVar g_cvLogLevel; int g_LogLevel; // abm_loglevel
ConVar g_cvMinPlayers; int g_MinPlayers; // abm_minplayers
ConVar g_cvPrimaryWeapon; char g_PrimaryWeapon[64]; // abm_primaryweapon
ConVar g_cvSecondaryWeapon; char g_SecondaryWeapon[64]; // abm_secondaryweapon
ConVar g_cvThrowable; char g_Throwable[64]; // abm_throwable
ConVar g_cvHealItem; char g_HealItem[64]; // abm_healitem
ConVar g_cvConsumable; char g_Consumable[64]; // abm_consumable
ConVar g_cvTankChunkHp; int g_TankChunkHp; // abm_tankchunkhp
ConVar g_cvSpawnInterval; int g_SpawnInterval; // abm_spawninterval
ConVar g_cvAutoHard; int g_AutoHard; // abm_autohard
ConVar g_cvUnlockSI; int g_UnlockSI; // abm_unlocksi
ConVar g_cvJoinMenu; int g_JoinMenu; // abm_joinmenu
ConVar g_cvTeamLimit; int g_TeamLimit; // abm_teamlimit
ConVar g_cvOfferTakeover; int g_OfferTakeover; // abm_offertakeover
ConVar g_cvStripKick; int g_StripKick; // abm_stripkick
ConVar g_cvAutoModel; int g_AutoModel; // abm_automodel
ConVar g_cvKeepDead; int g_KeepDead; // abm_keepdead
ConVar g_cvIdentityFix; int g_IdentityFix; // abm_identityfix
ConVar g_cvZoey; int g_Zoey; // abm_zoey
ConVar g_cvRespawnDelay; float g_RespawnDelay; // abm_respawndelay
ConVar g_cvWrapSwitch; int g_WrapSwitch; // _abm_wrapswitch
ConVar g_cvMaxMinions; // z_minion_limit
ConVar g_cvMaxSurvivors; // survivor_limit
ConVar g_cvMaxInfecteds; // z_max_player_zombies
int g_MaxMates; // X vs X
int g_SILimits;
ConVar g_cvTankHealth;
ConVar g_cvMaxSwitches;
ConVar g_cvGameMode;
ConVar g_cvVDOUHandle;
ConVar g_cvVDOUOrigin;
ConVar g_cvConsistency; char g_consistency[2];
char g_VDOUCurVal[2048];
char g_VDOUOrigin[2048];
public Plugin myinfo= {
name = "ABM",
author = "Victor \"NgBUCKWANGS\" Gonzalez",
description = "A 5+ Player Enhancement Plugin for L4D2",
version = PLUGIN_VERSION,
url = "https://gitlab.com/vbgunz/ABM"
}
public void OnPluginStart() {
Echo(2, "OnPluginStart");
g_GameData = LoadGameConfigFile("abm");
if (g_GameData == null) {
SetFailState("[ABM] Game data missing!");
}
HookEvent("player_first_spawn", OnSpawnHook);
HookEvent("player_spawn", OnAllSpawnHook);
HookEvent("player_death", OnDeathHook, EventHookMode_Pre);
HookEvent("player_disconnect", CleanQDBHook);
HookEvent("player_afk", GoIdleHook);
HookEvent("player_team", QTeamHook);
HookEvent("player_bot_replace", QAfkHook);
HookEvent("bot_player_replace", QBakHook);
HookEvent("player_activate", PlayerActivateHook, EventHookMode_Pre);
HookEvent("player_connect", PlayerActivateHook, EventHookMode_Pre);
HookEvent("round_end", RoundFreezeEndHook, EventHookMode_Pre);
HookEvent("mission_lost", RoundFreezeEndHook, EventHookMode_Pre);
HookEvent("round_freeze_end", RoundFreezeEndHook, EventHookMode_Pre);
HookEvent("map_transition", RoundFreezeEndHook, EventHookMode_Pre);
HookEvent("round_start", RoundStartHook, EventHookMode_Pre);
// base the following on a cvar
HookUserMessage(GetUserMessageId("SayText2"), UserMessageHook, true);
RegAdminCmd("abm", MainMenuCmd, ADMFLAG_GENERIC);
RegAdminCmd("abm-menu", MainMenuCmd, ADMFLAG_GENERIC, "Menu: (Main ABM menu)");
RegAdminCmd("abm-join", SwitchTeamCmd, ADMFLAG_GENERIC, "Menu/Cmd: <TEAM> | <ID> <TEAM>");
RegAdminCmd("abm-takeover", SwitchToBotCmd, ADMFLAG_GENERIC, "Menu/Cmd: <ID> | <ID1> <ID2>");
RegAdminCmd("abm-respawn", RespawnClientCmd, ADMFLAG_GENERIC, "Menu/Cmd: <ID> [ID]");
RegAdminCmd("abm-model", AssignModelCmd, ADMFLAG_GENERIC, "Menu/Cmd: <MODEL> | <MODEL> <ID>");
RegAdminCmd("abm-strip", StripClientCmd, ADMFLAG_GENERIC, "Menu/Cmd: <ID> [SLOT]");
RegAdminCmd("abm-teleport", TeleportClientCmd, ADMFLAG_GENERIC, "Menu/Cmd: <ID1> <ID2>");
RegAdminCmd("abm-cycle", CycleBotsCmd, ADMFLAG_GENERIC, "Menu/Cmd: <TEAM> | <ID> <TEAM>");
RegAdminCmd("abm-reset", ResetCmd, ADMFLAG_GENERIC, "Cmd: (Use only in case of emergency)");
RegAdminCmd("abm-info", QuickClientPrintCmd, ADMFLAG_GENERIC, "Cmd: (Print some diagnostic information)");
RegAdminCmd("abm-mk", MkBotsCmd, ADMFLAG_GENERIC, "Cmd: <N|-N> <TEAM>");
RegAdminCmd("abm-rm", RmBotsCmd, ADMFLAG_GENERIC, "Cmd: <TEAM> | <N|-N> <TEAM>");
RegAdminCmd("abm-debug", DebugCmd, ADMFLAG_GENERIC, "");
RegConsoleCmd("takeover", SwitchToBotCmd, "Menu/Cmd: <ID> | <ID1> <ID2>");
RegConsoleCmd("join", SwitchTeamCmd, "Menu/Cmd: <TEAM> | <ID> <TEAM>");
g_OS = GetOS(); // 0: Linux 1: Windows
g_QDB = new StringMap();
g_QRecord = new StringMap();
g_QRtmp = new StringMap();
g_Cvars = new StringMap();
g_sQueue = new ArrayList(2);
g_iQueue = new ArrayList(2);
char zoeyId[2];
switch(g_OS) {
case 0: zoeyId = "5"; // 5 is the real Zoey
case 1: zoeyId = "1"; // Zoey crashes Windows servers, 1 is Rochelle
}
// remember vscript director options unlocker settings between reloads
g_cvVDOUHandle = FindConVar("l4d2_directoroptions_overwrite");
SetupCvar(g_cvVDOUOrigin, "_abm_vdouorigin", ";", "DO NOT EDIT");
if (g_cvVDOUHandle != null) {
HookConVarChange(g_cvVDOUHandle, UpdateConVarsHook);
GetConVarString(g_cvVDOUOrigin, g_VDOUOrigin, sizeof(g_VDOUOrigin));
GetConVarString(g_cvVDOUHandle, g_VDOUCurVal, sizeof(g_VDOUCurVal));
if(g_VDOUOrigin[0] != ';')
UpdateConVarsHook(g_cvVDOUHandle, g_VDOUOrigin, g_VDOUOrigin);
else
UpdateConVarsHook(g_cvVDOUHandle, g_VDOUCurVal, g_VDOUCurVal);
}
g_cvTankHealth = FindConVar("z_tank_health");
g_cvMaxSwitches = FindConVar("vs_max_team_switches");
g_cvConsistency = FindConVar("sv_consistency");
HookConVarChange(g_cvConsistency, UpdateConVarsHook);
g_cvGameMode = FindConVar("mp_gamemode");
HookConVarChange(g_cvGameMode, UpdateConVarsHook);
UpdateGameMode();
float maxClients = float(MaxClients);
g_cvMaxMinions = FindConVar("z_minion_limit");
SetConVarFloat(g_cvMaxMinions, maxClients);
g_cvMaxInfecteds = FindConVar("z_max_player_zombies");
SetConVarBounds(g_cvMaxInfecteds, ConVarBound_Upper, true, maxClients);
SetConVarFloat(g_cvMaxInfecteds, maxClients);
g_cvMaxSurvivors = FindConVar("survivor_limit");
SetConVarBounds(g_cvMaxSurvivors, ConVarBound_Upper, true, maxClients);
SetConVarFloat(g_cvMaxSurvivors, maxClients);
// thanks to bl4nk
int flags = GetConVarFlags(g_cvMaxSurvivors);
flags &= ~FCVAR_NOTIFY;
SetConVarFlags(g_cvMaxSurvivors, flags);
SetupCvar(g_cvVersion, "abm_version", PLUGIN_VERSION, "ABM plugin version");
SetupCvar(g_cvLogLevel, "abm_loglevel", "-1", "Development logging level -1: Off, 6: Max");
SetupCvar(g_cvMinPlayers, "abm_minplayers", "4", "Pruning extra survivors stops at this size");
SetupCvar(g_cvPrimaryWeapon, "abm_primaryweapon", "shotgun_chrome", "5+ survivor primary weapon");
SetupCvar(g_cvSecondaryWeapon,"abm_secondaryweapon", "baseball_bat", "5+ survivor secondary weapon");
SetupCvar(g_cvThrowable, "abm_throwable", "", "5+ survivor throwable item");
SetupCvar(g_cvHealItem, "abm_healitem", "", "5+ survivor healing item");
SetupCvar(g_cvConsumable, "abm_consumable", "adrenaline", "5+ survivor consumable item");
SetupCvar(g_cvTankChunkHp, "abm_tankchunkhp", "2500", "Health chunk per survivor on 5+ missions");
SetupCvar(g_cvSpawnInterval, "abm_spawninterval", "36", "SI full team spawn in (5 x N)");
SetupCvar(g_cvAutoHard, "abm_autohard", "1", "0: Off 1: Non-Vs > 4 2: Non-Vs >= 1");
SetupCvar(g_cvUnlockSI, "abm_unlocksi", "0", "0: Off 1: Use Left 4 Downtown 2 2: Use VScript Director Options Unlocker");
SetupCvar(g_cvJoinMenu, "abm_joinmenu", "1", "0: Off 1: Admins only 2: Everyone");
SetupCvar(g_cvTeamLimit, "abm_teamlimit", "16", "Humans on team limit");
SetupCvar(g_cvOfferTakeover, "abm_offertakeover", "1", "0: Off 1: Survivors 2: Infected 3: All");
SetupCvar(g_cvStripKick, "abm_stripkick", "0", "0: Don't strip removed bots 1: Strip removed bots");
SetupCvar(g_cvAutoModel, "abm_automodel", "1", "1: Full set of survivors 0: Map set of survivors");
SetupCvar(g_cvKeepDead, "abm_keepdead", "0", "0: The dead return alive 1: the dead return dead");
SetupCvar(g_cvIdentityFix, "abm_identityfix", "1", "0: Do not assign identities 1: Assign identities");
SetupCvar(g_cvZoey, "abm_zoey", zoeyId, "0:Nick 1:Rochelle 2:Coach 3:Ellis 4:Bill 5:Zoey 6:Francis 7:Louis");
SetupCvar(g_cvRespawnDelay, "abm_respawndelay", "1.0", "SI respawn delay time in non-competitive modes");
SetupCvar(g_cvWrapSwitch, "abm_wrapswitch", "0", "Intercept chooseteam/jointeam commands");
// clean out client menu stacks
for (int i = 1; i <= MaxClients; i++) {
g_menuStack[i] = new ArrayStack(128);
}
// Register everyone that we can find
for (int i = 1; i <= MaxClients; i++) {
SetQRecord(i, true);
}
AddCommandListener(TeamSwitchIntercept, "jointeam");
AddCommandListener(CmdIntercept, "z_spawn");
AddCommandListener(CmdIntercept, "z_spawn_old");
AddCommandListener(CmdIntercept, "z_add");
AutoExecConfig(true, "abm");
StartAD();
}
public void OnPluginEnd() {
Echo(2, "OnPluginEnd");
for (int i = 1; i <= MaxClients; i++) {
if (GetQRecord(i)) {
SetClientName(i, g_name);
}
}
}
public Action UserMessageHook(UserMsg MsgId, Handle hBitBuffer, const int[] iPlayers, int iNumPlayers, bool bReliable, bool bInit) {
Echo(2, "UserMessageHook: %d %d %d", iNumPlayers, bReliable, bInit);
// thanks https://forums.alliedmods.net/showpost.php?p=914509&postcount=10
// Skip the first two bytes
BfReadByte(hBitBuffer);
BfReadByte(hBitBuffer);
// Read the message
static char strMessage[1024];
BfReadString(hBitBuffer, strMessage, sizeof(strMessage));
// If the message equals to the string we want to filter, skip.
if (StrEqual(strMessage, "#Cstrike_Name_Change")) {
return Plugin_Handled;
}
return Plugin_Continue;
}
public Action TeamSwitchIntercept(int client, const char[] cmd, int args) {
Echo(2, "TeamSwitchIntercept: %d %s %d", client, cmd, args);
if (g_WrapSwitch) {
GetCmdArg(1, g_sB, sizeof(g_sB));
if (IsClientValid(client)) {
if (StrEqual(g_sB, "survivor", false) || StrEqual(g_sB, "2")) {
SwitchTeam(client, 2);
}
else if (StrEqual(g_sB, "infected", false) || StrEqual(g_sB, "3")) {
if (g_IsVs || IsAdmin(client)) {
SwitchTeam(client, 3);
}
}
else if (StrEqual(g_sB, "0") || StrEqual(g_sB, "1")) {
SwitchTeam(client, StringToInt(g_sB));
}
else {
SwitchTeamCmd(client, 0);
}
return Plugin_Handled;
}
}
return Plugin_Continue;
}
void SetupCvar(Handle &cvHandle, char[] name, char[] value, char[] details) {
Echo(2, "SetupCvar: %s %s %s", name, value, details);
cvHandle = CreateConVar(name, value, details);
HookConVarChange(cvHandle, UpdateConVarsHook);
UpdateConVarsHook(cvHandle, value, value);
}
Action CmdIntercept(int client, const char[] cmd, int args) {
Echo(2, "CmdIntercept: %d %s %d", client, cmd, args);
if (g_AssistedSpawning) {
GhostsModeProtector();
}
return Plugin_Continue;
}
public void OnMapStart() {
Echo(2, "OnMapStart:");
PrecacheModels();
}
public void OnMapEnd() {
Echo(2, "OnMapEnd:");
StopAD();
StringMapSnapshot keys = g_QDB.Snapshot();
g_iQueue.Clear();
g_sQueue.Clear();
// if (!g_IsVs) {
// SetConVarInt(g_cvMaxSurvivors, g_MinPlayers);
// }
for (int i; i < keys.Length; i++) {
keys.GetKey(i, g_sB, sizeof(g_sB));
g_QDB.GetValue(g_sB, g_QRecord);
g_QRecord.GetValue("onteam", g_onteam);
g_QRecord.GetValue("client", g_client);
g_QRecord.GetValue("target", g_target);
g_QRecord.SetValue("status",true,true);
if (g_onteam >= 2) {
g_QRecord.SetValue("inspec", false, true);
}
if (g_IsVs) {
g_QRecord.SetString("model", "", true);
}
else if (g_onteam == 3) {
//SwitchToSpec(g_client);
g_QRecord.SetValue("status",false, true);
g_QRecord.SetString("model", "", true);
QueueUp(g_client,3);
}
}
delete keys;
}
public void OnEntityCreated(int ent, const char[] clsName) {
Echo(2, "OnEntityCreated: %d %s", ent, clsName);
if (clsName[0] == 'f') {
bool gClip = !StrEqual(clsName, "func_playerghostinfected_clip", false);
bool iClip = !StrEqual(clsName, "func_playerinfected_clip", false);
if (!(gClip && iClip)) {
CreateTimer(2.0, KillEntTimer, EntIndexToEntRef(ent));
}
}
else if (clsName[0] == 's' && StrEqual(clsName, "survivor_bot")) {
SDKHook(ent, SDKHook_SpawnPost, AutoModel);
}
}
public Action KillEntTimer(Handle timer, any ref) {
Echo(2, "KillEntTimer: %d", ref);
int ent = EntRefToEntIndex(ref);
if (ent != INVALID_ENT_REFERENCE || IsEntityValid(ent)) {
AcceptEntityInput(ent, "kill");
}
return Plugin_Stop;
}
public Action L4D_OnGetScriptValueInt(const char[] key, int &retVal) {
Echo(5, "L4D_OnGetScriptValueInt: %s, %d", key, retVal);
// see UpdateConVarsHook "g_UnlockSI" for VScript Director Options Unlocker
if (g_UnlockSI == 1 && g_MaxMates > 4) {
int val = retVal;
if (StrEqual(key, "DominatorLimit")) val = g_MaxMates;
else if (StrEqual(key, "MaxSpecials")) val = g_MaxMates;
else if (StrEqual(key, "BoomerLimit")) val = g_SILimits;
else if (StrEqual(key, "SmokerLimit")) val = g_SILimits;
else if (StrEqual(key, "HunterLimit")) val = g_SILimits;
else if (StrEqual(key, "ChargerLimit")) val = g_SILimits;
else if (StrEqual(key, "SpitterLimit")) val = g_SILimits;
else if (StrEqual(key, "JockeyLimit")) val = g_SILimits;
if (val != retVal) {
retVal = val;
return Plugin_Handled;
}
}
return Plugin_Continue;
}
public void RoundFreezeEndHook(Handle event, const char[] name, bool dontBroadcast) {
Echo(2, "RoundFreezeEndHook: %s", name);
OnMapEnd();
}
public void PlayerActivateHook(Handle event, const char[] name, bool dontBroadcast) {
Echo(2, "PlayerActivateHook: %s", name);
int userid = GetEventInt(event, "userid");
int client = GetClientOfUserId(userid);
PlayerActivate(client);
}
void PlayerActivate(int client) {
Echo(2, "PlayerActivate: %d", client);
if (GetQRecord(client)) {
g_QRecord.SetString("model", "", true);
StartAD();
if (!g_IsVs && g_onteam == 3) {
//SwitchTeam(client, 3);
QueueUp(client, 3);
AddInfected();
}
}
}
int GetRealClient(int client) {
Echo(2, "GetRealClient: %d", client);
if (IsClientValid(client, 0)) {
if (HasEntProp(client, Prop_Send, "m_humanSpectatorUserID")) {
int userid = GetEntProp(client, Prop_Send, "m_humanSpectatorUserID");
int target = GetClientOfUserId(userid);
if (IsClientValid(target)) {
client = target;
}
else {
for (int i = 1; i <= MaxClients; i++) {
if (GetQRtmp(i) && g_tmpTarget == client) {
client = i;
break;
}
}
}
}
}
else {
client = 0;
}
return client;
}
Action LifeCheckTimer(Handle timer, int target) {
Echo(2, "LifeCheckTimer: %d", target);
if (GetQRecord(GetRealClient(target))) {
int status = IsPlayerAlive(target);
if(g_model[0] != EOS) {
AssignModel(target, g_model, g_IdentityFix);
} else {
GetBotCharacter(target, g_model);
g_QRecord.SetString("model", g_model, true);
}
g_QRecord.SetValue("status", status, true);
AssignModel(g_client, g_model, g_IdentityFix);
}
return Plugin_Handled;
}
public void OnAllSpawnHook(Handle event, const char[] name, bool dontBroadcast) {
Echo(2, "OnAllSpawnHook: %s", name);
int userid = GetEventInt(event, "userid");
int client = GetClientOfUserId(userid);
if (GetQRtmp(client)) {
CreateTimer(0.5, LifeCheckTimer, client);
if (!g_IsVs && g_tmpOnteam == 3 && !g_tmpStatus) {
g_QRtmp.SetValue("status", 1, true);
State_TransitionSig(client, 8);
}
}
}
public void RoundStartHook(Handle event, const char[] name, bool dontBroadcast) {
Echo(2, "RoundStartHook: %s", name);
StartAD();
for (int i = 1; i <= MaxClients; i++) {
if (GetQRtmp(i)) {
if (!g_IsVs && g_tmpOnteam == 3) {
//SwitchTeam(i, 3);
QueueUp(i, 3);
AddInfected();
}
}
}
}
bool StopAD() {
Echo(2, "StopAD");
if (g_AD != null) {
g_ADFreeze = true;
g_AssistedSpawning = false;
g_survivorSetScan = true;
g_ADInterval = 0;
for (int i = 1; i <= MaxClients; i++) {
if (g_rDelays[i] != null) {
KillTimer(g_rDelays[i]);
g_rDelays[i] = null;
}
}
if (g_MkBotsTimer != null) {
KillTimer(g_MkBotsTimer);
g_MkBotsTimer = null;
}
KillTimer(g_AD); // delete causes errors?
g_AD = null;
}
return g_AD == null;
}
bool StartAD(float interval=0.5) {
Echo(2, "StartAD");
if (g_ADAssistant == null) {
g_ADAssistant = CreateTimer(0.1, StartADTimer, _, TIMER_REPEAT);
}
if (StopAD()) {
g_AD = CreateTimer(interval, ADTimer, _, TIMER_REPEAT);
}
return g_AD != null;
}
public Action StartADTimer(Handle timer) {
Echo(6, "StartADTimer");
if (g_AD == null) {
StartAD();
return Plugin_Continue;
}
g_ADAssistant = null;
return Plugin_Stop;
}
bool AllClientsLoadedIn() {
Echo(2, "AllClientsLoadedIn");
for (int i = 1; i <= MaxClients; i++) {
if (IsClientConnected(i) && !IsClientInGame(i)) {
return false;
}
}
return true;
}
public Action ADTimer(Handle timer) {
Echo(6, "ADTimer");
g_MaxMates = CountTeamMates(2);
if (g_MaxMates == 0) {
return Plugin_Continue;
}
static bool takeover;
takeover = !g_IsVs || (g_IsVs && !g_ADFreeze);
for (int i = 1; i <= MaxClients; i++) {
if (g_ADFreeze) {
g_ADInterval = 0;
if (g_MaxMates >= 4 || AllClientsLoadedIn()) {
while (CountTeamMates(2) < g_MinPlayers) {
AddSurvivor();
}
if (AllClientsLoadedIn() && StartAD(5.0)) {
RmBots(g_MinPlayers * -1, 2);
//SetConVarInt(g_cvMaxSurvivors, MaxClients);
g_AssistedSpawning = false;
g_ADFreeze = false;
}
}
}
if (IsClientValid(GetRealClient(i), 2, 0)) {
if (IsPlayerAlive(i)) {
_AutoModel(i);
}
}
else if (GetQRtmp(i)) {
//if (IsClientValid(g_tmpTarget, 2, 0)) {
// ResetClientSpecUserId(i, g_tmpTarget);
//}
if (g_tmpOnteam == 3) {
if (!g_IsVs) {
g_AssistedSpawning = true;
if (!g_tmpInspec && GetClientTeam(i) <= 1) {
QueueUp(i, 3);
AddInfected();
}
}
}
else if (!g_tmpInspec && GetClientTeam(i) <= 1) {
if (takeover) {
g_QRtmp.SetValue("onteam", 0,true);
CreateTimer(0.1, TakeoverTimer, i);
}
}
}
}
static int lastSize;
if (lastSize != g_MaxMates) {
lastSize = g_MaxMates;
g_SILimits = 1;
while (g_SILimits * 6 < g_MaxMates) {
g_SILimits++;
}
VDOUnlocker();
g_AutoWave = false;
if (g_IsCoop) {
g_AutoWave = (g_AutoHard == 2 || g_MaxMates > 4 && g_AutoHard == 1);
AutoSetTankHp();
}
}
if (g_AutoWave || g_AssistedSpawning) {
if (g_SpawnInterval > 0 && g_ADInterval >= g_SpawnInterval) {
if (g_ADInterval % g_SpawnInterval == 0) {
Echo(2, " -- Assisting SI %d: Matching Full Team", g_ADInterval);
MkBots(g_MaxMates * -1, 3);
}
else if (g_ADInterval % (g_SpawnInterval / 2) == 0) {
Echo(2, " -- Assisting SI %d: Matching Half Team", g_ADInterval);
MkBots((g_MaxMates / 2) * -1, 3);
}
else if (g_ADInterval % (g_SpawnInterval / 3) == 0) {
Echo(2, " -- Assisting SI %d: Matching a Quarter", g_ADInterval);
MkBots((g_MaxMates / 4) * -1, 3);
}
}
}
g_ADInterval++;
return Plugin_Continue;
}
public void UpdateConVarsHook(Handle convar, const char[] oldCv, const char[] newCv) {
GetConVarName(convar, g_sB, sizeof(g_sB));
Echo(2, "UpdateConVarsHook: %s %s %s", g_sB, oldCv, newCv);
static char name[32]; name = "";
static char value[2048]; value = "";
Format(name, sizeof(name), g_sB);
Format(value, sizeof(value), "%s", newCv);
TrimString(value);
if (StrContains(newCv, "-l") == 0) {
strcopy(value, sizeof(value), value[2]);
TrimString(value);
g_Cvars.SetString(name, value, true);
}
else if (StrContains(newCv, "-u") == 0) {
strcopy(value, sizeof(value), value[2]);
TrimString(value);
g_Cvars.Remove(name);
}
g_Cvars.GetString(name, value, sizeof(value));
if (!StrEqual(newCv, value)) {
SetConVarString(convar, value);
return;
}
if (name[0] == 'a') {
if (name[4] == 'a') {
if (name[8] == 'h' && StrEqual(name, "abm_autohard")) {
g_AutoHard = GetConVarInt(g_cvAutoHard);
AutoSetTankHp();
}
else if (name[8] == 'm' && StrEqual(name, "abm_automodel")) {
g_AutoModel = GetConVarInt(g_cvAutoModel);
}
}
else if (name[4] == 'c' && StrEqual(name, "abm_consumable")) {
GetConVarString(g_cvConsumable, g_Consumable, sizeof(g_Consumable));
}
else if (name[4] == 'h' && StrEqual(name, "abm_healitem")) {
GetConVarString(g_cvHealItem, g_HealItem, sizeof(g_HealItem));
}
else if (name[4] == 'i' && StrEqual(name, "abm_identityfix")) {
g_IdentityFix = GetConVarInt(g_cvIdentityFix);
}
else if (name[4] == 'j' && StrEqual(name, "abm_joinmenu")) {
g_JoinMenu = GetConVarInt(g_cvJoinMenu);
}
else if (name[4] == 'k' && StrEqual(name, "abm_keepdead")) {
g_KeepDead = GetConVarInt(g_cvKeepDead);
}
else if (name[4] == 'l' && StrEqual(name, "abm_loglevel")) {
g_LogLevel = GetConVarInt(g_cvLogLevel);
}
else if (name[4] == 'm' && StrEqual(name, "abm_minplayers")) {
g_MinPlayers = GetConVarInt(g_cvMinPlayers);
}
else if (name[4] == 'o' && StrEqual(name, "abm_offertakeover")) {
g_OfferTakeover = GetConVarInt(g_cvOfferTakeover);
}
else if (name[4] == 'p' && StrEqual(name, "abm_primaryweapon")) {
GetConVarString(g_cvPrimaryWeapon, g_PrimaryWeapon, sizeof(g_PrimaryWeapon));
}
else if (name[4] == 'r' && StrEqual(name, "abm_respawndelay")) {
g_RespawnDelay = GetConVarFloat(g_cvRespawnDelay);
StringMapSnapshot keys = g_QDB.Snapshot();
for (int i; i < keys.Length; i++) {
keys.GetKey(i, g_sB, sizeof(g_sB));
g_QDB.GetValue(g_sB, g_QRecord);
g_QRecord.SetValue("rdelay", g_RespawnDelay, true);
}
delete keys;
}
else if (name[4] == 's') {
if (name[5] == 'e' && StrEqual(name, "abm_secondaryweapon")) {
GetConVarString(g_cvSecondaryWeapon, g_SecondaryWeapon, sizeof(g_SecondaryWeapon));
}
else if (name[5] == 'p' && StrEqual(name, "abm_spawninterval")) {
g_SpawnInterval = GetConVarInt(g_cvSpawnInterval);
}
else if (name[5] == 't' && StrEqual(name, "abm_stripkick")) {
g_StripKick = GetConVarInt(g_cvStripKick);
}
}
else if (name[4] == 't') {
if (name[5] == 'a' && StrEqual(name, "abm_tankchunkhp")) {
g_TankChunkHp = GetConVarInt(g_cvTankChunkHp);
AutoSetTankHp();
}
else if (name[5] == 'e' && StrEqual(name, "abm_teamlimit")) {
g_TeamLimit = GetConVarInt(g_cvTeamLimit);
}
else if (name[5] == 'h' && StrEqual(name, "abm_throwable")) {
GetConVarString(g_cvThrowable, g_Throwable, sizeof(g_Throwable));
}
}
else if (name[4] == 'u' && StrEqual(name, "abm_unlocksi")) {
g_UnlockSI = GetConVarInt(g_cvUnlockSI);
switch (g_UnlockSI) {
case 2: VDOUnlocker();
default: RestoreVDOU();
}
}
else if (name[4] == 'z' && StrEqual(name, "abm_zoey")) {
g_Zoey = GetConVarInt(g_cvZoey);
}
}
else if (name[0] == 'l' && StrEqual(name, "l4d2_directoroptions_overwrite")) {
g_VDOUCurVal = value;
if (g_UnlockSI != 2) {
g_VDOUOrigin = value;
SetConVarString(g_cvVDOUOrigin, value);
}
}
else if (name[0] == 'm' && StrEqual(name, "mp_gamemode")) {
UpdateGameMode();
}
else if (name[0] == 's' && StrEqual(name, "sv_consistency")) {
GetConVarString(g_cvConsistency, g_consistency, sizeof(g_consistency));
}
else if (name[5] == 'w' && StrEqual(name, "_abm_wrapswitch")) {
g_WrapSwitch = GetConVarInt(g_cvWrapSwitch);
}
}
int GetGameType() {
Echo(2, "GetGameType");
// 0: coop 1: versus 2: scavenge 3: survival
GetConVarString(g_cvGameMode, g_sB, sizeof(g_sB));
switch (g_sB[0]) {
case 'c': {
if (StrEqual(g_sB, "coop")) return 0;
else if (StrEqual(g_sB, "community1")) return 0; // Special Delivery
else if (StrEqual(g_sB, "community2")) return 0; // Flu Season
else if (StrEqual(g_sB, "community3")) return 1; // Riding My Survivor
else if (StrEqual(g_sB, "community4")) return 3; // Nightmare
else if (StrEqual(g_sB, "community5")) return 0; // Death's Door
}
case 'm': {
if (StrEqual(g_sB, "mutation1")) return 0; // Last Man on Earth
else if (StrEqual(g_sB, "mutation2")) return 0; // Headshot!
else if (StrEqual(g_sB, "mutation3")) return 0; // Bleed Out
else if (StrEqual(g_sB, "mutation4")) return 0; // Hard Eight
else if (StrEqual(g_sB, "mutation5")) return 0; // Four Swordsmen
else if (StrEqual(g_sB, "mutation7")) return 0; // Chainsaw Massacre
else if (StrEqual(g_sB, "mutation8")) return 0; // Ironman
else if (StrEqual(g_sB, "mutation9")) return 0; // Last Gnome on Earth
else if (StrEqual(g_sB, "mutation10")) return 0; // Room for One
else if (StrEqual(g_sB, "mutation11")) return 1; // Healthpackalypse
else if (StrEqual(g_sB, "mutation12")) return 1; // Realism Versus
else if (StrEqual(g_sB, "mutation13")) return 2; // Follow the Liter
else if (StrEqual(g_sB, "mutation14")) return 0; // Gib Fest
else if (StrEqual(g_sB, "mutation15")) return 1; // Versus Survival
else if (StrEqual(g_sB, "mutation16")) return 0; // Hunting Party
else if (StrEqual(g_sB, "mutation17")) return 0; // Lone Gunman
else if (StrEqual(g_sB, "mutation18")) return 1; // Bleed Out Versus
else if (StrEqual(g_sB, "mutation19")) return 1; // Taaannnkk!
else if (StrEqual(g_sB, "mutation20")) return 0; // Healing Gnome
}
case 'r': {
if (StrEqual(g_sB, "realism")) return 0;
}
case 's': {
if (StrEqual(g_sB, "scavenge")) return 2;
else if (StrEqual(g_sB, "survival")) return 3;
}
case 't': {
if (StrEqual(g_sB, "teamscavenge")) return 2;
else if (StrEqual(g_sB, "teamversus")) return 1;
}
case 'v': {
if (StrEqual(g_sB, "versus")) return 1;
}
}
return -1;
}
void UpdateGameMode() {
Echo(2, "UpdateGameMode");
switch (GetGameType()) {
case 0: {
g_IsCoop = true;
g_IsVs = false;
}
case 1, 2: {
g_IsVs = true;
g_IsCoop = false;
}
case 3: {
g_IsVs = false;
g_IsCoop = false;
}
}
}
void SetVDOU(char[] val, any ...) {
Echo(2, "SetVDOU");
static const char tmp[128] = "\
DominatorLimit=%s;\
MaxSpecials=%s;\
BoomerLimit=%s;\
SmokerLimit=%s;\
HunterLimit=%s;\
ChargerLimit=%s;\
SpitterLimit=%s;\
JockeyLimit=%s;";
VFormat(val, sizeof(tmp), tmp, 2);
}
void VDOUnlocker() {
Echo(2, "VDOUnlocker");
if (g_cvVDOUHandle != null) {
static bool restore = false;
g_MaxMates = CountTeamMates(2);
if (g_UnlockSI == 2 && g_MaxMates > 4) {
static char m[3]; static char l[3];
Format(m, sizeof(m), "%d", g_MaxMates);
Format(l, sizeof(l), "%d", g_SILimits);
SetVDOU(g_VDOUCurVal, m, m, l, l, l, l, l, l);
SetConVarString(g_cvVDOUHandle, g_VDOUCurVal);
restore = true;
}
else if (restore) {
RestoreVDOU();
restore = false;
}
}
}
void RestoreVDOU() {
Echo(2, "RestoreVDOU");
if (!g_ADFreeze && g_cvVDOUHandle != null) {
static char origin[2048];
origin = g_VDOUOrigin;
SetVDOU(g_VDOUCurVal, "","","","","","","","");
SetConVarString(g_cvVDOUHandle, g_VDOUCurVal);
SetConVarString(g_cvVDOUHandle, origin);
}
}
void AutoSetTankHp() {
Echo(2, "AutoSetTankHp");
static int tankHp;
GetConVarDefault(g_cvTankHealth, g_sB, sizeof(g_sB));
tankHp = StringToInt(g_sB);
if (g_TankChunkHp != 0 && (g_IsCoop || g_IsVs)) {
if (g_AutoHard == 2 || g_MaxMates > 4 && g_AutoHard == 1) {
tankHp = g_MaxMates * g_TankChunkHp;
}
}
SetConVarInt(g_cvTankHealth, tankHp);
}
public void OnConfigsExecuted() {
Echo(2, "OnConfigsExecuted");
// extend the base cfg with a map specific cfg
GetCurrentMap(g_sB, sizeof(g_sB));
Format(g_sB, sizeof(g_sB), "cfg/sourcemod/abm/%s.cfg", g_sB);
if (FileExists(g_sB, true)) {
strcopy(g_sB, sizeof(g_sB), g_sB[4]);
ServerCommand("exec \"%s\"", g_sB);
Echo(1, "Extending ABM: %s", g_sB);
}
// some servers don't pick up on this automatically
else if (FileExists("cfg/sourcemod/abm.cfg", true)) {
ServerCommand("exec \"sourcemod/abm.cfg\"");
}
}
public void OnClientPostAdminCheck(int client) {
Echo(2, "OnClientPostAdminCheck: %d", client);
if (!IsFakeClient(client)) {
if (GetQRecord(client) && !g_update) {
return;
}
if (IsAdmin(client) || !GetQRecord(client)) {
SetQRecord(client, true);
}
else {
int status = g_status;
int onteam = g_onteam;
if (SetQRecord(client, true) >= 0) {
g_QRecord.SetValue("status", status, true);
g_QRecord.SetValue("onteam", onteam, true);
}
}
if (g_JoinMenu == 2 || g_JoinMenu == 1 && IsAdmin(client)) {
GoIdle(client, 1);
menuArg0 = client;
SwitchTeamHandler(client, 1, "");
}
else if (CountTeamMates(2) >= 1) {
CreateTimer(0.1, TakeoverTimer, client);
CreateTimer(0.5, AutoIdleTimer, client, TIMER_REPEAT);
}
}
}
public Action AutoIdleTimer(Handle timer, int client) {
Echo(2, "AutoIdleTimer: %d", client);
if (g_IsVs || !IsClientValid(client)) {
return Plugin_Stop;
}
static int onteam;
onteam = GetClientTeam(client);
if (onteam >= 2) {
if (onteam == 2) {
GoIdle(client, 0);
}
return Plugin_Stop;
}
return Plugin_Continue;
}
public void GoIdleHook(Handle event, const char[] name, bool dontBroadcast) {
Echo(2, "GoIdleHook: %s", name);
int player = GetEventInt(event, "player");
int client = GetClientOfUserId(player);
if (GetQRecord(client)) {
switch (g_onteam) {
case 2: GoIdle(client);
case 3: SwitchTeam(client, 3);
}
}
}
void GoIdle(int client, int onteam=0) {
Echo(2, "GoIdle: %d %d", client, onteam);
if (GetQRecord(client)) {
int spec_target;
// going from idle survivor to infected, leaves an icon behind
if (IsClientValid(g_target, 2, 0)) {
SwitchToBot(client, g_target);
}
if (g_onteam == 2) {
SwitchToSpec(client);
if (onteam == 0) {
SetHumanSpecSig(g_target, client);
}
if (onteam == 1) {
SwitchToSpec(client);
Unqueue(client);
}
AssignModel(g_target, g_model, g_IdentityFix);
}
else {
SwitchToSpec(client);
}
if (g_onteam == 3 && onteam <= 1) {
g_QRecord.SetString("model", "", true);
}
spec_target = IsClientValid(g_target, 0, 0) ? g_target : GetSafeSurvivor(client);
if (IsClientValid(spec_target)) {
SetEntPropEnt(client, Prop_Send, "m_hObserverTarget", spec_target);
SetEntProp(client, Prop_Send, "m_iObserverMode", 5);
}
}
}
public void CleanQDBHook(Handle event, const char[] name, bool dontBroadcast) {
Echo(2, "CleanQDBHook: %s", name);
int userid = GetEventInt(event, "userid");
int client = GetClientOfUserId(userid);
RemoveQDBKey(client);
}
void RemoveQDBKey(int client) {
Echo(2, "RemoveQDBKey: %d", client);
if (GetQRecord(client)) {
SetClientName(client, g_name);
g_QRecord.SetValue("update", true, true);
g_QRecord.SetString("model", "", true);
if (CountTeamMates(2) > g_MinPlayers) {
CreateTimer(1.0, RmBotsTimer, 1);
}
}
}
Action RmBotsTimer(Handle timer, any asmany) {
Echo(4, "RmBotsTimer: %d", asmany);
if (!g_IsVs) {
RmBots(asmany, 2);
}
return Plugin_Handled;
}
bool IsAdmin(int client) {
Echo(2, "IsAdmin: %d", client);
return CheckCommandAccess(
client, "generic_admin", ADMFLAG_GENERIC, false
);
}
bool IsEntityValid(int ent) {
Echo(2, "IsEntityValid: %d", ent);
return (ent > MaxClients && ent <= 2048 && IsValidEntity(ent));
}
bool IsClientValid(int client, int onteam=0, int mtype=2) {
Echo(6, "IsClientValid: %d, %d, %d", client, onteam, mtype);
if (client >= 1 && client <= MaxClients) {
if (IsClientConnected(client)) {
if (IsClientInGame(client)) {
if (onteam != 0 && GetClientTeam(client) != onteam) {
return false;
}
switch (mtype) {
case 0: return IsFakeClient(client);
case 1: return !IsFakeClient(client);
}
return true;
}
}
}
return false;
}
bool CanClientTarget(int client, int target) {
Echo(2, "CanClientTarget: %d %d", client, target);
if (client == target) {
return true;
}
else if (!IsClientValid(client) || !IsClientValid(target)) {
return false;
}
else if (IsFakeClient(target)) {
int manager = GetClientManager(target);
if (manager != -1) {
if (manager == 0) {
return true;
}
else {
return CanClientTarget(client, manager);
}
}
}
return CanUserTarget(client, target);
}
int GetPlayClient(int client) {
Echo(3, "GetPlayClient: %d", client);
if (GetQRecord(client)) {
return g_target;
}
else if (IsClientValid(client)) {
return client;
}
return -1;
}
int ClientHomeTeam(int client) {
Echo(2, "ClientHomeTeam: %d", client);
if (GetQRecord(client)) {
return g_onteam;
}
else if (IsClientValid(client)) {
return GetClientTeam(client);
}
return -1;
}
// ================================================================== //
// g_QDB MANAGEMENT
// ================================================================== //
bool SetQKey(int client) {
Echo(3, "SetQKey: %d", client);
if (IsClientValid(client, 0, 1)) {
if (GetClientAuthId(client, AuthId_Steam2, g_QKey, sizeof(g_QKey), true)) {
return true;
}
}
return false;
}
bool GetQRtmp(int client) {
Echo(3, "GetQRtmp: %d", client);
bool result;
static char QKey[64];
QKey = g_QKey;
if (SetQKey(client)) {
if (g_QDB.GetValue(g_QKey, g_QRtmp)) {
if (IsClientValid(client) && IsPlayerAlive(client)) {
GetClientAbsOrigin(client, g_tmpOrigin);
g_QRtmp.SetArray("origin", g_tmpOrigin, sizeof(g_tmpOrigin), true);
}
g_QRtmp.GetValue("client", g_tmpClient);
g_QRtmp.GetValue("target", g_tmpTarget);
g_QRtmp.GetValue("lastid", g_tmpLastid);
g_QRtmp.GetValue("onteam", g_tmpOnteam);
g_QRtmp.GetValue("queued", g_tmpQueued);
g_QRtmp.GetValue("inspec", g_tmpInspec);
g_QRtmp.GetValue("status", g_tmpStatus);
g_QRtmp.GetValue("update", g_tmpUpdate);
g_QRtmp.GetValue("rdelay", g_tmpRdelay);
g_QRtmp.GetString("ghost", g_tmpGhost, sizeof(g_tmpGhost));
g_QRtmp.GetString("model", g_tmpModel, sizeof(g_tmpModel));
g_QRtmp.GetString("name", g_tmpName, sizeof(g_tmpName));
if (g_tmpModel[0] == EOS || g_tmpOnteam == 3) {
GetBotCharacter(g_tmpTarget, g_tmpModel);
g_QRtmp.SetString("model", g_tmpModel, true);
if (!g_IsVs && g_tmpOnteam == 3 && IsPlayerAlive(client)) {
SetClientName(client, g_tmpModel);
}
}
result = true;
}
}
g_QKey = QKey;
return result;
}
bool GetQRecord(int client) {
Echo(3, "GetQRecord: %d", client);
if (SetQKey(client)) {
if (g_QDB.GetValue(g_QKey, g_QRecord)) {
if (IsClientValid(client) && IsPlayerAlive(client)) {
GetClientAbsOrigin(client, g_origin);
g_QRecord.SetArray("origin", g_origin, sizeof(g_origin), true);
}
g_QRecord.GetValue("client", g_client);
g_QRecord.GetValue("target", g_target);
g_QRecord.GetValue("lastid", g_lastid);
g_QRecord.GetValue("onteam", g_onteam);
g_QRecord.GetValue("queued", g_queued);
g_QRecord.GetValue("inspec", g_inspec);
g_QRecord.GetValue("status", g_status);
g_QRecord.GetValue("update", g_update);
g_QRecord.GetValue("rdelay", g_rdelay);
g_QRecord.GetString("ghost", g_ghost, sizeof(g_ghost));
g_QRecord.GetString("model", g_model, sizeof(g_model));
g_QRecord.GetString("name", g_name, sizeof(g_name));
if (g_model[0] == EOS || g_onteam == 3) {
GetBotCharacter(g_target, g_model);
g_QRecord.SetString("model", g_model, true);
if (!g_IsVs && g_onteam == 3 && IsPlayerAlive(client)) {
SetClientName(client, g_model);
}
}
return true;
}
}
return false;
}
bool NewQRecord(int client) {
Echo(3, "NewQRecord: %d", client);
g_QRecord = new StringMap();
GetClientAbsOrigin(client, g_origin);
g_QRecord.SetArray("origin", g_origin, sizeof(g_origin), true);
g_QRecord.SetValue("client", client, true);
g_QRecord.SetValue("target", client, true);
g_QRecord.SetValue("lastid", client, true);
g_QRecord.SetValue("onteam", GetClientTeam(client), true);
g_QRecord.SetValue("queued", false, true);
g_QRecord.SetValue("inspec", false, true);
g_QRecord.SetValue("status", true, true);
g_QRecord.SetValue("update", false, true);
g_QRecord.SetValue("rdelay", g_RespawnDelay, true);
g_QRecord.SetString("ghost", "", true);
g_QRecord.SetString("model", "", true);
GetClientName(client, g_name, sizeof(g_name));
g_QRecord.SetString("name", g_name, true);
return true;
}
int SetQRecord(int client, bool update=false) {
Echo(3, "SetQRecord: %d %d", client, update);
int result = -1;
if (SetQKey(client)) {
if (g_QDB.GetValue(g_QKey, g_QRecord) && !update) {
result = 0;
}
else if (NewQRecord(client)) {
GetClientName(client, g_pN, sizeof(g_pN));
Echo(0, "AUTH ID: %s, (%s) ADDED TO QDB.", g_QKey, g_pN);
g_QDB.SetValue(g_QKey, g_QRecord, true);
result = 1;
}
GetQRecord(client);
}
return result;
}
void QueueUp(int client, int onteam) {
Echo(2, "QueueUp: %d %d", client, onteam);
if (onteam >= 2 && onteam <= 3 && GetQRecord(client)) {
Unqueue(client);
switch (onteam) {
case 2: g_sQueue.Push(client);
case 3: g_iQueue.Push(client);
}
g_QRecord.SetValue("target", client, true);
g_QRecord.SetValue("inspec", false, true);
g_QRecord.SetValue("onteam", onteam, true);
g_QRecord.SetValue("queued", true, true);
}
}
void Unqueue(int client) {
Echo(2, "Unqueue: %d", client);
if (GetQRecord(client)) {
g_QRecord.SetValue("queued", false, true);
int iLength = g_iQueue.Length;
int sLength = g_sQueue.Length;
if (iLength > 0) {
for (int i = iLength - 1; i > -1; i--) {
if (g_iQueue.Get(i) == client) {
g_iQueue.Erase(i);
}
}
}
if (sLength > 0) {
for (int i = sLength - 1; i > -1; i--) {
if (g_sQueue.Get(i) == client) {
g_sQueue.Erase(i);
}
}
}
}
}
public Action OnSpawnHook(Handle event, const char[] name, bool dontBroadcast) {
Echo(2, "OnSpawnHook: %s", name);
int userid = GetEventInt(event, "userid");
int target = GetClientOfUserId(userid);
GetClientName(target, g_pN, sizeof(g_pN));
if (g_pN[0] == 'A' && StrContains(g_pN, "ABMclient") >= 0) {
return Plugin_Handled;
}
int onteam = GetClientTeam(target);
int client;
if (onteam == 3) {
int zClass = GetEntProp(target, Prop_Send, "m_zombieClass");
if (g_iQueue.Length == 0 && g_UnlockSI != 0) {
if (CountTeamMates(3) > g_MaxMates && zClass != 8) {
if (IsFakeClient(target)) {
KickClient(target);
return Plugin_Handled;
}
}
}
if (!g_IsVs) {
if (g_AssistedSpawning) {
if (zClass == 8) {
int j = 1;
static int i = 1;
for (; i <= MaxClients + 1; i++) {
if (j++ == MaxClients + 1) { // join 3 Tank requires +1
return Plugin_Handled;
}
if (i > MaxClients) {
i = 1;
}
if (GetQRecord(i) && g_onteam == 3 && !g_inspec) {
if (GetEntProp(i, Prop_Send, "m_zombieClass") != 8) {
client = i;
i++;
break;
}
}
}
if(IsClientValid(client)) {
SwitchToBot(client, target);
} else {
CreateTimer(1.0, TankAssistTimer, target, TIMER_REPEAT);
}
}
}
if (g_iQueue.Length > 0) {
client = g_iQueue.Get(0);
if (IsClientValid(client) && !IsPlayerAlive(client)) {
SwitchToBot(client, target);
}
return Plugin_Handled;
}
}
}
if (onteam == 2) {
// AutoModeling now takes place in OnEntityCreated
CreateTimer(0.4, OnSpawnHookTimer, target);
return Plugin_Handled;
}
return Plugin_Continue;
}
public Action TankAssistTimer(Handle timer, any client) {
Echo(4, "TankAssistTimer: %d", client);
/*
* Human players on the infected team in modes that do not officially
* support them, can get Tanks stuck in "stasis" until they die. This
* function works around the issue by watching Tanks for movement. If
* a Tank does not move in 11 seconds, it is replaced with another.
*/
float origin[3];
static const float nullOrigin[3];
static int times[MAXPLAYERS + 1] = {11, ...};
static float origins[MAXPLAYERS + 1][3];
static int i;
if (IsClientValid(client)) {
i = times[client]--;
if (i == 11) {
GetClientAbsOrigin(client, origins[client]);
return Plugin_Continue;
}
else if (i >= 0) {
GetClientAbsOrigin(client, origin);
if (origin[0] == origins[client][0]) {
if (i == 0) {
TeleportEntity(client, nullOrigin, NULL_VECTOR, NULL_VECTOR);
ForcePlayerSuicide(client);
AddInfected("tank");
}
return Plugin_Continue;
}
}
}
i = times[client] = 11;
return Plugin_Stop;
}
public Action ForceSpawnTimer(Handle timer, any client) {
Echo(4, "ForceSpawnTimer: %d", client);
static int times[MAXPLAYERS + 1] = {20, ...};
static int i;
if (IsClientValid(client)) {
i = times[client]--;
if (GetEntProp(client, Prop_Send, "m_zombieClass") != 8) {
i = times[client] = 20;
return Plugin_Stop;
}
if (GetEntProp(client, Prop_Send, "m_isGhost") == 1) {
if (i >= 1) {
PrintHintText(client, "FORCING SPAWN IN: %d", i);
return Plugin_Continue;
}
if (GetEntProp(client, Prop_Send, "m_ghostSpawnState") <= 2) {
SetEntProp(client, Prop_Send, "m_isGhost", 0);
}
return Plugin_Continue;
}
}
if (!IsEntityValid(GetEntPropEnt(client, Prop_Send, "m_hActiveWeapon"))) {
i = CreateEntityByName("weapon_tank_claw");
if (IsEntityValid(i) && DispatchSpawn(i)) {
EquipPlayerWeapon(client, i);
}
}
i = times[client] = 20;
PrintHintText(client, "KILL ALL HUMANS");
return Plugin_Stop;
}
Action OnSpawnHookTimer(Handle timer, any target) {
Echo(2, "OnSpawnHookTimer: %d", target);
if (g_sQueue.Length > 0) {
SwitchToBot(g_sQueue.Get(0), target);
}
return Plugin_Handled;
}
public void OnDeathHook(Handle event, const char[] name, bool dontBroadcast) {
Echo(4, "OnDeathHook: %s", name);
int userid = GetEventInt(event, "userid");
int client = GetClientOfUserId(userid);
if (GetQRecord(client)) {
GetClientAbsOrigin(client, g_origin);
g_QRecord.SetValue("target", client, true);
g_QRecord.SetArray("origin", g_origin, sizeof(g_origin), true);
g_QRecord.SetValue("status", false, true);
bool offerTakeover;
switch (g_onteam) {
case 3: {
g_QRecord.SetString("model", "", true);
if (!g_IsVs) {
QueueSI(client, g_rdelay);
switch (g_OfferTakeover) {
case 2, 3: {
GoIdle(client, 1);
offerTakeover = true;
}
default: SwitchTeam(client, 3);
}
}
}
case 2: {
switch (g_OfferTakeover) {
case 1, 3: offerTakeover = true;
}
}
}
if (offerTakeover) {
GenericMenuCleaner(client);
menuArg0 = client;
SwitchToBotHandler(client, 1);
}
}
else if (GetQRecord(GetRealClient(client))) {
g_QRecord.SetValue("status", false, true);
}
}
public void QTeamHook(Handle event, const char[] name, bool dontBroadcast) {
Echo(2, "QTeamHook: %s", name);
int userid = GetEventInt(event, "userid");
int client = GetClientOfUserId(userid);
int onteam = GetEventInt(event, "team");
if (GetQRecord(client)) {
if (!g_IsVs && g_onteam == 3) {
SetClientName(client, g_name);
}
if (onteam >= 2) {
g_QRecord.SetValue("inspec", false, true);
g_QRecord.SetValue("target", client, true);
g_QRecord.SetValue("onteam", onteam, true);
// attempt to apply a model asap
if (g_ADFreeze && onteam == 2 && g_model[0] != EOS) {
AssignModel(client, g_model, g_IdentityFix);
}
}
if (onteam <= 1) { // cycling requires 0.2 or higher?
CreateTimer(0.2, QTeamHookTimer, client);
}
}
}
Action QTeamHookTimer(Handle timer, any client) {
Echo(2, "QTeamHookTimer: %d", client);
if (GetQRecord(client) && !g_inspec) {
if (g_onteam == 2) {
if (IsClientValid(g_target) && g_target != client) {
SetHumanSpecSig(g_target, client);
}
}
}
return Plugin_Handled;
}
public void QAfkHook(Handle event, const char[] name, bool dontBroadcast) {
Echo(2, "QAfkHook: %s", name);
int client = GetClientOfUserId(GetEventInt(event, "player"));
int target = GetClientOfUserId(GetEventInt(event, "bot"));
int clientTeam = GetClientTeam(client);
int targetTeam = GetClientTeam(target);
if (GetQRecord(client)) {
int onteam = GetClientTeam(client);
if (onteam == 2) {
g_QRecord.SetValue("target", target, true);
AssignModel(target, g_model, g_IdentityFix);
}
}
if (targetTeam == 2 && IsClientValid(client)) {
if (IsClientInKickQueue(client)) {
if (client && target && clientTeam == targetTeam) {
int safeClient = GetSafeSurvivor(target);
RespawnClient(target, safeClient);
}
}
}
}
public void QBakHook(Handle event, const char[] name, bool dontBroadcast) {
Echo(2, "QBakHook: %s", name);
int client = GetClientOfUserId(GetEventInt(event, "player"));
int target = GetClientOfUserId(GetEventInt(event, "bot"));
if (GetQRecord(client)) {
if (g_target != target) {
g_QRecord.SetValue("lastid", target);
g_QRecord.SetValue("target", client);
}
if (GetClientTeam(client) == 2) {
AssignModel(client, g_model, g_IdentityFix);
}
}
}
// ================================================================== //
// UNORGANIZED AS OF YET
// ================================================================== //
void StripClient(int client) {
Echo(2, "StripClient: %d", client);
if (IsClientValid(client)) {
if (GetClientTeam(client) == 2) {
for (int i = 4; i >= 0; i--) {
StripClientSlot(client, i);
}
}
}
}
void StripClientSlot(int client, int slot) {
Echo(2, "StripClientSlot: %d %d", client, slot);
client = GetPlayClient(client);
if (IsClientValid(client)) {
if (GetClientTeam(client) == 2) {
int ent = GetPlayerWeaponSlot(client, slot);
if (IsEntityValid(ent)) {
RemovePlayerItem(client, ent);
AcceptEntityInput(ent,"kill");
}
}
}
}
void RespawnClient(int client, int target=0) {
Echo(2, "RespawnClient: %d %d", client, target);
if (!IsClientValid(client)) {
return;
}
else if (GetQRecord(client)) {
if (g_onteam == 3) {
Takeover(client, 3);
return;
}
}
client = GetPlayClient(client);
target = GetPlayClient(target);
bool weaponizePlayer = true;
static const float pos0[3];
static float pos1[3];
pos1 = pos0;
if (client != GetRealClient(target) && IsClientValid(target)) {
GetClientAbsOrigin(target, pos1);
}
else if (GetQRtmp(client)) {
pos1 = g_origin;
if (pos1[0] != 0 && pos1[1] != 0 && pos1[2] != 0) {
weaponizePlayer = false;
}
}
if (pos1[0] != 0 && pos1[1] != 0 && pos1[2] != 0) {
RoundRespawnSig(client);
if (!g_ADFreeze && weaponizePlayer) {
QuickCheat(client, "give", g_PrimaryWeapon);
QuickCheat(client, "give", g_SecondaryWeapon);
QuickCheat(client, "give", g_Throwable);
QuickCheat(client, "give", g_HealItem);
QuickCheat(client, "give", g_Consumable);
}
TeleportEntity(client, pos1, NULL_VECTOR, NULL_VECTOR);
}
}
void TeleportClient(int client, int target) {
Echo(2, "TeleportClient: %d %d", client, target);
float origin[3];
client = GetPlayClient(client);
target = GetPlayClient(target);
if (IsClientValid(client) && IsClientValid(target)) {
GetClientAbsOrigin(target, origin);
TeleportEntity(client, origin, NULL_VECTOR, NULL_VECTOR);
}
}
int GetSafeSurvivor(int client) {
Echo(2, "GetSafeSurvivor: %d", client);
float lowestIntensity;
int lowestClient = -1;
for (int i = 1; i <= MaxClients; i++) {
if (IsClientValid(i) && i != client && IsPlayerAlive(i) && GetClientTeam(i) == 2) {
// Skip if incapped or on a ledge
if (GetEntProp(i, Prop_Send, "m_isHangingFromLedge") || GetEntProp(i, Prop_Send, "m_isIncapacitated")) {
continue;
}
float intensity = L4D_GetPlayerIntensity(i);
if(intensity < lowestIntensity || lowestClient == -1) {
lowestIntensity = intensity;
lowestClient = i;
}
}
}
return lowestClient;
}
bool AddSurvivor() {
Echo(2, "AddSurvivor");
if (GetClientCount(false) >= MaxClients - 1) {
return false;
}
bool result = false;
if(GetFeatureStatus(FeatureType_Native, "NextBotCreatePlayerBotSurvivorBot") != FeatureStatus_Available) {
// Fallback to manual kick trick if no CreateSurvivorBot API
int i = CreateFakeClient("ABMclient2");
if (IsClientValid(i)) {
if (DispatchKeyValue(i, "classname", "SurvivorBot")) {
ChangeClientTeam(i, 2);
if (DispatchSpawn(i)) {
result = true;
}
}
KickClient(i);
}
} else {
result = CreateSurvivorBot() > 0;
}
return result;
}
bool AddInfected(char model[32]="", int version=0) {
Echo(2, "AddInfected: '%s' %d", model, version);
if (GetClientCount(false) >= MaxClients - 1) {
return false;
}
CleanSIName(model);
int i = CreateFakeClient("ABMclient3");
if (IsClientValid(i)) {
ChangeClientTeam(i, 3);
Format(g_sB, sizeof(g_sB), "%s auto area", model);
switch (version) {
case 0: QuickCheat(i, "z_spawn_old", g_sB);
case 1: QuickCheat(i, "z_spawn", g_sB);
}
KickClient(i);
return true;
}
return false;
}
void GhostsModeProtector(int state=0) {
Echo(2, "GhostsModeProtector: %d", state);
// CAREFUL: 0 starts this function and you must close it with 1 or
// risk breaking things. Close this with 1 immediately when done.
// e.g.,
// GhostsModeProtector(0);
// z_spawn_old tank auto;
// GhostsModeProtector(1);
static int ghosts[MAXPLAYERS + 1];
static int lifeState[MAXPLAYERS + 1]; // prevent early rise from the dead
switch (state) {
case 0: {
for (int i = 1; i <= MaxClients; i++) {
if (GetQRtmp(i) && g_tmpOnteam == 3) {
if (GetEntProp(i, Prop_Send, "m_isGhost") == 1 || g_tmpQueued) {
SetEntProp(i, Prop_Send, "m_isGhost", 0);
ghosts[i] = 1;
}
if (GetEntProp(i, Prop_Send, "m_lifeState") == 1) {
SetEntProp(i, Prop_Send, "m_lifeState", 0);
g_QRtmp.SetValue("status", 1, true);
lifeState[i] = 1;
}
}
}
}
case 1: {
for (int i = 1; i <= MaxClients; i++) {
if (ghosts[i] == 1) {
SetEntProp(i, Prop_Send, "m_isGhost", 1);
}
if (lifeState[i] == 1) {
SetEntProp(i, Prop_Send, "m_lifeState", 1);
}
ghosts[i] = 0;
lifeState[i] = 0;
}
}
}
if (state == 0) {
RequestFrame(GhostsModeProtector, 1);
}
}
void CleanSIName(char model[32]) {
Echo(2, "CleanSIName: %s", model);
int i;
static char tmpModel[32];
if (model[0] != EOS) {
for (i = 0; i < sizeof(g_InfectedNames); i++) {
strcopy(tmpModel, sizeof(tmpModel), g_InfectedNames[i]);
if (StrContains(tmpModel, model, false) == 0) {
model = tmpModel;
return;
}
}
if (StrContains("Tank", model, false) == 0) {
model = "Tank";
return;
}
}
i = GetRandomInt(0, sizeof(g_InfectedNames) - 1);
strcopy(model, sizeof(model), g_InfectedNames[i]);
}
void SwitchToSpec(int client, int onteam=1) {
Echo(2, "SwitchToSpec: %d %d", client, onteam);
if (GetQRecord(client)) {
// clearparent jockey bug switching teams (thanks to Lux)
AcceptEntityInput(client, "clearparent");
g_QRecord.SetValue("inspec", true, true);
ChangeClientTeam(client, onteam);
if (GetRealClient(g_target) == client) {
if (g_onteam == 2) {
AssignModel(g_target, g_model, g_IdentityFix);
}
if (HasEntProp(g_target, Prop_Send, "m_humanSpectatorUserID")) {
SetEntProp(g_target, Prop_Send, "m_humanSpectatorUserID", 0);
}
}
}
}
void QuickCheat(int client, char [] cmd, char [] arg) {
Echo(2, "QuickCheat: %d %s %s", client, cmd, arg);
int flags = GetCommandFlags(cmd);
SetCommandFlags(cmd, flags & ~FCVAR_CHEAT);
FakeClientCommand(client, "%s %s", cmd, arg);
SetCommandFlags(cmd, flags);
}
void SwitchToBot(int client, int target, bool si_ghost=true) {
Echo(2, "SwitchToBot: %d %d %d", client, target, si_ghost);
if (IsClientValid(target, 0, 0)) {
Unqueue(client);
switch (GetClientTeam(target)) {
case 2: TakeoverBotSig(client, target);
case 3: TakeoverZombieBotSig(client, target, si_ghost);
}
}
}
void Takeover(int client, int onteam) {
Echo(2, "Takeover: %d %d", client, onteam);
if (GetQRecord(client)) {
if (IsClientValid(g_target, 0, 0)) {
if (client != g_target && GetClientTeam(g_target) == onteam) {
SwitchToBot(client, g_target);
return;
}
}
int nextBot;
nextBot = GetNextBot(onteam, 0, true);
if (IsClientValid(nextBot)) {
SwitchToBot(client, nextBot);
return;
}
switch (onteam) {
case 2: {
if (g_KeepDead == 1 && !g_status) {
if (g_onteam == 2 && CountTeamMates(2, 0) == 0) {
ChangeClientTeam(client, 2);
ForcePlayerSuicide(client); // without this player may spawn if survivors are close to start
}
return;
}
QueueUp(client, 2);
AddSurvivor();
}
case 3: {
QueueUp(client, 3);
AddInfected();
}
}
}
}
public Action TakeoverTimer(Handle timer, any client) {
Echo(4, "TakeoverTimer: %d", client);
if (CountTeamMates(2) <= 0) {
return Plugin_Handled;
}
static int team2;
static int team3;
static int teamX;
if (GetQRecord(client)) {
if (GetClientTeam(client) >= 2) {
return Plugin_Handled;
}
teamX = 2;
if (g_onteam == 3) {
teamX = 3;
}
if (g_IsVs && g_onteam <= 1) {
team2 = CountTeamMates(2, 1);
team3 = CountTeamMates(3, 1);
if (team3 < team2) {
teamX = 3;
}
}
if (CountTeamMates(teamX, 1) < g_TeamLimit) {
Takeover(client, teamX);
}
}
return Plugin_Handled;
}
int CountTeamMates(int onteam, int mtype=2) {
Echo(2, "CountTeamMates: %d %d", onteam, mtype);
// mtype 0: counts only bots
// mtype 1: counts only humans
// mtype 2: counts all players on team
static int clients, bots, humans;
clients = bots = humans = 0;
for (int i = 1; i <= MaxClients; i++) {
if (IsClientValid(i, onteam)) {
clients++;
if(IsFakeClient(GetRealClient(i))) {
bots++;
} else {
humans++;
}
}
}
switch (mtype) {
case 0: clients = bots;
case 1: clients = humans;
}
return clients;
}
int GetClientManager(int target) {
Echo(4, "GetClientManager: %d", target);
int result = -1;
target = GetRealClient(target);
if (IsClientValid(target)) {
result = IsFakeClient(target) ? 0 : target;
}
return result;
}
int GetNextBot(int onteam, int start=1, bool alive=false) {
Echo(2, "GetNextBot: %d %d %d", onteam, start, alive);
static int bot, j;
bot = 0;
j = start;
if (onteam == 3) {
alive = true;
}
for (int i = 1; i <= MaxClients; i++) {
if (j > 32) {
j = 1;
}
if (IsClientValid(j, onteam, 0)) {
if (GetClientManager(j) == 0) {
if (onteam == 2) {
bot = j;
}
if (alive && IsPlayerAlive(j)) {
return j;
}
else if (!alive) {
return j;
}
}
}
j++;
}
return bot;
}
void CycleBots(int client, int onteam) {
Echo(2, "CycleBots: %d %d", client, onteam);
if (onteam <= 1) {
return;
}
if (GetQRecord(client)) {
int bot = GetNextBot(onteam, g_lastid, true);
if (IsClientValid(bot, onteam, 0)) {
SwitchToBot(client, bot, false);
}
}
}
void SwitchTeam(int client, int onteam, char model[32]="") {
Echo(2, "SwitchTeam: %d %d", client, onteam);
if (GetQRecord(client)) {
if (GetClientTeam(client) >= 2) {
if (onteam == 2 && onteam == g_onteam) {
return; // keep survivors from rejoining survivors
}
}
if (g_onteam == 2 && onteam <= 1) {
if (IsClientValid(g_target, 0, 0)) {
SwitchToBot(client, g_target);
}
}
switch (onteam) {
case 0: GoIdle(client, 0);
case 1: GoIdle(client, 1);
//case 4: ChangeClientTeam(client, 4);
default: {
if (onteam <= 3 && onteam >= 2) {
if (g_onteam != onteam) {
GoIdle(client, 1);
}
g_QRecord.SetString("model", model, true);
if (onteam == 3) {
if (g_IsVs) { // see if a proper way to get on team 2 exist
static int switches; // A Lux idea
switches = GetConVarInt(g_cvMaxSwitches);
SetConVarInt(g_cvMaxSwitches, 9999);
ChangeClientTeam(client, onteam);
SetConVarInt(g_cvMaxSwitches, switches);
return;
}
g_QRecord.SetValue("onteam", onteam,true);
g_QRecord.SetString("ghost", model, true);
QueueSI(client, g_rdelay);
return;
}
Takeover(client, onteam);
}
}
}
}
}
void QueueSI(int client, float delay=1.0) {
Echo(2, "QueueSI: %d %f", client, delay);
if (g_rDelays[client] != null) {
KillTimer(g_rDelays[client]);
g_rDelays[client] = null;
}
g_rDelays[client] = CreateTimer(
delay, QueueSITimer, client, TIMER_REPEAT
);
}
Action QueueSITimer(Handle Timer, int client) {
Echo(2, "QueueSITimer: %d", client);
if (GetQRecord(client) && g_onteam == 3) {
QueueUp(client, 3);
if (AddInfected(g_ghost, 1)) {
g_QRecord.SetValue("rdelay", g_RespawnDelay, true);
g_QRecord.SetString("ghost", "", true);
}
}
g_rDelays[client] = null;
return Plugin_Stop;
}
Action MkBotsCmd(int client, int args) {
Echo(2, "MkBotsCmd: %d", client);
switch(args) {
case 2: {
GetCmdArg(1, g_sB, sizeof(g_sB));
int asmany = StringToInt(g_sB);
GetCmdArg(2, g_sB, sizeof(g_sB));
int onteam = StringToInt(g_sB);
if (onteam >= 2 || onteam <= 3) {
MkBots(asmany, onteam);
}
}
}
return Plugin_Handled;
}
void MkBots(int asmany, int onteam) {
Echo(2, "MkBots: %d %d", asmany, onteam);
if (asmany < 0) {
asmany = asmany * -1 - CountTeamMates(onteam);
}
float rate;
DataPack pack = new DataPack();
switch (onteam) {
case 2: rate = 0.2;
case 3: rate = 0.4;
}
g_MkBotsTimer = CreateDataTimer(rate, MkBotsTimer, pack, TIMER_REPEAT);
pack.WriteCell(asmany);
pack.WriteCell(onteam);
}
public Action MkBotsTimer(Handle timer, Handle pack) {
Echo(2, "MkBotsTimer");
static int i;
ResetPack(pack);
int asmany = ReadPackCell(pack);
int onteam = ReadPackCell(pack);
if (i++ < asmany) {
switch (onteam) {
case 2: AddSurvivor();
case 3: AddInfected();
}
return Plugin_Continue;
}
i = 0;
g_MkBotsTimer = null;
return Plugin_Stop;
}
Action RmBotsCmd(int client, int args) {
Echo(2, "RmBotsCmd: %d", client);
int asmany;
int onteam;
switch(args) {
case 1: {
GetCmdArg(1, g_sB, sizeof(g_sB));
onteam = StringToInt(g_sB);
asmany = MaxClients;
}
case 2: {
GetCmdArg(1, g_sB, sizeof(g_sB));
asmany = StringToInt(g_sB);
GetCmdArg(2, g_sB, sizeof(g_sB));
onteam = StringToInt(g_sB);
}
}
if (onteam >= 2 || onteam <= 3) {
RmBots(asmany, onteam);
}
return Plugin_Handled;
}
void RmBots(int asmany, int onteam) {
Echo(2, "RmBots: %d %d", asmany, onteam);
int j;
if (onteam == 0) {
onteam = asmany;
asmany = MaxClients;
}
else if (asmany == -0) {
return;
}
else if (asmany < 0) {
asmany += CountTeamMates(onteam);
if (asmany <= 0) {
return;
}
}
for (int i = MaxClients; i >= 1; i--) {
if (GetClientManager(i) == 0 && GetClientTeam(i) == onteam) {
j++;
if (g_StripKick == 1) {
StripClient(i);
}
KickClient(i);
if (j >= asmany) {
break;
}
}
}
}
// ================================================================== //
// MODEL FEATURES
// ================================================================== //
void AutoModel(int client) {
Echo(5, "AutoModel: %d", client);
RequestFrame(_AutoModel, client);
}
public void _AutoModel(int client) {
Echo(5, "_AutoModel: %d", client);
if (IsClientValid(client, 2)) {
SDKUnhook(client, SDKHook_SpawnPost, AutoModel);
}
if (g_AutoModel && IsClientValid(client, 2)) {
static int realClient;
realClient = GetRealClient(client);
if (GetQRecord(realClient) && g_model[0] != EOS) {
return;
}
static int set, survivors, character;
set = GetSurvivorSet(client);
GetAllSurvivorModels(client);
survivors = CountTeamMates(2);
character = g_models[GetClientModelIndex(client)];
if (character == 0 || character < survivors / 8) {
return;
}
for (int i = 0; i < 4; i++) {
for (int index = set; index < sizeof(g_models); index++) {
if (g_models[index] <= i) {
g_models[index]++;
AssignModel(client, g_SurvivorNames[index], g_IdentityFix);
i = 4; // we want to fall through
break;
}
if (set != 0 && index + 1 == sizeof(g_models)) {
index=-1; set=0;
}
}
}
}
}
int GetSurvivorSet(int client) {
Echo(6, "GetSurvivorSet: %d", client);
if (g_survivorSetScan && IsClientValid(client, 2)) {
g_survivorSetScan = false;
g_survivorSet = GetClientModelIndex(client);
if (g_survivorSet >= 0 && g_survivorSet <= 3) {
g_survivorSet = 0;
} else {
g_survivorSet = 4;
}
}
return g_survivorSet;
}
void GetAllSurvivorModels(int client=-1) {
Echo(2, "GetAllSurvivorModels");
static int index;
static const int models[8];
g_models = models;
for (int i = 1; i <= MaxClients; i++) {
if (client == i) {
continue;
}
index = -1;
if (GetQRecord(i) && g_onteam == 2 && g_model[0] != EOS) {
index = GetModelIndexByName(g_model, 2);
}
else if (IsClientValid(i, 2, 0) && GetRealClient(i) == i) {
index = GetClientModelIndex(i);
}
if (index >= 0) {
g_models[index]++;
}
}
}
void PrecacheModels() {
Echo(2, "PrecacheModels");
for (int i = 0; i < sizeof(g_SurvivorPaths); i++) {
PrecacheModel(g_SurvivorPaths[i]);
}
}
void AssignModel(int client, char [] model, int identityFix) {
Echo(2, "AssignModel: %d %s %d", client, model, identityFix);
if (identityFix == 1 && IsClientValid(client, 2)) {
if (IsClientsModel(client, model)) {
return;
}
int i = GetModelIndexByName(model);
int realClient = GetRealClient(client);
if (i >= 0 && i < sizeof(g_SurvivorPaths)) {
if(i == 5) {
SetEntProp(client, Prop_Send, "m_survivorCharacter", g_Zoey);
} else {
SetEntProp(client, Prop_Send, "m_survivorCharacter", i);
}
SetEntityModel(client, g_SurvivorPaths[i]);
Format(g_pN, sizeof(g_pN), "%s", g_SurvivorNames[i]);
if (IsFakeClient(client)) {
SetClientInfo(client, "name", g_pN);
}
if (GetQRecord(realClient)) {
g_QRecord.SetString("model", g_pN, true);
}
}
}
}
int GetClientModelIndex(int client) {
Echo(3, "GetClientModelIndex: %d", client);
if (!IsClientValid(client)) {
return -2;
}
char modelName[64];
GetEntPropString(client, Prop_Data, "m_ModelName", modelName, sizeof(modelName));
for (int i = 0; i < sizeof(g_SurvivorPaths); i++) {
if (StrEqual(modelName, g_SurvivorPaths[i], false)) {
return i;
}
}
return -1;
}
int GetModelIndexByName(char [] name, int onteam=2) {
Echo(2, "GetModelIndexByName: %s %d", name, onteam);
if (onteam == 2) {
for (int i; i < sizeof(g_SurvivorNames); i++) {
if (StrContains(name, g_SurvivorNames[i], false) != -1) {
return i;
}
}
}
else if (onteam == 3) {
for (int i; i < sizeof(g_InfectedNames); i++) {
if (StrContains(g_InfectedNames[i], name, false) != -1) {
return i;
}
}
}
return -1;
}
bool IsClientsModel(int client, char [] name) {
Echo(2, "IsClientsModel: %d %s", client, name);
int modelIndex = GetClientModelIndex(client);
Format(g_sB, sizeof(g_sB), "%s", g_SurvivorNames[modelIndex]);
return StrEqual(name, g_sB);
}
void GetBotCharacter(int client, char strBuffer[32]) {
Echo(2, "GetBotCharacter: %d", client);
if (IsClientValid(client)) {
strBuffer = "";
switch (GetClientTeam(client)) {
case 2: GetSurvivorCharacter(client, strBuffer);
case 3: GetInfectedCharacter(client, strBuffer);
}
}
}
void GetSurvivorCharacter(int client, char strBuffer[32]) {
Echo(2, "GetSurvivorCharacter: %d %s", client, strBuffer);
GetEntPropString(client, Prop_Data, "m_ModelName", g_sB, sizeof(g_sB));
for (int i = 0; i < sizeof(g_SurvivorPaths); i++) {
if (StrEqual(g_SurvivorPaths[i], g_sB)) {
Format(strBuffer, sizeof(strBuffer), g_SurvivorNames[i]);
break;
}
}
}
void GetInfectedCharacter(int client, char strBuffer[32]) {
Echo(2, "GetInfectedCharacter: %d %s", client, strBuffer);
switch (GetEntProp(client, Prop_Send, "m_zombieClass")) {
case 1: strBuffer = "Smoker";
case 2: strBuffer = "Boomer";
case 3: strBuffer = "Hunter";
case 4: strBuffer = "Spitter";
case 5: strBuffer = "Jockey";
case 6: strBuffer = "Charger";
case 8: strBuffer = "Tank";
}
}
// ================================================================== //
// BLACK MAGIC SIGNATURES. SOME SPOOKY SHIT.
// ================================================================== //
int GetOS() {
Echo(2, "GetOS");
return GameConfGetOffset(g_GameData, "OS");
}
void RoundRespawnSig(int client) {
Echo(2, "RoundRespawnSig: %d", client);
static Handle hRoundRespawn;
if (hRoundRespawn == null) {
StartPrepSDKCall(SDKCall_Player);
PrepSDKCall_SetFromConf(g_GameData, SDKConf_Signature, "RoundRespawn");
hRoundRespawn = EndPrepSDKCall();
}
if (hRoundRespawn != null) {
SDKCall(hRoundRespawn, client);
}
else {
PrintToChat(client, "[ABM] RoundRespawnSig Signature broken.");
SetFailState("[ABM] RoundRespawnSig Signature broken.");
}
}
void SetHumanSpecSig(int bot, int client) {
Echo(2, "SetHumanSpecSig: %d %d", bot, client);
static Handle hSpec;
if (hSpec == null) {
StartPrepSDKCall(SDKCall_Player);
PrepSDKCall_SetFromConf(g_GameData, SDKConf_Signature, "SetHumanSpec");
PrepSDKCall_AddParameter(SDKType_CBasePlayer, SDKPass_Pointer);
hSpec = EndPrepSDKCall();
}
if (IsClientValid(client) && IsClientValid(bot)) {
if(hSpec != null) {
SDKCall(hSpec, bot, client);
ResetClientSpecUserId(client, bot);
}
else {
PrintToChat(client, "[ABM] SetHumanSpecSig Signature broken.");
SetFailState("[ABM] SetHumanSpecSig Signature broken.");
}
}
}
void ResetClientSpecUserId(int client, int target) {
Echo(2, "ResetClientSpecUserId: %d %d", client, target);
if (!IsClientValid(client) || !IsClientValid(target)) {
return;
}
static int spec, userid;
userid = GetClientUserId(client);
for (int i = 1; i <= MaxClients; i++) {
if (IsClientValid(i, 2, 0)) {
if (HasEntProp(i, Prop_Send, "m_humanSpectatorUserID")) {
spec = GetEntProp(i, Prop_Send, "m_humanSpectatorUserID");
if (userid == spec && i != target) {
SetEntProp(i, Prop_Send, "m_humanSpectatorUserID", 0);
}
}
}
}
}
void State_TransitionSig(int client, int mode) {
Echo(2, "State_TransitionSig: %d %d", client, mode);
static Handle hSpec;
if (hSpec == null) {
StartPrepSDKCall(SDKCall_Player);
PrepSDKCall_SetFromConf(g_GameData, SDKConf_Signature, "State_Transition");
PrepSDKCall_AddParameter(SDKType_PlainOldData , SDKPass_Plain);
hSpec = EndPrepSDKCall();
}
if(hSpec != null) {
SDKCall(hSpec, client, mode); // mode 8, press 8 to get closer
}
else {
PrintToChat(client, "[ABM] State_TransitionSig Signature broken.");
SetFailState("[ABM] State_TransitionSig Signature broken.");
}
}
bool TakeoverBotSig(int client, int target) {
Echo(2, "TakeoverBotSig: %d %d", client, target);
if (!GetQRecord(client)) {
return false;
}
static Handle hSwitch;
if (hSwitch == null) {
StartPrepSDKCall(SDKCall_Player);
PrepSDKCall_SetFromConf(g_GameData, SDKConf_Signature, "TakeOverBot");
PrepSDKCall_AddParameter(SDKType_Bool, SDKPass_Plain);
hSwitch = EndPrepSDKCall();
}
if (hSwitch != null) {
if (IsClientInKickQueue(target)) {
KickClient(target);
}
else if (IsClientValid(target, 2, 0)) {
SwitchToSpec(client);
if (GetRealClient(target) != client) {
GetBotCharacter(target, g_model);
g_QRecord.SetString("model", g_model, true);
}
SetHumanSpecSig(target, client);
SDKCall(hSwitch, client, true);
GetConVarString(g_cvGameMode, g_sB, sizeof(g_sB));
SendConVarValue(client, g_cvGameMode, g_sB);
return true;
}
}
else {
PrintToChat(client, "[ABM] TakeoverBotSig Signature broken.");
SetFailState("[ABM] TakeoverBotSig Signature broken.");
}
g_QRecord.SetValue("lastid", target, true);
if (GetClientTeam(client) == 1) {
QueueUp(client, 2);
}
return false;
}
bool TakeoverZombieBotSig(int client, int target, bool si_ghost) {
Echo(2, "TakeoverZombieBotSig: %d %d %d", client, target, si_ghost);
if (!GetQRecord(client)) {
return false;
}
static Handle hSwitch;
if (hSwitch == null) {
StartPrepSDKCall(SDKCall_Player);
PrepSDKCall_SetFromConf(g_GameData, SDKConf_Signature, "TakeOverZombieBot");
PrepSDKCall_AddParameter(SDKType_CBasePlayer, SDKPass_Pointer);
hSwitch = EndPrepSDKCall();
}
if (hSwitch != null) {
if (IsClientInKickQueue(target)) {
KickClient(target);
}
else if (IsClientValid(target, 3, 0) && IsPlayerAlive(target)) {
SwitchToSpec(client);
SDKCall(hSwitch, client, target);
if (si_ghost) {
State_TransitionSig(client, 8);
if (GetEntProp(client, Prop_Send, "m_zombieClass") == 8) {
CreateTimer(1.0, ForceSpawnTimer, client, TIMER_REPEAT);
}
}
// trade off, see ladders, not survivors
SendConVarValue(client, g_cvGameMode, "versus");
SendConVarValue(client, g_cvConsistency, g_consistency);
return true;
}
}
else {
PrintToChat(client, "[ABM] TakeoverZombieBotSig Signature broken.");
SetFailState("[ABM] TakeoverZombieBotSig Signature broken.");
}
g_QRecord.SetValue("lastid", target, true);
if (GetClientTeam(client) == 1) {
QueueUp(client, 3);
}
return false;
}
// ================================================================== //
// PUBLIC INTERFACE AND MENU HANDLERS
// ================================================================== //
public Action TeleportClientCmd(int client, int args) {
Echo(2, "TeleportClientCmd: %d", client);
int level;
switch(args) {
case 1: {
GetCmdArg(1, g_sB, sizeof(g_sB));
menuArg1 = StringToInt(g_sB);
}
case 2: {
GetCmdArg(1, g_sB, sizeof(g_sB));
menuArg0 = StringToInt(g_sB);
GetCmdArg(2, g_sB, sizeof(g_sB));
menuArg1 = StringToInt(g_sB);
}
}
if (args) {
level = 2;
}
TeleportClientHandler(client, level);
return Plugin_Handled;
}
public void TeleportClientHandler(int client, int level) {
Echo(2, "TeleportClientHandler: %d %d", client, level);
if (!RegMenuHandler(client, "TeleportClientHandler", level, 0)) {
return;
}
switch(level) {
case 0: TeamMatesMenu(client, "Teleport Client", 2, 1);
case 1: {
GetClientName(menuArg0, g_sB, sizeof(g_sB));
Format(g_sB, sizeof(g_sB), "%s: Teleporting", g_sB);
TeamMatesMenu(client, g_sB, 2, 1);
}
case 2: {
if (CanClientTarget(client, menuArg0)) {
if (GetClientTeam(menuArg0) <= 1) {
menuArg0 = GetPlayClient(menuArg0);
}
TeleportClient(menuArg0, menuArg1);
}
GenericMenuCleaner(client);
}
}
}
public Action SwitchTeamCmd(int client, int args) {
Echo(2, "SwitchTeamCmd: %d", client);
int level;
char model[32];
GetCmdArg(args, model, sizeof(model));
int result = StringToInt(model);
CleanSIName(model);
if (args == 1 || args == 2 && result == 0) {
menuArg0 = client;
GetCmdArg(1, g_sB, sizeof(g_sB));
menuArg1 = StringToInt(g_sB);
}
else if (args >= 2) {
GetCmdArg(1, g_sB, sizeof(g_sB));
menuArg0 = StringToInt(g_sB);
GetCmdArg(2, g_sB, sizeof(g_sB));
menuArg1 = StringToInt(g_sB);
}
if (args) {
level = 2;
}
else if (!IsAdmin(client)) {
menuArg0 = client;
level = 1;
}
if (menuArg1 != 3) {
model = "";
}
SwitchTeamHandler(client, level, model);
return Plugin_Handled;
}
public void SwitchTeamHandler(int client, int level, char model[32]) {
Echo(2, "SwitchTeamHandler: %d %d %s", client, level, model);
if (!RegMenuHandler(client, "SwitchTeamHandler", level, 0)) {
return;
}
switch(level) {
case 0: TeamMatesMenu(client, "Switch Client's Team", 1);
case 1: {
GetClientName(menuArg0, g_sB, sizeof(g_sB));
Format(g_sB, sizeof(g_sB), "%s: Switching", g_sB);
TeamsMenu(client, g_sB);
}
case 2: {
if (CanClientTarget(client, menuArg0)) {
if (!g_IsVs && !IsAdmin(client) && menuArg1 == 3) {
GenericMenuCleaner(client);
return;
}
if (GetQRecord(menuArg0)) {
g_QRecord.SetValue("rdelay", 0.1, true);
SwitchToSpec(menuArg0);
}
SwitchTeam(menuArg0, menuArg1, model);
}
GenericMenuCleaner(client);
}
}
}
public Action AssignModelCmd(int client, int args) {
Echo(2, "AssignModelCmd: %d", client);
int level;
switch(args) {
case 1: {
menuArg0 = client;
GetCmdArg(1, g_sB, sizeof(g_sB));
menuArg1 = GetModelIndexByName(g_sB);
}
case 2: {
GetCmdArg(1, g_sB, sizeof(g_sB));
menuArg1 = GetModelIndexByName(g_sB);
GetCmdArg(2, g_sB, sizeof(g_sB));
menuArg0 = StringToInt(g_sB);
}
}
if (args) {
level = 2;
}
AssignModelHandler(client, level);
return Plugin_Handled;
}
public void AssignModelHandler(int client, int level) {
Echo(2, "AssignModelHandler: %d %d", client, level);
if (!RegMenuHandler(client, "AssignModelHandler", level, 0)) {
return;
}
switch(level) {
case 0: TeamMatesMenu(client, "Change Client's Model", 2, 0, false);
case 1: {
GetClientName(menuArg0, g_sB, sizeof(g_sB));
Format(g_sB, sizeof(g_sB), "%s: Modeling", g_sB);
ModelsMenu(client, g_sB);
}
case 2: {
if (CanClientTarget(client, menuArg0)) {
if (GetClientTeam(menuArg0) <= 1) {
menuArg0 = GetPlayClient(menuArg0);
}
AssignModel(menuArg0, g_SurvivorNames[menuArg1], 1);
}
GenericMenuCleaner(client);
}
}
}
public Action SwitchToBotCmd(int client, int args) {
Echo(2, "SwitchToBotCmd: %d", client);
int level;
switch(args) {
case 1: {
menuArg0 = client;
GetCmdArg(1, g_sB, sizeof(g_sB));
menuArg1 = StringToInt(g_sB);
}
case 2: {
GetCmdArg(1, g_sB, sizeof(g_sB));
menuArg0 = StringToInt(g_sB);
GetCmdArg(2, g_sB, sizeof(g_sB));
menuArg1 = StringToInt(g_sB);
}
}
if (args) {
level = 2;
}
else if (!IsAdmin(client)) {
menuArg0 = client;
level = 1;
}
SwitchToBotHandler(client, level);
return Plugin_Handled;
}
public void SwitchToBotHandler(int client, int level) {
Echo(2, "SwitchToBotHandler: %d %d", client, level);
int homeTeam = ClientHomeTeam(client);
if (!RegMenuHandler(client, "SwitchToBotHandler", level, 0)) {
return;
}
switch(level) {
case 0: TeamMatesMenu(client, "Takeover Bot", 1);
case 1: {
GetClientName(menuArg0, g_sB, sizeof(g_sB));
Format(g_sB, sizeof(g_sB), "%s: Takeover", g_sB);
TeamMatesMenu(client, g_sB, 0, 0, true, false, homeTeam);
}
case 2: {
if (CanClientTarget(client, menuArg0)) {
if (IsClientValid(menuArg1)) {
if (homeTeam != 3 && GetClientTeam(menuArg1) == 3) {
if (!IsAdmin(client)) {
GenericMenuCleaner(client);
return;
}
}
if (GetClientManager(menuArg1) == 0) {
SwitchToBot(menuArg0, menuArg1, false);
}
}
}
GenericMenuCleaner(client);
}
}
}
public Action RespawnClientCmd(int client, int args) {
Echo(2, "RespawnClientCmd: %d", client);
int level;
switch(args) {
case 1: {
GetCmdArg(1, g_sB, sizeof(g_sB));
menuArg0 = StringToInt(g_sB);
menuArg1 = menuArg0;
}
case 2: {
GetCmdArg(1, g_sB, sizeof(g_sB));
menuArg0 = StringToInt(g_sB);
GetCmdArg(2, g_sB, sizeof(g_sB));
menuArg1 = StringToInt(g_sB);
}
}
if (args) {
level = 2;
}
RespawnClientHandler(client, level);
return Plugin_Handled;
}
public void RespawnClientHandler(int client, int level) {
Echo(2, "RespawnClientHandler: %d %d", client, level);
if (!RegMenuHandler(client, "RespawnClientHandler", level, 0)) {
return;
}
switch(level) {
case 0: TeamMatesMenu(client, "Respawn Client");
case 1: {
GetClientName(menuArg0, g_sB, sizeof(g_sB));
Format(g_sB, sizeof(g_sB), "%s: Respawning", g_sB);
TeamMatesMenu(client, g_sB);
}
case 2: {
if (CanClientTarget(client, menuArg0)) {
if (GetClientTeam(menuArg0) <= 1) {
menuArg0 = GetPlayClient(menuArg0);
}
RespawnClient(menuArg0, menuArg1);
}
GenericMenuCleaner(client);
}
}
}
public Action CycleBotsCmd(int client, int args) {
Echo(2, "CycleBotsCmd: %d", client);
int level;
switch(args) {
case 1: {
menuArg0 = client;
GetCmdArg(1, g_sB, sizeof(g_sB));
menuArg1 = StringToInt(g_sB);
}
case 2: {
GetCmdArg(1, g_sB, sizeof(g_sB));
menuArg0 = StringToInt(g_sB);
GetCmdArg(2, g_sB, sizeof(g_sB));
menuArg1 = StringToInt(g_sB);
}
}
if (args) {
if (menuArg1 > 3 || menuArg1 < 2) {
return Plugin_Handled;
}
level = 2;
}
CycleBotsHandler(client, level);
return Plugin_Handled;
}
public void CycleBotsHandler(int client, int level) {
Echo(2, "CycleBotsHandler: %d %d", client, level);
if (!RegMenuHandler(client, "CycleBotsHandler", level, 0)) {
return;
}
switch(level) {
case 0: TeamMatesMenu(client, "Cycle Client", 1);
case 1: {
GetClientName(menuArg0, g_sB, sizeof(g_sB));
Format(g_sB, sizeof(g_sB), "%s: Cycling", g_sB);
TeamsMenu(client, g_sB, false);
}
case 2: {
if (CanClientTarget(client, menuArg0)) {
if (!IsAdmin(client) && menuArg1 == 3) {
GenericMenuCleaner(client);
return;
}
CycleBots(menuArg0, menuArg1);
menuArg1 = 0;
}
CycleBotsHandler(client, 1);
}
}
}
public Action StripClientCmd(int client, int args) {
Echo(2, "StripClientCmd: %d", client);
int target;
int level;
switch(args) {
case 1: {
GetCmdArg(1, g_sB, sizeof(g_sB));
target = StringToInt(g_sB);
target = GetPlayClient(target);
if (CanClientTarget(client, target)) {
StripClient(target);
}
return Plugin_Handled;
}
case 2: {
GetCmdArg(1, g_sB, sizeof(g_sB));
menuArg0 = StringToInt(g_sB);
GetCmdArg(2, g_sB, sizeof(g_sB));
menuArg1 = StringToInt(g_sB);
}
}
if (args) {
level = 2;
}
StripClientHandler(client, level);
return Plugin_Handled;
}
public void StripClientHandler(int client, int level) {
Echo(2, "StripClientHandler: %d %d", client, level);
if (!RegMenuHandler(client, "StripClientHandler", level, 0)) {
return;
}
switch(level) {
case 0: TeamMatesMenu(client, "Strip Client", 2, 1);
case 1: {
GetClientName(menuArg0, g_sB, sizeof(g_sB));
Format(g_sB, sizeof(g_sB), "%s: Stripping", g_sB);
InvSlotsMenu(client, menuArg0, g_sB);
}
case 2: {
if (CanClientTarget(client, menuArg0)) {
if (GetClientTeam(menuArg0) <= 1) {
menuArg0 = GetPlayClient(menuArg0);
}
StripClientSlot(menuArg0, menuArg1);
menuArg1 = 0;
StripClientHandler(client, 1);
}
}
}
}
public Action ResetCmd(int client, int args) {
Echo(2, "ResetCmd: %d", client);
for (int i = 1; i <= MaxClients; i++) {
GenericMenuCleaner(i);
if (GetQRecord(i)) {
CancelClientMenu(i, true, null);
}
}
return Plugin_Handled;
}
bool RegMenuHandler(int client, char [] handler, int level, int clearance=0) {
Echo(2, "RegMenuHandler: %d %s %d %d", client, handler, level, clearance);
g_callBacks.PushString(handler);
if (!IsAdmin(client) && level <= clearance) {
GenericMenuCleaner(client);
return false;
}
return true;
}
public Action MainMenuCmd(int client, int args) {
Echo(2, "MainMenuCmd: %d", client);
GenericMenuCleaner(client);
MainMenuHandler(client, 0);
return Plugin_Handled;
}
public void MainMenuHandler(int client, int level) {
Echo(2, "MainMenuHandler: %d %d", client, level);
if (!RegMenuHandler(client, "MainMenuHandler", level, 0)) {
return;
}
int cmd = menuArg0;
menuArg0 = 0;
char title[32];
Format(title, sizeof(title), "ABM Menu %s", PLUGIN_VERSION);
switch(level) {
case 0: MainMenu(client, title);
case 1: {
switch(cmd) {
case 0: TeleportClientCmd(client, 0);
case 1: SwitchTeamCmd(client, 0);
case 2: AssignModelCmd(client, 0);
case 3: SwitchToBotCmd(client, 0);
case 4: RespawnClientCmd(client, 0);
case 5: CycleBotsCmd(client, 0);
case 6: StripClientCmd(client, 0);
}
}
}
}
// ================================================================== //
// MENUS BACKBONE
// ================================================================== //
void GenericMenuCleaner(int client, bool clearStack=true) {
Echo(2, "GenericMenuCleaner: %d %d", client, clearStack);
for (int i = 0; i < sizeof(g_menuItems[]); i++) {
g_menuItems[client][i] = 0;
}
if (clearStack == true) {
if (g_callBacks != null) {
delete g_callBacks;
}
g_callBacks = new ArrayStack(128);
}
}
public int GenericMenuHandler(Menu menu, MenuAction action, int param1, int param2) {
Echo(2, "GenericMenuHandler: %d %d", param1, param2);
int client = param1;
int i; // -1;
char sB[128];
if (IsClientValid(param1)) {
for (i = 0; i < sizeof(g_menuItems[]); i++) {
if (menuArgs[i] == 0) {
break;
}
}
}
switch(action) {
case MenuAction_Select: {
menu.GetItem(param2, g_sB, sizeof(g_sB));
menuArgs[i] = StringToInt(g_sB);
i = i + 1;
}
case MenuAction_Cancel: {
if (param2 == MenuCancel_ExitBack) {
if (i > 0) {
i = i - 1;
menuArgs[i] = 0;
}
else if (i == 0) {
if (g_callBacks.Empty) {
GenericMenuCleaner(param1);
return 0;
}
g_callBacks.PopString(g_sB, sizeof(g_sB));
GenericMenuCleaner(param1, false);
while (!g_callBacks.Empty) {
g_callBacks.PopString(sB, sizeof(sB));
if (!StrEqual(g_sB, sB)) {
g_callBacks.PushString(sB);
break;
}
}
if (g_callBacks.Empty) {
GenericMenuCleaner(param1);
return 0;
}
}
}
else {
return 0;
}
}
case MenuAction_End: {
delete menu;
return 0;
}
}
if (g_callBacks == null || g_callBacks.Empty) {
GenericMenuCleaner(param1);
return 0;
}
g_callBacks.PopString(g_sB, sizeof(g_sB));
callBack = GetFunctionByName(null, g_sB);
Call_StartFunction(null, callBack);
Call_PushCell(param1);
Call_PushCell(i);
Call_Finish();
return 0;
}
// ================================================================== //
// MENUS
// ================================================================== //
void MainMenu(int client, char [] title) {
Echo(2, "MainMenu: %d %s", client, title);
Menu menu = new Menu(GenericMenuHandler);
menu.SetTitle(title);
menu.AddItem("0", "Teleport Client"); // "Telespiznat"); // teleport
menu.AddItem("1", "Switch Client Team"); //"Swintootle"); // switch team
menu.AddItem("2", "Change Client Model"); //"Changdangle"); // makeover
menu.AddItem("3", "Switch Client Bot"); //"Inbosnachup"); // takeover
menu.AddItem("4", "Respawn Client"); //"Respiggle"); // respawn
menu.AddItem("5", "Cycle Client"); //"Cycolicoo"); // cycle
menu.AddItem("6", "Strip Client"); //"Upsticky"); // strip
menu.ExitBackButton = true;
menu.ExitButton = true;
menu.Display(client, 120);
}
void InvSlotsMenu(int client, int target, char [] title) {
Echo(2, "InvSlotsMenu: %d %d %s", client, target, title);
int ent;
char weapon[64];
Menu menu = new Menu(GenericMenuHandler);
menu.SetTitle(title);
for (int i; i < 5; i++) {
IntToString(i, g_sB, sizeof(g_sB));
ent = GetPlayerWeaponSlot(target, i);
if (IsEntityValid(ent)) {
GetEntityClassname(ent, weapon, sizeof(weapon));
menu.AddItem(g_sB, weapon);
}
}
menu.ExitBackButton = true;
menu.ExitButton = true;
menu.Display(client, 120);
}
void ModelsMenu(int client, char [] title) {
Echo(2, "ModelsMenu: %d %s", client, title);
Menu menu = new Menu(GenericMenuHandler);
menu.SetTitle(title);
for (int i; i < sizeof(g_SurvivorNames); i++) {
IntToString(i, g_sB, sizeof(g_sB));
menu.AddItem(g_sB, g_SurvivorNames[i]);
}
menu.ExitBackButton = true;
menu.ExitButton = true;
menu.Display(client, 120);
}
void TeamsMenu(int client, char [] title, bool all=true) {
Echo(2, "TeamsMenu: %d %s %d", client, title, all);
Menu menu = new Menu(GenericMenuHandler);
menu.SetTitle(title);
if (all) {
menu.AddItem("0", "Idler");
menu.AddItem("1", "Spectator");
}
menu.AddItem("2", "Survivor");
if (g_IsVs || IsAdmin(client)) {
menu.AddItem("3", "Infected");
}
menu.ExitBackButton = true;
menu.ExitButton = true;
menu.Display(client, 120);
}
void TeamMatesMenu(int client, char [] title, int mtype=2, int target=0, bool incDead=true,
bool repeat=false, int homeTeam=0) {
Echo(2, "TeamMatesMenu: %d %s %d %d %d %d %d", client, title, mtype, target, incDead, repeat, homeTeam);
Menu menu = new Menu(GenericMenuHandler);
menu.SetTitle(title);
int isAdmin = IsAdmin(client);
char health[32];
bool mflag = false;
int isAlive;
int playClient;
int bossClient;
int targetClient;
int manager;
for (int i = 1; i <= MaxClients; i++) {
bossClient = i;
playClient = i;
if (GetQRecord(i)) {
if (mtype == 0) {
continue;
}
if (mtype == 1 || mtype == 2) {
mflag = true;
}
if (IsClientValid(g_target) && g_target != i) {
isAlive = IsPlayerAlive(g_target);
playClient = g_target;
}
else {
isAlive = IsPlayerAlive(i);
}
}
else if (IsClientValid(i)) {
isAlive = IsPlayerAlive(i);
if (mtype == 0 || mtype == 2) {
mflag = true;
}
manager = GetClientManager(i);
if (manager != 0) {
if (target == 0 || !repeat) {
mflag = false;
continue;
}
bossClient = manager;
}
}
else {
continue;
}
// at this point the client is valid.
// bossClient is the human (if there is one)
// playClient is the bot (or human if not idle)
if (!isAlive && !incDead) {
continue;
}
if (GetClientTeam(playClient) != homeTeam && !isAdmin) {
continue;
}
switch(target) {
case 0: targetClient = bossClient;
case 1: targetClient = playClient;
}
if (mflag) {
mflag = false;
Format(health, sizeof(health), "%d", GetClientHealth(playClient));
if (!IsPlayerAlive(playClient)) {
Format(health, sizeof(health), "DEAD");
}
else if (GetEntProp(playClient, Prop_Send, "m_isIncapacitated")) {
Format(health, sizeof(health), "DOWN");
}
GetClientName(bossClient, g_pN, sizeof(g_pN));
Format(g_pN, sizeof(g_pN), "%s (%s)", g_pN, health);
IntToString(targetClient, g_sB, sizeof(g_sB));
if(bossClient == client && menu.ItemCount > 0) {
menu.InsertItem(0, g_sB, g_pN);
} else {
menu.AddItem(g_sB, g_pN);
}
}
}
menu.ExitBackButton = true;
menu.ExitButton = true;
menu.Display(client, 120);
}
// ================================================================== //
// MISC STUFF USEFUL FOR TROUBLESHOOTING
// ================================================================== //
void Echo(int level, char [] format, any ...) {
static char g_dB[512];
if (g_LogLevel >= level) {
VFormat(g_dB, sizeof(g_dB), format, 3);
LogToFile(LOGFILE, g_dB);
PrintToServer("%s", g_dB);
}
}
void QDBCheckCmd(int client) {
Echo(2, "QDBCheckCmd");
PrintToConsole(client, "-- STAT: QDB Size is %d", g_QDB.Size);
PrintToConsole(client, "-- MinPlayers is %d", g_MinPlayers);
for (int i = 1; i <= MaxClients; i++) {
if (GetQRtmp(i)) {
PrintToConsole(client, "\n -");
GetClientName(i, g_pN, sizeof(g_pN));
float x = g_origin[0];
float y = g_origin[1];
float z = g_origin[2];
PrintToConsole(client, " - Name: %s", g_pN);
PrintToConsole(client, " - Origin: {%d.0, %d.0, %d.0}", x, y, z);
PrintToConsole(client, " - Status: %d", g_tmpStatus);
PrintToConsole(client, " - Client: %d", g_tmpClient);
PrintToConsole(client, " - Target: %d", g_tmpTarget);
PrintToConsole(client, " - LastId: %d", g_tmpLastid);
PrintToConsole(client, " - OnTeam: %d", g_tmpOnteam);
PrintToConsole(client, " - Queued: %d", g_tmpQueued);
PrintToConsole(client, " - InSpec: %d", g_tmpInspec);
PrintToConsole(client, " - Model: %s", g_tmpModel);
PrintToConsole(client, " -\n");
}
}
}
Action QuickClientPrintCmd(int client, int args) {
Echo(2, "QuickClientPrintCmd: %d", client);
int onteam;
int state;
int manager;
PrintToConsole(client, "\nTeam\tState\tId\tManager\tName");
for (int i = 1; i <= MaxClients; i++) {
if (IsClientValid(i)) {
manager = i;
GetClientName(i, g_pN, sizeof(g_pN));
onteam = GetClientTeam(i);
state = IsPlayerAlive(i);
if (IsFakeClient(i)) {
manager = GetClientManager(i);
}
PrintToConsole(client,
"%d, \t%d, \t%d, \t%d, \t%s", onteam, state, i, manager, g_pN
);
}
}
QDBCheckCmd(client);
return Plugin_Handled;
}
Action DebugCmd(int client, int args) {
ReplyToCommand(client, "CreateSurvivorBot: %b", GetFeatureStatus(FeatureType_Native, "NextBotCreatePlayerBotSurvivorBot") == FeatureStatus_Available);
return Plugin_Handled;
}