Source:NetHack 3.4.0/pager.c

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