From 680c8a542cb981da0b64fbdd1457d69447f05efb Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Wed, 5 Oct 2016 17:19:06 +0300 Subject: [PATCH] Add key rebinding This is a modified version of Jason Dorje Short's key rebinding patch, and allows also binding special keys, such as the ones used in getloc and getpos. One of the ways to play NetHack on nethack.alt.org is via a HTML terminal in browser. Unfortunately this means several ctrl-key combinations cannot be entered, because the browser intercepts those. Similar thing applies to some international keyboard layouts on Windows. With this patch, the user can just rebind the command to a key that works best for them. I've tested this on Linux TTY, X11, and Windows TTY and GUI. --- doc/Guidebook.mn | 348 +++++++++++++++++-- doc/Guidebook.tex | 493 +++++++++++++++++++++++++-- doc/fixes36.1 | 1 + include/extern.h | 10 +- include/flag.h | 50 ++- include/func_tab.h | 15 +- src/allmain.c | 3 - src/cmd.c | 938 +++++++++++++++++++++++++++++++++++----------------- src/do_name.c | 150 ++++++--- src/files.c | 4 + src/hacklib.c | 23 +- src/options.c | 55 ++- src/pager.c | 18 +- win/tty/getline.c | 4 +- win/win32/mswproc.c | 4 +- 15 files changed, 1695 insertions(+), 421 deletions(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index a4f96648..87cfb992 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -465,7 +465,9 @@ more info). .hn 1 Commands .pg -Commands are initiated by typing one or two characters. Some commands, +Commands can be initiated by typing one or two characters to which +the command is bound to, or typing the command name in the extended +commands entry. Some commands, like ``search'', do not require that any more information be collected by NetHack. Other commands might require additional information, for example a direction, or an object to be used. For those commands that @@ -498,11 +500,11 @@ ESC key. .pg The list of commands is rather long, but it can be read at any time during the game through the `?' command, which accesses a menu of -helpful texts. Here are the commands for your reference: +helpful texts. Here are the default key bindings for your reference: .lp ? Help menu: display one of several help texts available. .lp / -The ``what-is'' command, to +The ``whatis'' command, to tell what a symbol represents. You may choose to specify a location or type a symbol (or even a whole word) to explain. Specifying a location is done by moving the cursor to a particular spot @@ -823,7 +825,7 @@ via the ``#twoweapon'' extended command. .lp "" (In versions prior to 3.6 this was the command to switch from normal play to "explore mode", also known as "discovery mode", which has now -been moved to ``#explore''.) +been moved to ``#exploremode''.) .lp ^X Display basic information about your character. .lp "" @@ -912,7 +914,7 @@ compiled with. .lp #adjust Adjust inventory letters (most useful when the .op fixinv -option is ``on''). +option is ``on''). Autocompletes. Default key is 'M-a'. .lp "" This command allows you to move an item from one particular inventory slot to another so that it has a letter which is more meaningful for you @@ -923,50 +925,113 @@ choosing the item to adjust, enter a count prior to its letter. .lp #annotate Allows you to specify one line of text to associate with the current dungeon level. All levels with annotations are displayed by the -``#overview'' command. +``#overview'' command. Autocompletes. Default key is 'M-A', and '^N' if number_pad is on. +.lp #apply +Apply (use) a tool such as a pick-axe, a key, or a lamp. Default key is 'a'. +.lp #attributes +Show your attributes. Default key is '^X'. +.lp #autopickup +Toggle the autopickup -option on/off. Default key is '@'. +.lp #call +Call (name) a monster, or a object in inventory, on the floor, +or in the discoveries list, or add an annotation for the +current level (same as ``#annotate''). Default key is 'C'. +.lp #cast +Cast a spell. Default key is 'Z'. .lp #chat -Talk to someone. +Talk to someone. Default key is 'M-c'. +.lp #close +Close a door. Default key is 'c'. .lp #conduct -List voluntary challenges you have maintained. +List voluntary challenges you have maintained. Autocompletes. Default key is 'M-C'. .lp "" See the section below entitled ``Conduct'' for details. .lp "#dip " -Dip an object into something. +Dip an object into something. Autocompletes. Default key is 'M-d'. +.lp #down +Go down a staircase. Default key is '>'. +.lp #drop +Drop an item. Default key is 'd'. +.lp #droptype +Drop specific item types. Default key is 'D'. +.lp "#eat " +Eat something. Default key is 'e'. +.lp #engrave +Engrave writing on the floor. Default key is 'E'. .lp #enhance -Advance or check weapon and spell skills. +Advance or check weapon and spell skills. Autocompletes. Default key is 'M-e'. .lp #exploremode Enter the explore mode. +.lp #fire +Fire ammunition from quiver. Default key is 'f'. .lp #force -Force a lock. +Force a lock. Autocompletes. Default key is 'M-f'. +.lp #glance +Show what type of thing a map symbol corresponds to. Default key is ';'. +.lp #help +Show the help menu. Default key is '?', and 'h' if number_pad is on. +.lp #history +Show long version and game history. Default key is 'V'. +.lp #inventory +Show your inventory. Default key is 'i'. +.lp #inventtype +Inventory specific item types. Default key is 'I'. .lp #invoke -Invoke an object's special powers. +Invoke an object's special powers. Autocompletes. Default key is 'M-i'. .lp #jump -Jump to another location. +Jump to another location. Autocompletes. Default key is 'M-j', and 'j' if number_pad is on. .lp #kick -Kick something. +Kick something. Default key is '^D', and 'k' if number_pad is on. +.lp #known +Show what object types have been discovered. Default key is '\'. +.lp #knownclass +Show discovered types for one class of objects. Default key is '`'. +.lp #levelchange +Change your experience level. Autocompletes. Wizard-mode only. +.lp #lightsources +Show mobile light sources. Autocompletes. Wizard-mode only. +.lp #look +Look at what is here, under you. Default key is ':'. .lp #loot Loot a box or bag on the floor beneath you, or the saddle -from a steed standing next to you. +from a steed standing next to you. Autocompletes. Precede with the `m' prefix to skip containers at your location -and go directly to removing a saddle. +and go directly to removing a saddle. Default key is 'M-l', and 'l' if number_pad is on. +.lp #monpolycontrol +Control monster polymorphs. Autocompletes. Wizard-mode only. .lp #monster Use a monster's special ability (when polymorphed into monster form). +Autocompletes. Default key is 'M-m'. .lp #name -Name a monster, an individual object, or a type of object. Same as `C'. +Name a monster, an individual object, or a type of object. Same as #call. +Autocompletes. Default keys are 'N', 'M-n', and 'M-N'. .lp #offer -Offer a sacrifice to the gods. +Offer a sacrifice to the gods. Autocompletes. Default key is 'M-o'. .lp "" You'll need to find an altar to have any chance at success. Corpses of recently killed monsters are the fodder of choice. +.lp #open +Open a door. Default key is 'o'. +.lp #options +Show and change option settings. Default key is 'O'. .lp #overview Display information you've discovered about the dungeon. Any visited level (unless forgotten due to amnesia) with an annotation is included, and many things (altars, thrones, fountains, and so on; extra stairs leading to another dungeon branch) trigger an automatic annotation. If dungeon overview is chosen during end-of-game disclosure, every visited -level will be included regardless of annotations. +level will be included regardless of annotations. Autocompletes. +Default keys are '^O', and 'M-O'. +.lp #panic +Test the panic routine. Autocompletes. Wizard-mode only. +.lp "#pay " +Pay your shopping bill. Default key is 'p'. +.lp #pickup +Pick up things at the current location. Default key is ','. +.lp #polyself +Polymorph self. Autocompletes. Wizard-mode only. .lp #pray -Pray to the gods for help. +Pray to the gods for help. Autocompletes. Default key is 'M-p'. .lp "" Praying too soon after receiving prior help is a bad idea. (Hint: entering the dungeon alive is treated as having received help. @@ -976,40 +1041,150 @@ option to make you confirm your intent before praying. It is enabled by default, and you can reset the .op paranoid_confirmation option to disable it. +.lp #prevmsg +Show previously displayed game messages. Default key is '^P'. +.lp #puton +Put on an accessory (ring, amulet, etc). Default key is 'P'. +.lp #quaff +Quaff (drink) something. Default key is 'q'. .lp #quit -Quit the program without saving your game. +Quit the program without saving your game. Autocompletes. Default key is 'M-q'. .lp "" Since using this command by accident would throw away the current game, you are asked to confirm your intent before quitting. By default a response of 'y' acknowledges that intent. You can set the .op paranoid_confirmation option to require a response of "yes" instead. +.lp #quiver +Select ammunition for quiver. Default key is 'Q'. +.lp #read +Read a scroll, a spellbook, or something else. Default key is 'r'. +.lp #redraw +Redraw the screen. Default key is '^R', and '^L' if number_pad is on. +.lp #remove +Remove an accessory (ring, amulet, etc). Default key is 'R'. .lp #ride -Ride (or stop riding) a saddled creature. +Ride (or stop riding) a saddled creature. Autocompletes. Default key is 'M-R'. .lp "#rub " -Rub a lamp or a stone. +Rub a lamp or a stone. Autocompletes. Default key is 'M-r'. +.lp #save +Save the game. Default key is 'S'. +.lp #search +Search for traps and secret doors around you. Default key is 's'. +.lp #seeall +Show all equipment in use. Default key is '*'. +.lp #seeamulet +Show the amulet currently worn. Default key is '"'. +.lp #seearmor +Show the armor currently worn. Default key is '['. +.lp #seegold +Count your gold. Default key is '$'. +.lp #seenv +Show seen vectors. Autocompletes. Wizard-mode only. +.lp #seerings +Show the ring(s) currently worn. Default key is '='. +.lp #seespells +List and reorder known spells. Default key is '+'. +.lp #seetools +Show the tools currently in use. Default key is '('. +.lp #seetrap +Show the type of a trap near you. Default key is '^'. +.lp #seeweapon +Show the weapon currently wielded. Default key is ')'. +.lp #shell +Do a shell escape. Default key is '!'. .lp "#sit " -Sit down. +Sit down. Autocompletes. Default key is 'M-s'. +.lp #stats +Show memory statistics. Autocompletes. Wizard-mode only. +.lp #suspend +Suspend the game. Default key is '^Z'. +.lp #swap +Swap wielded and secondary weapons. Default key is 'x'. +.lp #takeoff +Take off one piece of armor. Default key is 'T'. +.lp #takeoffall +Remove all armor. Default key is 'A'. +.lp #teleport +Teleport around the level. Default key is '^T'. .lp #terrain Show bare map without displaying monsters, objects, or traps. +Autocompletes. +.lp #throw +Throw something. Default key is 't'. +.lp #timeout +Look at the timeout queue. Autocompletes. Wizard-mode only. .lp "#tip " Tip over a container (bag or box) to pour out its contents. +Autocompletes. Default key is 'M-T'. +.lp #travel +Travel to a specific location on the map. Default key is '_'. .lp #turn -Turn undead. +Turn undead away. Autocompletes. Default key is 'M-t'. .lp #twoweapon -Toggle two-weapon combat on or off. +Toggle two-weapon combat on or off. Autocompletes. Default keys +are 'X', and 'M-2'. .lp "" Note that you must use suitable weapons for this type of combat, or it will be automatically turned off. .lp #untrap -Untrap something (trap, door, or chest). +Untrap something (trap, door, or chest). Default key is 'M-u', and 'u' if number_pad is on. .lp "" In some circumstances it can also be used to rescue trapped monsters. +.lp "#up " +Go up a staircase. Default key is '<'. +.lp #vanquished +List vanquished monsters. Autocompletes. Wizard-mode only. .lp #version Print compile time options for this version of NetHack. +Autocompletes. Default key is 'M-v'. +.lp #versionshort +Show version string. Default key is 'v'. +.lp #vision +Show vision array. Autocompletes. Wizard-mode only. +.lp #wait +Rest one move while doing nothing. Default key is '.', and ' ' if +rest_on_space is on. +.lp #wear +Wear a piece of armor. Default key is 'W'. +.lp #whatdoes +Tell what a key does. Default key is '&'. +.lp #whatis +Show what type of thing a symbol corresponds to. Default key is '/'. +.lp #wield +Wield a weapon. Default key is 'w'. .lp #wipe -Wipe off your face. +Wipe off your face. Autocompletes. Default key is 'M-w'. +.lp #wizdebug_bury +Bury objects under and around you. Autocompletes. Wizard-mode only. +.lp #wizdebug_traveldisplay +Toggle travel display. Autocompletes. Wizard-mode only. +.lp #wizdetect +Search a room. Autocompletes. Wizard-mode only. Default key is '^E'. +.lp #wizgenesis +Create a monster. Autocompletes. Wizard-mode only. Default key is '^G'. +.lp #wizidentify +Identify all items in inventory. Autocompletes. Wizard-mode only. +Default key is '^I'. +.lp #wizintrinsic +Set intrinsic. Autocompletes. Wizard-mode only. +.lp #wizlevelport +Teleport to another level. Autocompletes. Wizard-mode only. Default key is '^V'. +.lp #wizmap +Map the level. Autocompletes. Wizard-mode only. Default key is '^F'. +.lp #wizrumorcheck +Verify rumor boundaries. Autocompletes. Wizard-mode only. +.lp #wizsmell +Smell monster. Autocompletes. Wizard-mode only. +.lp #wizwhere +Show locations of special levels. Autocompletes. Wizard-mode only. +.lp #wizwish +Wish for something. Autocompletes. Wizard-mode only. Default key is '^W'. +.lp #wmode +Show wall modes. Autocompletes. Wizard-mode only. +.lp "#zap " +Zap a wand. Default key is 'z'. .lp "#? " Help menu: get the list of available extended commands. .lp "" @@ -2054,10 +2229,32 @@ HACKDIR, must be writeable. .lp TROUBLEDIR The location that a record of game aborts and self-diagnosed game problems is kept. Defaults to HACKDIR, must be writeable. +.lp AUTOCOMPLETE +Enable or disable an extended command autocompletion. +Autocompletion has no effect for the X11 windowport. +You can specify multiple autocompletions. To enable +autocompletion, list the extended command. Prefix the +command with ``!'' to disable the autocompletion +for that command. +.pg +Example: +.sd +\fBAUTOCOMPLETE=zap,!annotate\fP +.ed .lp AUTOPICKUP_EXCEPTION Set exceptions to the .op pickup_types option. See the ``Configuring Autopickup Exceptions'' section. +.lp BINDINGS +Change the key bindings of some special keys, menu accelerators, or +extended commands. You can specify multiple bindings. Format is key +followed by the command, separated by a colon. +See the ``Changing Key Bindings`` section for more information. +.pg +Example: +.sd +\fBBIND=^X:getpos.autodescribe\fP +.ed .lp MSGTYPE Change the way messages are shown in the top status line. See the ``Configuring Message Types`` section. @@ -2955,6 +3152,101 @@ 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 +Changing Key Bindings +.pg +It is possible to change the default key bindings of some special commands, +menu accelerator keys, and extended commands, by using BIND stanzas in the +configuration file. Format is key, followed by the command to bind to, separated +by a colon. The key can be a single character (``x''), a control key (``^X'', +``C-x''), a meta key (``M-x''), or a three-digit decimal ASCII code. +.pg +For example: +.sd +.si +BIND=^X:getpos.autodescribe +BIND={:menu_first_page +BIND=v:loot +.ei +.ed +.pg +.lp "Extended command keys" +You can bind multiple keys to the same extended command. Unbind a key by +using ``nothing'' as the extended command to bind to. You can also bind +the ``'', ``'', and ``'' keys. +.lp "Menu accelerator keys" +The menu control or accelerator keys can also be rebound via OPTIONS-lines +in the config file. You cannot bind object symbols into menu accelerators. +.lp "Special command keys" +Below are the special commands you can rebind. Some of them can be bound to +same keys with no problems, others are in the same "context", and if bound +to same keys, only one of those commands will be available. Special command +can only be bound to a single key. +.pg +.lp count +Prefix key to start a count, to repeat a command this many times. With number_pad only. Default is 'n'. +.lp doinv +Show inventory. With number_pad only. Default is '0'. +.lp fight +Prefix key to force fight a direction. Default is 'F'. +.lp fight.numpad +Prefix key to force fight a direction. With number_pad only. Default is '-'. +.lp getdir.help +When asked for a direction, the key to show the help. Default is '?'. +.lp getdir.self +When asked for a direction, the key to target yourself. Default is '.'. +.lp getdir.self2 +When asked for a direction, the key to target yourself. Default is 's'. +.lp getpos.autodescribe +When asked for a location, the key to toggle autodescribe. Default is '#'. +.lp getpos.door.next +When asked for a location, the key to go to next closest door or doorway. Default is 'd'. +.lp getpos.door.prev +When asked for a location, the key to go to previous closest door or doorway. Default is 'D'. +.lp getpos.help +When asked for a location, the key to show help. Default is '?'. +.lp getpos.mon.next +When asked for a location, the key to go to next closest monster. Default is 'm'. +.lp getpos.mon.prev +When asked for a location, the key to go to previous closest monster. Default is 'M'. +.lp getpos.obj.next +When asked for a location, the key to go to next closest object. Default is 'o'. +.lp getpos.obj.prev +When asked for a location, the key to go to previous closest object. Default is 'O'. +.lp getpos.pick +When asked for a location, the key to choose the location, and possibly ask for more info. Default is '.'. +.lp getpos.pick.once +When asked for a location, the key to choose the location, and skip asking for more info. Default is ','. +.lp getpos.pick.quick +When asked for a location, the key to choose the location, skip asking for more info, and exit the location asking loop. Default is ';'. +.lp getpos.pick.verbose +When asked for a location, the key to choose the location, and show more info without asking. Default is ':'. +.lp getpos.self +When asked for a location, the key to go to your location. Default is '@'. +.lp getpos.unexplored.next +When asked for a location, the key to go to next closest unexplored location. Default is 'x'. +.lp getpos.unexplored.prev +When asked for a location, the key to go to previous closest unexplored location. Default is 'X'. +.lp getpos.valid +When asked for a location, the key to go to show valid target locations. Default is '$'. +.lp nopickup +Prefix key to move without picking up items. Default is 'm'. +.lp redraw +Key to redraw the screen. Default is '^R'. +.lp redraw.numpad +Key to redraw the screen. With number_pad only. Default is '^L'. +.lp repeat +Key to repeat previous command. Default is '^A'. +.lp reqmenu +Prefix key to request menu from some commands. Default is 'm'. +.lp run +Prefix key to run towards a direction. Default is 'G'. +.lp run.nopickup +Prefix key to run towards a direction without picking up items on the way. Default is 'M'. +.lp run.numpad +Prefix key to run towards a direction. With number_pad only. Default is '5'. +.lp rush +Prefix key to rush towards a direction. Default is 'g'. +.hn 2 Configuring Message Types .pg You can change the way the messages are shown in the message area, when diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index f8299cbd..12db38c2 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -571,7 +571,9 @@ more info). \section{Commands} %.pg -Commands are initiated by typing one or two characters. Some commands, +Commands can initiated by typing one or two characters to which +the command is bound to, or typing the command name in the extended +commands entry. Some commands, like ``{\tt search}'', do not require that any more information be collected by {\it NetHack\/}. Other commands might require additional information, for example a direction, or an object to be used. For those commands that @@ -607,7 +609,7 @@ greater control (see below). To cancel a count or a prefix, press the %.pg The list of commands is rather long, but it can be read at any time during the game through the `{\tt ?}' command, which accesses a menu of -helpful texts. Here are the commands for your reference: +helpful texts. Here are the default key bindings for your reference: \blist{} %.lp @@ -615,7 +617,7 @@ helpful texts. Here are the commands for your reference: Help menu: display one of several help texts available. %.lp \item[\tb{/}] -The {\tt what-is} command, to +The {\tt whatis} command, to tell what a symbol represents. You may choose to specify a location or type a symbol (or even a whole word) to explain. Specifying a location is done by moving the cursor to a particular spot @@ -985,7 +987,7 @@ via the ``{\tt \#twoweapon}'' extended command.\\ (In versions prior to 3.6 this was the command to switch from normal play to ``explore mode'', also known as ``discovery mode'', which has now -been moved to ``{\tt \#explore}''.) +been moved to ``{\tt \#exploremode}''.) %.lp \item[\tb{\^{}X}] Display basic information about your character.\\ @@ -1092,7 +1094,7 @@ the game was compiled with. \item[\tb{\#adjust}] Adjust inventory letters (most useful when the {\it fixinv\/} -option is ``on'').\\ +option is ``on''). Autocompletes. Default key is '{\tt M-a}'.\\ %.lp "" This command allows you to move an item from one particular inventory slot to another so that it has a letter which is more meaningful for you @@ -1104,65 +1106,162 @@ choosing the item to adjust, enter a count prior to its letter. \item[\tb{\#annotate}] Allows you to specify one line of text to associate with the current dungeon level. All levels with annotations are displayed by the -``{\tt \#overview}'' command. +``{\tt \#overview}'' command. Autocompletes. Default key is '{\tt M-A}', +and '{\tt \^{}N}' if {\it number\verb+_+pad\/} is on. +%.lp +\item[\tb{\#apply}] +Apply (use) a tool such as a pick-axe, a key, or a lamp. +Default key is '{\tt a}'. +%.lp +\item[\tb{\#attributes}] +Show your attributes. Default key is '{\tt \^{}X}'.\\ +%.lp +\item[\tb{\#autopickup}] +Toggle the {\it autopickup\/} -option. Default key is '{\tt @}'.\\ +%.lp +\item[\tb{\#call}] +Call (name) a monster, or a object in inventory, on the floor, +or in the discoveries list, or add an annotation for the +current level (same as ``{\tt \#annotate}''). Default key is '{\tt C}'. +%.lp +\item[\tb{\#cast}] +Cast a spell. Default key is '{\tt Z}'.\\ %.lp \item[\tb{\#chat}] -Talk to someone. +Talk to someone. Default key is '{\tt M-c}'.\\ +%.lp +\item[\tb{\#close}] +Close a door. Default key is '{\tt c}'.\\ %.lp \item[\tb{\#conduct}] -List voluntary challenges you have maintained.\\ +List voluntary challenges you have maintained. Autocompletes. +Default key is '{\tt M-C}'.\\ %.lp "" See the section below entitled ``Conduct'' for details. %.lp \item[\tb{\#dip}] -Dip an object into something. +Dip an object into something. Autocompletes. Default key is '{\tt M-d}'. +%.lp +\item[\tb{\#down}] +Go down a staircase. Default key is '{\tt >}'. +%.lp +\item[\tb{\#drop}] +Drop an item. Default key is '{\tt d}'. +%.lp +\item[\tb{\#droptype}] +Drop specific item types. Default key is '{\tt D}'. +%.lp +\item[\tb{\#eat}] +Eat something. Default key is '{\tt e}'. +%.lp +\item[\tb{\#engrave}] +Engrave writing on the floor. Default key is '{\tt E}'. %.lp \item[\tb{\#enhance}] -Advance or check weapon and spell skills. +Advance or check weapon and spell skills. Autocompletes. +Default key is '{\tt M-e}'. %.lp \item[\tb{\#exploremode}] Enter the explore mode. %.lp +\item[\tb{\#fire}] +Fire ammunition from quiver. Default key is '{\tt f}'. +%.lp \item[\tb{\#force}] -Force a lock. +Force a lock. Autocompletes. Default key is '{\tt M-f}'. +%.lp +\item[\tb{\#glance}] +Show what type of thing a map symbol corresponds to. Default key is '{\tt ;}'. +%.lp +\item[\tb{\#help}] +Show the help menu. Default key is '{\tt ?}', and '{\tt h}' if {\it number\verb+_+pad\/} is on. +%.lp +\item[\tb{\#history}] +Show long version and game history. Default key is '{\tt V}'. +%.lp +\item[\tb{\#inventory}] +Show your inventory. Default key is '{\tt i}'. +%.lp +\item[\tb{\#inventtype}] +Inventory specific item types. Default key is '{\tt I}'. %.lp \item[\tb{\#invoke}] -Invoke an object's special powers. +Invoke an object's special powers. Autocompletes. Default key is '{\tt M-i}'. %.lp \item[\tb{\#jump}] -Jump to another location. +Jump to another location. Autocompletes. Default key is '{\tt M-j}', and '{\tt j}' if {\it number\verb+_+pad\/} is on. %.lp \item[\tb{\#kick}] -Kick something. +Kick something. Default key is '{\tt \^{}D}', and '{\tt k}' if {\it number\verb+_+pad\/} is on. +%.lp +\item[\tb{\#known}] +Show what object types have been discovered. Default key is '{\tt $\backslash$}'. +%.lp +\item[\tb{\#knownclass}] +Show discovered types for one class of objects. Default key is '{\tt `}'. +%.lp +\item[\tb{\#levelchange}] +Change your experience level. Autocompletes. Wizard-mode only. +%.lp +\item[\tb{\#lightsources}] +Show mobile light sources. Autocompletes. Wizard-mode only. +%.lp +\item[\tb{\#look}] +Look at what is here, under you. Default key is '{\tt :}'. %.lp \item[\tb{\#loot}] Loot a box or bag on the floor beneath you, or the saddle -from a steed standing next to you. +from a steed standing next to you. Autocompletes. +Default key is '{\tt M-l}', and '{\tt l}' if {\it number\verb+_+pad\/} is on. Precede with the `{\tt m}' prefix to skip containers at your location and go directly to removing a saddle. %.lp +\item[\tb{\#monpolycontrol}] +Control monster polymorphs. Autocompletes. Wizard-mode only. +%.lp \item[\tb{\#monster}] Use a monster's special ability (when polymorphed into monster form). +Autocompletes. Default key is '{\tt M-m}'. %.lp \item[\tb{\#name}] -Name a monster, an individual object, or a type of object. Same as `{\tt C}'. +Name a monster, an individual object, or a type of object. Same as `{\tt \#call}'. +Autocompletes. Default keys are '{\tt N}', '{\tt M-n}', and '{\tt M-N}'. %.lp \item[\tb{\#offer}] -Offer a sacrifice to the gods.\\ +Offer a sacrifice to the gods. Autocompletes. Default key is '{\tt M-o}'.\\ %.lp "" You'll need to find an altar to have any chance at success. Corpses of recently killed monsters are the fodder of choice. %.lp +\item[\tb{\#open}] +Open a door. Default key is '{\tt o}'. +%.lp +\item[\tb{\#options}] +Show and change option settings. Default key is '{\tt O}'. +%.lp \item[\tb{\#overview}] Display information you've discovered about the dungeon. Any visited level (unless forgotten due to amnesia) with an annotation is included, and many things (altars, thrones, fountains, and so on; extra stairs leading to another dungeon branch) trigger an automatic annotation. If dungeon overview is chosen during end-of-game disclosure, every visited -level will be included regardless of annotations. +level will be included regardless of annotations. Autocompletes. +Default keys are '{\tt \^{}O}', and '{\tt M-O}'. +%.lp +\item[\tb{\#panic}] +Test the panic routine. Autocompletes. Wizard-mode only. +%.lp +\item[\tb{\#pay}] +Pay your shopping bill. Default key is '{\tt p}'. +%.lp +\item[\tb{\#pickup}] +Pick up things at the current location. Default key is '{\tt ,}'. +%.lp +\item[\tb{\#polyself}] +Polymorph self. Autocompletes. Wizard-mode only. %.lp \item[\tb{\#pray}] -Pray to the gods for help.\\ +Pray to the gods for help. Autocompletes. Default key is '{\tt M-p}'.\\ %.lp "" Praying too soon after receiving prior help is a bad idea. (Hint: entering the dungeon alive is treated as having received help. @@ -1173,8 +1272,18 @@ by default, and you can reset the {\it paranoid\verb+_+confirmation\/} option to disable it. %.lp +\item[\tb{\#prevmsg}] +Show previously displayed game messages. Default key is '{\tt \^{}P}'. +%.lp +\item[\tb{\#puton}] +Put on an accessory (ring, amulet, etc). Default key is '{\tt P}'. +%.lp +\item[\tb{\#quaff}] +Quaff (drink) something. Default key is '{\tt q}'. +%.lp \item[\tb{\#quit}] -Quit the program without saving your game.\\ +Quit the program without saving your game. Autocompletes. +Default key is '{\tt M-q}'. %.lp "" Since using this command by accident would throw away the current game, you are asked to confirm your intent before quitting. By default a @@ -1182,40 +1291,194 @@ response of `{\tt y}' acknowledges that intent. You can set the {\it paranoid\verb+_+confirmation\/} option to require a response of ``{\tt yes}'' instead. %.lp +\item[\tb{\#quiver}] +Select ammunition for quiver. Default key is '{\tt Q}'. +%.lp +\item[\tb{\#read}] +Read a scroll, a spellbook, or something else. Default key is '{\tt r}'. +%.lp +\item[\tb{\#redraw}] +Redraw the screen. Default key is '{\tt \^{}R}', and '{\tt \^{}L}' if {\it number\verb+_+pad\/} is on. +%.lp +\item[\tb{\#remove}] +Remove an accessory (ring, amulet, etc). Default key is '{\tt R}'. +%.lp \item[\tb{\#ride}] -Ride (or stop riding) a saddled creature. +Ride (or stop riding) a saddled creature. Autocompletes. +Default key is '{\tt M-R}'. %.lp \item[\tb{\#rub}] -Rub a lamp or a stone. +Rub a lamp or a stone. Autocompletes. Default key is '{\tt M-r}'. +%.lp +\item[\tb{\#save}] +Save the game. Default key is '{\tt S}'. +%.lp +\item[\tb{\#search}] +Search for traps and secret doors around you. Default key is '{\tt s}'. +%.lp +\item[\tb{\#seeall}] +Show all equipment in use. Default key is '{\tt *}'. +%.lp +\item[\tb{\#seeamulet}] +Show the amulet currently worn. Default key is '{\tt "}'. +%.lp +\item[\tb{\#seearmor}] +Show the armor currently worn. Default key is '{\tt [}'. +%.lp +\item[\tb{\#seegold}] +Count your gold. Default key is '{\tt \$}'. +%.lp +\item[\tb{\#seenv}] +Show seen vectors. Autocompletes. Wizard-mode only. +%.lp +\item[\tb{\#seerings}] +Show the ring(s) currently worn. Default key is '{\tt =}'. +%.lp +\item[\tb{\#seespells}] +List and reorder known spells. Default key is '{\tt +}'. +%.lp +\item[\tb{\#seetools}] +Show the tools currently in use. Default key is '{\tt (}'. +%.lp +\item[\tb{\#seetrap}] +Show the type of a trap near you. Default key is '{\tt \^{}}'. +%.lp +\item[\tb{\#seeweapon}] +Show the weapon currently wielded. Default key is '{\tt )}'. +%.lp +\item[\tb{\#shell}] +Do a shell escape. Default key is '{\tt !}'. %.lp \item[\tb{\#sit}] -Sit down. +Sit down. Autocompletes. Default key is '{\tt M-s}'. +%.lp +\item[\tb{\#stats}] +Show memory statistics. Autocompletes. Wizard-mode only. +%.lp +\item[\tb{\#suspend}] +Suspend the game. Default key is '{\tt \^{}Z}'. +%.lp +\item[\tb{\#swap}] +Swap wielded and secondary weapons. Default key is '{\tt x}'. +%.lp +\item[\tb{\#takeoff}] +Take off one piece of armor. Default key is '{\tt T}'. +%.lp +\item[\tb{\#takeoffall}] +Remove all armor. Default key is '{\tt A}'. +%.lp +\item[\tb{\#teleport}] +Teleport around the level. Default key is '{\tt \^{}T}'. %.lp \item[\tb{\#terrain}] Show bare map without displaying monsters, objects, or traps. +Autocompletes. +%.lp +\item[\tb{\#throw}] +Throw something. Default key is '{\tt t}'. +%.lp +\item[\tb{\#timeout}] +Look at the timeout queue. Autocompletes. Wizard-mode only. +%.lp \item[\tb{\#tip}] Tip over a container (bag or box) to pour out its contents. +Autocompletes. Default key is '{\tt M-T}'. +%.lp +\item[\tb{\#travel}] +Travel to a specific location on the map. Default key is '{\tt _}'. %.lp \item[\tb{\#turn}] -Turn undead. +Turn undead away. Autocompletes. Default key is '{\tt M-t}'. %.lp \item[\tb{\#twoweapon}] -Toggle two-weapon combat on or off.\\ +Toggle two-weapon combat on or off. Autocompletes. Default keys are '{\tt X}', +and '{\tt M-2}'. %.lp "" Note that you must use suitable weapons for this type of combat, or it will be automatically turned off. %.lp \item[\tb{\#untrap}] -Untrap something (trap, door, or chest).\\ +Untrap something (trap, door, or chest). Default key is '{\tt M-u}', and '{\tt u}' if {\it number\verb+_+pad\/} is on. %.lp "" In some circumstancs it can also be used to rescue trapped monsters. %.lp +\item[\tb{\#up}] +Go up a staircase. Default key is '{\tt <}'. +%.lp +\item[\tb{\#vanquished}] +List vanquished monsters. Autocompletes. Wizard-mode only. +%.lp \item[\tb{\#version}] Print compile time options for this version of {\it NetHack}. +Autocompletes. Default key is '{\tt M-v}'. +%.lp +\item[\tb{\#versionshort}] +Show version string. Default key is '{\tt v}'. +%.lp +\item[\tb{\#vision}] +Show vision array. Autocompletes. Wizard-mode only. +%.lp +\item[\tb{\#wait}] +Rest one move while doing nothing. Default key is '{\tt .}', and '{\tt{ }}' if {\it rest\verb+_+on\verb+_+space\/} is on. +%.lp +\item[\tb{\#wear}] +Wear a piece of armor. Default key is '{\tt W}'. +%.lp +\item[\tb{\#whatdoes}] +Tell what a key does. Default key is '{\tt \&}'. +%.lp +\item[\tb{\#whatis}] +Show what type of thing a symbol corresponds to. Default key is '{\tt /}'. +%.lp +\item[\tb{\#wield}] +Wield a weapon. Default key is '{\tt w}'. %.lp \item[\tb{\#wipe}] -Wipe off your face. +Wipe off your face. Autocompletes. Default key is '{\tt M-w}'. +%.lp +\item[\tb{\#wizdebug\verb+_+bury}] +Bury objects under and around you. Autocompletes. Wizard-mode only. +%.lp +\item[\tb{\#wizdebug\verb+_+traveldisplay}] +Toggle travel display. Autocompletes. Wizard-mode only. +%.lp +\item[\tb{\#wizdetect}] +Search a room. Autocompletes. Wizard-mode only. Default key is '{\tt \^{}E}'. +%.lp +\item[\tb{\#wizgenesis}] +Create a monster. Autocompletes. Wizard-mode only. Default key is '{\tt \^{}G}'. +%.lp +\item[\tb{\#wizidentify}] +Identify all items in inventory. Autocompletes. Wizard-mode only. +Default key is '{\tt \^{}I}'. +%.lp +\item[\tb{\#wizintrinsic}] +Set intrinsic. Autocompletes. Wizard-mode only. +%.lp +\item[\tb{\#wizlevelport}] +Teleport to another level. Autocompletes. Wizard-mode only. Default key is '{\tt \^{}V}'. +%.lp +\item[\tb{\#wizmap}] +Map the level. Autocompletes. Wizard-mode only. Default key is '{\tt \^{}F}'. +%.lp +\item[\tb{\#wizrumorcheck}] +Verify rumor boundaries. Autocompletes. Wizard-mode only. +%.lp +\item[\tb{\#wizsmell}] +Smell monster. Autocompletes. Wizard-mode only. +%.lp +\item[\tb{\#wizwhere}] +Show locations of special levels. Autocompletes. Wizard-mode only. +%.lp +\item[\tb{\#wizwish}] +Wish for something. Autocompletes. Wizard-mode only. Default key is '{\tt \^{}W}'. +%.lp +\item[\tb{\#wmode}] +Show wall modes. Autocompletes. Wizard-mode only. +%.lp +\item[\tb{\#zap}] +Zap a wand. Default key is '{\tt z}'. %.lp \item[\tb{\#?}] Help menu: get the list of available extended commands. @@ -2465,10 +2728,42 @@ HACKDIR, must be writeable. The location that a record of game aborts and self-diagnosed game problems is kept. Defaults to HACKDIR, must be writeable. %.lp +\item[\bb{AUTOCOMPLETE}] +Enable or disable an extended command autocompletion. +Autocompletion has no effect for the X11 windowport. +You can specify multiple autocompletions. To enable +autocompletion, list the extended command. Prefix the +command with ``{{\tt !}}'' to disable the autocompletion +for that command. + +%.pg +Example: +%.sd +\begin{verbatim} + AUTOCOMPLETE=zap,!annotate +\end{verbatim} +%.ed + +%.lp \item[\bb{AUTOPICKUP\_EXCEPTION}] Set exceptions to the {{\it pickup\_types\/}} option. See the ``Configuring Autopickup Exceptions'' section. %.lp +\item[\bb{BINDINGS}] +Change the key bindings of some special keys, menu accelerators, or +extended commands. You can specify multiple bindings. Format is key +followed by the command, separated by a colon. +See the ``Changing Key Bindings`` section for more information. + +%.pg +Example: +%.sd +\begin{verbatim} + BIND=^X:getpos.autodescribe +\end{verbatim} +%.ed + +%.lp \item[\bb{MSGTYPE}] Change the way messages are shown in the top status line. See the ``Configuring Message Types`` section. @@ -3552,6 +3847,148 @@ The last example results in the exclusion of items known to be cursed from autopickup. %.lp + +%.hn 2 +\subsection*{Changing Key Bindings} + +%.pg +It is possible to change the default key bindings of some special commands, +menu accelerator keys, and extended commands, by using BIND stanzas in the +configuration file. Format is key, followed by the command to bind to, separated +by a colon. The key can be a single character (``{\tt x}''), +a control key (``{\tt \^{}X}'', ``{\tt C-x}''), a meta key (``{\tt M-x}''), +or a three-digit decimal ASCII code. + +%.pg +For example: + +\begin{verbatim} + BIND=^X:getpos.autodescribe + BIND={:menu_first_page + BIND=v:loot +\end{verbatim} + +\blist{} +%.lp "Extended command keys" +\item[\tb{Extended command keys}] +You can bind multiple keys to the same extended command. Unbind a key by +using ``{\tt nothing}'' as the extended command to bind to. You can also bind +the ``{\tt }'', ``{\tt }'', and ``{\tt }'' keys. + +%.lp "Menu accelerator keys" +\item[\tb{Menu accelerator keys}] +The menu control or accelerator keys can also be rebound via OPTIONS-lines +in the config file. You cannot bind object symbols into menu accelerators. + +%.lp "Special command keys" +\item[\tb{Special command keys}] +Below are the special commands you can rebind. Some of them can be bound to +same keys with no problems, others are in the same "context", and if bound +to same keys, only one of those commands will be available. Special command +can only be bound to a single key. + +%.pg +\blist{} +%.lp +\item{\bb{count}} +Prefix key to start a count, to repeat a command this many times. With {\it number\verb+_+pad\/} only. Default is ``{\tt n}''. +%.lp +\item{\bb{doinv}} +Show inventory. With {\it number\verb+_+pad\/} only. Default is ``{\tt 0}''. +%.lp +\item{\bb{fight}} +Prefix key to force fight a direction. Default is ``{\tt F}''. +%.lp +\item{\bb{fight.numpad}} +Prefix key to force fight a direction. With {\it number\verb+_+pad\/} only. Default is ``{\tt -}''. +%.lp +\item{\bb{getdir.help}} +When asked for a direction, the key to show the help. Default is ``{\tt ?}''. +%.lp +\item{\bb{getdir.self}} +When asked for a direction, the key to target yourself. Default is ``{\tt .}''. +%.lp +\item{\bb{getdir.self2}} +When asked for a direction, the key to target yourself. Default is ``{\tt s}''. +%.lp +\item{\bb{getpos.autodescribe}} +When asked for a location, the key to toggle {\it autodescribe\/}. Default is ``{\tt \#}''. +%.lp +\item{\bb{getpos.door.next}} +When asked for a location, the key to go to next closest door or doorway. Default is ``{\tt d}''. +%.lp +\item{\bb{getpos.door.prev}} +When asked for a location, the key to go to previous closest door or doorway. Default is ``{\tt D}''. +%.lp +\item{\bb{getpos.help}} +When asked for a location, the key to show help. Default is ``{\tt ?}''. +%.lp +\item{\bb{getpos.mon.next}} +When asked for a location, the key to go to next closest monster. Default is ``{\tt m}''. +%.lp +\item{\bb{getpos.mon.prev}} +When asked for a location, the key to go to previous closest monster. Default is ``{\tt M}''. +%.lp +\item{\bb{getpos.obj.next}} +When asked for a location, the key to go to next closest object. Default is ``{\tt o}''. +%.lp +\item{\bb{getpos.obj.prev}} +When asked for a location, the key to go to previous closest object. Default is ``{\tt O}''. +%.lp +\item{\bb{getpos.pick}} +When asked for a location, the key to choose the location, and possibly ask for more info. Default is ``{\tt .}''. +%.lp +\item{\bb{getpos.pick.once}} +When asked for a location, the key to choose the location, and skip asking for more info. Default is ``{\tt ,}''. +%.lp +\item{\bb{getpos.pick.quick}} +When asked for a location, the key to choose the location, skip asking for more info, and exit the location asking loop. Default is ``{\tt ;}''. +%.lp +\item{\bb{getpos.pick.verbose}} +When asked for a location, the key to choose the location, and show more info without asking. Default is ``{\tt :}''. +%.lp +\item{\bb{getpos.self}} +When asked for a location, the key to go to your location. Default is ``{\tt @}''. +%.lp +\item{\bb{getpos.unexplored.next}} +When asked for a location, the key to go to next closest unexplored location. Default is ``{\tt x}''. +%.lp +\item{\bb{getpos.unexplored.prev}} +When asked for a location, the key to go to previous closest unexplored location. Default is ``{\tt X}''. +%.lp +\item{\bb{getpos.valid}} +When asked for a location, the key to go to show valid target locations. Default is ``{\tt \$}''. +%.lp +\item{\bb{nopickup}} +Prefix key to move without picking up items. Default is ``{\tt m}''. +%.lp +\item{\bb{redraw}} +Key to redraw the screen. Default is ``{\tt \^{}R}''. +%.lp +\item{\bb{redraw.numpad}} +Key to redraw the screen. With {\it number\verb+_+pad\/} only. Default is ``{\tt \^{}L}''. +%.lp +\item{\bb{repeat}} +Key to repeat previous command. Default is ``{\tt \^{}A}''. +%.lp +\item{\bb{reqmenu}} +Prefix key to request menu from some commands. Default is ``{\tt m}''. +%.lp +\item{\bb{run}} +Prefix key to run towards a direction. Default is ``{\tt G}''. +%.lp +\item{\bb{run.nopickup}} +Prefix key to run towards a direction without picking up items on the way. Default is ``{\tt M}''. +%.lp +\item{\bb{run.numpad}} +Prefix key to run towards a direction. With {\it number\verb+_+pad\/} only. Default is ``{\tt 5}''. +%.lp +\item{\bb{rush}} +Prefix key to rush towards a direction. Default is ``{\tt g}''. +\elist +\elist + + %.hn 2 \subsection*{Configuring Message Types} @@ -3999,7 +4436,7 @@ These co-ordinates are often useful in giving players a better sense of the overall location of items on the screen. %.pg NetHack can also be compiled with support for sending the game messages -to an external program, such as a text-to-speech synthesizer. If the #version +to an external program, such as a text-to-speech synthesizer. If the \#version extended command shows "external program as a message handler", your NetHack has been compiled with the capability. When compiling NetHack from source on Linux and other POSIX systems, define {\it MSGHANDLER\/} to enable it. To use diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 9114150f..30379730 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -530,6 +530,7 @@ Ray Chason's MS-DOS port restored to functionality with credit to Reddit user b_helyer for the fix to sys/share/pcmain.c Ray Chason's MSDOS port support for some VESA modes Darshan Shaligram's pet ranged attack +Jason Dorje Short's key rebinding Code Cleanup and Reorganization diff --git a/include/extern.h b/include/extern.h index e2f4b622..7442d998 100644 --- a/include/extern.h +++ b/include/extern.h @@ -184,7 +184,10 @@ E void FDECL(set_occupation, (int (*)(void), const char *, int)); E char NDECL(pgetchar); E void FDECL(pushch, (CHAR_P)); E void FDECL(savech, (CHAR_P)); -E void NDECL(add_debug_extended_commands); +E const char *FDECL(key2extcmddesc, (UCHAR_P)); +E boolean FDECL(bind_specialkey, (UCHAR_P, char *)); +E char FDECL(txt2key, (char *)); +E void FDECL(parseautocomplete, (char *, BOOLEAN_P)); E void FDECL(reset_commands, (BOOLEAN_P)); E void FDECL(rhack, (char *)); E int NDECL(doextlist); @@ -193,6 +196,8 @@ E int NDECL(enter_explore_mode); E void FDECL(enlightenment, (int, int)); E void FDECL(youhiding, (BOOLEAN_P, int)); E void FDECL(show_conduct, (int)); +E void FDECL(bind_key, (UCHAR_P, char *)); +E void NDECL(dokeylist); E int FDECL(xytod, (SCHAR_P, SCHAR_P)); E void FDECL(dtoxy, (coord *, int)); E int FDECL(movecmd, (CHAR_P)); @@ -211,6 +216,7 @@ E void NDECL(end_of_input); #endif E char NDECL(readchar); E void NDECL(sanity_check); +E char* FDECL(key2txt, (UCHAR_P, char *)); E char FDECL(yn_function, (const char *, const char *, CHAR_P)); E boolean FDECL(paranoid_query, (BOOLEAN_P, const char *)); @@ -847,6 +853,7 @@ E char *FDECL(lcase, (char *)); E char *FDECL(ucase, (char *)); E char *FDECL(upstart, (char *)); E char *FDECL(mungspaces, (char *)); +E char *FDECL(trimspaces, (char *)); E char *FDECL(strip_newline, (char *)); E char *FDECL(eos, (char *)); E boolean FDECL(str_end_is, (const char *, const char *)); @@ -1661,6 +1668,7 @@ E void FDECL(next_opt, (winid, const char *)); E int FDECL(fruitadd, (char *, struct fruit *)); E int FDECL(choose_classes_menu, (const char *, int, BOOLEAN_P, char *, char *)); +E void FDECL(parsebindings, (char *)); E void FDECL(add_menu_cmd_alias, (CHAR_P, CHAR_P)); E char FDECL(map_menu_cmd, (CHAR_P)); E void FDECL(assign_warnings, (uchar *)); diff --git a/include/flag.h b/include/flag.h index 47139d35..bd34fed4 100644 --- a/include/flag.h +++ b/include/flag.h @@ -424,9 +424,54 @@ extern NEARDATA struct instance_flags iflags; #ifdef NHSTDC /* forward declaration sufficient to declare pointers */ -struct func_tab; /* from func_tab.h */ +struct ext_func_tab; /* from func_tab.h */ #endif +/* special key functions */ +enum nh_keyfunc { + NHKF_ESC = 0, + NHKF_DOAGAIN, + + NHKF_REQMENU, + + /* run ... clicklook need to be in a continuous block */ + NHKF_RUN, + NHKF_RUN2, + NHKF_RUSH, + NHKF_FIGHT, + NHKF_FIGHT2, + NHKF_NOPICKUP, + NHKF_RUN_NOPICKUP, + NHKF_DOINV, + NHKF_TRAVEL, + NHKF_CLICKLOOK, + + NHKF_REDRAW, + NHKF_REDRAW2, + NHKF_GETDIR_SELF, + NHKF_GETDIR_SELF2, + NHKF_GETDIR_HELP, + NHKF_COUNT, + NHKF_GETPOS_SELF, + NHKF_GETPOS_PICK, + NHKF_GETPOS_PICK_Q, /* quick */ + NHKF_GETPOS_PICK_O, /* once */ + NHKF_GETPOS_PICK_V, /* verbose */ + NHKF_GETPOS_SHOWVALID, + NHKF_GETPOS_AUTODESC, + NHKF_GETPOS_MON_NEXT, + NHKF_GETPOS_MON_PREV, + NHKF_GETPOS_OBJ_NEXT, + NHKF_GETPOS_OBJ_PREV, + NHKF_GETPOS_DOOR_NEXT, + NHKF_GETPOS_DOOR_PREV, + NHKF_GETPOS_UNEX_NEXT, + NHKF_GETPOS_UNEX_PREV, + NHKF_GETPOS_HELP, + + NUM_NHKF +}; + /* commands[] is used to directly access cmdlist[] instead of looping through it to find the entry for a given input character; move_X is the character used for moving one step in direction X; @@ -443,7 +488,8 @@ struct cmd { char move_W, move_NW, move_N, move_NE, move_E, move_SE, move_S, move_SW; const char *dirchars; /* current movement/direction characters */ const char *alphadirchars; /* same as dirchars if !numpad */ - const struct func_tab *commands[256]; /* indexed by input character */ + const struct ext_func_tab *commands[256]; /* indexed by input character */ + char spkeys[NUM_NHKF]; }; extern NEARDATA struct cmd Cmd; diff --git a/include/func_tab.h b/include/func_tab.h index bb5c16b4..c0570d96 100644 --- a/include/func_tab.h +++ b/include/func_tab.h @@ -5,17 +5,18 @@ #ifndef FUNC_TAB_H #define FUNC_TAB_H -struct func_tab { - char f_char; - boolean can_if_buried; - int NDECL((*f_funct)); - const char *f_text; -}; +/* extended command flags */ +#define IFBURIED 0x01 /* can do command when buried */ +#define AUTOCOMPLETE 0x02 /* command autocompletes */ +#define WIZMODECMD 0x04 /* wizard-mode command */ +#define GENERALCMD 0x08 /* general command, does not take game time */ struct ext_func_tab { + uchar key; const char *ef_txt, *ef_desc; int NDECL((*ef_funct)); - boolean can_if_buried; + int flags; + const char *f_text; }; extern struct ext_func_tab extcmdlist[]; diff --git a/src/allmain.c b/src/allmain.c index 3aecf399..de1fb155 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -34,9 +34,6 @@ boolean resuming; monstr_init(); /* monster strengths */ objects_init(); - if (wizard) - add_debug_extended_commands(); - /* if a save file created in normal mode is now being restored in explore mode, treat it as normal restore followed by 'X' command to use up the save file and require confirmation for explore mode */ diff --git a/src/cmd.c b/src/cmd.c index 31e2a4bd..e40150e1 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -117,7 +117,6 @@ STATIC_PTR int NDECL(doprev_message); STATIC_PTR int NDECL(timed_occupation); STATIC_PTR int NDECL(doextcmd); STATIC_PTR int NDECL(domonability); -STATIC_PTR int NDECL(dooverview_or_wiz_where); STATIC_PTR int NDECL(dotravel); STATIC_PTR int NDECL(doterrain); STATIC_PTR int NDECL(wiz_wish); @@ -323,8 +322,13 @@ doextcmd(VOID_ARGS) return 0; /* quit */ func = extcmdlist[idx].ef_funct; + if (!wizard && (extcmdlist[idx].flags & WIZMODECMD)) { + You("can't do that."); + return 0; + } if (iflags.menu_requested && !accept_menu_prefix(func)) { - pline("'m' prefix has no effect for this command."); + pline("'%s' prefix has no effect for this command.", + visctrl(Cmd.spkeys[NHKF_REQMENU])); iflags.menu_requested = FALSE; } retval = (*func)(); @@ -349,16 +353,23 @@ doextlist(VOID_ARGS) putstr(datawin, 0, ""); for (efp = extcmdlist; efp->ef_txt; efp++) { - Sprintf(buf, " %-15s - %s.", efp->ef_txt, efp->ef_desc); + if (!wizard && (efp->flags & WIZMODECMD)) + continue; + Sprintf(buf, " %-15s %c %s.", + efp->ef_txt, + (efp->flags & AUTOCOMPLETE) ? '*' : ' ', + efp->ef_desc); putstr(datawin, 0, buf); } + putstr(datawin, 0, ""); + putstr(datawin, 0, " Commands marked with a * will be autocompleted."); display_nhwindow(datawin, FALSE); destroy_nhwindow(datawin); return 0; } #ifdef TTY_GRAPHICS -#define MAX_EXT_CMD 50 /* Change if we ever have > 50 ext cmds */ +#define MAX_EXT_CMD 200 /* Change if we ever have more ext cmds */ /* * This is currently used only by the tty port and is @@ -391,6 +402,9 @@ extcmd_via_menu() any = zeroany; /* populate choices */ for (efp = extcmdlist; efp->ef_txt; efp++) { + if (!(efp->flags & AUTOCOMPLETE) + || (!wizard && (efp->flags & WIZMODECMD))) + continue; if (!matchlevel || !strncmp(efp->ef_txt, cbuf, matchlevel)) { choices[i] = efp; if ((int) strlen(efp->ef_desc) > biggest) { @@ -413,7 +427,9 @@ extcmd_via_menu() /* if we're down to one, we have our selection so get out of here */ if (nchoices == 1) { for (i = 0; extcmdlist[i].ef_txt != (char *) 0; i++) - if (!strncmpi(extcmdlist[i].ef_txt, cbuf, matchlevel)) { + if ((extcmdlist[i].flags & AUTOCOMPLETE) + && !(!wizard && (extcmdlist[i].flags & WIZMODECMD)) + && !strncmpi(extcmdlist[i].ef_txt, cbuf, matchlevel)) { ret = i; break; } @@ -562,16 +578,6 @@ enter_explore_mode(VOID_ARGS) return 0; } -STATIC_PTR int -dooverview_or_wiz_where(VOID_ARGS) -{ - if (wizard) - return wiz_where(); - else - dooverview(); - return 0; -} - /* ^W command - wish for something */ STATIC_PTR int wiz_wish(VOID_ARGS) /* Unlimited wishes for debug mode by Paul Polderman */ @@ -2667,6 +2673,12 @@ int final; en_win = WIN_ERR; } +/* Macros for meta and ctrl modifiers: + * M and C return the meta/ctrl code for the given character; + * e.g., (C('c') is ctrl-c + * ISMETA and ISCTRL return TRUE iff the code is a meta/ctrl code + * UNMETA and UNCTRL are the opposite of M/C and return the key for a given + * meta/ctrl code. */ #ifndef M #ifndef NHSTDC #define M(c) (0x80 | (c)) @@ -2674,254 +2686,350 @@ int final; #define M(c) ((c) -128) #endif /* NHSTDC */ #endif +#define ISMETA(c) (((c) & 0x80) != 0) +#define UNMETA(c) ((c) & 0x7f) + #ifndef C #define C(c) (0x1f & (c)) #endif - -static const struct func_tab cmdlist[] = { - { C('d'), FALSE, dokick }, /* "D" is for door!...? Msg is in dokick.c */ - { C('e'), TRUE, wiz_detect }, - { C('f'), TRUE, wiz_map }, - { C('g'), TRUE, wiz_genesis }, - { C('i'), TRUE, wiz_identify }, - { C('l'), TRUE, doredraw }, /* if number_pad is set */ - { C('n'), TRUE, donamelevel }, /* if number_pad is set */ - { C('o'), TRUE, dooverview_or_wiz_where }, /* depends on wizard status */ - { C('p'), TRUE, doprev_message }, - { C('r'), TRUE, doredraw }, - { C('t'), TRUE, dotele }, - { C('v'), TRUE, wiz_level_tele }, - { C('w'), TRUE, wiz_wish }, - { C('x'), TRUE, doattributes }, - { C('z'), TRUE, dosuspend_core }, - { 'a', FALSE, doapply }, - { 'A', FALSE, doddoremarm }, - { M('a'), TRUE, doorganize }, - { M('A'), TRUE, donamelevel }, /* #annotate */ - /* 'b', 'B' : go sw */ - { 'c', FALSE, doclose }, - { 'C', TRUE, docallcmd }, - { M('c'), TRUE, dotalk }, - { M('C'), TRUE, doconduct }, /* #conduct */ - { 'd', FALSE, dodrop }, - { 'D', FALSE, doddrop }, - { M('d'), FALSE, dodip }, - { 'e', FALSE, doeat }, - { 'E', FALSE, doengrave }, - { M('e'), TRUE, enhance_weapon_skill }, - { 'f', FALSE, dofire }, - /* 'F' : fight (one time) */ - { M('f'), FALSE, doforce }, - /* 'g', 'G' : multiple go */ - /* 'h', 'H' : go west */ - { 'h', TRUE, dohelp }, /* if number_pad is set */ - { 'i', TRUE, ddoinv }, - { 'I', TRUE, dotypeinv }, /* Robert Viduya */ - { M('i'), TRUE, doinvoke }, - /* 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N' : move commands */ - { 'j', FALSE, dojump }, /* if number_pad is on */ - { M('j'), FALSE, dojump }, - { 'k', FALSE, dokick }, /* if number_pad is on */ - { 'l', FALSE, doloot }, /* if number_pad is on */ - { M('l'), FALSE, doloot }, - /* 'n' prefixes a count if number_pad is on */ - { M('m'), TRUE, domonability }, - { 'N', TRUE, docallcmd }, /* if number_pad is on */ - { M('n'), TRUE, docallcmd }, - { M('N'), TRUE, docallcmd }, - { 'o', FALSE, doopen }, - { 'O', TRUE, doset }, - { M('o'), FALSE, dosacrifice }, - { M('O'), TRUE, dooverview }, /* #overview */ - { 'p', FALSE, dopay }, - { 'P', FALSE, doputon }, - { M('p'), TRUE, dopray }, - { 'q', FALSE, dodrink }, - { 'Q', FALSE, dowieldquiver }, - { M('q'), TRUE, done2 }, - { 'r', FALSE, doread }, - { 'R', FALSE, doremring }, - { M('r'), FALSE, dorub }, - { M('R'), FALSE, doride }, /* #ride */ - { 's', TRUE, dosearch, "searching" }, - { 'S', TRUE, dosave }, - { M('s'), FALSE, dosit }, - { 't', FALSE, dothrow }, - { 'T', FALSE, dotakeoff }, - { M('t'), TRUE, doturn }, - { M('T'), FALSE, dotip }, /* #tip */ - /* 'u', 'U' : go ne */ - { 'u', FALSE, dountrap }, /* if number_pad is on */ - { M('u'), FALSE, dountrap }, - { 'v', TRUE, doversion }, - { 'V', TRUE, dohistory }, - { M('v'), TRUE, doextversion }, - { 'w', FALSE, dowield }, - { 'W', FALSE, dowear }, - { M('w'), FALSE, dowipe }, - { 'x', FALSE, doswapweapon }, - { 'X', FALSE, dotwoweapon }, - /* 'y', 'Y' : go nw */ - { 'z', FALSE, dozap }, - { 'Z', TRUE, docast }, - { '<', FALSE, doup }, - { '>', FALSE, dodown }, - { '/', TRUE, dowhatis }, - { '&', TRUE, dowhatdoes }, - { '?', TRUE, dohelp }, - { M('?'), TRUE, doextlist }, -#ifdef SHELL - { '!', TRUE, dosh }, -#endif - { '.', TRUE, donull, "waiting" }, - { ' ', TRUE, donull, "waiting" }, - { ',', FALSE, dopickup }, - { ':', TRUE, dolook }, - { ';', TRUE, doquickwhatis }, - { '^', TRUE, doidtrap }, - { '\\', TRUE, dodiscovered }, /* Robert Viduya */ - { '`', TRUE, doclassdisco }, - { '@', TRUE, dotogglepickup }, - { M('2'), FALSE, dotwoweapon }, - { WEAPON_SYM, TRUE, doprwep }, - { ARMOR_SYM, TRUE, doprarm }, - { RING_SYM, TRUE, doprring }, - { AMULET_SYM, TRUE, dopramulet }, - { TOOL_SYM, TRUE, doprtool }, - { '*', TRUE, doprinuse }, /* inventory of all equipment in use */ - { GOLD_SYM, TRUE, doprgold }, - { SPBOOK_SYM, TRUE, dovspell }, /* Mike Stephenson */ - { '#', TRUE, doextcmd }, - { '_', TRUE, dotravel }, - { 0, 0, 0, 0 } -}; +#define ISCTRL(c) ((uchar)(c) < 0x20) +#define UNCTRL(c) (ISCTRL(c) ? (0x60 | (c)) : (c)) struct ext_func_tab extcmdlist[] = { - { "adjust", "adjust inventory letters", doorganize, TRUE }, - { "annotate", "name current level", donamelevel, TRUE }, - { "chat", "talk to someone", dotalk, TRUE }, /* converse? */ - { "conduct", "list voluntary challenges you have maintained", doconduct, - TRUE }, - { "dip", "dip an object into something", dodip, FALSE }, - { "enhance", "advance or check weapon and spell skills", - enhance_weapon_skill, TRUE }, - { "exploremode", "enter explore mode", enter_explore_mode, TRUE }, - { "force", "force a lock", doforce, FALSE }, - { "invoke", "invoke an object's powers", doinvoke, TRUE }, - { "jump", "jump to a location", dojump, FALSE }, - { "kick", "kick something", dokick, FALSE }, - { "loot", "loot a box on the floor", doloot, FALSE }, - { "monster", "use a monster's special ability", domonability, TRUE }, - { "name", "name a monster or an object", docallcmd, TRUE }, - { "offer", "offer a sacrifice to the gods", dosacrifice, FALSE }, - { "overview", "show an overview of the dungeon", dooverview, TRUE }, - { "pray", "pray to the gods for help", dopray, TRUE }, - { "quit", "exit without saving current game", done2, TRUE }, - { "ride", "ride (or stop riding) a monster", doride, FALSE }, - { "rub", "rub a lamp or a stone", dorub, FALSE }, - { "sit", "sit down", dosit, FALSE }, - { "terrain", "show map without obstructions", doterrain, TRUE }, - { "tip", "empty a container", dotip, FALSE }, - { "turn", "turn undead", doturn, TRUE }, - { "twoweapon", "toggle two-weapon combat", dotwoweapon, FALSE }, - { "untrap", "untrap something", dountrap, FALSE }, - { "version", "list compile time options for this version of NetHack", - doextversion, TRUE }, - { "wipe", "wipe off your face", dowipe, FALSE }, - { "?", "get this list of extended commands", doextlist, TRUE }, - /* - * There must be a blank entry here for every entry in the table - * below. - */ - { (char *) 0, (char *) 0, donull, TRUE }, /* levelchange */ - { (char *) 0, (char *) 0, donull, TRUE }, /* lightsources */ + { '#', "#", "perform an extended command", doextcmd, IFBURIED|GENERALCMD }, + { M('?'), "?", "get this list of extended commands", doextlist, IFBURIED|AUTOCOMPLETE|GENERALCMD }, + { M('a'), "adjust", "adjust inventory letters", doorganize, IFBURIED|AUTOCOMPLETE }, + { M('A'), "annotate", "name current level", donamelevel, IFBURIED|AUTOCOMPLETE }, + { 'a', "apply", "apply (use) a tool (pick-axe, key, lamp...)", doapply }, + { C('x'), "attributes", "show your attributes", doattributes, IFBURIED }, + { '@', "autopickup", "toggle the pickup option on/off", dotogglepickup, IFBURIED }, + { 'C', "call", "call (name) something", docallcmd, IFBURIED }, + { 'Z', "cast", "zap (cast) a spell", docast, IFBURIED }, + { M('c'), "chat", "talk to someone", dotalk, IFBURIED|AUTOCOMPLETE }, + { 'c', "close", "close a door", doclose }, + { M('C'), "conduct", "list voluntary challenges you have maintained", doconduct, IFBURIED|AUTOCOMPLETE }, + { M('d'), "dip", "dip an object into something", dodip, AUTOCOMPLETE }, + { '>', "down", "go down a staircase", dodown }, + { 'd', "drop", "drop an item", dodrop }, + { 'D', "droptype", "drop specific item types", doddrop }, + { 'e', "eat", "eat something", doeat }, + { 'E', "engrave", "engrave writing on the floor", doengrave }, + { M('e'), "enhance", "advance or check weapon and spell skills", enhance_weapon_skill, IFBURIED|AUTOCOMPLETE }, + { '\0', "exploremode", "enter explore (discovery) mode", enter_explore_mode, IFBURIED }, + { 'f', "fire", "fire ammunition from quiver", dofire }, + { M('f'), "force", "force a lock", doforce, AUTOCOMPLETE }, + { ';', "glance", "show what type of thing a map symbol corresponds to", doquickwhatis, IFBURIED|GENERALCMD }, + { '?', "help", "give a help message", dohelp, IFBURIED|GENERALCMD }, + { 'V', "history", "show long version and game history", dohistory, IFBURIED|GENERALCMD }, + { 'i', "inventory", "show your inventory", ddoinv, IFBURIED }, + { 'I', "inventtype", "inventory specific item types", dotypeinv, IFBURIED }, + { M('i'), "invoke", "invoke an object's special powers", doinvoke, IFBURIED|AUTOCOMPLETE }, + { M('j'), "jump", "jump to another location", dojump, AUTOCOMPLETE }, + { C('d'), "kick", "kick something", dokick }, + { '\\', "known", "show what object types have been discovered", dodiscovered, IFBURIED|GENERALCMD }, + { '`', "knownclass", "show discovered types for one class of objects", doclassdisco, IFBURIED|GENERALCMD }, + { '\0', "levelchange", "change experience level", wiz_level_change, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { '\0', "lightsources", "show mobile light sources", wiz_light_sources, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { ':', "look", "look at what is here", dolook, IFBURIED }, + { M('l'), "loot", "loot a box on the floor", doloot, AUTOCOMPLETE }, #ifdef DEBUG_MIGRATING_MONS - { (char *) 0, (char *) 0, donull, TRUE }, /* migratemons */ + { '\0', "migratemons", "migrate n random monsters", wiz_migrate_mons, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, #endif - { (char *) 0, (char *) 0, donull, TRUE }, /* monpolycontrol */ - { (char *) 0, (char *) 0, donull, TRUE }, /* panic */ - { (char *) 0, (char *) 0, donull, TRUE }, /* polyself */ + { '\0', "monpolycontrol", "control monster polymorphs", wiz_mon_polycontrol, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { M('m'), "monster", "use a monster's special ability", domonability, IFBURIED|AUTOCOMPLETE }, + { 'N', "name", "name a monster or an object", docallcmd, IFBURIED|AUTOCOMPLETE }, + { M('o'), "offer", "offer a sacrifice to the gods", dosacrifice, AUTOCOMPLETE }, + { 'o', "open", "open a door", doopen }, + { 'O', "options", "show option settings, possibly change them", doset, IFBURIED|GENERALCMD }, + { C('o'), "overview", "show a summary of the explored dungeon", dooverview, IFBURIED|AUTOCOMPLETE }, + { '\0', "panic", "test panic routine (fatal to game)", wiz_panic, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { 'p', "pay", "pay your shopping bill", dopay }, + { ',', "pickup", "pick up things at the current location", dopickup }, + { '\0', "polyself", "polymorph self", wiz_polyself, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, #ifdef PORT_DEBUG - { (char *) 0, (char *) 0, donull, TRUE }, /* portdebug */ + { '\0', "portdebug", "wizard port debug command", wiz_port_debug, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, #endif - { (char *) 0, (char *) 0, donull, TRUE }, /* seenv */ - { (char *) 0, (char *) 0, donull, TRUE }, /* stats */ - { (char *) 0, (char *) 0, donull, TRUE }, /* timeout */ - { (char *) 0, (char *) 0, donull, TRUE }, /* vanquished */ - { (char *) 0, (char *) 0, donull, TRUE }, /* vision */ - { (char *) 0, (char *) 0, donull, TRUE }, /* wizsmell */ - { (char *) 0, (char *) 0, donull, TRUE }, /* wizintrinsic */ + { M('p'), "pray", "pray to the gods for help", dopray, IFBURIED|AUTOCOMPLETE }, + { C('p'), "prevmsg", "toggle through previously displayed game messages", doprev_message, IFBURIED|GENERALCMD }, + { 'P', "puton", "put on an accessory (ring, amulet, etc)", doputon }, + { 'q', "quaff", "quaff (drink) something", dodrink }, + { M('q'), "quit", "exit without saving current game", done2, IFBURIED|AUTOCOMPLETE|GENERALCMD }, + { 'Q', "quiver", "select ammunition for quiver", dowieldquiver }, + { 'r', "read", "read a scroll or spellbook", doread }, + { C('r'), "redraw", "redraw screen", doredraw, IFBURIED|GENERALCMD }, + { 'R', "remove", "remove an accessory (ring, amulet, etc)", doremring }, + { M('R'), "ride", "mount or dismount a saddled steed", doride, AUTOCOMPLETE }, + { M('r'), "rub", "rub a lamp or a stone", dorub, AUTOCOMPLETE }, + { 'S', "save", "save the game", dosave, IFBURIED|GENERALCMD }, + { 's', "search", "search for traps and secret doors", dosearch, IFBURIED, "searching" }, + { '*', "seeall", "show all equipment in use", doprinuse, IFBURIED }, + { AMULET_SYM, "seeamulet", "show the amulet currently worn", dopramulet, IFBURIED }, + { ARMOR_SYM, "seearmor", "show the armor currently worn", doprarm, IFBURIED }, + { GOLD_SYM, "seegold", "count your gold", doprgold, IFBURIED }, + { '\0', "seenv", "show seen vectors", wiz_show_seenv, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { RING_SYM, "seerings", "show the ring(s) currently worn", doprring, IFBURIED }, + { SPBOOK_SYM, "seespells", "list and reorder known spells", dovspell, IFBURIED }, + { TOOL_SYM, "seetools", "show the tools currently in use", doprtool, IFBURIED }, + { '^', "seetrap", "show the type of a trap", doidtrap, IFBURIED }, + { WEAPON_SYM, "seeweapon", "show the weapon currently wielded", doprwep, IFBURIED }, +#ifdef SHELL + { '!', "shell", "do a shell escape", dosh, IFBURIED|GENERALCMD }, +#endif /* SHELL */ + { M('s'), "sit", "sit down", dosit, AUTOCOMPLETE }, + { '\0', "stats", "show memory statistics", wiz_show_stats, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, +#ifdef SUSPEND + { C('z'), "suspend", "suspend the game", dosuspend_core, IFBURIED|GENERALCMD }, +#endif /* SUSPEND */ + { 'x', "swap", "swap wielded and secondary weapons", doswapweapon }, + { 'T', "takeoff", "take off one piece of armor", dotakeoff }, + { 'A', "takeoffall", "remove all armor", doddoremarm }, + { C('t'), "teleport", "teleport around the level", dotele, IFBURIED }, + { '\0', "terrain", "show map without obstructions", doterrain, IFBURIED|AUTOCOMPLETE }, + { 't', "throw", "throw something", dothrow }, + { '\0', "timeout", "look at timeout queue", wiz_timeout_queue, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { M('T'), "tip", "empty a container", dotip, AUTOCOMPLETE }, + { '_', "travel", "travel to a specific location on the map", dotravel }, + { M('t'), "turn", "turn undead away", doturn, IFBURIED|AUTOCOMPLETE }, + { 'X', "twoweapon", "toggle two-weapon combat", dotwoweapon, AUTOCOMPLETE }, + { M('u'), "untrap", "untrap something", dountrap, AUTOCOMPLETE }, + { '<', "up", "go up a staircase", doup }, + { '\0', "vanquished", "list vanquished monsters", dovanquished, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { M('v'), "version", "list compile time options for this version of NetHack", doextversion, IFBURIED|AUTOCOMPLETE|GENERALCMD }, + { 'v', "versionshort", "show version", doversion, IFBURIED|GENERALCMD }, + { '\0', "vision", "show vision array", wiz_show_vision, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { '.', "wait", "rest one move while doing nothing", donull, IFBURIED, "waiting" }, + { 'W', "wear", "wear a piece of armor", dowear }, + { '&', "whatdoes", "tell what a command does", dowhatdoes, IFBURIED }, + { '/', "whatis", "show what type of thing a symbol corresponds to", dowhatis, IFBURIED|GENERALCMD }, + { 'w', "wield", "wield (put in use) a weapon", dowield }, + { M('w'), "wipe", "wipe off your face", dowipe, AUTOCOMPLETE }, #ifdef DEBUG - { (char *) 0, (char *) 0, donull, TRUE }, /* wizdebug_traveldisplay */ - { (char *) 0, (char *) 0, donull, TRUE }, /* wizdebug_bury */ + { '\0', "wizdebug_bury", "wizard debug: bury objs under and around you", wiz_debug_cmd_bury, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { '\0', "wizdebug_traveldisplay", "wizard debug: toggle travel display", wiz_debug_cmd_traveldisplay, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, #endif - { (char *) 0, (char *) 0, donull, TRUE }, /* wizrumorcheck */ - { (char *) 0, (char *) 0, donull, TRUE }, /* wmode */ - { (char *) 0, (char *) 0, donull, TRUE } /* sentinel */ + { C('e'), "wizdetect", "search a room", wiz_detect, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { C('g'), "wizgenesis", "create a monster", wiz_genesis, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { C('i'), "wizidentify", "identify all items in inventory", wiz_identify, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { '\0', "wizintrinsic", "set intrinsic", wiz_intrinsic, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { C('v'), "wizlevelport", "teleport to another level", wiz_level_tele, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { C('f'), "wizmap", "map the level", wiz_map, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { '\0', "wizrumorcheck", "verify rumor boundaries", wiz_rumor_check, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { '\0', "wizsmell", "smell monster", wiz_smell, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { '\0', "wizwhere", "show locations of special levels", wiz_where, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { C('w'), "wizwish", "wish for something", wiz_wish, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { '\0', "wmode", "show wall modes", wiz_show_wmodes, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + { 'z', "zap", "zap a wand", dozap }, + + { '\0', (char *)0, (char *)0, donull } /* sentinel */ }; -/* there must be a placeholder in the table above for every entry here */ -static const struct ext_func_tab debug_extcmdlist[] = { - { "levelchange", "change experience level", wiz_level_change, TRUE }, - { "lightsources", "show mobile light sources", wiz_light_sources, TRUE }, -#ifdef DEBUG_MIGRATING_MONS - { "migratemons", "migrate n random monsters", wiz_migrate_mons, TRUE }, -#endif - { "monpolycontrol", "control monster polymorphs", wiz_mon_polycontrol, - TRUE }, - { "panic", "test panic routine (fatal to game)", wiz_panic, TRUE }, - { "polyself", "polymorph self", wiz_polyself, TRUE }, -#ifdef PORT_DEBUG - { "portdebug", "wizard port debug command", wiz_port_debug, TRUE }, -#endif - { "seenv", "show seen vectors", wiz_show_seenv, TRUE }, - { "stats", "show memory statistics", wiz_show_stats, TRUE }, - { "timeout", "look at timeout queue", wiz_timeout_queue, TRUE }, - { "vanquished", "list vanquished monsters", dovanquished, TRUE }, - { "vision", "show vision array", wiz_show_vision, TRUE }, - { "wizsmell", "smell monster", wiz_smell, TRUE }, - { "wizintrinsic", "set intrinsic", wiz_intrinsic, TRUE }, -#ifdef DEBUG - { "wizdebug_traveldisplay", "wizard debug: toggle travel display", - wiz_debug_cmd_traveldisplay, TRUE }, - { "wizdebug_bury", "wizard debug: bury objs under and around you", - wiz_debug_cmd_bury, TRUE }, -#endif - { "wizrumorcheck", "verify rumor boundaries", wiz_rumor_check, TRUE }, - { "wmode", "show wall modes", wiz_show_wmodes, TRUE }, - { (char *) 0, (char *) 0, donull, TRUE } -}; +const char * +key2extcmddesc(key) +uchar key; +{ + if (Cmd.commands[key] && Cmd.commands[key]->ef_txt) + return Cmd.commands[key]->ef_desc; + return NULL; +} -/* - * Insert debug commands into the extended command list. This function - * assumes that the last entry will be the help entry. - * - * You must add entries in ext_func_tab every time you add one to the - * debug_extcmdlist(). - */ void -add_debug_extended_commands() +bind_key(key, command) +uchar key; +char* command; { - int i, j, k, n; - - /* count the # of help entries */ - for (n = 0; extcmdlist[n].ef_txt[0] != '?'; n++) - ; - - for (i = 0; debug_extcmdlist[i].ef_txt; i++) { - /* need enough room for "?" entry plus terminator */ - if (n + 2 >= SIZE(extcmdlist)) - panic("Too many debugging commands!"); - for (j = 0; j < n; j++) - if (strcmp(debug_extcmdlist[i].ef_txt, extcmdlist[j].ef_txt) < 0) - break; + struct ext_func_tab * extcmd; + + /* special case: "nothing" is reserved for unbinding */ + if (!strcmp(command, "nothing")) { + Cmd.commands[key] = NULL; + return; + } + + for(extcmd = extcmdlist; extcmd->ef_txt; extcmd++) { + if (strcmp(command, extcmd->ef_txt)) continue; + Cmd.commands[key] = extcmd; + return; + } + + pline("Bad command %s matched with key %c (ASCII %i). " + "Ignoring command.\n", command, key, key); +} + +/* initialize all keyboard commands */ +void +commands_init() +{ + struct ext_func_tab *extcmd; + + for (extcmd = extcmdlist; extcmd->ef_txt; extcmd++) + if (extcmd->key) + Cmd.commands[extcmd->key] = extcmd; + + bind_key(C('l'), "redraw"); /* if number_pad is set */ + /* 'b', 'B' : go sw */ + /* 'F' : fight (one time) */ + /* 'g', 'G' : multiple go */ + /* 'h', 'H' : go west */ + bind_key('h', "help"); /* if number_pad is set */ + bind_key('j', "jump"); /* if number_pad is on */ + /* 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N' : move commands */ + bind_key('k', "kick"); /* if number_pad is on */ + bind_key('l', "loot"); /* if number_pad is on */ + bind_key(C('n'), "annotate"); /* if number_pad is on */ + bind_key(M('n'), "name"); + bind_key(M('N'), "name"); + bind_key('u', "untrap"); /* if number_pad is on */ + + /* alt keys: */ + bind_key(M('O'), "overview"); + bind_key(M('2'), "twoweapon"); + + /* wait_on_space */ + bind_key(' ', "wait"); +} + +int +dokeylist_putcmds(datawin, docount, cmdflags, exflags, keys_used) +winid datawin; +boolean docount; +int cmdflags, exflags; +boolean *keys_used; /* boolean keys_used[256] */ +{ + int i; + char buf[BUFSZ]; + char buf2[QBUFSZ]; + int count = 0; + + for (i = 0; i < 256; i++) { + const struct ext_func_tab * extcmd; + uchar key = (uchar)i; + + if (keys_used[i]) continue; + if (key == ' ' && !flags.rest_on_space) continue; + if ((extcmd = Cmd.commands[i]) != NULL) { + if ((cmdflags && !(extcmd->flags & cmdflags)) + || (exflags && (extcmd->flags & exflags))) + continue; + if (docount) { + count++; + continue; + } + Sprintf(buf, "%-8s %-12s %s", key2txt(key, buf2), + extcmd->ef_txt, + extcmd->ef_desc); + putstr(datawin, 0, buf); + keys_used[i] = TRUE; + } + } + return count; +} + +/* list all keys and their bindings, like dat/hh but dynamic */ +void +dokeylist(VOID_ARGS) +{ + char buf[BUFSZ], buf2[BUFSZ]; + uchar key; + boolean keys_used[256] = {0}; + winid datawin; + int i; + const struct { + int nhkf; + const char *desc; + boolean numpad; + } misc_keys[] = { + { NHKF_ESC, "escape from the current query/action" }, + { NHKF_RUSH, "Prefix: rush until something interesting is seen" }, + { NHKF_RUN, "Prefix: run until something extremely interesting is seen" }, + { NHKF_RUN2, "Prefix: run until something extremely interesting is seen", TRUE }, + { NHKF_FIGHT, "Prefix: force fight even if you don't see a monster" }, + { NHKF_FIGHT2, "Prefix: force fight even if you don't see a monster", TRUE }, + { NHKF_NOPICKUP, "Prefix: move without picking up objects/fighting" }, + { NHKF_RUN_NOPICKUP, "Prefix: run without picking up objects/fighting" }, + { NHKF_DOINV, "inventory (same as #inventory)", TRUE }, + { NHKF_REQMENU, "Prefix: request a menu" }, +#ifdef REDO + { NHKF_DOAGAIN , "redo the previous command" }, +#endif + { 0, NULL } + }; + + datawin = create_nhwindow(NHW_TEXT); + putstr(datawin, 0, ""); + putstr(datawin, 0, " Full Current Key Bindings List"); + + /* directional keys */ + putstr(datawin, 0, ""); + putstr(datawin, 0, "Directional keys:"); + show_direction_keys(datawin, FALSE); + + keys_used[(uchar)Cmd.move_NW] = keys_used[(uchar)Cmd.move_N] + = keys_used[(uchar)Cmd.move_NE] = keys_used[(uchar)Cmd.move_W] + = keys_used[(uchar)Cmd.move_E] = keys_used[(uchar)Cmd.move_SW] + = keys_used[(uchar)Cmd.move_S] = keys_used[(uchar)Cmd.move_SE] = TRUE; + + if (!iflags.num_pad) { + keys_used[(uchar)highc(Cmd.move_NW)] + = keys_used[(uchar)highc(Cmd.move_N)] + = keys_used[(uchar)highc(Cmd.move_NE)] + = keys_used[(uchar)highc(Cmd.move_W)] + = keys_used[(uchar)highc(Cmd.move_E)] + = keys_used[(uchar)highc(Cmd.move_SW)] + = keys_used[(uchar)highc(Cmd.move_S)] + = keys_used[(uchar)highc(Cmd.move_SE)] = TRUE; + keys_used[(uchar)C(Cmd.move_NW)] + = keys_used[(uchar)C(Cmd.move_N)] + = keys_used[(uchar)C(Cmd.move_NE)] + = keys_used[(uchar)C(Cmd.move_W)] + = keys_used[(uchar)C(Cmd.move_E)] + = keys_used[(uchar)C(Cmd.move_SW)] + = keys_used[(uchar)C(Cmd.move_S)] + = keys_used[(uchar)C(Cmd.move_SE)] = TRUE; + putstr(datawin, 0, ""); + putstr(datawin, 0, + "Shift- will move in specified direction until you hit"); + putstr(datawin, 0, " a wall or run into something."); + putstr(datawin, 0, + "Ctrl- will run in specified direction until something"); + putstr(datawin, 0, " very interesting is seen."); + } + + putstr(datawin, 0, ""); + putstr(datawin, 0, "Miscellaneous keys:"); + for (i = 0; misc_keys[i].desc; i++) { + key = Cmd.spkeys[misc_keys[i].nhkf]; + if (key && ((misc_keys[i].numpad && iflags.num_pad) + || !misc_keys[i].numpad)) { + keys_used[(uchar)key] = TRUE; + Sprintf(buf, "%-8s %s", key2txt(key, buf2), misc_keys[i].desc); + putstr(datawin, 0, buf); + } + } +#ifndef NO_SIGNAL + putstr(datawin, 0, "^c break out of NetHack (SIGINT)"); + keys_used[(uchar)C('c')] = TRUE; +#endif + + if (dokeylist_putcmds(datawin, TRUE, GENERALCMD, WIZMODECMD, &keys_used)) { + putstr(datawin, 0, ""); + putstr(datawin, 0, "General commands:"); + (void) dokeylist_putcmds(datawin, FALSE, GENERALCMD, WIZMODECMD, &keys_used); + } - /* insert i'th debug entry into extcmdlist[j], pushing down */ - for (k = n; k >= j; --k) - extcmdlist[k + 1] = extcmdlist[k]; - extcmdlist[j] = debug_extcmdlist[i]; - n++; /* now an extra entry */ + if (dokeylist_putcmds(datawin, TRUE, 0, WIZMODECMD, &keys_used)) { + putstr(datawin, 0, ""); + putstr(datawin, 0, "Game commands:"); + (void) dokeylist_putcmds(datawin, FALSE, 0, WIZMODECMD, &keys_used); } + + if (wizard && dokeylist_putcmds(datawin, TRUE, WIZMODECMD, 0, &keys_used)) { + putstr(datawin, 0, ""); + putstr(datawin, 0, "Wizard-mode commands:"); + (void) dokeylist_putcmds(datawin, FALSE, WIZMODECMD, 0, &keys_used); + } + + display_nhwindow(datawin, FALSE); + destroy_nhwindow(datawin); } STATIC_OVL char @@ -2929,9 +3037,9 @@ cmd_from_func(fn) int NDECL((*fn)); { int i; - for (i = 0; i < SIZE(cmdlist); ++i) - if (cmdlist[i].f_funct == fn) - return cmdlist[i].f_char; + for (i = 0; i < 256; ++i) + if (Cmd.commands[i] && Cmd.commands[i]->ef_funct == fn) + return (char)i; return 0; } @@ -3350,6 +3458,181 @@ wiz_migrate_mons() #define unctrl(c) ((c) <= C('z') ? (0x60 | (c)) : (c)) #define unmeta(c) (0x7f & (c)) +struct { + int nhkf; + char key; + const char *name; +} const spkeys_binds[] = { + { NHKF_ESC, '\033', NULL }, /* no binding */ + { NHKF_DOAGAIN, DOAGAIN, "repeat" }, + { NHKF_REQMENU, 'm', "reqmenu" }, + { NHKF_RUN, 'G', "run" }, + { NHKF_RUN2, '5', "run.numpad" }, + { NHKF_RUSH, 'g', "rush" }, + { NHKF_FIGHT, 'F', "fight" }, + { NHKF_FIGHT2, '-', "fight.numpad" }, + { NHKF_NOPICKUP, 'm', "nopickup" }, + { NHKF_RUN_NOPICKUP, 'M', "run.nopickup" }, + { NHKF_DOINV, '0', "doinv" }, + { NHKF_TRAVEL, CMD_TRAVEL, NULL }, /* no binding */ + { NHKF_CLICKLOOK, CMD_CLICKLOOK, NULL }, /* no binding */ + { NHKF_REDRAW, C('r'), "redraw" }, + { NHKF_REDRAW2, C('l'), "redraw.numpad" }, + { NHKF_GETDIR_SELF, '.', "getdir.self" }, + { NHKF_GETDIR_SELF2, 's', "getdir.self2" }, + { NHKF_GETDIR_HELP, '?', "getdir.help" }, + { NHKF_COUNT, 'n', "count" }, + { NHKF_GETPOS_SELF, '@', "getpos.self" }, + { NHKF_GETPOS_PICK, '.', "getpos.pick" }, + { NHKF_GETPOS_PICK_Q, ',', "getpos.pick.quick" }, + { NHKF_GETPOS_PICK_O, ';', "getpos.pick.once" }, + { NHKF_GETPOS_PICK_V, ':', "getpos.pick.verbose" }, + { NHKF_GETPOS_SHOWVALID, '$', "getpos.valid" }, + { NHKF_GETPOS_AUTODESC, '#', "getpos.autodescribe" }, + { NHKF_GETPOS_MON_NEXT, 'm', "getpos.mon.next" }, + { NHKF_GETPOS_MON_PREV, 'M', "getpos.mon.prev" }, + { NHKF_GETPOS_OBJ_NEXT, 'o', "getpos.obj.next" }, + { NHKF_GETPOS_OBJ_PREV, 'O', "getpos.obj.prev" }, + { NHKF_GETPOS_DOOR_NEXT, 'd', "getpos.door.next" }, + { NHKF_GETPOS_DOOR_PREV, 'D', "getpos.door.prev" }, + { NHKF_GETPOS_UNEX_NEXT, 'x', "getpos.unexplored.next" }, + { NHKF_GETPOS_UNEX_PREV, 'X', "getpos.unexplored.prev" }, + { NHKF_GETPOS_HELP, '?', "getpos.help" } +}; + +boolean +bind_specialkey(key, command) +uchar key; +char *command; +{ + int i; + for (i = 0; i < SIZE(spkeys_binds); i++) { + if (!spkeys_binds[i].name || strcmp(command, spkeys_binds[i].name)) + continue; + Cmd.spkeys[spkeys_binds[i].nhkf] = key; + return TRUE; + } + return FALSE; +} + +/* returns a one-byte character from the text (it may massacre the txt + * buffer) */ +char +txt2key(txt) +char *txt; +{ + txt = trimspaces(txt); + if (!*txt) return 0; + + /* simple character */ + if (!txt[1]) return txt[0]; + + /* a few special entries */ + if (!strcmp(txt, "")) return '\n'; + if (!strcmp(txt, "")) return ' '; + if (!strcmp(txt, "")) return '\033'; + + /* control and meta keys */ + switch (*txt) { + case 'm': /* can be mx, Mx, m-x, M-x */ + case 'M': + txt++; + if(*txt == '-' && txt[1]) txt++; + if (txt[1]) return 0; + return M( *txt ); + case 'c': /* can be cx, Cx, ^x, c-x, C-x, ^-x */ + case 'C': + case '^': + txt++; + if(*txt == '-' && txt[1]) txt++; + if (txt[1]) return 0; + return C( *txt ); + } + + /* ascii codes: must be three-digit decimal */ + if (*txt >= '0' && *txt <= '9') { + uchar key = 0; + int i; + for(i = 0; i < 3; i++) { + if(txt[i]<'0' || txt[i]>'9') return 0; + key = 10 * key + txt[i]-'0'; + } + return key; + } + + return 0; +} + +/* returns the text for a one-byte encoding + * must be shorter than a tab for proper formatting */ +char* +key2txt(c, txt) +uchar c; +char* txt; /* sufficiently long buffer */ +{ + if (c == ' ') + Sprintf(txt, ""); + else if (c == '\033') + Sprintf(txt, ""); + else if (c == '\n') + Sprintf(txt, ""); + else if (ISCTRL(c)) + Sprintf(txt, "^%c", UNCTRL(c)); + else if (ISMETA(c)) + Sprintf(txt, "M-%c", UNMETA(c)); + else if (c >= 33 && c <= 126) + Sprintf(txt, "%c", c); /* regular keys: ! through ~ */ + else + Sprintf(txt, "A-%i", c); /* arbitrary ascii combinations */ + return txt; +} + + +void +parseautocomplete(autocomplete,condition) +char* autocomplete; +boolean condition; +{ + struct ext_func_tab *efp; + register char *autoc; + + /* break off first autocomplete from the rest; parse the rest */ + if ((autoc = index(autocomplete, ',')) + || (autoc = index(autocomplete, ':'))) { + *autoc++ = 0; + parseautocomplete(autoc, condition); + } + + /* strip leading and trailing white space */ + autocomplete = trimspaces(autocomplete); + + if (!*autocomplete) return; + + /* take off negation */ + if (*autocomplete == '!') { + /* unlike most options, a leading "no" might actually be a part of + * the extended command. Thus you have to use ! */ + autocomplete++; + autocomplete = trimspaces(autocomplete); + condition = !condition; + } + + /* find and modify the extended command */ + for (efp = extcmdlist; efp->ef_txt; efp++) { + if (!strcmp(autocomplete, efp->ef_txt)) { + if (condition) + efp->flags |= AUTOCOMPLETE; + else + efp->flags &= ~AUTOCOMPLETE; + return; + } + } + + /* not a real extended command */ + raw_printf("Bad autocomplete: invalid extended command '%s'.", autocomplete); + wait_synch(); +} + /* called at startup and after number_pad is twiddled */ void reset_commands(initial) @@ -3362,19 +3645,27 @@ boolean initial; static const int ylist[] = { 'y', 'Y', C('y'), M('y'), M('Y'), M(C('y')) }; - const struct func_tab *cmdtmp; + static struct ext_func_tab *back_dir_cmd[10]; + const struct ext_func_tab *cmdtmp; boolean flagtemp; int c, i, updated = 0; + static boolean backed_dir_cmd = FALSE; if (initial) { updated = 1; - for (i = 0; i < SIZE(cmdlist); i++) { - c = cmdlist[i].f_char & 0xff; - Cmd.commands[c] = &cmdlist[i]; - } Cmd.num_pad = FALSE; Cmd.pcHack_compat = Cmd.phone_layout = Cmd.swap_yz = FALSE; + for (i = 0; i < SIZE(spkeys_binds); i++) + Cmd.spkeys[spkeys_binds[i].nhkf] = spkeys_binds[i].key; + commands_init(); } else { + + if (backed_dir_cmd) { + for (i = 0; i < 10; i++) { + Cmd.commands[(uchar)Cmd.dirchars[i]] = back_dir_cmd[i]; + } + } + /* basic num_pad */ flagtemp = iflags.num_pad; if (flagtemp != Cmd.num_pad) { @@ -3443,6 +3734,17 @@ boolean initial; Cmd.move_SE = Cmd.dirchars[5]; Cmd.move_S = Cmd.dirchars[6]; Cmd.move_SW = Cmd.dirchars[7]; + + if (!initial) { + for (i = 0; i < 10; i++) { + back_dir_cmd[i] = + (struct ext_func_tab *)Cmd.commands[(uchar)Cmd.dirchars[i]]; + Cmd.commands[(uchar)Cmd.dirchars[i]] = NULL; + } + backed_dir_cmd = TRUE; + for (i = 0; i < 8; i++) + bind_key(Cmd.dirchars[i], "nothing"); + } } /* non-movement commands which accept 'm' prefix to request menu operation */ @@ -3464,6 +3766,18 @@ int NDECL((*cmd_func)); return FALSE; } +int +ch2spkeys(c, start,end) +char c; +int start,end; +{ + int i; + for (i = start; i <= end; i++) + if (Cmd.spkeys[i] == c) + return i; + return NHKF_ESC; +} + void rhack(cmd) register char *cmd; @@ -3480,7 +3794,7 @@ register char *cmd; context.nopick = 0; cmd = parse(); } - if (*cmd == '\033') { + if (*cmd == Cmd.spkeys[NHKF_ESC]) { context.move = FALSE; return; } @@ -3501,25 +3815,25 @@ register char *cmd; /* handle most movement commands */ do_walk = do_rush = prefix_seen = FALSE; context.travel = context.travel1 = 0; - switch (*cmd) { - case 'g': + switch (ch2spkeys(*cmd, NHKF_RUN,NHKF_CLICKLOOK)) { + case NHKF_RUSH: if (movecmd(cmd[1])) { context.run = 2; do_rush = TRUE; } else prefix_seen = TRUE; break; - case '5': + case NHKF_RUN2: if (!Cmd.num_pad) break; /* else FALLTHRU */ - case 'G': + case NHKF_RUN: if (movecmd(lowc(cmd[1]))) { context.run = 3; do_rush = TRUE; } else prefix_seen = TRUE; break; - case '-': + case NHKF_FIGHT2: if (!Cmd.num_pad) break; /* else FALLTHRU */ /* Effects of movement commands and invisible monsters: @@ -3527,14 +3841,14 @@ register char *cmd; * F: always attack space (even if 'I' not remembered) * normal movement: attack if 'I', move otherwise. */ - case 'F': + case NHKF_FIGHT: if (movecmd(cmd[1])) { context.forcefight = 1; do_walk = TRUE; } else prefix_seen = TRUE; break; - case 'm': + case NHKF_NOPICKUP: if (movecmd(cmd[1]) || u.dz) { context.run = 0; context.nopick = 1; @@ -3545,7 +3859,7 @@ register char *cmd; } else prefix_seen = TRUE; break; - case 'M': + case NHKF_RUN_NOPICKUP: if (movecmd(lowc(cmd[1]))) { context.run = 1; context.nopick = 1; @@ -3553,20 +3867,20 @@ register char *cmd; } else prefix_seen = TRUE; break; - case '0': + case NHKF_DOINV: if (!Cmd.num_pad) break; (void) ddoinv(); /* a convenience borrowed from the PC */ context.move = FALSE; multi = 0; return; - case CMD_CLICKLOOK: + case NHKF_CLICKLOOK: if (iflags.clicklook) { context.move = FALSE; do_look(2, &clicklook_cc); } return; - case CMD_TRAVEL: + case NHKF_TRAVEL: if (flags.travelcmd) { context.travel = 1; context.travel1 = 1; @@ -3592,10 +3906,10 @@ register char *cmd; /* some special prefix handling */ /* overload 'm' prefix to mean "request a menu" */ - if (prefix_seen && cmd[0] == 'm') { + if (prefix_seen && cmd[0] == Cmd.spkeys[NHKF_REQMENU]) { /* (for func_tab cast, see below) */ - const struct func_tab *ft = Cmd.commands[cmd[1] & 0xff]; - int NDECL((*func)) = ft ? ((struct func_tab *) ft)->f_funct : 0; + const struct ext_func_tab *ft = Cmd.commands[cmd[1] & 0xff]; + int NDECL((*func)) = ft ? ((struct ext_func_tab *) ft)->ef_funct : 0; if (func && accept_menu_prefix(func)) { iflags.menu_requested = TRUE; @@ -3632,7 +3946,8 @@ register char *cmd; context.mv = TRUE; domove(); return; - } else if (prefix_seen && cmd[1] == '\033') { /* */ + } else if (prefix_seen && cmd[1] == Cmd.spkeys[NHKF_ESC]) { + /* */ /* don't report "unknown command" for change of heart... */ bad_command = FALSE; } else if (*cmd == ' ' && !flags.rest_on_space) { @@ -3640,18 +3955,21 @@ register char *cmd; /* handle all other commands */ } else { - register const struct func_tab *tlist; + register const struct ext_func_tab *tlist; int res, NDECL((*func)); /* current - use *cmd to directly index cmdlist array */ if ((tlist = Cmd.commands[*cmd & 0xff]) != 0) { - if (u.uburied && !tlist->can_if_buried) { + if (!wizard && (tlist->flags & WIZMODECMD)) { + You_cant("do that!"); + res = 0; + } else if (u.uburied && !(tlist->flags & IFBURIED)) { You_cant("do that while you are buried!"); res = 0; } else { /* we discard 'const' because some compilers seem to have trouble with the pointer passed to set_occupation() */ - func = ((struct func_tab *) tlist)->f_funct; + func = ((struct ext_func_tab *) tlist)->ef_funct; if (tlist->f_text && !occupation && multi) set_occupation(func, tlist->f_text, multi); res = (*func)(); /* perform the command */ @@ -3744,17 +4062,21 @@ boolean redraw_cmd(c) char c; { - return (boolean) (c == C('r') || (Cmd.num_pad && c == C('l'))); + return (boolean) (c == Cmd.spkeys[NHKF_REDRAW] + || (Cmd.num_pad && c == Cmd.spkeys[NHKF_REDRAW2])); } boolean prefix_cmd(c) char c; { - return (boolean) (c == 'g' || c == 'G' - || c == 'm' || c == 'M' - || c == 'F' - || (Cmd.num_pad && (c == '5' || c == '-'))); + return (c == Cmd.spkeys[NHKF_RUSH] + || c == Cmd.spkeys[NHKF_RUN] + || c == Cmd.spkeys[NHKF_NOPICKUP] + || c == Cmd.spkeys[NHKF_RUN_NOPICKUP] + || c == Cmd.spkeys[NHKF_FIGHT] + || (Cmd.num_pad && (c == Cmd.spkeys[NHKF_RUN2] + || c == Cmd.spkeys[NHKF_FIGHT2]))); } /* @@ -3813,13 +4135,14 @@ retry: } savech(dirsym); - if (dirsym == '.' || dirsym == 's') { + if (dirsym == Cmd.spkeys[NHKF_GETDIR_SELF] + || dirsym == Cmd.spkeys[NHKF_GETDIR_SELF2]) { u.dx = u.dy = u.dz = 0; } else if (!(is_mov = movecmd(dirsym)) && !u.dz) { boolean did_help = FALSE, help_requested; if (!index(quitchars, dirsym)) { - help_requested = (dirsym == '?'); + help_requested = (dirsym == Cmd.spkeys[NHKF_GETDIR_HELP]); if (help_requested || iflags.cmdassist) { did_help = help_dir((s && *s == '^') ? dirsym : 0, help_requested ? (const char *) 0 @@ -3918,7 +4241,9 @@ const char *msg; putstr(win, 0, ""); putstr(win, 0, " < up"); putstr(win, 0, " > down"); - putstr(win, 0, " . direct at yourself"); + Sprintf(buf, " %4s direct at yourself", + visctrl(Cmd.spkeys[NHKF_GETDIR_SELF])); + putstr(win, 0, buf); if (msg) { /* non-null msg means that this wasn't an explicit user request */ putstr(win, 0, ""); @@ -3978,7 +4303,7 @@ int x, y, mod; if (iflags.clicklook && mod == CLICK_2) { clicklook_cc.x = x; clicklook_cc.y = y; - cmd[0] = CMD_CLICKLOOK; + cmd[0] = Cmd.spkeys[NHKF_CLICKLOOK]; return cmd; } @@ -3991,7 +4316,7 @@ int x, y, mod; } else { u.tx = u.ux + x; u.ty = u.uy + y; - cmd[0] = CMD_TRAVEL; + cmd[0] = Cmd.spkeys[NHKF_TRAVEL]; return cmd; } @@ -3999,27 +4324,30 @@ int x, y, mod; /* here */ if (IS_FOUNTAIN(levl[u.ux][u.uy].typ) || IS_SINK(levl[u.ux][u.uy].typ)) { - cmd[0] = mod == CLICK_1 ? 'q' : M('d'); + cmd[0] = cmd_from_func(mod == CLICK_1 ? dodrink : dodip); return cmd; } else if (IS_THRONE(levl[u.ux][u.uy].typ)) { - cmd[0] = M('s'); + cmd[0] = cmd_from_func(dosit); return cmd; } else if ((u.ux == xupstair && u.uy == yupstair) || (u.ux == sstairs.sx && u.uy == sstairs.sy && sstairs.up) || (u.ux == xupladder && u.uy == yupladder)) { - return "<"; + cmd[0] = cmd_from_func(doup); + return cmd; } else if ((u.ux == xdnstair && u.uy == ydnstair) || (u.ux == sstairs.sx && u.uy == sstairs.sy && !sstairs.up) || (u.ux == xdnladder && u.uy == ydnladder)) { - return ">"; + cmd[0] = cmd_from_func(dodown); + return cmd; } else if (OBJ_AT(u.ux, u.uy)) { - cmd[0] = - Is_container(level.objects[u.ux][u.uy]) ? M('l') : ','; + cmd[0] = cmd_from_func(Is_container(level.objects[u.ux][u.uy]) + ? doloot : dopickup); return cmd; } else { - return "."; /* just rest */ + cmd[0] = cmd_from_func(donull); /* just rest */ + return cmd; } } @@ -4035,16 +4363,16 @@ int x, y, mod; /* slight assistance to the player: choose kick/open for them */ if (levl[u.ux + x][u.uy + y].doormask & D_LOCKED) { - cmd[0] = C('d'); + cmd[0] = cmd_from_func(dokick); return cmd; } if (levl[u.ux + x][u.uy + y].doormask & D_CLOSED) { - cmd[0] = 'o'; + cmd[0] = cmd_from_func(doopen); return cmd; } } if (levl[u.ux + x][u.uy + y].typ <= SCORR) { - cmd[0] = 's'; + cmd[0] = cmd_from_func(dosearch); cmd[1] = 0; return cmd; } @@ -4062,9 +4390,11 @@ int x, y, mod; else x = sgn(x), y = sgn(y); - if (x == 0 && y == 0) /* map click on player to "rest" command */ - return "."; - + if (x == 0 && y == 0) { + /* map click on player to "rest" command */ + cmd[0] = cmd_from_func(donull); + return cmd; + } dir = xytod(x, y); } @@ -4112,7 +4442,7 @@ long *count; } else if (cnt && (key == '\b' || key == STANDBY_erase_char)) { cnt = cnt / 10; backspaced = TRUE; - } else if (key == '\033') { + } else if (key == Cmd.spkeys[NHKF_ESC]) { break; } else if (!allowchars || index(allowchars, key)) { *count = cnt; @@ -4153,7 +4483,7 @@ parse() #ifdef ALTMETA alt_esc = iflags.altmeta; /* readchar() hack */ #endif - if (!Cmd.num_pad || (foo = readchar()) == 'n') { + if (!Cmd.num_pad || (foo = readchar()) == Cmd.spkeys[NHKF_COUNT]) { long tmpmulti = multi; foo = get_count(NULL, '\0', LARGEST_INT, &tmpmulti); @@ -4163,10 +4493,10 @@ parse() alt_esc = FALSE; /* readchar() reset */ #endif - if (foo == '\033') { /* esc cancels count (TH) */ + if (foo == Cmd.spkeys[NHKF_ESC]) { /* esc cancels count (TH) */ clear_nhwindow(WIN_MESSAGE); multi = last_multi = 0; - } else if (foo == DOAGAIN || in_doagain) { + } else if (foo == Cmd.spkeys[NHKF_DOAGAIN] || in_doagain) { multi = last_multi; } else { last_multi = multi; @@ -4188,13 +4518,13 @@ parse() from the number pad. Now do not map them until here. */ switch (foo) { case '5': - foo = 'g'; + foo = Cmd.spkeys[NHKF_RUSH]; break; case M('5'): - foo = 'G'; + foo = Cmd.spkeys[NHKF_RUN]; break; case M('0'): - foo = 'I'; + foo = Cmd.spkeys[NHKF_DOINV]; break; default: break; /* as is */ @@ -4211,7 +4541,7 @@ parse() } clear_nhwindow(WIN_MESSAGE); if (prezero) - in_line[0] = '\033'; + in_line[0] = Cmd.spkeys[NHKF_ESC]; return in_line; } @@ -4342,7 +4672,7 @@ dotravel(VOID_ARGS) iflags.getloc_travelmode = FALSE; iflags.travelcc.x = u.tx = cc.x; iflags.travelcc.y = u.ty = cc.y; - cmd[0] = CMD_TRAVEL; + cmd[0] = Cmd.spkeys[NHKF_TRAVEL]; readchar_queue = cmd; return 0; } diff --git a/src/do_name.c b/src/do_name.c index c5ee5808..5c470265 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -59,45 +59,84 @@ const char *goal; putstr(tmpwin, 0, sbuf); putstr(tmpwin, 0, "Use 'H', 'J', 'K', 'L' to move the cursor 8 units at a time."); putstr(tmpwin, 0, "Or enter a background symbol (ex. '<')."); - putstr(tmpwin, 0, "Use '@' to move the cursor on yourself."); - if (!iflags.terrainmode || (iflags.terrainmode & TER_MON) != 0) - putstr(tmpwin, 0, "Use 'm' or 'M' to move the cursor to next monster."); - if (!iflags.terrainmode || (iflags.terrainmode & TER_OBJ) != 0) - putstr(tmpwin, 0, "Use 'o' or 'O' to move the cursor to next object."); + Sprintf(sbuf, "Use '%s' to move the cursor on yourself.", + visctrl(Cmd.spkeys[NHKF_GETPOS_SELF])); + putstr(tmpwin, 0, sbuf); + if (!iflags.terrainmode || (iflags.terrainmode & TER_MON) != 0) { + Sprintf(sbuf, "Use '%s' or '%s' to move the cursor to next monster.", + visctrl(Cmd.spkeys[NHKF_GETPOS_MON_NEXT]), + visctrl(Cmd.spkeys[NHKF_GETPOS_MON_PREV])); + putstr(tmpwin, 0, sbuf); + } + if (!iflags.terrainmode || (iflags.terrainmode & TER_OBJ) != 0) { + Sprintf(sbuf, "Use '%s' or '%s' to move the cursor to next object.", + visctrl(Cmd.spkeys[NHKF_GETPOS_OBJ_NEXT]), + visctrl(Cmd.spkeys[NHKF_GETPOS_OBJ_PREV])); + putstr(tmpwin, 0, sbuf); + } if (!iflags.terrainmode || (iflags.terrainmode & TER_MAP) != 0) { /* both of these are primarily useful when choosing a travel destination for the '_' command */ - putstr(tmpwin, 0, - "Use 'd' or 'D' to move the cursor to next door or doorway."); - putstr(tmpwin, 0, - "Use 'x' or 'X' to move the cursor to unexplored location."); + Sprintf(sbuf, + "Use '%s' or '%s' to move the cursor to next door or doorway.", + visctrl(Cmd.spkeys[NHKF_GETPOS_DOOR_NEXT]), + visctrl(Cmd.spkeys[NHKF_GETPOS_DOOR_PREV])); + putstr(tmpwin, 0, sbuf); + Sprintf(sbuf, + "Use '%s' or '%s' to move the cursor to unexplored location.", + visctrl(Cmd.spkeys[NHKF_GETPOS_UNEX_NEXT]), + visctrl(Cmd.spkeys[NHKF_GETPOS_UNEX_PREV])); + putstr(tmpwin, 0, sbuf); } if (!iflags.terrainmode) { - if (getpos_hilitefunc) - putstr(tmpwin, 0, "Use '$' to display valid locations."); - putstr(tmpwin, 0, "Use '#' to toggle automatic description."); - if (iflags.cmdassist) /* assisting the '/' command, I suppose... */ - putstr(tmpwin, 0, - (iflags.getpos_coords == GPCOORDS_NONE) - ? "(Set 'whatis_coord' option to include coordinates with '#' text.)" - : "(Reset 'whatis_coord' option to omit coordinates from '#' text.)"); + char kbuf[BUFSZ]; + if (getpos_hilitefunc) { + Sprintf(sbuf, "Use '%s' to display valid locations.", + visctrl(Cmd.spkeys[NHKF_GETPOS_SHOWVALID])); + putstr(tmpwin, 0, sbuf); + } + Sprintf(sbuf, "Use '%s' to toggle automatic description.", + visctrl(Cmd.spkeys[NHKF_GETPOS_AUTODESC])); + putstr(tmpwin, 0, sbuf); + if (iflags.cmdassist) { /* assisting the '/' command, I suppose... */ + Sprintf(sbuf, + (iflags.getpos_coords == GPCOORDS_NONE) + ? "(Set 'whatis_coord' option to include coordinates with '%s' text.)" + : "(Reset 'whatis_coord' option to omit coordinates from '%s' text.)", + visctrl(Cmd.spkeys[NHKF_GETPOS_AUTODESC])); + } /* disgusting hack; the alternate selection characters work for any getpos call, but only matter for dowhatis (and doquickwhatis) */ - doing_what_is = (goal == what_is_an_unknown_object); - Sprintf(sbuf, "Type a '.'%s when you are at the right place.", - doing_what_is ? " or ',' or ';' or ':'" : ""); + doing_what_is = (goal == what_is_an_unknown_object); + if (doing_what_is) { + Sprintf(kbuf, "'%s' or '%s' or '%s' or '%s'", + visctrl(Cmd.spkeys[NHKF_GETPOS_PICK]), + visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_Q]), + visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_O]), + visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_V])); + } else { + Sprintf(kbuf, "'%s'", visctrl(Cmd.spkeys[NHKF_GETPOS_PICK])); + } + Sprintf(sbuf, "Type a %s when you are at the right place.", kbuf); putstr(tmpwin, 0, sbuf); if (doing_what_is) { - putstr(tmpwin, 0, - " ':' describe current spot, show 'more info', move to another spot."); Sprintf(sbuf, - " '.' describe current spot,%s move to another spot;", + " '%s' describe current spot, show 'more info', move to another spot.", + visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_V])); + putstr(tmpwin, 0, sbuf); + Sprintf(sbuf, + " '%s' describe current spot,%s move to another spot;", + visctrl(Cmd.spkeys[NHKF_GETPOS_PICK]), flags.help ? " prompt if 'more info'," : ""); putstr(tmpwin, 0, sbuf); - putstr(tmpwin, 0, - " ',' describe current spot, move to another spot;"); - putstr(tmpwin, 0, - " ';' describe current spot, stop looking at things;"); + Sprintf(sbuf, + " '%s' describe current spot, move to another spot;", + visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_Q])); + putstr(tmpwin, 0, sbuf); + Sprintf(sbuf, + " '%s' describe current spot, stop looking at things;", + visctrl(Cmd.spkeys[NHKF_GETPOS_PICK_O])); + putstr(tmpwin, 0, sbuf); } } if (!force) @@ -337,9 +376,27 @@ coord *ccp; boolean force; const char *goal; { - static const char pick_chars[] = ".,;:", - mMoOdDxX[] = "mMoOdDxX"; const char *cp; + struct { + int nhkf, ret; + } const pick_chars_def[] = { + { NHKF_GETPOS_PICK, LOOK_TRADITIONAL }, + { NHKF_GETPOS_PICK_Q, LOOK_QUICK }, + { NHKF_GETPOS_PICK_O, LOOK_ONCE }, + { NHKF_GETPOS_PICK_V, LOOK_VERBOSE } + }; + const int mMoOdDxX_def[] = { + NHKF_GETPOS_MON_NEXT, + NHKF_GETPOS_MON_PREV, + NHKF_GETPOS_OBJ_NEXT, + NHKF_GETPOS_OBJ_PREV, + NHKF_GETPOS_DOOR_NEXT, + NHKF_GETPOS_DOOR_PREV, + NHKF_GETPOS_UNEX_NEXT, + NHKF_GETPOS_UNEX_PREV + }; + char pick_chars[6]; + char mMoOdDxX[9]; int result = 0; int cx, cy, i, c; int sidx, tx, ty; @@ -350,10 +407,19 @@ const char *goal; int gcount[NUM_GLOCS] = DUMMY; int gidx[NUM_GLOCS] = DUMMY; + for (i = 0; i < SIZE(pick_chars_def); i++) + pick_chars[i] = Cmd.spkeys[pick_chars_def[i].nhkf]; + pick_chars[SIZE(pick_chars_def)] = '\0'; + + for (i = 0; i < SIZE(mMoOdDxX_def); i++) + mMoOdDxX[i] = Cmd.spkeys[mMoOdDxX_def[i]]; + mMoOdDxX[SIZE(mMoOdDxX_def)] = '\0'; + if (!goal) goal = "desired location"; if (flags.verbose) { - pline("(For instructions type a '?')"); + pline("(For instructions type a '%s')", + visctrl(Cmd.spkeys[NHKF_GETPOS_HELP])); msg_given = TRUE; } cx = ccp->x; @@ -388,7 +454,7 @@ const char *goal; if (iflags.autodescribe) msg_given = FALSE; - if (c == '\033') { + if (c == Cmd.spkeys[NHKF_ESC]) { cx = cy = -10; msg_given = TRUE; /* force clear */ result = -1; @@ -404,7 +470,7 @@ const char *goal; } if ((cp = index(pick_chars, c)) != 0) { /* '.' => 0, ',' => 1, ';' => 2, ':' => 3 */ - result = (int) (cp - pick_chars); + result = pick_chars_def[(int) (cp - pick_chars)].ret; break; } for (i = 0; i < 8; i++) { @@ -442,26 +508,23 @@ const char *goal; goto nxtc; } - if (c == '?' || redraw_cmd(c)) { - if (c == '?') + if (c == Cmd.spkeys[NHKF_GETPOS_HELP] || redraw_cmd(c)) { + if (c == Cmd.spkeys[NHKF_GETPOS_HELP]) getpos_help(force, goal); else /* ^R */ docrt(); /* redraw */ /* update message window to reflect that we're still targetting */ show_goal_msg = TRUE; msg_given = TRUE; - } else if (c == '$' && getpos_hilitefunc) { + } else if (c == Cmd.spkeys[NHKF_GETPOS_SHOWVALID] + && getpos_hilitefunc) { if (!hilite_state) { (*getpos_hilitefunc)(0); (*getpos_hilitefunc)(1); hilite_state = TRUE; } goto nxtc; - } else if (c == '#') { - /* unfortunately, using '#' as a command means we can't move - cursor to sinks, iron bars, and poison clouds; perhaps - when autodescribe is already on, next '#' should try to - move to '#' rather than to toggle off? (or ask; ick...) */ + } else if (c == Cmd.spkeys[NHKF_GETPOS_AUTODESC]) { iflags.autodescribe = !iflags.autodescribe; pline("Automatic description %sis %s.", flags.verbose ? "of features under cursor " : "", @@ -470,7 +533,7 @@ const char *goal; show_goal_msg = TRUE; msg_given = TRUE; goto nxtc; - } else if (c == '@') { /* return to hero's spot */ + } else if (c == Cmd.spkeys[NHKF_GETPOS_SELF]) { /* reset 'm&M', 'o&O', &c; otherwise, there's no way for player to achieve that except by manually cycling through all spots */ for (i = 0; i < NUM_GLOCS; i++) @@ -568,9 +631,10 @@ const char *goal; if (!force) Strcpy(note, "aborted"); else - Sprintf(note, "use '%c', '%c', '%c', '%c' or '.'", /* hjkl */ + Sprintf(note, "use '%c', '%c', '%c', '%c' or '%s'", /* hjkl */ Cmd.move_W, Cmd.move_S, Cmd.move_N, - Cmd.move_E); + Cmd.move_E, + visctrl(Cmd.spkeys[NHKF_GETPOS_PICK])); pline("Unknown direction: '%s' (%s).", visctrl((char) c), note); msg_given = TRUE; diff --git a/src/files.c b/src/files.c index 96d602fc..1138284a 100644 --- a/src/files.c +++ b/src/files.c @@ -2152,6 +2152,10 @@ int src; parseoptions(bufp, TRUE, TRUE); } else if (match_varname(buf, "AUTOPICKUP_EXCEPTION", 5)) { add_autopickup_exception(bufp); + } else if (match_varname(buf, "BINDINGS", 4)) { + parsebindings(bufp); + } else if (match_varname(buf, "AUTOCOMPLETE", 5)) { + parseautocomplete(bufp, TRUE); } else if (match_varname(buf, "MSGTYPE", 7)) { (void) msgtype_parse_add(bufp); #ifdef NOCWD_ASSUMPTIONS diff --git a/src/hacklib.c b/src/hacklib.c index 4d75ba73..8cb76021 100644 --- a/src/hacklib.c +++ b/src/hacklib.c @@ -18,6 +18,7 @@ char * ucase (char *) char * upstart (char *) char * mungspaces (char *) + char * trimspaces (char *) char * strip_newline (char *) char * eos (char *) boolean str_end_is (const char *, const char *) @@ -159,6 +160,22 @@ char *bp; return bp; } +/* remove leading and trailing whitespace, in place */ +char* +trimspaces(txt) +char* txt; +{ + char* end; + + while (*txt == ' ' || *txt == '\t') + txt++; + end = eos(txt); + while (--end >= txt && (*end == ' ' || *end == '\t')) + *end = '\0'; + + return txt; +} + /* remove \n from end of line; remove \r too if one is there */ char * strip_newline(str) @@ -385,13 +402,17 @@ char *sbuf; return strcpy(sbuf, buf); } +#define VISCTRL_NBUF 5 /* make a displayable string from a character */ char * visctrl(c) char c; { - Static char ccc[5]; + Static char visctrl_bufs[VISCTRL_NBUF][5]; + static int nbuf = 0; register int i = 0; + char *ccc = visctrl_bufs[nbuf]; + nbuf = (nbuf + 1) % VISCTRL_NBUF; if ((uchar) c & 0200) { ccc[i++] = 'M'; diff --git a/src/options.c b/src/options.c index ebe189e6..53d64a66 100644 --- a/src/options.c +++ b/src/options.c @@ -833,6 +833,10 @@ int maxlen; * has the effect of 'meta'-ing the value which follows (so that the * alternate character set will be enabled). * + * X normal key X + * ^X control-X + * \mX meta-X + * * For 3.4.3 and earlier, input ending with "\M", backslash, or caret * prior to terminating '\0' would pull that '\0' into the output and then * keep processing past it, potentially overflowing the output buffer. @@ -3241,7 +3245,6 @@ boolean tinitial, tfrom_file; if (negated) { bad_negation(fullname, FALSE); } else if ((op = string_for_opt(opts, FALSE)) != 0) { - int j; char c, op_buf[BUFSZ]; escapes(op, op_buf); @@ -3475,6 +3478,56 @@ boolean tinitial, tfrom_file; badoption(opts); } +/* parse key:command */ +void +parsebindings(bindings) +char* bindings; +{ + char *bind; + char key; + int i; + + /* break off first binding from the rest; parse the rest */ + if ((bind = index(bindings, ',')) != 0) { + *bind++ = 0; + parsebindings(bind); + } + + /* parse a single binding: first split around : */ + if (! (bind = index(bindings, ':'))) return; /* it's not a binding */ + *bind++ = 0; + + /* read the key to be bound */ + key = txt2key(bindings); + if (!key) { + raw_printf("Bad binding %s.", bindings); + wait_synch(); + return; + } + + bind = trimspaces(bind); + + /* is it a special key? */ + if (bind_specialkey(key, bind)) + return; + + /* is it a menu command? */ + for (i = 0; i < NUM_MENU_CMDS; i++) { + if (!strcmp(default_menu_cmd_info[i].name, bind)) { + if (illegal_menu_cmd_key(key)) { + char tmp[BUFSZ]; + Sprintf(tmp, "Bad menu key %s:%s", visctrl(key), bind); + badoption(tmp); + } else + add_menu_cmd_alias(key, default_menu_cmd_info[i].cmd); + return; + } + } + + /* extended command? */ + bind_key(key, bind); +} + static NEARDATA const char *menutype[] = { "traditional", "combination", "full", "partial" }; diff --git a/src/pager.c b/src/pager.c index 184027ba..47374f0a 100644 --- a/src/pager.c +++ b/src/pager.c @@ -1371,6 +1371,7 @@ whatdoes_help() destroy_nhwindow(tmpwin); } +#if 0 #define WD_STACKLIMIT 5 struct wd_stack_frame { Bitfield(active, 1); @@ -1501,18 +1502,31 @@ int *depth, lnum; } return stack[*depth].active ? TRUE : FALSE; } +#endif /* 0 */ char * dowhatdoes_core(q, cbuf) char q; char *cbuf; { - dlb *fp; char buf[BUFSZ]; +#if 0 + dlb *fp; struct wd_stack_frame stack[WD_STACKLIMIT]; boolean cond; int ctrl, meta, depth = 0, lnum = 0; +#endif /* 0 */ + const char *ec_desc; + + if ((ec_desc = key2extcmddesc(q)) != NULL) { + char keybuf[QBUFSZ]; + Sprintf(buf, "%-8s%s.", key2txt(q, keybuf), ec_desc); + Strcpy(cbuf, buf); + return cbuf; + } + return 0; +#if 0 fp = dlb_fopen(CMDHELPFILE, "r"); if (!fp) { pline("Cannot open \"%s\" data file!", CMDHELPFILE); @@ -1568,6 +1582,7 @@ char *cbuf; if (depth != 0) impossible("cmdhelp: mismatched &? &: &. conditionals."); return (char *) 0; +#endif /* 0 */ } int @@ -1718,6 +1733,7 @@ static struct { { hmenu_dowhatdoes, "Info on what a given key does." }, { option_help, "List of game options." }, { dispfile_optionfile, "Longer explanation of game options." }, + { dokeylist, "Full list of keyboard commands" }, { hmenu_doextlist, "List of extended commands." }, { dispfile_license, "The NetHack license." }, { docontact, "Support information." }, diff --git a/win/tty/getline.c b/win/tty/getline.c index 9c9efdf0..4d86f9a1 100644 --- a/win/tty/getline.c +++ b/win/tty/getline.c @@ -238,7 +238,9 @@ char *base; com_index = -1; for (oindex = 0; extcmdlist[oindex].ef_txt != (char *) 0; oindex++) { - if (!strncmpi(base, extcmdlist[oindex].ef_txt, strlen(base))) { + if ((extcmdlist[oindex].flags & AUTOCOMPLETE) + && !(!wizard && (extcmdlist[oindex].flags & WIZMODECMD)) + && !strncmpi(base, extcmdlist[oindex].ef_txt, strlen(base))) { if (com_index == -1) /* no matches yet */ com_index = oindex; else /* more than 1 match */ diff --git a/win/win32/mswproc.c b/win/win32/mswproc.c index 7247dbbb..df53f7a8 100644 --- a/win/win32/mswproc.c +++ b/win/win32/mswproc.c @@ -1726,7 +1726,9 @@ mswin_get_ext_cmd() com_index = -1; for (oindex = 0; extcmdlist[oindex].ef_txt != (char *) 0; oindex++) { - if (!strncmpi(cmd, extcmdlist[oindex].ef_txt, len)) { + if ((extcmdlist[oindex].flags & AUTOCOMPLETE) + && !(!wizard && (extcmdlist[oindex].flags & WIZMODECMD)) + && !strncmpi(cmd, extcmdlist[oindex].ef_txt, len)) { if (com_index == -1) /* no matches yet */ com_index = oindex; else -- 2.11.4.GIT