From: Pasi Kallinen Date: Fri, 11 Dec 2020 17:49:21 +0000 (+0200) Subject: Monsters can revive corpses on floor with undead turning X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ea93c17fa70c7d1f0ceecfeed2107dcac96fc0eb;p=nethack Monsters can revive corpses on floor with undead turning ... but only if the corpse is in direct line from the monster to hero --- diff --git a/doc/fixes37.0 b/doc/fixes37.0 index 104142e2d..cc5b3f954 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -324,6 +324,8 @@ brown pudding monster hitting another monster with decay attack corroded armor and ^A/re-do was suppressed due lack of obsolete '#define REDO' add missing key binding support for rush.numpad; default is M-5 for numpad==1 or plain 5 for numpad==2 where behavior of 5 and M-5 are swapped +allow monsters to use wand of undead turning to revive corpses on floor + in some situations Fixes to 3.7.0-x Problems that Were Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index bbb2fd88d..cef35a4d8 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1683,6 +1683,7 @@ E int FDECL(ohitmon, (struct monst *, struct obj *, int, BOOLEAN_P)); E void FDECL(thrwmu, (struct monst *)); E int FDECL(spitmu, (struct monst *, struct attack *)); E int FDECL(breamu, (struct monst *, struct attack *)); +E boolean FDECL(linedup_callback, (XCHAR_P, XCHAR_P, XCHAR_P, XCHAR_P, boolean FDECL((*), (int,int)))); E boolean FDECL(linedup, (XCHAR_P, XCHAR_P, XCHAR_P, XCHAR_P, int)); E boolean FDECL(lined_up, (struct monst *)); E struct obj *FDECL(m_carrying, (struct monst *, int)); diff --git a/src/mthrowu.c b/src/mthrowu.c index 2f21e543e..c5064e37d 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -969,6 +969,43 @@ struct attack *mattk; return breamm(mtmp, mattk, &g.youmonst); } +/* Move from (ax,ay) to (bx,by), but only if distance is up to BOLT_LIM + and only in straight line or diagonal, calling fnc for each step. + Stops if fnc return TRUE, or if step was blocked by wall or closed door. + Returns TRUE if fnc returned TRUE. */ +boolean +linedup_callback(ax, ay, bx, by, fnc) +xchar ax, ay, bx, by; +boolean FDECL((*fnc), (int, int)); +{ + int dx, dy; + + /* These two values are set for use after successful return. */ + g.tbx = ax - bx; + g.tby = ay - by; + + /* sometimes displacement makes a monster think that you're at its + own location; prevent it from throwing and zapping in that case */ + if (!g.tbx && !g.tby) + return FALSE; + + if ((!g.tbx || !g.tby || abs(g.tbx) == abs(g.tby)) /* straight line or diagonal */ + && distmin(g.tbx, g.tby, 0, 0) < BOLT_LIM) { + dx = sgn(ax - bx), dy = sgn(ay - by); + do { + /* is guaranteed to eventually converge with */ + bx += dx, by += dy; + if (!isok(bx, by)) + return FALSE; + if (IS_ROCK(levl[bx][by].typ) || closed_door(bx, by)) + return FALSE; + if ((*fnc)(bx, by)) + return TRUE; + } while (bx != ax || by != ay); + } + return FALSE; +} + boolean linedup(ax, ay, bx, by, boulderhandling) register xchar ax, ay, bx, by; diff --git a/src/muse.c b/src/muse.c index d82792d4a..94401d056 100644 --- a/src/muse.c +++ b/src/muse.c @@ -20,6 +20,8 @@ static void FDECL(mplayhorn, (struct monst *, struct obj *, BOOLEAN_P)); static void FDECL(mreadmsg, (struct monst *, struct obj *)); static void FDECL(mquaffmsg, (struct monst *, struct obj *)); static boolean FDECL(m_use_healing, (struct monst *)); +static boolean FDECL(linedup_chk_corpse, (int, int)); +static void FDECL(m_use_undead_turning, (struct monst *, struct obj *)); static int FDECL(mbhitm, (struct monst *, struct obj *)); static void FDECL(mbhit, (struct monst *, int, int FDECL((*), (MONST_P, OBJ_P)), @@ -1134,6 +1136,60 @@ struct monst *mtmp; /*#define MUSE_WAN_UNDEAD_TURNING 20*/ /* also a defensive item so don't * redefine; nonconsecutive value is ok */ +static boolean +linedup_chk_corpse(x, y) +int x, y; +{ + return (sobj_at(CORPSE, x, y) != 0); +} + +static void +m_use_undead_turning(mtmp, obj) +struct monst *mtmp; +struct obj *obj; +{ + int ax = u.ux + sgn(mtmp->mux - mtmp->mx) * 3, + ay = u.uy + sgn(mtmp->muy - mtmp->my) * 3; + int bx = mtmp->mx, by = mtmp->my; + + if (!(obj->otyp == WAN_UNDEAD_TURNING && obj->spe > 0)) + return; + + /* 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 */ + if (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; + } else if (linedup_callback(ax, ay, bx, by, linedup_chk_corpse)) { + /* There's a corpse on the ground in a direct line from the + * monster to the hero, and up to 3 steps beyond. + */ + g.m.offensive = obj; + g.m.has_offense = MUSE_WAN_UNDEAD_TURNING; + } +} + /* Select an offensive item/action for a monster. Returns TRUE iff one is * found. */ @@ -1208,34 +1264,7 @@ struct monst *mtmp; } } 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; - } + m_use_undead_turning(mtmp, obj); nomore(MUSE_WAN_STRIKING); if (obj->otyp == WAN_STRIKING && obj->spe > 0) { g.m.offensive = obj; @@ -2342,11 +2371,9 @@ struct obj *obj; if (typ == WAN_POLYMORPH) return (boolean) (mons[monsndx(mon->data)].difficulty < 6); if (objects[typ].oc_dir == RAY || typ == WAN_STRIKING + || typ == WAN_UNDEAD_TURNING || typ == WAN_TELEPORTATION || typ == WAN_CREATE_MONSTER) return TRUE; - if (typ == WAN_UNDEAD_TURNING) - return (necrophiliac(g.invent, TRUE) - || (Upolyd && is_undead(g.youmonst.data))); break; case POTION_CLASS: if (typ == POT_HEALING || typ == POT_EXTRA_HEALING