AlliedModders Donor
|
07-25-2012
, 17:36
Re: Tank Damage Announce by griffin
|
#12
|
.sp :
Code:
#pragma semicolon 1
#include <sourcemod>
new const TEAM_SURVIVOR = 2;
new const TEAM_INFECTED = 3;
new const ZOMBIECLASS_TANK = 8; // Zombie class of the tank, used to find tank after he have been passed to another player
new bool: g_bEnabled = true;
new bool: g_bAnnounceTankDamage = false; // Whether or not tank damage should be announced
new bool: g_bIsTankInPlay = false; // Whether or not the tank is active
new g_iOffset_Incapacitated = 0; // Used to check if tank is dying
new g_iTankClient = 0; // Which client is currently playing as tank
new g_iLastTankHealth = 0; // Used to award the killing blow the exact right amount of damage
new g_iSurvivorLimit = 4; // For survivor array in damage print
new g_iDamage[MAXPLAYERS + 1];
new Float: g_fMaxTankHealth = 6000.0;
new Handle: g_hCvarEnabled = INVALID_HANDLE;
new Handle: g_hCvarTankHealth = INVALID_HANDLE;
new Handle: g_hCvarSurvivorLimit = INVALID_HANDLE;
public Plugin:myinfo =
{
name = "Tank Damage Announce L4D2",
author = "Griffin and Blade",
description = "Announce damage dealt to tanks by survivors",
version = "0.6.5c"
};
public OnPluginStart()
{
g_bIsTankInPlay = false;
g_bAnnounceTankDamage = false;
g_iTankClient = 0;
ClearTankDamage();
HookEvent("tank_spawn", Event_TankSpawn);
HookEvent("player_death", Event_PlayerKilled);
HookEvent("round_start", Event_RoundStart);
HookEvent("round_end", Event_RoundEnd);
HookEvent("player_hurt", Event_PlayerHurt);
g_hCvarEnabled = CreateConVar("l4d_tankdamage_enabled", "1", "Announce damage done to tanks when enabled", FCVAR_PLUGIN|FCVAR_SPONLY|FCVAR_NOTIFY, true, 0.0, true, 1.0);
g_hCvarSurvivorLimit = FindConVar("survivor_limit");
g_hCvarTankHealth = FindConVar("z_tank_health");
HookConVarChange(g_hCvarEnabled, Cvar_Enabled);
HookConVarChange(g_hCvarSurvivorLimit, Cvar_SurvivorLimit);
HookConVarChange(g_hCvarTankHealth, Cvar_TankHealth);
g_bEnabled = GetConVarBool(g_hCvarEnabled);
CalculateTankHealth();
g_iOffset_Incapacitated = FindSendPropInfo("Tank", "m_isIncapacitated");
}
public OnMapStart()
{
// In cases where a tank spawns and map is changed manually, bypassing round end
ClearTankDamage();
}
public OnClientDisconnect_Post(client)
{
if (!g_bIsTankInPlay || client != g_iTankClient) return;
CreateTimer(0.5, Timer_CheckTank, client); // Use a delayed timer due to bugs where the tank passes to another player
}
public Cvar_Enabled(Handle:convar, const String:oldValue[], const String:newValue[])
{
g_bEnabled = StringToInt(newValue) > 0 ? true:false;
}
public Cvar_SurvivorLimit(Handle:convar, const String:oldValue[], const String:newValue[])
{
g_iSurvivorLimit = StringToInt(newValue);
}
public Cvar_TankHealth(Handle:convar, const String:oldValue[], const String:newValue[])
{
CalculateTankHealth();
}
CalculateTankHealth()
{
g_fMaxTankHealth = GetConVarFloat(g_hCvarTankHealth);
if (g_fMaxTankHealth <= 0.0) g_fMaxTankHealth = 1.0; // No dividing by 0!
}
public Event_PlayerHurt(Handle:event, const String:name[], bool:dontBroadcast)
{
if (!g_bIsTankInPlay) return; // No tank in play; no damage to record
new victim = GetClientOfUserId(GetEventInt(event, "userid"));
if (victim != GetTankClient() || // Victim isn't tank; no damage to record
IsTankDying() // Something buggy happens when tank is dying with regards to damage
) return;
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
// We only care about damage dealt by survivors, though it can be funny to see
// claw/self inflicted hittable damage, so maybe in the future we'll do that
if (attacker == 0 || // Damage from world?
!IsClientInGame(attacker) || // Not sure if this happens
GetClientTeam(attacker) != TEAM_SURVIVOR
) return;
g_iDamage[attacker] += GetEventInt(event, "dmg_health");
g_iLastTankHealth = GetEventInt(event, "health");
}
public Event_PlayerKilled(Handle:event, const String:name[], bool:dontBroadcast)
{
if (!g_bIsTankInPlay) return; // No tank in play; no damage to record
new victim = GetClientOfUserId(GetEventInt(event, "userid"));
if (victim != g_iTankClient) return;
// Award the killing blow's damage to the attacker; we don't award
// damage from player_hurt after the tank has died/is dying
// If we don't do it this way, we get wonky/inaccurate damage values
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
if (attacker && IsClientInGame(attacker)) g_iDamage[attacker] += g_iLastTankHealth;
// Damage announce could probably happen right here...
CreateTimer(0.5, Timer_CheckTank, victim); // Use a delayed timer due to bugs where the tank passes to another player
}
public Event_TankSpawn(Handle:event, const String:name[], bool:dontBroadcast)
{
new client = GetClientOfUserId(GetEventInt(event, "userid"));
g_iTankClient = client;
if (g_bIsTankInPlay) return; // Tank passed
// New tank, damage has not been announced
g_bAnnounceTankDamage = true;
g_bIsTankInPlay = true;
// Set health for damage print in case it doesn't get set by player_hurt (aka no one shoots the tank)
g_iLastTankHealth = GetClientHealth(client);
}
public Event_RoundStart(Handle:event, const String:name[], bool:dontBroadcast)
{
g_bIsTankInPlay = false;
g_iTankClient = 0;
ClearTankDamage(); // Probably redundant
}
// When survivors wipe or juke tank, announce damage
public Event_RoundEnd(Handle:event, const String:name[], bool:dontBroadcast)
{
// But only if a tank that hasn't been killed exists
if (g_bAnnounceTankDamage)
{
PrintRemainingHealth();
PrintTankDamage();
}
ClearTankDamage();
}
public Action:Timer_CheckTank(Handle:timer, any:oldtankclient)
{
if (g_iTankClient != oldtankclient) return; // Tank passed
new tankclient = FindTankClient();
if (tankclient && tankclient != oldtankclient)
{
g_iTankClient = tankclient;
return; // Found tank, done
}
if (g_bAnnounceTankDamage) PrintTankDamage();
ClearTankDamage();
g_bIsTankInPlay = false; // No tank in play
}
bool:IsTankDying()
{
new tankclient = GetTankClient();
if (!tankclient) return false;
return bool:GetEntData(tankclient, g_iOffset_Incapacitated);
}
PrintRemainingHealth()
{
if (!g_bEnabled)
return;
new tankclient = GetTankClient();
if (!tankclient) return;
decl String:name[MAX_NAME_LENGTH];
if (IsFakeClient(tankclient)) name = "AI";
else GetClientName(tankclient, name, sizeof(name));
PrintToChatAll("[HP TANK] Il reste %N HP a (%d).", name, g_iLastTankHealth);
}
PrintTankDamage()
{
if (!g_bEnabled) return;
PrintToChatAll("[SM] Les dégâts infligés au Tank :");
new client;
new percent_total; // Accumulated total of calculated percents, for fudging out numbers at the end
new damage_total; // Accumulated total damage dealt by survivors, to see if we need to fudge upwards to 100%
new survivor_index = -1;
new survivor_clients[g_iSurvivorLimit]; // Array to store survivor client indexes in, for the display iteration
decl percent_damage, damage;
for (client = 1; client <= MaxClients; client++)
{
if (!IsClientInGame(client) || GetClientTeam(client) != TEAM_SURVIVOR) continue;
survivor_index++;
survivor_clients[survivor_index] = client;
damage = g_iDamage[client];
damage_total += damage;
percent_damage = GetDamageAsPercent(damage);
percent_total += percent_damage;
}
SortCustom1D(survivor_clients, g_iSurvivorLimit, SortByDamageDesc);
new percent_adjustment;
// Percents add up to less than 100% AND > 99.5% damage was dealt to tank
if ((percent_total < 100 &&
float(damage_total) > (g_fMaxTankHealth - (g_fMaxTankHealth / 200.0)))
)
{
percent_adjustment = 100 - percent_total;
}
new last_percent = 100; // Used to store the last percent in iteration to make sure an adjusted percent doesn't exceed the previous percent
decl adjusted_percent_damage;
for (new i; i <= survivor_index; i++)
{
client = survivor_clients[i];
damage = g_iDamage[client];
percent_damage = GetDamageAsPercent(damage);
// Attempt to adjust the top damager's percent, defer adjustment to next player if it's an exact percent
// e.g. 3000 damage on 6k health tank shouldn't be adjusted
if (percent_adjustment != 0 && // Is there percent to adjust
damage > 0 && // Is damage dealt > 0%
!IsExactPercent(damage) // Percent representation is not exact, e.g. 3000 damage on 6k tank = 50%
)
{
adjusted_percent_damage = percent_damage + percent_adjustment;
if (adjusted_percent_damage <= last_percent) // Make sure adjusted percent is not higher than previous percent, order must be maintained
{
percent_damage = adjusted_percent_damage;
percent_adjustment = 0;
}
}
PrintToChatAll("%4d HP (%d%%) : %N", damage, percent_damage, client);
}
}
ClearTankDamage()
{
g_iLastTankHealth = 0;
for (new i = 1; i <= MaxClients; i++) { g_iDamage[i] = 0; }
g_bAnnounceTankDamage = false;
}
GetTankClient()
{
if (!g_bIsTankInPlay) return 0;
new tankclient = g_iTankClient;
if (!IsClientInGame(tankclient)) // If tank somehow is no longer in the game (kicked, hence events didn't fire)
{
tankclient = FindTankClient(); // find the tank client
if (!tankclient) return 0;
g_iTankClient = tankclient;
}
return tankclient;
}
FindTankClient()
{
for (new client = 1; client <= MaxClients; client++)
{
if (!IsClientInGame(client) ||
GetClientTeam(client) != TEAM_INFECTED ||
!IsPlayerAlive(client) ||
GetEntProp(client, Prop_Send, "m_zombieClass") != ZOMBIECLASS_TANK)
continue;
return client; // Found tank, return
}
return 0;
}
GetDamageAsPercent(damage)
{
return RoundToFloor(FloatMul(FloatDiv(float(damage), g_fMaxTankHealth), 100.0));
}
bool:IsExactPercent(damage)
{
return (FloatAbs(float(GetDamageAsPercent(damage)) - FloatMul(FloatDiv(float(damage), g_fMaxTankHealth), 100.0)) < 0.001) ? true:false;
}
public SortByDamageDesc(elem1, elem2, const array[], Handle:hndl)
{
// By damage, then by client index, descending
if (g_iDamage[elem1] > g_iDamage[elem2]) return -1;
else if (g_iDamage[elem2] > g_iDamage[elem1]) return 1;
else if (elem1 > elem2) return -1;
else if (elem2 > elem1) return 1;
return 0;
}
|
|