]> granicus.if.org Git - nethack/commitdiff
Handle buried zombifying corpses
authorPasi Kallinen <paxed@alt.org>
Thu, 2 Sep 2021 17:54:32 +0000 (20:54 +0300)
committerPasi Kallinen <paxed@alt.org>
Thu, 2 Sep 2021 17:54:36 +0000 (20:54 +0300)
When a zombifying corpse is buried, allow it to zombify and
dig itself out of the ground.

Also allow wishing for zombifying corpses.

src/do.c
src/objnam.c
src/zap.c
test/testwish.lua

index ac3a7f0ea55f23ebc0d0555f7c98199208496e52..00b9b72b2d884ce3da49cbf893ba37d2cd8ee36e 100644 (file)
--- a/src/do.c
+++ b/src/do.c
@@ -1848,6 +1848,7 @@ revive_corpse(struct obj *corpse)
     char cname[BUFSZ];
     struct obj *container = (struct obj *) 0;
     int container_where = 0;
+    boolean is_zomb = (mons[corpse->corpsenm].mlet == S_ZOMBIE);
 
     where = corpse->where;
     is_uwep = (corpse == uwep);
@@ -1915,6 +1916,21 @@ revive_corpse(struct obj *corpse)
             }
             break;
         }
+        case OBJ_BURIED:
+            if (is_zomb) {
+                maketrap(mtmp->mx, mtmp->my, PIT);
+                if (cansee(mtmp->mx, mtmp->my)) {
+                    struct trap *ttmp;
+
+                    ttmp = t_at(mtmp->mx, mtmp->my);
+                    ttmp->tseen = TRUE;
+                    pline("%s claws itself out of the ground!", Amonnam(mtmp));
+                    newsym(mtmp->mx, mtmp->my);
+                } else if (distu(mtmp->mx, mtmp->my) < 5*5)
+                    You_hear("scratching noises.");
+                break;
+            }
+            /*FALLTHRU*/
         default:
             /* we should be able to handle the other cases... */
             impossible("revive_corpse: lost corpse @ %d", where);
@@ -1961,13 +1977,15 @@ revive_mon(anything *arg, long timeout UNUSED)
             action = REVIVE_MON;
             when = rider_revival_time(body, TRUE);
         } else { /* rot this corpse away */
-            You_feel("%sless hassled.", is_rider(mptr) ? "much " : "");
+            if (!obj_has_timer(body, ROT_CORPSE))
+                You_feel("%sless hassled.", is_rider(mptr) ? "much " : "");
             action = ROT_CORPSE;
             when = (long) d(5, 50) - (g.moves - body->age);
             if (when < 1L)
                 when = 1L;
         }
-        (void) start_timer(when, TIMER_OBJECT, action, arg);
+        if (!obj_has_timer(body, action))
+            (void) start_timer(when, TIMER_OBJECT, action, arg);
     }
 }
 
index bcf43ef49cf7337f18daa8404969a581dd9492a3..d8ea61c2b58a80c32ce9584ec7cf58800473c721 100644 (file)
@@ -27,6 +27,7 @@ struct _readobjnam_data {
     int tmp, tinv, tvariety, mgend;
     int wetness, gsize;
     int ftype;
+    boolean zombify;
     char globbuf[BUFSZ];
     char fruitbuf[BUFSZ];
 };
@@ -3317,6 +3318,7 @@ readobjnam_init(char *bp, struct _readobjnam_data *d)
     d->actualn = d->dn = d->un = 0;
     d->wetness = 0;
     d->gsize = 0;
+    d->zombify = FALSE;
     d->bp = d->origbp = bp;
     d->p = (char *) 0;
     d->name = (const char *) 0;
@@ -3432,6 +3434,8 @@ readobjnam_preparse(struct _readobjnam_data *d)
             d->looted = 1;
         } else if (!strncmpi(d->bp, "greased ", l = 8)) {
             d->isgreased = 1;
+        } else if (!strncmpi(d->bp, "zombifying ", l = 11)) {
+            d->zombify = TRUE;
         } else if (!strncmpi(d->bp, "very ", l = 5)) {
             /* very rusted very heavy iron ball */
             d->very = 1;
@@ -4499,6 +4503,10 @@ readobjnam(char *bp, struct obj *no_wish)
                     d.mntmp = genus(d.mntmp, 1);
                 set_corpsenm(d.otmp, d.mntmp);
             }
