]> granicus.if.org Git - nethack/commitdiff
implement #H4340 - indirect kills vs pacifism
authorPatR <rankin@nethack.org>
Sat, 14 May 2016 23:57:56 +0000 (16:57 -0700)
committerPatR <rankin@nethack.org>
Sat, 14 May 2016 23:57:56 +0000 (16:57 -0700)
Implement the suggestion that a monster killing itself with acid
to avoid turning to stone or with fire to avoid turning into green
slime not break pacifist conduct even if the player caused the
"turning into" situation that triggered the accidental suicide.

Along the way I discovered a serious bug:  zhitm() applies damage
to target monster but leaves it to caller to finish killing off
that monster when damage is fatal, but muse_unslime() called it
without checking whether the monster should die.  For fire breath
that shouldn't matter since all fire breathers are immune to fire
damage, but when support for wands of fire and fire horns was
added later it just cloned the fire breath code and neglected to
check for fatal damage.  The result was that a monster with 0 HP
would be left on the map, then impossible "dmonsfree: 1 removed
doesn't match 0 pending" would be given when taking it off fmon
list, but a stale monster symbol (presumably level.monsters[][]
pointer too) was left on the map which eventually led to monsndx
panic or arbitrary crash.

doc/fixes36.1
src/explode.c
src/muse.c

