]> granicus.if.org Git - nethack/commitdiff
Add MSGTYPE config option
authorPasi Kallinen <paxed@alt.org>
Wed, 27 May 2015 15:29:12 +0000 (18:29 +0300)
committerPasi Kallinen <paxed@alt.org>
Wed, 27 May 2015 17:21:57 +0000 (20:21 +0300)
MSGTYPE allows the user to define how messages in the message
area behave. For example:

  MSGTYPE=stop "You swap places with "

would always make that message prompt for -more-.  Allowed types
are "show" (normal message), "hide" (do not show), "stop" (wait
for user), and "norep" (do not repeat message).

Adding this, because it's relatively simple, proven to work, and
it seemed to be the major thing betatesters felt was lacking when
compared to NAO.

doc/Guidebook.mn
doc/Guidebook.tex
include/decl.h
include/extern.h
src/decl.c
src/files.c
src/options.c
src/pline.c
src/save.c

index 7315631b00ff7657d478de9f9313e962e939ed89..3a7df513aae39a6afd6c1fb0052a3dca5ebeddef 100644 (file)
@@ -2638,6 +2638,50 @@ The second example results in the exclusion of any corpse from autopickup.
 The last example results in the exclusion of items known to be cursed from
 autopickup.
 .hn 2
+Configuring Message Types
+.pg
+You can change the way the messages are shown in the message area, when
+the message matches a user-defined pattern.
+.pg
+In general, the config file entries to configure the message types
+look like this:
+.si
+MSGTYPE=type "pattern"
+.ei
+.PS "message type"
+.PL type
+how the message should be shown;
+.PL pattern
+the pattern to match.
+.PE
+.lp ""
+The pattern should be a regular expression.
+.lp ""
+Allowed types are:
+.sd
+.si
+show  - show message normally.
+hide  - never show the message.
+stop  - wait for user with more-prompt.
+norep - show the message once, but not again if no other message is shown in between.
+.ei
+.ed
+.lp ""
+Here's an example of message types using NetHack's internal
+pattern matching facility:
+.sd
+.si
+MSGTYPE=stop "You feel hungry."
+MSGTYPE=hide "You displaced *."
+.ei
+.ed
+specifies that whenever a message "You feel hungry" is shown,
+the user is prompted with more-prompt, and a message matching
+"You displaced <something>." is not shown at all.
+.lp ""
+The order of the defined MSGTYPE-lines is important; the last matching
+rule is used. Put the general case first, exceptions below them.
+.hn 2
 Configuring Menu Colors
 .pg
 Some platforms allow you to define colors used in menu lines when the
index f9fb923bc11542669f53b165dc8d833430b83628..566402cd9b156900a287f58aafd71bb019ae5537 100644 (file)
@@ -3184,6 +3184,64 @@ The second example results in the exclusion of any corpse from autopickup.
 The last example results in the exclusion of items known to be cursed from
 autopickup.
 
+%.lp
+%.hn 2
+\subsection*{Configuring Message Types}
+
+%.pg
+You can change the way the messages are shown in the message area, when
+the message matches a user-defined pattern.
+
+%.pg
+In general, the config file entries to configure the message types
+look like this:
+\begin{verbatim}
+    MSGTYPE=type "pattern"
+\end{verbatim}
+
+\blist{}
+%.lp
+\item[\ib{type}]
+how the message should be shown;
+%.lp
+\item[\ib{pattern}]
+the pattern to match.
+\elist
+
+%.lp ""
+The pattern should be a regular expression.
+
+%.lp ""
+Allowed types are:
+
+%.sd
+%.si
+{\tt show}  --- show message normally.\\
+{\tt hide}  --- never show the message.\\
+{\tt stop}  --- wait for user with more-prompt.\\
+{\tt norep} --- show the message once, but not again if no other message is shown in between.
+%.ei
+%.ed
+
+%.lp ""
+Here's an example of menu colors using NetHack's internal
+pattern matching facility:
+
+\begin{verbatim}
+    MSGTYPE=stop "You feel hungry."
+    MSGTYPE=hide "You displaced *."
+\end{verbatim}
+
+specifies that whenever a message ``You feel hungry'' is shown,
+the user is prompted with more-prompt, and a message matching
+``You displaced <something>'' is not shown at all.
+
+%.lp
+The order of the defined MSGTYPE-lines is important; the last matching
+rule is used. Put the general case first, exceptions below them.
+
+%.pg
+
 %.lp
 %.hn 2
 \subsection*{Configuring Menu Colors}
index 61abc88b2b9d8b741a2b4560253d7cb008cc839f..fae4fcf4bd27d28e83070d68666fa72c36487a30 100644 (file)
@@ -395,6 +395,20 @@ struct autopickup_exception {
     struct autopickup_exception *next;
 };
 
