]> granicus.if.org Git - nethack/commitdiff
let monsters use wand of undead turning
authorPatR <rankin@nethack.org>
Sat, 11 Apr 2020 01:39:46 +0000 (18:39 -0700)
committerPatR <rankin@nethack.org>
Sat, 11 Apr 2020 01:39:46 +0000 (18:39 -0700)
when hero is wielding a cockatrice corpse.  Wands of undead turning
aren't generated as starting equipment but they will now be picked
up if come across while the hero is carrying any corpse, and used
in preference to any other item when carried and non-empty and hero
is wielding a petrifier's corpse.

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

index 6e602bac61e104a23410a59ebc79f58d954d7277..c888f83534b4eb8996b9aa35f98aab08086428cf 100644 (file)
@@ -251,6 +251,8 @@ for !fixinv option where inventory letters normally don't stick, try to put
        thrown from; only works if it does return and is successfully caught
 wizard mode #wizborn command
 include more skill information in ^X output when dual-wielding
+item-using monsters will zap wand of undead turning at corpse-wielding hero
+       when the corpse is harmful
 
 
 Platform- and/or Interface-Specific New Features
index a1f9ff99b877c61b711bc43d345d45d90a4fbd5f..fb6fb5ad5095fa8196d1296d15c2695e8b912518 100644 (file)
@@ -3120,6 +3120,7 @@ E struct monst *FDECL(get_container_location,
 E struct monst *FDECL(montraits, (struct obj *, coord *, BOOLEAN_P));
 E struct monst *FDECL(revive, (struct obj *, BOOLEAN_P));
 E int FDECL(unturn_dead, (struct monst *));
+E void NDECL(unturn_you);
 E void FDECL(cancel_item, (struct obj *));
 E boolean FDECL(drain_item, (struct obj *, BOOLEAN_P));
 E struct obj *FDECL(poly_obj, (struct obj *, int));
index 2662829957fad0e4be570cc6ae37f163f196c042..db124f0a3e6d48a79a731b4891dd0a23b37ffd63 100644 (file)
@@ -155,6 +155,10 @@ struct monst *mtmp;
 struct obj *otmp;
 boolean self;
 {
+    if (otmp->spe < 1) {
+        impossible("Mon zapping wand with %d charges?", otmp->spe);
+        return;
+    }
     if (!canseemon(mtmp)) {
         int range = couldsee(mtmp->mx, mtmp->my) /* 9 or 5 */
                        ? (BOLT_LIM + 1) : (BOLT_LIM - 3);
@@ -281,6 +285,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_INNATE_TPT 9999
  * We cannot use this.  Since monsters get unlimited teleportation, if they
@@ -320,10 +325,9 @@ struct monst *mtmp;
 {
     register struct obj *obj = 0;
     struct trap *t;
-    int x = mtmp->mx, y = mtmp->my;
-    boolean stuck = (mtmp == u.ustuck);
-    boolean immobile = (mtmp->data->mmove == 0);
-    int fraction;
+    int fraction, x = mtmp->mx, y = mtmp->my;
+    boolean stuck = (mtmp == u.ustuck),
+            immobile = (mtmp->data->mmove == 0);
 
     if (is_animal(mtmp->data) || mindless(mtmp->data))
         return FALSE;
@@ -386,6 +390,24 @@ struct monst *mtmp;
             return TRUE;
     }
 
+    /* monsters aren't given wands of undead turning but if they
+       happen to have picked one up, use it against corpse wielder;
+       when applicable, use it now even if 'mtmp' isn't wounded */
+    if (!mtmp->mpeaceful && !nohands(mtmp->data)
+        && uwep && uwep->otyp == CORPSE
+        && touch_petrifies(&mons[uwep->corpsenm])
+        && !poly_when_stoned(mtmp->data) && !resists_ston(mtmp)
+        && lined_up(mtmp)) { /* only lines up if distu range is within 5*5 */
+        /* could use m_carrying(), then nxtobj() when matching wand
+           is empty, but direct traversal is actually simpler here */
+        for (obj = mtmp->minvent; obj; obj = obj->nobj)
+            if (obj->otyp == WAN_UNDEAD_TURNING && obj->spe > 0) {
+                g.m.defensive = obj;
+                g.m.has_defense = MUSE_WAN_UNDEAD_TURNING;
+                return TRUE;
+            }
+    }
+
     fraction = u.ulevel < 10 ? 5 : u.ulevel < 14 ? 4 : 3;
     if (mtmp->mhp >= mtmp->mhpmax
         || (mtmp->mhp >= 10 && mtmp->mhp * fraction >= mtmp->mhpmax))
@@ -768,14 +790,19 @@ struct monst *mtmp;
                          (coord *) 0);
         return 2;
     }
+    case MUSE_WAN_UNDEAD_TURNING:
+        g.zap_oseen = oseen;
+        mzapwand(mtmp, otmp, FALSE);
+        g.m_using = TRUE;
+        mbhit(mtmp, rn1(8, 6), mbhitm, bhito, otmp);
+        g.m_using = FALSE;
+        return 2;
     case MUSE_WAN_CREATE_MONSTER: {
         coord cc;
-        /* pm: 0 => random, eel => aquatic, croc => amphibious */
-        struct permonst *pm =
-            !is_pool(mtmp->mx, mtmp->my)
-                ? 0
-                : &mons[u.uinwater ? PM_GIANT_EEL : PM_CROCODILE];
         struct monst *mon;
+        /* pm: 0 => random, eel => aquatic, croc => amphibious */
+        struct permonst *pm = !is_pool(mtmp->mx, mtmp->my) ? 0
+                            : &mons[u.uinwater ? PM_GIANT_EEL : PM_CROCODILE];
 
         if (!enexto(&cc, mtmp->mx, mtmp->my, pm))
             return 0;
@@ -1225,9 +1252,9 @@ register struct monst *mtmp;
 register struct obj *otmp;
 {
     int tmp;
-    boolean reveal_invis = FALSE;
+    boolean reveal_invis = FALSE, hits_you = (mtmp == &g.youmonst);
 
-    if (mtmp != &g.youmonst) {
+    if (!hits_you && otmp->otyp != WAN_UNDEAD_TURNING) {
         mtmp->msleeping = 0;
         if (mtmp->m_ap_type)
             seemimic(mtmp);
@@ -1235,9 +1262,7 @@ register struct obj *otmp;
     switch (otmp->otyp) {
     case WAN_STRIKING:
         reveal_invis = TRUE;
-        if (mtmp == &g.youmonst) {
-            if (g.zap_oseen)
-                makeknown(WAN_STRIKING);
+        if (hits_you) {
             if (Antimagic) {
                 shieldeff(u.ux, u.uy);
                 pline("Boing!");
@@ -1268,10 +1293,10 @@ register struct obj *otmp;
         break;
 #if 0   /* disabled because find_offensive() never picks WAN_TELEPORTATION */
     case WAN_TELEPORTATION:
-        if (mtmp == &g.youmonst) {
+        if (hits_you) {
+            tele();
             if (g.zap_oseen)
                 makeknown(WAN_TELEPORTATION);
-            tele();
         } else {
             /* for consistency with zap.c, don't identify */
             if (mtmp->ispriest && *in_rooms(mtmp->mx, mtmp->my, TEMPLE)) {
@@ -1286,12 +1311,42 @@ register struct obj *otmp;
     case SPE_CANCELLATION:
         (void) cancel_monst(mtmp, otmp, FALSE, TRUE, FALSE);
         break;
+    case WAN_UNDEAD_TURNING: {
+        boolean learnit = FALSE;
+
+        if (hits_you) {
+            unturn_you();
+            learnit = g.zap_oseen;
+        } else {
+            boolean wake = FALSE;
+
+            if (unturn_dead(mtmp)) /* affects mtmp's invent, not mtmp */
+                wake = TRUE;
+            if (is_undead(mtmp->data) || is_vampshifter(mtmp)) {
+                wake = reveal_invis = TRUE;
+                /* context.bypasses=True: if resist() happens to be fatal,
+                   make_corpse() will set obj->bypass on the new corpse
+                   so that mbhito() will skip it instead of reviving it */
+                g.context.bypasses = TRUE; /* for make_corpse() */
+                (void) resist(mtmp, WAND_CLASS, rnd(8), NOTELL);
+            }
+            if (wake) {
+                if (!DEADMONSTER(mtmp))
+                    wakeup(mtmp, FALSE);
+                learnit = g.zap_oseen;
+            }
+        }
+        if (learnit)
+            makeknown(WAN_UNDEAD_TURNING);
+        break;
     }
-    if (reveal_invis) {
-        if (!DEADMONSTER(mtmp) && cansee(g.bhitpos.x, g.bhitpos.y)
-            && !canspotmon(mtmp))
-            map_invisible(g.bhitpos.x, g.bhitpos.y);
+    default:
+        break;
     }
+    if (reveal_invis && !DEADMONSTER(mtmp)
+        && cansee(g.bhitpos.x, g.bhitpos.y) && !canspotmon(mtmp))
+        map_invisible(g.bhitpos.x, g.bhitpos.y);
+
     return 0;
 }
 
@@ -2175,8 +2230,8 @@ struct obj *obj;
     int typ = obj->otyp;
 
     /* don't let monsters interact with protected items on the floor */
-    if ((obj->where == OBJ_FLOOR)
-        && (obj->ox == mon->mx) && (obj->oy == mon->my)
+    if (obj->where == OBJ_FLOOR
+        && (obj->ox == mon->mx && obj->oy == mon->my)
         && onscary(obj->ox, obj->oy, mon)) {
         return FALSE;
     }
@@ -2202,6 +2257,8 @@ struct obj *obj;
         if (objects[typ].oc_dir == RAY || typ == WAN_STRIKING
             || typ == WAN_TELEPORTATION || typ == WAN_CREATE_MONSTER)
             return TRUE;
+        if (typ == WAN_UNDEAD_TURNING)
+            return carrying(CORPSE) || (Upolyd && is_undead(g.youmonst.data));
         break;
     case POTION_CLASS:
         if (typ == POT_HEALING || typ == POT_EXTRA_HEALING
index dd7ee93fabf862097dbdfd5507ef1b9dfef7c472..9987814886e883d9d2fbe4c31e0a7635ace6f592 100644 (file)
--- a/src/zap.c
+++ b/src/zap.c
@@ -989,6 +989,19 @@ struct monst *mon;
     return res;
 }
 
+void
+unturn_you()
+{
+    (void) unturn_dead(&g.youmonst); /* hit carried corpses and eggs */
+
+    if (is_undead(g.youmonst.data)) {
+        You_feel("frightened and %sstunned.", Stunned ? "even more " : "");
+        make_stunned((HStun & TIMEOUT) + (long) rnd(30), FALSE);
+    } else {
+        You("shudder in dread.");
+    }
+}
+
 /* cancel obj, possibly carried by you or a monster */
 void
 cancel_item(obj)
@@ -1998,6 +2011,7 @@ struct obj *obj, *otmp;
             } else if (obj->otyp == CORPSE) {
                 struct monst *mtmp;
                 xchar ox, oy;
+                boolean by_u = !g.context.mon_moving;
                 int corpsenm = corpse_revive_type(obj);
                 char *corpsname = cxname_singular(obj);
 
@@ -2013,7 +2027,7 @@ struct obj *obj, *otmp;
                         if (canspotmon(mtmp)) {
                             pline("%s is resurrected!",
                                   upstart(noname_monnam(mtmp, ARTICLE_THE)));
-                            learn_it = TRUE;
+                            learn_it = by_u ? TRUE : g.zap_oseen;
                         } else {
                             /* saw corpse but don't see monster: maybe
                                mtmp is invisible, or has been placed at
@@ -2032,7 +2046,7 @@ struct obj *obj, *otmp;
                                 You_hear("%s reviving.", corpsname);
                             else
                                 You_hear("a defibrillator.");
-                            learn_it = TRUE;
+                            learn_it = by_u ? TRUE : g.zap_oseen;
                         }
                         if (canspotmon(mtmp))
                             /* didn't see corpse but do see monster: it
@@ -2479,13 +2493,7 @@ boolean ordinary;
     case WAN_UNDEAD_TURNING:
     case SPE_TURN_UNDEAD:
         learn_it = TRUE;
-        (void) unturn_dead(&g.youmonst);
-        if (is_undead(g.youmonst.data)) {
-            You_feel("frightened and %sstunned.",
-                     Stunned ? "even more " : "");
-            make_stunned((HStun & TIMEOUT) + (long) rnd(30), FALSE);
-        } else
-            You("shudder in dread.");
+        unturn_you();
         break;
     case SPE_HEALING:
     case SPE_EXTRA_HEALING: