]> granicus.if.org Git - nethack/commitdiff
github issue #475 revisited - Trollsbane
authorPatR <rankin@nethack.org>
Fri, 2 Apr 2021 17:38:57 +0000 (10:38 -0700)
committerPatR <rankin@nethack.org>
Fri, 2 Apr 2021 17:38:57 +0000 (10:38 -0700)
Change Trollsbane versus troll corpse revival:  instead of revival
failing if Trollsbane is wielded at time of revival attempt, mark
the corpse no-revive if killed by Trollsbane (whether by the hero
or a monster).

If a no-revive corpse is within view when time to revive occurs,
give "the troll corpse twitches feebly" even when the hero isn't
responsible.  That used to only apply if the hero zapped the
corpse with undead turning, which would have become inoperative
because now being zapped by undead turning clears the no-revive
flag and revives as normal.  In other words, undead turning magic
overrides killed-by-Trollsbane or non-ice troll having been in an
ice box.

include/decl.h
include/extern.h
include/monst.h
src/artifact.c
src/decl.c
src/dig.c
src/do.c
src/mhitm.c
src/mkobj.c
src/uhitm.c
src/zap.c

index 85f2d38fb50e4395549ce89f735bef046781ab1e..e432cdeffd683ad8c2e9646377e7a1444a9bafb1 100644 (file)
@@ -961,6 +961,9 @@ struct instance_globals {
     int xmin, ymin, xmax, ymax; /* level boundaries */
     boolean ransacked;
 
+    /* mkobj.c */
+    boolean mkcorpstat_norevive; /* for trolls */
+
     /* mon.c */
     boolean vamp_rise_msg;
     boolean disintegested;
index 25cb5dc06e61a9377d5262c84d7ffece5e381f5e..a42769579c3e61813e3557a573d8badd44dc3e8c 100644 (file)
@@ -98,7 +98,6 @@ extern void retouch_equipment(int);
 extern void mkot_trap_warn(void);
 extern boolean is_magic_key(struct monst *, struct obj *);
 extern struct obj *has_magic_key(struct monst *);
-extern boolean Trollsbane_wielded(void);
 
 /* ### attrib.c ### */
 
index 66f51f469fb05be27cb7414472ab132581fcce97..9b99a6ca6569195254f5df394f8c13659ab3a000 100644 (file)
@@ -210,6 +210,10 @@ struct monst {
 #define is_obj_mappear(mon,otyp) (M_AP_TYPE(mon) == M_AP_OBJECT \
                                   && (mon)->mappearance == (otyp))
 
+/* is mon m (presumably just killed) a troll and obj o Trollsbane? */
+#define troll_baned(m,o) \
+    ((m)->data->mlet == S_TROLL && (o) && (o)->oartifact == ART_TROLLSBANE)
+
 /* Get the maximum difficulty monsters that can currently be generated,
    given the current level difficulty and the hero's level. */
 #define monmax_difficulty(levdif) (((levdif) + u.ulevel) / 2)
index 47b3ca03d5f1a6acac964e961044f655e9e5707f..1202369e1fc32716546139942381ced32bfa03b2 100644 (file)
@@ -2176,25 +2176,4 @@ has_magic_key(struct monst *mon) /* if null, hero assumed */
     return (struct obj *) 0;
 }
 
-/* True if anyone on the level is wielding Trollsbane, False otherwise;
-   used to prevent troll resurrection (FIXME: really ought to be inhibited
-   when killed by Trollsbane rather than whether anyone wields that) */
-boolean
-Trollsbane_wielded(void)
-{
-    struct monst *mtmp;
-    struct obj *mw_tmp;
-
-    if (uwep && uwep->oartifact == ART_TROLLSBANE)
-        return TRUE;
-    for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
-        if (DEADMONSTER(mtmp))
-            continue;
-        if ((mw_tmp = MON_WEP(mtmp)) != 0
-            && mw_tmp->oartifact == ART_TROLLSBANE)
-            return TRUE;
-    }
-    return FALSE;
-}
-
 /*artifact.c*/
