2017-07-10 15:40:42 +00:00
2004-08-23 00:15:46 +00:00
# include "quakedef.h"
2017-07-10 15:40:42 +00:00
# ifdef QUAKEHUD
2004-08-23 00:15:46 +00:00
# define MAX_WEAPONS 64 //fixme: make dynamic.
typedef enum {
//one componant
ff_death ,
2007-08-30 18:23:57 +00:00
ff_tkdeath ,
2004-08-23 00:15:46 +00:00
ff_suicide ,
ff_bonusfrag ,
ff_tkbonus ,
ff_flagtouch ,
ff_flagcaps ,
ff_flagdrops ,
//two componant
ff_frags , //must be the first of the two componant
ff_fragedby ,
ff_tkills ,
ff_tkilledby ,
} fragfilemsgtypes_t ;
typedef struct statmessage_s {
fragfilemsgtypes_t type ;
int wid ;
2015-07-06 14:33:22 +00:00
size_t l1 , l2 ;
2004-08-23 00:15:46 +00:00
char * msgpart1 ;
char * msgpart2 ;
struct statmessage_s * next ;
} statmessage_t ;
typedef unsigned short stat ;
typedef struct {
stat totaldeaths ;
stat totalsuicides ;
stat totalteamkills ;
stat totalkills ;
stat totaltouches ;
stat totalcaps ;
stat totaldrops ;
//I was going to keep track of kills with a certain gun - too much memory
//track only your own and total weapon kills rather than per client
struct wt_s {
//these include you.
stat kills ;
stat teamkills ;
stat suicides ;
stat ownkills ;
stat owndeaths ;
stat ownteamkills ;
stat ownteamdeaths ;
stat ownsuicides ;
char * fullname ;
char * abrev ;
2015-04-14 23:12:17 +00:00
char * image ;
2004-08-23 00:15:46 +00:00
char * codename ;
} weapontotals [ MAX_WEAPONS ] ;
struct ct_s {
stat caps ; //times they captured the flag
stat drops ; //times lost the flag
stat grabs ; //times grabbed flag
stat owndeaths ; //times you killed them
stat ownkills ; //times they killed you
stat deaths ; //times they died (including by you)
stat kills ; //times they killed (including by you)
stat teamkills ; //times they killed a team member.
2007-07-23 12:02:11 +00:00
stat teamdeaths ; //times they died to a team member.
2015-06-16 23:53:58 +00:00
stat suicides ; //times they were stupid.
2004-08-23 00:15:46 +00:00
} clienttotals [ MAX_CLIENTS ] ;
2007-07-23 12:02:11 +00:00
qboolean readcaps ;
qboolean readkills ;
2004-08-23 00:15:46 +00:00
statmessage_t * message ;
} fragstats_t ;
2024-04-25 05:58:12 +00:00
static void TrackerCallback ( struct cvar_s * var , char * oldvalue ) ;
static cvar_t r_tracker_frags = CVARD ( " r_tracker_frags " , " 0 " , " 0: like vanilla quake \n 1: shows only your kills/deaths \n 2: shows all kills \n " ) ;
static cvar_t r_tracker_time = CVARCD ( " r_tracker_time " , " 4 " , TrackerCallback , " how long it takes for r_tracker messages to start fading \n " ) ;
static cvar_t r_tracker_fadetime = CVARCD ( " r_tracker_fadetime " , " 1 " , TrackerCallback , " how long it takes for r_tracker messages to fully fade once they start fading \n " ) ;
static cvar_t r_tracker_x = CVARCD ( " r_tracker_x " , " 0.5 " , TrackerCallback , " left position of the r_tracker messages, as a fraction of the screen's width, eg 0.5 \n " ) ;
static cvar_t r_tracker_y = CVARCD ( " r_tracker_y " , " 0.333 " , TrackerCallback , " top position of the r_tracker messages, as a fraction of the screen's height, eg 0.333 \n " ) ;
static cvar_t r_tracker_w = CVARCD ( " r_tracker_w " , " 0.5 " , TrackerCallback , " width of the r_tracker messages, as a fraction of the screen's width, eg 0.5 \n " ) ;
static cvar_t r_tracker_lines = CVARCD ( " r_tracker_lines " , " 8 " , TrackerCallback , " number of r_tracker messages to display \n " ) ;
static void Tracker_Update ( console_t * tracker )
{
tracker - > notif_l = tracker - > maxlines = max ( 1 , r_tracker_lines . ival ) ;
tracker - > notif_x = r_tracker_x . value ;
tracker - > notif_y = r_tracker_y . value ;
tracker - > notif_w = max ( 0 , r_tracker_w . value ) ;
tracker - > notif_t = max ( 0 , r_tracker_time . value ) ;
tracker - > notif_fade = max ( 0 , r_tracker_fadetime . value ) ;
//if its mostly on one side of the screen, align it accordingly.
if ( tracker - > notif_x + tracker - > notif_w * 0.5 > = 0.5 )
tracker - > flags | = CONF_NOTIFY_RIGHT ;
else
tracker - > flags & = ~ ( CONF_NOTIFY_RIGHT ) ;
}
static void TrackerCallback ( struct cvar_s * var , char * oldvalue )
{
console_t * tracker = Con_FindConsole ( " tracker " ) ;
if ( tracker )
Tracker_Update ( tracker ) ;
}
2004-08-23 00:15:46 +00:00
static fragstats_t fragstats ;
2007-07-23 12:02:11 +00:00
int Stats_GetKills ( int playernum )
{
return fragstats . clienttotals [ playernum ] . kills ;
}
int Stats_GetTKills ( int playernum )
{
return fragstats . clienttotals [ playernum ] . teamkills ;
}
int Stats_GetDeaths ( int playernum )
{
return fragstats . clienttotals [ playernum ] . deaths ;
}
int Stats_GetTouches ( int playernum )
{
return fragstats . clienttotals [ playernum ] . grabs ;
}
int Stats_GetCaptures ( int playernum )
{
return fragstats . clienttotals [ playernum ] . caps ;
}
2015-06-16 23:53:58 +00:00
qboolean Stats_HaveFlags ( int showtype )
2007-07-23 12:02:11 +00:00
{
2015-06-16 23:53:58 +00:00
int i ;
2019-09-15 15:09:36 +00:00
if ( showtype )
return fragstats . readcaps ;
2015-06-16 23:53:58 +00:00
for ( i = 0 ; i < cl . allocated_client_slots ; i + + )
{
if ( fragstats . clienttotals [ i ] . caps | |
fragstats . clienttotals [ i ] . drops | |
fragstats . clienttotals [ i ] . grabs )
return fragstats . readcaps ;
}
return false ;
2007-07-23 12:02:11 +00:00
}
qboolean Stats_HaveKills ( void )
{
return fragstats . readkills ;
}
2015-11-18 07:37:39 +00:00
static char lastownfragplayer [ 64 ] ;
static float lastownfragtime ;
float Stats_GetLastOwnFrag ( int seat , char * res , int reslen )
{
if ( seat )
{
if ( reslen )
* res = 0 ;
return 0 ;
}
//erk, realtime was reset?
if ( lastownfragtime > ( float ) realtime )
lastownfragtime = 0 ;
Q_strncpyz ( res , lastownfragplayer , reslen ) ;
return realtime - lastownfragtime ;
} ;
static void Stats_OwnFrag ( char * name )
{
Q_strncpyz ( lastownfragplayer , name , sizeof ( lastownfragplayer ) ) ;
lastownfragtime = realtime ;
}
2015-04-14 23:12:17 +00:00
void VARGS Stats_Message ( char * msg , . . . ) ;
2018-11-27 16:48:19 +00:00
qboolean Stats_TrackerImageLoaded ( const char * in )
2015-06-14 01:28:01 +00:00
{
2015-06-15 20:11:27 +00:00
int error ;
2015-07-04 02:04:46 +00:00
if ( in )
return Font_TrackerValid ( unicode_decode ( & error , in , & in , true ) ) ;
return false ;
2015-06-14 01:28:01 +00:00
}
2015-04-14 23:12:17 +00:00
static char * Stats_GenTrackerImageString ( char * in )
{ //images are of the form "foo \sg\ bar \q\"
//which should eg be remapped to: "foo ^Ue200 bar foo ^Ue201"
char res [ 256 ] ;
char image [ MAX_QPATH ] ;
char * outi ;
char * out ;
int i ;
if ( ! in | | ! * in )
return NULL ;
for ( out = res ; * in & & out < res + sizeof ( res ) - 10 ; )
{
if ( * in = = ' \\ ' )
{
in + + ;
for ( outi = image ; * in & & outi < image + sizeof ( image ) - 10 ; )
{
if ( * in = = ' \\ ' )
break ;
* outi + + = * in + + ;
}
* outi = 0 ;
in + + ;
i = Font_RegisterTrackerImage ( va ( " tracker/%s " , image ) ) ;
if ( i )
{
char hexchars [ 16 ] = " 0123456789abcdef " ;
* out + + = ' ^ ' ;
* out + + = ' U ' ;
* out + + = hexchars [ ( i > > 12 ) & 15 ] ;
* out + + = hexchars [ ( i > > 8 ) & 15 ] ;
* out + + = hexchars [ ( i > > 4 ) & 15 ] ;
* out + + = hexchars [ ( i > > 0 ) & 15 ] ;
}
else
{
//just copy the short name over, not much else we can do.
for ( outi = image ; out < res + sizeof ( res ) - 10 & & * outi ; )
* out + + = * outi + + ;
}
}
else
* out + + = * in + + ;
}
* out = 0 ;
return Z_StrDup ( res ) ;
}
void Stats_FragMessage ( int p1 , int wid , int p2 , qboolean teamkill )
2004-09-13 03:12:20 +00:00
{
2015-04-14 23:12:17 +00:00
static const char * nonplayers [ ] = {
" BUG " ,
" (teamkill) " ,
" (suicide) " ,
" (death) " ,
" (unknown) " ,
" (fixme) " ,
" (fixme) "
} ;
char message [ 512 ] ;
console_t * tracker ;
struct wt_s * w = & fragstats . weapontotals [ wid ] ;
const char * p1n = ( p1 < 0 ) ? nonplayers [ - p1 ] : cl . players [ p1 ] . name ;
const char * p2n = ( p2 < 0 ) ? nonplayers [ - p2 ] : cl . players [ p2 ] . name ;
2015-06-22 11:49:15 +00:00
int localplayer = ( cl . playerview [ 0 ] . cam_state = = CAM_EYECAM ) ? cl . playerview [ 0 ] . cam_spec_track : cl . playerview [ 0 ] . playernum ;
2015-04-14 23:12:17 +00:00
# define YOU_GOOD S_COLOR_GREEN
# define YOU_BAD S_COLOR_BLUE
# define TEAM_GOOD S_COLOR_GREEN
# define TEAM_BAD S_COLOR_RED
# define TEAM_VBAD S_COLOR_BLUE
# define TEAM_NEUTRAL S_COLOR_WHITE //enemy team thing that does not directly affect us
# define ENEMY_GOOD S_COLOR_RED
# define ENEMY_BAD S_COLOR_GREEN
# define ENEMY_NEUTRAL S_COLOR_WHITE
char * p1c = S_COLOR_WHITE ;
char * p2c = S_COLOR_WHITE ;
if ( ! r_tracker_frags . ival )
return ;
if ( r_tracker_frags . ival < 2 )
if ( p1 ! = localplayer & & p2 ! = localplayer )
return ;
if ( teamkill )
{ //team kills/suicides are always considered bad.
if ( p1 = = localplayer )
p1c = YOU_BAD ;
else if ( cl . teamplay & & ! strcmp ( cl . players [ p1 ] . team , cl . players [ localplayer ] . team ) )
p1c = TEAM_VBAD ;
else
p1c = TEAM_NEUTRAL ;
p2c = p1c ;
}
else if ( p1 = = p2 )
p1c = p2c = YOU_BAD ;
else if ( cl . teamplay & & p1 > = 0 & & p2 > = 0 & & ! strcmp ( cl . players [ p1 ] . team , cl . players [ p2 ] . team ) )
p1c = p2c = TEAM_VBAD ;
else
{
if ( p2 > = 0 )
{
//us/teammate killing is good - unless it was a teammate.
if ( p2 = = localplayer )
p2c = YOU_GOOD ;
else if ( cl . teamplay & & ! strcmp ( cl . players [ p2 ] . team , cl . players [ localplayer ] . team ) )
p2c = TEAM_GOOD ;
else
p2c = ENEMY_GOOD ;
}
if ( p1 > = 0 )
{
//us/teammate dying is bad.
if ( p1 = = localplayer )
p1c = YOU_BAD ;
else if ( cl . teamplay & & ! strcmp ( cl . players [ p1 ] . team , cl . players [ localplayer ] . team ) )
p1c = TEAM_BAD ;
else
p1c = p2c ;
}
}
2015-07-04 02:04:46 +00:00
Q_snprintfz ( message , sizeof ( message ) , " %s%s ^7%s %s%s \n " , p2c , p2n , Stats_TrackerImageLoaded ( w - > image ) ? w - > image : w - > abrev , p1c , p1n ) ;
2015-04-14 23:12:17 +00:00
tracker = Con_FindConsole ( " tracker " ) ;
if ( ! tracker )
{
2024-04-25 05:58:12 +00:00
tracker = Con_Create ( " tracker " , CONF_HIDDEN | CONF_NOTIFY | CONF_NOTIFY_BOTTOM ) ;
Tracker_Update ( tracker ) ;
2015-04-14 23:12:17 +00:00
}
Con_PrintCon ( tracker , message , tracker - > parseflags ) ;
2004-09-13 03:12:20 +00:00
}
2004-08-23 00:15:46 +00:00
void Stats_Evaluate ( fragfilemsgtypes_t mt , int wid , int p1 , int p2 )
{
qboolean u1 ;
qboolean u2 ;
if ( mt = = ff_frags | | mt = = ff_tkills )
{
int tmp = p1 ;
p1 = p2 ;
p2 = tmp ;
}
2013-06-23 02:17:02 +00:00
u1 = ( p1 = = ( cl . playerview [ 0 ] . playernum ) ) ;
u2 = ( p2 = = ( cl . playerview [ 0 ] . playernum ) ) ;
2004-08-23 00:15:46 +00:00
2016-02-10 23:23:43 +00:00
if ( p1 = = - 1 )
p1 = p2 ;
if ( p2 = = - 1 )
p2 = p1 ;
2015-04-14 23:12:17 +00:00
//messages are killed weapon killer
2004-08-23 00:15:46 +00:00
switch ( mt )
{
case ff_death :
if ( u1 )
{
fragstats . weapontotals [ wid ] . owndeaths + + ;
fragstats . weapontotals [ wid ] . ownkills + + ;
}
2015-04-14 23:12:17 +00:00
2004-08-23 00:15:46 +00:00
fragstats . weapontotals [ wid ] . kills + + ;
2016-02-10 23:23:43 +00:00
if ( p1 > = 0 )
fragstats . clienttotals [ p1 ] . deaths + + ;
2004-08-23 00:15:46 +00:00
fragstats . totaldeaths + + ;
2015-04-14 23:12:17 +00:00
Stats_FragMessage ( p1 , wid , - 3 , true ) ;
if ( u1 )
Stats_Message ( " You died \n %s deaths: %i \n " , fragstats . weapontotals [ wid ] . fullname , fragstats . weapontotals [ wid ] . owndeaths ) ;
2004-08-23 00:15:46 +00:00
break ;
case ff_suicide :
if ( u1 )
{
fragstats . weapontotals [ wid ] . ownsuicides + + ;
fragstats . weapontotals [ wid ] . owndeaths + + ;
fragstats . weapontotals [ wid ] . ownkills + + ;
}
2015-04-14 23:12:17 +00:00
2004-08-23 00:15:46 +00:00
fragstats . weapontotals [ wid ] . suicides + + ;
fragstats . weapontotals [ wid ] . kills + + ;
2016-02-10 23:23:43 +00:00
if ( p1 > = 0 )
{
fragstats . clienttotals [ p1 ] . suicides + + ;
fragstats . clienttotals [ p1 ] . deaths + + ;
}
2004-08-23 00:15:46 +00:00
fragstats . totalsuicides + + ;
fragstats . totaldeaths + + ;
2015-04-14 23:12:17 +00:00
Stats_FragMessage ( p1 , wid , - 2 , true ) ;
if ( u1 )
Stats_Message ( " You killed your own dumb self \n %s suicides: %i (%i) \n " , fragstats . weapontotals [ wid ] . fullname , fragstats . weapontotals [ wid ] . ownsuicides , fragstats . weapontotals [ wid ] . suicides ) ;
2004-08-23 00:15:46 +00:00
break ;
case ff_bonusfrag :
if ( u1 )
fragstats . weapontotals [ wid ] . ownkills + + ;
fragstats . weapontotals [ wid ] . kills + + ;
2016-02-10 23:23:43 +00:00
if ( p1 > = 0 )
fragstats . clienttotals [ p1 ] . kills + + ;
2004-08-23 00:15:46 +00:00
fragstats . totalkills + + ;
2015-04-14 23:12:17 +00:00
Stats_FragMessage ( - 4 , wid , p1 , false ) ;
if ( u1 )
2015-11-18 07:37:39 +00:00
{
Stats_OwnFrag ( " someone " ) ;
2015-04-14 23:12:17 +00:00
Stats_Message ( " You killed someone \n %s kills: %i \n " , fragstats . weapontotals [ wid ] . fullname , fragstats . weapontotals [ wid ] . ownkills ) ;
2015-11-18 07:37:39 +00:00
}
2004-08-23 00:15:46 +00:00
break ;
case ff_tkbonus :
if ( u1 )
fragstats . weapontotals [ wid ] . ownkills + + ;
fragstats . weapontotals [ wid ] . kills + + ;
fragstats . clienttotals [ p1 ] . kills + + ;
fragstats . totalkills + + ;
if ( u1 )
fragstats . weapontotals [ wid ] . ownteamkills + + ;
fragstats . weapontotals [ wid ] . teamkills + + ;
2016-02-10 23:23:43 +00:00
if ( p1 > = 0 )
fragstats . clienttotals [ p1 ] . teamkills + + ;
2004-08-23 00:15:46 +00:00
fragstats . totalteamkills + + ;
2015-04-14 23:12:17 +00:00
Stats_FragMessage ( - 1 , wid , p1 , true ) ;
if ( u1 )
2015-11-18 07:37:39 +00:00
{
2015-04-14 23:12:17 +00:00
Stats_Message ( " You killed your teammate \n %s teamkills: %i \n " , fragstats . weapontotals [ wid ] . fullname , fragstats . weapontotals [ wid ] . ownteamkills ) ;
2015-11-18 07:37:39 +00:00
}
2004-08-23 00:15:46 +00:00
break ;
case ff_flagtouch :
fragstats . clienttotals [ p1 ] . grabs + + ;
fragstats . totaltouches + + ;
2004-09-13 03:12:20 +00:00
2016-02-10 23:23:43 +00:00
if ( u1 & & p1 > = 0 )
2004-09-13 03:12:20 +00:00
{
2015-04-14 23:12:17 +00:00
Stats_Message ( " You grabbed the flag \n flag grabs: %i (%i) \n " , fragstats . clienttotals [ p1 ] . grabs , fragstats . totaltouches ) ;
2004-09-13 03:12:20 +00:00
}
2004-08-23 00:15:46 +00:00
break ;
case ff_flagcaps :
2016-02-10 23:23:43 +00:00
if ( p1 > = 0 )
fragstats . clienttotals [ p1 ] . caps + + ;
2004-08-23 00:15:46 +00:00
fragstats . totalcaps + + ;
2004-09-13 03:12:20 +00:00
2016-02-10 23:23:43 +00:00
if ( u1 & & p1 > = 0 )
2004-09-13 03:12:20 +00:00
{
2015-04-14 23:12:17 +00:00
Stats_Message ( " You captured the flag \n flag captures: %i (%i) \n " , fragstats . clienttotals [ p1 ] . caps , fragstats . totalcaps ) ;
2004-09-13 03:12:20 +00:00
}
2004-08-23 00:15:46 +00:00
break ;
case ff_flagdrops :
2016-02-10 23:23:43 +00:00
if ( p1 > = 0 )
fragstats . clienttotals [ p1 ] . drops + + ;
2004-08-23 00:15:46 +00:00
fragstats . totaldrops + + ;
2004-09-13 03:12:20 +00:00
2016-02-10 23:23:43 +00:00
if ( u1 & & p1 > = 0 )
2004-09-13 03:12:20 +00:00
{
2015-04-14 23:12:17 +00:00
Stats_Message ( " You dropped the flag \n flag drops: %i (%i) \n " , fragstats . clienttotals [ p1 ] . drops , fragstats . totaldrops ) ;
2004-09-13 03:12:20 +00:00
}
2004-08-23 00:15:46 +00:00
break ;
//p1 died, p2 killed
case ff_frags :
case ff_fragedby :
fragstats . weapontotals [ wid ] . kills + + ;
2016-02-10 23:23:43 +00:00
if ( p1 > = 0 )
fragstats . clienttotals [ p1 ] . deaths + + ;
2004-08-23 00:15:46 +00:00
fragstats . totaldeaths + + ;
2004-09-13 03:12:20 +00:00
if ( u1 )
{
fragstats . weapontotals [ wid ] . owndeaths + + ;
2016-02-10 23:23:43 +00:00
if ( p1 > = 0 & & p2 > = 0 )
Stats_Message ( " %s killed you \n %s deaths: %i (%i/%i) \n " , cl . players [ p2 ] . name , fragstats . weapontotals [ wid ] . fullname , fragstats . clienttotals [ p2 ] . owndeaths , fragstats . weapontotals [ wid ] . owndeaths , fragstats . totaldeaths ) ;
2004-09-13 03:12:20 +00:00
}
2004-08-23 00:15:46 +00:00
2016-02-10 23:23:43 +00:00
if ( p2 > = 0 )
fragstats . clienttotals [ p2 ] . kills + + ;
2004-08-23 00:15:46 +00:00
fragstats . totalkills + + ;
2004-09-13 03:12:20 +00:00
if ( u2 )
{
2016-02-10 23:23:43 +00:00
if ( p1 > = 0 )
Stats_OwnFrag ( cl . players [ p1 ] . name ) ;
2004-09-13 03:12:20 +00:00
fragstats . weapontotals [ wid ] . ownkills + + ;
2016-02-10 23:23:43 +00:00
if ( p1 > = 0 & & p2 > = 0 )
Stats_Message ( " You killed %s \n %s kills: %i (%i/%i) \n " , cl . players [ p1 ] . name , fragstats . weapontotals [ wid ] . fullname , fragstats . clienttotals [ p2 ] . kills , fragstats . weapontotals [ wid ] . kills , fragstats . totalkills ) ;
2004-09-13 03:12:20 +00:00
}
2015-04-14 23:12:17 +00:00
Stats_FragMessage ( p1 , wid , p2 , false ) ;
2004-08-23 00:15:46 +00:00
break ;
2007-08-30 18:23:57 +00:00
case ff_tkdeath :
//killed by a teammate, but we don't know who
//kinda useless, but this is all some mods give us
fragstats . weapontotals [ wid ] . teamkills + + ;
fragstats . weapontotals [ wid ] . kills + + ;
fragstats . totalkills + + ; //its a kill, but we don't know who from
fragstats . totalteamkills + + ;
2015-04-14 23:12:17 +00:00
if ( u1 )
fragstats . weapontotals [ wid ] . owndeaths + + ;
2007-08-30 18:23:57 +00:00
fragstats . clienttotals [ p1 ] . teamdeaths + + ;
fragstats . clienttotals [ p1 ] . deaths + + ;
fragstats . totaldeaths + + ;
2015-04-14 23:12:17 +00:00
Stats_FragMessage ( p1 , wid , - 1 , true ) ;
if ( u1 )
Stats_Message ( " Your teammate killed you \n %s deaths: %i \n " , fragstats . weapontotals [ wid ] . fullname , fragstats . weapontotals [ wid ] . owndeaths ) ;
2007-08-30 18:23:57 +00:00
break ;
2004-08-23 00:15:46 +00:00
case ff_tkills :
case ff_tkilledby :
2007-08-30 18:23:57 +00:00
//p1 killed by p2 (kills is already inverted)
2004-08-23 00:15:46 +00:00
fragstats . weapontotals [ wid ] . teamkills + + ;
fragstats . weapontotals [ wid ] . kills + + ;
if ( u1 )
{
fragstats . weapontotals [ wid ] . ownteamdeaths + + ;
fragstats . weapontotals [ wid ] . owndeaths + + ;
}
2016-02-10 23:23:43 +00:00
if ( p1 > = 0 )
{
fragstats . clienttotals [ p1 ] . teamdeaths + + ;
fragstats . clienttotals [ p1 ] . deaths + + ;
}
2004-08-23 00:15:46 +00:00
fragstats . totaldeaths + + ;
if ( u2 )
{
fragstats . weapontotals [ wid ] . ownkills + + ;
fragstats . weapontotals [ wid ] . ownkills + + ;
}
2016-02-10 23:23:43 +00:00
if ( p2 > = 0 )
{
fragstats . clienttotals [ p2 ] . teamkills + + ;
fragstats . clienttotals [ p2 ] . kills + + ;
}
2004-08-23 00:15:46 +00:00
fragstats . totalkills + + ;
fragstats . totalteamkills + + ;
2015-04-14 23:12:17 +00:00
Stats_FragMessage ( p1 , wid , p2 , false ) ;
2016-02-10 23:23:43 +00:00
if ( u1 & & p2 > = 0 )
2015-04-14 23:12:17 +00:00
Stats_Message ( " %s killed you \n %s deaths: %i (%i/%i) \n " , cl . players [ p2 ] . name , fragstats . weapontotals [ wid ] . fullname , fragstats . clienttotals [ p2 ] . owndeaths , fragstats . weapontotals [ wid ] . owndeaths , fragstats . totaldeaths ) ;
2016-02-10 23:23:43 +00:00
if ( u2 & & p1 > = 0 & & p2 > = 0 )
2015-11-18 07:37:39 +00:00
{
Stats_OwnFrag ( cl . players [ p1 ] . name ) ;
2015-04-14 23:12:17 +00:00
Stats_Message ( " You killed %s \n %s kills: %i (%i/%i) \n " , cl . players [ p1 ] . name , fragstats . weapontotals [ wid ] . fullname , fragstats . clienttotals [ p2 ] . kills , fragstats . weapontotals [ wid ] . kills , fragstats . totalkills ) ;
2015-11-18 07:37:39 +00:00
}
2004-08-23 00:15:46 +00:00
break ;
}
}
static int Stats_FindWeapon ( char * codename , qboolean create )
{
int i ;
if ( ! strcmp ( codename , " NONE " ) )
return 0 ;
if ( ! strcmp ( codename , " NULL " ) )
return 0 ;
if ( ! strcmp ( codename , " NOWEAPON " ) )
return 0 ;
for ( i = 1 ; i < MAX_WEAPONS ; i + + )
{
if ( ! fragstats . weapontotals [ i ] . codename )
{
fragstats . weapontotals [ i ] . codename = Z_Malloc ( strlen ( codename ) + 1 ) ;
strcpy ( fragstats . weapontotals [ i ] . codename , codename ) ;
return i ;
}
if ( ! stricmp ( fragstats . weapontotals [ i ] . codename , codename ) )
{
if ( create )
return - 2 ;
return i ;
}
}
return - 1 ;
}
static void Stats_StatMessage ( fragfilemsgtypes_t type , int wid , char * token1 , char * token2 )
{
statmessage_t * ms ;
char * t ;
ms = Z_Malloc ( sizeof ( statmessage_t ) + strlen ( token1 ) + 1 + ( token2 & & * token2 ? strlen ( token2 ) + 1 : 0 ) ) ;
t = ( char * ) ( ms + 1 ) ;
ms - > msgpart1 = t ;
strcpy ( t , token1 ) ;
2015-07-06 14:33:22 +00:00
ms - > l1 = strlen ( ms - > msgpart1 ) ;
2004-08-23 00:15:46 +00:00
if ( token2 & & * token2 )
{
t + = strlen ( t ) + 1 ;
ms - > msgpart2 = t ;
strcpy ( t , token2 ) ;
2015-07-06 14:33:22 +00:00
ms - > l2 = strlen ( ms - > msgpart2 ) ;
2004-08-23 00:15:46 +00:00
}
ms - > type = type ;
ms - > wid = wid ;
2006-01-13 06:27:18 +00:00
ms - > next = fragstats . message ;
fragstats . message = ms ;
2007-07-23 12:02:11 +00:00
//we have a message type, save the fact that we have it.
if ( type = = ff_flagtouch | | type = = ff_flagcaps | | type = = ff_flagdrops )
fragstats . readcaps = true ;
if ( type = = ff_frags | | type = = ff_fragedby )
fragstats . readkills = true ;
2004-08-23 00:15:46 +00:00
}
2014-10-05 20:04:11 +00:00
void Stats_Clear ( void )
2004-08-23 00:15:46 +00:00
{
int i ;
statmessage_t * ms ;
while ( fragstats . message )
{
ms = fragstats . message ;
fragstats . message = ms - > next ;
Z_Free ( ms ) ;
}
for ( i = 1 ; i < MAX_WEAPONS ; i + + )
{
if ( fragstats . weapontotals [ i ] . codename ) Z_Free ( fragstats . weapontotals [ i ] . codename ) ;
if ( fragstats . weapontotals [ i ] . fullname ) Z_Free ( fragstats . weapontotals [ i ] . fullname ) ;
if ( fragstats . weapontotals [ i ] . abrev ) Z_Free ( fragstats . weapontotals [ i ] . abrev ) ;
2015-04-14 23:12:17 +00:00
if ( fragstats . weapontotals [ i ] . image ) Z_Free ( fragstats . weapontotals [ i ] . image ) ;
2004-08-23 00:15:46 +00:00
}
memset ( & fragstats , 0 , sizeof ( fragstats ) ) ;
}
# define Z_Copy(tk) tz = Z_Malloc(strlen(tk)+1);strcpy(tz, tk) //remember the braces
2015-06-12 14:44:50 +00:00
void Stats_Init ( void )
{
Cvar_Register ( & r_tracker_frags , NULL ) ;
2024-04-25 05:58:12 +00:00
Cvar_Register ( & r_tracker_time , NULL ) ;
Cvar_Register ( & r_tracker_fadetime , NULL ) ;
Cvar_Register ( & r_tracker_x , NULL ) ;
Cvar_Register ( & r_tracker_y , NULL ) ;
Cvar_Register ( & r_tracker_w , NULL ) ;
Cvar_Register ( & r_tracker_lines , NULL ) ;
2015-06-12 14:44:50 +00:00
}
2004-08-23 00:15:46 +00:00
static void Stats_LoadFragFile ( char * name )
{
char filename [ MAX_QPATH ] ;
char * file ;
char * end ;
char * tk , * tz ;
2004-09-20 23:25:38 +00:00
char oend ;
2004-08-23 00:15:46 +00:00
Stats_Clear ( ) ;
strcpy ( filename , name ) ;
2006-03-11 03:12:10 +00:00
COM_DefaultExtension ( filename , " .dat " , sizeof ( filename ) ) ;
2004-08-23 00:15:46 +00:00
2018-07-05 16:21:44 +00:00
file = COM_LoadTempFile ( filename , 0 , NULL ) ;
2004-09-20 23:25:38 +00:00
if ( ! file | | ! * file )
2007-07-23 12:02:11 +00:00
{
Con_DPrintf ( " Couldn't load %s \n " , filename ) ;
2004-08-23 00:15:46 +00:00
return ;
2007-07-23 12:02:11 +00:00
}
else
Con_DPrintf ( " Loaded %s \n " , filename ) ;
2004-08-23 00:15:46 +00:00
2004-09-20 23:25:38 +00:00
oend = 1 ;
2020-09-08 05:11:09 +00:00
for ( ; oend ; )
2004-08-23 00:15:46 +00:00
{
for ( end = file ; * end & & * end ! = ' \n ' ; end + + )
;
2004-09-20 23:25:38 +00:00
oend = * end ;
2004-08-23 00:15:46 +00:00
* end = ' \0 ' ;
2004-12-08 04:14:52 +00:00
Cmd_TokenizeString ( file , true , false ) ;
2004-08-23 00:15:46 +00:00
file = end + 1 ;
if ( ! Cmd_Argc ( ) )
continue ;
tk = Cmd_Argv ( 0 ) ;
if ( ! stricmp ( tk , " #fragfile " ) )
{
tk = Cmd_Argv ( 1 ) ;
if ( ! stricmp ( tk , " version " ) ) { }
else if ( ! stricmp ( tk , " gamedir " ) ) { }
else Con_Printf ( " Unrecognised #meta \" %s \" \n " , tk ) ;
}
else if ( ! stricmp ( tk , " #meta " ) )
{
tk = Cmd_Argv ( 1 ) ;
if ( ! stricmp ( tk , " title " ) ) { }
else if ( ! stricmp ( tk , " description " ) ) { }
else if ( ! stricmp ( tk , " author " ) ) { }
else if ( ! stricmp ( tk , " email " ) ) { }
else if ( ! stricmp ( tk , " webpage " ) ) { }
else { Con_Printf ( " Unrecognised #meta \" %s \" \n " , tk ) ; continue ; }
}
else if ( ! stricmp ( tk , " #define " ) )
{
tk = Cmd_Argv ( 1 ) ;
if ( ! stricmp ( tk , " weapon_class " ) | |
! stricmp ( tk , " wc " ) )
{
int wid ;
tk = Cmd_Argv ( 2 ) ;
wid = Stats_FindWeapon ( tk , true ) ;
if ( wid = = - 1 )
{ Con_Printf ( " Too many weapon definitions. The max is %i \n " , MAX_WEAPONS ) ; continue ; }
else if ( wid < - 1 )
{ Con_Printf ( " Weapon \" %s \" is already defined \n " , tk ) ; continue ; }
else
{
fragstats . weapontotals [ wid ] . fullname = Z_Copy ( Cmd_Argv ( 3 ) ) ;
fragstats . weapontotals [ wid ] . abrev = Z_Copy ( Cmd_Argv ( 4 ) ) ;
2015-04-14 23:12:17 +00:00
fragstats . weapontotals [ wid ] . image = Stats_GenTrackerImageString ( Cmd_Argv ( 5 ) ) ;
2004-08-23 00:15:46 +00:00
}
}
else if ( ! stricmp ( tk , " obituary " ) | |
! stricmp ( tk , " obit " ) )
{
int fftype ;
tk = Cmd_Argv ( 2 ) ;
if ( ! stricmp ( tk , " PLAYER_DEATH " ) ) { fftype = ff_death ; }
else if ( ! stricmp ( tk , " PLAYER_SUICIDE " ) ) { fftype = ff_suicide ; }
else if ( ! stricmp ( tk , " X_FRAGS_UNKNOWN " ) ) { fftype = ff_bonusfrag ; }
else if ( ! stricmp ( tk , " X_TEAMKILLS_UNKNOWN " ) ) { fftype = ff_tkbonus ; }
2007-08-30 18:23:57 +00:00
else if ( ! stricmp ( tk , " X_TEAMKILLED_UNKNOWN " ) ) { fftype = ff_tkdeath ; }
2004-08-23 00:15:46 +00:00
else if ( ! stricmp ( tk , " X_FRAGS_Y " ) ) { fftype = ff_frags ; }
else if ( ! stricmp ( tk , " X_FRAGGED_BY_Y " ) ) { fftype = ff_fragedby ; }
else if ( ! stricmp ( tk , " X_TEAMKILLS_Y " ) ) { fftype = ff_tkills ; }
else if ( ! stricmp ( tk , " X_TEAMKILLED_BY_Y " ) ) { fftype = ff_tkilledby ; }
else { Con_Printf ( " Unrecognised obituary \" %s \" \n " , tk ) ; continue ; }
Stats_StatMessage ( fftype , Stats_FindWeapon ( Cmd_Argv ( 3 ) , false ) , Cmd_Argv ( 4 ) , Cmd_Argv ( 5 ) ) ;
}
else if ( ! stricmp ( tk , " flag_alert " ) | |
! stricmp ( tk , " flag_msg " ) )
{
int fftype ;
tk = Cmd_Argv ( 2 ) ;
if ( ! stricmp ( tk , " X_TOUCHES_FLAG " ) ) { fftype = ff_flagtouch ; }
else if ( ! stricmp ( tk , " X_GETS_FLAG " ) ) { fftype = ff_flagtouch ; }
else if ( ! stricmp ( tk , " X_TAKES_FLAG " ) ) { fftype = ff_flagtouch ; }
else if ( ! stricmp ( tk , " X_CAPTURES_FLAG " ) ) { fftype = ff_flagcaps ; }
else if ( ! stricmp ( tk , " X_CAPS_FLAG " ) ) { fftype = ff_flagcaps ; }
else if ( ! stricmp ( tk , " X_SCORES " ) ) { fftype = ff_flagcaps ; }
else if ( ! stricmp ( tk , " X_DROPS_FLAG " ) ) { fftype = ff_flagdrops ; }
else if ( ! stricmp ( tk , " X_FUMBLES_FLAG " ) ) { fftype = ff_flagdrops ; }
else if ( ! stricmp ( tk , " X_LOSES_FLAG " ) ) { fftype = ff_flagdrops ; }
else { Con_Printf ( " Unrecognised flag alert \" %s \" \n " , tk ) ; continue ; }
Stats_StatMessage ( fftype , 0 , Cmd_Argv ( 3 ) , NULL ) ;
}
2004-09-20 23:25:38 +00:00
else
{ Con_Printf ( " Unrecognised directive \" %s \" \n " , tk ) ; continue ; }
2004-08-23 00:15:46 +00:00
}
2004-09-20 23:25:38 +00:00
else
{ Con_Printf ( " Unrecognised directive \" %s \" \n " , tk ) ; continue ; }
2004-08-23 00:15:46 +00:00
}
}
2015-07-06 14:47:46 +00:00
2016-07-28 13:18:22 +00:00
static int qm_strcmp ( const char * s1 , const char * s2 ) //not like strcmp at all...
2004-08-23 00:15:46 +00:00
{
while ( * s1 )
{
if ( ( * s1 + + & 0x7f ) ! = ( * s2 + + & 0x7f ) )
return 1 ;
}
return 0 ;
}
2015-07-06 14:47:46 +00:00
/*
static int qm_stricmp ( char * s1 , char * s2 ) //not like strcmp at all...
2004-08-23 00:15:46 +00:00
{
int c1 , c2 ;
while ( * s1 )
{
c1 = * s1 + + & 0x7f ;
c2 = * s2 + + & 0x7f ;
if ( c1 > = ' A ' & & c1 < = ' Z ' )
c1 = c1 - ' A ' + ' a ' ;
if ( c2 > = ' A ' & & c2 < = ' Z ' )
c2 = c2 - ' A ' + ' a ' ;
if ( c1 ! = c2 )
return 1 ;
}
return 0 ;
}
2015-07-06 14:33:22 +00:00
*/
2004-08-23 00:15:46 +00:00
2016-07-28 13:18:22 +00:00
static int Stats_ExtractName ( const char * * line )
2004-08-23 00:15:46 +00:00
{
int i ;
int bm ;
int ml = 0 ;
int l ;
bm = - 1 ;
2013-10-29 17:38:22 +00:00
for ( i = 0 ; i < cl . allocated_client_slots ; i + + )
2004-08-23 00:15:46 +00:00
{
if ( ! qm_strcmp ( cl . players [ i ] . name , * line ) )
{
l = strlen ( cl . players [ i ] . name ) ;
if ( l > ml )
{
bm = i ;
ml = l ;
}
}
}
* line + = ml ;
return bm ;
}
2016-07-26 11:47:59 +00:00
qboolean Stats_ParsePickups ( const char * line )
{
2019-04-16 22:40:05 +00:00
# ifdef HAVE_LEGACY
2016-07-26 11:47:59 +00:00
//fixme: rework this to support custom strings, with custom pickup icons
if ( ! Q_strncmp ( line , " You got the " , 12 ) ) //weapons, ammo, keys, powerups
return true ;
if ( ! Q_strncmp ( line , " You got armor " , 13 ) ) //caaake...
return true ;
2019-04-16 22:40:05 +00:00
if ( ! Q_strncmp ( line , " You get " , 8 ) ) //backpacks
2016-07-26 11:47:59 +00:00
return true ;
if ( ! Q_strncmp ( line , " You receive " , 12 ) ) //%i health\n
return true ;
# endif
return false ;
}
2016-07-26 11:56:55 +00:00
qboolean Stats_ParsePrintLine ( const char * line )
2004-08-23 00:15:46 +00:00
{
statmessage_t * ms ;
int p1 ;
int p2 ;
2016-07-28 13:18:22 +00:00
const char * m2 ;
2004-08-23 00:15:46 +00:00
p1 = Stats_ExtractName ( & line ) ;
if ( p1 < 0 ) //reject it.
2007-07-23 12:02:11 +00:00
{
2015-04-14 23:12:17 +00:00
return false ;
2007-07-23 12:02:11 +00:00
}
2004-08-23 00:15:46 +00:00
for ( ms = fragstats . message ; ms ; ms = ms - > next )
{
2015-07-06 14:33:22 +00:00
if ( ! Q_strncmp ( ms - > msgpart1 , line , ms - > l1 ) )
2004-08-23 00:15:46 +00:00
{
if ( ms - > type > = ff_frags )
{ //two players
2015-07-06 14:33:22 +00:00
m2 = line + ms - > l1 ;
2004-08-23 00:15:46 +00:00
p2 = Stats_ExtractName ( & m2 ) ;
2015-07-06 14:33:22 +00:00
if ( ( ! ms - > msgpart2 & & * m2 = = ' \n ' ) | | ( ms - > msgpart2 & & ! Q_strncmp ( ms - > msgpart2 , m2 , ms - > l2 ) ) )
2004-08-23 00:15:46 +00:00
{
Stats_Evaluate ( ms - > type , ms - > wid , p1 , p2 ) ;
2015-04-14 23:12:17 +00:00
return true ; //done.
2004-08-23 00:15:46 +00:00
}
}
else
{ //one player
Stats_Evaluate ( ms - > type , ms - > wid , p1 , p1 ) ;
2015-04-14 23:12:17 +00:00
return true ; //done.
2004-08-23 00:15:46 +00:00
}
}
}
2015-04-14 23:12:17 +00:00
return false ;
2004-08-23 00:15:46 +00:00
}
void Stats_NewMap ( void )
{
Stats_LoadFragFile ( " fragfile " ) ;
}
2022-03-08 05:31:34 +00:00
# endif