From 05cc5fd4fdfaa6af8da4f8620845113b3996a422 Mon Sep 17 00:00:00 2001 From: jwalz Date: Sat, 5 Jan 2002 21:05:53 +0000 Subject: [PATCH] *** empty log message *** --- src/topten.c | 978 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 978 insertions(+) create mode 100644 src/topten.c diff --git a/src/topten.c b/src/topten.c new file mode 100644 index 000000000..26da46372 --- /dev/null +++ b/src/topten.c @@ -0,0 +1,978 @@ +/* SCCS Id: @(#)topten.c 3.3 2000/01/21 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "dlb.h" +#ifdef SHORT_FILENAMES +#include "patchlev.h" +#else +#include "patchlevel.h" +#endif + +#ifdef VMS + /* We don't want to rewrite the whole file, because that entails */ + /* creating a new version which requires that the old one be deletable. */ +# define UPDATE_RECORD_IN_PLACE +#endif + +/* + * Updating in place can leave junk at the end of the file in some + * circumstances (if it shrinks and the O.S. doesn't have a straightforward + * way to truncate it). The trailing junk is harmless and the code + * which reads the scores will ignore it. + */ +#ifdef UPDATE_RECORD_IN_PLACE +static long final_fpos; +#endif + +#define done_stopprint program_state.stopprint + +#define newttentry() (struct toptenentry *) alloc(sizeof(struct toptenentry)) +#define dealloc_ttentry(ttent) free((genericptr_t) (ttent)) +#define NAMSZ 10 +#define DTHSZ 100 +#define ROLESZ 3 +#define PERSMAX 3 /* entries per name/uid per char. allowed */ +#define POINTSMIN 1 /* must be > 0 */ +#define ENTRYMAX 100 /* must be >= 10 */ + +#if !defined(MICRO) && !defined(MAC) +#define PERS_IS_UID /* delete for PERSMAX per name; now per uid */ +#endif +struct toptenentry { + struct toptenentry *tt_next; +#ifdef UPDATE_RECORD_IN_PLACE + long fpos; +#endif + long points; + int deathdnum, deathlev; + int maxlvl, hp, maxhp, deaths; + int ver_major, ver_minor, patchlevel; + long deathdate, birthdate; + int uid; + char plrole[ROLESZ+1]; + char plrace[ROLESZ+1]; + char plgend[ROLESZ+1]; + char plalign[ROLESZ+1]; + char name[NAMSZ+1]; + char death[DTHSZ+1]; +} *tt_head; + +STATIC_DCL void FDECL(topten_print, (const char *)); +STATIC_DCL void FDECL(topten_print_bold, (const char *)); +STATIC_DCL xchar FDECL(observable_depth, (d_level *)); +STATIC_DCL void NDECL(outheader); +STATIC_DCL void FDECL(outentry, (int,struct toptenentry *,BOOLEAN_P)); +STATIC_DCL void FDECL(readentry, (FILE *,struct toptenentry *)); +STATIC_DCL void FDECL(writeentry, (FILE *,struct toptenentry *)); +STATIC_DCL void FDECL(free_ttlist, (struct toptenentry *)); +STATIC_DCL int FDECL(classmon, (char *,BOOLEAN_P)); +STATIC_DCL int FDECL(score_wanted, + (BOOLEAN_P, int,struct toptenentry *,int,const char **,int)); +#ifdef NO_SCAN_BRACK +STATIC_DCL void FDECL(nsb_mung_line,(char*)); +STATIC_DCL void FDECL(nsb_unmung_line,(char*)); +#endif + +/* must fit with end.c; used in rip.c */ +NEARDATA const char *killed_by_prefix[] = { + "killed by ", "choked on ", "poisoned by ", "", "drowned in ", + "", "dissolved in ", "crushed to death by ", "petrified by ", + "turned to slime by ", "killed by ", "", "", "", "", "" +}; + +static winid toptenwin = WIN_ERR; + +STATIC_OVL void +topten_print(x) +const char *x; +{ + if (toptenwin == WIN_ERR) + raw_print(x); + else + putstr(toptenwin, ATR_NONE, x); +} + +STATIC_OVL void +topten_print_bold(x) +const char *x; +{ + if (toptenwin == WIN_ERR) + raw_print_bold(x); + else + putstr(toptenwin, ATR_BOLD, x); +} + +STATIC_OVL xchar +observable_depth(lev) +d_level *lev; +{ +#if 0 /* if we ever randomize the order of the elemental planes, we + must use a constant external representation in the record file */ + if (In_endgame(lev)) { + if (Is_astralevel(lev)) return -5; + else if (Is_waterlevel(lev)) return -4; + else if (Is_firelevel(lev)) return -3; + else if (Is_airlevel(lev)) return -2; + else if (Is_earthlevel(lev)) return -1; + else return 0; /* ? */ + } else +#endif + return depth(lev); +} + +STATIC_OVL void +readentry(rfile,tt) +FILE *rfile; +struct toptenentry *tt; +{ +#ifdef NO_SCAN_BRACK /* Version_ Pts DgnLevs_ Hp___ Died__Born id */ + static const char *fmt = "%d %d %d %ld %d %d %d %d %d %d %ld %ld %d%*c"; + static const char *fmt32 = "%c%c %s %s%*c"; + static const char *fmt33 = "%s %s %s %s %s %s%*c"; +#else + static const char *fmt = "%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d "; + static const char *fmt32 = "%c%c %[^,],%[^\n]%*c"; + static const char *fmt33 = "%s %s %s %s %[^,],%[^\n]%*c"; +#endif + +#ifdef UPDATE_RECORD_IN_PLACE + /* note: fscanf() below must read the record's terminating newline */ + final_fpos = tt->fpos = ftell(rfile); +#endif +#define TTFIELDS 13 + if(fscanf(rfile, fmt, + &tt->ver_major, &tt->ver_minor, &tt->patchlevel, + &tt->points, &tt->deathdnum, &tt->deathlev, + &tt->maxlvl, &tt->hp, &tt->maxhp, &tt->deaths, + &tt->deathdate, &tt->birthdate, + &tt->uid) != TTFIELDS) +#undef TTFIELDS + tt->points = 0; + else { + /* Check for backwards compatibility */ + if (tt->ver_major < 3 || + (tt->ver_major == 3 && tt->ver_minor < 3)) { + int i; + + if (fscanf(rfile, fmt32, + tt->plrole, tt->plgend, + tt->name, tt->death) != 4) + tt->points = 0; + tt->plrole[1] = '\0'; + if ((i = str2role(tt->plrole)) >= 0) + Strcpy(tt->plrole, roles[i].filecode); + Strcpy(tt->plrace, "?"); + Strcpy(tt->plgend, (tt->plgend[0] == 'M') ? "Mal" : "Fem"); + Strcpy(tt->plalign, "?"); + } else if (fscanf(rfile, fmt33, + tt->plrole, tt->plrace, tt->plgend, + tt->plalign, tt->name, tt->death) != 6) + tt->points = 0; +#ifdef NO_SCAN_BRACK + if(tt->points > 0) { + nsb_unmung_line(tt->name); + nsb_unmung_line(tt->death); + } +#endif + } + + /* check old score entries for Y2K problem and fix whenever found */ + if (tt->points > 0) { + if (tt->birthdate < 19000000L) tt->birthdate += 19000000L; + if (tt->deathdate < 19000000L) tt->deathdate += 19000000L; + } +} + +STATIC_OVL void +writeentry(rfile,tt) +FILE *rfile; +struct toptenentry *tt; +{ +#ifdef NO_SCAN_BRACK + nsb_mung_line(tt->name); + nsb_mung_line(tt->death); + /* Version_ Pts DgnLevs_ Hp___ Died__Born id */ + (void) fprintf(rfile,"%d %d %d %ld %d %d %d %d %d %d %ld %ld %d ", +#else + (void) fprintf(rfile,"%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ", +#endif + tt->ver_major, tt->ver_minor, tt->patchlevel, + tt->points, tt->deathdnum, tt->deathlev, + tt->maxlvl, tt->hp, tt->maxhp, tt->deaths, + tt->deathdate, tt->birthdate, tt->uid); + if (tt->ver_major < 3 || + (tt->ver_major == 3 && tt->ver_minor < 3)) +#ifdef NO_SCAN_BRACK + (void) fprintf(rfile,"%c%c %s %s\n", +#else + (void) fprintf(rfile,"%c%c %s,%s\n", +#endif + tt->plrole[0], tt->plgend[0], + onlyspace(tt->name) ? "_" : tt->name, tt->death); + else +#ifdef NO_SCAN_BRACK + (void) fprintf(rfile,"%s %s %s %s %s %s\n", +#else + (void) fprintf(rfile,"%s %s %s %s %s,%s\n", +#endif + tt->plrole, tt->plrace, tt->plgend, tt->plalign, + onlyspace(tt->name) ? "_" : tt->name, tt->death); + +#ifdef NO_SCAN_BRACK + nsb_unmung_line(tt->name); + nsb_unmung_line(tt->death); +#endif +} + +STATIC_OVL void +free_ttlist(tt) +struct toptenentry *tt; +{ + struct toptenentry *ttnext; + + while (tt->points > 0) { + ttnext = tt->tt_next; + dealloc_ttentry(tt); + tt = ttnext; + } + dealloc_ttentry(tt); +} + +void +topten(how) +int how; +{ + int uid = getuid(); + int rank, rank0 = -1, rank1 = 0; + int occ_cnt = PERSMAX; + register struct toptenentry *t0, *tprev; + struct toptenentry *t1; + FILE *rfile; + register int flg = 0; + boolean t0_used; +#ifdef LOGFILE + FILE *lfile; +#endif /* LOGFILE */ + +/* Under DICE 3.0, this crashes the system consistently, apparently due to + * corruption of *rfile somewhere. Until I figure this out, just cut out + * topten support entirely - at least then the game exits cleanly. --AC + */ +#ifdef _DCC + return; +#endif + +/* If we are in the midst of a panic, cut out topten entirely. + * topten uses alloc() several times, which will lead to + * problems if the panic was the result of an alloc() failure. + */ + if (program_state.panicking) + return; + + if (flags.toptenwin) { + toptenwin = create_nhwindow(NHW_TEXT); + } + +#if defined(UNIX) || defined(VMS) || defined(__EMX__) +#define HUP if (!program_state.done_hup) +#else +#define HUP +#endif + +#ifdef TOS + restore_colors(); /* make sure the screen is black on white */ +#endif + /* create a new 'topten' entry */ + t0_used = FALSE; + t0 = newttentry(); + /* deepest_lev_reached() is in terms of depth(), and reporting the + * deepest level reached in the dungeon death occurred in doesn't + * seem right, so we have to report the death level in depth() terms + * as well (which also seems reasonable since that's all the player + * sees on the screen anyway) + */ + t0->ver_major = VERSION_MAJOR; + t0->ver_minor = VERSION_MINOR; + t0->patchlevel = PATCHLEVEL; + t0->points = u.urexp; + t0->deathdnum = u.uz.dnum; + t0->deathlev = observable_depth(&u.uz); + t0->maxlvl = deepest_lev_reached(TRUE); + t0->hp = u.uhp; + t0->maxhp = u.uhpmax; + t0->deaths = u.umortality; + t0->uid = uid; + (void) strncpy(t0->plrole, urole.filecode, ROLESZ); + t0->plrole[ROLESZ] = '\0'; + (void) strncpy(t0->plrace, urace.filecode, ROLESZ); + t0->plrace[ROLESZ] = '\0'; + (void) strncpy(t0->plgend, genders[flags.female].filecode, ROLESZ); + t0->plgend[ROLESZ] = '\0'; + (void) strncpy(t0->plalign, aligns[1-u.ualign.type].filecode, ROLESZ); + t0->plalign[ROLESZ] = '\0'; + (void) strncpy(t0->name, plname, NAMSZ); + t0->name[NAMSZ] = '\0'; + t0->death[0] = '\0'; + switch (killer_format) { + default: impossible("bad killer format?"); + case KILLED_BY_AN: + Strcat(t0->death, killed_by_prefix[how]); + (void) strncat(t0->death, an(killer), + DTHSZ-strlen(t0->death)); + break; + case KILLED_BY: + Strcat(t0->death, killed_by_prefix[how]); + (void) strncat(t0->death, killer, + DTHSZ-strlen(t0->death)); + break; + case NO_KILLER_PREFIX: + (void) strncat(t0->death, killer, DTHSZ); + break; + } + t0->birthdate = yyyymmdd(u.ubirthday); + t0->deathdate = yyyymmdd((time_t)0L); + t0->tt_next = 0; +#ifdef UPDATE_RECORD_IN_PLACE + t0->fpos = -1L; +#endif + +#ifdef LOGFILE /* used for debugging (who dies of what, where) */ + if (lock_file(LOGFILE, SCOREPREFIX, 10)) { + if(!(lfile = fopen_datafile(LOGFILE, "a", TRUE))) { + HUP raw_print("Cannot open log file!"); + } else { + writeentry(lfile, t0); + (void) fclose(lfile); + } + unlock_file(LOGFILE); + } +#endif /* LOGFILE */ + + if (wizard || discover) { + if (how != PANICKED) HUP { + char pbuf[BUFSZ]; + topten_print(""); + Sprintf(pbuf, + "Since you were in %s mode, the score list will not be checked.", + wizard ? "wizard" : "discover"); + topten_print(pbuf); + } + goto showwin; + } + + if (!lock_file(RECORD, SCOREPREFIX, 60)) + goto destroywin; + +#ifdef UPDATE_RECORD_IN_PLACE + rfile = fopen_datafile(RECORD, "r+", TRUE); +#else + rfile = fopen_datafile(RECORD, "r", TRUE); +#endif + + if (!rfile) { + HUP raw_print("Cannot open record file!"); + unlock_file(RECORD); + goto destroywin; + } + + HUP topten_print(""); + + /* assure minimum number of points */ + if(t0->points < POINTSMIN) t0->points = 0; + + t1 = tt_head = newttentry(); + tprev = 0; + /* rank0: -1 undefined, 0 not_on_list, n n_th on list */ + for(rank = 1; ; ) { + readentry(rfile, t1); + if (t1->points < POINTSMIN) t1->points = 0; + if(rank0 < 0 && t1->points < t0->points) { + rank0 = rank++; + if(tprev == 0) + tt_head = t0; + else + tprev->tt_next = t0; + t0->tt_next = t1; +#ifdef UPDATE_RECORD_IN_PLACE + t0->fpos = t1->fpos; /* insert here */ +#endif + t0_used = TRUE; + occ_cnt--; + flg++; /* ask for a rewrite */ + } else tprev = t1; + + if(t1->points == 0) break; + if( +#ifdef PERS_IS_UID + t1->uid == t0->uid && +#else + strncmp(t1->name, t0->name, NAMSZ) == 0 && +#endif + !strncmp(t1->plrole, t0->plrole, ROLESZ) && + --occ_cnt <= 0) { + if(rank0 < 0) { + rank0 = 0; + rank1 = rank; + HUP { + char pbuf[BUFSZ]; + Sprintf(pbuf, + "You didn't beat your previous score of %ld points.", + t1->points); + topten_print(pbuf); + topten_print(""); + } + } + if(occ_cnt < 0) { + flg++; + continue; + } + } + if(rank <= ENTRYMAX) { + t1->tt_next = newttentry(); + t1 = t1->tt_next; + rank++; + } + if(rank > ENTRYMAX) { + t1->points = 0; + break; + } + } + if(flg) { /* rewrite record file */ +#ifdef UPDATE_RECORD_IN_PLACE + (void) fseek(rfile, (t0->fpos >= 0 ? + t0->fpos : final_fpos), SEEK_SET); +#else + (void) fclose(rfile); + if(!(rfile = fopen_datafile(RECORD, "w", TRUE))){ + HUP raw_print("Cannot write record file"); + unlock_file(RECORD); + free_ttlist(tt_head); + goto destroywin; + } +#endif /* UPDATE_RECORD_IN_PLACE */ + if(!done_stopprint) if(rank0 > 0){ + if(rank0 <= 10) + topten_print("You made the top ten list!"); + else { + char pbuf[BUFSZ]; + Sprintf(pbuf, + "You reached the %d%s place on the top %d list.", + rank0, ordin(rank0), ENTRYMAX); + topten_print(pbuf); + } + topten_print(""); + } + } + if(rank0 == 0) rank0 = rank1; + if(rank0 <= 0) rank0 = rank; + if(!done_stopprint) outheader(); + t1 = tt_head; + for(rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) { + if(flg +#ifdef UPDATE_RECORD_IN_PLACE + && rank >= rank0 +#endif + ) writeentry(rfile, t1); + if (done_stopprint) continue; + if (rank > flags.end_top && + (rank < rank0 - flags.end_around || + rank > rank0 + flags.end_around) && + (!flags.end_own || +#ifdef PERS_IS_UID + t1->uid != t0->uid +#else + strncmp(t1->name, t0->name, NAMSZ) +#endif + )) continue; + if (rank == rank0 - flags.end_around && + rank0 > flags.end_top + flags.end_around + 1 && + !flags.end_own) + topten_print(""); + if(rank != rank0) + outentry(rank, t1, FALSE); + else if(!rank1) + outentry(rank, t1, TRUE); + else { + outentry(rank, t1, TRUE); + outentry(0, t0, TRUE); + } + } + if(rank0 >= rank) if(!done_stopprint) + outentry(0, t0, TRUE); +#ifdef UPDATE_RECORD_IN_PLACE + if (flg) { +# ifdef TRUNCATE_FILE + /* if a reasonable way to truncate a file exists, use it */ + truncate_file(rfile); +# else + /* use sentinel record rather than relying on truncation */ + t1->points = 0L; /* terminates file when read back in */ + t1->ver_major = t1->ver_minor = t1->patchlevel = 0; + t1->uid = t1->deathdnum = t1->deathlev = 0; + t1->maxlvl = t1->hp = t1->maxhp = t1->deaths = 0; + t1->plrole[0] = t1->plrace[0] = t1->plgend[0] = t1->plalign[0] = '-'; + t1->plrole[1] = t1->plrace[1] = t1->plgend[1] = t1->plalign[1] = 0; + t1->birthdate = t1->deathdate = yyyymmdd((time_t)0L); + Strcpy(t1->name, "@"); + Strcpy(t1->death, "\n"); + writeentry(rfile, t1); + (void) fflush(rfile); +# endif /* TRUNCATE_FILE */ + } +#endif /* UPDATE_RECORD_IN_PLACE */ + (void) fclose(rfile); + unlock_file(RECORD); + free_ttlist(tt_head); + + showwin: + if (flags.toptenwin && !done_stopprint) display_nhwindow(toptenwin, 1); + destroywin: + if (!t0_used) dealloc_ttentry(t0); + if (flags.toptenwin) { + destroy_nhwindow(toptenwin); + toptenwin=WIN_ERR; + } +} + +STATIC_OVL void +outheader() +{ + char linebuf[BUFSZ]; + register char *bp; + + Strcpy(linebuf, " No Points Name"); + bp = eos(linebuf); + while(bp < linebuf + COLNO - 9) *bp++ = ' '; + Strcpy(bp, "Hp [max]"); + topten_print(linebuf); +} + +/* so>0: standout line; so=0: ordinary line */ +STATIC_OVL void +outentry(rank, t1, so) +struct toptenentry *t1; +int rank; +boolean so; +{ + boolean second_line = TRUE; + char linebuf[BUFSZ]; + char *bp, hpbuf[24], linebuf3[BUFSZ]; + int hppos, lngr; + + + linebuf[0] = '\0'; + if (rank) Sprintf(eos(linebuf), "%3d", rank); + else Strcat(linebuf, " "); + + Sprintf(eos(linebuf), " %10ld %.10s", t1->points, t1->name); + Sprintf(eos(linebuf), "-%s", t1->plrole); + if (t1->plrace[0] != '?') + Sprintf(eos(linebuf), "-%s", t1->plrace); + /* Printing of gender and alignment is intentional. It has been + * part of the NetHack Geek Code, and illustrates a proper way to + * specify a character from the command line. + */ + Sprintf(eos(linebuf), "-%s", t1->plgend); + if (t1->plalign[0] != '?') + Sprintf(eos(linebuf), "-%s ", t1->plalign); + else + Strcat(linebuf, " "); + if (!strncmp("escaped", t1->death, 7)) { + Sprintf(eos(linebuf), "escaped the dungeon %s[max level %d]", + !strncmp(" (", t1->death + 7, 2) ? t1->death + 7 + 2 : "", + t1->maxlvl); + /* fixup for closing paren in "escaped... with...Amulet)[max..." */ + if ((bp = index(linebuf, ')')) != 0) + *bp = (t1->deathdnum == astral_level.dnum) ? '\0' : ' '; + second_line = FALSE; + } else if (!strncmp("ascended", t1->death, 8)) { + Sprintf(eos(linebuf), "ascended to demigod%s-hood", + (t1->plgend[0] == 'F') ? "dess" : ""); + second_line = FALSE; + } else { + if (!strncmp(t1->death, "quit", 4)) { + Strcat(linebuf, "quit"); + second_line = FALSE; + } else if (!strncmp(t1->death, "starv", 5)) { + Strcat(linebuf, "starved to death"); + second_line = FALSE; + } else if (!strncmp(t1->death, "choked", 6)) { + Sprintf(eos(linebuf), "choked on h%s food", + (t1->plgend[0] == 'F') ? "er" : "is"); + } else if (!strncmp(t1->death, "poisoned", 8)) { + Strcat(linebuf, "was poisoned"); + } else if (!strncmp(t1->death, "crushed", 7)) { + Strcat(linebuf, "was crushed to death"); + } else if (!strncmp(t1->death, "petrified by ", 13)) { + Strcat(linebuf, "turned to stone"); + } else Strcat(linebuf, "died"); + + if (t1->deathdnum == astral_level.dnum) { + const char *arg, *fmt = " on the Plane of %s"; + + switch (t1->deathlev) { + case -5: + fmt = " on the %s Plane"; + arg = "Astral"; break; + case -4: + arg = "Water"; break; + case -3: + arg = "Fire"; break; + case -2: + arg = "Air"; break; + case -1: + arg = "Earth"; break; + default: + arg = "Void"; break; + } + Sprintf(eos(linebuf), fmt, arg); + } else { + Sprintf(eos(linebuf), " in %s on level %d", + dungeons[t1->deathdnum].dname, t1->deathlev); + if (t1->deathlev != t1->maxlvl) + Sprintf(eos(linebuf), " [max %d]", t1->maxlvl); + } + + /* kludge for "quit while already on Charon's boat" */ + if (!strncmp(t1->death, "quit ", 5)) + Strcat(linebuf, t1->death + 4); + } + Strcat(linebuf, "."); + + /* Quit, starved, ascended, and escaped contain no second line */ + if (second_line) + Sprintf(eos(linebuf), " %c%s.", highc(*(t1->death)), t1->death+1); + + lngr = (int)strlen(linebuf); + if (t1->hp <= 0) hpbuf[0] = '-', hpbuf[1] = '\0'; + else Sprintf(hpbuf, "%d", t1->hp); + /* beginning of hp column after padding (not actually padded yet) */ + hppos = COLNO - (sizeof(" Hp [max]")-1); /* sizeof(str) includes \0 */ + while (lngr >= hppos) { + for(bp = eos(linebuf); + !(*bp == ' ' && (bp-linebuf < hppos)); + bp--) + ; + /* special case: if about to wrap in the middle of maximum + dungeon depth reached, wrap in front of it instead */ + if (bp > linebuf + 5 && !strncmp(bp - 5, " [max", 5)) bp -= 5; + Strcpy(linebuf3, bp+1); + *bp = 0; + if (so) { + while (bp < linebuf + (COLNO-1)) *bp++ = ' '; + *bp = 0; + topten_print_bold(linebuf); + } else + topten_print(linebuf); + Sprintf(linebuf, "%15s %s", "", linebuf3); + lngr = strlen(linebuf); + } + /* beginning of hp column not including padding */ + hppos = COLNO - 7 - (int)strlen(hpbuf); + bp = eos(linebuf); + + if (bp <= linebuf + hppos) { + /* pad any necessary blanks to the hit point entry */ + while (bp < linebuf + hppos) *bp++ = ' '; + Strcpy(bp, hpbuf); + Sprintf(eos(bp), " %s[%d]", + (t1->maxhp < 10) ? " " : (t1->maxhp < 100) ? " " : "", + t1->maxhp); + } + + if (so) { + bp = eos(linebuf); + if (so >= COLNO) so = COLNO-1; + while (bp < linebuf + so) *bp++ = ' '; + *bp = 0; + topten_print_bold(linebuf); + } else + topten_print(linebuf); +} + +STATIC_OVL int +score_wanted(current_ver, rank, t1, playerct, players, uid) +boolean current_ver; +int rank; +struct toptenentry *t1; +int playerct; +const char **players; +int uid; +{ + int i; + + if (current_ver && (t1->ver_major != VERSION_MAJOR || + t1->ver_minor != VERSION_MINOR || + t1->patchlevel != PATCHLEVEL)) + return 0; + +#ifdef PERS_IS_UID + if (!playerct && t1->uid == uid) + return 1; +#endif + + for (i = 0; i < playerct; i++) { + if (players[i][0] == '-' && index("pr", players[i][1]) && + players[i][2] == 0 && i + 1 < playerct) { + char *arg = (char *)players[i + 1]; + if ((players[i][1] == 'p' && + str2role(arg) == str2role(t1->plrole)) || + (players[i][1] == 'r' && + str2race(arg) == str2race(t1->plrace))) + return 1; + i++; + } else if (strcmp(players[i], "all") == 0 || + strncmp(t1->name, players[i], NAMSZ) == 0 || + (players[i][0] == '-' && + players[i][1] == t1->plrole[0] && + players[i][2] == 0) || + (digit(players[i][0]) && rank <= atoi(players[i]))) + return 1; + } + return 0; +} + +/* + * print selected parts of score list. + * argc >= 2, with argv[0] untrustworthy (directory names, et al.), + * and argv[1] starting with "-s". + */ +void +prscore(argc,argv) +int argc; +char **argv; +{ + const char **players; + int playerct, rank; + boolean current_ver = TRUE, init_done = FALSE; + register struct toptenentry *t1; + FILE *rfile; + boolean match_found = FALSE; + register int i; + char pbuf[BUFSZ]; + int uid = -1; +#ifndef PERS_IS_UID + const char *player0; +#endif + + if (argc < 2 || strncmp(argv[1], "-s", 2)) { + raw_printf("prscore: bad arguments (%d)", argc); + return; + } + + rfile = fopen_datafile(RECORD, "r", TRUE); + if (!rfile) { + raw_print("Cannot open record file!"); + return; + } + +#ifdef AMIGA + { + extern winid amii_rawprwin; + init_nhwindows(&argc, argv); + amii_rawprwin = create_nhwindow(NHW_TEXT); + } +#endif + + /* If the score list isn't after a game, we never went through + * initialization. */ + if (wiz1_level.dlevel == 0) { + dlb_init(); + init_dungeons(); + init_done = TRUE; + } + + if (!argv[1][2]){ /* plain "-s" */ + argc--; + argv++; +#if 0 /* uses obsolete pl_classes[] */ + } else if (!argv[1][3] && index(pl_classes, argv[1][2])) { + /* may get this case instead of next accidentally, + * but neither is listed in the documentation, so + * anything useful that happens is a bonus anyway */ + argv[1]++; + argv[1][0] = '-'; +#endif + } else argv[1] += 2; + + if (argc > 1 && !strcmp(argv[1], "-v")) { + current_ver = FALSE; + argc--; + argv++; + } + + if (argc <= 1) { +#ifdef PERS_IS_UID + uid = getuid(); + playerct = 0; + players = (const char **)0; +#else + player0 = plname; + if (!*player0) +# ifdef AMIGA + player0 = "all"; /* single user system */ +# else + player0 = "hackplayer"; +# endif + playerct = 1; + players = &player0; +#endif + } else { + playerct = --argc; + players = (const char **)++argv; + } + raw_print(""); + + t1 = tt_head = newttentry(); + for (rank = 1; ; rank++) { + readentry(rfile, t1); + if (t1->points == 0) break; + if (!match_found && + score_wanted(current_ver, rank, t1, playerct, players, uid)) + match_found = TRUE; + t1->tt_next = newttentry(); + t1 = t1->tt_next; + } + + (void) fclose(rfile); + if (init_done) { + free_dungeons(); + dlb_cleanup(); + } + + if (match_found) { + outheader(); + t1 = tt_head; + for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) { + if (score_wanted(current_ver, rank, t1, playerct, players, uid)) + (void) outentry(rank, t1, 0); + } + } else { + Sprintf(pbuf, "Cannot find any %sentries for ", + current_ver ? "current " : ""); + if (playerct < 1) Strcat(pbuf, "you."); + else { + if (playerct > 1) Strcat(pbuf, "any of "); + for (i = 0; i < playerct; i++) { + Strcat(pbuf, players[i]); + if (i < playerct-1) { + if (players[i][0] == '-' && + index("pr", players[i][1]) && players[i][2] == 0) + Strcat(pbuf, " "); + else Strcat(pbuf, ":"); + } + } + } + raw_print(pbuf); + raw_printf("Usage: %s -s [-v] [maxrank] [playernames]", + + hname); + raw_printf("Player types are: [-p role] [-r race]"); + } + free_ttlist(tt_head); +#ifdef AMIGA + { + extern winid amii_rawprwin; + display_nhwindow(amii_rawprwin, 1); + destroy_nhwindow(amii_rawprwin); + amii_rawprwin = WIN_ERR; + } +#endif +} + +STATIC_OVL int +classmon(plch, fem) + char *plch; + boolean fem; +{ + int i; + + /* Look for this role in the role table */ + for (i = 0; roles[i].name.m; i++) + if (!strncmp(plch, roles[i].filecode, ROLESZ)) { + if (fem && roles[i].femalenum != NON_PM) + return roles[i].femalenum; + else if (roles[i].malenum != NON_PM) + return roles[i].malenum; + else + return PM_HUMAN; + } + /* this might be from a 3.2.x score for former Elf class */ + if (!strcmp(plch, "E")) return PM_RANGER; + + impossible("What weird role is this? (%s)", plch); + return (PM_HUMAN_MUMMY); +} + +/* + * Get a random player name and class from the high score list, + * and attach them to an object (for statues or morgue corpses). + */ +struct obj * +tt_oname(otmp) +struct obj *otmp; +{ + int rank; + register int i; + register struct toptenentry *tt; + FILE *rfile; + struct toptenentry tt_buf; + + if (!otmp) return((struct obj *) 0); + + rfile = fopen_datafile(RECORD, "r", TRUE); + if (!rfile) { + impossible("Cannot open record file!"); + return (struct obj *)0; + } + + tt = &tt_buf; + rank = rnd(10); +pickentry: + for(i = rank; i; i--) { + readentry(rfile, tt); + if(tt->points == 0) break; + } + + if(tt->points == 0) { + if(rank > 1) { + rank = 1; + rewind(rfile); + goto pickentry; + } + otmp = (struct obj *) 0; + } else { + /* reset timer in case corpse started out as lizard or troll */ + if (otmp->otyp == CORPSE) obj_stop_timers(otmp); + otmp->corpsenm = classmon(tt->plrole, (tt->plgend[0] == 'F')); + otmp->owt = weight(otmp); + otmp = oname(otmp, tt->name); + if (otmp->otyp == CORPSE) start_corpse_timeout(otmp); + } + + (void) fclose(rfile); + return otmp; +} + +#ifdef NO_SCAN_BRACK +/* Lattice scanf isn't up to reading the scorefile. What */ +/* follows deals with that; I admit it's ugly. (KL) */ +/* Now generally available (KL) */ +STATIC_OVL void +nsb_mung_line(p) + char *p; +{ + while ((p = index(p, ' ')) != 0) *p = '|'; +} + +STATIC_OVL void +nsb_unmung_line(p) + char *p; +{ + while ((p = index(p, '|')) != 0) *p = ' '; +} +#endif /* NO_SCAN_BRACK */ + +/*topten.c*/