mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-13 07:47:45 +00:00
3fd21ea6ea
implement cfg_save_all cvar, so cfg_save can save all. downloads attempt to avoid using the fte/ gamedir actually registering r_tracker_frags cvar. fix ezhud wad image issues. fix mouse binds not working when running fullscreen. dedicated servers can now use getsurface builtins. gl_font can now attempt to use conchars subdir too. terrain editor can now display the areas which cannot accept the selected texture for painting. this should help reduce edges. attempt to fix some of the less-supported ports. don't be annoying with entity foo = someclass; fteqcc now offers to create files if you try opening one that doesn't exist. plugins can now query ping etc info properly. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4893 fc73d0e0-1445-4013-8a0c-d673dee63da5
750 lines
18 KiB
C
750 lines
18 KiB
C
#include "quakedef.h"
|
|
|
|
#define MAX_WEAPONS 64 //fixme: make dynamic.
|
|
|
|
typedef enum {
|
|
//one componant
|
|
ff_death,
|
|
ff_tkdeath,
|
|
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;
|
|
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;
|
|
char *image;
|
|
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.
|
|
stat teamdeaths; //times they died to a team member.
|
|
stat suisides; //times they were stupid.
|
|
} clienttotals[MAX_CLIENTS];
|
|
|
|
qboolean readcaps;
|
|
qboolean readkills;
|
|
statmessage_t *message;
|
|
} fragstats_t;
|
|
|
|
cvar_t r_tracker_frags = CVARD("r_tracker_frags", "0", "0: like vanilla quake\n1: shows only your kills/deaths\n2: shows all kills\n");
|
|
static fragstats_t fragstats;
|
|
|
|
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;
|
|
}
|
|
|
|
qboolean Stats_HaveFlags(void)
|
|
{
|
|
return fragstats.readcaps;
|
|
}
|
|
qboolean Stats_HaveKills(void)
|
|
{
|
|
return fragstats.readkills;
|
|
}
|
|
|
|
void VARGS Stats_Message(char *msg, ...);
|
|
|
|
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)
|
|
{
|
|
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;
|
|
int localplayer = (cl.spectator && cl.playerview[0].cam_locked)?cl.playerview[0].cam_spec_track:cl.playerview[0].playernum;
|
|
|
|
#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;
|
|
}
|
|
}
|
|
|
|
Q_snprintfz(message, sizeof(message), "%s%s ^7%s %s%s\n", p1c, p1n, w->image?w->image:w->abrev, p2c, p2n);
|
|
|
|
tracker = Con_FindConsole("tracker");
|
|
if (!tracker)
|
|
{
|
|
tracker = Con_Create("tracker", CONF_HIDDEN|CONF_NOTIFY|CONF_NOTIFY_RIGHT|CONF_NOTIFY_BOTTOM);
|
|
//this stuff should be configurable
|
|
tracker->notif_l = tracker->maxlines = 8;
|
|
tracker->notif_x = 0.5;
|
|
tracker->notif_y = 0.333;
|
|
tracker->notif_w = 1-tracker->notif_x;
|
|
tracker->notif_t = 4;
|
|
tracker->notif_fade = 1;
|
|
}
|
|
Con_PrintCon(tracker, message, tracker->parseflags);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
u1 = (p1 == (cl.playerview[0].playernum));
|
|
u2 = (p2 == (cl.playerview[0].playernum));
|
|
|
|
//messages are killed weapon killer
|
|
switch(mt)
|
|
{
|
|
case ff_death:
|
|
if (u1)
|
|
{
|
|
fragstats.weapontotals[wid].owndeaths++;
|
|
fragstats.weapontotals[wid].ownkills++;
|
|
}
|
|
|
|
fragstats.weapontotals[wid].kills++;
|
|
fragstats.clienttotals[p1].deaths++;
|
|
fragstats.totaldeaths++;
|
|
|
|
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);
|
|
break;
|
|
case ff_suicide:
|
|
if (u1)
|
|
{
|
|
fragstats.weapontotals[wid].ownsuicides++;
|
|
fragstats.weapontotals[wid].owndeaths++;
|
|
fragstats.weapontotals[wid].ownkills++;
|
|
}
|
|
|
|
fragstats.weapontotals[wid].suicides++;
|
|
fragstats.weapontotals[wid].kills++;
|
|
fragstats.clienttotals[p1].suisides++;
|
|
fragstats.clienttotals[p1].deaths++;
|
|
fragstats.totalsuicides++;
|
|
fragstats.totaldeaths++;
|
|
|
|
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);
|
|
break;
|
|
case ff_bonusfrag:
|
|
if (u1)
|
|
fragstats.weapontotals[wid].ownkills++;
|
|
fragstats.weapontotals[wid].kills++;
|
|
fragstats.clienttotals[p1].kills++;
|
|
fragstats.totalkills++;
|
|
|
|
Stats_FragMessage(-4, wid, p1, false);
|
|
if (u1)
|
|
Stats_Message("You killed someone\n%s kills: %i\n", fragstats.weapontotals[wid].fullname, fragstats.weapontotals[wid].ownkills);
|
|
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++;
|
|
fragstats.clienttotals[p1].teamkills++;
|
|
fragstats.totalteamkills++;
|
|
|
|
Stats_FragMessage(-1, wid, p1, true);
|
|
|
|
if (u1)
|
|
Stats_Message("You killed your teammate\n%s teamkills: %i\n", fragstats.weapontotals[wid].fullname, fragstats.weapontotals[wid].ownteamkills);
|
|
break;
|
|
case ff_flagtouch:
|
|
fragstats.clienttotals[p1].grabs++;
|
|
fragstats.totaltouches++;
|
|
|
|
if (u1)
|
|
{
|
|
Stats_Message("You grabbed the flag\nflag grabs: %i (%i)\n", fragstats.clienttotals[p1].grabs, fragstats.totaltouches);
|
|
}
|
|
break;
|
|
case ff_flagcaps:
|
|
fragstats.clienttotals[p1].caps++;
|
|
fragstats.totalcaps++;
|
|
|
|
if (u1)
|
|
{
|
|
Stats_Message("You captured the flag\nflag captures: %i (%i)\n", fragstats.clienttotals[p1].caps, fragstats.totalcaps);
|
|
}
|
|
break;
|
|
case ff_flagdrops:
|
|
fragstats.clienttotals[p1].drops++;
|
|
fragstats.totaldrops++;
|
|
|
|
if (u1)
|
|
{
|
|
Stats_Message("You dropped the flag\nflag drops: %i (%i)\n", fragstats.clienttotals[p1].drops, fragstats.totaldrops);
|
|
}
|
|
break;
|
|
|
|
//p1 died, p2 killed
|
|
case ff_frags:
|
|
case ff_fragedby:
|
|
fragstats.weapontotals[wid].kills++;
|
|
|
|
fragstats.clienttotals[p1].deaths++;
|
|
fragstats.totaldeaths++;
|
|
if (u1)
|
|
{
|
|
fragstats.weapontotals[wid].owndeaths++;
|
|
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);
|
|
}
|
|
|
|
fragstats.clienttotals[p2].kills++;
|
|
fragstats.totalkills++;
|
|
if (u2)
|
|
{
|
|
fragstats.weapontotals[wid].ownkills++;
|
|
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);
|
|
}
|
|
|
|
Stats_FragMessage(p1, wid, p2, false);
|
|
break;
|
|
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++;
|
|
|
|
if (u1)
|
|
fragstats.weapontotals[wid].owndeaths++;
|
|
fragstats.clienttotals[p1].teamdeaths++;
|
|
fragstats.clienttotals[p1].deaths++;
|
|
fragstats.totaldeaths++;
|
|
|
|
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);
|
|
break;
|
|
|
|
case ff_tkills:
|
|
case ff_tkilledby:
|
|
//p1 killed by p2 (kills is already inverted)
|
|
fragstats.weapontotals[wid].teamkills++;
|
|
fragstats.weapontotals[wid].kills++;
|
|
|
|
if (u1)
|
|
{
|
|
fragstats.weapontotals[wid].ownteamdeaths++;
|
|
fragstats.weapontotals[wid].owndeaths++;
|
|
}
|
|
fragstats.clienttotals[p1].teamdeaths++;
|
|
fragstats.clienttotals[p1].deaths++;
|
|
fragstats.totaldeaths++;
|
|
|
|
if (u2)
|
|
{
|
|
fragstats.weapontotals[wid].ownkills++;
|
|
fragstats.weapontotals[wid].ownkills++;
|
|
}
|
|
fragstats.clienttotals[p2].teamkills++;
|
|
fragstats.clienttotals[p2].kills++;
|
|
fragstats.totalkills++;
|
|
|
|
fragstats.totalteamkills++;
|
|
|
|
Stats_FragMessage(p1, wid, p2, false);
|
|
if (u1)
|
|
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);
|
|
if (u2)
|
|
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);
|
|
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);
|
|
if (token2 && *token2)
|
|
{
|
|
t += strlen(t)+1;
|
|
ms->msgpart2 = t;
|
|
strcpy(t, token2);
|
|
}
|
|
ms->type = type;
|
|
ms->wid = wid;
|
|
|
|
ms->next = fragstats.message;
|
|
fragstats.message = ms;
|
|
|
|
//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;
|
|
}
|
|
|
|
void Stats_Clear(void)
|
|
{
|
|
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);
|
|
if (fragstats.weapontotals[i].image) Z_Free(fragstats.weapontotals[i].image);
|
|
}
|
|
|
|
memset(&fragstats, 0, sizeof(fragstats));
|
|
}
|
|
|
|
#define Z_Copy(tk) tz = Z_Malloc(strlen(tk)+1);strcpy(tz, tk) //remember the braces
|
|
|
|
void Stats_Init(void)
|
|
{
|
|
Cvar_Register(&r_tracker_frags, NULL);
|
|
}
|
|
static void Stats_LoadFragFile(char *name)
|
|
{
|
|
char filename[MAX_QPATH];
|
|
char *file;
|
|
char *end;
|
|
char *tk, *tz;
|
|
char oend;
|
|
|
|
Stats_Clear();
|
|
|
|
strcpy(filename, name);
|
|
COM_DefaultExtension(filename, ".dat", sizeof(filename));
|
|
|
|
file = COM_LoadTempFile(filename, NULL);
|
|
if (!file || !*file)
|
|
{
|
|
Con_DPrintf("Couldn't load %s\n", filename);
|
|
return;
|
|
}
|
|
else
|
|
Con_DPrintf("Loaded %s\n", filename);
|
|
|
|
oend = 1;
|
|
for (;*file;)
|
|
{
|
|
if (!oend)
|
|
break;
|
|
for (end = file; *end && *end != '\n'; end++)
|
|
;
|
|
oend = *end;
|
|
*end = '\0';
|
|
Cmd_TokenizeString(file, true, false);
|
|
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));
|
|
fragstats.weapontotals[wid].image = Stats_GenTrackerImageString(Cmd_Argv(5));
|
|
}
|
|
}
|
|
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;}
|
|
else if (!stricmp(tk, "X_TEAMKILLED_UNKNOWN")) {fftype = ff_tkdeath;}
|
|
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);
|
|
}
|
|
else
|
|
{Con_Printf("Unrecognised directive \"%s\"\n", tk);continue;}
|
|
}
|
|
else
|
|
{Con_Printf("Unrecognised directive \"%s\"\n", tk);continue;}
|
|
}
|
|
}
|
|
|
|
int qm_strcmp(char *s1, char *s2)//not like strcmp at all...
|
|
{
|
|
while(*s1)
|
|
{
|
|
if ((*s1++&0x7f)!=(*s2++&0x7f))
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int qm_stricmp(char *s1, char *s2)//not like strcmp at all...
|
|
{
|
|
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;
|
|
}
|
|
|
|
|
|
static int Stats_ExtractName(char **line)
|
|
{
|
|
int i;
|
|
int bm;
|
|
int ml = 0;
|
|
int l;
|
|
bm = -1;
|
|
for (i = 0; i < cl.allocated_client_slots; i++)
|
|
{
|
|
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;
|
|
}
|
|
|
|
qboolean Stats_ParsePrintLine(char *line)
|
|
{
|
|
statmessage_t *ms;
|
|
int p1;
|
|
int p2;
|
|
char *m2;
|
|
|
|
p1 = Stats_ExtractName(&line);
|
|
if (p1<0) //reject it.
|
|
{
|
|
return false;
|
|
}
|
|
|
|
for (ms = fragstats.message; ms; ms = ms->next)
|
|
{
|
|
if (!qm_stricmp(ms->msgpart1, line))
|
|
{
|
|
if (ms->type >= ff_frags)
|
|
{ //two players
|
|
m2 = line + strlen(ms->msgpart1);
|
|
p2 = Stats_ExtractName(&m2);
|
|
if (!ms->msgpart2)
|
|
continue;
|
|
if (!qm_stricmp(ms->msgpart2, m2))
|
|
{
|
|
Stats_Evaluate(ms->type, ms->wid, p1, p2);
|
|
return true; //done.
|
|
}
|
|
}
|
|
else
|
|
{ //one player
|
|
Stats_Evaluate(ms->type, ms->wid, p1, p1);
|
|
return true; //done.
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Stats_NewMap(void)
|
|
{
|
|
Stats_LoadFragFile("fragfile");
|
|
}
|
|
|