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, 8 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.5: +4 -10 lines
Log Message:
indent

File Contents

# Content
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 # include <sproto.h>
27 #endif
28
29 /*
30 * The score structure is used when treating new high-scores
31 */
32
33 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 } 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 char *
50 spool (char *bp, char *error)
51 {
52 static char *prev_pos = NULL;
53 char *next_pos;
54
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 return NULL;
68 }
69 if ((next_pos = strchr (bp, ':')) != NULL)
70 {
71 *next_pos = '\0';
72 prev_pos = next_pos + 1;
73 }
74 else
75 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 static void
85 copy_score (score *sc1, score *sc2)
86 {
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 }
99
100 /*
101 * Writes the given score structure to a static buffer, and returns
102 * a pointer to it.
103 */
104
105 static char *
106 put_score (score *sc)
107 {
108 static char buf[MAX_BUF];
109
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 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 static score *
121 get_score (char *bp)
122 {
123 static score sc;
124 char *cp;
125
126 if ((cp = strchr (bp, '\n')) != NULL)
127 *cp = '\0';
128
129 if ((cp = spool (bp, "name")) == NULL)
130 return NULL;
131 strncpy (sc.name, cp, BIG_NAME);
132 sc.name[BIG_NAME - 1] = '\0';
133
134 if ((cp = spool (NULL, "title")) == NULL)
135 return NULL;
136 strncpy (sc.title, cp, BIG_NAME);
137 sc.title[BIG_NAME - 1] = '\0';
138
139 if ((cp = spool (NULL, "score")) == NULL)
140 return NULL;
141 long long exp;
142
143 sscanf (cp, "%lld", &exp);
144 sc.exp = exp;
145
146 if ((cp = spool (NULL, "killer")) == NULL)
147 return NULL;
148 strncpy (sc.killer, cp, BIG_NAME);
149 sc.killer[BIG_NAME - 1] = '\0';
150
151 if ((cp = spool (NULL, "map")) == NULL)
152 return NULL;
153 strncpy (sc.maplevel, cp, BIG_NAME);
154 sc.maplevel[BIG_NAME - 1] = '\0';
155
156 if ((cp = spool (NULL, "maxhp")) == NULL)
157 return NULL;
158 sscanf (cp, "%d", &sc.maxhp);
159
160 if ((cp = spool (NULL, "maxsp")) == NULL)
161 return NULL;
162 sscanf (cp, "%d", &sc.maxsp);
163
164 if ((cp = spool (NULL, "maxgrace")) == NULL)
165 return NULL;
166 sscanf (cp, "%d", &sc.maxgrace);
167 return &sc;
168 }
169
170 static char *
171 draw_one_high_score (score *sc)
172 {
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
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 static score *
193 add_score (score *new_score)
194 {
195 FILE *fp;
196 static score old_score;
197 score *tmp_score, pscore[HIGHSCORE_LENGTH];
198 char buf[MAX_BUF], filename[MAX_BUF], *bp;
199 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 /* Eneq(@csd.uu.se): Patch to fix error in adding a new score to the
248 hiscore-list */
249 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 return &old_score;
256 if (nrofscores)
257 {
258 copy_score (&pscore[nrofscores - 1], &old_score);
259 return &old_score;
260 }
261 LOG (llevError, "Highscore error.\n");
262 return NULL;
263 }
264
265 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 }
281 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 }
295 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
337 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 }
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 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 }
389 else
390 continue;
391 }
392 /* 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 }
409 else
410 {
411 new_draw_info (NDI_UNIQUE, 0, op, cp);
412 }
413 sprintf (buf, " %s", bp + 1);
414 cp = buf;
415 i++;
416 }
417 if (op == NULL)
418 LOG (llevDebug, "%s\n", buf);
419 else
420 new_draw_info (NDI_UNIQUE, 0, op, buf);
421 }
422 close_and_delete (fp, comp);
423 }