ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/hiscore.C
Revision: 1.5
Committed: Sun Sep 10 15:59:57 2006 UTC (17 years, 8 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.4: +296 -246 lines
Log Message:
indent

File Contents

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