]> granicus.if.org Git - nethack/commitdiff
git issue #994 - killed by a touch of death
authorPatR <rankin@nethack.org>
Sun, 5 Mar 2023 23:11:25 +0000 (15:11 -0800)
committerPatR <rankin@nethack.org>
Sun, 5 Mar 2023 23:11:25 +0000 (15:11 -0800)
Issue reported by vultur-cadens:  cause of death reason for touch
of death and death due to loss of strength only showed the cause,
not the monster spellcaster who was responsible.

This changes
|Killed by a touch of death.
to
|Killed by the touch of death inflicted by the Wizard of Yendor.
and
|Killed by terminal fraility.
to
|Killed by strength loss inflicted by a chameleon imitating an arch-lich.
(The 'imitating' part doesn't fit on the tombstone but will be present
in logfile/xlogfile.)

Noticed while implemented this:  touch of death was modifying u.uhpmax
and basing death vs damage on that even when hero was polymorphed.
It now rehumanizes the hero in that situation.

Closes #994

include/extern.h
src/do_name.c
src/end.c
src/mcastu.c
src/polyself.c
src/uhitm.c

index 802b564c4c03796dd21ce73d11dc48a5af340a71..ecd49a89fa0c74decacc84a49ef4d105c3703873 100644 (file)
@@ -1320,7 +1320,7 @@ extern boolean usmellmon(struct permonst *);
 /* ### mcastu.c ### */
 
 extern int castmu(struct monst *, struct attack *, boolean, boolean);
-extern void touch_of_death(void);
+extern void touch_of_death(struct monst *);
 extern int buzzmu(struct monst *, struct attack *);
 
 /* ### mdlib.c ### */
index abd8e2d18eb2e3f0948f97ee110ab104ca146ff6..9e52945eaada7cedd996e2900d404a4ed04665b1 100644 (file)
@@ -2228,10 +2228,10 @@ Amonnam(struct monst *mtmp)
 /* used for monster ID by the '/', ';', and 'C' commands to block remote
    identification of the endgame altars via their attending priests */
 char *
