Raised This Month: $12 Target: $400
 3% 

[TUT] Recording Damage/Hits Per-Round and Displaying It


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
wrecked_
Veteran Member
Join Date: Jan 2010
Location: New York (GMT-5)
Old 03-10-2010 , 18:59   [TUT] Recording Damage/Hits Per-Round and Displaying It
Reply With Quote #1

This tutorial has been written under the assumption that you are a professional Fortran programmer beginner to intermediate level AMXX scripter, but is written to be extremely newbie-friendly.

Alright, let's get straight to the point. The following will teach you how to create your own damage recording system that will reset every round. If you've ever played in ESEA,
this is extremely similar (if not exactly) like the .dmg command.


Let's begin with the top.
PHP Code:
#include <amxmodx>
#include <hamsandwich>

#define MAX_PLAYERS    32

#define IsPlayer(%1)    (1<=%1<=g_iMaxPlayers)

// Time after round start the stats reset
#define RESET_DELAY    5.0

/* To store the damage, we need 2
indexes to be saved in each variable */
new iDamageTaken[MAX_PLAYERS+1]/*took x damage from*/[MAX_PLAYERS+1]
new 
iDamageGiven[MAX_PLAYERS+1]/*did x damage to*/[MAX_PLAYERS+1]

new 
iHitsTaken[MAX_PLAYERS+1]/*took x hits from*/[MAX_PLAYERS+1]
new 
iHitsGiven[MAX_PLAYERS+1]/*did x hits to*/[MAX_PLAYERS+1]

new 
b_CanTypeWhileAlive

new g_iMaxPlayers 
Now, you'll see that we have our IsPlayer macro which will be explained a little later on.

We also have RESET_DELAY. This is the time after round start that the damage count will be reset.

Most importantly, we have our two variables that eventually will be holding the damage amount that tells the plugin how much damage was received and taken for each player. The two array sizes (MAX_PLAYERS+1) are necessary to hold player-specific data.
We also have the HitsGiven variable, pretty self-explanatory.

You will see b_CanTypeWhileAlive in use later on.
Finally, there's g_iMaxPlayers which will be used in accordance with IsPlayer.


Let's move on, shall we?
PHP Code:
public plugin_init()
{
    
register_plugin"Damage Saving Example""1.0""Wrecked" // :avast:
    
    
RegisterHamHam_TakeDamage"player""HAM_TakeDamage_Post")
    
    
register_clcmd"say .zomggaben""CMD_ShowDamage" )
    
    
register_logevent"LEV_RoundStart"2"1=Round_Start" )
    
    
g_iMaxPlayers get_maxplayers()

This one's pretty self-explanatory. We register the plugin, clcmd to check how much damage has been given, the TakeDamage event (which is used to store damage between the two players), and the round start.

g_iMaxPlayers obtains the maximum players, which will be used with IsPlayer, as said earlier.


Next!
PHP Code:
public HAM_TakeDamage_Postvictiminflictorattackerfloat:damagedamagebits )
{
    if( !
IsPlayerattacker ) ) // make sure it's not generic damage
        
return HAM_IGNORED;
        
    
iDamageTaken[victim][attacker] += pevvictimpev_dmg_take )
    
iDamageGiven[attacker][victim] += pevvictimpev_dmg_take )
    
    
iHitsTaken[victim][attacker]++ // adds 1
    
iHitsGiven[attacker][victim]++ // ...
    
    
return HAM_IGNORED;

Finally! Some real code!

First off, you'll notice the IsPlayer being put to use. This macro makes sure that the specified attacker is a player, and not the map or other entity. Without this check, there's a strong possibility that an excessive amount of babies problems would be created.
The macro up top makes sure the player is within the player range, since each player is given an imaginary number on connect (correct me if I'm wrong, anybody), and we check to see if they're between 0 and 32 (legitimate player range).

Next, we have our two variables. These two will be holding the amount of damage given and recieved. The two indexes hold the players involved in the damage.
Also, pev( victim, pev_dmg_take ) is getting the ACTUAL amount of damage taken after taken into consideration the armor and other factors of the victim (thanks to Connor, Arkshine, and Bugsy).

Finally, there's iHitsTaken and iHitsGiven to record the amount of hits given and taken, just to make your life more difficult.


Alright, the following two blocks of code are going to be a bit more difficult to comprehend, but I'll try my best to explain them in simple terms.
PHP Code:
public CMD_ShowDamageid )
{
    if( 
is_user_aliveid ) && !b_CanTypeWhileAlive )
    {
        
client_printidprint_chat"Hey! You can't do that right now!" )
        
        return 
PLUGIN_CONTINUE;
    }
    
    new 
iPlayers[32]
    new 
iNum
    
new plrid
    
    get_players
iPlayersiNum )
    
    static 
name[32]
    static 
message[108]
    
    new 
CsTeams:idteam cs_get_user_teamid )
    
    for( new 
0iNumi++ )
    {
        
plrid iPlayers[i]
        
        if( 
id == plrid || cs_get_user_teamplrid ) == idteam )
        {
            continue;
        }
        
        
get_user_nameplridname31 )
        
        
formatex(
        
message107"Given: ( %i Damage / %i Hits ) Taken: ( %i Damage / %i Hits )",
        
iDamageGiven[id][plrid],
        
iHitsGiven[id][plrid],
        
iDamageTaken[id][plrid],
        
iHitsTaken[id][plrid]
        )
        
        
client_printidprint_chat"[%s] %s"namemessage )
    }
    
    return 
PLUGIN_CONTINUE;

Alright, this is the function that's called when a user types .damage in chat. At the top, you can see that I check is_user_alive. If he IS alive, I tell him he can't do this right now, and then return PLUGIN_CONTINUE. What this does is stop the function from executing any further code in that function, but continue with normal operation. This means the chat message will be sent. If I wanted to block it, I'd return PLUGIN_HANDLED.

Now, the rest of the code may come off as a bit confusing if you're new. What I'm doing at first is getting the amount of players in-game and then declaring a new variable that will hold the name and message that will be sent.

I then initiate the for loop, declaring a new variable, making the conditional, and then the increment. This declares the new variable, then checks the conditional. If the conditional is true, it executes the code inside the block. After that, it executes the third param of the loop, and repeats from the 2nd param. When the conditional is false, it ends the for loop.
Inside the for loop, I check if the current player it's looping through is the player we're display it to, and if the player's team is the same as the player we're looping through. If it is, we continue to the next iteration (go back to the conditional) of the loop.

Otherwise, we get their name, and format a message that we'll be displaying to the player. It will look something like
Code:
[Wrecked] Given: ( 49 Damage / 2 Hits ) Taken: ( 100 Damage / 4 Hits )
If you're unfamiliar, %i tells the plugin to replace it with an integer, which is pointed out in the parameters after the message.
We then display the message to the user (%s means replace it with a string), and return PLUGIN_CONTINUE to let the say command run through and let everyone else see what the original client command was.


Phewf! Next!
PHP Code:
public LEV_RoundStart()
{
    
#if defined RESET_DELAY
        
set_taskRESET_DELAY"TASK_ResetStats" )
    
#else
        
TASK_ResetStats()
    
#endif
    
    
b_CanTypeWhileAlive true
}

public 
TASK_ResetStats()
{
    new 
iPlayers[32]
    new 
iNum
    
new plrid
    
new otherid
    
    get_players
iPlayersiNum )
    
    for( new 
0iNumi++ )
    {
        
plrid iPlayers[i]
        
        for( new 
0iNumj++ )
        {
            
otherid iPlayers[j]
            
            if( 
otherid == plrid )
            {
                continue;
            }
            
            
iDamageTaken[plrid][otherid] = 0
            iDamageGiven
[plrid][otherid] = 0
            
            iHitsTaken
[plrid][otherid] = 0
            iHitsGiven
[plrid][otherid] = 0
        
}
    }
    
    
