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
+146 -53
View File
@@ -1,96 +1,143 @@
^ Show the type of a trap &# cmdhelp
& Tell what command a keystroke invokes
^ Show the type of an adjacent trap
^[ Cancel command (same as ESCape key) ^[ Cancel command (same as ESCape key)
^A Redo the previous command &? debug
^C Quit the game ^E Search for nearby traps, secret doors, and unseen monsters
^D Kick something (usually a door, chest, or box) ^F Map level; reveals traps and secret corridors but not secret doors
^E Search a room (available in debug mode only) ^G Create a monster by name or class
^F Map the level (available in debug mode only) ^I View inventory with all items identified
^G Create a monster (available in debug mode only) ^O List special level locations
^I Identify all items (available in debug mode only) ^V Teleport between levels
^O Show dungeon overview (normal play) or special levels (debug mode) ^W Wish for something
^P Toggle through previously displayed game messages &: #!debug
^R Redraw screen ^E unavailable debugging command
^T Teleport around level ^F unavailable debugging command
^V Teleport between levels (available in debug mode only) ^G unavailable debugging command
^W Wish (available in debug mode only) ^I unavailable debugging command
^X Show your attributes (shows more in debug or explore mode) ^O Shortcut for '#overview': list interesting levels you have visited
^Z Suspend game (only if defined) ^V unavailable debugging command
a Apply (use) a tool ^W unavailable debugging command
A Remove all armor &. #?debug
&? number_pad=0,-1
b Go southwest 1 space b Go southwest 1 space
B Go southwest until you are on top of something 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 ^B Go southwest until you are near something
c Close a door c Close a door
C Call (name) a monster, an individual object, or a type of object C Call (name) a monster, an individual object, or a type of object
^C Interrupt: quit the game
d Drop an item d Drop an item
D Drop specific item types D Drop specific item types
^D Kick something (usually a door, chest, or box)
e Eat something e Eat something
E Engrave writing on the floor E Engrave writing on the floor
f Fire ammunition from quiver f Fire ammunition from quiver
F Followed by direction, fight a monster (even if you don't sense it) 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, move until you are near something
G Followed by direction, same as control-direction 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 ^H Go west until you are near something
i Show your inventory i Show your inventory
I Inventory specific item types 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 ^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 ^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 ^L Go east until you are near something
m Followed by direction, move without picking anything up or fighting m Followed by direction, move without picking anything up or fighting
M Followed by direction, move a distance without picking anything up 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 ^N Go southeast until you are near something
o Open a door o Open a door
O Show option settings, possibly change them O Show option settings, possibly change them
p Pay your shopping bill p Pay your shopping bill
P Put on an accessory (ring, amulet, etc) 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 Quaff (drink) something (potion, water, etc)
Q Select ammunition for quiver Q Select ammunition for quiver (use '#quit' to quit)
r Read a scroll or spellbook r Read a scroll or spellbook
R Remove an accessory (ring, amulet, etc) R Remove an accessory (ring, amulet, etc; will work for armor too)
s Search for traps and secret doors ^R Redraw screen
S Save the game s Search all immediately adjacent locations for traps and secret doors
t Throw something S Save the game (and exit; there is no "save and keep going")
T Take off one piece of armor t Throw something (choose an item, then a direction--not a target)
u Go northeast 1 space (or if number_pad is on, untrap something) T Take off one piece of armor (will work for accessories too)
U Go northeast until you are on top of something ^T Teleport around level
^U Go northeast until you are near something ^U Go northeast until you are near something
v Show version v Show version ('#version' shows more information)
V Show long version and game history V Show history of game's development
w Wield (put in use) a weapon w Wield a weapon (for dual weapons: 'w' secondary, 'x', 'w' primary, 'X')
W Wear a piece of armor W Wear a piece of armor (will work for accessories too)
x Swap wielded and secondary weapons x Swap wielded and secondary weapons
X Toggle two-weapon combat 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 1 space
Y Go northwest until you are on top of something Y Go northwest until you are on top of something
&.
^Y Go northwest until you are near something ^Y Go northwest until you are near something
z Zap a wand z Zap a wand
Z Zap (cast) a spell 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 up a staircase
> Go down a staircase > Go down a staircase
/ Show what type of thing a symbol corresponds to / Show what type of thing a symbol corresponds to
? Give a help message ? Give a help message
& Tell what a command does &? shell
! Do a shell escape (only if defined) ! Do a shell escape; 'exit' shell to come back
&:
! unavailable command: shell
&.
\ Show what object types have been discovered \ Show what object types have been discovered
` Show discovered types for one class of objects ` Show discovered types for one class of objects
_ Travel via a shortest-path algorithm to a point on the map _ Travel via a shortest-path algorithm to a point on the map
. Rest one move while doing nothing . Rest one move while doing nothing
Rest one move while doing nothing (if rest_on_space option is on) &? rest_on_space
Rest one move while doing nothing
&.
: Look at what is on the floor : Look at what is on the floor
; Show what type of thing a map symbol on the level corresponds to ; Show what type of thing a map symbol on the level corresponds to
, Pick up things at the current location , Pick up things at the current location
@ Toggle the pickup option on/off @ Toggle the pickup option on/off
) Show the weapon currently wielded ) Show the weapon(s) currently wielded or readied
[ Show the armor currently worn [ Show the armor currently worn
= Show the ring(s) currently worn = Show the ring(s) currently worn
" Show the amulet currently worn " Show the amulet currently worn
@@ -98,26 +145,72 @@ _ Travel via a shortest-path algorithm to a point on the map
* Show all equipment in use (combination of the ),[,=,",( commands) * Show all equipment in use (combination of the ),[,=,",( commands)
$ Count your gold $ Count your gold
+ List known spells + List known spells
# Perform an extended command # 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-? 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 Adjust inventory letters
M-A Annotate: supply a name for the current dungeon level M-A Annotate: supply a name for the current dungeon level
M-c Talk to someone M-c Chat: talk to an adjacent creature
M-C Conduct: list voluntary challenges you have maintained M-C Conduct: list voluntary challenges you have maintained
M-d Dip an object into something M-d Dip an object into something
M-e Advance or check weapons skills M-e Enhance: check weapons skills, advance them if eligible
M-f Force a lock M-f Force a lock
M-i Invoke an object's special powers M-i Invoke an object's special powers
M-j Jump to another location M-j Jump to a nearby location
M-l Loot a box on the floor M-l Loot a box on the floor
M-m Use a monster's special ability 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-N Name a monster, an individual object, or a type of object
M-o Offer a sacrifice to the gods M-o Offer a sacrifice to the gods
M-O Overview: show a summary of the explored dungeon M-O Overview: show a summary of the explored dungeon
M-p Pray to the gods for help M-p Pray to the gods for help
M-q Quit M-q Quit (exit without saving)
M-r Rub a lamp M-r Rub a lamp or a touchstone
M-R Ride: mount or dismount a saddled steed M-R Ride: mount or dismount a saddled steed
M-s Sit down M-s Sit down
M-t Turn undead M-t Turn undead
+163 -23
View File
@@ -17,6 +17,8 @@ STATIC_DCL struct permonst *FDECL(lookat, (int, int, char *, char *));
STATIC_DCL void FDECL(checkfile, (char *, struct permonst *, STATIC_DCL void FDECL(checkfile, (char *, struct permonst *,
BOOLEAN_P, BOOLEAN_P)); BOOLEAN_P, BOOLEAN_P));
STATIC_DCL void FDECL(look_all, (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 boolean FDECL(help_menu, (int *));
STATIC_DCL void NDECL(docontact); STATIC_DCL void NDECL(docontact);
#ifdef PORT_HELP #ifdef PORT_HELP
@@ -1307,7 +1309,7 @@ doidtrap()
} }
STATIC_DCL void STATIC_DCL void
dowhatdoes_help() whatdoes_help()
{ {
dlb *fp; dlb *fp;
char *p, buf[BUFSZ]; char *p, buf[BUFSZ];
@@ -1332,14 +1334,131 @@ dowhatdoes_help()
destroy_nhwindow(tmpwin); 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 * char *
dowhatdoes_core(q, cbuf) dowhatdoes_core(q, cbuf)
char q; char q;
char *cbuf; char *cbuf;
{ {
dlb *fp; dlb *fp;
char bufr[BUFSZ]; char buf[BUFSZ];
register char *buf = &bufr[6], ctrl, meta; boolean cond, stack[WD_STACKLIMIT];
int ctrl, meta, depth = 0, lnum = 0;
fp = dlb_fopen(CMDHELPFILE, "r"); fp = dlb_fopen(CMDHELPFILE, "r");
if (!fp) { if (!fp) {
@@ -1347,25 +1466,44 @@ char *cbuf;
return 0; return 0;
} }
ctrl = ((q <= '\033') ? (q - 1 + 'A') : 0); meta = (0x80 & (uchar) q) != 0;
meta = ((0x80 & q) ? (0x7f & q) : 0); if (meta)
while (dlb_fgets(buf, BUFSZ - 6, fp)) { q &= 0x7f;
if ((ctrl && *buf == '^' && *(buf + 1) == ctrl) ctrl = (0x1f & (uchar) q) == (uchar) q;
|| (meta && *buf == 'M' && *(buf + 1) == '-' if (ctrl)
&& *(buf + 2) == meta) || *buf == q) { 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); (void) strip_newline(buf);
if (ctrl && buf[2] == '\t') { if (index(buf, '\t'))
buf = bufr + 1; (void) tabexpand(buf);
(void) strncpy(buf, "^? ", 8); if (meta && ctrl && buf[4] == ' ') {
buf[1] = ctrl; (void) strncpy(buf, "M-^? ", 8);
} else if (meta && buf[3] == '\t') { buf[3] = q;
buf = bufr + 2; } else if (meta && buf[3] == ' ') {
(void) strncpy(buf, "M-? ", 8); (void) strncpy(buf, "M-? ", 8);
buf[2] = meta; buf[2] = q;
} else if (buf[1] == '\t') { } else if (ctrl && buf[2] == ' ') {
buf = bufr; (void) strncpy(buf, "^? ", 8);
buf[1] = q;
} else if (buf[1] == ' ') {
(void) strncpy(buf, "? ", 8);
buf[0] = q; buf[0] = q;
(void) strncpy(buf + 1, " ", 7);
} }
(void) dlb_fclose(fp); (void) dlb_fclose(fp);
Strcpy(cbuf, buf); Strcpy(cbuf, buf);
@@ -1373,6 +1511,8 @@ char *cbuf;
} }
} }
(void) dlb_fclose(fp); (void) dlb_fclose(fp);
if (depth != 0)
impossible("cmdhelp: mismatched &? &: &. conditionals.");
return (char *) 0; return (char *) 0;
} }
@@ -1395,9 +1535,9 @@ dowhatdoes()
if (q == '\033' && iflags.altmeta) { if (q == '\033' && iflags.altmeta) {
/* in an ideal world, we would know whether another keystroke /* in an ideal world, we would know whether another keystroke
was already pending, but this is not an ideal world... 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 */ character is typed */
q = yn_function("", (char *) 0, '\0'); q = yn_function("]", (char *) 0, '\0');
if (q != '\033') if (q != '\033')
q = (char) ((uchar) q | 0200); q = (char) ((uchar) q | 0200);
} }
@@ -1407,7 +1547,7 @@ dowhatdoes()
reslt = dowhatdoes_core(q, bufr); reslt = dowhatdoes_core(q, bufr);
if (reslt) { if (reslt) {
if (q == '&' || q == '?') if (q == '&' || q == '?')
dowhatdoes_help(); whatdoes_help();
pline("%s", reslt); pline("%s", reslt);
} else { } else {
pline("No such command '%s', char code %d (0%03o or 0x%02x).", pline("No such command '%s', char code %d (0%03o or 0x%02x).",
@@ -1416,7 +1556,7 @@ dowhatdoes()
return 0; return 0;
} }
void STATIC_OVL void
docontact() docontact()
{ {
winid cwin = create_nhwindow(NHW_TEXT); winid cwin = create_nhwindow(NHW_TEXT);