AlliedModders

AlliedModders (https://forums.alliedmods.net/index.php)
-   Scripting (https://forums.alliedmods.net/forumdisplay.php?f=107)
-   -   Solved [L4D] How accurately calculate tank damage with SDKHook? (https://forums.alliedmods.net/showthread.php?t=332360)

Dragokas 05-07-2021 08:16

[L4D] How accurately calculate tank damage with SDKHook?
 
Hi,

I want to get damage deal to tank by player (solo) using SDKHook method.

Let's say tank hp is exactly 1000:

Code:

sm_cvar z_tank_health 1000
There are no bots. You are using a pump shotgun.

PHP Code:

#pragma semicolon 1
#pragma newdecls required

#include <sourcemod>
#include <sdktools>
#include <sdkhooks>

float         g_fDamage[MAXPLAYERS+1][MAXPLAYERS+1];
bool         g_bIncapped[MAXPLAYERS+1];

public 
void OnPluginStart()
{
    
HookEvent("tank_spawn",                 Event_TankSpawn);
    
HookEvent("tank_killed",                 Event_TankKilled);
    
HookEvent("player_incapacitated",         Event_Incap,        EventHookMode_Pre);
}

public 
Action Event_TankSpawn(Event eventchar[] namebool dontBroadcast)
{
    
int tank GetClientOfUserId(event.GetInt("userid"));
    if( 
tank && IsClientInGame(tank) )
    {
        
TraceTank(tank);
    }
}

void TraceTank(int tank)
{
    
g_bIncapped[tank] = false;
    
    for( 
int i 1<= MaxClientsi++ )
    {
        
g_fDamage[i][tank] = 0.0;
    }
    
SDKHook(tankSDKHook_OnTakeDamageOnTakeDamage);
    
    
//SDKHook(tank, SDKHook_OnTakeDamageAlive, OnTakeDamage); // alternate
}

public 
Action OnTakeDamage(int victimint &attackerint &inflictorfloat &damageint &damagetypeint &weaponfloat damageForce[3], float damagePosition[3])
{
    if( 
GetEntProp(victimProp_Send"m_isIncapacitated") )
        return 
Plugin_Continue;
    
    if( 
g_bIncapped[victim] ) // alternate
    
{
        return 
Plugin_Continue;
    }

    if( 
attacker <= MaxClients )
    {
        
g_fDamage[attacker][victim] += damage;
    }
    return 
Plugin_Continue;
}

public 
Action Event_Incap(Event eventchar[] namebool dontBroadcast)
{
    
int victim GetClientOfUserId(event.GetInt("userid"));
    
g_bIncapped[victim] = true;
}

public 
Action Event_TankKilled(Event eventchar[] namebool dontBroadcast)
{
    
int victim GetClientOfUserId(event.GetInt("userid"));
    if( !
victim )
    {
        return 
Plugin_Continue;
    }
    for( 
int i 1<= MaxClientsi++ )
    {
        if( 
IsClientInGame(i) && !IsFakeClient(i) )
        {
            
PrintToChatAll("Damage done: %f"g_fDamage[i][victim]);
        }
    }
    return 
Plugin_Continue;


With OnTakeDamage (+ incap filter), total damage solo == 1032 ± 6.
With OnTakeDamageAlive (+ incap filter), total damage solo == 990 ± 6.

How to get exactly 1000 ?

Thanks.

-
PS. Just if somebody don't know, before tank die, incap animation is played, and tank still receiving damage, that's why incap filter is required.

Marttt 05-07-2021 10:59

Re: [L4D] How accurately calculate tank damage with SDKHook?
 
Try checking if "damage > tank health" in the "last hit" then subtract.

Lux 05-07-2021 11:07

Re: [L4D] How accurately calculate tank damage with SDKHook?
 
Use the player_hurt event may give better results.

Or

You can patch out the CTerrorPlayer::OnIncapacitatedAsTank in CTerrorPlayer::AllowDamage and let the tank just die :P

Dragokas 05-07-2021 12:28

Re: [L4D] How accurately calculate tank damage with SDKHook?
 
Thank you, Lux.

Quote:

Originally Posted by Lux (Post 2746208)
Use the player_hurt event may give better results.

"player_hurt" still returns sometimes 984, but sometimes even 999.

PHP Code:

public void OnPluginStart()
{
...
    
HookEvent("player_hurt",                 Event_PlayerHurt);
...
}

public 
Action Event_PlayerHurt(Event eventchar[] namebool dontBroadcast)
{
    
int victim GetClientOfUserId(event.GetInt("userid"));
    
int attacker GetClientOfUserId(event.GetInt("attacker"));
    
float damage event.GetFloat("dmg_health");
    
    
/*
    if( GetEntProp(victim, Prop_Send, "m_isIncapacitated") )
        return Plugin_Continue;
    */
    
    
if( g_bIncapped[victim] ) // alternate
    
{
        return 
Plugin_Continue;
    }
    
    if( 
attacker <= MaxClients && victim <= MaxClients )
    {
        
g_fDamage[attacker][victim] += damage;
    }


Quote:

Originally Posted by Lux (Post 2746208)
You can patch out the CTerrorPlayer::OnIncapacitatedAsTank in CTerrorPlayer::AllowDamage and let the tank just die :P

If you mean NOP, patching those made tank invulnerable :D

Quote:

Originally Posted by Marttt (Post 2746206)
Try checking if "damage > tank health" in the "last hit" then subtract.

Thank you, Marttt.
In incap state tank still receiving a lot of "1.0" damage. Last damage value == 5000.0
So, if I remove incap filter, total damage will be like > 7.000 (with the start tank hp == 1000.0).

Dragokas 05-07-2021 12:43

Re: [L4D] How accurately calculate tank damage with SDKHook?
 
---

Dragokas 05-07-2021 13:16

Re: [L4D] How accurately calculate tank damage with SDKHook?
 
Marttt, nice, I undertood what you suggested.

I did some tests, and now I know how it works.

When you do the last shoot (before incap), the damage is > than tank hp elapsed.
This is where incap event is fired, but OnTankDamage (or "player_hurt") are not called before incap. It is called after incap. And even more, the damage is passed as full, which is > tank hp elapsed at the last moment of its life.
Also, when OnTakeDamage is fired, the float damage is rounding to zero (even if value is 0.9), and those value is get written to "m_iHealth" prop of tank's entity.

With the proper calculations we are receiving the correct final value, using SDKHook_OnTakeDamage:

PHP Code:

public Action OnTakeDamage(int victimint &attackerint &inflictorfloat &damageint &damagetypeint &weaponfloat damageForce[3], float damagePosition[3])
{
    if( 
g_bDied[victim] )
    {
        return 
Plugin_Continue;
    }
    
    
int health GetEntProp(victimProp_Data"m_iHealth");
    if( 
RoundToFloor(damage) >= health )
    {
        
PrintToChatAll("incap. Damage = %f. Health: %i"damagehealth);
        
damage float(health);
        
g_bDied[victim] = true;
    }
    
    
times ++;
    
    if( 
attacker <= MaxClients )
    {
        
g_fDamage[attacker][victim] += RoundToFloor(damage);
        
PrintToChatAll("[%f] [%i] damage = %f. Health: %i"g_fDamage[attacker][victim], timesdamageGetEntProp(victimProp_Data"m_iHealth") );
    }
    return 
Plugin_Continue;


Quote:

// For sm_cvar z_tank_health 100

[22.000000] [1] damage = 22.803766. Health: 100
[44.000000] [2] damage = 22.806159. Health: 78
[66.000000] [3] damage = 22.811071. Health: 56
[89.000000] [4] damage = 23.212322. Health: 34
incap. Damage = 22.794973. Health: 11
[100.000000] [5] damage = 11.000000. Health: 11
Damage done: 100.000000
Topic solved.

Dragokas 05-07-2021 13:56

Re: [L4D] How accurately calculate tank damage with SDKHook?
 
Just as a final, for somebody else.

SDKHook version:

PHP Code:

#pragma semicolon 1
#pragma newdecls required

#include <sourcemod>
#include <sdktools>
#include <sdkhooks>

int         g_iDamage[MAXPLAYERS+1][MAXPLAYERS+1];
bool         g_bDied[MAXPLAYERS+1];

int times;

public 
void OnPluginStart()
{
    
HookEvent("tank_spawn",                 Event_TankSpawn);
    
HookEvent("tank_killed",                 Event_TankKilled);
}

public 
Action Event_TankSpawn(Event eventchar[] namebool dontBroadcast)
{
    
int tank GetClientOfUserId(event.GetInt("userid"));
    if( 
tank && IsClientInGame(tank) )
    {
        
TraceTank(tank);
    }
}

void TraceTank(int tank)
{
    
g_bDied[tank] = false;
    
    for( 
int i 1<= MaxClientsi++ )
    {
        
g_iDamage[i][tank] = 0;
    }
    
times 0;
    
    
SDKHook(tankSDKHook_OnTakeDamageOnTakeDamage);
    
    
//Seemse, not useul at all
    //SDKHook(tank, SDKHook_OnTakeDamageAlive, OnTakeDamage); // alternate
}

public 
Action OnTakeDamage(int victimint &attackerint &inflictorfloat &damageint &damagetypeint &weaponfloat damageForce[3], float damagePosition[3])
{
    if( 
g_bDied[victim] )
    {
        return 
Plugin_Continue;
    }
    
    
int health GetEntProp(victimProp_Data"m_iHealth");
    if( 
RoundToFloor(damage) >= health )
    {
        
PrintToChatAll("incap. Damage = %f. Health: %i"damagehealth);
        
damage float(health);
        
g_bDied[victim] = true;
    }
    
    
times ++;
    
    if( 
attacker <= MaxClients )
    {
        
g_iDamage[attacker][victim] += RoundToFloor(damage);
        
PrintToChatAll("[%i] [%i] damage = %i. Health: %i"g_iDamage[attacker][victim], timesRoundToFloor(damage), GetEntProp(victimProp_Data"m_iHealth") );
    }
    return 
Plugin_Continue;
}

public 
Action Event_TankKilled(Event eventchar[] namebool dontBroadcast)
{
    
int victim GetClientOfUserId(event.GetInt("userid"));
    if( !
victim )
    {
        return 
Plugin_Continue;
    }
    for( 
int i 1<= MaxClientsi++ )
    {
        if( 
IsClientInGame(i) && !IsFakeClient(i) )
        {
            
PrintToChatAll("Damage done: %i"g_iDamage[i][victim]);
        }
    }
    return 
Plugin_Continue;


"player_hurt" event version:

PHP Code:

#pragma semicolon 1
#pragma newdecls required

#include <sourcemod>
#include <sdktools>

int         g_iDamage[MAXPLAYERS+1][MAXPLAYERS+1];
int         g_iLastAttacker[MAXPLAYERS+1];
bool         g_bDied[MAXPLAYERS+1];
float        g_fLastHP[MAXPLAYERS+1];

int times;

public 
void OnPluginStart()
{
    
HookEvent("tank_spawn",                 Event_TankSpawn);
    
HookEvent("tank_killed",                 Event_TankKilled);
    
HookEvent("player_hurt",                 Event_PlayerHurt,    EventHookMode_Pre);
}

public 
Action Event_TankSpawn(Event eventchar[] namebool dontBroadcast)
{
    
int tank GetClientOfUserId(event.GetInt("userid"));
    if( 
tank && IsClientInGame(tank) )
    {
        
TraceTank(tank);
    }
}

void TraceTank(int tank)
{
    
g_bDied[tank] = false;
    
g_fLastHP[tank] = float(GetEntProp(tankProp_Data"m_iHealth"));
    
    for( 
int i 1<= MaxClientsi++ )
    {
        
g_iDamage[i][tank] = 0;
    }
    
times 0;
}

public 
Action Event_PlayerHurt(Event eventchar[] namebool dontBroadcast)
{
    
int victim GetClientOfUserId(event.GetInt("userid"));
    
int attacker GetClientOfUserId(event.GetInt("attacker"));
    
float damage event.GetFloat("dmg_health");
    
    if( 
g_bDied[victim] )
    {
        return 
Plugin_Continue;
    }
    
    if( 
GetEntProp(victimProp_Send"m_isIncapacitated") )
    {
        
g_bDied[victim] = true;
        
damage g_fLastHP[victim];
        if( !
attacker attacker g_iLastAttacker[victim];
        
PrintToChatAll("incap. dmg: %f. att: %i. vic: %i"damageattackervictim);
    }
    
    
g_fLastHP[victim] = event.GetFloat("health");
    
g_iLastAttacker[victim] = attacker;
    
    
times ++;
    
    if( 
attacker <= MaxClients && victim <= MaxClients )
    {
        
g_iDamage[attacker][victim] += RoundToFloor(damage);
        
PrintToChatAll("[%i] [%i] damage = %i. Health: %f"g_iDamage[attacker][victim], timesRoundToFloor(damage), event.GetFloat("health") );
    }
    
    return 
Plugin_Continue;
}

public 
Action Event_TankKilled(Event eventchar[] namebool dontBroadcast)
{
    
int victim GetClientOfUserId(event.GetInt("userid"));
    if( !
victim )
    {
        return 
Plugin_Continue;
    }
    for( 
int i 1<= MaxClientsi++ )
    {
        if( 
IsClientInGame(i) && !IsFakeClient(i) )
        {
            
PrintToChatAll("Damage done: %i"g_iDamage[i][victim]);
        }
    }
    return 
Plugin_Continue;


Event Hook version is much more complicated, because hook is called after health is already set (even with Pre-mode). Even more, after incap, hurt event is called with 0 attacker index. So, both attacker and hp are required to be pre-saved beforehand to get a reliable results.

cravenge 05-07-2021 20:20

Re: [L4D] How accurately calculate tank damage with SDKHook?
 
The SDKHook version should return Plugin_Changed when the damage gets modified so it would also reflect on the scoreboard when transitioning between maps.

Dragokas 05-08-2021 08:48

Re: [L4D] How accurately calculate tank damage with SDKHook?
 
cravenge, sorry, I don't understand the relationship to my topic. I don't change damage, only measure.

cravenge 05-09-2021 05:12

Re: [L4D] How accurately calculate tank damage with SDKHook?
 
Quote:

Originally Posted by Dragokas (Post 2746272)
cravenge, sorry, I don't understand the relationship to my topic. I don't change damage, only measure.

Yes you aren't but what I suggested is merely for consistency and probably cosmetic too if I am to be honest.


All times are GMT -4. The time now is 13:10.

Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.