]> granicus.if.org Git - nethack/commitdiff
'/' enhancement
authorPatR <rankin@nethack.org>
Tue, 7 Jul 2015 02:07:26 +0000 (19:07 -0700)
committerPatR <rankin@nethack.org>
Tue, 7 Jul 2015 02:07:26 +0000 (19:07 -0700)
Add new entries to the menu used for the '/' command:  describe nearby
monsters, describe all shown monsters, describe nearby objects, and
describe all shown objects.  It makes a text window listing monsters--
or objects--currently displayed on the map, showing lines of
X  r,c  monster-or-object description
where 'X' is symbol (monster or object class letter for tty), 'r,c' is
row and column separated by comma, and description is similar to what
using '/y' or ';' manually would provide (how-seen info is omitted
when listing monsters).

Originally intended for blind players using screen readers to describe
what is displayed, but will probably get used by other players too.

The map doesn't use a separate set of glyphs for objects which are the
tops of piles, so the information that there are additional objects
beneath the ones shown isn't available to '/' and ';'.  That feels like
a bug to me....

src/pager.c

index cd58783ac0459dd0590245b47cee0aa2ff23e8a1..a32ae3ef73331661830eef57b4d53e1493f5d133 100644 (file)
@@ -1,4 +1,4 @@
-/* NetHack 3.6 pager.c $NHDT-Date: 1433572586 2015/06/06 06:36:26 $  $NHDT-Branch: master $:$NHDT-Revision: 1.77 $ */
+/* NetHack 3.6 pager.c $NHDT-Date: 1436234837 2015/07/07 02:07:17 $  $NHDT-Branch: master $:$NHDT-Revision: 1.78 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
 
 STATIC_DCL boolean FDECL(is_swallow_sym, (int));
 STATIC_DCL int FDECL(append_str, (char *, const char *));
+STATIC_DCL void FDECL(look_at_object, (char *, int, int, int));
+STATIC_DCL void FDECL(look_at_monster, (char *, char *,
+                                        struct monst *, int, int));
 STATIC_DCL struct permonst *FDECL(lookat, (int, int, char *, char *));
-STATIC_DCL void FDECL(checkfile,
-                      (char *, struct permonst *, BOOLEAN_P, BOOLEAN_P));
+STATIC_DCL void FDECL(checkfile, (char *, struct permonst *,
+                                  BOOLEAN_P, BOOLEAN_P));
+STATIC_DCL void FDECL(look_all, (BOOLEAN_P,BOOLEAN_P));
 STATIC_DCL boolean FDECL(help_menu, (int *));
 #ifdef PORT_HELP
 extern void NDECL(port_help);
@@ -112,6 +116,138 @@ struct obj **obj_p;
     return fakeobj; /* when True, caller needs to dealloc *obj_p */
 }
 
