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

# Content
1 /*
2 * 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
25 #include <global.h>
26 #include <sproto.h>
27
28 /*
29 * The score structure is used when treating new high-scores
30 */
31 typedef struct scr
32 {
33 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 } 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 char *
48 spool (char *bp, char *error)
49 {
50 static char *prev_pos = NULL;
51 char *next_pos;
52
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 return NULL;
66 }
67 if ((next_pos = strchr (bp, ':')) != NULL)
68 {
69 *next_pos = '\0';
70 prev_pos = next_pos + 1;
71 }
72 else
73 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 static void
83 copy_score (score *sc1, score *sc2)
84 {
85 assign (sc2->name , sc1->name);
86 assign (sc2->title , sc1->title);
87 assign (sc2->killer , sc1->killer);
88 assign (sc2->maplevel, sc1->maplevel);
89 sc2->exp = sc1->exp;
90 sc2->maxhp = sc1->maxhp;
91 sc2->maxsp = sc1->maxsp;
92 sc2->maxgrace = sc1->maxgrace;
93 }
94
95 /*
96 * Writes the given score structure to a static buffer, and returns
97 * a pointer to it.
98 */
99
100 static char *
101 put_score (score *sc)
102 {
103 static char buf[MAX_BUF];
104
105 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 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 static score *
117 get_score (char *bp)
118 {
119 static score sc;
120 char *cp;
121
122 if ((cp = strchr (bp, '\n')) != NULL)
123 *cp = '\0';
124
125 if ((cp = spool (bp, "name")) == NULL)
126 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
138 sscanf (cp, "%" SCNd64, &sc.exp);
139
140 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
153 sscanf (cp, "%d", &sc.maxhp);
154
155 if ((cp = spool (0, "maxsp")) == NULL)
156 return 0;
157
158 sscanf (cp, "%d", &sc.maxsp);
159
160 if ((cp = spool (0, "maxgrace")) == NULL)
161 return 0;
162
163 sscanf (cp, "%d", &sc.maxgrace);
164 return &sc;
165 }
166
167 static char *
168 draw_one_high_score (score *sc)
169 {
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
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 static score *
190 add_score (score *new_score)
191 {
192 FILE *fp;
193 static score old_score;
194 score *tmp_score, pscore[HIGHSCORE_LENGTH];
195 char buf[MAX_BUF], filename[MAX_BUF], *bp;
196 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 /* Eneq(@csd.uu.se): Patch to fix error in adding a new score to the
245 hiscore-list */
246 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 return &old_score;
253 if (nrofscores)
254 {
255 copy_score (&pscore[nrofscores - 1], &old_score);
256 return &old_score;
257 }
258 LOG (llevError, "Highscore error.\n");
259 return NULL;
260 }
261
262 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 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 assign (new_score.maplevel, op->map ? op->map->name ? &op->map->name : &op->map->path : "");
275
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 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
287 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
299 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
304 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 }
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 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 }
354 else
355 continue;
356 }
357 /* 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 assign (buf, scorebuf);
362
363 cp = buf;
364 while (strlen (cp) > maxchar)
365 {
366 bp = cp + maxchar - 1;
367 while (*bp != ' ' && bp > cp)
368 bp--;
369 *bp = '\0';
370
371 if (op == NULL)
372 LOG (llevDebug, "%s\n", cp);
373 else
374 new_draw_info (NDI_UNIQUE, 0, op, cp);
375
376 sprintf (buf, " %s", bp + 1);
377 cp = buf;
378 i++;
379 }
380
381 if (op == NULL)
382 LOG (llevDebug, "%s\n", buf);
383 else
384 new_draw_info (NDI_UNIQUE, 0, op, buf);
385 }
386
387 close_and_delete (fp, comp);
388 }