ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/hiscore.C
Revision: 1.6
Committed: Tue Sep 12 18:17:34 2006 UTC (17 years, 9 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.5: +4 -10 lines
Log Message:
indent

File Contents

# User Rev Content
1 elmex 1.1 /*
2     CrossFire, A Multiplayer game for X-windows
3    
4     Copyright (C) 2002 Mark Wedel & Crossfire Development Team
5     Copyright (C) 1992 Frank Tore Johansen
6    
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11    
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15     GNU General Public License for more details.
16    
17     You should have received a copy of the GNU General Public License
18     along with this program; if not, write to the Free Software
19     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20    
21     The authors can be reached via e-mail at crossfire-devel@real-time.com
22     */
23    
24     #include <global.h>
25     #ifndef __CEXTRACT__
26 root 1.5 # include <sproto.h>
27 elmex 1.1 #endif
28    
29     /*
30     * The score structure is used when treating new high-scores
31     */
32    
33 root 1.5 typedef struct scr
34     {
35     char name[BIG_NAME]; /* name */
36     char title[BIG_NAME]; /* Title */
37     char killer[BIG_NAME]; /* name (+ title) or "quit" */
38     sint64 exp; /* Experience */
39     char maplevel[BIG_NAME]; /* Killed on what level */
40     int maxhp, maxsp, maxgrace; /* Max hp, sp, grace when killed */
41     int position; /* Position in the highscore list */
42 elmex 1.1 } score;
43    
44     /*
45     * spool works mostly like strtok(char *, ":"), but it can also
46     * log a specified error message if something goes wrong.
47     */
48    
49 root 1.5 char *
50     spool (char *bp, char *error)
51     {
52 elmex 1.1 static char *prev_pos = NULL;
53     char *next_pos;
54 root 1.5
55     if (bp == NULL)
56     {
57     if (prev_pos == NULL)
58     {
59     LOG (llevError, "Called spool (%s) with NULL without previous call.\n", error);
60     return NULL;
61     }
62     bp = prev_pos;
63     }
64     if (*bp == '\0')
65     {
66     LOG (llevError, "spool: End of line at %s\n", error);
67 elmex 1.1 return NULL;
68     }
69 root 1.5 if ((next_pos = strchr (bp, ':')) != NULL)
70     {
71     *next_pos = '\0';
72     prev_pos = next_pos + 1;
73     }
74     else
75 elmex 1.1 prev_pos = NULL;
76     return bp;
77     }
78    
79     /*
80     * Does what it says, copies the contents of the first score structure
81     * to the second one.
82     */
83    
84 root 1.5 static void
85 root 1.6 copy_score (score *sc1, score *sc2)
86 root 1.5 {
87     strncpy (sc2->name, sc1->name, BIG_NAME);
88     sc2->name[BIG_NAME - 1] = '\0';
89     strncpy (sc2->title, sc1->title, BIG_NAME);
90     sc2->title[BIG_NAME - 1] = '\0';
91     strncpy (sc2->killer, sc1->killer, BIG_NAME);
92     sc2->killer[BIG_NAME - 1] = '\0';
93     sc2->exp = sc1->exp;
94     strcpy (sc2->maplevel, sc1->maplevel);
95     sc2->maxhp = sc1->maxhp;
96     sc2->maxsp = sc1->maxsp;
97     sc2->maxgrace = sc1->maxgrace;
98 elmex 1.1 }
99    
100     /*
101     * Writes the given score structure to a static buffer, and returns
102     * a pointer to it.
103     */
104    
105 root 1.5 static char *
106 root 1.6 put_score (score *sc)
107 root 1.5 {
108 elmex 1.1 static char buf[MAX_BUF];
109 root 1.5
110     sprintf (buf, "%s:%s:%lld:%s:%s:%d:%d:%d", sc->name, sc->title, (long long) sc->exp, sc->killer, sc->maplevel,
111     sc->maxhp, sc->maxsp, sc->maxgrace);
112 elmex 1.1 return buf;
113     }
114    
115     /*
116     * The oposite of put_score, get_score reads from the given buffer into
117     * a static score structure, and returns a pointer to it.
118     */
119    
120 root 1.5 static score *
121     get_score (char *bp)
122     {
123 elmex 1.1 static score sc;
124     char *cp;
125    
126 root 1.5 if ((cp = strchr (bp, '\n')) != NULL)
127     *cp = '\0';
128 elmex 1.1
129 root 1.5 if ((cp = spool (bp, "name")) == NULL)
130 elmex 1.1 return NULL;
131 root 1.5 strncpy (sc.name, cp, BIG_NAME);
132 elmex 1.1 sc.name[BIG_NAME - 1] = '\0';
133    
134 root 1.5 if ((cp = spool (NULL, "title")) == NULL)
135 elmex 1.1 return NULL;
136 root 1.5 strncpy (sc.title, cp, BIG_NAME);
137 elmex 1.1 sc.title[BIG_NAME - 1] = '\0';
138    
139 root 1.5 if ((cp = spool (NULL, "score")) == NULL)
140 elmex 1.1 return NULL;
141 root 1.4 long long exp;
142 elmex 1.1
143 root 1.5 sscanf (cp, "%lld", &exp);
144     sc.exp = exp;
145    
146     if ((cp = spool (NULL, "killer")) == NULL)
147 elmex 1.1 return NULL;
148 root 1.5 strncpy (sc.killer, cp, BIG_NAME);
149 elmex 1.1 sc.killer[BIG_NAME - 1] = '\0';
150    
151 root 1.5 if ((cp = spool (NULL, "map")) == NULL)
152 elmex 1.1 return NULL;
153 root 1.5 strncpy (sc.maplevel, cp, BIG_NAME);
154 elmex 1.1 sc.maplevel[BIG_NAME - 1] = '\0';
155    
156 root 1.5 if ((cp = spool (NULL, "maxhp")) == NULL)
157 elmex 1.1 return NULL;
158 root 1.5 sscanf (cp, "%d", &sc.maxhp);
159 elmex 1.1
160 root 1.5 if ((cp = spool (NULL, "maxsp")) == NULL)
161 elmex 1.1 return NULL;
162 root 1.5 sscanf (cp, "%d", &sc.maxsp);
163 elmex 1.1
164 root 1.5 if ((cp = spool (NULL, "maxgrace")) == NULL)
165 elmex 1.1 return NULL;
166 root 1.5 sscanf (cp, "%d", &sc.maxgrace);
167 elmex 1.1 return &sc;
168     }
169    
170 root 1.5 static char *
171 root 1.6 draw_one_high_score (score *sc)
172 root 1.5 {
173     static char retbuf[MAX_BUF];
174    
175     if (!strncmp (sc->killer, "quit", MAX_NAME))
176     sprintf (retbuf, "%3d %10lld %s the %s quit the game on map %s [%d][%d][%d].",
177     sc->position, (long long) sc->exp, sc->name, sc->title, sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
178     else if (!strncmp (sc->killer, "left", MAX_NAME))
179     sprintf (retbuf, "%3d %10lld %s the %s left the game on map %s [%d][%d][%d].",
180     sc->position, (long long) sc->exp, sc->name, sc->title, sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
181     else
182     sprintf (retbuf, "%3d %10lld %s the %s was killed by %s on map %s [%d][%d][%d].",
183     sc->position, (long long) sc->exp, sc->name, sc->title, sc->killer, sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
184     return retbuf;
185     }
186 elmex 1.1
187     /*
188     * add_score() adds the given score-structure to the high-score list, but
189     * only if it was good enough to deserve a place.
190     */
191    
192 root 1.5 static score *
193 root 1.6 add_score (score *new_score)
194 root 1.5 {
195 elmex 1.1 FILE *fp;
196     static score old_score;
197 root 1.5 score *tmp_score, pscore[HIGHSCORE_LENGTH];
198 elmex 1.1 char buf[MAX_BUF], filename[MAX_BUF], *bp;
199 root 1.5 int nrofscores = 0, flag = 0, i, comp;
200    
201     new_score->position = HIGHSCORE_LENGTH + 1;
202     old_score.position = -1;
203     sprintf (filename, "%s/%s", settings.localdir, HIGHSCORE);
204     if ((fp = open_and_uncompress (filename, 1, &comp)) != NULL)
205     {
206     while (fgets (buf, MAX_BUF, fp) != NULL && nrofscores < HIGHSCORE_LENGTH)
207     {
208     if ((tmp_score = get_score (buf)) == NULL)
209     break;
210     if (!flag && new_score->exp >= tmp_score->exp)
211     {
212     copy_score (new_score, &pscore[nrofscores]);
213     new_score->position = nrofscores;
214     flag = 1;
215     if (++nrofscores >= HIGHSCORE_LENGTH)
216     break;
217     }
218     if (!strcmp (new_score->name, tmp_score->name))
219     { /* Another entry */
220     copy_score (tmp_score, &old_score);
221     old_score.position = nrofscores;
222     if (flag)
223     continue;
224     }
225     copy_score (tmp_score, &pscore[nrofscores++]);
226     }
227     close_and_delete (fp, comp);
228     }
229     if (old_score.position != -1 && old_score.exp >= new_score->exp)
230     return &old_score; /* Did not beat old score */
231     if (!flag && nrofscores < HIGHSCORE_LENGTH)
232     copy_score (new_score, &pscore[nrofscores++]);
233     if ((fp = fopen (filename, "w")) == NULL)
234     {
235     LOG (llevError, "Cannot write to highscore file %s: %s\n", filename, strerror (errno));
236     return NULL;
237     }
238     for (i = 0; i < nrofscores; i++)
239     {
240     bp = put_score (&pscore[i]);
241     fprintf (fp, "%s\n", bp);
242     }
243     fclose (fp);
244     if (flag)
245     {
246    
247 elmex 1.1 /* Eneq(@csd.uu.se): Patch to fix error in adding a new score to the
248     hiscore-list */
249 root 1.5 if (old_score.position == -1)
250     return new_score;
251     return &old_score;
252     }
253     new_score->position = -1;
254     if (old_score.position != -1)
255 elmex 1.1 return &old_score;
256 root 1.5 if (nrofscores)
257     {
258     copy_score (&pscore[nrofscores - 1], &old_score);
259     return &old_score;
260     }
261     LOG (llevError, "Highscore error.\n");
262 elmex 1.1 return NULL;
263     }
264    
265 root 1.5 void
266     check_score (object *op)
267     {
268     score new_score;
269     score *old_score;
270    
271     if (op->stats.exp == 0)
272     return;
273    
274     if (!op->contr->name_changed)
275     {
276     if (op->stats.exp > 0)
277     {
278     new_draw_info (NDI_UNIQUE, 0, op, "As you haven't changed your name, you won't");
279     new_draw_info (NDI_UNIQUE, 0, op, "get into the high-score list.");
280 root 1.2 }
281 root 1.5 return;
282     }
283     if (QUERY_FLAG (op, FLAG_WAS_WIZ))
284     {
285     new_draw_info (NDI_UNIQUE, 0, op, "Since you have been in wizard mode,");
286     new_draw_info (NDI_UNIQUE, 0, op, "you can't enter the high-score list.");
287     return;
288     }
289     if (op->contr->explore)
290     {
291     new_draw_info (NDI_UNIQUE, 0, op, "Since you were in explore mode,");
292     new_draw_info (NDI_UNIQUE, 0, op, "you can't enter the high-score list.");
293     return;
294 elmex 1.1 }
295 root 1.5 strncpy (new_score.name, op->name, BIG_NAME);
296     new_score.name[BIG_NAME - 1] = '\0';
297     strncpy (new_score.title, op->contr->own_title, BIG_NAME);
298     if (new_score.title[0] == '\0')
299     strncpy (new_score.title, op->contr->title, BIG_NAME);
300     new_score.title[BIG_NAME - 1] = '\0';
301     strncpy (new_score.killer, op->contr->killer, BIG_NAME);
302     if (new_score.killer[0] == '\0')
303     strcpy (new_score.killer, "a dungeon collapse");
304     new_score.killer[BIG_NAME - 1] = '\0';
305     new_score.exp = op->stats.exp;
306     if (op->map == NULL)
307     *new_score.maplevel = '\0';
308     else
309     {
310     strncpy (new_score.maplevel, op->map->name ? op->map->name : op->map->path, BIG_NAME - 1);
311     new_score.maplevel[BIG_NAME - 1] = '\0';
312     }
313     new_score.maxhp = (int) op->stats.maxhp;
314     new_score.maxsp = (int) op->stats.maxsp;
315     new_score.maxgrace = (int) op->stats.maxgrace;
316     if ((old_score = add_score (&new_score)) == NULL)
317     {
318     new_draw_info (NDI_UNIQUE, 0, op, "Error in the highscore list.");
319     return;
320     }
321     if (new_score.position == -1)
322     {
323     new_score.position = HIGHSCORE_LENGTH + 1; /* Not strictly correct... */
324     if (!strcmp (old_score->name, new_score.name))
325     new_draw_info (NDI_UNIQUE, 0, op, "You didn't beat your last highscore:");
326     else
327     new_draw_info (NDI_UNIQUE, 0, op, "You didn't enter the highscore list:");
328     new_draw_info (NDI_UNIQUE, 0, op, draw_one_high_score (old_score));
329     new_draw_info (NDI_UNIQUE, 0, op, draw_one_high_score (&new_score));
330     return;
331     }
332     if (old_score->exp >= new_score.exp)
333     new_draw_info (NDI_UNIQUE, 0, op, "You didn't beat your last score:");
334     else
335     new_draw_info (NDI_UNIQUE, 0, op, "You beat your last score:");
336 elmex 1.1
337 root 1.5 new_draw_info (NDI_UNIQUE, 0, op, draw_one_high_score (old_score));
338     new_draw_info (NDI_UNIQUE, 0, op, draw_one_high_score (&new_score));
339 elmex 1.1 }
340    
341    
342    
343     /* displays the high score file. object is the calling object
344     * (null if being called via command line.) max is the maximum
345     * number of scores to display. match, if set, is the name or class
346     * to match to.
347     */
348    
349 root 1.5 void
350     display_high_score (object *op, int max, const char *match)
351     {
352     const size_t maxchar = 80;
353     FILE *fp;
354     char buf[MAX_BUF], *scorebuf, *bp, *cp;
355     int i = 0, j = 0, comp;
356     score *sc;
357    
358     sprintf (buf, "%s/%s", settings.localdir, HIGHSCORE);
359     if ((fp = open_and_uncompress (buf, 0, &comp)) == NULL)
360     {
361     LOG (llevError, "Cannot open highscore file %s: %s\n", buf, strerror (errno));
362     if (op != NULL)
363     new_draw_info (NDI_UNIQUE, 0, op, "There is no highscore file.");
364     return;
365     }
366     if (op != NULL)
367     clear_win_info (op);
368     new_draw_info (NDI_UNIQUE, 0, op, "Nr Score Who [max hp][max sp][max grace]");
369    
370     while (fgets (buf, MAX_BUF, fp) != NULL)
371     {
372     if (j >= HIGHSCORE_LENGTH || i >= (max - 1))
373     break;
374     if ((sc = get_score (buf)) == NULL)
375     break;
376     sc->position = ++j;
377     if (match == NULL)
378     {
379     scorebuf = draw_one_high_score (sc);
380     i++;
381     }
382     else
383     {
384     if (!strcasecmp (sc->name, match) || !strcasecmp (sc->title, match))
385     {
386     scorebuf = draw_one_high_score (sc);
387     i++;
388 root 1.2 }
389 root 1.5 else
390     continue;
391 root 1.2 }
392 root 1.5 /* Replaced what seemed to an overly complicated word wrap method
393     * still word wraps, but assumes at most 2 lines of data.
394     * mw - 2-12-97
395     */
396     strncpy (buf, scorebuf, MAX_BUF);
397     buf[MAX_BUF - 1] = '\0';
398     cp = buf;
399     while (strlen (cp) > maxchar)
400     {
401     bp = cp + maxchar - 1;
402     while (*bp != ' ' && bp > cp)
403     bp--;
404     *bp = '\0';
405     if (op == NULL)
406     {
407     LOG (llevDebug, "%s\n", cp);
408 root 1.2 }
409 root 1.5 else
410     {
411     new_draw_info (NDI_UNIQUE, 0, op, cp);
412 root 1.2 }
413 root 1.5 sprintf (buf, " %s", bp + 1);
414     cp = buf;
415     i++;
416 root 1.2 }
417 root 1.5 if (op == NULL)
418     LOG (llevDebug, "%s\n", buf);
419     else
420     new_draw_info (NDI_UNIQUE, 0, op, buf);
421 elmex 1.1 }
422 root 1.5 close_and_delete (fp, comp);
423 elmex 1.1 }