1 /* NetHack 3.6 pager.c $NHDT-Date: 1448482543 2015/11/25 20:15:43 $ $NHDT-Branch: master $:$NHDT-Revision: 1.86 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 /* This file contains the command routines dowhatis() and dohelp() and */
6 /* a few other help related facilities */
11 STATIC_DCL boolean FDECL(is_swallow_sym, (int));
12 STATIC_DCL int FDECL(append_str, (char *, const char *));
13 STATIC_DCL void FDECL(look_at_object, (char *, int, int, int));
14 STATIC_DCL void FDECL(look_at_monster, (char *, char *,
15 struct monst *, int, int));
16 STATIC_DCL struct permonst *FDECL(lookat, (int, int, char *, char *));
17 STATIC_DCL void FDECL(checkfile, (char *, struct permonst *,
18 BOOLEAN_P, BOOLEAN_P));
19 STATIC_DCL void FDECL(look_all, (BOOLEAN_P,BOOLEAN_P));
20 STATIC_DCL boolean FDECL(help_menu, (int *));
21 STATIC_DCL void NDECL(docontact);
23 extern void NDECL(port_help);
26 /* Returns "true" for characters that could represent a monster's stomach. */
33 for (i = S_sw_tl; i <= S_sw_br; i++)
34 if ((int) showsyms[i] == c)
40 * Append new_str to the end of buf if new_str doesn't already exist as
41 * a substring of buf. Return 1 if the string was appended, 0 otherwise.
42 * It is expected that buf is of size BUFSZ.
45 append_str(buf, new_str)
49 int space_left; /* space remaining in buf */
51 if (strstri(buf, new_str))
54 space_left = BUFSZ - strlen(buf) - 1;
56 (void) strncat(buf, " or ", space_left);
57 (void) strncat(buf, new_str, space_left - 4);
59 (void) strncat(buf, "
\82Ü
\82½
\82Í", space_left);
60 (void) strncat(buf, new_str, space_left - 6);
65 /* shared by monster probing (via query_objlist!) as well as lookat() */
72 /* include race with role unless polymorphed */
76 Sprintf(race, "%s ", urace.adj);
78 Sprintf(race, "%s", urace.adj);
80 Sprintf(outbuf, "%s%s%s called %s",
81 /* being blinded may hide invisibility from self */
82 (Invis && (senseself() || !Blind)) ? "invisible " : "", race,
83 mons[u.umonnum].mname, plname);
85 Sprintf(outbuf, "%s%s%s
\82Æ
\82¢
\82¤
\96¼
\82Ì%s",
86 (Invis && (senseself() || !Blind)) ? "
\8ep
\82Ì
\8c©
\82¦
\82È
\82¢" : "", race,
88 mons[u.umonnum].mname);
92 Sprintf(eos(outbuf), ", mounted on %s", y_monnam(u.usteed));
94 Sprintf(eos(outbuf), "
\81C%s
\82É
\8fæ
\82Á
\82Ä
\82¢
\82é", y_monnam(u.usteed));
98 /* extracted from lookat(); also used by namefloorobj() */
100 object_from_map(glyph, x, y, obj_p)
104 boolean fakeobj = FALSE;
106 struct obj *otmp = vobj_at(x, y);
107 int glyphotyp = glyph_to_obj(glyph);
109 *obj_p = (struct obj *) 0;
110 /* there might be a mimic here posing as an object */
112 if (mtmp && is_obj_mappear(mtmp, (unsigned) glyphotyp))
117 if (!otmp || otmp->otyp != glyphotyp) {
118 /* this used to exclude STRANGE_OBJECT; now caller deals with it */
119 otmp = mksobj(glyphotyp, FALSE, FALSE);
123 if (otmp->oclass == COIN_CLASS)
124 otmp->quan = 2L; /* to force pluralization */
125 else if (otmp->otyp == SLIME_MOLD)
126 otmp->spe = context.current_fruit; /* give it a type */
127 if (mtmp && has_mcorpsenm(mtmp)) /* mimic as corpse/statue */
128 otmp->corpsenm = MCORPSENM(mtmp);
130 /* if located at adjacent spot, mark it as having been seen up close */
131 if (otmp && distu(x, y) <= 2 && !Blind && !Hallucination)
135 return fakeobj; /* when True, caller needs to dealloc *obj_p */
139 look_at_object(buf, x, y, glyph)
140 char *buf; /* output buffer */
143 struct obj *otmp = 0;
144 boolean fakeobj = object_from_map(glyph, x, y, &otmp);
147 Strcpy(buf, (otmp->otyp != STRANGE_OBJECT)
148 ? distant_name(otmp, xname)
149 : obj_descr[STRANGE_OBJECT].oc_name);
151 dealloc_obj(otmp), otmp = 0;
153 Strcpy(buf, something); /* sanity precaution */
155 if (levl[x][y].typ == STONE || levl[x][y].typ == SCORR)
157 Strcat(buf, " embedded in stone");
159 Strcat(buf, "
\81C
\8aâ
\82É
\96\84\82ß
\82±
\82Ü
\82ê
\82Ä
\82¢
\82é");
160 else if (IS_WALL(levl[x][y].typ) || levl[x][y].typ == SDOOR)
162 Strcat(buf, " embedded in a wall");
164 Strcat(buf, "
\81C
\95Ç
\82É
\96\84\82ß
\82±
\82Ü
\82ê
\82Ä
\82¢
\82é");
165 else if (closed_door(x, y))
167 Strcat(buf, " embedded in a door");
169 Strcat(buf, "
\81C
\94à
\82É
\96\84\82ß
\82±
\82Ü
\82ê
\82Ä
\82¢
\82é");
170 else if (is_pool(x, y))
171 /*JP 3.4.3
\82±
\82Ì
\95\94\95ª
\82Í
\95¨
\91Ì
\82É
\82µ
\82©
\8eg
\82í
\82ê
\82È
\82¢ */
173 Strcat(buf, " in water");
175 Strcat(buf, "
\81C
\90\85\92\86\82É
\82 \82é");
176 else if (is_lava(x, y))
178 Strcat(buf, " in molten lava"); /* [can this ever happen?] */
180 Strcat(buf, "
\81C
\97n
\8aâ
\82Ì
\92\86\82É
\82 \82é"); /* [can this ever happen?] */
186 look_at_monster(buf, monbuf, mtmp, x, y)
187 char *buf, *monbuf; /* buf: output, monbuf: optional output */
191 char *name, monnambuf[BUFSZ];
192 boolean accurate = !Hallucination;
194 if (mtmp->data == &mons[PM_COYOTE] && accurate)
195 name = coyotename(mtmp, monnambuf);
197 name = distant_monnam(mtmp, ARTICLE_NONE, monnambuf);
200 Sprintf(buf, "%s%s%s",
201 (mtmp->mx != x || mtmp->my != y)
202 ? ((mtmp->isshk && accurate) ? "tail of " : "tail of a ")
204 (mtmp->mtame && accurate)
206 : (mtmp->mpeaceful && accurate)
211 Sprintf(buf, "%s%s%s",
212 (mtmp->mtame && accurate) ? "
\8eè
\82È
\82¸
\82¯
\82ç
\82ê
\82½" :
213 (mtmp->mpeaceful && accurate) ? "
\97F
\8dD
\93I
\82È" : "",
215 (mtmp->mx != x || mtmp->my != y) ?
216 ((mtmp->isshk && accurate)
217 ? "
\82Ì
\90K
\94ö" : "
\82Ì
\90K
\94ö") : "");
219 if (u.ustuck == mtmp)
220 Strcat(buf, (Upolyd && sticks(youmonst.data))
222 ? ", being held" : ", holding you");
224 ? "
\81C
\82 \82È
\82½
\82ª
\92Í
\82Ü
\82¦
\82Ä
\82¢
\82é" : "
\81C
\82 \82È
\82½
\82ð
\92Í
\82Ü
\82¦
\82Ä
\82¢
\82é");
227 Strcat(buf, ", leashed to you");
229 Strcat(buf, "
\81C
\95R
\82Å
\8c\8b\82Î
\82ê
\82Ä
\82¢
\82é");
231 if (mtmp->mtrapped && cansee(mtmp->mx, mtmp->my)) {
232 struct trap *t = t_at(mtmp->mx, mtmp->my);
233 int tt = t ? t->ttyp : NO_TRAP;
235 /* newsym lets you know of the trap, so mention it here */
236 if (tt == BEAR_TRAP || tt == PIT || tt == SPIKED_PIT || tt == WEB)
238 Sprintf(eos(buf), ", trapped in %s",
240 Sprintf(eos(buf), ", %s
\82É
\95ß
\82Ü
\82Á
\82Ä
\82¢
\82é",
241 an(defsyms[trap_to_defsym(tt)].explanation));
245 unsigned how_seen = howmonseen(mtmp);
248 if (how_seen != 0 && how_seen != MONSEEN_NORMAL) {
249 if (how_seen & MONSEEN_NORMAL) {
251 Strcat(monbuf, "normal vision");
253 Strcat(monbuf, "
\92Ê
\8fí
\82Ì
\8e\8b\8ao");
254 how_seen &= ~MONSEEN_NORMAL;
255 /* how_seen can't be 0 yet... */
257 Strcat(monbuf, ", ");
259 if (how_seen & MONSEEN_SEEINVIS) {
261 Strcat(monbuf, "see invisible");
263 Strcat(monbuf, "
\8c©
\82¦
\82È
\82¢
\82à
\82Ì
\82ð
\8c©
\82é
\8e\8b\8ao");
264 how_seen &= ~MONSEEN_SEEINVIS;
266 Strcat(monbuf, ", ");
268 if (how_seen & MONSEEN_INFRAVIS) {
270 Strcat(monbuf, "infravision");
272 Strcat(monbuf, "
\90Ô
\8aO
\90ü
\82ª
\8c©
\82¦
\82é
\8e\8b\8ao");
273 how_seen &= ~MONSEEN_INFRAVIS;
275 Strcat(monbuf, ", ");
277 if (how_seen & MONSEEN_TELEPAT) {
279 Strcat(monbuf, "telepathy");
281 Strcat(monbuf, "
\83e
\83\8c\83p
\83V
\81[");
282 how_seen &= ~MONSEEN_TELEPAT;
284 Strcat(monbuf, ", ");
286 if (how_seen & MONSEEN_XRAYVIS) {
287 /* Eyes of the Overworld */
289 Strcat(monbuf, "astral vision");
291 Strcat(monbuf, "
\90¸
\90_
\82É
\82æ
\82é
\8e\8b\8ao");
292 how_seen &= ~MONSEEN_XRAYVIS;
294 Strcat(monbuf, ", ");
296 if (how_seen & MONSEEN_DETECT) {
298 Strcat(monbuf, "monster detection");
300 Strcat(monbuf, "
\89ö
\95¨
\82ð
\94
\8c©
\82·
\82é
\94\
\97Í");
301 how_seen &= ~MONSEEN_DETECT;
303 Strcat(monbuf, ", ");
305 if (how_seen & MONSEEN_WARNMON) {
308 Strcat(monbuf, "paranoid delusion");
310 Strcat(monbuf, "
\95Î
\8e·
\93I
\96Ï
\91z");
313 Sprintf(eos(monbuf), "warned of %s",
315 Sprintf(eos(monbuf), "%s
\82ð
\8cx
\8d\90\82µ
\82Ä
\82¢
\82é",
316 makeplural(mtmp->data->mname));
317 how_seen &= ~MONSEEN_WARNMON;
319 Strcat(monbuf, ", ");
321 /* should have used up all the how_seen bits by now */
323 impossible("lookat: unknown method of seeing monster");
324 Sprintf(eos(monbuf), "(%u)", how_seen);
326 } /* seen by something other than normal vision */
327 } /* monbuf is non-null */
331 * Return the name of the glyph found at (x,y).
332 * If not hallucinating and the glyph is a monster, also monster data.
334 STATIC_OVL struct permonst *
335 lookat(x, y, buf, monbuf)
339 struct monst *mtmp = (struct monst *) 0;
340 struct permonst *pm = (struct permonst *) 0;
343 buf[0] = monbuf[0] = '\0';
344 glyph = glyph_at(x, y);
345 if (u.ux == x && u.uy == y && canspotself()) {
347 (void) self_lookat(buf);
349 /* file lookup can't distinguish between "gnomish wizard" monster
350 and correspondingly named player character, always picking the
351 former; force it to find the general "wizard" entry instead */
352 if (Role_if(PM_WIZARD) && Race_if(PM_GNOME) && !Upolyd)
353 pm = &mons[PM_WIZARD];
355 /* When you see yourself normally, no explanation is appended
356 (even if you could also see yourself via other means).
357 Sensing self while blind or swallowed is treated as if it
358 were by normal vision (cf canseeself()). */
359 if ((Invisible || u.uundetected) && !Blind && !u.uswallow) {
371 eos(buf), " [seen: %s%s%s%s%s]",
372 (how & 1) ? "infravision" : "",
373 /* add comma if telep and infrav */
374 ((how & 3) > 2) ? ", " : "", (how & 2) ? "telepathy" : "",
375 /* add comma if detect and (infrav or telep or both) */
376 ((how & 7) > 4) ? ", " : "",
377 (how & 4) ? "monster detection" : "");
379 } else if (u.uswallow) {
380 /* all locations when swallowed other than the hero are the monster */
382 Sprintf(buf, "interior of %s",
383 Blind ? "a monster" : a_monnam(u.ustuck));
385 Sprintf(buf, "%s
\82Ì
\93à
\95\94",
386 Blind ? "
\89ö
\95¨" : a_monnam(u.ustuck));
389 } else if (glyph_is_monster(glyph)) {
392 if ((mtmp = m_at(x, y)) != 0) {
393 look_at_monster(buf, monbuf, mtmp, x, y);
396 } else if (glyph_is_object(glyph)) {
397 look_at_object(buf, x, y, glyph); /* fill in buf[] */
398 } else if (glyph_is_trap(glyph)) {
399 int tnum = what_trap(glyph_to_trap(glyph));
401 Strcpy(buf, defsyms[trap_to_defsym(tnum)].explanation);
402 } else if (!glyph_is_cmap(glyph)) {
404 Strcpy(buf, "unexplored area");
406 Strcpy(buf, "
\96¢
\92T
\8dõ
\82Ì
\8fê
\8f\8a");
408 switch (glyph_to_cmap(glyph)) {
411 Sprintf(buf, "%s %saltar",
413 Sprintf(buf, "%s%s
\8dÕ
\92d",
414 /* like endgame high priests, endgame high altars
415 are only recognizable when immediately adjacent */
416 (Is_astralevel(&u.uz) && distu(x, y) > 2)
422 Amask2align(levl[x][y].altarmask & ~AM_SHRINE)),
423 ((levl[x][y].altarmask & AM_SHRINE)
424 && (Is_astralevel(&u.uz) || Is_sanctum(&u.uz)))
432 if (is_drawbridge_wall(x, y) >= 0)
434 Strcpy(buf, "open drawbridge portcullis");
436 Strcpy(buf,"
\8aJ
\82¢
\82Ä
\82¢
\82é
\92µ
\82Ë
\8b´");
437 else if ((levl[x][y].doormask & ~D_TRAPPED) == D_BROKEN)
439 Strcpy(buf, "broken door");
441 Strcpy(buf,"
\89ó
\82ê
\82½
\94à");
444 Strcpy(buf, "doorway");
446 Strcpy(buf,"
\8fo
\93ü
\82è
\8cû");
451 Is_airlevel(&u.uz) ? "cloudy area" : "fog/vapor cloud");
453 Is_airlevel(&u.uz) ? "
\93Ü
\82Á
\82Ä
\82¢
\82é
\8fê
\8f\8a" : "
\96¶/
\8fö
\8bC
\82Ì
\89_");
456 if (!levl[x][y].seenv) {
458 Strcpy(buf, "unexplored");
460 Strcpy(buf, "
\96¢
\92T
\8dõ");
462 } else if (levl[x][y].typ == STONE || levl[x][y].typ == SCORR) {
464 Strcpy(buf, "stone");
471 Strcpy(buf, defsyms[glyph_to_cmap(glyph)].explanation);
475 return (pm && !Hallucination) ? pm : (struct permonst *) 0;
479 * Look in the "data" file for more info. Called if the user typed in the
480 * whole name (user_typed_name == TRUE), or we've found a possible match
481 * with a character/glyph and flags.help is TRUE.
483 * NOTE: when (user_typed_name == FALSE), inp is considered read-only and
484 * must not be changed directly, e.g. via lcase(). We want to force
485 * lcase() for data.base lookup so that we can have a clean key.
486 * Therefore, we create a copy of inp _just_ for data.base lookup.
489 checkfile(inp, pm, user_typed_name, without_asking)
492 boolean user_typed_name, without_asking;
495 char buf[BUFSZ], newstr[BUFSZ];
496 char *ep, *dbase_str;
497 unsigned long txt_offset = 0L;
499 boolean found_in_file = FALSE, skipping_entry = FALSE;
500 winid datawin = WIN_ERR;
502 fp = dlb_fopen(DATAFILE, "r");
505 pline("Cannot open data file!");
507 pline("
\83f
\81[
\83^
\83t
\83@
\83C
\83\8b\82ð
\8aJ
\82¯
\82È
\82¢
\81I");
513 * If someone passed us garbage, prevent fault.
515 if (!inp || (inp && strlen(inp) > (BUFSZ - 1))) {
516 pline("bad do_look buffer passed!");
520 /* To prevent the need for entries in data.base like *ngel to account
521 * for Angel and angel, make the lookup string the same for both
522 * user_typed_name and picked name.
524 if (pm != (struct permonst *) 0 && !user_typed_name)
525 dbase_str = strcpy(newstr, pm->mname);
527 dbase_str = strcpy(newstr, inp);
528 (void) lcase(dbase_str);
530 if (!strncmp(dbase_str, "interior of ", 12))
532 if (!strncmp(dbase_str, "a ", 2))
534 else if (!strncmp(dbase_str, "an ", 3))
536 else if (!strncmp(dbase_str, "the ", 4))
538 if (!strncmp(dbase_str, "tame ", 5))
540 else if (!strncmp(dbase_str, "peaceful ", 9))
542 if (!strncmp(dbase_str, "invisible ", 10))
544 if (!strncmp(dbase_str, "saddled ", 8))
546 if (!strncmp(dbase_str, "statue of ", 10))
548 else if (!strncmp(dbase_str, "figurine of ", 12))
551 /* Make sure the name is non-empty. */
553 /* adjust the input to remove "named " and convert to lower case */
554 char *alt = 0; /* alternate description */
556 if ((ep = strstri(dbase_str, " named ")) != 0)
559 ep = strstri(dbase_str, " called ");
561 ep = strstri(dbase_str, ", ");
562 if (ep && ep > dbase_str)
566 * If the object is named, then the name is the alternate description;
567 * otherwise, the result of makesingular() applied to the name is.
569 * isn't strictly optimal, but named objects of interest to the user
570 * will usually be found under their name, rather than under their
571 * object type, so looking for a singular form is pointless.
574 alt = makesingular(dbase_str);
576 /* skip first record; read second */
578 if (!dlb_fgets(buf, BUFSZ, fp) || !dlb_fgets(buf, BUFSZ, fp)) {
579 impossible("can't read 'data' file");
580 (void) dlb_fclose(fp);
582 } else if (sscanf(buf, "%8lx\n", &txt_offset) < 1 || txt_offset == 0L)
585 /* look for the appropriate entry */
586 while (dlb_fgets(buf, BUFSZ, fp)) {
588 break; /* we passed last entry without success */
591 /* a number indicates the end of current entry */
592 skipping_entry = FALSE;
593 } else if (!skipping_entry) {
594 if (!(ep = index(buf, '\n')))
597 /* if we match a key that begins with "~", skip this entry */
598 chk_skip = (*buf == '~') ? 1 : 0;
599 if (pmatch(&buf[chk_skip], dbase_str)
600 || (alt && pmatch(&buf[chk_skip], alt))) {
602 skipping_entry = TRUE;
605 found_in_file = TRUE;
618 /* skip over other possible matches for the info */
620 if (!dlb_fgets(buf, BUFSZ, fp))
622 } while (!digit(*buf));
623 if (sscanf(buf, "%ld,%d\n", &entry_offset, &entry_count) < 2) {
625 impossible("'data' file in wrong format or corrupted");
626 /* window will exist if we came here from below via 'goto' */
627 if (datawin != WIN_ERR)
628 destroy_nhwindow(datawin);
629 (void) dlb_fclose(fp);
634 if (user_typed_name || without_asking || yn("More info?") == 'y') {
636 if (user_typed_name || without_asking || yn("
\8fÚ
\8d×
\82ð
\8c©
\82é
\81H") == 'y') {
637 if (dlb_fseek(fp, (long) txt_offset + entry_offset, SEEK_SET)
640 pline("? Seek error on 'data' file!");
642 pline("'data'
\83t
\83@
\83C
\83\8b\82Ì
\83V
\81[
\83N
\83G
\83\89\81[
\81I");
643 (void) dlb_fclose(fp);
646 datawin = create_nhwindow(NHW_MENU);
647 for (i = 0; i < entry_count; i++) {
648 if (!dlb_fgets(buf, BUFSZ, fp))
650 if ((ep = index(buf, '\n')) != 0)
652 if (index(buf + 1, '\t') != 0)
653 (void) tabexpand(buf + 1);
654 putstr(datawin, 0, buf + 1);
656 display_nhwindow(datawin, FALSE);
657 destroy_nhwindow(datawin);
659 } else if (user_typed_name)
661 pline("I don't have any information on those things.");
663 pline("
\82»
\82ñ
\82È
\96¼
\91O
\82Í
\95·
\82¢
\82½
\82±
\82Æ
\82ª
\82È
\82¢
\81D");
665 (void) dlb_fclose(fp);
669 do_screen_description(cc, looked, sym, out_str, firstmatch)
674 const char **firstmatch;
676 boolean need_to_look = FALSE;
677 int glyph = NO_GLYPH;
678 static char look_buf[BUFSZ];
680 int found = 0; /* count of matching syms found */
682 int skipped_venom = 0;
686 static const char *mon_interior = "the interior of a monster";
688 static const char *mon_interior = "
\89ö
\95¨
\82Ì
\93à
\95\94";
694 glyph = glyph_at(cc.x, cc.y);
695 /* Convert glyph at selected position to a symbol for use below. */
696 (void) mapglyph(glyph, &sym, &oc, &os, cc.x, cc.y);
698 Sprintf(prefix, "%s ", encglyph(glyph));
700 Sprintf(prefix, "%c ", sym);
703 * Check all the possibilities, saving all explanations in a buffer.
704 * When all have been checked then the string is printed.
708 * Special case: if identifying from the screen, and we're swallowed,
709 * and looking at something other than our own symbol, then just say
710 * "the interior of a monster".
712 if (u.uswallow && looked
713 && (is_swallow_sym(sym) || (int) showsyms[S_stone] == sym)) {
715 Sprintf(out_str, "%s%s", prefix, mon_interior);
716 *firstmatch = mon_interior;
718 found += append_str(out_str, mon_interior);
724 /* Check for monsters */
725 for (i = 0; i < MAXMCLASSES; i++) {
726 if (sym == (looked ? showsyms[i + SYM_OFF_M] : def_monsyms[i].sym)
727 && def_monsyms[i].explain) {
730 Sprintf(out_str, "%s%s", prefix, an(def_monsyms[i].explain));
731 *firstmatch = def_monsyms[i].explain;
734 found += append_str(out_str, an(def_monsyms[i].explain));
738 /* handle '@' as a special case if it refers to you and you're
739 playing a character which isn't normally displayed by that
740 symbol; firstmatch is assumed to already be set for '@' */
741 if ((looked ? (sym == showsyms[S_HUMAN + SYM_OFF_M]
742 && cc.x == u.ux && cc.y == u.uy)
743 : (sym == def_monsyms[S_HUMAN].sym && !flags.showrace))
744 && !(Race_if(PM_HUMAN) || Race_if(PM_ELF)) && !Upolyd)
746 found += append_str(out_str, "you"); /* tack on "or you" */
748 found += append_str(out_str, "
\82 \82È
\82½"); /* tack on "or you" */
751 /* Now check for objects */
752 for (i = 1; i < MAXOCLASSES; i++) {
753 if (sym == (looked ? showsyms[i + SYM_OFF_O] : def_oc_syms[i].sym)) {
755 if (looked && i == VENOM_CLASS) {
760 Sprintf(out_str, "%s%s", prefix, an(def_oc_syms[i].explain));
761 *firstmatch = def_oc_syms[i].explain;
764 found += append_str(out_str, an(def_oc_syms[i].explain));
769 if (sym == DEF_INVISIBLE) {
771 Sprintf(out_str, "%s%s", prefix, an(invisexplain));
772 *firstmatch = invisexplain;
775 found += append_str(out_str, an(invisexplain));
779 #define is_cmap_trap(i) ((i) >= S_arrow_trap && (i) <= S_polymorph_trap)
780 #define is_cmap_drawbridge(i) ((i) >= S_vodbridge && (i) <= S_hcdbridge)
782 /* Now check for graphics symbols */
783 alt_i = (sym == (looked ? showsyms[0] : defsyms[0].sym)) ? 0 : (2 + 1);
784 for (hit_trap = FALSE, i = 0; i < MAXPCHARS; i++) {
785 /* when sym is the default background character, we process
786 i == 0 three times: unexplored, stone, dark part of a room */
788 x_str = !alt_i++ ? "unexplored" : "stone";
789 i = 0; /* for second iteration, undo loop increment */
790 /* alt_i is now 1 or 2 */
793 i = 0; /* undo loop increment */
794 x_str = defsyms[i].explanation;
795 /* alt_i is now 3 or more and no longer of interest */
797 if (sym == (looked ? showsyms[i] : defsyms[i].sym) && *x_str) {
798 #if 0 /*JP*//*
\93ú
\96{
\8cê
\82É
\82Í
\8aÖ
\8cW
\82È
\82¢*/
799 /* avoid "an unexplored", "an stone", "an air", "a water",
800 "a floor of a room", "a dark part of a room";
801 article==2 => "the", 1 => "an", 0 => (none) */
802 int article = strstri(x_str, " of a room") ? 2
804 || strcmp(x_str, "air") == 0
805 || strcmp(x_str, "water") == 0);
809 if (is_cmap_trap(i)) {
811 Sprintf(out_str, "%sa trap", prefix);
813 Sprintf(out_str, "%sã©", prefix);
817 Sprintf(out_str, "%s%s", prefix,
818 article == 2 ? the(x_str)
819 : article == 1 ? an(x_str) : x_str);
821 Sprintf(out_str, "%s%s", prefix, x_str);
826 } else if (!u.uswallow && !(hit_trap && is_cmap_trap(i))
827 && !(found >= 3 && is_cmap_drawbridge(i))
828 /* don't mention vibrating square outside of Gehennom
829 unless this happens to be one (hallucination?) */
830 && (i != S_vibrating_square || Inhell
831 || (looked && glyph_is_trap(glyph)
832 && glyph_to_trap(glyph) == VIBRATING_SQUARE))) {
834 found += append_str(out_str, (article == 2) ? the(x_str)
835 : (article == 1) ? an(x_str)
838 found += append_str(out_str, x_str);
844 if (i == S_altar || is_cmap_trap(i))
849 /* Now check for warning symbols */
850 for (i = 1; i < WARNCOUNT; i++) {
851 x_str = def_warnsyms[i].explanation;
852 if (sym == (looked ? warnsyms[i] : def_warnsyms[i].sym)) {
854 Sprintf(out_str, "%s%s", prefix, def_warnsyms[i].explanation);
855 *firstmatch = def_warnsyms[i].explanation;
858 found += append_str(out_str, def_warnsyms[i].explanation);
860 /* Kludge: warning trumps boulders on the display.
861 Reveal the boulder too or player can get confused */
862 if (looked && sobj_at(BOULDER, cc.x, cc.y))
863 Strcat(out_str, " co-located with a boulder");
864 break; /* out of for loop*/
868 /* if we ignored venom and list turned out to be short, put it back */
869 if (skipped_venom && found < 2) {
870 x_str = def_oc_syms[VENOM_CLASS].explain;
872 Sprintf(out_str, "%s%s", prefix, an(x_str));
876 found += append_str(out_str, an(x_str));
880 /* handle optional boulder symbol as a special case */
881 if (iflags.bouldersym && sym == iflags.bouldersym) {
884 *firstmatch = "boulder";
886 *firstmatch = "
\8aâ";
887 Sprintf(out_str, "%s%s", prefix, an(*firstmatch));
891 found += append_str(out_str, "boulder");
893 found += append_str(out_str, "
\8aâ");
898 * If we are looking at the screen, follow multiple possibilities or
899 * an ambiguous explanation by something more detailed.
903 if (found > 1 || need_to_look) {
905 char temp_buf[BUFSZ];
907 (void) lookat(cc.x, cc.y, look_buf, monbuf);
908 *firstmatch = look_buf;
909 if (*(*firstmatch)) {
910 Sprintf(temp_buf, " (%s)", *firstmatch);
911 (void) strncat(out_str, temp_buf,
912 BUFSZ - strlen(out_str) - 1);
913 found = 1; /* we have something to look up */
916 Sprintf(temp_buf, " [seen: %s]", monbuf);
917 (void) strncat(out_str, temp_buf,
918 BUFSZ - strlen(out_str) - 1);
926 /* getpos() return values */
927 #define LOOK_TRADITIONAL 0 /* '.' -- ask about "more info?" */
928 #define LOOK_QUICK 1 /* ',' -- skip "more info?" */
929 #define LOOK_ONCE 2 /* ';' -- skip and stop looping */
930 #define LOOK_VERBOSE 3 /* ':' -- show more info w/o asking */
932 /* also used by getpos hack in do_name.c */
934 const char what_is_an_unknown_object[] = "an unknown object";
936 const char what_is_an_unknown_object[] = "
\93ä
\82Ì
\95¨
\91Ì";
939 do_look(mode, click_cc)
943 boolean quick = (mode == 1); /* use cursor; don't search for "more info" */
944 boolean clicklook = (mode == 2); /* right mouse-click method */
946 const char *firstmatch = 0;
947 struct permonst *pm = 0;
948 int i = '\0', ans = 0;
949 int sym; /* typed symbol or converted glyph */
950 int found; /* count of matching syms found */
951 coord cc; /* screen pos of unknown glyph */
952 boolean save_verbose; /* saved value of flags.verbose */
953 boolean from_screen; /* question from the screen */
957 from_screen = TRUE; /* yes, we want to use the cursor */
962 menu_item *pick_list = (menu_item *) 0;
967 win = create_nhwindow(NHW_MENU);
970 /* 'y' and 'n' to keep backwards compatibility with previous
971 versions: "Specify unknown object by cursor?" */
972 add_menu(win, NO_GLYPH, &any,
973 flags.lootabc ? 0 : any.a_char, 'y', ATR_NONE,
974 "something on the map", MENU_UNSELECTED);
976 add_menu(win, NO_GLYPH, &any,
977 flags.lootabc ? 0 : any.a_char, 0, ATR_NONE,
978 "something you're carrying", MENU_UNSELECTED);
980 add_menu(win, NO_GLYPH, &any,
981 flags.lootabc ? 0 : any.a_char, 'n', ATR_NONE,
982 "something else (by symbol or name)", MENU_UNSELECTED);
983 if (!u.uswallow && !Hallucination) {
985 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
986 "", MENU_UNSELECTED);
987 /* these options work sensibly for the swallowed case,
988 but there's no reason for the player to use them then;
989 objects work fine when hallucinating, but screen
990 symbol/monster class letter doesn't match up with
991 bogus monster type, so suppress when hallucinating */
993 add_menu(win, NO_GLYPH, &any,
994 flags.lootabc ? 0 : any.a_char, 0, ATR_NONE,
995 "nearby monsters", MENU_UNSELECTED);
997 add_menu(win, NO_GLYPH, &any,
998 flags.lootabc ? 0 : any.a_char, 0, ATR_NONE,
999 "all monsters shown on map", MENU_UNSELECTED);
1001 add_menu(win, NO_GLYPH, &any,
1002 flags.lootabc ? 0 : any.a_char, 0, ATR_NONE,
1003 "nearby objects", MENU_UNSELECTED);
1005 add_menu(win, NO_GLYPH, &any,
1006 flags.lootabc ? 0 : any.a_char, 0, ATR_NONE,
1007 "all objects shown on map", MENU_UNSELECTED);
1009 end_menu(win, "What do you want to look at:");
1010 if (select_menu(win, PICK_ONE, &pick_list) > 0) {
1011 i = pick_list->item.a_char;
1012 free((genericptr_t) pick_list);
1014 destroy_nhwindow(win);
1033 invlet = display_inventory((const char *) 0, TRUE);
1034 if (!invlet || invlet == '\033')
1037 for (invobj = invent; invobj; invobj = invobj->nobj)
1038 if (invobj->invlet == invlet) {
1039 strcpy(out_str, singular(invobj, xname));
1043 checkfile(out_str, pm, TRUE, TRUE);
1047 from_screen = FALSE;
1049 getlin("Specify what? (type the word)", out_str);
1051 getlin("
\89½
\82ð
\92²
\82×
\82é
\81H(
\95¶
\8e\9a\82ð
\93ü
\82ê
\82Ä
\82Ë)", out_str);
1052 if (out_str[0] == '\0' || out_str[0] == '\033')
1055 if (out_str[1]) { /* user typed in a complete string */
1056 checkfile(out_str, pm, TRUE, TRUE);
1062 look_all(TRUE, TRUE); /* list nearby monsters */
1065 look_all(FALSE, TRUE); /* list all monsters */
1068 look_all(TRUE, FALSE); /* list nearby objects */
1071 look_all(FALSE, FALSE); /* list all objects */
1074 } else { /* clicklook */
1078 from_screen = FALSE;
1081 /* Save the verbose flag, we change it later. */
1082 save_verbose = flags.verbose;
1083 flags.verbose = flags.verbose && !quick;
1085 * The user typed one letter, or we're identifying from the screen.
1088 /* Reset some variables. */
1089 pm = (struct permonst *) 0;
1093 if (from_screen || clicklook) {
1097 pline("Please move the cursor to %s.",
1098 what_is_an_unknown_object);
1100 pline("
\83J
\81[
\83\
\83\8b\82ð
\95¨
\91Ì
\82É
\88Ú
\93®
\82µ
\82Ä
\82
\82¾
\82³
\82¢
\81D");
1104 pline("Pick an object.");
1106 pline("
\95¨
\91Ì
\82ð
\8ew
\92è
\82µ
\82Ä
\82
\82¾
\82³
\82¢
\81D");
1108 ans = getpos(&cc, quick, what_is_an_unknown_object);
1109 if (ans < 0 || cc.x < 0) {
1110 flags.verbose = save_verbose;
1111 return 0; /* done */
1113 flags.verbose = FALSE; /* only print long question once */
1117 found = do_screen_description(cc, (from_screen || clicklook), sym,
1118 out_str, &firstmatch);
1120 /* Finally, print out our explanation. */
1122 /* Used putmixed() because there may be an encoded glyph present
1124 putmixed(WIN_MESSAGE, 0, out_str);
1126 /* check the data file for information about this thing */
1127 if (found == 1 && ans != LOOK_QUICK && ans != LOOK_ONCE
1128 && (ans == LOOK_VERBOSE || (flags.help && !quick))
1130 char temp_buf[BUFSZ];
1132 Strcpy(temp_buf, firstmatch);
1133 checkfile(temp_buf, pm, FALSE,
1134 (boolean) (ans == LOOK_VERBOSE));
1138 pline("I've never heard of such things.");
1140 pline("
\82»
\82ñ
\82È
\96¼
\91O
\82Í
\95·
\82¢
\82½
\82±
\82Æ
\82ª
\82È
\82¢
\81D");
1143 } while (from_screen && !quick && ans != LOOK_ONCE && !clicklook);
1145 flags.verbose = save_verbose;
1150 look_all(nearby, do_mons)
1151 boolean nearby; /* True => within BOLTLIM, False => entire map */
1152 boolean do_mons; /* True => monsters, False => objects */
1155 int x, y, lo_x, lo_y, hi_x, hi_y, glyph, count = 0;
1156 char buf[BUFSZ], outbuf[BUFSZ], coordbuf[12], fmt[12]; /* "%02d,%02d" */
1158 /* row,column orientation rather than cartesian x,y */
1159 Sprintf(fmt, "%%%sd,%%%sd",
1160 (ROWNO <= 100) ? "02" : (ROWNO <= 1000) ? "03" : "",
1161 (COLNO <= 100) ? "02" : (COLNO <= 1000) ? "03" : "");
1163 win = create_nhwindow(NHW_TEXT);
1164 lo_y = nearby ? max(u.uy - BOLT_LIM, 0) : 0;
1165 lo_x = nearby ? max(u.ux - BOLT_LIM, 1) : 1;
1166 hi_y = nearby ? min(u.uy + BOLT_LIM, ROWNO - 1) : ROWNO - 1;
1167 hi_x = nearby ? min(u.ux + BOLT_LIM, COLNO - 1) : COLNO - 1;
1168 for (y = lo_y; y <= hi_y; y++) {
1169 for (x = lo_x; x <= hi_x; x++) {
1171 glyph = glyph_at(x, y);
1173 if (glyph_is_monster(glyph)) {
1176 bhitpos.x = x; /* [is this actually necessary?] */
1178 if (x == u.ux && y == u.uy && canspotself()) {
1179 (void) self_lookat(buf);
1181 } else if ((mtmp = m_at(x, y)) != 0) {
1182 look_at_monster(buf, (char *) 0, mtmp, x, y);
1185 } else if (glyph_is_invisible(glyph)) {
1186 /* remembered, unseen, creature */
1187 Strcpy(buf, invisexplain);
1189 } else if (glyph_is_warning(glyph)) {
1190 int warnindx = glyph_to_warning(glyph);
1192 Strcpy(buf, def_warnsyms[warnindx].explanation);
1195 } else { /* !do_mons */
1196 if (glyph_is_object(glyph)) {
1197 look_at_object(buf, x, y, glyph);
1205 Strcpy(which, do_mons ? "monsters" : "objects");
1207 Sprintf(coordbuf, fmt, u.uy, u.ux);
1208 Sprintf(outbuf, "%s currently shown near %s:",
1209 upstart(which), coordbuf);
1211 Sprintf(outbuf, "All %s currently shown on the map:",
1213 putstr(win, 0, outbuf);
1216 Sprintf(coordbuf, fmt, y, x);
1217 /* prefix: "C row,column " */
1218 Sprintf(outbuf, "%s %s ", encglyph(glyph), coordbuf);
1219 /* guard against potential overflow */
1220 buf[sizeof buf - 1 - strlen(outbuf)] = '\0';
1221 Strcat(outbuf, buf);
1222 putmixed(win, 0, outbuf);
1227 display_nhwindow(win, TRUE);
1229 pline("No %s are currently shown %s.",
1230 do_mons ? "monsters" : "objects",
1231 nearby ? "nearby" : "on the map");
1232 destroy_nhwindow(win);
1235 /* the '/' command */
1239 return do_look(0, (coord *) 0);
1242 /* the ';' command */
1246 return do_look(1, (coord *) 0);
1249 /* the '^' command */
1253 register struct trap *trap;
1260 for (trap = ftrap; trap; trap = trap->ntrap)
1261 if (trap->tx == x && trap->ty == y) {
1266 if (u.dz < 0 ? (tt == TRAPDOOR || tt == HOLE)
1272 pline("That is %s%s%s.",
1273 an(defsyms[trap_to_defsym(tt)].explanation),
1278 /* trap doors & spiked pits can't be made by
1279 player, and should be considered at least
1280 as much "set" as "dug" anyway */
1281 : (tt == HOLE || tt == PIT)
1284 !trap->madeby_u ? "" : " by you");
1286 pline("
\82»
\82ê
\82Í%s%s
\82¾
\81D",
1290 ? "
\82 \82È
\82½
\82ª
\92£
\82Á
\82½"
1291 : (tt == HOLE || tt == PIT)
1292 ? "
\82 \82È
\82½
\82ª
\8c@
\82Á
\82½"
1293 : "
\82 \82È
\82½
\82ª
\8ed
\8a|
\82¯
\82½",
1294 defsyms[trap_to_defsym(tt)].explanation);
1299 pline("I can't see a trap there.");
1301 pline("
\82»
\82±
\82É
\82Íã©
\82Í
\82Ý
\82 \82½
\82ç
\82È
\82¢
\81D");
1306 dowhatdoes_core(q, cbuf)
1312 register char *buf = &bufr[6], *ep, ctrl, meta;
1314 fp = dlb_fopen(CMDHELPFILE, "r");
1317 pline("Cannot open data file!");
1319 pline("
\83f
\81[
\83^
\83t
\83@
\83C
\83\8b\82ð
\8aJ
\82¯
\82È
\82¢
\81I");
1324 ctrl = ((q <= '\033') ? (q - 1 + 'A') : 0);
1325 meta = ((0x80 & q) ? (0x7f & q) : 0);
1326 while (dlb_fgets(buf, BUFSZ - 6, fp)) {
1327 if ((ctrl && *buf == '^' && *(buf + 1) == ctrl)
1328 || (meta && *buf == 'M' && *(buf + 1) == '-'
1329 && *(buf + 2) == meta) || *buf == q) {
1330 ep = index(buf, '\n');
1333 if (ctrl && buf[2] == '\t') {
1335 (void) strncpy(buf, "^? ", 8);
1337 } else if (meta && buf[3] == '\t') {
1339 (void) strncpy(buf, "M-? ", 8);
1341 } else if (buf[1] == '\t') {
1344 (void) strncpy(buf + 1, " ", 7);
1346 (void) dlb_fclose(fp);
1351 (void) dlb_fclose(fp);
1361 #if defined(UNIX) || defined(VMS)
1365 q = yn_function("What command?", (char *) 0, '\0');
1367 q = yn_function("
\82Ç
\82¤
\82¢
\82¤
\83R
\83}
\83\93\83h
\81H", (char *) 0, '\0');
1368 #if defined(UNIX) || defined(VMS)
1371 reslt = dowhatdoes_core(q, bufr);
1376 pline("I've never heard of such commands.");
1378 pline("
\82»
\82ñ
\82È
\83R
\83}
\83\93\83h
\82Í
\95·
\82¢
\82½
\82±
\82Æ
\82ª
\82È
\82¢
\81D");
1385 winid cwin = create_nhwindow(NHW_TEXT);
1388 if (sysopt.support) {
1389 /*XXX overflow possibilities*/
1390 Sprintf(buf, "To contact local support, %s", sysopt.support);
1391 putstr(cwin, 0, buf);
1392 putstr(cwin, 0, "");
1393 } else if (sysopt.fmtd_wizard_list) { /* formatted SYSCF WIZARDS */
1394 Sprintf(buf, "To contact local support, contact %s.",
1395 sysopt.fmtd_wizard_list);
1396 putstr(cwin, 0, buf);
1397 putstr(cwin, 0, "");
1399 putstr(cwin, 0, "To contact the NetHack development team directly,");
1400 /*XXX overflow possibilities*/
1401 Sprintf(buf, "see the 'Contact' form on our website or email <%s>.",
1403 putstr(cwin, 0, buf);
1404 putstr(cwin, 0, "");
1405 putstr(cwin, 0, "For more information on NetHack, or to report a bug,");
1406 Sprintf(buf, "visit our website \"%s\".", DEVTEAM_URL);
1407 putstr(cwin, 0, buf);
1408 display_nhwindow(cwin, FALSE);
1409 destroy_nhwindow(cwin);
1412 /* data for help_menu() */
1413 static const char *help_menu_items[] = {
1415 /* 0*/ "About NetHack (version information).",
1416 /* 1*/ "Long description of the game and commands.",
1417 /* 2*/ "List of game commands.",
1418 /* 3*/ "Concise history of NetHack.",
1419 /* 4*/ "Info on a character in the game display.",
1420 /* 5*/ "Info on what a given key does.",
1421 /* 6*/ "List of game options.",
1422 /* 7*/ "Longer explanation of game options.",
1423 /* 8*/ "List of extended commands.",
1424 /* 9*/ "The NetHack license.",
1425 /* 10*/ "Support information.",
1427 "%s-specific help and commands.",
1428 #define PORT_HELP_ID 100
1429 #define WIZHLP_SLOT 12
1431 #define WIZHLP_SLOT 11
1433 "List of wizard-mode commands.", "", (char *) 0
1435 /* 0*/ "NetHack
\82É
\82Â
\82¢
\82Ä(
\83o
\81[
\83W
\83\87\83\93\8fî
\95ñ)",
1436 /* 1*/ "
\83Q
\81[
\83\80\82¨
\82æ
\82Ñ
\83R
\83}
\83\93\83h
\82Ì
\89ð
\90à(
\92·
\95¶)",
1437 /* 2*/ "
\83R
\83}
\83\93\83h
\88ê
\97\97",
1438 /* 3*/ "NetHack
\82Ì
\8aÈ
\92P
\82È
\97ð
\8ej",
1439 /* 4*/ "
\89æ
\96Ê
\82É
\95\
\8e¦
\82³
\82ê
\82é
\95¶
\8e\9a\82Ì
\90à
\96¾",
1440 /* 5*/ "
\82±
\82Ì
\83L
\81[
\82ª
\89½
\82ð
\88Ó
\96¡
\82·
\82é
\82©
\82Ì
\90à
\96¾",
1441 /* 6*/ "
\83Q
\81[
\83\80\82Ì
\83I
\83v
\83V
\83\87\83\93\88ê
\97\97",
1442 /* 7*/ "
\83Q
\81[
\83\80\82Ì
\83I
\83v
\83V
\83\87\83\93\88ê
\97\97(
\92·
\95¶)",
1443 /* 8*/ "
\8ag
\92£
\83R
\83}
\83\93\83h
\88ê
\97\97",
1444 /* 9*/ "NetHack
\82Ì
\83\89\83C
\83Z
\83\93\83X",
1445 /* 10*/ "
\83T
\83|
\81[
\83g
\8fî
\95ñ",
1447 "%s
\82É
\93Á
\97L
\82Ì
\83w
\83\8b\83v
\82¨
\82æ
\82Ñ
\83R
\83}
\83\93\83h",
1448 #define PORT_HELP_ID 100
1449 #define WIZHLP_SLOT 12
1451 #define WIZHLP_SLOT 11
1453 "
\83E
\83B
\83U
\81[
\83h
\83\82\81[
\83h
\82Ì
\83R
\83}
\83\93\83h
\88ê
\97\97", "", (char *) 0
1461 winid tmpwin = create_nhwindow(NHW_MENU);
1463 char helpbuf[QBUFSZ];
1466 menu_item *selected;
1469 any = zeroany; /* zero all bits */
1472 help_menu_items[WIZHLP_SLOT] = "",
1473 help_menu_items[WIZHLP_SLOT + 1] = (char *) 0;
1474 for (i = 0; help_menu_items[i]; i++)
1476 /* port-specific line has a %s in it for the PORT_ID */
1477 if (help_menu_items[i][0] == '%') {
1478 Sprintf(helpbuf, help_menu_items[i], PORT_ID);
1479 any.a_int = PORT_HELP_ID + 1;
1480 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, helpbuf,
1485 any.a_int = (*help_menu_items[i]) ? i + 1 : 0;
1486 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1487 help_menu_items[i], MENU_UNSELECTED);
1490 end_menu(tmpwin, "Select one item:");
1492 end_menu(tmpwin, "
\91I
\82ñ
\82Å
\82
\82¾
\82³
\82¢
\81F");
1493 n = select_menu(tmpwin, PICK_ONE, &selected);
1494 destroy_nhwindow(tmpwin);
1496 *sel = selected[0].item.a_int - 1;
1497 free((genericptr_t) selected);
1503 /* the '?' command */
1509 if (help_menu(&sel)) {
1512 (void) doextversion();
1515 display_file(HELP, TRUE);
1518 display_file(SHELP, TRUE);
1527 (void) dowhatdoes();
1533 display_file(OPTIONFILE, TRUE);
1539 display_file(LICENSE, TRUE);
1550 /* handle slot 11 or 12 */
1551 display_file(DEBUGHELP, TRUE);
1558 /* the 'V' command; also a choice for '?' */
1562 display_file(HISTORY, TRUE);