
Posted by WarZone (209.223.137.*) on April 28,
1999 at 02:11:21:
This entire tutorial is
aviable in TXT format via email if you want it (the line breaks get killed in
the html). There are no restrictions on using this particular code, mainly due
to the fact that Raven (of the Viking mod) did 99% of the highscores code. And,
because there are other motd tutorials already out there. So, it would be silly
to restrict its use.
Okay, let's get started!
There's a decent ammount of commentary in this tutorial, so hopefully even
beginners will be able to follow along. And, if you actually follow the code,
rather than cut-n-paste, you should learn a lot by doing this tutorial.
First save the next entire section to highscore.c and add it to your project/makefile.
_______________
<<highscore.c>>
#include "g_local.h"
#define SCORESTOKEEP 15
/* # of people
to save in the highscore file
if you changed this number, you will need to delete your old
highscore files! */
#define SCORE resp.points
// variable to
use for score tracking
#define MOD_NAME "assim"
// the
directory you use for your mod
#define MOD_TITLE "-- Assimilation 2.5 --"
/* this text is
appended to the end of the high
score file */
typedef struct {
char netname[16];
int score;
time_t dt_occured;
} HS_STRUCT;
// room to hold max # of players
HS_STRUCT g_TopScores[SCORESTOKEEP];
// used for the
int MP_Sort(const void *a, const void *b)
{
return (((HS_STRUCT
*)b)->score - ((HS_STRUCT *)a)->score);
}
void highscore (void)
{
int i, count=0;
edict_t *ent;
edict_t *cl_ent;
FILE *HS_file;
char filename[32], filename2[32];
i = sprintf(filename,
"./");
i += sprintf(filename + i,
MOD_NAME);
i += sprintf(filename + i,
"/hs/%s_hs.bin", level.mapname);
i = sprintf(filename2,
"./");
i += sprintf(filename2 + i,
"assim");
i += sprintf(filename2 + i,
"/hs/%s_hs.txt", level.mapname);
// is the high score file for this map already loaded?
// no - load it
if(HS_file = fopen(filename,
"rb"))
{
fread(g_TopScores,
sizeof(g_TopScores[0]) * SCORESTOKEEP, 1, HS_file);
fclose(HS_file);
}
else
{
// if it doesnt
exist, create it with the top current players in it
memset(g_TopScores,
0, sizeof(g_TopScores));
count=0;
for (i = 0 ; i <
maxclients->value; i++)
{
cl_ent =
g_edicts + 1 + i;
if
(cl_ent->inuse)
{
strcpy(g_TopScores[count].netname,
game.clients[i].pers.netname);
g_TopScores[count].score = game.clients[i].SCORE;
g_TopScores[count].dt_occured = time(NULL);
count++;
if (count >= SCORESTOKEEP)
break;
}
}
// sort it
qsort(g_TopScores,
sizeof(g_TopScores)/sizeof(g_TopScores[0]), sizeof(g_TopScores[0]), MP_Sort);
}
for (ent = g_edicts + 1; ent
<= g_edicts + (int)maxclients->value; ent++)
{
if (!ent->inuse ||
!ent->client)
continue;
// HS_file loaded - see if any entity made the
list
for (i = 0 ; i <
maxclients->value ; i++)
{
cl_ent =
g_edicts + 1 + i;
if
(cl_ent->inuse)
{
// if it beat the lowest, keep score
if (game.clients[i].SCORE > g_TopScores[SCORESTOKEEP-1].score)
{
strcpy(g_TopScores[SCORESTOKEEP-1].netname,
game.clients[i].pers.netname);
g_TopScores[SCORESTOKEEP-1].score = game.clients[i].SCORE;
g_TopScores[SCORESTOKEEP-1].dt_occured = time(NULL);
// sort it
qsort(g_TopScores, sizeof(g_TopScores)/sizeof(g_TopScores[0]),
sizeof(g_TopScores[0]), MP_Sort);
}
}
}
} // end main for loop
// write the high score HS_file
HS_file = fopen(filename,
"wb");
if (HS_file != NULL)
{
fwrite(g_TopScores,
sizeof(g_TopScores[0]), SCORESTOKEEP, HS_file);
fclose(HS_file);
}
// print top scores to a readable file
HS_file = fopen(filename2,
"wt");
if (HS_file != NULL)
{
fprintf(HS_file,"Top %d Scores for %s\n", SCORESTOKEEP,
level.mapname);
for (i = 0; i <
SCORESTOKEEP; i++)
fprintf(HS_file,
"%2d - %-16.16s %4d\n", i+1, g_TopScores[i].netname,
g_TopScores[i].score);
fprintf(HS_file,"%s\n", MOD_TITLE);
fclose(HS_file);
}
}
<<highscore.c>>
---------------
Now, open up g_local.h and Search
for "showscores". The line should look like this:
g_local.h ->
qboolean
showscores;
Change it to this:
g_local.h -> int
showscores;
Now, scroll down to the very end of that file and add these two
new variables:
char motd[1400]; //master motd variable to store the motd file
contents
char highscores[1400]; //master
variable to store the highscore printout
Close, and save. Now open p_hud.c and Search for the
"Cmd_Score_f" function. Replace the old function with this one:
void Cmd_Score_f (edict_t *ent)
{
ent->client->showinventory
= false;
ent->client->showhelp =
false;
if (!deathmatch->value && !coop->value)
return;
switch (ent->client->showscores)
{
case 0: //nothing
showing..
ent->client->showscores = 1; //show the standard DM scoreboard
break;
case 1: //score board
showing..
if
(level.intermissiontime)
ent->client->showscores = 3; //show the highscores
else
ent->client->showscores = 0; //show nothing
break;
case 2: //motd
showing
ent->client->showscores = 0; //show nothing
break;
case 3: //highscores
showing
if
(level.intermissiontime)
ent->client->showscores = 1;
//show the standard DM scoreboard
break;
default: //???
ent->client->showscores = 0; //show nothing
break;
}
switch (ent->client->showscores)
{
case 1:
DeathmatchScoreboard (ent);
break;
case 2:
ShowMOTD
(ent);
break;
case 3:
ShowHighScores (ent);
gi.unicast (ent, true); //update the score display (is this necessary?)
break;
default:
break;
}
}
Now there's a bunch of changes that need to be made to the
original source to accomidate the new definition of "showscores".
First open up p_client.c and Search for the "player_die" function and
add the indicated line into the function:
player_die()
{
...
if
(deathmatch->value)
{
+
self->client->showscores = 0; //clear the hud
Cmd_Help_f (self);
// show score board
}
...
}
Now Search for the
"ClientBeginDeathmatch" function and add the indicated line:
ClientBeginDeathMatch()
{
...
+ ent->client->showscores
= 4; //make the MOTD show up
// send effect
gi.WriteByte
(svc_muzzleflash);
gi.WriteShort
(ent-g_edicts);
gi.WriteByte
(MZ_LOGIN);
gi.multicast
(ent->s.origin, MULTICAST_PVS);
...
}
Now save and close. Now open up
p_view.c and search for the "ClientEndServerFrame" function. Then
replace the following lines:
// if the scoreboard is up, update it
if (ent->client->showscores
&& !(level.framenum & 31) )
{
DeathmatchScoreboardMessage (ent, ent->enemy);
gi.unicast (ent,
false);
}
With this code:
// if the scoreboard is up, update it
if (ent->client->showscores
> 0 && !(level.framenum & 31))
{
switch
(ent->client->showscores)
{
case 1:
DeathmatchScoreboardMessage (ent, ent->enemy);
break;
case 2:
ShowMOTD (ent);
break;
default:
//highscores, etc. don't need to be updated
break;
}
gi.unicast (ent,
false);
}
Now the only thing left to do is add the MOTD functions into
your source. Open up p_hud.c and add the following functions to the end of the
file:
//---begin new code
void LoadMOTD (void)
{
char entry[1024];
char string[1400];
int stringlength;
int i, j;
FILE *motd_file;
char line[80];
if (!(motd_file = fopen("assim/motd.txt", "r")))
return;
string[0] = 0;
stringlength
= strlen(string);
Com_sprintf (entry, sizeof(entry),
"xv 12 yv 8
string2 \"Welcome to <Mod Name> Version <version>\"
"); // change this line!!
j = strlen(entry);
if (stringlength + j < 1024)
{
strcpy (string +
stringlength, entry);
stringlength += j;
}
i = 0;
while ( fgets(line, 80,
motd_file) )
{
Com_sprintf (entry,
sizeof(entry),
"xv
2 yv %i string \"%s\" ",
i*8 + 24,
line);
j = strlen(entry);
if (stringlength + j
> 1024)
break;
strcpy (string +
stringlength, entry);
stringlength += j;
i++;
}
// be good now ! ... close the
file
fclose(motd_file);
j = strlen(entry);
if (stringlength + j < 1024)
{
strcpy (string +
stringlength, entry);
stringlength += j;
}
Com_sprintf (motd, sizeof(motd), string);
}
void LoadHighScores (void)
{
char entry[1024];
char string[1400];
int stringlength;
int i, j;
FILE
*motd_file;
char filename[32];
char line[80];
i = sprintf(filename,
"./");
i += sprintf(filename + i,
"assim");
i += sprintf(filename + i,
"/hs/%s_hs.txt", level.mapname);
if (!(motd_file = fopen(filename, "r")))
return;
string[0] = 0;
stringlength
= strlen(string);
i = 0;
while ( fgets(line, 80,
motd_file) )
{
Com_sprintf (entry,
sizeof(entry),
"xv
2 yv %i string \"%s\" ",
i*8 + 24,
line);
j = strlen(entry);
if (stringlength + j
> 1024)
break;
strcpy (string +
stringlength, entry);
stringlength += j;
i++;
}
// be good now ! ... close the
file
fclose(motd_file);
j = strlen(entry);
if (stringlength + j < 1024)
{
strcpy (string +
stringlength, entry);
stringlength += j;
}
Com_sprintf (highscores, sizeof(highscores), string);
}
void ShowMOTD (edict_t *ent)
{
gi.WriteByte (svc_layout);
gi.WriteString (motd); // send
the MOTD to the client
}
void ShowHighScores (edict_t *ent)
{
gi.WriteByte (svc_layout);
gi.WriteString (highscores); //
send the high scores to the client
}
//---end new code
You could actually cut-n-paste the LoadHighScores() function
into highscore.c if you want, but it really doesn't matter where you put it.
Now that the features of the High Score list and MOTD are in
place, its time to actually add the function calls that will make it all
happen. First open g_save.c and add these two lines to the very end of the
"InitGame" function.
InitGame()
{
...
LoadMOTD();
LoadHighScores();
}
Save and close. Now open p_hud.c again
and find the function "BeginIntermission" and add the indicated
lines:
BeginIntermission()
{
...
+ highscore(); // first add
players to the highscores list
+ LoadHighScores(); // then
load the new list.
level.intermissiontime = level.time;
level.changemap =
targ->map;
if
(strstr(level.changemap, "*"))
...
}
Now find the "MoveClientToIntermission" function
(still in p_hud.c) and add the indicated lines to the end of the function:
MoveClientToIntermission()
{
...
+ if (deathmatch->value
|| coop->value)
+ {
+
ent->client->showscores = 5;
+ ShowHighScores
(ent);
+ gi.unicast
(ent, true);
+ }
}
There! Now save and close every thing! All you need to do now is
create a directory called "hs" (stands for highscores) in your mod's
directory. And finally create the motd.txt file (also in your mod's directory).
Here is a sample motd.txt file:
Welcome to <Mod Name> -- Version <version>
Be sure to stop by:
<mod's homepage>
For the latest news, and updates.
Press F1 to remove this message
Enjoy!
--
WarZone