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.
This commit is contained in:
Pasi Kallinen
2015-05-27 18:29:12 +03:00
parent c447088538
commit f0eca282a8
9 changed files with 356 additions and 0 deletions

View 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

View 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}

View 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

View 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 ### */

View File

@@ -328,6 +328,8 @@ NEARDATA struct savefile_info sfrestinfo, sfsaveinfo = {
#endif
};
struct plinemsg_type *plinemsg_types = NULL;
#ifdef PANICTRACE
char *ARGV0;
#endif

View 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);

View 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];

View 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() */

View 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)