diff --git a/dat/bogusmon.txt b/dat/bogusmon.txt index 73e8aa71a..322b31123 100644 --- a/dat/bogusmon.txt +++ b/dat/bogusmon.txt @@ -73,6 +73,9 @@ small megabat uberhulk tofurkey +Dudley +shrinking violet +shallow one +spherical cow # Quendor (Zork, &c.) grue @@ -119,6 +122,7 @@ were-rabbit # Discworld Luggage +vampiric watermelon # Lord of the Rings Ent @@ -230,6 +234,7 @@ velociraptor corpulent porpoise quokka potoo +lemming # european cryptids wolpertinger @@ -297,6 +302,7 @@ gazebo gray goo magnetic monopole first category perpetual motion device +big dumb object # Ultima +Lord British diff --git a/dat/symbols b/dat/symbols index de4b5dc5b..619d436a7 100644 --- a/dat/symbols +++ b/dat/symbols @@ -513,6 +513,7 @@ start: Blank S_ss4: \032 S_statue_trap: \032 S_stone: \032 + S_strange_obj: \032 S_sw_bc: \032 S_sw_bl: \032 S_sw_br: \032 diff --git a/dat/tribute b/dat/tribute index 10b29a582..3bdb540a9 100644 --- a/dat/tribute +++ b/dat/tribute @@ -8277,17 +8277,141 @@ for such a long time, you don't know how to spit it out." %e title # # +# Sir Terry Pratchett's final book, published posthumously. +# The story is complete, but the length is substantially shorter than +# other recent Discworld novels. Presumably it would have been expanded +# if he had had more time to work on it.... # -%title The Shepherd's Crown (1) +%title The Shepherd's Crown (7) +# pp. 29-30 (Harper edition) %passage 1 -'It's an inconvenience, true enough, and I don't like it at all, but I -know that you do it for everyone, Mister Death. Is there any other way?' +"It's an inconvenience, true enough, and I don't like it at all, but I +know that you do it for everyone, Mr. Death. Is there any other way?" -NO, THERE ISN'T, I'M AFRAID. WE ARE ALL FLOATING IN THE WINDS OF TIME. +NO, THERE ISN'T, I'M AFRAID. WE ARE ALL FLOATING IN THE WINDS OF TIME. BUT YOUR CANDLE, MISTRESS WEATHERWAX, WILL FLICKER FOR SOME TIME BEFORE -IT GOES OUT -- A LITTLE REWARD FOR A LIFE WELL LIVED. FOR I CAN SEE THE +IT GOES OUT--A LITTLE REWARD FOR A LIFE WELL LIVED. FOR I CAN SEE THE BALANCE AND YOU HAVE LEFT THE WORLD MUCH BETTER THAN YOU FOUND IT, AND -IF YOU ASK ME, said Death, NOBODY COULD DO ANY BETTER THAN THAT... +IF YOU ASK ME, said Death, NOBODY COULD DO ANY BETTER THAN THAT.... + + [The Shepherd's Crown, by Terry Pratchett] +%e passage +# p. 30 ('she' is Miss Tick, a travelling witch who finds new witches; +# 'under water' is spelled as two words; 'ducking' is accurate) +%passage 2 +She sighed. It was such a shame when old customs disappeared. A good +witch-ducking was something she had liked doing in the bad old days--she +had even /trained/ for it. All those swimming lessons, and practice with +knots at the Quirm College for Young Ladies. She had been able to defeat +the mobs under water if necessary. Or at least work at breaking her own +record for untying the simple knots they all thought worked on the nasty +witch. + +Now, a bit of pond-dipping had become more like a hobby, and she had a +nasty feeling that others were copying her after she passed through their +villages. She'd even heard talk of a swimming club being started in one +small hamlet over by Ham-on-Rye.(1) + +(1) A popular idea among the young lads, since they felt that everyone-- +and "everyone" definitely included the young ladies--should swim without +their clothes. + + [The Shepherd's Crown, by Terry Pratchett] +%e passage +# p. 37 (passage starts mid-paragraph; 'she' is Tiffany Aching) +%passage 3 +Not for the first time, she wondered how it was that cats seemed to be +able to be in one place one moment, and then /almost at the same time/, +reappear somewhere else.(1) + +(1) She did not know it, but a keen young philosopher in Ephebe had +pondered exactly that same conumdrum, until he was found one morning-- +most of him, anyway--surrounded by a number of purring, and very well fed, +cats. No one had seemed keen to continue his experiments after that. + + [The Shepherd's Crown, by Terry Pratchett] +%e passage +# pp. 112-113 (the footnote has a misprint of mismatched quotes: "Chuffley') +%passage 4 +Roland de Chumsfanleigh,(1) the young Baron on the Chalk /did/ want to be +like his father in many ways. He knew the old man had been popular--what +was known as an "old-school Baron," which meant that everyone knew what +to expect and the guards polished up their armor and saluted, and did +what was expected of /them/, while the Baron did what was expected of him, +and pretty much left them alone. + +But his father had also been a bit of a bad-tempered bully at times. And +/that/ bit Roland wanted to forget about. He particularly wanted to sound +the right note when he called round to see Tiffany Aching at Home Farm. +For they had once been good friends, and to Roland's alarm, Tiffany was +thought of as a good friend by his wife Letitia. Any man with sense was +wise to be fearful of a wife's best friends. For who knew what ... little +secrets might be shared. Roland, having been educated at home and with +limited knowledge of the world outside the Chalk, feared that "little" +might be /exactly/ the kind of comment Letitia might share with Tiffany. + +(1) Pronounced "Chuffley" under the strange rule that the more gentrified +a family is, the more peculiar the pronunciation of their name becomes. +Tiffany had once heard a high-born visitor named Ponsonby-Macklewright +(/Pwt/) refer to Roland as /Chf/. She wondered how they managed at dinner +when /Pwt/ introduced /Chf/ to /Wm/ or /Hmpfh/. Surely it could lead to +misunderstandings? + + [The Shepherd's Crown, by Terry Pratchett] +%e passage +# p. 158 +%passage 5 +He kicked the helmet of his chief, the Big Man of the clan, and shouted, +"There's elves here! I can smell it, ye ken!" + +And from every hole in turn, the clan of the Nac Mac Feegle poured out in +their hundreds to deal with the ancient enemy, waving claymores and swords, +yodeling their war cries: + +"Ach, stickit yer trakkans!" + +"Nac Mac Feegle wha hae!" + +"Gae awa' wi' ye, yer bogle!" + +"Gi'e you sich a guid kickin'!" + +"Nae king! Nae quin! We will nae be fooled agin!" + +There is a concept known as a hustle and bustle, and the Feegles were very +good at it, cheerfully getting in one another's way in the drive to be the +first into battle, and it seemed as if each small warrior had a battle cry +of his own--and he was very ready to fight anyone who tried to take it +away from him. + +"How many elves?" asked Rob Anybody, trying to adjust his spog. + +There was a pause. + +"One," said Big Yan sheepishly. + + [The Shepherd's Crown, by Terry Pratchett] +%e passage +# p. 159 (passage starts mid-paragraph. 'oor' is accurate) +%passage 6 +"This elf is oor prisoner. A hostage, ye ken. That means ye are nae tae +kill it until ye are told." He ignored the grumbles from the clan. "As +tae the rest o' ye, tak guard around yon stones. And if they come in +force, show them what the Feegles can dae!" + +Daft Wullie said, "I can play the harmonica." + +Rob Anybody sighed. "Aye, weel, I suppose that puts the willies up me, +so wud likely keep them awa'." + + [The Shepherd's Crown, by Terry Pratchett] +%e passage +# p. 202 +%passage 7 +Sometimes, Tiffany thought, I am so /fed up/ with being young.(1) + +(1) A thought that she would most certainly grow out of, assuming she +survived long enough. [The Shepherd's Crown, by Terry Pratchett] %e passage diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index dacbd1db5..171388b6e 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 @@ -584,7 +586,8 @@ A few other commands (eat food, offer sacrifice, apply tinning-kit) use the `m' prefix to skip checking for applicable objects on the floor and go straight to checking inventory, or (for ``#loot'' to remove a saddle), -skip containers and go straight to adjacent monsters. +skip containers and go straight to adjacent monsters. The prefix will +make ``#travel'' command show a menu of interesting targets in sight. .lp F[yuhjklbn] Prefix: fight a monster (even if you only guess one is there). .lp M[yuhjklbn] @@ -823,7 +826,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 +915,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 +926,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 +1042,152 @@ 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 '_'. +Using the ``request menu'' prefix shows a menu of interesting targets in sight +without asking to move the cursor. .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 +2232,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 +3155,105 @@ 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.menu +When asked for a location, show a menu of all interesting targets. Default is 'A'. +.lp getpos.menu.cansee +When asked for a location, show a menu of interesting targets in view. Default is 'a'. +.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 @@ -3285,6 +3584,7 @@ s S_spider (arachnid or centipede) * S_ss4 (magic shield 4 of 4) ^ S_statue_trap (statue trap) S_stone (dark part of a room) +] S_strange_obj (strange object) - S_sw_bc (swallow bottom center) `\e' S_sw_bl (swallow bottom left) / S_sw_br (swallow bottom right) diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 2f6cf96f6..5ea6a5679 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 @@ -704,7 +706,8 @@ A few other commands (eat food, offer sacrifice, apply tinning-kit) use the `{\tt m}' prefix to skip checking for applicable objects on the floor and go straight to checking inventory, or (for ``{\tt \#loot}'' to remove a saddle), -skip containers and go straight to adjacent monsters. +skip containers and go straight to adjacent monsters. The prefix will +make ``{\tt \#travel}'' command show a menu of interesting targets in sight. %.lp \item[\tb{F[yuhjklbn]}] Prefix: fight a monster (even if you only guess one is there). @@ -985,7 +988,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 +1095,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 +1107,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 +1273,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 +1292,196 @@ 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 _}'. +Using the ``request menu'' prefix shows a menu of interesting targets in sight +without asking to move the cursor. %.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. @@ -2464,10 +2730,42 @@ HACKDIR, must be writeable. \item[\bb{TROUBLEDIR}] 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. @@ -3552,6 +3850,154 @@ 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.menu}} +When asked for a location, show a menu of all interesting targets. Default is '{\tt A}'. +%.lp +\item{\bb{getpos.menu.cansee}} +When asked for a location, show a menu of interesting targets in view. Default is '{\tt a}'. +%.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} @@ -3926,6 +4372,7 @@ Default & Symbol Name & Description\\ \verb@*@ & S\verb+_+ss4 & (magic shield 4 of 4)\\ \verb@^@ & S\verb+_+statue\verb+_+trap & (statue trap)\\ \verb@ @ & S\verb+_+stone & (dark part of a room)\\ +\verb@]@ & S\verb+_+strange\verb+_+obj & (strange object)\\ \verb@-@ & S\verb+_+sw\verb+_+bc & (swallow bottom center)\\ \verb@\@ & S\verb+_+sw\verb+_+bl & (swallow bottom left)\\ \verb@/@ & S\verb+_+sw\verb+_+br & (swallow bottom right )\\ @@ -3998,7 +4445,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 1ab4b2566..47995a24f 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -344,6 +344,10 @@ don't show monster vs. monster attack message, if the message refers to a monster that cannot be sensed by the hero umber hulk gazing at hidden mimic forces mimic to unhide show some timed hero properties in wizard-mode #timeout +give quest guardians some equipment +hero polyed into ghoul can only eat non-veggy corpses or eggs +kicking activates statue traps +pets start with apport equal to your charisma Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository @@ -441,8 +445,8 @@ allow knife and stiletto as possible tin opening tools wizard mode #wizintrinsic command additional tribute passages for The Colour of Magic, The Light Fantastic, Equal Rites, Mort, Sourcery, Wyrd Sisters, Pyramids, Guards! Guards!, - Eric, Moving Pictures, Reaper Man, Witches Abroad, Snuff, and - Raising Steam + Eric, Moving Pictures, Reaper Man, Witches Abroad, Snuff, + Raising Steam, and The Shepherd's Crown compile-time options SIMPLE_MAIL and SERVER_ADMIN_MSG for public server use database entries for Cleaver, Sunsword, Frost and Fire brands, and polymorph trap @@ -496,6 +500,8 @@ when moving a cursor for travel target, show if there is no known travel show the reason why travel was interrupted if mention_walls is on random horses have a tiny chance of being generated saddled give feedback just before timed levitation runs out +travel accepts 'm' (request menu) prefix +pressing a or A when cursor positioning shows menu of "interesting" features Platform- and/or Interface-Specific New Features @@ -514,6 +520,7 @@ X11: status display split into three columns to accomodate Stone/Deaf/Lev/&c; NetHack*status_condition.foreground, .background, and .showGrip resources replaced by status_condition[1-3].* X11: more terminal-like default resources +win32gui: save and load map colors from registry NetHack Community Patches (or Variation) Included @@ -530,6 +537,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 b3a8316b0..00d4b3de9 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, const 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, const 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 *)); @@ -373,6 +379,7 @@ E void NDECL(heal_legs); /* ### do_name.c ### */ E char *FDECL(coord_desc, (int, int, char *, CHAR_P)); +E boolean FDECL(getpos_menu, (coord *, BOOLEAN_P)); E int FDECL(getpos, (coord *, BOOLEAN_P, const char *)); E void FDECL(getpos_sethilite, (void (*f)(int))); E void FDECL(new_mname, (struct monst *, int)); @@ -847,6 +854,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 *)); @@ -1664,8 +1672,11 @@ 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(get_menu_cmd_key, (CHAR_P)); E char FDECL(map_menu_cmd, (CHAR_P)); +E void FDECL(show_menu_controls, (winid, BOOLEAN_P)); E void FDECL(assign_warnings, (uchar *)); E char *FDECL(nh_getenv, (const char *)); E void FDECL(set_duplicate_opt_detection, (int)); diff --git a/include/flag.h b/include/flag.h index 47139d355..642c680be 100644 --- a/include/flag.h +++ b/include/flag.h @@ -424,9 +424,56 @@ 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, + NHKF_GETPOS_MENU, + NHKF_GETPOS_MENU_FOV, + + 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 +490,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 bb5c16b43..c0570d968 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/include/rm.h b/include/rm.h index 8419aa413..2f21bd634 100644 --- a/include/rm.h +++ b/include/rm.h @@ -230,6 +230,7 @@ #define is_cmap_trap(i) ((i) >= S_arrow_trap && (i) <= S_polymorph_trap) #define is_cmap_drawbridge(i) ((i) >= S_vodbridge && (i) <= S_hcdbridge) #define is_cmap_door(i) ((i) >= S_vodoor && (i) <= S_hcdoor) +#define is_cmap_wall(i) ((i) >= S_stone && (i) <= S_trwall) struct symdef { uchar sym; diff --git a/src/allmain.c b/src/allmain.c index 49e724dad..988004bb0 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 31e2a4bd1..8444fbfae 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 */ @@ -882,7 +888,7 @@ wiz_map_levltyp(VOID_ARGS) for (x = 1; x < COLNO; x++) { terrain = levl[x][y].typ; /* assumes there aren't more than 10+26+26 terrain types */ - row[x - 1] = (char) ((terrain == 0 && !may_dig(x, y)) + row[x - 1] = (char) ((terrain == STONE && !may_dig(x, y)) ? '*' : (terrain < 10) ? '0' + terrain @@ -890,7 +896,7 @@ wiz_map_levltyp(VOID_ARGS) ? 'a' + terrain - 10 : 'A' + terrain - 36); } - if (levl[0][y].typ != 0 || may_dig(0, y)) + if (levl[0][y].typ != STONE || may_dig(0, y)) row[x++] = '!'; row[x] = '\0'; putstr(win, 0, row); @@ -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,367 @@ 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 } -}; - -/* - * 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() +const char * +key2extcmddesc(key) +uchar key; { - int i, j, k, n; + if (Cmd.commands[key] && Cmd.commands[key]->ef_txt) + return Cmd.commands[key]->ef_desc; + return (char *) 0; +} - /* count the # of help entries */ - for (n = 0; extcmdlist[n].ef_txt[0] != '?'; n++) - ; +void +bind_key(key, command) +uchar key; +const char *command; +{ + struct ext_func_tab *extcmd; - 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; - - /* 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 */ + /* special case: "nothing" is reserved for unbinding */ + if (!strcmp(command, "nothing")) { + Cmd.commands[key] = (struct ext_func_tab *) 0; + 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]) != (struct ext_func_tab *) 0) { + 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", FALSE }, + { NHKF_RUSH, + "Prefix: rush until something interesting is seen", FALSE }, + { NHKF_RUN, + "Prefix: run until something extremely interesting is seen", FALSE }, + { NHKF_RUN2, + "Prefix: run until something extremely interesting is seen", TRUE }, + { NHKF_FIGHT, + "Prefix: force fight even if you don't see a monster", FALSE }, + { NHKF_FIGHT2, + "Prefix: force fight even if you don't see a monster", TRUE }, + { NHKF_NOPICKUP, + "Prefix: move without picking up objects/fighting", FALSE }, + { NHKF_RUN_NOPICKUP, + "Prefix: run without picking up objects/fighting", FALSE }, + { NHKF_DOINV, "inventory (same as #inventory)", TRUE }, + { NHKF_REQMENU, "Prefix: request a menu", FALSE }, +#ifdef REDO + { NHKF_DOAGAIN , "redo the previous command", FALSE }, +#endif + { 0, (const char *) 0, FALSE } + }; + + 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 + + putstr(datawin, 0, ""); + show_menu_controls(datawin, TRUE); + + 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); + } + + 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,12 +3054,17 @@ 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; - return 0; + + for (i = 0; i < 256; ++i) + if (Cmd.commands[i] && Cmd.commands[i]->ef_funct == fn) + return (char) i; + return '\0'; } +/* + * wizard mode sanity_check code + */ + static const char template[] = "%-27s %4ld %6ld"; static const char stats_hdr[] = " count bytes"; static const char stats_sep[] = "--------------------------- ----- -------"; @@ -3350,6 +3480,196 @@ 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', (char *) 0 }, /* 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, (char *) 0 }, /* no binding */ + { NHKF_CLICKLOOK, CMD_CLICKLOOK, (char *) 0 }, /* 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" }, + { NHKF_GETPOS_MENU, 'A', "getpos.menu" }, + { NHKF_GETPOS_MENU_FOV, 'a', "getpos.menu.cansee" } +}; + +boolean +bind_specialkey(key, command) +uchar key; +const 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, ',')) != 0 + || (autoc = index(autocomplete, ':')) != 0) { + *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 +3682,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[8]; + 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 < 8; 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 +3771,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 < 8; i++) { + back_dir_cmd[i] = + (struct ext_func_tab *) Cmd.commands[(uchar) Cmd.dirchars[i]]; + Cmd.commands[(uchar) Cmd.dirchars[i]] = (struct ext_func_tab *) 0; + } + 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 */ @@ -3458,12 +3797,26 @@ int NDECL((*cmd_func)); /* 'm' for removing saddle from adjacent monster without checking for containers at */ || cmd_func == doloot + /* travel: pop up a menu of interesting targets in view */ + || cmd_func == dotravel /* 'm' prefix allowed for some extended commands */ || cmd_func == doextcmd || cmd_func == doextlist) return TRUE; 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 +3833,7 @@ register char *cmd; context.nopick = 0; cmd = parse(); } - if (*cmd == '\033') { + if (*cmd == Cmd.spkeys[NHKF_ESC]) { context.move = FALSE; return; } @@ -3501,25 +3854,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 +3880,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 +3898,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 +3906,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 +3945,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 +3985,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 +3994,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 +4101,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 +4174,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 @@ -3870,7 +4232,6 @@ boolean nodiag; }; } - STATIC_OVL boolean help_dir(sym, msg) char sym; @@ -3918,7 +4279,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 +4341,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 +4354,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 +4362,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 +4401,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 +4428,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 +4480,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,20 +4521,20 @@ 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); + foo = get_count((char *) 0, '\0', LARGEST_INT, &tmpmulti); last_multi = multi = tmpmulti; } #ifdef ALTMETA 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 +4556,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 +4579,7 @@ parse() } clear_nhwindow(WIN_MESSAGE); if (prezero) - in_line[0] = '\033'; + in_line[0] = Cmd.spkeys[NHKF_ESC]; return in_line; } @@ -4306,7 +4674,7 @@ readchar() sym = '\033'; else if (sym != '\033') sym |= 0200; /* force 8th bit on */ -#endif /*ALTMETA*/ +#endif /*ALTMETA*/ } else if (sym == 0) { /* click event */ readchar_queue = click_to_cmd(x, y, mod); @@ -4333,16 +4701,23 @@ dotravel(VOID_ARGS) cc.y = u.uy; } iflags.getloc_travelmode = TRUE; - pline("Where do you want to travel to?"); - if (getpos(&cc, TRUE, "the desired destination") < 0) { - /* user pressed ESC */ - iflags.getloc_travelmode = FALSE; - return 0; + if (iflags.menu_requested) { + if (!getpos_menu(&cc, TRUE)) { + iflags.getloc_travelmode = FALSE; + return 0; + } + } else { + pline("Where do you want to travel to?"); + if (getpos(&cc, TRUE, "the desired destination") < 0) { + /* user pressed ESC */ + iflags.getloc_travelmode = FALSE; + return 0; + } } 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; } @@ -4368,7 +4743,7 @@ wiz_port_debug() { "show keystroke handler information (tty only)", win32con_handler_info }, #endif - { (char *) 0, (void NDECL((*) )) 0 } /* array terminator */ + { (char *) 0, (void NDECL((*))) 0 } /* array terminator */ }; num_menu_selections = SIZE(menu_selections) - 1; diff --git a/src/do_name.c b/src/do_name.c index c5ee58081..e55caf1d3 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -59,45 +59,90 @@ 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); } + Sprintf(sbuf, "Use '%s' for a menu of interesting targets in view.", + visctrl(Cmd.spkeys[NHKF_GETPOS_MENU_FOV])); + putstr(tmpwin, 0, sbuf); + Sprintf(sbuf, "Use '%s' for a menu of all interesting targets.", + visctrl(Cmd.spkeys[NHKF_GETPOS_MENU])); + 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) @@ -135,7 +180,10 @@ enum gloctypes { GLOC_DOOR, GLOC_EXPLORE, - NUM_GLOCS + NUM_GLOCS, + + GLOC_INTERESTING, + GLOC_INTERESTING_FOV }; @@ -183,6 +231,26 @@ int x,y, gloc; || IS_UNEXPLORED_LOC(x - 1, y) || IS_UNEXPLORED_LOC(x, y + 1) || IS_UNEXPLORED_LOC(x, y - 1))); + case GLOC_INTERESTING_FOV: + if (!cansee(x,y)) + return FALSE; + case GLOC_INTERESTING: + return gather_locs_interesting(x,y, GLOC_DOOR) + || !(glyph_is_cmap(glyph) + && (is_cmap_wall(glyph_to_cmap(glyph)) + || glyph_to_cmap(glyph) == S_tree + || glyph_to_cmap(glyph) == S_bars + || glyph_to_cmap(glyph) == S_ice + || glyph_to_cmap(glyph) == S_air + || glyph_to_cmap(glyph) == S_cloud + || glyph_to_cmap(glyph) == S_lava + || glyph_to_cmap(glyph) == S_water + || glyph_to_cmap(glyph) == S_pool + || glyph_to_cmap(glyph) == S_ndoor + || glyph_to_cmap(glyph) == S_room + || glyph_to_cmap(glyph) == S_darkroom + || glyph_to_cmap(glyph) == S_corr + || glyph_to_cmap(glyph) == S_litcorr)); } /*NOTREACHED*/ return FALSE; @@ -331,15 +399,88 @@ int cx, cy; } } +boolean +getpos_menu(ccp, fovonly) +coord *ccp; +boolean fovonly; +{ + coord *garr = DUMMY; + int gcount = 0; + winid tmpwin; + anything any; + int i, pick_cnt; + menu_item *picks = (menu_item *) 0; + char tmpbuf[BUFSZ]; + + gather_locs(&garr, &gcount, + fovonly ? GLOC_INTERESTING_FOV : GLOC_INTERESTING); + if (gcount < 2) { /* gcount always includes the hero */ + You("cannot %s anything interesting.", fovonly ? "see" : "detect"); + return FALSE; + } + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + any = zeroany; + + for (i = 0; i < gcount; i++) { + char fullbuf[BUFSZ]; + coord tmpcc; + const char *firstmatch = "unknown"; + int sym = 0; + any.a_int = i + 1; + tmpcc.x = garr[i].x; + tmpcc.y = garr[i].y; + if (do_screen_description(tmpcc, TRUE, sym, tmpbuf, &firstmatch)) { + (void) coord_desc(garr[i].x, garr[i].y, tmpbuf, iflags.getpos_coords); + Sprintf(fullbuf, "%s%s%s", firstmatch, (*tmpbuf ? " " : ""), tmpbuf); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, fullbuf, + MENU_UNSELECTED); + } + } + + Sprintf(tmpbuf, "Pick a target%s%s", + fovonly ? " in view" : "", + iflags.getloc_travelmode ? " for travel" : ""); + end_menu(tmpwin, tmpbuf); + pick_cnt = select_menu(tmpwin, PICK_ONE, &picks); + destroy_nhwindow(tmpwin); + if (pick_cnt > 0) { + ccp->x = garr[picks->item.a_int - 1].x; + ccp->y = garr[picks->item.a_int - 1].y; + free((genericptr_t) picks); + } + free((genericptr_t) garr); + return (pick_cnt > 0); +} + int getpos(ccp, force, goal) 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 +491,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 +538,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 +554,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 +592,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 +617,15 @@ 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_MENU] + || c == Cmd.spkeys[NHKF_GETPOS_MENU_FOV]) { + coord tmpcrd; + if (getpos_menu(&tmpcrd, (c == Cmd.spkeys[NHKF_GETPOS_MENU_FOV]))) { + cx = tmpcrd.x; + cy = tmpcrd.y; + } + goto nxtc; + } 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 +723,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/dog.c b/src/dog.c index 0d55baad8..fba998407 100644 --- a/src/dog.c +++ b/src/dog.c @@ -41,7 +41,7 @@ register struct monst *mtmp; mtmp->meating = 0; EDOG(mtmp)->droptime = 0; EDOG(mtmp)->dropdist = 10000; - EDOG(mtmp)->apport = 10; + EDOG(mtmp)->apport = ACURR(A_CHA); EDOG(mtmp)->whistletime = 0; EDOG(mtmp)->hungrytime = 1000 + monstermoves; EDOG(mtmp)->ogoal.x = -1; /* force error if used before set */ diff --git a/src/dokick.c b/src/dokick.c index 4737a27a0..f1e0dcddf 100644 --- a/src/dokick.c +++ b/src/dokick.c @@ -497,15 +497,20 @@ xchar x, y; || kickedobj == uchain) return 0; - if ((trap = t_at(x, y)) != 0 - && (((trap->ttyp == PIT || trap->ttyp == SPIKED_PIT) && !Passes_walls) - || trap->ttyp == WEB)) { - if (!trap->tseen) - find_trap(trap); - You_cant("kick %s that's in a %s!", something, - Hallucination ? "tizzy" : (trap->ttyp == WEB) ? "web" - : "pit"); - return 1; + if ((trap = t_at(x, y)) != 0) { + if (((trap->ttyp == PIT || trap->ttyp == SPIKED_PIT) && !Passes_walls) + || trap->ttyp == WEB) { + if (!trap->tseen) + find_trap(trap); + You_cant("kick %s that's in a %s!", something, + Hallucination ? "tizzy" : + (trap->ttyp == WEB) ? "web" : "pit"); + return 1; + } + if (trap->ttyp == STATUE_TRAP) { + activate_statue_trap(trap, x,y, FALSE); + return 1; + } } if (Fumbling && !rn2(3)) { diff --git a/src/drawing.c b/src/drawing.c index 4a16c1f90..88bc26750 100644 --- a/src/drawing.c +++ b/src/drawing.c @@ -671,6 +671,7 @@ struct symparse loadsyms[] = { { SYM_PCHAR, S_explode7, "S_explode7" }, { SYM_PCHAR, S_explode8, "S_explode8" }, { SYM_PCHAR, S_explode9, "S_explode9" }, + { SYM_OC, ILLOBJ_CLASS + SYM_OFF_O, "S_strange_obj" }, { SYM_OC, WEAPON_CLASS + SYM_OFF_O, "S_weapon" }, { SYM_OC, ARMOR_CLASS + SYM_OFF_O, "S_armor" }, { SYM_OC, ARMOR_CLASS + SYM_OFF_O, "S_armour" }, diff --git a/src/eat.c b/src/eat.c index 1191a8292..34277af03 100644 --- a/src/eat.c +++ b/src/eat.c @@ -93,6 +93,12 @@ register struct obj *obj; && (youmonst.data != &mons[PM_RUST_MONSTER] || is_rustprone(obj))) return TRUE; + /* Ghouls only eat non-veggy corpses or eggs (see dogfood()) */ + if (u.umonnum == PM_GHOUL) + return (boolean)((obj->otyp == CORPSE + && !vegan(&mons[obj->corpsenm])) + || (obj->otyp == EGG)); + if (u.umonnum == PM_GELATINOUS_CUBE && is_organic(obj) /* [g.cubes can eat containers and retain all contents as engulfed items, but poly'd player can't do that] */ @@ -1622,7 +1628,7 @@ struct obj *otmp; pline("Ecch - that must have been poisonous!"); if (!Poison_resistance) { losestr(rnd(4)); - losehp(rnd(15), !glob ? "poisonous corpse" : "posionous glob", + losehp(rnd(15), !glob ? "poisonous corpse" : "poisonous glob", KILLED_BY_AN); } else You("seem unaffected by the poison."); diff --git a/src/files.c b/src/files.c index 96d602fc1..1138284a9 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 4d75ba73a..8cb76021b 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/makemon.c b/src/makemon.c index 0b1af6f71..97e6edbd8 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -269,6 +269,58 @@ register struct monst *mtmp; } else if (mm == PM_NINJA) { /* extra quest villains */ (void) mongets(mtmp, rn2(4) ? SHURIKEN : DART); (void) mongets(mtmp, rn2(4) ? SHORT_SWORD : AXE); + } else if (ptr->msound == MS_GUARDIAN) { + /* quest "guardians" */ + switch (mm) { + case PM_STUDENT: + case PM_ATTENDANT: + case PM_ABBOT: + case PM_ACOLYTE: + case PM_GUIDE: + case PM_APPRENTICE: + if (rn2(2)) + (void) mongets(mtmp, rn2(3) ? DAGGER : KNIFE); + if (rn2(5)) + (void) mongets(mtmp, rn2(3) ? LEATHER_JACKET : LEATHER_CLOAK); + if (rn2(3)) + (void) mongets(mtmp, rn2(3) ? LOW_BOOTS : HIGH_BOOTS); + if (rn2(3)) + (void) mongets(mtmp, POT_HEALING); + break; + case PM_CHIEFTAIN: + case PM_PAGE: + case PM_ROSHI: + case PM_WARRIOR: + (void) mongets(mtmp, rn2(3) ? LONG_SWORD : SHORT_SWORD); + (void) mongets(mtmp, rn2(3) ? CHAIN_MAIL : LEATHER_ARMOR); + if (rn2(2)) + (void) mongets(mtmp, rn2(2) ? LOW_BOOTS : HIGH_BOOTS); + if (!rn2(3)) + (void) mongets(mtmp, LEATHER_CLOAK); + if (!rn2(3)) { + (void) mongets(mtmp, BOW); + m_initthrow(mtmp, ARROW, 12); + } + break; + case PM_HUNTER: + (void) mongets(mtmp, rn2(3) ? SHORT_SWORD : DAGGER); + if (rn2(2)) + (void) mongets(mtmp, rn2(2) ? LEATHER_JACKET : LEATHER_ARMOR); + (void) mongets(mtmp, BOW); + m_initthrow(mtmp, ARROW, 12); + break; + case PM_THUG: + (void) mongets(mtmp, CLUB); + (void) mongets(mtmp, rn2(3) ? DAGGER : KNIFE); + if (rn2(2)) + (void) mongets(mtmp, LEATHER_GLOVES); + (void) mongets(mtmp, rn2(2) ? LEATHER_JACKET : LEATHER_ARMOR); + break; + case PM_NEANDERTHAL: + (void) mongets(mtmp, CLUB); + (void) mongets(mtmp, LEATHER_ARMOR); + break; + } } break; diff --git a/src/mkmaze.c b/src/mkmaze.c index 639b17108..59ab57fac 100644 --- a/src/mkmaze.c +++ b/src/mkmaze.c @@ -237,7 +237,7 @@ int dir; mz_move(x, y, dir); mz_move(x, y, dir); if (x < 3 || y < 3 || x > x_maze_max || y > y_maze_max - || levl[x][y].typ != 0) + || levl[x][y].typ != STONE) return FALSE; return TRUE; } diff --git a/src/options.c b/src/options.c index ebe189e6e..2d30ba03a 100644 --- a/src/options.c +++ b/src/options.c @@ -469,21 +469,21 @@ static char def_inv_order[MAXOCLASSES] = { typedef struct { const char *name; char cmd; + const char *desc; } menu_cmd_t; -#define NUM_MENU_CMDS 11 -static const menu_cmd_t default_menu_cmd_info[NUM_MENU_CMDS] = { -/* 0*/ { "menu_first_page", MENU_FIRST_PAGE }, - { "menu_last_page", MENU_LAST_PAGE }, - { "menu_next_page", MENU_NEXT_PAGE }, - { "menu_previous_page", MENU_PREVIOUS_PAGE }, - { "menu_select_all", MENU_SELECT_ALL }, -/* 5*/ { "menu_deselect_all", MENU_UNSELECT_ALL }, - { "menu_invert_all", MENU_INVERT_ALL }, - { "menu_select_page", MENU_SELECT_PAGE }, - { "menu_deselect_page", MENU_UNSELECT_PAGE }, - { "menu_invert_page", MENU_INVERT_PAGE }, -/*10*/ { "menu_search", MENU_SEARCH }, +static const menu_cmd_t default_menu_cmd_info[] = { + { "menu_first_page", MENU_FIRST_PAGE, "Go to first page" }, + { "menu_last_page", MENU_LAST_PAGE, "Go to last page" }, + { "menu_next_page", MENU_NEXT_PAGE, "Go to next page" }, + { "menu_previous_page", MENU_PREVIOUS_PAGE, "Go to previous page" }, + { "menu_select_all", MENU_SELECT_ALL, "Select all items" }, + { "menu_deselect_all", MENU_UNSELECT_ALL, "Unselect all items" }, + { "menu_invert_all", MENU_INVERT_ALL, "Insert selection" }, + { "menu_select_page", MENU_SELECT_PAGE, "Select items in current page" }, + { "menu_deselect_page", MENU_UNSELECT_PAGE, "Unselect items in current page" }, + { "menu_invert_page", MENU_INVERT_PAGE, "Invert current page selection" }, + { "menu_search", MENU_SEARCH, "Search and toggle matching items" }, }; /* @@ -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. @@ -3233,7 +3237,7 @@ boolean tinitial, tfrom_file; } /* check for menu command mapping */ - for (i = 0; i < NUM_MENU_CMDS; i++) { + for (i = 0; i < SIZE(default_menu_cmd_info); i++) { fullname = default_menu_cmd_info[i].name; if (duplicate) complain_about_duplicate(opts, 1); @@ -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 < SIZE(default_menu_cmd_info); 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" }; @@ -3525,6 +3578,18 @@ char from_ch, to_ch; } } +char +get_menu_cmd_key(ch) +char ch; +{ + char *found = index(mapped_menu_op, ch); + if (found) { + int idx = (int) (found - mapped_menu_op); + ch = mapped_menu_cmds[idx]; + } + return ch; +} + /* * Map the given character to its corresponding menu command. If it * doesn't match anything, just return the original. @@ -3541,6 +3606,57 @@ char ch; return ch; } +void +show_menu_controls(win, dolist) +winid win; +boolean dolist; +{ + char buf[BUFSZ]; + + putstr(win, 0, "Menu control keys:"); + if (dolist) { + int i; + for (i = 0; i < SIZE(default_menu_cmd_info); i++) { + Sprintf(buf, "%-8s %s", + visctrl(get_menu_cmd_key(default_menu_cmd_info[i].cmd)), + default_menu_cmd_info[i].desc); + putstr(win, 0, buf); + } + } else { + putstr(win, 0, ""); + putstr(win, 0, " Page All items"); + Sprintf(buf, " Select %s %s", + visctrl(get_menu_cmd_key(MENU_SELECT_PAGE)), + visctrl(get_menu_cmd_key(MENU_SELECT_ALL))); + putstr(win, 0, buf); + Sprintf(buf, "Deselect %s %s", + visctrl(get_menu_cmd_key(MENU_UNSELECT_PAGE)), + visctrl(get_menu_cmd_key(MENU_UNSELECT_ALL))); + putstr(win, 0, buf); + Sprintf(buf, " Invert %s %s", + visctrl(get_menu_cmd_key(MENU_INVERT_PAGE)), + visctrl(get_menu_cmd_key(MENU_INVERT_ALL))); + putstr(win, 0, buf); + putstr(win, 0, ""); + Sprintf(buf, " Go to %s Next page", + visctrl(get_menu_cmd_key(MENU_NEXT_PAGE))); + putstr(win, 0, buf); + Sprintf(buf, " %s Previous page", + visctrl(get_menu_cmd_key(MENU_PREVIOUS_PAGE))); + putstr(win, 0, buf); + Sprintf(buf, " %s First page", + visctrl(get_menu_cmd_key(MENU_FIRST_PAGE))); + putstr(win, 0, buf); + Sprintf(buf, " %s Last page", + visctrl(get_menu_cmd_key(MENU_LAST_PAGE))); + putstr(win, 0, buf); + putstr(win, 0, ""); + Sprintf(buf, " %s Search and toggle matching entries", + visctrl(get_menu_cmd_key(MENU_SEARCH))); + putstr(win, 0, buf); + } +} + #if defined(MICRO) || defined(MAC) || defined(WIN32) #define OPTIONS_HEADING "OPTIONS" #else diff --git a/src/pager.c b/src/pager.c index 184027ba0..c14a49bf1 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 @@ -1705,6 +1720,15 @@ hmenu_doextlist() (void) doextlist(); } +void +domenucontrols() +{ + winid cwin = create_nhwindow(NHW_TEXT); + show_menu_controls(cwin, FALSE); + display_nhwindow(cwin, FALSE); + destroy_nhwindow(cwin); +} + /* data for dohelp() */ static struct { void (*f)(); @@ -1718,7 +1742,9 @@ 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." }, + { domenucontrols, "List menu control keys" }, { dispfile_license, "The NetHack license." }, { docontact, "Support information." }, #ifdef PORT_HELP diff --git a/src/vault.c b/src/vault.c index 143f390dc..909cc3872 100644 --- a/src/vault.c +++ b/src/vault.c @@ -814,7 +814,7 @@ nextpos: else ny += dy; - while ((typ = (crm = &levl[nx][ny])->typ) != 0) { + while ((typ = (crm = &levl[nx][ny])->typ) != STONE) { /* in view of the above we must have IS_WALL(typ) or typ == POOL */ /* must be a wall here */ if (isok(nx + nx - x, ny + ny - y) && !IS_POOL(typ) diff --git a/win/tty/getline.c b/win/tty/getline.c index 9c9efdf08..4d86f9a11 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/mhmap.c b/win/win32/mhmap.c index 821c91802..8b301da66 100644 --- a/win/win32/mhmap.c +++ b/win/win32/mhmap.c @@ -9,6 +9,7 @@ #include "mhinput.h" #include "mhfont.h" +#include "color.h" #include "patchlevel.h" #define NHMAP_FONT_NAME TEXT("Terminal") @@ -1025,41 +1026,8 @@ nhglyph2charcolor(short g, uchar *ch, int *color) COLORREF nhcolor_to_RGB(int c) { - switch (c) { - case CLR_BLACK: - return RGB(0x55, 0x55, 0x55); - case CLR_RED: - return RGB(0xFF, 0x00, 0x00); - case CLR_GREEN: - return RGB(0x00, 0x80, 0x00); - case CLR_BROWN: - return RGB(0xA5, 0x2A, 0x2A); - case CLR_BLUE: - return RGB(0x00, 0x00, 0xFF); - case CLR_MAGENTA: - return RGB(0xFF, 0x00, 0xFF); - case CLR_CYAN: - return RGB(0x00, 0xFF, 0xFF); - case CLR_GRAY: - return RGB(0xC0, 0xC0, 0xC0); - case NO_COLOR: - return RGB(0xFF, 0xFF, 0xFF); - case CLR_ORANGE: - return RGB(0xFF, 0xA5, 0x00); - case CLR_BRIGHT_GREEN: - return RGB(0x00, 0xFF, 0x00); - case CLR_YELLOW: - return RGB(0xFF, 0xFF, 0x00); - case CLR_BRIGHT_BLUE: - return RGB(0x00, 0xC0, 0xFF); - case CLR_BRIGHT_MAGENTA: - return RGB(0xFF, 0x80, 0xFF); - case CLR_BRIGHT_CYAN: - return RGB(0x80, 0xFF, 0xFF); /* something close to aquamarine */ - case CLR_WHITE: - return RGB(0xFF, 0xFF, 0xFF); - default: - return RGB(0x00, 0x00, 0x00); /* black */ - } + if (c >= 0 && c < CLR_MAX) + return GetNHApp()->regMapColors[c]; + return RGB(0x00, 0x00, 0x00); } diff --git a/win/win32/mswproc.c b/win/win32/mswproc.c index 7247dbbb3..fe3ce07b5 100644 --- a/win/win32/mswproc.c +++ b/win/win32/mswproc.c @@ -8,6 +8,7 @@ */ #include "hack.h" +#include "color.h" #include "dlb.h" #include "func_tab.h" /* for extended commands */ #include "winMS.h" @@ -1726,7 +1727,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 @@ -2276,6 +2279,25 @@ mswin_read_reg() DWORD size; DWORD safe_buf; char keystring[MAX_PATH]; + int i; + COLORREF default_mapcolors[CLR_MAX] = { + RGB(0x55, 0x55, 0x55), /* CLR_BLACK */ + RGB(0xFF, 0x00, 0x00), /* CLR_RED */ + RGB(0x00, 0x80, 0x00), /* CLR_GREEN */ + RGB(0xA5, 0x2A, 0x2A), /* CLR_BROWN */ + RGB(0x00, 0x00, 0xFF), /* CLR_BLUE */ + RGB(0xFF, 0x00, 0xFF), /* CLR_MAGENTA */ + RGB(0x00, 0xFF, 0xFF), /* CLR_CYAN */ + RGB(0xC0, 0xC0, 0xC0), /* CLR_GRAY */ + RGB(0xFF, 0xFF, 0xFF), /* NO_COLOR */ + RGB(0xFF, 0xA5, 0x00), /* CLR_ORANGE */ + RGB(0x00, 0xFF, 0x00), /* CLR_BRIGHT_GREEN */ + RGB(0xFF, 0xFF, 0x00), /* CLR_YELLOW */ + RGB(0x00, 0xC0, 0xFF), /* CLR_BRIGHT_BLUE */ + RGB(0xFF, 0x80, 0xFF), /* CLR_BRIGHT_MAGENTA */ + RGB(0x80, 0xFF, 0xFF), /* CLR_BRIGHT_CYAN */ + RGB(0xFF, 0xFF, 0xFF) /* CLR_WHITE */ + }; sprintf(keystring, "%s\\%s\\%s\\%s", CATEGORYKEY, COMPANYKEY, PRODUCTKEY, SETTINGSKEY); @@ -2286,6 +2308,9 @@ mswin_read_reg() GetNHApp()->saveRegistrySettings = 1; /* Normally, we always save */ GetNHApp()->regNetHackMode = TRUE; + for (i = 0; i < CLR_MAX; i++) + GetNHApp()->regMapColors[i] = default_mapcolors[i]; + if (RegOpenKeyEx(HKEY_CURRENT_USER, keystring, 0, KEY_READ, &key) != ERROR_SUCCESS) return; @@ -2338,6 +2363,14 @@ mswin_read_reg() NHGETREG_DWORD(INVENTBOTTOM, GetNHApp()->rtInvenWindow.bottom); #undef NHGETREG_DWORD + for (i = 0; i < CLR_MAX; i++) { + COLORREF cl; + char mapcolorkey[64]; + sprintf(mapcolorkey, "MapColor%02d", i); + if (RegQueryValueEx(key, mapcolorkey, NULL, NULL, (BYTE *)&cl, &size) == ERROR_SUCCESS) + GetNHApp()->regMapColors[i] = cl; + } + RegCloseKey(key); /* check the data for validity */ @@ -2356,6 +2389,7 @@ mswin_write_reg() { HKEY key; DWORD disposition; + int i; if (GetNHApp()->saveRegistrySettings) { char keystring[MAX_PATH]; @@ -2417,6 +2451,13 @@ mswin_write_reg() NHSETREG_DWORD(INVENTBOTTOM, GetNHApp()->rtInvenWindow.bottom); #undef NHSETREG_DWORD + for (i = 0; i < CLR_MAX; i++) { + COLORREF cl = GetNHApp()->regMapColors[i]; + char mapcolorkey[64]; + sprintf(mapcolorkey, "MapColor%02d", i); + RegSetValueEx(key, mapcolorkey, 0, REG_DWORD, (BYTE *)&cl, sizeof(DWORD)); + } + RegCloseKey(key); } } diff --git a/win/win32/winMS.h b/win/win32/winMS.h index 7938d83d1..f35e601c9 100644 --- a/win/win32/winMS.h +++ b/win/win32/winMS.h @@ -25,6 +25,7 @@ #include #include #include "hack.h" +#include "color.h" /* Create an array to keep track of the various windows */ @@ -94,6 +95,8 @@ typedef struct mswin_nhwindow_app { regNetHackMode; /* NetHack mode means no Windows keys in some places */ + COLORREF regMapColors[CLR_MAX]; + LONG regMainMinX; LONG regMainMinY; LONG regMainMaxX;