+struct plinemsg_type {
+    xchar msgtype;  /* one of MSGTYP_foo */
+    struct nhregex *regex;
+    char *pattern;
+    struct plinemsg_type *next;
+};
+
+#define MSGTYP_NORMAL   0
+#define MSGTYP_NOREP    1
+#define MSGTYP_NOSHOW   2
+#define MSGTYP_STOP     3
+
+E struct plinemsg_type *plinemsg_types;
+
 #ifdef PANICTRACE
 E char *ARGV0;
 #endif
index 75272c20530b7c0c2fcae24ab7ae01a7d52373fc..1d26a6e55325189831f070f437239fffb8f8ef07 100644 (file)
@@ -1638,6 +1638,9 @@ E int FDECL(sym_val, (char *));
 E boolean FDECL(add_menu_coloring, (char *));
 E boolean FDECL(get_menu_coloring, (char *, int *, int *));
 E void NDECL(free_menu_coloring);
+E boolean FDECL(msgtype_parse_add, (char *));
+E int FDECL(msgtype_type, (const char *));
+E void NDECL(msgtype_free);
 
 /* ### pager.c ### */
 
index c99eae4c854950cd826dde214c189177900bcdec..7e4f95f6dde80508071e66904ddef8c90ccb57a4 100644 (file)
@@ -328,6 +328,8 @@ NEARDATA struct savefile_info sfrestinfo, sfsaveinfo = {
 #endif
 };
 
+struct plinemsg_type *plinemsg_types = NULL;
+
 #ifdef PANICTRACE
 char *ARGV0;
 #endif
index a43869cfa3a8b4c980f30f4bafc0088b4adcb3e8..697a1fdd12afdc6581e4916e798cabfbd5b4d4d4 100644 (file)
@@ -2134,6 +2134,8 @@ int src;
             plnamesuffix(); /* set the character class */
     } else if (match_varname(buf, "AUTOPICKUP_EXCEPTION", 5)) {
         add_autopickup_exception(bufp);
+    } else if (match_varname(buf, "MSGTYPE", 7)) {
+       (void) msgtype_parse_add(bufp);
 #ifdef NOCWD_ASSUMPTIONS
     } else if (match_varname(buf, "HACKDIR", 4)) {
         adjust_prefix(bufp, HACKPREFIX);
index 78bd233980dcc427af5f4472da28613dfaa28817..9b767a810c902e310fe4c1678e9fab1067565133 100644 (file)
@@ -505,6 +505,7 @@ STATIC_OVL int FDECL(count_ape_maps, (int *, int *));
 STATIC_DCL const char *FDECL(clr2colorname, (int));
 STATIC_DCL const char *FDECL(attr2attrname, (int));
 STATIC_DCL int NDECL(query_color);
+STATIC_DCL int NDECL(query_msgtype);
 STATIC_DCL int FDECL(query_attr, (const char *));
 STATIC_DCL boolean FDECL(add_menu_coloring_parsed, (char *, int, int));
 STATIC_DCL void FDECL(free_one_menu_coloring, (int));
@@ -1279,6 +1280,168 @@ const char *prompt;
     return -1;
 }
 