+            if (d.zombify && zombie_form(&mons[d.mntmp])) {
+                (void) start_timer(rn1(5, 10), TIMER_OBJECT,
+                                   ZOMBIFY_MON, obj_to_any(d.otmp));
+            }
             break;
         case EGG:
             d.mntmp = can_be_hatched(d.mntmp);
index bc57fb7b6dc85761bab2706ee98fcd692d3f9f31..245968e64c46c6f09b5fa8e97866beefac939bfe 100644 (file)
--- a/src/zap.c
+++ b/src/zap.c
@@ -12,6 +12,7 @@
  */
 #define MAGIC_COOKIE 1000
 
+static boolean zombie_can_dig(xchar x, xchar y);
 static void polyuse(struct obj *, int, int);
 static void create_polymon(struct obj *, int);
 static int stone_to_flesh_obj(struct obj *);
@@ -742,6 +743,22 @@ get_container_location(struct obj *obj, int *loc, int *container_nesting)
     return (struct monst *) 0;
 }
 
+/* can zombie dig the location at x,y */
+static boolean
+zombie_can_dig(xchar x, xchar y)
+{
+    if (isok(x,y)) {
+        schar typ = levl[x][y].typ;
+        struct trap *ttmp;
+
+        if ((ttmp = t_at(x, y)) != 0)
+            return FALSE;
+        if (typ == ROOM || typ == CORR || typ == GRAVE)
+            return TRUE;
+    }
+    return FALSE;
+}
+
 /*
  * Attempt to revive the given corpse, return the revived monster if
  * successful.  Note: this does NOT use up the corpse if it fails.
@@ -759,6 +776,7 @@ revive(struct obj *corpse, boolean by_hero)
     boolean one_of;
     long mmflags = NO_MINVENT | MM_NOWAIT;
     int montype, cgend, container_nesting = 0;
+    boolean is_zomb = (mons[corpse->corpsenm].mlet == S_ZOMBIE);
 
     if (corpse->otyp != CORPSE) {
         impossible("Attempting to revive %s?", xname(corpse));
@@ -767,9 +785,11 @@ revive(struct obj *corpse, boolean by_hero)
 
     x = y = 0;
     if (corpse->where != OBJ_CONTAINED) {
-        /* only for invent, minvent, or floor */
+        int locflags = is_zomb ? BURIED_TOO : 0;
+
+        /* only for invent, minvent, or floor, or if zombie, buried */
         container = 0;
-        (void) get_obj_location(corpse, &x, &y, 0);
+        (void) get_obj_location(corpse, &x, &y, locflags);
     } else {
         /* deal with corpses in [possibly nested] containers */
         struct monst *carrier;
@@ -804,6 +824,10 @@ revive(struct obj *corpse, boolean by_hero)
                           || (container->otyp == BAG_OF_HOLDING && rn2(40)))))
         return (struct monst *) 0;
 
+    /* buried zombie cannot dig itself out, do not revive */
+    if (is_zomb && corpse->where == OBJ_BURIED && !zombie_can_dig(x, y))
+        return (struct monst *) 0;
+
     /* record the object's location now that we're sure where it is */
     corpse->ox = x, corpse->oy = y;
 
@@ -977,6 +1001,13 @@ revive(struct obj *corpse, boolean by_hero)
         obj_extract_self(corpse);
         obfree(corpse, (struct obj *) 0);
         break;
+    case OBJ_BURIED:
+        if (is_zomb) {
+            obj_extract_self(corpse);
+            obfree(corpse, (struct obj *) 0);
+            break;
+        }
+        /*FALLTHRU*/
     default:
         panic("revive");
     }
index d28c82e3b2de0b0bfeb96a2831cd33f54a36014a..2fe5d869bc8b241800537024cc4af92a2bd9de91 100644 (file)
@@ -39,6 +39,8 @@ local wishtest_objects = {
    ["spinach"] = { otyp_name = "tin", oclass = "%", corpsenm = -1, spe = 1 },
    ["trapped tin of floating eye meat"] = { otyp_name = "tin", oclass = "%", otrapped = 1, corpsenm_name = "floating eye" },
    ["hill orc corpse"] = { otyp_name = "corpse", oclass = "%", corpsenm_name = "hill orc" },
+   -- TODO: zombifying and other timers cannot be seen via lua
+   ["zombifying elf corpse"] = { otyp_name = "corpse", oclass = "%", corpsenm_name = "elf" },
    ["destroy armor"] = { otyp_name = "destroy armor", oclass = "?" },
    ["enchant weapon"] = { otyp_name = "enchant weapon", oclass = "?" },
    ["scroll of food detection"] = { otyp_name = "food detection", oclass = "?" },