ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/hiscore.C
Revision: 1.12
Committed: Mon Dec 11 21:32:18 2006 UTC (17 years, 5 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.11: +4 -5 lines
Log Message:
- simplify headerfiles (this is the 21st century)
- remove long long hacks for printf etc.

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 root 1.8 The authors can be reached via e-mail at <crossfire@schmorp.de>
22 elmex 1.1 */
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 root 1.9 char name[64]; // name */
36     char title[64]; // Title */
37     char killer[64]; // name (+ title) or "quit" */
38     sint64 exp; // Experience */
39     char maplevel[128]; // 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 root 1.7 assign (sc2->name , sc1->name);
88     assign (sc2->title , sc1->title);
89     assign (sc2->killer , sc1->killer);
90     assign (sc2->maplevel, sc1->maplevel);
91 root 1.5 sc2->exp = sc1->exp;
92     sc2->maxhp = sc1->maxhp;
93     sc2->maxsp = sc1->maxsp;
94     sc2->maxgrace = sc1->maxgrace;
95 elmex 1.1 }
96    
97     /*
98     * Writes the given score structure to a static buffer, and returns
99     * a pointer to it.
100     */
101    
102 root 1.5 static char *
103 root 1.6 put_score (score *sc)
104 root 1.5 {
105 elmex 1.1 static char buf[MAX_BUF];
106 root 1.5
107 root 1.12 sprintf (buf, "%s:%s:%" PRId64 ":%s:%s:%d:%d:%d",
108     sc->name, sc->title, (sint64)sc->exp, sc->killer,
109     sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
110 elmex 1.1 return buf;
111     }
112    
113     /*
114     * The oposite of put_score, get_score reads from the given buffer into
115     * a static score structure, and returns a pointer to it.
116     */
117    
118 root 1.5 static score *
119     get_score (char *bp)
120     {
121 elmex 1.1 static score sc;
122     char *cp;
123    
124 root 1.5 if ((cp = strchr (bp, '\n')) != NULL)
125     *cp = '\0';
126 elmex 1.1
127 root 1.5 if ((cp = spool (bp, "name")) == NULL)
128 root 1.7 return 0;
129    
130     assign (sc.name, cp);
131    
132     if ((cp = spool (0, "title")) == NULL)
133     return 0;
134    
135     assign (sc.title, cp);
136    
137     if ((cp = spool (0, "score")) == NULL)
138     return 0;
139 elmex 1.1
140 root 1.12 sscanf (cp, "%" SCNd64, &sc.exp);
141 root 1.5
142 root 1.7 if ((cp = spool (0, "killer")) == NULL)
143     return 0;
144    
145     assign (sc.killer, cp);
146    
147     if ((cp = spool (0, "map")) == NULL)
148     return 0;
149    
150     assign (sc.maplevel, cp);
151    
152     if ((cp = spool (0, "maxhp")) == NULL)
153     return 0;
154 elmex 1.1
155 root 1.5 sscanf (cp, "%d", &sc.maxhp);
156 elmex 1.1
157 root 1.7 if ((cp = spool (0, "maxsp")) == NULL)
158     return 0;
159    
160 root 1.5 sscanf (cp, "%d", &sc.maxsp);
161 elmex 1.1
162 root 1.7 if ((cp = spool (0, "maxgrace")) == NULL)
163     return 0;
164    
165 root 1.5 sscanf (cp, "%d", &sc.maxgrace);
166 elmex 1.1 return &sc;
167     }
168    
169 root 1.5 static char *
170 root 1.6 draw_one_high_score (score *sc)
171 root 1.5 {
172     static char retbuf[MAX_BUF];
173    
174     if (!strncmp (sc->killer, "quit", MAX_NAME))
175     sprintf (retbuf, "%3d %10lld %s the %s quit the game on map %s [%d][%d][%d].",
176     sc->position, (long long) sc->exp, sc->name, sc->title, sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
177     else if (!strncmp (sc->killer, "left", MAX_NAME))
178     sprintf (retbuf, "%3d %10lld %s the %s left the game on map %s [%d][%d][%d].",
179     sc->position, (long long) sc->exp, sc->name, sc->title, sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
180     else
181     sprintf (retbuf, "%3d %10lld %s the %s was killed by %s on map %s [%d][%d][%d].",
182     sc->position, (long long) sc->exp, sc->name, sc->title, sc->killer, sc->maplevel, sc->maxhp, sc->maxsp, sc->maxgrace);
183     return retbuf;
184     }
185 elmex 1.1
186     /*
187     * add_score() adds the given score-structure to the high-score list, but
188     * only if it was good enough to deserve a place.
189     */
190    
191 root 1.5 static score *
192 root 1.6 add_score (score *new_score)
193 root 1.5 {
194 elmex 1.1 FILE *fp;
195     static score old_score;
196 root 1.5 score *tmp_score, pscore[HIGHSCORE_LENGTH];
197 elmex 1.1 char buf[MAX_BUF], filename[MAX_BUF], *bp;
198 root 1.5 int nrofscores = 0, flag = 0, i, comp;
199    
200     new_score->position = HIGHSCORE_LENGTH + 1;
201     old_score.position = -1;
202     sprintf (filename, "%s/%s", settings.localdir, HIGHSCORE);
203     if ((fp = open_and_uncompress (filename, 1, &comp)) != NULL)
204     {
205     while (fgets (buf, MAX_BUF, fp) != NULL && nrofscores < HIGHSCORE_LENGTH)
206     {
207     if ((tmp_score = get_score (buf)) == NULL)
208     break;
209     if (!flag && new_score->exp >= tmp_score->exp)
210     {
211     copy_score (new_score, &pscore[nrofscores]);
212     new_score->position = nrofscores;
213     flag = 1;
214     if (++nrofscores >= HIGHSCORE_LENGTH)
215     break;
216     }
217     if (!strcmp (new_score->name, tmp_score->name))
218     { /* Another entry */
219     copy_score (tmp_score, &old_score);
220     old_score.position = nrofscores;
221     if (flag)
222     continue;
223     }
224     copy_score (tmp_score, &pscore[nrofscores++]);
225     }
226     close_and_delete (fp, comp);
227     }
228     if (old_score.position != -1 && old_score.exp >= new_score->exp)
229     return &old_score; /* Did not beat old score */
230     if (!flag && nrofscores < HIGHSCORE_LENGTH)
231     copy_score (new_score, &pscore[nrofscores++]);
232     if ((fp = fopen (filename, "w")) == NULL)
233     {
234     LOG (llevError, "Cannot write to highscore file %s: %s\n", filename, strerror (errno));
235     return NULL;
236     }
237     for (i = 0; i < nrofscores; i++)
238     {
239     bp = put_score (&pscore[i]);
240     fprintf (fp, "%s\n", bp);
241     }
242     fclose (fp);
243     if (flag)
244     {
245    
246 elmex 1.1 /* Eneq(@csd.uu.se): Patch to fix error in adding a new score to the
247     hiscore-list */
248 root 1.5 if (old_score.position == -1)
249     return new_score;
250     return &old_score;
251     }
252     new_score->position = -1;
253     if (old_score.position != -1)
254 elmex 1.1 return &old_score;
255 root 1.5 if (nrofscores)
256     {
257     copy_score (&pscore[nrofscores - 1], &old_score);
258     return &old_score;
259     }
260     LOG (llevError, "Highscore error.\n");
261 elmex 1.1 return NULL;
262     }
263    
264 root 1.5 void
265     check_score (object *op)
266     {
267     score new_score;
268     score *old_score;
269    
270     if (op->stats.exp == 0)
271     return;
272    
273     if (!op->contr->name_changed)
274     {
275     if (op->stats.exp > 0)
276     {
277     new_draw_info (NDI_UNIQUE, 0, op, "As you haven't changed your name, you won't");
278     new_draw_info (NDI_UNIQUE, 0, op, "get into the high-score list.");
279 root 1.2 }
280 root 1.5 return;
281     }
282 root 1.10
283 root 1.5 if (op->contr->explore)
284     {
285     new_draw_info (NDI_UNIQUE, 0, op, "Since you were in explore mode,");
286     new_draw_info (NDI_UNIQUE, 0, op, "you can't enter the high-score list.");
287     return;
288 elmex 1.1 }
289 root 1.7
290     assign (new_score.name, op->name);
291     assign (new_score.title, op->title.length () ? &op->title : op->contr->title);
292     assign (new_score.killer, op->contr->killer[0] ? op->contr->killer : "a dungeon collapse");
293     assign (new_score.maplevel, op->map ? op->map->name ? op->map->name : op->map->path : "");
294    
295     new_score.exp = op->stats.exp;
296     new_score.maxhp = op->stats.maxhp;
297     new_score.maxsp = op->stats.maxsp;
298     new_score.maxgrace = op->stats.maxgrace;
299    
300 root 1.5 if ((old_score = add_score (&new_score)) == NULL)
301     {
302     new_draw_info (NDI_UNIQUE, 0, op, "Error in the highscore list.");
303     return;
304     }
305 root 1.7
306 root 1.5 if (new_score.position == -1)
307     {
308     new_score.position = HIGHSCORE_LENGTH + 1; /* Not strictly correct... */
309     if (!strcmp (old_score->name, new_score.name))
310     new_draw_info (NDI_UNIQUE, 0, op, "You didn't beat your last highscore:");
311     else
312     new_draw_info (NDI_UNIQUE, 0, op, "You didn't enter the highscore list:");
313     new_draw_info (NDI_UNIQUE, 0, op, draw_one_high_score (old_score));
314     new_draw_info (NDI_UNIQUE, 0, op, draw_one_high_score (&new_score));
315     return;
316     }
317 root 1.7
318 root 1.5 if (old_score->exp >= new_score.exp)
319     new_draw_info (NDI_UNIQUE, 0, op, "You didn't beat your last score:");
320     else
321     new_draw_info (NDI_UNIQUE, 0, op, "You beat your last score:");
322 elmex 1.1
323 root 1.5 new_draw_info (NDI_UNIQUE, 0, op, draw_one_high_score (old_score));
324     new_draw_info (NDI_UNIQUE, 0, op, draw_one_high_score (&new_score));
325 elmex 1.1 }
326    
327     /* displays the high score file. object is the calling object
328     * (null if being called via command line.) max is the maximum
329     * number of scores to display. match, if set, is the name or class
330     * to match to.
331     */
332    
333 root 1.5 void
334     display_high_score (object *op, int max, const char *match)
335     {
336     const size_t maxchar = 80;
337     FILE *fp;
338     char buf[MAX_BUF], *scorebuf, *bp, *cp;
339     int i = 0, j = 0, comp;
340     score *sc;
341    
342     sprintf (buf, "%s/%s", settings.localdir, HIGHSCORE);
343     if ((fp = open_and_uncompress (buf, 0, &comp)) == NULL)
344     {
345     LOG (llevError, "Cannot open highscore file %s: %s\n", buf, strerror (errno));
346     if (op != NULL)
347     new_draw_info (NDI_UNIQUE, 0, op, "There is no highscore file.");
348     return;
349     }
350     if (op != NULL)
351     clear_win_info (op);
352     new_draw_info (NDI_UNIQUE, 0, op, "Nr Score Who [max hp][max sp][max grace]");
353    
354     while (fgets (buf, MAX_BUF, fp) != NULL)
355     {
356     if (j >= HIGHSCORE_LENGTH || i >= (max - 1))
357     break;
358     if ((sc = get_score (buf)) == NULL)
359     break;
360     sc->position = ++j;
361     if (match == NULL)
362     {
363     scorebuf = draw_one_high_score (sc);
364     i++;
365     }
366     else
367     {
368     if (!strcasecmp (sc->name, match) || !strcasecmp (sc->title, match))
369     {
370     scorebuf = draw_one_high_score (sc);
371     i++;
372 root 1.2 }
373 root 1.5 else
374     continue;
375 root 1.2 }
376 root 1.5 /* Replaced what seemed to an overly complicated word wrap method
377     * still word wraps, but assumes at most 2 lines of data.
378     * mw - 2-12-97
379     */
380 root 1.7 assign (buf, scorebuf);
381    
382 root 1.5 cp = buf;
383     while (strlen (cp) > maxchar)
384     {
385     bp = cp + maxchar - 1;
386     while (*bp != ' ' && bp > cp)
387     bp--;
388     *bp = '\0';
389 root 1.7
390 root 1.5 if (op == NULL)
391 root 1.7 LOG (llevDebug, "%s\n", cp);
392 root 1.5 else
393 root 1.7 new_draw_info (NDI_UNIQUE, 0, op, cp);
394    
395 root 1.5 sprintf (buf, " %s", bp + 1);
396     cp = buf;
397     i++;
398 root 1.2 }
399 root 1.7
400 root 1.5 if (op == NULL)
401     LOG (llevDebug, "%s\n", buf);
402     else
403     new_draw_info (NDI_UNIQUE, 0, op, buf);
404 elmex 1.1 }
405 root 1.7
406 root 1.5 close_and_delete (fp, comp);
407 elmex 1.1 }