Add max client total velocity for knockback in addition to max force per tick.

This commit is contained in:
BotoX 2019-09-29 01:13:13 +02:00
parent 197eb861bd
commit 37698b0c20
8 changed files with 194 additions and 120 deletions

View File

@ -66,7 +66,8 @@ public void __pl_zombiereloaded_SetNTVOptional()
MarkNativeAsOptional("ZR_SetKilledByWorld");
MarkNativeAsOptional("ZR_GetKilledByWorld");
MarkNativeAsOptional("ZR_SetClientKnockbackMaxVelocity");
MarkNativeAsOptional("ZR_SetClientKnockbackScale");
MarkNativeAsOptional("ZR_SetClientKnockbackMaxForce");
MarkNativeAsOptional("ZR_SetClientKnockbackMaxVelocity");
}
#endif

View File

@ -27,15 +27,8 @@
#define ZR_KNOCKBACK_CUSTOM (1<<31)
#define ZR_KNOCKBACK_SCALE (1<<1)
#define ZR_KNOCKBACK_LIMITVEL (1<<2)
/**
* Set a maximum knockback velocity.
*
* @param client The client.
* @param fVelocity Maximum knockback velocity / force.
*/
native void ZR_SetClientKnockbackMaxVelocity(int client, float fVelocity);
#define ZR_KNOCKBACK_LIMITFORCE (1<<2)
#define ZR_KNOCKBACK_LIMITVEL (1<<3)
/**
* Set a custom knockback scale.
@ -44,3 +37,19 @@ native void ZR_SetClientKnockbackMaxVelocity(int client, float fVelocity);
* @param fScale Custom knockback scale.
*/
native void ZR_SetClientKnockbackScale(int client, float fScale);
/**
* Set a maximum knockback force per tick.
*
* @param client The client.
* @param fForce Maximum knockback force per tick.
*/
native void ZR_SetClientKnockbackMaxForce(int client, float fForce);
/**
* Set a maximum knockback velocity.
*
* @param client The client.
* @param fVelocity Maximum knockback velocity.
*/
native void ZR_SetClientKnockbackMaxVelocity(int client, float fVelocity);

View File

@ -33,23 +33,9 @@ APIKnockbackInit()
// Class module natives/forwards (knockback.zr.inc)
// Natives
CreateNative("ZR_SetClientKnockbackMaxVelocity", APISetClientKnockbackMaxVelocity);
CreateNative("ZR_SetClientKnockbackScale", APISetClientKnockbackScale);
}
/**
* Native call function (ZR_SetClientKnockbackMaxVelocity)
*
* int ZR_SetClientKnockbackMaxVelocity(int client, float fVelocity);
*/
public int APISetClientKnockbackMaxVelocity(Handle plugin, int numParams)
{
int client = GetNativeCell(1);
float fVelocity = view_as<float>(GetNativeCell(2));
APIValidateClientIndex(client);
KnockbackSetClientMaxVelocity(client, fVelocity);
CreateNative("ZR_SetClientKnockbackMaxForce", APISetClientKnockbackMaxForce);
CreateNative("ZR_SetClientKnockbackMaxVelocity", APISetClientKnockbackMaxVelocity);
}
/**
@ -66,3 +52,33 @@ public int APISetClientKnockbackScale(Handle plugin, int numParams)
KnockbackSetClientScale(client, fScale);
}
/**
* Native call function (ZR_SetClientKnockbackMaxForce)
*
* int ZR_SetClientKnockbackMaxForce(int client, float fForce);
*/
public int APISetClientKnockbackMaxForce(Handle plugin, int numParams)
{
int client = GetNativeCell(1);
float fForce = view_as<float>(GetNativeCell(2));
APIValidateClientIndex(client);
KnockbackSetClientMaxForce(client, fForce);
}
/**
* Native call function (ZR_SetClientKnockbackMaxVelocity)
*
* int ZR_SetClientKnockbackMaxVelocity(int client, float fVelocity);
*/
public int APISetClientKnockbackMaxVelocity(Handle plugin, int numParams)
{
int client = GetNativeCell(1);
float fVelocity = view_as<float>(GetNativeCell(2));
APIValidateClientIndex(client);
KnockbackSetClientMaxVelocity(client, fVelocity);
}

View File

@ -86,6 +86,7 @@ DamageClientInit(client)
// Hook damage callbacks.
SDKHook(client, SDKHook_TraceAttack, DamageTraceAttack);
SDKHook(client, SDKHook_OnTakeDamage, DamageOnTakeDamage);
SDKHook(client, SDKHook_OnTakeDamageAlivePost, DamageOnTakeDamageAlivePost);
}
/**
@ -97,6 +98,7 @@ DamageOnClientDisconnect(client)
{
SDKUnhook(client, SDKHook_TraceAttack, DamageTraceAttack);
SDKUnhook(client, SDKHook_OnTakeDamage, DamageOnTakeDamage);
SDKUnhook(client, SDKHook_OnTakeDamageAlivePost, DamageOnTakeDamageAlivePost);
}
/**
@ -159,7 +161,7 @@ public Action DamageTraceAttack(int client, int &attacker, int &inflictor, float
// Here we know that attacker and client are different teams.
// Check if immunity module requires damage to be blocked.
if (ImmunityOnClientTraceAttack(client, attacker, inflictor, damage, damagetype, ammotype))
if (ImmunityOnClientTraceAttack(client, attacker, inflictor, damage, damagetype, ammotype, hitgroup))
{
// Block damage.
return Plugin_Handled;
@ -216,8 +218,13 @@ public Action DamageTraceAttack(int client, int &attacker, int &inflictor, float
* @return Return Plugin_Handled to stop the damage to client.
* Plugin_Continue to allow damage to client.
*/
public Action:DamageOnTakeDamage(client, &attacker, &inflictor, &Float:damage, &damagetype)
public Action DamageOnTakeDamage(int client, int &attacker, int &inflictor, float &damage, int &damagetype, int &weapon,
float damageForce[3], float damagePosition[3], int damagecustom)
{
bool custom = view_as<bool>(damagecustom & ZR_KNOCKBACK_CUSTOM);
if (custom)
return Plugin_Continue;
// Get classname of the inflictor.
decl String:classname[64];
GetEdictClassname(inflictor, classname, sizeof(classname));
@ -437,3 +444,48 @@ public Action:DamageSuicideIntercept(int client, const char[] command, int argc)
// Block command.
return Plugin_Handled;
}
public void DamageOnTakeDamageAlivePost(int victim, int attacker, int inflictor, float damage, int damagetype, int weapon,
const float damageForce[3], const float damagePosition[3], int damagecustom)
{
// If attacker is invalid, then stop.
if (!ZRIsClientValid(attacker))
{
return;
}
bool custom = view_as<bool>(damagecustom & ZR_KNOCKBACK_CUSTOM);
char weaponname[64];
if (inflictor == attacker)
{
weapon = WeaponsGetActiveWeaponIndex(attacker);
if (weapon > 0)
GetEdictClassname(weapon, weaponname, sizeof(weaponname));
}
else
GetEdictClassname(inflictor, weaponname, sizeof(weaponname));
ReplaceString(weaponname, sizeof(weaponname), "weapon_", "");
ReplaceString(weaponname, sizeof(weaponname), "_projectile", "");
int hitgroup = HITGROUP_GENERIC;
if (!(damagetype & DMG_BLAST) && !custom)
hitgroup = ToolsGetClientLastHitGroup(victim);
KnockbackOnClientHurt(victim, attacker, inflictor, weaponname, hitgroup, damage, damagetype, weapon, damagecustom);
// If attacker is a human and is getting damaged through DamageProxy, then stop.
// We don't want to get infected if we're being knifed inside of a human item.
if (custom && InfectIsClientHuman(victim))
{
return;
}
ClassAlphaUpdate(victim);
InfectOnClientHurt(victim, attacker, weaponname);
AccountOnClientHurt(victim, attacker, RoundToFloor(damage));
SEffectsOnClientHurt(victim);
NapalmOnClientHurt(victim, attacker, weaponname, RoundToFloor(damage));
ZHPOnClientHurt(victim);
}

View File

