ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/hiscore.C
Revision: 1.18
Committed: Mon Jan 15 21:06:20 2007 UTC (17 years, 4 months ago) by pippijn
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_0
Changes since 1.17: +22 -22 lines
Log Message:
comments

File Contents

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