Source:SLASH'EM 0.0.7E7F2/topten.c

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.

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, " \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] [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*/