mirror of
https://github.com/Jackzmc/sourcemod-plugins.git
synced 2025-05-05 17:03:21 +00:00
811 lines
No EOL
31 KiB
SourcePawn
811 lines
No EOL
31 KiB
SourcePawn
/**
|
|
// ====================================================================================================
|
|
Change Log:
|
|
|
|
1.0.2 (08-February-2021)
|
|
- Fixed wrong value on max health calculation.
|
|
- Fixed sprite hiding behind tank rocks.
|
|
- Fixed sprite hiding while tank throws rocks (ability use).
|
|
- Moved visibility logic to timer.
|
|
|
|
1.0.1 (30-January-2021)
|
|
- Public release.
|
|
|
|
1.0.0 (21-April-2019)
|
|
- Private version.
|
|
|
|
// ====================================================================================================
|
|
*/
|
|
|
|
// ====================================================================================================
|
|
// Plugin Info - define
|
|
// ====================================================================================================
|
|
#define PLUGIN_NAME "[L4D1 & L4D2] Tank HP Sprite"
|
|
#define PLUGIN_AUTHOR "Mart"
|
|
#define PLUGIN_DESCRIPTION "Shows a sprite at the tank head that goes from green to red based on its HP"
|
|
#define PLUGIN_VERSION "1.0.2"
|
|
#define PLUGIN_URL "https://forums.alliedmods.net/showthread.php?t=330370"
|
|
|
|
// ====================================================================================================
|
|
// Plugin Info
|
|
// ====================================================================================================
|
|
public Plugin myinfo =
|
|
{
|
|
name = PLUGIN_NAME,
|
|
author = PLUGIN_AUTHOR,
|
|
description = PLUGIN_DESCRIPTION,
|
|
version = PLUGIN_VERSION,
|
|
url = PLUGIN_URL
|
|
}
|
|
|
|
// ====================================================================================================
|
|
// Includes
|
|
// ====================================================================================================
|
|
#include <sourcemod>
|
|
#include <sdktools>
|
|
#include <sdkhooks>
|
|
|
|
// ====================================================================================================
|
|
// 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;
|
|
}
|
|
} |