index f723e03eae01f1e47aa225e10b30781de20f8887..6940995aacd7ce042b447721e62f7a974d50c097 100644 (file)
@@ -234,6 +234,8 @@ effects of cursed potion of levitation were skipped if already levitating
 when engulfed, having swallower be killed by angry deity trying to zap hero
        no longer violates pacifist conduct (other penalties--reduced luck or
        alignment--still apply if target is something you shouldn't kill)
+likewise when a monster kills inself trying to prevent turning to stone or
+       into slime that's been caused by the player, pacifism is not affected
 metabolism adjustments: hero poly'd into metallivore form still needs to eat;
        being fainted or unconscious from other than sleep now consumes
        nutrition at lower rate, like being asleep already did;
@@ -249,6 +251,10 @@ farlook when underwater now reports "land" for adjacent non-{water,lava,ice}
        and "unreconnoitered" for non-adjacent anything, instead of "dark part
        of a room" for the former and either dark-room or "unexplored" for the
        latter depending upon whether the spot had previously been scouted
+monster who accidentally killed itself by zapping wand of fire or fire horn
+       at self to prevent turning into slime was not properly killed off;
+       it wouldn't benefit from an amulet of life saving and would trigger
+       impossible "dmonsfree: N removed doesn't match M pending"
 
 
 Fixes to Post-3.6.0 Problems that Were Exposed Via git Respository
index 3a3b35597d8f4a53dad76d3bdc7d1838d284b579..e6f85866bfe374a762c02f1c4356ee15af21a018 100644 (file)
@@ -40,8 +40,7 @@ int expltype;
     int idamres, idamnonres;
     struct monst *mtmp, *mdef = 0;
     uchar adtyp;
-    int explmask[3][3];
-    /* 0=normal explosion, 1=do shieldeff, 2=do nothing */
+    int explmask[3][3]; /* 0=normal explosion, 1=do shieldeff, 2=do nothing */
     boolean shopdamage = FALSE, generic = FALSE, physical_dmg = FALSE,
             do_hallu = FALSE, inside_engulfer;
     char hallu_buf[BUFSZ];
@@ -414,9 +413,23 @@ int expltype;
                     mtmp->mhp -= (idamres + idamnonres);
                 }
                 if (mtmp->mhp <= 0) {
-                    if (mdef ? (mtmp == mdef) : !context.mon_moving)
+                    if (!context.mon_moving) {
                         killed(mtmp);
-                    else
+                    } else if (mdef && mtmp == mdef) {
+                        /* 'mdef' killed self trying to cure being turned
+                         * into slime due to some action by the player.
+                         * Hero gets the credit (experience) and most of
+                         * the blame (possible loss of alignment and/or
+                         * luck and/or telepathy depending on mtmp) but
+                         * doesn't break pacifism.  xkilled()'s message
+                         * would be "you killed <mdef>" so give our own.
+                         */
+                        if (cansee(mtmp->mx, mtmp->my) || canspotmon(mtmp))
+                            pline("%s is %s!", Monnam(mtmp),
+                                  nonliving(mtmp->data) ? "destroyed"
+                                                        : "killed");
+                        xkilled(mtmp, XKILL_NOMSG | XKILL_NOCONDUCT);
+                    } else
                         monkilled(mtmp, "", (int) adtyp);
                 } else if (!context.mon_moving) {
                     /* all affected monsters, even if mdef is set */
index f0b8bbb7081929c60136f6af5ca416e8555e6ea3..1483273fb4889ef6ff9b43c6934ef73017c77b44 100644 (file)
@@ -2201,7 +2201,7 @@ mon_consume_unstone(mon, obj, by_you, stoning)
 struct monst *mon;
 struct obj *obj;
 boolean by_you;
-boolean stoning;
+boolean stoning; /* True: stop petrification, False: cure stun && confusion */
 {
     boolean vis = canseemon(mon), tinned = obj->otyp == TIN,
             food = obj->otyp == CORPSE || tinned,
@@ -2240,7 +2240,10 @@ boolean stoning;
         if (mon->mhp <= 0) {
             pline("%s dies!", Monnam(mon));
             if (by_you)
-                xkilled(mon, XKILL_NOMSG);
+                /* hero gets credit (experience) and blame (possible loss
+                   of alignment and/or luck and/or telepathy depending on
+                   mon) for the kill but does not break pacifism conduct */
+                xkilled(mon, XKILL_NOMSG | XKILL_NOCONDUCT);
             else
                 mondead(mon);
             return;
@@ -2404,7 +2407,7 @@ struct trap *trap;
 boolean by_you; /* true: if mon kills itself, hero gets credit/blame */
 {               /* [by_you not honored if 'mon' triggers fire trap]. */
     struct obj *odummyp;
-    int otyp = obj->otyp, dmg;
+    int otyp = obj->otyp, dmg = 0;
     boolean vis = canseemon(mon), res = TRUE;
 
     if (vis)
@@ -2443,7 +2446,7 @@ boolean by_you; /* true: if mon kills itself, hero gets credit/blame */
         if (!rn2(3))
             mon->mspec_used = rn1(10, 5);
         /* -21 => monster's fire breath; 1 => # of damage dice */
-        (void) zhitm(mon, by_you ? 21 : -21, 1, &odummyp);
+        dmg = zhitm(mon, by_you ? 21 : -21, 1, &odummyp);
     } else if (otyp == SCR_FIRE) {
         mreadmsg(mon, obj);
         if (mon->mconf) {
@@ -2462,14 +2465,37 @@ boolean by_you; /* true: if mon kills itself, hero gets credit/blame */
             explode(mon->mx, mon->my, -11, dmg, SCROLL_CLASS,
                     /* by_you: override -11 for mon but not others */
                     by_you ? -EXPL_FIERY : EXPL_FIERY);
+            dmg = 0; /* damage has been applied by explode() */
         }
     } else { /* wand/horn of fire w/ positive charge count */
         mzapmsg(mon, obj, TRUE);
         obj->spe--;
         /* -1 => monster's wand of fire; 2 => # of damage dice */
-        (void) zhitm(mon, by_you ? 1 : -1, 2, &odummyp);
+        dmg = zhitm(mon, by_you ? 1 : -1, 2, &odummyp);
     }
 
+    if (dmg) {
+        /* zhitm() applies damage but doesn't kill creature off;
+           for fire breath, dmg is going to be 0 (fire breathers are
+           immune to fire damage) but for wand of fire or fire horn,
+           'mon' could have taken damage so might die */
+        if (mon->mhp <= 0) {
+            if (by_you) {
+                /* mon killed self but hero gets credit and blame (except
+                   for pacifist conduct); xkilled()'s message would say
+                   "You killed/destroyed <mon>" so give our own message */
+                if (vis)
+                    pline("%s is %s by the fire!", Monnam(mon),
+                          nonliving(mon->data) ? "destroyed" : "killed");
+                xkilled(mon, XKILL_NOMSG | XKILL_NOCONDUCT);
+            } else
+                monkilled(mon, "fire", AD_FIRE);
+        } else {
+            /* non-fatal damage occurred */
+            if (vis)
+                pline("%s is burned%s", Monnam(mon), exclam(dmg));
+        }
+    }
     if (vis) {
         if (res && mon->mhp > 0)
             pline("%s slime is burned away!", s_suffix(Monnam(mon)));