]> granicus.if.org Git - nethack/commitdiff
Monsters can revive corpses on floor with undead turning
authorPasi Kallinen <paxed@alt.org>
Fri, 11 Dec 2020 17:49:21 +0000 (19:49 +0200)
committerPasi Kallinen <paxed@alt.org>
Fri, 11 Dec 2020 17:49:25 +0000 (19:49 +0200)
... but only if the corpse is in direct line from the monster to hero

doc/fixes37.0
include/extern.h
src/mthrowu.c
src/muse.c

index 104142e2dee63fd050024c51d62f2ffc90a53619..cc5b3f9544383962b1d15cf84a8131d3338319c8 100644 (file)
@@ -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
index bbb2fd88d693cb5cdfd06b0f55a9dbeeff3c8979..cef35a4d8fe11ac0ac814b156dbf647d6c9bff86 100644 (file)
@@ -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));
index 2f21e543e8ae2e2d4c6baeb1c6e65e121e125eba..c5064e37d9c4f1a844cda54ff7d2cd5de038b030 100644 (file)
@@ -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 {
+            /* <bx,by> is guaranteed to eventually converge with <ax,ay> */
+            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;
index d82792d4a812301235688f558ec7df12025824c2..94401d05603cf268614962b73ee41a28580d7d7e 100644 (file)
@@ -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