b_CanTypeWhileAlive false

I'm going to assume you read the above example, and skip the explanation of for loops, since there's two of them in here and it'd be wasting both of our time.

The RoundStart function sets the delay to reset the player data if it's defined. If it's not, the function automatically calls TASK_ResetStats to reset the player damage / hit data.
The bool lets players type .damage until the player stats are reset in the following function.

So, as you can see, it's a quite extensive function, as it has 2 for loops, but it really isn't much code at all.
What I'm doing is;
  • Looping through each player
  • Looping through each player every time we loop through a different player (yes, very memory extensive, let me know if you have a better solution)
  • Resetting the damage and hit data in relation to each player with every bi-player combo possible.

So, we reset the player data in relation to the original player index with the new player index in relation to the secondary player index.

If you have any personal questions on this function, let me know and I'll explain it more in-depth.


So, our final plugin should look something like this:
PHP Code:
#include <amxmodx>
#include <hamsandwich>
#include <cstrike>

#define MAX_PLAYERS    32

#define IsPlayer(%1)    (1<=%1<=g_iMaxPlayers)

// Time after round start the stats reset
#define RESET_DELAY    5.0

/* To store the damage, we need 2
indexes to be saved in each variable */
new iDamageTaken[MAX_PLAYERS+1]/*took x damage from*/[MAX_PLAYERS+1]
new 
iDamageGiven[MAX_PLAYERS+1]/*did x damage to*/[MAX_PLAYERS+1]

new 
iHitsTaken[MAX_PLAYERS+1]/*took x hits from*/[MAX_PLAYERS+1]
new 
iHitsGiven[MAX_PLAYERS+1]/*did x hits to*/[MAX_PLAYERS+1]

new 
bool:b_CanTypeWhileAlive

new g_iMaxPlayers

public plugin_init()
{
    
register_plugin"Damage Saving Example""1.0""Wrecked" // :avast:
    
    
RegisterHamHam_TakeDamage"player""HAM_TakeDamage_Post")
    
    
register_clcmd"say .damage""CMD_ShowDamage" )
    
    
register_logevent"LEV_RoundStart"2"1=Round_Start" )
    
    
g_iMaxPlayers get_maxplayers()
}

public 
LEV_RoundStart()
{
    
#if defined RESET_DELAY
        
set_taskRESET_DELAY"TASK_ResetStats" )
    
#else
        
TASK_ResetStats()
    
#endif
    
    
b_CanTypeWhileAlive true
}
    

public 
TASK_ResetStats()
{
    new 
iPlayers[32]
    new 
iNum
    
new plrid
    
new otherid
    
    get_players
iPlayersiNum )
    
    for( new 
0iNumi++ )
    {
        
plrid iPlayers[i]
        
        for( new 
0iNumj++ )
        {
            
otherid iPlayers[j]
            
            if( 
otherid == plrid )
            {
                continue;
            }
            
            
iDamageTaken[plrid][otherid] = 0
            iDamageGiven
[plrid][otherid] = 0
            
            iHitsTaken
[plrid][otherid] = 0
            iHitsGiven
[plrid][otherid] = 0
        
}
    }
    
    
b_CanTypeWhileAlive false
}

public 
HAM_TakeDamage_Postvictiminflictorattackerfloat:damagedamagebits )
{
    if( !
IsPlayerattacker ) ) // make sure it's not generic damage
        
return HAM_IGNORED;
        
    
iDamageTaken[victim][attacker] += pevvictimpev_dmg_take // record the damage
    
iDamageGiven[attacker][victim] += pevvictimpev_dmg_take // ...
    
    
iHitsTaken[victim][attacker]++ // adds 1
    
iHitsGiven[attacker][victim]++ // ...
    
    
return HAM_IGNORED;
}

