From: Pasi Kallinen Date: Wed, 27 May 2015 15:29:12 +0000 (+0300) Subject: Add MSGTYPE config option X-Git-Tag: NetHack-3.6.0_RC01~347 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f0eca282a87349979a348a8fe8297c3e61362209;p=nethack Add MSGTYPE config option 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. --- diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 7315631b0..3a7df513a 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -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 ." 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 diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index f9fb923bc..566402cd9 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -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 '' 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} diff --git a/include/decl.h b/include/decl.h index 61abc88b2..fae4fcf4b 100644 --- a/include/decl.h +++ b/include/decl.h @@ -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 diff --git a/include/extern.h b/include/extern.h index 75272c205..1d26a6e55 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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 ### */ diff --git a/src/decl.c b/src/decl.c index c99eae4c8..7e4f95f6d 100644 --- a/src/decl.c +++ b/src/decl.c @@ -328,6 +328,8 @@ NEARDATA struct savefile_info sfrestinfo, sfsaveinfo = { #endif }; +struct plinemsg_type *plinemsg_types = NULL; + #ifdef PANICTRACE char *ARGV0; #endif diff --git a/src/files.c b/src/files.c index a43869cfa..697a1fdd1 100644 --- a/src/files.c +++ b/src/files.c @@ -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); diff --git a/src/options.c b/src/options.c index 78bd23398..9b767a810 100644 --- a/src/options.c +++ b/src/options.c @@ -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]; diff --git a/src/pline.c b/src/pline.c index ab4fc8a17..ce4b60702 100644 --- a/src/pline.c +++ b/src/pline.c @@ -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() */ diff --git a/src/save.c b/src/save.c index 4f65ececf..8ab8225de 100644 --- a/src/save.c +++ b/src/save.c @@ -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)