From e7cc1fe2ef6c789340fafe92770d6489592454e0 Mon Sep 17 00:00:00 2001 From: Jackz Date: Mon, 8 Feb 2021 11:34:28 -0600 Subject: [PATCH] l4d_tank_hp_sprite: Add support for all specials instead of only tanks --- plugins/l4d_tank_hp_sprite.smx | Bin 0 -> 12554 bytes scripting/l4d_tank_hp_sprite.sp | 811 ++++++++++++++++++++++++++++++++ 2 files changed, 811 insertions(+) create mode 100644 plugins/l4d_tank_hp_sprite.smx create mode 100644 scripting/l4d_tank_hp_sprite.sp diff --git a/plugins/l4d_tank_hp_sprite.smx b/plugins/l4d_tank_hp_sprite.smx new file mode 100644 index 0000000000000000000000000000000000000000..1766cc5f8f1e3b76840dc7cb75a4d444485957ac GIT binary patch literal 12554 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 +#include +#include + +// ==================================================================================================== +// Pragmas +// ==================================================================================================== +#pragma semicolon 1 +#pragma newdecls required + +// ==================================================================================================== +// Cvar Flags +// ==================================================================================================== +#define CVAR_FLAGS FCVAR_NOTIFY +#define CVAR_FLAGS_PLUGIN_VERSION FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_SPONLY + +// ==================================================================================================== +// Filenames +// ==================================================================================================== +#define CONFIG_FILENAME "l4d_tank_hp_sprite" + +// ==================================================================================================== +// Defines +// ==================================================================================================== +#define CLASSNAME_ENV_SPRITE "env_sprite" +#define CLASSNAME_TANK_ROCK "tank_rock" + +#define TEAM_SPECTATOR 1 +#define TEAM_SURVIVOR 2 +#define TEAM_INFECTED 3 +#define TEAM_HOLDOUT 4 + +#define FLAG_TEAM_NONE (0 << 0) // 0 | 0000 +#define FLAG_TEAM_SURVIVOR (1 << 0) // 1 | 0001 +#define FLAG_TEAM_INFECTED (1 << 1) // 2 | 0010 +#define FLAG_TEAM_SPECTATOR (1 << 2) // 4 | 0100 +#define FLAG_TEAM_HOLDOUT (1 << 3) // 8 | 1000 + +#define L4D1_ZOMBIECLASS_TANK 5 +#define L4D2_ZOMBIECLASS_TANK 8 + +#define MAXENTITIES 2048 + +// ==================================================================================================== +// Plugin Cvars +// ==================================================================================================== +static ConVar g_hCvar_Enabled; +static ConVar g_hCvar_ZAxis; +static ConVar g_hCvar_FadeDistance; +static ConVar g_hCvar_Sight; +static ConVar g_hCvar_AttackDelay; +static ConVar g_hCvar_AliveShow; +static ConVar g_hCvar_AliveModel; +static ConVar g_hCvar_AliveAlpha; +static ConVar g_hCvar_AliveScale; +static ConVar g_hCvar_DeadShow; +static ConVar g_hCvar_DeadModel; +static ConVar g_hCvar_DeadAlpha; +static ConVar g_hCvar_DeadScale; +static ConVar g_hCvar_DeadColor; +static ConVar g_hCvar_Team; +static ConVar g_hCvar_AllSpecials; + +// ==================================================================================================== +// bool - Plugin Variables +// ==================================================================================================== +static bool g_bL4D2; +static bool g_bConfigLoaded; +static bool g_bEventsHooked; +static bool g_bCvar_Enabled; +static bool g_bCvar_Sight; +static bool g_bCvar_AttackDelay; +static bool g_bCvar_AliveShow; +static bool g_bCvar_DeadShow; + +// ==================================================================================================== +// int - Plugin Variables +// ==================================================================================================== +static int g_iTankClass; +static int g_iCvar_AliveAlpha; +static int g_iCvar_DeadAlpha; +static int g_iCvar_FadeDistance; +static int g_iCvar_Team; + +// ==================================================================================================== +// float - Plugin Variables +// ==================================================================================================== +static float g_fVPlayerMins[3] = {-16.0, -16.0, 0.0}; +static float g_fVPlayerMaxs[3] = { 16.0, 16.0, 71.0}; +static float g_fVPos[3]; +static float g_fCvar_ZAxis; +static float g_fCvar_AttackDelay; +static float g_fCvar_AliveScale; +static float g_fCvar_DeadScale; + +// ==================================================================================================== +// string - Plugin Variables +// ==================================================================================================== +static char g_sCvar_AliveModel[100]; +static char g_sCvar_AliveAlpha[4]; +static char g_sCvar_AliveScale[5]; +static char g_sCvar_DeadModel[100]; +static char g_sCvar_DeadAlpha[4]; +static char g_sCvar_DeadScale[5]; +static char g_sCvar_DeadColor[12]; +static char g_sCvar_FadeDistance[5]; + +// ==================================================================================================== +// client - Plugin Variables +// ==================================================================================================== +static int gc_iTankSpriteRef[MAXPLAYERS+1] = { INVALID_ENT_REFERENCE, ... }; +static bool gc_bVisible[MAXPLAYERS+1][MAXPLAYERS+1]; +static float gc_fLastAttack[MAXPLAYERS+1][MAXPLAYERS+1]; + +// ==================================================================================================== +// entity - Plugin Variables +// ==================================================================================================== +static bool ge_bInvalidTrace[MAXENTITIES+1]; +static int ge_iOwner[MAXENTITIES+1]; + +// ==================================================================================================== +// Plugin Start +// ==================================================================================================== +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + EngineVersion engine = GetEngineVersion(); + + if (engine != Engine_Left4Dead && engine != Engine_Left4Dead2) + { + strcopy(error, err_max, "This plugin only runs in \"Left 4 Dead\" and \"Left 4 Dead 2\" game"); + return APLRes_SilentFailure; + } + + g_bL4D2 = (engine == Engine_Left4Dead2); + g_iTankClass = (g_bL4D2 ? L4D2_ZOMBIECLASS_TANK : L4D1_ZOMBIECLASS_TANK); + + return APLRes_Success; +} + +/****************************************************************************************************/ + +public void OnPluginStart() +{ + CreateConVar("l4d_tank_hp_sprite_version", PLUGIN_VERSION, PLUGIN_DESCRIPTION, CVAR_FLAGS_PLUGIN_VERSION); + g_hCvar_Enabled = CreateConVar("l4d_tank_hp_sprite_enable", "1", "Enable/Disable the plugin.\n0 = Disable, 1 = Enable", CVAR_FLAGS, true, 0.0, true, 1.0); + g_hCvar_ZAxis = CreateConVar("l4d_tank_hp_sprite_z_axis", "92", "Additional Z distance based on the tank position.", CVAR_FLAGS, true, 0.0); + g_hCvar_FadeDistance = CreateConVar("l4d_tank_hp_sprite_fade_distance", "-1", "Minimum distance that a client must be from the tank to see the sprite (both alive and dead sprites).\n-1 = Always visible.", CVAR_FLAGS, true, -1.0, true, 9999.0); + g_hCvar_Sight = CreateConVar("l4d_tank_hp_sprite_sight", "1", "Show the sprite to the survivor only if the Tank is on sight.\n0 = OFF, 1 = ON.", CVAR_FLAGS, true, 0.0, true, 1.0); + g_hCvar_AttackDelay = CreateConVar("l4d_tank_hp_sprite_attack_delay", "0.0", "Show the sprite to the survivor attacker, by this amount of time in seconds, after hitting the Tank.\n0 = OFF.", CVAR_FLAGS, true, 0.0); + g_hCvar_AliveShow = CreateConVar("l4d_tank_hp_sprite_alive_show", "1", "Show the alive sprite while tank is alive.\n0 = OFF, 1 = ON.", CVAR_FLAGS, true, 0.0, true, 1.0); + g_hCvar_AliveModel = CreateConVar("l4d_tank_hp_sprite_alive_model", "materials/vgui/healthbar_white.vmt", "Model of alive tank sprite."); + g_hCvar_AliveAlpha = CreateConVar("l4d_tank_hp_sprite_alive_alpha", "200", "Alpha of alive tank sprite.\n0 = Invisible, 255 = Fully Visible", CVAR_FLAGS, true, 0.0, true, 255.0); + g_hCvar_AliveScale = CreateConVar("l4d_tank_hp_sprite_alive_scale", "0.25", "Scale of alive tank sprite (increases both height and width).\nNote: Some range values maintain the same size. (e.g. from 0.0 to 0.38 the size doesn't change).", CVAR_FLAGS, true, 0.0); + g_hCvar_DeadShow = CreateConVar("l4d_tank_hp_sprite_dead_show", "1", "Show the dead sprite when a tank dies.\n0 = OFF, 1 = ON.", CVAR_FLAGS, true, 0.0, true, 1.0); + g_hCvar_DeadModel = CreateConVar("l4d_tank_hp_sprite_dead_model", "materials/sprites/death_icon.vmt", "Model of dead tank sprite."); + g_hCvar_DeadAlpha = CreateConVar("l4d_tank_hp_sprite_dead_alpha", "200", "Alpha of dead tank sprite.\n0 = Invisible, 255 = Fully Visible", CVAR_FLAGS, true, 0.0, true, 255.0); + g_hCvar_DeadScale = CreateConVar("l4d_tank_hp_sprite_dead_scale", "0.25", "Scale of dead tank sprite (increases both height and width).\nSome range values maintain the size the same.", CVAR_FLAGS, true, 0.0); + g_hCvar_DeadColor = CreateConVar("l4d_tank_hp_sprite_dead_color", "225 0 0", "Color of dead tank sprite.\nUse three values between 0-255 separated by spaces (\"<0-255> <0-255> <0-255>\").", CVAR_FLAGS); + g_hCvar_Team = CreateConVar("l4d_tank_hp_sprite_team", "3", "Which teams should the sprite be visible.\n0 = NONE, 1 = SURVIVOR, 2 = INFECTED, 4 = SPECTATOR, 8 = HOLDOUT.\nAdd numbers greater than 0 for multiple options.\nExample: \"3\", enables for SURVIVOR and INFECTED.", CVAR_FLAGS, true, 0.0, true, 15.0); + g_hCvar_AllSpecials = CreateConVar("l4d_tank_hp_sprite_all_specials", "1", "Should all specials have healthbar or only tanks\n0 = Tanks Only, 1 = All Specials", CVAR_FLAGS, true, 0.0, true, 1.0); + + g_hCvar_Enabled.AddChangeHook(Event_ConVarChanged); + g_hCvar_ZAxis.AddChangeHook(Event_ConVarChanged); + g_hCvar_FadeDistance.AddChangeHook(Event_ConVarChanged); + g_hCvar_Sight.AddChangeHook(Event_ConVarChanged); + g_hCvar_AttackDelay.AddChangeHook(Event_ConVarChanged); + g_hCvar_AliveShow.AddChangeHook(Event_ConVarChanged); + g_hCvar_AliveModel.AddChangeHook(Event_ConVarChanged); + g_hCvar_AliveAlpha.AddChangeHook(Event_ConVarChanged); + g_hCvar_AliveScale.AddChangeHook(Event_ConVarChanged); + g_hCvar_DeadShow.AddChangeHook(Event_ConVarChanged); + g_hCvar_DeadModel.AddChangeHook(Event_ConVarChanged); + g_hCvar_DeadAlpha.AddChangeHook(Event_ConVarChanged); + g_hCvar_DeadScale.AddChangeHook(Event_ConVarChanged); + g_hCvar_DeadColor.AddChangeHook(Event_ConVarChanged); + g_hCvar_Team.AddChangeHook(Event_ConVarChanged); + + // Load plugin configs from .cfg + AutoExecConfig(true, CONFIG_FILENAME); + + // Admin Commands + RegAdminCmd("sm_print_cvars_l4d_tank_hp_sprite", CmdPrintCvars, ADMFLAG_ROOT, "Print the plugin related cvars and their respective values to the console."); + + CreateTimer(0.1, TimerKill, _, TIMER_REPEAT); + CreateTimer(0.1, TimerVisible, _, TIMER_REPEAT); + CreateTimer(0.1, TimerRender, _, TIMER_REPEAT); +} + +/****************************************************************************************************/ + +public void OnPluginEnd() +{ + int entity; + char targetname[64]; + + entity = INVALID_ENT_REFERENCE; + while ((entity = FindEntityByClassname(entity, CLASSNAME_ENV_SPRITE)) != INVALID_ENT_REFERENCE) + { + if (GetEntProp(entity, Prop_Data, "m_iHammerID") == -1) + { + GetEntPropString(entity, Prop_Data, "m_iName", targetname, sizeof(targetname)); + if (StrEqual(targetname, "l4d_tank_hp_sprite")) + AcceptEntityInput(entity, "Kill"); + } + } +} + +/****************************************************************************************************/ + +public void OnConfigsExecuted() +{ + GetCvars(); + + g_bConfigLoaded = true; + + LateLoad(); + + HookEvents(g_bCvar_Enabled); +} + +/****************************************************************************************************/ + +public void Event_ConVarChanged(Handle convar, const char[] sOldValue, const char[] sNewValue) +{ + GetCvars(); + + HookEvents(g_bCvar_Enabled); +} + +/****************************************************************************************************/ + +public void GetCvars() +{ + g_bCvar_Enabled = g_hCvar_Enabled.BoolValue; + g_fCvar_ZAxis = g_hCvar_ZAxis.FloatValue; + g_fVPos[2] = g_fCvar_ZAxis; + g_iCvar_FadeDistance = g_hCvar_FadeDistance.IntValue; + FormatEx(g_sCvar_FadeDistance, sizeof(g_sCvar_FadeDistance), "%i", g_iCvar_FadeDistance); + g_bCvar_Sight = g_hCvar_Sight.BoolValue; + g_fCvar_AttackDelay = g_hCvar_AttackDelay.FloatValue; + g_bCvar_AttackDelay = (g_fCvar_AttackDelay > 0.0); + g_bCvar_AliveShow = g_hCvar_AliveShow.BoolValue; + g_hCvar_AliveModel.GetString(g_sCvar_AliveModel, sizeof(g_sCvar_AliveModel)); + TrimString(g_sCvar_AliveModel); + PrecacheModel(g_sCvar_AliveModel, true); + g_iCvar_AliveAlpha = g_hCvar_AliveAlpha.IntValue; + FormatEx(g_sCvar_AliveAlpha, sizeof(g_sCvar_AliveAlpha), "%i", g_iCvar_AliveAlpha); + g_fCvar_AliveScale = g_hCvar_AliveScale.FloatValue; + FormatEx(g_sCvar_AliveScale, sizeof(g_sCvar_AliveScale), "%.2f", g_fCvar_AliveScale); + g_bCvar_DeadShow = g_hCvar_DeadShow.BoolValue; + g_hCvar_DeadModel.GetString(g_sCvar_DeadModel, sizeof(g_sCvar_DeadModel)); + TrimString(g_sCvar_DeadModel); + PrecacheModel(g_sCvar_DeadModel, true); + g_iCvar_DeadAlpha = g_hCvar_DeadAlpha.IntValue; + FormatEx(g_sCvar_DeadAlpha, sizeof(g_sCvar_DeadAlpha), "%i", g_iCvar_DeadAlpha); + g_fCvar_DeadScale = g_hCvar_DeadScale.FloatValue; + FormatEx(g_sCvar_DeadScale, sizeof(g_sCvar_DeadScale), "%.2f", g_fCvar_DeadScale); + g_hCvar_DeadColor.GetString(g_sCvar_DeadColor, sizeof(g_sCvar_DeadColor)); + TrimString(g_sCvar_DeadColor); + g_iCvar_Team = g_hCvar_Team.IntValue; +} + +/****************************************************************************************************/ + +public void LateLoad() +{ + for (int client = 1; client <= MaxClients; client++) + { + if (IsPlayerSpecialInfected(client)) + TankSprite(client); + } +} + +/****************************************************************************************************/ + +public void OnClientDisconnect(int client) +{ + if (!g_bConfigLoaded) + return; + + gc_iTankSpriteRef[client] = INVALID_ENT_REFERENCE; + + for (int target = 1; target <= MaxClients; target++) + { + gc_bVisible[target][client] = false; + gc_fLastAttack[target][client] = 0.0; + } +} + +/****************************************************************************************************/ + +public void OnEntityDestroyed(int entity) +{ + if (!g_bConfigLoaded) + return; + + if (!IsValidEntityIndex(entity)) + return; + + ge_bInvalidTrace[entity] = false; + ge_iOwner[entity] = 0; +} + +/****************************************************************************************************/ + +public void OnEntityCreated(int entity, const char[] classname) +{ + if (!g_bConfigLoaded) + return; + + if (!IsValidEntityIndex(entity)) + return; + + switch (classname[0]) + { + case 't': + { + if (StrEqual(classname, CLASSNAME_TANK_ROCK)) + ge_bInvalidTrace[entity] = true; + } + } +} + +/****************************************************************************************************/ + +public void HookEvents(bool hook) +{ + if (hook && !g_bEventsHooked) + { + g_bEventsHooked = true; + + HookEvent("player_hurt", Event_PlayerHurt); + + return; + } + + if (!hook && g_bEventsHooked) + { + g_bEventsHooked = false; + + UnhookEvent("player_hurt", Event_PlayerHurt); + + return; + } +} + +/****************************************************************************************************/ + + +public void OnClientPutInServer(int client) { + if(GetClientTeam(client) == TEAM_INFECTED) { + //If all specials turned off and not tank; ignore. + if(!g_hCvar_AllSpecials.BoolValue && GetZombieClass(client) != g_iTankClass) return; + TankSprite(client); + } +} + +/****************************************************************************************************/ + +public void Event_PlayerHurt(Event event, const char[] name, bool dontBroadcast) { + int tank = GetClientOfUserId(event.GetInt("userid")); + if (IsPlayerSpecialInfected(tank)) { + TankSprite(tank); + if(g_bCvar_AttackDelay) { + int attacker = GetClientOfUserId(event.GetInt("attacker")); + if (IsValidClient(attacker) && GetClientTeam(attacker) != TEAM_SURVIVOR) + gc_fLastAttack[tank][attacker] = GetGameTime(); + } + } +} + +/****************************************************************************************************/ + +public Action TimerKill(Handle timer) +{ + if (!g_bConfigLoaded) + return Plugin_Continue; + + for (int client = 1; client <= MaxClients; client++) + { + if (gc_iTankSpriteRef[client] != INVALID_ENT_REFERENCE && !IsPlayerSpecialInfected(client)) { + int entity = EntRefToEntIndex(gc_iTankSpriteRef[client]); + + if (entity != INVALID_ENT_REFERENCE) + AcceptEntityInput(entity, "Kill"); + + gc_iTankSpriteRef[client] = INVALID_ENT_REFERENCE; + + for (int client2 = 1; client2 <= MaxClients; client2++) + { + gc_bVisible[client][client2] = false; + gc_fLastAttack[client][client2] = 0.0; + } + } + } + + return Plugin_Continue; +} + +/****************************************************************************************************/ + +public Action TimerVisible(Handle timer) +{ + if (!g_bConfigLoaded) + return Plugin_Continue; + + if (!g_bCvar_Enabled) + return Plugin_Continue; + + for (int target = 1; target <= MaxClients; target++) + { + if (gc_iTankSpriteRef[target] == INVALID_ENT_REFERENCE) + continue; + + if (!IsClientConnected(target)) + continue; + + for (int client = 1; client <= MaxClients; client++) + { + if (!IsClientConnected(client)) + continue; + + if (IsFakeClient(client)) + continue; + + if (!(GetClientTeamFlag(client) & g_iCvar_Team)) + { + gc_bVisible[target][client] = false; + continue; + } + + if (g_bCvar_AttackDelay || g_bCvar_Sight) + { + if (GetClientTeam(client) == TEAM_SURVIVOR || GetClientTeam(client) == TEAM_HOLDOUT) + { + if (g_bCvar_AttackDelay && (GetGameTime() - gc_fLastAttack[target][client] > g_fCvar_AttackDelay)) + { + gc_bVisible[target][client] = false; + continue; + } + + if (g_bCvar_Sight && !IsVisibleTo(client, target)) + { + gc_bVisible[target][client] = false; + continue; + } + } + } + + gc_bVisible[target][client] = true; + } + } + + return Plugin_Continue; +} + +/****************************************************************************************************/ + +public Action TimerRender(Handle timer) +{ + if (!g_bConfigLoaded) + return Plugin_Continue; + + if (!g_bCvar_Enabled) + return Plugin_Continue; + + for (int target = 1; target <= MaxClients; target++) + { + if (!IsPlayerSpecialInfected(target)) + continue; + + TankSprite(target); + } + + return Plugin_Continue; +} + +/****************************************************************************************************/ + +public void TankSprite(int client) +{ + int entity = INVALID_ENT_REFERENCE; + + if (gc_iTankSpriteRef[client] != INVALID_ENT_REFERENCE) + entity = EntRefToEntIndex(gc_iTankSpriteRef[client]); + + if (entity == INVALID_ENT_REFERENCE) + { + entity = CreateEntityByName(CLASSNAME_ENV_SPRITE); + DispatchKeyValue(entity, "targetname", "l4d_tank_hp_sprite"); + DispatchKeyValue(entity, "spawnflags", "1"); + DispatchKeyValue(entity, "fademindist", g_sCvar_FadeDistance); + SetEntProp(entity, Prop_Data, "m_iHammerID", -1); + SDKHook(entity, SDKHook_SetTransmit, OnSetTransmit); + ge_iOwner[entity] = client; + gc_iTankSpriteRef[client] = EntIndexToEntRef(entity); + } + + if (IsPlayerIncapacitated(client)) + { + if (g_bCvar_DeadShow) + { + DispatchKeyValue(entity, "model", g_sCvar_DeadModel); + DispatchKeyValue(entity, "rendercolor", g_sCvar_DeadColor); + DispatchKeyValue(entity, "renderamt", g_sCvar_DeadAlpha); // If renderamt goes before rendercolor, it doesn't render + DispatchKeyValue(entity, "scale", g_sCvar_DeadScale); + DispatchSpawn(entity); + SetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity", client); + SetVariantString("!activator"); + AcceptEntityInput(entity, "SetParent", client); + AcceptEntityInput(entity, "ShowSprite"); + TeleportEntity(entity, g_fVPos, NULL_VECTOR, NULL_VECTOR); + } + + return; + } + + if (!g_bCvar_AliveShow) + { + AcceptEntityInput(entity, "HideSprite"); + return; + } + + int maxHealth = GetEntProp(client, Prop_Data, "m_iMaxHealth"); + int currentHealth = GetClientHealth(client); + + float percentageHealth; + if (maxHealth == 0) + percentageHealth = 0.0; + else + percentageHealth = (float(currentHealth) / float(maxHealth)); + + bool halfHealth = (percentageHealth <= 0.5); + + char sRenderColor[12]; + Format(sRenderColor, sizeof(sRenderColor), "%i %i 0", halfHealth ? 255 : RoundFloat(255.0 * ((1.0 - percentageHealth) * 2)), halfHealth ? RoundFloat(255.0 * (percentageHealth) * 2) : 255); + + DispatchKeyValue(entity, "model", g_sCvar_AliveModel); + DispatchKeyValue(entity, "rendercolor", sRenderColor); + DispatchKeyValue(entity, "renderamt", g_sCvar_AliveAlpha); // If renderamt goes before rendercolor, it doesn't render + DispatchKeyValue(entity, "scale", g_sCvar_AliveScale); + DispatchSpawn(entity); + SetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity", client); + SetVariantString("!activator"); + AcceptEntityInput(entity, "SetParent", client); + AcceptEntityInput(entity, "ShowSprite"); + TeleportEntity(entity, g_fVPos, NULL_VECTOR, NULL_VECTOR); +} + +/****************************************************************************************************/ + +public Action OnSetTransmit(int entity, int client) +{ + int owner = ge_iOwner[entity]; + + if (owner == client) + return Plugin_Handled; + + if (gc_bVisible[owner][client]) + return Plugin_Continue; + + return Plugin_Handled; +} + +/****************************************************************************************************/ + +bool IsVisibleTo(int client, int target) +{ + float vClientPos[3]; + float vEntityPos[3]; + float vLookAt[3]; + float vAngles[3]; + + GetClientEyePosition(client, vClientPos); + GetClientEyePosition(target, vEntityPos); + MakeVectorFromPoints(vClientPos, vEntityPos, vLookAt); + GetVectorAngles(vLookAt, vAngles); + + Handle trace = TR_TraceRayFilterEx(vClientPos, vAngles, MASK_PLAYERSOLID, RayType_Infinite, TraceFilter, target); + + bool isVisible; + + if (TR_DidHit(trace)) + { + isVisible = (TR_GetEntityIndex(trace) == target); + + if (!isVisible) + { + vEntityPos[2] -= 62.0; // results the same as GetClientAbsOrigin + + delete trace; + trace = TR_TraceHullFilterEx(vClientPos, vEntityPos, g_fVPlayerMins, g_fVPlayerMaxs, MASK_PLAYERSOLID, TraceFilter, target); + + if (TR_DidHit(trace)) + isVisible = (TR_GetEntityIndex(trace) == target); + } + } + + delete trace; + + return isVisible; +} + +/****************************************************************************************************/ + +public bool TraceFilter(int entity, int contentsMask, int client) +{ + if (entity == client) + return true; + + if (IsValidClientIndex(entity)) + return false; + + return ge_bInvalidTrace[entity] ? false : true; +} + +/****************************************************************************************************/ + +public Action CmdPrintCvars(int client, int args) +{ + PrintToConsole(client, ""); + PrintToConsole(client, "======================================================================"); + PrintToConsole(client, ""); + PrintToConsole(client, "----------------- Plugin Cvars (l4d_tank_hp_sprite) ------------------"); + PrintToConsole(client, ""); + PrintToConsole(client, "l4d_tank_hp_sprite_version : %s", PLUGIN_VERSION); + PrintToConsole(client, "l4d_tank_hp_sprite_enable : %b (%s)", g_bCvar_Enabled, g_bCvar_Enabled ? "true" : "false"); + PrintToConsole(client, "l4d_tank_hp_sprite_z_axis : %.2f", g_fCvar_ZAxis); + PrintToConsole(client, "l4d_tank_hp_sprite_fade_distance : %i", g_iCvar_FadeDistance); + PrintToConsole(client, "l4d_tank_hp_sprite_sight : %b (%s)", g_bCvar_Sight, g_bCvar_Sight ? "true" : "false"); + PrintToConsole(client, "l4d_tank_hp_sprite_attack_delay : %.2f (%s)", g_fCvar_AttackDelay, g_bCvar_AttackDelay ? "true" : "false"); + PrintToConsole(client, "l4d_tank_hp_sprite_alive_show : %b (%s)", g_bCvar_AliveShow, g_bCvar_AliveShow ? "true" : "false"); + PrintToConsole(client, "l4d_tank_hp_sprite_alive_model : \"%s\"", g_sCvar_AliveModel); + PrintToConsole(client, "l4d_tank_hp_sprite_alive_alpha : %i", g_iCvar_AliveAlpha); + PrintToConsole(client, "l4d_tank_hp_sprite_alive_scale : %.2f", g_fCvar_AliveScale); + PrintToConsole(client, "l4d_tank_hp_sprite_dead_show : %b (%s)", g_bCvar_DeadShow, g_bCvar_DeadShow ? "true" : "false"); + PrintToConsole(client, "l4d_tank_hp_sprite_dead_model : \"%s\"", g_sCvar_DeadModel); + PrintToConsole(client, "l4d_tank_hp_sprite_dead_alpha : %i", g_iCvar_DeadAlpha); + PrintToConsole(client, "l4d_tank_hp_sprite_dead_scale : %.2f", g_fCvar_DeadScale); + PrintToConsole(client, "l4d_tank_hp_sprite_dead_color : \"%s\"", g_sCvar_DeadColor); + PrintToConsole(client, "l4d_tank_hp_sprite_team : %i", g_iCvar_Team); + PrintToConsole(client, ""); + PrintToConsole(client, "======================================================================"); + PrintToConsole(client, ""); + + return Plugin_Handled; +} + +// ==================================================================================================== +// Helpers +// ==================================================================================================== +/** + * Validates if is a valid client index. + * + * @param client Client index. + * @return True if client index is valid, false otherwise. + */ +bool IsValidClientIndex(int client) +{ + return (1 <= client <= MaxClients); +} + +/****************************************************************************************************/ + +/** + * Validates if is a valid client. + * + * @param client Client index. + * @return True if client index is valid and client is in game, false otherwise. + */ +bool IsValidClient(int client) +{ + return (IsValidClientIndex(client) && IsClientInGame(client)); +} + +/****************************************************************************************************/ + +/** + * Validates if is a valid entity index (between MaxClients+1 and 2048). + * + * @param entity Entity index. + * @return True if entity index is valid, false otherwise. + */ +bool IsValidEntityIndex(int entity) +{ + return (MaxClients+1 <= entity <= GetMaxEntities()); +} + +/****************************************************************************************************/ + +/** + * Gets the client L4D1/L4D2 zombie class id. + * + * @param client Client index. + * @return L4D1 1=SMOKER, 2=BOOMER, 3=HUNTER, 4=WITCH, 5=TANK, 6=NOT INFECTED + * @return L4D2 1=SMOKER, 2=BOOMER, 3=HUNTER, 4=SPITTER, 5=JOCKEY, 6=CHARGER, 7=WITCH, 8=TANK, 9=NOT INFECTED + */ +int GetZombieClass(int client) +{ + return (GetEntProp(client, Prop_Send, "m_zombieClass")); +} + +/****************************************************************************************************/ + +/** + * Returns is a player is in ghost state. + * + * @param client Client index. + * @return True if client is in ghost state, false otherwise. + */ +bool IsPlayerGhost(int client) +{ + return (GetEntProp(client, Prop_Send, "m_isGhost") == 1); +} + +/****************************************************************************************************/ + +/** + * Validates if the client is incapacitated. + * + * @param client Client index. + * @return True if the client is incapacitated, false otherwise. + */ +bool IsPlayerIncapacitated(int client) +{ + return (GetEntProp(client, Prop_Send, "m_isIncapacitated") == 1); +} + +/****************************************************************************************************/ + +/** + * Returns if the client is a valid tank. + * + * @param client Client index. + * @return True if client is a tank, false otherwise. + */ +bool IsPlayerSpecialInfected(int client) { + bool isValid = IsValidClient(client) && GetClientTeam(client) == TEAM_INFECTED && IsPlayerAlive(client) && !IsPlayerGhost(client); + if(!g_hCvar_AllSpecials.BoolValue && GetZombieClass(client) != g_iTankClass) + return false; + else + return isValid; +} + +/****************************************************************************************************/ + +/** + * Returns the team flag from a client. + * + * @param client Client index. + * @return Client team flag. + */ +int GetClientTeamFlag(int client) +{ + switch (GetClientTeam(client)) + { + case TEAM_SURVIVOR: + return FLAG_TEAM_SURVIVOR; + case TEAM_INFECTED: + return FLAG_TEAM_INFECTED; + case TEAM_SPECTATOR: + return FLAG_TEAM_SPECTATOR; + case TEAM_HOLDOUT: + return FLAG_TEAM_HOLDOUT; + default: + return FLAG_TEAM_NONE; + } +} \ No newline at end of file