From: PatR Date: Thu, 22 Apr 2021 23:13:41 +0000 (-0700) Subject: fix "Killed by foo, while paralyzed by a monster" X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b4ae19ed0c64498db24012c65b2ebb2d186e93dc;p=nethack fix "Killed by foo, while paralyzed by a monster" If the killer and the paralyzer are the same monster, truncate that to "Killed by a foo, while paralyzed". When not the same, spell out the paralyzer's monster type instead of using generic "monster". "Killed by a fox, while paralyzed by a ghoul", or "Killed by a ghoul, while paralyzed by a ghoul" *if* they were two different ghouls. --- diff --git a/doc/fixes37.0 b/doc/fixes37.0 index 91af31f7a..7434b8931 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -472,7 +472,7 @@ if a branch has only one level (Fort Ludios), prevent creation of any level wishing could attempt to place one) opening/unlocking magic zapped at monster holding the hero will release hold (zap at engulfer already expels hero); zapping at self has same effect -when riding, allow scroll of remove curse to affect to affect steed's saddle +when riding, allow scroll of remove curse read by hero to affect steed's saddle the 'scores' option for final top ten display left default values in place if only some of the three settings were set; 'scores:own' should have produced '0 top/0 around/own' but ended up as '3 top/2 around/own' @@ -488,6 +488,9 @@ when swallowed or underwater, player could be told about events (such as a were not shown on the screen; treat being swallowed or underwater as situations which block telepathy, extended monster detection, warning some rolling boulder trap feedback was inconsistent +change "killed by , while {paralyzed|frozen} by " into + "killed by , while {paralyzed|frozen}" if the killer caused + hero's helplessness Fixes to 3.7.0-x Problems that Were Exposed Via git Repository diff --git a/include/decl.h b/include/decl.h index 8276f2ddf..4ef27b6b5 100644 --- a/include/decl.h +++ b/include/decl.h @@ -717,6 +717,7 @@ struct instance_globals { char command_line[COLNO]; long command_count; const char *multi_reason; + char multireasonbuf[QBUFSZ]; /* note: smaller than usual [BUFSZ] */ int nroom; int nsubroom; int occtime; diff --git a/include/extern.h b/include/extern.h index fc3a7a14e..fb167cb17 100644 --- a/include/extern.h +++ b/include/extern.h @@ -2637,6 +2637,7 @@ extern void u_init(void); /* ### uhitm.c ### */ +extern void dynamic_multi_reason(struct monst *, const char *, boolean); extern void erode_armor(struct monst *, int); extern boolean attack_checks(struct monst *, struct obj *); extern void check_caitiff(struct monst *); diff --git a/src/decl.c b/src/decl.c index 5a764ed5b..81d5e9e52 100644 --- a/src/decl.c +++ b/src/decl.c @@ -259,6 +259,9 @@ const struct instance_globals g_init = { UNDEFINED_VALUES, /* command_line */ 0, /* command_count */ NULL, /* multi_reason */ + /* multi_reason usually points to a string literal (when not Null) + but multireasonbuf[] is available for when it needs to be dynamic */ + DUMMY, /* multireasonbuf[] */ 0, /* nroom */ 0, /* nsubroom */ 0, /* occtime */ diff --git a/src/end.c b/src/end.c index 98e1a9c51..614e161c5 100644 --- a/src/end.c +++ b/src/end.c @@ -494,6 +494,37 @@ done_in_by(struct monst *mtmp, int how) } Strcpy(g.killer.name, buf); + + /* might need to fix up multi_reason if 'mtmp' caused the reason */ + if (g.multi_reason + && g.multi_reason > g.multireasonbuf + && g.multi_reason < g.multireasonbuf + sizeof g.multireasonbuf - 1) { + char reasondummy, *p; + unsigned reasonmid = 0; + + /* + * multireasonbuf[] contains 'm_id:reason' and multi_reason + * points at the text past the colon, so we have something + * like "42:paralyzed by a ghoul"; if mtmp->m_id matches 42 + * then we truncate 'reason' at its first space so that final + * death reason becomes "Killed by a ghoul, while paralyzed." + * instead of "Killed by a ghoul, while paralyzed by a ghoul." + * (3.6.x gave "Killed by a ghoul, while paralyzed by a monster." + * which is potenitally misleading when the monster is also + * the killer.) + * + * Note that if the hero is life-saved and then killed again + * before the helplessness has cleared, the second death will + * report the truncated helplessness reason even if some other + * monster peforms the /coup de grace/. + */ + if (sscanf(g.multireasonbuf, "%u:%c", &reasonmid, &reasondummy) == 2 + && mtmp->m_id == reasonmid) { + if ((p = index(g.multireasonbuf, ' ')) != 0) + *p = '\0'; + } + } + /* * Chicken and egg issue: * Ordinarily Unchanging ought to override something like this, @@ -555,6 +586,7 @@ fixup_death(int how) g.multi_reason = death_fixups[i].include; else /* remove the helplessness reason */ g.multi_reason = (char *) 0; + g.multireasonbuf[0] = '\0'; /* dynamic buf stale either way */ if (death_fixups[i].unmulti) /* possibly hide helplessness */ g.multi = 0L; break; diff --git a/src/hack.c b/src/hack.c index 435ad9b3a..a324726b7 100644 --- a/src/hack.c +++ b/src/hack.c @@ -3020,7 +3020,7 @@ nomul(int nval) u.usleep = 0; g.multi = nval; if (nval == 0) - g.multi_reason = NULL; + g.multi_reason = NULL, g.multireasonbuf[0] = '\0'; end_running(TRUE); } @@ -3047,7 +3047,7 @@ unmul(const char *msg_override) } g.nomovemsg = 0; u.usleep = 0; - g.multi_reason = NULL; + g.multi_reason = NULL, g.multireasonbuf[0] = '\0'; if (g.afternmv) { int (*f)(void) = g.afternmv; diff --git a/src/uhitm.c b/src/uhitm.c index 45ddb0450..2085e5dee 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -28,6 +28,30 @@ static boolean shade_aware(struct obj *); #define PROJECTILE(obj) ((obj) && is_ammo(obj)) +/* multi_reason is usually a literal string; here we generate one that + has the causing monster's type included */ +void +dynamic_multi_reason(struct monst *mon, const char *verb, boolean by_gaze) +{ + /* combination of noname_monnam() and m_monnam(), more or less; + accurate regardless of visibility or hallucination (only seen + if game ends) and without personal name (M2_PNAME excepted) */ + char *who = x_monnam(mon, ARTICLE_A, (char *) 0, + (SUPPRESS_IT | SUPPRESS_INVISIBLE + | SUPPRESS_HALLUCINATION | SUPPRESS_SADDLE + | SUPPRESS_NAME), + FALSE), + *p = g.multireasonbuf; + + /* prefix info for done_in_by() */ + Sprintf(p, "%u:", mon->m_id); + p = eos(p); + Sprintf(p, "%s by %s%s", verb, + !by_gaze ? who : s_suffix(who), + !by_gaze ? "" : " gaze"); + g.multi_reason = p; +} + void erode_armor(struct monst *mdef, int hurt) { @@ -2813,7 +2837,9 @@ mhitm_ad_plys(struct monst *magr, struct attack *mattk, struct monst *mdef, You("are frozen by %s!", mon_nam(magr)); g.nomovemsg = You_can_move_again; nomul(-rnd(10)); - g.multi_reason = "paralyzed by a monster"; + /* set g.multi_reason; + 3.6.x used "paralyzed by a monster"; be more specific */ + dynamic_multi_reason(magr, "paralyzed", FALSE); exercise(A_DEX, FALSE); } } @@ -4967,7 +4993,9 @@ passive(struct monst *mon, } else { You("are frozen by %s gaze!", s_suffix(mon_nam(mon))); nomul((ACURR(A_WIS) > 12 || rn2(4)) ? -tmp : -127); - g.multi_reason = "frozen by a monster's gaze"; + /* set g.multi_reason; + 3.6.x used "frozen by a monster's gaze" */ + dynamic_multi_reason(mon, "frozen", TRUE); g.nomovemsg = 0; } } else { @@ -4982,7 +5010,9 @@ passive(struct monst *mon, You("are frozen by %s!", mon_nam(mon)); g.nomovemsg = You_can_move_again; nomul(-tmp); - g.multi_reason = "frozen by a monster"; + /* set g.multi_reason; + 3.6.x used "frozen by a monster"; be more specific */ + dynamic_multi_reason(mon, "frozen", FALSE); exercise(A_DEX, FALSE); } break;