Source:NetHack 3.3.0/topten.c

From NetHackWiki
Jump to navigation Jump to search

Below is the full text to topten.c from the source code of NetHack 3.3.0. To link to a particular line, write [[NetHack 3.3.0/topten.c#line123]], for example.

Warning! This is the source code from an old release. For the latest release, see Source code

The NetHack General Public License applies to screenshots, source code and other content from NetHack.

This content was modified from the original NetHack source code distribution (by splitting up NetHack content between wiki pages, and possibly further editing). See the page history for a list of who changed it, and on what dates.

1.    /*	SCCS Id: @(#)topten.c	3.3	1999/11/01	*/
2.    /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3.    /* NetHack may be freely redistributed.  See license for details. */
4.    
5.    #include "hack.h"
6.    #include "dlb.h"
7.    #ifdef SHORT_FILENAMES
8.    #include "patchlev.h"
9.    #else
10.   #include "patchlevel.h"
11.   #endif
12.   
13.   #ifdef VMS
14.    /* We don't want to rewrite the whole file, because that entails	 */
15.    /* creating a new version which requires that the old one be deletable. */
16.   # define UPDATE_RECORD_IN_PLACE
17.   #endif
18.   
19.   /*
20.    * Updating in place can leave junk at the end of the file in some
21.    * circumstances (if it shrinks and the O.S. doesn't have a straightforward
22.    * way to truncate it).  The trailing junk is harmless and the code
23.    * which reads the scores will ignore it.
24.    */
25.   #ifdef UPDATE_RECORD_IN_PLACE
26.   static long final_fpos;
27.   #endif
28.   
29.   #define done_stopprint program_state.stopprint
30.   
31.   #define newttentry() (struct toptenentry *) alloc(sizeof(struct toptenentry))
32.   #define dealloc_ttentry(ttent) free((genericptr_t) (ttent))
33.   #define NAMSZ	10
34.   #define DTHSZ	60
35.   #define ROLESZ   3
36.   #define PERSMAX	 3		/* entries per name/uid per char. allowed */
37.   #define POINTSMIN	1	/* must be > 0 */
38.   #define ENTRYMAX	100	/* must be >= 10 */
39.   
40.   #if !defined(MICRO) && !defined(MAC)
41.   #define PERS_IS_UID		/* delete for PERSMAX per name; now per uid */
42.   #endif
43.   struct toptenentry {
44.   	struct toptenentry *tt_next;
45.   #ifdef UPDATE_RECORD_IN_PLACE
46.   	long fpos;
47.   #endif
48.   	long points;
49.   	int deathdnum, deathlev;
50.   	int maxlvl, hp, maxhp, deaths;
51.   	int ver_major, ver_minor, patchlevel;
52.   	long deathdate, birthdate;
53.   	int uid;
54.   	char plrole[ROLESZ+1];
55.   	char plrace[ROLESZ+1];
56.   	char plgend[ROLESZ+1];
57.   	char plalign[ROLESZ+1];
58.   	char name[NAMSZ+1];
59.   	char death[DTHSZ+1];
60.   } *tt_head;
61.   
62.   STATIC_DCL void FDECL(topten_print, (const char *));
63.   STATIC_DCL void FDECL(topten_print_bold, (const char *));
64.   STATIC_DCL xchar FDECL(observable_depth, (d_level *));
65.   STATIC_DCL void NDECL(outheader);
66.   STATIC_DCL void FDECL(outentry, (int,struct toptenentry *,BOOLEAN_P));
67.   STATIC_DCL void FDECL(readentry, (FILE *,struct toptenentry *));
68.   STATIC_DCL void FDECL(writeentry, (FILE *,struct toptenentry *));
69.   STATIC_DCL void FDECL(free_ttlist, (struct toptenentry *));
70.   STATIC_DCL int FDECL(classmon, (char *,BOOLEAN_P));
71.   STATIC_DCL int FDECL(score_wanted,
72.   		(BOOLEAN_P, int,struct toptenentry *,int,const char **,int));
73.   #ifdef NO_SCAN_BRACK
74.   STATIC_DCL void FDECL(nsb_mung_line,(char*));
75.   STATIC_DCL void FDECL(nsb_unmung_line,(char*));
76.   #endif
77.   
78.   /* must fit with end.c; used in rip.c */
79.   NEARDATA const char *killed_by_prefix[] = {
80.   	"killed by ", "choked on ", "poisoned by ", "", "drowned in ",
81.   	"", "dissolved in ", "crushed to death by ", "petrified by ",
82.   	"", "", "", "", "", "", ""
83.   };
84.   
85.   static winid toptenwin = WIN_ERR;
86.   
87.   STATIC_OVL void
88.   topten_print(x)
89.   const char *x;
90.   {
91.   	if (toptenwin == WIN_ERR)
92.   	    raw_print(x);
93.   	else
94.   	    putstr(toptenwin, ATR_NONE, x);
95.   }
96.   
97.   STATIC_OVL void
98.   topten_print_bold(x)
99.   const char *x;
100.  {
101.  	if (toptenwin == WIN_ERR)
102.  	    raw_print_bold(x);
103.  	else
104.  	    putstr(toptenwin, ATR_BOLD, x);
105.  }
106.  
107.  STATIC_OVL xchar
108.  observable_depth(lev)
109.  d_level *lev;
110.  {
111.  #if 0	/* if we ever randomize the order of the elemental planes, we
112.  	   must use a constant external representation in the record file */
113.  	if (In_endgame(lev)) {
114.  	    if (Is_astralevel(lev))	 return -5;
115.  	    else if (Is_waterlevel(lev)) return -4;
116.  	    else if (Is_firelevel(lev))	 return -3;
117.  	    else if (Is_airlevel(lev))	 return -2;
118.  	    else if (Is_earthlevel(lev)) return -1;
119.  	    else			 return 0;	/* ? */
120.  	} else
121.  #endif
122.  	    return depth(lev);
123.  }
124.  
125.  STATIC_OVL void
126.  readentry(rfile,tt)
127.  FILE *rfile;
128.  struct toptenentry *tt;
129.  {
130.  #ifdef NO_SCAN_BRACK /* Version_ Pts DgnLevs_ Hp___ Died__Born id */
131.  	static const char *fmt = "%d %d %d %ld %d %d %d %d %d %d %ld %ld %d%*c";
132.  	static const char *fmt32 = "%c%c %s %s%*c";
133.  	static const char *fmt33 = "%s %s %s %s %s %s%*c";
134.  #else
135.  	static const char *fmt = "%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ";
136.  	static const char *fmt32 = "%c%c %[^,],%[^\n]%*c";
137.  	static const char *fmt33 = "%s %s %s %s %[^,],%[^\n]%*c";
138.  #endif
139.  
140.  #ifdef UPDATE_RECORD_IN_PLACE
141.  	/* note: fscanf() below must read the record's terminating newline */
142.  	final_fpos = tt->fpos = ftell(rfile);
143.  #endif
144.  #define TTFIELDS 13
145.  	if(fscanf(rfile, fmt,
146.  			&tt->ver_major, &tt->ver_minor, &tt->patchlevel,
147.  			&tt->points, &tt->deathdnum, &tt->deathlev,
148.  			&tt->maxlvl, &tt->hp, &tt->maxhp, &tt->deaths,
149.  			&tt->deathdate, &tt->birthdate,
150.  			&tt->uid) != TTFIELDS)
151.  #undef TTFIELDS
152.  		tt->points = 0;
153.  	else {
154.  		/* Check for backwards compatibility */
155.  		if (tt->ver_major < 3 ||
156.  				(tt->ver_major == 3 && tt->ver_minor < 3)) {
157.  			int i;
158.  
159.  		    if (fscanf(rfile, fmt32,
160.  		    		tt->plrole, tt->plgend,
161.  		    		tt->name, tt->death) != 4)
162.  		    	tt->points = 0;
163.  		    tt->plrole[1] = 0;
164.  		    if ((i = str2role(tt->plrole)) >= 0)
165.  		    	(void) strncpy(tt->plrole, roles[i].filecode, ROLESZ);
166.  		    tt->plrole[ROLESZ] = 0;
167.  		    Strcpy(tt->plrace, "?");
168.  		    Strcpy(tt->plgend, (tt->plgend[0] == 'M') ? "Mal" : "Fem");
169.  		    Strcpy(tt->plalign, "?");
170.  		} else if (fscanf(rfile, fmt33,
171.  				tt->plrole, tt->plrace, tt->plgend,
172.  				tt->plalign, tt->name, tt->death) != 6)
173.  			tt->points = 0;
174.  #ifdef NO_SCAN_BRACK
175.  		if(tt->points > 0) {
176.  			nsb_unmung_line(tt->name);
177.  			nsb_unmung_line(tt->death);
178.  		}
179.  #endif
180.  	}
181.  
182.  	/* check old score entries for Y2K problem and fix whenever found */
183.  	if (tt->points > 0) {
184.  		if (tt->birthdate < 19000000L) tt->birthdate += 19000000L;
185.  		if (tt->deathdate < 19000000L) tt->deathdate += 19000000L;
186.  	}
187.  }
188.  
189.  STATIC_OVL void
190.  writeentry(rfile,tt)
191.  FILE *rfile;
192.  struct toptenentry *tt;
193.  {
194.  #ifdef NO_SCAN_BRACK
195.  	nsb_mung_line(tt->name);
196.  	nsb_mung_line(tt->death);
197.  	                   /* Version_ Pts DgnLevs_ Hp___ Died__Born id */
198.  	(void) fprintf(rfile,"%d %d %d %ld %d %d %d %d %d %d %ld %ld %d ",
199.  #else
200.  	(void) fprintf(rfile,"%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ",
201.  #endif
202.  		tt->ver_major, tt->ver_minor, tt->patchlevel,
203.  		tt->points, tt->deathdnum, tt->deathlev,
204.  		tt->maxlvl, tt->hp, tt->maxhp, tt->deaths,
205.  		tt->deathdate, tt->birthdate, tt->uid);
206.  	if (tt->ver_major < 3 ||
207.  			(tt->ver_major == 3 && tt->ver_minor < 3))
208.  #ifdef NO_SCAN_BRACK
209.  		(void) fprintf(rfile,"%c%c %s %s\n",
210.  #else
211.  		(void) fprintf(rfile,"%c%c %s,%s\n",
212.  #endif
213.  			tt->plrole[0], tt->plgend[0],
214.  			onlyspace(tt->name) ? "_" : tt->name, tt->death);
215.  	else
216.  #ifdef NO_SCAN_BRACK
217.  		(void) fprintf(rfile,"%s %s %s %s %s %s\n",
218.  #else
219.  		(void) fprintf(rfile,"%s %s %s %s %s,%s\n",
220.  #endif
221.  			tt->plrole, tt->plrace, tt->plgend, tt->plalign,
222.  			onlyspace(tt->name) ? "_" : tt->name, tt->death);
223.  
224.  #ifdef NO_SCAN_BRACK
225.  	nsb_unmung_line(tt->name);
226.  	nsb_unmung_line(tt->death);
227.  #endif
228.  }
229.  
230.  STATIC_OVL void
231.  free_ttlist(tt)
232.  struct toptenentry *tt;
233.  {
234.  	struct toptenentry *ttnext;
235.  
236.  	while (tt->points > 0) {
237.  		ttnext = tt->tt_next;
238.  		dealloc_ttentry(tt);
239.  		tt = ttnext;
240.  	}
241.  	dealloc_ttentry(tt);
242.  }
243.  
244.  void
245.  topten(how)
246.  int how;
247.  {
248.  	int uid = getuid();
249.  	int rank, rank0 = -1, rank1 = 0;
250.  	int occ_cnt = PERSMAX;
251.  	register struct toptenentry *t0, *tprev;
252.  	struct toptenentry *t1;
253.  	FILE *rfile;
254.  	register int flg = 0;
255.  	boolean t0_used;
256.  #ifdef LOGFILE
257.  	FILE *lfile;
258.  #endif /* LOGFILE */
259.  
260.  /* Under DICE 3.0, this crashes the system consistently, apparently due to
261.   * corruption of *rfile somewhere.  Until I figure this out, just cut out
262.   * topten support entirely - at least then the game exits cleanly.  --AC
263.   */
264.  #ifdef _DCC
265.  	return;
266.  #endif
267.  
268.  	if (flags.toptenwin) {
269.  	    toptenwin = create_nhwindow(NHW_TEXT);
270.  	}
271.  
272.  #if defined(UNIX) || defined(VMS) || defined(__EMX__)
273.  #define HUP	if (!program_state.done_hup)
274.  #else
275.  #define HUP
276.  #endif
277.  
278.  #ifdef TOS
279.  	restore_colors();	/* make sure the screen is black on white */
280.  #endif
281.  	/* create a new 'topten' entry */
282.  	t0_used = FALSE;
283.  	t0 = newttentry();
284.  	/* deepest_lev_reached() is in terms of depth(), and reporting the
285.  	 * deepest level reached in the dungeon death occurred in doesn't
286.  	 * seem right, so we have to report the death level in depth() terms
287.  	 * as well (which also seems reasonable since that's all the player
288.  	 * sees on the screen anyway)
289.  	 */
290.  	t0->ver_major = VERSION_MAJOR;
291.  	t0->ver_minor = VERSION_MINOR;
292.  	t0->patchlevel = PATCHLEVEL;
293.  	t0->points = u.urexp;
294.  	t0->deathdnum = u.uz.dnum;
295.  	t0->deathlev = observable_depth(&u.uz);
296.  	t0->maxlvl = deepest_lev_reached(TRUE);
297.  	t0->hp = u.uhp;
298.  	t0->maxhp = u.uhpmax;
299.  	t0->deaths = u.umortality;
300.  	t0->uid = uid;
301.  	(void) strncpy(t0->plrole, urole.filecode, ROLESZ);
302.  	t0->plrole[ROLESZ] = '\0';
303.  	(void) strncpy(t0->plrace, urace.filecode, ROLESZ);
304.  	t0->plrace[ROLESZ] = '\0';
305.  	(void) strncpy(t0->plgend, genders[flags.female].filecode, ROLESZ);
306.  	t0->plgend[ROLESZ] = '\0';
307.  	(void) strncpy(t0->plalign, aligns[1-u.ualign.type].filecode, ROLESZ);
308.  	t0->plalign[ROLESZ] = '\0';
309.  	(void) strncpy(t0->name, plname, NAMSZ);
310.  	t0->name[NAMSZ] = '\0';
311.  	t0->death[0] = '\0';
312.  	switch (killer_format) {
313.  		default: impossible("bad killer format?");
314.  		case KILLED_BY_AN:
315.  			Strcat(t0->death, killed_by_prefix[how]);
316.  			(void) strncat(t0->death, an(killer),
317.  						DTHSZ-strlen(t0->death));
318.  			break;
319.  		case KILLED_BY:
320.  			Strcat(t0->death, killed_by_prefix[how]);
321.  			(void) strncat(t0->death, killer,
322.  						DTHSZ-strlen(t0->death));
323.  			break;
324.  		case NO_KILLER_PREFIX:
325.  			(void) strncat(t0->death, killer, DTHSZ);
326.  			break;
327.  	}
328.  	t0->birthdate = yyyymmdd(u.ubirthday);
329.  	t0->deathdate = yyyymmdd((time_t)0L);
330.  	t0->tt_next = 0;
331.  #ifdef UPDATE_RECORD_IN_PLACE
332.  	t0->fpos = -1L;
333.  #endif
334.  
335.  #ifdef LOGFILE		/* used for debugging (who dies of what, where) */
336.  	if (lock_file(LOGFILE, 10)) {
337.  	    if(!(lfile = fopen_datafile(LOGFILE,"a"))) {
338.  		HUP raw_print("Cannot open log file!");
339.  	    } else {
340.  		writeentry(lfile, t0);
341.  		(void) fclose(lfile);
342.  	    }
343.  	    unlock_file(LOGFILE);
344.  	}
345.  #endif /* LOGFILE */
346.  
347.  	if (wizard || discover) {
348.  	    if (how != PANICKED) HUP {
349.  		char pbuf[BUFSZ];
350.  		topten_print("");
351.  		Sprintf(pbuf,
352.  	      "Since you were in %s mode, the score list will not be checked.",
353.  		    wizard ? "wizard" : "discover");
354.  		topten_print(pbuf);
355.  	    }
356.  	    goto showwin;
357.  	}
358.  
359.  	if (!lock_file(RECORD, 60))
360.  		goto destroywin;
361.  
362.  #ifdef UPDATE_RECORD_IN_PLACE
363.  	rfile = fopen_datafile(RECORD, "r+");
364.  #else
365.  	rfile = fopen_datafile(RECORD, "r");
366.  #endif
367.  
368.  	if (!rfile) {
369.  		HUP raw_print("Cannot open record file!");
370.  		unlock_file(RECORD);
371.  		goto destroywin;
372.  	}
373.  
374.  	HUP topten_print("");
375.  
376.  	/* assure minimum number of points */
377.  	if(t0->points < POINTSMIN) t0->points = 0;
378.  
379.  	t1 = tt_head = newttentry();
380.  	tprev = 0;
381.  	/* rank0: -1 undefined, 0 not_on_list, n n_th on list */
382.  	for(rank = 1; ; ) {
383.  	    readentry(rfile, t1);
384.  	    if (t1->points < POINTSMIN) t1->points = 0;
385.  	    if(rank0 < 0 && t1->points < t0->points) {
386.  		rank0 = rank++;
387.  		if(tprev == 0)
388.  			tt_head = t0;
389.  		else
390.  			tprev->tt_next = t0;
391.  		t0->tt_next = t1;
392.  #ifdef UPDATE_RECORD_IN_PLACE
393.  		t0->fpos = t1->fpos;	/* insert here */
394.  #endif
395.  		t0_used = TRUE;
396.  		occ_cnt--;
397.  		flg++;		/* ask for a rewrite */
398.  	    } else tprev = t1;
399.  
400.  	    if(t1->points == 0) break;
401.  	    if(
402.  #ifdef PERS_IS_UID
403.  		t1->uid == t0->uid &&
404.  #else
405.  		strncmp(t1->name, t0->name, NAMSZ) == 0 &&
406.  #endif
407.  		!strncmp(t1->plrole, t0->plrole, ROLESZ) &&
408.  		--occ_cnt <= 0) {
409.  		    if(rank0 < 0) {
410.  			rank0 = 0;
411.  			rank1 = rank;
412.  			HUP {
413.  			    char pbuf[BUFSZ];
414.  			    Sprintf(pbuf,
415.  			  "You didn't beat your previous score of %ld points.",
416.  				    t1->points);
417.  			    topten_print(pbuf);
418.  			    topten_print("");
419.  			}
420.  		    }
421.  		    if(occ_cnt < 0) {
422.  			flg++;
423.  			continue;
424.  		    }
425.  		}
426.  	    if(rank <= ENTRYMAX) {
427.  		t1->tt_next = newttentry();
428.  		t1 = t1->tt_next;
429.  		rank++;
430.  	    }
431.  	    if(rank > ENTRYMAX) {
432.  		t1->points = 0;
433.  		break;
434.  	    }
435.  	}
436.  	if(flg) {	/* rewrite record file */
437.  #ifdef UPDATE_RECORD_IN_PLACE
438.  		(void) fseek(rfile, (t0->fpos >= 0 ?
439.  				     t0->fpos : final_fpos), SEEK_SET);
440.  #else
441.  		(void) fclose(rfile);
442.  		if(!(rfile = fopen_datafile(RECORD,"w"))){
443.  			HUP raw_print("Cannot write record file");
444.  			unlock_file(RECORD);
445.  			free_ttlist(tt_head);
446.  			goto destroywin;
447.  		}
448.  #endif	/* UPDATE_RECORD_IN_PLACE */
449.  		if(!done_stopprint) if(rank0 > 0){
450.  		    if(rank0 <= 10)
451.  			topten_print("You made the top ten list!");
452.  		    else {
453.  			char pbuf[BUFSZ];
454.  			Sprintf(pbuf,
455.  			  "You reached the %d%s place on the top %d list.",
456.  				rank0, ordin(rank0), ENTRYMAX);
457.  			topten_print(pbuf);
458.  		    }
459.  		    topten_print("");
460.  		}
461.  	}
462.  	if(rank0 == 0) rank0 = rank1;
463.  	if(rank0 <= 0) rank0 = rank;
464.  	if(!done_stopprint) outheader();
465.  	t1 = tt_head;
466.  	for(rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
467.  	    if(flg
468.  #ifdef UPDATE_RECORD_IN_PLACE
469.  		    && rank >= rank0
470.  #endif
471.  		) writeentry(rfile, t1);
472.  	    if (done_stopprint) continue;
473.  	    if (rank > flags.end_top &&
474.  		    (rank < rank0 - flags.end_around ||
475.  		     rank > rank0 + flags.end_around) &&
476.  		    (!flags.end_own ||
477.  #ifdef PERS_IS_UID
478.  					t1->uid != t0->uid
479.  #else
480.  					strncmp(t1->name, t0->name, NAMSZ)
481.  #endif
482.  		)) continue;
483.  	    if (rank == rank0 - flags.end_around &&
484.  		    rank0 > flags.end_top + flags.end_around + 1 &&
485.  		    !flags.end_own)
486.  		topten_print("");
487.  	    if(rank != rank0)
488.  		outentry(rank, t1, FALSE);
489.  	    else if(!rank1)
490.  		outentry(rank, t1, TRUE);
491.  	    else {
492.  		outentry(rank, t1, TRUE);
493.  		outentry(0, t0, TRUE);
494.  	    }
495.  	}
496.  	if(rank0 >= rank) if(!done_stopprint)
497.  		outentry(0, t0, TRUE);
498.  #ifdef UPDATE_RECORD_IN_PLACE
499.  	if (flg) {
500.  # ifdef TRUNCATE_FILE
501.  	    /* if a reasonable way to truncate a file exists, use it */
502.  	    truncate_file(rfile);
503.  # else
504.  	    /* use sentinel record rather than relying on truncation */
505.  	    t1->points = 0L;	/* terminates file when read back in */
506.  	    t1->ver_major = t1->ver_minor = t1->patchlevel = 0;
507.  	    t1->uid = t1->deathdnum = t1->deathlev = 0;
508.  	    t1->maxlvl = t1->hp = t1->maxhp = t1->deaths = 0;
509.  	    t1->plrole[0] = t1->plrace[0] = t1->plgend[0] = t1->plalign[0] = '-';
510.  	    t1->plrole[1] = t1->plrace[1] = t1->plgend[1] = t1->plalign[1] = 0;
511.  	    t1->birthdate = t1->deathdate = yyyymmdd((time_t)0L);
512.  	    Strcpy(t1->name, "@");
513.  	    Strcpy(t1->death, "<eod>\n");
514.  	    writeentry(rfile, t1);
515.  	    (void) fflush(rfile);
516.  # endif	/* TRUNCATE_FILE */
517.  	}
518.  #endif	/* UPDATE_RECORD_IN_PLACE */
519.  	(void) fclose(rfile);
520.  	unlock_file(RECORD);
521.  	free_ttlist(tt_head);
522.  
523.    showwin:
524.  	if (flags.toptenwin && !done_stopprint) display_nhwindow(toptenwin, 1);
525.    destroywin:
526.  	if (!t0_used) dealloc_ttentry(t0);
527.  	if (flags.toptenwin) {
528.  	    destroy_nhwindow(toptenwin);
529.  	    toptenwin=WIN_ERR;
530.  	}
531.  }
532.  
533.  STATIC_OVL void
534.  outheader()
535.  {
536.  	char linebuf[BUFSZ];
537.  	register char *bp;
538.  
539.  	Strcpy(linebuf, " No  Points     Name");
540.  	bp = eos(linebuf);
541.  	while(bp < linebuf + COLNO - 9) *bp++ = ' ';
542.  	Strcpy(bp, "Hp [max]");
543.  	topten_print(linebuf);
544.  }
545.  
546.  /* so>0: standout line; so=0: ordinary line */
547.  STATIC_OVL void
548.  outentry(rank, t1, so)
549.  struct toptenentry *t1;
550.  int rank;
551.  boolean so;
552.  {
553.  	boolean second_line = TRUE;
554.  	char linebuf[BUFSZ];
555.  	char *bp, hpbuf[24], linebuf3[BUFSZ];
556.  	int hppos, lngr;
557.  
558.  
559.  	linebuf[0] = '\0';
560.  	if (rank) Sprintf(eos(linebuf), "%3d", rank);
561.  	else Strcat(linebuf, "   ");
562.  
563.  	Sprintf(eos(linebuf), " %10ld  %.10s", t1->points, t1->name);
564.  	Sprintf(eos(linebuf), "-%s", t1->plrole);
565.  	if (t1->plrace[0] != '?')
566.  		Sprintf(eos(linebuf), "-%s", t1->plrace);
567.  	Sprintf(eos(linebuf), "-%s", t1->plgend);
568.  	if (t1->plalign[0] != '?')
569.  		Sprintf(eos(linebuf), "-%s ", t1->plalign);
570.  	else
571.  		Strcat(linebuf, " ");
572.  	if (!strncmp("escaped", t1->death, 7)) {
573.  	    Sprintf(eos(linebuf), "escaped the dungeon %s[max level %d]",
574.  		    !strncmp(" (", t1->death + 7, 2) ? t1->death + 7 + 2 : "",
575.  		    t1->maxlvl);
576.  	    /* fixup for closing paren in "escaped... with...Amulet)[max..." */
577.  	    if ((bp = index(linebuf, ')')) != 0)
578.  		*bp = (t1->deathdnum == astral_level.dnum) ? '\0' : ' ';
579.  	    second_line = FALSE;
580.  	} else if (!strncmp("ascended", t1->death, 8)) {
581.  	    Sprintf(eos(linebuf), "ascended to demigod%s-hood",
582.  		    (t1->plgend[0] == 'F') ? "dess" : "");
583.  	    second_line = FALSE;
584.  	} else {
585.  	    if (!strncmp(t1->death, "quit", 4)) {
586.  		Strcat(linebuf, "quit");
587.  		second_line = FALSE;
588.  	    } else if (!strncmp(t1->death, "starv", 5)) {
589.  		Strcat(linebuf, "starved to death");
590.  		second_line = FALSE;
591.  	    } else if (!strncmp(t1->death, "choked", 6)) {
592.  		Sprintf(eos(linebuf), "choked on h%s food",
593.  			(t1->plgend[0] == 'F') ? "er" : "is");
594.  	    } else if (!strncmp(t1->death, "poisoned", 8)) {
595.  		Strcat(linebuf, "was poisoned");
596.  	    } else if (!strncmp(t1->death, "crushed", 7)) {
597.  		Strcat(linebuf, "was crushed to death");
598.  	    } else if (!strncmp(t1->death, "petrified by ", 13)) {
599.  		Strcat(linebuf, "turned to stone");
600.  	    } else Strcat(linebuf, "died");
601.  
602.  	    if (t1->deathdnum == astral_level.dnum) {
603.  		const char *arg, *fmt = " on the Plane of %s";
604.  
605.  		switch (t1->deathlev) {
606.  		case -5:
607.  			fmt = " on the %s Plane";
608.  			arg = "Astral";	break;
609.  		case -4:
610.  			arg = "Water";	break;
611.  		case -3:
612.  			arg = "Fire";	break;
613.  		case -2:
614.  			arg = "Air";	break;
615.  		case -1:
616.  			arg = "Earth";	break;
617.  		default:
618.  			arg = "Void";	break;
619.  		}
620.  		Sprintf(eos(linebuf), fmt, arg);
621.  	    } else {
622.  		Sprintf(eos(linebuf), " in %s on level %d",
623.  			dungeons[t1->deathdnum].dname, t1->deathlev);
624.  		if (t1->deathlev != t1->maxlvl)
625.  		    Sprintf(eos(linebuf), " [max %d]", t1->maxlvl);
626.  	    }
627.  
628.  	    /* kludge for "quit while already on Charon's boat" */
629.  	    if (!strncmp(t1->death, "quit ", 5))
630.  		Strcat(linebuf, t1->death + 4);
631.  	}
632.  	Strcat(linebuf, ".");
633.  
634.  	/* Quit, starved, ascended, and escaped contain no second line */
635.  	if (second_line)
636.  	    Sprintf(eos(linebuf), "  %c%s.", highc(*(t1->death)), t1->death+1);
637.  
638.  	lngr = (int)strlen(linebuf);
639.  	if (t1->hp <= 0) hpbuf[0] = '-', hpbuf[1] = '\0';
640.  	else Sprintf(hpbuf, "%d", t1->hp);
641.  	/* beginning of hp column after padding (not actually padded yet) */
642.  	hppos = COLNO - (sizeof("  Hp [max]")-1); /* sizeof(str) includes \0 */
643.  	while (lngr >= hppos) {
644.  	    for(bp = eos(linebuf);
645.  		    !(*bp == ' ' && (bp-linebuf < hppos));
646.  		    bp--)
647.  		;
648.  	    /* special case: if about to wrap in the middle of maximum
649.  	       dungeon depth reached, wrap in front of it instead */
650.  	    if (bp > linebuf + 5 && !strncmp(bp - 5, " [max", 5)) bp -= 5;
651.  	    Strcpy(linebuf3, bp+1);
652.  	    *bp = 0;
653.  	    if (so) {
654.  		while (bp < linebuf + (COLNO-1)) *bp++ = ' ';
655.  		*bp = 0;
656.  		topten_print_bold(linebuf);
657.  	    } else
658.  		topten_print(linebuf);
659.  	    Sprintf(linebuf, "%15s %s", "", linebuf3);
660.  	    lngr = strlen(linebuf);
661.  	}
662.  	/* beginning of hp column not including padding */
663.  	hppos = COLNO - 7 - (int)strlen(hpbuf);
664.  	bp = eos(linebuf);
665.  
666.  	if (bp <= linebuf + hppos) {
667.  	    /* pad any necessary blanks to the hit point entry */
668.  	    while (bp < linebuf + hppos) *bp++ = ' ';
669.  	    Strcpy(bp, hpbuf);
670.  	    Sprintf(eos(bp), " %s[%d]",
671.  		    (t1->maxhp < 10) ? "  " : (t1->maxhp < 100) ? " " : "",
672.  		    t1->maxhp);
673.  	}
674.  
675.  	if (so) {
676.  	    bp = eos(linebuf);
677.  	    if (so >= COLNO) so = COLNO-1;
678.  	    while (bp < linebuf + so) *bp++ = ' ';
679.  	    *bp = 0;
680.  	    topten_print_bold(linebuf);
681.  	} else
682.  	    topten_print(linebuf);
683.  }
684.  
685.  STATIC_OVL int
686.  score_wanted(current_ver, rank, t1, playerct, players, uid)
687.  boolean current_ver;
688.  int rank;
689.  struct toptenentry *t1;
690.  int playerct;
691.  const char **players;
692.  int uid;
693.  {
694.  	int i;
695.  
696.  	if (current_ver && (t1->ver_major != VERSION_MAJOR ||
697.  			    t1->ver_minor != VERSION_MINOR ||
698.  			    t1->patchlevel != PATCHLEVEL))
699.  		return 0;
700.  
701.  #ifdef PERS_IS_UID
702.  	if (!playerct && t1->uid == uid)
703.  		return 1;
704.  #endif
705.  
706.  	for (i = 0; i < playerct; i++) {
707.  		if (strcmp(players[i], "all") == 0 ||
708.  		    strncmp(t1->name, players[i], NAMSZ) == 0 ||
709.  		    (players[i][0] == '-' &&
710.  		     players[i][1] == t1->plrole[0] &&
711.  		     players[i][2] == 0) ||
712.  		    (digit(players[i][0]) && rank <= atoi(players[i])))
713.  		return 1;
714.  	}
715.  	return 0;
716.  }
717.  
718.  /*
719.   * print selected parts of score list.
720.   * argc >= 2, with argv[0] untrustworthy (directory names, et al.),
721.   * and argv[1] starting with "-s".
722.   */
723.  void
724.  prscore(argc,argv)
725.  int argc;
726.  char **argv;
727.  {
728.  	const char **players;
729.  	int playerct, rank;
730.  	boolean current_ver = TRUE, init_done = FALSE;
731.  	register struct toptenentry *t1;
732.  	FILE *rfile;
733.  	boolean match_found = FALSE;
734.  	register int i;
735.  	char pbuf[BUFSZ];
736.  	int uid = -1;
737.  #ifndef PERS_IS_UID
738.  	const char *player0;
739.  #endif
740.  
741.  	if (argc < 2 || strncmp(argv[1], "-s", 2)) {
742.  		raw_printf("prscore: bad arguments (%d)", argc);
743.  		return;
744.  	}
745.  
746.  	rfile = fopen_datafile(RECORD, "r");
747.  	if (!rfile) {
748.  		raw_print("Cannot open record file!");
749.  		return;
750.  	}
751.  
752.  #ifdef	AMIGA
753.  	{
754.  	    extern winid amii_rawprwin;
755.  	    init_nhwindows(&argc, argv);
756.  	    amii_rawprwin = create_nhwindow(NHW_TEXT);
757.  	}
758.  #endif
759.  
760.  	/* If the score list isn't after a game, we never went through
761.  	 * initialization. */
762.  	if (wiz1_level.dlevel == 0) {
763.  		dlb_init();
764.  		init_dungeons();
765.  		init_done = TRUE;
766.  	}
767.  
768.  	if (!argv[1][2]){	/* plain "-s" */
769.  		argc--;
770.  		argv++;
771.  #if 0 /* uses obsolete pl_classes[] */
772.  	} else if (!argv[1][3] && index(pl_classes, argv[1][2])) {
773.  		/* may get this case instead of next accidentally,
774.  		 * but neither is listed in the documentation, so
775.  		 * anything useful that happens is a bonus anyway */
776.  		argv[1]++;
777.  		argv[1][0] = '-';
778.  #endif
779.  	} else	argv[1] += 2;
780.  
781.  	if (argc > 1 && !strcmp(argv[1], "-v")) {
782.  		current_ver = FALSE;
783.  		argc--;
784.  		argv++;
785.  	}
786.  
787.  	if (argc <= 1) {
788.  #ifdef PERS_IS_UID
789.  		uid = getuid();
790.  		playerct = 0;
791.  		players = (const char **)0;
792.  #else
793.  		player0 = plname;
794.  		if (!*player0)
795.  # ifdef AMIGA
796.  			player0 = "all";	/* single user system */
797.  # else
798.  			player0 = "hackplayer";
799.  # endif
800.  		playerct = 1;
801.  		players = &player0;
802.  #endif
803.  	} else {
804.  		playerct = --argc;
805.  		players = (const char **)++argv;
806.  	}
807.  	raw_print("");
808.  
809.  	t1 = tt_head = newttentry();
810.  	for (rank = 1; ; rank++) {
811.  	    readentry(rfile, t1);
812.  	    if (t1->points == 0) break;
813.  	    if (!match_found &&
814.  		    score_wanted(current_ver, rank, t1, playerct, players, uid))
815.  		match_found = TRUE;
816.  	    t1->tt_next = newttentry();
817.  	    t1 = t1->tt_next;
818.  	}
819.  
820.  	(void) fclose(rfile);
821.  	if (init_done) {
822.  	    free_dungeons();
823.  	    dlb_cleanup();
824.  	}
825.  
826.  	if (match_found) {
827.  	    outheader();
828.  	    t1 = tt_head;
829.  	    for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
830.  		if (score_wanted(current_ver, rank, t1, playerct, players, uid))
831.  		    (void) outentry(rank, t1, 0);
832.  	    }
833.  	} else {
834.  	    Sprintf(pbuf, "Cannot find any %sentries for ",
835.  				current_ver ? "current " : "");
836.  	    if (playerct < 1) Strcat(pbuf, "you.");
837.  	    else {
838.  		if (playerct > 1) Strcat(pbuf, "any of ");
839.  		for (i = 0; i < playerct; i++) {
840.  		    Strcat(pbuf, players[i]);
841.  		    if (i < playerct-1) Strcat(pbuf, ":");
842.  		}
843.  	    }
844.  	    raw_print(pbuf);
845.  	    raw_printf("Call is: %s -s [-v] [-role] [maxrank] [playernames]",
846.  			 hname);
847.  	}
848.  	free_ttlist(tt_head);
849.  #ifdef	AMIGA
850.  	display_nhwindow(amii_rawprwin, 1);
851.  	destroy_nhwindow(amii_rawprwin);
852.  	amii_rawprwin = WIN_ERR;
853.  #endif
854.  }
855.  
856.  STATIC_OVL int
857.  classmon(plch, fem)
858.  	char *plch;
859.  	boolean fem;
860.  {
861.  	int i;
862.  
863.  	/* Look for this role in the role table */
864.  	for (i = 0; roles[i].name.m; i++)
865.  	    if (!strncmp(plch, roles[i].filecode, ROLESZ)) {
866.  	    	if (fem && roles[i].femalenum != NON_PM)
867.  	    		return (roles[i].femalenum);
868.  	    	else if (roles[i].malenum != NON_PM)
869.  	    		return (roles[i].malenum);
870.  	    	else
871.  	    		return (PM_HUMAN);
872.  	    }
873.  	impossible("What weird role is this? (%s)", plch);
874.  	return (PM_HUMAN_MUMMY);
875.  }
876.  
877.  /*
878.   * Get a random player name and class from the high score list,
879.   * and attach them to an object (for statues or morgue corpses).
880.   */
881.  struct obj *
882.  tt_oname(otmp)
883.  struct obj *otmp;
884.  {
885.  	int rank;
886.  	register int i;
887.  	register struct toptenentry *tt;
888.  	FILE *rfile;
889.  	struct toptenentry tt_buf;
890.  
891.  	if (!otmp) return((struct obj *) 0);
892.  
893.  	rfile = fopen_datafile(RECORD, "r");
894.  	if (!rfile) {
895.  		impossible("Cannot open record file!");
896.  		return (struct obj *)0;
897.  	}
898.  
899.  	tt = &tt_buf;
900.  	rank = rnd(10);
901.  pickentry:
902.  	for(i = rank; i; i--) {
903.  	    readentry(rfile, tt);
904.  	    if(tt->points == 0) break;
905.  	}
906.  
907.  	if(tt->points == 0) {
908.  		if(rank > 1) {
909.  			rank = 1;
910.  			rewind(rfile);
911.  			goto pickentry;
912.  		}
913.  		otmp = (struct obj *) 0;
914.  	} else {
915.  		/* reset timer in case corpse started out as lizard or troll */
916.  		if (otmp->otyp == CORPSE) obj_stop_timers(otmp);
917.  		otmp->corpsenm = classmon(tt->plrole, (tt->plgend[0] == 'F'));
918.  		otmp->owt = weight(otmp);
919.  		otmp = oname(otmp, tt->name);
920.  		if (otmp->otyp == CORPSE) start_corpse_timeout(otmp);
921.  	}
922.  
923.  	(void) fclose(rfile);
924.  	return otmp;
925.  }
926.  
927.  #ifdef NO_SCAN_BRACK
928.  /* Lattice scanf isn't up to reading the scorefile.  What */
929.  /* follows deals with that; I admit it's ugly. (KL) */
930.  /* Now generally available (KL) */
931.  STATIC_OVL void
932.  nsb_mung_line(p)
933.  	char *p;
934.  {
935.  	while ((p = index(p, ' ')) != 0) *p = '|';
936.  }
937.  
938.  STATIC_OVL void
939.  nsb_unmung_line(p)
940.  	char *p;
941.  {
942.  	while ((p = index(p, '|')) != 0) *p = ' ';
943.  }
944.  #endif /* NO_SCAN_BRACK */
945.  
946.  /*topten.c*/