Source:SLASH'EM 0.0.7E7F2/topten.c

From NetHackWiki
Jump to navigation Jump to search

Below is the full text to topten.c from the source code of SLASH'EM 0.0.7E7F2. To link to a particular line, write [[SLASH'EM 0.0.7E7F2/topten.c#line123]], for example.

The latest source code for vanilla NetHack is at 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.4	2000/01/21	*/
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	100
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) && !defined(WIN32)
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.   #ifdef RECORD_CONDUCT
54.   	long conduct;
55.   #endif
56.   	int uid;
57.   	char plrole[ROLESZ+1];
58.   	char plrace[ROLESZ+1];
59.   	char plgend[ROLESZ+1];
60.   	char plalign[ROLESZ+1];
61.   	char name[NAMSZ+1];
62.   	char death[DTHSZ+1];
63.   } *tt_head;
64.   
65.   STATIC_DCL void FDECL(topten_print, (const char *));
66.   STATIC_DCL void FDECL(topten_print_bold, (const char *));
67.   STATIC_DCL xchar FDECL(observable_depth, (d_level *));
68.   STATIC_DCL void NDECL(outheader);
69.   STATIC_DCL void FDECL(outentry, (int,struct toptenentry *,BOOLEAN_P));
70.   STATIC_DCL void FDECL(readentry, (FILE *,struct toptenentry *));
71.   STATIC_DCL void FDECL(writeentry, (FILE *,struct toptenentry *));
72.   STATIC_DCL void FDECL(free_ttlist, (struct toptenentry *));
73.   STATIC_DCL int FDECL(classmon, (char *,BOOLEAN_P));
74.   STATIC_DCL int FDECL(score_wanted,
75.   		(BOOLEAN_P, int,struct toptenentry *,int,const char **,int));
76.   #ifdef RECORD_CONDUCT
77.   STATIC_DCL long FDECL(encodeconduct, (void));
78.   #endif
79.   #ifdef NO_SCAN_BRACK
80.   STATIC_DCL void FDECL(nsb_mung_line,(char*));
81.   STATIC_DCL void FDECL(nsb_unmung_line,(char*));
82.   #endif
83.   
84.   /* must fit with end.c; used in rip.c */
85.   NEARDATA const char * const killed_by_prefix[] = {
86.   	"killed by ", "betrayed by ", "choked on ", "poisoned by ", "died of ", 
87.   	"drowned in ", "burned by ", "dissolved in ", "crushed to death by ", 
88.   	"petrified by ", "turned to slime by ", "killed by ", 
89.   	"", "", "", "", ""
90.   };
91.   
92.   static winid toptenwin = WIN_ERR;
93.   
94.   STATIC_OVL void
95.   topten_print(x)
96.   const char *x;
97.   {
98.   	if (toptenwin == WIN_ERR)
99.   	    raw_print(x);
100.  	else
101.  	    putstr(toptenwin, ATR_NONE, x);
102.  }
103.  
104.  STATIC_OVL void
105.  topten_print_bold(x)
106.  const char *x;
107.  {
108.  	if (toptenwin == WIN_ERR)
109.  	    raw_print_bold(x);
110.  	else
111.  	    putstr(toptenwin, ATR_BOLD, x);
112.  }
113.  
114.  STATIC_OVL xchar
115.  observable_depth(lev)
116.  d_level *lev;
117.  {
118.  #if 0	/* if we ever randomize the order of the elemental planes, we
119.  	   must use a constant external representation in the record file */
120.  	if (In_endgame(lev)) {
121.  	    if (Is_astralevel(lev))	 return -5;
122.  	    else if (Is_waterlevel(lev)) return -4;
123.  	    else if (Is_firelevel(lev))	 return -3;
124.  	    else if (Is_airlevel(lev))	 return -2;
125.  	    else if (Is_earthlevel(lev)) return -1;
126.  	    else			 return 0;	/* ? */
127.  	} else
128.  #endif
129.  	    return depth(lev);
130.  }
131.  
132.  #ifdef RECORD_CONDUCT
133.  long
134.  encodeconduct(void)
135.  {
136.         long e = 0L;
137.  
138.         if(u.uconduct.unvegetarian)    e |= 0x1L;
139.         if(u.uconduct.unvegan)         e |= 0x2L;
140.         if(u.uconduct.food)            e |= 0x4L;
141.         if(u.uconduct.gnostic)         e |= 0x8L;
142.         if(u.uconduct.weaphit)         e |= 0x10L;
143.         if(u.uconduct.killer)          e |= 0x20L;
144.         if(u.uconduct.literate)        e |= 0x40L;
145.         if(u.uconduct.polypiles)       e |= 0x80L;
146.         if(u.uconduct.polyselfs)       e |= 0x100L;
147.         if(u.uconduct.wishes)          e |= 0x200L;
148.         if(u.uconduct.wisharti)        e |= 0x400L;
149.         if(num_genocides())            e |= 0x800L;
150.  
151.         return e;
152.  }
153.  #endif
154.  
155.  STATIC_OVL void
156.  readentry(rfile,tt)
157.  FILE *rfile;
158.  struct toptenentry *tt;
159.  {
160.  #ifdef NO_SCAN_BRACK		/* Version_ Pts DgnLevs_ Hp___ Died__Born id */
161.  	static const char fmt[] = "%d %d %d %ld %d %d %d %d %d %d %ld %ld %d%*c";
162.  	static const char fmt005[] = "%s %c %s %s%*c";
163.  	static const char fmt33[] = "%s %s %s %s %s %s%*c";
164.  #else
165.  	static const char fmt[] = "%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ";
166.  	static const char fmt005[] = "%s %c %[^,],%[^\n]%*c";
167.  	static const char fmt33[] = "%s %s %s %s %[^,],%[^\n]%*c";
168.  #endif
169.  
170.  #ifdef UPDATE_RECORD_IN_PLACE
171.  	/* note: fscanf() below must read the record's terminating newline */
172.  	final_fpos = tt->fpos = ftell(rfile);
173.  #endif
174.  #define TTFIELDS 13
175.  
176.  #ifdef RECORD_CONDUCT
177.  	tt->conduct = 4095;
178.  #endif
179.  
180.  	if(fscanf(rfile, fmt,
181.  			&tt->ver_major, &tt->ver_minor, &tt->patchlevel,
182.  			&tt->points, &tt->deathdnum, &tt->deathlev,
183.  			&tt->maxlvl, &tt->hp, &tt->maxhp, &tt->deaths,
184.  			&tt->deathdate, &tt->birthdate,
185.  			&tt->uid) != TTFIELDS)
186.  #undef TTFIELDS
187.  		tt->points = 0;
188.  	else {
189.  		/* Check for backwards compatibility */
190.  		if (!tt->ver_major && !tt->ver_minor && tt->patchlevel < 6) {
191.  			int i;
192.  
193.  		    if (fscanf(rfile, fmt005,
194.  				tt->plrole, tt->plgend,
195.  				tt->name, tt->death) != 4)
196.  			tt->points = 0;
197.  		    tt->plrole[1] = '\0';
198.  		    if ((i = str2role(tt->plrole)) >= 0)
199.  			Strcpy(tt->plrole, roles[i].filecode);
200.  		    tt->plrole[ROLESZ] = 0;
201.  		    Strcpy(tt->plrace, "?");
202.  		    Strcpy(tt->plgend, (tt->plgend[0] == 'M') ? "Mal" : "Fem");
203.  		    Strcpy(tt->plalign, "?");
204.  		} else if (fscanf(rfile, fmt33,
205.  				tt->plrole, tt->plrace, tt->plgend,
206.  				tt->plalign, tt->name, tt->death) != 6)
207.  			tt->points = 0;
208.  #ifdef NO_SCAN_BRACK
209.  		if(tt->points > 0) {
210.  			nsb_unmung_line(tt->name);
211.  			nsb_unmung_line(tt->death);
212.  		}
213.  #endif
214.  
215.  #ifdef RECORD_CONDUCT
216.  		if(tt->points > 0) {
217.  			/* If the string "Conduct=%d" appears, set tt->conduct and remove that
218.  			 * portion of the string */
219.  			char *dp, *dp2;
220.  			for(dp = tt->death; *dp; dp++) {
221.  				if(!strncmp(dp, " Conduct=", 9)) {
222.  					dp2 = dp + 9;
223.  					sscanf(dp2, "%d", &tt->conduct);
224.  					/* Find trailing null or space */
225.  					while(*dp2 && *dp2 != ' ')
226.  						dp2++;
227.  
228.  					/* Cut out the " Conduct=" portion of the death string */
229.  					while(*dp2) {
230.  						*dp = *dp2;
231.  						dp2++;
232.  						dp++;
233.  					}
234.  					
235.  					*dp = *dp2;
236.  				}
237.  			}
238.  
239.  			/* Sanity check */
240.  			if(tt->conduct < 0 || tt->conduct > 4095)
241.  				tt->conduct = 4095;
242.  		}
243.  #endif
244.  
245.  	}
246.  
247.  	/* check old score entries for Y2K problem and fix whenever found */
248.  	if (tt->points > 0) {
249.  		if (tt->birthdate < 19000000L) tt->birthdate += 19000000L;
250.  		if (tt->deathdate < 19000000L) tt->deathdate += 19000000L;
251.  	}
252.  }
253.  
254.  STATIC_OVL void
255.  writeentry(rfile,tt)
256.  FILE *rfile;
257.  struct toptenentry *tt;
258.  {
259.  	char *cp = NULL;
260.  
261.  #ifdef RECORD_CONDUCT
262.  	/* Add a trailing " Conduct=%d" to tt->death */
263.  	if(tt->conduct != 4095) {
264.  		cp = tt->death + strlen(tt->death);
265.  		Sprintf(cp, " Conduct=%d", tt->conduct);
266.  	}
267.  #endif
268.  
269.  #ifdef NO_SCAN_BRACK
270.  	nsb_mung_line(tt->name);
271.  	nsb_mung_line(tt->death);
272.  	                   /* Version_ Pts DgnLevs_ Hp___ Died__Born id */
273.  	(void) fprintf(rfile,"%d %d %d %ld %d %d %d %d %d %d %ld %ld %d ",
274.  #else
275.  	(void) fprintf(rfile,"%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ",
276.  #endif
277.  		tt->ver_major, tt->ver_minor, tt->patchlevel,
278.  		tt->points, tt->deathdnum, tt->deathlev,
279.  		tt->maxlvl, tt->hp, tt->maxhp, tt->deaths,
280.  		tt->deathdate, tt->birthdate, tt->uid);
281.  	if (!tt->ver_major && !tt->ver_minor && tt->patchlevel < 6)
282.  #ifdef NO_SCAN_BRACK
283.  		(void) fprintf(rfile,"%s %c %s %s\n",
284.  #else
285.  		(void) fprintf(rfile,"%s %c %s,%s\n",
286.  #endif
287.  			tt->plrole, tt->plgend[0],
288.  			onlyspace(tt->name) ? "_" : tt->name, tt->death);
289.  	else
290.  #ifdef NO_SCAN_BRACK
291.  		(void) fprintf(rfile,"%s %s %s %s %s %s\n",
292.  #else
293.  		(void) fprintf(rfile,"%s %s %s %s %s,%s\n",
294.  #endif
295.  			tt->plrole, tt->plrace, tt->plgend, tt->plalign,
296.  			onlyspace(tt->name) ? "_" : tt->name, tt->death);
297.  
298.  #ifdef NO_SCAN_BRACK
299.  	nsb_unmung_line(tt->name);
300.  	nsb_unmung_line(tt->death);
301.  #endif
302.  
303.  #ifdef RECORD_CONDUCT
304.  	/* Return the tt->death line to the original form */
305.  	if(cp)
306.  		*cp = '\0';
307.  #endif
308.  }
309.  
310.  STATIC_OVL void
311.  free_ttlist(tt)
312.  struct toptenentry *tt;
313.  {
314.  	struct toptenentry *ttnext;
315.  
316.  	while (tt->points > 0) {
317.  		ttnext = tt->tt_next;
318.  		dealloc_ttentry(tt);
319.  		tt = ttnext;
320.  	}
321.  	dealloc_ttentry(tt);
322.  }
323.  
324.  void
325.  topten(how)
326.  int how;
327.  {
328.  	int uid = getuid();
329.  	int rank, rank0 = -1, rank1 = 0;
330.  	int occ_cnt = PERSMAX;
331.  	register struct toptenentry *t0, *tprev;
332.  	struct toptenentry *t1;
333.  	FILE *rfile;
334.  	register int flg = 0;
335.  	boolean t0_used;
336.  #ifdef LOGFILE
337.  	FILE *lfile;
338.  #endif /* LOGFILE */
339.  
340.  /* Under DICE 3.0, this crashes the system consistently, apparently due to
341.   * corruption of *rfile somewhere.  Until I figure this out, just cut out
342.   * topten support entirely - at least then the game exits cleanly.  --AC
343.   */
344.  #ifdef _DCC
345.  	return;
346.  #endif
347.  
348.  /* If we are in the midst of a panic, cut out topten entirely.
349.   * topten uses alloc() several times, which will lead to
350.   * problems if the panic was the result of an alloc() failure.
351.   */
352.  	if (program_state.panicking)
353.  		return;
354.  
355.  	if (flags.toptenwin) {
356.  	    toptenwin = create_nhwindow(NHW_TEXT);
357.  	}
358.  
359.  #if defined(UNIX) || defined(VMS) || defined(__EMX__)
360.  #define HUP	if (!program_state.done_hup)
361.  #else
362.  #define HUP
363.  #endif
364.  
365.  #ifdef TOS
366.  	restore_colors();	/* make sure the screen is black on white */
367.  #endif
368.  	/* create a new 'topten' entry */
369.  	t0_used = FALSE;
370.  	t0 = newttentry();
371.  	/* deepest_lev_reached() is in terms of depth(), and reporting the
372.  	 * deepest level reached in the dungeon death occurred in doesn't
373.  	 * seem right, so we have to report the death level in depth() terms
374.  	 * as well (which also seems reasonable since that's all the player
375.  	 * sees on the screen anyway)
376.  	 */
377.  	t0->ver_major = VERSION_MAJOR;
378.  	t0->ver_minor = VERSION_MINOR;
379.  	t0->patchlevel = PATCHLEVEL;
380.  	t0->points = u.urexp;
381.  	t0->deathdnum = u.uz.dnum;
382.  	t0->deathlev = observable_depth(&u.uz);
383.  	t0->maxlvl = deepest_lev_reached(TRUE);
384.  	t0->hp = u.uhp;
385.  	t0->maxhp = u.uhpmax;
386.  	t0->deaths = u.umortality;
387.  	t0->uid = uid;
388.  	(void) strncpy(t0->plrole, urole.filecode, ROLESZ);
389.  	t0->plrole[ROLESZ] = '\0';
390.  	(void) strncpy(t0->plrace, urace.filecode, ROLESZ);
391.  	t0->plrace[ROLESZ] = '\0';
392.  	(void) strncpy(t0->plgend, genders[flags.female].filecode, ROLESZ);
393.  	t0->plgend[ROLESZ] = '\0';
394.  	(void) strncpy(t0->plalign, aligns[1-u.ualign.type].filecode, ROLESZ);
395.  	t0->plalign[ROLESZ] = '\0';
396.  	(void) strncpy(t0->name, plname, NAMSZ);
397.  	t0->name[NAMSZ] = '\0';
398.  	t0->death[0] = '\0';
399.  	switch (killer_format) {
400.  		default: impossible("bad killer format?");
401.  		case KILLED_BY_AN:
402.  			Strcat(t0->death, killed_by_prefix[how]);
403.  			(void) strncat(t0->death, an(killer),
404.  						DTHSZ-strlen(t0->death));
405.  			break;
406.  		case KILLED_BY:
407.  			Strcat(t0->death, killed_by_prefix[how]);
408.  			(void) strncat(t0->death, killer,
409.  						DTHSZ-strlen(t0->death));
410.  			break;
411.  		case NO_KILLER_PREFIX:
412.  			(void) strncat(t0->death, killer, DTHSZ);
413.  			break;
414.  	}
415.  	t0->birthdate = yyyymmdd(u.ubirthday);
416.  	t0->deathdate = yyyymmdd((time_t)0L);
417.  #ifdef RECORD_CONDUCT
418.  	t0->conduct = encodeconduct();
419.  #endif
420.  	t0->tt_next = 0;
421.  #ifdef UPDATE_RECORD_IN_PLACE
422.  	t0->fpos = -1L;
423.  #endif
424.  
425.  #ifdef LOGFILE		/* used for debugging (who dies of what, where) */
426.  #ifdef FILE_AREAS
427.  	if (lock_file_area(LOGAREA, LOGFILE, 10)) {
428.  #else
429.  	if (lock_file(LOGFILE, SCOREPREFIX, 10)) {
430.  #endif
431.  	    if(!(lfile = fopen_datafile_area(LOGAREA, LOGFILE, "a", SCOREPREFIX))) {
432.  		HUP raw_print("Cannot open log file!");
433.  	    } else {
434.  		writeentry(lfile, t0);
435.  		(void) fclose(lfile);
436.  	    }
437.  	    unlock_file_area(LOGAREA, LOGFILE);
438.  	}
439.  #endif /* LOGFILE */
440.  
441.  	if (wizard || discover) {
442.  	    if (how != PANICKED) HUP {
443.  		char pbuf[BUFSZ];
444.  		topten_print("");
445.  		Sprintf(pbuf,
446.  	      "Since you were in %s mode, the score list will not be checked.",
447.  		    wizard ? "wizard" : "discover");
448.  		topten_print(pbuf);
449.  	    }
450.  	    goto showwin;
451.  	}
452.  
453.  #ifdef FILE_AREAS
454.  	if (!lock_file_area(NH_RECORD_AREA, NH_RECORD, 60))
455.  #else
456.  	if (!lock_file(NH_RECORD, SCOREPREFIX, 60))
457.  #endif
458.  		goto destroywin;
459.  
460.  #ifdef UPDATE_RECORD_IN_PLACE
461.  	rfile = fopen_datafile_area(NH_RECORD_AREA, NH_RECORD, "r+", SCOREPREFIX);
462.  #else
463.  	rfile = fopen_datafile_area(NH_RECORD_AREA, NH_RECORD, "r", SCOREPREFIX);
464.  #endif
465.  
466.  	if (!rfile) {
467.  		HUP raw_print("Cannot open record file!");
468.  		unlock_file_area(NH_RECORD_AREA, NH_RECORD);
469.  		goto destroywin;
470.  	}
471.  
472.  	HUP topten_print("");
473.  
474.  	/* assure minimum number of points */
475.  	if(t0->points < POINTSMIN) t0->points = 0;
476.  
477.  	t1 = tt_head = newttentry();
478.  	tprev = 0;
479.  	/* rank0: -1 undefined, 0 not_on_list, n n_th on list */
480.  	for(rank = 1; ; ) {
481.  	    readentry(rfile, t1);
482.  	    if (t1->points < POINTSMIN) t1->points = 0;
483.  	    if(rank0 < 0 && t1->points < t0->points) {
484.  		rank0 = rank++;
485.  		if(tprev == 0)
486.  			tt_head = t0;
487.  		else
488.  			tprev->tt_next = t0;
489.  		t0->tt_next = t1;
490.  #ifdef UPDATE_RECORD_IN_PLACE
491.  		t0->fpos = t1->fpos;	/* insert here */
492.  #endif
493.  		t0_used = TRUE;
494.  		occ_cnt--;
495.  		flg++;		/* ask for a rewrite */
496.  	    } else tprev = t1;
497.  
498.  	    if(t1->points == 0) break;
499.  	    if(
500.  #ifdef PERS_IS_UID
501.  		t1->uid == t0->uid &&
502.  #else
503.  		strncmp(t1->name, t0->name, NAMSZ) == 0 &&
504.  #endif
505.  		!strncmp(t1->plrole, t0->plrole, ROLESZ) &&
506.  		--occ_cnt <= 0) {
507.  		    if(rank0 < 0) {
508.  			rank0 = 0;
509.  			rank1 = rank;
510.  			HUP {
511.  			    char pbuf[BUFSZ];
512.  			    Sprintf(pbuf,
513.  			  "You didn't beat your previous score of %ld points.",
514.  				    t1->points);
515.  			    topten_print(pbuf);
516.  			    topten_print("");
517.  			}
518.  		    }
519.  		    if(occ_cnt < 0) {
520.  			flg++;
521.  			continue;
522.  		    }
523.  		}
524.  	    if(rank <= ENTRYMAX) {
525.  		t1->tt_next = newttentry();
526.  		t1 = t1->tt_next;
527.  		rank++;
528.  	    }
529.  	    if(rank > ENTRYMAX) {
530.  		t1->points = 0;
531.  		break;
532.  	    }
533.  	}
534.  	if(flg) {	/* rewrite record file */
535.  #ifdef UPDATE_RECORD_IN_PLACE
536.  		(void) fseek(rfile, (t0->fpos >= 0 ?
537.  				     t0->fpos : final_fpos), SEEK_SET);
538.  #else
539.  		(void) fclose(rfile);
540.  		if(!(rfile = fopen_datafile_area(NH_RECORD_AREA, NH_RECORD, "w", SCOREPREFIX))){
541.  			HUP raw_print("Cannot write record file");
542.  			unlock_file_area(NH_RECORD_AREA, NH_RECORD);
543.  			free_ttlist(tt_head);
544.  			goto destroywin;
545.  		}
546.  #endif	/* UPDATE_RECORD_IN_PLACE */
547.  		if(!done_stopprint) if(rank0 > 0){
548.  		    if(rank0 <= 10)
549.  			topten_print("You made the top ten list!");
550.  		    else {
551.  			char pbuf[BUFSZ];
552.  			Sprintf(pbuf,
553.  			  "You reached the %d%s place on the top %d list.",
554.  				rank0, ordin(rank0), ENTRYMAX);
555.  			topten_print(pbuf);
556.  		    }
557.  		    topten_print("");
558.  		}
559.  	}
560.  	if(rank0 == 0) rank0 = rank1;
561.  	if(rank0 <= 0) rank0 = rank;
562.  	if(!done_stopprint) outheader();
563.  	t1 = tt_head;
564.  	for(rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
565.  	    if(flg
566.  #ifdef UPDATE_RECORD_IN_PLACE
567.  		    && rank >= rank0
568.  #endif
569.  		) writeentry(rfile, t1);
570.  	    if (done_stopprint) continue;
571.  	    if (rank > flags.end_top &&
572.  		    (rank < rank0 - flags.end_around ||
573.  		     rank > rank0 + flags.end_around) &&
574.  		    (!flags.end_own ||
575.  #ifdef PERS_IS_UID
576.  					t1->uid != t0->uid
577.  #else
578.  					strncmp(t1->name, t0->name, NAMSZ)
579.  #endif
580.  		)) continue;
581.  	    if (rank == rank0 - flags.end_around &&
582.  		    rank0 > flags.end_top + flags.end_around + 1 &&
583.  		    !flags.end_own)
584.  		topten_print("");
585.  	    if(rank != rank0)
586.  		outentry(rank, t1, FALSE);
587.  	    else if(!rank1)
588.  		outentry(rank, t1, TRUE);
589.  	    else {
590.  		outentry(rank, t1, TRUE);
591.  		outentry(0, t0, TRUE);
592.  	    }
593.  	}
594.  	if(rank0 >= rank) if(!done_stopprint)
595.  		outentry(0, t0, TRUE);
596.  #ifdef UPDATE_RECORD_IN_PLACE
597.  	if (flg) {
598.  # ifdef TRUNCATE_FILE
599.  	    /* if a reasonable way to truncate a file exists, use it */
600.  	    truncate_file(rfile);
601.  # else
602.  	    /* use sentinel record rather than relying on truncation */
603.  	    t1->points = 0L;	/* terminates file when read back in */
604.  	    t1->ver_major = t1->ver_minor = t1->patchlevel = 0;
605.  	    t1->uid = t1->deathdnum = t1->deathlev = 0;
606.  	    t1->maxlvl = t1->hp = t1->maxhp = t1->deaths = 0;
607.  	    t1->plrole[0] = t1->plrace[0] = t1->plgend[0] = t1->plalign[0] = '-';
608.  	    t1->plrole[1] = t1->plrace[1] = t1->plgend[1] = t1->plalign[1] = 0;
609.  	    t1->birthdate = t1->deathdate = yyyymmdd((time_t)0L);
610.  	    Strcpy(t1->name, "@");
611.  	    Strcpy(t1->death, "<eod>\n");
612.  	    writeentry(rfile, t1);
613.  	    (void) fflush(rfile);
614.  # endif	/* TRUNCATE_FILE */
615.  	}
616.  #endif	/* UPDATE_RECORD_IN_PLACE */
617.  	(void) fclose(rfile);
618.  	unlock_file_area(NH_RECORD_AREA, NH_RECORD);
619.  	free_ttlist(tt_head);
620.  
621.    showwin:
622.  	if (flags.toptenwin && !done_stopprint) display_nhwindow(toptenwin, 1);
623.    destroywin:
624.  	if (!t0_used) dealloc_ttentry(t0);
625.  	if (flags.toptenwin) {
626.  	    destroy_nhwindow(toptenwin);
627.  	    toptenwin=WIN_ERR;
628.  	}
629.  }
630.  
631.  STATIC_OVL void
632.  outheader()
633.  {
634.  	char linebuf[BUFSZ];
635.  	register char *bp;
636.  
637.  	Strcpy(linebuf, " No  Points     Name");
638.  	bp = eos(linebuf);
639.  	while(bp < linebuf + COLNO - 9) *bp++ = ' ';
640.  	Strcpy(bp, "Hp [max]");
641.  	topten_print(linebuf);
642.  }
643.  
644.  /* so>0: standout line; so=0: ordinary line */
645.  STATIC_OVL void
646.  outentry(rank, t1, so)
647.  struct toptenentry *t1;
648.  int rank;
649.  boolean so;
650.  {
651.  	boolean second_line = TRUE;
652.  	char linebuf[BUFSZ];
653.  	char *bp, hpbuf[24], linebuf3[BUFSZ];
654.  	int hppos, lngr;
655.  
656.  
657.  	linebuf[0] = '\0';
658.  	if (rank) Sprintf(eos(linebuf), "%3d", rank);
659.  	else Strcat(linebuf, "   ");
660.  
661.  	Sprintf(eos(linebuf), " %10ld  %.10s", t1->points, t1->name);
662.  	Sprintf(eos(linebuf), "-%s", t1->plrole);
663.  	if (t1->plrace[0] != '?')
664.  		Sprintf(eos(linebuf), "-%s", t1->plrace);
665.  	/* Printing of gender and alignment is intentional.  It has been
666.  	 * part of the NetHack Geek Code, and illustrates a proper way to
667.  	 * specify a character from the command line.
668.  	 */
669.  	Sprintf(eos(linebuf), "-%s", t1->plgend);
670.  	if (t1->plalign[0] != '?')
671.  		Sprintf(eos(linebuf), "-%s ", t1->plalign);
672.  	else
673.  		Strcat(linebuf, " ");
674.  	if (!strncmp("escaped", t1->death, 7)) {
675.  	    Sprintf(eos(linebuf), "escaped the dungeon %s[max level %d]",
676.  		    !strncmp(" (", t1->death + 7, 2) ? t1->death + 7 + 2 : "",
677.  		    t1->maxlvl);
678.  	    /* fixup for closing paren in "escaped... with...Amulet)[max..." */
679.  	    if ((bp = index(linebuf, ')')) != 0)
680.  		*bp = (t1->deathdnum == astral_level.dnum) ? '\0' : ' ';
681.  	    second_line = FALSE;
682.  	} else if (!strncmp("ascended", t1->death, 8)) {
683.  
684.  #ifdef RECORD_CONDUCT
685.  		/* Add a notation for conducts kept */
686.  		if(t1->conduct != 4095) {
687.  			int i, m;
688.  			char dash = 0, skip;
689.  			const char *conduct_names[] = {
690.  				"Food", "Vgn", "Vgt", "Ath", "Weap", "Pac",
691.  				"Ill", "Poly", "Form", "Wish", "Art", "Geno",
692.  				NULL };
693.  		
694.  			Strcat(eos(linebuf), "(");
695.  			for(i = 0, m = 1; conduct_names[i]; i += skip + 1, m <<= (skip + 1)) {
696.  				skip = 0;
697.  				if(t1->conduct & m)
698.  					continue;
699.  		
700.  				/* Only show one of foodless, vegan, vegetarian */
701.  				if(i == 0) skip = 2;
702.  				if(i == 1) skip = 1;
703.  
704.  				/* Only show one of wishless, artiwishless */
705.  				if(i == 9) skip = 1;
706.  
707.  				/* Add a hyphen for multiple conducts */
708.  				if(dash) Strcat(eos(linebuf), "-");
709.  				Strcat(eos(linebuf), conduct_names[i]);
710.  				dash = 1;
711.  			}
712.  			Strcat(eos(linebuf), ") ");
713.  		}
714.  #endif
715.  
716.  	    Sprintf(eos(linebuf), "ascended to demigod%s-hood",
717.  		    (t1->plgend[0] == 'F') ? "dess" : "");
718.  	    second_line = FALSE;
719.  	} else {
720.  	    if (!strncmp(t1->death, "quit", 4)) {
721.  		Strcat(linebuf, "quit");
722.  		second_line = FALSE;
723.  	    } else if (!strncmp(t1->death, "died of st", 10)) {
724.  		Strcat(linebuf, "starved to death");
725.  		second_line = FALSE;
726.  	    } else if (!strncmp(t1->death, "choked", 6)) {
727.  		Sprintf(eos(linebuf), "choked on h%s food",
728.  			(t1->plgend[0] == 'F') ? "er" : "is");
729.  	    } else if (!strncmp(t1->death, "poisoned", 8)) {
730.  		Strcat(linebuf, "was poisoned");
731.  	    } else if (!strncmp(t1->death, "crushed", 7)) {
732.  		Strcat(linebuf, "was crushed to death");
733.  	    } else if (!strncmp(t1->death, "petrified by ", 13)) {
734.  		Strcat(linebuf, "turned to stone");
735.  	    } else Strcat(linebuf, "died");
736.  
737.  	    if (t1->deathdnum == astral_level.dnum) {
738.  		int deathlev = t1->deathlev;
739.  		const char *arg, *fmt = " on the Plane of %s";
740.  
741.  		if (!t1->ver_major && !t1->ver_minor && t1->patchlevel < 7)
742.  			deathlev--;
743.  
744.  		switch (deathlev) {
745.  		case -5:
746.  			fmt = " on the %s Plane";
747.  			arg = "Astral";	break;
748.  		case -4:
749.  			arg = "Water";	break;
750.  		case -3:
751.  			arg = "Fire";	break;
752.  		case -2:
753.  			arg = "Air";	break;
754.  		case -1:
755.  			arg = "Earth";	break;
756.  		default:
757.  			arg = "Void";	break;
758.  		}
759.  		Sprintf(eos(linebuf), fmt, arg);
760.  	    } else {
761.  		Sprintf(eos(linebuf), " in %s", dungeons[t1->deathdnum].dname);
762.  		if (t1->deathdnum != knox_level.dnum)
763.  		    Sprintf(eos(linebuf), " on level %d", t1->deathlev);
764.  		if (t1->deathlev != t1->maxlvl)
765.  		    Sprintf(eos(linebuf), " [max %d]", t1->maxlvl);
766.  	    }
767.  
768.  	    /* kludge for "quit while already on Charon's boat" */
769.  	    if (!strncmp(t1->death, "quit ", 5))
770.  		Strcat(linebuf, t1->death + 4);
771.  	}
772.  	Strcat(linebuf, ".");
773.  
774.  	/* Quit, starved, ascended, and escaped contain no second line */
775.  	if (second_line)
776.  	    Sprintf(eos(linebuf), "  %c%s.", highc(*(t1->death)), t1->death+1);
777.  
778.  	lngr = (int)strlen(linebuf);
779.  	if (t1->hp <= 0) hpbuf[0] = '-', hpbuf[1] = '\0';
780.  	else Sprintf(hpbuf, "%d", t1->hp);
781.  	/* beginning of hp column after padding (not actually padded yet) */
782.  	hppos = COLNO - (sizeof("  Hp [max]")-1); /* sizeof(str) includes \0 */
783.  	while (lngr >= hppos) {
784.  	    for(bp = eos(linebuf);
785.  		    !(*bp == ' ' && (bp-linebuf < hppos));
786.  		    bp--)
787.  		;
788.  	    /* special case: if about to wrap in the middle of maximum
789.  	       dungeon depth reached, wrap in front of it instead */
790.  	    if (bp > linebuf + 5 && !strncmp(bp - 5, " [max", 5)) bp -= 5;
791.  	    Strcpy(linebuf3, bp+1);
792.  	    *bp = 0;
793.  	    if (so) {
794.  		while (bp < linebuf + (COLNO-1)) *bp++ = ' ';
795.  		*bp = 0;
796.  		topten_print_bold(linebuf);
797.  	    } else
798.  		topten_print(linebuf);
799.  	    Sprintf(linebuf, "%15s %s", "", linebuf3);
800.  	    lngr = strlen(linebuf);
801.  	}
802.  	/* beginning of hp column not including padding */
803.  	hppos = COLNO - 7 - (int)strlen(hpbuf);
804.  	bp = eos(linebuf);
805.  
806.  	if (bp <= linebuf + hppos) {
807.  	    /* pad any necessary blanks to the hit point entry */
808.  	    while (bp < linebuf + hppos) *bp++ = ' ';
809.  	    Strcpy(bp, hpbuf);
810.  	    Sprintf(eos(bp), " %s[%d]",
811.  		    (t1->maxhp < 10) ? "  " : (t1->maxhp < 100) ? " " : "",
812.  		    t1->maxhp);
813.  	}
814.  
815.  	if (so) {
816.  	    bp = eos(linebuf);
817.  	    if (so >= COLNO) so = COLNO-1;
818.  	    while (bp < linebuf + so) *bp++ = ' ';
819.  	    *bp = 0;
820.  	    topten_print_bold(linebuf);
821.  	} else
822.  	    topten_print(linebuf);
823.  }
824.  
825.  STATIC_OVL int
826.  score_wanted(current_ver, rank, t1, playerct, players, uid)
827.  boolean current_ver;
828.  int rank;
829.  struct toptenentry *t1;
830.  int playerct;
831.  const char **players;
832.  int uid;
833.  {
834.  	int i;
835.  
836.  	if (current_ver && (t1->ver_major != VERSION_MAJOR ||
837.  			    t1->ver_minor != VERSION_MINOR ||
838.  			    t1->patchlevel != PATCHLEVEL))
839.  		return 0;
840.  
841.  #ifdef PERS_IS_UID
842.  	if (!playerct && t1->uid == uid)
843.  		return 1;
844.  #endif
845.  
846.  	for (i = 0; i < playerct; i++) {
847.  		if (players[i][0] == '-' && index("prga", players[i][1]) &&
848.                  players[i][2] == 0 && i + 1 < playerct) {
849.  		char *arg = (char *)players[i + 1];
850.  		if ((players[i][1] == 'p' &&
851.  		     str2role(arg) == str2role(t1->plrole)) ||
852.  		    (players[i][1] == 'r' &&
853.  		     str2race(arg) == str2race(t1->plrace)) ||
854.  		    (players[i][1] == 'g' &&
855.  		     str2gend(arg) == str2gend(t1->plgend)) ||
856.  		    (players[i][1] == 'a' &&
857.  		     str2align(arg) == str2align(t1->plalign)))
858.  		    return 1;
859.  		i++;
860.  		}
861.  		else if (strcmp(players[i], "all") == 0 ||
862.  		    strncmp(t1->name, players[i], NAMSZ) == 0 ||
863.  		    (players[i][0] == '-' &&
864.  		     players[i][1] == t1->plrole[0] &&
865.  		     players[i][2] == 0) ||
866.  		    (digit(players[i][0]) && rank <= atoi(players[i])))
867.  		return 1;
868.  	}
869.  	return 0;
870.  }
871.  
872.  /*
873.   * print selected parts of score list.
874.   * argc >= 2, with argv[0] untrustworthy (directory names, et al.),
875.   * and argv[1] starting with "-s".
876.   */
877.  void
878.  prscore(argc,argv)
879.  int argc;
880.  char **argv;
881.  {
882.  	const char **players;
883.  	int playerct, rank;
884.  	boolean current_ver = TRUE, init_done = FALSE;
885.  	register struct toptenentry *t1;
886.  	FILE *rfile;
887.  	boolean match_found = FALSE;
888.  	register int i;
889.  	char pbuf[BUFSZ];
890.  	int uid = -1;
891.  #ifndef PERS_IS_UID
892.  	const char *player0;
893.  #endif
894.  
895.  	if (argc < 2 || strncmp(argv[1], "-s", 2)) {
896.  		raw_printf("prscore: bad arguments (%d)", argc);
897.  		return;
898.  	}
899.  
900.  	rfile = fopen_datafile_area(NH_RECORD_AREA, NH_RECORD, "r", SCOREPREFIX);
901.  	if (!rfile) {
902.  		raw_print("Cannot open record file!");
903.  		return;
904.  	}
905.  
906.  #ifdef	AMIGA
907.  	{
908.  	    extern winid amii_rawprwin;
909.  	    init_nhwindows(&argc, argv);
910.  	    amii_rawprwin = create_nhwindow(NHW_TEXT);
911.  	}
912.  #endif
913.  
914.  	/* If the score list isn't after a game, we never went through
915.  	 * initialization. */
916.  	if (wiz1_level.dlevel == 0) {
917.  		dlb_init();
918.  		init_dungeons();
919.  		init_done = TRUE;
920.  	}
921.  
922.  	if (!argv[1][2]){	/* plain "-s" */
923.  		argc--;
924.  		argv++;
925.  	} else	argv[1] += 2;
926.  
927.  	if (argc > 1 && !strcmp(argv[1], "-v")) {
928.  		current_ver = FALSE;
929.  		argc--;
930.  		argv++;
931.  	}
932.  
933.  	if (argc <= 1) {
934.  #ifdef PERS_IS_UID
935.  		uid = getuid();
936.  		playerct = 0;
937.  		players = (const char **)0;
938.  #else
939.  		player0 = plname;
940.  		if (!*player0)
941.  # ifdef AMIGA
942.  			player0 = "all";	/* single user system */
943.  # else
944.  			player0 = "hackplayer";
945.  # endif
946.  		playerct = 1;
947.  		players = &player0;
948.  #endif
949.  	} else {
950.  		playerct = --argc;
951.  		players = (const char **)++argv;
952.  	}
953.  	raw_print("");
954.  
955.  	t1 = tt_head = newttentry();
956.  	for (rank = 1; ; rank++) {
957.  	    readentry(rfile, t1);
958.  	    if (t1->points == 0) break;
959.  	    if (!match_found &&
960.  		    score_wanted(current_ver, rank, t1, playerct, players, uid))
961.  		match_found = TRUE;
962.  	    t1->tt_next = newttentry();
963.  	    t1 = t1->tt_next;
964.  	}
965.  
966.  	(void) fclose(rfile);
967.  	if (init_done) {
968.  	    free_dungeons();
969.  	    dlb_cleanup();
970.  	}
971.  
972.  	if (match_found) {
973.  	    outheader();
974.  	    t1 = tt_head;
975.  	    for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
976.  		if (score_wanted(current_ver, rank, t1, playerct, players, uid))
977.  		    (void) outentry(rank, t1, 0);
978.  	    }
979.  	} else {
980.  	    Sprintf(pbuf, "Cannot find any %sentries for ",
981.  				current_ver ? "current " : "");
982.  	    if (playerct < 1) Strcat(pbuf, "you.");
983.  	    else {
984.  		if (playerct > 1) Strcat(pbuf, "any of ");
985.  		for (i = 0; i < playerct; i++) {
986.  		    /* stop printing players if there are too many to fit */
987.  		    if (strlen(pbuf) + strlen(players[i]) + 2 >= BUFSZ) {
988.  			if (strlen(pbuf) < BUFSZ-4) Strcat(pbuf, "...");
989.  			else Strcpy(pbuf+strlen(pbuf)-4, "...");
990.  			break;
991.  		    }
992.  		    Strcat(pbuf, players[i]);
993.  		    if (i < playerct-1) {
994.  			if (players[i][0] == '-' &&
995.  			    index("prga", players[i][1]) && players[i][2] == 0)
996.  			    Strcat(pbuf, " ");
997.  			else Strcat(pbuf, ":");
998.  		    }
999.  		}
1000. 	    }
1001. 	    raw_print(pbuf);
1002. 	    raw_printf("Usage: %s -s [-v] <playertypes> [maxrank] [playernames]",
1003. 
1004. 			 hname);
1005. 	    raw_printf("Player types are: [-p role] [-r race] [-g gender] [-a align]");
1006. 	}
1007. 	free_ttlist(tt_head);
1008. #ifdef	AMIGA
1009. 	{
1010. 	    extern winid amii_rawprwin;
1011. 	    display_nhwindow(amii_rawprwin, 1);
1012. 	    destroy_nhwindow(amii_rawprwin);
1013. 	    amii_rawprwin = WIN_ERR;
1014. 	}
1015. #endif
1016. }
1017. 
1018. STATIC_OVL int
1019. classmon(plch, fem)
1020. 	char *plch;
1021. 	boolean fem;
1022. {
1023. 	int i;
1024. 
1025. 	/* Look for this role in the role table */
1026. 	for (i = 0; roles[i].name.m; i++)
1027. 	    if (!strncmp(plch, roles[i].filecode, ROLESZ)) {
1028. 		if (fem && roles[i].femalenum != NON_PM)
1029. 		    return roles[i].femalenum;
1030. 		else if (roles[i].malenum != NON_PM)
1031. 		    return roles[i].malenum;
1032. 		else
1033. 		    return PM_HUMAN;
1034. 	    }
1035. 	/* this might be from a 3.2.x score for former Elf class */
1036. 	if (!strcmp(plch, "E")) return PM_RANGER;
1037. 
1038. 	impossible("What weird role is this? (%s)", plch);
1039. 	return (PM_HUMAN_MUMMY);
1040. }
1041. 
1042. /*
1043.  * Get a random player name and class from the high score list,
1044.  * and attach them to an object (for statues or morgue corpses).
1045.  */
1046. struct obj *
1047. tt_oname(otmp)
1048. struct obj *otmp;
1049. {
1050. 	int rank;
1051. 	register int i;
1052. 	register struct toptenentry *tt;
1053. 	FILE *rfile;
1054. 	struct toptenentry tt_buf;
1055. 
1056. 	if (!otmp) return((struct obj *) 0);
1057. 
1058. 	rfile = fopen_datafile_area(NH_RECORD_AREA, NH_RECORD, "r", SCOREPREFIX);
1059. 	if (!rfile) {
1060. 		impossible("Cannot open record file!");
1061. 		return (struct obj *)0;
1062. 	}
1063. 
1064. 	tt = &tt_buf;
1065. 	rank = rnd(10);
1066. pickentry:
1067. 	for(i = rank; i; i--) {
1068. 	    readentry(rfile, tt);
1069. 	    if(tt->points == 0) break;
1070. 	}
1071. 
1072. 	if(tt->points == 0) {
1073. 		if(rank > 1) {
1074. 			rank = 1;
1075. 			rewind(rfile);
1076. 			goto pickentry;
1077. 		}
1078. 		otmp = (struct obj *) 0;
1079. 	} else {
1080. 		/* reset timer in case corpse started out as lizard or troll */
1081. 		if (otmp->otyp == CORPSE) obj_stop_timers(otmp);
1082. 		otmp->corpsenm = classmon(tt->plrole, (tt->plgend[0] == 'F'));
1083. 		otmp->owt = weight(otmp);
1084. 		otmp = oname(otmp, tt->name);
1085. 		if (otmp->otyp == CORPSE) start_corpse_timeout(otmp);
1086. 	}
1087. 
1088. 	(void) fclose(rfile);
1089. 	return otmp;
1090. }
1091. 
1092. #ifdef NO_SCAN_BRACK
1093. /* Lattice scanf isn't up to reading the scorefile.  What */
1094. /* follows deals with that; I admit it's ugly. (KL) */
1095. /* Now generally available (KL) */
1096. STATIC_OVL void
1097. nsb_mung_line(p)
1098. 	char *p;
1099. {
1100. 	while ((p = index(p, ' ')) != 0) *p = '|';
1101. }
1102. 
1103. STATIC_OVL void
1104. nsb_unmung_line(p)
1105. 	char *p;
1106. {
1107. 	while ((p = index(p, '|')) != 0) *p = ' ';
1108. }
1109. #endif /* NO_SCAN_BRACK */
1110. 
1111. #if defined(GTK_GRAPHICS) || defined(PROXY_GRAPHICS)
1112. winid
1113. create_toptenwin()
1114. {
1115.     toptenwin = create_nhwindow(NHW_TEXT);
1116. 
1117.     return toptenwin;
1118. }
1119. 
1120. void
1121. destroy_toptenwin()
1122. {
1123.     destroy_nhwindow(toptenwin);
1124.     toptenwin = WIN_ERR;
1125. }
1126. #endif
1127. /*topten.c*/