+STATIC_OVL void
+look_at_object(buf, x, y, glyph)
+char *buf; /* output buffer */
+int x, y, glyph;
+{
+    struct obj *otmp = 0;
+    boolean fakeobj = object_from_map(glyph, x, y, &otmp);
+
+    if (otmp) {
+        Strcpy(buf, (otmp->otyp != STRANGE_OBJECT)
+                     ? distant_name(otmp, xname)
+                     : obj_descr[STRANGE_OBJECT].oc_name);
+        if (fakeobj)
+            dealloc_obj(otmp), otmp = 0;
+    } else
+        Strcpy(buf, something); /* sanity precaution */
+
+    if (levl[x][y].typ == STONE || levl[x][y].typ == SCORR)
+        Strcat(buf, " embedded in stone");
+    else if (IS_WALL(levl[x][y].typ) || levl[x][y].typ == SDOOR)
+        Strcat(buf, " embedded in a wall");
+    else if (closed_door(x, y))
+        Strcat(buf, " embedded in a door");
+    else if (is_pool(x, y))
+        Strcat(buf, " in water");
+    else if (is_lava(x, y))
+        Strcat(buf, " in molten lava"); /* [can this ever happen?] */
+    return;
+}
+
+STATIC_OVL void
+look_at_monster(buf, monbuf, mtmp, x, y)
+char *buf, *monbuf; /* buf: output, monbuf: optional output */
+struct monst *mtmp;
+int x, y;
+{
+    char *name, monnambuf[BUFSZ];
+    boolean accurate = !Hallucination;
+
+    if (mtmp->data == &mons[PM_COYOTE] && accurate)
+        name = coyotename(mtmp, monnambuf);
+    else
+        name = distant_monnam(mtmp, ARTICLE_NONE, monnambuf);
+
+    Sprintf(buf, "%s%s%s",
+            (mtmp->mx != x || mtmp->my != y)
+                ? ((mtmp->isshk && accurate) ? "tail of " : "tail of a ")
+                : "",
+            (mtmp->mtame && accurate)
+                ? "tame "
+                : (mtmp->mpeaceful && accurate)
+                    ? "peaceful "
+                    : "",
+            name);
+    if (u.ustuck == mtmp)
+        Strcat(buf, (Upolyd && sticks(youmonst.data))
+                     ? ", being held" : ", holding you");
+    if (mtmp->mleashed)
+        Strcat(buf, ", leashed to you");
+
+    if (mtmp->mtrapped && cansee(mtmp->mx, mtmp->my)) {
+        struct trap *t = t_at(mtmp->mx, mtmp->my);
+        int tt = t ? t->ttyp : NO_TRAP;
+
+        /* newsym lets you know of the trap, so mention it here */
+        if (tt == BEAR_TRAP || tt == PIT || tt == SPIKED_PIT || tt == WEB)
+            Sprintf(eos(buf), ", trapped in %s",
+                    an(defsyms[trap_to_defsym(tt)].explanation));
+    }
+
+    if (monbuf) {
+        unsigned how_seen = howmonseen(mtmp);
+
+        monbuf[0] = '\0';
+        if (how_seen != 0 && how_seen != MONSEEN_NORMAL) {
+            if (how_seen & MONSEEN_NORMAL) {
+                Strcat(monbuf, "normal vision");
+                how_seen &= ~MONSEEN_NORMAL;
+                /* how_seen can't be 0 yet... */
+                if (how_seen)
+                    Strcat(monbuf, ", ");
+            }
+            if (how_seen & MONSEEN_SEEINVIS) {
+                Strcat(monbuf, "see invisible");
+                how_seen &= ~MONSEEN_SEEINVIS;
+                if (how_seen)
+                    Strcat(monbuf, ", ");
+            }
+            if (how_seen & MONSEEN_INFRAVIS) {
+                Strcat(monbuf, "infravision");
+                how_seen &= ~MONSEEN_INFRAVIS;
+                if (how_seen)
+                    Strcat(monbuf, ", ");
+            }
+            if (how_seen & MONSEEN_TELEPAT) {
+                Strcat(monbuf, "telepathy");
+                how_seen &= ~MONSEEN_TELEPAT;
+                if (how_seen)
+                    Strcat(monbuf, ", ");
+            }
+            if (how_seen & MONSEEN_XRAYVIS) {
+                /* Eyes of the Overworld */
+                Strcat(monbuf, "astral vision");
+                how_seen &= ~MONSEEN_XRAYVIS;
+                if (how_seen)
+                    Strcat(monbuf, ", ");
+            }
+            if (how_seen & MONSEEN_DETECT) {
+                Strcat(monbuf, "monster detection");
+                how_seen &= ~MONSEEN_DETECT;
+                if (how_seen)
+                    Strcat(monbuf, ", ");
+            }
+            if (how_seen & MONSEEN_WARNMON) {
+                if (Hallucination)
+                    Strcat(monbuf, "paranoid delusion");
+                else
+                    Sprintf(eos(monbuf), "warned of %s",
+                            makeplural(mtmp->data->mname));
+                how_seen &= ~MONSEEN_WARNMON;
+                if (how_seen)
+                    Strcat(monbuf, ", ");
+            }
+            /* should have used up all the how_seen bits by now */
+            if (how_seen) {
+                impossible("lookat: unknown method of seeing monster");
+                Sprintf(eos(monbuf), "(%u)", how_seen);
+            }
+        } /* seen by something other than normal vision */
+    } /* monbuf is non-null */
+}
+
 /*
  * Return the name of the glyph found at (x,y).
  * If not hallucinating and the glyph is a monster, also monster data.
@@ -125,7 +261,7 @@ char *buf, *monbuf;
     struct permonst *pm = (struct permonst *) 0;
     int glyph;
 
-    buf[0] = monbuf[0] = 0;
+    buf[0] = monbuf[0] = '\0';
     glyph = glyph_at(x, y);
     if (u.ux == x && u.uy == y && canspotself()) {
         /* fill in buf[] */