@ -50,7 +50,6 @@ EventHook(bool:unhook = false)
UnhookEvent("round_end", EventRoundEnd);
UnhookEvent("player_team", EventPlayerTeam, EventHookMode_Pre);
UnhookEvent("player_spawn", EventPlayerSpawn);
UnhookEvent("player_hurt", EventPlayerHurt);
UnhookEvent("player_death", EventPlayerDeath);
UnhookEvent("player_jump", EventPlayerJump);
@ -67,7 +66,6 @@ EventHook(bool:unhook = false)
HookEvent("round_end", EventRoundEnd);
HookEvent("player_team", EventPlayerTeam, EventHookMode_Pre);
HookEvent("player_spawn", EventPlayerSpawn);
HookEvent("player_hurt", EventPlayerHurt);
HookEvent("player_death", EventPlayerDeath);
HookEvent("player_jump", EventPlayerJump);
@ -214,33 +212,6 @@ public Action:EventPlayerSpawnPost(Handle:timer, any:client)
SpawnProtectOnClientSpawnPost(client); // Must be executed after class attributes are applied.
}
/**
* Event callback (player_hurt)
* Client is being hurt.
*
* @param event The event handle.
* @param name Name of the event.
* @dontBroadcast If true, event is broadcasted to all clients, false if not.
*/
public Action:EventPlayerHurt(Handle:event, const String:name[], bool:dontBroadcast)
{
// Get all required event info.
new index = GetClientOfUserId(GetEventInt(event, "userid"));
new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
new dmg_health = GetEventInt(event, "dmg_health");
decl String:weapon[WEAPONS_MAX_LENGTH];
GetEventString(event, "weapon", weapon, sizeof(weapon));
// Forward event to modules.
ClassAlphaUpdate(index);
InfectOnClientHurt(index, attacker, weapon);
AccountOnClientHurt(index, attacker, dmg_health);
SEffectsOnClientHurt(index);
NapalmOnClientHurt(index, attacker, weapon, dmg_health);
ZHPOnClientHurt(index);
}
/**
* Event callback (player_death)
* Client has been killed.

View File

@ -161,7 +161,7 @@ bool:ImmunityOnClientInfect(client, attacker)
*
* @return True if damage should be blocked, false otherwise.
*/
bool ImmunityOnClientTraceAttack(int client, int &attacker, int &inflictor, float &damage, int &damagetype, int &ammotype)
bool ImmunityOnClientTraceAttack(int client, int &attacker, int &inflictor, float &damage, int &damagetype, int &ammotype, int hitgroup)
{
// Check if there is no attacker (world damage).
if (!ZRIsClientValid(attacker))
@ -225,7 +225,7 @@ bool ImmunityOnClientTraceAttack(int client, int &attacker, int &inflictor, floa
}
// Since damage is blocked, trigger knock back hurt event manually.
KnockbackOnTakeDamageAlivePost(client, attacker, inflictor, damage, damagetype, -1, NULL_VECTOR, NULL_VECTOR, 0);
KnockbackOnClientHurt(client, attacker, inflictor, weapon, hitgroup, damage, damagetype, -1, 0);
// Block damage from attacker.
return true;

View File

@ -25,10 +25,12 @@
* ============================================================================
*/
bool g_bKnockbackFrame[MAXPLAYERS + 1];
float g_fKnockbackVelLimit[MAXPLAYERS + 1];
float g_fKnockbackVectors[MAXPLAYERS + 1][3];
float g_fKnockbackScale[MAXPLAYERS + 1];
float g_fKnockbackForceLimit[MAXPLAYERS + 1];
bool g_bKnockbackFrame[MAXPLAYERS + 1];
bool g_bKnockbackFrameLimitVel[MAXPLAYERS + 1];
float g_fKnockbackVectors[MAXPLAYERS + 1][3];
float g_fKnockbackVelLimit[MAXPLAYERS + 1] = {-1.0, ...};
/**
* Minimum upwards boost that is required to push zombies off the ground.
@ -43,14 +45,16 @@ float g_fKnockbackScale[MAXPLAYERS + 1];
*/
void KnockbackClientInit(int client)
{
g_fKnockbackScale[client] = 1.0;
g_fKnockbackForceLimit[client] = 0.0;
g_bKnockbackFrame[client] = false;
g_fKnockbackVelLimit[client] = 0.0;
g_bKnockbackFrameLimitVel[client] = false;
g_fKnockbackVectors[client][0] = 0.0;
g_fKnockbackVectors[client][1] = 0.0;
g_fKnockbackVectors[client][2] = 0.0;
g_fKnockbackScale[client] = 1.0;
SDKHook(client, SDKHook_OnTakeDamageAlivePost, KnockbackOnTakeDamageAlivePost);
g_fKnockbackVelLimit[client] = -1.0;
}
/**
@ -60,10 +64,7 @@ void KnockbackClientInit(int client)
*/
void KnockbackOnClientDisconnect(int client)
{
g_bKnockbackFrame[client] = false;
g_fKnockbackVelLimit[client] = 0.0;
SDKUnhook(client, SDKHook_OnTakeDamageAlivePost, KnockbackOnTakeDamageAlivePost);
KnockbackClientInit(client);
}
/**
@ -71,18 +72,7 @@ void KnockbackOnClientDisconnect(int client)
*/
void KnockbackOnClientDeath(int client)
{
g_bKnockbackFrame[client] = false;
g_fKnockbackVelLimit[client] = 0.0;
g_fKnockbackVectors[client][0] = 0.0;
g_fKnockbackVectors[client][1] = 0.0;
g_fKnockbackVectors[client][2] = 0.0;
g_fKnockbackScale[client] = 1.0;
}
void KnockbackSetClientMaxVelocity(int client, float fVelocity)
{
if(g_fKnockbackVelLimit[client] >= 0.0)
g_fKnockbackVelLimit[client] = fVelocity;
KnockbackClientInit(client);
}
void KnockbackSetClientScale(int client, float fScale)
@ -90,6 +80,17 @@ void KnockbackSetClientScale(int client, float fScale)
g_fKnockbackScale[client] = fScale;
}
void KnockbackSetClientMaxForce(int client, float fForce)
{
if(g_fKnockbackForceLimit[client] >= 0.0)
g_fKnockbackForceLimit[client] = fForce;
}
void KnockbackSetClientMaxVelocity(int client, float fVelocity)
{
g_fKnockbackForceLimit[client] = fVelocity;
}
/**
* Called before every server frame. Note that you should avoid
* doing expensive computations or declaring large local arrays.
@ -98,17 +99,22 @@ void KnockbackOnGameFrame()
{
for(int client = 1; client <= MaxClients; client++)
{
if (g_bKnockbackFrame[client] && g_fKnockbackVelLimit[client] > 0.0)
if (g_bKnockbackFrame[client] && g_fKnockbackForceLimit[client] > 0.0)
{
// Get the knockback force
float magnitude = GetVectorLength(g_fKnockbackVectors[client]);
if(magnitude > g_fKnockbackVelLimit[client])
// ... and limit it if it's too high.
if(magnitude > g_fKnockbackForceLimit[client])
{
ScaleVector(g_fKnockbackVectors[client], g_fKnockbackVelLimit[client] / magnitude);
ScaleVector(g_fKnockbackVectors[client], g_fKnockbackForceLimit[client] / magnitude);
}
KnockbackApplyVector(client, g_fKnockbackVectors[client]);
// Apply the knockback.
KnockbackApplyVector(client, g_fKnockbackVectors[client], g_bKnockbackFrameLimitVel[client]);
g_bKnockbackFrame[client] = false;
g_bKnockbackFrameLimitVel[client] = false;
g_fKnockbackVectors[client][0] = 0.0;
g_fKnockbackVectors[client][1] = 0.0;
g_fKnockbackVectors[client][2] = 0.0;
@ -117,23 +123,18 @@ void KnockbackOnGameFrame()
}
/**
* Hook: KnockbackOnTakeDamageAlivePost
* Hook: KnockbackOnClientHurt
* Called after client has been hurt.
*
* @param client The client index.
* @param inflictor The entity index of the inflictor.
* @param victim The client index.
* @param attacker The client index of the attacker.
* @param inflictor The entity index of the inflictor.
* @param weaponname The weapon used.
* @param hitgroup Hitgroup attacker has damaged.
* @param damage The amount of damage inflicted.
*/
public void KnockbackOnTakeDamageAlivePost(int victim, int attacker, int inflictor, float damage, int damagetype, int weapon,
const float damageForce[3], const float damagePosition[3], int damagecustom)
public void KnockbackOnClientHurt(int victim, int attacker, int inflictor, const char[] weaponname, int hitgroup, float damage, int damagetype, int weapon, int damagecustom)
{
// If attacker is invalid, then stop.
if (!ZRIsClientValid(attacker))
{
return;
}
// Client is a human, then stop.
if (InfectIsClientHuman(victim))
{
@ -160,19 +161,6 @@ public void KnockbackOnTakeDamageAlivePost(int victim, int attacker, int inflict
GetClientAbsOrigin(victim, clientloc);
char weaponname[64];
if (inflictor == attacker)
{
int activeweapon = WeaponsGetActiveWeaponIndex(attacker);
if (activeweapon > 0)
GetEdictClassname(activeweapon, weaponname, sizeof(weaponname));
}
else
GetEdictClassname(inflictor, weaponname, sizeof(weaponname));
ReplaceString(weaponname, sizeof(weaponname), "weapon_", "");
ReplaceString(weaponname, sizeof(weaponname), "_projectile", "");
// Check if a grenade was thrown.
if (StrEqual(weaponname, "hegrenade"))
{
@ -210,12 +198,9 @@ public void KnockbackOnTakeDamageAlivePost(int victim, int attacker, int inflict
bool custom = view_as<bool>(damagecustom & ZR_KNOCKBACK_CUSTOM);
bool scale = custom && view_as<bool>(damagecustom & ZR_KNOCKBACK_SCALE);
bool limitforce = custom && view_as<bool>(damagecustom & ZR_KNOCKBACK_LIMITFORCE);
bool limitvel = custom && view_as<bool>(damagecustom & ZR_KNOCKBACK_LIMITVEL);
int hitgroup = HITGROUP_GENERIC;
if (!(damagetype & DMG_BLAST) && !custom)
hitgroup = ToolsGetClientLastHitGroup(victim);
if (custom)
ToolsSetClientLastHitGroup(victim, HITGROUP_GENERIC);
@ -239,7 +224,7 @@ public void KnockbackOnTakeDamageAlivePost(int victim, int attacker, int inflict
// Apply knockback.
if (knockback)
KnockbackSetVelocity(victim, attackerloc, clientloc, knockback, limitvel);
KnockbackSetVelocity(victim, attackerloc, clientloc, knockback, limitforce, limitvel);
}
/**
@ -250,7 +235,7 @@ public void KnockbackOnTakeDamageAlivePost(int victim, int attacker, int inflict
* @param endpoint The ending coordinate to push towards.
* @param magnitude Magnitude of the push.
*/
KnockbackSetVelocity(client, const Float:startpoint[3], const Float:endpoint[3], Float:magnitude, int limitvel)
KnockbackSetVelocity(client, const Float:startpoint[3], const Float:endpoint[3], Float:magnitude, bool limitforce, bool limitvel)
{
// Create vector from the given starting and ending points.
new Float:vector[3];
@ -262,17 +247,18 @@ KnockbackSetVelocity(client, const Float:startpoint[3], const Float:endpoint[3],
// Apply the magnitude by scaling the vector (multiplying each of its components).
ScaleVector(vector, magnitude);
if (limitvel && g_fKnockbackVelLimit[client])
if (limitforce && g_fKnockbackForceLimit[client])
{
AddVectors(g_fKnockbackVectors[client], vector, g_fKnockbackVectors[client]);
g_bKnockbackFrame[client] = true;
g_bKnockbackFrameLimitVel[client] |= limitvel;
return;
}
KnockbackApplyVector(client, vector);
KnockbackApplyVector(client, vector, limitvel);
}
void KnockbackApplyVector(int client, float vector[3])
void KnockbackApplyVector(int client, float vector[3], bool limitvel)
{
// CS: GO workaround. Apply knock back boost if enabled.
if (g_Game == Game_CSGO && GetConVarBool(g_hCvarsList[CVAR_CLASSES_CSGO_KNOCKBACK_BOOST]))
@ -294,8 +280,36 @@ void KnockbackApplyVector(int client, float vector[3])
}
}
// ADD the given vector to the client's current velocity.
ToolsClientVelocity(client, vector);
// Get their current velocity.
float velocity[3];
ToolsGetClientVelocity(client, velocity);
// Calculate their speed.
float magnitude_pre = GetVectorLength(velocity);
// Add the knockback to their velocity.
AddVectors(vector, velocity, velocity);
// Should we limit their knockback velocity?
if (limitvel && g_fKnockbackVelLimit[client] >= 0.0)
{
// Calculate their new speed.
float magnitude_post = GetVectorLength(velocity);
// Did we actually push them back or just slow them down?
if (magnitude_post > magnitude_pre)
{
// Would their knockback velocity be higher than wanted?
if(magnitude_post > g_fKnockbackVelLimit[client])
{
// ... then scale it down.
ScaleVector(velocity, g_fKnockbackVelLimit[client] / magnitude_post);
}
}
}
// Set the new client's velocity.
ToolsSetClientVelocity(client, velocity);
}
/**

View File

@ -70,6 +70,17 @@ stock ToolsGetClientVelocity(client, Float:vecVelocity[3])
GetEntDataVector(client, g_iToolsVelocity, vecVelocity);
}
/**
* Gets client's velocity.
*
* @param client The client index.
* @param vecVelocity Array to store vector in.
*/
stock ToolsSetClientVelocity(client, const Float:vecVelocity[3])
{
SetEntDataVector(client, g_iToolsVelocity, vecVelocity);
}
/**
* Set a client's lagged movement value.
* @param client The client index.