diff --git a/doc/fixes37.0 b/doc/fixes37.0 index c888f8353..6feb94b74 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.172 $ $NHDT-Date: 1586384219 2020/04/08 22:16:59 $ +$NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.175 $ $NHDT-Date: 1586633039 2020/04/11 19:23:59 $ General Fixes and Modified Features ----------------------------------- @@ -124,6 +124,8 @@ object taking erosion damage might give feedback message when out of view inventory when out-of-view ice melted] it's possible to wish for tins of the Riders in wizard mode; eating one is fatal but if you're life-saved or decline to die, the game crashed +revival via undead turning of corpse carried by hero said "your corpse + comes alive" even when revived monster was undead Fixes to 3.7.0-x Problems that Were Exposed Via git Repository diff --git a/src/muse.c b/src/muse.c index db124f0a3..ede7b9ed3 100644 --- a/src/muse.c +++ b/src/muse.c @@ -28,6 +28,7 @@ static struct permonst *FDECL(muse_newcham_mon, (struct monst *)); static int FDECL(mloot_container, (struct monst *mon, struct obj *, BOOLEAN_P)); static void FDECL(you_aggravate, (struct monst *)); +static boolean FDECL(necrophiliac, (struct obj *, BOOLEAN_P)); static void FDECL(mon_consume_unstone, (struct monst *, struct obj *, BOOLEAN_P, BOOLEAN_P)); static boolean FDECL(cures_stoning, (struct monst *, struct obj *, @@ -285,7 +286,7 @@ struct obj *otmp; #define MUSE_UNICORN_HORN 17 #define MUSE_POT_FULL_HEALING 18 #define MUSE_LIZARD_CORPSE 19 -#define MUSE_WAN_UNDEAD_TURNING 20 +#define MUSE_WAN_UNDEAD_TURNING 20 /* also an offensive item */ /* #define MUSE_INNATE_TPT 9999 * We cannot use this. Since monsters get unlimited teleportation, if they @@ -1092,6 +1093,8 @@ struct monst *mtmp; /*#define MUSE_WAN_TELEPORTATION 15*/ #define MUSE_POT_SLEEPING 16 #define MUSE_SCR_EARTH 17 +/*#define MUSE_WAN_UNDEAD_TURNING 20*/ /* also a defensive item so don't + * redefine; nonconsecutive value is ok */ /* Select an offensive item/action for a monster. Returns TRUE iff one is * found. @@ -1166,6 +1169,35 @@ struct monst *mtmp; g.m.has_offense = MUSE_WAN_MAGIC_MISSILE; } } + nomore(MUSE_WAN_UNDEAD_TURNING); + if (obj->otyp == WAN_UNDEAD_TURNING && obj->spe > 0 + /* not necrophiliac(); unlike deciding whether to pick this + type of wand up, we aren't interested in corpses within + carried containers until they're moved into open inventory; + we don't check whether hero is poly'd into an undead--the + wand's turning effect is too weak to be a useful direct + attack--only whether hero is carrying at least one corpse */ + && carrying(CORPSE)) { + /* + * Hero is carrying one or more corpses but isn't wielding + * a cockatrice corpse (unless being hit by one won't do + * the monster much harm); otherwise we'd be using this wand + * as a defensive item with higher priority. + * + * Might be cockatrice intended as a weapon (or being denied + * to glove-wearing monsters for use as a weapon) or lizard + * intended as a cure or lichen intended as veggy food or + * sacrifice fodder being lugged to an altar. Zapping with + * this will deprive hero of one from each stack although + * they might subsequently be recovered after killing again. + * In the sacrifice fodder case, it could even be to the + * player's advantage (fresher corpse if a new one gets + * dropped; player might not choose to spend a wand charge + * on that when/if hero acquires this wand). + */ + g.m.offensive = obj; + g.m.has_offense = MUSE_WAN_UNDEAD_TURNING; + } nomore(MUSE_WAN_STRIKING); if (obj->otyp == WAN_STRIKING && obj->spe > 0) { g.m.offensive = obj; @@ -1486,6 +1518,7 @@ struct monst *mtmp; g.m_using = FALSE; return (DEADMONSTER(mtmp)) ? 1 : 2; case MUSE_WAN_TELEPORTATION: + case MUSE_WAN_UNDEAD_TURNING: case MUSE_WAN_STRIKING: g.zap_oseen = oseen; mzapwand(mtmp, otmp, FALSE); @@ -2222,6 +2255,23 @@ struct monst *mtmp; return 0; } +/* check whether hero is carrying a corpse or contained petrifier corpse */ +static boolean +necrophiliac(objlist, any_corpse) +struct obj *objlist; +boolean any_corpse; +{ + while (objlist) { + if (objlist->otyp == CORPSE + && (any_corpse || touch_petrifies(&mons[objlist->corpsenm]))) + return TRUE; + if (Has_contents(objlist) && necrophiliac(objlist->cobj, FALSE)) + return TRUE; + objlist = objlist->nobj; + } + return FALSE; +} + boolean searches_for_item(mon, obj) struct monst *mon; @@ -2258,7 +2308,8 @@ struct obj *obj; || typ == WAN_TELEPORTATION || typ == WAN_CREATE_MONSTER) return TRUE; if (typ == WAN_UNDEAD_TURNING) - return carrying(CORPSE) || (Upolyd && is_undead(g.youmonst.data)); + return (necrophiliac(g.invent, TRUE) + || (Upolyd && is_undead(g.youmonst.data))); break; case POTION_CLASS: if (typ == POT_HEALING || typ == POT_EXTRA_HEALING diff --git a/src/zap.c b/src/zap.c index 998781488..52c65cb34 100644 --- a/src/zap.c +++ b/src/zap.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 zap.c $NHDT-Date: 1580322890 2020/01/29 18:34:50 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.330 $ */ +/* NetHack 3.6 zap.c $NHDT-Date: 1586633039 2020/04/11 19:23:59 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.335 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -852,6 +852,7 @@ boolean by_hero; if (one_of) /* could be simplified to ''corpse->quan = 1L;'' */ corpse->quan--; pline("%s glows iridescently.", upstart(buf)); + iflags.last_msg = PLNMSG_OBJ_GLOWS; /* usually for BUC change */ } else if (shkp) { /* need some prior description of the corpse since stolen_value() will refer to the object as "it" */ @@ -957,11 +958,11 @@ struct monst *mon; struct obj *otmp, *otmp2; struct monst *mtmp2; char owner[BUFSZ], corpse[BUFSZ]; - boolean youseeit; - int res = 0; + boolean youseeit, different_type, is_u = (mon == &g.youmonst); + int corpsenm, res = 0; - youseeit = (mon == &g.youmonst) ? TRUE : canseemon(mon); - otmp2 = (mon == &g.youmonst) ? g.invent : mon->minvent; + youseeit = is_u ? TRUE : canseemon(mon); + otmp2 = is_u ? g.invent : mon->minvent; owner[0] = corpse[0] = '\0'; /* lint suppression */ while ((otmp = otmp2) != 0) { @@ -972,20 +973,43 @@ struct monst *mon; continue; /* save the name; the object is liable to go away */ if (youseeit) { - Strcpy(corpse, - corpse_xname(otmp, (const char *) 0, CXN_SINGULAR)); - Shk_Your(owner, otmp); /* includes a trailing space */ + Strcpy(corpse, corpse_xname(otmp, (const char *) 0, CXN_NORMAL)); + /* shk_your/Shk_Your produces a value with a trailing space */ + if (otmp->quan > 1L) { + Strcpy(owner, "One of "); + (void) shk_your(eos(owner), otmp); + } else + (void) Shk_Your(owner, otmp); } - - /* for a stack, only one is revived */ + /* for a stack, only one is revived; if is_u, revive() calls + useup() which calls update_inventory() but not encumber_msg() */ + corpsenm = otmp->corpsenm; if ((mtmp2 = revive(otmp, !g.context.mon_moving)) != 0) { ++res; + /* might get revived as a zombie rather than corpse's monster */ + different_type = (mtmp2->data != &mons[corpsenm]); + if (iflags.last_msg == PLNMSG_OBJ_GLOWS) { + /* when hero zaps undead turning at self (or breaks + non-empty wand), revive() reports "[one of] your + corpse[s] glows iridescently"; override saved corpse + and owner names to say "It comes alive" [note: we did + earlier setup because corpse gets used up but need to + do the override here after revive() sets 'last_msg'] */ + Strcpy(corpse, "It"); + owner[0] = '\0'; + } if (youseeit) - pline("%s%s suddenly comes alive!", owner, corpse); + pline("%s%s suddenly %s%s%s!", owner, corpse, + nonliving(mtmp2->data) ? "reanimates" : "comes alive", + different_type ? " as " : "", + different_type ? an(mtmp2->data->mname) : ""); else if (canseemon(mtmp2)) pline("%s suddenly appears!", Amonnam(mtmp2)); } } + if (is_u && res) + (void) encumber_msg(); + return res; }