diff --git a/dat/opthelp b/dat/opthelp index cfa7692b2..06fbf092d 100644 --- a/dat/opthelp +++ b/dat/opthelp @@ -6,8 +6,8 @@ autodescribe describe the terrain under cursor [FALSE] autodig dig if moving and wielding digging tool [FALSE] autoopen walking into a door attempts to open it [TRUE] autopickup automatically pick up objects you move over [TRUE] -autoquiver when firing with an empty quiver, select some - suitable inventory weapon to fill the quiver [FALSE] +autoquiver when firing with an empty quiver, select some [FALSE] + suitable inventory weapon to fill the quiver BIOS allow the use of IBM ROM BIOS calls [FALSE] cmdassist give help for errors on direction & other commands [TRUE] confirm ask before hitting tame or peaceful monsters [TRUE] @@ -15,6 +15,8 @@ DECgraphics use DEC/VT line-drawing characters for the dungeon [FALSE] eight_bit_tty send 8-bit characters straight to terminal [FALSE] extmenu use a menu for selecting extended commands (#) [FALSE] fixinv try to retain the same letter for the same object [TRUE] +goldX when filtering objects by bless/curse state, [FALSE] + whether to classify gold as X (unknown) or U (uncursed) help print all available info when using the / command [TRUE] IBMgraphics use IBM extended characters for the dungeon [FALSE] ignintr ignore interrupt signal, including breaks [FALSE] @@ -29,8 +31,8 @@ null allow nulls to be sent to your terminal [TRUE] delay code) if moving objects seem to teleport across rooms perm_invent keep inventory in a permanent window [FALSE] pickup_thrown override pickup_types for thrown objects [TRUE] -pushweapon when wielding a new weapon, put your previously - wielded weapon into the secondary weapon slot [FALSE] +pushweapon when wielding a new weapon, put your previously [FALSE] + wielded weapon into the secondary weapon slot rawio allow the use of raw I/O [FALSE] rest_on_space count the space bar as a rest character [FALSE] safe_pet prevent you from (knowingly) attacking your pet(s) [TRUE] @@ -54,43 +56,41 @@ use_inverse display detected monsters in highlighted manner [FALSE] verbose print more commentary during the game [TRUE] - There are further boolean options controlled by compilation flags. Boolean option if INSURANCE was set at compile time: -checkpoint save game state after each level change, for possible [TRUE] - recovery after program crash +checkpoint save game state after each level change, for [TRUE] + possible recovery after program crash Boolean option if NEWS was set at compile time: -news print any news from game administrator on startup [TRUE] +news print any news from game administrator on startup [TRUE] Boolean option if MFLOPPY was set at compile time: -checkspace check free disk space before writing files to disk [TRUE] - +checkspace check free disk space before writing files to disk [TRUE] Boolean option if SCORE_ON_BOTL was set at compile time: -showscore display your approximate accumulated score [FALSE] +showscore display your approximate accumulated score [FALSE] Boolean options if TEXTCOLOR was set at compile time: -color use different colors for objects on screen [TRUE for micros] -hilite_pet display pets in a highlighted manner [FALSE] +color use different colors for objects on screen [TRUE for micros] +hilite_pet display pets in a highlighted manner [FALSE] Boolean option if TIMED_DELAY was set at compile time (tty interface only): -timed_delay on unix and VMS, use a timer instead of sending +timed_delay on unix and VMS, use a timer instead of sending [TRUE] extra screen output when attempting to pause for display effect. on MSDOS without the termcap - lib, whether or not to pause for visual effect. [TRUE] + lib, whether or not to pause for visual effect. Boolean option for Amiga, or for others if ALTMETA was set at compile time: altmeta For Amiga, treat Alt+key as Meta+key. [TRUE] altmeta For unix and VMS, treat two character sequence - "ESC c" as M-c (Meta+c, 8th bit set) when nethack - obtains a command from player's keyboard. [FALSE] + "ESC c" as M-c (Meta+c, 8th bit set) when nethack [FALSE] + obtains a command from player's keyboard. Boolean option if USE_TILES was set at compile time (MSDOS protected mode only): -preload_tiles control whether tiles get pre-loaded into RAM at the - start of the game. Doing so enhances performance - of the tile graphics, but uses more memory. [TRUE] +preload_tiles control whether tiles get pre-loaded into RAM at [TRUE] + the start of the game. Doing so enhances performance + of the tile graphics, but uses more memory. Any Boolean option can be negated by prefixing it with a '!' or 'no'. @@ -99,34 +99,45 @@ Compound options are written as option_name:option_value. Compound options which can be set during the game are: -boulder override the default boulder symbol with another default: [`] -disclose the types of information you want offered at the end of the - game [ni na nv ng nc no] -fruit the name of a fruit you enjoy eating [slime mold] +boulder override the default boulder symbol [`] +disclose the types of information you want [ni na nv ng nc no] + offered at the end of the game + (space separated list of two-character values; + prefix: '+' always disclose, '-' never disclose, + 'n' prompt with default "no", 'y' prompt with default "yes", + 'a' prompt to select sorting order (for suffix 'v' only); + suffix: 'i' inventory, 'a' attributes, 'v' vanquished + monsters, 'g' genocided and extinct monsters, 'c' conduct, + 'o' dungeon overview) +fruit the name of a fruit you enjoy eating [slime mold] (basically a whimsy which NetHack uses from time to time). -menustyle user interface for selection of multiple objects: +menustyle user interface for selection of multiple objects: [Full] Traditional -- prompt for classes of interest, then prompt item-by-item for those classes; Combination -- prompt for classes of interest, then use a menu for choosing items; Full -- menu for classes of interest, then item menu; Partial -- skip class filtering, use menu of all items; - only the first letter ('T','C','P','F') matters; 'N' (None) - is a synonym for 'T', as is boolean style negation [Full] -number_pad alphabetic versus numeric control over movement: + only the first letter ('T','C','F','P') matters + (With Traditional, many actions allow pseudo-class 'm' to + request a menu for choosing items: one-shot Combination.) +number_pad alphabetic versus numeric control over movement: [0] 0 -- traditional hjkl + yubn movement (default); 1 -- digits control movement, for use with numeric keypad; 2 -- same as 1, but '5' works as 'g' prefix rather than 'G'; 3 -- numeric for phone keypad (1,2,3 above, 7,8,9 below); - 4 -- phone keypad (3) combined with MSDOS compatibility (2); - -1 -- alphabetic movement but 'z' swapped with 'y'. [0] -packorder a list of default symbols for kinds of objects that gives the - order in which your pack will be displayed [")[%?+!=/(*`0_] + 4 -- phone keypad (3) combined with '5' preference (2); + -1 -- alphabetic movement but 'z' swapped with 'y'. + Setting number_pad (to a positive value) affects how all + digit keys are handled, not just those on numeric keypad. +packorder a list of default symbols for kinds of [")[%?+!=/(*`0_] + objects that gives the order in which your inventory (and + some other things) gets shown if the 'sortpack' option is on (If you specify only some kinds of items, the others from the default order will be appended to the end.) -paranoid_confirmation space separated list of situations where alternate - prompting is desired [paranoid_confirmation:pray] - Confirm -- when requiring yes, also require no to reject +paranoid_confirmation space separated list [paranoid_confirmation:pray] + of situations where alternate prompting is desired + Confirm -- when requiring "yes", also require "no" to reject quit -- yes vs y to confirm quitting or to enter explore mode die -- yes vs y to confirm dying (for explore or debug mode) bones -- yes vs y to confirm saving bones data in debug mode @@ -134,87 +145,109 @@ paranoid_confirmation space separated list of situations where alternate pray -- y to confirm an attempt to pray; on by default Remove -- always pick from inventory for 'R' and 'T' even when wearing just one applicable item to remove or take off -pickup_burden when you pick up an item that exceeds this encumberance +pickup_burden when you pick up an item that exceeds this encumberance [S] level (Unencumbered, Burdened, streSsed, straiNed, overTaxed, - or overLoaded), you will be asked if you want to continue. [S] -pickup_types a list of default symbols for kinds of objects to autopickup - when that option is on [all] -pile_limit for feedback when walking across floor objects, threshold at - which "there are many objects here" is displayed instead of - listing the objects. (0 means "always list objects.") [5] -runmode controls how often the map window is updated when performing + or overLoaded), you will be asked if you want to continue. +pickup_types a list of default symbols for kinds of objects to [] + autopickup when that option is on; empty list means "all" +pile_limit for feedback when walking across floor objects, [5] + threshold at which "there are objects here" is displayed + instead of listing them. (0 means "always list objects.") +runmode controls how often the map window is updated for [run] multi-step movement (various running modes or travel command): teleport -- don't update map until movement stops; run -- periodically update map (interval is seven steps); walk -- update map after every step; crawl -- like walk, but delay after making each step. - (This only affects screen display, not actual movement.) [run] -scores the parts of the score list you wish to see when the game ends - You choose a combination of top scores, scores around the top - scores, and all of your own scores. [!own/3 top/2 around] -suppress_alert disable various version-specific warnings about changes + (This only affects screen display, not actual movement.) +scores the parts of the score list you wish [!own/3 top/2 around] + to see when the game ends. You choose a combination of + top scores, scores around the top scores, and all of your + own scores. +suppress_alert disable various version-specific warnings about changes [] in game play or the user interface, such as notification given for the 'Q' command that quitting is now done via #quit (e.g., use suppress_alert:3.3.1 to stop that and any other - notifications added in that version or earlier) default: [(none)] -whatis_coord controls whether to include map coordinates when autodescribe - is active for the '/' and ';' commands. Value is the first - letter of one of - compass -- ('east' or '3s' or '2n,4w') - map -- (map column x=0 is not used) - screen -- [row,column] (row is offset to match tty usage) - none -- no coordinates shown; default: [n] + notifications added in that version or earlier) +whatis_coord controls whether to include map coordinates when [n] + autodescribe is active for the '/' and ';' commands. + Value is the first letter of one of + compass -- (relative to you; 'east' or '3s' or '2n,4w') + full compass -- ('east' or '3south' or '2north,4west') + map -- (map column x=0 is not used) + screen -- [row,column] (row is offset to match tty usage) + none -- no coordinates shown. Compound options which may be set only on startup are: -align Your starting alignment (align:lawful, align:neutral, - or align:chaotic). You may specify just the first letter. [RANDOM] -catname the name of your first cat [NONE] -dogname the name of your first dog [NONE] -gender Your starting gender (gender:male or gender:female). +align Your starting alignment (lawful, neutral, chaotic, [random] + or random). Many roles restrict the choice to a subset. + You may specify just the first letter. +catname the name of your first cat [none] +dogname the name of your first dog [none] + Several roles who start with a dog have one whose name is + pre-set (for example, "Hachi" for Samurai), but that name + will be overridden if you specify dogname. +gender Your starting gender (male, female, or random). [random] You may specify just the first letter. Although you can - still denote your gender using the "male" and "female" - options, the "gender" option will take precedence. [RANDOM] -horsename the name of your first horse [NONE] -menu_* create single character accelerators for menu commands. Below - is a list of all commands. Each is followed by a list of window- - ports that implement them: 'x' is X11, 't' is tty, 'g' is Gem, - 'a' is Amiga. - menu_deselect_all deselect all items in a menu [-](gxta) - menu_deselect_page deselect all items on this menu page [\](gta) - menu_first_page jump to the first page in a menu [^](gta) - menu_invert_all invert all items in a menu [@](gxta) - menu_invert_page invert all items on this menu page [~](gta) - menu_last_page jump to the last page in a menu [|](gta) - menu_next_page goto the next menu page [>](gta) - menu_previous_page goto the previous menu page [<](gta) - menu_search search for a menu item [:](gxta) - menu_select_all select all items in a menu [.](gxta) - menu_select_page select all items on this menu page [,](gta) -msghistory number of top line messages to save [20] -name the name of your character [obtained by asking the system or - the player] -pettype your preferred type of pet (cat or dog), if your character - class uses both types; or none for no pet [RANDOM] -playmode normal play or non-scoring explore mode or debug mode [normal] -race Your starting race (e.g., race:Human, race:Elf). [RANDOM] -role Your starting role (e.g., role:Barbarian, role:Valk). + still denote your gender using the old "male" and "female" + boolean options, the "gender" option will take precedence. +horsename the name of your first horse [none] +menu_* specify single character accelerators for menu commands. + Here is a list of all commands with their default keystroke + followed by a list of window-ports that implement them: + 'x' is X11, 't' is tty, 'g' is Gem, 'a' is Amiga. + menu_deselect_all deselect all items in a menu [-](xtga) + menu_deselect_page deselect all items on this menu page [\](tga) + menu_first_page jump to the first page in a menu [^](tga) + menu_invert_all invert all items in a menu [@](xtga) + menu_invert_page invert all items on this menu page [~](tga) + menu_last_page jump to the last page in a menu [|](tga) + menu_next_page goto the next menu page [>](tga) + menu_previous_page goto the previous menu page [<](tga) + menu_search search for a menu item [:](xtga) + menu_select_all select all items in a menu [.](xtga) + menu_select_page select all items on this menu page [,](tga) +msghistory number of top line messages to save [20] +name the name of your character [defaults to username on multi- + user systems, asks "who are you?" on single-user systems or if + the username is classified as generic like "games"] + MS Windows is treated as single-user even though it supports + usernames. If character name is specified on the command + line (typically via 'nethack -u myname' depending upon type + of system and method of access to it), that name overrides + 'name' from your options. +pettype your preferred type of pet (cat, dog, horse, random, [random] + or none), if your role allows more than one type (or if you + want to avoid a starting pet). Most roles allow dog or cat + but not horse. For roles which force a particular type, + pettype is ignored unless it specifies 'none'. +playmode normal play or non-scoring explore mode or debug mode [normal] +race Your starting race (e.g., race:Human, race:Elf). [random] + Most roles restrict race choice to a subset. +role Your starting role (e.g., role:Barbarian, role:Valk). [random] Although you can specify just the first letter(s), it will choose only the first role it finds that matches; thus, it is recommended that you spell out as much of the role name as possible. You can also still denote your role by - appending it to the "name" option (e.g., name:Vic-V), but the - "role" option will take precedence. [RANDOM] - -windowtype windowing system to be used [depends on operating system] + appending it to the "name" option (e.g., name:Vic-V), but + the "role" option will take precedence. +windowtype windowing system to be used [depends on operating system and + compile-time setup] if more than one choice is available. + Most instances of the program support only one window-type; + when that is the case, you don't need to specify anything. + The list of supported window-types in your program can be + seen while the program is running by using the #version + command or from outside the program by examining the text file + named 'options' which is generated when building it. Compound option if TTY_GRAPHICS was set at compile time: -msg_window the type of message window to use: +msg_window the type of message window to use: [single] single -- One message at a time full -- Full window with all saved top line messages reverse -- Same as full, but messages printed most-recent-first combination -- Two single messages, then as full - default: single + Some sample options lists are: !autopickup,!tombstone,name:Gandalf,scores:own/3 top/2 around diff --git a/dat/wizhelp b/dat/wizhelp index b85d301ce..667f657d4 100644 --- a/dat/wizhelp +++ b/dat/wizhelp @@ -1,28 +1,29 @@ Debug-Mode Quick Reference: -^E == detect secret doors and traps. -^F == map level; reveals traps and secret corridors but not secret doors. -^G == create monster by name or class. -^I == identify items in pack. -^O == tell locations of special levels. -^T == do intra-level teleport. -^V == do trans-level teleport. -^W == make a wish for an item or a trap or a limited subset of terrain. -^X == show attributes including intrinsic attributes. +^E == detect secret doors and traps +^F == map level; reveals traps and secret corridors but not secret doors +^G == create monster by name or class +^I == identify items in pack +^T == do intra-level teleport +^V == do trans-level teleport; '?' yields menu of special destinations +^W == make a wish for an item or a trap or a limited subset of terrain +^X == show status, attributes, and characteristics (extended enlightenment) -#levelchange == change experience level -#lightsources == show mobile light sources +#levelchange == set hero's experience level +#lightsources == show mobile light sources #monpolycontrol == control monster polymorphs -#panic == panic test -#polyself == polymorph self -#seenv == show seen vectors -#stats == show memory statistics -#timeout == look at timeout queue -#vision == show vision array -#vanquished == disclose counts of dead monsters -#wizmakemap == recreate the current level -#wizsmell == smell a monster -#wizintrinsic == set selected intrinsic timeouts -#wizrumorcheck == validate first and last rumor for true and false set -#wmode == show wall modes +#panic == panic test (warning: current game will be terminated) +#polyself == polymorph self +#seenv == show seen vectors +#stats == show memory statistics +#terrain == show current level (more options than in normal play) +#timeout == look at timeout queue and hero's timed intrinsics +#vanquished == disclose counts of dead monsters sorted in various ways +#vision == show vision array +#wizintrinsic == set selected intrinsic timeouts +#wizmakemap == recreate the current dungeon level +#wizrumorcheck == validate first and last rumor for true and false set +#wizsmell == smell a monster +#wizwhere == show dungeon placement of all special levels +#wmode == show wall modes diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index d9263acf9..32e71245f 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -1,4 +1,4 @@ -.\" $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.203 $ $NHDT-Date: 1456192371 2016/02/23 01:52:51 $ +.\" $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.224 $ $NHDT-Date: 1498078870 2017/06/21 21:01:10 $ .ds h0 "NetHack Guidebook .ds h1 .ds h2 % @@ -2507,6 +2507,14 @@ The default is to randomly pick an appropriate gender. If you prefix the value with `!' or ``no'', you will exclude that gender from being picked randomly. Cannot be set with the `O' command. Persistent. +.lp "goldX " +When filtering objects based on bless/curse state (BUCX), whether to +treat gold pieces as X (unknown bless/curse state, when `on') +or U (known to be uncursed, when `off', the default). +Gold is never blessed or cursed, but it is not described as ``uncursed'' +even when the +.op implicit_uncursed +option is `off'. .lp "help " If more information is available for an object looked at with the `/' command, ask if you want to see it (default on). Turning help diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 11c40a89a..07cc90964 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -3071,6 +3071,13 @@ If you prefix the value with `{\tt !}' or ``{\tt no}'', you will exclude that gender from being picked randomly. Cannot be set with the `{\tt O}' command. Persistent. %.lp +\item[\ib{goldX}] +When filtering objects based on bless/curse state (BUCX), whether to +treat gold pieces as {\tt X} (unknown bless/curse state, when `on') +or {\tt U} (known to be uncursed, when `off', the default). +Gold is never blessed or cursed, but it is not described as ``uncursed'' +even when the {\it implicit\verb+_+uncursed\/} option is `off'. +%.lp \item[\ib{help}] If more information is available for an object looked at with the `{\tt /}' command, ask if you want to see it (default on). diff --git a/doc/fixes36.1 b/doc/fixes36.1 index bf5df58cb..30ffed891 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -398,7 +398,12 @@ adult green dragons and the Chromatic Dragon were blinded by gas clouds named floating eye (when hit by another monster with reflection) or named silver weapon (when hero hits silver-hating monster) could disrupt message formatting and conceivably trigger crash if name had '%' in it -crashes for 'A' above were downgraded to impossible "cursed without otmp" +fix "you feel like a new man" if female human hero's polymorph attempt failed + while in human form (when already poly'd, "new woman" correctly shown) +fix the 'A' command to have the 'D' command's fix for C331-1 (quirk for + menustyle:Combination; if user included 'a' in "which object classes?" + response, to operate on applicable all items, there would still be a + followup menu asking to choose specific items) Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository @@ -446,6 +451,8 @@ once Moloch's Sanctum (or Astral Plane via wizard mode level teleport direct high priests had been incorrectly flagged as extinct attempting to name an item as an artifact and failing via hand slip violates illiterate conduct +crashes for 'A' above were downgraded to impossible "cursed without otmp" +wizhelp: ^O is #overview in wizard mode too; #wizwhere shows dungeon layout Platform- and/or Interface-Specific Fixes @@ -579,6 +586,13 @@ 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 wizard-mode command #wizmakemap to recreate the current level +'goldX' boolean option to treat gold pieces as X (vs U) during BUCX filtering + (should be persistent but is reset each save/restore cycle in order + to avoid breaking 3.6.0 save files) +for menustyle:Traditional and Combination, support BUCX filtering for item + pick-up and container put-in and take-out; also for object IDing +for menustyle:Full and Traditional and Combination, support BUCX filtering + for the 'A' command Platform- and/or Interface-Specific New Features diff --git a/include/extern.h b/include/extern.h index 88afd012e..dcbf8f003 100644 --- a/include/extern.h +++ b/include/extern.h @@ -983,7 +983,9 @@ E void NDECL(reassign); E int NDECL(doorganize); E void NDECL(free_pickinv_cache); E int FDECL(count_unpaid, (struct obj *)); -E int FDECL(count_buc, (struct obj *, int)); +E int FDECL(count_buc, (struct obj *, int, boolean (*)(OBJ_P))); +E void FDECL(tally_BUCX, (struct obj *, BOOLEAN_P, + int *, int *, int *, int *, int *)); E long FDECL(count_contents, (struct obj *, BOOLEAN_P, BOOLEAN_P, BOOLEAN_P)); E void FDECL(carry_obj_effects, (struct obj *)); E const char *FDECL(currency, (long)); @@ -1783,6 +1785,7 @@ E void NDECL(getlock); E int FDECL(collect_obj_classes, (char *, struct obj *, BOOLEAN_P, boolean FDECL((*), (OBJ_P)), int *)); E boolean FDECL(rider_corpse_revival, (struct obj *, BOOLEAN_P)); +E boolean FDECL(menu_class_present, (int)); E void FDECL(add_valid_menu_class, (int)); E boolean FDECL(allow_all, (struct obj *)); E boolean FDECL(allow_category, (struct obj *)); @@ -2127,6 +2130,7 @@ E void FDECL(store_savefileinfo, (int)); /* ### shk.c ### */ +E void FDECL(setpaid, (struct monst *)); E long FDECL(money2mon, (struct monst *, long)); E void FDECL(money2u, (struct monst *, long)); E void FDECL(shkgone, (struct monst *)); @@ -2764,6 +2768,7 @@ E void FDECL(detect_wsegs, (struct monst *, BOOLEAN_P)); E void FDECL(save_worm, (int, int)); E void FDECL(rest_worm, (int)); E void FDECL(place_wsegs, (struct monst *)); +E void FDECL(sanity_check_worm, (struct monst *)); E void FDECL(remove_worm, (struct monst *)); E void FDECL(place_worm_tail_randomly, (struct monst *, XCHAR_P, XCHAR_P)); E int FDECL(size_wseg, (struct monst *)); diff --git a/include/flag.h b/include/flag.h index f5072081a..668f9cce7 100644 --- a/include/flag.h +++ b/include/flag.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 flag.h $NHDT-Date: 1461102045 2016/04/19 21:40:45 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.103 $ */ +/* NetHack 3.6 flag.h $NHDT-Date: 1498078871 2017/06/21 21:01:11 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.119 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -211,33 +211,39 @@ struct instance_flags { int *opt_booldup; /* for duplication of boolean opts in config file */ int *opt_compdup; /* for duplication of compound opts in conf file */ #ifdef ALTMETA - boolean altmeta; /* Alt-c sends ESC c rather than M-c */ + boolean altmeta; /* Alt-c sends ESC c rather than M-c */ #endif + boolean autodescribe; /* autodescribe mode in getpos() */ boolean cbreak; /* in cbreak mode, rogue format */ boolean deferred_X; /* deferred entry into explore mode */ - boolean num_pad; /* use numbers for movement commands */ - boolean news; /* print news */ + boolean echo; /* 1 to echo characters */ + /* FIXME: goldX belongs in flags, but putting it in iflags avoids + breaking 3.6.[01] save files */ + boolean goldX; /* for BUCX filtering, whether gold is X or U */ + boolean hilite_pile; /* mark piles of objects with a hilite */ boolean implicit_uncursed; /* maybe omit "uncursed" status in inventory */ boolean mention_walls; /* give feedback when bumping walls */ - boolean menu_tab_sep; /* Use tabs to separate option menu fields */ boolean menu_head_objsym; /* Show obj symbol in menu headings */ boolean menu_overlay; /* Draw menus over the map */ boolean menu_requested; /* Flag for overloaded use of 'm' prefix * on some non-move commands */ + boolean menu_tab_sep; /* Use tabs to separate option menu fields */ + boolean news; /* print news */ + boolean num_pad; /* use numbers for movement commands */ boolean renameallowed; /* can change hero name during role selection */ boolean renameinprogress; /* we are changing hero name */ + boolean status_updates; /* allow updates to bottom status lines; + * disable to avoid excessive noise when using + * a screen reader (use ^X to review status) */ boolean toptenwin; /* ending list in window instead of stdout */ - boolean zerocomp; /* write zero-compressed save files */ - boolean rlecomp; /* run-length comp of levels when writing savefile */ - uchar num_pad_mode; - boolean echo; /* 1 to echo characters */ - boolean use_menu_color; /* use color in menus; only if wc_color */ - boolean use_status_hilites; /* use color in status line */ - boolean status_updates; /* allow updates to bottom status lines */ boolean use_background_glyph; /* use background glyph when appropriate */ - boolean hilite_pile; /* mark piles of objects with a hilite */ - boolean autodescribe; /* autodescribe mode in getpos() */ -#if 0 + boolean use_menu_color; /* use color in menus; only if wc_color */ + boolean use_status_hilites; /* use color in status line */ + boolean zerocomp; /* write zero-compressed save files */ + boolean rlecomp; /* alternative to zerocomp; run-length encoding + * compression of levels when writing savefile */ + uchar num_pad_mode; +#if 0 /* XXXgraphics superseded by symbol sets */ boolean DECgraphics; /* use DEC VT-xxx extended character set */ boolean IBMgraphics; /* use IBM extended character set */ #ifdef MAC_GRAPHICS_ENV diff --git a/include/hack.h b/include/hack.h index b7cf512c7..dffb26c5c 100644 --- a/include/hack.h +++ b/include/hack.h @@ -280,6 +280,7 @@ enum hmon_atkmode_types { #define BUC_UNCURSED 0x200 #define BUC_UNKNOWN 0x400 #define BUC_ALLBKNOWN (BUC_BLESSED | BUC_CURSED | BUC_UNCURSED) +#define BUCX_TYPES (BUC_ALLBKNOWN | BUC_UNKNOWN) #define ALL_TYPES_SELECTED -2 /* Flags to control find_mid() */ diff --git a/include/prop.h b/include/prop.h index e4d3ec7aa..cb4cd3fde 100644 --- a/include/prop.h +++ b/include/prop.h @@ -5,7 +5,11 @@ #ifndef PROP_H #define PROP_H -/*** What the properties are ***/ +/*** What the properties are *** + * + * note: propertynames[] array in timeout.c must be kept in synch with these. + * Property #0 is not used. + */ /* Resistances to troubles */ #define FIRE_RES 1 #define COLD_RES 2 diff --git a/src/cmd.c b/src/cmd.c index fc29aaf6b..2ec304a8a 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -621,13 +621,29 @@ wiz_identify(VOID_ARGS) STATIC_PTR int wiz_makemap(VOID_ARGS) { + /* FIXME: doesn't handle riding */ if (wizard) { + struct monst *mtmp; + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (mtmp->isshk) + setpaid(mtmp); + if (Punished) { + ballrelease(FALSE); + unplacebc(); + } + check_special_room(TRUE); + dmonsfree(); savelev(-1, ledger_no(&u.uz), FREE_SAVE); mklev(); - reglyph_darkroom(); vision_reset(); vision_full_recalc = 1; + cls(); (void) safe_teleds(TRUE); + if (Punished) { + unplacebc(); + placebc(); + } docrt(); flush_screen(1); } @@ -1141,41 +1157,94 @@ STATIC_PTR int wiz_intrinsic(VOID_ARGS) { if (wizard) { + extern const char *const propertynames[]; /* timeout.c */ + static const char wizintrinsic[] = "#wizintrinsic"; + static const char fmt[] = "You are%s %s."; winid win; anything any; - int i, n, accelerator; + char buf[BUFSZ]; + int i, n, p, amt, typ; + long oldtimeout, newtimeout; + const char *propname; menu_item *pick_list = (menu_item *) 0; - static const char *const intrinsics[] = { - "deafness", - }; - win = create_nhwindow(NHW_MENU); start_menu(win); - accelerator = 0; - for (i = 0; i < SIZE(intrinsics); ++i) { - accelerator = intrinsics[i][0]; - any.a_int = i + 1; - add_menu(win, NO_GLYPH, &any, accelerator, 0, - ATR_NONE, intrinsics[i], FALSE); + for (i = 1; (propname = propertynames[i]) != 0; ++i) { + if (i == HALLUC_RES) { + /* Grayswandir vs hallucination; ought to be redone to + use u.uprops[HALLUC].blocked instead of being treated + as a separate property; letting in be manually toggled + even only in wizard mode would be asking for trouble... */ + continue; + } + any.a_int = i; + add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, propname, FALSE); } - end_menu(win, "Which intrinsic?"); - n = select_menu(win, PICK_ONE, &pick_list); + end_menu(win, "Which intrinsics?"); + n = select_menu(win, PICK_ANY, &pick_list); destroy_nhwindow(win); - if (n >= 1) { - i = pick_list[0].item.a_int-1; - free((genericptr_t) pick_list); - } else { - return 0; - } + amt = 30; /* TODO: prompt for duration */ + for (i = 0; i < n; ++i) { + p = pick_list[i].item.a_int; + oldtimeout = u.uprops[p].intrinsic & TIMEOUT; + newtimeout = oldtimeout + (long) amt; + switch (p) { + case SICK: + case SLIMED: + case STONED: + if (oldtimeout > 0L && newtimeout > oldtimeout) + newtimeout = oldtimeout; + break; + } - if (!strcmp(intrinsics[i], "deafness")) { - You("go deaf."); - incr_itimeout(&HDeaf, 30); - context.botl = TRUE; + switch (p) { + case BLINDED: + make_blinded(newtimeout, TRUE); + break; + case CONFUSION: + make_confused(newtimeout, TRUE); + break; + case DEAF: + make_deaf(newtimeout, TRUE); + break; + case HALLUC: + make_hallucinated(newtimeout, TRUE, 0L); + break; + case SICK: + typ = !rn2(2) ? SICK_VOMITABLE : SICK_NONVOMITABLE; + make_sick(newtimeout, wizintrinsic, TRUE, typ); + break; + case SLIMED: + Sprintf(buf, fmt, + !Slimed ? "" : " still", "turning into slime"); + make_slimed(newtimeout, buf); + break; + case STONED: + Sprintf(buf, fmt, + !Stoned ? "" : " still", "turning into stone"); + make_stoned(newtimeout, buf, KILLED_BY, wizintrinsic); + break; + case STUNNED: + make_stunned(newtimeout, TRUE); + break; + case VOMITING: + Sprintf(buf, fmt, !Vomiting ? "" : " still", "vomiting"); + make_vomiting(newtimeout, FALSE); + pline1(buf); + break; + default: + pline("Timeout for %s %s %d.", propertynames[p], + oldtimeout ? "increased by" : "set to", amt); + incr_itimeout(&u.uprops[p].intrinsic, amt); + break; + } + context.botl = 1; /* probably not necessary... */ } + if (n >= 1) + free((genericptr_t) pick_list); } else pline("Unavailable command '%s'.", visctrl((int) cmd_from_func(wiz_intrinsic))); @@ -2814,7 +2883,7 @@ struct ext_func_tab extcmdlist[] = { { '\\', "known", "show what object types have been discovered", dodiscovered, IFBURIED | GENERALCMD }, { '`', "knownclass", "show discovered types for one class of objects", - doclassdisco, IFBURIED|GENERALCMD }, + doclassdisco, IFBURIED | GENERALCMD }, { '\0', "levelchange", "change experience level", wiz_level_change, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, { '\0', "lightsources", "show mobile light sources", @@ -2835,11 +2904,11 @@ struct ext_func_tab extcmdlist[] = { dosacrifice, AUTOCOMPLETE }, { 'o', "open", "open a door", doopen }, { 'O', "options", "show option settings, possibly change them", - doset, IFBURIED|GENERALCMD }, + doset, IFBURIED | GENERALCMD }, { C('o'), "overview", "show a summary of the explored dungeon", - dooverview, IFBURIED|AUTOCOMPLETE }, + dooverview, IFBURIED | AUTOCOMPLETE }, { '\0', "panic", "test panic routine (fatal to game)", - wiz_panic, IFBURIED|AUTOCOMPLETE|WIZMODECMD }, + 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", @@ -2851,7 +2920,7 @@ struct ext_func_tab extcmdlist[] = { { M('p'), "pray", "pray to the gods for help", dopray, IFBURIED | AUTOCOMPLETE }, { C('p'), "prevmsg", "view recent game messages", - doprev_message, IFBURIED|GENERALCMD }, + 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", @@ -2900,7 +2969,7 @@ struct ext_func_tab extcmdlist[] = { { '\0', "terrain", "show map without obstructions", doterrain, IFBURIED | AUTOCOMPLETE }, { 't', "throw", "throw something", dothrow }, - { '\0', "timeout", "look at timeout queue", + { '\0', "timeout", "look at timeout queue and hero's timed intrinsics", wiz_timeout_queue, IFBURIED | AUTOCOMPLETE | WIZMODECMD }, { M('T'), "tip", "empty a container", dotip, AUTOCOMPLETE }, { '_', "travel", "travel to a specific location on the map", dotravel }, diff --git a/src/do_wear.c b/src/do_wear.c index 71599fd7a..2adcb6816 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -2541,8 +2541,9 @@ int retry; } else if (flags.menu_style == MENU_FULL) { all_worn_categories = FALSE; n = query_category("What type of things do you want to take off?", - invent, WORN_TYPES | ALL_TYPES, &pick_list, - PICK_ANY); + invent, (WORN_TYPES | ALL_TYPES + | UNPAID_TYPES | BUCX_TYPES), + &pick_list, PICK_ANY); if (!n) return 0; for (i = 0; i < n; i++) { @@ -2553,10 +2554,17 @@ int retry; } free((genericptr_t) pick_list); } else if (flags.menu_style == MENU_COMBINATION) { - all_worn_categories = FALSE; - if (ggetobj("take off", select_off, 0, TRUE, (unsigned *) 0) == -2) - all_worn_categories = TRUE; + unsigned ggofeedback = 0; + + i = ggetobj("take off", select_off, 0, TRUE, &ggofeedback); + if (ggofeedback & ALL_FINISHED) + return 0; + all_worn_categories = (i == -2); } + if (menu_class_present('u') + || menu_class_present('B') || menu_class_present('U') + || menu_class_present('C') || menu_class_present('X')) + all_worn_categories = FALSE; n = query_objlist("What do you want to take off?", &invent, (SIGNAL_NOMENU | USE_INVLET | INVORDER_SORT), diff --git a/src/invent.c b/src/invent.c index d79bf1ef5..cb1cfb2d0 100644 --- a/src/invent.c +++ b/src/invent.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 invent.c $NHDT-Date: 1472809075 2016/09/02 09:37:55 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.210 $ */ +/* NetHack 3.6 invent.c $NHDT-Date: 1498078873 2017/06/21 21:01:13 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.214 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -25,8 +25,6 @@ STATIC_PTR char *FDECL(safeq_shortxprname, (struct obj *)); STATIC_DCL char FDECL(display_pickinv, (const char *, const char *, BOOLEAN_P, long *)); STATIC_DCL char FDECL(display_used_invlets, (CHAR_P)); -STATIC_DCL void FDECL(tally_BUCX, (struct obj *, - int *, int *, int *, int *, int *)); STATIC_DCL boolean FDECL(this_type_only, (struct obj *)); STATIC_DCL void NDECL(dounpaid); STATIC_DCL struct obj *FDECL(find_unpaid, (struct obj *, struct obj **)); @@ -1560,7 +1558,7 @@ unsigned *resultflags; boolean takeoff, ident, allflag, m_seen; int itemcount; int oletct, iletct, unpaid, oc_of_sym; - char sym, *ip, olets[MAXOCLASSES + 5], ilets[MAXOCLASSES + 5]; + char sym, *ip, olets[MAXOCLASSES + 5], ilets[MAXOCLASSES + 10]; char extra_removeables[3 + 1]; /* uwep,uswapwep,uquiver */ char buf[BUFSZ], qbuf[QBUFSZ]; @@ -1587,22 +1585,19 @@ unsigned *resultflags; if (ident && !iletct) { return -1; /* no further identifications */ - } else if (!takeoff && (unpaid || invent)) { + } else if (invent) { ilets[iletct++] = ' '; if (unpaid) ilets[iletct++] = 'u'; - if (count_buc(invent, BUC_BLESSED)) + if (count_buc(invent, BUC_BLESSED, ofilter)) ilets[iletct++] = 'B'; - if (count_buc(invent, BUC_UNCURSED)) + if (count_buc(invent, BUC_UNCURSED, ofilter)) ilets[iletct++] = 'U'; - if (count_buc(invent, BUC_CURSED)) + if (count_buc(invent, BUC_CURSED, ofilter)) ilets[iletct++] = 'C'; - if (count_buc(invent, BUC_UNKNOWN)) + if (count_buc(invent, BUC_UNKNOWN, ofilter)) ilets[iletct++] = 'X'; - if (invent) - ilets[iletct++] = 'a'; - } else if (takeoff && invent) { - ilets[iletct++] = ' '; + ilets[iletct++] = 'a'; } ilets[iletct++] = 'i'; if (!combo) @@ -1680,21 +1675,12 @@ unsigned *resultflags; } else if (sym == 'a') { allflag = TRUE; } else if (sym == 'A') { - /* same as the default */; + ; /* same as the default */ } else if (sym == 'u') { add_valid_menu_class('u'); ckfn = ckunpaid; - } else if (sym == 'B') { - add_valid_menu_class('B'); - ckfn = ckvalidcat; - } else if (sym == 'U') { - add_valid_menu_class('U'); - ckfn = ckvalidcat; - } else if (sym == 'C') { - add_valid_menu_class('C'); - ckfn = ckvalidcat; - } else if (sym == 'X') { - add_valid_menu_class('X'); + } else if (index("BUCX", sym)) { + add_valid_menu_class(sym); /* 'B','U','C',or 'X' */ ckfn = ckvalidcat; } else if (sym == 'm') { m_seen = TRUE; @@ -1751,7 +1737,7 @@ int FDECL((*fn), (OBJ_P)), FDECL((*ckfn), (OBJ_P)); struct obj *otmp, *otmpo; register char sym, ilet; register int cnt = 0, dud = 0, tmp; - boolean takeoff, nodot, ident, take_out, put_in, first, ininv; + boolean takeoff, nodot, ident, take_out, put_in, first, ininv, bycat; char qbuf[QBUFSZ], qpfx[QBUFSZ]; takeoff = taking_off(word); @@ -1761,6 +1747,8 @@ int FDECL((*fn), (OBJ_P)), FDECL((*ckfn), (OBJ_P)); nodot = (!strcmp(word, "nodot") || !strcmp(word, "drop") || ident || takeoff || take_out || put_in); ininv = (*objchn == invent); + bycat = (menu_class_present('B') || menu_class_present('U') + || menu_class_present('C') || menu_class_present('X')); /* someday maybe we'll sort by 'olets' too (temporarily replace flags.packorder and pass SORTLOOT_PACK), but not yet... */ @@ -1792,6 +1780,8 @@ nextclass: while ((otmp = nxt_unbypassed_obj(*objchn)) != 0) { if (ilet == 'z') ilet = 'A'; + else if (ilet == 'Z') + ilet = NOINVSYM; /* '#' */ else ilet++; if (olets && *olets && otmp->oclass != *olets) @@ -1802,6 +1792,8 @@ nextclass: continue; if (ckfn && !(*ckfn)(otmp)) continue; + if (bycat && !ckvalidcat(otmp)) + continue; if (!allflag) { safeq_xprn_ctx.let = ilet; safeq_xprn_ctx.dot = !nodot; @@ -2405,20 +2397,27 @@ struct obj *list; * at some point: bknown is forced for priest[ess], like in xname(). */ int -count_buc(list, type) +count_buc(list, type, filterfunc) struct obj *list; int type; +boolean FDECL((*filterfunc), (OBJ_P)); { int count = 0; for (; list; list = list->nobj) { - /* coins are "none of the above" as far as BUCX filtering goes */ - if (list->oclass == COIN_CLASS) - continue; /* priests always know bless/curse state */ if (Role_if(PM_PRIEST)) - list->bknown = 1; + list->bknown = (list->oclass != COIN_CLASS); + /* some actions exclude some or most items */ + if (filterfunc && !(*filterfunc)(list)) + continue; + /* coins are either uncursed or unknown based upon option setting */ + if (list->oclass == COIN_CLASS) { + if (type == (iflags.goldX ? BUC_UNKNOWN : BUC_UNCURSED)) + ++count; + continue; + } /* check whether this object matches the requested type */ if (!list->bknown ? (type == BUC_UNKNOWN) @@ -2432,21 +2431,32 @@ int type; /* similar to count_buc(), but tallies all states at once rather than looking for a specific type */ -STATIC_OVL void -tally_BUCX(list, bcp, ucp, ccp, xcp, ocp) +void +tally_BUCX(list, by_nexthere, bcp, ucp, ccp, xcp, ocp) struct obj *list; +boolean by_nexthere; int *bcp, *ucp, *ccp, *xcp, *ocp; { + /* Future extensions: + * Skip current_container when list is invent, uchain when + * first object of list is located on the floor. 'ocp' will then + * have a function again (it was a counter for having skipped gold, + * but that's not skipped anymore). + */ *bcp = *ucp = *ccp = *xcp = *ocp = 0; - for (; list; list = list->nobj) { - if (list->oclass == COIN_CLASS) { - ++(*ocp); /* "other" */ - continue; - } + for ( ; list; list = (by_nexthere ? list->nexthere : list->nobj)) { /* priests always know bless/curse state */ if (Role_if(PM_PRIEST)) - list->bknown = 1; - + list->bknown = (list->oclass != COIN_CLASS); + /* coins are either uncursed or unknown based upon option setting */ + if (list->oclass == COIN_CLASS) { + if (iflags.goldX) + ++(*xcp); + else + ++(*ucp); + continue; + } + /* ordinary items */ if (!list->bknown) ++(*xcp); else if (list->blessed) @@ -2583,7 +2593,12 @@ struct obj *obj; { boolean res = (obj->oclass == this_type); - if (obj->oclass != COIN_CLASS) { + if (obj->oclass == COIN_CLASS) { + /* if filtering by bless/curse state, gold is classified as + either unknown or uncursed based on user option setting */ + if (this_type && index("BUCX", this_type)) + res = (this_type == (iflags.goldX ? 'X' : 'U')); + } else { switch (this_type) { case 'B': res = (obj->bknown && obj->blessed); @@ -2623,7 +2638,7 @@ dotypeinv() return 0; } unpaid_count = count_unpaid(invent); - tally_BUCX(invent, &bcnt, &ucnt, &ccnt, &xcnt, &ocnt); + tally_BUCX(invent, FALSE, &bcnt, &ucnt, &ccnt, &xcnt, &ocnt); if (flags.menu_style != MENU_TRADITIONAL) { if (flags.menu_style == MENU_FULL @@ -2651,9 +2666,9 @@ dotypeinv() /* collect a list of classes of objects carried, for use as a prompt */ types[0] = 0; - class_count = - collect_obj_classes(types, invent, FALSE, - (boolean FDECL((*), (OBJ_P))) 0, &itemcount); + class_count = collect_obj_classes(types, invent, FALSE, + (boolean FDECL((*), (OBJ_P))) 0, + &itemcount); if (unpaid_count || billx || (bcnt + ccnt + ucnt + xcnt) != 0) types[class_count++] = ' '; if (unpaid_count) @@ -2733,28 +2748,32 @@ dotypeinv() return doprgold(); if (index(types, c) > index(types, '\033')) { /* '> ESC' => hidden choice, something known not to be carried */ - const char *which = 0; + const char *before = "", *after = ""; switch (c) { case 'B': - which = "known to be blessed"; + before = "known to be blessed "; break; case 'U': - which = "known to be uncursed"; + before = "known to be uncursed "; break; case 'C': - which = "known to be cursed"; + before = "known to be cursed "; break; case 'X': - You( - "have no objects whose blessed/uncursed/cursed status is unknown."); + after = " whose blessed/uncursed/cursed status is unknown"; break; /* better phrasing is desirable */ default: - which = "such"; + /* 'c' is an object class, because we've already handled + all the non-class letters which were put into 'types[]'; + could/should move object class names[] array from below + to somewhere above so that we can access it here (via + lcase(strcpy(classnamebuf, names[(int) c]))), but the + game-play value of doing so is low... */ + before = "such "; break; } - if (which) - You("have no %s objects.", which); + You("have no %sobjects%s.", before, after); return 0; } this_type = oclass; @@ -3368,13 +3387,11 @@ STATIC_VAR NEARDATA const char *names[] = { "Comestibles", "Potions", "Scrolls", "Spellbooks", "Wands", "Coins", "Gems/Stones", "Boulders/Statues", "Iron balls", "Chains", "Venoms" }; +STATIC_VAR NEARDATA const char oth_symbols[] = { CONTAINED_SYM, '\0' }; +STATIC_VAR NEARDATA const char *oth_names[] = { "Bagged/Boxed items" }; -static NEARDATA const char oth_symbols[] = { CONTAINED_SYM, '\0' }; - -static NEARDATA const char *oth_names[] = { "Bagged/Boxed items" }; - -static NEARDATA char *invbuf = (char *) 0; -static NEARDATA unsigned invbufsiz = 0; +STATIC_VAR NEARDATA char *invbuf = (char *) 0; +STATIC_VAR NEARDATA unsigned invbufsiz = 0; char * let_to_name(let, unpaid, showsym) diff --git a/src/mon.c b/src/mon.c index 33a84372e..69d64490b 100644 --- a/src/mon.c +++ b/src/mon.c @@ -93,6 +93,8 @@ mon_sanity_check() else if (level.monsters[x][y] != mtmp) impossible("mon (%s) at <%d,%d> is not there!", fmt_ptr((genericptr_t) mtmp), x, y); + else if (mtmp->wormno) + sanity_check_worm(mtmp); } for (x = 0; x < COLNO; x++) diff --git a/src/options.c b/src/options.c index d49102ddc..b7ee5f0ba 100644 --- a/src/options.c +++ b/src/options.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 options.c $NHDT-Date: 1470357737 2016/08/05 00:42:17 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.279 $ */ +/* NetHack 3.6 options.c $NHDT-Date: 1498078876 2017/06/21 21:01:16 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.288 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -126,6 +126,7 @@ static struct Bool_Opt { { "flush", (boolean *) 0, FALSE, SET_IN_FILE }, #endif { "fullscreen", &iflags.wc2_fullscreen, FALSE, SET_IN_FILE }, + { "goldX", &iflags.goldX, FALSE, SET_IN_GAME }, { "help", &flags.help, TRUE, SET_IN_GAME }, { "hilite_pet", &iflags.wc_hilite_pet, FALSE, SET_IN_GAME }, /*WC*/ { "hilite_pile", &iflags.hilite_pile, FALSE, SET_IN_GAME }, @@ -265,7 +266,7 @@ static struct Comp_Opt { { "altkeyhandler", "alternate key handler", 20, DISP_IN_GAME }, #ifdef BACKWARD_COMPAT { "boulder", "deprecated (use S_boulder in sym file instead)", 1, - SET_IN_FILE }, + SET_IN_GAME }, #endif { "catname", "the name of your (first) cat (e.g., catname:Tabby)", PL_PSIZ, DISP_IN_GAME }, @@ -2441,8 +2442,12 @@ boolean tinitial, tfrom_file; */ iflags.bouldersym = (uchar) opts[0]; } - if (!initial) + /* for 'initial', update_bouldersym() is done in initoptions_finish(), + after all symset options have been processed */ + if (!initial) { + update_bouldersym(); need_redraw = TRUE; + } return; } #endif @@ -2585,7 +2590,7 @@ boolean tinitial, tfrom_file; op = pp + 1; else break; /* no next token */ - } /* for(;;) */ + } /* for(;;) */ } return; } @@ -3497,7 +3502,8 @@ char* bindings; } /* parse a single binding: first split around : */ - if (! (bind = index(bindings, ':'))) return; /* it's not a binding */ + if (! (bind = index(bindings, ':'))) + return; /* it's not a binding */ *bind++ = 0; /* read the key to be bound */ @@ -4691,7 +4697,8 @@ boolean setinitial, setfromfile; } sl = sl->next; } - Sprintf(buf, "Select %ssymbol set:", rogueflag ? "rogue level " : ""); + Sprintf(buf, "Select %ssymbol set:", + rogueflag ? "rogue level " : ""); end_menu(tmpwin, buf); if (select_menu(tmpwin, PICK_ONE, &symset_pick) > 0) { chosen = symset_pick->item.a_int - 2; @@ -5335,7 +5342,8 @@ register char *opts; if ((op = index(opts, ',')) != 0) { *op++ = 0; - if (!parsesymbols(op)) return FALSE; + if (!parsesymbols(op)) + return FALSE; } /* S_sample:string */ diff --git a/src/pickup.c b/src/pickup.c index 4b42be708..2395c4110 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 pickup.c $NHDT-Date: 1470449858 2016/08/06 02:17:38 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.184 $ */ +/* NetHack 3.6 pickup.c $NHDT-Date: 1498078877 2017/06/21 21:01:17 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.185 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -149,45 +149,54 @@ struct obj *objs; boolean here; int *menu_on_demand; { - char ilets[30], inbuf[BUFSZ]; /* FIXME: hardcoded ilets[] length */ + char ilets[36], inbuf[BUFSZ]; /* FIXME: hardcoded ilets[] length */ int iletct, oclassct; - boolean not_everything; + boolean not_everything, filtered; char qbuf[QBUFSZ]; boolean m_seen; - int itemcount; + int itemcount, bcnt, ucnt, ccnt, xcnt, ocnt; oclasses[oclassct = 0] = '\0'; *one_at_a_time = *everything = m_seen = FALSE; + if (menu_on_demand) + *menu_on_demand = 0; iletct = collect_obj_classes(ilets, objs, here, (boolean FDECL((*), (OBJ_P))) 0, &itemcount); - if (iletct == 0) { + if (iletct == 0) return FALSE; - } else if (iletct == 1) { + + if (iletct == 1) { oclasses[0] = def_char_to_objclass(ilets[0]); oclasses[1] = '\0'; - if (itemcount && menu_on_demand) { - ilets[iletct++] = 'm'; - *menu_on_demand = 0; - ilets[iletct] = '\0'; - } } else { /* more than one choice available */ - const char *where = 0; - char sym, oc_of_sym, *p; - /* additional choices */ ilets[iletct++] = ' '; ilets[iletct++] = 'a'; ilets[iletct++] = 'A'; ilets[iletct++] = (objs == invent ? 'i' : ':'); - if (menu_on_demand) { - ilets[iletct++] = 'm'; - *menu_on_demand = 0; - } - ilets[iletct] = '\0'; + } + if (itemcount && menu_on_demand) + ilets[iletct++] = 'm'; + + tally_BUCX(objs, here, &bcnt, &ucnt, &ccnt, &xcnt, &ocnt); + if (bcnt) + ilets[iletct++] = 'B'; + if (ucnt) + ilets[iletct++] = 'U'; + if (ccnt) + ilets[iletct++] = 'C'; + if (xcnt) + ilets[iletct++] = 'X'; + ilets[iletct] = '\0'; + + if (iletct > 1) { + const char *where = 0; + char sym, oc_of_sym, *p; + ask_again: oclasses[oclassct = 0] = '\0'; *one_at_a_time = *everything = FALSE; - not_everything = FALSE; + not_everything = filtered = FALSE; Sprintf(qbuf, "What kinds of thing do you want to %s? [%s]", action, ilets); getlin(qbuf, inbuf); @@ -213,6 +222,9 @@ int *menu_on_demand; goto ask_again; } else if (sym == 'm') { m_seen = TRUE; + } else if (index("BUCX", sym)) { + add_valid_menu_class(sym); /* 'B','U','C',or 'X' */ + filtered = TRUE; } else { oc_of_sym = def_char_to_objclass(sym); if (index(ilets, sym)) { @@ -230,9 +242,11 @@ int *menu_on_demand; not_everything = TRUE; } } - } + } /* for p:sym in inbuf */ + if (m_seen && menu_on_demand) { - *menu_on_demand = (*everything || !oclassct) ? -2 : -3; + *menu_on_demand = (((*everything || !oclassct) && !filtered) + ? -2 : -3); return FALSE; } if (!oclassct && (!*everything || not_everything)) { @@ -325,6 +339,14 @@ struct obj *obj; static char valid_menu_classes[MAXOCLASSES + 1 + 4 + 1]; static boolean class_filter, bucx_filter, shop_filter; +/* check valid_menu_classes[] for an entry; also used by askchain() */ +boolean +menu_class_present(c) +int c; +{ + return (c && index(valid_menu_classes, c)) ? TRUE : FALSE; +} + void add_valid_menu_class(c) int c; @@ -334,7 +356,7 @@ int c; if (c == 0) { /* reset */ vmc_count = 0; class_filter = bucx_filter = shop_filter = FALSE; - } else { + } else if (!menu_class_present(c)) { valid_menu_classes[vmc_count++] = (char) c; /* categorize the new class */ switch (c) { @@ -376,9 +398,21 @@ boolean allow_category(obj) struct obj *obj; { - /* unpaid and BUC checks don't apply to coins */ + /* For coins, if any class filter is specified, accept if coins + * are included regardless of whether either unpaid or BUC-status + * is also specified since player has explicitly requested coins. + * If no class filtering is specified but bless/curse state is, + * coins are either unknown or uncursed based on an option setting. + */ if (obj->oclass == COIN_CLASS) - return index(valid_menu_classes, COIN_CLASS) ? TRUE : FALSE; + return class_filter + ? (index(valid_menu_classes, COIN_CLASS) ? TRUE : FALSE) + : shop_filter /* coins are never unpaid, but check anyway */ + ? (obj->unpaid ? TRUE : FALSE) + : bucx_filter + ? (index(valid_menu_classes, iflags.goldX ? 'X' : 'U') + ? TRUE : FALSE) + : TRUE; /* catchall: no filters specified, so accept */ if (Role_if(PM_PRIEST)) obj->bknown = TRUE; @@ -429,7 +463,7 @@ allow_cat_no_uchain(obj) struct obj *obj; { if (obj != uchain - && ((index(valid_menu_classes,'u') && obj->unpaid) + && ((index(valid_menu_classes, 'u') && obj->unpaid) || index(valid_menu_classes, obj->oclass))) return TRUE; return FALSE; @@ -441,8 +475,7 @@ boolean is_worn_by_type(otmp) register struct obj *otmp; { - return (boolean) (!!(otmp->owornmask & (W_ARMOR | W_ACCESSORY | W_WEAPON)) - && index(valid_menu_classes, otmp->oclass) != 0); + return (is_worn(otmp) && allow_category(otmp)) ? TRUE : FALSE; } /* @@ -574,8 +607,8 @@ int what; /* should be a long */ /* old style interface */ int ct = 0; long lcount; - boolean all_of_a_type, selective; - char oclasses[MAXOCLASSES]; + boolean all_of_a_type, selective, bycat; + char oclasses[MAXOCLASSES + 10]; /* +10: room for B,U,C,X plus slop */ struct obj *obj, *obj2; oclasses[0] = '\0'; /* types to consider (empty for all) */ @@ -604,7 +637,7 @@ int what; /* should be a long */ (traverse_how & BY_NEXTHERE) ? TRUE : FALSE, &via_menu)) { if (!via_menu) - return 0; + goto pickupdone; if (selective) traverse_how |= INVORDER_SORT; n = query_objlist("Pick up what?", objchain_p, traverse_how, @@ -614,10 +647,14 @@ int what; /* should be a long */ goto menu_pickup; } } + bycat = (menu_class_present('B') || menu_class_present('U') + || menu_class_present('C') || menu_class_present('X')); for (obj = *objchain_p; obj; obj = obj2) { obj2 = FOLLOW(obj, traverse_how); - if (!selective && oclasses[0] && !index(oclasses, obj->oclass)) + if (bycat ? !allow_category(obj) + : (!selective && oclasses[0] + && !index(oclasses, obj->oclass))) continue; lcount = -1L; @@ -674,6 +711,8 @@ int what; /* should be a long */ if (autopickup) check_here(n_picked > 0); } + pickupdone: + add_valid_menu_class(0); /* reset */ return (n_tried > 0); } @@ -969,6 +1008,7 @@ int how; /* type of query */ boolean collected_type_name; char invlet; int ccount; + boolean FDECL((*ofilter), (OBJ_P)) = (boolean FDECL((*), (OBJ_P))) 0; boolean do_unpaid = FALSE; boolean do_blessed = FALSE, do_cursed = FALSE, do_uncursed = FALSE, do_buc_unknown = FALSE; @@ -979,19 +1019,21 @@ int how; /* type of query */ return 0; if ((qflags & UNPAID_TYPES) && count_unpaid(olist)) do_unpaid = TRUE; - if ((qflags & BUC_BLESSED) && count_buc(olist, BUC_BLESSED)) { + if (qflags & WORN_TYPES) + ofilter = is_worn; + if ((qflags & BUC_BLESSED) && count_buc(olist, BUC_BLESSED, ofilter)) { do_blessed = TRUE; num_buc_types++; } - if ((qflags & BUC_CURSED) && count_buc(olist, BUC_CURSED)) { + if ((qflags & BUC_CURSED) && count_buc(olist, BUC_CURSED, ofilter)) { do_cursed = TRUE; num_buc_types++; } - if ((qflags & BUC_UNCURSED) && count_buc(olist, BUC_UNCURSED)) { + if ((qflags & BUC_UNCURSED) && count_buc(olist, BUC_UNCURSED, ofilter)) { do_uncursed = TRUE; num_buc_types++; } - if ((qflags & BUC_UNKNOWN) && count_buc(olist, BUC_UNKNOWN)) { + if ((qflags & BUC_UNKNOWN) && count_buc(olist, BUC_UNKNOWN, ofilter)) { do_buc_unknown = TRUE; num_buc_types++; } @@ -1001,8 +1043,7 @@ int how; /* type of query */ if (ccount == 1 && !do_unpaid && num_buc_types <= 1 && !(qflags & BILLED_TYPES)) { for (curr = olist; curr; curr = FOLLOW(curr, qflags)) { - if ((qflags & WORN_TYPES) - && !(curr->owornmask & (W_ARMOR | W_ACCESSORY | W_WEAPON))) + if (ofilter && !(*ofilter)(curr)) continue; break; } @@ -1033,8 +1074,7 @@ int how; /* type of query */ collected_type_name = FALSE; for (curr = olist; curr; curr = FOLLOW(curr, qflags)) { if (curr->oclass == *pack) { - if ((qflags & WORN_TYPES) - && !(curr->owornmask & (W_ARMOR | W_ACCESSORY | W_WEAPON))) + if (ofilter && !(*ofilter)(curr)) continue; if (!collected_type_name) { any = zeroany; @@ -1081,7 +1121,9 @@ int how; /* type of query */ : "Auto-select every item", MENU_UNSELECTED); } - /* items with b/u/c/unknown if there are any */ + /* items with b/u/c/unknown if there are any; + this cluster of menu entries is in alphabetical order, + reversing the usual sequence of 'U' and 'C' in BUCX */ if (do_blessed) { invlet = 'B'; any = zeroany; @@ -1108,7 +1150,7 @@ int how; /* type of query */ any = zeroany; any.a_int = 'X'; add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE, - "Items of unknown B/C/U status", MENU_UNSELECTED); + "Items of unknown Bless/Curse status", MENU_UNSELECTED); } end_menu(win, qstr); n = select_menu(win, how, pick_list); @@ -2116,6 +2158,11 @@ register struct obj *obj; return (current_container ? 1 : -1); } +/* askchain() filter used by in_container(); + * returns True if the container is intact and 'obj' isn't it, False if + * container is gone (magic bag explosion) or 'obj' is the container itself; + * also used by getobj() when picking a single item to stash + */ int ck_bag(obj) struct obj *obj; @@ -2490,6 +2537,7 @@ boolean more_containers; /* True iff #loot multiple and this isn't last one */ used |= traditional_loot(FALSE); else used |= (menu_loot(0, FALSE) > 0); + add_valid_menu_class(0); } } @@ -2510,6 +2558,7 @@ boolean more_containers; /* True iff #loot multiple and this isn't last one */ used |= traditional_loot(TRUE); else used |= (menu_loot(0, TRUE) > 0); + add_valid_menu_class(0); } else if (stash_one) { /* put one item into container */ if ((otmp = getobj(stashable, "stash")) != 0) { @@ -2539,6 +2588,7 @@ boolean more_containers; /* True iff #loot multiple and this isn't last one */ used |= traditional_loot(FALSE); else used |= (menu_loot(0, FALSE) > 0); + add_valid_menu_class(0); } } @@ -2568,7 +2618,7 @@ boolean put_in; { int FDECL((*actionfunc), (OBJ_P)), FDECL((*checkfunc), (OBJ_P)); struct obj **objlist; - char selection[MAXOCLASSES + 1]; + char selection[MAXOCLASSES + 10]; /* +10: room for B,U,C,X plus slop */ const char *action; boolean one_by_one, allflag; int used = 0, menu_on_request = 0; @@ -2616,7 +2666,7 @@ boolean put_in; } else if (flags.menu_style == MENU_FULL) { all_categories = FALSE; Sprintf(buf, "%s what type of objects?", action); - mflags = (ALL_TYPES | BUC_ALLBKNOWN | BUC_UNKNOWN); + mflags = (ALL_TYPES | BUCX_TYPES); if (put_in) mflags |= CHOOSE_ALL; n = query_category(buf, put_in ? invent : current_container->cobj, diff --git a/src/polyself.c b/src/polyself.c index 869a37a07..9b15b32c0 100644 --- a/src/polyself.c +++ b/src/polyself.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 polyself.c $NHDT-Date: 1457572516 2016/03/10 01:15:16 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.108 $ */ +/* NetHack 3.6 polyself.c $NHDT-Date: 1497485548 2017/06/15 00:12:28 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.112 $ */ /* Copyright (C) 1987, 1988, 1989 by Ken Arromdee */ /* NetHack may be freely redistributed. See license for details. */ @@ -366,9 +366,11 @@ newman() newuhs(FALSE); polyman("feel like a new %s!", /* use saved gender we're about to revert to, not current */ - (u.mfemale && urace.individual.f) + ((Upolyd ? u.mfemale : flags.female) && urace.individual.f) ? urace.individual.f - : (urace.individual.m) ? urace.individual.m : urace.noun); + : (urace.individual.m) + ? urace.individual.m + : urace.noun); if (Slimed) { Your("body transforms, but there is still slime on you."); make_slimed(10L, (const char *) 0); diff --git a/src/shk.c b/src/shk.c index abd9a33b0..62052fbe7 100644 --- a/src/shk.c +++ b/src/shk.c @@ -29,7 +29,6 @@ STATIC_VAR const char and_its_contents[] = " and its contents"; STATIC_VAR const char the_contents_of[] = "the contents of "; STATIC_DCL void FDECL(append_honorific, (char *)); -STATIC_DCL void FDECL(setpaid, (struct monst *)); STATIC_DCL long FDECL(addupbill, (struct monst *)); STATIC_DCL void FDECL(pacify_shk, (struct monst *)); STATIC_DCL struct bill_x *FDECL(onbill, (struct obj *, struct monst *, @@ -286,7 +285,7 @@ struct obj *list; } /* either you paid or left the shop or the shopkeeper died */ -STATIC_OVL void +void setpaid(shkp) register struct monst *shkp; { diff --git a/src/timeout.c b/src/timeout.c index 508fe9609..66add4ec8 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -15,6 +15,45 @@ STATIC_DCL void FDECL(see_lamp_flicker, (struct obj *, const char *)); STATIC_DCL void FDECL(lantern_message, (struct obj *)); STATIC_DCL void FDECL(cleanup_burn, (ANY_P *, long)); +/* the order of these must match their numerical sequence in prop.h + because the property number is inferred from the array index; + used by wizard mode #timeout and #wizintrinsic */ +const char *const propertynames[] = { + "0: not used", +/* Resistances */ + /* 1..2 */ "fire resistance", "cold resistance", + /* 3..4 */ "sleep resistance", "disintegration resistance", + /* 5..6 */ "shock resistance", "poison resistance", + /* 7..8 */ "acid resistance", "stoning resistance", + /* 9..10 */ "drain resistance", "sickness resistance", + /* 11..12 */ "invulnerable", "magic resistance", +/* Troubles */ + /* 13..16 */ "stunned", "confused", "blinded", "deafness", + /* 17..20 */ "fatally sick", "petrifying", "strangling", "vomiting", + /* 21..22 */ "slippery fingers", "becoming slime", + /* 23..24 */ "hallucinating", "halluicination resistance", + /* 25..28 */ "fumbling", "wounded legs", "sleepy", "voracious hunger", +/* Vision and senses */ + /* 29..30 */ "see invisible", "telepathic", + /* 31..33 */ "warning", "warn:monster", "warn:undead", + /* 34..37 */ "searching", "clairvoyant", "infravision", "monster detection", +/* Appearance and behavior */ + /* 38..41 */ "adorned (+/-Cha)", "invisible", "displaced", "stealthy", + /* 42..43 */ "monster aggrevation", "conflict", +/* Transportation */ + /* 44..46 */ "jumping", "teleporting", "teleport control", + /* 47..49 */ "levitating", "flying", "water walking", + /* 50..52 */ "swimming", "magical breathing", "pass thru walls", +/* Physical attributes */ + /* 53..55 */ "slow digestion", "half spell damage", "half physical damage", + /* 56..58 */ "HP regeneration", "energy regeneration", "extra protection", + /* 59 */ "protection from shape changers", + /* 60..62 */ "polymorphing", "polymorph control", "unchanging", + /* 63..66 */ "fast", "reflecting", "free action", "fixed abilites", + /* 67 */ "life will be saved", + 0 /* sentinel */ +}; + /* He is being petrified - dialogue by inmet!tower */ static NEARDATA const char *const stoned_texts[] = { "You are slowing down.", /* 5 */ @@ -57,6 +96,9 @@ stoned_dialogue() multi_reason = "getting stoned"; nomovemsg = You_can_move_again; /* not unconscious */ break; + case 2: + if ((HDeaf & TIMEOUT) > 0L && (HDeaf & TIMEOUT) < 5L) + set_itimeout(&HDeaf, 5L); /* avoid Hear_again at tail end */ default: break; } @@ -179,8 +221,9 @@ levitation_dialogue() if (((HLevitation & TIMEOUT) % 2L) && i > 0L && i <= SIZE(levi_texts)) { const char *s = levi_texts[SIZE(levi_texts) - i]; if (index(s, '%')) { - boolean danger = is_pool_or_lava(u.ux, u.uy) - && !Is_waterlevel(&u.uz); + boolean danger = (is_pool_or_lava(u.ux, u.uy) + && !Is_waterlevel(&u.uz)); + pline(s, danger ? "over" : "in", danger ? surface(u.ux, u.uy) : "air"); } else @@ -225,6 +268,8 @@ slime_dialogue() if (multi > 0) nomul(0); } + if (i == 2L && (HDeaf & TIMEOUT) > 0L && (HDeaf & TIMEOUT) < 5L) + set_itimeout(&HDeaf, 5L); /* avoid Hear_again at tail end */ exercise(A_DEX, FALSE); } @@ -1473,7 +1518,7 @@ timer_element *base; char buf[BUFSZ]; if (!base) { - putstr(win, 0, ""); + putstr(win, 0, " "); } else { putstr(win, 0, "timeout id kind call"); for (curr = base; curr; curr = curr->next) { @@ -1492,31 +1537,14 @@ timer_element *base; } } -static boolean print_prop_header = TRUE; -void -print_prop(win, text, prop) -winid win; -const char *text; -long prop; -{ - char buf[BUFSZ]; - if (prop & TIMEOUT) { - if (print_prop_header) { - putstr(win, 0, ""); - putstr(win, 0, "Properties:"); - putstr(win, 0, ""); - print_prop_header = FALSE; - } - Sprintf(buf, " %10s: %ld", text, (prop & TIMEOUT)); - putstr(win, 0, buf); - } -} - int wiz_timeout_queue() { winid win; char buf[BUFSZ]; + const char *propname; + long intrinsic; + int i, count, longestlen, ln; win = create_nhwindow(NHW_MENU); /* corner text window */ if (win == WIN_ERR) @@ -1529,13 +1557,38 @@ wiz_timeout_queue() putstr(win, 0, ""); print_queue(win, timer_base); - print_prop_header = TRUE; - print_prop(win, "Levitation", HLevitation); - print_prop(win, "Stoned", Stoned); - print_prop(win, "Vomiting", Vomiting); - print_prop(win, "Strangled", Strangled); - print_prop(win, "Slimed", Slimed); - + /* Timed properies: + * check every one; the majority can't obtain temporary timeouts in + * normal play but those can be forced via the #wizintrinsic command. + */ + count = longestlen = 0; + for (i = 1; (propname = propertynames[i]) != 0; ++i) { /* [0] not used */ + intrinsic = u.uprops[i].intrinsic; + if (intrinsic & TIMEOUT) { + ++count; + if ((ln = (int) strlen(propname)) > longestlen) + longestlen = ln; + } + } + putstr(win, 0, ""); + if (!count) { + putstr(win, 0, "No timed properties."); + } else { + putstr(win, 0, "Timed properties:"); + putstr(win, 0, ""); + for (i = 1; (propname = propertynames[i]) != 0; ++i) { + intrinsic = u.uprops[i].intrinsic; + if (intrinsic & TIMEOUT) { + /* timeout value can be up to 16777215 (0x00ffffff) but + width of 4 digits should result in values lining up + almost all the time (if/when they don't, it won't + look nice but the information will still be accurate) */ + Sprintf(buf, " %*s %4ld", -longestlen, propname, + (intrinsic & TIMEOUT)); + putstr(win, 0, buf); + } + } + } display_nhwindow(win, FALSE); destroy_nhwindow(win); @@ -1551,6 +1604,7 @@ timer_sanity_check() for (curr = timer_base; curr; curr = curr->next) if (curr->kind == TIMER_OBJECT) { struct obj *obj = curr->arg.a_obj; + if (obj->timed == 0) { pline("timer sanity: untimed obj %s, timer %ld", fmt_ptr((genericptr_t) obj), curr->tid); diff --git a/src/worm.c b/src/worm.c index ac69ea72b..4e5d12dd8 100644 --- a/src/worm.c +++ b/src/worm.c @@ -569,6 +569,28 @@ struct monst *worm; } } +void +sanity_check_worm(worm) +struct monst *worm; +{ + struct wseg *curr; + + if (!worm) + panic("no worm!"); + if (!worm->wormno) + panic("not a worm?!"); + + curr = wtails[worm->wormno]; + + while (curr != wheads[worm->wormno]) { + if (!isok(curr->wx, curr->wy)) + panic("worm seg not isok"); + if (level.monsters[curr->wx][curr->wy] != worm) + panic("worm not at seg location"); + curr = curr->nseg; + } +} + /* * remove_worm() *