index da8402cd9e172908b47f3e5bbeffccb9db0c2702..ddeb071e9befad58124b696513a3d5b303815f21 100644 (file)
@@ -486,6 +486,9 @@ const struct instance_globals g_init = {
     UNDEFINED_VALUE, /* ymax */
     0, /* ransacked */
 
+    /* mkobj.c */
+    FALSE, /* mkcorpstat_norevive */
+
     /* mon.c */
     FALSE, /* vamp_rise_msg */
     FALSE, /* disintegested */
index b62a30d67870286c579bffe9f3dbee626203a993..16948cd7744060baa30e4b2c7def682e0a49bdbd 100644 (file)
--- a/src/dig.c
+++ b/src/dig.c
@@ -2022,7 +2022,8 @@ rot_corpse(anything *arg, long timeout)
         if (mtmp && !OBJ_AT(x, y) && mtmp->mundetected
             && hides_under(mtmp->data)) {
             mtmp->mundetected = 0;
-        } else if (x == u.ux && y == u.uy && u.uundetected && hides_under(g.youmonst.data))
+        } else if (x == u.ux && y == u.uy
+                   && u.uundetected && hides_under(g.youmonst.data))
             (void) hideunder(&g.youmonst);
         newsym(x, y);
     } else if (in_invent)
index fadc37ff83cef1bf5b6f5aac6b9edc4971074485..647225519a320549a2f235ba328a842cfe1b6182 100644 (file)
--- a/src/do.c
+++ b/src/do.c
@@ -1941,7 +1941,7 @@ revive_mon(anything *arg, long timeout UNUSED)
         } else { /* rot this corpse away */
             You_feel("%sless hassled.", is_rider(mptr) ? "much " : "");
             action = ROT_CORPSE;
-            when = 250L - (g.monstermoves - body->age);
+            when = (long) d(5, 50) - (g.monstermoves - body->age);
             if (when < 1L)
                 when = 1L;
         }
@@ -1950,15 +1950,13 @@ revive_mon(anything *arg, long timeout UNUSED)
 }
 
 /* Timeout callback. Revive the corpse as a zombie. */
-/*ARGSUSED*/
 void
-zombify_mon(anything *arg, long timeout UNUSED)
+zombify_mon(anything *arg, long timeout)
 {
     struct obj *body = arg->a_obj;
     int zmon = zombie_form(&mons[body->corpsenm]);
 
     if (zmon != NON_PM) {
-
         if (has_omid(body))
             free_omid(body);
         if (has_omonst(body))
@@ -1966,6 +1964,8 @@ zombify_mon(anything *arg, long timeout UNUSED)
 
         body->corpsenm = zmon;
         revive_mon(arg, timeout);
+    } else {
+        rot_corpse(arg, timeout);
     }
 }
 
index 2c3f2388ef7de05708e3bb5e66c6eff3ca2caf28..e6f990593ae641ed46666904d59a4cc4dbb551ca 100644 (file)
@@ -906,6 +906,8 @@ mdamagem(struct monst *magr, struct monst *mdef,
             place_monster(mdef, mdef->mx, mdef->my);
             mdef->mhp = 0;
         }
