Skip to content

Commit

Permalink
Rework l4d2_tank_flying_incap + Animation fix (SirPlease#742)
Browse files Browse the repository at this point in the history
* l4d2_tank_flying_incap: Rework + Animation fix

Feature:
- A version that doesn't mess up with damage calculations for incapped.
- An animation fix to skip the getting-up at the end of fly.
ㅤ- NOTE: Survivors will be able to shoot as soon as they land.

* l4d2_tank_flying_incap: Fix a rare case where animation fix is not working

* l4d2_tank_flying_incap: Default animation fix to off
  • Loading branch information
jensewe authored Feb 25, 2024
1 parent 727011b commit 05b484e
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 90 deletions.
Binary file modified addons/sourcemod/plugins/fixes/l4d2_tank_flying_incap.smx
Binary file not shown.
182 changes: 92 additions & 90 deletions addons/sourcemod/scripting/l4d2_tank_flying_incap.sp
Original file line number Diff line number Diff line change
Expand Up @@ -2,141 +2,143 @@
#pragma newdecls required

#include <sourcemod>
#include <sdkhooks>
#define L4D2UTIL_STOCKS_ONLY 1
#include <l4d2util>

int iSurvivorIncapHealth = 300;
#include <left4dhooks>

bool
bLateLoad,
bDebug = false;
bDebug = false,
bAnim = false;

ConVar
convarDebug,
convarSurvivorIncapHealth = null;
convarAnim = null;

float
g_flIncapTime[MAXPLAYERS+1];

public Plugin myinfo =
{
name = "[L4D2] Flying Incap - Tank Punch",
author = "Sir",
author = "Sir, Forgetest",
description = "Sends Survivors flying on the incapping punch.",
version = "1.0",
version = "2.0",
url = "https://github.com/SirPlease/L4D2-Competitive-Rework"
};

public APLRes AskPluginLoad2(Handle plugin, bool late, char[] error, int errMax)
public void OnPluginStart()
{
bLateLoad = late;
return APLRes_Success;
convarDebug = CreateConVar("l4d2_tank_flying_incap_debug", "0", "Are we debugging?");
convarAnim = CreateConVar("l4d2_tank_flying_incap_anim_fix", "0", "Remove the getting-up animation at the end of fly. (NOTE: Survivors will be able to shoot as soon as they land.)");
bDebug = convarDebug.BoolValue;
bAnim = convarAnim.BoolValue;
convarDebug.AddChangeHook(CvarsChanged);
convarAnim.AddChangeHook(CvarsChanged);

HookEvent("player_incapacitated", Event_player_incapacitated);
}

public void OnPluginStart()
// also serves as late-loader
public void OnMapStart()
{
if (bLateLoad)
for (int i = 1; i <= MaxClients; ++i)
{
for (int i = 1; i < MaxClients+1; i++)
{
if (IsClientInGame(i))
{
OnClientPutInServer(i);
}
}
g_flIncapTime[i] = 0.0;
}

convarDebug = CreateConVar("l4d2_tank_flying_incap_debug", "0", "Are we debugging?");
convarSurvivorIncapHealth = FindConVar("survivor_incap_health");
bDebug = convarDebug.BoolValue;
iSurvivorIncapHealth = convarSurvivorIncapHealth.IntValue;
convarDebug.AddChangeHook(CvarsChanged);
convarSurvivorIncapHealth.AddChangeHook(CvarsChanged);
}

public void OnClientPutInServer(int client)
void Event_player_incapacitated(Event event, const char[] name, bool dontBroadcast)
{
SDKHook(client, SDKHook_OnTakeDamage, OnTakeDamage);
int userid = event.GetInt("userid");

// Doesn't matter it's valid or not, has a minimum of 0.
g_flIncapTime[GetClientOfUserId(userid)] = GetGameTime();

// punch fly animation is applied this frame,
RequestFrame(NextFrame_HookAnimation, userid);
}

public void OnClientDisconnect(int client)
void NextFrame_HookAnimation(int userid)
{
SDKUnhook(client, SDKHook_OnTakeDamage, OnTakeDamage);
if (!bAnim)
return;

int client = GetClientOfUserId(userid);
if (!client || !IsClientInGame(client))
return;

if (GetClientTeam(client) != 2 || !IsPlayerAlive(client))
return;

if (!GetEntProp(client, Prop_Send, "m_isIncapacitated"))
return;

AnimHookEnable(client, AnimHook_PunchFly);
}

public Action OnTakeDamage(int victim, int &attacker, int &inflictor, float &damage, int &damagetype)
Action AnimHook_PunchFly(int client, int &activity)
{
if (damagetype != DMG_CLUB)
return Plugin_Continue;
static int last = 0;

if (!IsValidSurvivor(victim) || !IsValidTank(attacker))
return Plugin_Continue;
if (bDebug && last != activity)
{
if (activity < 0) activity = 0;

if (inflictor <= MaxClients || !IsValidEdict(inflictor))
return Plugin_Continue;
char curActName[64], lastActName[64];
AnimGetActivity(activity, curActName, sizeof(curActName));
AnimGetActivity(last, lastActName, sizeof(lastActName));
PrintToChatAll("\x01[FlyingIncap]: (%.1f) (%N) [\x05%s\x01] [\x04%s\x01]", GetGameTime(), client, curActName, lastActName);

char sClassName[ENTITY_MAX_NAME_LENGTH];
GetEdictClassname(inflictor, sClassName, sizeof(sClassName));
if (strcmp("weapon_tank_claw", sClassName) == 0)
last = activity;
}

switch (activity)
{
if (!IsIncapacitated(victim))
case L4D2_ACT_TERROR_HIT_BY_TANKPUNCH,
L4D2_ACT_TERROR_IDLE_FALL_FROM_TANKPUNCH,
L4D2_ACT_TERROR_JUMP_LANDING,
L4D2_ACT_TERROR_JUMP_LANDING_HARD,
L4D2_ACT_DEPLOY_PISTOL:
{
return Plugin_Continue;
}

// Skip the getting up from ground animation
case L4D2_ACT_TERROR_TANKPUNCH_LAND:
{
int iTotalHealth = GetSurvivorPermanentHealth(victim) + GetSurvivorTemporaryHealth(victim);

if (iTotalHealth <= damage)
{
if (bDebug)
PrintToChatAll("[FlyingIncap]: %N got hit by Tank and has %i HP on incapping punch", victim, iTotalHealth);

DataPack dp;
CreateDataTimer(0.4, Timer_ApplyDamageLater, dp, TIMER_FLAG_NO_MAPCHANGE);
dp.WriteCell(GetClientUserId(victim));
dp.WriteCell(GetClientUserId(attacker));
dp.WriteCell(inflictor);
dp.WriteCell(iTotalHealth);

SetEntityHealth(victim, 1);
SetTempHealth(victim, 0.0);
damage = 0.0;
return Plugin_Changed;
}
PlayerAnimState.FromPlayer(client).m_bIsPunchedByTank = false; // no longer in punched animation

activity = L4D2_ACT_DIESIMPLE; // incap animation intro
}
}
return Plugin_Continue;

AnimHookDisable(client, AnimHook_PunchFly);
return Plugin_Changed;
}

Action Timer_ApplyDamageLater(Handle timer, DataPack dp)
int g_iIncap;
public Action L4D_TankClaw_OnPlayerHit_Pre(int tank, int claw, int player)
{
dp.Reset();
int iSurvivor = GetClientOfUserId(dp.ReadCell());
int iTank = GetClientOfUserId(dp.ReadCell());
int iInflictor = dp.ReadCell();
float damage = float(dp.ReadCell());
damage = damage >= 1.0 ? damage : 1.0;

if (iSurvivor > 0)
{
iTank = iTank > 0 ? iTank : 0;

if (bDebug)
PrintToChatAll("[FlyingIncap]: Applied %0.2f damage to %N", damage, iSurvivor);
g_iIncap = GetEntProp(player, Prop_Send, "m_isIncapacitated");

SDKHooks_TakeDamage(iSurvivor, iInflictor, iTank, damage, DMG_CLUB, WEPID_TANK_CLAW);

if (IsIncapacitated(iSurvivor))
SetEntityHealth(iSurvivor, iSurvivorIncapHealth);
if (GetGameTime() == g_flIncapTime[player])
{
SetEntProp(player, Prop_Send, "m_isIncapacitated", 0);
}

return Plugin_Stop;
return Plugin_Continue;
}

void CvarsChanged(ConVar convar, const char[] oldValue, const char[] newValue)
public void L4D_TankClaw_OnPlayerHit_Post(int tank, int claw, int player)
{
bDebug = convarDebug.BoolValue;
iSurvivorIncapHealth = convarSurvivorIncapHealth.IntValue;
SetEntProp(player, Prop_Send, "m_isIncapacitated", g_iIncap);
}

void SetTempHealth(int client, float fHealth)
public void L4D_TankClaw_OnPlayerHit_PostHandled(int tank, int claw, int player)
{
SetEntPropFloat(client, Prop_Send, "m_healthBufferTime", GetGameTime());
SetEntPropFloat(client, Prop_Send, "m_healthBuffer", fHealth);
SetEntProp(player, Prop_Send, "m_isIncapacitated", g_iIncap);
}

void CvarsChanged(ConVar convar, const char[] oldValue, const char[] newValue)
{
bDebug = convarDebug.BoolValue;
bAnim = convarAnim.BoolValue;
}

0 comments on commit 05b484e

Please sign in to comment.