-distant_monnam(struct monst *mon,
-               int article, /* only ARTICLE_NONE and ARTICLE_THE
-                               are handled here */
-               char *outbuf)
+distant_monnam(
+    struct monst *mon,
+    int article, /* only ARTICLE_NONE and ARTICLE_THE are handled here */
+    char *outbuf)
 {
     /* high priest(ess)'s identity is concealed on the Astral Plane,
        unless you're adjacent (overridden for hallucination which does
index a83f082f5b661706b682f956b4e3616849d3918d..b730264a823ee47006ac2214e9b956d454fd4eb6 100644 (file)
--- a/src/end.c
+++ b/src/end.c
@@ -409,9 +409,8 @@ done_in_by(struct monst *mtmp, int how)
 {
     char buf[BUFSZ];
     struct permonst *mptr = mtmp->data,
-                    *champtr = ((mtmp->cham >= LOW_PM)
-                                   ? &mons[mtmp->cham]
-                                   : mptr);
+                    *champtr = (mtmp->cham >= LOW_PM) ? &mons[mtmp->cham]
+                                                      : mptr;
     boolean distorted = (boolean) (Hallucination && canspotmon(mtmp)),
             mimicker = (M_AP_TYPE(mtmp) == M_AP_MONSTER),
             imitator = (mptr != champtr || mimicker);
@@ -442,7 +441,7 @@ done_in_by(struct monst *mtmp, int how)
     if (imitator) {
         char shape[BUFSZ];
         const char *realnm = pmname(champtr, Mgender(mtmp)),
-                             *fakenm = pmname(mptr, Mgender(mtmp));
+                   *fakenm = pmname(mptr, Mgender(mtmp));
         boolean alt = is_vampshifter(mtmp);
 
         if (mimicker) {
@@ -498,7 +497,8 @@ done_in_by(struct monst *mtmp, int how)
     /* might need to fix up multi_reason if 'mtmp' caused the reason */
     if (gm.multi_reason
         && gm.multi_reason > gm.multireasonbuf
-        && gm.multi_reason < gm.multireasonbuf + sizeof gm.multireasonbuf - 1) {
+        && gm.multi_reason
+           < gm.multireasonbuf + sizeof gm.multireasonbuf - 1) {
         char reasondummy, *p;
         unsigned reasonmid = 0;
 
index c1390e09195fc75847a0de96198ac976661dfa2d..a97fcd04a08e9fd54545605bc6256fa0274c8ef6 100644 (file)
@@ -39,6 +39,7 @@ static void cursetxt(struct monst *, boolean);
 static int choose_magic_spell(int);
 static int choose_clerical_spell(int);
 static int m_cure_self(struct monst *, int);
+static char *death_inflicted_by(char *, const char *, struct monst *);
 static void cast_wizard_spell(struct monst *, int, int);
 static void cast_cleric_spell(struct monst *, int, int);
 static boolean is_undirected_spell(unsigned int, int);
@@ -345,23 +346,60 @@ m_cure_self(struct monst *mtmp, int dmg)
     return dmg;
 }
 
+/* unlike the finger of death spell which behaves like a wand of death,
+   this monster spell only attacks the hero */
 void
-touch_of_death(void)
+touch_of_death(struct monst *mtmp)
 {
-    static const char touchodeath[] = "touch of death";
+    char kbuf[BUFSZ];
     int dmg = 50 + d(8, 6);
     int drain = dmg / 2;
 
+    /* if we get here, we know that hero isn't magic resistant and isn't
+       poly'd into an undead or demon */
     You_feel("drained...");
-
-    if (drain >= u.uhpmax) {
-        gk.killer.format = KILLED_BY_AN;
-        Strcpy(gk.killer.name, touchodeath);
+    (void) death_inflicted_by(kbuf, "the touch of death", mtmp);
+
+    if (Upolyd) {
+        u.mh = 0;
+        rehumanize(); /* fatal iff Unchanging */
+    } else if (drain >= u.uhpmax) {
+        gk.killer.format = KILLED_BY;
+        Strcpy(gk.killer.name, kbuf);
         done(DIED);
     } else {
         u.uhpmax -= drain;
-        losehp(dmg, touchodeath, KILLED_BY_AN);
+        losehp(dmg, kbuf, KILLED_BY);
+    }
+    gk.killer.name[0] = '\0'; /* not killed if we get here... */
+}
+
+/* give a reason for death by some monster spells */
+static char *
+death_inflicted_by(
+    char *outbuf,            /* assumed big enough; pm_names are short */
+    const char *deathreason, /* cause of death */
+    struct monst *mtmp)      /* monster who caused it */
+{
+    Strcpy(outbuf, deathreason);
+    if (mtmp) {
+        struct permonst *mptr = mtmp->data,
+            *champtr = (mtmp->cham >= LOW_PM) ? &mons[mtmp->cham] : mptr;
+        const char *realnm = pmname(champtr, Mgender(mtmp)),
+            *fakenm = pmname(mptr, Mgender(mtmp));
+
+        /* greatly simplfied extract from done_in_by(), primarily for
+           reason for death due to 'touch of death' spell; if mtmp is
+           shape changed, it won't be a vampshifter or mimic since they
+           can't cast spells */
+        if (!type_is_pname(champtr) && !the_unique_pm(mptr))
+            realnm = an(realnm);
+        Sprintf(eos(outbuf), " inflicted by %s%s",
+                the_unique_pm(mptr) ? "the " : "", realnm);
+        if (champtr != mptr)
+            Sprintf(eos(outbuf), " imitating %s", an(fakenm));
     }
+    return outbuf;
 }
 
 /*
@@ -394,7 +432,7 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum)
             if (Hallucination) {
                 You("have an out of body experience.");
             } else {
-                touch_of_death();
+                touch_of_death(mtmp);
             }
         } else {
             if (Antimagic) {
@@ -467,13 +505,18 @@ cast_wizard_spell(struct monst *mtmp, int dmg, int spellnum)
             monstseesu(M_SEEN_MAGR);
             You_feel("momentarily weakened.");
         } else {
+            char kbuf[BUFSZ];
+
             You("suddenly feel weaker!");
             dmg = mtmp->m_lev - 6;
             if (dmg < 1) /* paranoia since only chosen when m_lev is high */
                 dmg = 1;
             if (Half_spell_damage)
                 dmg = (dmg + 1) / 2;
-            losestr(rnd(dmg), (const char *) 0, 0);
+            losestr(rnd(dmg),
+                    death_inflicted_by(kbuf, "strength loss", mtmp),
+                    KILLED_BY);
+            gk.killer.name[0] = '\0'; /* not killed if we get here... */
         }
         dmg = 0;
         break;
index 3791013531fcaac690eae5946d561f4e2363be19..87a1dfcbbf39a9306516038c6fe7fd87984935bb 100644 (file)
@@ -1254,6 +1254,10 @@ rehumanize(void)
             gk.killer.format = NO_KILLER_PREFIX;
             Strcpy(gk.killer.name, "killed while stuck in creature form");
             done(DIED);
+            /* can get to here if declining to die in explore or wizard
+               mode; since we're wearing an amulet of unchanging we can't
+               be wearing an amulet of life-saving */
+            return; /* don't rehumanize after all */
         } else if (uamul && uamul->otyp == AMULET_OF_UNCHANGING) {
             Your("%s %s!", simpleonames(uamul), otense(uamul, "fail"));
             uamul->dknown = 1;
@@ -1274,7 +1278,8 @@ rehumanize(void)
         /* can only happen if some bit of code reduces u.uhp
            instead of u.mh while poly'd */
         Your("old form was not healthy enough to survive.");
-        Sprintf(gk.killer.name, "reverting to unhealthy %s form", gu.urace.adj);
+        Sprintf(gk.killer.name, "reverting to unhealthy %s form",
+                gu.urace.adj);
         gk.killer.format = KILLED_BY;
         done(DIED);
     }
index 7c65267a13ad78c3679145cfd1f8fbdb3f6712a7..35a54dc56fb8139b488d11d36ff7ae21648ad355 100644 (file)
@@ -3477,8 +3477,10 @@ mhitm_ad_pest(struct monst *magr, struct attack *mattk,
 }
 
 void
-mhitm_ad_deth(struct monst *magr, struct attack *mattk UNUSED,
-              struct monst *mdef, struct mhitm_data *mhm)
+mhitm_ad_deth(
+    struct monst *magr,
+    struct attack *mattk UNUSED,
+    struct monst *mdef, struct mhitm_data *mhm)
 {
     struct permonst *pd = mdef->data;
 
@@ -3501,7 +3503,7 @@ mhitm_ad_deth(struct monst *magr, struct attack *mattk UNUSED,
         case 18:
         case 17:
             if (!Antimagic) {
-                touch_of_death();
+                touch_of_death(magr);
                 mhm->damage = 0;
                 return;
             }