+static const struct {
+    const char *name;
+    const xchar msgtyp;
+    const char *descr;
+} msgtype_names[] = {
+    { "show", MSGTYP_NORMAL, "Show message normally" },
+    { "hide", MSGTYP_NOSHOW, "Hide message" },
+    { "noshow", MSGTYP_NOSHOW, NULL },
+    { "stop", MSGTYP_STOP, "Prompt for more after the message" },
+    { "more", MSGTYP_STOP, NULL },
+    { "norep", MSGTYP_NOREP, "Do not repeat the message" }
+};
+
+const char *
+msgtype2name(typ)
+int typ;
+{
+    int i;
+    for (i = 0; i < SIZE(msgtype_names); i++)
+       if (msgtype_names[i].descr && msgtype_names[i].msgtyp == typ)
+           return msgtype_names[i].name;
+    return NULL;
+}
+
+int
+query_msgtype()
+{
+    winid tmpwin;
+    anything any;
+    int i, pick_cnt;
+    xchar prev_typ = -1;
+    menu_item *picks = (menu_item *) 0;
+
+    tmpwin = create_nhwindow(NHW_MENU);
+    start_menu(tmpwin);
+    any = zeroany;
+    for (i = 0; i < SIZE(msgtype_names); i++)
+       if (msgtype_names[i].descr) {
+           any.a_int = msgtype_names[i].msgtyp + 1;
+           add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
+                 msgtype_names[i].descr, MENU_UNSELECTED);
+       }
+    end_menu(tmpwin, "How to show the message");
+    pick_cnt = select_menu(tmpwin, PICK_ONE, &picks);
+    destroy_nhwindow(tmpwin);
+    if (pick_cnt > 0) {
+        i = picks->item.a_int - 1;
+        free((genericptr_t) picks);
+        return i;
+    }
+    return -1;
+}
+
+boolean
+msgtype_add(typ, pattern)
+int typ;
+char *pattern;
+{
+    struct plinemsg_type *tmp = (struct plinemsg_type *) alloc(sizeof(struct plinemsg_type));
+    if (!tmp) return FALSE;
+    tmp->msgtype = typ;
+    tmp->regex = regex_init();
+    if (!regex_compile(pattern, tmp->regex)) {
+       static const char *re_error = "MSGTYPE regex error";
+        if (!iflags.window_inited)
+            raw_printf("\n%s: %s\n", re_error, regex_error_desc(tmp->regex));
+        else
+            pline("%s: %s", re_error, regex_error_desc(tmp->regex));
+        wait_synch();
+        free(tmp);
+        return FALSE;
+    }
+    tmp->pattern = dupstr(pattern);
+    tmp->next = plinemsg_types;
+    plinemsg_types = tmp;
+    return TRUE;
+}
+
+void
+msgtype_free()
+{
+    struct plinemsg_type *tmp = plinemsg_types;
+    struct plinemsg_type *tmp2;
+    while (tmp) {
+       free(tmp->pattern);
+       regex_free(tmp->regex);
+       tmp2 = tmp;
+       tmp = tmp->next;
+       free(tmp2);
+    }
+    plinemsg_types = NULL;
+}
+
+void
+free_one_msgtype(idx)
+int idx; /* 0 .. */
+{
+    struct plinemsg_type *tmp = plinemsg_types;
+    struct plinemsg_type *prev = NULL;
+
+    while (tmp) {
+        if (idx == 0) {
+            struct plinemsg_type *next = tmp->next;
+            regex_free(tmp->regex);
+            free(tmp->pattern);
+            free(tmp);
+            if (prev)
+                prev->next = next;
+            else
+                plinemsg_types = next;
+            return;
+        }
+        idx--;
+        prev = tmp;
+        tmp = tmp->next;
+    }
+}
+
+int
+msgtype_type(msg)
+const char *msg;
+{
+    struct plinemsg_type *tmp = plinemsg_types;
+    while (tmp) {
+       if (regex_match(msg, tmp->regex)) return tmp->msgtype;
+       tmp = tmp->next;
+    }
+    return MSGTYP_NORMAL;
+}
+
+int
+msgtype_count()
+{
+    int c = 0;
+    struct plinemsg_type *tmp = plinemsg_types;
+    while (tmp) {
+       c++;
+       tmp = tmp->next;
+    }
+    return c;
+}
+
+boolean
+msgtype_parse_add(str)
+char *str;
+{
+    char pattern[256];
+    char msgtype[11];
+    if (sscanf(str, "%10s \"%255[^\"]\"", msgtype, pattern) == 2) {
+       int typ = -1;
+       int i;
+       for (i = 0; i < SIZE(msgtype_names); i++)
+           if (!strcasecmp(msgtype_names[i].name, msgtype)) {
+               typ = msgtype_names[i].msgtyp;
+               break;
+           }
+       if (typ != -1)
+           return msgtype_add(typ, pattern);
+    }
+    return FALSE;
+}
+
 boolean
 add_menu_coloring_parsed(str, c, a)
 char *str;
@@ -3379,6 +3542,11 @@ doset()
                     doset_add_menu(tmpwin, compopt[i].name,
                                    (pass == DISP_IN_GAME) ? 0 : indexoffset);
             }
