I am working on writing in some new functionality for a stat tracking plugin for one of our 10 man servers. Currently, we can track the basics like kills, deaths, score, assists, steamid and so on.
I am working on adding in a couple stats:
RWS/Rating (we have a mathematical formula we are following) but this requires us to calculate stuff like 2k, 3k, 4k, 5k in a round. Is the best way to do this to run: m_iNumRoundKills every round end and update columns for players in the match based on the number of kills they had for that round?
Also if we wanted to track clutch kills how would I go about that? I can't find any good property for doing that. How do I determine when it is a clutch situation code wise. I know that if Team A Player Count > Team B Player count AND Team B Player Count = 1 then it is a clutch situation for team B. How do I do that in the code though because I would have to mid round know that there is a clutch situation, after the round I wouldn't be able to tell because the round would be Team A alive = 0, Team B alive = 1 and I wouldn't know how many Team A were killed.
I am fairly new to sourcepawn and all the properties are very confusing as they are not well documented in any 1 spot I can find and half the time if I implement one it doesn't work and I have to use trial and error to find working properties.
I am working off an existing plugin and just adding bits to it here and there, so far all that I have tried has worked but now that I am getting into more round specific stats it is getting more difficult. I assume for round end stats like rws I would use a listener for round end and update after that.
public void PlayerDeath(Event event, const char[] name, bool dontBroadcast)
{
int client = GetClientOfUserId(event.GetInt("userid"));
int attacker = GetClientOfUserId(event.GetInt("attacker"));
if (attacker && client && attacker != client)
{
g_RoundKills[attacker]++;
if (IsInClutchSituation(attacker))
{
g_ClutchKills[attacker]++;
}
}
}
bool IsInClutchSituation(int client)
{
int clientTeam = GetClientTeam(client);
for (int i = 1; i <= MaxClients; i++)
{
if (IsClientInGame(i))
{
if (i != client && GetClientTeam(i) == clientTeam)
{
if (IsPlayerAlive(i))
{
return false; // we found another player alive in client's team
}
}
}
}
return true; // if he's alone in his team or he's the last player alive in his team - clutch situation. You can improve this code.
}
public void RoundStart(Event event, const char[] name, bool dontBroadcast)
{
for (int i = 1; i <= MaxClients; i++)
{
g_RoundKills[i] = 0; // when a new round starts, we reset the array
}
}
public void RoundEnd(Event event, const char[] name, bool dontBroadcast)
{
for (int i = 1; i <= MaxClients; i++)
{
if (IsClientConnected(i))
{
switch (g_RoundKills[i])
{
case 2: // 2k
case 3: // 3k
case 4: // 4k
case 5: g_Aces[i]++; //ace
}
}
}
}
code and add RWS after the switch? That would work correct? Also just for my own clarification what is the difference between the i_ and g_ properties? I am very new to writing even snippets of plugins, I have a big list of properties but I have never seen g_XYZ. Just asking for my own understanding.
code and add RWS after the switch? That would work correct? Also just for my own clarification what is the difference between the i_ and g_ properties? I am very new to writing even snippets of plugins, I have a big list of properties but I have never seen g_XYZ. Just asking for my own understanding.
A few notes regarding Ilusion9's code, which he graciously provided you:
IsInClutchSituation will need to verify the round hasnt ended already (during the time between round end and start of next round). They didn't clutch if the kills are after round end and/or they lost the round.
Additionally, IsInClutchSituation will need to verify there are players alive on the other team. While checking that, you might as well count them to verify if 5v1, 4v1, etc.
Since you are already hooking the player death event, you should manually count the deaths by incrementing client variables in the hook. The other methodology would miss kills that happen after the round.
To your later question, all coders have different styles of formatting, naming conventions, etc. It is common to name variables so that they are self-identifying by the label. For variables that are outside of functions (i.e. they are "global" variables), many coders, myself included, start the variable name with "g_". Personally, I always put a lower case letter before the label indicating what type of variable it is, then add g_ for global, a_ for arraylists, ga_ for global arraylists, etc. So, when I see a variable in my code that is ga_iSomeClientVariable, I instantly know that it is a global arraylist of integers that I've named "SomeClientVariable". Styles/conventions can vary significantly across different coders.
Here is some code snippets from one of my plugins in how I addressed clutches:
public Action Event_PlayerDeath(Event hEvent, const char[] sName, bool bDontBroadcast)
{
if(IsMatchLive())
{
int victim = GetClientOfUserId(hEvent.GetInt("userid"));
int killer = GetClientOfUserId(hEvent.GetInt("attacker"));
if(killer != victim)
{
if(IsValidClient(victim) && IsValidClient(killer))
{
if(GetClientTeam(victim) == GetClientTeam(killer)) //if TK
{
//Do Stuff
}
else //valid kill against other team
{
//Do Stuff to increment their stats
}
//Do Stuff to increment their stats
}
}
else //suicide
{
//Do Stuff
}
CheckForClutch();
}
return Plugin_Continue;
}
void CheckForClutch()
{
int iAliveCTs, iAliveTs, iT, iCT, iClutchPlayer;
LoopValidPlayers(i) //Count alive players on each team
{
int iTeam = GetClientTeam(i);
if(IsPlayerAlive(i))
{
if(iTeam == 2)
{
iAliveTs++;
iT = i; //store alive T for possible new clutch
}
else if(iTeam == 3)
{
iAliveCTs++;
iCT = i; //store alive CT for possible new clutch
}
if(ga_iInClutch[i])
{
iClutchPlayer = i; //store player already noted as in clutch situation
}
}
else //player is dead
{
if(ga_iInClutch[i])
{
if(g_bBombPlanted)
{
if(iTeam != 2)
{
ga_iInClutch[i] = CLUTCH_NONE;
return; //in clutch, therefore they are last alive and died. Not terr, so no bomb plant win. No need to continue - no clutch can result from round
}
}
else
{
ga_iInClutch[i] = CLUTCH_NONE;
return; //in clutch, therefore they are last alive and died. no bomb plant win. No need to continue - no clutch can result from round
}
}
}
}
if(iClutchPlayer) //existing clutch situation in progress
{
int iTeam = GetClientTeam(iClutchPlayer);
if(iTeam == 2) //player is on T
{
if(!iAliveCTs) //All T's dead - clutch successful
{
//Do Stuff to increment their stats
ga_iInClutch[iClutchPlayer] = CLUTCH_NONE;
}
}
else if(iTeam == 3) //player is on CT
{
if(!iAliveTs) //All Ts dead
{
if(!g_bBombPlanted) //Bomb not planted - clutch successful
{
switch(ga_iInClutch[iClutchPlayer])
{
//Do Stuff to increment their stats
ga_iInClutch[iClutchPlayer] = CLUTCH_NONE;
}
}
}
}
}
else if((iAliveTs == 1) && (iAliveCTs >= 3) && !g_bRoundEnded) //check for new clutch situation of at least 1v3
{
switch(iAliveCTs)
{
case 5:
{
ga_iInClutch[iT] = CLUTCH_5v1;
}
case 4:
{
ga_iInClutch[iT] = CLUTCH_4v1;
}
case 3:
{
ga_iInClutch[iT] = CLUTCH_3v1;
}
}
}
else if((iAliveCTs == 1) && (iAliveTs >= 3) && !g_bRoundEnded) //check for new clutch situation of at least 1v3
{
switch(iAliveTs)
{
case 5:
{
ga_iInClutch[iCT] = CLUTCH_5v1;
}
case 4:
{
ga_iInClutch[iCT] = CLUTCH_4v1;
}
case 3:
{
ga_iInClutch[iCT] = CLUTCH_3v1;
}
}
}
}
public Action Event_BombExplode(Event hEvent, const char[] name, bool dontBroadcast)
{
int client = GetClientOfUserId(hEvent.GetInt("userid"));
if(IsValidClient(client))
{
if(IsMatchLive())
{
//if(!g_bRoundEnded) //commented out due to possibly firing after round end even when CTs still alive - isnt needed anyways, since they already clutched if CT killed while "in clutch"
//{
if(ga_iInClutch[client]) //clutch successful
{
//Do Stuff to increment their stats
}
//}
}
}
LoopValidPlayers(i) //no additional clutches in round, even if other team killed
{
ga_iInClutch[i] = CLUTCH_NONE;
}
return Plugin_Continue;
}
public Action Event_Defused(Event hEvent, const char[] name, bool dontBroadcast)
{
int client = GetClientOfUserId(hEvent.GetInt("userid"));
if(IsValidClient(client))
{
if(IsMatchLive())
{
gaa_iStats[client][STATS_DEFUSES]++;
gaa_iStats[client][STATS_TOTALPOINTS] += g_cPtsDefuse.IntValue;
if(ga_iInClutch[client]) //clutch successful
{
//Do Stuff to increment their stats
}
}
}
LoopValidPlayers(i) //no additional clutches in round
{
ga_iInClutch[i] = CLUTCH_NONE;
}
Side note: I'm currently separating the stats component from my scrim plugin, then personalizing it for a community's specific wants. PM me if interested in a non-personalized version.
Additionally, IsInClutchSituation will need to verify there are players alive on the other team. While checking that, you might as well count them to verify if 5v1, 4v1, etc.
If there are not enemies alive, then player_death will not be triggered, unless there's a team kill or self kill. This is useless and a waste of time.