ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/hiscore.C
Revision: 1.15
Committed: Sat Dec 30 10:16:11 2006 UTC (17 years, 4 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.14: +1 -1 lines
Log Message:
preliminary snapshot check-in, DO NOT USE IN PRODUCTION SYSTEMS
See the Changes file for details

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