@@ -169,125 +305,12 @@ char *buf, *monbuf;
     } else if (glyph_is_monster(glyph)) {
         bhitpos.x = x;
         bhitpos.y = y;
-        mtmp = m_at(x, y);
-        if (mtmp) {
-            char *name, monnambuf[BUFSZ];
-            unsigned how_seen;
-            boolean accurate = !Hallucination;
-
-            if (mtmp->data == &mons[PM_COYOTE] && accurate)
-                name = coyotename(mtmp, monnambuf);
-            else
-                name = distant_monnam(mtmp, ARTICLE_NONE, monnambuf);
-
+        if ((mtmp = m_at(x, y)) != 0) {
+            look_at_monster(buf, monbuf, mtmp, x, y);
             pm = mtmp->data;
-            Sprintf(
-                buf, "%s%s%s",
-                (mtmp->mx != x || mtmp->my != y)
-                    ? ((mtmp->isshk && accurate) ? "tail of " : "tail of a ")
-                    : "",
-                (mtmp->mtame && accurate)
-                    ? "tame "
-                    : (mtmp->mpeaceful && accurate) ? "peaceful " : "",
-                name);
-            if (u.ustuck == mtmp)
-                Strcat(buf, (Upolyd && sticks(youmonst.data))
-                                ? ", being held"
-                                : ", holding you");
-            if (mtmp->mleashed)
-                Strcat(buf, ", leashed to you");
-
-            if (mtmp->mtrapped && cansee(mtmp->mx, mtmp->my)) {
-                struct trap *t = t_at(mtmp->mx, mtmp->my);
-                int tt = t ? t->ttyp : NO_TRAP;
-
-                /* newsym lets you know of the trap, so mention it here */
-                if (tt == BEAR_TRAP || tt == PIT || tt == SPIKED_PIT
-                    || tt == WEB)
-                    Sprintf(eos(buf), ", trapped in %s",
-                            an(defsyms[trap_to_defsym(tt)].explanation));
-            }
-
-            how_seen = howmonseen(mtmp);
-            if (how_seen && how_seen != MONSEEN_NORMAL) {
-                if (how_seen & MONSEEN_NORMAL) {
-                    Strcat(monbuf, "normal vision");
-                    how_seen &= ~MONSEEN_NORMAL;
-                    /* how_seen can't be 0 yet... */
-                    if (how_seen)
-                        Strcat(monbuf, ", ");
-                }
-                if (how_seen & MONSEEN_SEEINVIS) {
-                    Strcat(monbuf, "see invisible");
-                    how_seen &= ~MONSEEN_SEEINVIS;
-                    if (how_seen)
-                        Strcat(monbuf, ", ");
-                }
-                if (how_seen & MONSEEN_INFRAVIS) {
-                    Strcat(monbuf, "infravision");
-                    how_seen &= ~MONSEEN_INFRAVIS;
-                    if (how_seen)
-                        Strcat(monbuf, ", ");
-                }
-                if (how_seen & MONSEEN_TELEPAT) {
-                    Strcat(monbuf, "telepathy");
-                    how_seen &= ~MONSEEN_TELEPAT;
-                    if (how_seen)
-                        Strcat(monbuf, ", ");
-                }
-                if (how_seen & MONSEEN_XRAYVIS) {
-                    /* Eyes of the Overworld */
-                    Strcat(monbuf, "astral vision");
-                    how_seen &= ~MONSEEN_XRAYVIS;
-                    if (how_seen)
-                        Strcat(monbuf, ", ");
-                }
-                if (how_seen & MONSEEN_DETECT) {
-                    Strcat(monbuf, "monster detection");
-                    how_seen &= ~MONSEEN_DETECT;
-                    if (how_seen)
-                        Strcat(monbuf, ", ");
-                }
-                if (how_seen & MONSEEN_WARNMON) {
-                    if (Hallucination)
-                        Strcat(monbuf, "paranoid delusion");
-                    else
-                        Sprintf(eos(monbuf), "warned of %s",
-                                makeplural(mtmp->data->mname));
-                    how_seen &= ~MONSEEN_WARNMON;
-                    if (how_seen)
-                        Strcat(monbuf, ", ");
-                }
-                /* should have used up all the how_seen bits by now */
-                if (how_seen) {
-                    impossible("lookat: unknown method of seeing monster");
-                    Sprintf(eos(monbuf), "(%u)", how_seen);
-                }
-            } /* seen by something other than normal vision */
-        }     /* mtmp */
-
-    } else if (glyph_is_object(glyph)) {
-        struct obj *otmp = 0;
-        boolean fakeobj = object_from_map(glyph, x, y, &otmp);
-
-        if (otmp) {
-            Strcpy(buf, (otmp->otyp != STRANGE_OBJECT)
-                         ? distant_name(otmp, xname)
-                         : obj_descr[STRANGE_OBJECT].oc_name);
-            if (fakeobj)
-                dealloc_obj(otmp), otmp = 0;
         }
-
-        if (levl[x][y].typ == STONE || levl[x][y].typ == SCORR)
-            Strcat(buf, " embedded in stone");
-        else if (IS_WALL(levl[x][y].typ) || levl[x][y].typ == SDOOR)
-            Strcat(buf, " embedded in a wall");
-        else if (closed_door(x, y))
-            Strcat(buf, " embedded in a door");
-        else if (is_pool(x, y))
-            Strcat(buf, " in water");
-        else if (is_lava(x, y))
-            Strcat(buf, " in molten lava"); /* [can this ever happen?] */
+    } else if (glyph_is_object(glyph)) {
+        look_at_object(buf, x, y, glyph); /* fill in buf[] */
     } else if (glyph_is_trap(glyph)) {
         int tnum = what_trap(glyph_to_trap(glyph));
         Strcpy(buf, defsyms[trap_to_defsym(tnum)].explanation);
@@ -758,8 +781,7 @@ do_look(mode, click_cc)
 int mode;
 coord *click_cc;
 {
-    boolean quick =
-        (mode == 1); /* use cursor && don't search for "more info" */
+    boolean quick = (mode == 1); /* use cursor; don't search for "more info" */
     boolean clicklook = (mode == 2); /* right mouse-click method */
     char out_str[BUFSZ];
     const char *firstmatch = 0;
@@ -781,21 +803,50 @@ coord *click_cc;
             menu_item *pick_list = (menu_item *) 0;
             winid win;
             anything any;
+
+            any = zeroany;
             win = create_nhwindow(NHW_MENU);
             start_menu(win);
-            any.a_void = 0;
-            any.a_char = 'a';
-            /* 'y' and 'n' to keep backwards compat with previous versions */
-            add_menu(win, NO_GLYPH, &any, 'a', 'y', ATR_NONE,
+            any.a_char = '/';
+            /* 'y' and 'n' to keep backwards compatability with previous
+               versions: "Specify unknown object by cursor?" */
+            add_menu(win, NO_GLYPH, &any,
+                     flags.lootabc ? 0 : any.a_char, 'y', ATR_NONE,
                      "something on the map", MENU_UNSELECTED);
-            any.a_void = 0;
-            any.a_char = 'b';
-            add_menu(win, NO_GLYPH, &any, 'b', 0, ATR_NONE,
+            any.a_char = 'i';
+            add_menu(win, NO_GLYPH, &any,
+                     flags.lootabc ? 0 : any.a_char, 0, ATR_NONE,
                      "something you're carrying", MENU_UNSELECTED);
-            any.a_void = 0;
-            any.a_char = 'c';
-            add_menu(win, NO_GLYPH, &any, 'c', 'n', ATR_NONE,
-                     "something else", MENU_UNSELECTED);
+            any.a_char = '?';
+            add_menu(win, NO_GLYPH, &any,
+                     flags.lootabc ? 0 : any.a_char, 'n', ATR_NONE,
+                     "something else (by symbol or name)", MENU_UNSELECTED);
+            if (!u.uswallow && !Hallucination) {
+                any = zeroany;
+                add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
+                         "", MENU_UNSELECTED);
+                /* these options work sensibly for the swallowed case,
+                   but there's no reason for the player to use them then;
+                   objects work fine when hallucinating, but screen
+                   symbol/monster class letter doesn't match up with
+                   bogus monster type, so suppress when hallucinating */
+                any.a_char = 'm';
+                add_menu(win, NO_GLYPH, &any,
+                         flags.lootabc ? 0 : any.a_char, 0, ATR_NONE,
+                         "nearby monsters", MENU_UNSELECTED);
+                any.a_char = 'M';
+                add_menu(win, NO_GLYPH, &any,
+                         flags.lootabc ? 0 : any.a_char, 0, ATR_NONE,
+                         "all monsters shown on map", MENU_UNSELECTED);
+                any.a_char = 'o';
+                add_menu(win, NO_GLYPH, &any,
+                         flags.lootabc ? 0 : any.a_char, 0, ATR_NONE,
+                         "nearby objects", MENU_UNSELECTED);
+                any.a_char = 'O';
+                add_menu(win, NO_GLYPH, &any,
+                         flags.lootabc ? 0 : any.a_char, 0, ATR_NONE,
+                         "all objects shown on map", MENU_UNSELECTED);
+            }
             end_menu(win, "What do you want to look at:");
             if (select_menu(win, PICK_ONE, &pick_list) > 0) {
                 i = pick_list->item.a_char;
@@ -809,17 +860,18 @@ coord *click_cc;
         case 'q':
             return 0;
         case 'y':
-        case 'a':
+        case '/':
             from_screen = TRUE;
             sym = 0;
             cc.x = u.ux;
             cc.y = u.uy;
             break;
-        case 'b': {
+        case 'i':
+          {
             char invlet;
             struct obj *invobj;
 
-            invlet = display_inventory(NULL, TRUE);
+            invlet = display_inventory((const char *) 0, TRUE);
             if (!invlet || invlet == '\033')
                 return 0;
             *out_str = '\0';
@@ -831,8 +883,8 @@ coord *click_cc;
             if (*out_str)
                 checkfile(out_str, pm, TRUE, TRUE);
             return 0;
-        } break;
-        case 'c':
+          }
+        case '?':
             from_screen = FALSE;
             getlin("Specify what? (type the word)", out_str);
             if (out_str[0] == '\0' || out_str[0] == '\033')
@@ -844,6 +896,18 @@ coord *click_cc;
             }
             sym = out_str[0];
             break;
+        case 'm':
+            look_all(TRUE, TRUE); /* list nearby monsters */
+            return 0;
+        case 'M':
+            look_all(FALSE, TRUE); /* list all monsters */
+            return 0;
+        case 'o':
+            look_all(TRUE, FALSE); /* list nearby objects */
+            return 0;
+        case 'O':
+            look_all(FALSE, FALSE); /* list all objects */
+            return 0;
         }
     } else { /* clicklook */
         cc.x = click_cc->x;
@@ -909,6 +973,92 @@ coord *click_cc;
     return 0;
 }
 
+STATIC_OVL void
+look_all(nearby, do_mons)
+boolean nearby; /* True => within BOLTLIM, False => entire map */
+boolean do_mons; /* True => monsters, False => objects */
+{
+    winid win;
+    int x, y, lo_x, lo_y, hi_x, hi_y, glyph, count = 0;
+    char buf[BUFSZ], outbuf[BUFSZ], coordbuf[12], fmt[12]; /* "%02d,%02d" */
+
+    /* row,column orientation rather than cartesian x,y */
+    Sprintf(fmt, "%%%sd,%%%sd",
+            (ROWNO <= 100) ? "02" : (ROWNO <= 1000) ? "03" : "",
+            (COLNO <= 100) ? "02" : (COLNO <= 1000) ? "03" : "");
+
+    win = create_nhwindow(NHW_TEXT);
+    lo_y = nearby ? max(u.uy - BOLT_LIM, 0) : 0;
+    lo_x = nearby ? max(u.ux - BOLT_LIM, 1) : 1;
+    hi_y = nearby ? min(u.uy + BOLT_LIM, ROWNO - 1) : ROWNO - 1;
+    hi_x = nearby ? min(u.ux + BOLT_LIM, COLNO - 1) : COLNO - 1;
+    for (y = lo_y; y <= hi_y; y++) {
+        for (x = lo_x; x <= hi_x; x++) {
+            buf[0] = '\0';
+            glyph = glyph_at(x, y);
+            if (do_mons) {
+                if (glyph_is_monster(glyph)) {
+                    struct monst *mtmp;
+
+                    bhitpos.x = x; /* [is this actually necessary?] */
+                    bhitpos.y = y;
+                    if (x == u.ux && y == u.uy && canspotself()) {
+                        (void) self_lookat(buf);
+                        ++count;
+                    } else if ((mtmp = m_at(x, y)) != 0) {
+                        look_at_monster(buf, (char *) 0, mtmp, x, y);
+                        ++count;
+                    }
+                } else if (glyph_is_invisible(glyph)) {
+                    /* remembered, unseen, creature */
+                    Strcpy(buf, invisexplain);
+                    ++count;
+                } else if (glyph_is_warning(glyph)) {
+                    int warnindx = glyph_to_warning(glyph);
+
+                    Strcpy(buf, def_warnsyms[warnindx].explanation);
+                    ++count;
+                }
+            } else { /* !do_mons */
+                if (glyph_is_object(glyph)) {
+                    look_at_object(buf, x, y, glyph);
+                    ++count;
+                }
+            }
+            if (*buf) {
+                if (count == 1) {
+                    char which[12];
+
+                    Strcpy(which, do_mons ? "monsters" : "objects");
+                    if (nearby) {
+                        Sprintf(coordbuf, fmt, u.uy, u.ux);
+                        Sprintf(outbuf, "%s currently shown near %s:",
+                                upstart(which), coordbuf);
+                    } else
+                        Sprintf(outbuf, "All %s currently shown on the map:",
+                                which);
+                    putstr(win, 0, outbuf);
+                    putstr(win, 0, "");
+                }
+                Sprintf(coordbuf, fmt, y, x);
+                /* prefix: "C  row,column  " */
+                Sprintf(outbuf, "%s  %s  ", encglyph(glyph), coordbuf);
+                /* guard against potential overflow */
+                buf[sizeof buf - 1 - strlen(outbuf)] = '\0';
+                Strcat(outbuf, buf);
+                putmixed(win, 0, outbuf);
+            }
+        }
+    }
+    if (count)
+        display_nhwindow(win, TRUE);
+    else
+        pline("No %s are currently shown %s.",
+              do_mons ? "monsters" : "objects",
+              nearby ? "nearby" : "on the map");
+    destroy_nhwindow(win);
+}
+
 /* the '/' command */
 int
 dowhatis()