Source:NetHack 3.3.0/pager.c

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

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

1.   /*	SCCS Id: @(#)pager.c	3.3	1999/10/10	*/ 2.   /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 3.    /* NetHack may be freely redistributed. See license for details. */ 4.     5.    /* This file contains the command routines dowhatis and dohelp and */ 6.   /* a few other help related facilities */ 7.    8.    #include "hack.h"  9.    #include "dlb.h"  10. 11.  STATIC_DCL boolean FDECL(is_swallow_sym, (int)); 12.  STATIC_DCL int FDECL(append_str, (char *, const char *)); 13.  STATIC_DCL struct permonst * FDECL(lookat, (int, int, char *, char *)); 14.  STATIC_DCL void FDECL(checkfile,  15.   		      (char *,struct permonst *,BOOLEAN_P,BOOLEAN_P)); 16.  STATIC_DCL int FDECL(do_look, (BOOLEAN_P)); 17.  STATIC_DCL boolean FDECL(help_menu, (int *)); 18.  #ifdef PORT_HELP 19.  extern void NDECL(port_help); 20.  #endif 21.   22.   /* Returns "true" for characters that could represent a monster's stomach. */ 23.   STATIC_OVL boolean 24.  is_swallow_sym(c) 25.  int c;  26. { 27.       int i;  28. for (i = S_sw_tl; i <= S_sw_br; i++) 29.  	if ((int)showsyms[i] == c) return TRUE; 30.      return FALSE; 31.  }  32.    33.   /*  34.    * Append new_str to the end of buf if new_str doesn't already exist as  35. * a substring of buf. Return 1 if the string was appended, 0 otherwise. 36.   * It is expected that buf is of size BUFSZ. 37.   */  38.   STATIC_OVL int 39.  append_str(buf, new_str) 40.      char *buf; 41.      const char *new_str; 42.  {  43.       int space_left;	/* space remaining in buf */ 44.   45.       if (strstri(buf, new_str)) return 0; 46.   47.       space_left = BUFSZ - strlen(buf) - 1; 48.      (void) strncat(buf, " or ", space_left); 49.      (void) strncat(buf, new_str, space_left - 4); 50.      return 1; 51.  }  52.    53.   /*  54.    * Return the name of the glyph found at (x,y). 55.   * If not hallucinating and the glyph is a monster, also monster data. 56.   */  57.   STATIC_OVL struct permonst * 58.  lookat(x, y, buf, monbuf) 59.      int x, y;  60. char *buf, *monbuf; 61.  {  62.       register struct monst *mtmp = (struct monst *) 0; 63.      struct permonst *pm = (struct permonst *) 0; 64.      int glyph; 65.   66.       buf[0] = monbuf[0] = 0; 67.      glyph = glyph_at(x,y); 68.      if (u.ux == x && u.uy == y && canseeself) { 69.  	char race[QBUFSZ]; 70.   71.   	/* if not polymorphed, show both the role and the race */ 72.  	race[0] = 0; 73.  	if (!Upolyd) { 74.  	    Sprintf(race, "%s ", urace.adj); 75.  	}  76.    77.   	Sprintf(buf, "%s%s%s called %s",  78.   		Invis ? "invisible " : "",  79.   		race,  80.   		mons[u.umonnum].mname,  81.   		plname); 82.   83.   #ifdef STEED 84.  	if (u.usteed) { 85.  	    char steedbuf[BUFSZ]; 86.   87.   	    Sprintf(steedbuf, ", mounted on %s", y_monnam(u.usteed)); 88.  	    /* assert((sizeof buf >= strlen(buf)+strlen(steedbuf)+1); */  89.   	    Strcat(buf, steedbuf);  90.   	}  91.   #endif  92.       } else if (u.uswallow) {  93.   	/* all locations when swallowed other than the hero are the monster */  94.   	Sprintf(buf, "interior of %s", 95.  				    Blind ? "a monster" : a_monnam(u.ustuck)); 96.   	pm = u.ustuck->data;  97.       } else if (glyph_is_monster(glyph)) {  98.   	bhitpos.x = x;  99.   	bhitpos.y = y;  100.  	mtmp = m_at(x,y);  101.  	if(mtmp != (struct monst *) 0) {  102.  	    register boolean hp = (mtmp->data == &mons[PM_HIGH_PRIEST]);  103.   104.  	    pm = mtmp->data;  105.  	    Sprintf(buf, "%s%s%s", 106. 		    (mtmp->mx != x || mtmp->my != y) ? 107. 			((mtmp->isshk && !Hallucination)  108.  				? "tail of " : "tail of a ") : "", 109. 		    (!hp && mtmp->mtame && !Hallucination) ? "tame " : 110. 		    (!hp && mtmp->mpeaceful && !Hallucination) ? 111. 		                                          "peaceful " : "", 112. 		    (hp ? "high priest" : l_monnam(mtmp)));  113.  	    if (u.ustuck == mtmp)  114.  		Strcat(buf, (Upolyd && sticks(youmonst.data)) ? 115. 			", being held" : ", holding you");  116.  	    if (mtmp->mleashed)  117.  		Strcat(buf, ", leashed to you");  118.   119.  	    {  120.  		int ways_seen = 0, normal = 0, xraydist;  121.  		boolean useemon = (boolean) canseemon(mtmp);  122.   123.  		xraydist = (u.xray_range<0) ? -1 : u.xray_range * u.xray_range;  124.  		/* normal vision */  125.  		if ((mtmp->wormno ? worm_known(mtmp) : cansee(mtmp->mx, mtmp->my)) && 126. 			mon_visible(mtmp) && !mtmp->minvis) {  127.  		    ways_seen++;  128.  		    normal++;  129.  		}  130.  		/* see invisible */  131.  		if (useemon && mtmp->minvis)  132.  		    ways_seen++;  133.  		/* infravision */  134.  		if ((!mtmp->minvis || See_invisible) && see_with_infrared(mtmp))  135.  		    ways_seen++;  136.  		/* telepathy */  137.  		if (tp_sensemon(mtmp))  138.  		    ways_seen++;  139.  		/* xray */  140.  		if (useemon && xraydist > 0 && 141. 			distu(mtmp->mx, mtmp->my) <= xraydist)  142.  		    ways_seen++;  143.  		if (Detect_monsters)  144.  		    ways_seen++;  145.   146.  		if (ways_seen > 1 || !normal) {  147.  		    if (normal) {  148.  			Strcat(monbuf, "normal vision");  149.  			/* can't actually be 1 yet here */  150.  			if (ways_seen-- > 1) Strcat(monbuf, ", ");  151.  		    }  152.  		    if (useemon && mtmp->minvis) {  153.  			Strcat(monbuf, "see invisible");  154.  			if (ways_seen-- > 1) Strcat(monbuf, ", ");  155.  		    }  156.  		    if ((!mtmp->minvis || See_invisible) && 157. 			    see_with_infrared(mtmp)) {  158.  			Strcat(monbuf, "infravision");  159.  			if (ways_seen-- > 1) Strcat(monbuf, ", ");  160.  		    }  161.  		    if (tp_sensemon(mtmp)) {  162.  			Strcat(monbuf, "telepathy");  163.  			if (ways_seen-- > 1) Strcat(monbuf, ", ");  164.  		    }  165.  		    if (useemon && xraydist > 0 && 166. 			    distu(mtmp->mx, mtmp->my) <= xraydist) {  167.  			/* Eyes of the Overworld */  168.  			Strcat(monbuf, "astral vision");  169.  			if (ways_seen-- > 1) Strcat(monbuf, ", ");  170.  		    }  171.  		    if (Detect_monsters) {  172.  			Strcat(monbuf, "monster detection");  173.  			if (ways_seen-- > 1) Strcat(monbuf, ", ");  174.  		    }  175.  		}  176.  	    }  177.  	}  178.      }  179.      else if (glyph_is_object(glyph)) {  180.  	struct obj *otmp = vobj_at(x,y);  181.   182.  	if (!otmp || otmp->otyp != glyph_to_obj(glyph)) {  183.  	    if (glyph_to_obj(glyph) != STRANGE_OBJECT) {  184.  		otmp = mksobj(glyph_to_obj(glyph), FALSE, FALSE);  185.  		if (otmp->oclass == GOLD_CLASS)  186.  		    otmp->quan = 2L; /* to force pluralization */  187.  		else if (otmp->otyp == SLIME_MOLD)  188.  		    otmp->spe = current_fruit;	/* give the fruit a type */  189.  		Strcpy(buf, distant_name(otmp, xname)); 190. 		dealloc_obj(otmp); 191. 	    }  192.  	} else 193. 	    Strcpy(buf, distant_name(otmp, xname)); 194.  195.  	if (levl[x][y].typ == STONE || levl[x][y].typ == SCORR) 196. 	    Strcat(buf, " embedded in stone"); 197. 	else if (IS_WALL(levl[x][y].typ) || levl[x][y].typ == SDOOR) 198. 	    Strcat(buf, " embedded in a wall"); 199. 	else if (closed_door(x,y)) 200. 	    Strcat(buf, " embedded in a door"); 201. 	else if (is_pool(x,y)) 202. 	    Strcat(buf, " in water"); 203. 	else if (is_lava(x,y)) 204. 	    Strcat(buf, " in molten lava");	/* [can this ever happen?] */ 205.     } else if (glyph_is_trap(glyph)) { 206. 	int tnum = what_trap(glyph_to_trap(glyph)); 207. 	Strcpy(buf, defsyms[trap_to_defsym(tnum)].explanation); 208.     } else if(!glyph_is_cmap(glyph)) { 209. 	Strcpy(buf,"dark part of a room"); 210.     } else switch(glyph_to_cmap(glyph)) { 211.     case S_altar: 212. 	if(!In_endgame(&u.uz)) 213. 	    Sprintf(buf, "%s altar",  214.  		align_str(Amask2align(levl[x][y].altarmask & ~AM_SHRINE))); 215. 	else Sprintf(buf, "aligned altar"); 216. 	break; 217.     case S_ndoor: 218. 	if (is_drawbridge_wall(x, y) >= 0) 219. 	    Strcpy(buf,"open drawbridge portcullis"); 220. 	else if ((levl[x][y].doormask & ~D_TRAPPED) == D_BROKEN) 221. 	    Strcpy(buf,"broken door"); 222. 	else 223. 	    Strcpy(buf,"doorway"); 224. 	break; 225.     case S_cloud: 226. 	Strcpy(buf, Is_airlevel(&u.uz) ? "cloudy area" : "fog/vapor cloud"); 227. 	break; 228.     default: 229. 	Strcpy(buf,defsyms[glyph_to_cmap(glyph)].explanation); 230. 	break; 231.     }  232.   233.      return ((pm && !Hallucination) ? pm : (struct permonst *) 0); 234. }  235.   236.  /*  237.   * Look in the "data" file for more info. Called if the user typed in the 238.  * whole name (user_typed_name == TRUE), or we've found a possible match 239.  * with a character/glyph and flags.help is TRUE. 240.  *  241.   * NOTE: when (user_typed_name == FALSE), inp is considered read-only and 242.  *	 must not be changed directly, e.g. via lcase. We want to force 243.  *	 lcase for data.base lookup so that we can have a clean key. 244.  *	 Therefore, we create a copy of inp _just_ for data.base lookup. 245.  */  246.  STATIC_OVL void 247. checkfile(inp, pm, user_typed_name, without_asking) 248.     char *inp; 249.     struct permonst *pm; 250.     boolean user_typed_name, without_asking; 251. {  252.      dlb *fp; 253.     char buf[BUFSZ], newstr[BUFSZ]; 254.     char *ep, *dbase_str; 255.     long txt_offset; 256.     int chk_skip; 257.     boolean found_in_file = FALSE, skipping_entry = FALSE; 258.  259.      fp = dlb_fopen(DATAFILE, "r"); 260.     if (!fp) { 261. 	pline("Cannot open data file!"); 262. 	return; 263.     }  264.   265.      /* To prevent the need for entries in data.base like *ngel to account 266.      * for Angel and angel, make the lookup string the same for both 267.      * user_typed_name and picked name. 268.      */  269.      if (pm != (struct permonst *) 0 && !user_typed_name) 270. 	dbase_str = strcpy(newstr, pm->mname); 271.     else dbase_str = strcpy(newstr, inp); 272.     (void) lcase(dbase_str); 273.  274.      if (!strncmp(dbase_str, "interior of ", 12)) 275. 	dbase_str += 12; 276.     if (!strncmp(dbase_str, "a ", 2)) 277. 	dbase_str += 2; 278.     else if (!strncmp(dbase_str, "an ", 3)) 279. 	dbase_str += 3; 280.     else if (!strncmp(dbase_str, "the ", 4)) 281. 	dbase_str += 4; 282.     if (!strncmp(dbase_str, "tame ", 5)) 283. 	dbase_str += 5; 284.     else if (!strncmp(dbase_str, "peaceful ", 9)) 285. 	dbase_str += 9; 286.     if (!strncmp(dbase_str, "invisible ", 10)) 287. 	dbase_str += 10; 288.     if (!strncmp(dbase_str, "statue of ", 10)) 289. 	dbase_str[6] = '\0'; 290.     else if (!strncmp(dbase_str, "figurine of ", 12)) 291. 	dbase_str[8] = '\0'; 292.  293.      /* Make sure the name is non-empty. */ 294.      if (*dbase_str) { 295. 	/* adjust the input to remove "named " and convert to lower case */ 296. 	char *alt = 0;	/* alternate description */ 297. 	if ((ep = strstri(dbase_str, " named ")) != 0) 298. 	    alt = ep + 7; 299. 	else 300. 	    ep = strstri(dbase_str, " called "); 301. 	if (ep) *ep = '\0'; 302. 	else if ((ep = strstri(dbase_str, ", ")) != 0) *ep = '\0'; 303.  304.  	/*  305.  	 * If the object is named, then the name is the alternate description; 306. 	 * otherwise, the result of makesingular applied to the name is. This 307. 	 * isn't strictly optimal, but named objects of interest to the user 308. 	 * will usually be found under their name, rather than under their 309. 	 * object type, so looking for a singular form is pointless. 310. 	 */  311.   312.  	if (!alt) 313. 	    alt = makesingular(dbase_str); 314. 	else 315. 	    if (user_typed_name) 316. 		(void) lcase(alt); 317.  318.  	/* skip first record; read second */ 319. 	txt_offset = 0L; 320. 	if (!dlb_fgets(buf, BUFSZ, fp) || !dlb_fgets(buf, BUFSZ, fp)) { 321. 	    impossible("can't read 'data' file"); 322. 	    (void) dlb_fclose(fp); 323. 	    return; 324. 	} else if (sscanf(buf, "%8lx\n", &txt_offset) < 1 || txt_offset <= 0) 325. 	    goto bad_data_file; 326.  327.  	/* look for the appropriate entry */ 328. 	while (dlb_fgets(buf,BUFSZ,fp)) { 329. 	    if (*buf == '.') break;  /* we passed last entry without success */ 330.  331.  	    if (digit(*buf)) { 332. 		/* a number indicates the end of current entry */ 333. 		skipping_entry = FALSE; 334. 	    } else if (!skipping_entry) { 335. 		if (!(ep = index(buf, '\n'))) goto bad_data_file; 336. 		*ep = 0; 337. 		/* if we match a key that begins with "~", skip this entry */ 338. 		chk_skip = (*buf == '~') ? 1 : 0; 339.  		if (pmatch(&buf[chk_skip], dbase_str) ||  340.  			(alt && pmatch(&buf[chk_skip], alt))) { 341. 		    if (chk_skip) { 342. 			skipping_entry = TRUE; 343. 			continue; 344. 		    } else { 345. 			found_in_file = TRUE; 346. 			break; 347. 		    }  348.  		}  349.  	    }  350.  	}  351.      }  352.   353.      if(found_in_file) { 354. 	long entry_offset; 355. 	int  entry_count; 356. 	int  i;  357. 358. 	/* skip over other possible matches for the info */ 359. 	do { 360. 	    if (!dlb_fgets(buf, BUFSZ, fp)) goto bad_data_file; 361. 	} while (!digit(*buf)); 362. 	if (sscanf(buf, "%ld,%d\n", &entry_offset, &entry_count) < 2) { 363. bad_data_file:	impossible("'data' file in wrong format"); 364. 		(void) dlb_fclose(fp); 365. 		return; 366. 	}  367.   368.  	if (user_typed_name || without_asking || yn("More info?") == 'y') { 369. 	    winid datawin; 370.  371.  	    if (dlb_fseek(fp, txt_offset + entry_offset, SEEK_SET) < 0) { 372. 		pline("? Seek error on 'data' file!"); 373. 		(void) dlb_fclose(fp); 374. 		return; 375. 	    }  376.  	    datawin = create_nhwindow(NHW_MENU); 377. 	    for (i = 0; i < entry_count; i++) { 378. 		if (!dlb_fgets(buf, BUFSZ, fp)) goto bad_data_file; 379. 		if ((ep = index(buf, '\n')) != 0) *ep = 0; 380. 		if (index(buf+1, '\t') != 0) (void) tabexpand(buf+1); 381. 		putstr(datawin, 0, buf+1); 382. 	    }  383.  	    display_nhwindow(datawin, FALSE); 384. 	    destroy_nhwindow(datawin); 385. 	}  386.      } else if (user_typed_name) 387. 	pline("I don't have any information on those things."); 388.  389.      (void) dlb_fclose(fp); 390. }  391.   392.  /* getpos return values */ 393. #define LOOK_TRADITIONAL	0	/* '.' -- ask about "more info?" */ 394.  #define LOOK_QUICK		1	/* ',' -- skip "more info?" */ 395.  #define LOOK_ONCE		2	/* ';' -- skip and stop looping */ 396. #define LOOK_VERBOSE		3	/* ':' -- show more info w/o asking */ 397.  398.  /* also used by getpos hack in do_name.c */ 399. const char what_is_an_unknown_object[] = "an unknown object"; 400.  401.  STATIC_OVL int 402. do_look(quick) 403.     boolean quick;	/* use cursor && don't search for "more info" */ 404. {  405.      char    out_str[BUFSZ], look_buf[BUFSZ]; 406.     const char *x_str, *firstmatch = 0; 407.     struct permonst *pm = 0; 408.     int     i, ans = 0; 409.     int     sym;		/* typed symbol or converted glyph */ 410.     int	    found;		/* count of matching syms found */ 411.     coord   cc;			/* screen pos of unknown glyph */ 412.     boolean save_verbose;	/* saved value of flags.verbose */ 413.     boolean from_screen;	/* question from the screen */ 414.     boolean need_to_look;	/* need to get explan. from glyph */ 415.     boolean hit_trap;		/* true if found trap explanation */ 416.     int skipped_venom = 0;	/* non-zero if we ignored "splash of venom" */ 417.     static const char *mon_interior = "the interior of a monster"; 418.  419.      if (quick) { 420. 	from_screen = TRUE;	/* yes, we want to use the cursor */ 421.     } else { 422. 	i = ynq("Specify unknown object by cursor?"); 423. 	if (i == 'q') return 0; 424. 	from_screen = (i == 'y'); 425.     }  426.   427.      if (from_screen) { 428. 	cc.x = u.ux; 429. 	cc.y = u.uy; 430. 	sym = 0;		/* gcc -Wall lint */ 431.     } else { 432. 	getlin("Specify what? (type the word)", out_str); 433. 	if (out_str[0] == '\0' || out_str[0] == '\033') 434. 	    return 0; 435.  436.  	if (out_str[1]) {	/* user typed in a complete string */ 437. 	    checkfile(out_str, pm, TRUE, TRUE); 438. 	    return 0; 439. 	}  440.  	sym = out_str[0]; 441.     }  442.   443.      /* Save the verbose flag, we change it later. */ 444.      save_verbose = flags.verbose; 445.     flags.verbose = flags.verbose && !quick; 446.     /*  447.       * The user typed one letter, or we're identifying from the screen. 448.      */  449.      do { 450. 	/* Reset some variables. */ 451.  	need_to_look = FALSE; 452. 	pm = (struct permonst *)0; 453. 	found = 0; 454. 	out_str[0] = '\0'; 455.  456.  	if (from_screen) { 457. 	    int glyph;	/* glyph at selected position */ 458.  459.  	    if (flags.verbose) 460. 		pline("Please move the cursor to %s.",  461.  		       what_is_an_unknown_object); 462. 	    else 463. 		pline("Pick an object."); 464.  465.  	    ans = getpos(&cc, quick, what_is_an_unknown_object); 466. 	    if (ans < 0 || cc.x < 0) { 467. 		flags.verbose = save_verbose; 468. 		return 0;	/* done */ 469. 	    }  470.  	    flags.verbose = FALSE;	/* only print long question once */ 471.  472.  	    /* Convert the glyph at the selected position to a symbol. */ 473.  	    glyph = glyph_at(cc.x,cc.y); 474. 	    if (glyph_is_cmap(glyph)) { 475. 		sym = showsyms[glyph_to_cmap(glyph)]; 476. 	    } else if (glyph_is_trap(glyph)) { 477. 		sym = showsyms[trap_to_defsym(glyph_to_trap(glyph))]; 478. 	    } else if (glyph_is_object(glyph)) { 479. 		sym = oc_syms[(int)objects[glyph_to_obj(glyph)].oc_class]; 480. 	    } else if (glyph_is_monster(glyph)) { 481. 		/* takes care of pets, detected, ridden, and regular mons */ 482. 		sym = monsyms[(int)mons[glyph_to_mon(glyph)].mlet]; 483. 	    } else if (glyph_is_swallow(glyph)) { 484. 		sym = showsyms[glyph_to_swallow(glyph)+S_sw_tl]; 485. 	    } else if (glyph_is_invisible(glyph)) { 486. 		sym = DEF_INVISIBLE; 487. 	    } else { 488. 		impossible("do_look:  bad glyph %d at (%d,%d)",  489.  						glyph, (int)cc.x, (int)cc.y); 490. 		sym = ' '; 491. 	    }  492.  	}  493.   494.  	/*  495.  	 * Check all the possibilities, saving all explanations in a buffer. 496. 	 * When all have been checked then the string is printed. 497. 	 */  498.   499.  	/* Check for monsters */ 500. 	for (i = 0; i < MAXMCLASSES; i++) { 501. 	    if (sym == (from_screen ? monsyms[i] : def_monsyms[i])) { 502. 		need_to_look = TRUE; 503. 		if (!found) { 504. 		    Sprintf(out_str, "%c       %s", sym, an(monexplain[i])); 505. 		    firstmatch = monexplain[i]; 506. 		    found++; 507. 		} else { 508. 		    found += append_str(out_str, an(monexplain[i])); 509. 		}  510.  	    }  511.  	}  512.  	/* handle '@' as a special case; firstmatch is guaranteed 513. 	   to already be set in that case */ 514. 	if (!from_screen ? (sym == def_monsyms[S_HUMAN]) :  515.  		(cc.x == u.ux && cc.y == u.uy && sym == monsyms[S_HUMAN])) 516. 	     found += append_str(out_str, "you");	/* tack on "or you" */ 517.  518.  	/*  519.  	 * Special case: if identifying from the screen, and we're swallowed, 520. 	 * and looking at something other than our own symbol, then just say 521. 	 * "the interior of a monster". 522. 	 */  523.  	if (u.uswallow && from_screen && is_swallow_sym(sym)) { 524. 	    if (!found) { 525. 		Sprintf(out_str, "%c       %s", sym, mon_interior); 526. 		firstmatch = mon_interior; 527. 	    } else { 528. 		found += append_str(out_str, mon_interior); 529. 	    }  530.  	    need_to_look = TRUE; 531. 	}  532.   533.  	/* Now check for objects */ 534. 	for (i = 1; i < MAXOCLASSES; i++) { 535. 	    if (sym == (from_screen ? oc_syms[i] : def_oc_syms[i])) { 536. 		need_to_look = TRUE; 537. 		if (from_screen && i == VENOM_CLASS) { 538. 		    skipped_venom++; 539. 		    continue; 540. 		}  541.  		if (!found) { 542. 		    Sprintf(out_str, "%c       %s", sym, an(objexplain[i])); 543. 		    firstmatch = objexplain[i]; 544. 		    found++; 545. 		} else { 546. 		    found += append_str(out_str, an(objexplain[i])); 547. 		}  548.  	    }  549.  	}  550.   551.  	if (sym == DEF_INVISIBLE) { 552. 	    if (!found) { 553. 		Sprintf(out_str, "%c       %s", sym, an(invisexplain)); 554. 		firstmatch = invisexplain; 555. 		found++; 556. 	    } else { 557. 		found += append_str(out_str, an(invisexplain)); 558. 	    }  559.  	}  560.   561.  #define is_cmap_trap(i) ((i) >= S_arrow_trap && (i) <= S_polymorph_trap) 562. #define is_cmap_drawbridge(i) ((i) >= S_vodbridge && (i) <= S_hcdbridge) 563.  564.  	/* Now check for graphics symbols */ 565. 	for (hit_trap = FALSE, i = 0; i < MAXPCHARS; i++) { 566. 	    x_str = defsyms[i].explanation; 567. 	    if (sym == (from_screen ? showsyms[i] : defsyms[i].sym) && *x_str) { 568. 		/* avoid "an air", "a water", or "a floor of a room" */ 569. 		int article = (i == S_room) ? 2 :		/* 2=>"the" */ 570. 			      !(strcmp(x_str, "air") == 0 ||	/* 1=>"an"  */  571.  				strcmp(x_str, "water") == 0);	/* 0=>(none)*/ 572.  573.  		if (!found) { 574. 		    if (is_cmap_trap(i)) { 575. 			Sprintf(out_str, "%c       a trap", sym); 576. 			hit_trap = TRUE; 577. 		    } else { 578. 			Sprintf(out_str, "%c       %s", sym,  579.  				article == 2 ? the(x_str) :  580.  				article == 1 ? an(x_str) : x_str); 581. 		    }  582.  		    firstmatch = x_str; 583. 		    found++; 584. 		} else if (!u.uswallow && !(hit_trap && is_cmap_trap(i)) &&  585.  			   !(found >= 3 && is_cmap_drawbridge(i))) { 586. 		    found += append_str(out_str,  587.  					article == 2 ? the(x_str) :  588.  					article == 1 ? an(x_str) : x_str); 589. 		    if (is_cmap_trap(i)) hit_trap = TRUE; 590. 		}  591.   592.  		if (i == S_altar || is_cmap_trap(i)) 593. 		    need_to_look = TRUE; 594. 	    }  595.  	}  596.   597.  	/* if we ignored venom and list turned out to be short, put it back */ 598. 	if (skipped_venom && found < 2) { 599. 	    x_str = objexplain[VENOM_CLASS]; 600. 	    if (!found) { 601. 		Sprintf(out_str, "%c       %s", sym, an(x_str)); 602. 		firstmatch = x_str; 603. 		found++; 604. 	    } else { 605. 		found += append_str(out_str, an(x_str)); 606. 	    }  607.  	}  608.   609.  	/*  610.  	 * If we are looking at the screen, follow multiple possibilities or  611. * an ambiguous explanation by something more detailed. 612. 	 */  613.  	if (from_screen) { 614. 	    if (found > 1 || need_to_look) { 615. 		char monbuf[BUFSZ]; 616. 		char temp_buf[BUFSZ], coybuf[QBUFSZ]; 617.  618.  		pm = lookat(cc.x, cc.y, look_buf, monbuf); 619. 		firstmatch = look_buf; 620. 		if (*firstmatch) { 621. 		    Sprintf(temp_buf, " (%s)",  622.  				(pm == &mons[PM_COYOTE]) ?  623.  				coyotename(coybuf) : firstmatch); 624. 		    (void)strncat(out_str, temp_buf, BUFSZ-strlen(out_str)-1); 625. 		    found = 1;	/* we have something to look up */ 626. 		}  627.  		if (monbuf[0]) { 628. 		    Sprintf(temp_buf, " [seen: %s]", monbuf); 629. 		    (void)strncat(out_str, temp_buf, BUFSZ-strlen(out_str)-1); 630. 		}  631.  	    }  632.  	}  633.   634.  	/* Finally, print out our explanation. */ 635.  	if (found) { 636. 	    pline("%s", out_str); 637. 	    /* check the data file for information about this thing */ 638. 	    if (found == 1 && ans != LOOK_QUICK && ans != LOOK_ONCE &&  639.  			(ans == LOOK_VERBOSE || (flags.help && !quick))) { 640. 		char temp_buf[BUFSZ]; 641. 		Strcpy(temp_buf, firstmatch); 642. 		checkfile(temp_buf, pm, FALSE, (boolean)(ans == LOOK_VERBOSE)); 643. 	    }  644.  	} else { 645. 	    pline("I've never heard of such things."); 646. 	}  647.   648.      } while (from_screen && !quick && ans != LOOK_ONCE); 649.  650.      flags.verbose = save_verbose; 651.     return 0; 652. }  653.   654.   655.  int 656. dowhatis 657. {  658.  	return do_look(FALSE); 659. }  660.   661.  int 662. doquickwhatis 663. {  664.  	return do_look(TRUE); 665. }  666.   667.  int 668. doidtrap 669. {  670.  	register struct trap *trap; 671. 	int x, y, tt; 672.  673.  	if (!getdir((char *)0)) return 0; 674. 	x = u.ux + u.dx; 675. 	y = u.uy + u.dy; 676. 	for (trap = ftrap; trap; trap = trap->ntrap) 677. 	    if (trap->tx == x && trap->ty == y) { 678. 		if (!trap->tseen) break; 679. 		tt = trap->ttyp; 680. 		if (u.dz) { 681. 		    if (u.dz < 0 ? (tt == TRAPDOOR || tt == HOLE) :  682.  			    tt == ROCKTRAP) break; 683. 		}  684.  		tt = what_trap(tt); 685. 		pline("That is %s%s%s.",  686.  		      an(defsyms[trap_to_defsym(tt)].explanation),  687.  		      !trap->madeby_u ? "" : (tt == WEB) ? " woven" :  688.  			  /* trap doors & spiked pits can't be made by  689.  			     player, and should be considered at least  690.  			     as much "set" as "dug" anyway */  691.  			  (tt == HOLE || tt == PIT) ? " dug" : " set",  692.  		      !trap->madeby_u ? "" : " by you"); 693. 		return 0; 694. 	    }  695.  	pline("I can't see a trap there."); 696. 	return 0; 697. }  698.   699.  int 700. dowhatdoes 701. {  702.  	dlb *fp; 703. 	char bufr[BUFSZ+6]; 704. 	register char *buf = &bufr[6], *ep, q, ctrl, meta; 705.  706.  	fp = dlb_fopen(CMDHELPFILE, "r"); 707. 	if (!fp) { 708. 		pline("Cannot open data file!"); 709. 		return 0; 710. 	}  711.   712.  #if defined(UNIX) || defined(VMS) 713. 	introff; 714. #endif 715. 	q = yn_function("What command?", (char *)0, '\0'); 716. #if defined(UNIX) || defined(VMS) 717. 	intron; 718. #endif 719. 	ctrl = ((q <= '\033') ? (q - 1 + 'A') : 0); 720. 	meta = ((0x80 & q) ? (0x7f & q) : 0); 721. 	while(dlb_fgets(buf,BUFSZ,fp)) 722. 	    if ((ctrl && *buf=='^' && *(buf+1)==ctrl) ||  723.  		(meta && *buf=='M' && *(buf+1)=='-' && *(buf+2)==meta) ||  724.  		*buf==q) { 725. 		ep = index(buf, '\n'); 726. 		if(ep) *ep = 0; 727. 		if (ctrl && buf[2] == '\t'){ 728. 			buf = bufr + 1; 729. 			(void) strncpy(buf, "^?      ", 8); 730. 			buf[1] = ctrl; 731. 		} else if (meta && buf[3] == '\t'){ 732. 			buf = bufr + 2; 733. 			(void) strncpy(buf, "M-?     ", 8); 734. 			buf[2] = meta; 735. 		} else if(buf[1] == '\t'){ 736. 			buf = bufr; 737. 			buf[0] = q;  738. (void) strncpy(buf+1, "      ", 7); 739. 		}  740.  		pline("%s", buf); 741. 		(void) dlb_fclose(fp); 742. 		return 0; 743. 	    }  744.  	pline("I've never heard of such commands."); 745. 	(void) dlb_fclose(fp); 746. 	return 0; 747. }  748.   749.  /* data for help_menu */ 750. static const char *help_menu_items[] = { 751. /* 0*/	"Long description of the game and commands.", 752. /* 1*/	"List of game commands.", 753. /* 2*/	"Concise history of NetHack.", 754. /* 3*/	"Info on a character in the game display.", 755. /* 4*/	"Info on what a given key does.", 756. /* 5*/	"List of game options.", 757. /* 6*/	"Longer explanation of game options.", 758. /* 7*/	"List of extended commands.", 759. /* 8*/	"The NetHack license.", 760. #ifdef PORT_HELP 761. 	"%s-specific help and commands.", 762. #define PORT_HELP_ID 100 763. #define WIZHLP_SLOT 10 764. #else 765. #define WIZHLP_SLOT 9 766. #endif 767. #ifdef WIZARD 768. 	"List of wizard-mode commands.", 769. #endif 770. 	"",  771.  	(char *)0 772. };  773.   774.  STATIC_OVL boolean 775. help_menu(sel) 776. 	int *sel; 777. {  778.  	winid tmpwin = create_nhwindow(NHW_MENU); 779. #ifdef PORT_HELP 780. 	char helpbuf[QBUFSZ]; 781. #endif 782. 	int i, n;  783. menu_item *selected; 784. 	anything any; 785.  786.  	any.a_void = 0;		/* zero all bits */ 787. 	start_menu(tmpwin); 788. #ifdef WIZARD 789. 	if (!wizard) help_menu_items[WIZHLP_SLOT] = "", 790. 		     help_menu_items[WIZHLP_SLOT+1] = (char *)0; 791. #endif 792. 	for (i = 0; help_menu_items[i]; i++) 793. #ifdef PORT_HELP 794. 	    /* port-specific line has a %s in it for the PORT_ID */ 795. 	    if (help_menu_items[i][0] == '%') { 796. 		Sprintf(helpbuf, help_menu_items[i], PORT_ID); 797. 		any.a_int = PORT_HELP_ID + 1; 798. 		add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,  799.  			 helpbuf, MENU_UNSELECTED); 800. 	    } else 801. #endif 802. 	    {  803.  		any.a_int = (*help_menu_items[i]) ? i+1 : 0; 804. 		add_menu(tmpwin, NO_GLYPH, &any, 0, 0,  805.  			ATR_NONE, help_menu_items[i], MENU_UNSELECTED); 806. 	    }  807.  	end_menu(tmpwin, "Select one item:"); 808. 	n = select_menu(tmpwin, PICK_ONE, &selected); 809. 	destroy_nhwindow(tmpwin); 810. 	if (n > 0) { 811. 	    *sel = selected[0].item.a_int - 1; 812. 	    free((genericptr_t)selected); 813. 	    return TRUE; 814. 	}  815.  	return FALSE; 816. }  817.   818.  int 819. dohelp 820. {  821.  	int sel = 0; 822.  823.  	if (help_menu(&sel)) { 824. 		switch (sel) { 825. 			case  0:  display_file(HELP, TRUE);  break; 826. 			case  1:  display_file(SHELP, TRUE);  break; 827. 			case  2:  (void) dohistory;  break; 828. 			case  3:  (void) dowhatis;  break; 829. 			case  4:  (void) dowhatdoes;  break; 830. 			case  5:  option_help;  break; 831. 			case  6:  display_file(OPTIONFILE, TRUE);  break; 832. 			case  7:  (void) doextlist;  break; 833. 			case  8:  display_file(LICENSE, TRUE);  break; 834. #ifdef WIZARD 835. 			/* handle slot 9 or 10 */ 836. 			default: display_file(DEBUGHELP, TRUE);  break; 837. #endif 838. #ifdef PORT_HELP 839. 			case PORT_HELP_ID:  port_help;  break; 840. #endif 841. 		}  842.  	}  843.  	return 0; 844. }  845.   846.  int 847. dohistory 848. {  849.  	display_file(HISTORY, TRUE); 850. 	return 0; 851. }  852.   853.  /*pager.c*/