cmdhelp revamp

Implement a rudimentary if/elif/else/endif interpretor and use
conditionals in dat/cmdhelp to describe what command each keystroke
currently invokes, so that there isn't a lot of "(debug mode only)"
and "(if number_pad is off)" cluttering the feedback that the user
sees.  (The conditionals add quite a bit of clutter to the raw data
but users don't see that.  number_pad produces a lot of conditional
commands:  basic letters vs digits, 'g' vs 'G' for '5', phone
keypad vs normal layout of digits, and QWERTZ keyboard swap between
y/Y/^Y/M-y/M-Y/M-^Y and z/Z/^Z/M-z/M-Z/M-^Z.)

The interpretor understands
 '&#' for comment,
 '&? option' for 'if' (also '&? !option'
                       or '&? option=value[,value2,...]'
                       or '&? !option=value[,value2,...]'),
 '&: option' for 'elif' (with argument variations same as 'if';
                         any number of instances for each 'if'),
 '&:' for 'else' (also '&: #comment';
                  0 or 1 instance for a given 'if'), and
 '&.' for 'endif' (also '&. #comment'; required for each 'if').

The option handling is a bit of a mess, with no generality for
which options to deal with and only a comma separated list of
integer values for the '=value' part.  number_pad is the only
supported option that has a value; the few others (wizard/debug,
rest_on_space, #if SHELL, #if SUSPEND) are booleans.
This commit is contained in:
PatR
2016-06-08 14:58:35 -07:00
parent d92ae04afb
commit ca22b3fe46
2 changed files with 383 additions and 150 deletions

View File

@@ -1,127 +1,220 @@
^ Show the type of a trap
^[ Cancel command (same as ESCape key)
^A Redo the previous command
^C Quit the game
^D Kick something (usually a door, chest, or box)
^E Search a room (available in debug mode only)
^F Map the level (available in debug mode only)
^G Create a monster (available in debug mode only)
^I Identify all items (available in debug mode only)
^O Show dungeon overview (normal play) or special levels (debug mode)
^P Toggle through previously displayed game messages
^R Redraw screen
^T Teleport around level
^V Teleport between levels (available in debug mode only)
^W Wish (available in debug mode only)
^X Show your attributes (shows more in debug or explore mode)
^Z Suspend game (only if defined)
a Apply (use) a tool
A Remove all armor
b Go southwest 1 space
B Go southwest until you are on top of something
^B Go southwest until you are near something
c Close a door
C Call (name) a monster, an individual object, or a type of object
d Drop an item
D Drop specific item types
e Eat something
E Engrave writing on the floor
f Fire ammunition from quiver
F Followed by direction, fight a monster (even if you don't sense it)
g Followed by direction, move until you are near something
G Followed by direction, same as control-direction
h Go west 1 space (if number_pad is on, display help message)
H Go west until you are on top of something
^H Go west until you are near something
i Show your inventory
I Inventory specific item types
j Go south 1 space (or if number_pad is on, jump to another location)
J Go south until you are on top of something
^J Go south until you are near something
k Go north 1 space (or if number_pad is on, kick something)
K Go north until you are on top of something
^K Go north until you are near something
l Go east 1 space (or if number_pad is on, loot a box on the floor)
L Go east until you are on top of something
^L Go east until you are near something
m Followed by direction, move without picking anything up or fighting
M Followed by direction, move a distance without picking anything up
n Go southeast 1 space
N Go southeast until you are on something (if number_pad is on, name)
^N Go southeast until you are near something
o Open a door
O Show option settings, possibly change them
p Pay your shopping bill
P Put on an accessory (ring, amulet, etc)
q Quaff (drink) something (potion, water, etc)
Q Select ammunition for quiver
r Read a scroll or spellbook
R Remove an accessory (ring, amulet, etc)
s Search for traps and secret doors
S Save the game
t Throw something
T Take off one piece of armor
u Go northeast 1 space (or if number_pad is on, untrap something)
U Go northeast until you are on top of something
^U Go northeast until you are near something
v Show version
V Show long version and game history
w Wield (put in use) a weapon
W Wear a piece of armor
x Swap wielded and secondary weapons
X Toggle two-weapon combat
y Go northwest 1 space
Y Go northwest until you are on top of something
^Y Go northwest until you are near something
z Zap a wand
Z Zap (cast) a spell
< Go up a staircase
> Go down a staircase
/ Show what type of thing a symbol corresponds to
? Give a help message
& Tell what a command does
! Do a shell escape (only if defined)
\ Show what object types have been discovered
` Show discovered types for one class of objects
_ Travel via a shortest-path algorithm to a point on the map
. Rest one move while doing nothing
Rest one move while doing nothing (if rest_on_space option is on)
: Look at what is on the floor
; Show what type of thing a map symbol on the level corresponds to
, Pick up things at the current location
@ Toggle the pickup option on/off
) Show the weapon currently wielded
[ Show the armor currently worn
= Show the ring(s) currently worn
" Show the amulet currently worn
( Show the tools currently in use
* Show all equipment in use (combination of the ),[,=,",( commands)
$ Count your gold
+ List known spells
# Perform an extended command
M-? Display extended command help (if the platform allows this)
M-2 Toggle two-weapon combat (unless number_pad is enabled)
M-a Adjust inventory letters
M-A Annotate: supply a name for the current dungeon level
M-c Talk to someone
M-C Conduct: list voluntary challenges you have maintained
M-d Dip an object into something
M-e Advance or check weapons skills
M-f Force a lock
M-i Invoke an object's special powers
M-j Jump to another location
M-l Loot a box on the floor
M-m Use a monster's special ability
M-n Name a monster, an individual object, or a type of object
M-o Offer a sacrifice to the gods
M-O Overview: show a summary of the explored dungeon
M-p Pray to the gods for help
M-q Quit
M-r Rub a lamp
M-R Ride: mount or dismount a saddled steed
M-s Sit down
M-t Turn undead
M-T Tip: empty a container
M-u Untrap something (trap, door, or chest)
M-v Print compile time options for this version of NetHack
M-w Wipe off your face
&# cmdhelp
& Tell what command a keystroke invokes
^ Show the type of an adjacent trap
^[ Cancel command (same as ESCape key)
&? debug
^E Search for nearby traps, secret doors, and unseen monsters
^F Map level; reveals traps and secret corridors but not secret doors
^G Create a monster by name or class
^I View inventory with all items identified
^O List special level locations
^V Teleport between levels
^W Wish for something
&: #!debug
^E unavailable debugging command
^F unavailable debugging command
^G unavailable debugging command
^I unavailable debugging command
^O Shortcut for '#overview': list interesting levels you have visited
^V unavailable debugging command
^W unavailable debugging command
&. #?debug
&? number_pad=0,-1
b Go southwest 1 space
B Go southwest until you are on top of something
h Go west 1 space
H Go west until you are on top of something
j Go south 1 space
J Go south until you are on top of something
k Go north 1 space
K Go north until you are on top of something
l Go east 1 space
L Go east until you are on top of something
n Go southeast 1 space
N Go southeast until you are on something
u Go northeast 1 space
U Go northeast until you are on top of something
&# y,Y handled below
&: #number_pad=1,2,3,4
h Help: synonym for '?'
j Jump: shortcut for '#jump'
k Kick: synonym for '^D'
l Loot: shortcut for '#loot'
n Start a count; continue with digit(s)
N Name: shortcut for '#name'
u Untrap: shortcut for '#untrap'
&. #0,-1 vs 1,2,3,4
a Apply (use) a tool or break a wand
A Remove all armor and/or all accessories and/or unwield weapons
^A Redo the previous command
^B Go southwest until you are near something
c Close a door
C Call (name) a monster, an individual object, or a type of object
^C Interrupt: quit the game
d Drop an item
D Drop specific item types
^D Kick something (usually a door, chest, or box)
e Eat something
E Engrave writing on the floor
f Fire ammunition from quiver
F Followed by direction, fight a monster (even if you don't sense it)
g Followed by direction, move until you are near something
G Followed by direction, same as control-direction
^H Go west until you are near something
i Show your inventory
I Inventory specific item types
^J Go south until you are near something
^K Go north until you are near something
^L Go east until you are near something
m Followed by direction, move without picking anything up or fighting
M Followed by direction, move a distance without picking anything up
^N Go southeast until you are near something
o Open a door
O Show option settings, possibly change them
p Pay your shopping bill
P Put on an accessory (ring, amulet, etc; will work for armor too)
^P Toggle through previously displayed game messages
q Quaff (drink) something (potion, water, etc)
Q Select ammunition for quiver (use '#quit' to quit)
r Read a scroll or spellbook
R Remove an accessory (ring, amulet, etc; will work for armor too)
^R Redraw screen
s Search all immediately adjacent locations for traps and secret doors
S Save the game (and exit; there is no "save and keep going")
t Throw something (choose an item, then a direction--not a target)
T Take off one piece of armor (will work for accessories too)
^T Teleport around level
^U Go northeast until you are near something
v Show version ('#version' shows more information)
V Show history of game's development
w Wield a weapon (for dual weapons: 'w' secondary, 'x', 'w' primary, 'X')
W Wear a piece of armor (will work for accessories too)
x Swap wielded and secondary weapons
X Toggle two-weapon combat
^X Show your attributes (shows more in debug or explore mode)
&? number_pad=0,1,2,3,4
&? number_pad=0
y Go northwest 1 space
Y Go northwest until you are on top of something
&.
^Y Go northwest until you are near something
z Zap a wand
Z Zap (cast) a spell
&? suspend
^Z Suspend game; 'fg' (foreground) to resume
&:
^Z unavailable command: suspend
&.
&: number_pad=-1
y Zap a wand
Y Zap (cast) a spell
&? suspend
^Y Suspend game; 'fg' (foreground) to resume
&:
^Y unavailable command: suspend
&.
z Go northwest 1 space
Z Go northwest until you are on top of something
^Z Go northwest until you are near something
&. #0,1..4 vs -1
< Go up a staircase
> Go down a staircase
/ Show what type of thing a symbol corresponds to
? Give a help message
&? shell
! Do a shell escape; 'exit' shell to come back
&:
! unavailable command: shell
&.
\ Show what object types have been discovered
` Show discovered types for one class of objects
_ Travel via a shortest-path algorithm to a point on the map
. Rest one move while doing nothing
&? rest_on_space
Rest one move while doing nothing
&.
: Look at what is on the floor
; Show what type of thing a map symbol on the level corresponds to
, Pick up things at the current location
@ Toggle the pickup option on/off
) Show the weapon(s) currently wielded or readied
[ Show the armor currently worn
= Show the ring(s) currently worn
" Show the amulet currently worn
( Show the tools currently in use
* Show all equipment in use (combination of the ),[,=,",( commands)
$ Count your gold
+ List known spells
# Perform an extended command (use '#?' to list choices)
&# number_pad:
&# -1 = numpad off, swap y with z (including Y with Z, ^Y with ^Z, M-y &c)
&# 0 = numpad off (default)
&# 1 = numpad on, normal keypad layout, '5'->'g'
&# 2 = numpad on, normal keypad layout, '5'->'G'
&# 3 = numpad on, phone keypad layout, '5'->'g'
&# 4 = numpad on, phone keypad layout, '5'->'G'
&? number_pad = 1,2,3,4
0 Show inventory
4 Move west
6 Move east
&: #-1,0
0 Continue a count
4 Start or continue a count
6 Start or continue a count
&. #1,2,3,4 vs -1,0
&? number_pad=1,2
7 Move northwest
8 Move north
9 Move northeast
1 Move southwest
2 Move south
3 Move southeast
&: number_pad=3,4
1 Move northwest
2 Move north
3 Move northeast
7 Move southwest
8 Move south
9 Move southeast
&: #-1,0
1 Start or continue a count
2 Start or continue a count
3 Start or continue a count
7 Start or continue a count
8 Start or continue a count
9 Start or continue a count
&. #1,2 vs 3,4 vs -1,0
&? number_pad=1,3
5 'g' movement prefix
&: number_pad=2,4
5 'G' movement prefix
&: #-1,0
5 Start or continue a count
M-2 Toggle two-weapon combat
&. #1,3 vs 2,4 vs -1,0
M-? Display extended command help (if the platform allows this)
M-a Adjust inventory letters
M-A Annotate: supply a name for the current dungeon level
M-c Chat: talk to an adjacent creature
M-C Conduct: list voluntary challenges you have maintained
M-d Dip an object into something
M-e Enhance: check weapons skills, advance them if eligible
M-f Force a lock
M-i Invoke an object's special powers
M-j Jump to a nearby location
M-l Loot a box on the floor
M-m When polymorphed, use a monster's special ability
M-n Name a monster, an individual object, or a type of object
M-N Name a monster, an individual object, or a type of object
M-o Offer a sacrifice to the gods
M-O Overview: show a summary of the explored dungeon
M-p Pray to the gods for help
M-q Quit (exit without saving)
M-r Rub a lamp or a touchstone
M-R Ride: mount or dismount a saddled steed
M-s Sit down
M-t Turn undead
M-T Tip: empty a container
M-u Untrap something (trap, door, or chest)
M-v Print compile time options for this version of NetHack
M-w Wipe off your face

View File

@@ -17,6 +17,8 @@ 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(look_all, (BOOLEAN_P,BOOLEAN_P));
STATIC_DCL void NDECL(whatdoes_help);
STATIC_DCL boolean FDECL(whatdoes_cond, (char *, boolean *, int *, int));
STATIC_DCL boolean FDECL(help_menu, (int *));
STATIC_DCL void NDECL(docontact);
#ifdef PORT_HELP
@@ -1307,7 +1309,7 @@ doidtrap()
}
STATIC_DCL void
dowhatdoes_help()
whatdoes_help()
{
dlb *fp;
char *p, buf[BUFSZ];
@@ -1332,14 +1334,131 @@ dowhatdoes_help()
destroy_nhwindow(tmpwin);
}
#define WD_STACKLIMIT 5
STATIC_OVL boolean
whatdoes_cond(buf, stack, depth, lnum)
char *buf;
boolean *stack;
int *depth, lnum;
{
const char badstackfmt[] = "cmdhlp: too many &%c directives at line %d.";
boolean newcond, neg;
char *p, *q, act = buf[1];
int np = 0;
buf += 2;
mungspaces(buf);
if (act == '#' || *buf == '#') {
*buf = '\0';
neg = FALSE; /* lint suppression */
p = q = (char *) 0;
} else {
if ((neg = (*buf == '!')) != 0)
if (*++buf == ' ')
++buf;
p = index(buf, '='), q = index(buf, ':');
if (!p || (q && q < p))
p = q;
if (p) { /* we have a value specified */
/* handle a space before or after (or both) '=' (or ':') */
if (p > buf && p[-1] == ' ')
p[-1] = '\0'; /* end of keyword in buf[] */
*p++ = '\0'; /* terminate keyword, advance to start of value */
if (*p == ' ')
p++;
}
}
newcond = TRUE;
if (*buf && (act == '?' || act == ':')) {
if (!strcmpi(buf, "number_pad")) {
if (!p) {
newcond = iflags.num_pad;
} else {
/* convert internal encoding (separate yes/no and 0..3)
back to user-visible one (-1..4) */
np = iflags.num_pad ? (1 + iflags.num_pad_mode) /* 1..4 */
: (-1 * iflags.num_pad_mode); /* -1..0 */
newcond = FALSE;
for (; p; p = q) {
q = index(p, ',');
if (q)
*q++ = '\0';
if (atoi(p) == np) {
newcond = TRUE;
break;
}
}
}
} else if (!strcmpi(buf, "rest_on_space")) {
newcond = flags.rest_on_space;
} else if (!strcmpi(buf, "debug") || !strcmpi(buf, "wizard")) {
newcond = flags.debug; /* == wizard */
} else if (!strcmpi(buf, "shell")) {
#ifdef SHELL
/* should we also check sysopt.shellers? */
newcond = TRUE;
#else
newcond = FALSE;
#endif
} else if (!strcmpi(buf, "suspend")) {
#ifdef SUSPEND
/* sysopt.shellers is also used for dosuspend()... */
newcond = TRUE;
#else
newcond = FALSE;
#endif
} else {
impossible(
"cmdhelp: unrecognized &%c conditional at line %d: \"%.20s\"",
act, lnum, buf);
neg = FALSE;
}
/* this works for number_pad too: &? !number_pad:-1,0
would be true for 1..4 after negation */
if (neg)
newcond = !newcond;
}
switch (act) {
default:
case '#': /* comment */
break;
case '.': /* endif */
if (--*depth < 0) {
impossible(badstackfmt, '.', lnum);
*depth = 0;
}
break;
case ':': /* else or elif */
if (*depth == 0) {
impossible(badstackfmt, ':', lnum);
*depth = 1; /* so that stack[*depth - 1] is a valid access */
}
if (stack[*depth] || !stack[*depth - 1])
stack[*depth] = FALSE;
else if (newcond)
stack[*depth] = TRUE;
break;
case '?': /* if */
if (++*depth >= WD_STACKLIMIT) {
impossible(badstackfmt, '?', lnum);
*depth = WD_STACKLIMIT - 1;
}
stack[*depth] = newcond && stack[*depth - 1];
break;
}
return stack[*depth];
}
char *
dowhatdoes_core(q, cbuf)
char q;
char *cbuf;
{
dlb *fp;
char bufr[BUFSZ];
register char *buf = &bufr[6], ctrl, meta;
char buf[BUFSZ];
boolean cond, stack[WD_STACKLIMIT];
int ctrl, meta, depth = 0, lnum = 0;
fp = dlb_fopen(CMDHELPFILE, "r");
if (!fp) {
@@ -1347,25 +1466,44 @@ char *cbuf;
return 0;
}
ctrl = ((q <= '\033') ? (q - 1 + 'A') : 0);
meta = ((0x80 & q) ? (0x7f & q) : 0);
while (dlb_fgets(buf, BUFSZ - 6, fp)) {
if ((ctrl && *buf == '^' && *(buf + 1) == ctrl)
|| (meta && *buf == 'M' && *(buf + 1) == '-'
&& *(buf + 2) == meta) || *buf == q) {
meta = (0x80 & (uchar) q) != 0;
if (meta)
q &= 0x7f;
ctrl = (0x1f & (uchar) q) == (uchar) q;
if (ctrl)
q |= 0x40; /* NUL -> '@', ^A -> 'A', ... ^Z -> 'Z', ^[ -> '[', ... */
else if (q == 0x7f)
ctrl = 1, q = '?';
cond = stack[0] = TRUE, stack[1] = FALSE;
while (dlb_fgets(buf, sizeof buf, fp)) {
++lnum;
if (buf[0] == '&' && buf[1] && index("?:.#", buf[1])) {
cond = whatdoes_cond(buf, stack, &depth, lnum);
continue;
}
if (!cond)
continue;
if (meta ? (buf[0] == 'M' && buf[1] == '-'
&& (ctrl ? buf[2] == '^' && highc(buf[3]) == q
: buf[2] == q))
: (ctrl ? buf[0] == '^' && highc(buf[1]) == q
: buf[0] == q)) {
(void) strip_newline(buf);
if (ctrl && buf[2] == '\t') {
buf = bufr + 1;
(void) strncpy(buf, "^? ", 8);
buf[1] = ctrl;
} else if (meta && buf[3] == '\t') {
buf = bufr + 2;
if (index(buf, '\t'))
(void) tabexpand(buf);
if (meta && ctrl && buf[4] == ' ') {
(void) strncpy(buf, "M-^? ", 8);
buf[3] = q;
} else if (meta && buf[3] == ' ') {
(void) strncpy(buf, "M-? ", 8);
buf[2] = meta;
} else if (buf[1] == '\t') {
buf = bufr;
buf[2] = q;
} else if (ctrl && buf[2] == ' ') {
(void) strncpy(buf, "^? ", 8);
buf[1] = q;
} else if (buf[1] == ' ') {
(void) strncpy(buf, "? ", 8);
buf[0] = q;
(void) strncpy(buf + 1, " ", 7);
}
(void) dlb_fclose(fp);
Strcpy(cbuf, buf);
@@ -1373,6 +1511,8 @@ char *cbuf;
}
}
(void) dlb_fclose(fp);
if (depth != 0)
impossible("cmdhelp: mismatched &? &: &. conditionals.");
return (char *) 0;
}
@@ -1395,9 +1535,9 @@ dowhatdoes()
if (q == '\033' && iflags.altmeta) {
/* in an ideal world, we would know whether another keystroke
was already pending, but this is not an ideal world...
if user types ESC, we'll essentially hang until another
if user typed ESC, we'll essentially hang until another
character is typed */
q = yn_function("", (char *) 0, '\0');
q = yn_function("]", (char *) 0, '\0');
if (q != '\033')
q = (char) ((uchar) q | 0200);
}
@@ -1407,7 +1547,7 @@ dowhatdoes()
reslt = dowhatdoes_core(q, bufr);
if (reslt) {
if (q == '&' || q == '?')
dowhatdoes_help();
whatdoes_help();
pline("%s", reslt);
} else {
pline("No such command '%s', char code %d (0%03o or 0x%02x).",
@@ -1416,7 +1556,7 @@ dowhatdoes()
return 0;
}
void
STATIC_OVL void
docontact()
{
winid cwin = create_nhwindow(NHW_TEXT);