From 93f61975f58a426cc6f58a9a4816976a08f04501 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 23 Dec 2017 15:19:27 -0800 Subject: [PATCH 1/9] flags.sortloot This should maximize save file compatibility between 3.6.1 and 3.6.0, at the risk of breaking save files for folks using post-3.6.0 git sources. (It's unlikely that many in that situation are using a configuration which will be affected, so probably nobody will notice.) --- include/flag.h | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/include/flag.h b/include/flag.h index a8f9172f7..d3938adca 100644 --- a/include/flag.h +++ b/include/flag.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 flag.h $NHDT-Date: 1508827590 2017/10/24 06:46:30 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.129 $ */ +/* NetHack 3.6 flag.h $NHDT-Date: 1514071158 2017/12/23 23:19:18 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.132 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -48,7 +48,42 @@ struct flag { boolean showexp; /* show experience points */ boolean showscore; /* show score */ boolean silent; /* whether the bell rings or not */ + /* The story so far: + * 'sortloot' originally took a True/False value but was changed + * to use a letter instead. 3.6.0 was released without changing its + * type from 'boolean' to 'char'. A compiler was smart enough to + * complain that assigning any of the relevant letters was not 0 or 1 + * so not appropriate for boolean (by a configuration which used + * SKIP_BOOLEAN to bypass nethack's 'boolean' and use a C++-compatible + * one). So the type was changed to 'xchar', which is guaranteed to + * match the size of 'boolean' (this guarantee only applies for the + * !SKIP_BOOLEAN config, unfortunately). Since xchar does not match + * actual use, the type was later changed to 'char'. But that would + * break 3.6.0 savefile compatibility for configurations which typedef + * 'schar' to 'short int' instead of to 'char'. (Needed by pre-ANSI + * systems that use unsigned characters without a way to force them + * to be signed.) So, the type has been changed back to 'xchar' for + * 3.6.1. + * + * TODO: change to 'char' (and move out of this block of booleans, + * and get rid of these comments...) once 3.6.0 savefile compatibility + * eventually ends. + */ +#ifndef SKIP_BOOLEAN + /* this is the normal configuration; assigning a character constant + for a normal letter to an 'xchar' variable should always work even + if 'char' is unsigned since character constants are actually 'int' + and letters are within the range where signedness shouldn't matter */ + xchar sortloot; /* 'n'=none, 'l'=loot (pickup), 'f'=full ('l'+invent) */ +#else + /* with SKIP_BOOLEAN, we have no idea what underlying type is being + used, other than it isn't 'xchar' (although its size might match + that) or a bitfield (because it must be directly addressable); + it's probably either 'char' for compactness or 'int' for access, + but we don't know which and it might be something else anyway; + flip a coin here and guess 'char' for compactness */ char sortloot; /* 'n'=none, 'l'=loot (pickup), 'f'=full ('l'+invent) */ +#endif boolean sortpack; /* sorted inventory */ boolean sparkle; /* show "resisting" special FX (Scott Bigham) */ boolean standout; /* use standout for --More-- */ From 062748c69554e276ad798e92c69ce13a27f2985f Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 23 Dec 2017 15:42:20 -0800 Subject: [PATCH 2/9] fix #H6648 - can't wear via 'W' but can via 'P' More fallout from allowing W/T on accessories and P/R on armor without combining them outright. If poly'd into verysmall or nohands critter, 'W' yields "don't even bother" before even prompting what to wear, but 'P' would prompt for an accessory and then wear armor if that was what got picked. Now 'P' will still prompt, in case it's for an accessory, but picking a piece of armor no longer wears that armor. 'W' still doesn't even prompt, so won't allow accessories as well as no armor. I'm not sure whether that should be changed. --- doc/fixes36.1 | 2 ++ src/do_wear.c | 12 ++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 80e792766..69724485b 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -490,6 +490,8 @@ if a fiery monster, wand of fire, or fiery explosion burned up a paper golem, it could still leave blank scrolls hero poly'd into paper or straw golem reverts to human if burned up even when Unchanging without any explanation given +hero polymorphed into form which can't wear armor via 'W' (eliciting "don't + even bother") could wear it via 'P' Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/do_wear.c b/src/do_wear.c index 2adcb6816..f8f94e342 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 do_wear.c $NHDT-Date: 1496959478 2017/06/08 22:04:38 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.98 $ */ +/* NetHack 3.6 do_wear.c $NHDT-Date: 1514072526 2017/12/23 23:42:06 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.100 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1617,6 +1617,14 @@ boolean noisy; int err = 0; const char *which; + /* this is the same check as for 'W' (dowear), but different message, + in case we get here via 'P' (doputon) */ + if (verysmall(youmonst.data) || nohands(youmonst.data)) { + if (noisy) + You("can't wear any armor in your current form."); + return 0; + } + which = is_cloak(otmp) ? c_cloak : is_shirt(otmp) @@ -1964,7 +1972,7 @@ dowear() /* cantweararm() checks for suits of armor, not what we want here; verysmall() or nohands() checks for shields, gloves, etc... */ - if ((verysmall(youmonst.data) || nohands(youmonst.data))) { + if (verysmall(youmonst.data) || nohands(youmonst.data)) { pline("Don't even bother."); return 0; } From 6857372f451d2c4ecfbf52c6d1f098e881ac6298 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 24 Dec 2017 14:00:49 -0800 Subject: [PATCH 3/9] fix #H6624 - missile miss message redundancy Excess verbosity for multi-shot throwing/shooting by monsters. The Green-elf shoots 2 elven arrows. You are almost hit by the 1st elven arrow. The 1st elven arrow misses. You are almost hit by the 2nd elven arrow. The 2nd elven arrow misses. Just give one or the other of the miss messages. If it reaches the hero's location, give the first. If it lands somewhere else, give the second. (It might be possible to get both if hero is displaced and the monster thinks he/she is behind his/her actual location. I'm not sure.) Also, only say "you are almost hit" if it is true: the dieroll nearly got past your armor. Otherwise, say "The Nth arrow misses you." --- doc/fixes36.1 | 1 + src/mthrowu.c | 24 +++++++++++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 69724485b..c27a3b795 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -492,6 +492,7 @@ hero poly'd into paper or straw golem reverts to human if burned up even when Unchanging without any explanation given hero polymorphed into form which can't wear armor via 'W' (eliciting "don't even bother") could wear it via 'P' +make multi-shot missiles fired by monsters be less verbose when they miss Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/mthrowu.c b/src/mthrowu.c index 3d2830b96..6806feef4 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mthrowu.c $NHDT-Date: 1446887531 2015/11/07 09:12:11 $ $NHDT-Branch: master $:$NHDT-Revision: 1.63 $ */ +/* NetHack 3.6 mthrowu.c $NHDT-Date: 1514152830 2017/12/24 22:00:30 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.73 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -26,6 +26,7 @@ STATIC_OVL NEARDATA const char *breathwep[] = { }; extern boolean notonhead; /* for long worms */ +STATIC_VAR int mesg_given; /* for m_throw()/thitu() 'miss' message */ /* hero is hit by something other than a monster */ int @@ -37,7 +38,7 @@ const char *name; /* if null, then format `*objp' */ struct obj *obj = objp ? *objp : 0; const char *onm, *knm; boolean is_acid; - int kprefix = KILLED_BY_AN; + int kprefix = KILLED_BY_AN, dieroll; char onmbuf[BUFSZ], knmbuf[BUFSZ]; if (!name) { @@ -59,10 +60,15 @@ const char *name; /* if null, then format `*objp' */ : an(name); is_acid = (obj && obj->otyp == ACID_VENOM); - if (u.uac + tlev <= rnd(20)) { - if (Blind || !flags.verbose) + if (u.uac + tlev <= (dieroll = rnd(20))) { + ++mesg_given; + if (Blind || !flags.verbose) { pline("It misses."); - else + } else if (u.uac + tlev <= dieroll - 2) { + if (onm != onmbuf) + Strcpy(onmbuf, onm); /* [modifiable buffer for upstart()] */ + pline("%s %s you.", upstart(onmbuf), vtense(onmbuf, "miss")); + } else You("are almost hit by %s.", onm); return 0; } else { @@ -518,6 +524,7 @@ struct obj *obj; /* missile (or stack providing it) */ (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y); return; } + mesg_given = 0; /* a 'missile misses' message has not yet been shown */ /* Note: drop_throw may destroy singleobj. Since obj must be destroyed * early to avoid the dagger bug, anyone who modifies this code should @@ -646,8 +653,10 @@ struct obj *obj; /* missile (or stack providing it) */ if (!range /* reached end of path */ || MT_FLIGHTCHECK(FALSE)) { if (singleobj) { /* hits_bars might have destroyed it */ - if (m_shot.n > 1 && (cansee(bhitpos.x, bhitpos.y) - || (archer && canseemon(archer)))) + if (m_shot.n > 1 + && (!mesg_given || bhitpos.x != u.ux || bhitpos.y != u.uy) + && (cansee(bhitpos.x, bhitpos.y) + || (archer && canseemon(archer)))) pline("%s misses.", The(mshot_xname(singleobj))); (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y); } @@ -659,6 +668,7 @@ struct obj *obj; /* missile (or stack providing it) */ tmp_at(bhitpos.x, bhitpos.y); delay_output(); tmp_at(DISP_END, 0); + mesg_given = 0; /* reset */ if (blindinc) { u.ucreamed += blindinc; From 860cdf6625bdbbc4133f31084d27140a7ae25fd8 Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 28 Dec 2017 15:40:11 -0800 Subject: [PATCH 4/9] playing music while impaired Newsgroup discussion mentioned that it was possible to open the castle drawbridge with musical notes even while confused. There was already some handling for confusion: improvisation treats magical instruments as their mundane equivalents. This takes if farther: when stunned or confused or hallucinating you'll always improvise instead of being given a chance to choose notes. Being stunned now behaves the same as being confused in regards to magical instruments (possibly/probably it should prevent playing music altogether). Hallucination gives different feedback at start but still allows magical playing. --- doc/fixes36.1 | 1 + src/music.c | 138 ++++++++++++++++++++++++++++++-------------------- 2 files changed, 83 insertions(+), 56 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index c27a3b795..05216acec 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -493,6 +493,7 @@ hero poly'd into paper or straw golem reverts to human if burned up even when hero polymorphed into form which can't wear armor via 'W' (eliciting "don't even bother") could wear it via 'P' make multi-shot missiles fired by monsters be less verbose when they miss +can no longer play controlled notes on musical instrument if impaired Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/music.c b/src/music.c index 75ab39d4d..68c9e97d5 100644 --- a/src/music.c +++ b/src/music.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 music.c $NHDT-Date: 1452660194 2016/01/13 04:43:14 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.42 $ */ +/* NetHack 3.6 music.c $NHDT-Date: 1514504228 2017/12/28 23:37:08 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.46 $ */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -6,7 +6,7 @@ * This file contains the different functions designed to manipulate the * musical instruments and their various effects. * - * Actually the list of instruments / effects is : + * The list of instruments / effects is : * * (wooden) flute may calm snakes if player has enough dexterity * magic flute may put monsters to sleep: area of effect depends @@ -450,8 +450,7 @@ STATIC_OVL int do_improvisation(instr) struct obj *instr; { - int damage, do_spec = !Confusion; -#if defined(MAC) || defined(AMIGA) || defined(VPIX_MUSIC) || defined(PCMUSIC) + int damage, mode, do_spec = !(Stunned || Confusion); struct obj itmp; itmp = *instr; @@ -476,23 +475,55 @@ struct obj *instr; #ifdef PCMUSIC pc_speaker(&itmp, "C"); #endif -#endif /* MAC || AMIGA || VPIX_MUSIC || PCMUSIC */ - if (!do_spec) +#define PLAY_NORMAL 0 +#define PLAY_STUNNED 1 +#define PLAY_CONFUSED 2 +#define PLAY_HALLU 4 + mode = PLAY_NORMAL; + if (Stunned) + mode |= PLAY_STUNNED; + if (Confusion) + mode |= PLAY_CONFUSED; + if (Hallucination) + mode |= PLAY_HALLU; + + switch (mode) { + case PLAY_NORMAL: + You("start playing %s.", yname(instr)); + break; + case PLAY_STUNNED: + You("produce an obnoxious droning sound."); + break; + case PLAY_CONFUSED: + You("produce a raucous noise."); + break; + case PLAY_HALLU: + You("produce a kaleidoscopic display of floating butterfiles."); + break; + /* TODO? give some or all of these combinations their own feedback; + hallucination ones should reference senses other than hearing... */ + case PLAY_STUNNED | PLAY_CONFUSED: + case PLAY_STUNNED | PLAY_HALLU: + case PLAY_CONFUSED | PLAY_HALLU: + case PLAY_STUNNED | PLAY_CONFUSED | PLAY_HALLU: + default: pline("What you produce is quite far from music..."); - else - You("start playing %s.", the(xname(instr))); + break; + } +#undef PLAY_NORMAL +#undef PLAY_STUNNED +#undef PLAY_CONFUSED +#undef PLAY_HALLU - switch (instr->otyp) { + switch (itmp.otyp) { /* note: itmp.otyp might differ from instr->otyp */ case MAGIC_FLUTE: /* Make monster fall asleep */ - if (do_spec && instr->spe > 0) { - consume_obj_charge(instr, TRUE); + consume_obj_charge(instr, TRUE); - You("produce %s music.", Hallucination ? "piped" : "soft"); - put_monsters_to_sleep(u.ulevel * 5); - exercise(A_DEX, TRUE); - break; - } /* else FALLTHRU */ + You("produce %s music.", Hallucination ? "piped" : "soft"); + put_monsters_to_sleep(u.ulevel * 5); + exercise(A_DEX, TRUE); + break; case WOODEN_FLUTE: /* May charm snakes */ do_spec &= (rn2(ACURR(A_DEX)) + u.ulevel > 25); pline("%s.", Tobjnam(instr, do_spec ? "trill" : "toot")); @@ -502,66 +533,59 @@ struct obj *instr; break; case FIRE_HORN: /* Idem wand of fire */ case FROST_HORN: /* Idem wand of cold */ - if (do_spec && instr->spe > 0) { - consume_obj_charge(instr, TRUE); + consume_obj_charge(instr, TRUE); - if (!getdir((char *) 0)) { - pline("%s.", Tobjnam(instr, "vibrate")); - break; - } else if (!u.dx && !u.dy && !u.dz) { - if ((damage = zapyourself(instr, TRUE)) != 0) { - char buf[BUFSZ]; - - Sprintf(buf, "using a magical horn on %sself", uhim()); - losehp(damage, buf, KILLED_BY); /* fire or frost damage */ - } - } else { - buzz((instr->otyp == FROST_HORN) ? AD_COLD - 1 : AD_FIRE - 1, - rn1(6, 6), u.ux, u.uy, u.dx, u.dy); - } - makeknown(instr->otyp); + if (!getdir((char *) 0)) { + pline("%s.", Tobjnam(instr, "vibrate")); break; - } /* else FALLTHRU */ + } else if (!u.dx && !u.dy && !u.dz) { + if ((damage = zapyourself(instr, TRUE)) != 0) { + char buf[BUFSZ]; + + Sprintf(buf, "using a magical horn on %sself", uhim()); + losehp(damage, buf, KILLED_BY); /* fire or frost damage */ + } + } else { + buzz((instr->otyp == FROST_HORN) ? AD_COLD - 1 : AD_FIRE - 1, + rn1(6, 6), u.ux, u.uy, u.dx, u.dy); + } + makeknown(instr->otyp); + break; case TOOLED_HORN: /* Awaken or scare monsters */ You("produce a frightful, grave sound."); awaken_monsters(u.ulevel * 30); exercise(A_WIS, FALSE); break; case BUGLE: /* Awaken & attract soldiers */ - You("extract a loud noise from %s.", the(xname(instr))); + You("extract a loud noise from %s.", yname(instr)); awaken_soldiers(&youmonst); exercise(A_WIS, FALSE); break; case MAGIC_HARP: /* Charm monsters */ - if (do_spec && instr->spe > 0) { - consume_obj_charge(instr, TRUE); + consume_obj_charge(instr, TRUE); - pline("%s very attractive music.", Tobjnam(instr, "produce")); - charm_monsters((u.ulevel - 1) / 3 + 1); - exercise(A_DEX, TRUE); - break; - } /* else FALLTHRU */ + pline("%s very attractive music.", Tobjnam(instr, "produce")); + charm_monsters((u.ulevel - 1) / 3 + 1); + exercise(A_DEX, TRUE); + break; case WOODEN_HARP: /* May calm Nymph */ do_spec &= (rn2(ACURR(A_DEX)) + u.ulevel > 25); - pline("%s %s.", The(xname(instr)), + pline("%s %s.", Yname2(instr), do_spec ? "produces a lilting melody" : "twangs"); if (do_spec) calm_nymphs(u.ulevel * 3); exercise(A_DEX, TRUE); break; case DRUM_OF_EARTHQUAKE: /* create several pits */ - if (do_spec && instr->spe > 0) { - consume_obj_charge(instr, TRUE); + consume_obj_charge(instr, TRUE); - You("produce a heavy, thunderous rolling!"); - pline_The("entire %s is shaking around you!", - generic_lvl_desc()); - do_earthquake((u.ulevel - 1) / 3 + 1); - /* shake up monsters in a much larger radius... */ - awaken_monsters(ROWNO * COLNO); - makeknown(DRUM_OF_EARTHQUAKE); - break; - } /* else FALLTHRU */ + You("produce a heavy, thunderous rolling!"); + pline_The("entire %s is shaking around you!", generic_lvl_desc()); + do_earthquake((u.ulevel - 1) / 3 + 1); + /* shake up monsters in a much larger radius... */ + awaken_monsters(ROWNO * COLNO); + makeknown(DRUM_OF_EARTHQUAKE); + break; case LEATHER_DRUM: /* Awaken monsters */ You("beat a deafening row!"); awaken_monsters(u.ulevel * 40); @@ -571,7 +595,7 @@ struct obj *instr; break; default: impossible("What a weird instrument (%d)!", instr->otyp); - break; + return 0; } return 2; /* That takes time */ } @@ -598,11 +622,13 @@ struct obj *instr; You("are incapable of playing %s.", the(distant_name(instr, xname))); return 0; } - if (instr->otyp != LEATHER_DRUM && instr->otyp != DRUM_OF_EARTHQUAKE) { + if (instr->otyp != LEATHER_DRUM && instr->otyp != DRUM_OF_EARTHQUAKE + && !(Stunned || Confusion || Hallucination)) { c = ynq("Improvise?"); if (c == 'q') goto nevermind; } + if (c == 'n') { if (u.uevent.uheard_tune == 2) c = ynq("Play the passtune?"); From 9f12aeb8abe3b91e9714e72b89d5a754007c211b Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 29 Dec 2017 16:20:05 -0800 Subject: [PATCH 5/9] uncursing prayer vs helm of opposite alignment Implement the suggestion that hero's current god not uncurse a worn helm of opposite alignment when prayer result is fix-worst-cursed-item or uncurse-all-cursed-items since doing so makes it easy for hero to switch to another god. The second boon will still uncurse non-worn helms of opposite alignment since that has no effect on how easy or hard it is for the hero to change alignments. (The first boon only applies to worn items plus luckstones and loadstones; non-worn helms aren't applicable.) --- doc/fixes36.1 | 5 ++++- src/pray.c | 30 ++++++++++++++++-------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 05216acec..3a3e33464 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -739,11 +739,14 @@ Master Key of Thievery always finds door and chest traps if used to lock or blessed (for non-rogues); player is offered the opportunity to disarm "Elbereth" must now be the only engraved text on a square to function "Elbereth" now erodes based on attacks by the player, not monsters scared -option herecmd_menu to make a mouse click on your character pop up +add option herecmd_menu to make a mouse click on your character pop up a context menu, and extended command #herecmdmenu to do the same change #adjust's behavior when collecting compatible stacks; that used to occur for any #adjust which lacked a split count, now it only happens when 'adjusting' into a stack's own inventory slot +a prayer result which results in uncursing some or all of the hero's items + won't uncurse a worn helm of opposite alignment since that would + facilitate the hero switching to another god by taking it off Platform- and/or Interface-Specific New Features diff --git a/src/pray.c b/src/pray.c index 51dfbb5bb..0e74bb98a 100644 --- a/src/pray.c +++ b/src/pray.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 pray.c $NHDT-Date: 1450577672 2015/12/20 02:14:32 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.89 $ */ +/* NetHack 3.6 pray.c $NHDT-Date: 1514593198 2017/12/30 00:19:58 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.95 $ */ /* Copyright (c) Benson I. Margulies, Mike Stephenson, Steve Linhart, 1989. */ /* NetHack may be freely redistributed. See license for details. */ @@ -265,18 +265,21 @@ worst_cursed_item() with taking off a ring or putting on a shield */ if (welded(uwep) && (uright || bimanual(uwep))) { /* weapon */ otmp = uwep; - /* gloves come next, due to rings */ + /* gloves come next, due to rings */ } else if (uarmg && uarmg->cursed) { /* gloves */ otmp = uarmg; - /* then shield due to two handed weapons and spells */ + /* then shield due to two handed weapons and spells */ } else if (uarms && uarms->cursed) { /* shield */ otmp = uarms; - /* then cloak due to body armor */ + /* then cloak due to body armor */ } else if (uarmc && uarmc->cursed) { /* cloak */ otmp = uarmc; } else if (uarm && uarm->cursed) { /* suit */ otmp = uarm; - } else if (uarmh && uarmh->cursed) { /* helmet */ + /* if worn helmet of opposite alignment is making you an adherent + of the current god, he/she/it won't uncurse that for you */ + } else if (uarmh && uarmh->cursed /* helmet */ + && uarmh->otyp != HELM_OF_OPPOSITE_ALIGNMENT) { otmp = uarmh; } else if (uarmf && uarmf->cursed) { /* boots */ otmp = uarmf; @@ -290,13 +293,13 @@ worst_cursed_item() otmp = uright; } else if (ublindf && ublindf->cursed) { /* eyewear */ otmp = ublindf; /* must be non-blinding lenses */ - /* if weapon wasn't handled above, do it now */ + /* if weapon wasn't handled above, do it now */ } else if (welded(uwep)) { /* weapon */ otmp = uwep; - /* active secondary weapon even though it isn't welded */ + /* active secondary weapon even though it isn't welded */ } else if (uswapwep && uswapwep->cursed && u.twoweap) { otmp = uswapwep; - /* all worn items ought to be handled by now */ + /* all worn items ought to be handled by now */ } else { for (otmp = invent; otmp; otmp = otmp->nobj) { if (!otmp->cursed) @@ -336,9 +339,7 @@ int trouble; break; case TROUBLE_LAVA: You("are back on solid ground."); - /* teleport should always succeed, but if not, - * just untrap them. - */ + /* teleport should always succeed, but if not, just untrap them */ if (!safe_teleds(FALSE)) u.utrap = 0; break; @@ -386,8 +387,7 @@ int trouble; if ((otmp = stuck_ring(uleft, RIN_SUSTAIN_ABILITY)) != 0) { if (otmp == uleft) what = leftglow; - } else if ((otmp = stuck_ring(uright, RIN_SUSTAIN_ABILITY)) - != 0) { + } else if ((otmp = stuck_ring(uright, RIN_SUSTAIN_ABILITY)) != 0) { if (otmp == uright) what = rightglow; } @@ -1070,7 +1070,9 @@ aligntyp g_align; else You("are surrounded by %s aura.", an(hcolor(NH_LIGHT_BLUE))); for (otmp = invent; otmp; otmp = otmp->nobj) { - if (otmp->cursed) { + if (otmp->cursed + && (otmp != uarmh /* [see worst_cursed_item()] */ + || uarmh->otyp != HELM_OF_OPPOSITE_ALIGNMENT)) { if (!Blind) { pline("%s %s.", Yobjnam2(otmp, "softly glow"), hcolor(NH_AMBER)); From 46da4b5e9045e66afef6cf1bbc8a47bc95ddd1e5 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 31 Dec 2017 03:38:29 -0800 Subject: [PATCH 6/9] fix #H6704 - appearance of mimic's replacement If mimics were genocided before loading a special level which contained mimics with specific appearances, whatever random monsters took their place also end up having their intended appearance. monst->cham uses NON_PM rather than 0 to mean "not a shapechanger". --- doc/fixes36.1 | 3 +++ src/sp_lev.c | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 3a3e33464..77e524f7d 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -494,6 +494,9 @@ hero polymorphed into form which can't wear armor via 'W' (eliciting "don't even bother") could wear it via 'P' make multi-shot missiles fired by monsters be less verbose when they miss can no longer play controlled notes on musical instrument if impaired +if a special level specified the appearance of a mimic and mimics had been + genocided prior to creating the level, whatever random monster took + the mimic's place got its intended appearance Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/sp_lev.c b/src/sp_lev.c index 5981a1b5e..170a6e251 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 sp_lev.c $NHDT-Date: 1513879435 2017/12/21 18:03:55 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.93 $ */ +/* NetHack 3.6 sp_lev.c $NHDT-Date: 1514720301 2017/12/31 11:38:21 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.94 $ */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -1593,7 +1593,7 @@ struct mkroom *croom; * eventually be expanded. */ if (m->appear_as.str - && ((mtmp->data->mlet == S_MIMIC) || mtmp->cham) + && ((mtmp->data->mlet == S_MIMIC) || mtmp->cham >= LOW_PM) && !Protection_from_shape_changers) { int i; From bb9738ff0ea3722b8cdac03bc712260a7cbbfb33 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 31 Dec 2017 17:19:38 -0800 Subject: [PATCH 7/9] special level mimics The special level loader would allow the level description to specify an alternate monster appearance for any type of monster, and if one was specified for a mimic then that mimic would be polymorphed into the appearance instead of masquerading as it. This changes it to only use an appearance for mimics, the Wizard, vampires, and general shapeshifters (chameleons, doppelgangers, sandestins). The mimic case doesn't work as expected: map display shows the symbol for the specified shape but farlook describes it as a mimic. The Wizard case hasn't been tested. The chameleon and vampshifter cases seem to work. It also allowed shapechangers (including vampires) to be given an object or furniture appearance. I didn't try things out to find out what what their behavior would be if/when that happened. I'm not sure whether the farlook issue for mimics-as-monsters is with the pager code or the monster name formatting code. (Possibly the mimic just needs to be flagged has 'hidden' as well has having an alternate appearance.) I'm not going to worry about it since none of our special levels attempt to give mimics a monster shape. Mimicking a monster is a feature for clones of the Wizard, not for mimics, although it might be nice if the latter worked correctly someday. --- include/extern.h | 3 ++- src/mon.c | 5 ++--- src/sp_lev.c | 35 ++++++++++++++++++++++++++++++----- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/include/extern.h b/include/extern.h index 6fd1df12f..6f355fa07 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1513130012 2017/12/13 01:53:32 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.621 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1514769568 2018/01/01 01:19:28 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.622 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1397,6 +1397,7 @@ E void FDECL(restore_cham, (struct monst *)); E boolean FDECL(hideunder, (struct monst *)); E void FDECL(hide_monst, (struct monst *)); E void FDECL(mon_animal_list, (BOOLEAN_P)); +E boolean FDECL(validvamp, (struct monst *, int *, int)); E int FDECL(select_newcham_form, (struct monst *)); E void FDECL(mgender_from_permonst, (struct monst *, struct permonst *)); E int FDECL(newcham, diff --git a/src/mon.c b/src/mon.c index a872b93a9..62fb34a8f 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mon.c $NHDT-Date: 1505266804 2017/09/13 01:40:04 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.244 $ */ +/* NetHack 3.6 mon.c $NHDT-Date: 1514769571 2018/01/01 01:19:31 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.246 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -23,7 +23,6 @@ STATIC_DCL void FDECL(kill_eggs, (struct obj *)); STATIC_DCL int FDECL(pickvampshape, (struct monst *)); STATIC_DCL boolean FDECL(isspecmon, (struct monst *)); STATIC_DCL boolean FDECL(validspecmon, (struct monst *, int)); -STATIC_DCL boolean FDECL(validvamp, (struct monst *, int *, int)); STATIC_DCL struct permonst *FDECL(accept_newcham_form, (int)); STATIC_DCL struct obj *FDECL(make_corpse, (struct monst *, unsigned)); STATIC_DCL void FDECL(m_detach, (struct monst *, struct permonst *)); @@ -3162,7 +3161,7 @@ int mndx; } /* prevent wizard mode user from specifying invalid vampshifter shape */ -STATIC_OVL boolean +boolean validvamp(mon, mndx_p, monclass) struct monst *mon; int *mndx_p, monclass; diff --git a/src/sp_lev.c b/src/sp_lev.c index 170a6e251..e2d362c6c 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 sp_lev.c $NHDT-Date: 1514720301 2017/12/31 11:38:21 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.94 $ */ +/* NetHack 3.6 sp_lev.c $NHDT-Date: 1514769572 2018/01/01 01:19:32 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.95 $ */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -1589,11 +1589,14 @@ struct mkroom *croom; mtmp = christen_monst(mtmp, m->name.str); /* - * This is currently hardwired for mimics only. It should - * eventually be expanded. + * This doesn't complain if an attempt is made to give a + * non-mimic/non-shapechanger an appearance or to give a + * shapechanger a non-monster shape, it just refuses to comply. */ if (m->appear_as.str - && ((mtmp->data->mlet == S_MIMIC) || mtmp->cham >= LOW_PM) + && ((mtmp->data->mlet == S_MIMIC) + /* shapechanger (chameleons, et al, and vampires) */ + || (mtmp->cham >= LOW_PM && m->appear == M_AP_MONSTER)) && !Protection_from_shape_changers) { int i; @@ -1658,7 +1661,29 @@ struct mkroom *croom; mndx = select_newcham_form(mtmp); else mndx = name_to_mon(m->appear_as.str); - if ((mndx != NON_PM) && (&mons[mndx] != mtmp->data)) { + + if (mndx == NON_PM || (is_vampshifter(mtmp) + && !validvamp(mtmp, &mndx, S_HUMAN))) { + impossible("create_monster: invalid %s (\"%s\")", + (mtmp->data->mlet == S_MIMIC) + ? "mimic appearance" + : (mtmp->data == &mons[PM_WIZARD_OF_YENDOR]) + ? "Wizard appearance" + : is_vampshifter(mtmp) + ? "vampire shape" + : "chameleon shape", + m->appear_as.str); + } else if (&mons[mndx] == mtmp->data) { + /* explicitly forcing a mimic to appear as itself */ + mtmp->m_ap_type = M_AP_NOTHING; + mtmp->mappearance = 0; + } else if (mtmp->data->mlet == S_MIMIC + || mtmp->data == &mons[PM_WIZARD_OF_YENDOR]) { + /* this is ordinarily only used for Wizard clones + and hasn't been exhaustively tested for mimics */ + mtmp->m_ap_type = M_AP_MONSTER; + mtmp->mappearance = mndx; + } else { /* chameleon or vampire */ struct permonst *mdat = &mons[mndx]; struct permonst *olddata = mtmp->data; From 66242a0691e41a3104a696a5087c2c95a9aa6a7f Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 1 Jan 2018 17:14:37 -0800 Subject: [PATCH 8/9] fix #H6707 - double "gush of water hits" messages When polymorphed into an iron golem (or gremlin with 2/3 chance), triggering a rust trap would give "a gush of water hits " and then give a second "a gush of water hits you" when dealing with golem or gremlin effects. That made it seem as if the trap was hitting twice. This removes the redundant messages. (Rust trap against monster iron golem or gremlin didn't have them.) --- doc/fixes36.1 | 1 + src/trap.c | 9 ++++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 77e524f7d..2b0c439e8 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -497,6 +497,7 @@ can no longer play controlled notes on musical instrument if impaired if a special level specified the appearance of a mimic and mimics had been genocided prior to creating the level, whatever random monster took the mimic's place got its intended appearance +redundant "hit by gush of water" message if poly'd into iron golem or gremlin Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/trap.c b/src/trap.c index 401d5a7fc..df0e1e883 100644 --- a/src/trap.c +++ b/src/trap.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 trap.c $NHDT-Date: 1494107206 2017/05/06 21:46:46 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.278 $ */ +/* NetHack 3.6 trap.c $NHDT-Date: 1514855666 2018/01/02 01:14:26 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.284 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1103,7 +1103,7 @@ unsigned trflags; if (uarmc) (void) water_damage(uarmc, cloak_simple_name(uarmc), TRUE); else if (uarm) - (void) water_damage(uarm, "armor", TRUE); + (void) water_damage(uarm, suit_simple_name(uarm), TRUE); else if (uarmu) (void) water_damage(uarmu, "shirt", TRUE); } @@ -1112,11 +1112,9 @@ unsigned trflags; if (u.umonnum == PM_IRON_GOLEM) { int dam = u.mhmax; - pline("%s you!", A_gush_of_water_hits); You("are covered with rust!"); losehp(Maybe_Half_Phys(dam), "rusting away", KILLED_BY); } else if (u.umonnum == PM_GREMLIN && rn2(3)) { - pline("%s you!", A_gush_of_water_hits); (void) split_mon(&youmonst, (struct monst *) 0); } @@ -2292,7 +2290,8 @@ register struct monst *mtmp; (void) water_damage(target, cloak_simple_name(target), TRUE); else if ((target = which_armor(mtmp, W_ARM)) != 0) - (void) water_damage(target, "armor", TRUE); + (void) water_damage(target, suit_simple_name(target), + TRUE); else if ((target = which_armor(mtmp, W_ARMU)) != 0) (void) water_damage(target, "shirt", TRUE); } From 0c5155584975731f2c37ace88acd046a54ae5aa6 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 5 Jan 2018 01:23:56 -0800 Subject: [PATCH 9/9] fix #H6713 - unpaid_cost: object not on any bill Stealing a shop object from outside the shop with a grappling hook would result in that item being left marked 'unpaid' after the shop's bill was treated as being bought and not yet paid for. This led to "unpaid_cost: object wasn't on any bill" every time inventory was examined. The problem was caused by handling the shop robbery after removing the object from the floor but before adding it to inventory, so it couldn't be found to have its unpaid bit cleared. When investigating this I came across a more severe bug: if the hero had never entered the shop, the shopkeeper's bill wasn't initialized properly and add_one_tobill() could crash while attempting to execute bp->bo_id = obj->o_id; because 'bp' was Null. --- doc/fixes36.1 | 5 +++++ src/pickup.c | 26 ++++++++++++++++++++------ src/shk.c | 8 +++++++- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 2b0c439e8..d33e86598 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -498,6 +498,11 @@ if a special level specified the appearance of a mimic and mimics had been genocided prior to creating the level, whatever random monster took the mimic's place got its intended appearance redundant "hit by gush of water" message if poly'd into iron golem or gremlin +a shop object stolen from outside the shop (via grappling hook) would be left + marked as 'unpaid' after the shop robbery took place, resulting in + "unpaid_cost: object wasn't on any bill" when looking at inventory +a shop object stolen from outside the shop could trigger a crash if that shop + had never been entered by the hero Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/pickup.c b/src/pickup.c index aa147080b..d09bbbfdb 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 pickup.c $NHDT-Date: 1508549438 2017/10/21 01:30:38 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.192 $ */ +/* NetHack 3.6 pickup.c $NHDT-Date: 1515144225 2018/01/05 09:23:45 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.193 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1495,15 +1495,30 @@ struct obj * pick_obj(otmp) struct obj *otmp; { + struct obj *result; + int ox = otmp->ox, oy = otmp->oy; + boolean robshop = (!u.uswallow && otmp != uball && costly_spot(ox, oy)); + obj_extract_self(otmp); - if (!u.uswallow && otmp != uball && costly_spot(otmp->ox, otmp->oy)) { + otmp->nomerge = 1; + result = addinv(otmp); + otmp->nomerge = 0; + newsym(ox, oy); + + /* this used to be done before addinv(), but remote_burglary() + calls rob_shop() which calls setpaid() after moving costs of + unpaid items to shop debt; setpaid() calls clear_unpaid() for + lots of object chains, but 'otmp' wasn't on any of those so + remained flagged as an unpaid item in inventory, triggering + impossible() every time inventory was examined... */ + if (robshop) { char saveushops[5], fakeshop[2]; /* addtobill cares about your location rather than the object's; usually they'll be the same, but not when using telekinesis (if ever implemented) or a grappling hook */ Strcpy(saveushops, u.ushops); - fakeshop[0] = *in_rooms(otmp->ox, otmp->oy, SHOPBASE); + fakeshop[0] = *in_rooms(ox, oy, SHOPBASE); fakeshop[1] = '\0'; Strcpy(u.ushops, fakeshop); /* sets obj->unpaid if necessary */ @@ -1511,10 +1526,9 @@ struct obj *otmp; Strcpy(u.ushops, saveushops); /* if you're outside the shop, make shk notice */ if (!index(u.ushops, *fakeshop)) - remote_burglary(otmp->ox, otmp->oy); + remote_burglary(ox, oy); } - newsym(otmp->ox, otmp->oy); - return addinv(otmp); /* might merge it with other objects */ + return result; } /* diff --git a/src/shk.c b/src/shk.c index 62052fbe7..c81c788b2 100644 --- a/src/shk.c +++ b/src/shk.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 shk.c $NHDT-Date: 1464138042 2016/05/25 01:00:42 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.132 $ */ +/* NetHack 3.6 shk.c $NHDT-Date: 1515144230 2018/01/05 09:23:50 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.136 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2354,6 +2354,12 @@ struct monst *shkp; return; } + /* normally bill_p gets set up whenever you enter the shop, but obj + might be going onto the bill because hero just snagged it with + a grappling hook from outside without ever having been inside */ + if (!eshkp->bill_p) + eshkp->bill_p = &(eshkp->bill[0]); + bct = eshkp->billct; bp = &(eshkp->bill_p[bct]); bp->bo_id = obj->o_id;