sourcemod-plugins/scripting/l4d2_sb_fix.sp

2259 lines
75 KiB
SourcePawn
Raw Permalink Blame History

#pragma newdecls required
#include <sourcemod>
#include <sdktools>
#include <sdkhooks>
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<75><6D>include/entity_prop_stock.inc<6E>Q<EFBFBD><51>)
* 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) { // <20><><EFBFBD>ĂĂ<C482> MeleeCount <20>ɂ͓<C982><CD93><EFBFBD><EFBFBD>
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;
}
}
/* ====================================================================================================
*
* <20>D<EFBFBD><44>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;
}
/* ====================================================================================================
*
* <20>D<EFBFBD><44>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;
}
/* ====================================================================================================
*
* <20>D<EFBFBD><44>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) { // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ă<EFBFBD><C482><EFBFBD>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; // <20><><EFBFBD><EFBFBD>
}
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;
}
}
/* ====================================================================================================
*
* <20>D<EFBFBD><44>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")) { //<2F>ߐڂ<DF90><DA82><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ă<EFBFBD><C482>Ȃ<EFBFBD><C882>
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;
}
/* ====================================================================================================
*
* <20>D<EFBFBD><44>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)<29>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);
}
}
}
/* ====================================================================================================
*
* <20>D<EFBFBD><44>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) {
// <20>S<EFBFBD><53><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ă<EFBFBD><C482><EFBFBD><E990B6><EFBFBD>҂<EFBFBD>T<EFBFBD><54>
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;
}
}
// <20>S<EFBFBD><53><EFBFBD><EFBFBD><EFBFBD>Ă<EFBFBD><C482><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EA8AB4><EFBFBD>҂<EFBFBD>T<EFBFBD><54>
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);
}