#pragma newdecls required #include #include #include public Plugin myinfo = { name = "L4D2 Survivor Bot Fix", author = "DingbatFlat", description = "Survivor Bot Fix. Improve Survivor Bot", version = "1.00", url = "https://github.com/Jackzmc/sourcemod-plugins" } /* // ==================================================================================================== About: - Main items that can be improve bots by introducing this plugin. Help a pinning Survivor. Attack a Common Infected. Attack a Special Infected. Attack a Tank. Bash a flying Hunter and Jockey. Shoot a tank rock. Shoot a Witch (Contronls the attack timing when have a shotgun). Restrict switching to the sub weapon. And the action during incapacitated. - Sourcemod ver 1.10 is required. // ==================================================================================================== How to use: Make sure "sb_fix_enabled" in the CVars is 1. - Select the improved bot with the following CVar. If "sb_fix_select_type" is 0, It is always enabled. If "sb_fix_select_type" is 1, the number of people set in "sb_fix_select_number" will be randomly select. If "sb_fix_select_type" is 2, Select the bot of the character entered in "sb_fix_select_character_name". - For 1 and 2, bots that improve after left the safe room are selected. // ==================================================================================================== Change Log: 1.00 (09-September-2021) - Initial release. // ==================================================================================================== // It is difficult to improve the movement operation. // This is the limit of my power and I can't add any further improvement points maybe... so arrange as you like. */ #define SOUND_SELECT "level/gnomeftw.wav" #define SOUND_SWING "ui/pickup_guitarriff10.wav" #define BUFSIZE (1 << 12) // 4k #define ZC_SMOKER 1 #define ZC_BOOMER 2 #define ZC_HUNTER 3 #define ZC_SPITTER 4 #define ZC_JOCKEY 5 #define ZC_CHARGER 6 #define ZC_TANK 8 #define MAXPLAYERS1 (MAXPLAYERS+1) #define MAXENTITIES 2048 #define WITCH_INCAPACITATED 1 #define WITCH_KILLED 2 /****************************************************************************************************/ // ==================================================================================================== // Handle // ==================================================================================================== ConVar sb_fix_enabled; ConVar sb_fix_select_type; ConVar sb_fix_select_number; ConVar sb_fix_select_character_name; ConVar sb_fix_dont_switch_secondary; ConVar sb_fix_help_enabled; ConVar sb_fix_help_range; ConVar sb_fix_help_shove_type; ConVar sb_fix_help_shove_reloading; ConVar sb_fix_ci_enabled; ConVar sb_fix_ci_range; ConVar sb_fix_ci_melee_allow; ConVar sb_fix_ci_melee_range; ConVar sb_fix_si_enabled; ConVar sb_fix_si_range; ConVar sb_fix_si_ignore_boomer; ConVar sb_fix_si_ignore_boomer_range; ConVar sb_fix_tank_enabled; ConVar sb_fix_tank_range; ConVar sb_fix_si_tank_priority_type; ConVar sb_fix_bash_enabled; ConVar sb_fix_bash_hunter_chance; ConVar sb_fix_bash_hunter_range; ConVar sb_fix_bash_jockey_chance; ConVar sb_fix_bash_jockey_range; ConVar sb_fix_rock_enabled; ConVar sb_fix_rock_range; ConVar sb_fix_witch_enabled; ConVar sb_fix_witch_range; ConVar sb_fix_witch_range_incapacitated; ConVar sb_fix_witch_range_killed; ConVar sb_fix_witch_shotgun_control; ConVar sb_fix_witch_shotgun_range_max; ConVar sb_fix_witch_shotgun_range_min; ConVar sb_fix_prioritize_ownersmoker; ConVar sb_fix_incapacitated_enabled; ConVar sb_fix_debug; // ==================================================================================================== // SendProp // ==================================================================================================== int g_Velo = -1; int g_ActiveWeapon = -1; int g_iAmmoOffset = -1; // ==================================================================================================== // Variables // ==================================================================================================== bool g_hEnabled; int c_iSelectType; int c_iSelectNumber; bool c_bDontSwitchSecondary; bool c_bHelp_Enabled; float c_fHelp_Range; int c_iHelp_ShoveType; bool c_bHelp_ShoveOnlyReloading; bool c_bCI_Enabled; float c_fCI_Range; bool c_bCI_MeleeEnabled; float c_fCI_MeleeRange; bool c_bSI_Enabled; float c_fSI_Range; bool c_bSI_IgnoreBoomer; float c_fSI_IgnoreBoomerRange; bool c_bTank_Enabled; float c_fTank_Range; int c_iSITank_PriorityType; bool c_bBash_Enabled; int c_iBash_HunterChance; float c_fBash_HunterRange; int c_iBash_JockeyChance; float c_fBash_JockeyRange; bool c_bRock_Enabled; float c_fRock_Range; bool c_bWitch_Enabled; float c_fWitch_Range; float c_fWitch_Range_Incapacitated; float c_fWitch_Range_Killed; bool c_bWitch_Shotgun_Control; float c_fWitch_Shotgun_Range_Max; float c_fWitch_Shotgun_Range_Min; bool c_bPrioritize_OwnerSmoker; bool c_bIncapacitated_Enabled; bool c_bDebug_Enabled; // ==================================================================================================== // Int Array // ==================================================================================================== int g_iWitch_Process[MAXENTITIES]; int g_Stock_NextThinkTick[MAXPLAYERS1]; // ==================================================================================================== // Bool Array // ==================================================================================================== bool g_bFixTarget[MAXPLAYERS1]; bool g_bDanger[MAXPLAYERS1]; bool g_bWitchActive = false; bool g_bCommonWithinMelee[MAXPLAYERS1]; bool g_bShove[MAXPLAYERS1][MAXPLAYERS1]; // ==================================================================================================== // Round // ==================================================================================================== bool LeftSafeRoom = false; bool TimerAlreadyWorking = false; /****************************************************************************************************/ bool bLateLoad = false; public APLRes AskPluginLoad2(Handle plugin, bool late, char[] error, int errMax) { bLateLoad = late; return APLRes_Success; } public void OnPluginStart() { // Notes: // If "~_enabled" of the group is not set to 1, other Cvars in that group will not work. // If the plugin is too heavy, Try disable searching for "Entities" other than Client. (CI, Witch and tank rock) // --------------------------------- sb_fix_enabled = CreateConVar("sb_fix_enabled", "1", "Enable the plugin. <0: Disable, 1: Enable>", FCVAR_NONE, true, 0.0, true, 1.0); // --------------------------------- sb_fix_select_type = CreateConVar("sb_fix_select_type", "0", "Which survivor bots to improved. <0: All, 1: Randomly select X people when left the safe area, 2: Enter the character name of the survivor bot to improve in \"sb_fix_select_character_name\">", FCVAR_NONE, true, 0.0, true, 2.0); sb_fix_select_number = CreateConVar("sb_fix_select_number", "1", "If \"sb_fix_select_type\" is 1, Enter the number of survivor bots. <0 ~ 4>", FCVAR_NONE, true, 0.0); sb_fix_select_character_name = CreateConVar("sb_fix_select_character_name", "", "If \"sb_fix_select_type\" is 4, Enter the character name to improved. Separate with spaces. Example: \"nick francis bill\"", FCVAR_NONE); // "coach ellis rochelle nick louis francis zoey bill" // --------------------------------- sb_fix_dont_switch_secondary = CreateConVar("sb_fix_dont_switch_secondary", "1", "Disallow switching to the secondary weapon until the primary weapon is out of ammo. <0:No, 1:Yes | def: 1>", FCVAR_NONE, true, 0.0, true, 1.0); // --------------------------------- sb_fix_help_enabled = CreateConVar("sb_fix_help_enabled", "1", "Help a pinning survivor. <0: Disable, 1: Enable | def: 1>", FCVAR_NONE, true, 0.0, true, 1.0); sb_fix_help_range = CreateConVar("sb_fix_help_range", "1200", "Range to shoot/search a pinning survivor. <1 ~ 3000 | def: 1200>", FCVAR_NONE, true, 1.0, true, 3000.0); sb_fix_help_shove_type = CreateConVar("sb_fix_help_shove_type", "2", "Whether to help by shove. <0: Not help by shove, 1: Smoker only, 2: Smoker and Jockey, 3: Smoker, Jockey and Hunter | def: 2>", FCVAR_NONE, true, 0.0, true, 3.0); sb_fix_help_shove_reloading = CreateConVar("sb_fix_help_shove_reloading", "0", "If \"sb_fix_help_shove_type\" is 2 or more, it is shove only while reloading. <0: No, 1: Yes | def: 0>", FCVAR_NONE, true, 0.0, true, 1.0); // --------------------------------- sb_fix_ci_enabled = CreateConVar("sb_fix_ci_enabled", "1", "Deal with Common Infecteds. <0: Disable, 1: Enable | def: 1>", FCVAR_NONE, true, 0.0, true, 1.0); sb_fix_ci_range = CreateConVar("sb_fix_ci_range", "500", "Range to shoot/search a Common Infected. <1 ~ 2000 | def: 500>", FCVAR_NONE, true, 1.0, true, 2000.0); sb_fix_ci_melee_allow = CreateConVar("sb_fix_ci_melee_allow", "1", "Allow to deal with the melee weapon. <0: Disable 1: Enable | def: 1>", FCVAR_NONE, true, 0.0, true, 1.0); sb_fix_ci_melee_range = CreateConVar("sb_fix_ci_melee_range", "160", "If \"sb_fix_ci_melee_allow\" is enabled, range to deal with the melee weapon. <1 ~ 500 | def: 160>", FCVAR_NONE, true, 1.0, true, 500.0); // --------------------------------- sb_fix_si_enabled = CreateConVar("sb_fix_si_enabled", "1", "Deal with Special Infecteds. <0: Disable, 1: Enable | def: 1>", FCVAR_NONE, true, 0.0, true, 1.0); sb_fix_si_range = CreateConVar("sb_fix_si_range", "500", "Range to shoot/search a Special Infected. <1 ~ 3000 | def: 500>", FCVAR_NONE, true, 1.0, true, 3000.0); sb_fix_si_ignore_boomer = CreateConVar("sb_fix_si_ignore_boomer", "1", "Ignore a Boomer near Survivors (and shove a Boomer). <0: No, 1: Yes | def: 1>", FCVAR_NONE, true, 0.0, true, 1.0); sb_fix_si_ignore_boomer_range = CreateConVar("sb_fix_si_ignore_boomer_range", "200", "Range to ignore a Boomer. <1 ~ 900 | def: 200>", FCVAR_NONE, true, 1.0, true, 500.0); // --------------------------------- sb_fix_tank_enabled = CreateConVar("sb_fix_tank_enabled", "1", "Deal with Tanks. <0: Disable, 1: Enable | def: 1>", FCVAR_NONE, true, 0.0, true, 1.0); sb_fix_tank_range = CreateConVar("sb_fix_tank_range", "1200", "Range to shoot/search a Tank. <1 ~ 3000 | def: 1200>", FCVAR_NONE, true, 1.0, true, 3000.0); // --------------------------------- sb_fix_si_tank_priority_type = CreateConVar("sb_fix_si_tank_priority_type", "0", "When a Special Infected and a Tank is together within the specified range, which to prioritize. <0: Nearest, 1: Special Infected, 2: Tank | def: 0>", FCVAR_NONE, true, 0.0, true, 2.0); // --------------------------------- sb_fix_bash_enabled = CreateConVar("sb_fix_bash_enabled", "1", "Bash a flying Hunter or Jockey. <0: Disable, 1: Enable | def: 1>", FCVAR_NONE, true, 0.0, true, 1.0); sb_fix_bash_hunter_chance = CreateConVar("sb_fix_bash_hunter_chance", "60", "Chance of bash a flying Hunter. (Even 100 doesn't can perfectly shove). <1 ~ 100 | def: 100>", FCVAR_NONE, true, 0.0, true, 100.0); sb_fix_bash_hunter_range = CreateConVar("sb_fix_bash_hunter_range", "145", "Range to bash/search a flying Hunter. <1 ~ 500 | def: 145>", FCVAR_NONE, true, 1.0, true, 500.0); sb_fix_bash_jockey_chance = CreateConVar("sb_fix_bash_jockey_chance", "60", "Chance of bash a flying Jockey. (Even 100 doesn't can perfectly shove). <1 ~ 100 | def: 100>", FCVAR_NONE, true, 0.0, true, 100.0); sb_fix_bash_jockey_range = CreateConVar("sb_fix_bash_jockey_range", "125", "Range to bash/search a flying Jockey. <1 ~ 500 | def: 125>", FCVAR_NONE, true, 1.0, true, 500.0); // --------------------------------- sb_fix_rock_enabled = CreateConVar("sb_fix_rock_enabled", "1", "Shoot a tank rock. <0: Disable, 1: Enable | def: 1>", FCVAR_NONE, true, 0.0, true, 1.0); sb_fix_rock_range = CreateConVar("sb_fix_rock_range", "500", "Range to shoot/search a tank rock. <1 ~ 2000 | def: 700>", FCVAR_NONE, true, 1.0, true, 2000.0); // --------------------------------- sb_fix_witch_enabled = CreateConVar("sb_fix_witch_enabled", "1", "Shoot a rage Witch. <0: Disable, 1: Enable | def: 1>", FCVAR_NONE, true, 0.0, true, 1.0); sb_fix_witch_range = CreateConVar("sb_fix_witch_range", "1500", "Range to shoot/search a rage Witch. <1 ~ 2000 | def: 1500>", FCVAR_NONE, true, 1.0, true, 2000.0); sb_fix_witch_range_incapacitated = CreateConVar("sb_fix_witch_range_incapacitated", "1000", "Range to shoot/search a Witch that incapacitated a survivor. <0 ~ 2000 | def: 1000>", FCVAR_NONE, true, 0.0, true, 2000.0); sb_fix_witch_range_killed = CreateConVar("sb_fix_witch_range_killed", "0", "Range to shoot/search a Witch that killed a survivor. <0 ~ 2000 | def: 0>", FCVAR_NONE, true, 0.0, true, 2000.0); sb_fix_witch_shotgun_control = CreateConVar("sb_fix_witch_shotgun_control", "1", "[Witch] If have the shotgun, controls the attack timing. <0: Disable, 1: Enable | def: 1>", FCVAR_NONE, true, 0.0, true, 1.0); sb_fix_witch_shotgun_range_max = CreateConVar("sb_fix_witch_shotgun_range_max", "300", "If a Witch is within distance of the values, stop the attack. <1 ~ 1000 | def: 300>", FCVAR_NONE, true, 1.0, true, 1000.0); sb_fix_witch_shotgun_range_min = CreateConVar("sb_fix_witch_shotgun_range_min", "70", "If a Witch is at distance of the values or more, stop the attack. <1 ~ 500 | def: 70>", FCVAR_NONE, true, 1.0, true, 500.0); // --------------------------------- sb_fix_prioritize_ownersmoker = CreateConVar("sb_fix_prioritize_ownersmoker", "1", "Priority given to dealt a Smoker that is try to pinning self. <0: No, 1: Yes | def: 1>", FCVAR_NONE, true, 0.0, true, 1.0); // --------------------------------- sb_fix_incapacitated_enabled = CreateConVar("sb_fix_incapacitated_enabled", "1", "Enable Incapacitated Cmd. <0: Disable, 1: Enable | def: 1>", FCVAR_NONE, true, 0.0, true, 1.0); // --------------------------------- sb_fix_debug = CreateConVar("sb_fix_debug", "0", "[For debug] Print the action status. <0:Disable, 1:Enable>", FCVAR_NONE, true, 0.0, true, 1.0); HookConVarChange(sb_fix_help_enabled, SBHelp_ChangeConvar); HookConVarChange(sb_fix_help_range, SBHelp_ChangeConvar); HookConVarChange(sb_fix_help_shove_type, SBHelp_ChangeConvar); HookConVarChange(sb_fix_help_shove_reloading, SBHelp_ChangeConvar); // --------------------------------- HookConVarChange(sb_fix_ci_enabled, SBCI_ChangeConvar); HookConVarChange(sb_fix_ci_range, SBCI_ChangeConvar); HookConVarChange(sb_fix_ci_melee_allow, SBCI_ChangeConvar); HookConVarChange(sb_fix_ci_melee_range, SBCI_ChangeConvar); // --------------------------------- HookConVarChange(sb_fix_si_enabled, SBSI_ChangeConvar); HookConVarChange(sb_fix_si_range, SBSI_ChangeConvar); HookConVarChange(sb_fix_si_ignore_boomer, SBSI_ChangeConvar); HookConVarChange(sb_fix_si_ignore_boomer_range, SBSI_ChangeConvar) // --------------------------------- HookConVarChange(sb_fix_tank_enabled, SBTank_ChangeConvar); HookConVarChange(sb_fix_tank_range, SBTank_ChangeConvar); // --------------------------------- HookConVarChange(sb_fix_si_tank_priority_type, SBTank_ChangeConvar); // --------------------------------- HookConVarChange(sb_fix_bash_enabled, SBBash_ChangeConvar); HookConVarChange(sb_fix_bash_hunter_chance, SBBash_ChangeConvar); HookConVarChange(sb_fix_bash_hunter_range, SBBash_ChangeConvar); HookConVarChange(sb_fix_bash_jockey_chance, SBBash_ChangeConvar); HookConVarChange(sb_fix_bash_jockey_range, SBBash_ChangeConvar); // --------------------------------- HookConVarChange(sb_fix_rock_enabled, SBEnt_ChangeConvar); HookConVarChange(sb_fix_rock_range, SBEnt_ChangeConvar); HookConVarChange(sb_fix_witch_enabled, SBEnt_ChangeConvar); HookConVarChange(sb_fix_witch_range, SBEnt_ChangeConvar); HookConVarChange(sb_fix_witch_range_incapacitated, SBEnt_ChangeConvar); HookConVarChange(sb_fix_witch_range_killed, SBEnt_ChangeConvar); HookConVarChange(sb_fix_witch_shotgun_control, SBEnt_ChangeConvar); HookConVarChange(sb_fix_witch_shotgun_range_max, SBEnt_ChangeConvar); HookConVarChange(sb_fix_witch_shotgun_range_min, SBEnt_ChangeConvar); // --------------------------------- HookConVarChange(sb_fix_enabled, SBConfigChangeConvar); HookConVarChange(sb_fix_select_type, SBConfigChangeConvar); HookConVarChange(sb_fix_select_number, SBConfigChangeConvar); HookConVarChange(sb_fix_dont_switch_secondary, SBConfigChangeConvar); HookConVarChange(sb_fix_prioritize_ownersmoker, SBConfigChangeConvar); HookConVarChange(sb_fix_incapacitated_enabled, SBConfigChangeConvar); HookConVarChange(sb_fix_debug, SBConfigChangeConvar); // --------------------------------- HookConVarChange(sb_fix_select_type, SBSelectChangeConvar); HookConVarChange(sb_fix_select_number, SBSelectChangeConvar); HookConVarChange(sb_fix_select_character_name, SBSelectChangeConvar); if (bLateLoad) { for (int x = 1; x <= MaxClients; x++) { if (x > 0 && x <= MaxClients && IsClientInGame(x)) { SDKHook(x, SDKHook_WeaponSwitch, WeaponSwitch); } } } AutoExecConfig(false, "l4d2_sb_fix"); PrecacheSound(SOUND_SWING); HookEvent("round_start", Event_RoundStart); HookEvent("bot_player_replace", Event_BotAndPlayerReplace, EventHookMode_Pre); // SelectImprovedTarget HookEvent("player_incapacitated", Event_PlayerIncapacitated); // Witch Event HookEvent("player_death", Event_PlayerDeath); // Witch Event HookEvent("witch_harasser_set", Event_WitchRage); g_Velo = FindSendPropInfo("CBasePlayer", "m_vecVelocity[0]"); g_ActiveWeapon = FindSendPropInfo("CBasePlayer", "m_hActiveWeapon"); g_iAmmoOffset = FindSendPropInfo("CTerrorPlayer", "m_iAmmo"); CreateTimer(3.0, Timer_ShoveChance, _, TIMER_REPEAT); InitTimers(); // Safe Room Check } public void OnMapStart() { input_Help(); input_CI(); input_SI(); input_Tank(); input_Bash(); input_Entity(); inputConfig(); } public void OnAllPluginsLoaded() { input_Help(); input_CI(); input_SI(); input_Tank(); input_Bash(); input_Entity(); inputConfig(); } public void SBHelp_ChangeConvar(Handle convar, const char[] oldValue, const char[] intValue) { input_Help(); } public void SBCI_ChangeConvar(Handle convar, const char[] oldValue, const char[] intValue) { input_CI(); } public void SBSI_ChangeConvar(Handle convar, const char[] oldValue, const char[] intValue) { input_SI(); } public void SBTank_ChangeConvar(Handle convar, const char[] oldValue, const char[] intValue) { input_Tank(); } public void SBBash_ChangeConvar(Handle convar, const char[] oldValue, const char[] intValue) { input_Bash(); } public void SBEnt_ChangeConvar(Handle convar, const char[] oldValue, const char[] intValue) { input_Entity(); } public void SBConfigChangeConvar(Handle convar, const char[] oldValue, const char[] intValue) { inputConfig(); } public void SBSelectChangeConvar(Handle convar, const char[] oldValue, const char[] intValue) { SelectImprovedTarget(); } // all _range cvars are squared for optimized distance check void input_Help() { c_bHelp_Enabled = GetConVarBool(sb_fix_help_enabled); c_fHelp_Range = Pow(sb_fix_help_range.FloatValue, 2.0); c_iHelp_ShoveType = GetConVarInt(sb_fix_help_shove_type); c_bHelp_ShoveOnlyReloading = GetConVarBool(sb_fix_help_shove_reloading); } void input_CI() { c_bCI_Enabled = GetConVarBool(sb_fix_ci_enabled); c_fCI_Range = Pow(sb_fix_ci_range.FloatValue, 2.0); c_bCI_MeleeEnabled = GetConVarBool(sb_fix_ci_melee_allow); c_fCI_MeleeRange = Pow(sb_fix_ci_melee_range.FloatValue, 2.0); } void input_SI() { c_bSI_Enabled = GetConVarBool(sb_fix_si_enabled); c_fSI_Range = Pow(sb_fix_si_range.FloatValue, 2.0); c_bSI_IgnoreBoomer = GetConVarBool(sb_fix_si_ignore_boomer); c_fSI_IgnoreBoomerRange = Pow(sb_fix_si_ignore_boomer_range.FloatValue, 2.0); } void input_Tank() { c_bTank_Enabled = GetConVarBool(sb_fix_tank_enabled); c_fTank_Range = Pow(sb_fix_tank_range.FloatValue, 2.0); c_iSITank_PriorityType = GetConVarInt(sb_fix_si_tank_priority_type); } void input_Bash() { c_bBash_Enabled = GetConVarBool(sb_fix_bash_enabled); c_iBash_HunterChance = GetConVarInt(sb_fix_bash_hunter_chance); c_fBash_HunterRange = Pow(sb_fix_bash_hunter_range.FloatValue, 2.0); c_iBash_JockeyChance = GetConVarInt(sb_fix_bash_jockey_chance); c_fBash_JockeyRange = Pow(sb_fix_bash_jockey_range.FloatValue, 2.0); } void input_Entity() { c_bRock_Enabled = GetConVarBool(sb_fix_rock_enabled); c_fRock_Range = Pow(sb_fix_rock_range.FloatValue, 2.0); c_bWitch_Enabled = GetConVarBool(sb_fix_witch_enabled); c_fWitch_Range = Pow(sb_fix_witch_range.FloatValue, 2.0); c_fWitch_Range_Incapacitated = Pow(sb_fix_witch_range_incapacitated.FloatValue, 2.0); c_fWitch_Range_Killed = Pow(sb_fix_witch_range_killed.FloatValue, 2.0); c_bWitch_Shotgun_Control = GetConVarBool(sb_fix_witch_shotgun_control); c_fWitch_Shotgun_Range_Max = Pow(sb_fix_witch_shotgun_range_max.FloatValue, 2.0); c_fWitch_Shotgun_Range_Min = Pow(sb_fix_witch_shotgun_range_min.FloatValue, 2.0); } void inputConfig() { g_hEnabled = GetConVarBool(sb_fix_enabled); c_iSelectType = GetConVarInt(sb_fix_select_type); c_iSelectNumber = GetConVarInt(sb_fix_select_number); c_bDontSwitchSecondary = GetConVarBool(sb_fix_dont_switch_secondary); c_bPrioritize_OwnerSmoker = GetConVarBool(sb_fix_prioritize_ownersmoker); c_bIncapacitated_Enabled = GetConVarBool(sb_fix_incapacitated_enabled); c_bDebug_Enabled = GetConVarBool(sb_fix_debug); } /****************************************************************************************************/ /* ================================================================================================ *= *= Round / Start Ready / Select Improved Targets *= ================================================================================================ */ public void Event_RoundStart(Event event, const char[] name, bool dontBroadcast) { for (int x = 1; x <= MAXPLAYERS; x++) g_bFixTarget[x] = false; // RESET LeftSafeRoom = false; if (!TimerAlreadyWorking) { CreateTimer(1.0, Timer_PlayerLeftCheck); TimerAlreadyWorking = true; } InitTimers(); } public void Event_BotAndPlayerReplace(Handle event, const char[] name, bool dontBroadcast) { if (!LeftSafeRoom) return; int bot = GetClientOfUserId(GetEventInt(event, "bot")); if (g_bFixTarget[bot]) { SelectImprovedTarget(); } } void InitTimers() { if (LeftSafeRoom) SelectImprovedTarget(); else if (!TimerAlreadyWorking) { TimerAlreadyWorking = true; CreateTimer(1.0, Timer_PlayerLeftCheck); } } public Action Timer_PlayerLeftCheck(Handle Timer) { if (LeftStartArea()) { if (!LeftSafeRoom) { LeftSafeRoom = true; SelectImprovedTarget(); // PrintToChatAll("[sb_fix] Survivors left the safe area."); } TimerAlreadyWorking = false; } else { CreateTimer(1.0, Timer_PlayerLeftCheck); } return Plugin_Continue; } bool LeftStartArea() { int ent = -1, maxents = GetMaxEntities(); for (int i = MaxClients+1; i <= maxents; i++) { if (IsValidEntity(i)) { static char netclass[32]; GetEntityNetClass(i, netclass, sizeof(netclass)); if (StrEqual(netclass, "CTerrorPlayerResource")) { ent = i; break; } } } if (ent > -1) { int offset = FindSendPropInfo("CTerrorPlayerResource", "m_hasAnySurvivorLeftSafeArea"); if (offset > 0) { if (GetEntData(ent, offset)) { if (GetEntData(ent, offset) == 1) return true; } } } return false; } void SelectImprovedTarget() { if (!g_hEnabled || !LeftSafeRoom) return; // Select targets when left the safe area. else if (c_iSelectType == 1) { int count; for (int x = 1; x <= MaxClients; x++) { if (isSurvivorBot(x)) { g_bFixTarget[x] = true; count++ } if (count >= c_iSelectNumber) { break; } } } else if (c_iSelectType == 2) { static char sSelectName[256]; GetConVarString(sb_fix_select_character_name, sSelectName, sizeof(sSelectName)); int count; for (int x = 1; x <= MaxClients; x++) { if (isSurvivorBot(x)) { static char sName[128]; GetClientName(x, sName, sizeof(sName)); if (StrContains(sSelectName, sName, false) != -1) { g_bFixTarget[x] = true; count++; //PrintToChatAll("\x04%d\x05. %N", count, x); } else { g_bFixTarget[x] = false; } } } } } public Action Timer_ShoveChance(Handle Timer) { // ----------------------- Bash Chance ----------------------- if (c_iBash_HunterChance < 100 || c_iBash_JockeyChance < 100) { for (int sb = 1; sb <= MaxClients; sb++) { if (isSurvivorBot(sb) && IsPlayerAlive(sb)) { for (int x = 1; x <= MaxClients; x++) { if (isInfected(x) && IsPlayerAlive(x)) { int zombieClass = getZombieClass(x); if (zombieClass == ZC_HUNTER) { if (GetRandomInt(0, 100) <= c_iBash_HunterChance) g_bShove[sb][x] = true; else g_bShove[sb][x] = false; // PrintToChatAll("%N's Shove to %N: %b", sb, x, g_bShove[sb][x]); } else if (zombieClass == ZC_JOCKEY) { if (GetRandomInt(0, 100) <= c_iBash_JockeyChance) g_bShove[sb][x] = true; else g_bShove[sb][x] = false; // PrintToChatAll("%N's Shove to %N: %b", sb, x, g_bShove[sb][x]); } } } } } } return Plugin_Continue; } /****************************************************************************************************/ /* Client key input processing * * buttons: Entered keys (enum��include/entity_prop_stock.inc�Q��) * angles: * [0]: pitch(UP-DOWN) -89~+89 * [1]: yaw(360) -180~+180 */ /* * OnPlayerRunCmd is Runs 30 times per second. (every 0.03333... seconds) */ public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon) { if(GetTickInterval() < 0.03333) return Plugin_Continue; //Stop running on lag if (g_hEnabled) { if (isSurvivorBot(client) && IsPlayerAlive(client)) { if ((c_iSelectType == 0) || (c_iSelectType >= 1 && g_bFixTarget[client])) { Action ret = Plugin_Continue; ret = onSBRunCmd(client, buttons, vel, angles); if (c_bIncapacitated_Enabled) ret = onSBRunCmd_Incapacitated(client, buttons, vel, angles); ret = onSBSlotActionCmd(client, buttons, vel, angles); return ret; } } } return Plugin_Continue; } /****************************************************************************************************/ /* ================================================================================================ *= *= Weapon Switch *= ================================================================================================ */ public void OnClientPutInServer(int client) { SDKHook(client, SDKHook_WeaponSwitch, WeaponSwitch); } public void OnClientDisconnect(int client) { SDKUnhook(client, SDKHook_WeaponSwitch, WeaponSwitch); } public Action WeaponSwitch(int client, int weapon) { if (!g_hEnabled) return Plugin_Continue; if (!isSurvivor(client) || !IsFakeClient(client) || !IsValidEntity(weapon)) return Plugin_Continue; if (isIncapacitated(client) || GetPlayerWeaponSlot(client, 0) == -1) return Plugin_Continue; static char classname[128]; GetEntityClassname(weapon, classname, sizeof(classname)); if (isHaveItem(classname, "weapon_melee") || isHaveItem(classname, "weapon_pistol") // Includes Magnum ("weapon_pistol_magnum") || isHaveItem(classname, "weapon_dual_pistol")) { if (c_bDontSwitchSecondary) { int slot0 = GetPlayerWeaponSlot(client, 0); int clip, extra_ammo; clip = GetEntProp(slot0, Prop_Send, "m_iClip1"); extra_ammo = PrimaryExtraAmmoCheck(client, slot0); // check //PrintToChatAll("[%N's] clip: %d, extra_ammo: %d", client, clip, extra_ammo); //if (!g_bCommonWithinMelee[client] && (clip != 0 || extra_ammo != 0)) PrintToChatAll("switch Stoped"); if (!g_bCommonWithinMelee[client] && (clip != 0 || extra_ammo != 0)) return Plugin_Handled; } } else if (StrContains(classname, "first_aid_kit", false) > -1 || StrContains(classname, "defibrillator", false) > -1) { if (g_bDanger[client]) return Plugin_Handled; } return Plugin_Continue; } // TODO: optimize it stock Action onSBSlotActionCmd(int client, int &buttons, float vel[3], float angles[3]) { if (!isIncapacitated(client) && GetPlayerWeaponSlot(client, 0) > -1) { int weapon = GetEntDataEnt2(client, g_ActiveWeapon); if (weapon <= 0) return Plugin_Continue; static char classname[32]; GetEntityClassname(weapon, classname, sizeof(classname)); if (StrEqual(classname, "weapon_melee", false) || StrContains(classname, "pistol", false) > -1) { if (!g_bCommonWithinMelee[client]) { static char main_weapon[32]; GetEntityClassname(GetPlayerWeaponSlot(client, 0), main_weapon, sizeof(main_weapon)); FakeClientCommand(client, "use %s", main_weapon); } } else if (StrEqual(classname, "weapon_first_aid_kit", false) || StrContains(classname, "weapon_defibrillator", false)) { if (g_bDanger[client]) { static char main_weapon[32]; GetEntityClassname(GetPlayerWeaponSlot(client, 0), main_weapon, sizeof(main_weapon)); FakeClientCommand(client, "use %s", main_weapon); } } } return Plugin_Continue; } /****************************************************************************************************/ /* ================================================================================================ *= *= SB Run Cmd *= ================================================================================================ */ stock Action onSBRunCmd(int client, int &buttons, float vel[3], float angles[3]) { if (!isIncapacitated(client) && GetEntityMoveType(client) != MOVETYPE_LADDER) { // Find a nearest visible Special Infected int int_target = -1; float min_dist = 100000.0; float self_pos[3], target_pos[3]; if ((c_bSI_Enabled || c_bTank_Enabled) && !NeedsTeammateHelp_ExceptSmoker(client)) { GetClientAbsOrigin(client, self_pos); for (int x = 1; x <= MaxClients; ++x) { if (isInfected(x) && IsPlayerAlive(x) && !isIncapacitated(x) && isVisibleTo(client, x)) { GetClientAbsOrigin(x, target_pos); float dist = GetVectorDistance(self_pos, target_pos, true); int zombieClass = getZombieClass(x); if ((c_bSI_Enabled && zombieClass != ZC_TANK && dist <= c_fSI_Range) || (c_bTank_Enabled && zombieClass == ZC_TANK && dist <= c_fTank_Range)) { if ((c_iSITank_PriorityType == 1 && zombieClass != ZC_TANK) || (c_iSITank_PriorityType == 2 && zombieClass == ZC_TANK)) { if (dist < min_dist) { min_dist = dist; int_target = x; continue; } } if (dist < min_dist) { min_dist = dist; int_target = x; } } } } } int aCap_Survivor = -1; float min_dist_CapSur = 10000000000.0; //org was 100,000, we square here float target_pos_CapSur[3]; int aCap_Infected = -1; float min_dist_CapInf = 10000000000.0; float target_pos_CapInf[3]; if (c_bHelp_Enabled && !NeedsTeammateHelp_ExceptSmoker(client)) { // Find a Survivor who are pinned for (int x = 1; x <= MaxClients; ++x) { if (isSurvivor(x) && NeedsTeammateHelp(x) && (x != client) && (isVisibleTo(client, x) || isVisibleTo(x, client))) { float dist; GetClientAbsOrigin(x, target_pos_CapSur); dist = GetVectorDistance(self_pos, target_pos_CapSur, true); if (dist < c_fHelp_Range) { if (dist < min_dist_CapSur) { min_dist_CapSur = dist; aCap_Survivor = x; } } } } // Find a Special Infected who are pinning for (int x = 1; x <= MaxClients; ++x) { if (isInfected(x) && CappingSuvivor(x) && (isVisibleTo(client, x) || isVisibleTo(x, client))) { float dist; GetClientAbsOrigin(x, target_pos_CapInf); dist = GetVectorDistance(self_pos, target_pos_CapInf, true); if (dist < c_fHelp_Range) { if (dist < min_dist_CapInf) { min_dist_CapInf = dist; aCap_Infected = x; } } } } } /* // Find aCapSmoker int aCapSmoker = -1; float min_dist_CapSmo = 100000.0; float target_pos_CapSmo[3]; for (int x = 1; x <= MaxClients; ++x) { if (isSpecialInfectedBot(x) && IsPlayerAlive(x) && HasValidEnt(x, "m_tongueVictim") && isVisibleTo(int client, x)) { float dist; GetClientAbsOrigin(x, target_pos_CapSmo); dist = GetVectorDistance(self_pos, target_pos_CapSmo); if (dist < 700.0) { if (dist < min_dist_CapSmo) { min_dist_CapSmo = dist; aCapSmoker = x; } } } } */ // Find a Smoker who is tongued self int aCapSmoker = -1; if (c_bPrioritize_OwnerSmoker) { float min_dist_CapSmo = 100000.0; float target_pos_CapSmo[3]; for (int x = 1; x <= MaxClients; ++x) { if (isInfected(x) && IsPlayerAlive(x) && HasValidEnt(x, "m_tongueVictim")) { if (GetEntPropEnt(x, Prop_Send, "m_tongueVictim") == client) { float dist; GetClientAbsOrigin(x, target_pos_CapSmo); dist = GetVectorDistance(self_pos, target_pos_CapSmo, true); if (dist < 750.0) { if (dist < min_dist_CapSmo) { min_dist_CapSmo = dist; aCapSmoker = x; } } } } } } // Find a flying Hunter and Jockey int aHunterJockey = -1; float hunjoc_pos[3]; float min_dist_HunJoc = 100000.0; if (c_bBash_Enabled && !NeedsTeammateHelp_ExceptSmoker(client)) { for (int x = 1; x <= MaxClients; ++x) { if (isInfected(x) && IsPlayerAlive(x) && !isStagger(x) && isVisibleTo(client, x)) { if (getZombieClass(x) == ZC_HUNTER) { if (c_iBash_HunterChance == 100 || (c_iBash_HunterChance < 100 && g_bShove[client][x])) { float hunterVelocity[3]; GetEntDataVector(x, g_Velo, hunterVelocity); if ((GetClientButtons(x) & IN_DUCK) && hunterVelocity[2] != 0.0) { GetClientAbsOrigin(x, hunjoc_pos); float hundist; hundist = GetVectorDistance(self_pos, hunjoc_pos, true); if (hundist < c_fBash_HunterRange) { // 145.0 best if (hundist < min_dist_HunJoc) { min_dist_HunJoc = hundist; aHunterJockey = x; } } } } } else if (getZombieClass(x) == ZC_JOCKEY) { if (c_iBash_JockeyChance == 100 || (c_iBash_JockeyChance < 100 && g_bShove[client][x])) { float jockeyVelocity[3]; GetEntDataVector(x, g_Velo, jockeyVelocity); if (jockeyVelocity[2] != 0.0) { GetClientAbsOrigin(x, hunjoc_pos); float jocdist; jocdist = GetVectorDistance(self_pos, hunjoc_pos, true); if (jocdist < c_fBash_JockeyRange) { // 125.0 best if (jocdist < min_dist_HunJoc) { min_dist_HunJoc = jocdist; aHunterJockey = x; } } } } } } } } // Find a Common Infected //int iMaxEntities = GetMaxEntities(); int aCommonInfected = -1; int iCI_MeleeCount = 0; float min_dist_CI = 10000000000.0; float ci_pos[3]; if (c_bCI_Enabled && !NeedsTeammateHelp(client)) { for (int iEntity = MaxClients+1; iEntity <= MAXENTITIES; ++iEntity) { if (IsCommonInfected(iEntity) && GetEntProp(iEntity, Prop_Data, "m_iHealth") > 0 && isVisibleToEntity(iEntity, client)) { float dist; GetEntPropVector(iEntity, Prop_Data, "m_vecAbsOrigin", ci_pos); dist = GetVectorDistance(self_pos, ci_pos, true); if (dist < c_fCI_Range) { int iSeq = GetEntProp(iEntity, Prop_Send, "m_nSequence", 2); // Stagger 122, 123, 126, 127, 128, 133, 134 // Down Stagger 128, 129, 130, 131 // Object Climb (Very Low) 182, 183, 184, 185 // Object Climb (Low) 190, 191, 192, 193, 194, 195, 196, 197, 198, 199 // Object Climb (High) 206, 207, 208, 209, 210, 211, 218, 219, 220, 221, 222, 223 if ((iSeq <= 121) || (iSeq >= 135 && iSeq <= 189) || (iSeq >= 200 && iSeq <= 205) || (iSeq >= 224)) { if (dist < min_dist_CI) { min_dist_CI = dist; aCommonInfected = iEntity; } } } if (dist <= c_fCI_MeleeRange) { // ��낯�ĂĂ� MeleeCount �ɂ͓���� iCI_MeleeCount += 1; } } } } // Fina a rage Witch int aWitch = -1; float min_dist_Witch = 10000000000.0; float witch_pos[3]; if (g_bWitchActive && c_bWitch_Enabled && !NeedsTeammateHelp(client)) { for (int iEntity = MaxClients+1; iEntity <= MAXENTITIES; ++iEntity) { if (IsWitch(iEntity) && GetEntProp(iEntity, Prop_Data, "m_iHealth") > 0 && IsWitchRage(iEntity) && isVisibleToEntity(iEntity, client)) { float witch_dist; GetEntPropVector(iEntity, Prop_Data, "m_vecAbsOrigin", witch_pos); witch_dist = GetVectorDistance(self_pos, witch_pos, true); if ((g_iWitch_Process[iEntity] == 0 && witch_dist < c_fWitch_Range) || (g_iWitch_Process[iEntity] == WITCH_INCAPACITATED && witch_dist < c_fWitch_Range_Incapacitated) || (g_iWitch_Process[iEntity] == WITCH_KILLED && witch_dist < c_fWitch_Range_Killed)) { if (witch_dist < min_dist_Witch) { min_dist_Witch = witch_dist; aWitch = iEntity; } } } } } // Find a tank rock int aTankRock = -1; float rock_min_dist = 10000000000.0; float rock_pos[3]; if (c_bRock_Enabled && !NeedsTeammateHelp(client)) { for (int iEntity = MaxClients+1; iEntity <= MAXENTITIES; ++iEntity) { if (IsTankRock(iEntity) && isVisibleToEntity(iEntity, client)) { float rock_dist; GetEntPropVector(iEntity, Prop_Data, "m_vecAbsOrigin", rock_pos); rock_dist = GetVectorDistance(self_pos, rock_pos, true); if (rock_dist < c_fRock_Range) { if (rock_dist < rock_min_dist) { rock_min_dist = rock_dist; aTankRock = iEntity; } } } } } /* -------------------------------------------------------------------------------------------------------------------------------------------------------------- ***************************** * Get The Weapon * ***************************** --------------------------------------------------------------------------------------------------------------------------------------------------------------- */ int weapon = GetEntDataEnt2(client, g_ActiveWeapon); static char AW_Classname[32]; if (weapon > MAXPLAYERS) GetEntityClassname(weapon, AW_Classname, sizeof(AW_Classname)); // Exception reported: Entity -1 (-1) is invalid static char main_weapon[32]; int slot0 = GetPlayerWeaponSlot(client, 0); if (slot0 > -1) { GetEntityClassname(slot0, main_weapon, sizeof(main_weapon)); } /* -------------------------------------------------------------------------------------------------------------------------------------------------------------- ********************** * Action * ********************** --------------------------------------------------------------------------------------------------------------------------------------------------------------- */ /* ==================================================================================================== * * Other Adjustment * ==================================================================================================== */ if (g_bDanger[client]) { // If have the medkit even though it is dangerous, switch to the main weapon if (isHaveItem(AW_Classname, "weapon_first_aid_kit")) { if (main_weapon[1] != 0) { FakeClientCommand(client, "use %s", main_weapon); } else { static char sub_weapon[32]; int slot1 = GetPlayerWeaponSlot(client, 1); if (slot1 > -1) { GetEntityClassname(slot1, sub_weapon, sizeof(sub_weapon)); // SubWeapon } FakeClientCommand(client, "use %s", main_weapon); } } } if (g_bCommonWithinMelee[client]) { if (aCommonInfected < 1) g_bCommonWithinMelee[client] = false; if (aCommonInfected > 0) { float c_pos[3], common_e_pos[3]; GetClientAbsOrigin(client, c_pos); GetEntPropVector(aCommonInfected, Prop_Data, "m_vecOrigin", common_e_pos); float aimdist = GetVectorDistance(c_pos, common_e_pos, true); if (aimdist > c_fCI_MeleeRange) g_bCommonWithinMelee[client] = false; } } /* ==================================================================================================== * * �D��xA : Bash | flying Hunter, Jockey * ==================================================================================================== */ if (aHunterJockey > 0) { if (!g_bDanger[client]) g_bDanger[client] = true; float c_pos[3], e_pos[3]; float lookat[3]; GetClientAbsOrigin(client, c_pos); GetClientAbsOrigin(aHunterJockey, e_pos); e_pos[2] += -10.0; MakeVectorFromPoints(c_pos, e_pos, lookat); GetVectorAngles(lookat, angles); TeleportEntity(client, NULL_VECTOR, angles, NULL_VECTOR); buttons |= IN_ATTACK2; if (c_bDebug_Enabled) { PrintToChatAll("\x01[%.2f] \x05%N \x01shoved: \x04flying %N (%d)", GetGameTime(), client, aHunterJockey, aHunterJockey); EmitSoundToAll(SOUND_SWING, client); } return Plugin_Changed; } /* ==================================================================================================== * * �D��xB : Self Smoker | aCapSmoker * ==================================================================================================== */ if (aCapSmoker > 0) { // Shoot even if client invisible the smoker if (!g_bDanger[client]) g_bDanger[client] = true; float c_pos[3], e_pos[3]; float lookat[3]; GetClientAbsOrigin(client, c_pos); GetEntPropVector(aCapSmoker, Prop_Data, "m_vecOrigin", e_pos); e_pos[2] += 5.0; //PrintToChatAll("c_pos[0] %.1f | [1] %.1f | [2] %.1f", c_pos[0], c_pos[1], c_pos[2]); //PrintToChatAll("e_pos[0] %.1f | [1] %.1f | [2] %.1f", e_pos[0], e_pos[1], e_pos[2]); // GetClientEyePosition(client, c_pos); // GetClientEyePosition(aCapSmoker, e_pos); // e_pos[2] += -10.0; MakeVectorFromPoints(c_pos, e_pos, lookat); GetVectorAngles(lookat, angles); if (c_bDebug_Enabled) PrintToChatAll("\x01[%.2f] \x05%N \x01Cap Smoker: \x04%N (%d)", GetGameTime(), client, aCapSmoker, aCapSmoker); TeleportEntity(client, NULL_VECTOR, angles, NULL_VECTOR); float aimdist = GetVectorDistance(c_pos, e_pos, true); if (aimdist < 100.0) buttons |= IN_ATTACK2; else { buttons &= ~IN_ATTACK2; buttons |= IN_DUCK; } if (GetRandomInt(0, 4) == 0) buttons &= ~IN_ATTACK; else buttons |= IN_ATTACK; return Plugin_Changed; } /* ==================================================================================================== * * �D��xC : Help | aCap_Infected, aCap_Survivor * ==================================================================================================== */ if (aCap_Survivor > 0) { // Pass if the client and target are "visible" to each other. so aCap Smoker doesn't pass if (!g_bDanger[client]) g_bDanger[client] = true; float c_pos[3], e_pos[3]; float lookat[3]; GetClientEyePosition(client, c_pos); GetClientEyePosition(aCap_Survivor, e_pos); if (HasValidEnt(aCap_Survivor, "m_pounceAttacker")) e_pos[2] += 5.0; else if (aCapSmoker > 0) { // ���������Ă���Smoker GetClientEyePosition(aCapSmoker, e_pos); e_pos[2] += -10.0; } float aimdist = GetVectorDistance(c_pos, e_pos, true); MakeVectorFromPoints(c_pos, e_pos, lookat); GetVectorAngles(lookat, angles); if (c_bDebug_Enabled) PrintToChatAll("\x01[%.2f] \x05%N \x01Cap Survivor: \x04%N (%d)", GetGameTime(), client, aCap_Survivor, aCap_Survivor); /****************************************************************************************************/ // If any of the following are active, Switch to the main weapon if (isHaveItem(AW_Classname, "weapon_first_aid_kit") || isHaveItem(AW_Classname, "weapon_defibrillator") || HasValidEnt(client, "m_reviveTarget")) { UseItem(client, main_weapon); } // If the melee weapon is active and the dist from the target is 110 or more, switch to the main weapon if (isHaveItem(AW_Classname, "weapon_melee") && aimdist > 11000.0) { if (g_bCommonWithinMelee[client]) g_bCommonWithinMelee[client] = false; UseItem(client, main_weapon); } /****************************************************************************************************/ if (!isHaveItem(AW_Classname, "weapon_melee") || aimdist < 11000.0) { TeleportEntity(client, NULL_VECTOR, angles, NULL_VECTOR); if (((c_iHelp_ShoveType >= 1 && HasValidEnt(aCap_Survivor, "m_tongueOwner") && aimdist < 12100.0) || (c_iHelp_ShoveType >= 2 && HasValidEnt(aCap_Survivor, "m_jockeyAttacker") && aimdist < 10000.0) || (c_iHelp_ShoveType >= 3 && HasValidEnt(aCap_Survivor, "m_pounceAttacker") && aimdist < 10000.0))) { if ((!c_bHelp_ShoveOnlyReloading) || (c_bHelp_ShoveOnlyReloading && isReloading(client))) buttons |= IN_ATTACK2; // ���� } if (GetRandomInt(0, 4) == 0) buttons &= ~IN_ATTACK; else buttons |= IN_ATTACK; return Plugin_Changed; } } else if (aCap_Infected > 0 && aCap_Survivor < 1) { if (!g_bDanger[client]) g_bDanger[client] = true; int zombieClass = getZombieClass(aCap_Infected); float c_pos[3], e_pos[3]; float lookat[3]; GetClientEyePosition(client, c_pos); if (aCapSmoker > 0) { // Prioritize aCapSmoker GetClientEyePosition(aCapSmoker, e_pos); e_pos[2] += -10.0; } else { GetClientEyePosition(aCap_Infected, e_pos); if (zombieClass == ZC_SMOKER || zombieClass == ZC_CHARGER) e_pos[2] += -9.0; else if (zombieClass == ZC_HUNTER) e_pos[2] += -14.0; } float aimdist = GetVectorDistance(c_pos, e_pos, true); if (zombieClass == ZC_CHARGER && aimdist < 300.0) e_pos[2] += 10.0; MakeVectorFromPoints(c_pos, e_pos, lookat); GetVectorAngles(lookat, angles); if (c_bDebug_Enabled) PrintToChatAll("\x01[%.2f] \x05%N \x01Cap Infected: \x04%N (%d)", GetGameTime(), client, aCap_Infected, aCap_Infected); /****************************************************************************************************/ // If any of the following are active, Switch to the main weapon if (isHaveItem(AW_Classname, "weapon_first_aid_kit") || isHaveItem(AW_Classname, "weapon_defibrillator") || HasValidEnt(client, "m_reviveTarget")) { UseItem(client, main_weapon); } // If the melee weapon is active and the dist from the target is 110 or more, switch to the main weapon if (isHaveItem(AW_Classname, "weapon_melee") && aimdist > 11000.0) { if (g_bCommonWithinMelee[client]) g_bCommonWithinMelee[client] = false; UseItem(client, main_weapon); } /****************************************************************************************************/ if (!isHaveItem(AW_Classname, "weapon_melee") || aimdist < 11000.0) { TeleportEntity(client, NULL_VECTOR, angles, NULL_VECTOR); if (aimdist < 10000.0 && ((c_iHelp_ShoveType >= 1 && zombieClass == ZC_SMOKER) || (c_iHelp_ShoveType >= 2 && zombieClass == ZC_JOCKEY) || (c_iHelp_ShoveType >= 3 && zombieClass == ZC_HUNTER))) { if ((!c_bHelp_ShoveOnlyReloading) || (c_bHelp_ShoveOnlyReloading && isReloading(client))) buttons |= IN_ATTACK2; // Shove } if (GetRandomInt(0, 4) == 0) buttons &= ~IN_ATTACK; else buttons |= IN_ATTACK; return Plugin_Changed; } } /* ==================================================================================================== * * �D��xD : Tank Rock, Witch * ==================================================================================================== */ if (aTankRock > 1 && !HasValidEnt(client, "m_reviveTarget")) { float c_pos[3], rock_e_pos[3]; float lookat[3]; GetClientAbsOrigin(client, c_pos); GetEntPropVector(aTankRock, Prop_Data, "m_vecAbsOrigin", rock_e_pos); rock_e_pos[2] += -50.0; MakeVectorFromPoints(c_pos, rock_e_pos, lookat); GetVectorAngles(lookat, angles); if (c_bDebug_Enabled) { // PrintToChatAll("\x01rock : \x01[0] - \x04%.2f \x01, [1] - \x04%.2f \x01, [2] - \x04%.2f", rock_e_pos[0], rock_e_pos[1], rock_e_pos[2]); // PrintToChatAll("\x01client(%N) : \x01[0] - \x04%.2f \x01, [1] - \x04%.2f \x01, [2] - \x04%.2f", client, c_pos[0], c_pos[1], c_pos[2]); // PrintToChatAll("---"); } float aimdist = GetVectorDistance(c_pos, rock_e_pos, true); if (aimdist > 1600.0 && !isHaveItem(AW_Classname, "weapon_melee")) { //�ߐڂ������Ă��Ȃ��ꍇ TeleportEntity(client, NULL_VECTOR, angles, NULL_VECTOR); if (GetRandomInt(0, 4) == 0) buttons &= ~IN_ATTACK; else buttons |= IN_ATTACK; } return Plugin_Changed; } if (aWitch > 1) { float c_pos[3], witch_e_pos[3]; float lookat[3]; GetClientEyePosition(client, c_pos); GetEntPropVector(aWitch, Prop_Data, "m_vecAbsOrigin", witch_e_pos); witch_e_pos[2] += 40.0; MakeVectorFromPoints(c_pos, witch_e_pos, lookat); GetVectorAngles(lookat, angles); if (c_bDebug_Enabled) PrintToChatAll("\x01[%.2f] \x05%N \x01Witch: \x05(%d)", GetGameTime(), client, aWitch); TeleportEntity(client, NULL_VECTOR, angles, NULL_VECTOR); float aimdist = GetVectorDistance(c_pos, witch_e_pos, true); if (c_bWitch_Shotgun_Control && StrContains(AW_Classname, "shotgun") != -1) { if (aimdist < 22500.0) buttons |= IN_DUCK; if (aimdist < c_fWitch_Shotgun_Range_Min || aimdist > c_fWitch_Shotgun_Range_Max) { // 70 ~ 300 if (GetRandomInt(0, 4) == 0) buttons &= ~IN_ATTACK; else buttons |= IN_ATTACK; //PrintToChatAll("\x05%N %.2f", client, aimdist); } else { buttons &= ~IN_ATTACK; //PrintToChatAll("\x04%N Attack Stop %.2f", client, aimdist); } return Plugin_Changed; } if (GetRandomInt(0, 4) == 0) buttons &= ~IN_ATTACK; else buttons |= IN_ATTACK; return Plugin_Changed; } /* ==================================================================================================== * * �D��xE : Common Infected * ==================================================================================================== */ if (aCommonInfected > 0) { if (!HasValidEnt(client, "m_reviveTarget") && StrEqual(AW_Classname, "weapon_first_aid_kit", false)) { // Even if aCommonInfected dies and disappears, the Entity may not disappear for a while.(Bot keeps shooting the place)�B Even with InValidEntity(), true appears... // When the entity disappears, m_nNextThinkTick will not advance, so skip that if NextThinkTick has the same value as before. int iNextThinkTick = GetEntProp(aCommonInfected, Prop_Data, "m_nNextThinkTick"); if (g_Stock_NextThinkTick[client] != iNextThinkTick) // If visible aCommonInfected { float c_pos[3], common_e_pos[3]; float lookat[3]; GetClientEyePosition(client, c_pos); GetEntPropVector(aCommonInfected, Prop_Data, "m_vecOrigin", common_e_pos); //float height_difference = (c_pos[2] - common_e_pos[2]) - 60.0; common_e_pos[2] += 40.0; float aimdist = GetVectorDistance(c_pos, common_e_pos, true); //common_e_pos[2] += (25.0 + (aimdist * 0.05) - (height_difference * 0.1)); // GetClientAbsOrigin(client, c_pos); // GetEntPropVector(aCommonInfected, Prop_Data, "m_vecOrigin", common_e_pos); // common_e_pos[2] += -30.0; int iSeq = GetEntProp(aCommonInfected, Prop_Send, "m_nSequence", 2); // Stagger 122, 123, 126, 127, 128, 133, 134 // Down Stagger 128, 129, 130, 131 // Object Climb (Very Low) 182, 183, 184, 185 // Object Climb (Low) 190, 191, 192, 193, 194, 195, 196, 197, 198, 199 // Object Climb (High) 206, 207, 208, 209, 210, 211, 218, 219, 220, 221, 222, 223 if (iSeq >= 182 && iSeq <= 189) common_e_pos[2] += -10.0; MakeVectorFromPoints(c_pos, common_e_pos, lookat); GetVectorAngles(lookat, angles); /****************************************************************************************************/ g_Stock_NextThinkTick[client] = iNextThinkTick; // Set the current m_nNextThinkTick if (c_bDebug_Enabled) PrintToChatAll("\x01[%.2f] \x05%N\x01 Commons: \x04(%d)\x01 | Dist: \x04%.1f\x01 | Melee Count: \x04%d", GetGameTime(), client, aCommonInfected, aimdist, iCI_MeleeCount); // iCI_MeleeCount is from ci_melee_range if (c_bCI_MeleeEnabled && aimdist <= c_fCI_MeleeRange && iCI_MeleeCount > 2) { g_bCommonWithinMelee[client] = true; static char sub_weapon[16]; int slot1 = GetPlayerWeaponSlot(client, 1); if (slot1 > -1) { GetEntityClassname(slot1, sub_weapon, sizeof(sub_weapon)); // SubWeapon } if (isHaveItem(sub_weapon, "weapon_melee")) { if (!isHaveItem(AW_Classname, "weapon_melee")) { FakeClientCommand(client, "use %s", sub_weapon); } } } if (int_target > 0) { if (aimdist <= 90.0) TeleportEntity(client, NULL_VECTOR, angles, NULL_VECTOR); } else { if (isHaveItem(AW_Classname, "weapon_melee")) { if (aimdist <= 90.0) TeleportEntity(client, NULL_VECTOR, angles, NULL_VECTOR); } else { TeleportEntity(client, NULL_VECTOR, angles, NULL_VECTOR); } } if (int_target < 1 || (int_target > 0 && aimdist <= 90.0)) { // If int_target and common at the same time, prioritize to int_target. Attack only when within 90.0 dist. if (isHaveItem(AW_Classname, "weapon_melee")) { if (GetRandomInt(0, 6) == 0) { if (aimdist <= 50.0) buttons |= IN_ATTACK2; else if (aimdist > 50.0 && aimdist <= 90.0) buttons |= IN_ATTACK; } else { if (aimdist <= 90.0) buttons |= IN_ATTACK; // 90.0 } // if (GetRandomInt(0, 6) == 0) { // if (aimdist < 50.0) { // buttons |= IN_ATTACK2; // } // } else { // if (aimdist < 90.0) buttons |= IN_ATTACK; // } } else { if (aimdist > 3600.0) { if (GetRandomInt(0, 4) == 0) buttons &= ~IN_ATTACK; else buttons |= IN_ATTACK; } else { if (GetRandomInt(0, 8) == 0) { if (GetRandomInt(0, 4) == 0) buttons &= ~IN_ATTACK; else buttons |= IN_ATTACK; } else { buttons |= IN_ATTACK2; } if (isReloading(client)) { if (GetRandomInt(0, 4) == 0) buttons &= ~IN_ATTACK2; else buttons |= IN_ATTACK2; } } } return Plugin_Changed; } } else // Skip if aCommonInfected is not visible { // PrintToChatAll("stock %i | next %i", g_Stock_NextThinkTick[client], iNextThinkTick); } } } /* ==================================================================================================== * * �D��xF : Special Infected and Tank (int_target) * ==================================================================================================== */ if (int_target > 0) { float c_pos[3], e_pos[3]; float lookat[3]; GetClientAbsOrigin(client, c_pos); int zombieClass = getZombieClass(int_target); if (aCapSmoker > 0) { // Prioritize aCapSmoker GetClientAbsOrigin(aCapSmoker, e_pos); e_pos[2] += -10.0; } else { GetClientAbsOrigin(int_target, e_pos); if (zombieClass == ZC_HUNTER && (GetClientButtons(int_target) & IN_DUCK)) { if (GetVectorDistance(c_pos, e_pos, true) > 6250.0) e_pos[2] += -30.0; else e_pos[2] += -35.0; } else if (zombieClass == ZC_JOCKEY) { e_pos[2] += -30.0; } else { e_pos[2] += -10.0; } } if (zombieClass == ZC_TANK && aTankRock > 0) return Plugin_Continue; // If the Tank and tank rock are visible at the same time, prioritize the tank rock float aimdist = GetVectorDistance(c_pos, e_pos, true); if (aimdist < 40000.0) {if (!g_bDanger[client]) g_bDanger[client] = true;} MakeVectorFromPoints(c_pos, e_pos, lookat); GetVectorAngles(lookat, angles); /****************************************************************************************************/ if(isHaveItem(AW_Classname, "first_aid_kit") || isHaveItem(AW_Classname, "defibrillator") || HasValidEnt(client, "m_reviveTarget")) { if (aimdist > 6250.0) return Plugin_Continue; else { UseItem(client, main_weapon); } } if (isHaveItem(AW_Classname, "weapon_shotgun_chrome") || isHaveItem(AW_Classname, "weapon_shotgun_spas") || isHaveItem(AW_Classname, "weapon_pumpshotgun") || isHaveItem(AW_Classname, "weapon_autoshotgun")) { if (aimdist > 1000000.0) return Plugin_Continue; } if (isHaveItem(AW_Classname, "weapon_melee") && aCommonInfected < 1) { if (aimdist > 1000000.0) UseItem(client, main_weapon); } /****************************************************************************************************/ bool isTargetBoomer = false; bool isBoomer_Shoot_OK = false; if (c_bSI_IgnoreBoomer && zombieClass == ZC_BOOMER) { float voS_pos[3]; for (int s = 1; s <= MaxClients; ++s) { if (isSurvivor(s) && IsPlayerAlive(s)) { float fVomit = GetEntPropFloat(s, Prop_Send, "m_vomitStart"); if (GetGameTime() - fVomit > 10.0) { // Survivors without vomit GetClientAbsOrigin(s, voS_pos); float dist = GetVectorDistance(voS_pos, e_pos, true); // Distance between the Survivor without vomit and the Boomer if (dist >= c_fSI_IgnoreBoomerRange) { isBoomer_Shoot_OK = true; } // If the survivor without vomit is farther than dist "c_fSI_IgnoreBoomerRange (def: 200)" else { isBoomer_Shoot_OK = false; break; } // If False appears even once, break } } } isTargetBoomer = true; } if ((zombieClass == ZC_JOCKEY && g_bShove[client][int_target]) || zombieClass == ZC_SMOKER || (isTargetBoomer && !isBoomer_Shoot_OK)) { if (aimdist < 8100.0 && !isStagger(int_target)) { TeleportEntity(client, NULL_VECTOR, angles, NULL_VECTOR); buttons |= IN_ATTACK2; if (c_bDebug_Enabled) { PrintToChatAll("\x01[%.2f] \x05%N\x01 int_target shoved: \x04%N (%d)", GetGameTime(), client, int_target, int_target); EmitSoundToAll(SOUND_SWING, client); } return Plugin_Changed; } } if (!isHaveItem(AW_Classname, "weapon_melee") || aimdist < 10000.0) { if (c_bDebug_Enabled) { if (!isTargetBoomer) PrintToChatAll("\x01[%.2f] \x05%N\x01 int_target: \x04%N (%d)", GetGameTime(), client, int_target, int_target); else PrintToChatAll("\x01[%.2f] \x05%N\x01 int_target: \x04%N (%d) (Shoot: %s)", GetGameTime(), client, int_target, int_target, (isBoomer_Shoot_OK) ? "OK" : "NO"); } if (!isTargetBoomer || (isTargetBoomer && isBoomer_Shoot_OK)) { TeleportEntity(client, NULL_VECTOR, angles, NULL_VECTOR); if (GetRandomInt(0, 4) == 0) buttons &= ~IN_ATTACK; else buttons |= IN_ATTACK; } return Plugin_Changed; } } // if there is no danger, false if (g_bDanger[client]) g_bDanger[client] = false; } return Plugin_Continue; } /* ================================================================================================ *= *= Incapacitated Run Cmd *= ================================================================================================ */ stock Action onSBRunCmd_Incapacitated(int client, int &buttons, float vel[3], float angles[3]) { if (isIncapacitated(client)) { int aCapper = -1; float min_dist_Cap = 10000000000.0; float self_pos[3], target_pos[3]; GetClientEyePosition(client, self_pos); if (!NeedsTeammateHelp(client)) { for (int x = 1; x <= MaxClients; ++x) { // �S������Ă��鐶���҂�T�� if (isSurvivor(x) && NeedsTeammateHelp(x) && (x != client) && (isVisibleTo(client, x) || isVisibleTo(x, client))) { GetClientAbsOrigin(x, target_pos); float dist = GetVectorDistance(self_pos, target_pos, true); if (dist < min_dist_Cap) { min_dist_Cap = dist; aCapper = x; } } // �S�����Ă�����ꊴ���҂�T�� if (isInfected(x) && CappingSuvivor(x) && (isVisibleTo(client, x) || isVisibleTo(x, client))) { GetClientAbsOrigin(x, target_pos); float dist = GetVectorDistance(self_pos, target_pos, true); if (dist < min_dist_Cap) { min_dist_Cap = dist; aCapper = x; } } } } if (aCapper > 0) { float c_pos[3], e_pos[3]; float lookat[3]; GetClientEyePosition(client, c_pos); GetClientEyePosition(aCapper, e_pos); e_pos[2] += -15.0; if ((isSurvivor(aCapper) && HasValidEnt(aCapper, "m_pounceAttacker"))) { e_pos[2] += 18.0; // Raise angles if near } if ((isInfected(aCapper) && getZombieClass(aCapper) == ZC_HUNTER)) { e_pos[2] += -15.0; } MakeVectorFromPoints(c_pos, e_pos, lookat); GetVectorAngles(lookat, angles); if (c_bDebug_Enabled) { if (isSurvivor(aCapper)) PrintToChatAll("\x01[%.2f] \x05%N \x01Cap Survivor Incapacitated: \x04%N", GetGameTime(), client, aCapper); else PrintToChatAll("\x01[%.2f] \x05%N \x01Cap Infected Incapacitated: \x04%N", GetGameTime(), client, aCapper); } TeleportEntity(client, NULL_VECTOR, angles, NULL_VECTOR); if (GetRandomInt(0, 4) == 0) buttons &= ~IN_ATTACK; else buttons |= IN_ATTACK; return Plugin_Changed; } int int_target = -1; int aCommonInfected = -1; if (aCapper < 1 && !NeedsTeammateHelp(client)) { float min_dist = 10000000000.0; float ci_pos[3]; for (int x = 1; x <= MaxClients; ++x){ if (isInfected(x) && IsPlayerAlive(x) && (isVisibleTo(client, x) || isVisibleTo(x, client))) { GetClientAbsOrigin(x, target_pos); float dist = GetVectorDistance(self_pos, target_pos, true); if (dist < min_dist) { min_dist = dist; int_target = x; aCommonInfected = -1; } } } if (c_bCI_Enabled) { for (int iEntity = MaxClients+1; iEntity <= MAXENTITIES; ++iEntity) { if (IsCommonInfected(iEntity) && GetEntProp(iEntity, Prop_Data, "m_iHealth") > 0 && isVisibleToEntity(iEntity, client)) { GetEntPropVector(iEntity, Prop_Data, "m_vecAbsOrigin", ci_pos); float dist = GetVectorDistance(self_pos, ci_pos, true); if (dist < min_dist) { min_dist = dist; aCommonInfected = iEntity; int_target = -1; } } } } } if (aCommonInfected > 0) { float c_pos[3], common_e_pos[3]; float lookat[3]; GetClientEyePosition(client, c_pos); GetEntPropVector(aCommonInfected, Prop_Data, "m_vecOrigin", common_e_pos); common_e_pos[2] += 35.0; MakeVectorFromPoints(c_pos, common_e_pos, lookat); GetVectorAngles(lookat, angles); float aimdist = GetVectorDistance(c_pos, common_e_pos, true); /****************************************************************************************************/ if (c_bDebug_Enabled) PrintToChatAll("\x01[%.2f] \x05%N\x01 Commons Incapacitated Dist: %.1f", GetGameTime(), client, aimdist); TeleportEntity(client, NULL_VECTOR, angles, NULL_VECTOR); if (GetRandomInt(0, 4) == 0) buttons &= ~IN_ATTACK; else buttons |= IN_ATTACK; return Plugin_Changed; } if (int_target > 0) { float c_pos[3], e_pos[3]; float lookat[3]; GetClientEyePosition(client, c_pos); GetClientEyePosition(int_target, e_pos); e_pos[2] += -15.0 int zombieClass = getZombieClass(int_target); if (zombieClass == ZC_JOCKEY) { e_pos[2] += -30.0; } else if (zombieClass == ZC_HUNTER) { if ((GetClientButtons(int_target) & IN_DUCK) || HasValidEnt(int_target, "m_pounceVictim")) e_pos[2] += -25.0; } MakeVectorFromPoints(c_pos, e_pos, lookat); GetVectorAngles(lookat, angles); if (c_bDebug_Enabled) PrintToChatAll("\x01[%.2f] \x05%N \x01int target Incapacitated: \x04%N", GetGameTime(), client, int_target); TeleportEntity(client, NULL_VECTOR, angles, NULL_VECTOR); if (GetRandomInt(0, 4) == 0) buttons &= ~IN_ATTACK; else buttons |= IN_ATTACK; return Plugin_Changed; } } return Plugin_Continue; } /* ================================================================================================ *= *= Events *= ================================================================================================ */ public Action Event_PlayerIncapacitated(Handle event, const char[] name, bool dontBroadcast) { if (!g_hEnabled) return Plugin_Handled; int victim = GetClientOfUserId(GetEventInt(event, "userid")); int attackerentid = GetEventInt(event, "attackerentid"); // int type = GetEventInt(event, "type"); // PrintToChatAll("\x04PlayerIncapacitated"); // PrintToChatAll("type %i", type); if (isSurvivor(victim) && IsWitch(attackerentid)) { g_iWitch_Process[attackerentid] = WITCH_INCAPACITATED; // PrintToChatAll("attackerentid %i attacked %N", attackerentid, victim); // int health = GetEventInt(event, "health"); // int dmg_health = GetEventInt(event, "dmg_health"); // PrintToChatAll("health: %i, damage: %i", health, dmg_health); } return Plugin_Handled; } public Action Event_PlayerDeath(Handle event, const char[] name, bool dontBroadcast) { if (!g_hEnabled) return Plugin_Handled; int victim = GetClientOfUserId(GetEventInt(event, "userid")); int attackerentid = GetEventInt(event, "attackerentid"); // int type = GetEventInt(event, "type"); // PrintToChatAll("\x04PlayerDeath"); // PrintToChatAll("type %i", type); if (isSurvivor(victim) && IsWitch(attackerentid)) { g_iWitch_Process[attackerentid] = WITCH_KILLED; // PrintToChatAll("attackerentid %i attacked %N", attackerentid, victim); // int health = GetEventInt(event, "health"); // int dmg_health = GetEventInt(event, "dmg_health"); // PrintToChatAll("health: %i, damage: %i", health, dmg_health); } // Witch Damage type: 4 // Witch Incapacitated type: 32772 return Plugin_Handled; } public void Event_WitchRage(Handle event, const char[] name, bool dontBroadcast) { int attacker = GetClientOfUserId(GetEventInt(event, "userid")); if (isSurvivor(attacker)) { // CallBotstoWitch(attacker); g_bWitchActive = true; } } public void OnEntityCreated(int entity, const char[] classname) { if (entity >= MaxClients && g_hEnabled && strcmp(classname, "witch") == 0) { g_iWitch_Process[entity] = 0; } } public void OnEntityDestroyed(int entity) { if(entity == -1) return; static char classname[32]; GetEntityClassname(entity, classname, sizeof(classname)); if (StrEqual(classname, "witch", false)) { if (g_bWitchActive) { int iWitch_Count = 0; for (int iEntity = MaxClients+1; iEntity <= MAXENTITIES; ++iEntity) { if (IsWitch(iEntity) && GetEntProp(iEntity, Prop_Data, "m_iHealth") > 0 && IsWitchRage(iEntity)) { iWitch_Count++; } //PrintToChatAll("witch count %d", iWitch_Count); if (iWitch_Count == 0) {g_bWitchActive = false;} } } } } /* ================================================================================================ *= *= Stock any *= ================================================================================================ */ stock void ScriptCommand(int client, const char[] command, const char[] arguments, any ...) { static char vscript[PLATFORM_MAX_PATH]; VFormat(vscript, sizeof(vscript), arguments, 4); int flags = GetCommandFlags(command); SetCommandFlags(command, flags & ~FCVAR_CHEAT); FakeClientCommand(client, "%s %s", command, vscript); SetCommandFlags(command, flags); } stock void L4D2_RunScript(const char[] sCode, any ...) { static int iScriptLogic = INVALID_ENT_REFERENCE; if(iScriptLogic == INVALID_ENT_REFERENCE || !IsValidEntity(iScriptLogic)) { iScriptLogic = EntIndexToEntRef(CreateEntityByName("logic_script")); if(iScriptLogic == INVALID_ENT_REFERENCE || !IsValidEntity(iScriptLogic)) SetFailState("Could not create 'logic_script'"); DispatchSpawn(iScriptLogic); } static char sBuffer[512]; VFormat(sBuffer, sizeof(sBuffer), sCode, 2); SetVariantString(sBuffer); AcceptEntityInput(iScriptLogic, "RunScriptCode"); } /* * * Bool * */ stock bool NeedsTeammateHelp(int client) { if (HasValidEnt(client, "m_tongueOwner") || HasValidEnt(client, "m_pounceAttacker") || HasValidEnt(client, "m_jockeyAttacker") || HasValidEnt(client, "m_carryAttacker") || HasValidEnt(client, "m_pummelAttacker")) { return true; } return false; } stock bool NeedsTeammateHelp_ExceptSmoker(int client) { if (HasValidEnt(client, "m_pounceAttacker") || HasValidEnt(client, "m_jockeyAttacker") || HasValidEnt(client, "m_carryAttacker") || HasValidEnt(client, "m_pummelAttacker")) { return true; } return false; } stock bool CappingSuvivor(int client) { if (HasValidEnt(client, "m_tongueVictim") || HasValidEnt(client, "m_pounceVictim") || HasValidEnt(client, "m_jockeyVictim") || HasValidEnt(client, "m_carryVictim") || HasValidEnt(client, "m_pummelVictim")) { return true; } return false; } stock bool HasValidEnt(int client, const char[] entprop) { int ent = GetEntPropEnt(client, Prop_Send, entprop); return (ent > 0 && IsClientInGame(ent)); } stock bool IsWitchRage(int id) { if (GetEntPropFloat(id, Prop_Send, "m_rage") >= 1.0) return true; return false; } stock bool IsCommonInfected(int iEntity) { if (iEntity && IsValidEntity(iEntity)) { static char strClassName[16]; GetEntityClassname(iEntity, strClassName, sizeof(strClassName)); if (strcmp(strClassName[7], "infected", false) == 0) return true; } return false; } stock bool IsWitch(int iEntity) { if (iEntity && IsValidEntity(iEntity)) { static char strClassName[8]; GetEntityClassname(iEntity, strClassName, sizeof(strClassName)); if (StrEqual(strClassName, "witch")) return true; } return false; } stock bool IsTankRock(int iEntity) { if (iEntity && IsValidEntity(iEntity)) { static char strClassName[16]; GetEntityClassname(iEntity, strClassName, sizeof(strClassName)); if (StrEqual(strClassName, "tank_rock")) return true; } return false; } stock bool isGhost(int i) { return GetEntProp(i, Prop_Send, "m_isGhost") != 0; } stock bool isSpecialInfectedBot(int i) { return i > 0 && i <= MaxClients && IsClientInGame(i) && IsFakeClient(i) && GetClientTeam(i) == 3; } stock bool isSurvivorBot(int i) { return isSurvivor(i) && IsFakeClient(i); } stock bool isInfected(int i) { return i > 0 && i <= MaxClients && IsClientInGame(i) && GetClientTeam(i) == 3 && !isGhost(i); } stock bool isSurvivor(int i) { return i > 0 && i <= MaxClients && IsClientInGame(i) && GetClientTeam(i) == 2; } stock any getZombieClass(int client) { return GetEntProp(client, Prop_Send, "m_zombieClass"); } stock bool isIncapacitated(int client) { return GetEntProp(client, Prop_Send, "m_isIncapacitated", 1) == 1; } stock bool isReloading(int client) { int slot0 = GetPlayerWeaponSlot(client, 0); if (slot0 > -1) { return GetEntProp(slot0, Prop_Data, "m_bInReload") > 0; } return false; } stock bool isStagger(int client) // Client Only { float staggerPos[3]; GetEntPropVector(client, Prop_Send, "m_staggerStart", staggerPos); if (staggerPos[0] != 0.0 && staggerPos[1] != 0.0 && staggerPos[2] != 0.0) return true; return false; } stock bool isJockeyLeaping(int client) { float jockeyVelocity[3]; GetEntDataVector(client, g_Velo, jockeyVelocity); if (jockeyVelocity[2] != 0.0) return true; return false; } stock bool isHaveItem(const char[] FItem, const char[] SItem) { return StrEqual(FItem, SItem, false); } stock void UseItem(int client, const char[] FItem) { FakeClientCommand(client, "use %s", FItem); } stock any PrimaryExtraAmmoCheck(int client, int weapon_index) { // Offset: // 12: Rifle ALL (Other than M60) // 20: SMG ALL // 28: Chrome, Pump // 32: SPAS, Auto // 36: Hunting // 40: Sniper // 68: Granade Launcher // NONE: Rifle M60 is only Clip1 int offset; static char sWeaponName[32]; GetEdictClassname(weapon_index, sWeaponName, sizeof(sWeaponName)); if (isHaveItem(sWeaponName, "weapon_rifle")) offset = 12; else if (isHaveItem(sWeaponName, "weapon_smg")) offset = 20; else if (isHaveItem(sWeaponName, "weapon_shotgun_chrome") || isHaveItem(sWeaponName, "weapon_pumpshotgun")) offset = 28; else if (isHaveItem(sWeaponName, "weapon_shotgun_spas") || isHaveItem(sWeaponName, "weapon_autoshotgun")) offset = 32; else if (isHaveItem(sWeaponName, "weapon_hunting_")) offset = 36; else if (isHaveItem(sWeaponName, "weapon_sniper")) offset = 40; else if (isHaveItem(sWeaponName, "weapon_grenade_launcher")) offset = 68; int extra_ammo = GetEntData(client, (g_iAmmoOffset + offset)); //PrintToChatAll("%N Gun Name: %s, Offset: %i, ExtraAmmo: %i:", client, sWeaponName, offset, extra_ammo); return extra_ammo; } /* -------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- */ public bool traceFilter(int entity, int mask, any self) { return entity != self; } public bool TraceRayDontHitPlayers(int entity, int mask) { // Check if the beam hit a player and tell it to keep tracing if it did return (entity <= 0 || entity > MaxClients); } // Determine if the head of the target can be seen from the client stock bool isVisibleTo(int client, int target) { bool ret = false; float aim_angles[3]; float self_pos[3]; GetClientEyePosition(client, self_pos); computeAimAngles(client, target, aim_angles); Handle trace = TR_TraceRayFilterEx(self_pos, aim_angles, MASK_VISIBLE, RayType_Infinite, traceFilter, client); if (TR_DidHit(trace)) { int hit = TR_GetEntityIndex(trace); if (hit == target) { ret = true; } } delete trace; return ret; } /* Determine if the head of the entity can be seen from the client */ stock bool isVisibleToEntity(int target, int client) { bool ret = false; float aim_angles[3]; float self_pos[3], target_pos[3]; float lookat[3]; GetEntPropVector(target, Prop_Data, "m_vecAbsOrigin", target_pos); GetClientEyePosition(client, self_pos); MakeVectorFromPoints(target_pos, self_pos, lookat); GetVectorAngles(lookat, aim_angles); Handle trace = TR_TraceRayFilterEx(target_pos, aim_angles, MASK_VISIBLE, RayType_Infinite, traceFilter, target); if (TR_DidHit(trace)) { int hit = TR_GetEntityIndex(trace); if (hit == client) { ret = true; } } delete trace; return ret; } /* From the client to the target's head, whether it is blocked by mesh */ stock bool isInterruptTo(int client, int target) { bool ret = false; float aim_angles[3]; float self_pos[3]; GetClientEyePosition(client, self_pos); computeAimAngles(client, target, aim_angles); Handle trace = TR_TraceRayFilterEx(self_pos, aim_angles, MASK_SOLID, RayType_Infinite, traceFilter, client); if (TR_DidHit(trace)) { int hit = TR_GetEntityIndex(trace); if (hit == target) { ret = true; } } delete trace; return ret; } // Calculate the angles from client to target stock void computeAimAngles(int client, int target, float angles[3], int type = 1) { float target_pos[3]; float self_pos[3]; float lookat[3]; GetClientEyePosition(client, self_pos); switch (type) { case 1: { // Eye (Default) GetClientEyePosition(target, target_pos); } case 2: { // Body GetEntPropVector(target, Prop_Data, "m_vecAbsOrigin", target_pos); } case 3: { // Chest GetClientAbsOrigin(target, target_pos); target_pos[2] += 45.0; } } MakeVectorFromPoints(self_pos, target_pos, lookat); GetVectorAngles(lookat, angles); }