diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index b76fa5a69..9e1a4c5c1 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -570,9 +570,23 @@ Figure 2 Go in that direction until you hit a wall or run into something. .lp m[yuhjklbn] Prefix: move without picking up objects or fighting (even if you remember -a monster there) +a monster there). +.lp "" +A few non-movement commands use the `m' prefix to request operating +via menu (to temporarily override the +.op menustyle:Traditional +option). +Primarily useful for `,' (pickup) when there is only one class of +objects present (where there won't be any ``what kinds of objects?'' prompt, +so no opportunity to answer `m' at that prompt). +.lp "" +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. .lp F[yuhjklbn] -Prefix: fight a monster (even if you only guess one is there) +Prefix: fight a monster (even if you only guess one is there). .lp M[yuhjklbn] Prefix: move far, no pickup. .lp "g[yuhjklbn] @@ -640,6 +654,11 @@ D%u - drop only unpaid food. Kick something (usually a door). .lp e Eat food. +.lp "" +Normally checks for edible item(s) on the floor, then if none are found +or none are chosen, checks for edible item(s) in inventory. +Precede `e' with the `m' prefix to bypass attempting to eat +anything off the floor. .\" Make sure Elbereth is not hyphenated below, the exact spelling matters .hw Elbereth .lp E @@ -928,6 +947,8 @@ Kick something. .lp #loot Loot a box or bag on the floor beneath you, or the saddle from a steed standing next to you. +Precede with the `m' prefix to skip containers at your location +and go directly to removing a saddle. .lp #monster Use a monster's special ability (when polymorphed into monster form). .lp #name @@ -1286,13 +1307,21 @@ have the right equipment and skill. Convincing a wild beast to let you saddle it up is difficult to say the least. Many a dungeoneer has had to resort to magic and wizardry in order to forge the alliance. Once you do have the beast under your control however, you can -easily climb in and out of the saddle with the `#ride' command. Lead +easily climb in and out of the saddle with the ``#ride'' command. Lead the beast around the dungeon when riding, in the same manner as you would move yourself. It is the beast that you will see displayed on the map. .pg -Riding skill is managed by the `#enhance' command. See the section +Riding skill is managed by the ``#enhance'' command. See the section on Weapon proficiency for more information about that. +.pg +Use the `a' (apply) command and pick a saddle in your inventory to +attempt to put that saddle on an adjacent creature. If successful, +it will be transferred to that creature's inventory. +.pg +Use the ``#loot'' command while adjacent to a saddled creature to +try to remove the saddle from that creature. If successful, it will +be transferred to your inventory. .hn 2 Bones levels .pg diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 54dc5a542..cca387b25 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -690,10 +690,24 @@ Go in that direction until you hit a wall or run into something. %.lp \item[\tb{m[yuhjklbn]}] Prefix: move without picking up objects or fighting (even if you remember -a monster there) +a monster there).\\ +%.lp "" +A few non-movement commands use the `{\tt m}' prefix to request operating +via menu (to temporarily override the +{\it menustyle:Traditional\/} +option). +Primarily useful for `{\tt ,}' (pickup) when there is only one class of +objects present (where there won't be any ``what kinds of objects?'' prompt, +so no opportunity to answer `{\tt m}' at that prompt).\\ +%.lp "" +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. %.lp \item[\tb{F[yuhjklbn]}] -Prefix: fight a monster (even if you only guess one is there) +Prefix: fight a monster (even if you only guess one is there). %.lp \item[\tb{M[yuhjklbn]}] Prefix: Move far, no pickup. @@ -775,7 +789,12 @@ the bless\-ed/\-un\-curs\-ed/\-curs\-ed groups may be typed.\\ Kick something (usually a door). %.lp \item[\tb{e}] -Eat food. +Eat food.\\ +%.lp "" +Normally checks for edible item(s) on the floor, then if none are found +or none are chosen, checks for edible item(s) in inventory. +Precede `{\tt e}' with the `{\tt m}' prefix to bypass attempting to eat +anything off the floor. %.lp % Make sure Elbereth is not hyphenated below, the exact spelling matters. % (Only specified here to parallel Guidebook.mn; use of \tt font implicity @@ -1119,6 +1138,8 @@ Kick something. \item[\tb{\#loot}] Loot a box or bag on the floor beneath you, or the saddle from a steed standing next to you. +Precede with the `{\tt m}' prefix to skip containers at your location +and go directly to removing a saddle. %.lp \item[\tb{\#monster}] Use a monster's special ability (when polymorphed into monster form). @@ -1574,15 +1595,25 @@ have the right equipment and skill. Convincing a wild beast to let you saddle it up is difficult to say the least. Many a dungeoneer has had to resort to magic and wizardry in order to forge the alliance. Once you do have the beast under your control however, you can -easily climb in and out of the saddle with the `{\tt \#ride}' command. Lead +easily climb in and out of the saddle with the ``{\tt \#ride}'' command. Lead the beast around the dungeon when riding, in the same manner as you would move yourself. It is the beast that you will see displayed on the map. %.pg -Riding skill is managed by the `{\tt \#enhance}' command. See the section +Riding skill is managed by the ``{\tt \#enhance}'' command. See the section on Weapon proficiency for more information about that. +%.pg +Use the `{\tt a}' (apply) command and pick a saddle in your inventory to +attempt to put that saddle on an adjacent creature. If successful, +it will be transferred to that creature's inventory. + +%.pg +Use the ``{\tt \#loot}'' command while adjacent to a saddled creature to +try to remove the saddle from that creature. If successful, it will +be transferred to your inventory. + %.hn 2 \subsection*{Bones levels} diff --git a/doc/fixes36.1 b/doc/fixes36.1 index cb61f0231..605268883 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -302,6 +302,16 @@ conduct: wishing for an artifact and not getting it because it already exists avoid ring of poison resistance as starting equipment for orcish wizard gelatinous cube shouldn't be able to eat a scroll of scare monster make fireballs or cones of cold cast a skilled or higher not go through walls +prevent flying monsters from hovering over unreachable underwater objects +lembas wafer gives increased nutrition to elves, reduced nutrition to orcs; + cram ration gives increase nutrition to dwarves +when #tip's terse object drop format got interrupted by a regular message, + it continued using "obj2, obj3, ..." for subsequent objects, where + the sentence grammar no longer made sense (the interrupting message + still follows the comma of a partial sentence--attempting to fix that + seems hopeless; leaving it is better than always using verbose format) +for #tip inside shop, credit was incorrectly given for spilled gold if that + gold's stale location coordinates didn't happen to be inside the shop Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository @@ -322,6 +332,7 @@ when picking a destination for #jump, '$' to show valid dest. is more accurate Platform- and/or Interface-Specific Fixes ----------------------------------------- +tiles: wrinkled spellbook had a spurious brown pixel on far right edge of tile tty: M-N gave "Unknown command 'M-" with "'." finishing the sentence on the line below it, leaving bogus '.' displayed on the top row of the map tty: specifying all four of role, race, gender, and alignment still prompted @@ -337,6 +348,8 @@ tty: if color is disabled and use_inverse is enabled, display lava in inverse tty: if "--More--" was written to leftmost column (beginning of second line) while hero was swallowed, cursor would end up in wrong place (at end of status line instead back on map) after message line was cleared +tty: long message lines which wrap when shown on the top line might be + re-displayed incorrectly by ^P for msg_window={full,combo,reverse} unix/X11: in top level Makefile, some commented out definitions of VARDATND misspelled pilemark.xbm (as pilemark.xpm) unix: options file with CR+LF line ends and an invalid option line resulted in @@ -365,6 +378,7 @@ X11: support pre-selected entries in menu windows X11: make the extended command menu be easier to use and look a little nicer X11: make the getline text entry widget display a bigger text entry area X11: color didn't work when the map was displayed as text rather than tiles +X11: use grayscale monster tiles for statues General New Features diff --git a/include/flag.h b/include/flag.h index a8a6d8593..55a544471 100644 --- a/include/flag.h +++ b/include/flag.h @@ -382,6 +382,7 @@ extern NEARDATA struct instance_flags iflags; #define PLNMSG_TOWER_OF_FLAME 2 /* scroll of fire */ #define PLNMSG_CAUGHT_IN_EXPLOSION 3 /* explode() feedback */ #define PLNMSG_OBJ_GLOWS 4 /* "the glows " */ +#define PLNMSG_OBJNAM_ONLY 5 /* xname/doname only, for #tip */ /* Usage: * pline("some message"); * pline: vsprintf + putstr + iflags.last_msg = PLNMSG_UNKNOWN; diff --git a/include/mondata.h b/include/mondata.h index 229617782..cdb299007 100644 --- a/include/mondata.h +++ b/include/mondata.h @@ -193,10 +193,18 @@ (vegan(ptr) \ || ((ptr)->mlet == S_PUDDING && (ptr) != &mons[PM_BLACK_PUDDING])) +/* monkeys are tameable via bananas but not pacifiable via food, + otherwise their theft attack could be nullified too easily; + dogs and cats can be tamed by anything they like to eat and are + pacified by any other food; + horses can be tamed by always-veggy food or lichen corpses but + not tamed or pacified by other corpses or tins of veggy critters */ #define befriend_with_obj(ptr, obj) \ - (((obj)->oclass == FOOD_CLASS && is_domestic(ptr)) \ - || ((obj)->otyp == BANANA && \ - ((ptr) == &mons[PM_MONKEY] \ - || (ptr) == &mons[PM_APE]))) + (((ptr) == &mons[PM_MONKEY] || (ptr) == &mons[PM_APE]) \ + ? (obj)->otyp == BANANA \ + : (is_domestic(ptr) && (obj)->oclass == FOOD_CLASS \ + && ((ptr)->mlet != S_UNICORN \ + || objects[(obj)->otyp].oc_material == VEGGY \ + || ((obj)->otyp == CORPSE && (obj)->corpsenm == PM_LICHEN)))) #endif /* MONDATA_H */ diff --git a/src/cmd.c b/src/cmd.c index 4feb9bf94..13086a9c2 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -3445,11 +3445,20 @@ boolean initial; Cmd.move_SW = Cmd.dirchars[7]; } +/* non-movement commands which accept 'm' prefix to request menu operation */ STATIC_OVL boolean accept_menu_prefix(cmd_func) int NDECL((*cmd_func)); { if (cmd_func == dopickup || cmd_func == dotip + /* eat, #offer, and apply tinning-kit all use floorfood() to pick + an item on floor or in invent; 'm' skips picking from floor + (ie, inventory only) rather than request use of menu operation */ + || cmd_func == doeat || cmd_func == dosacrifice || cmd_func == doapply + /* 'm' for removing saddle from adjacent monster without checking + for containers at */ + || cmd_func == doloot + /* 'm' prefix allowed for some extended commands */ || cmd_func == doextcmd || cmd_func == doextlist) return TRUE; return FALSE; diff --git a/src/dog.c b/src/dog.c index f2993751d..0d55baad8 100644 --- a/src/dog.c +++ b/src/dog.c @@ -814,8 +814,8 @@ register struct obj *obj; case CARROT: return herbi ? DOGFOOD : starving ? ACCFOOD : MANFOOD; case BANANA: - return (mptr->mlet == S_YETI) - ? DOGFOOD + return (mptr->mlet == S_YETI && herbi) + ? DOGFOOD /* for monkey and ape (tameable), sasquatch */ : (herbi || starving) ? ACCFOOD : MANFOOD; diff --git a/src/eat.c b/src/eat.c index 5b8f08a8d..52e29def5 100644 --- a/src/eat.c +++ b/src/eat.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 eat.c $NHDT-Date: 1466289475 2016/06/18 22:37:55 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.169 $ */ +/* NetHack 3.6 eat.c $NHDT-Date: 1467028559 2016/06/27 11:55:59 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.171 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -13,6 +13,7 @@ STATIC_PTR int NDECL(unfaint); STATIC_DCL const char *FDECL(food_xname, (struct obj *, BOOLEAN_P)); STATIC_DCL void FDECL(choke, (struct obj *)); STATIC_DCL void NDECL(recalc_wt); +STATIC_DCL unsigned FDECL(obj_nutrition, (struct obj *)); STATIC_DCL struct obj *FDECL(touchfood, (struct obj *)); STATIC_DCL void NDECL(do_reset_eat); STATIC_DCL void FDECL(done_eating, (BOOLEAN_P)); @@ -296,6 +297,32 @@ reset_eat() return; } +/* base nutrition of a food-class object */ +STATIC_OVL unsigned +obj_nutrition(otmp) +struct obj *otmp; +{ + unsigned nut = (otmp->otyp == CORPSE) ? mons[otmp->corpsenm].cnutrit + : otmp->globby ? otmp->owt + : (unsigned) objects[otmp->otyp].oc_nutrition; + + if (otmp->otyp == LEMBAS_WAFER) { + if (maybe_polyd(is_elf(youmonst.data), Race_if(PM_ELF))) + nut += nut / 4; /* 800 -> 1000 */ + else if (maybe_polyd(is_orc(youmonst.data), Race_if(PM_ORC))) + nut -= nut / 4; /* 800 -> 600 */ + /* prevent polymorph making a partly eaten wafer + become more nutritious than an untouched one */ + if (otmp->oeaten >= nut) + otmp->oeaten = (otmp->oeaten < objects[LEMBAS_WAFER].oc_nutrition) + ? (nut - 1) : nut; + } else if (otmp->otyp == CRAM_RATION) { + if (maybe_polyd(is_dwarf(youmonst.data), Race_if(PM_DWARF))) + nut += nut / 6; /* 600 -> 700 */ + } + return nut; +} + STATIC_OVL struct obj * touchfood(otmp) struct obj *otmp; @@ -310,9 +337,7 @@ struct obj *otmp; if (!otmp->oeaten) { costly_alteration(otmp, COST_BITE); - otmp->oeaten = (otmp->otyp == CORPSE) ? mons[otmp->corpsenm].cnutrit - : otmp->globby ? otmp->owt - : (unsigned) objects[otmp->otyp].oc_nutrition; + otmp->oeaten = obj_nutrition(otmp); } if (carried(otmp)) { @@ -1741,6 +1766,15 @@ struct obj *otmp; make_vomiting((long) rn1(context.victual.reqtime, 14), FALSE); } break; + case LEMBAS_WAFER: + if (maybe_polyd(is_orc(youmonst.data), Race_if(PM_ORC))) { + pline("%s", "!#?&* elf kibble!"); + break; + } else if (maybe_polyd(is_elf(youmonst.data), Race_if(PM_ELF))) { + pline("A little goes a long way."); + break; + } + goto give_feedback; case MEATBALL: case MEAT_STICK: case HUGE_CHUNK_OF_MEAT: @@ -2368,6 +2402,7 @@ doeat() if (u.uedibility) { int res = edibility_prompts(otmp); + if (res) { Your( "%s stops tingling and your sense of smell returns to normal.", @@ -2577,9 +2612,7 @@ doeat() } /* re-calc the nutrition */ - basenutrit = (otmp->otyp == CORPSE) ? mons[otmp->corpsenm].cnutrit - : otmp->globby ? otmp->owt - : (unsigned) objects[otmp->otyp].oc_nutrition; + basenutrit = (int) obj_nutrition(otmp); debugpline3( "before rounddiv: victual.reqtime == %d, oeaten == %d, basenutrit == %d", @@ -2974,7 +3007,8 @@ int corpsecheck; /* 0, no check, 1, corpses, 2, tinnable corpses */ offering = !strcmp(verb, "sacrifice"); /* corpsecheck==1 */ /* if we can't touch floor objects then use invent food only */ - if (!can_reach_floor(TRUE) || (feeding && u.usteed) + if (iflags.menu_requested /* command was preceded by 'm' prefix */ + || !can_reach_floor(TRUE) || (feeding && u.usteed) || (is_pool_or_lava(u.ux, u.uy) && (Wwalking || is_clinger(youmonst.data) || (Flying && !Breathless)))) @@ -3085,10 +3119,9 @@ struct obj *obj; { long uneaten_amt, full_amount; + /* get full_amount first; obj_nutrition() might modify obj->oeaten */ + full_amount = (long) obj_nutrition(obj); uneaten_amt = (long) obj->oeaten; - full_amount = (long) ((obj->otyp == CORPSE) ? mons[obj->corpsenm].cnutrit - : obj->globby ? obj->owt - : (unsigned) objects[obj->otyp].oc_nutrition); if (uneaten_amt > full_amount) { impossible( "partly eaten food (%ld) more nutritious than untouched food (%ld)", diff --git a/src/monmove.c b/src/monmove.c index c703f2231..c032db504 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -979,15 +979,21 @@ not_special: * mpickstuff() as well. */ if (xx >= lmx && xx <= oomx && yy >= lmy && yy <= oomy) { - /* don't get stuck circling around an object that's - underneath - an immobile or hidden monster; paralysis victims - excluded */ + /* don't get stuck circling around object that's + underneath an immobile or hidden monster; + paralysis victims excluded */ if ((mtoo = m_at(xx, yy)) != 0 && (mtoo->msleeping || mtoo->mundetected || (mtoo->mappearance && !mtoo->iswiz) || !mtoo->data->mmove)) continue; + /* the mfndpos() test for whether to allow a move to a + water location accepts flyers, but they can't reach + underwater objects, so being able to move to a spot + is insufficient for deciding whether to do so */ + if ((is_pool(xx, yy) && !is_swimmer(ptr)) + || (is_lava(xx, yy) && !likes_lava(ptr))) + continue; if (((likegold && otmp->oclass == COIN_CLASS) || (likeobjs && index(practical, otmp->oclass) diff --git a/src/monst.c b/src/monst.c index 8a4ce63f1..5ae98c795 100644 --- a/src/monst.c +++ b/src/monst.c @@ -1923,16 +1923,18 @@ struct permonst _mons2[] = { /* * Apelike beasts */ + /* tameable via banana; does not grow up into ape... + not flagged as domestic, so no guilt penalty for eating non-pet one */ MON("monkey", S_YETI, LVL(2, 12, 6, 0, 0), (G_GENO | 1), A(ATTK(AT_CLAW, AD_SITM, 0, 0), ATTK(AT_BITE, AD_PHYS, 1, 3), NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), SIZ(100, 50, MS_GROWL, MZ_SMALL), 0, 0, - M1_ANIMAL | M1_HUMANOID | M1_CARNIVORE, 0, M3_INFRAVISIBLE, CLR_GRAY), + M1_ANIMAL | M1_HUMANOID | M1_OMNIVORE, 0, M3_INFRAVISIBLE, CLR_GRAY), MON("ape", S_YETI, LVL(4, 12, 6, 0, 0), (G_GENO | G_SGROUP | 2), A(ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_BITE, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK), SIZ(1100, 500, MS_GROWL, MZ_LARGE), 0, 0, - M1_ANIMAL | M1_HUMANOID | M1_CARNIVORE, M2_STRONG, M3_INFRAVISIBLE, + M1_ANIMAL | M1_HUMANOID | M1_OMNIVORE, M2_STRONG, M3_INFRAVISIBLE, CLR_BROWN), MON("owlbear", S_YETI, LVL(5, 12, 5, 0, 0), (G_GENO | 3), A(ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_CLAW, AD_PHYS, 1, 6), diff --git a/src/mthrowu.c b/src/mthrowu.c index bff417325..a448ba8e6 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -414,6 +414,24 @@ boolean verbose; /* give message(s) even when you can't see what happened */ return 0; } +#define MT_FLIGHTCHECK(pre) \ + (/* missile hits edge of screen */ \ + !isok(bhitpos.x + dx, bhitpos.y + dy) \ + /* missile hits the wall */ \ + || IS_ROCK(levl[bhitpos.x + dx][bhitpos.y + dy].typ) \ + /* missile hit closed door */ \ + || closed_door(bhitpos.x + dx, bhitpos.y + dy) \ + /* missile might hit iron bars */ \ + /* the random chance for small objects hitting bars is */ \ + /* skipped when reaching them at point blank range */ \ + || (levl[bhitpos.x + dx][bhitpos.y + dy].typ == IRONBARS \ + && hits_bars(&singleobj, \ + bhitpos.x, bhitpos.y, \ + bhitpos.x + dx, bhitpos.y + dy, \ + ((pre) ? 0 : !rn2(5)), 0)) \ + /* Thrown objects "sink" */ \ + || (!(pre) && IS_SINK(levl[bhitpos.x][bhitpos.y].typ))) + void m_throw(mon, x, y, dx, dy, range, obj) struct monst *mon; /* launching monster */ @@ -471,17 +489,7 @@ struct obj *obj; /* missile (or stack providing it) */ } } - /* pre-check for doors, walls and boundaries. - Also need to pre-check for bars regardless of direction; - the random chance for small objects hitting bars is - skipped when reaching them at point blank range */ - if (!isok(bhitpos.x + dx, bhitpos.y + dy) - || IS_ROCK(levl[bhitpos.x + dx][bhitpos.y + dy].typ) - || closed_door(bhitpos.x + dx, bhitpos.y + dy) - || (levl[bhitpos.x + dx][bhitpos.y + dy].typ == IRONBARS - && hits_bars(&singleobj, - bhitpos.x, bhitpos.y, - bhitpos.x + dx, bhitpos.y + dy, 0, 0))) { + if (MT_FLIGHTCHECK(TRUE)) { (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y); return; } @@ -611,20 +619,7 @@ struct obj *obj; /* missile (or stack providing it) */ } } if (!range /* reached end of path */ - /* missile hits edge of screen */ - || !isok(bhitpos.x + dx, bhitpos.y + dy) - /* missile hits the wall */ - || IS_ROCK(levl[bhitpos.x + dx][bhitpos.y + dy].typ) - /* missile hit closed door */ - || closed_door(bhitpos.x + dx, bhitpos.y + dy) - /* missile might hit iron bars */ - || (levl[bhitpos.x + dx][bhitpos.y + dy].typ == IRONBARS - && hits_bars(&singleobj, - bhitpos.x, bhitpos.y, - bhitpos.x + dx, bhitpos.y + dy, - !rn2(5), 0)) - /* Thrown objects "sink" */ - || IS_SINK(levl[bhitpos.x][bhitpos.y].typ)) { + || MT_FLIGHTCHECK(FALSE)) { if (singleobj) { /* hits_bars might have destroyed it */ if (m_shot.n > 1 && (cansee(bhitpos.x, bhitpos.y) || (archer && canseemon(archer)))) @@ -648,6 +643,8 @@ struct obj *obj; /* missile (or stack providing it) */ } } +#undef MT_FLIGHTCHECK + /* Monster throws item at another monster */ int thrwmm(mtmp, mtarg) diff --git a/src/pager.c b/src/pager.c index 661cf04a5..b00c425a9 100644 --- a/src/pager.c +++ b/src/pager.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 pager.c $NHDT-Date: 1465114189 2016/06/05 08:09:49 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.107 $ */ +/* NetHack 3.6 pager.c $NHDT-Date: 1466638086 2016/06/22 23:28:06 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.111 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1307,6 +1307,35 @@ doidtrap() return 0; } +/* + Implements a rudimentary if/elif/else/endif interpretor and use + conditionals in dat/cmdhelp to describe what command each keystroke + currently invokes, so that there isn't a lot of "(debug mode only)" + and "(if number_pad is off)" cluttering the feedback that the user + sees. (The conditionals add quite a bit of clutter to the raw data + but users don't see that. number_pad produces a lot of conditional + commands: basic letters vs digits, 'g' vs 'G' for '5', phone + keypad vs normal layout of digits, and QWERTZ keyboard swap between + y/Y/^Y/M-y/M-Y/M-^Y and z/Z/^Z/M-z/M-Z/M-^Z.) + + The interpretor understands + '&#' for comment, + '&? option' for 'if' (also '&? !option' + or '&? option=value[,value2,...]' + or '&? !option=value[,value2,...]'), + '&: option' for 'elif' (with argument variations same as 'if'; + any number of instances for each 'if'), + '&:' for 'else' (also '&: #comment'; + 0 or 1 instance for a given 'if'), and + '&.' for 'endif' (also '&. #comment'; required for each 'if'). + + The option handling is a bit of a mess, with no generality for + which options to deal with and only a comma separated list of + integer values for the '=value' part. number_pad is the only + supported option that has a value; the few others (wizard/debug, + rest_on_space, #if SHELL, #if SUSPEND) are booleans. +*/ + STATIC_DCL void whatdoes_help() { diff --git a/src/pickup.c b/src/pickup.c index 9be8c809f..0f8b5b5d4 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -1630,7 +1630,7 @@ doloot() char qbuf[BUFSZ]; int prev_inquiry = 0; boolean prev_loot = FALSE; - int num_conts; + int num_conts = 0; abort_looting = FALSE; @@ -1653,13 +1653,30 @@ doloot() cc.x = u.ux; cc.y = u.uy; -lootcont: + if (iflags.menu_requested) + goto lootmon; + + lootcont: if ((num_conts = container_at(cc.x, cc.y, TRUE)) > 0) { boolean anyfound = FALSE; if (!able_to_loot(cc.x, cc.y, TRUE)) return 0; + if (Blind && !uarmg) { + /* if blind and without gloves, attempting to #loot at the + location of a cockatrice corpse is fatal before asking + whether to manipulate any containers */ + for (nobj = sobj_at(CORPSE, cc.x, cc.y); nobj; + nobj = nxtobj(nobj, CORPSE, TRUE)) + if (will_feel_cockatrice(nobj, FALSE)) { + feel_cockatrice(nobj, FALSE); + /* if life-saved (or poly'd into stone golem), + terminate attempt to loot */ + return 1; + } + } + if (num_conts > 1) { /* use a menu to loot many containers */ int n, i; @@ -1722,9 +1739,11 @@ lootcont: } else if (IS_GRAVE(levl[cc.x][cc.y].typ)) { You("need to dig up the grave to effectively loot it..."); } + /* * 3.3.1 introduced directional looting for some things. */ + lootmon: if (c != 'y' && mon_beside(u.ux, u.uy)) { if (!get_adjacent_loc("Loot in what direction?", "Invalid loot location", u.ux, u.uy, &cc)) @@ -2883,20 +2902,27 @@ tipcontainer(box) struct obj *box; /* or bag */ { xchar ox = u.ux, oy = u.uy; /* #tip only works at hero's location */ - boolean empty_it = FALSE, - /* Shop handling: can't rely on the container's own unpaid - or no_charge status because contents might differ with it. - A carried container's contents will be flagged as unpaid - or not, as appropriate, and need no special handling here. - Items owned by the hero get sold to the shop without - confirmation as with other uncontrolled drops. A floor - container's contents will be marked no_charge if owned by - hero, otherwise they're owned by the shop. By passing - the contents through shop billing, they end up getting - treated the same as in the carried case. We do so one - item at a time instead of doing whole container at once - to reduce the chance of exhausting shk's billing capacity. */ - maybeshopgoods = !carried(box) && costly_spot(ox, oy); + boolean empty_it = FALSE, maybeshopgoods; + + /* box is either held or on floor at hero's spot; no need to check for + nesting; when held, we need to update its location to match hero's; + for floor, the coordinate updating is redundant */ + if (get_obj_location(box, &ox, &oy, 0)) + box->ox = ox, box->oy = oy; + + /* Shop handling: can't rely on the container's own unpaid + or no_charge status because contents might differ with it. + A carried container's contents will be flagged as unpaid + or not, as appropriate, and need no special handling here. + Items owned by the hero get sold to the shop without + confirmation as with other uncontrolled drops. A floor + container's contents will be marked no_charge if owned by + hero, otherwise they're owned by the shop. By passing + the contents through shop billing, they end up getting + treated the same as in the carried case. We do so one + item at a time instead of doing whole container at once + to reduce the chance of exhausting shk's billing capacity. */ + maybeshopgoods = !carried(box) && costly_spot(box->ox, box->oy); /* caveat: this assumes that cknown, lknown, olocked, and otrapped fields haven't been overloaded to mean something special for the @@ -2959,7 +2985,7 @@ struct obj *box; /* or bag */ if (empty_it) { struct obj *otmp, *nobj; - boolean verbose = FALSE, highdrop = !can_reach_floor(TRUE), + boolean terse, highdrop = !can_reach_floor(TRUE), altarizing = IS_ALTAR(levl[ox][oy].typ), cursed_mbag = (Is_mbag(box) && box->cursed); int held = carried(box); @@ -2967,20 +2993,27 @@ struct obj *box; /* or bag */ if (u.uswallow) highdrop = altarizing = FALSE; + terse = !(highdrop || altarizing || costly_spot(box->ox, box->oy)); box->cknown = 1; + /* Terse formatting is + * "Objects spill out: obj1, obj2, obj3, ..., objN." + * If any other messages intervene between objects, we revert to + * "ObjK drops to the floor.", "ObjL drops to the floor.", &c. + */ pline("%s out%c", box->cobj->nobj ? "Objects spill" : "An object spills", - !(highdrop || altarizing) ? ':' : '.'); + terse ? ':' : '.'); for (otmp = box->cobj; otmp; otmp = nobj) { nobj = otmp->nobj; obj_extract_self(otmp); + otmp->ox = box->ox, otmp->oy = box->oy; if (box->otyp == ICE_BOX) { removed_from_icebox(otmp); /* resume rotting for corpse */ } else if (cursed_mbag && !rn2(13)) { loss += mbag_item_gone(held, otmp); /* abbreviated drop format is no longer appropriate */ - verbose = TRUE; + terse = FALSE; continue; } @@ -2993,14 +3026,18 @@ struct obj *box; /* or bag */ /* might break or fall down stairs; handles altars itself */ hitfloor(otmp); } else { - if (altarizing) + if (altarizing) { doaltarobj(otmp); - else if (verbose) + } else if (!terse) { pline("%s %s to the %s.", Doname2(otmp), otense(otmp, "drop"), surface(ox, oy)); - else + } else { pline("%s%c", doname(otmp), nobj ? ',' : '.'); + iflags.last_msg = PLNMSG_OBJNAM_ONLY; + } dropy(otmp); + if (iflags.last_msg != PLNMSG_OBJNAM_ONLY) + terse = FALSE; /* terse formatting has been interrupted */ } if (maybeshopgoods) iflags.suppress_price--; /* reset */ diff --git a/src/shk.c b/src/shk.c index 586f5de6e..a914a56d7 100644 --- a/src/shk.c +++ b/src/shk.c @@ -2130,9 +2130,10 @@ boolean unpaid_only; return price; } +/* count amount of gold inside container 'obj' and any nested containers */ long contained_gold(obj) -register struct obj *obj; +struct obj *obj; { register struct obj *otmp; register long value = 0L; @@ -2867,12 +2868,12 @@ xchar x, y; boolean isgold = (obj->oclass == COIN_CLASS); boolean only_partially_your_contents = FALSE; + if (!*u.ushops) /* do cheapest exclusion test first */ + return; if (!(shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) || !inhishop(shkp)) return; if (!costly_spot(x, y)) return; - if (!*u.ushops) - return; if (obj->unpaid && !container && !isgold) { sub_one_frombill(obj, shkp); diff --git a/src/spell.c b/src/spell.c index 40969a62e..dba05de6c 100644 --- a/src/spell.c +++ b/src/spell.c @@ -43,7 +43,7 @@ STATIC_DCL int NDECL(throwspell); STATIC_DCL void NDECL(cast_protection); STATIC_DCL void FDECL(spell_backfire, (int)); STATIC_DCL const char *FDECL(spelltypemnemonic, (int)); -STATIC_DCL boolean FDECL(spell_hurtle_step, (genericptr_t, int, int)); +STATIC_DCL boolean FDECL(spell_aim_step, (genericptr_t, int, int)); /* The roles[] table lists the role-specific values for tuning * percent_success(). @@ -1181,12 +1181,14 @@ boolean atme; return 1; } +/*ARGSUSED*/ STATIC_OVL boolean -spell_hurtle_step(arg, x, y) -genericptr_t arg; +spell_aim_step(arg, x, y) +genericptr_t arg UNUSED; int x, y; { - if (!isok(x,y)) return FALSE; + if (!isok(x,y)) + return FALSE; if (!ZAP_POS(levl[x][y].typ) && !(IS_DOOR(levl[x][y].typ) && (levl[x][y].doormask & D_ISOPEN))) return FALSE; @@ -1233,7 +1235,7 @@ throwspell() uc.x = u.ux; uc.y = u.uy; - walk_path(&uc, &cc, spell_hurtle_step, NULL); + walk_path(&uc, &cc, spell_aim_step, (genericptr_t) 0); u.dx = cc.x; u.dy = cc.y; diff --git a/sys/unix/Makefile.dat b/sys/unix/Makefile.dat index cd35a29c5..ffc6720ac 100644 --- a/sys/unix/Makefile.dat +++ b/sys/unix/Makefile.dat @@ -31,9 +31,11 @@ all: $(VARDAT) spec_levs quest_levs dungeon (cd ../util ; $(MAKE) tile2bmp) x11tiles: ../util/tile2x11 ../win/share/monsters.txt ../win/share/objects.txt \ - ../win/share/other.txt + ../win/share/other.txt \ + ../win/share/monsters.txt ../util/tile2x11 ../win/share/monsters.txt ../win/share/objects.txt \ - ../win/share/other.txt + ../win/share/other.txt \ + -grayscale ../win/share/monsters.txt beostiles: ../util/tile2beos ../win/share/monsters.txt \ ../win/share/objects.txt \ diff --git a/win/X11/tile2x11.c b/win/X11/tile2x11.c index c9e410f46..8b7b455e1 100644 --- a/win/X11/tile2x11.c +++ b/win/X11/tile2x11.c @@ -177,7 +177,7 @@ char **argv; header.per_row = TILES_PER_ROW; if (argc == 1) { - Fprintf(stderr, "usage: %s txt_file1 [txt_file2 ...]\n", argv[0]); + Fprintf(stderr, "usage: %s txt_file1 [txt_file2 ...] [-grayscale txt_fileN]\n", argv[0]); exit(1); } @@ -190,8 +190,15 @@ char **argv; /* don't leave garbage at end of partial row */ (void) memset((genericptr_t) tile_bytes, 0, sizeof(tile_bytes)); - for (i = 1; i < argc; i++) + for (i = 1; i < argc; i++) { + if (!strncmp(argv[i], "-grayscale", 10)) { + set_grayscale(TRUE); + if (i < (argc - 1)) i++; + } else { + set_grayscale(FALSE); + } process_file(argv[i]); + } Fprintf(stderr, "Total tiles: %ld\n", header.ntiles); /* round size up to the end of the row */ diff --git a/win/share/objects.txt b/win/share/objects.txt index 996c59198..15c6181e8 100644 --- a/win/share/objects.txt +++ b/win/share/objects.txt @@ -7108,7 +7108,7 @@ Z = (195, 195, 195) ..KKAKKAAKKKOAA. .JKKKAAAKKKOKA.. .KKKKKKKKKLOAA.. - .KOOLKKKKKOKA..K + .KOOLKKKKKOKA... ..JKOOKKKKOAA... ....JKLOOOKA.... ......JJKKAA.... diff --git a/win/share/tile.doc b/win/share/tile.doc index 8137f0f6e..1817982a7 100644 --- a/win/share/tile.doc +++ b/win/share/tile.doc @@ -57,8 +57,10 @@ boolean read_text_tile(pixel[TILE_Y][TILE_X]); boolean write_text_tile(pixel[TILE_Y][TILE_X]); writes tile -There are two additional shared routines provided for writers: +There are some additional shared routines provided for writers: +void set_grayscale(boolean g); + do grayscale color substitutions when reading the tile text file void init_colormap(); initialize the output colormap from the input one must be called before opening output file as colormap is part of header diff --git a/win/share/tile.h b/win/share/tile.h index 566cc386c..7c01fb067 100644 --- a/win/share/tile.h +++ b/win/share/tile.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 tile.h $NHDT-Date: 1432512803 2015/05/25 00:13:23 $ $NHDT-Branch: master $:$NHDT-Revision: 1.10 $ */ +/* NetHack 3.6 tile.h $NHDT-Date: 1466687973 2016/06/23 13:19:33 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.12 $ */ typedef unsigned char pixval; typedef struct pixel_s { @@ -35,6 +35,7 @@ extern boolean FDECL(read_text_tile, (pixel(*) [TILE_X])); extern boolean FDECL(write_text_tile, (pixel(*) [TILE_X])); extern int NDECL(fclose_text_file); +extern void FDECL(set_grayscale, (int)); extern void NDECL(init_colormap); extern void NDECL(merge_colormap); diff --git a/win/share/tile2bmp.c b/win/share/tile2bmp.c index 7a6c736a4..d0fb36333 100644 --- a/win/share/tile2bmp.c +++ b/win/share/tile2bmp.c @@ -237,6 +237,7 @@ char *argv[]; } initflag = 1; } + set_grayscale(pass == 3); /* printf("Colormap initialized\n"); */ while (read_text_tile(tilepixels)) { build_bmptile(tilepixels); @@ -327,11 +328,6 @@ BITMAPINFOHEADER *pbmih; pbmih->biClrImportant = (DWORD) 0; } -static int graymappings[] = { - /* . A B C D E F G H I J K L M N O P */ - 0, 1, 17, 18, 19, 20, 27, 22, 23, 24, 25, 26, 21, 15, 13, 14, 14 -}; - static void build_bmptile(pixels) pixel (*pixels)[TILE_X]; @@ -351,14 +347,6 @@ pixel (*pixels)[TILE_X]; Fprintf(stderr, "color not in colormap!\n"); y = (MAX_Y - 1) - (cur_y + yoffset); apply_color = cur_color; - if (pass == 3) { - /* map to shades of gray */ - if (cur_color > (SIZE(graymappings) - 1)) - Fprintf(stderr, "Gray mapping issue %d %d.\n", cur_color, - SIZE(graymappings) - 1); - else - apply_color = graymappings[cur_color]; - } #if BITCOUNT == 4 x = (cur_x / 2) + xoffset; bmp.packtile[y][x] = cur_x % 2 diff --git a/win/share/tilemap.c b/win/share/tilemap.c index f7acba0e8..cae25fdfb 100644 --- a/win/share/tilemap.c +++ b/win/share/tilemap.c @@ -21,7 +21,7 @@ extern void FDECL(exit, (int)); #endif #endif -#if defined(MSDOS) || defined(WIN32) +#if defined(MSDOS) || defined(WIN32) || defined(X11_GRAPHICS) #define STATUES_LOOK_LIKE_MONSTERS #endif diff --git a/win/share/tiletext.c b/win/share/tiletext.c index 5837ae422..8d913f897 100644 --- a/win/share/tiletext.c +++ b/win/share/tiletext.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 tiletext.c $NHDT-Date: 1454464783 2016/02/03 01:59:43 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.12 $ */ +/* NetHack 3.6 tiletext.c $NHDT-Date: 1466687974 2016/06/23 13:19:34 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.14 $ */ /* NetHack may be freely redistributed. See license for details. */ #include "config.h" @@ -37,6 +37,20 @@ static void FDECL(write_txttile, (FILE *, pixel (*)[TILE_X])); "%[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.] = " \ "(%d, %d, %d) " +static int grayscale = 0; +/* grayscale color mapping */ +static const int graymappings[] = { + /* . A B C D E F G H I J K L M N O P */ + 0, 1, 17, 18, 19, 20, 27, 22, 23, 24, 25, 26, 21, 15, 13, 14, 14 +}; + +void +set_grayscale(g) +int g; +{ + grayscale = g; +} + static void read_text_colormap(txtfile) FILE *txtfile; @@ -135,6 +149,13 @@ pixel (*pixels)[TILE_X]; return FALSE; } k = color_index[(int) c[0]]; + if (grayscale) { + if (k > (SIZE(graymappings) - 1)) + Fprintf(stderr, "Gray mapping issue %d > %d.\n", k, + SIZE(graymappings) - 1); + else + k = graymappings[k]; + } if (k == -1) Fprintf(stderr, "color %c not in colormap!\n", c[0]); else { diff --git a/win/tty/topl.c b/win/tty/topl.c index 4c857cb81..dd6758a4e 100644 --- a/win/tty/topl.c +++ b/win/tty/topl.c @@ -8,7 +8,6 @@ #include "tcap.h" #include "wintty.h" -#include #ifndef C /* this matches src/cmd.c */ #define C(c) (0x1f & (c)) @@ -48,8 +47,7 @@ tty_doprev_message() do { morc = 0; if (cw->maxcol == cw->maxrow) { - ttyDisplay->dismiss_more = - C('p'); /* allowed at --More-- */ + ttyDisplay->dismiss_more = C('p'); /* ^P ok at --More-- */ redotoplin(toplines); cw->maxcol--; if (cw->maxcol < 0) @@ -57,8 +55,7 @@ tty_doprev_message() if (!cw->data[cw->maxcol]) cw->maxcol = cw->maxrow; } else if (cw->maxcol == (cw->maxrow - 1)) { - ttyDisplay->dismiss_more = - C('p'); /* allowed at --More-- */ + ttyDisplay->dismiss_more = C('p'); /* ^P ok at --More-- */ redotoplin(cw->data[cw->maxcol]); cw->maxcol--; if (cw->maxcol < 0) @@ -130,6 +127,7 @@ redotoplin(str) const char *str; { int otoplin = ttyDisplay->toplin; + home(); if (*str & 0x80) { /* kludge for the / command, the only time we ever want a */ @@ -251,10 +249,11 @@ register const char *bp; (void) strncpy(toplines, bp, TBUFSZ); toplines[TBUFSZ - 1] = 0; - for (tl = toplines; n0 >= CO;) { + for (tl = toplines; n0 >= CO; ) { otl = tl; - for (tl += CO - 1; tl != otl && !isspace(*tl); --tl) - ; + for (tl += CO - 1; tl != otl; --tl) + if (*tl == ' ') + break; if (tl == otl) { /* Eek! A huge token. Try splitting after it. */ tl = index(otl, ' '); @@ -299,7 +298,7 @@ char c; break; default: if (ttyDisplay->curx == CO - 1) - topl_putsym('\n'); /* 1 <= curx <= CO; avoid CO */ + topl_putsym('\n'); /* 1 <= curx < CO; avoid CO */ #ifdef WIN32CON (void) putchar(c); #endif diff --git a/win/tty/wintty.c b/win/tty/wintty.c index 8ceb1dc4e..e38e3e7c8 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -2443,22 +2443,27 @@ compress_str(str) const char *str; { static char cbuf[BUFSZ]; - /* compress in case line too long */ - if ((int) strlen(str) >= CO) { - register const char *bp0 = str; - register char *bp1 = cbuf; + /* compress out consecutive spaces if line is too long; + topline wrapping converts space at wrap point into newline, + we reverse that here */ + if ((int) strlen(str) >= CO || index(str, '\n')) { + register const char *bp0 = str; + char c, nxtc, *bp1 = cbuf, *endbp1 = &cbuf[sizeof cbuf - 1]; + + cbuf[0] = cbuf[sizeof cbuf - 1] = '\0'; /* superfluous */ + nxtc = (*bp0 == '\n') ? ' ' : *bp0; do { -#ifdef CLIPPING - if (*bp0 != ' ' || bp0[1] != ' ') -#else - if (*bp0 != ' ' || bp0[1] != ' ' || bp0[2] != ' ') -#endif - *bp1++ = *bp0; - } while (*bp0++); - } else - return str; - return cbuf; + c = nxtc; + nxtc = bp0[1]; + if (nxtc == '\n') + nxtc = ' '; + if (c != ' ' || nxtc != ' ') + *bp1++ = c; + } while (*bp0++ && bp1 < endbp1); + str = cbuf; + } + return str; } void @@ -2601,7 +2606,7 @@ const char *str; } if (cw->data[cw->cury]) free((genericptr_t) cw->data[cw->cury]); - n0 = strlen(str) + 1; + n0 = (long) strlen(str) + 1L; ob = cw->data[cw->cury] = (char *) alloc((unsigned) n0 + 1); *ob++ = (char) (attr + 1); /* avoid nuls, for convenience */ Strcpy(ob, str);