public 
CMD_ShowDamageid )
{
    if( 
is_user_aliveid ) && !b_CanTypeWhileAlive )
    {
        
client_printidprint_chat"Hey! You can't do that right now!" )
        
        return 
PLUGIN_CONTINUE;
    }
    
    new 
iPlayers[32]
    new 
iNum
    
new plrid
    
    get_players
iPlayersiNum )
    
    static 
name[32]
    static 
message[108]
    
    new 
CsTeams:idteam cs_get_user_teamid )
    
    for( new 
0iNumi++ )
    {
        
plrid iPlayers[i]
        
        if( 
id == plrid || cs_get_user_teamplrid ) == idteam )
        {
            continue;
        }
        
        
get_user_nameplridname31 )
        
        
formatex(
        
message107"Given: ( %i Damage / %i Hits ) Taken: ( %i Damage / %i Hits )",
        
iDamageGiven[id][plrid],
        
iHitsGiven[id][plrid],
        
iDamageTaken[id][plrid],
        
iHitsTaken[id][plrid]
        )
        
        
client_printidprint_chat"[%s] %s"namemessage )
    }
    
    return 
PLUGIN_CONTINUE;

Notes
  • The plugin has been compiled and tested, it works.
  • If you have any issues, questions, or errors, don't hesitate to message me or post in the thread.
  • I'm aware that creating a triearray for the values would be very efficient, but I felt triearrays would be a little controversial for new scripters and a little confusing. If you'd like an example with tri's, message me.


Alright, well, I'm off to go begin my new life as a lemon. Good bye.
__________________
[ Paid Requests ]
DO NOT PM ME ABOUT BLOCKMAKER
NO PRIVATE SUPPORT

Last edited by wrecked_; 03-31-2010 at 00:31.
wrecked_ is offline
stevenisecko138
Senior Member
Join Date: Dec 2008
Location: CA
Old 03-10-2010 , 19:01   Re: Recording Damage/Hits Per-Round and Displaying It
Reply With Quote #2

yes :p
stevenisecko138 is offline
Send a message via AIM to stevenisecko138
Seta00
The Seta00 user has crashed.
Join Date: Jan 2010
Location: Berlin
Old 03-10-2010 , 20:11   Re: Recording Damage/Hits Per-Round and Displaying It
Reply With Quote #3

Great job wrecked.
Seta00 is offline
ConnorMcLeod
Veteran Member
Join Date: Jul 2006
Location: France (95)
Old 03-11-2010 , 01:16   Re: Recording Damage/Hits Per-Round and Displaying It
Reply With Quote #4

Just to let you know that damage param passed in TakeDamage is not the real damage taken/given, to retrieve the real damage done, you have to check pev_dmg_take value in TakeDamage post registered.
You could also hook Damage event as well.
__________________
- tired and retired -

- my plugins -
ConnorMcLeod is offline
wrecked_
Veteran Member
Join Date: Jan 2010
Location: New York (GMT-5)
Old 03-11-2010 , 07:06   Re: Recording Damage/Hits Per-Round and Displaying It
Reply With Quote #5

Quote:
Originally Posted by ConnorMcLeod View Post
Just to let you know that damage param passed in TakeDamage is not the real damage taken/given, to retrieve the real damage done, you have to check pev_dmg_take value in TakeDamage post registered.
You could also hook Damage event as well.
Oh. What value does it hold then? I used the method that I have posted and it worked like a charm.
__________________
[ Paid Requests ]
DO NOT PM ME ABOUT BLOCKMAKER
NO PRIVATE SUPPORT
wrecked_ is offline
ConnorMcLeod
Veteran Member
Join Date: Jul 2006
Location: France (95)
Old 03-11-2010 , 11:35   Re: Recording Damage/Hits Per-Round and Displaying It
Reply With Quote #6

