diff --git a/doc/action.md b/doc/action.md index 97609d603..03df86704 100644 --- a/doc/action.md +++ b/doc/action.md @@ -767,6 +767,8 @@ For this, TNG has two new commands. ### Statistics To see how well players are doing, we have implemented statistics into TNG. This feature will track various statistics of each player, for example accuracy or frags per minute. Tracked stats include shots fired and hits for all weapons, including punch attacks and grenade throws. +In matchmode, FPM is calculated using active match play time rather than total connected time. Only frames where the player is on a team and not a substitute count toward the FPM denominator. This means connecting early during warmup, spectating, or being a sub does not inflate your play time. In non-matchmode games, FPM uses total connected time as before. + **Commands:** - `stats_endmap [0/1]` - when set to 1, this will display the stats scoreboard at the end of the map instead of the normal screen. - `stats_afterround [0/1]` - when on (1), this will also record stats for events that happen when a round ends, for example when ff_afterround or FF is on. diff --git a/src/action/a_cmds.c b/src/action/a_cmds.c index f66655a54..900d99fbb 100644 --- a/src/action/a_cmds.c +++ b/src/action/a_cmds.c @@ -1314,6 +1314,7 @@ void Cmd_Ghost_f(edict_t * ent) ent->client->resp.team_kills = ghost->team_kills; ent->client->resp.streakKillsHighest = ghost->streakKillsHighest; ent->client->resp.streakHSHighest = ghost->streakHSHighest; + ent->client->resp.active_frames = ghost->active_frames; if (teamplay->value && ghost->team && ghost->team != ent->client->resp.team) JoinTeam( ent, ghost->team, 1 ); diff --git a/src/action/g_local.h b/src/action/g_local.h index 409384ad5..f9e8b2528 100644 --- a/src/action/g_local.h +++ b/src/action/g_local.h @@ -2035,6 +2035,7 @@ typedef struct int ctf_lastfraggedcarrier; int joined_team; // last frame # at which the player joined a team + int active_frames; // frames actively playing on a team during a match int lastWave; //last time used wave radio_t radio; @@ -2888,6 +2889,7 @@ typedef struct int team_kills; int streakKillsHighest; int streakHSHighest; + int active_frames; } gghost_t; diff --git a/src/action/g_main.c b/src/action/g_main.c index 3077f82cc..a7d84401e 100644 --- a/src/action/g_main.c +++ b/src/action/g_main.c @@ -1403,6 +1403,9 @@ void G_RunFrame (void) ClientBeginServerFrame (ent); + if (team_game_going && ent->client->resp.team != NOTEAM && !ent->client->resp.subteam) + ent->client->resp.active_frames++; + #ifndef NO_BOTS // allow bots to think if(!ent->is_bot) diff --git a/src/action/p_client.c b/src/action/p_client.c index df182179f..0631d54bf 100644 --- a/src/action/p_client.c +++ b/src/action/p_client.c @@ -4047,6 +4047,7 @@ void CreateGhost(edict_t * ent) ghost->team_kills = ent->client->resp.team_kills; ghost->streakKillsHighest = ent->client->resp.streakKillsHighest; ghost->streakHSHighest = ent->client->resp.streakHSHighest; + ghost->active_frames = ent->client->resp.active_frames; // Teamplay variables if (teamplay->value) { diff --git a/src/action/tng_stats.c b/src/action/tng_stats.c index 4a0f23029..f5621c51c 100644 --- a/src/action/tng_stats.c +++ b/src/action/tng_stats.c @@ -436,7 +436,9 @@ void A_ScoreboardEndLevel (edict_t * ent, edict_t * killer) else accuracy = 0; - secs = (level.framenum - cl->resp.enterframe) / HZ; + secs = cl->resp.active_frames > 0 + ? cl->resp.active_frames / HZ + : (level.framenum - cl->resp.enterframe) / HZ; if (secs > 0) fpm = (double)cl->resp.score * 60.0 / (double)secs; else @@ -738,7 +740,9 @@ void G_RegisterScore(void) if (roundbased && game.roundNum > 0) { fragsper = c->resp.score / game.roundNum; } else { - sec = (level.framenum - c->resp.enterframe) / HZ; + sec = c->resp.active_frames > 0 + ? c->resp.active_frames / HZ + : (level.framenum - c->resp.enterframe) / HZ; if (!sec) sec = 1; fragsper = c->resp.score * 3600 / sec; @@ -1418,7 +1422,9 @@ void WriteLogEndMatchStats(gclient_t *cl) char msg[1024]; shots = min( cl->resp.shotsTotal, 9999 ); - secs = (level.framenum - cl->resp.enterframe) / HZ; + secs = cl->resp.active_frames > 0 + ? cl->resp.active_frames / HZ + : (level.framenum - cl->resp.enterframe) / HZ; if (shots) accuracy = (double)cl->resp.hitsTotal * 100.0 / (double)cl->resp.shotsTotal; @@ -1597,6 +1603,7 @@ void LogEndMatchStats(void) ghlient->resp.team_kills = ghost->team_kills; ghlient->resp.streakKillsHighest = ghost->streakKillsHighest; ghlient->resp.streakHSHighest = ghost->streakHSHighest; + ghlient->resp.active_frames = ghost->active_frames; ghlient->resp.shotsTotal = ghost->shotsTotal; ghlient->resp.hitsTotal = ghost->hitsTotal;