ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/hiscore.C
Revision: 1.13
Committed: Sat Dec 23 06:21:02 2006 UTC (17 years, 5 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.12: +0 -10 lines
Log Message:
- improved login code
- removed some cruft code and (finally), the player_pod hack

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@schmorp.de>
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[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 } 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 assign (sc2->name , sc1->name);
88 assign (sc2->title , sc1->title);
89 assign (sc2->killer , sc1->killer);
90 assign (sc2->maplevel, sc1->maplevel);
91 sc2->exp = sc1->exp;
92 sc2->maxhp = sc1->maxhp;
93 sc2->maxsp = sc1->maxsp;
94 sc2->maxgrace = sc1->maxgrace;
95 }
96
97 /*
98 * Writes the given score structure to a static buffer, and returns
99 * a pointer to it.
100 */
101
102 static char *
103 put_score (score *sc)
104 {
105 static char buf[MAX_BUF];
106
107 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 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 static score *
119 get_score (char *bp)
120 {
121 static score sc;
122 char *cp;
123
124 if ((cp = strchr (bp, '\n')) != NULL)
125 *cp = '\0';
126
127 if ((cp = spool (bp, "name")) == NULL)
128 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
140 sscanf (cp, "%" SCNd64, &sc.exp);
141
142 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
155 sscanf (cp, "%d", &sc.maxhp);
156
157 if ((cp = spool (0, "maxsp")) == NULL)
158 return 0;
159
160 sscanf (cp, "%d", &sc.maxsp);
161
162 if ((cp = spool (0, "maxgrace")) == NULL)
163 return 0;
164
165 sscanf (cp, "%d", &sc.maxgrace);
166 return &sc;
167 }
168
169 static char *
170 draw_one_high_score (score *sc)
171 {
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
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 static score *
192 add_score (score *new_score)
193 {
194 FILE *fp;
195 static score old_score;
196 score *tmp_score, pscore[HIGHSCORE_LENGTH];
197 char buf[MAX_BUF], filename[MAX_BUF], *bp;
198 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 /* Eneq(@csd.uu.se): Patch to fix error in adding a new score to the
247 hiscore-list */
248 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 return &old_score;
255 if (nrofscores)
256 {
257 copy_score (&pscore[nrofscores - 1], &old_score);
258 return &old_score;
259 }
260 LOG (llevError, "Highscore error.\n");
261 return NULL;
262 }
263
264 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->explore)
274 {
275 new_draw_info (NDI_UNIQUE, 0, op, "Since you were in explore mode,");
276 new_draw_info (NDI_UNIQUE, 0, op, "you can't enter the high-score list.");
277 return;
278 }
279
280 assign (new_score.name, op->name);
281 assign (new_score.title, op->title.length () ? &op->title : op->contr->title);
282 assign (new_score.killer, op->contr->killer[0] ? op->contr->killer : "a dungeon collapse");
283 assign (new_score.maplevel, op->map ? op->map->name ? op->map->name : op->map->path : "");
284
285 new_score.exp = op->stats.exp;
286 new_score.maxhp = op->stats.maxhp;
287 new_score.maxsp = op->stats.maxsp;
288 new_score.maxgrace = op->stats.maxgrace;
289
290 if ((old_score = add_score (&new_score)) == NULL)
291 {
292 new_draw_info (NDI_UNIQUE, 0, op, "Error in the highscore list.");
293 return;
294 }
295
296 if (new_score.position == -1)
297 {
298 new_score.position = HIGHSCORE_LENGTH + 1; /* Not strictly correct... */
299 if (!strcmp (old_score->name, new_score.name))
300 new_draw_info (NDI_UNIQUE, 0, op, "You didn't beat your last highscore:");
301 else
302 new_draw_info (NDI_UNIQUE, 0, op, "You didn't enter the highscore list:");
303 new_draw_info (NDI_UNIQUE, 0, op, draw_one_high_score (old_score));
304 new_draw_info (NDI_UNIQUE, 0, op, draw_one_high_score (&new_score));
305 return;
306 }
307
308 if (old_score->exp >= new_score.exp)
309 new_draw_info (NDI_UNIQUE, 0, op, "You didn't beat your last score:");
310 else
311 new_draw_info (NDI_UNIQUE, 0, op, "You beat your last score:");
312
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 }
316
317 /* displays the high score file. object is the calling object
318 * (null if being called via command line.) max is the maximum
319 * number of scores to display. match, if set, is the name or class
320 * to match to.
321 */
322
323 void
324 display_high_score (object *op, int max, const char *match)
325 {
326 const size_t maxchar = 80;
327 FILE *fp;
328 char buf[MAX_BUF], *scorebuf, *bp, *cp;
329 int i = 0, j = 0, comp;
330 score *sc;
331
332 sprintf (buf, "%s/%s", settings.localdir, HIGHSCORE);
333 if ((fp = open_and_uncompress (buf, 0, &comp)) == NULL)
334 {
335 LOG (llevError, "Cannot open highscore file %s: %s\n", buf, strerror (errno));
336 if (op != NULL)
337 new_draw_info (NDI_UNIQUE, 0, op, "There is no highscore file.");
338 return;
339 }
340 if (op != NULL)
341 clear_win_info (op);
342 new_draw_info (NDI_UNIQUE, 0, op, "Nr Score Who [max hp][max sp][max grace]");
343
344 while (fgets (buf, MAX_BUF, fp) != NULL)
345 {
346 if (j >= HIGHSCORE_LENGTH || i >= (max - 1))
347 break;
348 if ((sc = get_score (buf)) == NULL)
349 break;
350 sc->position = ++j;
351 if (match == NULL)
352 {
353 scorebuf = draw_one_high_score (sc);
354 i++;
355 }
356 else
357 {
358 if (!strcasecmp (sc->name, match) || !strcasecmp (sc->title, match))
359 {
360 scorebuf = draw_one_high_score (sc);
361 i++;
362 }
363 else
364 continue;
365 }
366 /* Replaced what seemed to an overly complicated word wrap method
367 * still word wraps, but assumes at most 2 lines of data.
368 * mw - 2-12-97
369 */
370 assign (buf, scorebuf);
371
372 cp = buf;
373 while (strlen (cp) > maxchar)
374 {
375 bp = cp + maxchar - 1;
376 while (*bp != ' ' && bp > cp)
377 bp--;
378 *bp = '\0';
379
380 if (op == NULL)
381 LOG (llevDebug, "%s\n", cp);
382 else
383 new_draw_info (NDI_UNIQUE, 0, op, cp);
384
385 sprintf (buf, " %s", bp + 1);
386 cp = buf;
387 i++;
388 }
389
390 if (op == NULL)
391 LOG (llevDebug, "%s\n", buf);
392 else
393 new_draw_info (NDI_UNIQUE, 0, op, buf);
394 }
395
396 close_and_delete (fp, comp);
397 }