Blindfold removal fix
[slashemextended.git] / src / topten.c
blob4e9298ecc528fef353960695de2985d6ef586cc3
1 /* SCCS Id: @(#)topten.c 3.4 2000/01/21 */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 #include "hack.h"
6 #include "dlb.h"
7 #include "patchlevel.h"
9 #ifdef VMS
10 /* We don't want to rewrite the whole file, because that entails */
11 /* creating a new version which requires that the old one be deletable. */
12 # define UPDATE_RECORD_IN_PLACE
13 #endif
16 * Updating in place can leave junk at the end of the file in some
17 * circumstances (if it shrinks and the O.S. doesn't have a straightforward
18 * way to truncate it). The trailing junk is harmless and the code
19 * which reads the scores will ignore it.
21 #ifdef UPDATE_RECORD_IN_PLACE
22 static long final_fpos;
23 #endif
25 #define done_stopprint program_state.stopprint
27 #define newttentry() (struct toptenentry *) alloc(sizeof(struct toptenentry))
28 #define dealloc_ttentry(ttent) free((void *) (ttent))
29 #define NAMSZ 10 /* several parts of this code depend on this, don't just change this value! --Amy */
30 #define DTHSZ 1000
31 #define ROLESZ 3
32 #define PERSMAX 10000 /* entries per name/uid per char. allowed */
33 #define POINTSMIN 1 /* must be > 0 */
34 #define ENTRYMAX 10000 /* must be >= 10 */
36 #if !defined(MICRO) && !defined(MAC) && !defined(WIN32) && !defined(PUBLIC_SERVER)
37 #define PERS_IS_UID /* delete for PERSMAX per name; now per uid */
38 #endif
39 struct toptenentry {
40 struct toptenentry *tt_next;
41 #ifdef UPDATE_RECORD_IN_PLACE
42 long fpos;
43 #endif
44 long points;
45 int deathdnum, deathlev;
46 int maxlvl, hp, maxhp, deaths;
47 int ver_major, ver_minor, patchlevel;
48 long deathdate, birthdate;
49 #ifdef RECORD_CONDUCT
50 long conduct;
51 #endif
52 int uid;
53 char plrole[ROLESZ+1];
54 char plrace[ROLESZ+1];
55 char plgend[ROLESZ+1];
56 char plalign[ROLESZ+1];
57 char name[NAMSZ+1];
58 char death[DTHSZ+1];
59 } *tt_head;
61 STATIC_DCL void topten_print(const char *);
62 STATIC_DCL void topten_print_bold(const char *);
63 STATIC_DCL xchar observable_depth(d_level *);
64 STATIC_DCL void outheader(void);
65 STATIC_DCL void outentry(int,struct toptenentry *,BOOLEAN_P);
66 STATIC_DCL void readentry(FILE *,struct toptenentry *);
67 STATIC_DCL void writeentry(FILE *,struct toptenentry *);
68 STATIC_DCL void free_ttlist(struct toptenentry *);
69 #ifdef XLOGFILE
70 STATIC_DCL void write_xlentry(FILE *,struct toptenentry *);
71 STATIC_DCL long encodexlogflags(void);
72 #endif
73 STATIC_DCL int classmon(char *,BOOLEAN_P);
74 STATIC_DCL int score_wanted(BOOLEAN_P, int,struct toptenentry *,int,const char **,int);
75 #ifdef RECORD_CONDUCT
76 STATIC_DCL long encodeconduct(void);
77 #endif
78 #ifdef RECORD_ACHIEVE
79 STATIC_DCL long encodeachieve(void);
80 STATIC_DCL char* encodeachieveX(void);
81 #endif
82 #ifdef NO_SCAN_BRACK
83 STATIC_DCL void nsb_mung_line(char*);
84 STATIC_DCL void nsb_unmung_line(char*);
85 #endif
87 /* must fit with end.c; used in rip.c */
88 NEARDATA const char * const killed_by_prefix[] = {
89 "killed by ", "betrayed by ", "choked on ", "poisoned by ", "died of ",
90 "drowned in ", "burned by ", "dissolved in ", "crushed to death by ",
91 "petrified by ", "turned to slime by ", "killed by ",
92 "", "", "", "", ""
95 static winid toptenwin = WIN_ERR;
97 #ifdef RECORD_START_END_TIME
98 static time_t deathtime = 0L;
99 #endif
101 STATIC_OVL void
102 topten_print(x)
103 const char *x;
105 if (toptenwin == WIN_ERR)
106 raw_print(x);
107 else
108 putstr(toptenwin, ATR_NONE, x);
111 STATIC_OVL void
112 topten_print_bold(x)
113 const char *x;
115 if (toptenwin == WIN_ERR)
116 raw_print_bold(x);
117 else
118 putstr(toptenwin, ATR_BOLD, x);
121 STATIC_OVL xchar
122 observable_depth(lev)
123 d_level *lev;
125 #if 0 /* if we ever randomize the order of the elemental planes, we
126 must use a constant external representation in the record file */
127 if (In_endgame(lev)) {
128 if (Is_astralevel(lev)) return -5;
129 else if (Is_waterlevel(lev)) return -4;
130 else if (Is_firelevel(lev)) return -3;
131 else if (Is_airlevel(lev)) return -2;
132 else if (Is_earthlevel(lev)) return -1;
133 else return 0; /* ? */
134 } else
135 #endif
136 return depth(lev);
139 #ifdef RECORD_CONDUCT
140 long
141 encodeconduct(void)
143 long e = 0L;
145 if(u.uconduct.unvegetarian) e |= 0x1L;
146 if(u.uconduct.unvegan) e |= 0x2L;
147 if(u.uconduct.food) e |= 0x4L;
148 if(u.uconduct.gnostic) e |= 0x8L;
149 if(u.uconduct.weaphit) e |= 0x10L;
150 if(u.uconduct.killer) e |= 0x20L;
151 if(u.uconduct.literate) e |= 0x40L;
152 if(u.uconduct.polypiles) e |= 0x80L;
153 if(u.uconduct.polyselfs) e |= 0x100L;
154 if(u.uconduct.wishes) e |= 0x200L;
155 if(u.uconduct.wisharti) e |= 0x400L;
156 if(num_genocides()) e |= 0x800L;
157 if(u.uconduct.praydone) e |= 0x1000L;
159 return e;
161 #endif
163 STATIC_OVL void
164 readentry(rfile,tt)
165 FILE *rfile;
166 struct toptenentry *tt;
168 #ifdef NO_SCAN_BRACK /* Version_ Pts DgnLevs_ Hp___ Died__Born id */
169 static const char fmt[] = "%d %d %d %ld %d %d %d %d %d %d %ld %ld %d%*c";
170 static const char fmt005[] = "%s %c %s %s%*c";
171 static const char fmt33[] = "%s %s %s %s %s %s%*c";
172 #else
173 static const char fmt[] = "%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ";
174 static const char fmt005[] = "%s %c %[^,],%[^\n]%*c";
175 static const char fmt33[] = "%s %s %s %s %[^,],%[^\n]%*c";
176 #endif
178 #ifdef UPDATE_RECORD_IN_PLACE
179 /* note: fscanf() below must read the record's terminating newline */
180 final_fpos = tt->fpos = ftell(rfile);
181 #endif
182 #define TTFIELDS 13
184 #ifdef RECORD_CONDUCT
185 tt->conduct = 8191;
186 #endif
188 if(fscanf(rfile, fmt,
189 &tt->ver_major, &tt->ver_minor, &tt->patchlevel,
190 &tt->points, &tt->deathdnum, &tt->deathlev,
191 &tt->maxlvl, &tt->hp, &tt->maxhp, &tt->deaths,
192 &tt->deathdate, &tt->birthdate,
193 &tt->uid) != TTFIELDS)
194 #undef TTFIELDS
195 tt->points = 0;
196 else {
197 /* Check for backwards compatibility */
198 if (!tt->ver_major && !tt->ver_minor && tt->patchlevel < 6) {
199 int i;
201 if (fscanf(rfile, fmt005,
202 tt->plrole, tt->plgend,
203 tt->name, tt->death) != 4)
204 tt->points = 0;
205 tt->plrole[1] = '\0';
206 if ((i = str2role(tt->plrole)) >= 0)
207 strcpy(tt->plrole, roles[i].filecode);
208 tt->plrole[ROLESZ] = 0;
209 strcpy(tt->plrace, "?");
210 strcpy(tt->plgend, (tt->plgend[0] == 'M') ? "Mal" : "Fem");
211 strcpy(tt->plalign, "?");
212 } else if (fscanf(rfile, fmt33,
213 tt->plrole, tt->plrace, tt->plgend,
214 tt->plalign, tt->name, tt->death) != 6)
215 tt->points = 0;
216 #ifdef NO_SCAN_BRACK
217 if(tt->points > 0) {
218 nsb_unmung_line(tt->name);
219 nsb_unmung_line(tt->death);
221 #endif
223 #ifdef RECORD_CONDUCT
224 if(tt->points > 0) {
225 /* If the string "Conduct=%d" appears, set tt->conduct and remove that
226 * portion of the string */
227 char *dp, *dp2;
228 for(dp = tt->death; *dp; dp++) {
229 if(!strncmp(dp, " Conduct=", 9)) {
230 dp2 = dp + 9;
231 sscanf(dp2, "%ld", &tt->conduct);
232 /* Find trailing null or space */
233 while(*dp2 && *dp2 != ' ')
234 dp2++;
236 /* Cut out the " Conduct=" portion of the death string */
237 while(*dp2) {
238 *dp = *dp2;
239 dp2++;
240 dp++;
243 *dp = *dp2;
247 /* Sanity check */
248 if(tt->conduct < 0 || tt->conduct > 8191)
249 tt->conduct = 8191;
251 #endif
255 /* check old score entries for Y2K problem and fix whenever found */
256 if (tt->points > 0) {
257 if (tt->birthdate < 19000000L) tt->birthdate += 19000000L;
258 if (tt->deathdate < 19000000L) tt->deathdate += 19000000L;
262 STATIC_OVL void
263 writeentry(rfile,tt)
264 FILE *rfile;
265 struct toptenentry *tt;
267 #ifdef RECORD_CONDUCT
268 char *cp = eos(tt->death);
270 /* Add a trailing " Conduct=%d" to tt->death */
271 /*if(tt->conduct != 8191) {*/
272 cp = tt->death + strlen(tt->death);
273 sprintf(cp, " Conduct=%ld", tt->conduct);
274 /*}*/
275 #endif
277 #ifdef NO_SCAN_BRACK
278 nsb_mung_line(tt->name);
279 nsb_mung_line(tt->death);
280 /* Version_ Pts DgnLevs_ Hp___ Died__Born id */
281 (void) fprintf(rfile,"%d %d %d %ld %d %d %d %d %d %d %ld %ld %d ",
282 #else
283 (void) fprintf(rfile,"%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ",
284 #endif
285 tt->ver_major, tt->ver_minor, tt->patchlevel,
286 tt->points, tt->deathdnum, tt->deathlev,
287 tt->maxlvl, tt->hp, tt->maxhp, tt->deaths,
288 tt->deathdate, tt->birthdate, tt->uid);
289 if (!tt->ver_major && !tt->ver_minor && tt->patchlevel < 6)
290 #ifdef NO_SCAN_BRACK
291 (void) fprintf(rfile,"%s %c %s %s\n",
292 #else
293 (void) fprintf(rfile,"%s %c %s,%s\n",
294 #endif
295 tt->plrole, tt->plgend[0],
296 onlyspace(tt->name) ? "_" : tt->name, tt->death);
297 else
298 #ifdef NO_SCAN_BRACK
299 (void) fprintf(rfile,"%s %s %s %s %s %s\n",
300 #else
301 (void) fprintf(rfile,"%s %s %s %s %s,%s\n",
302 #endif
303 tt->plrole, tt->plrace, tt->plgend, tt->plalign,
304 onlyspace(tt->name) ? "_" : tt->name, tt->death);
306 #ifdef NO_SCAN_BRACK
307 nsb_unmung_line(tt->name);
308 nsb_unmung_line(tt->death);
309 #endif
311 #ifdef RECORD_CONDUCT
312 /* Return the tt->death line to the original form */
313 *cp = '\0';
314 #endif
317 #ifdef XLOGFILE
318 /* Use \t instead of ":" so that we don't have to mangle anything else (3.6.0)*/
319 #define XLOG_SEP "\t"
321 STATIC_OVL void
322 write_xlentry(rfile,tt)
323 FILE *rfile;
324 struct toptenentry *tt;
327 char buf[DTHSZ+1];
329 /* Log all of the data found in the regular logfile */
330 (void)fprintf(rfile,
331 "version=slex-%d.%d.%d"
332 XLOG_SEP "points=%ld"
333 XLOG_SEP "deathdnum=%d"
334 XLOG_SEP "deathlev=%d"
335 XLOG_SEP "maxlvl=%d"
336 XLOG_SEP "hp=%d"
337 XLOG_SEP "maxhp=%d"
338 XLOG_SEP "deaths=%d"
339 XLOG_SEP "deathdate=%ld"
340 XLOG_SEP "birthdate=%ld"
341 XLOG_SEP "uid=%d",
342 tt->ver_major, tt->ver_minor, tt->patchlevel,
343 tt->points, tt->deathdnum, tt->deathlev,
344 tt->maxlvl, tt->hp, tt->maxhp, tt->deaths,
345 tt->deathdate, tt->birthdate, tt->uid);
347 (void)fprintf(rfile,
348 XLOG_SEP "role=%s"
349 XLOG_SEP "race=%s"
350 XLOG_SEP "gender=%s"
351 XLOG_SEP "align=%s",
352 tt->plrole, tt->plrace, tt->plgend, tt->plalign);
354 (void)fprintf(rfile, XLOG_SEP "hybrid=%s", hybrid_strcode());
356 (void)fprintf(rfile, XLOG_SEP "gamemode=%s", gamemode_strcode());
358 (void)fprintf(rfile, XLOG_SEP "name=%s", plname);
360 (void)fprintf(rfile, XLOG_SEP "alias=%s", plalias);
362 (void)fprintf(rfile, XLOG_SEP "death=%s", tt->death);
364 #ifdef RECORD_CONDUCT
365 (void)fprintf(rfile, XLOG_SEP "conduct=0x%lx", 0x1fffL & ~encodeconduct());
366 #endif
368 #ifdef RECORD_TURNS
369 (void)fprintf(rfile, XLOG_SEP "turns=%ld", moves);
370 #endif
372 #ifdef RECORD_ACHIEVE
373 (void)fprintf(rfile, XLOG_SEP "achieve=0x%lx", encodeachieve());
374 (void)fprintf(rfile, XLOG_SEP "achieveX=%s", encodeachieveX());
375 #endif
377 #ifdef RECORD_REALTIME
378 (void)fprintf(rfile, XLOG_SEP "realtime=%ld", (long)realtime_data.realtime);
379 #endif
381 #ifdef RECORD_START_END_TIME
382 (void)fprintf(rfile, XLOG_SEP "starttime=%ld", (long)u.ubirthday);
383 (void)fprintf(rfile, XLOG_SEP "endtime=%ld", (long)deathtime);
384 #endif
386 /* Amy addition: unlike any other variant, your role and race can change during gameplay. This is for junethack,
387 * where a certain competition is about ascending as many as possible starting combinations. */
388 (void)fprintf(rfile, XLOG_SEP "role0=%s", ustartrole.filecode);
390 (void)fprintf(rfile, XLOG_SEP "race0=%s", ustartrace.filecode);
392 #ifdef RECORD_GENDER0
393 (void)fprintf(rfile, XLOG_SEP "gender0=%s", genders[flags.initgend].filecode);
394 #endif
396 #ifdef RECORD_ALIGN0
397 (void)fprintf(rfile, XLOG_SEP "align0=%s",
398 aligns[1 - u.ualignbase[A_ORIGINAL]].filecode);
399 #endif
401 fprintf(rfile, XLOG_SEP "flags=0x%lx", encodexlogflags());
403 (void)fprintf(rfile,"%s", "\n");
407 static long encodexlogflags(void) {
408 long tmp = 0L;
410 if (wizard)
411 tmp |= 1L << 0;
412 if (discover)
413 tmp |= 1L << 1;
414 if (u.freeplaymode)
415 tmp |= 1L << 2;
417 return tmp;
420 #undef XLOG_SEP
421 #endif /* XLOGFILE */
423 STATIC_OVL void
424 free_ttlist(tt)
425 struct toptenentry *tt;
427 struct toptenentry *ttnext;
429 while (tt->points > 0) {
430 ttnext = tt->tt_next;
431 dealloc_ttentry(tt);
432 tt = ttnext;
434 dealloc_ttentry(tt);
437 void
438 topten(how)
439 int how;
441 int uid = getuid();
442 int rank, rank0 = -1, rank1 = 0;
443 int occ_cnt = PERSMAX;
444 register struct toptenentry *t0, *tprev;
445 struct toptenentry *t1;
446 FILE *rfile;
447 register int flg = 0;
448 boolean t0_used;
449 #ifdef LOGFILE
450 FILE *lfile;
451 #endif /* LOGFILE */
452 #ifdef XLOGFILE
453 FILE *xlfile;
454 #endif /* XLOGFILE */
456 /* Under DICE 3.0, this crashes the system consistently, apparently due to
457 * corruption of *rfile somewhere. Until I figure this out, just cut out
458 * topten support entirely - at least then the game exits cleanly. --AC
460 #ifdef _DCC
461 return;
462 #endif
464 /* If we are in the midst of a panic, cut out topten entirely.
465 * topten uses alloc() several times, which will lead to
466 * problems if the panic was the result of an alloc() failure.
468 if (program_state.panicking)
469 return;
471 if (flags.toptenwin) {
472 toptenwin = create_nhwindow(NHW_TEXT);
475 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
476 #define HUP if (!program_state.done_hup)
477 #else
478 #define HUP
479 #endif
481 #ifdef TOS
482 restore_colors(); /* make sure the screen is black on white */
483 #endif
484 /* create a new 'topten' entry */
485 t0_used = FALSE;
486 t0 = newttentry();
487 /* deepest_lev_reached() is in terms of depth(), and reporting the
488 * deepest level reached in the dungeon death occurred in doesn't
489 * seem right, so we have to report the death level in depth() terms
490 * as well (which also seems reasonable since that's all the player
491 * sees on the screen anyway)
493 t0->ver_major = VERSION_MAJOR;
494 t0->ver_minor = VERSION_MINOR;
495 t0->patchlevel = PATCHLEVEL;
496 t0->points = u.urexp;
497 t0->deathdnum = u.uz.dnum;
498 t0->deathlev = observable_depth(&u.uz);
499 t0->maxlvl = deepest_lev_reached(TRUE);
500 t0->hp = u.uhp;
501 t0->maxhp = u.uhpmax;
502 t0->deaths = u.umortality;
503 t0->uid = uid;
504 (void) strncpy(t0->plrole, urole.filecode, ROLESZ);
505 t0->plrole[ROLESZ] = '\0';
506 (void) strncpy(t0->plrace, urace.filecode, ROLESZ);
507 t0->plrace[ROLESZ] = '\0';
508 (void) strncpy(t0->plgend, genders[flags.female].filecode, ROLESZ);
509 t0->plgend[ROLESZ] = '\0';
510 (void) strncpy(t0->plalign, aligns[1-u.ualign.type].filecode, ROLESZ);
511 t0->plalign[ROLESZ] = '\0';
512 (void) strncpy(t0->name, playeraliasname, NAMSZ);
513 t0->name[NAMSZ] = '\0';
514 t0->death[0] = '\0';
515 switch (killer_format) {
516 default: impossible("bad killer format?");
517 case KILLED_BY_AN:
518 strcat(t0->death, killed_by_prefix[how]);
519 (void) strncat(t0->death, an(killer),
520 DTHSZ-strlen(t0->death));
521 break;
522 case KILLED_BY:
523 strcat(t0->death, killed_by_prefix[how]);
524 (void) strncat(t0->death, killer,
525 DTHSZ-strlen(t0->death));
526 break;
527 case NO_KILLER_PREFIX:
528 (void) strncat(t0->death, killer, DTHSZ);
529 break;
531 t0->birthdate = yyyymmdd(u.ubirthday);
532 t0->deathdate = yyyymmdd((time_t)0L);
534 #ifdef RECORD_START_END_TIME
535 /* Make sure that deathdate and deathtime refer to the same time; it
536 * wouldn't be good to have deathtime refer to the day after deathdate. */
538 #if defined(BSD) && !defined(POSIX_TYPES)
539 (void) time((long *)&deathtime);
540 #else
541 (void) time(&deathtime);
542 #endif
544 t0->deathdate = yyyymmdd(deathtime);
545 #else
546 t0->deathdate = yyyymmdd((time_t)0L);
547 #endif /* RECORD_START_END_TIME */
549 #ifdef RECORD_CONDUCT
550 t0->conduct = encodeconduct();
551 #endif
552 t0->tt_next = 0;
553 #ifdef UPDATE_RECORD_IN_PLACE
554 t0->fpos = -1L;
555 #endif
557 #ifdef LOGFILE /* used for debugging (who dies of what, where) */
558 #ifdef FILE_AREAS
559 if (lock_file_area(LOGAREA, LOGFILE, 10)) {
560 #else
561 if (lock_file(LOGFILE, SCOREPREFIX, 10)) {
562 #endif
563 if(!(lfile = fopen_datafile_area(LOGAREA, LOGFILE, "a", SCOREPREFIX))) {
564 HUP raw_print("Cannot open log file!");
565 } else {
566 writeentry(lfile, t0);
567 (void) fclose(lfile);
569 unlock_file_area(LOGAREA, LOGFILE);
571 #endif /* LOGFILE */
573 #ifdef XLOGFILE
574 #ifdef FILE_AREAS
575 if(lock_file_area(LOGAREA, XLOGFILE, 10)) {
576 #else
577 if(lock_file(XLOGFILE, SCOREPREFIX, 10)) {
578 #endif
579 if(!(xlfile = fopen_datafile_area(LOGAREA, XLOGFILE, "a", SCOREPREFIX))) {
580 HUP raw_print("Cannot open extended log file!");
581 } else {
582 write_xlentry(xlfile, t0);
583 (void) fclose(xlfile);
585 unlock_file_area(LOGAREA, XLOGFILE);
587 #endif /* XLOGFILE */
589 if (wizard || discover || u.freeplaymode) {
590 if (how != PANICKED) HUP {
591 char pbuf[BUFSZ];
592 topten_print("");
593 sprintf(pbuf,
594 "Since you were in %s mode, the score list will not be checked.",
595 wizard ? "wizard" : u.freeplaymode ? "freeplay" : "discover");
596 topten_print(pbuf);
597 #ifdef DUMP_LOG
598 if (dump_fn[0]) {
599 dump("", pbuf);
600 dump("", "");
602 #endif
604 goto showwin;
607 /* "Forget Quitters" patch - Elronnd suggested that if the game went on for long enough, it should count --Amy */
608 if (how == QUIT && (moves && moves < 100)) {
609 char pbuf[]="Since you quit, the score list will not be checked.";
610 topten_print("");
611 topten_print(pbuf);
612 #ifdef DUMP_LOG
613 if (dump_fn[0]) {
614 dump("", pbuf);
615 dump("", "");
617 #endif
618 goto showwin;
621 /* some startscummers will escape instead, but probably very early --Amy */
622 if (how == ESCAPED && (moves && moves < 10)) {
623 char pbuf[]="Since you escaped early, the score list will not be checked.";
624 topten_print("");
625 topten_print(pbuf);
626 #ifdef DUMP_LOG
627 if (dump_fn[0]) {
628 dump("", pbuf);
629 dump("", "");
631 #endif
632 goto showwin;
635 #ifdef FILE_AREAS
636 if (!lock_file_area(NH_RECORD_AREA, NH_RECORD, 60))
637 #else
638 if (!lock_file(NH_RECORD, SCOREPREFIX, 60))
639 #endif
640 goto destroywin;
642 #ifdef UPDATE_RECORD_IN_PLACE
643 rfile = fopen_datafile_area(NH_RECORD_AREA, NH_RECORD, "r+", SCOREPREFIX);
644 #else
645 rfile = fopen_datafile_area(NH_RECORD_AREA, NH_RECORD, "r", SCOREPREFIX);
646 #endif
648 if (!rfile) {
649 HUP raw_print("Cannot open record file!");
650 unlock_file_area(NH_RECORD_AREA, NH_RECORD);
651 goto destroywin;
654 HUP topten_print("");
655 #ifdef DUMP_LOG
656 dump("", "");
657 #endif
659 /* assure minimum number of points */
660 if(t0->points < POINTSMIN) t0->points = 0;
662 t1 = tt_head = newttentry();
663 tprev = 0;
664 /* rank0: -1 undefined, 0 not_on_list, n n_th on list */
665 for(rank = 1; ; ) {
666 readentry(rfile, t1);
667 if (t1->points < POINTSMIN) t1->points = 0;
668 if(rank0 < 0 && t1->points < t0->points) {
669 rank0 = rank++;
670 if(tprev == 0)
671 tt_head = t0;
672 else
673 tprev->tt_next = t0;
674 t0->tt_next = t1;
675 #ifdef UPDATE_RECORD_IN_PLACE
676 t0->fpos = t1->fpos; /* insert here */
677 #endif
678 t0_used = TRUE;
679 occ_cnt--;
680 flg++; /* ask for a rewrite */
681 } else tprev = t1;
683 if(t1->points == 0) break;
685 #ifdef PERS_IS_UID
686 t1->uid == t0->uid &&
687 #else
688 strncmp(t1->name, t0->name, NAMSZ) == 0 &&
689 #endif
690 !strncmp(t1->plrole, t0->plrole, ROLESZ) &&
691 --occ_cnt <= 0) {
692 if(rank0 < 0) {
693 rank0 = 0;
694 rank1 = rank;
695 HUP {
696 char pbuf[BUFSZ];
697 sprintf(pbuf,
698 "You didn't beat your previous score of %ld points.",
699 t1->points);
700 topten_print(pbuf);
701 topten_print("");
702 #ifdef DUMP_LOG
703 dump("", pbuf);
704 dump("", "");
705 #endif
708 if(occ_cnt < 0) {
709 flg++;
710 continue;
713 if(rank <= ENTRYMAX) {
714 t1->tt_next = newttentry();
715 t1 = t1->tt_next;
716 rank++;
718 if(rank > ENTRYMAX) {
719 t1->points = 0;
720 break;
723 if(flg) { /* rewrite record file */
724 #ifdef UPDATE_RECORD_IN_PLACE
725 (void) fseek(rfile, (t0->fpos >= 0 ?
726 t0->fpos : final_fpos), SEEK_SET);
727 #else
728 (void) fclose(rfile);
729 if(!(rfile = fopen_datafile_area(NH_RECORD_AREA, NH_RECORD, "w", SCOREPREFIX))){
730 HUP raw_print("Cannot write record file");
731 unlock_file_area(NH_RECORD_AREA, NH_RECORD);
732 free_ttlist(tt_head);
733 goto destroywin;
735 #endif /* UPDATE_RECORD_IN_PLACE */
736 if(!done_stopprint) if(rank0 > 0){
737 if(rank0 <= 10) {
738 topten_print("You made the top ten list!");
739 #ifdef DUMP_LOG
740 dump("", "You made the top ten list!");
741 #endif
742 } else {
743 char pbuf[BUFSZ];
744 sprintf(pbuf,
745 "You reached the %d%s place on the top %d list.",
746 rank0, ordin(rank0), ENTRYMAX);
747 topten_print(pbuf);
748 #ifdef DUMP_LOG
749 dump("", pbuf);
750 #endif
753 topten_print("");
754 #ifdef DUMP_LOG
755 dump("", "");
756 #endif
760 if(rank0 == 0) rank0 = rank1;
761 if(rank0 <= 0) rank0 = rank;
762 if(!done_stopprint) outheader();
763 t1 = tt_head;
764 for(rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
765 if(flg
766 #ifdef UPDATE_RECORD_IN_PLACE
767 && rank >= rank0
768 #endif
769 ) writeentry(rfile, t1);
770 if (done_stopprint) continue;
771 if (rank > flags.end_top &&
772 (rank < rank0 - flags.end_around ||
773 rank > rank0 + flags.end_around) &&
774 (!flags.end_own ||
775 #ifdef PERS_IS_UID
776 t1->uid != t0->uid
777 #else
778 strncmp(t1->name, t0->name, NAMSZ)
779 #endif
780 )) continue;
781 if (rank == rank0 - flags.end_around &&
782 rank0 > flags.end_top + flags.end_around + 1 &&
783 !flags.end_own) {
784 topten_print("");
785 #ifdef DUMP_LOG
786 dump("", "");
787 #endif
789 if(rank != rank0)
790 outentry(rank, t1, FALSE);
791 else if(!rank1)
792 outentry(rank, t1, TRUE);
793 else {
794 outentry(rank, t1, TRUE);
795 outentry(0, t0, TRUE);
798 if(rank0 >= rank) if(!done_stopprint)
799 outentry(0, t0, TRUE);
800 #ifdef UPDATE_RECORD_IN_PLACE
801 if (flg) {
802 # ifdef TRUNCATE_FILE
803 /* if a reasonable way to truncate a file exists, use it */
804 truncate_file(rfile);
805 # else
806 /* use sentinel record rather than relying on truncation */
807 t1->points = 0L; /* terminates file when read back in */
808 t1->ver_major = t1->ver_minor = t1->patchlevel = 0;
809 t1->uid = t1->deathdnum = t1->deathlev = 0;
810 t1->maxlvl = t1->hp = t1->maxhp = t1->deaths = 0;
811 t1->plrole[0] = t1->plrace[0] = t1->plgend[0] = t1->plalign[0] = '-';
812 t1->plrole[1] = t1->plrace[1] = t1->plgend[1] = t1->plalign[1] = 0;
813 t1->birthdate = t1->deathdate = yyyymmdd((time_t)0L);
814 strcpy(t1->name, "@");
815 strcpy(t1->death, "<eod>\n");
816 writeentry(rfile, t1);
817 (void) fflush(rfile);
818 # endif /* TRUNCATE_FILE */
820 #endif /* UPDATE_RECORD_IN_PLACE */
821 (void) fclose(rfile);
822 unlock_file_area(NH_RECORD_AREA, NH_RECORD);
823 free_ttlist(tt_head);
825 showwin:
826 if (flags.toptenwin && !done_stopprint) display_nhwindow(toptenwin, 1);
827 destroywin:
828 if (!t0_used) dealloc_ttentry(t0);
829 if (flags.toptenwin) {
830 destroy_nhwindow(toptenwin);
831 toptenwin=WIN_ERR;
835 STATIC_OVL void
836 outheader()
838 char linebuf[BUFSZ];
839 register char *bp;
841 strcpy(linebuf, " No Points Name");
842 bp = eos(linebuf);
843 while(bp < linebuf + COLNO - 9) *bp++ = ' ';
844 strcpy(bp, "Hp [max]");
845 topten_print(linebuf);
846 #ifdef DUMP_LOG
847 dump("", linebuf);
848 #endif
851 /* so>0: standout line; so=0: ordinary line */
852 STATIC_OVL void
853 outentry(rank, t1, so)
854 struct toptenentry *t1;
855 int rank;
856 boolean so;
858 boolean second_line = TRUE;
859 char linebuf[BUFSZ];
860 char *bp, hpbuf[24], linebuf3[BUFSZ];
861 int hppos, lngr;
864 linebuf[0] = '\0';
865 if (rank) sprintf(eos(linebuf), "%3d", rank);
866 else strcat(linebuf, " ");
868 sprintf(eos(linebuf), " %10ld %.10s", t1->points, t1->name);
869 sprintf(eos(linebuf), "-%s", t1->plrole);
870 if (t1->plrace[0] != '?')
871 sprintf(eos(linebuf), "-%s", t1->plrace);
872 /* Printing of gender and alignment is intentional. It has been
873 * part of the NetHack Geek Code, and illustrates a proper way to
874 * specify a character from the command line.
876 sprintf(eos(linebuf), "-%s", t1->plgend);
877 if (t1->plalign[0] != '?')
878 sprintf(eos(linebuf), "-%s ", t1->plalign);
879 else
880 strcat(linebuf, " ");
881 if (!strncmp("escaped", t1->death, 7)) {
882 sprintf(eos(linebuf), "escaped the dungeon %s[max level %d]",
883 !strncmp(" (", t1->death + 7, 2) ? t1->death + 7 + 2 : "",
884 t1->maxlvl);
885 /* fixup for closing paren in "escaped... with...Amulet)[max..." */
886 if ((bp = index(linebuf, ')')) != 0)
887 *bp = (t1->deathdnum == astral_level.dnum) ? '\0' : ' ';
888 second_line = FALSE;
889 } else if (!strncmp("ascended", t1->death, 8)) {
891 #ifdef RECORD_CONDUCT
892 /* Add a notation for conducts kept */
893 if(t1->conduct != 4095) {
894 int i, m;
895 char dash = 0, skip;
896 const char *conduct_names[] = {
897 "Food", "Vgn", "Vgt", "Ath", "Weap", "Pac",
898 "Ill", "Poly", "Form", "Wish", "Art", "Geno",
899 NULL };
901 strcat(eos(linebuf), "(");
902 for(i = 0, m = 1; conduct_names[i]; i += skip + 1, m <<= (skip + 1)) {
903 skip = 0;
904 if(t1->conduct & m)
905 continue;
907 /* Only show one of foodless, vegan, vegetarian */
908 if(i == 0) skip = 2;
909 if(i == 1) skip = 1;
911 /* Only show one of wishless, artiwishless */
912 if(i == 9) skip = 1;
914 /* Add a hyphen for multiple conducts */
915 if(dash) strcat(eos(linebuf), "-");
916 strcat(eos(linebuf), conduct_names[i]);
917 dash = 1;
919 strcat(eos(linebuf), ") ");
921 #endif
923 sprintf(eos(linebuf), "ascended to demigod%s-hood",
924 (t1->plgend[0] == 'F') ? "dess" : "");
925 second_line = FALSE;
926 } else {
927 if (!strncmp(t1->death, "quit", 4)) {
928 strcat(linebuf, "quit");
929 second_line = FALSE;
930 } else if (!strncmp(t1->death, "died of st", 10)) {
931 strcat(linebuf, "starved to death");
932 second_line = FALSE;
933 } else if (!strncmp(t1->death, "choked", 6)) {
934 sprintf(eos(linebuf), "choked on h%s food",
935 (t1->plgend[0] == 'F') ? "er" : "is");
936 } else if (!strncmp(t1->death, "poisoned", 8)) {
937 strcat(linebuf, "was poisoned");
938 } else if (!strncmp(t1->death, "crushed", 7)) {
939 strcat(linebuf, "was crushed to death");
940 } else if (!strncmp(t1->death, "petrified by ", 13)) {
941 strcat(linebuf, "turned to stone");
942 } else strcat(linebuf, "died");
944 if (t1->deathdnum == astral_level.dnum) {
945 int deathlev = t1->deathlev;
946 const char *arg, *fmt = " on the Plane of %s";
948 if (!t1->ver_major && !t1->ver_minor && t1->patchlevel < 7)
949 deathlev--;
951 switch (deathlev) {
952 case -5:
953 fmt = " on the %s Plane";
954 arg = "Astral"; break;
955 case -4:
956 arg = "Water"; break;
957 case -3:
958 arg = "Fire"; break;
959 case -2:
960 arg = "Air"; break;
961 case -1:
962 arg = "Earth"; break;
963 default:
964 arg = "Void"; break;
966 sprintf(eos(linebuf), fmt, arg);
967 } else {
968 sprintf(eos(linebuf), " in %s", dungeons[t1->deathdnum].dname);
969 /*if (t1->deathdnum != knox_level.dnum)*/ /* not working for some reason, so let's just remove the check */
970 sprintf(eos(linebuf), " on level %d", t1->deathlev);
971 if (t1->deathlev != t1->maxlvl)
972 sprintf(eos(linebuf), " [max %d]", t1->maxlvl);
975 /* kludge for "quit while already on Charon's boat" */
976 if (!strncmp(t1->death, "quit ", 5))
977 strcat(linebuf, t1->death + 4);
979 strcat(linebuf, ".");
981 /* Quit, starved, ascended, and escaped contain no second line */
982 if (second_line)
983 sprintf(eos(linebuf), " %c%s.", highc(*(t1->death)), t1->death+1);
985 lngr = (int)strlen(linebuf);
986 if (t1->hp <= 0) hpbuf[0] = '-', hpbuf[1] = '\0';
987 else sprintf(hpbuf, "%d", t1->hp);
988 /* beginning of hp column after padding (not actually padded yet) */
989 hppos = COLNO - (sizeof(" Hp [max]")-1); /* sizeof(str) includes \0 */
990 while (lngr >= hppos) {
991 for(bp = eos(linebuf);
992 !(*bp == ' ' && (bp-linebuf < hppos));
993 bp--)
995 /* special case: word is too long, wrap in the middle */
996 if (linebuf+15 >= bp) bp = linebuf + hppos - 1;
997 /* special case: if about to wrap in the middle of maximum
998 dungeon depth reached, wrap in front of it instead */
999 if (bp > linebuf + 5 && !strncmp(bp - 5, " [max", 5)) bp -= 5;
1001 /* gigabug from vanilla: infinite loop if the line is too long, fix from the variant that calls itself 3.7 */
1002 if (*bp != ' ')
1003 strcpy(linebuf3, bp);
1004 else
1005 strcpy(linebuf3, bp+1);
1007 *bp = 0;
1008 if (so) {
1009 while (bp < linebuf + (COLNO-1)) *bp++ = ' ';
1010 *bp = 0;
1011 topten_print_bold(linebuf);
1012 #ifdef DUMP_LOG
1013 dump("*", linebuf[0]==' '? linebuf+1: linebuf);
1014 #endif
1015 } else {
1016 topten_print(linebuf);
1017 #ifdef DUMP_LOG
1018 dump(" ", linebuf[0]==' '? linebuf+1: linebuf);
1019 #endif
1021 sprintf(linebuf, "%15s %s", "", linebuf3);
1022 lngr = strlen(linebuf);
1024 /* beginning of hp column not including padding */
1025 hppos = COLNO - 7 - (int)strlen(hpbuf);
1026 bp = eos(linebuf);
1028 if (bp <= linebuf + hppos) {
1029 /* pad any necessary blanks to the hit point entry */
1030 while (bp < linebuf + hppos) *bp++ = ' ';
1031 strcpy(bp, hpbuf);
1032 sprintf(eos(bp), " %s[%d]",
1033 (t1->maxhp < 10) ? " " : (t1->maxhp < 100) ? " " : "",
1034 t1->maxhp);
1037 if (so) {
1038 bp = eos(linebuf);
1039 if (so >= COLNO) so = COLNO-1;
1040 while (bp < linebuf + so) *bp++ = ' ';
1041 *bp = 0;
1042 topten_print_bold(linebuf);
1043 } else
1044 topten_print(linebuf);
1045 #ifdef DUMP_LOG
1046 dump(" ", linebuf[0]==' '? linebuf+1: linebuf);
1047 #endif
1050 STATIC_OVL int
1051 score_wanted(current_ver, rank, t1, playerct, players, uid)
1052 boolean current_ver;
1053 int rank;
1054 struct toptenentry *t1;
1055 int playerct;
1056 const char **players;
1057 int uid;
1059 int i;
1061 if (current_ver && (t1->ver_major != VERSION_MAJOR ||
1062 t1->ver_minor != VERSION_MINOR ||
1063 t1->patchlevel != PATCHLEVEL))
1064 return 0;
1066 #ifdef PERS_IS_UID
1067 if (!playerct && t1->uid == uid)
1068 return 1;
1069 #endif
1071 for (i = 0; i < playerct; i++) {
1072 if (players[i][0] == '-' && index("prga", players[i][1]) &&
1073 players[i][2] == 0 && i + 1 < playerct) {
1074 char *arg = (char *)players[i + 1];
1075 if ((players[i][1] == 'p' &&
1076 str2role(arg) == str2role(t1->plrole)) ||
1077 (players[i][1] == 'r' &&
1078 str2race(arg) == str2race(t1->plrace)) ||
1079 (players[i][1] == 'g' &&
1080 str2gend(arg) == str2gend(t1->plgend)) ||
1081 (players[i][1] == 'a' &&
1082 str2align(arg) == str2align(t1->plalign)))
1083 return 1;
1084 i++;
1086 else if (strcmp(players[i], "all") == 0 ||
1087 strncmp(t1->name, players[i], NAMSZ) == 0 ||
1088 (players[i][0] == '-' &&
1089 players[i][1] == t1->plrole[0] &&
1090 players[i][2] == 0) ||
1091 (digit(players[i][0]) && rank <= atoi(players[i])))
1092 return 1;
1094 return 0;
1097 #ifdef RECORD_ACHIEVE
1098 long
1099 encodeachieve(void)
1101 /* Achievement bitfield:
1102 * bit meaning
1103 * 0 obtained the Bell of Opening
1104 * 1 entered gehennom (by any means)
1105 * 2 obtained the Candelabrum of Invocation
1106 * 3 obtained the Book of the Dead
1107 * 4 performed the invocation ritual
1108 * 5 obtained the amulet
1109 * 6 entered elemental planes
1110 * 7 entered astral plane
1111 * 8 ascended (not "escaped in celestial disgrace!")
1112 * 9 obtained the luckstone from the Mines
1113 * 10 obtained the sokoban prize
1114 * 11 killed medusa
1115 * 12 killed Nightmare
1116 * 13 killed Vecna
1117 * 14 killed Beholder
1118 * 15 killed Ruggo
1119 * 16 killed Kroo
1120 * 17 killed Grund
1121 * 18 killed The Largest Giant
1122 * 19 killed Shelob
1123 * 20 killed Girtab
1124 * 21 killed Aphrodite
1125 * 22 killed Frankenstein
1126 * 23 killed Croesus
1127 * 24 killed Dagon
1128 * 25 killed Hydra
1129 * 26 imbued the Bell of Opening
1130 * 27 imbued the Amulet of Yendor
1133 long r;
1135 r = 0;
1137 if(achieve.get_bell) r |= 1L << 0;
1138 if(achieve.enter_gehennom) r |= 1L << 1;
1139 if(achieve.get_candelabrum) r |= 1L << 2;
1140 if(achieve.get_book) r |= 1L << 3;
1141 if(achieve.perform_invocation) r |= 1L << 4;
1142 if(achieve.get_amulet) r |= 1L << 5;
1143 if(In_endgame(&u.uz)) r |= 1L << 6;
1144 if(Is_astralevel(&u.uz)) r |= 1L << 7;
1145 if(achieve.ascended) r |= 1L << 8;
1146 if(achieve.get_luckstone) r |= 1L << 9;
1147 if(achieve.finish_sokoban) r |= 1L << 10;
1148 if(achieve.killed_medusa) r |= 1L << 11;
1149 if(achieve.killed_nightmare) r |= 1L << 12;
1150 if(achieve.killed_vecna) r |= 1L << 13;
1151 if(achieve.killed_beholder) r |= 1L << 14;
1152 if(achieve.killed_ruggo) r |= 1L << 15;
1153 if(achieve.killed_kroo) r |= 1L << 16;
1154 if(achieve.killed_grund) r |= 1L << 17;
1155 if(achieve.killed_largestgiant) r |= 1L << 18;
1156 if(achieve.killed_shelob) r |= 1L << 19;
1157 if(achieve.killed_girtab) r |= 1L << 20;
1158 if(achieve.killed_aphrodite) r |= 1L << 21;
1159 if(achieve.killed_frankenstein) r |= 1L << 22;
1160 if(achieve.killed_croesus) r |= 1L << 23;
1161 if(achieve.killed_dagon) r |= 1L << 24;
1162 if(achieve.killed_hydra) r |= 1L << 25;
1163 if(achieve.imbued_bell) r |= 1L << 26;
1164 if(achieve.imbued_amulet) r |= 1L << 27;
1166 return r;
1170 char encoded_achievements[BUFSZ];
1171 char* encodeachieveX(void)
1173 /* Achievement bitfield:
1174 * bit meaning
1175 * 0 killed an elder priest
1176 * 1 killed the Motherfucker Glass Golem
1177 * 2 killed Tiksrvzllat
1178 * 3 killed the BOFH
1179 * 4 reached the bottom of the Swimming Pools
1180 * 5 killed Erogenous Katia
1181 * 6 killed the Witch King of Angmar
1182 * 7 obtained the stone of magic resistance from the Deep Mines
1183 * 8 visited all five DevNull challenge dungeons
1184 * 9 killed the Minotaur of the Maze
1185 * 10 killed Kalwina
1186 * 11 killed Stahngnir
1187 * 12 killed Ariane
1188 * 13 completed the Rival Quest
1189 * 14 completed Minus World
1190 * 15 killed Vera
1191 * 16 killed Elaine
1192 * but this isn't a bitfield, it's a string...
1195 encoded_achievements[0] = '\0';
1197 if(achieveX.killed_elderpriest) sprintf(eos(encoded_achievements), "%s,", "killed_elderpriest");
1198 if(achieveX.killed_glassgolem) sprintf(eos(encoded_achievements), "%s,", "killed_glassgolem");
1199 if(achieveX.killed_tiksrvzllat) sprintf(eos(encoded_achievements), "%s,", "killed_tiksrvzllat");
1200 if(achieveX.killed_bofh) sprintf(eos(encoded_achievements), "%s,", "killed_bofh");
1201 if(achieveX.swimmingpool_cleared) sprintf(eos(encoded_achievements), "%s,", "swimmingpool_cleared");
1202 if(achieveX.killed_katia) sprintf(eos(encoded_achievements), "%s,", "killed_katia");
1203 if(achieveX.killed_witchking) sprintf(eos(encoded_achievements), "%s,", "killed_witchking");
1204 if(achieveX.get_magresstone) sprintf(eos(encoded_achievements), "%s,", "get_magresstone");
1205 if(achieveX.devnull_complete) sprintf(eos(encoded_achievements), "%s,", "devnull_complete");
1206 if(achieveX.killed_minotaur) sprintf(eos(encoded_achievements), "%s,", "killed_minotaur");
1207 if(achieveX.killed_kalwina) sprintf(eos(encoded_achievements), "%s,", "killed_kalwina");
1208 if(achieveX.killed_stahngnir) sprintf(eos(encoded_achievements), "%s,", "killed_stahngnir");
1209 if(achieveX.killed_ariane) sprintf(eos(encoded_achievements), "%s,", "killed_ariane");
1210 if(achieveX.completed_rivalquest) sprintf(eos(encoded_achievements), "%s,", "completed_rivalquest");
1211 if(achieveX.completed_minusworld) sprintf(eos(encoded_achievements), "%s,", "completed_minusworld");
1212 if(achieveX.killed_vera) sprintf(eos(encoded_achievements), "%s,", "killed_vera");
1213 if(achieveX.killed_elaine) sprintf(eos(encoded_achievements), "%s,", "killed_elaine");
1215 int len;
1216 if ((len=strlen(encoded_achievements))) { encoded_achievements[len-1] = '\0'; }
1217 return encoded_achievements;
1219 #endif
1222 * print selected parts of score list.
1223 * argc >= 2, with argv[0] untrustworthy (directory names, et al.),
1224 * and argv[1] starting with "-s".
1226 void
1227 prscore(argc,argv)
1228 int argc;
1229 char **argv;
1231 const char **players;
1232 int playerct, rank;
1233 boolean current_ver = TRUE, init_done = FALSE;
1234 register struct toptenentry *t1;
1235 FILE *rfile;
1236 boolean match_found = FALSE;
1237 register int i;
1238 char pbuf[BUFSZ];
1239 int uid = -1;
1240 #ifndef PERS_IS_UID
1241 const char *player0;
1242 #endif
1244 if (argc < 2 || strncmp(argv[1], "-s", 2)) {
1245 raw_printf("prscore: bad arguments (%d)", argc);
1246 return;
1249 rfile = fopen_datafile_area(NH_RECORD_AREA, NH_RECORD, "r", SCOREPREFIX);
1250 if (!rfile) {
1251 raw_print("Cannot open record file!");
1252 return;
1255 #ifdef AMIGA
1257 extern winid amii_rawprwin;
1258 init_nhwindows(&argc, argv);
1259 amii_rawprwin = create_nhwindow(NHW_TEXT);
1261 #endif
1263 /* If the score list isn't after a game, we never went through
1264 * initialization. */
1265 if (wiz1_level.dlevel == 0) {
1266 dlb_init();
1267 init_dungeons();
1268 init_done = TRUE;
1271 if (!argv[1][2]){ /* plain "-s" */
1272 argc--;
1273 argv++;
1274 } else argv[1] += 2;
1276 if (argc > 1 && !strcmp(argv[1], "-v")) {
1277 current_ver = FALSE;
1278 argc--;
1279 argv++;
1282 if (argc <= 1) {
1283 #ifdef PERS_IS_UID
1284 uid = getuid();
1285 playerct = 0;
1286 players = (const char **)0;
1287 #else
1288 player0 = plname;
1289 if (!*player0)
1290 # ifdef AMIGA
1291 player0 = "all"; /* single user system */
1292 # else
1293 player0 = "hackplayer";
1294 # endif
1295 playerct = 1;
1296 players = &player0;
1297 #endif
1298 } else {
1299 playerct = --argc;
1300 players = (const char **)++argv;
1302 raw_print("");
1304 t1 = tt_head = newttentry();
1305 for (rank = 1; ; rank++) {
1306 readentry(rfile, t1);
1307 if (t1->points == 0) break;
1308 if (!match_found &&
1309 score_wanted(current_ver, rank, t1, playerct, players, uid))
1310 match_found = TRUE;
1311 t1->tt_next = newttentry();
1312 t1 = t1->tt_next;
1315 (void) fclose(rfile);
1316 if (init_done) {
1317 free_dungeons();
1318 dlb_cleanup();
1321 if (match_found) {
1322 outheader();
1323 t1 = tt_head;
1324 for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
1325 if (score_wanted(current_ver, rank, t1, playerct, players, uid))
1326 (void) outentry(rank, t1, 0);
1328 } else {
1329 sprintf(pbuf, "Cannot find any %sentries for ",
1330 current_ver ? "current " : "");
1331 if (playerct < 1) strcat(pbuf, "you.");
1332 else {
1333 if (playerct > 1) strcat(pbuf, "any of ");
1334 for (i = 0; i < playerct; i++) {
1335 /* stop printing players if there are too many to fit */
1336 if (strlen(pbuf) + strlen(players[i]) + 2 >= BUFSZ) {
1337 if (strlen(pbuf) < BUFSZ-4) strcat(pbuf, "...");
1338 else strcpy(pbuf+strlen(pbuf)-4, "...");
1339 break;
1341 strcat(pbuf, players[i]);
1342 if (i < playerct-1) {
1343 if (players[i][0] == '-' &&
1344 index("prga", players[i][1]) && players[i][2] == 0)
1345 strcat(pbuf, " ");
1346 else strcat(pbuf, ":");
1350 raw_print(pbuf);
1351 raw_printf("Usage: %s -s [-v] <playertypes> [maxrank] [playernames]",
1353 hname);
1354 raw_printf("Player types are: [-p role] [-r race] [-g gender] [-a align]");
1356 free_ttlist(tt_head);
1357 #ifdef AMIGA
1359 extern winid amii_rawprwin;
1360 display_nhwindow(amii_rawprwin, 1);
1361 destroy_nhwindow(amii_rawprwin);
1362 amii_rawprwin = WIN_ERR;
1364 #endif
1367 STATIC_OVL int
1368 classmon(plch, fem)
1369 char *plch;
1370 boolean fem;
1372 int i;
1374 /* Look for this role in the role table */
1375 for (i = 0; roles[i].name.m; i++)
1376 if (!strncmp(plch, roles[i].filecode, ROLESZ)) {
1377 if (fem && roles[i].femalenum != NON_PM)
1378 return roles[i].femalenum;
1379 else if (roles[i].malenum != NON_PM)
1380 return roles[i].malenum;
1381 else
1382 return PM_HUMAN;
1384 /* this might be from a 3.2.x score for former Elf class */
1385 if (!strcmp(plch, "E")) return PM_RANGER;
1387 impossible("What weird role is this? (%s)", plch);
1388 return (PM_HUMAN_MUMMY);
1391 STATIC_OVL int
1392 undeadclassmon(plch, fem)
1393 char *plch;
1394 boolean fem;
1396 int i;
1398 /* Look for this role in the role table */
1399 for (i = 0; roles[i].name.m; i++)
1400 if (!strncmp(plch, roles[i].filecode, ROLESZ)) {
1401 if (fem && roles[i].undeadfemalenum != NON_PM)
1402 return roles[i].undeadfemalenum;
1403 else if (roles[i].undeadmalenum != NON_PM)
1404 return roles[i].undeadmalenum;
1405 else
1406 return PM_HUMAN;
1408 /* this might be from a 3.2.x score for former Elf class */
1409 if (!strcmp(plch, "E")) return PM_RANGER;
1411 impossible("What weird role is this? (%s)", plch);
1412 return (PM_HUMAN_MUMMY);
1416 * Get a random player name and class from the high score list,
1417 * and attach them to an object (for statues or morgue corpses).
1419 struct obj *
1420 tt_oname(otmp)
1421 struct obj *otmp;
1423 int rank, rankamount;
1424 rankamount = 1000;
1425 register int i;
1426 register struct toptenentry *tt;
1427 FILE *rfile;
1428 struct toptenentry tt_buf;
1430 if (!otmp) return((struct obj *) 0);
1432 rfile = fopen_datafile_area(NH_RECORD_AREA, NH_RECORD, "r", SCOREPREFIX);
1433 if (!rfile) {
1434 impossible("Cannot open record file!");
1435 return (struct obj *)0;
1438 tt = &tt_buf;
1440 rank = rnd(rankamount); /* new code by Amy that allows more randomness - up to 1000 entries can be read now */
1441 pickentry:
1442 rank = rnd(rankamount);
1443 for(i = rank; i; i--) {
1444 readentry(rfile, tt);
1445 if(tt->points == 0) break;
1448 if(tt->points == 0) {
1449 if(rank > 1) {
1450 rankamount = (rank - 1);
1451 if (rankamount < 1) {
1452 impossible("Not enough records!");
1453 return (struct obj *)0;
1455 rewind(rfile);
1456 goto pickentry;
1458 otmp = (struct obj *) 0;
1459 } else {
1460 /* reset timer in case corpse started out as lizard or troll */
1461 if (otmp->otyp == CORPSE) obj_stop_timers(otmp);
1462 otmp->corpsenm = classmon(tt->plrole, (tt->plgend[0] == 'F'));
1463 otmp->owt = weight(otmp);
1464 otmp = oname(otmp, tt->name);
1465 if (otmp->otyp == CORPSE) start_corpse_timeout(otmp);
1468 (void) fclose(rfile);
1469 return otmp;
1473 * Get a random player name and class from the high score list,
1474 * and attach them to a monster (for ghost summon spell). --Amy
1476 void
1477 tt_mname(mm, revive_corpses, mm_flags)
1478 coord *mm;
1479 boolean revive_corpses;
1480 int mm_flags;
1482 struct monst *mtmp;
1483 int nonefound;
1485 int cnt = 1;
1486 if (!rn2(2)) cnt = (monster_difficulty() + 1)/10;
1487 if (!rn2(5)) cnt += rnz(5);
1488 if (cnt < 1) cnt = 1;
1489 int mdat;
1490 struct obj *otmp;
1491 coord cc;
1493 while (cnt--) {
1496 int rank, rankamount;
1497 rankamount = 1000;
1498 register int i;
1499 register struct toptenentry *tt;
1500 FILE *rfile;
1501 struct toptenentry tt_buf;
1503 /*if (!mtmp) { pline("No records!");
1507 rfile = fopen_datafile_area(NH_RECORD_AREA, NH_RECORD, "r", SCOREPREFIX);
1508 if (!rfile) {
1509 impossible("Cannot open record file!");
1512 tt = &tt_buf;
1514 rank = rnd(rankamount); /* new code by Amy that allows more randomness - up to 1000 entries can be read now */
1515 pickentry:
1516 rank = rnd(rankamount);
1517 for(i = rank; i; i--) {
1518 readentry(rfile, tt);
1519 if(tt->points == 0) break;
1522 if(tt->points == 0) {
1523 if(rank > 1) {
1524 rankamount = (rank - 1);
1525 if (rankamount < 1) {
1526 impossible("Not enough records!");
1528 rewind(rfile);
1529 goto pickentry;
1532 /* we should only end up here if there are no entries --Amy */
1533 if (enexto(&cc, mm->x, mm->y, youmonst.data) &&
1534 (!revive_corpses ||
1535 !(otmp = sobj_at(CORPSE, cc.x, cc.y)) ||
1536 !revive(otmp)))
1538 mtmp = makemon(&mons[PM_UNDEAD_ARCHEOLOGIST + rn2(PM_UNDEAD_WIZARD - PM_UNDEAD_ARCHEOLOGIST + 1)], cc.x, cc.y, mm_flags);
1540 /*mtmp = (struct monst *) 0;*/
1541 } else {
1543 /*mtmp = undeadclassmon(tt->plrole, (tt->plgend[0] == 'F')) ;*/
1545 if (enexto(&cc, mm->x, mm->y, youmonst.data) &&
1546 (!revive_corpses ||
1547 !(otmp = sobj_at(CORPSE, cc.x, cc.y)) ||
1548 !revive(otmp)))
1552 mtmp = makemon(&mons[undeadclassmon(tt->plrole, (tt->plgend[0] == 'F'))], cc.x, cc.y, mm_flags);
1553 christen_monst(mtmp, tt->name);
1554 /*mtmp = christen_monst(mtmp, tt->name);*/
1558 (void) fclose(rfile);
1563 #ifdef NO_SCAN_BRACK
1564 /* Lattice scanf isn't up to reading the scorefile. What */
1565 /* follows deals with that; I admit it's ugly. (KL) */
1566 /* Now generally available (KL) */
1567 STATIC_OVL void
1568 nsb_mung_line(p)
1569 char *p;
1571 while ((p = index(p, ' ')) != 0) *p = '|';
1574 STATIC_OVL void
1575 nsb_unmung_line(p)
1576 char *p;
1578 while ((p = index(p, '|')) != 0) *p = ' ';
1580 #endif /* NO_SCAN_BRACK */
1582 #if defined(GTK_GRAPHICS) || defined(PROXY_GRAPHICS)
1583 winid
1584 create_toptenwin()
1586 toptenwin = create_nhwindow(NHW_TEXT);
1588 return toptenwin;
1591 void
1592 destroy_toptenwin()
1594 destroy_nhwindow(toptenwin);
1595 toptenwin = WIN_ERR;
1597 #endif
1599 const char *
1600 gamemode_strcode()
1602 static char string[BUFSZ];
1603 *string = '\0';
1605 if (u.freeplaymode) sprintf(eos(string), "freeplay");
1606 if (flags.gehenna) sprintf(eos(string), "gehenna");
1607 if (flags.dudley) sprintf(eos(string), "dudley");
1608 if (flags.iwbtg) sprintf(eos(string), "iwbtg");
1609 if (flags.elmstreet) sprintf(eos(string), "elmstreet");
1610 if (flags.hippie) sprintf(eos(string), "hippie");
1611 if (flags.blindfox) sprintf(eos(string), "blindfox");
1612 if (flags.uberlostsoul) sprintf(eos(string), "uberlostsoul");
1613 if (flags.lostsoul && !(flags.uberlostsoul)) sprintf(eos(string), "lostsoul");
1614 #ifdef GMMODE
1615 if (flags.gmmode) sprintf(eos(string), "gmmode");
1616 if (flags.supergmmode) sprintf(eos(string), "supergmmode");
1617 #endif
1618 if (flags.wonderland) sprintf(eos(string), "wonderland");
1619 if (flags.zapem) sprintf(eos(string), "zapm");
1620 if (flags.assholemode) sprintf(eos(string), "asshole");
1622 if (!u.freeplaymode && !(flags.gehenna) && !(flags.dudley)
1623 #ifdef GMMODE
1624 && !(flags.gmmode) && !(flags.supergmmode)
1625 #endif
1626 && !(flags.iwbtg) && !(flags.assholemode) && !(flags.elmstreet) && !(flags.hippie) && !(flags.blindfox) && !(flags.uberlostsoul) && !(flags.lostsoul) && !(flags.wonderland) && !(flags.zapem)) sprintf(eos(string), "none");
1628 return (string);
1631 /*topten.c*/