+        if (mattk->aatyp == AT_WEAP || mattk->aatyp == AT_CLAW)
+            g.mkcorpstat_norevive = troll_baned(mdef, mwep) ? TRUE : FALSE;
         g.zombify = (!mwep && zombie_maker(magr)
                      && (mattk->aatyp == AT_TUCH
                          || mattk->aatyp == AT_CLAW
@@ -913,6 +915,7 @@ mdamagem(struct monst *magr, struct monst *mdef,
                      && zombie_form(mdef->data) != NON_PM);
         monkilled(mdef, "", (int) mattk->adtyp);
         g.zombify = FALSE; /* reset */
+        g.mkcorpstat_norevive = FALSE;
         if (!DEADMONSTER(mdef))
             return mhm.hitflags; /* mdef lifesaved */
         else if (mhm.hitflags == MM_AGR_DIED)
index 50aa4c3ea1eb4c930ace684af0c45316398d9437..71295af3dd44dfbc7124e62bc34e28a459017d77 100644 (file)
@@ -1136,44 +1136,43 @@ void
 start_corpse_timeout(struct obj* body)
 {
     long when;       /* rot away when this old */
-    long corpse_age; /* age of corpse          */
+    long age;        /* age of corpse          */
     int rot_adjust;
     short action;
-    boolean no_revival;
 
-    /* if a troll corpse was frozen, it won't get a revive timer */
-    no_revival = (body->norevive != 0);
-    body->norevive = 0; /* always clear corpse's 'frozen' flag */
+    /*
+     * Note:
+     *      if body->norevive is set, the corpse will rot away instead
+     *      of revive when its REVIVE_MON timer finishes.
+     */
 
     /* lizards and lichen don't rot or revive */
     if (body->corpsenm == PM_LIZARD || body->corpsenm == PM_LICHEN)
         return;
 
-    action = ROT_CORPSE;             /* default action: rot away */
+    action = ROT_CORPSE;               /* default action: rot away */
     rot_adjust = g.in_mklev ? 25 : 10; /* give some variation */
-    corpse_age = g.monstermoves - body->age;
-    if (corpse_age > ROT_AGE)
+    age = g.monstermoves - body->age;
+    if (age > ROT_AGE)
         when = rot_adjust;
     else
-        when = ROT_AGE - corpse_age;
+        when = ROT_AGE - age;
     when += (long) (rnz(rot_adjust) - rot_adjust);
 
     if (is_rider(&mons[body->corpsenm])) {
         action = REVIVE_MON;
         when = rider_revival_time(body, FALSE);
-    } else if (mons[body->corpsenm].mlet == S_TROLL && !no_revival) {
-        long age;
-
+    } else if (mons[body->corpsenm].mlet == S_TROLL) {
         for (age = 2; age <= TAINT_AGE; age++)
             if (!rn2(TROLL_REVIVE_CHANCE)) { /* troll revives */
                 action = REVIVE_MON;
                 when = age;
                 break;
             }
-    } else if (!no_revival && g.zombify
-               && zombie_form(&mons[body->corpsenm]) != NON_PM) {
+    } else if (g.zombify && zombie_form(&mons[body->corpsenm]) != NON_PM
+               && !body->norevive) {
         action = ZOMBIFY_MON;
-        when = 5 + rn2(15);
+        when = rn1(15, 5); /* 5..19 */
     }
 
     (void) start_timer(when, TIMER_OBJECT, action, obj_to_any(body));
@@ -1463,10 +1462,10 @@ mkgold(long amount, int x, int y)
  */
 struct obj *
 mkcorpstat(
-    int objtype, /* CORPSE or STATUE */
-    struct monst *mtmp,
-    struct permonst *ptr,
-    int x, int y,
+    int objtype,          /* CORPSE or STATUE */
+    struct monst *mtmp,   /* dead monster, might be Null */
+    struct permonst *ptr, /* if non-Null, overrides mtmp->mndx */
+    int x, int y,         /* where to place corpse; <0,0> => random */
     unsigned corpstatflags)
 {
     struct obj *otmp;
@@ -1480,6 +1479,7 @@ mkcorpstat(
     } else {
         otmp = mksobj_at(objtype, x, y, init, FALSE);
     }
+    otmp->norevive = g.mkcorpstat_norevive;
 
     /* when 'mtmp' is non-null save the monster's details with the
        corpse or statue; it will also force the 'ptr' override below */
index ac55059b04f1d8af6a60e22b94e5c46eda6a6616..d16b41acc5a1452576ff5655c56d69e8c177b872 100644 (file)
@@ -1361,6 +1361,7 @@ hmon_hitmon(struct monst *mon,
     /* [note: thrown obj might go away during killed()/xkilled() call
        (via 'thrownobj'; if swallowed, it gets added to engulfer's
        minvent and might merge with a stack that's already there)] */
+    /* already_killed and poiskilled won't apply for Trollsbane */
 
     if (needpoismsg)
         pline_The("poison doesn't seem to affect %s.", mon_nam(mon));
@@ -1370,8 +1371,12 @@ hmon_hitmon(struct monst *mon,
             xkilled(mon, XKILL_NOMSG);
         destroyed = TRUE; /* return FALSE; */
     } else if (destroyed) {
-        if (!already_killed)
+        if (!already_killed) {
+            if (troll_baned(mon, obj))
+                g.mkcorpstat_norevive = TRUE;
             killed(mon); /* takes care of most messages */
+            g.mkcorpstat_norevive = FALSE;
+        }
     } else if (u.umconf && hand_to_hand) {
         nohandglow(mon);
         if (!mon->mconf && !resist(mon, SPBOOK_CLASS, 0, NOTELL)) {
@@ -3306,7 +3311,8 @@ mhitm_ad_phys(struct monst *magr, struct attack *mattk, struct monst *mdef,
                    || mattk->aatyp == AT_TUCH
                    || mattk->aatyp == AT_HUGS) {
             if (thick_skinned(pd))
-                mhm->damage = (mattk->aatyp == AT_KICK) ? 0 : (mhm->damage + 1) / 2;
+                mhm->damage = (mattk->aatyp == AT_KICK) ? 0
+                              : (mhm->damage + 1) / 2;
             /* add ring(s) of increase damage */
             if (u.udaminc > 0) {
                 /* applies even if damage was 0 */
@@ -3362,7 +3368,8 @@ mhitm_ad_phys(struct monst *magr, struct attack *mattk, struct monst *mdef,
                 if (mhm->damage <= 0)
                     mhm->damage = 1;
                 if (!(otmp->oartifact && artifact_hit(magr, mdef, otmp,
-                                                      &mhm->damage, g.mhitu_dieroll)))
+                                                      &mhm->damage,
+                                                      g.mhitu_dieroll)))
                     hitmsg(magr, mattk);
                 if (!mhm->damage)
                     return;
@@ -3434,7 +3441,8 @@ mhitm_ad_phys(struct monst *magr, struct attack *mattk, struct monst *mdef,
                    now we'll know and might need to deliver skipped message
                    (note: if there's no message there'll be no auxilliary
                    damage so the message here isn't coming too late) */
-                if (!artifact_hit(magr, mdef, mwep, &mhm->damage, mhm->dieroll)) {
+                if (!artifact_hit(magr, mdef, mwep, &mhm->damage,
+                                  mhm->dieroll)) {
                     if (g.vis)
                         pline("%s hits %s.", Monnam(magr),
                               mon_nam_too(mdef, magr));
@@ -3443,7 +3451,8 @@ mhitm_ad_phys(struct monst *magr, struct attack *mattk, struct monst *mdef,
                    damage; however, it might cause carried items to be
                    destroyed and they might do so */
                 if (DEADMONSTER(mdef)) {
-                    mhm->hitflags = (MM_DEF_DIED | (grow_up(magr, mdef) ? 0 : MM_AGR_DIED));
+                    mhm->hitflags = (MM_DEF_DIED | (grow_up(magr, mdef) ? 0
+                                                    : MM_AGR_DIED));
                     mhm->done = TRUE;
                     return;
                 }
@@ -4044,6 +4053,7 @@ damageum(
     int specialdmg) /* blessed and/or silver bonus against various things */
 {
     struct mhitm_data mhm;
+
     mhm.damage = d((int) mattk->damn, (int) mattk->damd);
     mhm.hitflags = MM_MISS;
     mhm.permdmg = 0;
@@ -4063,6 +4073,12 @@ damageum(
     mdef->mstrategy &= ~STRAT_WAITFORU; /* in case player is very fast */
     mdef->mhp -= mhm.damage;
     if (DEADMONSTER(mdef)) {
+        /* troll killed by Trollsbane won't auto-revive; FIXME? same when
+           Trollsbane is wielded as primary and two-weaponing kills with
+           secondary, which matches monster vs monster behavior but is
+           different from the non-poly'd hero vs monster behavior */
+        if (mattk->aatyp == AT_WEAP || mattk->aatyp == AT_CLAW)
+            g.mkcorpstat_norevive = troll_baned(mdef, uwep) ? TRUE : FALSE;
         /* (DEADMONSTER(mdef) and !mhm.damage => already killed) */
         if (mdef->mtame && !cansee(mdef->mx, mdef->my)) {
             You_feel("embarrassed for a moment.");
@@ -4075,6 +4091,7 @@ damageum(
         } else if (mhm.damage) {
             killed(mdef); /* regular "you kill <mdef>" message */
         }
+        g.mkcorpstat_norevive = FALSE;
         return MM_DEF_DIED;
     }
     return MM_HIT;
@@ -4393,7 +4410,8 @@ hmonas(struct monst *mon)
     struct attack *mattk, alt_attk;
     struct obj *weapon, **originalweapon;
     boolean altwep = FALSE, weapon_used = FALSE, odd_claw = TRUE;
-    int i, tmp, armorpenalty, sum[NATTK], nsum = MM_MISS, dhit = 0, attknum = 0;
+    int i, tmp, armorpenalty, sum[NATTK], nsum = MM_MISS,
+        dhit = 0, attknum = 0;
     int dieroll, multi_claw = 0;
 
     /* with just one touch/claw/weapon attack, both rings matter;
@@ -4425,7 +4443,8 @@ hmonas(struct monst *mon)
                get to make another weapon attack (note:  monsters who
                use weapons do not have this restriction, but they also
                never have the opportunity to use two weapons) */
-            if (weapon_used && (sum[i - 1] > MM_MISS) && uwep && bimanual(uwep))
+            if (weapon_used && (sum[i - 1] > MM_MISS)
+                && uwep && bimanual(uwep))
                 continue;
             /* Certain monsters don't use weapons when encountered as enemies,
              * but players who polymorph into them have hands or claws and
@@ -4674,7 +4693,8 @@ hmonas(struct monst *mon)
                 if (silverhit && flags.verbose)
                     silver_sears(&g.youmonst, mon, silverhit);
                 sum[i] = damageum(mon, mattk, specialdmg);
-            } else if (i >= 2 && (sum[i - 1] > MM_MISS) && (sum[i - 2] > MM_MISS)) {
+            } else if (i >= 2 && (sum[i - 1] > MM_MISS)
+                       && (sum[i - 2] > MM_MISS)) {
                 /* in case we're hugging a new target while already
                    holding something else; yields feedback
                    "<u.ustuck> is no longer in your clutches" */
index a2de95a5d7b5c0f1a27df8f67476a4131b81191c..ea78ea23dbe3c0802cea39d53bc2dc81f4ed074c 100644 (file)
--- a/src/zap.c
+++ b/src/zap.c
@@ -783,9 +783,9 @@ revive(struct obj *corpse, boolean by_hero)
             x = xy.x, y = xy.y;
     }
 
-    if ((mons[montype].mlet == S_EEL && !IS_POOL(levl[x][y].typ))
-        || (mons[montype].mlet == S_TROLL && Trollsbane_wielded())) {
-        if (by_hero && cansee(x, y))
+    if (corpse->norevive
+        || (mons[montype].mlet == S_EEL && !IS_POOL(levl[x][y].typ))) {
+        if (cansee(x, y))
             pline("%s twitches feebly.",
                 upstart(corpse_xname(corpse, (const char *) 0, CXN_PFX_THE)));
         return (struct monst *) 0;
@@ -961,6 +961,7 @@ unturn_dead(struct monst *mon)
     struct obj *otmp, *otmp2;
     struct monst *mtmp2;
     char owner[BUFSZ], corpse[BUFSZ];
+    unsigned save_norevive;
     boolean youseeit, different_type, is_u = (mon == &g.youmonst);
     int corpsenm, res = 0;
 
@@ -987,6 +988,10 @@ unturn_dead(struct monst *mon)
         /* for a stack, only one is revived; if is_u, revive() calls
            useup() which calls update_inventory() but not encumber_msg() */
         corpsenm = otmp->corpsenm;
+        /* norevive applies to revive timer, not to explicit unturn_dead() */
+        save_norevive = otmp->norevive;
+        otmp->norevive = 0;
+
         if ((mtmp2 = revive(otmp, !g.context.mon_moving)) != 0) {
             ++res;
             /* might get revived as a zombie rather than corpse's monster */
@@ -1005,9 +1010,13 @@ unturn_dead(struct monst *mon)
                 pline("%s%s suddenly %s%s%s!", owner, corpse,
                       nonliving(mtmp2->data) ? "reanimates" : "comes alive",
                       different_type ? " as " : "",
-                      different_type ? an(pmname(mtmp2->data, Mgender(mtmp2))) : "");
+                      different_type ? an(pmname(mtmp2->data, Mgender(mtmp2)))
+                                     : "");
             else if (canseemon(mtmp2))
                 pline("%s suddenly appears!", Amonnam(mtmp2));
+        } else {
+            /* revival failed; corpse 'otmp' is intact */
+            otmp->norevive = save_norevive ? 1 : 0;
         }
     }
     if (is_u && res)