diff --git a/src/include/zombiereloaded.inc b/src/include/zombiereloaded.inc index 861a280..52f08be 100644 --- a/src/include/zombiereloaded.inc +++ b/src/include/zombiereloaded.inc @@ -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 diff --git a/src/include/zr/knockback.zr.inc b/src/include/zr/knockback.zr.inc index 1cd74cc..861e251 100644 --- a/src/include/zr/knockback.zr.inc +++ b/src/include/zr/knockback.zr.inc @@ -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); diff --git a/src/zr/api/knockback.api.inc b/src/zr/api/knockback.api.inc index 8fa9daf..1a38391 100644 --- a/src/zr/api/knockback.api.inc +++ b/src/zr/api/knockback.api.inc @@ -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(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(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(GetNativeCell(2)); + + APIValidateClientIndex(client); + + KnockbackSetClientMaxVelocity(client, fVelocity); +} diff --git a/src/zr/damage.inc b/src/zr/damage.inc index fdb7242..2af82ef 100644 --- a/src/zr/damage.inc +++ b/src/zr/damage.inc @@ -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(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(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); +} diff --git a/src/zr/event.inc b/src/zr/event.inc index 3bbe94e..bd9ac14 100644 --- a/src/zr/event.inc +++ b/src/zr/event.inc @@ -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. diff --git a/src/zr/immunityhandler.inc b/src/zr/immunityhandler.inc index 2e9f91f..cb9e643 100644 --- a/src/zr/immunityhandler.inc +++ b/src/zr/immunityhandler.inc @@ -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; diff --git a/src/zr/knockback.inc b/src/zr/knockback.inc index 6b17a70..5cf2632 100644 --- a/src/zr/knockback.inc +++ b/src/zr/knockback.inc @@ -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(damagecustom & ZR_KNOCKBACK_CUSTOM); bool scale = custom && view_as(damagecustom & ZR_KNOCKBACK_SCALE); + bool limitforce = custom && view_as(damagecustom & ZR_KNOCKBACK_LIMITFORCE); bool limitvel = custom && view_as(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); } /** diff --git a/src/zr/tools_functions.inc b/src/zr/tools_functions.inc index 3d4db7a..44487ee 100644 --- a/src/zr/tools_functions.inc +++ b/src/zr/tools_functions.inc @@ -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.