+    any.a_int = -4;
+    Sprintf(buf2, "(%d currently set)", msgtype_count());
+    Sprintf(buf, fmtstr_doset_add_menu, any.a_int ? "" : "    ", "message types",
+            buf2);
+    add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
     any.a_int = -3;
     Sprintf(buf2, "(%d currently set)", count_menucolors());
     Sprintf(buf, fmtstr_doset_add_menu, any.a_int ? "" : "    ", "menucolors",
@@ -3438,6 +3606,8 @@ doset()
 #endif
                 if (opt_indx == -4) {
                 (void) special_handling("menucolors", setinitial, fromfile);
+               } else if (opt_indx == -5) {
+                   (void) special_handling("msgtype", setinitial, fromfile);
             } else if (opt_indx < boolcount) {
                 /* boolean option */
                 Sprintf(buf, "%s%s", *boolopt[opt_indx].addr ? "!" : "",
@@ -3835,6 +4005,60 @@ boolean setinitial, setfromfile;
         int mhattr = query_attr("How to highlight menu headings:");
         if (mhattr != -1)
             iflags.menu_headings = mhattr;
+    } else if (!strcmp("msgtype", optname)) {
+       int opt_idx, nmt, mttyp;
+       char mtbuf[BUFSZ];
+    msgtypes_again:
+       nmt = msgtype_count();
+       opt_idx = handle_add_list_remove("message type", nmt);
+       if (opt_idx == 3) {
+           ; /* done--fall through to function exit */
+       } else if (opt_idx == 0) { /* add new */
+           getlin("What new message pattern?", mtbuf);
+           if (*mtbuf == '\033' || !*mtbuf)
+               goto msgtypes_again;
+           mttyp = query_msgtype();
+           if (mttyp == -1)
+               goto msgtypes_again;
+           if (!msgtype_add(mttyp, mtbuf)) {
+               pline("Error adding the message type.");
+               wait_synch();
+               goto msgtypes_again;
+           }
+       } else { /* list or remove */
+            int pick_idx, pick_cnt;
+            int mt_idx;
+           char mtbuf[BUFSZ];
+            menu_item *pick_list = (menu_item *) 0;
+            struct plinemsg_type *tmp = plinemsg_types;
+            tmpwin = create_nhwindow(NHW_MENU);
+            start_menu(tmpwin);
+            any = zeroany;
+            mt_idx = 0;
+            while (tmp) {
+                const char *mtype = msgtype2name(tmp->msgtype);
+                any.a_int = (++mt_idx);
+                Sprintf(mtbuf, "%-5s \"%s\"", mtype, tmp->pattern);
+                add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, mtbuf,
+                         MENU_UNSELECTED);
+                tmp = tmp->next;
+            }
+            Sprintf(mtbuf, "%s message types",
+                    (opt_idx == 1) ? "List of" : "Remove which");
+            end_menu(tmpwin, mtbuf);
+            pick_cnt = select_menu(
+                tmpwin, (opt_idx == 1) ? PICK_NONE : PICK_ANY, &pick_list);
+            if (pick_cnt > 0) {
+                for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx)
+                    free_one_msgtype(pick_list[pick_idx].item.a_int - 1
+                                           - pick_idx);
+            }
+            free((genericptr_t) pick_list);
+            pick_list = (menu_item *) 0;
+            destroy_nhwindow(tmpwin);
+            if (pick_cnt >= 0)
+                goto msgtypes_again;
+       }
     } else if (!strcmp("menucolors", optname)) {
         int opt_idx, nmc, mcclr, mcattr;
         char mcbuf[BUFSZ];
index ab4fc8a17c3b00b9768772da0d67eb33c81bee07..ce4b6070224ae459ac29ba09212c9d7436bef767 100644 (file)
@@ -7,6 +7,7 @@
 #include "hack.h"
 
 static boolean no_repeat = FALSE;
+static char prevmsg[BUFSZ];
 
 static char *FDECL(You_buf, (int));
 
@@ -47,6 +48,7 @@ VA_DECL(const char *, line)
 {       /* start of vpline() or of nested block in USE_OLDARG's pline() */
     char pbuf[3 * BUFSZ];
     int ln;
+    xchar msgtyp;
     /* Do NOT use VA_START and VA_END in here... see above */
 
     if (!line || !*line)
@@ -89,9 +91,15 @@ VA_DECL(const char *, line)
         vision_recalc(0);
     if (u.ux)
         flush_screen(1); /* %% */
+    msgtyp = msgtype_type(line);
+    if (msgtyp == MSGTYP_NOSHOW) return;
+    if (msgtyp == MSGTYP_NOREP && !strcmp(line, prevmsg)) return;
     putstr(WIN_MESSAGE, 0, line);
     /* this gets cleared after every pline message */
     iflags.last_msg = PLNMSG_UNKNOWN;
+    strncpy(prevmsg, line, BUFSZ);
+    if (msgtyp == MSGTYP_STOP) display_nhwindow(WIN_MESSAGE, TRUE); /* --more-- */
+
 #if !(defined(USE_STDARG) || defined(USE_VARARGS))
     /* provide closing brace for the nested block
        which immediately follows USE_OLDARGS's VA_DECL() */
index 4f65ececfa2c11a43e5a89fef7645ab945514199..8ab8225de4d23afa96439b2122967aacef72b82a 100644 (file)
@@ -1320,6 +1320,7 @@ freedynamicdata()
     free_menu_coloring();
     free_invbuf();           /* let_to_name (invent.c) */
     free_youbuf();           /* You_buf,&c (pline.c) */
+    msgtype_free();
     tmp_at(DISP_FREEMEM, 0); /* temporary display effects */
 #ifdef FREE_ALL_MEMORY
 #define freeobjchn(X) (saveobjchn(0, X, FREE_SAVE), X = 0)