Quote:
Originally Posted by wrecked_ View Post
I used the method that I have posted and it worked like a charm.
It work only with few damage types as DMG_FALL, because original damage is equal to final one, but for bullet dmg it's always different.
Also, csx should provide some natives that can give you all information.
__________________
- tired and retired -

- my plugins -
ConnorMcLeod is offline
Arkshine
AMX Mod X Plugin Approver
Join Date: Oct 2005
Old 03-11-2010 , 09:31   Re: Recording Damage/Hits Per-Round and Displaying It
Reply With Quote #7

It holds the original damage value. pev_dmg_take is the final damage after considering armor and other things.
__________________
Arkshine is offline
wrecked_
Veteran Member
Join Date: Jan 2010
Location: New York (GMT-5)
Old 03-11-2010 , 15:13   Re: Recording Damage/Hits Per-Round and Displaying It
Reply With Quote #8

Oh, interesting, I'll look into that pev.

Thanks guys.
__________________
[ Paid Requests ]
DO NOT PM ME ABOUT BLOCKMAKER
NO PRIVATE SUPPORT
wrecked_ is offline
ConnorMcLeod
Veteran Member
Join Date: Jul 2006
Location: France (95)
Old 03-11-2010 , 18:09   Re: [TUT] Recording Damage/Hits Per-Round and Displaying It
Reply With Quote #9

pev_dmg_take is normally a float, but you can retrieve its integer value using it as if it was an integer, pev(victim, pev_dmg_take) will work fine.
Also, if you look at HLSDK, TakeDamage returns the damage done, but it's not the case on counter-strike.
You could extend your snippet to specific weapon damage save ;)

But first, double check that you can't achieve this with csx module, because you would make it for nothing.
__________________
- tired and retired -

- my plugins -
ConnorMcLeod is offline
flamin
BANNED
Join Date: Jul 2009
Location: Los Teques
Old 03-11-2010 , 19:54   Re: [TUT] Recording Damage/Hits Per-Round and Displaying It
Reply With Quote #10

hi! wrecked_ I have tried to implement this code in the yap mode but i did not succeed when you start a new round tells me that i have done no harm in that round you puts my code help please!

PHP Code:
#include <amxmodx>
#include <amxmisc>

#include <yap_modspecific>
#include <yap_forwards>
#include <yap_stocks>
#include <yap_const>

new const plugin_author[] = "PsychO"
new const plugin_name[] = "YAP-AUX"
new const plugin_version[] = "0.0.1"

public pug_cvar_help
public pug_cvar_admin_help
#define RESET_DELAY    15.0 // Changed from 5.0 to 15.0

public plugin_init()
{
    
register_plugin(plugin_name,plugin_version,plugin_author);

    
register_dictionary("yap.txt")
    
register_dictionary("yap_aux.txt")

    
pug_cvar_help register_cvar("pug_help","http://")
    
pug_cvar_admin_help register_cvar("pug_adminhelp","http://")

    
register_pug_admincmd("help","cmd_cmdlist",_,"Lista de todos los comandos PUG")

    
register_pug_clcmd("hp","cmd_hpteam",_,"Muestra el hp de los oponentes")

    
register_pug_clcmd("dmg","cmd_dmg",_,"Muestra el daņo que causastes y el que te causo el oponente")
    
register_pug_clcmd("rdmg","cmd_dmg",_,"Muestra el daņo que causastes y el que te causo el oponente")

    
register_pug_admincmd("svrestart","cmd_restart",PUG_CMD_LVL,"Solo para ADMIN")
    
register_logevent"LEV_RoundStart"2"1=Round_Start" )

    
//parse_header("BOE-E",pug_header,5)
}

// Rukia: We need to reset the dmg and hits when a client connects, disconnects, and rounds start
new pug_dmg[33][33]
new 
pug_hits[33][33]

public 
client_connect(id
{
    
arrayset(pug_dmg[id], 033); 
    
arrayset(pug_hits[id], 033);
}

public 
client_disconnect(id)
{
    for (new 
033i++)
    {
        
pug_dmg[i][id] = 0;
        
pug_hits[i][id] = 0;
    }
}

public 
pug_round_start()
{
    for (new 
033; ++i)
    {
        
arrayset(pug_dmg[i], 033)    
        
arrayset(pug_hits[i], 033)    
    }
}

public 
pug_round_start_failed()
{
    for (new 
033; ++i)
    {
        
arrayset(pug_dmg[i], 033)    
        
arrayset(pug_hits[i], 033)    
    }
}

// Rukia: Assume that the mod specific plugin has loaded the correct module, or provided this for us
public  client_damage attackervictimamountwpnindexhitplaceTA )
{
    
//if (attacker == victim) return;

    
pug_dmg[attacker][victim] += amount
    pug_hits
[attacker][victim] += 1
}

#include <yap_aux>

public LEV_RoundStart()
{
    
#if defined RESET_DELAY
        
set_taskRESET_DELAY"TASK_ResetStats" )
    
#else
        
TASK_ResetStats()
    
#endif
    
    
b_CanTypeWhileAlive true
}

public 
TASK_ResetStats()
{
    new 
iPlayers[32]
    new 
iNum
    
new player // Changed from plrid to player    
    
new id // Changed from otherid to id
    
    
get_playersiPlayersiNum )
    
    for( new 
0iNumi++ )
    {
        
player iPlayers[i]
        
        for( new 
0iNumj++ )
        {
            
id iPlayers[j]
            
            if( 
otherid == plrid )
            {
                continue;
            }
            
            
pug_dmg[id][player] = // Changed from you code to this one
        
pug_dmg[player][id] = 
            
            pug_hits
[id][player] = 0
        pug_hits
[player][id] = 0
        
}
    }
    
    
b_CanTypeWhileAlive false
}  

public 
cmd_dmg(id)
{
    if(
is_user_alive(id) && id != && !b_CanTypeWhileAlive pug_msg_tmp_empty(id,"PUG_CMD_NOTALLOWED");
    else
    {
        static 
Players[32], name[32]
        new 
playerCountiplayer
        get_players
(PlayersplayerCount"ch")

        new 
tmp_hitstmp_dmgtmp_rhitstmp_rdmgcheck
        
for (i=0i<playerCounti++)
        {
            
player Players[i]
            
tmp_hits pug_hits[id][player]
            
tmp_rhits pug_hits[player][id]

            if( 
tmp_hits || tmp_rhits )
            {
                
check 1
                tmp_dmg 
pug_dmg[id][player]
                
tmp_rdmg pug_dmg[player][id]

                if(
player == idclient_print(id,print_chat,"%s %L",pug_header,id,"PUG_AUX_DMG_SELF",tmp_dmg,tmp_hits)
                else
                {
                    
get_user_name(player,name31)
                    
client_print(id,print_chat,"%s %L",pug_header,id,"PUG_AUX_DMG",tmp_dmg,tmp_hits,tmp_rdmg,tmp_rhitsname)
                }
            }
          }

        
tmp_hits pug_hits[id][0]
        
tmp_rhits pug_hits[0][id]
        if( 
tmp_hits || tmp_rhits )
        {
            
tmp_dmg pug_dmg[id][0]
            
tmp_rdmg pug_dmg[0][id]
            
client_print(id,print_chat,"%s %L",pug_header,id,"PUG_AUX_DMG","WorldSpawn",tmp_hits,tmp_dmg)
        }
        else if(!
checkclient_print(id,print_chat,"%s %L",pug_header,id,"PUG_AUX_NODMG")
    }
    return 
PLUGIN_HANDLED

You leave my sma code without amendments so you can look out for better as could adjust your code to my code?
Attached Files
File Type: sma Get Plugin or Get Source (yap_aux.sma - 1134 views - 8.8 KB)
flamin is offline
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 08:38.


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